@hachej/boring-workspace 0.1.4 → 0.1.6

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.
@@ -2095,7 +2095,7 @@ function Qs(e) {
2095
2095
  }
2096
2096
  return t;
2097
2097
  }
2098
- const Qn = () => import("./FileTree-DRq_bfue.js").then((e) => ({ default: e.FileTree }));
2098
+ const Qn = () => import("./FileTree-Bgg50HQf.js").then((e) => ({ default: e.FileTree }));
2099
2099
  function Xs() {
2100
2100
  Qn();
2101
2101
  }
@@ -2945,7 +2945,7 @@ function Xt({ params: e, api: t, className: n }) {
2945
2945
  );
2946
2946
  }
2947
2947
  const yi = Ge(
2948
- () => import("./MarkdownEditor-DjiHxnRv.js").then((e) => ({ default: e.MarkdownEditor }))
2948
+ () => import("./MarkdownEditor-BA37oZKg.js").then((e) => ({ default: e.MarkdownEditor }))
2949
2949
  );
2950
2950
  function vi({ params: e, api: t, className: n }) {
2951
2951
  const r = typeof (e == null ? void 0 : e.path) == "string" ? e.path : "", {
@@ -2970,7 +2970,7 @@ function vi({ params: e, api: t, className: n }) {
2970
2970
  onReload: d,
2971
2971
  onOverwrite: i,
2972
2972
  editorComponent: yi,
2973
- editorProps: { className: n }
2973
+ editorProps: { className: n, documentPath: r }
2974
2974
  }
2975
2975
  );
2976
2976
  }
