@kortix/ui 0.14.8 → 0.14.10

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 (567) hide show
  1. package/README.md +2 -2
  2. package/dist/_vendor/kortix-shared.js +116 -3
  3. package/dist/_vendor/shared/app-config.d.ts +175 -0
  4. package/dist/_vendor/shared/artifact-comments.d.ts +235 -0
  5. package/dist/_vendor/shared/channel-descriptors.d.ts +96 -0
  6. package/dist/_vendor/shared/channel-message.d.ts +45 -5
  7. package/dist/_vendor/shared/index.d.ts +2 -0
  8. package/dist/_vendor/shared/space-slug.d.ts +37 -0
  9. package/dist/access/access/access-detail-shell.js +1 -1
  10. package/dist/access/access/principal-picker.d.ts +11 -0
  11. package/dist/access/access/principal-picker.js +3 -2
  12. package/dist/apps/app-preview-pool.d.ts +7 -2
  13. package/dist/apps/app-preview-pool.js +30 -7
  14. package/dist/apps/app-shared.d.ts +22 -15
  15. package/dist/apps/app-shared.js +37 -19
  16. package/dist/apps/index.d.ts +2 -2
  17. package/dist/apps/index.js +2 -2
  18. package/dist/artifacts/artifact-frame.d.ts +16 -0
  19. package/dist/artifacts/artifact-frame.js +55 -0
  20. package/dist/artifacts/artifact-session.d.ts +18 -0
  21. package/dist/artifacts/artifact-session.js +101 -0
  22. package/dist/artifacts/frame-policy.d.ts +30 -0
  23. package/dist/artifacts/frame-policy.js +61 -0
  24. package/dist/artifacts/index.d.ts +3 -0
  25. package/dist/artifacts/index.js +5 -0
  26. package/dist/browser/sandbox-browser-chrome.d.ts +4 -1
  27. package/dist/browser/sandbox-browser-chrome.js +2 -2
  28. package/dist/customize/agents/agent-grant-pages.d.ts +5 -1
  29. package/dist/customize/agents/agent-grant-pages.js +30 -4
  30. package/dist/customize/agents/agent-page.js +3 -1
  31. package/dist/customize/agents/agent-policy-sets.d.ts +43 -0
  32. package/dist/customize/agents/agent-policy-sets.js +72 -0
  33. package/dist/customize/agents/agent-triggers-section.js +2 -1
  34. package/dist/customize/connectors/add-mcp/add-mcp-server-dialog.d.ts +6 -1
  35. package/dist/customize/connectors/add-mcp/add-mcp-server-dialog.js +10 -4
  36. package/dist/customize/connectors/agent-access.d.ts +35 -0
  37. package/dist/customize/connectors/agent-access.js +77 -0
  38. package/dist/customize/connectors/catalog/browse-sections.d.ts +2 -29
  39. package/dist/customize/connectors/catalog/browse-sections.js +7 -74
  40. package/dist/customize/connectors/catalog/catalog-entry.d.ts +18 -37
  41. package/dist/customize/connectors/catalog/catalog-entry.js +28 -40
  42. package/dist/customize/connectors/catalog/connector-browse.d.ts +3 -32
  43. package/dist/customize/connectors/catalog/connector-browse.js +19 -50
  44. package/dist/customize/connectors/catalog/use-catalog.d.ts +20 -59
  45. package/dist/customize/connectors/catalog/use-catalog.js +48 -175
  46. package/dist/customize/connectors/connector-identity.d.ts +9 -11
  47. package/dist/customize/connectors/connector-identity.js +13 -42
  48. package/dist/customize/connectors/connector-moves-banner.d.ts +2 -1
  49. package/dist/customize/connectors/connector-moves-banner.js +4 -6
  50. package/dist/customize/connectors/connector-routes.d.ts +21 -9
  51. package/dist/customize/connectors/connector-routes.js +29 -20
  52. package/dist/customize/connectors/connectors-page.js +53 -82
  53. package/dist/customize/connectors/detail/app-page.js +226 -98
  54. package/dist/customize/connectors/detail/computer-app-page.d.ts +16 -0
  55. package/dist/customize/connectors/detail/computer-app-page.js +39 -0
  56. package/dist/customize/connectors/detail/connector-accounts.js +3 -1
  57. package/dist/customize/connectors/detail/connector-advanced-tools.d.ts +4 -1
  58. package/dist/customize/connectors/detail/connector-advanced-tools.js +2 -2
  59. package/dist/customize/connectors/detail/connector-agents.d.ts +14 -0
  60. package/dist/customize/connectors/detail/connector-agents.js +69 -0
  61. package/dist/customize/connectors/detail/connector-detail-tabs.js +2 -1
  62. package/dist/customize/connectors/detail/connector-move-row.d.ts +8 -5
  63. package/dist/customize/connectors/detail/connector-move-row.js +23 -14
  64. package/dist/customize/connectors/detail/connector-page.d.ts +4 -1
  65. package/dist/customize/connectors/detail/connector-page.js +14 -10
  66. package/dist/customize/connectors/detail/connector-policy-sets.d.ts +28 -0
  67. package/dist/customize/connectors/detail/connector-policy-sets.js +203 -0
  68. package/dist/customize/connectors/detail/connector-settings.d.ts +4 -1
  69. package/dist/customize/connectors/detail/connector-settings.js +7 -3
  70. package/dist/customize/connectors/detail/connector-status.d.ts +19 -6
  71. package/dist/customize/connectors/detail/connector-status.js +34 -10
  72. package/dist/customize/connectors/detail/connector-tabs.d.ts +4 -1
  73. package/dist/customize/connectors/detail/connector-tabs.js +8 -2
  74. package/dist/customize/connectors/detail/connector-tools.d.ts +3 -3
  75. package/dist/customize/connectors/detail/connector-tools.js +105 -48
  76. package/dist/customize/connectors/detail/email-inboxes.d.ts +43 -0
  77. package/dist/customize/connectors/detail/email-inboxes.js +184 -0
  78. package/dist/customize/connectors/detail/provider-info.d.ts +1 -1
  79. package/dist/customize/connectors/detail/provider-info.js +2 -2
  80. package/dist/customize/connectors/detail/status-badge.d.ts +1 -1
  81. package/dist/customize/connectors/detail/status-badge.js +4 -5
  82. package/dist/customize/connectors/detail/use-connector-detail.d.ts +6 -0
  83. package/dist/customize/connectors/detail/use-connector-detail.js +14 -4
  84. package/dist/customize/connectors/install/install-dialog.d.ts +23 -0
  85. package/dist/customize/connectors/install/install-dialog.js +40 -0
  86. package/dist/customize/connectors/install/install.d.ts +13 -2
  87. package/dist/customize/connectors/install/install.js +9 -4
  88. package/dist/customize/connectors/install/use-install.d.ts +19 -2
  89. package/dist/customize/connectors/install/use-install.js +65 -37
  90. package/dist/customize/connectors/install/way-kind.d.ts +5 -0
  91. package/dist/customize/connectors/install/way-kind.js +16 -0
  92. package/dist/customize/connectors/provider-label.d.ts +2 -1
  93. package/dist/customize/connectors/provider-label.js +13 -3
  94. package/dist/customize/connectors/tools/pattern-rule-draft.d.ts +4 -0
  95. package/dist/customize/connectors/tools/pattern-rule-draft.js +12 -3
  96. package/dist/customize/connectors/tools/policy-sets.d.ts +27 -0
  97. package/dist/customize/connectors/tools/policy-sets.js +41 -0
  98. package/dist/customize/connectors/tools/tool-groups.d.ts +32 -9
  99. package/dist/customize/connectors/tools/tool-groups.js +78 -16
  100. package/dist/customize/connectors/tools/tool-policy-control.d.ts +11 -10
  101. package/dist/customize/connectors/tools/tool-policy-control.js +15 -16
  102. package/dist/customize/connectors/tools/tool-policy-labels.d.ts +6 -5
  103. package/dist/customize/connectors/tools/tool-policy-labels.js +17 -16
  104. package/dist/customize/connectors/tools/tool-policy.d.ts +50 -4
  105. package/dist/customize/connectors/tools/tool-policy.js +74 -8
  106. package/dist/customize/connectors/use-agent-access.d.ts +85 -0
  107. package/dist/customize/connectors/use-agent-access.js +72 -0
  108. package/dist/customize/migrate-to-v2/index.d.ts +1 -2
  109. package/dist/customize/migrate-to-v2/index.js +1 -2
  110. package/dist/customize/migrate-to-v2/migration-prompt.d.ts +12 -25
  111. package/dist/customize/migrate-to-v2/migration-prompt.js +34 -236
  112. package/dist/customize/migrate-to-v2/upgrade-defs.d.ts +1 -1
  113. package/dist/customize/migrate-to-v2/upgrade-defs.js +7 -6
  114. package/dist/customize/migrate-to-v2/upgrade-view.js +7 -7
  115. package/dist/customize/migrate-to-v2/use-run-upgrade.d.ts +3 -3
  116. package/dist/customize/providers/provider-accordion.js +17 -2
  117. package/dist/customize/sections/component/channel-access-panel.d.ts +16 -0
  118. package/dist/customize/sections/component/channel-access-panel.js +146 -0
  119. package/dist/customize/sections/component/channel-connect-hero.d.ts +3 -2
  120. package/dist/customize/sections/component/channel-connect-hero.js +3 -2
  121. package/dist/customize/sections/component/channel-hero-frame.d.ts +31 -0
  122. package/dist/customize/sections/component/channel-hero-frame.js +40 -0
  123. package/dist/customize/sections/component/channel-mocks.d.ts +6 -0
  124. package/dist/customize/sections/component/channel-mocks.js +46 -0
  125. package/dist/customize/sections/component/channel-row.d.ts +13 -3
  126. package/dist/customize/sections/component/channel-row.js +10 -4
  127. package/dist/customize/sections/component/email-connect-form.d.ts +27 -0
  128. package/dist/customize/sections/component/email-connect-form.js +183 -0
  129. package/dist/customize/sections/component/slack-connect-card.d.ts +7 -0
  130. package/dist/customize/sections/component/slack-connect-card.js +2 -3
  131. package/dist/customize/sections/component/teams-connect-card.d.ts +7 -0
  132. package/dist/customize/sections/component/teams-connect-card.js +2 -3
  133. package/dist/customize/sections/component/telegram-channel-row.js +21 -28
  134. package/dist/customize/sections/connectors-view.d.ts +1 -15
  135. package/dist/customize/sections/connectors-view.js +81 -314
  136. package/dist/customize/sections/view/agent-editor-catalog.js +10 -0
  137. package/dist/customize/sections/view/channels-view.d.ts +6 -1
  138. package/dist/customize/sections/view/channels-view.js +72 -81
  139. package/dist/customize/sections/view/gateway/gateway-overview.js +10 -2
  140. package/dist/customize/settings/rail.js +9 -0
  141. package/dist/customize/settings/settings-tabs.d.ts +1 -1
  142. package/dist/customize/settings/settings-tabs.js +1 -0
  143. package/dist/customize/settings/tabs/chrome-connections-tab.d.ts +6 -0
  144. package/dist/customize/settings/tabs/chrome-connections-tab.js +99 -0
  145. package/dist/customize/settings/tabs/experimental-tab.js +35 -16
  146. package/dist/customize/shared/catalog/catalog-empty-state.d.ts +4 -15
  147. package/dist/customize/shared/catalog/catalog-empty-state.js +9 -7
  148. package/dist/customize/shared/channels-switch-dialog.d.ts +18 -0
  149. package/dist/customize/shared/channels-switch-dialog.js +30 -0
  150. package/dist/customize/shared/feature-gate-screen.js +27 -9
  151. package/dist/customize/shared/use-customize-prefetch.d.ts +2 -3
  152. package/dist/customize/shared/use-customize-prefetch.js +9 -27
  153. package/dist/customize/triggers/schedule/post-to-field.d.ts +18 -0
  154. package/dist/customize/triggers/schedule/post-to-field.js +49 -0
  155. package/dist/customize/triggers/schedule/schedule-copy.d.ts +10 -6
  156. package/dist/customize/triggers/schedule/schedule-copy.js +42 -11
  157. package/dist/customize/triggers/schedule/schedule-detail-sheet.js +2 -1
  158. package/dist/customize/triggers/schedule/trigger-composer-logic.d.ts +22 -0
  159. package/dist/customize/triggers/schedule/trigger-composer-logic.js +25 -0
  160. package/dist/customize/triggers/schedule/trigger-composer.d.ts +5 -0
  161. package/dist/customize/triggers/schedule/trigger-composer.js +23 -14
  162. package/dist/customize/triggers/schedule/trigger-edit-logic.js +3 -0
  163. package/dist/customize/triggers/schedule/trigger-filter.js +1 -1
  164. package/dist/customize/triggers/schedule/trigger-runs-logic.d.ts +2 -2
  165. package/dist/customize/triggers/schedule/trigger-runs-panel.js +1 -0
  166. package/dist/customize/triggers/schedule/trigger-toast.js +2 -1
  167. package/dist/customize/triggers/schedule-view.js +5 -2
  168. package/dist/files/index.d.ts +1 -0
  169. package/dist/files/index.js +1 -0
  170. package/dist/files/project/change-request-recovery.d.ts +1 -26
  171. package/dist/files/project/change-request-recovery.js +1 -59
  172. package/dist/files/project/components/change-request-detail-dialog.js +13 -3
  173. package/dist/files/project/components/drive-explorer.js +15 -15
  174. package/dist/files/project/components/drive-toolbar.d.ts +16 -2
  175. package/dist/files/project/components/drive-toolbar.js +14 -5
  176. package/dist/files/project/hooks/use-change-request-recovery.js +3 -19
  177. package/dist/files/project/hooks/use-change-requests.d.ts +1 -0
  178. package/dist/files/project/hooks/use-change-requests.js +11 -2
  179. package/dist/files/renderers/image-frame.d.ts +48 -0
  180. package/dist/files/renderers/image-frame.js +59 -0
  181. package/dist/files/renderers/image-renderer.d.ts +11 -1
  182. package/dist/files/renderers/image-renderer.js +13 -3
  183. package/dist/files/renderers/pdf/pdf-clipboard.d.ts +2 -0
  184. package/dist/files/renderers/pdf/pdf-clipboard.js +21 -0
  185. package/dist/files/renderers/pdf/pdf-viewer.js +4 -2
  186. package/dist/files/renderers/show-content-renderer.d.ts +9 -1
  187. package/dist/files/renderers/show-content-renderer.js +56 -14
  188. package/dist/files/runtime/file-source.js +2 -0
  189. package/dist/files/runtime/hooks/use-binary-blob.d.ts +10 -1
  190. package/dist/files/runtime/hooks/use-binary-blob.js +10 -4
  191. package/dist/files/runtime/hooks/use-shared-image.d.ts +41 -0
  192. package/dist/files/runtime/hooks/use-shared-image.js +177 -0
  193. package/dist/files/viewer/file-content-renderer.d.ts +22 -1
  194. package/dist/files/viewer/file-content-renderer.js +50 -7
  195. package/dist/files/viewer/file-header.d.ts +7 -3
  196. package/dist/files/viewer/file-header.js +20 -11
  197. package/dist/files/viewer/file-preview-modal.d.ts +1 -1
  198. package/dist/files/viewer/file-preview-modal.js +10 -22
  199. package/dist/files/viewer/file-source.d.ts +13 -0
  200. package/dist/files/viewer/use-file-content-state.d.ts +2 -0
  201. package/dist/files/viewer/use-file-content-state.js +28 -10
  202. package/dist/files/viewer/viewer-actions.d.ts +3 -1
  203. package/dist/files/viewer/viewer-actions.js +2 -2
  204. package/dist/hooks/use-media-query.d.ts +1 -0
  205. package/dist/hooks/use-mobile.d.ts +5 -0
  206. package/dist/hooks/use-mobile.js +8 -0
  207. package/dist/i18n/en.d.ts +1062 -85
  208. package/dist/i18n/en.js +1078 -101
  209. package/dist/i18n/ui-catalog/localize-ui-catalog.js +4 -0
  210. package/dist/i18n/ui-catalog/remaining-ui-translation-keys.generated.js +15 -3
  211. package/dist/index.d.ts +2 -2
  212. package/dist/index.js +2 -2
  213. package/dist/internal/account-hub/account-settings-sidebar.js +1 -1
  214. package/dist/internal/account-hub/hub-sheet.d.ts +9 -1
  215. package/dist/internal/account-hub/hub-sheet.js +21 -5
  216. package/dist/internal/account-hub/sections.d.ts +1 -1
  217. package/dist/internal/account-hub/sections.js +2 -1
  218. package/dist/internal/apps-and-extensions/apps-modal.d.ts +9 -0
  219. package/dist/internal/apps-and-extensions/apps-modal.js +107 -0
  220. package/dist/internal/auth/auth-card-shell.js +3 -3
  221. package/dist/internal/chrome/browser-icon.d.ts +24 -0
  222. package/dist/internal/chrome/browser-icon.js +58 -0
  223. package/dist/internal/chrome/chrome-card-state.d.ts +16 -0
  224. package/dist/internal/chrome/chrome-card-state.js +9 -0
  225. package/dist/internal/chrome/chrome-connect.d.ts +69 -0
  226. package/dist/internal/chrome/chrome-connect.js +178 -0
  227. package/dist/internal/chrome/chrome-mock.d.ts +4 -0
  228. package/dist/internal/chrome/chrome-mock.js +15 -0
  229. package/dist/internal/chrome/chrome-names.d.ts +15 -0
  230. package/dist/internal/chrome/chrome-names.js +35 -0
  231. package/dist/internal/chrome/extension.d.ts +47 -0
  232. package/dist/internal/chrome/extension.js +91 -0
  233. package/dist/internal/chrome/use-chrome-offered.d.ts +11 -0
  234. package/dist/internal/chrome/use-chrome-offered.js +22 -0
  235. package/dist/internal/components/agent-mark.js +5 -2
  236. package/dist/internal/connector-logo/connector-logo-source.d.ts +64 -0
  237. package/dist/internal/connector-logo/connector-logo-source.js +49 -0
  238. package/dist/internal/connector-logo/connector-logo.d.ts +39 -0
  239. package/dist/internal/connector-logo/connector-logo.js +56 -0
  240. package/dist/internal/connector-logo/connector-mark-files.d.ts +11 -0
  241. package/dist/internal/connector-logo/connector-mark-files.js +35 -0
  242. package/dist/internal/files/file-read-retry.d.ts +13 -0
  243. package/dist/internal/files/file-read-retry.js +23 -3
  244. package/dist/internal/files/session-drive-files.d.ts +5 -1
  245. package/dist/internal/files/session-drive-files.js +7 -2
  246. package/dist/internal/hooks/channels/use-channels-installations.d.ts +32 -5
  247. package/dist/internal/hooks/channels/use-channels-installations.js +55 -7
  248. package/dist/internal/hooks/channels/use-email-inboxes.d.ts +19 -0
  249. package/dist/internal/hooks/channels/use-email-inboxes.js +48 -0
  250. package/dist/internal/hooks/connectors/use-add-account.js +1 -1
  251. package/dist/internal/hooks/index.d.ts +1 -1
  252. package/dist/internal/hooks/index.js +1 -1
  253. package/dist/internal/hooks/projects/use-new-project-session.d.ts +9 -0
  254. package/dist/internal/hooks/projects/use-new-project-session.js +28 -12
  255. package/dist/internal/hooks/use-auto-scroll.d.ts +8 -1
  256. package/dist/internal/hooks/use-auto-scroll.js +4 -4
  257. package/dist/internal/icons/edge.d.ts +5 -0
  258. package/dist/internal/icons/edge.js +10 -0
  259. package/dist/internal/icons/telegram.d.ts +5 -0
  260. package/dist/internal/icons/telegram.js +6 -1
  261. package/dist/internal/layout/nav-hover-card.d.ts +35 -0
  262. package/dist/internal/layout/nav-hover-card.js +84 -0
  263. package/dist/internal/layout/project-nav-row.d.ts +3 -1
  264. package/dist/internal/layout/project-nav-row.js +3 -2
  265. package/dist/internal/layout/project-page-header.d.ts +4 -2
  266. package/dist/internal/layout/project-page-header.js +4 -3
  267. package/dist/internal/layout/project-settings-nav.js +59 -2
  268. package/dist/internal/lib/browser-error-noise.d.ts +1 -1
  269. package/dist/internal/lib/browser-error-noise.js +1 -1
  270. package/dist/internal/lib/browser-noise/rules/extensions.js +17 -3
  271. package/dist/internal/lib/browser-noise/rules/injected-scripts.d.ts +7 -0
  272. package/dist/internal/lib/browser-noise/rules/injected-scripts.js +15 -0
  273. package/dist/internal/lib/detect-os.d.ts +43 -0
  274. package/dist/internal/lib/detect-os.js +70 -0
  275. package/dist/internal/lib/manifest-version.d.ts +1 -1
  276. package/dist/internal/lib/manifest-version.js +2 -2
  277. package/dist/internal/lib/memory-href.d.ts +16 -0
  278. package/dist/internal/lib/memory-href.js +46 -0
  279. package/dist/internal/lib/use-project-feature-flags.js +13 -9
  280. package/dist/internal/lib/utils/heic-convert.d.ts +1 -1
  281. package/dist/internal/lib/utils/heic-convert.js +3 -4
  282. package/dist/internal/lib/web-notifications.d.ts +20 -0
  283. package/dist/internal/lib/web-notifications.js +48 -4
  284. package/dist/internal/notifications/notification-bell.d.ts +4 -1
  285. package/dist/internal/notifications/notification-bell.js +2 -2
  286. package/dist/internal/selection/selection-bar.d.ts +2 -1
  287. package/dist/internal/selection/selection-bar.js +1 -1
  288. package/dist/internal/stores/apps-modal-store.d.ts +8 -0
  289. package/dist/internal/stores/apps-modal-store.js +6 -0
  290. package/dist/internal/stores/composer-prefill-store.d.ts +12 -5
  291. package/dist/internal/stores/file-preview-store.d.ts +29 -2
  292. package/dist/internal/stores/file-preview-store.js +19 -1
  293. package/dist/internal/stores/kortix-computer-store.js +3 -0
  294. package/dist/internal/stores/sidebar-nav-store.d.ts +1 -1
  295. package/dist/internal/stores/sidebar-nav-store.js +3 -1
  296. package/dist/lib/routes.d.ts +13 -1
  297. package/dist/lib/routes.js +13 -0
  298. package/dist/markdown/index.d.ts +2 -1
  299. package/dist/markdown/index.js +2 -1
  300. package/dist/markdown/setup-links/approval-decision.d.ts +1 -0
  301. package/dist/markdown/setup-links/approval-decision.js +9 -3
  302. package/dist/markdown/setup-links/connector-handshake.d.ts +3 -1
  303. package/dist/markdown/setup-links/connector-handshake.js +12 -10
  304. package/dist/markdown/setup-links/secret-intake-form.d.ts +0 -10
  305. package/dist/markdown/setup-links/secret-intake-form.js +1 -1
  306. package/dist/markdown/setup-links/show-connect-hero.d.ts +28 -0
  307. package/dist/markdown/setup-links/show-connect-hero.js +91 -0
  308. package/dist/markdown/unified-markdown.js +5 -5
  309. package/dist/primitives/badge.d.ts +1 -1
  310. package/dist/primitives/badge.js +2 -0
  311. package/dist/primitives/disclosure.js +14 -2
  312. package/dist/primitives/empty-art.d.ts +12 -0
  313. package/dist/primitives/empty-art.js +11 -0
  314. package/dist/primitives/empty-state.d.ts +2 -2
  315. package/dist/primitives/empty-state.js +6 -5
  316. package/dist/primitives/mermaid-renderer.d.ts +2 -0
  317. package/dist/primitives/mermaid-renderer.js +21 -15
  318. package/dist/primitives/resizable.js +6 -1
  319. package/dist/primitives/resize-seam.d.ts +11 -0
  320. package/dist/primitives/resize-seam.js +12 -0
  321. package/dist/primitives/sidebar-panel.js +4 -7
  322. package/dist/primitives/tabs.d.ts +14 -1
  323. package/dist/primitives/tabs.js +1 -1
  324. package/dist/providers/ui-provider.d.ts +9 -0
  325. package/dist/session/composer/ComposerAboveCard.d.ts +1 -1
  326. package/dist/session/composer/ComposerAboveCard.js +2 -2
  327. package/dist/session/composer/ComposerCard.d.ts +1 -1
  328. package/dist/session/composer/ComposerCard.js +3 -3
  329. package/dist/session/composer/agent-selector.d.ts +13 -1
  330. package/dist/session/composer/agent-selector.js +25 -5
  331. package/dist/session/composer/composer-agent-access.d.ts +15 -0
  332. package/dist/session/composer/composer-agent-access.js +27 -0
  333. package/dist/session/composer/composer-chat-input.d.ts +8 -2
  334. package/dist/session/composer/composer-chat-input.js +8 -6
  335. package/dist/session/composer/composer-logic.d.ts +16 -0
  336. package/dist/session/composer/composer-logic.js +12 -0
  337. package/dist/session/composer/composer-underbar.d.ts +3 -1
  338. package/dist/session/composer/composer-underbar.js +4 -3
  339. package/dist/session/composer/composer.d.ts +15 -1
  340. package/dist/session/composer/composer.js +19 -1
  341. package/dist/session/composer/draft/composer-draft.d.ts +6 -2
  342. package/dist/session/composer/draft/composer-draft.js +5 -1
  343. package/dist/session/composer/reasoning-effort-selector.js +1 -1
  344. package/dist/session/models/chatgpt-connection-button.d.ts +11 -0
  345. package/dist/session/models/chatgpt-connection-button.js +54 -0
  346. package/dist/session/models/model-selector.js +5 -2
  347. package/dist/session/models/use-model-connection-gate.d.ts +5 -5
  348. package/dist/session/models/use-model-connection-gate.js +5 -5
  349. package/dist/session/outcomes/jump-to-outcome.d.ts +10 -0
  350. package/dist/session/outcomes/jump-to-outcome.js +11 -0
  351. package/dist/session/outcomes/session-outcomes-bridge.d.ts +14 -0
  352. package/dist/session/outcomes/session-outcomes-bridge.js +12 -0
  353. package/dist/session/outcomes/session-outcomes-provider.d.ts +3 -1
  354. package/dist/session/outcomes/session-outcomes-provider.js +33 -3
  355. package/dist/session/panel/easy/app-preview.d.ts +10 -0
  356. package/dist/session/panel/easy/app-preview.js +31 -12
  357. package/dist/session/panel/easy/change-requests-card.d.ts +7 -0
  358. package/dist/session/panel/easy/change-requests-card.js +17 -0
  359. package/dist/session/panel/easy/easy-panel-logic.js +7 -1
  360. package/dist/session/panel/easy/easy-panel.js +5 -2
  361. package/dist/session/panel/easy/file-preview.js +1 -1
  362. package/dist/session/panel/easy/outputs-card.d.ts +2 -1
  363. package/dist/session/panel/easy/outputs-card.js +3 -2
  364. package/dist/session/panel/mobile-tool-drawer.js +7 -1
  365. package/dist/session/panel/session-audit-panel.js +9 -2
  366. package/dist/session/panel/session-audit-shared.js +3 -1
  367. package/dist/session/panel/session-detail-panel.js +9 -3
  368. package/dist/session/panel/session-files-explorer.d.ts +9 -0
  369. package/dist/session/panel/session-files-explorer.js +12 -3
  370. package/dist/session/panel/session-panel-provider.d.ts +15 -1
  371. package/dist/session/panel/session-panel-provider.js +32 -7
  372. package/dist/session/panel/shared/derive-panels.d.ts +8 -1
  373. package/dist/session/panel/shared/derive-panels.js +12 -2
  374. package/dist/session/panel/shared/output-priority.js +2 -0
  375. package/dist/session/participants/session-participants.d.ts +8 -2
  376. package/dist/session/participants/session-participants.js +11 -4
  377. package/dist/session/prompts/approval-request.d.ts +5 -0
  378. package/dist/session/prompts/approval-request.js +9 -1
  379. package/dist/session/prompts/session-approval-review.d.ts +9 -0
  380. package/dist/session/prompts/session-approval-review.js +18 -0
  381. package/dist/session/status/session-error-banner.js +2 -49
  382. package/dist/session/status/session-starting-loader.d.ts +14 -2
  383. package/dist/session/status/session-starting-loader.js +16 -4
  384. package/dist/session/thread/after-first-prompt.d.ts +7 -0
  385. package/dist/session/thread/after-first-prompt.js +8 -0
  386. package/dist/session/thread/detect-command.d.ts +12 -0
  387. package/dist/session/thread/detect-command.js +20 -0
  388. package/dist/session/thread/session-chat.d.ts +3 -1
  389. package/dist/session/thread/session-chat.js +377 -252
  390. package/dist/session/thread/session-thread.d.ts +15 -1
  391. package/dist/session/thread/session-thread.js +20 -3
  392. package/dist/session/thread/session-transcript-class.d.ts +5 -1
  393. package/dist/session/thread/session-transcript-class.js +5 -1
  394. package/dist/session/thread/thread-boot-rows.d.ts +36 -0
  395. package/dist/session/thread/thread-boot-rows.js +89 -0
  396. package/dist/session/thread/transcript-rows.d.ts +31 -0
  397. package/dist/session/thread/transcript-rows.js +3 -0
  398. package/dist/session/thread/transcript.d.ts +0 -9
  399. package/dist/session/thread/transcript.js +104 -19
  400. package/dist/session/tool/shared/infrastructure-contexts.d.ts +28 -0
  401. package/dist/session/tool/shared/infrastructure-contexts.js +31 -3
  402. package/dist/session/tool/tool-part-renderer.d.ts +5 -4
  403. package/dist/session/tool/tool-part-renderer.js +8 -3
  404. package/dist/session/tool/tools/bash-tool.js +7 -6
  405. package/dist/session/tool/tools/channel-send-card.d.ts +1 -2
  406. package/dist/session/tool/tools/channel-send.d.ts +2 -1
  407. package/dist/session/tool/tools/codemode-tool.js +1 -1
  408. package/dist/session/tool/tools/connector-tools.js +1 -1
  409. package/dist/session/tool/tools/image-gen-tool.d.ts +1 -1
  410. package/dist/session/tool/tools/image-gen-tool.js +241 -53
  411. package/dist/session/tool/tools/show-tool.js +19 -8
  412. package/dist/session/turn/channel-brand.d.ts +8 -3
  413. package/dist/session/turn/channel-brand.js +20 -4
  414. package/dist/session/turn/message-author.d.ts +8 -0
  415. package/dist/session/turn/message-author.js +24 -0
  416. package/dist/session/turn/message-row.d.ts +52 -0
  417. package/dist/session/turn/message-row.js +44 -0
  418. package/dist/session/turn/plan-anchor.d.ts +1 -1
  419. package/dist/session/turn/plan-anchor.js +1 -1
  420. package/dist/session/turn/user-message.d.ts +14 -6
  421. package/dist/session/turn/user-message.js +35 -15
  422. package/dist/styles.css +1 -1
  423. package/dist/workspace/home/access-requests-bell.d.ts +9 -2
  424. package/dist/workspace/home/access-requests-bell.js +20 -6
  425. package/dist/workspace/home/first-chat.d.ts +8 -21
  426. package/dist/workspace/home/first-chat.js +22 -72
  427. package/dist/workspace/index.d.ts +6 -0
  428. package/dist/workspace/index.js +6 -0
  429. package/dist/workspace/layout/project-home.d.ts +14 -2
  430. package/dist/workspace/layout/project-home.js +60 -21
  431. package/dist/workspace/layout/use-project-home-send.d.ts +9 -1
  432. package/dist/workspace/layout/use-project-home-send.js +82 -3
  433. package/dist/workspace/memory/body-doc.d.ts +17 -0
  434. package/dist/workspace/memory/body-doc.js +208 -0
  435. package/dist/workspace/memory/body-extensions.d.ts +38 -0
  436. package/dist/workspace/memory/body-extensions.js +103 -0
  437. package/dist/workspace/memory/company-memory-page.d.ts +8 -0
  438. package/dist/workspace/memory/company-memory-page.js +16 -0
  439. package/dist/workspace/memory/memory-all.d.ts +20 -0
  440. package/dist/workspace/memory/memory-all.js +27 -0
  441. package/dist/workspace/memory/memory-body.d.ts +19 -0
  442. package/dist/workspace/memory/memory-body.js +139 -0
  443. package/dist/workspace/memory/memory-empty.d.ts +13 -0
  444. package/dist/workspace/memory/memory-empty.js +28 -0
  445. package/dist/workspace/memory/memory-history.d.ts +27 -0
  446. package/dist/workspace/memory/memory-history.js +108 -0
  447. package/dist/workspace/memory/memory-labels.d.ts +16 -0
  448. package/dist/workspace/memory/memory-labels.js +47 -0
  449. package/dist/workspace/memory/memory-page-list.d.ts +34 -0
  450. package/dist/workspace/memory/memory-page-list.js +156 -0
  451. package/dist/workspace/memory/memory-reader.d.ts +58 -0
  452. package/dist/workspace/memory/memory-reader.js +123 -0
  453. package/dist/workspace/memory/memory-view.d.ts +21 -0
  454. package/dist/workspace/memory/memory-view.js +124 -0
  455. package/dist/workspace/memory/remember-popover.d.ts +19 -0
  456. package/dist/workspace/memory/remember-popover.js +61 -0
  457. package/dist/workspace/memory/remember-this.d.ts +12 -0
  458. package/dist/workspace/memory/remember-this.js +89 -0
  459. package/dist/workspace/memory/source-mark.d.ts +18 -0
  460. package/dist/workspace/memory/source-mark.js +79 -0
  461. package/dist/workspace/memory/used-memory-chip.d.ts +14 -0
  462. package/dist/workspace/memory/used-memory-chip.js +61 -0
  463. package/dist/workspace/new/advanced-fields.d.ts +2 -1
  464. package/dist/workspace/new/advanced-fields.js +2 -2
  465. package/dist/workspace/new/source-param.d.ts +1 -1
  466. package/dist/workspace/new/source-param.js +3 -1
  467. package/dist/workspace/new/use-create-workspace.d.ts +8 -10
  468. package/dist/workspace/new/use-create-workspace.js +3 -3
  469. package/dist/workspace/projects/onboarding/app-logo.d.ts +7 -2
  470. package/dist/workspace/projects/onboarding/app-logo.js +6 -13
  471. package/dist/workspace/projects/policies-panel.js +5 -3
  472. package/dist/workspace/review/review-actions.d.ts +4 -2
  473. package/dist/workspace/review/review-actions.js +8 -2
  474. package/dist/workspace/review/review-center.js +11 -7
  475. package/dist/workspace/review/review-detail.d.ts +10 -0
  476. package/dist/workspace/review/review-detail.js +101 -13
  477. package/dist/workspace/saved-session-skeleton.d.ts +2 -2
  478. package/dist/workspace/saved-session-skeleton.js +3 -3
  479. package/dist/workspace/session-workspace.js +15 -13
  480. package/dist/workspace/sessions/project-sessions-view.js +3 -2
  481. package/dist/workspace/sessions/session-label-badges.js +2 -1
  482. package/dist/workspace/sessions/sessions-toolbar.d.ts +3 -1
  483. package/dist/workspace/sessions/sessions-toolbar.js +2 -2
  484. package/dist/workspace/sidebar/footer/project-apps-nav.d.ts +5 -0
  485. package/dist/workspace/sidebar/footer/project-apps-nav.js +41 -2
  486. package/dist/workspace/sidebar/footer/project-artifacts-nav.d.ts +6 -0
  487. package/dist/workspace/sidebar/footer/project-artifacts-nav.js +22 -0
  488. package/dist/workspace/sidebar/footer/project-chrome-nav.d.ts +10 -0
  489. package/dist/workspace/sidebar/footer/project-chrome-nav.js +56 -0
  490. package/dist/workspace/sidebar/footer/project-manifest-upgrade-alert.js +13 -13
  491. package/dist/workspace/sidebar/footer/project-memory-nav.d.ts +7 -0
  492. package/dist/workspace/sidebar/footer/project-memory-nav.js +19 -0
  493. package/dist/workspace/sidebar/nav-row-meta.js +14 -2
  494. package/dist/workspace/sidebar/project-nav-items.js +3 -0
  495. package/dist/workspace/sidebar/project-session-list.js +68 -56
  496. package/dist/workspace/sidebar/project-sidebar.js +13 -2
  497. package/dist/workspace/sidebar/project-spaces-nav.d.ts +8 -0
  498. package/dist/workspace/sidebar/project-spaces-nav.js +69 -0
  499. package/dist/workspace/sidebar/session-group-order.d.ts +9 -0
  500. package/dist/workspace/sidebar/session-group-order.js +25 -0
  501. package/dist/workspace/sidebar/session-group-sections.d.ts +62 -0
  502. package/dist/workspace/sidebar/session-group-sections.js +267 -0
  503. package/dist/workspace/sidebar/session-list-chrome.d.ts +28 -0
  504. package/dist/workspace/sidebar/session-list-chrome.js +50 -0
  505. package/dist/workspace/spaces/channel-glyph.d.ts +5 -0
  506. package/dist/workspace/spaces/channel-glyph.js +6 -0
  507. package/dist/workspace/spaces/channel-setup.d.ts +16 -0
  508. package/dist/workspace/spaces/channel-setup.js +105 -0
  509. package/dist/workspace/spaces/channels-onboarding.d.ts +13 -0
  510. package/dist/workspace/spaces/channels-onboarding.js +37 -0
  511. package/dist/workspace/spaces/create-channel-dialog.d.ts +16 -0
  512. package/dist/workspace/spaces/create-channel-dialog.js +158 -0
  513. package/dist/workspace/spaces/private-channels-not-in.d.ts +9 -0
  514. package/dist/workspace/spaces/private-channels-not-in.js +69 -0
  515. package/dist/workspace/spaces/space-agents.d.ts +10 -0
  516. package/dist/workspace/spaces/space-agents.js +10 -0
  517. package/dist/workspace/spaces/space-details-dialog.d.ts +20 -0
  518. package/dist/workspace/spaces/space-details-dialog.js +176 -0
  519. package/dist/workspace/spaces/space-link-label.d.ts +49 -0
  520. package/dist/workspace/spaces/space-link-label.js +73 -0
  521. package/dist/workspace/spaces/space-links.d.ts +19 -0
  522. package/dist/workspace/spaces/space-links.js +230 -0
  523. package/dist/workspace/spaces/space-members.d.ts +46 -0
  524. package/dist/workspace/spaces/space-members.js +182 -0
  525. package/dist/workspace/spaces/space-roster.d.ts +15 -0
  526. package/dist/workspace/spaces/space-roster.js +44 -0
  527. package/dist/workspace/spaces/space-threads-tab.d.ts +56 -0
  528. package/dist/workspace/spaces/space-threads-tab.js +156 -0
  529. package/dist/workspace/spaces/space-timeline-view.d.ts +254 -0
  530. package/dist/workspace/spaces/space-timeline-view.js +235 -0
  531. package/dist/workspace/spaces/space-timeline.d.ts +106 -0
  532. package/dist/workspace/spaces/space-timeline.js +183 -0
  533. package/dist/workspace/spaces/space-triggers.d.ts +13 -0
  534. package/dist/workspace/spaces/space-triggers.js +78 -0
  535. package/dist/workspace/spaces/space-view.d.ts +37 -0
  536. package/dist/workspace/spaces/space-view.js +436 -0
  537. package/package.json +67 -36
  538. package/skills/kortix-ui/SKILL.md +3 -3
  539. package/skills/kortix-ui/references/custom-dashboard.md +3 -3
  540. package/skills/kortix-ui/references/modules.md +29 -5
  541. package/skills/kortix-ui/references/theming.md +1 -1
  542. package/dist/customize/connectors/catalog/merge-search.d.ts +0 -10
  543. package/dist/customize/connectors/catalog/merge-search.js +0 -26
  544. package/dist/customize/connectors/detail/find-easy-connect-app.d.ts +0 -29
  545. package/dist/customize/connectors/detail/find-easy-connect-app.js +0 -22
  546. package/dist/customize/migrate-to-v2/use-migrate-to-v2.d.ts +0 -9
  547. package/dist/customize/migrate-to-v2/use-migrate-to-v2.js +0 -24
  548. package/dist/customize/sections/connector-connection-header.d.ts +0 -36
  549. package/dist/customize/sections/connector-connection-header.js +0 -33
  550. package/dist/customize/sections/connector-connection-modal.d.ts +0 -34
  551. package/dist/customize/sections/connector-connection-modal.js +0 -72
  552. package/dist/customize/sections/discover-catalogue-card.d.ts +0 -19
  553. package/dist/customize/sections/discover-catalogue-card.js +0 -26
  554. package/dist/customize/sections/discover-catalogue-detail.d.ts +0 -15
  555. package/dist/customize/sections/discover-catalogue-detail.js +0 -24
  556. package/dist/customize/sections/discover-catalogue.d.ts +0 -17
  557. package/dist/customize/sections/discover-catalogue.js +0 -132
  558. package/dist/customize/sections/llm-provider/provider-access-menu.d.ts +0 -10
  559. package/dist/customize/sections/llm-provider/provider-access-menu.js +0 -23
  560. package/dist/files/project/components/drive-empty-art.d.ts +0 -2
  561. package/dist/files/project/components/drive-empty-art.js +0 -5
  562. package/dist/internal/hooks/use-media-query.d.ts +0 -2
  563. package/dist/internal/share/public-share-link-button.d.ts +0 -14
  564. package/dist/internal/share/public-share-link-button.js +0 -17
  565. package/dist/markdown/setup-links/app-credentials-form.d.ts +0 -12
  566. package/dist/markdown/setup-links/app-credentials-form.js +0 -115
  567. /package/dist/{internal/hooks → hooks}/use-media-query.js +0 -0
