@hachej/boring-workspace 0.1.39 → 0.1.41

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