@heycys/gallery 0.2.1 → 0.2.3

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.
Files changed (169) hide show
  1. package/THIRD-PARTY-NOTICES.md +1 -1
  2. package/browser/editor/page/annotate/index.mjs +3 -2
  3. package/browser/editor/page/board/index.mjs +2 -1
  4. package/browser/editor/page/board/settings.mjs +2 -1
  5. package/browser/editor/page/core.mjs +3 -2
  6. package/browser/editor/page/custom-page.mjs +4 -3
  7. package/browser/editor/page/editor.mjs +13 -7
  8. package/browser/editor/page/emoji-picker/support.mjs +3 -2
  9. package/browser/editor/page/links/index.mjs +1 -12
  10. package/browser/editor/page/links/pages.mjs +3 -2
  11. package/browser/editor/page/outline/folds.mjs +3 -0
  12. package/browser/editor/page/post.mjs +2 -1
  13. package/browser/editor/page/present-links.mjs +4 -3
  14. package/browser/editor/page/present-shell.mjs +3 -58
  15. package/browser/editor/page/session.mjs +3 -37
  16. package/browser/editor/page/storage.mjs +5 -5
  17. package/browser/shared/component-catalog.mjs +2 -2
  18. package/browser/shared/drafts.mjs +26 -51
  19. package/browser/shared/emoji/icon-value.mjs +2 -1
  20. package/browser/shared/extension-manifest.mjs +90 -0
  21. package/browser/shared/extensions.mjs +26 -16
  22. package/browser/shared/http.mjs +1 -1
  23. package/browser/shared/i18n/en.mjs +46 -15
  24. package/browser/shared/media.mjs +2 -1
  25. package/browser/shared/outline.mjs +11 -51
  26. package/browser/shared/page-address.mjs +8 -15
  27. package/browser/shared/paths.mjs +2 -1
  28. package/browser/shared/preferences.mjs +3 -1
  29. package/browser/shared/preview-server.mjs +51 -63
  30. package/browser/shared/service-location.mjs +18 -0
  31. package/browser/shared/session.mjs +3 -6
  32. package/browser/shared/workspace-address.mjs +22 -0
  33. package/browser/shared/workspace-columns.mjs +1 -2
  34. package/browser/shared/workspace-registry.mjs +6 -40
  35. package/browser/shared/workspace.mjs +3 -6
  36. package/browser/shared/writing-preferences.mjs +6 -4
  37. package/dist/{chunk-5MEXZ2UF.mjs → chunk-225ALKYI.mjs} +1 -1
  38. package/dist/{chunk-7N2LPG33.mjs → chunk-2HS7ZBPU.mjs} +1 -1
  39. package/dist/chunk-3F57LEN5.mjs +2 -0
  40. package/dist/{chunk-MKTX5PGV.mjs → chunk-43B75J5T.mjs} +1 -1
  41. package/dist/{chunk-VKSRM7EY.mjs → chunk-4B2TB5K2.mjs} +1 -1
  42. package/dist/{chunk-LV24RY2E.mjs → chunk-4K5KC6V2.mjs} +1 -1
  43. package/dist/chunk-5GK7HS62.mjs +2 -0
  44. package/dist/chunk-6GV3CBB2.mjs +2 -0
  45. package/dist/{chunk-TOE6DPLH.mjs → chunk-6NLXUKAY.mjs} +1 -1
  46. package/dist/{chunk-ZCP7BTOZ.mjs → chunk-6QBHU4DF.mjs} +1 -1
  47. package/dist/{chunk-QXVNUASA.mjs → chunk-6QWQXHAV.mjs} +1 -1
  48. package/dist/chunk-6XD6U3QA.mjs +5 -0
  49. package/dist/{chunk-DKR7LFIY.mjs → chunk-6Y3HYOBM.mjs} +1 -1
  50. package/dist/{chunk-36FIRJIF.mjs → chunk-742D2WWJ.mjs} +2 -2
  51. package/dist/{chunk-7M7UPWU6.mjs → chunk-7EFHNQ5B.mjs} +1 -1
  52. package/dist/{chunk-2GKGEVMR.mjs → chunk-7UZUQQ7Q.mjs} +25 -24
  53. package/dist/{chunk-VPVOAJ7X.mjs → chunk-AGDILNWB.mjs} +1 -1
  54. package/dist/{chunk-5IIH335D.mjs → chunk-ALZ6ZENZ.mjs} +1 -1
  55. package/dist/{chunk-G4YP7GVC.mjs → chunk-BFJGHSQN.mjs} +1 -1
  56. package/dist/{chunk-AMDGVDNV.mjs → chunk-BQNTTFPL.mjs} +1 -1
  57. package/dist/chunk-BX5AKINW.mjs +15 -0
  58. package/dist/{chunk-PAFSFVYB.mjs → chunk-BYNPEUDS.mjs} +1 -1
  59. package/dist/{chunk-74RPP2M2.mjs → chunk-C564NU2H.mjs} +1 -1
  60. package/dist/{chunk-DH6GMDNN.mjs → chunk-CRDG6EF2.mjs} +1 -1
  61. package/dist/{chunk-7XDYK3UC.mjs → chunk-E7656LR7.mjs} +1 -1
  62. package/dist/{chunk-KFZBYDIS.mjs → chunk-ECZBLQKK.mjs} +1 -1
  63. package/dist/{chunk-ZEY2RLBT.mjs → chunk-ERLAIBOA.mjs} +2 -2
  64. package/dist/{chunk-YFJU3O7G.mjs → chunk-FJLM7YE7.mjs} +1 -1
  65. package/dist/{chunk-AFGUR7SY.mjs → chunk-FNCCVGKX.mjs} +1 -1
  66. package/dist/{chunk-VNUWREXU.mjs → chunk-FOJ3K5Z3.mjs} +1 -1
  67. package/dist/{chunk-WCWYNFDF.mjs → chunk-FQ72LZHB.mjs} +1 -1
  68. package/dist/chunk-G7N7YFVD.mjs +2 -0
  69. package/dist/chunk-GIRXJBGC.mjs +2 -0
  70. package/dist/{chunk-VA3KAQZD.mjs → chunk-GKOBB6DH.mjs} +1 -1
  71. package/dist/chunk-GMJD7ID3.mjs +2 -0
  72. package/dist/chunk-GQJMVSSN.mjs +2 -0
  73. package/dist/{chunk-QZ35UAB4.mjs → chunk-GRAXKKG4.mjs} +1 -1
  74. package/dist/{chunk-SAHS56QM.mjs → chunk-HX4DLE3J.mjs} +1 -1
  75. package/dist/{chunk-2RKIHJI2.mjs → chunk-HYMEEXKN.mjs} +1 -1
  76. package/dist/{chunk-QK3VISCA.mjs → chunk-IA4PSIMD.mjs} +1 -1
  77. package/dist/chunk-IS7N3N6Q.mjs +168 -0
  78. package/dist/{chunk-CZ5KJSGK.mjs → chunk-JJNHKGTD.mjs} +1 -1
  79. package/dist/chunk-JPG2LBQM.mjs +11 -0
  80. package/dist/chunk-JYK5WJPM.mjs +2 -0
  81. package/dist/{chunk-LE44MD3O.mjs → chunk-KLQP46Y3.mjs} +1 -1
  82. package/dist/chunk-L55NOXBE.mjs +4 -0
  83. package/dist/{chunk-FXQBADMN.mjs → chunk-LCB6MARS.mjs} +1 -1
  84. package/dist/{chunk-SOD23RGL.mjs → chunk-LJLVCU6W.mjs} +8 -8
  85. package/dist/{chunk-NAGMRVW4.mjs → chunk-LKX55H76.mjs} +1 -1
  86. package/dist/chunk-LMUZQK5O.mjs +2 -0
  87. package/dist/{chunk-CG3UHDKH.mjs → chunk-M4NRDYI5.mjs} +1 -1
  88. package/dist/{chunk-HM4E43VM.mjs → chunk-MAFIGVXY.mjs} +1 -1
  89. package/dist/chunk-NLDF35P5.mjs +2 -0
  90. package/dist/{chunk-3OFXMN6N.mjs → chunk-NMF3IPYA.mjs} +1 -1
  91. package/dist/chunk-ONKC4BCC.mjs +2 -0
  92. package/dist/{chunk-CPS4SRPF.mjs → chunk-OU6ZZVBX.mjs} +2 -2
  93. package/dist/{chunk-NWRZXELO.mjs → chunk-PAREWQGB.mjs} +1 -1
  94. package/dist/{chunk-BYSF5UMM.mjs → chunk-PKVK6WFC.mjs} +2 -2
  95. package/dist/{chunk-4AMTKWDO.mjs → chunk-PUOQ65NC.mjs} +1 -1
  96. package/dist/{chunk-L7W624FD.mjs → chunk-QJAVCTY7.mjs} +1 -1
  97. package/dist/{chunk-6MPKCH3P.mjs → chunk-QNECWMGR.mjs} +1 -1
  98. package/dist/{chunk-HBAUKKQH.mjs → chunk-QP7BOHE4.mjs} +1 -1
  99. package/dist/chunk-QVGOPHOF.mjs +2 -0
  100. package/dist/{chunk-NFABH3QW.mjs → chunk-RRKJMHNX.mjs} +2 -2
  101. package/dist/chunk-S3SX3COT.mjs +20 -0
  102. package/dist/chunk-SQ3FQCCN.mjs +8 -0
  103. package/dist/{chunk-ZO7SFB2E.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
  104. package/dist/{chunk-3A5I2GJK.mjs → chunk-T4Y2ZSVX.mjs} +1 -1
  105. package/dist/{chunk-VG5JEIUQ.mjs → chunk-TKWEX4FD.mjs} +2 -2
  106. package/dist/{chunk-CCSGFCLZ.mjs → chunk-U3SSPL4K.mjs} +1 -1
  107. package/dist/{chunk-DHB7UZ7F.mjs → chunk-U5XL7D6A.mjs} +1 -1
  108. package/dist/{chunk-JXLSHVJ2.mjs → chunk-UMHUSOPJ.mjs} +1 -1
  109. package/dist/chunk-UTF7GBQU.mjs +4 -0
  110. package/dist/chunk-UTMAY3WK.mjs +2 -0
  111. package/dist/{chunk-S2Y7VI4H.mjs → chunk-V4ZHRALW.mjs} +2 -2
  112. package/dist/{chunk-GWOYMENX.mjs → chunk-V6XZ4VIR.mjs} +1 -1
  113. package/dist/{chunk-WTEGUPFG.mjs → chunk-VRPVVMRB.mjs} +1 -1
  114. package/dist/{chunk-XKOHV4H6.mjs → chunk-W5NPH5UA.mjs} +1 -1
  115. package/dist/{chunk-VGTMLQWZ.mjs → chunk-WD4U6XXT.mjs} +1 -1
  116. package/dist/{chunk-4DNHHJSL.mjs → chunk-XBNFXWAW.mjs} +1 -1
  117. package/dist/chunk-XZV72SQ4.mjs +2 -0
  118. package/dist/{chunk-QJ3TFJLQ.mjs → chunk-Y34NR5JL.mjs} +1 -1
  119. package/dist/{chunk-YBB5LWZK.mjs → chunk-YFJB534W.mjs} +1 -1
  120. package/dist/{chunk-V5JRSGZF.mjs → chunk-YN37ZDLW.mjs} +1 -1
  121. package/dist/chunk-Z6OKLRHC.mjs +2 -0
  122. package/dist/chunk-ZLS2FRS6.mjs +3 -0
  123. package/dist/{chunk-JI6T3A4Y.mjs → chunk-ZMHUDYLV.mjs} +1 -1
  124. package/dist/{chunk-KH24LWAT.mjs → chunk-ZTHICSSF.mjs} +1 -1
  125. package/dist/cli.mjs +1 -1
  126. package/dist/server.mjs +1 -1
  127. package/dist/worker.mjs +1 -1
  128. package/kit/math.js +1 -1
  129. package/kit/runtime.js +13 -2
  130. package/kit/state.js +2 -0
  131. package/kit/ui.js +5 -1
  132. package/package.json +1 -1
  133. package/release.json +7 -7
  134. package/skill/SKILL.md +13 -13
  135. package/skill/references/building-blocks.md +1 -1
  136. package/skill/references/extensions.md +45 -9
  137. package/web/assets/index-D8P8uhDW.js +15 -0
  138. package/web/assets/{index-xP0uw3a0.css → index-FLb9jCfl.css} +1 -1
  139. package/web/index.html +2 -2
  140. package/dist/chunk-46ACXOU5.mjs +0 -2
  141. package/dist/chunk-5DPABPBI.mjs +0 -2
  142. package/dist/chunk-5L26JUYA.mjs +0 -2
  143. package/dist/chunk-AEMVZYZQ.mjs +0 -166
  144. package/dist/chunk-ASAXY2DJ.mjs +0 -2
  145. package/dist/chunk-DAMVDXR3.mjs +0 -2
  146. package/dist/chunk-DLFZJJEE.mjs +0 -2
  147. package/dist/chunk-EKNHM3P4.mjs +0 -2
  148. package/dist/chunk-FCK6NXON.mjs +0 -3
  149. package/dist/chunk-FX42XU6J.mjs +0 -2
  150. package/dist/chunk-HATA2W7X.mjs +0 -2
  151. package/dist/chunk-JJNRIFND.mjs +0 -8
  152. package/dist/chunk-JL6R4FG4.mjs +0 -4
  153. package/dist/chunk-KUILYI7H.mjs +0 -4
  154. package/dist/chunk-NIW7F7LU.mjs +0 -4
  155. package/dist/chunk-OU3S466R.mjs +0 -20
  156. package/dist/chunk-QCEATQC4.mjs +0 -2
  157. package/dist/chunk-TDWJKOLN.mjs +0 -2
  158. package/dist/chunk-UGO5N3EI.mjs +0 -15
  159. package/dist/chunk-XMKHCUI6.mjs +0 -15
  160. package/dist/chunk-XQRUG4G3.mjs +0 -2
  161. package/dist/chunk-YPPJVDQI.mjs +0 -2
  162. package/dist/chunk-ZANMKAWS.mjs +0 -2
  163. package/web/assets/index-BHC5ZOMh.js +0 -15
  164. /package/dist/{chunk-N3DSBH5S.mjs → chunk-A55HN5OK.mjs} +0 -0
  165. /package/dist/{chunk-KNZJT4ET.mjs → chunk-QAIHRH3I.mjs} +0 -0
  166. /package/dist/{chunk-ZW2A6GBW.mjs → chunk-QR6A54KJ.mjs} +0 -0
  167. /package/dist/{chunk-74UW7QSL.mjs → chunk-UH4SIXTR.mjs} +0 -0
  168. /package/dist/{chunk-EKGYYOSP.mjs → chunk-W7XHE4CT.mjs} +0 -0
  169. /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 { layoutOf } from "./workspace.mjs";
8
+ import { defaultWorkspace } from "./workspace.mjs";
9
+ import { serviceLocation } from "./service-location.mjs";
9
10
 
10
- // Where a workspace's Gallery server listens (one a gallery command started, kind "cli", or npm run
11
- // dev's, kind "dev"): the one way the commands tell this workspace's server from any other. gallery
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 server's workspace and a command's). */
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 = (reportsDirectory) => layoutOf(reportsDirectory).serverFile;
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
- * Keep where the server listens: { port, pid, workspace, token, nonce, kind, version, started }, for
52
- * as long as it does (the owner's alone on POSIX: the token authorizes writes), so
53
- * that a server that runs is one the commands find, never one a command starts a second server
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, workspace: path.resolve(reportsDirectory), token, nonce, kind, version, started: new Date().toISOString() })}\n`;
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 before = await fs.readFile(file, "utf8").catch(() => null);
73
- await save(text);
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
- try {
95
- if (JSON.parse(await fs.readFile(file, "utf8")).nonce !== nonce) return;
96
- } catch {
97
- return;
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 announcement of this workspace's server, while its process runs: { port, pid, workspace, token, nonce, kind, version, started }.
109
- export async function readAnnouncement({ reportsDirectory }) {
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(reportsDirectory), "utf8"));
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.workspace !== "string" || !sameDirectory(found.workspace, reportsDirectory)) return null;
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 this
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, reportsDirectory, timeout) {
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.workspace === "string" && sameDirectory(info.server.workspace, reportsDirectory);
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 of this workspace: { port, token, pid }, or null when none says it runs. With
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({ reportsDirectory, answering = false, timeout = 10_000 }) {
150
- const found = await readAnnouncement({ reportsDirectory });
151
- if (!found || (answering && (await answerOf(found, reportsDirectory, timeout)) !== "yes")) return null;
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: `This workspace's server is npm run dev's (port ${found.port}, pid ${found.pid}): stop it where it runs.` };
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
- * This workspace's server, for a command that needs one (src/cli/server-process.mjs serverFor): the
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 }`: this workspace's server, busy or stuck, and none is started beside it; and when
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({ reportsDirectory, ports, start, stop = stopServer, timeout = 10_000, version = galleryVersion }) {
225
- const found = await readAnnouncement({ reportsDirectory });
226
- const answer = found ? await answerOf(found, reportsDirectory, timeout) : "no";
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; or, for a draft of another workspace, between the
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,22 @@
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
+ // Editor modules, emoji data and the global custom-block catalog are shared by every
15
+ // document. Keep their canonical URLs to reuse the cache without project redirects.
16
+ if (/^\/__gallery\/(?:edit\/(?:v\/[a-f0-9]{16}\/)?src\/|components\/)/.test(address)) return address;
17
+ return /^\/(?:__gallery\/|generated\/|pages\/)/.test(address) ? base + address : address;
18
+ }
19
+
20
+ /** Browser state is shared by origin, so a document's owner is part of its identity. */
21
+ export const documentKey = (slug, base = currentProjectBase()) => base && typeof slug === "string" && slug && !slug.startsWith("~") ? base.slice(1) + "/" + slug : slug;
22
+ 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 server drops one at once when that workspace's own
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, realpathSync } from "node:fs";
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; never one in
20
- // the system's temporary folder (the tests', a trial's, a check's), nor a folder whose gallery.json is
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 in the system's
176
- * temporary folder or one that is no workspace of Gallery's. Resolves to "added", "seen", "fresh" (its
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(), tmp = os.tmpdir(), force = false } = {}) {
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 { defaultPreferencesFile, reportsDirectory as defaultReportsDirectory } from "./paths.mjs";
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 = (reportsDirectory = defaultReportsDirectory) => path.join(reportsDirectory, "preferences.md");
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(reportsDirectory = defaultReportsDirectory) {
26
- const file = preferencesFileOf(reportsDirectory);
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,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{a,b,c,d,e,f,g,h,i,j,k,l}from"./chunk-FCK6NXON.mjs";import"./chunk-TOE6DPLH.mjs";import"./chunk-WTEGUPFG.mjs";import"./chunk-SAHS56QM.mjs";import"./chunk-YFJU3O7G.mjs";import"./chunk-2RKIHJI2.mjs";import"./chunk-JXLSHVJ2.mjs";import"./chunk-XKOHV4H6.mjs";import"./chunk-KFZBYDIS.mjs";import"./chunk-7XDYK3UC.mjs";import"./chunk-VG5JEIUQ.mjs";import"./chunk-N3DSBH5S.mjs";import"./chunk-5L7RYZCL.mjs";export{e as BASELINE_FROM,h as adoptIdless,g as baselineOf,b as blocksOf,k as exportBaselines,j as exportedText,i as hasBaseline,d as packageStamps,c as pageOf,l as previewBaseline,a as rankOf,f as readBaseline};
2
+ import{a,b,c,d,e,f,g,h,i,j,k,l}from"./chunk-ZLS2FRS6.mjs";import"./chunk-6NLXUKAY.mjs";import"./chunk-VRPVVMRB.mjs";import"./chunk-HX4DLE3J.mjs";import"./chunk-FJLM7YE7.mjs";import"./chunk-HYMEEXKN.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 BASELINE_FROM,h as adoptIdless,g as baselineOf,b as blocksOf,k as exportBaselines,j as exportedText,i as hasBaseline,d as packageStamps,c as pageOf,l as previewBaseline,a as rankOf,f as readBaseline};
@@ -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{c as O,d as P,e as b,l as C}from"./chunk-JJNRIFND.mjs";import{a as q}from"./chunk-ASAXY2DJ.mjs";import{a as u,f as S,g as w,i as E,j as I,k as H}from"./chunk-YPPJVDQI.mjs";import{a as h,b as R}from"./chunk-JEUK3ZO7.mjs";import{b as D,f as L,g as T}from"./chunk-4OZWXZE5.mjs";import{b as N}from"./chunk-NWRZXELO.mjs";import{existsSync as g}from"node:fs";import i from"node:path";import{reportsDirectory as c}from"../browser/shared/paths.mjs";import{idHolder as z,isDocSlug as B,keptName as G,layoutOf as A,listTrash as J}from"../browser/shared/workspace.mjs";import{parentIn as K}from"../browser/shared/outline.mjs";import{joinDefaultTree as Q,joinedLine as V}from"../browser/shared/drafts.mjs";var W="\u65B0\u6587\u6863";async function F(t,n=c){if(G(t))return`"${t}" is a name the workspace keeps for itself: choose another slug.`;if(g(i.join(A(n).preferencesPage,t)))return`"${t}" is the \u5199\u4F5C\u504F\u597D page's slug: choose another slug.`;if(g(i.join(A(n).customPages,t)))return`"${t}" is a custom page's slug (documents and custom pages share one set of names: gallery page list lists them): choose another slug.`;if(g(i.join(n,t)))return`Document already exists: ${h(i.join(n,t))}`;if((await J(n)).some(r=>r.slug===t))return`"${t}" is the slug of something in the trash: choose another slug.`;if(B(t)){let r=await z(t,n);if(r)return`"${t}" would have the page id (${t.slice(4)}) of "${r}" in this workspace, and two documents may not share an address: choose another slug.`}return null}async function fe(t){let{positional:n,options:r,values:U}=L({options:["--no-preview","--no-browser","--first"],values:["--under","--after"],argv:t}),[e,y,$=W]=n;if(n.length<2||n.length>3||!e||!y?.trim()||!$.trim())throw new D("Name the slug and the title (then a description, if you choose one), each quoted when it has spaces.");T(e);let s=H(U,r,{top:!1}),x=await F(e);x&&(console.error(x),process.exit(1));let M=s.under!==void 0||s.after!==void 0||s.first,l=null;if(M){let o=await u(),a=async(X,_)=>{let p=await S(X,_,o);return p.refused&&(console.error(`${p.refused} Nothing made.`),process.exit(1)),p.slug},f=s.under!==void 0?await a(s.under,"--under"):void 0,d=s.after!==void 0?await a(s.after,"--after"):null;d!==null&&f!==void 0&&K(o.children,d)!==f&&(console.error(`${w(o,e,"level",{parent:f,after:d})} Nothing made.`),process.exit(1)),l={parent:f,after:d,first:s.first}}try{await N(e,y,$)}catch(o){if(o.code!=="EEXIST")throw o;console.error(await F(e)??`Document already exists: ${h(i.join(c,e))}`),process.exit(1)}if(await import("./chunk-TDWJKOLN.mjs").then(({noteNew:o})=>o(e)),console.log(`Created ${h(i.join(c,e,"doc.html"))} and doc.json`),l){let o=await E(e,l),a=await u();o.ok?console.log(`Placed it ${I(a,o)}.`):(console.log(`Not placed (${w(a,e,o.reason,{...l,message:o.message})}): it is at the top level, last; gallery move ${e} puts it where it goes.`),process.exitCode=1)}let v=V(await Q(c,e));v&&console.log(v);for(let o of await R())console.log(o);let j=q(c);j&&console.log(j);let m=await C(e,{skipped:b(r),command:`gallery new ${e}`});console.log(O(m,"replace the starter section with the document's skeleton (each section's heading) as soon as you can outline it, then write it a section at a time, as ordinary HTML, so that the user watches it grow (gallery check fails while the starter section remains).")),console.log(`Hand in: gallery export ${e}, then gallery qa ${e}`);let k=await P(m,r);k&&console.log(k);for(let o of m)console.log(o)}export{W as a,F as b,fe as c};
2
+ import{c as O,d as P,e as b,l as C}from"./chunk-SQ3FQCCN.mjs";import{a as q}from"./chunk-3F57LEN5.mjs";import{a as u,f as S,g as w,i as E,j as I,k as H}from"./chunk-GMJD7ID3.mjs";import{a as h,b as R}from"./chunk-JEUK3ZO7.mjs";import{b as D,f as L,g as T}from"./chunk-4OZWXZE5.mjs";import{b as N}from"./chunk-PAREWQGB.mjs";import{existsSync as g}from"node:fs";import i from"node:path";import{reportsDirectory as c}from"../browser/shared/paths.mjs";import{idHolder as z,isDocSlug as B,keptName as G,layoutOf as A,listTrash as J}from"../browser/shared/workspace.mjs";import{parentIn as K}from"../browser/shared/outline.mjs";import{joinDefaultTree as Q,joinedLine as V}from"../browser/shared/drafts.mjs";var W="\u65B0\u6587\u6863";async function F(t,n=c){if(G(t))return`"${t}" is a name the workspace keeps for itself: choose another slug.`;if(g(i.join(A(n).preferencesPage,t)))return`"${t}" is the \u5199\u4F5C\u504F\u597D page's slug: choose another slug.`;if(g(i.join(A(n).customPages,t)))return`"${t}" is a custom page's slug (documents and custom pages share one set of names: gallery page list lists them): choose another slug.`;if(g(i.join(n,t)))return`Document already exists: ${h(i.join(n,t))}`;if((await J(n)).some(r=>r.slug===t))return`"${t}" is the slug of something in the trash: choose another slug.`;if(B(t)){let r=await z(t,n);if(r)return`"${t}" would have the page id (${t.slice(4)}) of "${r}" in this workspace, and two documents may not share an address: choose another slug.`}return null}async function fe(t){let{positional:n,options:r,values:U}=L({options:["--no-preview","--no-browser","--first"],values:["--under","--after"],argv:t}),[e,y,$=W]=n;if(n.length<2||n.length>3||!e||!y?.trim()||!$.trim())throw new D("Name the slug and the title (then a description, if you choose one), each quoted when it has spaces.");T(e);let s=H(U,r,{top:!1}),x=await F(e);x&&(console.error(x),process.exit(1));let M=s.under!==void 0||s.after!==void 0||s.first,l=null;if(M){let o=await u(),a=async(X,_)=>{let p=await S(X,_,o);return p.refused&&(console.error(`${p.refused} Nothing made.`),process.exit(1)),p.slug},f=s.under!==void 0?await a(s.under,"--under"):void 0,d=s.after!==void 0?await a(s.after,"--after"):null;d!==null&&f!==void 0&&K(o.children,d)!==f&&(console.error(`${w(o,e,"level",{parent:f,after:d})} Nothing made.`),process.exit(1)),l={parent:f,after:d,first:s.first}}try{await N(e,y,$)}catch(o){if(o.code!=="EEXIST")throw o;console.error(await F(e)??`Document already exists: ${h(i.join(c,e))}`),process.exit(1)}if(await import("./chunk-Z6OKLRHC.mjs").then(({noteNew:o})=>o(e)),console.log(`Created ${h(i.join(c,e,"doc.html"))} and doc.json`),l){let o=await E(e,l),a=await u();o.ok?console.log(`Placed it ${I(a,o)}.`):(console.log(`Not placed (${w(a,e,o.reason,{...l,message:o.message})}): it is at the top level, last; gallery move ${e} puts it where it goes.`),process.exitCode=1)}let v=V(await Q(c,e));v&&console.log(v);for(let o of await R())console.log(o);let j=q(c);j&&console.log(j);let m=await C(e,{skipped:b(r),command:`gallery new ${e}`});console.log(O(m,"replace the starter section with the document's skeleton (each section's heading) as soon as you can outline it, then write it a section at a time, as ordinary HTML, so that the user watches it grow (gallery check fails while the starter section remains).")),console.log(`Hand in: gallery export ${e}, then gallery qa ${e}`);let k=await P(m,r);k&&console.log(k);for(let o of m)console.log(o)}export{W as a,F as b,fe as c};
@@ -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,b as M}from"./chunk-QP7BOHE4.mjs";import{c as y,d as j}from"./chunk-L55NOXBE.mjs";import{b as w,f as A}from"./chunk-4OZWXZE5.mjs";import L from"node:fs/promises";import k from"node:path";import{readAnnouncement as T}from"../browser/shared/preview-server.mjs";import{nameProblem as _,nameStops as v,ownName as B,workspaceLabels as K,workspaceName as b,writeWorkspaceName as Q}from"../browser/shared/workspace-name.mjs";import{forgetWorkspace as R,isWorkspace as C,isWorkspaceId as U,readRegistry as F,registerQuietly as x,registerWorkspace as q,SCAN_DEPTH as P,SCAN_LIMIT as z,scanForWorkspaces as J,workspaceId as V,workspaceKey as l}from"../browser/shared/workspace-registry.mjs";import{defaultWorkspace as X,documentsFolder as E,ensureWorkspace as Y,layoutOf as Z}from"../browser/shared/workspace.mjs";import{clashingIds as ee}from"../browser/shared/drafts.mjs";var u=(o,t)=>`${o} ${t}${o===1?"":"s"}`,W=(o,t)=>l(o)===l(t);function oe(o){if(B(o)||W(o,X()))return null;let{name:t}=b(o,{stops:v()});return`\u5DE5\u4F5C\u533A\u540D\u5B57\uFF1A\u6CA1\u6709\uFF08\u522B\u5904\u6309\u6587\u4EF6\u5939\u53EB\u5B83"${t}"\uFF09\uFF1B\u8FD9\u4E2A\u5DE5\u4F5C\u533A\u662F\u4E3A\u4E00\u4E2A\u9879\u76EE\u5EFA\u7684\uFF0C\u5C31\u7528 gallery workspaces --name "<\u9879\u76EE\u540D>" \u5199\u4E0A\u9879\u76EE\u540D`}async function se(o,t){let n=_(t);if(n)throw new w(n);try{let{ours:r,reads:f}=await Y(o);r||(console.error(j(o,f)),process.exit(1))}catch(r){if(!r.code)throw r;console.error(y(o,r.code)),process.exit(1)}let s;try{s=await Q(o,t)}catch(r){console.error(r.code?y(o,r.code):r.message),process.exit(1)}await x(o),console.log(`\u5DE5\u4F5C\u533A\uFF1A${o}`),console.log(`\u540D\u5B57\uFF1A${s.name} (in ${Z(o).marker}${s.before&&s.before!==s.name?`; it was "${s.before}"`:""}): the sidebar and gallery workspaces call it so.`)}async function re(o,t){let n=await R(o);n.length||(console.error(`No registered workspace is ${U(o)?o:k.resolve(o)}: gallery workspaces lists them, with their paths and ws- ids.`),process.exit(1));for(let s of n)console.log(`Forgot ${s} (only the registry changed: nothing on disk).${W(s,t)?" It is the current workspace: the next command run in it registers it again.":""}`)}async function te(o){let t=k.resolve(o??E());await L.stat(t).then(a=>a.isDirectory(),()=>!1)||(console.error(`No such folder: ${t} (--scan names a folder to look in).`),process.exit(1));let n=new Set(F().workspaces.map(({root:a})=>l(a))),{found:s,read:r,cut:f}=await J(t);console.log(`Scanned ${t}, ${P} levels down (${u(r,"folder")}): ${s.length?`${u(s.length,"workspace")}.`:"no workspace."}`);let i=v();for(let a of s){let{name:d}=b(a,{stops:i});if(n.has(l(a))){await x(a),console.log(` \u5DF2\u767B\u8BB0\uFF1A${a}\uFF08${d}\uFF09`);continue}try{await q(a)}catch(m){console.log(` \u6CA1\u80FD\u767B\u8BB0\uFF08${m.code??m.message}\uFF09\uFF1A${a}`);continue}console.log(` \u65B0\u767B\u8BB0\uFF1A${a}\uFF08${d}\uFF09`)}f&&console.log(`Stopped after ${u(z,"folder")}: gallery workspaces --scan <a folder further down> looks at the rest.`),s.length&&console.log("gallery workspaces lists them; gallery list <words> --all searches their documents.")}async function ne(o){let t=C(o);t&&await x(o);let n=F().workspaces,s=[o,...n.map(({root:e})=>e).filter(e=>!W(e,o))],r=new Map(n.map(({root:e,seen:c})=>[l(e),Date.parse(c)])),f=E(),i=v({documents:f}),a=await Promise.all(s.map(async e=>{let c=C(e),p=c?await D(e):[];return{root:e,there:c,count:p.length,edited:p.reduce((g,{edited:h})=>Math.max(g,h),0),named:{root:e,...b(e,{stops:i})}}})),[d,...m]=a;m.sort((e,c)=>c.there-e.there||c.edited-e.edited||(r.get(l(c.root))??0)-(r.get(l(e.root))??0));let I=[d,...m],G=new Map(K(I.map(({named:e})=>e)).map(e=>[l(e.root),e])),$=await T();console.log($?`Gallery \u670D\u52A1\uFF1Ahttp://127.0.0.1:${$.port}/ (pid ${$.pid})`:"Gallery \u670D\u52A1\uFF1A\u672A\u542F\u52A8");let N=n.length;console.log(`${u(N,"workspace")} on this machine's registry; the current one first (*):`);for(let e of I){let{name:c,own:p,above:g}=G.get(l(e.root)),h=e===d,S=[...p||!e.there?[]:["\u6CA1\u6709\u8D77\u540D\uFF1A\u7528\u7684\u662F\u6587\u4EF6\u5939\u7684\u540D\u5B57"],...e.there?[]:[h?"\u8FD8\u6CA1\u6709\u5EFA":"\u4E0D\u5728\u4E86\uFF1A\u6587\u4EF6\u5939\u6216\u5B83\u7684 gallery.json \u627E\u4E0D\u5230\u4E86"]],H=g?k.isAbsolute(g)?`\uFF08${g}\uFF09`:`\uFF08\u4E0A\u4E00\u7EA7\uFF1A${g}\uFF09`:"";console.log(`${h?"*":" "} ${c}${H} ${V(e.root)}${S.length?` \uFF08${S.join("\uFF1B")}\uFF09`:""}`),console.log(` \u8DEF\u5F84\uFF1A${e.root}`),e.there&&console.log(` \u6587\u6863\uFF1A${e.count?`${e.count} \u7BC7\uFF1B\u4E0A\u6B21\u7F16\u8F91\uFF1A${M(e.edited)}`:"\u6CA1\u6709"}`)}for(let{id:e,roots:c}of ee())console.log(`${e} \u662F ${c.length} \u4E2A\u767B\u8BB0\u7684\u5DE5\u4F5C\u533A\u5171\u7528\u7684\u7F16\u53F7\uFF08${c.join("\u3001")}\uFF09\uFF1A\u4FA7\u8FB9\u680F\u91CC\u5B83\u4EEC\u7684\u5E95\u7A3F\u90FD\u627E\u4E0D\u5230\uFF1Bgallery workspaces --forget <\u8DEF\u5F84> \u53BB\u6389\u5176\u4E2D\u4E00\u4E2A`);let O=t?oe(o):null;O&&console.log(O),m.some(({there:e})=>!e)&&console.log("One no longer there stays registered (a drive may come back): gallery workspaces --forget <\u8DEF\u5F84> takes it off, nothing on disk changed."),N<=1&&console.log(`gallery workspaces --scan registers the workspaces already on this machine (in the Documents folder, ${P} levels down; or --scan <folder>).`),console.log("gallery list <words> --all searches the documents of them all; every command takes --workspace <\u8DEF\u5F84> to work in one.")}async function we(o,t){let{positional:n,options:s,values:r}=A({options:["--scan"],values:["--name","--forget"],argv:o});if([s.has("--scan"),r.has("--name"),r.has("--forget")].filter(Boolean).length>1)throw new w("One of --scan, --name and --forget at a time.");if(n.length>(s.has("--scan")?1:0))throw new w(s.has("--scan")?"--scan takes one folder at most.":"gallery workspaces names no folder: --scan <folder> looks in one, --workspace <dir> names the current workspace.");let i=k.resolve(t.workspace);return r.has("--name")?se(i,r.get("--name")):r.has("--forget")?re(r.get("--forget"),i):s.has("--scan")?te(n[0]):ne(i)}export{oe as a,we as b};
@@ -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{a}from"./chunk-74UW7QSL.mjs";import"./chunk-5L7RYZCL.mjs";export{a as _keyDefinitions};
2
+ import{a}from"./chunk-UH4SIXTR.mjs";import"./chunk-5L7RYZCL.mjs";export{a as _keyDefinitions};
@@ -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{c as Ee,d as X,f as ae,h as ie,i as Ne,l as oe,p as ve}from"./chunk-LE44MD3O.mjs";import{D as ce}from"./chunk-7XDYK3UC.mjs";import{a as Q,f as Ce,g as Ie,n as je,t as Ae}from"./chunk-VG5JEIUQ.mjs";import{b as ye,e as mt}from"./chunk-N3DSBH5S.mjs";mt();import{createHash as ht}from"node:crypto";import{cellsOf as gt}from"../browser/shared/card-size.mjs";import{escapeAttribute as de}from"../browser/shared/escape.mjs";import{attr as N,elementsOf as le,hasClass as _e,walk as Te}from"../browser/shared/html-tree.mjs";import{ATTRIBUTE as He}from"../browser/shared/components/installed.mjs";var q=/^[a-z0-9]+(?:-[a-z0-9]+)*$/,sr=Object.freeze(["data-board-id","data-card-id","data-x","data-y","data-w","data-h"]),De="gallery-board",Fe="gallery-card",Le=e=>(e.tagName==="div"||e.tagName==="section")&&N(e,He)!==void 0,wt=e=>ht("sha256").update(e.replace(/\r\n?/g,`
2
+ import{c as Ee,d as X,f as ae,h as ie,i as Ne,l as oe,p as ve}from"./chunk-KLQP46Y3.mjs";import{D as ce}from"./chunk-E7656LR7.mjs";import{a as Q,f as Ce,g as Ie,n as je,t as Ae}from"./chunk-TKWEX4FD.mjs";import{b as ye,e as mt}from"./chunk-A55HN5OK.mjs";mt();import{createHash as ht}from"node:crypto";import{cellsOf as gt}from"../browser/shared/card-size.mjs";import{escapeAttribute as de}from"../browser/shared/escape.mjs";import{attr as N,elementsOf as le,hasClass as _e,walk as Te}from"../browser/shared/html-tree.mjs";import{ATTRIBUTE as He}from"../browser/shared/components/installed.mjs";var q=/^[a-z0-9]+(?:-[a-z0-9]+)*$/,sr=Object.freeze(["data-board-id","data-card-id","data-x","data-y","data-w","data-h"]),De="gallery-board",Fe="gallery-card",Le=e=>(e.tagName==="div"||e.tagName==="section")&&N(e,He)!==void 0,wt=e=>ht("sha256").update(e.replace(/\r\n?/g,`
3
3
  `)).digest("hex").slice(0,16),kt=e=>ye(e.replace(/^/," "),{sourceCodeLocationInfo:!0});function I(e){let n=[];return Te(kt(e),r=>{if(!_e(r,De))return;let i=le(r).filter(s=>_e(s,Fe)&&s.sourceCodeLocation).map(s=>{let a=le(s).find(Le)??null;a||Te(s,O=>{a??=Le(O)?O:null});let h=s.sourceCodeLocation,g={x:N(s,"data-x"),y:N(s,"data-y"),w:N(s,"data-w"),h:N(s,"data-h")};return{id:N(s,"data-card-id")??null,node:s,board:N(r,"data-board-id")??null,...gt({dataset:g}),instance:a,block:a?N(a,He):null,start:h.startOffset,end:h.endOffset,closed:!!h.endTag}});n.push({id:N(r,"data-board-id")??null,node:r,cards:i})},{inTemplates:!1}),{boards:n,cards:n.flatMap(r=>r.cards)}}function Ot(e){let n=new Set(e.map(({id:s})=>s).filter(s=>typeof s=="string"&&q.test(s))),r=new Set,i=1;return e.map(({id:s})=>{if(typeof s=="string"&&q.test(s)&&!r.has(s))return r.add(s),s;for(;n.has(`c${i}`)||r.has(`c${i}`);)i++;let a=`c${i}`;return r.add(a),a})}function fe(e){let n=new Set(e),r=1;for(;n.has(`c${r}`);)r++;return`c${r}`}function ee(e,n,r){let{startTag:i}=n.sourceCodeLocation,s=i.attrs??{},a=[],h=[];for(let[g,O]of Object.entries(r)){let S=s[g];S?(a.push({start:S.startOffset,end:S.endOffset,text:O===null?"":`${g}="${de(String(O))}"`}),O===null&&e[S.startOffset-1]===" "&&(a.at(-1).start-=1)):O!==null&&h.push(` ${g}="${de(String(O))}"`)}if(h.length){let g=s.class?.endOffset??i.startOffset+1+n.tagName.length;a.push({start:g,end:g,text:h.join("")})}return a}function te(e,n){return[...n].sort((r,i)=>i.start-r.start||i.end-r.end).reduce((r,{start:i,end:s,text:a})=>r.slice(0,i)+a+r.slice(s),e)}function W(e){let{cards:n}=I(e),r=Ot(n),i=n.flatMap((s,a)=>s.id===r[a]?[]:ee(e,s.node,{"data-card-id":r[a]}));return{text:i.length?te(e,i):e,filled:n.flatMap((s,a)=>s.id===r[a]?[]:[r[a]])}}var V=(e,n)=>wt(e.slice(n.start,n.end));function Re(e){let n=W(e).text;return I(n).cards.map(r=>({id:r.id,board:r.board,x:r.x,y:r.y,w:r.w,h:r.h,base:V(n,r),block:r.block}))}var Pe=e=>[e.board,...["x","y","w","h"].map(n=>N(e.node,`data-${n}`))].join(",");function Me(e,n){let r=n.node.sourceCodeLocation.startTag.endOffset;return e.slice(n.start,r).replace(/\s+data-[xywh]="[^"]*"/g,"")+e.slice(r,n.end)}function Je(e,n){let[r,i]=[W(e).text,W(n).text],[s,a]=[I(r).cards,I(i).cards],h={};for(let g of s)a.some(O=>O.id===g.id)||(h[g.id]=["removed"]);for(let g of a){let O=s.find(v=>v.id===g.id);if(!O){h[g.id]=["added"];continue}let S=[...Pe(O)!==Pe(g)?["cells"]:[],...Me(r,O)!==Me(i,g)?["content"]:[]];S.length&&(h[g.id]=S)}return h}function Y(e,n){let{cards:r}=I(e),i=r.flatMap(s=>{let a=n.get(s.id);if(!a)return[];let h={};for(let g of["x","y","w","h"])a[g]!==void 0&&N(s.node,`data-${g}`)!==String(a[g])&&(h[`data-${g}`]=a[g]);return Object.keys(h).length?ee(e,s.node,h):[]});return i.length?te(e,i):e}function xt(e,n,r){let i=e.lastIndexOf(`
4
4
  `,n-1)+1,s=e.indexOf(`
5
5
  `,r),a=s===-1?e.slice(r):e.slice(r,s);return!/^[ \t]*$/.test(e.slice(i,n))||!/^[ \t\r]*$/.test(a)?{from:n,to:r}:{from:i,to:s===-1?e.length:s+1}}function Be(e,n){let r=I(e).cards.find(a=>a.id===n);if(!r)return null;let{from:i,to:s}=xt(e,r.start,r.end);return{text:e.slice(0,i)+e.slice(s),html:e.slice(r.start,r.end)}}var ze=(e,n)=>/^[ \t]*/.exec(e.slice(e.lastIndexOf(`
@@ -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{a as e,b as i,c as r,e as p}from"./chunk-KNZJT4ET.mjs";import"./chunk-5L7RYZCL.mjs";var f=(o,t,m)=>p(o,t,m,r);export{e as Parser,f as applyExtends,i as hideBin};
2
+ import{a as e,b as i,c as r,e as p}from"./chunk-QAIHRH3I.mjs";import"./chunk-5L7RYZCL.mjs";var f=(o,t,m)=>p(o,t,m,r);export{e as Parser,f as applyExtends,i as hideBin};
@@ -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{i as p}from"./chunk-S3SX3COT.mjs";import{g as b}from"./chunk-VRPVVMRB.mjs";import{g as k}from"./chunk-BX5AKINW.mjs";import{k as d}from"./chunk-FJLM7YE7.mjs";import{workspaceBase as v}from"../browser/shared/service-location.mjs";import j from"node:fs/promises";import f from"node:path";import{editStateDirectory as m,reportsDirectory as h}from"../browser/shared/paths.mjs";import{findPreviewServer as $}from"../browser/shared/preview-server.mjs";import{documentDirectory as S,documentFiles as g,isDocumentSource as D}from"../browser/shared/workspace.mjs";var u=e=>f.resolve(e),N=e=>f.basename(g(f.dirname(e)).json),O={busy:"it kept changing while it was being written (an Agent writing it?); nothing written. Run the export again.",timeout:"the workspace's Gallery server did not answer in 30s; nothing written. Run the export again."};async function w(e,n,o="hand-in",l=3e4){let t=`127.0.0.1:${e.port}`,s;try{s=await fetch(`http://${t}${v(h)}/__gallery/edit/${o}`,{method:"POST",headers:{"Content-Type":"application/json",Origin:`http://${t}`,"X-Gallery-Token":e.token},body:JSON.stringify({slug:n,workspace:h}),signal:AbortSignal.timeout(l)})}catch(a){return a.name==="TimeoutError"?{ok:!1,reason:"timeout"}:null}let i=await s.json().catch(()=>null);return typeof i?.ok!="boolean"||["elsewhere","token","host","origin"].includes(i.reason)?null:i}async function C(e){let n=await $(),o=null,l=[],t=new Map,s=[];for(let i of e){let{html:a,json:c}=g(S(i,h)),r=n?await w(n,i):null;if(r===null&&(o??=p({reportsDirectory:h,stateDirectory:m,finish:d}),r=await o.handIn(i),r.ok&&await o.settleNotes(i)),!r.ok){r.reason!=="missing"&&t.set(i,[`${u(a)} was not handed in: ${O[r.reason]??r.message??r.reason}`]);continue}if(r.repeated?.length){t.set(i,k(r.repeated));continue}s.push(...R(a,r)),r.written&&(r.changes.length&&s.push(...b(a,r.changes,{reread:!1})),r.added&&s.push(...L(a,r.given,r.added)),l.push(u(a))),T(r)&&l.push(u(c))}return{written:l,failures:t,lines:s}}async function H(e){let n=await $();if(n){for(let o of e)if(await w(n,o,"settle")===null)return}}async function K(e,{wait:n=3e4}={}){try{let o=await $(),l=o?await w(o,e,"round",n):null;return l!==null?l.ok?l.ended===!0:null:await p({reportsDirectory:h,stateDirectory:m,finish:d}).beginRound(e)}catch{return null}}async function W(e){try{let n=p({reportsDirectory:h,stateDirectory:m,finish:d}),{html:o}=g(S(e,h));await n.noteTaken(e,await j.readFile(o,"utf8"))}catch{}}var T=({title:e=null,icon:n=null})=>!!(e&&typeof e.from=="string"&&e.from!==e.to||n&&!n.missing),x=10;function L(e,n,o=n?.length??0,{pending:l=!1}={}){let t=`${o} block${o===1?"":"s"}`;if(l){let i=[`ids ${u(e)}: ${t} without an id yet: the export gives ${o===1?"it":"each"} one`];return o>x?i:[...i,...n.map(({line:a,tag:c})=>` line ${a} ${c}`)]}let s=[`ids ${u(e)}: ${t} given an id`];return!n||o>x?s:[...s,...n.map(({line:i,tag:a,id:c})=>` line ${i} ${a} \u2192 ${c}`)]}function R(e,{title:n=null,icon:o=null}={},{pending:l=!1}={}){let t=u(f.dirname(e)),s=N(e),i=(c,r,y)=>l?`${c} not ${r} yet (${y}): the export writes ${c.includes(" and ")?"them":"it"}`:`${c} written ${r}: ${y}`,a=[];if(n){let c=[...n.page?["<title>"]:[],...typeof n.from=="string"&&n.from!==n.to?[`${s}'s title`]:[]];c.length&&a.push(`title ${t}: ${i(c.join(" and "),"as its h1 says it",JSON.stringify(n.to))}`)}return o?.missing?a.push(`icon ${t}: ${s} has the icon ${JSON.stringify(o.from)} and the page's header none: write it there (<div class="doc-icon">\u2026</div> before the h1) or take "icon" out of ${s} (the export leaves ${s}'s as it is)`):o&&a.push(`icon ${t}: ${i(`${s}'s icon`,"as its header has it",o.to===null?"none":JSON.stringify(o.to))}`),a}function X(e,{by:n="this export"}={}){let o=e.length===1,l=[...e.filter(t=>D(f.basename(t))).map(t=>f.basename(t)).filter((t,s,i)=>i.indexOf(t)===s).map(t=>`${t}: the kit's markup, block ids`),...e.filter(t=>/^(doc|report)\.json$/.test(f.basename(t))).map(t=>f.basename(t)).filter((t,s,i)=>i.indexOf(t)===s).map(t=>`${t}: the title or icon said above`)];return`Re-read ${e.join(", ")} before editing ${o?"it":"them"}: ${n} wrote ${o?"it":"them"} (${l.join("; ")}).`}export{C as a,H as b,K as c,W as d,L as e,R as f,X as g};
@@ -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{d as v}from"./chunk-2OHANJ6Q.mjs";import t from"node:fs/promises";import a from"node:path";import{randomBytes as F}from"node:crypto";import{defaultWorkspace as k,layoutOf as M}from"../browser/shared/workspace.mjs";import{withOutlineLock as L}from"../browser/shared/outline.mjs";import{extensionDirectory as P,extensionPath as x,forgetExtensionGrant as z,readManifest as O}from"../browser/shared/extension-manifest.mjs";import{findPreviewServer as T}from"../browser/shared/preview-server.mjs";var b=100*1024*1024,B=1e4;async function I(e,p){let c=await T({answering:!0});if(!c)return null;let n=`http://127.0.0.1:${c.port}`,o=await fetch(`${n}/__gallery/extensions/${e}`,{method:"POST",headers:{Origin:n,"Content-Type":"application/json","X-Gallery-Token":c.token},body:JSON.stringify(p)}),y=await o.json();if(!o.ok)throw new Error(y.message??"\u6269\u5C55\u64CD\u4F5C\u5931\u8D25");return y}async function N(e,{inService:p=!1,beforeReplace:c=async()=>{}}={}){if(e=a.resolve(e),!p){let s=await I("install",{path:e});if(s)return s.installed}let n=M(k()).extensions;if(await t.mkdir(n,{recursive:!0}),(await t.lstat(n)).isSymbolicLink())throw new Error("\u6269\u5C55\u76EE\u5F55\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5");let o=await t.mkdtemp(a.join(n,".install-")),y=0,Z=0,d=s=>{if(y+=s,++Z>B||y>b)throw new Error("\u6269\u5C55\u8D85\u8FC7 100MB \u6216 10000 \u4E2A\u6587\u4EF6")};try{let s=await t.lstat(e);if(s.isSymbolicLink())throw new Error("\u5B89\u88C5\u6E90\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5");if(s.isDirectory()){let r=async(f,u)=>{for(let w of await t.readdir(f,{withFileTypes:!0})){x(w.name);let i=a.join(f,w.name),m=a.join(u,w.name);if(w.isDirectory())d(0),await t.mkdir(m),await r(i,m);else if(w.isFile())d((await t.stat(i)).size),await t.copyFile(i,m);else throw new Error("\u6269\u5C55\u53EA\u80FD\u5305\u542B\u666E\u901A\u6587\u4EF6\u548C\u6587\u4EF6\u5939")}};await r(e,o)}else if(s.isFile()&&e.toLowerCase().endsWith(".zip")){if(s.size>b)throw new Error("\u6269\u5C55 ZIP \u8D85\u8FC7 100MB");let r=v(e);try{let f=new Set,u=r.entries.find(i=>i.name==="extension.json")??r.entries.find(i=>/^[^/]+\/extension.json$/.test(i.name));if(!u)throw new Error("ZIP \u4E2D\u6CA1\u6709 extension.json");let w=u.name.slice(0,-14);for(let i of r.entries){if(x(i.name),!i.name.startsWith(w))throw new Error("ZIP \u7684\u6587\u4EF6\u5FC5\u987B\u5728\u540C\u4E00\u4E2A\u6269\u5C55\u6587\u4EF6\u5939\u5185");let m=i.name.slice(w.length),$=x(m),S=m.toLowerCase();if(f.has(S))throw new Error("ZIP \u4E2D\u5B58\u5728\u91CD\u540D\u6587\u4EF6");f.add(S),d(i.size);let j=a.join(o,...$);await t.mkdir(a.dirname(j),{recursive:!0}),await r.extract(i,j)}}finally{r.close()}}else throw new Error("\u8BF7\u9009\u62E9\u6269\u5C55\u6587\u4EF6\u5939\u6216 ZIP");let E=await O(o),h=E.id??a.basename(e).replace(/\.zip$/i,""),l=P(h),g=a.join(n,`.old-${F(8).toString("hex")}`);return await c(h),await L(k(),async()=>{let r=await t.lstat(l).catch(()=>null);if(r?.isSymbolicLink())throw new Error("\u5DF2\u5B89\u88C5\u7684\u6269\u5C55\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5");await z(h),r&&await t.rename(l,g);try{await t.rename(o,l)}catch(f){throw r&&await t.rename(g,l),f}r&&await t.rm(g,{recursive:!0,force:!0})}),{id:h,folder:l,agent:E.agent?a.join(l,E.agent):null}}finally{await t.rm(o,{recursive:!0,force:!0})}}async function Y(e,{inService:p=!1,beforeReplace:c=async()=>{}}={}){if(!p&&await I("remove",{id:e}))return;let n=P(e);if((await t.lstat(a.dirname(n))).isSymbolicLink())throw new Error("\u6269\u5C55\u76EE\u5F55\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5");await c(e),await L(k(),async()=>{if((await t.lstat(n)).isSymbolicLink())throw new Error("\u6269\u5C55\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5");await z(e),await t.rm(n,{recursive:!0})})}export{N as a,Y as b};
@@ -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{a as U,b as Z,c as ot,d as q}from"./chunk-XKOHV4H6.mjs";import{e as L}from"./chunk-KFZBYDIS.mjs";import{D as nt,a as _,b as V,l as tt,m as D,o as et}from"./chunk-7XDYK3UC.mjs";import{b as J,c as P,e as Q}from"./chunk-N3DSBH5S.mjs";import{IDENTITY as Ne}from"../browser/editor/components/registry.mjs";function gt(e){let{blocks:a,locks:r,units:n,boxes:c}=L(e),l=wt(e,[...a.map(t=>({at:t.innerStart-1,text:` data-ge-block="${t.id}"`})),...r.map(t=>({at:t.at,text:` data-ge-lock="${t.reason}"`})),...n.map(t=>({at:t.at,text:` data-ge-unit="${t.id}"${t.grid?" data-ge-grid":""}${t.keep?` data-ge-keep="${t.keep.reason}"${t.keep.own?" data-ge-keep-own":""}`:""}`})),...n.filter(t=>t.lead!==null).map(t=>({at:t.lead,text:` data-ge-lead="${t.id}"`})),...c.map(t=>({at:t.at,text:` data-ge-box="${t.id}"${t.pure?" data-ge-pure":""}`}))]),p={version:_(e),blocks:a.map(t=>({id:t.id,tag:t.tag,kind:t.kind,caps:Object.keys(t.caps),inner:e.slice(t.innerStart,t.innerEnd),atoms:t.atoms,unit:t.unit,...t.path?{path:t.path}:{},...t.self!==null?{self:!0}:{}})),units:n.map(t=>({id:t.id,kind:t.kind,parent:t.parent,hash:D(e,t),...t.blockId?{blockId:t.blockId}:{},...t.keep?{keep:t.keep}:{},...t.grid?{grid:!0}:{},...t.parts?{parts:t.parts}:{}})),boxes:c.map(t=>({id:t.id,kind:t.kind,unit:t.unit,...t.key!==null?{key:t.key}:{}}))};return{html:l,map:p}}function wt(e,a,r=!0){let n=[],c=0;for(let l of[...a].sort((p,t)=>p.at-t.at)){if(e[l.at]!==">"){if(r)throw new Error(`Unexpected start tag before offset ${l.at}.`);continue}let p=et(e,l.at);n.push(e.slice(c,p),l.text),c=p}return n.push(e.slice(c)),n.join("")}Q();import{COMPONENTS as kt,instancesOf as xt,partOf as st}from"../browser/shared/components/index.mjs";import{attr as rt}from"../browser/shared/html-tree.mjs";var at=new Set(["main","section","article","aside","nav","header","footer","div","details","dialog","figure","blockquote","table","ul","ol","dl"]),Nt=new Set(["script","style","template","textarea","title","xmp","iframe","noembed","noframes","noscript","plaintext","pre","code","kbd","samp","var","svg","math"]),It=kt.filter(e=>e.match&&e.pairs);function Ot(e){let a=e.replace(/^/," "),r=J(a,{sourceCodeLocationInfo:!0}),n=f=>a.slice(0,f).split(`
2
+ import{a as U,b as Z,c as ot,d as q}from"./chunk-W5NPH5UA.mjs";import{e as L}from"./chunk-ECZBLQKK.mjs";import{D as nt,a as _,b as V,l as tt,m as D,o as et}from"./chunk-E7656LR7.mjs";import{b as J,c as P,e as Q}from"./chunk-A55HN5OK.mjs";import{IDENTITY as Ne}from"../browser/editor/components/registry.mjs";function gt(e){let{blocks:a,locks:r,units:n,boxes:c}=L(e),l=wt(e,[...a.map(t=>({at:t.innerStart-1,text:` data-ge-block="${t.id}"`})),...r.map(t=>({at:t.at,text:` data-ge-lock="${t.reason}"`})),...n.map(t=>({at:t.at,text:` data-ge-unit="${t.id}"${t.grid?" data-ge-grid":""}${t.keep?` data-ge-keep="${t.keep.reason}"${t.keep.own?" data-ge-keep-own":""}`:""}`})),...n.filter(t=>t.lead!==null).map(t=>({at:t.lead,text:` data-ge-lead="${t.id}"`})),...c.map(t=>({at:t.at,text:` data-ge-box="${t.id}"${t.pure?" data-ge-pure":""}`}))]),p={version:_(e),blocks:a.map(t=>({id:t.id,tag:t.tag,kind:t.kind,caps:Object.keys(t.caps),inner:e.slice(t.innerStart,t.innerEnd),atoms:t.atoms,unit:t.unit,...t.path?{path:t.path}:{},...t.self!==null?{self:!0}:{}})),units:n.map(t=>({id:t.id,kind:t.kind,parent:t.parent,hash:D(e,t),...t.blockId?{blockId:t.blockId}:{},...t.keep?{keep:t.keep}:{},...t.grid?{grid:!0}:{},...t.parts?{parts:t.parts}:{}})),boxes:c.map(t=>({id:t.id,kind:t.kind,unit:t.unit,...t.key!==null?{key:t.key}:{}}))};return{html:l,map:p}}function wt(e,a,r=!0){let n=[],c=0;for(let l of[...a].sort((p,t)=>p.at-t.at)){if(e[l.at]!==">"){if(r)throw new Error(`Unexpected start tag before offset ${l.at}.`);continue}let p=et(e,l.at);n.push(e.slice(c,p),l.text),c=p}return n.push(e.slice(c)),n.join("")}Q();import{COMPONENTS as kt,instancesOf as xt,partOf as st}from"../browser/shared/components/index.mjs";import{attr as rt}from"../browser/shared/html-tree.mjs";var at=new Set(["main","section","article","aside","nav","header","footer","div","details","dialog","figure","blockquote","table","ul","ol","dl"]),Nt=new Set(["script","style","template","textarea","title","xmp","iframe","noembed","noframes","noscript","plaintext","pre","code","kbd","samp","var","svg","math"]),It=kt.filter(e=>e.match&&e.pairs);function Ot(e){let a=e.replace(/^/," "),r=J(a,{sourceCodeLocationInfo:!0}),n=f=>a.slice(0,f).split(`
3
3
  `).length,c=[],l=(f,d,s)=>c.push({key:f,tag:a.slice(d,s).replace(/\s+/g," "),line:n(d),offset:d});V(r,"main")||l("no <main> in the file",0,0);let p=[],t=new Set,k=f=>{for(let d of f.childNodes??[]){let s=d.sourceCodeLocation;if(d.nodeName==="#comment"){s&&p.push([s.startOffset,s.endOffset]);continue}if(d.tagName){if(s?.startTag&&p.push([s.startTag.startOffset,s.startTag.endOffset]),Nt.has(d.tagName)){s&&p.push([s.startOffset,s.endOffset??a.length]);continue}s?.endTag&&t.add(s.endTag.startOffset),at.has(d.tagName)&&s?.startTag&&!s.endTag&&l(`<${d.tagName}> not closed`,s.startTag.startOffset,s.startTag.endOffset),k(d)}}};k(r),p.sort((f,d)=>f[0]-d[0]);let N="",h=0;for(let[f,d]of p)d<=h||(N+=a.slice(h,Math.max(h,f))+" ".repeat(d-Math.max(h,f)),h=d);N+=a.slice(h);for(let f of N.matchAll(/<\/([A-Za-z][A-Za-z0-9]*)[^<>]*>?/g)){let d=f[1].toLowerCase();at.has(d)&&!t.has(f.index)&&l(`</${d}> closes nothing`,f.index,f.index+f[0].length)}for(let f of It)for(let d of xt(r,f))for(let{a:s,attrA:g,b:x,attrB:A}of f.pairs){let o=st(d,f.parts[s].at).map(M=>rt(M,g)),u=st(d,f.parts[x].at).map(M=>rt(M,A)),j=M=>new Set(M).size===M.length,I=j(o)&&j(u)&&o.length===u.length&&o.every(M=>u.includes(M)),b=d.sourceCodeLocation;!I&&b&&l(`${f.id}: ${s} and ${x} do not pair`,b.startOffset,b.startTag?.endOffset??b.startOffset)}return c.sort((f,d)=>f.offset-d.offset).map(({key:f,tag:d,line:s})=>({key:f,tag:d,line:s,text:`${f} (line ${s})`}))}Q();import{treeText as z}from"../browser/shared/annotations.mjs";import{attr as F}from"../browser/shared/html-tree.mjs";var it=2e3*2e3,G=2e3,ct=50,lt=1500;function ft(e,a,r,n){let c="",l=a;for(let p of n)p.end<=l||p.start>=r||(p.start>l&&(c+=e.slice(l,p.start)),l=Math.max(l,p.end));return c+e.slice(l,r)}var dt=e=>e.replace(/\s+/g," ").trim(),bt=e=>/\S/.test(e.replace(/<!--[^]*?-->/g,"").replace(/<[^>]*>/g,"").replace(/\u0000/g,"")),pt=e=>e.companion?[{start:e.start,end:e.end},{start:e.companion.start,end:e.companion.end}]:[{start:e.start,end:e.end}];function H(e){let a=L(e),{units:r,blocks:n}=a,c=r[0]?.node??n[0]?.node??null;for(;c?.parentNode;)c=c.parentNode;let l=c?tt(e,[c]):[],p=[...r].sort((o,u)=>o.start-u.start||u.end-o.end),t=new Map,k=[];for(let o of p){for(;k.length&&k.at(-1).end<=o.start;)k.pop();t.set(o,k.at(-1)??null),k.push(o)}for(let o of r.filter(u=>u.companion)){let{start:u,end:j}=o.companion;for(let I of r){if(I===o||I.start<u||I.end>j)continue;let b=t.get(I);(!b||b.start<u)&&t.set(I,o)}}let N=new Map;for(let o of r){let u=t.get(o);u&&N.set(u,[...N.get(u)??[],o])}let h=n.find(o=>o.kind==="title")??null,f=[...r.map(o=>({unit:o,at:o.elStart})),...h?[{unit:null,at:h.start}]:[]].sort((o,u)=>o.at-u.at),d=new Map(f.map((o,u)=>[o.unit,u])),s=new Map;for(let o of r)o.blockId&&s.set(o.blockId,(s.get(o.blockId)??0)+1);let g=f.map(({unit:o})=>{if(!o){let $=dt(ft(e,h.start,h.end,l));return{unit:null,title:!0,kind:"title",key:`title\0${$}`,own:$,counts:1,parent:null,blockId:null,text:z(h.node).slice(0,G)}}let u=N.get(o)??[],j=[...u.flatMap(pt),...l].sort(($,R)=>$.start-R.start),I=dt(pt(o).map(({start:$,end:R})=>ft(e,$,R,j)).join("\0")),b=u.length>0&&!bt(I),M=t.get(o);return{unit:o,kind:o.kind,key:`${o.kind}\0${I}`,own:I,counts:b?0:1,parent:M?d.get(M):null,blockId:o.blockId,text:z(o.node).slice(0,G)}}),x=null;return{rows:g,scanned:a,ref:o=>o.title?{title:!0}:o.blockId&&s.get(o.blockId)===1?{id:o.blockId}:(x??=(()=>{let u=new Map,j=new Map;for(let{unit:I}of f){if(!I)continue;let b=D(e,I);j.set(I,{hash:b,nth:u.get(b)??0}),u.set(b,(u.get(b)??0)+1)}return j})(),x.get(o.unit))}}function St(e,a){if(e.length*a.length<=it)return Z(e,a);let r=s=>s.reduce((g,x)=>g.set(x,(g.get(x)??0)+1),new Map),[n,c]=[r(e),r(a)],l=new Map;a.forEach((s,g)=>{c.get(s)===1&&n.get(s)===1&&l.set(s,g)});let p=[];e.forEach((s,g)=>{l.has(s)&&p.push([g,l.get(s)])});let t=[],k=[];p.forEach(([,s],g)=>{let x=0,A=t.length;for(;x<A;){let o=x+A>>1;p[t[o]][1]<s?x=o+1:A=o}k[g]=x>0?t[x-1]:-1,t[x]=g});let N=[];for(let s=t.at(-1)??-1;s>=0;s=k[s])N.unshift(p[s]);let h=[],[f,d]=[0,0];for(let[s,g]of[...N,[e.length,a.length]]){if((s-f)*(g-d)<=it)for(let[x,A]of Z(e.slice(f,s),a.slice(d,g)))h.push([f+x,d+A]);s<e.length&&h.push([s,g]),[f,d]=[s+1,g+1]}return h}var ut=(e,a)=>a.nested?z(P(e.slice(a.innerStart,a.innerEnd))):z(a.node),mt=e=>e.match(/[A-Za-z0-9]+|\s+|[^A-Za-z0-9\s]/gu)?.length??0,ht=({scanned:e},a)=>(a.title?e.blocks.filter(r=>r.kind==="title"):e.blocks.filter(r=>r.unit===a.unit.id)).sort((r,n)=>r.innerStart-n.innerStart);function yt(e){let a=[],r=n=>{for(let c of n.childNodes??[])c.tagName==="tr"?a.push((c.childNodes??[]).filter(l=>l.tagName==="td"||l.tagName==="th").map(l=>z(l).replace(/\s+/g," ")).join(" \xB7 ")):c.tagName&&r(c.tagName==="template"?c.content:c)};return r(e),a.length?a.join(`
4
4
  `):z(e)}function Mt(e,a,r){let n=e[a],c=0;for(let N=n.parent;N!==null;N=e[N].parent)r(N)&&(c+=1);let l=n.title?"h1":n.unit.node.tagName,p=n.title?n.text:yt(P(n.own.replace(/\u0000/g," "))).slice(0,G),t={kind:n.kind,tag:l,depth:c,text:p},k=l==="li"?n.unit.node.parentNode:null;if((k?.tagName==="ul"||k?.tagName==="ol")&&(t.marker=k.tagName,k.tagName==="ol")){let N=(k.childNodes??[]).filter(h=>h.tagName==="li");t.number=(Number.parseInt(F(k,"start")??"1",10)||1)+N.indexOf(n.unit.node)}return t}function $t(e,a){if(e===a)return null;let r=H(e),n=H(a),[c,l]=[r.rows.map(i=>i.key),n.rows.map(i=>i.key)],p=St(c,l);if(p.length===c.length&&p.length===l.length)return null;let t=i=>{let w=new Map;for(let S of i)S.blockId&&w.set(S.blockId,(w.get(S.blockId)??0)+1);return S=>!!S.blockId&&w.get(S.blockId)===1},[k,N]=[t(r.rows),t(n.rows)],h=[],[f,d]=[0,0];for(let[i,w]of[...p,[c.length,l.length]]){let S=new Map;for(let m=d;m<w;m++)N(n.rows[m])&&S.set(n.rows[m].blockId,m);let T=[],v=d-1;for(let m=f;m<i;m++){let O=k(r.rows[m])?S.get(r.rows[m].blockId):void 0;O!==void 0&&O>v&&(T.push([m,O]),v=O)}let B=[],[y,E]=[f,d];for(let[m,O]of[...T,[i,w]]){for(;y<m;y++){let C=r.rows[y];for(let W=E;W<O;W++){let Y=n.rows[W];if(Y.kind===C.kind&&(C.title||ot(C.own,Y.own))){B.push([y,W]),E=W+1;break}}}m<i&&B.push([m,O]),[y,E]=[m+1,O+1]}B.sort((m,O)=>m[0]-O[0]),[y,E]=[f,d];for(let[m,O]of[...B,[i,w]]){for(;y<m;y++)h.push({type:"del",i:y});for(;E<O;E++)h.push({type:"ins",j:E});m<i&&h.push({type:"chg",i:m,j:O}),[y,E]=[m+1,O+1]}i<c.length&&h.push({type:"same",i,j:w}),[f,d]=[i+1,w+1]}let s=new Set(h.filter(i=>i.type==="ins").map(i=>i.j)),g=new Set(h.filter(i=>i.type==="del").map(i=>i.i)),x=(i,w)=>{let S=new Set;for(let T of w)if(i[T].counts)for(let v=i[T].parent;v!==null;v=i[v].parent)S.add(v);return T=>i[T].counts||(S.has(T)?0:1)},[A,o]=[x(n.rows,s),x(r.rows,g)],u={added:0,changed:0,removed:0},j=[],I=[],b=[],M,$=null,R=null,K=i=>{if(!$)return;let w=Math.max(0,$.rows.length-ct);b.push({...i!==null?{before:n.ref(n.rows[i])}:R!==null?{after:n.ref(n.rows[R])}:{into:"main"},count:$.count,rows:$.rows.slice(0,ct),...w?{more:w}:{}}),$=null},X=i=>g.has(i)&&r.rows[i].counts>0;for(let i of h){if(i.type==="del"){$??={count:0,rows:[]},$.count+=o(i.i),u.removed+=o(i.i),X(i.i)&&$.rows.push(Mt(r.rows,i.i,X));continue}if(K(i.j),R=i.j,i.type==="ins"){let w=n.rows[i.j];u.added+=A(i.j),(w.parent===null||!s.has(w.parent))&&j.push({ref:n.ref(w)})}else if(i.type==="chg"){let[w,S]=[r.rows[i.i],n.rows[i.j]];u.changed+=1;let T=ht(r,w),v=ht(n,S),B=[],y=T.length!==v.length||T.length===0||T.some((E,m)=>E.tag!==v[m].tag);y||(T.forEach((E,m)=>{let[O,C]=[ut(e,E),ut(a,v[m])];mt(O)>lt||mt(C)>lt?y=!0:O!==C&&B.push({nth:m,base:O})}),B.length===0&&(y=!0)),y&&(B=[]),I.push({ref:n.ref(S),...y?{whole:!0}:{texts:B}}),S.title&&(M={base:w.text})}}return K(null),{counts:u,added:j,changed:I,...M?{title:M}:{},removed:b}}function Tt(e){let{units:a}=L(e),r=c=>{for(let l=c;l;l=l.parentNode)if(l.tagName&&(F(l,"data-doc-placeholder")!==void 0||l.tagName==="header"||(F(l,"class")??"").split(/\s+/).includes("doc-header")))return!1;return!0},n=c=>/^(img|picture|svg|canvas|video|audio|iframe|object|embed)$/.test(c.tagName??"")||(c.childNodes??[]).some(n);return!a.some(c=>r(c.node)&&(z(c.node)!==""||n(c.node)))}function jt(e,a){try{return q({current:e,ids:!0,...a}).text}catch{return e}}function Et(e,a){try{let r=L(e).blocks.find(c=>c.kind==="title");if(!r)return e;let n=U({current:e,edits:[{id:r.id,parts:[{keep:!0,html:a}]}],ids:!1});return n.ok?n.text:e}catch{return e}}var we=(e,a,r,n)=>nt.installed.rewrite(e,a,r,{packages:n});export{gt as a,Ot as b,$t as c,Tt as d,jt as e,Et as f,we as g};