@zq-silk/yui 2.2.0 → 2.3.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 +4 -2
- package/dist/cli/commandCatalog.js +8 -3
- package/dist/cli/interactionPolicy.js +4 -0
- package/dist/commands/taskCommands.js +5 -7
- package/dist/commands/taskIntegrationCommands.js +5 -16
- package/dist/controlPlaneCli.js +28 -21
- package/dist/controller/clientRuntime.js +3 -2
- package/dist/integration/gitIntegrationService.js +8 -67
- package/dist/job/jobCleanupInspection.js +67 -0
- package/dist/repository/gitWorkspace.js +41 -12
- package/dist/repository/taskWorkspaceCoordinator.js +72 -145
- package/dist/repository/taskWorkspacePreparer.js +39 -43
- package/dist/repository/workspaceCleanupInspection.js +88 -7
- package/dist/resources/resourceRegistrar.js +16 -10
- package/dist/runtime/integrationRuntimeIsolation.js +21 -0
- package/dist/runtime/taskRuntimeIsolation.js +91 -21
- package/dist/task/archiveDiagnostics.js +26 -8
- package/dist/task/archivePreflight.js +26 -9
- package/dist/task/leaderArchive.js +9 -15
- package/dist/web/assets/client/api.js +8 -0
- package/dist/web/assets/client/app.js +10 -6
- package/dist/web/assets/client/components.js +95 -36
- package/dist/web/assets/client/detail.js +110 -64
- package/dist/web/assets/client/dock.js +95 -11
- package/dist/web/assets/client/evidence.js +286 -94
- package/dist/web/assets/client/i18n.js +178 -62
- package/dist/web/assets/client/overview.js +9 -13
- package/dist/web/assets/client/records.js +142 -7
- package/dist/web/assets/client/sections.js +234 -198
- package/dist/web/assets/icons.js +4 -0
- package/dist/web/assets/styles/components.js +64 -17
- package/dist/web/assets/styles/responsive.js +7 -5
- package/dist/web/assets/styles/tokens.js +1 -1
- package/dist/web/assets/styles/views.js +48 -31
- package/dist/web/webServer.js +12 -1
- package/dist/web/webTaskSurface.js +8 -1
- package/docs/observability/README.md +13 -3
- package/docs/observability/README.zh-CN.md +10 -3
- package/docs/release-workflow.md +25 -0
- package/docs/release-workflow.zh-CN.md +19 -0
- package/docs/task-delivery.md +36 -8
- package/docs/task-delivery.zh-CN.md +24 -7
- package/i18n/README.zh-CN.md +3 -2
- package/package.json +1 -1
- package/skills/yui-leader/references/execution.md +2 -1
- package/skills/yui-leader/references/integration.md +20 -0
- package/skills/yui-operator/SKILL.md +4 -0
- package/skills/yui-reviewer/SKILL.md +3 -0
- package/skills/yui-runtime/SKILL.md +4 -0
- package/skills/yui-runtime/references/publication.md +20 -4
- package/skills/yui-worker/SKILL.md +3 -0
|
@@ -1,72 +1,42 @@
|
|
|
1
1
|
export const SECTIONS_SCRIPT = String.raw `
|
|
2
|
-
//
|
|
2
|
+
// The Runtime and Records tabs. Runtime is what is live now (Roles and their
|
|
3
|
+
// Sessions, open execution, open operations, usage, diagnostics); Records is
|
|
4
|
+
// what happened (a paged timeline of decisions, milestones, executions and
|
|
5
|
+
// questions) plus Task facts. Every #detail-* anchor stays addressable via
|
|
6
|
+
// ?section=.
|
|
3
7
|
import { h, icon, clear } from "/assets/js/dom.js";
|
|
4
8
|
import { formatDateTime } from "/assets/js/format.js";
|
|
5
9
|
import {
|
|
6
|
-
|
|
7
|
-
kv, richText,
|
|
10
|
+
card, listCard, filterChips, disclosure, emptyState, note, button, statusBadge, label, tone, dot, chip, mono,
|
|
11
|
+
kv, richText, runCard, runBody, roleCard, timeTag, observabilityMetricCard
|
|
8
12
|
} from "/assets/js/components.js";
|
|
9
|
-
import {
|
|
10
|
-
|
|
13
|
+
import {
|
|
14
|
+
entriesOf, totalOf, summaryFields, exactRow, lazyRow, recordCard, familyState, loadFamily
|
|
15
|
+
} from "/assets/js/records.js";
|
|
11
16
|
import { controlForm, titleForm } from "/assets/js/forms.js";
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// --- Work -------------------------------------------------------------------------
|
|
18
|
-
export function renderWork(panel, data, t, locale, ctx) {
|
|
19
|
-
const core = data.core;
|
|
20
|
-
const items = entriesOf(core, "work-item");
|
|
21
|
-
const titles = {};
|
|
22
|
-
valuesOf(core, "work-item").forEach(function (item) { titles[item.id] = item.title; });
|
|
23
|
-
const rank = function (entry) { return entry.omitted ? 1 : entry.value.status === "open" ? 0 : 2; };
|
|
24
|
-
const sorted = items.slice().sort(function (a, b) {
|
|
25
|
-
return rank(a) - rank(b) || Date.parse((b.value && b.value.updatedAt) || 0) - Date.parse((a.value && a.value.updatedAt) || 0);
|
|
26
|
-
});
|
|
27
|
-
const work = h("section.section", { id: "detail-work" }, sectionTitle(t("work.title"), items.length));
|
|
28
|
-
if (!sorted.length) work.append(emptyState(omittedNote(core, t), "layers"));
|
|
29
|
-
const list = h("div.stack");
|
|
30
|
-
sorted.forEach(function (entry) {
|
|
31
|
-
list.append(entry.omitted ? recordCard(entry, data.task.id, t, ctx) : workItemCard(entry.value, t, locale, titles));
|
|
32
|
-
});
|
|
33
|
-
work.append(list);
|
|
34
|
-
panel.append(work);
|
|
35
|
-
|
|
36
|
-
const results = h("section.section", { id: "detail-results" }, sectionTitle(t("evidence.title")));
|
|
37
|
-
const resultsBody = h("div.card-grid.card-grid-1");
|
|
38
|
-
renderEvidence(resultsBody, data, t, locale, ctx);
|
|
39
|
-
results.append(resultsBody);
|
|
40
|
-
panel.append(results);
|
|
41
|
-
|
|
42
|
-
const reviews = entriesOf(core, "review-round");
|
|
43
|
-
const reviewSection = h("section.section", { id: "detail-reviews" }, sectionTitle(t("reviews.title"), reviews.length));
|
|
44
|
-
if (!reviews.length) reviewSection.append(emptyState(omittedNote(core, t)));
|
|
45
|
-
else {
|
|
46
|
-
reviewSection.append(note(t("reviews.hint")));
|
|
47
|
-
const reviewList = h("div.stack");
|
|
48
|
-
reviews.forEach(function (entry) {
|
|
49
|
-
reviewList.append(entry.omitted ? recordCard(entry, data.task.id, t, ctx) : reviewCard(entry.value, t, locale));
|
|
50
|
-
});
|
|
51
|
-
reviewSection.append(reviewList);
|
|
52
|
-
}
|
|
53
|
-
panel.append(reviewSection);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// --- Execution --------------------------------------------------------------------
|
|
57
|
-
export function renderExecution(panel, data, t, locale, ctx) {
|
|
18
|
+
// --- Runtime ----------------------------------------------------------------------
|
|
19
|
+
export function renderRuntime(panel, data, t, locale, ctx) {
|
|
58
20
|
const core = data.core;
|
|
59
21
|
const task = data.task;
|
|
22
|
+
const view = data.viewState;
|
|
23
|
+
view.exact = view.exact || {};
|
|
60
24
|
const runtimeRoles = (data.runtime && data.runtime.roles) || [];
|
|
61
|
-
|
|
25
|
+
// Active runs already read exactly (this view or an earlier render).
|
|
26
|
+
const knownRuns = entriesOf(core, "run").map(function (entry) {
|
|
27
|
+
const hit = view.exact["run/" + entry.ref.refId];
|
|
28
|
+
return hit && hit.digest === entry.ref.digest && hit.result ? hit.result.value : null;
|
|
29
|
+
}).filter(Boolean);
|
|
62
30
|
|
|
63
31
|
const roleEntries = entriesOf(core, "role");
|
|
64
|
-
const roles =
|
|
65
|
-
|
|
32
|
+
const roles = listCard({
|
|
33
|
+
id: "detail-roles", title: t("roles.title"), icon: "user", count: totalOf(core, "role"), hint: t("roles.hint"),
|
|
34
|
+
actions: h("div.chip-row", { dataset: { slot: "session-counts" } })
|
|
35
|
+
});
|
|
66
36
|
roleEntries.forEach(function (entry) {
|
|
67
|
-
if (entry.omitted) {
|
|
37
|
+
if (entry.omitted) { roles.list.append(recordCard(entry, task.id, t, ctx)); return; }
|
|
68
38
|
const role = entry.value;
|
|
69
|
-
const active =
|
|
39
|
+
const active = knownRuns.find(function (run) { return run.roleName === role.name && run.status === "active"; });
|
|
70
40
|
const runtimeRole = runtimeRoles.find(function (item) { return item.name === role.name; });
|
|
71
41
|
const element = roleCard(role, t, locale, {
|
|
72
42
|
runtimeRole: runtimeRole,
|
|
@@ -74,162 +44,228 @@ export function renderExecution(panel, data, t, locale, ctx) {
|
|
|
74
44
|
launchDrift: active && active.effective && active.effective.sourceDesiredRevision !== role.launchRevision,
|
|
75
45
|
onOpen: task.status === "archived" ? null : ctx.openSession
|
|
76
46
|
});
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
47
|
+
const head = element.querySelector(".role-head");
|
|
48
|
+
head.append(h("span", { dataset: { roleStatus: role.name } }));
|
|
49
|
+
head.after(h("div.role-session", { dataset: { roleSession: role.name } }));
|
|
50
|
+
roles.list.append(element);
|
|
81
51
|
});
|
|
82
|
-
if (!roleEntries.length)
|
|
83
|
-
roles.append(roleGrid);
|
|
52
|
+
if (!roleEntries.length) roles.list.append(emptyState(t("records.none")));
|
|
84
53
|
panel.append(roles);
|
|
85
54
|
|
|
86
55
|
const redirect = disclosure(t("control.title"), "control", { className: "card-disclosure", meta: t("control.meta") });
|
|
56
|
+
redirect.id = "detail-control";
|
|
87
57
|
redirect.body.append(note(t("control.help")), controlForm(task, t, ctx, roleEntries.map(function (entry) { return entry.ref.refId; })));
|
|
88
|
-
panel.append(
|
|
58
|
+
panel.append(redirect);
|
|
89
59
|
|
|
90
60
|
const runEntries = entriesOf(core, "run");
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const visible = runEntries.filter(function (entry) { return filter === "all" || (!entry.omitted && entry.value.status === filter); });
|
|
99
|
-
if (!visible.length) { list.append(emptyState(omittedNote(core, t), "pulse")); return; }
|
|
100
|
-
pagedList(list, visible, 12, function (entry) {
|
|
101
|
-
return entry.omitted ? recordCard(entry, task.id, t, ctx) : runCard({ ...entry.value, execution: entry.execution }, t, locale);
|
|
102
|
-
}, t);
|
|
103
|
-
}
|
|
104
|
-
filters.forEach(function (status) {
|
|
105
|
-
const count = status === "all" ? runEntries.length : runs.filter(function (run) { return run.status === status; }).length;
|
|
106
|
-
if (status !== "all" && !count) return;
|
|
107
|
-
filterRow.append(h("button.chip-tab", {
|
|
108
|
-
type: "button", "aria-pressed": String(status === filter), dataset: { status },
|
|
109
|
-
onclick: function () {
|
|
110
|
-
filter = status;
|
|
111
|
-
filterRow.querySelectorAll(".chip-tab").forEach(function (b) { b.setAttribute("aria-pressed", String(b.dataset.status === status)); });
|
|
112
|
-
draw();
|
|
113
|
-
}
|
|
114
|
-
}, status === "all" ? t("filter.all") : label(t, "run", status), h("span.count", null, String(count))));
|
|
61
|
+
const runTotal = totalOf(core, "run");
|
|
62
|
+
const runList = listCard({ id: "detail-exec", title: t("runs.title"), icon: "pulse", count: runTotal || null, hint: t("runs.hint") });
|
|
63
|
+
if (!runEntries.length) runList.list.append(emptyState(t("runs.none"), "pulse"));
|
|
64
|
+
runEntries.forEach(function (entry) {
|
|
65
|
+
runList.list.append(exactRow(entry, task.id, t, ctx, view.exact, function (value, result) {
|
|
66
|
+
return runCard({ ...value, execution: (result && result.execution) || entry.execution }, t, locale);
|
|
67
|
+
}));
|
|
115
68
|
});
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
panel.append(runSection);
|
|
69
|
+
if (runTotal > runEntries.length) runList.list.append(note(t("runs.more").replace("{count}", String(runTotal - runEntries.length))));
|
|
70
|
+
panel.append(runList);
|
|
119
71
|
|
|
120
72
|
const jobs = entriesOf(core, "job");
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
73
|
+
const jobTotal = totalOf(core, "job");
|
|
74
|
+
const jobList = listCard({
|
|
75
|
+
id: "detail-operations", title: t("jobs.title"), icon: "refresh", count: jobTotal || null,
|
|
76
|
+
hint: jobTotal ? t("jobs.hint") : null
|
|
77
|
+
});
|
|
78
|
+
if (!jobs.length) jobList.list.append(emptyState(t("jobs.none")));
|
|
79
|
+
jobs.forEach(function (entry) {
|
|
80
|
+
jobList.list.append(exactRow(entry, task.id, t, ctx, view.exact, function (job) {
|
|
81
|
+
return h("article.record", null,
|
|
130
82
|
h("header.record-head", null, dot(tone("job", job.status)), mono(task.id + "/" + job.id), h("span.spacer"),
|
|
131
83
|
statusBadge(t, "job", "job", job.status)),
|
|
132
84
|
h("p.small", null, t("job." + job.status + ".note", t("job.terminal.note"))),
|
|
133
85
|
h("p.faint.small", null, (job.steps || []).map(function (step) { return step.name; }).join(" → ")),
|
|
134
|
-
timeTag(job.terminalAt || job.startedAt || job.createdAt, locale, t))
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
panel.append(jobSection);
|
|
86
|
+
timeTag(job.terminalAt || job.startedAt || job.createdAt, locale, t));
|
|
87
|
+
}));
|
|
88
|
+
});
|
|
89
|
+
panel.append(jobList);
|
|
139
90
|
|
|
140
|
-
const usage =
|
|
91
|
+
const usage = card({ id: "detail-usage", title: t("usage.title"), icon: "clock" });
|
|
141
92
|
const usageBody = h("div", { dataset: { slot: "usage" } });
|
|
142
93
|
const metrics = data.runtime && observabilityMetricCard(data.runtime.observability, t);
|
|
143
94
|
usageBody.append(metrics || emptyState(t("usage.unavailable")));
|
|
144
|
-
usage.append(usageBody);
|
|
95
|
+
usage.body.append(usageBody);
|
|
145
96
|
panel.append(usage);
|
|
146
97
|
|
|
147
|
-
const
|
|
148
|
-
|
|
98
|
+
const diagnostics = disclosure(t("diagnostics.title"), "diagnostics", { className: "card-disclosure", meta: t("diagnostics.meta") });
|
|
99
|
+
diagnostics.id = "detail-diagnostics";
|
|
100
|
+
diagnostics.body.append(note(t("observation.help")));
|
|
149
101
|
(core.observations || []).forEach(function (observation) {
|
|
150
|
-
|
|
102
|
+
diagnostics.body.append(h("p.small.mono-line", null,
|
|
151
103
|
observation.source + " · " + observation.status + " · " + observation.coverage + " · " + observation.observedAt));
|
|
152
104
|
});
|
|
153
|
-
|
|
154
|
-
|
|
105
|
+
diagnostics.body.append(h("div.stack", { dataset: { slot: "session-facts" } }),
|
|
106
|
+
h("p.faint.small", { dataset: { slot: "runtime-status" } }), h("pre.code-block", { dataset: { slot: "runtime-raw" } }));
|
|
107
|
+
panel.append(diagnostics);
|
|
155
108
|
}
|
|
156
109
|
|
|
157
|
-
// ---
|
|
158
|
-
|
|
110
|
+
// --- Records ----------------------------------------------------------------------
|
|
111
|
+
// One timeline over four Context families, read as paged summaries. Messages
|
|
112
|
+
// live in the Discussion dock. Ascending families are read in full (bounded);
|
|
113
|
+
// descending runs page on request, and older rows of other families are held
|
|
114
|
+
// back until the run pages reach them so the merged order stays honest.
|
|
115
|
+
const TIMELINE = [
|
|
116
|
+
{ kind: "decision", store: "task-decision", icon: "check", ascending: true },
|
|
117
|
+
{ kind: "milestone", store: "task-milestone", icon: "flag", ascending: true },
|
|
118
|
+
{ kind: "run", store: "run", icon: "pulse", ascending: false },
|
|
119
|
+
{ kind: "question", store: "input-request", icon: "inbox", ascending: true }
|
|
120
|
+
];
|
|
121
|
+
const FULL_PAGES = 5;
|
|
122
|
+
|
|
123
|
+
function at(item) { return Date.parse(item.createdAt || 0) || 0; }
|
|
124
|
+
|
|
125
|
+
export function renderRecords(panel, data, t, locale, ctx) {
|
|
159
126
|
const core = data.core;
|
|
160
127
|
const task = data.task;
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
128
|
+
const view = data.viewState;
|
|
129
|
+
view.panels = view.panels || {};
|
|
130
|
+
view.panels.records = panel;
|
|
131
|
+
view.exact = view.exact || {};
|
|
132
|
+
view.openRows = view.openRows || {};
|
|
133
|
+
if (!TIMELINE.some(function (family) { return family.kind === view.recordsFilter; })) view.recordsFilter = "all";
|
|
134
|
+
// Values the current snapshot already carries need no read.
|
|
135
|
+
const known = {};
|
|
136
|
+
core.records.forEach(function (entry) { if (!entry.omitted) known[entry.ref.store + "/" + entry.ref.refId] = entry; });
|
|
137
|
+
|
|
138
|
+
const history = card({
|
|
139
|
+
id: "detail-history", title: t("history.title"), icon: "history", hint: t("history.hint"),
|
|
140
|
+
actions: filterChips(t("history.filter"), [{ value: "all", label: t("history.all") }].concat(TIMELINE.map(function (family) {
|
|
141
|
+
return { value: family.kind, label: t("history." + family.kind) };
|
|
142
|
+
})), view.recordsFilter, function (value) { view.recordsFilter = value; draw(); })
|
|
143
|
+
});
|
|
174
144
|
const list = h("ol.timeline");
|
|
145
|
+
const foot = h("div.list-foot");
|
|
146
|
+
history.body.append(list, foot);
|
|
147
|
+
|
|
148
|
+
function families() {
|
|
149
|
+
return TIMELINE.filter(function (family) { return view.recordsFilter === "all" || view.recordsFilter === family.kind; });
|
|
150
|
+
}
|
|
175
151
|
function draw() {
|
|
176
152
|
clear(list);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
153
|
+
clear(foot);
|
|
154
|
+
history.querySelectorAll(".chip-tab").forEach(function (chipButton) {
|
|
155
|
+
chipButton.setAttribute("aria-pressed", String(chipButton.dataset.filter === view.recordsFilter));
|
|
156
|
+
});
|
|
157
|
+
const selected = families();
|
|
158
|
+
const states = selected.map(function (family) { return { family: family, state: familyState(view, family.store) }; });
|
|
159
|
+
let horizon = -Infinity;
|
|
160
|
+
states.forEach(function (row) {
|
|
161
|
+
if (row.state.nextCursor && !row.family.ascending && row.state.items.length) {
|
|
162
|
+
horizon = Math.max(horizon, Math.min.apply(null, row.state.items.map(at)));
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
const items = [];
|
|
166
|
+
states.forEach(function (row) {
|
|
167
|
+
row.state.items.forEach(function (item) { if (at(item) >= horizon) items.push({ family: row.family, item: item }); });
|
|
168
|
+
});
|
|
169
|
+
items.sort(function (a, b) { return at(b.item) - at(a.item); });
|
|
170
|
+
const loading = states.some(function (row) { return row.state.pending && !row.state.pages; });
|
|
171
|
+
const loaded = states.every(function (row) { return row.state.pages || row.state.error; });
|
|
172
|
+
states.forEach(function (row) {
|
|
173
|
+
if (row.state.error) list.append(h("li.timeline-empty", null, note(t("history." + row.family.kind) + " · " + t("records.unavailable") + " " + row.state.error, "bad")));
|
|
174
|
+
});
|
|
175
|
+
if (!items.length) {
|
|
176
|
+
list.append(h("li.timeline-empty", null, emptyState(loading || !loaded ? t("history.loading") : t("records.none"), "history")));
|
|
177
|
+
}
|
|
178
|
+
items.forEach(function (row) { list.append(timelineItem(row.family, row.item)); });
|
|
179
|
+
const total = states.reduce(function (sum, row) { return sum + (row.state.total || 0); }, 0);
|
|
180
|
+
if (!loaded) return;
|
|
181
|
+
foot.append(h("span.faint.small", null, t("history.loaded").replace("{shown}", String(items.length)).replace("{total}", String(total))));
|
|
182
|
+
const stale = states.some(function (row) { return row.state.key && row.state.key !== core.coreCursor; });
|
|
183
|
+
if (stale) {
|
|
184
|
+
foot.append(button(t("history.stale"), { icon: "refresh", variant: "ghost", onClick: function () { show(true); } }));
|
|
185
|
+
}
|
|
186
|
+
if (states.some(function (row) { return row.state.nextCursor; })) {
|
|
187
|
+
const more = button(t("history.more"), { icon: "chevron", variant: "ghost" });
|
|
188
|
+
more.addEventListener("click", function () {
|
|
189
|
+
more.disabled = true;
|
|
190
|
+
Promise.all(states.filter(function (row) { return row.state.nextCursor; }).map(function (row) {
|
|
191
|
+
return loadFamily(view, task.id, row.family.store, row.state.key, ctx, { more: true, limit: 40 });
|
|
192
|
+
})).then(refresh);
|
|
193
|
+
});
|
|
194
|
+
foot.append(more);
|
|
195
|
+
}
|
|
180
196
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
|
|
198
|
+
function timelineItem(family, item) {
|
|
199
|
+
const fields = summaryFields(item.summary);
|
|
200
|
+
const entry = known[item.ref.store + "/" + item.ref.refId];
|
|
201
|
+
const value = entry && entry.ref.digest === item.ref.digest ? entry.value : null;
|
|
202
|
+
const element = h("li.timeline-item.kind-" + family.kind);
|
|
203
|
+
element.append(h("span.timeline-mark", { "aria-hidden": "true" }, icon(family.icon, "icon-sm")));
|
|
204
|
+
const body = h("div.timeline-body");
|
|
205
|
+
let title;
|
|
206
|
+
if (family.kind === "run") title = [item.roleName || t("store.run"), item.purpose ? label(t, "run.purpose", item.purpose) : null].filter(Boolean).join(" · ");
|
|
207
|
+
else if (family.kind === "question") title = fields.question || fields.title || item.ref.refId;
|
|
208
|
+
else title = fields.title || item.ref.refId;
|
|
209
|
+
const status = family.kind === "decision" ? statusBadge(t, "decision", "decision", item.status)
|
|
210
|
+
: family.kind === "run" ? statusBadge(t, "run", "run", item.status)
|
|
211
|
+
: family.kind === "question" ? statusBadge(t, "input", "inputStatus", item.status) : null;
|
|
212
|
+
const head = h("div.timeline-summary", null, h("header.timeline-head", null, h("strong.timeline-title", null, title),
|
|
213
|
+
family.kind === "run" ? mono(item.ref.refId) : null,
|
|
214
|
+
item.workItemId ? chip(item.workItemId) : null,
|
|
215
|
+
item.history === "retired" ? chip(t("history.retired")) : null,
|
|
216
|
+
item.status ? status : null, h("span.spacer"), timeTag(item.createdAt, locale, t)));
|
|
217
|
+
const preview = value && family.kind === "decision" ? value.rationale : fields.summary !== title ? fields.summary : null;
|
|
218
|
+
if (preview) head.append(h("p.small.muted.timeline-preview", null, preview));
|
|
219
|
+
// The row opens in place; its exact record is read on first open.
|
|
220
|
+
body.append(lazyRow(item, task.id, t, ctx, { cache: view.exact, openRows: view.openRows, head: head, render: function (exact, result) {
|
|
221
|
+
if (family.kind === "run") return h("div.stack", null, runBody({ ...exact, execution: result && result.execution }, t, locale));
|
|
222
|
+
if (family.kind === "decision") return h("div.stack", null,
|
|
223
|
+
exact.rationale ? richText(t("record.rationale"), exact.rationale, t, { muted: true }) : null,
|
|
224
|
+
exact.supersededReason ? richText(t("history.superseded"), exact.supersededReason, t, { muted: true }) : null);
|
|
225
|
+
if (family.kind === "milestone") return h("div.stack", null, exact.summary ? richText(null, exact.summary, t) : null);
|
|
226
|
+
if (family.kind === "question") {
|
|
227
|
+
const answer = exact.resolution && exact.resolution.answer;
|
|
228
|
+
return h("div.stack", null, exact.question ? richText(null, exact.question, t) : null,
|
|
229
|
+
answer ? richText(t("history.answer") + " · " + label(t, "answeredBy", exact.resolution.answeredBy),
|
|
230
|
+
answer.text || answer.choiceKey, t, { muted: true }) : null,
|
|
231
|
+
exact.cancellation ? note(exact.cancellation.reason) : null);
|
|
189
232
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
panel.append(section);
|
|
196
|
-
}
|
|
233
|
+
return null;
|
|
234
|
+
} }));
|
|
235
|
+
element.append(body);
|
|
236
|
+
return element;
|
|
237
|
+
}
|
|
197
238
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (entry.omitted) { item.append(recordCard(entry, taskId, t, ctx, { compact: true })); return item; }
|
|
203
|
-
const value = entry.value;
|
|
204
|
-
const body = h("div.timeline-body");
|
|
205
|
-
if (event.kind === "message") {
|
|
206
|
-
const author = (value.author && value.author.roleName) || label(t, "author", (value.author && value.author.type) || "system");
|
|
207
|
-
body.append(h("header.timeline-head", null, h("strong", null, author),
|
|
208
|
-
value.kind ? chip(label(t, "messageKind", value.kind)) : null,
|
|
209
|
-
value.intent ? chip(label(t, "intent", value.intent)) : null,
|
|
210
|
-
h("span.spacer"), timeTag(value.createdAt, locale, t)));
|
|
211
|
-
body.append(richText(null, value.body || "", t, { threshold: 480 }));
|
|
212
|
-
if (value.resultRef) body.append(h("p.faint.small", null, t("history.resultOf") + " ", mono(value.resultRef.runId)));
|
|
213
|
-
} else if (event.kind === "decision") {
|
|
214
|
-
body.append(h("header.timeline-head", null, h("strong", null, value.title),
|
|
215
|
-
value.status ? statusBadge(t, "decision", "decision", value.status) : null, h("span.spacer"), timeTag(value.createdAt, locale, t)));
|
|
216
|
-
if (value.rationale) body.append(richText(null, value.rationale, t, { muted: true }));
|
|
217
|
-
if (value.supersededReason) body.append(richText(t("history.superseded"), value.supersededReason, t, { muted: true }));
|
|
218
|
-
} else {
|
|
219
|
-
body.append(h("header.timeline-head", null, h("strong", null, value.title), h("span.spacer"), timeTag(value.createdAt, locale, t)));
|
|
220
|
-
if (value.summary) body.append(richText(null, value.summary, t, { muted: true }));
|
|
239
|
+
// Reads redraw whichever Records panel is current.
|
|
240
|
+
function refresh() {
|
|
241
|
+
const current = view.panels.records;
|
|
242
|
+
if (current && current.isConnected) current.redraw();
|
|
221
243
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
244
|
+
function loadFull(family, key) {
|
|
245
|
+
return loadFamily(view, task.id, family.store, key, ctx, { limit: 40 }).then(function (state) {
|
|
246
|
+
if (!family.ascending || !state.nextCursor || state.pages >= FULL_PAGES || state.error) return state;
|
|
247
|
+
return loadFamily(view, task.id, family.store, key, ctx, { more: true, limit: 40 }).then(function () { return loadFull(family, key); });
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
// A stale first page re-reads silently; once the user paged further, the
|
|
251
|
+
// read waits for an explicit refresh so their position is kept.
|
|
252
|
+
function show(force) {
|
|
253
|
+
const key = core.coreCursor;
|
|
254
|
+
TIMELINE.forEach(function (family) {
|
|
255
|
+
const state = familyState(view, family.store);
|
|
256
|
+
if (state.pending || state.key === key || (!force && state.errorKey === key)) return;
|
|
257
|
+
if (!force && state.key && !family.ascending && state.pages > 1) return;
|
|
258
|
+
loadFull(family, key).then(refresh);
|
|
259
|
+
});
|
|
260
|
+
refresh();
|
|
261
|
+
}
|
|
262
|
+
panel.redraw = draw;
|
|
263
|
+
panel.onShow = function () { show(false); };
|
|
264
|
+
draw();
|
|
265
|
+
panel.append(history);
|
|
227
266
|
|
|
228
|
-
// ---
|
|
229
|
-
|
|
230
|
-
const task = data.task;
|
|
231
|
-
const core = data.core;
|
|
232
|
-
const facts = card({ title: t("details.facts"), icon: "file" });
|
|
267
|
+
// --- Task information --------------------------------------------------------
|
|
268
|
+
const facts = card({ id: "detail-facts", title: t("details.facts"), icon: "info" });
|
|
233
269
|
facts.body.append(kv([
|
|
234
270
|
[t("details.id"), mono(task.id)],
|
|
235
271
|
[t("details.status"), statusBadge(t, "task", "status", task.status)],
|
|
@@ -240,37 +276,37 @@ export function renderDetails(panel, data, t, locale, ctx) {
|
|
|
240
276
|
[t("details.updated"), formatDateTime(task.updatedAt, locale)],
|
|
241
277
|
[t("details.workspace"), task.cwd ? mono(task.cwd) : null]
|
|
242
278
|
]));
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const grid = h("div.card-grid");
|
|
246
|
-
grid.append(facts, edit);
|
|
247
|
-
panel.append(h("section.section", { id: "detail-facts" }, grid));
|
|
279
|
+
facts.body.append(h("div.prose-block", null, h("h4.prose-label", null, t("details.title")), titleForm(task, t, ctx)));
|
|
280
|
+
panel.append(facts);
|
|
248
281
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
282
|
+
// --- Advanced -------------------------------------------------------------------
|
|
283
|
+
const advanced = disclosure(t("advanced.title"), "advanced", { className: "card-disclosure", meta: t("advanced.meta") });
|
|
284
|
+
advanced.id = "detail-advanced";
|
|
285
|
+
advanced.body.append(h("h4.sub-head", null, t("details.context")), h("pre.code-block", null, JSON.stringify({
|
|
286
|
+
coreCursor: core.coreCursor, throughCursor: core.throughCursor, count: core.count, collections: core.collections, omitted: core.omitted
|
|
252
287
|
}, null, 2)), note(t("details.contextHelp")));
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const panels = disclosure(t("panels.title"), "panels", { className: "card-disclosure" });
|
|
288
|
+
const panels = h("div.stack");
|
|
289
|
+
advanced.body.append(h("h4.sub-head", null, t("panels.title")), panels);
|
|
256
290
|
let loading = false;
|
|
257
|
-
|
|
258
|
-
|
|
291
|
+
let loadedPanels = false;
|
|
292
|
+
advanced.addEventListener("toggle", async function () {
|
|
293
|
+
if (!advanced.open || loading || loadedPanels) return;
|
|
259
294
|
loading = true;
|
|
260
|
-
clear(panels
|
|
261
|
-
panels.
|
|
295
|
+
clear(panels);
|
|
296
|
+
panels.append(h("p.faint", null, t("panels.reading")));
|
|
262
297
|
try {
|
|
263
298
|
const current = await ctx.panels(task.id);
|
|
264
|
-
clear(panels
|
|
265
|
-
panels.
|
|
266
|
-
if (!current.panels.length) panels.
|
|
267
|
-
current.panels.forEach(function (item) { panels.
|
|
299
|
+
clear(panels);
|
|
300
|
+
panels.append(h("p.faint.small", null, t("panels.observed") + " " + formatDateTime(current.observedAt, locale)));
|
|
301
|
+
if (!current.panels.length) panels.append(emptyState(t("panels.none")));
|
|
302
|
+
current.panels.forEach(function (item) { panels.append(panelCard(item, task, t, ctx)); });
|
|
303
|
+
loadedPanels = true;
|
|
268
304
|
} catch (error) {
|
|
269
|
-
clear(panels
|
|
270
|
-
panels.
|
|
305
|
+
clear(panels);
|
|
306
|
+
panels.append(note(t("panels.unavailable") + " " + error.message, "bad"));
|
|
271
307
|
} finally { loading = false; }
|
|
272
308
|
});
|
|
273
|
-
panel.append(
|
|
309
|
+
panel.append(advanced);
|
|
274
310
|
}
|
|
275
311
|
|
|
276
312
|
function panelCard(item, task, t, ctx) {
|
package/dist/web/assets/icons.js
CHANGED
|
@@ -21,6 +21,10 @@ export const ICON_PATHS = Object.freeze({
|
|
|
21
21
|
file: ["M7 3h7l5 5v12a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z", "M14 3v5h5"],
|
|
22
22
|
copy: ["M9 9h10a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V10a1 1 0 0 1 1-1z", "M5 15H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1"],
|
|
23
23
|
layers: ["M12 3l9 5-9 5-9-5z", "M3 13l9 5 9-5"],
|
|
24
|
+
package: ["M21 8l-9-5-9 5v8l9 5 9-5z", "M3 8l9 5 9-5", "M12 13v8"],
|
|
25
|
+
merge: ["M18 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6z", "M6 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6z", "M6 21V9a9 9 0 0 0 9 9"],
|
|
26
|
+
folder: ["M3 7a1 1 0 0 1 1-1h5l2 2h9a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1z"],
|
|
27
|
+
info: ["M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18z", "M12 11v5", "M12 8h.01"],
|
|
24
28
|
pulse: ["M3 12h4l3-7 4 14 3-7h4"],
|
|
25
29
|
history: ["M3 12a9 9 0 1 0 3-6.7", "M3 4v4h4", "M12 8v4l3 2"],
|
|
26
30
|
dots: ["M5 12h.01", "M12 12h.01", "M19 12h.01"],
|