tmux-ide 2.9.0-beta.44 → 2.9.0-beta.45
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/bin/cli.js +2 -2
- package/package.json +2 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-home-agent-roster.jsx +1 -1
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +1 -1
- package/packages/daemon/dist/tui/mirror/runtime/application-shell-home.jsx +53 -18
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-workspace.jsx +33 -2
- package/packages/daemon/dist/tui/mirror/ui/agent-row.jsx +26 -6
- package/packages/daemon/dist/tui/mirror/ui/pane-interaction-presentation.js +72 -0
- package/packages/daemon/dist/tui/mirror/ui/pane-interaction.jsx +76 -0
- package/packages/daemon/dist/tui/mirror/ui/pane-mode-control.jsx +23 -0
- package/packages/daemon/dist/tui/mirror/ui/status-presentation.js +41 -0
- package/packages/daemon/dist/tui/mirror/workspace/terminal-pane-header.jsx +35 -54
- package/packages/daemon/src/tui/mirror/runtime/__snapshots__/application-shell-view-renderer.test.tsx.snap +4 -4
- package/packages/daemon/src/tui/mirror/runtime/application-home-agent-roster.tsx +5 -0
- package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +8 -0
- package/packages/daemon/src/tui/mirror/runtime/application-shell-home.tsx +89 -26
- package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +32 -0
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-workspace.tsx +56 -2
- package/packages/daemon/src/tui/mirror/ui/agent-row.tsx +39 -11
- package/packages/daemon/src/tui/mirror/ui/pane-interaction-presentation.ts +90 -0
- package/packages/daemon/src/tui/mirror/ui/pane-interaction.tsx +127 -0
- package/packages/daemon/src/tui/mirror/ui/pane-mode-control.tsx +47 -0
- package/packages/daemon/src/tui/mirror/ui/status-presentation.ts +54 -0
- package/packages/daemon/src/tui/mirror/workspace/terminal-pane-header.tsx +56 -68
- package/packages/daemon/dist/tui/mirror/ui/detail-row.jsx +0 -22
package/bin/cli.js
CHANGED
|
@@ -11279,7 +11279,7 @@ var require_package = __commonJS({
|
|
|
11279
11279
|
"package.json"(exports, module) {
|
|
11280
11280
|
module.exports = {
|
|
11281
11281
|
name: "tmux-ide",
|
|
11282
|
-
version: "2.9.0-beta.
|
|
11282
|
+
version: "2.9.0-beta.45",
|
|
11283
11283
|
description: "A visual, agent-aware IDE for any tmux session, with optional workspace presets",
|
|
11284
11284
|
type: "module",
|
|
11285
11285
|
bin: {
|
|
@@ -11343,7 +11343,7 @@ var require_package = __commonJS({
|
|
|
11343
11343
|
postinstall: "node scripts/postinstall.js",
|
|
11344
11344
|
docs: "turbo run dev --filter=@tmux-ide/docs",
|
|
11345
11345
|
"demo:tui": "bun --preload @opentui/solid/preload docs/scripts/render-tui-demo.tsx",
|
|
11346
|
-
"test:tui-renderer": "bun test --preload @opentui/solid/preload --preload ./packages/daemon/test-support/opentui-renderer-preload.ts ./packages/daemon/src/tui/mirror/automatic-contrast-renderer.test.tsx ./packages/daemon/src/tui/mirror/pane-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/native-grid-projection-renderer.test.tsx ./packages/daemon/src/tui/mirror/widget-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/missions-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/recipes-gallery-renderer.test.tsx ./packages/daemon/src/tui/mirror/shell-chrome-renderer.test.tsx ./packages/daemon/src/tui/mirror/sidebar-renderer.test.tsx ./packages/daemon/src/tui/mirror/home-files-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/changes-terminal-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/activity-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/features/files/session-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-terminal-workspace-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-view-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-root-error-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-backpressure-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-demand-cadence-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/files-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/changes-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/missions-activity-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/dialogs-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/optional-feature-registry-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/palette-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/pane-scoped-terminal-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/features/rich-preview/feature.test.ts ./packages/daemon/src/tui/mirror/runtime/rich-preview-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/application-shell-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/pane-frame-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-pane-chrome-view.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-window-strip-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/workbench-shell-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/workbench-dock-dual-host-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/agent-terminal-canvas-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/command-palette-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/opentui-insertion-stability-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-home-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-pane-header-polish-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-home-agent-roster-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-home-agent-flow-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/guided-tour-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-sidebar-catalog-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/ui-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-machine-sidebar-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-add-machine-dialog-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-switcher-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-palette-preview-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-palette-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-session-actions-renderer.test.tsx",
|
|
11346
|
+
"test:tui-renderer": "bun test --preload @opentui/solid/preload --preload ./packages/daemon/test-support/opentui-renderer-preload.ts ./packages/daemon/src/tui/mirror/automatic-contrast-renderer.test.tsx ./packages/daemon/src/tui/mirror/pane-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/native-grid-projection-renderer.test.tsx ./packages/daemon/src/tui/mirror/widget-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/missions-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/recipes-gallery-renderer.test.tsx ./packages/daemon/src/tui/mirror/shell-chrome-renderer.test.tsx ./packages/daemon/src/tui/mirror/sidebar-renderer.test.tsx ./packages/daemon/src/tui/mirror/home-files-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/changes-terminal-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/activity-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/features/files/session-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-terminal-workspace-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-view-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-root-error-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-backpressure-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-demand-cadence-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/files-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/changes-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/missions-activity-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/dialogs-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/optional-feature-registry-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/palette-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/pane-scoped-terminal-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/features/rich-preview/feature.test.ts ./packages/daemon/src/tui/mirror/runtime/rich-preview-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/application-shell-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/pane-frame-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-pane-chrome-view.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-window-strip-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/workbench-shell-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/workbench-dock-dual-host-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/agent-terminal-canvas-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/command-palette-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/opentui-insertion-stability-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-home-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-pane-header-polish-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-home-agent-roster-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-home-agent-flow-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/guided-tour-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-sidebar-catalog-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/ui-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/pane-interaction-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-machine-sidebar-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-add-machine-dialog-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-switcher-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-palette-preview-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-palette-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-session-actions-renderer.test.tsx",
|
|
11347
11347
|
"test:tui-smoke": "bun scripts/smoke-tui-missions.mjs",
|
|
11348
11348
|
"test:tui-live": "node scripts/tui-testdrive.mjs smoke",
|
|
11349
11349
|
"test:tui-testdrive": "node --test scripts/lib/tui-testdrive-clipboard-hook.test.mjs scripts/lib/tui-testdrive-input.test.mjs",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tmux-ide",
|
|
3
|
-
"version": "2.9.0-beta.
|
|
3
|
+
"version": "2.9.0-beta.45",
|
|
4
4
|
"description": "A visual, agent-aware IDE for any tmux session, with optional workspace presets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"postinstall": "node scripts/postinstall.js",
|
|
65
65
|
"docs": "turbo run dev --filter=@tmux-ide/docs",
|
|
66
66
|
"demo:tui": "bun --preload @opentui/solid/preload docs/scripts/render-tui-demo.tsx",
|
|
67
|
-
"test:tui-renderer": "bun test --preload @opentui/solid/preload --preload ./packages/daemon/test-support/opentui-renderer-preload.ts ./packages/daemon/src/tui/mirror/automatic-contrast-renderer.test.tsx ./packages/daemon/src/tui/mirror/pane-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/native-grid-projection-renderer.test.tsx ./packages/daemon/src/tui/mirror/widget-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/missions-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/recipes-gallery-renderer.test.tsx ./packages/daemon/src/tui/mirror/shell-chrome-renderer.test.tsx ./packages/daemon/src/tui/mirror/sidebar-renderer.test.tsx ./packages/daemon/src/tui/mirror/home-files-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/changes-terminal-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/activity-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/features/files/session-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-terminal-workspace-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-view-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-root-error-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-backpressure-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-demand-cadence-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/files-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/changes-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/missions-activity-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/dialogs-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/optional-feature-registry-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/palette-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/pane-scoped-terminal-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/features/rich-preview/feature.test.ts ./packages/daemon/src/tui/mirror/runtime/rich-preview-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/application-shell-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/pane-frame-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-pane-chrome-view.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-window-strip-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/workbench-shell-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/workbench-dock-dual-host-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/agent-terminal-canvas-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/command-palette-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/opentui-insertion-stability-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-home-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-pane-header-polish-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-home-agent-roster-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-home-agent-flow-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/guided-tour-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-sidebar-catalog-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/ui-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-machine-sidebar-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-add-machine-dialog-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-switcher-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-palette-preview-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-palette-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-session-actions-renderer.test.tsx",
|
|
67
|
+
"test:tui-renderer": "bun test --preload @opentui/solid/preload --preload ./packages/daemon/test-support/opentui-renderer-preload.ts ./packages/daemon/src/tui/mirror/automatic-contrast-renderer.test.tsx ./packages/daemon/src/tui/mirror/pane-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/native-grid-projection-renderer.test.tsx ./packages/daemon/src/tui/mirror/widget-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/missions-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/recipes-gallery-renderer.test.tsx ./packages/daemon/src/tui/mirror/shell-chrome-renderer.test.tsx ./packages/daemon/src/tui/mirror/sidebar-renderer.test.tsx ./packages/daemon/src/tui/mirror/home-files-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/changes-terminal-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/activity-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/features/files/session-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-terminal-workspace-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-view-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-root-error-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-backpressure-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-demand-cadence-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/files-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/changes-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/missions-activity-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/dialogs-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/optional-feature-registry-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/palette-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/pane-scoped-terminal-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/features/rich-preview/feature.test.ts ./packages/daemon/src/tui/mirror/runtime/rich-preview-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/application-shell-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/pane-frame-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-pane-chrome-view.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-window-strip-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/workbench-shell-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/workbench-dock-dual-host-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/agent-terminal-canvas-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/command-palette-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/opentui-insertion-stability-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-home-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-pane-header-polish-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-home-agent-roster-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-home-agent-flow-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/guided-tour-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-sidebar-catalog-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/ui-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/pane-interaction-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-machine-sidebar-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-add-machine-dialog-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-switcher-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-palette-preview-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-palette-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-session-actions-renderer.test.tsx",
|
|
68
68
|
"test:tui-smoke": "bun scripts/smoke-tui-missions.mjs",
|
|
69
69
|
"test:tui-live": "node scripts/tui-testdrive.mjs smoke",
|
|
70
70
|
"test:tui-testdrive": "node --test scripts/lib/tui-testdrive-clipboard-hook.test.mjs scripts/lib/tui-testdrive-input.test.mjs",
|
|
@@ -191,7 +191,7 @@ export function HomeAgentRoster(props) {
|
|
|
191
191
|
return (<box height={rowHeight()} width={width()} flexShrink={0} onMouseOver={() => setHovered(key)} onMouseMove={() => setHovered(key)}>
|
|
192
192
|
<AgentRow surface="canvas" theme={props.theme} id={`home-agent:${key}`} name={row().name} context={[row().machineLabel, row().serverLabel, row().sessionName]
|
|
193
193
|
.filter(Boolean)
|
|
194
|
-
.join(" · ")} width={width()} compact={rowHeight() === 1} activity={row().activity} attention={row().attention} unavailable={row().paneId === null || stale(row())} selected={props.selection.selectedKey === key} focused={props.inputActive && !editing() && props.selection.selectedKey === key} hovered={hovered() === key} onOpen={(source) => {
|
|
194
|
+
.join(" · ")} width={width()} compact={rowHeight() === 1} interaction={props.interactionForAgent?.(row())} paneName={props.paneName} activity={row().activity} attention={row().attention} unavailable={row().paneId === null || stale(row())} selected={props.selection.selectedKey === key} focused={props.inputActive && !editing() && props.selection.selectedKey === key} hovered={hovered() === key} onOpen={(source) => {
|
|
195
195
|
if (!props.inputActive || row().paneId === null || stale(row()))
|
|
196
196
|
return;
|
|
197
197
|
props.onSelect(key);
|
|
@@ -380,7 +380,7 @@ export function ApplicationMachineSidebar(props) {
|
|
|
380
380
|
friendlySessionLabel(agent().sessionName),
|
|
381
381
|
]
|
|
382
382
|
.filter(Boolean)
|
|
383
|
-
.join(" · ")} width={Math.max(1, props.width - 1)} activity={agent().activity} attention={agent().attention} unavailable={row.group.state !== "ready" ||
|
|
383
|
+
.join(" · ")} width={Math.max(1, props.width - 1)} interaction={props.interactionForAgent?.(agent())} paneName={props.paneName} activity={agent().activity} attention={agent().attention} unavailable={row.group.state !== "ready" ||
|
|
384
384
|
row.group.agentsAvailable === false ||
|
|
385
385
|
!!agent().disabled ||
|
|
386
386
|
!agent().paneId} selected={active(row)} focused={focused() && row.key === selectedKey()} onOpen={(source) => activate(row, source)}/>)}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { For, Show, createSignal } from "solid-js";
|
|
1
|
+
import { For, Show, createEffect, createSignal } from "solid-js";
|
|
3
2
|
import { clipTerminal, terminalDisplayWidth } from "../terminal-text.js";
|
|
4
3
|
import { SectionHeading } from "../ui/section-heading.jsx";
|
|
5
4
|
import { KeyHint } from "../ui/key-hint.jsx";
|
|
6
5
|
import { CHROME_ACTIONS, HOME_ACTIONS } from "../workspace/application-action-descriptions.js";
|
|
7
|
-
import {
|
|
6
|
+
import { PaneInteraction, PaneInteractionDetails } from "../ui/pane-interaction.jsx";
|
|
7
|
+
import { receiptPaneInteraction, } from "../ui/pane-interaction-presentation.js";
|
|
8
8
|
import { TuiButton } from "../ui/button.jsx";
|
|
9
9
|
import { HomeAgentRoster } from "./application-home-agent-roster.jsx";
|
|
10
10
|
import { APPLICATION_HOME_WORDMARK, APPLICATION_HOME_WORDMARK_WIDTH } from "../ui/home-wordmark.js";
|
|
@@ -14,6 +14,7 @@ export function applicationHomeBrandVariant(width, height) {
|
|
|
14
14
|
}
|
|
15
15
|
/** Presentation only: session data, commands, and keyboard admission stay with the shell. */
|
|
16
16
|
export function ApplicationHomeSurface(props) {
|
|
17
|
+
const [inspection, setInspection] = createSignal(null);
|
|
17
18
|
const [tipHidden, setTipHidden] = createSignal(false);
|
|
18
19
|
const width = () => Math.max(0, Math.floor(props.width));
|
|
19
20
|
const height = () => Math.max(0, Math.floor(props.height));
|
|
@@ -75,14 +76,22 @@ export function ApplicationHomeSurface(props) {
|
|
|
75
76
|
return [];
|
|
76
77
|
return (props.recentPaneActivity ?? []).filter((receipt) => receipt.workspaceName === selected.sessionName &&
|
|
77
78
|
receipt.target.kind === "pane" &&
|
|
78
|
-
receipt.target.semanticPaneId === selected.paneId
|
|
79
|
+
(receipt.target.semanticPaneId === selected.paneId ||
|
|
80
|
+
(receipt.phase === "observed" && receipt.sourceSemanticPaneId === selected.paneId)));
|
|
79
81
|
};
|
|
80
82
|
const recentActivity = () => selectedActivity().slice(0, spacious()
|
|
81
83
|
? Math.max(0, Math.min(1, Math.floor((height() - reservedRows() - (props.agentRoster ? 10 : 2) - 2) / activityRows())))
|
|
82
84
|
: 0);
|
|
83
85
|
const activityHeight = () => recentActivity().length > 0 ? recentActivity().length * activityRows() + 2 : 0;
|
|
86
|
+
createEffect(() => {
|
|
87
|
+
const value = inspection();
|
|
88
|
+
if (value && value.key !== `${props.activityDaemonId}:${selectedAgent()?.key}`)
|
|
89
|
+
setInspection(null);
|
|
90
|
+
});
|
|
84
91
|
const paneLabel = (paneId) => {
|
|
85
|
-
const matches = props.agentRoster?.rows.filter((row) => row.paneId === paneId
|
|
92
|
+
const matches = props.agentRoster?.rows.filter((row) => row.paneId === paneId &&
|
|
93
|
+
row.daemonInstanceId === props.activityDaemonId &&
|
|
94
|
+
row.sessionName === selectedAgent()?.sessionName) ?? [];
|
|
86
95
|
return matches.length === 1 ? matches[0].name : paneId;
|
|
87
96
|
};
|
|
88
97
|
const activityTime = (receipt) => {
|
|
@@ -91,17 +100,17 @@ export function ApplicationHomeSurface(props) {
|
|
|
91
100
|
? `${new Date(at).toISOString().slice(5, 16).replace("T", " ")}Z`
|
|
92
101
|
: "Time unknown";
|
|
93
102
|
};
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
:
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
const inspect = (event) => {
|
|
104
|
+
const names = new Map();
|
|
105
|
+
for (const id of [event.sourcePaneId, event.destinationPaneId])
|
|
106
|
+
if (id)
|
|
107
|
+
names.set(id, paneLabel(id));
|
|
108
|
+
setInspection({
|
|
109
|
+
event: { ...event },
|
|
110
|
+
names,
|
|
111
|
+
key: `${props.activityDaemonId}:${selectedAgent()?.key}`,
|
|
112
|
+
});
|
|
113
|
+
};
|
|
105
114
|
const rosterHeight = () => Math.max(0, height() - reservedRows() - activityHeight());
|
|
106
115
|
return (<box id="application-home" width={width()} height={height()} paddingLeft={inset()} paddingRight={inset()} paddingTop={spacious() ? 1 : 0} flexDirection="column" alignItems="flex-start" backgroundColor={props.theme.roles.surfaces.canvas} overflow="hidden">
|
|
107
116
|
<box height={brandRows()} width={bodyWidth()} flexShrink={0} flexDirection="column">
|
|
@@ -134,7 +143,19 @@ export function ApplicationHomeSurface(props) {
|
|
|
134
143
|
{clipTerminal(summary(), bodyWidth())}
|
|
135
144
|
</text>
|
|
136
145
|
</>}>
|
|
137
|
-
{(snapshot) => (<HomeAgentRoster fitContent query={props.agentQuery} onQueryChange={props.onAgentQueryChange} filterLabel={props.agentFilterLabel} activityFilter={props.agentActivityFilter} onSetActivityFilter={props.onSetAgentActivityFilter} onCycleMachine={props.onCycleAgentMachine} onToggleAttention={props.onToggleAgentAttention} theme={props.theme} width={bodyWidth()} height={rosterHeight()}
|
|
146
|
+
{(snapshot) => (<HomeAgentRoster fitContent query={props.agentQuery} onQueryChange={props.onAgentQueryChange} filterLabel={props.agentFilterLabel} activityFilter={props.agentActivityFilter} onSetActivityFilter={props.onSetAgentActivityFilter} onCycleMachine={props.onCycleAgentMachine} onToggleAttention={props.onToggleAgentAttention} theme={props.theme} width={bodyWidth()} height={rosterHeight()} paneName={paneLabel} interactionForAgent={(row) => {
|
|
147
|
+
if (!row.paneId || row.daemonInstanceId !== props.activityDaemonId)
|
|
148
|
+
return undefined;
|
|
149
|
+
const matches = props.agentRoster?.rows.filter((other) => other.paneId === row.paneId && other.sessionName === row.sessionName);
|
|
150
|
+
if (matches?.length !== 1)
|
|
151
|
+
return undefined;
|
|
152
|
+
const event = props.paneInteractions?.get(row.paneId);
|
|
153
|
+
return event &&
|
|
154
|
+
props.recentPaneActivity?.some((receipt) => receipt.operationId === event.operationId &&
|
|
155
|
+
receipt.workspaceName === row.sessionName)
|
|
156
|
+
? event
|
|
157
|
+
: undefined;
|
|
158
|
+
}} snapshot={snapshot()} selection={props.agentSelection ?? { selectedKey: null, scrollOffset: 0 }} inputActive={(props.agentInputActive ?? false) && !inspection()} onSelect={(key) => props.onSelectAgent?.(key)} onMove={(delta) => props.onMoveAgent?.(delta)} onViewport={(rows) => props.onAgentViewport?.(rows)} onOpen={(row, source) => props.onOpenAgent?.(row, source)} onRetry={props.onRetryAgents} onLoadMore={props.onLoadMoreAgents}/>)}
|
|
138
159
|
</Show>
|
|
139
160
|
<Show when={activityHeight() > 0}>
|
|
140
161
|
<box width={bodyWidth()} height={activityHeight()} flexShrink={0} flexDirection="column" overflow="hidden">
|
|
@@ -142,7 +163,16 @@ export function ApplicationHomeSurface(props) {
|
|
|
142
163
|
{clipTerminal(`${selectedAgent()?.name ?? "Agent"} · latest activity`, bodyWidth())}
|
|
143
164
|
</text>
|
|
144
165
|
<For each={recentActivity()}>
|
|
145
|
-
{(receipt) => (<
|
|
166
|
+
{(receipt) => (<box height={activityRows()} width={bodyWidth()} flexDirection="column">
|
|
167
|
+
<Show when={receiptPaneInteraction(receipt, selectedAgent()?.paneId)} keyed>
|
|
168
|
+
{(event) => (<PaneInteraction theme={props.theme} event={event} paneName={paneLabel} width={bodyWidth()} onDetails={() => inspect(event)}/>)}
|
|
169
|
+
</Show>
|
|
170
|
+
<Show when={activityRows() === 2}>
|
|
171
|
+
<text height={1} fg={props.theme.roles.text.muted}>
|
|
172
|
+
{activityTime(receipt)}
|
|
173
|
+
</text>
|
|
174
|
+
</Show>
|
|
175
|
+
</box>)}
|
|
146
176
|
</For>
|
|
147
177
|
<text height={1} width={bodyWidth()} fg={props.theme.roles.text.muted}>
|
|
148
178
|
{clipTerminal("Activity reported through tmux-ide", bodyWidth())}
|
|
@@ -179,5 +209,10 @@ export function ApplicationHomeSurface(props) {
|
|
|
179
209
|
{clipTerminal(note(), bodyWidth())}
|
|
180
210
|
</text>)}
|
|
181
211
|
</Show>
|
|
212
|
+
<Show when={inspection()} keyed>
|
|
213
|
+
{(value) => (<Show when={value.key === `${props.activityDaemonId}:${selectedAgent()?.key}`}>
|
|
214
|
+
<PaneInteractionDetails theme={props.theme} event={value.event} paneName={(id) => value.names.get(id)} width={bodyWidth()} viewportWidth={props.width} viewportHeight={props.height} onDismiss={() => setInspection(null)}/>
|
|
215
|
+
</Show>)}
|
|
216
|
+
</Show>
|
|
182
217
|
</box>);
|
|
183
218
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PaneInteractionDetails } from "../ui/pane-interaction.jsx";
|
|
1
2
|
import { Menu } from "../ui/index.js";
|
|
2
3
|
import { windowLinkTarget } from "./application-terminal-workspace-policy.js";
|
|
3
4
|
/* @jsxImportSource @opentui/solid */
|
|
@@ -252,6 +253,27 @@ export function ApplicationTerminalWorkspace(props) {
|
|
|
252
253
|
},
|
|
253
254
|
});
|
|
254
255
|
const paneContextMenu = paneMenu.state;
|
|
256
|
+
const [interactionDetails, setInteractionDetails] = createSignal(null);
|
|
257
|
+
const paneName = (id) => props.agentIndicators?.().get(id)?.name ?? id;
|
|
258
|
+
const inspectInteraction = (paneId) => {
|
|
259
|
+
const event = props.paneInteractions?.().get(paneId);
|
|
260
|
+
if (!event)
|
|
261
|
+
return;
|
|
262
|
+
paneMenu.dismiss();
|
|
263
|
+
const names = new Map();
|
|
264
|
+
for (const id of [event.sourcePaneId, event.destinationPaneId])
|
|
265
|
+
if (id)
|
|
266
|
+
names.set(id, paneName(id));
|
|
267
|
+
setInteractionDetails({ event: { ...event }, names, paneId, epoch: props.rendererEpoch });
|
|
268
|
+
};
|
|
269
|
+
createEffect(() => {
|
|
270
|
+
const details = interactionDetails();
|
|
271
|
+
if (details &&
|
|
272
|
+
(details.epoch !== props.rendererEpoch ||
|
|
273
|
+
props.interactive === false ||
|
|
274
|
+
!projectedFrames().some((frame) => frame.visible && frame.paneId === details.paneId)))
|
|
275
|
+
setInteractionDetails(null);
|
|
276
|
+
});
|
|
255
277
|
let selecting = null;
|
|
256
278
|
let linkPointer = false;
|
|
257
279
|
let liveReturnPointer = false;
|
|
@@ -605,6 +627,11 @@ export function ApplicationTerminalWorkspace(props) {
|
|
|
605
627
|
const handlePaneMenuKey = (name, event) => {
|
|
606
628
|
if (props.interactive === false)
|
|
607
629
|
return false;
|
|
630
|
+
if (interactionDetails()) {
|
|
631
|
+
if (name === "escape" && event?.eventType !== "release")
|
|
632
|
+
setInteractionDetails(null);
|
|
633
|
+
return true;
|
|
634
|
+
}
|
|
608
635
|
if (windowLinkMenu()) {
|
|
609
636
|
if (event?.eventType === "release")
|
|
610
637
|
return true;
|
|
@@ -769,6 +796,7 @@ export function ApplicationTerminalWorkspace(props) {
|
|
|
769
796
|
}, () => props.interactive !== false &&
|
|
770
797
|
(drag !== null ||
|
|
771
798
|
windowLinkMenu() !== null ||
|
|
799
|
+
interactionDetails() !== null ||
|
|
772
800
|
paneMenu.ownsInput() ||
|
|
773
801
|
keyboardCopy() !== null), () => {
|
|
774
802
|
// Called only after global shortcuts, copy, and local navigation decline
|
|
@@ -789,7 +817,7 @@ export function ApplicationTerminalWorkspace(props) {
|
|
|
789
817
|
const routePointer = (event) => {
|
|
790
818
|
if (event.type === "down")
|
|
791
819
|
wheelGesture.reset();
|
|
792
|
-
if (windowLinkMenu() || paneMenu.ownsInput()) {
|
|
820
|
+
if (interactionDetails() || windowLinkMenu() || paneMenu.ownsInput()) {
|
|
793
821
|
event.stopPropagation?.();
|
|
794
822
|
return;
|
|
795
823
|
}
|
|
@@ -1284,7 +1312,7 @@ export function ApplicationTerminalWorkspace(props) {
|
|
|
1284
1312
|
].join(":");
|
|
1285
1313
|
});
|
|
1286
1314
|
return (<box position="absolute" left={frame().left} top={frame().top + topOffset()} width={frame().width} height={frame().height} visible={frame().visible} backgroundColor={props.theme.roles.surfaces.canvas} onMouse={routePointer}>
|
|
1287
|
-
<PaneTitleBar theme={props.theme} paneId={frame().paneId} title={`${frame().compactPosition ? `Compact ${frame().compactPosition} · Ctrl+O: next · ` : ""}${displayTitle()}
|
|
1315
|
+
<PaneTitleBar theme={props.theme} paneId={frame().paneId} title={`${frame().compactPosition ? `Compact ${frame().compactPosition} · Ctrl+O: next · ` : ""}${displayTitle()}`} connectionStatus={props.connectionStatus} scrollback={scrollback.offset(frame().paneId) > 0} linesAboveLive={scrollback.offset(frame().paneId)} onBackToLiveIntent={() => scrollback.live(frame().paneId)} zoomed={layout().windows.some((window) => window.zoomed && window.panes.some((pane) => pane.pane === frame().paneId))} onRestoreIntent={() => props.onPaneContextAction?.(frame().paneId, "zoom-pane", displayTitle())} width={frame().width} selected={props.focusedPane === frame().paneId} terminalFocused={terminalSurfaceFocused(frame())} keyboardFocused={props.focusedPane === frame().paneId} menuOpen={paneContextMenu()?.paneId === frame().paneId} interaction={props.paneInteractions?.().get(frame().paneId)} paneName={paneName} onInteractionDetails={() => inspectInteraction(frame().paneId)} activity={indicator()?.activity} attention={indicator()?.attention} menuAnchor={{
|
|
1288
1316
|
x: (props.originX ?? 0) + frame().left + Math.max(0, frame().width - 1),
|
|
1289
1317
|
y: (props.originY ?? 0) + frame().top + topOffset(),
|
|
1290
1318
|
}} onSelectIntent={() => props.onSelectPane(frame().paneId)} onMenuIntent={(anchor) => openPaneContextMenu(frame().paneId, anchor)}/>
|
|
@@ -1325,6 +1353,9 @@ export function ApplicationTerminalWorkspace(props) {
|
|
|
1325
1353
|
<Show when={windowLinkMenu()}>
|
|
1326
1354
|
<Menu theme={props.theme} viewportOrigin={{ x: props.originX ?? 0, y: props.originY ?? 0 }} title="Window link" left={0} top={1} width={Math.min(36, props.width)} viewportWidth={props.width} viewportHeight={props.height} selectedId="unlink" items={[{ id: "unlink", label: "Unlink this tab", shortcut: "U" }]} footer="Other links keep their panes" onDismiss={() => setWindowLinkMenu(null)} onSelect={unlinkWindowTab}/>
|
|
1327
1355
|
</Show>
|
|
1356
|
+
<Show when={interactionDetails()} keyed>
|
|
1357
|
+
{(details) => (<PaneInteractionDetails theme={props.theme} event={details.event} paneName={(id) => details.names.get(id)} width={props.width} viewportWidth={props.width} viewportHeight={props.height} viewportOrigin={{ x: props.originX ?? 0, y: props.originY ?? 0 }} onDismiss={() => setInteractionDetails(null)}/>)}
|
|
1358
|
+
</Show>
|
|
1328
1359
|
<Show when={paneContextMenu()}>
|
|
1329
1360
|
{(menu) => (<PaneActionMenu theme={props.theme} left={menu().left} top={menu().top} width={paneContextMenuWidth()} viewportWidth={props.width} viewportHeight={props.height + topOffset()} viewportOrigin={{ x: props.originX ?? 0, y: props.originY ?? 0 }} paneTitle={menu().displayName} active onDismiss={paneMenu.dismiss} selectedId={menu().selectedId} closeArmed={menu().closeArmed} onHighlight={paneMenu.highlight} onActionIntent={paneMenu.activate}/>)}
|
|
1330
1361
|
</Show>
|
|
@@ -1,14 +1,27 @@
|
|
|
1
|
+
import { createPaneInteractionMarker } from "./pane-interaction.jsx";
|
|
2
|
+
import { paneInteractionPresentation, } from "./pane-interaction-presentation.js";
|
|
1
3
|
import { Show } from "solid-js";
|
|
2
4
|
import { clipTerminal } from "../terminal-text.js";
|
|
3
5
|
import { NavigationRow } from "./navigation-row.jsx";
|
|
4
6
|
import { createAgentStatusMarker } from "./agent-status-marker.js";
|
|
7
|
+
import { statusPresentation } from "./status-presentation.js";
|
|
5
8
|
import { componentPalette } from "./state.js";
|
|
6
9
|
/** One agent identity and one state marker, shared by Home and fleet navigation. */
|
|
7
10
|
export function AgentRow(props) {
|
|
11
|
+
const status = () => statusPresentation(props);
|
|
12
|
+
const receipt = () => !props.unavailable &&
|
|
13
|
+
props.activity !== "disconnected" &&
|
|
14
|
+
!props.attention &&
|
|
15
|
+
props.activity !== "waiting" &&
|
|
16
|
+
props.activity !== "failed" &&
|
|
17
|
+
props.interaction
|
|
18
|
+
? paneInteractionPresentation(props.interaction, props.paneName)
|
|
19
|
+
: undefined;
|
|
20
|
+
const receiptMarker = createPaneInteractionMarker(() => (receipt() ? props.interaction : undefined), () => props.theme);
|
|
8
21
|
const marker = createAgentStatusMarker({
|
|
9
22
|
theme: () => props.theme,
|
|
10
|
-
status: () =>
|
|
11
|
-
attention: () =>
|
|
23
|
+
status: () => (receipt() ? undefined : status()?.activity),
|
|
24
|
+
attention: () => status()?.activity === "waiting",
|
|
12
25
|
unavailable: () => !!props.unavailable,
|
|
13
26
|
});
|
|
14
27
|
const palette = () => componentPalette(props.theme, {
|
|
@@ -20,7 +33,6 @@ export function AgentRow(props) {
|
|
|
20
33
|
const background = () => palette().state === "base" && props.surface === "canvas"
|
|
21
34
|
? props.theme.roles.surfaces.canvas
|
|
22
35
|
: palette().background;
|
|
23
|
-
const exceptional = () => props.unavailable ? "unavailable" : props.activity === "disconnected" ? "unknown" : undefined;
|
|
24
36
|
return (<box width={props.width} height={props.compact ? 1 : 2} flexShrink={0} flexDirection="column" backgroundColor={background()} onMouseDown={(event) => {
|
|
25
37
|
if (event.button !== 0 || props.unavailable)
|
|
26
38
|
return;
|
|
@@ -28,9 +40,17 @@ export function AgentRow(props) {
|
|
|
28
40
|
event.stopPropagation();
|
|
29
41
|
props.onOpen("mouse");
|
|
30
42
|
}}>
|
|
31
|
-
<NavigationRow surface={props.surface} theme={props.theme} id={props.id} width={props.width} label={props.name} marker={
|
|
32
|
-
?
|
|
33
|
-
: "
|
|
43
|
+
<NavigationRow surface={props.surface} theme={props.theme} id={props.id} width={props.width} label={props.name} marker={receipt()
|
|
44
|
+
? receiptMarker()
|
|
45
|
+
: props.activity === "idle" && !props.attention && !props.unavailable
|
|
46
|
+
? " "
|
|
47
|
+
: marker()} detail={props.width < 24
|
|
48
|
+
? undefined
|
|
49
|
+
: receipt()
|
|
50
|
+
? clipTerminal(receipt().compactLabel, Math.max(0, props.width - 14))
|
|
51
|
+
: status()?.label === "Idle"
|
|
52
|
+
? undefined
|
|
53
|
+
: status()?.label} selected={props.selected} focused={props.focused} hovered={props.hovered} disabled={props.unavailable} tone={status()?.tone === "blocked" ? "warning" : "neutral"} onActivate={props.onOpen}/>
|
|
34
54
|
<Show when={!props.compact}>
|
|
35
55
|
<text height={1} width={props.width} fg={props.selected ? palette().foreground : props.theme.roles.text.muted}>
|
|
36
56
|
{clipTerminal(` ${props.context}`, props.width)}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export function receiptPaneInteraction(receipt, viewingPaneId) {
|
|
2
|
+
if (receipt.target.kind !== "pane" ||
|
|
3
|
+
!["workspace.pane.read", "workspace.pane.send"].includes(receipt.operationKind))
|
|
4
|
+
return null;
|
|
5
|
+
return {
|
|
6
|
+
direction: viewingPaneId &&
|
|
7
|
+
receipt.sourceSemanticPaneId === viewingPaneId &&
|
|
8
|
+
receipt.target.semanticPaneId !== viewingPaneId
|
|
9
|
+
? "outgoing"
|
|
10
|
+
: "incoming",
|
|
11
|
+
operationId: receipt.operationId,
|
|
12
|
+
operationKind: receipt.operationKind,
|
|
13
|
+
phase: receipt.phase,
|
|
14
|
+
origin: receipt.origin,
|
|
15
|
+
sourcePaneId: receipt.sourceSemanticPaneId,
|
|
16
|
+
destinationPaneId: receipt.target.semanticPaneId,
|
|
17
|
+
at: receipt.at,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function paneInteractionPresentation(event, name = () => undefined) {
|
|
21
|
+
const read = event.operationKind === "workspace.pane.read";
|
|
22
|
+
// An accepted request is not an authenticated actor or proof of delivery.
|
|
23
|
+
const source = event.phase === "observed" && event.sourcePaneId ? name(event.sourcePaneId) : undefined;
|
|
24
|
+
const target = name(event.destinationPaneId) ?? "Pane";
|
|
25
|
+
const pending = event.phase === "accepted";
|
|
26
|
+
const failed = event.phase === "rejected" || event.phase === "timed-out";
|
|
27
|
+
const label = failed
|
|
28
|
+
? `${read ? "Read" : "Send"} ${event.phase === "timed-out" ? "timed out" : "failed"}`
|
|
29
|
+
: pending
|
|
30
|
+
? read
|
|
31
|
+
? "Read requested"
|
|
32
|
+
: "Input pending"
|
|
33
|
+
: event.direction === "outgoing"
|
|
34
|
+
? read
|
|
35
|
+
? `Read ${target}`
|
|
36
|
+
: `Input sent to ${target}`
|
|
37
|
+
: read
|
|
38
|
+
? source
|
|
39
|
+
? `Read by ${source}`
|
|
40
|
+
: "Pane read · reader unknown"
|
|
41
|
+
: source
|
|
42
|
+
? `Input from ${source}`
|
|
43
|
+
: "Input observed · sender unknown";
|
|
44
|
+
return {
|
|
45
|
+
label,
|
|
46
|
+
compactLabel: !pending && !failed && !source && event.direction !== "outgoing"
|
|
47
|
+
? read
|
|
48
|
+
? "Pane read"
|
|
49
|
+
: "Input observed"
|
|
50
|
+
: label,
|
|
51
|
+
source: source ?? (pending ? "Not yet verified" : "Unknown"),
|
|
52
|
+
target,
|
|
53
|
+
pending,
|
|
54
|
+
failed,
|
|
55
|
+
explanation: failed
|
|
56
|
+
? "The operation did not complete successfully."
|
|
57
|
+
: pending
|
|
58
|
+
? "Request accepted. Completion and source attribution are not yet confirmed."
|
|
59
|
+
: read
|
|
60
|
+
? "Pane output was read. This does not prove comprehension or change your focus."
|
|
61
|
+
: "Input delivery was observed. This does not mean the application processed it.",
|
|
62
|
+
phase: pending
|
|
63
|
+
? "Requested"
|
|
64
|
+
: failed
|
|
65
|
+
? event.phase === "timed-out"
|
|
66
|
+
? "Timed out"
|
|
67
|
+
: "Failed"
|
|
68
|
+
: read
|
|
69
|
+
? "Read completed"
|
|
70
|
+
: "Input delivered",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* @jsxImportSource @opentui/solid */
|
|
2
|
+
import { createEffect, createSignal, onCleanup, Show } from "solid-js";
|
|
3
|
+
import { INTERACTION_PRESENCE_MS } from "@tmux-ide/core";
|
|
4
|
+
import { Badge } from "./badge.jsx";
|
|
5
|
+
import { TuiButton } from "./button.jsx";
|
|
6
|
+
import { createAgentStatusMarker } from "./agent-status-marker.js";
|
|
7
|
+
import { OverlayFrame } from "./overlay-frame.jsx";
|
|
8
|
+
import { useKeyboardRoute, usePasteRoute } from "./keyboard-router.jsx";
|
|
9
|
+
import { paneInteractionPresentation, } from "./pane-interaction-presentation.js";
|
|
10
|
+
export function createPaneInteractionMarker(event, theme) {
|
|
11
|
+
const [animate, setAnimate] = createSignal(false);
|
|
12
|
+
createEffect(() => {
|
|
13
|
+
const currentEvent = event();
|
|
14
|
+
setAnimate(false);
|
|
15
|
+
if (!currentEvent || currentEvent.phase !== "accepted" || theme().accessibility.reducedMotion)
|
|
16
|
+
return;
|
|
17
|
+
const age = Date.now() - Date.parse(currentEvent.at);
|
|
18
|
+
if (!Number.isFinite(age) || age < 0 || age >= INTERACTION_PRESENCE_MS)
|
|
19
|
+
return;
|
|
20
|
+
// Fast operations never acquire the spinner; old history never looks live.
|
|
21
|
+
const start = setTimeout(() => setAnimate(true), Math.max(0, 150 - age));
|
|
22
|
+
const end = setTimeout(() => setAnimate(false), INTERACTION_PRESENCE_MS - age);
|
|
23
|
+
onCleanup(() => {
|
|
24
|
+
clearTimeout(start);
|
|
25
|
+
clearTimeout(end);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
const marker = createAgentStatusMarker({
|
|
29
|
+
theme: () => theme(),
|
|
30
|
+
status: () => (animate() ? "running" : undefined),
|
|
31
|
+
});
|
|
32
|
+
return () => {
|
|
33
|
+
const current = event();
|
|
34
|
+
if (!current)
|
|
35
|
+
return "";
|
|
36
|
+
const value = paneInteractionPresentation(current);
|
|
37
|
+
return value.failed ? "!" : value.pending ? (animate() ? marker() : "↳") : "✓";
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** The same receipt vocabulary and shared animation clock in chrome and Home. */
|
|
41
|
+
export function PaneInteraction(props) {
|
|
42
|
+
const value = () => paneInteractionPresentation(props.event, props.paneName);
|
|
43
|
+
const marker = createPaneInteractionMarker(() => props.event, () => props.theme);
|
|
44
|
+
const detailsWidth = () => (props.onDetails && props.width >= 32 ? 9 : 0);
|
|
45
|
+
return (<box width={props.width} height={1} flexShrink={0} flexDirection="row">
|
|
46
|
+
<Badge theme={props.theme} width={Math.max(1, props.width - detailsWidth())} label={value().label} marker={marker()} tone={value().failed ? "warning" : value().pending ? "accent" : "done"}/>
|
|
47
|
+
<Show when={detailsWidth()}>
|
|
48
|
+
<TuiButton theme={props.theme} label="Details" size="compact" width={9} onPress={props.onDetails}/>
|
|
49
|
+
</Show>
|
|
50
|
+
</box>);
|
|
51
|
+
}
|
|
52
|
+
export function PaneInteractionDetails(props) {
|
|
53
|
+
const value = () => paneInteractionPresentation(props.event, props.paneName);
|
|
54
|
+
useKeyboardRoute((event) => {
|
|
55
|
+
if (event.eventType === "press" && event.name === "escape")
|
|
56
|
+
props.onDismiss();
|
|
57
|
+
return true;
|
|
58
|
+
});
|
|
59
|
+
usePasteRoute(() => true);
|
|
60
|
+
return (<OverlayFrame theme={props.theme} viewportWidth={props.viewportWidth} viewportHeight={props.viewportHeight} viewportOrigin={props.viewportOrigin} width={68} height={19} title="Pane interaction" surface modal onDismiss={props.onDismiss}>
|
|
61
|
+
<scrollbox flexGrow={1}>
|
|
62
|
+
<box flexDirection="column" gap={1}>
|
|
63
|
+
<text fg={props.theme.roles.text.primary}>
|
|
64
|
+
<strong>{value().phase}</strong>
|
|
65
|
+
</text>
|
|
66
|
+
<text fg={props.theme.roles.text.secondary}>{value().explanation}</text>
|
|
67
|
+
<text fg={props.theme.roles.text.muted}>From</text>
|
|
68
|
+
<text fg={props.theme.roles.text.primary}>{value().source}</text>
|
|
69
|
+
<text fg={props.theme.roles.text.muted}>To</text>
|
|
70
|
+
<text fg={props.theme.roles.text.primary}>{value().target}</text>
|
|
71
|
+
<text fg={props.theme.roles.text.secondary}>{props.event.at}</text>
|
|
72
|
+
<TuiButton theme={props.theme} label="Close" shortcut="Esc" onPress={props.onDismiss}/>
|
|
73
|
+
</box>
|
|
74
|
+
</scrollbox>
|
|
75
|
+
</OverlayFrame>);
|
|
76
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* @jsxImportSource @opentui/solid */
|
|
2
|
+
import { Show } from "solid-js";
|
|
3
|
+
import { clipTerminal, terminalDisplayWidth } from "../terminal-text.js";
|
|
4
|
+
import { TuiButton } from "./button.jsx";
|
|
5
|
+
/** Fixed-budget mode description and an explicit action, never terminal geometry. */
|
|
6
|
+
export function PaneModeControl(props) {
|
|
7
|
+
const action = () => (props.scrollback ? "Back to live" : "Restore");
|
|
8
|
+
const actionWidth = () => Math.min(props.width, terminalDisplayWidth(action()) + 2);
|
|
9
|
+
const labelWidth = () => Math.max(0, props.width - actionWidth());
|
|
10
|
+
const label = () => props.scrollback
|
|
11
|
+
? props.linesAboveLive && labelWidth() >= 28
|
|
12
|
+
? ` ↑ Scrollback · ${props.linesAboveLive} lines up `
|
|
13
|
+
: " Scrollback "
|
|
14
|
+
: " Expanded ";
|
|
15
|
+
return (<box width={props.width} height={1} flexShrink={0} flexDirection="row" backgroundColor={props.theme.roles.surfaces.panel}>
|
|
16
|
+
<Show when={labelWidth() > 0}>
|
|
17
|
+
<text width={labelWidth()} fg={props.theme.roles.text.secondary}>
|
|
18
|
+
{clipTerminal(label(), labelWidth())}
|
|
19
|
+
</text>
|
|
20
|
+
</Show>
|
|
21
|
+
<TuiButton theme={props.theme} label={action()} size="compact" width={actionWidth()} onPress={() => (props.scrollback ? props.onBackToLive?.() : props.onRestore?.())}/>
|
|
22
|
+
</box>);
|
|
23
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Availability > input capability > urgent activity > local mode > routine activity. No output inference. */
|
|
2
|
+
export function statusPresentation(facts) {
|
|
3
|
+
if (facts.unavailable)
|
|
4
|
+
return { label: "Unavailable", tone: "unknown" };
|
|
5
|
+
if (facts.connection && !["live", "read-only"].includes(facts.connection)) {
|
|
6
|
+
return {
|
|
7
|
+
label: facts.connection === "connecting"
|
|
8
|
+
? "Connecting…"
|
|
9
|
+
: facts.connection === "recovering" ||
|
|
10
|
+
facts.connection === "reconnecting" ||
|
|
11
|
+
facts.connection === "rebinding"
|
|
12
|
+
? "Reconnecting…"
|
|
13
|
+
: facts.connection === "unavailable" || facts.connection === "disposed"
|
|
14
|
+
? "Unavailable"
|
|
15
|
+
: "Disconnected",
|
|
16
|
+
tone: "unknown",
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (facts.activity === "disconnected")
|
|
20
|
+
return { label: "Unknown", tone: "unknown" };
|
|
21
|
+
if (facts.connection === "read-only")
|
|
22
|
+
return { label: "Read-only", tone: "unknown" };
|
|
23
|
+
if (facts.activity === "failed")
|
|
24
|
+
return { label: "Failed", tone: "blocked", activity: "failed" };
|
|
25
|
+
if (facts.attention || facts.activity === "waiting")
|
|
26
|
+
return { label: "Needs input", tone: "blocked", activity: "waiting" };
|
|
27
|
+
if (facts.scrollback)
|
|
28
|
+
return { label: "Scrollback", tone: "idle", action: "back-to-live" };
|
|
29
|
+
if (facts.expanded)
|
|
30
|
+
return { label: "Expanded", tone: "idle", action: "restore" };
|
|
31
|
+
switch (facts.activity) {
|
|
32
|
+
case "running":
|
|
33
|
+
return { label: "Working", tone: "working", activity: "running" };
|
|
34
|
+
case "complete":
|
|
35
|
+
return { label: "Done", tone: "done", activity: "complete" };
|
|
36
|
+
case "idle":
|
|
37
|
+
return { label: "Idle", tone: "idle", activity: "idle" };
|
|
38
|
+
default:
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|