@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
@@ -9,31 +9,36 @@ interface SessionBrief {
9
9
  changeRequests: readonly ChangeRequest[];
10
10
  /** The session's free-form labels; the sidebar row has no room for them. */
11
11
  labels?: readonly string[];
12
+ /** `needs-you` only: what the session waits on, see `SessionStatusMark`. */
13
+ waitingOn?: 'answer' | 'review';
14
+ /** The session was spawned by another one and is drawn under it. */
15
+ spawned?: boolean;
16
+ /** What this session started: its sub-agents and the sessions it spawned. */
17
+ subagentCount?: number;
18
+ spawnedCount?: number;
12
19
  }
13
20
  interface SessionBriefInteractionProps {
14
21
  projectId: string;
15
22
  }
16
23
  /**
17
- * Radix HoverCard strips every tab stop inside its content — `getTabbableNodes`
18
- * runs on each render and sets `tabindex="-1"` — because a hover card is a
19
- * preview surface, not a menu. Pointer clicks and `cmd`-clicks on the change
20
- * rows still work; the keyboard route to the same changes is the footer's
21
- * Review pill, which is a Popover and therefore fully tabbable. Do not try to
22
- * restore focusability here: the effect has no dependency array and will
23
- * overwrite whatever you set on the next commit.
24
+ * The one card for the whole session list. Rows do not own a card: the active
25
+ * row renders its brief into this host's slot (`SessionBriefCard`), and the
26
+ * host lines up with that row. Moving to the next row therefore moves the same
27
+ * card, with a 150ms glide, instead of unmounting one card and mounting another
28
+ * at a new place. Radix HoverCard cannot do this: it exports no `Anchor`, so a
29
+ * single instance cannot follow the pointer between triggers.
24
30
  *
25
- * Timing and exclusivity belong to `useSessionHoverStore`, not to this instance,
26
- * because both are properties of the LIST: only one card may be open at a time,
27
- * and the second row the pointer visits must not pay the opening delay again.
28
- * Radix's own delays are zeroed and its `onOpenChange` left unwired — with
29
- * `closeDelay={0}` it would report a close the instant the pointer left the row,
30
- * cancelling the window in which the pointer travels into the card. The store is
31
- * the only thing that decides whether this card is showing.
31
+ * The first open fades in over 100ms and does not move: the card opens to the
32
+ * right, into the path the pointer is already taking. The glide is only for a
33
+ * move between rows, never for keyboard focus or reduced motion.
32
34
  *
33
- * `animated={false}` for the reason submenus opt out of their enter animation:
34
- * the card opens to the right, into the path the pointer is already taking, so
35
- * an enter animation is time spent moving content away from the hand.
35
+ * The left padding is the gap between row and card. It is part of the card's
36
+ * box, so the pointer never leaves on its way in and the card needs no close
37
+ * grace for the crossing beyond the store's own.
38
+ *
39
+ * Mount it once, client-side only, where hover cards can show at all.
36
40
  */
