@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
@@ -138,7 +138,11 @@ function ComposerForm({ projectId, onOpenChange, onCreated, initialAgent = null,
138
138
  reveal(errorRef.current);
139
139
  }, [error]);
140
140
  const create = useMutation({
141
- onSettled: () => setFrozenApp(null),
141
+ onMutate: () => onBusyChange(true),
142
+ onSettled: () => {
143
+ setFrozenApp(null);
144
+ onBusyChange(false);
145
+ },
142
146
  mutationFn: async () => {
143
147
  const trimmedName = name.trim();
144
148
  const slug = slugify(draft.customId.trim() || trimmedName);
@@ -251,7 +255,6 @@ function ComposerForm({ projectId, onOpenChange, onCreated, initialAgent = null,
251
255
  setError(message);
252
256
  },
253
257
  });
254
- useEffect(() => onBusyChange(create.isPending), [create.isPending, onBusyChange]);
255
258
  const submit = () => {
256
259
  setError(null);
257
260
  setTaken(null);
@@ -3,8 +3,6 @@ import type { ProjectTrigger } from '@kortix/sdk';
3
3
  import { type ReactNode } from 'react';
4
4
  /** "next Mon 09:00", "next 08:45" today, "On event" for webhooks and app events, "Paused" while paused. */
5
5
  export declare function nextRunLabel(trigger: Pick<ProjectTrigger, 'type' | 'enabled' | 'next_fire_at' | 'next_cron_at' | 'auto_paused_at'>, t: UiTranslator, locale: string, now?: number): string;
6
- /** "2d ago", "9h ago", "3w ago" in the viewer's locale, or "Never run". */
7
- export declare function lastRunLabel(trigger: Pick<ProjectTrigger, 'last_fired_at' | 'event'>, t: UiTranslator, locale: string, now?: number): string;
8
6
  export declare function TriggerList({ triggers, groups, eventNames, selectedSlug, runningSlug, onOpen, selection, projectPaused, }: {
9
7
  triggers: ProjectTrigger[];
10
8
  /** The trigger whose Run now is in flight: its row shows the session busy mark. */
@@ -9,6 +9,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
9
9
  import { Checkbox } from '../../../primitives/checkbox.js';
10
10
  import { SessionDotMatrix } from '../../../primitives/dot-matrix/session-dot-matrix.js';
11
11
  import { useLocale, useTranslations } from '../../../i18n/index.js';
12
+ import { lastRunLabel } from '../../../internal/lib/trigger-clock.js';
12
13
  import { cn } from '../../../internal/lib/utils.js';
13
14
  import { Fragment } from 'react';
14
15
  import { describeNextRun, describeWhen, triggerName, triggerSubtitle } from './schedule-copy.js';
@@ -35,24 +36,6 @@ export function nextRunLabel(trigger, t, locale, now = Date.now()) {
35
36
  : date.toLocaleDateString(locale, { month: 'short', day: 'numeric' });
36
37
  return t('text27a2c8b3a0d2', { value0: day ? `${day} ${time}` : time });
37
38
  }
38
- /** "2d ago", "9h ago", "3w ago" in the viewer's locale, or "Never run". */
39
- export function lastRunLabel(trigger, t, locale, now = Date.now()) {
40
- const iso = trigger.last_fired_at ?? trigger.event?.last_event_at ?? null;
41
- const at = iso ? Date.parse(iso) : Number.NaN;
42
- if (!Number.isFinite(at))
43
- return t.raw('text3d40a69d3160');
44
- const rtf = new Intl.RelativeTimeFormat(locale, { style: 'narrow', numeric: 'auto' });
45
- const min = Math.floor(Math.max(0, now - at) / 60_000);
46
- if (min < 1)
47
- return rtf.format(0, 'second');
48
- if (min < 60)
49
- return rtf.format(-min, 'minute');
50
- if (min < 24 * 60)
51
- return rtf.format(-Math.floor(min / 60), 'hour');
52
- if (min < 7 * 24 * 60)
53
- return rtf.format(-Math.floor(min / (24 * 60)), 'day');
54
- return rtf.format(-Math.floor(min / (7 * 24 * 60)), 'week');
55
- }
56
39
  export function TriggerList({ triggers, groups, eventNames, selectedSlug, runningSlug = null, onOpen, selection = null, projectPaused = false, }) {
57
40
  const row = (trigger) => (_jsx(TriggerRow, { trigger: trigger, eventNames: eventNames, selected: !selection && trigger.slug === selectedSlug, running: trigger.slug === runningSlug, onOpen: () => onOpen(trigger), checked: selection ? selection.selected.has(trigger.slug) : null, projectPaused: projectPaused, onToggle: (range) => selection?.onToggle(trigger, range) }, trigger.slug));
58
41
  return (
@@ -1,41 +1,10 @@
1
+ import { type ClockState } from '../../../internal/lib/trigger-clock.js';
1
2
  import type { ProjectTrigger } from '@kortix/sdk';
2
- export type ClockState = 'ok' | 'failed' | 'needs_connection' | 'pending' | 'paused' | 'never';
3
3
  /**
4
4
  * The health the clock shows: the badge state, with "live" split by whether
5
5
  * it ever ran. `projectPaused` (the project-wide switch) pauses every clock.
6
6
  */
7
7
  export declare function clockState(trigger: ProjectTrigger, projectPaused?: boolean, running?: boolean): ClockState;
8
- /**
9
- * ONE tone per state drives both the reflection and the dot, so they cannot
10
- * disagree. Three colours, always full strength: green = live (running, ran,
11
- * or not run yet), red = failed, yellow = paused or waiting on setup.
12
- */
13
- export declare const CLOCK_TONE: {
14
- readonly ok: {
15
- readonly reflection: "text-kortix-green";
16
- readonly dot: "bg-kortix-green";
17
- };
18
- readonly never: {
19
- readonly reflection: "text-kortix-green";
20
- readonly dot: "bg-kortix-green";
21
- };
22
- readonly failed: {
23
- readonly reflection: "text-kortix-red";
24
- readonly dot: "bg-kortix-red";
25
- };
26
- readonly needs_connection: {
27
- readonly reflection: "text-kortix-yellow";
28
- readonly dot: "bg-kortix-yellow";
29
- };
30
- readonly pending: {
31
- readonly reflection: "text-kortix-yellow";
32
- readonly dot: "bg-kortix-yellow";
33
- };
34
- readonly paused: {
35
- readonly reflection: "text-kortix-yellow";
36
- readonly dot: "bg-kortix-yellow";
37
- };
38
- };
39
8
  export declare function TriggerTile({ trigger, projectPaused, running, surface, }: {
40
9
  trigger: ProjectTrigger;
41
10
  /** The project-wide pause switch is on. */
@@ -13,6 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  */
14
14
  import { REFLECTION_MASK } from '../../../primitives/toast.js';
15
15
  import { useTranslations } from '../../../i18n/index.js';
16
+ import { CLOCK_TONE } from '../../../internal/lib/trigger-clock.js';
16
17
  import { cn } from '../../../internal/lib/utils.js';
17
18
  import { ClockIcon } from '@phosphor-icons/react';
18
19
  import { triggerBadgeState } from './schedule-copy.js';
@@ -34,22 +35,6 @@ export function clockState(trigger, projectPaused = false, running = false) {
34
35
  return state;
35
36
  return trigger.last_fired_at || trigger.event?.last_event_at ? 'ok' : 'never';
36
37
  }
37
- const GREEN = { reflection: 'text-kortix-green', dot: 'bg-kortix-green' };
38
- const RED = { reflection: 'text-kortix-red', dot: 'bg-kortix-red' };
39
- const YELLOW = { reflection: 'text-kortix-yellow', dot: 'bg-kortix-yellow' };
40
- /**
41
- * ONE tone per state drives both the reflection and the dot, so they cannot
42
- * disagree. Three colours, always full strength: green = live (running, ran,
43
- * or not run yet), red = failed, yellow = paused or waiting on setup.
44
- */
45
- export const CLOCK_TONE = {
46
- ok: GREEN,
47
- never: GREEN,
48
- failed: RED,
49
- needs_connection: YELLOW,
50
- pending: YELLOW,
51
- paused: YELLOW,
52
- };
53
38
  const LABEL = {
54
39
  ok: LABEL_KEY.live,
55
40
  failed: LABEL_KEY.error,
@@ -143,7 +143,7 @@ function Connection({ projectId, app, draft, patch, setDraft, canConnect, }) {
143
143
  */
144
144
  function EventSettings({ fields, draft, errors, onChange, }) {
145
145
  const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
146
- const [open, setOpen] = useState(fields.some((f) => f.required && f.kind !== 'boolean'));
146
+ const [open, setOpen] = useState(() => fields.some((f) => f.required && f.kind !== 'boolean'));
147
147
  const hasProblem = Object.keys(errors).length > 0;
148
148
  return (_jsxs(Disclosure, { className: "group", open: open || hasProblem, onOpenChange: setOpen, children: [_jsx(FoldTrigger, { children: tI18nComplete.raw('text6abe3b60f184') }), _jsx(DisclosureContent, { children: _jsxs("div", { className: "space-y-3 pt-3", children: [_jsx("p", { className: "text-muted-foreground text-xs leading-relaxed text-pretty", children: tI18nComplete.raw('text9f2c75e76be9') }), _jsx(EventConfigForm, { fields: fields, draft: draft, errors: errors, onChange: onChange })] }) })] }));
149
149
  }
@@ -24,19 +24,18 @@ export function splitablePatch(patch) {
24
24
  export function DiffView(props) {
25
25
  const isDark = useIsDark();
26
26
  const themeType = isDark ? 'dark' : 'light';
27
+ const patchInput = 'patch' in props ? props.patch : null;
28
+ const beforeName = 'before' in props ? props.before.name : '';
29
+ const beforeContents = 'before' in props ? props.before.contents : '';
30
+ const afterName = 'after' in props ? props.after.name : '';
31
+ const afterContents = 'after' in props ? props.after.contents : '';
27
32
  const patch = useMemo(() => {
28
33
  // `createTwoFilesPatch` writes no `new file mode` / `deleted file mode`
29
34
  // line, so only the patch path can be one-sided.
30
- if ('patch' in props)
31
- return splitablePatch(props.patch);
32
- return createTwoFilesPatch(props.before.name, props.after.name, props.before.contents, props.after.contents, '', '');
33
- }, [
34
- 'patch' in props ? props.patch : null,
35
- 'before' in props ? props.before.name : null,
36
- 'before' in props ? props.before.contents : null,
37
- 'after' in props ? props.after.name : null,
38
- 'after' in props ? props.after.contents : null,
39
- ]);
35
+ if (patchInput !== null)
36
+ return splitablePatch(patchInput);
37
+ return createTwoFilesPatch(beforeName, afterName, beforeContents, afterContents, '', '');
38
+ }, [patchInput, beforeName, beforeContents, afterName, afterContents]);
40
39
  const options = useMemo(() => ({
41
40
  theme: { dark: SHIKI_THEME_DARK, light: SHIKI_THEME_LIGHT },
42
41
  themeType,
@@ -477,8 +477,7 @@ export function CodeEditor({ content, originalContent, hasUnsavedChanges: extern
477
477
  }
478
478
  return exts;
479
479
  }, [langExtension, diagExt]);
480
- const SaveButton = () => {
481
- const tHardcodedUi = useTranslations('hardcodedUi');
480
+ const renderSaveButton = () => {
482
481
  if (readOnly || !onSave)
483
482
  return null;
484
483
  switch (saveState) {
@@ -494,7 +493,7 @@ export function CodeEditor({ content, originalContent, hasUnsavedChanges: extern
494
493
  };
495
494
  return (_jsxs("div", { className: cn('flex max-w-full flex-col', readOnly
496
495
  ? 'min-h-full' // For read-only, fill at least one viewport so the editor background doesn't end at the last line
497
- : 'h-full max-h-full overflow-hidden', className), style: readOnly ? undefined : { contain: 'strict' }, children: [!readOnly && showHeader && (_jsxs("div", { className: "bg-muted/30 flex max-w-full min-w-0 shrink-0 items-center justify-between border-b px-3 py-1.5", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [_jsx(SaveButton, {}), hasChanges && (_jsx(TooltipProvider, { delayDuration: 300, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "sm", onClick: handleDiscard, className: "text-muted-foreground hover:text-destructive h-7 w-7 p-0", children: _jsx(RotateCcw, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { side: "bottom", children: tHardcodedUi.raw('componentsFileEditorsCodeEditor.line719JsxTextDiscardChanges') })] }) })), hasChanges && (_jsxs("div", { className: "flex items-center gap-1.5 rounded-md bg-amber-50 px-2 py-1 text-xs text-amber-600 dark:bg-amber-900/20 dark:text-amber-500", children: [_jsxs("span", { className: "relative flex h-2 w-2", children: [_jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-amber-400 opacity-75" }), _jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-amber-500" })] }), _jsx("span", { className: "font-semibold", children: tHardcodedUi.raw('i18nComplete.text6250d5725dda') })] }))] }), _jsx(Badge, { variant: "outline", className: "font-mono text-xs", children: language })] })), _jsx("div", { ref: editorContainerRef, className: cn('bg-background w-full max-w-full', editorClassName, readOnly
496
+ : 'h-full max-h-full overflow-hidden', className), style: readOnly ? undefined : { contain: 'strict' }, children: [!readOnly && showHeader && (_jsxs("div", { className: "bg-muted/30 flex max-w-full min-w-0 shrink-0 items-center justify-between border-b px-3 py-1.5", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [renderSaveButton(), hasChanges && (_jsx(TooltipProvider, { delayDuration: 300, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "sm", onClick: handleDiscard, className: "text-muted-foreground hover:text-destructive h-7 w-7 p-0", children: _jsx(RotateCcw, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { side: "bottom", children: tHardcodedUi.raw('componentsFileEditorsCodeEditor.line719JsxTextDiscardChanges') })] }) })), hasChanges && (_jsxs("div", { className: "flex items-center gap-1.5 rounded-md bg-amber-50 px-2 py-1 text-xs text-amber-600 dark:bg-amber-900/20 dark:text-amber-500", children: [_jsxs("span", { className: "relative flex h-2 w-2", children: [_jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-amber-400 opacity-75" }), _jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-amber-500" })] }), _jsx("span", { className: "font-semibold", children: tHardcodedUi.raw('i18nComplete.text6250d5725dda') })] }))] }), _jsx(Badge, { variant: "outline", className: "font-mono text-xs", children: language })] })), _jsx("div", { ref: editorContainerRef, className: cn('bg-background w-full max-w-full', editorClassName, readOnly
498
497
  ? 'min-h-full flex-1 overflow-visible' // Stretch to fill parent so short files still fill the viewport
499
498
  : 'max-h-full min-h-0 flex-1 overflow-hidden'), style: readOnly ? undefined : { contain: 'strict' }, children: mounted && (_jsx(CodeMirror, { ref: editorRef, onCreateEditor: setEditorView, value: localContent, onChange: handleChange, theme: theme, extensions: extensions, basicSetup: {
500
499
  lineNumbers: showLineNumbers,
@@ -8,6 +8,8 @@ export declare const diffRendererViewportClass: typeof diffViewportClass;
8
8
  interface ChangeRequestDetailDialogProps {
9
9
  crId: string | null;
10
10
  onClose: () => void;
11
+ /** Opens another change request in this dialog: a superseded one's replacement. */
12
+ onOpenChangeRequest?: (crId: string) => void;
11
13
  }
12
- export declare function ChangeRequestDetailDialog({ crId, onClose }: ChangeRequestDetailDialogProps): import("react").JSX.Element;
14
+ export declare function ChangeRequestDetailDialog({ crId, onClose, onOpenChangeRequest }: ChangeRequestDetailDialogProps): import("react").JSX.Element;
13
15
  export {};
@@ -17,7 +17,6 @@ import { useTranslations } from '../../../i18n/index.js';
17
17
  * has actually gone wrong and the reader has to act on them.
18
18
  */
19
19
  import { UnifiedMarkdown } from '../../../markdown/index.js';
20
- import { Badge } from '../../../primitives/badge.js';
21
20
  import { Button } from '../../../primitives/button.js';
22
21
  import { InfoBanner } from '../../../primitives/info-banner.js';
23
22
  import Loading from '../../../primitives/loading.js';
@@ -27,14 +26,13 @@ import { errorToast, successToast } from '../../../primitives/toast.js';
27
26
  import { ChangeList, ChangeSummary, DiffLayoutToggle, ExpandAllButton, diffViewportClass, entryFromCommitFile, fileCount, proposedChangeTimeline, splitUnifiedPatch, useChangeExpansion, } from '../../../changes/index.js';
28
27
  import { EmptyState } from '../../../primitives/empty-state.js';
29
28
  import { manifestFilename, useProjectManifestVersion, } from '../../../internal/lib/manifest-version.js';
30
- import { translateUiCatalogText } from '../../../i18n/ui-catalog/localize-ui-catalog.js';
31
- import { REMAINING_UI_TRANSLATION_KEYS } from '../../../i18n/ui-catalog/remaining-ui-translation-keys.generated.js';
32
29
  import { useProjectCans } from '../../../internal/lib/use-project-can.js';
33
30
  import { REVIEW_CONTROL_ACTIONS, changeRequestControlsFrom } from '@kortix/sdk';
34
31
  import { ArrowCounterClockwiseIcon, CheckIcon, FileDashedIcon, SparkleIcon, WarningIcon, } from '@phosphor-icons/react';
35
32
  import { formatDistanceToNowStrict } from 'date-fns';
36
33
  import { useMemo, useState } from 'react';
37
34
  import { useProjectContext } from '../context.js';
35
+ import { ChangeRequestStatusBadge } from './change-request-status-badge.js';
38
36
  import { useChangeRequestRecovery } from '../hooks/use-change-request-recovery.js';
39
37
  import { useChangeRequest, useChangeRequestDiff, useChangeRequestMergePreview, useCloseChangeRequest, useMergeChangeRequest, useReopenChangeRequest, } from '../hooks/use-change-requests.js';
40
38
  /**
@@ -44,7 +42,7 @@ import { useChangeRequest, useChangeRequestDiff, useChangeRequestMergePreview, u
44
42
  */
45
43
  export const diffRendererViewportClass = diffViewportClass;
46
44
  const relative = (iso) => formatDistanceToNowStrict(new Date(iso), { addSuffix: true });
47
- export function ChangeRequestDetailDialog({ crId, onClose }) {
45
+ export function ChangeRequestDetailDialog({ crId, onClose, onOpenChangeRequest }) {
48
46
  const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
49
47
  const open = crId !== null;
50
48
  const detailQuery = useChangeRequest(crId);
@@ -149,7 +147,7 @@ export function ChangeRequestDetailDialog({ crId, onClose }) {
149
147
  const blocked = recoveryBlocker !== null;
150
148
  // An open change offers Dismiss and the apply; a closed one offers Reopen.
151
149
  const hasDecision = cr?.status === 'open' ? controls.canClose || controls.canMerge : controls.canClose;
152
- return (_jsx(Modal, { open: open, onOpenChange: (v) => !v && onClose(), children: _jsxs(ModalContent, { className: "flex h-[94dvh] max-h-[94dvh] min-h-0 w-full flex-col gap-0 space-y-0 overflow-hidden p-0 lg:h-[90vh] lg:max-h-[90vh] lg:min-h-[90vh] lg:max-w-5xl", children: [_jsx(ModalHeader, { className: "flex shrink-0 flex-col space-y-0 border-b px-5 py-4 pr-14", children: !cr ? (_jsxs("div", { className: "space-y-2", children: [_jsx(ModalTitle, { className: "sr-only", children: tI18nComplete.raw('text28b00af7cb71') }), _jsx(Skeleton, { className: "h-5 w-2/3" }), _jsx(Skeleton, { className: "h-3 w-1/3" })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [_jsx(ModalTitle, { className: "text-foreground min-w-0 flex-1 text-base leading-snug font-medium text-balance", children: cr.title || tI18nComplete.raw('text63cb7c2ea609') }), cr.status !== 'open' && (_jsx(Badge, { variant: cr.status === 'merged' ? 'badgeSuccess' : 'secondary', size: "sm", className: "mt-0.5 shrink-0", children: translateUiCatalogText(cr.status === 'merged' ? 'Applied' : 'Dismissed', tI18nComplete, REMAINING_UI_TRANSLATION_KEYS) }))] }), _jsxs("p", { className: "text-muted-foreground mt-1 flex min-w-0 flex-wrap items-center gap-x-1.5 text-xs", children: [_jsxs("span", { className: "tabular-nums", children: ["#", cr.number] }), _jsx("span", { className: "text-muted-foreground", "aria-hidden": true, children: tI18nComplete.raw('text3b9453dad42b') }), _jsx("span", { children: proposedChangeTimeline(cr, relative, tI18nComplete) }), cr.base_ref !== defaultBranch && (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-muted-foreground", "aria-hidden": true, children: tI18nComplete.raw('text3b9453dad42b') }), _jsxs("span", { className: "truncate", children: [tI18nComplete.raw('text6b847a0ed0b2'), " ", cr.base_ref] })] }))] })] })) }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto overscroll-contain", children: _jsxs("div", { className: "space-y-4 px-5 py-4", children: [manifestIssues !== null && (_jsx(InfoBanner, { tone: "destructive", icon: WarningIcon, title: tI18nComplete('text8e5d6f03865c', { value0: manifestFile }), children: manifestIssues.length > 0 ? (_jsx("ul", { className: "mt-1 space-y-1", children: dedupeIssues(manifestIssues).map(({ key, issue }) => (_jsxs("li", { children: [_jsx("code", { className: "text-foreground font-mono text-xs break-all", children: issue.path }), ' ', issue.message, issue.line
150
+ return (_jsx(Modal, { open: open, onOpenChange: (v) => !v && onClose(), children: _jsxs(ModalContent, { className: "flex h-[94dvh] max-h-[94dvh] min-h-0 w-full flex-col gap-0 space-y-0 overflow-hidden p-0 lg:h-[90vh] lg:max-h-[90vh] lg:min-h-[90vh] lg:max-w-5xl", children: [_jsx(ModalHeader, { className: "flex shrink-0 flex-col space-y-0 border-b px-5 py-4 pr-14", children: !cr ? (_jsxs("div", { className: "space-y-2", children: [_jsx(ModalTitle, { className: "sr-only", children: tI18nComplete.raw('text28b00af7cb71') }), _jsx(Skeleton, { className: "h-5 w-2/3" }), _jsx(Skeleton, { className: "h-3 w-1/3" })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [_jsx(ModalTitle, { className: "text-foreground min-w-0 flex-1 text-base leading-snug font-medium text-balance", children: cr.title || tI18nComplete.raw('text63cb7c2ea609') }), _jsx(ChangeRequestStatusBadge, { cr: cr, onOpenChangeRequest: onOpenChangeRequest })] }), _jsxs("p", { className: "text-muted-foreground mt-1 flex min-w-0 flex-wrap items-center gap-x-1.5 text-xs", children: [_jsxs("span", { className: "tabular-nums", children: ["#", cr.number] }), _jsx("span", { className: "text-muted-foreground", "aria-hidden": true, children: tI18nComplete.raw('text3b9453dad42b') }), _jsx("span", { children: proposedChangeTimeline(cr, relative, tI18nComplete) }), cr.base_ref !== defaultBranch && (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-muted-foreground", "aria-hidden": true, children: tI18nComplete.raw('text3b9453dad42b') }), _jsxs("span", { className: "truncate", children: [tI18nComplete.raw('text6b847a0ed0b2'), " ", cr.base_ref] })] }))] })] })) }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto overscroll-contain", children: _jsxs("div", { className: "space-y-4 px-5 py-4", children: [manifestIssues !== null && (_jsx(InfoBanner, { tone: "destructive", icon: WarningIcon, title: tI18nComplete('text8e5d6f03865c', { value0: manifestFile }), children: manifestIssues.length > 0 ? (_jsx("ul", { className: "mt-1 space-y-1", children: dedupeIssues(manifestIssues).map(({ key, issue }) => (_jsxs("li", { children: [_jsx("code", { className: "text-foreground font-mono text-xs break-all", children: issue.path }), ' ', issue.message, issue.line
153
151
  ? tI18nComplete('texta6b97728ff89', { value0: issue.line })
154
152
  : ''] }, key))) })) : (_jsx("span", { children: tI18nComplete.raw('textcc85fb556a48') })) })), manifestIssues === null && conflictPaths !== null && conflictPaths.length > 0 && (_jsx(InfoBanner, { tone: "warning", icon: WarningIcon, title: tI18nComplete('text57bbed2817ff', {
155
153
  value0: fileCount(conflictPaths.length),
@@ -0,0 +1,12 @@
1
+ import type { ChangeRequest } from '@kortix/sdk';
2
+ /**
3
+ * The word for what happened to a finished change request. An open one shows
4
+ * nothing: its state is the two buttons at the bottom of the dialog.
5
+ *
6
+ * A request an agent replaced (closed, with a later request on the same
7
+ * branch) names its replacement, and opens it when the host can (KRTX-3031).
8
+ */
9
+ export declare function ChangeRequestStatusBadge({ cr, onOpenChangeRequest, }: {
10
+ cr: ChangeRequest;
11
+ onOpenChangeRequest?: (crId: string) => void;
12
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useTranslations } from '../../../i18n/index.js';
4
+ import { translateUiCatalogText } from '../../../i18n/ui-catalog/localize-ui-catalog.js';
5
+ import { REMAINING_UI_TRANSLATION_KEYS } from '../../../i18n/ui-catalog/remaining-ui-translation-keys.generated.js';
6
+ import { Badge } from '../../../primitives/badge.js';
7
+ /**
8
+ * The word for what happened to a finished change request. An open one shows
9
+ * nothing: its state is the two buttons at the bottom of the dialog.
10
+ *
11
+ * A request an agent replaced (closed, with a later request on the same
12
+ * branch) names its replacement, and opens it when the host can (KRTX-3031).
13
+ */
14
+ export function ChangeRequestStatusBadge({ cr, onOpenChangeRequest, }) {
15
+ const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
16
+ if (cr.status === 'open')
17
+ return null;
18
+ const replacement = cr.status === 'closed' ? (cr.superseded_by ?? null) : null;
19
+ if (replacement) {
20
+ const label = tI18nComplete('textddcd87c21894', { number: replacement.number });
21
+ if (!onOpenChangeRequest) {
22
+ return (_jsx(Badge, { variant: "secondary", size: "sm", className: "mt-0.5 shrink-0", children: label }));
23
+ }
24
+ return (_jsx(Badge, { asChild: true, variant: "secondary", size: "sm", className: "mt-0.5 shrink-0 cursor-pointer hover:underline", children: _jsx("button", { type: "button", onClick: () => onOpenChangeRequest(replacement.cr_id), children: label }) }));
25
+ }
26
+ return (_jsx(Badge, { variant: cr.status === 'merged' ? 'badgeSuccess' : 'secondary', size: "sm", className: "mt-0.5 shrink-0", children: translateUiCatalogText(cr.status === 'merged' ? 'Applied' : 'Dismissed', tI18nComplete, REMAINING_UI_TRANSLATION_KEYS) }));
27
+ }
@@ -90,5 +90,5 @@ export function ChangeRequestsPanel({ open = false, onClose }) {
90
90
  ? tSidebar('loadingMore')
91
91
  : isFetchNextPageError
92
92
  ? tSidebar('retry')
93
- : tSidebar('loadMore') }) }))] }))] }), _jsx(ChangeRequestDetailDialog, { crId: selectedCrId, onClose: () => setSelectedCrId(null) }), _jsx(OpenChangeRequestDialog, { open: openDialogShown, onOpenChange: setOpenDialogShown, projectId: ctx?.projectId ?? '', defaultBranch: defaultBranch, initialHeadRef: initialHeadForDialog, onCreated: (crId) => setSelectedCrId(crId) })] }));
93
+ : tSidebar('loadMore') }) }))] }))] }), _jsx(ChangeRequestDetailDialog, { crId: selectedCrId, onClose: () => setSelectedCrId(null), onOpenChangeRequest: setSelectedCrId }), _jsx(OpenChangeRequestDialog, { open: openDialogShown, onOpenChange: setOpenDialogShown, projectId: ctx?.projectId ?? '', defaultBranch: defaultBranch, initialHeadRef: initialHeadForDialog, onCreated: (crId) => setSelectedCrId(crId) })] }));
94
94
  }
@@ -490,7 +490,8 @@ export function DriveExplorer({ embedded = false, shareContext, leading, trailin
490
490
  const { accepted, rejected } = partitionUploadBatch(tree.map((t) => t.file));
491
491
  for (const { reason } of rejected)
492
492
  errorToast(reason);
493
- const items = tree.filter((t) => accepted.includes(t.file));
493
+ const acceptedSet = new Set(accepted);
494
+ const items = tree.filter((t) => acceptedSet.has(t.file));
494
495
  if (items.length === 0)
495
496
  return;
496
497
  const root = base === '/' ? '' : base.replace(/\/+$/, '');
@@ -17,15 +17,19 @@ import { SelectionCheck, SelectMenuItem } from './drive-selection-ui.js';
17
17
  import { FileThumbnail } from './file-thumbnail.js';
18
18
  import { Download } from '../../../internal/icons/download.js';
19
19
  import { ownClicks } from './own-clicks.js';
20
- export function FolderDriveMenuItems({ Item, Separator, node, onClick, onDownload, onCopy, onCut, onRename, onDelete, startRenaming, isDownloadingItem, openFolderLabel, copyPathLabel, }) {
20
+ /** Copy, cut, copy path, rename and delete: the same on a folder and a file. */
21
+ function DriveMenuSharedItems({ Item, Separator, node, onCopy, onCut, onRename, onDelete, startRenaming, copyPathLabel, }) {
21
22
  const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
22
23
  const hasEditActions = Boolean(onRename || onDelete);
23
- return (_jsxs(_Fragment, { children: [_jsxs(Item, { onClick: onClick, children: [_jsx(ArrowUpRight, {}), openFolderLabel] }), onDownload && (_jsxs(Item, { onClick: () => onDownload(node), disabled: isDownloadingItem, children: [_jsx(Download, {}), isDownloadingItem ? 'Zipping...' : tI18nComplete.raw('textdda839b13617')] })), _jsx(Separator, {}), onCopy && (_jsxs(Item, { onClick: () => onCopy(node), children: [_jsx(ClipboardCopy, {}), tI18nComplete.raw('texte21f935f11d7')] })), onCut && (_jsxs(Item, { onClick: () => onCut(node), children: [_jsx(Scissors, {}), tI18nComplete.raw('text1f45f02561f4')] })), _jsxs(Item, { onClick: () => navigator.clipboard.writeText(node.path), children: [_jsx(Copy, {}), copyPathLabel] }), hasEditActions && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), onRename && (_jsxs(Item, { onClick: () => setTimeout(startRenaming, 100), children: [_jsx(PencilSimpleIcon, {}), tI18nComplete.raw('text3064d79a295c')] })), onDelete && (_jsxs(Item, { onClick: () => onDelete(node), children: [_jsx(TrashIcon, {}), tI18nComplete.raw('textc3812fc4acb8')] }))] }))] }));
24
+ return (_jsxs(_Fragment, { children: [onCopy && (_jsxs(Item, { onClick: () => onCopy(node), children: [_jsx(ClipboardCopy, {}), tI18nComplete.raw('texte21f935f11d7')] })), onCut && (_jsxs(Item, { onClick: () => onCut(node), children: [_jsx(Scissors, {}), tI18nComplete.raw('text1f45f02561f4')] })), _jsxs(Item, { onClick: () => navigator.clipboard.writeText(node.path), children: [_jsx(Copy, {}), copyPathLabel] }), hasEditActions && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), onRename && (_jsxs(Item, { onClick: () => setTimeout(startRenaming, 100), children: [_jsx(PencilSimpleIcon, {}), tI18nComplete.raw('text3064d79a295c')] })), onDelete && (_jsxs(Item, { onClick: () => onDelete(node), children: [_jsx(TrashIcon, {}), tI18nComplete.raw('textc3812fc4acb8')] }))] }))] }));
25
+ }
26
+ export function FolderDriveMenuItems({ Item, Separator, node, onClick, onDownload, onCopy, onCut, onRename, onDelete, startRenaming, isDownloadingItem, openFolderLabel, copyPathLabel, }) {
27
+ const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
28
+ return (_jsxs(_Fragment, { children: [_jsxs(Item, { onClick: onClick, children: [_jsx(ArrowUpRight, {}), openFolderLabel] }), onDownload && (_jsxs(Item, { onClick: () => onDownload(node), disabled: isDownloadingItem, children: [_jsx(Download, {}), isDownloadingItem ? 'Zipping...' : tI18nComplete.raw('textdda839b13617')] })), _jsx(Separator, {}), _jsx(DriveMenuSharedItems, { Item: Item, Separator: Separator, node: node, onCopy: onCopy, onCut: onCut, onRename: onRename, onDelete: onDelete, startRenaming: startRenaming, copyPathLabel: copyPathLabel })] }));
24
29
  }
25
30
  export function FileDriveMenuItems({ Item, Separator, node, onClick, onDownload, onHistory, onCopy, onCut, onRename, onDelete, startRenaming, checkpointHistoryLabel, copyPathLabel, }) {
26
31
  const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
27
- const hasEditActions = Boolean(onRename || onDelete);
28
- return (_jsxs(_Fragment, { children: [_jsxs(Item, { onClick: onClick, children: [_jsx(Eye, {}), tI18nComplete.raw('text324b134f57c7')] }), onDownload && (_jsxs(Item, { onClick: () => onDownload(node), children: [_jsx(Download, {}), tI18nComplete.raw('textd6eafe823591')] })), onHistory && (_jsxs(Item, { onClick: () => onHistory(node), children: [_jsx(History, {}), checkpointHistoryLabel] })), _jsx(Separator, {}), onCopy && (_jsxs(Item, { onClick: () => onCopy(node), children: [_jsx(ClipboardCopy, {}), tI18nComplete.raw('texte21f935f11d7')] })), onCut && (_jsxs(Item, { onClick: () => onCut(node), children: [_jsx(Scissors, {}), tI18nComplete.raw('text1f45f02561f4')] })), _jsxs(Item, { onClick: () => navigator.clipboard.writeText(node.path), children: [_jsx(Copy, {}), copyPathLabel] }), hasEditActions && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), onRename && (_jsxs(Item, { onClick: () => setTimeout(startRenaming, 100), children: [_jsx(PencilSimpleIcon, {}), tI18nComplete.raw('text3064d79a295c')] })), onDelete && (_jsxs(Item, { onClick: () => onDelete(node), children: [_jsx(TrashIcon, {}), tI18nComplete.raw('textc3812fc4acb8')] }))] }))] }));
32
+ return (_jsxs(_Fragment, { children: [_jsxs(Item, { onClick: onClick, children: [_jsx(Eye, {}), tI18nComplete.raw('text324b134f57c7')] }), onDownload && (_jsxs(Item, { onClick: () => onDownload(node), children: [_jsx(Download, {}), tI18nComplete.raw('textd6eafe823591')] })), onHistory && (_jsxs(Item, { onClick: () => onHistory(node), children: [_jsx(History, {}), checkpointHistoryLabel] })), _jsx(Separator, {}), _jsx(DriveMenuSharedItems, { Item: Item, Separator: Separator, node: node, onCopy: onCopy, onCut: onCut, onRename: onRename, onDelete: onDelete, startRenaming: startRenaming, copyPathLabel: copyPathLabel })] }));
29
33
  }
30
34
  function selectFolderRenameInput(el) {
31
35
  el.focus();
@@ -62,7 +66,7 @@ const FolderCard = memo(function FolderCard({ node, onClick, onDownload, onRenam
62
66
  // Transparent at rest, so the marked border takes no space when it appears.
63
67
  'h-[120px] border border-transparent',
64
68
  // Copied, cut or selected: the focus border plus its soft 15% ring.
65
- (isCut || isCopied || selected) && 'border-ring ring-ring/15 ring-2', isDragOver && 'ring-primary ring-2') }), selectMode ? _jsx(SelectionCheck, { selected: selected, className: "absolute top-2 left-2" }) : null, !isRenaming && (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { onClick: (e) => e.stopPropagation(), variant: "secondary", size: "icon-sm", className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 data-[state=open]:opacity-100", children: _jsx(MoreVertical, { weight: "bold", className: "size-3.5" }) }) }), _jsx(DropdownMenuContent, { align: "end", className: "w-48", children: _jsx(FolderDriveMenuItems, { Item: DropdownMenuItem, Separator: DropdownMenuSeparator, ...folderMenuProps }) })] })), _jsx("div", { className: "flex min-w-0 items-center px-0.5", children: isRenaming ? (_jsx("input", { ref: renameInputRef, type: "text", value: renameName, onChange: (e) => setRenameName(e.target.value), onKeyDown: (e) => {
69
+ (isCut || isCopied || selected) && 'border-ring ring-ring/15 ring-2', isDragOver && 'ring-primary ring-2') }), selectMode ? _jsx(SelectionCheck, { selected: selected, className: "absolute top-2 left-2" }) : null, !isRenaming && (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { onClick: (e) => e.stopPropagation(), "aria-label": tHardcodedUi.raw('i18nComplete.textf8d46c2570e7'), variant: "secondary", size: "icon-sm", className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 data-[state=open]:opacity-100", children: _jsx(MoreVertical, { weight: "bold", className: "size-3.5" }) }) }), _jsx(DropdownMenuContent, { align: "end", className: "w-48", children: _jsx(FolderDriveMenuItems, { Item: DropdownMenuItem, Separator: DropdownMenuSeparator, ...folderMenuProps }) })] })), _jsx("div", { className: "flex min-w-0 items-center px-0.5", children: isRenaming ? (_jsx("input", { ref: renameInputRef, type: "text", "aria-label": tHardcodedUi.raw('i18nComplete.text3064d79a295c'), value: renameName, onChange: (e) => setRenameName(e.target.value), onKeyDown: (e) => {
66
70
  if (e.nativeEvent.isComposing)
67
71
  return;
68
72
  if (e.key === 'Enter')
@@ -96,7 +100,7 @@ const FileCard = memo(function FileCard({ node, onClick, onDoubleClick, onDownlo
96
100
  };
97
101
  return (_jsxs(ContextMenu, { children: [_jsx(ContextMenuTrigger, { asChild: true, children: _jsxs("div", { draggable: !isRenaming, onDragStart: handleDragStart, onDragEnd: handleDragEnd, onClick: isRenaming ? undefined : onItemClick, "data-drive-item": node.path, onDoubleClick: isRenaming || !onDoubleClick ? undefined : ownClicks(() => onDoubleClick(node)), className: cn('group relative flex cursor-pointer flex-col gap-2 select-none', isCut && 'opacity-60', isDragging && 'opacity-30'), children: [_jsx(FileThumbnail, { filePath: node.path, fileName: node.name, onOpen: open, className: cn('border-border h-[120px] rounded-lg border',
98
102
  // Copied, cut or selected: the focus border plus its soft 15% ring.
99
- (isCut || isCopied || selected) && 'border-ring ring-ring/15 ring-2') }), selectMode && !uploading ? (_jsx(SelectionCheck, { selected: selected, className: "absolute top-2 left-2 z-10" })) : null, uploading ? (_jsx("span", { role: "status", "aria-label": tDrives('uploadingFile', { name: node.name }), className: "bg-popover border-border absolute top-2 left-2 z-10 flex size-7 items-center justify-center rounded-md border shadow-xs", children: _jsx(Loading, { variant: "spokes", className: "size-4" }) })) : null, !isRenaming && (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { onClick: (e) => e.stopPropagation(), variant: "secondary", size: "icon-sm", className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 data-[state=open]:opacity-100", children: _jsx(MoreVertical, { weight: "bold", className: "size-3.5" }) }) }), _jsx(DropdownMenuContent, { align: "end", className: "w-48", children: _jsx(FileDriveMenuItems, { Item: DropdownMenuItem, Separator: DropdownMenuSeparator, ...fileMenuProps }) })] })), _jsx("div", { className: "flex min-w-0 items-center px-0.5", children: isRenaming ? (_jsx("input", { ref: renameInputRef, type: "text", value: renameName, onChange: (e) => setRenameName(e.target.value), onKeyDown: (e) => {
103
+ (isCut || isCopied || selected) && 'border-ring ring-ring/15 ring-2') }), selectMode && !uploading ? (_jsx(SelectionCheck, { selected: selected, className: "absolute top-2 left-2 z-10" })) : null, uploading ? (_jsx("span", { role: "status", "aria-label": tDrives('uploadingFile', { name: node.name }), className: "bg-popover border-border absolute top-2 left-2 z-10 flex size-7 items-center justify-center rounded-md border shadow-xs", children: _jsx(Loading, { variant: "spokes", className: "size-4" }) })) : null, !isRenaming && (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { onClick: (e) => e.stopPropagation(), "aria-label": tHardcodedUi.raw('i18nComplete.textf8d46c2570e7'), variant: "secondary", size: "icon-sm", className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 data-[state=open]:opacity-100", children: _jsx(MoreVertical, { weight: "bold", className: "size-3.5" }) }) }), _jsx(DropdownMenuContent, { align: "end", className: "w-48", children: _jsx(FileDriveMenuItems, { Item: DropdownMenuItem, Separator: DropdownMenuSeparator, ...fileMenuProps }) })] })), _jsx("div", { className: "flex min-w-0 items-center px-0.5", children: isRenaming ? (_jsx("input", { ref: renameInputRef, type: "text", "aria-label": tHardcodedUi.raw('i18nComplete.text3064d79a295c'), value: renameName, onChange: (e) => setRenameName(e.target.value), onKeyDown: (e) => {
100
104
  if (e.nativeEvent.isComposing)
101
105
  return;
102
106
  if (e.key === 'Enter')
@@ -125,7 +129,7 @@ export function DriveGridView({ elevatedDirs, dirs, files, onNavigateToDir, onOp
125
129
  const description = meta
126
130
  ? tI18nComplete.raw(node.name === '.kortix' ? 'text85231c543c71' : 'text96ef9c0ac0c9')
127
131
  : undefined;
128
- return (_jsxs("div", { onClick: () => onNavigateToDir(node), title: description, className: "group relative flex cursor-pointer flex-col gap-2 select-none", children: [_jsx(DriveFolderThumbnail, { path: node.path, className: "h-[120px]" }), _jsx("span", { className: "text-foreground truncate px-0.5 text-sm font-medium", children: node.name })] }, node.path));
132
+ return (_jsxs("button", { type: "button", onClick: () => onNavigateToDir(node), title: description, className: "group relative flex w-full min-w-0 cursor-pointer flex-col gap-2 text-left select-none", children: [_jsx(DriveFolderThumbnail, { path: node.path, className: "h-[120px]" }), _jsx("span", { className: "text-foreground truncate px-0.5 text-sm font-medium", children: node.name })] }, node.path));
129
133
  }) })] })), dirs.length > 0 && (_jsxs("section", { className: "space-y-2", children: [_jsx(SectionHeader, { label: tI18nComplete.raw('textc4d6bb200f4c'), count: dirs.length }), _jsx("div", { className: "grid gap-x-4 gap-y-5", style: { gridTemplateColumns: "repeat(auto-fill, minmax(168px, 1fr))" }, children: dirs.map((node) => (_jsx(FolderCard, { node: node, onClick: onNavigateToDir, onDownload: onDownloadDir, isDownloadingItem: isDirDownloading(node.path), onRename: isFixed?.(node.path) ? undefined : onRename, onDelete: isFixed?.(node.path) ? undefined : onDelete, onCopy: onCopy, onCut: isFixed?.(node.path) ? undefined : onCut, onDropMove: onDropMove, onDropUpload: onDropUpload, isCut: clipboardOperation === 'cut' && clipboardPath === node.path, isCopied: clipboardOperation === 'copy' && clipboardPath === node.path }, node.path))) })] })), files.length > 0 && (_jsxs("section", { className: "space-y-2", children: [_jsx(SectionHeader, { label: tI18nComplete.raw('textabc7e9892806'), count: files.length }), _jsx("div", { className: "grid gap-x-4 gap-y-5", style: { gridTemplateColumns: "repeat(auto-fill, minmax(168px, 1fr))" }, children: files.map((node) => (_jsx(FileCard, { node: node, onClick: onPreviewFile, onDoubleClick: onOpenFile, onDownload: onDownload, onRename: onRename, onDelete: onDelete, onHistory: onHistory, onCopy: onCopy, onCut: onCut, gitStatus: gitStatusMap.get(node.path), isCut: clipboardOperation === 'cut' && clipboardPath === node.path, isCopied: clipboardOperation === 'copy' && clipboardPath === node.path, uploading: uploadingPaths?.has(node.path) }, node.path))) })] }))] }));
130
134
  }
131
135
  function SectionHeader({ label, count, icon, }) {
@@ -84,7 +84,7 @@ function ListRow({ node, onClick, onDoubleClick, onDownload, onRename, onDelete,
84
84
  (isCut || isCopied || selected) && 'ring-ring bg-ring/15 ring-1 ring-inset', isDragging && 'opacity-30', isDragOver && 'bg-primary/8'), children: [_jsx(TableCell, { children: _jsxs("div", { className: "flex min-w-0 items-center gap-2.5", children: [selectMode ? _jsx(SelectionCheck, { selected: selected, className: "shrink-0" }) : null, isDir ? (_jsx(Folder, { className: "text-muted-foreground size-4 shrink-0" })) : (getFileIcon(node.name, {
85
85
  className: 'size-4 shrink-0 text-muted-foreground',
86
86
  variant: 'monochrome',
87
- })), isRenaming ? (_jsx("input", { ref: renameInputRef, type: "text", value: renameName, onChange: (e) => setRenameName(e.target.value), onKeyDown: (e) => {
87
+ })), isRenaming ? (_jsx("input", { ref: renameInputRef, type: "text", "aria-label": tHardcodedUi.raw('i18nComplete.text3064d79a295c'), value: renameName, onChange: (e) => setRenameName(e.target.value), onKeyDown: (e) => {
88
88
  if (e.key === 'Enter') {
89
89
  if (e.nativeEvent.isComposing)
90
90
  return;
@@ -92,7 +92,7 @@ function ListRow({ node, onClick, onDoubleClick, onDownload, onRename, onDelete,
92
92
  }
93
93
  if (e.key === 'Escape')
94
94
  setIsRenaming(false);
95
- }, onBlur: confirmRename, onClick: (e) => e.stopPropagation(), className: "border-border min-w-0 flex-1 border-b bg-transparent py-0.5 text-sm outline-none" })) : (_jsx("span", { className: cn('truncate text-sm font-medium', node.ignored && 'opacity-50', node.name.startsWith('.') && 'text-muted-foreground'), children: node.name })), uploading ? (_jsx("span", { role: "status", "aria-label": tDrives('uploadingFile', { name: node.name }), className: "shrink-0", children: _jsx(Loading, { variant: "spokes", className: "size-3.5" }) })) : null] }) }), _jsx(TableCell, { children: isDir ? (_jsx(ChalkBadge, { label: tHardcodedUi.raw('i18nComplete.text74ccd4330384') })) : ext ? (_jsx(ChalkBadge, { label: ext })) : (_jsx("span", { className: "text-muted-foreground text-sm", children: "\u2014" })) }), _jsx(TableCell, { children: !isRenaming && (_jsx("div", { className: "flex justify-end", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { onClick: (e) => e.stopPropagation(), variant: "ghost", size: "icon-xs", className: "shrink-0 opacity-0 group-hover:opacity-100", children: _jsx(MoreVertical, { className: "text-muted-foreground size-3.5" }) }) }), _jsx(DropdownMenuContent, { align: "end", className: "w-48", children: isDir ? (_jsx(FolderDriveMenuItems, { Item: DropdownMenuItem, Separator: DropdownMenuSeparator, ...folderMenuProps })) : (_jsx(FileDriveMenuItems, { Item: DropdownMenuItem, Separator: DropdownMenuSeparator, ...fileMenuProps })) })] }) })) })] }) }), _jsx(ContextMenuContent, { className: "w-48", children: selection?.selectionMenu(node, ContextMenuItem, ContextMenuSeparator) ?? (_jsxs(_Fragment, { children: [_jsx(SelectMenuItem, { selection: selection, node: node, Item: ContextMenuItem, Separator: ContextMenuSeparator }), isDir ? (_jsx(FolderDriveMenuItems, { Item: ContextMenuItem, Separator: ContextMenuSeparator, ...folderMenuProps })) : (_jsx(FileDriveMenuItems, { Item: ContextMenuItem, Separator: ContextMenuSeparator, ...fileMenuProps }))] })) })] }));
95
+ }, onBlur: confirmRename, onClick: (e) => e.stopPropagation(), className: "border-border min-w-0 flex-1 border-b bg-transparent py-0.5 text-sm outline-none" })) : (_jsx("span", { className: cn('truncate text-sm font-medium', node.ignored && 'opacity-50', node.name.startsWith('.') && 'text-muted-foreground'), children: node.name })), uploading ? (_jsx("span", { role: "status", "aria-label": tDrives('uploadingFile', { name: node.name }), className: "shrink-0", children: _jsx(Loading, { variant: "spokes", className: "size-3.5" }) })) : null] }) }), _jsx(TableCell, { children: isDir ? (_jsx(ChalkBadge, { label: tHardcodedUi.raw('i18nComplete.text74ccd4330384') })) : ext ? (_jsx(ChalkBadge, { label: ext })) : (_jsx("span", { className: "text-muted-foreground text-sm", children: "\u2014" })) }), _jsx(TableCell, { children: !isRenaming && (_jsx("div", { className: "flex justify-end", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { onClick: (e) => e.stopPropagation(), "aria-label": tHardcodedUi.raw('i18nComplete.textf8d46c2570e7'), variant: "ghost", size: "icon-xs", className: "shrink-0 opacity-0 group-hover:opacity-100", children: _jsx(MoreVertical, { className: "text-muted-foreground size-3.5" }) }) }), _jsx(DropdownMenuContent, { align: "end", className: "w-48", children: isDir ? (_jsx(FolderDriveMenuItems, { Item: DropdownMenuItem, Separator: DropdownMenuSeparator, ...folderMenuProps })) : (_jsx(FileDriveMenuItems, { Item: DropdownMenuItem, Separator: DropdownMenuSeparator, ...fileMenuProps })) })] }) })) })] }) }), _jsx(ContextMenuContent, { className: "w-48", children: selection?.selectionMenu(node, ContextMenuItem, ContextMenuSeparator) ?? (_jsxs(_Fragment, { children: [_jsx(SelectMenuItem, { selection: selection, node: node, Item: ContextMenuItem, Separator: ContextMenuSeparator }), isDir ? (_jsx(FolderDriveMenuItems, { Item: ContextMenuItem, Separator: ContextMenuSeparator, ...folderMenuProps })) : (_jsx(FileDriveMenuItems, { Item: ContextMenuItem, Separator: ContextMenuSeparator, ...fileMenuProps }))] })) })] }));
96
96
  }
