@june-ai/june-ui 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -2
- package/README.md +633 -400
- package/dist/block-automount.js +2 -0
- package/dist/{chunk-VFZI3SEL.js → chunk-27XM6EVX.js} +8 -8
- package/dist/chunk-3NVO336D.cjs +4314 -0
- package/dist/chunk-G6YPSE6J.cjs +520 -0
- package/dist/chunk-H26BMR5S.js +4314 -0
- package/dist/{document-engine-RKD5SLXR.js → document-engine-BUTK73DW.js} +1 -1
- package/dist/document-engine-GXZH2LNW.cjs +12 -0
- package/dist/entry-core.cjs +148 -0
- package/dist/entry-core.d.cts +1005 -0
- package/dist/entry-core.d.ts +1005 -0
- package/dist/entry-core.js +149 -0
- package/dist/index.cjs +173 -4196
- package/dist/index.d.cts +81 -525
- package/dist/index.d.ts +81 -525
- package/dist/index.js +155 -3552
- package/dist/june.css +94 -0
- package/docs/architecture.md +95 -0
- package/docs/component-registration.md +115 -0
- package/docs/custom-features.md +68 -0
- package/docs/streaming.md +76 -0
- package/docs/theming.md +65 -0
- package/docs/troubleshooting.md +59 -0
- package/package.json +23 -6
|
@@ -0,0 +1,4314 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
var _chunkG6YPSE6Jcjs = require('./chunk-G6YPSE6J.cjs');
|
|
23
|
+
|
|
24
|
+
// src/engine/openui-devtools.tsx
|
|
25
|
+
var _devtools = require('@openuidev/devtools');
|
|
26
|
+
var _jsxruntime = require('react/jsx-runtime');
|
|
27
|
+
var AUTO_MOUNT_FLAG = /* @__PURE__ */ Symbol.for("openui.devtools.autoMount");
|
|
28
|
+
var BLOCKED_BY_JUNE = /* @__PURE__ */ Symbol.for("openui.devtools.blockedByJune");
|
|
29
|
+
var flags = globalThis;
|
|
30
|
+
var scheduledByOpenUI = flags[AUTO_MOUNT_FLAG] === true && flags[BLOCKED_BY_JUNE] !== true;
|
|
31
|
+
flags[AUTO_MOUNT_FLAG] = true;
|
|
32
|
+
flags[BLOCKED_BY_JUNE] = true;
|
|
33
|
+
var DEVTOOLS_BUNDLE = "https://cdn.jsdelivr.net/npm/@openuidev/devtools@0/dist/devtools.browser.js";
|
|
34
|
+
var ALIAS_CACHE_MS = 5 * 60 * 1e3;
|
|
35
|
+
function claimDisabledDevtools() {
|
|
36
|
+
if (typeof document === "undefined") return () => {
|
|
37
|
+
};
|
|
38
|
+
const url = `${DEVTOOLS_BUNDLE}?t=${Math.floor(Date.now() / ALIAS_CACHE_MS)}`;
|
|
39
|
+
let cancelled = false;
|
|
40
|
+
let unmount = () => {
|
|
41
|
+
cancelled = true;
|
|
42
|
+
};
|
|
43
|
+
void Promise.all([
|
|
44
|
+
Promise.resolve().then(() => _interopRequireWildcard(require(
|
|
45
|
+
/* @vite-ignore */
|
|
46
|
+
url
|
|
47
|
+
))),
|
|
48
|
+
Promise.resolve().then(() => _interopRequireWildcard(require("react"))),
|
|
49
|
+
Promise.resolve().then(() => _interopRequireWildcard(require("react-dom"))),
|
|
50
|
+
Promise.resolve().then(() => _interopRequireWildcard(require("react-dom/client")))
|
|
51
|
+
]).then(([devtools, react, reactDom, reactDomClient]) => {
|
|
52
|
+
if (cancelled) return;
|
|
53
|
+
unmount = devtools.mountOpenUIDevtools({
|
|
54
|
+
React: react,
|
|
55
|
+
ReactDOM: reactDom,
|
|
56
|
+
ReactDOMClient: reactDomClient,
|
|
57
|
+
loadReactLang: () => Promise.resolve().then(() => _interopRequireWildcard(require("@openuidev/react-lang"))),
|
|
58
|
+
props: { enabled: false }
|
|
59
|
+
});
|
|
60
|
+
}).catch(() => {
|
|
61
|
+
});
|
|
62
|
+
return () => unmount();
|
|
63
|
+
}
|
|
64
|
+
if (scheduledByOpenUI && process.env.NODE_ENV === "development" && process.env.VITEST !== "true" && typeof document !== "undefined") {
|
|
65
|
+
claimDisabledDevtools();
|
|
66
|
+
}
|
|
67
|
+
function OpenUIDevtoolsGate() {
|
|
68
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _devtools.OpenUIDevtools, { enabled: false });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/engine/renderer/june-renderer.tsx
|
|
72
|
+
var _reactlang = require('@openuidev/react-lang');
|
|
73
|
+
var _react = require('react');
|
|
74
|
+
|
|
75
|
+
// src/core/context.ts
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
// src/themes/theme-context.ts
|
|
79
|
+
|
|
80
|
+
var JuneContext = _react.createContext.call(void 0, {
|
|
81
|
+
mode: "light",
|
|
82
|
+
theme: "light"
|
|
83
|
+
});
|
|
84
|
+
function useJuneTheme() {
|
|
85
|
+
return _react.useContext.call(void 0, JuneContext);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/core/context.ts
|
|
89
|
+
var JuneRuntimeContext = _react.createContext.call(void 0, { isStreaming: false });
|
|
90
|
+
function useJuneRuntime() {
|
|
91
|
+
return _react.useContext.call(void 0, JuneRuntimeContext);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/features/actions/action-handler.ts
|
|
95
|
+
function firstString(...values) {
|
|
96
|
+
for (const v of values) if (typeof v === "string" && v.length > 0) return v;
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
var defaultParseActionEvent = (event) => {
|
|
100
|
+
if (!event || typeof event !== "object") return null;
|
|
101
|
+
const e = event;
|
|
102
|
+
const name = firstString(e.type, e.name, e.action);
|
|
103
|
+
if (!name) return null;
|
|
104
|
+
return { name, payload: _nullishCoalesce(_nullishCoalesce(e.params, () => ( e.payload)), () => ( e.data)) };
|
|
105
|
+
};
|
|
106
|
+
async function runAction(event, options) {
|
|
107
|
+
const { actions, parseEvent = defaultParseActionEvent, confirm, onError, onUnhandled } = options;
|
|
108
|
+
const parsed = parseEvent(event);
|
|
109
|
+
const action = parsed && Object.prototype.hasOwnProperty.call(actions, parsed.name) ? actions[parsed.name] : void 0;
|
|
110
|
+
if (!parsed || !action) {
|
|
111
|
+
_optionalChain([onUnhandled, 'optionalCall', _2 => _2(event)]);
|
|
112
|
+
return "unhandled";
|
|
113
|
+
}
|
|
114
|
+
const { name } = parsed;
|
|
115
|
+
let payload = parsed.payload;
|
|
116
|
+
if (action.schema) {
|
|
117
|
+
const result = action.schema.safeParse(payload);
|
|
118
|
+
if (!result.success) {
|
|
119
|
+
_optionalChain([onError, 'optionalCall', _3 => _3(
|
|
120
|
+
new (0, _chunkG6YPSE6Jcjs.JuneError)("ACTION_VALIDATION_ERROR", `Invalid payload for action "${name}"`, {
|
|
121
|
+
details: result.error
|
|
122
|
+
})
|
|
123
|
+
)]);
|
|
124
|
+
return "failed";
|
|
125
|
+
}
|
|
126
|
+
payload = result.data;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
if (action.sensitive || action.confirm) {
|
|
130
|
+
const ask = _nullishCoalesce(action.confirm, () => ( confirm));
|
|
131
|
+
if (!ask) {
|
|
132
|
+
_optionalChain([onError, 'optionalCall', _4 => _4(
|
|
133
|
+
new (0, _chunkG6YPSE6Jcjs.JuneError)(
|
|
134
|
+
"ACTION_REJECTED",
|
|
135
|
+
`Action "${name}" requires confirmation but no confirm handler was provided`
|
|
136
|
+
)
|
|
137
|
+
)]);
|
|
138
|
+
return "rejected";
|
|
139
|
+
}
|
|
140
|
+
const approved = await ask({ name, payload, description: action.description });
|
|
141
|
+
if (!approved) return "rejected";
|
|
142
|
+
}
|
|
143
|
+
await action.handler(payload, { name, event });
|
|
144
|
+
return "handled";
|
|
145
|
+
} catch (error) {
|
|
146
|
+
_optionalChain([onError, 'optionalCall', _5 => _5(_chunkG6YPSE6Jcjs.toJuneError.call(void 0, error, "ACTION_ERROR"))]);
|
|
147
|
+
return "failed";
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function createActionHandler(options) {
|
|
151
|
+
return (event) => {
|
|
152
|
+
void runAction(event, options);
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/features/document-editor/document-context.ts
|
|
157
|
+
|
|
158
|
+
var JuneDocumentContext = _react.createContext.call(void 0, {});
|
|
159
|
+
function useDocumentOptions() {
|
|
160
|
+
return _react.useContext.call(void 0, JuneDocumentContext);
|
|
161
|
+
}
|
|
162
|
+
function useDocumentValue(store, id, selector) {
|
|
163
|
+
const read = () => selector(store.get(id));
|
|
164
|
+
return _react.useSyncExternalStore.call(void 0, store.subscribe, read, read);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/features/document-editor/use-document-bridge.ts
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
// src/features/document-editor/document-merge.ts
|
|
171
|
+
var MAX_CELLS = 4e6;
|
|
172
|
+
function matchIndices(a, b) {
|
|
173
|
+
const n = a.length;
|
|
174
|
+
const m = b.length;
|
|
175
|
+
if (n * m > MAX_CELLS) return null;
|
|
176
|
+
const dp = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));
|
|
177
|
+
for (let i2 = n - 1; i2 >= 0; i2--) {
|
|
178
|
+
for (let j2 = m - 1; j2 >= 0; j2--) {
|
|
179
|
+
dp[i2][j2] = a[i2] === b[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const matches = /* @__PURE__ */ new Map();
|
|
183
|
+
let i = 0;
|
|
184
|
+
let j = 0;
|
|
185
|
+
while (i < n && j < m) {
|
|
186
|
+
if (a[i] === b[j]) {
|
|
187
|
+
matches.set(i, j);
|
|
188
|
+
i++;
|
|
189
|
+
j++;
|
|
190
|
+
} else if (dp[i + 1][j] >= dp[i][j + 1]) i++;
|
|
191
|
+
else j++;
|
|
192
|
+
}
|
|
193
|
+
return matches;
|
|
194
|
+
}
|
|
195
|
+
var same = (x, y) => x.length === y.length && x.every((v, i) => v === y[i]);
|
|
196
|
+
function mergeBlocks(base, local, incoming) {
|
|
197
|
+
if (same(local, base)) return { blocks: incoming, conflicts: 0 };
|
|
198
|
+
if (same(incoming, base) || same(local, incoming)) return { blocks: local, conflicts: 0 };
|
|
199
|
+
const ml = matchIndices(base, local);
|
|
200
|
+
const mn = matchIndices(base, incoming);
|
|
201
|
+
if (!ml || !mn) return { blocks: null, conflicts: 1 };
|
|
202
|
+
const anchors = [...ml.keys()].filter((i) => mn.has(i)).sort((x, y) => x - y);
|
|
203
|
+
const out = [];
|
|
204
|
+
let conflicts = 0;
|
|
205
|
+
let pb = -1;
|
|
206
|
+
let pl = -1;
|
|
207
|
+
let pn = -1;
|
|
208
|
+
const region = (bEnd, lEnd, nEnd) => {
|
|
209
|
+
const B = base.slice(pb + 1, bEnd);
|
|
210
|
+
const L = local.slice(pl + 1, lEnd);
|
|
211
|
+
const N = incoming.slice(pn + 1, nEnd);
|
|
212
|
+
if (B.length === L.length && L.length === N.length) {
|
|
213
|
+
for (let k = 0; k < B.length; k++) {
|
|
214
|
+
if (L[k] === B[k]) out.push(N[k]);
|
|
215
|
+
else if (N[k] === B[k] || N[k] === L[k]) out.push(L[k]);
|
|
216
|
+
else {
|
|
217
|
+
conflicts++;
|
|
218
|
+
out.push(L[k]);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (same(L, B)) out.push(...N);
|
|
224
|
+
else if (same(N, B) || same(L, N)) out.push(...L);
|
|
225
|
+
else {
|
|
226
|
+
conflicts++;
|
|
227
|
+
out.push(...L);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
for (const a of anchors) {
|
|
231
|
+
const la = ml.get(a);
|
|
232
|
+
const na = mn.get(a);
|
|
233
|
+
region(a, la, na);
|
|
234
|
+
out.push(local[la]);
|
|
235
|
+
pb = a;
|
|
236
|
+
pl = la;
|
|
237
|
+
pn = na;
|
|
238
|
+
}
|
|
239
|
+
region(base.length, local.length, incoming.length);
|
|
240
|
+
return { blocks: out, conflicts };
|
|
241
|
+
}
|
|
242
|
+
function mergeDocuments(base, local, incoming) {
|
|
243
|
+
const toBlocks = (d) => (_nullishCoalesce(d.content, () => ( []))).map((node) => JSON.stringify(node));
|
|
244
|
+
const { blocks: blocks2, conflicts } = mergeBlocks(toBlocks(base), toBlocks(local), toBlocks(incoming));
|
|
245
|
+
if (!blocks2) return { doc: local, conflicts: Math.max(1, conflicts) };
|
|
246
|
+
if (blocks2.length === 0) return { doc: _chunkG6YPSE6Jcjs.emptyDoc.call(void 0, ), conflicts };
|
|
247
|
+
return { doc: { type: "doc", content: blocks2.map((b) => JSON.parse(b)) }, conflicts };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/features/document-editor/document-store.ts
|
|
251
|
+
var incomingKey = (doc) => JSON.stringify([doc.title, _chunkG6YPSE6Jcjs.docKey.call(void 0, doc.json)]);
|
|
252
|
+
function createDocumentStore() {
|
|
253
|
+
const records = /* @__PURE__ */ new Map();
|
|
254
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
255
|
+
const emit = () => listeners.forEach((listener) => listener());
|
|
256
|
+
const put = (record) => {
|
|
257
|
+
records.set(record.id, record);
|
|
258
|
+
emit();
|
|
259
|
+
};
|
|
260
|
+
const fresh2 = (doc) => ({
|
|
261
|
+
id: doc.id,
|
|
262
|
+
title: doc.title,
|
|
263
|
+
documentType: doc.documentType,
|
|
264
|
+
status: doc.status,
|
|
265
|
+
json: doc.json,
|
|
266
|
+
baseJson: doc.json,
|
|
267
|
+
baseTitle: doc.title,
|
|
268
|
+
lastIncomingKey: incomingKey(doc),
|
|
269
|
+
revision: 0,
|
|
270
|
+
origin: "external",
|
|
271
|
+
pending: null,
|
|
272
|
+
updatedAt: Date.now()
|
|
273
|
+
});
|
|
274
|
+
return {
|
|
275
|
+
get: (id) => records.get(id),
|
|
276
|
+
list: () => [...records.values()],
|
|
277
|
+
subscribe: (listener) => {
|
|
278
|
+
listeners.add(listener);
|
|
279
|
+
return () => {
|
|
280
|
+
listeners.delete(listener);
|
|
281
|
+
};
|
|
282
|
+
},
|
|
283
|
+
receive(incoming) {
|
|
284
|
+
const current = records.get(incoming.id);
|
|
285
|
+
if (!current) {
|
|
286
|
+
put(fresh2(incoming));
|
|
287
|
+
return "created";
|
|
288
|
+
}
|
|
289
|
+
const key = incomingKey(incoming);
|
|
290
|
+
if (current.lastIncomingKey === key) return "unchanged";
|
|
291
|
+
const userEditedBody = _chunkG6YPSE6Jcjs.docKey.call(void 0, current.json) !== _chunkG6YPSE6Jcjs.docKey.call(void 0, current.baseJson);
|
|
292
|
+
const userEditedTitle = current.title !== current.baseTitle;
|
|
293
|
+
const title = userEditedTitle ? current.title : incoming.title;
|
|
294
|
+
const meta = {
|
|
295
|
+
documentType: _nullishCoalesce(incoming.documentType, () => ( current.documentType)),
|
|
296
|
+
status: _nullishCoalesce(incoming.status, () => ( current.status))
|
|
297
|
+
};
|
|
298
|
+
if (!userEditedBody) {
|
|
299
|
+
put({
|
|
300
|
+
...current,
|
|
301
|
+
...meta,
|
|
302
|
+
title,
|
|
303
|
+
baseTitle: incoming.title,
|
|
304
|
+
json: incoming.json,
|
|
305
|
+
baseJson: incoming.json,
|
|
306
|
+
lastIncomingKey: key,
|
|
307
|
+
revision: current.revision + 1,
|
|
308
|
+
origin: "external",
|
|
309
|
+
pending: null,
|
|
310
|
+
updatedAt: Date.now()
|
|
311
|
+
});
|
|
312
|
+
return "applied";
|
|
313
|
+
}
|
|
314
|
+
const { doc, conflicts } = mergeDocuments(current.baseJson, current.json, incoming.json);
|
|
315
|
+
if (conflicts === 0) {
|
|
316
|
+
put({
|
|
317
|
+
...current,
|
|
318
|
+
...meta,
|
|
319
|
+
title,
|
|
320
|
+
baseTitle: incoming.title,
|
|
321
|
+
json: doc,
|
|
322
|
+
baseJson: incoming.json,
|
|
323
|
+
lastIncomingKey: key,
|
|
324
|
+
revision: current.revision + 1,
|
|
325
|
+
origin: "external",
|
|
326
|
+
pending: null,
|
|
327
|
+
updatedAt: Date.now()
|
|
328
|
+
});
|
|
329
|
+
return "merged";
|
|
330
|
+
}
|
|
331
|
+
put({
|
|
332
|
+
...current,
|
|
333
|
+
...meta,
|
|
334
|
+
lastIncomingKey: key,
|
|
335
|
+
pending: { json: incoming.json, title: incoming.title, merged: doc, conflicts }
|
|
336
|
+
});
|
|
337
|
+
return "conflict";
|
|
338
|
+
},
|
|
339
|
+
hydrate(doc) {
|
|
340
|
+
put(fresh2(doc));
|
|
341
|
+
},
|
|
342
|
+
edit(id, json, origin) {
|
|
343
|
+
const current = records.get(id);
|
|
344
|
+
if (!current || _chunkG6YPSE6Jcjs.docKey.call(void 0, current.json) === _chunkG6YPSE6Jcjs.docKey.call(void 0, json)) return;
|
|
345
|
+
put({ ...current, json, origin, revision: current.revision + 1, updatedAt: Date.now() });
|
|
346
|
+
},
|
|
347
|
+
setTitle(id, title) {
|
|
348
|
+
const current = records.get(id);
|
|
349
|
+
if (!current || current.title === title) return;
|
|
350
|
+
put({ ...current, title, updatedAt: Date.now() });
|
|
351
|
+
},
|
|
352
|
+
resolve(id, choice) {
|
|
353
|
+
const current = records.get(id);
|
|
354
|
+
const pending = _optionalChain([current, 'optionalAccess', _6 => _6.pending]);
|
|
355
|
+
if (!current || !pending) return;
|
|
356
|
+
if (choice === "keep") {
|
|
357
|
+
put({ ...current, baseJson: pending.json, baseTitle: pending.title, pending: null });
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const json = choice === "merge" ? pending.merged : pending.json;
|
|
361
|
+
const title = choice === "merge" ? current.title : pending.title;
|
|
362
|
+
put({
|
|
363
|
+
...current,
|
|
364
|
+
title,
|
|
365
|
+
json,
|
|
366
|
+
baseJson: pending.json,
|
|
367
|
+
baseTitle: pending.title,
|
|
368
|
+
revision: current.revision + 1,
|
|
369
|
+
origin: "external",
|
|
370
|
+
pending: null,
|
|
371
|
+
updatedAt: Date.now()
|
|
372
|
+
});
|
|
373
|
+
},
|
|
374
|
+
remove(id) {
|
|
375
|
+
if (records.delete(id)) emit();
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
var echoes = /* @__PURE__ */ new WeakMap();
|
|
380
|
+
var ECHO_MEMORY = 8;
|
|
381
|
+
function rememberDocumentEcho(store, id, content) {
|
|
382
|
+
let byId = echoes.get(store);
|
|
383
|
+
if (!byId) echoes.set(store, byId = /* @__PURE__ */ new Map());
|
|
384
|
+
const list2 = _nullishCoalesce(byId.get(id), () => ( []));
|
|
385
|
+
list2.push(content);
|
|
386
|
+
if (list2.length > ECHO_MEMORY) list2.shift();
|
|
387
|
+
byId.set(id, list2);
|
|
388
|
+
}
|
|
389
|
+
function isDocumentEcho(store, id, content) {
|
|
390
|
+
return !!content && !!_optionalChain([echoes, 'access', _7 => _7.get, 'call', _8 => _8(store), 'optionalAccess', _9 => _9.get, 'call', _10 => _10(id), 'optionalAccess', _11 => _11.includes, 'call', _12 => _12(content)]);
|
|
391
|
+
}
|
|
392
|
+
var defaultStore = null;
|
|
393
|
+
function getDefaultDocumentStore() {
|
|
394
|
+
if (!defaultStore) defaultStore = createDocumentStore();
|
|
395
|
+
return defaultStore;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/features/document-editor/program-sync.ts
|
|
399
|
+
function readString(src, i) {
|
|
400
|
+
if (src[i] !== '"') return null;
|
|
401
|
+
let j = i + 1;
|
|
402
|
+
let value = "";
|
|
403
|
+
while (j < src.length) {
|
|
404
|
+
const ch = src[j];
|
|
405
|
+
if (ch === '"') return { start: i, end: j + 1, value };
|
|
406
|
+
if (ch === "\\") {
|
|
407
|
+
const next = src[j + 1];
|
|
408
|
+
if (next === void 0) return null;
|
|
409
|
+
if (next === "u" && /^[0-9a-fA-F]{4}$/.test(src.slice(j + 2, j + 6))) {
|
|
410
|
+
value += String.fromCharCode(parseInt(src.slice(j + 2, j + 6), 16));
|
|
411
|
+
j += 6;
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
value += next === "n" ? "\n" : next === "t" ? " " : next === "r" ? "\r" : next === "b" ? "\b" : next === "f" ? "\f" : next;
|
|
415
|
+
j += 2;
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
value += ch;
|
|
419
|
+
j++;
|
|
420
|
+
}
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
var skipSpace = (src, i) => {
|
|
424
|
+
while (i < src.length && /\s/.test(src[i])) i++;
|
|
425
|
+
return i;
|
|
426
|
+
};
|
|
427
|
+
var toLangString = (value) => JSON.stringify(value);
|
|
428
|
+
function updateDocumentInProgram(program, documentId, update, componentName = "DocumentEditor") {
|
|
429
|
+
const re = new RegExp(`\\b${componentName}\\s*\\(`, "g");
|
|
430
|
+
let match;
|
|
431
|
+
while (match = re.exec(program)) {
|
|
432
|
+
const args = [];
|
|
433
|
+
let i = match.index + match[0].length;
|
|
434
|
+
for (let n = 0; n < 3; n++) {
|
|
435
|
+
i = skipSpace(program, i);
|
|
436
|
+
const tok = readString(program, i);
|
|
437
|
+
if (!tok) break;
|
|
438
|
+
args.push(tok);
|
|
439
|
+
i = skipSpace(program, tok.end);
|
|
440
|
+
if (program[i] === ",") i++;
|
|
441
|
+
else break;
|
|
442
|
+
}
|
|
443
|
+
if (_optionalChain([args, 'access', _13 => _13[0], 'optionalAccess', _14 => _14.value]) !== documentId) continue;
|
|
444
|
+
const edits = [];
|
|
445
|
+
if (update.title !== void 0 && args[1] && args[1].value !== update.title) {
|
|
446
|
+
edits.push({ start: args[1].start, end: args[1].end, text: toLangString(update.title) });
|
|
447
|
+
}
|
|
448
|
+
if (update.content !== void 0 && args[2] && args[2].value !== update.content) {
|
|
449
|
+
edits.push({ start: args[2].start, end: args[2].end, text: toLangString(update.content) });
|
|
450
|
+
}
|
|
451
|
+
if (!args[2] && update.content !== void 0) return null;
|
|
452
|
+
let out = program;
|
|
453
|
+
for (const e of edits.sort((a, b) => b.start - a.start)) {
|
|
454
|
+
out = out.slice(0, e.start) + e.text + out.slice(e.end);
|
|
455
|
+
}
|
|
456
|
+
return out;
|
|
457
|
+
}
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
function readDocumentFromProgram(program, documentId, componentName = "DocumentEditor") {
|
|
461
|
+
const re = new RegExp(`\\b${componentName}\\s*\\(`, "g");
|
|
462
|
+
let match;
|
|
463
|
+
while (match = re.exec(program)) {
|
|
464
|
+
const args = [];
|
|
465
|
+
let i = match.index + match[0].length;
|
|
466
|
+
for (let n = 0; n < 3; n++) {
|
|
467
|
+
i = skipSpace(program, i);
|
|
468
|
+
const tok = readString(program, i);
|
|
469
|
+
if (!tok) break;
|
|
470
|
+
args.push(tok);
|
|
471
|
+
i = skipSpace(program, tok.end);
|
|
472
|
+
if (program[i] === ",") i++;
|
|
473
|
+
else break;
|
|
474
|
+
}
|
|
475
|
+
if (_optionalChain([args, 'access', _15 => _15[0], 'optionalAccess', _16 => _16.value]) === documentId && args[1] && args[2]) return { title: args[1].value, content: args[2].value };
|
|
476
|
+
}
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// src/features/document-editor/use-document-bridge.ts
|
|
481
|
+
function useDocumentBridge(options) {
|
|
482
|
+
const { documents, response, onResponseChange, responseChangeDelay = 300, onJuneError } = options;
|
|
483
|
+
const latest = _react.useRef.call(void 0, { onResponseChange, responseChangeDelay, response, onJuneError });
|
|
484
|
+
latest.current = { onResponseChange, responseChangeDelay, response, onJuneError };
|
|
485
|
+
const timersRef = _react.useRef.call(void 0, /* @__PURE__ */ new Map());
|
|
486
|
+
_react.useEffect.call(void 0, () => {
|
|
487
|
+
const timers = timersRef.current;
|
|
488
|
+
return () => {
|
|
489
|
+
timers.forEach(clearTimeout);
|
|
490
|
+
timers.clear();
|
|
491
|
+
};
|
|
492
|
+
}, []);
|
|
493
|
+
const syncProgram = _react.useCallback.call(void 0, async (change, store) => {
|
|
494
|
+
if (!latest.current.onResponseChange || typeof latest.current.response !== "string") return;
|
|
495
|
+
const record = store.get(change.documentId);
|
|
496
|
+
const title = _nullishCoalesce(_optionalChain([record, 'optionalAccess', _17 => _17.title]), () => ( change.title));
|
|
497
|
+
const json = _nullishCoalesce(_optionalChain([record, 'optionalAccess', _18 => _18.json]), () => ( change.json));
|
|
498
|
+
try {
|
|
499
|
+
const { docToMarkdown: docToMarkdown2 } = await Promise.resolve().then(() => _interopRequireWildcard(require("./document-engine-GXZH2LNW.cjs")));
|
|
500
|
+
const markdown = docToMarkdown2(json);
|
|
501
|
+
const program = latest.current.response;
|
|
502
|
+
if (typeof program !== "string") return;
|
|
503
|
+
const next = updateDocumentInProgram(program, change.documentId, { title, content: markdown });
|
|
504
|
+
if (next === null || next === program) return;
|
|
505
|
+
rememberDocumentEcho(store, change.documentId, markdown);
|
|
506
|
+
_optionalChain([latest, 'access', _19 => _19.current, 'access', _20 => _20.onResponseChange, 'optionalCall', _21 => _21(next, { documentId: change.documentId, title, markdown, source: change.source })]);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
_optionalChain([latest, 'access', _22 => _22.current, 'access', _23 => _23.onJuneError, 'optionalCall', _24 => _24(_chunkG6YPSE6Jcjs.toJuneError.call(void 0, error, "DOCUMENT_ERROR", "Updating the program failed"))]);
|
|
509
|
+
}
|
|
510
|
+
}, []);
|
|
511
|
+
return _react.useMemo.call(void 0,
|
|
512
|
+
() => ({
|
|
513
|
+
...documents,
|
|
514
|
+
onDocumentChange: (change) => {
|
|
515
|
+
_optionalChain([documents, 'optionalAccess', _25 => _25.onDocumentChange, 'optionalCall', _26 => _26(change)]);
|
|
516
|
+
if (!latest.current.onResponseChange || change.source === "ai") return;
|
|
517
|
+
const store = _nullishCoalesce(_optionalChain([documents, 'optionalAccess', _27 => _27.store]), () => ( getDefaultDocumentStore()));
|
|
518
|
+
const timers = timersRef.current;
|
|
519
|
+
const prev = timers.get(change.documentId);
|
|
520
|
+
if (prev) clearTimeout(prev);
|
|
521
|
+
timers.set(
|
|
522
|
+
change.documentId,
|
|
523
|
+
setTimeout(() => {
|
|
524
|
+
timers.delete(change.documentId);
|
|
525
|
+
void syncProgram(change, store);
|
|
526
|
+
}, Math.max(0, latest.current.responseChangeDelay))
|
|
527
|
+
);
|
|
528
|
+
},
|
|
529
|
+
onError: (error) => {
|
|
530
|
+
_optionalChain([documents, 'optionalAccess', _28 => _28.onError, 'optionalCall', _29 => _29(error)]);
|
|
531
|
+
_optionalChain([latest, 'access', _30 => _30.current, 'access', _31 => _31.onJuneError, 'optionalCall', _32 => _32(error)]);
|
|
532
|
+
}
|
|
533
|
+
}),
|
|
534
|
+
[documents, syncProgram]
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// src/features/document-editor/document-bridge.tsx
|
|
539
|
+
|
|
540
|
+
function DocumentBridge({ children, ...options }) {
|
|
541
|
+
const value = useDocumentBridge(options);
|
|
542
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, JuneDocumentContext.Provider, { value, children });
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// src/themes/theme-provider.tsx
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
// src/themes/dark.ts
|
|
549
|
+
var darkTheme = {
|
|
550
|
+
"--june-foreground": "#f3f4f6",
|
|
551
|
+
"--june-muted": "#9ca3af",
|
|
552
|
+
"--june-border": "#1f2937",
|
|
553
|
+
"--june-accent": "#818cf8",
|
|
554
|
+
"--june-danger": "#f87171"
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
// src/themes/light.ts
|
|
558
|
+
var lightTheme = {
|
|
559
|
+
"--june-foreground": "#111827",
|
|
560
|
+
"--june-muted": "#6b7280",
|
|
561
|
+
"--june-border": "#e5e7eb",
|
|
562
|
+
"--june-accent": "#4f46e5",
|
|
563
|
+
"--june-danger": "#b91c1c"
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// src/themes/create-theme.ts
|
|
567
|
+
function isThemeSet(input) {
|
|
568
|
+
if (!input) return false;
|
|
569
|
+
const keys = Object.keys(input);
|
|
570
|
+
return keys.length > 0 && keys.every((k) => (k === "light" || k === "dark") && typeof input[k] === "object");
|
|
571
|
+
}
|
|
572
|
+
function resolveThemeVars(input, mode) {
|
|
573
|
+
if (!input) return void 0;
|
|
574
|
+
return isThemeSet(input) ? input[mode] : input;
|
|
575
|
+
}
|
|
576
|
+
function createTheme(overrides = {}) {
|
|
577
|
+
return {
|
|
578
|
+
light: { ...lightTheme, ...overrides.light },
|
|
579
|
+
dark: { ...darkTheme, ...overrides.dark }
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// src/themes/theme-provider.tsx
|
|
584
|
+
|
|
585
|
+
var QUERY = "(prefers-color-scheme: dark)";
|
|
586
|
+
function subscribe(onChange) {
|
|
587
|
+
if (typeof window === "undefined" || !window.matchMedia) return () => {
|
|
588
|
+
};
|
|
589
|
+
const mq = window.matchMedia(QUERY);
|
|
590
|
+
mq.addEventListener("change", onChange);
|
|
591
|
+
return () => mq.removeEventListener("change", onChange);
|
|
592
|
+
}
|
|
593
|
+
function getSnapshot() {
|
|
594
|
+
if (typeof window === "undefined") return "light";
|
|
595
|
+
return _optionalChain([window, 'access', _33 => _33.matchMedia, 'optionalCall', _34 => _34(QUERY), 'optionalAccess', _35 => _35.matches]) ? "dark" : "light";
|
|
596
|
+
}
|
|
597
|
+
function getServerSnapshot() {
|
|
598
|
+
return "light";
|
|
599
|
+
}
|
|
600
|
+
function useResolvedTheme(mode) {
|
|
601
|
+
const system = _react.useSyncExternalStore.call(void 0, subscribe, getSnapshot, getServerSnapshot);
|
|
602
|
+
return mode === "system" ? system : mode;
|
|
603
|
+
}
|
|
604
|
+
function JuneThemeProvider({ mode, vars, className, style, children }) {
|
|
605
|
+
const explicit = mode !== void 0;
|
|
606
|
+
const resolved = useResolvedTheme(_nullishCoalesce(mode, () => ( "system")));
|
|
607
|
+
const mergedStyle = _react.useMemo.call(void 0, () => {
|
|
608
|
+
const own = resolveThemeVars(vars, resolved);
|
|
609
|
+
if (!explicit && !own && !style) return void 0;
|
|
610
|
+
const base = explicit ? resolved === "dark" ? darkTheme : lightTheme : void 0;
|
|
611
|
+
return {
|
|
612
|
+
...base,
|
|
613
|
+
...own,
|
|
614
|
+
...explicit ? { colorScheme: resolved } : {},
|
|
615
|
+
...style
|
|
616
|
+
};
|
|
617
|
+
}, [explicit, resolved, vars, style]);
|
|
618
|
+
const context = _react.useMemo.call(void 0, () => ({ mode: _nullishCoalesce(mode, () => ( "system")), theme: resolved }), [mode, resolved]);
|
|
619
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, JuneContext.Provider, { value: context, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
620
|
+
"div",
|
|
621
|
+
{
|
|
622
|
+
className: className ? `june-ui-root ${className}` : "june-ui-root",
|
|
623
|
+
"data-june-theme": explicit ? resolved : void 0,
|
|
624
|
+
style: mergedStyle,
|
|
625
|
+
children
|
|
626
|
+
}
|
|
627
|
+
) });
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// src/engine/parser/normalize-program.ts
|
|
631
|
+
var ATTACHABLE = /* @__PURE__ */ new Set([
|
|
632
|
+
"Accordion",
|
|
633
|
+
"AreaChart",
|
|
634
|
+
"BarChart",
|
|
635
|
+
"Buttons",
|
|
636
|
+
"Button",
|
|
637
|
+
"Callout",
|
|
638
|
+
"Card",
|
|
639
|
+
"CardHeader",
|
|
640
|
+
"Carousel",
|
|
641
|
+
"CodeBlock",
|
|
642
|
+
"CompositeCardBlock",
|
|
643
|
+
"ContextCardBlock",
|
|
644
|
+
"DocumentEditor",
|
|
645
|
+
"EditableTable",
|
|
646
|
+
"EntityList",
|
|
647
|
+
"Form",
|
|
648
|
+
"HorizontalBarChart",
|
|
649
|
+
"IconButton",
|
|
650
|
+
"Image",
|
|
651
|
+
"ImageBlock",
|
|
652
|
+
"ImageGallery",
|
|
653
|
+
"InlineHeader",
|
|
654
|
+
"LineChart",
|
|
655
|
+
"ListBlock",
|
|
656
|
+
"MarkDownRenderer",
|
|
657
|
+
"Modal",
|
|
658
|
+
"OverviewCardBlock",
|
|
659
|
+
"PieChart",
|
|
660
|
+
"RadarChart",
|
|
661
|
+
"RadialChart",
|
|
662
|
+
"ScatterChart",
|
|
663
|
+
"Separator",
|
|
664
|
+
"SingleStackedBarChart",
|
|
665
|
+
"SnippetCardBlock",
|
|
666
|
+
"Stack",
|
|
667
|
+
"Steps",
|
|
668
|
+
"Table",
|
|
669
|
+
"Tabs",
|
|
670
|
+
"TagBlock",
|
|
671
|
+
"TextCallout",
|
|
672
|
+
"TextContent",
|
|
673
|
+
"VisualCardBlock"
|
|
674
|
+
]);
|
|
675
|
+
function normalizeProgram(source) {
|
|
676
|
+
const rewritten = rewriteCalls(source);
|
|
677
|
+
return attachOrphans(rewritten);
|
|
678
|
+
}
|
|
679
|
+
function rewriteCalls(source) {
|
|
680
|
+
let out = "";
|
|
681
|
+
let i = 0;
|
|
682
|
+
let quote = false;
|
|
683
|
+
while (i < source.length) {
|
|
684
|
+
const ch = source[i];
|
|
685
|
+
if (quote) {
|
|
686
|
+
out += ch;
|
|
687
|
+
if (ch === "\\") {
|
|
688
|
+
i += 1;
|
|
689
|
+
if (i < source.length) out += source[i];
|
|
690
|
+
} else if (ch === '"') {
|
|
691
|
+
quote = false;
|
|
692
|
+
}
|
|
693
|
+
i += 1;
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
if (ch === '"') {
|
|
697
|
+
quote = true;
|
|
698
|
+
out += ch;
|
|
699
|
+
i += 1;
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
const call = readCall(source, i);
|
|
703
|
+
if (call) {
|
|
704
|
+
const next = rewriteKnownCall(call);
|
|
705
|
+
if (next !== null) {
|
|
706
|
+
out += next;
|
|
707
|
+
i = call.end;
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
out += ch;
|
|
712
|
+
i += 1;
|
|
713
|
+
}
|
|
714
|
+
return out === source ? source : out;
|
|
715
|
+
}
|
|
716
|
+
function rewriteKnownCall(call) {
|
|
717
|
+
if (call.callee === "Grid") {
|
|
718
|
+
const args = call.args.map((arg) => rewriteCalls(arg));
|
|
719
|
+
const children = args.length === 0 ? "[]" : args.length === 1 ? args[0] : null;
|
|
720
|
+
if (children !== null) {
|
|
721
|
+
return `Stack(${children}, "row", "m", "stretch", "start", true)`;
|
|
722
|
+
}
|
|
723
|
+
return `Stack(${args.join(", ")})`;
|
|
724
|
+
}
|
|
725
|
+
if (call.callee === "Card") {
|
|
726
|
+
const args = call.args.map((arg) => rewriteCalls(arg));
|
|
727
|
+
if (args.length >= 2 && isStringLiteral(args[0]) && isStringLiteral(args[1])) {
|
|
728
|
+
const children = `[TextContent(${args[0]}, "small"), TextContent(${args[1]}, "large-heavy")]`;
|
|
729
|
+
return `Card(${[children, ...args.slice(2)].join(", ")})`;
|
|
730
|
+
}
|
|
731
|
+
if (args.some((arg, index) => arg !== call.args[index])) {
|
|
732
|
+
return `Card(${args.join(", ")})`;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return null;
|
|
736
|
+
}
|
|
737
|
+
function readCall(source, index) {
|
|
738
|
+
if (index > 0 && /[A-Za-z0-9_$]/.test(source[index - 1])) return null;
|
|
739
|
+
const match = /^(Grid|Card)\s*\(/.exec(source.slice(index));
|
|
740
|
+
if (!match) return null;
|
|
741
|
+
const open = index + match[0].length - 1;
|
|
742
|
+
const close = findMatchingParen(source, open);
|
|
743
|
+
if (close < 0) return null;
|
|
744
|
+
const args = splitArgs(source.slice(open + 1, close));
|
|
745
|
+
if (!args) return null;
|
|
746
|
+
return { callee: match[1], args, end: close + 1 };
|
|
747
|
+
}
|
|
748
|
+
function findMatchingParen(source, open) {
|
|
749
|
+
let depth = 0;
|
|
750
|
+
let quote = false;
|
|
751
|
+
for (let i = open; i < source.length; i += 1) {
|
|
752
|
+
const ch = source[i];
|
|
753
|
+
if (quote) {
|
|
754
|
+
if (ch === "\\") i += 1;
|
|
755
|
+
else if (ch === '"') quote = false;
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
if (ch === '"') quote = true;
|
|
759
|
+
else if (ch === "(") depth += 1;
|
|
760
|
+
else if (ch === ")") {
|
|
761
|
+
depth -= 1;
|
|
762
|
+
if (depth === 0) return i;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return -1;
|
|
766
|
+
}
|
|
767
|
+
function splitArgs(inner) {
|
|
768
|
+
const args = [];
|
|
769
|
+
let start = 0;
|
|
770
|
+
let depth = 0;
|
|
771
|
+
let quote = false;
|
|
772
|
+
for (let i = 0; i < inner.length; i += 1) {
|
|
773
|
+
const ch = inner[i];
|
|
774
|
+
if (quote) {
|
|
775
|
+
if (ch === "\\") i += 1;
|
|
776
|
+
else if (ch === '"') quote = false;
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
if (ch === '"') quote = true;
|
|
780
|
+
else if (ch === "(" || ch === "[" || ch === "{") depth += 1;
|
|
781
|
+
else if (ch === ")" || ch === "]" || ch === "}") depth -= 1;
|
|
782
|
+
else if (ch === "," && depth === 0) {
|
|
783
|
+
args.push(inner.slice(start, i).trim());
|
|
784
|
+
start = i + 1;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (quote || depth !== 0) return null;
|
|
788
|
+
const last = inner.slice(start).trim();
|
|
789
|
+
if (last) args.push(last);
|
|
790
|
+
return args;
|
|
791
|
+
}
|
|
792
|
+
function isStringLiteral(value) {
|
|
793
|
+
return /^"(?:\\.|[^"\\])*"$/.test(value.trim());
|
|
794
|
+
}
|
|
795
|
+
function attachOrphans(source) {
|
|
796
|
+
let text = source;
|
|
797
|
+
let stmts = parseStatements(text);
|
|
798
|
+
if (!stmts) return source;
|
|
799
|
+
const taken = new Set(stmts.map((stmt) => stmt.name));
|
|
800
|
+
const extraRoots = stmts.filter((stmt) => stmt.name === "root").slice(1);
|
|
801
|
+
if (extraRoots.length > 0) {
|
|
802
|
+
const edits = extraRoots.map((stmt) => {
|
|
803
|
+
const body2 = fresh(taken, "rootBlock");
|
|
804
|
+
const stmtText = text.slice(stmt.start, stmt.end);
|
|
805
|
+
return {
|
|
806
|
+
start: stmt.start,
|
|
807
|
+
end: stmt.end,
|
|
808
|
+
text: stmtText.replace(/^(\s*)root\b/, `$1${body2}`)
|
|
809
|
+
};
|
|
810
|
+
});
|
|
811
|
+
text = applyEdits(text, edits);
|
|
812
|
+
stmts = parseStatements(text);
|
|
813
|
+
if (!stmts) return source;
|
|
814
|
+
}
|
|
815
|
+
const orphans = stmts.filter(
|
|
816
|
+
(stmt) => stmt.name !== "root" && ATTACHABLE.has(_nullishCoalesce(calleeOf(stmt.rhs), () => ( ""))) && !referencedByOther(stmt.name, stmts)
|
|
817
|
+
);
|
|
818
|
+
if (orphans.length === 0) return source;
|
|
819
|
+
const names = orphans.map((stmt) => stmt.name);
|
|
820
|
+
const root = stmts.find((stmt) => stmt.name === "root");
|
|
821
|
+
if (!root) return `root = Stack([${names.join(", ")}])
|
|
822
|
+
${text}`;
|
|
823
|
+
const rawRhs = text.slice(root.rhsStart, root.end);
|
|
824
|
+
const lead = _nullishCoalesce(_optionalChain([/^\s*/, 'access', _36 => _36.exec, 'call', _37 => _37(rawRhs), 'optionalAccess', _38 => _38[0]]), () => ( ""));
|
|
825
|
+
const spliced = spliceFirstArray(rawRhs.trim(), names);
|
|
826
|
+
if (spliced !== null) {
|
|
827
|
+
return text.slice(0, root.rhsStart) + lead + spliced + text.slice(root.end);
|
|
828
|
+
}
|
|
829
|
+
const body = fresh(new Set(stmts.map((stmt) => stmt.name)), "rootBody");
|
|
830
|
+
const wrapped = `root = Stack([${body}, ${names.join(", ")}])
|
|
831
|
+
${body} = ${rawRhs.trim()}`;
|
|
832
|
+
return text.slice(0, root.start) + wrapped + text.slice(root.end);
|
|
833
|
+
}
|
|
834
|
+
function spliceFirstArray(rhs, names) {
|
|
835
|
+
const match = /^([A-Za-z_]\w*)\s*\(/.exec(rhs);
|
|
836
|
+
if (!match || match[1] !== "Stack" && match[1] !== "Card") return null;
|
|
837
|
+
const openParen = match[0].length - 1;
|
|
838
|
+
let paren = 0;
|
|
839
|
+
let quote = false;
|
|
840
|
+
let arrayStart = -1;
|
|
841
|
+
let arrayDepth = 0;
|
|
842
|
+
let arrayEnd = -1;
|
|
843
|
+
for (let i = openParen; i < rhs.length; i += 1) {
|
|
844
|
+
const ch = rhs[i];
|
|
845
|
+
if (quote) {
|
|
846
|
+
if (ch === "\\") i += 1;
|
|
847
|
+
else if (ch === '"') quote = false;
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
if (ch === '"') quote = true;
|
|
851
|
+
else if (ch === "(") paren += 1;
|
|
852
|
+
else if (ch === ")") {
|
|
853
|
+
paren -= 1;
|
|
854
|
+
if (paren === 0) break;
|
|
855
|
+
} else if (ch === "[") {
|
|
856
|
+
if (paren === 1 && arrayStart < 0) {
|
|
857
|
+
arrayStart = i;
|
|
858
|
+
arrayDepth = 1;
|
|
859
|
+
} else if (arrayStart >= 0 && arrayEnd < 0) {
|
|
860
|
+
arrayDepth += 1;
|
|
861
|
+
}
|
|
862
|
+
} else if (ch === "]" && arrayStart >= 0 && arrayEnd < 0) {
|
|
863
|
+
arrayDepth -= 1;
|
|
864
|
+
if (arrayDepth === 0) arrayEnd = i;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (arrayStart < 0 || arrayEnd < 0) return null;
|
|
868
|
+
const inner = rhs.slice(arrayStart + 1, arrayEnd).trim();
|
|
869
|
+
const merged = inner === "" ? names.join(", ") : `${inner.replace(/,\s*$/, "")}, ${names.join(", ")}`;
|
|
870
|
+
return rhs.slice(0, arrayStart + 1) + merged + rhs.slice(arrayEnd);
|
|
871
|
+
}
|
|
872
|
+
function referencedByOther(name, stmts) {
|
|
873
|
+
const pattern = new RegExp(
|
|
874
|
+
`(?<![A-Za-z0-9_$.])${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_])`
|
|
875
|
+
);
|
|
876
|
+
return stmts.some((stmt) => stmt.name !== name && pattern.test(stripStrings(stmt.rhs)));
|
|
877
|
+
}
|
|
878
|
+
function calleeOf(rhs) {
|
|
879
|
+
const match = /^([A-Za-z_]\w*)\s*\(/.exec(rhs.trim());
|
|
880
|
+
return match ? match[1] : null;
|
|
881
|
+
}
|
|
882
|
+
function stripStrings(text) {
|
|
883
|
+
return text.replace(/"(?:\\.|[^"\\])*"/g, '""');
|
|
884
|
+
}
|
|
885
|
+
function fresh(taken, base) {
|
|
886
|
+
let name = base;
|
|
887
|
+
let n = 2;
|
|
888
|
+
while (taken.has(name)) {
|
|
889
|
+
name = `${base}${n}`;
|
|
890
|
+
n += 1;
|
|
891
|
+
}
|
|
892
|
+
taken.add(name);
|
|
893
|
+
return name;
|
|
894
|
+
}
|
|
895
|
+
function applyEdits(source, edits) {
|
|
896
|
+
const ordered = [...edits].sort((a, b) => b.start - a.start);
|
|
897
|
+
let text = source;
|
|
898
|
+
for (const edit of ordered) {
|
|
899
|
+
text = text.slice(0, edit.start) + edit.text + text.slice(edit.end);
|
|
900
|
+
}
|
|
901
|
+
return text;
|
|
902
|
+
}
|
|
903
|
+
function parseStatements(source) {
|
|
904
|
+
const lines = source.split("\n");
|
|
905
|
+
const stmts = [];
|
|
906
|
+
let offset = 0;
|
|
907
|
+
let depth = 0;
|
|
908
|
+
let quote = false;
|
|
909
|
+
let current = null;
|
|
910
|
+
const scan2 = (line) => {
|
|
911
|
+
for (let i = 0; i < line.length; i += 1) {
|
|
912
|
+
const ch = line[i];
|
|
913
|
+
if (quote) {
|
|
914
|
+
if (ch === "\\") i += 1;
|
|
915
|
+
else if (ch === '"') quote = false;
|
|
916
|
+
} else if (ch === '"') {
|
|
917
|
+
quote = true;
|
|
918
|
+
} else if (ch === "(" || ch === "[" || ch === "{") {
|
|
919
|
+
depth += 1;
|
|
920
|
+
} else if (ch === ")" || ch === "]" || ch === "}") {
|
|
921
|
+
depth = Math.max(0, depth - 1);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
for (let li = 0; li < lines.length; li += 1) {
|
|
926
|
+
const line = lines[li];
|
|
927
|
+
const lineStart = offset;
|
|
928
|
+
const lineEnd = offset + line.length;
|
|
929
|
+
offset = lineEnd + (li < lines.length - 1 ? 1 : 0);
|
|
930
|
+
if (current && depth > 0) {
|
|
931
|
+
scan2(line);
|
|
932
|
+
if (depth === 0) {
|
|
933
|
+
stmts.push({
|
|
934
|
+
name: current.name,
|
|
935
|
+
rhs: source.slice(current.rhsStart, lineEnd).trim(),
|
|
936
|
+
start: current.start,
|
|
937
|
+
end: lineEnd,
|
|
938
|
+
rhsStart: current.rhsStart
|
|
939
|
+
});
|
|
940
|
+
current = null;
|
|
941
|
+
}
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
const trimmed = line.trim();
|
|
945
|
+
if (trimmed === "" || trimmed.startsWith("```")) continue;
|
|
946
|
+
const match = /^(\s*)(\$?[A-Za-z_]\w*)\s*=/.exec(line);
|
|
947
|
+
if (!match) return null;
|
|
948
|
+
const eq = line.indexOf("=");
|
|
949
|
+
const rhsStart = lineStart + eq + 1;
|
|
950
|
+
depth = 0;
|
|
951
|
+
quote = false;
|
|
952
|
+
scan2(line);
|
|
953
|
+
if (depth === 0) {
|
|
954
|
+
stmts.push({
|
|
955
|
+
name: match[2],
|
|
956
|
+
rhs: line.slice(eq + 1).trim(),
|
|
957
|
+
start: lineStart,
|
|
958
|
+
end: lineEnd,
|
|
959
|
+
rhsStart
|
|
960
|
+
});
|
|
961
|
+
} else {
|
|
962
|
+
current = { name: match[2], start: lineStart, rhsStart };
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
if (current || depth !== 0) return null;
|
|
966
|
+
return stmts;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// src/engine/parser/split-response.ts
|
|
970
|
+
var STATEMENT_START = /^\$?[A-Za-z_]\w*\s*=/;
|
|
971
|
+
var PENDING_IDENTIFIER = /^\s*\$?[A-Za-z_]\w*\s*$/;
|
|
972
|
+
var FENCE = /^(`{3,})\s*([\w-]*)/;
|
|
973
|
+
var OPENUI_FENCE_LANGS = /* @__PURE__ */ new Set(["", "openui", "openui-lang", "openuilang"]);
|
|
974
|
+
function scan(line, state) {
|
|
975
|
+
for (let i = 0; i < line.length; i++) {
|
|
976
|
+
const ch = line[i];
|
|
977
|
+
if (state.quote) {
|
|
978
|
+
if (ch === "\\") i++;
|
|
979
|
+
else if (ch === state.quote) state.quote = null;
|
|
980
|
+
} else if (ch === '"') {
|
|
981
|
+
state.quote = ch;
|
|
982
|
+
} else if (ch === "(" || ch === "[" || ch === "{") {
|
|
983
|
+
state.depth++;
|
|
984
|
+
} else if (ch === ")" || ch === "]" || ch === "}") {
|
|
985
|
+
state.depth = Math.max(0, state.depth - 1);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
state.quote = null;
|
|
989
|
+
}
|
|
990
|
+
function splitResponse(source, options = {}) {
|
|
991
|
+
const markdown = options.markdown === true;
|
|
992
|
+
let input = source;
|
|
993
|
+
if (options.streaming && !input.endsWith("\n")) {
|
|
994
|
+
const cut = input.lastIndexOf("\n");
|
|
995
|
+
if (PENDING_IDENTIFIER.test(input.slice(cut + 1))) input = input.slice(0, cut + 1);
|
|
996
|
+
}
|
|
997
|
+
const before = [];
|
|
998
|
+
const after = [];
|
|
999
|
+
const ui = [];
|
|
1000
|
+
const state = { depth: 0, quote: null };
|
|
1001
|
+
let text = [];
|
|
1002
|
+
let seenUi = false;
|
|
1003
|
+
let sawFence = false;
|
|
1004
|
+
let codeFence = null;
|
|
1005
|
+
const flushText = () => {
|
|
1006
|
+
const joined = (markdown ? text.join("\n") : text.join(" ")).trim();
|
|
1007
|
+
text = [];
|
|
1008
|
+
if (joined) (seenUi ? after : before).push(joined);
|
|
1009
|
+
};
|
|
1010
|
+
for (const line of input.split(/\r?\n/)) {
|
|
1011
|
+
const trimmed = line.trim();
|
|
1012
|
+
if (codeFence !== null) {
|
|
1013
|
+
text.push(line);
|
|
1014
|
+
if (trimmed.startsWith(codeFence)) {
|
|
1015
|
+
codeFence = null;
|
|
1016
|
+
flushText();
|
|
1017
|
+
}
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
const fence = FENCE.exec(trimmed);
|
|
1021
|
+
if (fence) {
|
|
1022
|
+
sawFence = true;
|
|
1023
|
+
if (markdown && state.depth === 0 && !OPENUI_FENCE_LANGS.has(fence[2].toLowerCase())) {
|
|
1024
|
+
flushText();
|
|
1025
|
+
codeFence = fence[1];
|
|
1026
|
+
text.push(line);
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
if (state.depth > 0) {
|
|
1032
|
+
ui.push(line);
|
|
1033
|
+
scan(line, state);
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
if (STATEMENT_START.test(trimmed)) {
|
|
1037
|
+
flushText();
|
|
1038
|
+
seenUi = true;
|
|
1039
|
+
ui.push(line);
|
|
1040
|
+
scan(line, state);
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
if (trimmed === "") {
|
|
1044
|
+
flushText();
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
text.push(markdown ? line.trimEnd() : trimmed);
|
|
1048
|
+
}
|
|
1049
|
+
flushText();
|
|
1050
|
+
return {
|
|
1051
|
+
before,
|
|
1052
|
+
ui: ui.join("\n"),
|
|
1053
|
+
after,
|
|
1054
|
+
pure: !sawFence && before.length === 0 && after.length === 0
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// src/engine/parser/mixed-content.ts
|
|
1059
|
+
function resolveMixedContent(response, options = {}) {
|
|
1060
|
+
const { mixedContent = true, isStreaming = false, repair = true, markdown = false } = options;
|
|
1061
|
+
const parts = mixedContent && response ? splitResponse(response, { streaming: isStreaming, markdown }) : null;
|
|
1062
|
+
const raw = parts && !parts.pure ? parts.ui : response;
|
|
1063
|
+
const ui = !repair || !mixedContent || isStreaming || typeof raw !== "string" || raw.length === 0 ? raw : normalizeProgram(raw);
|
|
1064
|
+
const before = _nullishCoalesce(_optionalChain([parts, 'optionalAccess', _39 => _39.before]), () => ( []));
|
|
1065
|
+
const after = _nullishCoalesce(_optionalChain([parts, 'optionalAccess', _40 => _40.after]), () => ( []));
|
|
1066
|
+
return {
|
|
1067
|
+
before,
|
|
1068
|
+
ui,
|
|
1069
|
+
after,
|
|
1070
|
+
hasUi: typeof ui === "string" && ui.trim().length > 0,
|
|
1071
|
+
hasText: before.length + after.length > 0
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// src/engine/renderer/error-boundary.tsx
|
|
1076
|
+
|
|
1077
|
+
|
|
1078
|
+
function keysChanged(a = [], b = []) {
|
|
1079
|
+
return a.length !== b.length || a.some((v, i) => !Object.is(v, b[i]));
|
|
1080
|
+
}
|
|
1081
|
+
var JuneErrorBoundary = class extends _react.Component {
|
|
1082
|
+
constructor() {
|
|
1083
|
+
super(...arguments);
|
|
1084
|
+
this.state = { error: null };
|
|
1085
|
+
this.reset = () => {
|
|
1086
|
+
this.setState({ error: null });
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
static getDerivedStateFromError(error) {
|
|
1090
|
+
return { error };
|
|
1091
|
+
}
|
|
1092
|
+
componentDidCatch(error, info) {
|
|
1093
|
+
_optionalChain([this, 'access', _41 => _41.props, 'access', _42 => _42.onError, 'optionalCall', _43 => _43(error, info)]);
|
|
1094
|
+
}
|
|
1095
|
+
componentDidUpdate(prev) {
|
|
1096
|
+
if (this.state.error && keysChanged(prev.resetKeys, this.props.resetKeys)) {
|
|
1097
|
+
this.reset();
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
render() {
|
|
1101
|
+
const { error } = this.state;
|
|
1102
|
+
if (!error) return this.props.children;
|
|
1103
|
+
const { fallback } = this.props;
|
|
1104
|
+
if (typeof fallback === "function") return fallback({ error, reset: this.reset });
|
|
1105
|
+
if (fallback !== void 0) return fallback;
|
|
1106
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { role: "alert", "data-june-error": "", children: "Something went wrong while rendering this UI." });
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
// src/engine/renderer/markdown-text.tsx
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
var MarkDownRenderer = _react.lazy.call(void 0,
|
|
1114
|
+
() => Promise.resolve().then(() => _interopRequireWildcard(require("@openuidev/react-ui"))).then((m) => ({ default: m.MarkDownRenderer }))
|
|
1115
|
+
);
|
|
1116
|
+
function MarkdownText({ text, className }) {
|
|
1117
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: className ? `june-markdown ${className}` : "june-markdown", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _react.Suspense, { fallback: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "june-text", children: text }), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, MarkDownRenderer, { textMarkdown: text, variant: "clear" }) }) });
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// src/engine/renderer/june-renderer.tsx
|
|
1121
|
+
|
|
1122
|
+
function JuneRenderer(props) {
|
|
1123
|
+
const {
|
|
1124
|
+
library,
|
|
1125
|
+
response,
|
|
1126
|
+
isStreaming = false,
|
|
1127
|
+
mixedContent = true,
|
|
1128
|
+
textFormat = "plain",
|
|
1129
|
+
renderText,
|
|
1130
|
+
repair = true,
|
|
1131
|
+
theme,
|
|
1132
|
+
themeVars,
|
|
1133
|
+
className,
|
|
1134
|
+
style,
|
|
1135
|
+
actions,
|
|
1136
|
+
confirmAction,
|
|
1137
|
+
parseActionEvent,
|
|
1138
|
+
onAction,
|
|
1139
|
+
onError,
|
|
1140
|
+
onJuneError,
|
|
1141
|
+
documents,
|
|
1142
|
+
onResponseChange,
|
|
1143
|
+
responseChangeDelay,
|
|
1144
|
+
loadingFallback = null,
|
|
1145
|
+
emptyFallback = null,
|
|
1146
|
+
errorFallback,
|
|
1147
|
+
resetKeys,
|
|
1148
|
+
...rendererProps
|
|
1149
|
+
} = props;
|
|
1150
|
+
const latest = _react.useRef.call(void 0, { actions, confirmAction, parseActionEvent, onAction, onError, onJuneError });
|
|
1151
|
+
latest.current = { actions, confirmAction, parseActionEvent, onAction, onError, onJuneError };
|
|
1152
|
+
const handleAction = _react.useCallback.call(void 0, (event) => {
|
|
1153
|
+
const l = latest.current;
|
|
1154
|
+
_optionalChain([l, 'access', _44 => _44.onAction, 'optionalCall', _45 => _45(event)]);
|
|
1155
|
+
if (l.actions) {
|
|
1156
|
+
void runAction(event, {
|
|
1157
|
+
actions: l.actions,
|
|
1158
|
+
parseEvent: l.parseActionEvent,
|
|
1159
|
+
confirm: l.confirmAction,
|
|
1160
|
+
onError: (e) => _optionalChain([l, 'access', _46 => _46.onJuneError, 'optionalCall', _47 => _47(e)])
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
}, []);
|
|
1164
|
+
const handleParserErrors = _react.useCallback.call(void 0, (errors) => {
|
|
1165
|
+
const list2 = Array.isArray(errors) ? errors : [];
|
|
1166
|
+
const l = latest.current;
|
|
1167
|
+
_optionalChain([l, 'access', _48 => _48.onError, 'optionalCall', _49 => _49(list2)]);
|
|
1168
|
+
const real = list2.filter((e) => _optionalChain([e, 'optionalAccess', _50 => _50.severity]) !== "warning");
|
|
1169
|
+
if (real.length > 0) {
|
|
1170
|
+
_optionalChain([l, 'access', _51 => _51.onJuneError, 'optionalCall', _52 => _52(new (0, _chunkG6YPSE6Jcjs.JuneError)("PARSE_ERROR", `OpenUI reported ${real.length} error(s)`, { details: real }))]);
|
|
1171
|
+
}
|
|
1172
|
+
if (list2.length > 0 && !l.onError && !l.onJuneError) {
|
|
1173
|
+
console.warn("[june-ui] OpenUI reported errors:", list2);
|
|
1174
|
+
}
|
|
1175
|
+
}, []);
|
|
1176
|
+
const handleBoundaryError = _react.useCallback.call(void 0, (error, info) => {
|
|
1177
|
+
_optionalChain([latest, 'access', _53 => _53.current, 'access', _54 => _54.onJuneError, 'optionalCall', _55 => _55(
|
|
1178
|
+
new (0, _chunkG6YPSE6Jcjs.JuneError)("RENDER_ERROR", error.message, {
|
|
1179
|
+
cause: error,
|
|
1180
|
+
details: { componentStack: info.componentStack }
|
|
1181
|
+
})
|
|
1182
|
+
)]);
|
|
1183
|
+
}, []);
|
|
1184
|
+
const handleDocumentError = _react.useCallback.call(void 0, (error) => _optionalChain([latest, 'access', _56 => _56.current, 'access', _57 => _57.onJuneError, 'optionalCall', _58 => _58(error)]), []);
|
|
1185
|
+
const runtime = _react.useMemo.call(void 0, () => ({ isStreaming }), [isStreaming]);
|
|
1186
|
+
const content = _react.useMemo.call(void 0,
|
|
1187
|
+
() => resolveMixedContent(response, { mixedContent, isStreaming, repair, markdown: textFormat === "markdown" }),
|
|
1188
|
+
[response, mixedContent, isStreaming, repair, textFormat]
|
|
1189
|
+
);
|
|
1190
|
+
const { before, after, ui, hasUi, hasText } = content;
|
|
1191
|
+
const text = (value, index, key) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _react.Fragment, { children: renderText ? renderText(value, index) : textFormat === "markdown" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, MarkdownText, { text: value }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "june-text", children: value }) }, key);
|
|
1192
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, JuneThemeProvider, { mode: theme, vars: themeVars, className, style, children: [
|
|
1193
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, OpenUIDevtoolsGate, {}),
|
|
1194
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, JuneRuntimeContext.Provider, { value: runtime, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1195
|
+
DocumentBridge,
|
|
1196
|
+
{
|
|
1197
|
+
documents,
|
|
1198
|
+
response,
|
|
1199
|
+
onResponseChange,
|
|
1200
|
+
responseChangeDelay,
|
|
1201
|
+
onJuneError: handleDocumentError,
|
|
1202
|
+
children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, JuneErrorBoundary, { fallback: errorFallback, onError: handleBoundaryError, resetKeys: _nullishCoalesce(resetKeys, () => ( [response])), children: [
|
|
1203
|
+
before.map((t, i) => text(t, i, `b${i}`)),
|
|
1204
|
+
hasUi && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1205
|
+
_reactlang.Renderer,
|
|
1206
|
+
{
|
|
1207
|
+
...rendererProps,
|
|
1208
|
+
library,
|
|
1209
|
+
response: ui,
|
|
1210
|
+
isStreaming,
|
|
1211
|
+
onAction: handleAction,
|
|
1212
|
+
onError: handleParserErrors
|
|
1213
|
+
}
|
|
1214
|
+
),
|
|
1215
|
+
after.map((t, i) => text(t, before.length + i, `a${i}`)),
|
|
1216
|
+
!hasUi && !hasText && (isStreaming ? loadingFallback : emptyFallback)
|
|
1217
|
+
] })
|
|
1218
|
+
}
|
|
1219
|
+
) })
|
|
1220
|
+
] });
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
// src/engine/registry/define-component.ts
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
|
|
1228
|
+
var NAME = /^[A-Z][A-Za-z0-9_]*$/;
|
|
1229
|
+
function isZodObject(value) {
|
|
1230
|
+
const def = _optionalChain([value, 'optionalAccess', _59 => _59._zod, 'optionalAccess', _60 => _60.def]);
|
|
1231
|
+
return _optionalChain([def, 'optionalAccess', _61 => _61.type]) === "object";
|
|
1232
|
+
}
|
|
1233
|
+
function validateComponentDefinition(definition) {
|
|
1234
|
+
const { name } = definition;
|
|
1235
|
+
if (typeof name !== "string" || !NAME.test(name)) {
|
|
1236
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)(
|
|
1237
|
+
"LIBRARY_ERROR",
|
|
1238
|
+
`Component name ${JSON.stringify(name)} is invalid. Use PascalCase letters, digits and "_" (for example "CustomCard").`
|
|
1239
|
+
);
|
|
1240
|
+
}
|
|
1241
|
+
if (typeof definition.description !== "string" || definition.description.trim() === "") {
|
|
1242
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)(
|
|
1243
|
+
"LIBRARY_ERROR",
|
|
1244
|
+
`Component "${name}" needs a description. The model reads it to decide when to use the component.`
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
if (!isZodObject(definition.schema)) {
|
|
1248
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)(
|
|
1249
|
+
"LIBRARY_ERROR",
|
|
1250
|
+
`Component "${name}" needs a Zod 4 object schema, e.g. z.object({ ... }). On Zod 3.25 import z from "zod/v4".`
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
const hasComponent = definition.component !== void 0;
|
|
1254
|
+
const hasRender = definition.render !== void 0;
|
|
1255
|
+
if (hasComponent === hasRender) {
|
|
1256
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)(
|
|
1257
|
+
"LIBRARY_ERROR",
|
|
1258
|
+
`Component "${name}" needs exactly one of "component" (a React component taking its props) or "render" (OpenUI renderer props).`
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
if (hasRender && typeof definition.render !== "function") {
|
|
1262
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", `Component "${name}": "render" must be a function.`);
|
|
1263
|
+
}
|
|
1264
|
+
if (hasComponent && typeof definition.component !== "function" && typeof definition.component !== "object") {
|
|
1265
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", `Component "${name}": "component" must be a React component.`);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
function defineComponent(options) {
|
|
1269
|
+
validateComponentDefinition(options);
|
|
1270
|
+
const { name, description, schema, slots, metadata } = options;
|
|
1271
|
+
let renderer;
|
|
1272
|
+
if (options.render) {
|
|
1273
|
+
const render = options.render;
|
|
1274
|
+
renderer = (args) => render(args);
|
|
1275
|
+
} else {
|
|
1276
|
+
const Component2 = options.component;
|
|
1277
|
+
renderer = ({ props }) => _react.createElement.call(void 0, Component2, props);
|
|
1278
|
+
}
|
|
1279
|
+
renderer.displayName = `June(${name})`;
|
|
1280
|
+
const defined = _reactlang.defineComponent.call(void 0, { name, description, props: schema, component: renderer });
|
|
1281
|
+
const june = Object.freeze({
|
|
1282
|
+
...slots ? { slots: Object.freeze([...slots]) } : {},
|
|
1283
|
+
...metadata ? { metadata } : {}
|
|
1284
|
+
});
|
|
1285
|
+
return Object.assign(defined, { june });
|
|
1286
|
+
}
|
|
1287
|
+
function isJuneComponent(value) {
|
|
1288
|
+
return typeof value === "object" && value !== null && "june" in value && "component" in value;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// src/engine/registry/component-registry.ts
|
|
1292
|
+
|
|
1293
|
+
function nameOf(component) {
|
|
1294
|
+
const name = _optionalChain([component, 'optionalAccess', _62 => _62.name]);
|
|
1295
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
1296
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", "Every component needs a non-empty string name");
|
|
1297
|
+
}
|
|
1298
|
+
return name;
|
|
1299
|
+
}
|
|
1300
|
+
function slotsOf(component) {
|
|
1301
|
+
return isJuneComponent(component) ? component.june.slots : void 0;
|
|
1302
|
+
}
|
|
1303
|
+
var isDev = () => typeof process !== "undefined" && _optionalChain([process, 'access', _63 => _63.env, 'optionalAccess', _64 => _64.NODE_ENV]) === "development";
|
|
1304
|
+
var ComponentRegistry = class {
|
|
1305
|
+
constructor(options = {}) {
|
|
1306
|
+
this.added = /* @__PURE__ */ new Map();
|
|
1307
|
+
this.overridden = /* @__PURE__ */ new Map();
|
|
1308
|
+
this.removed = /* @__PURE__ */ new Set();
|
|
1309
|
+
this.cached = null;
|
|
1310
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
1311
|
+
/** For `useSyncExternalStore`: called after every change. */
|
|
1312
|
+
this.subscribe = (listener) => {
|
|
1313
|
+
this.listeners.add(listener);
|
|
1314
|
+
return () => {
|
|
1315
|
+
this.listeners.delete(listener);
|
|
1316
|
+
};
|
|
1317
|
+
};
|
|
1318
|
+
this.getSnapshot = () => this.library;
|
|
1319
|
+
this.base = options.base;
|
|
1320
|
+
this.root = options.root;
|
|
1321
|
+
if (options.base && typeof options.base.extend !== "function") {
|
|
1322
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", "The base library has no extend(); create it with OpenUI's createLibrary()");
|
|
1323
|
+
}
|
|
1324
|
+
this.transaction(() => {
|
|
1325
|
+
for (const name of _nullishCoalesce(options.remove, () => ( []))) this.applyRemove(name);
|
|
1326
|
+
for (const c of _nullishCoalesce(options.components, () => ( []))) this.applyRegister(c, false);
|
|
1327
|
+
for (const c of _nullishCoalesce(options.overrides, () => ( []))) this.applyRegister(c, true);
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
/** The OpenUI library for the current set of components. Stable until the registry changes. */
|
|
1331
|
+
get library() {
|
|
1332
|
+
if (!this.cached) this.cached = this.build();
|
|
1333
|
+
return this.cached;
|
|
1334
|
+
}
|
|
1335
|
+
/** Adds one or more components. Set `override: true` to replace an existing name. */
|
|
1336
|
+
register(components, options = {}) {
|
|
1337
|
+
const list2 = Array.isArray(components) ? components : [components];
|
|
1338
|
+
this.transaction(() => {
|
|
1339
|
+
for (const c of list2) this.applyRegister(c, options.override === true);
|
|
1340
|
+
});
|
|
1341
|
+
return this;
|
|
1342
|
+
}
|
|
1343
|
+
/** Replaces existing components by name (same as `register(c, { override: true })`). */
|
|
1344
|
+
override(components) {
|
|
1345
|
+
return this.register(components, { override: true });
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Removes a component. A registered component is dropped; an overridden built-in goes back to
|
|
1349
|
+
* the original; a built-in is removed from the library.
|
|
1350
|
+
*/
|
|
1351
|
+
unregister(name) {
|
|
1352
|
+
this.transaction(() => this.applyRemove(name));
|
|
1353
|
+
return this;
|
|
1354
|
+
}
|
|
1355
|
+
/** Drops every change and returns to the base library. */
|
|
1356
|
+
reset() {
|
|
1357
|
+
if (this.added.size + this.overridden.size + this.removed.size === 0) return this;
|
|
1358
|
+
this.added.clear();
|
|
1359
|
+
this.overridden.clear();
|
|
1360
|
+
this.removed.clear();
|
|
1361
|
+
this.changed();
|
|
1362
|
+
return this;
|
|
1363
|
+
}
|
|
1364
|
+
has(name) {
|
|
1365
|
+
return Object.prototype.hasOwnProperty.call(this.library.components, name);
|
|
1366
|
+
}
|
|
1367
|
+
get(name) {
|
|
1368
|
+
return this.has(name) ? this.library.components[name] : void 0;
|
|
1369
|
+
}
|
|
1370
|
+
/** June metadata (`slots`, `metadata`) of a component, when it was made with June's defineComponent. */
|
|
1371
|
+
getMeta(name) {
|
|
1372
|
+
const c = _nullishCoalesce(this.added.get(name), () => ( this.overridden.get(name)));
|
|
1373
|
+
return c && isJuneComponent(c) ? c.june : void 0;
|
|
1374
|
+
}
|
|
1375
|
+
/** Every component name in the library. */
|
|
1376
|
+
names() {
|
|
1377
|
+
return Object.keys(this.library.components);
|
|
1378
|
+
}
|
|
1379
|
+
/** Names added or overridden through this registry. */
|
|
1380
|
+
customNames() {
|
|
1381
|
+
return [...this.added.keys(), ...this.overridden.keys()];
|
|
1382
|
+
}
|
|
1383
|
+
/** The system prompt for these components (OpenUI's `library.prompt`). Usually called on the server. */
|
|
1384
|
+
prompt(options) {
|
|
1385
|
+
return this.library.prompt(options);
|
|
1386
|
+
}
|
|
1387
|
+
/* ---------------- internals ---------------- */
|
|
1388
|
+
exists(name) {
|
|
1389
|
+
if (this.added.has(name)) return true;
|
|
1390
|
+
if (this.removed.has(name)) return false;
|
|
1391
|
+
return !!this.base && Object.prototype.hasOwnProperty.call(this.base.components, name);
|
|
1392
|
+
}
|
|
1393
|
+
applyRegister(component, override) {
|
|
1394
|
+
const name = nameOf(component);
|
|
1395
|
+
const exists = this.exists(name);
|
|
1396
|
+
if (exists && !override) {
|
|
1397
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)(
|
|
1398
|
+
"LIBRARY_ERROR",
|
|
1399
|
+
`Component "${name}" is already registered. Pass { override: true } (or use overrides) to replace it.`
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
if (!exists && override) {
|
|
1403
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", `Cannot override "${name}": no component with that name. Register it instead.`);
|
|
1404
|
+
}
|
|
1405
|
+
const inBase = !!this.base && Object.prototype.hasOwnProperty.call(this.base.components, name);
|
|
1406
|
+
if (inBase && !this.removed.has(name)) this.overridden.set(name, component);
|
|
1407
|
+
else this.added.set(name, component);
|
|
1408
|
+
}
|
|
1409
|
+
applyRemove(name) {
|
|
1410
|
+
if (this.added.delete(name)) return;
|
|
1411
|
+
if (this.overridden.delete(name)) return;
|
|
1412
|
+
const inBase = !!this.base && Object.prototype.hasOwnProperty.call(this.base.components, name);
|
|
1413
|
+
if (!inBase || this.removed.has(name)) {
|
|
1414
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", `Cannot remove "${name}": no component with that name.`);
|
|
1415
|
+
}
|
|
1416
|
+
if (_optionalChain([this, 'access', _65 => _65.base, 'optionalAccess', _66 => _66.root]) === name) {
|
|
1417
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", `Cannot remove "${name}": it is the root component.`);
|
|
1418
|
+
}
|
|
1419
|
+
this.removed.add(name);
|
|
1420
|
+
}
|
|
1421
|
+
/** Applies changes atomically: on error the previous state is restored and the error rethrown. */
|
|
1422
|
+
transaction(apply) {
|
|
1423
|
+
const snapshot = {
|
|
1424
|
+
added: new Map(this.added),
|
|
1425
|
+
overridden: new Map(this.overridden),
|
|
1426
|
+
removed: new Set(this.removed),
|
|
1427
|
+
cached: this.cached
|
|
1428
|
+
};
|
|
1429
|
+
try {
|
|
1430
|
+
apply();
|
|
1431
|
+
this.cached = this.build();
|
|
1432
|
+
} catch (error) {
|
|
1433
|
+
this.added = snapshot.added;
|
|
1434
|
+
this.overridden = snapshot.overridden;
|
|
1435
|
+
this.removed = snapshot.removed;
|
|
1436
|
+
this.cached = snapshot.cached;
|
|
1437
|
+
throw error instanceof _chunkG6YPSE6Jcjs.JuneError ? error : new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", "OpenUI rejected the component library", { cause: error });
|
|
1438
|
+
}
|
|
1439
|
+
if (this.cached !== snapshot.cached) this.notify();
|
|
1440
|
+
}
|
|
1441
|
+
changed() {
|
|
1442
|
+
this.cached = null;
|
|
1443
|
+
this.notify();
|
|
1444
|
+
}
|
|
1445
|
+
notify() {
|
|
1446
|
+
this.listeners.forEach((l) => l());
|
|
1447
|
+
}
|
|
1448
|
+
build() {
|
|
1449
|
+
const added = [...this.added.values()];
|
|
1450
|
+
const overrides = [...this.overridden.values()];
|
|
1451
|
+
const remove = [...this.removed];
|
|
1452
|
+
if (!this.base) {
|
|
1453
|
+
return _reactlang.createLibrary.call(void 0, { components: added, root: this.root });
|
|
1454
|
+
}
|
|
1455
|
+
if (added.length + overrides.length + remove.length === 0) return this.base;
|
|
1456
|
+
const base = this.base;
|
|
1457
|
+
const extension = (withSlots) => ({
|
|
1458
|
+
components: {
|
|
1459
|
+
add: added.map((component) => {
|
|
1460
|
+
const slots = withSlots ? slotsOf(component) : void 0;
|
|
1461
|
+
return slots && slots.length > 0 ? { component, slots: [...slots] } : component;
|
|
1462
|
+
}),
|
|
1463
|
+
...overrides.length ? { override: overrides } : {},
|
|
1464
|
+
...remove.length ? { remove } : {}
|
|
1465
|
+
}
|
|
1466
|
+
});
|
|
1467
|
+
try {
|
|
1468
|
+
return base.extend(extension(true));
|
|
1469
|
+
} catch (first) {
|
|
1470
|
+
if (added.some((c) => _optionalChain([slotsOf, 'call', _67 => _67(c), 'optionalAccess', _68 => _68.length]))) {
|
|
1471
|
+
try {
|
|
1472
|
+
const library = base.extend(extension(false));
|
|
1473
|
+
if (isDev()) console.warn("[june-ui] Component slots were skipped (they need Zod 4.1+):", first);
|
|
1474
|
+
return library;
|
|
1475
|
+
} catch (e2) {
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
try {
|
|
1479
|
+
return this.rebuild(added, overrides);
|
|
1480
|
+
} catch (e3) {
|
|
1481
|
+
throw first;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
rebuild(added, overrides) {
|
|
1486
|
+
const base = this.base;
|
|
1487
|
+
const replace = new Map(overrides.map((c) => [nameOf(c), c]));
|
|
1488
|
+
const components = Object.values(base.components).filter((c) => !this.removed.has(c.name)).map((c) => _nullishCoalesce(replace.get(c.name), () => ( c))).concat(added);
|
|
1489
|
+
const componentGroups = _optionalChain([base, 'access', _69 => _69.componentGroups, 'optionalAccess', _70 => _70.map, 'call', _71 => _71((g) => ({ ...g, components: g.components.filter((n) => !this.removed.has(n)) })), 'access', _72 => _72.filter, 'call', _73 => _73((g) => g.components.length > 0)]);
|
|
1490
|
+
return _reactlang.createLibrary.call(void 0, {
|
|
1491
|
+
components,
|
|
1492
|
+
componentGroups,
|
|
1493
|
+
root: base.root,
|
|
1494
|
+
id: base.id,
|
|
1495
|
+
functions: base.functions ? Object.values(base.functions) : void 0,
|
|
1496
|
+
actions: base.actions ? Object.values(base.actions) : void 0
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
};
|
|
1500
|
+
function createComponentRegistry(options = {}) {
|
|
1501
|
+
return new ComponentRegistry(options);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
// src/engine/registry/use-component-registry.ts
|
|
1505
|
+
|
|
1506
|
+
function useRegistryLibrary(registry2) {
|
|
1507
|
+
return _react.useSyncExternalStore.call(void 0, registry2.subscribe, registry2.getSnapshot, registry2.getSnapshot);
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
// src/engine/registry/create-library.ts
|
|
1511
|
+
|
|
1512
|
+
var defineJuneComponent = _reactlang.defineComponent;
|
|
1513
|
+
function createJuneLibrary(options) {
|
|
1514
|
+
const { allow, deny, ...rest } = options;
|
|
1515
|
+
const input = rest;
|
|
1516
|
+
const raw = input;
|
|
1517
|
+
const components = _nullishCoalesce(raw.components, () => ( []));
|
|
1518
|
+
const names = /* @__PURE__ */ new Set();
|
|
1519
|
+
for (const component of components) {
|
|
1520
|
+
if (typeof _optionalChain([component, 'optionalAccess', _74 => _74.name]) !== "string" || component.name.length === 0) {
|
|
1521
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", "Every component needs a non-empty string name");
|
|
1522
|
+
}
|
|
1523
|
+
if (names.has(component.name)) {
|
|
1524
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", `Duplicate component name "${component.name}"`);
|
|
1525
|
+
}
|
|
1526
|
+
names.add(component.name);
|
|
1527
|
+
}
|
|
1528
|
+
const allowSet = allow ? new Set(allow) : null;
|
|
1529
|
+
const denySet = deny ? new Set(deny) : null;
|
|
1530
|
+
if (allowSet) {
|
|
1531
|
+
for (const n of allowSet) {
|
|
1532
|
+
if (!names.has(n)) throw new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", `Allowed component "${n}" is not registered`);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
const keep = components.filter((c) => {
|
|
1536
|
+
const n = c.name;
|
|
1537
|
+
return (!allowSet || allowSet.has(n)) && !(denySet && denySet.has(n));
|
|
1538
|
+
});
|
|
1539
|
+
if (raw.root !== void 0 && !keep.some((c) => c.name === raw.root)) {
|
|
1540
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", `Root component "${raw.root}" is missing after filtering`);
|
|
1541
|
+
}
|
|
1542
|
+
try {
|
|
1543
|
+
const unfiltered = keep.length === components.length;
|
|
1544
|
+
return _reactlang.createLibrary.call(void 0,
|
|
1545
|
+
unfiltered ? input : { ...input, components: keep }
|
|
1546
|
+
);
|
|
1547
|
+
} catch (error) {
|
|
1548
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("LIBRARY_ERROR", "OpenUI rejected the component library", { cause: error });
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
// src/features/actions/define-action.ts
|
|
1553
|
+
function defineJuneAction(definition) {
|
|
1554
|
+
if (typeof definition.handler !== "function") {
|
|
1555
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("ACTION_VALIDATION_ERROR", "defineJuneAction requires a handler function");
|
|
1556
|
+
}
|
|
1557
|
+
return Object.freeze({ ...definition, __june: "action" });
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
// src/features/streaming/use-june-stream.ts
|
|
1561
|
+
|
|
1562
|
+
|
|
1563
|
+
// src/features/streaming/stream-handler.ts
|
|
1564
|
+
function isReadableStream(source) {
|
|
1565
|
+
return typeof source.getReader === "function";
|
|
1566
|
+
}
|
|
1567
|
+
async function* iterateReadable(stream, signal) {
|
|
1568
|
+
const reader = stream.getReader();
|
|
1569
|
+
const onAbort = () => {
|
|
1570
|
+
void reader.cancel().catch(() => {
|
|
1571
|
+
});
|
|
1572
|
+
};
|
|
1573
|
+
_optionalChain([signal, 'optionalAccess', _75 => _75.addEventListener, 'call', _76 => _76("abort", onAbort, { once: true })]);
|
|
1574
|
+
try {
|
|
1575
|
+
while (true) {
|
|
1576
|
+
const { done, value } = await reader.read();
|
|
1577
|
+
if (done) return;
|
|
1578
|
+
yield value;
|
|
1579
|
+
}
|
|
1580
|
+
} finally {
|
|
1581
|
+
_optionalChain([signal, 'optionalAccess', _77 => _77.removeEventListener, 'call', _78 => _78("abort", onAbort)]);
|
|
1582
|
+
try {
|
|
1583
|
+
reader.releaseLock();
|
|
1584
|
+
} catch (e4) {
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
function toAsyncIterable(source, signal) {
|
|
1589
|
+
return isReadableStream(source) ? iterateReadable(source, signal) : source;
|
|
1590
|
+
}
|
|
1591
|
+
function abortedError(partial) {
|
|
1592
|
+
return new (0, _chunkG6YPSE6Jcjs.JuneError)("STREAM_ABORTED", "Stream was aborted", { details: { partial } });
|
|
1593
|
+
}
|
|
1594
|
+
async function consumeStream(source, options = {}) {
|
|
1595
|
+
const { signal, onText, allowEmpty = false } = options;
|
|
1596
|
+
if (_optionalChain([signal, 'optionalAccess', _79 => _79.aborted])) throw abortedError("");
|
|
1597
|
+
const decoder = new TextDecoder();
|
|
1598
|
+
let text = "";
|
|
1599
|
+
let received = false;
|
|
1600
|
+
const push = (delta) => {
|
|
1601
|
+
if (!delta) return;
|
|
1602
|
+
received = true;
|
|
1603
|
+
text += delta;
|
|
1604
|
+
_optionalChain([onText, 'optionalCall', _80 => _80(delta, text)]);
|
|
1605
|
+
};
|
|
1606
|
+
try {
|
|
1607
|
+
for await (const chunk of toAsyncIterable(source, signal)) {
|
|
1608
|
+
if (_optionalChain([signal, 'optionalAccess', _81 => _81.aborted])) break;
|
|
1609
|
+
push(typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true }));
|
|
1610
|
+
}
|
|
1611
|
+
push(decoder.decode());
|
|
1612
|
+
} catch (error) {
|
|
1613
|
+
if (_optionalChain([signal, 'optionalAccess', _82 => _82.aborted])) throw abortedError(text);
|
|
1614
|
+
if (error instanceof _chunkG6YPSE6Jcjs.JuneError) throw error;
|
|
1615
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("STREAM_ERROR", "Stream was interrupted", {
|
|
1616
|
+
cause: error,
|
|
1617
|
+
details: { partial: text }
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
if (_optionalChain([signal, 'optionalAccess', _83 => _83.aborted])) throw abortedError(text);
|
|
1621
|
+
if (!received && !allowEmpty) {
|
|
1622
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("STREAM_EMPTY", "Stream completed without any content");
|
|
1623
|
+
}
|
|
1624
|
+
return text;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
// src/features/streaming/sse-adapter.ts
|
|
1628
|
+
var BOUNDARY = /\r\n\r\n|\n\n|\r\r/;
|
|
1629
|
+
function parseFrame(raw) {
|
|
1630
|
+
let event = "message";
|
|
1631
|
+
const data = [];
|
|
1632
|
+
for (const line of raw.split(/\r\n|\n|\r/)) {
|
|
1633
|
+
if (!line || line.startsWith(":")) continue;
|
|
1634
|
+
const i = line.indexOf(":");
|
|
1635
|
+
const field = i === -1 ? line : line.slice(0, i);
|
|
1636
|
+
let value = i === -1 ? "" : line.slice(i + 1);
|
|
1637
|
+
if (value.startsWith(" ")) value = value.slice(1);
|
|
1638
|
+
if (field === "event") event = value;
|
|
1639
|
+
else if (field === "data") data.push(value);
|
|
1640
|
+
}
|
|
1641
|
+
if (data.length === 0 && event === "message") return null;
|
|
1642
|
+
return { event, data: data.join("\n") };
|
|
1643
|
+
}
|
|
1644
|
+
async function* sseToText(source, options = {}) {
|
|
1645
|
+
const { extract, doneSentinel = "[DONE]", signal } = options;
|
|
1646
|
+
const decoder = new TextDecoder();
|
|
1647
|
+
let buffer = "";
|
|
1648
|
+
const handle = (raw) => {
|
|
1649
|
+
const frame = parseFrame(raw);
|
|
1650
|
+
if (!frame) return { done: false, text: null };
|
|
1651
|
+
if (frame.event === "error") {
|
|
1652
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("STREAM_ERROR", frame.data || "Server sent an error event", {
|
|
1653
|
+
details: frame
|
|
1654
|
+
});
|
|
1655
|
+
}
|
|
1656
|
+
if (doneSentinel !== null && frame.data === doneSentinel) {
|
|
1657
|
+
return { done: true, text: null };
|
|
1658
|
+
}
|
|
1659
|
+
const text = extract ? extract(frame.data, frame.event) : frame.data;
|
|
1660
|
+
return { done: false, text: text ? text : null };
|
|
1661
|
+
};
|
|
1662
|
+
for await (const chunk of toAsyncIterable(source, signal)) {
|
|
1663
|
+
buffer += typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
|
|
1664
|
+
let match;
|
|
1665
|
+
while ((match = BOUNDARY.exec(buffer)) !== null) {
|
|
1666
|
+
const raw = buffer.slice(0, match.index);
|
|
1667
|
+
buffer = buffer.slice(match.index + match[0].length);
|
|
1668
|
+
const result = handle(raw);
|
|
1669
|
+
if (result.done) return;
|
|
1670
|
+
if (result.text) yield result.text;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
buffer += decoder.decode();
|
|
1674
|
+
if (buffer.trim()) {
|
|
1675
|
+
const result = handle(buffer);
|
|
1676
|
+
if (!result.done && result.text) yield result.text;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
function jsonFieldExtractor(path) {
|
|
1680
|
+
const keys = path.split(".");
|
|
1681
|
+
return (data) => {
|
|
1682
|
+
try {
|
|
1683
|
+
let current = JSON.parse(data);
|
|
1684
|
+
for (const key of keys) {
|
|
1685
|
+
if (current === null || typeof current !== "object") return null;
|
|
1686
|
+
current = current[key];
|
|
1687
|
+
}
|
|
1688
|
+
return typeof current === "string" ? current : null;
|
|
1689
|
+
} catch (e5) {
|
|
1690
|
+
return null;
|
|
1691
|
+
}
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
// src/features/streaming/fetch-adapter.ts
|
|
1696
|
+
async function fetchStream(input, options = {}) {
|
|
1697
|
+
const { sse, ...init } = options;
|
|
1698
|
+
let response;
|
|
1699
|
+
try {
|
|
1700
|
+
response = await fetch(input, init);
|
|
1701
|
+
} catch (error) {
|
|
1702
|
+
if (_optionalChain([init, 'access', _84 => _84.signal, 'optionalAccess', _85 => _85.aborted])) {
|
|
1703
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("STREAM_ABORTED", "Request was aborted", { cause: error });
|
|
1704
|
+
}
|
|
1705
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("NETWORK_ERROR", "Network request failed", { cause: error });
|
|
1706
|
+
}
|
|
1707
|
+
if (!response.ok) {
|
|
1708
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("NETWORK_ERROR", `Request failed with status ${response.status}`, {
|
|
1709
|
+
details: { status: response.status, statusText: response.statusText }
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
if (!response.body) {
|
|
1713
|
+
throw new (0, _chunkG6YPSE6Jcjs.JuneError)("STREAM_EMPTY", "Response has no body");
|
|
1714
|
+
}
|
|
1715
|
+
const body = response.body;
|
|
1716
|
+
if (sse) {
|
|
1717
|
+
return sseToText(body, {
|
|
1718
|
+
...sse === true ? {} : sse,
|
|
1719
|
+
signal: _nullishCoalesce(init.signal, () => ( void 0))
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
return body;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
// src/features/streaming/use-june-stream.ts
|
|
1726
|
+
var FLUSH_MS = 16;
|
|
1727
|
+
var INITIAL = {
|
|
1728
|
+
response: "",
|
|
1729
|
+
isStreaming: false,
|
|
1730
|
+
status: "idle",
|
|
1731
|
+
error: null
|
|
1732
|
+
};
|
|
1733
|
+
function useJuneStream(options = {}) {
|
|
1734
|
+
const [state, setState] = _react.useState.call(void 0, INITIAL);
|
|
1735
|
+
const optionsRef = _react.useRef.call(void 0, options);
|
|
1736
|
+
optionsRef.current = options;
|
|
1737
|
+
const controllerRef = _react.useRef.call(void 0, null);
|
|
1738
|
+
const runIdRef = _react.useRef.call(void 0, 0);
|
|
1739
|
+
const textRef = _react.useRef.call(void 0, "");
|
|
1740
|
+
const timerRef = _react.useRef.call(void 0, null);
|
|
1741
|
+
const clearTimer = _react.useCallback.call(void 0, () => {
|
|
1742
|
+
if (timerRef.current !== null) {
|
|
1743
|
+
clearTimeout(timerRef.current);
|
|
1744
|
+
timerRef.current = null;
|
|
1745
|
+
}
|
|
1746
|
+
}, []);
|
|
1747
|
+
const schedule = _react.useCallback.call(void 0, () => {
|
|
1748
|
+
if (timerRef.current !== null) return;
|
|
1749
|
+
timerRef.current = setTimeout(() => {
|
|
1750
|
+
timerRef.current = null;
|
|
1751
|
+
const text = textRef.current;
|
|
1752
|
+
setState(
|
|
1753
|
+
(s) => s.status !== "streaming" || s.response === text ? s : { ...s, response: text }
|
|
1754
|
+
);
|
|
1755
|
+
}, FLUSH_MS);
|
|
1756
|
+
}, []);
|
|
1757
|
+
const start = _react.useCallback.call(void 0,
|
|
1758
|
+
async (input) => {
|
|
1759
|
+
_optionalChain([controllerRef, 'access', _86 => _86.current, 'optionalAccess', _87 => _87.abort, 'call', _88 => _88()]);
|
|
1760
|
+
const controller = new AbortController();
|
|
1761
|
+
controllerRef.current = controller;
|
|
1762
|
+
const runId = ++runIdRef.current;
|
|
1763
|
+
const stale = () => runId !== runIdRef.current;
|
|
1764
|
+
textRef.current = "";
|
|
1765
|
+
clearTimer();
|
|
1766
|
+
setState({ response: "", isStreaming: true, status: "streaming", error: null });
|
|
1767
|
+
try {
|
|
1768
|
+
const source = typeof input === "function" ? await input(controller.signal) : input;
|
|
1769
|
+
if (stale()) return null;
|
|
1770
|
+
const text = await consumeStream(source, {
|
|
1771
|
+
signal: controller.signal,
|
|
1772
|
+
allowEmpty: optionsRef.current.allowEmpty,
|
|
1773
|
+
onText: (_delta, full) => {
|
|
1774
|
+
if (stale()) return;
|
|
1775
|
+
textRef.current = full;
|
|
1776
|
+
schedule();
|
|
1777
|
+
}
|
|
1778
|
+
});
|
|
1779
|
+
if (stale()) return null;
|
|
1780
|
+
clearTimer();
|
|
1781
|
+
setState({ response: text, isStreaming: false, status: "done", error: null });
|
|
1782
|
+
_optionalChain([optionsRef, 'access', _89 => _89.current, 'access', _90 => _90.onComplete, 'optionalCall', _91 => _91(text)]);
|
|
1783
|
+
return text;
|
|
1784
|
+
} catch (caught) {
|
|
1785
|
+
if (stale()) return null;
|
|
1786
|
+
clearTimer();
|
|
1787
|
+
const error = _chunkG6YPSE6Jcjs.toJuneError.call(void 0, caught, "STREAM_ERROR");
|
|
1788
|
+
const aborted = controller.signal.aborted || error.code === "STREAM_ABORTED";
|
|
1789
|
+
setState({
|
|
1790
|
+
response: textRef.current,
|
|
1791
|
+
// keep partial output
|
|
1792
|
+
isStreaming: false,
|
|
1793
|
+
status: aborted ? "aborted" : "error",
|
|
1794
|
+
error: aborted ? null : error
|
|
1795
|
+
});
|
|
1796
|
+
if (!aborted) _optionalChain([optionsRef, 'access', _92 => _92.current, 'access', _93 => _93.onError, 'optionalCall', _94 => _94(error)]);
|
|
1797
|
+
return null;
|
|
1798
|
+
}
|
|
1799
|
+
},
|
|
1800
|
+
[clearTimer, schedule]
|
|
1801
|
+
);
|
|
1802
|
+
const startFetch = _react.useCallback.call(void 0,
|
|
1803
|
+
(input, fetchOptions) => start((signal) => fetchStream(input, { ...fetchOptions, signal })),
|
|
1804
|
+
[start]
|
|
1805
|
+
);
|
|
1806
|
+
const abort = _react.useCallback.call(void 0, () => {
|
|
1807
|
+
_optionalChain([controllerRef, 'access', _95 => _95.current, 'optionalAccess', _96 => _96.abort, 'call', _97 => _97()]);
|
|
1808
|
+
}, []);
|
|
1809
|
+
const reset = _react.useCallback.call(void 0, () => {
|
|
1810
|
+
_optionalChain([controllerRef, 'access', _98 => _98.current, 'optionalAccess', _99 => _99.abort, 'call', _100 => _100()]);
|
|
1811
|
+
runIdRef.current++;
|
|
1812
|
+
textRef.current = "";
|
|
1813
|
+
clearTimer();
|
|
1814
|
+
setState(INITIAL);
|
|
1815
|
+
}, [clearTimer]);
|
|
1816
|
+
_react.useEffect.call(void 0,
|
|
1817
|
+
() => () => {
|
|
1818
|
+
runIdRef.current++;
|
|
1819
|
+
_optionalChain([controllerRef, 'access', _101 => _101.current, 'optionalAccess', _102 => _102.abort, 'call', _103 => _103()]);
|
|
1820
|
+
clearTimer();
|
|
1821
|
+
},
|
|
1822
|
+
[clearTimer]
|
|
1823
|
+
);
|
|
1824
|
+
return _react.useMemo.call(void 0,
|
|
1825
|
+
() => ({ ...state, start, startFetch, abort, reset }),
|
|
1826
|
+
[state, start, startFetch, abort, reset]
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
// src/features/document-editor/document-component.tsx
|
|
1831
|
+
|
|
1832
|
+
var _v4 = require('zod/v4');
|
|
1833
|
+
|
|
1834
|
+
// src/features/document-editor/document-draft.tsx
|
|
1835
|
+
|
|
1836
|
+
function DocumentDraft({ title, words, state }) {
|
|
1837
|
+
if (state === "empty") {
|
|
1838
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-doc june-doc--draft", role: "alert", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "june-doc__draft-text", children: "No document content was received." }) });
|
|
1839
|
+
}
|
|
1840
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc june-doc--draft", role: "status", "aria-live": "polite", children: [
|
|
1841
|
+
title ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "june-doc__draft-title", children: title }) : null,
|
|
1842
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "june-doc__draft-text", children: state === "drafting" ? `Drafting document\u2026${words ? ` ${words} words so far` : ""}` : "Loading editor\u2026" }),
|
|
1843
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__pulse", "aria-hidden": "true", children: [
|
|
1844
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", {}),
|
|
1845
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", {}),
|
|
1846
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", {})
|
|
1847
|
+
] })
|
|
1848
|
+
] });
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
// src/features/document-editor/document-editor.tsx
|
|
1852
|
+
var _react3 = require('@tiptap/react');
|
|
1853
|
+
|
|
1854
|
+
|
|
1855
|
+
// src/features/document-editor/document-bubble.tsx
|
|
1856
|
+
|
|
1857
|
+
var _menus = require('@tiptap/react/menus');
|
|
1858
|
+
|
|
1859
|
+
// src/features/document-editor/document-toolbar.tsx
|
|
1860
|
+
|
|
1861
|
+
|
|
1862
|
+
|
|
1863
|
+
// src/features/document-editor/document-icons.tsx
|
|
1864
|
+
|
|
1865
|
+
var PATHS = {
|
|
1866
|
+
undo: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1867
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M9 14 4 9l5-5" }),
|
|
1868
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M4 9h10.5a5.5 5.5 0 0 1 0 11H11" })
|
|
1869
|
+
] }),
|
|
1870
|
+
redo: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1871
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m15 14 5-5-5-5" }),
|
|
1872
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M20 9H9.5a5.5 5.5 0 0 0 0 11H13" })
|
|
1873
|
+
] }),
|
|
1874
|
+
bold: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8" }),
|
|
1875
|
+
italic: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1876
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "line", { x1: "19", x2: "10", y1: "4", y2: "4" }),
|
|
1877
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "line", { x1: "14", x2: "5", y1: "20", y2: "20" }),
|
|
1878
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "line", { x1: "15", x2: "9", y1: "4", y2: "20" })
|
|
1879
|
+
] }),
|
|
1880
|
+
underline: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1881
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M6 4v6a6 6 0 0 0 12 0V4" }),
|
|
1882
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "line", { x1: "4", x2: "20", y1: "20", y2: "20" })
|
|
1883
|
+
] }),
|
|
1884
|
+
strike: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1885
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M16 4H9a3 3 0 0 0-2.83 4" }),
|
|
1886
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M14 12a4 4 0 0 1 0 8H6" }),
|
|
1887
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "line", { x1: "4", x2: "20", y1: "12", y2: "12" })
|
|
1888
|
+
] }),
|
|
1889
|
+
code: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1890
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m16 18 6-6-6-6" }),
|
|
1891
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m8 6-6 6 6 6" })
|
|
1892
|
+
] }),
|
|
1893
|
+
codeBlock: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1894
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
|
|
1895
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m10 9-3 3 3 3" }),
|
|
1896
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m14 15 3-3-3-3" })
|
|
1897
|
+
] }),
|
|
1898
|
+
superscript: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1899
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m4 19 8-8" }),
|
|
1900
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m12 19-8-8" }),
|
|
1901
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M20 12h-4c0-1.5.44-2 1.5-2.5S20 8.33 20 7c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07" })
|
|
1902
|
+
] }),
|
|
1903
|
+
subscript: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1904
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m4 5 8 8" }),
|
|
1905
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m12 5-8 8" }),
|
|
1906
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M20 19h-4c0-1.5.44-2 1.5-2.5S20 15.33 20 14c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07" })
|
|
1907
|
+
] }),
|
|
1908
|
+
alignLeft: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1909
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M15 12H3" }),
|
|
1910
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M17 18H3" }),
|
|
1911
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 6H3" })
|
|
1912
|
+
] }),
|
|
1913
|
+
alignCenter: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1914
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M17 12H7" }),
|
|
1915
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M19 18H5" }),
|
|
1916
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 6H3" })
|
|
1917
|
+
] }),
|
|
1918
|
+
alignRight: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1919
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 12H9" }),
|
|
1920
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 18H7" }),
|
|
1921
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 6H3" })
|
|
1922
|
+
] }),
|
|
1923
|
+
alignJustify: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1924
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M3 12h18" }),
|
|
1925
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M3 18h18" }),
|
|
1926
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M3 6h18" })
|
|
1927
|
+
] }),
|
|
1928
|
+
list: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1929
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M3 12h.01" }),
|
|
1930
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M3 18h.01" }),
|
|
1931
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M3 6h.01" }),
|
|
1932
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M8 12h13" }),
|
|
1933
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M8 18h13" }),
|
|
1934
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M8 6h13" })
|
|
1935
|
+
] }),
|
|
1936
|
+
listOrdered: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1937
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M10 12h11" }),
|
|
1938
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M10 18h11" }),
|
|
1939
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M10 6h11" }),
|
|
1940
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M4 10h2" }),
|
|
1941
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M4 6h1v4" }),
|
|
1942
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" })
|
|
1943
|
+
] }),
|
|
1944
|
+
outdent: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1945
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 12H11" }),
|
|
1946
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 18H11" }),
|
|
1947
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 6H11" }),
|
|
1948
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m7 8-4 4 4 4" })
|
|
1949
|
+
] }),
|
|
1950
|
+
indent: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1951
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 12H11" }),
|
|
1952
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 18H11" }),
|
|
1953
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 6H11" }),
|
|
1954
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m3 8 4 4-4 4" })
|
|
1955
|
+
] }),
|
|
1956
|
+
quote: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1957
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M17 6H3" }),
|
|
1958
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 12H8" }),
|
|
1959
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 18H8" }),
|
|
1960
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M3 12v6" })
|
|
1961
|
+
] }),
|
|
1962
|
+
pilcrow: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1963
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M13 4v16" }),
|
|
1964
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M17 4v16" }),
|
|
1965
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M19 4H9.5a4.5 4.5 0 0 0 0 9H13" })
|
|
1966
|
+
] }),
|
|
1967
|
+
pageBreak: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1968
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "line", { x1: "3", x2: "21", y1: "12", y2: "12" }),
|
|
1969
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "polyline", { points: "8 8 12 4 16 8" }),
|
|
1970
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "polyline", { points: "16 16 12 20 8 16" })
|
|
1971
|
+
] }),
|
|
1972
|
+
divider: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1973
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M3 12h18" }),
|
|
1974
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M8 6h8", opacity: ".45" }),
|
|
1975
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M8 18h8", opacity: ".45" })
|
|
1976
|
+
] }),
|
|
1977
|
+
layout: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1978
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
|
|
1979
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
|
|
1980
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
|
|
1981
|
+
] }),
|
|
1982
|
+
search: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1983
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "circle", { cx: "11", cy: "11", r: "8" }),
|
|
1984
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m21 21-4.3-4.3" })
|
|
1985
|
+
] }),
|
|
1986
|
+
palette: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1987
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "circle", { cx: "13.5", cy: "6.5", r: ".5", fill: "currentColor" }),
|
|
1988
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "circle", { cx: "17.5", cy: "10.5", r: ".5", fill: "currentColor" }),
|
|
1989
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "circle", { cx: "8.5", cy: "7.5", r: ".5", fill: "currentColor" }),
|
|
1990
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "circle", { cx: "6.5", cy: "12.5", r: ".5", fill: "currentColor" }),
|
|
1991
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.93 0 1.65-.75 1.65-1.69 0-.44-.18-.84-.44-1.13-.29-.29-.44-.65-.44-1.13a1.64 1.64 0 0 1 1.67-1.67h2c3.05 0 5.55-2.5 5.55-5.55C21.97 6.01 17.46 2 12 2z" })
|
|
1992
|
+
] }),
|
|
1993
|
+
highlighter: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1994
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m9 11-6 6v3h9l3-3" }),
|
|
1995
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4" })
|
|
1996
|
+
] }),
|
|
1997
|
+
link: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1998
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
1999
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
2000
|
+
] }),
|
|
2001
|
+
image: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2002
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
|
|
2003
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "circle", { cx: "9", cy: "9", r: "2" }),
|
|
2004
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m21 15-3.09-3.09a2 2 0 0 0-2.82 0L6 21" })
|
|
2005
|
+
] }),
|
|
2006
|
+
table: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2007
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M12 3v18" }),
|
|
2008
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
|
|
2009
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M3 9h18" }),
|
|
2010
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M3 15h18" })
|
|
2011
|
+
] }),
|
|
2012
|
+
eraser: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2013
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21" }),
|
|
2014
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M22 21H7" }),
|
|
2015
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m5 11 9 9" })
|
|
2016
|
+
] }),
|
|
2017
|
+
save: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2018
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z" }),
|
|
2019
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7" }),
|
|
2020
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M7 3v4a1 1 0 0 0 1 1h7" })
|
|
2021
|
+
] }),
|
|
2022
|
+
download: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2023
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
2024
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "polyline", { points: "7 10 12 15 17 10" }),
|
|
2025
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "line", { x1: "12", x2: "12", y1: "15", y2: "3" })
|
|
2026
|
+
] }),
|
|
2027
|
+
chevronDown: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m6 9 6 6 6-6" }),
|
|
2028
|
+
chevronUp: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m18 15-6-6-6 6" }),
|
|
2029
|
+
close: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2030
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M18 6 6 18" }),
|
|
2031
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m6 6 12 12" })
|
|
2032
|
+
] }),
|
|
2033
|
+
maximize: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2034
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M8 3H5a2 2 0 0 0-2 2v3" }),
|
|
2035
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 8V5a2 2 0 0 0-2-2h-3" }),
|
|
2036
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M3 16v3a2 2 0 0 0 2 2h3" }),
|
|
2037
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
|
|
2038
|
+
] }),
|
|
2039
|
+
copy: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2040
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "rect", { width: "14", height: "14", x: "8", y: "8", rx: "2" }),
|
|
2041
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })
|
|
2042
|
+
] }),
|
|
2043
|
+
file: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2044
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
2045
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
2046
|
+
] }),
|
|
2047
|
+
plus: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2048
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M5 12h14" }),
|
|
2049
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M12 5v14" })
|
|
2050
|
+
] }),
|
|
2051
|
+
minus: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M5 12h14" }),
|
|
2052
|
+
check: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M20 6 9 17l-5-5" }),
|
|
2053
|
+
sparkles: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2054
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M9.94 14.06 8 20l-1.94-5.94L0 12l6.06-1.94L8 4l1.94 6.06L16 12z", transform: "translate(3 -1) scale(.85)" }),
|
|
2055
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M20 3v4" }),
|
|
2056
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M22 5h-4" })
|
|
2057
|
+
] }),
|
|
2058
|
+
upload: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2059
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
2060
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "polyline", { points: "17 8 12 3 7 8" }),
|
|
2061
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "line", { x1: "12", x2: "12", y1: "3", y2: "15" })
|
|
2062
|
+
] })
|
|
2063
|
+
};
|
|
2064
|
+
function Icon({ name, size = 16 }) {
|
|
2065
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2066
|
+
"svg",
|
|
2067
|
+
{
|
|
2068
|
+
className: "june-doc__icon",
|
|
2069
|
+
width: size,
|
|
2070
|
+
height: size,
|
|
2071
|
+
viewBox: "0 0 24 24",
|
|
2072
|
+
fill: "none",
|
|
2073
|
+
stroke: "currentColor",
|
|
2074
|
+
strokeWidth: 2,
|
|
2075
|
+
strokeLinecap: "round",
|
|
2076
|
+
strokeLinejoin: "round",
|
|
2077
|
+
"aria-hidden": "true",
|
|
2078
|
+
focusable: "false",
|
|
2079
|
+
children: PATHS[name]
|
|
2080
|
+
}
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
// src/features/document-editor/document-popover.tsx
|
|
2085
|
+
|
|
2086
|
+
|
|
2087
|
+
function Dropdown({ trigger, children, align = "start", className, onOpen }) {
|
|
2088
|
+
const [open, setOpen] = _react.useState.call(void 0, false);
|
|
2089
|
+
const anchor = _react.useRef.call(void 0, null);
|
|
2090
|
+
_react.useEffect.call(void 0, () => {
|
|
2091
|
+
if (!open) return;
|
|
2092
|
+
const onDown = (e) => {
|
|
2093
|
+
if (anchor.current && !anchor.current.contains(e.target)) setOpen(false);
|
|
2094
|
+
};
|
|
2095
|
+
document.addEventListener("mousedown", onDown);
|
|
2096
|
+
return () => document.removeEventListener("mousedown", onDown);
|
|
2097
|
+
}, [open]);
|
|
2098
|
+
const toggle = () => setOpen((v) => {
|
|
2099
|
+
if (!v) _optionalChain([onOpen, 'optionalCall', _104 => _104()]);
|
|
2100
|
+
return !v;
|
|
2101
|
+
});
|
|
2102
|
+
const close = () => setOpen(false);
|
|
2103
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2104
|
+
"div",
|
|
2105
|
+
{
|
|
2106
|
+
ref: anchor,
|
|
2107
|
+
className: "june-doc__anchor",
|
|
2108
|
+
onKeyDown: (e) => {
|
|
2109
|
+
if (e.key === "Escape" && open) {
|
|
2110
|
+
e.stopPropagation();
|
|
2111
|
+
close();
|
|
2112
|
+
}
|
|
2113
|
+
},
|
|
2114
|
+
children: [
|
|
2115
|
+
trigger({ open, toggle }),
|
|
2116
|
+
open ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: `june-doc__pop june-doc__pop--${align}${className ? ` ${className}` : ""}`, children: children(close) }) : null
|
|
2117
|
+
]
|
|
2118
|
+
}
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
// src/features/document-editor/document-toolbar.tsx
|
|
2123
|
+
|
|
2124
|
+
var FONT_FAMILIES = [
|
|
2125
|
+
{ label: "Default", value: "" },
|
|
2126
|
+
{ label: "Arial", value: "Arial, Helvetica, sans-serif" },
|
|
2127
|
+
{ label: "Calibri", value: "Calibri, Carlito, sans-serif" },
|
|
2128
|
+
{ label: "Georgia", value: "Georgia, serif" },
|
|
2129
|
+
{ label: "Garamond", value: "Garamond, 'EB Garamond', serif" },
|
|
2130
|
+
{ label: "Times New Roman", value: "'Times New Roman', Times, serif" },
|
|
2131
|
+
{ label: "Verdana", value: "Verdana, Geneva, sans-serif" },
|
|
2132
|
+
{ label: "Trebuchet MS", value: "'Trebuchet MS', sans-serif" },
|
|
2133
|
+
{ label: "Courier New", value: "'Courier New', Courier, monospace" }
|
|
2134
|
+
];
|
|
2135
|
+
var FONT_SIZES = [8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 24, 28, 32, 36, 48, 60, 72];
|
|
2136
|
+
var TEXT_COLORS = [
|
|
2137
|
+
"#111827",
|
|
2138
|
+
"#374151",
|
|
2139
|
+
"#6b7280",
|
|
2140
|
+
"#9ca3af",
|
|
2141
|
+
"#dc2626",
|
|
2142
|
+
"#ea580c",
|
|
2143
|
+
"#ca8a04",
|
|
2144
|
+
"#16a34a",
|
|
2145
|
+
"#0d9488",
|
|
2146
|
+
"#0284c7",
|
|
2147
|
+
"#2563eb",
|
|
2148
|
+
"#4f46e5",
|
|
2149
|
+
"#9333ea",
|
|
2150
|
+
"#db2777",
|
|
2151
|
+
"#7f1d1d",
|
|
2152
|
+
"#1e3a8a"
|
|
2153
|
+
];
|
|
2154
|
+
var HIGHLIGHT_COLORS = [
|
|
2155
|
+
"#fef08a",
|
|
2156
|
+
"#fde68a",
|
|
2157
|
+
"#fed7aa",
|
|
2158
|
+
"#fecaca",
|
|
2159
|
+
"#fbcfe8",
|
|
2160
|
+
"#e9d5ff",
|
|
2161
|
+
"#c7d2fe",
|
|
2162
|
+
"#bfdbfe",
|
|
2163
|
+
"#a5f3fc",
|
|
2164
|
+
"#bbf7d0",
|
|
2165
|
+
"#d9f99d",
|
|
2166
|
+
"#e5e7eb"
|
|
2167
|
+
];
|
|
2168
|
+
var MAX_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
2169
|
+
function Btn({ label, icon, active, disabled, toggle, onClick, children, className, text }) {
|
|
2170
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2171
|
+
"button",
|
|
2172
|
+
{
|
|
2173
|
+
type: "button",
|
|
2174
|
+
className: `june-doc__btn${icon && !text && !children ? " june-doc__btn--icon" : ""}${className ? ` ${className}` : ""}`,
|
|
2175
|
+
"aria-label": label,
|
|
2176
|
+
title: label,
|
|
2177
|
+
"aria-pressed": toggle ? !!active : void 0,
|
|
2178
|
+
"data-active": active || void 0,
|
|
2179
|
+
disabled,
|
|
2180
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2181
|
+
onClick,
|
|
2182
|
+
children: [
|
|
2183
|
+
icon ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: icon }) : null,
|
|
2184
|
+
_nullishCoalesce(children, () => ( (text ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: label }) : null)))
|
|
2185
|
+
]
|
|
2186
|
+
}
|
|
2187
|
+
);
|
|
2188
|
+
}
|
|
2189
|
+
function Group({ label, children }) {
|
|
2190
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__group", role: "group", "aria-label": label, children: [
|
|
2191
|
+
label ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__group-label", "aria-hidden": "true", children: label }) : null,
|
|
2192
|
+
children
|
|
2193
|
+
] });
|
|
2194
|
+
}
|
|
2195
|
+
function Field({ label, children }) {
|
|
2196
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "label", { className: "june-doc__field", children: [
|
|
2197
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__field-label", children: label }),
|
|
2198
|
+
children
|
|
2199
|
+
] });
|
|
2200
|
+
}
|
|
2201
|
+
function ColorPicker({ label, icon, colors, current, disabled, resetLabel, onPick, onReset }) {
|
|
2202
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2203
|
+
Dropdown,
|
|
2204
|
+
{
|
|
2205
|
+
trigger: ({ open, toggle }) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2206
|
+
"button",
|
|
2207
|
+
{
|
|
2208
|
+
type: "button",
|
|
2209
|
+
className: "june-doc__btn june-doc__btn--icon june-doc__btn--color",
|
|
2210
|
+
"aria-label": label,
|
|
2211
|
+
title: label,
|
|
2212
|
+
"aria-expanded": open,
|
|
2213
|
+
"aria-haspopup": "dialog",
|
|
2214
|
+
disabled,
|
|
2215
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2216
|
+
onClick: toggle,
|
|
2217
|
+
children: [
|
|
2218
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: icon }),
|
|
2219
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__color-bar", style: { background: _nullishCoalesce(current, () => ( "transparent")) } })
|
|
2220
|
+
]
|
|
2221
|
+
}
|
|
2222
|
+
),
|
|
2223
|
+
children: (close) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__swatches-wrap", role: "dialog", "aria-label": label, children: [
|
|
2224
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-doc__pop-title", children: label }),
|
|
2225
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-doc__swatches", children: colors.map((c) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2226
|
+
"button",
|
|
2227
|
+
{
|
|
2228
|
+
type: "button",
|
|
2229
|
+
className: "june-doc__swatch",
|
|
2230
|
+
style: { background: c },
|
|
2231
|
+
"aria-label": c,
|
|
2232
|
+
title: c,
|
|
2233
|
+
"data-active": _optionalChain([current, 'optionalAccess', _105 => _105.toLowerCase, 'call', _106 => _106()]) === c ? true : void 0,
|
|
2234
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2235
|
+
onClick: () => {
|
|
2236
|
+
onPick(c);
|
|
2237
|
+
close();
|
|
2238
|
+
}
|
|
2239
|
+
},
|
|
2240
|
+
c
|
|
2241
|
+
)) }),
|
|
2242
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__pop-row", children: [
|
|
2243
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2244
|
+
"button",
|
|
2245
|
+
{
|
|
2246
|
+
type: "button",
|
|
2247
|
+
className: "june-doc__btn june-doc__btn--ghost",
|
|
2248
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2249
|
+
onClick: () => {
|
|
2250
|
+
onReset();
|
|
2251
|
+
close();
|
|
2252
|
+
},
|
|
2253
|
+
children: resetLabel
|
|
2254
|
+
}
|
|
2255
|
+
),
|
|
2256
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "label", { className: "june-doc__custom-color", title: "Custom color", children: [
|
|
2257
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: "Custom" }),
|
|
2258
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2259
|
+
"input",
|
|
2260
|
+
{
|
|
2261
|
+
type: "color",
|
|
2262
|
+
"aria-label": `${label}: custom`,
|
|
2263
|
+
defaultValue: current && /^#[0-9a-f]{6}$/i.test(current) ? current : "#000000",
|
|
2264
|
+
onChange: (e) => onPick(e.target.value)
|
|
2265
|
+
}
|
|
2266
|
+
)
|
|
2267
|
+
] })
|
|
2268
|
+
] })
|
|
2269
|
+
] })
|
|
2270
|
+
}
|
|
2271
|
+
);
|
|
2272
|
+
}
|
|
2273
|
+
function LinkPanel({
|
|
2274
|
+
editor,
|
|
2275
|
+
href,
|
|
2276
|
+
isLink,
|
|
2277
|
+
close
|
|
2278
|
+
}) {
|
|
2279
|
+
const [value, setValue] = _react.useState.call(void 0, href);
|
|
2280
|
+
const apply = () => {
|
|
2281
|
+
const safe = _chunkG6YPSE6Jcjs.sanitizeHref.call(void 0, value);
|
|
2282
|
+
if (safe) editor.chain().focus().extendMarkRange("link").setLink({ href: safe }).run();
|
|
2283
|
+
close();
|
|
2284
|
+
};
|
|
2285
|
+
const remove = () => {
|
|
2286
|
+
editor.chain().focus().extendMarkRange("link").unsetLink().run();
|
|
2287
|
+
close();
|
|
2288
|
+
};
|
|
2289
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__form", role: "dialog", "aria-label": "Edit link", children: [
|
|
2290
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-doc__pop-title", children: "Link" }),
|
|
2291
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2292
|
+
"input",
|
|
2293
|
+
{
|
|
2294
|
+
"aria-label": "Link address",
|
|
2295
|
+
className: "june-doc__input",
|
|
2296
|
+
placeholder: "https://example.com",
|
|
2297
|
+
value,
|
|
2298
|
+
autoFocus: true,
|
|
2299
|
+
onChange: (e) => setValue(e.target.value),
|
|
2300
|
+
onKeyDown: (e) => {
|
|
2301
|
+
if (e.key === "Enter") {
|
|
2302
|
+
e.preventDefault();
|
|
2303
|
+
apply();
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
),
|
|
2308
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__pop-row june-doc__pop-row--end", children: [
|
|
2309
|
+
isLink ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn june-doc__btn--ghost", onClick: remove, children: "Remove" }) : null,
|
|
2310
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn", onClick: close, children: "Cancel" }),
|
|
2311
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn june-doc__btn--primary", onClick: apply, children: "Apply" })
|
|
2312
|
+
] })
|
|
2313
|
+
] });
|
|
2314
|
+
}
|
|
2315
|
+
function ImagePanel({ editor, close, onNotice }) {
|
|
2316
|
+
const [url, setUrl] = _react.useState.call(void 0, "");
|
|
2317
|
+
const [alt, setAlt] = _react.useState.call(void 0, "");
|
|
2318
|
+
const file = _react.useRef.call(void 0, null);
|
|
2319
|
+
const insert = (src) => {
|
|
2320
|
+
editor.chain().focus().setImage({ src, alt: alt || void 0 }).run();
|
|
2321
|
+
close();
|
|
2322
|
+
};
|
|
2323
|
+
const fromUrl = () => {
|
|
2324
|
+
const safe = _chunkG6YPSE6Jcjs.sanitizeHref.call(void 0, url);
|
|
2325
|
+
if (!safe || !/^https?:/i.test(safe)) {
|
|
2326
|
+
onNotice("Enter an http(s) image address");
|
|
2327
|
+
return;
|
|
2328
|
+
}
|
|
2329
|
+
insert(safe);
|
|
2330
|
+
};
|
|
2331
|
+
const fromFile = (f) => {
|
|
2332
|
+
if (!f) return;
|
|
2333
|
+
if (!f.type.startsWith("image/")) return onNotice("That file is not an image");
|
|
2334
|
+
if (f.size > MAX_IMAGE_BYTES) return onNotice("Images must be 8 MB or smaller");
|
|
2335
|
+
const reader = new FileReader();
|
|
2336
|
+
reader.onload = () => typeof reader.result === "string" && insert(reader.result);
|
|
2337
|
+
reader.onerror = () => onNotice("The image could not be read");
|
|
2338
|
+
reader.readAsDataURL(f);
|
|
2339
|
+
};
|
|
2340
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__form", role: "dialog", "aria-label": "Insert image", children: [
|
|
2341
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-doc__pop-title", children: "Image" }),
|
|
2342
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2343
|
+
"button",
|
|
2344
|
+
{
|
|
2345
|
+
type: "button",
|
|
2346
|
+
className: "june-doc__dropzone",
|
|
2347
|
+
onClick: () => _optionalChain([file, 'access', _107 => _107.current, 'optionalAccess', _108 => _108.click, 'call', _109 => _109()]),
|
|
2348
|
+
onDragOver: (e) => e.preventDefault(),
|
|
2349
|
+
onDrop: (e) => {
|
|
2350
|
+
e.preventDefault();
|
|
2351
|
+
fromFile(e.dataTransfer.files[0]);
|
|
2352
|
+
},
|
|
2353
|
+
children: [
|
|
2354
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "upload", size: 18 }),
|
|
2355
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: "Upload or drop an image" })
|
|
2356
|
+
]
|
|
2357
|
+
}
|
|
2358
|
+
),
|
|
2359
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2360
|
+
"input",
|
|
2361
|
+
{
|
|
2362
|
+
ref: file,
|
|
2363
|
+
type: "file",
|
|
2364
|
+
accept: "image/*",
|
|
2365
|
+
hidden: true,
|
|
2366
|
+
onChange: (e) => fromFile(_optionalChain([e, 'access', _110 => _110.target, 'access', _111 => _111.files, 'optionalAccess', _112 => _112[0]]))
|
|
2367
|
+
}
|
|
2368
|
+
),
|
|
2369
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-doc__or", children: "or" }),
|
|
2370
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2371
|
+
"input",
|
|
2372
|
+
{
|
|
2373
|
+
"aria-label": "Image address",
|
|
2374
|
+
className: "june-doc__input",
|
|
2375
|
+
placeholder: "https://example.com/image.png",
|
|
2376
|
+
value: url,
|
|
2377
|
+
onChange: (e) => setUrl(e.target.value),
|
|
2378
|
+
onKeyDown: (e) => e.key === "Enter" && fromUrl()
|
|
2379
|
+
}
|
|
2380
|
+
),
|
|
2381
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2382
|
+
"input",
|
|
2383
|
+
{
|
|
2384
|
+
"aria-label": "Alternative text",
|
|
2385
|
+
className: "june-doc__input",
|
|
2386
|
+
placeholder: "Alt text (optional)",
|
|
2387
|
+
value: alt,
|
|
2388
|
+
onChange: (e) => setAlt(e.target.value)
|
|
2389
|
+
}
|
|
2390
|
+
),
|
|
2391
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__pop-row june-doc__pop-row--end", children: [
|
|
2392
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn", onClick: close, children: "Cancel" }),
|
|
2393
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn june-doc__btn--primary", disabled: !url.trim(), onClick: fromUrl, children: "Insert" })
|
|
2394
|
+
] })
|
|
2395
|
+
] });
|
|
2396
|
+
}
|
|
2397
|
+
function TablePanel({ editor, close }) {
|
|
2398
|
+
const [hover, setHover] = _react.useState.call(void 0, [0, 0]);
|
|
2399
|
+
const N = 8;
|
|
2400
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__form", role: "dialog", "aria-label": "Insert table", children: [
|
|
2401
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__pop-title", children: [
|
|
2402
|
+
"Table ",
|
|
2403
|
+
hover[0] ? `${hover[0]} \xD7 ${hover[1]}` : ""
|
|
2404
|
+
] }),
|
|
2405
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-doc__grid", onMouseLeave: () => setHover([0, 0]), children: Array.from({ length: N * N }, (_, i) => {
|
|
2406
|
+
const r = Math.floor(i / N) + 1;
|
|
2407
|
+
const c = i % N + 1;
|
|
2408
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2409
|
+
"button",
|
|
2410
|
+
{
|
|
2411
|
+
type: "button",
|
|
2412
|
+
className: "june-doc__cell",
|
|
2413
|
+
"aria-label": `${r} by ${c} table`,
|
|
2414
|
+
"data-active": r <= hover[0] && c <= hover[1] ? true : void 0,
|
|
2415
|
+
onMouseEnter: () => setHover([r, c]),
|
|
2416
|
+
onFocus: () => setHover([r, c]),
|
|
2417
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2418
|
+
onClick: () => {
|
|
2419
|
+
editor.chain().focus().insertTable({ rows: r, cols: c, withHeaderRow: true }).run();
|
|
2420
|
+
close();
|
|
2421
|
+
}
|
|
2422
|
+
},
|
|
2423
|
+
i
|
|
2424
|
+
);
|
|
2425
|
+
}) })
|
|
2426
|
+
] });
|
|
2427
|
+
}
|
|
2428
|
+
var px = (v) => typeof v === "string" ? v.replace(/px$/, "") : "";
|
|
2429
|
+
function DocumentToolbar(props) {
|
|
2430
|
+
const { editor, editable, expanded, onToggleExpanded, showMarks, onToggleMarks, layout, onToggleLayout, findOpen, onToggleFind, onNotice } = props;
|
|
2431
|
+
const s = _react3.useEditorState.call(void 0, {
|
|
2432
|
+
editor,
|
|
2433
|
+
selector: ({ editor: ed }) => {
|
|
2434
|
+
if (!ed) return null;
|
|
2435
|
+
const ts = ed.getAttributes("textStyle");
|
|
2436
|
+
return {
|
|
2437
|
+
bold: ed.isActive("bold"),
|
|
2438
|
+
italic: ed.isActive("italic"),
|
|
2439
|
+
underline: ed.isActive("underline"),
|
|
2440
|
+
strike: ed.isActive("strike"),
|
|
2441
|
+
code: ed.isActive("code"),
|
|
2442
|
+
sup: ed.isActive("superscript"),
|
|
2443
|
+
sub: ed.isActive("subscript"),
|
|
2444
|
+
bullet: ed.isActive("bulletList"),
|
|
2445
|
+
ordered: ed.isActive("orderedList"),
|
|
2446
|
+
quote: ed.isActive("blockquote"),
|
|
2447
|
+
codeBlock: ed.isActive("codeBlock"),
|
|
2448
|
+
link: ed.isActive("link"),
|
|
2449
|
+
linkHref: _nullishCoalesce(ed.getAttributes("link").href, () => ( "")),
|
|
2450
|
+
h1: ed.isActive("heading", { level: 1 }),
|
|
2451
|
+
h2: ed.isActive("heading", { level: 2 }),
|
|
2452
|
+
h3: ed.isActive("heading", { level: 3 }),
|
|
2453
|
+
left: ed.isActive({ textAlign: "left" }),
|
|
2454
|
+
center: ed.isActive({ textAlign: "center" }),
|
|
2455
|
+
right: ed.isActive({ textAlign: "right" }),
|
|
2456
|
+
justify: ed.isActive({ textAlign: "justify" }),
|
|
2457
|
+
inTable: ed.isActive("table"),
|
|
2458
|
+
color: _nullishCoalesce(ts.color, () => ( null)),
|
|
2459
|
+
fontFamily: _nullishCoalesce(ts.fontFamily, () => ( "")),
|
|
2460
|
+
fontSize: px(ts.fontSize),
|
|
2461
|
+
highlight: ed.isActive("highlight") ? _nullishCoalesce(ed.getAttributes("highlight").color, () => ( "#fef08a")) : null,
|
|
2462
|
+
canUndo: ed.can().undo(),
|
|
2463
|
+
canRedo: ed.can().redo(),
|
|
2464
|
+
canSink: ed.can().sinkListItem("listItem"),
|
|
2465
|
+
canLift: ed.can().liftListItem("listItem"),
|
|
2466
|
+
canMerge: ed.can().mergeCells(),
|
|
2467
|
+
canSplit: ed.can().splitCell()
|
|
2468
|
+
};
|
|
2469
|
+
}
|
|
2470
|
+
});
|
|
2471
|
+
const run = () => _optionalChain([editor, 'optionalAccess', _113 => _113.chain, 'call', _114 => _114(), 'access', _115 => _115.focus, 'call', _116 => _116()]);
|
|
2472
|
+
const off = !editor || !editable;
|
|
2473
|
+
const style = _optionalChain([s, 'optionalAccess', _117 => _117.codeBlock]) ? "code" : _optionalChain([s, 'optionalAccess', _118 => _118.h1]) ? "1" : _optionalChain([s, 'optionalAccess', _119 => _119.h2]) ? "2" : _optionalChain([s, 'optionalAccess', _120 => _120.h3]) ? "3" : _optionalChain([s, 'optionalAccess', _121 => _121.quote]) ? "quote" : "p";
|
|
2474
|
+
const setStyle = (v) => {
|
|
2475
|
+
if (!editor || !s) return;
|
|
2476
|
+
let c = editor.chain().focus();
|
|
2477
|
+
if (v === "quote") {
|
|
2478
|
+
if (!s.quote) c = c.setParagraph().toggleBlockquote();
|
|
2479
|
+
} else if (v === "code") {
|
|
2480
|
+
if (!s.codeBlock) c = c.toggleCodeBlock();
|
|
2481
|
+
} else {
|
|
2482
|
+
if (s.quote) c = c.lift("blockquote");
|
|
2483
|
+
c = v === "p" ? c.setParagraph() : c.setHeading({ level: Number(v) });
|
|
2484
|
+
}
|
|
2485
|
+
c.run();
|
|
2486
|
+
};
|
|
2487
|
+
const fontKnown = FONT_FAMILIES.some((f) => f.value === _optionalChain([s, 'optionalAccess', _122 => _122.fontFamily]));
|
|
2488
|
+
const sizes = _optionalChain([s, 'optionalAccess', _123 => _123.fontSize]) && !FONT_SIZES.includes(Number(s.fontSize)) ? [...FONT_SIZES, Number(s.fontSize)].sort((a, b) => a - b) : FONT_SIZES;
|
|
2489
|
+
if (!editable) return null;
|
|
2490
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__ribbon", role: "toolbar", "aria-label": "Document formatting", children: [
|
|
2491
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__row", children: [
|
|
2492
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Group, { label: "History", children: [
|
|
2493
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Undo", icon: "undo", disabled: off || !_optionalChain([s, 'optionalAccess', _124 => _124.canUndo]), onClick: () => _optionalChain([run, 'call', _125 => _125(), 'optionalAccess', _126 => _126.undo, 'call', _127 => _127(), 'access', _128 => _128.run, 'call', _129 => _129()]) }),
|
|
2494
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Redo", icon: "redo", disabled: off || !_optionalChain([s, 'optionalAccess', _130 => _130.canRedo]), onClick: () => _optionalChain([run, 'call', _131 => _131(), 'optionalAccess', _132 => _132.redo, 'call', _133 => _133(), 'access', _134 => _134.run, 'call', _135 => _135()]) })
|
|
2495
|
+
] }),
|
|
2496
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Group, { label: "Format", children: [
|
|
2497
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Bold", icon: "bold", toggle: true, active: _optionalChain([s, 'optionalAccess', _136 => _136.bold]), disabled: off, onClick: () => _optionalChain([run, 'call', _137 => _137(), 'optionalAccess', _138 => _138.toggleBold, 'call', _139 => _139(), 'access', _140 => _140.run, 'call', _141 => _141()]) }),
|
|
2498
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Italic", icon: "italic", toggle: true, active: _optionalChain([s, 'optionalAccess', _142 => _142.italic]), disabled: off, onClick: () => _optionalChain([run, 'call', _143 => _143(), 'optionalAccess', _144 => _144.toggleItalic, 'call', _145 => _145(), 'access', _146 => _146.run, 'call', _147 => _147()]) }),
|
|
2499
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Underline", icon: "underline", toggle: true, active: _optionalChain([s, 'optionalAccess', _148 => _148.underline]), disabled: off, onClick: () => _optionalChain([run, 'call', _149 => _149(), 'optionalAccess', _150 => _150.toggleUnderline, 'call', _151 => _151(), 'access', _152 => _152.run, 'call', _153 => _153()]) }),
|
|
2500
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Strikethrough", icon: "strike", toggle: true, active: _optionalChain([s, 'optionalAccess', _154 => _154.strike]), disabled: off, onClick: () => _optionalChain([run, 'call', _155 => _155(), 'optionalAccess', _156 => _156.toggleStrike, 'call', _157 => _157(), 'access', _158 => _158.run, 'call', _159 => _159()]) }),
|
|
2501
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Inline code", icon: "code", toggle: true, active: _optionalChain([s, 'optionalAccess', _160 => _160.code]), disabled: off, onClick: () => _optionalChain([run, 'call', _161 => _161(), 'optionalAccess', _162 => _162.toggleCode, 'call', _163 => _163(), 'access', _164 => _164.run, 'call', _165 => _165()]) }),
|
|
2502
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Superscript", icon: "superscript", toggle: true, active: _optionalChain([s, 'optionalAccess', _166 => _166.sup]), disabled: off, onClick: () => _optionalChain([run, 'call', _167 => _167(), 'optionalAccess', _168 => _168.toggleSuperscript, 'call', _169 => _169(), 'access', _170 => _170.run, 'call', _171 => _171()]) }),
|
|
2503
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Subscript", icon: "subscript", toggle: true, active: _optionalChain([s, 'optionalAccess', _172 => _172.sub]), disabled: off, onClick: () => _optionalChain([run, 'call', _173 => _173(), 'optionalAccess', _174 => _174.toggleSubscript, 'call', _175 => _175(), 'access', _176 => _176.run, 'call', _177 => _177()]) })
|
|
2504
|
+
] }),
|
|
2505
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Group, { label: "Align", children: [
|
|
2506
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Align left", icon: "alignLeft", toggle: true, active: _optionalChain([s, 'optionalAccess', _178 => _178.left]), disabled: off, onClick: () => _optionalChain([run, 'call', _179 => _179(), 'optionalAccess', _180 => _180.setTextAlign, 'call', _181 => _181("left"), 'access', _182 => _182.run, 'call', _183 => _183()]) }),
|
|
2507
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Align center", icon: "alignCenter", toggle: true, active: _optionalChain([s, 'optionalAccess', _184 => _184.center]), disabled: off, onClick: () => _optionalChain([run, 'call', _185 => _185(), 'optionalAccess', _186 => _186.setTextAlign, 'call', _187 => _187("center"), 'access', _188 => _188.run, 'call', _189 => _189()]) }),
|
|
2508
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Align right", icon: "alignRight", toggle: true, active: _optionalChain([s, 'optionalAccess', _190 => _190.right]), disabled: off, onClick: () => _optionalChain([run, 'call', _191 => _191(), 'optionalAccess', _192 => _192.setTextAlign, 'call', _193 => _193("right"), 'access', _194 => _194.run, 'call', _195 => _195()]) }),
|
|
2509
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Justify", icon: "alignJustify", toggle: true, active: _optionalChain([s, 'optionalAccess', _196 => _196.justify]), disabled: off, onClick: () => _optionalChain([run, 'call', _197 => _197(), 'optionalAccess', _198 => _198.setTextAlign, 'call', _199 => _199("justify"), 'access', _200 => _200.run, 'call', _201 => _201()]) })
|
|
2510
|
+
] }),
|
|
2511
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Group, { label: "Lists", children: [
|
|
2512
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Bullet list", icon: "list", toggle: true, active: _optionalChain([s, 'optionalAccess', _202 => _202.bullet]), disabled: off, onClick: () => _optionalChain([run, 'call', _203 => _203(), 'optionalAccess', _204 => _204.toggleBulletList, 'call', _205 => _205(), 'access', _206 => _206.run, 'call', _207 => _207()]) }),
|
|
2513
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Numbered list", icon: "listOrdered", toggle: true, active: _optionalChain([s, 'optionalAccess', _208 => _208.ordered]), disabled: off, onClick: () => _optionalChain([run, 'call', _209 => _209(), 'optionalAccess', _210 => _210.toggleOrderedList, 'call', _211 => _211(), 'access', _212 => _212.run, 'call', _213 => _213()]) }),
|
|
2514
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Decrease indent", icon: "outdent", disabled: off || !_optionalChain([s, 'optionalAccess', _214 => _214.canLift]), onClick: () => _optionalChain([run, 'call', _215 => _215(), 'optionalAccess', _216 => _216.liftListItem, 'call', _217 => _217("listItem"), 'access', _218 => _218.run, 'call', _219 => _219()]) }),
|
|
2515
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Increase indent", icon: "indent", disabled: off || !_optionalChain([s, 'optionalAccess', _220 => _220.canSink]), onClick: () => _optionalChain([run, 'call', _221 => _221(), 'optionalAccess', _222 => _222.sinkListItem, 'call', _223 => _223("listItem"), 'access', _224 => _224.run, 'call', _225 => _225()]) })
|
|
2516
|
+
] }),
|
|
2517
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2518
|
+
"button",
|
|
2519
|
+
{
|
|
2520
|
+
type: "button",
|
|
2521
|
+
className: "june-doc__btn june-doc__more",
|
|
2522
|
+
"aria-expanded": expanded,
|
|
2523
|
+
"aria-label": expanded ? "Show fewer tools" : "Show more tools",
|
|
2524
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2525
|
+
onClick: onToggleExpanded,
|
|
2526
|
+
children: [
|
|
2527
|
+
expanded ? "Less" : "More",
|
|
2528
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: expanded ? "chevronUp" : "chevronDown", size: 14 })
|
|
2529
|
+
]
|
|
2530
|
+
}
|
|
2531
|
+
)
|
|
2532
|
+
] }),
|
|
2533
|
+
expanded ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__row june-doc__row--secondary", children: [
|
|
2534
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Group, { label: "Style", children: [
|
|
2535
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Field, { label: "Style", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "select", { className: "june-doc__select", "aria-label": "Text style", disabled: off, value: style, onChange: (e) => setStyle(e.target.value), children: [
|
|
2536
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: "p", children: "Paragraph" }),
|
|
2537
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: "1", children: "Heading 1" }),
|
|
2538
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: "2", children: "Heading 2" }),
|
|
2539
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: "3", children: "Heading 3" }),
|
|
2540
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: "quote", children: "Quote" }),
|
|
2541
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: "code", children: "Code block" })
|
|
2542
|
+
] }) }),
|
|
2543
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Field, { label: "Font", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2544
|
+
"select",
|
|
2545
|
+
{
|
|
2546
|
+
className: "june-doc__select june-doc__select--font",
|
|
2547
|
+
"aria-label": "Font family",
|
|
2548
|
+
disabled: off,
|
|
2549
|
+
value: _nullishCoalesce(_optionalChain([s, 'optionalAccess', _226 => _226.fontFamily]), () => ( "")),
|
|
2550
|
+
onChange: (e) => {
|
|
2551
|
+
const v = e.target.value;
|
|
2552
|
+
if (v) _optionalChain([run, 'call', _227 => _227(), 'optionalAccess', _228 => _228.setFontFamily, 'call', _229 => _229(v), 'access', _230 => _230.run, 'call', _231 => _231()]);
|
|
2553
|
+
else _optionalChain([run, 'call', _232 => _232(), 'optionalAccess', _233 => _233.unsetFontFamily, 'call', _234 => _234(), 'access', _235 => _235.run, 'call', _236 => _236()]);
|
|
2554
|
+
},
|
|
2555
|
+
children: [
|
|
2556
|
+
FONT_FAMILIES.map((f) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: f.value, style: { fontFamily: f.value || void 0 }, children: f.label }, f.label)),
|
|
2557
|
+
!fontKnown && _optionalChain([s, 'optionalAccess', _237 => _237.fontFamily]) ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: s.fontFamily, children: s.fontFamily.split(",")[0].replace(/['"]/g, "") }) : null
|
|
2558
|
+
]
|
|
2559
|
+
}
|
|
2560
|
+
) }),
|
|
2561
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Field, { label: "Size", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2562
|
+
"select",
|
|
2563
|
+
{
|
|
2564
|
+
className: "june-doc__select june-doc__select--size",
|
|
2565
|
+
"aria-label": "Font size",
|
|
2566
|
+
disabled: off,
|
|
2567
|
+
value: _nullishCoalesce(_optionalChain([s, 'optionalAccess', _238 => _238.fontSize]), () => ( "")),
|
|
2568
|
+
onChange: (e) => {
|
|
2569
|
+
const v = e.target.value;
|
|
2570
|
+
if (v) _optionalChain([run, 'call', _239 => _239(), 'optionalAccess', _240 => _240.setFontSize, 'call', _241 => _241(`${v}px`), 'access', _242 => _242.run, 'call', _243 => _243()]);
|
|
2571
|
+
else _optionalChain([run, 'call', _244 => _244(), 'optionalAccess', _245 => _245.unsetFontSize, 'call', _246 => _246(), 'access', _247 => _247.run, 'call', _248 => _248()]);
|
|
2572
|
+
},
|
|
2573
|
+
children: [
|
|
2574
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: "", children: "Auto" }),
|
|
2575
|
+
sizes.map((n) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: String(n), children: n }, n))
|
|
2576
|
+
]
|
|
2577
|
+
}
|
|
2578
|
+
) })
|
|
2579
|
+
] }),
|
|
2580
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Show formatting marks", icon: "pilcrow", toggle: true, active: showMarks, onClick: onToggleMarks, text: true, className: "june-doc__btn--marks", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: "Paragraph" }) }),
|
|
2581
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Group, { label: "Document", children: [
|
|
2582
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Insert page break", icon: "pageBreak", disabled: off, onClick: () => _optionalChain([run, 'call', _249 => _249(), 'optionalAccess', _250 => _250.setPageBreak, 'call', _251 => _251(), 'access', _252 => _252.run, 'call', _253 => _253()]) }),
|
|
2583
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Insert divider", icon: "divider", disabled: off, onClick: () => _optionalChain([run, 'call', _254 => _254(), 'optionalAccess', _255 => _255.setHorizontalRule, 'call', _256 => _256(), 'access', _257 => _257.run, 'call', _258 => _258()]) }),
|
|
2584
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: layout === "page" ? "Switch to full-width layout" : "Switch to page layout", icon: "layout", toggle: true, active: layout === "fluid", onClick: onToggleLayout }),
|
|
2585
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Find and replace", icon: "search", toggle: true, active: findOpen, onClick: onToggleFind })
|
|
2586
|
+
] }),
|
|
2587
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Group, { label: "Color", children: [
|
|
2588
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2589
|
+
ColorPicker,
|
|
2590
|
+
{
|
|
2591
|
+
label: "Text color",
|
|
2592
|
+
icon: "palette",
|
|
2593
|
+
colors: TEXT_COLORS,
|
|
2594
|
+
current: _nullishCoalesce(_optionalChain([s, 'optionalAccess', _259 => _259.color]), () => ( null)),
|
|
2595
|
+
disabled: off,
|
|
2596
|
+
resetLabel: "Default color",
|
|
2597
|
+
onPick: (c) => _optionalChain([run, 'call', _260 => _260(), 'optionalAccess', _261 => _261.setColor, 'call', _262 => _262(c), 'access', _263 => _263.run, 'call', _264 => _264()]),
|
|
2598
|
+
onReset: () => _optionalChain([run, 'call', _265 => _265(), 'optionalAccess', _266 => _266.unsetColor, 'call', _267 => _267(), 'access', _268 => _268.run, 'call', _269 => _269()])
|
|
2599
|
+
}
|
|
2600
|
+
),
|
|
2601
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2602
|
+
ColorPicker,
|
|
2603
|
+
{
|
|
2604
|
+
label: "Highlight",
|
|
2605
|
+
icon: "highlighter",
|
|
2606
|
+
colors: HIGHLIGHT_COLORS,
|
|
2607
|
+
current: _nullishCoalesce(_optionalChain([s, 'optionalAccess', _270 => _270.highlight]), () => ( null)),
|
|
2608
|
+
disabled: off,
|
|
2609
|
+
resetLabel: "No highlight",
|
|
2610
|
+
onPick: (c) => _optionalChain([run, 'call', _271 => _271(), 'optionalAccess', _272 => _272.setHighlight, 'call', _273 => _273({ color: c }), 'access', _274 => _274.run, 'call', _275 => _275()]),
|
|
2611
|
+
onReset: () => _optionalChain([run, 'call', _276 => _276(), 'optionalAccess', _277 => _277.unsetHighlight, 'call', _278 => _278(), 'access', _279 => _279.run, 'call', _280 => _280()])
|
|
2612
|
+
}
|
|
2613
|
+
)
|
|
2614
|
+
] }),
|
|
2615
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Group, { label: "Insert", children: [
|
|
2616
|
+
editor ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2617
|
+
Dropdown,
|
|
2618
|
+
{
|
|
2619
|
+
trigger: ({ open, toggle }) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Link", icon: "link", toggle: true, active: _optionalChain([s, 'optionalAccess', _281 => _281.link]) || open, disabled: off, onClick: toggle }),
|
|
2620
|
+
children: (close) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, LinkPanel, { editor, href: _nullishCoalesce(_optionalChain([s, 'optionalAccess', _282 => _282.linkHref]), () => ( "")), isLink: !!_optionalChain([s, 'optionalAccess', _283 => _283.link]), close })
|
|
2621
|
+
}
|
|
2622
|
+
) : null,
|
|
2623
|
+
editor ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Dropdown, { trigger: ({ open, toggle }) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Insert image", icon: "image", active: open, disabled: off, onClick: toggle }), children: (close) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ImagePanel, { editor, close, onNotice }) }) : null,
|
|
2624
|
+
editor ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Dropdown, { trigger: ({ open, toggle }) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Insert table", icon: "table", active: open, disabled: off, onClick: toggle }), children: (close) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TablePanel, { editor, close }) }) : null
|
|
2625
|
+
] }),
|
|
2626
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Group, { label: "Actions", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Clear formatting", icon: "eraser", disabled: off, onClick: () => _optionalChain([run, 'call', _284 => _284(), 'optionalAccess', _285 => _285.unsetAllMarks, 'call', _286 => _286(), 'access', _287 => _287.clearNodes, 'call', _288 => _288(), 'access', _289 => _289.run, 'call', _290 => _290()]) }) })
|
|
2627
|
+
] }) : null,
|
|
2628
|
+
_optionalChain([s, 'optionalAccess', _291 => _291.inTable]) ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-doc__row june-doc__row--context", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Group, { label: "Table", children: [
|
|
2629
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Add row below", disabled: off, onClick: () => _optionalChain([run, 'call', _292 => _292(), 'optionalAccess', _293 => _293.addRowAfter, 'call', _294 => _294(), 'access', _295 => _295.run, 'call', _296 => _296()]), children: "+ Row" }),
|
|
2630
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Add column right", disabled: off, onClick: () => _optionalChain([run, 'call', _297 => _297(), 'optionalAccess', _298 => _298.addColumnAfter, 'call', _299 => _299(), 'access', _300 => _300.run, 'call', _301 => _301()]), children: "+ Column" }),
|
|
2631
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Delete row", disabled: off, onClick: () => _optionalChain([run, 'call', _302 => _302(), 'optionalAccess', _303 => _303.deleteRow, 'call', _304 => _304(), 'access', _305 => _305.run, 'call', _306 => _306()]), children: "\u2212 Row" }),
|
|
2632
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Delete column", disabled: off, onClick: () => _optionalChain([run, 'call', _307 => _307(), 'optionalAccess', _308 => _308.deleteColumn, 'call', _309 => _309(), 'access', _310 => _310.run, 'call', _311 => _311()]), children: "\u2212 Column" }),
|
|
2633
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Merge cells", disabled: off || !_optionalChain([s, 'optionalAccess', _312 => _312.canMerge]), onClick: () => _optionalChain([run, 'call', _313 => _313(), 'optionalAccess', _314 => _314.mergeCells, 'call', _315 => _315(), 'access', _316 => _316.run, 'call', _317 => _317()]), children: "Merge" }),
|
|
2634
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Split cell", disabled: off || !_optionalChain([s, 'optionalAccess', _318 => _318.canSplit]), onClick: () => _optionalChain([run, 'call', _319 => _319(), 'optionalAccess', _320 => _320.splitCell, 'call', _321 => _321(), 'access', _322 => _322.run, 'call', _323 => _323()]), children: "Split" }),
|
|
2635
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Toggle header row", disabled: off, onClick: () => _optionalChain([run, 'call', _324 => _324(), 'optionalAccess', _325 => _325.toggleHeaderRow, 'call', _326 => _326(), 'access', _327 => _327.run, 'call', _328 => _328()]), children: "Header row" }),
|
|
2636
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Delete table", disabled: off, onClick: () => _optionalChain([run, 'call', _329 => _329(), 'optionalAccess', _330 => _330.deleteTable, 'call', _331 => _331(), 'access', _332 => _332.run, 'call', _333 => _333()]), className: "june-doc__btn--danger", children: "Delete table" })
|
|
2637
|
+
] }) }) : null
|
|
2638
|
+
] });
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
// src/features/document-editor/document-bubble.tsx
|
|
2642
|
+
|
|
2643
|
+
function DocumentBubbleMenu({ editor }) {
|
|
2644
|
+
const s = _react3.useEditorState.call(void 0, {
|
|
2645
|
+
editor,
|
|
2646
|
+
selector: ({ editor: ed }) => ({
|
|
2647
|
+
bold: ed.isActive("bold"),
|
|
2648
|
+
italic: ed.isActive("italic"),
|
|
2649
|
+
underline: ed.isActive("underline"),
|
|
2650
|
+
strike: ed.isActive("strike"),
|
|
2651
|
+
link: ed.isActive("link"),
|
|
2652
|
+
linkHref: _nullishCoalesce(ed.getAttributes("link").href, () => ( "")),
|
|
2653
|
+
color: _nullishCoalesce(ed.getAttributes("textStyle").color, () => ( null)),
|
|
2654
|
+
highlight: ed.isActive("highlight") ? _nullishCoalesce(ed.getAttributes("highlight").color, () => ( "#fef08a")) : null
|
|
2655
|
+
})
|
|
2656
|
+
});
|
|
2657
|
+
const run = () => editor.chain().focus();
|
|
2658
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2659
|
+
_menus.BubbleMenu,
|
|
2660
|
+
{
|
|
2661
|
+
editor,
|
|
2662
|
+
className: "june-doc__bubble",
|
|
2663
|
+
options: { placement: "top", offset: 8 },
|
|
2664
|
+
shouldShow: ({ editor: ed, state }) => {
|
|
2665
|
+
const { empty } = state.selection;
|
|
2666
|
+
if (empty || !ed.isEditable) return false;
|
|
2667
|
+
if (ed.isActive("image") || ed.isActive("pageBreak") || ed.isActive("codeBlock")) return false;
|
|
2668
|
+
return state.doc.textBetween(state.selection.from, state.selection.to).trim().length > 0;
|
|
2669
|
+
},
|
|
2670
|
+
children: [
|
|
2671
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Bold", icon: "bold", toggle: true, active: s.bold, onClick: () => run().toggleBold().run() }),
|
|
2672
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Italic", icon: "italic", toggle: true, active: s.italic, onClick: () => run().toggleItalic().run() }),
|
|
2673
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Underline", icon: "underline", toggle: true, active: s.underline, onClick: () => run().toggleUnderline().run() }),
|
|
2674
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Strikethrough", icon: "strike", toggle: true, active: s.strike, onClick: () => run().toggleStrike().run() }),
|
|
2675
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__bubble-sep", "aria-hidden": "true" }),
|
|
2676
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Dropdown, { trigger: ({ open, toggle }) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Link", icon: "link", toggle: true, active: s.link || open, onClick: toggle }), children: (close) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, LinkPanel, { editor, href: s.linkHref, isLink: s.link, close }) }),
|
|
2677
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2678
|
+
ColorPicker,
|
|
2679
|
+
{
|
|
2680
|
+
label: "Text color",
|
|
2681
|
+
icon: "palette",
|
|
2682
|
+
colors: TEXT_COLORS,
|
|
2683
|
+
current: s.color,
|
|
2684
|
+
resetLabel: "Default color",
|
|
2685
|
+
onPick: (c) => run().setColor(c).run(),
|
|
2686
|
+
onReset: () => run().unsetColor().run()
|
|
2687
|
+
}
|
|
2688
|
+
),
|
|
2689
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2690
|
+
ColorPicker,
|
|
2691
|
+
{
|
|
2692
|
+
label: "Highlight",
|
|
2693
|
+
icon: "highlighter",
|
|
2694
|
+
colors: HIGHLIGHT_COLORS,
|
|
2695
|
+
current: s.highlight,
|
|
2696
|
+
resetLabel: "No highlight",
|
|
2697
|
+
onPick: (c) => run().setHighlight({ color: c }).run(),
|
|
2698
|
+
onReset: () => run().unsetHighlight().run()
|
|
2699
|
+
}
|
|
2700
|
+
),
|
|
2701
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__bubble-sep", "aria-hidden": "true" }),
|
|
2702
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Btn, { label: "Clear formatting", icon: "eraser", onClick: () => run().unsetAllMarks().run() })
|
|
2703
|
+
]
|
|
2704
|
+
}
|
|
2705
|
+
);
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
// src/features/document-editor/document-content.tsx
|
|
2709
|
+
|
|
2710
|
+
|
|
2711
|
+
|
|
2712
|
+
var PAGE_WIDTH = 794;
|
|
2713
|
+
var PAGE_HEIGHT = 1123;
|
|
2714
|
+
var PAGE_GAP = 28;
|
|
2715
|
+
var CANVAS_PAD_X = 24;
|
|
2716
|
+
var useIsoLayoutEffect = typeof window === "undefined" ? _react.useEffect : _react.useLayoutEffect;
|
|
2717
|
+
function geometry(width) {
|
|
2718
|
+
const ratio = width / PAGE_WIDTH;
|
|
2719
|
+
const height = Math.round(PAGE_HEIGHT * ratio);
|
|
2720
|
+
const padX = Math.round(Math.min(96, Math.max(20, width * 0.12)));
|
|
2721
|
+
const padY = Math.round(Math.min(96, Math.max(36, 96 * ratio)));
|
|
2722
|
+
return { width, height, padX, padY };
|
|
2723
|
+
}
|
|
2724
|
+
function DocumentContent({ editor, layout = "page", zoom = 1, showMarks = false, onMetrics }) {
|
|
2725
|
+
const canvas = _react.useRef.call(void 0, null);
|
|
2726
|
+
const sheet = _react.useRef.call(void 0, null);
|
|
2727
|
+
const [avail, setAvail] = _react.useState.call(void 0, PAGE_WIDTH);
|
|
2728
|
+
const [sheetH, setSheetH] = _react.useState.call(void 0, 0);
|
|
2729
|
+
const [pages, setPages] = _react.useState.call(void 0, 1);
|
|
2730
|
+
const metricsRef = _react.useRef.call(void 0, onMetrics);
|
|
2731
|
+
metricsRef.current = onMetrics;
|
|
2732
|
+
const last = _react.useRef.call(void 0, { page: 0, pages: 0 });
|
|
2733
|
+
const paged = layout === "page";
|
|
2734
|
+
const width = paged ? Math.max(260, Math.min(PAGE_WIDTH, Math.floor(avail / zoom))) : Math.max(260, Math.floor(avail / zoom));
|
|
2735
|
+
const g = geometry(width);
|
|
2736
|
+
const stride = g.height + PAGE_GAP;
|
|
2737
|
+
useIsoLayoutEffect(() => {
|
|
2738
|
+
const c = canvas.current;
|
|
2739
|
+
if (!c) return;
|
|
2740
|
+
const read = () => {
|
|
2741
|
+
const cs = typeof getComputedStyle === "function" ? getComputedStyle(c) : null;
|
|
2742
|
+
const padX = cs ? (parseFloat(cs.paddingLeft) || 0) + (parseFloat(cs.paddingRight) || 0) : CANVAS_PAD_X * 2;
|
|
2743
|
+
const w = c.clientWidth - padX;
|
|
2744
|
+
if (w > 0) setAvail(w);
|
|
2745
|
+
};
|
|
2746
|
+
read();
|
|
2747
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
2748
|
+
const ro = new ResizeObserver(read);
|
|
2749
|
+
ro.observe(c);
|
|
2750
|
+
return () => ro.disconnect();
|
|
2751
|
+
}, []);
|
|
2752
|
+
_react.useEffect.call(void 0, () => {
|
|
2753
|
+
if (!editor) return;
|
|
2754
|
+
_chunkG6YPSE6Jcjs.setPaginationConfig.call(void 0, editor, {
|
|
2755
|
+
enabled: paged,
|
|
2756
|
+
pageHeight: g.height,
|
|
2757
|
+
gap: PAGE_GAP,
|
|
2758
|
+
padTop: g.padY,
|
|
2759
|
+
padBottom: g.padY
|
|
2760
|
+
});
|
|
2761
|
+
}, [editor, paged, g.height, g.padY, width]);
|
|
2762
|
+
_react.useEffect.call(void 0, () => {
|
|
2763
|
+
const c = canvas.current;
|
|
2764
|
+
const s = sheet.current;
|
|
2765
|
+
if (!editor || !c || !s) return;
|
|
2766
|
+
const prose = editor.view.dom;
|
|
2767
|
+
const report = () => {
|
|
2768
|
+
const total = paged ? _chunkG6YPSE6Jcjs.getPageCount.call(void 0, editor) : 1;
|
|
2769
|
+
setPages(total);
|
|
2770
|
+
setSheetH(s.offsetHeight);
|
|
2771
|
+
const cRect = c.getBoundingClientRect();
|
|
2772
|
+
const sRect = s.getBoundingClientRect();
|
|
2773
|
+
const y = (cRect.top + c.clientHeight * 0.35 - sRect.top) / zoom;
|
|
2774
|
+
const page = paged ? Math.min(total, Math.max(1, Math.floor(y / stride) + 1)) : 1;
|
|
2775
|
+
if (last.current.page !== page || last.current.pages !== total) {
|
|
2776
|
+
last.current = { page, pages: total };
|
|
2777
|
+
_optionalChain([metricsRef, 'access', _334 => _334.current, 'optionalCall', _335 => _335({ page, pages: total })]);
|
|
2778
|
+
}
|
|
2779
|
+
};
|
|
2780
|
+
report();
|
|
2781
|
+
editor.on("transaction", report);
|
|
2782
|
+
c.addEventListener("scroll", report, { passive: true });
|
|
2783
|
+
const onLoad = () => _chunkG6YPSE6Jcjs.remeasurePagination.call(void 0, editor);
|
|
2784
|
+
prose.addEventListener("load", onLoad, true);
|
|
2785
|
+
const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(report) : null;
|
|
2786
|
+
_optionalChain([ro, 'optionalAccess', _336 => _336.observe, 'call', _337 => _337(s)]);
|
|
2787
|
+
return () => {
|
|
2788
|
+
editor.off("transaction", report);
|
|
2789
|
+
c.removeEventListener("scroll", report);
|
|
2790
|
+
prose.removeEventListener("load", onLoad, true);
|
|
2791
|
+
_optionalChain([ro, 'optionalAccess', _338 => _338.disconnect, 'call', _339 => _339()]);
|
|
2792
|
+
};
|
|
2793
|
+
}, [editor, paged, zoom, stride]);
|
|
2794
|
+
const minHeight = paged ? pages * g.height + (pages - 1) * PAGE_GAP : void 0;
|
|
2795
|
+
const sheetStyle = {
|
|
2796
|
+
width,
|
|
2797
|
+
minHeight,
|
|
2798
|
+
transform: zoom === 1 ? void 0 : `scale(${zoom})`,
|
|
2799
|
+
"--jd-pad-x": `${g.padX}px`,
|
|
2800
|
+
"--jd-pad-y": `${g.padY}px`
|
|
2801
|
+
};
|
|
2802
|
+
const zoomerStyle = {
|
|
2803
|
+
width: width * zoom,
|
|
2804
|
+
height: zoom === 1 ? void 0 : Math.max(sheetH, _nullishCoalesce(minHeight, () => ( 0))) * zoom
|
|
2805
|
+
};
|
|
2806
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { ref: canvas, className: "june-doc__content june-doc__canvas", "data-layout": layout, "data-marks": showMarks || void 0, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-doc__zoomer", style: zoomerStyle, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { ref: sheet, className: "june-doc__sheet", style: sheetStyle, children: [
|
|
2807
|
+
paged ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-doc__pages", "aria-hidden": "true", children: Array.from({ length: pages }, (_, i) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-doc__page", style: { top: i * stride, height: g.height }, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "june-doc__page-num", children: [
|
|
2808
|
+
i + 1,
|
|
2809
|
+
" / ",
|
|
2810
|
+
pages
|
|
2811
|
+
] }) }, i)) }) : null,
|
|
2812
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _react3.EditorContent, { editor })
|
|
2813
|
+
] }) }) });
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
// src/features/document-editor/document-findbar.tsx
|
|
2817
|
+
|
|
2818
|
+
|
|
2819
|
+
|
|
2820
|
+
function DocumentFindBar({ editor, editable, onClose, onNotice }) {
|
|
2821
|
+
const [term, setTerm] = _react.useState.call(void 0, () => {
|
|
2822
|
+
const { from, to, empty } = editor.state.selection;
|
|
2823
|
+
return empty ? "" : editor.state.doc.textBetween(from, to, " ").slice(0, 100);
|
|
2824
|
+
});
|
|
2825
|
+
const [replacement, setReplacement] = _react.useState.call(void 0, "");
|
|
2826
|
+
const [caseSensitive, setCaseSensitive] = _react.useState.call(void 0, false);
|
|
2827
|
+
const input = _react.useRef.call(void 0, null);
|
|
2828
|
+
const info = _react3.useEditorState.call(void 0, {
|
|
2829
|
+
editor,
|
|
2830
|
+
selector: ({ editor: ed }) => {
|
|
2831
|
+
const s = _chunkG6YPSE6Jcjs.getSearchState.call(void 0, ed);
|
|
2832
|
+
return { count: _nullishCoalesce(_optionalChain([s, 'optionalAccess', _340 => _340.matches, 'access', _341 => _341.length]), () => ( 0)), current: _nullishCoalesce(_optionalChain([s, 'optionalAccess', _342 => _342.current]), () => ( -1)) };
|
|
2833
|
+
}
|
|
2834
|
+
});
|
|
2835
|
+
_react.useEffect.call(void 0, () => {
|
|
2836
|
+
_optionalChain([input, 'access', _343 => _343.current, 'optionalAccess', _344 => _344.focus, 'call', _345 => _345()]);
|
|
2837
|
+
_optionalChain([input, 'access', _346 => _346.current, 'optionalAccess', _347 => _347.select, 'call', _348 => _348()]);
|
|
2838
|
+
}, []);
|
|
2839
|
+
_react.useEffect.call(void 0, () => {
|
|
2840
|
+
_chunkG6YPSE6Jcjs.setSearch.call(void 0, editor, { term, caseSensitive, current: 0 });
|
|
2841
|
+
}, [editor, term, caseSensitive]);
|
|
2842
|
+
_react.useEffect.call(void 0, () => () => {
|
|
2843
|
+
if (!editor.isDestroyed) _chunkG6YPSE6Jcjs.setSearch.call(void 0, editor, { term: "" });
|
|
2844
|
+
}, [editor]);
|
|
2845
|
+
const count = _nullishCoalesce(_optionalChain([info, 'optionalAccess', _349 => _349.count]), () => ( 0));
|
|
2846
|
+
const current = _nullishCoalesce(_optionalChain([info, 'optionalAccess', _350 => _350.current]), () => ( -1));
|
|
2847
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2848
|
+
"div",
|
|
2849
|
+
{
|
|
2850
|
+
className: "june-doc__findbar",
|
|
2851
|
+
role: "search",
|
|
2852
|
+
onKeyDown: (e) => {
|
|
2853
|
+
if (e.key === "Escape") {
|
|
2854
|
+
e.stopPropagation();
|
|
2855
|
+
onClose();
|
|
2856
|
+
editor.commands.focus();
|
|
2857
|
+
}
|
|
2858
|
+
},
|
|
2859
|
+
children: [
|
|
2860
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__find-field", children: [
|
|
2861
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "search", size: 14 }),
|
|
2862
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2863
|
+
"input",
|
|
2864
|
+
{
|
|
2865
|
+
ref: input,
|
|
2866
|
+
className: "june-doc__find-input",
|
|
2867
|
+
"aria-label": "Find in document",
|
|
2868
|
+
placeholder: "Find in document",
|
|
2869
|
+
value: term,
|
|
2870
|
+
onChange: (e) => setTerm(e.target.value),
|
|
2871
|
+
onKeyDown: (e) => {
|
|
2872
|
+
if (e.key === "Enter") {
|
|
2873
|
+
e.preventDefault();
|
|
2874
|
+
_chunkG6YPSE6Jcjs.gotoMatch.call(void 0, editor, e.shiftKey ? -1 : 1);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
),
|
|
2879
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__find-count", "aria-live": "polite", children: term ? count ? `${current + 1} of ${count}` : "No results" : "" })
|
|
2880
|
+
] }),
|
|
2881
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn june-doc__btn--icon", "aria-label": "Previous match", title: "Previous (Shift+Enter)", disabled: !count, onClick: () => _chunkG6YPSE6Jcjs.gotoMatch.call(void 0, editor, -1), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "chevronUp" }) }),
|
|
2882
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn june-doc__btn--icon", "aria-label": "Next match", title: "Next (Enter)", disabled: !count, onClick: () => _chunkG6YPSE6Jcjs.gotoMatch.call(void 0, editor, 1), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "chevronDown" }) }),
|
|
2883
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2884
|
+
"button",
|
|
2885
|
+
{
|
|
2886
|
+
type: "button",
|
|
2887
|
+
className: "june-doc__btn june-doc__btn--icon june-doc__btn--case",
|
|
2888
|
+
"aria-label": "Match case",
|
|
2889
|
+
title: "Match case",
|
|
2890
|
+
"aria-pressed": caseSensitive,
|
|
2891
|
+
"data-active": caseSensitive || void 0,
|
|
2892
|
+
onClick: () => setCaseSensitive((v) => !v),
|
|
2893
|
+
children: "Aa"
|
|
2894
|
+
}
|
|
2895
|
+
),
|
|
2896
|
+
editable ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
2897
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__find-sep", "aria-hidden": "true" }),
|
|
2898
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2899
|
+
"input",
|
|
2900
|
+
{
|
|
2901
|
+
className: "june-doc__input june-doc__replace-input",
|
|
2902
|
+
"aria-label": "Replace with",
|
|
2903
|
+
placeholder: "Replace with",
|
|
2904
|
+
value: replacement,
|
|
2905
|
+
onChange: (e) => setReplacement(e.target.value),
|
|
2906
|
+
onKeyDown: (e) => {
|
|
2907
|
+
if (e.key === "Enter") {
|
|
2908
|
+
e.preventDefault();
|
|
2909
|
+
_chunkG6YPSE6Jcjs.replaceCurrent.call(void 0, editor, replacement);
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
),
|
|
2914
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn", disabled: !count, onClick: () => _chunkG6YPSE6Jcjs.replaceCurrent.call(void 0, editor, replacement), children: "Replace" }),
|
|
2915
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2916
|
+
"button",
|
|
2917
|
+
{
|
|
2918
|
+
type: "button",
|
|
2919
|
+
className: "june-doc__btn",
|
|
2920
|
+
disabled: !count,
|
|
2921
|
+
onClick: () => {
|
|
2922
|
+
const n = _chunkG6YPSE6Jcjs.replaceAll.call(void 0, editor, replacement);
|
|
2923
|
+
onNotice(`Replaced ${n} ${n === 1 ? "match" : "matches"}`);
|
|
2924
|
+
},
|
|
2925
|
+
children: "Replace all"
|
|
2926
|
+
}
|
|
2927
|
+
)
|
|
2928
|
+
] }) : null,
|
|
2929
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost june-doc__find-close", "aria-label": "Close find", onClick: onClose, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "close" }) })
|
|
2930
|
+
]
|
|
2931
|
+
}
|
|
2932
|
+
);
|
|
2933
|
+
}
|
|
2934
|
+
|
|
2935
|
+
// src/features/document-editor/document-title.tsx
|
|
2936
|
+
|
|
2937
|
+
function DocumentTitle({ value, disabled, onChange }) {
|
|
2938
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2939
|
+
"input",
|
|
2940
|
+
{
|
|
2941
|
+
className: "june-doc__title",
|
|
2942
|
+
"aria-label": "Document title",
|
|
2943
|
+
value,
|
|
2944
|
+
disabled,
|
|
2945
|
+
placeholder: "Untitled document",
|
|
2946
|
+
onChange: (e) => onChange(e.target.value)
|
|
2947
|
+
}
|
|
2948
|
+
);
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2951
|
+
// src/features/document-editor/document-header.tsx
|
|
2952
|
+
|
|
2953
|
+
var FORMATS = {
|
|
2954
|
+
docx: { label: "Word document", hint: ".docx", icon: "file" },
|
|
2955
|
+
pdf: { label: "PDF", hint: "Print / Save as PDF", icon: "file" },
|
|
2956
|
+
html: { label: "Web page", hint: ".html", icon: "code" },
|
|
2957
|
+
markdown: { label: "Markdown", hint: ".md", icon: "file" },
|
|
2958
|
+
txt: { label: "Plain text", hint: ".txt", icon: "file" }
|
|
2959
|
+
};
|
|
2960
|
+
function DocumentHeader(props) {
|
|
2961
|
+
const { title, editable, isDraft, fullscreen, onToggleFullscreen, onTitle, exportFormats, canSave, showSave = canSave, saveState, onExport, onCopy, onSave } = props;
|
|
2962
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__header", children: [
|
|
2963
|
+
fullscreen ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost", "aria-label": "Close full screen", title: "Close (Esc)", onClick: onToggleFullscreen, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "close", size: 18 }) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__doc-icon", "aria-hidden": "true", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "file", size: 16 }) }),
|
|
2964
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, DocumentTitle, { value: title, disabled: !editable, onChange: onTitle }),
|
|
2965
|
+
isDraft ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "june-doc__badge", children: [
|
|
2966
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "sparkles", size: 12 }),
|
|
2967
|
+
"AI draft"
|
|
2968
|
+
] }) : null,
|
|
2969
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__header-actions", children: [
|
|
2970
|
+
showSave ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2971
|
+
"button",
|
|
2972
|
+
{
|
|
2973
|
+
type: "button",
|
|
2974
|
+
className: "june-doc__btn june-doc__btn--outline june-doc__save",
|
|
2975
|
+
"data-state": saveState,
|
|
2976
|
+
"aria-label": "Save",
|
|
2977
|
+
title: "Save (Ctrl+S)",
|
|
2978
|
+
disabled: saveState === "saving" || saveState === "saved" || saveState === "idle",
|
|
2979
|
+
onClick: onSave,
|
|
2980
|
+
children: [
|
|
2981
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: saveState === "saved" ? "check" : "save" }),
|
|
2982
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: saveState === "saving" ? "Saving\u2026" : saveState === "saved" ? "Saved" : "Save" })
|
|
2983
|
+
]
|
|
2984
|
+
}
|
|
2985
|
+
) : null,
|
|
2986
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2987
|
+
Dropdown,
|
|
2988
|
+
{
|
|
2989
|
+
align: "end",
|
|
2990
|
+
className: "june-doc__menu",
|
|
2991
|
+
trigger: ({ open, toggle }) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
2992
|
+
"button",
|
|
2993
|
+
{
|
|
2994
|
+
type: "button",
|
|
2995
|
+
className: "june-doc__btn june-doc__btn--solid",
|
|
2996
|
+
"aria-label": "Download",
|
|
2997
|
+
"aria-haspopup": "menu",
|
|
2998
|
+
"aria-expanded": open,
|
|
2999
|
+
onClick: toggle,
|
|
3000
|
+
children: [
|
|
3001
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: "Download" }),
|
|
3002
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "chevronDown", size: 14 })
|
|
3003
|
+
]
|
|
3004
|
+
}
|
|
3005
|
+
),
|
|
3006
|
+
children: (close) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { role: "menu", "aria-label": "Download options", children: [
|
|
3007
|
+
exportFormats.map((f) => {
|
|
3008
|
+
const meta = _nullishCoalesce(FORMATS[f], () => ( { label: f.toUpperCase(), hint: "", icon: "file" }));
|
|
3009
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
3010
|
+
"button",
|
|
3011
|
+
{
|
|
3012
|
+
type: "button",
|
|
3013
|
+
role: "menuitem",
|
|
3014
|
+
className: "june-doc__menu-item",
|
|
3015
|
+
onClick: () => {
|
|
3016
|
+
close();
|
|
3017
|
+
onExport(f);
|
|
3018
|
+
},
|
|
3019
|
+
children: [
|
|
3020
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: meta.icon }),
|
|
3021
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__menu-label", children: meta.label }),
|
|
3022
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__menu-hint", children: meta.hint })
|
|
3023
|
+
]
|
|
3024
|
+
},
|
|
3025
|
+
f
|
|
3026
|
+
);
|
|
3027
|
+
}),
|
|
3028
|
+
exportFormats.length ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-doc__menu-sep", role: "separator" }) : null,
|
|
3029
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
3030
|
+
"button",
|
|
3031
|
+
{
|
|
3032
|
+
type: "button",
|
|
3033
|
+
role: "menuitem",
|
|
3034
|
+
className: "june-doc__menu-item",
|
|
3035
|
+
"aria-label": "Copy document",
|
|
3036
|
+
onClick: () => {
|
|
3037
|
+
close();
|
|
3038
|
+
onCopy();
|
|
3039
|
+
},
|
|
3040
|
+
children: [
|
|
3041
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "copy" }),
|
|
3042
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__menu-label", children: "Copy to clipboard" })
|
|
3043
|
+
]
|
|
3044
|
+
}
|
|
3045
|
+
)
|
|
3046
|
+
] })
|
|
3047
|
+
}
|
|
3048
|
+
),
|
|
3049
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
3050
|
+
"button",
|
|
3051
|
+
{
|
|
3052
|
+
type: "button",
|
|
3053
|
+
className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost",
|
|
3054
|
+
"aria-label": fullscreen ? "Exit full screen" : "Open full screen",
|
|
3055
|
+
title: fullscreen ? "Exit full screen" : "Open full screen",
|
|
3056
|
+
"aria-pressed": fullscreen,
|
|
3057
|
+
onClick: onToggleFullscreen,
|
|
3058
|
+
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "maximize" })
|
|
3059
|
+
}
|
|
3060
|
+
)
|
|
3061
|
+
] })
|
|
3062
|
+
] });
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
// src/features/document-editor/document-export.ts
|
|
3066
|
+
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3067
|
+
var PRINT_CSS = 'body{font-family:system-ui,-apple-system,"Segoe UI",sans-serif;line-height:1.6;color:#111827;max-width:780px;margin:32px auto;padding:0 20px}h1,h2,h3{line-height:1.25}table{border-collapse:collapse;width:100%}th,td{border:1px solid #d1d5db;padding:6px 10px;text-align:left}blockquote{border-left:4px solid #d1d5db;margin:1em 0;padding-left:1em;color:#4b5563}pre,code{font-family:ui-monospace,Menlo,monospace;background:#f3f4f6}pre{padding:12px;overflow:auto}img{max-width:100%;height:auto}mark{padding:0 .1em;border-radius:2px}div[data-page-break]{break-after:page;page-break-after:always;height:0}@media screen{div[data-page-break]{border-top:1px dashed #d1d5db;margin:24px 0}}@media print{body{margin:0;max-width:none}}';
|
|
3068
|
+
function buildHtmlDocument(title, bodyHtml) {
|
|
3069
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${escapeHtml(
|
|
3070
|
+
title
|
|
3071
|
+
)}</title><style>${PRINT_CSS}</style></head><body><h1>${escapeHtml(title)}</h1>${bodyHtml}</body></html>`;
|
|
3072
|
+
}
|
|
3073
|
+
var textResult = (text, filename, mimeType) => ({
|
|
3074
|
+
filename,
|
|
3075
|
+
mimeType,
|
|
3076
|
+
text,
|
|
3077
|
+
blob: new Blob([text], { type: `${mimeType};charset=utf-8` })
|
|
3078
|
+
});
|
|
3079
|
+
var exportHtml = async (ctx) => {
|
|
3080
|
+
const { docToHtml: docToHtml2 } = await Promise.resolve().then(() => _interopRequireWildcard(require("./document-engine-GXZH2LNW.cjs")));
|
|
3081
|
+
return textResult(
|
|
3082
|
+
buildHtmlDocument(ctx.title, docToHtml2(ctx.json)),
|
|
3083
|
+
`${ctx.basename}.html`,
|
|
3084
|
+
"text/html"
|
|
3085
|
+
);
|
|
3086
|
+
};
|
|
3087
|
+
var exportMarkdown = async (ctx) => {
|
|
3088
|
+
const { docToMarkdown: docToMarkdown2 } = await Promise.resolve().then(() => _interopRequireWildcard(require("./document-engine-GXZH2LNW.cjs")));
|
|
3089
|
+
return textResult(
|
|
3090
|
+
`# ${ctx.title}
|
|
3091
|
+
|
|
3092
|
+
${docToMarkdown2(ctx.json)}
|
|
3093
|
+
`,
|
|
3094
|
+
`${ctx.basename}.md`,
|
|
3095
|
+
"text/markdown"
|
|
3096
|
+
);
|
|
3097
|
+
};
|
|
3098
|
+
var exportTxt = async (ctx) => {
|
|
3099
|
+
const { docToText } = await Promise.resolve().then(() => _interopRequireWildcard(require("./document-engine-GXZH2LNW.cjs")));
|
|
3100
|
+
return textResult(`${ctx.title}
|
|
3101
|
+
|
|
3102
|
+
${docToText(ctx.json)}
|
|
3103
|
+
`, `${ctx.basename}.txt`, "text/plain");
|
|
3104
|
+
};
|
|
3105
|
+
function toDocxColor(value) {
|
|
3106
|
+
if (typeof value !== "string") return void 0;
|
|
3107
|
+
const v = value.trim();
|
|
3108
|
+
let m = /^#([0-9a-f]{3})$/i.exec(v);
|
|
3109
|
+
if (m) return m[1].split("").map((c) => c + c).join("").toUpperCase();
|
|
3110
|
+
m = /^#([0-9a-f]{6})$/i.exec(v);
|
|
3111
|
+
if (m) return m[1].toUpperCase();
|
|
3112
|
+
m = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i.exec(v);
|
|
3113
|
+
if (m) {
|
|
3114
|
+
return [m[1], m[2], m[3]].map((n) => Math.min(255, Number(n)).toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
3115
|
+
}
|
|
3116
|
+
return void 0;
|
|
3117
|
+
}
|
|
3118
|
+
function toHalfPoints(value) {
|
|
3119
|
+
if (typeof value !== "string") return void 0;
|
|
3120
|
+
const m = /^([\d.]+)\s*(px|pt)?$/i.exec(value.trim());
|
|
3121
|
+
if (!m) return void 0;
|
|
3122
|
+
const n = Number(m[1]);
|
|
3123
|
+
if (!Number.isFinite(n) || n <= 0) return void 0;
|
|
3124
|
+
const pt = _optionalChain([m, 'access', _351 => _351[2], 'optionalAccess', _352 => _352.toLowerCase, 'call', _353 => _353()]) === "pt" ? n : n * 0.75;
|
|
3125
|
+
return Math.round(pt * 2);
|
|
3126
|
+
}
|
|
3127
|
+
function firstFontFamily(value) {
|
|
3128
|
+
if (typeof value !== "string") return void 0;
|
|
3129
|
+
const first = _optionalChain([value, 'access', _354 => _354.split, 'call', _355 => _355(","), 'access', _356 => _356[0], 'optionalAccess', _357 => _357.trim, 'call', _358 => _358(), 'access', _359 => _359.replace, 'call', _360 => _360(/^['"]|['"]$/g, "")]);
|
|
3130
|
+
return first || void 0;
|
|
3131
|
+
}
|
|
3132
|
+
var ORDERED_REF = "june-ordered";
|
|
3133
|
+
var DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
3134
|
+
function runs(d, node, fmt) {
|
|
3135
|
+
const out = [];
|
|
3136
|
+
for (const child of _nullishCoalesce(node.content, () => ( []))) {
|
|
3137
|
+
if (child.type === "hardBreak") {
|
|
3138
|
+
out.push(new d.TextRun({ break: 1 }));
|
|
3139
|
+
continue;
|
|
3140
|
+
}
|
|
3141
|
+
if (child.type !== "text" || !child.text) continue;
|
|
3142
|
+
const marks = _nullishCoalesce(child.marks, () => ( []));
|
|
3143
|
+
const has = (type) => marks.some((m) => m.type === type);
|
|
3144
|
+
const link = marks.find((m) => m.type === "link");
|
|
3145
|
+
const style = _nullishCoalesce(_optionalChain([marks, 'access', _361 => _361.find, 'call', _362 => _362((m) => m.type === "textStyle"), 'optionalAccess', _363 => _363.attrs]), () => ( {}));
|
|
3146
|
+
const highlight = marks.find((m) => m.type === "highlight");
|
|
3147
|
+
const fill = highlight ? _nullishCoalesce(toDocxColor(_optionalChain([highlight, 'access', _364 => _364.attrs, 'optionalAccess', _365 => _365.color])), () => ( "FFF59D")) : void 0;
|
|
3148
|
+
const run = new d.TextRun({
|
|
3149
|
+
text: child.text,
|
|
3150
|
+
bold: fmt.bold || has("bold") || void 0,
|
|
3151
|
+
italics: has("italic") || void 0,
|
|
3152
|
+
underline: has("underline") || link ? {} : void 0,
|
|
3153
|
+
strike: has("strike") || void 0,
|
|
3154
|
+
superScript: has("superscript") || void 0,
|
|
3155
|
+
subScript: has("subscript") || void 0,
|
|
3156
|
+
font: has("code") ? "Courier New" : firstFontFamily(style.fontFamily),
|
|
3157
|
+
size: toHalfPoints(style.fontSize),
|
|
3158
|
+
color: _nullishCoalesce(toDocxColor(style.color), () => ( (link ? "0563C1" : void 0))),
|
|
3159
|
+
shading: fill ? { type: d.ShadingType.CLEAR, fill, color: "auto" } : void 0
|
|
3160
|
+
});
|
|
3161
|
+
const href = typeof _optionalChain([link, 'optionalAccess', _366 => _366.attrs, 'optionalAccess', _367 => _367.href]) === "string" ? _chunkG6YPSE6Jcjs.sanitizeHref.call(void 0, link.attrs.href) : null;
|
|
3162
|
+
out.push(href ? new d.ExternalHyperlink({ link: href, children: [run] }) : run);
|
|
3163
|
+
}
|
|
3164
|
+
return out;
|
|
3165
|
+
}
|
|
3166
|
+
function paragraph(d, node, fmt, extra = {}) {
|
|
3167
|
+
const align = _optionalChain([node, 'access', _368 => _368.attrs, 'optionalAccess', _369 => _369.textAlign]);
|
|
3168
|
+
return new d.Paragraph({
|
|
3169
|
+
children: runs(d, node, fmt),
|
|
3170
|
+
alignment: align === "center" ? d.AlignmentType.CENTER : align === "right" ? d.AlignmentType.RIGHT : align === "justify" ? d.AlignmentType.JUSTIFIED : void 0,
|
|
3171
|
+
indent: fmt.indent ? { left: fmt.indent } : void 0,
|
|
3172
|
+
border: fmt.quote ? { left: { color: "9CA3AF", space: 8, style: d.BorderStyle.SINGLE, size: 12 } } : void 0,
|
|
3173
|
+
spacing: { after: 120 },
|
|
3174
|
+
...extra
|
|
3175
|
+
});
|
|
3176
|
+
}
|
|
3177
|
+
function list(d, node, fmt, level, state) {
|
|
3178
|
+
const ordered = node.type === "orderedList";
|
|
3179
|
+
const instance = ordered ? ++state.instance : 0;
|
|
3180
|
+
const out = [];
|
|
3181
|
+
for (const item of _nullishCoalesce(node.content, () => ( []))) {
|
|
3182
|
+
let first = true;
|
|
3183
|
+
for (const child of _nullishCoalesce(item.content, () => ( []))) {
|
|
3184
|
+
if (child.type === "bulletList" || child.type === "orderedList") {
|
|
3185
|
+
out.push(...list(d, child, fmt, level + 1, state));
|
|
3186
|
+
} else if (child.type === "paragraph" && first) {
|
|
3187
|
+
first = false;
|
|
3188
|
+
const lvl = Math.min(level, 2);
|
|
3189
|
+
out.push(
|
|
3190
|
+
paragraph(
|
|
3191
|
+
d,
|
|
3192
|
+
child,
|
|
3193
|
+
fmt,
|
|
3194
|
+
ordered ? { numbering: { reference: ORDERED_REF, level: lvl, instance } } : { bullet: { level: lvl } }
|
|
3195
|
+
)
|
|
3196
|
+
);
|
|
3197
|
+
} else {
|
|
3198
|
+
out.push(...blocks(d, [child], { ...fmt, indent: (_nullishCoalesce(fmt.indent, () => ( 0))) + 720 * (level + 1) }, state));
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
return out;
|
|
3203
|
+
}
|
|
3204
|
+
function code(d, node) {
|
|
3205
|
+
const lines = (_nullishCoalesce(node.content, () => ( []))).map((c) => _nullishCoalesce(c.text, () => ( ""))).join("").split("\n");
|
|
3206
|
+
return new d.Paragraph({
|
|
3207
|
+
children: lines.map((line, i) => new d.TextRun({ text: line, font: "Courier New", break: i ? 1 : void 0 })),
|
|
3208
|
+
spacing: { after: 120 },
|
|
3209
|
+
shading: { type: d.ShadingType.CLEAR, fill: "F3F4F6" }
|
|
3210
|
+
});
|
|
3211
|
+
}
|
|
3212
|
+
function table(d, node, state) {
|
|
3213
|
+
const rows = (_nullishCoalesce(node.content, () => ( []))).map(
|
|
3214
|
+
(row) => new d.TableRow({
|
|
3215
|
+
children: (_nullishCoalesce(row.content, () => ( []))).map((cell) => {
|
|
3216
|
+
const header = cell.type === "tableHeader";
|
|
3217
|
+
const children = blocks(d, cell.content, { bold: header }, state);
|
|
3218
|
+
return new d.TableCell({
|
|
3219
|
+
children: children.length ? children : [new d.Paragraph("")],
|
|
3220
|
+
shading: header ? { type: d.ShadingType.CLEAR, fill: "F3F4F6" } : void 0
|
|
3221
|
+
});
|
|
3222
|
+
})
|
|
3223
|
+
})
|
|
3224
|
+
);
|
|
3225
|
+
if (rows.length === 0) return new d.Paragraph("");
|
|
3226
|
+
return new d.Table({ rows, width: { size: 100, type: d.WidthType.PERCENTAGE } });
|
|
3227
|
+
}
|
|
3228
|
+
function image(d, node, state) {
|
|
3229
|
+
const src = typeof _optionalChain([node, 'access', _370 => _370.attrs, 'optionalAccess', _371 => _371.src]) === "string" ? node.attrs.src : "";
|
|
3230
|
+
const alt = typeof _optionalChain([node, 'access', _372 => _372.attrs, 'optionalAccess', _373 => _373.alt]) === "string" ? node.attrs.alt : "";
|
|
3231
|
+
const img = state.images.get(src);
|
|
3232
|
+
if (img) {
|
|
3233
|
+
return [
|
|
3234
|
+
new d.Paragraph({
|
|
3235
|
+
children: [
|
|
3236
|
+
new d.ImageRun({
|
|
3237
|
+
type: img.type,
|
|
3238
|
+
data: img.data,
|
|
3239
|
+
transformation: { width: img.width, height: img.height },
|
|
3240
|
+
altText: alt ? { name: alt, description: alt, title: alt } : void 0
|
|
3241
|
+
})
|
|
3242
|
+
],
|
|
3243
|
+
spacing: { after: 120 }
|
|
3244
|
+
})
|
|
3245
|
+
];
|
|
3246
|
+
}
|
|
3247
|
+
const href = src && !src.startsWith("data:") ? _chunkG6YPSE6Jcjs.sanitizeHref.call(void 0, src) : null;
|
|
3248
|
+
if (!href) return [];
|
|
3249
|
+
return [
|
|
3250
|
+
new d.Paragraph({
|
|
3251
|
+
children: [
|
|
3252
|
+
new d.ExternalHyperlink({
|
|
3253
|
+
link: href,
|
|
3254
|
+
children: [new d.TextRun({ text: alt || href, color: "0563C1", underline: {} })]
|
|
3255
|
+
})
|
|
3256
|
+
]
|
|
3257
|
+
})
|
|
3258
|
+
];
|
|
3259
|
+
}
|
|
3260
|
+
function collectImages(nodes, out) {
|
|
3261
|
+
for (const node of _nullishCoalesce(nodes, () => ( []))) {
|
|
3262
|
+
const src = _optionalChain([node, 'access', _374 => _374.attrs, 'optionalAccess', _375 => _375.src]);
|
|
3263
|
+
if (node.type === "image" && typeof src === "string" && src && !out.has(src)) out.set(src, node.attrs);
|
|
3264
|
+
if (node.content) collectImages(node.content, out);
|
|
3265
|
+
}
|
|
3266
|
+
return out;
|
|
3267
|
+
}
|
|
3268
|
+
var IMAGE_TYPES = {
|
|
3269
|
+
"image/png": "png",
|
|
3270
|
+
"image/jpeg": "jpg",
|
|
3271
|
+
"image/jpg": "jpg",
|
|
3272
|
+
"image/gif": "gif",
|
|
3273
|
+
"image/bmp": "bmp"
|
|
3274
|
+
};
|
|
3275
|
+
var MAX_IMAGE_WIDTH = 600;
|
|
3276
|
+
function naturalSize(blob) {
|
|
3277
|
+
const fallback = { width: 400, height: 300 };
|
|
3278
|
+
return new Promise((resolve) => {
|
|
3279
|
+
if (typeof Image === "undefined" || typeof URL.createObjectURL !== "function") return resolve(fallback);
|
|
3280
|
+
const url = URL.createObjectURL(blob);
|
|
3281
|
+
const img = new Image();
|
|
3282
|
+
const done = (size) => {
|
|
3283
|
+
URL.revokeObjectURL(url);
|
|
3284
|
+
resolve(size);
|
|
3285
|
+
};
|
|
3286
|
+
img.onload = () => done({ width: img.naturalWidth || 400, height: img.naturalHeight || 300 });
|
|
3287
|
+
img.onerror = () => done(fallback);
|
|
3288
|
+
img.src = url;
|
|
3289
|
+
});
|
|
3290
|
+
}
|
|
3291
|
+
async function loadImage(src, attrs) {
|
|
3292
|
+
try {
|
|
3293
|
+
const res = await fetch(src);
|
|
3294
|
+
if (!res.ok) return null;
|
|
3295
|
+
const blob = await res.blob();
|
|
3296
|
+
const type = IMAGE_TYPES[blob.type.toLowerCase()];
|
|
3297
|
+
if (!type) return null;
|
|
3298
|
+
const natural = await naturalSize(blob);
|
|
3299
|
+
let width = Number(_optionalChain([attrs, 'optionalAccess', _376 => _376.width])) || natural.width;
|
|
3300
|
+
let height = Number(_optionalChain([attrs, 'optionalAccess', _377 => _377.height])) || natural.height * width / natural.width;
|
|
3301
|
+
if (width > MAX_IMAGE_WIDTH) {
|
|
3302
|
+
height = height * MAX_IMAGE_WIDTH / width;
|
|
3303
|
+
width = MAX_IMAGE_WIDTH;
|
|
3304
|
+
}
|
|
3305
|
+
return { data: new Uint8Array(await blob.arrayBuffer()), type, width: Math.round(width), height: Math.round(height) };
|
|
3306
|
+
} catch (e6) {
|
|
3307
|
+
return null;
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
async function resolveImages(doc) {
|
|
3311
|
+
const map = /* @__PURE__ */ new Map();
|
|
3312
|
+
if (typeof fetch !== "function") return map;
|
|
3313
|
+
const found = collectImages(doc.content, /* @__PURE__ */ new Map());
|
|
3314
|
+
await Promise.all(
|
|
3315
|
+
[...found].map(async ([src, attrs]) => {
|
|
3316
|
+
const info = await loadImage(src, attrs);
|
|
3317
|
+
if (info) map.set(src, info);
|
|
3318
|
+
})
|
|
3319
|
+
);
|
|
3320
|
+
return map;
|
|
3321
|
+
}
|
|
3322
|
+
function blocks(d, nodes, fmt, state) {
|
|
3323
|
+
const headings = [d.HeadingLevel.HEADING_1, d.HeadingLevel.HEADING_2, d.HeadingLevel.HEADING_3];
|
|
3324
|
+
const out = [];
|
|
3325
|
+
for (const node of _nullishCoalesce(nodes, () => ( []))) {
|
|
3326
|
+
switch (node.type) {
|
|
3327
|
+
case "paragraph":
|
|
3328
|
+
out.push(paragraph(d, node, fmt));
|
|
3329
|
+
break;
|
|
3330
|
+
case "heading": {
|
|
3331
|
+
const level = Math.min(Math.max(Number(_optionalChain([node, 'access', _378 => _378.attrs, 'optionalAccess', _379 => _379.level])) || 1, 1), 3);
|
|
3332
|
+
out.push(paragraph(d, node, fmt, { heading: headings[level - 1] }));
|
|
3333
|
+
break;
|
|
3334
|
+
}
|
|
3335
|
+
case "bulletList":
|
|
3336
|
+
case "orderedList":
|
|
3337
|
+
out.push(...list(d, node, fmt, 0, state));
|
|
3338
|
+
break;
|
|
3339
|
+
case "blockquote":
|
|
3340
|
+
out.push(...blocks(d, node.content, { ...fmt, quote: true, indent: (_nullishCoalesce(fmt.indent, () => ( 0))) + 360 }, state));
|
|
3341
|
+
break;
|
|
3342
|
+
case "codeBlock":
|
|
3343
|
+
out.push(code(d, node));
|
|
3344
|
+
break;
|
|
3345
|
+
case "horizontalRule":
|
|
3346
|
+
out.push(
|
|
3347
|
+
new d.Paragraph({
|
|
3348
|
+
border: { bottom: { color: "9CA3AF", space: 1, style: d.BorderStyle.SINGLE, size: 6 } },
|
|
3349
|
+
spacing: { after: 120 }
|
|
3350
|
+
})
|
|
3351
|
+
);
|
|
3352
|
+
break;
|
|
3353
|
+
case "table":
|
|
3354
|
+
out.push(table(d, node, state));
|
|
3355
|
+
break;
|
|
3356
|
+
case "pageBreak":
|
|
3357
|
+
out.push(new d.Paragraph({ children: [new d.PageBreak()] }));
|
|
3358
|
+
break;
|
|
3359
|
+
case "image":
|
|
3360
|
+
out.push(...image(d, node, state));
|
|
3361
|
+
break;
|
|
3362
|
+
default:
|
|
3363
|
+
if (node.content) out.push(...blocks(d, node.content, fmt, state));
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
return out;
|
|
3367
|
+
}
|
|
3368
|
+
var exportDocx = async (ctx) => {
|
|
3369
|
+
const d = await Promise.resolve().then(() => _interopRequireWildcard(require("docx")));
|
|
3370
|
+
const state = { instance: 0, images: await resolveImages(ctx.json) };
|
|
3371
|
+
const children = [
|
|
3372
|
+
new d.Paragraph({ text: ctx.title, heading: d.HeadingLevel.TITLE }),
|
|
3373
|
+
...blocks(d, ctx.json.content, {}, state)
|
|
3374
|
+
];
|
|
3375
|
+
const doc = new d.Document({
|
|
3376
|
+
title: ctx.title,
|
|
3377
|
+
numbering: {
|
|
3378
|
+
config: [
|
|
3379
|
+
{
|
|
3380
|
+
reference: ORDERED_REF,
|
|
3381
|
+
levels: [0, 1, 2].map((level) => ({
|
|
3382
|
+
level,
|
|
3383
|
+
format: d.LevelFormat.DECIMAL,
|
|
3384
|
+
text: `%${level + 1}.`,
|
|
3385
|
+
alignment: d.AlignmentType.START,
|
|
3386
|
+
style: { paragraph: { indent: { left: 720 * (level + 1), hanging: 360 } } }
|
|
3387
|
+
}))
|
|
3388
|
+
}
|
|
3389
|
+
]
|
|
3390
|
+
},
|
|
3391
|
+
sections: [{ children }]
|
|
3392
|
+
});
|
|
3393
|
+
const blob = await d.Packer.toBlob(doc);
|
|
3394
|
+
return { filename: `${ctx.basename}.docx`, mimeType: DOCX_MIME, blob };
|
|
3395
|
+
};
|
|
3396
|
+
function printHtml(html) {
|
|
3397
|
+
return new Promise((resolve, reject) => {
|
|
3398
|
+
const frame = document.createElement("iframe");
|
|
3399
|
+
frame.setAttribute("aria-hidden", "true");
|
|
3400
|
+
frame.style.cssText = "position:fixed;right:0;bottom:0;width:0;height:0;border:0;";
|
|
3401
|
+
const cleanup = () => setTimeout(() => frame.remove(), 500);
|
|
3402
|
+
frame.onload = () => {
|
|
3403
|
+
try {
|
|
3404
|
+
const win = frame.contentWindow;
|
|
3405
|
+
if (!win) throw new Error("The print frame is unavailable");
|
|
3406
|
+
win.addEventListener("afterprint", cleanup);
|
|
3407
|
+
win.focus();
|
|
3408
|
+
win.print();
|
|
3409
|
+
setTimeout(cleanup, 12e4);
|
|
3410
|
+
resolve();
|
|
3411
|
+
} catch (error) {
|
|
3412
|
+
cleanup();
|
|
3413
|
+
reject(error);
|
|
3414
|
+
}
|
|
3415
|
+
};
|
|
3416
|
+
frame.srcdoc = html;
|
|
3417
|
+
document.body.appendChild(frame);
|
|
3418
|
+
});
|
|
3419
|
+
}
|
|
3420
|
+
var exportPdf = async (ctx) => {
|
|
3421
|
+
const { docToHtml: docToHtml2 } = await Promise.resolve().then(() => _interopRequireWildcard(require("./document-engine-GXZH2LNW.cjs")));
|
|
3422
|
+
await printHtml(buildHtmlDocument(ctx.title, docToHtml2(ctx.json)));
|
|
3423
|
+
return { filename: `${ctx.basename}.pdf`, mimeType: "application/pdf", handled: true };
|
|
3424
|
+
};
|
|
3425
|
+
var registry = /* @__PURE__ */ new Map([
|
|
3426
|
+
["html", exportHtml],
|
|
3427
|
+
["markdown", exportMarkdown],
|
|
3428
|
+
["txt", exportTxt],
|
|
3429
|
+
["docx", exportDocx],
|
|
3430
|
+
["pdf", exportPdf]
|
|
3431
|
+
]);
|
|
3432
|
+
function registerDocumentExporter(format2, exporter) {
|
|
3433
|
+
registry.set(format2, exporter);
|
|
3434
|
+
}
|
|
3435
|
+
function listDocumentExporters() {
|
|
3436
|
+
return [...registry.keys()];
|
|
3437
|
+
}
|
|
3438
|
+
async function exportDocument(format2, doc) {
|
|
3439
|
+
const exporter = registry.get(format2);
|
|
3440
|
+
if (!exporter) throw new (0, _chunkG6YPSE6Jcjs.JuneError)("EXPORT_ERROR", `No exporter is registered for "${format2}"`);
|
|
3441
|
+
try {
|
|
3442
|
+
return await exporter({ ...doc, basename: _chunkG6YPSE6Jcjs.slugify.call(void 0, doc.title) || "document" });
|
|
3443
|
+
} catch (error) {
|
|
3444
|
+
throw _chunkG6YPSE6Jcjs.toJuneError.call(void 0, error, "EXPORT_ERROR", `Exporting ${format2} failed`);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
function downloadResult(result) {
|
|
3448
|
+
if (!result.blob) return;
|
|
3449
|
+
const url = URL.createObjectURL(result.blob);
|
|
3450
|
+
const link = document.createElement("a");
|
|
3451
|
+
link.href = url;
|
|
3452
|
+
link.download = result.filename;
|
|
3453
|
+
document.body.appendChild(link);
|
|
3454
|
+
link.click();
|
|
3455
|
+
link.remove();
|
|
3456
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
3457
|
+
}
|
|
3458
|
+
async function copyDocumentToClipboard(doc) {
|
|
3459
|
+
const { docToHtml: docToHtml2, docToText } = await Promise.resolve().then(() => _interopRequireWildcard(require("./document-engine-GXZH2LNW.cjs")));
|
|
3460
|
+
const html = docToHtml2(doc.json);
|
|
3461
|
+
const text = `${doc.title}
|
|
3462
|
+
|
|
3463
|
+
${docToText(doc.json)}`;
|
|
3464
|
+
try {
|
|
3465
|
+
if (typeof ClipboardItem !== "undefined" && _optionalChain([navigator, 'access', _380 => _380.clipboard, 'optionalAccess', _381 => _381.write])) {
|
|
3466
|
+
await navigator.clipboard.write([
|
|
3467
|
+
new ClipboardItem({
|
|
3468
|
+
"text/html": new Blob([html], { type: "text/html" }),
|
|
3469
|
+
"text/plain": new Blob([text], { type: "text/plain" })
|
|
3470
|
+
})
|
|
3471
|
+
]);
|
|
3472
|
+
return true;
|
|
3473
|
+
}
|
|
3474
|
+
await navigator.clipboard.writeText(text);
|
|
3475
|
+
return true;
|
|
3476
|
+
} catch (e7) {
|
|
3477
|
+
return false;
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
// src/features/document-editor/document-types.ts
|
|
3482
|
+
var ALL_EXPORT_FORMATS = [
|
|
3483
|
+
"html",
|
|
3484
|
+
"markdown",
|
|
3485
|
+
"txt",
|
|
3486
|
+
"docx",
|
|
3487
|
+
"pdf"
|
|
3488
|
+
];
|
|
3489
|
+
|
|
3490
|
+
// src/features/document-editor/document-editor.tsx
|
|
3491
|
+
|
|
3492
|
+
var snapshotOf = (r) => ({
|
|
3493
|
+
documentId: r.id,
|
|
3494
|
+
title: r.title,
|
|
3495
|
+
documentType: r.documentType,
|
|
3496
|
+
status: r.status,
|
|
3497
|
+
json: r.json
|
|
3498
|
+
});
|
|
3499
|
+
var SAVE_LABEL = {
|
|
3500
|
+
idle: "",
|
|
3501
|
+
dirty: "Unsaved changes",
|
|
3502
|
+
saving: "Saving\u2026",
|
|
3503
|
+
saved: "Saved",
|
|
3504
|
+
error: "Save failed"
|
|
3505
|
+
};
|
|
3506
|
+
var ZOOM_STEPS = [0.5, 0.67, 0.75, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2];
|
|
3507
|
+
var stepZoom = (z2, dir) => dir > 0 ? _nullishCoalesce(ZOOM_STEPS.find((s) => s > z2 + 1e-3), () => ( z2)) : _nullishCoalesce([...ZOOM_STEPS].reverse().find((s) => s < z2 - 1e-3), () => ( z2));
|
|
3508
|
+
function DocumentEditorImpl(props) {
|
|
3509
|
+
const {
|
|
3510
|
+
documentId,
|
|
3511
|
+
title: titleProp = "Untitled document",
|
|
3512
|
+
content,
|
|
3513
|
+
documentType,
|
|
3514
|
+
status = "draft",
|
|
3515
|
+
editable = true,
|
|
3516
|
+
exportFormats,
|
|
3517
|
+
theme = "auto",
|
|
3518
|
+
className
|
|
3519
|
+
} = props;
|
|
3520
|
+
const ctxOptions = useDocumentOptions();
|
|
3521
|
+
const options = _react.useMemo.call(void 0, () => ({ ...ctxOptions, ...props.options }), [ctxOptions, props.options]);
|
|
3522
|
+
const optionsRef = _react.useRef.call(void 0, options);
|
|
3523
|
+
optionsRef.current = options;
|
|
3524
|
+
const store = _react.useMemo.call(void 0, () => _nullishCoalesce(options.store, () => ( getDefaultDocumentStore())), [options.store]);
|
|
3525
|
+
const instanceId = _react.useId.call(void 0, );
|
|
3526
|
+
const { theme: contextTheme } = useJuneTheme();
|
|
3527
|
+
const resolvedTheme = theme === "auto" ? contextTheme : theme;
|
|
3528
|
+
const contentKey = typeof content === "string" ? content : content ? JSON.stringify(content) : "";
|
|
3529
|
+
const parsed = _react.useMemo.call(void 0, () => {
|
|
3530
|
+
if (!contentKey) return { json: null, error: null };
|
|
3531
|
+
try {
|
|
3532
|
+
return { json: _chunkG6YPSE6Jcjs.parseDocumentContent.call(void 0, contentKey), error: null };
|
|
3533
|
+
} catch (e) {
|
|
3534
|
+
return { json: null, error: _chunkG6YPSE6Jcjs.toJuneError.call(void 0, e, "DOCUMENT_ERROR") };
|
|
3535
|
+
}
|
|
3536
|
+
}, [contentKey]);
|
|
3537
|
+
_react.useEffect.call(void 0, () => {
|
|
3538
|
+
if (parsed.error) _optionalChain([optionsRef, 'access', _382 => _382.current, 'access', _383 => _383.onError, 'optionalCall', _384 => _384(parsed.error)]);
|
|
3539
|
+
}, [parsed.error]);
|
|
3540
|
+
const title = useDocumentValue(store, documentId, (r) => _nullishCoalesce(_optionalChain([r, 'optionalAccess', _385 => _385.title]), () => ( titleProp)));
|
|
3541
|
+
const pending = useDocumentValue(store, documentId, (r) => _nullishCoalesce(_optionalChain([r, 'optionalAccess', _386 => _386.pending]), () => ( null)));
|
|
3542
|
+
const words = useDocumentValue(store, documentId, (r) => r ? _chunkG6YPSE6Jcjs.wordCountOf.call(void 0, r.json) : 0);
|
|
3543
|
+
const recordStatus = useDocumentValue(store, documentId, (r) => _nullishCoalesce(_optionalChain([r, 'optionalAccess', _387 => _387.status]), () => ( status)));
|
|
3544
|
+
const [saveState, setSaveState] = _react.useState.call(void 0, "idle");
|
|
3545
|
+
const [notice, setNotice] = _react.useState.call(void 0, null);
|
|
3546
|
+
const [fullscreen, setFullscreen] = _react.useState.call(void 0, false);
|
|
3547
|
+
const [expanded, setExpanded] = _react.useState.call(void 0, true);
|
|
3548
|
+
const [showMarks, setShowMarks] = _react.useState.call(void 0, false);
|
|
3549
|
+
const [layout, setLayout] = _react.useState.call(void 0, "page");
|
|
3550
|
+
const [findOpen, setFindOpen] = _react.useState.call(void 0, false);
|
|
3551
|
+
const [zoom, setZoom] = _react.useState.call(void 0, 1);
|
|
3552
|
+
const [metrics, setMetrics] = _react.useState.call(void 0, { page: 1, pages: 1 });
|
|
3553
|
+
_react.useEffect.call(void 0, () => {
|
|
3554
|
+
if (!fullscreen || typeof document === "undefined") return;
|
|
3555
|
+
const prev = document.body.style.overflow;
|
|
3556
|
+
document.body.style.overflow = "hidden";
|
|
3557
|
+
return () => {
|
|
3558
|
+
document.body.style.overflow = prev;
|
|
3559
|
+
};
|
|
3560
|
+
}, [fullscreen]);
|
|
3561
|
+
const lastSavedKey = _react.useRef.call(void 0, "");
|
|
3562
|
+
const seenRevision = _react.useRef.call(void 0, _nullishCoalesce(_optionalChain([store, 'access', _388 => _388.get, 'call', _389 => _389(documentId), 'optionalAccess', _390 => _390.revision]), () => ( 0)));
|
|
3563
|
+
const readyRef = _react.useRef.call(void 0, false);
|
|
3564
|
+
const flash = _react.useCallback.call(void 0, (message) => {
|
|
3565
|
+
setNotice(message);
|
|
3566
|
+
}, []);
|
|
3567
|
+
_react.useEffect.call(void 0, () => {
|
|
3568
|
+
if (!notice) return;
|
|
3569
|
+
const t = setTimeout(() => setNotice(null), 3e3);
|
|
3570
|
+
return () => clearTimeout(t);
|
|
3571
|
+
}, [notice]);
|
|
3572
|
+
const emitChange = _react.useCallback.call(void 0,
|
|
3573
|
+
(source) => {
|
|
3574
|
+
const record = store.get(documentId);
|
|
3575
|
+
if (record) _optionalChain([optionsRef, 'access', _391 => _391.current, 'access', _392 => _392.onDocumentChange, 'optionalCall', _393 => _393({ ...snapshotOf(record), source })]);
|
|
3576
|
+
},
|
|
3577
|
+
[store, documentId]
|
|
3578
|
+
);
|
|
3579
|
+
const saveRef = _react.useRef.call(void 0, async () => {
|
|
3580
|
+
});
|
|
3581
|
+
const autosave = _react.useMemo.call(void 0, () => {
|
|
3582
|
+
const delay = _nullishCoalesce(options.autosaveDelay, () => ( 1500));
|
|
3583
|
+
return delay > 0 ? _chunkG6YPSE6Jcjs.debounce.call(void 0, () => void saveRef.current(), delay) : null;
|
|
3584
|
+
}, [options.autosaveDelay]);
|
|
3585
|
+
_react.useEffect.call(void 0, () => () => _optionalChain([autosave, 'optionalAccess', _394 => _394.flush, 'call', _395 => _395()]), [autosave]);
|
|
3586
|
+
saveRef.current = async () => {
|
|
3587
|
+
const record = store.get(documentId);
|
|
3588
|
+
const onSave = optionsRef.current.onSave;
|
|
3589
|
+
if (!record) return;
|
|
3590
|
+
const key = JSON.stringify([record.title, _chunkG6YPSE6Jcjs.docKey.call(void 0, record.json)]);
|
|
3591
|
+
if (!onSave) {
|
|
3592
|
+
lastSavedKey.current = key;
|
|
3593
|
+
setSaveState("saved");
|
|
3594
|
+
return;
|
|
3595
|
+
}
|
|
3596
|
+
if (key === lastSavedKey.current) {
|
|
3597
|
+
setSaveState("saved");
|
|
3598
|
+
return;
|
|
3599
|
+
}
|
|
3600
|
+
setSaveState("saving");
|
|
3601
|
+
try {
|
|
3602
|
+
await onSave({
|
|
3603
|
+
...snapshotOf(record),
|
|
3604
|
+
html: _chunkG6YPSE6Jcjs.docToHtml.call(void 0, record.json),
|
|
3605
|
+
markdown: _chunkG6YPSE6Jcjs.docToMarkdown.call(void 0, record.json)
|
|
3606
|
+
});
|
|
3607
|
+
lastSavedKey.current = key;
|
|
3608
|
+
const latest = store.get(documentId);
|
|
3609
|
+
const latestKey = latest ? JSON.stringify([latest.title, _chunkG6YPSE6Jcjs.docKey.call(void 0, latest.json)]) : key;
|
|
3610
|
+
setSaveState(latestKey === key ? "saved" : "dirty");
|
|
3611
|
+
} catch (error) {
|
|
3612
|
+
setSaveState("error");
|
|
3613
|
+
_optionalChain([optionsRef, 'access', _396 => _396.current, 'access', _397 => _397.onError, 'optionalCall', _398 => _398(_chunkG6YPSE6Jcjs.toJuneError.call(void 0, error, "DOCUMENT_ERROR", "Saving the document failed"))]);
|
|
3614
|
+
}
|
|
3615
|
+
};
|
|
3616
|
+
const markDirty = _react.useCallback.call(void 0,
|
|
3617
|
+
(userEdit = true) => {
|
|
3618
|
+
if (!optionsRef.current.onSave) {
|
|
3619
|
+
if (userEdit) setSaveState("dirty");
|
|
3620
|
+
return;
|
|
3621
|
+
}
|
|
3622
|
+
setSaveState("dirty");
|
|
3623
|
+
_optionalChain([autosave, 'optionalCall', _399 => _399()]);
|
|
3624
|
+
},
|
|
3625
|
+
[autosave]
|
|
3626
|
+
);
|
|
3627
|
+
_react.useEffect.call(void 0, () => {
|
|
3628
|
+
if (saveState !== "saved" || options.onSave) return;
|
|
3629
|
+
const t = setTimeout(() => setSaveState((s) => s === "saved" ? "idle" : s), 2500);
|
|
3630
|
+
return () => clearTimeout(t);
|
|
3631
|
+
}, [saveState, options.onSave]);
|
|
3632
|
+
const [initialJson] = _react.useState.call(void 0, () => _nullishCoalesce(_nullishCoalesce(_optionalChain([store, 'access', _400 => _400.get, 'call', _401 => _401(documentId), 'optionalAccess', _402 => _402.json]), () => ( parsed.json)), () => ( _chunkG6YPSE6Jcjs.emptyDoc.call(void 0, ))));
|
|
3633
|
+
const extensions = _react.useMemo.call(void 0, () => _chunkG6YPSE6Jcjs.createDocumentExtensions.call(void 0, ), []);
|
|
3634
|
+
const onUpdateRef = _react.useRef.call(void 0, () => {
|
|
3635
|
+
});
|
|
3636
|
+
onUpdateRef.current = (json) => {
|
|
3637
|
+
store.edit(documentId, json, instanceId);
|
|
3638
|
+
emitChange("user");
|
|
3639
|
+
markDirty();
|
|
3640
|
+
};
|
|
3641
|
+
const editor = _react3.useEditor.call(void 0, {
|
|
3642
|
+
extensions,
|
|
3643
|
+
content: initialJson,
|
|
3644
|
+
editable,
|
|
3645
|
+
immediatelyRender: false,
|
|
3646
|
+
editorProps: {
|
|
3647
|
+
attributes: {
|
|
3648
|
+
class: "june-doc__prose",
|
|
3649
|
+
role: "textbox",
|
|
3650
|
+
"aria-multiline": "true",
|
|
3651
|
+
"aria-label": "Document content"
|
|
3652
|
+
}
|
|
3653
|
+
},
|
|
3654
|
+
onUpdate: ({ editor: ed }) => onUpdateRef.current(ed.getJSON())
|
|
3655
|
+
});
|
|
3656
|
+
_react.useEffect.call(void 0, () => {
|
|
3657
|
+
if (!editor || editor.isEditable === editable) return;
|
|
3658
|
+
editor.setEditable(editable);
|
|
3659
|
+
}, [editor, editable]);
|
|
3660
|
+
_react.useEffect.call(void 0, () => {
|
|
3661
|
+
if (!parsed.json) return;
|
|
3662
|
+
if (isDocumentEcho(store, documentId, contentKey)) return;
|
|
3663
|
+
const outcome = store.receive({
|
|
3664
|
+
id: documentId,
|
|
3665
|
+
title: titleProp,
|
|
3666
|
+
json: parsed.json,
|
|
3667
|
+
documentType,
|
|
3668
|
+
status
|
|
3669
|
+
});
|
|
3670
|
+
if (outcome === "applied" || outcome === "merged") {
|
|
3671
|
+
emitChange(outcome === "applied" ? "ai" : "merge");
|
|
3672
|
+
markDirty(false);
|
|
3673
|
+
} else if (outcome === "conflict") {
|
|
3674
|
+
const record = store.get(documentId);
|
|
3675
|
+
_optionalChain([optionsRef, 'access', _403 => _403.current, 'access', _404 => _404.onConflict, 'optionalCall', _405 => _405({
|
|
3676
|
+
documentId,
|
|
3677
|
+
title: _nullishCoalesce(_optionalChain([record, 'optionalAccess', _406 => _406.title]), () => ( titleProp)),
|
|
3678
|
+
conflicts: _nullishCoalesce(_optionalChain([record, 'optionalAccess', _407 => _407.pending, 'optionalAccess', _408 => _408.conflicts]), () => ( 1))
|
|
3679
|
+
})]);
|
|
3680
|
+
}
|
|
3681
|
+
}, [store, documentId, titleProp, parsed.json, documentType, status, emitChange, markDirty]);
|
|
3682
|
+
_react.useEffect.call(void 0, () => {
|
|
3683
|
+
if (!editor) return;
|
|
3684
|
+
const apply = () => {
|
|
3685
|
+
const record = store.get(documentId);
|
|
3686
|
+
if (!record || record.revision === seenRevision.current) return;
|
|
3687
|
+
seenRevision.current = record.revision;
|
|
3688
|
+
if (record.origin === instanceId) return;
|
|
3689
|
+
const { from, to } = editor.state.selection;
|
|
3690
|
+
editor.commands.setContent(record.json, { emitUpdate: false });
|
|
3691
|
+
if (editor.isFocused) {
|
|
3692
|
+
const size = editor.state.doc.content.size;
|
|
3693
|
+
editor.commands.setTextSelection({ from: Math.min(from, size), to: Math.min(to, size) });
|
|
3694
|
+
}
|
|
3695
|
+
};
|
|
3696
|
+
apply();
|
|
3697
|
+
return store.subscribe(apply);
|
|
3698
|
+
}, [editor, store, documentId, instanceId]);
|
|
3699
|
+
_react.useEffect.call(void 0, () => {
|
|
3700
|
+
if (!editor || readyRef.current) return;
|
|
3701
|
+
const record = store.get(documentId);
|
|
3702
|
+
if (!record) return;
|
|
3703
|
+
readyRef.current = true;
|
|
3704
|
+
lastSavedKey.current = JSON.stringify([record.title, _chunkG6YPSE6Jcjs.docKey.call(void 0, record.json)]);
|
|
3705
|
+
_optionalChain([optionsRef, 'access', _409 => _409.current, 'access', _410 => _410.onDocumentReady, 'optionalCall', _411 => _411(snapshotOf(record))]);
|
|
3706
|
+
}, [editor, store, documentId, title]);
|
|
3707
|
+
const handleTitle = (value) => {
|
|
3708
|
+
store.setTitle(documentId, value);
|
|
3709
|
+
emitChange("user");
|
|
3710
|
+
markDirty();
|
|
3711
|
+
};
|
|
3712
|
+
const resolve = (choice) => {
|
|
3713
|
+
store.resolve(documentId, choice);
|
|
3714
|
+
if (choice !== "keep") {
|
|
3715
|
+
emitChange(choice === "merge" ? "merge" : "resolve");
|
|
3716
|
+
markDirty();
|
|
3717
|
+
}
|
|
3718
|
+
};
|
|
3719
|
+
const handleExport = async (format2) => {
|
|
3720
|
+
const record = store.get(documentId);
|
|
3721
|
+
if (!record) return;
|
|
3722
|
+
try {
|
|
3723
|
+
const result = await exportDocument(format2, {
|
|
3724
|
+
documentId,
|
|
3725
|
+
title: record.title,
|
|
3726
|
+
json: record.json
|
|
3727
|
+
// always the latest editor content
|
|
3728
|
+
});
|
|
3729
|
+
if (result.blob) downloadResult(result);
|
|
3730
|
+
_optionalChain([optionsRef, 'access', _412 => _412.current, 'access', _413 => _413.onExport, 'optionalCall', _414 => _414({ documentId, format: format2, ...result })]);
|
|
3731
|
+
} catch (error) {
|
|
3732
|
+
_optionalChain([optionsRef, 'access', _415 => _415.current, 'access', _416 => _416.onError, 'optionalCall', _417 => _417(_chunkG6YPSE6Jcjs.toJuneError.call(void 0, error, "EXPORT_ERROR"))]);
|
|
3733
|
+
flash("Export failed");
|
|
3734
|
+
}
|
|
3735
|
+
};
|
|
3736
|
+
const handleCopy = async () => {
|
|
3737
|
+
const record = store.get(documentId);
|
|
3738
|
+
if (!record) return;
|
|
3739
|
+
flash(await copyDocumentToClipboard({ title: record.title, json: record.json }) ? "Copied" : "Copy failed");
|
|
3740
|
+
};
|
|
3741
|
+
const formats = (_nullishCoalesce(exportFormats, () => ( [...ALL_EXPORT_FORMATS]))).filter((f) => listDocumentExporters().includes(f));
|
|
3742
|
+
if (parsed.error && !store.get(documentId)) {
|
|
3743
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc june-doc--error", "data-theme": resolvedTheme, role: "alert", children: [
|
|
3744
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "june-doc__draft-text", children: "This document could not be displayed." }),
|
|
3745
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "details", { children: [
|
|
3746
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "summary", { children: "Show raw content" }),
|
|
3747
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "pre", { className: "june-doc__raw", children: contentKey })
|
|
3748
|
+
] })
|
|
3749
|
+
] });
|
|
3750
|
+
}
|
|
3751
|
+
if (!editor) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DocumentDraft, { title, state: "loading" });
|
|
3752
|
+
const isDraft = recordStatus === "draft";
|
|
3753
|
+
const classes = ["june-doc", fullscreen ? "june-doc--fullscreen" : "", _nullishCoalesce(className, () => ( ""))].filter(Boolean).join(" ");
|
|
3754
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
3755
|
+
"div",
|
|
3756
|
+
{
|
|
3757
|
+
className: classes,
|
|
3758
|
+
"data-theme": resolvedTheme,
|
|
3759
|
+
"data-status": recordStatus,
|
|
3760
|
+
role: fullscreen ? "dialog" : void 0,
|
|
3761
|
+
"aria-modal": fullscreen || void 0,
|
|
3762
|
+
"aria-label": fullscreen ? title : void 0,
|
|
3763
|
+
onKeyDown: (e) => {
|
|
3764
|
+
const mod = e.ctrlKey || e.metaKey;
|
|
3765
|
+
if (mod && e.key.toLowerCase() === "s" && options.onSave) {
|
|
3766
|
+
e.preventDefault();
|
|
3767
|
+
void saveRef.current();
|
|
3768
|
+
} else if (mod && e.key.toLowerCase() === "f") {
|
|
3769
|
+
e.preventDefault();
|
|
3770
|
+
setFindOpen(true);
|
|
3771
|
+
} else if (e.key === "Escape" && fullscreen && !e.defaultPrevented) {
|
|
3772
|
+
setFullscreen(false);
|
|
3773
|
+
}
|
|
3774
|
+
},
|
|
3775
|
+
children: [
|
|
3776
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
3777
|
+
DocumentHeader,
|
|
3778
|
+
{
|
|
3779
|
+
title,
|
|
3780
|
+
editable,
|
|
3781
|
+
isDraft,
|
|
3782
|
+
fullscreen,
|
|
3783
|
+
onToggleFullscreen: () => setFullscreen((v) => !v),
|
|
3784
|
+
onTitle: handleTitle,
|
|
3785
|
+
exportFormats: formats,
|
|
3786
|
+
canSave: !!options.onSave,
|
|
3787
|
+
showSave: !!options.onSave || editable && saveState !== "idle",
|
|
3788
|
+
saveState,
|
|
3789
|
+
onExport: (f) => void handleExport(f),
|
|
3790
|
+
onCopy: () => void handleCopy(),
|
|
3791
|
+
onSave: () => void saveRef.current()
|
|
3792
|
+
}
|
|
3793
|
+
),
|
|
3794
|
+
pending ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__banner", role: "status", children: [
|
|
3795
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { children: [
|
|
3796
|
+
"The assistant suggested changes that overlap your edits (",
|
|
3797
|
+
pending.conflicts,
|
|
3798
|
+
" ",
|
|
3799
|
+
pending.conflicts === 1 ? "section" : "sections",
|
|
3800
|
+
")."
|
|
3801
|
+
] }),
|
|
3802
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "june-doc__banner-actions", children: [
|
|
3803
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn", onClick: () => resolve("merge"), children: "Merge, keep my edits" }),
|
|
3804
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn", onClick: () => resolve("replace"), children: "Use AI version" }),
|
|
3805
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn", onClick: () => resolve("keep"), children: "Keep mine" })
|
|
3806
|
+
] })
|
|
3807
|
+
] }) : null,
|
|
3808
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
3809
|
+
DocumentToolbar,
|
|
3810
|
+
{
|
|
3811
|
+
editor,
|
|
3812
|
+
editable,
|
|
3813
|
+
expanded,
|
|
3814
|
+
onToggleExpanded: () => setExpanded((v) => !v),
|
|
3815
|
+
showMarks,
|
|
3816
|
+
onToggleMarks: () => setShowMarks((v) => !v),
|
|
3817
|
+
layout,
|
|
3818
|
+
onToggleLayout: () => setLayout((v) => v === "page" ? "fluid" : "page"),
|
|
3819
|
+
findOpen,
|
|
3820
|
+
onToggleFind: () => setFindOpen((v) => !v),
|
|
3821
|
+
onNotice: flash
|
|
3822
|
+
}
|
|
3823
|
+
),
|
|
3824
|
+
findOpen ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DocumentFindBar, { editor, editable, onClose: () => setFindOpen(false), onNotice: flash }) : null,
|
|
3825
|
+
editable ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DocumentBubbleMenu, { editor }) : null,
|
|
3826
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, DocumentContent, { editor, layout, zoom, showMarks, onMetrics: setMetrics }),
|
|
3827
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__footer", children: [
|
|
3828
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__stats", children: [
|
|
3829
|
+
layout === "page" ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
3830
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { children: [
|
|
3831
|
+
"Page ",
|
|
3832
|
+
metrics.page,
|
|
3833
|
+
" of ",
|
|
3834
|
+
metrics.pages
|
|
3835
|
+
] }),
|
|
3836
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__stats-sep", "aria-hidden": "true" })
|
|
3837
|
+
] }) : null,
|
|
3838
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { children: [
|
|
3839
|
+
words,
|
|
3840
|
+
" words"
|
|
3841
|
+
] })
|
|
3842
|
+
] }),
|
|
3843
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-doc__status", "aria-live": "polite", "data-state": notice ? "notice" : saveState, children: _nullishCoalesce(notice, () => ( SAVE_LABEL[saveState])) }),
|
|
3844
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-doc__zoom", role: "group", "aria-label": "Zoom", children: [
|
|
3845
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost", "aria-label": "Zoom out", disabled: zoom <= ZOOM_STEPS[0], onClick: () => setZoom(stepZoom(zoom, -1)), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "minus", size: 14 }) }),
|
|
3846
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { type: "button", className: "june-doc__zoom-value", "aria-label": "Reset zoom", title: "Reset zoom", onClick: () => setZoom(1), children: [
|
|
3847
|
+
Math.round(zoom * 100),
|
|
3848
|
+
"%"
|
|
3849
|
+
] }),
|
|
3850
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost", "aria-label": "Zoom in", disabled: zoom >= ZOOM_STEPS[ZOOM_STEPS.length - 1], onClick: () => setZoom(stepZoom(zoom, 1)), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { name: "plus", size: 14 }) })
|
|
3851
|
+
] })
|
|
3852
|
+
] })
|
|
3853
|
+
]
|
|
3854
|
+
}
|
|
3855
|
+
);
|
|
3856
|
+
}
|
|
3857
|
+
|
|
3858
|
+
// src/features/document-editor/document-component.tsx
|
|
3859
|
+
|
|
3860
|
+
var DOCUMENT_COMPONENT_DESCRIPTION = 'Custom component. An editable document. DocumentEditor(documentId, title, content, documentType?, status?, editable?, exportFormats?). Place it in the root container (Card or Stack). "content" is the full document body as one Markdown string. Reuse documentId when revising the same document. Use it only when the user asks to create or revise a document.';
|
|
3861
|
+
var documentProps = _v4.z.object({
|
|
3862
|
+
documentId: _v4.z.string().describe("Stable id. Reuse it when revising the same document."),
|
|
3863
|
+
title: _v4.z.string().describe("Document title. Do not repeat it as a heading in content."),
|
|
3864
|
+
content: _v4.z.string().describe("The whole document body as Markdown."),
|
|
3865
|
+
documentType: _v4.z.string().optional().describe("Kind of document, such as proposal, contract or invoice."),
|
|
3866
|
+
status: _v4.z.string().optional().describe('Use "draft" for generated documents.'),
|
|
3867
|
+
editable: _v4.z.boolean().optional().describe("Whether the user can edit. Defaults to true."),
|
|
3868
|
+
exportFormats: _v4.z.array(_v4.z.string()).optional().describe("Export formats to offer, such as html, markdown, docx.")
|
|
3869
|
+
});
|
|
3870
|
+
function DocumentEditor(props) {
|
|
3871
|
+
const { isStreaming } = useJuneRuntime();
|
|
3872
|
+
if (props.generating || isStreaming) {
|
|
3873
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DocumentDraft, { title: props.title, state: "drafting" });
|
|
3874
|
+
}
|
|
3875
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DocumentEditorImpl, { ...props });
|
|
3876
|
+
}
|
|
3877
|
+
function OpenUIDocument({ props }) {
|
|
3878
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
3879
|
+
DocumentEditor,
|
|
3880
|
+
{
|
|
3881
|
+
documentId: props.documentId,
|
|
3882
|
+
title: props.title,
|
|
3883
|
+
content: props.content,
|
|
3884
|
+
documentType: props.documentType,
|
|
3885
|
+
status: props.status,
|
|
3886
|
+
editable: props.editable,
|
|
3887
|
+
exportFormats: props.exportFormats
|
|
3888
|
+
}
|
|
3889
|
+
);
|
|
3890
|
+
}
|
|
3891
|
+
var JuneDocumentEditorComponent = _reactlang.defineComponent.call(void 0, {
|
|
3892
|
+
name: "DocumentEditor",
|
|
3893
|
+
description: DOCUMENT_COMPONENT_DESCRIPTION,
|
|
3894
|
+
props: documentProps,
|
|
3895
|
+
component: OpenUIDocument
|
|
3896
|
+
});
|
|
3897
|
+
function withDocumentEditor(library) {
|
|
3898
|
+
try {
|
|
3899
|
+
return library.extend({
|
|
3900
|
+
components: {
|
|
3901
|
+
add: [{ component: JuneDocumentEditorComponent, slots: ["Card"] }]
|
|
3902
|
+
}
|
|
3903
|
+
});
|
|
3904
|
+
} catch (e8) {
|
|
3905
|
+
return library.extend({ components: { add: [JuneDocumentEditorComponent] } });
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
|
|
3909
|
+
// src/features/document-editor/document-instructions.ts
|
|
3910
|
+
var DEFAULT_TYPES = [
|
|
3911
|
+
"proposal",
|
|
3912
|
+
"report",
|
|
3913
|
+
"email",
|
|
3914
|
+
"letter",
|
|
3915
|
+
"invoice",
|
|
3916
|
+
"contract",
|
|
3917
|
+
"NDA",
|
|
3918
|
+
"memo",
|
|
3919
|
+
"meeting notes"
|
|
3920
|
+
];
|
|
3921
|
+
function getDocumentEditorRules(options = {}) {
|
|
3922
|
+
const name = _nullishCoalesce(options.componentName, () => ( "DocumentEditor"));
|
|
3923
|
+
const types = (_nullishCoalesce(options.documentTypes, () => ( DEFAULT_TYPES))).join(", ");
|
|
3924
|
+
return [
|
|
3925
|
+
`WHEN TO USE ${name}: use it only when the user asks you to create, draft, write, prepare, generate or revise an editable document (${types}, or similar). Put the finished document inside ${name}.`,
|
|
3926
|
+
`WHEN NOT TO USE ${name}: for explanations ("explain what an NDA is"), factual answers, comparisons, brainstorming and general questions, answer with normal text and the other components. Never open ${name} for those.`,
|
|
3927
|
+
`${name}(documentId, title, content, documentType?, status?, editable?, exportFormats?): "content" is the whole document body as Markdown inside ONE double-quoted string. Write line breaks as \\n and escape double quotes as \\". Supported Markdown: headings (#, ##, ###), paragraphs, **bold**, *italic*, lists, > quotes, [links](https://...), and tables.`,
|
|
3928
|
+
`Do not repeat the title as a heading inside "content"; the title is shown separately. Structure the document with headings, short paragraphs and tables where useful. Keep a professional tone.`,
|
|
3929
|
+
`EDITING: when the user asks to change an existing document (add a section, rewrite a part, change the title), write ${name} again with the SAME documentId and the COMPLETE updated content, starting from the current document text you were given. Copy every unchanged section exactly as it was. Change only what was requested. The app merges your version with the user's own edits.`,
|
|
3930
|
+
`If the user asks for a document but leaves out details, write the document anyway and use clear placeholders such as [CLIENT NAME], [DATE], [AMOUNT]. Do not stop to ask unless the request is impossible to interpret.`,
|
|
3931
|
+
`Never invent legal, financial or business facts, prices, dates, names or figures. Use placeholders instead. Set status to "draft" for generated documents, and for legal or financial documents say in one short sentence outside the component that the document is a draft that should be reviewed by a professional.`,
|
|
3932
|
+
`You may add one short sentence before or after the component. Do not repeat the document text outside the component.`,
|
|
3933
|
+
..._nullishCoalesce(options.extraRules, () => ( []))
|
|
3934
|
+
];
|
|
3935
|
+
}
|
|
3936
|
+
function getDocumentEditorInstructions(options = {}) {
|
|
3937
|
+
const rules = getDocumentEditorRules(options);
|
|
3938
|
+
return ["Document drafting:", ...rules.map((rule, i) => `${i + 1}. ${rule}`)].join("\n");
|
|
3939
|
+
}
|
|
3940
|
+
var EXAMPLE_NEW = 'root = Stack([doc])\ndoc = DocumentEditor("business-proposal", "Business Proposal", "## Executive Summary\\n\\nThis proposal outlines [SERVICE] for [CLIENT NAME].\\n\\n## Scope of Work\\n\\n- Discovery\\n- Delivery\\n\\n## Pricing\\n\\n| Item | Cost |\\n| --- | --- |\\n| Discovery | [AMOUNT] |", "proposal", "draft")';
|
|
3941
|
+
var EXAMPLE_EDIT = 'root = Stack([doc])\ndoc = DocumentEditor("business-proposal", "Business Proposal", "## Executive Summary\\n\\nThis proposal outlines [SERVICE] for [CLIENT NAME].\\n\\n## Scope of Work\\n\\n- Discovery\\n- Delivery\\n\\n## Pricing\\n\\n| Item | Cost |\\n| --- | --- |\\n| Discovery | [AMOUNT] |\\n| Delivery | [AMOUNT] |", "proposal", "draft")';
|
|
3942
|
+
function documentPromptOptions(options = {}) {
|
|
3943
|
+
return {
|
|
3944
|
+
additionalRules: getDocumentEditorRules(options),
|
|
3945
|
+
examples: [EXAMPLE_NEW, EXAMPLE_EDIT]
|
|
3946
|
+
};
|
|
3947
|
+
}
|
|
3948
|
+
|
|
3949
|
+
// src/features/document-editor/document-agent.ts
|
|
3950
|
+
var attr = (value) => value.replace(/"/g, """);
|
|
3951
|
+
async function serializeDocumentsForAgent(options = {}) {
|
|
3952
|
+
const store = _nullishCoalesce(options.store, () => ( getDefaultDocumentStore()));
|
|
3953
|
+
const { docToMarkdown: docToMarkdown2 } = await Promise.resolve().then(() => _interopRequireWildcard(require("./document-engine-GXZH2LNW.cjs")));
|
|
3954
|
+
return store.list().filter((r) => !options.ids || options.ids.includes(r.id)).map(
|
|
3955
|
+
(r) => `<document id="${attr(r.id)}" title="${attr(r.title)}"${r.documentType ? ` type="${attr(r.documentType)}"` : ""}>
|
|
3956
|
+
${docToMarkdown2(r.json)}
|
|
3957
|
+
</document>`
|
|
3958
|
+
).join("\n\n");
|
|
3959
|
+
}
|
|
3960
|
+
|
|
3961
|
+
// src/features/tool-approval/tool-approval.tsx
|
|
3962
|
+
|
|
3963
|
+
|
|
3964
|
+
var defaultToolApprovalLabels = {
|
|
3965
|
+
approve: "Approve",
|
|
3966
|
+
reject: "Reject",
|
|
3967
|
+
edit: "Edit",
|
|
3968
|
+
save: "Approve with changes",
|
|
3969
|
+
cancel: "Cancel",
|
|
3970
|
+
invalidJson: "Arguments must be valid JSON.",
|
|
3971
|
+
status: {
|
|
3972
|
+
approved: "Approved",
|
|
3973
|
+
rejected: "Rejected",
|
|
3974
|
+
running: "Running\u2026",
|
|
3975
|
+
completed: "Completed",
|
|
3976
|
+
failed: "Failed"
|
|
3977
|
+
},
|
|
3978
|
+
sideEffect: { read: "Reads data", write: "Makes changes", destructive: "Deletes or overwrites" },
|
|
3979
|
+
risk: (risk) => `${risk.charAt(0).toUpperCase()}${risk.slice(1)} risk`
|
|
3980
|
+
};
|
|
3981
|
+
var format = (value) => {
|
|
3982
|
+
try {
|
|
3983
|
+
return _nullishCoalesce(JSON.stringify(value, null, 2), () => ( String(value)));
|
|
3984
|
+
} catch (e9) {
|
|
3985
|
+
return String(value);
|
|
3986
|
+
}
|
|
3987
|
+
};
|
|
3988
|
+
var hasArguments = (args) => args !== void 0 && args !== null && !(typeof args === "object" && Object.keys(args).length === 0);
|
|
3989
|
+
function ToolApproval({
|
|
3990
|
+
request,
|
|
3991
|
+
status = "pending",
|
|
3992
|
+
onDecide,
|
|
3993
|
+
disabled = false,
|
|
3994
|
+
error,
|
|
3995
|
+
labels: labelOverrides,
|
|
3996
|
+
renderArguments,
|
|
3997
|
+
className
|
|
3998
|
+
}) {
|
|
3999
|
+
const labels = { ...defaultToolApprovalLabels, ...labelOverrides };
|
|
4000
|
+
const options = _optionalChain([request, 'access', _418 => _418.options, 'optionalAccess', _419 => _419.length]) ? request.options : ["approve", "reject"];
|
|
4001
|
+
const [draft, setDraft] = _react.useState.call(void 0, null);
|
|
4002
|
+
const [draftError, setDraftError] = _react.useState.call(void 0, null);
|
|
4003
|
+
const titleId = _react.useId.call(void 0, );
|
|
4004
|
+
const pending = status === "pending";
|
|
4005
|
+
const decide = (decision) => {
|
|
4006
|
+
if (!disabled && pending) _optionalChain([onDecide, 'optionalCall', _420 => _420(decision)]);
|
|
4007
|
+
};
|
|
4008
|
+
const saveEdit = () => {
|
|
4009
|
+
let editedArguments;
|
|
4010
|
+
try {
|
|
4011
|
+
editedArguments = JSON.parse(_nullishCoalesce(draft, () => ( "null")));
|
|
4012
|
+
} catch (e10) {
|
|
4013
|
+
setDraftError(labels.invalidJson);
|
|
4014
|
+
return;
|
|
4015
|
+
}
|
|
4016
|
+
setDraftError(null);
|
|
4017
|
+
setDraft(null);
|
|
4018
|
+
decide({ choice: "edit", editedArguments });
|
|
4019
|
+
};
|
|
4020
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
4021
|
+
"section",
|
|
4022
|
+
{
|
|
4023
|
+
className: className ? `june-approval ${className}` : "june-approval",
|
|
4024
|
+
"aria-labelledby": titleId,
|
|
4025
|
+
"data-status": status,
|
|
4026
|
+
"data-risk": request.risk,
|
|
4027
|
+
children: [
|
|
4028
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "header", { className: "june-approval__header", children: [
|
|
4029
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "strong", { id: titleId, className: "june-approval__tool", children: request.tool }),
|
|
4030
|
+
request.risk && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-approval__badge", "data-risk": request.risk, children: labels.risk(request.risk) }),
|
|
4031
|
+
request.sideEffect && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "june-approval__badge", children: labels.sideEffect[request.sideEffect] })
|
|
4032
|
+
] }),
|
|
4033
|
+
request.description && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "june-approval__description", children: request.description }),
|
|
4034
|
+
draft !== null ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "june-approval__edit", children: [
|
|
4035
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
4036
|
+
"textarea",
|
|
4037
|
+
{
|
|
4038
|
+
"aria-label": labels.edit,
|
|
4039
|
+
className: "june-approval__editor",
|
|
4040
|
+
value: draft,
|
|
4041
|
+
onChange: (e) => setDraft(e.target.value),
|
|
4042
|
+
rows: Math.min(14, Math.max(4, draft.split("\n").length + 1)),
|
|
4043
|
+
spellCheck: false
|
|
4044
|
+
}
|
|
4045
|
+
),
|
|
4046
|
+
draftError && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { role: "alert", className: "june-approval__error", children: draftError })
|
|
4047
|
+
] }) : hasArguments(request.arguments) ? renderArguments ? renderArguments(request.arguments) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "pre", { className: "june-approval__args", children: format(request.arguments) }) : null,
|
|
4048
|
+
pending ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "june-approval__actions", children: draft !== null ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
4049
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-approval__button", "data-variant": "primary", onClick: saveEdit, disabled, children: labels.save }),
|
|
4050
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", className: "june-approval__button", onClick: () => setDraft(null), disabled, children: labels.cancel })
|
|
4051
|
+
] }) : options.map((choice) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
4052
|
+
"button",
|
|
4053
|
+
{
|
|
4054
|
+
type: "button",
|
|
4055
|
+
className: "june-approval__button",
|
|
4056
|
+
"data-variant": choice === "approve" ? "primary" : choice === "reject" ? "danger" : void 0,
|
|
4057
|
+
disabled,
|
|
4058
|
+
onClick: () => choice === "edit" ? setDraft(format(_nullishCoalesce(request.arguments, () => ( {})))) : decide({ choice }),
|
|
4059
|
+
children: labels[choice]
|
|
4060
|
+
},
|
|
4061
|
+
choice
|
|
4062
|
+
)) }) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { className: "june-approval__status", role: "status", children: [
|
|
4063
|
+
labels.status[status],
|
|
4064
|
+
status === "failed" && error ? `: ${error}` : null
|
|
4065
|
+
] })
|
|
4066
|
+
]
|
|
4067
|
+
}
|
|
4068
|
+
);
|
|
4069
|
+
}
|
|
4070
|
+
|
|
4071
|
+
// src/features/tool-approval/tool-approval-list.tsx
|
|
4072
|
+
|
|
4073
|
+
function ToolApprovalList({ approvals, pendingOnly = false, renderItem, labels, className }) {
|
|
4074
|
+
const entries = pendingOnly ? approvals.entries.filter((e) => e.status === "pending") : approvals.entries;
|
|
4075
|
+
if (entries.length === 0) return null;
|
|
4076
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: className ? `june-approval-list ${className}` : "june-approval-list", children: entries.map(
|
|
4077
|
+
(entry) => renderItem ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { children: renderItem(entry, approvals.decide) }, entry.request.id) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
4078
|
+
ToolApproval,
|
|
4079
|
+
{
|
|
4080
|
+
request: entry.request,
|
|
4081
|
+
status: entry.status,
|
|
4082
|
+
error: entry.error,
|
|
4083
|
+
labels,
|
|
4084
|
+
onDecide: (decision) => approvals.decide(entry.request.id, decision)
|
|
4085
|
+
},
|
|
4086
|
+
entry.request.id
|
|
4087
|
+
)
|
|
4088
|
+
) });
|
|
4089
|
+
}
|
|
4090
|
+
|
|
4091
|
+
// src/features/tool-approval/use-tool-approval.ts
|
|
4092
|
+
|
|
4093
|
+
var counter = 0;
|
|
4094
|
+
var nextId = () => `june-approval-${++counter}`;
|
|
4095
|
+
function useToolApproval() {
|
|
4096
|
+
const [entries, setEntries] = _react.useState.call(void 0, []);
|
|
4097
|
+
const waiters = _react.useRef.call(void 0, /* @__PURE__ */ new Map());
|
|
4098
|
+
const settle = _react.useCallback.call(void 0, (id, decision) => {
|
|
4099
|
+
const resolve = waiters.current.get(id);
|
|
4100
|
+
waiters.current.delete(id);
|
|
4101
|
+
_optionalChain([resolve, 'optionalCall', _421 => _421(decision)]);
|
|
4102
|
+
}, []);
|
|
4103
|
+
const request = _react.useCallback.call(void 0, (input) => {
|
|
4104
|
+
const id = _nullishCoalesce(input.id, () => ( nextId()));
|
|
4105
|
+
const entry = { request: { ...input, id }, status: "pending" };
|
|
4106
|
+
return new Promise((resolve) => {
|
|
4107
|
+
waiters.current.set(id, resolve);
|
|
4108
|
+
setEntries((list2) => [...list2.filter((e) => e.request.id !== id), entry]);
|
|
4109
|
+
});
|
|
4110
|
+
}, []);
|
|
4111
|
+
const decide = _react.useCallback.call(void 0,
|
|
4112
|
+
(id, decision) => {
|
|
4113
|
+
if (!waiters.current.has(id)) return;
|
|
4114
|
+
setEntries(
|
|
4115
|
+
(list2) => list2.map(
|
|
4116
|
+
(e) => e.request.id === id ? { ...e, decision, status: decision.choice === "reject" ? "rejected" : "approved" } : e
|
|
4117
|
+
)
|
|
4118
|
+
);
|
|
4119
|
+
settle(id, decision);
|
|
4120
|
+
},
|
|
4121
|
+
[settle]
|
|
4122
|
+
);
|
|
4123
|
+
const setStatus = _react.useCallback.call(void 0, (id, status, error) => {
|
|
4124
|
+
setEntries((list2) => list2.map((e) => e.request.id === id ? { ...e, status, error } : e));
|
|
4125
|
+
}, []);
|
|
4126
|
+
const dismiss = _react.useCallback.call(void 0,
|
|
4127
|
+
(id) => {
|
|
4128
|
+
const ids = id ? [id] : [...waiters.current.keys()];
|
|
4129
|
+
ids.forEach((i) => settle(i, { choice: "reject" }));
|
|
4130
|
+
setEntries((list2) => id ? list2.filter((e) => e.request.id !== id) : []);
|
|
4131
|
+
},
|
|
4132
|
+
[settle]
|
|
4133
|
+
);
|
|
4134
|
+
const confirmAction = _react.useCallback.call(void 0,
|
|
4135
|
+
async ({ name, payload, description }) => {
|
|
4136
|
+
const decision = await request({ tool: name, description, arguments: payload });
|
|
4137
|
+
return decision.choice !== "reject";
|
|
4138
|
+
},
|
|
4139
|
+
[request]
|
|
4140
|
+
);
|
|
4141
|
+
_react.useEffect.call(void 0, () => {
|
|
4142
|
+
const map = waiters.current;
|
|
4143
|
+
return () => {
|
|
4144
|
+
map.forEach((resolve) => resolve({ choice: "reject" }));
|
|
4145
|
+
map.clear();
|
|
4146
|
+
};
|
|
4147
|
+
}, []);
|
|
4148
|
+
const pending = _react.useMemo.call(void 0, () => entries.filter((e) => e.status === "pending"), [entries]);
|
|
4149
|
+
return _react.useMemo.call(void 0,
|
|
4150
|
+
() => ({ entries, pending, request, decide, setStatus, dismiss, confirmAction }),
|
|
4151
|
+
[entries, pending, request, decide, setStatus, dismiss, confirmAction]
|
|
4152
|
+
);
|
|
4153
|
+
}
|
|
4154
|
+
|
|
4155
|
+
// src/adapters/langgraph/langgraph.ts
|
|
4156
|
+
var AI_TYPES = /* @__PURE__ */ new Set(["ai", "AIMessage", "AIMessageChunk"]);
|
|
4157
|
+
function isAiMessage(message) {
|
|
4158
|
+
return message.type === void 0 || typeof message.type === "string" && AI_TYPES.has(message.type);
|
|
4159
|
+
}
|
|
4160
|
+
function messageContentToText(content) {
|
|
4161
|
+
if (typeof content === "string") return content;
|
|
4162
|
+
if (!Array.isArray(content)) return "";
|
|
4163
|
+
let text = "";
|
|
4164
|
+
for (const block of content) {
|
|
4165
|
+
if (typeof block === "string") text += block;
|
|
4166
|
+
else if (block && typeof block === "object" && block.type === "text") {
|
|
4167
|
+
const value = block.text;
|
|
4168
|
+
if (typeof value === "string") text += value;
|
|
4169
|
+
}
|
|
4170
|
+
}
|
|
4171
|
+
return text;
|
|
4172
|
+
}
|
|
4173
|
+
function parse(data) {
|
|
4174
|
+
try {
|
|
4175
|
+
return JSON.parse(data);
|
|
4176
|
+
} catch (e11) {
|
|
4177
|
+
return void 0;
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
function createLangGraphExtractor(options = {}) {
|
|
4181
|
+
const nodes = options.nodes ? new Set(options.nodes) : null;
|
|
4182
|
+
const emitted = /* @__PURE__ */ new Map();
|
|
4183
|
+
return (data, event) => {
|
|
4184
|
+
const base = event.split("|")[0];
|
|
4185
|
+
if (base === "messages") {
|
|
4186
|
+
const parsed = parse(data);
|
|
4187
|
+
if (!Array.isArray(parsed)) return null;
|
|
4188
|
+
const [chunk, metadata] = parsed;
|
|
4189
|
+
if (!chunk || typeof chunk !== "object" || !isAiMessage(chunk)) return null;
|
|
4190
|
+
if (nodes && !(typeof _optionalChain([metadata, 'optionalAccess', _422 => _422.langgraph_node]) === "string" && nodes.has(metadata.langgraph_node))) return null;
|
|
4191
|
+
return messageContentToText(chunk.content) || null;
|
|
4192
|
+
}
|
|
4193
|
+
if (base === "messages/partial" || base === "messages/complete") {
|
|
4194
|
+
const parsed = parse(data);
|
|
4195
|
+
if (!Array.isArray(parsed)) return null;
|
|
4196
|
+
let out = "";
|
|
4197
|
+
for (const message of parsed) {
|
|
4198
|
+
if (!message || typeof message !== "object" || !isAiMessage(message)) continue;
|
|
4199
|
+
const key = typeof message.id === "string" ? message.id : "__no_id__";
|
|
4200
|
+
const text = messageContentToText(message.content);
|
|
4201
|
+
const seen = _nullishCoalesce(emitted.get(key), () => ( 0));
|
|
4202
|
+
if (text.length > seen) {
|
|
4203
|
+
out += text.slice(seen);
|
|
4204
|
+
emitted.set(key, text.length);
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
return out || null;
|
|
4208
|
+
}
|
|
4209
|
+
return null;
|
|
4210
|
+
};
|
|
4211
|
+
}
|
|
4212
|
+
function langGraphStreamOptions(options = {}) {
|
|
4213
|
+
const sse = { extract: createLangGraphExtractor(options), doneSentinel: null };
|
|
4214
|
+
return { sse };
|
|
4215
|
+
}
|
|
4216
|
+
function readLangGraphInterrupts(data, event = "updates") {
|
|
4217
|
+
if (event.split("|")[0] !== "updates") return [];
|
|
4218
|
+
const parsed = parse(data);
|
|
4219
|
+
const list2 = parsed && typeof parsed === "object" ? parsed.__interrupt__ : void 0;
|
|
4220
|
+
if (!Array.isArray(list2)) return [];
|
|
4221
|
+
return list2.map((i) => i && typeof i === "object" && "value" in i ? i.value : i);
|
|
4222
|
+
}
|
|
4223
|
+
function interruptToApprovalRequests(value, idPrefix = "interrupt") {
|
|
4224
|
+
const hitl = value;
|
|
4225
|
+
if (!hitl || typeof hitl !== "object" || !Array.isArray(hitl.action_requests)) return [];
|
|
4226
|
+
const configs = Array.isArray(hitl.review_configs) ? hitl.review_configs : [];
|
|
4227
|
+
return hitl.action_requests.flatMap((action, index) => {
|
|
4228
|
+
if (!action || typeof action.name !== "string") return [];
|
|
4229
|
+
const config = configs.find((c) => _optionalChain([c, 'optionalAccess', _423 => _423.action_name]) === action.name);
|
|
4230
|
+
const allowed = Array.isArray(_optionalChain([config, 'optionalAccess', _424 => _424.allowed_decisions])) ? config.allowed_decisions.filter(
|
|
4231
|
+
(d) => d === "approve" || d === "reject" || d === "edit"
|
|
4232
|
+
) : void 0;
|
|
4233
|
+
return [
|
|
4234
|
+
{
|
|
4235
|
+
id: `${idPrefix}-${index}`,
|
|
4236
|
+
tool: action.name,
|
|
4237
|
+
description: typeof action.description === "string" ? action.description : void 0,
|
|
4238
|
+
arguments: _nullishCoalesce(action.args, () => ( action.arguments)),
|
|
4239
|
+
options: allowed && allowed.length ? allowed : void 0
|
|
4240
|
+
}
|
|
4241
|
+
];
|
|
4242
|
+
});
|
|
4243
|
+
}
|
|
4244
|
+
|
|
4245
|
+
|
|
4246
|
+
|
|
4247
|
+
|
|
4248
|
+
|
|
4249
|
+
|
|
4250
|
+
|
|
4251
|
+
|
|
4252
|
+
|
|
4253
|
+
|
|
4254
|
+
|
|
4255
|
+
|
|
4256
|
+
|
|
4257
|
+
|
|
4258
|
+
|
|
4259
|
+
|
|
4260
|
+
|
|
4261
|
+
|
|
4262
|
+
|
|
4263
|
+
|
|
4264
|
+
|
|
4265
|
+
|
|
4266
|
+
|
|
4267
|
+
|
|
4268
|
+
|
|
4269
|
+
|
|
4270
|
+
|
|
4271
|
+
|
|
4272
|
+
|
|
4273
|
+
|
|
4274
|
+
|
|
4275
|
+
|
|
4276
|
+
|
|
4277
|
+
|
|
4278
|
+
|
|
4279
|
+
|
|
4280
|
+
|
|
4281
|
+
|
|
4282
|
+
|
|
4283
|
+
|
|
4284
|
+
|
|
4285
|
+
|
|
4286
|
+
|
|
4287
|
+
|
|
4288
|
+
|
|
4289
|
+
|
|
4290
|
+
|
|
4291
|
+
|
|
4292
|
+
|
|
4293
|
+
|
|
4294
|
+
|
|
4295
|
+
|
|
4296
|
+
|
|
4297
|
+
|
|
4298
|
+
|
|
4299
|
+
|
|
4300
|
+
|
|
4301
|
+
|
|
4302
|
+
|
|
4303
|
+
|
|
4304
|
+
|
|
4305
|
+
|
|
4306
|
+
|
|
4307
|
+
|
|
4308
|
+
|
|
4309
|
+
|
|
4310
|
+
|
|
4311
|
+
|
|
4312
|
+
|
|
4313
|
+
|
|
4314
|
+
exports.useJuneTheme = useJuneTheme; exports.JuneRuntimeContext = JuneRuntimeContext; exports.useJuneRuntime = useJuneRuntime; exports.defaultParseActionEvent = defaultParseActionEvent; exports.runAction = runAction; exports.createActionHandler = createActionHandler; exports.JuneDocumentContext = JuneDocumentContext; exports.useDocumentOptions = useDocumentOptions; exports.mergeDocuments = mergeDocuments; exports.createDocumentStore = createDocumentStore; exports.rememberDocumentEcho = rememberDocumentEcho; exports.isDocumentEcho = isDocumentEcho; exports.getDefaultDocumentStore = getDefaultDocumentStore; exports.updateDocumentInProgram = updateDocumentInProgram; exports.readDocumentFromProgram = readDocumentFromProgram; exports.useDocumentBridge = useDocumentBridge; exports.DocumentBridge = DocumentBridge; exports.darkTheme = darkTheme; exports.lightTheme = lightTheme; exports.isThemeSet = isThemeSet; exports.resolveThemeVars = resolveThemeVars; exports.createTheme = createTheme; exports.useResolvedTheme = useResolvedTheme; exports.JuneThemeProvider = JuneThemeProvider; exports.normalizeProgram = normalizeProgram; exports.splitResponse = splitResponse; exports.resolveMixedContent = resolveMixedContent; exports.JuneErrorBoundary = JuneErrorBoundary; exports.MarkdownText = MarkdownText; exports.JuneRenderer = JuneRenderer; exports.validateComponentDefinition = validateComponentDefinition; exports.defineComponent = defineComponent; exports.isJuneComponent = isJuneComponent; exports.ComponentRegistry = ComponentRegistry; exports.createComponentRegistry = createComponentRegistry; exports.useRegistryLibrary = useRegistryLibrary; exports.defineJuneComponent = defineJuneComponent; exports.createJuneLibrary = createJuneLibrary; exports.defineJuneAction = defineJuneAction; exports.toAsyncIterable = toAsyncIterable; exports.consumeStream = consumeStream; exports.sseToText = sseToText; exports.jsonFieldExtractor = jsonFieldExtractor; exports.fetchStream = fetchStream; exports.useJuneStream = useJuneStream; exports.registerDocumentExporter = registerDocumentExporter; exports.listDocumentExporters = listDocumentExporters; exports.exportDocument = exportDocument; exports.downloadResult = downloadResult; exports.copyDocumentToClipboard = copyDocumentToClipboard; exports.ALL_EXPORT_FORMATS = ALL_EXPORT_FORMATS; exports.DOCUMENT_COMPONENT_DESCRIPTION = DOCUMENT_COMPONENT_DESCRIPTION; exports.DocumentEditor = DocumentEditor; exports.JuneDocumentEditorComponent = JuneDocumentEditorComponent; exports.withDocumentEditor = withDocumentEditor; exports.getDocumentEditorRules = getDocumentEditorRules; exports.getDocumentEditorInstructions = getDocumentEditorInstructions; exports.documentPromptOptions = documentPromptOptions; exports.serializeDocumentsForAgent = serializeDocumentsForAgent; exports.defaultToolApprovalLabels = defaultToolApprovalLabels; exports.ToolApproval = ToolApproval; exports.ToolApprovalList = ToolApprovalList; exports.useToolApproval = useToolApproval; exports.messageContentToText = messageContentToText; exports.createLangGraphExtractor = createLangGraphExtractor; exports.langGraphStreamOptions = langGraphStreamOptions; exports.readLangGraphInterrupts = readLangGraphInterrupts; exports.interruptToApprovalRequests = interruptToApprovalRequests;
|