@grifhinz/logics-manager 2.2.0 → 2.3.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.
- package/README.md +95 -1
- package/VERSION +1 -1
- package/clients/README.md +9 -0
- package/clients/shared-web/media/css/board.css +658 -0
- package/clients/shared-web/media/css/details.css +457 -0
- package/clients/shared-web/media/css/layout.css +123 -0
- package/clients/shared-web/media/css/toolbar.css +576 -0
- package/clients/shared-web/media/harnessApi.js +324 -0
- package/clients/shared-web/media/hostApi.js +213 -0
- package/clients/shared-web/media/hostApiContract.js +55 -0
- package/clients/shared-web/media/icon.png +0 -0
- package/clients/shared-web/media/layoutController.js +246 -0
- package/clients/shared-web/media/logics.svg +7 -0
- package/clients/shared-web/media/logicsModel.js +910 -0
- package/clients/shared-web/media/main.css +112 -0
- package/clients/shared-web/media/main.js +3 -0
- package/clients/shared-web/media/mainApp.js +1005 -0
- package/clients/shared-web/media/mainCore.js +604 -0
- package/clients/shared-web/media/mainInteractionHandlers.js +324 -0
- package/clients/shared-web/media/mainInteractions.js +378 -0
- package/clients/shared-web/media/renderBoard.js +3 -0
- package/clients/shared-web/media/renderBoardApp.js +1339 -0
- package/clients/shared-web/media/renderDetails.js +685 -0
- package/clients/shared-web/media/renderMarkdown.js +449 -0
- package/clients/shared-web/media/toolsPanelLayout.js +172 -0
- package/clients/shared-web/media/uiStatus.js +54 -0
- package/clients/shared-web/media/webviewChrome.js +405 -0
- package/clients/shared-web/media/webviewPersistence.js +116 -0
- package/clients/shared-web/media/webviewSelectors.js +491 -0
- package/clients/viewer/README.md +5 -0
- package/clients/viewer/browser-host.js +847 -0
- package/clients/viewer/index.html +237 -0
- package/clients/viewer/viewer.css +433 -0
- package/logics_manager/assist.py +9 -142
- package/logics_manager/assist_handoff.py +132 -0
- package/logics_manager/assist_surface.py +38 -0
- package/logics_manager/cli.py +78 -5
- package/logics_manager/flow.py +126 -24
- package/logics_manager/flow_evidence.py +63 -0
- package/logics_manager/update_check.py +138 -0
- package/logics_manager/viewer.py +533 -0
- package/package.json +12 -6
- package/pyproject.toml +1 -1
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
function escapeHtml(value) {
|
|
3
|
+
return String(value)
|
|
4
|
+
.replace(/&/g, "&")
|
|
5
|
+
.replace(/</g, "<")
|
|
6
|
+
.replace(/>/g, ">")
|
|
7
|
+
.replace(/"/g, """)
|
|
8
|
+
.replace(/'/g, "'");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function renderInlineMarkdown(value) {
|
|
12
|
+
const codeSpans = [];
|
|
13
|
+
const withPlaceholders = String(value).replace(/`([^`]+)`/g, (_match, code) => {
|
|
14
|
+
const placeholder = `@@CODE_SPAN_${codeSpans.length}@@`;
|
|
15
|
+
codeSpans.push(code);
|
|
16
|
+
return placeholder;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
let rendered = escapeHtml(withPlaceholders);
|
|
20
|
+
rendered = rendered.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => {
|
|
21
|
+
return `<a href="${escapeHtml(String(href).trim())}">${escapeHtml(String(label).trim())}</a>`;
|
|
22
|
+
});
|
|
23
|
+
rendered = rendered.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
24
|
+
rendered = rendered.replace(/(^|[^*])\*([^*]+)\*/g, "$1<em>$2</em>");
|
|
25
|
+
rendered = rendered.replace(/~~([^~]+)~~/g, "<s>$1</s>");
|
|
26
|
+
|
|
27
|
+
codeSpans.forEach((code, index) => {
|
|
28
|
+
rendered = rendered.replace(`@@CODE_SPAN_${index}@@`, `<code>${escapeHtml(String(code))}</code>`);
|
|
29
|
+
});
|
|
30
|
+
return rendered;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function splitTableCells(line) {
|
|
34
|
+
const normalized = String(line || "")
|
|
35
|
+
.trim()
|
|
36
|
+
.replace(/^\|/, "")
|
|
37
|
+
.replace(/\|$/, "");
|
|
38
|
+
const cells = [];
|
|
39
|
+
let current = "";
|
|
40
|
+
let escaping = false;
|
|
41
|
+
|
|
42
|
+
for (let index = 0; index < normalized.length; index += 1) {
|
|
43
|
+
const char = normalized[index];
|
|
44
|
+
if (escaping) {
|
|
45
|
+
current += char;
|
|
46
|
+
escaping = false;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (char === "\\") {
|
|
50
|
+
escaping = true;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (char === "|") {
|
|
54
|
+
cells.push(current.trim());
|
|
55
|
+
current = "";
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
current += char;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
cells.push(current.trim());
|
|
62
|
+
return cells;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseTableAlignment(cell) {
|
|
66
|
+
const trimmed = String(cell || "").trim();
|
|
67
|
+
if (!/^:?-{3,}:?$/.test(trimmed)) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const hasLeft = trimmed.startsWith(":");
|
|
71
|
+
const hasRight = trimmed.endsWith(":");
|
|
72
|
+
if (hasLeft && hasRight) {
|
|
73
|
+
return "center";
|
|
74
|
+
}
|
|
75
|
+
if (hasRight) {
|
|
76
|
+
return "right";
|
|
77
|
+
}
|
|
78
|
+
if (hasLeft) {
|
|
79
|
+
return "left";
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isTableDividerRow(line) {
|
|
85
|
+
const cells = splitTableCells(line);
|
|
86
|
+
return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(String(cell || "").trim()));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function parseTableBlock(lines, index) {
|
|
90
|
+
const headerLine = lines[index] || "";
|
|
91
|
+
const dividerLine = lines[index + 1] || "";
|
|
92
|
+
if (!headerLine.includes("|") || !isTableDividerRow(dividerLine)) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const headerCells = splitTableCells(headerLine);
|
|
97
|
+
const dividerCells = splitTableCells(dividerLine);
|
|
98
|
+
if (headerCells.length === 0 || headerCells.length !== dividerCells.length) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const bodyRows = [];
|
|
103
|
+
let cursor = index + 2;
|
|
104
|
+
while (cursor < lines.length) {
|
|
105
|
+
const line = lines[cursor];
|
|
106
|
+
if (line.trim() === "" || !line.includes("|")) {
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
const cells = splitTableCells(line);
|
|
110
|
+
if (cells.length === 0) {
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
bodyRows.push(cells);
|
|
114
|
+
cursor += 1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
headerCells,
|
|
119
|
+
alignments: dividerCells.map((cell) => parseTableAlignment(cell)),
|
|
120
|
+
bodyRows
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function renderTableBlock(table) {
|
|
125
|
+
const columnCount = Math.max(
|
|
126
|
+
table.headerCells.length,
|
|
127
|
+
table.alignments.length,
|
|
128
|
+
...table.bodyRows.map((row) => row.length)
|
|
129
|
+
);
|
|
130
|
+
const columns = Array.from({ length: columnCount }, (_value, columnIndex) => ({
|
|
131
|
+
alignment: table.alignments[columnIndex] || null,
|
|
132
|
+
header: table.headerCells[columnIndex] || ""
|
|
133
|
+
}));
|
|
134
|
+
|
|
135
|
+
const renderCell = (tagName, text, alignment) => {
|
|
136
|
+
const style = alignment ? ` style="text-align:${alignment}"` : "";
|
|
137
|
+
const scope = tagName === "th" ? ' scope="col"' : "";
|
|
138
|
+
return `<${tagName}${scope}${style}>${renderInlineMarkdown(text)}</${tagName}>`;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const rows = table.bodyRows
|
|
142
|
+
.map((row) => {
|
|
143
|
+
const cells = Array.from({ length: columnCount }, (_value, columnIndex) =>
|
|
144
|
+
renderCell("td", row[columnIndex] || "", columns[columnIndex].alignment)
|
|
145
|
+
).join("");
|
|
146
|
+
return `<tr>${cells}</tr>`;
|
|
147
|
+
})
|
|
148
|
+
.join("");
|
|
149
|
+
|
|
150
|
+
return [
|
|
151
|
+
'<div class="markdown-preview__table-wrap"><table>',
|
|
152
|
+
`<thead><tr>${columns.map((column) => renderCell("th", column.header, column.alignment)).join("")}</tr></thead>`,
|
|
153
|
+
`<tbody>${rows}</tbody>`,
|
|
154
|
+
"</table></div>"
|
|
155
|
+
].join("");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function renderMarkdownToHtml(markdown) {
|
|
159
|
+
const lines = String(markdown || "").replace(/\r\n/g, "\n").split("\n");
|
|
160
|
+
const html = [];
|
|
161
|
+
let paragraph = [];
|
|
162
|
+
let listType = null;
|
|
163
|
+
let listItems = [];
|
|
164
|
+
let codeFence = null;
|
|
165
|
+
|
|
166
|
+
function flushParagraph() {
|
|
167
|
+
if (paragraph.length === 0) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const content = paragraph.join(" ").trim();
|
|
171
|
+
if (content) {
|
|
172
|
+
html.push(`<p>${renderInlineMarkdown(content)}</p>`);
|
|
173
|
+
}
|
|
174
|
+
paragraph = [];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function flushList() {
|
|
178
|
+
if (!listType || listItems.length === 0) {
|
|
179
|
+
listType = null;
|
|
180
|
+
listItems = [];
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
html.push(`<${listType}>`);
|
|
184
|
+
listItems.forEach((item) => {
|
|
185
|
+
html.push(renderListItem(item));
|
|
186
|
+
});
|
|
187
|
+
html.push(`</${listType}>`);
|
|
188
|
+
listType = null;
|
|
189
|
+
listItems = [];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function renderListItem(item) {
|
|
193
|
+
if (!item.checkbox) {
|
|
194
|
+
return `<li>${renderInlineMarkdown(item.text)}</li>`;
|
|
195
|
+
}
|
|
196
|
+
const checked = item.checked ? " checked" : "";
|
|
197
|
+
return `<li class="markdown-preview__task-item"><label class="markdown-preview__task-label"><input class="markdown-preview__task-checkbox" type="checkbox" disabled${checked} /><span>${renderInlineMarkdown(item.text)}</span></label></li>`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function flushCodeFence() {
|
|
201
|
+
if (!codeFence) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const language = String(codeFence.language || "").toLowerCase();
|
|
205
|
+
const content = codeFence.lines.join("\n");
|
|
206
|
+
if (language === "mermaid") {
|
|
207
|
+
html.push(
|
|
208
|
+
`<section class="markdown-preview__diagram"><pre class="mermaid">${escapeHtml(content)}</pre><div class="markdown-preview__mermaid-fallback" hidden>Mermaid preview unavailable. Raw diagram source shown below.</div></section>`
|
|
209
|
+
);
|
|
210
|
+
} else {
|
|
211
|
+
const languageClass = language ? ` class="language-${escapeHtml(language)}"` : "";
|
|
212
|
+
html.push(`<pre><code${languageClass}>${escapeHtml(content)}</code></pre>`);
|
|
213
|
+
}
|
|
214
|
+
codeFence = null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
218
|
+
const rawLine = lines[index];
|
|
219
|
+
const line = rawLine.replace(/\t/g, " ");
|
|
220
|
+
|
|
221
|
+
if (codeFence) {
|
|
222
|
+
if (line.trimStart().startsWith("```")) {
|
|
223
|
+
flushCodeFence();
|
|
224
|
+
} else {
|
|
225
|
+
codeFence.lines.push(rawLine);
|
|
226
|
+
}
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const fenceMatch = line.match(/^```([a-zA-Z0-9_-]+)?\s*$/);
|
|
231
|
+
if (fenceMatch) {
|
|
232
|
+
flushParagraph();
|
|
233
|
+
flushList();
|
|
234
|
+
codeFence = {
|
|
235
|
+
language: fenceMatch[1] || "",
|
|
236
|
+
lines: []
|
|
237
|
+
};
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (line.trim() === "") {
|
|
242
|
+
flushParagraph();
|
|
243
|
+
flushList();
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.*)$/);
|
|
248
|
+
if (headingMatch) {
|
|
249
|
+
flushParagraph();
|
|
250
|
+
flushList();
|
|
251
|
+
const level = headingMatch[1].length;
|
|
252
|
+
html.push(`<h${level}>${renderInlineMarkdown(headingMatch[2].trim())}</h${level}>`);
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const tableBlock = parseTableBlock(lines, index);
|
|
257
|
+
if (tableBlock) {
|
|
258
|
+
flushParagraph();
|
|
259
|
+
flushList();
|
|
260
|
+
html.push(renderTableBlock(tableBlock));
|
|
261
|
+
index += tableBlock.bodyRows.length + 1;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const taskMatch = line.match(/^\s*-\s+\[( |x|X)\]\s+(.*)$/);
|
|
266
|
+
if (taskMatch) {
|
|
267
|
+
flushParagraph();
|
|
268
|
+
if (listType && listType !== "ul") {
|
|
269
|
+
flushList();
|
|
270
|
+
}
|
|
271
|
+
listType = "ul";
|
|
272
|
+
listItems.push({
|
|
273
|
+
checkbox: true,
|
|
274
|
+
checked: taskMatch[1].toLowerCase() === "x",
|
|
275
|
+
text: taskMatch[2]
|
|
276
|
+
});
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const unorderedMatch = line.match(/^\s*-\s+(.*)$/);
|
|
281
|
+
if (unorderedMatch) {
|
|
282
|
+
flushParagraph();
|
|
283
|
+
if (listType && listType !== "ul") {
|
|
284
|
+
flushList();
|
|
285
|
+
}
|
|
286
|
+
listType = "ul";
|
|
287
|
+
listItems.push({ text: unorderedMatch[1] });
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const orderedMatch = line.match(/^\s*\d+\.\s+(.*)$/);
|
|
292
|
+
if (orderedMatch) {
|
|
293
|
+
flushParagraph();
|
|
294
|
+
if (listType && listType !== "ol") {
|
|
295
|
+
flushList();
|
|
296
|
+
}
|
|
297
|
+
listType = "ol";
|
|
298
|
+
listItems.push({ text: orderedMatch[1] });
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
flushList();
|
|
303
|
+
paragraph.push(line.trim());
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
flushParagraph();
|
|
307
|
+
flushList();
|
|
308
|
+
flushCodeFence();
|
|
309
|
+
return html.join("\n");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function buildReadPreviewDocument(item, sourceLabel, content) {
|
|
313
|
+
const safeTitle = escapeHtml(item && item.title ? item.title : "Logics item");
|
|
314
|
+
const safeSourceLabel = escapeHtml(sourceLabel || "unknown");
|
|
315
|
+
const renderedHtml = renderMarkdownToHtml(stripLeadingDocumentFrontMatter(content || "", item || {}));
|
|
316
|
+
return `<!doctype html><html lang="en"><head><meta charset="UTF-8" /><title>${safeTitle}</title><style>
|
|
317
|
+
body{margin:0;font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;line-height:1.6;background:linear-gradient(180deg,#f8fafc 0%,#eef2ff 100%);color:#0f172a;}
|
|
318
|
+
.preview{max-width:980px;margin:0 auto;padding:24px 24px 48px;}
|
|
319
|
+
.preview__header{margin-bottom:24px;padding:18px 20px;border:1px solid rgba(148,163,184,.35);border-radius:16px;background:rgba(255,255,255,.75);backdrop-filter:blur(8px);}
|
|
320
|
+
.preview__title{margin:0 0 4px;font-size:32px;line-height:1.1;}
|
|
321
|
+
.preview__source{margin:0;color:#475569;font-size:14px;}
|
|
322
|
+
.preview__body{padding:24px 28px;border-radius:18px;background:#fff;border:1px solid rgba(148,163,184,.28);box-shadow:0 18px 40px rgba(15,23,42,.08);}
|
|
323
|
+
.preview__body h1,.preview__body h2,.preview__body h3,.preview__body h4{line-height:1.15;margin:1.35em 0 .55em;}
|
|
324
|
+
.preview__body h1:first-child,.preview__body h2:first-child,.preview__body h3:first-child{margin-top:0;}
|
|
325
|
+
.preview__body pre{overflow-x:auto;padding:16px;border-radius:14px;background:#e2e8f0;border:1px solid rgba(148,163,184,.35);}
|
|
326
|
+
.preview__body code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.92em;}
|
|
327
|
+
.preview__body p code,.preview__body li code{padding:2px 6px;border-radius:6px;background:#e2e8f0;}
|
|
328
|
+
.preview__body .markdown-preview__task-item{margin:.15em 0;}
|
|
329
|
+
.preview__body .markdown-preview__task-label{display:inline-flex;align-items:flex-start;gap:.65em;}
|
|
330
|
+
.preview__body .markdown-preview__task-checkbox{margin-top:.2em;width:1rem;height:1rem;flex:0 0 auto;accent-color:#0369a1;}
|
|
331
|
+
.preview__body .markdown-preview__table-wrap{overflow-x:auto;margin:18px 0;border:1px solid rgba(148,163,184,.28);border-radius:16px;background:rgba(255,255,255,.78);}
|
|
332
|
+
.preview__body table{width:100%;min-width:520px;border-collapse:collapse;border-spacing:0;}
|
|
333
|
+
.preview__body thead th{background:#dbeafe;color:#0f172a;font-weight:700;}
|
|
334
|
+
.preview__body th,.preview__body td{padding:12px 14px;border-bottom:1px solid rgba(148,163,184,.28);vertical-align:top;text-align:left;word-break:break-word;}
|
|
335
|
+
.preview__body tbody tr:nth-child(even){background:rgba(59,130,246,.04);}
|
|
336
|
+
.preview__body tbody tr:last-child td{border-bottom:none;}
|
|
337
|
+
.preview__body a{color:#0369a1;}
|
|
338
|
+
.markdown-preview__diagram svg{max-width:100%;height:auto;}
|
|
339
|
+
.markdown-preview__mermaid-fallback{margin-top:10px;padding:10px 12px;border-radius:10px;color:#991b1b;background:#fee2e2;border:1px solid rgba(248,113,113,.35);}
|
|
340
|
+
</style></head><body><div class="preview"><header class="preview__header"><h1 class="preview__title">${safeTitle}</h1><p class="preview__source">File: <code>${safeSourceLabel}</code></p></header><main class="preview__body">${renderedHtml}</main></div><script src="/node_modules/mermaid/dist/mermaid.min.js"></script><script>
|
|
341
|
+
(() => {
|
|
342
|
+
const fallbackNodes = Array.from(document.querySelectorAll(".markdown-preview__mermaid-fallback"));
|
|
343
|
+
const showFallback = (message) => {
|
|
344
|
+
fallbackNodes.forEach((node) => {
|
|
345
|
+
node.hidden = false;
|
|
346
|
+
if (message) {
|
|
347
|
+
node.textContent = message;
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
};
|
|
351
|
+
if (!window.mermaid) {
|
|
352
|
+
showFallback("Mermaid preview unavailable. Raw diagram source shown below.");
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
window.mermaid.initialize({ startOnLoad: false, securityLevel: "strict", theme: "dark" });
|
|
357
|
+
const nodes = Array.from(document.querySelectorAll(".mermaid"));
|
|
358
|
+
if (nodes.length === 0) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
Promise.resolve(window.mermaid.run({ nodes })).catch((error) => {
|
|
362
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
363
|
+
showFallback("Mermaid preview unavailable. Raw diagram source shown below. (" + detail + ")");
|
|
364
|
+
});
|
|
365
|
+
} catch (error) {
|
|
366
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
367
|
+
showFallback("Mermaid preview unavailable. Raw diagram source shown below. (" + detail + ")");
|
|
368
|
+
}
|
|
369
|
+
})();
|
|
370
|
+
</script></body></html>`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function stripLeadingDocumentFrontMatter(markdown, item) {
|
|
374
|
+
const lines = String(markdown || "").replace(/\r\n/g, "\n").split("\n");
|
|
375
|
+
let index = 0;
|
|
376
|
+
while (index < lines.length && lines[index].trim() === "") {
|
|
377
|
+
index += 1;
|
|
378
|
+
}
|
|
379
|
+
if (index >= lines.length) {
|
|
380
|
+
return markdown;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const headingMatch = lines[index].match(/^(#{1,6})\s+(.*)$/);
|
|
384
|
+
if (!headingMatch) {
|
|
385
|
+
return stripLeadingIndicatorBlock(markdown);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const normalizedHeading = headingMatch[2].replace(/\s+/g, " ").trim().toLowerCase();
|
|
389
|
+
const title = String(item && item.title ? item.title : "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
390
|
+
const id = String(item && item.id ? item.id : "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
391
|
+
const titleWithId = id && title ? `${id} - ${title}` : "";
|
|
392
|
+
const headingMatches =
|
|
393
|
+
(title && normalizedHeading === title) ||
|
|
394
|
+
(titleWithId && normalizedHeading === titleWithId) ||
|
|
395
|
+
(id && normalizedHeading.startsWith(`${id} - `));
|
|
396
|
+
|
|
397
|
+
if (!headingMatches) {
|
|
398
|
+
return stripLeadingIndicatorBlock(markdown);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const remainder = lines.slice(index + 1).join("\n");
|
|
402
|
+
return stripLeadingIndicatorBlock(remainder);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function stripLeadingIndicatorBlock(markdown) {
|
|
406
|
+
const lines = String(markdown || "").replace(/\r\n/g, "\n").split("\n");
|
|
407
|
+
let index = 0;
|
|
408
|
+
while (index < lines.length && lines[index].trim() === "") {
|
|
409
|
+
index += 1;
|
|
410
|
+
}
|
|
411
|
+
if (index >= lines.length || !lines[index].trimStart().startsWith(">")) {
|
|
412
|
+
return markdown;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
let cursor = index;
|
|
416
|
+
let sawIndicatorLine = false;
|
|
417
|
+
while (cursor < lines.length) {
|
|
418
|
+
const line = lines[cursor];
|
|
419
|
+
if (line.trim() === "") {
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
if (!line.trimStart().startsWith(">")) {
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
sawIndicatorLine =
|
|
426
|
+
sawIndicatorLine ||
|
|
427
|
+
/>(\s+)?(From version|Schema version|Status|Understanding|Confidence|Progress|Complexity|Theme|Reminder)\s*:/.test(line);
|
|
428
|
+
cursor += 1;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (!sawIndicatorLine) {
|
|
432
|
+
return markdown;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
while (cursor < lines.length && lines[cursor].trim() === "") {
|
|
436
|
+
cursor += 1;
|
|
437
|
+
}
|
|
438
|
+
return lines.slice(cursor).join("\n");
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
window.createCdxLogicsMarkdownApi = function createCdxLogicsMarkdownApi() {
|
|
442
|
+
return {
|
|
443
|
+
escapeHtml,
|
|
444
|
+
renderMarkdownToHtml,
|
|
445
|
+
stripLeadingDocumentFrontMatter,
|
|
446
|
+
buildReadPreviewDocument
|
|
447
|
+
};
|
|
448
|
+
};
|
|
449
|
+
})();
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
window.createCdxLogicsToolsPanelLayoutApi = function createCdxLogicsToolsPanelLayoutApi(options) {
|
|
3
|
+
const { toolsPanel, getCanBootstrapLogics, getBootstrapLogicsTitle, getShouldRecommendCheckEnvironment } = options;
|
|
4
|
+
let activeToolsView = "workflow";
|
|
5
|
+
const toolButtons = toolsPanel
|
|
6
|
+
? new Map(
|
|
7
|
+
Array.from(toolsPanel.querySelectorAll(".tools-panel__item")).map((button) => [button.dataset.action, button])
|
|
8
|
+
)
|
|
9
|
+
: new Map();
|
|
10
|
+
const toolViews = toolsPanel
|
|
11
|
+
? new Map(
|
|
12
|
+
Array.from(toolsPanel.querySelectorAll("[data-tools-view]")).map((view) => [
|
|
13
|
+
view.getAttribute("data-tools-view"),
|
|
14
|
+
view
|
|
15
|
+
])
|
|
16
|
+
)
|
|
17
|
+
: new Map();
|
|
18
|
+
const toolViewSwitches = toolsPanel
|
|
19
|
+
? new Map(
|
|
20
|
+
Array.from(toolsPanel.querySelectorAll("[data-tools-view-switch]")).map((button) => [
|
|
21
|
+
button.getAttribute("data-tools-view-switch"),
|
|
22
|
+
button
|
|
23
|
+
])
|
|
24
|
+
)
|
|
25
|
+
: new Map();
|
|
26
|
+
const toolSectionBodies = toolsPanel
|
|
27
|
+
? new Map(
|
|
28
|
+
Array.from(toolsPanel.querySelectorAll("[data-tools-body]")).map((body) => [body.getAttribute("data-tools-body"), body])
|
|
29
|
+
)
|
|
30
|
+
: new Map();
|
|
31
|
+
const toolSections = toolsPanel
|
|
32
|
+
? new Map(
|
|
33
|
+
Array.from(toolsPanel.querySelectorAll("[data-tools-section]")).map((section) => [
|
|
34
|
+
section.getAttribute("data-tools-section"),
|
|
35
|
+
section
|
|
36
|
+
])
|
|
37
|
+
)
|
|
38
|
+
: new Map();
|
|
39
|
+
const toolSectionLayout = {
|
|
40
|
+
workflow: ["open-onboarding", "select-agent", "new-request", "create-companion-doc", "bootstrap-logics"],
|
|
41
|
+
assist: [
|
|
42
|
+
"assist-next-step",
|
|
43
|
+
"assist-triage",
|
|
44
|
+
"assist-commit-all",
|
|
45
|
+
"assist-diff-risk",
|
|
46
|
+
"assist-summarize-changelog",
|
|
47
|
+
"assist-prepare-release",
|
|
48
|
+
"assist-publish-release",
|
|
49
|
+
"assist-summarize-validation",
|
|
50
|
+
"assist-validation-checklist",
|
|
51
|
+
"assist-doc-consistency"
|
|
52
|
+
],
|
|
53
|
+
runtime: ["check-environment", "check-hybrid-runtime", "open-hybrid-insights"],
|
|
54
|
+
kit: ["update-logics-kit", "repair-logics-kit"],
|
|
55
|
+
workspace: ["change-project-root", "reset-project-root"],
|
|
56
|
+
maintenance: ["open-logics-insights", "about"]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function applyActiveToolsView() {
|
|
60
|
+
toolViews.forEach((view, viewName) => {
|
|
61
|
+
view.hidden = viewName !== activeToolsView;
|
|
62
|
+
});
|
|
63
|
+
toolViewSwitches.forEach((button, viewName) => {
|
|
64
|
+
const isActive = viewName === activeToolsView;
|
|
65
|
+
button.classList.toggle("is-active", isActive);
|
|
66
|
+
button.setAttribute("aria-selected", String(isActive));
|
|
67
|
+
button.setAttribute("aria-pressed", String(isActive));
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function setActiveToolsView(viewName) {
|
|
72
|
+
if (!toolViews.has(viewName)) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
activeToolsView = viewName;
|
|
76
|
+
applyActiveToolsView();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function getActiveToolsView() {
|
|
80
|
+
return activeToolsView;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function formatActivityUpdated(updatedAt) {
|
|
84
|
+
const timestamp = Date.parse(updatedAt || "");
|
|
85
|
+
if (!Number.isFinite(timestamp)) {
|
|
86
|
+
return "Updated: Unknown";
|
|
87
|
+
}
|
|
88
|
+
const date = new Date(timestamp);
|
|
89
|
+
const deltaMinutes = Math.max(0, Math.round((Date.now() - timestamp) / 60000));
|
|
90
|
+
if (deltaMinutes < 24 * 60) {
|
|
91
|
+
const relativeLabel =
|
|
92
|
+
deltaMinutes < 1
|
|
93
|
+
? "just now"
|
|
94
|
+
: deltaMinutes < 60
|
|
95
|
+
? `${deltaMinutes}m ago`
|
|
96
|
+
: `${Math.floor(deltaMinutes / 60)}h ago`;
|
|
97
|
+
const preciseTime = new Intl.DateTimeFormat(undefined, {
|
|
98
|
+
hour: "2-digit",
|
|
99
|
+
minute: "2-digit"
|
|
100
|
+
}).format(date);
|
|
101
|
+
return `Updated: ${relativeLabel} • ${preciseTime}`;
|
|
102
|
+
}
|
|
103
|
+
return `Updated: ${date.toLocaleDateString("en-CA")}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function getRecommendedToolActions() {
|
|
107
|
+
const bootstrapTitle = (getBootstrapLogicsTitle() || "").toLowerCase();
|
|
108
|
+
if (getCanBootstrapLogics()) {
|
|
109
|
+
return ["bootstrap-logics", "check-environment", "change-project-root"];
|
|
110
|
+
}
|
|
111
|
+
if (bootstrapTitle.includes("repaired")) {
|
|
112
|
+
return ["check-environment", "update-logics-kit", "change-project-root"];
|
|
113
|
+
}
|
|
114
|
+
if (getShouldRecommendCheckEnvironment()) {
|
|
115
|
+
return ["check-environment", "change-project-root"];
|
|
116
|
+
}
|
|
117
|
+
return ["new-request", "assist-next-step", "assist-triage"];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function renderToolsPanelStructure() {
|
|
121
|
+
if (!toolsPanel) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
applyActiveToolsView();
|
|
126
|
+
|
|
127
|
+
const assigned = new Set();
|
|
128
|
+
const recommendedBody = toolSectionBodies.get("recommended");
|
|
129
|
+
if (recommendedBody) {
|
|
130
|
+
recommendedBody.replaceChildren();
|
|
131
|
+
getRecommendedToolActions().forEach((action) => {
|
|
132
|
+
const button = toolButtons.get(action);
|
|
133
|
+
if (!button) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
recommendedBody.appendChild(button);
|
|
137
|
+
assigned.add(action);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
Object.entries(toolSectionLayout).forEach(([sectionName, actions]) => {
|
|
142
|
+
const body = toolSectionBodies.get(sectionName);
|
|
143
|
+
if (!body) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
body.replaceChildren();
|
|
147
|
+
actions.forEach((action) => {
|
|
148
|
+
if (assigned.has(action)) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const button = toolButtons.get(action);
|
|
152
|
+
if (!button) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
body.appendChild(button);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
toolSections.forEach((section, sectionName) => {
|
|
160
|
+
const body = toolSectionBodies.get(sectionName);
|
|
161
|
+
section.hidden = !body || body.childElementCount === 0;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
formatActivityUpdated,
|
|
167
|
+
getActiveToolsView,
|
|
168
|
+
renderToolsPanelStructure,
|
|
169
|
+
setActiveToolsView
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
})();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
window.createCdxLogicsUiStatusApi = function createCdxLogicsUiStatusApi(options) {
|
|
3
|
+
const { documentRef = document, layout, harnessBridge } = options;
|
|
4
|
+
|
|
5
|
+
let statusBanner = null;
|
|
6
|
+
let noticeTimeoutId = null;
|
|
7
|
+
|
|
8
|
+
function ensureStatusBanner() {
|
|
9
|
+
if (statusBanner && statusBanner.isConnected) {
|
|
10
|
+
return statusBanner;
|
|
11
|
+
}
|
|
12
|
+
const anchor = layout && layout.parentElement ? layout.parentElement : documentRef.body;
|
|
13
|
+
if (!anchor) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
statusBanner = documentRef.createElement("div");
|
|
17
|
+
statusBanner.className = "status-banner";
|
|
18
|
+
statusBanner.hidden = true;
|
|
19
|
+
statusBanner.setAttribute("role", "status");
|
|
20
|
+
statusBanner.setAttribute("aria-live", "polite");
|
|
21
|
+
anchor.insertBefore(statusBanner, layout || anchor.firstChild);
|
|
22
|
+
return statusBanner;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function showStatus(message, tone = "info") {
|
|
26
|
+
if (!message) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const banner = ensureStatusBanner();
|
|
30
|
+
if (banner) {
|
|
31
|
+
banner.hidden = false;
|
|
32
|
+
banner.textContent = message;
|
|
33
|
+
banner.dataset.tone = tone;
|
|
34
|
+
if (noticeTimeoutId) {
|
|
35
|
+
window.clearTimeout(noticeTimeoutId);
|
|
36
|
+
}
|
|
37
|
+
noticeTimeoutId = window.setTimeout(() => {
|
|
38
|
+
if (!statusBanner) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
statusBanner.hidden = true;
|
|
42
|
+
}, 4500);
|
|
43
|
+
}
|
|
44
|
+
if (harnessBridge && typeof harnessBridge.notify === "function") {
|
|
45
|
+
harnessBridge.notify(message, tone);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
ensureStatusBanner,
|
|
51
|
+
showStatus
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
})();
|