@heycys/gallery 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THIRD-PARTY-NOTICES.md +1 -1
- package/assets/preview/runtime.js +156 -0
- package/assets/preview/shell.html +107 -0
- package/assets/writing-preferences.md +1 -1
- package/browser/editor/components/divider/index.mjs +1 -1
- package/browser/editor/page/annotate/card.mjs +6 -0
- package/browser/editor/page/annotate/index.mjs +22 -2
- package/browser/editor/page/columns/index.mjs +1 -1
- package/browser/editor/page/conflicts.mjs +1 -1
- package/browser/editor/page/drag/index.mjs +1 -1
- package/browser/editor/page/editor.mjs +39 -3
- package/browser/editor/page/focus.mjs +104 -0
- package/browser/editor/page/handle/index.mjs +14 -2
- package/browser/editor/page/links/index.mjs +2 -2
- package/browser/editor/page/quiet.mjs +3 -3
- package/browser/editor/page/resize/index.mjs +1 -1
- package/browser/editor/page/session.mjs +4 -1
- package/browser/editor/page/ui.mjs +1 -1
- package/browser/shared/annotations.mjs +22 -3
- package/browser/shared/block-packs.mjs +13 -4
- package/browser/shared/document-look.mjs +14 -0
- package/browser/shared/drafts.mjs +1 -1
- package/browser/shared/extension-manifest.mjs +40 -12
- package/browser/shared/favorite-merge.mjs +17 -0
- package/browser/shared/favorites.mjs +77 -0
- package/browser/shared/i18n/en.mjs +123 -24
- package/browser/shared/outline-ops.mjs +109 -0
- package/browser/shared/outline.mjs +6 -0
- package/browser/shared/pages.mjs +20 -8
- package/browser/shared/paths.mjs +5 -2
- package/browser/shared/preference-documents.mjs +137 -0
- package/browser/shared/presence.mjs +94 -0
- package/browser/shared/preview-server.mjs +8 -5
- package/browser/shared/qa-levels.mjs +9 -1
- package/browser/shared/session.mjs +4 -0
- package/browser/shared/shipped-blocks.mjs +22 -4
- package/browser/shared/startup.mjs +101 -0
- package/browser/shared/workspace-columns.mjs +1 -1
- package/browser/shared/workspace-look.mjs +21 -8
- package/browser/shared/workspace-registry.mjs +6 -3
- package/browser/shared/workspace.mjs +37 -10
- package/browser/shared/writing-preferences.mjs +2 -42
- package/dist/chunk-25LYSMAE.mjs +2 -0
- package/dist/chunk-2I6ZVC5O.mjs +2 -0
- package/dist/{chunk-M4NRDYI5.mjs → chunk-2MYTOOP6.mjs} +1 -1
- package/dist/chunk-2VNENLER.mjs +2 -0
- package/dist/chunk-3FERXZF5.mjs +4 -0
- package/dist/chunk-3M6SBBGU.mjs +2 -0
- package/dist/chunk-3VCAAF34.mjs +2 -0
- package/dist/{chunk-WD4U6XXT.mjs → chunk-3ZTRLGSA.mjs} +1 -1
- package/dist/chunk-4FRA5AUE.mjs +2 -0
- package/dist/chunk-4LR2QEC2.mjs +4 -0
- package/dist/chunk-56SVJENP.mjs +966 -0
- package/dist/chunk-5CQISL2G.mjs +4 -0
- package/dist/{chunk-RRKJMHNX.mjs → chunk-5I5ZDDWC.mjs} +5 -5
- package/dist/chunk-5IHDLEB4.mjs +10 -0
- package/dist/chunk-5LRDMV7O.mjs +2 -0
- package/dist/chunk-5MVUYRKG.mjs +5 -0
- package/dist/chunk-5TLLIZT7.mjs +2 -0
- package/dist/chunk-6ILJWFYW.mjs +4 -0
- package/dist/chunk-6N25CJ6Y.mjs +3 -0
- package/dist/{chunk-SQ6Y2ZMA.mjs → chunk-7DTINW6U.mjs} +2 -2
- package/dist/chunk-7KWAGKIK.mjs +2 -0
- package/dist/chunk-7WNUOAFK.mjs +2 -0
- package/dist/chunk-7XCRW5RC.mjs +2 -0
- package/dist/{chunk-BYNPEUDS.mjs → chunk-7XSDTILF.mjs} +1 -1
- package/dist/chunk-ABKYFOVR.mjs +2 -0
- package/dist/chunk-AMUF7PUN.mjs +2 -0
- package/dist/{chunk-FJLM7YE7.mjs → chunk-B2EIMERT.mjs} +2 -2
- package/dist/{chunk-QNECWMGR.mjs → chunk-B45IAAU6.mjs} +1 -1
- package/dist/chunk-B6RAGF4R.mjs +2 -0
- package/dist/chunk-BSJKFE7I.mjs +4 -0
- package/dist/chunk-C72EEVLK.mjs +2 -0
- package/dist/chunk-CRAUKZ23.mjs +4 -0
- package/dist/chunk-F4CT35KL.mjs +2 -0
- package/dist/chunk-F63BNJLG.mjs +2 -0
- package/dist/chunk-FBKUPS44.mjs +2 -0
- package/dist/chunk-FL52ZL5Z.mjs +15 -0
- package/dist/chunk-FOZCU4VW.mjs +2 -0
- package/dist/chunk-G5HV7BC3.mjs +2 -0
- package/dist/{chunk-ERLAIBOA.mjs → chunk-GB5SOYQJ.mjs} +2 -2
- package/dist/chunk-GN3SG3IF.mjs +2 -0
- package/dist/{chunk-6QWQXHAV.mjs → chunk-HFTBVEZK.mjs} +1 -1
- package/dist/chunk-IOW5HLRK.mjs +2 -0
- package/dist/chunk-IQ3GJNTI.mjs +6 -0
- package/dist/chunk-K6SPFBUJ.mjs +2 -0
- package/dist/chunk-K7Y7SPTS.mjs +2 -0
- package/dist/chunk-L2IJCEUH.mjs +2 -0
- package/dist/{chunk-GPIWQ4X4.mjs → chunk-L3VT6LM4.mjs} +1 -1
- package/dist/chunk-L4WLQYFC.mjs +5 -0
- package/dist/{chunk-NLDF35P5.mjs → chunk-LAMCAGK6.mjs} +1 -1
- package/dist/chunk-LHGKPMTI.mjs +2 -0
- package/dist/chunk-LJZIYVNA.mjs +3 -0
- package/dist/chunk-LSVCTLS2.mjs +2 -0
- package/dist/chunk-LZY74XTP.mjs +2 -0
- package/dist/chunk-MC4LRSVH.mjs +2 -0
- package/dist/chunk-MCEXN4G5.mjs +2 -0
- package/dist/chunk-MTLAOCFF.mjs +181 -0
- package/dist/chunk-MZ66LN3B.mjs +2 -0
- package/dist/chunk-N5QWJRRI.mjs +2 -0
- package/dist/chunk-NF4MMOUB.mjs +2 -0
- package/dist/chunk-NHJYAJ73.mjs +3 -0
- package/dist/chunk-NRSD7SVY.mjs +2 -0
- package/dist/chunk-NT23I4XW.mjs +2 -0
- package/dist/chunk-NUALABIS.mjs +3 -0
- package/dist/chunk-NW6LAU22.mjs +2 -0
- package/dist/chunk-NWWHDGKF.mjs +5 -0
- package/dist/chunk-OFRCVMSJ.mjs +2 -0
- package/dist/chunk-OKVQQ3CX.mjs +4 -0
- package/dist/chunk-OM24O4KR.mjs +8 -0
- package/dist/{chunk-4B2TB5K2.mjs → chunk-PBMOC7KA.mjs} +2 -2
- package/dist/chunk-PVTCP43W.mjs +2 -0
- package/dist/chunk-Q62HZAJS.mjs +2 -0
- package/dist/chunk-QBJWIR3Q.mjs +2 -0
- package/dist/chunk-QDSEZX7Z.mjs +6 -0
- package/dist/chunk-QR4V4A2Z.mjs +15 -0
- package/dist/{chunk-QVGOPHOF.mjs → chunk-QRLVVZ3R.mjs} +1 -1
- package/dist/chunk-QW3QG67J.mjs +2 -0
- package/dist/chunk-R2T2EVWY.mjs +8 -0
- package/dist/{chunk-JJNHKGTD.mjs → chunk-R5RERE6G.mjs} +1 -1
- package/dist/chunk-RFHEWT3Y.mjs +5 -0
- package/dist/chunk-RS2Z2JQP.mjs +2 -0
- package/dist/chunk-S6YEC4GE.mjs +16 -0
- package/dist/chunk-SHZLMGGW.mjs +2 -0
- package/dist/chunk-TALJQM2O.mjs +2 -0
- package/dist/chunk-TLNJWEFY.mjs +22 -0
- package/dist/{chunk-ZLS2FRS6.mjs → chunk-TQZU7D7C.mjs} +1 -1
- package/dist/chunk-UTKNWYBA.mjs +2 -0
- package/dist/chunk-V3TM3TEN.mjs +2 -0
- package/dist/chunk-V3XROSP5.mjs +7 -0
- package/dist/{chunk-6QBHU4DF.mjs → chunk-V5JTW2LH.mjs} +1 -1
- package/dist/chunk-VAXLSISW.mjs +4 -0
- package/dist/chunk-VBHI3RG6.mjs +2 -0
- package/dist/{chunk-CRDG6EF2.mjs → chunk-VKZJRGDR.mjs} +1 -1
- package/dist/chunk-VZXZM7NS.mjs +2 -0
- package/dist/chunk-WDS6HEPE.mjs +2 -0
- package/dist/chunk-WHGANNEY.mjs +13 -0
- package/dist/chunk-WMJWC4QY.mjs +56 -0
- package/dist/{chunk-UTMAY3WK.mjs → chunk-WWONJJQS.mjs} +1 -1
- package/dist/chunk-X4WNLFIK.mjs +2 -0
- package/dist/chunk-XNNCQROX.mjs +11 -0
- package/dist/chunk-XRYECF23.mjs +170 -0
- package/dist/{chunk-225ALKYI.mjs → chunk-Z4NEXXFK.mjs} +1 -1
- package/dist/{chunk-PKVK6WFC.mjs → chunk-ZFVF2YPV.mjs} +2 -2
- package/dist/chunk-ZM2IX3R2.mjs +2 -0
- package/dist/chunk-ZNGGDUZI.mjs +2 -0
- package/dist/chunk-ZOEJQOKT.mjs +2 -0
- package/dist/cli.mjs +1 -1
- package/dist/server.mjs +1 -1
- package/dist/update-fetch.mjs +1 -1
- package/dist/worker.mjs +1 -1
- package/package.json +1 -1
- package/release.json +3 -3
- package/skill/SKILL.md +54 -148
- package/skill/references/building-blocks.md +4 -4
- package/skill/references/charts-and-figures.md +73 -0
- package/skill/references/extensions.md +47 -4
- package/skill/references/pages-and-data.md +22 -3
- package/skill/references/preference-files.md +7 -0
- package/skill/references/previews.md +17 -0
- package/skill/references/writing-html.md +21 -82
- package/web/assets/index-B4uUluLM.js +15 -0
- package/web/assets/index-C3vHT8sH.css +1 -0
- package/web/index.html +2 -2
- package/dist/chunk-2HS7ZBPU.mjs +0 -2
- package/dist/chunk-3F57LEN5.mjs +0 -2
- package/dist/chunk-4OZWXZE5.mjs +0 -5
- package/dist/chunk-5GK7HS62.mjs +0 -2
- package/dist/chunk-6GV3CBB2.mjs +0 -2
- package/dist/chunk-6NLXUKAY.mjs +0 -4
- package/dist/chunk-6XD6U3QA.mjs +0 -5
- package/dist/chunk-6Y3HYOBM.mjs +0 -2
- package/dist/chunk-7EFHNQ5B.mjs +0 -3
- package/dist/chunk-7M2BQQGZ.mjs +0 -2
- package/dist/chunk-7UZUQQ7Q.mjs +0 -554
- package/dist/chunk-ALZ6ZENZ.mjs +0 -3
- package/dist/chunk-BX5AKINW.mjs +0 -15
- package/dist/chunk-DR73Y2UJ.mjs +0 -3
- package/dist/chunk-EURLTOTZ.mjs +0 -4
- package/dist/chunk-F4ZA6JRZ.mjs +0 -3
- package/dist/chunk-FNCCVGKX.mjs +0 -2
- package/dist/chunk-FOJ3K5Z3.mjs +0 -6
- package/dist/chunk-FQ72LZHB.mjs +0 -2
- package/dist/chunk-G7N7YFVD.mjs +0 -2
- package/dist/chunk-GIRXJBGC.mjs +0 -2
- package/dist/chunk-GKOBB6DH.mjs +0 -2
- package/dist/chunk-GMJD7ID3.mjs +0 -2
- package/dist/chunk-GQJMVSSN.mjs +0 -2
- package/dist/chunk-HX4DLE3J.mjs +0 -10
- package/dist/chunk-IS7N3N6Q.mjs +0 -168
- package/dist/chunk-JEUK3ZO7.mjs +0 -2
- package/dist/chunk-JPG2LBQM.mjs +0 -11
- package/dist/chunk-JYK5WJPM.mjs +0 -2
- package/dist/chunk-KLQP46Y3.mjs +0 -4
- package/dist/chunk-L55NOXBE.mjs +0 -4
- package/dist/chunk-LCB6MARS.mjs +0 -5
- package/dist/chunk-LJLVCU6W.mjs +0 -16
- package/dist/chunk-LMUZQK5O.mjs +0 -2
- package/dist/chunk-NMF3IPYA.mjs +0 -13
- package/dist/chunk-ONKC4BCC.mjs +0 -2
- package/dist/chunk-OU6ZZVBX.mjs +0 -5
- package/dist/chunk-PUOQ65NC.mjs +0 -2
- package/dist/chunk-QJAVCTY7.mjs +0 -2
- package/dist/chunk-QP7BOHE4.mjs +0 -2
- package/dist/chunk-S3SX3COT.mjs +0 -20
- package/dist/chunk-SQ3FQCCN.mjs +0 -8
- package/dist/chunk-T4Y2ZSVX.mjs +0 -4
- package/dist/chunk-TANZRINB.mjs +0 -2
- package/dist/chunk-UGWMMEJ5.mjs +0 -15
- package/dist/chunk-UTF7GBQU.mjs +0 -4
- package/dist/chunk-V4ZHRALW.mjs +0 -170
- package/dist/chunk-XBNFXWAW.mjs +0 -6
- package/dist/chunk-XZV72SQ4.mjs +0 -2
- package/dist/chunk-YFJB534W.mjs +0 -2
- package/dist/chunk-Z6OKLRHC.mjs +0 -2
- package/dist/chunk-ZMHUDYLV.mjs +0 -2
- package/web/assets/index-D8P8uhDW.js +0 -15
- package/web/assets/index-FLb9jCfl.css +0 -1
- /package/dist/{chunk-PAREWQGB.mjs → chunk-NOGQX56R.mjs} +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// What the browser tells the server about what the reader has open (gallery status, notes/plans/agent-parity-2026-11-11.md
|
|
2
|
+
// part one, 4): each workbench tab (a window of the workbench, one document or custom page open in it) reports itself to
|
|
3
|
+
// its server now and then (POST /__gallery/workspace/presence: src/workbench/app/session/presence.ts), the server keeps
|
|
4
|
+
// the reports in memory only (src/workbench/presence.mjs), and gallery status asks for them. Pure: the shape of a report, what
|
|
5
|
+
// it is cut to and how a report is read as the command prints it, shared by the browser, the server and the command.
|
|
6
|
+
//
|
|
7
|
+
// A report: { tab, entry, kind, title, visible, focused, touched, flags, notes, caret, selection } or { tab, gone: true }.
|
|
8
|
+
// tab the workbench tab's own name (made when it loads, kept until it closes)
|
|
9
|
+
// entry what is open, as the workbench names it: a document's slug ("weekly"), a draft of another workspace
|
|
10
|
+
// ("~ws-abc123/weekly"), a custom page ("@home", or "@~ws-abc123/home" of another workspace)
|
|
11
|
+
// kind "document" or "page"
|
|
12
|
+
// title the title the reader sees (the page's, so one just typed counts)
|
|
13
|
+
// visible the tab is the one shown in its window; focused the window has the focus
|
|
14
|
+
// touched the reader did something in it (a heartbeat is not): what "the last one used" goes by
|
|
15
|
+
// flags the page's save state (shared/session.mjs STATE_FLAGS: editing, pending, saving, offline, conflicts, unfit, changed)
|
|
16
|
+
// notes how many 批注 the page has
|
|
17
|
+
// caret { block (a data-block-id or null: a block the file has no id for yet), text (its words) } or null
|
|
18
|
+
// selection { blocks: [data-block-id…], text (the selected words, or the blocks' words), kind: "text" | "blocks" } or null
|
|
19
|
+
// What a word of it is cut to is below: the words the reader selected are the most private thing a report holds, so
|
|
20
|
+
// they are cut short and never leave the computer (the server answers only to this computer, and only with the token).
|
|
21
|
+
|
|
22
|
+
/** How long a tab's report counts after its last one (ms): the tab sends one every 20 s while it is open, one more as it closes. */
|
|
23
|
+
export const ALIVE_FOR = 90_000;
|
|
24
|
+
/** The most of a selection (and of a block's words) a report carries, in characters. */
|
|
25
|
+
export const WORDS_MOST = 200;
|
|
26
|
+
/** The most blocks a selection names. */
|
|
27
|
+
export const BLOCKS_MOST = 20;
|
|
28
|
+
|
|
29
|
+
const TAB = /^[a-z0-9]{4,40}$/;
|
|
30
|
+
const ENTRY = /^@?(?:~ws-[0-9a-f]{6}\/)?[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
31
|
+
const BLOCK = /^[a-z0-9]{8}$/;
|
|
32
|
+
// Not KINDS: the dev server's Vite config bundles the server's modules into one scope, and a second top-level KINDS
|
|
33
|
+
// renames qa-levels.mjs's, whose functions' text a page runs (layoutNotesScript): the page then names one it never had.
|
|
34
|
+
const PRESENCE_KINDS = new Set(["document", "page"]);
|
|
35
|
+
|
|
36
|
+
const bool = (value) => value === true;
|
|
37
|
+
const count = (value) => (Number.isInteger(value) && value > 0 ? Math.min(value, 9999) : 0);
|
|
38
|
+
|
|
39
|
+
/** `text` as a line of words: runs of white space as one space, the ends trimmed, cut to `most` ({ text, more: whether it was cut }). */
|
|
40
|
+
export function cutWords(text, most = WORDS_MOST) {
|
|
41
|
+
const words = typeof text === "string" ? text.replace(/[]/g, "").replace(/\s+/g, " ").trim() : "";
|
|
42
|
+
const chars = [...words];
|
|
43
|
+
return chars.length > most ? { text: chars.slice(0, most).join(""), more: true } : { text: words, more: false };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const blockOf = (value) => (typeof value === "string" && BLOCK.test(value) ? value : null);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A report as the server keeps it, or null when it is none: `{ tab, gone: true }` for a tab that closed; else the fields
|
|
50
|
+
* above, each cut to what it may be (a block's id an id, a selection's words 200 characters, 20 blocks at most).
|
|
51
|
+
*/
|
|
52
|
+
export function cleanReport(body) {
|
|
53
|
+
if (!body || typeof body !== "object" || Array.isArray(body) || typeof body.tab !== "string" || !TAB.test(body.tab)) return null;
|
|
54
|
+
if (body.gone === true) return { tab: body.tab, gone: true };
|
|
55
|
+
if (typeof body.entry !== "string" || body.entry.length > 160 || !ENTRY.test(body.entry) || !PRESENCE_KINDS.has(body.kind)) return null;
|
|
56
|
+
if ((body.kind === "page") !== body.entry.startsWith("@")) return null;
|
|
57
|
+
const flags = body.flags && typeof body.flags === "object" ? body.flags : {};
|
|
58
|
+
let caret = null;
|
|
59
|
+
if (body.caret && typeof body.caret === "object") {
|
|
60
|
+
const words = cutWords(body.caret.text);
|
|
61
|
+
caret = { block: blockOf(body.caret.block), text: words.text };
|
|
62
|
+
}
|
|
63
|
+
let selection = null;
|
|
64
|
+
if (body.selection && typeof body.selection === "object") {
|
|
65
|
+
const words = cutWords(body.selection.text);
|
|
66
|
+
const blocks = Array.isArray(body.selection.blocks) ? body.selection.blocks.map(blockOf).filter(Boolean).slice(0, BLOCKS_MOST) : [];
|
|
67
|
+
selection = { kind: body.selection.kind === "blocks" ? "blocks" : "text", blocks, text: words.text, truncated: words.more, ...(Number.isInteger(body.selection.length) ? { length: Math.min(body.selection.length, 1e7) } : {}) };
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
tab: body.tab,
|
|
71
|
+
entry: body.entry,
|
|
72
|
+
kind: body.kind,
|
|
73
|
+
title: cutWords(body.title, 200).text,
|
|
74
|
+
visible: bool(body.visible),
|
|
75
|
+
focused: bool(body.focused),
|
|
76
|
+
touched: bool(body.touched),
|
|
77
|
+
flags: { editing: bool(flags.editing), pending: bool(flags.pending), saving: bool(flags.saving), offline: bool(flags.offline), conflicts: count(flags.conflicts), unfit: count(flags.unfit), changed: bool(flags.changed) },
|
|
78
|
+
notes: count(body.notes),
|
|
79
|
+
caret,
|
|
80
|
+
selection,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Where saving stands for a report's flags, the one word gallery status says: "conflict" (the reader has something to decide
|
|
86
|
+
* on the page: a conflict, or content the document cannot hold), "offline" (commits do not get through), "pending" (changes wait
|
|
87
|
+
* to be saved, or are on their way), else "saved".
|
|
88
|
+
*/
|
|
89
|
+
export function saveOf(flags) {
|
|
90
|
+
if ((flags?.conflicts ?? 0) > 0) return "conflict";
|
|
91
|
+
if (flags?.offline) return "offline";
|
|
92
|
+
if (flags?.pending || flags?.saving) return "pending";
|
|
93
|
+
return "saved";
|
|
94
|
+
}
|
|
@@ -121,6 +121,7 @@ export async function answerOf(found, timeout = 10_000) {
|
|
|
121
121
|
const response = await fetch(`http://127.0.0.1:${found.port}/__gallery/workspace/info`, { redirect: "error", signal: AbortSignal.timeout(timeout) });
|
|
122
122
|
const info = response.ok ? await response.json() : null;
|
|
123
123
|
const same = info?.token === found.token && info.server?.pid === found.pid && typeof info.server.home === "string" && sameDirectory(info.server.home, found.home) && sameDirectory(info.server.data, serviceLocation().data);
|
|
124
|
+
if (process.env.GALLERY_SANDBOX && info?.server?.sandbox !== process.env.GALLERY_SANDBOX) return "no";
|
|
124
125
|
return same ? "yes" : "no";
|
|
125
126
|
} catch (error) {
|
|
126
127
|
return error?.name === "TimeoutError" ? "busy" : "no";
|
|
@@ -174,7 +175,7 @@ async function goneWithin(pid, ms) {
|
|
|
174
175
|
* when its process still runs after `wait` ms, that process killed. Resolves to { ok: true, killed }
|
|
175
176
|
* or { ok: false, reason: "dev" (npm run dev's: stopped where it runs) | "running" (it would not stop), message }.
|
|
176
177
|
*/
|
|
177
|
-
export async function stopServer(found, { wait = 10_000 } = {}) {
|
|
178
|
+
export async function stopServer(found, { wait = 10_000, force = !process.env.GALLERY_SANDBOX } = {}) {
|
|
178
179
|
const host = `127.0.0.1:${found.port}`;
|
|
179
180
|
let status = 0;
|
|
180
181
|
try {
|
|
@@ -189,10 +190,12 @@ export async function stopServer(found, { wait = 10_000 } = {}) {
|
|
|
189
190
|
} catch {} // it closed as it answered, or did not answer: its process says
|
|
190
191
|
if (status === 409) return { ok: false, reason: "dev", message: `The shared Gallery server is npm run dev's (port ${found.port}, pid ${found.pid}): stop it where it runs.` };
|
|
191
192
|
if (await goneWithin(found.pid, wait)) return { ok: true, killed: false };
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
193
|
+
if (force) {
|
|
194
|
+
try {
|
|
195
|
+
process.kill(found.pid);
|
|
196
|
+
} catch {}
|
|
197
|
+
if (await goneWithin(found.pid, 3000)) return { ok: true, killed: true };
|
|
198
|
+
}
|
|
196
199
|
return { ok: false, reason: "running", message: `The server on port ${found.port} (pid ${found.pid}) did not stop: end that process yourself.` };
|
|
197
200
|
}
|
|
198
201
|
|
|
@@ -89,5 +89,13 @@ export function noteInWords(note, { english = false } = {}) {
|
|
|
89
89
|
return text;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// The dev server's Vite config bundles the server's modules into one scope, renaming a constant another module also
|
|
93
|
+
// has at its top (KINDS$1: src/editor/history.mjs and src/render/images.mjs have a KINDS too), and the functions' text
|
|
94
|
+
// below then names the renamed one: the script declares each constant under every name that text has for it.
|
|
92
95
|
/** levelOf, layoutNotesOf and noteInWords as a script's text, for a page (the preview's note) to read notes as qa does and say them. */
|
|
93
|
-
|
|
96
|
+
// Each constant is declared under every name the functions' text has for it (KINDS, or KINDS$1 where a bundle renamed it).
|
|
97
|
+
export const layoutNotesScript = () => {
|
|
98
|
+
const body = `${levelOf}; ${layoutNotesOf}; ${noteInWords};`;
|
|
99
|
+
const declare = (name, value) => [...new Set(body.match(new RegExp(`\\b${name}(?:\\$\\d+)?\\b`, "g")) ?? [name])].map((each) => `const ${each} = ${value};`).join(" ");
|
|
100
|
+
return `${declare("WIDE", WIDE)} ${declare("KINDS", JSON.stringify(KINDS))} ${declare("NARROW_INFO", JSON.stringify(NARROW_INFO))} ${body}`;
|
|
101
|
+
};
|
|
@@ -35,6 +35,10 @@ import { documentKey } from "./workspace-address.mjs";
|
|
|
35
35
|
// consent a block's card asks to be let online (its 允许访问: the kit's net.js):
|
|
36
36
|
// { block (its id), hosts (those of its needs waiting) }: the workbench
|
|
37
37
|
// opens its consent dialog, which writes what the reader says
|
|
38
|
+
// focus where the reader is on the page, when it changed (editor/page/focus.mjs): { caret, selection },
|
|
39
|
+
// caret: { block (a data-block-id, or null), text } or null; selection: { kind ("text" or
|
|
40
|
+
// "blocks"), blocks: [data-block-id…], text } or null, each cut to 200 characters. The workbench
|
|
41
|
+
// reports it, with the tab's other facts, to its server: gallery status (shared/presence.mjs)
|
|
38
42
|
// annotations-removed
|
|
39
43
|
// a 批注 deleted from its card: { items }: the workbench says so, as
|
|
40
44
|
// it does for 全部删除, and its 撤销 puts them back
|
|
@@ -127,6 +127,14 @@ export function writeRecord(root, record) {
|
|
|
127
127
|
/** Where a block of the workspace at `root` is. */
|
|
128
128
|
const folderIn = (root, id) => path.join(layoutOf(root).components, ...id.split("/"));
|
|
129
129
|
|
|
130
|
+
function versionIn(folder) {
|
|
131
|
+
try {
|
|
132
|
+
return JSON.parse(fs.readFileSync(path.join(folder, PACKAGE_MANIFEST), "utf8")).version ?? null;
|
|
133
|
+
} catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
130
138
|
/**
|
|
131
139
|
* What the workspace at `root` holds of the shipped `block`: { added (a folder of its id is there),
|
|
132
140
|
* version (what its manifest says, or null), changed (its files are not what was added: changed since,
|
|
@@ -136,10 +144,7 @@ export function shippedState(root, block) {
|
|
|
136
144
|
const folder = folderIn(root, block.id);
|
|
137
145
|
const now = fingerprintOf(folder);
|
|
138
146
|
if (now === null) return { added: false, version: null, changed: false, behind: false };
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
version = JSON.parse(fs.readFileSync(path.join(folder, PACKAGE_MANIFEST), "utf8")).version ?? null;
|
|
142
|
-
} catch {}
|
|
147
|
+
const version = versionIn(folder);
|
|
143
148
|
const kept = recordOf(root)[block.id];
|
|
144
149
|
const shipped = fingerprintOf(block.folder);
|
|
145
150
|
const changed = now !== shipped && now !== kept?.fingerprint;
|
|
@@ -189,6 +194,8 @@ export function versionAfter(a, b) {
|
|
|
189
194
|
* changed since it was added (or no add put it there), when Gallery's comes after the copy's; else null.
|
|
190
195
|
*/
|
|
191
196
|
export function shippedNewer(root, block) {
|
|
197
|
+
// A content comparison matters only when Gallery actually ships a newer version.
|
|
198
|
+
if (!versionAfter(block.version, versionIn(folderIn(root, block.id)))) return null;
|
|
192
199
|
const state = shippedState(root, block);
|
|
193
200
|
return state.changed && versionAfter(block.version, state.version) ? block.version : null;
|
|
194
201
|
}
|
|
@@ -235,6 +242,17 @@ export function followShipped(root, { ids = null, directory = packsDirectory, lo
|
|
|
235
242
|
return done;
|
|
236
243
|
}
|
|
237
244
|
|
|
245
|
+
/**
|
|
246
|
+
* The blocks of `blocks` (shippedBlocks') that the workspace at `root` holds as Gallery ships them now,
|
|
247
|
+
* an install of their pack having just found or made them so (installOfficialPacks): looked at, so that
|
|
248
|
+
* followShipped, which would find nothing to do for them, does not read each one's files again (a
|
|
249
|
+
* server's start read every block twice over for it, some 150 ms).
|
|
250
|
+
*/
|
|
251
|
+
export function followedAlready(root, blocks) {
|
|
252
|
+
const key = path.resolve(root);
|
|
253
|
+
for (const block of blocks) looked.set(`${key}${path.resolve(block.folder)}`, block.version);
|
|
254
|
+
}
|
|
255
|
+
|
|
238
256
|
const following = new Set(); // the workspaces whose server runs in this process (followOnRead), by their resolved root
|
|
239
257
|
const keyOf = (root) => (process.platform === "win32" ? path.resolve(root).toLowerCase() : path.resolve(root));
|
|
240
258
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import { execFile } from "node:child_process";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { packageRoot, release } from "./paths.mjs";
|
|
7
|
+
import { serviceLocation } from "./service-location.mjs";
|
|
8
|
+
import { defaultWorkspace } from "./workspace.mjs";
|
|
9
|
+
import { writeAtomic } from "./files.mjs";
|
|
10
|
+
|
|
11
|
+
const execute = promisify(execFile);
|
|
12
|
+
const RUN = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
|
13
|
+
const NAME = "HeycysGallery";
|
|
14
|
+
const LABEL = "com.heycys.gallery";
|
|
15
|
+
const xml = value => String(value).replace(/[&<>"']/g, char => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[char]);
|
|
16
|
+
const quoted = value => '"' + String(value).replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/g, '$1$1') + '"';
|
|
17
|
+
const read = file => fs.readFile(file, "utf8").catch(error => { if (error.code === "ENOENT") return null; throw error; });
|
|
18
|
+
|
|
19
|
+
// One user-level login item, pointing to the installed CLI. The CLI reuses serverFor's
|
|
20
|
+
// single-service lock, saved port and hidden process creation; it never opens a browser.
|
|
21
|
+
// No postinstall hook, administrator access or keep-alive loop: gallery stop stays stopped.
|
|
22
|
+
export function createStartup({ platform = process.platform, home = os.homedir(), data = serviceLocation().data,
|
|
23
|
+
workspace = defaultWorkspace(), node = process.execPath, cli = path.join(packageRoot, "bin", "gallery.mjs"),
|
|
24
|
+
available = release && /[\\/]node_modules[\\/]@heycys[\\/]gallery$/.test(packageRoot) && !/(?:^|[\\/])_npx[\\/]/.test(packageRoot) && process.env.GALLERY_NO_AUTOSTART !== "1",
|
|
25
|
+
run = (program, args) => execute(program, args, { windowsHide: true, timeout: 10000 }) } = {}) {
|
|
26
|
+
const supported = platform === "win32" || platform === "darwin";
|
|
27
|
+
const settings = path.join(data, "startup.json");
|
|
28
|
+
const script = path.join(data, "startup.js");
|
|
29
|
+
const plist = path.join(home, "Library", "LaunchAgents", `${LABEL}.plist`);
|
|
30
|
+
let queue = Promise.resolve();
|
|
31
|
+
const serial = task => { const next = queue.then(task); queue = next.catch(() => {}); return next; };
|
|
32
|
+
const config = async () => {
|
|
33
|
+
const text = await read(settings);
|
|
34
|
+
const value = text === null ? {} : JSON.parse(text);
|
|
35
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || (value.enabled !== undefined && typeof value.enabled !== "boolean")) throw new Error("Invalid Gallery startup settings");
|
|
36
|
+
return { text, value };
|
|
37
|
+
};
|
|
38
|
+
async function registration() {
|
|
39
|
+
if (platform === "darwin") return read(plist);
|
|
40
|
+
if (platform !== "win32") return null;
|
|
41
|
+
try {
|
|
42
|
+
const { stdout } = await run("reg.exe", ["query", RUN, "/v", NAME]);
|
|
43
|
+
return /REG_SZ\s+(.+?)\s*$/m.exec(stdout)?.[1] ?? null;
|
|
44
|
+
} catch (error) { if (error.code === 1) return null; throw error; }
|
|
45
|
+
}
|
|
46
|
+
const command = [path.join(process.env.SystemRoot || "C:\\Windows", "System32", "wscript.exe"), "//B", "//Nologo", script].map(quoted).join(" ");
|
|
47
|
+
const environment = { GALLERY_DATA_DIR: data, GALLERY_DEFAULT_WORKSPACE: workspace, GALLERY_WORKSPACE: workspace, GALLERY_NO_BROWSER: "1", GALLERY_NO_UPDATE_CHECK: "1" };
|
|
48
|
+
const windowsScript = `// Gallery login launcher. Window style 0: no console or browser.\nvar shell = new ActiveXObject("WScript.Shell");\nvar env = shell.Environment("Process");\n${Object.entries(environment).map(([key, value]) => `env(${JSON.stringify(key)}) = ${JSON.stringify(value)};`).join("\n")}\nshell.Run(${JSON.stringify([node, cli, "startup", "run"].map(quoted).join(" "))}, 0, false);\n`;
|
|
49
|
+
const macPlist = `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict>
|
|
50
|
+
<key>Label</key><string>${LABEL}</string>
|
|
51
|
+
<key>ProgramArguments</key><array>${[node, cli, "startup", "run"].map(value => `<string>${xml(value)}</string>`).join("")}</array>
|
|
52
|
+
<key>RunAtLoad</key><true/>
|
|
53
|
+
<key>AbandonProcessGroup</key><true/>
|
|
54
|
+
<key>EnvironmentVariables</key><dict>${Object.entries(environment).map(([key, value]) => `<key>${xml(key)}</key><string>${xml(value)}</string>`).join("")}</dict>
|
|
55
|
+
<key>StandardOutPath</key><string>${xml(path.join(data, "logs", "startup.log"))}</string>
|
|
56
|
+
<key>StandardErrorPath</key><string>${xml(path.join(data, "logs", "startup.log"))}</string>
|
|
57
|
+
</dict></plist>\n`;
|
|
58
|
+
const desired = platform === "win32" ? command : macPlist;
|
|
59
|
+
async function apply(enabled) {
|
|
60
|
+
const current = await registration();
|
|
61
|
+
if (enabled) {
|
|
62
|
+
await fs.mkdir(path.join(data, "logs"), { recursive: true });
|
|
63
|
+
if (platform === "win32") {
|
|
64
|
+
// WSH reads ANSI by default. ASCII Unicode escapes preserve Chinese user paths
|
|
65
|
+
// independently of the machine's active code page.
|
|
66
|
+
const encoded = windowsScript.replace(/[^\x00-\x7f]/g, character => "\\u" + character.charCodeAt(0).toString(16).padStart(4, "0"));
|
|
67
|
+
if (await read(script) !== encoded) await writeAtomic(script, encoded);
|
|
68
|
+
if (current !== desired) await run("reg.exe", ["add", RUN, "/v", NAME, "/t", "REG_SZ", "/d", command, "/f"]);
|
|
69
|
+
} else if (current !== desired) {
|
|
70
|
+
await fs.mkdir(path.dirname(plist), { recursive: true });
|
|
71
|
+
await writeAtomic(plist, macPlist);
|
|
72
|
+
}
|
|
73
|
+
} else if (current !== null) {
|
|
74
|
+
if (platform === "win32") await run("reg.exe", ["delete", RUN, "/v", NAME, "/f"]);
|
|
75
|
+
else await fs.unlink(plist);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function status() {
|
|
79
|
+
const { value } = await config();
|
|
80
|
+
const installed = supported && available && await registration() !== null;
|
|
81
|
+
return { supported, available: supported && available, enabled: Boolean(installed) && value.enabled !== false, requested: value.enabled !== false };
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
status,
|
|
85
|
+
sync: () => serial(async () => { if (supported && available) await apply((await config()).value.enabled !== false); return status(); }),
|
|
86
|
+
set: enabled => serial(async () => {
|
|
87
|
+
if (typeof enabled !== "boolean") throw new Error("enabled must be a boolean");
|
|
88
|
+
if (!supported || !available) throw new Error("Autostart requires an installed Gallery package on Windows or macOS");
|
|
89
|
+
const { text, value } = await config();
|
|
90
|
+
await fs.mkdir(data, { recursive: true });
|
|
91
|
+
// Persist the user's choice first. A failed OS operation remains retryable, and a
|
|
92
|
+
// disabled choice can never be silently turned back on by the next package update.
|
|
93
|
+
if (!await writeAtomic(settings, JSON.stringify({ ...value, enabled }, null, 2) + "\n", { expect: text })) throw new Error("Startup settings changed; try again");
|
|
94
|
+
await apply(enabled);
|
|
95
|
+
return status();
|
|
96
|
+
}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let manager;
|
|
101
|
+
export const startup = () => manager ??= createStartup();
|
|
@@ -115,7 +115,7 @@ async function read(root, readOwn) {
|
|
|
115
115
|
return [slug, { ...link, kind: "link", title: link?.title ?? slug, description: "", path: path.join(root, slug, "link.json") }];
|
|
116
116
|
}
|
|
117
117
|
const files = documentFiles(path.join(root, slug));
|
|
118
|
-
const metadata = await fs.readFile(files.json, "utf8").then((text) => JSON.parse(text.replace(/^/, ""))
|
|
118
|
+
const metadata = await fs.readFile(files.json, "utf8").then((text) => JSON.parse(text.replace(/^/, ""))).catch(() => null);
|
|
119
119
|
const value = metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata : {};
|
|
120
120
|
return [slug, { title: oneLine(value.title) || slug, description: oneLine(value.description), icon: shownIcon(value.icon), path: files.html }];
|
|
121
121
|
})));
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { userDirectory } from "./user-directory.mjs";
|
|
3
5
|
import { effectiveLook, lookOf, withLookField } from "./document-look.mjs";
|
|
4
6
|
import { writeAtomic } from "./files.mjs";
|
|
5
7
|
import { refusal, said } from "./i18n/index.mjs";
|
|
6
8
|
import { WORKSPACE_MARK, documentDirectory, documentFiles, layoutOf, markerOf } from "./workspace.mjs";
|
|
7
9
|
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// choice by choice, and a document with none of its own, a new one among them, shows it whole. A
|
|
12
|
-
// workspace without it, an older one's, has every choice at its default. The 写作偏好 page (a doc.json
|
|
13
|
-
// marked "instructions": true) has no look, the workspace's or its own. Here for the server alone: the
|
|
14
|
-
// render, the workbench's service and the preview's watcher read it from disk; a page and the
|
|
15
|
-
// workbench's app are told the look a document shows.
|
|
10
|
+
// One user-level appearance default; legacy workspace defaults are a fallback until
|
|
11
|
+
// a global choice is made. Document overrides win field by field. Instructions pages
|
|
12
|
+
// are exempt. Rendering, report metadata and export all share this resolver.
|
|
16
13
|
|
|
17
14
|
// gallery.json as it reads now: what it holds when it is ours (a workspace without one holds nothing),
|
|
18
15
|
// else why it cannot be taken (its components' messages: src/shared/component-catalog.mjs).
|
|
@@ -34,10 +31,26 @@ const readText = (file) => {
|
|
|
34
31
|
|
|
35
32
|
/** The workspace's default look at `root` (lookOf's form), {} for none, for no gallery.json and for one that does not read or is not ours. */
|
|
36
33
|
export function workspaceLook(root) {
|
|
34
|
+
const global = readText(globalLookFile());
|
|
35
|
+
if (global != null) {
|
|
36
|
+
try { const value = JSON.parse(global); if (value && typeof value.look === "object" && !Array.isArray(value.look)) return lookOf(value.look); } catch {}
|
|
37
|
+
}
|
|
37
38
|
const text = readText(layoutOf(root).marker);
|
|
38
39
|
return text === undefined ? {} : lookOf(settingsOf(text).value?.look);
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
// One default for all storage directories. An explicit empty look overrides legacy
|
|
43
|
+
// gallery.json defaults too; until the user chooses a global default, keep those intact.
|
|
44
|
+
export const globalLookFile = () => path.join(userDirectory("data"), "appearance.json");
|
|
45
|
+
export async function writeGlobalLook(look) {
|
|
46
|
+
const file = globalLookFile();
|
|
47
|
+
const text = await fs.readFile(file, "utf8").catch(error => { if (error.code === "ENOENT") return null; throw error; });
|
|
48
|
+
const next = JSON.stringify({ look: lookOf(look) }, null, 2) + "\n";
|
|
49
|
+
if (text === next) return;
|
|
50
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
51
|
+
if (!await writeAtomic(file, next, { expect: text })) throw new Error("Global appearance changed; try again");
|
|
52
|
+
}
|
|
53
|
+
|
|
41
54
|
/**
|
|
42
55
|
* What document `slug`'s doc.json says of its look at `root`: { own: its `look` as written (null for
|
|
43
56
|
* none, for a doc.json that does not read and for none at all), instructions: whether it is the 写作偏好
|
|
@@ -4,7 +4,7 @@ import fs from "node:fs/promises";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { writeAtomic } from "./files.mjs";
|
|
6
6
|
import { userDirectory } from "./user-directory.mjs";
|
|
7
|
-
import { layoutOf, markerOf } from "./workspace.mjs";
|
|
7
|
+
import { defaultWorkspace, layoutOf, markerOf } from "./workspace.mjs";
|
|
8
8
|
|
|
9
9
|
// The registry of the workspaces used on this machine (notes/plans/workspaces-links-plan-2026-11-08.md
|
|
10
10
|
// §2.3): one file in the user's data folder (../shared/user-directory.mjs "data": %LOCALAPPDATA%\gallery
|
|
@@ -33,6 +33,7 @@ export const registryFile = (options = {}) => path.join(userDirectory("data", op
|
|
|
33
33
|
|
|
34
34
|
/** A workspace's path as one key, however it is spelt: absolute, and lower case on Windows. */
|
|
35
35
|
export const workspaceKey = (root, platform = process.platform) => (platform === "win32" ? path.resolve(root).toLowerCase() : path.resolve(root));
|
|
36
|
+
const allowed = root => !process.env.GALLERY_SANDBOX || workspaceKey(root) === workspaceKey(defaultWorkspace());
|
|
36
37
|
|
|
37
38
|
/** The workspace's short id: ws- and the first six hex digits of the SHA-256 of its key. */
|
|
38
39
|
export const workspaceId = (root, platform = process.platform) => `ws-${createHash("sha256").update(workspaceKey(root, platform)).digest("hex").slice(0, 6)}`;
|
|
@@ -67,7 +68,9 @@ export function registryOf(text, platform = process.platform) {
|
|
|
67
68
|
/** The registry as read now (registryOf of its file; none there: an empty one). */
|
|
68
69
|
export function readRegistry({ file = registryFile() } = {}) {
|
|
69
70
|
try {
|
|
70
|
-
|
|
71
|
+
const registry = registryOf(readFileSync(file, "utf8"));
|
|
72
|
+
if (process.env.GALLERY_SANDBOX) registry.workspaces = registry.workspaces.filter(({ root }) => allowed(root));
|
|
73
|
+
return registry;
|
|
71
74
|
} catch {
|
|
72
75
|
return { version: 1, workspaces: [] };
|
|
73
76
|
}
|
|
@@ -145,7 +148,7 @@ export function isWorkspace(root) {
|
|
|
145
148
|
*/
|
|
146
149
|
export async function registerWorkspace(root, { file = registryFile(), now = Date.now(), force = false } = {}) {
|
|
147
150
|
const resolved = path.resolve(root);
|
|
148
|
-
if (!isWorkspace(resolved)) return "none";
|
|
151
|
+
if (!allowed(resolved) || !isWorkspace(resolved)) return "none";
|
|
149
152
|
const key = workspaceKey(resolved);
|
|
150
153
|
const recent = (entry) => {
|
|
151
154
|
const age = now - Date.parse(entry.seen);
|
|
@@ -76,7 +76,7 @@ export const PACKAGE_MANIFEST = "component.json";
|
|
|
76
76
|
/** The folders a document keeps beside its files: what is its own of a folder it shares with the packages or the custom pages. */
|
|
77
77
|
export const DOCUMENT_FOLDERS = ["assets", "materials"];
|
|
78
78
|
/** The folders at the root that hold no document but may share their name with one made before them. */
|
|
79
|
-
export const SHARED_FOLDERS = [COMPONENTS_FOLDER, PAGES_FOLDER, EXTENSIONS_FOLDER];
|
|
79
|
+
export const SHARED_FOLDERS = [COMPONENTS_FOLDER, PAGES_FOLDER, EXTENSIONS_FOLDER, 'preferences'];
|
|
80
80
|
/** Whether a new document (or custom page) may not be called `name`, whatever the workspace holds. */
|
|
81
81
|
export const keptName = (name) => RESERVED.has(name) || SHARED_FOLDERS.includes(name);
|
|
82
82
|
/** Whether `name` may be a document's slug. */
|
|
@@ -167,6 +167,24 @@ function registryDocuments() {
|
|
|
167
167
|
return utf8 ? valueOf(utf8.toString("utf8")) : null;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
// The Personal value as registryDocuments reads it, once a process: every defaultWorkspace() asks for it
|
|
171
|
+
// where GALLERY_DEFAULT_WORKSPACE names no folder (a server `gallery open` started: some 300 times as a
|
|
172
|
+
// document's page loads, and a command a dozen), and each reg.exe blocked for some 30 ms.
|
|
173
|
+
let personal;
|
|
174
|
+
const registryOnce = () => (personal === undefined ? (personal = registryDocuments()) : personal);
|
|
175
|
+
// The user's own home as the system knows it, once a process too.
|
|
176
|
+
let ownHome;
|
|
177
|
+
const realHomeOnce = () => {
|
|
178
|
+
if (ownHome === undefined) {
|
|
179
|
+
try {
|
|
180
|
+
ownHome = os.userInfo().homedir;
|
|
181
|
+
} catch {
|
|
182
|
+
ownHome = null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return ownHome;
|
|
186
|
+
};
|
|
187
|
+
|
|
170
188
|
const sameFolder = (a, b, platform) => {
|
|
171
189
|
const [one, two] = [path.resolve(a), path.resolve(b)];
|
|
172
190
|
return platform === "win32" ? one.toLowerCase() === two.toLowerCase() : one === two;
|
|
@@ -174,7 +192,7 @@ const sameFolder = (a, b, platform) => {
|
|
|
174
192
|
|
|
175
193
|
/**
|
|
176
194
|
* The user's Documents folder, the default workspace's parent: on Windows the one Explorer shows as
|
|
177
|
-
* 文档 (SHELL_FOLDERS' Personal, its %VARIABLES% expanded), read for the user's own home alone (a test
|
|
195
|
+
* 文档 (SHELL_FOLDERS' Personal, read once a process, its %VARIABLES% expanded), read for the user's own home alone (a test
|
|
178
196
|
* or a sandbox naming another HOME/USERPROFILE gets <that home>/Documents, never the user's folder);
|
|
179
197
|
* on Linux the XDG documents folder (~/.config/user-dirs.dirs' XDG_DOCUMENTS_DIR: ~/文档 on a Chinese
|
|
180
198
|
* desktop); a folder either names that is not there, or not absolute, is passed over; else, and on
|
|
@@ -182,8 +200,8 @@ const sameFolder = (a, b, platform) => {
|
|
|
182
200
|
*/
|
|
183
201
|
export function documentsFolder({
|
|
184
202
|
home = os.homedir(), env = process.env, platform = process.platform,
|
|
185
|
-
realHome = (
|
|
186
|
-
readRegistry =
|
|
203
|
+
realHome = realHomeOnce(),
|
|
204
|
+
readRegistry = registryOnce, readText = (file) => readFileSync(file, "utf8"), exists = existsSync,
|
|
187
205
|
} = {}) {
|
|
188
206
|
const fallback = path.join(home, "Documents");
|
|
189
207
|
const usable = (folder) => folder && path.isAbsolute(folder) && exists(folder) ? path.normalize(folder) : null;
|
|
@@ -194,10 +212,15 @@ export function documentsFolder({
|
|
|
194
212
|
value = readRegistry();
|
|
195
213
|
} catch {}
|
|
196
214
|
if (!value) return fallback;
|
|
197
|
-
|
|
198
|
-
|
|
215
|
+
// A variable by its name in any case, USERPROFILE the home where none is set (the environment looked
|
|
216
|
+
// through only for a name not written in capitals there: Windows' own is read by any case).
|
|
217
|
+
const variable = (name) => {
|
|
218
|
+
const wanted = name.toUpperCase();
|
|
219
|
+
const setting = env[wanted] ?? Object.entries(env).findLast(([key]) => key.toUpperCase() === wanted)?.[1];
|
|
220
|
+
return setting ?? (wanted === "USERPROFILE" ? home : undefined);
|
|
221
|
+
};
|
|
199
222
|
let unknown = false;
|
|
200
|
-
const expanded = value.replace(/%([^%]+)%/g, (whole, name) =>
|
|
223
|
+
const expanded = value.replace(/%([^%]+)%/g, (whole, name) => variable(name) ?? ((unknown = true), whole));
|
|
201
224
|
return (!unknown && usable(expanded)) || fallback;
|
|
202
225
|
}
|
|
203
226
|
if (platform === "linux") {
|
|
@@ -462,9 +485,13 @@ export async function readLink(folder) {
|
|
|
462
485
|
return text === null ? null : readLinkText(text);
|
|
463
486
|
}
|
|
464
487
|
|
|
465
|
-
/** The
|
|
466
|
-
export async function pageSlug(root) {
|
|
467
|
-
|
|
488
|
+
/** The editor page associated with one preference file; legacy unmarked pages belong to the original. */
|
|
489
|
+
export async function pageSlug(root, preference = null) {
|
|
490
|
+
for (const item of await documentsIn(layoutOf(root).preferencesPage)) {
|
|
491
|
+
const meta = await readJson(documentFiles(item.folder).json);
|
|
492
|
+
if (meta?.preference !== null && (preference === null || meta?.preference === preference || (preference === 'preferences.md' && meta?.preference === undefined))) return item.slug;
|
|
493
|
+
}
|
|
494
|
+
return null;
|
|
468
495
|
}
|
|
469
496
|
|
|
470
497
|
/** The id of an entry of the trash, a folder of .gallery/trash/. */
|
|
@@ -1,46 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { defaultWorkspace } from "./workspace.mjs";
|
|
4
|
-
import { defaultPreferencesFile } from "./paths.mjs";
|
|
1
|
+
export { defaultPreferencesFile } from "./paths.mjs";
|
|
5
2
|
|
|
6
|
-
//
|
|
7
|
-
// at <workspace>/preferences.md (shared/workspace.mjs). Gallery ships its default (writing-preferences.md,
|
|
8
|
-
// beside this module; the release's assets/writing-preferences.md: paths.mjs defaultPreferencesFile): what
|
|
9
|
-
// to write, in what order, the opening summary, titles, fidelity to the materials, evidence). The first
|
|
10
|
-
// time the preferences are read and there is no preferences.md in the workspace (by gallery new, gallery
|
|
11
|
-
// import, gallery list, or the workbench's 写作偏好 in 设置), it is made from that default; from then on it is the user's, edited in the workbench (its page, src/shared/reports.mjs
|
|
12
|
-
// isInstructions, written as this file when the user saves it: src/transfer/service.mjs), and
|
|
13
|
-
// never written over by the default again: a file that exists stays as it is, an empty one too.
|
|
14
|
-
|
|
15
|
-
/** Gallery's default writing preferences (Markdown). */
|
|
16
|
-
export { defaultPreferencesFile };
|
|
17
|
-
|
|
18
|
-
/** Where the user's writing preferences are, for the documents of workspace `reportsDirectory`. */
|
|
19
|
-
export const preferencesFileOf = () => path.join(defaultWorkspace(), "preferences.md");
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* The user's writing preferences: { file, text, made }, the file made from Gallery's default
|
|
23
|
-
* first when there is none (`made`), never over one that exists (another reader making it at the
|
|
24
|
-
* same moment: its file is read).
|
|
25
|
-
*/
|
|
26
|
-
export async function readWritingPreferences() {
|
|
27
|
-
const reportsDirectory = defaultWorkspace();
|
|
28
|
-
const file = preferencesFileOf();
|
|
29
|
-
const existing = await fs.readFile(file, "utf8").catch((error) => {
|
|
30
|
-
if (error.code === "ENOENT") return null;
|
|
31
|
-
throw error;
|
|
32
|
-
});
|
|
33
|
-
if (existing !== null) return { file, text: existing, made: false };
|
|
34
|
-
const text = await fs.readFile(defaultPreferencesFile, "utf8");
|
|
35
|
-
await fs.mkdir(reportsDirectory, { recursive: true });
|
|
36
|
-
try {
|
|
37
|
-
await fs.writeFile(file, text, { flag: "wx" });
|
|
38
|
-
} catch (error) {
|
|
39
|
-
if (error.code !== "EEXIST") throw error;
|
|
40
|
-
return { file, text: await fs.readFile(file, "utf8"), made: false };
|
|
41
|
-
}
|
|
42
|
-
return { file, text, made: true };
|
|
43
|
-
}
|
|
3
|
+
// Storage and migration live in preference-documents.mjs.
|
|
44
4
|
|
|
45
5
|
/** Whether preferences (their Markdown) say anything: a letter or a digit under the title (its first line, a # heading). */
|
|
46
6
|
export const saysAnything = (text) => /[\p{L}\p{N}]/u.test(text.replace(/^\s*# [^\n]*/, ""));
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
+
import{h as a,i as b}from"./chunk-TLNJWEFY.mjs";import"./chunk-ZOEJQOKT.mjs";import"./chunk-IA4PSIMD.mjs";import"./chunk-6N25CJ6Y.mjs";import"./chunk-F4CT35KL.mjs";import"./chunk-6ILJWFYW.mjs";import"./chunk-VRPVVMRB.mjs";import"./chunk-UMHUSOPJ.mjs";import"./chunk-W5NPH5UA.mjs";import"./chunk-ECZBLQKK.mjs";import"./chunk-E7656LR7.mjs";import"./chunk-TKWEX4FD.mjs";import"./chunk-A55HN5OK.mjs";import"./chunk-5L7RYZCL.mjs";export{b as createEditService,a as loadToken};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
+
import{a as d,e as c,h as x,n as v}from"./chunk-B6RAGF4R.mjs";import{a as N}from"./chunk-5TLLIZT7.mjs";import{b as m,f as $}from"./chunk-L4WLQYFC.mjs";import"./chunk-5L7RYZCL.mjs";import I from"node:path";import{draftOf as L,workspaceOf as b}from"../browser/shared/drafts.mjs";import{pageLinks as j,pageOfLink as A}from"../browser/shared/page-links.mjs";import{reportsDirectory as E}from"../browser/shared/paths.mjs";import{isWorkspace as F,isWorkspaceId as K,registerQuietly as Q,workspaceId as f,workspaceKey as O}from"../browser/shared/workspace-registry.mjs";import{documentDirectory as S,documentFiles as U}from"../browser/shared/workspace.mjs";var D=r=>[...r.documents.values()].filter(({url:e})=>e===void 0);async function q(r){if(K(r)){let t=b(r);return t.root?{root:t.root,id:r}:{refused:`${r} is ${t.refused==="ambiguous"?"the id of more than one registered workspace":"no registered workspace"}: gallery workspaces lists them, with their ids and paths.`}}let e=I.resolve(r);if(!F(e))return{refused:`${e} is not a Gallery workspace (it has no gallery.json of Gallery's): gallery init "${e}" makes one, gallery workspaces lists the registered ones.`};await Q(e);let i=b(f(e));return i.root&&O(i.root)===O(e)?{root:e,id:f(e)}:{refused:`${e} could not be registered (gallery workspaces lists the registered ones).`}}async function Y(r){let{positional:e,values:i}=$({values:["--to","--under"],argv:r});if(e.length!==1)throw new m("Name the document to copy, one.");if(!i.has("--to"))throw new m("Say where it is copied to: --to <workspace> (its path, or its ws- id from gallery workspaces).");let t=await d(),s=await x(e[0],"the document to copy",t);s.refused&&(console.error(s.refused),process.exit(1));let T=t.documents.get(s.slug),w=L(s.slug),W=f(E),g=w?.ws??W,C=w?.slug??s.slug,o=await q(i.get("--to"));o.refused&&(console.error(`${o.refused} Nothing copied.`),process.exit(1)),o.id===g&&(console.error(`${c(t,s.slug)} is in ${o.root} already: gallery duplicate <slug> copies it there, after itself.`),process.exit(1)),T.url!==void 0&&(console.error(`${c(t,s.slug)} is a web link, not a document: gallery link add makes the same link in the other workspace (gallery link add <url> "<name>" --workspace "${o.root}").`),process.exit(1));let u=await d(o.root),a="";if(i.has("--under")){let l=i.get("--under"),y=D(u).some(({slug:G})=>G===l)?l:A(j(D(u)),l.replace(/^.*#/,""));y||(console.error(`No document "${l}" in ${o.root} to put the copy under: gallery list --tree --workspace "${o.root}" shows its documents. Nothing copied.`),process.exit(1)),a=y}let n=await v("copy-from",{sourceWs:g,slug:C,parent:a,index:(u.children[a]??[]).length,keep:!0},{root:o.root});n?.ok||(console.error(`Not copied: ${n?.message??n?.reason??"no answer"}.`),process.exit(1));let h=await d(o.root),k=h.documents.get(n.slug),p=n.count-1;console.log(`Copied ${c(t,s.slug)}${p?` with the ${p} document${p===1?"":"s"} under it`:""} to ${o.root}: ${n.slug}${k?.title?` (${k.title})`:""}, last ${a?`under ${c(h,a)}`:"at its top level"}. The original stays where it was.`),console.log(`The copy's file: ${N(U(S(n.slug,o.root)).html)}`),console.log(`\u4E0B\u4E00\u6B65\uFF1Awork on the copy in that workspace: gallery open ${n.slug} --workspace "${o.root}" (every command takes --workspace).`)}export{Y as run};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
-
import{e as H,f as K,g as Q}from"./chunk-
|
|
2
|
+
import{e as H,f as K,g as Q}from"./chunk-TLNJWEFY.mjs";import{d as D,f as E,g as T,h as F}from"./chunk-TKWEX4FD.mjs";import{existsSync as R}from"node:fs";var U=120;function W(n,I){let p=({packages:s,problems:c})=>new Map([...s,...c].map(([a,{stamp:t}])=>[a,t])),[l,f]=[p(n),p(I)];return[...new Set([...l.keys(),...f.keys()])].filter(s=>l.get(s)!==f.get(s)).sort()}function A({root:n,tell:I,delay:p=U,quiet:l=Q,hold:f=H,limit:s=K,now:c=Date.now,timers:a={set:(t,m)=>setTimeout(t,m),clear:t=>clearTimeout(t)}}){let t=null,m=0,u=null,r=new Map;T(n);let M=i=>{t&&m<=c()+i||(a.clear(t),m=c()+i,t=a.set(w,i),t.unref?.())};function w({settled:i=!1}={}){t=null;let d=T(n),o=E(n,d.settings),b=c();o.stamp!==u?.stamp&&(u={stamp:o.stamp,at:b});let y=b-u.at;if(y<l&&!i)return M(l-y);let g=new Map(o.packages),k=new Map(o.problems),h=1/0;for(let[e,L]of d.packages){if(!(!o.settings.disabled.includes(e)&&(k.has(e)||!g.has(e)&&R(L.folder)))){r.delete(e);continue}r.has(e)||r.set(e,u.at);let O=Math.min(f-y,s-(b-r.get(e)));if(O<=0){r.delete(e);continue}g.set(e,L),k.delete(e),h=Math.min(h,O)}let{settings:C,packs:S}=o,x=D({packages:g,problems:k,settings:C,packs:S});if(x!==d.stamp){let e={packages:g,problems:k,settings:C,packs:S,stamp:x};F(n,e),I(e,W(d,e))}h!==1/0&&M(h)}return{changed:()=>M(p),taken(){a.clear(t),w({settled:!0})},close(){a.clear(t),t=null}}}export{U as a,A as b};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
+
import{a as P,b as S}from"./chunk-HFTBVEZK.mjs";import{a as y,d as C,k as b,r as h,t as g}from"./chunk-BSJKFE7I.mjs";import{c as m,g as c,k,v as w}from"./chunk-TKWEX4FD.mjs";import{BLOCK_ICON as E}from"../browser/editor/components/icons.mjs";import{LISTED_BLOCKS as O}from"../browser/editor/components/registry.mjs";import{PACKAGE_ID as L}from"../browser/shared/components/installed.mjs";import{netHostsOf as I}from"../browser/shared/net-policy.mjs";import{inLanguage as A,refusal as l,said as o}from"../browser/shared/i18n/index.mjs";function T({root:s,taken:u}){let d=()=>{let e=c(s),i=new Map(C().map(t=>[t.id,t]));return{packages:w(e).map(t=>{let n=i.get(t.id);return{...t,newer:n?b(s,n):null,...n?{addAs:n.pack===y?n.name:n.id}:{}}}),packs:g(s,{settings:e.settings}),packageIcon:E,blocks:O.map(({id:t,name:n,icon:a,hideable:r})=>({id:t,name:{zh:A("zh",n),en:A("en",n)},icon:a,hidden:r&&e.settings.hidden.includes(t),hideable:r})),problem:k(e)}},B=({id:e,hidden:i,shown:t,disabled:n,unlisted:a,net:r})=>{if(![i,t,n,a,r].every(f=>f===void 0||typeof f=="boolean"))return o("\u5F00\u5173\u7684\u503C\u4E0D\u5BF9");let p=O.find(f=>f.id===e);return p?n!==void 0||t!==void 0||a!==void 0||r!==void 0?o("\u5185\u7F6E\u5757\u53EA\u80FD\u9690\u85CF\uFF0C\u4E0D\u80FD\u505C\u7528"):i&&!p.hideable?o("\u8FD9\u4E2A\u5757\u4E0D\u80FD\u9690\u85CF"):null:typeof e=="string"&&L.test(e)?r&&I(c(s).packages.get(e)?.needs).length===0?o("\u8FD9\u4E2A\u5757\u4E0D\u8054\u7F51"):null:o("\u6CA1\u6709\u8FD9\u4E2A\u7EC4\u4EF6")};return{read:d,write:async e=>{if(e?.pack!==void 0){let{pack:r,enabled:p}=e;if(typeof p!="boolean")throw l(o("\u5F00\u5173\u7684\u503C\u4E0D\u5BF9"));if(typeof r!="string"||!g(s,{settings:c(s).settings}).some(({id:f})=>f===r))throw l(o("\u6CA1\u6709\u8FD9\u4E2A\u5757\u5305"));return await m(s,{pack:r,enabled:p}),u(),d()}let i=B(e);if(i)throw l(i);let{net:t,...n}=e,a=t===void 0?void 0:t?I(c(s).packages.get(e.id)?.needs):[];return await m(s,{...n,...a?{net:a}:{}}),u(),d()},install:({path:e,zip:i}={})=>{let t=typeof i=="string"?S(Buffer.from(i,"base64")):typeof e=="string"&&e.trim()?P(e):null;if(!t)throw l(o("\u7C98\u8D34\u5757\u5305\u7684\u6587\u4EF6\u5939\u6216 zip \u7684\u8DEF\u5F84"),{code:"path"});let n=h(s,t);return u(),{...d(),installed:n}}}}export{T as a};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
+
import"./chunk-5L7RYZCL.mjs";import t from"node:fs/promises";import l from"node:path";import{storedOf as g}from"../browser/shared/outline.mjs";import{entryPattern as k,isTop as h,topOf as j}from"../browser/shared/outline-shape.mjs";import{workspaceId as S}from"../browser/shared/workspace-registry.mjs";import{isDocumentSource as b,isSlugName as O,layoutOf as y,markerOf as F}from"../browser/shared/workspace.mjs";var N=new Set(["outline.json","blocks.json","hints.json","emoji-library","pages"]),L=new Set([".git","node_modules"]);async function _(o,w){let r=y(o),n=y(w),p=await t.lstat(r.marker).catch(()=>null);if(!p?.isFile()||p.isSymbolicLink())throw new Error("\u6E90\u5DE5\u4F5C\u533A\u9700\u8981\u666E\u901A\u6587\u4EF6 gallery.json");let c=F(await t.readFile(r.marker,"utf8"));if(!c.ours)throw new Error("\u6E90\u76EE\u5F55\u4E0D\u662F Gallery \u5DE5\u4F5C\u533A");delete c.value.extensions,await t.writeFile(n.marker,JSON.stringify(c.value,null,2)+`
|
|
3
|
+
`,{flag:"wx"});let f=0;async function a(i,e){let s=await t.lstat(i);if(s.isSymbolicLink()){f++;return}if(s.isFile())return t.copyFile(i,e);if(!s.isDirectory()){f++;return}await t.mkdir(e);for(let d of await t.readdir(i))L.has(d.toLowerCase())||await a(l.join(i,d),l.join(e,d))}for(let i of await t.readdir(o,{withFileTypes:!0})){let{name:e}=i,s=l.join(o,e);if(["preferences.md","preferences","components","pages","extensions"].includes(e))await a(s,l.join(w,e));else if(O(e)){if(i.isSymbolicLink()){f++;continue}if(!i.isDirectory())continue;(await t.readdir(s)).some(u=>b(u)||u==="link.json")&&await a(s,l.join(w,e))}}let m=await t.lstat(r.gallery).catch(i=>{if(i.code!=="ENOENT")throw i;return null});if(m?.isDirectory()&&!m.isSymbolicLink()){await t.mkdir(n.gallery);for(let i of await t.readdir(r.gallery))N.has(i)&&await a(l.join(r.gallery,i),l.join(n.gallery,i));await x(n.outline,o)}return f+Number(m?.isSymbolicLink()??!1)}async function x(o,w){let r=await t.readFile(o,"utf8").then(JSON.parse).catch(()=>null),n=g(r);if(!n)return;let p=S(w),c=n.groups.filter(i=>!i.workspace&&(!i.owner||i.owner===p)).map(({owner:i,...e})=>e),f=new Set(c.map(i=>j(i.id))),a=i=>typeof i=="string"&&!i.startsWith("~")&&k.test(i),m=Object.fromEntries(Object.entries(n.children).filter(([i])=>h(i)?f.has(i):a(i)).map(([i,e])=>[i,e.filter(a)]));await t.writeFile(o,JSON.stringify({version:2,groups:c,children:m,...r.head?{head:r.head}:{}},null,2)+`
|
|
4
|
+
`)}export{_ as copySandboxWorkspace};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
+
import{c as a,d as b,e as c,f as d,g as e,h as f,i as g,j as h,k as i,l as j,m as k,n as l,o as m,p as n,q as o,r as p,s as q,t as r,u as s,v as t,w as u,x as v}from"./chunk-B2EIMERT.mjs";import"./chunk-HYMEEXKN.mjs";import"./chunk-A55HN5OK.mjs";import"./chunk-5L7RYZCL.mjs";export{d as ASSETS,o as afterKitTags,s as allowedScripts,t as allowedStyles,j as assetProblems,i as assetTags,a as d3SourcePath,q as earlyScriptFailures,b as katexFontsSourceDirectory,h as kitTags,u as loadKatexCss,v as loadVendorFile,c as mhchemSourcePath,l as pruneUnusedKitTags,f as publicFileOf,e as publicPathOf,p as tagFailures,g as tagOf,k as usedAssets,r as withKitCopies,m as withKitTags,n as withNeededKitTags};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire as __galleryRequire } from "node:module"; import { fileURLToPath as __galleryPath } from "node:url"; const require = __galleryRequire(import.meta.url); const __filename = __galleryPath(import.meta.url); const __dirname = __galleryPath(new URL(".", import.meta.url));
|
|
2
|
+
import{b as a,c as b,d as c,e as d,f as e,g as f,h as g,i as h,j as i}from"./chunk-G5HV7BC3.mjs";import"./chunk-5TLLIZT7.mjs";import"./chunk-TLNJWEFY.mjs";import"./chunk-ZOEJQOKT.mjs";import"./chunk-IA4PSIMD.mjs";import"./chunk-B2EIMERT.mjs";import"./chunk-6N25CJ6Y.mjs";import"./chunk-F4CT35KL.mjs";import"./chunk-6ILJWFYW.mjs";import"./chunk-VRPVVMRB.mjs";import"./chunk-HYMEEXKN.mjs";import"./chunk-L4WLQYFC.mjs";import"./chunk-UMHUSOPJ.mjs";import"./chunk-W5NPH5UA.mjs";import"./chunk-ECZBLQKK.mjs";import"./chunk-E7656LR7.mjs";import"./chunk-TKWEX4FD.mjs";import"./chunk-A55HN5OK.mjs";import"./chunk-5L7RYZCL.mjs";export{e as blockSaid,d as countsSaid,f as documentOf,h as failed,c as kindName,a as localTime,g as rereadLine,i as run,b as shortOf};
|