@@ -5201,6 +5201,8 @@ export {
5201
5201
  $n as ak,
5202
5202
  Ka as al,
5203
5203
  pe as am,
5204
+ Ye as an,
5205
+ Je as ao,
5204
5206
  Ja as b,
5205
5207
  Xt as c,
5206
5208
  go as d,
@@ -2,7 +2,7 @@ import { jsx as i, jsxs as C } from "react/jsx-runtime";
2
2
  import { useRef as w, useEffect as R, useMemo as P, useCallback as x, createContext as H, useContext as L } from "react";
3
3
  import { Tree as q } from "react-arborist";
4
4
  import { FolderOpenIcon as z, FolderIcon as B, ChevronRightIcon as K, Loader2Icon as E } from "lucide-react";
5
- import { K as Y } from "./CommandPalette-aM61U-b0.js";
5
+ import { K as Y } from "./CommandPalette-CdiVSPYz.js";
6
6
  import { Input as G } from "@hachej/boring-ui-kit";
7
7
  import { c as v } from "./utils-B6yFEsav.js";
8
8
  const T = /* @__PURE__ */ new Set(), A = H({
@@ -0,0 +1,458 @@
1
+ import { jsxs as T, jsx as i } from "react/jsx-runtime";
2
+ import { useRef as v, useState as B, useEffect as L } from "react";
3
+ import { ReactNodeViewRenderer as $, NodeViewWrapper as E, useEditor as z, EditorContent as P, useEditorState as D } from "@tiptap/react";
4
+ import q from "@tiptap/starter-kit";
5
+ import j from "@tiptap/extension-underline";
6
+ import F from "@tiptap/extension-link";
7
+ import S from "@tiptap/extension-placeholder";
8
+ import W from "@tiptap/extension-task-list";
9
+ import O from "@tiptap/extension-task-item";
10
+ import V from "@tiptap/extension-text-align";
11
+ import _ from "@tiptap/extension-highlight";
12
+ import { Table as X } from "@tiptap/extension-table";
13
+ import { TableRow as K } from "@tiptap/extension-table-row";
14
+ import { TableHeader as Q } from "@tiptap/extension-table-header";
15
+ import { TableCell as J } from "@tiptap/extension-table-cell";
16
+ import Z from "@tiptap/extension-image";
17
+ import { c as x } from "./utils-B6yFEsav.js";
18
+ import { an as G, ao as Y } from "./CommandPalette-CdiVSPYz.js";
19
+ import tt from "@tiptap/extension-code-block-lowlight";
20
+ import { createLowlight as et, common as it } from "lowlight";
21
+ import { Markdown as rt } from "@tiptap/markdown";
22
+ import { BoldIcon as nt, ItalicIcon as at, UnderlineIcon as ot, StrikethroughIcon as lt, Heading1Icon as st, Heading2Icon as ct, Heading3Icon as ut, ListIcon as gt, ListOrderedIcon as dt, ListChecksIcon as ft, QuoteIcon as mt, CodeIcon as ht, LinkIcon as pt, ImageIcon as wt, HighlighterIcon as kt, AlignLeftIcon as bt, AlignCenterIcon as vt, AlignRightIcon as It, MinusIcon as xt } from "lucide-react";
23
+ import { Toolbar as At, Input as Lt, ToolbarButton as Nt, ToolbarSeparator as Tt } from "@hachej/boring-ui-kit";
24
+ const C = 64, y = 2e3;
25
+ function Ct(t) {
26
+ return t.replace(/\\/g, "\\\\").replace(/]/g, "\\]");
27
+ }
28
+ function A(t) {
29
+ return t.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
30
+ }
31
+ function R(t) {
32
+ if (typeof t == "number" && Number.isFinite(t) && t > 0) return t;
33
+ if (typeof t == "string" && t.trim()) {
34
+ const n = Number(t);
35
+ if (Number.isFinite(n) && n > 0) return n;
36
+ }
37
+ return null;
38
+ }
39
+ const yt = Z.extend({
40
+ name: "image",
41
+ draggable: !0,
42
+ parseMarkdown: (t, n) => n.createNode("image", {
43
+ src: t.href,
44
+ title: t.title,
45
+ alt: t.text
46
+ }),
47
+ renderMarkdown: (t) => {
48
+ var k, f, h, w, e, a, u;
49
+ const n = typeof ((k = t.attrs) == null ? void 0 : k.src) == "string" ? t.attrs.src : "", r = typeof ((f = t.attrs) == null ? void 0 : f.alt) == "string" ? t.attrs.alt : "", o = typeof ((h = t.attrs) == null ? void 0 : h.title) == "string" ? t.attrs.title : "", c = R((w = t.attrs) == null ? void 0 : w.width), d = R((e = t.attrs) == null ? void 0 : e.height), m = ((a = t.attrs) == null ? void 0 : a.align) === "center" || ((u = t.attrs) == null ? void 0 : u.align) === "right" ? t.attrs.align : "left";
50
+ if (!c && !d && m === "left") {
51
+ const s = Ct(r);
52
+ return o ? `![${s}](${n} "${o.replace(/"/g, '\\"')}")` : `![${s}](${n})`;
53
+ }
54
+ const l = `<img ${[
55
+ `src="${A(n)}"`,
56
+ r ? `alt="${A(r)}"` : null,
57
+ o ? `title="${A(o)}"` : null,
58
+ c ? `width="${c}"` : null,
59
+ d ? `height="${d}"` : null
60
+ ].filter(Boolean).join(" ")} />`;
61
+ return m === "center" || m === "right" ? `<p align="${m}">${l}</p>` : l;
62
+ },
63
+ addAttributes() {
64
+ var n;
65
+ return {
66
+ ...((n = this.parent) == null ? void 0 : n.call(this)) ?? {},
67
+ width: {
68
+ default: null,
69
+ parseHTML: (r) => {
70
+ const o = r.getAttribute("width");
71
+ if (o) {
72
+ const d = parseInt(o, 10);
73
+ return Number.isFinite(d) ? d : null;
74
+ }
75
+ const c = r.style.width;
76
+ if (c && c.endsWith("px")) {
77
+ const d = parseInt(c, 10);
78
+ return Number.isFinite(d) ? d : null;
79
+ }
80
+ return null;
81
+ },
82
+ renderHTML: (r) => r.width ? { width: String(r.width) } : {}
83
+ },
84
+ align: {
85
+ default: "left",
86
+ parseHTML: (r) => {
87
+ const o = r.getAttribute("data-align");
88
+ return o === "left" || o === "center" || o === "right" ? o : r.style.marginLeft === "auto" && r.style.marginRight === "auto" ? "center" : r.style.marginLeft === "auto" ? "right" : "left";
89
+ },
90
+ renderHTML: (r) => ({ "data-align": r.align ?? "left" })
91
+ }
92
+ };
93
+ },
94
+ addNodeView() {
95
+ return $(Rt);
96
+ }
97
+ });
98
+ function Rt({ node: t, updateAttributes: n, selected: r }) {
99
+ const o = v(null), c = v(null), [d, m] = B(!1), p = t.attrs.src ?? "", l = t.attrs.alt ?? "", k = t.attrs.title ?? void 0, f = t.attrs.width, h = t.attrs.align ?? "left";
100
+ L(() => {
101
+ if (!d) return;
102
+ const e = (u) => {
103
+ const s = c.current;
104
+ if (!s) return;
105
+ const I = u.clientX - s.x, U = Math.max(C, Math.min(y, s.width + I));
106
+ n({ width: Math.round(U) });
107
+ }, a = () => {
108
+ c.current = null, m(!1);
109
+ };
110
+ return window.addEventListener("pointermove", e), window.addEventListener("pointerup", a), window.addEventListener("pointercancel", a), () => {
111
+ window.removeEventListener("pointermove", e), window.removeEventListener("pointerup", a), window.removeEventListener("pointercancel", a);
112
+ };
113
+ }, [d, n]);
114
+ const w = (e) => {
115
+ var s;
116
+ e.preventDefault(), e.stopPropagation();
117
+ const a = (s = o.current) == null ? void 0 : s.querySelector("img"), u = f ?? (a == null ? void 0 : a.getBoundingClientRect().width) ?? 320;
118
+ c.current = { x: e.clientX, width: u }, m(!0);
119
+ };
120
+ return /* @__PURE__ */ T(
121
+ E,
122
+ {
123
+ ref: o,
124
+ "data-resizable-image": "",
125
+ "data-selected": r ? "true" : void 0,
126
+ className: x(
127
+ "relative block max-w-full align-baseline",
128
+ "[&>img]:block [&>img]:max-w-full [&>img]:h-auto [&>img]:rounded-md",
129
+ h === "left" && "mr-auto",
130
+ h === "center" && "mx-auto",
131
+ h === "right" && "ml-auto",
132
+ r && "ring-2 ring-[color:var(--accent)] ring-offset-1 ring-offset-background"
133
+ ),
134
+ style: { width: f ? `${f}px` : void 0 },
135
+ children: [
136
+ /* @__PURE__ */ i("img", { src: p, alt: l, title: k, draggable: !1 }),
137
+ /* @__PURE__ */ i(
138
+ "span",
139
+ {
140
+ role: "slider",
141
+ "aria-label": "Resize image",
142
+ "aria-valuenow": f ?? 0,
143
+ "aria-valuemin": C,
144
+ "aria-valuemax": y,
145
+ "data-testid": "resize-handle",
146
+ onPointerDown: w,
147
+ className: x(
148
+ "absolute right-0 bottom-0 h-3 w-3 translate-x-1/2 translate-y-1/2 cursor-nwse-resize",
149
+ "rounded-full border border-background bg-[color:var(--accent)] shadow",
150
+ "opacity-0 transition-opacity duration-150 ease-out",
151
+ (r || d) && "opacity-100"
152
+ )
153
+ }
154
+ )
155
+ ]
156
+ }
157
+ );
158
+ }
159
+ const Ht = et(it), Mt = [
160
+ /<script[\s>][\s\S]*?<\/script>/gi,
161
+ /<iframe[\s>][\s\S]*?<\/iframe>/gi,
162
+ /\s+on\w+\s*=\s*["'][^"']*["']/gi,
163
+ /\s+on\w+\s*=\s*\S+/gi,
164
+ /href\s*=\s*["']?\s*javascript:[^"'\s>]*/gi,
165
+ /src\s*=\s*["']?\s*javascript:[^"'\s>]*/gi
166
+ ];
167
+ function Bt(t) {
168
+ let n = t;
169
+ for (const r of Mt)
170
+ n = n.replace(r, "");
171
+ return n;
172
+ }
173
+ const H = [
174
+ q.configure({
175
+ codeBlock: !1,
176
+ link: !1,
177
+ underline: !1
178
+ }),
179
+ j,
180
+ F.configure({
181
+ openOnClick: !0,
182
+ autolink: !0,
183
+ linkOnPaste: !0,
184
+ defaultProtocol: "https",
185
+ HTMLAttributes: { rel: "noopener noreferrer nofollow", target: "_blank" }
186
+ }),
187
+ S.configure({
188
+ placeholder: "Start writing..."
189
+ }),
190
+ W,
191
+ O.configure({ nested: !0 }),
192
+ V.configure({
193
+ types: ["heading", "paragraph"]
194
+ }),
195
+ _,
196
+ X.configure({
197
+ resizable: !0
198
+ }),
199
+ K,
200
+ Q,
201
+ J,
202
+ yt.configure({
203
+ inline: !1,
204
+ allowBase64: !0
205
+ }),
206
+ tt.configure({ lowlight: Ht }),
207
+ rt.configure({
208
+ markedOptions: {
209
+ gfm: !0,
210
+ breaks: !1,
211
+ pedantic: !1
212
+ }
213
+ })
214
+ ];
215
+ function g({ onClick: t, active: n, disabled: r, title: o, children: c }) {
216
+ return /* @__PURE__ */ i(
217
+ Nt,
218
+ {
219
+ type: "button",
220
+ size: "icon-xs",
221
+ onClick: t,
222
+ disabled: r,
223
+ title: o,
224
+ "aria-pressed": n,
225
+ className: x(
226
+ "text-muted-foreground/70",
227
+ n && "bg-[color:var(--accent-soft)] text-[color:var(--accent)]"
228
+ ),
229
+ children: c
230
+ }
231
+ );
232
+ }
233
+ function b() {
234
+ return /* @__PURE__ */ i(Tt, { className: "mx-2 h-3.5" });
235
+ }
236
+ function M(t) {
237
+ const n = t.trim().toLowerCase();
238
+ return !n.startsWith("javascript:") && !n.startsWith("data:text/html");
239
+ }
240
+ function N(t) {
241
+ return new Promise((n, r) => {
242
+ const o = new FileReader();
243
+ o.onload = () => n(o.result), o.onerror = () => r(o.error ?? new Error("Read failed")), o.readAsDataURL(t);
244
+ });
245
+ }
246
+ function St(t, n) {
247
+ return `${t.replace(/\/$/, "")}${n}`;
248
+ }
249
+ async function Ut(t, n, r = {}) {
250
+ if (!n) return await N(t);
251
+ const o = await N(t), c = o.indexOf(","), d = c >= 0 ? o.slice(c + 1) : "", m = { "Content-Type": "application/json" };
252
+ r.workspaceRequestId && (m["x-boring-workspace-id"] = r.workspaceRequestId);
253
+ const p = await fetch(St(r.apiBaseUrl ?? "", "/api/v1/files/upload"), {
254
+ method: "POST",
255
+ headers: m,
256
+ credentials: "include",
257
+ body: JSON.stringify({
258
+ filename: t.name,
259
+ contentType: t.type,
260
+ contentBase64: d,
261
+ sourcePath: n
262
+ })
263
+ });
264
+ if (!p.ok) throw new Error(`Upload failed: ${p.status}`);
265
+ const l = await p.json();
266
+ return l.markdownUrl ?? l.path ?? o;
267
+ }
268
+ function $t({
269
+ editor: t,
270
+ documentPath: n,
271
+ apiBaseUrl: r,
272
+ workspaceRequestId: o,
273
+ rawMode: c,
274
+ onToggleRawMode: d
275
+ }) {
276
+ const m = (e) => {
277
+ if (t) {
278
+ if (t.isActive("image")) {
279
+ t.chain().focus().updateAttributes("image", { align: e }).run();
280
+ return;
281
+ }
282
+ t.chain().focus().setTextAlign(e).run();
283
+ }
284
+ }, p = v(null), l = D({
285
+ editor: t,
286
+ selector: ({ editor: e }) => e ? {
287
+ bold: e.isActive("bold"),
288
+ italic: e.isActive("italic"),
289
+ underline: e.isActive("underline"),
290
+ strike: e.isActive("strike"),
291
+ h1: e.isActive("heading", { level: 1 }),
292
+ h2: e.isActive("heading", { level: 2 }),
293
+ h3: e.isActive("heading", { level: 3 }),
294
+ bulletList: e.isActive("bulletList"),
295
+ orderedList: e.isActive("orderedList"),
296
+ taskList: e.isActive("taskList"),
297
+ blockquote: e.isActive("blockquote"),
298
+ codeBlock: e.isActive("codeBlock"),
299
+ link: e.isActive("link"),
300
+ highlight: e.isActive("highlight"),
301
+ alignLeft: e.isActive("image") ? (e.getAttributes("image").align ?? "left") === "left" : e.isActive({ textAlign: "left" }),
302
+ alignCenter: e.isActive("image") ? e.getAttributes("image").align === "center" : e.isActive({ textAlign: "center" }),
303
+ alignRight: e.isActive("image") ? e.getAttributes("image").align === "right" : e.isActive({ textAlign: "right" })
304
+ } : null
305
+ });
306
+ if (!t || !l) return null;
307
+ const k = () => {
308
+ const e = window.prompt("URL:");
309
+ e && M(e) && t.chain().focus().extendMarkRange("link").setLink({ href: e }).run();
310
+ }, f = () => {
311
+ const e = window.prompt("Image URL:");
312
+ e && M(e) && t.chain().focus().setImage({ src: e }).run();
313
+ }, h = (e) => {
314
+ var a;
315
+ if (e != null && e.shiftKey) {
316
+ f();
317
+ return;
318
+ }
319
+ (a = p.current) == null || a.click();
320
+ }, w = async (e) => {
321
+ var u;
322
+ const a = (u = e.target.files) == null ? void 0 : u[0];
323
+ if (e.target.value = "", !(!a || !a.type.startsWith("image/")))
324
+ try {
325
+ const s = await Ut(a, n, { apiBaseUrl: r, workspaceRequestId: o });
326
+ t.chain().focus().setImage({ src: s, alt: a.name }).run();
327
+ } catch {
328
+ try {
329
+ const s = await N(a);
330
+ t.chain().focus().setImage({ src: s, alt: a.name }).run();
331
+ } catch {
332
+ }
333
+ }
334
+ };
335
+ return /* @__PURE__ */ T(At, { className: "border-b border-border/60 bg-background px-3 py-1.5", "aria-label": "Formatting toolbar", children: [
336
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleBold().run(), active: l.bold, title: "Bold", children: /* @__PURE__ */ i(nt, { className: "h-4 w-4" }) }),
337
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleItalic().run(), active: l.italic, title: "Italic", children: /* @__PURE__ */ i(at, { className: "h-4 w-4" }) }),
338
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleUnderline().run(), active: l.underline, title: "Underline", children: /* @__PURE__ */ i(ot, { className: "h-4 w-4" }) }),
339
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleStrike().run(), active: l.strike, title: "Strikethrough", children: /* @__PURE__ */ i(lt, { className: "h-4 w-4" }) }),
340
+ /* @__PURE__ */ i(b, {}),
341
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleHeading({ level: 1 }).run(), active: l.h1, title: "Heading 1", children: /* @__PURE__ */ i(st, { className: "h-4 w-4" }) }),
342
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleHeading({ level: 2 }).run(), active: l.h2, title: "Heading 2", children: /* @__PURE__ */ i(ct, { className: "h-4 w-4" }) }),
343
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleHeading({ level: 3 }).run(), active: l.h3, title: "Heading 3", children: /* @__PURE__ */ i(ut, { className: "h-4 w-4" }) }),
344
+ /* @__PURE__ */ i(b, {}),
345
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleBulletList().run(), active: l.bulletList, title: "Bullet list", children: /* @__PURE__ */ i(gt, { className: "h-4 w-4" }) }),
346
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleOrderedList().run(), active: l.orderedList, title: "Ordered list", children: /* @__PURE__ */ i(dt, { className: "h-4 w-4" }) }),
347
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleTaskList().run(), active: l.taskList, title: "Task list", children: /* @__PURE__ */ i(ft, { className: "h-4 w-4" }) }),
348
+ /* @__PURE__ */ i(b, {}),
349
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleBlockquote().run(), active: l.blockquote, title: "Quote", children: /* @__PURE__ */ i(mt, { className: "h-4 w-4" }) }),
350
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleCodeBlock().run(), active: l.codeBlock, title: "Code block", children: /* @__PURE__ */ i(ht, { className: "h-4 w-4" }) }),
351
+ /* @__PURE__ */ i(g, { onClick: k, active: l.link, title: "Link", children: /* @__PURE__ */ i(pt, { className: "h-4 w-4" }) }),
352
+ /* @__PURE__ */ i(
353
+ g,
354
+ {
355
+ onClick: h,
356
+ title: "Image (click to upload, Shift+click for URL)",
357
+ children: /* @__PURE__ */ i(wt, { className: "h-4 w-4" })
358
+ }
359
+ ),
360
+ /* @__PURE__ */ i(
361
+ Lt,
362
+ {
363
+ ref: p,
364
+ "data-testid": "image-file-input",
365
+ type: "file",
366
+ accept: "image/*",
367
+ className: "hidden",
368
+ onChange: w
369
+ }
370
+ ),
371
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().toggleHighlight().run(), active: l.highlight, title: "Highlight", children: /* @__PURE__ */ i(kt, { className: "h-4 w-4" }) }),
372
+ /* @__PURE__ */ i(b, {}),
373
+ /* @__PURE__ */ i(g, { onClick: () => m("left"), active: l.alignLeft, title: "Align left", children: /* @__PURE__ */ i(bt, { className: "h-4 w-4" }) }),
374
+ /* @__PURE__ */ i(g, { onClick: () => m("center"), active: l.alignCenter, title: "Center align", children: /* @__PURE__ */ i(vt, { className: "h-4 w-4" }) }),
375
+ /* @__PURE__ */ i(g, { onClick: () => m("right"), active: l.alignRight, title: "Align right", children: /* @__PURE__ */ i(It, { className: "h-4 w-4" }) }),
376
+ /* @__PURE__ */ i(b, {}),
377
+ /* @__PURE__ */ i(g, { onClick: () => t.chain().focus().setHorizontalRule().run(), title: "Horizontal rule", children: /* @__PURE__ */ i(xt, { className: "h-4 w-4" }) }),
378
+ /* @__PURE__ */ i(b, {}),
379
+ /* @__PURE__ */ i(
380
+ g,
381
+ {
382
+ onClick: d,
383
+ active: c,
384
+ title: c ? "Rich text" : "Raw markdown",
385
+ children: /* @__PURE__ */ i("span", { className: "font-mono text-[10px] font-semibold leading-none tracking-[-0.02em]", children: "MD" })
386
+ }
387
+ )
388
+ ] });
389
+ }
390
+ function ae({
391
+ content: t,
392
+ onChange: n,
393
+ readOnly: r = !1,
394
+ placeholder: o,
395
+ className: c,
396
+ documentPath: d
397
+ }) {
398
+ const m = G(), p = Y(), [l, k] = B(!1), f = v(n);
399
+ f.current = n;
400
+ const h = v(!1), w = t || { type: "doc", content: [{ type: "paragraph" }] }, e = t ? "markdown" : "json", a = z({
401
+ extensions: o ? [
402
+ ...H.filter((u) => u.name !== "placeholder"),
403
+ S.configure({ placeholder: o })
404
+ ] : H,
405
+ content: w,
406
+ contentType: e,
407
+ editable: !r,
408
+ editorProps: {
409
+ attributes: {
410
+ class: "tiptap-prose max-w-[68ch] px-8 py-6 focus:outline-none min-h-[200px]"
411
+ },
412
+ transformPastedHTML: Bt
413
+ },
414
+ onUpdate: ({ editor: u }) => {
415
+ var s, I;
416
+ h.current || (I = f.current) == null || I.call(f, ((s = u.getMarkdown) == null ? void 0 : s.call(u)) ?? u.getHTML());
417
+ }
418
+ });
419
+ return L(() => {
420
+ !a || a.isDestroyed || a.setEditable(!r);
421
+ }, [a, r]), L(() => {
422
+ var s;
423
+ !a || a.isDestroyed || (((s = a.getMarkdown) == null ? void 0 : s.call(a)) ?? a.getHTML()) === t || (h.current = !0, a.commands.setContent(w, { contentType: e }), h.current = !1);
424
+ }, [a, t]), /* @__PURE__ */ T("div", { className: x("flex h-full flex-col overflow-hidden", c), children: [
425
+ !r && /* @__PURE__ */ i(
426
+ $t,
427
+ {
428
+ editor: a,
429
+ documentPath: d,
430
+ apiBaseUrl: m,
431
+ workspaceRequestId: p,
432
+ rawMode: l,
433
+ onToggleRawMode: () => k((u) => !u)
434
+ }
435
+ ),
436
+ /* @__PURE__ */ i("div", { className: "flex-1 overflow-auto", children: l && !r ? /* @__PURE__ */ i(
437
+ "textarea",
438
+ {
439
+ "aria-label": "Raw markdown",
440
+ "data-testid": "markdown-raw-editor",
441
+ 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",
442
+ value: t,
443
+ placeholder: o,
444
+ spellCheck: !1,
445
+ onChange: (u) => {
446
+ var s;
447
+ return (s = f.current) == null ? void 0 : s.call(f, u.target.value);
448
+ }
449
+ }
450
+ ) : /* @__PURE__ */ i(P, { editor: a }) })
451
+ ] });
452
+ }
453
+ export {
454
+ ae as MarkdownEditor,
455
+ M as isSafeUrl,
456
+ N as readFileAsDataUrl,
457
+ Bt as sanitizeHtml
458
+ };
@@ -2,7 +2,7 @@ import { jsxs as d, jsx as o, Fragment as H } from "react/jsx-runtime";
2
2
  import { useCallback as b, useMemo as A, useEffect as S, useState as V, Suspense as X, useRef as q } from "react";
