@bendyline/docblocks-react 2.6.2 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +3 -10
  2. package/THIRD_PARTY_NOTICES.txt +4284 -3651
  3. package/dist/AiDiagrams-XWOM3YZX.js +13 -0
  4. package/dist/AiEditorAssistant-M4LI2CRY.js +771 -0
  5. package/dist/AudioExportDialog-F23QXNPB.js +253 -0
  6. package/dist/DocumentConflictDialog-5LRVIGXS.js +111 -0
  7. package/dist/ExportDialog-L6FG6W3H.js +9 -0
  8. package/dist/{ExportToolbarControls-QYLBRWM7.js → ExportToolbarControls-BRCPZ7H3.js} +108 -42
  9. package/dist/{GitUI-L5T3XHZG.js → GitUI-LZW3IUXO.js} +5 -5
  10. package/dist/PresentationMaker-622NHMIN.js +9 -0
  11. package/dist/QuickOpenDialog-2KK4SEBU.js +356 -0
  12. package/dist/Settings-DuXk6baH.d.ts +144 -0
  13. package/dist/{ShareDialog-4OX3UPJ4.js → ShareDialog-N7GPM4SB.js} +10 -59
  14. package/dist/SpeechToolbarControl-UZQKGQWD.js +434 -0
  15. package/dist/TextFromNarration-XYRAB6O2.js +598 -0
  16. package/dist/WorkspaceSearchPanel-OUQQ5DOM.js +328 -0
  17. package/dist/chunk-2IYBBFQX.js +201 -0
  18. package/dist/{chunk-LG6HAWCK.js → chunk-56KO2LYN.js} +35 -4
  19. package/dist/chunk-5OZBGIIT.js +1826 -0
  20. package/dist/chunk-6B7G33D5.js +88 -0
  21. package/dist/chunk-ARATARVJ.js +40 -0
  22. package/dist/{chunk-ODYNOPC3.js → chunk-BCQZTWE5.js} +57 -17
  23. package/dist/chunk-EA4BO6E4.js +73 -0
  24. package/dist/chunk-G4MFB3IO.js +208 -0
  25. package/dist/chunk-I44HKDQX.js +314 -0
  26. package/dist/chunk-K4I3NJFL.js +49 -0
  27. package/dist/chunk-KBDZDFPU.js +61 -0
  28. package/dist/{chunk-FNSRCCBC.js → chunk-KWG6UP5O.js} +0 -10
  29. package/dist/chunk-MMOTEQK6.js +518 -0
  30. package/dist/chunk-OINKNX26.js +574 -0
  31. package/dist/chunk-Q5ZVP5LV.js +1711 -0
  32. package/dist/chunk-RP3DVNTI.js +62 -0
  33. package/dist/{chunk-FMDLZ4ZG.js → chunk-SGQKTKDX.js} +2 -2
  34. package/dist/chunk-TBLZBNQF.js +25 -0
  35. package/dist/{chunk-M5Y5WO7Z.js → chunk-UG4G7PJ4.js} +1 -1
  36. package/dist/chunk-UONFWKJ7.js +22 -0
  37. package/dist/chunk-VO2JYYQZ.js +40 -0
  38. package/dist/{chunk-EBWYGTN7.js → chunk-X437UAF7.js} +6 -0
  39. package/dist/chunk-YS4HVWIO.js +63 -0
  40. package/dist/editor.d.ts +36 -1
  41. package/dist/editor.js +16 -1
  42. package/dist/export/index.d.ts +27 -11
  43. package/dist/export/index.js +11 -7
  44. package/dist/index.d.ts +100 -13
  45. package/dist/index.js +3916 -1633
  46. package/dist/{run-export-I2XOVGG4.js → run-export-47X5FTYZ.js} +3 -3
  47. package/dist/settings/index.d.ts +32 -101
  48. package/dist/settings/index.js +14 -2
  49. package/package.json +9 -9
  50. package/src/styles/docblocks.css +2332 -656
  51. package/dist/ExportDialog-Q4VWI43H.js +0 -9
  52. package/dist/chunk-JDJVRDOP.js +0 -92
  53. package/dist/chunk-Q6XNIKDG.js +0 -669
  54. package/dist/{GitToolbarControl-O5AXBC6T.js → GitToolbarControl-J4UYZDV6.js} +3 -3
