Repository navigation
feat(icons): taskbar/Start menu shell surfaces + File Explorer folders/drives (+ uninstall restore) - #12
Open
MelanieLezcano wants to merge 16 commits into
Open
feat(icons): taskbar/Start menu shell surfaces + File Explorer folders/drives (+ uninstall restore)#12MelanieLezcano wants to merge 16 commits into
MelanieLezcano wants to merge 16 commits into
Conversation
added 9 commits
October 12, 2026 05:07
…k draft)
Scan + wire three shell surfaces behind a second, OPT-IN participation axis (surfaces: {taskbar, startMenu}, all-OFF default — consent is never inherited; kindPolicy semantics inverted on purpose). The desktop stays always-on and kindPolicy-governed.
- dm-contracts: SurfaceDto + IconItemDto.{surface,parent} (serde defaults); bridge schema 10->11; bindings regenerated.
- dm-windows: taskbar pin root + both Start menu roots; surface-labeled walk (flat pin, recursive Start menu with parent grouping, depth cap 8, reparse skip); fs-fixture tests.
- dm-operations: recipe surfaces parsed in style_resolve + gated in effective_config (one participation authority); scope classify test for Common Start menu -> ProgramData; settings_store round-trip preservation test (G11).
- src-tauri: surface-aware icon_host assembly; live_slots stays desktop-only; non-desktop roots are best-effort (only the user desktop is mandatory/loud).
- Store: surfaces rides the recipe draft (4th knob; G11 whitelist on both bridge sides), surface gate inside effectiveTileConfig so disabling a surface auto-restores it on the next apply (RESTORE-FIRST), setSurface with one undo step per flip, history/preset/snapshot paths carry it.
- UI: SURFACES participation group with live counts, canvas surface chips (Desktop | Taskbar | Start menu), real pins rendered in the taskbar strip via the shared tile renderer, Start panel mock grouped by parent, per-surface consent copy; i18n en + zh-hans in lockstep.
- Resident/reconciler stays hard desktop-only (spec 07 scan filter unchanged).
Gates: bun test 670/0 · bun run lint · tsc -b · cargo test --workspace · bindings drift · banned-colors. Live [WINDOWS-VERIFY] pass pending (plan task 9). ADR-0024 status: Proposed — NOT owner-disposed.
…endment) Owner decision 2026-10-12: the taskbar + Start menu legs now default TRUE on both bridge sides (serde `impl Default for SurfacesJson` + `DEFAULT_SURFACES`), the same participate-by-default rule as kindPolicy — system-wide restyling is the app's promise. An explicit OFF leg still gates that surface (RESTORE-FIRST: its items ride restoreIds on the next apply) and the desktop stays never-gated. Round-trip/normalize/store/Rust tests updated for the new default; ADR-0024 §4.2 amended.
…sk 9 point 2) render-wasm.worker.ts posts `sourceReady ok:false` on init/decode failure (main thread re-requests instead of waiting forever), exactly ONE `baked png:null` on every failure path via a `posted` flag, releases `arrowReady` in a finally, and guards drainRenders so a rejected `rendererReady` can neither wedge `draining=true` nor surface as an unhandled promise rejection. Drops the working-tree-only TEMP-DEBUG tracing (command boundaries, UAC path, shell restart, stage beacon) and adds vite's `**/target/**` watch ignore (EBUSY on the linker was crashing the dev loop). Plan doc records the Task 9 field passes — CAS conflict on a hand-edited pin, restore byte-identical 94/94 FileBytes — plus this pass's findings (jpg attrs race, vanish-residue heal, 3 steady conflicts open, WebView2 CDP policy workaround).
…task 11) Start entries that are AppListEntries with no file-backed shortcut (7 of 97 on the dev box) become styleable through an OWNED wrapper .lnk in the user Programs root: target %SystemRoot%\explorer.exe + args shell:AppsFolder\<AUMID>, ownership marker DeskMakeover:appx-wrapper:v1, and System.AppUserModel.ID set on the ShellLink OBJECT (QI IPropertyStore -> SetValue VT_LPWSTR -> Commit) so the ONE IPersistFile::Save publishes link + property block atomically - Windows then merges the wrapper with the virtual entry (2 rows -> 1) and renders our icon. A by-path property write is impossible (the save target is a .tmp claim-temp with no shell property handler - verified E_FAIL on-box), and the icon-rewrite path reads the AUMID up front and re-attaches it to the loaded link before Save, so a re-style never un-merges the wrapper (fail closed if a known AUMID will not re-persist). Scan classifies file-backed AppsFolder shortcuts from their arguments and synthesizes items for virtual-only apps via the new shell/appsfolder.rs (AppsFolder enumerate/resolve/shell-item/glyph; collisions with a walked stem or a duplicate name skip + warn). State reader splits the originals: a present wrapper reads IconRef like any .lnk; an absent one reads a dedicated AppxAbsent fingerprint sentinel and anchors RestoreAnchor::FileAbsent, so reset REMOVES the wrapper and the package icon returns. Extraction early-returns for AppxShortcut and renders the AppsFolder item's own glyph via IShellItemImageFactory::GetImage through the shared factory_icon_image tail - never the wrapper's IconLocation. Elevation never (user Programs root only); dm-operations rides the existing kind-agnostic driver, its fake world gains a virtual-item model, and the two orchestration tests live in the Mac-gated icons/tests.rs (verified on-box with the gate lifted: 272/272). Docs: ADR-0024 records the sanctioned wrapper recipe (Decision 6); the plan fixes the SHCreateItemFromRelativeName parent phrasing (IShellItem parent, not FOLDERID), records the implemented atomic recipe, and keeps the on-box ship gates open (enumeration pin, synthesized scan count, click-to-launch end-to-end with the deskmakeover-desktop.exe --activate-aumid fallback if the explorer route still fails; then the HKLM WebView2 CDP dev value is deleted).
…(task 11 gate) On-box click-to-launch verification: the documented primary recipe (explorer.exe + shell:AppsFolder\<AUMID>, what Windows' own Store pins use) failed a real-wrapper ShellExecute test, confirming the 3 earlier CLI attempts - pre-existing COM registration damage on this box (same family as the wallpaper 0x80040154). The sanctioned re-target is now implemented: create_appx_shortcut points the wrapper at the HOST binary (current_exe) with args '--activate-aumid <AUMID>', and main.rs gains a fast-path that calls IApplicationActivationManager::ActivateApplication (CLSID 45BA127D-10A8-46EA-8AB7-56EA9078943C, IID 2E941141-7F97-4756-BA1D-9DECDE894A3D) and exits without booting the GUI (0 activated / 2 missing arg / 3 failed). Host is asInvoker, so no UAC on wrapper click. The AUMID property still drives the Start-menu merge - independent of the launch mechanism. parse_appx_aumid now recognizes BOTH wrapper recipes in a walked .lnk's arguments (launcher arm: first token after the flag is the AUMID, case-insensitive flag, anchored - same posture as the AppsFolder arm); without it a present wrapper would re-scan as an ordinary shortcut and re-style would compound. activate_app lives in shell/appsfolder.rs, owns its COM init, and both recipes are pinned by tests (args + target round-trip through an icon rewrite, launcher-arm parser cases, walk reclassification). Evidence (plan status + checklist + ADR-0024 D6 updated): enumeration pin green; 42 synthesized AppxShortcut with zero walked collisions; apply created 42 wrappers verified field-by-field (target/args/marker/System.AppUserModel.ID/baked asset); click gate PASSED - direct CLI exit 0 with WhatsApp.Root up, then wrapper click -> WhatsApp.Root up; two full apply/restore cycles clean (all 42 removed via RestoreAnchor::FileAbsent, user-root + ProgramData + desktop originals byte-restored); steady-state scans show no duplicate-name warns (transient ones were the watcher rescan racing the deletions, first-wins). Gates: cargo test --workspace 0 failures (dm-windows 138), bun test 670 + banned-colors 146, tsc, lint, check:bindings all green. HKLM WebView2 AdditionalBrowserArguments CDP dev value + WebView2 key deleted after verification. Also lands the plan Task 11 doc corrections (SHCreateItemFromRelativeName IShellItem parent, factory_icon_image extraction tail, object-attached AUMID write recipe).
…-0004 D7) Land the folder half of the `explorer` surface: enumerate the user profile's browsable folders and style them through the existing desktop.ini writer (apply/folder.rs), so folder icons follow a chosen look across File Explorer. - domain: `ItemSurface::Explorer` — one surface covers folders + drives, routing by `kind` (drives land in a follow-up). - ops/contracts: `surfaces.explorer` toggle (default ON) + allows() arm. - windows: `known_folders::profile_root()` (FOLDERID_Profile) + `walk_explorer_folders` — BFS (spends the shared icon-preview budget on shallow content first, never on deep tooling junk), depth cap 8, denylist + dot-prefix + Hidden/System + reparse exclusions, cap 1400; scan dedup drops profile folders the desktop surface already owns. - frontend: participation toggle leg, consent count, en/zh i18n. Verified on-box: profile folders carry our IconResource; AppData and node_modules/target/android-sdk/.bun/... stay clean (0 writes). Gates: cargo test --workspace, bun test (670), tsc, lint, check:bindings all green.
…ADR-0004 D7) Phase B of the Explorer module (folder half landed in 82386c0): the fixed drives the user browses are styled through the PER-USER icon key HKCU\Software\Classes\Applications\Explorer.exe\Drives\<L>\DefaultIcon written IN-HOST with NO elevation. An on-box spike proved the shell honours this key (SHGetFileInfo("C:\") changed and reverted), so drives stay inside the Warm tier; the machine HKLM ...\Explorer\DriveIcons tree is read ONLY as the original-icon fallback and is never written. autorun.inf is never touched, and only DRIVE_FIXED letters are keyed (removable/optical/network letters excluded). - domain: ItemKind::Drive; DriveAnchor + RestoreAnchor::Drive (dm-domain). - apply/drive.rs: read_current (per-user -> machine fallback), apply, restore, strict letter_from_path (X: / X:\ only). Restore removes just the (Default) value we created — recycle-bin posture, never delete_subkey_all over a key another program may share. - state_reader + fingerprint_surface: Drive surface state, read fingerprint, expected-after-apply. - source.rs: original_drive() recovers the original from the CAPTURED anchor (never a live read post-apply — SHGetFileInfo would answer with our own asset and compound Style(Style(orig))); an absent value falls back to the STATIC stock glyph shell32.dll,8. - shell: known_folders::visible_fixed_drives() (GetDriveTypeW, DRIVE_FIXED only); scan emits one Drive item per fixed root, display name from the shell ("SYSTEM (C:)"), fallback "(C:)". - bucket: Drive rides the Folder bucket (same policy switch + explorer consent); IconKindDto/TS union/bindings gain "Drive". - tests: letter_from_path, visible_fixed_drives (on-box smoke), drive-item emission, bucket_of coverage, serde/TS literals, domain kind coverage. Docs: ADR-0004 amendment (per-user key, in-host, no elevation), roadmap tick, plan status (two clean on-box apply/restore cycles verified).
Closes the "uninstall residue" live-pass check (taskbar/startmenu plan Task 9). The per-user NSIS uninstaller reverted nothing, so uninstalling while styled left the applied icons, wallpaper, arrow overlay, per-user drive keys and app data behind — a gap against the module's "one-click reversible with zero residue" promise. - lib.rs::run_uninstall_restore(): headless full restore reusing the same composition roots as setup (startup recovery -> settings store -> icon host restore + restore_overlay -> wallpaper restore per monitor -> calm/tweaks restore); resolves the app-data dir as %APPDATA%\com.xiaominglab.deskmakeover (app.path().app_data_dir() without a running Tauri app). - main.rs: handle --uninstall-restore before booting the GUI, exit with its code. - src-tauri/installer-hooks.nsh + bundle.windows.nsis.installerHooks: the NSIS_HOOK_PREUNINSTALL runs the restore BEFORE the uninstaller removes our files, then drops the per-user data dirs + C:\ProgramData\DeskMakeover. The machine-wide HKLM overlay restore raises one UAC (the uninstaller is per-user). Tested on-box against a live applied state: exit 0; HKLM Shell Icons\29 removed; the three HKCU Drives values cleared; 417/423 profile desktop.ini removed. Gates green (cargo workspace, bun 670, tsc, lint, check:bindings). The full install->apply->uninstall->verify loop rides the owner-supervised live pass.
This was referenced Oct 16, 2026
Open
Open
added 7 commits
October 16, 2026 06:57
…ADR-0004 D7) Explorer phase 2, per the owner rulings of 2026-10-16: - folders: one breadth-first walk over every fixed root (protected-tree and Users gates; depth 10 / 5000-folder caps), with extensions collected along the way. Sources are content-addressed `dmicon://` keys WITHOUT the item id, so N folders showing the same stock glyph cost ONE preview-budget entry. - file types: new `ItemKind::FileType` + `FileTypeAnchor`, a per-user `HKCU\Software\Classes\<ProgId>\DefaultIcon` writer/reader/restore that refuses poisoned ProgIds, and a two-pass scan enumeration with the image and `%1`/`%L`/`%l` gates (one item per ProgId, never one per extension). - bucket "File" as a second, independent kill switch; DTO + generated bindings; file types sorted LAST so `.ext` labels cannot starve the preview budget of desktop/taskbar/folder items. Two scale defects the first full-size apply (~5000 items) forced ? the owner box died at ~3 GB twice ? are fixed here because they are entangled with the same identity change: `package_masters` now bakes identical masters once and shares one `Arc<IcoAsset>` across every item that uses it, and `commit_apply` / `version_switch` build each `ApplyRequest` inside its own per-item transaction instead of materialising every item's ICO bytes up front (~1.5 GB per stage before). Regression: `identical_masters_bake_once_and_share_one_allocation`.
The worker registered one 256x256 RGBA buffer per item id, so 5042 explorer folders showing the same stock folder glyph held 5042 copies of the same pixels (~1.3 GB) and the WebView renderer died mid-apply. Decode identity is now the CONTENT (the source URL): `ready` and `seeds` are keyed by URL, an `itemSource` alias map records item -> URL, and render/bake worker messages carry `skey` (the content key) for the pixel lookup while the coalescing lane, the tile cache and the reply stay per item. Items sharing a URL now await ONE decode instead of each paying a round trip. Regression: tests/compositor-source-alias.test.ts (one dispatch per content, alias-resolved seedOf/getTile/bake, failed decode clears its claim).
- ADR-0004 D7 amendment: folder styling broadened to every fixed drive; the file-type ban lifted by owner ruling with explicit mitigations (ProgId-level image gate, %1/%L/%l refusal, poisoned-ProgId refusal); images carved out. - ADR-0024 amendment: the owner ask was re-checked item by item ? pinned taskbar icons are already styled; running-window exe resources stay a platform floor (an item-by-item re-check, no decision changed). - new plan doc 2026-10-16 (all drives + file types) with the final gates, the on-box verification numbers and the two scale defects plus their fixes. - roadmap and the two prior plans annotate the follow-on.
Three owner rulings after seeing the applied desktop (2026-10-16b):
- Loose images on the desktop were wrapped in a styled companion `.lnk` with the
original hidden ? visible as "styled" images, including inside the Desktop
folder in Explorer. `NEVER_RESTYLE_EXTENSIONS` (images + `.3mf`) now marks them
`PreviewOnly` with `Icons_Reason_KeptOriginal`, so they render original AND ride
the RESTORE-FIRST set: the next apply removes the wrappers. The static image
list gained the formats with no `PerceivedType` (`jxl`, `jp2`, `j2k`, `dds`,
`exr`, `hdr`, `qoi`, `psb`, `svgz`); `PerceivedType = image` stays the systemic
gate, and `.3mf` joined the list explicitly.
- Folders no longer wear the plate card: the `Folder` shape is a plate-filled
silhouette that reads as a WHITE card under the Mono + white-fallback look
("la parte blanca no deberia estar"). The Folder bucket's ladder now uses the
passthrough `None` shape (tinted glyph alone), the built-in preset ladders
follow, and the recipe migration chain gained v1 -> v2, rewriting a stored
`Folder: { shape: "Folder" }` override to `None` so an already-applied recipe
self-heals on the next apply (a user's other choices are untouched).
Tests: never-restyle list coverage + the desktop loose-file gate; the v1 -> v2
migration (only the Folder card is rewritten; other buckets and shapes survive).
An all-drives apply (~5200 masters, ~50 KiB each) tripped the session's cumulative-base64 cap (`MAX_SESSION_B64_BYTES = 256 MiB`, sized for a few hundred icons) and EVERY apply failed silently in ~8 s ? the desktop stayed native behind a generic "couldn't apply" toast. The truly DISTINCT data is a few MiB: every folder shares the stock glyph. `IconApplySession::push` now stores identical payloads ONCE (shared `Arc`) and counts only DISTINCT bytes, so the cap bounds the memory the session actually holds (the audit-F4 guard stays meaningful) instead of re-counting the same master once per item. `package_masters` already dedups the post-commit side. Verified on-box: 5200 items applied in one pass, peak RSS ~205 MB.
…fect Plan doc gains the follow-up section (images / `.3mf` kept original, folders without the plate card, the v1 -> v2 recipe migration) and the third scale defect ? the apply session's byte budget ? with its fix and the on-box verification numbers (5200 items, ~205 MB peak RSS).
The owner's desktop came back with NO taskbar and a black screen after an apply: explorer.exe was alive but had created neither `Shell_TrayWnd` nor the desktop layer, and `restart_shell`'s success criterion was the PROCESS only ? a shell-less Explorer passed verification and the app reported success over a desktop with no shell. (The wallpaper was untouched; the black screen was the missing desktop layer, not a wallpaper change.) The restart now requires BOTH halves ? `shell_ready` (pure, unit-tested): process alive AND `Shell_TrayWnd` present. A shell-less Explorer is cleared before relaunching (spawning on top of it never helps), and the failure error names the taskbar so the caller can surface an actionable message. Plan doc records the defect, the on-box repair (restart Explorer) and the verification.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements ADR-0024 (shell surfaces) + ADR-0004 D7 (Explorer module), carried on this fork per the 2026-07-21 community-proposal panel (the PR is expected to be deferred; write-surface reliability is the dominant ship risk, and the fork carries the feature until then).
Included (9 commits)
--activate-aumidfallback (Task 11 gate)desktop.ini(ADR-0004 D7)PREUNINSTALLhook)Touched spec sections
ADR-0024 (new) · ADR-0004 D7 (amended) · specs 00 roadmap / 01 architecture / 06 icons.
Gates run (all green)
cargo test --workspace·bun test(670) ·bun x tsc -b·bun run lint·bun run check:bindings· banned-colors.Owner invariants
Verification status
On-box apply/restore cycles verified for the Explorer surfaces (profile folders + fixed-drive keys) and the uninstall restore; the full write-surface live pass remains owner-supervised (
docs/verification/owner-supervised-live-runs.md). Records:docs/plans/2026-10-12-taskbar-startmenu-surfaces.md,docs/plans/2026-10-13-explorer-folder-drive-icons.md.