3
3
  import { LoadingState as Z, ResizeHandle as G, IconButton as $, Button as J, Kbd as Q } from "@hachej/boring-ui-kit";
4
4
  import { c as g } from "./utils-B6yFEsav.js";
5
- import { $ as Y, a6 as ee, E as te, am as ne, ak as re, u as oe } from "./CommandPalette-aM61U-b0.js";
5
+ import { $ as Y, a6 as ee, E as te, am as ne, ak as re, u as oe } from "./CommandPalette-CdiVSPYz.js";
6
6
  import { Search as ae, Plus as ie } from "lucide-react";
7
7
  function be(e, t, r = !0) {
8
8
  if (!r || typeof window > "u") return t;
package/dist/app-front.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { jsx as d, jsxs as Te, Fragment as x } from "react/jsx-runtime";
2
2
  import { useSyncExternalStore as ze, useMemo as v, useRef as K, useState as X, useEffect as y, useCallback as P } from "react";
3
3
  import { ChatPanel as Fe, useSessions as Ge } from "@hachej/boring-agent/front";
4
- import { aj as ke, q as Ye, ak as Qe, u as Xe, al as Ze } from "./CommandPalette-aM61U-b0.js";
5
- import { T as He, C as qe, r as Ee, w as et, W as $e } from "./WorkspaceLoadingState-By0dZoPD.js";
4
+ import { aj as ke, q as Ye, ak as Qe, u as Xe, al as Ze } from "./CommandPalette-CdiVSPYz.js";
5
+ import { T as He, C as qe, r as Ee, w as et, W as $e } from "./WorkspaceLoadingState-CLhYOvtL.js";
6
6
  function tt() {
7
7
  const e = `s${Date.now()}`;
8
8
  return {
package/dist/testing.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx as ka } from "react/jsx-runtime";
2
2
  import * as Pa from "react";
3
3
  import { createElement as ds, useMemo as Tn, useLayoutEffect as fs, isValidElement as ps, cloneElement as ms, useSyncExternalStore as Ji } from "react";
4
- import { i as vs, q as bs, ai as hs } from "./CommandPalette-aM61U-b0.js";
4
+ import { i as vs, q as bs, ai as hs } from "./CommandPalette-CdiVSPYz.js";
5
5
  import { d as ys } from "./panel-DnvDNQac.js";
6
6
  import * as Rs from "react-dom/test-utils";
7
7
  import Ba from "react-dom";
@@ -2827,6 +2827,10 @@
2827
2827
  --tw-tracking: -0.01em;
2828
2828
  letter-spacing: -0.01em;
2829
2829
  }
2830
+ .tracking-\[-0\.02em\] {
2831
+ --tw-tracking: -0.02em;
2832
+ letter-spacing: -0.02em;
2833
+ }
2830
2834
  .tracking-\[0\.05em\] {
2831
2835
  --tw-tracking: 0.05em;
2832
2836
  letter-spacing: 0.05em;
@@ -3340,6 +3344,14 @@
3340
3344
  color: var(--boring-muted-foreground);
3341
3345
  }
3342
3346
  }
