@bendyline/docblocks-react 2.7.1 → 2.7.3
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 +1 -1
- package/THIRD_PARTY_NOTICES.txt +290 -48
- package/dist/{AiDiagrams-LFV4YU6W.js → AiDiagrams-QE3LVYFK.js} +2 -2
- package/dist/{AiEditorAssistant-GN2KXKZD.js → AiEditorAssistant-YLJ5OZK2.js} +203 -164
- package/dist/AiStudyDialog-4TQZZGU3.js +1150 -0
- package/dist/{ExportDialog-IMBVGRYG.js → ExportDialog-JRVZCG3J.js} +2 -2
- package/dist/{ExportToolbarControls-MUM6HM3K.js → ExportToolbarControls-A2YBKUUD.js} +46 -9
- package/dist/KeyboardShortcutsDialog-G5OBDTNR.js +145 -0
- package/dist/{PresentationMaker-W2JIBHRN.js → PresentationMaker-P2JP6UUX.js} +2 -2
- package/dist/{Settings-DuXk6baH.d.ts → Settings-ByNImjk5.d.ts} +13 -13
- package/dist/{SpeechToolbarControl-OSQS7NVY.js → SpeechToolbarControl-CLU7RB2G.js} +1 -1
- package/dist/{TextFromNarration-XYRAB6O2.js → TextFromNarration-NITJQ4IB.js} +1 -1
- package/dist/{WorkspaceSearchPanel-BKZJRPMY.js → WorkspaceSearchPanel-J6BKEZQJ.js} +1 -1
- package/dist/{chunk-X3DDTJKR.js → chunk-3CE5RH2B.js} +255 -17
- package/dist/{chunk-UG4G7PJ4.js → chunk-5O5YTOXG.js} +1 -1
- package/dist/{chunk-VO2JYYQZ.js → chunk-6JJOGV6M.js} +5 -13
- package/dist/{chunk-X437UAF7.js → chunk-6MSHXQNB.js} +7 -2
- package/dist/{chunk-BCQZTWE5.js → chunk-6PA7QBZT.js} +107 -24
- package/dist/chunk-ACQF63SB.js +136 -0
- package/dist/chunk-DICE7VBJ.js +77 -0
- package/dist/{chunk-EPOLZNFD.js → chunk-EOU2JWAQ.js} +186 -71
- package/dist/{chunk-G4MFB3IO.js → chunk-VG434562.js} +2 -70
- package/dist/chunk-XNT66JC2.js +168 -0
- package/dist/{chunk-BD54Z5XP.js → chunk-Y5OE3HJC.js} +4 -3
- package/dist/{chunk-NAH52736.js → chunk-YPIVCCTB.js} +969 -98
- package/dist/{chunk-W55E6TE2.js → chunk-ZMCQO57Q.js} +28 -23
- package/dist/editor.js +7 -5
- package/dist/export/index.d.ts +27 -8
- package/dist/export/index.js +4 -4
- package/dist/index.d.ts +36 -8
- package/dist/index.js +629 -446
- package/dist/run-export-OHRNT2TP.js +13 -0
- package/dist/settings/index.d.ts +1 -1
- package/dist/settings/index.js +1 -1
- package/package.json +9 -8
- package/src/styles/docblocks.css +968 -329
- package/dist/chunk-EA4BO6E4.js +0 -73
- package/dist/run-export-47X5FTYZ.js +0 -11
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AiDiagramDialog
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3CE5RH2B.js";
|
|
4
|
+
import {
|
|
5
|
+
AiGenerationStatus,
|
|
6
|
+
STUDY_AID_KINDS,
|
|
7
|
+
STUDY_AID_LABELS
|
|
8
|
+
} from "./chunk-XNT66JC2.js";
|
|
4
9
|
import {
|
|
5
10
|
useConfirmDialog
|
|
6
11
|
} from "./chunk-6B7G33D5.js";
|
|
@@ -11,6 +16,7 @@ import {
|
|
|
11
16
|
AI_INSTRUCTION_CHARACTERS,
|
|
12
17
|
AI_REVIEW_DOCUMENT_CHARACTERS,
|
|
13
18
|
AI_REWRITE_SELECTION_CHARACTERS,
|
|
19
|
+
AiReportButton,
|
|
14
20
|
aiReviewReplyWasCutOff,
|
|
15
21
|
applyAiReviewFinding,
|
|
16
22
|
buildDraftContinuation,
|
|
@@ -19,8 +25,10 @@ import {
|
|
|
19
25
|
findUniqueExcerpt,
|
|
20
26
|
parseAiReviewResponse,
|
|
21
27
|
parseTruncatedAiReviewResponse,
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
reviewDocumentCharacters,
|
|
29
|
+
sanitizeGeneratedMarkdown,
|
|
30
|
+
withoutPictureFindings
|
|
31
|
+
} from "./chunk-YPIVCCTB.js";
|
|
24
32
|
import {
|
|
25
33
|
unavailableSentence,
|
|
26
34
|
useAiStatus
|
|
@@ -30,65 +38,21 @@ import {
|
|
|
30
38
|
} from "./chunk-56KO2LYN.js";
|
|
31
39
|
|
|
32
40
|
// src/Ai/AiEditorAssistant.tsx
|
|
33
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
lazy,
|
|
43
|
+
Suspense,
|
|
44
|
+
useCallback,
|
|
45
|
+
useEffect,
|
|
46
|
+
useMemo,
|
|
47
|
+
useRef,
|
|
48
|
+
useState
|
|
49
|
+
} from "react";
|
|
34
50
|
import { createPortal } from "react-dom";
|
|
35
51
|
import {
|
|
36
52
|
useEditorContext,
|
|
37
53
|
useEditorContextMenuItems
|
|
38
54
|
} from "@bendyline/squisq-editor-react";
|
|
39
55
|
|
|
40
|
-
// src/Ai/AiGenerationStatus.tsx
|
|
41
|
-
import { useEffect, useState } from "react";
|
|
42
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
43
|
-
var LABELS = {
|
|
44
|
-
starting: "Waiting for model\u2026",
|
|
45
|
-
queued: "Waiting for an available model\u2026",
|
|
46
|
-
loading_model: "Loading model\u2026",
|
|
47
|
-
prefill: "Processing input (prefill)\u2026",
|
|
48
|
-
reasoning: "Thinking\u2026",
|
|
49
|
-
generating: "Writing\u2026"
|
|
50
|
-
};
|
|
51
|
-
function duration(milliseconds) {
|
|
52
|
-
const seconds = Math.max(0, Math.floor(milliseconds / 1e3));
|
|
53
|
-
return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${String(seconds % 60).padStart(2, "0")}s`;
|
|
54
|
-
}
|
|
55
|
-
function AiGenerationStatus({
|
|
56
|
-
state,
|
|
57
|
-
characters
|
|
58
|
-
}) {
|
|
59
|
-
const [now, setNow] = useState(Date.now);
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
const timer = setInterval(() => setNow(Date.now()), 1e3);
|
|
62
|
-
return () => clearInterval(timer);
|
|
63
|
-
}, []);
|
|
64
|
-
const progress = state.progress;
|
|
65
|
-
const label = progress ? LABELS[progress.phase] : "Waiting for response\u2026";
|
|
66
|
-
const percent = progress?.phase === "prefill" ? progress.percent : null;
|
|
67
|
-
const details = [`${duration(now - state.startedAt)} elapsed`];
|
|
68
|
-
if (progress?.outputTokens != null) {
|
|
69
|
-
details.push(`${progress.outputTokens.toLocaleString()} tokens generated`);
|
|
70
|
-
} else if (characters > 0) {
|
|
71
|
-
details.push(`${characters.toLocaleString()} characters received`);
|
|
72
|
-
}
|
|
73
|
-
if (progress?.tokensPerSecond != null) {
|
|
74
|
-
details.push(`${progress.tokensPerSecond.toFixed(1)} tokens/s`);
|
|
75
|
-
}
|
|
76
|
-
if (now - state.lastActivityAt >= 15e3) {
|
|
77
|
-
details.push(
|
|
78
|
-
progress || characters > 0 ? `Last update ${duration(now - state.lastActivityAt)} ago` : "No progress updates received"
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
return /* @__PURE__ */ jsxs("div", { className: "db-ai-generation-status", children: [
|
|
82
|
-
/* @__PURE__ */ jsxs("p", { className: "db-ai-progress", role: "status", children: [
|
|
83
|
-
/* @__PURE__ */ jsx("span", { className: "db-ai-spinner", "aria-hidden": "true" }),
|
|
84
|
-
label,
|
|
85
|
-
percent != null ? ` ${Math.floor(percent)}%` : ""
|
|
86
|
-
] }),
|
|
87
|
-
percent != null && /* @__PURE__ */ jsx("progress", { "aria-label": label.replace("\u2026", ""), max: 100, value: percent }),
|
|
88
|
-
/* @__PURE__ */ jsx("p", { className: "db-ai-note", "aria-live": "off", children: details.join(" \xB7 ") })
|
|
89
|
-
] });
|
|
90
|
-
}
|
|
91
|
-
|
|
92
56
|
// src/Ai/draft-capacity.ts
|
|
93
57
|
import { AI_WIRE_LIMITS } from "@bendyline/docblocks/host";
|
|
94
58
|
function estimateTokens(text) {
|
|
@@ -117,7 +81,10 @@ function draftCapacityNotice(request, model, expectedOutput = "") {
|
|
|
117
81
|
}
|
|
118
82
|
|
|
119
83
|
// src/Ai/AiEditorAssistant.tsx
|
|
120
|
-
import { Fragment, jsx
|
|
84
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
85
|
+
var AiStudyDialog = lazy(
|
|
86
|
+
() => import("./AiStudyDialog-4TQZZGU3.js").then((module) => ({ default: module.AiStudyDialog }))
|
|
87
|
+
);
|
|
121
88
|
function AiToolbarControl({
|
|
122
89
|
ai,
|
|
123
90
|
readOnly = false,
|
|
@@ -127,24 +94,27 @@ function AiToolbarControl({
|
|
|
127
94
|
const {
|
|
128
95
|
activeView,
|
|
129
96
|
editorMode,
|
|
97
|
+
fileName,
|
|
130
98
|
getSelection,
|
|
131
99
|
insertBlockAfterCursor,
|
|
132
100
|
layoutMode,
|
|
133
101
|
markdownSource,
|
|
134
102
|
replaceSelection,
|
|
135
|
-
selectionVersion
|
|
103
|
+
selectionVersion,
|
|
104
|
+
workspaceContainer
|
|
136
105
|
} = useEditorContext();
|
|
137
106
|
const status = useAiStatus(ai);
|
|
138
|
-
const [menuOpen, setMenuOpen] =
|
|
139
|
-
const [draftMode, setDraftMode] =
|
|
140
|
-
const [capturedSelection, setCapturedSelection] =
|
|
141
|
-
const [diagramSelection, setDiagramSelection] =
|
|
107
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
108
|
+
const [draftMode, setDraftMode] = useState(null);
|
|
109
|
+
const [capturedSelection, setCapturedSelection] = useState(null);
|
|
110
|
+
const [diagramSelection, setDiagramSelection] = useState(null);
|
|
111
|
+
const [studyKind, setStudyKind] = useState(null);
|
|
142
112
|
const rootRef = useRef(null);
|
|
143
113
|
const { menuRef, triggerRef, handleMenuKeyDown, handleTriggerKeyDown, closeMenu } = useMenuKeyboard(menuOpen, setMenuOpen);
|
|
144
114
|
void activeView;
|
|
145
115
|
void selectionVersion;
|
|
146
116
|
const selection = getSelection();
|
|
147
|
-
|
|
117
|
+
useEffect(() => {
|
|
148
118
|
if (!menuOpen) return;
|
|
149
119
|
const onPointerDown = (event) => {
|
|
150
120
|
if (rootRef.current && !rootRef.current.contains(event.target)) setMenuOpen(false);
|
|
@@ -152,6 +122,7 @@ function AiToolbarControl({
|
|
|
152
122
|
document.addEventListener("pointerdown", onPointerDown);
|
|
153
123
|
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
154
124
|
}, [menuOpen]);
|
|
125
|
+
useEffect(() => setStudyKind(null), [workspaceContainer]);
|
|
155
126
|
const ready = status?.kind === "ready" && status.model !== null;
|
|
156
127
|
const openDiagram = () => {
|
|
157
128
|
const current = getSelection();
|
|
@@ -174,6 +145,7 @@ function AiToolbarControl({
|
|
|
174
145
|
return null;
|
|
175
146
|
}
|
|
176
147
|
const editableSelection = !readOnly && ready ? selection : null;
|
|
148
|
+
const canStudy = ready && !readOnly && workspaceContainer !== null && markdownSource.trim().length > 0;
|
|
177
149
|
const canCompose = editableSelection?.empty === true;
|
|
178
150
|
const canRewrite = editableSelection?.empty === false && (editableSelection.markdown ?? editableSelection.text).length <= AI_REWRITE_SELECTION_CHARACTERS;
|
|
179
151
|
const openDraft = (mode) => {
|
|
@@ -187,9 +159,9 @@ function AiToolbarControl({
|
|
|
187
159
|
setDraftMode(null);
|
|
188
160
|
setCapturedSelection(null);
|
|
189
161
|
};
|
|
190
|
-
return /* @__PURE__ */
|
|
191
|
-
/* @__PURE__ */
|
|
192
|
-
/* @__PURE__ */
|
|
162
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
163
|
+
/* @__PURE__ */ jsxs("div", { className: "db-toolbar-menu db-ai-toolbar", ref: rootRef, children: [
|
|
164
|
+
/* @__PURE__ */ jsx(
|
|
193
165
|
"button",
|
|
194
166
|
{
|
|
195
167
|
ref: triggerRef,
|
|
@@ -201,10 +173,10 @@ function AiToolbarControl({
|
|
|
201
173
|
"aria-haspopup": "menu",
|
|
202
174
|
"aria-expanded": menuOpen,
|
|
203
175
|
"data-tooltip": "AI actions",
|
|
204
|
-
children: /* @__PURE__ */
|
|
176
|
+
children: /* @__PURE__ */ jsx(SparklesGlyph, {})
|
|
205
177
|
}
|
|
206
178
|
),
|
|
207
|
-
menuOpen && /* @__PURE__ */
|
|
179
|
+
menuOpen && /* @__PURE__ */ jsxs(
|
|
208
180
|
"div",
|
|
209
181
|
{
|
|
210
182
|
ref: menuRef,
|
|
@@ -213,8 +185,8 @@ function AiToolbarControl({
|
|
|
213
185
|
"aria-label": "AI actions",
|
|
214
186
|
onKeyDown: handleMenuKeyDown,
|
|
215
187
|
children: [
|
|
216
|
-
!ready && /* @__PURE__ */
|
|
217
|
-
/* @__PURE__ */
|
|
188
|
+
!ready && /* @__PURE__ */ jsx("div", { className: "db-ai-toolbar-status", role: "status", children: unavailableSentence(status) }),
|
|
189
|
+
/* @__PURE__ */ jsx(
|
|
218
190
|
"button",
|
|
219
191
|
{
|
|
220
192
|
type: "button",
|
|
@@ -226,7 +198,7 @@ function AiToolbarControl({
|
|
|
226
198
|
children: "Add content\u2026"
|
|
227
199
|
}
|
|
228
200
|
),
|
|
229
|
-
/* @__PURE__ */
|
|
201
|
+
/* @__PURE__ */ jsx(
|
|
230
202
|
"button",
|
|
231
203
|
{
|
|
232
204
|
type: "button",
|
|
@@ -238,7 +210,7 @@ function AiToolbarControl({
|
|
|
238
210
|
children: "Rewrite selection\u2026"
|
|
239
211
|
}
|
|
240
212
|
),
|
|
241
|
-
/* @__PURE__ */
|
|
213
|
+
/* @__PURE__ */ jsx(
|
|
242
214
|
"button",
|
|
243
215
|
{
|
|
244
216
|
type: "button",
|
|
@@ -253,7 +225,7 @@ function AiToolbarControl({
|
|
|
253
225
|
children: openPanel === "review" ? "Show document review" : "Review document\u2026"
|
|
254
226
|
}
|
|
255
227
|
),
|
|
256
|
-
/* @__PURE__ */
|
|
228
|
+
/* @__PURE__ */ jsx(
|
|
257
229
|
"button",
|
|
258
230
|
{
|
|
259
231
|
type: "button",
|
|
@@ -268,7 +240,7 @@ function AiToolbarControl({
|
|
|
268
240
|
children: openPanel === "illustrate" ? "Show illustrations" : "Illustrate document\u2026"
|
|
269
241
|
}
|
|
270
242
|
),
|
|
271
|
-
/* @__PURE__ */
|
|
243
|
+
/* @__PURE__ */ jsx(
|
|
272
244
|
"button",
|
|
273
245
|
{
|
|
274
246
|
type: "button",
|
|
@@ -280,14 +252,31 @@ function AiToolbarControl({
|
|
|
280
252
|
children: "Insert diagram\u2026"
|
|
281
253
|
}
|
|
282
254
|
),
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
255
|
+
/* @__PURE__ */ jsx("div", { className: "db-toolbar-menu-divider", role: "separator" }),
|
|
256
|
+
STUDY_AID_KINDS.map((kind) => /* @__PURE__ */ jsx(
|
|
257
|
+
"button",
|
|
258
|
+
{
|
|
259
|
+
type: "button",
|
|
260
|
+
role: "menuitem",
|
|
261
|
+
tabIndex: -1,
|
|
262
|
+
className: "db-toolbar-menu-item",
|
|
263
|
+
disabled: !canStudy,
|
|
264
|
+
onClick: () => {
|
|
265
|
+
closeMenu(false);
|
|
266
|
+
setStudyKind(kind);
|
|
267
|
+
},
|
|
268
|
+
children: STUDY_AID_LABELS[kind].menu
|
|
269
|
+
},
|
|
270
|
+
kind
|
|
271
|
+
)),
|
|
272
|
+
ready && !selection && /* @__PURE__ */ jsx("div", { className: "db-ai-toolbar-status", children: "Switch to Write or Source to edit with AI." }),
|
|
273
|
+
ready && selection && !selection.empty && !canRewrite && /* @__PURE__ */ jsx("div", { className: "db-ai-toolbar-status", children: "The selection is too long to rewrite." }),
|
|
274
|
+
ready && layoutMode !== "document" && /* @__PURE__ */ jsx("div", { className: "db-ai-toolbar-status", children: "Switch to the Document layout to illustrate." })
|
|
286
275
|
]
|
|
287
276
|
}
|
|
288
277
|
)
|
|
289
278
|
] }),
|
|
290
|
-
draftMode && capturedSelection && /* @__PURE__ */
|
|
279
|
+
draftMode && capturedSelection && /* @__PURE__ */ jsx(
|
|
291
280
|
AiDraftDialog,
|
|
292
281
|
{
|
|
293
282
|
ai,
|
|
@@ -300,7 +289,7 @@ function AiToolbarControl({
|
|
|
300
289
|
onClose: closeDraft
|
|
301
290
|
}
|
|
302
291
|
),
|
|
303
|
-
diagramSelection && /* @__PURE__ */
|
|
292
|
+
diagramSelection && /* @__PURE__ */ jsx(
|
|
304
293
|
AiDiagramDialog,
|
|
305
294
|
{
|
|
306
295
|
ai,
|
|
@@ -310,7 +299,19 @@ function AiToolbarControl({
|
|
|
310
299
|
insertBlockAfterCursor,
|
|
311
300
|
onClose: () => setDiagramSelection(null)
|
|
312
301
|
}
|
|
313
|
-
)
|
|
302
|
+
),
|
|
303
|
+
studyKind && workspaceContainer && /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
|
|
304
|
+
AiStudyDialog,
|
|
305
|
+
{
|
|
306
|
+
ai,
|
|
307
|
+
kind: studyKind,
|
|
308
|
+
documentSource: markdownSource,
|
|
309
|
+
documentName: (fileName ?? "").split("/").pop() || "Untitled.md",
|
|
310
|
+
container: workspaceContainer,
|
|
311
|
+
onClose: () => setStudyKind(null)
|
|
312
|
+
},
|
|
313
|
+
studyKind
|
|
314
|
+
) })
|
|
314
315
|
] });
|
|
315
316
|
}
|
|
316
317
|
function AiDraftDialog({
|
|
@@ -324,17 +325,17 @@ function AiDraftDialog({
|
|
|
324
325
|
onClose
|
|
325
326
|
}) {
|
|
326
327
|
const defaultInstructions = mode === "compose" ? "Add a concise introduction for this document." : "Improve clarity and flow while preserving the meaning and voice.";
|
|
327
|
-
const [instructions, setInstructions] =
|
|
328
|
-
const [draft, setDraft] =
|
|
328
|
+
const [instructions, setInstructions] = useState("");
|
|
329
|
+
const [draft, setDraft] = useState(null);
|
|
329
330
|
const generatedDraftRef = useRef("");
|
|
330
331
|
const carriedEditsRef = useRef(false);
|
|
331
|
-
const [originalRequest, setOriginalRequest] =
|
|
332
|
-
const [incomplete, setIncomplete] =
|
|
333
|
-
const [prefixCharacters, setPrefixCharacters] =
|
|
334
|
-
const [running, setRunning] =
|
|
335
|
-
const [generation, setGeneration] =
|
|
336
|
-
const [error, setError] =
|
|
337
|
-
const [finishNote, setFinishNote] =
|
|
332
|
+
const [originalRequest, setOriginalRequest] = useState(null);
|
|
333
|
+
const [incomplete, setIncomplete] = useState(false);
|
|
334
|
+
const [prefixCharacters, setPrefixCharacters] = useState(0);
|
|
335
|
+
const [running, setRunning] = useState(false);
|
|
336
|
+
const [generation, setGeneration] = useState(null);
|
|
337
|
+
const [error, setError] = useState(null);
|
|
338
|
+
const [finishNote, setFinishNote] = useState(null);
|
|
338
339
|
const promptRef = useRef(null);
|
|
339
340
|
const handleRef = useRef(null);
|
|
340
341
|
const requestRef = useRef(0);
|
|
@@ -358,7 +359,7 @@ function AiDraftDialog({
|
|
|
358
359
|
model,
|
|
359
360
|
incomplete || mode === "compose" ? "" : selectedMarkdown
|
|
360
361
|
);
|
|
361
|
-
|
|
362
|
+
useEffect(
|
|
362
363
|
() => () => {
|
|
363
364
|
requestRef.current += 1;
|
|
364
365
|
handleRef.current?.cancel();
|
|
@@ -415,7 +416,7 @@ function AiDraftDialog({
|
|
|
415
416
|
}
|
|
416
417
|
const emptyContinuation = Boolean(prefix) && !event.completion.text.trim();
|
|
417
418
|
const finished = event.completion.finishReason === "stop" && !emptyContinuation;
|
|
418
|
-
generatedDraftRef.current = prefix || !finished ? prefix + event.completion.text : sanitizeGeneratedMarkdown(event.completion.text);
|
|
419
|
+
generatedDraftRef.current = prefix || !finished ? prefix + event.completion.text : sanitizeGeneratedMarkdown(event.completion.text, documentSource);
|
|
419
420
|
setDraft(generatedDraftRef.current);
|
|
420
421
|
setIncomplete(!finished && Boolean(generatedDraftRef.current.trim()));
|
|
421
422
|
if (event.completion.finishReason === "cancelled") {
|
|
@@ -434,7 +435,7 @@ function AiDraftDialog({
|
|
|
434
435
|
});
|
|
435
436
|
handleRef.current = handle;
|
|
436
437
|
},
|
|
437
|
-
[ai]
|
|
438
|
+
[ai, documentSource]
|
|
438
439
|
);
|
|
439
440
|
const generate = useCallback(async () => {
|
|
440
441
|
if (running || draftCapacityNotice(draftRequest, model)?.blocked) return;
|
|
@@ -460,7 +461,7 @@ function AiDraftDialog({
|
|
|
460
461
|
const stop = useCallback(() => handleRef.current?.cancel(), []);
|
|
461
462
|
const apply = useCallback(async () => {
|
|
462
463
|
if (running) return;
|
|
463
|
-
const replacement = sanitizeGeneratedMarkdown(draft ?? "");
|
|
464
|
+
const replacement = sanitizeGeneratedMarkdown(draft ?? "", documentSource);
|
|
464
465
|
if (!replacement) return;
|
|
465
466
|
if (incomplete) {
|
|
466
467
|
const previousRequest = requestRef.current;
|
|
@@ -493,6 +494,7 @@ function AiDraftDialog({
|
|
|
493
494
|
}, [
|
|
494
495
|
capturedSelection,
|
|
495
496
|
selectedMarkdown,
|
|
497
|
+
documentSource,
|
|
496
498
|
draft,
|
|
497
499
|
getSelection,
|
|
498
500
|
mode,
|
|
@@ -502,43 +504,43 @@ function AiDraftDialog({
|
|
|
502
504
|
incomplete,
|
|
503
505
|
confirmAction
|
|
504
506
|
]);
|
|
505
|
-
const dialog = /* @__PURE__ */
|
|
507
|
+
const dialog = /* @__PURE__ */ jsxs(
|
|
506
508
|
Dialog,
|
|
507
509
|
{
|
|
508
510
|
title: mode === "compose" ? "Add content with AI" : "Rewrite with AI",
|
|
509
511
|
onClose,
|
|
510
512
|
size: "wide",
|
|
511
|
-
closeOnBackdrop:
|
|
513
|
+
closeOnBackdrop: false,
|
|
512
514
|
initialFocusRef: promptRef,
|
|
513
515
|
className: "db-ai-draft-dialog",
|
|
514
516
|
bodyClassName: "db-ai-draft-body",
|
|
515
517
|
footerClassName: "db-ai-draft-footer",
|
|
516
|
-
footer: /* @__PURE__ */
|
|
517
|
-
(running || finishNote || error || capacity) && /* @__PURE__ */
|
|
518
|
-
running && generation && /* @__PURE__ */
|
|
518
|
+
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
519
|
+
(running || finishNote || error || capacity) && /* @__PURE__ */ jsxs("div", { className: "db-ai-footer-status", children: [
|
|
520
|
+
running && generation && /* @__PURE__ */ jsx(
|
|
519
521
|
AiGenerationStatus,
|
|
520
522
|
{
|
|
521
523
|
state: generation,
|
|
522
524
|
characters: Math.max(0, (draft?.length ?? 0) - prefixCharacters)
|
|
523
525
|
}
|
|
524
526
|
),
|
|
525
|
-
finishNote && /* @__PURE__ */
|
|
526
|
-
!running && capacity && /* @__PURE__ */
|
|
527
|
-
error && /* @__PURE__ */
|
|
527
|
+
finishNote && /* @__PURE__ */ jsx("p", { className: "db-ai-note", children: finishNote }),
|
|
528
|
+
!running && capacity && /* @__PURE__ */ jsx("p", { className: "db-ai-note", role: "status", children: capacity.message }),
|
|
529
|
+
error && /* @__PURE__ */ jsx("p", { className: "db-ai-error", role: "alert", children: error })
|
|
528
530
|
] }),
|
|
529
|
-
/* @__PURE__ */
|
|
530
|
-
/* @__PURE__ */
|
|
531
|
-
running ? /* @__PURE__ */
|
|
532
|
-
/* @__PURE__ */
|
|
533
|
-
incomplete && draft.trim() && /* @__PURE__ */
|
|
531
|
+
/* @__PURE__ */ jsxs("div", { className: "db-ai-draft-actions", children: [
|
|
532
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: onClose, children: "Cancel" }),
|
|
533
|
+
running ? /* @__PURE__ */ jsx("button", { type: "button", className: "db-ai-secondary-action", onClick: stop, children: "Stop" }) : draft !== null ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
534
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "db-ai-secondary-action", onClick: generate, children: "Regenerate" }),
|
|
535
|
+
incomplete && draft.trim() && /* @__PURE__ */ jsx("button", { type: "button", disabled: capacity?.blocked, onClick: continueDraft, children: "Continue draft" })
|
|
534
536
|
] }) : null,
|
|
535
|
-
/* @__PURE__ */
|
|
537
|
+
/* @__PURE__ */ jsx("button", { type: "button", disabled: !draft?.trim() || running, onClick: apply, children: mode === "compose" ? "Insert" : "Replace selection" })
|
|
536
538
|
] })
|
|
537
539
|
] }),
|
|
538
540
|
children: [
|
|
539
|
-
/* @__PURE__ */
|
|
540
|
-
/* @__PURE__ */
|
|
541
|
-
/* @__PURE__ */
|
|
541
|
+
/* @__PURE__ */ jsxs("label", { className: "db-ai-field", children: [
|
|
542
|
+
/* @__PURE__ */ jsx("span", { children: mode === "compose" ? "What should be added?" : "How should this be rewritten?" }),
|
|
543
|
+
/* @__PURE__ */ jsx(
|
|
542
544
|
"textarea",
|
|
543
545
|
{
|
|
544
546
|
ref: promptRef,
|
|
@@ -550,14 +552,14 @@ function AiDraftDialog({
|
|
|
550
552
|
}
|
|
551
553
|
)
|
|
552
554
|
] }),
|
|
553
|
-
mode === "rewrite" && /* @__PURE__ */
|
|
554
|
-
/* @__PURE__ */
|
|
555
|
-
/* @__PURE__ */
|
|
555
|
+
mode === "rewrite" && /* @__PURE__ */ jsxs("div", { className: "db-ai-selection-preview", children: [
|
|
556
|
+
/* @__PURE__ */ jsx("span", { children: "Selected text" }),
|
|
557
|
+
/* @__PURE__ */ jsx("blockquote", { tabIndex: 0, "aria-label": "Selected text", children: capturedSelection.text })
|
|
556
558
|
] }),
|
|
557
|
-
draft === null ? /* @__PURE__ */
|
|
558
|
-
/* @__PURE__ */
|
|
559
|
-
/* @__PURE__ */
|
|
560
|
-
/* @__PURE__ */
|
|
559
|
+
draft === null ? /* @__PURE__ */ jsx("div", { className: "db-ai-draft-start", children: /* @__PURE__ */ jsx("button", { type: "button", onClick: generate, disabled: capacity?.blocked, children: capacity && !capacity.blocked ? "Generate anyway" : "Generate" }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
560
|
+
/* @__PURE__ */ jsxs("label", { className: "db-ai-field db-ai-draft-field", children: [
|
|
561
|
+
/* @__PURE__ */ jsx("span", { children: "AI draft" }),
|
|
562
|
+
/* @__PURE__ */ jsx(
|
|
561
563
|
"textarea",
|
|
562
564
|
{
|
|
563
565
|
className: "db-ai-draft-output",
|
|
@@ -570,26 +572,49 @@ function AiDraftDialog({
|
|
|
570
572
|
}
|
|
571
573
|
)
|
|
572
574
|
] }),
|
|
573
|
-
/* @__PURE__ */
|
|
575
|
+
/* @__PURE__ */ jsxs("p", { className: "db-ai-disclaimer", children: [
|
|
576
|
+
"Review and edit AI-generated content before adding it.",
|
|
577
|
+
!running && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
578
|
+
" ",
|
|
579
|
+
/* @__PURE__ */ jsx(
|
|
580
|
+
AiReportButton,
|
|
581
|
+
{
|
|
582
|
+
feature: mode === "compose" ? "Add content" : "Rewrite selection",
|
|
583
|
+
getOutput: () => generatedDraftRef.current,
|
|
584
|
+
model: model?.label ?? null
|
|
585
|
+
}
|
|
586
|
+
)
|
|
587
|
+
] })
|
|
588
|
+
] })
|
|
574
589
|
] })
|
|
575
590
|
]
|
|
576
591
|
}
|
|
577
592
|
);
|
|
578
|
-
const dialogs = /* @__PURE__ */
|
|
593
|
+
const dialogs = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
579
594
|
dialog,
|
|
580
595
|
confirmDialog
|
|
581
596
|
] });
|
|
582
597
|
return typeof document === "undefined" ? dialogs : createPortal(dialogs, document.body);
|
|
583
598
|
}
|
|
599
|
+
function formatReviewFindings(findings) {
|
|
600
|
+
return findings.map(
|
|
601
|
+
(finding) => [
|
|
602
|
+
`${finding.severity} \xB7 ${finding.category}: ${finding.message}`,
|
|
603
|
+
finding.rationale,
|
|
604
|
+
finding.replacement !== null ? `Suggested: ${finding.replacement}` : ""
|
|
605
|
+
].filter(Boolean).join("\n")
|
|
606
|
+
).join("\n\n");
|
|
607
|
+
}
|
|
584
608
|
function AiReviewPanel({ ai, onClose }) {
|
|
585
609
|
const { markdownSource, replaceAll } = useEditorContext();
|
|
586
|
-
const [phase, setPhase] =
|
|
587
|
-
const [findings, setFindings] =
|
|
588
|
-
const [reviewedSource, setReviewedSource] =
|
|
589
|
-
const [resolvedIds, setResolvedIds] =
|
|
590
|
-
const [error, setError] =
|
|
591
|
-
const [cutOff, setCutOff] =
|
|
592
|
-
const [applyErrorId, setApplyErrorId] =
|
|
610
|
+
const [phase, setPhase] = useState("idle");
|
|
611
|
+
const [findings, setFindings] = useState([]);
|
|
612
|
+
const [reviewedSource, setReviewedSource] = useState(null);
|
|
613
|
+
const [resolvedIds, setResolvedIds] = useState(() => /* @__PURE__ */ new Set());
|
|
614
|
+
const [error, setError] = useState(null);
|
|
615
|
+
const [cutOff, setCutOff] = useState(false);
|
|
616
|
+
const [applyErrorId, setApplyErrorId] = useState(null);
|
|
617
|
+
const [reviewModel, setReviewModel] = useState(null);
|
|
593
618
|
const handleRef = useRef(null);
|
|
594
619
|
const requestRef = useRef(0);
|
|
595
620
|
const startedRef = useRef(false);
|
|
@@ -609,7 +634,7 @@ function AiReviewPanel({ ai, onClose }) {
|
|
|
609
634
|
setPhase("ready");
|
|
610
635
|
return;
|
|
611
636
|
}
|
|
612
|
-
if (markdownSource
|
|
637
|
+
if (reviewDocumentCharacters(markdownSource) > AI_REVIEW_DOCUMENT_CHARACTERS) {
|
|
613
638
|
setError("This document is too long for one AI review pass. Review a shorter document.");
|
|
614
639
|
setPhase("error");
|
|
615
640
|
return;
|
|
@@ -641,18 +666,19 @@ function AiReviewPanel({ ai, onClose }) {
|
|
|
641
666
|
return;
|
|
642
667
|
}
|
|
643
668
|
setCutOff(truncated);
|
|
644
|
-
|
|
669
|
+
setReviewModel(event.completion.model || null);
|
|
670
|
+
setFindings(withoutPictureFindings(parsed, source));
|
|
645
671
|
setReviewedSource(source);
|
|
646
672
|
setPhase("ready");
|
|
647
673
|
});
|
|
648
674
|
handleRef.current = handle;
|
|
649
675
|
}, [ai, markdownSource]);
|
|
650
|
-
|
|
676
|
+
useEffect(() => {
|
|
651
677
|
if (startedRef.current) return;
|
|
652
678
|
startedRef.current = true;
|
|
653
679
|
runReview();
|
|
654
680
|
}, [runReview]);
|
|
655
|
-
|
|
681
|
+
useEffect(
|
|
656
682
|
() => () => {
|
|
657
683
|
requestRef.current += 1;
|
|
658
684
|
handleRef.current?.cancel();
|
|
@@ -681,17 +707,17 @@ function AiReviewPanel({ ai, onClose }) {
|
|
|
681
707
|
}, []);
|
|
682
708
|
const visibleFindings = findings.filter((finding) => !resolvedIds.has(finding.id));
|
|
683
709
|
const documentChanged = reviewedSource !== null && markdownSource !== reviewedSource;
|
|
684
|
-
return /* @__PURE__ */
|
|
685
|
-
/* @__PURE__ */
|
|
686
|
-
/* @__PURE__ */
|
|
687
|
-
/* @__PURE__ */
|
|
688
|
-
phase === "ready" && /* @__PURE__ */
|
|
710
|
+
return /* @__PURE__ */ jsxs("aside", { className: "db-ai-review-panel", "aria-label": "AI document review", children: [
|
|
711
|
+
/* @__PURE__ */ jsxs("div", { className: "db-ai-review-header", children: [
|
|
712
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
713
|
+
/* @__PURE__ */ jsx("h2", { children: "AI review" }),
|
|
714
|
+
phase === "ready" && /* @__PURE__ */ jsxs("span", { className: "db-ai-review-count", children: [
|
|
689
715
|
visibleFindings.length,
|
|
690
716
|
" ",
|
|
691
717
|
visibleFindings.length === 1 ? "finding" : "findings"
|
|
692
718
|
] })
|
|
693
719
|
] }),
|
|
694
|
-
/* @__PURE__ */
|
|
720
|
+
/* @__PURE__ */ jsx(
|
|
695
721
|
"button",
|
|
696
722
|
{
|
|
697
723
|
type: "button",
|
|
@@ -702,48 +728,48 @@ function AiReviewPanel({ ai, onClose }) {
|
|
|
702
728
|
}
|
|
703
729
|
)
|
|
704
730
|
] }),
|
|
705
|
-
/* @__PURE__ */
|
|
706
|
-
phase === "running" && /* @__PURE__ */
|
|
707
|
-
/* @__PURE__ */
|
|
731
|
+
/* @__PURE__ */ jsx("div", { className: "db-ai-review-actions", children: phase === "running" ? /* @__PURE__ */ jsx("button", { type: "button", onClick: cancelReview, children: "Stop review" }) : /* @__PURE__ */ jsx("button", { type: "button", onClick: runReview, children: phase === "idle" ? "Review document" : "Review again" }) }),
|
|
732
|
+
phase === "running" && /* @__PURE__ */ jsxs("div", { className: "db-ai-review-empty", role: "status", children: [
|
|
733
|
+
/* @__PURE__ */ jsx("span", { className: "db-ai-spinner", "aria-hidden": "true" }),
|
|
708
734
|
" Reviewing this document\u2026"
|
|
709
735
|
] }),
|
|
710
|
-
phase === "error" && error && /* @__PURE__ */
|
|
711
|
-
cutOff && phase === "ready" && /* @__PURE__ */
|
|
736
|
+
phase === "error" && error && /* @__PURE__ */ jsx("div", { className: "db-ai-review-empty db-ai-error", role: "alert", children: error }),
|
|
737
|
+
cutOff && phase === "ready" && /* @__PURE__ */ jsxs("div", { className: "db-ai-review-stale", role: "status", children: [
|
|
712
738
|
"The AI ran out of room after ",
|
|
713
739
|
findings.length,
|
|
714
740
|
" ",
|
|
715
741
|
findings.length === 1 ? "finding" : "findings",
|
|
716
742
|
", so there may be more. After you apply these, review again to look for others."
|
|
717
743
|
] }),
|
|
718
|
-
documentChanged && phase === "ready" && /* @__PURE__ */
|
|
719
|
-
phase === "ready" && visibleFindings.length === 0 && /* @__PURE__ */
|
|
720
|
-
/* @__PURE__ */
|
|
744
|
+
documentChanged && phase === "ready" && /* @__PURE__ */ jsx("div", { className: "db-ai-review-stale", role: "status", children: "The document changed after this review. Suggestions are applied only when their original text still matches exactly." }),
|
|
745
|
+
phase === "ready" && visibleFindings.length === 0 && /* @__PURE__ */ jsx("div", { className: "db-ai-review-empty", children: findings.length === 0 ? "Nothing to report." : "All findings have been handled." }),
|
|
746
|
+
/* @__PURE__ */ jsx("ol", { className: "db-ai-review-list", children: visibleFindings.map((finding) => {
|
|
721
747
|
const canApply = finding.replacement !== null && findUniqueExcerpt(markdownSource, finding.originalText) !== null;
|
|
722
|
-
return /* @__PURE__ */
|
|
723
|
-
/* @__PURE__ */
|
|
724
|
-
/* @__PURE__ */
|
|
748
|
+
return /* @__PURE__ */ jsxs("li", { className: "db-ai-review-finding", children: [
|
|
749
|
+
/* @__PURE__ */ jsxs("div", { className: "db-ai-review-finding-meta", children: [
|
|
750
|
+
/* @__PURE__ */ jsx(
|
|
725
751
|
"span",
|
|
726
752
|
{
|
|
727
753
|
className: `db-ai-review-severity db-ai-review-severity--${finding.severity}`,
|
|
728
754
|
children: finding.severity
|
|
729
755
|
}
|
|
730
756
|
),
|
|
731
|
-
/* @__PURE__ */
|
|
757
|
+
/* @__PURE__ */ jsx("span", { children: finding.category })
|
|
732
758
|
] }),
|
|
733
|
-
/* @__PURE__ */
|
|
734
|
-
finding.rationale && /* @__PURE__ */
|
|
735
|
-
/* @__PURE__ */
|
|
736
|
-
/* @__PURE__ */
|
|
737
|
-
/* @__PURE__ */
|
|
759
|
+
/* @__PURE__ */ jsx("h3", { children: finding.message }),
|
|
760
|
+
finding.rationale && /* @__PURE__ */ jsx("p", { children: finding.rationale }),
|
|
761
|
+
/* @__PURE__ */ jsxs("div", { className: "db-ai-review-excerpt", children: [
|
|
762
|
+
/* @__PURE__ */ jsx("span", { children: "Current text" }),
|
|
763
|
+
/* @__PURE__ */ jsx("blockquote", { children: finding.originalText })
|
|
738
764
|
] }),
|
|
739
|
-
finding.replacement !== null && /* @__PURE__ */
|
|
740
|
-
/* @__PURE__ */
|
|
741
|
-
/* @__PURE__ */
|
|
765
|
+
finding.replacement !== null && /* @__PURE__ */ jsxs("div", { className: "db-ai-review-excerpt db-ai-review-excerpt--replacement", children: [
|
|
766
|
+
/* @__PURE__ */ jsx("span", { children: "Suggested" }),
|
|
767
|
+
/* @__PURE__ */ jsx("blockquote", { children: finding.replacement })
|
|
742
768
|
] }),
|
|
743
|
-
applyErrorId === finding.id && /* @__PURE__ */
|
|
744
|
-
/* @__PURE__ */
|
|
745
|
-
/* @__PURE__ */
|
|
746
|
-
finding.replacement !== null && /* @__PURE__ */
|
|
769
|
+
applyErrorId === finding.id && /* @__PURE__ */ jsx("p", { className: "db-ai-error", role: "alert", children: "This text no longer has one exact match. Review the document again before applying it." }),
|
|
770
|
+
/* @__PURE__ */ jsxs("div", { className: "db-ai-review-finding-actions", children: [
|
|
771
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => dismissFinding(finding.id), children: "Dismiss" }),
|
|
772
|
+
finding.replacement !== null && /* @__PURE__ */ jsx(
|
|
747
773
|
"button",
|
|
748
774
|
{
|
|
749
775
|
type: "button",
|
|
@@ -756,11 +782,24 @@ function AiReviewPanel({ ai, onClose }) {
|
|
|
756
782
|
] })
|
|
757
783
|
] }, finding.id);
|
|
758
784
|
}) }),
|
|
759
|
-
/* @__PURE__ */
|
|
785
|
+
/* @__PURE__ */ jsxs("p", { className: "db-ai-review-disclaimer", children: [
|
|
786
|
+
"AI suggestions can be wrong. Review each change.",
|
|
787
|
+
phase === "ready" && findings.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
788
|
+
" ",
|
|
789
|
+
/* @__PURE__ */ jsx(
|
|
790
|
+
AiReportButton,
|
|
791
|
+
{
|
|
792
|
+
feature: "Review document",
|
|
793
|
+
getOutput: () => formatReviewFindings(findings),
|
|
794
|
+
model: reviewModel
|
|
795
|
+
}
|
|
796
|
+
)
|
|
797
|
+
] })
|
|
798
|
+
] })
|
|
760
799
|
] });
|
|
761
800
|
}
|
|
762
801
|
function SparklesGlyph() {
|
|
763
|
-
return /* @__PURE__ */
|
|
802
|
+
return /* @__PURE__ */ jsxs(
|
|
764
803
|
"svg",
|
|
765
804
|
{
|
|
766
805
|
viewBox: "0 0 18 18",
|
|
@@ -773,8 +812,8 @@ function SparklesGlyph() {
|
|
|
773
812
|
strokeLinejoin: "round",
|
|
774
813
|
"aria-hidden": "true",
|
|
775
814
|
children: [
|
|
776
|
-
/* @__PURE__ */
|
|
777
|
-
/* @__PURE__ */
|
|
815
|
+
/* @__PURE__ */ jsx("path", { d: "M7.2 2.2c.45 2.2 1.75 3.5 3.95 3.95-2.2.45-3.5 1.75-3.95 3.95-.45-2.2-1.75-3.5-3.95-3.95C5.45 5.7 6.75 4.4 7.2 2.2Z" }),
|
|
816
|
+
/* @__PURE__ */ jsx("path", { d: "M13.1 9.2c.3 1.45 1.15 2.3 2.6 2.6-1.45.3-2.3 1.15-2.6 2.6-.3-1.45-1.15-2.3-2.6-2.6 1.45-.3 2.3-1.15 2.6-2.6Z" })
|
|
778
817
|
]
|
|
779
818
|
}
|
|
780
819
|
);
|