Skip to content

Commit b4b413d

Browse files
feat: actualizar la validación de nombres de perfil y mejorar la documentación sobre restricciones de nombres en Windows
1 parent fec1c15 commit b4b413d

12 files changed

Lines changed: 180 additions & 30 deletions

File tree

.github/workflows/release.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,21 @@ jobs:
2424
- name: Cache cargo
2525
uses: Swatinem/rust-cache@v2
2626

27+
- name: Verify tag matches Cargo.toml version
28+
shell: pwsh
29+
run: |
30+
$tagVersion = "${{ github.ref_name }}" -replace '^v', ''
31+
$cargoToml = Get-Content Cargo.toml -Raw
32+
if ($cargoToml -notmatch '(?m)^version\s*=\s*"([^"]+)"') {
33+
throw "Could not parse package version from Cargo.toml"
34+
}
35+
$cargoVersion = $Matches[1]
36+
if ($cargoVersion -ne $tagVersion) {
37+
throw "Tag version '$tagVersion' does not match Cargo.toml version '$cargoVersion'"
38+
}
39+
2740
- name: Build release
28-
run: cargo build --release
41+
run: cargo build --release --locked
2942

3043
- name: Package portable zip
3144
shell: pwsh

CHANGELOG.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,21 @@
22

33
## Unreleased
44

5-
### Added
5+
## [0.1.2] - 2026-04-13
6+
7+
### Fixed
8+
9+
- invalid profile names entered via CLI or the settings window are now rejected instead of being silently rewritten or falling back to the current profile when launching an extra instance;
10+
- forced process termination now waits briefly for the target to exit so stale windows disappear more reliably after a kill action.
11+
12+
### Changed
13+
14+
- the GitHub release workflow now verifies that the pushed tag matches `Cargo.toml` and builds the release artifacts with `--locked`;
15+
- user-facing docs now document the Windows-safe profile naming rules used by Panopticon.
16+
17+
## Earlier project notes
18+
19+
### Earlier additions
620

721
- persistent per-monitor filters;
822
- tag and application filters from the tray;
@@ -13,7 +27,7 @@
1327
- documentation and `cargo audit` jobs in GitHub Actions;
1428
- i18n system with English (default) and Spanish support.
1529

16-
### Changed
30+
### Earlier changes
1731

1832
- README updated with real repository links;
1933
- architecture documented with filters and grouping flow;

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "panopticon"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
edition = "2021"
55
description = "Real-time window thumbnail viewer for Windows using the DWM API"
66
readme = "README.md"

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ To run a named profile:
6060
cargo run --release -- --profile work
6161
```
6262

63+
Profile names are used as TOML filenames on Windows, so avoid reserved characters such as `<>:"/\\|?*` and control characters.
64+
6365
Panopticon also understands:
6466

6567
- `cargo run --release -- --profile=work`

docs/CONFIGURATION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ Panopticon supports separate per-file profiles.
241241
- the app can start with `--profile <name>`;
242242
- the CLI also accepts `--profile=<name>` for the same behaviour;
243243
- `--help` prints the available startup flags and `--version` prints the current app version;
244+
- interactive profile names must be valid Windows filename stems, so avoid `<>:"/\\|?*` and control characters;
244245
- the settings window allows saving the current state to another profile;
245246
- it also allows opening another instance with a different profile;
246247
- if no extra profiles exist, the runtime seeds `profile-1` and `profile-2`.

docs/GETTING_STARTED.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ cargo run --release -- --profile work
3333

3434
This loads the configuration from `%APPDATA%\Panopticon\profiles\work.toml`.
3535

36+
Profile names become Windows filenames, so keep them free of reserved characters such as `<>:"/\\|?*` and control characters.
37+
3638
You can also use `--profile=work`, inspect the CLI with `--help`, or print the current app version with `--version`.
3739

3840
## What happens at startup

docs/PRD.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Project: Panopticon
44

5-
**Documented product version:** 0.1.1
5+
**Documented product version:** 0.1.2
66
**Document status:** updated to reflect the current implementation
77
**Target platform:** Windows 10 / Windows 11 (64-bit)
88
**Actual stack:** Rust + Slint + Win32/DWM

src/app/secondary_windows.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::path::Path;
55
use std::process::Command;
66
use std::rc::Rc;
77

