@dooc-dev/dooc 0.0.0-stage → 0.0.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.
Files changed (221) hide show
  1. package/README.md +26 -2
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +311 -0
  4. package/dist/code.global.js +285 -0
  5. package/dist/compiler/bundle-urls.d.ts +3 -0
  6. package/dist/compiler/bundle-urls.js +294 -0
  7. package/dist/compiler/bundle.d.ts +2 -0
  8. package/dist/compiler/bundle.js +100 -0
  9. package/dist/compiler/check-prose.d.ts +11 -0
  10. package/dist/compiler/check-prose.js +138 -0
  11. package/dist/compiler/check-types.d.ts +44 -0
  12. package/dist/compiler/check-types.js +1 -0
  13. package/dist/compiler/check.d.ts +5 -0
  14. package/dist/compiler/check.js +450 -0
  15. package/dist/compiler/code-blocks.d.ts +17 -0
  16. package/dist/compiler/code-blocks.js +198 -0
  17. package/dist/compiler/code-languages.d.ts +10 -0
  18. package/dist/compiler/code-languages.js +109 -0
  19. package/dist/compiler/compile.d.ts +95 -0
  20. package/dist/compiler/compile.js +410 -0
  21. package/dist/compiler/config.d.ts +7 -0
  22. package/dist/compiler/config.js +52 -0
  23. package/dist/compiler/dialect.d.ts +24 -0
  24. package/dist/compiler/dialect.js +198 -0
  25. package/dist/compiler/drawio.d.ts +26 -0
  26. package/dist/compiler/drawio.js +176 -0
  27. package/dist/compiler/figures.d.ts +14 -0
  28. package/dist/compiler/figures.js +221 -0
  29. package/dist/compiler/file-cache.d.ts +14 -0
  30. package/dist/compiler/file-cache.js +61 -0
  31. package/dist/compiler/files.d.ts +29 -0
  32. package/dist/compiler/files.js +431 -0
  33. package/dist/compiler/fonts.d.ts +10 -0
  34. package/dist/compiler/fonts.js +54 -0
  35. package/dist/compiler/format.d.ts +1 -0
  36. package/dist/compiler/format.js +42 -0
  37. package/dist/compiler/from-mdast.d.ts +16 -0
  38. package/dist/compiler/from-mdast.js +142 -0
  39. package/dist/compiler/icons.d.ts +14 -0
  40. package/dist/compiler/icons.js +85 -0
  41. package/dist/compiler/language-codes.d.ts +7 -0
  42. package/dist/compiler/language-codes.js +17 -0
  43. package/dist/compiler/locales.d.ts +7 -0
  44. package/dist/compiler/locales.js +67 -0
  45. package/dist/compiler/manifest.d.ts +6 -0
  46. package/dist/compiler/manifest.js +30 -0
  47. package/dist/compiler/ops.d.ts +44 -0
  48. package/dist/compiler/ops.js +362 -0
  49. package/dist/compiler/public-file.d.ts +2 -0
  50. package/dist/compiler/public-file.js +93 -0
  51. package/dist/compiler/serialize.d.ts +3 -0
  52. package/dist/compiler/serialize.js +16 -0
  53. package/dist/compiler/terms.d.ts +9 -0
  54. package/dist/compiler/terms.js +44 -0
  55. package/dist/compiler/textfile-remote.d.ts +12 -0
  56. package/dist/compiler/textfile-remote.js +124 -0
  57. package/dist/compiler/textfile.d.ts +9 -0
  58. package/dist/compiler/textfile.js +113 -0
  59. package/dist/compiler/theme-variants.d.ts +4 -0
  60. package/dist/compiler/theme-variants.js +22 -0
  61. package/dist/compiler/zip.d.ts +6 -0
  62. package/dist/compiler/zip.js +48 -0
  63. package/dist/core/blocks.d.ts +99 -0
  64. package/dist/core/blocks.js +29 -0
  65. package/dist/core/code-file.d.ts +12 -0
  66. package/dist/core/code-file.js +68 -0
  67. package/dist/core/content-metadata.d.ts +48 -0
  68. package/dist/core/content-metadata.js +264 -0
  69. package/dist/core/content-types.d.ts +45 -0
  70. package/dist/core/content-types.js +1 -0
  71. package/dist/core/extension-package.d.ts +34 -0
  72. package/dist/core/extension-package.js +147 -0
  73. package/dist/core/figure-frame.d.ts +12 -0
  74. package/dist/core/figure-frame.js +108 -0
  75. package/dist/core/file-embed.d.ts +13 -0
  76. package/dist/core/file-embed.js +43 -0
  77. package/dist/core/files.d.ts +15 -0
  78. package/dist/core/files.js +59 -0
  79. package/dist/core/flow-base-css.generated.d.ts +1 -0
  80. package/dist/core/flow-base-css.generated.js +4 -0
  81. package/dist/core/flow-css.d.ts +9 -0
  82. package/dist/core/flow-css.js +54 -0
  83. package/dist/core/graph-common.d.ts +32 -0
  84. package/dist/core/graph-common.js +113 -0
  85. package/dist/core/graph.d.ts +117 -0
  86. package/dist/core/graph.js +480 -0
  87. package/dist/core/html.d.ts +29 -0
  88. package/dist/core/html.js +444 -0
  89. package/dist/core/icons.d.ts +2 -0
  90. package/dist/core/icons.js +2 -0
  91. package/dist/core/inline.d.ts +27 -0
  92. package/dist/core/inline.js +42 -0
  93. package/dist/core/language.d.ts +41 -0
  94. package/dist/core/language.js +44 -0
  95. package/dist/core/languages/ja.d.ts +2 -0
  96. package/dist/core/languages/ja.js +5 -0
  97. package/dist/core/markdown.d.ts +27 -0
  98. package/dist/core/markdown.js +154 -0
  99. package/dist/core/previewers.d.ts +37 -0
  100. package/dist/core/previewers.js +103 -0
  101. package/dist/core/routing.d.ts +20 -0
  102. package/dist/core/routing.js +64 -0
  103. package/dist/core/schema-comments.d.ts +14 -0
  104. package/dist/core/schema-comments.js +52 -0
  105. package/dist/core/schema-graph-flow.d.ts +9 -0
  106. package/dist/core/schema-graph-flow.js +379 -0
  107. package/dist/core/schema-graph-mount.d.ts +9 -0
  108. package/dist/core/schema-graph-mount.js +105 -0
  109. package/dist/core/schema-graph.d.ts +254 -0
  110. package/dist/core/schema-graph.js +981 -0
  111. package/dist/core/table-ui.d.ts +2 -0
  112. package/dist/core/table-ui.js +180 -0
  113. package/dist/core/taxonomy.d.ts +12 -0
  114. package/dist/core/taxonomy.js +49 -0
  115. package/dist/core/term-reference.d.ts +5 -0
  116. package/dist/core/term-reference.js +19 -0
  117. package/dist/core/terms-ui.d.ts +6 -0
  118. package/dist/core/terms-ui.js +22 -0
  119. package/dist/core/textfile.d.ts +10 -0
  120. package/dist/core/textfile.js +39 -0
  121. package/dist/core/theme-variants.d.ts +3 -0
  122. package/dist/core/theme-variants.js +37 -0
  123. package/dist/core/toc-ui.d.ts +2 -0
  124. package/dist/core/toc-ui.js +161 -0
  125. package/dist/core/video-ui.d.ts +2 -0
  126. package/dist/core/video-ui.js +253 -0
  127. package/dist/core/workspace.d.ts +15 -0
  128. package/dist/core/workspace.js +28 -0
  129. package/dist/edit.global.js +306 -0
  130. package/dist/flow.global.js +4959 -0
  131. package/dist/index.d.ts +17 -0
  132. package/dist/index.js +31 -0
  133. package/dist/mcp.d.ts +24 -0
  134. package/dist/mcp.js +467 -0
  135. package/dist/plugins/actions.d.ts +45 -0
  136. package/dist/plugins/actions.js +125 -0
  137. package/dist/plugins/blocks.d.ts +93 -0
  138. package/dist/plugins/blocks.js +100 -0
  139. package/dist/plugins/index.d.ts +4 -0
  140. package/dist/plugins/index.js +1 -0
  141. package/dist/plugins/local-storage.d.ts +18 -0
  142. package/dist/plugins/local-storage.js +115 -0
  143. package/dist/plugins/oauth.d.ts +14 -0
  144. package/dist/plugins/oauth.js +170 -0
  145. package/dist/plugins/runtime.d.ts +26 -0
  146. package/dist/plugins/runtime.js +395 -0
  147. package/dist/plugins/types.d.ts +59 -0
  148. package/dist/plugins/types.js +1 -0
  149. package/dist/plugins/view.d.ts +7 -0
  150. package/dist/plugins/view.js +21 -0
  151. package/dist/ui.global.js +78 -0
  152. package/dist/viewer/assets.d.ts +2 -0
  153. package/dist/viewer/assets.js +3 -0
  154. package/dist/viewer/build.d.ts +12 -0
  155. package/dist/viewer/build.js +55 -0
  156. package/dist/viewer/bundle-network.d.ts +2 -0
  157. package/dist/viewer/bundle-network.js +88 -0
  158. package/dist/viewer/code-file.d.ts +20 -0
  159. package/dist/viewer/code-file.js +42 -0
  160. package/dist/viewer/editor-tools.d.ts +38 -0
  161. package/dist/viewer/editor-tools.js +211 -0
  162. package/dist/viewer/extension-pack.d.ts +2 -0
  163. package/dist/viewer/extension-pack.js +36 -0
  164. package/dist/viewer/extensions.d.ts +10 -0
  165. package/dist/viewer/extensions.js +302 -0
  166. package/dist/viewer/file-references.d.ts +14 -0
  167. package/dist/viewer/file-references.js +38 -0
  168. package/dist/viewer/font-picker.d.ts +4 -0
  169. package/dist/viewer/font-picker.js +145 -0
  170. package/dist/viewer/font-settings.d.ts +11 -0
  171. package/dist/viewer/font-settings.js +50 -0
  172. package/dist/viewer/fonts/LICENSE.txt +119 -0
  173. package/dist/viewer/fonts/udev-gothic-regular.woff2 +0 -0
  174. package/dist/viewer/fonts.d.ts +2 -0
  175. package/dist/viewer/fonts.js +13 -0
  176. package/dist/viewer/git-diff.d.ts +3 -0
  177. package/dist/viewer/git-diff.js +33 -0
  178. package/dist/viewer/git-types.d.ts +73 -0
  179. package/dist/viewer/git-types.js +1 -0
  180. package/dist/viewer/git.d.ts +5 -0
  181. package/dist/viewer/git.js +337 -0
  182. package/dist/viewer/hmr.d.ts +53 -0
  183. package/dist/viewer/hmr.js +337 -0
  184. package/dist/viewer/i18n.d.ts +6 -0
  185. package/dist/viewer/i18n.js +65 -0
  186. package/dist/viewer/ja-lint.d.ts +3 -0
  187. package/dist/viewer/ja-lint.js +44 -0
  188. package/dist/viewer/language-picker.d.ts +2 -0
  189. package/dist/viewer/language-picker.js +233 -0
  190. package/dist/viewer/languages.d.ts +11 -0
  191. package/dist/viewer/languages.js +20 -0
  192. package/dist/viewer/locales/eng.json +317 -0
  193. package/dist/viewer/locales/jpn.json +317 -0
  194. package/dist/viewer/locales/zho.json +317 -0
  195. package/dist/viewer/nav-focus.d.ts +2 -0
  196. package/dist/viewer/nav-focus.js +64 -0
  197. package/dist/viewer/plugin-blocks.d.ts +3 -0
  198. package/dist/viewer/plugin-blocks.js +115 -0
  199. package/dist/viewer/plugin-config.d.ts +35 -0
  200. package/dist/viewer/plugin-config.js +57 -0
  201. package/dist/viewer/plugin-view.d.ts +2 -0
  202. package/dist/viewer/plugin-view.js +6 -0
  203. package/dist/viewer/plugins.d.ts +12 -0
  204. package/dist/viewer/plugins.js +267 -0
  205. package/dist/viewer/resize.d.ts +4 -0
  206. package/dist/viewer/resize.js +238 -0
  207. package/dist/viewer/resource-library.d.ts +29 -0
  208. package/dist/viewer/resource-library.js +104 -0
  209. package/dist/viewer/search.d.ts +16 -0
  210. package/dist/viewer/search.js +358 -0
  211. package/dist/viewer/serve.d.ts +210 -0
  212. package/dist/viewer/serve.js +3312 -0
  213. package/dist/viewer/site-icon.d.ts +8 -0
  214. package/dist/viewer/site-icon.js +56 -0
  215. package/dist/viewer/textfile.d.ts +9 -0
  216. package/dist/viewer/textfile.js +50 -0
  217. package/dist/viewer/theme-picker.d.ts +2 -0
  218. package/dist/viewer/theme-picker.js +135 -0
  219. package/dist/viewer/workspace.d.ts +4 -0
  220. package/dist/viewer/workspace.js +47 -0
  221. package/package.json +128 -5