@@ -0,0 +1,177 @@
1
+ 'use client';
2
+ import { createImageCache, isRuntimeStartingError, isSessionAttachmentRef } from '@kortix/sdk';
3
+ import { binaryBlobKeys, useRuntimeStore } from '@kortix/sdk/react';
4
+ import { useQueryClient } from '@tanstack/react-query';
5
+ import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react';
6
+ import { useCurrentAccountStore } from '../../../internal/stores/current-account-store.js';
7
+ import { useBinaryBlob } from './use-binary-blob.js';
8
+ /**
9
+ * The page's shared images (R38). Two stores, one per key:
10
+ *
11
+ * - **By path: React Query's blob cache** (`useBinaryBlob`, `binaryBlobKeys`).
12
+ * The image card, the full-screen viewer, a `show` card and a Files preview
13
+ * read ONE query per sandbox file. Every invalidation that already exists
14
+ * reaches them all: `file.edited`, the turn end, the show card's Refresh and
15
+ * the viewer's Refresh refetch it, and a file rewritten in place draws its
16
+ * new bytes everywhere (`keepBlobIfUnchanged` keeps unchanged bytes the same
17
+ * Blob, so nothing reloads for them).
18
+ * - **By sha256: `byDigest`.** Bytes whose digest the tool output named, after
19
+ * the digest is checked against the bytes. An image already loaded under one
20
+ * path draws at once under another path with the same bytes (a Drive copy),
21
+ * with no read. Content-addressed and verified, so it never goes stale.
22
+ * Evicted least recently used past 64 images or 256 MiB.
23
+ *
24
+ * One object URL per Blob, shared by every surface that draws it, so a second
25
+ * surface never decodes the image again. It is revoked about 1 s after the last
26
+ * surface lets it go.
27
+ *
28
+ * Bound: an image on screen is never evicted. Its Blob stays while a surface
29
+ * draws it, so memory is the images mounted at once plus `byDigest`'s 256 MiB.
30
+ * This is the same as before R38, when each mount held its own blob URL.
31
+ */
32
+ const byDigest = createImageCache({ maxEntries: 64, maxBytes: 256 * 1024 * 1024 });
33
+ // One object URL per Blob. Revoked a moment after its last release, so a
34
+ // StrictMode remount (release, then acquire in the same tick) and a render that
35
+ // never commits keep a live URL instead of a revoked one.
36
+ const urls = new Map();
37
+ const naturalRatios = new WeakMap();
38
+ const REVOKE_GRACE_MS = 1000;
39
+ function urlFor(blob) {
40
+ let held = urls.get(blob);
41
+ if (!held) {
42
+ held = { url: URL.createObjectURL(blob), refs: 0 };
43
+ urls.set(blob, held);
44
+ scheduleRevoke(blob);
45
+ }
46
+ return held.url;
47
+ }
48
+ function scheduleRevoke(blob) {
49
+ setTimeout(() => {
50
+ const held = urls.get(blob);
51
+ if (!held || held.refs > 0)
52
+ return;
53
+ URL.revokeObjectURL(held.url);
54
+ urls.delete(blob);
55
+ }, REVOKE_GRACE_MS);
56
+ }
57
+ /**
58
+ * Drops every shared image: the digests, and the URL of every image no surface
59
+ * draws (the rest go when their surface unmounts). Runs on a sign-out and an
60
+ * account switch.
61
+ */
62
+ export function clearSharedImages() {
63
+ byDigest.clear();
64
+ for (const [blob, held] of urls) {
65
+ if (held.refs > 0)
66
+ continue;
67
+ URL.revokeObjectURL(held.url);
68
+ urls.delete(blob);
69
+ }
70
+ }
71
+ // A sign-out clears the selected account; an account switch changes it.
72
+ useCurrentAccountStore.subscribe((state, previous) => {
73
+ if (state.selectedAccountId !== previous.selectedAccountId)
74
+ clearSharedImages();
75
+ });
76
+ /** The sha256 of each Blob read and hashed. */
77
+ const blobDigests = new WeakMap();
78
+ /** The bytes a digest disagreed with, read again once each (never a loop). */
79
+ const rereadOnce = new WeakSet();
80
+ /** `useBinaryBlob`'s query key for `path`. */
81
+ function blobKey(serverUrl, path) {
82
+ return isSessionAttachmentRef(path) ? ['session-attachment', 'blob', path] : binaryBlobKeys.file(serverUrl, path);
83
+ }
84
+ /** The lowercase hex sha256 of `blob`, or null when it cannot be computed (no Web Crypto, over 32 MiB). */
85
+ async function digestOf(blob) {
86
+ if (blob.size > 32 * 1024 * 1024 || !globalThis.crypto?.subtle)
87
+ return null;
88
+ const hash = await globalThis.crypto.subtle.digest('SHA-256', await blob.arrayBuffer());
89
+ return [...new Uint8Array(hash)].map((byte) => byte.toString(16).padStart(2, '0')).join('');
90
+ }
91
+ /**
92
+ * The image at `path` (a sandbox path or a stored attachment ref), or the
93
+ * bytes with digest `sha256`. A digest already in `byDigest` draws at once and
94
+ * reads nothing. Otherwise the file is read through its one shared query, and
95
+ * bytes whose digest matches `sha256` join `byDigest`.
96
+ */
97
+ export function useSharedImage(path, sha256, options) {
98
+ const revalidate = !!options?.revalidate;
99
+ const serverUrl = useRuntimeStore((s) => s.getActiveServerUrl());
100
+ const read = useCallback(() => (sha256 ? byDigest.get({ sha256 })?.value : undefined), [sha256]);
101
+ const pinned = useSyncExternalStore(byDigest.subscribe, read, read);
102
+ const file = useBinaryBlob(path && (!pinned || revalidate) ? path : null, { imageWait: true });
103
+ const queryClient = useQueryClient();
104
+ // Re-renders once a read's digest is known.
105
+ const [, setDigested] = useState(0);
106
+ const readDigest = file.blob ? blobDigests.get(file.blob) : undefined;
107
+ // The file's own bytes win once known to differ from the pinned digest.
108
+ const blob = revalidate && file.blob && readDigest !== undefined && readDigest !== sha256 ? file.blob : (pinned ?? file.blob);
109
+ // Trust a digest only once the bytes match it: the file may have changed
110
+ // after the tool hashed it, and a wrong entry would draw under every later
111
+ // lookup. Cached bytes that do not match are read again once: the output
112
+ // names newer bytes than the cache holds (the file was rewritten).
113
+ useEffect(() => {
114
+ const read = file.blob;
115
+ if (!read || !sha256 || !path)
116
+ return;
117
+ let live = true;
118
+ void digestOf(read).then((digest) => {
119
+ if (!live || digest === null)
120
+ return;
121
+ blobDigests.set(read, digest);
122
+ setDigested((n) => n + 1);
123
+ if (digest === sha256)
124
+ byDigest.put({ sha256 }, read, read.size);
125
+ else if (!revalidate && !rereadOnce.has(read)) {
126
+ rereadOnce.add(read);
127
+ void queryClient.invalidateQueries({ queryKey: blobKey(serverUrl, path) });
128
+ }
129
+ });
130
+ return () => {
131
+ live = false;
132
+ };
133
+ }, [file.blob, sha256, path, queryClient, serverUrl, revalidate]);
134
+ // An image on screen is never evicted from `byDigest`: held while drawn.
135
+ useEffect(() => {
136
+ const entry = sha256 && pinned ? byDigest.get({ sha256 }) : undefined;
137
+ return entry ? byDigest.retain(entry) : undefined;
138
+ }, [sha256, pinned]);
139
+ // Held while drawn, so the URL is never revoked under an <img>.
140
+ useEffect(() => {
141
+ if (!blob)
142
+ return;
143
+ const held = urls.get(blob);
144
+ if (!held)
145
+ return;
146
+ held.refs++;
147
+ return () => {
148
+ held.refs--;
149
+ if (held.refs === 0)
150
+ scheduleRevoke(blob);
151
+ };
152
+ }, [blob]);
153
+ const starting = !!file.error && isRuntimeStartingError(file.error) && !file.waitedOut;
154
+ const retry = useCallback(() => {
155
+ if (!path)
156
+ return;
157
+ // A reset, not a refetch: the wait's failure count starts over.
158
+ void queryClient.resetQueries({ queryKey: blobKey(serverUrl, path) });
159
+ }, [path, queryClient, serverUrl]);
160
+ const onNaturalSize = useCallback((width, height) => {
161
+ if (blob)
162
+ naturalRatios.set(blob, `${width} / ${height}`);
163
+ }, [blob]);
164
+ return useMemo(() => ({
165
+ // The read's own URL covers a render with no Blob in hand (a server render).
166
+ url: blob ? urlFor(blob) : file.blobUrl,
167
+ naturalRatio: blob ? (naturalRatios.get(blob) ?? null) : null,
168
+ loading: !blob && !!path && (file.isLoading || starting),
169
+ error: blob || starting ? null : file.waitedOut ? 'unavailable' : file.error,
170
+ retry,
171
+ onNaturalSize,
172
+ }), [blob, file.blobUrl, file.isLoading, file.error, file.waitedOut, starting, path, retry, onNaturalSize]);
173
+ }
174
+ /** The full-screen viewer's image (`FileSource.useSharedImage`): a digest draws at once, the file is always read too. */
175
+ export function useViewerImage(path, sha256) {
176
+ return useSharedImage(path, sha256, { revalidate: true });
177
+ }
@@ -1,9 +1,23 @@
1
1
  import React from 'react';
