@kortix/ui 0.14.5 → 0.14.7

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 (236) hide show
  1. package/dist/_vendor/kortix-shared.js +489 -381
  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-authority-card.js +1 -2
  12. package/dist/customize/agents/agent-detail-aside.js +15 -14
  13. package/dist/customize/agents/agent-drives-section.js +5 -6
  14. package/dist/customize/agents/agent-grant-pages.js +73 -76
  15. package/dist/customize/agents/agent-nav.prototype.d.ts +35 -0
  16. package/dist/customize/agents/agent-nav.prototype.js +95 -0
  17. package/dist/customize/agents/agent-page.js +59 -23
  18. package/dist/customize/agents/agent-people-section.js +18 -17
  19. package/dist/customize/agents/agent-triggers-section.js +6 -5
  20. package/dist/customize/agents/agents-page.js +18 -1
  21. package/dist/customize/connectors/add-mcp/add-mcp-server.js +4 -2
  22. package/dist/customize/connectors/catalog/catalog-entry.d.ts +14 -0
  23. package/dist/customize/connectors/catalog/catalog-entry.js +18 -1
  24. package/dist/customize/connectors/catalog/connector-browse.d.ts +28 -0
  25. package/dist/customize/connectors/catalog/connector-browse.js +18 -5
  26. package/dist/customize/connectors/connector-routes.d.ts +3 -1
  27. package/dist/customize/connectors/connector-routes.js +18 -0
  28. package/dist/customize/connectors/connectors-page.js +23 -4
  29. package/dist/customize/connectors/detail/app-page.js +19 -5
  30. package/dist/customize/connectors/detail/connector-advanced-tools.js +4 -6
  31. package/dist/customize/connectors/detail/connector-modal.d.ts +10 -13
  32. package/dist/customize/connectors/detail/connector-modal.js +18 -101
  33. package/dist/customize/connectors/detail/connector-notice.js +6 -2
  34. package/dist/customize/connectors/detail/connector-page.d.ts +30 -0
  35. package/dist/customize/connectors/detail/connector-page.js +26 -13
  36. package/dist/customize/providers/provider-accordion.js +2 -1
  37. package/dist/customize/providers/provider-connect.js +2 -1
  38. package/dist/customize/sections/component/telegram-channel-row.js +1 -1
  39. package/dist/customize/sections/connector-connection-modal.js +11 -9
  40. package/dist/customize/sections/connectors-view.js +11 -13
  41. package/dist/customize/sections/llm-provider/custom-provider-form.js +24 -7
  42. package/dist/customize/sections/llm-provider/custom-provider-panel.js +44 -8
  43. package/dist/customize/sections/llm-provider/types.d.ts +3 -0
  44. package/dist/customize/sections/llm-provider/utils.js +2 -2
  45. package/dist/customize/sections/view/agent-editor-access-fields.d.ts +9 -16
  46. package/dist/customize/sections/view/agent-editor-access-fields.js +30 -30
  47. package/dist/customize/sections/view/agent-editor-basics-fields.js +3 -10
  48. package/dist/customize/sections/view/agent-editor-primitives.d.ts +28 -10
  49. package/dist/customize/sections/view/agent-editor-primitives.js +32 -19
  50. package/dist/customize/sections/view/agent-editor.js +3 -3
  51. package/dist/customize/sections/view/gateway/gateway-routing.js +18 -21
  52. package/dist/customize/sections/view/grant-mode-field.d.ts +2 -5
  53. package/dist/customize/sections/view/grant-mode-field.js +10 -18
  54. package/dist/customize/sections/view/permission-editor.js +11 -8
  55. package/dist/customize/sections/view/secrets-view.js +2 -1
  56. package/dist/customize/settings/rail-copy-context.js +5 -3
  57. package/dist/customize/settings/tabs/general-tab.d.ts +9 -2
  58. package/dist/customize/settings/tabs/general-tab.js +33 -11
  59. package/dist/customize/settings/tabs/sandbox-template-form.js +9 -12
  60. package/dist/customize/settings/tabs/transfer-workspace-dialog.d.ts +4 -0
  61. package/dist/customize/settings/tabs/transfer-workspace-dialog.js +82 -0
  62. package/dist/customize/shared/capability-tabs.js +2 -1
  63. package/dist/customize/skills/skills-page.js +3 -5
  64. package/dist/customize/triggers/schedule/event-trigger-copy.js +3 -2
  65. package/dist/customize/triggers/schedule/schedule-builder.js +7 -4
  66. package/dist/customize/triggers/schedule/trigger-composer.js +5 -2
  67. package/dist/customize/triggers/schedule/when-event.js +1 -1
  68. package/dist/diff/diff-view.js +9 -10
  69. package/dist/files/editor/code-editor.js +2 -3
  70. package/dist/files/project/api/runtime-files.js +3 -2
  71. package/dist/files/project/components/change-request-detail-dialog.d.ts +3 -1
  72. package/dist/files/project/components/change-request-detail-dialog.js +3 -5
  73. package/dist/files/project/components/change-request-status-badge.d.ts +12 -0
  74. package/dist/files/project/components/change-request-status-badge.js +27 -0
  75. package/dist/files/project/components/change-requests-panel.js +1 -1
  76. package/dist/files/project/components/drive-explorer.js +2 -1
  77. package/dist/files/project/components/drive-grid-view.js +11 -7
  78. package/dist/files/project/components/drive-list-view.js +4 -4
  79. package/dist/files/project/components/file-breadcrumbs.js +1 -1
  80. package/dist/files/project/components/file-explorer-page.js +1 -1
  81. package/dist/files/project/components/file-history-popover.js +1 -6
  82. package/dist/files/project/components/file-search.js +6 -1
  83. package/dist/files/project/components/open-change-request-dialog.js +2 -1
  84. package/dist/files/project/components/version-selector-logic.d.ts +11 -0
  85. package/dist/files/project/components/version-selector-logic.js +26 -0
  86. package/dist/files/project/components/version-selector.js +11 -20
  87. package/dist/files/project/file-source.js +18 -1
  88. package/dist/files/project/hooks/use-file-content.js +2 -0
  89. package/dist/files/project/hooks/use-server-health.js +17 -15
  90. package/dist/files/renderers/docx/docx-viewer.js +0 -5
  91. package/dist/files/renderers/image-renderer.js +6 -24
  92. package/dist/files/renderers/pdf/pdf-viewer.js +1 -1
  93. package/dist/files/renderers/presentation/FullScreenPresentationViewer.js +2 -11
  94. package/dist/files/renderers/show-content-renderer.js +1 -1
  95. package/dist/files/renderers/sqlite-renderer.js +8 -3
  96. package/dist/files/renderers/xlsx/xlsx-viewer.js +15 -16
  97. package/dist/files/tree/components/file-tree-item.js +1 -1
  98. package/dist/files/viewer/file-header.js +1 -1
  99. package/dist/files/viewer/file-preview-modal.d.ts +1 -1
  100. package/dist/files/viewer/file-preview-modal.js +13 -3
  101. package/dist/files/viewer/file-source.d.ts +3 -1
  102. package/dist/files/viewer/use-file-content-state.js +27 -8
  103. package/dist/files/viewer/viewer-actions.js +2 -2
  104. package/dist/i18n/en.d.ts +355 -12
  105. package/dist/i18n/en.js +360 -17
  106. package/dist/internal/desktop/desktop-back-button.js +5 -3
  107. package/dist/internal/files/types.d.ts +2 -0
  108. package/dist/internal/goals/goal-meta.d.ts +30 -0
  109. package/dist/internal/goals/goal-meta.js +55 -0
  110. package/dist/internal/hooks/use-auto-scroll.js +5 -3
  111. package/dist/internal/lib/query-client-singleton.d.ts +9 -3
  112. package/dist/internal/lib/query-client-singleton.js +18 -12
  113. package/dist/internal/notifications/notification-rows.js +2 -1
  114. package/dist/internal/outcomes/outcome-types.d.ts +5 -0
  115. package/dist/internal/share/session-shares-modal.js +3 -7
  116. package/dist/internal/stores/kortix-computer-store.js +14 -2
  117. package/dist/internal/stores/session-hover-store.d.ts +32 -0
  118. package/dist/internal/stores/session-hover-store.js +38 -0
  119. package/dist/internal/stores/sidebar-nav-store.d.ts +1 -1
  120. package/dist/internal/stores/sidebar-nav-store.js +3 -1
  121. package/dist/lib/routes.d.ts +2 -0
  122. package/dist/lib/routes.js +1 -0
  123. package/dist/markdown/code/inline-code.js +8 -0
  124. package/dist/markdown/code/output-paths-context.d.ts +18 -0
  125. package/dist/markdown/code/output-paths-context.js +35 -0
  126. package/dist/markdown/genui/components/charts.js +6 -5
  127. package/dist/primitives/map.js +2 -1
  128. package/dist/primitives/preview-image.js +4 -2
  129. package/dist/primitives/settings-section-header.d.ts +1 -1
  130. package/dist/primitives/textarea.js +1 -1
  131. package/dist/primitives/typed-title.js +12 -14
  132. package/dist/primitives/typing-animation.js +4 -2
  133. package/dist/session/composer/agent-selector.js +6 -1
  134. package/dist/session/composer/attachment-tile.d.ts +0 -2
  135. package/dist/session/composer/attachment-tile.js +0 -8
  136. package/dist/session/composer/clipboard-files.d.ts +2 -1
  137. package/dist/session/composer/composer-chat-input.js +5 -3
  138. package/dist/session/composer/composer-draft-recovery.d.ts +1 -2
  139. package/dist/session/composer/composer-draft-recovery.js +0 -13
  140. package/dist/session/composer/composer-logic.js +8 -7
  141. package/dist/session/composer/context-ring.d.ts +1 -1
  142. package/dist/session/composer/context-ring.js +2 -2
  143. package/dist/session/composer/context-usage.d.ts +3 -6
  144. package/dist/session/composer/context-usage.js +3 -6
  145. package/dist/session/composer/draft/composer-draft.js +4 -4
  146. package/dist/session/composer/inbox-row-claims.d.ts +2 -2
  147. package/dist/session/composer/inbox-row-claims.js +1 -1
  148. package/dist/session/composer/pasted-text.js +3 -2
  149. package/dist/session/composer/queue-projection.d.ts +3 -2
  150. package/dist/session/composer/queued-prompt-edit.d.ts +1 -1
  151. package/dist/session/composer/reasoning-effort-selector.d.ts +2 -1
  152. package/dist/session/composer/session-composer-readiness.d.ts +7 -22
  153. package/dist/session/composer/session-composer-readiness.js +12 -40
  154. package/dist/session/composer/stores/queued-draft-store.js +3 -2
  155. package/dist/session/composer/token-progress.d.ts +1 -1
  156. package/dist/session/composer/token-progress.js +2 -2
  157. package/dist/session/header/reminder-parts.d.ts +11 -2
  158. package/dist/session/header/reminder-parts.js +9 -4
  159. package/dist/session/header/session-goal-indicator.d.ts +1 -0
  160. package/dist/session/header/session-goal-indicator.js +41 -0
  161. package/dist/session/header/session-site-header.js +2 -1
  162. package/dist/session/media/sandbox-url-detector.js +0 -7
  163. package/dist/session/outcomes/change-request-outcomes.js +24 -3
  164. package/dist/session/overrides/provider-secret-pool-editor.js +2 -1
  165. package/dist/session/panel/advanced/advanced-panel.js +3 -7
  166. package/dist/session/panel/easy/app-preview.js +12 -2
  167. package/dist/session/panel/easy/easy-panel-logic.d.ts +1 -1
  168. package/dist/session/panel/easy/easy-panel-logic.js +1 -1
  169. package/dist/session/panel/easy/outputs-card.js +2 -1
  170. package/dist/session/panel/easy/panel-card.js +5 -3
  171. package/dist/session/panel/easy/preview-load.d.ts +14 -0
  172. package/dist/session/panel/easy/preview-load.js +15 -0
  173. package/dist/session/panel/easy/step-detail-body.js +2 -7
  174. package/dist/session/panel/session-action-panel-column.js +11 -1
  175. package/dist/session/panel/session-audit-shared.d.ts +0 -4
  176. package/dist/session/panel/session-audit-shared.js +1 -1
  177. package/dist/session/panel/session-changes-shared.d.ts +2 -1
  178. package/dist/session/panel/session-panel-provider.js +6 -2
  179. package/dist/session/panel/shared/use-panel-derivations.js +6 -4
  180. package/dist/session/panel/use-session-panel-layout.js +7 -5
  181. package/dist/session/print/session-print-header.js +9 -8
  182. package/dist/session/prompts/session-permission-prompt.js +6 -3
  183. package/dist/session/status/provisioning-failure.d.ts +2 -1
  184. package/dist/session/status/session-error-banner.js +8 -6
  185. package/dist/session/status/session-starting-loader.d.ts +24 -31
  186. package/dist/session/status/session-starting-loader.js +32 -81
  187. package/dist/session/thread/chat-minimap-items.d.ts +4 -4
  188. package/dist/session/thread/first-prompt-handover.d.ts +3 -2
  189. package/dist/session/thread/message-parsing.d.ts +3 -3
  190. package/dist/session/thread/session-chat.js +15 -19
  191. package/dist/session/thread/session-history-scroll.d.ts +2 -1
  192. package/dist/session/thread/session-history.js +3 -1
  193. package/dist/session/thread/session-thread.js +1 -1
  194. package/dist/session/thread/transcript.js +13 -7
  195. package/dist/session/tool/shared/file-list.js +19 -2
  196. package/dist/session/tool/shared/infrastructure-contexts.d.ts +7 -0
  197. package/dist/session/tool/shared/infrastructure-contexts.js +7 -0
  198. package/dist/session/tool/shared/infrastructure-output.js +9 -5
  199. package/dist/session/tool/shared/infrastructure-row-header.js +9 -1
  200. package/dist/session/tool/shared/infrastructure-rows.js +7 -5
  201. package/dist/session/tool/tools/bash-tool.js +10 -6
  202. package/dist/session/tool/tools/web-search-tool.js +25 -3
  203. package/dist/session/turn/recovered-failures.d.ts +8 -0
  204. package/dist/session/turn/recovered-failures.js +32 -0
  205. package/dist/session/turn/streaming-cadence.js +5 -8
  206. package/dist/styles.css +1 -1
  207. package/dist/terminal/pty-connection.d.ts +5 -4
  208. package/dist/terminal/session-terminal-panel.js +3 -5
  209. package/dist/theme/theme.js +2 -1
  210. package/dist/workspace/index.d.ts +1 -0
  211. package/dist/workspace/index.js +1 -0
  212. package/dist/workspace/projects/modal/github-import-pickers.js +7 -6
  213. package/dist/workspace/review/review-center.js +13 -10
  214. package/dist/workspace/saved-session-skeleton-shape.d.ts +4 -3
  215. package/dist/workspace/saved-session-skeleton.d.ts +5 -1
  216. package/dist/workspace/saved-session-skeleton.js +5 -4
  217. package/dist/workspace/session-workspace.js +9 -14
  218. package/dist/workspace/sessions/deleted-session-row.js +5 -2
  219. package/dist/workspace/sidebar/footer/project-computer-nav.js +6 -4
  220. package/dist/workspace/sidebar/footer/project-goals-nav.d.ts +6 -0
  221. package/dist/workspace/sidebar/footer/project-goals-nav.js +26 -0
  222. package/dist/workspace/sidebar/footer/project-mcp-nav.js +6 -2
  223. package/dist/workspace/sidebar/modal/rename-session-modal.js +8 -3
  224. package/dist/workspace/sidebar/modal/session-embed-section.d.ts +25 -0
  225. package/dist/workspace/sidebar/modal/session-embed-section.js +71 -0
  226. package/dist/workspace/sidebar/modal/share-session-modal.js +2 -2
  227. package/dist/workspace/sidebar/nav-row-meta.js +14 -2
  228. package/dist/workspace/sidebar/project-nav-items.js +3 -0
  229. package/dist/workspace/sidebar/project-session-list.js +12 -3
  230. package/dist/workspace/sidebar/session-brief-hover-card.js +5 -8
  231. package/package.json +4 -4
  232. package/skills/kortix-ui/references/modules.md +2 -0
  233. package/dist/session/status/use-readiness-settling.d.ts +0 -18
  234. package/dist/session/status/use-readiness-settling.js +0 -43
  235. package/dist/workspace/sidebar/modal/session-public-link-section.d.ts +0 -13
  236. package/dist/workspace/sidebar/modal/session-public-link-section.js +0 -66
