@elabs-ai/components-viewer 4.2.0 → 5.1.0

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 (204) hide show
  1. package/dist/{code-adapter-ADZ4UOGN.js → adapters/code/code-adapter.js} +15 -64
  2. package/dist/adapters/code/code-adapter.js.map +1 -0
  3. package/dist/adapters/code/code-language.js +10 -0
  4. package/dist/adapters/code/code-language.js.map +1 -0
  5. package/dist/adapters/code/code-manifest.js +10 -0
  6. package/dist/adapters/code/code-manifest.js.map +1 -0
  7. package/dist/adapters/code/code-theme.js +8 -0
  8. package/dist/adapters/code/code-theme.js.map +1 -0
  9. package/dist/{csv-adapter-7HOFZ5Z6.js → adapters/csv/csv-adapter.js} +13 -9
  10. package/dist/adapters/csv/csv-adapter.js.map +1 -0
  11. package/dist/adapters/csv/csv-manifest.js +9 -0
  12. package/dist/adapters/csv/csv-manifest.js.map +1 -0
  13. package/dist/{docx-adapter-5CQDHWTD.js → adapters/docx/docx-adapter.js} +17 -175
  14. package/dist/adapters/docx/docx-adapter.js.map +1 -0
  15. package/dist/adapters/docx/docx-manifest.js +9 -0
  16. package/dist/adapters/docx/docx-manifest.js.map +1 -0
  17. package/dist/adapters/docx/docx-model.js +21 -0
  18. package/dist/adapters/docx/docx-model.js.map +1 -0
  19. package/dist/{image-adapter-WOHZR24J.js → adapters/image/image-adapter.js} +4 -3
  20. package/dist/adapters/image/image-adapter.js.map +1 -0
  21. package/dist/adapters/image/image-manifest.js +9 -0
  22. package/dist/adapters/image/image-manifest.js.map +1 -0
  23. package/dist/adapters/index.js +56 -0
  24. package/dist/adapters/index.js.map +1 -0
  25. package/dist/{json-adapter-ZUW5GQHE.js → adapters/json/json-adapter.js} +4 -3
  26. package/dist/adapters/json/json-adapter.js.map +1 -0
  27. package/dist/adapters/json/json-manifest.js +9 -0
  28. package/dist/adapters/json/json-manifest.js.map +1 -0
  29. package/dist/{markdown-adapter-YC6WTBS4.js → adapters/markdown/markdown-adapter.js} +14 -36
  30. package/dist/adapters/markdown/markdown-adapter.js.map +1 -0
  31. package/dist/adapters/markdown/markdown-manifest.js +9 -0
  32. package/dist/adapters/markdown/markdown-manifest.js.map +1 -0
  33. package/dist/adapters/markdown/markdown-marks.js +10 -0
  34. package/dist/adapters/markdown/markdown-marks.js.map +1 -0
  35. package/dist/{media-adapter-MCTB4GBH.js → adapters/media/media-adapter.js} +4 -3
  36. package/dist/adapters/media/media-adapter.js.map +1 -0
  37. package/dist/adapters/media/media-manifest.js +9 -0
  38. package/dist/adapters/media/media-manifest.js.map +1 -0
  39. package/dist/{pdf-adapter-5PMKEXUD.js → adapters/pdf/pdf-adapter.js} +31 -81
  40. package/dist/adapters/pdf/pdf-adapter.js.map +1 -0
  41. package/dist/adapters/pdf/pdf-engine.js +14 -0
  42. package/dist/adapters/pdf/pdf-engine.js.map +1 -0
  43. package/dist/adapters/pdf/pdf-manifest.js +9 -0
  44. package/dist/adapters/pdf/pdf-manifest.js.map +1 -0
  45. package/dist/adapters/pdf/pdf-text.js +20 -0
  46. package/dist/adapters/pdf/pdf-text.js.map +1 -0
  47. package/dist/{pptx-adapter-BONWFHYT.js → adapters/pptx/pptx-adapter.js} +34 -135
  48. package/dist/adapters/pptx/pptx-adapter.js.map +1 -0
  49. package/dist/adapters/pptx/pptx-manifest.js +9 -0
  50. package/dist/adapters/pptx/pptx-manifest.js.map +1 -0
  51. package/dist/adapters/pptx/pptx-model.js +37 -0
  52. package/dist/adapters/pptx/pptx-model.js.map +1 -0
  53. package/dist/{text-adapter-NFNWB5W3.js → adapters/text/text-adapter.js} +8 -5
  54. package/dist/adapters/text/text-adapter.js.map +1 -0
  55. package/dist/adapters/text/text-manifest.js +9 -0
  56. package/dist/adapters/text/text-manifest.js.map +1 -0
  57. package/dist/{xlsx-adapter-O3XIVLFC.js → adapters/xlsx/xlsx-adapter.js} +12 -8
  58. package/dist/adapters/xlsx/xlsx-adapter.js.map +1 -0
  59. package/dist/adapters/xlsx/xlsx-manifest.js +9 -0
  60. package/dist/adapters/xlsx/xlsx-manifest.js.map +1 -0
  61. package/dist/chunk-3HKQFFZW.js +31 -0
  62. package/dist/chunk-3HKQFFZW.js.map +1 -0
  63. package/dist/chunk-5VJWILZ7.js +182 -0
  64. package/dist/chunk-5VJWILZ7.js.map +1 -0
  65. package/dist/{chunk-UL43NGUG.js → chunk-5YE2MUS2.js} +1 -1
  66. package/dist/{chunk-5OMONA2G.js → chunk-6FVJ4D7W.js} +3 -15
  67. package/dist/chunk-6FVJ4D7W.js.map +1 -0
  68. package/dist/{chunk-RGWDIQJR.js → chunk-7EASJBQM.js} +2 -2
  69. package/dist/chunk-7PGGKQVY.js +26 -0
  70. package/dist/chunk-7PGGKQVY.js.map +1 -0
  71. package/dist/{chunk-XBOR2YXS.js → chunk-AK7F3IZE.js} +9 -30
  72. package/dist/chunk-AK7F3IZE.js.map +1 -0
  73. package/dist/chunk-AOYEL7O3.js +34 -0
  74. package/dist/chunk-AOYEL7O3.js.map +1 -0
  75. package/dist/chunk-BDUFH66C.js +61 -0
  76. package/dist/chunk-BDUFH66C.js.map +1 -0
  77. package/dist/chunk-BWMXMHVZ.js +107 -0
  78. package/dist/chunk-BWMXMHVZ.js.map +1 -0
  79. package/dist/chunk-CIT2HHRI.js +9 -0
  80. package/dist/chunk-CIT2HHRI.js.map +1 -0
  81. package/dist/chunk-CLOI4HBA.js +18 -0
  82. package/dist/chunk-CLOI4HBA.js.map +1 -0
  83. package/dist/chunk-CMYBLW7D.js +21 -0
  84. package/dist/chunk-CMYBLW7D.js.map +1 -0
  85. package/dist/chunk-CO2VJRSU.js +18 -0
  86. package/dist/chunk-CO2VJRSU.js.map +1 -0
  87. package/dist/{chunk-3U3MESBU.js → chunk-EA72HA4I.js} +2 -2
  88. package/dist/{chunk-KOMDRWOU.js → chunk-FDT4QOIG.js} +2 -2
  89. package/dist/chunk-FQHZEQUH.js +90 -0
  90. package/dist/chunk-FQHZEQUH.js.map +1 -0
  91. package/dist/chunk-G27UJZWH.js +82 -0
  92. package/dist/chunk-G27UJZWH.js.map +1 -0
  93. package/dist/{chunk-BCF3RWRL.js → chunk-GH4UBYST.js} +2 -2
  94. package/dist/{chunk-2NQ4RSJ3.js → chunk-GNBD53AT.js} +2 -2
  95. package/dist/{chunk-VO2273Z2.js → chunk-HOQZ7OPH.js} +2 -18
  96. package/dist/chunk-HOQZ7OPH.js.map +1 -0
  97. package/dist/{chunk-GGR7BNW4.js → chunk-HUUJIFFT.js} +2 -2
  98. package/dist/{chunk-FELIAGKI.js → chunk-I5XD2NJ6.js} +2 -2
  99. package/dist/chunk-IB4N72FA.js +21 -0
  100. package/dist/chunk-IB4N72FA.js.map +1 -0
  101. package/dist/{chunk-4OFQYALG.js → chunk-IQ6Z4X3K.js} +8 -68
  102. package/dist/chunk-IQ6Z4X3K.js.map +1 -0
  103. package/dist/{chunk-AORNMH77.js → chunk-JHFC2KBI.js} +1 -5
  104. package/dist/chunk-JHFC2KBI.js.map +1 -0
  105. package/dist/chunk-LRPZ373O.js +93 -0
  106. package/dist/chunk-LRPZ373O.js.map +1 -0
  107. package/dist/chunk-M77ICVWP.js +24 -0
  108. package/dist/chunk-M77ICVWP.js.map +1 -0
  109. package/dist/chunk-MJUH32ZK.js +24 -0
  110. package/dist/chunk-MJUH32ZK.js.map +1 -0
  111. package/dist/chunk-PAIVK4N6.js +162 -0
  112. package/dist/chunk-PAIVK4N6.js.map +1 -0
  113. package/dist/chunk-PKYDKXCO.js +129 -0
  114. package/dist/chunk-PKYDKXCO.js.map +1 -0
  115. package/dist/chunk-QIPKBOZJ.js +59 -0
  116. package/dist/chunk-QIPKBOZJ.js.map +1 -0
  117. package/dist/chunk-SASY77ZL.js +29 -0
  118. package/dist/chunk-SASY77ZL.js.map +1 -0
  119. package/dist/{chunk-SLXRUYT3.js → chunk-SBG7WPGO.js} +2 -44
  120. package/dist/chunk-SBG7WPGO.js.map +1 -0
  121. package/dist/chunk-T4Y4YRHJ.js +733 -0
  122. package/dist/chunk-T4Y4YRHJ.js.map +1 -0
  123. package/dist/chunk-TLCNGQKL.js +37 -0
  124. package/dist/chunk-TLCNGQKL.js.map +1 -0
  125. package/dist/chunk-US5KT3TR.js +141 -0
  126. package/dist/chunk-US5KT3TR.js.map +1 -0
  127. package/dist/{chunk-HMG2ERXH.js → chunk-YA72QE57.js} +2 -2
  128. package/dist/{chunk-H3OAON3D.js → chunk-YWHFMCS4.js} +2 -2
  129. package/dist/{chunk-UJIJECEF.js → chunk-ZAVXZRIO.js} +2 -2
  130. package/dist/components/grid-text.js +19 -0
  131. package/dist/components/grid-text.js.map +1 -0
  132. package/dist/components/marked-text.js +10 -0
  133. package/dist/components/marked-text.js.map +1 -0
  134. package/dist/components/sheet-table.js +14 -0
  135. package/dist/components/sheet-table.js.map +1 -0
  136. package/dist/core/errors.js +18 -0
  137. package/dist/core/errors.js.map +1 -0
  138. package/dist/core/highlight-marks.js +10 -0
  139. package/dist/core/highlight-marks.js.map +1 -0
  140. package/dist/core/highlight-resolve.js +10 -0
  141. package/dist/core/highlight-resolve.js.map +1 -0
  142. package/dist/core/highlight.js +12 -0
  143. package/dist/core/highlight.js.map +1 -0
  144. package/dist/core/registry.js +12 -0
  145. package/dist/core/registry.js.map +1 -0
  146. package/dist/core/scroll-host.js +10 -0
  147. package/dist/core/scroll-host.js.map +1 -0
  148. package/dist/core/text-index.js +14 -0
  149. package/dist/core/text-index.js.map +1 -0
  150. package/dist/core/types.js +8 -0
  151. package/dist/core/types.js.map +1 -0
  152. package/dist/core/use-highlight-scroll.js +11 -0
  153. package/dist/core/use-highlight-scroll.js.map +1 -0
  154. package/dist/core/use-page-control.js +9 -0
  155. package/dist/core/use-page-control.js.map +1 -0
  156. package/dist/core/use-paged-scroll.js +10 -0
  157. package/dist/core/use-paged-scroll.js.map +1 -0
  158. package/dist/core/use-viewport-size.js +10 -0
  159. package/dist/core/use-viewport-size.js.map +1 -0
  160. package/dist/core/zoom.js +16 -0
  161. package/dist/core/zoom.js.map +1 -0
  162. package/dist/file-viewer/file-viewer-context.js +11 -0
  163. package/dist/file-viewer/file-viewer-context.js.map +1 -0
  164. package/dist/file-viewer/file-viewer-find.js +13 -0
  165. package/dist/file-viewer/file-viewer-find.js.map +1 -0
  166. package/dist/file-viewer/file-viewer-pager.js +10 -0
  167. package/dist/file-viewer/file-viewer-pager.js.map +1 -0
  168. package/dist/file-viewer/file-viewer-zoom.js +13 -0
  169. package/dist/file-viewer/file-viewer-zoom.js.map +1 -0
  170. package/dist/file-viewer/file-viewer.js +48 -0
  171. package/dist/file-viewer/file-viewer.js.map +1 -0
  172. package/dist/index.js +78 -1274
  173. package/dist/index.js.map +1 -1
  174. package/package.json +5 -5
  175. package/src/file-viewer/file-viewer-recipes.stories.tsx +406 -0
  176. package/src/file-viewer/file-viewer.stories.tsx +2 -2
  177. package/dist/chunk-4OFQYALG.js.map +0 -1
  178. package/dist/chunk-5OMONA2G.js.map +0 -1
  179. package/dist/chunk-AORNMH77.js.map +0 -1
  180. package/dist/chunk-SLXRUYT3.js.map +0 -1
  181. package/dist/chunk-VO2273Z2.js.map +0 -1
  182. package/dist/chunk-XBOR2YXS.js.map +0 -1
  183. package/dist/code-adapter-ADZ4UOGN.js.map +0 -1
  184. package/dist/csv-adapter-7HOFZ5Z6.js.map +0 -1
  185. package/dist/docx-adapter-5CQDHWTD.js.map +0 -1
  186. package/dist/image-adapter-WOHZR24J.js.map +0 -1
  187. package/dist/json-adapter-ZUW5GQHE.js.map +0 -1
  188. package/dist/markdown-adapter-YC6WTBS4.js.map +0 -1
  189. package/dist/media-adapter-MCTB4GBH.js.map +0 -1
  190. package/dist/pdf-adapter-5PMKEXUD.js.map +0 -1
  191. package/dist/pptx-adapter-BONWFHYT.js.map +0 -1
  192. package/dist/text-adapter-NFNWB5W3.js.map +0 -1
  193. package/dist/xlsx-adapter-O3XIVLFC.js.map +0 -1
  194. /package/dist/{chunk-UL43NGUG.js.map → chunk-5YE2MUS2.js.map} +0 -0
  195. /package/dist/{chunk-RGWDIQJR.js.map → chunk-7EASJBQM.js.map} +0 -0
  196. /package/dist/{chunk-3U3MESBU.js.map → chunk-EA72HA4I.js.map} +0 -0
  197. /package/dist/{chunk-KOMDRWOU.js.map → chunk-FDT4QOIG.js.map} +0 -0
  198. /package/dist/{chunk-BCF3RWRL.js.map → chunk-GH4UBYST.js.map} +0 -0
  199. /package/dist/{chunk-2NQ4RSJ3.js.map → chunk-GNBD53AT.js.map} +0 -0
  200. /package/dist/{chunk-GGR7BNW4.js.map → chunk-HUUJIFFT.js.map} +0 -0
  201. /package/dist/{chunk-FELIAGKI.js.map → chunk-I5XD2NJ6.js.map} +0 -0
  202. /package/dist/{chunk-HMG2ERXH.js.map → chunk-YA72QE57.js.map} +0 -0
  203. /package/dist/{chunk-H3OAON3D.js.map → chunk-YWHFMCS4.js.map} +0 -0
  204. /package/dist/{chunk-UJIJECEF.js.map → chunk-ZAVXZRIO.js.map} +0 -0
