@bendyline/docblocks-react 2.7.7 → 2.7.8

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 (42) hide show
  1. package/THIRD_PARTY_NOTICES.txt +205 -35
  2. package/dist/{AiDiagrams-7KBAOXPB.js → AiDiagrams-2IKKSBAU.js} +5 -3
  3. package/dist/{AiEditorAssistant-4B2PFFOT.js → AiEditorAssistant-ANYGVG7P.js} +46 -6
  4. package/dist/AiImagePanel-OXWEFOBC.js +314 -0
  5. package/dist/{AiStudyDialog-JPVFFZBB.js → AiStudyDialog-T3FY74HY.js} +2 -2
  6. package/dist/{AudioExportDialog-EICCBSSE.js → AudioExportDialog-CBMSLQBS.js} +1 -1
  7. package/dist/DocumentConflictDialog-4R4QMKBT.js +217 -0
  8. package/dist/{ExportDialog-EYDGTJ3S.js → ExportDialog-FNFD53SY.js} +2 -2
  9. package/dist/{ExportToolbarControls-2XY2CKGH.js → ExportToolbarControls-ZOYH4I36.js} +108 -71
  10. package/dist/{GitUI-F3CNBURE.js → GitUI-3FANM7I3.js} +88 -155
  11. package/dist/{KeyboardShortcutsDialog-HVKDFP5Z.js → KeyboardShortcutsDialog-7XJTDIX2.js} +1 -1
  12. package/dist/{PresentationMaker-VYQKNL3L.js → PresentationMaker-COLTVMTS.js} +3 -3
  13. package/dist/{QuickOpenDialog-3QEX7RZG.js → QuickOpenDialog-U2FWCEPA.js} +1 -1
  14. package/dist/{Settings-CfhY489G.d.ts → Settings-ButIB78y.d.ts} +20 -4
  15. package/dist/{ShareDialog-PF7D7ORP.js → ShareDialog-WOWHI6YI.js} +1 -1
  16. package/dist/{SpeechToolbarControl-E6OSJGHL.js → SpeechToolbarControl-BZW6IVZM.js} +1 -1
  17. package/dist/{TextFromNarration-ME6FAYWO.js → TextFromNarration-6BYTUKVZ.js} +2 -2
  18. package/dist/chunk-27E2OON2.js +90 -0
  19. package/dist/{chunk-5O5YTOXG.js → chunk-5YMULR6A.js} +13 -1
  20. package/dist/{chunk-5OIHB3SR.js → chunk-A7BK6QFL.js} +362 -25
  21. package/dist/{chunk-T2PA7QEF.js → chunk-BGA5UVKL.js} +58 -17
  22. package/dist/{chunk-N7AKLJPY.js → chunk-CXLXGFG2.js} +16 -8
  23. package/dist/{chunk-7CSBRAMT.js → chunk-F7TR5EKX.js} +8 -6
  24. package/dist/chunk-HIKMOKBQ.js +37 -0
  25. package/dist/chunk-OW6JWRAA.js +66 -0
  26. package/dist/{chunk-BNKIPCXO.js → chunk-QXBV3G6T.js} +5 -3
  27. package/dist/{chunk-7HBKKXRV.js → chunk-TN7BY5N3.js} +722 -281
  28. package/dist/{chunk-VJPGZCFS.js → chunk-UYJCL6OW.js} +4 -2
  29. package/dist/{chunk-G63YXKBC.js → chunk-WDNE5TI3.js} +13 -2
  30. package/dist/{chunk-KBUHWZCF.js → chunk-YX275NPE.js} +1 -1
  31. package/dist/editor.js +3 -3
  32. package/dist/export/index.d.ts +16 -162
  33. package/dist/export/index.js +6 -4
  34. package/dist/image-save-D18dJrUr.d.ts +182 -0
  35. package/dist/index.d.ts +15 -11
  36. package/dist/index.js +1154 -964
  37. package/dist/{run-export-SPNNH32M.js → run-export-C3PLLWLS.js} +2 -2
  38. package/dist/settings/index.d.ts +1 -1
  39. package/dist/settings/index.js +3 -2
  40. package/package.json +8 -8
  41. package/src/styles/docblocks.css +302 -10
  42. package/dist/DocumentConflictDialog-DZ2BV2IH.js +0 -111
