aicodeman 1.35.0 → 1.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -7
- package/dist/attachment-registry.d.ts +6 -0
- package/dist/attachment-registry.d.ts.map +1 -1
- package/dist/attachment-registry.js +9 -3
- package/dist/attachment-registry.js.map +1 -1
- package/dist/cli-agent.d.ts +225 -0
- package/dist/cli-agent.d.ts.map +1 -0
- package/dist/cli-agent.js +767 -0
- package/dist/cli-agent.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +8 -32
- package/dist/cli.js.map +1 -1
- package/dist/codeman-credentials.d.ts +27 -0
- package/dist/codeman-credentials.d.ts.map +1 -0
- package/dist/codeman-credentials.js +66 -0
- package/dist/codeman-credentials.js.map +1 -0
- package/dist/config/cli-registry/patterns.d.ts +6 -0
- package/dist/config/cli-registry/patterns.d.ts.map +1 -1
- package/dist/config/cli-registry/patterns.js +13 -0
- package/dist/config/cli-registry/patterns.js.map +1 -1
- package/dist/config/cli-registry/schema.d.ts +15 -0
- package/dist/config/cli-registry/schema.d.ts.map +1 -1
- package/dist/config/cli-registry/schema.js +63 -2
- package/dist/config/cli-registry/schema.js.map +1 -1
- package/dist/config/cli-registry/stock.d.ts.map +1 -1
- package/dist/config/cli-registry/stock.js +180 -7
- package/dist/config/cli-registry/stock.js.map +1 -1
- package/dist/config/cli-registry/types.d.ts +105 -7
- package/dist/config/cli-registry/types.d.ts.map +1 -1
- package/dist/deepseek-route-config.d.ts +110 -0
- package/dist/deepseek-route-config.d.ts.map +1 -0
- package/dist/deepseek-route-config.js +477 -0
- package/dist/deepseek-route-config.js.map +1 -0
- package/dist/git-clone.d.ts.map +1 -1
- package/dist/git-clone.js +4 -0
- package/dist/git-clone.js.map +1 -1
- package/dist/git-workspace-status.d.ts +31 -5
- package/dist/git-workspace-status.d.ts.map +1 -1
- package/dist/git-workspace-status.js +49 -29
- package/dist/git-workspace-status.js.map +1 -1
- package/dist/image-watcher.d.ts.map +1 -1
- package/dist/image-watcher.js +6 -2
- package/dist/image-watcher.js.map +1 -1
- package/dist/mcp-sync-targets.d.ts +34 -0
- package/dist/mcp-sync-targets.d.ts.map +1 -0
- package/dist/mcp-sync-targets.js +55 -0
- package/dist/mcp-sync-targets.js.map +1 -0
- package/dist/mcp-sync.d.ts +2 -1
- package/dist/mcp-sync.d.ts.map +1 -1
- package/dist/mcp-sync.js +62 -1
- package/dist/mcp-sync.js.map +1 -1
- package/dist/model-config-resolvers.d.ts +29 -0
- package/dist/model-config-resolvers.d.ts.map +1 -0
- package/dist/model-config-resolvers.js +34 -0
- package/dist/model-config-resolvers.js.map +1 -0
- package/dist/session-display-model.d.ts +110 -0
- package/dist/session-display-model.d.ts.map +1 -0
- package/dist/session-display-model.js +154 -0
- package/dist/session-display-model.js.map +1 -0
- package/dist/session.d.ts +145 -8
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +290 -39
- package/dist/session.js.map +1 -1
- package/dist/subagent-watcher.d.ts.map +1 -1
- package/dist/subagent-watcher.js +29 -6
- package/dist/subagent-watcher.js.map +1 -1
- package/dist/templates/case-template.md +2 -0
- package/dist/tmux-manager.d.ts +5 -0
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +47 -22
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tui/tui-client.d.ts +3 -17
- package/dist/tui/tui-client.d.ts.map +1 -1
- package/dist/tui/tui-client.js +3 -44
- package/dist/tui/tui-client.js.map +1 -1
- package/dist/types/session.d.ts +28 -1
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/dist/types/tools.d.ts +1 -1
- package/dist/types/tools.d.ts.map +1 -1
- package/dist/utils/deepseek-cli-resolver.d.ts +8 -0
- package/dist/utils/deepseek-cli-resolver.d.ts.map +1 -1
- package/dist/utils/deepseek-cli-resolver.js +18 -2
- package/dist/utils/deepseek-cli-resolver.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/process-exit-wait.d.ts +40 -0
- package/dist/utils/process-exit-wait.d.ts.map +1 -0
- package/dist/utils/process-exit-wait.js +68 -0
- package/dist/utils/process-exit-wait.js.map +1 -0
- package/dist/web/launch-defaults.d.ts +22 -0
- package/dist/web/launch-defaults.d.ts.map +1 -0
- package/dist/web/launch-defaults.js +46 -0
- package/dist/web/launch-defaults.js.map +1 -0
- package/dist/web/paste-image-gc.d.ts +46 -6
- package/dist/web/paste-image-gc.d.ts.map +1 -1
- package/dist/web/paste-image-gc.js +106 -32
- package/dist/web/paste-image-gc.js.map +1 -1
- package/dist/web/public/admin-ui.js.gz +0 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.6f8552e1.js +43 -0
- package/dist/web/public/app.6f8552e1.js.br +0 -0
- package/dist/web/public/app.6f8552e1.js.gz +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/{constants.d95fade9.js → constants.bc9c1f63.js} +1276 -127
- package/dist/web/public/constants.bc9c1f63.js.br +0 -0
- package/dist/web/public/constants.bc9c1f63.js.gz +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/entrance-animations.js +491 -43
- package/dist/web/public/entrance-animations.js.br +0 -0
- package/dist/web/public/entrance-animations.js.gz +0 -0
- package/dist/web/public/git-status-ui.js +55 -9
- package/dist/web/public/git-status-ui.js.br +0 -0
- package/dist/web/public/git-status-ui.js.gz +0 -0
- package/dist/web/public/home-sessions.js +13 -9
- package/dist/web/public/home-sessions.js.br +0 -0
- package/dist/web/public/home-sessions.js.gz +0 -0
- package/dist/web/public/host-wake-ui.js.gz +0 -0
- package/dist/web/public/i18n.a9840ea2.js +2 -0
- package/dist/web/public/i18n.a9840ea2.js.br +0 -0
- package/dist/web/public/i18n.a9840ea2.js.gz +0 -0
- package/dist/web/public/{image-input.a216cf9e.js → image-input.e00471c6.js} +21 -9
- package/dist/web/public/image-input.e00471c6.js.br +0 -0
- package/dist/web/public/image-input.e00471c6.js.gz +0 -0
- package/dist/web/public/index.html +250 -59
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.c1e7c7b7.js.gz +0 -0
- package/dist/web/public/{mobile-handlers.23a85db1.js → mobile-handlers.0ef9c311.js} +20 -0
- package/dist/web/public/mobile-handlers.0ef9c311.js.br +0 -0
- package/dist/web/public/mobile-handlers.0ef9c311.js.gz +0 -0
- package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/mobile.111b187d.css +1 -0
- package/dist/web/public/mobile.111b187d.css.br +0 -0
- package/dist/web/public/mobile.111b187d.css.gz +0 -0
- package/dist/web/public/{notification-manager.50bb87d8.js → notification-manager.feb82c04.js} +19 -2
- package/dist/web/public/notification-manager.feb82c04.js.br +0 -0
- package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.6a5898d9.js → panels-ui.71846fcb.js} +4 -4
- package/dist/web/public/panels-ui.71846fcb.js.br +0 -0
- package/dist/web/public/panels-ui.71846fcb.js.gz +0 -0
- package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
- package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
- package/dist/web/public/readmymind-ui.js.gz +0 -0
- package/dist/web/public/reboot-restore-ui.js.gz +0 -0
- package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
- package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
- package/dist/web/public/session-lineage.js +229 -86
- package/dist/web/public/session-lineage.js.br +0 -0
- package/dist/web/public/session-lineage.js.gz +0 -0
- package/dist/web/public/session-ui.f731602c.js +100 -0
- package/dist/web/public/session-ui.f731602c.js.br +0 -0
- package/dist/web/public/session-ui.f731602c.js.gz +0 -0
- package/dist/web/public/settings-ui.4e33390f.js +97 -0
- package/dist/web/public/settings-ui.4e33390f.js.br +0 -0
- package/dist/web/public/settings-ui.4e33390f.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview-worker.js +290 -0
- package/dist/web/public/spreadsheet-preview-worker.js.br +0 -0
- package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview.af82fa2f.js +555 -0
- package/dist/web/public/spreadsheet-preview.af82fa2f.js.br +0 -0
- package/dist/web/public/spreadsheet-preview.af82fa2f.js.gz +0 -0
- package/dist/web/public/spreadsheet-xlsx-core.js +1006 -0
- package/dist/web/public/spreadsheet-xlsx-core.js.br +0 -0
- package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
- package/dist/web/public/styles.81a72906.css +1 -0
- package/dist/web/public/styles.81a72906.css.br +0 -0
- package/dist/web/public/styles.81a72906.css.gz +0 -0
- package/dist/web/public/{subagent-windows.e6ca799f.js → subagent-windows.07277a58.js} +7 -5
- package/dist/web/public/subagent-windows.07277a58.js.br +0 -0
- package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
- package/dist/web/public/sw.js +2 -2
- package/dist/web/public/sw.js.br +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/tab-layout-browser.065fcba4.js.gz +0 -0
- package/dist/web/public/{tab-rail-resize.94cd59f0.js → tab-rail-resize.a5867793.js} +1 -1
- package/dist/web/public/tab-rail-resize.a5867793.js.br +0 -0
- package/dist/web/public/{tab-rail-resize.94cd59f0.js.gz → tab-rail-resize.a5867793.js.gz} +0 -0
- package/dist/web/public/terminal-keycode229-recovery.af09f99a.js +1 -0
- package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.br +0 -0
- package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
- package/dist/web/public/terminal-split.js +146 -649
- package/dist/web/public/terminal-split.js.br +0 -0
- package/dist/web/public/terminal-split.js.gz +0 -0
- package/dist/web/public/terminal-tile.js +1688 -0
- package/dist/web/public/terminal-tile.js.br +0 -0
- package/dist/web/public/terminal-tile.js.gz +0 -0
- package/dist/web/public/terminal-ui.b7267005.js +2 -0
- package/dist/web/public/terminal-ui.b7267005.js.br +0 -0
- package/dist/web/public/terminal-ui.b7267005.js.gz +0 -0
- package/dist/web/public/tile-grid.js +2895 -0
- package/dist/web/public/tile-grid.js.br +0 -0
- package/dist/web/public/tile-grid.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js +9 -6
- package/dist/web/public/ultracode-windows.js.br +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/exceljs.min.js +44 -0
- package/dist/web/public/vendor/exceljs.min.js.br +0 -0
- package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
- package/dist/web/public/vendor/fflate.min.js +1 -0
- package/dist/web/public/vendor/fflate.min.js.br +0 -0
- package/dist/web/public/vendor/fflate.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js +4 -0
- package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.br +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/{voice-input.c4b51eb6.js → voice-input.2342c37b.js} +64 -10
- package/dist/web/public/voice-input.2342c37b.js.br +0 -0
- package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
- package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
- package/dist/web/public/webview-tabs.js +9 -1
- package/dist/web/public/webview-tabs.js.br +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/routes/cli-registry-routes.d.ts +23 -4
- package/dist/web/routes/cli-registry-routes.d.ts.map +1 -1
- package/dist/web/routes/cli-registry-routes.js +95 -8
- package/dist/web/routes/cli-registry-routes.js.map +1 -1
- package/dist/web/routes/file-routes.d.ts +7 -0
- package/dist/web/routes/file-routes.d.ts.map +1 -1
- package/dist/web/routes/file-routes.js +34 -4
- package/dist/web/routes/file-routes.js.map +1 -1
- package/dist/web/routes/git-status-routes.d.ts.map +1 -1
- package/dist/web/routes/git-status-routes.js +33 -6
- package/dist/web/routes/git-status-routes.js.map +1 -1
- package/dist/web/routes/mcp-sync-routes.d.ts +7 -2
- package/dist/web/routes/mcp-sync-routes.d.ts.map +1 -1
- package/dist/web/routes/mcp-sync-routes.js +10 -3
- package/dist/web/routes/mcp-sync-routes.js.map +1 -1
- package/dist/web/routes/reboot-restore-routes.js +1 -1
- package/dist/web/routes/reboot-restore-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts +18 -0
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +154 -69
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/status-telemetry-routes.d.ts +3 -0
- package/dist/web/routes/status-telemetry-routes.d.ts.map +1 -1
- package/dist/web/routes/status-telemetry-routes.js +12 -0
- package/dist/web/routes/status-telemetry-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +13 -0
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +30 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +43 -0
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts +6 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +47 -21
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-listener-wiring.d.ts +1 -0
- package/dist/web/session-listener-wiring.d.ts.map +1 -1
- package/dist/web/session-listener-wiring.js +11 -0
- package/dist/web/session-listener-wiring.js.map +1 -1
- package/package.json +6 -4
- package/scripts/postinstall.js +16 -0
- package/skills/codeman/SKILL.md +6 -0
- package/README.zh-CN.md +0 -1139
- package/dist/web/public/app.b9516c7b.js +0 -43
- package/dist/web/public/app.b9516c7b.js.br +0 -0
- package/dist/web/public/app.b9516c7b.js.gz +0 -0
- package/dist/web/public/constants.d95fade9.js.br +0 -0
- package/dist/web/public/constants.d95fade9.js.gz +0 -0
- package/dist/web/public/i18n.52f5b7cc.js +0 -1
- package/dist/web/public/i18n.52f5b7cc.js.br +0 -0
- package/dist/web/public/i18n.52f5b7cc.js.gz +0 -0
- package/dist/web/public/image-input.a216cf9e.js.br +0 -0
- package/dist/web/public/image-input.a216cf9e.js.gz +0 -0
- package/dist/web/public/mobile-handlers.23a85db1.js.br +0 -0
- package/dist/web/public/mobile-handlers.23a85db1.js.gz +0 -0
- package/dist/web/public/mobile.1aa4952d.css +0 -1
- package/dist/web/public/mobile.1aa4952d.css.br +0 -0
- package/dist/web/public/mobile.1aa4952d.css.gz +0 -0
- package/dist/web/public/notification-manager.50bb87d8.js.br +0 -0
- package/dist/web/public/notification-manager.50bb87d8.js.gz +0 -0
- package/dist/web/public/panels-ui.6a5898d9.js.br +0 -0
- package/dist/web/public/panels-ui.6a5898d9.js.gz +0 -0
- package/dist/web/public/session-ui.6bddf916.js +0 -89
- package/dist/web/public/session-ui.6bddf916.js.br +0 -0
- package/dist/web/public/session-ui.6bddf916.js.gz +0 -0
- package/dist/web/public/settings-ui.6e798b7b.js +0 -97
- package/dist/web/public/settings-ui.6e798b7b.js.br +0 -0
- package/dist/web/public/settings-ui.6e798b7b.js.gz +0 -0
- package/dist/web/public/styles.2d175c60.css +0 -1
- package/dist/web/public/styles.2d175c60.css.br +0 -0
- package/dist/web/public/styles.2d175c60.css.gz +0 -0
- package/dist/web/public/subagent-windows.e6ca799f.js.br +0 -0
- package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
- package/dist/web/public/tab-rail-resize.94cd59f0.js.br +0 -0
- package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js +0 -1
- package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.br +0 -0
- package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.gz +0 -0
- package/dist/web/public/terminal-ui.a4535311.js +0 -2
- package/dist/web/public/terminal-ui.a4535311.js.br +0 -0
- package/dist/web/public/terminal-ui.a4535311.js.gz +0 -0
- package/dist/web/public/upload.html +0 -155
- package/dist/web/public/upload.html.br +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js +0 -4
- package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js.br +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js.gz +0 -0
- package/dist/web/public/voice-input.c4b51eb6.js.br +0 -0
- package/dist/web/public/voice-input.c4b51eb6.js.gz +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Entrance animations for the
|
|
2
|
+
* @fileoverview Entrance animations for the things that appear when work
|
|
3
3
|
* starts: session TABS, the main TERMINAL pane a session's CLI runs in, floating
|
|
4
|
-
* agent WINDOWS,
|
|
5
|
-
* One picker per surface, plus themes
|
|
4
|
+
* agent WINDOWS, the CONNECTION LINES tying a window back to its parent tab, and
|
|
5
|
+
* the TILES of the tile grid (tile-grid.js). One picker per surface, plus themes
|
|
6
|
+
* that set all five to a matching look.
|
|
6
7
|
*
|
|
7
8
|
* Everything is OFF by default (the `legacy` theme), so an untouched install
|
|
8
9
|
* behaves exactly as it did before this module existed. Opt in via App Settings
|
|
9
|
-
* →
|
|
10
|
+
* → Animations.
|
|
10
11
|
*
|
|
11
12
|
* Four constraints shape the design:
|
|
12
13
|
*
|
|
@@ -34,13 +35,28 @@
|
|
|
34
35
|
* once-per-id even though the POST response and the SSE event both call
|
|
35
36
|
* `_onSessionCreated`.
|
|
36
37
|
*
|
|
38
|
+
* Tiles are off by default (`settle`, the grid's own quick fade, exactly as
|
|
39
|
+
* before) and switched on in App Settings → Animations → Tile Animations, or
|
|
40
|
+
* preset by a theme. A styled tile plays in two beats that combine two
|
|
41
|
+
* surfaces. The FRAME enters as it mounts, in its own style
|
|
42
|
+
* (TILE_ANIM_STYLES); the SCREEN plays the terminal pane's style when its
|
|
43
|
+
* first capture lands (`.tile-body.term-enter`, the same keyframes as the main
|
|
44
|
+
* pane). The load queue serves one capture at a time, so
|
|
45
|
+
* the screens light up one after another, the focused tile first. The frame
|
|
46
|
+
* styles move transform and opacity only (six tiles animate at once; a blur
|
|
47
|
+
* belongs to the serialized screen beat), and each has its own way out on the
|
|
48
|
+
* closing grid's still copy. A reload restores with `settle` whatever the
|
|
49
|
+
* setting.
|
|
50
|
+
*
|
|
37
51
|
* Styles are selected by `data-tab-anim` / `data-term-anim` / `data-win-anim` /
|
|
38
|
-
* `data-line-anim` on <html>; the keyframes live in
|
|
39
|
-
* opens a floating picker that fakes tabs, a pane
|
|
40
|
-
*
|
|
52
|
+
* `data-line-anim` / `data-tile-anim` on <html>; the keyframes live in
|
|
53
|
+
* styles.css. `?animlab=1` opens a floating picker that fakes tabs, a pane
|
|
54
|
+
* replay, a window and a line, and replays the tile grid in place, so styles
|
|
55
|
+
* can be compared without spawning real sessions or agents.
|
|
41
56
|
*
|
|
42
57
|
* @mixin Extends CodemanApp.prototype via Object.assign
|
|
43
|
-
* @dependency app.js (tab render pipeline), subagent-windows.js (window + line hooks)
|
|
58
|
+
* @dependency app.js (tab render pipeline), subagent-windows.js (window + line hooks),
|
|
59
|
+
* tile-grid.js (tile mount, reveal and still-copy hooks)
|
|
44
60
|
* @dependency constants.js (escapeHtml)
|
|
45
61
|
* @loadorder 12.6 of 16, after webview-tabs.js, before ralph-wizard.js
|
|
46
62
|
*/
|
|
@@ -94,7 +110,6 @@ const LINE_ANIM_STYLES = [
|
|
|
94
110
|
*/
|
|
95
111
|
const TERM_ANIM_STYLES = [
|
|
96
112
|
{ key: 'crt', label: 'CRT', blurb: 'Power-on: a hot line that expands to full height.', duration: 560 },
|
|
97
|
-
{ key: 'boot', label: 'Boot', blurb: 'Flickers on under a green scan sweep.', duration: 760 },
|
|
98
113
|
{ key: 'wipe', label: 'Wipe', blurb: 'Reveals top-to-bottom behind a bright edge.', duration: 520 },
|
|
99
114
|
{ key: 'slide', label: 'Slide up', blurb: 'Rises into place from below.', duration: 420 },
|
|
100
115
|
{ key: 'fade', label: 'Fade', blurb: 'Quiet fade with a touch of scale.', duration: 340 },
|
|
@@ -102,30 +117,79 @@ const TERM_ANIM_STYLES = [
|
|
|
102
117
|
{ key: 'off', label: 'Off', blurb: 'Current behaviour: the pane just appears.', duration: 0 },
|
|
103
118
|
];
|
|
104
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Tile grid entrance styles, for each tile's FRAME (its screen plays the
|
|
122
|
+
* TERM_ANIM_STYLES style once content lands). Transform and opacity only, plus
|
|
123
|
+
* a wash on ::before: FitAddon reads the untransformed layout box, so a tile
|
|
124
|
+
* still fits once at its final size (#464). `stagger` is the default gap
|
|
125
|
+
* between tiles and `order` the default cascade: `reading` (row by row),
|
|
126
|
+
* `wave` (diagonals from the top left) or `ripple` (outward from the focused
|
|
127
|
+
* tile). `from`: the tile is measured against a source after layout (its tab,
|
|
128
|
+
* the Tiles button), so it is held one frame and timed by _runTileEntrances.
|
|
129
|
+
*/
|
|
130
|
+
// prettier-ignore
|
|
131
|
+
const TILE_ANIM_STYLES = [
|
|
132
|
+
{ key: 'settle', label: 'Off (default)', blurb: "The grid's own quick fade and settle.", duration: 180, stagger: 24, order: 'reading' },
|
|
133
|
+
{ key: 'fly', label: 'Fly from tab', blurb: 'Each tile flies out of its session tab, and back into it on close.', duration: 560, stagger: 55, order: 'reading', from: 'tab' },
|
|
134
|
+
{ key: 'deal', label: 'Deal', blurb: 'Dealt out of the Tiles button like cards, gathered back on close.', duration: 600, stagger: 75, order: 'reading', from: 'button' },
|
|
135
|
+
{ key: 'crt', label: 'CRT', blurb: 'Powers on as a hot line; switches off to a dot on close.', duration: 560, stagger: 70, order: 'wave' },
|
|
136
|
+
{ key: 'beam', label: 'Beam down', blurb: 'A beam draws down from its tab, then the tile materializes.', duration: 620, stagger: 90, order: 'reading', from: 'tab' },
|
|
137
|
+
{ key: 'cascade', label: 'Cascade', blurb: 'Swings down from its top edge in a diagonal wave.', duration: 600, stagger: 80, order: 'wave' },
|
|
138
|
+
{ key: 'pop', label: 'Pop', blurb: 'Springs open, rippling out from the focused tile.', duration: 480, stagger: 70, order: 'ripple' },
|
|
139
|
+
{ key: 'soft', label: 'Soft', blurb: 'Drifts in slowly, rippling out from the focused tile.', duration: 620, stagger: 60, order: 'ripple' },
|
|
140
|
+
{ key: 'off', label: 'None', blurb: 'Tiles just appear.', duration: 0, stagger: 0, order: 'reading' },
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
/** Cascade orders for the tile grid; `auto` is each style's own. */
|
|
144
|
+
const TILE_ANIM_ORDERS = [
|
|
145
|
+
{ key: 'auto', label: 'Style default' },
|
|
146
|
+
{ key: 'reading', label: 'Reading order' },
|
|
147
|
+
{ key: 'wave', label: 'Diagonal wave' },
|
|
148
|
+
{ key: 'ripple', label: 'Ripple from focus' },
|
|
149
|
+
];
|
|
150
|
+
|
|
105
151
|
/** How long a `beam` window waits before materializing. Just under the line draw. */
|
|
106
152
|
const BEAM_HOLD_MS = 360;
|
|
107
153
|
|
|
154
|
+
/** Gap between tiles leaving, in reading order, so the last copy ends last. */
|
|
155
|
+
const TILE_EXIT_STAGGER_MS = 35;
|
|
156
|
+
|
|
157
|
+
/** Tile exit durations per style (styles.css `tile-leave-*`); the rest use the default fade. */
|
|
158
|
+
const TILE_EXIT_MS = { fly: 460, deal: 520, crt: 520, beam: 480, cascade: 480, pop: 380, soft: 520 };
|
|
159
|
+
|
|
108
160
|
/** One-click combinations that read as a single look. */
|
|
109
161
|
const ANIM_THEMES = [
|
|
110
|
-
{ key: 'terminal', label: 'Terminal', tab: 'crt', win: 'crt', line: 'draw', term: 'crt' },
|
|
111
|
-
{ key: 'beamdown', label: 'Beam down', tab: 'crt', win: 'beam', line: 'draw', term: 'wipe' },
|
|
112
|
-
{ key: '
|
|
113
|
-
{ key: '
|
|
114
|
-
{ key: '
|
|
115
|
-
{ key: '
|
|
162
|
+
{ key: 'terminal', label: 'Terminal', tab: 'crt', win: 'crt', line: 'draw', term: 'crt', tile: 'crt' },
|
|
163
|
+
{ key: 'beamdown', label: 'Beam down', tab: 'crt', win: 'beam', line: 'draw', term: 'wipe', tile: 'beam' },
|
|
164
|
+
{ key: 'launch', label: 'Launch', tab: 'pop', win: 'fly', line: 'packet', term: 'fade', tile: 'fly' },
|
|
165
|
+
{ key: 'softfocus', label: 'Soft focus', tab: 'blur', win: 'blur', line: 'blur', term: 'blur', tile: 'soft' },
|
|
166
|
+
{ key: 'quiet', label: 'Quiet', tab: 'slide', win: 'materialize', line: 'fade', term: 'fade', tile: 'settle' },
|
|
167
|
+
{ key: 'playful', label: 'Playful', tab: 'pop', win: 'pop', line: 'packet', term: 'slide', tile: 'deal' },
|
|
168
|
+
{ key: 'legacy', label: 'Legacy', tab: 'off', win: 'fly', line: 'off', term: 'off', tile: 'settle' },
|
|
116
169
|
];
|
|
117
170
|
|
|
171
|
+
/**
|
|
172
|
+
* The surfaces a theme is recognised by. A theme also PRESETS the tile style
|
|
173
|
+
* when it is picked, but the tile style is its own setting (App Settings →
|
|
174
|
+
* Animations → Tile Animations, off by default), so changing it afterwards
|
|
175
|
+
* does not turn the theme into "Custom".
|
|
176
|
+
*/
|
|
177
|
+
const ANIM_SURFACES = ['tab', 'win', 'line', 'term'];
|
|
178
|
+
|
|
118
179
|
/**
|
|
119
180
|
* Defaults are the `legacy` theme: every entrance OFF, and agent windows on the
|
|
120
181
|
* `fly` behaviour Codeman already had before this module existed. So a user who
|
|
121
182
|
* never opens the picker sees exactly the pre-existing UI, and each mark/apply
|
|
122
|
-
* hook short-circuits on its first line. Opt in via App Settings →
|
|
123
|
-
*
|
|
183
|
+
* hook short-circuits on its first line. Opt in via App Settings → Animations,
|
|
184
|
+
* which persists to the localStorage keys below.
|
|
124
185
|
*/
|
|
125
186
|
const TAB_ANIM_DEFAULT = 'off';
|
|
126
187
|
const WIN_ANIM_DEFAULT = 'fly';
|
|
127
188
|
const LINE_ANIM_DEFAULT = 'off';
|
|
128
189
|
const TERM_ANIM_DEFAULT = 'off';
|
|
190
|
+
/** The grid's own fade and settle, unchanged for anyone who never picks a theme. */
|
|
191
|
+
const TILE_ANIM_DEFAULT = 'settle';
|
|
192
|
+
const TILE_ANIM_ORDER_DEFAULT = 'auto';
|
|
129
193
|
const TAB_ANIM_STAGGER_DEFAULT = 90;
|
|
130
194
|
/** A new id joins the current cascade if it arrives within this of the last one. */
|
|
131
195
|
const TAB_ANIM_BATCH_WINDOW_MS = 600;
|
|
@@ -135,6 +199,8 @@ const ANIM_KEYS = {
|
|
|
135
199
|
win: 'codeman:winAnim',
|
|
136
200
|
line: 'codeman:lineAnim',
|
|
137
201
|
term: 'codeman:termAnim',
|
|
202
|
+
tile: 'codeman:tileAnim',
|
|
203
|
+
tileOrder: 'codeman:tileAnimOrder',
|
|
138
204
|
termSwitch: 'codeman:termAnimOnSwitch',
|
|
139
205
|
stagger: 'codeman:tabAnimStagger',
|
|
140
206
|
speed: 'codeman:tabAnimSpeed',
|
|
@@ -164,6 +230,10 @@ Object.assign(CodemanApp.prototype, {
|
|
|
164
230
|
this.setWinAnimStyle(pick('winanim', WIN_ANIM_STYLES, ANIM_KEYS.win, WIN_ANIM_DEFAULT), { persist: false });
|
|
165
231
|
this.setLineAnimStyle(pick('lineanim', LINE_ANIM_STYLES, ANIM_KEYS.line, LINE_ANIM_DEFAULT), { persist: false });
|
|
166
232
|
this.setTermAnimStyle(pick('termanim', TERM_ANIM_STYLES, ANIM_KEYS.term, TERM_ANIM_DEFAULT), { persist: false });
|
|
233
|
+
// Off (the grid's own `settle`) until chosen: a theme saved before tiles
|
|
234
|
+
// were a surface gives them nothing new.
|
|
235
|
+
this.setTileAnimStyle(pick('tileanim', TILE_ANIM_STYLES, ANIM_KEYS.tile, TILE_ANIM_DEFAULT), { persist: false });
|
|
236
|
+
this.setTileAnimOrder(this._animRead(ANIM_KEYS.tileOrder, TILE_ANIM_ORDER_DEFAULT), { persist: false });
|
|
167
237
|
this.setTermAnimOnSwitch(this._animRead(ANIM_KEYS.termSwitch, '0') === '1', { persist: false });
|
|
168
238
|
|
|
169
239
|
this.setTabAnimStagger(Number(this._animRead(ANIM_KEYS.stagger, TAB_ANIM_STAGGER_DEFAULT)), { persist: false });
|
|
@@ -219,6 +289,18 @@ Object.assign(CodemanApp.prototype, {
|
|
|
219
289
|
this._setAnimStyle('_termAnimStyle', key, TERM_ANIM_STYLES, TERM_ANIM_DEFAULT, 'data-term-anim', ANIM_KEYS.term, persist);
|
|
220
290
|
},
|
|
221
291
|
|
|
292
|
+
setTileAnimStyle(key, { persist = true } = {}) {
|
|
293
|
+
// prettier-ignore
|
|
294
|
+
this._setAnimStyle('_tileAnimStyle', key, TILE_ANIM_STYLES, TILE_ANIM_DEFAULT, 'data-tile-anim', ANIM_KEYS.tile, persist);
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
/** The tile cascade: `auto` (the style's own) or a TILE_ANIM_ORDERS key. */
|
|
298
|
+
setTileAnimOrder(key, { persist = true } = {}) {
|
|
299
|
+
this._tileAnimOrder = TILE_ANIM_ORDERS.some((o) => o.key === key) ? key : TILE_ANIM_ORDER_DEFAULT;
|
|
300
|
+
if (persist) this._animWrite(ANIM_KEYS.tileOrder, this._tileAnimOrder);
|
|
301
|
+
this._syncAnimLab?.();
|
|
302
|
+
},
|
|
303
|
+
|
|
222
304
|
/** Replay the terminal entrance on every tab switch, not just on a new session. */
|
|
223
305
|
setTermAnimOnSwitch(on, { persist = true } = {}) {
|
|
224
306
|
this._termAnimOnSwitch = !!on;
|
|
@@ -234,22 +316,29 @@ Object.assign(CodemanApp.prototype, {
|
|
|
234
316
|
this.setWinAnimStyle(theme.win);
|
|
235
317
|
this.setLineAnimStyle(theme.line);
|
|
236
318
|
this.setTermAnimStyle(theme.term);
|
|
319
|
+
this.setTileAnimStyle(theme.tile);
|
|
237
320
|
this._syncEntranceAnimSetting?.();
|
|
238
321
|
},
|
|
239
322
|
|
|
240
|
-
/** The
|
|
323
|
+
/** The current style of each surface, keyed as ANIM_SURFACES. */
|
|
324
|
+
_currentAnimStyles() {
|
|
325
|
+
return {
|
|
326
|
+
tab: this._tabAnimStyle,
|
|
327
|
+
win: this._winAnimStyle,
|
|
328
|
+
line: this._lineAnimStyle,
|
|
329
|
+
term: this._termAnimStyle,
|
|
330
|
+
tile: this._tileAnimStyle,
|
|
331
|
+
};
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
/** The theme matching the current tab, window, line and pane styles, or 'custom' for a lab mix. */
|
|
241
335
|
currentAnimTheme() {
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
t.tab === this._tabAnimStyle &&
|
|
245
|
-
t.win === this._winAnimStyle &&
|
|
246
|
-
t.line === this._lineAnimStyle &&
|
|
247
|
-
t.term === this._termAnimStyle
|
|
248
|
-
);
|
|
336
|
+
const current = this._currentAnimStyles();
|
|
337
|
+
const match = ANIM_THEMES.find((t) => ANIM_SURFACES.every((k) => t[k] === current[k]));
|
|
249
338
|
return match ? match.key : 'custom';
|
|
250
339
|
},
|
|
251
340
|
|
|
252
|
-
// ── App Settings
|
|
341
|
+
// ── App Settings → Animations ─────────────────────────────────────────────
|
|
253
342
|
//
|
|
254
343
|
// Wired straight to setAnimTheme() rather than through saveAppSettings(): the
|
|
255
344
|
// styles live in their own localStorage keys, so they stay per-device and never
|
|
@@ -257,16 +346,40 @@ Object.assign(CodemanApp.prototype, {
|
|
|
257
346
|
|
|
258
347
|
_syncEntranceAnimSetting() {
|
|
259
348
|
const sel = document.getElementById('appSettingsEntranceAnim');
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
349
|
+
if (sel) {
|
|
350
|
+
sel.value = this.currentAnimTheme();
|
|
351
|
+
if (!sel.dataset.bound) {
|
|
352
|
+
sel.dataset.bound = '1';
|
|
353
|
+
sel.addEventListener('change', () => {
|
|
354
|
+
// 'custom' is a readout of a lab mix, not something you can select into.
|
|
355
|
+
if (sel.value === 'custom') sel.value = this.currentAnimTheme();
|
|
356
|
+
else this.setAnimTheme(sel.value);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// App Settings → Animations → Animation Lab. Settings has no unsaved-edit
|
|
361
|
+
// tracking, so this closes it as Cancel does (the row says so).
|
|
362
|
+
const labBtn = document.getElementById('appSettingsOpenAnimLab');
|
|
363
|
+
if (labBtn && !labBtn.dataset.bound) {
|
|
364
|
+
labBtn.dataset.bound = '1';
|
|
365
|
+
labBtn.addEventListener('click', () => {
|
|
366
|
+
this.closeAppSettings?.();
|
|
367
|
+
this.openAnimLab();
|
|
268
368
|
});
|
|
269
369
|
}
|
|
370
|
+
// Tile Animations: its own row, off (`settle`) by default. A theme picked
|
|
371
|
+
// above presets it; picked here, it applies to tiles alone.
|
|
372
|
+
const tileSel = document.getElementById('appSettingsTileAnim');
|
|
373
|
+
if (tileSel) {
|
|
374
|
+
tileSel.value = this._tileAnimStyle || TILE_ANIM_DEFAULT;
|
|
375
|
+
if (!tileSel.dataset.bound) {
|
|
376
|
+
tileSel.dataset.bound = '1';
|
|
377
|
+
tileSel.addEventListener('change', () => {
|
|
378
|
+
this.setTileAnimStyle(tileSel.value);
|
|
379
|
+
this._syncEntranceAnimSetting();
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
270
383
|
},
|
|
271
384
|
|
|
272
385
|
setTabAnimStagger(ms, { persist = true } = {}) {
|
|
@@ -303,6 +416,10 @@ Object.assign(CodemanApp.prototype, {
|
|
|
303
416
|
return this._styleDuration(TERM_ANIM_STYLES, this._termAnimStyle);
|
|
304
417
|
},
|
|
305
418
|
|
|
419
|
+
_tileAnimDuration() {
|
|
420
|
+
return this._styleDuration(TILE_ANIM_STYLES, this._tileAnimStyle);
|
|
421
|
+
},
|
|
422
|
+
|
|
306
423
|
// ── Tabs ──────────────────────────────────────────────────────────────────
|
|
307
424
|
|
|
308
425
|
/** Queue a session id to animate on its next render. Idempotent per id. */
|
|
@@ -501,6 +618,317 @@ Object.assign(CodemanApp.prototype, {
|
|
|
501
618
|
}
|
|
502
619
|
},
|
|
503
620
|
|
|
621
|
+
// ── Tile grid ─────────────────────────────────────────────────────────────
|
|
622
|
+
|
|
623
|
+
/** The frame style a tile mounted now enters with (tile-grid.js _mountTile). */
|
|
624
|
+
tileEntranceStyle() {
|
|
625
|
+
return this._tileAnimStyle || TILE_ANIM_DEFAULT;
|
|
626
|
+
},
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Holds a just-mounted tile (`.tile--enter-hold`: invisible, not animating)
|
|
630
|
+
* until the next frame, when its cell is final and _runTileEntrances can
|
|
631
|
+
* order it and measure it against its source. `setBackstop(ms)` arms the
|
|
632
|
+
* mount's own timer that ends the entrance should animationend never come
|
|
633
|
+
* (a hidden browser tab, a zoomed grid hiding the tile); armed long at once,
|
|
634
|
+
* so a frame that never comes cannot strand a tile invisible.
|
|
635
|
+
*/
|
|
636
|
+
_stageTileEntrance(el, sessionId, setBackstop) {
|
|
637
|
+
el.classList.add('tile--enter-themed', 'tile--enter-hold');
|
|
638
|
+
(this._tileEnterQueue ||= []).push({ el, sessionId, setBackstop });
|
|
639
|
+
setBackstop(4000);
|
|
640
|
+
if (!this._tileEnterRaf) this._tileEnterRaf = requestAnimationFrame(() => this._runTileEntrances());
|
|
641
|
+
},
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* One frame after the tiles mounted, every cell is final (openTileGrid packs,
|
|
645
|
+
* then a stored grid moves its tiles back). Each held tile gets its delay
|
|
646
|
+
* from the cascade order and, for `fly`/`deal`, the offset that starts it on
|
|
647
|
+
* its tab or the Tiles button (FLIP: transform only, so the fit it already
|
|
648
|
+
* did at its real size stands). `beam` draws its lines instead.
|
|
649
|
+
*/
|
|
650
|
+
_runTileEntrances() {
|
|
651
|
+
this._tileEnterRaf = 0;
|
|
652
|
+
const queue = (this._tileEnterQueue || []).filter((q) => q.el.isConnected);
|
|
653
|
+
this._tileEnterQueue = [];
|
|
654
|
+
if (queue.length === 0) return;
|
|
655
|
+
const def = TILE_ANIM_STYLES.find((s) => s.key === this._tileAnimStyle) || TILE_ANIM_STYLES[0];
|
|
656
|
+
const speed = this._animSpeed || 1;
|
|
657
|
+
const stagger = def.stagger / speed;
|
|
658
|
+
const duration = this._tileAnimDuration();
|
|
659
|
+
const hold = def.key === 'beam' ? BEAM_HOLD_MS / speed : 0;
|
|
660
|
+
const order = this._tileAnimOrder && this._tileAnimOrder !== 'auto' ? this._tileAnimOrder : def.order;
|
|
661
|
+
const ranks = this._tileEnterRanks(
|
|
662
|
+
queue.map((q) => q.sessionId),
|
|
663
|
+
order
|
|
664
|
+
);
|
|
665
|
+
const beams = [];
|
|
666
|
+
queue.forEach((item, k) => {
|
|
667
|
+
const { el, sessionId } = item;
|
|
668
|
+
const delay = ranks[k] * stagger;
|
|
669
|
+
el.style.setProperty('--tile-enter-delay', `${Math.round(delay + hold)}ms`);
|
|
670
|
+
if (def.from) {
|
|
671
|
+
const to = el.getBoundingClientRect();
|
|
672
|
+
const from = this._tileSourceRect(def.from, sessionId);
|
|
673
|
+
if (from && to.width > 0 && to.height > 0) {
|
|
674
|
+
if (def.key === 'beam') beams.push({ from, to, delay });
|
|
675
|
+
else this._setTileFlight(el, from, to, def.key, ranks[k], '--tile-from');
|
|
676
|
+
if (def.from === 'tab') this._flashTileSourceTab(sessionId, delay);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
el.classList.remove('tile--enter-hold');
|
|
680
|
+
// When the frame lands, for a screen whose content arrives earlier.
|
|
681
|
+
el._tileEnterEndsAt = performance.now() + delay + hold + duration;
|
|
682
|
+
item.setBackstop(delay + hold + duration + 900);
|
|
683
|
+
});
|
|
684
|
+
if (beams.length > 0) this._drawTileBeams(beams);
|
|
685
|
+
},
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Cascade steps for `ids` (tiles in this batch) by their cells: `reading`
|
|
689
|
+
* row by row, `wave` by diagonal (row + column), `ripple` by distance from
|
|
690
|
+
* the focused tile. Equal keys share a step, so a diagonal lands together.
|
|
691
|
+
*/
|
|
692
|
+
_tileEnterRanks(ids, order) {
|
|
693
|
+
const grid = this._tileGrid;
|
|
694
|
+
const cols = Math.max(1, grid?.cols || 1);
|
|
695
|
+
const cellOf = (id) => (Array.isArray(grid?.cells) ? grid.cells.indexOf(id) : -1);
|
|
696
|
+
const pos = ids.map((id, k) => {
|
|
697
|
+
const c = cellOf(id);
|
|
698
|
+
return c < 0 ? { cell: k, row: 0, col: k } : { cell: c, row: Math.floor(c / cols), col: c % cols };
|
|
699
|
+
});
|
|
700
|
+
let keys;
|
|
701
|
+
if (order === 'wave') {
|
|
702
|
+
keys = pos.map((p) => p.row + p.col);
|
|
703
|
+
} else if (order === 'ripple') {
|
|
704
|
+
const f = cellOf(grid?.focusedId);
|
|
705
|
+
const fr = f < 0 ? 0 : Math.floor(f / cols);
|
|
706
|
+
const fc = f < 0 ? 0 : f % cols;
|
|
707
|
+
keys = pos.map((p) => Math.abs(p.row - fr) + Math.abs(p.col - fc));
|
|
708
|
+
} else {
|
|
709
|
+
keys = pos.map((p) => p.cell);
|
|
710
|
+
}
|
|
711
|
+
const steps = [...new Set(keys)].sort((a, b) => a - b);
|
|
712
|
+
return keys.map((v) => steps.indexOf(v));
|
|
713
|
+
},
|
|
714
|
+
|
|
715
|
+
/** On-screen rect of a tile's source: its session tab (`tab`), else the Tiles button. */
|
|
716
|
+
_tileSourceRect(kind, sessionId) {
|
|
717
|
+
const visible = (node) => {
|
|
718
|
+
const r = node?.getBoundingClientRect?.();
|
|
719
|
+
if (!r || !(r.width > 0 && r.height > 0)) return null;
|
|
720
|
+
return r.bottom > 0 && r.right > 0 && r.top < window.innerHeight && r.left < window.innerWidth ? r : null;
|
|
721
|
+
};
|
|
722
|
+
if (kind === 'tab') {
|
|
723
|
+
const tab = document.querySelector(`.session-tab[data-id="${CSS.escape(sessionId)}"]`);
|
|
724
|
+
const r = visible(tab);
|
|
725
|
+
if (r) return r;
|
|
726
|
+
}
|
|
727
|
+
return visible(document.querySelector('.btn-tile-grid'));
|
|
728
|
+
},
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* The transform that puts a tile laid out at `to` onto `from`, as custom
|
|
732
|
+
* properties `<prefix>-x/-y/-sx/-sy/-rot` for the keyframes: the tab's own
|
|
733
|
+
* size for `fly` (it grows out of it), a small card turned a little for
|
|
734
|
+
* `deal`.
|
|
735
|
+
*/
|
|
736
|
+
_setTileFlight(el, from, to, kind, rank, prefix) {
|
|
737
|
+
const dx = from.left + from.width / 2 - (to.left + to.width / 2);
|
|
738
|
+
const dy = from.top + from.height / 2 - (to.top + to.height / 2);
|
|
739
|
+
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
740
|
+
let sx;
|
|
741
|
+
let sy;
|
|
742
|
+
let rot = 0;
|
|
743
|
+
if (kind === 'deal') {
|
|
744
|
+
sx = sy = clamp((from.width * 1.6) / to.width, 0.04, 0.3);
|
|
745
|
+
rot = [-14, 10, -7, 13, -11, 8][rank % 6];
|
|
746
|
+
} else {
|
|
747
|
+
sx = clamp(from.width / to.width, 0.02, 1);
|
|
748
|
+
sy = clamp(from.height / to.height, 0.02, 1);
|
|
749
|
+
}
|
|
750
|
+
el.style.setProperty(`${prefix}-x`, `${Math.round(dx)}px`);
|
|
751
|
+
el.style.setProperty(`${prefix}-y`, `${Math.round(dy)}px`);
|
|
752
|
+
el.style.setProperty(`${prefix}-sx`, sx.toFixed(4));
|
|
753
|
+
el.style.setProperty(`${prefix}-sy`, sy.toFixed(4));
|
|
754
|
+
el.style.setProperty(`${prefix}-rot`, `${rot}deg`);
|
|
755
|
+
},
|
|
756
|
+
|
|
757
|
+
/** The tab a tile leaves from glows as it goes (`fly`, `beam`). */
|
|
758
|
+
_flashTileSourceTab(sessionId, delay) {
|
|
759
|
+
const tab = document.querySelector(`.session-tab[data-id="${CSS.escape(sessionId)}"]`);
|
|
760
|
+
if (!tab) return;
|
|
761
|
+
tab.style.setProperty('--tab-launch-delay', `${Math.round(delay)}ms`);
|
|
762
|
+
tab.classList.remove('tab-launch');
|
|
763
|
+
void tab.offsetWidth;
|
|
764
|
+
tab.classList.add('tab-launch');
|
|
765
|
+
const onEnd = (e) => {
|
|
766
|
+
if (e.target === tab && /^tab-launch/.test(e.animationName || '')) done();
|
|
767
|
+
};
|
|
768
|
+
const done = () => {
|
|
769
|
+
clearTimeout(timer);
|
|
770
|
+
tab.removeEventListener('animationend', onEnd);
|
|
771
|
+
tab.classList.remove('tab-launch');
|
|
772
|
+
tab.style.removeProperty('--tab-launch-delay');
|
|
773
|
+
};
|
|
774
|
+
const timer = setTimeout(done, delay + 900);
|
|
775
|
+
tab.addEventListener('animationend', onEnd);
|
|
776
|
+
},
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* `beam`: a line draws from each tile's tab (or the Tiles button) down into
|
|
780
|
+
* the middle of its tile, in the connection-line look, with a packet riding it
|
|
781
|
+
* when the line style is `packet`; the tile materializes as it lands. Its
|
|
782
|
+
* own overlay: the agent lines' one is rebuilt from scratch on every redraw.
|
|
783
|
+
* The overlay goes once every line has faded.
|
|
784
|
+
*/
|
|
785
|
+
_drawTileBeams(beams) {
|
|
786
|
+
const ns = 'http://www.w3.org/2000/svg';
|
|
787
|
+
let svg = document.getElementById('tileBeamLines');
|
|
788
|
+
if (!svg) {
|
|
789
|
+
svg = document.createElementNS(ns, 'svg');
|
|
790
|
+
svg.id = 'tileBeamLines';
|
|
791
|
+
svg.setAttribute('class', 'connection-lines-svg tile-beam-lines');
|
|
792
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
793
|
+
document.body.appendChild(svg);
|
|
794
|
+
}
|
|
795
|
+
const packet = this._lineAnimStyle === 'packet';
|
|
796
|
+
let last = 0;
|
|
797
|
+
for (const { from, to, delay } of beams) {
|
|
798
|
+
const x1 = from.left + from.width / 2;
|
|
799
|
+
const y1 = from.bottom;
|
|
800
|
+
// Into the tile's middle: its top edge sits right under the tab strip,
|
|
801
|
+
// so a beam aimed there ran sideways along the strip instead of down.
|
|
802
|
+
const x2 = to.left + to.width / 2;
|
|
803
|
+
const y2 = to.top + to.height / 2;
|
|
804
|
+
const midY = (y1 + y2) / 2;
|
|
805
|
+
const path = document.createElementNS(ns, 'path');
|
|
806
|
+
path.setAttribute('d', `M ${x1} ${y1} C ${x1} ${midY}, ${x2} ${midY}, ${x2} ${y2}`);
|
|
807
|
+
path.setAttribute('class', 'connection-line tile-beam-line');
|
|
808
|
+
svg.appendChild(path);
|
|
809
|
+
const len = Math.max(1, Math.round(path.getTotalLength()));
|
|
810
|
+
path.style.setProperty('--line-len', `${len}px`);
|
|
811
|
+
path.style.setProperty('--line-enter-delay', `${Math.round(delay)}ms`);
|
|
812
|
+
if (packet) {
|
|
813
|
+
const dot = path.cloneNode(false);
|
|
814
|
+
dot.setAttribute('class', 'connection-line-packet');
|
|
815
|
+
svg.appendChild(dot);
|
|
816
|
+
}
|
|
817
|
+
last = Math.max(last, delay);
|
|
818
|
+
}
|
|
819
|
+
clearTimeout(this._tileBeamTimer);
|
|
820
|
+
this._tileBeamTimer = setTimeout(() => svg.remove(), last + 1500 / (this._animSpeed || 1));
|
|
821
|
+
},
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* A tile's screen lights up when its first capture lands (tile-grid.js load
|
|
825
|
+
* queue), in the terminal pane's style: the same keyframes as the main pane,
|
|
826
|
+
* on `.tile-body`. Transform, opacity and clip-path (and `blur`'s filter, one
|
|
827
|
+
* tile at a time, since the queue serves one capture at a time), so the
|
|
828
|
+
* xterm inside keeps its size and its fit.
|
|
829
|
+
*/
|
|
830
|
+
playTileScreenEntrance(body) {
|
|
831
|
+
if (!body || (this._termAnimStyle || TERM_ANIM_DEFAULT) === 'off') return;
|
|
832
|
+
if (window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches) return;
|
|
833
|
+
body._codemanScreenDone?.();
|
|
834
|
+
// Content that lands while the frame is still flying in waits (held at
|
|
835
|
+
// its first keyframe, so hidden) until the frame has nearly landed: two
|
|
836
|
+
// beats, frame then screen, rather than both at once.
|
|
837
|
+
const frame = body.closest?.('.tile');
|
|
838
|
+
const endsAt = frame?.classList.contains('tile--entering') ? frame._tileEnterEndsAt || 0 : 0;
|
|
839
|
+
const wait = Math.max(0, Math.round(endsAt - performance.now() - 120 / (this._animSpeed || 1)));
|
|
840
|
+
body.style.setProperty('--tile-screen-delay', `${wait}ms`);
|
|
841
|
+
body.classList.remove('term-enter');
|
|
842
|
+
void body.offsetWidth;
|
|
843
|
+
body.classList.add('term-enter');
|
|
844
|
+
let timer = null;
|
|
845
|
+
const done = (e) => {
|
|
846
|
+
if (e && (e.target !== body || e.pseudoElement)) return;
|
|
847
|
+
clearTimeout(timer);
|
|
848
|
+
body.removeEventListener('animationend', done);
|
|
849
|
+
body.removeEventListener('animationcancel', done);
|
|
850
|
+
body.classList.remove('term-enter');
|
|
851
|
+
body.style.removeProperty('--tile-screen-delay');
|
|
852
|
+
if (body._codemanScreenDone === done) body._codemanScreenDone = null;
|
|
853
|
+
};
|
|
854
|
+
body._codemanScreenDone = done;
|
|
855
|
+
body.addEventListener('animationend', done);
|
|
856
|
+
body.addEventListener('animationcancel', done);
|
|
857
|
+
// Backstop, as the main pane's: a backgrounded tab never fires animationend.
|
|
858
|
+
timer = setTimeout(() => done(), wait + this._termAnimDuration() + 900);
|
|
859
|
+
},
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* The closing grid's still copy (tile-grid.js _ghostTileGrid) leaves the
|
|
863
|
+
* frame style's own way: `fly` back into each tab, `deal` gathered into the
|
|
864
|
+
* Tiles button, `crt` switched off to a dot, and so on. Copies leave in
|
|
865
|
+
* reading order, so the last one ends last (the layer goes on its
|
|
866
|
+
* animationend). Re-forming the grid (`now`) keeps the plain fade: that copy
|
|
867
|
+
* covers tiles that stay. Returns how long the slowest copy takes, for the
|
|
868
|
+
* layer's fallback timer, or 0 for the default fade.
|
|
869
|
+
*/
|
|
870
|
+
_stageTileExit(copies, { now = false } = {}) {
|
|
871
|
+
const style = this._tileAnimStyle || TILE_ANIM_DEFAULT;
|
|
872
|
+
const ms = TILE_EXIT_MS[style];
|
|
873
|
+
if (now || !ms || copies.length === 0) return 0;
|
|
874
|
+
const speed = this._animSpeed || 1;
|
|
875
|
+
const def = TILE_ANIM_STYLES.find((s) => s.key === style);
|
|
876
|
+
copies.forEach(({ ghost, el, sessionId }, k) => {
|
|
877
|
+
ghost.style.setProperty('--tile-exit-delay', `${Math.round((k * TILE_EXIT_STAGGER_MS) / speed)}ms`);
|
|
878
|
+
if (style !== 'fly' && style !== 'deal') return;
|
|
879
|
+
const at = el.getBoundingClientRect();
|
|
880
|
+
const target = this._tileSourceRect(def.from, sessionId);
|
|
881
|
+
if (target && at.width > 0 && at.height > 0) this._setTileFlight(ghost, target, at, style, k, '--tile-to');
|
|
882
|
+
});
|
|
883
|
+
return (ms + copies.length * TILE_EXIT_STAGGER_MS) / speed + 250;
|
|
884
|
+
},
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Lab: replay the open grid's entrance IN PLACE (frames re-enter, screens
|
|
888
|
+
* light up again in queue order): no remount, reconnect or resize. With the
|
|
889
|
+
* grid closed it opens it, the real path.
|
|
890
|
+
*/
|
|
891
|
+
_demoTiles() {
|
|
892
|
+
const grid = this._tileGrid;
|
|
893
|
+
if (!grid?.open) {
|
|
894
|
+
if (this.canOpenTileGrid?.()) this.toggleTileGrid?.();
|
|
895
|
+
else this.showToast?.('The tile grid needs a window at least 1180 px wide', 'info');
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
if (!this._tileMotionAllowed?.()) return;
|
|
899
|
+
const ids = grid.ids.slice();
|
|
900
|
+
ids.forEach((id, k) => {
|
|
901
|
+
const entry = grid.tiles.get(id);
|
|
902
|
+
if (entry) this._replayTileEntrance?.(entry.el, id, k);
|
|
903
|
+
});
|
|
904
|
+
// The screens, as the load queue would land them: focused first, then reading order.
|
|
905
|
+
const speed = this._animSpeed || 1;
|
|
906
|
+
const lead = Math.min(this._tileAnimDuration() * 0.55, 420);
|
|
907
|
+
const order = [grid.focusedId, ...ids.filter((id) => id !== grid.focusedId)].filter(Boolean);
|
|
908
|
+
clearTimeout(this._tileDemoTimer);
|
|
909
|
+
const timers = order.map((id, k) =>
|
|
910
|
+
setTimeout(() => this.playTileScreenEntrance(grid.tiles.get(id)?.body), lead + (k * 140) / speed)
|
|
911
|
+
);
|
|
912
|
+
this._tileDemoTimers?.forEach(clearTimeout);
|
|
913
|
+
this._tileDemoTimers = timers;
|
|
914
|
+
},
|
|
915
|
+
|
|
916
|
+
/** Lab: close the grid with its exit, then open it again with its entrance (the real paths). */
|
|
917
|
+
_demoTilesRoundTrip() {
|
|
918
|
+
if (!this._tileGrid?.open) {
|
|
919
|
+
this._demoTiles();
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
this.toggleTileGrid?.();
|
|
923
|
+
clearTimeout(this._tileDemoTimer);
|
|
924
|
+
this._tileDemoTimer = setTimeout(
|
|
925
|
+
() => {
|
|
926
|
+
if (!this._tileGrid?.open) this.toggleTileGrid?.();
|
|
927
|
+
},
|
|
928
|
+
1400 / (this._animSpeed || 1)
|
|
929
|
+
);
|
|
930
|
+
},
|
|
931
|
+
|
|
504
932
|
// ── Lab (compare styles without spawning sessions or agents) ───────────────
|
|
505
933
|
|
|
506
934
|
/** Floating picker: switch styles per surface and replay fake entrances. */
|
|
@@ -538,6 +966,12 @@ Object.assign(CodemanApp.prototype, {
|
|
|
538
966
|
</label>
|
|
539
967
|
${group('Agent windows', WIN_ANIM_STYLES, 'win')}
|
|
540
968
|
${group('Connection lines', LINE_ANIM_STYLES, 'line')}
|
|
969
|
+
${group('Tile grid (frames; screens use the pane style)', TILE_ANIM_STYLES, 'tile')}
|
|
970
|
+
<label class="anim-lab-select">Tile order
|
|
971
|
+
<select data-select="tileOrder">
|
|
972
|
+
${TILE_ANIM_ORDERS.map((o) => `<option value="${o.key}">${escapeHtml(o.label)}</option>`).join('')}
|
|
973
|
+
</select>
|
|
974
|
+
</label>
|
|
541
975
|
</div>
|
|
542
976
|
<label class="anim-lab-range">Tab stagger <output data-out="stagger"></output>
|
|
543
977
|
<input type="range" data-range="stagger" min="0" max="260" step="10">
|
|
@@ -552,6 +986,11 @@ Object.assign(CodemanApp.prototype, {
|
|
|
552
986
|
<button type="button" data-demo="window">Window</button>
|
|
553
987
|
<button type="button" data-demo="all">All</button>
|
|
554
988
|
</div>
|
|
989
|
+
<div class="anim-lab-demo">
|
|
990
|
+
<span>Tiles</span>
|
|
991
|
+
<button type="button" data-demo="tiles">Replay</button>
|
|
992
|
+
<button type="button" data-demo="tiles-roundtrip">Close + reopen</button>
|
|
993
|
+
</div>
|
|
555
994
|
<p class="anim-lab-hint">Fake tabs, window and line, removed after the run. Real launches use the same timing.</p>
|
|
556
995
|
`;
|
|
557
996
|
document.body.appendChild(panel);
|
|
@@ -570,11 +1009,17 @@ Object.assign(CodemanApp.prototype, {
|
|
|
570
1009
|
if (attr === 'tab') this.setTabAnimStyle(style);
|
|
571
1010
|
else if (attr === 'win') this.setWinAnimStyle(style);
|
|
572
1011
|
else if (attr === 'term') this.setTermAnimStyle(style);
|
|
1012
|
+
else if (attr === 'tile') this.setTileAnimStyle(style);
|
|
573
1013
|
else this.setLineAnimStyle(style);
|
|
574
1014
|
this._syncAnimLab();
|
|
575
|
-
this.
|
|
1015
|
+
this._syncEntranceAnimSetting?.();
|
|
1016
|
+
this.demoEntrance({ tab: 'tabs', term: 'term', tile: 'tiles' }[attr] || 'all');
|
|
576
1017
|
});
|
|
577
1018
|
});
|
|
1019
|
+
panel.querySelector('select[data-select="tileOrder"]').addEventListener('change', (e) => {
|
|
1020
|
+
this.setTileAnimOrder(e.target.value);
|
|
1021
|
+
this.demoEntrance('tiles');
|
|
1022
|
+
});
|
|
578
1023
|
panel.querySelector('input[data-check="termSwitch"]').addEventListener('change', (e) => {
|
|
579
1024
|
this.setTermAnimOnSwitch(e.target.checked);
|
|
580
1025
|
});
|
|
@@ -601,19 +1046,16 @@ Object.assign(CodemanApp.prototype, {
|
|
|
601
1046
|
_syncAnimLab() {
|
|
602
1047
|
const panel = document.getElementById('animLab');
|
|
603
1048
|
if (!panel) return;
|
|
604
|
-
const current =
|
|
605
|
-
tab: this._tabAnimStyle,
|
|
606
|
-
win: this._winAnimStyle,
|
|
607
|
-
line: this._lineAnimStyle,
|
|
608
|
-
term: this._termAnimStyle,
|
|
609
|
-
};
|
|
1049
|
+
const current = this._currentAnimStyles();
|
|
610
1050
|
panel.querySelectorAll('.anim-lab-style').forEach((btn) => {
|
|
611
1051
|
btn.classList.toggle('selected', current[btn.dataset.attr] === btn.dataset.style);
|
|
612
1052
|
});
|
|
613
1053
|
panel.querySelectorAll('button[data-theme]').forEach((btn) => {
|
|
614
1054
|
const t = ANIM_THEMES.find((x) => x.key === btn.dataset.theme);
|
|
615
|
-
btn.classList.toggle('selected', !!t &&
|
|
1055
|
+
btn.classList.toggle('selected', !!t && ANIM_SURFACES.every((k) => t[k] === current[k]));
|
|
616
1056
|
});
|
|
1057
|
+
const order = panel.querySelector('select[data-select="tileOrder"]');
|
|
1058
|
+
if (order) order.value = this._tileAnimOrder || TILE_ANIM_ORDER_DEFAULT;
|
|
617
1059
|
const check = panel.querySelector('input[data-check="termSwitch"]');
|
|
618
1060
|
if (check) check.checked = !!this._termAnimOnSwitch;
|
|
619
1061
|
panel.querySelector('input[data-range="stagger"]').value = String(this._tabAnimStagger);
|
|
@@ -647,10 +1089,15 @@ Object.assign(CodemanApp.prototype, {
|
|
|
647
1089
|
return svg;
|
|
648
1090
|
},
|
|
649
1091
|
|
|
650
|
-
/** @param {'tabs'|'term'|'window'|'all'} what */
|
|
1092
|
+
/** @param {'tabs'|'term'|'window'|'all'|'tiles'|'tiles-roundtrip'} what */
|
|
651
1093
|
demoEntrance(what = 'all') {
|
|
652
1094
|
this._clearEntranceDemo();
|
|
653
1095
|
|
|
1096
|
+
if (what === 'tiles') return this._demoTiles();
|
|
1097
|
+
if (what === 'tiles-roundtrip') return this._demoTilesRoundTrip();
|
|
1098
|
+
// With the grid open, the "pane" is every tile's screen.
|
|
1099
|
+
if (what === 'term' && this._tileGrid?.open) return this._demoTiles();
|
|
1100
|
+
|
|
654
1101
|
// The pane is a real, shared element rather than a throwaway, so replay it
|
|
655
1102
|
// through the same entry point a real launch uses (bypassing the owed-id
|
|
656
1103
|
// check, which only exists to keep background sessions from hijacking it).
|
|
@@ -692,6 +1139,7 @@ Object.assign(CodemanApp.prototype, {
|
|
|
692
1139
|
<span class="tab-number">${base + i + 1}</span>
|
|
693
1140
|
<span class="tab-status idle" aria-hidden="true"></span>
|
|
694
1141
|
<span class="tab-info"><span class="tab-name-row">
|
|
1142
|
+
<span class="tab-harness run-mode-dot claude" aria-hidden="true"></span>
|
|
695
1143
|
<span class="tab-name">w${base + i + 1}-demo</span>
|
|
696
1144
|
</span></span>`;
|
|
697
1145
|
container.appendChild(tab);
|