@kortix/ui 0.14.4 → 0.14.6

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 (228) hide show
  1. package/README.md +8 -4
  2. package/dist/access/access/access-dialog.js +8 -5
  3. package/dist/access/access/copy-row.js +2 -2
  4. package/dist/access/sharing-picker.js +2 -1
  5. package/dist/browser/app-in-browser.js +5 -3
  6. package/dist/browser/browser-panel.js +6 -6
  7. package/dist/browser/sandbox-browser-chrome.d.ts +4 -1
  8. package/dist/browser/sandbox-browser-chrome.js +4 -3
  9. package/dist/customize/agents/agent-access.d.ts +20 -0
  10. package/dist/customize/agents/agent-access.js +17 -0
  11. package/dist/customize/agents/agent-grant-pages.js +5 -6
  12. package/dist/customize/agents/agent-page.js +2 -1
  13. package/dist/customize/agents/agents-page.js +18 -1
  14. package/dist/customize/connectors/add-mcp/add-mcp-server.js +4 -2
  15. package/dist/customize/connectors/connector-routes.d.ts +3 -1
  16. package/dist/customize/connectors/connector-routes.js +18 -0
  17. package/dist/customize/connectors/connectors-page.js +23 -4
  18. package/dist/customize/connectors/detail/connector-advanced-tools.js +4 -6
  19. package/dist/customize/providers/provider-accordion.js +2 -1
  20. package/dist/customize/providers/provider-connect.js +2 -1
  21. package/dist/customize/sections/component/telegram-channel-row.js +1 -1
  22. package/dist/customize/sections/connector-connection-modal.js +11 -9
  23. package/dist/customize/sections/connectors-view.js +11 -13
  24. package/dist/customize/sections/llm-provider/constants.d.ts +0 -1
  25. package/dist/customize/sections/llm-provider/constants.js +0 -2
  26. package/dist/customize/sections/llm-provider/models-tab-group.d.ts +1 -1
  27. package/dist/customize/sections/llm-provider/models-tab-group.js +7 -4
  28. package/dist/customize/sections/llm-provider/models-tab-row.d.ts +2 -18
  29. package/dist/customize/sections/llm-provider/models-tab-row.js +6 -30
  30. package/dist/customize/sections/llm-provider/models-tab.d.ts +0 -1
  31. package/dist/customize/sections/llm-provider/models-tab.js +1 -10
  32. package/dist/customize/sections/llm-provider/use-connected-providers.js +2 -2
  33. package/dist/customize/sections/llm-provider/utils.d.ts +1 -1
  34. package/dist/customize/sections/llm-provider/utils.js +7 -14
  35. package/dist/customize/sections/view/gateway/_shared.js +1 -2
  36. package/dist/customize/sections/view/gateway/gateway-routing.js +18 -20
  37. package/dist/customize/sections/view/sandbox-provider-coverage.d.ts +3 -18
  38. package/dist/customize/sections/view/sandbox-provider-coverage.js +7 -27
  39. package/dist/customize/settings/rail-copy-context.js +5 -3
  40. package/dist/customize/settings/tabs/sandbox-tab.d.ts +2 -27
  41. package/dist/customize/settings/tabs/sandbox-tab.js +13 -112
  42. package/dist/customize/settings/tabs/sandbox-template-form.js +9 -12
  43. package/dist/customize/shared/capability-tabs.js +2 -1
  44. package/dist/customize/shared/entity/entity-modal.d.ts +5 -1
  45. package/dist/customize/shared/entity/entity-modal.js +7 -4
  46. package/dist/customize/skills/remove-skill-button.d.ts +16 -0
  47. package/dist/customize/skills/remove-skill-button.js +42 -0
  48. package/dist/customize/skills/skills-page.js +4 -6
  49. package/dist/customize/triggers/schedule/event-trigger-copy.js +3 -2
  50. package/dist/customize/triggers/schedule/schedule-builder.js +7 -4
  51. package/dist/customize/triggers/schedule/trigger-composer.js +5 -2
  52. package/dist/customize/triggers/schedule/trigger-list.d.ts +0 -2
  53. package/dist/customize/triggers/schedule/trigger-list.js +1 -18
  54. package/dist/customize/triggers/schedule/trigger-tile.d.ts +1 -32
  55. package/dist/customize/triggers/schedule/trigger-tile.js +1 -16
  56. package/dist/customize/triggers/schedule/when-event.js +1 -1
  57. package/dist/diff/diff-view.js +9 -10
  58. package/dist/files/editor/code-editor.js +2 -3
  59. package/dist/files/project/components/change-request-detail-dialog.d.ts +3 -1
  60. package/dist/files/project/components/change-request-detail-dialog.js +3 -5
  61. package/dist/files/project/components/change-request-status-badge.d.ts +12 -0
  62. package/dist/files/project/components/change-request-status-badge.js +27 -0
  63. package/dist/files/project/components/change-requests-panel.js +1 -1
  64. package/dist/files/project/components/drive-explorer.js +2 -1
  65. package/dist/files/project/components/drive-grid-view.js +11 -7
  66. package/dist/files/project/components/drive-list-view.js +4 -4
  67. package/dist/files/project/components/file-breadcrumbs.js +1 -1
  68. package/dist/files/project/components/file-explorer-page.js +1 -1
  69. package/dist/files/project/components/file-history-popover.js +1 -6
  70. package/dist/files/project/components/file-search.js +6 -1
  71. package/dist/files/project/components/open-change-request-dialog.js +2 -1
  72. package/dist/files/project/components/version-selector-logic.d.ts +11 -0
  73. package/dist/files/project/components/version-selector-logic.js +26 -0
  74. package/dist/files/project/components/version-selector.js +11 -20
  75. package/dist/files/project/hooks/use-change-requests.d.ts +14 -1
  76. package/dist/files/project/hooks/use-change-requests.js +44 -25
  77. package/dist/files/project/hooks/use-server-health.js +17 -15
  78. package/dist/files/renderers/docx/docx-viewer.js +4 -6
  79. package/dist/files/renderers/image-renderer.js +6 -24
  80. package/dist/files/renderers/pdf/pdf-renderer.d.ts +3 -1
  81. package/dist/files/renderers/pdf/pdf-renderer.js +2 -2
  82. package/dist/files/renderers/pdf/pdf-viewer.d.ts +3 -0
  83. package/dist/files/renderers/pdf/pdf-viewer.js +16 -7
  84. package/dist/files/renderers/presentation/FullScreenPresentationViewer.js +2 -11
  85. package/dist/files/renderers/show-content-renderer.js +1 -1
  86. package/dist/files/renderers/sqlite-renderer.js +8 -3
  87. package/dist/files/renderers/xlsx/xlsx-viewer.js +15 -16
  88. package/dist/files/tree/components/file-tree-item.js +1 -1
  89. package/dist/files/viewer/file-content-renderer.d.ts +4 -1
  90. package/dist/files/viewer/file-content-renderer.js +2 -2
  91. package/dist/files/viewer/file-preview-modal.js +1 -1
  92. package/dist/files/viewer/use-file-content-state.js +1 -1
  93. package/dist/i18n/en.d.ts +36 -35
  94. package/dist/i18n/en.js +45 -44
  95. package/dist/index.d.ts +1 -1
  96. package/dist/internal/desktop/desktop-back-button.js +5 -3
  97. package/dist/internal/hooks/use-auto-scroll.js +5 -3
  98. package/dist/internal/hooks/use-notification-center.js +21 -15
  99. package/dist/internal/hooks/use-review-session-summary.d.ts +6 -0
  100. package/dist/internal/hooks/use-review-session-summary.js +11 -0
  101. package/dist/internal/lib/browser-error-noise.d.ts +1 -1
  102. package/dist/internal/lib/browser-error-noise.js +1 -1
  103. package/dist/internal/lib/browser-noise/rules/rejections.d.ts +10 -0
  104. package/dist/internal/lib/browser-noise/rules/rejections.js +27 -1
  105. package/dist/internal/lib/provider-branding.js +14 -3
  106. package/dist/internal/lib/trigger-clock.d.ts +42 -0
  107. package/dist/internal/lib/trigger-clock.js +34 -0
  108. package/dist/internal/lib/web-notifications.d.ts +6 -0
  109. package/dist/internal/lib/web-notifications.js +57 -7
  110. package/dist/internal/notifications/notification-bell.d.ts +13 -0
  111. package/dist/internal/notifications/notification-bell.js +1 -1
  112. package/dist/internal/notifications/notification-rows.js +2 -1
  113. package/dist/internal/outcomes/outcome-types.d.ts +5 -0
  114. package/dist/internal/reminders/reminder-filter.d.ts +12 -0
  115. package/dist/internal/reminders/reminder-filter.js +34 -0
  116. package/dist/internal/reminders/reminder-format.d.ts +6 -0
  117. package/dist/internal/reminders/reminder-format.js +20 -2
  118. package/dist/internal/stores/kortix-computer-store.d.ts +1 -1
  119. package/dist/internal/stores/session-hover-store.d.ts +20 -5
  120. package/dist/internal/stores/session-hover-store.js +31 -13
  121. package/dist/internal/stores/session-panel-memory-store.js +2 -0
  122. package/dist/markdown/code/inline-code.js +8 -0
  123. package/dist/markdown/code/output-paths-context.d.ts +18 -0
  124. package/dist/markdown/code/output-paths-context.js +35 -0
  125. package/dist/markdown/genui/components/charts.js +6 -5
  126. package/dist/primitives/count-badge.d.ts +3 -1
  127. package/dist/primitives/count-badge.js +3 -3
  128. package/dist/primitives/glyph-paths-source.js +1 -1
  129. package/dist/primitives/glyph-paths.generated.js +1 -1
  130. package/dist/primitives/map.js +2 -1
  131. package/dist/primitives/preview-image.js +4 -2
  132. package/dist/primitives/textarea.js +1 -1
  133. package/dist/primitives/typed-title.js +12 -14
  134. package/dist/primitives/typing-animation.js +4 -2
  135. package/dist/providers/ui-provider.d.ts +57 -0
  136. package/dist/providers/ui-provider.js +4 -0
  137. package/dist/session/composer/ComposerCard.d.ts +2 -0
  138. package/dist/session/composer/ComposerCard.js +5 -1
  139. package/dist/session/composer/agent-selector.js +17 -5
  140. package/dist/session/composer/composer-chat-input.js +5 -3
  141. package/dist/session/composer/composer-logic.js +8 -7
  142. package/dist/session/composer/composer-toolbar.js +4 -2
  143. package/dist/session/composer/composer-underbar.js +3 -1
  144. package/dist/session/composer/composer.js +34 -5
  145. package/dist/session/composer/draft/composer-draft.js +4 -4
  146. package/dist/session/composer/menus/slash-actions.d.ts +22 -2
  147. package/dist/session/composer/menus/slash-actions.js +20 -0
  148. package/dist/session/composer/menus/slash-items.js +19 -3
  149. package/dist/session/composer/pasted-text.js +3 -2
  150. package/dist/session/composer/stores/queued-draft-store.js +3 -2
  151. package/dist/session/header/reminder-parts.d.ts +294 -0
  152. package/dist/session/header/reminder-parts.js +120 -0
  153. package/dist/session/header/session-reminders-indicator.d.ts +13 -0
  154. package/dist/session/header/session-reminders-indicator.js +142 -35
  155. package/dist/session/media/sandbox-url-detector.js +0 -7
  156. package/dist/session/models/model-availability.d.ts +23 -0
  157. package/dist/session/models/model-availability.js +20 -0
  158. package/dist/session/models/model-grouping.js +1 -4
  159. package/dist/session/models/model-selector.js +23 -14
  160. package/dist/session/models/use-model-connection-gate.js +4 -1
  161. package/dist/session/outcomes/change-request-outcomes.js +24 -3
  162. package/dist/session/outcomes/session-outcomes-provider.d.ts +3 -1
  163. package/dist/session/outcomes/session-outcomes-provider.js +31 -3
  164. package/dist/session/overrides/provider-secret-pool-editor.js +2 -1
  165. package/dist/session/overrides/session-overrides-composer.d.ts +1 -1
  166. package/dist/session/overrides/session-overrides-composer.js +5 -1
  167. package/dist/session/panel/advanced/advanced-panel.js +3 -7
  168. package/dist/session/panel/easy/app-preview.js +12 -2
  169. package/dist/session/panel/easy/easy-panel-logic.d.ts +1 -1
  170. package/dist/session/panel/easy/easy-panel-logic.js +1 -1
  171. package/dist/session/panel/easy/outputs-card.js +2 -1
  172. package/dist/session/panel/easy/panel-card.js +5 -3
  173. package/dist/session/panel/easy/preview-load.d.ts +14 -0
  174. package/dist/session/panel/easy/preview-load.js +15 -0
  175. package/dist/session/panel/easy/step-detail-body.js +2 -7
  176. package/dist/session/panel/mobile-tool-drawer.js +4 -2
  177. package/dist/session/panel/open-session-quick-view.js +2 -1
  178. package/dist/session/panel/panel-fallback.js +8 -4
  179. package/dist/session/panel/session-panel-provider.d.ts +1 -0
  180. package/dist/session/panel/session-panel-provider.js +19 -3
  181. package/dist/session/panel/session-reminders-panel.d.ts +4 -0
  182. package/dist/session/panel/session-reminders-panel.js +109 -0
  183. package/dist/session/panel/shared/use-panel-derivations.js +6 -4
  184. package/dist/session/panel/shared/use-panel-handoffs.d.ts +2 -2
  185. package/dist/session/panel/shared/use-panel-handoffs.js +5 -1
  186. package/dist/session/panel/use-session-panel-layout.js +7 -5
  187. package/dist/session/print/session-print-header.d.ts +0 -10
  188. package/dist/session/print/session-print-header.js +25 -9
  189. package/dist/session/status/session-surface.js +1 -1
  190. package/dist/session/thread/optimistic-turn.d.ts +5 -1
  191. package/dist/session/thread/optimistic-turn.js +8 -6
  192. package/dist/session/thread/session-chat.d.ts +6 -1
  193. package/dist/session/thread/session-chat.js +34 -12
  194. package/dist/session/thread/session-history.js +3 -1
  195. package/dist/session/thread/session-thread.d.ts +10 -1
  196. package/dist/session/thread/session-thread.js +2 -2
  197. package/dist/session/thread/transcript.js +13 -7
  198. package/dist/session/tool/shared/file-list.js +19 -2
  199. package/dist/session/tool/shared/infrastructure-contexts.d.ts +7 -0
  200. package/dist/session/tool/shared/infrastructure-contexts.js +7 -0
  201. package/dist/session/tool/shared/infrastructure-output.js +9 -5
  202. package/dist/session/tool/shared/infrastructure-row-header.js +9 -1
  203. package/dist/session/tool/shared/infrastructure-rows.js +7 -5
  204. package/dist/session/tool/tools/bash-tool.js +10 -6
  205. package/dist/session/tool/tools/web-search-tool.js +25 -3
  206. package/dist/session/turn/burst-step-body.js +4 -0
  207. package/dist/session/turn/recovered-failures.d.ts +8 -0
  208. package/dist/session/turn/recovered-failures.js +32 -0
  209. package/dist/session/turn/streaming-cadence.js +5 -8
  210. package/dist/styles.css +1 -1
  211. package/dist/terminal/session-terminal-panel.js +3 -5
  212. package/dist/theme/theme.js +2 -1
  213. package/dist/workspace/projects/modal/github-import-pickers.js +7 -6
  214. package/dist/workspace/review/review-center.js +13 -10
  215. package/dist/workspace/session-workspace.js +5 -9
  216. package/dist/workspace/sessions/deleted-session-row.js +5 -2
  217. package/dist/workspace/sidebar/footer/project-computer-nav.js +6 -4
  218. package/dist/workspace/sidebar/footer/project-mcp-nav.js +6 -2
  219. package/dist/workspace/sidebar/modal/rename-session-modal.js +8 -3
  220. package/dist/workspace/sidebar/project-session-list.js +52 -55
  221. package/dist/workspace/sidebar/session-brief-hover-card.d.ts +35 -18
  222. package/dist/workspace/sidebar/session-brief-hover-card.js +114 -37
  223. package/dist/workspace/sidebar/session-status-mark.d.ts +15 -1
  224. package/dist/workspace/sidebar/session-status-mark.js +63 -17
  225. package/package.json +7 -3
  226. package/skills/kortix-ui/SKILL.md +1 -1
  227. package/dist/customize/sections/view/sandbox-provider-result.d.ts +0 -47
  228. package/dist/customize/sections/view/sandbox-provider-result.js +0 -79