2
+ import { type Variant } from 'motion/react';
3
+ /**
4
+ * The caption's disclosure motion (kortix-brand motion.md: a disclosure is
5
+ * `duration-moderate`, ease-out; the exit runs faster, at `duration-normal`).
6
+ * Height and opacity together, so the image above shrinks and grows with it.
7
+ * Reduced motion: no transition, the caption is simply there or not.
8
+ */
9
+ export declare function captionMotion(reduceMotion: boolean): {
10
+ expanded: Variant;
11
+ collapsed: Variant;
12
+ };
2
13
  export interface FileContentRendererProps {
3
14
  /** Path to the file to render */
4
15
  filePath: string;
5
16
  /** Whether to show the compact header bar with file name, toggles, save/download buttons */
6
17
  showHeader?: boolean;
18
+ /** Render the header's edit controls into this host toolbar instead of a
19
+ * row of their own (`FilePreviewModal`). The host owns the path and Download. */
20
+ headerSlot?: HTMLElement | null;
7
21
  /** Additional header actions (rendered after built-in buttons) */
8
22
  headerActions?: React.ReactNode;
9
23
  /** Callback when unsaved state changes */
@@ -44,5 +58,12 @@ export interface FileContentRendererProps {
44
58
  * their own bytes (xlsx, sqlite, the HTML frame), which a cache refetch
45
59
  * cannot reach. */
46
60
  reloadKey?: number;
61
+ /**
62
+ * A full-screen viewer (the preview modal, the panel's file preview) shows
63
+ * an image's caption under it when it was opened with one (a generated
64
+ * image's prompt and model). Off for every embedded render: a Files grid
65
+ * tile, a `show` card.
66
+ */
67
+ showCaption?: boolean;
47
68
  }
48
- export declare function FileContentRenderer({ filePath, showHeader, headerActions, onUnsavedChange, onSaved, className, errorFallback, targetLine, readOnly, markdownPreview, onMarkdownPreviewChange, onStatusChange, imageControls, fitOnOpen, findText, codeEditorEditorClassName, reloadKey, }: FileContentRendererProps): React.JSX.Element;
69
+ export declare function FileContentRenderer({ filePath, showHeader, headerSlot, headerActions, onUnsavedChange, onSaved, className, errorFallback, targetLine, readOnly, markdownPreview, onMarkdownPreviewChange, onStatusChange, imageControls, fitOnOpen, findText, codeEditorEditorClassName, reloadKey, showCaption, }: FileContentRendererProps): React.JSX.Element;
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useTranslations } from '../../i18n/index.js';
4
4
  import { ErrorBoundary } from '../../primitives/error-boundary.js';