41
+ export declare function SessionBriefCardHost(): import("react").ReactPortal;
37
42
  export declare function SessionBriefHoverCard({ sessionId, children, ...brief }: SessionBrief & SessionBriefInteractionProps & {
38
43
  sessionId: string;
39
44
  children: ReactElement;
@@ -42,10 +47,22 @@ export declare function SessionBriefHoverCard({ sessionId, children, ...brief }:
42
47
  * The same brief remains available to keyboard and screen-reader users when
43
48
  * the visual HoverCard is absent or intentionally ignored by assistive tech.
44
49
  */
45
- export declare function SessionBriefDescription({ id, status, createdAt, source, changeRequests, }: Omit<SessionBrief, 'title'> & {
50
+ export declare function SessionBriefDescription({ id, status, createdAt, source, changeRequests, waitingOn, }: Omit<SessionBrief, 'title'> & {
46
51
  id: string;
47
52
  }): import("react").JSX.Element;
48
53
  export declare function MobileSessionCreatedTime({ createdAt }: {
49
54
  createdAt: string;
50
55
  }): import("react").JSX.Element;
56
+ /**
57
+ * The hover card of a sub-agent row: what it is and when it ran. Same store,
58
+ * same shell as `SessionBriefHoverCard`, so one card is open at a time across
59
+ * sessions, sub-agents and spawned children, and moving between them is free.
60
+ */
61
+ export declare function SubagentHoverCard({ id, title, createdAt, updatedAt, children, }: {
62
+ id: string;
63
+ title: string;
64
+ createdAt: number | null;
65
+ updatedAt: number | null;
66
+ children: ReactElement;
67
+ }): import("react").JSX.Element;
51
68
  export {};
@@ -2,20 +2,25 @@
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { HoverPrefetchLink } from '../../internal/components/hover-prefetch-link.js';
4
4
  import { SESSION_STATUS_TRANSLATION_KEY, } from '../../session/actions/session-label.js';
5
- import { HoverCard, HoverCardContent, HoverCardTrigger } from '../../primitives/hover-card.js';
5
+ import { FLOATING_PANEL_SURFACE } from '../../primitives/menu-recipe.js';
6
+ import { usePortalContainer } from '../../lib/portal-container.js';
7
+ import { cn } from '../../lib/utils.js';
8
+ import { floatingZ, useDialogDepth } from '../../lib/z-stack.js';
6
9
  import { LocalTime } from '../../primitives/local-time.js';
7
10
  import { menuRow } from '../../primitives/menu-recipe.js';
8
11
  import { CR_ID_PREFIX } from '../review/review-actions.js';
9
12
  import { reviewHref } from '../../internal/lib/capability-tab-routes.js';
10
13
  import { useTranslations } from '../../i18n/index.js';
11
14
  import { useSessionHoverStore } from '../../internal/stores/session-hover-store.js';
12
- import { GitDiffIcon } from '@phosphor-icons/react';
15
+ import { ArrowsSplitIcon, GitDiffIcon, RobotIcon } from '@phosphor-icons/react';
13
16
  import { formatDistanceToNowStrict } from 'date-fns';
14
- import { useEffect } from 'react';
17
+ import { Slot } from 'radix-ui';
18
+ import { useEffect, useLayoutEffect, useRef } from 'react';
19
+ import { createPortal } from 'react-dom';
15
20
  import { shortRelative } from '../../session/actions/project-session-list-helpers.js';
16
21
  import { SessionLabelBadges } from '../sessions/session-label-badges.js';
17
22
  import { SOURCE_ICONS } from './session-source-icons.js';
18
- import { SessionStatusMark } from './session-status-mark.js';
23
+ import { isReviewOverdue, SessionStatusMark } from './session-status-mark.js';
19
24
  const DATE_TIME_OPTIONS = {
20
25
  year: 'numeric',
21
26
  month: 'short',
@@ -40,8 +45,10 @@ const CHANGE_REQUEST_STATUS_KEY = {
40
45
  merged: 'changeRequestStatus.merged',
41
46
  closed: 'changeRequestStatus.closed',
42
47
  };
43
- function useStatusLabel(status) {
48
+ function useStatusLabel(status, waitingOn) {
44
49
  const t = useTranslations('sidebar.sessionList.status');
50
+ if (status === 'needs-you' && waitingOn === 'answer')
51
+ return t('needsInput');
45
52
  return t(SESSION_STATUS_TRANSLATION_KEY[status]);
46
53
  }
47
54
  function SessionCreatedTime({ createdAt, className }) {
@@ -87,59 +94,118 @@ function ChangeRequestRow({ changeRequest, projectId, onDismiss, }) {
87
94
  * `px-2.5`, so 3.5 spacing steps is exactly where the row labels start. The
88
95
  * title and the change titles share one left edge.
89
96
  */
90
- function SessionBriefContent({ title, status, createdAt, source, changeRequests, labels, projectId, onDismiss, }) {
97
+ function SessionBriefContent({ title, status, createdAt, source, changeRequests, labels, waitingOn, spawned = false, subagentCount = 0, spawnedCount = 0, projectId, onDismiss, }) {
91
98
  const SourceIcon = source.kind === 'chat' ? null : SOURCE_ICONS[source.kind];
92
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "space-y-1.5 px-3.5 py-2.5", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [_jsx("p", { className: "text-foreground truncate text-sm font-medium", children: title }), _jsx("span", { className: "shrink-0", children: _jsx(SessionStatusMark, { status: status }) })] }), _jsx(RelativeCreatedTime, { createdAt: createdAt })] }), SourceIcon ? (_jsxs("div", { className: "text-muted-foreground flex min-w-0 items-center gap-2 text-xs", children: [_jsx(SourceIcon, { className: "size-3.5 shrink-0" }), _jsxs("span", { className: "text-foreground min-w-0 truncate", children: [source.label, source.triggerSlug ? (_jsxs("span", { className: "text-muted-foreground", children: [" \u00B7 ", source.triggerSlug] })) : null] })] })) : null, labels?.length ? _jsx(SessionLabelBadges, { session: { labels: [...labels] }, max: 6, className: "flex-wrap" }) : null] }), changeRequests.length > 0 ? (_jsx("ul", { className: "border-border max-h-64 overflow-y-auto overscroll-contain border-t p-1", children: changeRequests.map((changeRequest) => (_jsx("li", { children: _jsx(ChangeRequestRow, { changeRequest: changeRequest, projectId: projectId, onDismiss: onDismiss }) }, changeRequest.cr_id))) })) : null] }));
99
+ const statusLabel = useStatusLabel(status, waitingOn);
100
+ const t = useTranslations('sidebar.sessionList.brief');
101
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "space-y-1.5 px-3.5 py-2.5", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("p", { className: "text-foreground min-w-0 flex-1 truncate text-sm font-medium", children: title }), _jsx(RelativeCreatedTime, { createdAt: createdAt })] }), _jsxs("div", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsx(SessionStatusMark, { status: status, waitingOn: waitingOn, overdue: isReviewOverdue(changeRequests) }), _jsx("span", { children: statusLabel })] }), spawned && (_jsxs("div", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsx(ArrowsSplitIcon, { "aria-hidden": true, className: "size-4 shrink-0" }), _jsx("span", { children: t('spawned') })] })), subagentCount > 0 && (_jsxs("div", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsx(RobotIcon, { "aria-hidden": true, className: "size-4 shrink-0" }), _jsx("span", { children: t('subAgentCount', { count: subagentCount }) })] })), spawnedCount > 0 && (_jsxs("div", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsx(ArrowsSplitIcon, { "aria-hidden": true, className: "size-4 shrink-0" }), _jsx("span", { children: t('spawnedCount', { count: spawnedCount }) })] })), SourceIcon ? (_jsxs("div", { className: "text-muted-foreground flex min-w-0 items-center gap-2 text-xs", children: [_jsx(SourceIcon, { className: "size-3.5 shrink-0" }), _jsxs("span", { className: "text-foreground min-w-0 truncate", children: [source.label, source.triggerSlug ? (_jsxs("span", { className: "text-muted-foreground", children: [" \u00B7 ", source.triggerSlug] })) : null] })] })) : null, labels?.length ? _jsx(SessionLabelBadges, { session: { labels: [...labels] }, max: 6, className: "flex-wrap" }) : null] }), changeRequests.length > 0 ? (_jsx("ul", { className: "border-border max-h-64 overflow-y-auto overscroll-contain border-t p-1", children: changeRequests.map((changeRequest) => (_jsx("li", { children: _jsx(ChangeRequestRow, { changeRequest: changeRequest, projectId: projectId, onDismiss: onDismiss }) }, changeRequest.cr_id))) })) : null] }));
93
102
  }