@@ -0,0 +1,182 @@
1
+ "use client";
2
+ import {
3
+ createTextIndexBuilder
4
+ } from "./chunk-5YE2MUS2.js";
5
+
6
+ // src/adapters/docx/docx-model.ts
7
+ var SAFE_SCHEMES = ["http:", "https:", "mailto:", "tel:"];
8
+ function safeHref(value) {
9
+ if (!value) return void 0;
10
+ try {
11
+ const url = new URL(value, "https://viewer.invalid");
12
+ return SAFE_SCHEMES.includes(url.protocol) && !value.startsWith("#") ? url.href : void 0;
13
+ } catch {
14
+ return void 0;
15
+ }
16
+ }
17
+ function collectRuns(node, style = {}, out = []) {
18
+ for (const child of Array.from(node.childNodes)) {
19
+ if (child.nodeType === 3) {
20
+ const text = child.nodeValue ?? "";
21
+ if (text) out.push({ text, ...style });
22
+ continue;
23
+ }
24
+ if (child.nodeType !== 1) continue;
25
+ const element = child;
26
+ const tag = element.tagName.toLowerCase();
27
+ if (tag === "br") {
28
+ out.push({ text: "\n", ...style });
29
+ continue;
30
+ }
31
+ const next = { ...style };
32
+ if (tag === "strong" || tag === "b") next.bold = true;
33
+ if (tag === "em" || tag === "i") next.italic = true;
34
+ if (tag === "a") {
35
+ const href = safeHref(element.getAttribute("href"));
36
+ if (href) next.href = href;
37
+ }
38
+ collectRuns(element, next, out);
39
+ }
40
+ return out;
41
+ }
42
+ function compact(runs) {
43
+ const merged = [];
44
+ for (const run of runs) {
45
+ const last = merged[merged.length - 1];
46
+ if (last && last.bold === run.bold && last.italic === run.italic && last.href === run.href) {
47
+ last.text += run.text;
48
+ } else {
49
+ merged.push({ ...run });
50
+ }
51
+ }
52
+ return merged.filter((run) => run.text.trim() !== "" || run.text.includes("\n"));
53
+ }
54
+ function textOf(node) {
55
+ return compact(collectRuns(node)).map((run) => run.text).join("").trim();
56
+ }
57
+ function listItems(element) {
58
+ const items = [];
59
+ for (const child of Array.from(element.children)) {
60
+ if (child.tagName.toLowerCase() !== "li") continue;
61
+ const runs = compact(collectRuns(child));
62
+ if (runs.length > 0) items.push(runs);
63
+ for (const nested of Array.from(child.children)) {
64
+ const tag = nested.tagName.toLowerCase();
65
+ if (tag === "ul" || tag === "ol") items.push(...listItems(nested));
66
+ }
67
+ }
68
+ return items;
69
+ }
70
+ function tableBlock(element) {
71
+ const rows = Array.from(element.querySelectorAll("tr"));
72
+ if (rows.length === 0) return void 0;
73
+ const cellsOf = (row) => Array.from(row.children).filter((cell) => ["td", "th"].includes(cell.tagName.toLowerCase())).map(textOf);
74
+ const [firstRow, ...restRows] = rows;
75
+ if (!firstRow) return void 0;
76
+ const isHeader = firstRow.querySelector("th") !== null;
77
+ return isHeader ? { type: "table", head: cellsOf(firstRow), rows: restRows.map(cellsOf) } : { type: "table", rows: rows.map(cellsOf) };
78
+ }
79
+ var HEADING_LEVELS = {
80
+ h1: 1,
81
+ h2: 2,
82
+ h3: 3,
83
+ h4: 4,
84
+ h5: 5,
85
+ h6: 6
86
+ };
87
+ function htmlToBlocks(html, parse) {
88
+ const document = parse(`<body>${html}</body>`);
89
+ const blocks = [];
90
+ const walk = (parent) => {
91
+ for (const element of Array.from(parent.children)) {
92
+ const tag = element.tagName.toLowerCase();
93
+ const level = HEADING_LEVELS[tag];
94
+ if (level) {
95
+ const runs = compact(collectRuns(element));
96
+ if (runs.length > 0) blocks.push({ type: "heading", level, runs });
97
+ continue;
98
+ }
99
+ if (tag === "p") {
100
+ const image = element.querySelector("img");
101
+ const runs = compact(collectRuns(element));
102
+ if (image && runs.length === 0) {
103
+ blocks.push(imageBlock(image));
104
+ continue;
105
+ }
106
+ if (runs.length > 0) blocks.push({ type: "paragraph", runs });
107
+ continue;
108
+ }
109
+ if (tag === "ul" || tag === "ol") {
110
+ const items = listItems(element);
111
+ if (items.length > 0) blocks.push({ type: "list", ordered: tag === "ol", items });
112
+ continue;
113
+ }
114
+ if (tag === "table") {
115
+ const table = tableBlock(element);
116
+ if (table) blocks.push(table);
117
+ continue;
118
+ }
119
+ if (tag === "img") {
120
+ blocks.push(imageBlock(element));
121
+ continue;
122
+ }
123
+ walk(element);
124
+ }
125
+ };
126
+ walk(document.body);
127
+ return blocks;
128
+ }
129
+ function imageBlock(element) {
130
+ const alt = element.getAttribute("alt")?.trim();
131
+ return {
132
+ type: "image",
133
+ // mammoth inlines images as `data:` URIs by default, so there is no remote
134
+ // origin here — and no `src` at all is better than a URL we did not vet.
135
+ src: element.getAttribute("src") ?? "",
136
+ ...alt ? { alt } : {}
137
+ };
138
+ }
139
+ var DOCX_LIST_BULLET = "\u2022 ";
140
+ var DOCX_CELL_SEPARATOR = " ";
141
+ var DOCX_HEAD_ROW = -1;
142
+ function blocksToTextWithMap(blocks) {
143
+ const builder = createTextIndexBuilder();
144
+ blocks.forEach((block, index) => {
145
+ if (block.type === "heading" || block.type === "paragraph") {
146
+ builder.push(block.runs.map((run) => run.text).join(""), { block: index });
147
+ return;
148
+ }
149
+ if (block.type === "list") {
150
+ block.items.forEach((item, item_) => {
151
+ builder.push(`${DOCX_LIST_BULLET}${item.map((run) => run.text).join("")}`, {
152
+ block: index,
153
+ item: item_
154
+ });
155
+ });
156
+ return;
157
+ }
158
+ if (block.type === "table") {
159
+ if (block.head) {
160
+ builder.push(block.head.join(DOCX_CELL_SEPARATOR), { block: index, row: DOCX_HEAD_ROW });
161
+ }
162
+ block.rows.forEach((row, rowIndex) => {
163
+ builder.push(row.join(DOCX_CELL_SEPARATOR), { block: index, row: rowIndex });
164
+ });
165
+ }
166
+ });
167
+ return builder.build();
168
+ }
169
+ function blocksToText(blocks) {
170
+ return blocksToTextWithMap(blocks).text;
171
+ }
172
+
173
+ export {
174
+ safeHref,
175
+ htmlToBlocks,
176
+ DOCX_LIST_BULLET,
177
+ DOCX_CELL_SEPARATOR,
178
+ DOCX_HEAD_ROW,
179
+ blocksToTextWithMap,
180
+ blocksToText
181
+ };
182
+ //# sourceMappingURL=chunk-5VJWILZ7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/docx/docx-model.ts"],"sourcesContent":["/**\n * Word markup → a block model.\n *\n * ## Why this exists at all\n *\n * mammoth converts a `.docx` into an HTML **string**. Rendering that string\n * would mean `dangerouslySetInnerHTML`, which is three separate problems: it\n * puts third-party markup past the token layer, it needs a sanitizer to be safe\n * at all, and it makes the surface unusable under a Trusted-Types policy (a raw\n * `innerHTML` write throws during commit and takes the React root down with it —\n * see `docs/CSP-AND-NETWORK.md`).\n *\n * So the HTML is parsed and thrown away, and what survives is a small model of\n * blocks and inline runs that React renders as real elements. **The parse IS the\n * allowlist**: a tag this file does not name contributes nothing but its text,\n * so there is no denylist to keep up to date, no `<script>` to strip, and no\n * sanitizer dependency. It is also why the viewer adds no CSP sink.\n */\n\nimport { createTextIndexBuilder, type TextIndex } from \"../../core/text-index\";\n\n/** A styled span of text inside a block. */\nexport interface DocxRun {\n text: string;\n bold?: boolean;\n italic?: boolean;\n /** Present only for a link whose scheme survived {@link safeHref}. */\n href?: string;\n}\n\nexport type DocxBlock =\n | { type: \"heading\"; level: 1 | 2 | 3 | 4 | 5 | 6; runs: DocxRun[] }\n | { type: \"paragraph\"; runs: DocxRun[] }\n | { type: \"list\"; ordered: boolean; items: DocxRun[][] }\n | { type: \"table\"; head?: string[]; rows: string[][] }\n | { type: \"image\"; src: string; alt?: string };\n\n/** Schemes a link may keep. Anything else (notably `javascript:`) becomes plain text. */\nconst SAFE_SCHEMES = [\"http:\", \"https:\", \"mailto:\", \"tel:\"];\n\n/**\n * The href, or `undefined` when it is not a scheme we will hand to the browser.\n * Relative hrefs are dropped too — a Word document's internal bookmarks point at\n * anchors that do not exist on the host page.\n */\nexport function safeHref(value: string | null): string | undefined {\n if (!value) return undefined;\n try {\n const url = new URL(value, \"https://viewer.invalid\");\n return SAFE_SCHEMES.includes(url.protocol) && !value.startsWith(\"#\") ? url.href : undefined;\n } catch {\n return undefined;\n }\n}\n\ninterface RunStyle {\n bold?: boolean;\n italic?: boolean;\n href?: string;\n}\n\n/** Collect the inline runs under a node, carrying style down the tree. */\nfunction collectRuns(node: Node, style: RunStyle = {}, out: DocxRun[] = []): DocxRun[] {\n for (const child of Array.from(node.childNodes)) {\n if (child.nodeType === 3 /* TEXT_NODE */) {\n const text = child.nodeValue ?? \"\";\n if (text) out.push({ text, ...style });\n continue;\n }\n if (child.nodeType !== 1 /* ELEMENT_NODE */) continue;\n\n const element = child as Element;\n const tag = element.tagName.toLowerCase();\n if (tag === \"br\") {\n out.push({ text: \"\\n\", ...style });\n continue;\n }\n const next: RunStyle = { ...style };\n if (tag === \"strong\" || tag === \"b\") next.bold = true;\n if (tag === \"em\" || tag === \"i\") next.italic = true;\n if (tag === \"a\") {\n const href = safeHref(element.getAttribute(\"href\"));\n if (href) next.href = href;\n }\n collectRuns(element, next, out);\n }\n return out;\n}\n\n/** Merge adjacent runs that carry identical styling — fewer, longer spans. */\nfunction compact(runs: DocxRun[]): DocxRun[] {\n const merged: DocxRun[] = [];\n for (const run of runs) {\n const last = merged[merged.length - 1];\n if (last && last.bold === run.bold && last.italic === run.italic && last.href === run.href) {\n last.text += run.text;\n } else {\n merged.push({ ...run });\n }\n }\n return merged.filter((run) => run.text.trim() !== \"\" || run.text.includes(\"\\n\"));\n}\n\nfunction textOf(node: Node): string {\n return compact(collectRuns(node))\n .map((run) => run.text)\n .join(\"\")\n .trim();\n}\n\nfunction listItems(element: Element): DocxRun[][] {\n const items: DocxRun[][] = [];\n for (const child of Array.from(element.children)) {\n if (child.tagName.toLowerCase() !== \"li\") continue;\n // A nested list inside an `<li>` is flattened into its own items rather than\n // being rendered as a sub-list: the depth is not worth a recursive component\n // in a preview, and losing it never loses the WORDS.\n const runs = compact(collectRuns(child));\n if (runs.length > 0) items.push(runs);\n for (const nested of Array.from(child.children)) {\n const tag = nested.tagName.toLowerCase();\n if (tag === \"ul\" || tag === \"ol\") items.push(...listItems(nested));\n }\n }\n return items;\n}\n\nfunction tableBlock(element: Element): DocxBlock | undefined {\n const rows = Array.from(element.querySelectorAll(\"tr\"));\n if (rows.length === 0) return undefined;\n\n const cellsOf = (row: Element) =>\n Array.from(row.children)\n .filter((cell) => [\"td\", \"th\"].includes(cell.tagName.toLowerCase()))\n .map(textOf);\n\n const [firstRow, ...restRows] = rows;\n if (!firstRow) return undefined;\n\n // Word only emits `<th>` when the author marked a header row. Without it the\n // first row is ordinary data, and promoting it would invent a header.\n const isHeader = firstRow.querySelector(\"th\") !== null;\n return isHeader\n ? { type: \"table\", head: cellsOf(firstRow), rows: restRows.map(cellsOf) }\n : { type: \"table\", rows: rows.map(cellsOf) };\n}\n\nconst HEADING_LEVELS: Record<string, 1 | 2 | 3 | 4 | 5 | 6> = {\n h1: 1,\n h2: 2,\n h3: 3,\n h4: 4,\n h5: 5,\n h6: 6,\n};\n\n/**\n * Parse mammoth's HTML into blocks.\n *\n * Takes a `DOMParser` factory so the caller decides where the parser comes from\n * — the browser's own in an app, jsdom's in a test — instead of this module\n * reaching for a global that may not exist on a server.\n */\nexport function htmlToBlocks(html: string, parse: (markup: string) => Document): DocxBlock[] {\n const document = parse(`<body>${html}</body>`);\n const blocks: DocxBlock[] = [];\n\n const walk = (parent: Element) => {\n for (const element of Array.from(parent.children)) {\n const tag = element.tagName.toLowerCase();\n const level = HEADING_LEVELS[tag];\n\n if (level) {\n const runs = compact(collectRuns(element));\n if (runs.length > 0) blocks.push({ type: \"heading\", level, runs });\n continue;\n }\n if (tag === \"p\") {\n // A `<p>` holding only an image is the image, not an empty paragraph.\n const image = element.querySelector(\"img\");\n const runs = compact(collectRuns(element));\n if (image && runs.length === 0) {\n blocks.push(imageBlock(image));\n continue;\n }\n if (runs.length > 0) blocks.push({ type: \"paragraph\", runs });\n continue;\n }\n if (tag === \"ul\" || tag === \"ol\") {\n const items = listItems(element);\n if (items.length > 0) blocks.push({ type: \"list\", ordered: tag === \"ol\", items });\n continue;\n }\n if (tag === \"table\") {\n const table = tableBlock(element);\n if (table) blocks.push(table);\n continue;\n }\n if (tag === \"img\") {\n blocks.push(imageBlock(element));\n continue;\n }\n // Anything else — a `div`, a `section`, a tag mammoth grew in a later\n // version — contributes its CHILDREN, never itself. That is what makes\n // this an allowlist that cannot silently let markup through.\n walk(element);\n }\n };\n\n walk(document.body);\n return blocks;\n}\n\nfunction imageBlock(element: Element): DocxBlock {\n const alt = element.getAttribute(\"alt\")?.trim();\n return {\n type: \"image\",\n // mammoth inlines images as `data:` URIs by default, so there is no remote\n // origin here — and no `src` at all is better than a URL we did not vet.\n src: element.getAttribute(\"src\") ?? \"\",\n ...(alt ? { alt } : {}),\n };\n}\n\n/** Written before a list item's own text in the projection. */\nexport const DOCX_LIST_BULLET = \"• \";\n\n/** Written between two cells of the same row. */\nexport const DOCX_CELL_SEPARATOR = \"\\t\";\n\n/** The header row's `row`, so one field can name every row of a table. */\nexport const DOCX_HEAD_ROW = -1;\n\n/**\n * Where a stretch of the projection came from in the block model.\n *\n * Deliberately one shape with optional fields rather than a union: the renderer\n * looks a span up by \"this block, that item\" in three places, and a union would\n * make each of those a type narrowing exercise for no gain.\n */\nexport interface DocxRef {\n /** Index into the `blocks` array. */\n block: number;\n /** Which item, for a `list` block. */\n item?: number;\n /** Which row, for a `table` block. {@link DOCX_HEAD_ROW} is the header. */\n row?: number;\n}\n\n/**\n * Plain-text projection of a parsed document, plus the map back into the blocks.\n *\n * Rows rather than cells are the finest ref: one builder has one separator, and\n * a row's cells are joined by a tab while the rows themselves are joined by a\n * newline. The renderer walks a row's cells the same way it walks a paragraph's\n * runs — accumulating lengths — so the granularity of the MARK is still the\n * character, even though the granularity of the REF is the row.\n */\nexport function blocksToTextWithMap(blocks: DocxBlock[]): TextIndex<DocxRef> {\n const builder = createTextIndexBuilder<DocxRef>();\n blocks.forEach((block, index) => {\n if (block.type === \"heading\" || block.type === \"paragraph\") {\n builder.push(block.runs.map((run) => run.text).join(\"\"), { block: index });\n return;\n }\n if (block.type === \"list\") {\n block.items.forEach((item, item_) => {\n builder.push(`${DOCX_LIST_BULLET}${item.map((run) => run.text).join(\"\")}`, {\n block: index,\n item: item_,\n });\n });\n return;\n }\n if (block.type === \"table\") {\n if (block.head) {\n builder.push(block.head.join(DOCX_CELL_SEPARATOR), { block: index, row: DOCX_HEAD_ROW });\n }\n block.rows.forEach((row, rowIndex) => {\n builder.push(row.join(DOCX_CELL_SEPARATOR), { block: index, row: rowIndex });\n });\n }\n });\n return builder.build();\n}\n\n/**\n * Plain-text projection of a parsed document — powers search, copy and the raw\n * view. A thin wrapper so the projection has exactly one definition: it is the\n * index's own text, never a second assembly that could drift from it.\n */\nexport function blocksToText(blocks: DocxBlock[]): string {\n return blocksToTextWithMap(blocks).text;\n}\n"],"mappings":"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","names":[]}
@@ -65,4 +65,4 @@ export {
65
65
  chunkOffset,
66
66
  spanAt
67
67
  };