@@ -13,6 +13,7 @@ import { useMemo, useState } from 'react';
13
13
  import { useProjectContext } from '../context.js';
14
14
  import { useBranches } from '../hooks/use-branches.js';
15
15
  import { useVersionStore } from '../store/version-store.js';
16
+ import { groupVersions, versionMatches, versionTitle } from './version-selector-logic.js';
16
17
  /** Shared formatter, hoisted so each row render does not rebuild the Intl
17
18
  * machinery. Same options as the old `toLocaleDateString` call — identical output. */
18
19
  const commitDateFormatter = new Intl.DateTimeFormat(undefined, {
@@ -30,24 +31,10 @@ export function VersionSelector() {
30
31
  const { data, isLoading, error } = useBranches({ enabled: open || activeRef !== '' });
31
32
  const defaultBranch = data?.default_branch ?? activeRef;
32
33
  const isOnMain = activeRef === defaultBranch;
33
- const filtered = useMemo(() => {
34
- const branches = data?.branches ?? [];
35
- const trimmed = query.trim().toLowerCase();
36
- if (!trimmed)
37
- return branches;
38
- return branches.filter((b) => b.name.toLowerCase().includes(trimmed) || b.subject.toLowerCase().includes(trimmed));
39
- }, [data?.branches, query]);
40
- const { primary, others } = useMemo(() => {
41
- const primary = [];
42
- const others = [];
43
- for (const b of filtered) {
44
- if (b.is_default)
45
- primary.push(b);
46
- else
47
- others.push(b);
48
- }
49
- return { primary, others };
50
- }, [filtered]);
34
+ const filtered = useMemo(() => (data?.branches ?? []).filter((b) => versionMatches(b, query)), [data?.branches, query]);
35
+ const { primary, others, archived } = useMemo(() => groupVersions(filtered), [filtered]);
36
+ const activeBranch = data?.branches.find((b) => b.name === activeRef);
37
+ const activeLabel = activeBranch ? versionTitle(activeBranch) : activeRef;
51
38
  const handleSelect = (branchName) => {
52
39
  setVersion(projectId, branchName === defaultBranch ? null : branchName);
53
40
  setOpen(false);
@@ -57,10 +44,14 @@ export function VersionSelector() {
57
44
  setOpen(v);
58
45
  if (!v)
59
46
  setQuery('');
60
- }, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline-ghost", type: "button", size: "sm", className: cn('w-30 max-w-[300px] min-w-0 shrink-0 justify-between'), title: tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line88JsxAttrTitleSwitchVersion'), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Layers, { className: "text-muted-foreground size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: activeRef || 'Version' })] }), _jsx(ChevronsUpDown, { className: "text-muted-foreground size-3 shrink-0" })] }) }), _jsxs(PopoverContent, { align: "start", sideOffset: 6, className: "flex w-[340px] flex-col overflow-hidden p-0", children: [_jsxs("div", { className: "border-border flex shrink-0 items-center gap-1.5 border-b px-3 py-2", children: [_jsx(Search, { className: "text-muted-foreground/60 h-3.5 w-3.5 shrink-0" }), _jsx("input", { type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line112JsxAttrPlaceholderFindAVersion'), className: "text-foreground placeholder:text-muted-foreground/50 h-7 min-w-0 flex-1 border-0 bg-transparent px-0 text-sm outline-none", autoFocus: true })] }), _jsx("div", { className: "h-[520px] max-h-[calc(100vh-8rem)] min-h-0 shrink", children: _jsxs(FadedScrollArea, { fadeColor: "from-popover", className: "h-full overscroll-contain", children: [isLoading && (_jsxs("div", { className: "text-muted-foreground flex items-center justify-center gap-2 py-8 text-xs", children: [_jsx(Loading, { className: "size-3.5" }), tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line122JsxTextLoadingVersions')] })), error && !isLoading && (_jsx("div", { className: "text-muted-foreground px-4 py-8 text-center text-xs", children: tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line128JsxTextFailedToLoadVersions') })), !isLoading && !error && filtered.length === 0 && (_jsxs("div", { className: "text-muted-foreground px-4 py-8 text-center text-xs", children: [tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line134JsxTextNoVersionsMatch'), ' ', query ? `“${query}”` : ''] })), primary.length > 0 && (_jsxs("div", { className: cn('space-y-3 p-3', others.length > 0 ? 'pb-0' : 'pb-3'), children: [_jsx(Label, { children: tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line140JsxTextMainVersion') }), primary.map((b) => (_jsx(VersionRow, { branch: b, isActive: activeRef === b.name, onClick: () => handleSelect(b.name) }, b.name)))] })), others.length > 0 && (_jsxs("div", { className: "space-y-3 p-3 pb-3", children: [_jsx(Label, { children: tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line154JsxTextOtherVersions') }), others.map((b) => (_jsx(VersionRow, { branch: b, isActive: activeRef === b.name, onClick: () => handleSelect(b.name) }, b.name)))] }))] }) })] })] }));
47
+ }, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline-ghost", type: "button", size: "sm", className: "max-w-48 min-w-30 shrink-0 justify-between", title: tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line88JsxAttrTitleSwitchVersion'), children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(Layers, { className: "text-muted-foreground size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: activeLabel || 'Version' })] }), _jsx(ChevronsUpDown, { className: "text-muted-foreground size-3 shrink-0" })] }) }), _jsxs(PopoverContent, { align: "start", sideOffset: 6, className: "flex w-[340px] flex-col overflow-hidden p-0", children: [_jsxs("div", { className: "border-border flex shrink-0 items-center gap-1.5 border-b px-3 py-2", children: [_jsx(Search, { className: "text-muted-foreground h-3.5 w-3.5 shrink-0" }), _jsx("input", { type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line112JsxAttrPlaceholderFindAVersion'), className: "text-foreground placeholder:text-muted-foreground h-7 min-w-0 flex-1 border-0 bg-transparent px-0 text-sm outline-none" })] }), _jsx("div", { className: "h-[520px] max-h-[calc(100vh-8rem)] min-h-0 shrink", children: _jsxs(FadedScrollArea, { fadeColor: "from-popover", className: "h-full overscroll-contain", children: [isLoading && (_jsxs("div", { className: "text-muted-foreground flex items-center justify-center gap-2 py-8 text-xs", children: [_jsx(Loading, { className: "size-3.5" }), tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line122JsxTextLoadingVersions')] })), error && !isLoading && (_jsx("div", { className: "text-muted-foreground px-4 py-8 text-center text-xs", children: tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line128JsxTextFailedToLoadVersions') })), !isLoading && !error && filtered.length === 0 && (_jsxs("div", { className: "text-muted-foreground px-4 py-8 text-center text-xs", children: [tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line134JsxTextNoVersionsMatch'), ' ', query ? `“${query}”` : ''] })), primary.length > 0 && (_jsxs("div", { className: cn('space-y-3 p-3', others.length + archived.length > 0 ? 'pb-0' : 'pb-3'), children: [_jsx(Label, { children: tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line140JsxTextMainVersion') }), primary.map((b) => (_jsx(VersionRow, { branch: b, isActive: activeRef === b.name, onClick: () => handleSelect(b.name) }, b.name)))] })), others.length > 0 && (_jsxs("div", { className: cn('space-y-3 p-3', archived.length > 0 ? 'pb-0' : 'pb-3'), children: [_jsx(Label, { children: tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line154JsxTextOtherVersions') }), others.map((b) => (_jsx(VersionRow, { branch: b, isActive: activeRef === b.name, onClick: () => handleSelect(b.name) }, b.name)))] })), archived.length > 0 && (_jsxs("div", { className: "space-y-3 p-3 pb-3", children: [_jsx(Label, { children: tHardcodedUi.raw('i18nComplete.textbdb86505f806') }), archived.map((b) => (_jsx(VersionRow, { branch: b, isActive: activeRef === b.name, onClick: () => handleSelect(b.name) }, b.name)))] }))] }) })] })] }));
61
48
  }