103
+ const CARD_EDGE_PX = 8;
94
104
  /**
95
- * Radix HoverCard strips every tab stop inside its content — `getTabbableNodes`
96
- * runs on each render and sets `tabindex="-1"` — because a hover card is a
97
- * preview surface, not a menu. Pointer clicks and `cmd`-clicks on the change
98
- * rows still work; the keyboard route to the same changes is the footer's
99
- * Review pill, which is a Popover and therefore fully tabbable. Do not try to
100
- * restore focusability here: the effect has no dependency array and will
101
- * overwrite whatever you set on the next commit.
105
+ * The one card for the whole session list. Rows do not own a card: the active
106
+ * row renders its brief into this host's slot (`SessionBriefCard`), and the
107
+ * host lines up with that row. Moving to the next row therefore moves the same
108
+ * card, with a 150ms glide, instead of unmounting one card and mounting another
109
+ * at a new place. Radix HoverCard cannot do this: it exports no `Anchor`, so a
110
+ * single instance cannot follow the pointer between triggers.
111
+ *
112
+ * The first open fades in over 100ms and does not move: the card opens to the
113
+ * right, into the path the pointer is already taking. The glide is only for a
114
+ * move between rows, never for keyboard focus or reduced motion.
102
115
  *
103
- * Timing and exclusivity belong to `useSessionHoverStore`, not to this instance,
104
- * because both are properties of the LIST: only one card may be open at a time,
105
- * and the second row the pointer visits must not pay the opening delay again.
106
- * Radix's own delays are zeroed and its `onOpenChange` left unwired — with
107
- * `closeDelay={0}` it would report a close the instant the pointer left the row,
108
- * cancelling the window in which the pointer travels into the card. The store is
109
- * the only thing that decides whether this card is showing.
116
+ * The left padding is the gap between row and card. It is part of the card's
117
+ * box, so the pointer never leaves on its way in and the card needs no close
118
+ * grace for the crossing beyond the store's own.
110
119
  *
111
- * `animated={false}` for the reason submenus opt out of their enter animation:
112
- * the card opens to the right, into the path the pointer is already taking, so
113
- * an enter animation is time spent moving content away from the hand.
120
+ * Mount it once, client-side only, where hover cards can show at all.
114
121
  */
