desktop: tray menu panels and pinnable context menus

The tray's right-click menu is drawn by the app: one tree from tray.rs
(stable submenu keys), one frameless always-on-top menu-N window per
panel rendered by tray-menu.html with the design system, placed inside
the monitor's work area. A pure panel book keeps transient and pinned
panels apart; pinned panels (pin button, Ctrl+click, middle click) are
draggable, step aside from each other and are saved in state.json
(menuPins) and restored at start. tray_update pushes the tree to the
open panels, which update in place; closed panels are destroyed. The
native menu, rendered from the same tree, stays as the fallback and on
Linux. Only menu-* windows get the menu_* commands (menu.json).

In-app context menus get the same behaviour through a pure menu stack
(ui/menu-stack.ts): pin a menu, tear off a submenu, pinned panels
survive the next menu, are draggable and re-read their entries from
live sources so data changes update them in place.

Also drops a stray diff3 marker line from CHANGELOG.md.
This commit is contained in:
CRTkafa 2026-10-24 13:44:08 +08:00
parent 445ab1adbc
commit 5236f002f1
35 changed files with 2308 additions and 123 deletions

View file

@ -3,10 +3,11 @@
## Unreleased
- Desktop tray: the right-click menu is drawn by the app (the design system's menu look) instead of the OS menu. Each submenu opens as its own small panel; the pin button, Ctrl+click or a middle click keeps a panel open (pinned): it can be dragged, steps aside so it never covers another pinned panel, and comes back after a restart. Values (the apps and their store state, mute) update in place after every store read without closing anything. The native menu stays as a fallback (and on Linux), built from the same menu tree. The left-click tray card is unchanged.
- Desktop right-click menus in the window work the same way: a menu's pin button keeps it open as a floating panel, a submenu can be torn off (its pin button, Ctrl+click or a middle click), pinned panels survive the next menu, can be dragged, and update in place when the data behind them changes ("Gone" when the thing they were for left the screen).
- `shots.feature.rotate` (degrees) is honoured by the Play feature graphic like a scene frame's `rotate`: added to the template tilt and the phone is re-fitted (`-90` stands a landscape game frame upright); with `shots.layout: "bleed"` the frame image is turned and still covers the canvas.
- `shots check` / `shots render` without frames for a language that has copy use the default language's frames (an info line, no failure); `shots.perLanguageFrames: true` requires per-language frames. `shots fonts --lang en,tr` limits the report to those languages (the font manifest keeps all of them).
- `shots capture` web shots take `waitSelector` and `click` selector lists per layout (`{ phone, tablet, any }`, data attributes first, `text=` fallback). A miss names the selector and the size with every candidate's match count; `shots capture --dry` prints the candidates per size and `--dry --probe` opens the pages and reports which one matched or MISSed on which size.
||||||| parent of ae8d773 (apple signing: every signable target (extensions, App Group check), build keychain unlock over SSH, api confirm docs)
- `apple signing ensure` signs every target of the Xcode project, not only the app: it reads the pbxproj targets (app, app extensions such as a share extension or widget, watch app, App Clip: Release PRODUCT_BUNDLE_IDENTIFIER and entitlements), registers each extension's bundle id (`POST /v1/bundleIds`, platform from the profile type), enables the capabilities its entitlements need (read back) and, with manual signing, creates its App Store profile; `--dry` lists every target. `apple.signing.extensions` (`bundleId`, `name`, `capabilities`, `appGroups`) replaces the detected extensions when detection is wrong; `status` shows a target table.
- App Groups: the API cannot assign a group to a bundle id, so `signing status` / `ensure` and (manual signing) `apple build` decode the profile and compare its `com.apple.security.application-groups` with the target's entitlements; a gap names the developer portal link (`.../identifiers/bundleId/edit/<id>`) and the steps to tick the group. `signing ensure --refresh-profile` makes the profile again afterwards; `apple build --allow-app-group-gap` overrides.