aicodeman 1.41.0 → 1.41.1
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 +58 -8
- package/dist/attachment-registry.d.ts +2 -0
- package/dist/attachment-registry.d.ts.map +1 -1
- package/dist/attachment-registry.js +14 -3
- package/dist/attachment-registry.js.map +1 -1
- package/dist/cli-agent.d.ts +2 -0
- package/dist/cli-agent.d.ts.map +1 -1
- package/dist/cli-agent.js +12 -8
- package/dist/cli-agent.js.map +1 -1
- package/dist/cli.d.ts +25 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +97 -11
- package/dist/cli.js.map +1 -1
- package/dist/config/buffer-limits.d.ts +9 -0
- package/dist/config/buffer-limits.d.ts.map +1 -1
- package/dist/config/buffer-limits.js +9 -0
- package/dist/config/buffer-limits.js.map +1 -1
- package/dist/config/cli-registry/schema.d.ts +8 -0
- package/dist/config/cli-registry/schema.d.ts.map +1 -1
- package/dist/config/cli-registry/schema.js +12 -0
- 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 +42 -0
- package/dist/config/cli-registry/stock.js.map +1 -1
- package/dist/config/cli-registry/types.d.ts +35 -0
- package/dist/config/cli-registry/types.d.ts.map +1 -1
- package/dist/folder-listing.d.ts +94 -0
- package/dist/folder-listing.d.ts.map +1 -0
- package/dist/folder-listing.js +241 -0
- package/dist/folder-listing.js.map +1 -0
- package/dist/hooks-config.d.ts +36 -16
- package/dist/hooks-config.d.ts.map +1 -1
- package/dist/hooks-config.js +45 -20
- package/dist/hooks-config.js.map +1 -1
- package/dist/html-view-capabilities.d.ts +54 -0
- package/dist/html-view-capabilities.d.ts.map +1 -0
- package/dist/html-view-capabilities.js +103 -0
- package/dist/html-view-capabilities.js.map +1 -0
- package/dist/mcp-server.d.ts +114 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +917 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp-sync.d.ts +5 -0
- package/dist/mcp-sync.d.ts.map +1 -1
- package/dist/mcp-sync.js +51 -5
- package/dist/mcp-sync.js.map +1 -1
- package/dist/mux-interface.d.ts +36 -1
- package/dist/mux-interface.d.ts.map +1 -1
- package/dist/remote-files.d.ts +37 -0
- package/dist/remote-files.d.ts.map +1 -1
- package/dist/remote-files.js +103 -0
- package/dist/remote-files.js.map +1 -1
- package/dist/remote-pane-history.d.ts +103 -0
- package/dist/remote-pane-history.d.ts.map +1 -0
- package/dist/remote-pane-history.js +258 -0
- package/dist/remote-pane-history.js.map +1 -0
- package/dist/remote-ssh-limiter.d.ts +16 -3
- package/dist/remote-ssh-limiter.d.ts.map +1 -1
- package/dist/remote-ssh-limiter.js +37 -6
- package/dist/remote-ssh-limiter.js.map +1 -1
- package/dist/session.d.ts +29 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +53 -0
- package/dist/session.js.map +1 -1
- package/dist/tab-layout-legacy-order.d.ts.map +1 -1
- package/dist/tab-layout-legacy-order.js +72 -13
- package/dist/tab-layout-legacy-order.js.map +1 -1
- package/dist/tab-layout-service.d.ts +33 -2
- package/dist/tab-layout-service.d.ts.map +1 -1
- package/dist/tab-layout-service.js +102 -7
- package/dist/tab-layout-service.js.map +1 -1
- package/dist/tab-layout.d.ts +9 -0
- package/dist/tab-layout.d.ts.map +1 -1
- package/dist/tab-layout.js +15 -2
- package/dist/tab-layout.js.map +1 -1
- package/dist/tmux-manager.d.ts +71 -2
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +169 -18
- package/dist/tmux-manager.js.map +1 -1
- package/dist/types/session.d.ts +53 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/dist/usage-telemetry.d.ts +44 -7
- package/dist/usage-telemetry.d.ts.map +1 -1
- package/dist/usage-telemetry.js +72 -7
- package/dist/usage-telemetry.js.map +1 -1
- package/dist/web/approval-inbox.d.ts +12 -0
- package/dist/web/approval-inbox.d.ts.map +1 -1
- package/dist/web/approval-inbox.js +1 -0
- package/dist/web/approval-inbox.js.map +1 -1
- package/dist/web/middleware/auth.d.ts.map +1 -1
- package/dist/web/middleware/auth.js +33 -0
- package/dist/web/middleware/auth.js.map +1 -1
- package/dist/web/ports/session-port.d.ts +8 -1
- package/dist/web/ports/session-port.d.ts.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.24689381.js +43 -0
- package/dist/web/public/app.24689381.js.br +0 -0
- package/dist/web/public/app.24689381.js.gz +0 -0
- package/dist/web/public/approvals-ui.js +4 -0
- package/dist/web/public/approvals-ui.js.br +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/{constants.bc9c1f63.js → constants.17d77b65.js} +395 -7
- package/dist/web/public/constants.17d77b65.js.br +0 -0
- package/dist/web/public/constants.17d77b65.js.gz +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/entrance-animations.js.gz +0 -0
- package/dist/web/public/gesture/gesture-codeman.js +112 -6
- package/dist/web/public/git-status-ui.js.gz +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.9afd8e4d.js +2 -0
- package/dist/web/public/i18n.9afd8e4d.js.br +0 -0
- package/dist/web/public/i18n.9afd8e4d.js.gz +0 -0
- package/dist/web/public/{image-input.e00471c6.js → image-input.9ed1d0d2.js} +186 -16
- package/dist/web/public/image-input.9ed1d0d2.js.br +0 -0
- package/dist/web/public/image-input.9ed1d0d2.js.gz +0 -0
- package/dist/web/public/index.html +105 -23
- 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 → keyboard-accessory.c458eea8.js} +115 -35
- package/dist/web/public/keyboard-accessory.c458eea8.js.br +0 -0
- package/dist/web/public/keyboard-accessory.c458eea8.js.gz +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 +11 -2
- package/dist/web/public/mobile-overview.js.br +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/mobile.2dff1580.css +1 -0
- package/dist/web/public/mobile.2dff1580.css.br +0 -0
- package/dist/web/public/mobile.2dff1580.css.gz +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.71846fcb.js → panels-ui.73ec0137.js} +87 -87
- package/dist/web/public/panels-ui.73ec0137.js.br +0 -0
- package/dist/web/public/panels-ui.73ec0137.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.gz +0 -0
- package/dist/web/public/{session-ui.f731602c.js → session-ui.dbc6819b.js} +33 -33
- package/dist/web/public/session-ui.dbc6819b.js.br +0 -0
- package/dist/web/public/session-ui.dbc6819b.js.gz +0 -0
- package/dist/web/public/{settings-ui.4e33390f.js → settings-ui.81ac264e.js} +9 -9
- package/dist/web/public/settings-ui.81ac264e.js.br +0 -0
- package/dist/web/public/settings-ui.81ac264e.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview-worker.js +11 -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 → spreadsheet-preview.75c751de.js} +1 -1
- package/dist/web/public/spreadsheet-preview.75c751de.js.br +0 -0
- package/dist/web/public/spreadsheet-preview.75c751de.js.gz +0 -0
- package/dist/web/public/spreadsheet-xlsx-core.js +110 -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.900a28e4.css +1 -0
- package/dist/web/public/styles.900a28e4.css.br +0 -0
- package/dist/web/public/styles.900a28e4.css.gz +0 -0
- package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
- package/dist/web/public/sw.js +6 -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.2d6189f7.js +1 -0
- package/dist/web/public/tab-layout-browser.2d6189f7.js.br +0 -0
- package/dist/web/public/tab-layout-browser.2d6189f7.js.gz +0 -0
- package/dist/web/public/{tab-rail-resize.a5867793.js → tab-rail-resize.7157e0bd.js} +1 -1
- package/dist/web/public/tab-rail-resize.7157e0bd.js.br +0 -0
- package/dist/web/public/tab-rail-resize.7157e0bd.js.gz +0 -0
- package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
- package/dist/web/public/terminal-split.js.gz +0 -0
- package/dist/web/public/terminal-tile.js +44 -24
- 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.858f5da5.js +2 -0
- package/dist/web/public/terminal-ui.858f5da5.js.br +0 -0
- package/dist/web/public/terminal-ui.858f5da5.js.gz +0 -0
- package/dist/web/public/tile-grid.js +439 -18
- 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.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/exceljs.min.js.gz +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.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.2342c37b.js.gz +0 -0
- package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
- package/dist/web/public/webview-tabs.js +12 -0
- package/dist/web/public/webview-tabs.js.br +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/route-helpers.d.ts +8 -0
- package/dist/web/route-helpers.d.ts.map +1 -1
- package/dist/web/route-helpers.js +8 -0
- package/dist/web/route-helpers.js.map +1 -1
- package/dist/web/routes/admin-routes.d.ts.map +1 -1
- package/dist/web/routes/admin-routes.js +4 -1
- package/dist/web/routes/admin-routes.js.map +1 -1
- package/dist/web/routes/approval-routes.d.ts.map +1 -1
- package/dist/web/routes/approval-routes.js +6 -8
- package/dist/web/routes/approval-routes.js.map +1 -1
- package/dist/web/routes/case-routes.js +1 -1
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/file-routes.d.ts +46 -1
- package/dist/web/routes/file-routes.d.ts.map +1 -1
- package/dist/web/routes/file-routes.js +214 -27
- package/dist/web/routes/file-routes.js.map +1 -1
- package/dist/web/routes/hook-event-routes.d.ts.map +1 -1
- package/dist/web/routes/hook-event-routes.js +8 -1
- package/dist/web/routes/hook-event-routes.js.map +1 -1
- package/dist/web/routes/html-view-routes.d.ts +60 -0
- package/dist/web/routes/html-view-routes.d.ts.map +1 -0
- package/dist/web/routes/html-view-routes.js +288 -0
- package/dist/web/routes/html-view-routes.js.map +1 -0
- package/dist/web/routes/index.d.ts +1 -0
- package/dist/web/routes/index.d.ts.map +1 -1
- package/dist/web/routes/index.js +1 -0
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/reboot-restore-routes.js +2 -2
- package/dist/web/routes/reboot-restore-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts +1 -0
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +278 -56
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/status-telemetry-routes.d.ts.map +1 -1
- package/dist/web/routes/status-telemetry-routes.js +16 -2
- package/dist/web/routes/status-telemetry-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +29 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +65 -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 +62 -14
- package/dist/web/server.js.map +1 -1
- package/dist/web/sse-events.d.ts +5 -2
- package/dist/web/sse-events.d.ts.map +1 -1
- package/dist/web/sse-events.js +5 -2
- package/dist/web/sse-events.js.map +1 -1
- package/package.json +2 -1
- package/skills/codeman/SKILL.md +29 -12
- package/dist/web/public/app.6f8552e1.js +0 -43
- package/dist/web/public/app.6f8552e1.js.br +0 -0
- package/dist/web/public/app.6f8552e1.js.gz +0 -0
- package/dist/web/public/constants.bc9c1f63.js.br +0 -0
- package/dist/web/public/constants.bc9c1f63.js.gz +0 -0
- package/dist/web/public/i18n.a9840ea2.js +0 -2
- 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.e00471c6.js.br +0 -0
- package/dist/web/public/image-input.e00471c6.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.c1e7c7b7.js.br +0 -0
- package/dist/web/public/keyboard-accessory.c1e7c7b7.js.gz +0 -0
- package/dist/web/public/mobile.111b187d.css +0 -1
- package/dist/web/public/mobile.111b187d.css.br +0 -0
- package/dist/web/public/mobile.111b187d.css.gz +0 -0
- 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/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.br +0 -0
- package/dist/web/public/settings-ui.4e33390f.js.gz +0 -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/styles.81a72906.css +0 -1
- package/dist/web/public/styles.81a72906.css.br +0 -0
- package/dist/web/public/styles.81a72906.css.gz +0 -0
- package/dist/web/public/tab-layout-browser.065fcba4.js +0 -1
- package/dist/web/public/tab-layout-browser.065fcba4.js.br +0 -0
- package/dist/web/public/tab-layout-browser.065fcba4.js.gz +0 -0
- package/dist/web/public/tab-rail-resize.a5867793.js.br +0 -0
- package/dist/web/public/tab-rail-resize.a5867793.js.gz +0 -0
- package/dist/web/public/terminal-ui.b7267005.js +0 -2
- package/dist/web/public/terminal-ui.b7267005.js.br +0 -0
- package/dist/web/public/terminal-ui.b7267005.js.gz +0 -0
|
@@ -56,6 +56,26 @@ const TILE_GRID_REFIT_MS = 150;
|
|
|
56
56
|
// the grid section's padding (styles.css .tile-grid), for the drag math.
|
|
57
57
|
const TILE_DIVIDER_PX = 6;
|
|
58
58
|
const TILE_GRID_PADDING_PX = 4;
|
|
59
|
+
// What a tile's header drag (and a popped-out window's title drag) carries:
|
|
60
|
+
// the session id under a type of its own, never text, so no text field or
|
|
61
|
+
// terminal takes it as typing. Another Codeman window reads it on drop.
|
|
62
|
+
const TILE_DRAG_TYPE = 'application/x-codeman-tile';
|
|
63
|
+
// Detach Tiles: a tile dropped outside every window waits this long before it
|
|
64
|
+
// opens as a window of its own, so another window that took it (a
|
|
65
|
+
// 'tile-adopted' message on the window channel) can still say so first.
|
|
66
|
+
const TILE_TEAR_OFF_SETTLE_MS = 120;
|
|
67
|
+
// How long after its drag ended a tile can still be claimed by another
|
|
68
|
+
// window's drop (the claim and the dragend come from two windows).
|
|
69
|
+
const TILE_ADOPT_WINDOW_MS = 5000;
|
|
70
|
+
// A session docked here from another window: its old window's 'detached'
|
|
71
|
+
// announcements this soon after are stale (it is closing), not a new pop-out.
|
|
72
|
+
const TILE_DOCK_GRACE_MS = 2000;
|
|
73
|
+
// The smallest window a detached tile opens as, and roughly how much browser
|
|
74
|
+
// chrome sits above a pop-out's content (its title and address bars), so the
|
|
75
|
+
// window's own header lands near where the tile was let go.
|
|
76
|
+
const TILE_WINDOW_MIN_W = 480;
|
|
77
|
+
const TILE_WINDOW_MIN_H = 320;
|
|
78
|
+
const TILE_WINDOW_CHROME_PX = 60;
|
|
59
79
|
|
|
60
80
|
/** `minmax(0, 1fr) 6px minmax(0, 2fr) ...`: tracks with a divider track between each. */
|
|
61
81
|
function tileGridTracks(fr) {
|
|
@@ -324,12 +344,14 @@ Object.assign(CodemanApp.prototype, {
|
|
|
324
344
|
const sessionId = tileAt(e.target);
|
|
325
345
|
const files = Array.from(e.dataTransfer?.files || []);
|
|
326
346
|
if (!sessionId || files.length === 0) return;
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
347
|
+
// The one classifier every pick and drop filter reads (image-input.js):
|
|
348
|
+
// MIME first, then the name's extension, so documents and videos count too.
|
|
349
|
+
const media = files.filter((f) => this._promptAttachKind?.(f));
|
|
350
|
+
if (media.length === 0) {
|
|
351
|
+
this.showToast?.('Unsupported file type', 'error');
|
|
330
352
|
return;
|
|
331
353
|
}
|
|
332
|
-
this._uploadAndInsertImages?.(
|
|
354
|
+
this._uploadAndInsertImages?.(media, { sessionId });
|
|
333
355
|
});
|
|
334
356
|
},
|
|
335
357
|
|
|
@@ -1552,8 +1574,10 @@ Object.assign(CodemanApp.prototype, {
|
|
|
1552
1574
|
* `accepts(id)` is the target's own rule (a tile takes any session but its
|
|
1553
1575
|
* own; an empty cell takes any). A session it does not accept
|
|
1554
1576
|
* is held there too, but refused (`dropEffect: 'none'`, no highlight, so no
|
|
1555
|
-
* drop follows).
|
|
1556
|
-
*
|
|
1577
|
+
* drop follows). With Detach Tiles on, a tile dragged from ANOTHER Codeman
|
|
1578
|
+
* window (no drag of this page's own, the tile type in the drag) is taken
|
|
1579
|
+
* too and docks here (_dockForeignTile). Any other drag (a file) is left to
|
|
1580
|
+
* the grid section's own guard (_installTileFileDrop).
|
|
1557
1581
|
*/
|
|
1558
1582
|
_acceptTabDrops(el, onDrop, { accepts = () => true } = {}) {
|
|
1559
1583
|
const dragged = () => (this._tileGrid?.open ? this.draggedTabId || this._draggedTileId || null : null);
|
|
@@ -1561,7 +1585,16 @@ Object.assign(CodemanApp.prototype, {
|
|
|
1561
1585
|
'dragover',
|
|
1562
1586
|
(e) => {
|
|
1563
1587
|
const id = dragged();
|
|
1564
|
-
if (!id)
|
|
1588
|
+
if (!id) {
|
|
1589
|
+
// A tile from another Codeman window (Detach Tiles). Its id can
|
|
1590
|
+
// only be read on drop, so it is held here whatever it is.
|
|
1591
|
+
if (!this._isForeignTileDrag(e)) return;
|
|
1592
|
+
e.preventDefault?.();
|
|
1593
|
+
e.stopPropagation?.();
|
|
1594
|
+
if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
|
|
1595
|
+
el.classList.add('tile--drop-target');
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1565
1598
|
e.preventDefault?.();
|
|
1566
1599
|
e.stopPropagation?.();
|
|
1567
1600
|
const ok = accepts(id);
|
|
@@ -1578,7 +1611,13 @@ Object.assign(CodemanApp.prototype, {
|
|
|
1578
1611
|
(e) => {
|
|
1579
1612
|
el.classList.remove('tile--drop-target');
|
|
1580
1613
|
const id = dragged();
|
|
1581
|
-
if (!id)
|
|
1614
|
+
if (!id) {
|
|
1615
|
+
if (!this._isForeignTileDrag(e)) return;
|
|
1616
|
+
e.preventDefault?.();
|
|
1617
|
+
e.stopPropagation?.();
|
|
1618
|
+
this._dockForeignTile(e.dataTransfer?.getData?.(TILE_DRAG_TYPE), onDrop);
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1582
1621
|
e.preventDefault?.();
|
|
1583
1622
|
e.stopPropagation?.();
|
|
1584
1623
|
if (accepts(id)) onDrop(id);
|
|
@@ -2080,7 +2119,10 @@ Object.assign(CodemanApp.prototype, {
|
|
|
2080
2119
|
* input starts no drag. dragstart's target is the header whatever was
|
|
2081
2120
|
* pressed, so where the press landed is noted in the capture phase, ahead of
|
|
2082
2121
|
* the buttons' own stopPropagation. `draggable` is off while moving is
|
|
2083
|
-
* (_paintTileHandle: a zoomed grid, a single tile, a rename in progress)
|
|
2122
|
+
* (_paintTileHandle: a zoomed grid, a single tile, a rename in progress),
|
|
2123
|
+
* unless Detach Tiles is on: then a drop outside the window pops the tile
|
|
2124
|
+
* out, and a drop on another Codeman window's tiles moves it there (see the
|
|
2125
|
+
* Detach Tiles section); a rename still turns it off.
|
|
2084
2126
|
*/
|
|
2085
2127
|
_installTileMoveDrag(sessionId, header, actions) {
|
|
2086
2128
|
let pressedOnControl = false;
|
|
@@ -2094,7 +2136,10 @@ Object.assign(CodemanApp.prototype, {
|
|
|
2094
2136
|
);
|
|
2095
2137
|
header.addEventListener('dragstart', (e) => {
|
|
2096
2138
|
const entry = this._tileGrid?.open ? this._tileGrid.tiles.get(sessionId) : null;
|
|
2097
|
-
|
|
2139
|
+
// Detach Tiles lets a tile that cannot move (alone, or zoomed) still be
|
|
2140
|
+
// dragged, out of the window; no drop target here takes it then.
|
|
2141
|
+
const detachable = this._tileDetachAllowed();
|
|
2142
|
+
if (!entry || pressedOnControl || entry.renaming || !(this._tilesMovable() || detachable)) {
|
|
2098
2143
|
e.preventDefault?.();
|
|
2099
2144
|
return;
|
|
2100
2145
|
}
|
|
@@ -2102,11 +2147,16 @@ Object.assign(CodemanApp.prototype, {
|
|
|
2102
2147
|
entry.el.classList.add('tile--dragging');
|
|
2103
2148
|
if (e.dataTransfer) {
|
|
2104
2149
|
e.dataTransfer.effectAllowed = 'move';
|
|
2105
|
-
// Firefox starts no drag without data.
|
|
2106
|
-
|
|
2150
|
+
// Firefox starts no drag without data. Another Codeman window reads
|
|
2151
|
+
// the id from it (Detach Tiles).
|
|
2152
|
+
e.dataTransfer.setData(TILE_DRAG_TYPE, sessionId);
|
|
2107
2153
|
}
|
|
2154
|
+
if (detachable) this._beginTileTearOff(sessionId, entry, e);
|
|
2155
|
+
});
|
|
2156
|
+
header.addEventListener('dragend', (e) => {
|
|
2157
|
+
this._settleTileTearOff(sessionId, e);
|
|
2158
|
+
this._endTileMoveDrag();
|
|
2108
2159
|
});
|
|
2109
|
-
header.addEventListener('dragend', () => this._endTileMoveDrag());
|
|
2110
2160
|
},
|
|
2111
2161
|
|
|
2112
2162
|
/**
|
|
@@ -2117,6 +2167,7 @@ Object.assign(CodemanApp.prototype, {
|
|
|
2117
2167
|
*/
|
|
2118
2168
|
_endTileMoveDrag() {
|
|
2119
2169
|
this._draggedTileId = null;
|
|
2170
|
+
this._stopTileTearOffTracking();
|
|
2120
2171
|
const grid = this._tileGrid;
|
|
2121
2172
|
for (const { el } of grid?.tiles.values() || []) el.classList.remove('tile--dragging', 'tile--drop-target');
|
|
2122
2173
|
for (const slot of grid?.slots || []) slot.classList.remove('tile--drop-target');
|
|
@@ -2124,19 +2175,25 @@ Object.assign(CodemanApp.prototype, {
|
|
|
2124
2175
|
|
|
2125
2176
|
/**
|
|
2126
2177
|
* The header as a handle: `draggable` while the tile can move (not while a
|
|
2127
|
-
* tile is zoomed, alone, or being renamed),
|
|
2128
|
-
*
|
|
2129
|
-
* the
|
|
2178
|
+
* tile is zoomed, alone, or being renamed) or, with Detach Tiles on, be
|
|
2179
|
+
* dragged out of the window (then also alone or zoomed), and its tooltip,
|
|
2180
|
+
* the state and how long ("working 3m") plus what a drag does. Diffs on the
|
|
2181
|
+
* last English text, never the DOM (translated in zh-CN; see
|
|
2130
2182
|
* _renderTileOverlay).
|
|
2131
2183
|
*/
|
|
2132
2184
|
_paintTileHandle(entry) {
|
|
2133
2185
|
const movable = this._tilesMovable();
|
|
2134
|
-
const
|
|
2186
|
+
const detachable = this._tileDetachAllowed();
|
|
2187
|
+
let hint = '';
|
|
2188
|
+
if (movable && detachable) hint = 'Drag to move the tile, or out of the window to open it on its own';
|
|
2189
|
+
else if (movable) hint = 'Drag to move the tile';
|
|
2190
|
+
else if (detachable) hint = 'Drag out of the window to open the tile on its own';
|
|
2191
|
+
const title = [entry.stateLabel, hint].filter(Boolean).join('\n');
|
|
2135
2192
|
if (entry.headerLabel !== title) {
|
|
2136
2193
|
entry.headerLabel = title;
|
|
2137
2194
|
entry.header.title = title;
|
|
2138
2195
|
}
|
|
2139
|
-
const draggable = movable && !entry.renaming ? 'true' : 'false';
|
|
2196
|
+
const draggable = (movable || detachable) && !entry.renaming ? 'true' : 'false';
|
|
2140
2197
|
if (entry.header.getAttribute('draggable') !== draggable) entry.header.setAttribute('draggable', draggable);
|
|
2141
2198
|
},
|
|
2142
2199
|
|
|
@@ -2859,6 +2916,370 @@ Object.assign(CodemanApp.prototype, {
|
|
|
2859
2916
|
for (const { tile } of grid.tiles.values()) tile.reconnectNow();
|
|
2860
2917
|
return true;
|
|
2861
2918
|
},
|
|
2919
|
+
|
|
2920
|
+
// ── Detach Tiles (per-device `tileDetachEnabled`, default OFF) ───────────
|
|
2921
|
+
//
|
|
2922
|
+
// A tile leaves the grid for a window of its own, and comes back into any
|
|
2923
|
+
// Codeman window's grid, by dragging:
|
|
2924
|
+
//
|
|
2925
|
+
// - its header dropped OUTSIDE every window opens it as a pop-out window
|
|
2926
|
+
// (detachSession, the tab's own "Open in a new window"), at the drop point
|
|
2927
|
+
// and the tile's size;
|
|
2928
|
+
// - its header dropped on ANOTHER Codeman window's tile or empty cell moves
|
|
2929
|
+
// it there: that window docks it and says so on the window channel
|
|
2930
|
+
// ('tile-adopted'), and this one lets the tile go;
|
|
2931
|
+
// - a pop-out window's title dropped on a window's tile or empty cell docks
|
|
2932
|
+
// the session there, and the pop-out closes.
|
|
2933
|
+
//
|
|
2934
|
+
// Nothing new on the server: a pop-out is another client of the same session
|
|
2935
|
+
// (see detachSession), and a tile is a TerminalTile in whichever window has it.
|
|
2936
|
+
// A session stays in ONE place in this browser, as with every pop-out.
|
|
2937
|
+
|
|
2938
|
+
/** Whether Detach Tiles is on for this device. */
|
|
2939
|
+
tileDetachEnabled() {
|
|
2940
|
+
return this.loadAppSettingsFromStorage?.()?.tileDetachEnabled === true;
|
|
2941
|
+
},
|
|
2942
|
+
|
|
2943
|
+
/** Detach Tiles applies here: on, and a dashboard (a pop-out has no grid to drag from). */
|
|
2944
|
+
_tileDetachAllowed() {
|
|
2945
|
+
return !this.isSoloWindow && this.tileDetachEnabled();
|
|
2946
|
+
},
|
|
2947
|
+
|
|
2948
|
+
/**
|
|
2949
|
+
* A drag over this page that is a tile from another Codeman window: none of
|
|
2950
|
+
* this page's own drags is in flight, Detach Tiles is on and the drag
|
|
2951
|
+
* carries the tile type (its id is only readable on drop).
|
|
2952
|
+
*/
|
|
2953
|
+
_isForeignTileDrag(e) {
|
|
2954
|
+
if (this.draggedTabId || this._draggedTileId || !this._tileGrid?.open || !this._tileDetachAllowed()) return false;
|
|
2955
|
+
const types = e?.dataTransfer?.types;
|
|
2956
|
+
return !!types && Array.from(types).includes(TILE_DRAG_TYPE);
|
|
2957
|
+
},
|
|
2958
|
+
|
|
2959
|
+
/**
|
|
2960
|
+
* A tile from another window dropped on one of this grid's targets: it docks
|
|
2961
|
+
* through the target's own drop (`place`: a tile it replaces or trades
|
|
2962
|
+
* places with, an empty cell it fills), and takes focus there (a human
|
|
2963
|
+
* selection: the user just put it there). A session that was popped out comes
|
|
2964
|
+
* back first (its window is asked to close). Once it is a tile here, every
|
|
2965
|
+
* other window hears 'tile-adopted', and the one it was dragged from lets
|
|
2966
|
+
* its tile go (_onTileAdoptedElsewhere). An id this page does not know (a
|
|
2967
|
+
* session closed meanwhile, another user's) docks nowhere.
|
|
2968
|
+
*
|
|
2969
|
+
* @returns {boolean} whether the session is a tile here afterwards
|
|
2970
|
+
*/
|
|
2971
|
+
_dockForeignTile(sessionId, place) {
|
|
2972
|
+
const grid = this._tileGrid;
|
|
2973
|
+
if (!grid?.open || typeof sessionId !== 'string' || !this.sessions.has(sessionId)) return false;
|
|
2974
|
+
const poppedOut = !!this.detachedSessions?.has(sessionId);
|
|
2975
|
+
// A pop-out window owns its session's place: take it back before mounting
|
|
2976
|
+
// (a detached session never mounts).
|
|
2977
|
+
if (poppedOut) this._redock?.(sessionId);
|
|
2978
|
+
// Already a tile here too (two windows showed it): it moves to where it
|
|
2979
|
+
// was dropped, as a drag inside this window would move it.
|
|
2980
|
+
place(sessionId);
|
|
2981
|
+
if (!grid.has(sessionId)) return false;
|
|
2982
|
+
(this._tileDockedAt ||= new Map()).set(sessionId, Date.now());
|
|
2983
|
+
// Asked unconditionally: a pop-out this window never heard of (opened from
|
|
2984
|
+
// another dashboard) closes too. Dashboards ignore it; only that id's
|
|
2985
|
+
// pop-out reacts.
|
|
2986
|
+
this._postWindowMessage?.({ type: 'close-request', id: sessionId });
|
|
2987
|
+
this._postWindowMessage?.({ type: 'tile-adopted', id: sessionId, by: this._wsTabNonce });
|
|
2988
|
+
return true;
|
|
2989
|
+
},
|
|
2990
|
+
|
|
2991
|
+
/**
|
|
2992
|
+
* True for a session this window docked from another one a moment ago: a
|
|
2993
|
+
* 'detached' announcement from its old pop-out is that window on its way
|
|
2994
|
+
* out, never a new pop-out (it would take the tile straight back off).
|
|
2995
|
+
*/
|
|
2996
|
+
_tileDockedRecently(sessionId) {
|
|
2997
|
+
const at = this._tileDockedAt?.get(sessionId);
|
|
2998
|
+
return typeof at === 'number' && Date.now() - at < TILE_DOCK_GRACE_MS;
|
|
2999
|
+
},
|
|
3000
|
+
|
|
3001
|
+
/**
|
|
3002
|
+
* Another window docked `sessionId` ('tile-adopted'). Only the window it
|
|
3003
|
+
* was dragged FROM acts: its drag is still settling or ended a moment ago.
|
|
3004
|
+
* The tile goes without taking the keyboard anywhere (the user is in the
|
|
3005
|
+
* other window now). The last tile leaving closes the grid onto the welcome
|
|
3006
|
+
* screen, never onto that session: two windows sizing one PTY garble it.
|
|
3007
|
+
*/
|
|
3008
|
+
_onTileAdoptedElsewhere(sessionId) {
|
|
3009
|
+
const recent = this._lastTileDrag;
|
|
3010
|
+
// A header drag counts even without a tear-off (Detach Tiles off here): the
|
|
3011
|
+
// other window can take the tile before this drag ends.
|
|
3012
|
+
const dragging = this._tileTearOff?.id === sessionId || this._draggedTileId === sessionId;
|
|
3013
|
+
const pending = this._tileTearOffPending?.id === sessionId;
|
|
3014
|
+
const justEnded = recent?.id === sessionId && Date.now() - recent.at < TILE_ADOPT_WINDOW_MS;
|
|
3015
|
+
if (!dragging && !pending && !justEnded) return false;
|
|
3016
|
+
this._cancelTileTearOff();
|
|
3017
|
+
this._lastTileDrag = null;
|
|
3018
|
+
const grid = this._tileGrid;
|
|
3019
|
+
if (!grid?.has(sessionId)) return false;
|
|
3020
|
+
// One stored grid per browser, and the window that docked the tile has
|
|
3021
|
+
// just written it: this window losing the tile is no change of the
|
|
3022
|
+
// user's to remember over that one (nor to forget it).
|
|
3023
|
+
this._tilePersistHold = true;
|
|
3024
|
+
try {
|
|
3025
|
+
if (grid.ids.length > 1) return this.removeTile(sessionId, { focus: false });
|
|
3026
|
+
this.closeTileGrid({ keepStored: true, reselect: false });
|
|
3027
|
+
} finally {
|
|
3028
|
+
this._tilePersistHold = false;
|
|
3029
|
+
}
|
|
3030
|
+
this.activeSessionId = null;
|
|
3031
|
+
try {
|
|
3032
|
+
localStorage.removeItem('codeman-active-session');
|
|
3033
|
+
} catch {
|
|
3034
|
+
/* Nothing stored. */
|
|
3035
|
+
}
|
|
3036
|
+
this.terminal?.clear();
|
|
3037
|
+
this.showWelcome?.();
|
|
3038
|
+
return true;
|
|
3039
|
+
},
|
|
3040
|
+
|
|
3041
|
+
/**
|
|
3042
|
+
* A header drag starts with Detach Tiles on: note the tile's size and where
|
|
3043
|
+
* in it the press landed (the pop-out opens that size, under the pointer),
|
|
3044
|
+
* and watch whether the pointer leaves the page. Two signals, since
|
|
3045
|
+
* browsers differ in what dragend reports for a drop outside the window:
|
|
3046
|
+
* its coordinates, and the page's own dragleave at the viewport's edge
|
|
3047
|
+
* with nothing entered (cleared again by any dragover inside).
|
|
3048
|
+
*/
|
|
3049
|
+
_beginTileTearOff(sessionId, entry, e) {
|
|
3050
|
+
this._stopTileTearOffTracking();
|
|
3051
|
+
const rect = entry.el.getBoundingClientRect();
|
|
3052
|
+
const within = (v, max) => (Number.isFinite(v) ? Math.max(0, Math.min(v, max)) : 0);
|
|
3053
|
+
const drag = {
|
|
3054
|
+
id: sessionId,
|
|
3055
|
+
width: rect.width,
|
|
3056
|
+
height: rect.height,
|
|
3057
|
+
offsetX: within(e?.clientX - rect.left, rect.width),
|
|
3058
|
+
offsetY: within(e?.clientY - rect.top, rect.height),
|
|
3059
|
+
outside: false,
|
|
3060
|
+
stop: null,
|
|
3061
|
+
};
|
|
3062
|
+
const onOver = () => {
|
|
3063
|
+
drag.outside = false;
|
|
3064
|
+
};
|
|
3065
|
+
const onLeave = (ev) => {
|
|
3066
|
+
if (ev.relatedTarget) return;
|
|
3067
|
+
const x = ev.clientX;
|
|
3068
|
+
const y = ev.clientY;
|
|
3069
|
+
if (x <= 0 || y <= 0 || x >= window.innerWidth - 1 || y >= window.innerHeight - 1) drag.outside = true;
|
|
3070
|
+
};
|
|
3071
|
+
document.addEventListener('dragover', onOver, true);
|
|
3072
|
+
document.addEventListener('dragleave', onLeave, true);
|
|
3073
|
+
drag.stop = () => {
|
|
3074
|
+
document.removeEventListener('dragover', onOver, true);
|
|
3075
|
+
document.removeEventListener('dragleave', onLeave, true);
|
|
3076
|
+
};
|
|
3077
|
+
this._tileTearOff = drag;
|
|
3078
|
+
},
|
|
3079
|
+
|
|
3080
|
+
/** The header drag is over (or the grid or tile went mid-drag): stop watching the pointer. */
|
|
3081
|
+
_stopTileTearOffTracking() {
|
|
3082
|
+
const drag = this._tileTearOff;
|
|
3083
|
+
if (!drag) return;
|
|
3084
|
+
this._tileTearOff = null;
|
|
3085
|
+
drag.stop?.();
|
|
3086
|
+
},
|
|
3087
|
+
|
|
3088
|
+
/**
|
|
3089
|
+
* dragend of a tile's header with Detach Tiles on. Taken by a drop target
|
|
3090
|
+
* (here or in another window: `dropEffect` is not 'none'), it stays where
|
|
3091
|
+
* that put it. Let go OUTSIDE the window (the dragend point, or the page's
|
|
3092
|
+
* own record of the pointer leaving), it opens as a window of its own after
|
|
3093
|
+
* TILE_TEAR_OFF_SETTLE_MS, unless another window claims it first. Let go
|
|
3094
|
+
* inside, on nothing, nothing happens, as before.
|
|
3095
|
+
*/
|
|
3096
|
+
_settleTileTearOff(sessionId, e) {
|
|
3097
|
+
const drag = this._tileTearOff?.id === sessionId ? this._tileTearOff : null;
|
|
3098
|
+
this._lastTileDrag = { id: sessionId, at: Date.now() };
|
|
3099
|
+
if (!drag) return false;
|
|
3100
|
+
this._stopTileTearOffTracking();
|
|
3101
|
+
const effect = e?.dataTransfer?.dropEffect;
|
|
3102
|
+
if (effect && effect !== 'none') return false;
|
|
3103
|
+
const cx = e?.clientX;
|
|
3104
|
+
const cy = e?.clientY;
|
|
3105
|
+
// (0, 0) is what a browser reports when it does not know where the drop was.
|
|
3106
|
+
const known = Number.isFinite(cx) && Number.isFinite(cy) && !(cx === 0 && cy === 0);
|
|
3107
|
+
const outside = known ? cx < 0 || cy < 0 || cx >= window.innerWidth || cy >= window.innerHeight : drag.outside;
|
|
3108
|
+
if (!outside) return false;
|
|
3109
|
+
const sx = e?.screenX;
|
|
3110
|
+
const sy = e?.screenY;
|
|
3111
|
+
const placed = Number.isFinite(sx) && Number.isFinite(sy) && !(sx === 0 && sy === 0);
|
|
3112
|
+
const placement = {
|
|
3113
|
+
width: drag.width,
|
|
3114
|
+
height: drag.height,
|
|
3115
|
+
...(placed ? { screenX: sx, screenY: sy, offsetX: drag.offsetX, offsetY: drag.offsetY } : {}),
|
|
3116
|
+
};
|
|
3117
|
+
this._cancelTileTearOff();
|
|
3118
|
+
const pending = { id: sessionId, timer: null };
|
|
3119
|
+
pending.timer = setTimeout(() => {
|
|
3120
|
+
if (this._tileTearOffPending !== pending) return;
|
|
3121
|
+
this._tileTearOffPending = null;
|
|
3122
|
+
this.detachTile(sessionId, placement);
|
|
3123
|
+
}, TILE_TEAR_OFF_SETTLE_MS);
|
|
3124
|
+
this._tileTearOffPending = pending;
|
|
3125
|
+
return true;
|
|
3126
|
+
},
|
|
3127
|
+
|
|
3128
|
+
_cancelTileTearOff() {
|
|
3129
|
+
const pending = this._tileTearOffPending;
|
|
3130
|
+
if (!pending) return;
|
|
3131
|
+
this._tileTearOffPending = null;
|
|
3132
|
+
clearTimeout(pending.timer);
|
|
3133
|
+
},
|
|
3134
|
+
|
|
3135
|
+
/**
|
|
3136
|
+
* Opens a tile as a window of its own (detachSession, which takes it out of
|
|
3137
|
+
* the grid), the tile's size (at least TILE_WINDOW_MIN_W x MIN_H). Placed
|
|
3138
|
+
* with its header under a screen point when given one (`screenX`/`screenY`
|
|
3139
|
+
* and where in the tile it was held, `offsetX`/`offsetY`: a drop outside the
|
|
3140
|
+
* window, a hand let go), otherwise over the tile itself (the ⋯ menu).
|
|
3141
|
+
*
|
|
3142
|
+
* @returns {boolean} whether the session is popped out afterwards
|
|
3143
|
+
*/
|
|
3144
|
+
detachTile(sessionId, { screenX, screenY, offsetX = 0, offsetY = 0, width, height } = {}) {
|
|
3145
|
+
const entry = this._tileGrid?.open ? this._tileGrid.tiles.get(sessionId) : null;
|
|
3146
|
+
if (!entry || !this._tileDetachAllowed()) return false;
|
|
3147
|
+
const rect = entry.el.getBoundingClientRect();
|
|
3148
|
+
const availW = window.screen?.availWidth || Infinity;
|
|
3149
|
+
const availH = window.screen?.availHeight || Infinity;
|
|
3150
|
+
const size = (v, min, max) => Math.round(Math.min(Math.max(Number.isFinite(v) ? v : min, min), max));
|
|
3151
|
+
const placement = {
|
|
3152
|
+
width: size(width ?? rect.width, TILE_WINDOW_MIN_W, availW),
|
|
3153
|
+
height: size(height ?? rect.height, TILE_WINDOW_MIN_H, availH),
|
|
3154
|
+
};
|
|
3155
|
+
if (Number.isFinite(screenX) && Number.isFinite(screenY)) {
|
|
3156
|
+
placement.left = Math.round(screenX - offsetX);
|
|
3157
|
+
placement.top = Math.round(screenY - offsetY - TILE_WINDOW_CHROME_PX);
|
|
3158
|
+
} else if (Number.isFinite(window.screenX) && Number.isFinite(window.screenY)) {
|
|
3159
|
+
// Over the tile: the page's own chrome sits above its viewport.
|
|
3160
|
+
const chrome = Math.max(0, (window.outerHeight || 0) - (window.innerHeight || 0));
|
|
3161
|
+
placement.left = Math.round(window.screenX + rect.left);
|
|
3162
|
+
placement.top = Math.round(window.screenY + chrome + rect.top - TILE_WINDOW_CHROME_PX);
|
|
3163
|
+
}
|
|
3164
|
+
this.detachSession?.(sessionId, placement);
|
|
3165
|
+
return !!this.detachedSessions?.has(sessionId);
|
|
3166
|
+
},
|
|
3167
|
+
|
|
3168
|
+
// ── For the gesture overlay (packages/gesture-control, src/codeman/entry.ts)
|
|
3169
|
+
//
|
|
3170
|
+
// A hand is no native drag: the overlay asks where it is and what is under
|
|
3171
|
+
// it, and every move still goes through the paths a mouse drag takes
|
|
3172
|
+
// (dropSessionOnTile / dropSessionOnSlot, and so _reorderTiles), the
|
|
3173
|
+
// pop-out through detachTile.
|
|
3174
|
+
|
|
3175
|
+
/** The tiled session whose tile is at viewport point (x, y), or null. */
|
|
3176
|
+
tileAtPoint(x, y) {
|
|
3177
|
+
const grid = this._tileGrid;
|
|
3178
|
+
if (!grid?.open) return null;
|
|
3179
|
+
const hit = document.elementFromPoint?.(x, y);
|
|
3180
|
+
if (!hit) return null;
|
|
3181
|
+
for (const [id, entry] of grid.tiles) if (entry.el.contains?.(hit)) return id;
|
|
3182
|
+
return null;
|
|
3183
|
+
},
|
|
3184
|
+
|
|
3185
|
+
/** Whether a hand may pick up this tile: it can move, or (Detach Tiles) leave the window. */
|
|
3186
|
+
canGrabTile(sessionId) {
|
|
3187
|
+
return !!this._tileGrid?.has(sessionId) && (this._tilesMovable() || this._tileDetachAllowed());
|
|
3188
|
+
},
|
|
3189
|
+
|
|
3190
|
+
/**
|
|
3191
|
+
* What a session dragged to viewport point (x, y) would drop onto, by the
|
|
3192
|
+
* mouse drag's own rules: another tile (it takes that tile's place, or the
|
|
3193
|
+
* two trade places), an empty cell (it moves or joins there). A tiled
|
|
3194
|
+
* session only while tiles can move (not zoomed). Null for anything else,
|
|
3195
|
+
* a popped-out session included.
|
|
3196
|
+
*
|
|
3197
|
+
* @returns {{kind: 'tile', id: string, el: HTMLElement}|{kind: 'cell', cell: number, el: HTMLElement}|null}
|
|
3198
|
+
*/
|
|
3199
|
+
tileDropTargetAt(x, y, draggedId) {
|
|
3200
|
+
const grid = this._tileGrid;
|
|
3201
|
+
if (!grid?.open || !this.sessions.has(draggedId) || this.detachedSessions?.has(draggedId)) return null;
|
|
3202
|
+
if (grid.tiles.has(draggedId) && !this._tilesMovable()) return null;
|
|
3203
|
+
const hit = document.elementFromPoint?.(x, y);
|
|
3204
|
+
if (!hit) return null;
|
|
3205
|
+
for (const [id, entry] of grid.tiles) {
|
|
3206
|
+
if (entry.el.contains?.(hit)) return id === draggedId ? null : { kind: 'tile', id, el: entry.el };
|
|
3207
|
+
}
|
|
3208
|
+
for (const slot of grid.slots || []) {
|
|
3209
|
+
if (slot.contains?.(hit)) return { kind: 'cell', cell: Number(slot.dataset.cell), el: slot };
|
|
3210
|
+
}
|
|
3211
|
+
return null;
|
|
3212
|
+
},
|
|
3213
|
+
|
|
3214
|
+
/**
|
|
3215
|
+
* Drops a session on a target tileDropTargetAt returned (the hand let go).
|
|
3216
|
+
*
|
|
3217
|
+
* @returns {boolean} whether the session is a tile afterwards
|
|
3218
|
+
*/
|
|
3219
|
+
dropOnTileTarget(draggedId, target) {
|
|
3220
|
+
if (!target || !this._tileGrid?.open) return false;
|
|
3221
|
+
if (target.kind === 'tile') this.dropSessionOnTile(draggedId, target.id);
|
|
3222
|
+
else if (target.kind === 'cell') this.dropSessionOnSlot(draggedId, target.cell);
|
|
3223
|
+
return !!this._tileGrid?.has(draggedId);
|
|
3224
|
+
},
|
|
3225
|
+
|
|
3226
|
+
/** Whether viewport point (x, y) is over the open grid (a hand let go outside it pops the tile out). */
|
|
3227
|
+
isOverTileGrid(x, y) {
|
|
3228
|
+
if (!this._tileGrid?.open) return false;
|
|
3229
|
+
const rect = document.getElementById('tileGrid')?.getBoundingClientRect?.();
|
|
3230
|
+
return !!rect && x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;
|
|
3231
|
+
},
|
|
3232
|
+
|
|
3233
|
+
/**
|
|
3234
|
+
* A hand let a tile go outside the grid (Detach Tiles): it opens as a window
|
|
3235
|
+
* of its own with its header near that point. A script opening a window
|
|
3236
|
+
* with no click behind it is what popup blockers stop; detachSession then
|
|
3237
|
+
* says so and the tile stays.
|
|
3238
|
+
*/
|
|
3239
|
+
detachTileAtPoint(sessionId, x, y, { offsetX = 0, offsetY = 0 } = {}) {
|
|
3240
|
+
const chrome = Math.max(0, (window.outerHeight || 0) - (window.innerHeight || 0));
|
|
3241
|
+
const sx = (window.screenX || 0) + x;
|
|
3242
|
+
const sy = (window.screenY || 0) + chrome + y;
|
|
3243
|
+
return this.detachTile(sessionId, { screenX: sx, screenY: sy, offsetX, offsetY });
|
|
3244
|
+
},
|
|
3245
|
+
|
|
3246
|
+
/**
|
|
3247
|
+
* A pop-out window's title, as a handle (Detach Tiles): dragged onto a
|
|
3248
|
+
* Codeman window's tile or empty cell, the session docks there as a tile
|
|
3249
|
+
* and this window closes (_dockForeignTile, which asks it to). The same
|
|
3250
|
+
* drag type as a tile's header; on or off follows the setting, also when it
|
|
3251
|
+
* changes in another window.
|
|
3252
|
+
*/
|
|
3253
|
+
_installSoloTileHandle() {
|
|
3254
|
+
const title = document.getElementById('soloSessionTitle');
|
|
3255
|
+
if (!this.isSoloWindow || !title || title._codemanTileHandle) return;
|
|
3256
|
+
title._codemanTileHandle = true;
|
|
3257
|
+
const sync = () => {
|
|
3258
|
+
this._cachedAppSettings = null;
|
|
3259
|
+
const on = this.tileDetachEnabled();
|
|
3260
|
+
title.setAttribute('draggable', on ? 'true' : 'false');
|
|
3261
|
+
title.classList.toggle('solo-session-title--handle', on);
|
|
3262
|
+
const label = on ? "Drag onto a Codeman window's tiles to dock this session there" : '';
|
|
3263
|
+
if (title._codemanTileHandleLabel !== label) {
|
|
3264
|
+
title._codemanTileHandleLabel = label;
|
|
3265
|
+
title.title = label;
|
|
3266
|
+
}
|
|
3267
|
+
};
|
|
3268
|
+
sync();
|
|
3269
|
+
window.addEventListener('storage', (e) => {
|
|
3270
|
+
if (!e.key || e.key === this.getSettingsStorageKey?.()) sync();
|
|
3271
|
+
});
|
|
3272
|
+
title.addEventListener('dragstart', (e) => {
|
|
3273
|
+
if (!this.tileDetachEnabled() || !this.soloSessionId) {
|
|
3274
|
+
e.preventDefault?.();
|
|
3275
|
+
return;
|
|
3276
|
+
}
|
|
3277
|
+
if (e.dataTransfer) {
|
|
3278
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
3279
|
+
e.dataTransfer.setData(TILE_DRAG_TYPE, this.soloSessionId);
|
|
3280
|
+
}
|
|
3281
|
+
});
|
|
3282
|
+
},
|
|
2862
3283
|
});
|
|
2863
3284
|
|
|
2864
3285
|
// A tiled session deleted (here or elsewhere) loses its tile; if it held focus,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -325,6 +325,7 @@ Object.assign(CodemanApp.prototype, {
|
|
|
325
325
|
|
|
326
326
|
return `<div class="session-tab session-tab--web ${isActive ? 'active' : ''}" data-webview-id="${escapeHtml(id)}"
|
|
327
327
|
onclick="app.handleWebviewTabClick(event, ${jsonId})"
|
|
328
|
+
oncontextmenu="app.handleWebviewTabContextMenu(event, ${jsonId})"
|
|
328
329
|
tabindex="0" role="tab" aria-selected="${isActive ? 'true' : 'false'}"
|
|
329
330
|
aria-label="${escapeHtml(webview.name)} web tab" title="${escapeHtml(webview.url)}">
|
|
330
331
|
${idx < 9 ? '<span class="tab-number">' + (idx + 1) + '</span>' : ''}
|
|
@@ -347,6 +348,16 @@ Object.assign(CodemanApp.prototype, {
|
|
|
347
348
|
return this.openWebview(id);
|
|
348
349
|
},
|
|
349
350
|
|
|
351
|
+
/**
|
|
352
|
+
* Right-click (or the menu key) on a web tab in the vertical rail opens its
|
|
353
|
+
* actions (Focus, group moves, settings), the flat rail included, where it is
|
|
354
|
+
* the only way in. Everywhere else the browser keeps its own menu.
|
|
355
|
+
*/
|
|
356
|
+
handleWebviewTabContextMenu(event, id) {
|
|
357
|
+
if (!this._tabLayoutEditable?.()) return false;
|
|
358
|
+
return this.openTabWebviewMenu?.(event, id);
|
|
359
|
+
},
|
|
360
|
+
|
|
350
361
|
/** Mark exactly one tab active across BOTH tab kinds. */
|
|
351
362
|
_updateActiveWebviewTab() {
|
|
352
363
|
const container = this.$('sessionTabs');
|
|
@@ -359,6 +370,7 @@ Object.assign(CodemanApp.prototype, {
|
|
|
359
370
|
for (const tab of container.querySelectorAll('.session-tab[data-id]')) tab.classList.remove('active');
|
|
360
371
|
}
|
|
361
372
|
this._syncTabTreeSelection?.(container);
|
|
373
|
+
this._renderTabFocus?.();
|
|
362
374
|
},
|
|
363
375
|
|
|
364
376
|
// ── Opening / closing ─────────────────────────────────────────────────────
|
|
Binary file
|
|
Binary file
|
|
@@ -10,6 +10,14 @@ import { Session } from '../session.js';
|
|
|
10
10
|
import { type AuthUser, type SessionState } from '../types.js';
|
|
11
11
|
import type { SessionPort } from './ports/session-port.js';
|
|
12
12
|
import type { EventPort } from './ports/event-port.js';
|
|
13
|
+
/**
|
|
14
|
+
* Longest path PARAMETER the router accepts (Fastify's default is 100). A file
|
|
15
|
+
* name goes up to 255 characters and arrives percent-encoded, so the folder
|
|
16
|
+
* grid's `…/files/:name/raw` answered 404 "Route not found" for a name the
|
|
17
|
+
* listing happily showed. Raw length, not decoded: 255 four-byte characters are
|
|
18
|
+
* 3060 characters of `%XX`.
|
|
19
|
+
*/
|
|
20
|
+
export declare const MAX_ROUTE_PARAM_LENGTH = 4096;
|
|
13
21
|
import type { AuthSessionRecord } from './ports/auth-port.js';
|
|
14
22
|
import type { StaleExpirationMap } from '../utils/index.js';
|
|
15
23
|
export declare const CASES_DIR: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-helpers.d.ts","sourceRoot":"","sources":["../../src/web/route-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAqC,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAIlG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"route-helpers.d.ts","sourceRoot":"","sources":["../../src/web/route-helpers.ts"],"names":[],"mappings":"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"}
|