@anchrd/intel 0.59.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 (364) hide show
  1. package/README.md +496 -0
  2. package/bin/intel.mjs +134 -0
  3. package/dist/adapters/cloudflare/cloudflare-flow-workflow.d.ts +42 -0
  4. package/dist/adapters/cloudflare/cloudflare-flow-workflow.js +89 -0
  5. package/dist/adapters/cloudflare/cloudflare.d.ts +29 -0
  6. package/dist/adapters/cloudflare/cloudflare.js +416 -0
  7. package/dist/adapters/cloudflare/cloudflare.types.d.ts +31 -0
  8. package/dist/adapters/cloudflare/cloudflare.types.js +1 -0
  9. package/dist/adapters/content/content.d.ts +3 -0
  10. package/dist/adapters/content/content.js +128 -0
  11. package/dist/adapters/content/content.types.d.ts +38 -0
  12. package/dist/adapters/content/content.types.js +1 -0
  13. package/dist/adapters/db/db-audit.d.ts +62 -0
  14. package/dist/adapters/db/db-audit.js +150 -0
  15. package/dist/adapters/db/db-boards.d.ts +6 -0
  16. package/dist/adapters/db/db-boards.js +324 -0
  17. package/dist/adapters/db/db-feed.d.ts +30 -0
  18. package/dist/adapters/db/db-feed.js +276 -0
  19. package/dist/adapters/db/db-flows.d.ts +42 -0
  20. package/dist/adapters/db/db-flows.js +1311 -0
  21. package/dist/adapters/db/db-grants.d.ts +133 -0
  22. package/dist/adapters/db/db-grants.js +269 -0
  23. package/dist/adapters/db/db-indexing.d.ts +3 -0
  24. package/dist/adapters/db/db-indexing.js +200 -0
  25. package/dist/adapters/db/db-oauth.d.ts +3 -0
  26. package/dist/adapters/db/db-oauth.js +26 -0
  27. package/dist/adapters/db/db-prompts.d.ts +46 -0
  28. package/dist/adapters/db/db-prompts.js +144 -0
  29. package/dist/adapters/db/db.d.ts +6 -0
  30. package/dist/adapters/db/db.js +1687 -0
  31. package/dist/adapters/db/db.types.d.ts +16 -0
  32. package/dist/adapters/db/db.types.js +1 -0
  33. package/dist/adapters/document-converter/document-converter.d.ts +2 -0
  34. package/dist/adapters/document-converter/document-converter.js +26 -0
  35. package/dist/adapters/document-converter/document-converter.types.d.ts +17 -0
  36. package/dist/adapters/document-converter/document-converter.types.js +1 -0
  37. package/dist/adapters/flow-runtime/flow-runtime.d.ts +3 -0
  38. package/dist/adapters/flow-runtime/flow-runtime.js +22 -0
  39. package/dist/adapters/flow-runtime/flow-runtime.types.d.ts +19 -0
  40. package/dist/adapters/flow-runtime/flow-runtime.types.js +1 -0
  41. package/dist/adapters/index-queue/index-queue.d.ts +10 -0
  42. package/dist/adapters/index-queue/index-queue.js +12 -0
  43. package/dist/adapters/index-queue/index-queue.types.d.ts +11 -0
  44. package/dist/adapters/index-queue/index-queue.types.js +1 -0
  45. package/dist/adapters/json-schema/json-schema.d.ts +2 -0
  46. package/dist/adapters/json-schema/json-schema.js +77 -0
  47. package/dist/adapters/openid/openid.d.ts +5 -0
  48. package/dist/adapters/openid/openid.js +301 -0
  49. package/dist/adapters/portal-tokens/portal-tokens.d.ts +8 -0
  50. package/dist/adapters/portal-tokens/portal-tokens.js +79 -0
  51. package/dist/adapters/remote-tools/remote-tools.d.ts +5 -0
  52. package/dist/adapters/remote-tools/remote-tools.js +129 -0
  53. package/dist/adapters/semantic-index/semantic-index.d.ts +2 -0
  54. package/dist/adapters/semantic-index/semantic-index.js +139 -0
  55. package/dist/adapters/semantic-index/semantic-index.types.d.ts +56 -0
  56. package/dist/adapters/semantic-index/semantic-index.types.js +1 -0
  57. package/dist/adapters/session-cookie/session-cookie.d.ts +5 -0
  58. package/dist/adapters/session-cookie/session-cookie.js +87 -0
  59. package/dist/adapters/tool-source-policy/tool-source-policy.d.ts +1 -0
  60. package/dist/adapters/tool-source-policy/tool-source-policy.js +22 -0
  61. package/dist/audit/audit.d.ts +2 -0
  62. package/dist/audit/audit.js +87 -0
  63. package/dist/audit/audit.types.d.ts +23 -0
  64. package/dist/audit/audit.types.js +1 -0
  65. package/dist/auth/auth.d.ts +2 -0
  66. package/dist/auth/auth.js +284 -0
  67. package/dist/auth/auth.types.d.ts +108 -0
  68. package/dist/auth/auth.types.js +1 -0
  69. package/dist/boards/boards.d.ts +44 -0
  70. package/dist/boards/boards.js +447 -0
  71. package/dist/boards/boards.types.d.ts +117 -0
  72. package/dist/boards/boards.types.js +1 -0
  73. package/dist/build/build.d.ts +2 -0
  74. package/dist/build/build.js +192 -0
  75. package/dist/build/build.types.d.ts +17 -0
  76. package/dist/build/build.types.js +1 -0
  77. package/dist/bundle/bundle.d.ts +4 -0
  78. package/dist/bundle/bundle.js +1194 -0
  79. package/dist/bundle/bundle.types.d.ts +33 -0
  80. package/dist/bundle/bundle.types.js +1 -0
  81. package/dist/cli/cli.d.ts +4 -0
  82. package/dist/cli/cli.js +262 -0
  83. package/dist/cli/cli.types.d.ts +14 -0
  84. package/dist/cli/cli.types.js +1 -0
  85. package/dist/contract/audit.d.ts +50 -0
  86. package/dist/contract/audit.js +72 -0
  87. package/dist/contract/board.d.ts +150 -0
  88. package/dist/contract/board.js +262 -0
  89. package/dist/contract/bundle.d.ts +147 -0
  90. package/dist/contract/bundle.js +139 -0
  91. package/dist/contract/contract.d.ts +112 -0
  92. package/dist/contract/contract.js +143 -0
  93. package/dist/contract/feed.d.ts +126 -0
  94. package/dist/contract/feed.js +149 -0
  95. package/dist/contract/flow-run.d.ts +346 -0
  96. package/dist/contract/flow-run.js +194 -0
  97. package/dist/contract/flow.d.ts +1057 -0
  98. package/dist/contract/flow.js +552 -0
  99. package/dist/contract/node.d.ts +551 -0
  100. package/dist/contract/node.js +456 -0
  101. package/dist/contract/share.d.ts +216 -0
  102. package/dist/contract/share.js +134 -0
  103. package/dist/contract/table.d.ts +171 -0
  104. package/dist/contract/table.js +117 -0
  105. package/dist/contract/tool.d.ts +122 -0
  106. package/dist/contract/tool.js +172 -0
  107. package/dist/feed/feed.d.ts +2 -0
  108. package/dist/feed/feed.js +77 -0
  109. package/dist/feed/feed.types.d.ts +23 -0
  110. package/dist/feed/feed.types.js +1 -0
  111. package/dist/flows/flows.d.ts +41 -0
  112. package/dist/flows/flows.js +2216 -0
  113. package/dist/flows/flows.types.d.ts +339 -0
  114. package/dist/flows/flows.types.js +1 -0
  115. package/dist/http/http.d.ts +9 -0
  116. package/dist/http/http.js +874 -0
  117. package/dist/http/http.types.d.ts +22 -0
  118. package/dist/http/http.types.js +1 -0
  119. package/dist/indexing/indexing.d.ts +6 -0
  120. package/dist/indexing/indexing.js +239 -0
  121. package/dist/indexing/indexing.types.d.ts +11 -0
  122. package/dist/indexing/indexing.types.js +1 -0
  123. package/dist/intel/intel.d.ts +3 -0
  124. package/dist/intel/intel.js +194 -0
  125. package/dist/intel/intel.types.d.ts +24 -0
  126. package/dist/intel/intel.types.js +1 -0
  127. package/dist/mcp/mcp.d.ts +2 -0
  128. package/dist/mcp/mcp.js +1292 -0
  129. package/dist/mcp/mcp.types.d.ts +25 -0
  130. package/dist/mcp/mcp.types.js +1 -0
  131. package/dist/nodes/document-links/document-links.d.ts +20 -0
  132. package/dist/nodes/document-links/document-links.js +57 -0
  133. package/dist/nodes/nodes.d.ts +2 -0
  134. package/dist/nodes/nodes.js +1667 -0
  135. package/dist/nodes/nodes.types.d.ts +511 -0
  136. package/dist/nodes/nodes.types.js +1 -0
  137. package/dist/prepare/prepare.d.ts +4 -0
  138. package/dist/prepare/prepare.js +16 -0
  139. package/dist/prepare/prepare.types.d.ts +9 -0
  140. package/dist/prepare/prepare.types.js +1 -0
  141. package/dist/prompts/prompts.d.ts +2 -0
  142. package/dist/prompts/prompts.js +65 -0
  143. package/dist/prompts/prompts.types.d.ts +71 -0
  144. package/dist/prompts/prompts.types.js +1 -0
  145. package/dist/shared/callers/callers.d.ts +28 -0
  146. package/dist/shared/callers/callers.js +30 -0
  147. package/dist/shared/csv/csv.d.ts +13 -0
  148. package/dist/shared/csv/csv.js +85 -0
  149. package/dist/shared/document-text/document-text.d.ts +21 -0
  150. package/dist/shared/document-text/document-text.js +31 -0
  151. package/dist/shared/gate-authorization/gate-authorization.d.ts +5 -0
  152. package/dist/shared/gate-authorization/gate-authorization.js +26 -0
  153. package/dist/shared/grant-expiry/grant-expiry.d.ts +19 -0
  154. package/dist/shared/grant-expiry/grant-expiry.js +26 -0
  155. package/dist/shared/intel-error/intel-error.d.ts +22 -0
  156. package/dist/shared/intel-error/intel-error.js +27 -0
  157. package/dist/shared/plain-title/plain-title.d.ts +16 -0
  158. package/dist/shared/plain-title/plain-title.js +45 -0
  159. package/dist/shared/problem-details/problem-details.d.ts +30 -0
  160. package/dist/shared/problem-details/problem-details.js +18 -0
  161. package/dist/shared/report-unexpected-error/report-unexpected-error.d.ts +1 -0
  162. package/dist/shared/report-unexpected-error/report-unexpected-error.js +37 -0
  163. package/dist/shared/safe-return-path/safe-return-path.d.ts +2 -0
  164. package/dist/shared/safe-return-path/safe-return-path.js +11 -0
  165. package/dist/shared/sha256/sha256.d.ts +2 -0
  166. package/dist/shared/sha256/sha256.js +14 -0
  167. package/dist/tools/tool-servers/tool-servers.d.ts +46 -0
  168. package/dist/tools/tool-servers/tool-servers.js +177 -0
  169. package/dist/tools/tools.d.ts +2 -0
  170. package/dist/tools/tools.js +207 -0
  171. package/dist/tools/tools.types.d.ts +56 -0
  172. package/dist/tools/tools.types.js +1 -0
  173. package/examples/branding/favicon.svg +5 -0
  174. package/examples/branding/logo.svg +5 -0
  175. package/examples/branding/theme.css +16 -0
  176. package/examples/dev.vars.example +7 -0
  177. package/examples/intel.json +9 -0
  178. package/migrations/0000_intel_foundation.sql +203 -0
  179. package/migrations/0001_portal_native_tools.sql +21 -0
  180. package/migrations/0002_flows_in_the_knowledge_tree.sql +34 -0
  181. package/migrations/0003_folder_permissions.sql +211 -0
  182. package/migrations/0004_subflow_runs.sql +14 -0
  183. package/migrations/0005_flow_node_cleanup.sql +28 -0
  184. package/migrations/0005_tables_in_the_knowledge_tree.sql +68 -0
  185. package/migrations/0006_links_are_written_in_the_text.sql +20 -0
  186. package/migrations/0007_no_node_waits.sql +69 -0
  187. package/migrations/0008_three_layers.sql +130 -0
  188. package/migrations/0009_no_context_policy.sql +48 -0
  189. package/migrations/0010_no_run_waits_a_year.sql +27 -0
  190. package/migrations/0011_one_name_for_the_tree.sql +53 -0
  191. package/migrations/0012_table_snapshots.sql +29 -0
  192. package/migrations/0017_a_vector_per_card.sql +38 -0
  193. package/migrations/0018_no_context_policy_at_last.sql +97 -0
  194. package/migrations/0019_one_name_for_the_grants.sql +52 -0
  195. package/migrations/0020_cascade_purge_replay.sql +11 -0
  196. package/migrations/0021_a_flow_carries_its_own_grant.sql +44 -0
  197. package/migrations/0022_a_cursor_over_the_journal.sql +14 -0
  198. package/migrations/0023_a_board_and_its_tasks.sql +116 -0
  199. package/migrations/0024_the_archive_is_a_column.sql +33 -0
  200. package/migrations/0025_archiving_takes_the_subtree.sql +29 -0
  201. package/migrations/0026_one_feed_over_two_kinds.sql +23 -0
  202. package/migrations/0027_the_runs_of_every_flow.sql +27 -0
  203. package/migrations/0028_a_prompt_name_over_two_kinds.sql +70 -0
  204. package/package.json +118 -0
  205. package/src/contract/CLAUDE.md +152 -0
  206. package/src/contract/README.md +102 -0
  207. package/src/contract/audit.ts +85 -0
  208. package/src/contract/board.ts +306 -0
  209. package/src/contract/bundle.ts +157 -0
  210. package/src/contract/contract.ts +161 -0
  211. package/src/contract/feed.ts +165 -0
  212. package/src/contract/flow-run.ts +241 -0
  213. package/src/contract/flow.ts +675 -0
  214. package/src/contract/node.ts +583 -0
  215. package/src/contract/share.ts +196 -0
  216. package/src/contract/table.ts +178 -0
  217. package/src/contract/tool.ts +202 -0
  218. package/ui/README.md +110 -0
  219. package/ui/components.json +21 -0
  220. package/ui/index.html +13 -0
  221. package/ui/package.json +11 -0
  222. package/ui/scripts/lint-tokens.mjs +187 -0
  223. package/ui/src/access-summary/access-summary.tsx +385 -0
  224. package/ui/src/app/action-slot/action-slot.tsx +27 -0
  225. package/ui/src/app/app-sidebar/app-sidebar.tsx +71 -0
  226. package/ui/src/app/app-tree/app-tree.tsx +1175 -0
  227. package/ui/src/app/app.tsx +87 -0
  228. package/ui/src/app/header-search/header-search.tsx +294 -0
  229. package/ui/src/app/reindex-dialog/reindex-dialog.tsx +79 -0
  230. package/ui/src/app/settings-dialog/settings-dialog.tsx +135 -0
  231. package/ui/src/app/sidebar-preferences/sidebar-preferences.ts +68 -0
  232. package/ui/src/app/sidebar-preferences/sidebar-preferences.types.ts +15 -0
  233. package/ui/src/app/sidebar-resize-handle/sidebar-resize-handle.tsx +86 -0
  234. package/ui/src/app/tree-expansion/tree-expansion.ts +107 -0
  235. package/ui/src/app/tree-expansion/tree-expansion.types.ts +31 -0
  236. package/ui/src/app/tree-move/tree-move.tsx +427 -0
  237. package/ui/src/app/tree-reveal/tree-reveal.ts +64 -0
  238. package/ui/src/app/tree-reveal/tree-reveal.types.ts +16 -0
  239. package/ui/src/app/user-footer/user-footer.tsx +188 -0
  240. package/ui/src/app/view-menu/view-menu.tsx +115 -0
  241. package/ui/src/app-root/app-root.tsx +109 -0
  242. package/ui/src/app-root/app-root.types.ts +27 -0
  243. package/ui/src/archive/archive.tsx +417 -0
  244. package/ui/src/attachment-detail/attachment-detail.ts +52 -0
  245. package/ui/src/attachment-viewer/attachment-viewer.tsx +166 -0
  246. package/ui/src/blocknote-view/blocknote-view.tsx +30 -0
  247. package/ui/src/board/board-assignee/board-assignee-picker.tsx +132 -0
  248. package/ui/src/board/board-assignee/board-assignee.ts +48 -0
  249. package/ui/src/board/board-assignee/board-assignee.tsx +75 -0
  250. package/ui/src/board/board-chip/board-chip.tsx +46 -0
  251. package/ui/src/board/board-crumbs/board-crumbs.ts +56 -0
  252. package/ui/src/board/board-crumbs/board-crumbs.tsx +111 -0
  253. package/ui/src/board/board-data/board-data.ts +195 -0
  254. package/ui/src/board/board-data/board-data.types.ts +38 -0
  255. package/ui/src/board/board-dates/board-dates.ts +40 -0
  256. package/ui/src/board/board-dates/board-dates.tsx +105 -0
  257. package/ui/src/board/board-kanban/board-kanban.ts +162 -0
  258. package/ui/src/board/board-kanban/board-kanban.tsx +680 -0
  259. package/ui/src/board/board-open-task/board-open-task.ts +32 -0
  260. package/ui/src/board/board-panel/board-panel.tsx +259 -0
  261. package/ui/src/board/board-settings/board-settings.tsx +226 -0
  262. package/ui/src/board/board-status/board-status.tsx +60 -0
  263. package/ui/src/board/board-stripes/board-stripes.ts +128 -0
  264. package/ui/src/board/board-table/board-table.ts +53 -0
  265. package/ui/src/board/board-table/board-table.tsx +610 -0
  266. package/ui/src/board/board-task/board-task.ts +108 -0
  267. package/ui/src/board/board-task/board-task.tsx +335 -0
  268. package/ui/src/board/board-title-row/board-title-row.tsx +66 -0
  269. package/ui/src/branding/branding.tsx +17 -0
  270. package/ui/src/branding/custom-logo.ts +1 -0
  271. package/ui/src/branding/favicon.default.svg +5 -0
  272. package/ui/src/branding/favicon.svg +5 -0
  273. package/ui/src/components/ui/avatar.tsx +39 -0
  274. package/ui/src/components/ui/button.tsx +64 -0
  275. package/ui/src/components/ui/collapsible.tsx +20 -0
  276. package/ui/src/components/ui/command.tsx +160 -0
  277. package/ui/src/components/ui/dialog.tsx +143 -0
  278. package/ui/src/components/ui/dropdown-menu.tsx +187 -0
  279. package/ui/src/components/ui/input.tsx +21 -0
  280. package/ui/src/components/ui/popover.tsx +41 -0
  281. package/ui/src/components/ui/select.tsx +163 -0
  282. package/ui/src/components/ui/separator.tsx +26 -0
  283. package/ui/src/components/ui/sheet.tsx +136 -0
  284. package/ui/src/components/ui/sidebar.tsx +693 -0
  285. package/ui/src/components/ui/skeleton.tsx +13 -0
  286. package/ui/src/components/ui/switch.tsx +25 -0
  287. package/ui/src/components/ui/table.tsx +82 -0
  288. package/ui/src/components/ui/tabs.tsx +52 -0
  289. package/ui/src/components/ui/tooltip.tsx +51 -0
  290. package/ui/src/data/intel-data-provider/intel-data-provider.ts +685 -0
  291. package/ui/src/data/intel-data-provider/intel-data-provider.types.ts +266 -0
  292. package/ui/src/data/query-client.ts +18 -0
  293. package/ui/src/data/request-refusal/refusal-notice.tsx +36 -0
  294. package/ui/src/data/request-refusal/request-refusal.ts +51 -0
  295. package/ui/src/data/sign-in/sign-in.ts +97 -0
  296. package/ui/src/data/sign-in/sign-in.types.ts +15 -0
  297. package/ui/src/document-link/document-link.tsx +114 -0
  298. package/ui/src/document-markdown/document-markdown.ts +195 -0
  299. package/ui/src/editor-schema/editor-schema.ts +23 -0
  300. package/ui/src/entry-picker/entry-picker.tsx +238 -0
  301. package/ui/src/feed/feed.tsx +344 -0
  302. package/ui/src/file-preview/file-preview-view.tsx +496 -0
  303. package/ui/src/file-preview/file-preview.tsx +28 -0
  304. package/ui/src/file-preview/pdf-file-preview.tsx +5 -0
  305. package/ui/src/file-preview/presentation-file-preview.tsx +21 -0
  306. package/ui/src/file-preview/spreadsheet-file-preview.tsx +5 -0
  307. package/ui/src/file-preview/word-file-preview.tsx +5 -0
  308. package/ui/src/flow-runs/flow-runs.tsx +238 -0
  309. package/ui/src/flows/flows.tsx +1646 -0
  310. package/ui/src/flows/node-icon/node-icon.ts +34 -0
  311. package/ui/src/flows/node-palette/node-palette.tsx +214 -0
  312. package/ui/src/flows/node-palette/node-palette.types.ts +15 -0
  313. package/ui/src/folder-contents/folder-contents.tsx +321 -0
  314. package/ui/src/folder-index/folder-index.ts +55 -0
  315. package/ui/src/frontmatter/frontmatter.tsx +193 -0
  316. package/ui/src/graph-pane/graph-pane.tsx +53 -0
  317. package/ui/src/hooks/use-mobile.ts +19 -0
  318. package/ui/src/i18n/custom.json +4 -0
  319. package/ui/src/i18n/de.json +609 -0
  320. package/ui/src/i18n/en.json +609 -0
  321. package/ui/src/i18n/es.json +609 -0
  322. package/ui/src/i18n/i18n-context.tsx +65 -0
  323. package/ui/src/i18n/i18n-languages/i18n-languages.ts +83 -0
  324. package/ui/src/i18n/i18n.ts +32 -0
  325. package/ui/src/i18n/i18n.types.ts +49 -0
  326. package/ui/src/kind-icon.ts +97 -0
  327. package/ui/src/lib/utils.ts +6 -0
  328. package/ui/src/main.tsx +68 -0
  329. package/ui/src/modal/modal.tsx +53 -0
  330. package/ui/src/node-details/node-details.tsx +545 -0
  331. package/ui/src/node-editor/node-editor.tsx +304 -0
  332. package/ui/src/node-graph/graph-notice.tsx +21 -0
  333. package/ui/src/node-graph/node-graph.ts +214 -0
  334. package/ui/src/node-graph/node-graph.tsx +295 -0
  335. package/ui/src/node-import/node-import.tsx +418 -0
  336. package/ui/src/node-save-error.ts +46 -0
  337. package/ui/src/node-source/node-source.tsx +128 -0
  338. package/ui/src/node-table/node-table.tsx +107 -0
  339. package/ui/src/nodes/nodes.tsx +240 -0
  340. package/ui/src/rename-resource/rename-resource.ts +80 -0
  341. package/ui/src/resolved-names/resolved-names.tsx +67 -0
  342. package/ui/src/resource-error.ts +33 -0
  343. package/ui/src/resource-menu/resource-menu.tsx +1156 -0
  344. package/ui/src/router/router-context.ts +6 -0
  345. package/ui/src/router/router.tsx +67 -0
  346. package/ui/src/router/router.types.ts +8 -0
  347. package/ui/src/router/selection-search.ts +88 -0
  348. package/ui/src/save-button/save-button.tsx +94 -0
  349. package/ui/src/section-hint/section-hint.tsx +40 -0
  350. package/ui/src/sign-in-refused/sign-in-refused.tsx +35 -0
  351. package/ui/src/styles.css +189 -0
  352. package/ui/src/table-columns/table-columns.tsx +379 -0
  353. package/ui/src/theme/custom.css +1 -0
  354. package/ui/src/theme/theme-context.tsx +89 -0
  355. package/ui/src/theme/theme.ts +70 -0
  356. package/ui/src/time/relative-time.tsx +41 -0
  357. package/ui/src/time/time-context.tsx +78 -0
  358. package/ui/src/time/time.ts +165 -0
  359. package/ui/src/title-row/title-row.tsx +185 -0
  360. package/ui/src/tools/tools.tsx +546 -0
  361. package/ui/src/user-name/user-name.ts +61 -0
  362. package/ui/src/verbatim/verbatim.tsx +68 -0
  363. package/ui/tsconfig.json +17 -0
  364. package/ui/vite.config.ts +129 -0