115
- export function SessionBriefHoverCard({ sessionId, children, ...brief }) {
116
- const open = useSessionHoverStore((state) => state.activeSessionId === sessionId);
122
+ export function SessionBriefCardHost() {
123
+ const activeSessionId = useSessionHoverStore((state) => state.activeSessionId);
124
+ const anchor = useSessionHoverStore((state) => state.anchor);
125
+ const via = useSessionHoverStore((state) => state.via);
126
+ const setSlot = useSessionHoverStore((state) => state.setSlot);
117
127
  const openSession = useSessionHoverStore((state) => state.openSession);
118
128
  const closeSession = useSessionHoverStore((state) => state.closeSession);
119
129
  const dismiss = useSessionHoverStore((state) => state.dismiss);
130
+ const container = usePortalContainer();
131
+ const depth = useDialogDepth();
132
+ const wrapperRef = useRef(null);
133
+ const shown = useRef(false);
134
+ const visible = !!activeSessionId && !!anchor;
135
+ // Before paint: line up with the row, kept inside the viewport.
136
+ useLayoutEffect(() => {
137
+ const el = wrapperRef.current;
138
+ if (!el || !visible || !anchor) {
139
+ shown.current = false;
140
+ return;
141
+ }
142
+ const rect = anchor.getBoundingClientRect();
143
+ const top = Math.max(CARD_EDGE_PX, Math.min(rect.top, window.innerHeight - CARD_EDGE_PX - el.offsetHeight));
144
+ const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
145
+ // A move between rows glides; the first open fades in, as the reminders card does.
146
+ el.style.transition =
147
+ shown.current && via === 'pointer' && !reduce
148
+ ? 'transform var(--duration-normal) var(--ease-in-out)'
149
+ : 'opacity var(--duration-fast) var(--ease-out)';
150
+ el.style.transform = `translate(${rect.right}px, ${top}px)`;
151
+ shown.current = true;
152
+ }, [visible, activeSessionId, anchor, via]);
153
+ // WCAG 1.4.13: dismissible without moving the pointer. A scroll moves the
154
+ // row away from the card, so it closes too, unless it scrolls the card itself.
155
+ useEffect(() => {
156
+ if (!visible)
157
+ return;
158
+ const onKey = (event) => event.key === 'Escape' && dismiss();
159
+ const onScroll = (event) => {
160
+ if (!wrapperRef.current?.contains(event.target))
161
+ dismiss();
162
+ };
163
+ document.addEventListener('keydown', onKey);
164
+ window.addEventListener('scroll', onScroll, true);
165
+ window.addEventListener('resize', dismiss);
166
+ return () => {
167
+ document.removeEventListener('keydown', onKey);
168
+ window.removeEventListener('scroll', onScroll, true);
169
+ window.removeEventListener('resize', dismiss);
170
+ };
171
+ }, [visible, dismiss]);
172
+ return createPortal(_jsx("div", { ref: wrapperRef, "data-slot": "session-brief-card", className: cn('fixed top-0 left-0 pl-3.5 starting:opacity-0', !visible && 'hidden'), style: { zIndex: floatingZ(depth) }, onPointerEnter: () => activeSessionId && openSession(activeSessionId), onPointerLeave: () => activeSessionId && closeSession(activeSessionId), children: _jsx("div", { ref: setSlot, className: cn(FLOATING_PANEL_SURFACE, 'w-72 overflow-hidden rounded-md shadow-xs') }) }), container ?? document.body);
173
+ }
174
+ /**
175
+ * A row's trigger plus its brief, rendered into the shared card while the row
176
+ * is active. Timing and exclusivity belong to `useSessionHoverStore`: only one
177
+ * card may show at a time, and the second row the pointer visits must not pay
178
+ * the opening delay again.
179
+ */
180
+ function SessionBriefCard({ id, children, content, }) {
181
+ const open = useSessionHoverStore((state) => state.activeSessionId === id);
182
+ const slot = useSessionHoverStore((state) => state.slot);
183
+ const openSession = useSessionHoverStore((state) => state.openSession);
184
+ const closeSession = useSessionHoverStore((state) => state.closeSession);
120
185
  // A row can be filtered out, or the sidebar unmounted, while its card shows.
121
186
  // Without this the store keeps naming a row that no longer exists and the
122
187
  // group is still warm the next time the list mounts.
123
188
  useEffect(() => () => {
124
189
  const store = useSessionHoverStore.getState();
125
- if (store.activeSessionId === sessionId)
190
+ if (store.activeSessionId === id)
126
191
  store.dismiss();
127
- }, [sessionId]);
128
- return (_jsxs(HoverCard, { open: open, openDelay: 0, closeDelay: 0, children: [_jsx(HoverCardTrigger, { asChild: true, onPointerEnter: () => openSession(sessionId), onPointerLeave: () => closeSession(sessionId), onFocus: () => openSession(sessionId), onBlur: () => closeSession(sessionId), children: children }), _jsx(HoverCardContent, { side: "right", align: "start", sideOffset: 14, animated: false, className: "w-72 overflow-hidden rounded-md p-0 shadow-xs", onPointerEnter: () => openSession(sessionId), onPointerLeave: () => closeSession(sessionId),
129
- // WCAG 1.4.13: content shown on hover must be dismissible without moving
130
- // the pointer. Radix's own dismissal path runs through `setOpen`, which
131
- // is inert here because the store owns `open`, so Escape is wired
132
- // straight to the store. DismissableLayer listens on the document, so it
133
- // fires even though nothing inside the card can hold focus.
134
- onEscapeKeyDown: dismiss, children: _jsx(SessionBriefContent, { ...brief, onDismiss: dismiss }) })] }));
192
+ }, [id]);
193
+ return (_jsxs(_Fragment, { children: [_jsx(Slot.Root, { "data-state": open ? 'open' : 'closed', onPointerEnter: (event) => {
194
+ if (event.pointerType !== 'touch')
195
+ openSession(id, event.currentTarget, 'pointer');
196
+ }, onPointerLeave: () => closeSession(id), onFocus: (event) => openSession(id, event.currentTarget, 'focus'), onBlur: () => closeSession(id), children: children }), open && slot ? createPortal(content, slot) : null] }));
197
+ }
198
+ export function SessionBriefHoverCard({ sessionId, children, ...brief }) {
199
+ const dismiss = useSessionHoverStore((state) => state.dismiss);
200
+ return (_jsx(SessionBriefCard, { id: sessionId, content: _jsx(SessionBriefContent, { ...brief, onDismiss: dismiss }), children: children }));
135
201
  }
136
202
  /**
137
203
  * The same brief remains available to keyboard and screen-reader users when
138
204
  * the visual HoverCard is absent or intentionally ignored by assistive tech.
139
205
  */