3347
+ .placeholder\:text-muted-foreground\/70 {
3348
+ &::placeholder {
3349
+ color: var(--boring-muted-foreground);
3350
+ @supports (color: color-mix(in lab, red, red)) {
3351
+ color: color-mix(in oklab, var(--boring-muted-foreground) 70%, transparent);
3352
+ }
3353
+ }
3354
+ }
3343
3355
  .after\:absolute {
3344
3356
  &::after {
3345
3357
  content: var(--tw-content);
@@ -1018,7 +1018,7 @@ export declare interface LeftTabParams {
1018
1018
  chromeless?: boolean;
1019
1019
  }
1020
1020
 
1021
- export declare function MarkdownEditor({ content, onChange, readOnly, placeholder, className, }: MarkdownEditorProps): JSX.Element;
1021
+ export declare function MarkdownEditor({ content, onChange, readOnly, placeholder, className, documentPath, }: MarkdownEditorProps): JSX.Element;
1022
1022
 
1023
1023
  export declare function MarkdownEditorPane({ params, api, className }: MarkdownEditorPaneProps): JSX.Element;
1024
1024
 
@@ -1032,6 +1032,8 @@ export declare interface MarkdownEditorProps {
1032
1032
  readOnly?: boolean;
1033
1033
  placeholder?: string;
1034
1034
  className?: string;
1035
+ /** Workspace-relative markdown file path, used to make uploaded image links relative. */
1036
+ documentPath?: string;
1035
1037
  }
1036
1038
 
1037
1039
  export declare const MAX_PANELS = 50;
package/dist/workspace.js CHANGED
@@ -1,8 +1,8 @@
1
1
  var qe = Object.defineProperty;
2
2
  var _e = (e, t, r) => t in e ? qe(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
3
3
  var ne = (e, t, r) => _e(e, typeof t != "symbol" ? t + "" : t, r);
4
- import { d as ae, u as Ne, p as Be, P as Ae, a as $e, b as Ke, D as Ve } from "./CommandPalette-aM61U-b0.js";
5
- import { A as xr, C as yr, c as br, e as vr, f as Sr, F as Nr, g as Cr, M as Tr, h as Pr, i as kr, j as Er, k as wr, l as Mr, R as Dr, S as Ir, m as Rr, n as Or, T as Fr, W as Lr, o as zr, q as qr, r as _r, s as Br, t as Ar, v as $r, w as Kr, x as Vr, y as Qr, z as jr, B as Gr, E as Hr, G as Wr, H as Ur, I as Xr, J as Jr, K as Yr, L as Zr, N as ea, O as ta, Q as ra, U as aa, V as na, X as la, Y as oa, Z as sa, _ as ca, $ as ia, a0 as ua, a1 as da, a2 as pa, a3 as fa, a4 as ma, a5 as ga, a6 as ha, a7 as xa, a8 as ya, a9 as ba, aa as va, ab as Sa, ac as Na, ad as Ca, ae as Ta, af as Pa, ag as ka, ah as Ea } from "./CommandPalette-aM61U-b0.js";
4
+ import { d as ae, u as Ne, p as Be, P as Ae, a as $e, b as Ke, D as Ve } from "./CommandPalette-CdiVSPYz.js";
5
+ import { A as xr, C as yr, c as br, e as vr, f as Sr, F as Nr, g as Cr, M as Tr, h as Pr, i as kr, j as Er, k as wr, l as Mr, R as Dr, S as Ir, m as Rr, n as Or, T as Fr, W as Lr, o as zr, q as qr, r as _r, s as Br, t as Ar, v as $r, w as Kr, x as Vr, y as Qr, z as jr, B as Gr, E as Hr, G as Wr, H as Ur, I as Xr, J as Jr, K as Yr, L as Zr, N as ea, O as ta, Q as ra, U as aa, V as na, X as la, Y as oa, Z as sa, _ as ca, $ as ia, a0 as ua, a1 as da, a2 as pa, a3 as fa, a4 as ma, a5 as ga, a6 as ha, a7 as xa, a8 as ya, a9 as ba, aa as va, ab as Sa, ac as Na, ad as Ca, ae as Ta, af as Pa, ag as ka, ah as Ea } from "./CommandPalette-CdiVSPYz.js";
6
6
  import { jsxs as h, jsx as a, Fragment as Qe } from "react/jsx-runtime";
7
7
  import { useSyncExternalStore as je, useState as V, useEffect as U, useRef as w, useCallback as S, useReducer as Ge, useMemo as Z, Suspense as He, Component as We } from "react";
8
8
  import { FilterIcon as Ue, XIcon as Xe, ChevronDownIcon as Ce, ChevronRightIcon as Te, Database as Je, BarChart3 as pe, MenuIcon as Ye, PanelLeftOpenIcon as Ze, PanelLeftCloseIcon as et, PinIcon as tt } from "lucide-react";
@@ -10,10 +10,10 @@ import { EmptyState as Pe, Toolbar as rt, Input as ke, Popover as at, PopoverTri
10
10
  import { Toaster as Ma, dismissToast as Da, toast as Ia } from "@hachej/boring-ui-kit";
11
11
  import { c as H } from "./utils-B6yFEsav.js";
12
12
  import { d as we } from "./panel-DnvDNQac.js";
13
- import { C as Oa, T as Fa, W as La, b as za } from "./WorkspaceLoadingState-By0dZoPD.js";
13
+ import { C as Oa, T as Fa, W as La, b as za } from "./WorkspaceLoadingState-CLhYOvtL.js";
14
14
  import { C as _a, c as Ba } from "./CodeEditor-DQqOn4xz.js";
15
- import { FileTree as $a } from "./FileTree-DRq_bfue.js";
16
- import { MarkdownEditor as Va } from "./MarkdownEditor-DjiHxnRv.js";
15
+ import { FileTree as $a } from "./FileTree-Bgg50HQf.js";
16
+ import { MarkdownEditor as Va } from "./MarkdownEditor-BA37oZKg.js";
17
17
  function re(e, t) {
18
18
  const { pluginId: r, ...n } = e;
19
19
  return t ? { ...n, pluginId: t } : n;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hachej/boring-workspace",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Workspace UI, plugin, and bridge package for composing chat, files, catalogs, editors, and app-specific panes.",
@@ -108,6 +108,7 @@
108
108
  "@tiptap/extension-task-list": "^3.22.4",
109
109
  "@tiptap/extension-text-align": "^3.22.4",
110
110
  "@tiptap/extension-underline": "^3.22.4",
111
+ "@tiptap/markdown": "^3.22.4",
111
112
  "@tiptap/pm": "^3.22.4",
112
113
  "@tiptap/react": "^3.22.4",
113
114
  "@tiptap/starter-kit": "^3.22.4",
@@ -122,11 +123,10 @@
122
123
  "radix-ui": "^1.4.3",
123
124
  "react-arborist": "^3.4.0",
124
125
  "tailwind-merge": "^2.0.0",
125
- "tiptap-markdown": "^0.9.0",
126
126
  "zod": "^3.23.0",
127
127
  "zustand": "^5.0.0",
128
- "@hachej/boring-agent": "0.1.4",
129
- "@hachej/boring-ui-kit": "0.1.4"
128
+ "@hachej/boring-agent": "0.1.6",
129
+ "@hachej/boring-ui-kit": "0.1.6"
130
130
  },
131
131
  "devDependencies": {
132
132
  "@tailwindcss/postcss": "^4.0.0",
@@ -1,349 +0,0 @@
1
- import { jsxs as A, jsx as t } from "react/jsx-runtime";
2
- import { useRef as k, useState as y, useEffect as L } from "react";
3
- import { ReactNodeViewRenderer as S, NodeViewWrapper as B, useEditor as E, EditorContent as U, useEditorState as P } from "@tiptap/react";
4
- import z from "@tiptap/starter-kit";
5
- import D from "@tiptap/extension-underline";
6
- import W from "@tiptap/extension-link";
7
- import T from "@tiptap/extension-placeholder";
8
- import F from "@tiptap/extension-task-list";
9
- import j from "@tiptap/extension-task-item";
10
- import q from "@tiptap/extension-text-align";
11
- import V from "@tiptap/extension-highlight";
12
- import { Table as _ } from "@tiptap/extension-table";
13
- import { TableRow as $ } from "@tiptap/extension-table-row";
14
- import { TableHeader as X } from "@tiptap/extension-table-header";
15
- import { TableCell as K } from "@tiptap/extension-table-cell";
16
- import O from "@tiptap/extension-image";
17
- import { c as v } from "./utils-B6yFEsav.js";
18
- import Q from "@tiptap/extension-code-block-lowlight";
19
- import { createLowlight as Z, common as G } from "lowlight";
20
- import { Markdown as J } from "tiptap-markdown";
21
- import { BoldIcon as Y, ItalicIcon as tt, UnderlineIcon as et, StrikethroughIcon as it, Heading1Icon as rt, Heading2Icon as nt, Heading3Icon as at, ListIcon as ot, ListOrderedIcon as lt, ListChecksIcon as st, QuoteIcon as ct, CodeIcon as ut, LinkIcon as gt, ImageIcon as dt, HighlighterIcon as mt, AlignLeftIcon as ft, AlignCenterIcon as ht, AlignRightIcon as pt, MinusIcon as wt } from "lucide-react";
22
- import { Toolbar as kt, Input as vt, ToolbarButton as bt, ToolbarSeparator as Lt } from "@hachej/boring-ui-kit";
23
- const I = 64, x = 2e3, At = O.extend({
24
- name: "image",
25
- draggable: !0,
26
- addAttributes() {
27
- var a;
28
- return {
29
- ...((a = this.parent) == null ? void 0 : a.call(this)) ?? {},
30
- width: {
31
- default: null,
32
- parseHTML: (n) => {
33
- const r = n.getAttribute("width");
34
- if (r) {
35
- const s = parseInt(r, 10);
36
- return Number.isFinite(s) ? s : null;
37
- }
38
- const u = n.style.width;
39
- if (u && u.endsWith("px")) {
40
- const s = parseInt(u, 10);
41
- return Number.isFinite(s) ? s : null;
42
- }
43
- return null;
44
- },
45
- renderHTML: (n) => n.width ? { width: String(n.width) } : {}
46
- },
47
- align: {
48
- default: "left",
49
- parseHTML: (n) => {
50
- const r = n.getAttribute("data-align");
51
- return r === "left" || r === "center" || r === "right" ? r : n.style.marginLeft === "auto" && n.style.marginRight === "auto" ? "center" : n.style.marginLeft === "auto" ? "right" : "left";
52
- },
53
- renderHTML: (n) => ({ "data-align": n.align ?? "left" })
54
- }
55
- };
56
- },
57
- addNodeView() {
58
- return S(It);
59
- }
60
- });
61
- function It({ node: i, updateAttributes: a, selected: n }) {
62
- const r = k(null), u = k(null), [s, d] = y(!1), c = i.attrs.src ?? "", e = i.attrs.alt ?? "", o = i.attrs.title ?? void 0, g = i.attrs.width, m = i.attrs.align ?? "left";
63
- L(() => {
64
- if (!s) return;
65
- const h = (b) => {
66
- const p = u.current;
67
- if (!p) return;
68
- const R = b.clientX - p.x, M = Math.max(I, Math.min(x, p.width + R));
69
- a({ width: Math.round(M) });
70
- }, f = () => {
71
- u.current = null, d(!1);
72
- };
73
- return window.addEventListener("pointermove", h), window.addEventListener("pointerup", f), window.addEventListener("pointercancel", f), () => {
74
- window.removeEventListener("pointermove", h), window.removeEventListener("pointerup", f), window.removeEventListener("pointercancel", f);
75
- };
76
- }, [s, a]);
77
- const H = (h) => {
78
- var p;
79
- h.preventDefault(), h.stopPropagation();
80
- const f = (p = r.current) == null ? void 0 : p.querySelector("img"), b = g ?? (f == null ? void 0 : f.getBoundingClientRect().width) ?? 320;
81
- u.current = { x: h.clientX, width: b }, d(!0);
82
- };
83
- return /* @__PURE__ */ A(
84
- B,
85
- {
86
- ref: r,
87
- "data-resizable-image": "",
88
- "data-selected": n ? "true" : void 0,
89
- className: v(
90
- "relative block max-w-full align-baseline",
91
- "[&>img]:block [&>img]:max-w-full [&>img]:h-auto [&>img]:rounded-md",
92
- m === "left" && "mr-auto",
93
- m === "center" && "mx-auto",
94
- m === "right" && "ml-auto",
95
- n && "ring-2 ring-[color:var(--accent)] ring-offset-1 ring-offset-background"
96
- ),
97
- style: { width: g ? `${g}px` : void 0 },
98
- children: [
99
- /* @__PURE__ */ t("img", { src: c, alt: e, title: o, draggable: !1 }),
100
- /* @__PURE__ */ t(
101
- "span",
102
- {
103
- role: "slider",
104
- "aria-label": "Resize image",
105
- "aria-valuenow": g ?? 0,
106
- "aria-valuemin": I,
107
- "aria-valuemax": x,
108
- "data-testid": "resize-handle",
109
- onPointerDown: H,
110
- className: v(
111
- "absolute right-0 bottom-0 h-3 w-3 translate-x-1/2 translate-y-1/2 cursor-nwse-resize",
112
- "rounded-full border border-background bg-[color:var(--accent)] shadow",
113
- "opacity-0 transition-opacity duration-150 ease-out",
114
- (n || s) && "opacity-100"
115
- )
116
- }
117
- )
118
- ]
119
- }
120
- );
121
- }
122
- const xt = Z(G), Ct = [
123
- /<script[\s>][\s\S]*?<\/script>/gi,
124
- /<iframe[\s>][\s\S]*?<\/iframe>/gi,
125
- /\s+on\w+\s*=\s*["'][^"']*["']/gi,
126
- /\s+on\w+\s*=\s*\S+/gi,
127
- /href\s*=\s*["']?\s*javascript:[^"'\s>]*/gi,
128
- /src\s*=\s*["']?\s*javascript:[^"'\s>]*/gi
129
- ];
130
- function Nt(i) {
131
- let a = i;
132
- for (const n of Ct)
133
- a = a.replace(n, "");
134
- return a;
135
- }
136
- const C = [
137
- z.configure({
138
- codeBlock: !1,
139
- link: !1,
140
- underline: !1
141
- }),
142
- D,
143
- W.configure({
144
- openOnClick: !0,
145
- autolink: !0,
146
- linkOnPaste: !0,
147
- defaultProtocol: "https",
148
- HTMLAttributes: { rel: "noopener noreferrer nofollow", target: "_blank" }
149
- }),
150
- T.configure({
151
- placeholder: "Start writing..."
152
- }),
153
- F,
154
- j.configure({ nested: !0 }),
155
- q.configure({
156
- types: ["heading", "paragraph"]
157
- }),
158
- V,
159
- _.configure({
160
- resizable: !0
161
- }),
162
- $,
163
- X,
164
- K,
165
- At.configure({
166
- inline: !1,
167
- allowBase64: !0
168
- }),
169
- Q.configure({ lowlight: xt }),
170
- J.configure({
171
- html: !0,
172
- transformPastedText: !0,
173
- transformCopiedText: !0
174
- })
175
- ];
176
- function l({ onClick: i, active: a, disabled: n, title: r, children: u }) {
177
- return /* @__PURE__ */ t(
178
- bt,
179
- {
180
- type: "button",
181
- size: "icon-xs",
182
- onClick: i,
183
- disabled: n,
184
- title: r,
185
- "aria-pressed": a,
186
- className: v(
187
- "text-muted-foreground/70",
188
- a && "bg-[color:var(--accent-soft)] text-[color:var(--accent)]"
189
- ),
190
- children: u
191
- }
192
- );
193
- }
194
- function w() {
195
- return /* @__PURE__ */ t(Lt, { className: "mx-2 h-3.5" });
196
- }
197
- function N(i) {
198
- const a = i.trim().toLowerCase();
199
- return !a.startsWith("javascript:") && !a.startsWith("data:text/html");
200
- }
201
- function Tt(i) {
202
- return new Promise((a, n) => {
203
- const r = new FileReader();
204
- r.onload = () => a(r.result), r.onerror = () => n(r.error ?? new Error("Read failed")), r.readAsDataURL(i);
205
- });
206
- }
207
- function Ht({ editor: i }) {
208
- const a = (e) => {
209
- if (i) {
210
- if (i.isActive("image")) {
211
- i.chain().focus().updateAttributes("image", { align: e }).run();
212
- return;
213
- }
214
- i.chain().focus().setTextAlign(e).run();
215
- }
216
- }, n = k(null), r = P({
217
- editor: i,
218
- selector: ({ editor: e }) => e ? {
219
- bold: e.isActive("bold"),
220
- italic: e.isActive("italic"),
221
- underline: e.isActive("underline"),
222
- strike: e.isActive("strike"),
223
- h1: e.isActive("heading", { level: 1 }),
224
- h2: e.isActive("heading", { level: 2 }),
225
- h3: e.isActive("heading", { level: 3 }),
226
- bulletList: e.isActive("bulletList"),
227
- orderedList: e.isActive("orderedList"),
228
- taskList: e.isActive("taskList"),
229
- blockquote: e.isActive("blockquote"),
230
- codeBlock: e.isActive("codeBlock"),
231
- link: e.isActive("link"),
232
- highlight: e.isActive("highlight"),
233
- alignLeft: e.isActive("image") ? (e.getAttributes("image").align ?? "left") === "left" : e.isActive({ textAlign: "left" }),
234
- alignCenter: e.isActive("image") ? e.getAttributes("image").align === "center" : e.isActive({ textAlign: "center" }),
235
- alignRight: e.isActive("image") ? e.getAttributes("image").align === "right" : e.isActive({ textAlign: "right" })
236
- } : null
237
- });
238
- if (!i || !r) return null;
239
- const u = () => {
240
- const e = window.prompt("URL:");
241
- e && N(e) && i.chain().focus().extendMarkRange("link").setLink({ href: e }).run();
242
- }, s = () => {
243
- const e = window.prompt("Image URL:");
244
- e && N(e) && i.chain().focus().setImage({ src: e }).run();
245
- }, d = (e) => {
246
- var o;
247
- if (e != null && e.shiftKey) {
248
- s();
249
- return;
250
- }
251
- (o = n.current) == null || o.click();
252
- }, c = async (e) => {
253
- var g;
254
- const o = (g = e.target.files) == null ? void 0 : g[0];
255
- if (e.target.value = "", !(!o || !o.type.startsWith("image/")))
256
- try {
257
- const m = await Tt(o);
258
- i.chain().focus().setImage({ src: m, alt: o.name }).run();
259
- } catch {
260
- }
261
- };
262
- return /* @__PURE__ */ A(kt, { className: "border-b border-border/60 bg-background px-3 py-1.5", "aria-label": "Formatting toolbar", children: [
263
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleBold().run(), active: r.bold, title: "Bold", children: /* @__PURE__ */ t(Y, { className: "h-4 w-4" }) }),
264
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleItalic().run(), active: r.italic, title: "Italic", children: /* @__PURE__ */ t(tt, { className: "h-4 w-4" }) }),
265
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleUnderline().run(), active: r.underline, title: "Underline", children: /* @__PURE__ */ t(et, { className: "h-4 w-4" }) }),
266
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleStrike().run(), active: r.strike, title: "Strikethrough", children: /* @__PURE__ */ t(it, { className: "h-4 w-4" }) }),
267
- /* @__PURE__ */ t(w, {}),
268
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleHeading({ level: 1 }).run(), active: r.h1, title: "Heading 1", children: /* @__PURE__ */ t(rt, { className: "h-4 w-4" }) }),
269
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleHeading({ level: 2 }).run(), active: r.h2, title: "Heading 2", children: /* @__PURE__ */ t(nt, { className: "h-4 w-4" }) }),
270
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleHeading({ level: 3 }).run(), active: r.h3, title: "Heading 3", children: /* @__PURE__ */ t(at, { className: "h-4 w-4" }) }),
271
- /* @__PURE__ */ t(w, {}),
272
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleBulletList().run(), active: r.bulletList, title: "Bullet list", children: /* @__PURE__ */ t(ot, { className: "h-4 w-4" }) }),
273
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleOrderedList().run(), active: r.orderedList, title: "Ordered list", children: /* @__PURE__ */ t(lt, { className: "h-4 w-4" }) }),
274
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleTaskList().run(), active: r.taskList, title: "Task list", children: /* @__PURE__ */ t(st, { className: "h-4 w-4" }) }),
275
- /* @__PURE__ */ t(w, {}),
276
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleBlockquote().run(), active: r.blockquote, title: "Quote", children: /* @__PURE__ */ t(ct, { className: "h-4 w-4" }) }),
277
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleCodeBlock().run(), active: r.codeBlock, title: "Code block", children: /* @__PURE__ */ t(ut, { className: "h-4 w-4" }) }),
278
- /* @__PURE__ */ t(l, { onClick: u, active: r.link, title: "Link", children: /* @__PURE__ */ t(gt, { className: "h-4 w-4" }) }),
279
- /* @__PURE__ */ t(
280
- l,
281
- {
282
- onClick: d,
283
- title: "Image (click to upload, Shift+click for URL)",
284
- children: /* @__PURE__ */ t(dt, { className: "h-4 w-4" })
285
- }
286
- ),
287
- /* @__PURE__ */ t(
288
- vt,
289
- {
290
- ref: n,
291
- "data-testid": "image-file-input",
292
- type: "file",
293
- accept: "image/*",
294
- className: "hidden",
295
- onChange: c
296
- }
297
- ),
298
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().toggleHighlight().run(), active: r.highlight, title: "Highlight", children: /* @__PURE__ */ t(mt, { className: "h-4 w-4" }) }),
299
- /* @__PURE__ */ t(w, {}),
300
- /* @__PURE__ */ t(l, { onClick: () => a("left"), active: r.alignLeft, title: "Align left", children: /* @__PURE__ */ t(ft, { className: "h-4 w-4" }) }),
301
- /* @__PURE__ */ t(l, { onClick: () => a("center"), active: r.alignCenter, title: "Center align", children: /* @__PURE__ */ t(ht, { className: "h-4 w-4" }) }),
302
- /* @__PURE__ */ t(l, { onClick: () => a("right"), active: r.alignRight, title: "Align right", children: /* @__PURE__ */ t(pt, { className: "h-4 w-4" }) }),
303
- /* @__PURE__ */ t(w, {}),
304
- /* @__PURE__ */ t(l, { onClick: () => i.chain().focus().setHorizontalRule().run(), title: "Horizontal rule", children: /* @__PURE__ */ t(wt, { className: "h-4 w-4" }) })
305
- ] });
306
- }
307
- function Gt({
308
- content: i,
309
- onChange: a,
310
- readOnly: n = !1,
311
- placeholder: r,
312
- className: u
313
- }) {
314
- const s = k(a);
315
- s.current = a;
316
- const d = k(!1), c = E({
317
- extensions: r ? [
318
- ...C.filter((e) => e.name !== "placeholder"),
319
- T.configure({ placeholder: r })
320
- ] : C,
321
- content: i,
322
- editable: !n,
323
- editorProps: {
324
- attributes: {
325
- class: "tiptap-prose max-w-[68ch] px-8 py-6 focus:outline-none min-h-[200px]"
326
- },
327
- transformPastedHTML: Nt
328
- },
329
- onUpdate: ({ editor: e }) => {
330
- var o, g, m;
331
- d.current || (m = s.current) == null || m.call(s, ((g = (o = e.storage.markdown) == null ? void 0 : o.getMarkdown) == null ? void 0 : g.call(o)) ?? e.getHTML());
332
- }
333
- });
334
- return L(() => {
335
- !c || c.isDestroyed || c.setEditable(!n);
336
- }, [c, n]), L(() => {
337
- var o, g;
338
- !c || c.isDestroyed || (((g = (o = c.storage.markdown) == null ? void 0 : o.getMarkdown) == null ? void 0 : g.call(o)) ?? c.getHTML()) === i || (d.current = !0, c.commands.setContent(i), d.current = !1);
339
- }, [c, i]), /* @__PURE__ */ A("div", { className: v("flex h-full flex-col overflow-hidden", u), children: [
340
- !n && /* @__PURE__ */ t(Ht, { editor: c }),
341
- /* @__PURE__ */ t("div", { className: "flex-1 overflow-auto", children: /* @__PURE__ */ t(U, { editor: c }) })
342
- ] });
343
- }
344
- export {
345
- Gt as MarkdownEditor,
346
- N as isSafeUrl,
347
- Tt as readFileAsDataUrl,
348
- Nt as sanitizeHtml
349
- };