68
- //# sourceMappingURL=chunk-UL43NGUG.js.map
68
+ //# sourceMappingURL=chunk-5YE2MUS2.js.map
@@ -1,7 +1,4 @@
1
1
  "use client";
2
- import {
3
- PROTOCOL_VERSION
4
- } from "./chunk-AORNMH77.js";
5
2
 
6
3
  // src/adapters/code/code-language.ts
7
4
  var LANGUAGE_BY_EXTENSION = {
@@ -71,17 +68,8 @@ function languageFor(fileName) {
71
68
  return map[extension] ?? map[name];
72
69
  }
73
70
 
74
- // src/adapters/code/code-manifest.ts
75
- var codeManifest = {
76
- id: "code",
77
- protocol: PROTOCOL_VERSION,
78
- extensions: CODE_EXTENSIONS,
79
- capabilities: { text: true, search: true, highlight: ["quote", "range"] },
80
- requires: ["shiki"]
81
- };
82
-
83
71
  export {
84
- languageFor,
85
- codeManifest
72
+ CODE_EXTENSIONS,
73
+ languageFor
86
74
  };
87
- //# sourceMappingURL=chunk-5OMONA2G.js.map
75
+ //# sourceMappingURL=chunk-6FVJ4D7W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/code/code-language.ts"],"sourcesContent":["/**\n * Extension → Shiki language id.\n *\n * A pure data module on purpose: `code-manifest.ts` derives the extensions it\n * claims from this map, so the list the registry matches on and the list the\n * highlighter can actually tokenize can never drift apart. Adding a language is\n * one line here.\n *\n * What is deliberately ABSENT is as load-bearing as what is present. `json`,\n * `csv`, `md` and `svg` are claimed by their own adapters — a JSON file gets a\n * collapsible tree, not a coloured wall of text — and an extension listed in two\n * manifests would be decided by registration order rather than by design.\n */\n\nimport type { BundledLanguage } from \"shiki\";\n\n/**\n * Shiki's own language ids.\n *\n * `satisfies Record<string, BundledLanguage>` is the point of the annotation: a\n * typo'd grammar name fails typecheck HERE, rather than at runtime as a file\n * that silently refuses to highlight. Type-only import, so this stays a pure\n * data module with no edge to the engine.\n */\nconst LANGUAGE_BY_EXTENSION = {\n bash: \"shellscript\",\n c: \"c\",\n cc: \"cpp\",\n cjs: \"javascript\",\n cpp: \"cpp\",\n cs: \"csharp\",\n css: \"css\",\n cts: \"typescript\",\n dart: \"dart\",\n diff: \"diff\",\n dockerfile: \"docker\",\n go: \"go\",\n gql: \"graphql\",\n graphql: \"graphql\",\n groovy: \"groovy\",\n h: \"c\",\n hcl: \"hcl\",\n hpp: \"cpp\",\n htm: \"html\",\n html: \"html\",\n ini: \"ini\",\n java: \"java\",\n js: \"javascript\",\n jsx: \"jsx\",\n kt: \"kotlin\",\n kts: \"kotlin\",\n less: \"less\",\n lua: \"lua\",\n makefile: \"make\",\n mjs: \"javascript\",\n mts: \"typescript\",\n patch: \"diff\",\n php: \"php\",\n pl: \"perl\",\n proto: \"proto\",\n ps1: \"powershell\",\n py: \"python\",\n r: \"r\",\n rb: \"ruby\",\n rs: \"rust\",\n sass: \"sass\",\n scala: \"scala\",\n scss: \"scss\",\n sh: \"shellscript\",\n sql: \"sql\",\n svelte: \"svelte\",\n swift: \"swift\",\n tf: \"terraform\",\n toml: \"toml\",\n ts: \"typescript\",\n tsx: \"tsx\",\n vue: \"vue\",\n xml: \"xml\",\n yaml: \"yaml\",\n yml: \"yaml\",\n zsh: \"shellscript\",\n} as const satisfies Record<string, BundledLanguage>;\n\n/** Every extension the code adapter claims, lowercase and without the dot. */\nexport const CODE_EXTENSIONS = Object.keys(LANGUAGE_BY_EXTENSION);\n\n/**\n * The Shiki language for a file name, or `undefined` when nothing matches.\n *\n * Matches the extension first, then the whole (lowercased) file name — that is\n * what catches `Dockerfile` and `Makefile`, which carry their language in the\n * name and have no extension at all.\n */\nexport function languageFor(fileName: string): BundledLanguage | undefined {\n const name = fileName.toLowerCase();\n const dot = name.lastIndexOf(\".\");\n const extension = dot > 0 ? name.slice(dot + 1) : name;\n const map: Record<string, BundledLanguage> = LANGUAGE_BY_EXTENSION;\n return map[extension] ?? map[name];\n}\n"],"mappings":";;;AAwBA,IAAM,wBAAwB;AAAA,EAC5B,MAAM;AAAA,EACN,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,GAAG;AAAA,EACH,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,UAAU;AAAA,EACV,KAAK;AAAA,EACL,KAAK;AAAA,EACL,OAAO;AAAA,EACP,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AACP;AAGO,IAAM,kBAAkB,OAAO,KAAK,qBAAqB;AASzD,SAAS,YAAY,UAA+C;AACzE,QAAM,OAAO,SAAS,YAAY;AAClC,QAAM,MAAM,KAAK,YAAY,GAAG;AAChC,QAAM,YAAY,MAAM,IAAI,KAAK,MAAM,MAAM,CAAC,IAAI;AAClD,QAAM,MAAuC;AAC7C,SAAO,IAAI,SAAS,KAAK,IAAI,IAAI;AACnC;","names":[]}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  PROTOCOL_VERSION
4
- } from "./chunk-AORNMH77.js";
4
+ } from "./chunk-CIT2HHRI.js";
5
5
 