5
5
  import { CodeEditor } from '../editor/lazy-code-editor.js';
@@ -7,16 +7,20 @@ import { MarkdownWithFrontmatter } from '../../markdown/markdown-frontmatter.js'
7
7
  import { Button } from '../../primitives/button.js';
8
8
  import { InfoBanner } from '../../primitives/info-banner.js';
9
9
  import Loading from '../../primitives/loading.js';
10
+ import { ImageUnavailable } from '../renderers/image-frame.js';
10
11
  import { MermaidDiagram } from '../renderers/mermaid/mermaid-diagram.js';
11
12
  import { cn } from '../../lib/utils.js';
12
13
  import { toSandboxAbsolutePath } from '@kortix/sdk';
13
14
  import { GitDiffIcon as FileDiff, FileXIcon as FileWarning, FileXIcon as FileX, } from '@phosphor-icons/react';
14
- import { lazy, Suspense, useCallback } from 'react';
15
+ import { lazy, Suspense, useCallback, useMemo, useState } from 'react';
16
+ import { useReducedMotion } from 'motion/react';
17
+ import { Disclosure, DisclosureContent } from '../../primitives/disclosure.js';
15
18
  // Direct module import, not the feature barrel: the barrel re-exports THIS file.
16
19
  import { HtmlPreview } from './html-preview.js';
