@june-ai/june-ui 0.2.5 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/{chunk-CZE533PM.js → chunk-VFZI3SEL.js} +186 -9
- package/dist/{document-engine-IAY2T6B3.js → document-engine-RKD5SLXR.js} +1 -1
- package/dist/index.cjs +685 -280
- package/dist/index.d.cts +44 -1
- package/dist/index.d.ts +44 -1
- package/dist/index.js +532 -309
- package/dist/june.css +43 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,24 +6,28 @@ import {
|
|
|
6
6
|
docToHtml,
|
|
7
7
|
docToMarkdown,
|
|
8
8
|
emptyDoc,
|
|
9
|
+
getPageCount,
|
|
9
10
|
getSearchState,
|
|
10
11
|
gotoMatch,
|
|
11
12
|
isJuneError,
|
|
12
13
|
parseDocumentContent,
|
|
14
|
+
remeasurePagination,
|
|
13
15
|
replaceAll,
|
|
14
16
|
replaceCurrent,
|
|
15
17
|
sanitizeHref,
|
|
18
|
+
setPaginationConfig,
|
|
16
19
|
setSearch,
|
|
17
20
|
slugify,
|
|
18
21
|
toJuneError,
|
|
19
22
|
wordCountOf
|
|
20
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-VFZI3SEL.js";
|
|
21
24
|
|
|
22
25
|
// src/renderer/renderer.tsx
|
|
23
26
|
import { Renderer } from "@openuidev/react-lang";
|
|
24
27
|
import {
|
|
25
28
|
Fragment,
|
|
26
29
|
useCallback,
|
|
30
|
+
useEffect,
|
|
27
31
|
useMemo as useMemo2,
|
|
28
32
|
useRef
|
|
29
33
|
} from "react";
|
|
@@ -101,6 +105,316 @@ function useDocumentValue(store, id, selector) {
|
|
|
101
105
|
return useSyncExternalStore(store.subscribe, read, read);
|
|
102
106
|
}
|
|
103
107
|
|
|
108
|
+
// src/components/document-editor/document-merge.ts
|
|
109
|
+
var MAX_CELLS = 4e6;
|
|
110
|
+
function matchIndices(a, b) {
|
|
111
|
+
const n = a.length;
|
|
112
|
+
const m = b.length;
|
|
113
|
+
if (n * m > MAX_CELLS) return null;
|
|
114
|
+
const dp = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));
|
|
115
|
+
for (let i2 = n - 1; i2 >= 0; i2--) {
|
|
116
|
+
for (let j2 = m - 1; j2 >= 0; j2--) {
|
|
117
|
+
dp[i2][j2] = a[i2] === b[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const matches = /* @__PURE__ */ new Map();
|
|
121
|
+
let i = 0;
|
|
122
|
+
let j = 0;
|
|
123
|
+
while (i < n && j < m) {
|
|
124
|
+
if (a[i] === b[j]) {
|
|
125
|
+
matches.set(i, j);
|
|
126
|
+
i++;
|
|
127
|
+
j++;
|
|
128
|
+
} else if (dp[i + 1][j] >= dp[i][j + 1]) i++;
|
|
129
|
+
else j++;
|
|
130
|
+
}
|
|
131
|
+
return matches;
|
|
132
|
+
}
|
|
133
|
+
var same = (x, y) => x.length === y.length && x.every((v, i) => v === y[i]);
|
|
134
|
+
function mergeBlocks(base, local, incoming) {
|
|
135
|
+
if (same(local, base)) return { blocks: incoming, conflicts: 0 };
|
|
136
|
+
if (same(incoming, base) || same(local, incoming)) return { blocks: local, conflicts: 0 };
|
|
137
|
+
const ml = matchIndices(base, local);
|
|
138
|
+
const mn = matchIndices(base, incoming);
|
|
139
|
+
if (!ml || !mn) return { blocks: null, conflicts: 1 };
|
|
140
|
+
const anchors = [...ml.keys()].filter((i) => mn.has(i)).sort((x, y) => x - y);
|
|
141
|
+
const out = [];
|
|
142
|
+
let conflicts = 0;
|
|
143
|
+
let pb = -1;
|
|
144
|
+
let pl = -1;
|
|
145
|
+
let pn = -1;
|
|
146
|
+
const region = (bEnd, lEnd, nEnd) => {
|
|
147
|
+
const B = base.slice(pb + 1, bEnd);
|
|
148
|
+
const L = local.slice(pl + 1, lEnd);
|
|
149
|
+
const N = incoming.slice(pn + 1, nEnd);
|
|
150
|
+
if (B.length === L.length && L.length === N.length) {
|
|
151
|
+
for (let k = 0; k < B.length; k++) {
|
|
152
|
+
if (L[k] === B[k]) out.push(N[k]);
|
|
153
|
+
else if (N[k] === B[k] || N[k] === L[k]) out.push(L[k]);
|
|
154
|
+
else {
|
|
155
|
+
conflicts++;
|
|
156
|
+
out.push(L[k]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (same(L, B)) out.push(...N);
|
|
162
|
+
else if (same(N, B) || same(L, N)) out.push(...L);
|
|
163
|
+
else {
|
|
164
|
+
conflicts++;
|
|
165
|
+
out.push(...L);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
for (const a of anchors) {
|
|
169
|
+
const la = ml.get(a);
|
|
170
|
+
const na = mn.get(a);
|
|
171
|
+
region(a, la, na);
|
|
172
|
+
out.push(local[la]);
|
|
173
|
+
pb = a;
|
|
174
|
+
pl = la;
|
|
175
|
+
pn = na;
|
|
176
|
+
}
|
|
177
|
+
region(base.length, local.length, incoming.length);
|
|
178
|
+
return { blocks: out, conflicts };
|
|
179
|
+
}
|
|
180
|
+
function mergeDocuments(base, local, incoming) {
|
|
181
|
+
const toBlocks = (d) => (d.content ?? []).map((node) => JSON.stringify(node));
|
|
182
|
+
const { blocks: blocks2, conflicts } = mergeBlocks(toBlocks(base), toBlocks(local), toBlocks(incoming));
|
|
183
|
+
if (!blocks2) return { doc: local, conflicts: Math.max(1, conflicts) };
|
|
184
|
+
if (blocks2.length === 0) return { doc: emptyDoc(), conflicts };
|
|
185
|
+
return { doc: { type: "doc", content: blocks2.map((b) => JSON.parse(b)) }, conflicts };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/components/document-editor/document-store.ts
|
|
189
|
+
var incomingKey = (doc) => JSON.stringify([doc.title, docKey(doc.json)]);
|
|
190
|
+
function createDocumentStore() {
|
|
191
|
+
const records = /* @__PURE__ */ new Map();
|
|
192
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
193
|
+
const emit = () => listeners.forEach((listener) => listener());
|
|
194
|
+
const put = (record) => {
|
|
195
|
+
records.set(record.id, record);
|
|
196
|
+
emit();
|
|
197
|
+
};
|
|
198
|
+
const fresh2 = (doc) => ({
|
|
199
|
+
id: doc.id,
|
|
200
|
+
title: doc.title,
|
|
201
|
+
documentType: doc.documentType,
|
|
202
|
+
status: doc.status,
|
|
203
|
+
json: doc.json,
|
|
204
|
+
baseJson: doc.json,
|
|
205
|
+
baseTitle: doc.title,
|
|
206
|
+
lastIncomingKey: incomingKey(doc),
|
|
207
|
+
revision: 0,
|
|
208
|
+
origin: "external",
|
|
209
|
+
pending: null,
|
|
210
|
+
updatedAt: Date.now()
|
|
211
|
+
});
|
|
212
|
+
return {
|
|
213
|
+
get: (id) => records.get(id),
|
|
214
|
+
list: () => [...records.values()],
|
|
215
|
+
subscribe: (listener) => {
|
|
216
|
+
listeners.add(listener);
|
|
217
|
+
return () => {
|
|
218
|
+
listeners.delete(listener);
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
receive(incoming) {
|
|
222
|
+
const current = records.get(incoming.id);
|
|
223
|
+
if (!current) {
|
|
224
|
+
put(fresh2(incoming));
|
|
225
|
+
return "created";
|
|
226
|
+
}
|
|
227
|
+
const key = incomingKey(incoming);
|
|
228
|
+
if (current.lastIncomingKey === key) return "unchanged";
|
|
229
|
+
const userEditedBody = docKey(current.json) !== docKey(current.baseJson);
|
|
230
|
+
const userEditedTitle = current.title !== current.baseTitle;
|
|
231
|
+
const title = userEditedTitle ? current.title : incoming.title;
|
|
232
|
+
const meta = {
|
|
233
|
+
documentType: incoming.documentType ?? current.documentType,
|
|
234
|
+
status: incoming.status ?? current.status
|
|
235
|
+
};
|
|
236
|
+
if (!userEditedBody) {
|
|
237
|
+
put({
|
|
238
|
+
...current,
|
|
239
|
+
...meta,
|
|
240
|
+
title,
|
|
241
|
+
baseTitle: incoming.title,
|
|
242
|
+
json: incoming.json,
|
|
243
|
+
baseJson: incoming.json,
|
|
244
|
+
lastIncomingKey: key,
|
|
245
|
+
revision: current.revision + 1,
|
|
246
|
+
origin: "external",
|
|
247
|
+
pending: null,
|
|
248
|
+
updatedAt: Date.now()
|
|
249
|
+
});
|
|
250
|
+
return "applied";
|
|
251
|
+
}
|
|
252
|
+
const { doc, conflicts } = mergeDocuments(current.baseJson, current.json, incoming.json);
|
|
253
|
+
if (conflicts === 0) {
|
|
254
|
+
put({
|
|
255
|
+
...current,
|
|
256
|
+
...meta,
|
|
257
|
+
title,
|
|
258
|
+
baseTitle: incoming.title,
|
|
259
|
+
json: doc,
|
|
260
|
+
baseJson: incoming.json,
|
|
261
|
+
lastIncomingKey: key,
|
|
262
|
+
revision: current.revision + 1,
|
|
263
|
+
origin: "external",
|
|
264
|
+
pending: null,
|
|
265
|
+
updatedAt: Date.now()
|
|
266
|
+
});
|
|
267
|
+
return "merged";
|
|
268
|
+
}
|
|
269
|
+
put({
|
|
270
|
+
...current,
|
|
271
|
+
...meta,
|
|
272
|
+
lastIncomingKey: key,
|
|
273
|
+
pending: { json: incoming.json, title: incoming.title, merged: doc, conflicts }
|
|
274
|
+
});
|
|
275
|
+
return "conflict";
|
|
276
|
+
},
|
|
277
|
+
hydrate(doc) {
|
|
278
|
+
put(fresh2(doc));
|
|
279
|
+
},
|
|
280
|
+
edit(id, json, origin) {
|
|
281
|
+
const current = records.get(id);
|
|
282
|
+
if (!current || docKey(current.json) === docKey(json)) return;
|
|
283
|
+
put({ ...current, json, origin, revision: current.revision + 1, updatedAt: Date.now() });
|
|
284
|
+
},
|
|
285
|
+
setTitle(id, title) {
|
|
286
|
+
const current = records.get(id);
|
|
287
|
+
if (!current || current.title === title) return;
|
|
288
|
+
put({ ...current, title, updatedAt: Date.now() });
|
|
289
|
+
},
|
|
290
|
+
resolve(id, choice) {
|
|
291
|
+
const current = records.get(id);
|
|
292
|
+
const pending = current?.pending;
|
|
293
|
+
if (!current || !pending) return;
|
|
294
|
+
if (choice === "keep") {
|
|
295
|
+
put({ ...current, baseJson: pending.json, baseTitle: pending.title, pending: null });
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const json = choice === "merge" ? pending.merged : pending.json;
|
|
299
|
+
const title = choice === "merge" ? current.title : pending.title;
|
|
300
|
+
put({
|
|
301
|
+
...current,
|
|
302
|
+
title,
|
|
303
|
+
json,
|
|
304
|
+
baseJson: pending.json,
|
|
305
|
+
baseTitle: pending.title,
|
|
306
|
+
revision: current.revision + 1,
|
|
307
|
+
origin: "external",
|
|
308
|
+
pending: null,
|
|
309
|
+
updatedAt: Date.now()
|
|
310
|
+
});
|
|
311
|
+
},
|
|
312
|
+
remove(id) {
|
|
313
|
+
if (records.delete(id)) emit();
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
var echoes = /* @__PURE__ */ new WeakMap();
|
|
318
|
+
var ECHO_MEMORY = 8;
|
|
319
|
+
function rememberDocumentEcho(store, id, content) {
|
|
320
|
+
let byId = echoes.get(store);
|
|
321
|
+
if (!byId) echoes.set(store, byId = /* @__PURE__ */ new Map());
|
|
322
|
+
const list2 = byId.get(id) ?? [];
|
|
323
|
+
list2.push(content);
|
|
324
|
+
if (list2.length > ECHO_MEMORY) list2.shift();
|
|
325
|
+
byId.set(id, list2);
|
|
326
|
+
}
|
|
327
|
+
function isDocumentEcho(store, id, content) {
|
|
328
|
+
return !!content && !!echoes.get(store)?.get(id)?.includes(content);
|
|
329
|
+
}
|
|
330
|
+
var defaultStore = null;
|
|
331
|
+
function getDefaultDocumentStore() {
|
|
332
|
+
if (!defaultStore) defaultStore = createDocumentStore();
|
|
333
|
+
return defaultStore;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/utils/update-document-program.ts
|
|
337
|
+
function readString(src, i) {
|
|
338
|
+
if (src[i] !== '"') return null;
|
|
339
|
+
let j = i + 1;
|
|
340
|
+
let value = "";
|
|
341
|
+
while (j < src.length) {
|
|
342
|
+
const ch = src[j];
|
|
343
|
+
if (ch === '"') return { start: i, end: j + 1, value };
|
|
344
|
+
if (ch === "\\") {
|
|
345
|
+
const next = src[j + 1];
|
|
346
|
+
if (next === void 0) return null;
|
|
347
|
+
if (next === "u" && /^[0-9a-fA-F]{4}$/.test(src.slice(j + 2, j + 6))) {
|
|
348
|
+
value += String.fromCharCode(parseInt(src.slice(j + 2, j + 6), 16));
|
|
349
|
+
j += 6;
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
value += next === "n" ? "\n" : next === "t" ? " " : next === "r" ? "\r" : next === "b" ? "\b" : next === "f" ? "\f" : next;
|
|
353
|
+
j += 2;
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
value += ch;
|
|
357
|
+
j++;
|
|
358
|
+
}
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
var skipSpace = (src, i) => {
|
|
362
|
+
while (i < src.length && /\s/.test(src[i])) i++;
|
|
363
|
+
return i;
|
|
364
|
+
};
|
|
365
|
+
var toLangString = (value) => JSON.stringify(value);
|
|
366
|
+
function updateDocumentInProgram(program, documentId, update, componentName = "DocumentEditor") {
|
|
367
|
+
const re = new RegExp(`\\b${componentName}\\s*\\(`, "g");
|
|
368
|
+
let match;
|
|
369
|
+
while (match = re.exec(program)) {
|
|
370
|
+
const args = [];
|
|
371
|
+
let i = match.index + match[0].length;
|
|
372
|
+
for (let n = 0; n < 3; n++) {
|
|
373
|
+
i = skipSpace(program, i);
|
|
374
|
+
const tok = readString(program, i);
|
|
375
|
+
if (!tok) break;
|
|
376
|
+
args.push(tok);
|
|
377
|
+
i = skipSpace(program, tok.end);
|
|
378
|
+
if (program[i] === ",") i++;
|
|
379
|
+
else break;
|
|
380
|
+
}
|
|
381
|
+
if (args[0]?.value !== documentId) continue;
|
|
382
|
+
const edits = [];
|
|
383
|
+
if (update.title !== void 0 && args[1] && args[1].value !== update.title) {
|
|
384
|
+
edits.push({ start: args[1].start, end: args[1].end, text: toLangString(update.title) });
|
|
385
|
+
}
|
|
386
|
+
if (update.content !== void 0 && args[2] && args[2].value !== update.content) {
|
|
387
|
+
edits.push({ start: args[2].start, end: args[2].end, text: toLangString(update.content) });
|
|
388
|
+
}
|
|
389
|
+
if (!args[2] && update.content !== void 0) return null;
|
|
390
|
+
let out = program;
|
|
391
|
+
for (const e of edits.sort((a, b) => b.start - a.start)) {
|
|
392
|
+
out = out.slice(0, e.start) + e.text + out.slice(e.end);
|
|
393
|
+
}
|
|
394
|
+
return out;
|
|
395
|
+
}
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
function readDocumentFromProgram(program, documentId, componentName = "DocumentEditor") {
|
|
399
|
+
const re = new RegExp(`\\b${componentName}\\s*\\(`, "g");
|
|
400
|
+
let match;
|
|
401
|
+
while (match = re.exec(program)) {
|
|
402
|
+
const args = [];
|
|
403
|
+
let i = match.index + match[0].length;
|
|
404
|
+
for (let n = 0; n < 3; n++) {
|
|
405
|
+
i = skipSpace(program, i);
|
|
406
|
+
const tok = readString(program, i);
|
|
407
|
+
if (!tok) break;
|
|
408
|
+
args.push(tok);
|
|
409
|
+
i = skipSpace(program, tok.end);
|
|
410
|
+
if (program[i] === ",") i++;
|
|
411
|
+
else break;
|
|
412
|
+
}
|
|
413
|
+
if (args[0]?.value === documentId && args[1] && args[2]) return { title: args[1].value, content: args[2].value };
|
|
414
|
+
}
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
|
|
104
418
|
// src/core/context.ts
|
|
105
419
|
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
106
420
|
var JuneContext = createContext2({
|
|
@@ -659,14 +973,43 @@ function JuneRenderer(props) {
|
|
|
659
973
|
onError,
|
|
660
974
|
onJuneError,
|
|
661
975
|
documents,
|
|
976
|
+
onResponseChange,
|
|
977
|
+
responseChangeDelay = 300,
|
|
662
978
|
loadingFallback = null,
|
|
663
979
|
emptyFallback = null,
|
|
664
980
|
errorFallback,
|
|
665
981
|
resetKeys,
|
|
666
982
|
...rendererProps
|
|
667
983
|
} = props;
|
|
668
|
-
const latest = useRef({ actions, confirmAction, parseActionEvent, onAction, onError, onJuneError });
|
|
669
|
-
latest.current = { actions, confirmAction, parseActionEvent, onAction, onError, onJuneError };
|
|
984
|
+
const latest = useRef({ actions, confirmAction, parseActionEvent, onAction, onError, onJuneError, onResponseChange, responseChangeDelay, response });
|
|
985
|
+
latest.current = { actions, confirmAction, parseActionEvent, onAction, onError, onJuneError, onResponseChange, responseChangeDelay, response };
|
|
986
|
+
const syncTimers = useRef(/* @__PURE__ */ new Map());
|
|
987
|
+
useEffect(() => {
|
|
988
|
+
const timers = syncTimers.current;
|
|
989
|
+
return () => {
|
|
990
|
+
timers.forEach(clearTimeout);
|
|
991
|
+
timers.clear();
|
|
992
|
+
};
|
|
993
|
+
}, []);
|
|
994
|
+
const syncProgram = useCallback(async (change, store) => {
|
|
995
|
+
const l = latest.current;
|
|
996
|
+
if (!l.onResponseChange || typeof l.response !== "string") return;
|
|
997
|
+
const record = store.get(change.documentId);
|
|
998
|
+
const title = record?.title ?? change.title;
|
|
999
|
+
const json = record?.json ?? change.json;
|
|
1000
|
+
try {
|
|
1001
|
+
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-RKD5SLXR.js");
|
|
1002
|
+
const markdown = docToMarkdown2(json);
|
|
1003
|
+
const program = latest.current.response;
|
|
1004
|
+
if (typeof program !== "string") return;
|
|
1005
|
+
const next = updateDocumentInProgram(program, change.documentId, { title, content: markdown });
|
|
1006
|
+
if (next === null || next === program) return;
|
|
1007
|
+
rememberDocumentEcho(store, change.documentId, markdown);
|
|
1008
|
+
latest.current.onResponseChange?.(next, { documentId: change.documentId, title, markdown, source: change.source });
|
|
1009
|
+
} catch (error) {
|
|
1010
|
+
latest.current.onJuneError?.(toJuneError(error, "DOCUMENT_ERROR", "Updating the program failed"));
|
|
1011
|
+
}
|
|
1012
|
+
}, []);
|
|
670
1013
|
const handleAction = useCallback((event) => {
|
|
671
1014
|
const l = latest.current;
|
|
672
1015
|
l.onAction?.(event);
|
|
@@ -705,12 +1048,27 @@ function JuneRenderer(props) {
|
|
|
705
1048
|
const documentOptions = useMemo2(
|
|
706
1049
|
() => ({
|
|
707
1050
|
...documents,
|
|
1051
|
+
onDocumentChange: (change) => {
|
|
1052
|
+
documents?.onDocumentChange?.(change);
|
|
1053
|
+
if (!latest.current.onResponseChange || change.source === "ai") return;
|
|
1054
|
+
const store = documents?.store ?? getDefaultDocumentStore();
|
|
1055
|
+
const timers = syncTimers.current;
|
|
1056
|
+
const prev = timers.get(change.documentId);
|
|
1057
|
+
if (prev) clearTimeout(prev);
|
|
1058
|
+
timers.set(
|
|
1059
|
+
change.documentId,
|
|
1060
|
+
setTimeout(() => {
|
|
1061
|
+
timers.delete(change.documentId);
|
|
1062
|
+
void syncProgram(change, store);
|
|
1063
|
+
}, Math.max(0, latest.current.responseChangeDelay))
|
|
1064
|
+
);
|
|
1065
|
+
},
|
|
708
1066
|
onError: (error) => {
|
|
709
1067
|
documents?.onError?.(error);
|
|
710
1068
|
latest.current.onJuneError?.(error);
|
|
711
1069
|
}
|
|
712
1070
|
}),
|
|
713
|
-
[documents]
|
|
1071
|
+
[documents, syncProgram]
|
|
714
1072
|
);
|
|
715
1073
|
const parts = useMemo2(
|
|
716
1074
|
() => mixedContent && response ? splitResponse(response, { streaming: isStreaming }) : null,
|
|
@@ -803,7 +1161,7 @@ function defineJuneAction(definition) {
|
|
|
803
1161
|
}
|
|
804
1162
|
|
|
805
1163
|
// src/streaming/use-june-stream.ts
|
|
806
|
-
import { useCallback as useCallback2, useEffect, useMemo as useMemo3, useRef as useRef2, useState } from "react";
|
|
1164
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef2, useState } from "react";
|
|
807
1165
|
|
|
808
1166
|
// src/streaming/stream-handler.ts
|
|
809
1167
|
function isReadableStream(source) {
|
|
@@ -1058,7 +1416,7 @@ function useJuneStream(options = {}) {
|
|
|
1058
1416
|
clearTimer();
|
|
1059
1417
|
setState(INITIAL);
|
|
1060
1418
|
}, [clearTimer]);
|
|
1061
|
-
|
|
1419
|
+
useEffect2(
|
|
1062
1420
|
() => () => {
|
|
1063
1421
|
runIdRef.current++;
|
|
1064
1422
|
controllerRef.current?.abort();
|
|
@@ -1095,7 +1453,7 @@ function DocumentDraft({ title, words, state }) {
|
|
|
1095
1453
|
|
|
1096
1454
|
// src/components/document-editor/document-editor.tsx
|
|
1097
1455
|
import { useEditor } from "@tiptap/react";
|
|
1098
|
-
import { useCallback as useCallback3, useEffect as
|
|
1456
|
+
import { useCallback as useCallback3, useEffect as useEffect6, useId, useMemo as useMemo4, useRef as useRef7, useState as useState6 } from "react";
|
|
1099
1457
|
|
|
1100
1458
|
// src/components/document-editor/document-bubble.tsx
|
|
1101
1459
|
import { useEditorState as useEditorState2 } from "@tiptap/react";
|
|
@@ -1327,12 +1685,12 @@ function Icon({ name, size = 16 }) {
|
|
|
1327
1685
|
}
|
|
1328
1686
|
|
|
1329
1687
|
// src/components/document-editor/document-popover.tsx
|
|
1330
|
-
import { useEffect as
|
|
1688
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState2 } from "react";
|
|
1331
1689
|
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1332
1690
|
function Dropdown({ trigger, children, align = "start", className, onOpen }) {
|
|
1333
1691
|
const [open, setOpen] = useState2(false);
|
|
1334
1692
|
const anchor = useRef3(null);
|
|
1335
|
-
|
|
1693
|
+
useEffect3(() => {
|
|
1336
1694
|
if (!open) return;
|
|
1337
1695
|
const onDown = (e) => {
|
|
1338
1696
|
if (anchor.current && !anchor.current.contains(e.target)) setOpen(false);
|
|
@@ -1952,60 +2310,116 @@ function DocumentBubbleMenu({ editor }) {
|
|
|
1952
2310
|
|
|
1953
2311
|
// src/components/document-editor/document-content.tsx
|
|
1954
2312
|
import { EditorContent } from "@tiptap/react";
|
|
1955
|
-
import { useEffect as
|
|
1956
|
-
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
2313
|
+
import { useEffect as useEffect4, useLayoutEffect, useRef as useRef5, useState as useState4 } from "react";
|
|
2314
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2315
|
+
var PAGE_WIDTH = 794;
|
|
1957
2316
|
var PAGE_HEIGHT = 1123;
|
|
1958
|
-
var
|
|
2317
|
+
var PAGE_GAP = 28;
|
|
2318
|
+
var CANVAS_PAD_X = 24;
|
|
2319
|
+
var useIsoLayoutEffect = typeof window === "undefined" ? useEffect4 : useLayoutEffect;
|
|
2320
|
+
function geometry(width) {
|
|
2321
|
+
const ratio = width / PAGE_WIDTH;
|
|
2322
|
+
const height = Math.round(PAGE_HEIGHT * ratio);
|
|
2323
|
+
const padX = Math.round(Math.min(96, Math.max(20, width * 0.12)));
|
|
2324
|
+
const padY = Math.round(Math.min(96, Math.max(36, 96 * ratio)));
|
|
2325
|
+
return { width, height, padX, padY };
|
|
2326
|
+
}
|
|
1959
2327
|
function DocumentContent({ editor, layout = "page", zoom = 1, showMarks = false, onMetrics }) {
|
|
1960
2328
|
const canvas = useRef5(null);
|
|
1961
2329
|
const sheet = useRef5(null);
|
|
2330
|
+
const [avail, setAvail] = useState4(PAGE_WIDTH);
|
|
2331
|
+
const [sheetH, setSheetH] = useState4(0);
|
|
1962
2332
|
const [pages, setPages] = useState4(1);
|
|
1963
2333
|
const metricsRef = useRef5(onMetrics);
|
|
1964
2334
|
metricsRef.current = onMetrics;
|
|
1965
2335
|
const last = useRef5({ page: 0, pages: 0 });
|
|
1966
|
-
|
|
2336
|
+
const paged = layout === "page";
|
|
2337
|
+
const width = paged ? Math.max(260, Math.min(PAGE_WIDTH, Math.floor(avail / zoom))) : Math.max(260, Math.floor(avail / zoom));
|
|
2338
|
+
const g = geometry(width);
|
|
2339
|
+
const stride = g.height + PAGE_GAP;
|
|
2340
|
+
useIsoLayoutEffect(() => {
|
|
1967
2341
|
const c = canvas.current;
|
|
1968
|
-
|
|
1969
|
-
const
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
const
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
2342
|
+
if (!c) return;
|
|
2343
|
+
const read = () => {
|
|
2344
|
+
const cs = typeof getComputedStyle === "function" ? getComputedStyle(c) : null;
|
|
2345
|
+
const padX = cs ? (parseFloat(cs.paddingLeft) || 0) + (parseFloat(cs.paddingRight) || 0) : CANVAS_PAD_X * 2;
|
|
2346
|
+
const w = c.clientWidth - padX;
|
|
2347
|
+
if (w > 0) setAvail(w);
|
|
2348
|
+
};
|
|
2349
|
+
read();
|
|
2350
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
2351
|
+
const ro = new ResizeObserver(read);
|
|
2352
|
+
ro.observe(c);
|
|
2353
|
+
return () => ro.disconnect();
|
|
2354
|
+
}, []);
|
|
2355
|
+
useEffect4(() => {
|
|
2356
|
+
if (!editor) return;
|
|
2357
|
+
setPaginationConfig(editor, {
|
|
2358
|
+
enabled: paged,
|
|
2359
|
+
pageHeight: g.height,
|
|
2360
|
+
gap: PAGE_GAP,
|
|
2361
|
+
padTop: g.padY,
|
|
2362
|
+
padBottom: g.padY
|
|
2363
|
+
});
|
|
2364
|
+
}, [editor, paged, g.height, g.padY, width]);
|
|
2365
|
+
useEffect4(() => {
|
|
2366
|
+
const c = canvas.current;
|
|
2367
|
+
const s = sheet.current;
|
|
2368
|
+
if (!editor || !c || !s) return;
|
|
2369
|
+
const prose = editor.view.dom;
|
|
2370
|
+
const report = () => {
|
|
2371
|
+
const total = paged ? getPageCount(editor) : 1;
|
|
1978
2372
|
setPages(total);
|
|
2373
|
+
setSheetH(s.offsetHeight);
|
|
2374
|
+
const cRect = c.getBoundingClientRect();
|
|
2375
|
+
const sRect = s.getBoundingClientRect();
|
|
2376
|
+
const y = (cRect.top + c.clientHeight * 0.35 - sRect.top) / zoom;
|
|
2377
|
+
const page = paged ? Math.min(total, Math.max(1, Math.floor(y / stride) + 1)) : 1;
|
|
1979
2378
|
if (last.current.page !== page || last.current.pages !== total) {
|
|
1980
2379
|
last.current = { page, pages: total };
|
|
1981
2380
|
metricsRef.current?.({ page, pages: total });
|
|
1982
2381
|
}
|
|
1983
2382
|
};
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2383
|
+
report();
|
|
2384
|
+
editor.on("transaction", report);
|
|
2385
|
+
c.addEventListener("scroll", report, { passive: true });
|
|
2386
|
+
const onLoad = () => remeasurePagination(editor);
|
|
2387
|
+
prose.addEventListener("load", onLoad, true);
|
|
2388
|
+
const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(report) : null;
|
|
2389
|
+
ro?.observe(s);
|
|
1990
2390
|
return () => {
|
|
1991
|
-
|
|
2391
|
+
editor.off("transaction", report);
|
|
2392
|
+
c.removeEventListener("scroll", report);
|
|
2393
|
+
prose.removeEventListener("load", onLoad, true);
|
|
1992
2394
|
ro?.disconnect();
|
|
1993
|
-
editor?.off("update", measure);
|
|
1994
2395
|
};
|
|
1995
|
-
}, [editor,
|
|
1996
|
-
const
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2396
|
+
}, [editor, paged, zoom, stride]);
|
|
2397
|
+
const minHeight = paged ? pages * g.height + (pages - 1) * PAGE_GAP : void 0;
|
|
2398
|
+
const sheetStyle = {
|
|
2399
|
+
width,
|
|
2400
|
+
minHeight,
|
|
2401
|
+
transform: zoom === 1 ? void 0 : `scale(${zoom})`,
|
|
2402
|
+
"--jd-pad-x": `${g.padX}px`,
|
|
2403
|
+
"--jd-pad-y": `${g.padY}px`
|
|
2404
|
+
};
|
|
2405
|
+
const zoomerStyle = {
|
|
2406
|
+
width: width * zoom,
|
|
2407
|
+
height: zoom === 1 ? void 0 : Math.max(sheetH, minHeight ?? 0) * zoom
|
|
2408
|
+
};
|
|
2409
|
+
return /* @__PURE__ */ jsx9("div", { ref: canvas, className: "june-doc__content june-doc__canvas", "data-layout": layout, "data-marks": showMarks || void 0, children: /* @__PURE__ */ jsx9("div", { className: "june-doc__zoomer", style: zoomerStyle, children: /* @__PURE__ */ jsxs7("div", { ref: sheet, className: "june-doc__sheet", style: sheetStyle, children: [
|
|
2410
|
+
paged ? /* @__PURE__ */ jsx9("div", { className: "june-doc__pages", "aria-hidden": "true", children: Array.from({ length: pages }, (_, i) => /* @__PURE__ */ jsx9("div", { className: "june-doc__page", style: { top: i * stride, height: g.height }, children: /* @__PURE__ */ jsxs7("span", { className: "june-doc__page-num", children: [
|
|
2411
|
+
i + 1,
|
|
2412
|
+
" / ",
|
|
2413
|
+
pages
|
|
2414
|
+
] }) }, i)) }) : null,
|
|
2415
|
+
/* @__PURE__ */ jsx9(EditorContent, { editor })
|
|
2416
|
+
] }) }) });
|
|
2003
2417
|
}
|
|
2004
2418
|
|
|
2005
2419
|
// src/components/document-editor/document-findbar.tsx
|
|
2006
2420
|
import { useEditorState as useEditorState3 } from "@tiptap/react";
|
|
2007
|
-
import { useEffect as
|
|
2008
|
-
import { Fragment as Fragment3, jsx as jsx10, jsxs as
|
|
2421
|
+
import { useEffect as useEffect5, useRef as useRef6, useState as useState5 } from "react";
|
|
2422
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2009
2423
|
function DocumentFindBar({ editor, editable, onClose, onNotice }) {
|
|
2010
2424
|
const [term, setTerm] = useState5(() => {
|
|
2011
2425
|
const { from, to, empty } = editor.state.selection;
|
|
@@ -2021,19 +2435,19 @@ function DocumentFindBar({ editor, editable, onClose, onNotice }) {
|
|
|
2021
2435
|
return { count: s?.matches.length ?? 0, current: s?.current ?? -1 };
|
|
2022
2436
|
}
|
|
2023
2437
|
});
|
|
2024
|
-
|
|
2438
|
+
useEffect5(() => {
|
|
2025
2439
|
input.current?.focus();
|
|
2026
2440
|
input.current?.select();
|
|
2027
2441
|
}, []);
|
|
2028
|
-
|
|
2442
|
+
useEffect5(() => {
|
|
2029
2443
|
setSearch(editor, { term, caseSensitive, current: 0 });
|
|
2030
2444
|
}, [editor, term, caseSensitive]);
|
|
2031
|
-
|
|
2445
|
+
useEffect5(() => () => {
|
|
2032
2446
|
if (!editor.isDestroyed) setSearch(editor, { term: "" });
|
|
2033
2447
|
}, [editor]);
|
|
2034
2448
|
const count = info?.count ?? 0;
|
|
2035
2449
|
const current = info?.current ?? -1;
|
|
2036
|
-
return /* @__PURE__ */
|
|
2450
|
+
return /* @__PURE__ */ jsxs8(
|
|
2037
2451
|
"div",
|
|
2038
2452
|
{
|
|
2039
2453
|
className: "june-doc__findbar",
|
|
@@ -2046,7 +2460,7 @@ function DocumentFindBar({ editor, editable, onClose, onNotice }) {
|
|
|
2046
2460
|
}
|
|
2047
2461
|
},
|
|
2048
2462
|
children: [
|
|
2049
|
-
/* @__PURE__ */
|
|
2463
|
+
/* @__PURE__ */ jsxs8("div", { className: "june-doc__find-field", children: [
|
|
2050
2464
|
/* @__PURE__ */ jsx10(Icon, { name: "search", size: 14 }),
|
|
2051
2465
|
/* @__PURE__ */ jsx10(
|
|
2052
2466
|
"input",
|
|
@@ -2082,7 +2496,7 @@ function DocumentFindBar({ editor, editable, onClose, onNotice }) {
|
|
|
2082
2496
|
children: "Aa"
|
|
2083
2497
|
}
|
|
2084
2498
|
),
|
|
2085
|
-
editable ? /* @__PURE__ */
|
|
2499
|
+
editable ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
2086
2500
|
/* @__PURE__ */ jsx10("span", { className: "june-doc__find-sep", "aria-hidden": "true" }),
|
|
2087
2501
|
/* @__PURE__ */ jsx10(
|
|
2088
2502
|
"input",
|
|
@@ -2138,7 +2552,7 @@ function DocumentTitle({ value, disabled, onChange }) {
|
|
|
2138
2552
|
}
|
|
2139
2553
|
|
|
2140
2554
|
// src/components/document-editor/document-header.tsx
|
|
2141
|
-
import { jsx as jsx12, jsxs as
|
|
2555
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2142
2556
|
var FORMATS = {
|
|
2143
2557
|
docx: { label: "Word document", hint: ".docx", icon: "file" },
|
|
2144
2558
|
pdf: { label: "PDF", hint: "Print / Save as PDF", icon: "file" },
|
|
@@ -2147,20 +2561,21 @@ var FORMATS = {
|
|
|
2147
2561
|
txt: { label: "Plain text", hint: ".txt", icon: "file" }
|
|
2148
2562
|
};
|
|
2149
2563
|
function DocumentHeader(props) {
|
|
2150
|
-
const { title, editable, isDraft, fullscreen, onToggleFullscreen, onTitle, exportFormats, canSave, saveState, onExport, onCopy, onSave } = props;
|
|
2151
|
-
return /* @__PURE__ */
|
|
2564
|
+
const { title, editable, isDraft, fullscreen, onToggleFullscreen, onTitle, exportFormats, canSave, showSave = canSave, saveState, onExport, onCopy, onSave } = props;
|
|
2565
|
+
return /* @__PURE__ */ jsxs9("div", { className: "june-doc__header", children: [
|
|
2152
2566
|
fullscreen ? /* @__PURE__ */ jsx12("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__ */ jsx12(Icon, { name: "close", size: 18 }) }) : /* @__PURE__ */ jsx12("span", { className: "june-doc__doc-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx12(Icon, { name: "file", size: 16 }) }),
|
|
2153
2567
|
/* @__PURE__ */ jsx12(DocumentTitle, { value: title, disabled: !editable, onChange: onTitle }),
|
|
2154
|
-
isDraft ? /* @__PURE__ */
|
|
2568
|
+
isDraft ? /* @__PURE__ */ jsxs9("span", { className: "june-doc__badge", children: [
|
|
2155
2569
|
/* @__PURE__ */ jsx12(Icon, { name: "sparkles", size: 12 }),
|
|
2156
2570
|
"AI draft"
|
|
2157
2571
|
] }) : null,
|
|
2158
|
-
/* @__PURE__ */
|
|
2159
|
-
|
|
2572
|
+
/* @__PURE__ */ jsxs9("div", { className: "june-doc__header-actions", children: [
|
|
2573
|
+
showSave ? /* @__PURE__ */ jsxs9(
|
|
2160
2574
|
"button",
|
|
2161
2575
|
{
|
|
2162
2576
|
type: "button",
|
|
2163
|
-
className: "june-doc__btn june-doc__btn--outline",
|
|
2577
|
+
className: "june-doc__btn june-doc__btn--outline june-doc__save",
|
|
2578
|
+
"data-state": saveState,
|
|
2164
2579
|
"aria-label": "Save",
|
|
2165
2580
|
title: "Save (Ctrl+S)",
|
|
2166
2581
|
disabled: saveState === "saving" || saveState === "saved" || saveState === "idle",
|
|
@@ -2176,7 +2591,7 @@ function DocumentHeader(props) {
|
|
|
2176
2591
|
{
|
|
2177
2592
|
align: "end",
|
|
2178
2593
|
className: "june-doc__menu",
|
|
2179
|
-
trigger: ({ open, toggle }) => /* @__PURE__ */
|
|
2594
|
+
trigger: ({ open, toggle }) => /* @__PURE__ */ jsxs9(
|
|
2180
2595
|
"button",
|
|
2181
2596
|
{
|
|
2182
2597
|
type: "button",
|
|
@@ -2191,10 +2606,10 @@ function DocumentHeader(props) {
|
|
|
2191
2606
|
]
|
|
2192
2607
|
}
|
|
2193
2608
|
),
|
|
2194
|
-
children: (close) => /* @__PURE__ */
|
|
2609
|
+
children: (close) => /* @__PURE__ */ jsxs9("div", { role: "menu", "aria-label": "Download options", children: [
|
|
2195
2610
|
exportFormats.map((f) => {
|
|
2196
2611
|
const meta = FORMATS[f] ?? { label: f.toUpperCase(), hint: "", icon: "file" };
|
|
2197
|
-
return /* @__PURE__ */
|
|
2612
|
+
return /* @__PURE__ */ jsxs9(
|
|
2198
2613
|
"button",
|
|
2199
2614
|
{
|
|
2200
2615
|
type: "button",
|
|
@@ -2214,7 +2629,7 @@ function DocumentHeader(props) {
|
|
|
2214
2629
|
);
|
|
2215
2630
|
}),
|
|
2216
2631
|
exportFormats.length ? /* @__PURE__ */ jsx12("div", { className: "june-doc__menu-sep", role: "separator" }) : null,
|
|
2217
|
-
/* @__PURE__ */
|
|
2632
|
+
/* @__PURE__ */ jsxs9(
|
|
2218
2633
|
"button",
|
|
2219
2634
|
{
|
|
2220
2635
|
type: "button",
|
|
@@ -2265,7 +2680,7 @@ var textResult = (text, filename, mimeType) => ({
|
|
|
2265
2680
|
blob: new Blob([text], { type: `${mimeType};charset=utf-8` })
|
|
2266
2681
|
});
|
|
2267
2682
|
var exportHtml = async (ctx) => {
|
|
2268
|
-
const { docToHtml: docToHtml2 } = await import("./document-engine-
|
|
2683
|
+
const { docToHtml: docToHtml2 } = await import("./document-engine-RKD5SLXR.js");
|
|
2269
2684
|
return textResult(
|
|
2270
2685
|
buildHtmlDocument(ctx.title, docToHtml2(ctx.json)),
|
|
2271
2686
|
`${ctx.basename}.html`,
|
|
@@ -2273,7 +2688,7 @@ var exportHtml = async (ctx) => {
|
|
|
2273
2688
|
);
|
|
2274
2689
|
};
|
|
2275
2690
|
var exportMarkdown = async (ctx) => {
|
|
2276
|
-
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-
|
|
2691
|
+
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-RKD5SLXR.js");
|
|
2277
2692
|
return textResult(
|
|
2278
2693
|
`# ${ctx.title}
|
|
2279
2694
|
|
|
@@ -2284,7 +2699,7 @@ ${docToMarkdown2(ctx.json)}
|
|
|
2284
2699
|
);
|
|
2285
2700
|
};
|
|
2286
2701
|
var exportTxt = async (ctx) => {
|
|
2287
|
-
const { docToText } = await import("./document-engine-
|
|
2702
|
+
const { docToText } = await import("./document-engine-RKD5SLXR.js");
|
|
2288
2703
|
return textResult(`${ctx.title}
|
|
2289
2704
|
|
|
2290
2705
|
${docToText(ctx.json)}
|
|
@@ -2606,7 +3021,7 @@ function printHtml(html) {
|
|
|
2606
3021
|
});
|
|
2607
3022
|
}
|
|
2608
3023
|
var exportPdf = async (ctx) => {
|
|
2609
|
-
const { docToHtml: docToHtml2 } = await import("./document-engine-
|
|
3024
|
+
const { docToHtml: docToHtml2 } = await import("./document-engine-RKD5SLXR.js");
|
|
2610
3025
|
await printHtml(buildHtmlDocument(ctx.title, docToHtml2(ctx.json)));
|
|
2611
3026
|
return { filename: `${ctx.basename}.pdf`, mimeType: "application/pdf", handled: true };
|
|
2612
3027
|
};
|
|
@@ -2644,7 +3059,7 @@ function downloadResult(result) {
|
|
|
2644
3059
|
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
2645
3060
|
}
|
|
2646
3061
|
async function copyDocumentToClipboard(doc) {
|
|
2647
|
-
const { docToHtml: docToHtml2, docToText } = await import("./document-engine-
|
|
3062
|
+
const { docToHtml: docToHtml2, docToText } = await import("./document-engine-RKD5SLXR.js");
|
|
2648
3063
|
const html = docToHtml2(doc.json);
|
|
2649
3064
|
const text = `${doc.title}
|
|
2650
3065
|
|
|
@@ -2666,221 +3081,6 @@ ${docToText(doc.json)}`;
|
|
|
2666
3081
|
}
|
|
2667
3082
|
}
|
|
2668
3083
|
|
|
2669
|
-
// src/components/document-editor/document-merge.ts
|
|
2670
|
-
var MAX_CELLS = 4e6;
|
|
2671
|
-
function matchIndices(a, b) {
|
|
2672
|
-
const n = a.length;
|
|
2673
|
-
const m = b.length;
|
|
2674
|
-
if (n * m > MAX_CELLS) return null;
|
|
2675
|
-
const dp = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));
|
|
2676
|
-
for (let i2 = n - 1; i2 >= 0; i2--) {
|
|
2677
|
-
for (let j2 = m - 1; j2 >= 0; j2--) {
|
|
2678
|
-
dp[i2][j2] = a[i2] === b[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
|
|
2679
|
-
}
|
|
2680
|
-
}
|
|
2681
|
-
const matches = /* @__PURE__ */ new Map();
|
|
2682
|
-
let i = 0;
|
|
2683
|
-
let j = 0;
|
|
2684
|
-
while (i < n && j < m) {
|
|
2685
|
-
if (a[i] === b[j]) {
|
|
2686
|
-
matches.set(i, j);
|
|
2687
|
-
i++;
|
|
2688
|
-
j++;
|
|
2689
|
-
} else if (dp[i + 1][j] >= dp[i][j + 1]) i++;
|
|
2690
|
-
else j++;
|
|
2691
|
-
}
|
|
2692
|
-
return matches;
|
|
2693
|
-
}
|
|
2694
|
-
var same = (x, y) => x.length === y.length && x.every((v, i) => v === y[i]);
|
|
2695
|
-
function mergeBlocks(base, local, incoming) {
|
|
2696
|
-
if (same(local, base)) return { blocks: incoming, conflicts: 0 };
|
|
2697
|
-
if (same(incoming, base) || same(local, incoming)) return { blocks: local, conflicts: 0 };
|
|
2698
|
-
const ml = matchIndices(base, local);
|
|
2699
|
-
const mn = matchIndices(base, incoming);
|
|
2700
|
-
if (!ml || !mn) return { blocks: null, conflicts: 1 };
|
|
2701
|
-
const anchors = [...ml.keys()].filter((i) => mn.has(i)).sort((x, y) => x - y);
|
|
2702
|
-
const out = [];
|
|
2703
|
-
let conflicts = 0;
|
|
2704
|
-
let pb = -1;
|
|
2705
|
-
let pl = -1;
|
|
2706
|
-
let pn = -1;
|
|
2707
|
-
const region = (bEnd, lEnd, nEnd) => {
|
|
2708
|
-
const B = base.slice(pb + 1, bEnd);
|
|
2709
|
-
const L = local.slice(pl + 1, lEnd);
|
|
2710
|
-
const N = incoming.slice(pn + 1, nEnd);
|
|
2711
|
-
if (B.length === L.length && L.length === N.length) {
|
|
2712
|
-
for (let k = 0; k < B.length; k++) {
|
|
2713
|
-
if (L[k] === B[k]) out.push(N[k]);
|
|
2714
|
-
else if (N[k] === B[k] || N[k] === L[k]) out.push(L[k]);
|
|
2715
|
-
else {
|
|
2716
|
-
conflicts++;
|
|
2717
|
-
out.push(L[k]);
|
|
2718
|
-
}
|
|
2719
|
-
}
|
|
2720
|
-
return;
|
|
2721
|
-
}
|
|
2722
|
-
if (same(L, B)) out.push(...N);
|
|
2723
|
-
else if (same(N, B) || same(L, N)) out.push(...L);
|
|
2724
|
-
else {
|
|
2725
|
-
conflicts++;
|
|
2726
|
-
out.push(...L);
|
|
2727
|
-
}
|
|
2728
|
-
};
|
|
2729
|
-
for (const a of anchors) {
|
|
2730
|
-
const la = ml.get(a);
|
|
2731
|
-
const na = mn.get(a);
|
|
2732
|
-
region(a, la, na);
|
|
2733
|
-
out.push(local[la]);
|
|
2734
|
-
pb = a;
|
|
2735
|
-
pl = la;
|
|
2736
|
-
pn = na;
|
|
2737
|
-
}
|
|
2738
|
-
region(base.length, local.length, incoming.length);
|
|
2739
|
-
return { blocks: out, conflicts };
|
|
2740
|
-
}
|
|
2741
|
-
function mergeDocuments(base, local, incoming) {
|
|
2742
|
-
const toBlocks = (d) => (d.content ?? []).map((node) => JSON.stringify(node));
|
|
2743
|
-
const { blocks: blocks2, conflicts } = mergeBlocks(toBlocks(base), toBlocks(local), toBlocks(incoming));
|
|
2744
|
-
if (!blocks2) return { doc: local, conflicts: Math.max(1, conflicts) };
|
|
2745
|
-
if (blocks2.length === 0) return { doc: emptyDoc(), conflicts };
|
|
2746
|
-
return { doc: { type: "doc", content: blocks2.map((b) => JSON.parse(b)) }, conflicts };
|
|
2747
|
-
}
|
|
2748
|
-
|
|
2749
|
-
// src/components/document-editor/document-store.ts
|
|
2750
|
-
var incomingKey = (doc) => JSON.stringify([doc.title, docKey(doc.json)]);
|
|
2751
|
-
function createDocumentStore() {
|
|
2752
|
-
const records = /* @__PURE__ */ new Map();
|
|
2753
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
2754
|
-
const emit = () => listeners.forEach((listener) => listener());
|
|
2755
|
-
const put = (record) => {
|
|
2756
|
-
records.set(record.id, record);
|
|
2757
|
-
emit();
|
|
2758
|
-
};
|
|
2759
|
-
const fresh2 = (doc) => ({
|
|
2760
|
-
id: doc.id,
|
|
2761
|
-
title: doc.title,
|
|
2762
|
-
documentType: doc.documentType,
|
|
2763
|
-
status: doc.status,
|
|
2764
|
-
json: doc.json,
|
|
2765
|
-
baseJson: doc.json,
|
|
2766
|
-
baseTitle: doc.title,
|
|
2767
|
-
lastIncomingKey: incomingKey(doc),
|
|
2768
|
-
revision: 0,
|
|
2769
|
-
origin: "external",
|
|
2770
|
-
pending: null,
|
|
2771
|
-
updatedAt: Date.now()
|
|
2772
|
-
});
|
|
2773
|
-
return {
|
|
2774
|
-
get: (id) => records.get(id),
|
|
2775
|
-
list: () => [...records.values()],
|
|
2776
|
-
subscribe: (listener) => {
|
|
2777
|
-
listeners.add(listener);
|
|
2778
|
-
return () => {
|
|
2779
|
-
listeners.delete(listener);
|
|
2780
|
-
};
|
|
2781
|
-
},
|
|
2782
|
-
receive(incoming) {
|
|
2783
|
-
const current = records.get(incoming.id);
|
|
2784
|
-
if (!current) {
|
|
2785
|
-
put(fresh2(incoming));
|
|
2786
|
-
return "created";
|
|
2787
|
-
}
|
|
2788
|
-
const key = incomingKey(incoming);
|
|
2789
|
-
if (current.lastIncomingKey === key) return "unchanged";
|
|
2790
|
-
const userEditedBody = docKey(current.json) !== docKey(current.baseJson);
|
|
2791
|
-
const userEditedTitle = current.title !== current.baseTitle;
|
|
2792
|
-
const title = userEditedTitle ? current.title : incoming.title;
|
|
2793
|
-
const meta = {
|
|
2794
|
-
documentType: incoming.documentType ?? current.documentType,
|
|
2795
|
-
status: incoming.status ?? current.status
|
|
2796
|
-
};
|
|
2797
|
-
if (!userEditedBody) {
|
|
2798
|
-
put({
|
|
2799
|
-
...current,
|
|
2800
|
-
...meta,
|
|
2801
|
-
title,
|
|
2802
|
-
baseTitle: incoming.title,
|
|
2803
|
-
json: incoming.json,
|
|
2804
|
-
baseJson: incoming.json,
|
|
2805
|
-
lastIncomingKey: key,
|
|
2806
|
-
revision: current.revision + 1,
|
|
2807
|
-
origin: "external",
|
|
2808
|
-
pending: null,
|
|
2809
|
-
updatedAt: Date.now()
|
|
2810
|
-
});
|
|
2811
|
-
return "applied";
|
|
2812
|
-
}
|
|
2813
|
-
const { doc, conflicts } = mergeDocuments(current.baseJson, current.json, incoming.json);
|
|
2814
|
-
if (conflicts === 0) {
|
|
2815
|
-
put({
|
|
2816
|
-
...current,
|
|
2817
|
-
...meta,
|
|
2818
|
-
title,
|
|
2819
|
-
baseTitle: incoming.title,
|
|
2820
|
-
json: doc,
|
|
2821
|
-
baseJson: incoming.json,
|
|
2822
|
-
lastIncomingKey: key,
|
|
2823
|
-
revision: current.revision + 1,
|
|
2824
|
-
origin: "external",
|
|
2825
|
-
pending: null,
|
|
2826
|
-
updatedAt: Date.now()
|
|
2827
|
-
});
|
|
2828
|
-
return "merged";
|
|
2829
|
-
}
|
|
2830
|
-
put({
|
|
2831
|
-
...current,
|
|
2832
|
-
...meta,
|
|
2833
|
-
lastIncomingKey: key,
|
|
2834
|
-
pending: { json: incoming.json, title: incoming.title, merged: doc, conflicts }
|
|
2835
|
-
});
|
|
2836
|
-
return "conflict";
|
|
2837
|
-
},
|
|
2838
|
-
hydrate(doc) {
|
|
2839
|
-
put(fresh2(doc));
|
|
2840
|
-
},
|
|
2841
|
-
edit(id, json, origin) {
|
|
2842
|
-
const current = records.get(id);
|
|
2843
|
-
if (!current || docKey(current.json) === docKey(json)) return;
|
|
2844
|
-
put({ ...current, json, origin, revision: current.revision + 1, updatedAt: Date.now() });
|
|
2845
|
-
},
|
|
2846
|
-
setTitle(id, title) {
|
|
2847
|
-
const current = records.get(id);
|
|
2848
|
-
if (!current || current.title === title) return;
|
|
2849
|
-
put({ ...current, title, updatedAt: Date.now() });
|
|
2850
|
-
},
|
|
2851
|
-
resolve(id, choice) {
|
|
2852
|
-
const current = records.get(id);
|
|
2853
|
-
const pending = current?.pending;
|
|
2854
|
-
if (!current || !pending) return;
|
|
2855
|
-
if (choice === "keep") {
|
|
2856
|
-
put({ ...current, baseJson: pending.json, baseTitle: pending.title, pending: null });
|
|
2857
|
-
return;
|
|
2858
|
-
}
|
|
2859
|
-
const json = choice === "merge" ? pending.merged : pending.json;
|
|
2860
|
-
const title = choice === "merge" ? current.title : pending.title;
|
|
2861
|
-
put({
|
|
2862
|
-
...current,
|
|
2863
|
-
title,
|
|
2864
|
-
json,
|
|
2865
|
-
baseJson: pending.json,
|
|
2866
|
-
baseTitle: pending.title,
|
|
2867
|
-
revision: current.revision + 1,
|
|
2868
|
-
origin: "external",
|
|
2869
|
-
pending: null,
|
|
2870
|
-
updatedAt: Date.now()
|
|
2871
|
-
});
|
|
2872
|
-
},
|
|
2873
|
-
remove(id) {
|
|
2874
|
-
if (records.delete(id)) emit();
|
|
2875
|
-
}
|
|
2876
|
-
};
|
|
2877
|
-
}
|
|
2878
|
-
var defaultStore = null;
|
|
2879
|
-
function getDefaultDocumentStore() {
|
|
2880
|
-
if (!defaultStore) defaultStore = createDocumentStore();
|
|
2881
|
-
return defaultStore;
|
|
2882
|
-
}
|
|
2883
|
-
|
|
2884
3084
|
// src/components/document-editor/document-types.ts
|
|
2885
3085
|
var ALL_EXPORT_FORMATS = [
|
|
2886
3086
|
"html",
|
|
@@ -2891,7 +3091,7 @@ var ALL_EXPORT_FORMATS = [
|
|
|
2891
3091
|
];
|
|
2892
3092
|
|
|
2893
3093
|
// src/components/document-editor/document-editor.tsx
|
|
2894
|
-
import { Fragment as Fragment4, jsx as jsx13, jsxs as
|
|
3094
|
+
import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2895
3095
|
var snapshotOf = (r) => ({
|
|
2896
3096
|
documentId: r.id,
|
|
2897
3097
|
title: r.title,
|
|
@@ -2937,7 +3137,7 @@ function DocumentEditorImpl(props) {
|
|
|
2937
3137
|
return { json: null, error: toJuneError(e, "DOCUMENT_ERROR") };
|
|
2938
3138
|
}
|
|
2939
3139
|
}, [contentKey]);
|
|
2940
|
-
|
|
3140
|
+
useEffect6(() => {
|
|
2941
3141
|
if (parsed.error) optionsRef.current.onError?.(parsed.error);
|
|
2942
3142
|
}, [parsed.error]);
|
|
2943
3143
|
const title = useDocumentValue(store, documentId, (r) => r?.title ?? titleProp);
|
|
@@ -2953,7 +3153,7 @@ function DocumentEditorImpl(props) {
|
|
|
2953
3153
|
const [findOpen, setFindOpen] = useState6(false);
|
|
2954
3154
|
const [zoom, setZoom] = useState6(1);
|
|
2955
3155
|
const [metrics, setMetrics] = useState6({ page: 1, pages: 1 });
|
|
2956
|
-
|
|
3156
|
+
useEffect6(() => {
|
|
2957
3157
|
if (!fullscreen || typeof document === "undefined") return;
|
|
2958
3158
|
const prev = document.body.style.overflow;
|
|
2959
3159
|
document.body.style.overflow = "hidden";
|
|
@@ -2967,7 +3167,7 @@ function DocumentEditorImpl(props) {
|
|
|
2967
3167
|
const flash = useCallback3((message) => {
|
|
2968
3168
|
setNotice(message);
|
|
2969
3169
|
}, []);
|
|
2970
|
-
|
|
3170
|
+
useEffect6(() => {
|
|
2971
3171
|
if (!notice) return;
|
|
2972
3172
|
const t = setTimeout(() => setNotice(null), 3e3);
|
|
2973
3173
|
return () => clearTimeout(t);
|
|
@@ -2985,12 +3185,17 @@ function DocumentEditorImpl(props) {
|
|
|
2985
3185
|
const delay = options.autosaveDelay ?? 1500;
|
|
2986
3186
|
return delay > 0 ? debounce(() => void saveRef.current(), delay) : null;
|
|
2987
3187
|
}, [options.autosaveDelay]);
|
|
2988
|
-
|
|
3188
|
+
useEffect6(() => () => autosave?.flush(), [autosave]);
|
|
2989
3189
|
saveRef.current = async () => {
|
|
2990
3190
|
const record = store.get(documentId);
|
|
2991
3191
|
const onSave = optionsRef.current.onSave;
|
|
2992
|
-
if (!record
|
|
3192
|
+
if (!record) return;
|
|
2993
3193
|
const key = JSON.stringify([record.title, docKey(record.json)]);
|
|
3194
|
+
if (!onSave) {
|
|
3195
|
+
lastSavedKey.current = key;
|
|
3196
|
+
setSaveState("saved");
|
|
3197
|
+
return;
|
|
3198
|
+
}
|
|
2994
3199
|
if (key === lastSavedKey.current) {
|
|
2995
3200
|
setSaveState("saved");
|
|
2996
3201
|
return;
|
|
@@ -3011,11 +3216,22 @@ function DocumentEditorImpl(props) {
|
|
|
3011
3216
|
optionsRef.current.onError?.(toJuneError(error, "DOCUMENT_ERROR", "Saving the document failed"));
|
|
3012
3217
|
}
|
|
3013
3218
|
};
|
|
3014
|
-
const markDirty = useCallback3(
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3219
|
+
const markDirty = useCallback3(
|
|
3220
|
+
(userEdit = true) => {
|
|
3221
|
+
if (!optionsRef.current.onSave) {
|
|
3222
|
+
if (userEdit) setSaveState("dirty");
|
|
3223
|
+
return;
|
|
3224
|
+
}
|
|
3225
|
+
setSaveState("dirty");
|
|
3226
|
+
autosave?.();
|
|
3227
|
+
},
|
|
3228
|
+
[autosave]
|
|
3229
|
+
);
|
|
3230
|
+
useEffect6(() => {
|
|
3231
|
+
if (saveState !== "saved" || options.onSave) return;
|
|
3232
|
+
const t = setTimeout(() => setSaveState((s) => s === "saved" ? "idle" : s), 2500);
|
|
3233
|
+
return () => clearTimeout(t);
|
|
3234
|
+
}, [saveState, options.onSave]);
|
|
3019
3235
|
const [initialJson] = useState6(() => store.get(documentId)?.json ?? parsed.json ?? emptyDoc());
|
|
3020
3236
|
const extensions = useMemo4(() => createDocumentExtensions(), []);
|
|
3021
3237
|
const onUpdateRef = useRef7(() => {
|
|
@@ -3040,11 +3256,13 @@ function DocumentEditorImpl(props) {
|
|
|
3040
3256
|
},
|
|
3041
3257
|
onUpdate: ({ editor: ed }) => onUpdateRef.current(ed.getJSON())
|
|
3042
3258
|
});
|
|
3043
|
-
|
|
3044
|
-
editor
|
|
3259
|
+
useEffect6(() => {
|
|
3260
|
+
if (!editor || editor.isEditable === editable) return;
|
|
3261
|
+
editor.setEditable(editable);
|
|
3045
3262
|
}, [editor, editable]);
|
|
3046
|
-
|
|
3263
|
+
useEffect6(() => {
|
|
3047
3264
|
if (!parsed.json) return;
|
|
3265
|
+
if (isDocumentEcho(store, documentId, contentKey)) return;
|
|
3048
3266
|
const outcome = store.receive({
|
|
3049
3267
|
id: documentId,
|
|
3050
3268
|
title: titleProp,
|
|
@@ -3054,7 +3272,7 @@ function DocumentEditorImpl(props) {
|
|
|
3054
3272
|
});
|
|
3055
3273
|
if (outcome === "applied" || outcome === "merged") {
|
|
3056
3274
|
emitChange(outcome === "applied" ? "ai" : "merge");
|
|
3057
|
-
markDirty();
|
|
3275
|
+
markDirty(false);
|
|
3058
3276
|
} else if (outcome === "conflict") {
|
|
3059
3277
|
const record = store.get(documentId);
|
|
3060
3278
|
optionsRef.current.onConflict?.({
|
|
@@ -3064,7 +3282,7 @@ function DocumentEditorImpl(props) {
|
|
|
3064
3282
|
});
|
|
3065
3283
|
}
|
|
3066
3284
|
}, [store, documentId, titleProp, parsed.json, documentType, status, emitChange, markDirty]);
|
|
3067
|
-
|
|
3285
|
+
useEffect6(() => {
|
|
3068
3286
|
if (!editor) return;
|
|
3069
3287
|
const apply = () => {
|
|
3070
3288
|
const record = store.get(documentId);
|
|
@@ -3081,7 +3299,7 @@ function DocumentEditorImpl(props) {
|
|
|
3081
3299
|
apply();
|
|
3082
3300
|
return store.subscribe(apply);
|
|
3083
3301
|
}, [editor, store, documentId, instanceId]);
|
|
3084
|
-
|
|
3302
|
+
useEffect6(() => {
|
|
3085
3303
|
if (!editor || readyRef.current) return;
|
|
3086
3304
|
const record = store.get(documentId);
|
|
3087
3305
|
if (!record) return;
|
|
@@ -3125,9 +3343,9 @@ function DocumentEditorImpl(props) {
|
|
|
3125
3343
|
};
|
|
3126
3344
|
const formats = (exportFormats ?? [...ALL_EXPORT_FORMATS]).filter((f) => listDocumentExporters().includes(f));
|
|
3127
3345
|
if (parsed.error && !store.get(documentId)) {
|
|
3128
|
-
return /* @__PURE__ */
|
|
3346
|
+
return /* @__PURE__ */ jsxs10("div", { className: "june-doc june-doc--error", "data-theme": resolvedTheme, role: "alert", children: [
|
|
3129
3347
|
/* @__PURE__ */ jsx13("p", { className: "june-doc__draft-text", children: "This document could not be displayed." }),
|
|
3130
|
-
/* @__PURE__ */
|
|
3348
|
+
/* @__PURE__ */ jsxs10("details", { children: [
|
|
3131
3349
|
/* @__PURE__ */ jsx13("summary", { children: "Show raw content" }),
|
|
3132
3350
|
/* @__PURE__ */ jsx13("pre", { className: "june-doc__raw", children: contentKey })
|
|
3133
3351
|
] })
|
|
@@ -3136,7 +3354,7 @@ function DocumentEditorImpl(props) {
|
|
|
3136
3354
|
if (!editor) return /* @__PURE__ */ jsx13(DocumentDraft, { title, state: "loading" });
|
|
3137
3355
|
const isDraft = recordStatus === "draft";
|
|
3138
3356
|
const classes = ["june-doc", fullscreen ? "june-doc--fullscreen" : "", className ?? ""].filter(Boolean).join(" ");
|
|
3139
|
-
return /* @__PURE__ */
|
|
3357
|
+
return /* @__PURE__ */ jsxs10(
|
|
3140
3358
|
"div",
|
|
3141
3359
|
{
|
|
3142
3360
|
className: classes,
|
|
@@ -3169,21 +3387,22 @@ function DocumentEditorImpl(props) {
|
|
|
3169
3387
|
onTitle: handleTitle,
|
|
3170
3388
|
exportFormats: formats,
|
|
3171
3389
|
canSave: !!options.onSave,
|
|
3390
|
+
showSave: !!options.onSave || editable && saveState !== "idle",
|
|
3172
3391
|
saveState,
|
|
3173
3392
|
onExport: (f) => void handleExport(f),
|
|
3174
3393
|
onCopy: () => void handleCopy(),
|
|
3175
3394
|
onSave: () => void saveRef.current()
|
|
3176
3395
|
}
|
|
3177
3396
|
),
|
|
3178
|
-
pending ? /* @__PURE__ */
|
|
3179
|
-
/* @__PURE__ */
|
|
3397
|
+
pending ? /* @__PURE__ */ jsxs10("div", { className: "june-doc__banner", role: "status", children: [
|
|
3398
|
+
/* @__PURE__ */ jsxs10("span", { children: [
|
|
3180
3399
|
"The assistant suggested changes that overlap your edits (",
|
|
3181
3400
|
pending.conflicts,
|
|
3182
3401
|
" ",
|
|
3183
3402
|
pending.conflicts === 1 ? "section" : "sections",
|
|
3184
3403
|
")."
|
|
3185
3404
|
] }),
|
|
3186
|
-
/* @__PURE__ */
|
|
3405
|
+
/* @__PURE__ */ jsxs10("span", { className: "june-doc__banner-actions", children: [
|
|
3187
3406
|
/* @__PURE__ */ jsx13("button", { type: "button", className: "june-doc__btn", onClick: () => resolve("merge"), children: "Merge, keep my edits" }),
|
|
3188
3407
|
/* @__PURE__ */ jsx13("button", { type: "button", className: "june-doc__btn", onClick: () => resolve("replace"), children: "Use AI version" }),
|
|
3189
3408
|
/* @__PURE__ */ jsx13("button", { type: "button", className: "june-doc__btn", onClick: () => resolve("keep"), children: "Keep mine" })
|
|
@@ -3208,10 +3427,10 @@ function DocumentEditorImpl(props) {
|
|
|
3208
3427
|
findOpen ? /* @__PURE__ */ jsx13(DocumentFindBar, { editor, editable, onClose: () => setFindOpen(false), onNotice: flash }) : null,
|
|
3209
3428
|
editable ? /* @__PURE__ */ jsx13(DocumentBubbleMenu, { editor }) : null,
|
|
3210
3429
|
/* @__PURE__ */ jsx13(DocumentContent, { editor, layout, zoom, showMarks, onMetrics: setMetrics }),
|
|
3211
|
-
/* @__PURE__ */
|
|
3212
|
-
/* @__PURE__ */
|
|
3213
|
-
layout === "page" ? /* @__PURE__ */
|
|
3214
|
-
/* @__PURE__ */
|
|
3430
|
+
/* @__PURE__ */ jsxs10("div", { className: "june-doc__footer", children: [
|
|
3431
|
+
/* @__PURE__ */ jsxs10("div", { className: "june-doc__stats", children: [
|
|
3432
|
+
layout === "page" ? /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
3433
|
+
/* @__PURE__ */ jsxs10("span", { children: [
|
|
3215
3434
|
"Page ",
|
|
3216
3435
|
metrics.page,
|
|
3217
3436
|
" of ",
|
|
@@ -3219,15 +3438,15 @@ function DocumentEditorImpl(props) {
|
|
|
3219
3438
|
] }),
|
|
3220
3439
|
/* @__PURE__ */ jsx13("span", { className: "june-doc__stats-sep", "aria-hidden": "true" })
|
|
3221
3440
|
] }) : null,
|
|
3222
|
-
/* @__PURE__ */
|
|
3441
|
+
/* @__PURE__ */ jsxs10("span", { children: [
|
|
3223
3442
|
words,
|
|
3224
3443
|
" words"
|
|
3225
3444
|
] })
|
|
3226
3445
|
] }),
|
|
3227
3446
|
/* @__PURE__ */ jsx13("span", { className: "june-doc__status", "aria-live": "polite", "data-state": notice ? "notice" : saveState, children: notice ?? SAVE_LABEL[saveState] }),
|
|
3228
|
-
/* @__PURE__ */
|
|
3447
|
+
/* @__PURE__ */ jsxs10("div", { className: "june-doc__zoom", role: "group", "aria-label": "Zoom", children: [
|
|
3229
3448
|
/* @__PURE__ */ jsx13("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__ */ jsx13(Icon, { name: "minus", size: 14 }) }),
|
|
3230
|
-
/* @__PURE__ */
|
|
3449
|
+
/* @__PURE__ */ jsxs10("button", { type: "button", className: "june-doc__zoom-value", "aria-label": "Reset zoom", title: "Reset zoom", onClick: () => setZoom(1), children: [
|
|
3231
3450
|
Math.round(zoom * 100),
|
|
3232
3451
|
"%"
|
|
3233
3452
|
] }),
|
|
@@ -3334,7 +3553,7 @@ function documentPromptOptions(options = {}) {
|
|
|
3334
3553
|
var attr = (value) => value.replace(/"/g, """);
|
|
3335
3554
|
async function serializeDocumentsForAgent(options = {}) {
|
|
3336
3555
|
const store = options.store ?? getDefaultDocumentStore();
|
|
3337
|
-
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-
|
|
3556
|
+
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-RKD5SLXR.js");
|
|
3338
3557
|
return store.list().filter((r) => !options.ids || options.ids.includes(r.id)).map(
|
|
3339
3558
|
(r) => `<document id="${attr(r.id)}" title="${attr(r.title)}"${r.documentType ? ` type="${attr(r.documentType)}"` : ""}>
|
|
3340
3559
|
${docToMarkdown2(r.json)}
|
|
@@ -3378,17 +3597,21 @@ export {
|
|
|
3378
3597
|
getDefaultDocumentStore,
|
|
3379
3598
|
getDocumentEditorInstructions,
|
|
3380
3599
|
getDocumentEditorRules,
|
|
3600
|
+
isDocumentEcho,
|
|
3381
3601
|
isJuneError,
|
|
3382
3602
|
jsonFieldExtractor,
|
|
3383
3603
|
juneLibrary,
|
|
3384
3604
|
lightTheme,
|
|
3385
3605
|
listDocumentExporters,
|
|
3386
3606
|
mergeDocuments,
|
|
3607
|
+
readDocumentFromProgram,
|
|
3387
3608
|
registerDocumentExporter,
|
|
3609
|
+
rememberDocumentEcho,
|
|
3388
3610
|
runAction,
|
|
3389
3611
|
serializeDocumentsForAgent,
|
|
3390
3612
|
sseToText,
|
|
3391
3613
|
toJuneError,
|
|
3614
|
+
updateDocumentInProgram,
|
|
3392
3615
|
useDocumentOptions,
|
|
3393
3616
|
useJuneRuntime,
|
|
3394
3617
|
useJuneStream,
|