@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.
- package/README.md +8 -4
- package/dist/access/access/access-dialog.js +8 -5
- package/dist/access/access/copy-row.js +2 -2
- package/dist/access/sharing-picker.js +2 -1
- package/dist/browser/app-in-browser.js +5 -3
- package/dist/browser/browser-panel.js +6 -6
- package/dist/browser/sandbox-browser-chrome.d.ts +4 -1
- package/dist/browser/sandbox-browser-chrome.js +4 -3
- package/dist/customize/agents/agent-access.d.ts +20 -0
- package/dist/customize/agents/agent-access.js +17 -0
- package/dist/customize/agents/agent-grant-pages.js +5 -6
- package/dist/customize/agents/agent-page.js +2 -1
- package/dist/customize/agents/agents-page.js +18 -1
- package/dist/customize/connectors/add-mcp/add-mcp-server.js +4 -2
- package/dist/customize/connectors/connector-routes.d.ts +3 -1
- package/dist/customize/connectors/connector-routes.js +18 -0
- package/dist/customize/connectors/connectors-page.js +23 -4
- package/dist/customize/connectors/detail/connector-advanced-tools.js +4 -6
- package/dist/customize/providers/provider-accordion.js +2 -1
- package/dist/customize/providers/provider-connect.js +2 -1
- package/dist/customize/sections/component/telegram-channel-row.js +1 -1
- package/dist/customize/sections/connector-connection-modal.js +11 -9
- package/dist/customize/sections/connectors-view.js +11 -13
- package/dist/customize/sections/llm-provider/constants.d.ts +0 -1
- package/dist/customize/sections/llm-provider/constants.js +0 -2
- package/dist/customize/sections/llm-provider/models-tab-group.d.ts +1 -1
- package/dist/customize/sections/llm-provider/models-tab-group.js +7 -4
- package/dist/customize/sections/llm-provider/models-tab-row.d.ts +2 -18
- package/dist/customize/sections/llm-provider/models-tab-row.js +6 -30
- package/dist/customize/sections/llm-provider/models-tab.d.ts +0 -1
- package/dist/customize/sections/llm-provider/models-tab.js +1 -10
- package/dist/customize/sections/llm-provider/use-connected-providers.js +2 -2
- package/dist/customize/sections/llm-provider/utils.d.ts +1 -1
- package/dist/customize/sections/llm-provider/utils.js +7 -14
- package/dist/customize/sections/view/gateway/_shared.js +1 -2
- package/dist/customize/sections/view/gateway/gateway-routing.js +18 -20
- package/dist/customize/sections/view/sandbox-provider-coverage.d.ts +3 -18
- package/dist/customize/sections/view/sandbox-provider-coverage.js +7 -27
- package/dist/customize/settings/rail-copy-context.js +5 -3
- package/dist/customize/settings/tabs/sandbox-tab.d.ts +2 -27
- package/dist/customize/settings/tabs/sandbox-tab.js +13 -112
- package/dist/customize/settings/tabs/sandbox-template-form.js +9 -12
- package/dist/customize/shared/capability-tabs.js +2 -1
- package/dist/customize/shared/entity/entity-modal.d.ts +5 -1
- package/dist/customize/shared/entity/entity-modal.js +7 -4
- package/dist/customize/skills/remove-skill-button.d.ts +16 -0
- package/dist/customize/skills/remove-skill-button.js +42 -0
- package/dist/customize/skills/skills-page.js +4 -6
- package/dist/customize/triggers/schedule/event-trigger-copy.js +3 -2
- package/dist/customize/triggers/schedule/schedule-builder.js +7 -4
- package/dist/customize/triggers/schedule/trigger-composer.js +5 -2
- package/dist/customize/triggers/schedule/trigger-list.d.ts +0 -2
- package/dist/customize/triggers/schedule/trigger-list.js +1 -18
- package/dist/customize/triggers/schedule/trigger-tile.d.ts +1 -32
- package/dist/customize/triggers/schedule/trigger-tile.js +1 -16
- package/dist/customize/triggers/schedule/when-event.js +1 -1
- package/dist/diff/diff-view.js +9 -10
- package/dist/files/editor/code-editor.js +2 -3
- package/dist/files/project/components/change-request-detail-dialog.d.ts +3 -1
- package/dist/files/project/components/change-request-detail-dialog.js +3 -5
- package/dist/files/project/components/change-request-status-badge.d.ts +12 -0
- package/dist/files/project/components/change-request-status-badge.js +27 -0
- package/dist/files/project/components/change-requests-panel.js +1 -1
- package/dist/files/project/components/drive-explorer.js +2 -1
- package/dist/files/project/components/drive-grid-view.js +11 -7
- package/dist/files/project/components/drive-list-view.js +4 -4
- package/dist/files/project/components/file-breadcrumbs.js +1 -1
- package/dist/files/project/components/file-explorer-page.js +1 -1
- package/dist/files/project/components/file-history-popover.js +1 -6
- package/dist/files/project/components/file-search.js +6 -1
- package/dist/files/project/components/open-change-request-dialog.js +2 -1
- package/dist/files/project/components/version-selector-logic.d.ts +11 -0
- package/dist/files/project/components/version-selector-logic.js +26 -0
- package/dist/files/project/components/version-selector.js +11 -20
- package/dist/files/project/hooks/use-change-requests.d.ts +14 -1
- package/dist/files/project/hooks/use-change-requests.js +44 -25
- package/dist/files/project/hooks/use-server-health.js +17 -15
- package/dist/files/renderers/docx/docx-viewer.js +4 -6
- package/dist/files/renderers/image-renderer.js +6 -24
- package/dist/files/renderers/pdf/pdf-renderer.d.ts +3 -1
- package/dist/files/renderers/pdf/pdf-renderer.js +2 -2
- package/dist/files/renderers/pdf/pdf-viewer.d.ts +3 -0
- package/dist/files/renderers/pdf/pdf-viewer.js +16 -7
- package/dist/files/renderers/presentation/FullScreenPresentationViewer.js +2 -11
- package/dist/files/renderers/show-content-renderer.js +1 -1
- package/dist/files/renderers/sqlite-renderer.js +8 -3
- package/dist/files/renderers/xlsx/xlsx-viewer.js +15 -16
- package/dist/files/tree/components/file-tree-item.js +1 -1
- package/dist/files/viewer/file-content-renderer.d.ts +4 -1
- package/dist/files/viewer/file-content-renderer.js +2 -2
- package/dist/files/viewer/file-preview-modal.js +1 -1
- package/dist/files/viewer/use-file-content-state.js +1 -1
- package/dist/i18n/en.d.ts +36 -35
- package/dist/i18n/en.js +45 -44
- package/dist/index.d.ts +1 -1
- package/dist/internal/desktop/desktop-back-button.js +5 -3
- package/dist/internal/hooks/use-auto-scroll.js +5 -3
- package/dist/internal/hooks/use-notification-center.js +21 -15
- package/dist/internal/hooks/use-review-session-summary.d.ts +6 -0
- package/dist/internal/hooks/use-review-session-summary.js +11 -0
- package/dist/internal/lib/browser-error-noise.d.ts +1 -1
- package/dist/internal/lib/browser-error-noise.js +1 -1
- package/dist/internal/lib/browser-noise/rules/rejections.d.ts +10 -0
- package/dist/internal/lib/browser-noise/rules/rejections.js +27 -1
- package/dist/internal/lib/provider-branding.js +14 -3
- package/dist/internal/lib/trigger-clock.d.ts +42 -0
- package/dist/internal/lib/trigger-clock.js +34 -0
- package/dist/internal/lib/web-notifications.d.ts +6 -0
- package/dist/internal/lib/web-notifications.js +57 -7
- package/dist/internal/notifications/notification-bell.d.ts +13 -0
- package/dist/internal/notifications/notification-bell.js +1 -1
- package/dist/internal/notifications/notification-rows.js +2 -1
- package/dist/internal/outcomes/outcome-types.d.ts +5 -0
- package/dist/internal/reminders/reminder-filter.d.ts +12 -0
- package/dist/internal/reminders/reminder-filter.js +34 -0
- package/dist/internal/reminders/reminder-format.d.ts +6 -0
- package/dist/internal/reminders/reminder-format.js +20 -2
- package/dist/internal/stores/kortix-computer-store.d.ts +1 -1
- package/dist/internal/stores/session-hover-store.d.ts +20 -5
- package/dist/internal/stores/session-hover-store.js +31 -13
- package/dist/internal/stores/session-panel-memory-store.js +2 -0
- package/dist/markdown/code/inline-code.js +8 -0
- package/dist/markdown/code/output-paths-context.d.ts +18 -0
- package/dist/markdown/code/output-paths-context.js +35 -0
- package/dist/markdown/genui/components/charts.js +6 -5
- package/dist/primitives/count-badge.d.ts +3 -1
- package/dist/primitives/count-badge.js +3 -3
- package/dist/primitives/glyph-paths-source.js +1 -1
- package/dist/primitives/glyph-paths.generated.js +1 -1
- package/dist/primitives/map.js +2 -1
- package/dist/primitives/preview-image.js +4 -2
- package/dist/primitives/textarea.js +1 -1
- package/dist/primitives/typed-title.js +12 -14
- package/dist/primitives/typing-animation.js +4 -2
- package/dist/providers/ui-provider.d.ts +57 -0
- package/dist/providers/ui-provider.js +4 -0
- package/dist/session/composer/ComposerCard.d.ts +2 -0
- package/dist/session/composer/ComposerCard.js +5 -1
- package/dist/session/composer/agent-selector.js +17 -5
- package/dist/session/composer/composer-chat-input.js +5 -3
- package/dist/session/composer/composer-logic.js +8 -7
- package/dist/session/composer/composer-toolbar.js +4 -2
- package/dist/session/composer/composer-underbar.js +3 -1
- package/dist/session/composer/composer.js +34 -5
- package/dist/session/composer/draft/composer-draft.js +4 -4
- package/dist/session/composer/menus/slash-actions.d.ts +22 -2
- package/dist/session/composer/menus/slash-actions.js +20 -0
- package/dist/session/composer/menus/slash-items.js +19 -3
- package/dist/session/composer/pasted-text.js +3 -2
- package/dist/session/composer/stores/queued-draft-store.js +3 -2
- package/dist/session/header/reminder-parts.d.ts +294 -0
- package/dist/session/header/reminder-parts.js +120 -0
- package/dist/session/header/session-reminders-indicator.d.ts +13 -0
- package/dist/session/header/session-reminders-indicator.js +142 -35
- package/dist/session/media/sandbox-url-detector.js +0 -7
- package/dist/session/models/model-availability.d.ts +23 -0
- package/dist/session/models/model-availability.js +20 -0
- package/dist/session/models/model-grouping.js +1 -4
- package/dist/session/models/model-selector.js +23 -14
- package/dist/session/models/use-model-connection-gate.js +4 -1
- package/dist/session/outcomes/change-request-outcomes.js +24 -3
- package/dist/session/outcomes/session-outcomes-provider.d.ts +3 -1
- package/dist/session/outcomes/session-outcomes-provider.js +31 -3
- package/dist/session/overrides/provider-secret-pool-editor.js +2 -1
- package/dist/session/overrides/session-overrides-composer.d.ts +1 -1
- package/dist/session/overrides/session-overrides-composer.js +5 -1
- package/dist/session/panel/advanced/advanced-panel.js +3 -7
- package/dist/session/panel/easy/app-preview.js +12 -2
- package/dist/session/panel/easy/easy-panel-logic.d.ts +1 -1
- package/dist/session/panel/easy/easy-panel-logic.js +1 -1
- package/dist/session/panel/easy/outputs-card.js +2 -1
- package/dist/session/panel/easy/panel-card.js +5 -3
- package/dist/session/panel/easy/preview-load.d.ts +14 -0
- package/dist/session/panel/easy/preview-load.js +15 -0
- package/dist/session/panel/easy/step-detail-body.js +2 -7
- package/dist/session/panel/mobile-tool-drawer.js +4 -2
- package/dist/session/panel/open-session-quick-view.js +2 -1
- package/dist/session/panel/panel-fallback.js +8 -4
- package/dist/session/panel/session-panel-provider.d.ts +1 -0
- package/dist/session/panel/session-panel-provider.js +19 -3
- package/dist/session/panel/session-reminders-panel.d.ts +4 -0
- package/dist/session/panel/session-reminders-panel.js +109 -0
- package/dist/session/panel/shared/use-panel-derivations.js +6 -4
- package/dist/session/panel/shared/use-panel-handoffs.d.ts +2 -2
- package/dist/session/panel/shared/use-panel-handoffs.js +5 -1
- package/dist/session/panel/use-session-panel-layout.js +7 -5
- package/dist/session/print/session-print-header.d.ts +0 -10
- package/dist/session/print/session-print-header.js +25 -9
- package/dist/session/status/session-surface.js +1 -1
- package/dist/session/thread/optimistic-turn.d.ts +5 -1
- package/dist/session/thread/optimistic-turn.js +8 -6
- package/dist/session/thread/session-chat.d.ts +6 -1
- package/dist/session/thread/session-chat.js +34 -12
- package/dist/session/thread/session-history.js +3 -1
- package/dist/session/thread/session-thread.d.ts +10 -1
- package/dist/session/thread/session-thread.js +2 -2
- package/dist/session/thread/transcript.js +13 -7
- package/dist/session/tool/shared/file-list.js +19 -2
- package/dist/session/tool/shared/infrastructure-contexts.d.ts +7 -0
- package/dist/session/tool/shared/infrastructure-contexts.js +7 -0
- package/dist/session/tool/shared/infrastructure-output.js +9 -5
- package/dist/session/tool/shared/infrastructure-row-header.js +9 -1
- package/dist/session/tool/shared/infrastructure-rows.js +7 -5
- package/dist/session/tool/tools/bash-tool.js +10 -6
- package/dist/session/tool/tools/web-search-tool.js +25 -3
- package/dist/session/turn/burst-step-body.js +4 -0
- package/dist/session/turn/recovered-failures.d.ts +8 -0
- package/dist/session/turn/recovered-failures.js +32 -0
- package/dist/session/turn/streaming-cadence.js +5 -8
- package/dist/styles.css +1 -1
- package/dist/terminal/session-terminal-panel.js +3 -5
- package/dist/theme/theme.js +2 -1
- package/dist/workspace/projects/modal/github-import-pickers.js +7 -6
- package/dist/workspace/review/review-center.js +13 -10
- package/dist/workspace/session-workspace.js +5 -9
- package/dist/workspace/sessions/deleted-session-row.js +5 -2
- package/dist/workspace/sidebar/footer/project-computer-nav.js +6 -4
- package/dist/workspace/sidebar/footer/project-mcp-nav.js +6 -2
- package/dist/workspace/sidebar/modal/rename-session-modal.js +8 -3
- package/dist/workspace/sidebar/project-session-list.js +52 -55
- package/dist/workspace/sidebar/session-brief-hover-card.d.ts +35 -18
- package/dist/workspace/sidebar/session-brief-hover-card.js +114 -37
- package/dist/workspace/sidebar/session-status-mark.d.ts +15 -1
- package/dist/workspace/sidebar/session-status-mark.js +63 -17
- package/package.json +7 -3
- package/skills/kortix-ui/SKILL.md +1 -1
- package/dist/customize/sections/view/sandbox-provider-result.d.ts +0 -47
- package/dist/customize/sections/view/sandbox-provider-result.js +0 -79
|
@@ -94,11 +94,9 @@ export function SessionTerminalPanel({ sessionId, projectId, projectSessionId, h
|
|
|
94
94
|
setOptimisticPty(null);
|
|
95
95
|
setTerminalPty(sessionId, null);
|
|
96
96
|
}, [sessionId, setTerminalPty]);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
}, [listedPty, optimisticPty?.id]);
|
|
97
|
+
if (listedPty && optimisticPty?.id === listedPty.id) {
|
|
98
|
+
setOptimisticPty(null);
|
|
99
|
+
}
|
|
102
100
|
useEffect(() => {
|
|
103
101
|
if (!terminalPtyId || isLoading || !ptys || pty || optimisticPty?.id === terminalPtyId)
|
|
104
102
|
return;
|
package/dist/theme/theme.js
CHANGED
|
@@ -123,8 +123,9 @@ function checkName(name) {
|
|
|
123
123
|
return name;
|
|
124
124
|
}
|
|
125
125
|
function steps(field, allowed, values) {
|
|
126
|
+
const allowedSet = new Set(allowed);
|
|
126
127
|
return Object.entries(values ?? {}).flatMap(([k, v]) => {
|
|
127
|
-
if (!
|
|
128
|
+
if (!allowedSet.has(k))
|
|
128
129
|
throw new Error(`@kortix/ui theme: unknown ${field} step "${k}"`);
|
|
129
130
|
return v === undefined ? [] : [[k, v]];
|
|
130
131
|
});
|
|
@@ -10,7 +10,7 @@ import { Github } from '../../../internal/icons/github.js';
|
|
|
10
10
|
import { cn } from '../../../internal/lib/utils.js';
|
|
11
11
|
import { CaretDownIcon as CaretDown, GitBranchIcon as GitBranch, MagnifyingGlassIcon as Search, } from '@phosphor-icons/react';
|
|
12
12
|
import { useTranslations } from '../../../i18n/index.js';
|
|
13
|
-
import {
|
|
13
|
+
import { useMemo, useState } from 'react';
|
|
14
14
|
/** Varied so the placeholder reads as a list of names, not a barcode. */
|
|
15
15
|
const SKELETON_WIDTHS = ['w-3/5', 'w-2/5', 'w-1/2', 'w-3/4', 'w-1/3'];
|
|
16
16
|
/** Above this many options the list is fixed-height (see `SearchPicker`). */
|
|
@@ -28,13 +28,14 @@ function SearchPicker({ value, options, loading, disabled, loadingLabel, placeho
|
|
|
28
28
|
const filtered = normalizedSearch
|
|
29
29
|
? options.filter((option) => option.keywords.includes(normalizedSearch))
|
|
30
30
|
: options;
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
const handleOpenChange = (next) => {
|
|
32
|
+
setOpen(next);
|
|
33
|
+
if (!next) {
|
|
33
34
|
setSearch('');
|
|
34
35
|
onSearchChange?.('');
|
|
35
36
|
}
|
|
36
|
-
}
|
|
37
|
-
return (_jsxs(Popover, { open: open, onOpenChange:
|
|
37
|
+
};
|
|
38
|
+
return (_jsxs(Popover, { open: open, onOpenChange: handleOpenChange, modal: false, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", disabled: disabled, className: cn(triggerVariants({ size: 'md' }), 'w-full'), children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground flex shrink-0 [&_svg]:size-4", children: icon }), _jsx("span", { className: cn('min-w-0 truncate', !selected && 'text-muted-foreground'), children: selected?.label ?? (firstLoad ? loadingLabel : placeholder) })] }), loading ? (_jsx(Loading, { variant: "spokes", className: "text-muted-foreground! size-4 shrink-0" })) : (_jsx(CaretDown, { className: cn(TRIGGER_CARET_CLASS, TRIGGER_ICON_SIZE.md) }))] }) }), _jsxs(PopoverContent, { side: "top", align: "start", avoidCollisions: false, className: "w-[var(--radix-popover-trigger-width)] overflow-hidden p-0", children: [_jsx("div", { className: "border-border border-b py-1", children: _jsxs(InputGroupSearch, { children: [_jsx(InputGroupSearchIcon, { children: _jsx(Search, {}) }), _jsx(InputGroupSearchInput, { size: "xs", className: "border-transparent focus:border-transparent focus:ring-0", value: search, onChange: (event) => {
|
|
38
39
|
setSearch(event.target.value);
|
|
39
40
|
onSearchChange?.(event.target.value);
|
|
40
41
|
}, placeholder: searchPlaceholder, autoCapitalize: "none", autoCorrect: "off", autoFocus: true })] }) }), firstLoad ? (
|
|
@@ -49,7 +50,7 @@ function SearchPicker({ value, options, loading, disabled, loadingLabel, placeho
|
|
|
49
50
|
const active = option.value === value;
|
|
50
51
|
return (_jsx("li", { children: _jsxs("button", { type: "button", "aria-pressed": active, className: cn(menuRow('md', 'default'), 'text-left', option.description && 'items-start', active && 'bg-primary/10 text-foreground'), onClick: () => {
|
|
51
52
|
onValueChange(option.value);
|
|
52
|
-
|
|
53
|
+
handleOpenChange(false);
|
|
53
54
|
}, children: [_jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: "text-foreground truncate", children: option.label }), option.description ? (_jsx("span", { className: "text-muted-foreground truncate text-xs", children: option.description })) : null] }), option.badge ? _jsx("span", { className: "shrink-0", children: option.badge }) : null] }) }, option.value));
|
|
54
55
|
}) }))] })] }));
|
|
55
56
|
}
|
|
@@ -347,15 +347,15 @@ export function ReviewCenter({ initialItems, segment: segmentProp, onSegmentChan
|
|
|
347
347
|
setSelectMode(false);
|
|
348
348
|
}
|
|
349
349
|
// If the active session filter no longer has items in this segment, reset it.
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
}, [sessionOpts, sessionFilter]);
|
|
350
|
+
if (sessionFilter !== 'all' && !sessionOpts.some((s) => s.sessionId === sessionFilter)) {
|
|
351
|
+
setSessionFilter('all');
|
|
352
|
+
}
|
|
355
353
|
// Keep the focus cursor in range as the visible list changes.
|
|
356
|
-
|
|
354
|
+
const [visibleCountSeen, setVisibleCountSeen] = useState(null);
|
|
355
|
+
if (visible.length !== visibleCountSeen) {
|
|
356
|
+
setVisibleCountSeen(visible.length);
|
|
357
357
|
setFocusedIdx((i) => Math.max(0, Math.min(i, visible.length - 1)));
|
|
358
|
-
}
|
|
358
|
+
}
|
|
359
359
|
// Select mode: DOM focus follows the cursor (j/k, a toggled row), so Space
|
|
360
360
|
// and Enter act on the row the user sees, and a pointer entry that
|
|
361
361
|
// unmounts the clicked checkbox keeps focus on its row.
|
|
@@ -377,12 +377,14 @@ export function ReviewCenter({ initialItems, segment: segmentProp, onSegmentChan
|
|
|
377
377
|
// mutation (there's no per-call settle callback threaded through `apply`),
|
|
378
378
|
// so clear the pending marker here rather than leave a button spinning
|
|
379
379
|
// forever if the item already left the list (e.g. a stale poll raced it).
|
|
380
|
-
|
|
380
|
+
const [initialItemsSeen, setInitialItemsSeen] = useState(initialItems);
|
|
381
|
+
if (initialItems !== initialItemsSeen) {
|
|
382
|
+
setInitialItemsSeen(initialItems);
|
|
381
383
|
if (initialItems)
|
|
382
384
|
setItems(initialItems);
|
|
383
385
|
setPendingId(null);
|
|
384
386
|
setPendingDecision(null);
|
|
385
|
-
}
|
|
387
|
+
}
|
|
386
388
|
// Detect items that arrived since the user last looked → flag them "fresh".
|
|
387
389
|
// The first load of each segment seeds its known set silently (nothing is
|
|
388
390
|
// "new" on open, or on a first visit to a segment).
|
|
@@ -711,9 +713,10 @@ export function ReviewCenter({ initialItems, segment: segmentProp, onSegmentChan
|
|
|
711
713
|
},
|
|
712
714
|
];
|
|
713
715
|
const confirmCount = bulkIds[confirm.verb].length;
|
|
716
|
+
const confirmIds = new Set(bulkIds[confirm.verb]);
|
|
714
717
|
// Every item the verb will decide: which app, what it does, to what, how risky.
|
|
715
718
|
const confirmList = (_jsx("ul", { className: "border-border divide-border max-h-72 w-full min-w-0 divide-y overflow-y-auto rounded-lg border", children: selectedItems
|
|
716
|
-
.filter((i) =>
|
|
719
|
+
.filter((i) => confirmIds.has(i.id))
|
|
717
720
|
.map((i) => {
|
|
718
721
|
const risk = RISK_BADGE[i.risk];
|
|
719
722
|
// The arguments, as the approval modal reads them: the content
|
|
@@ -21,7 +21,7 @@ import { useKortixComputerStore } from '../internal/stores/kortix-computer-store
|
|
|
21
21
|
import { sessionPreviewTabId, useSessionBrowserStore, } from '../internal/stores/session-browser-store.js';
|
|
22
22
|
import { ProjectScope } from '../internal/project-scope.js';
|
|
23
23
|
import { SessionThread } from '../session/thread/session-thread.js';
|
|
24
|
-
import { memo, useCallback,
|
|
24
|
+
import { memo, useCallback, useState } from 'react';
|
|
25
25
|
export const SessionLayout = memo(function SessionLayout({ sessionId, projectId, projectSessionId, children, bootStage = null, transient = false, }) {
|
|
26
26
|
const isMobile = useIsMobile();
|
|
27
27
|
const booting = !!bootStage;
|
|
@@ -129,15 +129,11 @@ function useSessionPanelBody({ sessionId, projectId, projectSessionId, isEasy, e
|
|
|
129
129
|
const showTerminal = !isEasy && effectiveView === 'terminal';
|
|
130
130
|
const showAudit = !isEasy && effectiveView === 'audit';
|
|
131
131
|
const [terminalActivated, setTerminalActivated] = useState(false);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
setTerminalActivated(true);
|
|
135
|
-
}, [showTerminal]);
|
|
132
|
+
if (showTerminal && !terminalActivated)
|
|
133
|
+
setTerminalActivated(true);
|
|
136
134
|
const [browserActivated, setBrowserActivated] = useState(false);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
setBrowserActivated(true);
|
|
140
|
-
}, [showBrowser]);
|
|
135
|
+
if (showBrowser && !browserActivated)
|
|
136
|
+
setBrowserActivated(true);
|
|
141
137
|
// Easy mode's body is the detail shell and nothing else — the cards moved to
|
|
142
138
|
// the floating overlay over the chat. The Advanced branches below are kept
|
|
143
139
|
// intact (Advanced is disabled, not deleted) and are unreachable while
|
|
@@ -5,6 +5,7 @@ import Loading from '../../primitives/loading.js';
|
|
|
5
5
|
import { getSessionDisplayTitle } from '../../session/actions/project-session-list-helpers.js';
|
|
6
6
|
import { useLocale, useTranslations } from '../../i18n/index.js';
|
|
7
7
|
import { relativeTime } from '../../internal/lib/relative-time.js';
|
|
8
|
+
import { useMemo } from 'react';
|
|
8
9
|
/**
|
|
9
10
|
* One row of Recently deleted (KRTX-2853): a session the viewer deleted (or,
|
|
10
11
|
* as a manager, one they can open), still inside its restore window. Not a
|
|
@@ -16,10 +17,12 @@ export function DeletedSessionRow({ session, restoring, onRestore, }) {
|
|
|
16
17
|
const title = getSessionDisplayTitle(session);
|
|
17
18
|
const deletedAt = session.deleted_at ? new Date(session.deleted_at) : null;
|
|
18
19
|
const restoreUntil = session.restore_until ? new Date(session.restore_until) : null;
|
|
19
|
-
const
|
|
20
|
+
const exactFormat = useMemo(() => new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeStyle: 'short' }), [locale]);
|
|
21
|
+
const dayFormat = useMemo(() => new Intl.DateTimeFormat(locale, { month: 'short', day: 'numeric' }), [locale]);
|
|
22
|
+
const exact = (date) => exactFormat.format(date);
|
|
20
23
|
return (_jsxs("div", { className: "bg-popover flex items-center gap-3 rounded-md border px-3 py-2", "data-deleted-session-id": session.session_id, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-foreground truncate text-sm", children: title }), _jsxs("p", { className: "text-muted-foreground truncate text-xs", children: [deletedAt ? (_jsx("time", { dateTime: session.deleted_at, title: exact(deletedAt), children: t('deletedAgo', {
|
|
21
24
|
when: relativeTime(session.deleted_at, locale, undefined, { style: 'long' }),
|
|
22
25
|
}) })) : null, deletedAt && restoreUntil ? ' · ' : null, restoreUntil ? (_jsx("time", { dateTime: session.restore_until, title: exact(restoreUntil), children: t('restorableUntil', {
|
|
23
|
-
date:
|
|
26
|
+
date: dayFormat.format(restoreUntil),
|
|
24
27
|
}) })) : null] })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", disabled: restoring, onClick: () => onRestore(session), "aria-label": t('restoreNamed', { title }), children: [restoring ? _jsx(Loading, { className: "size-3.5 shrink-0" }) : null, t('restore')] })] }));
|
|
25
28
|
}
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { PromoNavRow } from './promo-nav-row.js';
|
|
4
4
|
import { LaptopIcon } from '@phosphor-icons/react';
|
|
5
|
-
import { useCallback, useState } from 'react';
|
|
5
|
+
import { useCallback, useState, useSyncExternalStore } from 'react';
|
|
6
6
|
import Loading from '../../../primitives/loading.js';
|
|
7
7
|
import { useSidebar } from '../../../primitives/sidebar.js';
|
|
8
8
|
import { useConnectDesktopComputer, useOwnsPairedComputer, useThisComputerState, } from '../../../internal/tunnel/computer-connect.js';
|
|
9
9
|
import { useIsMobile } from '../../../internal/hooks/index.js';
|
|
10
10
|
import { useTranslations } from '../../../i18n/index.js';
|
|
11
11
|
export const COMPUTER_PROMO_DISMISSED_KEY = 'kortix.computer-promo.dismissed';
|
|
12
|
+
const subscribeNoop = () => () => { };
|
|
12
13
|
function readDismissed() {
|
|
13
14
|
try {
|
|
14
15
|
return window.localStorage.getItem(COMPUTER_PROMO_DISMISSED_KEY) === '1';
|
|
@@ -30,9 +31,10 @@ function readDismissed() {
|
|
|
30
31
|
* and closing the sheet unmounts it together with anything it renders.
|
|
31
32
|
*/
|
|
32
33
|
export function ProjectComputerNavItem({ projectId, onOpenConnect, }) {
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
const [
|
|
34
|
+
// The server and the hydrating render read "dismissed"; the stored flag applies after.
|
|
35
|
+
const stored = useSyncExternalStore(subscribeNoop, readDismissed, () => true);
|
|
36
|
+
const [dismissedNow, setDismissed] = useState(false);
|
|
37
|
+
const dismissed = stored || dismissedNow;
|
|
36
38
|
const dismiss = useCallback(() => {
|
|
37
39
|
setDismissed(true);
|
|
38
40
|
try {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { PromoNavRow } from './promo-nav-row.js';
|
|
4
|
-
import { useCallback, useState } from 'react';
|
|
4
|
+
import { useCallback, useState, useSyncExternalStore } from 'react';
|
|
5
5
|
import { useSidebar } from '../../../primitives/sidebar.js';
|
|
6
6
|
import { Mcp } from '../../../internal/icons/mcp.js';
|
|
7
7
|
import { useIsMobile } from '../../../internal/hooks/index.js';
|
|
8
8
|
import { useTranslations } from '../../../i18n/index.js';
|
|
9
9
|
export const MCP_PROMO_DISMISSED_KEY = 'kortix.mcp-promo.dismissed';
|
|
10
|
+
const subscribeNoop = () => () => { };
|
|
10
11
|
function readDismissed() {
|
|
11
12
|
try {
|
|
12
13
|
return window.localStorage.getItem(MCP_PROMO_DISMISSED_KEY) === '1';
|
|
@@ -26,7 +27,10 @@ export function ProjectMcpNavItem({ onOpenConnect }) {
|
|
|
26
27
|
const tComputers = useTranslations('computers');
|
|
27
28
|
const isMobile = useIsMobile();
|
|
28
29
|
const { setOpenMobile } = useSidebar();
|
|
29
|
-
|
|
30
|
+
// The server and the hydrating render read "dismissed"; the stored flag applies after.
|
|
31
|
+
const stored = useSyncExternalStore(subscribeNoop, readDismissed, () => true);
|
|
32
|
+
const [dismissedNow, setDismissed] = useState(false);
|
|
33
|
+
const dismissed = stored || dismissedNow;
|
|
30
34
|
const dismiss = useCallback(() => {
|
|
31
35
|
setDismissed(true);
|
|
32
36
|
try {
|
|
@@ -5,15 +5,20 @@ import { Input } from '../../../primitives/input.js';
|
|
|
5
5
|
import Loading from '../../../primitives/loading.js';
|
|
6
6
|
import { Modal, ModalBody, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, } from '../../../primitives/modal.js';
|
|
7
7
|
import { useTranslations } from '../../../i18n/index.js';
|
|
8
|
-
import {
|
|
8
|
+
import { useState } from 'react';
|
|
9
9
|
import { MAX_SESSION_NAME_LENGTH, useRenameSession } from '../../../session/actions/use-rename-session.js';
|
|
10
10
|
export function RenameSessionModal({ projectId, sessionId, currentName, open, onOpenChange, onSaved, }) {
|
|
11
11
|
const tI18nHardcoded = useTranslations('hardcodedUi');
|
|
12
12
|
const [value, setValue] = useState(currentName ?? '');
|
|
13
|
-
|
|
13
|
+
const [syncedFor, setSyncedFor] = useState([
|
|
14
|
+
open,
|
|
15
|
+
currentName,
|
|
16
|
+
]);
|
|
17
|
+
if (syncedFor[0] !== open || syncedFor[1] !== currentName) {
|
|
18
|
+
setSyncedFor([open, currentName]);
|
|
14
19
|
if (open)
|
|
15
20
|
setValue(currentName ?? '');
|
|
16
|
-
}
|
|
21
|
+
}
|
|
17
22
|
const renameMutation = useRenameSession(projectId, sessionId, {
|
|
18
23
|
onSuccess: () => {
|
|
19
24
|
onSaved?.();
|
|
@@ -17,7 +17,7 @@ import { useSidebar } from '../../primitives/sidebar.js';
|
|
|
17
17
|
import { Skeleton } from '../../primitives/skeleton.js';
|
|
18
18
|
import { errorToast, successToast } from '../../primitives/toast.js';
|
|
19
19
|
import { changeRequestKeys } from '../../files/project/hooks/use-change-requests.js';
|
|
20
|
-
import { useSessionNeedsYouCounts } from '../../internal/hooks/use-review-session-summary.js';
|
|
20
|
+
import { useSessionNeedsYouCounts, useSessionsAwaitingAnswer, } from '../../internal/hooks/use-review-session-summary.js';
|
|
21
21
|
import { childSessionHref, readRuntimeSessionParam, } from '../../session/tool/tools/session-spawn-urls.js';
|
|
22
22
|
import { SessionStarterMark, useSessionStarter, } from '../sessions/session-starter-mark.js';
|
|
23
23
|
import { SessionsEmptyState } from '../sessions/sessions-empty-state.js';
|
|
@@ -26,11 +26,11 @@ import { SessionDeleteModal } from '../../session/actions/session-delete-modal.j
|
|
|
26
26
|
import { SessionLabelsModal } from './modal/session-labels-modal.js';
|
|
27
27
|
import { ShareSessionModal } from './modal/share-session-modal.js';
|
|
28
28
|
import { getSessionDisplayTitle, groupChangeRequestsBySession, projectSessionsRefetchInterval, resolveSessionListViewState, shortRelative, starterSectionOf, } from '../../session/actions/project-session-list-helpers.js';
|
|
29
|
-
import { MobileSessionCreatedTime, SessionBriefDescription, SessionBriefHoverCard, } from './session-brief-hover-card.js';
|
|
29
|
+
import { MobileSessionCreatedTime, SessionBriefDescription, SessionBriefCardHost, SessionBriefHoverCard, SubagentHoverCard, } from './session-brief-hover-card.js';
|
|
30
30
|
import { SessionFilterMenu } from './session-filter-menu.js';
|
|
31
31
|
import { groupSessions, } from './session-grouping.js';
|
|
32
32
|
import { useSessionOpenIntent } from './session-open-intent.js';
|
|
33
|
-
import { SessionStatusMark } from './session-status-mark.js';
|
|
33
|
+
import { isReviewOverdue, SessionStatusMark } from './session-status-mark.js';
|
|
34
34
|
import { SessionTitle } from './session-title.js';
|
|
35
35
|
import { useMediaQuery } from '../../internal/hooks/index.js';
|
|
36
36
|
import { cn } from '../../internal/lib/utils.js';
|
|
@@ -40,7 +40,7 @@ import { selectCollapsedSections, selectGroupMode, selectHiddenSections, selectL
|
|
|
40
40
|
import { shouldBeginSessionSwitch, useSessionSwitchStore } from '../../internal/stores/session-switch-store.js';
|
|
41
41
|
import { directSubsessions, createProjectSession, listChangeRequests, restartProjectSession, sessionParentId, stopProjectSession, } from '@kortix/sdk';
|
|
42
42
|
import { qk, useProjectSession, useProjectSessions, useRuntimeSupports, useSessionChildren, } from '@kortix/sdk/react';
|
|
43
|
-
import { ArchiveIcon, CaretRightIcon, DotsThreeIcon, PushPinIcon, PushPinSlashIcon, GitForkIcon, PencilSimpleIcon, ArrowCounterClockwiseIcon as RotateCcw, ShareIcon as Share, SparkleIcon, SquareIcon as Square, TagIcon, TrashIcon, } from '@phosphor-icons/react';
|
|
43
|
+
import { ArchiveIcon, ArrowsSplitIcon, CaretRightIcon, DotsThreeIcon, PushPinIcon, PushPinSlashIcon, RobotIcon, GitForkIcon, PencilSimpleIcon, ArrowCounterClockwiseIcon as RotateCcw, ShareIcon as Share, SparkleIcon, SquareIcon as Square, TagIcon, TrashIcon, TreeViewIcon, } from '@phosphor-icons/react';
|
|
44
44
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
45
45
|
import { formatDistanceToNowStrict } from 'date-fns';
|
|
46
46
|
import { useUiPathname, useUiRouter, useUiSearchParams, useUiViewerId } from '../../providers/ui-provider.js';
|
|
@@ -227,6 +227,7 @@ export function ProjectSessionList({ projectId }) {
|
|
|
227
227
|
// summary the footer "Review" pill reads) plus open agent questions and
|
|
228
228
|
// connector approvals (KRTX-1739). A positive count makes a row `needs-you`.
|
|
229
229
|
const needsYouBySession = useSessionNeedsYouCounts(projectId);
|
|
230
|
+
const awaitingAnswer = useSessionsAwaitingAnswer(projectId);
|
|
230
231
|
// Grouping, ordering, and the two multi-select facets all live in the
|
|
231
232
|
// persisted session-filter store (keyed by project) — see SessionFilterMenu,
|
|
232
233
|
// which writes to the same store from the nested `⋯` menu.
|
|
@@ -302,7 +303,11 @@ export function ProjectSessionList({ projectId }) {
|
|
|
302
303
|
const isSwitchTarget = switchingToSessionId === session.session_id;
|
|
303
304
|
const children = directSubsessions(session);
|
|
304
305
|
const spawnedCount = nested ? 0 : (session.child_count ?? 0);
|
|
305
|
-
|
|
306
|
+
// Sub-agents and spawned sessions open together, under one toggle. A row
|
|
307
|
+
// whose sub-agent is the open page stays open, so that row stays visible.
|
|
308
|
+
const childRunsOpen = children.length + spawnedCount > 0 &&
|
|
309
|
+
(expandedIdSet.has(session.session_id) ||
|
|
310
|
+
(!!isActive && children.some((child) => child.id === activeRuntimeSessionId)));
|
|
306
311
|
return (_jsxs("div", { className: "space-y-px", children: [_jsx(ProjectSessionRow, { session: session, href: href, isActive: !!isActive && !activeRuntimeSessionId, isSwitching: isSwitchTarget, onNavigate: (event) => {
|
|
307
312
|
if (switchingToSessionId && session.session_id === activeSessionId) {
|
|
308
313
|
event.preventDefault();
|
|
@@ -313,7 +318,7 @@ export function ProjectSessionList({ projectId }) {
|
|
|
313
318
|
if (shouldBeginSessionSwitch(event, session.session_id, activeSessionId)) {
|
|
314
319
|
beginSessionSwitch(session.session_id);
|
|
315
320
|
}
|
|
316
|
-
}, displayTitle: getSessionDisplayTitle(session), inSharedGroup: inSharedGroup,
|
|
321
|
+
}, displayTitle: getSessionDisplayTitle(session), inSharedGroup: inSharedGroup, spawned: nested, subagentCount: children.length, spawnedCount: spawnedCount, childRunsOpen: childRunsOpen, onToggleChildRuns: () => toggleExpanded(projectId, session.session_id), reviewCount: needsYouBySession[session.session_id] ?? 0, awaitingAnswer: awaitingAnswer.has(session.session_id), changeRequests: changeRequestsBySession.get(session.session_id) ?? [], canShowHoverCard: canShowSessionHoverCard, onDelete: (id, label) => setSessionToDelete({ id, label }), onPin: (id, pinned) => void listActions.setPinned(id, pinned), onArchive: (id, archived) => void listActions.setArchived(id, archived), onShare: (s) => setSessionToShare(s), onRename: (id, name) => setSessionToRename({ id, name }), onEditLabels: setSessionToLabel, onRestart: (id, label) => restartMutation.mutate({ sessionId: id, label }), isRestarting: restartMutation.isPending && restartMutation.variables?.sessionId === session.session_id, onStop: (id, label) => stopMutation.mutate({ sessionId: id, label }), isStopping: stopMutation.isPending && stopMutation.variables?.sessionId === session.session_id, canFork: runtimeForks &&
|
|
317
322
|
session.session_id === activeSessionId &&
|
|
318
323
|
!!(session.runtime_session_id ?? session.opencode_session_id), isForking: forkSession.isPending &&
|
|
319
324
|
forkSession.variables?.sessionId ===
|
|
@@ -325,28 +330,7 @@ export function ProjectSessionList({ projectId }) {
|
|
|
325
330
|
onError: (err) => {
|
|
326
331
|
errorToast(err instanceof Error ? err.message : tI18nComplete.raw('text32ad3abe4479'));
|
|
327
332
|
},
|
|
328
|
-
}) }), children.
|
|
329
|
-
// `ml-4` puts the trunk's local x=0 exactly under the parent's
|
|
330
|
-
// icon center (px-2 + half of size-4, both 2 tokens = 4 tokens
|
|
331
|
-
// total). No padding on this wrapper: padding here would shift an
|
|
332
|
-
// absolutely-positioned child's own `left:0` inward with it (it
|
|
333
|
-
// resolves against the padding box), throwing off the trunk.
|
|
334
|
-
//
|
|
335
|
-
// The trunk is ONE span for the whole block, not one per row: N
|
|
336
|
-
// separate `top-4 bottom-0` segments stacked edge-to-edge should
|
|
337
|
-
// in theory touch with zero gap, but sub-pixel rounding at each
|
|
338
|
-
// row boundary showed up as visible hairline breaks.
|
|
339
|
-
//
|
|
340
|
-
// The trunk stops at the TOP of the last row (each row is a fixed
|
|
341
|
-
// `h-8`, so that is `(N - 1) * 8` tokens). The last row's own
|
|
342
|
-
// elbow draws the rest and curves away. Running the trunk to the
|
|
343
|
-
// last row's center instead leaves a straight tail below the
|
|
344
|
-
// point where the elbow starts to curve.
|
|
345
|
-
_jsxs("div", { className: "relative ml-4", children: [children.length > 1 && (_jsx("span", { "aria-hidden": true, className: "border-border pointer-events-none absolute top-0 left-0 border-l-2", style: { height: `calc(var(--spacing) * ${(children.length - 1) * 8})` } })), children.map((child) => {
|
|
346
|
-
const childHref = childSessionHref(href, child.id);
|
|
347
|
-
const activeChild = !!isActive && activeRuntimeSessionId === child.id;
|
|
348
|
-
return (_jsxs("div", { className: "relative h-8", children: [_jsx(SubAgentConnector, {}), _jsx("div", { style: { marginLeft: SUB_AGENT_CONNECTOR_RUN }, children: _jsx(ProjectSubsessionRow, { title: child.title || 'Sub-session', href: childHref, isActive: activeChild, updatedAt: child.updated_at }) })] }, child.id));
|
|
349
|
-
})] })), spawnedOpen && (_jsx(SpawnedSessions, { projectId: projectId, parentId: session.session_id, renderNode: (child) => renderSessionNode(child, true) }))] }, session.session_id));
|
|
333
|
+
}) }), childRunsOpen && (_jsx(ChildRuns, { projectId: projectId, parentId: session.session_id, subagents: children, spawnedCount: spawnedCount, renderSubagent: (child) => (_jsx(ProjectSubsessionRow, { id: child.id, title: child.title || 'Sub-session', href: childSessionHref(href, child.id), isActive: !!isActive && activeRuntimeSessionId === child.id, createdAt: child.created_at, updatedAt: child.updated_at })), renderSpawned: (child) => renderSessionNode(child, true) }))] }, session.session_id));
|
|
350
334
|
};
|
|
351
335
|
// Everything below the header — skeleton, error, empty, or the grouped list.
|
|
352
336
|
// Kept as one function so the header stays mounted across all four states
|
|
@@ -383,12 +367,12 @@ export function ProjectSessionList({ projectId }) {
|
|
|
383
367
|
if (viewState !== 'more' && grouped.sections.length === 0 && visibleOtherCount === 0) {
|
|
384
368
|
return (_jsx("div", { className: "text-muted-foreground px-2 pt-1 pb-2 text-xs", children: t('allSectionsHidden') }));
|
|
385
369
|
}
|
|
386
|
-
return (_jsxs(FadedScrollArea, { fadeColor: "from-background", className: "h-full min-h-0 space-y-px", children: [_jsx(StarterSection, { title: tPinArchive('section'), query: pinnedQuery, sessions: visiblePinned, open: !collapsedSectionIds.has(PINNED_SECTION_ID), onToggle: () => toggleSectionCollapsed(projectId, PINNED_SECTION_ID), renderNode: (session) => renderSessionNode(session, false) }), visiblePinned.length > 0 && (_jsx("div", { className: "px-2 py-1", children: _jsx(Separator, {}) })), grouped.sections.map((section) => (_jsx(SessionListSection, { section: section, projectId: projectId, sessions: sessions, reviewCountBySession: needsYouBySession, showHeader: grouped.showHeaders, open: !collapsedSectionIds.has(section.id), onOpenChange: () => toggleSectionCollapsed(projectId, section.id), children: section.sessions.map((session) => renderSessionNode(session, false)) }, section.id))), hasNextPage && (_jsx(ShowMoreButton, { loading: isFetchingNextPage, failed: isFetchNextPageError, label: t('loadMore'), onClick: () => fetchNextPage() })), _jsx(StarterSection, { title: t('startedBy.others'), query: sharedQuery, sessions: visibleShared, open: expandedIdSet.has('section:shared'), onToggle: () => toggleExpanded(projectId, 'section:shared'), renderNode: (session) => renderSessionNode(session, false, true) }), _jsx(StarterSection, { title: t('startedBy.automated'), query: automatedQuery, sessions: visibleAutomated, open: expandedIdSet.has('section:automated'), onToggle: () => toggleExpanded(projectId, 'section:automated'), renderNode: (session) => renderSessionNode(session, false) }), firstChatPending &&
|
|
370
|
+
return (_jsxs(FadedScrollArea, { fadeColor: "from-background", className: "h-full min-h-0 space-y-px", children: [_jsx(StarterSection, { title: tPinArchive('section'), query: pinnedQuery, sessions: visiblePinned, open: !collapsedSectionIds.has(PINNED_SECTION_ID), onToggle: () => toggleSectionCollapsed(projectId, PINNED_SECTION_ID), renderNode: (session) => renderSessionNode(session, false), caretOnHover: true }), visiblePinned.length > 0 && !grouped.showHeaders && (_jsx("div", { className: "px-2 py-1", children: _jsx(Separator, {}) })), grouped.sections.map((section) => (_jsx(SessionListSection, { section: section, projectId: projectId, sessions: sessions, reviewCountBySession: needsYouBySession, showHeader: grouped.showHeaders, open: !collapsedSectionIds.has(section.id), onOpenChange: () => toggleSectionCollapsed(projectId, section.id), children: section.sessions.map((session) => renderSessionNode(session, false)) }, section.id))), hasNextPage && (_jsx(ShowMoreButton, { loading: isFetchingNextPage, failed: isFetchNextPageError, label: t('loadMore'), onClick: () => fetchNextPage() })), _jsx(StarterSection, { title: t('startedBy.others'), query: sharedQuery, sessions: visibleShared, open: expandedIdSet.has('section:shared'), onToggle: () => toggleExpanded(projectId, 'section:shared'), renderNode: (session) => renderSessionNode(session, false, true) }), _jsx(StarterSection, { title: t('startedBy.automated'), query: automatedQuery, sessions: visibleAutomated, open: expandedIdSet.has('section:automated'), onToggle: () => toggleExpanded(projectId, 'section:automated'), renderNode: (session) => renderSessionNode(session, false), caretOnHover: true }), firstChatPending &&
|
|
387
371
|
!hasNextPage &&
|
|
388
372
|
!sharedQuery.hasNextPage &&
|
|
389
373
|
!automatedQuery.hasNextPage && (_jsx(FirstChatRow, { projectId: projectId, isActive: pathname === uiRoutes().project(projectId) && isFirstChatRequested(searchParams) }))] }));
|
|
390
374
|
}
|
|
391
|
-
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col space-y-px", children: [_jsx(SessionListHeader, { projectId: projectId, sessions: sessions, reviewCountBySession: needsYouBySession, onMenuOpenChange: holdPeek }), _jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: renderBody() }), _jsx(ShareSessionModal, { projectId: projectId, session: sessionToShare, open: !!sessionToShare, onOpenChange: (open) => !open && setSessionToShare(null), onSaved: () => queryClient.invalidateQueries({ queryKey: qk.project.sessionsScope(projectId) }) }), _jsx(SessionLabelsModal, { projectId: projectId, session: sessionToLabel, open: !!sessionToLabel, onOpenChange: (open) => !open && setSessionToLabel(null) }), _jsx(RenameSessionModal, { projectId: projectId, sessionId: sessionToRename?.id ?? null, currentName: sessionToRename?.name, open: !!sessionToRename, onOpenChange: (open) => !open && setSessionToRename(null) }), _jsx(SessionDeleteModal, { projectId: projectId, sessionId: sessionToDelete?.id ?? null, sessionLabel: sessionToDelete?.label, open: !!sessionToDelete, onOpenChange: (open) => !open && setSessionToDelete(null) })] }));
|
|
375
|
+
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col space-y-px", children: [_jsx(SessionListHeader, { projectId: projectId, sessions: sessions, reviewCountBySession: needsYouBySession, onMenuOpenChange: holdPeek }), _jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: renderBody() }), _jsx(ShareSessionModal, { projectId: projectId, session: sessionToShare, open: !!sessionToShare, onOpenChange: (open) => !open && setSessionToShare(null), onSaved: () => queryClient.invalidateQueries({ queryKey: qk.project.sessionsScope(projectId) }) }), _jsx(SessionLabelsModal, { projectId: projectId, session: sessionToLabel, open: !!sessionToLabel, onOpenChange: (open) => !open && setSessionToLabel(null) }), _jsx(RenameSessionModal, { projectId: projectId, sessionId: sessionToRename?.id ?? null, currentName: sessionToRename?.name, open: !!sessionToRename, onOpenChange: (open) => !open && setSessionToRename(null) }), _jsx(SessionDeleteModal, { projectId: projectId, sessionId: sessionToDelete?.id ?? null, sessionLabel: sessionToDelete?.label, open: !!sessionToDelete, onOpenChange: (open) => !open && setSessionToDelete(null) }), canShowSessionHoverCard && _jsx(SessionBriefCardHost, {})] }));
|
|
392
376
|
}
|
|
393
377
|
/** The `Sessions` row above the list. Lives here — not in `project-sidebar.tsx`
|
|
394
378
|
* — because everything it needs (the session list, the review summary, the
|
|
@@ -462,58 +446,73 @@ function SessionSectionMenu({ projectId, sessions, reviewCountBySession, }) {
|
|
|
462
446
|
}
|
|
463
447
|
}, children: _jsx(DotsThreeIcon, { className: "size-4" }) }) }), _jsx(SessionFilterMenu, { projectId: projectId, sessions: sessions, reviewCountBySession: reviewCountBySession, align: "start" })] }));
|
|
464
448
|
}
|
|
465
|
-
/**
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
* so hovering the row never
|
|
469
|
-
|
|
449
|
+
/** Tree icon + count of everything this session started: its sub-agents and
|
|
450
|
+
* the sessions it spawned. A `span`, not a button: it lives inside the row's
|
|
451
|
+
* link, so it stops the click before it navigates. Without an indicator strip
|
|
452
|
+
* beside it, it keeps the `⋯` slot free at rest, so hovering the row never
|
|
453
|
+
* moves it. */
|
|
454
|
+
function ChildRunsToggle({ count, open, onToggle, clearOfMenu, }) {
|
|
470
455
|
const t = useTranslations('sidebar.sessionList');
|
|
471
456
|
const toggle = (event) => {
|
|
472
457
|
event.preventDefault();
|
|
473
458
|
event.stopPropagation();
|
|
474
459
|
onToggle();
|
|
475
460
|
};
|
|
476
|
-
return (
|
|
461
|
+
return (_jsx("span", { role: "button", tabIndex: 0, "aria-expanded": open, "aria-label": open ? t('collapseChildren') : t('expandChildren', { count }), "data-session-children-toggle": "true", onClick: toggle, onKeyDown: (event) => {
|
|
477
462
|
if (event.key === 'Enter' || event.key === ' ')
|
|
478
463
|
toggle(event);
|
|
479
|
-
}, className: cn('
|
|
464
|
+
}, className: cn('flex shrink-0 items-center rounded-sm', open ? 'text-sidebar-foreground' : 'text-muted-foreground hover:text-sidebar-foreground', 'focus-visible:ring-kortix-base focus-visible:ring-[0.6px] focus-visible:outline-none',
|
|
480
465
|
// Reserve the `⋯` slot at rest. A toggle that slid out of the way on
|
|
481
466
|
// hover moved away from the pointer and put the menu under the click.
|
|
482
|
-
clearOfMenu && 'mr-6'), children:
|
|
467
|
+
clearOfMenu && 'mr-6'), children: _jsx(CountBadge, { count: count, icon: _jsx(TreeViewIcon, { "aria-hidden": true, className: "size-3.5" }), className: cn(open && 'bg-foreground/12') }) }));
|
|
483
468
|
}
|
|
484
469
|
/**
|
|
485
470
|
* The Shared and Automated sections: top-level sessions another member or an
|
|
486
471
|
* automation started. Closed until opened, and absent while the viewer has none.
|
|
487
472
|
*/
|
|
488
|
-
function StarterSection({ title, query, sessions, open, onToggle, renderNode, }) {
|
|
473
|
+
function StarterSection({ title, query, sessions, open, onToggle, renderNode, caretOnHover = false, }) {
|
|
489
474
|
const t = useTranslations('sidebar');
|
|
490
475
|
// A section with no row yet but a next page stays, with its Load more: the
|
|
491
476
|
// rows the viewer can see may sit past the first pages (KRTX-1727).
|
|
492
477
|
if (query.sessions.length === 0 && !query.hasNextPage)
|
|
493
478
|
return null;
|
|
494
|
-
return (_jsxs(Disclosure, { open: open, onOpenChange: onToggle, className: "group/section space-y-1", transition: { duration: 0.15, ease: 'easeOut' }, children: [_jsx(DisclosureTrigger, { children: _jsxs("div", { className: cn('group/section-header text-muted-foreground flex items-center gap-1 px-2 text-sm font-medium', SESSION_ROW_HEIGHT_CLASS), children: [_jsx("span", { className: "truncate", children: title }), _jsx(CaretRightIcon, { "aria-hidden": true, className:
|
|
479
|
+
return (_jsxs(Disclosure, { open: open, onOpenChange: onToggle, className: "group/section space-y-1", transition: { duration: 0.15, ease: 'easeOut' }, children: [_jsx(DisclosureTrigger, { children: _jsxs("div", { className: cn('group/section-header text-muted-foreground flex items-center gap-1 px-2 text-sm font-medium', SESSION_ROW_HEIGHT_CLASS), children: [_jsx("span", { className: "truncate", children: title }), _jsx(CaretRightIcon, { "aria-hidden": true, className: cn('duration-normal size-3 shrink-0 transition-[transform,opacity] ease-out group-data-[state=open]/section:rotate-90', caretOnHover &&
|
|
480
|
+
'opacity-0 group-hover/section-header:opacity-100 group-focus-visible/section-header:opacity-100') })] }) }), _jsxs(DisclosureContent, { contentClassName: "space-y-px", children: [sessions.map(renderNode), query.hasNextPage && (_jsx(ShowMoreButton, { loading: query.isFetchingNextPage, failed: query.isFetchNextPageError, label: t('loadMore'), onClick: () => query.fetchNextPage() }))] })] }));
|
|
495
481
|
}
|
|
496
482
|
function ShowMoreButton({ loading, failed, label, onClick, }) {
|
|
497
483
|
const t = useTranslations('sidebar');
|
|
498
484
|
return (_jsx(Button, { variant: "ghost", size: "sm", className: "text-muted-foreground hover:text-foreground h-8 w-full justify-start px-2 text-xs", disabled: loading, onClick: onClick, children: loading ? t('loadingMore') : failed ? t('retry') : label }));
|
|
499
485
|
}
|
|
500
486
|
/**
|
|
501
|
-
*
|
|
502
|
-
*
|
|
487
|
+
* Everything one session started, on ONE trunk: its sub-agents first, then the
|
|
488
|
+
* sessions it spawned (fetched when the row opens, 20 at a time). `ml-4` puts
|
|
489
|
+
* the trunk's x=0 under the parent's icon center (px-2 + half of size-4).
|
|
503
490
|
*/
|
|
491
|
+
function ChildRuns({ projectId, parentId, subagents, spawnedCount, renderSubagent, renderSpawned, }) {
|
|
492
|
+
return (_jsxs("div", { className: "ml-4", "data-session-children": parentId, children: [subagents.map((child, index) => (_jsx(ChildRunBranch, { continues: index < subagents.length - 1 || spawnedCount > 0, children: renderSubagent(child) }, child.id))), spawnedCount > 0 && (_jsx(SpawnedSessions, { projectId: projectId, parentId: parentId, renderNode: renderSpawned }))] }));
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* One child on the trunk. A child with siblings below it carries the trunk
|
|
496
|
+
* through its whole height; the last one ends at its own elbow. Each segment
|
|
497
|
+
* overlaps the next by 1px: edge-to-edge segments showed hairline breaks from
|
|
498
|
+
* sub-pixel rounding at row boundaries.
|
|
499
|
+
*/
|
|
500
|
+
function ChildRunBranch({ continues, children }) {
|
|
501
|
+
return (_jsxs("div", { className: "relative", children: [continues && (_jsx("span", { "aria-hidden": true, className: "border-border pointer-events-none absolute top-0 -bottom-px left-0 border-l-2" })), _jsx(SubAgentConnector, {}), _jsx("div", { style: { marginLeft: SUB_AGENT_CONNECTOR_RUN }, children: children })] }));
|
|
502
|
+
}
|
|
504
503
|
function SpawnedSessions({ projectId, parentId, renderNode, }) {
|
|
505
504
|
const t = useTranslations('sidebar');
|
|
506
505
|
const children = useSessionChildren(projectId, parentId, { limit: SIDEBAR_PAGE_SIZE });
|
|
507
506
|
if (children.data === undefined) {
|
|
508
|
-
return (_jsx("div", { className: "
|
|
507
|
+
return (_jsx("div", { className: "px-2", children: _jsx(Skeleton, { className: "h-3 w-24 py-0", "aria-hidden": true }) }));
|
|
509
508
|
}
|
|
510
509
|
if (children.isError && children.sessions.length === 0) {
|
|
511
|
-
return _jsx("p", { className: "text-destructive/80
|
|
510
|
+
return _jsx("p", { className: "text-destructive/80 px-2 text-xs", children: t('sessionList.loadError') });
|
|
512
511
|
}
|
|
513
512
|
const rows = children.sessions;
|
|
514
|
-
return (_jsxs(
|
|
513
|
+
return (_jsxs(_Fragment, { children: [rows.map((child, index) => (_jsx(ChildRunBranch, { continues: index < rows.length - 1 || children.hasNextPage, children: renderNode(child) }, child.session_id))), children.hasNextPage && (_jsx(ChildRunBranch, { continues: false, children: _jsx(ShowMoreButton, { loading: children.isFetchingNextPage, failed: children.isFetchNextPageError, label: t('sessionList.showMore'), onClick: () => children.fetchNextPage() }) }))] }));
|
|
515
514
|
}
|
|
516
|
-
function ProjectSessionRow({ session, href, isActive, isSwitching, onNavigate, displayTitle, onDelete, onPin, onArchive, onShare, onRename, onEditLabels, onRestart, isRestarting, onStop, isStopping, canFork = false, isForking = false, onFork,
|
|
515
|
+
function ProjectSessionRow({ session, href, isActive, isSwitching, onNavigate, displayTitle, onDelete, onPin, onArchive, onShare, onRename, onEditLabels, onRestart, isRestarting, onStop, isStopping, canFork = false, isForking = false, onFork, spawned = false, subagentCount = 0, spawnedCount = 0, childRunsOpen = false, onToggleChildRuns, reviewCount = 0, awaitingAnswer = false, changeRequests, canShowHoverCard, inSharedGroup = false, }) {
|
|
517
516
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
518
517
|
const t = useTranslations('sidebar');
|
|
519
518
|
const tPinArchive = useTranslations('sidebar.pinArchive');
|
|
@@ -549,7 +548,8 @@ function ProjectSessionRow({ session, href, isActive, isSwitching, onNavigate, d
|
|
|
549
548
|
// listing the very change requests that made it `needs-you`. Both now read the
|
|
550
549
|
// same `reviewCount` prop, so they agree by construction rather than by luck.
|
|
551
550
|
const displayStatus = sessionDisplayStatus(session, reviewCount);
|
|
552
|
-
const
|
|
551
|
+
const waitingOn = awaitingAnswer ? 'answer' : 'review';
|
|
552
|
+
const sessionLink = (_jsxs(HoverPrefetchLink, { href: href, prefetch: true, onClick: onNavigate, ...openIntent, "aria-busy": isSwitching || undefined, "aria-current": isActive ? 'page' : undefined, "aria-describedby": descriptionId, className: "focus-visible:ring-kortix-base flex min-w-0 flex-1 items-center gap-2 self-stretch rounded-md py-1 focus-visible:ring-[0.6px] focus-visible:outline-none", children: [_jsx("div", { className: "size-4 shrink-0", children: spawned ? (_jsx(ArrowsSplitIcon, { "aria-hidden": true, className: "text-muted-foreground size-4" })) : (_jsx(SessionStatusDot, { session: session, reviewCount: reviewCount, waitingOn: waitingOn, changeRequests: changeRequests })) }), isMeta && (_jsx(Hint, { side: "top", label: t('metaCoordinator'), children: _jsx("span", { className: "text-muted-foreground flex size-4 shrink-0 items-center justify-center", children: _jsx(SparkleIcon, { className: "size-3.5", weight: "fill" }) }) })), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx(SessionTitle, { title: displayTitle, className: cn('min-w-0', isActive && 'font-medium') }), _jsx("span", { className: "hidden max-md:block pointer-coarse:block [@media(hover:none)]:block", "aria-hidden": true, "data-session-mobile-created-at": "true", children: _jsx(MobileSessionCreatedTime, { createdAt: session.created_at }) })] }), subagentCount + spawnedCount > 0 && onToggleChildRuns && (_jsx(ChildRunsToggle, { count: subagentCount + spawnedCount, open: childRunsOpen, onToggle: onToggleChildRuns, clearOfMenu: !hasIndicators })), hasIndicators && (_jsxs("div", { className: cn('flex shrink-0 items-center gap-0 transition-none', 'max-md:hidden pointer-coarse:hidden [@media(hover:none)]:hidden', 'group-hover/session-list:pointer-events-none group-hover/session-list:opacity-0', 'group-has-data-[state=open]/session-list:pointer-events-none group-has-data-[state=open]/session-list:opacity-0'), "data-session-indicators": "true", children: [showStarter && (_jsx("span", { className: "flex size-5 shrink-0 items-center justify-center", "data-session-source": "true", "data-session-starter": starter.type, children: _jsx(Hint, { side: "top", label: t('startedByLabel', { name: starter.label }), children: _jsx("span", { className: "text-muted-foreground flex size-5 items-center justify-center", children: _jsx(SessionStarterMark, { session: session, starter: starter, iconClassName: "size-3", avatarClassName: "size-5" }) }) }) })), !inSharedGroup && _jsx(SessionSharedIcon, { session: session })] }))] }));
|
|
553
553
|
return (_jsx("div", { className: "group/session-list block", children: _jsxs("div", { className: cn(
|
|
554
554
|
// --session-row-surface paints the row AND the title fade in the same
|
|
555
555
|
// style pass. Do not transition background — transition-colors made the
|
|
@@ -558,7 +558,7 @@ function ProjectSessionRow({ session, href, isActive, isSwitching, onNavigate, d
|
|
|
558
558
|
// the end fade can never disagree with the row fill.
|
|
559
559
|
'relative flex h-8 cursor-pointer items-center gap-2 rounded-md px-2 transition-none', 'max-md:h-auto max-md:min-h-12 max-md:gap-1', '[@media(hover:none)]:h-auto [@media(hover:none)]:min-h-12 [@media(hover:none)]:gap-1', 'pointer-coarse:h-auto pointer-coarse:min-h-12 pointer-coarse:gap-1', isActive
|
|
560
560
|
? 'text-sidebar-foreground bg-(--session-row-surface) font-medium [--session-row-surface:var(--sidebar-row)]'
|
|
561
|
-
: 'text-muted-foreground hover:text-sidebar-foreground bg-(--session-row-surface) [--session-row-surface:var(--background)] hover:[--session-row-surface:var(--sidebar-row)]'), children: [canShowHoverCard ? (_jsx(SessionBriefHoverCard, { sessionId: session.session_id, title: displayTitle, status: displayStatus, createdAt: session.created_at, source: source, changeRequests: changeRequests, labels: session.labels, projectId: session.project_id, children: sessionLink })) : (sessionLink), _jsx(SessionBriefDescription, { id: descriptionId, status: displayStatus, createdAt: session.created_at, source: source, changeRequests: changeRequests }), _jsxs(DropdownMenu, { open: menuOpen, onOpenChange: setMenuOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", type: "button", "aria-label": t('sessionList.actionsFor', { title: displayTitle }), className: cn(SESSION_MENU_TRIGGER_CLASS, 'absolute top-1/2 right-1 z-10 -translate-y-1/2', 'bg-(--session-row-surface)', 'pointer-events-none opacity-0', 'group-hover/session-list:pointer-events-auto group-hover/session-list:opacity-100', 'focus-visible:pointer-events-auto focus-visible:opacity-100', 'data-[state=open]:pointer-events-auto data-[state=open]:opacity-100', SESSION_MENU_TRIGGER_TOUCH_CLASS), onClick: (e) => {
|
|
561
|
+
: 'text-muted-foreground hover:text-sidebar-foreground bg-(--session-row-surface) [--session-row-surface:var(--background)] hover:[--session-row-surface:var(--sidebar-row)]'), children: [canShowHoverCard ? (_jsx(SessionBriefHoverCard, { sessionId: session.session_id, title: displayTitle, status: displayStatus, createdAt: session.created_at, source: source, changeRequests: changeRequests, waitingOn: waitingOn, spawned: spawned, subagentCount: subagentCount, spawnedCount: spawnedCount, labels: session.labels, projectId: session.project_id, children: sessionLink })) : (sessionLink), _jsx(SessionBriefDescription, { id: descriptionId, status: displayStatus, createdAt: session.created_at, source: source, changeRequests: changeRequests, waitingOn: waitingOn }), _jsxs(DropdownMenu, { open: menuOpen, onOpenChange: setMenuOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", type: "button", "aria-label": t('sessionList.actionsFor', { title: displayTitle }), className: cn(SESSION_MENU_TRIGGER_CLASS, 'absolute top-1/2 right-1 z-10 -translate-y-1/2', 'bg-(--session-row-surface)', 'pointer-events-none opacity-0', 'group-hover/session-list:pointer-events-auto group-hover/session-list:opacity-100', 'focus-visible:pointer-events-auto focus-visible:opacity-100', 'data-[state=open]:pointer-events-auto data-[state=open]:opacity-100', SESSION_MENU_TRIGGER_TOUCH_CLASS), onClick: (e) => {
|
|
562
562
|
e.preventDefault();
|
|
563
563
|
e.stopPropagation();
|
|
564
564
|
}, children: _jsx(DotsThreeIcon, { className: "size-4" }) }) }), _jsxs(DropdownMenuContent, { align: "start", side: "right", className: "w-44", children: [_jsxs(DropdownMenuItem, { className: "cursor-pointer", onSelect: () => deferAfterClose(() => onRename(session.session_id, displayTitle)), children: [_jsx(PencilSimpleIcon, {}), tI18nComplete.raw('text3064d79a295c')] }), _jsxs(DropdownMenuItem, { className: "cursor-pointer", onSelect: () => deferAfterClose(() => onEditLabels(session)), children: [_jsx(TagIcon, {}), t('labels.menu')] }), _jsxs(DropdownMenuItem, { className: "cursor-pointer", onSelect: () => deferAfterClose(() => onShare(session)), children: [_jsx(Share, {}), session.can_manage_sharing !== false
|
|
@@ -596,16 +596,13 @@ const SUB_AGENT_CONNECTOR_RUN = 'calc(var(--spacing) * 3.5)';
|
|
|
596
596
|
function SubAgentConnector() {
|
|
597
597
|
return (_jsx("span", { "aria-hidden": true, className: "border-border pointer-events-none absolute top-0 left-0 h-4 rounded-bl-md border-b-2 border-l-2", style: { width: SUB_AGENT_CONNECTOR_RUN } }));
|
|
598
598
|
}
|
|
599
|
-
function ProjectSubsessionRow({ title, href, isActive, updatedAt, }) {
|
|
599
|
+
function ProjectSubsessionRow({ id, title, href, isActive, createdAt, updatedAt, }) {
|
|
600
600
|
const relative = updatedAt
|
|
601
601
|
? shortRelative(formatDistanceToNowStrict(new Date(updatedAt), { addSuffix: false }))
|
|
602
602
|
: '';
|
|
603
|
-
return (
|
|
604
|
-
// Same reason as ProjectSessionRow: sub-session rows are session routes too,
|
|
605
|
-
// so prefetching them on mount multiplies the per-open RSC storm.
|
|
606
|
-
_jsx(HoverPrefetchLink, { href: href, className: "block", children: _jsxs("div", { className: cn('flex h-8 cursor-pointer items-center gap-2 rounded-lg px-2 text-sm', isActive ? 'bg-sidebar-accent text-sidebar-foreground font-medium' : ''), children: [_jsx("span", { title: title, className: cn('flex-1 truncate', isActive && 'font-medium'), children: title }), relative && _jsx("span", { className: SESSION_RELATIVE_TIME_CLASS, children: relative })] }) }));
|
|
603
|
+
return (_jsx(SubagentHoverCard, { id: id, title: title, createdAt: createdAt, updatedAt: updatedAt, children: _jsx(HoverPrefetchLink, { href: href, className: "block", children: _jsxs("div", { className: cn('flex h-8 cursor-pointer items-center gap-2 rounded-lg px-2 text-sm', isActive ? 'bg-sidebar-accent text-sidebar-foreground font-medium' : ''), children: [_jsx(RobotIcon, { "aria-hidden": true, className: "text-muted-foreground size-4 shrink-0" }), _jsx("span", { className: cn('flex-1 truncate', isActive && 'font-medium'), children: title }), relative && _jsx("span", { className: SESSION_RELATIVE_TIME_CLASS, children: relative })] }) }) }));
|
|
607
604
|
}
|
|
608
|
-
function SessionStatusDot({ session, reviewCount = 0, }) {
|
|
605
|
+
function SessionStatusDot({ session, reviewCount = 0, waitingOn, changeRequests, }) {
|
|
609
606
|
const display = sessionDisplayStatus(session, reviewCount);
|
|
610
|
-
return (_jsx("div", { className: "flex size-4 shrink-0 items-center justify-center", children: _jsx(SessionStatusMark, { status: display }) }));
|
|
607
|
+
return (_jsx("div", { className: "flex size-4 shrink-0 items-center justify-center", children: _jsx(SessionStatusMark, { status: display, waitingOn: waitingOn, overdue: isReviewOverdue(changeRequests) }) }));
|
|
611
608
|
}
|