@@ -0,0 +1,358 @@
1
+ import { inlineSegmenter } from "../core/inline.js";
2
+ import { docPath } from "../core/routing.js";
3
+ import { docDisplayName } from "../core/taxonomy.js";
4
+ export const searchIndex = (site) => {
5
+ const parents = new Set(site.entries.map((entry) => entry.parent));
6
+ const segments = inlineSegmenter({ terms: site.ctx.terms });
7
+ const plain = (text) => segments(text)
8
+ .map((part) => part.text)
9
+ .join("");
10
+ const list = (items) => items
11
+ .map((item) => typeof item === "string"
12
+ ? plain(item)
13
+ : "blocks" in item
14
+ ? item.blocks.map(text).join(" ")
15
+ : `${plain(item.text)} ${list(item.children)}`)
16
+ .join(" ");
17
+ const text = (block) => {
18
+ switch (block.type) {
19
+ case "themeVariants":
20
+ return text(block.default);
21
+ case "external":
22
+ return `${block.alt ?? ""} ${block.url}`;
23
+ case "h":
24
+ case "p":
25
+ return plain(block.text);
26
+ case "quote":
27
+ return block.blocks.map(text).join(" ");
28
+ case "list":
29
+ return list(block.items);
30
+ case "code":
31
+ return [block.filename ?? "", ...block.lines].join(" ");
32
+ case "table":
33
+ return [block.caption ?? "", ...block.headers, ...block.rows.flat()]
34
+ .map(plain)
35
+ .join(" ");
36
+ case "callout":
37
+ return [
38
+ block.title ?? "",
39
+ ...block.body.map((part) => part.type === "p" ? plain(part.text) : list(part.items)),
40
+ ].join(" ");
41
+ case "term":
42
+ return [
43
+ block.entry.name,
44
+ block.entry.description ?? "",
45
+ block.entry.definition,
46
+ ].join(" ");
47
+ case "figureRef":
48
+ return block.caption ?? site.ctx.figures.get(block.name)?.title ?? block.name;
49
+ case "graph":
50
+ case "schemaGraph":
51
+ return `${block.caption ?? ""} ${block.source ?? ""}`;
52
+ case "figures":
53
+ return [...site.ctx.figures.values()]
54
+ .map((figure) => `${figure.title ?? figure.name} ${figure.desc ?? ""}`)
55
+ .join(" ");
56
+ }
57
+ };
58
+ return site.entries.flatMap((entry) => {
59
+ const base = {
60
+ path: docPath(site.entries, entry.id),
61
+ file: entry.file,
62
+ folder: parents.has(entry.id),
63
+ title: docDisplayName(entry.frontmatter, entry.id),
64
+ };
65
+ let section = {
66
+ ...base,
67
+ heading: "",
68
+ anchor: "",
69
+ text: "",
70
+ };
71
+ const sections = [section];
72
+ for (const block of entry.blocks) {
73
+ if (block.type === "h") {
74
+ section = { ...base, heading: plain(block.text), anchor: block.slug, text: "" };
75
+ sections.push(section);
76
+ }
77
+ else
78
+ section.text += ` ${text(block)}`;
79
+ }
80
+ return sections.map((item) => ({ ...item, text: item.text.replace(/\s+/g, " ").trim() }));
81
+ });
82
+ };
83
+ // Self-contained: the viewer embeds this function in offline HTML as well.
84
+ export const searchSections = (index, query, currentPath = "") => {
85
+ const normalize = (text) => text.normalize("NFKC").toLowerCase();
86
+ const words = [...new Set(normalize(query).trim().split(/\s+/).filter(Boolean))];
87
+ if (!words.length)
88
+ return [];
89
+ const seen = new Set();
90
+ return index
91
+ .flatMap((section) => {
92
+ const title = normalize(section.title);
93
+ const heading = normalize(section.heading);
94
+ const text = section.text.normalize("NFKC");
95
+ const body = text.toLowerCase();
96
+ const name = section.anchor ? "" : normalize(`${section.file} ${section.path}`);
97
+ const all = `${title} ${heading} ${body} ${name}`;
98
+ if (!words.every((word) => all.includes(word)))
99
+ return [];
100
+ if (section.anchor && !words.some((word) => `${heading} ${body}`.includes(word)))
101
+ return [];
102
+ const score = (title === words.join(" ") ? 100 : 0) +
103
+ words.reduce((sum, word) => sum +
104
+ (title.includes(word) ? 10 : 0) +
105
+ (name.includes(word) ? 10 : 0) +
106
+ (heading.includes(word) ? 5 : 0), 0);
107
+ const matches = words.map((word) => body.indexOf(word)).filter((at) => at >= 0);
108
+ const start = Math.max(0, (matches.length ? Math.min(...matches) : 0) - 45);
109
+ const snippet = `${start ? "…" : ""}${text.slice(start, start + 180)}${text.length > start + 180 ? "…" : ""}`;
110
+ return [{ ...section, snippet, score }];
111
+ })
112
+ .sort((a, b) => Number(b.path === currentPath) - Number(a.path === currentPath) ||
113
+ b.score - a.score)
114
+ .filter((hit) => {
115
+ if (hit.path === currentPath)
116
+ return true;
117
+ if (seen.has(hit.path))
118
+ return false;
119
+ seen.add(hit.path);
120
+ return true;
121
+ });
122
+ };
123
+ export const SEARCH_LABELS = [
124
+ "Search documents",
125
+ "No results",
126
+ "Searching…",
127
+ "Could not load search. Press Ctrl+F to retry.",
128
+ "{count} results",
129
+ "Showing {shown} of {count} results",
130
+ "Folder",
131
+ "Move search",
132
+ "Resize search",
133
+ ];
134
+ // Embedded by page(); keep browser behavior independent of the editor bundle.
135
+ export const mountSearch = (initial, search, labels, published) => {
136
+ const dialog = document.createElement("dialog");
137
+ dialog.id = "doc-search";
138
+ dialog.setAttribute("aria-label", labels["Search documents"] ?? "Search documents");
139
+ dialog.innerHTML = `<div class="search-head"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><circle cx="10.5" cy="10.5" r="6.5"/><path d="m16 16 4 4"/></svg><input id="doc-search-input" type="search" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="doc-search-results" autocomplete="off" spellcheck="false"></div><div class="search-status" role="status" aria-live="polite"></div><div id="doc-search-results" role="listbox"></div><div class="search-grip search-grip-left" role="button" tabindex="0"></div><div class="search-grip search-grip-right" role="button" tabindex="0"></div><div class="search-resize" role="button" tabindex="0"></div>`;
140
+ document.body.append(dialog);
141
+ const input = dialog.querySelector("input");
142
+ const results = dialog.querySelector("#doc-search-results");
143
+ const status = dialog.querySelector(".search-status");
144
+ const t = (key, values = {}) => {
145
+ const translate = window
146
+ .__docsTranslate;
147
+ return (translate?.(key) ?? labels[key] ?? key).replace(/\{(\w+)\}/g, (token, name) => String(values[name] ?? token));
148
+ };
149
+ let index = initial;
150
+ let selected = 0;
151
+ let revision = 0;
152
+ let loading = false;
153
+ let failed = false;
154
+ let language = "";
155
+ const currentPath = () => published
156
+ ? location.hash.slice(1).split(/[?#]/)[0] || initial[0]?.path || ""
157
+ : location.pathname;
158
+ const select = (position) => {
159
+ const items = [...results.querySelectorAll("a")];
160
+ selected = items.length ? (position + items.length) % items.length : 0;
161
+ items.forEach((item, i) => {
162
+ item.setAttribute("aria-selected", String(i === selected));
163
+ });
164
+ const active = items[selected];
165
+ if (active) {
166
+ input.setAttribute("aria-activedescendant", active.id);
167
+ active.scrollIntoView({ block: "nearest" });
168
+ }
169
+ else
170
+ input.removeAttribute("aria-activedescendant");
171
+ };
172
+ const render = () => {
173
+ const hits = search(index, input.value, currentPath());
174
+ results.replaceChildren();
175
+ dialog.classList.toggle("search-filled", input.value.length > 0);
176
+ for (const [i, hit] of hits.slice(0, 60).entries()) {
177
+ const link = document.createElement("a");
178
+ link.id = `doc-search-result-${i}`;
179
+ link.setAttribute("role", "option");
180
+ link.tabIndex = -1;
181
+ link.href = `${published ? "#" : ""}${hit.path}${!published && language ? `?lang=${encodeURIComponent(language)}` : ""}${hit.anchor ? `#${encodeURIComponent(hit.anchor)}` : ""}`;
182
+ const meta = document.createElement("div");
183
+ meta.className = "search-meta";
184
+ const folder = hit.folder && !hit.anchor;
185
+ const type = document.createElement("small");
186
+ type.textContent = folder
187
+ ? t("Folder")
188
+ : (hit.file.split(".").pop()?.toUpperCase() ?? "");
189
+ const path = document.createElement("small");
190
+ path.className = "search-path";
191
+ path.textContent = folder ? `${hit.path}/` : hit.file;
192
+ path.title = path.textContent;
193
+ meta.append(type, path);
194
+ const title = document.createElement("strong");
195
+ title.textContent = `${hit.title}${hit.heading ? ` › ${hit.heading}` : ""}`;
196
+ const snippet = document.createElement("span");
197
+ snippet.textContent = hit.snippet;
198
+ link.append(meta, title, snippet);
199
+ results.append(link);
200
+ }
201
+ status.textContent = loading
202
+ ? t("Searching…")
203
+ : failed
204
+ ? t("Could not load search. Press Ctrl+F to retry.")
205
+ : !input.value.trim()
206
+ ? ""
207
+ : !hits.length
208
+ ? t("No results")
209
+ : hits.length > 60
210
+ ? t("Showing {shown} of {count} results", { shown: 60, count: hits.length })
211
+ : t("{count} results", { count: hits.length });
212
+ status.classList.toggle("search-count", !loading && !failed && hits.length > 0);
213
+ input.setAttribute("aria-expanded", String(hits.length > 0));
214
+ select(0);
215
+ };
216
+ const localize = () => {
217
+ dialog.setAttribute("aria-label", t("Search documents"));
218
+ input.setAttribute("aria-label", t("Search documents"));
219
+ for (const handle of dialog.querySelectorAll(".search-grip, .search-resize")) {
220
+ handle.title = t(handle.classList.contains("search-resize") ? "Resize search" : "Move search");
221
+ handle.setAttribute("aria-label", handle.title);
222
+ }
223
+ render();
224
+ };
225
+ const position = (rect, dx, dy, resize = false) => {
226
+ const clamp = (value, min, max) => Math.max(Math.min(min, max), Math.min(value, max));
227
+ const width = clamp(rect.width + (resize ? dx : 0), 280, innerWidth - 16);
228
+ const height = clamp(rect.height + (resize ? dy : 0), 56, innerHeight - 16);
229
+ const left = clamp(rect.left + (resize ? 0 : dx), 8, innerWidth - width - 8);
230
+ const top = clamp(rect.top + (resize ? 0 : dy), 8, innerHeight - height - 8);
231
+ Object.assign(dialog.style, {
232
+ left: `${left}px`,
233
+ top: `${top}px`,
234
+ right: "auto",
235
+ margin: "0",
236
+ width: `${width}px`,
237
+ maxHeight: `${innerHeight - top - 8}px`,
238
+ });
239
+ if (resize || dialog.style.height)
240
+ dialog.style.height = `${height}px`;
241
+ };
242
+ for (const handle of dialog.querySelectorAll(".search-grip, .search-resize")) {
243
+ const resize = handle.classList.contains("search-resize");
244
+ let drag;
245
+ handle.addEventListener("pointerdown", (event) => {
246
+ if (event.button !== 0 || drag)
247
+ return;
248
+ event.preventDefault();
249
+ handle.focus();
250
+ drag = {
251
+ id: event.pointerId,
252
+ x: event.clientX,
253
+ y: event.clientY,
254
+ rect: dialog.getBoundingClientRect(),
255
+ };
256
+ handle.setPointerCapture(event.pointerId);
257
+ });
258
+ handle.addEventListener("pointermove", (event) => {
259
+ if (drag?.id === event.pointerId)
260
+ position(drag.rect, event.clientX - drag.x, event.clientY - drag.y, resize);
261
+ });
262
+ const stop = () => {
263
+ drag = undefined;
264
+ };
265
+ handle.addEventListener("pointerup", stop);
266
+ handle.addEventListener("pointercancel", stop);
267
+ handle.addEventListener("lostpointercapture", stop);
268
+ handle.addEventListener("keydown", (event) => {
269
+ if (!event.key.startsWith("Arrow"))
270
+ return;
271
+ event.preventDefault();
272
+ event.stopPropagation();
273
+ const step = event.shiftKey ? 40 : 10;
274
+ position(dialog.getBoundingClientRect(), event.key === "ArrowRight" ? step : event.key === "ArrowLeft" ? -step : 0, event.key === "ArrowDown" ? step : event.key === "ArrowUp" ? -step : 0, resize);
275
+ });
276
+ }
277
+ window.addEventListener("resize", () => {
278
+ if (dialog.open)
279
+ position(dialog.getBoundingClientRect(), 0, 0);
280
+ });
281
+ const open = async () => {
282
+ const request = ++revision;
283
+ language =
284
+ JSON.parse(document.getElementById("boot")?.textContent || "null")?.contentLanguage ??
285
+ new URL(location.href).searchParams.get("lang") ??
286
+ "";
287
+ if (!dialog.open)
288
+ dialog.show();
289
+ if (dialog.style.left)
290
+ position(dialog.getBoundingClientRect(), 0, 0);
291
+ input.focus();
292
+ input.select();
293
+ failed = false;
294
+ loading = !published;
295
+ if (!published)
296
+ index = [];
297
+ render();
298
+ if (published)
299
+ return;
300
+ try {
301
+ const response = await fetch(`/api/search${language ? `?lang=${encodeURIComponent(language)}` : ""}`);
302
+ if (!response.ok)
303
+ throw new Error(String(response.status));
304
+ const data = (await response.json());
305
+ if (request !== revision)
306
+ return;
307
+ index = data;
308
+ }
309
+ catch {
310
+ if (request !== revision)
311
+ return;
312
+ failed = true;
313
+ }
314
+ loading = false;
315
+ render();
316
+ };
317
+ document.addEventListener("keydown", (event) => {
318
+ if ((event.ctrlKey || event.metaKey) &&
319
+ !event.altKey &&
320
+ !event.shiftKey &&
321
+ event.key.toLowerCase() === "f") {
322
+ event.preventDefault();
323
+ event.stopPropagation();
324
+ if (document.querySelector(":modal"))
325
+ return;
326
+ void open();
327
+ }
328
+ }, true);
329
+ input.addEventListener("input", render);
330
+ input.addEventListener("keydown", (event) => {
331
+ if (event.isComposing)
332
+ return;
333
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
334
+ event.preventDefault();
335
+ select(selected + (event.key === "ArrowDown" ? 1 : -1));
336
+ }
337
+ else if (event.key === "Enter") {
338
+ event.preventDefault();
339
+ results.querySelector('[aria-selected="true"]')?.click();
340
+ }
341
+ });
342
+ results.addEventListener("click", (event) => {
343
+ if (event.target.closest("a"))
344
+ dialog.close();
345
+ });
346
+ dialog.addEventListener("keydown", (event) => {
347
+ if (event.key === "Escape" && !event.isComposing) {
348
+ event.preventDefault();
349
+ dialog.close();
350
+ }
351
+ });
352
+ document.addEventListener("pointerdown", (event) => {
353
+ if (dialog.open && !dialog.contains(event.target))
354
+ dialog.close();
355
+ });
356
+ document.addEventListener("docs:ui-language", localize);
357
+ localize();
358
+ };
@@ -0,0 +1,210 @@
1
+ import { type ProjectFont } from "../compiler/fonts.js";
2
+ import type { LanguageRegistration } from "shiki/core";
3
+ import { type Compiled } from "../compiler/compile.js";
4
+ import { assembleDoc } from "../compiler/ops.js";
5
+ import { type Block, type TocEntry } from "../core/blocks.js";
6
+ import type { TermEntry } from "../core/content-types.js";
7
+ import { type RenderCtx } from "../core/html.js";
8
+ import { type LanguageAdapter } from "../core/language.js";
9
+ import { type PreviewSettings } from "../core/previewers.js";
10
+ import { type Frontmatter } from "../core/taxonomy.js";
11
+ import { type WorkspaceSource } from "../core/workspace.js";
12
+ import { type FilePreviewResults } from "./file-references.js";
13
+ import { type ViewerRuntime } from "./plugin-config.js";
14
+ export type ServeMode = "preview" | "edit";
15
+ /**
16
+ * One doc in the viewer = a view model combining the compiler's CompiledDoc (rendering) and
17
+ * DocSource (extra info for editing).
18
+ * file is `<id>.md` (= same shape as the save API's file parameter; CompiledDoc.file minus the
19
+ * content/ prefix).
20
+ */
21
+ export interface Entry {
22
+ id: string;
23
+ translatedLanguages?: string[];
24
+ /** Parent doc id (omitted = top level). */
25
+ parent?: string;
26
+ file: string;
27
+ sourceFile?: string;
28
+ iconFile?: string;
29
+ frontmatter: Frontmatter | undefined;
30
+ /** Resolved language (front-matter `lang`, else the site default). */
31
+ lang: string;
32
+ /** Source md without the front-matter (the editor's initial value). */
33
+ body: string;
34
+ blocks: Block[];
35
+ toc: TocEntry[];
36
+ /**
37
+ * For the editor's correspondence display: body-relative start line of each block (same
38
+ * index as blocks).
39
+ */
40
+ blockLines: number[];
41
+ /**
42
+ * Full source on disk (including front-matter). Used as the base for the editor's
43
+ * external-change detection.
44
+ */
45
+ raw: string;
46
+ /** Validation errors for this doc (the compiler's DocSource.errors). */
47
+ errors: string[];
48
+ /** Icon for the sidebar (full svg, 1:1). */
49
+ icon?: string;
50
+ /** Names of the figures the body references (Resources' "used in"). */
51
+ figureRefs?: string[];
52
+ termRefs?: string[];
53
+ terms?: TermEntry[];
54
+ }
55
+ export interface Site {
56
+ fonts?: readonly ProjectFont[];
57
+ workspace?: WorkspaceSource;
58
+ filePaths?: readonly string[];
59
+ previews?: PreviewSettings;
60
+ title?: string;
61
+ icon?: string;
62
+ codeLanguages?: string[];
63
+ codeGrammars?: LanguageRegistration[];
64
+ contentLanguage?: string;
65
+ /** Display order (pre-order traversal of the manifest). */
66
+ entries: Entry[];
67
+ ctx: RenderCtx;
68
+ /** Errors not tied to a doc (the compiler's siteErrors). Shown on every page. */
69
+ errors: string[];
70
+ /** Language adapters (docs.config.mjs + built-ins); each doc renders with its own. */
71
+ languages: readonly LanguageAdapter[];
72
+ /** Site default language. */
73
+ defaultLang: string;
74
+ /** Declared site languages, when the manifest restricts the language picker. */
75
+ contentLanguages?: readonly string[];
76
+ themes?: readonly ("light" | "dark")[];
77
+ }
78
+ /** Compile result → viewer Site (shared by the server's per-request path and one-off publish). */
79
+ export declare const siteFrom: ({ title, icon, docs, terms, figures, sources, siteErrors, defaultLang, previews, contentLanguages, themes, }: Compiled, languages?: readonly LanguageAdapter[]) => Site;
80
+ export declare const filePreviewResults: (entry: Entry, site: Site) => FilePreviewResults;
81
+ /** Renders only the requested doc, not every doc in site.entries. */
82
+ export declare const renderEntryHtml: (site: Site, entry: Entry) => {
83
+ html: string;
84
+ errors: string[];
85
+ };
86
+ /** Compiles + renders one source (its current content on disk) (implements `/api/doc`). */
87
+ export declare const renderDocFile: (site: Site, file: string, raw: string) => {
88
+ gitBase: string | null;
89
+ raw: string;
90
+ frontmatter: Frontmatter | undefined;
91
+ body: string;
92
+ html: string;
93
+ errors: string[];
94
+ toc: TocEntry[];
95
+ blockLines: number[];
96
+ filePreviews: FilePreviewResults;
97
+ previews?: PreviewSettings;
98
+ sourceFile?: string;
99
+ };
100
+ /** Response for `GET /api/doc?file=<id>.md` (status + body). mode === "edit" only. */
101
+ export declare const docQueryResponse: (site: Site, file: unknown) => {
102
+ status: number;
103
+ body: unknown;
104
+ };
105
+ export { assembleDoc };
106
+ /** Client JSON → Frontmatter (keeps supported fields and drops empty optional values). */
107
+ export declare const sanitizeFrontmatter: (v: unknown) => Frontmatter;
108
+ /**
109
+ * Compares the base the editor started from (the disk source the client last read) with the
110
+ * current disk source to tell whether it was rewritten externally (direct edit / git etc.)
111
+ * without the editor knowing.
112
+ * A client that sends no base (no baseRaw) can't be judged, so false (= overwrite).
113
+ */
114
+ export declare const isExternallyModified: (baseRaw: unknown, currentRaw: string) => boolean;
115
+ /**
116
+ * Validates the write target path — rejects anything other than `<id>.md` (directly under
117
+ * content/), including traversal.
118
+ */
119
+ export declare const resolveContentFile: (rel: unknown) => {
120
+ id: string;
121
+ name: string;
122
+ abs: string;
123
+ } | undefined;
124
+ export declare const navGroupsHtml: (mode: ServeMode, site: Site, currentId?: string, currentRes?: string) => string;
125
+ export declare const textfilePreviewHtml: (site: Site, file: string, source: string) => string;
126
+ export declare const textfilePreviewErrors: (site: Site, file: string, source: string) => string[];
127
+ export declare const resourcesMain: (site: Site, rest: string) => {
128
+ title: string;
129
+ main: string;
130
+ sourceFile?: string;
131
+ } | undefined;
132
+ /**
133
+ * Turns the whole Site into a single HTML file readable without a server (everything embedded,
134
+ * down to figures / terms / schema graph JS). Each doc is held in a
135
+ * <template> and switched via hash routes.
136
+ */
137
+ export declare const publishHtml: (site: Site, graphJs: string, title?: string, codeJs?: string, mode?: "static" | "network") => string;
138
+ /**
139
+ * Boot data for the client (edit-client.ts) — the same shape is passed for the initial render and
140
+ * on doc switch. Insertion happens via the client's in-editor autocomplete (`/` palette), so the
141
+ * candidate data (figures / docs / terms) is passed here, plus blockLines for the correspondence
142
+ * display.
143
+ */
144
+ export interface EditorBoot {
145
+ gitBase: string | null;
146
+ filePreviews: FilePreviewResults;
147
+ previews?: PreviewSettings;
148
+ siteTitle?: string;
149
+ siteIcon?: string;
150
+ contentLanguage?: string;
151
+ sourceLanguage?: string;
152
+ docLanguage?: string;
153
+ translatedLanguages?: string[];
154
+ /** `<id>.md` (the save API's key). */
155
+ file: string;
156
+ sourceFile?: string;
157
+ /** Resolution key for published URLs. */
158
+ docId: string;
159
+ title: string;
160
+ path: string;
161
+ body: string;
162
+ figures: string[];
163
+ codeLanguages: readonly string[];
164
+ docs: {
165
+ ref: string;
166
+ title: string;
167
+ }[];
168
+ terms: TermEntry[];
169
+ blockLines: number[];
170
+ /** Full source on disk (the base for external-change detection). */
171
+ baseRaw: string;
172
+ /** Data URI of the sidebar icon (null if none). */
173
+ icon: string | null;
174
+ /** Site default language (used for lint when the `lang` field is left empty). */
175
+ defaultLang: string;
176
+ /** Language of the CodeMirror content's browser spellcheck (the doc language's adapter). */
177
+ spellcheckLang: string;
178
+ }
179
+ export declare const languageFieldHtml: (defaultLang: string, language?: string, translatedLanguages?: readonly string[], declaredLanguages?: readonly string[]) => string;
180
+ export declare const editorBoot: (entry: Entry, site: Site) => EditorBoot;
181
+ /**
182
+ * Response for `GET /api/page?route=/<ancestor/.../docId>` (status + body). mode === "edit" only.
183
+ * The diff for doing edit's doc switch via client-side navigation (= keep scroll position /
184
+ * search term / open state without rebuilding the sidebar DOM); it returns only what the client
185
+ * replaces: main html + the panel's variable parts (file name / front-matter fields) + the new
186
+ * doc's boot.
187
+ * File views replace the central workspace while retaining the current editor.
188
+ */
189
+ export declare const pageDataResponse: (site: Site, path: unknown) => {
190
+ status: number;
191
+ body: unknown;
192
+ };
193
+ /** Cache of entry name → { js, etag }. clear() is called on HMR "client" notifications
194
+ * (changes to edit-client.ts / the schema graph interaction layer) (= invalidation is
195
+ * startServer's job; this is just the container). */
196
+ export interface BundleCache {
197
+ get: (entry: string, build: () => Promise<string>) => Promise<{
198
+ js: string;
199
+ etag: string;
200
+ }>;
201
+ clear: () => void;
202
+ }
203
+ export declare const createBundleCache: () => BundleCache;
204
+ /** Client bundle (src/<entry> as an IIFE). Sourcemap for the server, minified for publish. */
205
+ export declare const bundleClient: (entry: string, publish?: boolean) => Promise<string>;
206
+ export declare const startServer: (mode: ServeMode, port?: number, pluginModule?: string, startup?: {
207
+ file?: string;
208
+ data?: string;
209
+ login?: string;
210
+ }) => Promise<ViewerRuntime>;