@@ -0,0 +1,314 @@
1
+ import {
2
+ imageAltText,
3
+ imageMarkdown,
4
+ saveGeneratedImage
5
+ } from "./chunk-HIKMOKBQ.js";
6
+ import {
7
+ imageAccessSentence,
8
+ imageProgressFraction,
9
+ imageProgressText,
10
+ useImageAccess
11
+ } from "./chunk-OW6JWRAA.js";
12
+
13
+ // src/Ai/AiImagePanel.tsx
14
+ import { useCallback, useEffect, useRef, useState } from "react";
15
+ import { useEditorContext } from "@bendyline/squisq-editor-react";
16
+ import { AI_IMAGE_ASPECTS } from "@bendyline/docblocks/host";
17
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
18
+ var ASPECT_LABELS = {
19
+ "1:1": "Square",
20
+ "4:3": "Landscape",
21
+ "3:4": "Portrait",
22
+ "16:9": "Wide",
23
+ "9:16": "Tall"
24
+ };
25
+ var PREFILL_CHARACTERS = 600;
26
+ function imagesOf(ai) {
27
+ return ai.images;
28
+ }
29
+ function AiImagePanel({ ai, onClose }) {
30
+ const images = imagesOf(ai);
31
+ const access = useImageAccess(images);
32
+ const { getSelection, insertBlockAfterCursor, mediaProvider, bumpMediaRevision } = useEditorContext();
33
+ const [prompt, setPrompt] = useState(() => {
34
+ const selection = getSelection();
35
+ const text = selection && !selection.empty ? selection.text.trim() : "";
36
+ return text.slice(0, PREFILL_CHARACTERS);
37
+ });
38
+ const [aspect, setAspect] = useState("1:1");
39
+ const [model, setModel] = useState(null);
40
+ const [models, setModels] = useState(null);
41
+ const [modelsError, setModelsError] = useState(null);
42
+ const [results, setResults] = useState([]);
43
+ const [notice, setNotice] = useState(null);
44
+ const [allowing, setAllowing] = useState(false);
45
+ const handle = useRef(null);
46
+ const urls = useRef(/* @__PURE__ */ new Set());
47
+ const counter = useRef(0);
48
+ const ready = access?.kind === "ready";
49
+ useEffect(() => {
50
+ if (!images) return;
51
+ let live = true;
52
+ void images.getPreferences().then((preferences) => {
53
+ if (!live) return;
54
+ setAspect(preferences.aspect);
55
+ setModel(preferences.model);
56
+ }).catch(() => void 0);
57
+ return () => {
58
+ live = false;
59
+ };
60
+ }, [images]);
61
+ useEffect(() => {
62
+ if (!images || !ready) return;
63
+ let live = true;
64
+ void images.models().then((result) => {
65
+ if (!live) return;
66
+ if (result.ok) {
67
+ setModels(result.value);
68
+ setModelsError(null);
69
+ } else {
70
+ setModelsError(result.error.message);
71
+ }
72
+ });
73
+ return () => {
74
+ live = false;
75
+ };
76
+ }, [images, ready]);
77
+ useEffect(() => {
78
+ const owned = urls.current;
79
+ return () => {
80
+ handle.current?.cancel();
81
+ for (const url of owned) URL.revokeObjectURL(url);
82
+ owned.clear();
83
+ };
84
+ }, []);
85
+ const installed = (models ?? []).filter((entry) => entry.state === "installed");
86
+ const chosen = installed.find((entry) => entry.id === model) ?? installed[0] ?? null;
87
+ const rendering = results.some((result) => result.status === "rendering");
88
+ const update = useCallback((key, change) => {
89
+ setResults(
90
+ (current) => current.map((result) => result.key === key ? { ...result, ...change } : result)
91
+ );
92
+ }, []);
93
+ const render = (text, shape) => {
94
+ if (!images || !chosen || !text.trim() || handle.current) return;
95
+ counter.current += 1;
96
+ const key = `image-${String(counter.current)}`;
97
+ setNotice(null);
98
+ setResults((current) => [
99
+ {
100
+ key,
101
+ prompt: text.trim(),
102
+ aspect: shape,
103
+ model: chosen.id,
104
+ status: "rendering",
105
+ progress: null,
106
+ image: null,
107
+ url: null,
108
+ alt: imageAltText(text),
109
+ error: null,
110
+ inserted: false
111
+ },
112
+ ...current
113
+ ]);
114
+ const started = images.generate(
115
+ { prompt: text.trim(), aspect: shape, model: chosen.id },
116
+ (event) => {
117
+ if (event.kind === "progress") update(key, { progress: event.progress });
118
+ }
119
+ );
120
+ handle.current = started;
121
+ void started.done.then((outcome) => {
122
+ if (handle.current === started) handle.current = null;
123
+ if (outcome.ok) {
124
+ const url = URL.createObjectURL(new Blob([outcome.value.data], { type: "image/png" }));
125
+ urls.current.add(url);
126
+ update(key, { status: "done", image: outcome.value, url, progress: null });
127
+ } else {
128
+ update(key, {
129
+ status: outcome.error.code === "cancelled" ? "stopped" : "failed",
130
+ error: outcome.error.code === "cancelled" ? null : outcome.error.message,
131
+ progress: null
132
+ });
133
+ }
134
+ });
135
+ };
136
+ const chooseAspect = (next) => {
137
+ setAspect(next);
138
+ void images?.setPreferences({ aspect: next }).catch(() => void 0);
139
+ };
140
+ const chooseModel = (next) => {
141
+ setModel(next);
142
+ void images?.setPreferences({ model: next }).catch(() => void 0);
143
+ };
144
+ const insert = async (result) => {
145
+ if (!result.image) return;
146
+ if (!mediaProvider) {
147
+ setNotice("Save the document in a folder to insert images.");
148
+ return;
149
+ }
150
+ try {
151
+ const path = await saveGeneratedImage(mediaProvider, result.image, result.prompt);
152
+ bumpMediaRevision?.();
153
+ if (!insertBlockAfterCursor(imageMarkdown(path, result.alt || result.prompt))) {
154
+ setNotice("Switch to Write or Source view, place the cursor, then insert again.");
155
+ return;
156
+ }
157
+ update(result.key, { inserted: true });
158
+ } catch (error) {
159
+ setNotice(
160
+ `DocBlocks could not save the image beside the document. ${error instanceof Error ? error.message : ""}`.trim()
161
+ );
162
+ }
163
+ };
164
+ const discard = (result) => {
165
+ if (result.url) {
166
+ URL.revokeObjectURL(result.url);
167
+ urls.current.delete(result.url);
168
+ }
169
+ setResults((current) => current.filter((entry) => entry.key !== result.key));
170
+ };
171
+ const allow = async () => {
172
+ if (!images) return;
173
+ setAllowing(true);
174
+ const outcome = await images.requestAccess();
175
+ setAllowing(false);
176
+ if (!outcome.ok) setNotice(outcome.error.message);
177
+ };
178
+ const blocked = imageAccessSentence(access);
179
+ const modelLabel = (id) => (models ?? []).find((entry) => entry.id === id)?.label ?? "the image model";
180
+ return /* @__PURE__ */ jsxs("aside", { className: "db-ai-review-panel db-ai-image-panel", "aria-label": "Generate image", children: [
181
+ /* @__PURE__ */ jsxs("div", { className: "db-ai-review-header", children: [
182
+ /* @__PURE__ */ jsxs("div", { children: [
183
+ /* @__PURE__ */ jsx("h2", { children: "Generate image" }),
184
+ chosen && /* @__PURE__ */ jsx("span", { className: "db-ai-review-count", children: chosen.label })
185
+ ] }),
186
+ /* @__PURE__ */ jsx(
187
+ "button",
188
+ {
189
+ type: "button",
190
+ className: "db-ai-review-close",
191
+ onClick: onClose,
192
+ "aria-label": "Close Generate image",
193
+ children: "\xD7"
194
+ }
195
+ )
196
+ ] }),
197
+ blocked && /* @__PURE__ */ jsxs("div", { className: "db-ai-review-empty", role: "status", children: [
198
+ access?.kind === "awaiting-approval" && access.verificationCode ? /* @__PURE__ */ jsxs(Fragment, { children: [
199
+ "In Gezel, approve DocBlocks and enter this code:",
200
+ /* @__PURE__ */ jsx("span", { className: "db-ai-image-code", children: access.verificationCode })
201
+ ] }) : blocked,
202
+ access?.kind === "needs-approval" && /* @__PURE__ */ jsx("div", { className: "db-ai-illustrate-bar db-ai-image-allow", children: /* @__PURE__ */ jsx("button", { type: "button", disabled: allowing, onClick: () => void allow(), children: "Allow image generation\u2026" }) })
203
+ ] }),
204
+ ready && models !== null && installed.length === 0 && /* @__PURE__ */ jsx("div", { className: "db-ai-review-empty", role: "status", children: "No image model is installed yet. Download one under Settings, AI assistance." }),
205
+ ready && modelsError && /* @__PURE__ */ jsx("div", { className: "db-ai-review-empty db-ai-error", role: "alert", children: modelsError }),
206
+ ready && installed.length > 0 && /* @__PURE__ */ jsxs("section", { className: "db-ai-image-form", "aria-label": "Describe the image", children: [
207
+ /* @__PURE__ */ jsxs("label", { className: "db-ai-field", children: [
208
+ "Description",
209
+ /* @__PURE__ */ jsx(
210
+ "textarea",
211
+ {
212
+ value: prompt,
213
+ onChange: (event) => setPrompt(event.target.value),
214
+ placeholder: "A watercolor lighthouse on a cliff at dusk",
215
+ maxLength: 4e3
216
+ }
217
+ )
218
+ ] }),
219
+ /* @__PURE__ */ jsx("div", { className: "db-ai-image-aspects", role: "radiogroup", "aria-label": "Shape", children: AI_IMAGE_ASPECTS.map((shape) => /* @__PURE__ */ jsx(
220
+ "button",
221
+ {
222
+ type: "button",
223
+ role: "radio",
224
+ "aria-checked": aspect === shape,
225
+ onClick: () => chooseAspect(shape),
226
+ children: ASPECT_LABELS[shape]
227
+ },
228
+ shape
229
+ )) }),
230
+ installed.length > 1 && /* @__PURE__ */ jsxs("label", { className: "db-ai-field db-ai-field--inline", children: [
231
+ "Model",
232
+ /* @__PURE__ */ jsx(
233
+ "select",
234
+ {
235
+ value: chosen?.id ?? "",
236
+ onChange: (event) => chooseModel(event.target.value),
237
+ children: installed.map((entry) => /* @__PURE__ */ jsx("option", { value: entry.id, children: entry.label }, entry.id))
238
+ }
239
+ )
240
+ ] }),
241
+ /* @__PURE__ */ jsx("div", { className: "db-ai-illustrate-bar db-ai-image-actions", children: rendering ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => handle.current?.cancel(), children: "Stop" }) : /* @__PURE__ */ jsx(
242
+ "button",
243
+ {
244
+ type: "button",
245
+ className: "db-ai-image-generate",
246
+ disabled: !prompt.trim(),
247
+ onClick: () => render(prompt, aspect),
248
+ children: "Generate"
249
+ }
250
+ ) })
251
+ ] }),
252
+ notice && /* @__PURE__ */ jsx("div", { className: "db-ai-review-empty db-ai-error", role: "alert", children: notice }),
253
+ results.length > 0 && /* @__PURE__ */ jsx("ol", { className: "db-ai-review-list db-ai-image-results", children: results.map((result) => {
254
+ const fraction = imageProgressFraction(result.progress);
255
+ return /* @__PURE__ */ jsxs("li", { className: "db-ai-review-finding db-ai-image-result", children: [
256
+ /* @__PURE__ */ jsx("p", { className: "db-ai-image-prompt", children: result.prompt }),
257
+ result.status === "rendering" && /* @__PURE__ */ jsxs("div", { className: "db-ai-illustrate-progress", role: "status", children: [
258
+ /* @__PURE__ */ jsx("span", { className: "db-ai-spinner", "aria-hidden": "true" }),
259
+ imageProgressText(result.progress, modelLabel(result.model)),
260
+ fraction !== null && /* @__PURE__ */ jsx("progress", { max: 1, value: fraction })
261
+ ] }),
262
+ result.status === "failed" && /* @__PURE__ */ jsx("p", { className: "db-ai-error", role: "alert", children: result.error }),
263
+ result.status === "stopped" && /* @__PURE__ */ jsx("p", { className: "db-ai-note", children: "Stopped." }),
264
+ result.status === "done" && result.url && result.image && /* @__PURE__ */ jsxs(Fragment, { children: [
265
+ /* @__PURE__ */ jsx(
266
+ "img",
267
+ {
268
+ className: "db-ai-image-preview",
269
+ src: result.url,
270
+ alt: result.alt,
271
+ width: result.image.width,
272
+ height: result.image.height
273
+ }
274
+ ),
275
+ /* @__PURE__ */ jsxs("label", { className: "db-ai-field db-ai-image-alt", children: [
276
+ "Description for readers",
277
+ /* @__PURE__ */ jsx(
278
+ "input",
279
+ {
280
+ value: result.alt,
281
+ onChange: (event) => update(result.key, { alt: event.target.value }),
282
+ maxLength: 160
283
+ }
284
+ )
285
+ ] })
286
+ ] }),
287
+ /* @__PURE__ */ jsxs("div", { className: "db-ai-illustrate-bar db-ai-image-result-actions", children: [
288
+ result.status === "done" && /* @__PURE__ */ jsx(
289
+ "button",
290
+ {
291
+ type: "button",
292
+ disabled: result.inserted,
293
+ onClick: () => void insert(result),
294
+ children: result.inserted ? "Inserted" : "Insert"
295
+ }
296
+ ),
297
+ result.status !== "rendering" && /* @__PURE__ */ jsx(
298
+ "button",
299
+ {
300
+ type: "button",
301
+ disabled: rendering || !chosen,
302
+ onClick: () => render(result.prompt, result.aspect),
303
+ children: result.status === "done" ? "Regenerate" : "Try again"
304
+ }
305
+ ),
306
+ result.status !== "rendering" && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => discard(result), children: "Discard" })
307
+ ] })
308
+ ] }, result.key);
309
+ }) })
310
+ ] });
311
+ }
312
+ export {
313
+ AiImagePanel
314
+ };
@@ -30,14 +30,14 @@ import {
30
30
  extractNumbers,
31
31
  sanitizeGeneratedMarkdown,
32
32
  stripReasoning
33
- } from "./chunk-N7AKLJPY.js";
33
+ } from "./chunk-CXLXGFG2.js";
34
34
  import {
35
35
  unavailableSentence,
36
36
  useAiStatus
37
37
  } from "./chunk-K4I3NJFL.js";