62
49
  function VersionRow({ branch, isActive, onClick, }) {
63
50
  const tHardcodedUi = useTranslations('hardcodedUi');
64
51
  const date = branch.committed_at ? commitDateFormatter.format(new Date(branch.committed_at)) : '';
65
- return (_jsxs("button", { type: "button", onClick: onClick, className: cn('border-border bg-popover hover:bg-foreground/4 flex w-full cursor-pointer items-start gap-2.5 rounded-md px-3 py-2 text-left', isActive && 'bg-primary/5'), children: [_jsx("div", { className: "mt-0.5 flex size-4 shrink-0 items-center justify-center", children: isActive ? (_jsx(Check, { className: "text-primary size-3.5" })) : (_jsx(Layers, { className: "text-muted-foreground size-3.5" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-foreground truncate text-sm font-medium", children: branch.name }), branch.is_default && (_jsx(Badge, { variant: "kortix", size: "xs", children: tHardcodedUi.raw('i18nComplete.texteb814be3ca3b') }))] }), _jsxs("div", { className: "text-muted-foreground/80 mt-0.5 flex items-center gap-1.5 text-xs", children: [date && (_jsxs("span", { children: [tHardcodedUi.raw('i18nComplete.text3a5ecca188c0'), " ", date] })), !branch.is_default && branch.ahead != null && branch.behind != null && (_jsxs(_Fragment, { children: [date && _jsx("span", { className: "text-muted-foreground/40", children: "\u00B7" }), _jsxs("span", { className: "inline-flex items-center gap-1 tabular-nums", title: tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line234JsxAttrTitleAheadBehindMainVersion'), children: [_jsxs("span", { className: "text-kortix-green inline-flex items-center gap-0.5", children: [_jsx(ArrowUpRight, { className: "h-2.5 w-2.5" }), branch.ahead] }), _jsxs("span", { className: "text-kortix-red inline-flex items-center gap-0.5", children: [_jsx(ArrowDownLeft, { className: "h-2.5 w-2.5" }), branch.behind] })] })] }))] }), branch.subject && (_jsx("div", { className: "text-muted-foreground/70 mt-0.5 truncate text-xs", children: branch.subject }))] })] }));
52
+ const title = versionTitle(branch);
53
+ const agent = branch.session?.agent_name;
54
+ return (_jsxs("button", { type: "button", onClick: onClick,
55
+ // The id stays one hover away, and search still finds it.
56
+ title: title === branch.name ? undefined : branch.name, className: cn('border-border bg-popover hover:bg-foreground/4 flex w-full cursor-pointer items-start gap-2.5 rounded-md px-3 py-2 text-left', isActive && 'bg-primary/5'), children: [_jsx("div", { className: "mt-0.5 flex size-4 shrink-0 items-center justify-center", children: isActive ? (_jsx(Check, { className: "text-primary size-3.5" })) : (_jsx(Layers, { className: "text-muted-foreground size-3.5" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-foreground truncate text-sm font-medium", children: title }), branch.is_default && (_jsx(Badge, { variant: "kortix", size: "xs", children: tHardcodedUi.raw('i18nComplete.texteb814be3ca3b') })), branch.session?.deleted && (_jsx(Badge, { variant: "outline", size: "xs", children: tHardcodedUi.raw('i18nComplete.textb48ff39c2e0f') }))] }), _jsxs("div", { className: "text-muted-foreground mt-0.5 flex items-center gap-1.5 text-xs", children: [agent && _jsx("span", { className: "truncate", children: agent }), agent && date && _jsx("span", { className: "text-muted-foreground", children: "\u00B7" }), date && (_jsxs("span", { children: [tHardcodedUi.raw('i18nComplete.text3a5ecca188c0'), " ", date] })), !branch.is_default && branch.ahead != null && branch.behind != null && (_jsxs(_Fragment, { children: [date && _jsx("span", { className: "text-muted-foreground", children: "\u00B7" }), _jsxs("span", { className: "inline-flex items-center gap-1 tabular-nums", title: tHardcodedUi.raw('featuresProjectFilesComponentsVersionSelector.line234JsxAttrTitleAheadBehindMainVersion'), children: [_jsxs("span", { className: "text-kortix-green inline-flex items-center gap-0.5", children: [_jsx(ArrowUpRight, { className: "h-2.5 w-2.5" }), branch.ahead] }), _jsxs("span", { className: "text-kortix-red inline-flex items-center gap-0.5", children: [_jsx(ArrowDownLeft, { className: "h-2.5 w-2.5" }), branch.behind] })] })] }))] }), branch.subject && (_jsx("div", { className: "text-muted-foreground mt-0.5 truncate text-xs", children: branch.subject }))] })] }));
66
57
  }
@@ -1,4 +1,4 @@
1
- import { type InfiniteData } from '@tanstack/react-query';
1
+ import { type InfiniteData, type QueryClient } from '@tanstack/react-query';
2
2
  import { type ChangeRequest, type ChangeRequestDetailResponse, type ChangeRequestDiffResponse, type ChangeRequestMergePreview, type ChangeRequestMergeResponse, type ChangeRequestStatus, type VersionDiffPreview } from '../api/change-requests.js';
3
3
  export declare const changeRequestKeys: {
4
4
  all: readonly ["project-files", "change-requests"];
@@ -118,6 +118,19 @@ export declare function useVersionDiff(input: {
118
118
  projectId?: string;
119
119
  }): import("@tanstack/react-query").UseQueryResult<VersionDiffPreview, Error>;
120
120
  export declare function useChangeRequestMergePreview(crId: string | null, enabled?: boolean): import("@tanstack/react-query").UseQueryResult<ChangeRequestMergePreview, Error>;
121
+ /**
122
+ * Marks every change-request read of one project stale: each list (the
123
+ * session's outcome cards, the sidebar brief), each detail, and the review
124
+ * summary behind the sidebar's "Needs you" count. Called at turn end, because
125
+ * an agent opens and closes change requests from its sandbox through the REST
126
+ * API and the browser is not told (KRTX-3009, KRTX-3010).
127
+ */
128
+ export declare function invalidateChangeRequestReads(qc: QueryClient, projectId: string): void;
129
+ /**
130
+ * Invalidates every CR query for the active project — used after open / merge
131
+ * / close / reopen so all panels and detail views re-fetch.
132
+ */
133
+ export declare function invalidateAfterChangeRequestWrite(qc: QueryClient, projectId: string): void;
121
134
  export declare function useOpenChangeRequest(options?: {
122
135
  projectId?: string;
123
136
  }): import("@tanstack/react-query").UseMutationResult<ChangeRequest, Error, {
@@ -217,39 +217,58 @@ export function useChangeRequestMergePreview(crId, enabled = true) {
217
217
  staleTime: 10_000,
218
218
  });
219
219
  }
220
+ /**
221
+ * Marks every change-request read of one project stale: each list (the
222
+ * session's outcome cards, the sidebar brief), each detail, and the review
223
+ * summary behind the sidebar's "Needs you" count. Called at turn end, because
224
+ * an agent opens and closes change requests from its sandbox through the REST
225
+ * API and the browser is not told (KRTX-3009, KRTX-3010).
226
+ */
227
+ export function invalidateChangeRequestReads(qc, projectId) {
228
+ if (!projectId)
229
+ return;
230
+ qc.invalidateQueries({ queryKey: changeRequestKeys.project(projectId) });
231
+ qc.invalidateQueries({ queryKey: qk.project.reviewSummary(projectId) });
232
+ }
220
233
  /**
221
234
  * Invalidates every CR query for the active project — used after open / merge
222
235
  * / close / reopen so all panels and detail views re-fetch.
223
236
  */
237
+ export function invalidateAfterChangeRequestWrite(qc, projectId) {
238
+ invalidateChangeRequestReads(qc, projectId);
239
+ // Branches list shows ahead/behind that may shift after a merge.
240
+ qc.invalidateQueries({ queryKey: branchKeys.list(projectId) });
241
+ // The merge commit lands on the default branch — commit list goes stale.
242
+ qc.invalidateQueries({ queryKey: commitKeys.project(projectId) });
243
+ // Whether this version still differs from its base changes the moment a CR
244
+ // merges — refresh the "Alternate version of main · N changes" banner
245
+ // (git-status, which is otherwise sticky and never re-fetches on its own),
246
+ // the live version-diff preview, and the cached session row (base_ref etc.).
247
+ qc.invalidateQueries({ queryKey: gitStatusKeys.all });
248
+ qc.invalidateQueries({ queryKey: versionDiffKeys.project(projectId) });
249
+ // Every individual git-connected session under this project — a CR
250
+ // landing on the base ref can change what `getProjectSession` returns
251
+ // (base_ref etc.) for any of them. sessionsScope is the shared prefix
252
+ // that reaches the sessions list AND every qk.project.session(id, sid)
253
+ // entry in one call; there is no "every session, not the list" prefix to
254
+ // narrow to, so this deliberately also refreshes the sessions list.
255
+ qc.invalidateQueries({ queryKey: qk.project.sessionsScope(projectId) });
256
+ // Landing a CR on the base ref is the one thing that happens INSIDE the app
257
+ // that can make an open session's compiled agent config stale. The
258
+ // freshness query is deliberately not polled — this is what tells it to
259
+ // look again, so the chip appears on merge rather than on next focus.
260
+ qc.invalidateQueries({ queryKey: ['session-config', projectId] });
261
+ // A merged CR can rewrite kortix.yaml: add or remove agents and change
262
+ // `default_agent`. Both live in the project detail entry, and the composer's
263
+ // agent list sits under it (`[...detail, 'agents']`), so New session kept
264
+ // offering removed agents until a reload (KRTX-3012).
265
+ qc.invalidateQueries({ queryKey: qk.project.detail(projectId) });
266
+ }
224
267
  function useInvalidateAll(projectIdArg) {
225
268
  const qc = useQueryClient();
226
269
  const ctx = useProjectContext();
227
270
  const projectId = projectIdArg ?? ctx?.projectId ?? '';
228
- return () => {
229
- qc.invalidateQueries({ queryKey: changeRequestKeys.project(projectId) });
230
- // Branches list shows ahead/behind that may shift after a merge.
231
- qc.invalidateQueries({ queryKey: branchKeys.list(projectId) });
232
- // The merge commit lands on the default branch — commit list goes stale.
233
- qc.invalidateQueries({ queryKey: commitKeys.project(projectId) });
234
- // Whether this version still differs from its base changes the moment a CR
235
- // merges — refresh the "Alternate version of main · N changes" banner
236
- // (git-status, which is otherwise sticky and never re-fetches on its own),
237
- // the live version-diff preview, and the cached session row (base_ref etc.).
238
- qc.invalidateQueries({ queryKey: gitStatusKeys.all });
239
- qc.invalidateQueries({ queryKey: versionDiffKeys.project(projectId) });
240
- // Every individual git-connected session under this project — a CR
241
- // landing on the base ref can change what `getProjectSession` returns
242
- // (base_ref etc.) for any of them. sessionsScope is the shared prefix
243
- // that reaches the sessions list AND every qk.project.session(id, sid)
244
- // entry in one call; there is no "every session, not the list" prefix to
245
- // narrow to, so this deliberately also refreshes the sessions list.
246
- qc.invalidateQueries({ queryKey: qk.project.sessionsScope(projectId) });
247
- // Landing a CR on the base ref is the one thing that happens INSIDE the app
248
- // that can make an open session's compiled agent config stale. The
249
- // freshness query is deliberately not polled — this is what tells it to
250
- // look again, so the chip appears on merge rather than on next focus.
251
- qc.invalidateQueries({ queryKey: ['session-config', projectId] });
252
- };
271
+ return () => invalidateAfterChangeRequestWrite(qc, projectId);
253
272
  }
254
273
  export function useOpenChangeRequest(options) {
255
274
  const ctx = useProjectContext();
@@ -18,19 +18,21 @@ export function useServerHealth(_options) {
18
18
  * git-status gate (`project?.vcs === 'git'`) continues to behave correctly.
19
19
  */
20
20
  export function useCurrentProject(_options) {
21
- const data = {
22
- id: 'project-files',
23
- worktree: '/workspace',
24
- vcs: 'git',
25
- name: 'project',
26
- time: { created: Date.now(), updated: Date.now() },
27
- sandboxes: [],
28
- };
29
- return useMemo(() => ({
30
- data,
31
- isLoading: false,
32
- isError: false,
33
- error: null,
34
- refetch: async () => ({ data }),
35
- }), []);
21
+ return useMemo(() => {
22
+ const data = {
23
+ id: 'project-files',
24
+ worktree: '/workspace',
25
+ vcs: 'git',
26
+ name: 'project',
27
+ time: { created: Date.now(), updated: Date.now() },
28
+ sandboxes: [],
29
+ };
30
+ return {
31
+ data,
32
+ isLoading: false,
33
+ isError: false,
34
+ error: null,
35
+ refetch: async () => ({ data }),
36
+ };
37
+ }, []);
36
38
  }
@@ -45,6 +45,9 @@ const ZOOM_OPTIONS = [10, 25, 50, 75, 100, 125, 150, 175, 200, 400];
45
45
  const DOCX_PADDING_WARNING_TEXT = 'a style property during rerender';
46
46
  const DOCX_THUMBNAIL_FOCUS_RING_CLASS = 'group-focus-visible/docx-thumbnail-sidebar:ring-2 group-focus-visible/docx-thumbnail-sidebar:ring-ring group-focus-visible/docx-thumbnail-sidebar:ring-offset-1 group-focus-visible/docx-thumbnail-sidebar:ring-offset-background';
47
47
  const DOCX_THUMBNAIL_PREFETCH_ROWS = 4;
48
+ // Night reader: the inverted page content's default text is black, so the
49
+ // inversion makes it light (see the <style> in the page shell below).
50
+ const NIGHT_READER_TEXT_STYLE = '.docx-night-reader-shell [style*="hue-rotate(180deg)"]{color:#000}';
48
51
  function createDocxActivePageStore() {
49
52
  let activePage = 1;
50
53
  const listeners = new Set();
@@ -193,11 +196,6 @@ function DocxPageNumberControl({ activePageStore, controlsDisabled, onPageChange
193
196
  const displayPage = pageCount ? activePage : 1;
194
197
  const [isEditing, setIsEditing] = React.useState(false);
195
198
  const [draftPage, setDraftPage] = React.useState(() => String(displayPage));
196
- React.useEffect(() => {
197
- if (!isEditing) {
198
- setDraftPage(String(displayPage));
199
- }
200
- }, [displayPage, isEditing]);
201
199
  React.useEffect(() => {
202
200
  if (!isEditing)
203
201
  return;
@@ -617,5 +615,5 @@ function DocxViewerContent({ className, defaultZoom, effectiveIsDark, fileName,
617
615
  event.currentTarget.focus({ preventScroll: true });
618
616
  },
619
617
  tabIndex: 0,
620
- }, viewportRef: setViewportRef, children: !url && !activeUploadedDocxFile ? (_jsx("div", { className: "grid h-full min-h-96 place-items-center p-6 text-center", children: _jsxs("div", { className: "bg-background max-w-md rounded-lg border p-4 text-sm shadow-xs", children: [_jsx("div", { className: "font-medium", children: tI18nComplete.raw('text53c247b8cf06') }), _jsxs("div", { className: "text-muted-foreground mt-1", children: [tI18nComplete.raw('text46800e585f61'), " ", _jsx("code", { children: "src" }), ' ', tI18nComplete.raw('text37056f2381e2')] }), _jsxs("div", { className: "text-muted-foreground mt-1", children: [tI18nComplete.raw('text1432897a6136'), " ", _jsx("code", { children: ".doc" }), ' ', tI18nComplete.raw('text73a2f5159338')] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "mt-4", onClick: () => fileInputRef.current?.click(), children: [_jsx(Upload, { className: "size-4" }), tI18nComplete.raw('textdc0451eb46ce')] })] }) })) : loadError ? (_jsx("div", { className: "grid h-full min-h-96 place-items-center p-6 text-center", children: _jsxs("div", { className: "bg-background text-destructive max-w-md rounded-lg border p-4 text-sm shadow-xs", children: [_jsx("div", { className: "font-medium", children: tI18nComplete.raw('text2a5502b48b14') }), _jsx("div", { className: "text-muted-foreground mt-1", children: loadError })] }) })) : isLoadingDocument ? (loadingState) : (_jsx("div", { className: "flex min-h-full w-max min-w-full justify-center", children: _jsx("div", { className: cn('origin-top', effectiveIsDark && 'docx-night-reader-shell'), style: { zoom: zoomScale / 100 }, children: _jsx(DocxEditorViewer, { editor: editor, mode: "read-only", showTrackedChanges: showTrackedChanges, renderTrackedChangeCard: renderTrackedChangeCard, showComments: showComments, renderCommentCard: renderCommentCard, loadingState: loadingState, pageBackgroundColor: effectiveIsDark ? 'var(--background)' : undefined, pageGapBackgroundColor: viewerBackgroundColor, pageVirtualization: pageVirtualization, deferInitialPaginationPaint: false, onPageCountChange: handlePageCountChange }) }) })) })] })] }));
618
+ }, viewportRef: setViewportRef, children: !url && !activeUploadedDocxFile ? (_jsx("div", { className: "grid h-full min-h-96 place-items-center p-6 text-center", children: _jsxs("div", { className: "bg-background max-w-md rounded-lg border p-4 text-sm shadow-xs", children: [_jsx("div", { className: "font-medium", children: tI18nComplete.raw('text53c247b8cf06') }), _jsxs("div", { className: "text-muted-foreground mt-1", children: [tI18nComplete.raw('text46800e585f61'), " ", _jsx("code", { children: "src" }), ' ', tI18nComplete.raw('text37056f2381e2')] }), _jsxs("div", { className: "text-muted-foreground mt-1", children: [tI18nComplete.raw('text1432897a6136'), " ", _jsx("code", { children: ".doc" }), ' ', tI18nComplete.raw('text73a2f5159338')] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "mt-4", onClick: () => fileInputRef.current?.click(), children: [_jsx(Upload, { className: "size-4" }), tI18nComplete.raw('textdc0451eb46ce')] })] }) })) : loadError ? (_jsx("div", { className: "grid h-full min-h-96 place-items-center p-6 text-center", children: _jsxs("div", { className: "bg-background text-destructive max-w-md rounded-lg border p-4 text-sm shadow-xs", children: [_jsx("div", { className: "font-medium", children: tI18nComplete.raw('text2a5502b48b14') }), _jsx("div", { className: "text-muted-foreground mt-1", children: loadError })] }) })) : isLoadingDocument ? (loadingState) : (_jsx("div", { className: "flex min-h-full w-max min-w-full justify-center", children: _jsxs("div", { className: cn('origin-top', effectiveIsDark && 'docx-night-reader-shell'), style: { zoom: zoomScale / 100 }, children: [effectiveIsDark && (_jsx("style", { children: NIGHT_READER_TEXT_STYLE })), _jsx(DocxEditorViewer, { editor: editor, mode: "read-only", showTrackedChanges: showTrackedChanges, renderTrackedChangeCard: renderTrackedChangeCard, showComments: showComments, renderCommentCard: renderCommentCard, loadingState: loadingState, pageBackgroundColor: effectiveIsDark ? 'var(--background)' : undefined, pageGapBackgroundColor: viewerBackgroundColor, pageVirtualization: pageVirtualization, deferInitialPaginationPaint: false, onPageCountChange: handlePageCountChange })] }) })) })] })] }));
621
619
  }
@@ -55,11 +55,10 @@ export function ImageRenderer({ url, className, fileName, controls = 'hover', ba
55
55
  const [rotation, setRotation] = useState(0);
56
56
  const [isPanning, setIsPanning] = useState(false);
57
57
  const [position, setPosition] = useState({ x: 0, y: 0 });
58
- const [startPanPosition, setStartPanPosition] = useState({ x: 0, y: 0 });
58
+ const startPanPositionRef = useRef({ x: 0, y: 0 });
59
59
  const [backdrop, setBackdrop] = useState('transparent');
60
60
  const [imgLoaded, setImgLoaded] = useState(false);
61
61
  const [imgError, setImgError] = useState(false);
62
- const [imgInfo, setImgInfo] = useState(null);
63
62
  // ── Retry state ──────────────────────────────────────────────────────
64
63
  const [retryCount, setRetryCount] = useState(0);
65
64
  const [imgSrc, setImgSrc] = useState(url);
@@ -71,18 +70,6 @@ export function ImageRenderer({ url, className, fileName, controls = 'hover', ba
71
70
  const isSvg = fileName?.toLowerCase().endsWith('.svg') ||
72
71
  url?.toLowerCase().endsWith('.svg') ||
73
72
  url?.includes('image/svg');
74
- // Derive the display file type for the info panel
75
- const displayFileType = (() => {
76
- if (isSvg)
77
- return 'SVG';
78
- if (fileName) {
79
- const ext = fileName.split('.').pop()?.toUpperCase();
80
- if (ext)
81
- return ext;
82
- }
83
- const ext = url.split('.').pop()?.toUpperCase();
84
- return ext || 'Image';
85
- })();
86
73
  // When the parent passes a new URL, reset everything. The reset runs during
87
74
  // render, not in an effect: an effect also ran on mount, after a cached
88
75
  // image had already fired `load`, and set `imgLoaded` back to false. The
@@ -123,17 +110,12 @@ export function ImageRenderer({ url, className, fileName, controls = 'hover', ba
123
110
  setImgError(false);
124
111
  setRetryCount(0); // reset on success
125
112
  if (imageRef.current) {
126
- setImgInfo({
127
- width: imageRef.current.naturalWidth,
128
- height: imageRef.current.naturalHeight,
129
- type: displayFileType,
130
- });
131
113
  previewFit?.report({
132
114
  width: imageRef.current.naturalWidth,
133
115
  height: imageRef.current.naturalHeight,
134
116
  });
135
117
  }
136
- }, [displayFileType, previewFit]);
118
+ }, [previewFit]);
137
119
  // Retries exhausted: these bytes are never going to decode, so there will be
138
120
  // no `report` for this image. Say "never" rather than staying silent — a
139
121
  // consumer reads silence as "still loading" and keeps whatever width it last
@@ -213,17 +195,17 @@ export function ImageRenderer({ url, className, fileName, controls = 'hover', ba
213
195
  const handleMouseDown = (e) => {
214
196
  if (canPan) {
215
197
  setIsPanning(true);
216
- setStartPanPosition({
198
+ startPanPositionRef.current = {
217
199
  x: e.clientX - position.x,
218
200
  y: e.clientY - position.y,
219
- });
201
+ };
220
202
  }
221
203
  };
222
204
  const handleMouseMove = (e) => {
223
205
  if (isPanning && canPan) {
224
206
  setPosition({
225
- x: e.clientX - startPanPosition.x,
226
- y: e.clientY - startPanPosition.y,
207
+ x: e.clientX - startPanPositionRef.current.x,
208
+ y: e.clientY - startPanPositionRef.current.y,
227
209
  });
228
210
  }
229
211
  };
@@ -13,9 +13,11 @@ interface PdfRendererProps {
13
13
  toolbarActions?: React.ReactNode;
14
14
  /** Start the zoom plugin at fit-to-page instead of the numeric default. */
15
15
  fitOnOpen?: boolean;
16
+ /** Search for this text once the document opens: highlighted, first hit in view. */
17
+ findText?: string;
16
18
  /** False when the host's own toolbar already has the Download button, so the
17
19
  * viewer does not show a second one. */
18
20
  showDownload?: boolean;
19
21
  }
20
- export declare function PdfRenderer({ fileContent, url, className, compact, fileName, toolbarActions, fitOnOpen, showDownload, }: PdfRendererProps): import("react").JSX.Element;
22
+ export declare function PdfRenderer({ fileContent, url, className, compact, fileName, toolbarActions, fitOnOpen, findText, showDownload, }: PdfRendererProps): import("react").JSX.Element;
21
23
  export {};
@@ -14,7 +14,7 @@ export function base64PdfContentToBlob(fileContent) {
14
14
  }
15
15
  return new Blob([bytes], { type: 'application/pdf' });
16
16
  }
17
- export function PdfRenderer({ fileContent, url, className, compact = false, fileName, toolbarActions, fitOnOpen, showDownload = true, }) {
17
+ export function PdfRenderer({ fileContent, url, className, compact = false, fileName, toolbarActions, fitOnOpen, findText, showDownload = true, }) {
18
18
  const tHardcodedUi = useTranslations('hardcodedUi');
19
19
  const [pdfUrl, setPdfUrl] = useState(null);
20
20
  const [status, setStatus] = useState('loading');
@@ -50,5 +50,5 @@ export function PdfRenderer({ fileContent, url, className, compact = false, file
50
50
  if (status === 'error' || !pdfUrl) {
51
51
  return (_jsxs("div", { className: cn('flex h-full w-full flex-col items-center justify-center gap-3 p-8 text-center', className), children: [_jsx("div", { className: "bg-muted flex h-10 w-10 items-center justify-center rounded-full", children: _jsx(AlertTriangle, { className: "text-muted-foreground h-4 w-4" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-foreground text-sm font-medium", children: tHardcodedUi.raw('componentsFileRenderersPdfRenderer.line277JsxTextFailedToLoadPdf') }), !compact && (_jsx("p", { className: "text-muted-foreground mt-1 text-xs", children: tHardcodedUi.raw('componentsFileRenderersPdfRenderer.line278JsxTextTheFileMayBeCorruptedOrInaccessible') }))] })] }));
52
52
  }
53
- return (_jsx(PDFViewer, { src: pdfUrl, fileName: fileName, showToolbar: !compact, showDownload: showDownload, showUpload: false, className: cn('h-full w-full', className), toolbarActions: toolbarActions, fitOnOpen: fitOnOpen }));
53
+ return (_jsx(PDFViewer, { src: pdfUrl, fileName: fileName, showToolbar: !compact, showDownload: showDownload, showUpload: false, className: cn('h-full w-full', className), toolbarActions: toolbarActions, fitOnOpen: fitOnOpen, findText: findText }));
54
54
  }
@@ -31,6 +31,9 @@ export type PDFViewerProps = {
31
31
  showUpload?: boolean;
32
32
  src?: string;
33
33
  toolbarActions?: React.ReactNode;
34
+ /** Search the document for this text once it opens: hits are highlighted and the
35
+ * first is scrolled into view, as if typed into the toolbar's search. */
36
+ findText?: string;
34
37
  pageClassName?: (pageNumber: number) => string | undefined;
35
38
  renderPageOverlay?: (props: PDFViewerPageOverlayProps) => React.ReactNode;
36
39
  onActivePageChange?: (pageNumber: number) => void;
@@ -240,7 +240,7 @@ function PDFViewerFileActionsMenu({ onUploadFile, showUpload = false, }) {
240
240
  const inputRef = React.useRef(null);
241
241
  if (!showUpload || !onUploadFile)
242
242
  return null;
243
- return (_jsxs(_Fragment, { children: [_jsx("input", { ref: inputRef, type: "file", accept: "application/pdf,.pdf", className: "sr-only", tabIndex: -1, onChange: (event) => {
243
+ return (_jsxs(_Fragment, { children: [_jsx("input", { ref: inputRef, type: "file", accept: "application/pdf,.pdf", "aria-label": tI18nComplete.raw('text865e89de78d9'), className: "sr-only", tabIndex: -1, onChange: (event) => {
244
244
  const nextFile = event.target.files?.[0];
245
245
  if (nextFile) {
246
246
  onUploadFile(nextFile);
@@ -282,7 +282,7 @@ function PDFViewerPageNumberControl({ activePage, controlsDisabled, numPages, on
282
282
  setIsEditing(true);
283
283
  }, children: displayPage })), _jsxs("span", { children: [tI18nComplete.raw('text28391d3bc64e'), " ", numPages || '–'] })] }));
284
284
  }
285
- function PDFViewerSearchControl({ documentId, controlsDisabled, }) {
285
+ function PDFViewerSearchControl({ documentId, controlsDisabled, initialSearch, }) {
286
286
  const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
287
287
  const { state, provides } = useSearch(documentId);
288
288
  const { provides: scroll } = useScroll(documentId);
@@ -323,6 +323,12 @@ function PDFViewerSearchControl({ documentId, controlsDisabled, }) {
323
323
  providesRef.current = provides;
324
324
  scrollRef.current = scroll;
325
325
  }, [provides, scroll]);
326
+ // A host's `findText`: search once the plugin is ready, and again when the text changes.
327
+ React.useEffect(() => {
328
+ const text = initialSearch?.trim();
329
+ if (text && provides && scroll)
330
+ setSearchDraft(text);
331
+ }, [initialSearch, provides, scroll]);
326
332
  const runSearch = React.useCallback((rawQuery) => {
327
333
  const query = rawQuery.trim();
328
334
  const requestId = searchRequestIdRef.current + 1;
@@ -922,7 +928,7 @@ function PDFViewerScroller({ documentId, pageRotationDeltas, basePageRotations,
922
928
  width: '100%',
923
929
  } })] }));
924
930
  }
925
- function PDFViewerInner({ viewerRef, pdfFile, documentId, document: pdfDocument, defaultZoom, className, fileName, showDownload, showToolbar, showRotateControls, showUpload, toolbarActions, pageClassName, renderPageOverlay, onActivePageChange, onPdfUpload, onPagePointerDown, onPagePointerMove, onPagePointerUp, onPagePointerCancel, onUploadFile, }) {
931
+ function PDFViewerInner({ viewerRef, pdfFile, documentId, document: pdfDocument, defaultZoom, className, fileName, showDownload, showToolbar, showRotateControls, showUpload, toolbarActions, findText, pageClassName, renderPageOverlay, onActivePageChange, onPdfUpload, onPagePointerDown, onPagePointerMove, onPagePointerUp, onPagePointerCancel, onUploadFile, }) {
926
932
  const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
927
933
  const { registry } = useRegistry();
928
934
  const { state: scrollState, provides: scroll } = useScroll(documentId);
@@ -1184,7 +1190,7 @@ function PDFViewerInner({ viewerRef, pdfFile, documentId, document: pdfDocument,
1184
1190
  pdfDocument?.pages[page.pageIndex]?.rotation ??
1185
1191
  normalizeRotation(0);
1186
1192
  const pageRotation = normalizeRotation(basePageRotation + (pageRotationDeltas.get(page.pageIndex) ?? 0));
1187
- return (_jsx(Rotate, { documentId: documentId, pageIndex: page.pageIndex, rotation: pageRotation, children: _jsxs(PagePointerProvider, { documentId: documentId, pageIndex: page.pageIndex, rotation: pageRotation, "data-pdf-viewer-page": pageNumber, className: cn('relative border border-transparent bg-transparent shadow-xs select-none selection:bg-transparent selection:text-inherit', pageClassName?.(pageNumber)), style: { backgroundColor: 'transparent' }, onPointerDown: (event) => onPagePointerDown?.(event, pageNumber), onPointerMove: (event) => onPagePointerMove?.(event, pageNumber), onPointerUp: (event) => onPagePointerUp?.(event, pageNumber), onPointerCancel: (event) => onPagePointerCancel?.(event, pageNumber), children: [_jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 bg-white" }), _jsx(RenderLayer, { documentId: documentId, pageIndex: page.pageIndex, scale: Math.min(currentZoomLevel, PAGE_BASE_RENDER_MAX_SCALE), dpr: PAGE_BASE_RENDER_DPR, className: "pointer-events-none absolute inset-0 h-full w-full object-fill opacity-100 blur-[0.35px] transition-none" }), _jsx(TilingLayer, { documentId: documentId, pageIndex: page.pageIndex, className: "pointer-events-none opacity-100 transition-none [&_img]:opacity-100 [&_img]:transition-none" }, `tiles-${page.pageIndex}-${pageRotation}`), _jsx(SearchLayer, { documentId: documentId, pageIndex: page.pageIndex, className: "pointer-events-none", highlightColor: "color-mix(in srgb, var(--kortix-yellow) 45%, transparent)", activeHighlightColor: "color-mix(in srgb, var(--kortix-orange) 55%, transparent)" }), _jsx(PDFViewerTextSelectionLayer, { documentId: documentId, pageIndex: page.pageIndex, scale: currentZoomLevel }), renderPageOverlay?.({
1193
+ return (_jsx(Rotate, { documentId: documentId, pageIndex: page.pageIndex, rotation: pageRotation, children: _jsxs(PagePointerProvider, { documentId: documentId, pageIndex: page.pageIndex, rotation: pageRotation, "data-pdf-viewer-page": pageNumber, className: cn('relative border border-transparent bg-transparent shadow-xs select-none selection:bg-transparent selection:text-inherit', pageClassName?.(pageNumber)), style: { backgroundColor: 'transparent' }, onPointerDown: (event) => onPagePointerDown?.(event, pageNumber), onPointerMove: (event) => onPagePointerMove?.(event, pageNumber), onPointerUp: (event) => onPagePointerUp?.(event, pageNumber), onPointerCancel: (event) => onPagePointerCancel?.(event, pageNumber), children: [_jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 grid place-items-center bg-white text-neutral-400", children: _jsx(Spinner, { className: "size-5" }) }), _jsx(RenderLayer, { documentId: documentId, pageIndex: page.pageIndex, scale: Math.min(currentZoomLevel, PAGE_BASE_RENDER_MAX_SCALE), dpr: PAGE_BASE_RENDER_DPR, className: "pointer-events-none absolute inset-0 h-full w-full object-fill opacity-100 blur-[0.35px] transition-none" }), _jsx(TilingLayer, { documentId: documentId, pageIndex: page.pageIndex, className: "pointer-events-none opacity-100 transition-none [&_img]:opacity-100 [&_img]:transition-none" }, `tiles-${page.pageIndex}-${pageRotation}`), _jsx(SearchLayer, { documentId: documentId, pageIndex: page.pageIndex, className: "pointer-events-none", highlightColor: "color-mix(in srgb, var(--kortix-yellow) 45%, transparent)", activeHighlightColor: "color-mix(in srgb, var(--kortix-orange) 55%, transparent)" }), _jsx(PDFViewerTextSelectionLayer, { documentId: documentId, pageIndex: page.pageIndex, scale: currentZoomLevel }), renderPageOverlay?.({
1188
1194
  pageNumber,
1189
1195
  pageWidth: page.width,
1190
1196
  pageHeight: page.height,
@@ -1212,7 +1218,7 @@ function PDFViewerInner({ viewerRef, pdfFile, documentId, document: pdfDocument,
1212
1218
  }, children: _jsx(CircleMinus, { className: "size-4" }) }) }), _jsxs(Select, { value: String(currentZoomLevel), onValueChange: (value) => zoom?.requestZoom(Number(value)), disabled: controlsDisabled, modal: false, children: [_jsx(SelectTrigger, { size: "sm", className: "w-[84px] min-w-[84px] tabular-nums", children: _jsxs(SelectValue, { placeholder: tI18nComplete.raw('text509c517ede79'), children: [Math.round(currentZoomLevel * 100), "%"] }) }), _jsx(SelectContent, { alignItemWithTrigger: false, children: ZOOM_OPTIONS.map((option) => (_jsxs(SelectItem, { value: String(option), children: [Math.round(option * 100), "%"] }, option))) })] }), _jsx(ToolbarTooltip, { label: tI18nComplete.raw('text0e47f09a748f'), children: _jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", className: "transition-transform active:scale-[0.96]", "aria-label": tI18nComplete.raw('text0e47f09a748f'), disabled: controlsDisabled || currentZoomLevel >= ZOOM_OPTIONS[ZOOM_OPTIONS.length - 1], onClick: () => {
1213
1219
  const nextZoom = ZOOM_OPTIONS.find((option) => option > currentZoomLevel);
1214
1220
  zoom?.requestZoom(nextZoom ?? ZOOM_OPTIONS[ZOOM_OPTIONS.length - 1]);
1215
- }, children: _jsx(CirclePlus, { className: "size-4" }) }) })] }), _jsx(Separator, { orientation: "vertical", className: "mx-1 h-4 self-center" }), _jsx(PDFViewerSearchControl, { documentId: documentId, controlsDisabled: controlsDisabled }, documentId), toolbarActions ? (_jsxs(_Fragment, { children: [_jsx(Separator, { orientation: "vertical", className: "mx-1 h-4 self-center" }), toolbarActions] })) : null, showDownload || showUpload ? (_jsxs(_Fragment, { children: [_jsx(Separator, { orientation: "vertical", className: "mx-1 h-4 self-center" }), _jsx(PDFViewerFileActionsMenu, { onUploadFile: handleUpload, showUpload: showUpload }), showDownload ? (_jsx(ViewerDownloadButton, { disabled: controlsDisabled, pending: isPreparingDownload, onDownload: () => void handleDownload() })) : null] })) : null] }) })] })) : null, _jsxs("div", { ref: viewerShellRef, className: "bg-muted/30 relative flex min-h-0 flex-1 overflow-hidden", children: [isLoading ? (_jsx(PDFViewerLoadingSkeleton, { sidebarInline: sidebarInline, sidebarOpen: sidebarOpen })) : null, _jsxs("div", { className: "flex h-full max-h-full min-h-0 w-full flex-1 overflow-hidden", children: [_jsx(DocumentViewerThumbnailSidebar, { closedInlineClassName: THUMBNAIL_SIDEBAR_CLOSED_CLASS, inline: sidebarInline, open: thumbnailSidebarVisible, widthClassName: THUMBNAIL_SIDEBAR_WIDTH_CLASS, children: thumbnailSidebarVisible ? (_jsx(PDFViewerThumbnails, { basePageRotations: basePageRotations, documentId: documentId, activePage: activePage, pageCount: numPages, pageRotationDeltas: pageRotationDeltas, pdfDocument: pdfDocument, selectedPageIndexes: selectedPageIndexes, onSelectPage: selectThumbnailPage })) : null }), _jsxs(PDFViewerScrollAreaViewport, { documentId: documentId, className: "relative h-full max-h-full min-h-0 min-w-0 flex-1", children: [_jsx(PDFViewerViewportBridge, { viewportElementRef: viewportElementRef }), _jsx(PDFViewerSelectionCopyShortcut, { documentId: documentId }), _jsx(PDFViewerSelectionReleaseGuard, { documentId: documentId }), _jsx(GlobalPointerProvider, { documentId: documentId, children: _jsx(PDFViewerScroller, { basePageRotations: basePageRotations, documentId: documentId, pageRotationDeltas: pageRotationDeltas, renderPage: renderPage }) }), _jsx(CopyToClipboard, {})] })] })] })] }));
1221
+ }, children: _jsx(CirclePlus, { className: "size-4" }) }) })] }), _jsx(Separator, { orientation: "vertical", className: "mx-1 h-4 self-center" }), _jsx(PDFViewerSearchControl, { documentId: documentId, controlsDisabled: controlsDisabled, initialSearch: findText }, documentId), toolbarActions ? (_jsxs(_Fragment, { children: [_jsx(Separator, { orientation: "vertical", className: "mx-1 h-4 self-center" }), toolbarActions] })) : null, showDownload || showUpload ? (_jsxs(_Fragment, { children: [_jsx(Separator, { orientation: "vertical", className: "mx-1 h-4 self-center" }), _jsx(PDFViewerFileActionsMenu, { onUploadFile: handleUpload, showUpload: showUpload }), showDownload ? (_jsx(ViewerDownloadButton, { disabled: controlsDisabled, pending: isPreparingDownload, onDownload: () => void handleDownload() })) : null] })) : null] }) })] })) : null, _jsxs("div", { ref: viewerShellRef, className: "bg-muted/30 relative flex min-h-0 flex-1 overflow-hidden", children: [isLoading ? (_jsx(PDFViewerLoadingSkeleton, { sidebarInline: sidebarInline, sidebarOpen: sidebarOpen })) : null, _jsxs("div", { className: "flex h-full max-h-full min-h-0 w-full flex-1 overflow-hidden", children: [_jsx(DocumentViewerThumbnailSidebar, { closedInlineClassName: THUMBNAIL_SIDEBAR_CLOSED_CLASS, inline: sidebarInline, open: thumbnailSidebarVisible, widthClassName: THUMBNAIL_SIDEBAR_WIDTH_CLASS, children: thumbnailSidebarVisible ? (_jsx(PDFViewerThumbnails, { basePageRotations: basePageRotations, documentId: documentId, activePage: activePage, pageCount: numPages, pageRotationDeltas: pageRotationDeltas, pdfDocument: pdfDocument, selectedPageIndexes: selectedPageIndexes, onSelectPage: selectThumbnailPage })) : null }), _jsxs(PDFViewerScrollAreaViewport, { documentId: documentId, className: "relative h-full max-h-full min-h-0 min-w-0 flex-1", children: [_jsx(PDFViewerViewportBridge, { viewportElementRef: viewportElementRef }), _jsx(PDFViewerSelectionCopyShortcut, { documentId: documentId }), _jsx(PDFViewerSelectionReleaseGuard, { documentId: documentId }), _jsx(GlobalPointerProvider, { documentId: documentId, children: _jsx(PDFViewerScroller, { basePageRotations: basePageRotations, documentId: documentId, pageRotationDeltas: pageRotationDeltas, renderPage: renderPage }) }), _jsx(CopyToClipboard, {})] })] })] })] }));
1216
1222
  }