6
6
  // src/adapters/xlsx/xlsx-manifest.ts
7
7
  var xlsxManifest = {
@@ -23,4 +23,4 @@ var xlsxManifest = {
23
23
  export {
24
24
  xlsxManifest
25
25
  };
26
- //# sourceMappingURL=chunk-RGWDIQJR.js.map
26
+ //# sourceMappingURL=chunk-7EASJBQM.js.map
@@ -0,0 +1,26 @@
1
+ "use client";
2
+
3
+ // src/core/scroll-host.ts
4
+ var SCROLL_HOST_SELECTOR = '[data-slot="file-viewer-content"]';
5
+ function findScrollHost(node) {
6
+ if (!node) return null;
7
+ const shell = node.closest(SCROLL_HOST_SELECTOR);
8
+ if (shell) return shell;
9
+ const view = node.ownerDocument?.defaultView;
10
+ if (!view) return null;
11
+ let current = node.parentElement;
12
+ while (current) {
13
+ const { overflowY, overflowX } = view.getComputedStyle(current);
14
+ if (/(auto|scroll|overlay)/.test(overflowY) || /(auto|scroll|overlay)/.test(overflowX)) {
15
+ return current;
16
+ }
17
+ current = current.parentElement;
18
+ }
19
+ return null;
20
+ }
21
+
22
+ export {
23
+ SCROLL_HOST_SELECTOR,
24
+ findScrollHost
25
+ };
26
+ //# sourceMappingURL=chunk-7PGGKQVY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/scroll-host.ts"],"sourcesContent":["/**\n * Finding the element a renderer actually scrolls inside.\n *\n * `FileViewerContent` is THE scroll boundary (`viewer-components.md`), and now\n * that page and zoom chrome lives in the shell (ADR 0026) even the PDF adapter\n * no longer has fixed controls of its own to justify a second viewport. But a\n * renderer still has to MEASURE that box — a fit-to-width scale is a function of\n * the viewport's width, and a virtualized page list is a function of its height.\n *\n * Resolved from the DOM rather than passed as a prop because a renderer may be\n * mounted anywhere: inside the shell, inside a consumer's own frame, or bare in\n * a test. Same shape as extend-hq/ui's `resolveScrollAreaViewport`.\n */\n\n/** The `data-slot` the shell's scrolling pane carries. */\nexport const SCROLL_HOST_SELECTOR = '[data-slot=\"file-viewer-content\"]';\n\n/**\n * The nearest scrolling ancestor of `node`.\n *\n * Prefers the shell's pane by its stable `data-slot`; falls back to the first\n * ancestor whose computed overflow actually scrolls, so a renderer composed into\n * someone else's frame still measures the right box. `null` when nothing\n * scrolls — the caller should then fall back to its own element.\n */\nexport function findScrollHost(node: Element | null | undefined): HTMLElement | null {\n if (!node) return null;\n const shell = node.closest<HTMLElement>(SCROLL_HOST_SELECTOR);\n if (shell) return shell;\n\n const view = node.ownerDocument?.defaultView;\n if (!view) return null;\n let current = node.parentElement;\n while (current) {\n const { overflowY, overflowX } = view.getComputedStyle(current);\n if (/(auto|scroll|overlay)/.test(overflowY) || /(auto|scroll|overlay)/.test(overflowX)) {\n return current;\n }\n current = current.parentElement;\n }\n return null;\n}\n"],"mappings":";;;AAeO,IAAM,uBAAuB;AAU7B,SAAS,eAAe,MAAsD;AACnF,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,KAAK,QAAqB,oBAAoB;AAC5D,MAAI,MAAO,QAAO;AAElB,QAAM,OAAO,KAAK,eAAe;AACjC,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,UAAU,KAAK;AACnB,SAAO,SAAS;AACd,UAAM,EAAE,WAAW,UAAU,IAAI,KAAK,iBAAiB,OAAO;AAC9D,QAAI,wBAAwB,KAAK,SAAS,KAAK,wBAAwB,KAAK,SAAS,GAAG;AACtF,aAAO;AAAA,IACT;AACA,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;","names":[]}
@@ -1,37 +1,17 @@
1
1
  "use client";
2
+ import {
3
+ GRID_CELL_SEPARATOR,
4
+ GRID_HEAD_ROW
5
+ } from "./chunk-TLCNGQKL.js";
2
6
  import {
3
7
  MarkedText
4
- } from "./chunk-2NQ4RSJ3.js";
8
+ } from "./chunk-GNBD53AT.js";
5
9
  import {
6
- chunkOffset,
7
- createTextIndexBuilder
8
- } from "./chunk-UL43NGUG.js";
10
+ chunkOffset
11
+ } from "./chunk-5YE2MUS2.js";
9
12
  import {
10
13
  useScrollActiveHighlightIntoView
11
- } from "./chunk-VO2273Z2.js";
12
-
13
- // src/components/grid-text.ts
14
- var GRID_CELL_SEPARATOR = " ";
15
- var GRID_ROW_SEPARATOR = "\n";
16
- var GRID_SHEET_SEPARATOR = "\n\n";
17
- var GRID_NAME_ROW = -2;
18
- var GRID_HEAD_ROW = -1;
19
- function gridToText(sheets) {
20
- const builder = createTextIndexBuilder({ separator: GRID_ROW_SEPARATOR });
21
- sheets.forEach((sheet, index) => {
22
- let separator = index === 0 ? void 0 : GRID_SHEET_SEPARATOR;
23
- const push = (chunk, row) => {
24
- builder.push(chunk, { sheet: index, row }, separator);
25
- if (chunk.length > 0) separator = void 0;
26
- };
27
- if (sheet.name) push(sheet.name, GRID_NAME_ROW);
28
- push(sheet.columns.join(GRID_CELL_SEPARATOR), GRID_HEAD_ROW);
29
- sheet.rows.forEach((row, rowIndex) => {
30
- push(row.join(GRID_CELL_SEPARATOR), rowIndex);
31
- });
32
- });
33
- return builder.build();
34
- }
14
+ } from "./chunk-IB4N72FA.js";
35
15
 
