@nestos/kanban 0.0.0-stage → 0.5.1

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.
Files changed (99) hide show
  1. package/README.md +121 -2
  2. package/dist/activity-C6xDM-cg.d.cts +232 -0
  3. package/dist/activity-Dw8DOw0L.d.ts +232 -0
  4. package/dist/activity.cjs +288 -0
  5. package/dist/activity.cjs.map +1 -0
  6. package/dist/activity.d.cts +34 -0
  7. package/dist/activity.d.ts +34 -0
  8. package/dist/activity.js +288 -0
  9. package/dist/activity.js.map +1 -0
  10. package/dist/chunk-2YFPV5GF.cjs +16 -0
  11. package/dist/chunk-2YFPV5GF.cjs.map +1 -0
  12. package/dist/chunk-4KF7KUAI.cjs +221 -0
  13. package/dist/chunk-4KF7KUAI.cjs.map +1 -0
  14. package/dist/chunk-4XF6LQSU.cjs +9 -0
  15. package/dist/chunk-4XF6LQSU.cjs.map +1 -0
  16. package/dist/chunk-5DLZWW5Y.js +148 -0
  17. package/dist/chunk-5DLZWW5Y.js.map +1 -0
  18. package/dist/chunk-5XKOCKY2.js +58 -0
  19. package/dist/chunk-5XKOCKY2.js.map +1 -0
  20. package/dist/chunk-6MN3H3YF.cjs +183 -0
  21. package/dist/chunk-6MN3H3YF.cjs.map +1 -0
  22. package/dist/chunk-ATUFFNLV.js +221 -0
  23. package/dist/chunk-ATUFFNLV.js.map +1 -0
  24. package/dist/chunk-DFTOTBGF.js +439 -0
  25. package/dist/chunk-DFTOTBGF.js.map +1 -0
  26. package/dist/chunk-G6FCDLDF.js +16 -0
  27. package/dist/chunk-G6FCDLDF.js.map +1 -0
  28. package/dist/chunk-IUI5CI2S.cjs +439 -0
  29. package/dist/chunk-IUI5CI2S.cjs.map +1 -0
  30. package/dist/chunk-KYNYPKKH.cjs +71 -0
  31. package/dist/chunk-KYNYPKKH.cjs.map +1 -0
  32. package/dist/chunk-L44TM4EE.cjs +1498 -0
  33. package/dist/chunk-L44TM4EE.cjs.map +1 -0
  34. package/dist/chunk-LSE6JG5I.cjs +20 -0
  35. package/dist/chunk-LSE6JG5I.cjs.map +1 -0
  36. package/dist/chunk-O4OGAAV4.js +71 -0
  37. package/dist/chunk-O4OGAAV4.js.map +1 -0
  38. package/dist/chunk-OHQTKGQN.js +1498 -0
  39. package/dist/chunk-OHQTKGQN.js.map +1 -0
  40. package/dist/chunk-PSKQAFDF.cjs +58 -0
  41. package/dist/chunk-PSKQAFDF.cjs.map +1 -0
  42. package/dist/chunk-QTMTBEBA.js +20 -0
  43. package/dist/chunk-QTMTBEBA.js.map +1 -0
  44. package/dist/chunk-RQP77ZTY.js +9 -0
  45. package/dist/chunk-RQP77ZTY.js.map +1 -0
  46. package/dist/chunk-T7P5EIC4.cjs +148 -0
  47. package/dist/chunk-T7P5EIC4.cjs.map +1 -0
  48. package/dist/chunk-TB2RZGDV.js +183 -0
  49. package/dist/chunk-TB2RZGDV.js.map +1 -0
  50. package/dist/content-CAIp1njn.d.cts +35 -0
  51. package/dist/content-CGMJdyH_.d.ts +35 -0
  52. package/dist/contracts/index.cjs +21 -0
  53. package/dist/contracts/index.cjs.map +1 -0
  54. package/dist/contracts/index.d.cts +4 -0
  55. package/dist/contracts/index.d.ts +4 -0
  56. package/dist/contracts/index.js +21 -0
  57. package/dist/contracts/index.js.map +1 -0
  58. package/dist/discovery-DxpirOz3.d.cts +61 -0
  59. package/dist/discovery-gSqWpuRm.d.ts +61 -0
  60. package/dist/discovery.cjs +379 -0
  61. package/dist/discovery.cjs.map +1 -0
  62. package/dist/discovery.d.cts +22 -0
  63. package/dist/discovery.d.ts +22 -0
  64. package/dist/discovery.js +379 -0
  65. package/dist/discovery.js.map +1 -0
  66. package/dist/execution.cjs +368 -0
  67. package/dist/execution.cjs.map +1 -0
  68. package/dist/execution.d.cts +89 -0
  69. package/dist/execution.d.ts +89 -0
  70. package/dist/execution.js +368 -0
  71. package/dist/execution.js.map +1 -0
  72. package/dist/index.cjs +76 -0
  73. package/dist/index.cjs.map +1 -0
  74. package/dist/index.d.cts +61 -0
  75. package/dist/index.d.ts +61 -0
  76. package/dist/index.js +76 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/module-BbvwdCDF.d.cts +11 -0
  79. package/dist/module-CWNK2BMj.d.ts +11 -0
  80. package/dist/planning-CcFZYdIL.d.cts +844 -0
  81. package/dist/planning-CcFZYdIL.d.ts +844 -0
  82. package/dist/ports-Bn6ro2FB.d.ts +142 -0
  83. package/dist/ports-CaL6geqR.d.cts +142 -0
  84. package/dist/storage.cjs +289 -0
  85. package/dist/storage.cjs.map +1 -0
  86. package/dist/storage.d.cts +71 -0
  87. package/dist/storage.d.ts +71 -0
  88. package/dist/storage.js +289 -0
  89. package/dist/storage.js.map +1 -0
  90. package/dist/store-Ct0gOAAg.d.ts +28 -0
  91. package/dist/store-q63eSMoT.d.cts +28 -0
  92. package/dist/ui/index.cjs +1961 -0
  93. package/dist/ui/index.cjs.map +1 -0
  94. package/dist/ui/index.d.cts +209 -0
  95. package/dist/ui/index.d.ts +209 -0
  96. package/dist/ui/index.js +1961 -0
  97. package/dist/ui/index.js.map +1 -0
  98. package/dist/ui/kanban.css +194 -0
  99. package/package.json +113 -4
