@zq-silk/yui 2.16.0 → 2.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commandCatalog.js +19 -12
- package/dist/cli/interactionCandidates.js +20 -5
- package/dist/cli/interactionPolicy.js +5 -4
- package/dist/cli/roleWizard.js +7 -0
- package/dist/commands/controllerCommands.js +13 -4
- package/dist/commands/durableJobCommands.js +50 -3
- package/dist/commands/globalRoleCommands.js +31 -14
- package/dist/commands/inboxCommands.js +1 -0
- package/dist/commands/operatorCommands.js +12 -2
- package/dist/commands/profileCommands.js +19 -2
- package/dist/commands/roleConfiguration.js +3 -0
- package/dist/commands/taskCommands.js +16 -0
- package/dist/commands/taskRoleRuntimeStatus.js +3 -0
- package/dist/controlPlaneCli.js +21 -4
- package/dist/controller/agentJobControl.js +9 -1
- package/dist/controller/clientRuntime.js +34 -13
- package/dist/controller/controller.js +35 -22
- package/dist/controller/diagnosticJournal.js +214 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +174 -105
- package/dist/controller/globalInputDelivery.js +19 -9
- package/dist/controller/globalRuntimeAttention.js +11 -3
- package/dist/controller/jobControl.js +1 -1
- package/dist/controller/jobDefinitionControl.js +24 -13
- package/dist/controller/jobSupervisor.js +4 -0
- package/dist/controller/providerRetryAdmission.js +9 -0
- package/dist/controller/providerRetryDelivery.js +16 -0
- package/dist/controller/runtime.js +80 -4
- package/dist/controller/runtimeLaunchCoordinator.js +3 -1
- package/dist/controller/workerSchedulerStore.js +24 -0
- package/dist/core/boundedRpc.js +4 -0
- package/dist/core/controllerServer.js +1 -1
- package/dist/core/controllerTelemetry.js +92 -19
- package/dist/executor/fileRoleLaunchPlanner.js +3 -0
- package/dist/job/globalShell.js +51 -0
- package/dist/job/jobBusinessResult.js +4 -3
- package/dist/job/jobDefinition.js +6 -1
- package/dist/job/jobRunner.js +9 -0
- package/dist/job/scheduleService.js +2 -0
- package/dist/message/globalInboxRecovery.js +30 -19
- package/dist/message/globalProviderRetry.js +7 -1
- package/dist/message/messageContinuation.js +4 -0
- package/dist/output/rolePresentation.js +3 -0
- package/dist/profile/agentProfile.js +8 -0
- package/dist/profile/agentProfileRuntime.js +16 -1
- package/dist/review/reviewDecision.js +6 -0
- package/dist/role/availability.js +28 -0
- package/dist/role/role.js +6 -0
- package/dist/runtime/builtinAgentErrorMappers.js +4 -0
- package/dist/runtime/codexNativeAccess.js +21 -4
- package/dist/runtime/globalSessionHealth.js +39 -0
- package/dist/runtime/tmuxAdapters.js +5 -0
- package/dist/scheduler/activeRoleRunDelivery.js +9 -0
- package/dist/scheduler/leaderWakeupProcessor.js +8 -1
- package/dist/scheduler/roleRunLiveness.js +3 -0
- package/dist/scheduler/roleRunStall.js +10 -10
- package/dist/storage/persistenceWorker.js +41 -19
- package/dist/storage/sqliteSchema.js +43 -9
- package/dist/storage/sqliteStore.js +86 -8
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +25 -11
- package/dist/storage/taskReadRevisionSchema.js +39 -0
- package/dist/web/assets/client/app/catalog.js +0 -29
- package/dist/web/assets/client/app/jobs.js +115 -0
- package/dist/web/assets/client/app/main.js +32 -28
- package/dist/web/assets/client/app/manage.js +1 -0
- package/dist/web/assets/client/app/settings.js +1 -5
- package/dist/web/assets/client/app/taskView.js +7 -11
- package/dist/web/assets/client/i18n/en.js +265 -36
- package/dist/web/assets/client/i18n/zh-CN.js +264 -35
- package/dist/web/assets/client/index.js +4 -0
- package/dist/web/assets/client/layout/manageShell.js +61 -15
- package/dist/web/assets/client/layout/router.js +2 -1
- package/dist/web/assets/client/layout/shortcuts.js +2 -2
- package/dist/web/assets/client/layout/workspace.js +99 -112
- package/dist/web/assets/client/lib/prefs.js +1 -1
- package/dist/web/assets/client/ui/primitives.js +15 -2
- package/dist/web/assets/client/views/dock/conversation.js +421 -123
- package/dist/web/assets/client/views/dock/session.js +229 -0
- package/dist/web/assets/client/views/dock/terminal.js +48 -39
- package/dist/web/assets/client/views/jobs.js +1042 -160
- package/dist/web/assets/client/views/overview.js +2 -2
- package/dist/web/assets/client/views/sidebar.js +3 -0
- package/dist/web/assets/client/views/task/page.js +2 -12
- package/dist/web/assets/shared/manageNav.js +3 -1
- package/dist/web/assets/shell/appRail.js +2 -1
- package/dist/web/assets/shell/dock.js +9 -27
- package/dist/web/assets/shell/manage.js +7 -2
- package/dist/web/assets/shell/models.js +4 -3
- package/dist/web/assets/shell/settingsContent.js +0 -3
- package/dist/web/assets/shell/sidebar.js +0 -1
- package/dist/web/assets/styles/views/conversation.js +96 -17
- package/dist/web/assets/styles/views/dock.js +17 -5
- package/dist/web/assets/styles/views/jobs.js +301 -12
- package/dist/web/assets/styles/views/sidebar.js +1 -2
- package/dist/web/assets/styles/views/task.js +1 -2
- package/dist/web/assets/styles/views/terminal.js +12 -8
- package/dist/web/webConversation.js +1 -0
- package/dist/web/webServer.js +28 -1
- package/dist/web/webSettings.js +10 -2
- package/docs/global-shell-jobs.md +78 -0
- package/docs/job-runs.md +6 -3
- package/docs/managed-turn-and-session-runtime.md +84 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +57 -0
- package/docs/release-workflow.md +67 -0
- package/docs/release-workflow.zh-CN.md +48 -0
- package/docs/roles-and-configuration.md +45 -1
- package/docs/roles-and-configuration.zh-CN.md +35 -0
- package/docs/storage-baseline.md +19 -1
- package/docs/storage-baseline.zh-CN.md +10 -1
- package/docs/testing/review-evaluation.md +35 -1
- package/docs/testing/review-evaluation.zh-CN.md +25 -1
- package/docs/testing/verification-levels.md +2 -2
- package/docs/testing/verification-levels.zh-CN.md +2 -2
- package/package.json +1 -1
- package/skills/yui-leader/VERSION +1 -1
- package/skills/yui-leader/references/execution.md +19 -8
- package/skills/yui-reviewer/SKILL.md +13 -2
- package/skills/yui-reviewer/VERSION +1 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { BREAKPOINTS, DOCK } from "../../shared/geometry.js";
|
|
2
2
|
export const WORKSPACE_SCRIPT = String.raw `
|
|
3
|
-
// The sidebar | center | dock workspace. The dock holds the
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// The sidebar | center | dock workspace. The dock holds the session page
|
|
4
|
+
// (one Role and Session, as chat or terminal), the Task discussion or the
|
|
5
|
+
// preview; on desktop it is a resizable column (at the end or in the center),
|
|
6
|
+
// below the narrow breakpoint a full-screen sheet opened explicitly per Task.
|
|
7
|
+
// Everything here is view state and browser preference.
|
|
7
8
|
import { readPreference, writePreference, readSessionAccessMode } from "/assets/js/lib/prefs.js";
|
|
8
9
|
import { markSelected, h, icon, clear } from "/assets/js/lib/dom.js";
|
|
9
10
|
import { createSizing } from "/assets/js/layout/sizing.js";
|
|
@@ -12,19 +13,21 @@ import { layoutTransition, leave } from "/assets/js/lib/motion.js";
|
|
|
12
13
|
const NARROW_QUERY = "(max-width: ${BREAKPOINTS.narrow}px)";
|
|
13
14
|
const DOCK_INITIAL = ${DOCK.initial};
|
|
14
15
|
|
|
15
|
-
// deps: { el, state, t,
|
|
16
|
+
// deps: { el, state, t, session, preview }
|
|
16
17
|
// el the shell elements (dock, dividers, sidebar, the dock's target
|
|
17
|
-
// menu, and the page's [data-
|
|
18
|
+
// menu, and the page's [data-panel-toggle] / legacy [data-operator-toggle]
|
|
18
19
|
// buttons inside el.detail)
|
|
19
20
|
// state read-only here: state.selected
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
21
|
+
// session the dock's session page (views/dock/session): owner, selected
|
|
22
|
+
// Session and its chat / terminal mode
|
|
23
|
+
// The dock state (open, view, side, width, sheet) and the Role list offered
|
|
24
|
+
// by the target menu are private to this module.
|
|
24
25
|
export function createWorkspace(deps) {
|
|
25
26
|
const ws = {
|
|
26
27
|
deps: deps,
|
|
27
28
|
dock: readDockState(),
|
|
29
|
+
// Roles the target menu offers besides Operator (setTargets, app/taskView).
|
|
30
|
+
targets: [],
|
|
28
31
|
// Task list width: null keeps the CSS default (--sidebar-w); otherwise
|
|
29
32
|
// the width the user dragged to.
|
|
30
33
|
sidebar: { width: Number(readPreference("yui.sidebar.width", "")) || null },
|
|
@@ -38,30 +41,24 @@ export function createWorkspace(deps) {
|
|
|
38
41
|
dockVisible: function () { return dockVisible(ws); },
|
|
39
42
|
updateLayout: function () { updateLayout(ws); },
|
|
40
43
|
syncDockButtons: function () { syncDockButtons(ws); },
|
|
44
|
+
setTargets: function (targets) { ws.targets = targets; drawTarget(ws); },
|
|
41
45
|
setDockOpen: function (open) { glide(function () { setDockOpen(ws, open); }); },
|
|
42
46
|
toggleDock: function (mode) { glide(function () { toggleDock(ws, mode); }); },
|
|
43
|
-
|
|
47
|
+
// Shortcuts into the session page: a Role's live Session opens in the
|
|
48
|
+
// terminal mode, a continuation of the Leader in the chat mode.
|
|
49
|
+
openSession: function (target) { openSessionPage(ws, target, { mode: "terminal", current: true }); },
|
|
44
50
|
openConversation: function (target, materials) {
|
|
45
|
-
ws
|
|
46
|
-
showDock(ws, true);
|
|
47
|
-
ws.dock.mode = "conversation";
|
|
48
|
-
updateLayout(ws);
|
|
49
|
-
ws.deps.conversation.open(target, { current: true, materials: materials });
|
|
50
|
-
syncDockButtons(ws);
|
|
51
|
+
openSessionPage(ws, target, { mode: "chat", current: true, ...(materials ? { materials: materials } : {}) });
|
|
51
52
|
},
|
|
52
53
|
openOperator: function () { glide(function () { openOperator(ws); }); },
|
|
53
54
|
toggleOperator: function () { glide(function () { toggleOperator(ws); }); },
|
|
54
55
|
// A newly selected Task starts with the narrow-screen sheet closed.
|
|
55
56
|
enterTask: function () {
|
|
56
|
-
ws.deps.terminal.close();
|
|
57
|
-
ws.terminalTarget = { scope: "task", taskId: ws.deps.state.selected, roleName: "leader" };
|
|
58
57
|
ws.dock.sheet = false;
|
|
59
|
-
ws.dock.mode =
|
|
60
|
-
ws.deps.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
ws.deps.terminal.open({ scope: "task", taskId: ws.deps.state.selected, roleName: "leader" });
|
|
64
|
-
}
|
|
58
|
+
ws.dock.mode = "conversation";
|
|
59
|
+
ws.deps.session.open({ scope: "task", taskId: ws.deps.state.selected, roleName: "leader" },
|
|
60
|
+
{ current: true, mode: preferredMode() });
|
|
61
|
+
pauseHiddenSession(ws);
|
|
65
62
|
syncDockButtons(ws);
|
|
66
63
|
},
|
|
67
64
|
leaveTask: function () { leaveTask(ws); }
|
|
@@ -79,6 +76,8 @@ function readDockState() {
|
|
|
79
76
|
return {
|
|
80
77
|
// The panel starts closed on every load and opens only on request.
|
|
81
78
|
open: false,
|
|
79
|
+
// Which dock view: the session page ("conversation"), the Task
|
|
80
|
+
// discussion or the preview.
|
|
82
81
|
mode: "conversation",
|
|
83
82
|
center: readPreference("yui.dock.side", "end") === "center",
|
|
84
83
|
width: Number(readPreference("yui.dock.width", String(DOCK_INITIAL))) || DOCK_INITIAL,
|
|
@@ -88,22 +87,22 @@ function readDockState() {
|
|
|
88
87
|
};
|
|
89
88
|
}
|
|
90
89
|
|
|
90
|
+
// The Settings preference picks the session page's starting mode.
|
|
91
91
|
function preferredMode() {
|
|
92
|
-
return readSessionAccessMode() === "native" ? "
|
|
92
|
+
return readSessionAccessMode() === "native" ? "terminal" : "chat";
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
function dockVisible(ws) {
|
|
96
96
|
const dock = ws.dock;
|
|
97
97
|
if (ws.narrow.matches ? !dock.sheet : !dock.open) return false;
|
|
98
|
-
|
|
99
|
-
if (dock.mode === "conversation") return true;
|
|
100
|
-
return dock.mode === "session" && !!ws.deps.terminal.current();
|
|
98
|
+
return !!ws.deps.state.selected || dock.mode === "conversation";
|
|
101
99
|
}
|
|
102
100
|
|
|
103
101
|
function updateLayout(ws) {
|
|
104
102
|
const el = ws.deps.el;
|
|
105
103
|
const dock = ws.dock;
|
|
106
104
|
const selected = !!ws.deps.state.selected;
|
|
105
|
+
if (!selected && dock.mode !== "conversation") dock.mode = "conversation";
|
|
107
106
|
const visible = dockVisible(ws);
|
|
108
107
|
document.body.classList.toggle("task-open", selected);
|
|
109
108
|
document.body.classList.toggle("dock-open", visible);
|
|
@@ -111,7 +110,6 @@ function updateLayout(ws) {
|
|
|
111
110
|
revealDock(ws, visible);
|
|
112
111
|
el.divider.hidden = !visible;
|
|
113
112
|
el.discussion.hidden = dock.mode !== "discussion";
|
|
114
|
-
el.session.hidden = dock.mode !== "session";
|
|
115
113
|
el.conversation.hidden = dock.mode !== "conversation";
|
|
116
114
|
el.preview.hidden = dock.mode !== "preview";
|
|
117
115
|
el.dockTabPreview.setAttribute("aria-selected", String(dock.mode === "preview"));
|
|
@@ -120,7 +118,6 @@ function updateLayout(ws) {
|
|
|
120
118
|
else ws.deps.preview.close();
|
|
121
119
|
el.dockTabConversation.setAttribute("aria-selected", String(dock.mode === "conversation"));
|
|
122
120
|
el.dockTabDiscussion.setAttribute("aria-selected", String(dock.mode === "discussion"));
|
|
123
|
-
el.dockTabSession.setAttribute("aria-selected", String(dock.mode === "session"));
|
|
124
121
|
el.dockTabDiscussion.disabled = !selected;
|
|
125
122
|
el.dockSwap.setAttribute("aria-pressed", String(dock.center));
|
|
126
123
|
ws.sizing.applySidebarWidth();
|
|
@@ -149,21 +146,21 @@ function revealDock(ws, visible) {
|
|
|
149
146
|
function syncDockButtons(ws) {
|
|
150
147
|
const el = ws.deps.el;
|
|
151
148
|
const visible = dockVisible(ws);
|
|
152
|
-
|
|
153
|
-
|
|
149
|
+
el.detail.querySelectorAll("[data-panel-toggle]").forEach(function (button) {
|
|
150
|
+
button.setAttribute("aria-expanded", String(visible));
|
|
151
|
+
button.setAttribute("aria-label", ws.deps.t(visible ? "dock.hide" : "dock.show"));
|
|
154
152
|
});
|
|
155
153
|
markSelected(el.detail.querySelectorAll("[data-operator-toggle]"), "aria-pressed", function () { return operatorShown(ws); });
|
|
156
154
|
drawTarget(ws);
|
|
157
155
|
}
|
|
158
156
|
|
|
159
157
|
// --- Conversation target -----------------------------------------------------
|
|
160
|
-
// The dock header names who the
|
|
161
|
-
//
|
|
158
|
+
// The dock header names who the session page talks to; it is the only Role
|
|
159
|
+
// selector for both chat and terminal. Operator is always offered; a selected
|
|
160
|
+
// Task adds its Roles. Other global Roles use the global message dialog.
|
|
162
161
|
function currentTarget(ws) {
|
|
163
|
-
const mode = ws.dock.mode;
|
|
164
162
|
const selected = ws.deps.state.selected;
|
|
165
|
-
if (mode === "
|
|
166
|
-
if (mode === "conversation") return ws.deps.conversation.current();
|
|
163
|
+
if (ws.dock.mode === "conversation") return ws.deps.session.current();
|
|
167
164
|
return selected ? { scope: "task", taskId: selected, roleName: "leader" } : null;
|
|
168
165
|
}
|
|
169
166
|
|
|
@@ -177,45 +174,57 @@ function sameTarget(a, b) {
|
|
|
177
174
|
return !!a && !!b && a.scope === b.scope && a.roleName === b.roleName && (a.scope !== "task" || a.taskId === b.taskId);
|
|
178
175
|
}
|
|
179
176
|
|
|
177
|
+
function targetOptions(ws, current) {
|
|
178
|
+
const selected = ws.deps.state.selected;
|
|
179
|
+
const options = [{ scope: "global", roleName: "operator" }];
|
|
180
|
+
if (selected) {
|
|
181
|
+
const roles = ws.targets.filter(function (target) { return target.scope === "task" && target.taskId === selected; });
|
|
182
|
+
if (!roles.some(function (target) { return target.roleName === "leader"; })) options.push({ scope: "task", taskId: selected, roleName: "leader" });
|
|
183
|
+
roles.forEach(function (target) { options.push(target); });
|
|
184
|
+
}
|
|
185
|
+
if (current && !options.some(function (option) { return sameTarget(option, current); })) options.push(current);
|
|
186
|
+
return options;
|
|
187
|
+
}
|
|
188
|
+
|
|
180
189
|
function drawTarget(ws) {
|
|
181
190
|
const el = ws.deps.el;
|
|
182
191
|
const current = currentTarget(ws);
|
|
183
192
|
el.dockTargetLabel.textContent = targetLabel(ws, current);
|
|
184
193
|
el.dockTargetIcon.replaceChildren(icon(current && current.scope === "task" ? "target" : "broadcast"));
|
|
185
|
-
const options =
|
|
186
|
-
if (ws.deps.state.selected) options.push({ scope: "task", taskId: ws.deps.state.selected, roleName: "leader" });
|
|
187
|
-
if (current && !options.some(function (option) { return sameTarget(option, current); })) options.push(current);
|
|
194
|
+
const options = targetOptions(ws, current);
|
|
188
195
|
const signature = JSON.stringify([options, current, ws.deps.t("actions.operator")]);
|
|
189
196
|
if (el.dockTargetOptions.dataset.signature === signature) return;
|
|
190
197
|
el.dockTargetOptions.dataset.signature = signature;
|
|
191
198
|
clear(el.dockTargetOptions);
|
|
192
199
|
options.forEach(function (target) {
|
|
200
|
+
const chosen = sameTarget(target, current);
|
|
193
201
|
el.dockTargetOptions.append(h("button.dock-target-item", {
|
|
194
|
-
type: "button", "aria-pressed": String(
|
|
202
|
+
type: "button", "aria-pressed": String(chosen),
|
|
195
203
|
onclick: function () { chooseTarget(ws, target); }
|
|
196
|
-
},
|
|
197
|
-
|
|
204
|
+
}, target.scope === "task" ? h("i.dock-target-dot" + (target.live ? ".is-live" : ""), { "aria-hidden": "true" }) : icon("broadcast"),
|
|
205
|
+
h("span", null, targetLabel(ws, target)),
|
|
206
|
+
chosen ? icon("check", "icon-sm dock-target-check") : null));
|
|
198
207
|
});
|
|
199
208
|
}
|
|
200
209
|
|
|
201
|
-
// Keep the
|
|
202
|
-
//
|
|
210
|
+
// Keep the session page's mode (chat or terminal) and switch only its owner;
|
|
211
|
+
// from the discussion or preview, the session page opens again.
|
|
203
212
|
function chooseTarget(ws, target) {
|
|
204
213
|
ws.deps.el.dockTarget.open = false;
|
|
205
|
-
|
|
206
|
-
|
|
214
|
+
openSessionPage(ws, { scope: target.scope, roleName: target.roleName, ...(target.scope === "task" ? { taskId: target.taskId } : {}) }, {});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function openSessionPage(ws, target, options) {
|
|
207
218
|
showDock(ws, true);
|
|
208
219
|
ws.dock.mode = "conversation";
|
|
209
220
|
updateLayout(ws);
|
|
210
|
-
ws.deps.
|
|
221
|
+
ws.deps.session.open(target, options);
|
|
211
222
|
syncDockButtons(ws);
|
|
212
223
|
}
|
|
213
224
|
|
|
214
225
|
// The dock currently shows the global operator Session.
|
|
215
226
|
function operatorShown(ws) {
|
|
216
|
-
|
|
217
|
-
const current = ws.deps.terminal.current();
|
|
218
|
-
return dockVisible(ws) && ws.dock.mode === "session" && !!current && current.scope === "global";
|
|
227
|
+
return dockVisible(ws) && ws.dock.mode === "conversation" && ws.deps.session.current()?.scope === "global";
|
|
219
228
|
}
|
|
220
229
|
|
|
221
230
|
function showDock(ws, open) {
|
|
@@ -226,70 +235,54 @@ function showDock(ws, open) {
|
|
|
226
235
|
}
|
|
227
236
|
|
|
228
237
|
function setDockOpen(ws, open) {
|
|
238
|
+
// Closing with focus inside the panel returns it to the page's panel switch
|
|
239
|
+
// rather than leaving it on a hidden element.
|
|
240
|
+
const active = document.activeElement;
|
|
241
|
+
const focusInside = !open && !!active && ws.deps.el.dock.contains(active);
|
|
229
242
|
showDock(ws, open);
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (open && ws.dock.mode === "session") {
|
|
237
|
-
// Keep the selected owner, not a Session identity cached before hiding.
|
|
238
|
-
const target = ws.terminalTarget || { scope: "global", roleName: "operator" };
|
|
239
|
-
ws.deps.terminal.open({ scope: target.scope, roleName: target.roleName,
|
|
240
|
-
...(target.scope === "task" ? { taskId: target.taskId } : {}) });
|
|
243
|
+
// Hiding stops polling and detaches the terminal; showing resumes the same
|
|
244
|
+
// owner and selected Session in the same mode.
|
|
245
|
+
if (!open) ws.deps.session.close();
|
|
246
|
+
else if (ws.dock.mode === "conversation") {
|
|
247
|
+
if (ws.deps.session.current()) ws.deps.session.reconnect();
|
|
248
|
+
else ws.deps.session.open({ scope: "global", roleName: "operator" }, { mode: preferredMode() });
|
|
241
249
|
}
|
|
242
250
|
updateLayout(ws);
|
|
251
|
+
if (focusInside) {
|
|
252
|
+
const toggle = ws.deps.el.detail.querySelector("[data-panel-toggle]");
|
|
253
|
+
if (toggle) toggle.focus();
|
|
254
|
+
}
|
|
243
255
|
}
|
|
244
256
|
|
|
245
257
|
function setDockMode(ws, mode) {
|
|
246
|
-
if (mode ===
|
|
247
|
-
ws.deps.terminal.close();
|
|
248
|
-
ws.deps.conversation.close();
|
|
249
|
-
ws.dock.mode = "preview";
|
|
250
|
-
updateLayout(ws);
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
258
|
+
if (mode === ws.dock.mode) return;
|
|
253
259
|
if (mode === "conversation") {
|
|
254
|
-
const target = ws.deps.terminal.current() || ws.deps.conversation.current();
|
|
255
|
-
ws.deps.terminal.close();
|
|
256
260
|
ws.dock.mode = mode;
|
|
257
261
|
updateLayout(ws);
|
|
258
|
-
ws.deps.
|
|
262
|
+
if (ws.deps.session.current()) ws.deps.session.reconnect();
|
|
263
|
+
else ws.deps.session.open(ws.deps.state.selected
|
|
259
264
|
? { scope: "task", taskId: ws.deps.state.selected, roleName: "leader" }
|
|
260
|
-
: { scope: "global", roleName: "operator" });
|
|
265
|
+
: { scope: "global", roleName: "operator" }, { mode: preferredMode() });
|
|
261
266
|
syncDockButtons(ws);
|
|
262
267
|
return;
|
|
263
268
|
}
|
|
264
|
-
if (
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
openSession(ws, target);
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
ws.deps.terminal.close();
|
|
271
|
-
ws.deps.conversation.close();
|
|
272
|
-
ws.dock.mode = mode === "session" || !ws.deps.state.selected ? "session" : "discussion";
|
|
269
|
+
if (!ws.deps.state.selected) return;
|
|
270
|
+
ws.deps.session.close();
|
|
271
|
+
ws.dock.mode = mode === "preview" ? "preview" : "discussion";
|
|
273
272
|
updateLayout(ws);
|
|
274
273
|
}
|
|
275
274
|
|
|
275
|
+
// mode "session" (the legacy native entry) opens the session page in its
|
|
276
|
+
// terminal mode; "discussion" and "preview" need a selected Task.
|
|
276
277
|
function toggleDock(ws, mode) {
|
|
277
278
|
const dock = ws.dock;
|
|
278
279
|
const selected = ws.deps.state.selected;
|
|
279
|
-
|
|
280
|
-
if (
|
|
281
|
-
if (
|
|
280
|
+
const view = mode === "session" ? "conversation" : mode;
|
|
281
|
+
if (dockVisible(ws) && (!view || dock.mode === view)) { setDockOpen(ws, false); return; }
|
|
282
|
+
if (view) dock.mode = view === "conversation" || selected ? view : "conversation";
|
|
283
|
+
if (!selected && dock.mode !== "conversation") dock.mode = "conversation";
|
|
282
284
|
setDockOpen(ws, true);
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
function openSession(ws, target) {
|
|
286
|
-
ws.terminalTarget = target;
|
|
287
|
-
ws.deps.conversation.close();
|
|
288
|
-
ws.dock.mode = "session";
|
|
289
|
-
showDock(ws, true);
|
|
290
|
-
ws.deps.terminal.open(target);
|
|
291
|
-
updateLayout(ws);
|
|
292
|
-
ws.deps.sessionOpened();
|
|
285
|
+
if (mode === "session") ws.deps.session.setMode("terminal");
|
|
293
286
|
}
|
|
294
287
|
|
|
295
288
|
function toggleOperator(ws) {
|
|
@@ -298,28 +291,23 @@ function toggleOperator(ws) {
|
|
|
298
291
|
}
|
|
299
292
|
|
|
300
293
|
function openOperator(ws) {
|
|
301
|
-
ws.deps.
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
ws.deps.conversation.open({ scope: "global", roleName: "operator" });
|
|
305
|
-
if (preferredMode() === "session") openSession(ws, { scope: "global", roleName: "operator" });
|
|
306
|
-
syncDockButtons(ws);
|
|
294
|
+
const current = ws.deps.session.current();
|
|
295
|
+
openSessionPage(ws, { scope: "global", roleName: "operator" },
|
|
296
|
+
current && current.scope === "global" && current.roleName === "operator" ? {} : { mode: preferredMode() });
|
|
307
297
|
}
|
|
308
298
|
|
|
309
|
-
// Leaving a Task:
|
|
310
|
-
// longer connected, the dock returns to the discussion mode.
|
|
299
|
+
// Leaving a Task: the session page returns to Operator, hidden unless shown.
|
|
311
300
|
function leaveTask(ws) {
|
|
312
|
-
ws.deps.terminal.close();
|
|
313
301
|
ws.dock.mode = "conversation";
|
|
314
|
-
ws.deps.
|
|
315
|
-
|
|
302
|
+
ws.deps.session.open({ scope: "global", roleName: "operator" });
|
|
303
|
+
pauseHiddenSession(ws);
|
|
316
304
|
syncDockButtons(ws);
|
|
317
305
|
}
|
|
318
306
|
|
|
319
|
-
// The
|
|
320
|
-
// polling; opening the panel reconnects it.
|
|
321
|
-
function
|
|
322
|
-
if (!dockVisible(ws) || ws.dock.mode !== "conversation") ws.deps.
|
|
307
|
+
// The session page keeps its owner while the panel is hidden but stops
|
|
308
|
+
// polling and detaches; opening the panel reconnects it.
|
|
309
|
+
function pauseHiddenSession(ws) {
|
|
310
|
+
if (!dockVisible(ws) || ws.dock.mode !== "conversation") ws.deps.session.close();
|
|
323
311
|
}
|
|
324
312
|
|
|
325
313
|
function bindDockControls(ws) {
|
|
@@ -327,7 +315,6 @@ function bindDockControls(ws) {
|
|
|
327
315
|
const dock = ws.dock;
|
|
328
316
|
el.dockTabDiscussion.addEventListener("click", function () { setDockMode(ws, "discussion"); });
|
|
329
317
|
el.dockTabConversation.addEventListener("click", function () { setDockMode(ws, "conversation"); });
|
|
330
|
-
el.dockTabSession.addEventListener("click", function () { setDockMode(ws, "session"); });
|
|
331
318
|
el.dockTabPreview.addEventListener("click", function () { setDockMode(ws, "preview"); });
|
|
332
319
|
el.dockClose.addEventListener("click", function () { glide(function () { setDockOpen(ws, false); }); });
|
|
333
320
|
el.dockSwap.addEventListener("click", function () {
|
|
@@ -18,7 +18,7 @@ export function clearPreference(key) {
|
|
|
18
18
|
// switching back restores the filters, Task or section the user left. Only
|
|
19
19
|
// same-origin paths of the expected area are accepted; anything else falls back.
|
|
20
20
|
const PLACES = { workbench: /^\/(?:\?|$)/, models: /^\/models(?:[/?]|$)/, settings: /^\/settings(?:[/?]|$)/,
|
|
21
|
-
manage: /^\/(?:models|settings)(?:[/?]|$)/ };
|
|
21
|
+
jobs: /^\/jobs(?:[/?]|$)/, manage: /^\/(?:jobs|models|settings)(?:[/?]|$)/ };
|
|
22
22
|
|
|
23
23
|
export function rememberPlace(area, href) {
|
|
24
24
|
if (!PLACES[area] || !PLACES[area].test(href)) return;
|
|
@@ -42,8 +42,21 @@ export function button(text, options) {
|
|
|
42
42
|
return element;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// The page header's
|
|
46
|
-
// every Task page
|
|
45
|
+
// The page header's session-panel switch: the same place on the overview and
|
|
46
|
+
// on every Task page. It shows or hides the dock with its last target and view
|
|
47
|
+
// (Operator, discussion, terminal and preview are chosen inside the panel);
|
|
48
|
+
// layout/workspace keeps aria-expanded and the accessible name current.
|
|
49
|
+
export function panelToggle(t, onClick) {
|
|
50
|
+
const element = h("button.btn.btn-ghost.dock-toggle.panel-toggle", {
|
|
51
|
+
type: "button", "aria-label": t("dock.show"), title: t("dock.label") + " · D",
|
|
52
|
+
"aria-expanded": "false", "aria-controls": "dock", onclick: onClick
|
|
53
|
+
}, icon("dock"), h("span", null, t("dock.label")));
|
|
54
|
+
element.dataset.panelToggle = "true";
|
|
55
|
+
return element;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// The former Operator-only header switch, kept unchanged for pages that still
|
|
59
|
+
// import it; layout/workspace marks it pressed while the dock shows Operator.
|
|
47
60
|
export function operatorToggle(t, onClick) {
|
|
48
61
|
const element = h("button.btn.btn-ghost.dock-toggle.operator-toggle", {
|
|
49
62
|
type: "button", title: t("actions.operator") + " · O", "aria-pressed": "false", onclick: onClick
|