97
97
  function ElevatedDirRow({ node, onNavigate }) {
98
98
  const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
@@ -119,10 +119,10 @@ export function DriveListView({ elevatedDirs, dirs, files, onNavigateToDir, onOp
119
119
  setSortBy(field);
120
120
  }
121
121
  };
122
- const SortIcon = ({ field }) => {
122
+ const renderSortIcon = (field) => {
123
123
  if (sortDisabled || sortBy !== field)
124
124
  return null;
125
125
  return sortOrder === 'asc' ? (_jsx(ArrowUp, { className: "ml-1 size-3" })) : (_jsx(ArrowDown, { className: "ml-1 size-3" }));
126
126
  };
127
- return (_jsx("div", { className: "space-y-7 p-5", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { className: "hover:bg-transparent", children: [_jsx(TableHead, { children: _jsxs(Button, { onClick: () => handleHeaderClick('name'), disabled: sortDisabled, variant: "transparent", className: "text-muted-foreground hover:text-foreground m-0 h-fit w-fit p-0 font-normal has-[>svg]:p-0", children: [tI18nComplete.raw('textdcd1d5223f73'), _jsx(SortIcon, { field: "name" })] }) }), _jsx(TableHead, { children: _jsxs(Button, { onClick: () => handleHeaderClick('type'), disabled: sortDisabled, variant: "transparent", className: "text-muted-foreground hover:text-foreground m-0 h-fit w-fit p-0 font-normal has-[>svg]:p-0", children: [tI18nComplete.raw('textbaaddf70fb5d'), _jsx(SortIcon, { field: "type" })] }) }), _jsx(TableHead, { className: "w-[52px]", children: _jsx("span", { className: "sr-only", children: tI18nComplete.raw('textff8059dc6752') }) })] }) }), _jsxs(TableBody, { children: [elevatedDirs.map((node) => (_jsx(ElevatedDirRow, { node: node, onNavigate: () => onNavigateToDir(node) }, node.path))), dirs.map((node) => (_jsx(ListRow, { node: node, onClick: () => onNavigateToDir(node), onDownload: onDownloadDir, isDownloadingItem: isDirDownloading(node.path), onRename: isFixed?.(node.path) ? undefined : onRename, onDelete: isFixed?.(node.path) ? undefined : onDelete, onCopy: onCopy, onCut: isFixed?.(node.path) ? undefined : onCut, onDropMove: onDropMove, onDropUpload: onDropUpload, isCut: clipboardOperation === 'cut' && clipboardPath === node.path, isCopied: clipboardOperation === 'copy' && clipboardPath === node.path }, node.path))), files.map((node) => (_jsx(ListRow, { node: node, onClick: () => onPreviewFile(node), onDoubleClick: () => onOpenFile(node), onDownload: onDownload, onRename: onRename, onDelete: onDelete, onHistory: onHistory, onCopy: onCopy, onCut: onCut, gitStatus: gitStatusMap.get(node.path), isCut: clipboardOperation === 'cut' && clipboardPath === node.path, isCopied: clipboardOperation === 'copy' && clipboardPath === node.path, uploading: uploadingPaths?.has(node.path) }, node.path)))] })] }) }));
127
+ return (_jsx("div", { className: "space-y-7 p-5", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { className: "hover:bg-transparent", children: [_jsx(TableHead, { children: _jsxs(Button, { onClick: () => handleHeaderClick('name'), disabled: sortDisabled, variant: "transparent", className: "text-muted-foreground hover:text-foreground m-0 h-fit w-fit p-0 font-normal has-[>svg]:p-0", children: [tI18nComplete.raw('textdcd1d5223f73'), renderSortIcon('name')] }) }), _jsx(TableHead, { children: _jsxs(Button, { onClick: () => handleHeaderClick('type'), disabled: sortDisabled, variant: "transparent", className: "text-muted-foreground hover:text-foreground m-0 h-fit w-fit p-0 font-normal has-[>svg]:p-0", children: [tI18nComplete.raw('textbaaddf70fb5d'), renderSortIcon('type')] }) }), _jsx(TableHead, { className: "w-[52px]", children: _jsx("span", { className: "sr-only", children: tI18nComplete.raw('textff8059dc6752') }) })] }) }), _jsxs(TableBody, { children: [elevatedDirs.map((node) => (_jsx(ElevatedDirRow, { node: node, onNavigate: () => onNavigateToDir(node) }, node.path))), dirs.map((node) => (_jsx(ListRow, { node: node, onClick: () => onNavigateToDir(node), onDownload: onDownloadDir, isDownloadingItem: isDirDownloading(node.path), onRename: isFixed?.(node.path) ? undefined : onRename, onDelete: isFixed?.(node.path) ? undefined : onDelete, onCopy: onCopy, onCut: isFixed?.(node.path) ? undefined : onCut, onDropMove: onDropMove, onDropUpload: onDropUpload, isCut: clipboardOperation === 'cut' && clipboardPath === node.path, isCopied: clipboardOperation === 'copy' && clipboardPath === node.path }, node.path))), files.map((node) => (_jsx(ListRow, { node: node, onClick: () => onPreviewFile(node), onDoubleClick: () => onOpenFile(node), onDownload: onDownload, onRename: onRename, onDelete: onDelete, onHistory: onHistory, onCopy: onCopy, onCut: onCut, gitStatus: gitStatusMap.get(node.path), isCut: clipboardOperation === 'cut' && clipboardPath === node.path, isCopied: clipboardOperation === 'copy' && clipboardPath === node.path, uploading: uploadingPaths?.has(node.path) }, node.path)))] })] }) }));
128
128
  }