@@ -0,0 +1,496 @@
1
+ import FileViewer, {
2
+ type FileViewerHandle,
3
+ type ViewerOptions,
4
+ type ViewerState,
5
+ type ViewerViewState,
6
+ } from "@file-viewer/react";
7
+ import {
8
+ ChevronLeft,
9
+ ChevronRight,
10
+ Minus,
11
+ PanelLeft,
12
+ Plus,
13
+ RotateCcw,
14
+ RotateCw,
15
+ Search,
16
+ X,
17
+ } from "lucide-react";
18
+ import { useEffect, useMemo, useRef, useState } from "react";
19
+ import { ActionSlot } from "@/app/action-slot/action-slot.tsx";
20
+ import { useI18n } from "@/i18n/i18n-context.tsx";
21
+ import { useResolvedTheme } from "@/theme/theme-context.tsx";
22
+
23
+ // The PDF renderer exposes no stable Shadow Parts for its navigation internals. These selectors
24
+ // intentionally target the pinned @file-viewer/renderer-pdf 2.3.0 DOM; verify them before upgrades.
25
+ const viewerTheme = `
26
+ :host,.file-viewer-web-shell{
27
+ --file-viewer-render-surface-background:var(--muted);
28
+ --file-viewer-bg:var(--background);--file-viewer-content-bg:var(--muted);
29
+ --file-viewer-text:var(--foreground);--file-viewer-muted:var(--muted-foreground);
30
+ --file-viewer-border:var(--border);--file-viewer-toolbar-bg:var(--background);
31
+ --file-viewer-toolbar-border:var(--border);--file-viewer-group-bg:var(--muted);
32
+ --file-viewer-group-border:var(--border);--file-viewer-button-color:var(--foreground);
33
+ --file-viewer-button-hover-bg:var(--accent);--file-viewer-button-hover-color:var(--accent-foreground);
34
+ --file-viewer-button-disabled-color:var(--muted-foreground);--file-viewer-input-bg:var(--background);
35
+ --file-viewer-input-color:var(--foreground);--file-viewer-focus-ring:var(--ring);
36
+ }
37
+ .pdf-shell,.pdf-wrapper{background:var(--muted)!important;color:var(--foreground)!important}
38
+ .pdf-nav-pane,.pdf-nav-tabs,.pdf-nav-head{background:var(--background)!important;border-color:var(--border)!important}
39
+ @media (min-width:721px){.pdf-shell:not(.pdf-shell--nav-hidden) .pdf-content{grid-template-columns:4rem minmax(0,1fr)!important}}
40
+ .pdfViewer{padding-inline:0!important}.pdfViewer .page{border-inline:0!important}
41
+ .pdf-nav-head{display:none!important}.pdf-nav-tabs{display:flex!important;justify-content:center;padding:4px 0!important}
42
+ .pdf-nav-tabs:not([data-outline-available="true"]){display:none!important}.pdf-nav-tabs button.active{display:none!important}
43
+ .pdf-nav-tabs button{width:30px;min-width:30px;padding:0;border:0!important;box-shadow:none!important;font-size:0;color:var(--muted-foreground)!important}
44
+ .pdf-nav-tabs button svg{width:15px;height:15px;margin:auto;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
45
+ .pdf-nav-tabs button:hover,.pdf-nav-tabs button.active{background:var(--accent)!important;border:0!important;color:var(--accent-foreground)!important}
46
+ .pdf-page-list{gap:2px!important;padding:4px 0!important}.pdf-page-button{display:flex!important;width:28px!important;min-width:28px!important;height:28px!important;min-height:28px!important;margin-inline:auto!important;padding:0!important;border:0!important;box-shadow:none!important;background:transparent!important;color:var(--foreground)!important;justify-content:center!important}
47
+ .pdf-page-button:hover,.pdf-page-button--active{background:var(--accent)!important;border:0!important;box-shadow:none!important}
48
+ .pdf-page-thumb{width:24px!important;height:24px!important;border:0!important;background:transparent!important;color:var(--foreground)!important}
49
+ .pdf-page-label{display:none!important}.pdf-outline-button{color:var(--foreground)!important}.pdf-outline-button:hover{background:var(--accent)!important;border-color:var(--border)!important}
50
+ `;
51
+
52
+ export function FilePreviewView({
53
+ file,
54
+ renderer,
55
+ pdf = false,
56
+ }: {
57
+ file: File;
58
+ renderer: unknown;
59
+ pdf?: boolean;
60
+ }) {
61
+ const theme = useResolvedTheme();
62
+ const i18n = useI18n();
63
+ const viewer = useRef<FileViewerHandle>(null);
64
+ const searchInput = useRef<HTMLInputElement>(null);
65
+ const pdfFit = useRef({ enabled: true, page: 0, pageWidthAtScaleOne: 0, rotation: 0 });
66
+ const schedulePdfFit = useRef<(() => void) | null>(null);
67
+ const [state, setState] = useState<ViewerState | null>(null);
68
+ const [searchOpen, setSearchOpen] = useState(false);
69
+ const [query, setQuery] = useState("");
70
+ const options = useMemo<ViewerOptions>(
71
+ () => ({
72
+ renderers: [renderer] as unknown as NonNullable<ViewerOptions["renderers"]>,
73
+ rendererMode: "replace",
74
+ builtinRenderers: "none",
75
+ autoRenderers: false,
76
+ styleIsolation: "shadow",
77
+ theme,
78
+ locale: "auto",
79
+ toolbar: false,
80
+ ...(pdf
81
+ ? {
82
+ fit: { mode: "width", resize: "until-interaction", padding: 0 },
83
+ pdf: { toolbar: false, navigation: true, defaultNavigationVisible: false },
84
+ }
85
+ : {}),
86
+ ui: { density: "compact", surfaceBackground: "transparent" },
87
+ }),
88
+ [pdf, renderer, theme],
89
+ );
90
+ const viewState = state?.viewState;
91
+ const page = viewState?.page ?? 1;
92
+ const pageCount = viewState?.pageCount ?? 0;
93
+ const scale = state?.zoom?.scale ?? viewState?.scale ?? 1;
94
+ const search = state?.search;
95
+ const latestView = useRef({ page, rotation: viewState?.rotation ?? 0, scale });
96
+ const pdfPageRequest = useRef({ page, pending: false });
97
+ latestView.current = { page, rotation: viewState?.rotation ?? 0, scale };
98
+
99
+ useEffect(() => {
100
+ const request = pdfPageRequest.current;
101
+ if (!request.pending || request.page === page) {
102
+ request.page = page;
103
+ request.pending = false;
104
+ }
105
+ }, [page]);
106
+
107
+ useEffect(() => {
108
+ if (!state?.ready) return;
109
+ const root = viewer.current?.getController()?.container.shadowRoot;
110
+ if (!root) return;
111
+ if (!root.querySelector("style[data-intel-viewer-theme]")) {
112
+ const style = document.createElement("style");
113
+ style.dataset.intelViewerTheme = "true";
114
+ style.textContent = viewerTheme;
115
+ root.append(style);
116
+ }
117
+ const adaptNavigation = () => {
118
+ for (const button of root.querySelectorAll<HTMLButtonElement>(".pdf-page-button")) {
119
+ const label = button.querySelector(".pdf-page-label")?.textContent?.trim();
120
+ if (label) button.title = label;
121
+ }
122
+ const tabs = root.querySelector<HTMLElement>(".pdf-nav-tabs");
123
+ const buttons = tabs?.querySelectorAll<HTMLButtonElement>("button");
124
+ if (tabs && buttons?.length === 2) {
125
+ tabs.setAttribute("role", "group");
126
+ for (const [index, button] of buttons.entries()) {
127
+ button.setAttribute("role", "button");
128
+ button.removeAttribute("aria-selected");
129
+ if (!button.querySelector("[data-intel-navigation-icon]")) {
130
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
131
+ svg.dataset.intelNavigationIcon = "true";
132
+ svg.setAttribute("viewBox", "0 0 24 24");
133
+ svg.setAttribute("aria-hidden", "true");
134
+ const paths =
135
+ index === 0
136
+ ? ["M8 6h13M8 12h13M8 18h13", "M3 6h.01M3 12h.01M3 18h.01"]
137
+ : ["M4 6h7M9 12h11M13 18h7", "M4 6v12h9M9 6v6M13 12v6"];
138
+ for (const pathData of paths) {
139
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
140
+ path.setAttribute("d", pathData);
141
+ svg.append(path);
142
+ }
143
+ button.prepend(svg);
144
+ }
145
+ if (button.dataset.intelNavigationAdapted !== "true") {
146
+ button.dataset.intelNavigationAdapted = "true";
147
+ button.addEventListener("click", () => {
148
+ if (root.activeElement === button) buttons[index === 0 ? 1 : 0]?.focus();
149
+ for (const tabButton of buttons) tabButton.removeAttribute("aria-selected");
150
+ });
151
+ }
152
+ }
153
+ }
154
+ };
155
+ let outlineObserver: MutationObserver;
156
+ let outlineTarget: HTMLElement | null = null;
157
+ const probeOutline = () => {
158
+ const tabs = root.querySelector<HTMLElement>(".pdf-nav-tabs");
159
+ const [pagesTab, outlineTab] = tabs?.querySelectorAll<HTMLButtonElement>("button") ?? [];
160
+ const navPane = root.querySelector<HTMLElement>(".pdf-nav-pane");
161
+ const navList = root.querySelector<HTMLElement>(".pdf-page-list,.pdf-outline-list");
162
+ if (!tabs || !pagesTab || !outlineTab || !navPane || !navList) return;
163
+
164
+ outlineObserver.disconnect();
165
+ const startedInOutline = outlineTab.classList.contains("active");
166
+ if (!startedInOutline) {
167
+ navPane.style.visibility = "hidden";
168
+ outlineTab.click();
169
+ }
170
+ tabs.dataset.outlineAvailable = String(root.querySelector(".pdf-outline-button") !== null);
171
+ if (!startedInOutline) pagesTab.click();
172
+ navPane.style.removeProperty("visibility");
173
+ adaptNavigation();
174
+ outlineTarget = navList;
175
+ outlineObserver.observe(navList, { childList: true });
176
+ };
177
+ adaptNavigation();
178
+ const observer = new MutationObserver(() => {
179
+ adaptNavigation();
180
+ if (!outlineTarget || !root.contains(outlineTarget)) probeOutline();
181
+ });
182
+ observer.observe(root, { childList: true, subtree: true });
183
+ outlineObserver = new MutationObserver(() => {
184
+ const tabs = root.querySelector<HTMLElement>(".pdf-nav-tabs");
185
+ if (tabs?.dataset.outlineAvailable !== "true") probeOutline();
186
+ });
187
+ // The pinned renderer exposes no outline count. Probe synchronously between paints, then let
188
+ // direct navigation-list mutations trigger another probe whenever a late outline read lands.
189
+ probeOutline();
190
+ return () => {
191
+ observer.disconnect();
192
+ outlineObserver.disconnect();
193
+ };
194
+ }, [state?.ready]);
195
+
196
+ useEffect(() => {
197
+ if (!pdf || !state?.ready) return;
198
+ const root = viewer.current?.getController()?.container.shadowRoot;
199
+ if (!root) return;
200
+ let frame = 0;
201
+ const fitPdfToWidth = () => {
202
+ cancelAnimationFrame(frame);
203
+ frame = requestAnimationFrame(() => {
204
+ const fitState = pdfFit.current;
205
+ if (!fitState.enabled) return;
206
+ const wrapper = root.querySelector<HTMLElement>(".pdf-wrapper");
207
+ const current = latestView.current;
208
+ const currentPage =
209
+ root.querySelector<HTMLElement>(`.pdfViewer .page[data-page-number="${current.page}"]`) ??
210
+ root.querySelector<HTMLElement>(".pdfViewer .page");
211
+ const renderedWidth = currentPage?.getBoundingClientRect().width ?? 0;
212
+ const availableWidth = wrapper?.clientWidth ?? 0;
213
+ if (renderedWidth <= 0 || availableWidth <= 0 || current.scale <= 0) return;
214
+ if (fitState.page !== current.page || fitState.rotation !== current.rotation) {
215
+ fitState.page = current.page;
216
+ fitState.rotation = current.rotation;
217
+ fitState.pageWidthAtScaleOne = 0;
218
+ }
219
+ if (fitState.pageWidthAtScaleOne <= 0) {
220
+ fitState.pageWidthAtScaleOne = renderedWidth / current.scale;
221
+ }
222
+ // The pinned PDF renderer clamps scale to two decimals. Floor to the same precision so the
223
+ // page never overflows and sub-pixel differences do not schedule identical updates.
224
+ const targetScale = Math.floor((availableWidth / fitState.pageWidthAtScaleOne) * 100) / 100;
225
+ if (Math.abs(targetScale - current.scale) <= 0.001) return;
226
+ void viewer.current?.applyViewState({ scale: targetScale });
227
+ });
228
+ };
229
+ schedulePdfFit.current = fitPdfToWidth;
230
+ const wrapper = root.querySelector<HTMLElement>(".pdf-wrapper");
231
+ const resizeObserver =
232
+ typeof ResizeObserver === "undefined" ? null : new ResizeObserver(fitPdfToWidth);
233
+ if (wrapper) resizeObserver?.observe(wrapper);
234
+ const mutationObserver = new MutationObserver(fitPdfToWidth);
235
+ mutationObserver.observe(root, { childList: true, subtree: true });
236
+ fitPdfToWidth();
237
+ return () => {
238
+ cancelAnimationFrame(frame);
239
+ resizeObserver?.disconnect();
240
+ mutationObserver.disconnect();
241
+ if (schedulePdfFit.current === fitPdfToWidth) schedulePdfFit.current = null;
242
+ };
243
+ }, [pdf, state?.ready]);
244
+
245
+ useEffect(() => {
246
+ if (page > 0 && viewState?.rotation !== undefined) schedulePdfFit.current?.();
247
+ }, [page, viewState?.rotation]);
248
+
249
+ useEffect(() => {
250
+ if (searchOpen) searchInput.current?.focus();
251
+ }, [searchOpen]);
252
+
253
+ const applyViewState = (next: ViewerViewState) => viewer.current?.applyViewState(next);
254
+ const goToPdfPage = (direction: -1 | 1) => {
255
+ const request = pdfPageRequest.current;
256
+ const nextPage = Math.max(1, Math.min(pageCount, request.page + direction));
257
+ request.page = nextPage;
258
+ request.pending = nextPage !== page;
259
+ const root = viewer.current?.getController()?.container.shadowRoot;
260
+ const pageElement = root?.querySelector<HTMLElement>(
261
+ `.pdfViewer .page[data-page-number="${nextPage}"]`,
262
+ );
263
+ if (pageElement) {
264
+ pageElement.scrollIntoView({ block: "start", inline: "nearest" });
265
+ return;
266
+ }
267
+ void applyViewState({ page: nextPage });
268
+ };
269
+
270
+ return (
271
+ <div className="relative h-full min-h-64">
272
+ {pdf ? (
273
+ <ActionSlot name="title-actions">
274
+ <button
275
+ type="button"
276
+ aria-label={i18n.t("attachment.viewer.navigation")}
277
+ aria-pressed={viewState?.navigation?.visible ?? false}
278
+ onClick={() =>
279
+ void applyViewState({
280
+ navigation: {
281
+ ...viewState?.navigation,
282
+ visible: !(viewState?.navigation?.visible ?? false),
283
+ },
284
+ })
285
+ }
286
+ className="inline-flex size-8 items-center justify-center rounded-md bg-transparent outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring aria-pressed:bg-muted"
287
+ >
288
+ <PanelLeft aria-hidden="true" className="size-4" />
289
+ </button>
290
+ </ActionSlot>
291
+ ) : null}
292
+ <div className="absolute inset-0 min-h-0">
293
+ <FileViewer
294
+ ref={viewer}
295
+ file={file}
296
+ className="h-full min-h-0"
297
+ options={options}
298
+ onStateChange={setState}
299
+ />
300
+ </div>
301
+ {state?.ready ? (
302
+ <div
303
+ role="toolbar"
304
+ aria-label={i18n.t("attachment.viewer.controls")}
305
+ className={`absolute right-3 bottom-2 left-3 z-[5] mx-auto flex min-h-10 w-fit max-w-[calc(100%-1.5rem)] items-center gap-1 rounded-full border bg-background/80 p-1 shadow-lg backdrop-blur-md ${
306
+ viewState?.navigation?.visible
307
+ ? "[@media(max-width:720px)]:hidden min-[721px]:left-[calc(4rem+0.75rem)]"
308
+ : ""
309
+ }`}
310
+ >
311
+ <span role="status" aria-live="polite" aria-atomic="true" className="sr-only">
312
+ {search?.query
313
+ ? search.total > 0
314
+ ? i18n.t("attachment.viewer.searchResults", {
315
+ current: search.currentIndex + 1,
316
+ total: search.total,
317
+ })
318
+ : i18n.t("attachment.viewer.searchNoResults")
319
+ : ""}
320
+ </span>
321
+ {pdf && pageCount > 0 && !searchOpen ? (
322
+ <div className="flex items-center rounded-full bg-muted p-0.5">
323
+ <button
324
+ type="button"
325
+ aria-label={i18n.t("attachment.viewer.previousPage")}
326
+ disabled={page <= 1}
327
+ onClick={() => goToPdfPage(-1)}
328
+ className="size-7 rounded-full hover:bg-accent disabled:opacity-40"
329
+ >
330
+ <ChevronLeft aria-hidden="true" className="mx-auto size-3.5" />
331
+ </button>
332
+ <span className="min-w-14 px-1 text-center text-xs tabular-nums">
333
+ {page} / {pageCount}
334
+ </span>
335
+ <button
336
+ type="button"
337
+ aria-label={i18n.t("attachment.viewer.nextPage")}
338
+ disabled={page >= pageCount}
339
+ onClick={() => goToPdfPage(1)}
340
+ className="size-7 rounded-full hover:bg-accent disabled:opacity-40"
341
+ >
342
+ <ChevronRight aria-hidden="true" className="mx-auto size-3.5" />
343
+ </button>
344
+ </div>
345
+ ) : null}
346
+ {searchOpen ? (
347
+ <form
348
+ className="flex min-w-0 items-center gap-1"
349
+ onSubmit={(event) => {
350
+ event.preventDefault();
351
+ void viewer.current?.searchDocument(query);
352
+ }}
353
+ >
354
+ <input
355
+ ref={searchInput}
356
+ value={query}
357
+ onChange={(event) => setQuery(event.target.value)}
358
+ placeholder={i18n.t("attachment.viewer.searchPlaceholder")}
359
+ className="h-7 w-28 min-w-0 rounded-full border bg-background px-3 text-xs outline-none focus:ring-2 focus:ring-ring sm:w-40"
360
+ />
361
+ <button
362
+ type="submit"
363
+ aria-label={i18n.t("attachment.viewer.search")}
364
+ className="grid size-7 place-items-center rounded-full hover:bg-accent"
365
+ >
366
+ <Search aria-hidden="true" className="size-3.5" />
367
+ </button>
368
+ {search && search.total > 0 ? (
369
+ <div className="flex items-center rounded-full bg-muted p-0.5">
370
+ <button
371
+ type="button"
372
+ aria-label={i18n.t("attachment.viewer.previousResult")}
373
+ disabled={search.total <= 1}
374
+ onClick={() => void viewer.current?.previousSearchResult()}
375
+ className="grid size-7 place-items-center rounded-full hover:bg-accent disabled:opacity-40"
376
+ >
377
+ <ChevronLeft aria-hidden="true" className="size-3.5" />
378
+ </button>
379
+ <span
380
+ aria-hidden="true"
381
+ className="min-w-10 px-1 text-center text-xs tabular-nums"
382
+ >
383
+ {search.currentIndex + 1} / {search.total}
384
+ </span>
385
+ <button
386
+ type="button"
387
+ aria-label={i18n.t("attachment.viewer.nextResult")}
388
+ disabled={search.total <= 1}
389
+ onClick={() => void viewer.current?.nextSearchResult()}
390
+ className="grid size-7 place-items-center rounded-full hover:bg-accent disabled:opacity-40"
391
+ >
392
+ <ChevronRight aria-hidden="true" className="size-3.5" />
393
+ </button>
394
+ </div>
395
+ ) : null}
396
+ {search?.query && search.total === 0 ? (
397
+ <span aria-hidden="true" className="min-w-10 px-1 text-center text-xs tabular-nums">
398
+ {i18n.t("attachment.viewer.searchNoResults")}
399
+ </span>
400
+ ) : null}
401
+ <button
402
+ type="button"
403
+ aria-label={i18n.t("common.close")}
404
+ onClick={() => {
405
+ setSearchOpen(false);
406
+ setQuery("");
407
+ void viewer.current?.clearDocumentSearch();
408
+ }}
409
+ className="grid size-7 place-items-center rounded-full hover:bg-accent"
410
+ >
411
+ <X aria-hidden="true" className="size-3.5" />
412
+ </button>
413
+ </form>
414
+ ) : (
415
+ <button
416
+ type="button"
417
+ aria-label={i18n.t("attachment.viewer.search")}
418
+ onClick={() => setSearchOpen(true)}
419
+ className="grid size-8 place-items-center rounded-full hover:bg-accent"
420
+ >
421
+ <Search aria-hidden="true" className="size-4" />
422
+ </button>
423
+ )}
424
+ {!searchOpen ? (
425
+ <div className="flex items-center rounded-full bg-muted p-0.5">
426
+ <button
427
+ type="button"
428
+ aria-label={i18n.t("attachment.viewer.zoomOut")}
429
+ disabled={state.zoom ? !state.zoom.canZoomOut : false}
430
+ onClick={() => {
431
+ pdfFit.current.enabled = false;
432
+ void viewer.current?.zoomOut();
433
+ }}
434
+ className="grid size-7 place-items-center rounded-full hover:bg-accent disabled:opacity-40"
435
+ >
436
+ <Minus aria-hidden="true" className="size-3.5" />
437
+ </button>
438
+ <button
439
+ type="button"
440
+ aria-label={i18n.t("attachment.viewer.resetZoom")}
441
+ disabled={state.zoom ? !state.zoom.canReset : false}
442
+ onClick={() => {
443
+ if (pdf) {
444
+ pdfFit.current.enabled = true;
445
+ schedulePdfFit.current?.();
446
+ return;
447
+ }
448
+ void viewer.current?.resetZoom();
449
+ }}
450
+ className="min-w-12 px-1 text-xs tabular-nums disabled:opacity-40"
451
+ >
452
+ {Math.round(scale * 100)}%
453
+ </button>
454
+ <button
455
+ type="button"
456
+ aria-label={i18n.t("attachment.viewer.zoomIn")}
457
+ disabled={state.zoom ? !state.zoom.canZoomIn : false}
458
+ onClick={() => {
459
+ pdfFit.current.enabled = false;
460
+ void viewer.current?.zoomIn();
461
+ }}
462
+ className="grid size-7 place-items-center rounded-full hover:bg-accent disabled:opacity-40"
463
+ >
464
+ <Plus aria-hidden="true" className="size-3.5" />
465
+ </button>
466
+ </div>
467
+ ) : null}
468
+ {pdf && !searchOpen ? (
469
+ <div className="hidden items-center sm:flex">
470
+ <button
471
+ type="button"
472
+ aria-label={i18n.t("attachment.viewer.rotateLeft")}
473
+ onClick={() =>
474
+ void applyViewState({ rotation: ((viewState?.rotation ?? 0) - 90 + 360) % 360 })
475
+ }
476
+ className="grid size-8 place-items-center rounded-full hover:bg-accent"
477
+ >
478
+ <RotateCcw aria-hidden="true" className="size-4" />
479
+ </button>
480
+ <button
481
+ type="button"
482
+ aria-label={i18n.t("attachment.viewer.rotateRight")}
483
+ onClick={() =>
484
+ void applyViewState({ rotation: ((viewState?.rotation ?? 0) + 90) % 360 })
485
+ }
486
+ className="grid size-8 place-items-center rounded-full hover:bg-accent"
487
+ >
488
+ <RotateCw aria-hidden="true" className="size-4" />
489
+ </button>
490
+ </div>
491
+ ) : null}
492
+ </div>
493
+ ) : null}
494
+ </div>
495
+ );
496
+ }
@@ -0,0 +1,28 @@
1
+ import { lazy, Suspense } from "react";
2
+ import type { AttachmentPreviewKind } from "@/attachment-detail/attachment-detail.ts";
3
+
4
+ const previews = {
5
+ pdf: lazy(async () => ({ default: (await import("./pdf-file-preview.tsx")).PdfFilePreview })),
6
+ presentation: lazy(async () => ({
7
+ default: (await import("./presentation-file-preview.tsx")).PresentationFilePreview,
8
+ })),
9
+ spreadsheet: lazy(async () => ({
10
+ default: (await import("./spreadsheet-file-preview.tsx")).SpreadsheetFilePreview,
11
+ })),
12
+ word: lazy(async () => ({ default: (await import("./word-file-preview.tsx")).WordFilePreview })),
13
+ };
14
+
15
+ export function FilePreview({
16
+ file,
17
+ kind,
18
+ }: {
19
+ file: File;
20
+ kind: Exclude<AttachmentPreviewKind, "image" | "unsupported" | "audio" | "video">;
21
+ }) {
22
+ const Preview = previews[kind];
23
+ return (
24
+ <Suspense fallback={null}>
25
+ <Preview file={file} />
26
+ </Suspense>
27
+ );
28
+ }
@@ -0,0 +1,5 @@
1
+ import { pdfRenderer } from "@file-viewer/renderer-pdf";
2
+ import { FilePreviewView } from "./file-preview-view.tsx";
3
+ export function PdfFilePreview({ file }: { file: File }) {
4
+ return <FilePreviewView file={file} renderer={pdfRenderer} pdf />;
5
+ }
@@ -0,0 +1,21 @@
1
+ import {
2
+ presentationRendererDefinition,
3
+ renderFileViewerPresentation,
4
+ } from "@file-viewer/renderer-presentation";
5
+ import { FilePreviewView } from "./file-preview-view.tsx";
6
+
7
+ const presentationRenderer = {
8
+ id: "intel-file-viewer-renderer-presentation",
9
+ label: "Intel PPTX renderer",
10
+ definitions: [presentationRendererDefinition],
11
+ handlers: [
12
+ {
13
+ rendererId: presentationRendererDefinition.id,
14
+ handler: renderFileViewerPresentation,
15
+ },
16
+ ],
17
+ };
18
+
19
+ export function PresentationFilePreview({ file }: { file: File }) {
20
+ return <FilePreviewView file={file} renderer={presentationRenderer} />;
21
+ }
@@ -0,0 +1,5 @@
1
+ import { spreadsheetRenderer } from "@file-viewer/renderer-spreadsheet";
2
+ import { FilePreviewView } from "./file-preview-view.tsx";
3
+ export function SpreadsheetFilePreview({ file }: { file: File }) {
4
+ return <FilePreviewView file={file} renderer={spreadsheetRenderer} />;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { wordRenderer } from "@file-viewer/renderer-word";
2
+ import { FilePreviewView } from "./file-preview-view.tsx";
3
+ export function WordFilePreview({ file }: { file: File }) {
4
+ return <FilePreviewView file={file} renderer={wordRenderer} />;
5
+ }