38
38
  import {
39
39
  Dialog
40
- } from "./chunk-VJPGZCFS.js";
40
+ } from "./chunk-UYJCL6OW.js";
41
41
 
42
42
  // src/Ai/AiStudyDialog.tsx
43
43
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-RP3DVNTI.js";
8
8
  import {
9
9
  Dialog
10
- } from "./chunk-VJPGZCFS.js";
10
+ } from "./chunk-UYJCL6OW.js";
11
11
 
12
12
  // src/Speech/AudioExportDialog.tsx
13
13
  import { useEffect, useId, useRef, useState } from "react";
@@ -0,0 +1,217 @@
1
+ import {
2
+ GitDiffView
3
+ } from "./chunk-27E2OON2.js";
4
+ import {
5
+ Dialog
6
+ } from "./chunk-UYJCL6OW.js";
7
+
8
+ // src/DocBlocksShell/DocumentConflictDialog.tsx
9
+ import { Component, useEffect, useMemo, useState } from "react";
10
+ import { resolveFileKind } from "@bendyline/squisq-editor-react";
11
+ import { MediaContext } from "@bendyline/squisq-react";
12
+ import { LinearDocView } from "@bendyline/squisq-react/page";
13
+ import { markdownToDoc, resolveThemeForDoc } from "@bendyline/squisq/doc";
14
+ import { parseMarkdown } from "@bendyline/squisq/markdown";
15
+ import { jsx, jsxs } from "react/jsx-runtime";
16
+ var DIFF_VIEWS = [
17
+ { value: "inline", label: "Inline" },
18
+ { value: "side-by-side", label: "Side by side" },
19
+ { value: "changes", label: "Changes only" }
20
+ ];
21
+ function formattedTime(value) {
22
+ if (value === null || value === void 0) return null;
23
+ const date = new Date(value);
24
+ return Number.isFinite(date.getTime()) ? date.toLocaleString() : null;
25
+ }
26
+ function VersionText({ label, content }) {
27
+ return /* @__PURE__ */ jsx(
28
+ "textarea",
29
+ {
30
+ className: "db-conflict-preview",
31
+ "aria-label": `${label} preview`,
32
+ value: content,
33
+ readOnly: true,
34
+ spellCheck: false
35
+ }
36
+ );
37
+ }
38
+ var RenderBoundary = class extends Component {
39
+ constructor() {
40
+ super(...arguments);
41
+ this.state = { failed: false, resetKey: this.props.resetKey };
42
+ }
43
+ static getDerivedStateFromError() {
44
+ return { failed: true };
45
+ }
46
+ static getDerivedStateFromProps(props, state) {
47
+ return props.resetKey === state.resetKey ? null : { failed: false, resetKey: props.resetKey };
48
+ }
49
+ render() {
50
+ return this.state.failed ? this.props.fallback : this.props.children;
51
+ }
52
+ };
53
+ function RenderedVersion({
54
+ label,
55
+ content,
56
+ defaultThemeId
57
+ }) {
58
+ const rendered = useMemo(() => {
59
+ try {
60
+ const doc = markdownToDoc(parseMarkdown(content));
61
+ return { doc, theme: resolveThemeForDoc(doc, doc.themeId ? void 0 : defaultThemeId) };
62
+ } catch {
63
+ return null;
64
+ }
65
+ }, [content, defaultThemeId]);
66
+ const text = /* @__PURE__ */ jsx(VersionText, { label, content });
67
+ if (!rendered) return text;
68
+ return /* @__PURE__ */ jsx(RenderBoundary, { resetKey: content, fallback: text, children: /* @__PURE__ */ jsx(
69
+ "div",
70
+ {
71
+ className: "db-conflict-preview db-conflict-preview--rendered",
72
+ role: "region",
73
+ "aria-label": `${label} preview`,
74
+ tabIndex: 0,
75
+ style: { background: rendered.theme.colors.background },
76
+ children: /* @__PURE__ */ jsx(
77
+ LinearDocView,
78
+ {
79
+ doc: rendered.doc,
80
+ theme: rendered.theme,
81
+ variant: "document",
82
+ showCover: false,
83
+ thinMargins: true,
84
+ animationsEnabled: false
85
+ }
86
+ )
87
+ }
88
+ ) });
89
+ }
90
+ function DocumentConflictDialog({
91
+ conflict,
92
+ fileName,
93
+ theme,
94
+ defaultThemeId,
95
+ mediaProvider,
96
+ readSavedFile,
97
+ onUseLocal,
98
+ onUseSaved,
99
+ onClose
100
+ }) {
101
+ const [savedFile, setSavedFile] = useState(null);
102
+ const [busy, setBusy] = useState(false);
103
+ const [error, setError] = useState(null);
104
+ const [diffView, setDiffView] = useState("inline");
105
+ useEffect(() => {
106
+ let disposed = false;
107
+ void readSavedFile().then(
108
+ (file) => {
109
+ if (!disposed) setSavedFile(file);
110
+ },
111
+ () => {
112
+ if (!disposed) setSavedFile(null);
113
+ }
114
+ );
115
+ return () => {
116
+ disposed = true;
117
+ };
118
+ }, [readSavedFile, conflict.targetKey, conflict.externalContent, conflict.externalVersion]);
119
+ const capturedAt = formattedTime(conflict.recoveredDraftCapturedAt);
120
+ const savedAt = savedFile?.content === conflict.externalContent ? formattedTime(savedFile?.lastModified) : null;
121
+ const localLabel = conflict.recoveredDraft ? "Recovery draft" : "Your unsaved version";
122
+ const fileKind = resolveFileKind(fileName);
123
+ const version = (label, content) => fileKind.mode === "markdown" ? /* @__PURE__ */ jsx(RenderedVersion, { label, content, defaultThemeId }) : /* @__PURE__ */ jsx(VersionText, { label, content });
124
+ const choose = async (resolve) => {
125
+ if (busy) return;
126
+ setBusy(true);
127
+ setError(null);
128
+ try {
129
+ if (await resolve()) onClose();
130
+ else setError("Could not resolve this conflict. Both versions are still available.");
131
+ } catch (cause) {
132
+ setError(cause instanceof Error ? cause.message : "Could not resolve this conflict.");
133
+ } finally {
134
+ setBusy(false);
135
+ }
136
+ };
137
+ return /* @__PURE__ */ jsxs(
138
+ Dialog,
139
+ {
140
+ title: "Compare document versions",
141
+ size: "full",
142
+ className: "db-conflict-dialog",
143
+ onClose: () => {
144
+ if (!busy) onClose();
145
+ },
146
+ closeOnBackdrop: !busy,
147
+ footer: /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, onClick: onClose, children: "Decide later" }),
148
+ children: [
149
+ /* @__PURE__ */ jsxs("p", { children: [
150
+ conflict.recoveredDraft ? "This draft was recovered from a previous session. The saved file may contain newer edits." : "The saved file changed while you had unsaved edits.",
151
+ " ",
152
+ "Compare the two versions before choosing. Neither version changes until you choose."
153
+ ] }),
154
+ /* @__PURE__ */ jsx(MediaContext.Provider, { value: mediaProvider ?? null, children: /* @__PURE__ */ jsxs(
155
+ "div",
156
+ {
157
+ className: diffView === "side-by-side" ? "db-conflict-columns db-conflict-columns--wide-diff" : "db-conflict-columns",
158
+ "aria-busy": busy,
159
+ children: [
160
+ /* @__PURE__ */ jsxs("section", { className: "db-conflict-version", "aria-label": localLabel, children: [
161
+ /* @__PURE__ */ jsx("h3", { children: localLabel }),
162
+ /* @__PURE__ */ jsx("p", { className: "db-conflict-metadata", children: conflict.recoveredDraft ? capturedAt ? `Recovery copy captured: ${capturedAt}` : "Recovery capture time unavailable" : "Not saved in this session" }),
163
+ version(localLabel, conflict.localContent),
164
+ /* @__PURE__ */ jsx("p", { className: "db-conflict-consequence", children: "Replaces the saved file with the draft shown above." }),
165
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, onClick: () => void choose(onUseLocal), children: conflict.recoveredDraft ? "Replace file with draft" : "Keep mine" })
166
+ ] }),
167
+ /* @__PURE__ */ jsxs("section", { className: "db-conflict-diff", "aria-label": "Differences", children: [
168
+ /* @__PURE__ */ jsxs("div", { className: "db-conflict-diff-header", children: [
169
+ /* @__PURE__ */ jsx("h3", { children: "Differences" }),
170
+ /* @__PURE__ */ jsxs("label", { className: "db-conflict-diff-view", children: [
171
+ /* @__PURE__ */ jsx("span", { children: "View" }),
172
+ /* @__PURE__ */ jsx(
173
+ "select",
174
+ {
175
+ value: diffView,
176
+ onChange: (event) => setDiffView(event.currentTarget.value),
177
+ children: DIFF_VIEWS.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
178
+ }
179
+ )
180
+ ] })
181
+ ] }),
182
+ /* @__PURE__ */ jsxs("p", { className: "db-conflict-metadata", children: [
183
+ "\u2212 Only in ",
184
+ localLabel.toLowerCase(),
185
+ " \xB7 + Only in the saved file"
186
+ ] }),
187
+ /* @__PURE__ */ jsx(
188
+ GitDiffView,
189
+ {
190
+ original: conflict.localContent,
191
+ modified: conflict.externalContent ?? "",
192
+ fileName,
193
+ language: fileKind.language,
194
+ theme,
195
+ layout: diffView === "side-by-side" ? "side-by-side" : "inline",
196
+ changesOnly: diffView === "changes"
197
+ }
198
+ )
199
+ ] }),
200
+ /* @__PURE__ */ jsxs("section", { className: "db-conflict-version", "aria-label": "Saved file", children: [
201
+ /* @__PURE__ */ jsx("h3", { children: "Saved file" }),
202
+ /* @__PURE__ */ jsx("p", { className: "db-conflict-metadata", children: conflict.externalContent === null ? "File was deleted outside DocBlocks" : savedAt ? `File last modified: ${savedAt}` : "File modification time unavailable" }),
203
+ conflict.externalContent === null ? /* @__PURE__ */ jsx("div", { className: "db-conflict-preview", children: "The saved file no longer exists." }) : version("Saved file", conflict.externalContent),
204
+ /* @__PURE__ */ jsx("p", { className: "db-conflict-consequence", children: conflict.externalContent === null ? "Discards the draft and closes this document." : "Discards the draft and opens the saved file shown above." }),
205
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, onClick: () => void choose(onUseSaved), children: conflict.externalContent === null ? "Accept deletion" : "Use saved file" })
206
+ ] })
207
+ ]
208
+ }
209
+ ) }),
210
+ error && /* @__PURE__ */ jsx("p", { role: "alert", children: error })
211
+ ]
212
+ }
213
+ );
214
+ }
215
+ export {
216
+ DocumentConflictDialog as default
217
+ };
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  ExportDialog
3
- } from "./chunk-G63YXKBC.js";
3
+ } from "./chunk-WDNE5TI3.js";
4
4
  import "./chunk-H3BK5MK6.js";
5
5
  import "./chunk-6MSHXQNB.js";
6
- import "./chunk-VJPGZCFS.js";
6
+ import "./chunk-UYJCL6OW.js";
7
7
  export {
8
8
  ExportDialog
9
9
  };