17
20
  import { JsonTreeView } from './json-tree-view.js';
18
21
  import { FileHeader } from './file-header.js';
19
22
  import { useFileContentState } from './use-file-content-state.js';
23
+ import { useFileCaption } from '../../internal/stores/file-preview-store.js';
20
24
  import { Download } from '../../internal/icons/download.js';
21
25
  // ---------------------------------------------------------------------------
22
26
  // Lazy-load heavy renderers to keep initial bundle small
@@ -39,13 +43,42 @@ const ZipRenderer = lazy(() => import('../renderers/zip/zip-renderer.js').then((
39
43
  function RendererFallback() {
40
44
  return (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsx(Loading, { className: "text-muted-foreground/40 h-4 w-4" }) }));
41
45
  }
46
+ /** kortix-brand `motion.easing.out`: enter and exit. */
47
+ const EASE_OUT = [0, 0, 0.2, 1];
48
+ /**
49
+ * The caption's disclosure motion (kortix-brand motion.md: a disclosure is
50
+ * `duration-moderate`, ease-out; the exit runs faster, at `duration-normal`).
51
+ * Height and opacity together, so the image above shrinks and grows with it.
52
+ * Reduced motion: no transition, the caption is simply there or not.
53
+ */
54
+ export function captionMotion(reduceMotion) {
55
+ const enter = reduceMotion ? { duration: 0 } : { duration: 0.2, ease: EASE_OUT };
56
+ const exit = reduceMotion ? { duration: 0 } : { duration: 0.15, ease: EASE_OUT };
57
+ return {
58
+ expanded: { height: 'auto', opacity: 1, transition: enter },
59
+ collapsed: { height: 0, opacity: 0, transition: exit },
60
+ };
61
+ }
62
+ /**
63
+ * An image with its caption under it: a generated image's prompt and the model
64
+ * that made it, in a disclosure the viewer's details toggle (ⓘ, the trigger on
65
+ * the zoom shelf, `ImageRenderer` `info`) opens. Only an image opened with a
66
+ * caption has one; any other file renders exactly as without it.
67
+ */
68
+ function CaptionedImage({ caption, open, onOpenChange, children, }) {
69
+ const reduceMotion = !!useReducedMotion();
70
+ const variants = useMemo(() => captionMotion(reduceMotion), [reduceMotion]);
71
+ if (!caption)
72
+ return _jsx(_Fragment, { children: children });
73
+ return (_jsxs(Disclosure, { open: open, onOpenChange: onOpenChange, variants: variants, className: "flex h-full min-h-0 flex-col", children: [_jsx("div", { className: "min-h-0 flex-1", children: children }), _jsx(DisclosureContent, { className: "shrink-0", children: _jsxs("div", { "data-file-caption": "", className: "border-border max-h-32 overflow-y-auto border-t px-4 py-3", children: [_jsx("p", { className: "text-foreground text-sm text-pretty", children: caption.text }), caption.model ? _jsx("p", { className: "text-muted-foreground mt-1 font-mono text-xs", children: caption.model }) : null] }) })] }));
74
+ }
42
75
  /** Shared "file does not exist" UI shown when a file cannot be loaded. */
43
76
  function FileNotFoundState({ filePath }) {
44
77
  const tHardcodedUi = useTranslations('hardcodedUi');
45
78
  return (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-3 p-8 text-center", children: [_jsx("div", { className: "bg-muted/50 flex h-12 w-12 items-center justify-center rounded-sm", children: _jsx(FileX, { className: "text-muted-foreground/40 h-6 w-6" }) }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: tHardcodedUi.raw('featuresFilesComponentsFileContentRenderer.line195JsxTextFileNotFound') }), _jsx("p", { className: "text-muted-foreground/50 max-w-sm font-mono text-xs break-all", children: filePath }), _jsx("p", { className: "text-muted-foreground/40 max-w-xs text-xs", children: tHardcodedUi.raw('featuresFilesComponentsFileContentRenderer.line201JsxTextThisFileDoesNotExistOrMayHave') })] }));
46
79
  }
47
- export function FileContentRenderer({ filePath, showHeader = true, headerActions, onUnsavedChange, onSaved, className, errorFallback, targetLine, readOnly = false, markdownPreview, onMarkdownPreviewChange, onStatusChange, imageControls, fitOnOpen = false, findText, codeEditorEditorClassName, reloadKey = 0, }) {
48
- const { source, Breadcrumbs, fileContent, isLoading, error, contentRevision, fetchRevision, hasUnsavedChanges, setHasUnsavedChanges, isSaving, saveFlash, latestContentRef, discardKey, fileName, isHeicImage, language, fileCategory, isMarkdownFile, isMermaidFile, hasPreviewToggle, isJsonFile, isHtmlFile, isMarkdownPreview, setIsMarkdownPreview, isJsonTreeView, setIsJsonTreeView, isHtmlPreview, setIsHtmlPreview, fileDiagErrorCount, fileDiagWarningCount, fileDiagnostics, tHardcodedUi, tI18nHardcoded, blobUrl, rawBlob, blobLoading, blobError, heicImageUrl, heicConverting, displayContent, handleDownload, handleSave, handleDiscard, handleEditorChange, imageDataUrl, needsBlob, isContentReady, contentError, showLoadingState, isSandboxWaking, isNotFound, } = useFileContentState({
80
+ export function FileContentRenderer({ filePath, showHeader = true, headerSlot, headerActions, onUnsavedChange, onSaved, className, errorFallback, targetLine, readOnly = false, markdownPreview, onMarkdownPreviewChange, onStatusChange, imageControls, fitOnOpen = false, findText, codeEditorEditorClassName, reloadKey = 0, showCaption = false, }) {
81
+ const { source, Breadcrumbs, fileContent, isLoading, error, contentRevision, fetchRevision, hasUnsavedChanges, setHasUnsavedChanges, isSaving, saveFlash, latestContentRef, discardKey, fileName, isHeicImage, language, fileCategory, isMarkdownFile, isMermaidFile, hasPreviewToggle, isJsonFile, isHtmlFile, isMarkdownPreview, setIsMarkdownPreview, isJsonTreeView, setIsJsonTreeView, isHtmlPreview, setIsHtmlPreview, fileDiagErrorCount, fileDiagWarningCount, fileDiagnostics, tHardcodedUi, tI18nHardcoded, blobUrl, rawBlob, blobLoading, blobError, sharedImagePath, retrySharedImage, heicImageUrl, heicConverting, displayContent, handleDownload, handleSave, handleDiscard, handleEditorChange, imageDataUrl, needsBlob, isContentReady, contentError, showLoadingState, isSandboxWaking, isNotFound, } = useFileContentState({
49
82
  filePath,
50
83
  onUnsavedChange,
51
84
  onSaved,
@@ -55,6 +88,14 @@ export function FileContentRenderer({ filePath, showHeader = true, headerActions
55
88
  onStatusChange,
56
89
  targetLine,
57
90
  });
91
+ const storedCaption = useFileCaption(filePath);
92
+ const caption = showCaption ? storedCaption : undefined;
93
+ // The caption is hidden until the details toggle opens it; per file, so the next image starts closed.
94
+ const tImageGen = useTranslations('imageGen');
95
+ const [captionOpenFor, setCaptionOpenFor] = useState(null);
96
+ const captionOpen = captionOpenFor === filePath;
97
+ const onCaptionOpenChange = (open) => setCaptionOpenFor(open ? filePath : null);
98
+ const imageInfo = caption ? { pressed: captionOpen, label: tImageGen('details') } : undefined;
58
99
  const bytesOnly = !!source.bytesOnly;
59
100
  // The table has no lines: a jump to a line (a content-search match) opens
60
101
  // CSV and TSV in the code editor.
@@ -86,10 +127,12 @@ export function FileContentRenderer({ filePath, showHeader = true, headerActions
86
127
  targetLine,
87
128
  };
88
129
  const discardLabel = tHardcodedUi.raw('featuresFilesComponentsFileContentRenderer.line600JsxAttrTitleDiscardChanges');
89
- return (_jsxs("div", { className: cn('flex h-full flex-col', className), children: [_jsx(FileHeader, { showHeader, Breadcrumbs, filePath, readOnly, hasUnsavedChanges, saveFlash, fileDiagErrorCount, fileDiagWarningCount, fileContent, isSaving, handleSave, latestContentRef, discardLabel, handleDiscard, isHtmlFile, isHtmlPreview, setIsHtmlPreview, isJsonFile, isJsonTreeView, setIsJsonTreeView, hasPreviewToggle, isMarkdownPreview, setIsMarkdownPreview, headerActions, handleDownload, blobUrl, rawBlob, tI18nHardcoded, tHardcodedUi }), _jsx("div", { className: cn('flex-1', readOnly ? 'overflow-auto' : 'overflow-hidden'), children: _jsxs(ErrorBoundary, { fallback: () => (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-3 p-8 text-center", children: [_jsx("div", { className: "bg-destructive/10 flex h-12 w-12 items-center justify-center rounded-sm", children: _jsx(FileWarning, { className: "text-destructive/50 h-6 w-6" }) }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: tI18nHardcoded.raw('autoFeaturesFileViewerFileContentRendererJsxTextCouldnT794ae800') }), _jsx("p", { className: "text-muted-foreground/50 max-w-sm font-mono text-xs break-all", children: filePath }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleDownload, children: [_jsx(Download, { className: "mr-1.5 h-3.5 w-3.5" }), tI18nHardcoded.raw('i18nComplete.textd6eafe823591')] })] })), children: [showLoadingState && (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsx(Loading, { className: "text-muted-foreground/40 h-4 w-4" }) })), contentError && !showLoadingState && isSandboxWaking && (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-3 p-8 text-center", children: [_jsx(Loading, { className: "text-muted-foreground/40 h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: tI18nHardcoded.raw('i18nComplete.text5e3de76869f3') }), _jsx("p", { className: "text-muted-foreground/50 max-w-sm font-mono text-xs break-all", children: filePath }), _jsx("p", { className: "text-muted-foreground/40 max-w-xs text-xs", children: tI18nHardcoded.raw('i18nComplete.textd4707c58c4a4') })] })), contentError &&
130
+ return (_jsxs("div", { className: cn('flex h-full flex-col', className), children: [_jsx(FileHeader, { showHeader, headerSlot, Breadcrumbs, filePath, readOnly, hasUnsavedChanges, saveFlash, fileDiagErrorCount, fileDiagWarningCount, fileContent, isSaving, handleSave, latestContentRef, discardLabel, handleDiscard, isHtmlFile, isHtmlPreview, setIsHtmlPreview, isJsonFile, isJsonTreeView, setIsJsonTreeView, hasPreviewToggle, isMarkdownPreview, setIsMarkdownPreview, headerActions, handleDownload, blobUrl, rawBlob, tI18nHardcoded, tHardcodedUi }), _jsx("div", { className: cn('flex-1', readOnly ? 'overflow-auto' : 'overflow-hidden'), children: _jsxs(ErrorBoundary, { fallback: () => (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-3 p-8 text-center", children: [_jsx("div", { className: "bg-destructive/10 flex h-12 w-12 items-center justify-center rounded-sm", children: _jsx(FileWarning, { className: "text-destructive/50 h-6 w-6" }) }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: tI18nHardcoded.raw('autoFeaturesFileViewerFileContentRendererJsxTextCouldnT794ae800') }), _jsx("p", { className: "text-muted-foreground/50 max-w-sm font-mono text-xs break-all", children: filePath }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleDownload, children: [_jsx(Download, { className: "mr-1.5 h-3.5 w-3.5" }), tI18nHardcoded.raw('i18nComplete.textd6eafe823591')] })] })), children: [showLoadingState && (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsx(Loading, { className: "text-muted-foreground/40 h-4 w-4" }) })), contentError && !showLoadingState && isSandboxWaking && (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-3 p-8 text-center", children: [_jsx(Loading, { className: "text-muted-foreground/40 h-4 w-4" }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: tI18nHardcoded.raw('i18nComplete.text5e3de76869f3') }), _jsx("p", { className: "text-muted-foreground/50 max-w-sm font-mono text-xs break-all", children: filePath }), _jsx("p", { className: "text-muted-foreground/40 max-w-xs text-xs", children: tI18nHardcoded.raw('i18nComplete.textd4707c58c4a4') })] })), contentError &&
90
131
  !showLoadingState &&
91
132
  !isSandboxWaking &&
92
- (errorFallback ? (errorFallback(contentError, filePath)) : isNotFound ? (_jsx(FileNotFoundState, { filePath: filePath })) : (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-3 p-8 text-center", children: [_jsx("div", { className: "bg-destructive/10 flex h-12 w-12 items-center justify-center rounded-sm", children: _jsx(FileWarning, { className: "text-destructive/50 h-6 w-6" }) }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: tHardcodedUi.raw('featuresFilesComponentsFileContentRenderer.line692JsxTextFailedToLoadFile') }), _jsx("p", { className: "text-muted-foreground/50 max-w-sm font-mono text-xs break-all", children: filePath }), _jsx("p", { className: "text-muted-foreground/60 max-w-sm text-xs", children: contentError })] }))), !isLoading && !error && imageDataUrl && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(ImageRenderer, { url: imageDataUrl, className: "h-full", fileName: fileName, controls: imageControls }) })), isHeicImage && (blobLoading || heicConverting) && _jsx(RendererFallback, {}), isHeicImage && !blobLoading && !blobError && heicImageUrl && !heicConverting && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(ImageRenderer, { url: heicImageUrl, className: "h-full", fileName: fileName, controls: imageControls }) })), isContentReady && fileCategory === 'pdf' && fileContent?.content && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(PdfRenderer, { fileContent: fileContent.content, fileName: fileName, className: "h-full", fitOnOpen: fitOnOpen, findText: findText, showDownload: false }) })), isContentReady && fileCategory === 'docx' && rawBlob && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(DocxRenderer, { blob: rawBlob, fileName: fileName, className: "h-full", showDownload: false }) })), isContentReady && fileCategory === 'zip' && rawBlob && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(ZipRenderer, { blob: rawBlob, fileName: fileName, className: "h-full" }, filePath) })), !isLoading && !error && !isNotFound && fileCategory === 'xlsx' && (!bytesOnly || blobUrl) && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(XlsxRenderer, {
133
+ (sharedImagePath && !isNotFound ? (
134
+ // An image never shows the read's raw error text (R38).
135
+ _jsx("div", { className: "flex h-full items-center justify-center p-8", children: _jsx(ImageUnavailable, { onRetry: retrySharedImage }) })) : errorFallback ? (errorFallback(contentError, filePath)) : isNotFound ? (_jsx(FileNotFoundState, { filePath: filePath })) : (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-3 p-8 text-center", children: [_jsx("div", { className: "bg-destructive/10 flex h-12 w-12 items-center justify-center rounded-sm", children: _jsx(FileWarning, { className: "text-destructive/50 h-6 w-6" }) }), _jsx("p", { className: "text-muted-foreground text-sm font-medium", children: tHardcodedUi.raw('featuresFilesComponentsFileContentRenderer.line692JsxTextFailedToLoadFile') }), _jsx("p", { className: "text-muted-foreground/50 max-w-sm font-mono text-xs break-all", children: filePath }), _jsx("p", { className: "text-muted-foreground/60 max-w-sm text-xs", children: contentError })] }))), !isLoading && !error && imageDataUrl && (_jsx(CaptionedImage, { caption: caption, open: captionOpen, onOpenChange: onCaptionOpenChange, children: _jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(ImageRenderer, { url: imageDataUrl, className: "h-full", fileName: fileName, controls: imageControls, info: imageInfo }) }) })), isHeicImage && (blobLoading || heicConverting) && _jsx(RendererFallback, {}), isHeicImage && !blobLoading && !blobError && heicImageUrl && !heicConverting && (_jsx(CaptionedImage, { caption: caption, open: captionOpen, onOpenChange: onCaptionOpenChange, children: _jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(ImageRenderer, { url: heicImageUrl, className: "h-full", fileName: fileName, controls: imageControls, info: imageInfo }) }) })), isContentReady && fileCategory === 'pdf' && fileContent?.content && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(PdfRenderer, { fileContent: fileContent.content, fileName: fileName, className: "h-full", fitOnOpen: fitOnOpen, findText: findText, showDownload: false }) })), isContentReady && fileCategory === 'docx' && rawBlob && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(DocxRenderer, { blob: rawBlob, fileName: fileName, className: "h-full", showDownload: false }) })), isContentReady && fileCategory === 'zip' && rawBlob && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(ZipRenderer, { blob: rawBlob, fileName: fileName, className: "h-full" }, filePath) })), !isLoading && !error && !isNotFound && fileCategory === 'xlsx' && (!bytesOnly || blobUrl) && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(XlsxRenderer, {
93
136
  // A bytes-only source hands the workbook over as a blob URL;
94
137
  // the renderer reads a sandbox path only for a session.
95
138
  filePath: bytesOnly ? blobUrl : filePath, fileName: fileName, className: "h-full", showDownload: false }, `xlsx-${filePath}-${contentRevision}-${reloadKey}`) })), !isLoading && !error && !isNotFound && fileCategory === 'sqlite' && (!bytesOnly || rawBlob) && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(SqliteRenderer, { filePath: filePath, fileName: fileName, className: "h-full", readOnly: readOnly, ...(bytesOnly ? { bytes: rawBlob, onSave: saveThroughSource } : {}) }, `sqlite-${filePath}-${contentRevision}-${reloadKey}`) })), !isLoading && !error && csvAsTable && fileContent && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(CsvRenderer, { content: fileContent.content, fileName: fileName, className: "h-full", showDownload: false }) })), isContentReady && fileCategory === 'video' && blobUrl && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(VideoRenderer, { url: blobUrl, className: "h-full" }) })), isContentReady && fileCategory === 'audio' && blobUrl && (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-5 p-8", children: [_jsx("div", { className: "bg-muted/50 flex h-14 w-14 items-center justify-center rounded-sm", children: _jsxs("svg", { className: "text-muted-foreground/40 h-6 w-6", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [_jsx("path", { d: "M9 18V5l12-2v13" }), _jsx("circle", { cx: "6", cy: "18", r: "3" }), _jsx("circle", { cx: "18", cy: "16", r: "3" })] }) }), _jsx("p", { className: "text-muted-foreground/60 text-sm", children: fileName }), _jsx("audio", { controls: true, src: blobUrl, className: "w-full max-w-sm" })] })), isContentReady && fileCategory === 'pptx' && rawBlob && (_jsx(Suspense, { fallback: _jsx(RendererFallback, {}), children: _jsx(PptxRenderer, { blob: rawBlob, binaryUrl: blobUrl, filePath: filePath, fileName: fileName, className: "h-full", onDownload: handleDownload }) })), isHtmlFile && isHtmlPreview && (_jsx(HtmlPreview, { path: toSandboxAbsolutePath(filePath), reloadKey: `${fetchRevision}-${reloadKey}`, fileName: fileName, pendingLabel: tHardcodedUi.raw('featuresFilesComponentsFileContentRenderer.line805JsxTextStartingPreviewServer') }, `html-preview-${filePath}`)), isHtmlFile && !isHtmlPreview && !isLoading && !error && fileContent?.type === 'text' && (_jsx(CodeEditor, { editorClassName: codeEditorEditorClassName, ...codeEditorProps, className: readOnly ? 'min-h-full' : 'h-full' }, `html-source-${filePath}-${discardKey}`)), !isLoading &&
@@ -114,7 +157,7 @@ export function FileContentRenderer({ filePath, showHeader = true, headerActions
114
157
  // below, so an edit in Source shows up here before saving.
115
158
  _jsx(MermaidDiagram, { source: hasUnsavedChanges ? latestContentRef.current : displayContent, fileName: fileName, onShowSource: () => setIsMarkdownPreview(false), className: "h-full" }, filePath)) : isMarkdownPreview && isMarkdownFile ? (
116
159
  // Markdown is prose, so it gets a measure. The markdown root
117
- // renders at text-[15px]; full-bleed on a wide viewport that
160
+ // renders at text-base (16px); full-bleed on a wide viewport that
118
161
  // is ~190 characters per line, well past the comfortable
119
162
  // 65-90. `max-w-2xl` is the same reading column the customize
120
163
  // sections use, and it is a no-op in panels already narrower
@@ -2,6 +2,10 @@ import type { ComponentType, ReactNode, Dispatch, SetStateAction, MutableRefObje
2
2
  import type { FileContent } from './file-source.js';
3
3
  interface FileHeaderProps {
4
4
  showHeader: boolean;
5
+ /** A host toolbar to render into (`FilePreviewModal`). The host owns the
6
+ * name, path, Download and the Markdown toggle, so only the edit state and
7
+ * the HTML/JSON view toggles go there. */
8
+ headerSlot?: HTMLElement | null;
5
9
  Breadcrumbs: ComponentType<{
6
10
  filePath: string;
7
11
  }> | undefined;
@@ -30,8 +34,8 @@ interface FileHeaderProps {
30
34
  handleDownload: () => Promise<void>;
31
35
  blobUrl: string | null;
32
36
  rawBlob: Blob | null;
33
- tI18nHardcoded: ReturnType<typeof import("../../i18n/index.js").useTranslations>;
34
- tHardcodedUi: ReturnType<typeof import("../../i18n/index.js").useTranslations>;
37
+ tI18nHardcoded: ReturnType<typeof import('../../i18n/index.js').useTranslations>;
38
+ tHardcodedUi: ReturnType<typeof import('../../i18n/index.js').useTranslations>;
35
39
  }
36
- export declare function FileHeader({ showHeader, Breadcrumbs, filePath, readOnly, hasUnsavedChanges, saveFlash, fileDiagErrorCount, fileDiagWarningCount, fileContent, isSaving, handleSave, latestContentRef, discardLabel, handleDiscard, isHtmlFile, isHtmlPreview, setIsHtmlPreview, isJsonFile, isJsonTreeView, setIsJsonTreeView, hasPreviewToggle, isMarkdownPreview, setIsMarkdownPreview, headerActions, handleDownload, blobUrl, rawBlob, tI18nHardcoded, tHardcodedUi }: FileHeaderProps): import("react").JSX.Element;
40
+ export declare function FileHeader({ showHeader, headerSlot, Breadcrumbs, filePath, readOnly, hasUnsavedChanges, saveFlash, fileDiagErrorCount, fileDiagWarningCount, fileContent, isSaving, handleSave, latestContentRef, discardLabel, handleDiscard, isHtmlFile, isHtmlPreview, setIsHtmlPreview, isJsonFile, isJsonTreeView, setIsJsonTreeView, hasPreviewToggle, isMarkdownPreview, setIsMarkdownPreview, headerActions, handleDownload, blobUrl, rawBlob, tI18nHardcoded, tHardcodedUi, }: FileHeaderProps): import("react").JSX.Element | null;
37
41
  export {};
@@ -6,16 +6,25 @@ import Hint from '../../primitives/hint.js';
6
6
  import Loading from '../../primitives/loading.js';
7
7
  import { StatusDot } from '../../primitives/status.js';
8
8
  import { cn } from '../../lib/utils.js';
9
- import { WarningIcon as AlertTriangle, BracketsCurlyIcon as Braces, CheckIcon as Check, WarningCircleIcon as CircleAlert, CodeIcon as Code, EyeIcon as Eye, GlobeIcon as Globe, ArrowCounterClockwiseIcon as RotateCcw, FloppyDiskIcon as Save } from '@phosphor-icons/react';
9
+ import { WarningIcon as AlertTriangle, BracketsCurlyIcon as Braces, CheckIcon as Check, WarningCircleIcon as CircleAlert, CodeIcon as Code, EyeIcon as Eye, GlobeIcon as Globe, ArrowCounterClockwiseIcon as RotateCcw, FloppyDiskIcon as Save, } from '@phosphor-icons/react';
10
+ import { createPortal } from 'react-dom';
10
11
  import { Download } from '../../internal/icons/download.js';
11
- export function FileHeader({ showHeader, Breadcrumbs, filePath, readOnly, hasUnsavedChanges, saveFlash, fileDiagErrorCount, fileDiagWarningCount, fileContent, isSaving, handleSave, latestContentRef, discardLabel, handleDiscard, isHtmlFile, isHtmlPreview, setIsHtmlPreview, isJsonFile, isJsonTreeView, setIsJsonTreeView, hasPreviewToggle, isMarkdownPreview, setIsMarkdownPreview, headerActions, handleDownload, blobUrl, rawBlob, tI18nHardcoded, tHardcodedUi }) {
12
- return (_jsx(_Fragment, { children: showHeader && (_jsxs("div", { className: "border-border/50 flex h-10 shrink-0 items-center gap-2 border-b px-3 py-1.5", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1", children: [Breadcrumbs && _jsx(Breadcrumbs, { filePath: filePath }), !readOnly && hasUnsavedChanges && (_jsxs(Badge, { variant: "warning", size: "sm", className: "shrink-0", children: [_jsx(StatusDot, { tone: "warning", pulse: true }), tI18nHardcoded.raw('i18nComplete.text7117f0807129')] })), !readOnly && saveFlash && !hasUnsavedChanges && (_jsxs(Badge, { variant: "success", size: "sm", className: "shrink-0", children: [_jsx(Check, { className: "h-3 w-3" }), tI18nHardcoded.raw('i18nComplete.textb5c120b316c2')] })), readOnly && (_jsx(Badge, { variant: "muted", size: "sm", className: "shrink-0 tracking-wider uppercase", children: tHardcodedUi.raw('featuresFilesComponentsFileContentRenderer.line554JsxTextViewOnly') })), (fileDiagErrorCount > 0 || fileDiagWarningCount > 0) && (_jsxs("span", { className: "inline-flex shrink-0 items-center gap-1.5", children: [fileDiagErrorCount > 0 && (_jsxs("span", { className: "text-destructive inline-flex items-center gap-0.5 text-xs font-medium tabular-nums", children: [_jsx(CircleAlert, { className: "h-3 w-3" }), fileDiagErrorCount] })), fileDiagWarningCount > 0 && (_jsxs("span", { className: "text-kortix-orange inline-flex items-center gap-0.5 text-xs font-medium tabular-nums", children: [_jsx(AlertTriangle, { className: "h-3 w-3" }), fileDiagWarningCount] }))] }))] }), _jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [!readOnly && hasUnsavedChanges && fileContent?.type === 'text' && (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "default", size: "sm", className: "h-7 gap-1.5 px-3 text-xs font-medium", onClick: () => { void handleSave(latestContentRef.current).catch(() => undefined); }, disabled: isSaving, title: tHardcodedUi.raw('featuresFilesComponentsFileContentRenderer.line586JsxAttrTitleSaveS'), children: [isSaving ? (_jsx(Loading, { className: "h-3.5 w-3.5" })) : (_jsx(Save, { className: "h-3.5 w-3.5" })), tI18nHardcoded.raw('i18nComplete.text1509f561f241')] }), _jsx(Hint, { label: discardLabel, side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": discardLabel, className: "text-muted-foreground hover:text-foreground h-7 w-7 active:scale-[0.96]", onClick: handleDiscard, children: _jsx(RotateCcw, { className: "h-3.5 w-3.5" }) }) })] })), isHtmlFile && (_jsx(Hint, { label: isHtmlPreview ? tI18nHardcoded.raw('i18nComplete.text6ee818aa2de3') : 'Preview', side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": isHtmlPreview ? tI18nHardcoded.raw('i18nComplete.text6ee818aa2de3') : 'Preview', "aria-pressed": isHtmlPreview, className: cn('h-7 w-7 active:scale-[0.96]', isHtmlPreview && 'text-primary'), onClick: () => setIsHtmlPreview((v) => !v), children: isHtmlPreview ? _jsx(Code, { className: "h-4 w-4" }) : _jsx(Globe, { className: "h-4 w-4" }) }) })), isJsonFile && fileContent?.type === 'text' && (_jsx(Hint, { label: isJsonTreeView
13
- ? tI18nHardcoded.raw('i18nComplete.text6ee818aa2de3')
14
- : tI18nHardcoded.raw('i18nComplete.text4f50bda41e87'), side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": isJsonTreeView
15
- ? tI18nHardcoded.raw('i18nComplete.text6ee818aa2de3')
16
- : tI18nHardcoded.raw('i18nComplete.text4f50bda41e87'), "aria-pressed": isJsonTreeView, className: cn('h-7 w-7 active:scale-[0.96]', isJsonTreeView && 'text-primary'), onClick: () => setIsJsonTreeView((v) => !v), children: _jsx(Braces, { className: "h-4 w-4" }) }) })), hasPreviewToggle && fileContent?.type === 'text' && (_jsx(Hint, { label: isMarkdownPreview
17
- ? tI18nHardcoded.raw('i18nComplete.text6ee818aa2de3')
18
- : 'Preview', side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": isMarkdownPreview
19
- ? tI18nHardcoded.raw('i18nComplete.text6ee818aa2de3')
20
- : 'Preview', "aria-pressed": isMarkdownPreview, className: cn('h-7 w-7 active:scale-[0.96]', isMarkdownPreview && 'text-primary'), onClick: () => setIsMarkdownPreview((v) => !v), children: isMarkdownPreview ? _jsx(Code, { className: "h-4 w-4" }) : _jsx(Eye, { className: "h-4 w-4" }) }) })), headerActions, _jsxs(Button, { variant: "outline", size: "sm", className: "h-7 gap-1.5 px-3 text-xs font-medium", onClick: handleDownload, disabled: !fileContent && !blobUrl && !rawBlob, "aria-label": tI18nHardcoded.raw('i18nComplete.textd6eafe823591'), children: [_jsx(Download, { className: "h-3.5 w-3.5" }), tI18nHardcoded.raw('i18nComplete.textd6eafe823591')] })] })] })) }));
12
+ export function FileHeader({ showHeader, headerSlot, Breadcrumbs, filePath, readOnly, hasUnsavedChanges, saveFlash, fileDiagErrorCount, fileDiagWarningCount, fileContent, isSaving, handleSave, latestContentRef, discardLabel, handleDiscard, isHtmlFile, isHtmlPreview, setIsHtmlPreview, isJsonFile, isJsonTreeView, setIsJsonTreeView, hasPreviewToggle, isMarkdownPreview, setIsMarkdownPreview, headerActions, handleDownload, blobUrl, rawBlob, tI18nHardcoded, tHardcodedUi, }) {
13
+ const status = (_jsxs(_Fragment, { children: [!readOnly && hasUnsavedChanges && (_jsxs(Badge, { variant: "warning", size: "sm", className: "shrink-0", children: [_jsx(StatusDot, { tone: "warning", pulse: true }), tI18nHardcoded.raw('i18nComplete.text7117f0807129')] })), !readOnly && saveFlash && !hasUnsavedChanges && (_jsxs(Badge, { variant: "success", size: "sm", className: "shrink-0", children: [_jsx(Check, { className: "h-3 w-3" }), tI18nHardcoded.raw('i18nComplete.textb5c120b316c2')] })), readOnly && (_jsx(Badge, { variant: "muted", size: "sm", className: "shrink-0 tracking-wider uppercase", children: tHardcodedUi.raw('featuresFilesComponentsFileContentRenderer.line554JsxTextViewOnly') })), (fileDiagErrorCount > 0 || fileDiagWarningCount > 0) && (_jsxs("span", { className: "inline-flex shrink-0 items-center gap-1.5", children: [fileDiagErrorCount > 0 && (_jsxs("span", { className: "text-destructive inline-flex items-center gap-0.5 text-xs font-medium tabular-nums", children: [_jsx(CircleAlert, { className: "h-3 w-3" }), fileDiagErrorCount] })), fileDiagWarningCount > 0 && (_jsxs("span", { className: "text-kortix-orange inline-flex items-center gap-0.5 text-xs font-medium tabular-nums", children: [_jsx(AlertTriangle, { className: "h-3 w-3" }), fileDiagWarningCount] }))] }))] }));
14
+ const editActions = (_jsxs(_Fragment, { children: [!readOnly && hasUnsavedChanges && fileContent?.type === 'text' && (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "default", size: "sm", className: "h-7 gap-1.5 px-3 text-xs font-medium", onClick: () => {
15
+ void handleSave(latestContentRef.current).catch(() => undefined);
16
+ }, disabled: isSaving, title: tHardcodedUi.raw('featuresFilesComponentsFileContentRenderer.line586JsxAttrTitleSaveS'), children: [isSaving ? _jsx(Loading, { className: "h-3.5 w-3.5" }) : _jsx(Save, { className: "h-3.5 w-3.5" }), tI18nHardcoded.raw('i18nComplete.text1509f561f241')] }), _jsx(Hint, { label: discardLabel, side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": discardLabel, className: "text-muted-foreground hover:text-foreground h-7 w-7 active:scale-[0.96]", onClick: handleDiscard, children: _jsx(RotateCcw, { className: "h-3.5 w-3.5" }) }) })] })), isHtmlFile && (_jsx(Hint, { label: isHtmlPreview ? tI18nHardcoded.raw('i18nComplete.text6ee818aa2de3') : 'Preview', side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": isHtmlPreview ? tI18nHardcoded.raw('i18nComplete.text6ee818aa2de3') : 'Preview', "aria-pressed": isHtmlPreview, className: cn('h-7 w-7 active:scale-[0.96]', isHtmlPreview && 'text-primary'), onClick: () => setIsHtmlPreview((v) => !v), children: isHtmlPreview ? _jsx(Code, { className: "h-4 w-4" }) : _jsx(Globe, { className: "h-4 w-4" }) }) })), isJsonFile && fileContent?.type === 'text' && (_jsx(Hint, { label: isJsonTreeView
17
+ ? tI18nHardcoded.raw('i18nComplete.text6ee818aa2de3')
18
+ : tI18nHardcoded.raw('i18nComplete.text4f50bda41e87'), side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": isJsonTreeView
19
+ ? tI18nHardcoded.raw('i18nComplete.text6ee818aa2de3')
20
+ : tI18nHardcoded.raw('i18nComplete.text4f50bda41e87'), "aria-pressed": isJsonTreeView, className: cn('h-7 w-7 active:scale-[0.96]', isJsonTreeView && 'text-primary'), onClick: () => setIsJsonTreeView((v) => !v), children: _jsx(Braces, { className: "h-4 w-4" }) }) }))] }));
21
+ // `null` while the host toolbar mounts: render nothing rather than a row.
22
+ if (headerSlot !== undefined) {
23
+ return showHeader && headerSlot
24
+ ? createPortal(_jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [status, editActions] }), headerSlot)
25
+ : null;
26
+ }
27
+ if (!showHeader)
28
+ return null;
29
+ return (_jsxs("div", { className: "border-border/50 flex h-10 shrink-0 items-center gap-2 border-b px-3 py-1.5", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1", children: [Breadcrumbs && _jsx(Breadcrumbs, { filePath: filePath }), status] }), _jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [editActions, hasPreviewToggle && fileContent?.type === 'text' && (_jsx(Hint, { label: isMarkdownPreview ? tI18nHardcoded.raw('i18nComplete.text6ee818aa2de3') : 'Preview', side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": isMarkdownPreview ? tI18nHardcoded.raw('i18nComplete.text6ee818aa2de3') : 'Preview', "aria-pressed": isMarkdownPreview, className: cn('h-7 w-7 active:scale-[0.96]', isMarkdownPreview && 'text-primary'), onClick: () => setIsMarkdownPreview((v) => !v), children: isMarkdownPreview ? _jsx(Code, { className: "h-4 w-4" }) : _jsx(Eye, { className: "h-4 w-4" }) }) })), headerActions, _jsxs(Button, { variant: "outline", size: "sm", className: "h-7 gap-1.5 px-3 text-xs font-medium", onClick: handleDownload, disabled: !fileContent && !blobUrl && !rawBlob, "aria-label": tI18nHardcoded.raw('i18nComplete.textd6eafe823591'), children: [_jsx(Download, { className: "h-3.5 w-3.5" }), tI18nHardcoded.raw('i18nComplete.textd6eafe823591')] })] })] }));
21
30
  }
