@hachej/boring-workspace 0.1.16 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -34
- package/dist/{FileTree-Dl-qUAB0.js → FileTree-DHVB9rpk.js} +15 -15
- package/dist/MarkdownEditor-L1KDH0bM.js +534 -0
- package/dist/{WorkspaceLoadingState-CSZfENWe.js → WorkspaceLoadingState-DYDxUYnx.js} +114 -110
- package/dist/WorkspaceProvider-CDPaAO5u.js +5971 -0
- package/dist/app-front.d.ts +94 -107
- package/dist/app-front.js +243 -233
- package/dist/app-server.d.ts +130 -15
- package/dist/app-server.js +1569 -304
- package/dist/{bootstrapServer-BreQ9QBc.d.ts → createInMemoryBridge-BDxDzihm.d.ts} +11 -26
- package/dist/manifest-CyNNdfYz.d.ts +58 -0
- package/dist/plugin.d.ts +199 -0
- package/dist/plugin.js +300 -0
- package/dist/server.d.ts +239 -4
- package/dist/server.js +901 -78
- package/dist/shared.d.ts +4 -112
- package/dist/surface-COYagY2m.d.ts +111 -0
- package/dist/testing.d.ts +19 -1
- package/dist/testing.js +2 -2
- package/dist/{agent-tool-DEtfQPVB.d.ts → ui-bridge-Gfh1MMgl.d.ts} +30 -30
- package/dist/workspace.css +36 -0
- package/dist/workspace.d.ts +165 -120
- package/dist/workspace.js +330 -377
- package/docs/INTERFACES.md +9 -9
- package/docs/PLUGIN_STRUCTURE.md +39 -145
- package/docs/PLUGIN_SYSTEM.md +355 -0
- package/docs/README.md +6 -1
- package/docs/plans/README.md +1 -0
- package/docs/plans/archive/HOT_RELOADABLE_AGENT_PLUGINS_PLAN.md +218 -0
- package/docs/plans/archive/RELOAD_PLUGGABILITY_PLAN.md +174 -0
- package/docs/plans/archive/UNIFIED_PLUGIN_SYSTEM_PLAN.md +769 -0
- package/package.json +11 -5
- package/dist/CommandPalette-NOEOVkN2.js +0 -5714
- package/dist/MarkdownEditor-yc6mFsnI.js +0 -533
- package/docs/bridge.md +0 -135
- package/docs/panels.md +0 -102
- package/docs/plugins.md +0 -158
- /package/docs/plans/{MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md → archive/MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md} +0 -0
|
@@ -1,533 +0,0 @@
|
|
|
1
|
-
import { jsxs as R, jsx as e } from "react/jsx-runtime";
|
|
2
|
-
import { useState as D, useCallback as V, useRef as L, useEffect as B, useMemo as X } from "react";
|
|
3
|
-
import { ReactNodeViewRenderer as K, NodeViewWrapper as Q, useEditor as Z, EditorContent as G, useEditorState as J } from "@tiptap/react";
|
|
4
|
-
import Y from "@tiptap/starter-kit";
|
|
5
|
-
import tt from "@tiptap/extension-underline";
|
|
6
|
-
import et from "@tiptap/extension-link";
|
|
7
|
-
import P from "@tiptap/extension-placeholder";
|
|
8
|
-
import rt from "@tiptap/extension-task-list";
|
|
9
|
-
import it from "@tiptap/extension-task-item";
|
|
10
|
-
import nt from "@tiptap/extension-text-align";
|
|
11
|
-
import at from "@tiptap/extension-highlight";
|
|
12
|
-
import { Table as ot } from "@tiptap/extension-table";
|
|
13
|
-
import { TableRow as lt } from "@tiptap/extension-table-row";
|
|
14
|
-
import { TableHeader as st } from "@tiptap/extension-table-header";
|
|
15
|
-
import { TableCell as ct } from "@tiptap/extension-table-cell";
|
|
16
|
-
import ut from "@tiptap/extension-image";
|
|
17
|
-
import { c as H } from "./utils-B6yFEsav.js";
|
|
18
|
-
import { ao as j, ap as gt } from "./CommandPalette-NOEOVkN2.js";
|
|
19
|
-
import { uploadFile as dt } from "@hachej/boring-agent/front";
|
|
20
|
-
import ft from "@tiptap/extension-code-block-lowlight";
|
|
21
|
-
import { createLowlight as mt, common as pt } from "lowlight";
|
|
22
|
-
import { Markdown as ht } from "@tiptap/markdown";
|
|
23
|
-
import { BoldIcon as wt, ItalicIcon as kt, UnderlineIcon as vt, StrikethroughIcon as bt, Heading1Icon as It, Heading2Icon as At, Heading3Icon as yt, ListIcon as Lt, ListOrderedIcon as xt, ListChecksIcon as Nt, QuoteIcon as Ct, CodeIcon as Tt, LinkIcon as Mt, ImageIcon as Rt, HighlighterIcon as Ht, AlignLeftIcon as Ut, AlignCenterIcon as $t, AlignRightIcon as St, MinusIcon as Bt, Loader2Icon as Dt } from "lucide-react";
|
|
24
|
-
import { Toolbar as Et, Input as Ft, ToolbarButton as zt, ToolbarSeparator as Wt } from "@hachej/boring-ui-kit";
|
|
25
|
-
function Pt(t) {
|
|
26
|
-
const i = j(), n = gt(), [a, c] = D(0);
|
|
27
|
-
return { upload: V(
|
|
28
|
-
async (f, o) => {
|
|
29
|
-
c((p) => p + 1);
|
|
30
|
-
try {
|
|
31
|
-
return await dt(f, {
|
|
32
|
-
apiBaseUrl: i,
|
|
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
|
-
c((p) => p - 1);
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
[i, n, t == null ? void 0 : t.directory]
|
|
42
|
-
), uploading: a > 0 };
|
|
43
|
-
}
|
|
44
|
-
const E = 64, F = 2e3;
|
|
45
|
-
function jt(t) {
|
|
46
|
-
return t.replace(/\\/g, "\\\\").replace(/]/g, "\\]");
|
|
47
|
-
}
|
|
48
|
-
function S(t) {
|
|
49
|
-
return t.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
50
|
-
}
|
|
51
|
-
function z(t) {
|
|
52
|
-
if (typeof t == "number" && Number.isFinite(t) && t > 0) return t;
|
|
53
|
-
if (typeof t == "string" && t.trim()) {
|
|
54
|
-
const i = Number(t);
|
|
55
|
-
if (Number.isFinite(i) && i > 0) return i;
|
|
56
|
-
}
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
const q = ut.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: (i) => i
|
|
67
|
-
};
|
|
68
|
-
},
|
|
69
|
-
parseMarkdown: (t, i) => i.createNode("image", {
|
|
70
|
-
src: t.href,
|
|
71
|
-
title: t.title,
|
|
72
|
-
alt: t.text
|
|
73
|
-
}),
|
|
74
|
-
renderMarkdown: (t) => {
|
|
75
|
-
var b, I, A, r, m, w, y;
|
|
76
|
-
const i = typeof ((b = t.attrs) == null ? void 0 : b.src) == "string" ? t.attrs.src : "", n = typeof ((I = t.attrs) == null ? void 0 : I.alt) == "string" ? t.attrs.alt : "", a = typeof ((A = t.attrs) == null ? void 0 : A.title) == "string" ? t.attrs.title : "", c = z((r = t.attrs) == null ? void 0 : r.width), l = z((m = t.attrs) == null ? void 0 : m.height), f = ((w = t.attrs) == null ? void 0 : w.align) === "center" || ((y = t.attrs) == null ? void 0 : y.align) === "right" ? t.attrs.align : "left";
|
|
77
|
-
if (!c && !l && f === "left") {
|
|
78
|
-
const k = jt(n);
|
|
79
|
-
return a ? `}")` : ``;
|
|
80
|
-
}
|
|
81
|
-
const p = `<img ${[
|
|
82
|
-
`src="${S(i)}"`,
|
|
83
|
-
n ? `alt="${S(n)}"` : null,
|
|
84
|
-
a ? `title="${S(a)}"` : null,
|
|
85
|
-
c ? `width="${c}"` : null,
|
|
86
|
-
l ? `height="${l}"` : null
|
|
87
|
-
].filter(Boolean).join(" ")} />`;
|
|
88
|
-
return f === "center" || f === "right" ? `<p align="${f}">${p}</p>` : p;
|
|
89
|
-
},
|
|
90
|
-
addAttributes() {
|
|
91
|
-
var i;
|
|
92
|
-
return {
|
|
93
|
-
...((i = this.parent) == null ? void 0 : i.call(this)) ?? {},
|
|
94
|
-
width: {
|
|
95
|
-
default: null,
|
|
96
|
-
parseHTML: (n) => {
|
|
97
|
-
const a = n.getAttribute("width");
|
|
98
|
-
if (a) {
|
|
99
|
-
const l = parseInt(a, 10);
|
|
100
|
-
return Number.isFinite(l) ? l : null;
|
|
101
|
-
}
|
|
102
|
-
const c = n.style.width;
|
|
103
|
-
if (c && c.endsWith("px")) {
|
|
104
|
-
const l = parseInt(c, 10);
|
|
105
|
-
return Number.isFinite(l) ? l : 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 K(qt);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
function qt({ node: t, updateAttributes: i, selected: n, extension: a }) {
|
|
136
|
-
const c = L(null), l = L(null), [f, o] = D(!1), p = t.attrs.src ?? "", I = (a.options.resolveSrc ?? ((k) => k))(p), A = t.attrs.alt ?? "", r = t.attrs.title ?? void 0, m = t.attrs.width, w = t.attrs.align ?? "left";
|
|
137
|
-
B(() => {
|
|
138
|
-
if (!f) return;
|
|
139
|
-
const k = (g) => {
|
|
140
|
-
const s = l.current;
|
|
141
|
-
if (!s) return;
|
|
142
|
-
const d = g.clientX - s.x, h = Math.max(E, Math.min(F, s.width + d));
|
|
143
|
-
i({ width: Math.round(h) });
|
|
144
|
-
}, v = () => {
|
|
145
|
-
l.current = null, o(!1);
|
|
146
|
-
};
|
|
147
|
-
return window.addEventListener("pointermove", k), window.addEventListener("pointerup", v), window.addEventListener("pointercancel", v), () => {
|
|
148
|
-
window.removeEventListener("pointermove", k), window.removeEventListener("pointerup", v), window.removeEventListener("pointercancel", v);
|
|
149
|
-
};
|
|
150
|
-
}, [f, i]);
|
|
151
|
-
const y = (k) => {
|
|
152
|
-
var s;
|
|
153
|
-
k.preventDefault(), k.stopPropagation();
|
|
154
|
-
const v = (s = c.current) == null ? void 0 : s.querySelector("img"), g = m ?? (v == null ? void 0 : v.getBoundingClientRect().width) ?? 320;
|
|
155
|
-
l.current = { x: k.clientX, width: g }, o(!0);
|
|
156
|
-
};
|
|
157
|
-
return /* @__PURE__ */ R(
|
|
158
|
-
Q,
|
|
159
|
-
{
|
|
160
|
-
ref: c,
|
|
161
|
-
"data-resizable-image": "",
|
|
162
|
-
"data-selected": n ? "true" : void 0,
|
|
163
|
-
className: H(
|
|
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: m ? `${m}px` : void 0 },
|
|
172
|
-
children: [
|
|
173
|
-
/* @__PURE__ */ e("img", { src: I, alt: A, title: r, draggable: !1 }),
|
|
174
|
-
/* @__PURE__ */ e(
|
|
175
|
-
"span",
|
|
176
|
-
{
|
|
177
|
-
role: "slider",
|
|
178
|
-
"aria-label": "Resize image",
|
|
179
|
-
"aria-valuenow": m ?? 0,
|
|
180
|
-
"aria-valuemin": E,
|
|
181
|
-
"aria-valuemax": F,
|
|
182
|
-
"data-testid": "resize-handle",
|
|
183
|
-
onPointerDown: y,
|
|
184
|
-
className: H(
|
|
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 || f) && "opacity-100"
|
|
189
|
-
)
|
|
190
|
-
}
|
|
191
|
-
)
|
|
192
|
-
]
|
|
193
|
-
}
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
const _t = mt(pt), Ot = [
|
|
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 Vt(t) {
|
|
205
|
-
let i = t;
|
|
206
|
-
for (const n of Ot)
|
|
207
|
-
i = i.replace(n, "");
|
|
208
|
-
return i;
|
|
209
|
-
}
|
|
210
|
-
function Xt(t) {
|
|
211
|
-
return /^(?:[a-z][a-z0-9+.-]*:|\/|#)/i.test(t);
|
|
212
|
-
}
|
|
213
|
-
function Kt(t, i) {
|
|
214
|
-
const n = t.match(/^([^?#]*)([?#].*)?$/), a = (n == null ? void 0 : n[1]) ?? t, c = (n == null ? void 0 : n[2]) ?? "", l = i != null && i.includes("/") ? i.slice(0, i.lastIndexOf("/")) : "", f = `${l ? `${l}/` : ""}${a}`.split("/"), o = [];
|
|
215
|
-
for (const p of f)
|
|
216
|
-
!p || p === "." || (p === ".." ? o.pop() : o.push(p));
|
|
217
|
-
return `${o.join("/")}${c}`;
|
|
218
|
-
}
|
|
219
|
-
function Qt(t, i, n) {
|
|
220
|
-
if (!t || Xt(t)) return t;
|
|
221
|
-
const a = Kt(t, i);
|
|
222
|
-
return `${n.replace(/\/$/, "")}/api/v1/files/raw?path=${encodeURIComponent(a)}`;
|
|
223
|
-
}
|
|
224
|
-
const Zt = [
|
|
225
|
-
Y.configure({
|
|
226
|
-
codeBlock: !1,
|
|
227
|
-
link: !1,
|
|
228
|
-
underline: !1
|
|
229
|
-
}),
|
|
230
|
-
tt,
|
|
231
|
-
et.configure({
|
|
232
|
-
openOnClick: !0,
|
|
233
|
-
autolink: !0,
|
|
234
|
-
linkOnPaste: !0,
|
|
235
|
-
defaultProtocol: "https",
|
|
236
|
-
HTMLAttributes: { rel: "noopener noreferrer nofollow", target: "_blank" }
|
|
237
|
-
}),
|
|
238
|
-
P.configure({
|
|
239
|
-
placeholder: "Start writing..."
|
|
240
|
-
}),
|
|
241
|
-
rt,
|
|
242
|
-
it.configure({ nested: !0 }),
|
|
243
|
-
nt.configure({
|
|
244
|
-
types: ["heading", "paragraph"]
|
|
245
|
-
}),
|
|
246
|
-
at,
|
|
247
|
-
ot.configure({
|
|
248
|
-
resizable: !0
|
|
249
|
-
}),
|
|
250
|
-
lt,
|
|
251
|
-
st,
|
|
252
|
-
ct,
|
|
253
|
-
q.configure({
|
|
254
|
-
inline: !1,
|
|
255
|
-
allowBase64: !0
|
|
256
|
-
}),
|
|
257
|
-
ft.configure({ lowlight: _t }),
|
|
258
|
-
ht.configure({
|
|
259
|
-
markedOptions: {
|
|
260
|
-
gfm: !0,
|
|
261
|
-
breaks: !1,
|
|
262
|
-
pedantic: !1
|
|
263
|
-
}
|
|
264
|
-
})
|
|
265
|
-
];
|
|
266
|
-
function u({ onClick: t, active: i, disabled: n, title: a, children: c }) {
|
|
267
|
-
return /* @__PURE__ */ e(
|
|
268
|
-
zt,
|
|
269
|
-
{
|
|
270
|
-
type: "button",
|
|
271
|
-
size: "icon-xs",
|
|
272
|
-
onClick: t,
|
|
273
|
-
disabled: n,
|
|
274
|
-
title: a,
|
|
275
|
-
"aria-pressed": i,
|
|
276
|
-
className: H(
|
|
277
|
-
"text-muted-foreground/70",
|
|
278
|
-
i && "bg-[color:var(--accent-soft)] text-[color:var(--accent)]"
|
|
279
|
-
),
|
|
280
|
-
children: c
|
|
281
|
-
}
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
function x() {
|
|
285
|
-
return /* @__PURE__ */ e(Wt, { className: "mx-2 h-3.5" });
|
|
286
|
-
}
|
|
287
|
-
function W(t) {
|
|
288
|
-
const i = t.trim().toLowerCase();
|
|
289
|
-
return !i.startsWith("javascript:") && !i.startsWith("data:text/html");
|
|
290
|
-
}
|
|
291
|
-
function Gt(t) {
|
|
292
|
-
return new Promise((i, n) => {
|
|
293
|
-
const a = new FileReader();
|
|
294
|
-
a.onload = () => i(a.result), a.onerror = () => n(a.error ?? new Error("Read failed")), a.readAsDataURL(t);
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
function Jt(t) {
|
|
298
|
-
if (!t) return null;
|
|
299
|
-
for (const i of Array.from(t.files ?? []))
|
|
300
|
-
if (i.type.startsWith("image/")) return i;
|
|
301
|
-
for (const i of Array.from(t.items ?? [])) {
|
|
302
|
-
if (i.kind !== "file" || !i.type.startsWith("image/")) continue;
|
|
303
|
-
const n = i.getAsFile();
|
|
304
|
-
if (n) return n;
|
|
305
|
-
}
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
function Yt({
|
|
309
|
-
editor: t,
|
|
310
|
-
onInsertImage: i,
|
|
311
|
-
rawMode: n,
|
|
312
|
-
onToggleRawMode: a,
|
|
313
|
-
uploading: c
|
|
314
|
-
}) {
|
|
315
|
-
const l = (r) => {
|
|
316
|
-
if (t) {
|
|
317
|
-
if (t.isActive("image")) {
|
|
318
|
-
t.chain().focus().updateAttributes("image", { align: r }).run();
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
t.chain().focus().setTextAlign(r).run();
|
|
322
|
-
}
|
|
323
|
-
}, f = L(null), o = J({
|
|
324
|
-
editor: t,
|
|
325
|
-
selector: ({ editor: r }) => r ? {
|
|
326
|
-
bold: r.isActive("bold"),
|
|
327
|
-
italic: r.isActive("italic"),
|
|
328
|
-
underline: r.isActive("underline"),
|
|
329
|
-
strike: r.isActive("strike"),
|
|
330
|
-
h1: r.isActive("heading", { level: 1 }),
|
|
331
|
-
h2: r.isActive("heading", { level: 2 }),
|
|
332
|
-
h3: r.isActive("heading", { level: 3 }),
|
|
333
|
-
bulletList: r.isActive("bulletList"),
|
|
334
|
-
orderedList: r.isActive("orderedList"),
|
|
335
|
-
taskList: r.isActive("taskList"),
|
|
336
|
-
blockquote: r.isActive("blockquote"),
|
|
337
|
-
codeBlock: r.isActive("codeBlock"),
|
|
338
|
-
link: r.isActive("link"),
|
|
339
|
-
highlight: r.isActive("highlight"),
|
|
340
|
-
alignLeft: r.isActive("image") ? (r.getAttributes("image").align ?? "left") === "left" : r.isActive({ textAlign: "left" }),
|
|
341
|
-
alignCenter: r.isActive("image") ? r.getAttributes("image").align === "center" : r.isActive({ textAlign: "center" }),
|
|
342
|
-
alignRight: r.isActive("image") ? r.getAttributes("image").align === "right" : r.isActive({ textAlign: "right" })
|
|
343
|
-
} : null
|
|
344
|
-
});
|
|
345
|
-
if (!t || !o) return null;
|
|
346
|
-
const p = () => {
|
|
347
|
-
const r = window.prompt("URL:");
|
|
348
|
-
r && W(r) && t.chain().focus().extendMarkRange("link").setLink({ href: r }).run();
|
|
349
|
-
}, b = () => {
|
|
350
|
-
const r = window.prompt("Image URL:");
|
|
351
|
-
r && W(r) && t.chain().focus().setImage({ src: r }).run();
|
|
352
|
-
}, I = (r) => {
|
|
353
|
-
var m;
|
|
354
|
-
if (r != null && r.shiftKey) {
|
|
355
|
-
b();
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
(m = f.current) == null || m.click();
|
|
359
|
-
}, A = async (r) => {
|
|
360
|
-
var w;
|
|
361
|
-
const m = (w = r.target.files) == null ? void 0 : w[0];
|
|
362
|
-
r.target.value = "", !(!m || !m.type.startsWith("image/")) && await i(m);
|
|
363
|
-
};
|
|
364
|
-
return /* @__PURE__ */ R(Et, { className: "border-b border-border/60 bg-background px-3 py-1.5", "aria-label": "Formatting toolbar", children: [
|
|
365
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleBold().run(), active: o.bold, title: "Bold", children: /* @__PURE__ */ e(wt, { className: "h-4 w-4" }) }),
|
|
366
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleItalic().run(), active: o.italic, title: "Italic", children: /* @__PURE__ */ e(kt, { className: "h-4 w-4" }) }),
|
|
367
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleUnderline().run(), active: o.underline, title: "Underline", children: /* @__PURE__ */ e(vt, { className: "h-4 w-4" }) }),
|
|
368
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleStrike().run(), active: o.strike, title: "Strikethrough", children: /* @__PURE__ */ e(bt, { className: "h-4 w-4" }) }),
|
|
369
|
-
/* @__PURE__ */ e(x, {}),
|
|
370
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleHeading({ level: 1 }).run(), active: o.h1, title: "Heading 1", children: /* @__PURE__ */ e(It, { className: "h-4 w-4" }) }),
|
|
371
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleHeading({ level: 2 }).run(), active: o.h2, title: "Heading 2", children: /* @__PURE__ */ e(At, { className: "h-4 w-4" }) }),
|
|
372
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleHeading({ level: 3 }).run(), active: o.h3, title: "Heading 3", children: /* @__PURE__ */ e(yt, { className: "h-4 w-4" }) }),
|
|
373
|
-
/* @__PURE__ */ e(x, {}),
|
|
374
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleBulletList().run(), active: o.bulletList, title: "Bullet list", children: /* @__PURE__ */ e(Lt, { className: "h-4 w-4" }) }),
|
|
375
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleOrderedList().run(), active: o.orderedList, title: "Ordered list", children: /* @__PURE__ */ e(xt, { className: "h-4 w-4" }) }),
|
|
376
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleTaskList().run(), active: o.taskList, title: "Task list", children: /* @__PURE__ */ e(Nt, { className: "h-4 w-4" }) }),
|
|
377
|
-
/* @__PURE__ */ e(x, {}),
|
|
378
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleBlockquote().run(), active: o.blockquote, title: "Quote", children: /* @__PURE__ */ e(Ct, { className: "h-4 w-4" }) }),
|
|
379
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleCodeBlock().run(), active: o.codeBlock, title: "Code block", children: /* @__PURE__ */ e(Tt, { className: "h-4 w-4" }) }),
|
|
380
|
-
/* @__PURE__ */ e(u, { onClick: p, active: o.link, title: "Link", children: /* @__PURE__ */ e(Mt, { className: "h-4 w-4" }) }),
|
|
381
|
-
/* @__PURE__ */ e(
|
|
382
|
-
u,
|
|
383
|
-
{
|
|
384
|
-
onClick: I,
|
|
385
|
-
title: "Image (click to upload, Shift+click for URL)",
|
|
386
|
-
children: /* @__PURE__ */ e(Rt, { className: "h-4 w-4" })
|
|
387
|
-
}
|
|
388
|
-
),
|
|
389
|
-
/* @__PURE__ */ e(
|
|
390
|
-
Ft,
|
|
391
|
-
{
|
|
392
|
-
ref: f,
|
|
393
|
-
"data-testid": "image-file-input",
|
|
394
|
-
type: "file",
|
|
395
|
-
accept: "image/*",
|
|
396
|
-
className: "hidden",
|
|
397
|
-
onChange: A
|
|
398
|
-
}
|
|
399
|
-
),
|
|
400
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleHighlight().run(), active: o.highlight, title: "Highlight", children: /* @__PURE__ */ e(Ht, { className: "h-4 w-4" }) }),
|
|
401
|
-
/* @__PURE__ */ e(x, {}),
|
|
402
|
-
/* @__PURE__ */ e(u, { onClick: () => l("left"), active: o.alignLeft, title: "Align left", children: /* @__PURE__ */ e(Ut, { className: "h-4 w-4" }) }),
|
|
403
|
-
/* @__PURE__ */ e(u, { onClick: () => l("center"), active: o.alignCenter, title: "Center align", children: /* @__PURE__ */ e($t, { className: "h-4 w-4" }) }),
|
|
404
|
-
/* @__PURE__ */ e(u, { onClick: () => l("right"), active: o.alignRight, title: "Align right", children: /* @__PURE__ */ e(St, { className: "h-4 w-4" }) }),
|
|
405
|
-
/* @__PURE__ */ e(x, {}),
|
|
406
|
-
/* @__PURE__ */ e(u, { onClick: () => t.chain().focus().setHorizontalRule().run(), title: "Horizontal rule", children: /* @__PURE__ */ e(Bt, { className: "h-4 w-4" }) }),
|
|
407
|
-
/* @__PURE__ */ e(x, {}),
|
|
408
|
-
c && /* @__PURE__ */ R(
|
|
409
|
-
"span",
|
|
410
|
-
{
|
|
411
|
-
role: "status",
|
|
412
|
-
"aria-live": "polite",
|
|
413
|
-
className: "ml-auto flex items-center gap-1.5 text-[11px] text-muted-foreground/80",
|
|
414
|
-
children: [
|
|
415
|
-
/* @__PURE__ */ e(Dt, { className: "h-3 w-3 animate-spin", "aria-hidden": !0 }),
|
|
416
|
-
"Uploading…"
|
|
417
|
-
]
|
|
418
|
-
}
|
|
419
|
-
),
|
|
420
|
-
/* @__PURE__ */ e(
|
|
421
|
-
u,
|
|
422
|
-
{
|
|
423
|
-
onClick: a,
|
|
424
|
-
active: n,
|
|
425
|
-
title: n ? "Rich text" : "Raw markdown",
|
|
426
|
-
children: /* @__PURE__ */ e("span", { className: "font-mono text-[10px] font-semibold leading-none tracking-[-0.02em]", children: "MD" })
|
|
427
|
-
}
|
|
428
|
-
)
|
|
429
|
-
] });
|
|
430
|
-
}
|
|
431
|
-
function Le({
|
|
432
|
-
content: t,
|
|
433
|
-
onChange: i,
|
|
434
|
-
readOnly: n = !1,
|
|
435
|
-
placeholder: a,
|
|
436
|
-
className: c,
|
|
437
|
-
documentPath: l
|
|
438
|
-
}) {
|
|
439
|
-
const f = j(), { upload: o, uploading: p } = Pt(), [b, I] = D(!1), A = X(() => {
|
|
440
|
-
const s = q.configure({
|
|
441
|
-
inline: !1,
|
|
442
|
-
allowBase64: !0,
|
|
443
|
-
resolveSrc: (h) => Qt(h, l, f)
|
|
444
|
-
}), d = Zt.map((h) => h.name === "image" ? s : h);
|
|
445
|
-
return a ? [
|
|
446
|
-
...d.filter((h) => h.name !== "placeholder"),
|
|
447
|
-
P.configure({ placeholder: a })
|
|
448
|
-
] : d;
|
|
449
|
-
}, [f, l, a]), r = L(i);
|
|
450
|
-
r.current = i;
|
|
451
|
-
const m = L(!1), w = L(null), y = L(async () => {
|
|
452
|
-
});
|
|
453
|
-
y.current = async (s) => {
|
|
454
|
-
const d = w.current;
|
|
455
|
-
if (!d) return;
|
|
456
|
-
let h;
|
|
457
|
-
try {
|
|
458
|
-
h = await Gt(s);
|
|
459
|
-
} catch {
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
const U = typeof crypto < "u" && typeof crypto.randomUUID == "function" ? crypto.randomUUID() : `pending-${Date.now()}-${Math.random().toString(36).slice(2)}`, _ = { src: h, alt: s.name, pendingUploadId: U };
|
|
463
|
-
d.chain().focus().setImage(_).run();
|
|
464
|
-
try {
|
|
465
|
-
const { url: N } = await o(s, { sourcePath: l }), C = w.current;
|
|
466
|
-
if (!C || C.isDestroyed) return;
|
|
467
|
-
C.commands.command(({ tr: $, state: T }) => (T.doc.descendants((M, O) => (M.type.name === "image" && M.attrs.pendingUploadId === U && $.setNodeMarkup(O, void 0, { ...M.attrs, src: N, pendingUploadId: null }), !0)), !0));
|
|
468
|
-
} catch {
|
|
469
|
-
const N = w.current;
|
|
470
|
-
if (!N || N.isDestroyed) return;
|
|
471
|
-
N.commands.command(({ tr: C, state: $ }) => ($.doc.descendants((T, M) => (T.type.name === "image" && T.attrs.pendingUploadId === U && C.setNodeMarkup(M, void 0, { ...T.attrs, pendingUploadId: null }), !0)), !0));
|
|
472
|
-
}
|
|
473
|
-
};
|
|
474
|
-
const k = t || { type: "doc", content: [{ type: "paragraph" }] }, v = t ? "markdown" : "json", g = Z({
|
|
475
|
-
extensions: A,
|
|
476
|
-
content: k,
|
|
477
|
-
contentType: v,
|
|
478
|
-
editable: !n,
|
|
479
|
-
editorProps: {
|
|
480
|
-
attributes: {
|
|
481
|
-
class: "tiptap-prose max-w-[68ch] px-8 py-6 focus:outline-none min-h-[200px]"
|
|
482
|
-
},
|
|
483
|
-
transformPastedHTML: Vt,
|
|
484
|
-
handlePaste: (s, d) => {
|
|
485
|
-
if (n) return !1;
|
|
486
|
-
const h = Jt(d.clipboardData);
|
|
487
|
-
return h ? (d.preventDefault(), y.current(h), !0) : !1;
|
|
488
|
-
}
|
|
489
|
-
},
|
|
490
|
-
onUpdate: ({ editor: s }) => {
|
|
491
|
-
var d, h;
|
|
492
|
-
m.current || (h = r.current) == null || h.call(r, ((d = s.getMarkdown) == null ? void 0 : d.call(s)) ?? s.getHTML());
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
return w.current = g, B(() => {
|
|
496
|
-
!g || g.isDestroyed || g.setEditable(!n);
|
|
497
|
-
}, [g, n]), B(() => {
|
|
498
|
-
var d;
|
|
499
|
-
!g || g.isDestroyed || (((d = g.getMarkdown) == null ? void 0 : d.call(g)) ?? g.getHTML()) === t || (m.current = !0, g.commands.setContent(k, { contentType: v }), m.current = !1);
|
|
500
|
-
}, [g, t]), /* @__PURE__ */ R("div", { className: H("flex h-full flex-col overflow-hidden", c), children: [
|
|
501
|
-
!n && /* @__PURE__ */ e(
|
|
502
|
-
Yt,
|
|
503
|
-
{
|
|
504
|
-
editor: g,
|
|
505
|
-
onInsertImage: (s) => y.current(s),
|
|
506
|
-
rawMode: b,
|
|
507
|
-
onToggleRawMode: () => I((s) => !s),
|
|
508
|
-
uploading: p
|
|
509
|
-
}
|
|
510
|
-
),
|
|
511
|
-
/* @__PURE__ */ e("div", { className: "flex-1 overflow-auto", children: b && !n ? /* @__PURE__ */ e(
|
|
512
|
-
"textarea",
|
|
513
|
-
{
|
|
514
|
-
"aria-label": "Raw markdown",
|
|
515
|
-
"data-testid": "markdown-raw-editor",
|
|
516
|
-
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",
|
|
517
|
-
value: t,
|
|
518
|
-
placeholder: a,
|
|
519
|
-
spellCheck: !1,
|
|
520
|
-
onChange: (s) => {
|
|
521
|
-
var d;
|
|
522
|
-
return (d = r.current) == null ? void 0 : d.call(r, s.target.value);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
) : /* @__PURE__ */ e(G, { editor: g }) })
|
|
526
|
-
] });
|
|
527
|
-
}
|
|
528
|
-
export {
|
|
529
|
-
Le as MarkdownEditor,
|
|
530
|
-
W as isSafeUrl,
|
|
531
|
-
Gt as readFileAsDataUrl,
|
|
532
|
-
Vt as sanitizeHtml
|
|
533
|
-
};
|
package/docs/bridge.md
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
> boring-ui agents use exec_ui to open panels and interact with the workspace. Ask boring-ui to wire up a new surface.
|
|
2
|
-
|
|
3
|
-
# UI Bridge
|
|
4
|
-
|
|
5
|
-
The UI bridge is the typed pubsub channel between the agent backend and the workspace frontend. The agent calls `exec_ui` (a tool) to post commands; the frontend dispatches them against the live workspace runtime.
|
|
6
|
-
|
|
7
|
-
## Table of Contents
|
|
8
|
-
|
|
9
|
-
- [Opening a panel from the agent](#opening-a-panel-from-the-agent)
|
|
10
|
-
- [openSurface vs openPanel](#opensurface-vs-openpanel)
|
|
11
|
-
- [Reading current UI state](#reading-current-ui-state)
|
|
12
|
-
- [Surface resolvers](#surface-resolvers)
|
|
13
|
-
- [Posting from server code](#posting-from-server-code)
|
|
14
|
-
- [Frontend event bus](#frontend-event-bus)
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## Opening a panel from the agent
|
|
19
|
-
|
|
20
|
-
Use `exec_ui` with `kind: "openSurface"`:
|
|
21
|
-
|
|
22
|
-
```json
|
|
23
|
-
{
|
|
24
|
-
"kind": "openSurface",
|
|
25
|
-
"params": {
|
|
26
|
-
"kind": "my-plugin.open",
|
|
27
|
-
"target": "item-123",
|
|
28
|
-
"meta": { "title": "My Item" }
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
The workspace routes this to the plugin's `surface-resolver` output, which maps the `kind` to a concrete panel open call.
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## openSurface vs openPanel
|
|
38
|
-
|
|
39
|
-
| method | use when |
|
|
40
|
-
|---|---|
|
|
41
|
-
| `openSurface` | you want plugin resolver selection — preferred for domain targets |
|
|
42
|
-
| `openPanel` | you intentionally name the concrete panel id |
|
|
43
|
-
|
|
44
|
-
**Prefer `openSurface`.** It keeps the agent decoupled from panel ids and lets the plugin control routing.
|
|
45
|
-
|
|
46
|
-
Open a file in the editor (built-in surface):
|
|
47
|
-
|
|
48
|
-
```json
|
|
49
|
-
{
|
|
50
|
-
"kind": "openSurface",
|
|
51
|
-
"params": {
|
|
52
|
-
"kind": "workspace.open.path",
|
|
53
|
-
"target": "src/index.ts"
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## Reading current UI state
|
|
61
|
-
|
|
62
|
-
Use `get_ui_state` before `openPanel` to discover which panel components are registered, or to check what the user is currently viewing:
|
|
63
|
-
|
|
64
|
-
```json
|
|
65
|
-
// tool call: get_ui_state (no params)
|
|
66
|
-
// returns:
|
|
67
|
-
{
|
|
68
|
-
"workbenchOpen": true,
|
|
69
|
-
"drawerOpen": false,
|
|
70
|
-
"openTabs": [{ "id": "...", "title": "...", "params": {} }],
|
|
71
|
-
"activeTab": "tab-id-or-null",
|
|
72
|
-
"activeFile": "src/index.ts-or-null",
|
|
73
|
-
"availablePanels": ["code-editor", "chart-canvas", "..."]
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
`availablePanels` lists every component id registered by the host — use these with `exec_ui openPanel`.
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
## Surface resolvers
|
|
82
|
-
|
|
83
|
-
Register a surface resolver in your plugin to map `SurfaceOpenRequest` kinds to panel opens:
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
import { defineFrontPlugin, type SurfacePanelResolution } from '@boring/workspace'
|
|
87
|
-
|
|
88
|
-
defineFrontPlugin({
|
|
89
|
-
outputs: [
|
|
90
|
-
{
|
|
91
|
-
type: 'surface-resolver',
|
|
92
|
-
resolve(req): SurfacePanelResolution | null {
|
|
93
|
-
if (req.kind === 'my-plugin.open') {
|
|
94
|
-
return {
|
|
95
|
-
panelId: 'my-panel',
|
|
96
|
-
params: { id: req.target },
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return null
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
|
-
})
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## Posting from server code
|
|
109
|
-
|
|
110
|
-
From a Fastify route or server plugin:
|
|
111
|
-
|
|
112
|
-
```ts
|
|
113
|
-
import { postUiCommand } from '@boring/workspace/server'
|
|
114
|
-
|
|
115
|
-
await postUiCommand(workspaceId, {
|
|
116
|
-
kind: 'openSurface',
|
|
117
|
-
params: { kind: 'my-plugin.open', target: 'item-123' },
|
|
118
|
-
})
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## Frontend event bus
|
|
124
|
-
|
|
125
|
-
Subscribe to workspace events on the frontend:
|
|
126
|
-
|
|
127
|
-
```ts
|
|
128
|
-
import { events, workspaceEvents } from '@boring/workspace'
|
|
129
|
-
|
|
130
|
-
events.on(workspaceEvents.panelOpened, (panel) => {
|
|
131
|
-
console.log('panel opened', panel.id)
|
|
132
|
-
})
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
See [plugins.md](./plugins.md) to register a surface resolver in your plugin.
|