1217
1223
  function PDFViewerDocumentLoader({ pdfFile, onDocumentLoadSuccess, ...innerProps }) {
1218
1224
  const { provides: documentManager } = useDocumentManagerCapability();
@@ -1288,7 +1294,7 @@ function PDFViewerDocumentLoader({ pdfFile, onDocumentLoadSuccess, ...innerProps
1288
1294
  }
1289
1295
  return (_jsx(PDFViewerInner, { ...innerProps, pdfFile: pdfFile, documentId: activeDocumentId, document: document }, activeDocumentId));
1290
1296
  }
1291
- export const PDFViewer = React.forwardRef(function PDFViewer({ className, defaultZoom = DEFAULT_ZOOM, fitOnOpen, fileName, showDownload = true, showRotateControls = true, showToolbar = true, showUpload = true, src, toolbarActions, pageClassName, renderPageOverlay, onActivePageChange, onDocumentLoadSuccess, onPdfUpload, onPagePointerDown, onPagePointerMove, onPagePointerUp, onPagePointerCancel, }, ref) {
1297
+ export const PDFViewer = React.forwardRef(function PDFViewer({ className, defaultZoom = DEFAULT_ZOOM, fitOnOpen, fileName, showDownload = true, showRotateControls = true, showToolbar = true, showUpload = true, src, toolbarActions, findText, pageClassName, renderPageOverlay, onActivePageChange, onDocumentLoadSuccess, onPdfUpload, onPagePointerDown, onPagePointerMove, onPagePointerUp, onPagePointerCancel, }, ref) {
1292
1298
  const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
1293
1299
  const { engine, error: engineError } = useSharedPdfEngine();
1294
1300
  const [uploadedPdfFile, setUploadedPdfFile] = React.useState(() => ({ src, url: null }));
@@ -1348,5 +1354,8 @@ export const PDFViewer = React.forwardRef(function PDFViewer({ className, defaul
1348
1354
  if (!engine) {
1349
1355
  return (_jsxs("div", { "data-slot": "pdf-viewer", className: cn('bg-background relative flex h-full max-h-full min-h-0 w-full flex-col overflow-hidden', className), children: [showToolbar ? _jsx("div", { className: "bg-background min-h-12 border-b" }) : null, _jsx("div", { className: "relative min-h-0 flex-1", children: _jsx(PDFViewerLoadingSkeleton, { sidebarInline: true, sidebarOpen: false }) })] }));
1350
1356
  }
1351
- return (_jsx(EmbedPDF, { engine: engine, plugins: plugins, children: _jsx(PDFViewerDocumentLoader, { viewerRef: ref, pdfFile: pdfFile, defaultZoom: defaultZoom, className: className, fileName: fileName, showDownload: showDownload, showToolbar: showToolbar, showRotateControls: showRotateControls, showUpload: showUpload, toolbarActions: toolbarActions, pageClassName: pageClassName, renderPageOverlay: renderPageOverlay, onActivePageChange: onActivePageChange, onDocumentLoadSuccess: onDocumentLoadSuccess, onPdfUpload: onPdfUpload, onPagePointerDown: onPagePointerDown, onPagePointerMove: onPagePointerMove, onPagePointerUp: onPagePointerUp, onPagePointerCancel: onPagePointerCancel, onUploadFile: handleUploadFile }) }));
1357
+ return (_jsx(EmbedPDF, { engine: engine, plugins: plugins, children: _jsx(PDFViewerDocumentLoader, { viewerRef: ref, pdfFile: pdfFile,
1358
+ // The same level the plugin starts at: a numeric default here would undo `fitOnOpen`
1359
+ // as soon as the document loads (the page opened at 100% and overflowed a narrow pane).
1360
+ defaultZoom: selectPdfZoomLevel(fitOnOpen, defaultZoom), className: className, fileName: fileName, showDownload: showDownload, showToolbar: showToolbar, showRotateControls: showRotateControls, showUpload: showUpload, toolbarActions: toolbarActions, findText: findText, pageClassName: pageClassName, renderPageOverlay: renderPageOverlay, onActivePageChange: onActivePageChange, onDocumentLoadSuccess: onDocumentLoadSuccess, onPdfUpload: onPdfUpload, onPagePointerDown: onPagePointerDown, onPagePointerMove: onPagePointerMove, onPagePointerUp: onPagePointerUp, onPagePointerCancel: onPagePointerCancel, onUploadFile: handleUploadFile }) }));
1352
1361
  });
@@ -19,11 +19,9 @@ export function FullScreenPresentationViewer({ isOpen, onClose, presentationName
19
19
  const [metadata, setMetadata] = useState(null);
20
20
  const [currentSlide, setCurrentSlide] = useState(initialSlide);
21
21
  const [isLoading, setIsLoading] = useState(false);
22
- const [error, setError] = useState(null);
23
22
  const [retryAttempt, setRetryAttempt] = useState(0);
24
23
  const retryTimeoutRef = useRef(null);
25
24
  const hasLoadedRef = useRef(false);
26
- const [showControls, setShowControls] = useState(true);
27
25
  const [showEditor, setShowEditor] = useState(false);
28
26
  const [isDownloadingPDF, setIsDownloadingPDF] = useState(false);
29
27
  const [isDownloadingPPTX, setIsDownloadingPPTX] = useState(false);
@@ -57,7 +55,6 @@ export function FullScreenPresentationViewer({ isOpen, onClose, presentationName
57
55
  return;
58
56
  }
59
57
  setIsLoading(true);
60
- setError(null);
61
58
  setRetryAttempt(retryCount);
62
59
  try {
63
60
  // Sanitize the presentation name to match backend directory creation
@@ -218,12 +215,6 @@ export function FullScreenPresentationViewer({ isOpen, onClose, presentationName
218
215
  document.addEventListener('keydown', handleKeyDown, true);
219
216
  return () => document.removeEventListener('keydown', handleKeyDown, true);
220
217
  }, [isOpen, goToNextSlide, goToPreviousSlide, totalSlides, onClose, showEditor]);
221
- // Always show controls
222
- useEffect(() => {
223
- if (isOpen) {
224
- setShowControls(true);
225
- }
226
- }, [isOpen]);
227
218
  // Download handlers
228
219
  const handleDownload = async (format) => {
229
220
  if (isDownloadRestricted) {
@@ -322,7 +313,7 @@ export function FullScreenPresentationViewer({ isOpen, onClose, presentationName
322
313
  });
323
314
  SlideIframeComponent.displayName = 'SlideIframeComponent';
324
315
  return SlideIframeComponent;
325
- }, [sandboxUrl, refreshTimestamp, showEditor, subdomainOpts, tHardcodedUi]);
316
+ }, [sandboxUrl, refreshTimestamp, showEditor, subdomainOpts, tHardcodedUi, tI18nComplete]);
326
317
  if (!isOpen)
327
318
  return null;
328
319
  // Render slide iframe with proper scaling
@@ -331,7 +322,7 @@ export function FullScreenPresentationViewer({ isOpen, onClose, presentationName
331
322
  ? tHardcodedUi.raw('i18nComplete.textbd0b7be215ce')
332
323
  : tHardcodedUi.raw('i18nComplete.text8268dd9ea939'), onClick: () => setShowEditor(!showEditor), children: showEditor ? (_jsx(Presentation, { className: "h-3.5 w-3.5" })) : (_jsx(Edit, { className: "h-3.5 w-3.5" })) }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "sm", className: "w-8 p-0", title: tHardcodedUi.raw('componentsThreadToolViewsPresentationToolsFullscreenpresentationviewer.line485JsxAttrTitleExportPresentation'), disabled: isDownloadingPDF || isDownloadingPPTX, children: isDownloadingPDF || isDownloadingPPTX ? (_jsx(KortixLoader, { customSize: 14 })) : (_jsx(Download, { className: "h-3.5 w-3.5" })) }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-32", children: [_jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => handleDownload(DownloadFormat.PDF), disabled: isDownloadingPDF, children: [_jsx(FileText, { className: "mr-2 h-4 w-4" }), "PDF"] }), _jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => handleDownload(DownloadFormat.PPTX), disabled: isDownloadingPPTX, children: [_jsx(Presentation, { className: "mr-2 h-4 w-4" }), "PPTX"] })] })] }), _jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, className: "h-8 w-8 p-0", title: tHardcodedUi.raw('componentsThreadToolViewsPresentationToolsFullscreenpresentationviewer.line529JsxAttrTitleCloseFullScreen'), children: _jsx(X, { className: "h-3.5 w-3.5" }) })] })] }) }), _jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center bg-zinc-100 p-2 dark:bg-zinc-900", children: isLoading || !currentSlideData ? (_jsxs("div", { className: "text-center", children: [_jsx(KortixLoader, { size: "large", className: "mx-auto mb-4" }), _jsx("p", { className: "text-zinc-700 dark:text-zinc-300", children: retryAttempt > 0
333
324
  ? tI18nComplete('text3df439a7a73b', { value0: retryAttempt + 1 })
334
- : tHardcodedUi.raw('i18nComplete.text1ac9fe8126fa') })] })) : currentSlideData ? (_jsxs("div", { className: "flex h-full w-full flex-col", children: [_jsx("div", { className: "flex-1 overflow-hidden rounded-xl bg-transparent", style: { aspectRatio: '16 / 9' }, children: renderSlide }), _jsxs("div", { className: "mt-3 flex items-center justify-between px-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setCurrentSlide(1), disabled: currentSlide <= 1, className: "h-8 w-8 p-0 disabled:opacity-50", children: _jsx(SkipBack, { className: "h-3.5 w-3.5" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: goToPreviousSlide, disabled: currentSlide <= 1, className: "h-8 w-8 p-0 disabled:opacity-50", children: _jsx(ChevronLeft, { className: "h-4 w-4" }) })] }), _jsx("div", { className: "flex items-center", children: _jsx("div", { className: "flex gap-2", children: slides.map((slide) => (_jsx("button", { onClick: () => setCurrentSlide(slide.number), className: cn('h-2.5 w-2.5 rounded-full transition-colors duration-200', slide.number === currentSlide
325
+ : tHardcodedUi.raw('i18nComplete.text1ac9fe8126fa') })] })) : currentSlideData ? (_jsxs("div", { className: "flex h-full w-full flex-col", children: [_jsx("div", { className: "flex-1 overflow-hidden rounded-xl bg-transparent", style: { aspectRatio: '16 / 9' }, children: renderSlide }), _jsxs("div", { className: "mt-3 flex items-center justify-between px-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setCurrentSlide(1), disabled: currentSlide <= 1, className: "h-8 w-8 p-0 disabled:opacity-50", children: _jsx(SkipBack, { className: "h-3.5 w-3.5" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: goToPreviousSlide, disabled: currentSlide <= 1, className: "h-8 w-8 p-0 disabled:opacity-50", children: _jsx(ChevronLeft, { className: "h-4 w-4" }) })] }), _jsx("div", { className: "flex items-center", children: _jsx("div", { className: "flex gap-2", children: slides.map((slide) => (_jsx("button", { "aria-label": `${tHardcodedUi.raw('i18nComplete.text6bb4c1177681')} ${slide.number}`, onClick: () => setCurrentSlide(slide.number), className: cn('h-2.5 w-2.5 rounded-full transition-colors duration-200', slide.number === currentSlide
335
326
  ? 'bg-black dark:bg-white'
336
327
  : 'bg-zinc-300 hover:bg-zinc-400 dark:bg-zinc-600 dark:hover:bg-zinc-500') }, slide.number))) }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: goToNextSlide, disabled: currentSlide >= totalSlides, className: "h-8 w-8 p-0 disabled:opacity-50", children: _jsx(ChevronRight, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setCurrentSlide(totalSlides), disabled: currentSlide >= totalSlides, className: "h-8 w-8 p-0 disabled:opacity-50", children: _jsx(SkipForward, { className: "h-3.5 w-3.5" }) })] })] })] })) : (_jsx("div", { className: "text-center", children: _jsx("p", { className: "text-zinc-700 dark:text-zinc-300", children: tHardcodedUi.raw('componentsThreadToolViewsPresentationToolsFullscreenpresentationviewer.line621JsxTextNoSlideFound') }) })) })] }));