@@ -60,4 +60,4 @@ export interface FilePreviewModalProps extends FilePreviewState {
60
60
  * hit that path, which is why only the mouse appeared broken.)
61
61
  * Z-index is dialog-depth aware so it always stacks above its host overlay.
62
62
  */
63
- export declare function FilePreviewModal({ selectedFilePath, panelMode, filePathList, currentFileIndex, onClose: closePreview, onNext: nextPreview, onPrev: prevPreview, source, HistoryContent, statusSlot, extraActions, shareContext, historyLabel, embedded, targetLine, }: FilePreviewModalProps): import("react").JSX.Element | null;
63
+ export declare function FilePreviewModal({ selectedFilePath, panelMode, filePathList, currentFileIndex, onClose: closePreview, onNext: nextPreview, onPrev: prevPreview, source, HistoryContent, renderFileIcon, statusSlot, extraActions, shareContext, historyLabel, embedded, targetLine, }: FilePreviewModalProps): import("react").JSX.Element | null;
@@ -1,22 +1,21 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { PublicShareLinkButton } from "../../internal/share/public-share-link-button.js";
4
3
  import { Button } from "../../primitives/button.js";
5
4
  import { ButtonGroup } from "../../primitives/button-group.js";
6
5
  import Hint from "../../primitives/hint.js";
