@lamberl-lee/file-preview 0.2.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 (263) hide show
  1. package/COPYING +674 -0
  2. package/LICENSE +165 -0
  3. package/README.md +165 -0
  4. package/dist/AudioPreview.d.ts +9 -0
  5. package/dist/AudioPreview.js +29 -0
  6. package/dist/AudioPreview.js.map +1 -0
  7. package/dist/CodePreview.d.ts +10 -0
  8. package/dist/CodePreview.js +121 -0
  9. package/dist/CodePreview.js.map +1 -0
  10. package/dist/CsvPreview.d.ts +9 -0
  11. package/dist/CsvPreview.js +117 -0
  12. package/dist/CsvPreview.js.map +1 -0
  13. package/dist/DocxPreview.d.ts +11 -0
  14. package/dist/DocxPreview.js +89 -0
  15. package/dist/DocxPreview.js.map +1 -0
  16. package/dist/EpubPreview.d.ts +9 -0
  17. package/dist/EpubPreview.js +693 -0
  18. package/dist/EpubPreview.js.map +1 -0
  19. package/dist/HtmlPreview.d.ts +9 -0
  20. package/dist/HtmlPreview.js +60 -0
  21. package/dist/HtmlPreview.js.map +1 -0
  22. package/dist/ImagePreview.d.ts +9 -0
  23. package/dist/ImagePreview.js +44 -0
  24. package/dist/ImagePreview.js.map +1 -0
  25. package/dist/LargeFileGate.d.ts +12 -0
  26. package/dist/LargeFileGate.js +88 -0
  27. package/dist/LargeFileGate.js.map +1 -0
  28. package/dist/MarkdownPreview.d.ts +8 -0
  29. package/dist/MarkdownPreview.js +140 -0
  30. package/dist/MarkdownPreview.js.map +1 -0
  31. package/dist/PdfPreview.d.ts +11 -0
  32. package/dist/PdfPreview.js +206 -0
  33. package/dist/PdfPreview.js.map +1 -0
  34. package/dist/PlainTextLargePreview.d.ts +9 -0
  35. package/dist/PlainTextLargePreview.js +62 -0
  36. package/dist/PlainTextLargePreview.js.map +1 -0
  37. package/dist/PluginPreviewRenderer.d.ts +13 -0
  38. package/dist/PluginPreviewRenderer.js +89 -0
  39. package/dist/PluginPreviewRenderer.js.map +1 -0
  40. package/dist/PptxPreview.d.ts +16 -0
  41. package/dist/PptxPreview.js +376 -0
  42. package/dist/PptxPreview.js.map +1 -0
  43. package/dist/PreviewErrorBoundary.d.ts +29 -0
  44. package/dist/PreviewErrorBoundary.js +53 -0
  45. package/dist/PreviewErrorBoundary.js.map +1 -0
  46. package/dist/PreviewFallback.d.ts +18 -0
  47. package/dist/PreviewFallback.js +143 -0
  48. package/dist/PreviewFallback.js.map +1 -0
  49. package/dist/PreviewLoading.d.ts +8 -0
  50. package/dist/PreviewLoading.js +14 -0
  51. package/dist/PreviewLoading.js.map +1 -0
  52. package/dist/RtfPreview.d.ts +10 -0
  53. package/dist/RtfPreview.js +240 -0
  54. package/dist/RtfPreview.js.map +1 -0
  55. package/dist/ShikiSourceView.d.ts +11 -0
  56. package/dist/ShikiSourceView.js +112 -0
  57. package/dist/ShikiSourceView.js.map +1 -0
  58. package/dist/SvgPreview.d.ts +9 -0
  59. package/dist/SvgPreview.js +89 -0
  60. package/dist/SvgPreview.js.map +1 -0
  61. package/dist/TextPreview.d.ts +9 -0
  62. package/dist/TextPreview.js +9 -0
  63. package/dist/TextPreview.js.map +1 -0
  64. package/dist/VideoPreview.d.ts +9 -0
  65. package/dist/VideoPreview.js +18 -0
  66. package/dist/VideoPreview.js.map +1 -0
  67. package/dist/XlsxPreview.d.ts +12 -0
  68. package/dist/XlsxPreview.js +856 -0
  69. package/dist/XlsxPreview.js.map +1 -0
  70. package/dist/ZipPreview.d.ts +9 -0
  71. package/dist/ZipPreview.js +153 -0
  72. package/dist/ZipPreview.js.map +1 -0
  73. package/dist/core/binary.d.ts +10 -0
  74. package/dist/core/binary.js +27 -0
  75. package/dist/core/binary.js.map +1 -0
  76. package/dist/core/config.d.ts +17 -0
  77. package/dist/core/config.js +19 -0
  78. package/dist/core/config.js.map +1 -0
  79. package/dist/core/download.d.ts +5 -0
  80. package/dist/core/download.js +20 -0
  81. package/dist/core/download.js.map +1 -0
  82. package/dist/core/i18n.d.ts +101 -0
  83. package/dist/core/i18n.js +200 -0
  84. package/dist/core/i18n.js.map +1 -0
  85. package/dist/core/plugin.d.ts +38 -0
  86. package/dist/core/plugin.js +46 -0
  87. package/dist/core/plugin.js.map +1 -0
  88. package/dist/core/registry.d.ts +13 -0
  89. package/dist/core/registry.js +33 -0
  90. package/dist/core/registry.js.map +1 -0
  91. package/dist/core/source.d.ts +14 -0
  92. package/dist/core/source.js +131 -0
  93. package/dist/core/source.js.map +1 -0
  94. package/dist/core/types.d.ts +88 -0
  95. package/dist/core/types.js +27 -0
  96. package/dist/core/types.js.map +1 -0
  97. package/dist/hooks/useObjectUrlFromSource.d.ts +9 -0
  98. package/dist/hooks/useObjectUrlFromSource.js +50 -0
  99. package/dist/hooks/useObjectUrlFromSource.js.map +1 -0
  100. package/dist/hooks/useSourceBase64.d.ts +10 -0
  101. package/dist/hooks/useSourceBase64.js +32 -0
  102. package/dist/hooks/useSourceBase64.js.map +1 -0
  103. package/dist/hooks/useSourceText.d.ts +10 -0
  104. package/dist/hooks/useSourceText.js +32 -0
  105. package/dist/hooks/useSourceText.js.map +1 -0
  106. package/dist/icons.d.ts +44 -0
  107. package/dist/icons.js +248 -0
  108. package/dist/icons.js.map +1 -0
  109. package/dist/index.d.ts +36 -0
  110. package/dist/index.js +147 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/limits.d.ts +26 -0
  113. package/dist/limits.js +45 -0
  114. package/dist/limits.js.map +1 -0
  115. package/dist/performance-limits.d.ts +27 -0
  116. package/dist/performance-limits.js +54 -0
  117. package/dist/performance-limits.js.map +1 -0
  118. package/dist/plugins/audio-plugin.d.ts +7 -0
  119. package/dist/plugins/audio-plugin.js +11 -0
  120. package/dist/plugins/audio-plugin.js.map +1 -0
  121. package/dist/plugins/builtin-plugins.d.ts +9 -0
  122. package/dist/plugins/builtin-plugins.js +43 -0
  123. package/dist/plugins/builtin-plugins.js.map +1 -0
  124. package/dist/plugins/csv-plugin.d.ts +7 -0
  125. package/dist/plugins/csv-plugin.js +11 -0
  126. package/dist/plugins/csv-plugin.js.map +1 -0
  127. package/dist/plugins/docx-plugin.d.ts +7 -0
  128. package/dist/plugins/docx-plugin.js +15 -0
  129. package/dist/plugins/docx-plugin.js.map +1 -0
  130. package/dist/plugins/epub-plugin.d.ts +7 -0
  131. package/dist/plugins/epub-plugin.js +15 -0
  132. package/dist/plugins/epub-plugin.js.map +1 -0
  133. package/dist/plugins/html-plugin.d.ts +7 -0
  134. package/dist/plugins/html-plugin.js +11 -0
  135. package/dist/plugins/html-plugin.js.map +1 -0
  136. package/dist/plugins/image-plugin.d.ts +7 -0
  137. package/dist/plugins/image-plugin.js +11 -0
  138. package/dist/plugins/image-plugin.js.map +1 -0
  139. package/dist/plugins/markdown-plugin.d.ts +7 -0
  140. package/dist/plugins/markdown-plugin.js +11 -0
  141. package/dist/plugins/markdown-plugin.js.map +1 -0
  142. package/dist/plugins/pdf-plugin.d.ts +7 -0
  143. package/dist/plugins/pdf-plugin.js +15 -0
  144. package/dist/plugins/pdf-plugin.js.map +1 -0
  145. package/dist/plugins/pptx-plugin.d.ts +7 -0
  146. package/dist/plugins/pptx-plugin.js +15 -0
  147. package/dist/plugins/pptx-plugin.js.map +1 -0
  148. package/dist/plugins/rtf-plugin.d.ts +7 -0
  149. package/dist/plugins/rtf-plugin.js +15 -0
  150. package/dist/plugins/rtf-plugin.js.map +1 -0
  151. package/dist/plugins/source-code-plugin.d.ts +7 -0
  152. package/dist/plugins/source-code-plugin.js +11 -0
  153. package/dist/plugins/source-code-plugin.js.map +1 -0
  154. package/dist/plugins/svg-plugin.d.ts +7 -0
  155. package/dist/plugins/svg-plugin.js +11 -0
  156. package/dist/plugins/svg-plugin.js.map +1 -0
  157. package/dist/plugins/text-plugin.d.ts +7 -0
  158. package/dist/plugins/text-plugin.js +11 -0
  159. package/dist/plugins/text-plugin.js.map +1 -0
  160. package/dist/plugins/video-plugin.d.ts +7 -0
  161. package/dist/plugins/video-plugin.js +11 -0
  162. package/dist/plugins/video-plugin.js.map +1 -0
  163. package/dist/plugins/xlsx-plugin.d.ts +7 -0
  164. package/dist/plugins/xlsx-plugin.js +15 -0
  165. package/dist/plugins/xlsx-plugin.js.map +1 -0
  166. package/dist/plugins/zip-plugin.d.ts +7 -0
  167. package/dist/plugins/zip-plugin.js +15 -0
  168. package/dist/plugins/zip-plugin.js.map +1 -0
  169. package/dist/preview-adapters/AudioPreviewAdapter.d.ts +8 -0
  170. package/dist/preview-adapters/AudioPreviewAdapter.js +31 -0
  171. package/dist/preview-adapters/AudioPreviewAdapter.js.map +1 -0
  172. package/dist/preview-adapters/CsvPreviewAdapter.d.ts +8 -0
  173. package/dist/preview-adapters/CsvPreviewAdapter.js +28 -0
  174. package/dist/preview-adapters/CsvPreviewAdapter.js.map +1 -0
  175. package/dist/preview-adapters/DocxPreviewAdapter.d.ts +8 -0
  176. package/dist/preview-adapters/DocxPreviewAdapter.js +16 -0
  177. package/dist/preview-adapters/DocxPreviewAdapter.js.map +1 -0
  178. package/dist/preview-adapters/EpubPreviewAdapter.d.ts +8 -0
  179. package/dist/preview-adapters/EpubPreviewAdapter.js +28 -0
  180. package/dist/preview-adapters/EpubPreviewAdapter.js.map +1 -0
  181. package/dist/preview-adapters/HtmlPreviewAdapter.d.ts +8 -0
  182. package/dist/preview-adapters/HtmlPreviewAdapter.js +28 -0
  183. package/dist/preview-adapters/HtmlPreviewAdapter.js.map +1 -0
  184. package/dist/preview-adapters/ImagePreviewAdapter.d.ts +8 -0
  185. package/dist/preview-adapters/ImagePreviewAdapter.js +31 -0
  186. package/dist/preview-adapters/ImagePreviewAdapter.js.map +1 -0
  187. package/dist/preview-adapters/MarkdownPreviewAdapter.d.ts +8 -0
  188. package/dist/preview-adapters/MarkdownPreviewAdapter.js +28 -0
  189. package/dist/preview-adapters/MarkdownPreviewAdapter.js.map +1 -0
  190. package/dist/preview-adapters/PdfPreviewAdapter.d.ts +8 -0
  191. package/dist/preview-adapters/PdfPreviewAdapter.js +16 -0
  192. package/dist/preview-adapters/PdfPreviewAdapter.js.map +1 -0
  193. package/dist/preview-adapters/PptxPreviewAdapter.d.ts +8 -0
  194. package/dist/preview-adapters/PptxPreviewAdapter.js +16 -0
  195. package/dist/preview-adapters/PptxPreviewAdapter.js.map +1 -0
  196. package/dist/preview-adapters/RtfPreviewAdapter.d.ts +8 -0
  197. package/dist/preview-adapters/RtfPreviewAdapter.js +54 -0
  198. package/dist/preview-adapters/RtfPreviewAdapter.js.map +1 -0
  199. package/dist/preview-adapters/SourceCodePreviewAdapter.d.ts +8 -0
  200. package/dist/preview-adapters/SourceCodePreviewAdapter.js +35 -0
  201. package/dist/preview-adapters/SourceCodePreviewAdapter.js.map +1 -0
  202. package/dist/preview-adapters/SvgPreviewAdapter.d.ts +8 -0
  203. package/dist/preview-adapters/SvgPreviewAdapter.js +28 -0
  204. package/dist/preview-adapters/SvgPreviewAdapter.js.map +1 -0
  205. package/dist/preview-adapters/TextPreviewAdapter.d.ts +8 -0
  206. package/dist/preview-adapters/TextPreviewAdapter.js +28 -0
  207. package/dist/preview-adapters/TextPreviewAdapter.js.map +1 -0
  208. package/dist/preview-adapters/UnsupportedPluginPreview.d.ts +11 -0
  209. package/dist/preview-adapters/UnsupportedPluginPreview.js +34 -0
  210. package/dist/preview-adapters/UnsupportedPluginPreview.js.map +1 -0
  211. package/dist/preview-adapters/VideoPreviewAdapter.d.ts +8 -0
  212. package/dist/preview-adapters/VideoPreviewAdapter.js +31 -0
  213. package/dist/preview-adapters/VideoPreviewAdapter.js.map +1 -0
  214. package/dist/preview-adapters/XlsxPreviewAdapter.d.ts +8 -0
  215. package/dist/preview-adapters/XlsxPreviewAdapter.js +17 -0
  216. package/dist/preview-adapters/XlsxPreviewAdapter.js.map +1 -0
  217. package/dist/preview-adapters/ZipPreviewAdapter.d.ts +8 -0
  218. package/dist/preview-adapters/ZipPreviewAdapter.js +28 -0
  219. package/dist/preview-adapters/ZipPreviewAdapter.js.map +1 -0
  220. package/dist/remote-url.d.ts +20 -0
  221. package/dist/remote-url.js +478 -0
  222. package/dist/remote-url.js.map +1 -0
  223. package/dist/rtf/load-rtfjs.d.ts +42 -0
  224. package/dist/rtf/load-rtfjs.js +71 -0
  225. package/dist/rtf/load-rtfjs.js.map +1 -0
  226. package/dist/rtf/normalize-codepage.d.ts +33 -0
  227. package/dist/rtf/normalize-codepage.js +88 -0
  228. package/dist/rtf/normalize-codepage.js.map +1 -0
  229. package/dist/shiki.d.ts +35 -0
  230. package/dist/shiki.js +128 -0
  231. package/dist/shiki.js.map +1 -0
  232. package/dist/styles/AudioPreview.css +35 -0
  233. package/dist/styles/CsvPreview.css +106 -0
  234. package/dist/styles/DocxPreview.css +93 -0
  235. package/dist/styles/EpubPreview.css +509 -0
  236. package/dist/styles/HtmlPreview.css +15 -0
  237. package/dist/styles/ImagePreview.css +45 -0
  238. package/dist/styles/LargeFileGate.css +55 -0
  239. package/dist/styles/MarkdownPreview.css +291 -0
  240. package/dist/styles/PdfPreview.css +68 -0
  241. package/dist/styles/PlainTextLargePreview.css +85 -0
  242. package/dist/styles/PluginDebugBar.css +30 -0
  243. package/dist/styles/PptxPreview.css +207 -0
  244. package/dist/styles/PreviewFallback.css +88 -0
  245. package/dist/styles/PreviewLoading.css +13 -0
  246. package/dist/styles/RtfPreview.css +99 -0
  247. package/dist/styles/ShikiSourceView.css +159 -0
  248. package/dist/styles/SvgPreview.css +99 -0
  249. package/dist/styles/VideoPreview.css +19 -0
  250. package/dist/styles/ViewModeBar.css +38 -0
  251. package/dist/styles/XlsxPreview.css +361 -0
  252. package/dist/styles/ZipPreview.css +86 -0
  253. package/dist/styles/base.css +238 -0
  254. package/dist/styles/index.css +39 -0
  255. package/dist/support-status.d.ts +19 -0
  256. package/dist/support-status.js +174 -0
  257. package/dist/support-status.js.map +1 -0
  258. package/dist/utils.d.ts +17 -0
  259. package/dist/utils.js +205 -0
  260. package/dist/utils.js.map +1 -0
  261. package/package.json +125 -0
  262. package/scripts/copy-pdf-worker.mjs +31 -0
  263. package/scripts/copy-rtfjs-bundles.mjs +49 -0
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Codepage normalization for RTF input.
3
+ *
4
+ * rtf.js defaults to Windows-1252 when a document declares only `\rtf1\ansi`
5
+ * without `\ansicpgN`. Files that look like
6
+ *
7
+ * {\rtf1\ansi\deff0
8
+ * {\fonttbl{\f0\fcharset134 SimSun;}}
9
+ * \f0 \'d6\'d0\'b9\'fa\par}
10
+ *
11
+ * (very common in legacy WPS / Mac TextEdit exports) then decode as
12
+ * mojibake even though the font table tells us the document is GBK-encoded.
13
+ *
14
+ * Fix: sniff the font table for the first non-default `\fcharsetN`, map it
15
+ * to a Windows codepage, and inject `\ansicpgN` after `\ansi`. We never
16
+ * overwrite an existing `\ansicpg` — the document author knows best.
17
+ *
18
+ * RTF is 7-bit-clean (non-ASCII bytes always get escaped as `\'XX` or `\uN`),
19
+ * so we operate on the bytes as Latin-1 without changing semantics.
20
+ */
21
+ /**
22
+ * Public entry point: take RTF bytes, return either the same bytes or a
23
+ * patched copy with `\ansicpgN` injected based on font-table sniffing.
24
+ *
25
+ * Also returns the codepage that was applied (or `null`) — callers can
26
+ * use this for diagnostic logging.
27
+ */
28
+ declare function normalizeRtfCodepage(buffer: ArrayBuffer): {
29
+ bytes: Uint8Array;
30
+ injectedCodepage: number | null;
31
+ };
32
+
33
+ export { normalizeRtfCodepage };
@@ -0,0 +1,88 @@
1
+ const FCHARSET_TO_CODEPAGE = {
2
+ 0: 1252,
3
+ // ANSI (default Latin-1) — never inject this
4
+ // 2: 42 Symbol — pseudo
5
+ 77: 1e4,
6
+ // Mac Roman
7
+ 78: 10001,
8
+ // Mac Japanese
9
+ 79: 10003,
10
+ // Mac Korean
11
+ 80: 10008,
12
+ // Mac Simplified Chinese
13
+ 81: 10002,
14
+ // Mac Traditional Chinese
15
+ 128: 932,
16
+ // Shift-JIS (Japanese)
17
+ 129: 949,
18
+ // EUC-KR (Korean)
19
+ 130: 1361,
20
+ // Johab (Korean)
21
+ 134: 936,
22
+ // GBK (Simplified Chinese)
23
+ 136: 950,
24
+ // Big5 (Traditional Chinese)
25
+ 161: 1253,
26
+ // Greek
27
+ 162: 1254,
28
+ // Turkish
29
+ 163: 1258,
30
+ // Vietnamese
31
+ 177: 1255,
32
+ // Hebrew
33
+ 178: 1256,
34
+ // Arabic
35
+ 186: 1257,
36
+ // Baltic
37
+ 204: 1251,
38
+ // Cyrillic
39
+ 222: 874,
40
+ // Thai
41
+ 238: 1250
42
+ // Eastern European
43
+ };
44
+ function detectCharsetFromFontTable(rtfText) {
45
+ const header = rtfText.slice(0, 8192);
46
+ const re = /\\fcharset(\d+)/g;
47
+ let m;
48
+ while ((m = re.exec(header)) !== null) {
49
+ const charset = parseInt(m[1], 10);
50
+ const cp = FCHARSET_TO_CODEPAGE[charset];
51
+ if (cp && cp !== 1252) return cp;
52
+ }
53
+ return null;
54
+ }
55
+ function injectAnsiCpg(bytes, codepage) {
56
+ const text = new TextDecoder("latin1").decode(bytes);
57
+ if (/\\ansicpg\d/.test(text)) return bytes;
58
+ const patched = text.replace(
59
+ /\\ansi(?=[^a-zA-Z0-9])/,
60
+ `\\ansi\\ansicpg${codepage}`
61
+ );
62
+ if (patched === text) return bytes;
63
+ const out = new Uint8Array(patched.length);
64
+ for (let i = 0; i < patched.length; i++) {
65
+ out[i] = patched.charCodeAt(i) & 255;
66
+ }
67
+ return out;
68
+ }
69
+ function normalizeRtfCodepage(buffer) {
70
+ const bytes = new Uint8Array(buffer);
71
+ const head = new TextDecoder("latin1").decode(bytes.slice(0, 256));
72
+ if (!head.trimStart().startsWith("{\\rtf")) {
73
+ return { bytes, injectedCodepage: null };
74
+ }
75
+ if (/\\ansicpg\d/.test(head)) {
76
+ return { bytes, injectedCodepage: null };
77
+ }
78
+ const text = new TextDecoder("latin1").decode(bytes);
79
+ const cp = detectCharsetFromFontTable(text);
80
+ if (cp === null) {
81
+ return { bytes, injectedCodepage: null };
82
+ }
83
+ return { bytes: injectAnsiCpg(bytes, cp), injectedCodepage: cp };
84
+ }
85
+ export {
86
+ normalizeRtfCodepage
87
+ };
88
+ //# sourceMappingURL=normalize-codepage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/rtf/normalize-codepage.ts"],"sourcesContent":["/**\n * Codepage normalization for RTF input.\n *\n * rtf.js defaults to Windows-1252 when a document declares only `\\rtf1\\ansi`\n * without `\\ansicpgN`. Files that look like\n *\n * {\\rtf1\\ansi\\deff0\n * {\\fonttbl{\\f0\\fcharset134 SimSun;}}\n * \\f0 \\'d6\\'d0\\'b9\\'fa\\par}\n *\n * (very common in legacy WPS / Mac TextEdit exports) then decode as\n * mojibake even though the font table tells us the document is GBK-encoded.\n *\n * Fix: sniff the font table for the first non-default `\\fcharsetN`, map it\n * to a Windows codepage, and inject `\\ansicpgN` after `\\ansi`. We never\n * overwrite an existing `\\ansicpg` — the document author knows best.\n *\n * RTF is 7-bit-clean (non-ASCII bytes always get escaped as `\\'XX` or `\\uN`),\n * so we operate on the bytes as Latin-1 without changing semantics.\n */\n\n/**\n * Map RTF `\\fcharsetN` values to Windows codepages.\n * Mirrors the `_charsetMap` in rtf.js's Helper.ts.\n */\nconst FCHARSET_TO_CODEPAGE: Record<number, number> = {\n 0: 1252, // ANSI (default Latin-1) — never inject this\n // 2: 42 Symbol — pseudo\n 77: 10000, // Mac Roman\n 78: 10001, // Mac Japanese\n 79: 10003, // Mac Korean\n 80: 10008, // Mac Simplified Chinese\n 81: 10002, // Mac Traditional Chinese\n 128: 932, // Shift-JIS (Japanese)\n 129: 949, // EUC-KR (Korean)\n 130: 1361, // Johab (Korean)\n 134: 936, // GBK (Simplified Chinese)\n 136: 950, // Big5 (Traditional Chinese)\n 161: 1253, // Greek\n 162: 1254, // Turkish\n 163: 1258, // Vietnamese\n 177: 1255, // Hebrew\n 178: 1256, // Arabic\n 186: 1257, // Baltic\n 204: 1251, // Cyrillic\n 222: 874, // Thai\n 238: 1250, // Eastern European\n};\n\n/**\n * Look at the bytes (as Latin-1 string), find a `\\fcharsetN` for which\n * `_charsetMap[N]` is a non-1252 codepage, and return that codepage.\n *\n * Returns `null` if no useful charset is declared — caller leaves the\n * stream unchanged.\n */\nfunction detectCharsetFromFontTable(rtfText: string): number | null {\n // Limit search to the document header area (first ~8 KB). The font table\n // is always declared near the top, and scanning the entire document\n // would risk matching `\\fcharsetN` inside style/list overrides which\n // are unrelated to the document's primary encoding.\n const header = rtfText.slice(0, 8192);\n\n // Find every \\fcharsetN occurrence in the header. We pick the FIRST\n // one with a CJK / non-Latin mapping — that's the document's primary\n // text font, the one rtf.js will use for unscoped text.\n const re = /\\\\fcharset(\\d+)/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(header)) !== null) {\n const charset = parseInt(m[1], 10);\n const cp = FCHARSET_TO_CODEPAGE[charset];\n if (cp && cp !== 1252) return cp;\n }\n return null;\n}\n\n/**\n * Inject `\\ansicpgN` after `\\ansi` in the byte stream. Returns the\n * modified Uint8Array, or `bytes` unchanged if no injection happened.\n */\nfunction injectAnsiCpg(bytes: Uint8Array, codepage: number): Uint8Array {\n // Decode as Latin-1 — every byte maps 1:1 to a JS char in 0x00-0xff,\n // so bytewise edits to ASCII regions stay byte-identical.\n const text = new TextDecoder(\"latin1\").decode(bytes);\n\n // Already has \\ansicpg? Don't second-guess.\n if (/\\\\ansicpg\\d/.test(text)) return bytes;\n\n // Insert right after `\\ansi` (not `\\ansiN` — `\\ansicpg` is what we want\n // to avoid colliding with). Only one replacement.\n const patched = text.replace(\n /\\\\ansi(?=[^a-zA-Z0-9])/,\n `\\\\ansi\\\\ansicpg${codepage}`,\n );\n if (patched === text) return bytes; // \\ansi not found — leave alone\n\n // Re-encode. TextEncoder produces UTF-8; for ASCII-only modifications\n // we want byte-identical output, so write a Latin-1 array manually.\n const out = new Uint8Array(patched.length);\n for (let i = 0; i < patched.length; i++) {\n out[i] = patched.charCodeAt(i) & 0xff;\n }\n return out;\n}\n\n/**\n * Public entry point: take RTF bytes, return either the same bytes or a\n * patched copy with `\\ansicpgN` injected based on font-table sniffing.\n *\n * Also returns the codepage that was applied (or `null`) — callers can\n * use this for diagnostic logging.\n */\nexport function normalizeRtfCodepage(buffer: ArrayBuffer): {\n bytes: Uint8Array;\n injectedCodepage: number | null;\n} {\n const bytes = new Uint8Array(buffer);\n\n // Cheap header peek — if we can't even decode the first bytes as RTF,\n // skip the work and let rtf.js produce its own error.\n const head = new TextDecoder(\"latin1\").decode(bytes.slice(0, 256));\n if (!head.trimStart().startsWith(\"{\\\\rtf\")) {\n return { bytes, injectedCodepage: null };\n }\n\n // Already declares its codepage? Trust it.\n if (/\\\\ansicpg\\d/.test(head)) {\n return { bytes, injectedCodepage: null };\n }\n\n const text = new TextDecoder(\"latin1\").decode(bytes);\n const cp = detectCharsetFromFontTable(text);\n if (cp === null) {\n return { bytes, injectedCodepage: null };\n }\n\n return { bytes: injectAnsiCpg(bytes, cp), injectedCodepage: cp };\n}\n"],"mappings":"AAyBA,MAAM,uBAA+C;AAAA,EACnD,GAAG;AAAA;AAAA;AAAA,EAEH,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AAAA,EACJ,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AACP;AASA,SAAS,2BAA2B,SAAgC;AAKlE,QAAM,SAAS,QAAQ,MAAM,GAAG,IAAI;AAKpC,QAAM,KAAK;AACX,MAAI;AACJ,UAAQ,IAAI,GAAG,KAAK,MAAM,OAAO,MAAM;AACrC,UAAM,UAAU,SAAS,EAAE,CAAC,GAAG,EAAE;AACjC,UAAM,KAAK,qBAAqB,OAAO;AACvC,QAAI,MAAM,OAAO,KAAM,QAAO;AAAA,EAChC;AACA,SAAO;AACT;AAMA,SAAS,cAAc,OAAmB,UAA8B;AAGtE,QAAM,OAAO,IAAI,YAAY,QAAQ,EAAE,OAAO,KAAK;AAGnD,MAAI,cAAc,KAAK,IAAI,EAAG,QAAO;AAIrC,QAAM,UAAU,KAAK;AAAA,IACnB;AAAA,IACA,kBAAkB,QAAQ;AAAA,EAC5B;AACA,MAAI,YAAY,KAAM,QAAO;AAI7B,QAAM,MAAM,IAAI,WAAW,QAAQ,MAAM;AACzC,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,QAAI,CAAC,IAAI,QAAQ,WAAW,CAAC,IAAI;AAAA,EACnC;AACA,SAAO;AACT;AASO,SAAS,qBAAqB,QAGnC;AACA,QAAM,QAAQ,IAAI,WAAW,MAAM;AAInC,QAAM,OAAO,IAAI,YAAY,QAAQ,EAAE,OAAO,MAAM,MAAM,GAAG,GAAG,CAAC;AACjE,MAAI,CAAC,KAAK,UAAU,EAAE,WAAW,QAAQ,GAAG;AAC1C,WAAO,EAAE,OAAO,kBAAkB,KAAK;AAAA,EACzC;AAGA,MAAI,cAAc,KAAK,IAAI,GAAG;AAC5B,WAAO,EAAE,OAAO,kBAAkB,KAAK;AAAA,EACzC;AAEA,QAAM,OAAO,IAAI,YAAY,QAAQ,EAAE,OAAO,KAAK;AACnD,QAAM,KAAK,2BAA2B,IAAI;AAC1C,MAAI,OAAO,MAAM;AACf,WAAO,EAAE,OAAO,kBAAkB,KAAK;AAAA,EACzC;AAEA,SAAO,EAAE,OAAO,cAAc,OAAO,EAAE,GAAG,kBAAkB,GAAG;AACjE;","names":[]}
@@ -0,0 +1,35 @@
1
+ import { ShikiTransformer } from 'shiki';
2
+ export { codeToHtml as shikiCodeToHtml } from 'shiki';
3
+
4
+ /**
5
+ * Shiki highlighter — lazy loading via main entry
6
+ *
7
+ * The `shiki` main entry automatically code-splits each language and theme
8
+ * into separate async chunks. Only the ones actually used are loaded.
9
+ *
10
+ * Loading sequence for first code preview:
11
+ * 0KB (React.lazy) → core + JS engine + 1 lang + 2 themes ≈ 49KB gzip
12
+ *
13
+ * Turbopack/webpack automatically splits each @shikijs/langs/* and
14
+ * @shikijs/themes/* into separate chunks.
15
+ */
16
+
17
+ /**
18
+ * Highlight code with dual theme support (light/dark CSS variables)
19
+ *
20
+ * This is a wrapper around shiki's codeToHtml that:
21
+ * - Uses github-light/github-dark dual themes
22
+ * - Outputs CSS variables (defaultColor: false) for zero-cost theme switching
23
+ * - Adds line numbers via transformer
24
+ */
25
+ declare function highlightCode(content: string, language: string): Promise<string>;
26
+ /**
27
+ * Map file extension → Shiki language ID
28
+ */
29
+ declare function getShikiLanguage(fileName: string): string;
30
+ /**
31
+ * Transformer: add data-line attribute to each line for CSS line numbers
32
+ */
33
+ declare function transformerLineNumbers(): ShikiTransformer;
34
+
35
+ export { getShikiLanguage, highlightCode, transformerLineNumbers };
package/dist/shiki.js ADDED
@@ -0,0 +1,128 @@
1
+ import { codeToHtml as shikiCodeToHtml } from "shiki";
2
+ async function highlightCode(content, language) {
3
+ return shikiCodeToHtml(content, {
4
+ lang: language,
5
+ themes: {
6
+ light: "github-light",
7
+ dark: "github-dark"
8
+ },
9
+ defaultColor: false,
10
+ transformers: [transformerLineNumbers()]
11
+ });
12
+ }
13
+ function getShikiLanguage(fileName) {
14
+ const ext = fileName.toLowerCase().split(".").pop() || "";
15
+ const baseName = fileName.split("/").pop() || "";
16
+ const extMap = {
17
+ // Web
18
+ js: "javascript",
19
+ mjs: "javascript",
20
+ cjs: "javascript",
21
+ jsx: "jsx",
22
+ ts: "typescript",
23
+ tsx: "tsx",
24
+ html: "html",
25
+ htm: "html",
26
+ css: "css",
27
+ scss: "scss",
28
+ less: "less",
29
+ vue: "vue",
30
+ svelte: "svelte",
31
+ // Scripting
32
+ py: "python",
33
+ pyw: "python",
34
+ rb: "ruby",
35
+ php: "php",
36
+ pl: "perl",
37
+ pm: "perl",
38
+ lua: "lua",
39
+ r: "r",
40
+ // Systems
41
+ java: "java",
42
+ c: "c",
43
+ h: "c",
44
+ cpp: "cpp",
45
+ cc: "cpp",
46
+ cxx: "cpp",
47
+ hpp: "cpp",
48
+ cs: "csharp",
49
+ go: "go",
50
+ rs: "rust",
51
+ swift: "swift",
52
+ kt: "kotlin",
53
+ kts: "kotlin",
54
+ scala: "scala",
55
+ dart: "dart",
56
+ // Shell
57
+ sh: "bash",
58
+ bash: "bash",
59
+ zsh: "bash",
60
+ ps1: "powershell",
61
+ bat: "bat",
62
+ cmd: "bat",
63
+ // Data / Config
64
+ json: "json",
65
+ yml: "yaml",
66
+ yaml: "yaml",
67
+ toml: "toml",
68
+ ini: "ini",
69
+ cfg: "ini",
70
+ conf: "ini",
71
+ env: "ini",
72
+ sql: "sql",
73
+ graphql: "graphql",
74
+ gql: "graphql",
75
+ xml: "xml",
76
+ svg: "xml",
77
+ // DevOps
78
+ dockerfile: "dockerfile",
79
+ makefile: "makefile",
80
+ nginx: "nginx",
81
+ diff: "diff",
82
+ patch: "diff",
83
+ // Docs
84
+ md: "markdown",
85
+ mdx: "mdx",
86
+ tex: "latex",
87
+ adoc: "asciidoc",
88
+ // Functional / Other
89
+ ex: "elixir",
90
+ exs: "elixir",
91
+ clj: "clojure",
92
+ cljs: "clojure",
93
+ erl: "erlang",
94
+ hs: "haskell",
95
+ m: "matlab",
96
+ vim: "vim",
97
+ coffee: "coffeescript",
98
+ wasm: "wasm",
99
+ objectivec: "objectivec",
100
+ objectivecpp: "objectivec"
101
+ };
102
+ if (extMap[ext]) return extMap[ext];
103
+ const lowerBase = baseName.toLowerCase();
104
+ if (lowerBase === "dockerfile") return "dockerfile";
105
+ if (lowerBase === "makefile" || lowerBase === "gnumakefile") return "makefile";
106
+ if (lowerBase === "gemfile") return "ruby";
107
+ if (lowerBase === "rakefile") return "ruby";
108
+ if (lowerBase === ".gitignore" || lowerBase === ".env") return "ini";
109
+ if (lowerBase === ".eslintrc" || lowerBase === ".prettierrc") return "json";
110
+ if (lowerBase === "vagrantfile") return "ruby";
111
+ return "text";
112
+ }
113
+ function transformerLineNumbers() {
114
+ return {
115
+ name: "line-numbers",
116
+ line(lineProps, line) {
117
+ if (!lineProps.properties) lineProps.properties = {};
118
+ lineProps.properties["data-line"] = String(line);
119
+ }
120
+ };
121
+ }
122
+ export {
123
+ getShikiLanguage,
124
+ highlightCode,
125
+ shikiCodeToHtml,
126
+ transformerLineNumbers
127
+ };
128
+ //# sourceMappingURL=shiki.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shiki.ts"],"sourcesContent":["/**\n * Shiki highlighter — lazy loading via main entry\n *\n * The `shiki` main entry automatically code-splits each language and theme\n * into separate async chunks. Only the ones actually used are loaded.\n *\n * Loading sequence for first code preview:\n * 0KB (React.lazy) → core + JS engine + 1 lang + 2 themes ≈ 49KB gzip\n *\n * Turbopack/webpack automatically splits each @shikijs/langs/* and\n * @shikijs/themes/* into separate chunks.\n */\n\nimport { codeToHtml as shikiCodeToHtml } from \"shiki\";\nimport type { ShikiTransformer } from \"shiki\";\n\n// Re-export for convenience\nexport { shikiCodeToHtml };\n\n/**\n * Highlight code with dual theme support (light/dark CSS variables)\n *\n * This is a wrapper around shiki's codeToHtml that:\n * - Uses github-light/github-dark dual themes\n * - Outputs CSS variables (defaultColor: false) for zero-cost theme switching\n * - Adds line numbers via transformer\n */\nexport async function highlightCode(\n content: string,\n language: string\n): Promise<string> {\n return shikiCodeToHtml(content, {\n lang: language,\n themes: {\n light: \"github-light\",\n dark: \"github-dark\",\n },\n defaultColor: false,\n transformers: [transformerLineNumbers()],\n });\n}\n\n/**\n * Map file extension → Shiki language ID\n */\nexport function getShikiLanguage(fileName: string): string {\n const ext = fileName.toLowerCase().split(\".\").pop() || \"\";\n const baseName = fileName.split(\"/\").pop() || \"\";\n\n const extMap: Record<string, string> = {\n // Web\n js: \"javascript\",\n mjs: \"javascript\",\n cjs: \"javascript\",\n jsx: \"jsx\",\n ts: \"typescript\",\n tsx: \"tsx\",\n html: \"html\",\n htm: \"html\",\n css: \"css\",\n scss: \"scss\",\n less: \"less\",\n vue: \"vue\",\n svelte: \"svelte\",\n // Scripting\n py: \"python\",\n pyw: \"python\",\n rb: \"ruby\",\n php: \"php\",\n pl: \"perl\",\n pm: \"perl\",\n lua: \"lua\",\n r: \"r\",\n // Systems\n java: \"java\",\n c: \"c\",\n h: \"c\",\n cpp: \"cpp\",\n cc: \"cpp\",\n cxx: \"cpp\",\n hpp: \"cpp\",\n cs: \"csharp\",\n go: \"go\",\n rs: \"rust\",\n swift: \"swift\",\n kt: \"kotlin\",\n kts: \"kotlin\",\n scala: \"scala\",\n dart: \"dart\",\n // Shell\n sh: \"bash\",\n bash: \"bash\",\n zsh: \"bash\",\n ps1: \"powershell\",\n bat: \"bat\",\n cmd: \"bat\",\n // Data / Config\n json: \"json\",\n yml: \"yaml\",\n yaml: \"yaml\",\n toml: \"toml\",\n ini: \"ini\",\n cfg: \"ini\",\n conf: \"ini\",\n env: \"ini\",\n sql: \"sql\",\n graphql: \"graphql\",\n gql: \"graphql\",\n xml: \"xml\",\n svg: \"xml\",\n // DevOps\n dockerfile: \"dockerfile\",\n makefile: \"makefile\",\n nginx: \"nginx\",\n diff: \"diff\",\n patch: \"diff\",\n // Docs\n md: \"markdown\",\n mdx: \"mdx\",\n tex: \"latex\",\n adoc: \"asciidoc\",\n // Functional / Other\n ex: \"elixir\",\n exs: \"elixir\",\n clj: \"clojure\",\n cljs: \"clojure\",\n erl: \"erlang\",\n hs: \"haskell\",\n m: \"matlab\",\n vim: \"vim\",\n coffee: \"coffeescript\",\n wasm: \"wasm\",\n objectivec: \"objectivec\",\n objectivecpp: \"objectivec\",\n };\n\n if (extMap[ext]) return extMap[ext];\n\n // Check base filename (case-insensitive)\n const lowerBase = baseName.toLowerCase();\n if (lowerBase === \"dockerfile\") return \"dockerfile\";\n if (lowerBase === \"makefile\" || lowerBase === \"gnumakefile\") return \"makefile\";\n if (lowerBase === \"gemfile\") return \"ruby\";\n if (lowerBase === \"rakefile\") return \"ruby\";\n if (lowerBase === \".gitignore\" || lowerBase === \".env\") return \"ini\";\n if (lowerBase === \".eslintrc\" || lowerBase === \".prettierrc\") return \"json\";\n if (lowerBase === \"vagrantfile\") return \"ruby\";\n\n return \"text\";\n}\n\n/**\n * Transformer: add data-line attribute to each line for CSS line numbers\n */\nexport function transformerLineNumbers(): ShikiTransformer {\n return {\n name: \"line-numbers\",\n line(lineProps, line) {\n // lineProps is a hast element; properties are in .properties\n if (!lineProps.properties) lineProps.properties = {};\n lineProps.properties[\"data-line\"] = String(line);\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,35 @@
1
+ .fv-audio {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: center;
5
+ justify-content: center;
6
+ height: 100%;
7
+ min-height: 300px;
8
+ gap: 1.5rem;
9
+ padding: 1.5rem;
10
+ }
11
+ .fv-audio__icon-wrap {
12
+ width: 6rem;
13
+ height: 6rem;
14
+ border-radius: 9999px;
15
+ background: linear-gradient(135deg, color-mix(in srgb, #8b5cf6 20%, transparent), color-mix(in srgb, #ec4899 20%, transparent));
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ color: #8b5cf6;
20
+ }
21
+ .fv-audio__info {
22
+ text-align: center;
23
+ }
24
+ .fv-audio__name {
25
+ font-weight: 500;
26
+ }
27
+ .fv-audio__label {
28
+ font-size: var(--fv-font-size-sm);
29
+ color: var(--fv-muted-foreground);
30
+ margin-top: 0.25rem;
31
+ }
32
+ .fv-audio__player {
33
+ width: 100%;
34
+ max-width: 28rem;
35
+ }
@@ -0,0 +1,106 @@
1
+ .fv-csv {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100%;
5
+ }
6
+ .fv-csv__toolbar {
7
+ display: flex;
8
+ align-items: center;
9
+ gap: 0.75rem;
10
+ padding: 0.75rem;
11
+ border-bottom: 1px solid var(--fv-border);
12
+ background: color-mix(in srgb, var(--fv-muted) 30%, transparent);
13
+ }
14
+ .fv-csv__search-wrap {
15
+ position: relative;
16
+ flex: 1;
17
+ max-width: 20rem;
18
+ }
19
+ .fv-csv__search-icon {
20
+ position: absolute;
21
+ left: 0.625rem;
22
+ top: 50%;
23
+ transform: translateY(-50%);
24
+ color: var(--fv-muted-foreground);
25
+ pointer-events: none;
26
+ }
27
+ .fv-csv__search-input {
28
+ width: 100%;
29
+ padding: 0.375rem 0.75rem 0.375rem 2rem;
30
+ font-size: var(--fv-font-size-sm);
31
+ background: var(--fv-primary-foreground, #fff);
32
+ border: 1px solid var(--fv-border);
33
+ border-radius: var(--fv-radius);
34
+ outline: none;
35
+ color: var(--fv-primary);
36
+ }
37
+ .fv-csv__search-input:focus {
38
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--fv-primary) 20%, transparent);
39
+ }
40
+ .fv-csv__row-count {
41
+ font-size: var(--fv-font-size-xs);
42
+ color: var(--fv-muted-foreground);
43
+ }
44
+ .fv-csv__table-wrap {
45
+ overflow: auto;
46
+ flex: 1;
47
+ }
48
+ .fv-csv__table {
49
+ width: 100%;
50
+ font-size: var(--fv-font-size-sm);
51
+ border-collapse: collapse;
52
+ }
53
+ .fv-csv__thead {
54
+ position: sticky;
55
+ top: 0;
56
+ background: color-mix(in srgb, var(--fv-muted) 80%, transparent);
57
+ backdrop-filter: blur(4px);
58
+ z-index: 10;
59
+ }
60
+ .fv-csv__th {
61
+ padding: 0.5rem 0.75rem;
62
+ text-align: left;
63
+ font-weight: 500;
64
+ font-size: var(--fv-font-size-xs);
65
+ color: var(--fv-muted-foreground);
66
+ cursor: pointer;
67
+ white-space: nowrap;
68
+ transition: background 0.15s;
69
+ }
70
+ .fv-csv__th:hover {
71
+ background: var(--fv-muted);
72
+ }
73
+ .fv-csv__th-inner {
74
+ display: flex;
75
+ align-items: center;
76
+ gap: 0.25rem;
77
+ }
78
+ .fv-csv__sort-icon {
79
+ opacity: 0.5;
80
+ }
81
+ .fv-csv__sort-icon--active {
82
+ opacity: 1;
83
+ color: var(--fv-primary);
84
+ }
85
+ .fv-csv__th-num {
86
+ width: 3rem;
87
+ cursor: default;
88
+ }
89
+ .fv-csv__td {
90
+ padding: 0.5rem 0.75rem;
91
+ border-bottom: 1px solid var(--fv-border);
92
+ white-space: nowrap;
93
+ }
94
+ .fv-csv__td:hover {
95
+ background: color-mix(in srgb, var(--fv-muted) 50%, transparent);
96
+ }
97
+ .fv-csv__td-num {
98
+ color: var(--fv-muted-foreground);
99
+ font-size: var(--fv-font-size-xs);
100
+ font-family: var(--fv-font-mono);
101
+ }
102
+ .fv-csv__empty {
103
+ padding: 2rem 0.75rem;
104
+ text-align: center;
105
+ color: var(--fv-muted-foreground);
106
+ }
@@ -0,0 +1,93 @@
1
+ .fv-docx {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100%;
5
+ }
6
+ .fv-docx__info-bar {
7
+ padding: 0.375rem 1rem;
8
+ border-bottom: 1px solid var(--fv-border);
9
+ background: color-mix(in srgb, var(--fv-muted) 30%, transparent);
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: space-between;
13
+ }
14
+ .fv-docx__info-bar span {
15
+ font-size: var(--fv-font-size-xs);
16
+ color: var(--fv-muted-foreground);
17
+ }
18
+ .fv-docx__viewport {
19
+ flex: 1;
20
+ overflow: auto;
21
+ background: var(--fv-canvas-bg);
22
+ position: relative;
23
+ }
24
+ .fv-docx__overlay {
25
+ position: absolute;
26
+ inset: 0;
27
+ z-index: 10;
28
+ display: flex;
29
+ flex-direction: column;
30
+ align-items: center;
31
+ justify-content: center;
32
+ background: var(--fv-canvas-bg);
33
+ }
34
+ .fv-docx__overlay-title {
35
+ font-size: var(--fv-font-size-lg);
36
+ font-weight: 500;
37
+ color: var(--fv-danger);
38
+ }
39
+ .fv-docx__overlay-msg {
40
+ font-size: var(--fv-font-size-sm);
41
+ color: var(--fv-muted-foreground);
42
+ max-width: 28rem;
43
+ text-align: center;
44
+ margin-top: 0.5rem;
45
+ }
46
+ .fv-docx__overlay-spinner {
47
+ margin-bottom: 0.75rem;
48
+ }
49
+
50
+ /* Container layout */
51
+ .docx-preview-container {
52
+ padding: 20px 0;
53
+ display: flex;
54
+ flex-direction: column;
55
+ align-items: center;
56
+ }
57
+
58
+ /* Override docx-wrapper to center pages */
59
+ .docx-preview-container .docx-wrapper {
60
+ background: transparent !important;
61
+ padding: 0 !important;
62
+ display: flex;
63
+ flex-direction: column;
64
+ align-items: center;
65
+ gap: 20px;
66
+ }
67
+
68
+ /* Page styling */
69
+ .docx-preview-container .docx-wrapper > section.docx {
70
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.08) !important;
71
+ margin-bottom: 0 !important;
72
+ }
73
+
74
+ /* Dark mode adjustments */
75
+ @media (prefers-color-scheme: dark) {
76
+ .docx-preview-container .docx-wrapper > section.docx {
77
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.3) !important;
78
+ }
79
+ }
80
+ [data-fv-theme="dark"] .docx-preview-container .docx-wrapper > section.docx {
81
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.3) !important;
82
+ }
83
+
84
+ /* Ensure images scale within pages */
85
+ .docx-preview-container .docx-wrapper img {
86
+ max-width: 100%;
87
+ height: auto;
88
+ }
89
+
90
+ /* Fix table rendering */
91
+ .docx-preview-container .docx-wrapper table {
92
+ border-collapse: collapse;
93
+ }