337
328
  }
@@ -692,7 +692,7 @@ export function ShowCarousel({ items, LocalhostPreview, onIndexChange, fill = fa
692
692
  const currentItem = items[currentIndex];
693
693
  if (!currentItem)
694
694
  return null;
695
- return (_jsxs("div", { className: cn('outline-none', fill && 'flex h-full flex-col'),
695
+ return (_jsxs("div", { role: "group", className: cn('outline-none', fill && 'flex h-full flex-col'),
696
696
  // -1: a click on the content focuses the card, so ←/→ page it next,
697
697
  // without adding a Tab stop. Tab still reaches the chevrons inside.
698
698
  tabIndex: fill || count < 2 ? undefined : -1, onKeyDown: fill || count < 2 ? undefined : (e) => handleArrowKey(e), children: [_jsx("div", { className: cn(fill ? 'min-h-0 flex-1 overflow-hidden' : 'min-h-[420px]'), children: currentItem.status === 'pending' ? (_jsxs("div", { className: cn('flex items-center justify-center gap-3', fill ? 'h-full' : 'min-h-[420px]'), children: [_jsx(Loading, { className: "text-muted-foreground size-4" }), _jsx(TextShimmer, { duration: 1, spread: 2, className: "text-sm", children: tHardcodedUi.raw('componentsSessionToolRenderers.line4935JsxTextPreparingOutput') })] })) : (_jsx(ShowContentRenderer, { type: currentItem.type, title: currentItem.title, description: currentItem.description, path: currentItem.path, url: currentItem.url, content: currentItem.content, language: currentItem.language, aspectRatio: currentItem.aspect_ratio, attachment: currentItem.attachment, LocalhostPreview: LocalhostPreview, toolbarActions: toolbarActions, fill: fill })) }), count > 1 && !hideNav && (_jsxs("div", { className: "border-border flex shrink-0 items-center gap-2 border-t px-2 py-1.5 pr-3.5", children: [_jsxs("div", { className: "flex shrink-0 items-center", children: [_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: prev, className: "hit-area-2 hit-area-r-0 transition-transform active:scale-[0.96]", disabled: currentIndex === 0, "aria-label": tI18nComplete.raw('text81b35f1b4332'), children: _jsx(ChevronLeft, { className: "size-4" }) }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: next, className: "hit-area-2 hit-area-l-0 transition-transform active:scale-[0.96]", disabled: currentIndex >= count - 1, "aria-label": tI18nComplete.raw('text1e47d4f7a1a3'), children: _jsx(ChevronRight, { className: "size-4" }) })] }), _jsx(FadedScrollArea, { ref: stripRef, orientation: "horizontal", fadeColor: "from-secondary", className: "min-w-0 flex-1 overscroll-x-contain", children: _jsx("div", { className: "flex w-max min-w-0 items-center gap-1", children: items.map((item, i) => {