@@ -11,7 +11,7 @@ import { useTranslations } from '../../../i18n/index.js';
11
11
  */
12
12
  import { ShippingContainerIcon as Container, FileCodeIcon as FileCode, PackageIcon as Package, } from '@phosphor-icons/react';
13
13
  import { useMutation, useQueryClient } from '@tanstack/react-query';
14
- import { useEffect, useMemo, useState } from 'react';
14
+ import { useMemo, useState } from 'react';
15
15
  import { Button } from '../../../primitives/button.js';
16
16
  import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '../../../primitives/dialog.js';
17
17
  import { Input } from '../../../primitives/input.js';
@@ -39,7 +39,7 @@ export function SandboxTemplateForm({ projectId, open, onOpenChange, template, }
39
39
  const isEdit = !!template;
40
40
  const queryClient = useQueryClient();
41
41
  const [name, setName] = useState('');
42
- const [slug, setSlug] = useState('');
42
+ const [typedSlug, setSlug] = useState('');
43
43
  const [slugManuallyEdited, setSlugManuallyEdited] = useState(false);
44
44
  const [mode, setMode] = useState('image');
45
45
  const [image, setImage] = useState('');
@@ -49,10 +49,10 @@ export function SandboxTemplateForm({ projectId, open, onOpenChange, template, }
49
49
  const [memoryGb, setMemoryGb] = useState('4');
50
50
  const [diskGb, setDiskGb] = useState('20');
51
51
  // Reset / hydrate when opening
52
- useEffect(() => {
53
- if (!open)
54
- return;
55
- if (template) {
52
+ const [hydratedFor, setHydratedFor] = useState(null);
53
+ if (!hydratedFor || hydratedFor[0] !== open || hydratedFor[1] !== template) {
54
+ setHydratedFor([open, template]);
55
+ if (open && template) {
56
56
  setName(template.name);
57
57
  setSlug(template.slug);
58
58
  setSlugManuallyEdited(true);
@@ -71,7 +71,7 @@ export function SandboxTemplateForm({ projectId, open, onOpenChange, template, }
71
71
  setMemoryGb(String(template.memory_gb));
72
72
  setDiskGb(String(template.disk_gb));
73
73
  }
74
- else {
74
+ else if (open) {
75
75
  setName('');
76
76
  setSlug('');
77
77
  setSlugManuallyEdited(false);
@@ -83,12 +83,9 @@ export function SandboxTemplateForm({ projectId, open, onOpenChange, template, }
83
83
  setMemoryGb('6');
84
84
  setDiskGb('20');
85
85
  }
86
- }, [open, template]);
86
+ }
87
87
  // Auto-slug from name when the user hasn't typed a slug manually yet.
88
- useEffect(() => {
89
- if (!slugManuallyEdited)
90
- setSlug(slugify(name));
91
- }, [name, slugManuallyEdited]);
88
+ const slug = slugManuallyEdited ? typedSlug : slugify(name);
92
89
  const slugError = useMemo(() => {
93
90
  if (!slug)
94
91
  return null;
@@ -0,0 +1,4 @@
1
+ export declare function TransferWorkspaceEntry({ projectId, currentAccountId, }: {
2
+ projectId: string;
3
+ currentAccountId?: string;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1,82 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * Transfer-workspace entry point — the settings row and its dialog.
5
+ *
6
+ * The API (POST /v1/projects/:id/transfer) refuses a caller who holds the
7
+ * destructive tier on the project but no rights on the TARGET organization,
8
+ * and answers with the target's project cap when the move would exceed its
9
+ * plan — so this dialog only has to pick a target and say plainly what
10
+ * moves: sessions, files, settings and member grants follow the project;
11
+ * the usage ledger stays with the organization that consumed it.
12
+ *
13
+ * The project may land in an organization the mover cannot see afterwards
14
+ * (they are not a member there) — the same outcome the Delete flow produces,
15
+ * so success leaves the project the same way: forget the remembered landing
16
+ * target, close the settings overlay, replace into the landing door.
17
+ */
18
+ import { useMemo, useState } from 'react';
19
+ import { Button } from '../../../primitives/button.js';
20
+ import { ConfirmDialog } from '../../../primitives/confirm-dialog.js';
21
+ import { SettingsRow } from '../../../primitives/settings-row.js';
22
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../../primitives/select.js';
23
+ import { errorToast, successToast } from '../../../primitives/toast.js';
24
+ import { useSettingsPanelStore } from '../../../internal/stores/settings-panel-store.js';
25
+ import { useUiRouter, useUiViewerId } from '../../../providers/ui-provider.js';
26
+ import { useAccountsList } from '../../../internal/hooks/account/use-accounts-list.js';
27
+ import { PROJECT_LANDING_PATH } from '../../../internal/lib/onboarding/landing-destination.js';
28
+ import { forgetLastProjectId } from '../../../internal/lib/onboarding/last-project-cookie.js';
29
+ import { transferProject } from '@kortix/sdk';
30
+ import { qk } from '@kortix/sdk/react';
31
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
32
+ import { useTranslations } from '../../../i18n/index.js';
33
+ export function TransferWorkspaceEntry({ projectId, currentAccountId, }) {
34
+ const t = useTranslations('settings.workspace');
35
+ const [open, setOpen] = useState(false);
36
+ return (_jsxs(_Fragment, { children: [_jsx(SettingsRow, { label: t('transferWorkspace'), description: t('transferDescription'), children: _jsx(Button, { variant: "ghost", size: "sm", className: "text-destructive hover:bg-destructive/10 hover:text-destructive", onClick: () => setOpen(true), children: t('transferWorkspace') }) }), _jsx(TransferWorkspaceDialog, { projectId: projectId, currentAccountId: currentAccountId, open: open, onOpenChange: setOpen })] }));
37
+ }
38
+ function TransferWorkspaceDialog({ projectId, currentAccountId, open, onOpenChange, }) {
39
+ const t = useTranslations('settings.workspace');
40
+ const queryClient = useQueryClient();
41
+ const router = useUiRouter();
42
+ const viewerId = useUiViewerId();
43
+ const [targetId, setTargetId] = useState('');
44
+ // Same freshness contract as every other account-list reader: fetch only
45
+ // while the dialog is open.
46
+ const accountsQuery = useAccountsList({ enabled: open });
47
+ // The target cannot be the account the project already lives in — the API
48
+ // refuses that anyway — and a stale `currentAccountId` (project query still
49
+ // in flight) is harmless: the API re-checks.
50
+ const targetOptions = useMemo(() => (accountsQuery.data ?? []).filter((account) => account.account_id !== currentAccountId), [accountsQuery.data, currentAccountId]);
51
+ const transferMutation = useMutation({
52
+ mutationFn: () => transferProject(projectId, targetId),
53
+ onSuccess: () => {
54
+ successToast(t('transferSuccess'));
55
+ // Same leave-the-project flow as Delete: the moved project must stop
56
+ // being the remembered landing target, the global overlay must close
57
+ // before the navigation lands, and Back must not return to a project
58
+ // the viewer may no longer see.
59
+ forgetLastProjectId(viewerId, projectId);
60
+ useSettingsPanelStore.getState().close();
61
+ // qk.projects.scope(): lists across every account plus the accountless
62
+ // slot — the project just changed accounts, so every list form is
63
+ // stale at once. The moved project's own caches are unreachable after
64
+ // the replace below (the old id's entries gcTime out).
65
+ queryClient.invalidateQueries({ queryKey: qk.projects.scope() });
66
+ onOpenChange(false);
67
+ router.replace(PROJECT_LANDING_PATH);
68
+ },
69
+ onError: (error) => errorToast(error.message || t('transferFailed')),
70
+ });
71
+ const targetName = targetOptions.find((account) => account.account_id === targetId)?.name;
72
+ const changeOpen = (next) => {
73
+ if (transferMutation.isPending)
74
+ return;
75
+ if (!next) {
76
+ setTargetId('');
77
+ transferMutation.reset();
78
+ }
79
+ onOpenChange(next);
80
+ };
81
+ return (_jsx(ConfirmDialog, { open: open, onOpenChange: changeOpen, title: t('transferTitle'), description: _jsxs("div", { className: "space-y-4", children: [_jsx("p", { children: t('transferDialog') }), accountsQuery.isLoading ? (_jsx("p", { children: t('transferLoadingTargets') })) : accountsQuery.isError ? (_jsx(Button, { variant: "outline", onClick: () => accountsQuery.refetch(), children: t('retry') })) : targetOptions.length === 0 ? (_jsx("p", { children: t('transferNoTargets') })) : (_jsxs(Select, { value: targetId, onValueChange: setTargetId, children: [_jsx(SelectTrigger, { className: "w-full", "aria-label": t('transferTargetLabel'), children: _jsx(SelectValue, { placeholder: t('transferTargetPlaceholder') }) }), _jsx(SelectContent, { children: targetOptions.map((account) => (_jsx(SelectItem, { value: account.account_id, children: account.name }, account.account_id))) })] }))] }), cancelLabel: t('keepWorkspace'), confirmLabel: targetName ? t('transferMoveTo', { name: targetName }) : t('transferMove'), confirmVariant: "destructive", confirmDisabled: !targetName || accountsQuery.isError, isPending: transferMutation.isPending, onConfirm: () => transferMutation.mutate() }));
82
+ }
@@ -126,7 +126,8 @@ export function CapabilityTabs({ projectId }) {
126
126
  // once the project turns its flag on. `capabilityAccess()` applies the flags
127
127
  // and the leaves; before it settles the bar is invisible and both are left out.
128
128
  const access = useCapabilityAccess(projectId);
129
- const tabs = allTabs.filter((tab) => access.settled ? access.tabs.includes(tab.key) : !FLAG_GATED_TABS.includes(tab.key));
129
+ const allowedTabs = new Set(access.tabs);
130
+ const tabs = allTabs.filter((tab) => access.settled ? allowedTabs.has(tab.key) : !FLAG_GATED_TABS.includes(tab.key));
130
131
  const leading = tabs.filter((tab) => !TRAILING_TABS.includes(tab.key));
131
132
  const primary = leading.filter((tab) => PRIMARY_TABS.includes(tab.key));
132
133
  const library = leading.filter((tab) => !PRIMARY_TABS.includes(tab.key));
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { NewEntityMenu } from '../shared/new-entity-menu.js';
4
4
  import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
5
- import { useEffect, useMemo, useRef, useState } from 'react';
5
+ import { useMemo, useRef, useState } from 'react';
6
6
  import { Button } from '../../primitives/button.js';
7
7
  import { InputGroupSearch, InputGroupSearchClear, InputGroupSearchIcon, InputGroupSearchInput, } from '../../primitives/input-group.js';
8
8
  import { Tabs, TabsList, TabsTrigger } from '../../primitives/tabs.js';
@@ -85,10 +85,8 @@ export function SkillsPage({ projectId }) {
85
85
  });
86
86
  // The one honest auto-close: the config came back and this skill is not in
87
87
  // it, so it really was deleted or renamed.
88
- useEffect(() => {
89
- if (detail.isMissing)
90
- setSelectedPath(null);
91
- }, [detail.isMissing]);
88
+ if (detail.isMissing)
89
+ setSelectedPath(null);
92
90
  // `null` = render the grid. Otherwise which "nothing to show" copy applies:
93
91
  // genuinely zero skills vs. skills exist but this filter/search hid all of
94
92
  // them. Telling the user "No skills yet" in the second case is false and
@@ -411,10 +411,11 @@ export function newConnectorSlug(app, taken) {
411
411
  .toLowerCase()
412
412
  .replace(/[^a-z0-9_-]+/g, '-')
413
413
  .replace(/^-+|-+$/g, '') || 'app';
414
- if (!taken.includes(slug))
414
+ const takenSet = new Set(taken);
415
+ if (!takenSet.has(slug))
415
416
  return slug;
416
417
  for (let n = 2;; n++)
417
- if (!taken.includes(`${slug}-${n}`))
418
+ if (!takenSet.has(`${slug}-${n}`))
418
419
  return `${slug}-${n}`;
419
420
  }
420
421
  /* ─── Delivery ──────────────────────────────────────────────────────────── */
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { CalendarDotsIcon as CalendarClock, CaretDownIcon as ChevronDown } from '@phosphor-icons/react';
4
4
  import { useTranslations } from '../../../i18n/index.js';
5
- import { useCallback, useEffect, useState } from 'react';
5
+ import { useCallback, useState } from 'react';
6
6
  import { Button } from '../../../primitives/button.js';
7
7
  import { Input } from '../../../primitives/input.js';
8
8
  import { Label } from '../../../primitives/label.js';
@@ -195,13 +195,15 @@ export function ScheduleBuilder({ value, onChange, disabled, allowOnce, runAt, o
195
195
  const onceMode = Boolean(allowOnce && onRunAtChange && runAt != null);
196
196
  const isCustom = cronToState(rawCron) === null;
197
197
  const hasPastRunAt = Boolean(runAt && new Date(runAt).getTime() <= Date.now());
198
- useEffect(() => {
198
+ const [syncedValue, setSyncedValue] = useState(value);
199
+ if (value !== syncedValue) {
200
+ setSyncedValue(value);
199
201
  const parsed = cronToState(value);
200
202
  if (parsed)
201
203
  setState(parsed);
202
204
  setRawCron(value);
203
205
  setShowCron((visible) => visible || !parsed);
204
- }, [value]);
206
+ }
205
207
  const update = useCallback((partial) => {
206
208
  const next = { ...state, ...partial };
207
209
  setState(next);
@@ -256,6 +258,7 @@ export function ScheduleBuilder({ value, onChange, disabled, allowOnce, runAt, o
256
258
  const canOnce = Boolean(allowOnce && onRunAtChange);
257
259
  const cadences = localizeUiCatalog(CADENCES, tI18nComplete, APP_REGISTRY_TRANSLATION_KEYS);
258
260
  const weekdayButtons = localizeUiCatalog(WEEKDAY_BUTTONS, tI18nComplete, APP_REGISTRY_TRANSLATION_KEYS);
261
+ const selectedWeekdays = new Set(state.weekdays);
259
262
  const intervalUnit = (value) => state.frequency === 'minutes' ? 'minutes' : `hour${value === 1 ? '' : 's'}`;
260
263
  return (_jsxs("div", { className: cn('space-y-3', disabled && 'pointer-events-none opacity-60 select-none'), children: [_jsxs("div", { className: "flex flex-wrap items-end gap-3", children: [_jsx(ScheduleField, { label: tI18nComplete.raw('textb6b7a0065808'), className: "w-full sm:w-44", children: _jsxs(Select, { value: onceMode ? 'once' : cadence, onValueChange: (next) => next === 'once' ? selectOnce() : selectCadence(next), disabled: disabled, children: [_jsx(SelectTrigger, { className: "h-9 w-full", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [cadences.map((option) => (_jsx(SelectItem, { value: option.value, children: option.label }, option.value))), canOnce ? (_jsx(SelectItem, { value: "once", children: tI18nComplete.raw('textd88f6d8372f4') })) : null] })] }) }), onceMode ? (_jsx(ScheduleField, { label: tI18nComplete.raw('text4b4c31294fb5'), className: "w-full sm:w-auto", children: _jsx(Input, { type: "datetime-local", value: runAt ? isoToLocalInput(runAt) : '', min: isoToLocalInput(new Date().toISOString()), onChange: (event) => onRunAtChange?.(localInputToIso(event.target.value)), onBlur: () => {
261
264
  if (hasPastRunAt)
@@ -265,7 +268,7 @@ export function ScheduleBuilder({ value, onChange, disabled, allowOnce, runAt, o
265
268
  : [1, 2, 3, 4, 6, 8, 12]).map((value) => (_jsxs(SelectItem, { value: String(value), children: [value, " ", intervalUnit(value)] }, value))) })] }) })), state.frequency === 'monthly' && (_jsx(ScheduleField, { label: tTrigger('dayOfMonth'), className: "w-28", children: _jsxs(Select, { value: String(state.monthDay), onValueChange: (value) => update({ monthDay: Number(value) }), disabled: disabled, children: [_jsx(SelectTrigger, { className: "h-9 w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: Array.from({ length: 31 }, (_, index) => index + 1).map((value) => (_jsx(SelectItem, { value: String(value), children: value }, value))) })] }) })), state.frequency !== 'minutes' && (_jsx(ScheduleField, { label: state.frequency === 'hourly'
266
269
  ? tI18nComplete.raw('text5f2ed72dfcbc')
267
270
  : tI18nComplete.raw('text33b93476cf59'), children: _jsxs("div", { className: "flex items-center gap-1.5", children: [state.frequency !== 'hourly' && (_jsxs(_Fragment, { children: [_jsxs(Select, { value: String(state.hour), onValueChange: (value) => update({ hour: Number(value) }), disabled: disabled, children: [_jsx(SelectTrigger, { className: "h-9 w-18 tabular-nums", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: HOURS.map((value) => (_jsx(SelectItem, { value: String(value), children: pad2(value) }, value))) })] }), _jsx("span", { className: "text-muted-foreground", children: ":" })] })), _jsxs(Select, { value: String(state.minute), onValueChange: (value) => update({ minute: Number(value) }), disabled: disabled, children: [_jsx(SelectTrigger, { className: "h-9 w-18 tabular-nums", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: MINUTE_OPTIONS.map((value) => (_jsx(SelectItem, { value: String(value), children: state.frequency === 'hourly' ? `:${pad2(value)}` : pad2(value) }, value))) })] })] }) })), trailing] }))] }), !onceMode && cadence === 'weekly' && (_jsx("div", { className: "flex flex-wrap gap-1.5", "aria-label": tI18nComplete.raw('text2069f4549744'), children: weekdayButtons.map(({ value, label }) => {
268
- const selected = state.weekdays.includes(value);
271
+ const selected = selectedWeekdays.has(value);
269
272
  return (_jsx(Button, { type: "button", variant: selected ? 'secondary' : 'outline', size: "sm", "aria-pressed": selected, onClick: () => toggleWeekday(value), disabled: disabled, className: "min-w-10 transition-transform active:scale-[0.96]", children: label }, value));
270
273
  }) })), onceMode && runAt && hasPastRunAt ? (_jsx(SchedulePreview, { text: describeRunAt(runAt, tI18nComplete), error: true })) : null, !onceMode && isCustom ? (_jsx(SchedulePreview, { text: tI18nComplete.raw('text7afd348d0d4b') })) : null, !onceMode && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Button, { type: "button", variant: "text", size: "xs", onClick: () => setShowCron((visible) => !visible), disabled: disabled, className: "text-muted-foreground hover:text-foreground -ml-1 transition-transform active:scale-[0.96]", children: [_jsx(ChevronDown, { className: cn('size-3 transition-transform duration-(--duration-normal)', showCron && 'rotate-180') }), showCron
271
274
  ? tI18nComplete.raw('text6c848b936d1d')
@@ -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);
@@ -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,
@@ -194,12 +194,13 @@ export async function readFile(projectId, ref, filePath) {
194
194
  // text, and the JSON content read is byte-safe for it.
195
195
  if (hasExtension && getLanguageFromExt(name) !== 'plaintext') {
196
196
  const result = await readProjectFile(projectId, relativePath, ref);
197
- return { type: 'text', content: result.content };
197
+ return { type: 'text', content: result.content, revision: result.revision };
198
198
  }
199
199
  // Unknown or missing extension: the bytes decide, text or binary.
200
200
  const bytes = await readRawBytes(projectId, relativePath, ref);
201
201
  if (isTextBytes(bytes)) {
202
- return { type: 'text', content: new TextDecoder().decode(bytes), mimeType: 'text/plain; charset=utf-8' };
202
+ const result = await readProjectFile(projectId, relativePath, ref);
203
+ return { type: 'text', content: result.content, revision: result.revision, mimeType: 'text/plain; charset=utf-8' };
203
204
  }
204
205
  return { type: 'binary', content: '', mimeType: 'application/octet-stream' };
205
206
  }
@@ -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, }) {