36
16
  // src/components/sheet-table.tsx
37
17
  import {
@@ -111,7 +91,6 @@ function SheetTable({
111
91
  }
112
92
 
113
93
  export {
114
- gridToText,
115
94
  SheetTable
116
95
  };
117
- //# sourceMappingURL=chunk-XBOR2YXS.js.map
96
+ //# sourceMappingURL=chunk-AK7F3IZE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/sheet-table.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * The one grid every tabular format renders into.\n *\n * CSV and XLSX arrive through completely different parsers and end up wanting\n * exactly the same thing: a header row, body rows, a truncation notice and an\n * `sr-only` caption. That is a PATTERN, not a coincidence, so it is named once\n * here rather than copied a second time (`.claude/rules/design-first.md` —\n * patterns over instances).\n *\n * It stays deliberately dumb: no sorting, no filtering, no virtualization. A\n * preview pane is for looking; past a few thousand rows the right component is\n * `DataTable` in `@elabs-ai/components-data`, which virtualizes.\n */\n\nimport {\n cn,\n Table,\n TableBody,\n TableCaption,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n useLocale,\n} from \"@elabs-ai/components-ui\";\nimport { useRef } from \"react\";\n\nimport { type MarkRanges } from \"../core/highlight-marks\";\nimport { chunkOffset } from \"../core/text-index\";\nimport { useScrollActiveHighlightIntoView } from \"../core/use-highlight-scroll\";\nimport { GRID_CELL_SEPARATOR, GRID_HEAD_ROW } from \"./grid-text\";\nimport { MarkedText } from \"./marked-text\";\n\nexport interface SheetTableProps {\n /** Header cells. May be empty — the grid still renders its body. */\n columns: string[];\n /** Body rows, already capped by the caller. */\n rows: string[][];\n /** Total body rows in the file, when more exist than `rows` holds. */\n totalRows?: number;\n className?: string;\n /** Marks to paint, in projection offsets. */\n marks?: MarkRanges;\n /**\n * Where a row's first cell begins in the projection — {@link GRID_HEAD_ROW}\n * for the header. A function rather than an array so a workbook can answer\n * for the sheet being drawn without slicing an index per tab.\n */\n rowStart?: (row: number) => number | undefined;\n /** Scrolls the current mark into this grid's own viewport when it changes. */\n activeHighlightId?: string | null;\n}\n\nexport function SheetTable({\n columns,\n rows,\n totalRows,\n className,\n marks,\n rowStart,\n activeHighlightId,\n}: SheetTableProps) {\n const { t, formatNumber } = useLocale();\n const viewport = useRef<HTMLDivElement>(null);\n\n useScrollActiveHighlightIntoView(viewport, activeHighlightId);\n\n return (\n <div className={cn(\"flex h-full min-h-0 flex-col gap-2\", className)}>\n {totalRows !== undefined && (\n // A status, not an error: the file is fine, we are simply showing part\n // of it (loading-states.md — a capability bound is news, not an alarm).\n <p role=\"status\" className=\"text-meta text-muted-foreground shrink-0\">\n {t(\"viewer.table.truncated\", { count: formatNumber(rows.length) })}\n </p>\n )}\n {/* The grid keeps its OWN viewport, unlike the flowing formats: a workbook\n puts a sheet-tab bar above it that must not scroll away with the rows.\n A table cell is not focusable, so a scrollable region wrapping one is\n unreachable from a keyboard (WCAG 2.1.1) — `tabIndex={0}` makes it a\n real stop that arrow keys and Page Up/Down drive. `group`, not\n `region`, so a sheet does not mint a second landmark inside the\n viewer's content region. */}\n <div\n ref={viewport}\n tabIndex={0}\n role=\"group\"\n aria-label={t(\"viewer.content\")}\n className=\"focus-ring min-h-0 flex-1 overflow-auto\"\n >\n <Table>\n <TableCaption className=\"sr-only\">\n {t(\"viewer.table.caption\", {\n rows: formatNumber(totalRows ?? rows.length),\n columns: formatNumber(columns.length),\n })}\n </TableCaption>\n <TableHeader>\n <TableRow>\n {columns.map((column, index) => (\n // The file's own header text is the only identity a column has;\n // an empty one still needs a cell so the grid stays aligned.\n <TableHead key={`${column}-${String(index)}`} scope=\"col\">\n <MarkedText\n text={column}\n marks={marks}\n start={chunkOffset(\n columns,\n index,\n rowStart?.(GRID_HEAD_ROW),\n GRID_CELL_SEPARATOR,\n )}\n />\n </TableHead>\n ))}\n </TableRow>\n </TableHeader>\n <TableBody>\n {rows.map((row, rowIndex) => {\n const start = rowStart?.(rowIndex);\n return (\n <TableRow key={rowIndex}>\n {columns.map((_, columnIndex) => (\n <TableCell key={columnIndex} className=\"whitespace-pre-wrap\">\n <MarkedText\n text={row[columnIndex] ?? \"\"}\n marks={marks}\n start={chunkOffset(row, columnIndex, start, GRID_CELL_SEPARATOR)}\n />\n </TableCell>\n ))}\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,cAAc;AA+Cf,cAkBA,YAlBA;AAnBD,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAoB;AAClB,QAAM,EAAE,GAAG,aAAa,IAAI,UAAU;AACtC,QAAM,WAAW,OAAuB,IAAI;AAE5C,mCAAiC,UAAU,iBAAiB;AAE5D,SACE,qBAAC,SAAI,WAAW,GAAG,sCAAsC,SAAS,GAC/D;AAAA,kBAAc;AAAA;AAAA,IAGb,oBAAC,OAAE,MAAK,UAAS,WAAU,4CACxB,YAAE,0BAA0B,EAAE,OAAO,aAAa,KAAK,MAAM,EAAE,CAAC,GACnE;AAAA,IASF;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,UAAU;AAAA,QACV,MAAK;AAAA,QACL,cAAY,EAAE,gBAAgB;AAAA,QAC9B,WAAU;AAAA,QAEV,+BAAC,SACC;AAAA,8BAAC,gBAAa,WAAU,WACrB,YAAE,wBAAwB;AAAA,YACzB,MAAM,aAAa,aAAa,KAAK,MAAM;AAAA,YAC3C,SAAS,aAAa,QAAQ,MAAM;AAAA,UACtC,CAAC,GACH;AAAA,UACA,oBAAC,eACC,8BAAC,YACE,kBAAQ,IAAI,CAAC,QAAQ;AAAA;AAAA;AAAA,YAGpB,oBAAC,aAA6C,OAAM,OAClD;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM;AAAA,gBACN;AAAA,gBACA,OAAO;AAAA,kBACL;AAAA,kBACA;AAAA,kBACA,WAAW,aAAa;AAAA,kBACxB;AAAA,gBACF;AAAA;AAAA,YACF,KAVc,GAAG,MAAM,IAAI,OAAO,KAAK,CAAC,EAW1C;AAAA,WACD,GACH,GACF;AAAA,UACA,oBAAC,aACE,eAAK,IAAI,CAAC,KAAK,aAAa;AAC3B,kBAAM,QAAQ,WAAW,QAAQ;AACjC,mBACE,oBAAC,YACE,kBAAQ,IAAI,CAAC,GAAG,gBACf,oBAAC,aAA4B,WAAU,uBACrC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM,IAAI,WAAW,KAAK;AAAA,gBAC1B;AAAA,gBACA,OAAO,YAAY,KAAK,aAAa,OAAO,mBAAmB;AAAA;AAAA,YACjE,KALc,WAMhB,CACD,KATY,QAUf;AAAA,UAEJ,CAAC,GACH;AAAA,WACF;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,34 @@
1
+ "use client";
2
+
3
+ // src/adapters/markdown/markdown-marks.ts
4
+ import { cn } from "@elabs-ai/components-ui";
5
+ var UNMARKED = { marked: false, active: false };
6
+ function blockMark(marks, node) {
7
+ const start = node?.position?.start?.offset;
8
+ const end = node?.position?.end?.offset;
9
+ if (start === void 0 || end === void 0 || end <= start) return UNMARKED;
10
+ let marked = false;
11
+ let active = false;
12
+ marks.ranges.forEach(([from, to], index) => {
13
+ if (from >= end || to <= start) return;
14
+ marked = true;
15
+ if (index === marks.activeIndex) active = true;
16
+ });
17
+ return marked ? { marked, active } : UNMARKED;
18
+ }
19
+ var MARKED_CLASS = "bg-highlight/40 border-s-2 border-highlight rounded-e-sm ps-2 -ms-2";
20
+ var ACTIVE_CLASS = "bg-highlight-active/40 border-s-4 border-highlight-active rounded-e-sm ps-2 -ms-2";
21
+ function blockMarkAttributes(mark, className) {
22
+ if (!mark.marked) return className === void 0 ? {} : { className };
23
+ return {
24
+ "data-slot": "highlight-block",
25
+ ...mark.active ? { "data-active": "", "aria-current": "true" } : {},
26
+ className: cn(mark.active ? ACTIVE_CLASS : MARKED_CLASS, className)
27
+ };
28
+ }
29
+
30
+ export {
31
+ blockMark,
32
+ blockMarkAttributes
33
+ };
34
+ //# sourceMappingURL=chunk-AOYEL7O3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/markdown/markdown-marks.ts"],"sourcesContent":["/**\n * Markdown highlights are BLOCK-shaped, and that is a deliberate downgrade.\n *\n * Every other text adapter marks characters, because its `document.text` is the\n * thing on screen. Markdown's is not: `text` is the SOURCE, so offset 12 might\n * land in the middle of `**bold**` — two of whose characters are never drawn at\n * all — or inside a link's URL, which the reader only sees as a word. There is\n * no honest character-level mapping from source offsets to rendered text\n * without re-implementing the parser's own position bookkeeping for inline\n * nodes, and getting that subtly wrong would put marks on the wrong words while\n * looking exactly as confident as a correct one.\n *\n * So a markdown mark says \"this passage is in HERE\" — the paragraph, heading,\n * list item or fence whose source span the mark touches is plated whole. That\n * is enough for the case the feature exists for (a citation pointing a reader at\n * the passage an answer came from) and it cannot be subtly wrong: a block either\n * contains the source range or it does not.\n *\n * The offsets are trustworthy because the renderer runs Streamdown in\n * `mode=\"static\"`, which hands the WHOLE file to one parse — no per-block\n * splitting, no incomplete-token repair — so every hast node's\n * `position.start.offset` is an offset into `document.text` itself.\n */\n\nimport { cn } from \"@elabs-ai/components-ui\";\n\nimport type { MarkRanges } from \"../../core/highlight-marks\";\n\n/** The subset of a unist `Position` this needs. Structural, so no unist import. */\nexport interface SourcePosition {\n start?: { offset?: number };\n end?: { offset?: number };\n}\n\n/** A hast node as the component overrides receive it. */\nexport interface PositionedNode {\n position?: SourcePosition;\n}\n\n/** Whether a block carries any mark, and whether it carries the current one. */\nexport interface BlockMark {\n marked: boolean;\n active: boolean;\n}\n\nconst UNMARKED: BlockMark = { marked: false, active: false };\n\n/**\n * Which marks land inside one block's source span.\n *\n * Half-open on both sides and strict, so a mark ending exactly where a block\n * begins belongs to the block before it — otherwise the paragraph after a\n * marked heading would light up too, on every citation.\n */\nexport function blockMark(marks: MarkRanges, node: PositionedNode | undefined): BlockMark {\n const start = node?.position?.start?.offset;\n const end = node?.position?.end?.offset;\n if (start === undefined || end === undefined || end <= start) return UNMARKED;\n\n let marked = false;\n let active = false;\n marks.ranges.forEach(([from, to], index) => {\n if (from >= end || to <= start) return;\n marked = true;\n if (index === marks.activeIndex) active = true;\n });\n return marked ? { marked, active } : UNMARKED;\n}\n\n/**\n * The attributes a plated block carries.\n *\n * `data-slot`/`data-active` are the same seam the character marks use, so one\n * selector finds \"the current highlight\" whatever adapter drew it — that is what\n * `useScrollActiveHighlightIntoView` scrolls to. `aria-current` is the part that\n * is not decoration: the active block is announced as the current one rather\n * than being distinguishable only by a warmer plate (WCAG 1.4.1).\n */\nexport interface BlockMarkAttributes {\n \"data-slot\"?: \"highlight-block\";\n /**\n * Present-or-absent, never `\"false\"` — the same shape `MatchHighlight`'s marks\n * and the PDF's boxes emit, so one selector (`[data-active]`) finds the current\n * highlight whichever painter drew it.\n */\n \"data-active\"?: \"\";\n \"aria-current\"?: \"true\";\n className?: string;\n}\n\n/**\n * Plate classes.\n *\n * A marked block gets a fill AND a rail; the current one doubles the rail's\n * width on top of warming the fill, so \"which one am I on\" survives greyscale\n * and a colour-vision difference. The negative margins let the plate bleed into\n * the prose column's own gutter instead of shifting the text when it appears.\n */\nconst MARKED_CLASS = \"bg-highlight/40 border-s-2 border-highlight rounded-e-sm ps-2 -ms-2\";\nconst ACTIVE_CLASS =\n \"bg-highlight-active/40 border-s-4 border-highlight-active rounded-e-sm ps-2 -ms-2\";\n\n/**\n * Spreadable attributes for a block, given how the marks fell on it.\n *\n * The block's OWN classes are merged last and therefore win, which is the\n * deliberate answer for a fenced code block: `bg-surface-muted` beats the\n * plate's fill, so the code keeps its own ground and the rail alone says it is\n * cited. Leaving the order to CSS source order instead would make that outcome\n * accidental.\n */\nexport function blockMarkAttributes(mark: BlockMark, className?: string): BlockMarkAttributes {\n if (!mark.marked) return className === undefined ? {} : { className };\n return {\n \"data-slot\": \"highlight-block\",\n ...(mark.active ? { \"data-active\": \"\" as const, \"aria-current\": \"true\" as const } : {}),\n className: cn(mark.active ? ACTIVE_CLASS : MARKED_CLASS, className),\n };\n}\n"],"mappings":";;;AAwBA,SAAS,UAAU;AAqBnB,IAAM,WAAsB,EAAE,QAAQ,OAAO,QAAQ,MAAM;AASpD,SAAS,UAAU,OAAmB,MAA6C;AACxF,QAAM,QAAQ,MAAM,UAAU,OAAO;AACrC,QAAM,MAAM,MAAM,UAAU,KAAK;AACjC,MAAI,UAAU,UAAa,QAAQ,UAAa,OAAO,MAAO,QAAO;AAErE,MAAI,SAAS;AACb,MAAI,SAAS;AACb,QAAM,OAAO,QAAQ,CAAC,CAAC,MAAM,EAAE,GAAG,UAAU;AAC1C,QAAI,QAAQ,OAAO,MAAM,MAAO;AAChC,aAAS;AACT,QAAI,UAAU,MAAM,YAAa,UAAS;AAAA,EAC5C,CAAC;AACD,SAAO,SAAS,EAAE,QAAQ,OAAO,IAAI;AACvC;AA+BA,IAAM,eAAe;AACrB,IAAM,eACJ;AAWK,SAAS,oBAAoB,MAAiB,WAAyC;AAC5F,MAAI,CAAC,KAAK,OAAQ,QAAO,cAAc,SAAY,CAAC,IAAI,EAAE,UAAU;AACpE,SAAO;AAAA,IACL,aAAa;AAAA,IACb,GAAI,KAAK,SAAS,EAAE,eAAe,IAAa,gBAAgB,OAAgB,IAAI,CAAC;AAAA,IACrF,WAAW,GAAG,KAAK,SAAS,eAAe,cAAc,SAAS;AAAA,EACpE;AACF;","names":[]}
@@ -0,0 +1,61 @@
1
+ "use client";
2
+
3
+ // src/adapters/code/code-theme.ts
4
+ var SCOPE_COLORS = [
5
+ {
6
+ scopes: ["comment", "punctuation.definition.comment"],
7
+ token: "--code-comment",
8
+ italic: true
9
+ },
10
+ { scopes: ["string", "string.quoted", "punctuation.definition.string"], token: "--code-string" },
11
+ {
12
+ scopes: ["constant", "constant.language", "constant.character", "support.constant"],
13
+ token: "--code-constant"
14
+ },
15
+ { scopes: ["constant.numeric", "keyword.other.unit"], token: "--code-number" },
16
+ {
17
+ scopes: ["keyword", "keyword.control", "keyword.operator", "storage", "storage.type"],
18
+ token: "--code-keyword"
19
+ },
20
+ {
21
+ scopes: ["entity.name.function", "support.function", "meta.function-call"],
22
+ token: "--code-function"
23
+ },
24
+ {
25
+ scopes: ["entity.name.type", "entity.name.class", "support.type", "support.class"],
26
+ token: "--code-type"
27
+ },
28
+ { scopes: ["entity.name.tag", "punctuation.definition.tag"], token: "--code-tag" },
29
+ { scopes: ["variable", "support.variable", "meta.object-literal.key"], token: "--code-property" },
30
+ // Same colour as a property, but the one other scope that leans. Its own rule,
31
+ // for the reason in the docblock above.
32
+ { scopes: ["entity.other.attribute-name"], token: "--code-property", italic: true }
33
+ ];
34
+ var codeViewerTheme = {
35
+ name: "brand-ui-viewer",
36
+ // Shiki uses `type` only to pick its own light/dark defaults, which this theme
37
+ // overrides wholesale. It is `css` — neither, resolved at paint time.
38
+ type: "dark",
39
+ colors: {
40
+ // The renderer owns the surface; the theme must not paint over it.
41
+ "editor.background": "transparent",
42
+ "editor.foreground": "var(--code-foreground)"
43
+ },
44
+ fg: "var(--code-foreground)",
45
+ bg: "transparent",
46
+ settings: [
47
+ { settings: { foreground: "var(--code-foreground)" } },
48
+ ...SCOPE_COLORS.map(({ scopes, token, italic }) => ({
49
+ scope: scopes,
50
+ settings: {
51
+ foreground: `var(${token})`,
52
+ ...italic ? { fontStyle: "italic" } : void 0
53
+ }
54
+ }))
55
+ ]
56
+ };
57
+
58
+ export {
59
+ codeViewerTheme
60
+ };
61
+ //# sourceMappingURL=chunk-BDUFH66C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/code/code-theme.ts"],"sourcesContent":["/**\n * The Shiki theme the code adapter tokenizes with — brand tokens, not a\n * recoloured `github-light`.\n *\n * Every colour here is a `var(--code-*)` reference rather than a literal, which\n * is what makes ONE theme correct in EVERY brand theme: the active `data-theme`\n * block decides what `--code-keyword` resolves to, so a runtime theme switch\n * recolours already-tokenized code with no re-highlight, no `MutationObserver`\n * and no cache to invalidate. It is the same technique Shiki ships as\n * `createCssVariablesTheme`, written out by hand for one reason: that helper\n * maps a fixed, smaller scope set and cannot reach `--code-type` or\n * `--code-tag`, which this design system does define.\n *\n * `@elabs-ai/components-ai`'s `CodeBlock` resolves the same tokens\n * the other way round (oklch → hex, re-derived per theme change) because\n * `@streamdown/code` freezes its themes at import time and cannot take a live\n * `var()`. Nothing here needs that, so nothing here pays for it.\n *\n * Type-only import: this module is pure data and holds no edge to the engine.\n */\n\nimport type { ThemeRegistrationRaw } from \"shiki\";\n\n/**\n * TextMate scopes → brand code tokens.\n *\n * Ordered coarse-to-fine, because Shiki resolves the LAST matching rule: the\n * broad `constant` rule has to come before `constant.numeric` or every number\n * would read as a constant.\n *\n * `italic` is a convention, not a colour, so it is theme-safe — but it is\n * declared PER RULE and never inferred from the scope list. A rule's styling\n * applies to every scope in its array, so a lone italic scope sharing an array\n * with ordinary ones italicises all of them: bundling `entity.other.attribute-name`\n * with `variable` slanted every plain identifier in the file, which is the most\n * common token class there is. Give an italic scope its own entry.\n */\nconst SCOPE_COLORS: ReadonlyArray<{\n readonly scopes: string[];\n readonly token: string;\n readonly italic?: boolean;\n}> = [\n {\n scopes: [\"comment\", \"punctuation.definition.comment\"],\n token: \"--code-comment\",\n italic: true,\n },\n { scopes: [\"string\", \"string.quoted\", \"punctuation.definition.string\"], token: \"--code-string\" },\n {\n scopes: [\"constant\", \"constant.language\", \"constant.character\", \"support.constant\"],\n token: \"--code-constant\",\n },\n { scopes: [\"constant.numeric\", \"keyword.other.unit\"], token: \"--code-number\" },\n {\n scopes: [\"keyword\", \"keyword.control\", \"keyword.operator\", \"storage\", \"storage.type\"],\n token: \"--code-keyword\",\n },\n {\n scopes: [\"entity.name.function\", \"support.function\", \"meta.function-call\"],\n token: \"--code-function\",\n },\n {\n scopes: [\"entity.name.type\", \"entity.name.class\", \"support.type\", \"support.class\"],\n token: \"--code-type\",\n },\n { scopes: [\"entity.name.tag\", \"punctuation.definition.tag\"], token: \"--code-tag\" },\n { scopes: [\"variable\", \"support.variable\", \"meta.object-literal.key\"], token: \"--code-property\" },\n // Same colour as a property, but the one other scope that leans. Its own rule,\n // for the reason in the docblock above.\n { scopes: [\"entity.other.attribute-name\"], token: \"--code-property\", italic: true },\n];\n\n/**\n * The theme, built once at module load. Shiki treats a theme object as opaque\n * data, so one instance is safely shared by every highlighter.\n */\nexport const codeViewerTheme: ThemeRegistrationRaw = {\n name: \"brand-ui-viewer\",\n // Shiki uses `type` only to pick its own light/dark defaults, which this theme\n // overrides wholesale. It is `css` — neither, resolved at paint time.\n type: \"dark\",\n colors: {\n // The renderer owns the surface; the theme must not paint over it.\n \"editor.background\": \"transparent\",\n \"editor.foreground\": \"var(--code-foreground)\",\n },\n fg: \"var(--code-foreground)\",\n bg: \"transparent\",\n settings: [\n { settings: { foreground: \"var(--code-foreground)\" } },\n ...SCOPE_COLORS.map(({ scopes, token, italic }) => ({\n scope: scopes,\n settings: {\n foreground: `var(${token})`,\n ...(italic ? { fontStyle: \"italic\" } : undefined),\n },\n })),\n ],\n};\n"],"mappings":";;;AAqCA,IAAM,eAID;AAAA,EACH;AAAA,IACE,QAAQ,CAAC,WAAW,gCAAgC;AAAA,IACpD,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AAAA,EACA,EAAE,QAAQ,CAAC,UAAU,iBAAiB,+BAA+B,GAAG,OAAO,gBAAgB;AAAA,EAC/F;AAAA,IACE,QAAQ,CAAC,YAAY,qBAAqB,sBAAsB,kBAAkB;AAAA,IAClF,OAAO;AAAA,EACT;AAAA,EACA,EAAE,QAAQ,CAAC,oBAAoB,oBAAoB,GAAG,OAAO,gBAAgB;AAAA,EAC7E;AAAA,IACE,QAAQ,CAAC,WAAW,mBAAmB,oBAAoB,WAAW,cAAc;AAAA,IACpF,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,QAAQ,CAAC,wBAAwB,oBAAoB,oBAAoB;AAAA,IACzE,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,QAAQ,CAAC,oBAAoB,qBAAqB,gBAAgB,eAAe;AAAA,IACjF,OAAO;AAAA,EACT;AAAA,EACA,EAAE,QAAQ,CAAC,mBAAmB,4BAA4B,GAAG,OAAO,aAAa;AAAA,EACjF,EAAE,QAAQ,CAAC,YAAY,oBAAoB,yBAAyB,GAAG,OAAO,kBAAkB;AAAA;AAAA;AAAA,EAGhG,EAAE,QAAQ,CAAC,6BAA6B,GAAG,OAAO,mBAAmB,QAAQ,KAAK;AACpF;AAMO,IAAM,kBAAwC;AAAA,EACnD,MAAM;AAAA;AAAA;AAAA,EAGN,MAAM;AAAA,EACN,QAAQ;AAAA;AAAA,IAEN,qBAAqB;AAAA,IACrB,qBAAqB;AAAA,EACvB;AAAA,EACA,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,UAAU;AAAA,IACR,EAAE,UAAU,EAAE,YAAY,yBAAyB,EAAE;AAAA,IACrD,GAAG,aAAa,IAAI,CAAC,EAAE,QAAQ,OAAO,OAAO,OAAO;AAAA,MAClD,OAAO;AAAA,MACP,UAAU;AAAA,QACR,YAAY,OAAO,KAAK;AAAA,QACxB,GAAI,SAAS,EAAE,WAAW,SAAS,IAAI;AAAA,MACzC;AAAA,IACF,EAAE;AAAA,EACJ;AACF;","names":[]}
@@ -0,0 +1,107 @@
1
+ "use client";
2
+ import {
3
+ ViewerError,
4
+ isModuleNotFound,
5
+ parserMissingError,
6
+ toViewerError
7
+ } from "./chunk-JHFC2KBI.js";
8
+ import {
9
+ PROTOCOL_VERSION
10
+ } from "./chunk-CIT2HHRI.js";
11
+
12
+ // src/core/registry.ts
13
+ import { resolveFileKind } from "@elabs-ai/components-ui";
14
+ var MatchScore = {
15
+ None: 0,
16
+ Category: 1,
17
+ MediaTypePrefix: 2,
18
+ MediaType: 3,
19
+ Extension: 4
20
+ };
21
+ function scoreManifest(manifest, kind) {
22
+ if (kind.extension && manifest.extensions?.includes(kind.extension)) {
23
+ return MatchScore.Extension;
24
+ }
25
+ for (const declared of manifest.mediaTypes ?? []) {
26
+ if (declared.endsWith("/")) {
27
+ if (kind.mediaType.startsWith(declared)) return MatchScore.MediaTypePrefix;
28
+ } else if (declared === kind.mediaType) {
29
+ return MatchScore.MediaType;
30
+ }
31
+ }
32
+ if (manifest.categories?.includes(kind.category)) return MatchScore.Category;
33
+ return MatchScore.None;
34
+ }
35
+ function unwrap(loaded) {
36
+ return "default" in loaded && loaded.default ? loaded.default : loaded;
37
+ }
38
+ function assertProtocol(manifest, where) {
39
+ if (manifest.protocol !== PROTOCOL_VERSION) {
40
+ throw new ViewerError(
41
+ "protocol-mismatch",
42
+ `Adapter "${manifest.id}" targets viewer protocol ${String(manifest.protocol)}, but this build speaks ${String(PROTOCOL_VERSION)} (${where}).`
43
+ );
44
+ }
45
+ }
46
+ function createRegistry() {
47
+ const entries = /* @__PURE__ */ new Map();
48
+ const modules = /* @__PURE__ */ new Map();
49
+ function ordered() {
50
+ return [...entries.values()].sort(
51
+ (a, b) => (b.manifest.priority ?? 0) - (a.manifest.priority ?? 0)
52
+ );
53
+ }
54
+ function detect(kind) {
55
+ let best;
56
+ for (const { manifest } of entries.values()) {
57
+ const score = scoreManifest(manifest, kind);
58
+ if (score === MatchScore.None) continue;
59
+ const priority = manifest.priority ?? 0;
60
+ const wins = !best || priority > best.priority || priority === best.priority && score > best.score;
61
+ if (wins) best = { manifest, score, priority };
62
+ }
63
+ return best?.manifest;
64
+ }
65
+ return {
66
+ register(manifest, loader) {
67
+ assertProtocol(manifest, "registration");
68
+ entries.set(manifest.id, { manifest, loader });
69
+ modules.delete(manifest.id);
70
+ },
71
+ manifests() {
72
+ return ordered().map((entry) => entry.manifest);
73
+ },
74
+ detect,
75
+ detectByName(name, mediaType) {
76
+ return detect(resolveFileKind(name, mediaType));
77
+ },
78
+ async load(id) {
79
+ const entry = entries.get(id);
80
+ if (!entry) {
81
+ throw new ViewerError("unsupported-format", `No adapter is registered for "${id}".`);
82
+ }
83
+ let pending = modules.get(id);
84
+ if (!pending) {
85
+ pending = entry.loader().then((loaded) => {
86
+ const adapterModule = unwrap(loaded);
87
+ assertProtocol(adapterModule.manifest, `module "${id}"`);
88
+ return adapterModule;
89
+ }).catch((error) => {
90
+ modules.delete(id);
91
+ if (isModuleNotFound(error)) {
92
+ throw parserMissingError(id, entry.manifest.requires ?? [], { cause: error });
93
+ }
94
+ throw toViewerError(error, "parse-failed");
95
+ });
96
+ modules.set(id, pending);
97
+ }
98
+ return pending;
99
+ }
100
+ };
101
+ }
102
+
103
+ export {
104
+ scoreManifest,
105
+ createRegistry
106
+ };
107
+ //# sourceMappingURL=chunk-BWMXMHVZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/registry.ts"],"sourcesContent":["/**\n * The adapter registry — how a file finds its renderer.\n *\n * Architecture adapted from [anyview](https://github.com/harshpreet931/anyview)\n * (MIT). Four properties are what make it work, and all four are deliberate:\n *\n * 1. **Eager manifests, lazy loaders.** Routing and capability questions are\n * answered from plain data; the parser is fetched only when a file of that\n * kind is actually opened.\n * 2. **Priority override.** A consumer replaces a built-in by registering a\n * higher priority — no fork, no patch.\n * 3. **A fresh adapter per document.** The module (and its parser) is cached;\n * the instance is not, so per-document state cannot leak between files.\n * 4. **A protocol guard.** A mismatched adapter fails with a named error at\n * registration, not with an undefined property at render.\n */\n\nimport { type FileKind, resolveFileKind } from \"@elabs-ai/components-ui\";\n\nimport { ViewerError, isModuleNotFound, parserMissingError, toViewerError } from \"./errors\";\nimport {\n type AdapterLoader,\n type AdapterManifest,\n type AdapterModule,\n PROTOCOL_VERSION,\n} from \"./types\";\n\n/**\n * How specifically a manifest claimed a file. Higher is more specific.\n * A plain object rather than a `const enum` — the repo builds with\n * `isolatedModules`, which cannot inline one.\n */\nconst MatchScore = {\n None: 0,\n Category: 1,\n MediaTypePrefix: 2,\n MediaType: 3,\n Extension: 4,\n} as const;\n\ntype MatchScore = (typeof MatchScore)[keyof typeof MatchScore];\n\ninterface Entry {\n manifest: AdapterManifest;\n loader: AdapterLoader;\n}\n\n/** A registry of file adapters. Create with {@link createRegistry}. */\nexport interface ViewerRegistry {\n /**\n * Add an adapter, or replace one with the same `id`.\n *\n * Replacement is by id and unconditional — the caller asked for it. Priority\n * governs which of several DIFFERENT adapters wins a file, not whether a\n * registration takes effect.\n *\n * @throws {ViewerError} `protocol-mismatch` if the manifest targets another protocol.\n */\n register(manifest: AdapterManifest, loader: AdapterLoader): void;\n /**\n * Every registered manifest, highest priority first. Not the routing order —\n * specificity is per-file, so only {@link detect} can rank for a given file.\n */\n manifests(): AdapterManifest[];\n /** The manifest that should open this file, or `undefined` if none claims it. */\n detect(kind: FileKind): AdapterManifest | undefined;\n /** Convenience over {@link detect} for a name + MIME. */\n detectByName(name: string, mediaType?: string): AdapterManifest | undefined;\n /**\n * Fetch an adapter module by id, caching the MODULE (never an instance).\n *\n * @throws {ViewerError} `unsupported-format` when the id is unknown,\n * `parser-missing` when the module's optional peer is not installed,\n * `protocol-mismatch` when the loaded module disagrees with its manifest.\n */\n load(id: string): Promise<AdapterModule>;\n}\n\n/** How well `manifest` claims `kind`. `MatchScore.None` means \"not mine\". */\nexport function scoreManifest(manifest: AdapterManifest, kind: FileKind): MatchScore {\n if (kind.extension && manifest.extensions?.includes(kind.extension)) {\n return MatchScore.Extension;\n }\n for (const declared of manifest.mediaTypes ?? []) {\n if (declared.endsWith(\"/\")) {\n if (kind.mediaType.startsWith(declared)) return MatchScore.MediaTypePrefix;\n } else if (declared === kind.mediaType) {\n return MatchScore.MediaType;\n }\n }\n if (manifest.categories?.includes(kind.category)) return MatchScore.Category;\n return MatchScore.None;\n}\n\n/** Unwrap `export default` vs named exports, so an adapter can use either. */\nfunction unwrap(loaded: AdapterModule | { default: AdapterModule }): AdapterModule {\n return \"default\" in loaded && loaded.default ? loaded.default : (loaded as AdapterModule);\n}\n\nfunction assertProtocol(manifest: AdapterManifest, where: string): void {\n if (manifest.protocol !== PROTOCOL_VERSION) {\n throw new ViewerError(\n \"protocol-mismatch\",\n `Adapter \"${manifest.id}\" targets viewer protocol ${String(manifest.protocol)}, but this build speaks ${String(PROTOCOL_VERSION)} (${where}).`,\n );\n }\n}\n\n/**\n * Create an empty registry.\n *\n * Empty on purpose: the built-ins live in `createDefaultRegistry()`\n * (`src/adapters`), so a consumer who wants only their own adapters — or only\n * images — never pulls in the rest.\n */\nexport function createRegistry(): ViewerRegistry {\n const entries = new Map<string, Entry>();\n const modules = new Map<string, Promise<AdapterModule>>();\n\n function ordered(): Entry[] {\n return [...entries.values()].sort(\n (a, b) => (b.manifest.priority ?? 0) - (a.manifest.priority ?? 0),\n );\n }\n\n function detect(kind: FileKind): AdapterManifest | undefined {\n let best: { manifest: AdapterManifest; score: MatchScore; priority: number } | undefined;\n for (const { manifest } of entries.values()) {\n const score = scoreManifest(manifest, kind);\n if (score === MatchScore.None) continue;\n const priority = manifest.priority ?? 0;\n // Priority is the consumer's explicit override, so it outranks how\n // specifically an adapter happened to claim the file.\n const wins =\n !best || priority > best.priority || (priority === best.priority && score > best.score);\n if (wins) best = { manifest, score, priority };\n }\n return best?.manifest;\n }\n\n return {\n register(manifest, loader) {\n assertProtocol(manifest, \"registration\");\n entries.set(manifest.id, { manifest, loader });\n // A replaced id must not keep serving the old module.\n modules.delete(manifest.id);\n },\n\n manifests() {\n return ordered().map((entry) => entry.manifest);\n },\n\n detect,\n\n detectByName(name, mediaType) {\n return detect(resolveFileKind(name, mediaType));\n },\n\n async load(id) {\n const entry = entries.get(id);\n if (!entry) {\n throw new ViewerError(\"unsupported-format\", `No adapter is registered for \"${id}\".`);\n }\n\n let pending = modules.get(id);\n if (!pending) {\n pending = entry\n .loader()\n .then((loaded) => {\n const adapterModule = unwrap(loaded);\n assertProtocol(adapterModule.manifest, `module \"${id}\"`);\n return adapterModule;\n })\n .catch((error: unknown) => {\n // Not cached: an optional peer installed later, or a transient\n // chunk-load failure, must be retryable without a page reload.\n modules.delete(id);\n if (isModuleNotFound(error)) {\n throw parserMissingError(id, entry.manifest.requires ?? [], { cause: error });\n }\n throw toViewerError(error, \"parse-failed\");\n });\n modules.set(id, pending);\n }\n return pending;\n },\n };\n}\n"],"mappings":"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","names":[]}