8-
use panopticon::settings::{AppSelectionEntry, AppSettings, HiddenAppEntry};
8+
use panopticon::settings::{AppSelectionEntry, AppSettings, HiddenAppEntry, ProfileNameValidation};
99
use panopticon::theme as theme_catalog;
1010
use panopticon::ui_option_ops::{
1111
app_option_label, current_profile_label, hidden_app_option_label, parse_option_value,
@@ -95,12 +95,18 @@ pub(crate) fn open_settings_window(
9595
let Some(settings_window) = guard.as_ref() else {
9696
return;
9797
};
98-
let requested = panopticon::settings::normalize_profile_name(
98+
let profile_name = match panopticon::settings::validate_profile_name_input(
9999
&settings_window.get_profile_name(),
100-
);
101-
let Some(profile_name) = requested else {
102-
tracing::warn!("ignoring empty/invalid profile save request");
103-
return;
100+
) {
101+
ProfileNameValidation::Valid(profile_name) => profile_name,
102+
ProfileNameValidation::Empty => {
103+
tracing::warn!("ignoring empty profile save request");
104+
return;
105+
}
106+
ProfileNameValidation::Invalid(reason) => {
107+
tracing::warn!(%reason, "ignoring invalid profile save request");
108+
return;
109+
}
104110
};
105111

106112
let settings_snapshot = state.borrow().settings.normalized();
@@ -122,10 +128,16 @@ pub(crate) fn open_settings_window(
122128
};
123129

124130
let current_profile = state.borrow().profile_name.clone();
125-
let requested = panopticon::settings::normalize_profile_name(
131+
let requested = match panopticon::settings::validate_profile_name_input(
126132
&settings_window.get_profile_name(),
127-
)
128-
.or(current_profile);
133+
) {
134+
ProfileNameValidation::Valid(profile_name) => Some(profile_name),
135+
ProfileNameValidation::Empty => current_profile,
136+
ProfileNameValidation::Invalid(reason) => {
137+
tracing::warn!(%reason, "ignoring invalid extra-instance profile request");
138+
return;
139+
}
140+
};
129141

130142
let settings_snapshot = state.borrow().settings.normalized();
131143
if let Some(profile_name) = requested.as_deref() {

src/app/thumbnail_interactions.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,11 @@ fn close_target_window(hwnd: HWND) {
307307
}
308308

309309
fn kill_target_process(hwnd: HWND) {
310-
use windows::Win32::System::Threading::{OpenProcess, TerminateProcess, PROCESS_TERMINATE};
310+
use windows::Win32::Foundation::{CloseHandle, WAIT_OBJECT_0, WAIT_TIMEOUT};
311+
use windows::Win32::System::Threading::{
312+
OpenProcess, TerminateProcess, WaitForSingleObject, PROCESS_QUERY_LIMITED_INFORMATION,
313+
PROCESS_TERMINATE,
314+
};
311315

312316
if hwnd.0.is_null() {
313317
return;
@@ -320,11 +324,32 @@ fn kill_target_process(hwnd: HWND) {
320324
return;
321325
}
322326
unsafe {
323-
if let Ok(process) = OpenProcess(PROCESS_TERMINATE, false, pid) {
324-
if let Err(error) = TerminateProcess(process, 1) {
325-
tracing::warn!(%error, pid, "TerminateProcess failed");
327+
match OpenProcess(
328+
PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION,
329+
false,
330+
pid,
331+
) {
332+
Ok(process) => {
333+
if let Err(error) = TerminateProcess(process, 1) {
334+
tracing::warn!(%error, pid, "TerminateProcess failed");
335+
} else {
336+
match WaitForSingleObject(process, 1_000) {
337+
WAIT_OBJECT_0 => {
338+
tracing::info!(pid, "terminated process after thumbnail kill request");
339+
}
340+
WAIT_TIMEOUT => {
341+
tracing::warn!(pid, "timed out waiting for terminated process");
342+
}
343+
status => tracing::warn!(
344+
pid,
345+
wait_status = status.0,
346+
"unexpected wait status after process termination"
347+
),
348+
}
349+
}
350+
let _ = CloseHandle(process);
326351
}
327-
let _ = windows::Win32::Foundation::CloseHandle(process);
352+
Err(error) => tracing::warn!(%error, pid, "OpenProcess failed for termination"),
328353
}
329354
}
330355
}

0 commit comments

Comments
 (0)