7
- import Loading from "../../primitives/loading.js";
8
6
  import { errorToast, successToast } from "../../primitives/toast.js";
9
7
  import { Download } from "../../internal/icons/download.js";
10
8
  import { useTranslations } from "../../i18n/index.js";
11
9
  import { cn } from "../../lib/utils.js";
12
10
  import { dialogContentZ, DialogDepthProvider, dialogOverlayZ, useDialogLayerDepth, } from "../../lib/z-stack.js";
13
- import { ArrowClockwiseIcon, CaretLeftIcon as ChevronLeft, CaretRightIcon as ChevronRight, CodeIcon as Code, EyeIcon as Eye, ClockCounterClockwiseIcon as History, ArrowsOutSimpleIcon as Maximize2, ArrowsInSimpleIcon as Minimize2, XIcon as X, } from "@phosphor-icons/react";
14
- import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
11
+ import { CaretLeftIcon as ChevronLeft, CaretRightIcon as ChevronRight, CodeIcon as Code, EyeIcon as Eye, ClockCounterClockwiseIcon as History, ArrowsOutSimpleIcon as Maximize2, ArrowsInSimpleIcon as Minimize2, XIcon as X, } from "@phosphor-icons/react";
12
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState, } from "react";
15
13
  import { createPortal } from "react-dom";
16
14
  import { FileContentRenderer } from "./file-content-renderer.js";
17
15
  import { FileSourceProvider, } from "./file-source.js";
18
16
  import { getLanguageFromExt } from "./preview-policy.js";
19
17
  import { SaveAsPdfButton } from "./save-as-pdf-button.js";
18
+ import { RefreshButton, ViewerActions, ViewerPathPill, fileShareInput, } from "./viewer-actions.js";
20
19
  /** Tabbable elements used by the focus trap below. */
