@bendyline/docblocks-react 2.6.2 → 2.7.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 +3 -10
- package/THIRD_PARTY_NOTICES.txt +4284 -3651
- package/dist/AiDiagrams-LFV4YU6W.js +13 -0
- package/dist/AiEditorAssistant-GN2KXKZD.js +786 -0
- package/dist/AudioExportDialog-F23QXNPB.js +253 -0
- package/dist/DocumentConflictDialog-5LRVIGXS.js +111 -0
- package/dist/ExportDialog-IMBVGRYG.js +9 -0
- package/dist/{ExportToolbarControls-QYLBRWM7.js → ExportToolbarControls-MUM6HM3K.js} +117 -49
- package/dist/{GitToolbarControl-O5AXBC6T.js → GitToolbarControl-B7KG4MFP.js} +6 -6
- package/dist/{GitUI-L5T3XHZG.js → GitUI-RCWUZVS6.js} +6 -6
- package/dist/PresentationMaker-W2JIBHRN.js +9 -0
- package/dist/QuickOpenDialog-2KK4SEBU.js +356 -0
- package/dist/Settings-DuXk6baH.d.ts +144 -0
- package/dist/ShareDialog-XUA2OVGI.js +292 -0
- package/dist/SpeechToolbarControl-OSQS7NVY.js +434 -0
- package/dist/TextFromNarration-XYRAB6O2.js +598 -0
- package/dist/WorkspaceSearchPanel-BKZJRPMY.js +334 -0
- package/dist/chunk-2IYBBFQX.js +201 -0
- package/dist/{chunk-LG6HAWCK.js → chunk-56KO2LYN.js} +35 -4
- package/dist/chunk-6B7G33D5.js +88 -0
- package/dist/chunk-ARATARVJ.js +40 -0
- package/dist/{chunk-ODYNOPC3.js → chunk-BCQZTWE5.js} +57 -17
- package/dist/{chunk-FMDLZ4ZG.js → chunk-BD54Z5XP.js} +12 -3
- package/dist/chunk-EA4BO6E4.js +73 -0
- package/dist/chunk-EPOLZNFD.js +518 -0
- package/dist/chunk-G4MFB3IO.js +208 -0
- package/dist/{chunk-MRUK56JS.js → chunk-GMA4KGGO.js} +7 -3
- package/dist/chunk-I44HKDQX.js +314 -0
- package/dist/chunk-K4I3NJFL.js +49 -0
- package/dist/chunk-KBDZDFPU.js +61 -0
- package/dist/{chunk-FNSRCCBC.js → chunk-KWG6UP5O.js} +0 -10
- package/dist/chunk-NAH52736.js +1884 -0
- package/dist/chunk-RP3DVNTI.js +62 -0
- package/dist/chunk-SPUPWCSI.js +111 -0
- package/dist/chunk-TBLZBNQF.js +25 -0
- package/dist/{chunk-M5Y5WO7Z.js → chunk-UG4G7PJ4.js} +1 -1
- package/dist/chunk-UONFWKJ7.js +22 -0
- package/dist/chunk-VO2JYYQZ.js +40 -0
- package/dist/chunk-W55E6TE2.js +1714 -0
- package/dist/chunk-X3DDTJKR.js +574 -0
- package/dist/{chunk-EBWYGTN7.js → chunk-X437UAF7.js} +6 -0
- package/dist/editor.d.ts +36 -1
- package/dist/editor.js +16 -1
- package/dist/export/index.d.ts +27 -11
- package/dist/export/index.js +11 -7
- package/dist/index.d.ts +100 -13
- package/dist/index.js +3927 -1638
- package/dist/proofing/index.js +11 -1
- package/dist/{run-export-I2XOVGG4.js → run-export-47X5FTYZ.js} +3 -3
- package/dist/settings/index.d.ts +32 -101
- package/dist/settings/index.js +14 -2
- package/package.json +9 -9
- package/src/styles/docblocks.css +2344 -657
- package/dist/ExportDialog-Q4VWI43H.js +0 -9
- package/dist/ShareDialog-4OX3UPJ4.js +0 -359
- package/dist/chunk-JDJVRDOP.js +0 -92
- package/dist/chunk-Q6XNIKDG.js +0 -669
|
@@ -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-NAH52736.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
|
+
};
|
|
@@ -26,6 +26,11 @@ var DEFAULT_OPTIONS = {
|
|
|
26
26
|
includeLinkedDocs: false,
|
|
27
27
|
entryAsIndex: false
|
|
28
28
|
};
|
|
29
|
+
function resolveExportDialogInitial(lastOptions, docThemeId, workspaceThemeId) {
|
|
30
|
+
const base = lastOptions ?? DEFAULT_OPTIONS;
|
|
31
|
+
const themeId = docThemeId || workspaceThemeId;
|
|
32
|
+
return themeId ? { ...base, themeId } : base;
|
|
33
|
+
}
|
|
29
34
|
function loadLastExportOptions() {
|
|
30
35
|
try {
|
|
31
36
|
const raw = localStorage.getItem(STORAGE_KEY);
|
|
@@ -47,6 +52,7 @@ export {
|
|
|
47
52
|
FORMAT_LABELS,
|
|
48
53
|
FORMAT_EXTENSIONS,
|
|
49
54
|
DEFAULT_OPTIONS,
|
|
55
|
+
resolveExportDialogInitial,
|
|
50
56
|
loadLastExportOptions,
|
|
51
57
|
saveExportOptions
|
|
52
58
|
};
|
package/dist/editor.d.ts
CHANGED
|
@@ -1,4 +1,39 @@
|
|
|
1
1
|
import { ViewportPreset } from '@bendyline/squisq/schemas';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { DocBlocksHostAiAPI } from '@bendyline/docblocks/host';
|
|
4
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
declare function PresentationMaker({ ai, onClose, }: {
|
|
7
|
+
ai?: DocBlocksHostAiAPI;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
}): react.ReactPortal;
|
|
10
|
+
|
|
11
|
+
interface FindButtonProps {
|
|
12
|
+
readonly active: boolean;
|
|
13
|
+
readonly onActiveChange: (active: boolean) => void;
|
|
14
|
+
/** Platform label for the shortcut (e.g. `Ctrl+F` or `⌘F`), shown in the tooltip. */
|
|
15
|
+
readonly shortcutLabel?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Toolbar toggle for Squisq's Find mode. Squisq ships the find bar but no
|
|
19
|
+
* trigger, so every host that mounts `EditorShell` renders this in a toolbar
|
|
20
|
+
* slot and passes the state through `findMode` / `onFindModeChange`.
|
|
21
|
+
*/
|
|
22
|
+
declare function FindButton({ active, onActiveChange, shortcutLabel }: FindButtonProps): react_jsx_runtime.JSX.Element;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Re-focus an already-open find bar and select its query. Squisq focuses the
|
|
26
|
+
* input only when the bar mounts, so a repeated Ctrl/Cmd+F needs this.
|
|
27
|
+
*/
|
|
28
|
+
declare function focusFindInput(scope?: ParentNode): void;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Cmd/Ctrl+F, for hosts that mount Squisq's `EditorShell` without the shell's
|
|
32
|
+
* listener (the VS Code webview) but still want one find UI everywhere.
|
|
33
|
+
*/
|
|
34
|
+
declare function isFindShortcut(event: KeyboardEvent): boolean;
|
|
35
|
+
/** Tooltip label for the find shortcut on the current platform. */
|
|
36
|
+
declare function findShortcutLabel(): string;
|
|
2
37
|
|
|
3
38
|
declare const PORTRAIT_FORM_FACTOR_QUERY = "(orientation: portrait)";
|
|
4
39
|
/** Map the host surface orientation to Squisq's matching preview canvas. */
|
|
@@ -19,4 +54,4 @@ type EmptyDocumentPrompt = (typeof EMPTY_DOCUMENT_PROMPTS)[number];
|
|
|
19
54
|
/** Pick a prompt using the supplied random source, or `Math.random`. */
|
|
20
55
|
declare function pickEmptyDocumentPrompt(random?: () => number): EmptyDocumentPrompt;
|
|
21
56
|
|
|
22
|
-
export { EMPTY_DOCUMENT_PROMPTS, type EmptyDocumentPrompt, PORTRAIT_FORM_FACTOR_QUERY, pickEmptyDocumentPrompt, previewViewportPresetForOrientation, useResponsivePreviewViewportPreset };
|
|
57
|
+
export { EMPTY_DOCUMENT_PROMPTS, type EmptyDocumentPrompt, FindButton, type FindButtonProps, PORTRAIT_FORM_FACTOR_QUERY, PresentationMaker, findShortcutLabel, focusFindInput, isFindShortcut, pickEmptyDocumentPrompt, previewViewportPresetForOrientation, useResponsivePreviewViewportPreset };
|
package/dist/editor.js
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EMPTY_DOCUMENT_PROMPTS,
|
|
3
|
+
FindButton,
|
|
3
4
|
PORTRAIT_FORM_FACTOR_QUERY,
|
|
5
|
+
findShortcutLabel,
|
|
6
|
+
focusFindInput,
|
|
7
|
+
isFindShortcut,
|
|
4
8
|
pickEmptyDocumentPrompt,
|
|
5
9
|
previewViewportPresetForOrientation,
|
|
6
10
|
useResponsivePreviewViewportPreset
|
|
7
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-G4MFB3IO.js";
|
|
12
|
+
import {
|
|
13
|
+
PresentationMaker
|
|
14
|
+
} from "./chunk-EPOLZNFD.js";
|
|
15
|
+
import "./chunk-NAH52736.js";
|
|
16
|
+
import "./chunk-K4I3NJFL.js";
|
|
17
|
+
import "./chunk-56KO2LYN.js";
|
|
8
18
|
export {
|
|
9
19
|
EMPTY_DOCUMENT_PROMPTS,
|
|
20
|
+
FindButton,
|
|
10
21
|
PORTRAIT_FORM_FACTOR_QUERY,
|
|
22
|
+
PresentationMaker,
|
|
23
|
+
findShortcutLabel,
|
|
24
|
+
focusFindInput,
|
|
25
|
+
isFindShortcut,
|
|
11
26
|
pickEmptyDocumentPrompt,
|
|
12
27
|
previewViewportPresetForOrientation,
|
|
13
28
|
useResponsivePreviewViewportPreset
|