tmux-ide 2.9.0-beta.28 → 2.9.0-beta.30
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 +1 -1
- package/package.json +1 -1
- package/packages/daemon/dist/tui/mirror/runtime/application-appearance-owner.js +13 -4
- package/packages/daemon/dist/tui/mirror/runtime/application-home-agent-roster.jsx +90 -21
- package/packages/daemon/dist/tui/mirror/runtime/application-home-agents-owner.js +23 -7
- package/packages/daemon/dist/tui/mirror/runtime/application-home-experience.js +10 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-home-fleet.js +18 -1
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +16 -16
- package/packages/daemon/dist/tui/mirror/runtime/application-palette-command-owner.js +23 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-palette-input.js +4 -1
- package/packages/daemon/dist/tui/mirror/runtime/application-shell-home.jsx +11 -9
- package/packages/daemon/dist/tui/mirror/runtime/application-shell-viewport.js +2 -2
- package/packages/daemon/dist/tui/mirror/runtime/semantic-shell-viewport-resize.js +2 -2
- package/packages/daemon/dist/tui/mirror/shell-chrome.js +2 -0
- package/packages/daemon/dist/tui/mirror/ui/activity-indicator.jsx +16 -0
- package/packages/daemon/dist/tui/mirror/ui/dialog.jsx +1 -1
- package/packages/daemon/dist/tui/mirror/ui/home-agent-search.jsx +66 -0
- package/packages/daemon/dist/tui/mirror/ui/key-hint.jsx +8 -2
- package/packages/daemon/dist/tui/mirror/ui/overlay-frame.jsx +24 -8
- package/packages/daemon/dist/tui/mirror/ui/overlay-list-row.jsx +34 -5
- package/packages/daemon/dist/tui/mirror/ui/overlay-model.js +7 -0
- package/packages/daemon/dist/tui/mirror/ui/overlay-surface.js +13 -0
- package/packages/daemon/dist/tui/mirror/workspace/application-command-description.js +23 -7
- package/packages/daemon/dist/tui/mirror/workspace/command-palette-surface-view.jsx +22 -16
- package/packages/daemon/dist/tui/mirror/workspace/command-palette-surface.js +9 -14
- package/packages/daemon/dist/tui/mirror/workspace/palette-list-layout.js +49 -0
- package/packages/daemon/src/tui/mirror/__snapshots__/shell-chrome-renderer.test.tsx.snap +2 -2
- package/packages/daemon/src/tui/mirror/runtime/__snapshots__/application-shell-view-renderer.test.tsx.snap +16 -16
- package/packages/daemon/src/tui/mirror/runtime/application-appearance-owner.ts +12 -4
- package/packages/daemon/src/tui/mirror/runtime/application-fleet-switcher.tsx +1 -3
- package/packages/daemon/src/tui/mirror/runtime/application-home-agent-roster.tsx +143 -27
- package/packages/daemon/src/tui/mirror/runtime/application-home-agents-owner.ts +24 -4
- package/packages/daemon/src/tui/mirror/runtime/application-home-experience.ts +20 -1
- package/packages/daemon/src/tui/mirror/runtime/application-home-fleet.ts +21 -1
- package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +24 -19
- package/packages/daemon/src/tui/mirror/runtime/application-palette-command-owner.ts +25 -0
- package/packages/daemon/src/tui/mirror/runtime/application-palette-input.ts +4 -1
- package/packages/daemon/src/tui/mirror/runtime/application-root-v2.tsx +19 -6
- package/packages/daemon/src/tui/mirror/runtime/application-shell-catalog.tsx +16 -9
- package/packages/daemon/src/tui/mirror/runtime/application-shell-home.tsx +27 -5
- package/packages/daemon/src/tui/mirror/runtime/application-shell-overlays.tsx +174 -140
- package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +14 -5
- package/packages/daemon/src/tui/mirror/runtime/application-shell-viewport.ts +2 -1
- package/packages/daemon/src/tui/mirror/runtime/palette-production-overlay.tsx +5 -8
- package/packages/daemon/src/tui/mirror/runtime/semantic-shell-viewport-resize.ts +2 -1
- package/packages/daemon/src/tui/mirror/shell-chrome-view.tsx +22 -16
- package/packages/daemon/src/tui/mirror/shell-chrome.ts +1 -0
- package/packages/daemon/src/tui/mirror/ui/activity-indicator.tsx +19 -0
- package/packages/daemon/src/tui/mirror/ui/dialog.tsx +2 -0
- package/packages/daemon/src/tui/mirror/ui/home-agent-search.tsx +81 -0
- package/packages/daemon/src/tui/mirror/ui/key-hint.tsx +11 -2
- package/packages/daemon/src/tui/mirror/ui/overlay-frame.tsx +49 -14
- package/packages/daemon/src/tui/mirror/ui/overlay-list-row.tsx +46 -9
- package/packages/daemon/src/tui/mirror/ui/overlay-model.ts +8 -0
- package/packages/daemon/src/tui/mirror/ui/overlay-search-field.tsx +34 -0
- package/packages/daemon/src/tui/mirror/ui/overlay-surface.ts +19 -0
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/application-shell-renderer.test.tsx.snap +2 -2
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/command-palette-surface-renderer.test.tsx.snap +168 -168
- package/packages/daemon/src/tui/mirror/workspace/application-command-description.ts +35 -8
- package/packages/daemon/src/tui/mirror/workspace/command-palette-surface-view.tsx +24 -16
- package/packages/daemon/src/tui/mirror/workspace/command-palette-surface.ts +9 -14
- package/packages/daemon/src/tui/mirror/workspace/palette-list-layout.ts +56 -0
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.30",
|
|
11283
11283
|
description: "A visual, agent-aware IDE for any tmux session, with optional workspace presets",
|
|
11284
11284
|
type: "module",
|
|
11285
11285
|
bin: {
|
package/package.json
CHANGED
|
@@ -77,7 +77,7 @@ export function createAppearanceOwner(config, renderer, terminalPaletteOwner) {
|
|
|
77
77
|
{ id: "system", name: "System · follow terminal" },
|
|
78
78
|
{ id: "dark", name: "Dark" },
|
|
79
79
|
{ id: "light", name: "Light" },
|
|
80
|
-
...VISUAL_THEME_PRESETS,
|
|
80
|
+
...[...VISUAL_THEME_PRESETS].sort((a, b) => a.name.localeCompare(b.name)),
|
|
81
81
|
];
|
|
82
82
|
const pickerOptions = () => options.filter((p) => `${p.id} ${p.name}`.toLowerCase().includes(pickerQuery().toLowerCase()));
|
|
83
83
|
const apply = (id) => {
|
|
@@ -102,6 +102,14 @@ export function createAppearanceOwner(config, renderer, terminalPaletteOwner) {
|
|
|
102
102
|
if (options.some((p) => p.id === id))
|
|
103
103
|
apply(id);
|
|
104
104
|
};
|
|
105
|
+
const filterPicker = (query) => {
|
|
106
|
+
setPickerQuery(query);
|
|
107
|
+
const first = pickerOptions()[0];
|
|
108
|
+
if (!query)
|
|
109
|
+
preview(originalSelection);
|
|
110
|
+
else if (first)
|
|
111
|
+
preview(first.id);
|
|
112
|
+
};
|
|
105
113
|
const cancelPicker = () => {
|
|
106
114
|
if (!pickerOpen())
|
|
107
115
|
return;
|
|
@@ -111,7 +119,7 @@ export function createAppearanceOwner(config, renderer, terminalPaletteOwner) {
|
|
|
111
119
|
setPickerError(null);
|
|
112
120
|
};
|
|
113
121
|
const savePicker = () => {
|
|
114
|
-
if (!pickerOpen())
|
|
122
|
+
if (!pickerOpen() || !pickerOptions().some((option) => option.id === pickerSelection()))
|
|
115
123
|
return;
|
|
116
124
|
try {
|
|
117
125
|
updateAppConfig({
|
|
@@ -150,6 +158,7 @@ export function createAppearanceOwner(config, renderer, terminalPaletteOwner) {
|
|
|
150
158
|
pickerError,
|
|
151
159
|
pickerQuery,
|
|
152
160
|
pickerSelection,
|
|
161
|
+
pickerOriginalSelection: () => originalSelection,
|
|
153
162
|
pickerOptions,
|
|
154
163
|
openPicker() {
|
|
155
164
|
if (pickerOpen())
|
|
@@ -188,12 +197,12 @@ export function createAppearanceOwner(config, renderer, terminalPaletteOwner) {
|
|
|
188
197
|
}
|
|
189
198
|
}
|
|
190
199
|
else if (name === "backspace") {
|
|
191
|
-
|
|
200
|
+
filterPicker(pickerQuery().slice(0, -1));
|
|
192
201
|
}
|
|
193
202
|
else if (!event.ctrl && !event.meta) {
|
|
194
203
|
const text = event.sequence ?? (name.length === 1 ? name : "");
|
|
195
204
|
if (text.length === 1 && text >= " ")
|
|
196
|
-
|
|
205
|
+
filterPicker((pickerQuery() + text).slice(0, 80));
|
|
197
206
|
}
|
|
198
207
|
return true;
|
|
199
208
|
},
|
|
@@ -4,6 +4,10 @@ import { clipTerminal, terminalDisplayWidth } from "../terminal-text.js";
|
|
|
4
4
|
import { NavigationRow } from "../ui/navigation-row.jsx";
|
|
5
5
|
import { TuiButton } from "../ui/button.jsx";
|
|
6
6
|
import { useKeyboardRoute } from "../ui/keyboard-router.jsx";
|
|
7
|
+
import { applicationPaneRenameKeyAction, applicationPaneRenamePaste, } from "./application-pane-rename-input.js";
|
|
8
|
+
import { HomeAgentSearch } from "../ui/home-agent-search.jsx";
|
|
9
|
+
import { ActivityIndicator } from "../ui/activity-indicator.jsx";
|
|
10
|
+
import { componentPalette } from "../ui/state.js";
|
|
7
11
|
import { homeAgentStatusLabel, } from "./application-home-agents.js";
|
|
8
12
|
function padCells(text, width) {
|
|
9
13
|
const clipped = clipTerminal(text, Math.max(0, width));
|
|
@@ -21,6 +25,7 @@ function statusTone(row) {
|
|
|
21
25
|
/** A flat projected roster; no subscriptions, navigation effects, or terminal input ingress. */
|
|
22
26
|
export function HomeAgentRoster(props) {
|
|
23
27
|
const [hovered, setHovered] = createSignal(null);
|
|
28
|
+
const [editing, setEditing] = createSignal(false);
|
|
24
29
|
const width = () => Math.max(0, Math.floor(props.width));
|
|
25
30
|
const height = () => Math.max(0, Math.floor(props.height));
|
|
26
31
|
const stale = (row) => row.disabled ||
|
|
@@ -29,7 +34,14 @@ export function HomeAgentRoster(props) {
|
|
|
29
34
|
const showRecovery = () => Boolean(((props.snapshot.phase === "unavailable" || props.snapshot.unavailableSessions > 0) &&
|
|
30
35
|
props.onRetry) ||
|
|
31
36
|
(props.snapshot.truncatedSessions > 0 && props.onLoadMore));
|
|
32
|
-
const
|
|
37
|
+
const rowHeight = () => (width() >= 60 && height() >= 16 ? 2 : 1);
|
|
38
|
+
const searchRows = () => (props.onQueryChange ? 2 : 0);
|
|
39
|
+
const visibleCount = () => Math.max(0, Math.floor((height() - 5 - searchRows() - (showRecovery() ? 1 : 0)) / rowHeight()));
|
|
40
|
+
const openSelected = () => {
|
|
41
|
+
const row = props.snapshot.rows.find((row) => row.key === props.selection.selectedKey);
|
|
42
|
+
if (props.inputActive && visibleCount() > 0 && row?.paneId && !stale(row))
|
|
43
|
+
props.onOpen(row, "keyboard");
|
|
44
|
+
};
|
|
33
45
|
createEffect(() => props.onViewport(visibleCount()));
|
|
34
46
|
const offset = () => Math.max(0, Math.min(props.selection.scrollOffset, Math.max(0, props.snapshot.rows.length - visibleCount())));
|
|
35
47
|
const visible = createMemo(() => props.snapshot.rows.slice(offset(), offset() + visibleCount()));
|
|
@@ -48,6 +60,8 @@ export function HomeAgentRoster(props) {
|
|
|
48
60
|
return "Agent overview unavailable";
|
|
49
61
|
if (props.snapshot.phase === "loading" && props.snapshot.rows.length === 0)
|
|
50
62
|
return "Loading agent overview…";
|
|
63
|
+
if (props.query && props.snapshot.rows.length === 0)
|
|
64
|
+
return "No matching agents";
|
|
51
65
|
if (props.snapshot.phase === "live" && props.snapshot.rows.length === 0)
|
|
52
66
|
return "No agents reported";
|
|
53
67
|
const count = props.snapshot.rows.length;
|
|
@@ -59,6 +73,8 @@ export function HomeAgentRoster(props) {
|
|
|
59
73
|
const footer = () => {
|
|
60
74
|
if (props.snapshot.note)
|
|
61
75
|
return props.snapshot.note;
|
|
76
|
+
if (props.query && props.snapshot.rows.length === 0)
|
|
77
|
+
return "Search covers loaded observations · Esc clears search";
|
|
62
78
|
if (props.snapshot.rows.length === 0) {
|
|
63
79
|
if (props.snapshot.phase === "live")
|
|
64
80
|
return "Observed sessions have no agent entries. F2 opens terminals.";
|
|
@@ -79,17 +95,19 @@ export function HomeAgentRoster(props) {
|
|
|
79
95
|
if (!props.inputActive || event.eventType !== "press" || event.ctrl || event.meta)
|
|
80
96
|
return false;
|
|
81
97
|
const key = event.name.toLowerCase();
|
|
82
|
-
const filter =
|
|
98
|
+
const filter = !editing() &&
|
|
99
|
+
(key === "f" ? props.onCycleMachine : key === "a" ? props.onToggleAttention : undefined);
|
|
83
100
|
if (filter) {
|
|
84
101
|
event.preventDefault();
|
|
85
102
|
event.stopPropagation();
|
|
86
103
|
filter();
|
|
87
104
|
return true;
|
|
88
105
|
}
|
|
89
|
-
const retry =
|
|
106
|
+
const retry = !editing() &&
|
|
107
|
+
key === "r" &&
|
|
90
108
|
(props.snapshot.phase === "unavailable" || props.snapshot.unavailableSessions > 0) &&
|
|
91
109
|
props.onRetry;
|
|
92
|
-
const more = key === "m" && props.snapshot.truncatedSessions > 0 && props.onLoadMore;
|
|
110
|
+
const more = !editing() && key === "m" && props.snapshot.truncatedSessions > 0 && props.onLoadMore;
|
|
93
111
|
const recovery = retry || more;
|
|
94
112
|
if (recovery) {
|
|
95
113
|
event.preventDefault();
|
|
@@ -112,7 +130,7 @@ export function HomeAgentRoster(props) {
|
|
|
112
130
|
props.onMove(delta);
|
|
113
131
|
return true;
|
|
114
132
|
});
|
|
115
|
-
return (<box width={width()} height={height()} flexShrink={0} flexDirection="column" overflow="hidden" onMouseScroll={(event) => {
|
|
133
|
+
return (<box position="relative" width={width()} height={height()} flexShrink={0} flexDirection="column" overflow="hidden" onMouseScroll={(event) => {
|
|
116
134
|
if (!props.inputActive)
|
|
117
135
|
return;
|
|
118
136
|
const direction = event.scroll?.direction;
|
|
@@ -122,38 +140,89 @@ export function HomeAgentRoster(props) {
|
|
|
122
140
|
event.stopPropagation();
|
|
123
141
|
props.onMove(direction === "up" ? -1 : 1);
|
|
124
142
|
}} onMouseOut={() => setHovered(null)}>
|
|
125
|
-
<
|
|
126
|
-
{
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
{
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<
|
|
143
|
+
<box width={width()} height={1} flexShrink={0} flexDirection="row">
|
|
144
|
+
<Show when={props.snapshot.phase === "loading" || props.snapshot.loadingSessions > 0}>
|
|
145
|
+
<ActivityIndicator theme={props.theme} active={props.inputActive}/>
|
|
146
|
+
</Show>
|
|
147
|
+
<text height={1} flexGrow={1} fg={props.theme.roles.text.primary}>
|
|
148
|
+
{clipTerminal(title(), width())}
|
|
149
|
+
</text>
|
|
150
|
+
</box>
|
|
151
|
+
<box height={searchRows()} flexShrink={0}/>
|
|
152
|
+
<box width={width()} height={1} flexShrink={0} flexDirection="row">
|
|
153
|
+
<text width={Math.max(0, width() - (props.onToggleAttention ? 16 : 0))} height={1} fg={props.theme.roles.text.secondary} onMouseDown={(event) => {
|
|
154
|
+
if (!props.inputActive || event.button !== 0)
|
|
155
|
+
return;
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
event.stopPropagation();
|
|
158
|
+
props.onCycleMachine?.();
|
|
159
|
+
}}>
|
|
160
|
+
{clipTerminal(props.filterLabel ? `${props.filterLabel} · f machine` : "", Math.max(0, width() - (props.onToggleAttention ? 16 : 0)))}
|
|
161
|
+
</text>
|
|
162
|
+
<Show when={props.onToggleAttention}>
|
|
163
|
+
<TuiButton theme={props.theme} label="Attention" shortcut="a" size="compact" variant="ghost" background={props.theme.roles.surfaces.canvas} width={Math.min(16, width())} onPress={() => {
|
|
164
|
+
if (props.inputActive)
|
|
165
|
+
props.onToggleAttention?.();
|
|
166
|
+
}}/>
|
|
167
|
+
</Show>
|
|
168
|
+
</box>
|
|
169
|
+
<Show when={props.snapshot.rows.length > 0} fallback={<box height={Math.max(0, visibleCount() * rowHeight() + 1)} flexShrink={0}/>}>
|
|
135
170
|
<text width={width()} height={1} flexShrink={0} fg={props.theme.roles.text.muted}>
|
|
136
|
-
{clipTerminal(` ${padCells("
|
|
171
|
+
{clipTerminal(` ${padCells("Agent", columns().agent)}${padCells(props.filterLabel ? "Workspace / machine" : "Session", columns().session)} ${padCells("Status", columns().status)}`, width())}
|
|
137
172
|
</text>
|
|
138
|
-
<box height={visibleCount()} width={width()} flexShrink={0} flexDirection="column" overflow="hidden">
|
|
173
|
+
<box height={visibleCount() * rowHeight()} width={width()} flexShrink={0} flexDirection="column" overflow="hidden">
|
|
139
174
|
<For each={keys()}>
|
|
140
175
|
{(key) => {
|
|
141
176
|
const row = () => byKey().get(key);
|
|
142
|
-
return (<box height={
|
|
177
|
+
return (<box height={rowHeight()} flexDirection="column" backgroundColor={componentPalette(props.theme, {
|
|
178
|
+
selected: props.selection.selectedKey === key,
|
|
179
|
+
disabled: row().paneId === null || stale(row()),
|
|
180
|
+
}).background} width={width()} flexShrink={0} onMouseDown={(event) => {
|
|
181
|
+
if (!props.inputActive || event.button !== 0 || !row().paneId || stale(row()))
|
|
182
|
+
return;
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
event.stopPropagation();
|
|
185
|
+
props.onSelect(key);
|
|
186
|
+
props.onOpen(row(), "mouse");
|
|
187
|
+
}} onMouseOver={() => setHovered(key)} onMouseMove={() => setHovered(key)}>
|
|
143
188
|
<NavigationRow theme={props.theme} id={`home-agent:${key}`} width={width()} label={padCells(row().name, columns().agent) +
|
|
144
|
-
padCells(
|
|
145
|
-
|
|
146
|
-
|
|
189
|
+
padCells(rowHeight() > 1
|
|
190
|
+
? row().sessionName
|
|
191
|
+
: [row().machineLabel, row().serverLabel, row().sessionName]
|
|
192
|
+
.filter(Boolean)
|
|
193
|
+
.join(" / "), columns().session)} detail={padCells(`${!stale(row()) && row().attention ? "! " : ""}${stale(row()) ? "last seen" : homeAgentStatusLabel(row().activity).toLowerCase()}`, columns().status)} detailAlign="end" marker=" " selected={props.selection.selectedKey === key} focused={props.inputActive && !editing() && props.selection.selectedKey === key} hovered={hovered() === key} status={stale(row()) ? "unknown" : statusTone(row())} disabled={row().paneId === null || stale(row())} onActivate={(source) => {
|
|
147
194
|
if (!props.inputActive || row().paneId === null || stale(row()))
|
|
148
195
|
return;
|
|
149
196
|
props.onSelect(key);
|
|
150
197
|
props.onOpen(row(), source);
|
|
151
198
|
}}/>
|
|
199
|
+
<Show when={rowHeight() > 1}>
|
|
200
|
+
<text width={width()} height={1} fg={props.selection.selectedKey === key
|
|
201
|
+
? props.theme.roles.selection.selectionText
|
|
202
|
+
: props.theme.roles.text.muted} bg={componentPalette(props.theme, {
|
|
203
|
+
selected: props.selection.selectedKey === key,
|
|
204
|
+
disabled: row().paneId === null || stale(row()),
|
|
205
|
+
}).background}>
|
|
206
|
+
{clipTerminal(` ${padCells(row().harness, columns().agent)}${[row().machineLabel, row().serverLabel].filter(Boolean).join(" / ")}`, width())}
|
|
207
|
+
</text>
|
|
208
|
+
</Show>
|
|
152
209
|
</box>);
|
|
153
210
|
}}
|
|
154
211
|
</For>
|
|
155
212
|
</box>
|
|
156
213
|
</Show>
|
|
214
|
+
<Show when={props.onQueryChange}>
|
|
215
|
+
{(onQueryChange) => (<box position="absolute" top={1} left={0} width={width()} height={1}>
|
|
216
|
+
<HomeAgentSearch theme={props.theme} width={width()} query={props.query ?? ""} active={props.inputActive} onChange={onQueryChange()} onEdit={(event) => {
|
|
217
|
+
const action = applicationPaneRenameKeyAction(event, props.query ?? "");
|
|
218
|
+
if (action.kind === "update")
|
|
219
|
+
onQueryChange()(action.value);
|
|
220
|
+
}} onPaste={(bytes) => onQueryChange()(applicationPaneRenamePaste(props.query ?? "", bytes))} onEditing={setEditing} onSubmit={openSelected}/>
|
|
221
|
+
</box>)}
|
|
222
|
+
</Show>
|
|
223
|
+
<text width={width()} height={1} flexShrink={0} fg={props.theme.roles.text.muted}>
|
|
224
|
+
{clipTerminal(coverage(), width())}
|
|
225
|
+
</text>
|
|
157
226
|
<text width={width()} height={1} flexShrink={0} fg={props.theme.roles.text.muted}>
|
|
158
227
|
{clipTerminal(footer(), width())}
|
|
159
228
|
</text>
|
|
@@ -189,12 +189,21 @@ export function createApplicationHomeNavigationOwner(options) {
|
|
|
189
189
|
observer: options.observer,
|
|
190
190
|
});
|
|
191
191
|
const paletteCommands = createApplicationPaletteCommandOwner({
|
|
192
|
-
commands: () =>
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
192
|
+
commands: () => [
|
|
193
|
+
...(options.fleetCommands
|
|
194
|
+
? [
|
|
195
|
+
...applicationPaletteCommands(options.shell().semantic).filter((c) => typeof c === "string"),
|
|
196
|
+
...options.fleetCommands(),
|
|
197
|
+
]
|
|
198
|
+
: applicationPaletteCommands(options.shell().semantic, options.catalog.sessionNames())),
|
|
199
|
+
...(options.toggleSidebar
|
|
200
|
+
? [
|
|
201
|
+
options.sidebarVisible?.() === false
|
|
202
|
+
? "show-sidebar"
|
|
203
|
+
: "hide-sidebar",
|
|
204
|
+
]
|
|
205
|
+
: []),
|
|
206
|
+
],
|
|
198
207
|
isOpen: () => Boolean(options.shell().semantic?.focus.palette.open ?? options.shell().localPaletteOpen),
|
|
199
208
|
targetKey: () => `${applicationGenerationNavigationKey(options.sessionOwner()?.snapshot() ?? null)}:${options.focusedPane()}`,
|
|
200
209
|
disabledReason: (command) => {
|
|
@@ -203,7 +212,12 @@ export function createApplicationHomeNavigationOwner(options) {
|
|
|
203
212
|
if (typeof command === "object" ||
|
|
204
213
|
command === "home" ||
|
|
205
214
|
command === "terminals" ||
|
|
206
|
-
command === "appearance"
|
|
215
|
+
command === "appearance" ||
|
|
216
|
+
command === "hide-sidebar" ||
|
|
217
|
+
command === "show-sidebar" ||
|
|
218
|
+
command === "shortcuts" ||
|
|
219
|
+
command === "whats-new" ||
|
|
220
|
+
command === "switch-session")
|
|
207
221
|
return null;
|
|
208
222
|
if (options.sessionOwner()?.snapshot()?.status !== "live")
|
|
209
223
|
return "Open a live session first";
|
|
@@ -215,6 +229,8 @@ export function createApplicationHomeNavigationOwner(options) {
|
|
|
215
229
|
setSurface: options.setSurface,
|
|
216
230
|
setNote: options.setNote,
|
|
217
231
|
openAppearance: options.openAppearance,
|
|
232
|
+
toggleSidebar: options.toggleSidebar,
|
|
233
|
+
openSessions: options.openSessions,
|
|
218
234
|
zoomPane: options.zoomPane,
|
|
219
235
|
newWindow: options.interaction.newWindow,
|
|
220
236
|
splitPane: options.interaction.splitPane,
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
import { createEffect } from "solid-js";
|
|
1
2
|
import { createApplicationHomeFleetOwner } from "./application-home-fleet.js";
|
|
2
3
|
import { createApplicationHomeNavigationOwner } from "./application-home-agents-owner.js";
|
|
3
4
|
import { createApplicationGuidedTourIntegration } from "./application-guided-tour-integration.jsx";
|
|
5
|
+
/** A late modal dismissal must not restore focus into Home's hidden sidebar. */
|
|
6
|
+
export function createHomeSidebarFocusGuard(surface, focused, blur, sidebarVisible = () => true) {
|
|
7
|
+
createEffect(() => {
|
|
8
|
+
if ((surface() === "home" || !sidebarVisible()) && focused())
|
|
9
|
+
blur();
|
|
10
|
+
});
|
|
11
|
+
}
|
|
4
12
|
/** Compose Home's fleet, navigation and learning experience without additional transports. */
|
|
5
13
|
export function createApplicationHomeExperience(options) {
|
|
6
14
|
const { machines, appearance } = options;
|
|
15
|
+
createHomeSidebarFocusGuard(options.activeSurface, machines.focused, () => machines.sidebar.onBlur?.(), options.sidebarVisible);
|
|
7
16
|
const paletteOpen = () => options.shell().semantic?.focus.palette.open ?? options.shell().localPaletteOpen;
|
|
8
17
|
const fleetHome = createApplicationHomeFleetOwner({
|
|
9
18
|
catalog: machines.catalog,
|
|
@@ -23,6 +32,7 @@ export function createApplicationHomeExperience(options) {
|
|
|
23
32
|
fleetHome: fleetHome.presentation,
|
|
24
33
|
fleetCommands: machines.paletteCommands,
|
|
25
34
|
openFleet: machines.openPalette,
|
|
35
|
+
openSessions: () => machines.showSwitcher(false),
|
|
26
36
|
});
|
|
27
37
|
const tour = createApplicationGuidedTourIntegration({
|
|
28
38
|
...options,
|
|
@@ -41,6 +41,17 @@ export function projectHomeFleet(catalog, groups, filter) {
|
|
|
41
41
|
rows.sort((a, b) => a.key.localeCompare(b.key));
|
|
42
42
|
const missing = machines.filter((machine) => machine.state !== "ready" ||
|
|
43
43
|
!groups.find((group) => group.machineId === machine.id)?.available);
|
|
44
|
+
const query = filter.query?.trim().toLocaleLowerCase() ?? "";
|
|
45
|
+
const matching = query
|
|
46
|
+
? rows.filter((row) => [
|
|
47
|
+
row.name,
|
|
48
|
+
row.harness,
|
|
49
|
+
row.projectName,
|
|
50
|
+
row.sessionName,
|
|
51
|
+
row.machineLabel,
|
|
52
|
+
row.serverLabel,
|
|
53
|
+
].some((value) => value?.toLocaleLowerCase().includes(query)))
|
|
54
|
+
: rows;
|
|
44
55
|
return {
|
|
45
56
|
phase: missing.length
|
|
46
57
|
? rows.length || observedSessions
|
|
@@ -49,7 +60,7 @@ export function projectHomeFleet(catalog, groups, filter) {
|
|
|
49
60
|
? "loading"
|
|
50
61
|
: "unavailable"
|
|
51
62
|
: "live",
|
|
52
|
-
rows,
|
|
63
|
+
rows: matching,
|
|
53
64
|
observedSessions,
|
|
54
65
|
totalSessions,
|
|
55
66
|
loadingSessions,
|
|
@@ -92,6 +103,12 @@ export function createApplicationHomeFleetOwner(options) {
|
|
|
92
103
|
selection.dispose();
|
|
93
104
|
});
|
|
94
105
|
const presentation = {
|
|
106
|
+
get agentQuery() {
|
|
107
|
+
return filter().query ?? "";
|
|
108
|
+
},
|
|
109
|
+
onAgentQueryChange(query) {
|
|
110
|
+
setFilter((value) => ({ ...value, query }));
|
|
111
|
+
},
|
|
95
112
|
get agentRoster() {
|
|
96
113
|
return snapshot();
|
|
97
114
|
},
|
|
@@ -165,10 +165,13 @@ export function ApplicationMachineSidebar(props) {
|
|
|
165
165
|
else
|
|
166
166
|
toggle(row.group);
|
|
167
167
|
};
|
|
168
|
+
const sectionGap = (row) => !row.session && !row.agent && !row.server && row.group.id !== props.model.groups()[0]?.id
|
|
169
|
+
? 1
|
|
170
|
+
: 0;
|
|
171
|
+
const rowHeight = (row) => (row.agent ? (row.agentHeading ? 3 : 2) : row.serverHeading ? 2 : 1) + sectionGap(row);
|
|
168
172
|
const revealFocusedRow = () => {
|
|
169
173
|
if (!focused() || !scroll)
|
|
170
174
|
return;
|
|
171
|
-
const rowHeight = (row) => row.agent ? (row.agentHeading ? 3 : 2) : row.serverHeading ? 2 : 1;
|
|
172
175
|
const y = rows()
|
|
173
176
|
.slice(0, index())
|
|
174
177
|
.reduce((sum, row) => sum + rowHeight(row), 0);
|
|
@@ -342,7 +345,8 @@ export function ApplicationMachineSidebar(props) {
|
|
|
342
345
|
return current().agentHeading;
|
|
343
346
|
},
|
|
344
347
|
};
|
|
345
|
-
return (<box width={Math.max(1, props.width - 1)} height={
|
|
348
|
+
return (<box width={Math.max(1, props.width - 1)} height={rowHeight(row)} flexShrink={0} flexDirection="column">
|
|
349
|
+
<box height={sectionGap(row)} flexShrink={0}/>
|
|
346
350
|
<Show when={row.serverHeading}>
|
|
347
351
|
<NavigationRow theme={props.theme} width={Math.max(1, props.width - 1)} id={`server:${row.group.id}:${row.session?.server?.serverId}`} label={row.serverHeading ?? "Server"} marker=" ▾" onActivate={() => {
|
|
348
352
|
if (row.session?.server)
|
|
@@ -364,19 +368,13 @@ export function ApplicationMachineSidebar(props) {
|
|
|
364
368
|
: row.group.label} marker={row.server
|
|
365
369
|
? " "
|
|
366
370
|
: row.agent
|
|
367
|
-
?
|
|
368
|
-
?
|
|
369
|
-
|
|
370
|
-
: "›"
|
|
371
|
-
: row.agent.attention
|
|
372
|
-
? "!"
|
|
373
|
-
: "•"
|
|
371
|
+
? row.agent.attention
|
|
372
|
+
? "!"
|
|
373
|
+
: " "
|
|
374
374
|
: row.session
|
|
375
375
|
? props.model.favorites?.().includes(row.session.id)
|
|
376
376
|
? " ★"
|
|
377
|
-
:
|
|
378
|
-
? " ›"
|
|
379
|
-
: " "
|
|
377
|
+
: " "
|
|
380
378
|
: collapsed().has(preferenceKey(row.group))
|
|
381
379
|
? "▸"
|
|
382
380
|
: "▾"} detail={row.server
|
|
@@ -386,14 +384,16 @@ export function ApplicationMachineSidebar(props) {
|
|
|
386
384
|
: row.agent
|
|
387
385
|
? row.group.state !== "ready" || row.agent.disabled
|
|
388
386
|
? "unavailable"
|
|
389
|
-
:
|
|
387
|
+
: terminalAgentStatusLabel(row.agent.activity).toLowerCase()
|
|
390
388
|
: row.session
|
|
391
389
|
? row.group.state !== "ready" || row.session.disabled
|
|
392
390
|
? "unavailable"
|
|
393
391
|
: `${row.session.paneCount}p${row.group.agents?.length ? ` ${activity(row).label}` : ""}`
|
|
394
392
|
: row.group.agents?.length && row.group.state === "ready"
|
|
395
393
|
? activity(row).label
|
|
396
|
-
: `${connectionDetail(row.group)}${row.group.agents?.length ? " ?" : ""}`} selected={Boolean((row.session || row.agent) && active(row))} focused={Boolean(focused() && row.key === selectedKey())}
|
|
394
|
+
: `${connectionDetail(row.group)}${row.group.agents?.length ? " ?" : ""}`} selected={Boolean((row.session || row.agent) && active(row))} focused={Boolean(focused() && row.key === selectedKey())} status={(row.agent?.attention ?? activity(row).kind === "attention")
|
|
395
|
+
? "blocked"
|
|
396
|
+
: undefined} onActivate={(source) => activate(row, source)}/>
|
|
397
397
|
<Show when={row.agent}>
|
|
398
398
|
{(agent) => (<text height={1} fg={props.theme.roles.text.muted} content={clipTerminal(` ${friendlySessionLabel(agent().sessionName)}`, Math.max(1, props.width - 1))}/>)}
|
|
399
399
|
</Show>
|
|
@@ -412,7 +412,7 @@ export function ApplicationMachineSidebar(props) {
|
|
|
412
412
|
</text>
|
|
413
413
|
</Show>
|
|
414
414
|
<Show when={props.model.onOpenSwitcher}>
|
|
415
|
-
<NavigationRow theme={props.theme} id="machine:switcher" label="
|
|
415
|
+
<NavigationRow theme={props.theme} id="machine:switcher" label="Sessions" detail="F6" marker=" " width={props.width} onActivate={() => props.model.onOpenSwitcher?.()}/>
|
|
416
416
|
</Show>
|
|
417
417
|
<Show when={props.model.onOpenAttention}>
|
|
418
418
|
<NavigationRow theme={props.theme} id="machine:attention" label={`Attention (${props.model.groups().reduce((sum, group) => sum + (group.state === "ready" ? (group.agents ?? []).filter((agent) => agent.attention && !agent.disabled).length : 0), 0)}) · F7`} marker="!" width={props.width} onActivate={() => props.model.onOpenAttention?.()}/>
|
|
@@ -429,7 +429,7 @@ export function ApplicationMachineSidebar(props) {
|
|
|
429
429
|
</>)}
|
|
430
430
|
</Show>
|
|
431
431
|
<For each={props.model.onAddMachine ? [props.model.onAddMachine] : []}>
|
|
432
|
-
{(add) => (<NavigationRow theme={props.theme} id="machine:add" label="Add machine
|
|
432
|
+
{(add) => (<NavigationRow theme={props.theme} id="machine:add" label="Add machine" detail="A" marker=" " width={props.width} onActivate={add}/>)}
|
|
433
433
|
</For>
|
|
434
434
|
</Surface>);
|
|
435
435
|
}
|
|
@@ -2,6 +2,7 @@ import { createSignal } from "solid-js";
|
|
|
2
2
|
import { applicationPaletteCommands } from "./application-palette-input.js";
|
|
3
3
|
import { createApplicationPaletteSearchOwner } from "./application-palette-search-owner.js";
|
|
4
4
|
export function createApplicationPaletteCommandOwner(options) {
|
|
5
|
+
const [referencePage, setReferencePage] = createSignal();
|
|
5
6
|
const [closeArmed, setCloseArmed] = createSignal(false);
|
|
6
7
|
const [busy, setBusy] = createSignal(false);
|
|
7
8
|
let armedTarget;
|
|
@@ -14,6 +15,7 @@ export function createApplicationPaletteCommandOwner(options) {
|
|
|
14
15
|
onChange: () => setCloseArmed(false),
|
|
15
16
|
});
|
|
16
17
|
const setOpen = (open, source) => {
|
|
18
|
+
setReferencePage(undefined);
|
|
17
19
|
openRevision += 1;
|
|
18
20
|
setCloseArmed(false);
|
|
19
21
|
if (open)
|
|
@@ -25,6 +27,25 @@ export function createApplicationPaletteCommandOwner(options) {
|
|
|
25
27
|
const activate = (command, source, confirmed = false) => {
|
|
26
28
|
if (busy())
|
|
27
29
|
return;
|
|
30
|
+
if (command === "shortcuts" || command === "whats-new") {
|
|
31
|
+
setCloseArmed(false);
|
|
32
|
+
setReferencePage(command === "shortcuts" ? "shortcuts" : "changes");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (command === "switch-session") {
|
|
36
|
+
if (!options.openSessions) {
|
|
37
|
+
options.setNote("Session switching is unavailable.");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
setOpen(false, source);
|
|
41
|
+
options.openSessions();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (command === "hide-sidebar" || command === "show-sidebar") {
|
|
45
|
+
setOpen(false, source);
|
|
46
|
+
options.toggleSidebar?.();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
28
49
|
if (command === "appearance") {
|
|
29
50
|
setOpen(false, source);
|
|
30
51
|
options.openAppearance?.();
|
|
@@ -124,6 +145,8 @@ export function createApplicationPaletteCommandOwner(options) {
|
|
|
124
145
|
};
|
|
125
146
|
return {
|
|
126
147
|
...search,
|
|
148
|
+
referencePage,
|
|
149
|
+
setReferencePage,
|
|
127
150
|
busy,
|
|
128
151
|
disabledReason: options.disabledReason ?? (() => null),
|
|
129
152
|
closeArmed: () => closeArmed() && armedTarget === options.targetKey?.(),
|
|
@@ -8,8 +8,11 @@ const BASE_COMMANDS = [
|
|
|
8
8
|
"split-right",
|
|
9
9
|
"split-down",
|
|
10
10
|
"close-pane",
|
|
11
|
-
"appearance",
|
|
12
11
|
"zoom-pane",
|
|
12
|
+
"appearance",
|
|
13
|
+
"shortcuts",
|
|
14
|
+
"whats-new",
|
|
15
|
+
"switch-session",
|
|
13
16
|
];
|
|
14
17
|
/** Agent commands are derived from the same semantic rows as the sidebar. */
|
|
15
18
|
export function applicationPaletteCommands(semantic, liveSessions = []) {
|
|
@@ -11,10 +11,12 @@ export function applicationHomeBrandVariant(_width, _height) {
|
|
|
11
11
|
export function ApplicationHomeSurface(props) {
|
|
12
12
|
const width = () => Math.max(0, Math.floor(props.width));
|
|
13
13
|
const height = () => Math.max(0, Math.floor(props.height));
|
|
14
|
-
const inset = () => (width() >= 40 ? 2 : width() >= 12 ? 1 : 0);
|
|
14
|
+
const inset = () => Math.max(width() >= 40 ? 2 : width() >= 12 ? 1 : 0, props.branded ? Math.floor((width() - 96) / 2) : 0);
|
|
15
15
|
const bodyWidth = () => Math.max(0, width() - inset() * 2);
|
|
16
16
|
const spacious = () => height() >= 14;
|
|
17
|
-
const context = () => clipTerminal(
|
|
17
|
+
const context = () => clipTerminal(props.branded && props.agentRoster
|
|
18
|
+
? "Across your machines"
|
|
19
|
+
: `${props.session ?? "No session selected"} · ${props.status}`, bodyWidth());
|
|
18
20
|
const summary = () => {
|
|
19
21
|
if (!props.session || props.agents === undefined)
|
|
20
22
|
return "Agent signals unavailable";
|
|
@@ -24,7 +26,7 @@ export function ApplicationHomeSurface(props) {
|
|
|
24
26
|
};
|
|
25
27
|
const themeLabel = () => `Theme: ${props.theme.setting}`;
|
|
26
28
|
// Use the existing TuiButton cell budget for both its label and hit target.
|
|
27
|
-
const naturalButtonWidth = (label, shortcut) => terminalDisplayWidth(label) + (shortcut ? terminalDisplayWidth(shortcut) + 1 : 0) +
|
|
29
|
+
const naturalButtonWidth = (label, shortcut) => terminalDisplayWidth(label) + (shortcut ? terminalDisplayWidth(shortcut) + 1 : 0) + 2;
|
|
28
30
|
const buttonWidth = (label, shortcut) => Math.min(bodyWidth(), naturalButtonWidth(label, shortcut));
|
|
29
31
|
const actionsInRow = () => bodyWidth() >=
|
|
30
32
|
naturalButtonWidth("Open terminals", "F2") +
|
|
@@ -64,7 +66,7 @@ export function ApplicationHomeSurface(props) {
|
|
|
64
66
|
const rosterHeight = () => Math.max(0, height() - reservedRows() - activityHeight());
|
|
65
67
|
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">
|
|
66
68
|
<text width={bodyWidth()} height={1} flexShrink={0} fg={props.theme.roles.text.primary}>
|
|
67
|
-
<strong>{clipTerminal(props.branded ? "
|
|
69
|
+
<strong>{clipTerminal(props.branded ? "Your agents" : props.project, bodyWidth())}</strong>
|
|
68
70
|
</text>
|
|
69
71
|
<box height={spacious() ? 1 : 0} flexShrink={0}/>
|
|
70
72
|
<text width={bodyWidth()} height={1} flexShrink={0} fg={props.theme.roles.text.secondary}>
|
|
@@ -79,7 +81,7 @@ export function ApplicationHomeSurface(props) {
|
|
|
79
81
|
{clipTerminal(summary(), bodyWidth())}
|
|
80
82
|
</text>
|
|
81
83
|
</>}>
|
|
82
|
-
{(snapshot) => (<HomeAgentRoster filterLabel={props.agentFilterLabel} onCycleMachine={props.onCycleAgentMachine} onToggleAttention={props.onToggleAgentAttention} theme={props.theme} width={bodyWidth()} height={rosterHeight()} snapshot={snapshot()} selection={props.agentSelection ?? { selectedKey: null, scrollOffset: 0 }} inputActive={props.agentInputActive ?? false} 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}/>)}
|
|
84
|
+
{(snapshot) => (<HomeAgentRoster query={props.agentQuery} onQueryChange={props.onAgentQueryChange} filterLabel={props.agentFilterLabel} onCycleMachine={props.onCycleAgentMachine} onToggleAttention={props.onToggleAgentAttention} theme={props.theme} width={bodyWidth()} height={rosterHeight()} snapshot={snapshot()} selection={props.agentSelection ?? { selectedKey: null, scrollOffset: 0 }} inputActive={props.agentInputActive ?? false} 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}/>)}
|
|
83
85
|
</Show>
|
|
84
86
|
<Show when={activityHeight() > 0}>
|
|
85
87
|
<box width={bodyWidth()} height={activityHeight()} flexShrink={0} flexDirection="column" overflow="hidden">
|
|
@@ -98,13 +100,13 @@ export function ApplicationHomeSurface(props) {
|
|
|
98
100
|
</Show>
|
|
99
101
|
<box height={spacious() ? 1 : 0} flexShrink={0}/>
|
|
100
102
|
<box width={bodyWidth()} flexShrink={0} flexDirection={actionsInRow() ? "row" : "column"} alignItems="flex-start" gap={actionsInRow() ? 2 : 0}>
|
|
101
|
-
<TuiButton theme={props.theme} label="Open terminals" shortcut="F2" width={buttonWidth("Open terminals", "F2")} variant="
|
|
102
|
-
<TuiButton theme={props.theme} label="Commands" shortcut="F5" width={buttonWidth("Commands", "F5")} onPress={props.onOpenCommands}/>
|
|
103
|
+
<TuiButton theme={props.theme} label="Open terminals" shortcut="F2" width={buttonWidth("Open terminals", "F2")} size="compact" variant="ghost" background={props.theme.roles.surfaces.canvas} onPress={props.onOpenTerminals}/>
|
|
104
|
+
<TuiButton theme={props.theme} label="Commands" size="compact" variant="ghost" background={props.theme.roles.surfaces.canvas} shortcut="F5" width={buttonWidth("Commands", "F5")} onPress={props.onOpenCommands}/>
|
|
103
105
|
<Show when={props.onOpenTutorial}>
|
|
104
|
-
{(open) => (<TuiButton theme={props.theme} label={props.tutorialLabel ?? "Learn tmux-ide"} width={buttonWidth(props.tutorialLabel ?? "Learn tmux-ide")} onPress={open()}/>)}
|
|
106
|
+
{(open) => (<TuiButton theme={props.theme} label={props.tutorialLabel ?? "Learn tmux-ide"} size="compact" variant="ghost" background={props.theme.roles.surfaces.canvas} width={buttonWidth(props.tutorialLabel ?? "Learn tmux-ide")} onPress={open()}/>)}
|
|
105
107
|
</Show>
|
|
106
108
|
<Show when={props.onCycleTheme}>
|
|
107
|
-
{(onCycleTheme) => (<TuiButton theme={props.theme} label={themeLabel()} width={buttonWidth(themeLabel())} variant="ghost" background={props.theme.roles.surfaces.canvas} onPress={onCycleTheme()}/>)}
|
|
109
|
+
{(onCycleTheme) => (<TuiButton theme={props.theme} label={themeLabel()} width={buttonWidth(themeLabel())} size="compact" variant="ghost" background={props.theme.roles.surfaces.canvas} onPress={onCycleTheme()}/>)}
|
|
108
110
|
</Show>
|
|
109
111
|
</box>
|
|
110
112
|
</Show>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { shellChromeLayout } from "../shell-chrome.js";
|
|
2
2
|
/** The one physical terminal viewport after production shell chrome. */
|
|
3
|
-
export function applicationShellViewport(dimensions, hasSemanticShell) {
|
|
3
|
+
export function applicationShellViewport(dimensions, hasSemanticShell, sidebarVisible = true) {
|
|
4
4
|
if (!hasSemanticShell)
|
|
5
5
|
return {
|
|
6
6
|
width: Math.max(1, dimensions.width),
|
|
7
7
|
height: Math.max(2, dimensions.height - 2),
|
|
8
8
|
};
|
|
9
|
-
const shell = shellChromeLayout(dimensions.width, dimensions.height, 28);
|
|
9
|
+
const shell = shellChromeLayout(dimensions.width, dimensions.height, sidebarVisible ? 28 : 0);
|
|
10
10
|
return {
|
|
11
11
|
width: Math.max(1, shell.main.width),
|
|
12
12
|
height: Math.max(2, shell.main.height - shell.status.height - 1),
|
|
@@ -3,7 +3,7 @@ import { applicationShellViewport } from "./application-shell-viewport.js";
|
|
|
3
3
|
* Owns the one semantic-shell viewport resize for the current generation.
|
|
4
4
|
* Provisional local chrome is intentionally not terminal geometry authority.
|
|
5
5
|
*/
|
|
6
|
-
export function createSemanticShellViewportResizeOwner(getLayout = () => ({ current: { paneBorderStatus: "top" } })) {
|
|
6
|
+
export function createSemanticShellViewportResizeOwner(getLayout = () => ({ current: { paneBorderStatus: "top" } }), sidebarVisible = () => true) {
|
|
7
7
|
let disposed = false;
|
|
8
8
|
let applied = null;
|
|
9
9
|
let pending = null;
|
|
@@ -118,7 +118,7 @@ export function createSemanticShellViewportResizeOwner(getLayout = () => ({ curr
|
|
|
118
118
|
retry?.();
|
|
119
119
|
}) ?? null;
|
|
120
120
|
}
|
|
121
|
-
const viewport = applicationShellViewport(dimensions, true);
|
|
121
|
+
const viewport = applicationShellViewport(dimensions, true, sidebarVisible());
|
|
122
122
|
const lane = generation.fastLane;
|
|
123
123
|
const target = Object.freeze({
|
|
124
124
|
lane,
|