@heycys/gallery 0.2.1 → 0.2.2
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/THIRD-PARTY-NOTICES.md +1 -1
- package/browser/editor/page/annotate/index.mjs +3 -2
- package/browser/editor/page/board/index.mjs +2 -1
- package/browser/editor/page/board/settings.mjs +2 -1
- package/browser/editor/page/core.mjs +3 -2
- package/browser/editor/page/custom-page.mjs +4 -3
- package/browser/editor/page/editor.mjs +9 -5
- package/browser/editor/page/emoji-picker/support.mjs +3 -2
- package/browser/editor/page/links/index.mjs +1 -12
- package/browser/editor/page/links/pages.mjs +3 -2
- package/browser/editor/page/outline/folds.mjs +3 -0
- package/browser/editor/page/post.mjs +2 -1
- package/browser/editor/page/present-links.mjs +4 -3
- package/browser/editor/page/present-shell.mjs +3 -58
- package/browser/editor/page/session.mjs +3 -37
- package/browser/editor/page/storage.mjs +5 -5
- package/browser/shared/component-catalog.mjs +2 -2
- package/browser/shared/drafts.mjs +26 -51
- package/browser/shared/emoji/icon-value.mjs +2 -1
- package/browser/shared/extension-manifest.mjs +90 -0
- package/browser/shared/extensions.mjs +26 -16
- package/browser/shared/http.mjs +1 -1
- package/browser/shared/i18n/en.mjs +46 -15
- package/browser/shared/media.mjs +2 -1
- package/browser/shared/outline.mjs +11 -51
- package/browser/shared/page-address.mjs +8 -15
- package/browser/shared/paths.mjs +2 -1
- package/browser/shared/preferences.mjs +3 -1
- package/browser/shared/preview-server.mjs +51 -63
- package/browser/shared/service-location.mjs +18 -0
- package/browser/shared/session.mjs +3 -6
- package/browser/shared/workspace-address.mjs +19 -0
- package/browser/shared/workspace-columns.mjs +1 -2
- package/browser/shared/workspace-registry.mjs +6 -40
- package/browser/shared/workspace.mjs +3 -6
- package/browser/shared/writing-preferences.mjs +6 -4
- package/dist/{chunk-BYSF5UMM.mjs → chunk-236JJXWY.mjs} +2 -2
- package/dist/{chunk-3A5I2GJK.mjs → chunk-2KAVXMKG.mjs} +1 -1
- package/dist/chunk-2T4PZQEP.mjs +2 -0
- package/dist/{chunk-SAHS56QM.mjs → chunk-3SKOU6OZ.mjs} +1 -1
- package/dist/{chunk-S2Y7VI4H.mjs → chunk-3XZOA7AH.mjs} +2 -2
- package/dist/{chunk-MKTX5PGV.mjs → chunk-43B75J5T.mjs} +1 -1
- package/dist/{chunk-VKSRM7EY.mjs → chunk-4B2TB5K2.mjs} +1 -1
- package/dist/{chunk-LV24RY2E.mjs → chunk-4K5KC6V2.mjs} +1 -1
- package/dist/{chunk-SOD23RGL.mjs → chunk-4KUO7R2M.mjs} +8 -8
- package/dist/chunk-4SNV4HA5.mjs +2 -0
- package/dist/chunk-53XDTHTG.mjs +4 -0
- package/dist/chunk-5XV3YYLG.mjs +2 -0
- package/dist/{chunk-VNUWREXU.mjs → chunk-65NTKF4N.mjs} +1 -1
- package/dist/chunk-6GV3CBB2.mjs +2 -0
- package/dist/{chunk-DH6GMDNN.mjs → chunk-6L6V7YR2.mjs} +1 -1
- package/dist/{chunk-TOE6DPLH.mjs → chunk-6NLXUKAY.mjs} +1 -1
- package/dist/chunk-6Q7ASAXJ.mjs +3 -0
- package/dist/{chunk-ZCP7BTOZ.mjs → chunk-6QBHU4DF.mjs} +1 -1
- package/dist/{chunk-QXVNUASA.mjs → chunk-6QWQXHAV.mjs} +1 -1
- package/dist/chunk-6XD6U3QA.mjs +5 -0
- package/dist/{chunk-36FIRJIF.mjs → chunk-742D2WWJ.mjs} +2 -2
- package/dist/{chunk-7M7UPWU6.mjs → chunk-7EFHNQ5B.mjs} +1 -1
- package/dist/{chunk-JI6T3A4Y.mjs → chunk-7ISBBDJR.mjs} +1 -1
- package/dist/{chunk-2GKGEVMR.mjs → chunk-7UZUQQ7Q.mjs} +25 -24
- package/dist/{chunk-VPVOAJ7X.mjs → chunk-AGDILNWB.mjs} +1 -1
- package/dist/{chunk-5IIH335D.mjs → chunk-ALZ6ZENZ.mjs} +1 -1
- package/dist/{chunk-G4YP7GVC.mjs → chunk-BFJGHSQN.mjs} +1 -1
- package/dist/chunk-BLNQUR5R.mjs +168 -0
- package/dist/chunk-BMX3VUHY.mjs +2 -0
- package/dist/chunk-BN7M3IN7.mjs +8 -0
- package/dist/{chunk-AMDGVDNV.mjs → chunk-BQNTTFPL.mjs} +1 -1
- package/dist/chunk-C257H6IA.mjs +2 -0
- package/dist/{chunk-74RPP2M2.mjs → chunk-C564NU2H.mjs} +1 -1
- package/dist/chunk-CHQU37CA.mjs +2 -0
- package/dist/{chunk-WCWYNFDF.mjs → chunk-E4IHAIJ7.mjs} +1 -1
- package/dist/{chunk-7XDYK3UC.mjs → chunk-E7656LR7.mjs} +1 -1
- package/dist/{chunk-KFZBYDIS.mjs → chunk-ECZBLQKK.mjs} +1 -1
- package/dist/{chunk-PAFSFVYB.mjs → chunk-ERE2BXYV.mjs} +1 -1
- package/dist/{chunk-ZEY2RLBT.mjs → chunk-ERLAIBOA.mjs} +2 -2
- package/dist/chunk-FFV733MA.mjs +2 -0
- package/dist/{chunk-CZ5KJSGK.mjs → chunk-GJJRBGMY.mjs} +1 -1
- package/dist/{chunk-VA3KAQZD.mjs → chunk-GKOBB6DH.mjs} +1 -1
- package/dist/chunk-GMJD7ID3.mjs +2 -0
- package/dist/{chunk-QZ35UAB4.mjs → chunk-GRAXKKG4.mjs} +1 -1
- package/dist/{chunk-2RKIHJI2.mjs → chunk-HYMEEXKN.mjs} +1 -1
- package/dist/{chunk-DKR7LFIY.mjs → chunk-IA2SIEJQ.mjs} +1 -1
- package/dist/{chunk-QK3VISCA.mjs → chunk-IA4PSIMD.mjs} +1 -1
- package/dist/chunk-JPG2LBQM.mjs +11 -0
- package/dist/{chunk-VGTMLQWZ.mjs → chunk-JXXHAXYH.mjs} +1 -1
- package/dist/{chunk-5MEXZ2UF.mjs → chunk-K3JKPFL5.mjs} +1 -1
- package/dist/{chunk-LE44MD3O.mjs → chunk-KLQP46Y3.mjs} +1 -1
- package/dist/{chunk-NAGMRVW4.mjs → chunk-LKX55H76.mjs} +1 -1
- package/dist/chunk-LMUZQK5O.mjs +2 -0
- package/dist/{chunk-CG3UHDKH.mjs → chunk-M4NRDYI5.mjs} +1 -1
- package/dist/{chunk-HM4E43VM.mjs → chunk-MAFIGVXY.mjs} +1 -1
- package/dist/{chunk-4DNHHJSL.mjs → chunk-MKIBJTJZ.mjs} +1 -1
- package/dist/{chunk-L7W624FD.mjs → chunk-MNWJ4EZY.mjs} +1 -1
- package/dist/{chunk-FXQBADMN.mjs → chunk-NHP66IWR.mjs} +1 -1
- package/dist/{chunk-YBB5LWZK.mjs → chunk-OEIJLMPI.mjs} +1 -1
- package/dist/{chunk-NWRZXELO.mjs → chunk-PAREWQGB.mjs} +1 -1
- package/dist/{chunk-7N2LPG33.mjs → chunk-PDZR3L3C.mjs} +1 -1
- package/dist/{chunk-YFJU3O7G.mjs → chunk-PLB7L22J.mjs} +1 -1
- package/dist/{chunk-4AMTKWDO.mjs → chunk-PUOQ65NC.mjs} +1 -1
- package/dist/chunk-Q4MS6OFC.mjs +2 -0
- package/dist/{chunk-3OFXMN6N.mjs → chunk-Q7P3Q5YX.mjs} +1 -1
- package/dist/{chunk-6MPKCH3P.mjs → chunk-QNECWMGR.mjs} +1 -1
- package/dist/{chunk-HBAUKKQH.mjs → chunk-QP7BOHE4.mjs} +1 -1
- package/dist/chunk-QVGOPHOF.mjs +2 -0
- package/dist/{chunk-NFABH3QW.mjs → chunk-RRKJMHNX.mjs} +2 -2
- package/dist/chunk-S3SX3COT.mjs +20 -0
- package/dist/{chunk-ZO7SFB2E.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
- package/dist/{chunk-VG5JEIUQ.mjs → chunk-TKWEX4FD.mjs} +2 -2
- package/dist/{chunk-CCSGFCLZ.mjs → chunk-U3SSPL4K.mjs} +1 -1
- package/dist/{chunk-DHB7UZ7F.mjs → chunk-U5XL7D6A.mjs} +1 -1
- package/dist/{chunk-CPS4SRPF.mjs → chunk-ULXSPX5H.mjs} +2 -2
- package/dist/{chunk-JXLSHVJ2.mjs → chunk-UMHUSOPJ.mjs} +1 -1
- package/dist/chunk-USIHBCSW.mjs +15 -0
- package/dist/{chunk-GWOYMENX.mjs → chunk-V6XZ4VIR.mjs} +1 -1
- package/dist/{chunk-AFGUR7SY.mjs → chunk-VPMRP2TL.mjs} +1 -1
- package/dist/{chunk-WTEGUPFG.mjs → chunk-VRPVVMRB.mjs} +1 -1
- package/dist/chunk-VZ2RQ3AM.mjs +2 -0
- package/dist/{chunk-XKOHV4H6.mjs → chunk-W5NPH5UA.mjs} +1 -1
- package/dist/chunk-WFGPMCTZ.mjs +4 -0
- package/dist/chunk-XZCFPKGO.mjs +2 -0
- package/dist/{chunk-QJ3TFJLQ.mjs → chunk-Y34NR5JL.mjs} +1 -1
- package/dist/{chunk-V5JRSGZF.mjs → chunk-YN37ZDLW.mjs} +1 -1
- package/dist/chunk-ZFGMJVJC.mjs +2 -0
- package/dist/{chunk-KH24LWAT.mjs → chunk-ZTHICSSF.mjs} +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/server.mjs +1 -1
- package/dist/worker.mjs +1 -1
- package/kit/math.js +1 -1
- package/kit/runtime.js +12 -2
- package/kit/state.js +2 -0
- package/kit/ui.js +5 -1
- package/package.json +1 -1
- package/release.json +7 -7
- package/skill/SKILL.md +13 -13
- package/skill/references/building-blocks.md +1 -1
- package/skill/references/extensions.md +45 -9
- package/web/assets/{index-xP0uw3a0.css → index-FLb9jCfl.css} +1 -1
- package/web/assets/index-czjrXVjf.js +15 -0
- package/web/index.html +2 -2
- package/dist/chunk-46ACXOU5.mjs +0 -2
- package/dist/chunk-5DPABPBI.mjs +0 -2
- package/dist/chunk-5L26JUYA.mjs +0 -2
- package/dist/chunk-AEMVZYZQ.mjs +0 -166
- package/dist/chunk-ASAXY2DJ.mjs +0 -2
- package/dist/chunk-DAMVDXR3.mjs +0 -2
- package/dist/chunk-DLFZJJEE.mjs +0 -2
- package/dist/chunk-EKNHM3P4.mjs +0 -2
- package/dist/chunk-FCK6NXON.mjs +0 -3
- package/dist/chunk-FX42XU6J.mjs +0 -2
- package/dist/chunk-HATA2W7X.mjs +0 -2
- package/dist/chunk-JJNRIFND.mjs +0 -8
- package/dist/chunk-JL6R4FG4.mjs +0 -4
- package/dist/chunk-KUILYI7H.mjs +0 -4
- package/dist/chunk-NIW7F7LU.mjs +0 -4
- package/dist/chunk-OU3S466R.mjs +0 -20
- package/dist/chunk-QCEATQC4.mjs +0 -2
- package/dist/chunk-TDWJKOLN.mjs +0 -2
- package/dist/chunk-UGO5N3EI.mjs +0 -15
- package/dist/chunk-XMKHCUI6.mjs +0 -15
- package/dist/chunk-XQRUG4G3.mjs +0 -2
- package/dist/chunk-YPPJVDQI.mjs +0 -2
- package/dist/chunk-ZANMKAWS.mjs +0 -2
- package/web/assets/index-BHC5ZOMh.js +0 -15
- /package/dist/{chunk-N3DSBH5S.mjs → chunk-A55HN5OK.mjs} +0 -0
- /package/dist/{chunk-KNZJT4ET.mjs → chunk-QAIHRH3I.mjs} +0 -0
- /package/dist/{chunk-ZW2A6GBW.mjs → chunk-QR6A54KJ.mjs} +0 -0
- /package/dist/{chunk-74UW7QSL.mjs → chunk-UH4SIXTR.mjs} +0 -0
- /package/dist/{chunk-EKGYYOSP.mjs → chunk-W7XHE4CT.mjs} +0 -0
- /package/dist/{chunk-5MMN3N5J.mjs → chunk-YEY3RSFQ.mjs} +0 -0
|
@@ -5,18 +5,11 @@ import net from "node:net";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { packageRoot } from "./paths.mjs";
|
|
7
7
|
import { compareVersions } from "./versions.mjs";
|
|
8
|
-
import {
|
|
8
|
+
import { defaultWorkspace } from "./workspace.mjs";
|
|
9
|
+
import { serviceLocation } from "./service-location.mjs";
|
|
9
10
|
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// export hands a document in through the server's edit service (POST /__gallery/edit/hand-in), in the
|
|
13
|
-
// queue its pages' commits run in, instead of writing the file beside them; gallery open, new and
|
|
14
|
-
// import give a document's address on it, or start one when none runs (src/cli/server-process.mjs).
|
|
15
|
-
// The server keeps a small file in its workspace while it listens, .gallery/server.json (one server
|
|
16
|
-
// a workspace), with its port, its process, the token its requests need, its kind and Gallery's
|
|
17
|
-
// version; a command finds it there, or does without it when there is none, or its process is gone.
|
|
18
|
-
// A port is never asked whose it is: 4317 may be another workspace's server, whose pages would write
|
|
19
|
-
// that workspace's files.
|
|
11
|
+
// One service per data directory: commands verify the announcement before reusing its port.
|
|
12
|
+
// Documents in every registered folder use the same service and retain owner-qualified paths.
|
|
20
13
|
|
|
21
14
|
// A directory as one name, however it was reached: on Windows a drive letter and the folders' names
|
|
22
15
|
// may be typed in either case (`cd /d d:\…` in cmd gives d:\), and they name the same directory.
|
|
@@ -24,10 +17,10 @@ const canonical = (directory) => {
|
|
|
24
17
|
const resolved = path.resolve(directory);
|
|
25
18
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
26
19
|
};
|
|
27
|
-
/** Whether two paths name the same directory (a
|
|
20
|
+
/** Whether two paths name the same directory (a service home or data directory). */
|
|
28
21
|
export const sameDirectory = (a, b) => canonical(a) === canonical(b);
|
|
29
22
|
|
|
30
|
-
const fileOf = (
|
|
23
|
+
const fileOf = () => serviceLocation().serverFile;
|
|
31
24
|
/** Gallery's version, as its package says it (a server of another version is another Gallery's). */
|
|
32
25
|
export const galleryVersion = (() => {
|
|
33
26
|
try {
|
|
@@ -47,30 +40,33 @@ const runs = (pid) => {
|
|
|
47
40
|
}
|
|
48
41
|
};
|
|
49
42
|
|
|
50
|
-
/**
|
|
51
|
-
*
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
* beside (which would take the announcement: the pages' commits in one queue, an export's hand-in in
|
|
55
|
-
* another). Every `every` ms (0: never) it is written again when its file is gone (taken away by
|
|
56
|
-
* hand, or by a server of these reports that closed as this one wrote it) or names a process that is
|
|
57
|
-
* gone (left behind). One that names another process that runs is left: a server of these reports
|
|
58
|
-
* started later (the last to start is the one the commands use), or the one Vite started again in
|
|
59
|
-
* this process. The .gallery/ folder is made again, never the workspace (a workspace deleted stays
|
|
60
|
-
* deleted). Resolves to a function that stops that and, only while the announcement is this one's
|
|
61
|
-
* (`nonce`), gives the file back to the server it was written over when that one's process runs (in
|
|
62
|
-
* another process: in this one it is Vite's earlier server), or else takes it away: for when the
|
|
63
|
-
* server closes. A server stopped without closing leaves it behind, which the commands find out (its
|
|
64
|
-
* process gone, its port answering no more).
|
|
65
|
-
*/
|
|
66
|
-
export async function announcePreviewServer({ reportsDirectory, port, token, kind = "dev", version = galleryVersion, every = 1000 }) {
|
|
67
|
-
const file = fileOf(reportsDirectory);
|
|
43
|
+
/** Acquire the data-directory lifetime lease and publish the sole service identity.
|
|
44
|
+
* The returned closer removes only this instance’s announcement and lease. */
|
|
45
|
+
export async function announcePreviewServer({ home = defaultWorkspace(), port, token, kind = "dev", version = galleryVersion, every = 1000 }) {
|
|
46
|
+
const file = fileOf();
|
|
68
47
|
const nonce = randomBytes(8).toString("hex");
|
|
69
|
-
const text = `${JSON.stringify({ port, pid: process.pid,
|
|
48
|
+
const text = `${JSON.stringify({ port, pid: process.pid, home: path.resolve(home), token, nonce, kind, version, started: new Date().toISOString() })}\n`;
|
|
70
49
|
const save = (content) => fs.writeFile(file, content, { mode: 0o600 });
|
|
71
50
|
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
72
|
-
const
|
|
73
|
-
|
|
51
|
+
const lease = path.join(path.dirname(file), "server.lock");
|
|
52
|
+
for (let attempt = 0; ; attempt++) {
|
|
53
|
+
try { await fs.writeFile(lease, text, { flag: "wx", mode: 0o600 }); break; }
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error.code !== "EEXIST" || attempt > 3) throw error;
|
|
56
|
+
const owner = await fs.readFile(lease, "utf8").then(JSON.parse).catch(() => null);
|
|
57
|
+
if (!owner) throw new Error("Gallery is starting; retry shortly.");
|
|
58
|
+
if (runs(owner.pid)) {
|
|
59
|
+
if (owner.pid === process.pid && owner.port === port) { await fs.writeFile(lease, text); break; }
|
|
60
|
+
throw new Error(`Gallery already runs on port ${owner.port} (pid ${owner.pid}).`);
|
|
61
|
+
}
|
|
62
|
+
await fs.rm(lease, { force: true });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
try { await save(text); } catch (error) {
|
|
66
|
+
const owner = await fs.readFile(lease, "utf8").then(JSON.parse).catch(() => null);
|
|
67
|
+
if (owner?.nonce === nonce) await fs.rm(lease, { force: true });
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
74
70
|
let closed = false;
|
|
75
71
|
const look = async () => {
|
|
76
72
|
let there = null;
|
|
@@ -91,29 +87,22 @@ export async function announcePreviewServer({ reportsDirectory, port, token, kin
|
|
|
91
87
|
closed = true;
|
|
92
88
|
clearInterval(keeping);
|
|
93
89
|
await looking;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
let earlier = null;
|
|
100
|
-
try {
|
|
101
|
-
earlier = JSON.parse(before);
|
|
102
|
-
} catch {}
|
|
103
|
-
const back = Number.isInteger(earlier?.pid) && earlier.pid !== process.pid && runs(earlier.pid);
|
|
104
|
-
await (back ? save(before) : fs.rm(file, { force: true })).catch(() => {});
|
|
90
|
+
const announcement = await fs.readFile(file, "utf8").then(JSON.parse).catch(() => null);
|
|
91
|
+
if (announcement?.nonce === nonce) await fs.rm(file, { force: true }).catch(() => {});
|
|
92
|
+
const owner = await fs.readFile(lease, "utf8").then(JSON.parse).catch(() => null);
|
|
93
|
+
if (owner?.nonce === nonce) await fs.rm(lease, { force: true }).catch(() => {});
|
|
105
94
|
};
|
|
106
95
|
}
|
|
107
96
|
|
|
108
|
-
// The
|
|
109
|
-
export async function readAnnouncement(
|
|
97
|
+
// The data-directory service announcement, while its process runs.
|
|
98
|
+
export async function readAnnouncement() {
|
|
110
99
|
let found;
|
|
111
100
|
try {
|
|
112
|
-
found = JSON.parse(await fs.readFile(fileOf(
|
|
101
|
+
found = JSON.parse(await fs.readFile(fileOf(), "utf8"));
|
|
113
102
|
} catch {
|
|
114
103
|
return null;
|
|
115
104
|
}
|
|
116
|
-
if (!Number.isInteger(found?.port) || !Number.isInteger(found.pid) || typeof found.token !== "string" || typeof found.
|
|
105
|
+
if (!Number.isInteger(found?.port) || !Number.isInteger(found.pid) || typeof found.token !== "string" || typeof found.home !== "string") return null;
|
|
117
106
|
return runs(found.pid) ? found : null;
|
|
118
107
|
}
|
|
119
108
|
|
|
@@ -122,17 +111,16 @@ export const processRuns = runs;
|
|
|
122
111
|
|
|
123
112
|
/**
|
|
124
113
|
* How the server an announcement names answers on its port, within `timeout` ms: "yes", as that
|
|
125
|
-
* server (GET /__gallery/workspace/info there names the announcement's token, its process and
|
|
126
|
-
* workspace, docs/workbench.md; there itself, not where it sends the request on to); "no", as none (a
|
|
114
|
+
* server (GET /__gallery/workspace/info there names the announcement's token, its process, home and data directory, docs/workbench.md; there itself, not where it sends the request on to); "no", as none (a
|
|
127
115
|
* server stopped without closing leaves its announcement behind, and by now its process's number may
|
|
128
116
|
* be another program's, its port another server's); or "busy": the port took the request and gave no
|
|
129
117
|
* answer in time.
|
|
130
118
|
*/
|
|
131
|
-
export async function answerOf(found,
|
|
119
|
+
export async function answerOf(found, timeout = 10_000) {
|
|
132
120
|
try {
|
|
133
121
|
const response = await fetch(`http://127.0.0.1:${found.port}/__gallery/workspace/info`, { redirect: "error", signal: AbortSignal.timeout(timeout) });
|
|
134
122
|
const info = response.ok ? await response.json() : null;
|
|
135
|
-
const same = info?.token === found.token && info.server?.pid === found.pid && typeof info.server.
|
|
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);
|
|
136
124
|
return same ? "yes" : "no";
|
|
137
125
|
} catch (error) {
|
|
138
126
|
return error?.name === "TimeoutError" ? "busy" : "no";
|
|
@@ -140,15 +128,15 @@ export async function answerOf(found, reportsDirectory, timeout) {
|
|
|
140
128
|
}
|
|
141
129
|
|
|
142
130
|
/**
|
|
143
|
-
* The running server
|
|
131
|
+
* The running server for this data directory: { port, token, pid }, or null when none says it runs. With
|
|
144
132
|
* `answering`, only one that answers on its port as the server its announcement names, within
|
|
145
133
|
* `timeout` ms (gallery open, which gives its address to the user); gallery export asks without it,
|
|
146
134
|
* its hand-in's own request telling whether the server takes it (a server slow to answer may still
|
|
147
135
|
* write the file).
|
|
148
136
|
*/
|
|
149
|
-
export async function findPreviewServer({
|
|
150
|
-
const found = await readAnnouncement(
|
|
151
|
-
if (!found || (answering &&
|
|
137
|
+
export async function findPreviewServer({ answering = false, timeout = 10_000 } = {}) {
|
|
138
|
+
const found = await readAnnouncement();
|
|
139
|
+
if (!found || (answering && await answerOf(found, timeout) !== "yes")) return null;
|
|
152
140
|
return { port: found.port, token: found.token, pid: found.pid };
|
|
153
141
|
}
|
|
154
142
|
|
|
@@ -199,7 +187,7 @@ export async function stopServer(found, { wait = 10_000 } = {}) {
|
|
|
199
187
|
status = response.status;
|
|
200
188
|
await response.text().catch(() => "");
|
|
201
189
|
} catch {} // it closed as it answered, or did not answer: its process says
|
|
202
|
-
if (status === 409) return { ok: false, reason: "dev", message: `
|
|
190
|
+
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.` };
|
|
203
191
|
if (await goneWithin(found.pid, wait)) return { ok: true, killed: false };
|
|
204
192
|
try {
|
|
205
193
|
process.kill(found.pid);
|
|
@@ -209,7 +197,7 @@ export async function stopServer(found, { wait = 10_000 } = {}) {
|
|
|
209
197
|
}
|
|
210
198
|
|
|
211
199
|
/**
|
|
212
|
-
*
|
|
200
|
+
* The shared service, for a command that needs one (src/cli/server-process.mjs serverFor): the
|
|
213
201
|
* one its announcement names when it answers there within `timeout` ms (as findPreviewServer with
|
|
214
202
|
* `answering`), or else one `start(port)` starts on the first free port of `ports`. A server of
|
|
215
203
|
* Gallery's of an older `version` (a command's, kind "cli": never npm run dev's) is stopped first
|
|
@@ -218,12 +206,12 @@ export async function stopServer(found, { wait = 10_000 } = {}) {
|
|
|
218
206
|
* pid, kind, version } of that one, replaced: { pid, version } of the one stopped, or null, passed:
|
|
219
207
|
* the ports passed over, as "4317 (in use)" }; `port` is null when none of `ports` is free; when the
|
|
220
208
|
* announced server's port took the request and gave no answer in time, with `busy: { port, pid,
|
|
221
|
-
* version, newer }`:
|
|
209
|
+
* version, newer }`: the shared Gallery server, busy or stuck, and none is started beside it; and when
|
|
222
210
|
* an older one would not stop, with `stuck: { port, pid, message }`.
|
|
223
211
|
*/
|
|
224
|
-
export async function previewServerFor({
|
|
225
|
-
const found = await readAnnouncement(
|
|
226
|
-
const answer = found ? await answerOf(found,
|
|
212
|
+
export async function previewServerFor({ ports, start, stop = stopServer, timeout = 10_000, version = galleryVersion }) {
|
|
213
|
+
const found = await readAnnouncement();
|
|
214
|
+
const answer = found ? await answerOf(found, timeout) : "no";
|
|
227
215
|
const kind = found?.kind === "dev" ? "dev" : "cli";
|
|
228
216
|
const order = found ? compareVersions(found.version, version) : 0;
|
|
229
217
|
if (answer === "busy") return { port: null, started: null, passed: [], replaced: null, busy: { port: found.port, pid: found.pid, version: found.version ?? null, newer: kind === "cli" && order > 0 } };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { userDirectory } from "./user-directory.mjs";
|
|
3
|
+
import { defaultWorkspace } from "./workspace.mjs";
|
|
4
|
+
import { workspaceId, workspaceKey } from "./workspace-registry.mjs";
|
|
5
|
+
import { projectBase } from "./workspace-address.mjs";
|
|
6
|
+
|
|
7
|
+
export const workspaceBase = root => workspaceKey(root) === workspaceKey(defaultWorkspace()) ? "" : projectBase(workspaceId(root));
|
|
8
|
+
|
|
9
|
+
// A service belongs to a Gallery data directory, never to a document storage directory.
|
|
10
|
+
export function serviceLocation() {
|
|
11
|
+
const data = userDirectory("data");
|
|
12
|
+
return {
|
|
13
|
+
data,
|
|
14
|
+
serverFile: path.join(data, "server.json"),
|
|
15
|
+
lastPortFile: path.join(data, "last-port"),
|
|
16
|
+
serverLog: path.join(data, "logs", "server.log"),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import { documentKey } from "./workspace-address.mjs";
|
|
1
2
|
// The session between a report page and a workbench that shows it in an iframe of the same origin
|
|
2
3
|
// (docs/workbench.md): the page's side is src/editor/page/session.mjs, the workbench's
|
|
3
4
|
// src/workbench/app/session/. Every message is { protocol: PROTOCOL, v: VERSION, type, slug, ... },
|
|
4
|
-
// posted to the other window for this origin only
|
|
5
|
-
// page and a workbench of another origin each side's server vouched for (the page's ?shell=<origin>,
|
|
6
|
-
// GET /__gallery/workspace/shell; the workbench's POST elsewhere: src/workbench/elsewhere.mjs).
|
|
5
|
+
// posted to the other window for this origin only. Every document uses this service.
|
|
7
6
|
//
|
|
8
7
|
// report -> workbench ready once the editor started: { commands, title, icon, look, state, annotations, card }
|
|
9
8
|
// state when the state, the title, the page's icon, the look it shows, how many 批注 it
|
|
@@ -39,8 +38,6 @@
|
|
|
39
38
|
// annotations-removed
|
|
40
39
|
// a 批注 deleted from its card: { items }: the workbench says so, as
|
|
41
40
|
// it does for 全部删除, and its 撤销 puts them back
|
|
42
|
-
// bye the page goes (pagehide), to a workbench of another origin alone, which
|
|
43
|
-
// cannot see it go: its session ends there
|
|
44
41
|
// workbench -> report command { id, name, ... }
|
|
45
42
|
|
|
46
43
|
export const PROTOCOL = "gallery/session";
|
|
@@ -63,7 +60,7 @@ export const VERSION = 2;
|
|
|
63
60
|
* back from another document keeps): sessionStorage, which a workbench and its frames share. A page
|
|
64
61
|
* puts it there before it is shown again; the workbench before the way back comes to it.
|
|
65
62
|
*/
|
|
66
|
-
export const viewKey = (slug) => `gallery-edit:${slug}:view`;
|
|
63
|
+
export const viewKey = (slug) => `gallery-edit:${documentKey(slug)}:view`;
|
|
67
64
|
|
|
68
65
|
/**
|
|
69
66
|
* What `state` reports, in this order: editing (the editor runs: the page edits from the start),
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// All projects share one origin. Only document resources and operations carry an owner;
|
|
2
|
+
// package assets and the workbench itself stay at the root of that origin.
|
|
3
|
+
export const projectBase = (id) => `/~${id}`;
|
|
4
|
+
export function projectItem(value) {
|
|
5
|
+
const match = /^~(ws-[0-9a-f]{6})\/([^/]+)$/.exec(String(value ?? ""));
|
|
6
|
+
return match ? { ws: match[1], slug: match[2], base: projectBase(match[1]) } : null;
|
|
7
|
+
}
|
|
8
|
+
const PROJECT = /^(\/~(ws-[0-9a-f]{6}))(?=\/|$)/;
|
|
9
|
+
export const projectOfPath = (pathname) => PROJECT.exec(pathname)?.[2] ?? null;
|
|
10
|
+
export const localPath = (pathname) => pathname.replace(PROJECT, "") || "/";
|
|
11
|
+
export const currentProjectBase = () => PROJECT.exec(globalThis.location?.pathname ?? "")?.[1] ?? "";
|
|
12
|
+
export function workspaceAddress(address, base = currentProjectBase()) {
|
|
13
|
+
if (!base || typeof address !== "string" || projectOfPath(address)) return address;
|
|
14
|
+
return /^\/(?:__gallery\/|generated\/|pages\/)/.test(address) ? base + address : address;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Browser state is shared by origin, so a document's owner is part of its identity. */
|
|
18
|
+
export const documentKey = (slug, base = currentProjectBase()) => base && typeof slug === "string" && slug && !slug.startsWith("~") ? base.slice(1) + "/" + slug : slug;
|
|
19
|
+
export const workspaceStoragePrefix = (base = currentProjectBase()) => base ? "gallery-project:" + base.slice(1) + ":" : "";
|
|
@@ -18,8 +18,7 @@ import { defaultWorkspace, documentFiles, isSlugName, readLink } from "./workspa
|
|
|
18
18
|
// says (title, description, an icon of the emoji or 图标 kind: no picture of another workspace's folder). It is
|
|
19
19
|
// read from its files, never written, and kept SNAPSHOT_KEPT ms at most, so that the reads of a burst (a
|
|
20
20
|
// command and the workbench's read after it, the list of 添加其他 Gallery 项目…) read each folder once while a
|
|
21
|
-
// change there shows at the next read after that; the
|
|
22
|
-
// server tells of a change (src/workbench/service.mjs, through ./elsewhere.mjs). Reads files only.
|
|
21
|
+
// change there shows at the next read after that; the cache is dropped when the storage runtime reports a change in the same process (src/server/projects.mjs). Reads files only.
|
|
23
22
|
|
|
24
23
|
/** How long a snapshot of another workspace is believed (ms). */
|
|
25
24
|
export const SNAPSHOT_KEPT = 2000;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { readFileSync
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
|
-
import os from "node:os";
|
|
5
4
|
import path from "node:path";
|
|
6
5
|
import { writeAtomic } from "./files.mjs";
|
|
7
6
|
import { userDirectory } from "./user-directory.mjs";
|
|
@@ -16,9 +15,8 @@ import { layoutOf, markerOf } from "./workspace.mjs";
|
|
|
16
15
|
// { "version": 1, "workspaces": [{ "root": "<absolute path>", "seen": "<ISO time>" }] }
|
|
17
16
|
// A workspace is registered once a gallery command has found it (src/cli/gallery.mjs: every command
|
|
18
17
|
// but stop and skill; init and workspaces --name once they made it) and when its server starts
|
|
19
|
-
// (src/server/routes.mjs listening), `seen` written again an hour later at the soonest
|
|
20
|
-
//
|
|
21
|
-
// not Gallery's. A path is one entry however it is spelt (upper and lower case alike on Windows). A
|
|
18
|
+
// (src/server/routes.mjs listening), `seen` written again an hour later at the soonest. Only folders
|
|
19
|
+
// with Gallery’s own gallery.json are registered; tests isolate their data and default home. A path is one entry however it is spelt (upper and lower case alike on Windows). A
|
|
22
20
|
// change is made under the registry's lock (workspaces.json.lock), on the file as it is then, and
|
|
23
21
|
// written whole to a file beside it renamed over it, so that two commands registering at once each
|
|
24
22
|
// keep the other's (updateRegistry). A workspace whose folder
|
|
@@ -131,36 +129,6 @@ export async function updateRegistry(change, { file = registryFile() } = {}) {
|
|
|
131
129
|
});
|
|
132
130
|
}
|
|
133
131
|
|
|
134
|
-
// The system's temporary folders, each as its path is written and as it really is (macOS's /var is
|
|
135
|
-
// /private/var, its /tmp /private/tmp): os.tmpdir()'s (`tmp`), and /tmp but on Windows, for macOS's
|
|
136
|
-
// os.tmpdir() is a folder of the user's under /var/folders while Agents' trials go to /tmp, which a
|
|
137
|
-
// restart clears.
|
|
138
|
-
function temporaryFolders(tmp, platform) {
|
|
139
|
-
const folders = [];
|
|
140
|
-
for (const folder of [tmp, ...(platform === "win32" ? [] : ["/tmp"])]) {
|
|
141
|
-
folders.push(path.resolve(folder));
|
|
142
|
-
try {
|
|
143
|
-
folders.push(realpathSync.native(folder));
|
|
144
|
-
} catch {}
|
|
145
|
-
}
|
|
146
|
-
return folders;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/** Whether `root` is in one of the system's temporary folders (`tmp`, os.tmpdir()'s, and /tmp but on Windows), or is one. */
|
|
150
|
-
export function isTemporary(root, { tmp = os.tmpdir(), platform = process.platform } = {}) {
|
|
151
|
-
const roots = [path.resolve(root)];
|
|
152
|
-
try {
|
|
153
|
-
roots.push(realpathSync.native(root));
|
|
154
|
-
} catch {}
|
|
155
|
-
return temporaryFolders(tmp, platform).some((folder) => {
|
|
156
|
-
const base = workspaceKey(folder, platform);
|
|
157
|
-
return roots.some((each) => {
|
|
158
|
-
const key = workspaceKey(each, platform);
|
|
159
|
-
return key === base || key.startsWith(base.endsWith(path.sep) ? base : `${base}${path.sep}`);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
|
|
164
132
|
/** Whether the folder `root` is a workspace now: its gallery.json there, and Gallery's. */
|
|
165
133
|
export function isWorkspace(root) {
|
|
166
134
|
try {
|
|
@@ -172,13 +140,11 @@ export function isWorkspace(root) {
|
|
|
172
140
|
|
|
173
141
|
/**
|
|
174
142
|
* Register the workspace at `root`: added with `seen` now, or its `seen` written again when it is
|
|
175
|
-
* SEEN_EVERY old (`force`: whatever its age). Nothing is written for a folder
|
|
176
|
-
*
|
|
177
|
-
* `seen` recent: nothing written), "temporary" or "none" (no workspace there).
|
|
143
|
+
* SEEN_EVERY old (`force`: whatever its age). Nothing is written for a folder that is no workspace
|
|
144
|
+
* of Gallery’s. Resolves to "added", "seen", "fresh" (`seen` recent: nothing written), or "none".
|
|
178
145
|
*/
|
|
179
|
-
export async function registerWorkspace(root, { file = registryFile(), now = Date.now(),
|
|
146
|
+
export async function registerWorkspace(root, { file = registryFile(), now = Date.now(), force = false } = {}) {
|
|
180
147
|
const resolved = path.resolve(root);
|
|
181
|
-
if (isTemporary(resolved, { tmp })) return "temporary";
|
|
182
148
|
if (!isWorkspace(resolved)) return "none";
|
|
183
149
|
const key = workspaceKey(resolved);
|
|
184
150
|
const recent = (entry) => {
|
|
@@ -256,9 +256,6 @@ export function layoutOf(root) {
|
|
|
256
256
|
backups: path.join(gallery, "backups"),
|
|
257
257
|
built: path.join(gallery, "built"),
|
|
258
258
|
logs: path.join(gallery, "logs"),
|
|
259
|
-
serverLog: path.join(gallery, "logs", "server.log"),
|
|
260
|
-
serverFile: path.join(gallery, "server.json"),
|
|
261
|
-
lastPortFile: path.join(gallery, "last-port"),
|
|
262
259
|
migrated: path.join(gallery, "migrated.json"),
|
|
263
260
|
};
|
|
264
261
|
}
|
|
@@ -270,7 +267,7 @@ export function layoutOf(root) {
|
|
|
270
267
|
* gallery.json was written now, ours: whether the gallery.json there is ours, reads: whether it reads
|
|
271
268
|
* at all (markerOf) }.
|
|
272
269
|
*/
|
|
273
|
-
export async function ensureWorkspace(root) {
|
|
270
|
+
export async function ensureWorkspace(root, { state = true } = {}) {
|
|
274
271
|
const layout = layoutOf(root);
|
|
275
272
|
await fs.mkdir(layout.root, { recursive: true });
|
|
276
273
|
let made = true;
|
|
@@ -281,8 +278,8 @@ export async function ensureWorkspace(root) {
|
|
|
281
278
|
made = false;
|
|
282
279
|
}
|
|
283
280
|
const hidden = !existsSync(layout.gallery);
|
|
284
|
-
await fs.mkdir(layout.state, { recursive: true, mode: 0o700 });
|
|
285
|
-
if (hidden && process.platform === "win32") execFile("attrib", ["+h", layout.gallery], { windowsHide: true }, () => {}); // once; a failure leaves it shown
|
|
281
|
+
if (state) await fs.mkdir(layout.state, { recursive: true, mode: 0o700 });
|
|
282
|
+
if (state && hidden && process.platform === "win32") execFile("attrib", ["+h", layout.gallery], { windowsHide: true }, () => {}); // once; a failure leaves it shown
|
|
286
283
|
const { ours, reads } = markerAt(layout.marker);
|
|
287
284
|
return { made, ours, reads };
|
|
288
285
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { defaultWorkspace } from "./workspace.mjs";
|
|
4
|
+
import { defaultPreferencesFile } from "./paths.mjs";
|
|
4
5
|
|
|
5
6
|
// 写作偏好: the user's rules for writing a report, which an Agent reads before it writes (SKILL.md),
|
|
6
7
|
// at <workspace>/preferences.md (shared/workspace.mjs). Gallery ships its default (writing-preferences.md,
|
|
@@ -15,15 +16,16 @@ import { defaultPreferencesFile, reportsDirectory as defaultReportsDirectory } f
|
|
|
15
16
|
export { defaultPreferencesFile };
|
|
16
17
|
|
|
17
18
|
/** Where the user's writing preferences are, for the documents of workspace `reportsDirectory`. */
|
|
18
|
-
export const preferencesFileOf = (
|
|
19
|
+
export const preferencesFileOf = () => path.join(defaultWorkspace(), "preferences.md");
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* The user's writing preferences: { file, text, made }, the file made from Gallery's default
|
|
22
23
|
* first when there is none (`made`), never over one that exists (another reader making it at the
|
|
23
24
|
* same moment: its file is read).
|
|
24
25
|
*/
|
|
25
|
-
export async function readWritingPreferences(
|
|
26
|
-
const
|
|
26
|
+
export async function readWritingPreferences() {
|
|
27
|
+
const reportsDirectory = defaultWorkspace();
|
|
28
|
+
const file = preferencesFileOf();
|
|
27
29
|
const existing = await fs.readFile(file, "utf8").catch((error) => {
|
|
28
30
|
if (error.code === "ENOENT") return null;
|
|
29
31
|
throw error;
|
|
@@ -1,6 +1,6 @@
|
|
|
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 re,J as ae,K as ne,O as ie,P as ce,a as C,b as Q,c as F,g as X,h as Z,n as D,o as ee,r as te,s as se,t as oe}from"./chunk-
|
|
2
|
+
import{E as re,J as ae,K as ne,O as ie,P as ce,a as C,b as Q,c as F,g as X,h as Z,n as D,o as ee,r as te,s as se,t as oe}from"./chunk-Q7P3Q5YX.mjs";import{j as K}from"./chunk-4KUO7R2M.mjs";import{d as le}from"./chunk-C257H6IA.mjs";import{i as q}from"./chunk-BN7M3IN7.mjs";import{a as L}from"./chunk-JEUK3ZO7.mjs";import{a as M,b as z,g as J}from"./chunk-PUOQ65NC.mjs";import{b as x,c as T}from"./chunk-4B2TB5K2.mjs";import{b as E}from"./chunk-4OZWXZE5.mjs";import{f as H,j as Y}from"./chunk-TKWEX4FD.mjs";import{b as V,e as pe}from"./chunk-A55HN5OK.mjs";pe();import N from"node:fs/promises";import P from"node:path";import{isInstance as ge}from"../browser/shared/components/index.mjs";import{ATTRIBUTE as me}from"../browser/shared/components/installed.mjs";import{attr as we,walk as $e}from"../browser/shared/html-tree.mjs";import{pageExists as ye,pageFiles as be}from"../browser/shared/pages.mjs";import{layoutOf as Ee}from"../browser/shared/workspace.mjs";var xe="card ids aside",ke="nothing else in page.html changed (card ids aside)",Se=e=>new Promise(s=>setTimeout(s,e)),Te=/^\/__gallery\/pages\/[a-z0-9-]+\/cards$/,de=e=>{let s=null;return $e(V(e,{sourceCodeLocationInfo:!0}),n=>{s??=ge(n)?n:null},{inTemplates:!1}),s},fe=(e,s)=>{let n=s.node.sourceCodeLocation.startTag.endOffset;return{frame:e.slice(s.start,n).replace(/\s+data-[xywh]="[^"]*"/g,""),rest:e.slice(n,s.end)}};function Ce(e,s,n,a){let[o,d]=[x(e).cards,x(s).cards],c=[],t=r=>`card ${r.id}${r.block?` (${r.block})`:""}`,l=r=>["x","y","w","h"].map(i=>`${i} ${we(r.node,`data-${i}`)??"-"}`).join(", ");for(let r of o){let i=d.find(g=>g.id===r.id);if(!i){c.push(`${t(r)} taken out`);continue}let m=r.board!==i.board;(m||l(r)!==l(i))&&c.push(`${t(r)}'s place, ${l(r)}${m?` on ${r.board}`:""} then ${l(i)}${m?` on ${i.board}`:""}`);let[h,p]=[fe(e,r),fe(s,i)];if(h.frame===p.frame&&h.rest===p.rest)continue;if(r.id!==n){c.push(t(r));continue}h.frame!==p.frame&&c.push(`${t(r)}'s own frame (an attribute of its <section> but its place)`);let[f,$]=[D(h.rest,de,a),D(p.rest,de,a)];!f||!$?c.push(`${t(r)}'s instance`):f.outside!==$.outside&&c.push(`${t(r)} itself, outside its data parts (a part edited as text or none, or what its instance holds besides its parts)`)}for(let r of d)o.some(i=>i.id===r.id)||c.push(`a ${t(r)} put in`);let w=(r,i)=>{let m="",h=0;for(let p of[...i].sort((f,$)=>f.start-$.start))m+=r.slice(h,p.start),h=p.end;return(m+r.slice(h)).replace(/\s+/g," ")};return(w(e,o)!==w(s,d)||!c.length)&&c.push("the page's own HTML outside its cards (its head, its style, its header)"),c.join("; ")}function Ie(e,s,{card:n,found:a}){let[o,d]=[T(e).text,T(s).text],t=ee(o,d,l=>x(l).cards.find(w=>w.id===n)?.instance??null,a);return t.outside&&(t.outside={...t.outside,what:Ce(o,d,n,a)}),t}async function Oe(e){try{return await e.waitForSelector("[data-doc-ready]",{timeout:C}).catch(s=>{if(s.name==="TimeoutError")throw s;return e.waitForSelector("[data-doc-ready]",{timeout:C})}),await e.waitForFunction(()=>globalThis.__galleryTryEditable===!0&&!!document.querySelector("script[data-ge-page]"),{timeout:C}),!0}catch(s){if(s.name!=="TimeoutError")throw s;return!1}}function ve(e){return e?.startsWith("stopped")?`the page service refused the board's write (${e.slice(7).trim()||"no reason given"}), and the page showed itself again as the file holds it`:{refused:"the page service refused the board's write: the card was changed or taken out meanwhile (someone else's write of page.html), and the page showed itself again as the file holds it",offline:"the page could not reach the server (offline)",reloading:"the page loaded itself again (page.html or a custom block it holds changed meanwhile, or the page service refused a write)",syncing:`its write took more than ${F/1e3}s to be answered`}[e]}function Ae(e){let s=[],n=new Map;return e.on("request",a=>{if(a.method()!=="POST"||!Te.test(new URL(a.url()).pathname))return;let o={done:!1,status:null,answer:null,failed:null};s.push(o),n.set(a,o)}),e.on("response",a=>{let o=n.get(a.request());o&&(o.status=a.status(),o.done=!0,o.status!==200&&a.json().then(d=>{o.answer=d},()=>{}))}),e.on("requestfailed",a=>{let o=n.get(a);o&&(o.failed=a.failure()?.errorText??"failed",o.done=!0)}),{mark:async()=>s.length,async until(a){let o=Date.now();for(let d=0;;await Se(100)){let c=s.slice(a);if(c.length===0){if(Date.now()-o>Q)return"unchanged";continue}if(c.some(l=>!l.done)){if(d=0,Date.now()-o>F)return"syncing";continue}if(++d<4)continue;if(c.some(l=>l.failed))return"offline";let t=c.find(l=>l.status!==200);return t?t.answer?.refused||t.status===409&&!t.answer?"refused":`stopped ${t.answer?.reason??`HTTP ${t.status}`}`:"saved"}}}}function Re({card:e,found:s}){let n={...s,parts:Object.fromEntries(Object.entries(s.parts).filter(([,a])=>a.edit==="data"))};return{where:{card:e},ready:Oe,saves:Ae,changes:(a,o)=>Ie(a,o,{card:e,found:n}),plain:()=>!1,stayed:ve,outside:"problem",outsideSaid:a=>`card ${e}'s data parts: ${a.what}, where a reader's gesture on the card is to change its data parts alone`,aside:xe,inside:ke,framed:a=>a.closest(".gallery-card")??a}}var Le=e=>` ${e.id} ${e.block??"(no instance)"} x ${e.x}, y ${e.y}, w ${e.w}, h ${e.h}${e.board?` on board ${e.board}`:""}`;async function Ke(e,{named:s,values:n,seconds:a={},keep:o,replace:d=!1}){if(s.length===0)throw new E("Name the page and the card: gallery page try <slug> <card id> and the gesture.");if(s.length===1)throw new E(`Name the card of ${s[0]} to try: gallery page try ${s[0]} <card id> and the gesture (its data-card-id; gallery page check ${s[0]} lists the cards).`);if(s.length>2)throw new E("gallery page try takes a page's slug and a card's id.");let c=await X(n,a,d),[t,l]=s,w=n.has("--steps")?P.resolve(n.get("--steps")):null,r=c?[c]:await Z(w),i=be(t,e);ye(t,e)||(console.error(await le(t,e)),process.exit(1));let m=await N.readFile(i.html,"utf8"),{cards:h}=x(T(m).text),p=h.find(u=>u.id===l);if(!p)throw new E(h.length?`${t} has no card ${l}: its cards are
|
|
3
3
|
${h.map(Le).join(`
|
|
4
|
-
`)}`:`${t} has no card yet: gallery page add ${t} <block> puts one on it.`);let f=P.basename(i.html),$=p.node.sourceCodeLocation?.startLine??null;p.instance||(console.error(`Card ${l} of ${t} (${f}:${$}) holds no instance of a custom block (an element with ${me}): there is nothing on it to try.`),process.exit(1));let g=p.block,_=H(e),k=_.packages.get(g);if(!k){let u=_.problems.get(g);console.error(u?.off?`${g} is turned off in \u8BBE\u7F6E \u203A \u7EC4\u4EF6\u5217\u8868: its card says so on the page and runs nothing.`:u?`${g} does not load (${Y("en",u.problem)}), so its card runs nothing: gallery block check ${g} says why.`:`No custom block ${g} in this workspace, so its card runs nothing: gallery block list says which there are${g.startsWith("gallery/")?`, and gallery block add ${g.slice(8)} puts one of Gallery's own in`:""}.`),process.exit(1)}k.places.includes("page")||(console.error(`${g} is a block of documents (its manifest's places has no "page"): on a custom page its card says so and runs nothing, so there is nothing to try there (gallery block try tries it in a document).`),process.exit(1)),console.log(`${g} in card ${l} of ${t}, at ${f}:${$}: ${L(i.html)}`),w&&console.log(`Steps: ${r.length} from ${L(w)}, made one after another in one page`);let S=await q({command:`gallery page try ${t}`});if(!S.ok){console.log(`UNVERIFIED ${t}: ${S.message}`),process.exitCode=3;return}let{pageAloneOf:j}=await import("./chunk-
|
|
4
|
+
`)}`:`${t} has no card yet: gallery page add ${t} <block> puts one on it.`);let f=P.basename(i.html),$=p.node.sourceCodeLocation?.startLine??null;p.instance||(console.error(`Card ${l} of ${t} (${f}:${$}) holds no instance of a custom block (an element with ${me}): there is nothing on it to try.`),process.exit(1));let g=p.block,_=H(e),k=_.packages.get(g);if(!k){let u=_.problems.get(g);console.error(u?.off?`${g} is turned off in \u8BBE\u7F6E \u203A \u7EC4\u4EF6\u5217\u8868: its card says so on the page and runs nothing.`:u?`${g} does not load (${Y("en",u.problem)}), so its card runs nothing: gallery block check ${g} says why.`:`No custom block ${g} in this workspace, so its card runs nothing: gallery block list says which there are${g.startsWith("gallery/")?`, and gallery block add ${g.slice(8)} puts one of Gallery's own in`:""}.`),process.exit(1)}k.places.includes("page")||(console.error(`${g} is a block of documents (its manifest's places has no "page"): on a custom page its card says so and runs nothing, so there is nothing to try there (gallery block try tries it in a document).`),process.exit(1)),console.log(`${g} in card ${l} of ${t}, at ${f}:${$}: ${L(i.html)}`),w&&console.log(`Steps: ${r.length} from ${L(w)}, made one after another in one page`);let S=await q({command:`gallery page try ${t}`});if(!S.ok){console.log(`UNVERIFIED ${t}: ${S.message}`),process.exitCode=3;return}let{pageAloneOf:j}=await import("./chunk-2T4PZQEP.mjs");await oe(e);let B=[],G=async u=>{let b=await te(e,{directory:i.folder,files:i,text:u});return B.push(b.slug),b},I=Re({card:l,found:k});try{let u=o?{slug:t,file:i.html}:await G(m),b=j(S.port,u.slug);console.log(o?`Page: ${b} (the custom page alone, editable: the Agent's own; --keep: what ${re(r)} write stays in ${f}, and every page open on it shows it, the user's too; the server runs on after this command)`:`Page: ${b} (a scratch copy of ${t}, its page alone, editable, made for this trial and gone at its end: ${f} is not written, and no page of the user's shows the trial; the server runs on after this command)`);let O=M();if(!O){console.log(`UNVERIFIED ${t}: no Chrome, Edge or Chromium found (GALLERY_BROWSER=<its executable> picks one), so nothing was tried`),process.exitCode=3;return}let v;try{v=await J({executablePath:O,args:z()})}catch(R){console.log(`UNVERIFIED ${t}: the browser ${O} did not start, twice, each time with a fresh profile (${K(R)}; GALLERY_BROWSER=<its executable> picks another), so nothing was tried`),process.exitCode=3;return}let A=P.join(Ee(e).qa,t,"try");try{await N.rm(A,{recursive:!0,force:!0}),await N.mkdir(A,{recursive:!0});let R=async y=>{if(o)return{address:b,file:u.file};let W=await G(y);return{address:j(S.port,W.slug),file:W.file}},U;try{U=await ae(v.browser,{root:e,address:b,fresh:R,steps:r,keep:o,file:u.file,name:f,line:$,found:k,out:A,host:I})}catch(y){if(y instanceof E&&y.made&&console.log(ce({name:f,steps:r,keep:o,host:I},y.made).join(`
|
|
5
5
|
`)),y.stuck)return ne(t,y,f,o);throw y}let{lines:ue,code:he}=ie({slug:t,name:f,steps:r,keep:o,host:I},U);console.log(ue.join(`
|
|
6
6
|
`)),process.exitCode=he}finally{await v.close()}}finally{for(let u of B)await se(e,u)}}export{xe as a,ke as b,de as c,Ce as d,Ie as e,Ae as f,Re as g,Ke as h};
|
|
@@ -1,4 +1,4 @@
|
|
|
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 D,f as x}from"./chunk-
|
|
2
|
+
import{e as D,f as x}from"./chunk-XZCFPKGO.mjs";import{a as d,b as u,c as w,d as y}from"./chunk-S3SX3COT.mjs";import{b as c,d as f,e as m,g as p,h as g,i as a,j as h}from"./chunk-VRPVVMRB.mjs";import{k as l}from"./chunk-PLB7L22J.mjs";import{a as s}from"./chunk-UMHUSOPJ.mjs";import S from"node:path";import{FILE as j}from"../browser/shared/annotations.mjs";import{editStateDirectory as L,reportsDirectory as i}from"../browser/shared/paths.mjs";import{documentDirectory as $,documentFiles as k}from"../browser/shared/workspace.mjs";function O({say:e=!0}={}){return o=>{let{text:t,changes:n}=c(o.html,{finish:l});if(e&&n.length&&console.log(p(o.htmlPath,n,{pending:!0}).join(`
|
|
3
3
|
`)),e){let r=f(o.html);r.length&&console.log(D(o.htmlPath,r,r.length,{pending:!0}).join(`
|
|
4
4
|
`));for(let N of x(o.htmlPath,m(s(o.html),o.metadata),{pending:!0}))console.log(N)}return t}}function b(e){for(let o of e){for(let t of g(o.html))console.log(`note ${o.slug}: ${t} (read-only in the preview)`);for(let t of a(o.html))console.log(`note ${o.slug}: ${t}`);for(let t of h(o.html))console.log(`note ${o.slug}: ${t}`)}}async function q(e){for(let o of e){let t=await w({stateDirectory:L,reportsDirectory:i,slug:o});for(let n of y(k($(o,i)).html,t))console.log(n)}}async function z(e){for(let o of e){let t=await d({stateDirectory:L,reportsDirectory:i,slug:o});for(let n of u(S.join($(o,i),j),t))console.log(n)}}export{O as a,b,q as c,z as d};
|
|
@@ -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,b,c,d,e,f,g,h}from"./chunk-C257H6IA.mjs";import"./chunk-3XZOA7AH.mjs";import"./chunk-BN7M3IN7.mjs";import"./chunk-JEUK3ZO7.mjs";import"./chunk-6QWQXHAV.mjs";import"./chunk-2OHANJ6Q.mjs";import"./chunk-7UZUQQ7Q.mjs";import"./chunk-4B2TB5K2.mjs";import"./chunk-KLQP46Y3.mjs";import"./chunk-4OZWXZE5.mjs";import"./chunk-3SKOU6OZ.mjs";import"./chunk-PLB7L22J.mjs";import"./chunk-HYMEEXKN.mjs";import"./chunk-E7656LR7.mjs";import"./chunk-TKWEX4FD.mjs";import"./chunk-A55HN5OK.mjs";import"./chunk-5L7RYZCL.mjs";export{c as HOME_NOT_MADE,g as emptyRequiredOf,d as missingPageSaid,a as pageAddressOf,b as pageAloneOf,h as run,e as setsOf,f as settingsGiven};
|
|
@@ -1,5 +1,5 @@
|
|
|
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{a as i,l as C}from"./chunk-
|
|
2
|
+
import{a as i,l as C}from"./chunk-PLB7L22J.mjs";import{a as g,c as w}from"./chunk-HYMEEXKN.mjs";import{g as j,o as A,p as S}from"./chunk-TKWEX4FD.mjs";import f from"node:path";import{attr as P,hasClass as z,walk as D}from"../browser/shared/html-tree.mjs";import{projectRoot as F,sdkDirectory as O,typeRoots as Y}from"../browser/shared/paths.mjs";var H=new Set(["application/ecmascript","application/javascript","application/x-ecmascript","application/x-javascript","text/ecmascript","text/javascript","text/javascript1.0","text/javascript1.1","text/javascript1.2","text/javascript1.3","text/javascript1.4","text/javascript1.5","text/jscript","text/livescript","text/x-ecmascript","text/x-javascript"]);function y(t,{diffs:s=!1,tree:r=g(t)}={}){let n=[];return D(r,a=>{if(a.tagName!=="script"||P(a,"src")!==void 0)return;let o=(P(a,"type")||"").trim().toLowerCase(),l=s&&o==="application/json"&&a.parentNode?.tagName?a.parentNode:null,e=l&&z(l,"doc-diff")?"__declaredDiff(":void 0;if(!e&&o&&o!=="module"&&!H.has(o))return;let{startTag:c,endTag:p}=a.sourceCodeLocation??{};c&&n.push({start:c.endOffset,end:p?.startOffset??t.length,module:o==="module",...e?{call:e}:{}})},{inTemplates:!1}),n}function L(t,s,r=[]){let n=[],a=0,o="",l=e=>e.replace(/[^\r\n]/g," ");for(let{start:e,end:c,call:p}of s){let u=o+l(t.slice(a+o.length,e));if(p){let h=V(u,p,o.length);if(h===null){r.push(e);continue}u=h}n.push(u,t.slice(e,c)),a=c,o=p?");":""}return n.push(o+l(t.slice(a+o.length)),`
|
|
3
3
|
export {};
|
|
4
4
|
`),n.join("")}function V(t,s,r){let n=t.length;for(;;){let a=Math.max(t.lastIndexOf(`
|
|
5
5
|
`,n-1)+1,r),o=t[n-1]==="\r"?n-1:n;if(o-a>=s.length)return t.slice(0,o-s.length)+s+t.slice(o);if(a<=r)return null;n=a-1}}function Pt(t,s=g(t)){return L(t,y(t,{tree:s}))}var q=new Set(["href","src","action","formaction"]),X=t=>{try{return decodeURIComponent(t)}catch{return t}};function Z(t,s){let r=y(t,{tree:s}).map(({start:n,end:a})=>({code:t.slice(n,a),start:n,where:"the document's script"}));return D(s,n=>{for(let{name:a,value:o}of n.attrs){let l=n.sourceCodeLocation?.attrs?.[a]?.startLine??n.sourceCodeLocation?.startLine,e=q.has(a)?o.replace(/[\t\n\r]/g,"").replace(/^[\u0000-\u0020]+/,""):"";/^on[a-z]+$/.test(a)?r.push({code:o,line:l,where:`the ${a} attribute of <${n.tagName}>`}):/^javascript:/i.test(e)&&r.push({code:X(e.slice(11)),line:l,where:`the javascript: address of <${n.tagName} ${a}>`})}},{inTemplates:!1}),r}var R=new Set(["fetch","XMLHttpRequest","WebSocket","EventSource","sendBeacon","import"]),E=new Set(["window","globalThis","self"]),Q=t=>i.isIdentifier(t)?t.text==="navigator":i.isPropertyAccessExpression(t)&&t.name.text==="navigator"&&i.isIdentifier(t.expression)&&E.has(t.expression.text);function I(t){let{initializer:s}=i.walkUpBindingElementsAndPatterns(t);return!!s&&i.isIdentifier(s)&&E.has(s.text)}function tt(t){let s=t.parent;return i.isPropertyAccessExpression(s)&&s.name===t?i.isIdentifier(s.expression)&&E.has(s.expression.text):i.isBindingElement(s)&&s.propertyName===t?I(s):!((i.isPropertyAssignment(s)||i.isMethodDeclaration(s)||i.isPropertyDeclaration(s)||i.isGetAccessorDeclaration(s)||i.isSetAccessorDeclaration(s))&&s.name===t)}function et(t,s){return i.isIdentifier(t)&&s.has(t.text)&&tt(t)?{name:t.text,reference:i.isPropertyAccessExpression(t.parent)&&t.parent.name===t?t.parent:t}:i.isElementAccessExpression(t)&&i.isIdentifier(t.expression)&&E.has(t.expression.text)&&i.isStringLiteralLike(t.argumentExpression)&&s.has(t.argumentExpression.text)?{name:t.argumentExpression.text,reference:t}:i.isPropertyAccessExpression(t)&&s.has(t.name.text)&&t.name.text==="sendBeacon"&&Q(t.expression)?{name:"sendBeacon",reference:t}:s.has("import")&&i.isCallExpression(t)&&t.expression.kind===i.SyntaxKind.ImportKeyword?{name:"import",reference:t.expression}:null}function st(t,s){let r=new Map,n=(e,c)=>{let p=e.parent;for(;p&&!c(p);)p=p.parent;return p??t},a=e=>i.isFunctionLike(e)||i.isBlock(e)||i.isCaseBlock(e)||i.isForStatement(e)||i.isForInStatement(e)||i.isForOfStatement(e),o=e=>i.isFunctionExpression(e)||i.isClassExpression(e)?e:i.isFunctionDeclaration(e)||i.isClassDeclaration(e)?n(e,a):i.isCatchClause(e.parent)?e.parent:n(e,i.getCombinedNodeFlags(e)&i.NodeFlags.BlockScoped?a:i.isFunctionLike),l=e=>{(i.isParameter(e)||i.isVariableDeclaration(e)||i.isBindingElement(e)||i.isFunctionDeclaration(e)||i.isClassDeclaration(e)||i.isFunctionExpression(e)||i.isClassExpression(e))&&e.name&&i.isIdentifier(e.name)&&s.has(e.name.text)&&!(i.isBindingElement(e)&&I(e))&&r.set(e.name.text,[...r.get(e.name.text)??[],o(e)]),i.forEachChild(e,l)};return l(t),r}function rt(t,s=[]){for(let r=t;r;r=r.parent)if(s.includes(r))return!0;return!1}function Nt(t,s,{tree:r=g(t)}={}){let n=[];for(let{code:a,start:o,line:l,where:e}of Z(t,r))for(let{name:c,how:p,at:u}of k(a,s))n.push({name:c,how:p,line:l??w(t,o+u),where:e});return n.sort((a,o)=>a.line-o.line)}function k(t,s){if(![...s].some(l=>t.includes(l)))return[];let r=[],n=i.createSourceFile("code.js",t,i.ScriptTarget.Latest,!0,i.ScriptKind.JS),a=st(n,s),o=l=>{let e=et(l,s),c=e?.reference.parent;if(e&&!i.isTypeOfExpression(c)&&!(i.isIdentifier(e.reference)&&rt(l,a.get(e.name)))){let p=(i.isCallExpression(c)||i.isNewExpression(c))&&c.expression===e.reference?i.isNewExpression(c)?"new":"call":"use";r.push({name:e.name,how:p,at:l.getStart(n)})}i.forEachChild(l,o)};return o(n),r}var nt=new Set([2304,2552]),it=new Set([2300,2451,2448,2449,2450]),M=t=>nt.has(t)||it.has(t)||t>=1e3&&t<2e3;function at(t,s,r){if(!t.file||r.has(t)||M(t.code))return!1;let n=t.start??-1;return s.some(a=>!a.call&&a.start<=n&&n<a.end)}var ot=(t,s)=>/\bNumberValue\b/.test(t)||/\bd3\.(?:min|max|extent)\(/.test(s)?"; here d3 reads values of no type as strings: give the data its shape once with JSDoc, /** @type {Array<{ label: string, value: number }>} */ const rows = data;, and d3.min or d3.max may give undefined (?? 0)":"";function B(t){let s={allowJs:!0,checkJs:!0,noEmit:!0,allowUmdGlobalAccess:!0,strict:!0,noImplicitAny:!1,target:i.ScriptTarget.ES2022,module:i.ModuleKind.ESNext,moduleResolution:i.ModuleResolutionKind.Bundler,lib:["lib.es2022.d.ts","lib.dom.d.ts","lib.dom.iterable.d.ts"],types:["d3"],typeRoots:Y,skipLibCheck:!1},r=i.createCompilerHost(s),n=r.readFile.bind(r),a=r.fileExists.bind(r);return r.readFile=o=>t.get(f.resolve(o))??n(o),r.fileExists=o=>t.has(f.resolve(o))||a(o),i.createProgram([f.join(O,"index.d.ts"),...t.keys()],s,r)}var ct=/\bDoc[A-Z]\w*/,lt=(t,s)=>ct.test(s)||(t.relatedInformation??[]).some(({file:r})=>r&&f.resolve(r.fileName).startsWith(O+f.sep)),N={katex:"katex","katex-auto-render":"renderMathInElement"};function Dt(t,{assets:s=[]}={}){let r=f.join(F,"reports","__package__","kit.js"),n=t.replace(/^([ \t]*)(?:<!--|-->)/gm,"$1//"),a=s.filter(c=>Object.hasOwn(N,c)).map(c=>`declare const ${N[c]}: any;
|