@@ -0,0 +1,574 @@
1
+ import {
2
+ AI_DIAGRAM_DESCRIPTION_CHARACTERS,
3
+ DIAGRAM_KINDS,
4
+ DIAGRAM_KIND_LABELS,
5
+ clip,
6
+ documentTitle,
7
+ idsIn,
8
+ inferKind,
9
+ locatePassage,
10
+ planInsertions,
11
+ promptBudget,
12
+ realizeDiagram,
13
+ specSummaryForAvoid,
14
+ startIllustrationRun
15
+ } from "./chunk-5OZBGIIT.js";
16
+ import {
17
+ unavailableSentence,
18
+ useAiStatus
19
+ } from "./chunk-K4I3NJFL.js";
20
+ import {
21
+ Dialog
22
+ } from "./chunk-56KO2LYN.js";
23
+
24
+ // src/Ai/AiDiagrams.tsx
25
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
26
+ import { createPortal } from "react-dom";
27
+ import {
28
+ headingDepthForInsertion,
29
+ useEditorContext,
30
+ usePreviewSettingsOptional,
31
+ validateMermaidSource
32
+ } from "@bendyline/squisq-editor-react";
33
+ import { LinearDocView } from "@bendyline/squisq-react/page";
34
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
35
+ var LINKED_KINDS = /* @__PURE__ */ new Set(["flow", "sequence", "hierarchy"]);
36
+ function contextWindowOf(status) {
37
+ return status?.kind === "ready" ? status.model?.contextWindow ?? null : null;
38
+ }
39
+ function DiagramPreview({ compiled }) {
40
+ const preview = usePreviewSettingsOptional();
41
+ const markdown = useMemo(() => compiled.render(2, /* @__PURE__ */ new Set()), [compiled]);
42
+ return /* @__PURE__ */ jsx(
43
+ "div",
44
+ {
45
+ className: "db-ai-diagram-preview",
46
+ role: "img",
47
+ "aria-label": `${compiled.title}. ${compiled.summary}`,
48
+ children: /* @__PURE__ */ jsx(
49
+ LinearDocView,
50
+ {
51
+ markdown,
52
+ showCover: false,
53
+ thinMargins: true,
54
+ theme: preview?.activeTheme
55
+ }
56
+ )
57
+ }
58
+ );
59
+ }
60
+ function DiagramSource({ compiled }) {
61
+ const markdown = useMemo(() => compiled.render(2, /* @__PURE__ */ new Set()), [compiled]);
62
+ return /* @__PURE__ */ jsx("pre", { className: "db-ai-diagram-source", children: markdown });
63
+ }
64
+ function placementNote(compiled) {
65
+ return compiled.placement === "sectionEnd" ? "Goes at the end of this section." : null;
66
+ }
67
+ function AiDiagramDialog({
68
+ ai,
69
+ documentSource,
70
+ capturedSelection,
71
+ getSelection,
72
+ insertBlockAfterCursor,
73
+ onClose
74
+ }) {
75
+ const status = useAiStatus(ai);
76
+ const material = capturedSelection.text.trim();
77
+ const [description, setDescription] = useState("");
78
+ const [kindChoice, setKindChoice] = useState("auto");
79
+ const [running, setRunning] = useState(false);
80
+ const [outcome, setOutcome] = useState(null);
81
+ const [error, setError] = useState(null);
82
+ const [showSource, setShowSource] = useState(false);
83
+ const promptRef = useRef(null);
84
+ const controllerRef = useRef(null);
85
+ const requestRef = useRef(0);
86
+ useEffect(
87
+ () => () => {
88
+ requestRef.current += 1;
89
+ controllerRef.current?.abort();
90
+ },
91
+ []
92
+ );
93
+ const autoKind = inferKind(description, material);
94
+ const kind = kindChoice === "auto" ? autoKind : kindChoice;
95
+ const ready = outcome?.status === "ready" ? outcome : null;
96
+ const generate = useCallback(async () => {
97
+ const request = description.trim();
98
+ if (!request && !material) {
99
+ setError("Describe the diagram you want, or select the text to draw it from.");
100
+ promptRef.current?.focus();
101
+ return;
102
+ }
103
+ controllerRef.current?.abort();
104
+ const controller = new AbortController();
105
+ controllerRef.current = controller;
106
+ const requestId = ++requestRef.current;
107
+ const avoid = ready ? specSummaryForAvoid(ready.compiled) : void 0;
108
+ setRunning(true);
109
+ setError(null);
110
+ const result = await realizeDiagram({
111
+ ai,
112
+ kind,
113
+ material: material || request,
114
+ heading: "",
115
+ title: request ? clip(request.charAt(0).toUpperCase() + request.slice(1), 60) : "",
116
+ description: request || void 0,
117
+ avoid,
118
+ // With selected text, the diagram must stick to it. Without, the person
119
+ // asked the model to supply the content.
120
+ ground: material.length > 0,
121
+ budget: promptBudget(contextWindowOf(status)),
122
+ validateMermaid: validateMermaidSource,
123
+ signal: controller.signal
124
+ });
125
+ if (requestRef.current !== requestId) return;
126
+ setRunning(false);
127
+ if (result.status === "cancelled") return;
128
+ if (result.status === "failed") {
129
+ setError(result.message);
130
+ return;
131
+ }
132
+ setOutcome(result);
133
+ }, [ai, description, kind, material, ready, status]);
134
+ const stop = useCallback(() => controllerRef.current?.abort(), []);
135
+ const insert = useCallback(() => {
136
+ if (!ready) return;
137
+ const current = getSelection();
138
+ if (!current || current.view !== capturedSelection.view) {
139
+ setError("Return to the original editor view and try again.");
140
+ return;
141
+ }
142
+ const { compiled } = ready;
143
+ const taken = idsIn(documentSource);
144
+ let noDepth = false;
145
+ const inserted = insertBlockAfterCursor(
146
+ (context) => {
147
+ if (compiled.placement === "afterBlock") return compiled.render(2, taken);
148
+ const depth = headingDepthForInsertion(context, { childLevels: 1 });
149
+ if (depth === null) noDepth = true;
150
+ return depth === null ? null : compiled.render(depth, taken);
151
+ },
152
+ { placement: compiled.placement }
153
+ );
154
+ if (!inserted) {
155
+ setError(
156
+ noDepth ? "This diagram cannot go at the end of this section. Move the cursor to another section and try again." : "DocBlocks could not insert the diagram in the current editor view."
157
+ );
158
+ return;
159
+ }
160
+ onClose();
161
+ }, [
162
+ capturedSelection.view,
163
+ documentSource,
164
+ getSelection,
165
+ insertBlockAfterCursor,
166
+ onClose,
167
+ ready
168
+ ]);
169
+ const dialog = /* @__PURE__ */ jsxs(
170
+ Dialog,
171
+ {
172
+ title: "Insert diagram with AI",
173
+ onClose,
174
+ size: "wide",
175
+ closeOnBackdrop: !running,
176
+ initialFocusRef: promptRef,
177
+ className: "db-ai-draft-dialog db-ai-diagram-dialog",
178
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
179
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: onClose, children: "Cancel" }),
180
+ running ? /* @__PURE__ */ jsx("button", { type: "button", className: "db-ai-secondary-action", onClick: stop, children: "Stop" }) : /* @__PURE__ */ jsx(
181
+ "button",
182
+ {
183
+ type: "button",
184
+ className: "db-ai-secondary-action",
185
+ onClick: () => void generate(),
186
+ children: ready ? "Regenerate" : "Generate"
187
+ }
188
+ ),
189
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: !ready || running, onClick: insert, children: "Insert" })
190
+ ] }),
191
+ children: [
192
+ /* @__PURE__ */ jsxs("label", { className: "db-ai-field", children: [
193
+ /* @__PURE__ */ jsx("span", { children: "What should the diagram show?" }),
194
+ /* @__PURE__ */ jsx(
195
+ "textarea",
196
+ {
197
+ ref: promptRef,
198
+ value: description,
199
+ maxLength: AI_DIAGRAM_DESCRIPTION_CHARACTERS,
200
+ rows: 3,
201
+ placeholder: material ? "Optional. For example: Show the approval steps as a flowchart." : "For example: How a pull request is reviewed and merged.",
202
+ onChange: (event) => setDescription(event.currentTarget.value)
203
+ }
204
+ )
205
+ ] }),
206
+ /* @__PURE__ */ jsxs("label", { className: "db-ai-field db-ai-field--inline", children: [
207
+ /* @__PURE__ */ jsx("span", { children: "Kind" }),
208
+ /* @__PURE__ */ jsxs(
209
+ "select",
210
+ {
211
+ value: kindChoice,
212
+ onChange: (event) => setKindChoice(event.currentTarget.value),
213
+ children: [
214
+ /* @__PURE__ */ jsxs("option", { value: "auto", children: [
215
+ "Auto (",
216
+ DIAGRAM_KIND_LABELS[autoKind],
217
+ ")"
218
+ ] }),
219
+ DIAGRAM_KINDS.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: DIAGRAM_KIND_LABELS[option] }, option))
220
+ ]
221
+ }
222
+ )
223
+ ] }),
224
+ material && /* @__PURE__ */ jsxs("div", { className: "db-ai-selection-preview", children: [
225
+ /* @__PURE__ */ jsx("span", { children: "Drawing from the selected text" }),
226
+ /* @__PURE__ */ jsx("blockquote", { children: clip(material, 600) })
227
+ ] }),
228
+ running && /* @__PURE__ */ jsxs("p", { className: "db-ai-progress", role: "status", children: [
229
+ /* @__PURE__ */ jsx("span", { className: "db-ai-spinner", "aria-hidden": "true" }),
230
+ " Drawing\u2026"
231
+ ] }),
232
+ ready && !running && /* @__PURE__ */ jsxs("div", { className: "db-ai-diagram-result", children: [
233
+ /* @__PURE__ */ jsx(DiagramPreview, { compiled: ready.compiled }),
234
+ ready.notes.map((note) => /* @__PURE__ */ jsx("p", { className: "db-ai-note", children: note }, note)),
235
+ placementNote(ready.compiled) && /* @__PURE__ */ jsx("p", { className: "db-ai-note", children: placementNote(ready.compiled) }),
236
+ LINKED_KINDS.has(ready.compiled.kind) && /* @__PURE__ */ jsx("p", { className: "db-ai-note", children: "Check the links between boxes before inserting." }),
237
+ /* @__PURE__ */ jsx(
238
+ "button",
239
+ {
240
+ type: "button",
241
+ className: "db-ai-link-button",
242
+ "aria-expanded": showSource,
243
+ onClick: () => setShowSource((open) => !open),
244
+ children: showSource ? "Hide source" : "Show source"
245
+ }
246
+ ),
247
+ showSource && /* @__PURE__ */ jsx(DiagramSource, { compiled: ready.compiled })
248
+ ] }),
249
+ status && !(status.kind === "ready" && status.model) && /* @__PURE__ */ jsx("p", { className: "db-ai-note", children: unavailableSentence(status) }),
250
+ error && /* @__PURE__ */ jsx("p", { className: "db-ai-error", role: "alert", children: error }),
251
+ /* @__PURE__ */ jsx("p", { className: "db-ai-disclaimer", children: "Review AI-generated diagrams before adding them." })
252
+ ]
253
+ }
254
+ );
255
+ return typeof document === "undefined" ? dialog : createPortal(dialog, document.body);
256
+ }
257
+ function AiIllustratePanel({ ai, onClose }) {
258
+ const { markdownSource, applySourceEdits } = useEditorContext();
259
+ const status = useAiStatus(ai);
260
+ const contextWindow = contextWindowOf(status);
261
+ const ready = status?.kind === "ready" && status.model !== null;
262
+ const [phase, setPhase] = useState("idle");
263
+ const [progress, setProgress] = useState(null);
264
+ const [suggestions, setSuggestions] = useState([]);
265
+ const [error, setError] = useState(null);
266
+ const [notice, setNotice] = useState(null);
267
+ const [regenerating, setRegenerating] = useState(null);
268
+ const [sourceOpen, setSourceOpen] = useState(() => /* @__PURE__ */ new Set());
269
+ const [kindChoice, setKindChoice] = useState({});
270
+ const runRef = useRef(null);
271
+ const regenerateRef = useRef(null);
272
+ const generationRef = useRef(0);
273
+ const startedRef = useRef(false);
274
+ const update = useCallback((key, change) => {
275
+ setSuggestions((current) => current.map((item) => item.key === key ? change(item) : item));
276
+ }, []);
277
+ const start = useCallback(() => {
278
+ runRef.current?.cancel();
279
+ regenerateRef.current?.abort();
280
+ const generation = ++generationRef.current;
281
+ setSuggestions([]);
282
+ setError(null);
283
+ setNotice(null);
284
+ setProgress(null);
285
+ setPhase("planning");
286
+ runRef.current = startIllustrationRun({
287
+ ai,
288
+ source: markdownSource,
289
+ title: documentTitle(markdownSource),
290
+ budget: promptBudget(contextWindow),
291
+ validateMermaid: validateMermaidSource,
292
+ onEvent: (event) => {
293
+ if (generationRef.current !== generation) return;
294
+ switch (event.type) {
295
+ case "planning":
296
+ setPhase("planning");
297
+ break;
298
+ case "planned":
299
+ setSuggestions(
300
+ event.picks.map((pick) => ({
301
+ key: pick.passage.id,
302
+ pick,
303
+ state: "waiting",
304
+ outcome: null,
305
+ inserted: false,
306
+ dismissed: false,
307
+ problem: null
308
+ }))
309
+ );
310
+ break;
311
+ case "drawing":
312
+ setPhase("drawing");
313
+ setProgress({ index: event.index, total: event.total });
314
+ setSuggestions(
315
+ (current) => current.map(
316
+ (item, index) => index === event.index ? { ...item, state: "drawing" } : item
317
+ )
318
+ );
319
+ break;
320
+ case "suggestion":
321
+ update(event.pick.passage.id, (item) => ({
322
+ ...item,
323
+ state: "done",
324
+ outcome: event.outcome
325
+ }));
326
+ break;
327
+ case "error":
328
+ setError(event.message);
329
+ setPhase("error");
330
+ break;
331
+ case "done":
332
+ runRef.current = null;
333
+ setProgress(null);
334
+ setPhase((current) => current === "error" ? current : "done");
335
+ setSuggestions(
336
+ (current) => current.map((item) => item.state === "done" ? item : { ...item, state: "done" })
337
+ );
338
+ break;
339
+ }
340
+ }
341
+ });
342
+ }, [ai, contextWindow, markdownSource, update]);
343
+ const stop = useCallback(() => {
344
+ runRef.current?.cancel();
345
+ regenerateRef.current?.abort();
346
+ }, []);
347
+ useEffect(() => {
348
+ if (startedRef.current || !ready) return;
349
+ startedRef.current = true;
350
+ start();
351
+ }, [ready, start]);
352
+ useEffect(
353
+ () => () => {
354
+ generationRef.current += 1;
355
+ runRef.current?.cancel();
356
+ regenerateRef.current?.abort();
357
+ startedRef.current = false;
358
+ },
359
+ []
360
+ );
361
+ const insert = useCallback(
362
+ (items) => {
363
+ const pending = [];
364
+ for (const item of items) {
365
+ if (item.inserted || item.dismissed || item.outcome?.status !== "ready") continue;
366
+ pending.push({
367
+ key: item.key,
368
+ passage: item.pick.passage,
369
+ compiled: item.outcome.compiled
370
+ });
371
+ }
372
+ if (pending.length === 0) return;
373
+ const plan = planInsertions(markdownSource, pending);
374
+ const stale = new Set(plan.stale);
375
+ const unplaceable = new Set(plan.unplaceable);
376
+ if (plan.edits.length > 0 && !applySourceEdits(plan.edits, { baseSource: markdownSource })) {
377
+ setNotice("Switch to Write or Source view, in the Document layout, to insert diagrams.");
378
+ return;
379
+ }
380
+ setNotice(null);
381
+ setSuggestions(
382
+ (current) => current.map((item) => {
383
+ if (!pending.some((entry) => entry.key === item.key)) return item;
384
+ if (stale.has(item.key)) {
385
+ return {
386
+ ...item,
387
+ problem: "This passage changed. Regenerate the diagram or dismiss it."
388
+ };
389
+ }
390
+ if (unplaceable.has(item.key)) {
391
+ return {
392
+ ...item,
393
+ problem: "No heading level fits at the end of this section without capturing the text after it."
394
+ };
395
+ }
396
+ return { ...item, inserted: true, problem: null };
397
+ })
398
+ );
399
+ },
400
+ [applySourceEdits, markdownSource]
401
+ );
402
+ const regenerate = useCallback(
403
+ async (item) => {
404
+ if (runRef.current || regenerating) return;
405
+ const kind = kindChoice[item.key] ?? item.pick.kind;
406
+ const controller = new AbortController();
407
+ regenerateRef.current = controller;
408
+ const generation = generationRef.current;
409
+ setRegenerating(item.key);
410
+ update(item.key, (current) => ({ ...current, state: "drawing", problem: null }));
411
+ const outcome = await realizeDiagram({
412
+ ai,
413
+ kind,
414
+ material: item.pick.passage.plain,
415
+ heading: item.pick.passage.heading,
416
+ title: item.pick.title,
417
+ avoid: item.outcome?.status === "ready" && kind === item.pick.kind ? specSummaryForAvoid(item.outcome.compiled) : void 0,
418
+ ground: true,
419
+ budget: promptBudget(contextWindow),
420
+ validateMermaid: validateMermaidSource,
421
+ signal: controller.signal
422
+ });
423
+ if (generationRef.current !== generation) return;
424
+ setRegenerating(null);
425
+ regenerateRef.current = null;
426
+ update(
427
+ item.key,
428
+ (current) => outcome.status === "cancelled" ? { ...current, state: "done" } : { ...current, state: "done", outcome, pick: { ...current.pick, kind } }
429
+ );
430
+ },
431
+ [ai, contextWindow, kindChoice, regenerating, update]
432
+ );
433
+ const busy = phase === "planning" || phase === "drawing" || regenerating !== null;
434
+ const visible = suggestions.filter((item) => !item.dismissed);
435
+ const insertable = visible.filter(
436
+ (item) => !item.inserted && item.outcome?.status === "ready" && locatePassage(markdownSource, item.pick.passage) !== null
437
+ );
438
+ return /* @__PURE__ */ jsxs("aside", { className: "db-ai-review-panel db-ai-illustrate-panel", "aria-label": "AI illustrations", children: [
439
+ /* @__PURE__ */ jsxs("div", { className: "db-ai-review-header", children: [
440
+ /* @__PURE__ */ jsxs("div", { children: [
441
+ /* @__PURE__ */ jsx("h2", { children: "AI illustrations" }),
442
+ phase === "done" && /* @__PURE__ */ jsxs("span", { className: "db-ai-review-count", children: [
443
+ visible.length,
444
+ " ",
445
+ visible.length === 1 ? "suggestion" : "suggestions"
446
+ ] })
447
+ ] }),
448
+ /* @__PURE__ */ jsx(
449
+ "button",
450
+ {
451
+ type: "button",
452
+ className: "db-ai-review-close",
453
+ onClick: onClose,
454
+ "aria-label": "Close AI illustrations",
455
+ children: "\xD7"
456
+ }
457
+ )
458
+ ] }),
459
+ /* @__PURE__ */ jsx("div", { className: "db-ai-review-actions", children: busy ? /* @__PURE__ */ jsx("button", { type: "button", onClick: stop, children: "Stop" }) : /* @__PURE__ */ jsx("button", { type: "button", disabled: !ready, onClick: start, children: phase === "idle" ? "Suggest diagrams" : "Suggest again" }) }),
460
+ !ready && status && /* @__PURE__ */ jsx("div", { className: "db-ai-review-empty", role: "status", children: unavailableSentence(status) }),
461
+ (phase === "planning" || phase === "drawing") && /* @__PURE__ */ jsxs("div", { className: "db-ai-review-empty", role: "status", children: [
462
+ /* @__PURE__ */ jsx("span", { className: "db-ai-spinner", "aria-hidden": "true" }),
463
+ " ",
464
+ phase === "planning" || !progress ? "Reading the document\u2026" : `Drawing diagram ${String(progress.index + 1)} of ${String(progress.total)}\u2026`
465
+ ] }),
466
+ phase === "error" && error && /* @__PURE__ */ jsx("div", { className: "db-ai-review-empty db-ai-error", role: "alert", children: error }),
467
+ notice && /* @__PURE__ */ jsx("div", { className: "db-ai-review-stale", role: "status", children: notice }),
468
+ phase === "done" && visible.length === 0 && /* @__PURE__ */ jsx("div", { className: "db-ai-review-empty", children: suggestions.length === 0 ? "No passage here would be clearer as a diagram." : "All suggestions have been handled." }),
469
+ /* @__PURE__ */ jsx("ol", { className: "db-ai-review-list", children: visible.map((item) => {
470
+ const outcome = item.outcome;
471
+ const compiled = outcome?.status === "ready" ? outcome.compiled : null;
472
+ const kind = item.pick.kind;
473
+ const passageMoved = locatePassage(markdownSource, item.pick.passage) === null;
474
+ return /* @__PURE__ */ jsxs("li", { className: "db-ai-review-finding db-ai-illustrate-card", children: [
475
+ /* @__PURE__ */ jsxs("div", { className: "db-ai-review-finding-meta", children: [
476
+ /* @__PURE__ */ jsx("span", { className: "db-ai-review-severity db-ai-illustrate-kind", children: DIAGRAM_KIND_LABELS[kind] }),
477
+ item.pick.passage.heading && /* @__PURE__ */ jsx("span", { children: clip(item.pick.passage.heading, 48) })
478
+ ] }),
479
+ /* @__PURE__ */ jsx("h3", { children: compiled?.title || item.pick.title || DIAGRAM_KIND_LABELS[kind] }),
480
+ item.pick.why && /* @__PURE__ */ jsx("p", { children: item.pick.why }),
481
+ /* @__PURE__ */ jsxs("div", { className: "db-ai-review-excerpt", children: [
482
+ /* @__PURE__ */ jsx("span", { children: "After" }),
483
+ /* @__PURE__ */ jsx("blockquote", { children: clip(item.pick.passage.plain, 180) })
484
+ ] }),
485
+ item.state === "waiting" && /* @__PURE__ */ jsx("p", { className: "db-ai-note", children: "Waiting\u2026" }),
486
+ item.state === "drawing" && /* @__PURE__ */ jsxs("p", { className: "db-ai-progress", role: "status", children: [
487
+ /* @__PURE__ */ jsx("span", { className: "db-ai-spinner", "aria-hidden": "true" }),
488
+ " Drawing\u2026"
489
+ ] }),
490
+ item.inserted && /* @__PURE__ */ jsx("p", { className: "db-ai-note", children: "Inserted." }),
491
+ item.state === "done" && compiled && !item.inserted && /* @__PURE__ */ jsxs(Fragment, { children: [
492
+ /* @__PURE__ */ jsx(DiagramPreview, { compiled }),
493
+ placementNote(compiled) && /* @__PURE__ */ jsx("p", { className: "db-ai-note", children: placementNote(compiled) }),
494
+ LINKED_KINDS.has(compiled.kind) && /* @__PURE__ */ jsx("p", { className: "db-ai-note", children: "Check the links between boxes before inserting." }),
495
+ sourceOpen.has(item.key) && /* @__PURE__ */ jsx(DiagramSource, { compiled })
496
+ ] }),
497
+ item.state === "done" && outcome?.status === "failed" && /* @__PURE__ */ jsx("p", { className: "db-ai-error", role: "alert", children: outcome.message }),
498
+ passageMoved && !item.inserted && /* @__PURE__ */ jsx("p", { className: "db-ai-error", role: "alert", children: "This passage changed. Regenerate the diagram or dismiss it." }),
499
+ item.problem && !passageMoved && /* @__PURE__ */ jsx("p", { className: "db-ai-error", role: "alert", children: item.problem }),
500
+ !item.inserted && item.state === "done" && /* @__PURE__ */ jsxs("div", { className: "db-ai-review-finding-actions db-ai-illustrate-actions", children: [
501
+ /* @__PURE__ */ jsx(
502
+ "button",
503
+ {
504
+ type: "button",
505
+ onClick: () => setSuggestions(
506
+ (current) => current.map(
507
+ (entry) => entry.key === item.key ? { ...entry, dismissed: true } : entry
508
+ )
509
+ ),
510
+ children: "Dismiss"
511
+ }
512
+ ),
513
+ compiled && /* @__PURE__ */ jsx(
514
+ "button",
515
+ {
516
+ type: "button",
517
+ "aria-expanded": sourceOpen.has(item.key),
518
+ onClick: () => setSourceOpen((current) => {
519
+ const next = new Set(current);
520
+ if (next.has(item.key)) next.delete(item.key);
521
+ else next.add(item.key);
522
+ return next;
523
+ }),
524
+ children: sourceOpen.has(item.key) ? "Hide source" : "Source"
525
+ }
526
+ ),
527
+ /* @__PURE__ */ jsx(
528
+ "select",
529
+ {
530
+ "aria-label": "Diagram kind",
531
+ value: kindChoice[item.key] ?? kind,
532
+ disabled: busy || passageMoved,
533
+ onChange: (event) => {
534
+ const value = event.currentTarget.value;
535
+ setKindChoice((current) => ({ ...current, [item.key]: value }));
536
+ },
537
+ children: DIAGRAM_KINDS.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: DIAGRAM_KIND_LABELS[option] }, option))
538
+ }
539
+ ),
540
+ /* @__PURE__ */ jsx(
541
+ "button",
542
+ {
543
+ type: "button",
544
+ disabled: busy || passageMoved,
545
+ onClick: () => void regenerate(item),
546
+ children: "Regenerate"
547
+ }
548
+ ),
549
+ compiled && /* @__PURE__ */ jsx(
550
+ "button",
551
+ {
552
+ type: "button",
553
+ disabled: busy || passageMoved,
554
+ onClick: () => insert([item]),
555
+ children: "Insert"
556
+ }
557
+ )
558
+ ] })
559
+ ] }, item.key);
560
+ }) }),
561
+ insertable.length > 1 && !busy && /* @__PURE__ */ jsx("div", { className: "db-ai-review-actions db-ai-illustrate-footer", children: /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => insert(insertable), children: [
562
+ "Insert all (",
563
+ insertable.length,
564
+ ")"
565
+ ] }) }),
566
+ /* @__PURE__ */ jsx("p", { className: "db-ai-review-disclaimer", children: "AI diagrams can be wrong. Check each one before inserting it." })
567
+ ] });
568
+ }
569
+
570
+ export {
571
+ DiagramPreview,
572
+ AiDiagramDialog,
573
+ AiIllustratePanel
574
+ };