@hachej/boring-workspace 0.1.34 → 0.1.35

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.
@@ -2,7 +2,7 @@ import { jsx as c, jsxs as z } from "react/jsx-runtime";
2
2
  import { useRef as D, useEffect as A, useMemo as M, useCallback as w, createContext as S, useContext as $ } from "react";
3
3
  import { Tree as E } from "react-arborist";
4
4
  import { FolderOpenIcon as J, FolderIcon as K, ChevronRightIcon as U, Loader2Icon as V } from "lucide-react";
5
- import { L as X } from "./WorkspaceProvider-hCE2wXKZ.js";
5
+ import { L as X } from "./WorkspaceProvider-BhRPFy5R.js";
6
6
  import { EmptyState as Y, Input as Z } from "@hachej/boring-ui-kit";
7
7
  import { c as N } from "./utils-B6yFEsav.js";
8
8
  import { createDragDropManager as y } from "dnd-core";
@@ -0,0 +1,549 @@
1
+ import { jsxs as H, jsx as e } from "react/jsx-runtime";
2
+ import { useState as F, useCallback as Z, useRef as A, useEffect as D, useMemo as z } from "react";
3
+ import { ReactNodeViewRenderer as G, NodeViewWrapper as J, useEditor as Y, EditorContent as tt, useEditorState as et } from "@tiptap/react";
4
+ import rt from "@tiptap/starter-kit";
5
+ import nt from "@tiptap/extension-underline";
6
+ import it from "@tiptap/extension-link";
7
+ import _ from "@tiptap/extension-placeholder";
8
+ import at from "@tiptap/extension-task-list";
9
+ import ot from "@tiptap/extension-task-item";
10
+ import st from "@tiptap/extension-text-align";
11
+ import lt from "@tiptap/extension-highlight";
12
+ import { Table as ct } from "@tiptap/extension-table";
13
+ import { TableRow as ut } from "@tiptap/extension-table-row";
14
+ import { TableHeader as gt } from "@tiptap/extension-table-header";
15
+ import { TableCell as dt } from "@tiptap/extension-table-cell";
16
+ import ft from "@tiptap/extension-image";
17
+ import { c as S } from "./utils-B6yFEsav.js";
18
+ import { _ as O, aq as V } from "./WorkspaceProvider-BhRPFy5R.js";
19
+ import { uploadFile as mt } from "@hachej/boring-agent/front";
20
+ import pt from "@tiptap/extension-code-block-lowlight";
21
+ import { createLowlight as ht, common as wt } from "lowlight";
22
+ import { Markdown as kt } from "@tiptap/markdown";
23
+ import { BoldIcon as bt, ItalicIcon as vt, UnderlineIcon as It, StrikethroughIcon as xt, Heading1Icon as Lt, Heading2Icon as yt, Heading3Icon as At, ListIcon as Nt, ListOrderedIcon as Ct, ListChecksIcon as Tt, QuoteIcon as Mt, CodeIcon as Rt, LinkIcon as $t, ImageIcon as Ut, HighlighterIcon as Ht, AlignLeftIcon as St, AlignCenterIcon as Bt, AlignRightIcon as Et, MinusIcon as Dt, Loader2Icon as Ft } from "lucide-react";
24
+ import { Toolbar as zt, Input as Wt, ToolbarButton as Pt, ToolbarSeparator as qt } from "@hachej/boring-ui-kit";
25
+ function jt(t) {
26
+ const r = O(), n = V(), [a, l] = F(0);
27
+ return { upload: Z(
28
+ async (u, o) => {
29
+ l((f) => f + 1);
30
+ try {
31
+ return await mt(u, {
32
+ apiBaseUrl: r,
33
+ workspaceRequestId: n,
34
+ directory: (o == null ? void 0 : o.directory) ?? (t == null ? void 0 : t.directory),
35
+ sourcePath: o == null ? void 0 : o.sourcePath
36
+ });
37
+ } finally {
38
+ l((f) => f - 1);
39
+ }
40
+ },
41
+ [r, n, t == null ? void 0 : t.directory]
42
+ ), uploading: a > 0 };
43
+ }
44
+ const W = 64, P = 2e3;
45
+ function _t(t) {
46
+ return t.replace(/\\/g, "\\\\").replace(/]/g, "\\]");
47
+ }
48
+ function E(t) {
49
+ return t.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
50
+ }
51
+ function q(t) {
52
+ if (typeof t == "number" && Number.isFinite(t) && t > 0) return t;
53
+ if (typeof t == "string" && t.trim()) {
54
+ const r = Number(t);
55
+ if (Number.isFinite(r) && r > 0) return r;
56
+ }
57
+ return null;
58
+ }
59
+ const X = ft.extend({
60
+ name: "image",
61
+ draggable: !0,
62
+ addOptions() {
63
+ var t;
64
+ return {
65
+ ...(t = this.parent) == null ? void 0 : t.call(this),
66
+ resolveSrc: (r) => r
67
+ };
68
+ },
69
+ parseMarkdown: (t, r) => r.createNode("image", {
70
+ src: t.href,
71
+ title: t.title,
72
+ alt: t.text
73
+ }),
74
+ renderMarkdown: (t) => {
75
+ var y, v, L, i, c, w, I;
76
+ const r = typeof ((y = t.attrs) == null ? void 0 : y.src) == "string" ? t.attrs.src : "", n = typeof ((v = t.attrs) == null ? void 0 : v.alt) == "string" ? t.attrs.alt : "", a = typeof ((L = t.attrs) == null ? void 0 : L.title) == "string" ? t.attrs.title : "", l = q((i = t.attrs) == null ? void 0 : i.width), s = q((c = t.attrs) == null ? void 0 : c.height), u = ((w = t.attrs) == null ? void 0 : w.align) === "center" || ((I = t.attrs) == null ? void 0 : I.align) === "right" ? t.attrs.align : "left";
77
+ if (!l && !s && u === "left") {
78
+ const k = _t(n);
79
+ return a ? `![${k}](${r} "${a.replace(/"/g, '\\"')}")` : `![${k}](${r})`;
80
+ }
81
+ const f = `<img ${[
82
+ `src="${E(r)}"`,
83
+ n ? `alt="${E(n)}"` : null,
84
+ a ? `title="${E(a)}"` : null,
85
+ l ? `width="${l}"` : null,
86
+ s ? `height="${s}"` : null
87
+ ].filter(Boolean).join(" ")} />`;
88
+ return u === "center" || u === "right" ? `<p align="${u}">${f}</p>` : f;
89
+ },
90
+ addAttributes() {
91
+ var r;
92
+ return {
93
+ ...((r = this.parent) == null ? void 0 : r.call(this)) ?? {},
94
+ width: {
95
+ default: null,
96
+ parseHTML: (n) => {
97
+ const a = n.getAttribute("width");
98
+ if (a) {
99
+ const s = parseInt(a, 10);
100
+ return Number.isFinite(s) ? s : null;
101
+ }
102
+ const l = n.style.width;
103
+ if (l && l.endsWith("px")) {
104
+ const s = parseInt(l, 10);
105
+ return Number.isFinite(s) ? s : null;
106
+ }
107
+ return null;
108
+ },
109
+ renderHTML: (n) => n.width ? { width: String(n.width) } : {}
110
+ },
111
+ align: {
112
+ default: "left",
113
+ parseHTML: (n) => {
114
+ const a = n.getAttribute("data-align");
115
+ return a === "left" || a === "center" || a === "right" ? a : n.style.marginLeft === "auto" && n.style.marginRight === "auto" ? "center" : n.style.marginLeft === "auto" ? "right" : "left";
116
+ },
117
+ renderHTML: (n) => ({ "data-align": n.align ?? "left" })
118
+ },
119
+ // Transient marker used by MarkdownEditor.insertImageRef to find THIS
120
+ // specific inserted image when its background upload completes, even
121
+ // if the user pasted the same file twice (which produces two image
122
+ // nodes with identical `src` data URLs). Never serialized to HTML or
123
+ // markdown — purely in-memory state for the swap.
124
+ pendingUploadId: {
125
+ default: null,
126
+ parseHTML: () => null,
127
+ renderHTML: () => ({})
128
+ }
129
+ };
130
+ },
131
+ addNodeView() {
132
+ return G(Ot);
133
+ }
134
+ });
135
+ function Ot({ node: t, updateAttributes: r, selected: n, extension: a }) {
136
+ const l = A(null), s = A(null), [u, o] = F(!1), f = t.attrs.src ?? "", v = (a.options.resolveSrc ?? ((k) => k))(f), L = t.attrs.alt ?? "", i = t.attrs.title ?? void 0, c = t.attrs.width, w = t.attrs.align ?? "left";
137
+ D(() => {
138
+ if (!u) return;
139
+ const k = (N) => {
140
+ const x = s.current;
141
+ if (!x) return;
142
+ const m = N.clientX - x.x, g = Math.max(W, Math.min(P, x.width + m));
143
+ r({ width: Math.round(g) });
144
+ }, b = () => {
145
+ s.current = null, o(!1);
146
+ };
147
+ return window.addEventListener("pointermove", k), window.addEventListener("pointerup", b), window.addEventListener("pointercancel", b), () => {
148
+ window.removeEventListener("pointermove", k), window.removeEventListener("pointerup", b), window.removeEventListener("pointercancel", b);
149
+ };
150
+ }, [u, r]);
151
+ const I = (k) => {
152
+ var x;
153
+ k.preventDefault(), k.stopPropagation();
154
+ const b = (x = l.current) == null ? void 0 : x.querySelector("img"), N = c ?? (b == null ? void 0 : b.getBoundingClientRect().width) ?? 320;
155
+ s.current = { x: k.clientX, width: N }, o(!0);
156
+ };
157
+ return /* @__PURE__ */ H(
158
+ J,
159
+ {
160
+ ref: l,
161
+ "data-resizable-image": "",
162
+ "data-selected": n ? "true" : void 0,
163
+ className: S(
164
+ "relative block max-w-full align-baseline",
165
+ "[&>img]:block [&>img]:max-w-full [&>img]:h-auto [&>img]:rounded-md",
166
+ w === "left" && "mr-auto",
167
+ w === "center" && "mx-auto",
168
+ w === "right" && "ml-auto",
169
+ n && "ring-2 ring-[color:var(--accent)] ring-offset-1 ring-offset-background"
170
+ ),
171
+ style: { width: c ? `${c}px` : void 0 },
172
+ children: [
173
+ /* @__PURE__ */ e("img", { src: v, alt: L, title: i, draggable: !1 }),
174
+ /* @__PURE__ */ e(
175
+ "span",
176
+ {
177
+ role: "slider",
178
+ "aria-label": "Resize image",
179
+ "aria-valuenow": c ?? 0,
180
+ "aria-valuemin": W,
181
+ "aria-valuemax": P,
182
+ "data-testid": "resize-handle",
183
+ onPointerDown: I,
184
+ className: S(
185
+ "absolute right-0 bottom-0 h-3 w-3 translate-x-1/2 translate-y-1/2 cursor-nwse-resize",
186
+ "rounded-full border border-background bg-[color:var(--accent)] shadow",
187
+ "opacity-0 transition-opacity duration-150 ease-out",
188
+ (n || u) && "opacity-100"
189
+ )
190
+ }
191
+ )
192
+ ]
193
+ }
194
+ );
195
+ }
196
+ const Vt = ht(wt), Xt = [
197
+ /<script[\s>][\s\S]*?<\/script>/gi,
198
+ /<iframe[\s>][\s\S]*?<\/iframe>/gi,
199
+ /\s+on\w+\s*=\s*["'][^"']*["']/gi,
200
+ /\s+on\w+\s*=\s*\S+/gi,
201
+ /href\s*=\s*["']?\s*javascript:[^"'\s>]*/gi,
202
+ /src\s*=\s*["']?\s*javascript:[^"'\s>]*/gi
203
+ ];
204
+ function Kt(t) {
205
+ let r = t;
206
+ for (const n of Xt)
207
+ r = r.replace(n, "");
208
+ return r;
209
+ }
210
+ function Qt(t) {
211
+ const n = t.replace(/```[\s\S]*?```/g, " ").replace(/`[^`]*`/g, " ").replace(/^\s{0,3}#{1,6}\s+/gm, "").replace(/!\[[^\]]*\]\([^)]*\)/g, " ").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").replace(/<[^>]+>/g, " ").replace(/^\s{0,3}[-*_]{3,}\s*$/gm, " ").replace(/^\s{0,3}(?:[-*+]\s+|\d+[.)]\s+|>\s?)/gm, "").replace(/^\s{0,3}\|?(?:\s*:?-+:?\s*\|)+\s*$/gm, " ").replace(/\|/g, " ").replace(/[*_~>#]/g, " ").match(/\b[\p{L}\p{N}']+\b/gu);
212
+ return (n == null ? void 0 : n.length) ?? 0;
213
+ }
214
+ function Zt(t) {
215
+ return `${t} word${t === 1 ? "" : "s"}`;
216
+ }
217
+ function Gt(t) {
218
+ return /^(?:[a-z][a-z0-9+.-]*:|\/|#)/i.test(t);
219
+ }
220
+ function Jt(t, r) {
221
+ return !(t === "" && !r);
222
+ }
223
+ function Yt(t, r) {
224
+ const n = t.match(/^([^?#]*)([?#].*)?$/), a = (n == null ? void 0 : n[1]) ?? t, l = (n == null ? void 0 : n[2]) ?? "", s = r != null && r.includes("/") ? r.slice(0, r.lastIndexOf("/")) : "", u = `${s ? `${s}/` : ""}${a}`.split("/"), o = [];
225
+ for (const f of u)
226
+ !f || f === "." || (f === ".." ? o.pop() : o.push(f));
227
+ return `${o.join("/")}${l}`;
228
+ }
229
+ function te(t, r, n, a) {
230
+ if (!t || Gt(t)) return t;
231
+ const l = Yt(t, r), s = n.replace(/\/$/, ""), u = new URLSearchParams({ path: l });
232
+ return a && u.set("workspaceId", a), `${s}/api/v1/files/raw?${u.toString()}`;
233
+ }
234
+ const ee = [
235
+ rt.configure({
236
+ codeBlock: !1,
237
+ link: !1,
238
+ underline: !1
239
+ }),
240
+ nt,
241
+ it.configure({
242
+ openOnClick: !0,
243
+ autolink: !0,
244
+ linkOnPaste: !0,
245
+ defaultProtocol: "https",
246
+ HTMLAttributes: { rel: "noopener noreferrer nofollow", target: "_blank" }
247
+ }),
248
+ _.configure({
249
+ placeholder: "Start writing..."
250
+ }),
251
+ at,
252
+ ot.configure({ nested: !0 }),
253
+ st.configure({
254
+ types: ["heading", "paragraph"]
255
+ }),
256
+ lt,
257
+ ct.configure({
258
+ resizable: !0
259
+ }),
260
+ ut,
261
+ gt,
262
+ dt,
263
+ X.configure({
264
+ inline: !1,
265
+ allowBase64: !0
266
+ }),
267
+ pt.configure({ lowlight: Vt }),
268
+ kt.configure({
269
+ markedOptions: {
270
+ gfm: !0,
271
+ breaks: !1,
272
+ pedantic: !1
273
+ }
274
+ })
275
+ ];
276
+ function d({ onClick: t, active: r, disabled: n, title: a, children: l }) {
277
+ return /* @__PURE__ */ e(
278
+ Pt,
279
+ {
280
+ type: "button",
281
+ size: "icon-xs",
282
+ onClick: t,
283
+ disabled: n,
284
+ title: a,
285
+ "aria-pressed": r,
286
+ className: S(
287
+ "text-muted-foreground/70",
288
+ r && "bg-[color:var(--accent-soft)] text-[color:var(--accent)]"
289
+ ),
290
+ children: l
291
+ }
292
+ );
293
+ }
294
+ function T() {
295
+ return /* @__PURE__ */ e(qt, { className: "mx-2 h-3.5" });
296
+ }
297
+ function j(t) {
298
+ const r = t.trim().toLowerCase();
299
+ return !r.startsWith("javascript:") && !r.startsWith("data:text/html");
300
+ }
301
+ function re(t) {
302
+ return new Promise((r, n) => {
303
+ const a = new FileReader();
304
+ a.onload = () => r(a.result), a.onerror = () => n(a.error ?? new Error("Read failed")), a.readAsDataURL(t);
305
+ });
306
+ }
307
+ function ne(t) {
308
+ if (!t) return null;
309
+ for (const r of Array.from(t.files ?? []))
310
+ if (r.type.startsWith("image/")) return r;
311
+ for (const r of Array.from(t.items ?? [])) {
312
+ if (r.kind !== "file" || !r.type.startsWith("image/")) continue;
313
+ const n = r.getAsFile();
314
+ if (n) return n;
315
+ }
316
+ return null;
317
+ }
318
+ function ie({
319
+ editor: t,
320
+ onInsertImage: r,
321
+ rawMode: n,
322
+ onToggleRawMode: a,
323
+ uploading: l
324
+ }) {
325
+ const s = (i) => {
326
+ if (t) {
327
+ if (t.isActive("image")) {
328
+ t.chain().focus().updateAttributes("image", { align: i }).run();
329
+ return;
330
+ }
331
+ t.chain().focus().setTextAlign(i).run();
332
+ }
333
+ }, u = A(null), o = et({
334
+ editor: t,
335
+ selector: ({ editor: i }) => i ? {
336
+ bold: i.isActive("bold"),
337
+ italic: i.isActive("italic"),
338
+ underline: i.isActive("underline"),
339
+ strike: i.isActive("strike"),
340
+ h1: i.isActive("heading", { level: 1 }),
341
+ h2: i.isActive("heading", { level: 2 }),
342
+ h3: i.isActive("heading", { level: 3 }),
343
+ bulletList: i.isActive("bulletList"),
344
+ orderedList: i.isActive("orderedList"),
345
+ taskList: i.isActive("taskList"),
346
+ blockquote: i.isActive("blockquote"),
347
+ codeBlock: i.isActive("codeBlock"),
348
+ link: i.isActive("link"),
349
+ highlight: i.isActive("highlight"),
350
+ alignLeft: i.isActive("image") ? (i.getAttributes("image").align ?? "left") === "left" : i.isActive({ textAlign: "left" }),
351
+ alignCenter: i.isActive("image") ? i.getAttributes("image").align === "center" : i.isActive({ textAlign: "center" }),
352
+ alignRight: i.isActive("image") ? i.getAttributes("image").align === "right" : i.isActive({ textAlign: "right" })
353
+ } : null
354
+ });
355
+ if (!t || !o) return null;
356
+ const f = () => {
357
+ const i = window.prompt("URL:");
358
+ i && j(i) && t.chain().focus().extendMarkRange("link").setLink({ href: i }).run();
359
+ }, y = () => {
360
+ const i = window.prompt("Image URL:");
361
+ i && j(i) && t.chain().focus().setImage({ src: i }).run();
362
+ }, v = (i) => {
363
+ var c;
364
+ if (i != null && i.shiftKey) {
365
+ y();
366
+ return;
367
+ }
368
+ (c = u.current) == null || c.click();
369
+ }, L = async (i) => {
370
+ var w;
371
+ const c = (w = i.target.files) == null ? void 0 : w[0];
372
+ i.target.value = "", !(!c || !c.type.startsWith("image/")) && await r(c);
373
+ };
374
+ return /* @__PURE__ */ H(zt, { className: "border-b border-border/60 bg-background px-3 py-1.5", "aria-label": "Formatting toolbar", children: [
375
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleBold().run(), active: o.bold, title: "Bold", children: /* @__PURE__ */ e(bt, { className: "h-4 w-4" }) }),
376
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleItalic().run(), active: o.italic, title: "Italic", children: /* @__PURE__ */ e(vt, { className: "h-4 w-4" }) }),
377
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleUnderline().run(), active: o.underline, title: "Underline", children: /* @__PURE__ */ e(It, { className: "h-4 w-4" }) }),
378
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleStrike().run(), active: o.strike, title: "Strikethrough", children: /* @__PURE__ */ e(xt, { className: "h-4 w-4" }) }),
379
+ /* @__PURE__ */ e(T, {}),
380
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleHeading({ level: 1 }).run(), active: o.h1, title: "Heading 1", children: /* @__PURE__ */ e(Lt, { className: "h-4 w-4" }) }),
381
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleHeading({ level: 2 }).run(), active: o.h2, title: "Heading 2", children: /* @__PURE__ */ e(yt, { className: "h-4 w-4" }) }),
382
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleHeading({ level: 3 }).run(), active: o.h3, title: "Heading 3", children: /* @__PURE__ */ e(At, { className: "h-4 w-4" }) }),
383
+ /* @__PURE__ */ e(T, {}),
384
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleBulletList().run(), active: o.bulletList, title: "Bullet list", children: /* @__PURE__ */ e(Nt, { className: "h-4 w-4" }) }),
385
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleOrderedList().run(), active: o.orderedList, title: "Ordered list", children: /* @__PURE__ */ e(Ct, { className: "h-4 w-4" }) }),
386
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleTaskList().run(), active: o.taskList, title: "Task list", children: /* @__PURE__ */ e(Tt, { className: "h-4 w-4" }) }),
387
+ /* @__PURE__ */ e(T, {}),
388
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleBlockquote().run(), active: o.blockquote, title: "Quote", children: /* @__PURE__ */ e(Mt, { className: "h-4 w-4" }) }),
389
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleCodeBlock().run(), active: o.codeBlock, title: "Code block", children: /* @__PURE__ */ e(Rt, { className: "h-4 w-4" }) }),
390
+ /* @__PURE__ */ e(d, { onClick: f, active: o.link, title: "Link", children: /* @__PURE__ */ e($t, { className: "h-4 w-4" }) }),
391
+ /* @__PURE__ */ e(
392
+ d,
393
+ {
394
+ onClick: v,
395
+ title: "Image (click to upload, Shift+click for URL)",
396
+ children: /* @__PURE__ */ e(Ut, { className: "h-4 w-4" })
397
+ }
398
+ ),
399
+ /* @__PURE__ */ e(
400
+ Wt,
401
+ {
402
+ ref: u,
403
+ "data-testid": "image-file-input",
404
+ type: "file",
405
+ accept: "image/*",
406
+ className: "hidden",
407
+ onChange: L
408
+ }
409
+ ),
410
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().toggleHighlight().run(), active: o.highlight, title: "Highlight", children: /* @__PURE__ */ e(Ht, { className: "h-4 w-4" }) }),
411
+ /* @__PURE__ */ e(T, {}),
412
+ /* @__PURE__ */ e(d, { onClick: () => s("left"), active: o.alignLeft, title: "Align left", children: /* @__PURE__ */ e(St, { className: "h-4 w-4" }) }),
413
+ /* @__PURE__ */ e(d, { onClick: () => s("center"), active: o.alignCenter, title: "Center align", children: /* @__PURE__ */ e(Bt, { className: "h-4 w-4" }) }),
414
+ /* @__PURE__ */ e(d, { onClick: () => s("right"), active: o.alignRight, title: "Align right", children: /* @__PURE__ */ e(Et, { className: "h-4 w-4" }) }),
415
+ /* @__PURE__ */ e(T, {}),
416
+ /* @__PURE__ */ e(d, { onClick: () => t.chain().focus().setHorizontalRule().run(), title: "Horizontal rule", children: /* @__PURE__ */ e(Dt, { className: "h-4 w-4" }) }),
417
+ /* @__PURE__ */ e(T, {}),
418
+ l && /* @__PURE__ */ H(
419
+ "span",
420
+ {
421
+ role: "status",
422
+ "aria-live": "polite",
423
+ className: "ml-auto flex items-center gap-1.5 text-[11px] text-muted-foreground/80",
424
+ children: [
425
+ /* @__PURE__ */ e(Ft, { className: "h-3 w-3 animate-spin", "aria-hidden": !0 }),
426
+ "Uploading…"
427
+ ]
428
+ }
429
+ ),
430
+ /* @__PURE__ */ e(
431
+ d,
432
+ {
433
+ onClick: a,
434
+ active: n,
435
+ title: n ? "Rich text" : "Raw markdown",
436
+ children: /* @__PURE__ */ e("span", { className: "font-mono text-[10px] font-semibold leading-none tracking-[-0.02em]", children: "MD" })
437
+ }
438
+ )
439
+ ] });
440
+ }
441
+ function Me({
442
+ content: t,
443
+ onChange: r,
444
+ readOnly: n = !1,
445
+ placeholder: a,
446
+ className: l,
447
+ documentPath: s
448
+ }) {
449
+ const u = O(), o = V(), { upload: f, uploading: y } = jt(), [v, L] = F(!1), i = z(() => {
450
+ const g = X.configure({
451
+ inline: !1,
452
+ allowBase64: !0,
453
+ resolveSrc: (h) => te(h, s, u, o)
454
+ }), p = ee.map((h) => h.name === "image" ? g : h);
455
+ return a ? [
456
+ ...p.filter((h) => h.name !== "placeholder"),
457
+ _.configure({ placeholder: a })
458
+ ] : p;
459
+ }, [u, s, a, o]), c = A(r);
460
+ c.current = r;
461
+ const w = A(!1), I = A(null), k = z(() => Qt(t), [t]), b = A(async () => {
462
+ });
463
+ b.current = async (g) => {
464
+ const p = I.current;
465
+ if (!p) return;
466
+ let h;
467
+ try {
468
+ h = await re(g);
469
+ } catch {
470
+ return;
471
+ }
472
+ const C = typeof crypto < "u" && typeof crypto.randomUUID == "function" ? crypto.randomUUID() : `pending-${Date.now()}-${Math.random().toString(36).slice(2)}`, K = { src: h, alt: g.name, pendingUploadId: C };
473
+ p.chain().focus().setImage(K).run();
474
+ try {
475
+ const { url: M } = await f(g, { sourcePath: s }), R = I.current;
476
+ if (!R || R.isDestroyed) return;
477
+ R.commands.command(({ tr: B, state: $ }) => ($.doc.descendants((U, Q) => (U.type.name === "image" && U.attrs.pendingUploadId === C && B.setNodeMarkup(Q, void 0, { ...U.attrs, src: M, pendingUploadId: null }), !0)), !0));
478
+ } catch {
479
+ const M = I.current;
480
+ if (!M || M.isDestroyed) return;
481
+ M.commands.command(({ tr: R, state: B }) => (B.doc.descendants(($, U) => ($.type.name === "image" && $.attrs.pendingUploadId === C && R.setNodeMarkup(U, void 0, { ...$.attrs, pendingUploadId: null }), !0)), !0));
482
+ }
483
+ };
484
+ const N = t || { type: "doc", content: [{ type: "paragraph" }] }, x = t ? "markdown" : "json", m = Y({
485
+ extensions: i,
486
+ content: N,
487
+ contentType: x,
488
+ editable: !n,
489
+ editorProps: {
490
+ attributes: {
491
+ class: "tiptap-prose max-w-[68ch] px-8 py-6 focus:outline-none min-h-[200px]"
492
+ },
493
+ transformPastedHTML: Kt,
494
+ handlePaste: (g, p) => {
495
+ if (n) return !1;
496
+ const h = ne(p.clipboardData);
497
+ return h ? (p.preventDefault(), b.current(h), !0) : !1;
498
+ }
499
+ },
500
+ onUpdate: ({ editor: g }) => {
501
+ var h, C;
502
+ if (w.current) return;
503
+ const p = ((h = g.getMarkdown) == null ? void 0 : h.call(g)) ?? g.getHTML();
504
+ Jt(p, g.isFocused) && ((C = c.current) == null || C.call(c, p));
505
+ }
506
+ });
507
+ return I.current = m, D(() => {
508
+ !m || m.isDestroyed || m.setEditable(!n);
509
+ }, [m, n]), D(() => {
510
+ var p;
511
+ !m || m.isDestroyed || (((p = m.getMarkdown) == null ? void 0 : p.call(m)) ?? m.getHTML()) === t || (w.current = !0, m.commands.setContent(N, { contentType: x }), w.current = !1);
512
+ }, [m, t]), /* @__PURE__ */ H("div", { className: S("flex h-full min-h-0 flex-col overflow-hidden", l), children: [
513
+ !n && /* @__PURE__ */ e(
514
+ ie,
515
+ {
516
+ editor: m,
517
+ onInsertImage: (g) => b.current(g),
518
+ rawMode: v,
519
+ onToggleRawMode: () => L((g) => !g),
520
+ uploading: y
521
+ }
522
+ ),
523
+ /* @__PURE__ */ e("div", { className: "min-h-0 flex-1 overflow-auto", children: v && !n ? /* @__PURE__ */ e(
524
+ "textarea",
525
+ {
526
+ "aria-label": "Raw markdown",
527
+ "data-testid": "markdown-raw-editor",
528
+ className: "h-full min-h-[200px] w-full resize-none bg-background px-8 py-6 font-mono text-[13px] leading-6 text-foreground outline-none placeholder:text-muted-foreground/70",
529
+ value: t,
530
+ placeholder: a,
531
+ spellCheck: !1,
532
+ onChange: (g) => {
533
+ var p;
534
+ return (p = c.current) == null ? void 0 : p.call(c, g.target.value);
535
+ }
536
+ }
537
+ ) : /* @__PURE__ */ e(tt, { editor: m }) }),
538
+ /* @__PURE__ */ e("div", { className: "border-t border-border/60 px-4 py-2 text-right text-xs text-muted-foreground", "data-testid": "markdown-word-count", children: Zt(k) })
539
+ ] });
540
+ }
541
+ export {
542
+ Me as MarkdownEditor,
543
+ Qt as countMarkdownWords,
544
+ j as isSafeUrl,
545
+ Jt as isUserEditedChange,
546
+ te as rawFileUrlForMarkdownImage,
547
+ re as readFileAsDataUrl,
548
+ Kt as sanitizeHtml
549
+ };