140
- export function SessionBriefDescription({ id, status, createdAt, source, changeRequests, }) {
206
+ export function SessionBriefDescription({ id, status, createdAt, source, changeRequests, waitingOn, }) {
141
207
  const t = useTranslations('sidebar.sessionList.brief');
142
- const statusLabel = useStatusLabel(status);
208
+ const statusLabel = useStatusLabel(status, waitingOn);
143
209
  return (_jsxs("span", { id: id, className: "sr-only", children: [t('status'), ": ", statusLabel, ". ", t('created'), ": ", _jsx(SessionCreatedTime, { createdAt: createdAt }), ".", source.kind !== 'chat'
144
210
  ? ` ${t('source')}: ${source.label}${source.triggerSlug ? `, ${source.triggerSlug}` : ''}.`
145
211
  : null, ' ', changeRequests.length > 0
@@ -151,3 +217,14 @@ export function SessionBriefDescription({ id, status, createdAt, source, changeR
151
217
  export function MobileSessionCreatedTime({ createdAt }) {
152
218
  return (_jsx(SessionCreatedTime, { createdAt: createdAt, className: "text-muted-foreground block truncate text-xs leading-4 tabular-nums" }));
153
219
  }
220
+ /**
221
+ * The hover card of a sub-agent row: what it is and when it ran. Same store,
222
+ * same shell as `SessionBriefHoverCard`, so one card is open at a time across
223
+ * sessions, sub-agents and spawned children, and moving between them is free.
224
+ */
225
+ export function SubagentHoverCard({ id, title, createdAt, updatedAt, children, }) {
226
+ const t = useTranslations('sidebar.sessionList.brief');
227
+ const created = createdAt ?? updatedAt;
228
+ const createdIso = created ? new Date(created).toISOString() : null;
229
+ return (_jsx(SessionBriefCard, { id: id, content: _jsxs("div", { className: "space-y-1.5 px-3.5 py-2.5", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("p", { className: "text-foreground min-w-0 flex-1 truncate text-sm font-medium", children: title }), createdIso && _jsx(RelativeCreatedTime, { createdAt: createdIso })] }), _jsxs("div", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsx(RobotIcon, { "aria-hidden": true, className: "size-4 shrink-0" }), _jsx("span", { children: t('subAgent') })] }), createdIso && (_jsxs("div", { className: "text-muted-foreground text-xs", children: [t('created'), ": ", _jsx(SessionCreatedTime, { createdAt: createdIso })] }))] }), children: children }));
230
+ }
@@ -1,5 +1,19 @@
1
1
  import type { SessionDisplayStatus } from '../../session/actions/session-label.js';
2
+ /**
3
+ * A review is overdue when an open change request has not moved for 24h.
4
+ * The row then turns the review glyph from blue (your turn) to yellow (still
5
+ * waiting on you). Merged and closed requests never count.
6
+ */
7
+ export declare function isReviewOverdue(changeRequests: readonly {
8
+ status: string;
9
+ updated_at: string;
10
+ }[], now?: number): boolean;
2
11
  /** A status glyph with stable 16px geometry across every session state. */
3
- export declare function SessionStatusMark({ status }: {
12
+ export declare function SessionStatusMark({ status, waitingOn, overdue, }: {
4
13
  status: SessionDisplayStatus;
14
+ /** `needs-you` only: an agent question or permission (`answer`) or a change
15
+ * request (`review`). An answer blocks the agent, so callers pass it first. */
16
+ waitingOn?: 'answer' | 'review';
17
+ /** `needs-you` review only: it has waited too long, see `isReviewOverdue`. */
18
+ overdue?: boolean;
5
19
  }): import("react").JSX.Element;
@@ -1,24 +1,70 @@
1
1
  'use client';
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
- import Loading from '../../primitives/loading.js';
4
- import { ClockCounterClockwiseIcon } from '@phosphor-icons/react';
5
- const STATUS_MARK_STYLE = {
6
- 'needs-you': { color: 'var(--kortix-green)', glyph: 'ring', fill: true },
7
- starting: { color: 'var(--kortix-yellow)', glyph: 'ring', fill: false },
8
- running: { color: 'var(--kortix-green)', glyph: 'ring', fill: true },
9
- done: { color: 'var(--muted-foreground)', glyph: 'check', fill: false },
10
- stopped: { color: 'var(--muted-foreground)', glyph: 'ring', fill: false },
11
- failed: { color: 'var(--kortix-red)', glyph: 'ring', fill: true },
12
- legacy: { color: 'var(--muted-foreground)', glyph: 'ring', fill: false },
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ClockCounterClockwiseIcon, GitDiffIcon, QuestionIcon } from '@phosphor-icons/react';
4
+ /**
5
+ * Color says who acts: gray nobody, green the agent, blue you (yellow once it waits
6
+ * over 24h), red broken.
7
+ * `starting` draws the stopped ring: a wake is meant to be instant, so it gets
8
+ * no state of its own, and blue and yellow stay reserved for `needs-you`.
9
+ */
10
+ const RING_STATUS_STYLE = {
11
+ starting: { color: 'var(--muted-foreground)', fill: false },
12
+ stopped: { color: 'var(--muted-foreground)', fill: false },
13
+ failed: { color: 'var(--kortix-red)', fill: true },
13
14
  };
15
+ const DOT_COUNT = 8;
16
+ const DOT_RADIUS = 6;
17
+ const DOTS = Array.from({ length: DOT_COUNT }, (_, i) => {
18
+ const angle = (i * 2 * Math.PI) / DOT_COUNT;
19
+ return { cx: 8 + DOT_RADIUS * Math.sin(angle), cy: 8 - DOT_RADIUS * Math.cos(angle) };
20
+ });
21
+ /** Behind the arc, clockwise: each dash fainter, so the still glyph reads as
22
+ * turning. Nothing animates: many rows can be working at once. */
23
+ const WORKING_TRAIL = [
24
+ { d: 'M2.84 4.39 A6.3 6.3 0 0 1 4.39 2.84', opacity: 0.85 },
25
+ { d: 'M1.8 9.1 A6.3 6.3 0 0 1 1.8 6.9', opacity: 0.55 },
26
+ { d: 'M4.39 13.16 A6.3 6.3 0 0 1 2.84 11.61', opacity: 0.25 },
27
+ ];
28
+ const GREEN_GLYPH = {
29
+ height: '16',
30
+ width: '16',
31
+ viewBox: '0 0 16 16',
32
+ style: { color: 'var(--kortix-green)' },
33
+ className: 'shrink-0',
34
+ 'aria-hidden': true,
35
+ };
36
+ /** `done` (agent idle, ready for a prompt): the dot ring filled with a core. */
37
+ function ReadyGlyph() {
38
+ return (_jsxs("svg", { ...GREEN_GLYPH, "data-glyph": "ready", children: [DOTS.map((dot, i) => (_jsx("circle", { "data-dot": "", cx: dot.cx, cy: dot.cy, r: "1", fill: "currentColor" }, i))), _jsx("circle", { "data-core": "", cx: "8", cy: "8", r: "3.6", fill: "currentColor" })] }));
39
+ }
40
+ /** `running`: a solid half arc leading a fading trail of dashes. */
41
+ function WorkingGlyph() {
42
+ return (_jsxs("svg", { ...GREEN_GLYPH, "data-glyph": "working", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [WORKING_TRAIL.map((dash) => (_jsx("path", { "data-dash": "", d: dash.d, opacity: dash.opacity }, dash.d))), _jsx("path", { d: "M8 1.7 A6.3 6.3 0 0 1 8 14.3" })] }));
43
+ }
44
+ const REVIEW_OVERDUE_MS = 24 * 60 * 60 * 1000;
45
+ /**
46
+ * A review is overdue when an open change request has not moved for 24h.
47
+ * The row then turns the review glyph from blue (your turn) to yellow (still
48
+ * waiting on you). Merged and closed requests never count.
49
+ */
50
+ export function isReviewOverdue(changeRequests, now = Date.now()) {
51
+ return changeRequests.some((cr) => cr.status === 'open' && now - Date.parse(cr.updated_at) >= REVIEW_OVERDUE_MS);
52
+ }
14
53
  /** A status glyph with stable 16px geometry across every session state. */
15
- export function SessionStatusMark({ status }) {
16
- const style = STATUS_MARK_STYLE[status];
17
- if (status === 'starting') {
18
- return _jsx(Loading, { className: "text-kortix-yellow size-3.5", "aria-hidden": true });
54
+ export function SessionStatusMark({ status, waitingOn = 'review', overdue = false, }) {
55
+ if (status === 'done')
56
+ return _jsx(ReadyGlyph, {});
57
+ if (status === 'running')
58
+ return _jsx(WorkingGlyph, {});
59
+ if (status === 'needs-you' && waitingOn === 'answer') {
60
+ return (_jsx(QuestionIcon, { "data-glyph": "question", className: "size-4 shrink-0", style: { color: 'var(--kortix-blue)' }, "aria-hidden": true }));
61
+ }
62
+ if (status === 'needs-you') {
63
+ return (_jsx(GitDiffIcon, { "data-glyph": "review", className: "size-4 shrink-0", style: { color: overdue ? 'var(--kortix-yellow)' : 'var(--kortix-blue)' }, "aria-hidden": true }));
19
64
  }
20
65
  if (status === 'legacy') {
21
- return (_jsx(ClockCounterClockwiseIcon, { className: "size-3.5 shrink-0", style: { color: style.color }, "aria-hidden": true }));
66
+ return (_jsx(ClockCounterClockwiseIcon, { className: "size-3.5 shrink-0", style: { color: 'var(--muted-foreground)' }, "aria-hidden": true }));
22
67
  }
23
- return (_jsx("svg", { height: "16", width: "16", viewBox: "0 0 16 16", strokeLinejoin: "round", style: { color: style.color }, className: "shrink-0", "aria-hidden": true, children: style.glyph === 'check' ? (_jsx("path", { d: "M4 8.4 L6.8 11.2 L12 5.2", stroke: "currentColor", fill: "none", strokeWidth: "1.5", strokeLinecap: "round" })) : (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "8", cy: "8", r: "6.3", stroke: "currentColor", fill: "none", strokeWidth: "1.5" }), style.fill && (_jsx("circle", { cx: "8", cy: "8", r: status === 'needs-you' ? 3.2 : 4, fill: "currentColor" }))] })) }));
68
+ const style = RING_STATUS_STYLE[status];
69
+ return (_jsxs("svg", { height: "16", width: "16", viewBox: "0 0 16 16", style: { color: style.color }, className: "shrink-0", "aria-hidden": true, children: [_jsx("circle", { cx: "8", cy: "8", r: "6.3", stroke: "currentColor", fill: "none", strokeWidth: "1.5" }), style.fill && _jsx("circle", { cx: "8", cy: "8", r: "4", fill: "currentColor" })] }));
24
70
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kortix/ui",
3
- "version": "0.14.4",
3
+ "version": "0.14.6",
4
4
  "description": "Kortix UI: the shared React component library for apps/web, Kortix Apps and SDK hosts. Tailwind v4 classes, themed by CSS variables.",
5
5
  "type": "module",
6
6
  "license": "Elastic-2.0",
@@ -1637,6 +1637,10 @@
1637
1637
  "types": "./dist/session/models/model-grouping.d.ts",
1638
1638
  "import": "./dist/session/models/model-grouping.js"
1639
1639
  },
1640
+ "./session/models/model-search-empty": {
1641
+ "types": "./dist/session/models/model-search-empty.d.ts",
1642
+ "import": "./dist/session/models/model-search-empty.js"
1643
+ },
1640
1644
  "./session/models/model-selector": {
1641
1645
  "types": "./dist/session/models/model-selector.d.ts",
1642
1646
  "import": "./dist/session/models/model-selector.js"
@@ -2153,7 +2157,7 @@
2153
2157
  "@extend-ai/react-docx": "^0.7.5",
2154
2158
  "@extend-ai/react-xlsx": "0.13.4",
2155
2159
  "@glideapps/glide-data-grid": "6.0.4-alpha24",
2156
- "@kortix/llm-catalog": "0.14.4",
2160
+ "@kortix/llm-catalog": "0.14.6",
2157
2161
  "@lezer/highlight": "1.2.3",
2158
2162
  "@paper-design/shaders-react": "^0.0.76",
2159
2163
  "@phosphor-icons/react": "2.1.10",
@@ -2242,7 +2246,7 @@
2242
2246
  "zustand": "^5.0.3"
2243
2247
  },
2244
2248
  "peerDependencies": {
2245
- "@kortix/sdk": "^0.14.4",
2249
+ "@kortix/sdk": "^0.14.6",
2246
2250
  "@tanstack/react-query": "^5.75.2",
2247
2251
  "@types/react": ">=18",
2248
2252
  "react": ">=19",
@@ -84,7 +84,7 @@ Theme with `@kortix/ui/theme`, never by forking a component: `themeToCss(theme)`
84
84
 
85
85
  | Import from | Names |
86
86
  | --- | --- |
87
- | `@kortix/ui` (adapters) | `cn`, `UiProvider`, `defaultUiStrings`, `useUiStrings`, `useUiLink`, `useUiImage`, `useUiBrand`, `UiStrings`, `UiStringsInput`, `UiLinkProps`, `UiLinkComponent`, `UiImageProps`, `UiImageComponent`, `UiBrand`, `UseUiBrand`, `useIsDark`, `useUiRouter`, `useUiShallowNavigate`, `UiShallowNavigate`, `useUiPathname`, `useUiSearchParams`, `useUiParams`, `defaultUiNavigation`, `UiNavigation`, `UiRouter`, `UiRouteParams`, `UiSearchParams`, `useUiViewerId`, `useUiHostConfig`, `defaultUiHostConfig`, `UiHostConfig`, `SessionIdsProvider`, `useSessionIds`, `UiSessionIds` |
87
+ | `@kortix/ui` (adapters) | `cn`, `UiProvider`, `defaultUiStrings`, `useUiStrings`, `useUiLink`, `useUiImage`, `useUiBrand`, `UiStrings`, `UiStringsInput`, `UiLinkProps`, `UiLinkComponent`, `UiImageProps`, `UiImageComponent`, `UiBrand`, `UseUiBrand`, `useIsDark`, `useUiRouter`, `useUiShallowNavigate`, `UiShallowNavigate`, `useUiPathname`, `useUiSearchParams`, `useUiParams`, `defaultUiNavigation`, `UiNavigation`, `UiRouter`, `UiRouteParams`, `UiSearchParams`, `useUiViewerId`, `useUiHostConfig`, `defaultUiHostConfig`, `UiHostConfig`, `UiSlashAction`, `SessionIdsProvider`, `useSessionIds`, `UiSessionIds` |
88
88
  | `@kortix/ui` (actions, display) | `Button`, `buttonVariants`, `ButtonProps`, `Badge`, `badgeVariants`, `Card`, `CardAction`, `CardButton`, `CardContent`, `CardDescription`, `CardEyebrow`, `CardFeature`, `CardFooter`, `CardGroup`, `CardHeader`, `CardImage`, `CardMedia`, `CardTitle`, `CardButtonProps`, `CardButtonSize`, `CardButtonVariant`, `CardGroupProps`, `CardLogo`, `CardProps`, `Separator`, `Skeleton` |
89
89
  | `@kortix/ui` (forms) | `Input`, `inputFocusClasses`, `inputInvalidClasses`, `inputSurfaceClasses`, `inputTransitionClasses`, `InputProps`, `useAutosizeTextArea`, `Textarea`, `AutosizeTextAreaRef`, `AutosizeTextAreaProps`, `Label`, `Checkbox`, `CheckboxProps`, `CheckboxVariant`, `Switch`, `SwitchProps`, `Select`, `SelectContent`, `SelectGroup`, `SelectItem`, `SelectLabel`, `SelectScrollDownButton`, `SelectScrollUpButton`, `SelectSeparator`, `SelectTrigger`, `SelectValue`, `SelectTriggerProps` |
90
90
  | `@kortix/ui` (overlays) | `Modal`, `ModalBody`, `ModalClose`, `ModalContent`, `ModalContentInner`, `ModalDescription`, `ModalFooter`, `ModalHeader`, `ModalOverlay`, `ModalPortal`, `ModalTitle`, `ModalTrigger`, `Hint`, `HintProps`, `Popover`, `PopoverAnchor`, `PopoverContent`, `PopoverTrigger`, `DropdownMenu`, `DropdownMenuCheckboxItem`, `DropdownMenuContent`, `DropdownMenuGroup`, `DropdownMenuItem`, `DropdownMenuLabel`, `DropdownMenuPortal`, `DropdownMenuRadioGroup`, `DropdownMenuRadioItem`, `DropdownMenuSeparator`, `DropdownMenuShortcut`, `DropdownMenuSub`, `DropdownMenuSubContent`, `DropdownMenuSubTrigger`, `DropdownMenuTrigger` |
@@ -1,47 +0,0 @@
1
- import { type SandboxProviderTransitionState, type UpdateProjectSandboxProviderResult } from '@kortix/sdk';
2
- import type { QueryClient } from '@tanstack/react-query';
3
- /**
4
- * A provider-migration transition never changes again once it reaches one of
5
- * these statuses (mirrors the API's TERMINAL_TRANSITION_STATUSES) — plus the
6
- * immediate `noop`/`cleared` markers a transition row can carry. Polling stops
7
- * on any of them.
8
- */
9
- export declare const SANDBOX_PROVIDER_TERMINAL_STATUSES: Set<string>;
10
- /** A missing/absent status (no live transition) is treated as terminal too. */
11
- export declare function isSandboxProviderTransitionTerminal(status: string | null | undefined): boolean;
12
- type CacheClient = Pick<QueryClient, 'setQueryData' | 'invalidateQueries'>;
13
- /**
14
- * FIX-L: apply the PATCH /sandbox-provider result to the query cache.
15
- *
16
- * Writes the project caches ONLY for the immediate `kind:'project'` result. A
17
- * `kind:'preparation'` result is a durable TRANSITION object, not a project —
18
- * writing it into `qk.project.summary(id)` would corrupt the cached project
19
- * shape (it has no repo_url / metadata / experimental_features …). On
20
- * preparation we leave the project cache untouched and return `'preparation'`
21
- * so the caller polls the transition instead. Returns the result's kind.
22
- */
23
- export declare function applySandboxProviderResult(queryClient: CacheClient, projectId: string, result: UpdateProjectSandboxProviderResult): 'project' | 'preparation';
24
- export interface PollSandboxProviderTransitionOptions {
25
- /** Injected for tests; defaults to the SDK poll call. */
26
- fetchState?: (projectId: string) => Promise<SandboxProviderTransitionState>;
27
- /** Called once when polling stops (terminal status, no transition, or exhausted).
28
- * `null` means the poll ended without a readable state (404/no-transition). */
29
- onSettled?: (state: SandboxProviderTransitionState | null) => void;
30
- maxAttempts?: number;
31
- baseDelayMs?: number;
32
- maxDelayMs?: number;
33
- /** Cooperative cancellation (e.g. component unmounted). */
34
- signal?: {
35
- aborted: boolean;
36
- };
37
- /** Injected for tests; defaults to setTimeout. */
38
- sleep?: (ms: number) => Promise<void>;
39
- }
40
- /**
41
- * FIX-L: poll the durable provider-migration transition after a `kind:'preparation'`
42
- * switch. Bounded (maxAttempts) with exponential backoff; stops on a terminal
43
- * status, and treats a 404 / no-transition / any read error as terminal (nothing
44
- * left to poll). Never throws — surfaces progress via `onSettled`.
45
- */
46
- export declare function pollSandboxProviderTransition(projectId: string, opts?: PollSandboxProviderTransitionOptions): Promise<SandboxProviderTransitionState | null>;
47
- export {};
@@ -1,79 +0,0 @@
1
- import { getProjectSandboxProviderTransition, } from '@kortix/sdk';
2
- import { qk } from '@kortix/sdk/react';
3
- /**
4
- * A provider-migration transition never changes again once it reaches one of
5
- * these statuses (mirrors the API's TERMINAL_TRANSITION_STATUSES) — plus the
6
- * immediate `noop`/`cleared` markers a transition row can carry. Polling stops
7
- * on any of them.
8
- */
9
- export const SANDBOX_PROVIDER_TERMINAL_STATUSES = new Set([
10
- 'activated',
11
- 'failed',
12
- 'superseded',
13
- 'cancelled',
14
- 'noop',
15
- 'cleared',
16
- ]);
17
- /** A missing/absent status (no live transition) is treated as terminal too. */
18
- export function isSandboxProviderTransitionTerminal(status) {
19
- return status == null || SANDBOX_PROVIDER_TERMINAL_STATUSES.has(status);
20
- }
21
- /**
22
- * FIX-L: apply the PATCH /sandbox-provider result to the query cache.
23
- *
24
- * Writes the project caches ONLY for the immediate `kind:'project'` result. A
25
- * `kind:'preparation'` result is a durable TRANSITION object, not a project —
26
- * writing it into `qk.project.summary(id)` would corrupt the cached project
27
- * shape (it has no repo_url / metadata / experimental_features …). On
28
- * preparation we leave the project cache untouched and return `'preparation'`
29
- * so the caller polls the transition instead. Returns the result's kind.
30
- */
31
- export function applySandboxProviderResult(queryClient, projectId, result) {
32
- if (result.kind !== 'project')
33
- return 'preparation';
34
- // Strip the discriminant so the cached value is a pure KortixProject.
35
- const { kind: _kind, ...project } = result;
36
- const cached = project;
37
- queryClient.setQueryData(qk.project.summary(projectId), cached);
38
- queryClient.setQueryData(qk.project.detail(projectId), (c) => c ? { ...c, project: cached } : c);
39
- queryClient.invalidateQueries({ queryKey: qk.project.detail(projectId) });
40
- // qk.projects.scope(), not the precise per-account form: restores the
41
- // reach the old bare projects-literal prefix match had — every account's
42
- // list, and the accountless slot the marketplace picker reads. A
43
- // sandbox-provider switch is rare — over-invalidating costs nothing.
44
- queryClient.invalidateQueries({ queryKey: qk.projects.scope() });
45
- return 'project';
46
- }
47
- /**
48
- * FIX-L: poll the durable provider-migration transition after a `kind:'preparation'`
49
- * switch. Bounded (maxAttempts) with exponential backoff; stops on a terminal
50
- * status, and treats a 404 / no-transition / any read error as terminal (nothing
51
- * left to poll). Never throws — surfaces progress via `onSettled`.
52
- */
53
- export async function pollSandboxProviderTransition(projectId, opts = {}) {
54
- const fetchState = opts.fetchState ?? getProjectSandboxProviderTransition;
55
- const maxAttempts = opts.maxAttempts ?? 60;
56
- const baseDelayMs = opts.baseDelayMs ?? 2_000;
57
- const maxDelayMs = opts.maxDelayMs ?? 15_000;
58
- const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
59
- let last = null;
60
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
61
- if (opts.signal?.aborted)
62
- return last;
63
- try {
64
- last = await fetchState(projectId);
65
- }
66
- catch {
67
- // 404 / no transition / transient read failure → nothing to keep polling.
68
- opts.onSettled?.(null);
69
- return null;
70
- }
71
- if (isSandboxProviderTransitionTerminal(last?.latest?.status)) {
72
- opts.onSettled?.(last);
73
- return last;
74
- }
75
- await sleep(Math.min(baseDelayMs * 2 ** attempt, maxDelayMs));
76
- }
77
- opts.onSettled?.(last);
78
- return last;
79
- }