@zq-silk/yui 2.3.0 → 2.4.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/README.md +15 -1
- package/dist/artifacts/artifactCapability.js +39 -10
- package/dist/artifacts/imageMetadata.js +120 -0
- package/dist/artifacts/managedGit.js +7 -4
- package/dist/artifacts/taskArtifactRepository.js +6 -0
- package/dist/cli/commandCatalog.js +17 -8
- package/dist/cli/invocationAuthority.js +1 -1
- package/dist/commands/executionAuditCommands.js +5 -0
- package/dist/commands/globalRoleCommands.js +41 -3
- package/dist/commands/projectCommands.js +89 -94
- package/dist/commands/roleSkillValidation.js +4 -8
- package/dist/commands/taskCommands.js +25 -2
- package/dist/commands/taskUsageCommand.js +38 -0
- package/dist/context/roleSessionContext.js +18 -2
- package/dist/context/runContextPack.js +5 -1
- package/dist/context/sessionBootstrapManifest.js +10 -1
- package/dist/context/skillCatalog.js +108 -0
- package/dist/context/skillPackage.js +161 -0
- package/dist/context/taskContext.js +3 -2
- package/dist/context/taskSearch.js +127 -0
- package/dist/controlPlaneCli.js +25 -20
- package/dist/controller/fileSchedulerStoreAdapter.js +32 -6
- package/dist/controller/jobControl.js +2 -0
- package/dist/controller/nativeHostInput.js +37 -0
- package/dist/controller/runtime.js +93 -2
- package/dist/controller/runtimeEventInbox.js +2 -2
- package/dist/controller/runtimeEventProcessor.js +3 -3
- package/dist/doctor/doctor.js +10 -0
- package/dist/execution/reviewMainRun.js +1 -0
- package/dist/execution/workItemMainRun.js +1 -0
- package/dist/executor/codexConfigConflict.js +6 -4
- package/dist/executor/effectiveLaunch.js +24 -1
- package/dist/executor/fileRoleLaunchPlanner.js +13 -5
- package/dist/integration/gitIntegrationService.js +7 -12
- package/dist/integration/integrationSourceApplication.js +4 -2
- package/dist/job/httpPreview.js +61 -0
- package/dist/job/jobRunner.js +12 -2
- package/dist/kernel/builtinCapabilities.js +3 -1
- package/dist/message/message.js +15 -1
- package/dist/output/rolePresentation.js +4 -1
- package/dist/repository/executionLaneGitSnapshot.js +2 -1
- package/dist/repository/gitCompatibility.js +62 -0
- package/dist/repository/gitWorkspace.js +48 -71
- package/dist/repository/gitWorktreeInventory.js +121 -0
- package/dist/repository/project.js +74 -25
- package/dist/resources/resourceDiscovery.js +2 -2
- package/dist/resources/resourceGc.js +7 -4
- package/dist/resources/resourceRegistrar.js +2 -1
- package/dist/runtime/agentEndpoint.js +8 -0
- package/dist/runtime/agentHost.js +170 -3
- package/dist/runtime/codexAppServerRuntime.js +1 -1
- package/dist/runtime/codexNativeAccess.js +175 -0
- package/dist/runtime/controlledCodexTui.js +116 -0
- package/dist/runtime/publicReply.js +23 -0
- package/dist/runtime/runtimeObservation.js +48 -0
- package/dist/runtime/structuredProviderHost.js +16 -7
- package/dist/runtime/taskCostMetrics.js +133 -0
- package/dist/runtime/taskUsageMetrics.js +11 -2
- package/dist/runtime/taskUsageQuery.js +38 -0
- package/dist/scheduler/taskExecutionProjection.js +2 -0
- package/dist/scheduler/taskObservabilityProjection.js +9 -1
- package/dist/storage/homeLayout.js +13 -0
- package/dist/storage/sqliteSchema.js +44 -0
- package/dist/storage/sqliteStore.js +53 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +2 -0
- package/dist/storage/taskSearch.js +35 -0
- package/dist/storage/upgrade/projectKnowledgeMigration.js +52 -0
- package/dist/task/ordinaryArchive.js +42 -0
- package/dist/tmux/tmuxManager.js +8 -8
- package/dist/web/assets/assetManifest.js +11 -51
- package/dist/web/assets/client/app/catalog.js +215 -0
- package/dist/web/assets/client/app/main.js +148 -0
- package/dist/web/assets/client/app/selection.js +139 -0
- package/dist/web/assets/client/app/settings.js +326 -0
- package/dist/web/assets/client/app/taskView.js +280 -0
- package/dist/web/assets/client/domain/context.js +96 -0
- package/dist/web/assets/client/domain/records.js +126 -0
- package/dist/web/assets/client/domain/runs.js +119 -0
- package/dist/web/assets/client/domain/taskForms.js +197 -0
- package/dist/web/assets/client/domain/vocab.js +69 -0
- package/dist/web/assets/client/domain/work.js +116 -0
- package/dist/web/assets/client/i18n/en.js +900 -0
- package/dist/web/assets/client/i18n/zh-CN.js +898 -0
- package/dist/web/assets/client/index.js +114 -0
- package/dist/web/assets/client/layout/resizer.js +37 -0
- package/dist/web/assets/client/layout/router.js +35 -0
- package/dist/web/assets/client/layout/shortcuts.js +45 -0
- package/dist/web/assets/client/layout/sizing.js +95 -0
- package/dist/web/assets/client/layout/workspace.js +247 -0
- package/dist/web/assets/client/{api.js → lib/api.js} +20 -4
- package/dist/web/assets/client/{dom.js → lib/dom.js} +24 -12
- package/dist/web/assets/client/{format.js → lib/format.js} +7 -0
- package/dist/web/assets/client/lib/i18n.js +54 -0
- package/dist/web/assets/client/lib/markdown.js +167 -0
- package/dist/web/assets/client/lib/prefs.js +28 -0
- package/dist/web/assets/client/lib/theme.js +68 -0
- package/dist/web/assets/client/ui/containers.js +101 -0
- package/dist/web/assets/client/ui/controls.js +51 -0
- package/dist/web/assets/client/ui/forms.js +48 -0
- package/dist/web/assets/client/ui/metrics.js +137 -0
- package/dist/web/assets/client/ui/primitives.js +92 -0
- package/dist/web/assets/client/ui/text.js +33 -0
- package/dist/web/assets/client/ui/toast.js +12 -0
- package/dist/web/assets/client/views/bodySearch.js +172 -0
- package/dist/web/assets/client/views/createTask.js +85 -0
- package/dist/web/assets/client/views/dock/conversation.js +612 -0
- package/dist/web/assets/client/views/dock/discussion.js +173 -0
- package/dist/web/assets/client/views/dock/preview.js +129 -0
- package/dist/web/assets/client/views/dock/terminal.js +199 -0
- package/dist/web/assets/client/views/globalInput.js +67 -0
- package/dist/web/assets/client/{overview.js → views/overview.js} +61 -51
- package/dist/web/assets/client/{sidebar.js → views/sidebar.js} +56 -62
- package/dist/web/assets/client/views/skills.js +152 -0
- package/dist/web/assets/client/views/task/actions.js +36 -0
- package/dist/web/assets/client/views/task/delivery.js +185 -0
- package/dist/web/assets/client/views/task/deliveryResults.js +159 -0
- package/dist/web/assets/client/views/task/files.js +272 -0
- package/dist/web/assets/client/views/task/observation.js +153 -0
- package/dist/web/assets/client/views/task/overview.js +161 -0
- package/dist/web/assets/client/views/task/page.js +131 -0
- package/dist/web/assets/client/views/task/records.js +163 -0
- package/dist/web/assets/client/views/task/runtime.js +130 -0
- package/dist/web/assets/client/views/task/timeline.js +190 -0
- package/dist/web/assets/shared/geometry.js +19 -0
- package/dist/web/assets/shared/taskTabs.js +8 -0
- package/dist/web/assets/shared/themes.js +93 -0
- package/dist/web/assets/shell/dialogs.js +63 -0
- package/dist/web/assets/shell/dock.js +40 -0
- package/dist/web/assets/shell/index.js +40 -0
- package/dist/web/assets/shell/settings.js +34 -0
- package/dist/web/assets/shell/sidebar.js +45 -0
- package/dist/web/assets/styles/index.js +41 -0
- package/dist/web/assets/styles/{responsive.js → layout/responsive.js} +10 -10
- package/dist/web/assets/styles/{layout.js → layout/workspace.js} +17 -24
- package/dist/web/assets/styles/markdown.js +22 -3
- package/dist/web/assets/styles/tokens.js +10 -63
- package/dist/web/assets/styles/ui/controls.js +76 -0
- package/dist/web/assets/styles/ui/forms.js +46 -0
- package/dist/web/assets/styles/ui/surfaces.js +122 -0
- package/dist/web/assets/styles/views/delivery.js +29 -0
- package/dist/web/assets/styles/views/dock.js +71 -0
- package/dist/web/assets/styles/views/page.js +32 -0
- package/dist/web/assets/styles/views/records.js +70 -0
- package/dist/web/assets/styles/views/settings.js +26 -0
- package/dist/web/assets/styles/views/sidebar.js +74 -0
- package/dist/web/assets/styles/views/task.js +80 -0
- package/dist/web/assets/styles/views/terminal.js +38 -0
- package/dist/web/taskPreviews.js +205 -0
- package/dist/web/tmuxWebTerminal.js +16 -9
- package/dist/web/webConversation.js +373 -0
- package/dist/web/webMaterials.js +155 -0
- package/dist/web/webServer.js +295 -18
- package/dist/web/webSettings.js +331 -0
- package/dist/web/webSkills.js +86 -0
- package/dist/web/webSnapshot.js +40 -8
- package/dist/web/webTaskSurface.js +106 -8
- package/dist/workspace/gitChangeSetCapture.js +3 -1
- package/docs/managed-turn-and-session-runtime.md +9 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +7 -0
- package/docs/release-workflow.md +28 -0
- package/docs/release-workflow.zh-CN.md +21 -0
- package/docs/storage-baseline.md +38 -1
- package/docs/storage-baseline.zh-CN.md +22 -0
- package/docs/task-discovery.md +28 -0
- package/docs/testing/verification-levels.md +2 -2
- package/i18n/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/dist/web/assets/client/app.js +0 -808
- package/dist/web/assets/client/components.js +0 -477
- package/dist/web/assets/client/detail.js +0 -379
- package/dist/web/assets/client/dock.js +0 -309
- package/dist/web/assets/client/evidence.js +0 -364
- package/dist/web/assets/client/forms.js +0 -230
- package/dist/web/assets/client/i18n.js +0 -1277
- package/dist/web/assets/client/markdown.js +0 -114
- package/dist/web/assets/client/records.js +0 -198
- package/dist/web/assets/client/sections.js +0 -349
- package/dist/web/assets/client/theme.js +0 -71
- package/dist/web/assets/shell.js +0 -161
- package/dist/web/assets/styles/components.js +0 -216
- package/dist/web/assets/styles/views.js +0 -309
- /package/dist/web/assets/{icons.js → shared/icons.js} +0 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
export const CATALOG_SCRIPT = String.raw `
|
|
2
|
+
// The Task catalog: the refresh loop over the dashboard read, the sidebar
|
|
3
|
+
// (attention, status tabs, search, paged list), its filters from the URL and
|
|
4
|
+
// the explicit Session read of the current catalog page. This module is the
|
|
5
|
+
// only writer of the catalog fields of state. Only the newest request may
|
|
6
|
+
// apply its result.
|
|
7
|
+
import { fill, formatClock } from "/assets/js/lib/format.js";
|
|
8
|
+
import { h } from "/assets/js/lib/dom.js";
|
|
9
|
+
import { loadingBlock } from "/assets/js/ui/primitives.js";
|
|
10
|
+
import { renderAttentionBar, renderFilters, renderTasks, STATUS_FILTERS, ATTENTION_KINDS } from "/assets/js/views/sidebar.js";
|
|
11
|
+
import { syncUrl } from "/assets/js/layout/router.js";
|
|
12
|
+
|
|
13
|
+
// deps: { el, state, t, locale, api, toast, selectTask, showOverview, reloadSelected }
|
|
14
|
+
// el the sidebar elements, the sync state and the refresh button
|
|
15
|
+
// state writes the catalog fields; reads state.selected
|
|
16
|
+
// selectTask(taskId) a task row was chosen (app/selection)
|
|
17
|
+
// showOverview() redraw the center overview while nothing is selected
|
|
18
|
+
// reloadSelected() re-read the selected Task; rejects when the read fails
|
|
19
|
+
export function createCatalog(deps) {
|
|
20
|
+
// progress: whether a refresh is in flight and the newest request number.
|
|
21
|
+
const progress = { refreshing: false, request: 0 };
|
|
22
|
+
const catalog = {
|
|
23
|
+
refresh: function (options) { return refresh(deps, progress, options); },
|
|
24
|
+
reset: function () { reset(deps, progress); },
|
|
25
|
+
renderSidebar: function () { renderSidebar(deps, progress); },
|
|
26
|
+
renderTaskList: function () { renderTaskList(deps); },
|
|
27
|
+
pickAttention: function (kind) { pickAttention(deps, progress, kind); },
|
|
28
|
+
readPageSessions: function () { return readPageSessions(deps); },
|
|
29
|
+
applyFilters: function (params) { applyFilters(deps, params); }
|
|
30
|
+
};
|
|
31
|
+
bindCatalogControls(deps.el, deps.state, catalog);
|
|
32
|
+
return catalog;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function reset(deps, progress) {
|
|
36
|
+
deps.state.catalogCursor = null;
|
|
37
|
+
deps.state.nextCursor = null;
|
|
38
|
+
refresh(deps, progress);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function refresh(deps, progress, options) {
|
|
42
|
+
const el = deps.el;
|
|
43
|
+
const state = deps.state;
|
|
44
|
+
const quiet = options && options.quiet;
|
|
45
|
+
if (progress.refreshing && quiet) return;
|
|
46
|
+
const request = ++progress.request;
|
|
47
|
+
progress.refreshing = true;
|
|
48
|
+
beginRefresh(el, state, deps.t, quiet);
|
|
49
|
+
const previousInputs = state.counts ? state.counts.openInputs : null;
|
|
50
|
+
try {
|
|
51
|
+
const query = catalogQuery(state);
|
|
52
|
+
const dashboard = await deps.api.dashboard(query);
|
|
53
|
+
if (request !== progress.request) return;
|
|
54
|
+
applyDashboard(state, dashboard, query);
|
|
55
|
+
showDashboard(deps, progress, previousInputs);
|
|
56
|
+
// A selected Task may live on another page or outside current filters;
|
|
57
|
+
// only its own read can establish that it no longer exists.
|
|
58
|
+
if (state.selected) {
|
|
59
|
+
try { await deps.reloadSelected(); }
|
|
60
|
+
catch { deps.toast(deps.t("errors.disconnected")); }
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
if (request !== progress.request) return;
|
|
64
|
+
dashboardFailed(deps, quiet);
|
|
65
|
+
} finally {
|
|
66
|
+
if (request === progress.request) {
|
|
67
|
+
progress.refreshing = false;
|
|
68
|
+
el.refresh.disabled = false;
|
|
69
|
+
el.catalogNext.disabled = !state.nextCursor;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function showDashboard(deps, progress, previousInputs) {
|
|
75
|
+
deps.el.sync.dataset.state = "ok";
|
|
76
|
+
renderSidebar(deps, progress);
|
|
77
|
+
if (!deps.state.selected) deps.showOverview();
|
|
78
|
+
if (previousInputs !== null && deps.state.counts.openInputs > previousInputs) deps.toast(deps.t("input.new"));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function dashboardFailed(deps, quiet) {
|
|
82
|
+
deps.el.sync.dataset.state = "error";
|
|
83
|
+
if (quiet) return;
|
|
84
|
+
deps.el.tasks.replaceChildren(h("div.list-empty.is-error", null, h("p", null, deps.t("errors.dashboard"))));
|
|
85
|
+
deps.toast(deps.t("errors.dashboard"));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function renderSidebar(deps, progress) {
|
|
89
|
+
const el = deps.el;
|
|
90
|
+
const state = deps.state;
|
|
91
|
+
const t = deps.t;
|
|
92
|
+
renderAttentionBar(el.attention, state, t, function (kind) { pickAttention(deps, progress, kind); });
|
|
93
|
+
renderFilters(el.filters, state, t, function (filter) {
|
|
94
|
+
state.filter = filter;
|
|
95
|
+
state.attentionFilter = null;
|
|
96
|
+
state.catalogAll = false;
|
|
97
|
+
syncUrl(state, false);
|
|
98
|
+
reset(deps, progress);
|
|
99
|
+
});
|
|
100
|
+
el.catalogCount.textContent = fill(t("catalog.count"), { shown: state.tasks.length, total: state.catalogTotal });
|
|
101
|
+
el.catalogReset.hidden = !state.catalogCursor;
|
|
102
|
+
el.catalogNext.hidden = !state.nextCursor && !state.catalogCursor;
|
|
103
|
+
el.catalogAttentionReset.hidden = !state.attentionFilter;
|
|
104
|
+
renderTaskList(deps);
|
|
105
|
+
el.lastSync.textContent = state.generatedAt ? formatClock(state.generatedAt, deps.locale()) : "—";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Redraw the list in place, keeping its scroll position.
|
|
109
|
+
function renderTaskList(deps) {
|
|
110
|
+
const scroll = deps.el.tasks.scrollTop;
|
|
111
|
+
renderTasks(deps.el.tasks, deps.state, deps.t, deps.locale(), deps.selectTask);
|
|
112
|
+
deps.el.tasks.scrollTop = scroll;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function pickAttention(deps, progress, kind) {
|
|
116
|
+
const state = deps.state;
|
|
117
|
+
state.attentionFilter = kind;
|
|
118
|
+
state.catalogAll = !!kind && !!state.catalogScope && state.catalogScope.archived === "included";
|
|
119
|
+
state.filter = "all";
|
|
120
|
+
state.query = "";
|
|
121
|
+
deps.el.search.value = "";
|
|
122
|
+
syncUrl(state, false);
|
|
123
|
+
reset(deps, progress);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function beginRefresh(el, state, t, quiet) {
|
|
127
|
+
el.catalogNext.disabled = true;
|
|
128
|
+
el.sync.dataset.state = "syncing";
|
|
129
|
+
if (quiet) return;
|
|
130
|
+
el.refresh.disabled = true;
|
|
131
|
+
if (!state.tasks.length) el.tasks.replaceChildren(loadingBlock(t("loading.dashboard")));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function catalogQuery(state) {
|
|
135
|
+
const query = new URLSearchParams();
|
|
136
|
+
if (state.catalogAll) query.set("all", "true");
|
|
137
|
+
if (state.filter !== "all") query.set("status", state.filter);
|
|
138
|
+
if (state.query.trim()) query.set("search", state.query.trim());
|
|
139
|
+
if (state.attentionFilter) query.set("attention", state.attentionFilter);
|
|
140
|
+
if (state.catalogCursor) query.set("cursor", state.catalogCursor);
|
|
141
|
+
return query.toString();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function taskIds(tasks, key) {
|
|
145
|
+
return JSON.stringify(tasks.map(function (task) { return task[key]; }));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function applyDashboard(state, dashboard, query) {
|
|
149
|
+
state.tasks = dashboard.tasks;
|
|
150
|
+
// A Session read describes exactly one catalog page.
|
|
151
|
+
if (state.catalogQuery !== query || (state.sessionOverview
|
|
152
|
+
&& taskIds(state.sessionOverview.tasks, "taskId") !== taskIds(dashboard.tasks, "id"))) {
|
|
153
|
+
state.sessionOverview = null;
|
|
154
|
+
}
|
|
155
|
+
state.catalogQuery = query;
|
|
156
|
+
state.counts = { ...dashboard.counts, openInputs: dashboard.attention.openInputs.count };
|
|
157
|
+
state.catalogAttention = dashboard.attention;
|
|
158
|
+
state.catalogScope = dashboard.scope;
|
|
159
|
+
state.catalogTotal = dashboard.total;
|
|
160
|
+
state.nextCursor = dashboard.nextCursor;
|
|
161
|
+
state.attention = dashboard.attention.openInputs.refs.map(function (ref) {
|
|
162
|
+
const task = state.tasks.find(function (entry) { return entry.id === ref.taskId; });
|
|
163
|
+
return { taskId: ref.taskId, taskTitle: task ? task.title : ref.taskId };
|
|
164
|
+
});
|
|
165
|
+
state.generatedAt = new Date().toISOString();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function readPageSessions(deps) {
|
|
169
|
+
const state = deps.state;
|
|
170
|
+
if (state.sessionLoading) return;
|
|
171
|
+
const query = state.catalogQuery;
|
|
172
|
+
state.sessionLoading = true;
|
|
173
|
+
deps.showOverview();
|
|
174
|
+
try {
|
|
175
|
+
const result = await deps.api.pageSessions(query);
|
|
176
|
+
if (query !== state.catalogQuery) return;
|
|
177
|
+
if (taskIds(result.tasks, "taskId") !== taskIds(state.tasks, "id")) throw new Error(deps.t("overview.sessionsPageChanged"));
|
|
178
|
+
state.sessionOverview = result;
|
|
179
|
+
state.sessionError = null;
|
|
180
|
+
} catch (error) { state.sessionError = error.message; }
|
|
181
|
+
finally {
|
|
182
|
+
state.sessionLoading = false;
|
|
183
|
+
if (!state.selected) deps.showOverview();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Filters from the URL, on load and on back/forward.
|
|
188
|
+
function applyFilters(deps, params) {
|
|
189
|
+
const state = deps.state;
|
|
190
|
+
const filter = params.get("filter");
|
|
191
|
+
state.filter = STATUS_FILTERS.includes(filter) ? filter : "all";
|
|
192
|
+
state.query = params.get("q") || "";
|
|
193
|
+
state.attentionFilter = ATTENTION_KINDS.includes(params.get("attention")) ? params.get("attention") : null;
|
|
194
|
+
state.catalogAll = params.get("all") === "true";
|
|
195
|
+
deps.el.search.value = state.query;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function bindCatalogControls(el, state, catalog) {
|
|
199
|
+
let searchTimer = null;
|
|
200
|
+
el.search.addEventListener("input", function () {
|
|
201
|
+
state.query = el.search.value;
|
|
202
|
+
syncUrl(state, true);
|
|
203
|
+
window.clearTimeout(searchTimer);
|
|
204
|
+
searchTimer = window.setTimeout(catalog.reset, 200);
|
|
205
|
+
});
|
|
206
|
+
el.catalogNext.addEventListener("click", function () {
|
|
207
|
+
if (!state.nextCursor) return;
|
|
208
|
+
state.catalogCursor = state.nextCursor;
|
|
209
|
+
catalog.refresh();
|
|
210
|
+
});
|
|
211
|
+
el.catalogReset.addEventListener("click", catalog.reset);
|
|
212
|
+
el.catalogAttentionReset.addEventListener("click", function () { catalog.pickAttention(null); });
|
|
213
|
+
el.refresh.addEventListener("click", function () { catalog.refresh(); });
|
|
214
|
+
}
|
|
215
|
+
`;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
export const APP_SCRIPT = String.raw `
|
|
2
|
+
// Composition root (served as /assets/app.js). It binds the static shell,
|
|
3
|
+
// owns the view state object and is the only place where controllers are
|
|
4
|
+
// wired to each other. Each controller receives one explicit deps object,
|
|
5
|
+
// documented at its factory: services, controllers created before it, and
|
|
6
|
+
// named callbacks into controllers created after it (bound here, late).
|
|
7
|
+
// Everything kept in the browser is view state; Task facts always come from
|
|
8
|
+
// the loopback API.
|
|
9
|
+
//
|
|
10
|
+
// layout/workspace sidebar | center | dock columns, dock modes, sizing
|
|
11
|
+
// app/taskView draws the selected Task, the dock discussion, the overview
|
|
12
|
+
// app/selection selected Task, its detail reads, URL-driven navigation
|
|
13
|
+
// app/catalog refresh loop, sidebar, catalog paging and filters
|
|
14
|
+
// layout/shortcuts keyboard shortcuts
|
|
15
|
+
//
|
|
16
|
+
// Flows:
|
|
17
|
+
// refresh catalog.refresh → sidebar, then the overview (nothing selected)
|
|
18
|
+
// or selection.reload → taskView.render
|
|
19
|
+
// select sidebar row / overview row / URL → selection.selectTask
|
|
20
|
+
// leave back button or Escape → selection.clearSelection
|
|
21
|
+
// write Task page or discussion afterWrite → quiet catalog refresh
|
|
22
|
+
// URL load and back/forward → catalog.applyFilters + selection.followUrl
|
|
23
|
+
import { createI18n } from "/assets/js/lib/i18n.js";
|
|
24
|
+
import { createThemeController } from "/assets/js/lib/theme.js";
|
|
25
|
+
import { api } from "/assets/js/lib/api.js";
|
|
26
|
+
import { createToast } from "/assets/js/ui/toast.js";
|
|
27
|
+
import { createTerminalController } from "/assets/js/views/dock/terminal.js";
|
|
28
|
+
import { createConversationController } from "/assets/js/views/dock/conversation.js";
|
|
29
|
+
import { createPreviewController } from "/assets/js/views/dock/preview.js";
|
|
30
|
+
import { bindGlobalInput } from "/assets/js/views/globalInput.js";
|
|
31
|
+
import { bindCreateTask } from "/assets/js/views/createTask.js";
|
|
32
|
+
import { bindBodySearch } from "/assets/js/views/bodySearch.js";
|
|
33
|
+
import { readQuery } from "/assets/js/layout/router.js";
|
|
34
|
+
import { createWorkspace } from "/assets/js/layout/workspace.js";
|
|
35
|
+
import { bindShortcuts } from "/assets/js/layout/shortcuts.js";
|
|
36
|
+
import { createTaskView } from "/assets/js/app/taskView.js";
|
|
37
|
+
import { createSelection } from "/assets/js/app/selection.js";
|
|
38
|
+
import { createCatalog } from "/assets/js/app/catalog.js";
|
|
39
|
+
|
|
40
|
+
const $ = function (selector) { return document.querySelector(selector); };
|
|
41
|
+
const el = {
|
|
42
|
+
sidebar: $(".sidebar"), sidebarDivider: $("#sidebar-divider"), center: $("#center"), detail: $("#detail"),
|
|
43
|
+
search: $("#search"), filters: $("#status-filters"), attention: $("#attention-bar"), tasks: $("#task-list"),
|
|
44
|
+
catalogNext: $("#catalog-next"), catalogReset: $("#catalog-reset"), catalogCount: $("#catalog-count"),
|
|
45
|
+
catalogAttentionReset: $("#catalog-attention-reset"),
|
|
46
|
+
refresh: $("#refresh"), sync: $("#sync-state"), lastSync: $("#last-sync"), toast: $("#toast"),
|
|
47
|
+
operator: $("#operator-terminal"), settingsOpen: $("#settings-open"), settings: $("#settings-dialog"),
|
|
48
|
+
locale: $("#locale-select"), themeOptions: $("#theme-options"),
|
|
49
|
+
dock: $("#dock"), divider: $("#dock-divider"), dockSwap: $("#dock-swap"), dockClose: $("#dock-close"),
|
|
50
|
+
dockTabDiscussion: $("#dock-tab-discussion"), dockTabSession: $("#dock-tab-session"),
|
|
51
|
+
dockTabConversation: $("#dock-tab-conversation"), conversation: $("#dock-conversation"),
|
|
52
|
+
dockTabPreview: $("#dock-tab-preview"), preview: $("#dock-preview"),
|
|
53
|
+
discussion: $("#dock-discussion"), session: $("#dock-session"),
|
|
54
|
+
terminalHost: $("#terminal-host"), terminalEmpty: $("#terminal-empty"), terminalState: $("#terminal-state"),
|
|
55
|
+
sessionTargets: $("#session-targets"), terminalCli: $("#terminal-cli")
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// View state. Each group has exactly one writer.
|
|
59
|
+
const state = {
|
|
60
|
+
// app/catalog: the dashboard read, its filters and paging, the Session read.
|
|
61
|
+
tasks: [], counts: null, attention: [], catalogAttention: null, catalogScope: null, catalogQuery: "",
|
|
62
|
+
catalogAll: false, catalogTotal: 0, catalogCursor: null, nextCursor: null,
|
|
63
|
+
sessionOverview: null, sessionLoading: false, sessionError: null,
|
|
64
|
+
attentionFilter: null, generatedAt: null, filter: "all", query: "",
|
|
65
|
+
// app/selection: the selected Task and its loaded detail.
|
|
66
|
+
selected: null, detail: null, detailKey: null,
|
|
67
|
+
// app/taskView: the tab the reader is on.
|
|
68
|
+
activeTab: "overview"
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const i18n = createI18n(el.locale);
|
|
72
|
+
const t = i18n.t;
|
|
73
|
+
const locale = i18n.getLocale;
|
|
74
|
+
const theme = createThemeController(el.themeOptions, t);
|
|
75
|
+
const toast = createToast(el.toast);
|
|
76
|
+
const conversation = createConversationController(el.conversation, t, locale);
|
|
77
|
+
const preview = createPreviewController(el.preview, t);
|
|
78
|
+
const terminal = createTerminalController({
|
|
79
|
+
host: el.terminalHost, empty: el.terminalEmpty, state: el.terminalState, targets: el.sessionTargets, cli: el.terminalCli
|
|
80
|
+
}, t, locale, toast);
|
|
81
|
+
theme.subscribe(function () { terminal.retheme(); });
|
|
82
|
+
|
|
83
|
+
// Controllers are created in dependency order; the arrow callbacks below are
|
|
84
|
+
// the only edges pointing at controllers created later.
|
|
85
|
+
let taskView = null;
|
|
86
|
+
let selection = null;
|
|
87
|
+
let catalog = null;
|
|
88
|
+
const workspace = createWorkspace({
|
|
89
|
+
el: el, state: state, terminal: terminal, conversation: conversation, preview: preview,
|
|
90
|
+
sessionOpened: function () { taskView.updateSessionTargets(); }
|
|
91
|
+
});
|
|
92
|
+
taskView = createTaskView({
|
|
93
|
+
el: el, state: state, t: t, locale: locale, api: api, toast: toast, terminal: terminal, workspace: workspace,
|
|
94
|
+
selectTask: function (taskId) { return selection.selectTask(taskId); },
|
|
95
|
+
clearSelection: function () { selection.clearSelection(); },
|
|
96
|
+
pickAttention: function (kind) { catalog.pickAttention(kind); },
|
|
97
|
+
readPageSessions: function () { return catalog.readPageSessions(); },
|
|
98
|
+
refreshCatalog: function () { return catalog.refresh({ quiet: true }); }
|
|
99
|
+
});
|
|
100
|
+
selection = createSelection({
|
|
101
|
+
state: state, t: t, api: api, toast: toast, workspace: workspace, taskView: taskView,
|
|
102
|
+
renderTaskList: function () { catalog.renderTaskList(); }
|
|
103
|
+
});
|
|
104
|
+
catalog = createCatalog({
|
|
105
|
+
el: el, state: state, t: t, locale: locale, api: api, toast: toast,
|
|
106
|
+
selectTask: selection.selectTask,
|
|
107
|
+
showOverview: taskView.showOverview,
|
|
108
|
+
reloadSelected: selection.reload
|
|
109
|
+
});
|
|
110
|
+
const globalInput = bindGlobalInput({ t: t, api: api });
|
|
111
|
+
bindCreateTask({ t: t, api: api, selectTask: selection.selectTask,
|
|
112
|
+
afterWrite: function () { catalog.refresh({ quiet: true }); } });
|
|
113
|
+
bindBodySearch({ t: t, api: api, selectTask: selection.selectTask,
|
|
114
|
+
currentTask: function () { return state.selected; },
|
|
115
|
+
captureDraftTarget: conversation.captureDraftTarget, insertReference: conversation.insertReference });
|
|
116
|
+
bindShortcuts({
|
|
117
|
+
el: el, state: state, globalInput: globalInput, workspace: workspace,
|
|
118
|
+
selection: selection, catalog: catalog, taskView: taskView
|
|
119
|
+
});
|
|
120
|
+
el.settingsOpen.addEventListener("click", function () { window.location.assign("/settings"); });
|
|
121
|
+
|
|
122
|
+
// The URL names the catalog filters and the selected Task (with its section).
|
|
123
|
+
function applyStateFromUrl() {
|
|
124
|
+
const params = readQuery();
|
|
125
|
+
catalog.applyFilters(params);
|
|
126
|
+
selection.followUrl(params);
|
|
127
|
+
}
|
|
128
|
+
window.addEventListener("popstate", function () {
|
|
129
|
+
applyStateFromUrl();
|
|
130
|
+
catalog.reset();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
i18n.subscribe(function () {
|
|
134
|
+
theme.render();
|
|
135
|
+
terminal.relabel();
|
|
136
|
+
catalog.renderSidebar();
|
|
137
|
+
taskView.relabel();
|
|
138
|
+
workspace.updateLayout();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
workspace.updateLayout();
|
|
142
|
+
taskView.showOverview();
|
|
143
|
+
conversation.open({ scope: "global", roleName: "operator" });
|
|
144
|
+
applyStateFromUrl();
|
|
145
|
+
workspace.restoreDefaultMode();
|
|
146
|
+
catalog.refresh();
|
|
147
|
+
window.setInterval(function () { catalog.refresh({ quiet: true }); }, 5000);
|
|
148
|
+
`;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
export const SELECTION_SCRIPT = String.raw `
|
|
2
|
+
// Which Task is selected and its loaded detail: selecting one, leaving it,
|
|
3
|
+
// following the URL, and reading the Context snapshot (the only input to core
|
|
4
|
+
// rendering) plus the optional runtime observation. This module is the only
|
|
5
|
+
// writer of state.selected, state.detail and state.detailKey; app/taskView
|
|
6
|
+
// draws the result. Leaving a Task with an unsent draft asks first; the page
|
|
7
|
+
// itself warns before unload.
|
|
8
|
+
import { applyUrl, readQuery, syncUrl, urlTaskId } from "/assets/js/layout/router.js";
|
|
9
|
+
|
|
10
|
+
// Fingerprint of the rendered payload. Quiet polling skips identical reads so
|
|
11
|
+
// local view state (open blocks, list pages, drafts) survives.
|
|
12
|
+
function fingerprint(value) {
|
|
13
|
+
const text = JSON.stringify(value);
|
|
14
|
+
let hash = 0;
|
|
15
|
+
for (let i = 0; i < text.length; i += 1) hash = (hash * 31 + text.charCodeAt(i)) | 0;
|
|
16
|
+
return text.length + ":" + hash;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// deps: { state, t, api, toast, workspace, taskView, renderTaskList }
|
|
20
|
+
// state writes selected, detail, detailKey
|
|
21
|
+
// api context, inspect and observation reads
|
|
22
|
+
// workspace layout/workspace: layout and the dock on entering/leaving
|
|
23
|
+
// taskView app/taskView: draws the selected Task and the overview
|
|
24
|
+
// renderTaskList() redraw the sidebar list's selection (app/catalog)
|
|
25
|
+
export function createSelection(deps) {
|
|
26
|
+
window.addEventListener("beforeunload", function (event) {
|
|
27
|
+
if (!deps.taskView.hasUnsent()) return;
|
|
28
|
+
event.preventDefault();
|
|
29
|
+
event.returnValue = "";
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
selectTask: function (taskId) { return selectTask(deps, taskId); },
|
|
33
|
+
clearSelection: function () { clearSelection(deps); },
|
|
34
|
+
followUrl: function (params) { followUrl(deps, params); },
|
|
35
|
+
// Quietly re-read the selected Task; rejects when the read fails.
|
|
36
|
+
reload: function () { return load(deps, deps.state.selected, false); }
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function canLeave(deps) {
|
|
41
|
+
return !deps.taskView.hasUnsent() || window.confirm(deps.t("confirm.leave"));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function selectTask(deps, taskId) {
|
|
45
|
+
const state = deps.state;
|
|
46
|
+
// Re-selecting the visible Task is not navigation.
|
|
47
|
+
if (state.selected === taskId && state.detail) return;
|
|
48
|
+
if (state.selected !== taskId && !canLeave(deps)) { syncUrl(state, true); return; }
|
|
49
|
+
if (urlTaskId() !== taskId) {
|
|
50
|
+
const params = readQuery();
|
|
51
|
+
params.delete("section");
|
|
52
|
+
applyUrl(params, true);
|
|
53
|
+
}
|
|
54
|
+
// Show taskId as selected before its detail arrives.
|
|
55
|
+
state.selected = taskId;
|
|
56
|
+
state.detail = null;
|
|
57
|
+
deps.workspace.enterTask();
|
|
58
|
+
deps.taskView.enterTask();
|
|
59
|
+
syncUrl(state, state.selected === urlTaskId());
|
|
60
|
+
deps.workspace.updateLayout();
|
|
61
|
+
deps.renderTaskList();
|
|
62
|
+
try {
|
|
63
|
+
await load(deps, taskId, true);
|
|
64
|
+
} catch {
|
|
65
|
+
if (state.selected !== taskId) return;
|
|
66
|
+
deps.taskView.showDetailError();
|
|
67
|
+
deps.toast(deps.t("errors.detail"));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function clearSelection(deps) {
|
|
72
|
+
if (!canLeave(deps)) return;
|
|
73
|
+
dropSelection(deps);
|
|
74
|
+
syncUrl(deps.state, !urlTaskId());
|
|
75
|
+
deps.workspace.updateLayout();
|
|
76
|
+
deps.taskView.showOverview();
|
|
77
|
+
deps.renderTaskList();
|
|
78
|
+
deps.taskView.updateSessionTargets();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Drop the selected Task and its dock discussion; callers redraw.
|
|
82
|
+
function dropSelection(deps) {
|
|
83
|
+
deps.state.selected = null;
|
|
84
|
+
deps.state.detail = null;
|
|
85
|
+
deps.taskView.leaveTask();
|
|
86
|
+
deps.workspace.leaveTask();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// The Task and section named by the URL, on load and on back/forward.
|
|
90
|
+
function followUrl(deps, params) {
|
|
91
|
+
const state = deps.state;
|
|
92
|
+
const taskId = params.get("task");
|
|
93
|
+
if (taskId) {
|
|
94
|
+
if (taskId === state.selected && state.detail) deps.taskView.applySection(params.get("section"));
|
|
95
|
+
else selectTask(deps, taskId);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (state.selected && !canLeave(deps)) { syncUrl(state, true); return; }
|
|
99
|
+
dropSelection(deps);
|
|
100
|
+
deps.workspace.updateLayout();
|
|
101
|
+
deps.taskView.showOverview();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function load(deps, taskId, navigate) {
|
|
105
|
+
const state = deps.state;
|
|
106
|
+
if (navigate) deps.taskView.showLoading();
|
|
107
|
+
// Rendering consumes the current Context snapshot, not event pages.
|
|
108
|
+
const snapshot = await deps.api.workbench(taskId);
|
|
109
|
+
const core = snapshot.core;
|
|
110
|
+
const task = snapshot.task;
|
|
111
|
+
if (state.selected !== taskId) return;
|
|
112
|
+
const previous = state.detail && state.detail.task.id === taskId ? state.detail : null;
|
|
113
|
+
const detail = {
|
|
114
|
+
task: task, core: core, briefValue: snapshot.brief, evidenceSummary: snapshot.evidence,
|
|
115
|
+
viewState: previous ? previous.viewState : {},
|
|
116
|
+
runtime: previous ? previous.runtime : null,
|
|
117
|
+
runtimeStatus: previous ? previous.runtimeStatus : "waiting",
|
|
118
|
+
runtimeObservedAt: previous ? previous.runtimeObservedAt : new Date().toISOString()
|
|
119
|
+
};
|
|
120
|
+
state.detail = detail;
|
|
121
|
+
state.detailKey = fingerprint(core);
|
|
122
|
+
deps.taskView.render(navigate);
|
|
123
|
+
if (navigate) deps.taskView.applySection(readQuery().get("section"));
|
|
124
|
+
observe(deps, taskId, detail);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// The optional observation never participates in core readiness.
|
|
128
|
+
function observe(deps, taskId, detail) {
|
|
129
|
+
function settle(runtime, status) {
|
|
130
|
+
if (deps.state.detail !== detail) return;
|
|
131
|
+
detail.runtime = runtime;
|
|
132
|
+
detail.runtimeStatus = status;
|
|
133
|
+
detail.runtimeObservedAt = new Date().toISOString();
|
|
134
|
+
deps.taskView.observationArrived(detail);
|
|
135
|
+
}
|
|
136
|
+
deps.api.observation(taskId).then(function (runtime) { settle(runtime, "available"); })
|
|
137
|
+
.catch(function () { settle(null, "unavailable"); });
|
|
138
|
+
}
|
|
139
|
+
`;
|