@@ -12,7 +12,7 @@ function BreadcrumbSegments({ segments, onSegmentClick, onHomeClick, fileMode, f
12
12
  // When rootPath is set, only show segments at or below it
13
13
  const rootSegmentCount = useMemo(() => (rootPath ? rootPath.split('/').filter(Boolean).length : 0), [rootPath]);
14
14
  const visibleSegments = useMemo(() => (rootPath ? segments.slice(rootSegmentCount) : segments), [rootPath, segments, rootSegmentCount]);
15
- return (_jsxs("nav", { className: "flex min-w-0 flex-1 items-center gap-0.5 overflow-x-auto", onDoubleClick: onDoubleClick, title: onDoubleClick ? tI18nComplete.raw('text5efd0bed7735') : undefined, children: [_jsx("button", { onClick: onHomeClick, className: cn('flex shrink-0 cursor-pointer items-center gap-1 rounded-lg px-2 py-1 transition-colors', 'hover:bg-muted', visibleSegments.length === 0
15
+ return (_jsxs("nav", { className: "flex min-w-0 flex-1 items-center gap-0.5 overflow-x-auto", onDoubleClick: onDoubleClick, title: onDoubleClick ? tI18nComplete.raw('text5efd0bed7735') : undefined, children: [_jsx("button", { onClick: onHomeClick, "aria-label": tI18nComplete.raw('text3a78695388b3'), className: cn('flex shrink-0 cursor-pointer items-center gap-1 rounded-lg px-2 py-1 transition-colors', 'hover:bg-muted', visibleSegments.length === 0
16
16
  ? 'text-foreground bg-muted/50 font-medium'
17
17
  : 'text-muted-foreground'), children: _jsx(FolderRoot, { className: "h-3.5 w-3.5" }) }), visibleSegments.map((segment, visibleIndex) => {
18
18
  // Skip 'workspace' when not sandboxed (rootPath null)
@@ -357,7 +357,7 @@ export function FileExplorerPage({ embedded = false } = {}) {
357
357
  gridTemplateColumns: "repeat(auto-fill, minmax(150px, 1fr))",
358
358
  }, children: Array.from({ length: 8 }).map((_, i) => (_jsx(Skeleton, { className: "h-[138px] rounded-lg" }, `fi${i}`))) })] })] })) : (_jsx("div", { className: "space-y-1", children: Array.from({ length: 10 }).map((_, i) => (_jsx(Skeleton, { className: "h-10 w-full rounded" }, i))) })) })), !!error && !isLoading && (_jsx(ErrorState, { title: tHardcodedUi.raw('featuresProjectFilesComponentsFileExplorerPage.line658JsxTextFailedToLoadFiles'), description: error instanceof Error
359
359
  ? error.message
360
- : tHardcodedUi.raw('i18nComplete.text27c2ccd962c2'), action: _jsx(Button, { variant: "outline", size: "sm", onClick: () => refetchFiles(), children: tHardcodedUi.raw('i18nComplete.text942087cc2d41') }) })), isEmpty && (_jsx(EmptyState, { icon: FolderOpen, title: tHardcodedUi.raw('featuresProjectFilesComponentsDriveListView.line396JsxTextThisFolderIsEmpty'), description: tHardcodedUi.raw('featuresProjectFilesComponentsDriveListView.line398JsxTextNoFilesOrSubfoldersAtThisPathIn') })), !isLoading && !error && files && !isEmpty && (_jsxs(_Fragment, { children: [viewMode === 'grid' ? (_jsx(DriveGridView, { elevatedDirs: elevatedDirs, dirs: dirs, files: fileItems, onNavigateToDir: handleNavigateToDir, onOpenFile: handleOpenFile, onPreviewFile: handlePreviewFile, onDownload: handleDownload, onDownloadDir: handleDownloadDir, onRename: handleRename, onDelete: handleDelete, onHistory: handleHistory, onCopy: handleCopy, onCut: handleCut, onDropMove: handleDropMove, gitStatusMap: gitStatusMap, clipboardPath: clipboard?.path, clipboardOperation: clipboard?.operation, isDirDownloading: isDirDownloading, readOnly: true })) : (_jsx(DriveListView, { elevatedDirs: elevatedDirs, dirs: dirs, files: fileItems, sortDisabled: partial, onNavigateToDir: handleNavigateToDir, onOpenFile: handleOpenFile, onPreviewFile: handlePreviewFile, onDownload: handleDownload, onDownloadDir: handleDownloadDir, onRename: handleRename, onDelete: handleDelete, onHistory: handleHistory, onCopy: handleCopy, onCut: handleCut, onDropMove: handleDropMove, gitStatusMap: gitStatusMap, clipboardPath: clipboard?.path, clipboardOperation: clipboard?.operation, isDirDownloading: isDirDownloading, readOnly: true })), _jsx(LoadMoreRow, { listing: listing })] }))] }), _jsx(CheckpointsPanel, { open: rightPanel === 'history', onClose: () => setRightPanel(null) }), _jsx(ChangeRequestsPanel, { open: rightPanel === 'proposed-changes', onClose: () => setRightPanel(null) })] }), _jsx(ChangeRequestDetailDialog, { crId: createdCrId, onClose: () => setCreatedCrId(null) }), _jsx(ExplorerFilePreview, {}), searchEnabled && isSearchOpen && _jsx(FileSearch, {}), historyPopoverPath && (_jsx("div", { className: "bg-popover border-border animate-in slide-in-from-bottom-4 fade-in-0 fixed right-4 bottom-4 z-50 overflow-hidden rounded-md border shadow-2xl duration-200", children: _jsx(FileHistoryPopoverContent, { filePath: historyPopoverPath, onClose: () => setHistoryPopoverPath(null) }) })), _jsx(AlertDialog, { open: !!deleteTarget, onOpenChange: () => setDeleteTarget(null), children: _jsxs(AlertDialogContent, { className: "sm:max-w-md", onOpenAutoFocus: (e) => {
360
+ : tHardcodedUi.raw('i18nComplete.text27c2ccd962c2'), action: _jsx(Button, { variant: "outline", size: "sm", onClick: () => refetchFiles(), children: tHardcodedUi.raw('i18nComplete.text942087cc2d41') }) })), isEmpty && (_jsx(EmptyState, { icon: FolderOpen, title: tHardcodedUi.raw('featuresProjectFilesComponentsDriveListView.line396JsxTextThisFolderIsEmpty'), description: tHardcodedUi.raw('featuresProjectFilesComponentsDriveListView.line398JsxTextNoFilesOrSubfoldersAtThisPathIn') })), !isLoading && !error && files && !isEmpty && (_jsxs(_Fragment, { children: [viewMode === 'grid' ? (_jsx(DriveGridView, { elevatedDirs: elevatedDirs, dirs: dirs, files: fileItems, onNavigateToDir: handleNavigateToDir, onOpenFile: handleOpenFile, onPreviewFile: handlePreviewFile, onDownload: handleDownload, onDownloadDir: handleDownloadDir, onRename: handleRename, onDelete: handleDelete, onHistory: handleHistory, onCopy: handleCopy, onCut: handleCut, onDropMove: handleDropMove, gitStatusMap: gitStatusMap, clipboardPath: clipboard?.path, clipboardOperation: clipboard?.operation, isDirDownloading: isDirDownloading, readOnly: true })) : (_jsx(DriveListView, { elevatedDirs: elevatedDirs, dirs: dirs, files: fileItems, sortDisabled: partial, onNavigateToDir: handleNavigateToDir, onOpenFile: handleOpenFile, onPreviewFile: handlePreviewFile, onDownload: handleDownload, onDownloadDir: handleDownloadDir, onRename: handleRename, onDelete: handleDelete, onHistory: handleHistory, onCopy: handleCopy, onCut: handleCut, onDropMove: handleDropMove, gitStatusMap: gitStatusMap, clipboardPath: clipboard?.path, clipboardOperation: clipboard?.operation, isDirDownloading: isDirDownloading, readOnly: true })), _jsx(LoadMoreRow, { listing: listing })] }))] }), _jsx(CheckpointsPanel, { open: rightPanel === 'history', onClose: () => setRightPanel(null) }), _jsx(ChangeRequestsPanel, { open: rightPanel === 'proposed-changes', onClose: () => setRightPanel(null) })] }), _jsx(ChangeRequestDetailDialog, { crId: createdCrId, onClose: () => setCreatedCrId(null), onOpenChangeRequest: setCreatedCrId }), _jsx(ExplorerFilePreview, {}), searchEnabled && isSearchOpen && _jsx(FileSearch, {}), historyPopoverPath && (_jsx("div", { className: "bg-popover border-border animate-in slide-in-from-bottom-4 fade-in-0 fixed right-4 bottom-4 z-50 overflow-hidden rounded-md border shadow-2xl duration-200", children: _jsx(FileHistoryPopoverContent, { filePath: historyPopoverPath, onClose: () => setHistoryPopoverPath(null) }) })), _jsx(AlertDialog, { open: !!deleteTarget, onOpenChange: () => setDeleteTarget(null), children: _jsxs(AlertDialogContent, { className: "sm:max-w-md", onOpenAutoFocus: (e) => {
361
361
  e.preventDefault();
362
362
  deleteButtonRef.current?.focus();
363
363
  }, children: [_jsxs(AlertDialogHeader, { children: [_jsxs(AlertDialogTitle, { children: [tHardcodedUi.raw('i18nComplete.texte2d0a54968ea'), ' ', deleteTarget?.type === 'directory' ? 'folder' : 'file'] }), _jsxs(AlertDialogDescription, { children: [tHardcodedUi.raw('featuresProjectFilesComponentsFileExplorerPage.line806JsxTextAreYouSureYouWantToDelete'), ' ', _jsxs("span", { className: "text-foreground font-semibold", children: [tHardcodedUi.raw('featuresProjectFilesComponentsFileExplorerPage.line807JsxTextQuot'), deleteTarget?.name, tHardcodedUi.raw('featuresProjectFilesComponentsFileExplorerPage.line807JsxTextQuot32c14d98')] }), tHardcodedUi.raw('featuresProjectFilesComponentsFileExplorerPage.line807JsxTextThisActionCannotBeUndone')] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: deleteMutation.isPending, children: tHardcodedUi.raw('i18nComplete.text19766ed6ccb2') }), _jsx(AlertDialogAction, { ref: deleteButtonRef, onClick: (e) => {
@@ -83,12 +83,7 @@ function CompactCommitRow({ commit, filePath }) {
83
83
  setTimeout(() => setCopied(false), 2000);
84
84
  }, [commit.hash]);
85
85
  const toggle = () => setExpanded((v) => !v);
86
- return (_jsxs("li", { className: cn('relative rounded-md', expanded && 'bg-muted'), children: [_jsxs("div", { role: "button", tabIndex: 0, "aria-expanded": expanded, onClick: toggle, onKeyDown: (e) => {
87
- if (e.key === 'Enter' || e.key === ' ') {
88
- e.preventDefault();
89
- toggle();
90
- }
91
- }, className: cn('group/row focus-visible:ring-ring flex w-full cursor-pointer items-start gap-3 rounded-md px-2 py-2 text-left outline-none focus-visible:ring-1', !expanded && 'hover:bg-muted'), children: [_jsx("span", { className: "border-muted-foreground bg-background relative z-10 mt-[5px] size-2.5 shrink-0 rounded-full border" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-foreground truncate text-sm font-medium", children: commit.subject }), _jsxs(InlineMeta, { className: "mt-0.5", children: [commit.author, _jsx("time", { title: formatFullDate(commit.timestamp), children: formatRelativeDate(commit.timestamp) })] })] }), _jsxs("button", { type: "button", onClick: handleCopyHash, className: "text-muted-foreground hover:text-foreground flex shrink-0 items-center gap-1 rounded-sm px-1 font-mono text-xs leading-5", title: tHardcodedUi.raw('featuresProjectFilesComponentsFileHistoryPopover.line160JsxAttrTitleCopyCheckpointId'), children: [copied ? (_jsx(SolidCheckIcon, { className: "text-kortix-green size-3" })) : (_jsx(Copy, { className: "size-3 opacity-0 group-hover/row:opacity-100" })), commit.shortHash] })] }), expanded && _jsx(CompactCommitDiff, { filePath: filePath, commitHash: commit.hash })] }));
86
+ return (_jsxs("li", { className: cn('relative rounded-md', expanded && 'bg-muted'), children: [_jsxs("div", { className: cn('group/row has-[button[aria-expanded]:focus-visible]:ring-ring flex w-full items-start gap-3 rounded-md has-[button[aria-expanded]:focus-visible]:ring-1', !expanded && 'hover:bg-muted'), children: [_jsxs("button", { type: "button", "aria-expanded": expanded, onClick: toggle, className: "flex min-w-0 flex-1 cursor-pointer items-start gap-3 py-2 pl-2 text-left outline-none", children: [_jsx("span", { className: "border-muted-foreground bg-background relative z-10 mt-[5px] size-2.5 shrink-0 rounded-full border" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-foreground truncate text-sm font-medium", children: commit.subject }), _jsxs(InlineMeta, { className: "mt-0.5", children: [commit.author, _jsx("time", { title: formatFullDate(commit.timestamp), children: formatRelativeDate(commit.timestamp) })] })] })] }), _jsxs("button", { type: "button", onClick: handleCopyHash, className: "text-muted-foreground hover:text-foreground my-2 mr-2 flex shrink-0 items-center gap-1 rounded-sm px-1 font-mono text-xs leading-5", title: tHardcodedUi.raw('featuresProjectFilesComponentsFileHistoryPopover.line160JsxAttrTitleCopyCheckpointId'), children: [copied ? (_jsx(SolidCheckIcon, { className: "text-kortix-green size-3" })) : (_jsx(Copy, { className: "size-3 opacity-0 group-hover/row:opacity-100" })), commit.shortHash] })] }), expanded && _jsx(CompactCommitDiff, { filePath: filePath, commitHash: commit.hash })] }));
92
87
  }
93
88
  export function FileHistoryPopoverContent({ filePath, onClose }) {
94
89
  const tHardcodedUi = useTranslations('hardcodedUi');
@@ -112,7 +112,12 @@ export function FileSearch() {
112
112
  }
113
113
  }
114
114
  }, [closeSearch, results, selectedIndex, handleSelect, scrollItemIntoView]);
115
- return (_jsx("div", { className: "absolute inset-0 z-50 bg-black/60 backdrop-blur-sm", onClick: closeSearch, children: _jsx("div", { className: "mx-auto mt-4 max-w-lg px-4", onClick: (e) => e.stopPropagation(), children: _jsxs("div", { className: "border-border bg-card overflow-hidden rounded-2xl border shadow-2xl", children: [_jsxs("div", { className: "flex items-center gap-2 border-b px-3", children: [_jsx(Search, { className: "text-muted-foreground h-4 w-4 shrink-0" }), _jsx(Input, { type: "text", ref: inputRef, value: query, onChange: (e) => setQuery(e.target.value), onKeyDown: handleInputKeyDown, placeholder: tHardcodedUi.raw(searchContents
115
+ return (_jsx("div", { role: "presentation", className: "absolute inset-0 z-50 bg-black/60 backdrop-blur-sm", onClick: (e) => {
116
+ if (e.target === e.currentTarget)
117
+ closeSearch();
118
+ else
119
+ e.stopPropagation();
120
+ }, children: _jsx("div", { className: "mx-auto mt-4 max-w-lg px-4", children: _jsxs("div", { className: "border-border bg-card overflow-hidden rounded-2xl border shadow-2xl", children: [_jsxs("div", { className: "flex items-center gap-2 border-b px-3", children: [_jsx(Search, { className: "text-muted-foreground h-4 w-4 shrink-0" }), _jsx(Input, { type: "text", ref: inputRef, value: query, onChange: (e) => setQuery(e.target.value), onKeyDown: handleInputKeyDown, placeholder: tHardcodedUi.raw(searchContents
116
121
  ? 'featuresProjectFilesComponentsFileSearch.searchContentsPlaceholder'
117
122
  : 'featuresProjectFilesComponentsFileSearch.line109JsxAttrPlaceholderSearchFiles'), className: "h-10 border-0 px-0 shadow-none focus-visible:ring-0" }), _jsx("button", { onClick: closeSearch, "aria-label": tI18nHardcoded.raw('autoFeaturesProjectFilesComponentsFileSearchJsxAttrAriaLabel732c1816'), className: "hover:bg-muted rounded p-1", children: _jsx(X, { className: "text-muted-foreground h-4 w-4" }) })] }), canSearchContents && (_jsx("div", { className: "flex items-center border-b", children: _jsx(Switch, { label: tHardcodedUi.raw('featuresProjectFilesComponentsFileSearch.searchContents'), "aria-label": tHardcodedUi.raw('featuresProjectFilesComponentsFileSearch.searchContents'), checked: contentMode, onCheckedChange: (checked) => {
118
123
  setContentMode(checked);
@@ -30,6 +30,7 @@ function BranchRow({ branch }) {
30
30
  const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
31
31
  return (_jsxs("div", { className: "flex items-center justify-start gap-2 py-0.5", children: [_jsx(Layers, { className: "text-muted-foreground size-4 shrink-0" }), _jsxs("div", { className: "flex flex-col items-start gap-0", children: [branch.subject && (_jsx("span", { className: "text-muted-foreground/80 truncate text-xs", children: branch.subject })), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-foreground truncate font-mono text-xs", children: displayBranchName(branch.name) }), branch.is_default && (_jsx(Badge, { variant: "kortix", size: "xs", className: "shrink-0 text-[11px]", children: tI18nComplete.raw('text37a8eec1ce19') }))] })] })] }));
32
32
  }
33
+ const NO_BRANCHES = [];
33
34
  const PICKER_TRIGGER_CLASS = 'py-2 flex-1 min-w-0 rounded-sm border-0 bg-transparent px-2 hover:bg-muted/40 focus:ring-0';
34
35
  export function OpenChangeRequestDialog({ open, onOpenChange, projectId, defaultBranch, session = null, initialHeadRef, onCreated, }) {
35
36
  const tHardcodedUi = useTranslations('hardcodedUi');
@@ -41,7 +42,7 @@ export function OpenChangeRequestDialog({ open, onOpenChange, projectId, default
41
42
  const activeSession = session ?? lastSessionRef.current;
42
43
  const sessionMode = activeSession !== null;
43
44
  const branchesQuery = useBranches({ enabled: open && !sessionMode, projectId });
44
- const branches = branchesQuery.data?.branches ?? [];
45
+ const branches = branchesQuery.data?.branches ?? NO_BRANCHES;
45
46
  const branchMap = useMemo(() => {
46
47
  const m = new Map();
47
48
  for (const b of branches)
@@ -0,0 +1,11 @@
1
+ import type { ProjectBranch } from '@kortix/sdk';
2
+ /** A session branch is named by its session id. A person knows it by the session's title (KRTX-3020). */
3
+ export declare function versionTitle(branch: ProjectBranch): string;
4
+ /** Search reads the title, the agent, the branch name and the last commit. */
5
+ export declare function versionMatches(branch: ProjectBranch, query: string): boolean;
6
+ /** The default branch, live work, then what the person archived or deleted, each in served order. */
7
+ export declare function groupVersions(branches: ProjectBranch[]): {
8
+ primary: ProjectBranch[];
9
+ others: ProjectBranch[];
10
+ archived: ProjectBranch[];
11
+ };
@@ -0,0 +1,26 @@
1
+ /** A session branch is named by its session id. A person knows it by the session's title (KRTX-3020). */
2
+ export function versionTitle(branch) {
3
+ return branch.session?.title?.trim() || branch.name;
4
+ }
5
+ /** Search reads the title, the agent, the branch name and the last commit. */
6
+ export function versionMatches(branch, query) {
7
+ const q = query.trim().toLowerCase();
8
+ if (!q)
9
+ return true;
10
+ return [branch.session?.title, branch.session?.agent_name, branch.name, branch.subject].some((value) => value?.toLowerCase().includes(q));
11
+ }
12
+ /** The default branch, live work, then what the person archived or deleted, each in served order. */
13
+ export function groupVersions(branches) {
14
+ const primary = [];
15
+ const others = [];
16
+ const archived = [];
17
+ for (const b of branches) {
18
+ if (b.is_default)
19
+ primary.push(b);
20
+ else if (b.session?.archived || b.session?.deleted)
21
+ archived.push(b);
22
+ else
23
+ others.push(b);
24
+ }
25
+ return { primary, others, archived };
26
+ }