@@ -0,0 +1,1961 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4;
2
+
3
+ var _chunkKYNYPKKHcjs = require('../chunk-KYNYPKKH.cjs');
4
+
5
+
6
+
7
+
8
+ var _chunkPSKQAFDFcjs = require('../chunk-PSKQAFDF.cjs');
9
+
10
+
11
+ var _chunk6MN3H3YFcjs = require('../chunk-6MN3H3YF.cjs');
12
+ require('../chunk-LSE6JG5I.cjs');
13
+
14
+ // src/ui/html.ts
15
+ function escapeHTML(value2) {
16
+ return String(_nullishCoalesce(value2, () => ( ""))).replace(/[&<>"']/g, (character) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[character]);
17
+ }
18
+ var escapeAttr = escapeHTML;
19
+
20
+ // src/ui/task-state.ts
21
+ function taskStatus(ui, task) {
22
+ if (!task) return "";
23
+ return `<span class="kanban__task-state" data-task-phase="${escapeAttr(task.phase)}">${escapeHTML(ui.text("taskPhase." + task.phase))}${task.reason ? `<small>${escapeHTML(taskReason(ui, task.reason))}</small>` : ""}${task.phase === "waiting-start" && task.reason !== "MANUAL_START_REQUIRED" && task.startAt !== void 0 ? `<time data-task-start-at="${task.startAt}" datetime="${new Date(task.startAt).toISOString()}">${escapeHTML(startTimeText(ui, task.startAt))}</time>` : ""}</span>`;
24
+ }
25
+ function taskControls(ui, task, fixAvailable = false) {
26
+ return task.actions.filter((action) => ["run", "pause", "resume", "edit", "cancel"].includes(action) || action === "fix" && fixAvailable).map((action) => {
27
+ const label = ui.text("taskAction." + action), icon = action === "cancel" ? ui.icons.close : ui.icons[action];
28
+ return `<button type="button" data-action="task-${action}" title="${escapeAttr(label)}" aria-label="${escapeAttr(label)}">${_nullishCoalesce(icon, () => ( escapeHTML(label)))}</button>`;
29
+ }).join("");
30
+ }
31
+ function phaseFilter(ui, selected) {
32
+ const phases = ["waiting-start", "analyzing", "queued", "waiting-dependency", "preparing", "running", "pausing", "paused", "resuming", "waiting-input", "error", "review", "completed", "cancelled", "unknown"];
33
+ return `<label>${escapeHTML(ui.text("taskState"))}<select name="phase"><option value="">${escapeHTML(ui.text("all"))}</option>${phases.map((phase) => `<option value="${phase}" ${phase === selected ? "selected" : ""}>${escapeHTML(ui.text("taskPhase." + phase))}</option>`).join("")}</select></label>`;
34
+ }
35
+ function startTimeText(ui, startAt) {
36
+ return ui.text("taskStartAt") + " " + new Date(startAt).toLocaleString() + " \xB7 " + Math.max(0, Math.ceil((startAt - Date.now()) / 1e3)) + "s";
37
+ }
38
+ function updateTaskCountdowns(editor, ui) {
39
+ for (const node of editor.querySelectorAll("[data-task-start-at]")) {
40
+ const at = Number(node.dataset.taskStartAt);
41
+ if (Number.isFinite(at)) node.textContent = startTimeText(ui, at);
42
+ }
43
+ }
44
+ function taskReason(ui, reason) {
45
+ const key = "taskReason." + reason, value2 = ui.text(key);
46
+ return value2 === key || value2 === "kanban." + key ? reason : value2;
47
+ }
48
+
49
+ // src/ui/session-links.ts
50
+ function sessionLink(ui, id, label = id, url) {
51
+ if (!url) return `<button type="button" data-session="${escapeAttr(id)}">${escapeHTML(label)}</button>`;
52
+ return linkPair(ui, url, label, id);
53
+ }
54
+ function linkPair(ui, href, label, sessionId) {
55
+ return `<span class="kanban__session-links"><a href="${escapeAttr(href)}"${sessionId ? ` data-session="${escapeAttr(sessionId)}" title="${escapeAttr(ui.text("openSessionInTab"))}"` : ""}>${escapeHTML(label)}</a></span>`;
56
+ }
57
+
58
+ // src/ui/references.ts
59
+ function referenceMenu(ui, board, entity, actions = "") {
60
+ const reference = _chunkPSKQAFDFcjs.kanbanReference.call(void 0, board, entity);
61
+ const path = `/board/${encodeURIComponent(board.id)}${entity ? entity.kind ? "/@record/" + encodeURIComponent(entity.id) : "/" + encodeURIComponent(entity.id) : ""}`;
62
+ return `<details class="kanban__menu" data-popover><summary aria-label="${escapeAttr(ui.text("referenceActions"))}" title="${escapeAttr(ui.text("referenceActions"))}">${ui.icons.more}</summary>
63
+ <div class="kanban__menu-content"><code class="kanban__menu-reference">${escapeHTML(reference)}</code>${actions}<button type="button" data-action="copy-reference" data-copy="${escapeAttr(reference)}">${escapeHTML(ui.text("copyReference"))}</button>
64
+ <button type="button" data-action="copy-link" data-copy="${escapeAttr(path)}">${escapeHTML(ui.text("copyLink"))}</button></div></details>`;
65
+ }
66
+ async function copyReference(ui, button2) {
67
+ const value2 = _nullishCoalesce(button2.dataset.copy, () => ( ""));
68
+ const text = button2.dataset.action === "copy-link" ? _nullishCoalesce(_optionalChain([ui, 'access', _ => _.referenceUrl, 'optionalCall', _2 => _2(value2)]), () => ( value2)) : value2;
69
+ try {
70
+ await navigator.clipboard.writeText(text);
71
+ if (button2.dataset.action === "copy-resource") {
72
+ button2.title = ui.text("copied");
73
+ button2.setAttribute("aria-label", ui.text("copied"));
74
+ } else button2.textContent = ui.text("copied");
75
+ } catch (e) {
76
+ throw Object.assign(new Error(ui.text("copyFailed")), { code: "COPY_FAILED" });
77
+ }
78
+ }
79
+
80
+ // src/ui/suggestion-field.ts
81
+ function suggestionField(options) {
82
+ return `<div class="kanban__suggest" data-kanban-suggest="${escapeAttr(JSON.stringify(options))}"></div>`;
83
+ }
84
+ function selectedItems(board, ids) {
85
+ return ids.map((id) => ({ id, label: _nullishCoalesce(_optionalChain([board, 'access', _3 => _3.items, 'access', _4 => _4.find, 'call', _5 => _5((item) => item.id === id), 'optionalAccess', _6 => _6.title]), () => ( id)) }));
86
+ }
87
+
88
+ // src/ui/session-view.ts
89
+ function executionBadge(ui, session, sessionUrl) {
90
+ if (!session) return "";
91
+ const resource = _nullishCoalesce(session.resourceId, () => ( session.sessionId));
92
+ return `<span class="kanban__execution" data-execution-state="${escapeAttr(session.state)}" data-stale="${session.stale}"><span>${sessionUrl ? sessionLink(ui, resource, ui.text("executionSession"), sessionUrl(resource)) : escapeHTML(ui.text("executionSession"))} \xB7 ${escapeHTML(ui.text("sessionState." + session.state))}${session.stale ? " \xB7 " + escapeHTML(ui.text("activityStale")) : ""}</span><small>${escapeHTML(activityTime(ui, session))}</small></span>`;
93
+ }
94
+ function activityTime(ui, session) {
95
+ if (session.lastActivityAt === void 0) return ui.text("activityUnknown");
96
+ const minutes = Math.max(0, Math.floor((Date.now() - session.lastActivityAt) / 6e4));
97
+ return ui.text("lastActivity") + " \xB7 " + (minutes < 1 ? ui.text("justNow") : minutes < 60 ? ui.text("minutesAgo").replace("{count}", String(minutes)) : new Date(session.lastActivityAt).toLocaleString());
98
+ }
99
+ function executionSessionView(ui, detail, editable, sessionUrl) {
100
+ const session = detail.current, resource = _nullishCoalesce(_optionalChain([session, 'optionalAccess', _7 => _7.resourceId]), () => ( _optionalChain([session, 'optionalAccess', _8 => _8.sessionId])));
101
+ return `<section class="kanban__execution-detail"><h4>${escapeHTML(ui.text("executionSession"))}</h4><p>${escapeHTML(ui.text("executionSessionHelp"))}</p>
102
+ ${detail.continuation ? `<button type="button" data-action="continue-execution" data-dispatch="${escapeAttr(detail.continuation.previousDispatchId)}">${escapeHTML(ui.text("continueExecution"))}</button>` : ""}
103
+ ${session ? `${executionBadge(ui, session)}${sessionLink(ui, resource, _nullishCoalesce(session.title, () => ( session.sessionId)), _optionalChain([sessionUrl, 'optionalCall', _9 => _9(resource)]))}<small>${escapeHTML(session.sessionId)}</small>${session.observedAt ? `<small>${escapeHTML(ui.text("observedAt"))} \xB7 ${escapeHTML(new Date(session.observedAt).toLocaleString())}</small>` : ""}${session.code ? `<p>${escapeHTML(ui.text("activityReadFailed"))} (${escapeHTML(session.code)})</p>` : ""}` : `<p>${escapeHTML(ui.text("noExecutionSession"))}</p>`}
104
+ ${editable ? `<details><summary>${escapeHTML(ui.text(session ? "changeExecutionSession" : "bindExecutionSession"))}</summary><form data-form="execution-session">${suggestionField({ name: "executionSession", label: "executionSession", kind: "session", manual: true, required: true })}<button>${escapeHTML(ui.text("bindExecutionSession"))}</button><p data-execution-error role="alert" hidden></p></form></details>` : ""}
105
+ </section>`;
106
+ }
107
+ function relatedSessionsView(ui, detail, sessionUrl) {
108
+ const sessions = _nullishCoalesce(detail.sessions, () => ( legacySessions(detail)));
109
+ return `${detail.sessionsCode ? `<p role="alert">${escapeHTML(ui.text("sessionRelationsIncomplete"))} (${escapeHTML(detail.sessionsCode)})</p>` : ""}
110
+ ${sessions.length ? `<ul class="kanban__session-list">${sessions.map((session) => relatedSession(ui, session, sessionUrl)).join("")}</ul>` : `<p>${escapeHTML(ui.text("noRelatedSessions"))}</p>`}`;
111
+ }
112
+ function relatedSession(ui, session, sessionUrl) {
113
+ const resource = _nullishCoalesce(session.resourceId, () => ( session.sessionId)), title = _nullishCoalesce(session.title, () => ( session.sessionId));
114
+ return `<li class="kanban__related-session" data-related-session="${escapeAttr(session.sessionId)}"><div class="kanban__session-heading" title="${escapeAttr(title)}">${sessionLink(ui, resource, title, _optionalChain([sessionUrl, 'optionalCall', _10 => _10(resource)]))}</div>
115
+ <small>${escapeHTML(session.sessionId)}</small>${session.createdAt !== void 0 ? `<small>${escapeHTML(ui.text("sessionCreatedAt"))} \xB7 ${escapeHTML(date(session.createdAt))}</small>` : ""}<p>${session.current ? `<strong>${escapeHTML(ui.text("currentExecution"))}</strong> \xB7 ` : ""}${session.purposes.map((purpose) => escapeHTML(ui.text("sessionPurpose." + purpose))).join(" \xB7 ")} \xB7 ${escapeHTML(ui.text("sessionState." + session.state))}${session.stale ? " \xB7 " + escapeHTML(ui.text("activityStale")) : ""}</p>
116
+ ${session.code ? `<p role="alert">${escapeHTML(ui.text("activityReadFailed"))} (${escapeHTML(session.code)})</p>` : ""}
117
+ ${session.executions.length ? `<details><summary>${escapeHTML(ui.text("sessionExecutions"))} (${session.executions.length})</summary><ol>${session.executions.map((run) => executionView(ui, run)).join("")}</ol></details>` : ""}</li>`;
118
+ }
119
+ function executionView(ui, run) {
120
+ const label = run.previousDispatchId ? ui.text("sessionContinuation") : ui.text("sessionPurpose." + run.purpose);
121
+ const showState = run.state && run.state !== run.outcome && !(run.state === "completed" && ["succeeded", "accepted", "rejected"].includes(_nullishCoalesce(run.outcome, () => ( ""))));
122
+ const fields = [
123
+ [ui.text("sessionExecutionId"), run.id],
124
+ [ui.text("sessionRoundId"), run.roundId],
125
+ [ui.text("sessionTurnId"), run.turnId],
126
+ [ui.text("sessionPreviousExecution"), run.previousDispatchId],
127
+ [ui.text(run.roundId ? "sessionExecutionStarted" : "sessionExecutionPrepared"), date(run.createdAt)],
128
+ [ui.text("sessionExecutionEnded"), date(run.completedAt)],
129
+ ..._nullishCoalesce(_optionalChain([run, 'access', _11 => _11.resources, 'optionalAccess', _12 => _12.map, 'call', _13 => _13((resource) => [resource.label, resource.value])]), () => ( []))
130
+ ];
131
+ return `<li class="kanban__session-run"><p>${escapeHTML(label)}${run.attempt ? " \xB7 #" + run.attempt : ""}${run.outcome ? " \xB7 " + escapeHTML(ui.text("sessionRunOutcome." + run.outcome)) : ""}${showState ? " \xB7 " + escapeHTML(ui.text("sessionState." + run.state)) : ""}</p>
132
+ <dl>${fields.filter((field2) => field2[1] !== void 0).map(([key, value2]) => `<dt>${escapeHTML(key)}</dt><dd>${escapeHTML(value2)}</dd>`).join("")}</dl>${run.code ? `<p role="alert">${escapeHTML(run.code)}</p>` : ""}</li>`;
133
+ }
134
+ function date(value2) {
135
+ return value2 === void 0 ? void 0 : new Date(value2).toLocaleString();
136
+ }
137
+ function legacySessions(detail) {
138
+ return [...new Map(detail.history.map((row) => [row.sessionId, row])).values()].map((row) => ({
139
+ ..._optionalChain([detail, 'access', _14 => _14.current, 'optionalAccess', _15 => _15.sessionId]) === row.sessionId ? detail.current : { sessionId: row.sessionId, state: "unknown", stale: true, recent: false },
140
+ current: _optionalChain([detail, 'access', _16 => _16.current, 'optionalAccess', _17 => _17.sessionId]) === row.sessionId,
141
+ purposes: ["execution"],
142
+ executions: []
143
+ }));
144
+ }
145
+ function executionFilter(ui, value2 = "all") {
146
+ return `<select name="execution" aria-label="${escapeAttr(ui.text("executionFilter"))}">${["all", "running", "recent", "active"].map((filter) => `<option value="${filter}" ${filter === value2 ? "selected" : ""}>${escapeHTML(ui.text("executionFilter." + filter))}</option>`).join("")}</select>`;
147
+ }
148
+
149
+ // src/ui/view.ts
150
+ var states = ["proposed", "backlog", "open", "claimed", "review", "completed", "cancelled"];
151
+ var button = (ui, action, key, primary = false) => `<button type="button"${primary ? ' class="kanban__primary"' : ""} data-action="${escapeAttr(action)}">${escapeHTML(ui.text(key))}</button>`;
152
+ function contextTabs(ui, active = "board") {
153
+ return `<nav class="kanban__context-tabs" aria-label="${escapeAttr(ui.text("recordContext"))}">${[["board", "tabBoard"], ["log", "tabLog"], ["decision", "tabDecision"]].map(([value2, key]) => `<button type="button" data-context="${value2}" ${active === value2 ? 'aria-current="page"' : ""}>${escapeHTML(ui.text(key))}</button>`).join("")}</nav>`;
154
+ }
155
+ function field(ui, name, key, value2 = "", required = true, type = "text") {
156
+ return `<label>${escapeHTML(ui.text(key))}<input name="${name}" type="${type}" value="${escapeAttr(value2)}" ${required ? "required" : ""}></label>`;
157
+ }
158
+ function textarea(ui, name, key, value2 = "") {
159
+ return `<label>${escapeHTML(ui.text(key))}<textarea name="${name}" required>${escapeHTML(value2)}</textarea></label>`;
160
+ }
161
+ function owns(item, actor) {
162
+ return !!actor && !!item.owner && (_nullishCoalesce(item.owner.kind, () => ( "agent"))) === actor.kind && (_nullishCoalesce(item.owner.id, () => ( item.owner.agentId))) === actor.id;
163
+ }
164
+ function navigation(ui, boards, selected) {
165
+ return `<header class="kanban__sidebar-header"><h2>${escapeHTML(ui.text("title"))}</h2>${button(ui, "new-board", "createShort")}</header>
166
+ <section class="kanban__board-list"><p class="kanban__caption">${escapeHTML(ui.text("scope"))}</p><nav aria-label="${escapeAttr(ui.text("title"))}">${boards.map((board) => `<button type="button" data-board="${escapeAttr(board.id)}" ${board.id === selected ? 'aria-current="page"' : ""}><small>${escapeHTML(_chunkPSKQAFDFcjs.kanbanReference.call(void 0, board))}</small><span>${escapeHTML(board.title)}</span></button>`).join("")}</nav></section>
167
+ <footer class="kanban__sidebar-tools"><details class="kanban__reference-lookup"><summary>${escapeHTML(ui.text("openReference"))}</summary>${referenceLookup(ui)}</details>
168
+ <details class="kanban__scope-note"><summary>${escapeHTML(ui.text("scopeInfo"))}</summary><p>${escapeHTML(ui.text("scopeHelp"))}</p></details></footer>`;
169
+ }
170
+ function referenceLookup(ui) {
171
+ return `<form data-form="reference"><input name="reference" aria-label="${escapeAttr(ui.text("reference"))}" placeholder="${escapeAttr(ui.text("referencePlaceholder"))}" required><button>${escapeHTML(ui.text("openShort"))}</button></form>`;
172
+ }
173
+ function boardHeader(ui, board, active = "board", automation = false) {
174
+ const [action, key] = active === "log" ? ["new-log", "newLog"] : active === "decision" ? ["new-decision", "newDecision"] : ["new-item", "newItem"];
175
+ const actions = `<div class="kanban__mobile-create">${button(ui, action, key)}</div>${button(ui, "refresh", "refresh")}${automation ? button(ui, "automation", "automationTitle") : ""}`;
176
+ return `<header class="kanban__page-header" data-titlebar><span class="kanban__navigation" data-navigation></span><div class="kanban__board-heading"><h2 data-titlebar-title title="${escapeAttr(board.title)}">${escapeHTML(board.title)}</h2></div>
177
+ <div class="kanban__header-actions"><span class="kanban__desktop-create">${button(ui, action, key, true)}</span><span class="kanban__window-navigation" data-window-navigation></span>${referenceMenu(ui, board, void 0, actions)}</div></header>`;
178
+ }
179
+ function catalogView(ui, boards) {
180
+ return `<header class="kanban__page-header" data-titlebar><span class="kanban__navigation" data-navigation></span><div class="kanban__board-heading"><h2 data-titlebar-title>${escapeHTML(ui.text("title"))}</h2></div><span class="kanban__window-navigation" data-window-navigation></span>${button(ui, "new-board", "createShort")}</header>
181
+ <div class="kanban__record-list">${boards.map((board) => `<button type="button" class="kanban__card" data-board="${escapeAttr(board.id)}"><strong>${escapeHTML(board.title)}</strong><small>${escapeHTML(_chunkPSKQAFDFcjs.kanbanReference.call(void 0, board))}</small></button>`).join("") || `<p>${escapeHTML(ui.text("emptyBoards"))}</p>`}</div>`;
182
+ }
183
+ function boardView(ui, board, page, query, activity = false, automation = false, sessionUrl) {
184
+ const columns = query.view === "archive" ? [{ id: "archive", label: ui.text("archive"), states }] : _nullishCoalesce(board.columns, () => ( states.map((state) => ({ id: state, label: ui.text(state), states: [state] }))));
185
+ return `${boardHeader(ui, board, "board", automation)}${contextTabs(ui)}<form data-form="filter" class="kanban__filters"><input name="search" aria-label="${escapeAttr(ui.text("search"))}" placeholder="${escapeAttr(ui.text("search"))}" value="${escapeAttr(_nullishCoalesce(query.search, () => ( "")))}">
186
+ <details class="kanban__filter-more" data-popover><summary>${escapeHTML(ui.text("moreFilters"))}${query.area || query.milestone || query.view !== "all" || query.execution && query.execution !== "all" ? '<span class="kanban__filter-dot" aria-hidden="true"></span>' : ""}</summary><div class="kanban__filter-fields"><label>${escapeHTML(ui.text("filter"))}<select name="view" aria-label="${escapeAttr(ui.text("filter"))}">${["all", "backlog", "ready", "in-progress", "review", "closed", "proposed", "archive"].map((view) => `<option value="${view}" ${view === query.view ? "selected" : ""}>${escapeHTML(ui.text(view === "ready" ? "open" : view === "in-progress" ? "claimed" : view === "closed" ? "completed" : view))}</option>`).join("")}</select></label>${phaseFilter(ui, query.phase)}${activity ? executionFilter(ui, query.execution) : ""}${suggestionField({ name: "area", label: "area", kind: "area", free: true, selected: query.area ? [{ id: query.area, label: query.area }] : [] })}
187
+ ${suggestionField({ name: "milestone", label: "milestone", kind: "milestone", free: true, selected: query.milestone ? [{ id: query.milestone, label: query.milestone }] : [] })}</div></details><button>${escapeHTML(ui.text("filter"))}</button></form>
188
+ <p class="kanban__count" role="status">${escapeHTML(ui.text("total", { count: page.total }))}</p><div class="kanban__columns" data-has-items="${page.items.length > 0}">${columns.map((column) => {
189
+ const items = page.items.filter((item) => column.states.includes(item.status));
190
+ return `<section class="kanban__column" data-empty="${items.length === 0}"><h3>${escapeHTML(column.label)} <small>${items.length}</small></h3><div class="kanban__column-items" data-column="${escapeAttr(column.id)}" tabindex="0" aria-label="${escapeAttr(column.label + " \xB7 " + ui.text("columnItems"))}">${items.map((item) => itemCard(ui, board, item, sessionUrl)).join("") || `<p>${escapeHTML(ui.text("emptyItems"))}</p>`}</div></section>`;
191
+ }).join("")}</div><footer class="kanban__toolbar"><small>${escapeHTML(ui.text("scrollHint"))}</small>${button(ui, "scroll-left", "previousColumns")}${button(ui, "scroll-right", "nextColumns")}${query.cursor ? button(ui, "first", "first") : ""}${page.nextCursor ? button(ui, "next", "next") : ""}</footer>`;
192
+ }
193
+ function itemCard(ui, board, item, sessionUrl) {
194
+ const content = `<strong>${escapeHTML(item.title)}</strong><small>${escapeHTML(_chunkPSKQAFDFcjs.kanbanReference.call(void 0, board, item))}</small><small>P${item.planning.priority} \xB7 ${escapeHTML(_nullishCoalesce(item.planning.area, () => ( "")))} \xB7 ${escapeHTML(_nullishCoalesce(item.planning.milestone, () => ( "")))}</small>${taskStatus(ui, item.task)}${item.pendingAmendments ? `<small class="kanban__badge">${escapeHTML(ui.text("pendingAmendments"))} (${item.pendingAmendments})</small>` : ""}${item.progress ? `<small>${item.progress.completed}/${item.progress.total}</small>` : ""}`;
195
+ const identity = _optionalChain([item, 'access', _18 => _18.task, 'optionalAccess', _19 => _19.sessionId]) ? `data-session="${escapeAttr(item.task.sessionId)}"` : `data-item="${escapeAttr(item.id)}"`;
196
+ return `<article class="kanban__card"><button type="button" class="kanban__item-open" ${identity}>${content}</button>${_optionalChain([item, 'access', _20 => _20.task, 'optionalAccess', _21 => _21.sessionId]) ? `<button type="button" data-item="${escapeAttr(item.id)}" title="${escapeAttr(ui.text("details"))}" aria-label="${escapeAttr(ui.text("details"))}">${escapeHTML(ui.text("details"))}</button>` : ""}<span data-item-execution="${escapeAttr(item.id)}">${executionBadge(ui, item.execution, sessionUrl)}</span></article>`;
197
+ }
198
+
199
+ // src/ui/children.ts
200
+ function childType(board, parent) {
201
+ const types = _nullishCoalesce(board.workTypes, () => ( _chunkPSKQAFDFcjs.DEFAULT_KANBAN_WORK_TYPES));
202
+ const rank = _optionalChain([types, 'access', _22 => _22.find, 'call', _23 => _23((type) => type.id === (_nullishCoalesce(_nullishCoalesce(_optionalChain([parent, 'access', _24 => _24.planning, 'optionalAccess', _25 => _25.kind]), () => ( board.defaultWorkType)), () => ( "task")))), 'optionalAccess', _26 => _26.rank]);
203
+ const candidates = types.filter((type) => rank !== void 0 && type.rank > rank);
204
+ return _optionalChain([(_nullishCoalesce(candidates.find((type) => type.id === (_nullishCoalesce(board.defaultWorkType, () => ( "task")))), () => ( candidates.sort((a, b) => a.rank - b.rank)[0]))), 'optionalAccess', _27 => _27.id]);
205
+ }
206
+ function childrenView(ui, page, cursor, canCreate, sessionUrl) {
207
+ return `<h4>${escapeHTML(ui.text("children"))} (${page.total})</h4><div class="kanban__actions">${canCreate ? button(ui, "new-child", "newChild") : ""}</div>
208
+ <div class="kanban__record-list">${page.items.map((item) => `<article class="kanban__card"><button type="button" data-item="${escapeAttr(item.id)}"><strong>${escapeHTML(item.title)}</strong></button>${taskStatus(ui, item.task)}${_optionalChain([item, 'access', _28 => _28.task, 'optionalAccess', _29 => _29.sessionId]) ? sessionLink(ui, item.task.sessionId, ui.text("executionSession"), _optionalChain([sessionUrl, 'optionalCall', _30 => _30(item.task.sessionId)])) : ""}${item.execution ? sessionLink(ui, _nullishCoalesce(item.execution.resourceId, () => ( item.execution.sessionId)), ui.text("executionSession"), _optionalChain([sessionUrl, 'optionalCall', _31 => _31(_nullishCoalesce(item.execution.resourceId, () => ( item.execution.sessionId)))])) : ""}</article>`).join("")}</div>
209
+ <div class="kanban__actions">${cursor ? button(ui, "children-first", "first") : ""}${page.nextCursor ? `<button type="button" data-action="children-next" data-cursor="${escapeAttr(page.nextCursor)}">${escapeHTML(ui.text("next"))}</button>` : ""}</div>`;
210
+ }
211
+
212
+ // src/ui/panel-header.ts
213
+ function panelHeader(ui, title, board, entity) {
214
+ const label = escapeAttr(ui.text("close"));
215
+ return `<header class="kanban__panel-header" data-titlebar><button type="button" class="kanban__panel-back" data-titlebar-back data-action="close-panel" aria-label="${label}" title="${label}">${_nullishCoalesce(ui.icons.back, () => ( ui.icons.close))}</button>
216
+ <h3 data-titlebar-title title="${escapeAttr(title)}">${escapeHTML(title)}</h3><span class="kanban__window-navigation" data-window-navigation></span>${board ? referenceMenu(ui, board, entity, `<p class="kanban__menu-title">${escapeHTML(title)}</p>`) : ""}</header>`;
217
+ }
218
+
219
+ // src/ui/context-view.ts
220
+ function recordsView(ui, page, kind, hasPrevious, board) {
221
+ return `${board ? boardHeader(ui, board, kind) : `<header class="kanban__toolbar"><h2>${escapeHTML(ui.text(kind))}</h2></header>`}
222
+ ${contextTabs(ui, kind)}<p>${escapeHTML(ui.text("recordHelp"))}</p><div class="kanban__record-list" tabindex="0" aria-label="${escapeAttr(ui.text(kind))}">${recordLinks(ui, page, board)}${!page.total ? `<p>${escapeHTML(ui.text("emptyRecords"))}</p>` : ""}</div>
223
+ <footer class="kanban__toolbar">${hasPrevious ? button(ui, "records-first", "first") : ""}${page.nextCursor ? button(ui, "records-next", "next") : ""}</footer>`;
224
+ }
225
+ function recordLinks(ui, page, board) {
226
+ return page.items.map((record) => `<button type="button" class="kanban__card" data-record="${escapeAttr(record.id)}"><strong>${escapeHTML(record.title)}</strong>${board && !record.activity ? `<small>${escapeHTML(_chunkPSKQAFDFcjs.kanbanReference.call(void 0, board, record))}</small>` : ""}<small>${escapeHTML(ui.text(record.activity ? "automaticLog" : record.kind))} \xB7 ${escapeHTML(ui.text(record.status))} \xB7 ${escapeHTML(new Date(record.createdAt).toLocaleString())}</small></button>`).join("");
227
+ }
228
+ function sourcesView(ui, sources) {
229
+ return sources.length ? `<ul>${sources.map((source) => `<li><button type="button" data-session="${escapeAttr(source.sessionId)}">${escapeHTML(source.sessionId)}</button><small>${escapeHTML(source.messageId)} \xB7 ${escapeHTML(source.revision)} \xB7 ${escapeHTML(source.role)}</small></li>`).join("")}</ul>` : `<p>${escapeHTML(ui.text("noSources"))}</p>`;
230
+ }
231
+ function recordForm(ui, kind, board, itemId = "", resources = []) {
232
+ return `${panelHeader(ui, ui.text(kind === "log" ? "newLog" : "newDecision"))}<form data-form="record">
233
+ <input type="hidden" name="kind" value="${kind}">${field(ui, "title", "name")}${textarea(ui, "content", "content")}${kind === "decision" ? textarea(ui, "rationale", "rationale") + suggestionField({ name: "supersedes", label: "supersedes", kind: "decision" }) : ""}
234
+ ${kind === "decision" && resources.length ? `<label>${escapeHTML(ui.text("recordResources"))}<select name="recordResources" multiple>${resources.map((resource) => `<option value="${escapeAttr(JSON.stringify(resource.reference))}">${escapeHTML(resource.label)}</option>`).join("")}</select></label>` : ""}
235
+ ${itemChoices(board, itemId)}<details><summary>${escapeHTML(ui.text("sources"))}</summary>${suggestionField({ name: "sourceSession", label: "sourceSession", kind: "session", manual: true })}${field(ui, "sourceMessage", "sourceMessage", "", false)}${field(ui, "sourceRevision", "sourceRevision", "", false)}</details><button>${escapeHTML(ui.text("save"))}</button></form>`;
236
+ }
237
+ function recordDetail(ui, record, board, executions) {
238
+ return `${panelHeader(ui, record.title, board, record)}
239
+ <p>${escapeHTML(ui.text(record.kind))} \xB7 ${escapeHTML(ui.text(record.status))} \xB7 ${escapeHTML(ui.text(record.origin))}</p>${record.activity ? "" : `<small>${escapeHTML(ui.text("recordId"))}: ${escapeHTML(record.id)}</small>`}<p>${escapeHTML(ui.text("author"))}: ${escapeHTML(record.author.id)} \xB7 ${escapeHTML(new Date(record.createdAt).toLocaleString())}</p>
240
+ <section data-reference-links></section><section class="kanban__prose"><h4>${escapeHTML(ui.text("content"))}</h4><p>${escapeHTML(record.content)}</p>${record.rationale ? `<h4>${escapeHTML(ui.text("rationale"))}</h4><p>${escapeHTML(record.rationale)}</p>` : ""}</section>
241
+ ${record.status === "draft" ? `<div class="kanban__actions">${button(ui, "confirm-record", "confirmRecord")}${button(ui, "reject-record", "rejectRecord")}</div>` : ""}
242
+ ${record.kind === "decision" && record.status === "confirmed" && executions ? recordExecutionView(ui, board, executions) : ""}
243
+ ${(_nullishCoalesce(record.externalRefs, () => ( []))).map((ref) => `<p>${escapeHTML(ref.system)}: ${escapeHTML(ref.id)}</p>`).join("")}
244
+ ${record.supersedes ? recordLink(ui, record.supersedes, "supersedes") : ""}${record.supersededBy ? recordLink(ui, record.supersededBy, "superseded") : ""}
245
+ <h4>${escapeHTML(ui.text("itemIds"))}</h4>${record.itemIds.map((id) => `<button type="button" data-item="${escapeAttr(id)}">${escapeHTML(_nullishCoalesce(_optionalChain([board, 'access', _32 => _32.items, 'access', _33 => _33.find, 'call', _34 => _34((item) => item.id === id), 'optionalAccess', _35 => _35.title]), () => ( id)))}</button>`).join("")}
246
+ ${record.activity ? "" : `<form data-form="record-link">${suggestionField({ name: "itemId", label: "itemIds", kind: "item", required: true })}<button>${escapeHTML(ui.text("linkRecord"))}</button></form>`}<h4>${escapeHTML(ui.text("sources"))}</h4>${sourcesView(ui, record.sources)}`;
247
+ }
248
+ function recordExecutionView(ui, board, executions) {
249
+ const completed = executions.some((run) => run.state === "completed");
250
+ const actions = completed ? `<p>${escapeHTML(ui.text("completedFollowup"))}</p>` : `${button(ui, "run-record", "runRecord")}<details><summary>${escapeHTML(ui.text("referenceActions"))}</summary>${button(ui, "run-record-separate", "runRecordSeparate")}</details>`;
251
+ return `<div class="kanban__actions">${actions}</div>${completed ? "" : `<p>${escapeHTML(ui.text("runRecordHelp"))}</p>`}
252
+ ${executions.map((run) => `<p><button type="button" ${run.rootItemId && run.boardId === board.id ? `data-item="${escapeAttr(run.rootItemId)}"` : `data-board="${escapeAttr(run.boardId)}"`}>${escapeHTML(ui.text(run.rootItemId && run.boardId === board.id ? "executionWork" : "executionBoard"))}</button> \xB7 ${escapeHTML(ui.text(run.state))}${run.code ? ` \xB7 ${escapeHTML(run.code)}` : ""}</p>`).join("")}`;
253
+ }
254
+ function recordLink(ui, id, key) {
255
+ return `<p>${escapeHTML(ui.text(key))}: <button type="button" data-record="${escapeAttr(id)}">${escapeHTML(id)}</button></p>`;
256
+ }
257
+ function amendmentsView(ui, item) {
258
+ const pending = _nullishCoalesce(_optionalChain([item, 'access', _36 => _36.amendments, 'optionalAccess', _37 => _37.filter, 'call', _38 => _38((proposal) => proposal.status === "pending")]), () => ( []));
259
+ if (!pending.length) return "";
260
+ return `<section class="kanban__amendments"><h4>${escapeHTML(ui.text("amendments"))}</h4><p>${escapeHTML(ui.text("amendmentHint"))}</p>${pending.map((proposal) => `<article><strong>${escapeHTML(proposal.proposedBy.id)}</strong><small> v${proposal.baseRevision}</small>
261
+ <details><summary>${escapeHTML(ui.text("currentText"))}</summary><p>${escapeHTML(item.title)}</p><p>${escapeHTML(item.responsibility)}</p><p>${escapeHTML(item.acceptance)}</p></details>
262
+ <h5>${escapeHTML(ui.text("proposedText"))}</h5><p>${escapeHTML(proposal.proposed.title)}</p><p>${escapeHTML(proposal.proposed.responsibility)}</p><p>${escapeHTML(proposal.proposed.acceptance)}</p>
263
+ ${sourcesView(ui, proposal.sources)}<form data-form="amendment" data-amendment="${escapeAttr(proposal.id)}"><select name="decision" aria-label="${escapeAttr(ui.text("amendments"))}"><option value="accept">${escapeHTML(ui.text("applyAmendment"))}</option><option value="reject">${escapeHTML(ui.text("rejectAmendment"))}</option></select>${field(ui, "reason", "reason", "", false)}<button>${escapeHTML(ui.text("save"))}</button></form></article>`).join("")}</section>`;
264
+ }
265
+ function itemChoices(board, itemId) {
266
+ return suggestionField({ name: "itemIds", label: "itemIds", kind: "item", multiple: true, selected: selectedItems(board, itemId ? [itemId] : []) });
267
+ }
268
+
269
+ // src/ui/event-history.ts
270
+ function meaningfulItemEvents(events) {
271
+ const last = /* @__PURE__ */ new Map(), hidden = /* @__PURE__ */ new Set();
272
+ for (const event of [...events].sort((a, b) => a.sequence - b.sequence)) {
273
+ if (event.kind === "renew") {
274
+ hidden.add(event);
275
+ continue;
276
+ }
277
+ if (event.kind !== "observe-execution" || !event.data.dispatchId || !event.data.reason) continue;
278
+ const key = JSON.stringify([event.boardId, event.itemId, event.data.dispatchId]);
279
+ const state = JSON.stringify([event.data.reason, event.data.code, event.data.status]);
280
+ if (last.get(key) === state) hidden.add(event);
281
+ last.set(key, state);
282
+ }
283
+ return events.filter((event) => !hidden.has(event));
284
+ }
285
+
286
+ // src/ui/forms.ts
287
+ function newBoardForm(ui) {
288
+ return `${panelHeader(ui, ui.text("newBoard"))}<form data-form="board">${field(ui, "title", "boardName")}
289
+ <label><input name="requireEstimate" type="checkbox">${escapeHTML(ui.text("requireEstimate"))}</label>
290
+ ${save(ui)}</form>`;
291
+ }
292
+ function newItemForm(ui, board, parent, resources) {
293
+ const preset = parent ? { kind: childType(board, parent), parentId: parent.id, priority: _nullishCoalesce(_optionalChain([parent, 'access', _39 => _39.planning, 'optionalAccess', _40 => _40.priority]), () => ( 2)), dependencies: [], references: [] } : void 0;
294
+ return `${panelHeader(ui, ui.text("newItem"))}<form data-form="item">${field(ui, "title", "name")}
295
+ ${textarea(ui, "responsibility", "responsibility")}${textarea(ui, "acceptance", "acceptance")}${launchModeField(ui)}${planningFields(ui, board, void 0, preset)}${itemResourceField(ui, resources, (_nullishCoalesce(board.workTypes, () => ( _chunkPSKQAFDFcjs.DEFAULT_KANBAN_WORK_TYPES))).some((type) => type.id === (_nullishCoalesce(_nullishCoalesce(_optionalChain([preset, 'optionalAccess', _41 => _41.kind]), () => ( board.defaultWorkType)), () => ( "task"))) && type.executable))}${save(ui)}</form>`;
296
+ }
297
+ function planningFields(ui, board, item, preset) {
298
+ const plan = _nullishCoalesce(preset, () => ( _optionalChain([item, 'optionalAccess', _42 => _42.planning]))), types = _nullishCoalesce(board.workTypes, () => ( _chunkPSKQAFDFcjs.DEFAULT_KANBAN_WORK_TYPES));
299
+ return `<label>${escapeHTML(ui.text("type"))}<select name="kind">${types.map((type) => `<option data-executable="${type.executable}" value="${escapeAttr(type.id)}" ${type.id === (_nullishCoalesce(_nullishCoalesce(_optionalChain([plan, 'optionalAccess', _43 => _43.kind]), () => ( board.defaultWorkType)), () => ( "task"))) ? "selected" : ""}>${escapeHTML(type.label)}</option>`).join("")}</select></label>
300
+ ${field(ui, "priority", "priority", String(_nullishCoalesce(_optionalChain([plan, 'optionalAccess', _44 => _44.priority]), () => ( 2))), true, "number")}
301
+ ${suggestionField({ name: "area", label: "area", kind: "area", free: true, selected: _optionalChain([plan, 'optionalAccess', _45 => _45.area]) ? [{ id: plan.area, label: plan.area }] : [] })}${suggestionField({ name: "milestone", label: "milestone", kind: "milestone", free: true, selected: _optionalChain([plan, 'optionalAccess', _46 => _46.milestone]) ? [{ id: plan.milestone, label: plan.milestone }] : [] })}
302
+ ${suggestionField({ name: "parentId", label: "parent", kind: "parent", excludeItemId: _optionalChain([item, 'optionalAccess', _47 => _47.id]), selected: selectedItems(board, _optionalChain([plan, 'optionalAccess', _48 => _48.parentId]) ? [plan.parentId] : []) })}${suggestionField({ name: "dependencies", label: "dependencies", kind: "dependency", multiple: true, excludeItemId: _optionalChain([item, 'optionalAccess', _49 => _49.id]), selected: selectedItems(board, _nullishCoalesce(_optionalChain([plan, 'optionalAccess', _50 => _50.dependencies]), () => ( []))) })}
303
+ ${field(ui, "dueAt", "dueAt", dateInput(_optionalChain([plan, 'optionalAccess', _51 => _51.dueAt])), false, "datetime-local")}`;
304
+ }
305
+ function dateInput(at) {
306
+ if (at === void 0) return "";
307
+ const date2 = new Date(at);
308
+ return new Date(at - date2.getTimezoneOffset() * 6e4).toISOString().slice(0, 16);
309
+ }
310
+ var save = (ui) => `<div class="kanban__actions"><button>${escapeHTML(ui.text("save"))}</button>${button(ui, "close-panel", "close")}</div>`;
311
+ function detailView(ui, board, item, actor, archived, events, relatedSessions = false, guidance, interactive = false, task = _chunkKYNYPKKHcjs.projectKanbanTask.call(void 0, item), fixAvailable = false) {
312
+ return `${panelHeader(ui, item.title, board, item)}${taskStatus(ui, task)}${archived ? "" : `<div class="kanban__actions">${taskControls(ui, task, fixAvailable)}</div>`}
313
+ <p>${escapeHTML(ui.text("origin"))}: ${escapeHTML(item.origin ? ui.text(item.origin.kind === "discovery" ? "flow" : "manual") + " \xB7 " + item.origin.principal.id : ui.text("unknownOrigin"))}</p>
314
+ <dl><dt>${escapeHTML(ui.text("itemId"))}</dt><dd>${escapeHTML(item.id)}</dd><dt>${escapeHTML(ui.text("responsibility"))}</dt><dd>${escapeHTML(item.responsibility)}</dd>
315
+ <dt>${escapeHTML(ui.text("acceptance"))}</dt><dd>${escapeHTML(item.acceptance)}</dd><dt>${escapeHTML(ui.text("owner"))}</dt><dd>${escapeHTML(_nullishCoalesce(_nullishCoalesce(_optionalChain([item, 'access', _52 => _52.owner, 'optionalAccess', _53 => _53.id]), () => ( _optionalChain([item, 'access', _54 => _54.owner, 'optionalAccess', _55 => _55.agentId]))), () => ( ui.text("none"))))}</dd></dl>
316
+ <p>${escapeHTML(ui.text(item.status))}${item.blocked ? " \xB7 " + escapeHTML(ui.text("blocked") + ": " + item.blocked.reason) : ""}</p>
317
+ ${interactive ? "<section data-item-operations></section>" : ""}
318
+ ${item.status === "review" ? interactive ? `<details><summary>${escapeHTML(ui.text("reviewFlow"))}</summary>${reviewGuidance(ui, guidance)}</details>` : reviewGuidance(ui, guidance) : ""}
319
+ <div class="kanban__actions">${controls(ui, board, item, actor, archived, interactive)}</div>
320
+ ${_nullishCoalesce(_optionalChain([item, 'access', _56 => _56.issues, 'optionalAccess', _57 => _57.filter, 'call', _58 => _58((issue) => !issue.resolvedBy), 'access', _59 => _59.map, 'call', _60 => _60((issue) => `<form data-form="task-reply"><input type="hidden" name="attemptId" value="${escapeAttr(issue.attemptId)}"><input type="hidden" name="replyTo" value="${escapeAttr(issue.id)}">${textarea(ui, "content", "message")}<button>${escapeHTML(ui.text("send"))}</button></form>`), 'access', _61 => _61.join, 'call', _62 => _62("")]), () => ( ""))}
321
+ ${amendmentsView(ui, item)}
322
+ ${archived ? "" : detailForms(ui, board, item, actor)}
323
+ ${(_nullishCoalesce(_optionalChain([item, 'access', _63 => _63.planning, 'optionalAccess', _64 => _64.dependencies]), () => ( []))).length ? `<p>${escapeHTML(ui.text("dependencies"))}: ${(_nullishCoalesce(_optionalChain([item, 'access', _65 => _65.planning, 'optionalAccess', _66 => _66.dependencies]), () => ( []))).map((id) => `<button type="button" data-item="${escapeAttr(id)}">${escapeHTML(_nullishCoalesce(_optionalChain([board, 'access', _67 => _67.items, 'access', _68 => _68.find, 'call', _69 => _69((i) => i.id === id), 'optionalAccess', _70 => _70.title]), () => ( id)))}</button>`).join(" ")}</p>` : ""}
324
+ ${_optionalChain([item, 'access', _71 => _71.planning, 'optionalAccess', _72 => _72.parentId]) ? `<p>${escapeHTML(ui.text("parent"))}: <button type="button" data-item="${escapeAttr(item.planning.parentId)}">${escapeHTML(_nullishCoalesce(_optionalChain([board, 'access', _73 => _73.items, 'access', _74 => _74.find, 'call', _75 => _75((parent) => parent.id === _optionalChain([item, 'access', _76 => _76.planning, 'optionalAccess', _77 => _77.parentId])), 'optionalAccess', _78 => _78.title]), () => ( item.planning.parentId)))}</button></p>` : ""}
325
+ <section data-children></section><section data-related-records></section><h4>${escapeHTML(ui.text("sources"))}</h4>${sourcesView(ui, _nullishCoalesce(item.sources, () => ( [])))}
326
+ <div data-analysis></div><div data-execution-session></div><section data-related-sessions-section></section><section data-reference-links></section><section data-work-resource-section></section>
327
+ ${relatedSessions ? "" : `<h4>${escapeHTML(ui.text("sessions"))}</h4>${item.participants.map((p) => `<button type="button" data-session="${escapeAttr(p.sessionId)}">${escapeHTML(p.sessionId)} \xB7 ${escapeHTML(p.roles.join(", "))}</button>`).join("") || `<p>${escapeHTML(ui.text("none"))}</p>`}`}
328
+ ${archived ? "" : `<details><summary>${escapeHTML(ui.text("registerSession"))}</summary><p>${escapeHTML(ui.text("registerSessionHelp"))}</p><form data-form="session">${suggestionField({ name: "sessionId", label: "session", kind: "session", manual: true, required: true })}<button>${escapeHTML(ui.text("registerSession"))}</button></form></details>`}
329
+ <section data-item-events>${itemEventsView(ui, events)}</section>`;
330
+ }
331
+ function reviewGuidance(ui, guidance) {
332
+ const [summary, ...steps] = _nullishCoalesce(_optionalChain([guidance, 'optionalAccess', _79 => _79.paragraphs]), () => ( [ui.text("reviewHelp")]));
333
+ return `<section class="kanban__review-guide"><strong>${escapeHTML(_nullishCoalesce(_optionalChain([guidance, 'optionalAccess', _80 => _80.title]), () => ( ui.text("reviewHelpTitle"))))}</strong><p>${escapeHTML(summary)}</p>
334
+ ${steps.length ? `<details><summary>${escapeHTML(ui.text("reviewFlow"))}</summary><ol>${steps.map((text) => `<li>${escapeHTML(text)}</li>`).join("")}</ol></details>` : ""}</section>`;
335
+ }
336
+ function itemEventsView(ui, events) {
337
+ return `<details><summary>${escapeHTML(ui.text("eventsHelp"))}</summary><ol>${meaningfulItemEvents(events).map((event) => `<li>${escapeHTML(event.kind)} \xB7 ${escapeHTML(_nullishCoalesce(_nullishCoalesce(event.actor.id, () => ( event.actor.agentId)), () => ( "")))} \xB7 ${escapeHTML(new Date(event.at).toLocaleString())}${event.data.message ? `<p>${escapeHTML(event.data.message)}</p>` : ""}</li>`).join("")}</ol></details>`;
338
+ }
339
+ function controls(ui, board, item, actor, archived, interactive = false) {
340
+ if (archived) return button(ui, "restore", "restore");
341
+ if (["completed", "cancelled"].includes(item.status)) return button(ui, "archive", "archiveItem");
342
+ const executable = _chunkPSKQAFDFcjs.executableWorkItem.call(void 0, board, item);
343
+ let result = item.status === "proposed" ? button(ui, "publish", "publish") : item.status === "backlog" && executable ? button(ui, "ready", "ready") : item.status === "open" ? button(ui, "defer", "defer") : "";
344
+ if (owns(item, actor) && item.status === "claimed") result += button(ui, "renew", "renew");
345
+ if (item.status === "review" && !owns(item, actor)) result += interactive ? "" : button(ui, "accept", "accept") + button(ui, "reject", "reject");
346
+ if (item.blocked) result += button(ui, "unblock", "unblock");
347
+ if (item.status === "review" && owns(item, actor) && board.policy.review !== "assignee") result += `<p>${escapeHTML(ui.text("noReview"))}</p>`;
348
+ return result;
349
+ }
350
+ function detailForms(ui, board, item, actor) {
351
+ if (["completed", "cancelled"].includes(item.status)) return "";
352
+ let forms = "";
353
+ const executable = _chunkPSKQAFDFcjs.executableWorkItem.call(void 0, board, item);
354
+ if (!executable && ["backlog", "proposed", "open"].includes(item.status)) forms += `<form data-form="close-plan">${field(ui, "resultRef", "result")}<button>${escapeHTML(ui.text("closePlan"))}</button></form>`;
355
+ if (!board.automation && executable && item.status === "open" && !item.blocked) forms += `<form data-form="claim">${board.policy.requireEstimate !== false ? estimates(ui) : ""}<button>${escapeHTML(ui.text("claim"))}</button></form>`;
356
+ if (item.status === "claimed" && owns(item, actor)) forms += `<form data-form="submit">${field(ui, "resultRef", "result")}<button>${escapeHTML(ui.text("submit"))}</button></form>`;
357
+ const automatic = _optionalChain([item, 'access', _81 => _81.eligiblePrincipals, 'optionalAccess', _82 => _82.some, 'call', _83 => _83((principal) => principal.kind === _optionalChain([board, 'access', _84 => _84.automation, 'optionalAccess', _85 => _85.worker, 'access', _86 => _86.kind]) && principal.id === board.automation.worker.id)]);
358
+ let secondary = "";
359
+ if (["proposed", "backlog", "open"].includes(item.status) && !automatic && !board.automation) secondary += `<details><summary>${escapeHTML(ui.text("editItem"))}</summary><form data-form="edit-item">${field(ui, "title", "name", item.title)}${textarea(ui, "responsibility", "responsibility", item.responsibility)}${textarea(ui, "acceptance", "acceptance", item.acceptance)}<button>${escapeHTML(ui.text("save"))}</button></form></details>`;
360
+ if (!board.automation && _optionalChain([item, 'access', _87 => _87.launch, 'optionalAccess', _88 => _88.admittedAt]) === void 0 && ["proposed", "backlog", "open"].includes(item.status)) secondary += `<details><summary>${escapeHTML(ui.text("plan"))}</summary><form data-form="plan">${planningFields(ui, board, item)}<button>${escapeHTML(ui.text("save"))}</button></form></details><form data-form="cancel">${field(ui, "reason", "reason")}<button>${escapeHTML(ui.text("cancel"))}</button></form>`;
361
+ if (owns(item, actor)) secondary += `<form data-form="release">${field(ui, "reason", "reason")}<button>${escapeHTML(ui.text("release"))}</button></form>`;
362
+ if (!item.blocked && (!item.owner || owns(item, actor))) secondary += `<form data-form="block">${field(ui, "reason", "reason")}<button>${escapeHTML(ui.text("block"))}</button></form>`;
363
+ return forms + `<details><summary>${escapeHTML(ui.text("moreActions"))}</summary>` + secondary + `<form data-form="message">${textarea(ui, "message", "message")}<button>${escapeHTML(ui.text("send"))}</button></form></details>`;
364
+ }
365
+ function estimates(ui) {
366
+ return `${field(ui, "complexity", "complexity", "1", true, "number")}${field(ui, "workUnits", "workUnits", "1", true, "number")}
367
+ ${field(ui, "duration", "duration", "30", true, "number")}<label>${escapeHTML(ui.text("confidence"))}<select name="confidence">${["low", "medium", "high"].map((value2) => `<option value="${value2}">${escapeHTML(ui.text(value2))}</option>`).join("")}</select></label>`;
368
+ }
369
+ function itemResourceField(ui, catalog, executable) {
370
+ if (!catalog || !catalog.resources.length && !catalog.selected) return "";
371
+ const options = [...catalog.resources], selected = catalog.selected ? JSON.stringify(catalog.selected) : "";
372
+ if (catalog.selected && !options.some((option) => option.reference.system === catalog.selected.system && option.reference.id === catalog.selected.id)) options.push({ reference: catalog.selected, label: catalog.selected.id });
373
+ return `<label>${escapeHTML(ui.text("itemResource"))}<select name="itemResource" data-resource-required="${catalog.required === true}" ${catalog.required && executable ? "required" : ""} ${catalog.readonly ? "disabled" : ""}>
374
+ <option value="">${escapeHTML(ui.text("none"))}</option>${options.map((option) => `<option value="${escapeAttr(JSON.stringify(option.reference))}" ${JSON.stringify(option.reference) === selected ? "selected" : ""}>${escapeHTML(option.label)}</option>`).join("")}</select>
375
+ ${catalog.readonly ? `<input type="hidden" name="itemResource" value="${escapeAttr(selected)}">` : ""}</label>`;
376
+ }
377
+ function editTaskForm(ui, board, item, editId, resources) {
378
+ return `${panelHeader(ui, item.title, board, item)}<p>${escapeHTML(ui.text("editLeaseHelp"))}</p><form data-form="save-edit"><input type="hidden" name="editId" value="${escapeAttr(editId)}">${field(ui, "title", "name", item.title)}${textarea(ui, "responsibility", "responsibility", item.responsibility)}${textarea(ui, "acceptance", "acceptance", item.acceptance)}${launchModeField(ui, _optionalChain([item, 'access', _89 => _89.launch, 'optionalAccess', _90 => _90.mode]))}${planningFields(ui, board, item)}${itemResourceField(ui, resources, true)}${save(ui)}</form>`;
379
+ }
380
+ function launchModeField(ui, mode = "automatic") {
381
+ return `<label>${escapeHTML(ui.text("taskStartMode"))}<select name="launchMode">${["automatic", "manual"].map((value2) => `<option value="${value2}" ${mode === value2 ? "selected" : ""}>${escapeHTML(ui.text("taskMode." + value2))}</option>`).join("")}</select></label>`;
382
+ }
383
+
384
+ // src/ui/automation.ts
385
+ function automationForm(ui, board, catalog) {
386
+ const settings = board.automation;
387
+ const select = (name, label) => {
388
+ const selected = _optionalChain([settings, 'optionalAccess', _91 => _91[name]]), targets = [...name === "preparation" ? _nullishCoalesce(catalog.preparations, () => ( [])) : catalog.targets];
389
+ if (_optionalChain([settings, 'optionalAccess', _92 => _92[name]]) && !targets.some((entry) => sameReference(entry.reference, selected)))
390
+ targets.push({ reference: settings[name], label: ui.text("automationTargetUnavailable") });
391
+ return `<label>${escapeHTML(ui.text(label))}<select name="${name}" ${name === "preparation" ? "" : "required"}><option value="">${escapeHTML(ui.text("automationChoose"))}</option>${targets.map((entry) => {
392
+ const value2 = JSON.stringify(entry.reference);
393
+ return `<option value="${escapeAttr(value2)}" ${sameReference(entry.reference, selected) ? "selected" : ""}>${escapeHTML(entry.label)}</option>`;
394
+ }).join("")}</select></label>`;
395
+ };
396
+ return `${panelHeader(ui, ui.text("automationTitle"))}<form data-form="automation">
397
+ <p>${escapeHTML(ui.text("automationHelp"))}</p>
398
+ <label><input type="checkbox" name="enabled" ${_optionalChain([settings, 'optionalAccess', _93 => _93.enabled]) ? "checked" : ""}>${escapeHTML(ui.text("automationEnabled"))}</label>
399
+ ${select("planner", "automationPlanner")}${select("executor", "automationExecutor")}${select("preparation", "automationPreparation")}<div data-preparation-options>${preparationFields(board, catalog)}</div>
400
+ <label><input type="checkbox" name="autoAccept" ${_optionalChain([settings, 'optionalAccess', _94 => _94.autoAccept]) ? "checked" : ""}>${escapeHTML(ui.text("automationAutoAccept"))}</label>
401
+ <p>${escapeHTML(ui.text("automationReviewHelp"))}</p>
402
+ <div class="kanban__actions"><button>${escapeHTML(ui.text("save"))}</button>${button(ui, "close-panel", "close")}</div></form>`;
403
+ }
404
+ function analysisView(ui, status) {
405
+ if (!status) return `<p>${escapeHTML(ui.text("automationPending"))}</p>`;
406
+ return `<section><h4>${escapeHTML(ui.text("automationAnalysis"))}</h4><p>${escapeHTML(ui.text("automationState." + status.state))}</p>
407
+ ${status.reason ? `<p>${escapeHTML(status.reason)}</p>` : ""}${status.code ? `<p>${escapeHTML(ui.text("errors." + status.code))}</p>` : ""}
408
+ ${["failed", "clarification"].includes(status.state) ? button(ui, "reanalyze", "automationReanalyze") : ""}</section>`;
409
+ }
410
+ function preparationFields(board, catalog, reference = _optionalChain([board, 'access', _95 => _95.automation, 'optionalAccess', _96 => _96.preparation])) {
411
+ const selected = reference;
412
+ const entries = _nullishCoalesce(_optionalChain([catalog, 'access', _97 => _97.preparations, 'optionalAccess', _98 => _98.filter, 'call', _99 => _99((entry) => sameReference(entry.reference, selected))]), () => ( []));
413
+ return entries.flatMap((entry) => (_nullishCoalesce(entry.options, () => ( []))).map((option) => {
414
+ const value2 = _nullishCoalesce(_nullishCoalesce(_optionalChain([board, 'access', _100 => _100.automation, 'optionalAccess', _101 => _101.preparationOptions, 'optionalAccess', _102 => _102[option.name]]), () => ( option.defaultValue)), () => ( ""));
415
+ const name = "preparation-option-" + option.name;
416
+ const control = option.choices ? `<select name="${escapeAttr(name)}">${option.choices.map((choice) => `<option value="${escapeAttr(choice.value)}" ${choice.value === value2 ? "selected" : ""}>${escapeHTML(choice.label)}</option>`).join("")}</select>` : `<input name="${escapeAttr(name)}" value="${escapeAttr(value2)}" maxlength="4096">`;
417
+ return `<label>${escapeHTML(option.label)}${control}</label>`;
418
+ })).join("");
419
+ }
420
+ function sameReference(left, right) {
421
+ const a = left;
422
+ const b = right;
423
+ return !!a && !!b && a.system === b.system && a.id === b.id && a.revision === b.revision;
424
+ }
425
+
426
+ // src/ui/suggestion-picker.ts
427
+ var KanbanSuggestionPicker = (_class = class {
428
+ constructor(root, config, fetch, presentation) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);
429
+ this.root = root;
430
+ this.config = config;
431
+ this.fetch = fetch;
432
+ this.presentation = presentation;
433
+ this.selected = [..._nullishCoalesce(config.selected, () => ( []))];
434
+ this.mount();
435
+ this.bind();
436
+ this.renderValues();
437
+ this.close();
438
+ }
439
+ __init() {this.input = document.createElement("input")}
440
+ __init2() {this.list = document.createElement("div")}
441
+ __init3() {this.chips = document.createElement("div")}
442
+ __init4() {this.values = document.createElement("div")}
443
+ __init5() {this.hint = document.createElement("small")}
444
+
445
+ __init6() {this.rows = []}
446
+ __init7() {this.active = -1}
447
+ __init8() {this.loading = false}
448
+ __init9() {this.generation = 0}
449
+
450
+ __init10() {this.abort = new AbortController()}
451
+ mount() {
452
+ const caption = document.createElement("label"), id = "kanban-suggest-" + _chunk6MN3H3YFcjs.randomUUID.call(void 0, );
453
+ caption.textContent = this.presentation.text(this.config.label);
454
+ caption.htmlFor = this.input.id = id;
455
+ this.input.type = "text";
456
+ this.input.autocomplete = "off";
457
+ this.input.maxLength = 256;
458
+ this.input.required = !!this.config.required;
459
+ this.input.setAttribute("role", "combobox");
460
+ this.input.setAttribute("aria-autocomplete", "list");
461
+ this.input.setAttribute("aria-controls", this.list.id = id + "-list");
462
+ this.input.setAttribute("aria-describedby", this.hint.id = id + "-hint");
463
+ this.input.placeholder = this.config.free ? this.presentation.text(this.config.label) + " \xB7 " + this.presentation.text("suggestFreePlaceholder") : this.presentation.text("suggestPlaceholder");
464
+ if (this.config.free) this.input.name = this.config.name;
465
+ this.input.value = this.config.multiple ? "" : _nullishCoalesce(_optionalChain([this, 'access', _103 => _103.selected, 'access', _104 => _104[0], 'optionalAccess', _105 => _105.label]), () => ( ""));
466
+ this.list.className = "kanban__suggest-list";
467
+ this.list.setAttribute("role", "listbox");
468
+ this.list.setAttribute("aria-label", caption.textContent);
469
+ this.chips.className = "kanban__suggest-chips";
470
+ this.hint.setAttribute("aria-live", "polite");
471
+ this.hint.textContent = this.presentation.text(this.config.free ? "suggestFreeHint" : "suggestHint");
472
+ this.root.replaceChildren(caption, this.chips, this.input, this.values, this.list, this.hint);
473
+ }
474
+ bind() {
475
+ const signal = this.abort.signal;
476
+ this.input.addEventListener("focus", () => {
477
+ if (!this.config.multiple && this.selected.length) this.input.select();
478
+ if (!this.loading) void this.search();
479
+ }, { signal });
480
+ this.input.addEventListener("click", () => {
481
+ if (this.list.hidden && !this.loading) void this.search();
482
+ }, { signal });
483
+ this.input.addEventListener("input", () => {
484
+ if (!this.config.multiple && !this.config.free) {
485
+ this.selected = [];
486
+ this.renderValues();
487
+ }
488
+ this.validate();
489
+ clearTimeout(this.timer);
490
+ this.generation++;
491
+ this.rows = [];
492
+ this.active = -1;
493
+ this.list.replaceChildren();
494
+ this.hint.textContent = this.presentation.text("loading");
495
+ this.open();
496
+ this.timer = setTimeout(() => {
497
+ void this.search();
498
+ }, 150);
499
+ }, { signal });
500
+ this.input.addEventListener("keydown", (event) => this.keydown(event), { signal });
501
+ this.list.addEventListener("pointerdown", (event) => event.preventDefault(), { signal });
502
+ this.root.addEventListener("click", (event) => this.click(event), { signal });
503
+ document.addEventListener("scroll", () => this.position(), { signal, capture: true });
504
+ window.addEventListener("resize", () => this.position(), { signal });
505
+ this.root.addEventListener("focusout", (event) => {
506
+ if (!this.root.contains(event.relatedTarget)) this.close();
507
+ }, { signal });
508
+ }
509
+ async search() {
510
+ const generation = ++this.generation, operationId = _chunk6MN3H3YFcjs.randomUUID.call(void 0, ), started = Date.now();
511
+ const search = !this.config.multiple && _optionalChain([this, 'access', _106 => _106.selected, 'access', _107 => _107[0], 'optionalAccess', _108 => _108.label]) === this.input.value ? "" : this.input.value.trim();
512
+ const kind = _optionalChain([this, 'access', _109 => _109.root, 'access', _110 => _110.closest, 'call', _111 => _111("form"), 'optionalAccess', _112 => _112.querySelector, 'call', _113 => _113("[name=kind]"), 'optionalAccess', _114 => _114.value]);
513
+ this.revealInput();
514
+ this.loading = true;
515
+ this.hint.textContent = this.presentation.text("loading");
516
+ try {
517
+ const page = await this.fetch({
518
+ kind: this.config.kind,
519
+ search,
520
+ limit: 12,
521
+ ...this.config.multiple ? { excludeIds: this.selected.map((row) => row.id) } : {},
522
+ ...this.config.excludeItemId ? { excludeItemId: this.config.excludeItemId } : {},
523
+ ...this.config.kind === "parent" && kind ? { childKind: kind } : {}
524
+ });
525
+ if (generation !== this.generation || this.abort.signal.aborted || !this.root.isConnected) return;
526
+ this.rows = page.items.filter((row) => !this.config.multiple || !this.selected.some((value2) => value2.id === row.id));
527
+ if (this.config.manual && search && !this.rows.some((row) => row.id === search)) this.rows.push({ id: search, label: search, detail: this.presentation.text("suggestManualId") });
528
+ this.active = this.rows.length ? 0 : -1;
529
+ this.renderOptions();
530
+ this.hint.textContent = this.presentation.text(page.hasMore ? "suggestRefine" : this.rows.length ? this.config.free ? "suggestFreeHint" : "suggestHint" : "suggestEmpty");
531
+ this.log({ operationId, stage: this.config.kind, result: "success", count: page.items.length, durationMs: Date.now() - started });
532
+ } catch (error) {
533
+ if (generation !== this.generation || this.abort.signal.aborted) return;
534
+ this.rows = [];
535
+ this.list.replaceChildren();
536
+ this.hint.textContent = this.presentation.text("suggestFailed");
537
+ const retry = document.createElement("button");
538
+ retry.type = "button";
539
+ retry.textContent = this.presentation.text("retry");
540
+ retry.dataset.suggestRetry = "";
541
+ this.list.append(retry);
542
+ this.open();
543
+ const raw = _optionalChain([error, 'optionalAccess', _115 => _115.code]), code = typeof raw === "string" && /^[A-Z_0-9]{1,80}$/.test(raw) ? raw : "KANBAN_SUGGEST_FAILED";
544
+ this.log({ operationId, stage: this.config.kind, result: "failure", code, durationMs: Date.now() - started });
545
+ } finally {
546
+ if (generation === this.generation) this.loading = false;
547
+ }
548
+ }
549
+ log(event) {
550
+ try {
551
+ _optionalChain([this, 'access', _116 => _116.presentation, 'access', _117 => _117.log, 'optionalCall', _118 => _118(event)]);
552
+ } catch (e2) {
553
+ }
554
+ }
555
+ renderOptions() {
556
+ this.list.replaceChildren();
557
+ this.rows.forEach((row, index) => {
558
+ const button2 = document.createElement("button");
559
+ button2.type = "button";
560
+ button2.tabIndex = -1;
561
+ button2.dataset.suggestIndex = String(index);
562
+ button2.id = this.list.id + "-" + index;
563
+ button2.setAttribute("role", "option");
564
+ const title = document.createElement("strong"), detail = document.createElement("small");
565
+ title.textContent = row.label;
566
+ detail.textContent = [row.status ? this.presentation.text(row.status) : "", row.archived ? this.presentation.text("archive") : "", row.detail, this.config.free ? "" : row.id].filter(Boolean).join(" \xB7 ");
567
+ button2.append(title, detail);
568
+ this.list.append(button2);
569
+ });
570
+ this.open();
571
+ this.markActive();
572
+ }
573
+ click(event) {
574
+ const button2 = event.target.closest("button");
575
+ if (!button2) return;
576
+ if (button2.dataset.suggestIndex !== void 0) this.choose(this.rows[Number(button2.dataset.suggestIndex)]);
577
+ else if (button2.dataset.suggestRemove !== void 0) {
578
+ this.selected = this.selected.filter((row) => row.id !== button2.dataset.suggestRemove);
579
+ this.renderValues();
580
+ this.changed();
581
+ this.input.focus();
582
+ } else if (button2.hasAttribute("data-suggest-retry")) {
583
+ void this.search();
584
+ this.input.focus();
585
+ }
586
+ }
587
+ choose(row) {
588
+ if (!row) return;
589
+ this.selected = this.config.multiple ? [...this.selected, row] : [row];
590
+ this.input.value = this.config.multiple ? "" : row.label;
591
+ this.renderValues();
592
+ this.input.setCustomValidity("");
593
+ this.hint.textContent = this.presentation.text(this.config.free ? "suggestFreeHint" : "suggestHint");
594
+ this.changed();
595
+ this.close();
596
+ this.input.focus();
597
+ }
598
+ renderValues() {
599
+ this.values.replaceChildren();
600
+ this.chips.replaceChildren();
601
+ if (!this.config.free) for (const row of this.selected.length ? this.selected : this.config.multiple ? [] : [{ id: "", label: "" }]) {
602
+ const input = document.createElement("input");
603
+ input.type = "hidden";
604
+ input.name = this.config.name;
605
+ input.value = row.id;
606
+ this.values.append(input);
607
+ }
608
+ if (this.config.multiple) for (const row of this.selected) {
609
+ const chip = document.createElement("button");
610
+ chip.type = "button";
611
+ chip.dataset.suggestRemove = row.id;
612
+ const name = document.createElement("span"), remove = document.createElement("span");
613
+ name.textContent = row.label;
614
+ remove.innerHTML = this.presentation.icons.close;
615
+ remove.setAttribute("aria-hidden", "true");
616
+ chip.append(name, remove);
617
+ chip.setAttribute("aria-label", this.presentation.text("suggestRemove") + ": " + row.label);
618
+ this.chips.append(chip);
619
+ }
620
+ }
621
+ validate() {
622
+ const unselected = !this.config.free && !!this.input.value.trim() && (this.config.multiple || !this.selected.length);
623
+ this.input.setCustomValidity(unselected ? this.presentation.text("suggestPickRequired") : "");
624
+ }
625
+ changed() {
626
+ this.validate();
627
+ this.root.dispatchEvent(new Event("input", { bubbles: true }));
628
+ }
629
+ keydown(event) {
630
+ if (event.key === "Escape" && !this.list.hidden) {
631
+ event.preventDefault();
632
+ event.stopPropagation();
633
+ this.close();
634
+ return;
635
+ }
636
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
637
+ event.preventDefault();
638
+ event.stopPropagation();
639
+ if (this.list.hidden) {
640
+ void this.search();
641
+ return;
642
+ }
643
+ this.active = this.rows.length ? (this.active + (event.key === "ArrowDown" ? 1 : -1) + this.rows.length) % this.rows.length : -1;
644
+ this.markActive();
645
+ } else if (event.key === "Enter" && !this.list.hidden) {
646
+ event.preventDefault();
647
+ event.stopPropagation();
648
+ this.choose(this.rows[this.active]);
649
+ } else if (event.key === "Tab" && !this.list.querySelector("[data-suggest-retry]")) this.close();
650
+ else if (event.key === "Backspace" && this.config.multiple && !this.input.value && this.selected.length) {
651
+ this.selected.pop();
652
+ this.renderValues();
653
+ this.changed();
654
+ }
655
+ }
656
+ markActive() {
657
+ [...this.list.children].forEach((node, index) => node.setAttribute("aria-selected", String(index === this.active)));
658
+ if (this.active >= 0) {
659
+ this.input.setAttribute("aria-activedescendant", this.list.id + "-" + this.active);
660
+ const row = this.list.children[this.active];
661
+ if (row && row.offsetTop < this.list.scrollTop) this.list.scrollTop = row.offsetTop;
662
+ else if (row && row.offsetTop + row.offsetHeight > this.list.scrollTop + this.list.clientHeight) this.list.scrollTop = row.offsetTop + row.offsetHeight - this.list.clientHeight;
663
+ } else this.input.removeAttribute("aria-activedescendant");
664
+ }
665
+ open() {
666
+ this.list.hidden = false;
667
+ this.input.setAttribute("aria-expanded", "true");
668
+ this.position();
669
+ }
670
+ revealInput() {
671
+ const panel = this.root.closest("[data-panel]");
672
+ if (!panel) return;
673
+ const input = this.input.getBoundingClientRect(), bounds = panel.getBoundingClientRect();
674
+ const top = (_nullishCoalesce(_optionalChain([panel, 'access', _119 => _119.querySelector, 'call', _120 => _120(".kanban__panel-header"), 'optionalAccess', _121 => _121.getBoundingClientRect, 'call', _122 => _122(), 'access', _123 => _123.bottom]), () => ( bounds.top))) + 8, bottom = bounds.bottom - 12;
675
+ if (input.bottom > bottom) panel.scrollTop += input.bottom - bottom;
676
+ else if (input.top < top) panel.scrollTop -= top - input.top;
677
+ }
678
+ position() {
679
+ if (this.list.hidden || !this.root.isConnected) return;
680
+ const input = this.input.getBoundingClientRect(), scope = (_nullishCoalesce(_nullishCoalesce(this.root.closest("[data-panel]"), () => ( this.root.closest(".kanban"))), () => ( document.documentElement))).getBoundingClientRect();
681
+ const below = Math.max(0, scope.bottom - input.bottom - 8), above = Math.max(0, input.top - scope.top - 8), up = above > below && below < 160;
682
+ const height = Math.min(210, up ? above : below), width = Math.min(Math.max(input.width, 220), Math.max(0, scope.width - 16));
683
+ const left = Math.max(scope.left + 8, Math.min(input.left, scope.right - width - 8));
684
+ Object.assign(this.list.style, {
685
+ position: "fixed",
686
+ zIndex: "30",
687
+ left: `${left}px`,
688
+ width: `${width}px`,
689
+ maxHeight: `${height}px`,
690
+ top: up ? "auto" : `${input.bottom + 4}px`,
691
+ bottom: up ? `${innerHeight - input.top + 4}px` : "auto"
692
+ });
693
+ }
694
+ close() {
695
+ this.loading = false;
696
+ if (this.hint.textContent === this.presentation.text("loading")) this.hint.textContent = this.presentation.text(this.config.free ? "suggestFreeHint" : "suggestHint");
697
+ clearTimeout(this.timer);
698
+ this.generation++;
699
+ this.list.hidden = true;
700
+ this.input.setAttribute("aria-expanded", "false");
701
+ this.input.removeAttribute("aria-activedescendant");
702
+ }
703
+ destroy() {
704
+ clearTimeout(this.timer);
705
+ this.generation++;
706
+ this.abort.abort();
707
+ }
708
+ }, _class);
709
+
710
+ // src/ui/suggestions.ts
711
+ var KanbanSuggestions = (_class2 = class {
712
+ constructor(root, service, boardId, presentation) {;_class2.prototype.__init11.call(this);
713
+ this.root = root;
714
+ this.service = service;
715
+ this.boardId = boardId;
716
+ this.presentation = presentation;
717
+ this.observer = new MutationObserver(() => this.sync());
718
+ this.observer.observe(root, { childList: true, subtree: true });
719
+ this.sync();
720
+ }
721
+
722
+ __init11() {this.fields = /* @__PURE__ */ new Map()}
723
+ sync() {
724
+ for (const [node, field2] of this.fields) if (!this.root.contains(node)) {
725
+ field2.destroy();
726
+ this.fields.delete(node);
727
+ }
728
+ if (!this.service) return;
729
+ for (const node of this.root.querySelectorAll("[data-kanban-suggest]")) {
730
+ if (this.fields.has(node)) continue;
731
+ const options = JSON.parse(node.dataset.kanbanSuggest);
732
+ const picker = new KanbanSuggestionPicker(node, options, (query) => this.service.suggest(this.boardId(), query), this.presentation);
733
+ this.fields.set(node, picker);
734
+ }
735
+ }
736
+ destroy() {
737
+ this.observer.disconnect();
738
+ for (const field2 of this.fields.values()) field2.destroy();
739
+ this.fields.clear();
740
+ }
741
+ }, _class2);
742
+
743
+ // src/ui/index.ts
744
+ var _uicommon = require('@nestos/ui-common');
745
+
746
+ // src/ui/drawer.ts
747
+ var KanbanDrawer = (_class3 = class {
748
+ constructor(editor, sidebar) {;_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);
749
+ this.editor = editor;
750
+ this.sidebar = sidebar;
751
+ }
752
+ __init12() {this.key = ""}
753
+ __init13() {this.focusRequested = false}
754
+
755
+
756
+
757
+ __init14() {this.restoreRequested = false}
758
+ get element() {
759
+ return this.editor.querySelector("[data-panel]");
760
+ }
761
+ get open() {
762
+ return !this.element.hidden;
763
+ }
764
+ show(html, key) {
765
+ const panel = this.element, same = this.open && this.key === key, top = same ? panel.scrollTop : 0;
766
+ if (!this.open) {
767
+ const active = document.activeElement;
768
+ this.restoreTarget = _nullishCoalesce(active, () => ( void 0));
769
+ this.restoreItem = _optionalChain([active, 'optionalAccess', _124 => _124.dataset, 'access', _125 => _125.item]);
770
+ this.restoreRecord = _optionalChain([active, 'optionalAccess', _126 => _126.dataset, 'access', _127 => _127.record]);
771
+ }
772
+ this.focusRequested ||= same && panel.contains(document.activeElement);
773
+ const retained = same ? ["[data-work-resource-section]", "[data-related-sessions-section]"].map((selector) => ({ selector, node: panel.querySelector(selector) })) : [];
774
+ panel.innerHTML = html;
775
+ for (const { selector, node } of retained) if (_optionalChain([node, 'optionalAccess', _128 => _128.hasChildNodes, 'call', _129 => _129()])) _optionalChain([panel, 'access', _130 => _130.querySelector, 'call', _131 => _131(selector), 'optionalAccess', _132 => _132.replaceWith, 'call', _133 => _133(node)]);
776
+ const error = document.createElement("p");
777
+ error.dataset.panelError = "";
778
+ error.setAttribute("role", "alert");
779
+ const header = panel.querySelector(".kanban__panel-header");
780
+ if (header) header.after(error);
781
+ else panel.prepend(error);
782
+ panel.hidden = false;
783
+ panel.scrollTop = top;
784
+ this.key = key;
785
+ this.focusRequested ||= !same;
786
+ this.editor.querySelector("[data-content]").inert = true;
787
+ if (this.sidebar) this.sidebar.inert = true;
788
+ this.editor.querySelector("[data-backdrop]").hidden = false;
789
+ this.editor.classList.add("kanban--detail");
790
+ }
791
+ close() {
792
+ this.element.hidden = true;
793
+ this.element.replaceChildren();
794
+ this.key = "";
795
+ this.focusRequested = false;
796
+ this.restoreRequested = true;
797
+ this.editor.querySelector("[data-content]").inert = false;
798
+ if (this.sidebar) this.sidebar.inert = false;
799
+ this.editor.querySelector("[data-backdrop]").hidden = true;
800
+ this.editor.classList.remove("kanban--detail");
801
+ }
802
+ flushFocus() {
803
+ if (this.focusRequested && this.open) {
804
+ this.focusRequested = false;
805
+ this.element.focus({ preventScroll: true });
806
+ } else if (this.restoreRequested) {
807
+ this.restoreRequested = false;
808
+ const matches = [...this.editor.querySelectorAll("[data-item], [data-record]")];
809
+ const target = _optionalChain([this, 'access', _134 => _134.restoreTarget, 'optionalAccess', _135 => _135.isConnected]) ? this.restoreTarget : matches.find((node) => this.restoreItem ? node.dataset.item === this.restoreItem : this.restoreRecord ? node.dataset.record === this.restoreRecord : false);
810
+ _optionalChain([(_nullishCoalesce(target, () => ( this.editor.querySelector("button")))), 'optionalAccess', _136 => _136.focus, 'call', _137 => _137({ preventScroll: true })]);
811
+ }
812
+ }
813
+ keydown(event) {
814
+ if (!this.open || event.key !== "Tab") return;
815
+ const nodes = [...this.element.querySelectorAll('button, input:not([type=hidden]), select, textarea, a[href], summary, [tabindex="0"]')].filter((node) => !node.matches(":disabled") && node.tabIndex >= 0 && !node.closest("[hidden], [inert]") && !node.hidden && !node.closest("details:not([open]) :not(summary)"));
816
+ const first = nodes[0], last = nodes[nodes.length - 1], active = document.activeElement;
817
+ if (!first) {
818
+ event.preventDefault();
819
+ this.element.focus();
820
+ return;
821
+ }
822
+ if (event.shiftKey && (active === first || active === this.element)) {
823
+ event.preventDefault();
824
+ last.focus();
825
+ } else if (!event.shiftKey && (active === last || !this.element.contains(active))) {
826
+ event.preventDefault();
827
+ first.focus();
828
+ }
829
+ }
830
+ destroy() {
831
+ if (this.sidebar) this.sidebar.inert = false;
832
+ this.editor.classList.remove("kanban--detail");
833
+ }
834
+ }, _class3);
835
+ function drawerMarkup(ui) {
836
+ return `<button type="button" class="kanban__backdrop" data-backdrop data-action="dismiss-panel" aria-label="${escapeAttr(ui.text("closeDetail"))}" tabindex="-1" hidden></button>
837
+ <section data-panel class="kanban__panel" role="dialog" aria-modal="true" aria-label="${escapeAttr(ui.text("details"))}" tabindex="-1" hidden></section>`;
838
+ }
839
+ function preserveBoardScroll(container, render) {
840
+ const columns = [...container.querySelectorAll("[data-column]")].map((node) => ({ id: node.dataset.column, top: node.scrollTop }));
841
+ const left = _nullishCoalesce(_optionalChain([container, 'access', _138 => _138.querySelector, 'call', _139 => _139(".kanban__columns"), 'optionalAccess', _140 => _140.scrollLeft]), () => ( 0));
842
+ render();
843
+ for (const node of container.querySelectorAll("[data-column]")) node.scrollTop = _nullishCoalesce(_optionalChain([columns, 'access', _141 => _141.find, 'call', _142 => _142((column) => column.id === node.dataset.column), 'optionalAccess', _143 => _143.top]), () => ( 0));
844
+ const board = container.querySelector(".kanban__columns");
845
+ if (board) board.scrollLeft = left;
846
+ }
847
+
848
+ // src/ui/diagnostics.ts
849
+ function failureDetails(error) {
850
+ if (!error || typeof error !== "object") return {};
851
+ const value2 = error;
852
+ const known = /^(?:Error|TypeError|ReferenceError|SyntaxError|RangeError|SecurityError|QuotaExceededError|NotSupportedError|InvalidStateError|DataCloneError|AbortError|NetworkError)$/;
853
+ const errorName = typeof value2.name === "string" && known.test(value2.name) ? value2.name : void 0;
854
+ const frame = typeof value2.stack === "string" ? value2.stack.match(/(?:^|\/)([A-Za-z0-9_.-]+\.m?js):(\d+):(\d+)/m) : null;
855
+ return { ...errorName ? { errorName } : {}, ...frame ? { source: `${frame[1]}:${frame[2]}:${frame[3]}` } : {} };
856
+ }
857
+
858
+ // src/ui/operations.ts
859
+ function itemOperationsView(ui, view, sessionUrl) {
860
+ return `<button type="button" data-action="refresh-item-operations">${escapeHTML(ui.text("operationRefresh"))}</button><h4>${escapeHTML(ui.text("operationsTitle"))}</h4>
861
+ <ol class="kanban__operations">${view.operations.map((operation) => operationCard(ui, view, operation, sessionUrl)).join("")}</ol>`;
862
+ }
863
+ function operationCard(ui, view, operation, url) {
864
+ const reason = operation.code ? ui.text("errors." + operation.code) : "";
865
+ return `<li data-operation-id="${escapeAttr(operation.id)}"><strong>${escapeHTML(operation.label)}</strong> \xB7 ${escapeHTML(ui.text("operationState." + operation.state))}
866
+ ${operation.reason ? `<p>${escapeHTML(operation.reason)}</p>` : ""}${operation.code ? `<p role="alert">${escapeHTML(reason.endsWith("errors." + operation.code) ? operation.code : reason)}</p>` : ""}
867
+ ${operation.summary ? `<pre class="kanban__operation-summary">${escapeHTML(operation.summary)}</pre>` : ""}
868
+ ${operation.sessionId ? sessionLink(ui, operation.sessionId, ui.text("operationSession"), _optionalChain([url, 'optionalCall', _144 => _144(operation.sessionId)])) : ""}
869
+ <form data-form="item-operation" data-operation="${escapeAttr(operation.id)}" data-revision="${escapeAttr(view.revision)}">
870
+ ${choices(ui, operation)}${operation.confirmation ? `<label><input type="checkbox" name="confirmed" required>${escapeHTML(operation.confirmation)}</label>` : ""}
871
+ <button type="submit"${operation.enabled ? "" : " disabled"}>${escapeHTML(operation.label)}</button></form>${configuration(ui, view, operation)}</li>`;
872
+ }
873
+ function choices(ui, operation) {
874
+ return (_nullishCoalesce(operation.fields, () => ( []))).map((field2) => `<label>${escapeHTML(field2.label)}<select name="choice-${escapeAttr(field2.id)}" required>
875
+ <option value="">${escapeHTML(ui.text("operationChoose"))}</option>${field2.choices.map((choice) => `<option value="${escapeAttr(choice.id)}"${field2.selected === choice.id ? " selected" : ""}>${escapeHTML(choice.label + (choice.detail ? " \xB7 " + choice.detail : ""))}</option>`).join("")}</select></label>`).join("");
876
+ }
877
+ function configuration(ui, view, operation) {
878
+ if (!operation.configurable) return "";
879
+ return `<details><summary>${escapeHTML(ui.text("operationConfigure"))}</summary><form data-form="operation-binding" data-operation="${escapeAttr(operation.id)}" data-revision="${escapeAttr(view.revision)}">
880
+ <label>${escapeHTML(ui.text("operationFlow"))}<select name="target" required><option value="">${escapeHTML(ui.text("operationChoose"))}</option>
881
+ ${(_nullishCoalesce(view.targets, () => ( []))).map((target) => `<option value="${escapeAttr(JSON.stringify(target.reference))}"${JSON.stringify(target.reference) === JSON.stringify(operation.binding) ? " selected" : ""}>${escapeHTML(target.label)}</option>`).join("")}</select></label>
882
+ <button type="submit">${escapeHTML(ui.text("save"))}</button>${_optionalChain([view, 'access', _145 => _145.targets, 'optionalAccess', _146 => _146.length]) ? "" : `<p>${escapeHTML(ui.text("operationNoFlows"))}</p>`}</form></details>`;
883
+ }
884
+
885
+ // src/ui/work-resources.ts
886
+ function workResourcesView(ui, resources) {
887
+ if (!resources.length) return `<p>${escapeHTML(ui.text("none"))}</p>`;
888
+ return resources.map((resource) => `<article class="kanban__work-resource" data-work-resource="${escapeAttr(resource.id)}">
889
+ <strong>${escapeHTML(resource.title)}</strong>${copy(ui, resource.title, ui.text("copyWorkResourceName"))} \xB7 ${escapeHTML(ui.text("workResourceState." + resource.state))}
890
+ <p><code>${escapeHTML(resource.location)}</code>${copy(ui, resource.location, ui.text("copyWorkResourcePath"))}</p>
891
+ <dl>${resource.properties.map((row) => `<dt>${escapeHTML(row.label)}</dt><dd>${escapeHTML(row.value)}${copy(ui, row.value, ui.text("copyValue") + " \xB7 " + row.label)}</dd>`).join("")}</dl>
892
+ ${resource.reasons.map((reason) => `<p>${escapeHTML(reasonLabel(ui, reason))}</p>`).join("")}
893
+ <button type="button" data-action="cleanup-work-resource" data-resource="${escapeAttr(resource.id)}" data-version="${escapeAttr(resource.version)}" ${resource.canCleanup ? "" : "disabled"}>${escapeHTML(ui.text("cleanupWorkResource"))}</button>
894
+ </article>`).join("");
895
+ }
896
+ function copy(ui, value2, label) {
897
+ return `<button type="button" class="kanban__copy-button" data-action="copy-resource" data-copy="${escapeAttr(value2)}" title="${escapeAttr(label)}" aria-label="${escapeAttr(label)}">${_nullishCoalesce(ui.icons.copy, () => ( escapeHTML(ui.text("copyValue"))))}</button>`;
898
+ }
899
+ function reasonLabel(ui, reason) {
900
+ const key = "workResourceReason." + reason, label = ui.text(key);
901
+ return label.includes(key) ? reason : label;
902
+ }
903
+
904
+ // src/ui/renderer.ts
905
+ function createRenderer(ui) {
906
+ const bind = (fn) => (...args) => fn(ui, ...args);
907
+ return {
908
+ label: ui.text.bind(ui),
909
+ boardView: bind(boardView),
910
+ catalogView: bind(catalogView),
911
+ navigation: bind(navigation),
912
+ button: bind(button),
913
+ detailView: bind(detailView),
914
+ itemEventsView: bind(itemEventsView),
915
+ newBoardForm: bind(newBoardForm),
916
+ newItemForm: bind(newItemForm),
917
+ recordLinks: bind(recordLinks),
918
+ recordsView: bind(recordsView),
919
+ recordDetail: bind(recordDetail),
920
+ recordForm: bind(recordForm),
921
+ executionSessionView: bind(executionSessionView),
922
+ executionBadge: bind(executionBadge),
923
+ copyReference: bind(copyReference)
924
+ };
925
+ }
926
+
927
+ // src/ui/commands.ts
928
+ var value = (data, key) => String(_nullishCoalesce(data.get(key), () => ( ""))).trim();
929
+ function readPlan(data, previous) {
930
+ const area = value(data, "area"), milestone = value(data, "milestone"), parentId = value(data, "parentId"), dueAt = value(data, "dueAt");
931
+ return {
932
+ kind: value(data, "kind"),
933
+ ..._optionalChain([previous, 'optionalAccess', _147 => _147.execution]) ? { execution: previous.execution } : {},
934
+ priority: Number(value(data, "priority")),
935
+ references: _nullishCoalesce(_optionalChain([previous, 'optionalAccess', _148 => _148.references]), () => ( [])),
936
+ dependencies: [...new Set(data.getAll("dependencies").flatMap((value2) => String(value2).split(",")).map((id) => id.trim()).filter(Boolean))],
937
+ ...area ? { area } : {},
938
+ ...milestone ? { milestone } : {},
939
+ ...parentId ? { parentId } : {},
940
+ ...dueAt ? { dueAt: new Date(dueAt).getTime() } : {}
941
+ };
942
+ }
943
+ function boardInput(data, actor, id) {
944
+ return {
945
+ id,
946
+ title: value(data, "title"),
947
+ scope: { kind: "system" },
948
+ members: [{ ...actor, responsibility: "Manage personal work", roles: ["worker", "reviewer", "coordinator"] }],
949
+ policy: { requireEstimate: data.has("requireEstimate"), review: "assignee" }
950
+ };
951
+ }
952
+ function formCommand(kind, data, board, item, id = _chunk6MN3H3YFcjs.randomUUID.call(void 0, )) {
953
+ if (kind === "item") return { type: "create-item", ...value(data, "launchMode") ? { launch: { mode: value(data, "launchMode") } } : {}, item: { id, title: value(data, "title"), responsibility: value(data, "responsibility"), acceptance: value(data, "acceptance"), ...value(data, "itemResource") ? { externalRefs: [JSON.parse(value(data, "itemResource"))] } : {} }, planning: readPlan(data) };
954
+ if (kind === "record") {
955
+ const sessionId = value(data, "sourceSession"), messageId = value(data, "sourceMessage"), revision = value(data, "sourceRevision");
956
+ if ([sessionId, messageId, revision].some(Boolean) && ![sessionId, messageId, revision].every(Boolean)) throw Object.assign(new Error("Missing source fields"), { code: "SOURCE_FIELDS_REQUIRED" });
957
+ return { type: "append-record", record: {
958
+ id,
959
+ kind: value(data, "kind"),
960
+ title: value(data, "title"),
961
+ content: value(data, "content"),
962
+ externalRefs: data.getAll("recordResources").map((value2) => JSON.parse(String(value2))),
963
+ itemIds: [...new Set(data.getAll("itemIds").flatMap((value2) => String(value2).split(",")).map((id2) => id2.trim()).filter(Boolean))],
964
+ sources: sessionId ? [{ sessionId, messageId, revision, role: "user" }] : [],
965
+ ...value(data, "rationale") ? { rationale: value(data, "rationale") } : {},
966
+ ...value(data, "supersedes") ? { supersedes: value(data, "supersedes") } : {}
967
+ } };
968
+ }
969
+ if (!item) throw new Error("ITEM_NOT_FOUND");
970
+ const itemId = item.id, token = _nullishCoalesce(_optionalChain([item, 'access', _149 => _149.owner, 'optionalAccess', _150 => _150.token]), () => ( ""));
971
+ if (kind === "edit-item") return { type: "edit-item", itemId, title: value(data, "title"), responsibility: value(data, "responsibility"), acceptance: value(data, "acceptance") };
972
+ if (kind === "amendment") return { type: "review-amendment", itemId, amendmentId: value(data, "amendmentId"), accept: value(data, "decision") === "accept", reason: value(data, "reason") || void 0 };
973
+ if (kind === "claim") return { type: "claim", itemId, ...board.policy.requireEstimate !== false ? { estimate: estimate(data) } : {} };
974
+ if (kind === "close-plan") return { type: "close-plan", itemId, resultRef: value(data, "resultRef") };
975
+ if (kind === "submit") return { type: "submit", itemId, token, resultRef: value(data, "resultRef") };
976
+ if (kind === "plan") return { type: "plan-item", itemId, planning: readPlan(data, item.planning) };
977
+ if (kind === "cancel") return { type: "cancel-item", itemId, reason: value(data, "reason") };
978
+ if (kind === "release") return { type: "release-item", itemId, token, next: "backlog", reason: value(data, "reason") };
979
+ if (kind === "block") return { type: "block-item", itemId, token, reason: value(data, "reason") };
980
+ if (kind === "session") return { type: "register-session", itemId, sessionId: value(data, "sessionId"), role: "observer", subscribed: true };
981
+ if (kind === "message") return { type: "message", itemId, message: value(data, "message"), recipientSessionIds: item.participants.filter((p) => p.subscribed).map((p) => p.sessionId) };
982
+ throw new Error("UNKNOWN_COMMAND");
983
+ }
984
+ function estimate(data) {
985
+ return { complexity: Number(value(data, "complexity")), workUnits: Number(value(data, "workUnits")), durationMs: Number(value(data, "duration")) * 6e4, confidence: value(data, "confidence") };
986
+ }
987
+ function actionCommand(action, item) {
988
+ const itemId = item.id, token = _nullishCoalesce(_optionalChain([item, 'access', _151 => _151.owner, 'optionalAccess', _152 => _152.token]), () => ( ""));
989
+ if (action === "ready" || action === "defer") return { type: "set-stage", itemId, stage: action === "ready" ? "open" : "backlog" };
990
+ if (action === "publish") return { type: "publish-proposal", itemId };
991
+ if (action === "renew") return { type: "renew", itemId, token };
992
+ if (action === "accept" || action === "reject") return { type: "review", itemId, accept: action === "accept" };
993
+ if (action === "archive") return { type: "archive-items", itemIds: [itemId], reason: "manual" };
994
+ if (action === "restore") return { type: "restore-item", itemId };
995
+ if (action === "unblock") return { type: "unblock-item", itemId, token };
996
+ throw new Error("UNKNOWN_COMMAND");
997
+ }
998
+
999
+ // src/ui/index.ts
1000
+ var KanbanWorkbench = (_class4 = class {
1001
+ constructor(options, container) {;_class4.prototype.__init15.call(this);_class4.prototype.__init16.call(this);_class4.prototype.__init17.call(this);_class4.prototype.__init18.call(this);_class4.prototype.__init19.call(this);_class4.prototype.__init20.call(this);_class4.prototype.__init21.call(this);_class4.prototype.__init22.call(this);_class4.prototype.__init23.call(this);_class4.prototype.__init24.call(this);_class4.prototype.__init25.call(this);_class4.prototype.__init26.call(this);_class4.prototype.__init27.call(this);_class4.prototype.__init28.call(this);
1002
+ this.options = options;
1003
+ this.container = container;
1004
+ this.ui = createRenderer(options.presentation);
1005
+ }
1006
+
1007
+
1008
+
1009
+
1010
+
1011
+
1012
+ __init15() {this.itemArchived = false}
1013
+
1014
+ __init16() {this.boardId = ""}
1015
+
1016
+ __init17() {this.recordId = ""}
1017
+
1018
+
1019
+
1020
+
1021
+ __init18() {this.itemId = ""}
1022
+ __init19() {this.query = { view: "all", limit: 50 }}
1023
+
1024
+ __init20() {this.generation = 0}
1025
+ __init21() {this.eventGeneration = 0}
1026
+ __init22() {this.abort = new AbortController()}
1027
+ __init23() {this.busy = false}
1028
+
1029
+ __init24() {this.operationStage = "ui.operation"}
1030
+ __init25() {this.dirty = false}
1031
+ __init26() {this.visible = true}
1032
+ __init27() {this.invalid = false}
1033
+
1034
+
1035
+
1036
+ __init28() {this.windowMounts = /* @__PURE__ */ new Map()}
1037
+ async start() {
1038
+ const { sidebar, editor } = this.options, signal = this.abort.signal;
1039
+ _optionalChain([sidebar, 'optionalAccess', _153 => _153.classList, 'access', _154 => _154.add, 'call', _155 => _155("kanban__sidebar")]);
1040
+ editor.classList.add("kanban");
1041
+ editor.innerHTML = `<p data-status role="status"></p><p data-error role="alert"></p><div data-retry hidden>${this.ui.button("refresh", "retry")}</div><div data-content class="kanban__content"></div>${drawerMarkup(this.options.presentation)}`;
1042
+ this.drawer = new KanbanDrawer(editor, sidebar);
1043
+ this.suggestions = new KanbanSuggestions(editor, this.options.service, () => this.boardId, this.options.presentation);
1044
+ document.addEventListener("pointerdown", (event) => {
1045
+ for (const menu of editor.querySelectorAll("[data-popover][open]")) if (!menu.contains(event.target)) menu.open = false;
1046
+ }, { signal });
1047
+ editor.addEventListener("toggle", (event) => {
1048
+ const opened = event.target;
1049
+ if (opened instanceof HTMLDetailsElement && opened.open && opened.hasAttribute("data-related-sessions")) {
1050
+ void this.loadRelatedSessions();
1051
+ return;
1052
+ }
1053
+ if (opened instanceof HTMLDetailsElement && opened.open && opened.hasAttribute("data-work-resources")) {
1054
+ void this.loadWorkResources();
1055
+ return;
1056
+ }
1057
+ if (!(opened instanceof HTMLDetailsElement) || !opened.open || !opened.hasAttribute("data-popover")) return;
1058
+ for (const menu of editor.querySelectorAll("[data-popover][open]")) if (menu !== opened) menu.open = false;
1059
+ }, { capture: true, signal });
1060
+ editor.addEventListener("change", (event) => {
1061
+ const target = event.target;
1062
+ if (!(target instanceof HTMLSelectElement) || target.name !== "kind") return;
1063
+ const resource = _optionalChain([target, 'access', _156 => _156.closest, 'call', _157 => _157("form"), 'optionalAccess', _158 => _158.querySelector, 'call', _159 => _159('select[name="itemResource"]')]);
1064
+ if (resource) resource.required = resource.dataset.resourceRequired === "true" && _optionalChain([target, 'access', _160 => _160.selectedOptions, 'access', _161 => _161[0], 'optionalAccess', _162 => _162.dataset, 'access', _163 => _163.executable]) === "true";
1065
+ }, { signal });
1066
+ editor.addEventListener("keydown", (event) => {
1067
+ const menu = editor.querySelector("[data-popover][open]");
1068
+ if (event.key === "Escape" && menu) {
1069
+ event.preventDefault();
1070
+ event.stopPropagation();
1071
+ menu.open = false;
1072
+ _optionalChain([menu, 'access', _164 => _164.querySelector, 'call', _165 => _165("summary"), 'optionalAccess', _166 => _166.focus, 'call', _167 => _167()]);
1073
+ return;
1074
+ }
1075
+ this.drawer.keydown(event);
1076
+ if (event.key === "Escape" && this.drawer.open) {
1077
+ event.preventDefault();
1078
+ void this.run(() => this.requestClose(false));
1079
+ }
1080
+ }, { signal });
1081
+ for (const element of sidebar ? [sidebar, editor] : [editor]) element.addEventListener("click", (event) => {
1082
+ if (event instanceof MouseEvent && (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey || event.button !== 0)) return;
1083
+ if (!event.target.closest("[data-action], [data-board], [data-item], [data-session], [data-record], [data-context]")) return;
1084
+ void this.run(async () => {
1085
+ await this.click(event);
1086
+ if (element === sidebar) _optionalChain([this, 'access', _168 => _168.options, 'access', _169 => _169.activated, 'optionalCall', _170 => _170()]);
1087
+ });
1088
+ }, { signal });
1089
+ _optionalChain([sidebar, 'optionalAccess', _171 => _171.addEventListener, 'call', _172 => _172("submit", (event) => {
1090
+ event.preventDefault();
1091
+ void this.run(() => this.submit(event));
1092
+ }, { signal })]);
1093
+ editor.addEventListener("submit", (event) => {
1094
+ event.preventDefault();
1095
+ void this.run(() => this.submit(event));
1096
+ }, { signal });
1097
+ editor.addEventListener("change", (event) => {
1098
+ const target = event.target;
1099
+ if (target.name === "preparation" && this.board && this.automationCatalog) {
1100
+ const container = editor.querySelector("[data-preparation-options]");
1101
+ if (container) container.innerHTML = preparationFields(this.board, this.automationCatalog, target.value ? JSON.parse(target.value) : null);
1102
+ }
1103
+ }, { signal });
1104
+ editor.addEventListener("input", (event) => {
1105
+ const target = event.target;
1106
+ if (target.closest("[data-form=reference]")) return;
1107
+ if (target.closest("form")) this.generation++;
1108
+ if (target.closest("[data-panel]")) {
1109
+ this.dirty = true;
1110
+ _optionalChain([this, 'access', _173 => _173.options, 'access', _174 => _174.changed, 'optionalCall', _175 => _175()]);
1111
+ }
1112
+ }, { signal });
1113
+ this.activityTimer = setInterval(() => {
1114
+ if (!this.visible || document.hidden) return;
1115
+ updateTaskCountdowns(this.options.editor, this.options.presentation);
1116
+ if (_optionalChain([this, 'access', _176 => _176.options, 'access', _177 => _177.service, 'optionalAccess', _178 => _178.queryActivity]) && (_optionalChain([this, 'access', _179 => _179.board, 'optionalAccess', _180 => _180.automation, 'optionalAccess', _181 => _181.enabled]) || _optionalChain([this, 'access', _182 => _182.page, 'optionalAccess', _183 => _183.items, 'access', _184 => _184.some, 'call', _185 => _185((item) => item.execution)]) || this.query.execution && this.query.execution !== "all")) this.refreshInBackground(true);
1117
+ }, 15e3);
1118
+ if (this.options.initialResource !== void 0) await this.openResource(this.options.initialResource);
1119
+ else await this.run(() => this.refresh());
1120
+ }
1121
+ open(target) {
1122
+ const path = target.boardId ? `/board/${encodeURIComponent(target.boardId)}${target.recordId ? "/@record/" + encodeURIComponent(target.recordId) : target.itemId ? "/" + encodeURIComponent(target.itemId) : ""}` : "/";
1123
+ return this.openResource(path);
1124
+ }
1125
+ dispose() {
1126
+ return this.destroy();
1127
+ }
1128
+ async openResource(path) {
1129
+ if (this.busy || this.dirty) throw new Error(this.ui.label("unsaved"));
1130
+ this.closePanel();
1131
+ const parts = path.split("/");
1132
+ this.boardId = decodeURIComponent(_nullishCoalesce(parts[2], () => ( "")));
1133
+ this.itemId = decodeURIComponent(_nullishCoalesce(parts[3], () => ( "")));
1134
+ this.recordId = parts[3] === "@record" ? decodeURIComponent(_nullishCoalesce(parts[4], () => ( ""))) : "";
1135
+ if (this.recordId) this.itemId = "";
1136
+ this.recordKind = ["@log", "@decision"].includes(_nullishCoalesce(parts[3], () => ( ""))) ? parts[3].slice(1) : void 0;
1137
+ if (this.recordKind) this.itemId = "";
1138
+ this.recordCursor = void 0;
1139
+ this.query = { view: "all", limit: 50 };
1140
+ await this.run(() => this.refresh());
1141
+ }
1142
+ createBoard() {
1143
+ return this.run(async () => {
1144
+ if (this.dirty) throw new Error(this.ui.label("unsaved"));
1145
+ this.showBoardForm();
1146
+ });
1147
+ }
1148
+ showBoardForm() {
1149
+ this.itemId = "";
1150
+ this.drawer.show(this.ui.newBoardForm(), "new-board");
1151
+ this.dirty = true;
1152
+ }
1153
+ getActiveResourceId() {
1154
+ return this.boardId ? `/board/${encodeURIComponent(this.boardId)}${this.recordId ? "/@record/" + encodeURIComponent(this.recordId) : this.itemId ? "/" + encodeURIComponent(this.itemId) : this.recordKind ? "/@" + this.recordKind : ""}` : "/";
1155
+ }
1156
+ getViewState() {
1157
+ return {
1158
+ title: _optionalChain([this, 'access', _186 => _186.drawer, 'optionalAccess', _187 => _187.open]) ? _nullishCoalesce(_optionalChain([this, 'access', _188 => _188.panel, 'call', _189 => _189(), 'access', _190 => _190.querySelector, 'call', _191 => _191("h3"), 'optionalAccess', _192 => _192.textContent]), () => ( _optionalChain([this, 'access', _193 => _193.board, 'optionalAccess', _194 => _194.title]))) : _nullishCoalesce(_optionalChain([this, 'access', _195 => _195.board, 'optionalAccess', _196 => _196.title]), () => ( this.ui.label("title"))),
1159
+ context: _optionalChain([this, 'access', _197 => _197.board, 'optionalAccess', _198 => _198.title]),
1160
+ dirty: this.dirty,
1161
+ busy: this.busy
1162
+ };
1163
+ }
1164
+ captureRecovery() {
1165
+ if (this.busy || this.dirty) throw new Error(this.ui.label("unsaved"));
1166
+ return null;
1167
+ }
1168
+ restoreRecovery() {
1169
+ }
1170
+ suspendForRecovery() {
1171
+ this.generation++;
1172
+ this.abort.abort();
1173
+ _optionalChain([this, 'access', _199 => _199.suggestions, 'optionalAccess', _200 => _200.destroy, 'call', _201 => _201()]);
1174
+ if (this.activityTimer) clearInterval(this.activityTimer);
1175
+ }
1176
+ async destroy() {
1177
+ for (const release of this.windowMounts.values()) release();
1178
+ this.windowMounts.clear();
1179
+ this.suspendForRecovery();
1180
+ _optionalChain([this, 'access', _202 => _202.releaseNavigation, 'optionalCall', _203 => _203()]);
1181
+ this.releaseNavigation = void 0;
1182
+ _optionalChain([this, 'access', _204 => _204.suggestions, 'optionalAccess', _205 => _205.destroy, 'call', _206 => _206()]);
1183
+ _optionalChain([this, 'access', _207 => _207.drawer, 'optionalAccess', _208 => _208.destroy, 'call', _209 => _209()]);
1184
+ _optionalChain([this, 'access', _210 => _210.options, 'access', _211 => _211.sidebar, 'optionalAccess', _212 => _212.replaceChildren, 'call', _213 => _213()]);
1185
+ this.options.editor.replaceChildren();
1186
+ this.options.editor.inert = false;
1187
+ this.options.editor.classList.remove("kanban");
1188
+ _optionalChain([this, 'access', _214 => _214.options, 'access', _215 => _215.sidebar, 'optionalAccess', _216 => _216.classList, 'access', _217 => _217.remove, 'call', _218 => _218("kanban__sidebar")]);
1189
+ _optionalChain([this, 'access', _219 => _219.container, 'optionalAccess', _220 => _220.replaceChildren, 'call', _221 => _221()]);
1190
+ _optionalChain([this, 'access', _222 => _222.container, 'optionalAccess', _223 => _223.classList, 'access', _224 => _224.remove, 'call', _225 => _225("kanban-layout")]);
1191
+ }
1192
+ invalidateFromEvents() {
1193
+ this.invalid = true;
1194
+ this.eventGeneration++;
1195
+ this.refreshInBackground();
1196
+ }
1197
+ setVisible(visible) {
1198
+ this.visible = visible;
1199
+ if (visible && this.invalid) this.invalidateFromEvents();
1200
+ }
1201
+ refreshInBackground(activityOnly = false) {
1202
+ if (this.background || this.busy || this.dirty || !this.visible || this.abort.signal.aborted) return;
1203
+ if (_optionalChain([document, 'access', _226 => _226.activeElement, 'optionalAccess', _227 => _227.closest, 'call', _228 => _228("form")]) && this.options.editor.contains(document.activeElement)) return;
1204
+ const started = Date.now(), operationId = _chunk6MN3H3YFcjs.randomUUID.call(void 0, );
1205
+ const reading = activityOnly ? this.refreshActivity(this.generation) : this.refresh(), generation = this.generation;
1206
+ this.background = reading.catch((error) => {
1207
+ if (!this.alive(generation)) return;
1208
+ this.invalid = false;
1209
+ if (!this.abort.signal.aborted) {
1210
+ this.reportRead("ui.background", error, operationId, started);
1211
+ this.error(errorText(error, this.ui.label, "ui.background"));
1212
+ }
1213
+ }).finally(() => {
1214
+ this.background = void 0;
1215
+ if (this.invalid && !this.busy && !this.dirty && this.visible && !this.abort.signal.aborted) this.refreshInBackground();
1216
+ });
1217
+ }
1218
+ queryPage() {
1219
+ const service = this.options.service;
1220
+ return this.query.execution && this.query.execution !== "all" && service.queryActivity ? service.queryActivity(this.boardId, this.query) : service.query(this.boardId, this.query);
1221
+ }
1222
+ async refreshActivity(generation) {
1223
+ if (this.item && this.itemId) {
1224
+ await Promise.all([this.loadExecutionSections(generation, this.item, this.itemArchived), _optionalChain([this, 'access', _229 => _229.operations, 'optionalAccess', _230 => _230.operations, 'access', _231 => _231.some, 'call', _232 => _232((operation) => ["running", "unknown"].includes(operation.state))]) ? this.loadOperations(generation) : void 0]);
1225
+ return;
1226
+ }
1227
+ if (!this.board || this.recordId || this.recordKind || !_optionalChain([this, 'access', _233 => _233.options, 'access', _234 => _234.service, 'optionalAccess', _235 => _235.queryActivity])) return;
1228
+ await this.supplement("ui.activity", generation, () => this.options.service.queryActivity(this.boardId, this.query), (page) => {
1229
+ if (page.revision !== _optionalChain([this, 'access', _236 => _236.page, 'optionalAccess', _237 => _237.revision])) {
1230
+ this.invalid = true;
1231
+ this.refreshInBackground();
1232
+ return;
1233
+ }
1234
+ if (JSON.stringify(page) === JSON.stringify(this.page)) return;
1235
+ const sameItems = page.total === this.page.total && page.nextCursor === this.page.nextCursor && JSON.stringify(page.items.map((item) => item.id)) === JSON.stringify(this.page.items.map((item) => item.id));
1236
+ this.page = page;
1237
+ if (sameItems) {
1238
+ const sessions = new Map(page.items.map((item) => [item.id, item.execution]));
1239
+ for (const slot of this.content().querySelectorAll("[data-item-execution]")) {
1240
+ const html = this.ui.executionBadge(sessions.get(slot.dataset.itemExecution), this.options.sessionUrl);
1241
+ if (slot.innerHTML !== html) slot.innerHTML = html;
1242
+ }
1243
+ return;
1244
+ }
1245
+ preserveBoardScroll(this.content(), () => {
1246
+ this.content().innerHTML = this.ui.boardView(this.board, page, this.query, true, !!_optionalChain([this, 'access', _238 => _238.options, 'access', _239 => _239.service, 'optionalAccess', _240 => _240.automationCatalog]), this.options.sessionUrl);
1247
+ });
1248
+ this.mountHeaderActions();
1249
+ });
1250
+ }
1251
+ async refresh() {
1252
+ const service = this.options.service, generation = ++this.generation, events = this.eventGeneration;
1253
+ if (!service) {
1254
+ this.content().innerHTML = `<p>${escapeHTML(this.ui.label("unavailable"))}</p>`;
1255
+ return;
1256
+ }
1257
+ this.operationStage = "ui.snapshot";
1258
+ const pageRequest = this.boardId ? this.queryPage() : void 0;
1259
+ const itemRequest = this.boardId && this.itemId ? service.item(this.boardId, this.itemId) : void 0;
1260
+ const [boards, actor, initialPage, initialItem] = await Promise.all([_nullishCoalesce(_optionalChain([service, 'access', _241 => _241.catalog, 'optionalCall', _242 => _242()]), () => ( service.list())), service.currentPrincipal(), pageRequest, itemRequest]);
1261
+ if (!this.alive(generation)) return;
1262
+ this.actor = actor;
1263
+ if (!this.boardId && this.options.initialResource === void 0) this.boardId = _nullishCoalesce(_optionalChain([boards, 'access', _243 => _243[0], 'optionalAccess', _244 => _244.id]), () => ( ""));
1264
+ this.board = boards.find((board) => board.id === this.boardId);
1265
+ if (this.boardId && !this.board) throw Object.assign(new Error("Board unavailable"), { code: "BOARD_NOT_FOUND" });
1266
+ if (this.query.execution && this.query.execution !== "all" && !service.queryActivity) throw Object.assign(new Error("Execution Session projection unavailable"), { code: "EXECUTION_SESSION_UNAVAILABLE" });
1267
+ const page = this.board ? await _asyncNullishCoalesce(initialPage, async () => ( await this.queryPage())) : void 0;
1268
+ if (!this.alive(generation)) return;
1269
+ this.page = page;
1270
+ this.invalid = events !== this.eventGeneration;
1271
+ if (this.options.sidebar) this.options.sidebar.innerHTML = this.ui.navigation(boards, this.boardId);
1272
+ this.operationStage = "ui.render";
1273
+ preserveBoardScroll(this.content(), () => {
1274
+ this.content().innerHTML = this.board && page ? this.ui.boardView(this.board, page, this.query, !!service.queryActivity, !!service.automationCatalog, this.options.sessionUrl) : this.ui.catalogView(boards);
1275
+ });
1276
+ if (this.recordKind) await this.showRecords(generation);
1277
+ if (!this.alive(generation)) return;
1278
+ _optionalChain([this, 'access', _245 => _245.releaseNavigation, 'optionalCall', _246 => _246()]);
1279
+ this.releaseNavigation = void 0;
1280
+ const navigation2 = this.content().querySelector("[data-navigation]");
1281
+ if (navigation2 && !this.options.mountTitleBar) this.releaseNavigation = _optionalChain([this, 'access', _247 => _247.options, 'access', _248 => _248.mountNavigation, 'optionalCall', _249 => _249(navigation2)]);
1282
+ if (this.recordId) await this.showRecord(this.recordId, generation);
1283
+ else if (this.itemId) await this.showItem(generation, initialItem);
1284
+ else if (!this.recordKind) void this.refreshActivity(generation);
1285
+ this.mountHeaderActions();
1286
+ this.operationStage = "ui.selection";
1287
+ this.options.selected(this.getActiveResourceId());
1288
+ }
1289
+ async showItem(generation = this.generation, loaded) {
1290
+ const service = this.options.service;
1291
+ if (!service || !this.board || !this.itemId) return;
1292
+ this.operationStage = "ui.item.load";
1293
+ const { item, archive, task } = await _asyncNullishCoalesce(loaded, async () => ( await service.item(this.boardId, this.itemId)));
1294
+ if (!this.alive(generation)) return;
1295
+ this.operationStage = "ui.item.render";
1296
+ const referencesChanged = _optionalChain([this, 'access', _250 => _250.item, 'optionalAccess', _251 => _251.id]) === item.id && JSON.stringify(this.item.externalRefs) !== JSON.stringify(item.externalRefs);
1297
+ if (!this.drawer.open || this.itemArchived !== !!archive || JSON.stringify(this.item) !== JSON.stringify(item)) this.drawer.show(this.ui.detailView(this.board, item, this.actor, !!archive, [], !!service.executionSession, _optionalChain([this, 'access', _252 => _252.options, 'access', _253 => _253.reviewGuidance, 'optionalCall', _254 => _254(this.board, item)]), !!service.itemOperations && !archive && (_nullishCoalesce(_optionalChain([this, 'access', _255 => _255.options, 'access', _256 => _256.itemOperationsAvailable, 'optionalCall', _257 => _257(this.board, item)]), () => ( true))), _nullishCoalesce(task, () => ( _chunkKYNYPKKHcjs.projectKanbanTask.call(void 0, item))), !!service.fixTask), "item:" + item.id);
1298
+ this.item = item;
1299
+ this.itemArchived = !!archive;
1300
+ this.loadItemSections(generation, item, !!archive);
1301
+ const resources = this.panel().querySelector("[data-work-resources]");
1302
+ if (referencesChanged && resources) {
1303
+ resources.dataset.associationRevision = String(Number(_nullishCoalesce(resources.dataset.associationRevision, () => ( 0))) + 1);
1304
+ delete resources.dataset.loaded;
1305
+ if (resources.open) void this.loadWorkResources(false, true);
1306
+ }
1307
+ this.options.selected(this.getActiveResourceId());
1308
+ }
1309
+ loadItemSections(generation, item, archived) {
1310
+ const service = this.options.service, boardId = this.boardId;
1311
+ if (service.executionSession && !this.panel().querySelector("[data-related-sessions]"))
1312
+ this.setSection("[data-related-sessions-section]", `<details data-related-sessions><summary>${escapeHTML(this.ui.label("relatedSessions"))}</summary><button type="button" data-action="refresh-related-sessions">${escapeHTML(this.ui.label("refresh"))}</button><div data-related-sessions-body></div></details>`);
1313
+ if (service.workResources) {
1314
+ const resources = this.panel().querySelector("[data-work-resources]");
1315
+ if (!resources) this.setSection("[data-work-resource-section]", `<details data-work-resources><summary>${escapeHTML(this.ui.label("workResourceTitle"))}</summary><p>${escapeHTML(this.ui.label("workResourceHelp"))}</p><div class="kanban__actions"><button type="button" data-action="refresh-work-resources">${escapeHTML(this.ui.label("refresh"))}</button><button type="button" data-action="inspect-work-resources">${escapeHTML(this.ui.label("inspectWorkResources"))}</button></div><div data-work-resource-body></div></details>`);
1316
+ }
1317
+ void this.supplement("ui.item.events", generation, () => service.events(boardId, 0, 200), (events) => {
1318
+ this.setSection("[data-item-events]", this.ui.itemEventsView(events.filter((event) => event.itemId === item.id).reverse()));
1319
+ }, "[data-item-events]");
1320
+ void this.supplement("ui.item.records", generation, () => service.records(boardId, { itemId: item.id, limit: 10 }), (records) => {
1321
+ this.setSection("[data-related-records]", `<h4>${escapeHTML(this.ui.label("relatedRecords"))} (${records.total})</h4>${this.ui.recordLinks(records, this.board)}<div class="kanban__actions">${this.ui.button("new-log", "newLog")}${this.ui.button("new-decision", "newDecision")}</div>`);
1322
+ }, "[data-related-records]");
1323
+ void this.loadOperations(generation);
1324
+ void this.loadExecutionSections(generation, item, archived, true);
1325
+ if (!service.executionSession) this.loadReferenceSection(generation, item);
1326
+ void this.supplement("ui.item.children", generation, () => this.showChildren(generation), () => {
1327
+ }, "[data-children]");
1328
+ }
1329
+ async loadExecutionSections(generation, item, archived, references = false) {
1330
+ const service = this.options.service, boardId = this.boardId, reads = [];
1331
+ if (service.executionSession) reads.push(this.supplement("ui.item.session", generation, () => service.executionSession(boardId, item.id), (execution) => {
1332
+ this.setSection("[data-execution-session]", this.ui.executionSessionView(execution, !!this.options.executionReference && !archived && !["completed", "cancelled"].includes(item.status) && _optionalChain([item, 'access', _258 => _258.launch, 'optionalAccess', _259 => _259.admittedAt]) === void 0, this.options.sessionUrl));
1333
+ if (references) this.loadReferenceSection(generation, item, new Set(execution.history.map((binding) => binding.sessionId)));
1334
+ }, "[data-execution-session]"));
1335
+ if (_optionalChain([this, 'access', _260 => _260.board, 'optionalAccess', _261 => _261.automation]) && service.analysisStatus) reads.push(this.supplement("ui.item.analysis", generation, () => service.analysisStatus(boardId, item.id), (analysis) => {
1336
+ this.setSection("[data-analysis]", analysisView(this.options.presentation, _nullishCoalesce(analysis, () => ( null))));
1337
+ }, "[data-analysis]"));
1338
+ await Promise.all(reads);
1339
+ }
1340
+ async loadOperations(generation) {
1341
+ const service = this.options.service, item = this.item;
1342
+ if (!_optionalChain([service, 'optionalAccess', _262 => _262.itemOperations]) || !item || !this.panel().querySelector("[data-item-operations]")) return;
1343
+ await this.supplement("ui.item.operations", generation, () => service.itemOperations(this.boardId, item.id), (view) => {
1344
+ this.operations = view;
1345
+ this.setSection("[data-item-operations]", itemOperationsView(this.options.presentation, view, this.options.sessionUrl));
1346
+ }, "[data-item-operations]");
1347
+ }
1348
+ async submitOperation(form, data, binding) {
1349
+ const service = this.options.service, action = form.dataset.operation, itemId = this.item.id;
1350
+ const request = { operationId: form.dataset.resourceId, itemId, action, expectedRevision: form.dataset.revision };
1351
+ const entry = _optionalChain([this, 'access', _263 => _263.operations, 'optionalAccess', _264 => _264.operations, 'access', _265 => _265.find, 'call', _266 => _266((operation) => operation.id === action)]);
1352
+ this.operationStage = binding ? "ui.operation.bind" : "ui.operation.start";
1353
+ const view = binding ? await service.bindItemOperation(this.boardId, { ...request, target: JSON.parse(value(data, "target")) }) : await service.runItemOperation(this.boardId, {
1354
+ ...request,
1355
+ confirmed: data.has("confirmed"),
1356
+ selections: Object.fromEntries((_nullishCoalesce(_optionalChain([entry, 'optionalAccess', _267 => _267.fields]), () => ( []))).map((field2) => [field2.id, value(data, "choice-" + field2.id)]))
1357
+ });
1358
+ this.dirty = false;
1359
+ this.operations = view;
1360
+ _optionalChain([this, 'access', _268 => _268.options, 'access', _269 => _269.committed, 'optionalCall', _270 => _270()]);
1361
+ this.setSection("[data-item-operations]", itemOperationsView(this.options.presentation, view, this.options.sessionUrl));
1362
+ _optionalChain([this, 'access', _271 => _271.options, 'access', _272 => _272.changed, 'optionalCall', _273 => _273()]);
1363
+ const result = view.operations.find((operation) => operation.id === action);
1364
+ if (!binding && _optionalChain([result, 'optionalAccess', _274 => _274.state]) === "succeeded") await this.refresh();
1365
+ if (!binding && _optionalChain([entry, 'optionalAccess', _275 => _275.openSessionOnRun]) && _optionalChain([result, 'optionalAccess', _276 => _276.sessionId])) await this.options.openSession(result.sessionId);
1366
+ }
1367
+ loadReferenceSection(generation, item, skip) {
1368
+ void this.supplement("ui.item.references", generation, () => this.showReferenceLinks(_nullishCoalesce(item.externalRefs, () => ( [])), generation, skip), () => {
1369
+ }, "[data-reference-links]");
1370
+ }
1371
+ async supplement(stage, generation, read, apply, selector) {
1372
+ const started = Date.now(), operationId = _chunk6MN3H3YFcjs.randomUUID.call(void 0, );
1373
+ try {
1374
+ const value2 = await read();
1375
+ if (!this.alive(generation) || this.dirty) return;
1376
+ apply(value2);
1377
+ try {
1378
+ _optionalChain([this, 'access', _277 => _277.options, 'access', _278 => _278.presentation, 'access', _279 => _279.log, 'optionalCall', _280 => _280({ operationId, stage, result: "success", durationMs: Date.now() - started })]);
1379
+ } catch (e3) {
1380
+ }
1381
+ } catch (error) {
1382
+ if (!this.alive(generation)) return;
1383
+ this.reportRead(stage, error, operationId, started);
1384
+ const slot = selector ? this.panel().querySelector(selector) : void 0;
1385
+ if (slot) {
1386
+ const notice = _nullishCoalesce(slot.querySelector("[data-read-error]"), () => ( document.createElement("p")));
1387
+ notice.setAttribute("role", "alert");
1388
+ notice.setAttribute("data-read-error", "");
1389
+ notice.textContent = errorText(error, this.ui.label, stage);
1390
+ if (!notice.parentElement) slot.append(notice);
1391
+ } else this.error(errorText(error, this.ui.label, stage));
1392
+ }
1393
+ }
1394
+ setSection(selector, html) {
1395
+ const slot = this.panel().querySelector(selector);
1396
+ if (slot && slot.innerHTML !== html) slot.innerHTML = html;
1397
+ }
1398
+ reportRead(stage, error, operationId = _chunk6MN3H3YFcjs.randomUUID.call(void 0, ), started = Date.now()) {
1399
+ const raw = _optionalChain([error, 'optionalAccess', _281 => _281.code]);
1400
+ try {
1401
+ _optionalChain([this, 'access', _282 => _282.options, 'access', _283 => _283.presentation, 'access', _284 => _284.log, 'optionalCall', _285 => _285({ operationId, stage, result: "failure", ...failureDetails(error), code: typeof raw === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(raw) ? raw : "KANBAN_OPERATION_FAILED", durationMs: Date.now() - started })]);
1402
+ } catch (e4) {
1403
+ }
1404
+ }
1405
+ async showChildren(generation, cursor) {
1406
+ const { service } = this.options, item = this.item, board = this.board;
1407
+ if (!service || !item || !board || !childType(board, item)) return;
1408
+ const query = { parentId: item.id, view: "all", limit: 50, ...cursor ? { cursor } : {} };
1409
+ const page = await (_nullishCoalesce(_optionalChain([service, 'access', _286 => _286.queryActivity, 'optionalCall', _287 => _287(board.id, query)]), () => ( service.query(board.id, query))));
1410
+ if (!this.alive(generation)) return;
1411
+ this.panel().querySelector("[data-children]").innerHTML = childrenView(this.options.presentation, page, cursor, !["completed", "cancelled"].includes(item.status) && !item.closedAt, this.options.sessionUrl);
1412
+ }
1413
+ async showRecords(generation) {
1414
+ if (!this.recordKind || !this.options.service) return;
1415
+ let page;
1416
+ try {
1417
+ page = await this.options.service.records(this.boardId, { kind: this.recordKind, limit: 50, ...this.recordCursor ? { cursor: this.recordCursor } : {} });
1418
+ } catch (error) {
1419
+ if (_optionalChain([error, 'optionalAccess', _288 => _288.code]) !== "CURSOR_STALE" || !this.recordCursor) throw error;
1420
+ this.recordCursor = void 0;
1421
+ page = await this.options.service.records(this.boardId, { kind: this.recordKind, limit: 50 });
1422
+ }
1423
+ if (!this.alive(generation)) return;
1424
+ this.recordPage = page;
1425
+ this.content().innerHTML = this.ui.recordsView(page, this.recordKind, !!this.recordCursor, this.board);
1426
+ }
1427
+ async showRecord(id, generation = this.generation) {
1428
+ if (!this.options.service) return;
1429
+ const record = await this.options.service.record(this.boardId, id);
1430
+ const executions = record.kind === "decision" ? await _optionalChain([this, 'access', _289 => _289.options, 'access', _290 => _290.service, 'access', _291 => _291.recordExecutions, 'optionalCall', _292 => _292(this.boardId, id)]) : void 0;
1431
+ if (!this.alive(generation)) return;
1432
+ this.recordId = id;
1433
+ this.drawer.show(this.ui.recordDetail(record, this.board, executions), "record:" + id);
1434
+ await this.showReferenceLinks(_nullishCoalesce(record.externalRefs, () => ( [])), generation);
1435
+ this.options.selected(this.getActiveResourceId());
1436
+ }
1437
+ async showReferenceLinks(references, generation, skip = /* @__PURE__ */ new Set()) {
1438
+ if (!this.options.referenceLink) return;
1439
+ const started = Date.now();
1440
+ const results = await Promise.allSettled(references.filter((ref) => !skip.has(ref.id)).map((ref) => this.options.referenceLink(ref)));
1441
+ if (!this.alive(generation)) return;
1442
+ const resolved = results.flatMap((result) => result.status === "fulfilled" && result.value ? [result.value] : []);
1443
+ const links = [...new Map(resolved.map((link) => [link.href, link])).values()];
1444
+ const slot = this.panel().querySelector("[data-reference-links]");
1445
+ if (slot) slot.innerHTML = links.map((link) => linkPair(this.options.presentation, link.href, link.label, link.sessionId)).join("");
1446
+ const errors = results.filter((result) => result.status === "rejected");
1447
+ if (errors.length) {
1448
+ const raw = _optionalChain([errors, 'access', _293 => _293[0], 'access', _294 => _294.reason, 'optionalAccess', _295 => _295.code]), code = typeof raw === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(raw) ? raw : "SESSION_LINK_UNAVAILABLE";
1449
+ if (slot) {
1450
+ const error = document.createElement("p");
1451
+ error.setAttribute("role", "alert");
1452
+ error.textContent = this.ui.label("sessionLinkUnavailable") + " (" + code + ")";
1453
+ slot.append(error);
1454
+ }
1455
+ _optionalChain([this, 'access', _296 => _296.options, 'access', _297 => _297.presentation, 'access', _298 => _298.log, 'optionalCall', _299 => _299({ operationId: _chunk6MN3H3YFcjs.randomUUID.call(void 0, ), stage: "ui.reference-links", result: "failure", code, ...failureDetails(errors[0].reason), durationMs: Date.now() - started })]);
1456
+ }
1457
+ }
1458
+ async loadWorkResources(inspect = false, refresh = false) {
1459
+ const service = this.options.service, boardId = this.boardId, itemId = _optionalChain([this, 'access', _300 => _300.item, 'optionalAccess', _301 => _301.id]);
1460
+ const section = this.panel().querySelector("[data-work-resources]");
1461
+ if (!_optionalChain([service, 'optionalAccess', _302 => _302.workResources]) || !itemId || !section || section.dataset.loading || !refresh && section.dataset.loaded) return;
1462
+ const operationId = _chunk6MN3H3YFcjs.randomUUID.call(void 0, ), started = Date.now(), revision = section.dataset.associationRevision;
1463
+ section.dataset.loading = "true";
1464
+ if (!section.dataset.loaded) this.setSection("[data-work-resource-body]", `<p role="status">${escapeHTML(this.ui.label("loading"))}</p>`);
1465
+ try {
1466
+ const resources = await service.workResources(boardId, itemId, { inspect });
1467
+ if (this.abort.signal.aborted || !section.isConnected || _optionalChain([this, 'access', _303 => _303.item, 'optionalAccess', _304 => _304.id]) !== itemId || this.boardId !== boardId || revision !== section.dataset.associationRevision) return;
1468
+ this.setSection("[data-work-resource-body]", workResourcesView(this.options.presentation, resources));
1469
+ section.dataset.loaded = "true";
1470
+ } catch (error) {
1471
+ if (!section.isConnected || this.abort.signal.aborted) return;
1472
+ this.reportRead("ui.item.work-resources", error, operationId, started);
1473
+ const body = section.querySelector("[data-work-resource-body]"), notice = document.createElement("p");
1474
+ notice.setAttribute("role", "alert");
1475
+ notice.textContent = errorText(error, this.ui.label, "ui.item.work-resources");
1476
+ _optionalChain([body, 'optionalAccess', _305 => _305.append, 'call', _306 => _306(notice)]);
1477
+ } finally {
1478
+ delete section.dataset.loading;
1479
+ if (!this.abort.signal.aborted && section.isConnected && section.open && revision !== section.dataset.associationRevision) void this.loadWorkResources(false, true);
1480
+ }
1481
+ }
1482
+ async loadRelatedSessions(refresh = false) {
1483
+ const service = this.options.service, boardId = this.boardId, itemId = _optionalChain([this, 'access', _307 => _307.item, 'optionalAccess', _308 => _308.id]);
1484
+ const details = this.panel().querySelector("[data-related-sessions]");
1485
+ if (!_optionalChain([service, 'optionalAccess', _309 => _309.executionSession]) || !itemId || !details || details.dataset.loading || !refresh && details.dataset.loaded) return;
1486
+ details.dataset.loading = "true";
1487
+ this.setSection("[data-related-sessions-body]", `<p role="status">${escapeHTML(this.ui.label("loading"))}</p>`);
1488
+ try {
1489
+ await this.supplement("ui.item.related-sessions", this.generation, () => service.executionSession(boardId, itemId, { includeRelated: true }), (detail) => {
1490
+ if (_optionalChain([this, 'access', _310 => _310.item, 'optionalAccess', _311 => _311.id]) !== itemId || this.boardId !== boardId) return;
1491
+ this.setSection("[data-related-sessions-body]", relatedSessionsView(this.options.presentation, detail, this.options.sessionUrl));
1492
+ details.dataset.loaded = "true";
1493
+ }, "[data-related-sessions-body]");
1494
+ } finally {
1495
+ delete details.dataset.loading;
1496
+ }
1497
+ }
1498
+ async click(event) {
1499
+ const target = event.target.closest("button, a");
1500
+ if (!target || target.matches(":disabled")) return;
1501
+ if (target.dataset.action === "refresh-related-sessions") {
1502
+ await this.loadRelatedSessions(true);
1503
+ return;
1504
+ }
1505
+ for (const menu of this.options.editor.querySelectorAll("[data-popover][open]")) if (!menu.contains(target)) menu.open = false;
1506
+ if (target.dataset.session) {
1507
+ event.preventDefault();
1508
+ await this.options.openSession(target.dataset.session);
1509
+ return;
1510
+ }
1511
+ if (this.dirty && !["close-panel", "copy-reference", "copy-link", "copy-resource"].includes(_nullishCoalesce(target.dataset.action, () => ( "")))) throw new Error(this.ui.label("unsaved"));
1512
+ const path = this.navigationTarget(target);
1513
+ if (path && this.options.navigate) {
1514
+ await this.options.navigate(path);
1515
+ return;
1516
+ }
1517
+ if (target.dataset.board) {
1518
+ await this.navigate(target.dataset.board);
1519
+ return;
1520
+ }
1521
+ if (target.dataset.context) {
1522
+ this.recordKind = target.dataset.context === "board" ? void 0 : target.dataset.context;
1523
+ this.recordCursor = void 0;
1524
+ this.closePanel();
1525
+ await this.refresh();
1526
+ return;
1527
+ }
1528
+ if (target.dataset.record) {
1529
+ this.itemId = "";
1530
+ this.item = void 0;
1531
+ await this.showRecord(target.dataset.record);
1532
+ return;
1533
+ }
1534
+ if (target.dataset.item) {
1535
+ this.recordId = "";
1536
+ this.itemId = target.dataset.item;
1537
+ await this.showItem();
1538
+ return;
1539
+ }
1540
+ const action = target.dataset.action;
1541
+ if (action === "continue-execution" && this.item && target.dataset.dispatch) {
1542
+ await this.execute({ type: "continue-execution", previousDispatchId: target.dataset.dispatch, dispatchId: _chunk6MN3H3YFcjs.randomUUID.call(void 0, ) });
1543
+ return;
1544
+ }
1545
+ if (action === "refresh-work-resources" || action === "inspect-work-resources") {
1546
+ await this.loadWorkResources(action === "inspect-work-resources", true);
1547
+ return;
1548
+ }
1549
+ if (action === "cleanup-work-resource" && this.item && _optionalChain([this, 'access', _312 => _312.options, 'access', _313 => _313.service, 'optionalAccess', _314 => _314.cleanupWorkResource])) {
1550
+ if (!globalThis.confirm(this.ui.label("cleanupWorkResourceConfirm"))) return;
1551
+ try {
1552
+ await this.options.service.cleanupWorkResource(this.boardId, { operationId: _chunk6MN3H3YFcjs.randomUUID.call(void 0, ), itemId: this.item.id, resourceId: target.dataset.resource, version: target.dataset.version });
1553
+ } finally {
1554
+ await this.loadWorkResources(false, true);
1555
+ }
1556
+ return;
1557
+ }
1558
+ if (action === "close-panel" || action === "dismiss-panel") {
1559
+ await this.requestClose(action === "close-panel");
1560
+ return;
1561
+ }
1562
+ if (action === "copy-reference" || action === "copy-link" || action === "copy-resource") {
1563
+ await this.ui.copyReference(target.closest("[data-action]"));
1564
+ return;
1565
+ }
1566
+ if (action === "automation" && this.board && _optionalChain([this, 'access', _315 => _315.options, 'access', _316 => _316.service, 'optionalAccess', _317 => _317.automationCatalog])) {
1567
+ this.automationCatalog = await this.options.service.automationCatalog();
1568
+ this.itemId = "";
1569
+ this.drawer.show(automationForm(this.options.presentation, this.board, this.automationCatalog), "automation");
1570
+ this.dirty = true;
1571
+ return;
1572
+ }
1573
+ if (action === "task-edit" && this.item && this.board) {
1574
+ const editId = _chunk6MN3H3YFcjs.randomUUID.call(void 0, ), itemId = this.item.id;
1575
+ await this.execute({ type: "begin-edit", itemId, editId, leaseMs: 3e5 });
1576
+ this.editing = { itemId, editId };
1577
+ const resources = await _optionalChain([this, 'access', _318 => _318.options, 'access', _319 => _319.service, 'optionalAccess', _320 => _320.itemResources, 'optionalCall', _321 => _321(this.boardId)]);
1578
+ if (resources) resources.selected = _optionalChain([this, 'access', _322 => _322.item, 'access', _323 => _323.externalRefs, 'optionalAccess', _324 => _324.find, 'call', _325 => _325((ref) => resources.resources.some((r) => r.reference.system === ref.system && r.reference.id === ref.id))]);
1579
+ this.drawer.show(editTaskForm(this.options.presentation, this.board, this.item, editId, resources), "edit:" + itemId);
1580
+ this.dirty = true;
1581
+ return;
1582
+ }
1583
+ if (action === "task-fix" && this.item && _optionalChain([this, 'access', _326 => _326.options, 'access', _327 => _327.service, 'optionalAccess', _328 => _328.fixTask])) {
1584
+ target.dataset.operationId ??= _chunk6MN3H3YFcjs.randomUUID.call(void 0, );
1585
+ const fix = await this.options.service.fixTask(this.boardId, { itemId: this.item.id, operationId: target.dataset.operationId });
1586
+ await this.options.openSession(fix.sessionId);
1587
+ return;
1588
+ }
1589
+ if (_optionalChain([action, 'optionalAccess', _329 => _329.startsWith, 'call', _330 => _330("task-")]) && this.item) {
1590
+ if (action === "task-cancel") await this.execute({ type: "cancel-item", itemId: this.item.id, reason: this.ui.label("taskAction.cancel") });
1591
+ else if (action === "task-run") await this.execute({ type: "request-run", itemId: this.item.id });
1592
+ else if (action === "task-pause" || action === "task-resume") await this.execute({ type: "request-control", itemId: this.item.id, controlId: _chunk6MN3H3YFcjs.randomUUID.call(void 0, ), action: action === "task-pause" ? "pause" : "resume" });
1593
+ return;
1594
+ }
1595
+ if (action === "reanalyze" && _optionalChain([this, 'access', _331 => _331.item, 'optionalAccess', _332 => _332.planning])) {
1596
+ await this.execute({ type: "retry-analysis", itemId: this.item.id, retryId: _chunk6MN3H3YFcjs.randomUUID.call(void 0, ) });
1597
+ return;
1598
+ }
1599
+ if (action === "new-board") {
1600
+ this.showBoardForm();
1601
+ return;
1602
+ }
1603
+ if (action === "children-next" || action === "children-first") {
1604
+ await this.showChildren(this.generation, action === "children-next" ? target.dataset.cursor : void 0);
1605
+ return;
1606
+ }
1607
+ if (action === "new-child" && this.board && this.item && childType(this.board, this.item)) {
1608
+ await this.showItemForm(this.item);
1609
+ return;
1610
+ }
1611
+ if (action === "new-item" && this.board) {
1612
+ await this.showItemForm();
1613
+ return;
1614
+ }
1615
+ if (action === "new-log" || action === "new-decision") {
1616
+ const resources = action === "new-decision" ? await _optionalChain([this, 'access', _333 => _333.options, 'access', _334 => _334.service, 'optionalAccess', _335 => _335.recordResources, 'optionalCall', _336 => _336()]) : [];
1617
+ this.recordId = "";
1618
+ this.drawer.show(this.ui.recordForm(action === "new-log" ? "log" : "decision", this.board, this.itemId, resources), action);
1619
+ this.dirty = true;
1620
+ return;
1621
+ }
1622
+ if ((action === "run-record" || action === "run-record-separate") && _optionalChain([this, 'access', _337 => _337.options, 'access', _338 => _338.service, 'optionalAccess', _339 => _339.runRecord])) {
1623
+ const record = await this.options.service.record(this.boardId, this.recordId);
1624
+ target.dataset.operationId ??= _chunk6MN3H3YFcjs.randomUUID.call(void 0, );
1625
+ await this.options.service.runRecord(this.boardId, { operationId: target.dataset.operationId, recordId: record.id, expectedRevision: record.revision, placement: action === "run-record-separate" ? "new-board" : "current-board" });
1626
+ _optionalChain([this, 'access', _340 => _340.options, 'access', _341 => _341.committed, 'optionalCall', _342 => _342()]);
1627
+ await this.refresh();
1628
+ return;
1629
+ }
1630
+ if (action === "scroll-left" || action === "scroll-right") {
1631
+ const board = this.content().querySelector(".kanban__columns");
1632
+ if (board) board.scrollLeft += board.clientWidth * (action === "scroll-left" ? -0.8 : 0.8);
1633
+ return;
1634
+ }
1635
+ if (action === "records-next" || action === "records-first") {
1636
+ this.recordCursor = action === "records-next" ? _optionalChain([this, 'access', _343 => _343.recordPage, 'optionalAccess', _344 => _344.nextCursor]) : void 0;
1637
+ await this.refresh();
1638
+ return;
1639
+ }
1640
+ if ((action === "confirm-record" || action === "reject-record") && this.recordId) {
1641
+ await this.execute({ type: "review-record", recordId: this.recordId, accept: action === "confirm-record" });
1642
+ return;
1643
+ }
1644
+ if (action === "next" || action === "first") {
1645
+ this.query.cursor = action === "next" ? _optionalChain([this, 'access', _345 => _345.page, 'optionalAccess', _346 => _346.nextCursor]) : void 0;
1646
+ await this.refresh();
1647
+ return;
1648
+ }
1649
+ if (action === "refresh-item-operations") {
1650
+ this.dirty = false;
1651
+ await this.loadOperations(this.generation);
1652
+ _optionalChain([this, 'access', _347 => _347.options, 'access', _348 => _348.changed, 'optionalCall', _349 => _349()]);
1653
+ return;
1654
+ }
1655
+ if (action === "refresh") {
1656
+ await this.refresh();
1657
+ return;
1658
+ }
1659
+ if (action && this.item) await this.execute(actionCommand(action, this.item));
1660
+ }
1661
+ async openReference(reference) {
1662
+ if (this.dirty) throw new Error(this.ui.label("unsaved"));
1663
+ const target = await this.options.service.resolve(reference);
1664
+ if (this.options.navigate) {
1665
+ const leaf = target.recordId ? "/@record/" + encodeURIComponent(target.recordId) : target.itemId ? "/" + encodeURIComponent(target.itemId) : "";
1666
+ await this.options.navigate(`/board/${encodeURIComponent(target.boardId)}${leaf}`);
1667
+ return;
1668
+ }
1669
+ this.closePanel();
1670
+ this.boardId = target.boardId;
1671
+ this.itemId = _nullishCoalesce(target.itemId, () => ( ""));
1672
+ this.recordId = _nullishCoalesce(target.recordId, () => ( ""));
1673
+ this.recordKind = void 0;
1674
+ this.recordCursor = void 0;
1675
+ this.query = { view: "all", limit: 50 };
1676
+ await this.refresh();
1677
+ _optionalChain([this, 'access', _350 => _350.options, 'access', _351 => _351.activated, 'optionalCall', _352 => _352()]);
1678
+ }
1679
+ async navigate(id) {
1680
+ this.boardId = id;
1681
+ this.itemId = "";
1682
+ this.recordKind = void 0;
1683
+ this.recordCursor = void 0;
1684
+ this.query = { view: "all", limit: 50 };
1685
+ this.closePanel();
1686
+ await this.refresh();
1687
+ }
1688
+ async submit(event) {
1689
+ const form = event.target, kind = form.dataset.form, data = new FormData(form);
1690
+ form.dataset.resourceId ??= _chunk6MN3H3YFcjs.randomUUID.call(void 0, );
1691
+ if (!form.reportValidity()) return;
1692
+ if (kind === "item-operation" || kind === "operation-binding") {
1693
+ await this.submitOperation(form, data, kind === "operation-binding");
1694
+ return;
1695
+ }
1696
+ if (kind === "reference") {
1697
+ await this.openReference(value(data, "reference"));
1698
+ return;
1699
+ }
1700
+ if (kind === "filter") {
1701
+ if (this.dirty) throw new Error(this.ui.label("unsaved"));
1702
+ this.query = { phase: value(data, "phase") || void 0, view: value(data, "view"), execution: value(data, "execution") || "all", search: value(data, "search"), area: value(data, "area") || void 0, milestone: value(data, "milestone") || void 0, limit: 50 };
1703
+ await this.refresh();
1704
+ return;
1705
+ }
1706
+ if (kind === "board" && this.actor && this.options.service) {
1707
+ const created = await this.options.service.create(boardInput(data, this.actor, form.dataset.resourceId));
1708
+ _optionalChain([this, 'access', _353 => _353.options, 'access', _354 => _354.committed, 'optionalCall', _355 => _355()]);
1709
+ this.dirty = false;
1710
+ await this.navigate(created.id);
1711
+ return;
1712
+ }
1713
+ if (kind === "automation" && this.automationCatalog) {
1714
+ await this.execute({ type: "configure-automation", settings: {
1715
+ enabled: data.has("enabled"),
1716
+ autoAccept: data.has("autoAccept"),
1717
+ ...value(data, "preparation") ? {
1718
+ preparation: JSON.parse(value(data, "preparation")),
1719
+ preparationOptions: Object.fromEntries([...data.entries()].filter(([key]) => key.startsWith("preparation-option-")).map(([key, value2]) => [key.slice("preparation-option-".length), String(value2)]))
1720
+ } : {},
1721
+ planner: JSON.parse(value(data, "planner")),
1722
+ executor: JSON.parse(value(data, "executor")),
1723
+ coordinator: this.automationCatalog.coordinator,
1724
+ worker: this.automationCatalog.worker
1725
+ } });
1726
+ return;
1727
+ }
1728
+ if (kind === "task-reply" && this.item && _optionalChain([this, 'access', _356 => _356.options, 'access', _357 => _357.service, 'optionalAccess', _358 => _358.replyTask])) {
1729
+ await this.options.service.replyTask(this.boardId, { itemId: this.item.id, attemptId: value(data, "attemptId"), replyTo: value(data, "replyTo"), operationId: form.dataset.resourceId, content: value(data, "content") });
1730
+ this.dirty = false;
1731
+ await this.refresh();
1732
+ return;
1733
+ }
1734
+ if (kind === "save-edit" && this.item) {
1735
+ const planning = formCommand("plan", data, this.board, this.item);
1736
+ if (planning.type !== "plan-item") return;
1737
+ await this.execute({ type: "save-edit", launchMode: value(data, "launchMode"), itemId: this.item.id, editId: value(data, "editId"), title: value(data, "title"), responsibility: value(data, "responsibility"), acceptance: value(data, "acceptance"), planning: planning.planning, externalRefs: [...(_nullishCoalesce(this.item.externalRefs, () => ( []))).filter((ref) => !value(data, "itemResource") || ref.system !== JSON.parse(value(data, "itemResource")).system), ...value(data, "itemResource") ? [JSON.parse(value(data, "itemResource"))] : []] });
1738
+ this.editing = void 0;
1739
+ return;
1740
+ }
1741
+ if (kind === "amendment") data.set("amendmentId", _nullishCoalesce(form.dataset.amendment, () => ( "")));
1742
+ if (kind === "record-link" && this.recordId) {
1743
+ await this.execute({ type: "link-record", recordId: this.recordId, itemId: value(data, "itemId") });
1744
+ return;
1745
+ }
1746
+ if (kind && this.board) {
1747
+ if (kind === "execution-session" && this.item && this.options.executionReference) {
1748
+ await this.execute({ type: "link-reference", itemId: this.item.id, reference: this.options.executionReference(value(data, "executionSession"), form.dataset.resourceId) });
1749
+ return;
1750
+ }
1751
+ await this.execute(formCommand(kind, data, this.board, this.item, form.dataset.resourceId));
1752
+ }
1753
+ }
1754
+ async showItemForm(parent) {
1755
+ const board = this.board, generation = this.generation;
1756
+ if (!board) return;
1757
+ const resources = await _optionalChain([this, 'access', _359 => _359.options, 'access', _360 => _360.service, 'optionalAccess', _361 => _361.itemResources, 'optionalCall', _362 => _362(board.id, _optionalChain([parent, 'optionalAccess', _363 => _363.id]))]);
1758
+ if (this.abort.signal.aborted || generation !== this.generation) return;
1759
+ this.itemId = "";
1760
+ this.drawer.show(this.ui.newItemForm(board, parent, resources), parent ? "new-child" : "new-item");
1761
+ this.dirty = true;
1762
+ }
1763
+ async execute(command) {
1764
+ if (!this.board || !this.options.service) return;
1765
+ if (command.type === "review") {
1766
+ const workflow = await this.options.service.workflow(this.board.id);
1767
+ const dispatch = Object.values(workflow.dispatches).find((dispatch2) => dispatch2.itemId === ("itemId" in command ? command.itemId : "") && dispatch2.outcome === "succeeded");
1768
+ if (dispatch) command = { type: "accept-execution", dispatchId: dispatch.id, accept: command.accept };
1769
+ }
1770
+ const fingerprint = JSON.stringify(command);
1771
+ if (this.pending && (this.pending.boardId !== this.board.id || this.pending.fingerprint !== fingerprint)) throw Object.assign(new Error("Pending write"), { code: "WRITE_OUTCOME_UNKNOWN" });
1772
+ this.pending ??= { boardId: this.board.id, fingerprint, request: { operationId: _chunk6MN3H3YFcjs.randomUUID.call(void 0, ), expectedRevision: this.board.revision, command } };
1773
+ try {
1774
+ await this.options.service.execute(this.board.id, this.pending.request);
1775
+ } catch (error) {
1776
+ if (_optionalChain([error, 'optionalAccess', _364 => _364.name]) === "KanbanError" || _optionalChain([error, 'optionalAccess', _365 => _365.outcome]) === "not-committed") this.pending = void 0;
1777
+ throw error;
1778
+ }
1779
+ this.pending = void 0;
1780
+ _optionalChain([this, 'access', _366 => _366.options, 'access', _367 => _367.committed, 'optionalCall', _368 => _368()]);
1781
+ if (command.type === "configure-automation") this.closePanel();
1782
+ this.dirty = false;
1783
+ this.query.cursor = void 0;
1784
+ this.recordCursor = void 0;
1785
+ if (command.type === "create-item") this.itemId = command.item.id;
1786
+ if (command.type === "append-record") {
1787
+ this.recordId = command.record.id;
1788
+ this.itemId = "";
1789
+ this.item = void 0;
1790
+ }
1791
+ await this.refresh();
1792
+ }
1793
+ async run(action) {
1794
+ if (this.busy || this.abort.signal.aborted) return;
1795
+ const operationId = _chunk6MN3H3YFcjs.randomUUID.call(void 0, ), started = Date.now();
1796
+ this.operationStage = "ui.operation";
1797
+ this.generation++;
1798
+ this.busy = true;
1799
+ this.status(this.ui.label("loading"));
1800
+ this.error("");
1801
+ this.options.editor.setAttribute("aria-busy", "true");
1802
+ let succeeded = false;
1803
+ try {
1804
+ await action();
1805
+ succeeded = true;
1806
+ } catch (error) {
1807
+ const raw = _optionalChain([error, 'optionalAccess', _369 => _369.code]);
1808
+ try {
1809
+ _optionalChain([this, 'access', _370 => _370.options, 'access', _371 => _371.presentation, 'access', _372 => _372.log, 'optionalCall', _373 => _373({ operationId, stage: this.operationStage, result: "failure", ...failureDetails(error), code: typeof raw === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(raw) ? raw : "KANBAN_OPERATION_FAILED", durationMs: Date.now() - started })]);
1810
+ } catch (e5) {
1811
+ }
1812
+ if (!this.abort.signal.aborted) this.error(errorText(error, this.ui.label, this.operationStage));
1813
+ } finally {
1814
+ this.busy = false;
1815
+ if (!this.abort.signal.aborted) {
1816
+ this.status("");
1817
+ this.options.editor.removeAttribute("aria-busy");
1818
+ this.options.editor.inert = false;
1819
+ this.mountHeaderActions();
1820
+ this.drawer.flushFocus();
1821
+ _optionalChain([this, 'access', _374 => _374.options, 'access', _375 => _375.changed, 'optionalCall', _376 => _376()]);
1822
+ if (succeeded && this.invalid && this.visible && !this.dirty) this.refreshInBackground();
1823
+ }
1824
+ }
1825
+ }
1826
+ navigationTarget(target) {
1827
+ if (target.dataset.board) return `/board/${encodeURIComponent(target.dataset.board)}`;
1828
+ const board = `/board/${encodeURIComponent(this.boardId)}`;
1829
+ if (target.dataset.item) return `${board}/${encodeURIComponent(target.dataset.item)}`;
1830
+ if (target.dataset.record) return `${board}/@record/${encodeURIComponent(target.dataset.record)}`;
1831
+ if (target.dataset.context) return board + (target.dataset.context === "board" ? "" : "/@" + target.dataset.context);
1832
+ }
1833
+ mountHeaderActions() {
1834
+ for (const [slot, release] of this.windowMounts) if (!this.options.editor.contains(slot)) {
1835
+ release();
1836
+ this.windowMounts.delete(slot);
1837
+ }
1838
+ if (this.options.mountTitleBar) {
1839
+ for (const header of this.options.editor.querySelectorAll("[data-titlebar]")) if (!this.windowMounts.has(header)) this.mountTitleBar(header);
1840
+ return;
1841
+ }
1842
+ for (const slot of this.options.editor.querySelectorAll("[data-window-navigation]")) {
1843
+ if (!this.windowMounts.has(slot) && this.options.mountWindowList) this.windowMounts.set(slot, this.options.mountWindowList(slot));
1844
+ }
1845
+ for (const header of this.options.editor.querySelectorAll("[data-titlebar]")) if (!this.windowMounts.has(header)) this.mountDefaultTitleBar(header);
1846
+ }
1847
+ mountDefaultTitleBar(header) {
1848
+ const back = _nullishCoalesce(header.querySelector("[data-titlebar-back]"), () => ( void 0));
1849
+ const title = _nullishCoalesce(header.querySelector("[data-titlebar-title]"), () => ( void 0));
1850
+ _optionalChain([back, 'optionalAccess', _377 => _377.classList, 'access', _378 => _378.add, 'call', _379 => _379("ui-nav-button")]);
1851
+ _optionalChain([header, 'access', _380 => _380.querySelector, 'call', _381 => _381(".kanban__menu > summary"), 'optionalAccess', _382 => _382.classList, 'access', _383 => _383.add, 'call', _384 => _384("ui-nav-button")]);
1852
+ const view = _uicommon.mountTitleBar.call(void 0, header, {
1853
+ title,
1854
+ back,
1855
+ leading: [...header.querySelectorAll("[data-navigation] > button")],
1856
+ trailing: [...header.querySelectorAll("[data-window-navigation]")],
1857
+ actions: [...header.querySelectorAll(".kanban__desktop-create, :scope > [data-action=new-board]")],
1858
+ menu: _nullishCoalesce(header.querySelector(".kanban__menu"), () => ( void 0))
1859
+ });
1860
+ view.element.dataset.titlebarSlot = "";
1861
+ header.dataset.titlebarHost = "true";
1862
+ view.update({ title: _nullishCoalesce(_optionalChain([title, 'optionalAccess', _385 => _385.textContent]), () => ( "")) });
1863
+ this.windowMounts.set(header, () => {
1864
+ view.destroy();
1865
+ delete header.dataset.titlebarHost;
1866
+ });
1867
+ }
1868
+ mountTitleBar(header) {
1869
+ const slot = document.createElement("div");
1870
+ slot.dataset.titlebarSlot = "";
1871
+ header.prepend(slot);
1872
+ header.dataset.titlebarHost = "true";
1873
+ const release = this.options.mountTitleBar(slot, {
1874
+ title: _nullishCoalesce(header.querySelector("[data-titlebar-title]"), () => ( void 0)),
1875
+ back: _nullishCoalesce(header.querySelector("[data-titlebar-back]"), () => ( void 0)),
1876
+ menu: _nullishCoalesce(header.querySelector(".kanban__menu"), () => ( void 0)),
1877
+ actions: [...header.querySelectorAll(".kanban__desktop-create, :scope > [data-action=new-board]")]
1878
+ });
1879
+ this.windowMounts.set(header, () => {
1880
+ release();
1881
+ slot.remove();
1882
+ delete header.dataset.titlebarHost;
1883
+ });
1884
+ }
1885
+ closePanel() {
1886
+ this.dirty = false;
1887
+ this.itemId = "";
1888
+ this.recordId = "";
1889
+ this.item = void 0;
1890
+ this.drawer.close();
1891
+ }
1892
+ async requestClose(explicit) {
1893
+ if (this.editing && explicit) {
1894
+ await this.execute({ type: "end-edit", ...this.editing });
1895
+ this.editing = void 0;
1896
+ }
1897
+ if (this.dirty && !explicit) throw new Error(this.ui.label("unsaved"));
1898
+ if (this.options.navigate && this.boardId) {
1899
+ if (this.dirty) this.closePanel();
1900
+ await this.options.navigate(`/board/${encodeURIComponent(this.boardId)}`);
1901
+ return;
1902
+ }
1903
+ this.closePanel();
1904
+ void this.refreshActivity(this.generation);
1905
+ this.options.selected(this.getActiveResourceId());
1906
+ }
1907
+ alive(generation) {
1908
+ return generation === this.generation && !this.abort.signal.aborted;
1909
+ }
1910
+ content() {
1911
+ return this.options.editor.querySelector("[data-content]");
1912
+ }
1913
+ panel() {
1914
+ return this.options.editor.querySelector("[data-panel]");
1915
+ }
1916
+ status(message) {
1917
+ this.options.editor.querySelector("[data-status]").textContent = message;
1918
+ }
1919
+ error(message) {
1920
+ const error = this.options.editor.querySelector("[data-error]");
1921
+ error.textContent = message;
1922
+ error.hidden = _nullishCoalesce(_optionalChain([this, 'access', _386 => _386.drawer, 'optionalAccess', _387 => _387.open]), () => ( false));
1923
+ const panelError = this.options.editor.querySelector("[data-panel-error]");
1924
+ if (panelError) panelError.textContent = message;
1925
+ const executionError = this.options.editor.querySelector("[data-execution-error]");
1926
+ if (executionError) {
1927
+ executionError.textContent = message;
1928
+ executionError.hidden = !message;
1929
+ if (message) {
1930
+ const panel = this.panel(), bottom = executionError.getBoundingClientRect().bottom, edge = panel.getBoundingClientRect().bottom;
1931
+ if (bottom > edge) panel.scrollTop += bottom - edge + 12;
1932
+ }
1933
+ }
1934
+ this.options.editor.querySelector("[data-retry]").hidden = !message || (_nullishCoalesce(_optionalChain([this, 'access', _388 => _388.drawer, 'optionalAccess', _389 => _389.open]), () => ( false)));
1935
+ }
1936
+ }, _class4);
1937
+ function errorText(error, label, stage) {
1938
+ const code = _optionalChain([error, 'optionalAccess', _390 => _390.outcome]) === "unknown" ? "WRITE_OUTCOME_UNKNOWN" : _optionalChain([error, 'optionalAccess', _391 => _391.code]);
1939
+ if (code === "REVISION_CONFLICT" || code === "CURSOR_STALE") return label("conflict");
1940
+ if (error instanceof Error && error.message === label("unsaved")) return error.message;
1941
+ const safe = typeof code === "string" && /^[A-Z_]{1,80}$/.test(code) ? code : "KANBAN_OPERATION_FAILED";
1942
+ const translated = label("errors." + safe);
1943
+ const details = safe === "KANBAN_OPERATION_FAILED" ? failureDetails(error) : {};
1944
+ const diagnostic = details.errorName ? `
1945
+ ${label("diagnostic")}: ${stage} \xB7 ${details.errorName}${details.source ? " \xB7 " + details.source : ""}` : "";
1946
+ return (translated !== "kanban.errors." + safe && translated !== "errors." + safe ? `${translated} (${safe})` : `${label("failed")}: ${safe}`) + diagnostic;
1947
+ }
1948
+ async function mountKanban(container, options) {
1949
+ const sidebar = document.createElement("aside"), editor = document.createElement("main");
1950
+ container.classList.add("kanban-layout");
1951
+ container.replaceChildren(sidebar, editor);
1952
+ const view = new KanbanWorkbench({ ...options, sidebar, editor }, container);
1953
+ await view.start();
1954
+ return view;
1955
+ }
1956
+
1957
+
1958
+
1959
+
1960
+ exports.KanbanSuggestionPicker = KanbanSuggestionPicker; exports.KanbanWorkbench = KanbanWorkbench; exports.mountKanban = mountKanban;
1961
+ //# sourceMappingURL=index.cjs.map