@zq-silk/yui 2.15.0 → 2.16.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/web/assets/client/app/catalog.js +40 -14
- package/dist/web/assets/client/app/main.js +33 -9
- package/dist/web/assets/client/app/manage.js +18 -0
- package/dist/web/assets/client/app/selection.js +4 -1
- package/dist/web/assets/client/app/taskView.js +22 -5
- package/dist/web/assets/client/i18n/en.js +10 -3
- package/dist/web/assets/client/i18n/zh-CN.js +10 -3
- package/dist/web/assets/client/index.js +4 -0
- package/dist/web/assets/client/layout/manageShell.js +30 -8
- package/dist/web/assets/client/layout/shortcuts.js +7 -3
- package/dist/web/assets/client/layout/sidebarNav.js +147 -0
- package/dist/web/assets/client/layout/sizing.js +5 -2
- package/dist/web/assets/client/layout/workspace.js +120 -21
- package/dist/web/assets/client/lib/motion.js +59 -0
- package/dist/web/assets/client/lib/prefs.js +29 -2
- package/dist/web/assets/client/lib/theme.js +11 -4
- package/dist/web/assets/client/ui/primitives.js +27 -0
- package/dist/web/assets/client/views/overview.js +8 -5
- package/dist/web/assets/client/views/task/page.js +10 -10
- package/dist/web/assets/shared/brand.js +22 -0
- package/dist/web/assets/shared/geometry.js +7 -1
- package/dist/web/assets/shared/icons.js +4 -0
- package/dist/web/assets/shared/motion.js +74 -0
- package/dist/web/assets/shell/appRail.js +14 -0
- package/dist/web/assets/shell/dialogs.js +1 -0
- package/dist/web/assets/shell/dock.js +19 -6
- package/dist/web/assets/shell/index.js +6 -2
- package/dist/web/assets/shell/login.js +9 -8
- package/dist/web/assets/shell/manage.js +32 -20
- package/dist/web/assets/shell/settingsContent.js +1 -1
- package/dist/web/assets/shell/sidebar.js +16 -23
- package/dist/web/assets/styles/base.js +1 -1
- package/dist/web/assets/styles/index.js +2 -0
- package/dist/web/assets/styles/layout/responsive.js +63 -19
- package/dist/web/assets/styles/layout/workspace.js +53 -16
- package/dist/web/assets/styles/motion.js +51 -0
- package/dist/web/assets/styles/tokens.js +5 -3
- package/dist/web/assets/styles/ui/controls.js +1 -1
- package/dist/web/assets/styles/ui/forms.js +1 -1
- package/dist/web/assets/styles/ui/surfaces.js +1 -2
- package/dist/web/assets/styles/views/conversation.js +1 -1
- package/dist/web/assets/styles/views/dock.js +22 -2
- package/dist/web/assets/styles/views/login.js +16 -13
- package/dist/web/assets/styles/views/manage.js +25 -42
- package/dist/web/assets/styles/views/page.js +2 -3
- package/dist/web/assets/styles/views/records.js +1 -1
- package/dist/web/assets/styles/views/settings.js +1 -1
- package/dist/web/assets/styles/views/sidebar.js +25 -20
- package/dist/web/assets/styles/views/task.js +9 -15
- package/dist/web/assets/styles/views/terminal.js +1 -1
- package/docs/release-workflow.md +22 -0
- package/docs/release-workflow.zh-CN.md +15 -0
- package/package.json +1 -1
|
@@ -51,8 +51,21 @@ async function refresh(deps, progress, options) {
|
|
|
51
51
|
const query = catalogQuery(state);
|
|
52
52
|
const dashboard = await deps.api.dashboard(query);
|
|
53
53
|
if (request !== progress.request) return;
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// An unchanged quiet poll leaves the list and overview alone: rebuilding
|
|
55
|
+
// them every few seconds would interrupt scrolling and animations. They
|
|
56
|
+
// still redraw once a minute so relative times stay true.
|
|
57
|
+
const signature = query + "|" + JSON.stringify(dashboard);
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
if (quiet && signature === progress.signature && now - progress.drawnAt < 60000) {
|
|
60
|
+
state.generatedAt = new Date().toISOString();
|
|
61
|
+
el.sync.dataset.state = "ok";
|
|
62
|
+
el.lastSync.textContent = formatClock(state.generatedAt, deps.locale());
|
|
63
|
+
} else {
|
|
64
|
+
progress.signature = signature;
|
|
65
|
+
progress.drawnAt = now;
|
|
66
|
+
applyDashboard(state, dashboard, query);
|
|
67
|
+
showDashboard(deps, progress, previousInputs);
|
|
68
|
+
}
|
|
56
69
|
// A selected Task may live on another page or outside current filters;
|
|
57
70
|
// only its own read can establish that it no longer exists.
|
|
58
71
|
if (state.selected) {
|
|
@@ -98,18 +111,10 @@ function renderSidebar(deps, progress) {
|
|
|
98
111
|
reset(deps, progress);
|
|
99
112
|
});
|
|
100
113
|
el.catalogCount.textContent = fill(t("catalog.count"), { shown: state.tasks.length, total: state.catalogTotal });
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
state.source = source.value; state.attentionFilter = null; state.query = ""; el.search.value = "";
|
|
106
|
-
syncUrl(state, false); reset(deps, progress);
|
|
107
|
-
});
|
|
108
|
-
el.filters.append(source);
|
|
109
|
-
}
|
|
110
|
-
source.replaceChildren(...["ordinary", "job", "all"].map(value =>
|
|
111
|
-
h("option", { value }, t("jobs.category." + value))));
|
|
112
|
-
source.value = state.query.trim() || state.attentionFilter ? "all" : state.source || "ordinary";
|
|
114
|
+
renderSource(el.source, state, t, function (value) {
|
|
115
|
+
state.source = value; state.attentionFilter = null; state.query = ""; el.search.value = "";
|
|
116
|
+
syncUrl(state, false); reset(deps, progress);
|
|
117
|
+
});
|
|
113
118
|
el.catalogReset.hidden = !state.catalogCursor;
|
|
114
119
|
el.catalogNext.hidden = !state.nextCursor && !state.catalogCursor;
|
|
115
120
|
el.catalogAttentionReset.hidden = !state.attentionFilter;
|
|
@@ -117,6 +122,27 @@ function renderSidebar(deps, progress) {
|
|
|
117
122
|
el.lastSync.textContent = state.generatedAt ? formatClock(state.generatedAt, deps.locale()) : "—";
|
|
118
123
|
}
|
|
119
124
|
|
|
125
|
+
// Task category: search and attention are cross-category, so they show "all";
|
|
126
|
+
// picking a category clears them, as the server filter then applies again.
|
|
127
|
+
function renderSource(container, state, t, onPick) {
|
|
128
|
+
const current = state.query.trim() || state.attentionFilter ? "all" : state.source || "ordinary";
|
|
129
|
+
// Bound once; the handler reads the live state, not this render's snapshot.
|
|
130
|
+
if (!container.childElementCount) {
|
|
131
|
+
["ordinary", "job", "all"].forEach(function (value) {
|
|
132
|
+
container.append(h("button.seg-btn", { type: "button", role: "radio", dataset: { source: value },
|
|
133
|
+
onclick: function () {
|
|
134
|
+
if (value !== (state.source || "ordinary") || state.query.trim() || state.attentionFilter) onPick(value);
|
|
135
|
+
} }));
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
container.querySelectorAll("[data-source]").forEach(function (button) {
|
|
139
|
+
const value = button.dataset.source;
|
|
140
|
+
button.textContent = t("jobs.categoryShort." + value);
|
|
141
|
+
button.title = t("jobs.category." + value);
|
|
142
|
+
button.setAttribute("aria-checked", String(value === current));
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
120
146
|
// Redraw the list in place, keeping its scroll position.
|
|
121
147
|
function renderTaskList(deps) {
|
|
122
148
|
const scroll = deps.el.tasks.scrollTop;
|
|
@@ -17,7 +17,7 @@ export const APP_SCRIPT = String.raw `
|
|
|
17
17
|
// refresh catalog.refresh → sidebar, then the overview (nothing selected)
|
|
18
18
|
// or selection.reload → taskView.render
|
|
19
19
|
// select sidebar row / overview row / URL → selection.selectTask
|
|
20
|
-
// leave
|
|
20
|
+
// leave rail Workbench or Escape → selection.clearSelection
|
|
21
21
|
// write Task page or discussion afterWrite → quiet catalog refresh
|
|
22
22
|
// URL load and back/forward → catalog.applyFilters + selection.followUrl
|
|
23
23
|
import { createI18n } from "/assets/js/lib/i18n.js";
|
|
@@ -31,7 +31,9 @@ import { bindGlobalInput } from "/assets/js/views/globalInput.js";
|
|
|
31
31
|
import { bindCreateTask } from "/assets/js/views/createTask.js";
|
|
32
32
|
import { bindBodySearch } from "/assets/js/views/bodySearch.js";
|
|
33
33
|
import { readQuery } from "/assets/js/layout/router.js";
|
|
34
|
+
import { rememberPlace, linkPlaces } from "/assets/js/lib/prefs.js";
|
|
34
35
|
import { createWorkspace } from "/assets/js/layout/workspace.js";
|
|
36
|
+
import { createSidebarNav } from "/assets/js/layout/sidebarNav.js";
|
|
35
37
|
import { bindShortcuts } from "/assets/js/layout/shortcuts.js";
|
|
36
38
|
import { createTaskView } from "/assets/js/app/taskView.js";
|
|
37
39
|
import { createSelection } from "/assets/js/app/selection.js";
|
|
@@ -39,12 +41,14 @@ import { createCatalog } from "/assets/js/app/catalog.js";
|
|
|
39
41
|
|
|
40
42
|
const $ = function (selector) { return document.querySelector(selector); };
|
|
41
43
|
const el = {
|
|
42
|
-
sidebar: $(".sidebar"),
|
|
43
|
-
|
|
44
|
+
sidebar: $(".sidebar"), rail: $(".app-rail"), railWorkbench: $("#rail-workbench"),
|
|
45
|
+
sidebarScrim: $("#sidebar-scrim"), sidebarCollapse: $("#sidebar-collapse"), sidebarDivider: $("#sidebar-divider"), center: $("#center"), detail: $("#detail"),
|
|
46
|
+
search: $("#search"), source: $("#catalog-source"), filters: $("#status-filters"), attention: $("#attention-bar"), tasks: $("#task-list"),
|
|
44
47
|
catalogNext: $("#catalog-next"), catalogReset: $("#catalog-reset"), catalogCount: $("#catalog-count"),
|
|
45
48
|
catalogAttentionReset: $("#catalog-attention-reset"),
|
|
46
49
|
refresh: $("#refresh"), sync: $("#sync-state"), lastSync: $("#last-sync"), toast: $("#toast"),
|
|
47
|
-
|
|
50
|
+
dockTarget: $("#dock-target"), dockTargetLabel: $("#dock-target-label"), dockTargetIcon: $("#dock-target-icon"),
|
|
51
|
+
dockTargetOptions: $("#dock-target-options"), globalInputOpen: $("#global-input-open"), settings: $("#settings-dialog"),
|
|
48
52
|
locale: $("#locale-select"), themeOptions: $("#theme-options"),
|
|
49
53
|
dock: $("#dock"), divider: $("#dock-divider"), dockSwap: $("#dock-swap"), dockClose: $("#dock-close"),
|
|
50
54
|
dockTabDiscussion: $("#dock-tab-discussion"), dockTabSession: $("#dock-tab-session"),
|
|
@@ -52,7 +56,7 @@ const el = {
|
|
|
52
56
|
dockTabPreview: $("#dock-tab-preview"), preview: $("#dock-preview"),
|
|
53
57
|
discussion: $("#dock-discussion"), session: $("#dock-session"),
|
|
54
58
|
terminalHost: $("#terminal-host"), terminalEmpty: $("#terminal-empty"), terminalState: $("#terminal-state"),
|
|
55
|
-
sessionTargets: $("#session-targets"), terminalCli: $("#terminal-cli")
|
|
59
|
+
sessionTargets: $("#session-targets"), settingsOpen: $("#settings-open"), terminalCli: $("#terminal-cli")
|
|
56
60
|
};
|
|
57
61
|
|
|
58
62
|
// View state. Each group has exactly one writer.
|
|
@@ -86,13 +90,18 @@ let taskView = null;
|
|
|
86
90
|
let selection = null;
|
|
87
91
|
let catalog = null;
|
|
88
92
|
const workspace = createWorkspace({
|
|
89
|
-
el: el, state: state, terminal: terminal, conversation: conversation, preview: preview,
|
|
93
|
+
el: el, state: state, t: t, terminal: terminal, conversation: conversation, preview: preview,
|
|
90
94
|
sessionOpened: function () { taskView.updateSessionTargets(); }
|
|
91
95
|
});
|
|
96
|
+
const sidebarNav = createSidebarNav({
|
|
97
|
+
el: { sidebar: el.sidebar, rail: el.rail, scrim: el.sidebarScrim, collapse: el.sidebarCollapse },
|
|
98
|
+
relayout: function () { workspace.updateLayout(); }
|
|
99
|
+
});
|
|
92
100
|
taskView = createTaskView({
|
|
93
101
|
el: el, state: state, t: t, locale: locale, api: api, toast: toast, terminal: terminal, workspace: workspace,
|
|
94
102
|
selectTask: function (taskId) { return selection.selectTask(taskId); },
|
|
95
103
|
clearSelection: function () { selection.clearSelection(); },
|
|
104
|
+
revealSidebar: function () { sidebarNav.reveal(); },
|
|
96
105
|
pickAttention: function (kind) { catalog.pickAttention(kind); },
|
|
97
106
|
readPageSessions: function () { return catalog.readPageSessions(); },
|
|
98
107
|
refreshCatalog: function () { return catalog.refresh({ quiet: true }); }
|
|
@@ -114,10 +123,24 @@ bindBodySearch({ t: t, api: api, selectTask: selection.selectTask,
|
|
|
114
123
|
currentTask: function () { return state.selected; },
|
|
115
124
|
captureDraftTarget: conversation.captureDraftTarget, insertReference: conversation.insertReference });
|
|
116
125
|
bindShortcuts({
|
|
117
|
-
el: el, state: state, globalInput: globalInput, workspace: workspace,
|
|
126
|
+
el: el, state: state, globalInput: globalInput, workspace: workspace, sidebarNav: sidebarNav,
|
|
118
127
|
selection: selection, catalog: catalog, taskView: taskView
|
|
119
128
|
});
|
|
120
|
-
|
|
129
|
+
// Workbench <-> Models/Settings: each side remembers where the user was, so a
|
|
130
|
+
// round trip returns to the same filters, Task and management section.
|
|
131
|
+
function rememberWorkbench() { rememberPlace("workbench", window.location.pathname + window.location.search); }
|
|
132
|
+
el.rail.querySelectorAll("a[data-nav-area]").forEach(function (link) { link.addEventListener("click", rememberWorkbench); });
|
|
133
|
+
// The rail's Workbench entry on the workbench itself returns to the overview.
|
|
134
|
+
el.railWorkbench.addEventListener("click", function (event) {
|
|
135
|
+
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
136
|
+
event.preventDefault();
|
|
137
|
+
sidebarNav.closeDrawer();
|
|
138
|
+
selection.clearSelection();
|
|
139
|
+
});
|
|
140
|
+
window.addEventListener("pagehide", rememberWorkbench);
|
|
141
|
+
function linkAll() { linkPlaces(el.rail); linkPlaces(el.sidebar); }
|
|
142
|
+
window.addEventListener("pageshow", linkAll);
|
|
143
|
+
linkAll();
|
|
121
144
|
|
|
122
145
|
// The URL names the catalog filters and the selected Task (with its section).
|
|
123
146
|
function applyStateFromUrl() {
|
|
@@ -140,9 +163,10 @@ i18n.subscribe(function () {
|
|
|
140
163
|
|
|
141
164
|
workspace.updateLayout();
|
|
142
165
|
taskView.showOverview();
|
|
166
|
+
// The panel starts closed: name its owner without polling until it opens.
|
|
143
167
|
conversation.open({ scope: "global", roleName: "operator" });
|
|
168
|
+
conversation.close();
|
|
144
169
|
applyStateFromUrl();
|
|
145
|
-
workspace.restoreDefaultMode();
|
|
146
170
|
catalog.refresh();
|
|
147
171
|
window.setInterval(function () { catalog.refresh({ quiet: true }); }, 5000);
|
|
148
172
|
`;
|
|
@@ -2,5 +2,23 @@ export const MANAGE_SCRIPT = String.raw `
|
|
|
2
2
|
import "/assets/js/app/settings.js";
|
|
3
3
|
import "/assets/js/app/models.js";
|
|
4
4
|
import { startManagement } from "/assets/js/layout/manageShell.js";
|
|
5
|
+
import { createSidebarNav } from "/assets/js/layout/sidebarNav.js";
|
|
6
|
+
import { isEditing } from "/assets/js/lib/dom.js";
|
|
7
|
+
|
|
8
|
+
// The section list behaves like the workbench task list: collapsible beside
|
|
9
|
+
// the content, a drawer (with the global rail) on narrow screens.
|
|
10
|
+
const sidebarNav = createSidebarNav({
|
|
11
|
+
el: { sidebar: document.querySelector(".manage-rail"), rail: document.querySelector(".app-rail"), scrim: document.querySelector("#sidebar-scrim"),
|
|
12
|
+
collapse: document.querySelector("#sidebar-collapse") },
|
|
13
|
+
relayout: function () {}
|
|
14
|
+
});
|
|
15
|
+
document.querySelectorAll("[data-sidebar-toggle]").forEach(function (button) {
|
|
16
|
+
button.addEventListener("click", sidebarNav.reveal);
|
|
17
|
+
});
|
|
18
|
+
document.addEventListener("keydown", function (event) {
|
|
19
|
+
if (document.querySelector("dialog[open]") || event.metaKey || event.ctrlKey || event.altKey) return;
|
|
20
|
+
if (event.key === "Escape" && sidebarNav.drawerOpen()) { sidebarNav.closeDrawer(); return; }
|
|
21
|
+
if (event.key === "[" && !isEditing(document.activeElement)) sidebarNav.toggle();
|
|
22
|
+
});
|
|
5
23
|
startManagement();
|
|
6
24
|
`;
|
|
@@ -74,6 +74,7 @@ function clearSelection(deps) {
|
|
|
74
74
|
syncUrl(deps.state, !urlTaskId());
|
|
75
75
|
deps.workspace.updateLayout();
|
|
76
76
|
deps.taskView.showOverview();
|
|
77
|
+
deps.taskView.revealPage();
|
|
77
78
|
deps.renderTaskList();
|
|
78
79
|
deps.taskView.updateSessionTargets();
|
|
79
80
|
}
|
|
@@ -96,9 +97,11 @@ function followUrl(deps, params) {
|
|
|
96
97
|
return;
|
|
97
98
|
}
|
|
98
99
|
if (state.selected && !canLeave(deps)) { syncUrl(state, true); return; }
|
|
100
|
+
const leaving = !!state.selected;
|
|
99
101
|
dropSelection(deps);
|
|
100
102
|
deps.workspace.updateLayout();
|
|
101
103
|
deps.taskView.showOverview();
|
|
104
|
+
if (leaving) deps.taskView.revealPage();
|
|
102
105
|
}
|
|
103
106
|
|
|
104
107
|
async function load(deps, taskId, navigate) {
|
|
@@ -120,7 +123,7 @@ async function load(deps, taskId, navigate) {
|
|
|
120
123
|
state.detail = detail;
|
|
121
124
|
state.detailKey = fingerprint(core);
|
|
122
125
|
deps.taskView.render(navigate);
|
|
123
|
-
if (navigate) deps.taskView.applySection(readQuery().get("section"));
|
|
126
|
+
if (navigate) { deps.taskView.applySection(readQuery().get("section")); deps.taskView.revealPage(); }
|
|
124
127
|
observe(deps, taskId, detail);
|
|
125
128
|
}
|
|
126
129
|
|
|
@@ -14,6 +14,7 @@ import { updateObservation } from "/assets/js/views/task/observation.js";
|
|
|
14
14
|
import { renderDiscussion, discussionHasUnsent } from "/assets/js/views/dock/discussion.js";
|
|
15
15
|
import { sameTarget } from "/assets/js/views/dock/terminal.js";
|
|
16
16
|
import { setSectionParam } from "/assets/js/layout/router.js";
|
|
17
|
+
import { enter } from "/assets/js/lib/motion.js";
|
|
17
18
|
|
|
18
19
|
// Only the observed identity participates: observation timestamps advance on
|
|
19
20
|
// every poll and would otherwise re-render continuously.
|
|
@@ -29,7 +30,7 @@ function runtimeSignature(detail) {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
// deps: { el, state, t, locale, api, toast, terminal, workspace,
|
|
32
|
-
// selectTask, clearSelection, pickAttention, readPageSessions, refreshCatalog }
|
|
33
|
+
// selectTask, clearSelection, revealSidebar, pickAttention, readPageSessions, refreshCatalog }
|
|
33
34
|
// el #detail, #center and the dock discussion
|
|
34
35
|
// state reads selected, detail, detailKey and the catalog fields the
|
|
35
36
|
// overview shows; writes activeTab
|
|
@@ -37,7 +38,8 @@ function runtimeSignature(detail) {
|
|
|
37
38
|
// terminal the dock's native Session (views/dock/terminal)
|
|
38
39
|
// workspace layout/workspace: dock toggles and opening Sessions
|
|
39
40
|
// selectTask(taskId) an overview row was chosen (app/selection)
|
|
40
|
-
// clearSelection() the Task page's
|
|
41
|
+
// clearSelection() the Task page's "Tasks" crumb (app/selection)
|
|
42
|
+
// revealSidebar() the page header's task-list switch (layout/sidebarNav)
|
|
41
43
|
// pickAttention(kind), readPageSessions() overview actions (app/catalog)
|
|
42
44
|
// refreshCatalog() quiet catalog refresh after a write; resolves when done
|
|
43
45
|
export function createTaskView(deps) {
|
|
@@ -59,7 +61,10 @@ export function createTaskView(deps) {
|
|
|
59
61
|
showOverview: function () { showOverview(deps); },
|
|
60
62
|
updateSessionTargets: function () { updateSessionTargets(deps); },
|
|
61
63
|
switchTab: function (tab, options) { switchTab(deps, tab, options); },
|
|
62
|
-
applySection: function (section) { applySection(deps, section); }
|
|
64
|
+
applySection: function (section) { applySection(deps, section); },
|
|
65
|
+
// The page content after an explicit navigation (a Task opened, back to
|
|
66
|
+
// the overview) rises in; polling redraws never call this.
|
|
67
|
+
revealPage: function () { reveal(deps.el.detail.querySelector(":scope > .page")); }
|
|
63
68
|
};
|
|
64
69
|
return view.controller;
|
|
65
70
|
}
|
|
@@ -143,9 +148,15 @@ function detailContext(view) {
|
|
|
143
148
|
const api = deps.api;
|
|
144
149
|
return {
|
|
145
150
|
activeTab: deps.state.activeTab,
|
|
146
|
-
onTab:
|
|
151
|
+
onTab: function (tab, options) {
|
|
152
|
+
const changed = tab !== deps.state.activeTab;
|
|
153
|
+
switchTab(deps, tab, options);
|
|
154
|
+
if (changed) reveal(deps.el.detail.querySelector(".tab-panel:not([hidden])"));
|
|
155
|
+
},
|
|
147
156
|
onBack: deps.clearSelection,
|
|
157
|
+
revealSidebar: deps.revealSidebar,
|
|
148
158
|
showDock: deps.workspace.toggleDock,
|
|
159
|
+
toggleOperator: deps.workspace.toggleOperator,
|
|
149
160
|
openConversation: function (materials) {
|
|
150
161
|
deps.workspace.openConversation({ scope: "task", taskId: deps.state.selected, roleName: "leader" }, materials);
|
|
151
162
|
},
|
|
@@ -253,8 +264,14 @@ function showOverview(deps) {
|
|
|
253
264
|
select: deps.selectTask,
|
|
254
265
|
filterAttention: deps.pickAttention,
|
|
255
266
|
readSessions: deps.readPageSessions,
|
|
256
|
-
|
|
267
|
+
toggleOperator: deps.workspace.toggleOperator,
|
|
268
|
+
revealSidebar: deps.revealSidebar
|
|
257
269
|
});
|
|
270
|
+
deps.workspace.syncDockButtons();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function reveal(element) {
|
|
274
|
+
if (element && typeof enter === "function") enter(element, "rise");
|
|
258
275
|
}
|
|
259
276
|
|
|
260
277
|
function switchTab(deps, tab, options) {
|
|
@@ -66,6 +66,9 @@ export const EN = {
|
|
|
66
66
|
"jobs.category.ordinary": "Ordinary Tasks",
|
|
67
67
|
"jobs.category.job": "Job execution Tasks",
|
|
68
68
|
"jobs.category.all": "All Tasks",
|
|
69
|
+
"jobs.categoryShort.ordinary": "Ordinary",
|
|
70
|
+
"jobs.categoryShort.job": "Jobs",
|
|
71
|
+
"jobs.categoryShort.all": "All",
|
|
69
72
|
"jobs.recentSuccess": "Most recent successful Run",
|
|
70
73
|
"jobs.diagnostics": "Execution diagnostics",
|
|
71
74
|
"jobs.artifacts": "Registered fixed artifacts",
|
|
@@ -146,6 +149,9 @@ export const EN = {
|
|
|
146
149
|
"models.window.monthly": "Monthly",
|
|
147
150
|
"manage.nav": "Main navigation",
|
|
148
151
|
"manage.workbench": "Workbench",
|
|
152
|
+
"manage.sections": "Sections",
|
|
153
|
+
"manage.collapseSections": "Collapse sections",
|
|
154
|
+
"manage.showSections": "Show sections",
|
|
149
155
|
"settings.section.preferences": "Preferences",
|
|
150
156
|
"settings.section.skills": "Skills",
|
|
151
157
|
"settings.section.server": "Agents & server",
|
|
@@ -444,6 +450,8 @@ export const EN = {
|
|
|
444
450
|
"sidebar.workItems": "Work items",
|
|
445
451
|
"sidebar.resize": "Resize task list",
|
|
446
452
|
"sidebar.resizeHint": "Drag to resize · double-click to reset",
|
|
453
|
+
"sidebar.collapse": "Collapse task list",
|
|
454
|
+
"sidebar.show": "Show task list",
|
|
447
455
|
"search.label": "Search tasks",
|
|
448
456
|
"bodySearch.title": "Search task contents",
|
|
449
457
|
"bodySearch.query": "Text to find",
|
|
@@ -505,7 +513,6 @@ export const EN = {
|
|
|
505
513
|
"catalog.help.executionSignals": "Live runs, open work and recovery signals to inspect; not failures",
|
|
506
514
|
"actions.refresh": "Refresh",
|
|
507
515
|
"actions.operator": "Operator",
|
|
508
|
-
"actions.back": "Back to tasks",
|
|
509
516
|
"actions.close": "Close",
|
|
510
517
|
"actions.answer": "Answer",
|
|
511
518
|
"actions.save": "Save",
|
|
@@ -558,6 +565,7 @@ export const EN = {
|
|
|
558
565
|
"shortcut.refresh": "Refresh",
|
|
559
566
|
"shortcut.dock": "Show or hide the session panel",
|
|
560
567
|
"shortcut.operator": "Operator session",
|
|
568
|
+
"shortcut.sidebar": "Show or hide the task list",
|
|
561
569
|
"shortcut.tabs": "Switch task sections",
|
|
562
570
|
"shortcut.escape": "Close panel / leave task",
|
|
563
571
|
"loading.dashboard": "Reading local state…",
|
|
@@ -572,7 +580,6 @@ export const EN = {
|
|
|
572
580
|
"common.notReported": "not reported",
|
|
573
581
|
"detail.unobserved": "unknown",
|
|
574
582
|
"detail.partial": "partial",
|
|
575
|
-
"overview.kicker": "Workspace",
|
|
576
583
|
"overview.title": "Overview",
|
|
577
584
|
"overview.sub": "{total} tasks · {active} active",
|
|
578
585
|
"overview.inbox": "Waiting on you",
|
|
@@ -604,7 +611,6 @@ export const EN = {
|
|
|
604
611
|
"metrics.completed": "Completed",
|
|
605
612
|
"metrics.total": "Total",
|
|
606
613
|
"crumbs.label": "Breadcrumb",
|
|
607
|
-
"crumbs.tasks": "Tasks",
|
|
608
614
|
"task.updated": "Updated",
|
|
609
615
|
"tabs.label": "Task sections",
|
|
610
616
|
"tabs.overview": "Overview",
|
|
@@ -1000,6 +1006,7 @@ export const EN = {
|
|
|
1000
1006
|
"terminal.cliHint": "Full terminal",
|
|
1001
1007
|
"terminal.historyLimited": "This Role has a {current}-line tmux history. Exit and re-enter it to use {target} lines.",
|
|
1002
1008
|
"global.open": "Global input",
|
|
1009
|
+
"global.openRole": "Message another Role…",
|
|
1003
1010
|
"global.title": "Global Role input",
|
|
1004
1011
|
"global.help": "Queue is the default. A cancel request does not prove the Turn stopped.",
|
|
1005
1012
|
"global.role": "Role",
|
|
@@ -64,6 +64,9 @@ export const ZH_CN = {
|
|
|
64
64
|
"jobs.category.ordinary": "普通任务",
|
|
65
65
|
"jobs.category.job": "Job 执行任务",
|
|
66
66
|
"jobs.category.all": "全部任务",
|
|
67
|
+
"jobs.categoryShort.ordinary": "普通",
|
|
68
|
+
"jobs.categoryShort.job": "Job",
|
|
69
|
+
"jobs.categoryShort.all": "全部",
|
|
67
70
|
"jobs.recentSuccess": "最近成功的 Run",
|
|
68
71
|
"jobs.diagnostics": "执行诊断",
|
|
69
72
|
"jobs.artifacts": "已登记固定产物",
|
|
@@ -144,6 +147,9 @@ export const ZH_CN = {
|
|
|
144
147
|
"models.window.monthly": "每月",
|
|
145
148
|
"manage.nav": "主导航",
|
|
146
149
|
"manage.workbench": "工作台",
|
|
150
|
+
"manage.sections": "分区",
|
|
151
|
+
"manage.collapseSections": "收起分区列表",
|
|
152
|
+
"manage.showSections": "展开分区列表",
|
|
147
153
|
"settings.section.preferences": "偏好设置",
|
|
148
154
|
"settings.section.skills": "Skill",
|
|
149
155
|
"settings.section.server": "Agent 与服务",
|
|
@@ -440,6 +446,8 @@ export const ZH_CN = {
|
|
|
440
446
|
"sidebar.workItems": "工作项",
|
|
441
447
|
"sidebar.resize": "调整任务列表宽度",
|
|
442
448
|
"sidebar.resizeHint": "拖拽调整宽度 · 双击恢复默认",
|
|
449
|
+
"sidebar.collapse": "收起任务列表",
|
|
450
|
+
"sidebar.show": "展开任务列表",
|
|
443
451
|
"search.label": "搜索任务",
|
|
444
452
|
"bodySearch.title": "搜索任务正文",
|
|
445
453
|
"bodySearch.query": "查找正文",
|
|
@@ -501,7 +509,6 @@ export const ZH_CN = {
|
|
|
501
509
|
"catalog.help.executionSignals": "需要检查的运行、未完成工作和恢复信号;不代表失败",
|
|
502
510
|
"actions.refresh": "刷新",
|
|
503
511
|
"actions.operator": "Operator",
|
|
504
|
-
"actions.back": "返回任务列表",
|
|
505
512
|
"actions.close": "关闭",
|
|
506
513
|
"actions.answer": "回答",
|
|
507
514
|
"actions.save": "保存",
|
|
@@ -554,6 +561,7 @@ export const ZH_CN = {
|
|
|
554
561
|
"shortcut.refresh": "刷新",
|
|
555
562
|
"shortcut.dock": "显示或隐藏会话面板",
|
|
556
563
|
"shortcut.operator": "Operator 会话",
|
|
564
|
+
"shortcut.sidebar": "显示或隐藏任务列表",
|
|
557
565
|
"shortcut.tabs": "切换任务分区",
|
|
558
566
|
"shortcut.escape": "关闭面板/离开任务",
|
|
559
567
|
"loading.dashboard": "正在读取本地状态…",
|
|
@@ -568,7 +576,6 @@ export const ZH_CN = {
|
|
|
568
576
|
"common.notReported": "未上报",
|
|
569
577
|
"detail.unobserved": "未知",
|
|
570
578
|
"detail.partial": "部分",
|
|
571
|
-
"overview.kicker": "工作区",
|
|
572
579
|
"overview.title": "总览",
|
|
573
580
|
"overview.sub": "{total} 个任务 · {active} 个进行中",
|
|
574
581
|
"overview.inbox": "等待你回答",
|
|
@@ -600,7 +607,6 @@ export const ZH_CN = {
|
|
|
600
607
|
"metrics.completed": "已完成",
|
|
601
608
|
"metrics.total": "总数",
|
|
602
609
|
"crumbs.label": "路径",
|
|
603
|
-
"crumbs.tasks": "任务",
|
|
604
610
|
"task.updated": "更新于",
|
|
605
611
|
"tabs.label": "任务分区",
|
|
606
612
|
"tabs.overview": "概览",
|
|
@@ -996,6 +1002,7 @@ export const ZH_CN = {
|
|
|
996
1002
|
"terminal.cliHint": "完整终端",
|
|
997
1003
|
"terminal.historyLimited": "这个 Role 的 tmux 历史只有 {current} 行;退出并重新进入后可使用 {target} 行。",
|
|
998
1004
|
"global.open": "全局消息",
|
|
1005
|
+
"global.openRole": "给其他 Role 发消息…",
|
|
999
1006
|
"global.title": "全局 Role 消息",
|
|
1000
1007
|
"global.help": "默认排队;取消请求不代表执行已停止。",
|
|
1001
1008
|
"global.role": "Role",
|
|
@@ -16,6 +16,7 @@ import { DOM_SCRIPT } from "./lib/dom.js";
|
|
|
16
16
|
import { FORMAT_SCRIPT } from "./lib/format.js";
|
|
17
17
|
import { I18N_SCRIPT } from "./lib/i18n.js";
|
|
18
18
|
import { MARKDOWN_SCRIPT } from "./lib/markdown.js";
|
|
19
|
+
import { MOTION_SCRIPT } from "./lib/motion.js";
|
|
19
20
|
import { PREFS_SCRIPT } from "./lib/prefs.js";
|
|
20
21
|
import { SESSION_SCRIPT } from "./lib/session.js";
|
|
21
22
|
import { THEME_SCRIPT } from "./lib/theme.js";
|
|
@@ -55,6 +56,7 @@ import { RESIZER_SCRIPT } from "./layout/resizer.js";
|
|
|
55
56
|
import { ROUTER_SCRIPT } from "./layout/router.js";
|
|
56
57
|
import { SHORTCUTS_SCRIPT } from "./layout/shortcuts.js";
|
|
57
58
|
import { SIZING_SCRIPT } from "./layout/sizing.js";
|
|
59
|
+
import { SIDEBAR_NAV_SCRIPT } from "./layout/sidebarNav.js";
|
|
58
60
|
import { WORKSPACE_SCRIPT } from "./layout/workspace.js";
|
|
59
61
|
import { CATALOG_SCRIPT } from "./app/catalog.js";
|
|
60
62
|
import { SELECTION_SCRIPT } from "./app/selection.js";
|
|
@@ -77,6 +79,7 @@ export const CLIENT_MODULES = Object.freeze({
|
|
|
77
79
|
"lib/dom": DOM_SCRIPT,
|
|
78
80
|
"lib/format": FORMAT_SCRIPT,
|
|
79
81
|
"lib/prefs": PREFS_SCRIPT,
|
|
82
|
+
"lib/motion": MOTION_SCRIPT,
|
|
80
83
|
"lib/i18n": I18N_SCRIPT,
|
|
81
84
|
"lib/theme": THEME_SCRIPT,
|
|
82
85
|
"lib/api": API_SCRIPT,
|
|
@@ -116,6 +119,7 @@ export const CLIENT_MODULES = Object.freeze({
|
|
|
116
119
|
"views/task/files": TASK_FILES_SCRIPT,
|
|
117
120
|
"layout/resizer": RESIZER_SCRIPT,
|
|
118
121
|
"layout/sizing": SIZING_SCRIPT,
|
|
122
|
+
"layout/sidebarNav": SIDEBAR_NAV_SCRIPT,
|
|
119
123
|
"layout/workspace": WORKSPACE_SCRIPT,
|
|
120
124
|
"layout/router": ROUTER_SCRIPT,
|
|
121
125
|
"layout/shortcuts": SHORTCUTS_SCRIPT,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { MANAGE_SECTIONS } from "../../shared/manageNav.js";
|
|
2
2
|
export const MANAGE_SHELL_SCRIPT = String.raw `
|
|
3
3
|
import { createI18n } from "/assets/js/lib/i18n.js";
|
|
4
|
+
import { rememberPlace, linkPlaces } from "/assets/js/lib/prefs.js";
|
|
5
|
+
import { enter } from "/assets/js/lib/motion.js";
|
|
4
6
|
const SECTIONS = ${JSON.stringify(MANAGE_SECTIONS)};
|
|
5
7
|
export const i18n = createI18n([document.querySelector("#locale-select"), document.querySelector("#settings-locale")]);
|
|
6
8
|
const pages = new Map();
|
|
@@ -34,6 +36,8 @@ export function manageHref(next) {
|
|
|
34
36
|
return "/" + manageArea(next.page, next.section) + "/" + next.section +
|
|
35
37
|
(next.section === "quota" && next.source === "command-code" ? "?source=command-code" : "");
|
|
36
38
|
}
|
|
39
|
+
// The center column scrolls, not the document (the frame is the workbench's).
|
|
40
|
+
function scroller() { return document.querySelector("#manage-scroll"); }
|
|
37
41
|
function render() {
|
|
38
42
|
if (!started) return;
|
|
39
43
|
const rail = document.querySelector(".manage-rail");
|
|
@@ -43,29 +47,45 @@ function render() {
|
|
|
43
47
|
if (active) link.setAttribute("aria-current", "page"); else link.removeAttribute("aria-current");
|
|
44
48
|
if (dirty[link.dataset.page]?.[link.dataset.section]) link.dataset.dirty = "true"; else delete link.dataset.dirty;
|
|
45
49
|
});
|
|
46
|
-
rail
|
|
47
|
-
|
|
50
|
+
// The global rail marks the current area.
|
|
51
|
+
const app = document.querySelector(".manage-app");
|
|
52
|
+
app.querySelectorAll("a[data-area]").forEach(link => {
|
|
53
|
+
if (link.dataset.area === area) link.setAttribute("aria-current", "page"); else link.removeAttribute("aria-current");
|
|
48
54
|
});
|
|
55
|
+
// The list head and the top bar name where the reader is.
|
|
56
|
+
const areaTitle = i18n.t(area + ".title");
|
|
57
|
+
const sectionTitle = i18n.t((route.page === "models" ? "models" : "settings") + ".section." + route.section);
|
|
58
|
+
document.querySelector("#manage-area-title").textContent = areaTitle;
|
|
59
|
+
document.querySelector("#manage-crumb-area").textContent = areaTitle;
|
|
60
|
+
document.querySelector("#manage-crumb-section").textContent = sectionTitle;
|
|
49
61
|
rail.querySelectorAll("[data-area-group]").forEach(group => {
|
|
50
62
|
if (group.dataset.areaGroup === area) group.dataset.active = "true"; else delete group.dataset.active;
|
|
51
63
|
});
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
linkPlaces(app);
|
|
65
|
+
document.title = sectionTitle + " · " + areaTitle + " · Yui";
|
|
54
66
|
}
|
|
55
67
|
function apply(next) {
|
|
56
68
|
route = next;
|
|
69
|
+
rememberPlace(manageArea(next.page, next.section), manageHref(next));
|
|
70
|
+
rememberPlace("manage", manageHref(next));
|
|
57
71
|
document.querySelector("#models-root").hidden = next.page !== "models";
|
|
58
72
|
document.querySelector("#settings-root").hidden = next.page !== "settings";
|
|
59
73
|
pages.get(next.page).show(next);
|
|
60
74
|
render();
|
|
61
75
|
}
|
|
76
|
+
// A section the reader switched to rises in (never on the first paint).
|
|
77
|
+
function revealSection() {
|
|
78
|
+
if (typeof enter !== "function") return;
|
|
79
|
+
enter(document.querySelector("#models-root:not([hidden]), #settings-root:not([hidden])"), "rise");
|
|
80
|
+
}
|
|
62
81
|
export function navigateManage(next) {
|
|
63
82
|
const normalized = readManageRoute(new URL(manageHref(next), location.origin));
|
|
64
83
|
if (manageHref(normalized) === location.pathname + location.search) return;
|
|
65
|
-
history.replaceState({ ...history.state, scroll:
|
|
84
|
+
history.replaceState({ ...history.state, scroll: scroller().scrollTop }, "");
|
|
66
85
|
history.pushState({ scroll: 0 }, "", manageHref(normalized));
|
|
67
86
|
apply(normalized);
|
|
68
|
-
|
|
87
|
+
scroller().scrollTop = 0;
|
|
88
|
+
revealSection();
|
|
69
89
|
}
|
|
70
90
|
export function createManageShell(options) {
|
|
71
91
|
return {
|
|
@@ -79,7 +99,7 @@ export function registerManagePage(name, controller) { pages.set(name, controlle
|
|
|
79
99
|
export function startManagement() {
|
|
80
100
|
started = true;
|
|
81
101
|
route = readManageRoute(new URL(location.href));
|
|
82
|
-
history.replaceState({ ...history.state, scroll:
|
|
102
|
+
history.replaceState({ ...history.state, scroll: scroller().scrollTop }, "", manageHref(route));
|
|
83
103
|
history.scrollRestoration = "manual";
|
|
84
104
|
apply(route);
|
|
85
105
|
document.querySelector(".manage-app").hidden = false;
|
|
@@ -96,8 +116,10 @@ export function startManagement() {
|
|
|
96
116
|
});
|
|
97
117
|
window.addEventListener("popstate", () => {
|
|
98
118
|
apply(readManageRoute(new URL(location.href)));
|
|
99
|
-
|
|
119
|
+
scroller().scrollTop = history.state?.scroll || 0;
|
|
120
|
+
revealSection();
|
|
100
121
|
});
|
|
122
|
+
window.addEventListener("pageshow", render);
|
|
101
123
|
i18n.subscribe(render);
|
|
102
124
|
}
|
|
103
125
|
`;
|
|
@@ -6,11 +6,12 @@ import { isEditing } from "/assets/js/lib/dom.js";
|
|
|
6
6
|
|
|
7
7
|
const TABS = ${JSON.stringify(TASK_TABS)};
|
|
8
8
|
|
|
9
|
-
// deps: { el, state, globalInput, workspace, selection, catalog, taskView }
|
|
9
|
+
// deps: { el, state, globalInput, workspace, sidebarNav, selection, catalog, taskView }
|
|
10
10
|
// el #search, #settings-dialog and #terminal-host
|
|
11
11
|
// state read-only here: state.selected, state.detail
|
|
12
12
|
// globalInput views/globalInput (its dialog blocks shortcuts while open)
|
|
13
13
|
// workspace layout/workspace: the dock and the operator Session
|
|
14
|
+
// sidebarNav layout/sidebarNav: "[" shows or hides the task list
|
|
14
15
|
// selection app/selection: Escape leaves the Task
|
|
15
16
|
// catalog app/catalog: "r" refreshes
|
|
16
17
|
// taskView app/taskView: 1–4 switch tabs
|
|
@@ -26,10 +27,12 @@ export function bindShortcuts(deps) {
|
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
// Escape unwinds one level: leave a field, close the
|
|
30
|
+
// Escape unwinds one level: leave a field, close the task-list drawer, close
|
|
31
|
+
// the dock, leave the Task.
|
|
30
32
|
function escape(deps, active, typing, inTerminal) {
|
|
31
33
|
if (inTerminal) return;
|
|
32
34
|
if (typing) { active.blur(); return; }
|
|
35
|
+
if (deps.sidebarNav.drawerOpen()) { deps.sidebarNav.closeDrawer(); return; }
|
|
33
36
|
if (deps.workspace.dockVisible()) { deps.workspace.setDockOpen(false); return; }
|
|
34
37
|
if (deps.state.selected) deps.selection.clearSelection();
|
|
35
38
|
}
|
|
@@ -37,8 +40,9 @@ function escape(deps, active, typing, inTerminal) {
|
|
|
37
40
|
function shortcut(deps, event) {
|
|
38
41
|
const key = event.key.toLowerCase();
|
|
39
42
|
if (key === "/") { event.preventDefault(); deps.el.search.focus(); deps.el.search.select(); }
|
|
43
|
+
else if (key === "[") deps.sidebarNav.toggle();
|
|
40
44
|
else if (key === "r") deps.catalog.refresh();
|
|
41
|
-
else if (key === "o") deps.workspace.
|
|
45
|
+
else if (key === "o") deps.workspace.toggleOperator();
|
|
42
46
|
else if (key === "d") deps.workspace.toggleDock();
|
|
43
47
|
else if (/^[1-9]$/.test(key) && Number(key) <= TABS.length && deps.state.detail) deps.taskView.switchTab(TABS[Number(key) - 1]);
|
|
44
48
|
}
|