21
20
  const FOCUSABLE_SELECTOR = [
22
21
  "a[href]",
@@ -54,7 +53,7 @@ const NO_REFRESH = {
54
53
  * hit that path, which is why only the mouse appeared broken.)
55
54
  * Z-index is dialog-depth aware so it always stacks above its host overlay.
56
55
  */
57
- export function FilePreviewModal({ selectedFilePath, panelMode, filePathList, currentFileIndex, onClose: closePreview, onNext: nextPreview, onPrev: prevPreview, source, HistoryContent, statusSlot, extraActions, shareContext, historyLabel = "History", embedded = false, targetLine = null, }) {
56
+ export function FilePreviewModal({ selectedFilePath, panelMode, filePathList, currentFileIndex, onClose: closePreview, onNext: nextPreview, onPrev: prevPreview, source, HistoryContent, renderFileIcon, statusSlot, extraActions, shareContext, historyLabel = "History", embedded = false, targetLine = null, }) {
58
57
  const tI18nHardcoded = useTranslations("hardcodedUi");
59
58
  const isOpen = panelMode === "viewer" && !!selectedFilePath;
60
59
  const [dirty, setDirty] = useState(false);
@@ -81,7 +80,6 @@ export function FilePreviewModal({ selectedFilePath, panelMode, filePathList, cu
81
80
  // renders — the same pattern `FileContentRenderer` uses for `useFileContent`.
82
81
  const useRefresh = source.useRefresh ?? useNoRefresh;
83
82
  const { refresh, refreshing, reloadKey } = useRefresh(selectedFilePath);
84
- const refreshLabel = tI18nHardcoded.raw("i18nComplete.text0e9161011702");
85
83
  const [markdownPreview, setMarkdownPreview] = useState(true);
86
84
  // Markdown and Mermaid files both open rendered, with a Source toggle.
87
85
  const isMarkdownFile = ["markdown", "mermaid"].includes(getLanguageFromExt(fileName));
@@ -90,17 +88,6 @@ export function FilePreviewModal({ selectedFilePath, panelMode, filePathList, cu
90
88
  // The PDF export renders through a session's sandbox; a bytes-only source has none.
91
89
  const exportsPdf = getLanguageFromExt(fileName) === "markdown" && !source.bytesOnly;
92
90
  const { data: pdfSource } = source.useFileContent(exportsPdf ? selectedFilePath : null);
93
- const shareInput = useMemo(() => {
94
- if (!selectedFilePath || !shareContext)
95
- return null;
96
- return {
97
- file: {
98
- label: fileName || selectedFilePath,
99
- path: selectedFilePath,
100
- },
101
- mode: "view",
102
- };
103
- }, [fileName, selectedFilePath, shareContext]);
104
91
  // The dialog surface — focus target for the trap and the focus-on-open below.
105
92
  const surfaceRef = useRef(null);
106
93
  // Reset transient view state when the file changes. A jump to a line shows
@@ -228,6 +215,7 @@ export function FilePreviewModal({ selectedFilePath, panelMode, filePathList, cu
228
215
  const [toolbarEl, toolbarRef] = useState(null);
229
216
  const [nameEl, nameRef] = useState(null);
230
217
  const compactToolbar = useCompactRow(toolbarEl, nameEl);
218
+ const [editSlot, editSlotRef] = useState(null);
231
219
  useEffect(() => {
232
220
  if (!isOpen || !fullscreen)
233
221
  return;
@@ -260,22 +248,22 @@ export function FilePreviewModal({ selectedFilePath, panelMode, filePathList, cu
260
248
  // or display:none would clip or collapse it. React context is preserved.
261
249
  if (typeof document === "undefined")
262
250
  return null;
263
- const toolbar = (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2 pl-1", children: [_jsx("span", { ref: nameRef, className: "max-w-[300px] truncate text-sm font-medium", children: fileName }), !compactToolbar && statusSlot] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [isMarkdownFile && (_jsx(Hint, { label: markdownPreview
251
+ const toolbar = (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [_jsxs(ViewerPathPill, { icon: renderFileIcon(fileName), path: selectedFilePath ?? undefined, fileName: fileName, nameRef: nameRef, children: [source.useRefresh && (_jsx(RefreshButton, { onRefresh: refresh, refreshing: refreshing })), _jsx(ViewerActions, { shareContext: shareContext, shareInput: fileShareInput(selectedFilePath ?? undefined, fileName) })] }), !compactToolbar && statusSlot] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [_jsx("div", { ref: editSlotRef, className: "flex items-center empty:hidden" }), isMarkdownFile && (_jsx(Hint, { label: markdownPreview
264
252
  ? tI18nHardcoded.raw("i18nComplete.text6ee818aa2de3")
265
253
  : "Preview", side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": markdownPreview
266
254
  ? tI18nHardcoded.raw("i18nComplete.text6ee818aa2de3")
267
255
  : "Preview", "aria-pressed": markdownPreview, className: cn("h-8 w-8 active:scale-[0.96]", markdownPreview
268
256
  ? "text-muted-foreground hover:text-foreground"
269
- : "text-foreground bg-muted"), onClick: () => setMarkdownPreview((v) => !v), children: markdownPreview ? (_jsx(Code, { className: "h-4 w-4" })) : (_jsx(Eye, { className: "h-4 w-4" })) }) })), source.useRefresh && (_jsx(Hint, { label: refreshLabel, side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": refreshLabel, "aria-busy": refreshing, disabled: refreshing, className: "text-muted-foreground hover:text-foreground h-8 w-8 active:scale-[0.96]", onClick: refresh, children: refreshing ? (_jsx(Loading, { className: "text-muted-foreground size-4 motion-reduce:animate-none" })) : (_jsx(ArrowClockwiseIcon, { className: "h-4 w-4" })) }) })), filePathList.length > 1 && !compactToolbar && (
257
+ : "text-foreground bg-muted"), onClick: () => setMarkdownPreview((v) => !v), children: markdownPreview ? (_jsx(Code, { className: "h-4 w-4" })) : (_jsx(Eye, { className: "h-4 w-4" })) }) })), filePathList.length > 1 && !compactToolbar && (
270
258
  // Previous / next live with the other actions; the arrow keys still work.
271
259
  _jsxs(ButtonGroup, { children: [_jsx(Hint, { label: tI18nHardcoded.raw("autoFeaturesFileViewerFilePreviewModalJsxAttrTitlePreviousb7cda316"), side: "bottom", children: _jsx(Button, { variant: "secondary", size: "icon-base", disabled: !hasPrev, "aria-label": tI18nHardcoded.raw("autoFeaturesFileViewerFilePreviewModalJsxAttrAriaLabel03ab3a76"), className: "active:scale-[0.96]", onClick: onPrev, children: _jsx(ChevronLeft, { className: "h-4 w-4" }) }) }), _jsx(Hint, { label: tI18nHardcoded.raw("autoFeaturesFileViewerFilePreviewModalJsxAttrTitleNext96666bd8"), side: "bottom", children: _jsx(Button, { variant: "secondary", size: "icon-base", disabled: !hasNext, "aria-label": tI18nHardcoded.raw("autoFeaturesFileViewerFilePreviewModalJsxAttrAriaLabelda6ea7e4"), className: "active:scale-[0.96]", onClick: onNext, children: _jsx(ChevronRight, { className: "h-4 w-4" }) }) })] })), _jsxs(ButtonGroup, { children: [_jsx(Hint, { label: historyLabel, side: "bottom", children: _jsx(Button, { variant: "secondary", size: "icon-base", "aria-label": historyLabel, "aria-pressed": !!historyPath, className: cn("active:scale-[0.96]", historyPath && "bg-accent"), onClick: () => setHistoryPath(historyPath ? null : selectedFilePath), children: _jsx(History, { className: "h-4 w-4" }) }) }), exportsPdf && !compactToolbar && (_jsx(SaveAsPdfButton, { fileName: fileName, content: pdfSource?.type === "text" ? pdfSource.content : undefined, variant: "secondary", className: "h-8 w-8", iconClassName: "h-4 w-4" })), _jsxs(Button, { variant: "secondary", size: "sm", className: "gap-1.5 active:scale-[0.96]", onClick: handleDownload, "aria-label": tI18nHardcoded.raw("i18nComplete.textd6eafe823591"), children: [_jsx(Download, { className: "h-3.5 w-3.5" }), !compactToolbar &&
272
- tI18nHardcoded.raw("i18nComplete.textd6eafe823591")] })] }), shareContext && !compactToolbar && (_jsx(PublicShareLinkButton, { projectId: shareContext.projectId, sessionId: shareContext.sessionId, input: shareInput, tooltip: tI18nHardcoded.raw("autoFeaturesFileViewerFilePreviewModalJsxAttrTooltipCopy42229181"), className: "text-muted-foreground hover:text-foreground" })), extraActions, embedded && (_jsx(Hint, { label: expanded
260
+ tI18nHardcoded.raw("i18nComplete.textd6eafe823591")] })] }), extraActions, embedded && (_jsx(Hint, { label: expanded
273
261
  ? tI18nHardcoded.raw("i18nComplete.textde12e3c7c7a8")
274
262
  : "Expand", side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": expanded
275
263
  ? tI18nHardcoded.raw("i18nComplete.textde12e3c7c7a8")
276
264
  : "Expand", "aria-pressed": expanded, className: "text-muted-foreground hover:text-foreground h-8 w-8 active:scale-[0.96]", onClick: () => setExpanded((v) => !v), children: expanded ? (_jsx(Minimize2, { className: "h-4 w-4" })) : (_jsx(Maximize2, { className: "h-4 w-4" })) }) })), _jsx(ButtonGroup, { className: "ml-1.5", children: _jsx(Hint, { label: tI18nHardcoded.raw("autoFeaturesFileViewerFilePreviewModalJsxAttrTitleClosea40d0510"), side: "bottom", children: _jsx(Button, { variant: "secondary", size: "icon-base", "aria-label": tI18nHardcoded.raw("autoFeaturesFileViewerFilePreviewModalJsxAttrTitleClosea40d0510"), className: "active:scale-[0.96]", onClick: onClose, children: _jsx(X, { className: "h-4 w-4" }) }) }) })] })] }));
277
- const body = (_jsxs(_Fragment, { children: [_jsx("div", { className: "h-full w-full overflow-hidden px-3 pt-3 pb-2", children: _jsx(FileSourceProvider, { value: source, children: _jsx(FileContentRenderer, { filePath: selectedFilePath, reloadKey: reloadKey, onUnsavedChange: setDirty, showHeader: !!source.editablePreview, readOnly: !source.editablePreview, targetLine: targetLine, markdownPreview: markdownPreview, onMarkdownPreviewChange: setMarkdownPreview }) }) }), historyPath && (_jsx("div", { className: "bg-popover border-border/60 animate-in slide-in-from-bottom-4 fade-in-0 absolute right-4 bottom-4 z-30 overflow-hidden rounded-md border shadow-md duration-normal", children: _jsx(HistoryContent, { filePath: historyPath, onClose: () => setHistoryPath(null) }) }))] }));
278
- const panelInner = (_jsxs(_Fragment, { children: [_jsx("div", { ref: toolbarRef, className: "border-border/40 bg-background/95 flex h-12 shrink-0 items-center gap-2 border-b px-3 backdrop-blur-sm", children: toolbar }), _jsx("div", { ref: contentRef, className: "relative min-h-0 flex-1 overflow-hidden", children: body })] }));
265
+ const body = (_jsxs(_Fragment, { children: [_jsx("div", { className: "h-full w-full overflow-hidden px-3 pt-3 pb-2", children: _jsx(FileSourceProvider, { value: source, children: _jsx(FileContentRenderer, { filePath: selectedFilePath, showCaption: true, reloadKey: reloadKey, onUnsavedChange: setDirty, showHeader: !!source.editablePreview, headerSlot: editSlot, readOnly: !source.editablePreview, targetLine: targetLine, markdownPreview: markdownPreview, onMarkdownPreviewChange: setMarkdownPreview }) }) }), historyPath && (_jsx("div", { className: "bg-popover border-border/60 animate-in slide-in-from-bottom-4 fade-in-0 absolute right-4 bottom-4 z-30 overflow-hidden rounded-md border shadow-md duration-normal", children: _jsx(HistoryContent, { filePath: historyPath, onClose: () => setHistoryPath(null) }) }))] }));
266
+ const panelInner = (_jsxs(_Fragment, { children: [_jsx("div", { ref: toolbarRef, "data-file-toolbar": "", className: "border-border/40 bg-background/95 flex h-12 shrink-0 items-center gap-2 border-b px-3 backdrop-blur-sm", children: toolbar }), _jsx("div", { ref: contentRef, className: "relative min-h-0 flex-1 overflow-hidden", children: body })] }));
279
267
  // Inline (embedded, not expanded): fill the relative parent — the side panel —
280
268
  // instead of portaling to a full-screen overlay. No backdrop, no body lock.
281
269
  if (!fullscreen) {
@@ -105,6 +105,19 @@ export interface FileSource {
105
105
  * `reloadKey` goes to `<FileContentRenderer reloadKey>`.
106
106
  */
107
107
  useRefresh?: (filePath: string | null) => FileRefreshResult;
108
+ /**
109
+ * The page's shared image store (R38), for a source whose paths are the live
110
+ * sandbox's: a raster image draws from the bytes another surface (the image
111
+ * card, a `show` card) already loaded, else is read once as bytes. A source
112
+ * with other paths (a git ref, a Drive, a share) omits it.
113
+ */
114
+ useSharedImage?: (filePath: string | null, sha256?: string | null) => SharedImageResult;
115
+ }
116
+ export interface SharedImageResult {
117
+ url: string | null;
118
+ loading: boolean;
119
+ error: string | null;
120
+ retry: () => void;
108
121
  }
109
122
  export interface FileRefreshResult {
110
123
  refresh: () => void;
@@ -43,6 +43,8 @@ export declare function useFileContentState({ filePath, onUnsavedChange, onSaved
43
43
  rawBlob: Blob | null;
44
44
  blobLoading: boolean;
45
45
  blobError: string | null;
46
+ sharedImagePath: string | null;
47
+ retrySharedImage: () => void;
46
48
  heicImageUrl: string | null;
47
49
  heicConverting: boolean;
48
50
  displayContent: string;