@kortix/ui 0.14.5 → 0.14.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_vendor/kortix-shared.js +489 -381
- 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-authority-card.js +1 -2
- package/dist/customize/agents/agent-detail-aside.js +15 -14
- package/dist/customize/agents/agent-drives-section.js +5 -6
- package/dist/customize/agents/agent-grant-pages.js +73 -76
- package/dist/customize/agents/agent-nav.prototype.d.ts +35 -0
- package/dist/customize/agents/agent-nav.prototype.js +95 -0
- package/dist/customize/agents/agent-page.js +59 -23
- package/dist/customize/agents/agent-people-section.js +18 -17
- package/dist/customize/agents/agent-triggers-section.js +6 -5
- 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/catalog/catalog-entry.d.ts +14 -0
- package/dist/customize/connectors/catalog/catalog-entry.js +18 -1
- package/dist/customize/connectors/catalog/connector-browse.d.ts +28 -0
- package/dist/customize/connectors/catalog/connector-browse.js +18 -5
- 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/app-page.js +19 -5
- package/dist/customize/connectors/detail/connector-advanced-tools.js +4 -6
- package/dist/customize/connectors/detail/connector-modal.d.ts +10 -13
- package/dist/customize/connectors/detail/connector-modal.js +18 -101
- package/dist/customize/connectors/detail/connector-notice.js +6 -2
- package/dist/customize/connectors/detail/connector-page.d.ts +30 -0
- package/dist/customize/connectors/detail/connector-page.js +26 -13
- 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/custom-provider-form.js +24 -7
- package/dist/customize/sections/llm-provider/custom-provider-panel.js +44 -8
- package/dist/customize/sections/llm-provider/types.d.ts +3 -0
- package/dist/customize/sections/llm-provider/utils.js +2 -2
- package/dist/customize/sections/view/agent-editor-access-fields.d.ts +9 -16
- package/dist/customize/sections/view/agent-editor-access-fields.js +30 -30
- package/dist/customize/sections/view/agent-editor-basics-fields.js +3 -10
- package/dist/customize/sections/view/agent-editor-primitives.d.ts +28 -10
- package/dist/customize/sections/view/agent-editor-primitives.js +32 -19
- package/dist/customize/sections/view/agent-editor.js +3 -3
- package/dist/customize/sections/view/gateway/gateway-routing.js +18 -21
- package/dist/customize/sections/view/grant-mode-field.d.ts +2 -5
- package/dist/customize/sections/view/grant-mode-field.js +10 -18
- package/dist/customize/sections/view/permission-editor.js +11 -8
- package/dist/customize/sections/view/secrets-view.js +2 -1
- package/dist/customize/settings/rail-copy-context.js +5 -3
- package/dist/customize/settings/tabs/general-tab.d.ts +9 -2
- package/dist/customize/settings/tabs/general-tab.js +33 -11
- package/dist/customize/settings/tabs/sandbox-template-form.js +9 -12
- package/dist/customize/settings/tabs/transfer-workspace-dialog.d.ts +4 -0
- package/dist/customize/settings/tabs/transfer-workspace-dialog.js +82 -0
- package/dist/customize/shared/capability-tabs.js +2 -1
- package/dist/customize/skills/skills-page.js +3 -5
- 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/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/api/runtime-files.js +3 -2
- 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/file-source.js +18 -1
- package/dist/files/project/hooks/use-file-content.js +2 -0
- package/dist/files/project/hooks/use-server-health.js +17 -15
- package/dist/files/renderers/docx/docx-viewer.js +0 -5
- package/dist/files/renderers/image-renderer.js +6 -24
- package/dist/files/renderers/pdf/pdf-viewer.js +1 -1
- 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-header.js +1 -1
- package/dist/files/viewer/file-preview-modal.d.ts +1 -1
- package/dist/files/viewer/file-preview-modal.js +13 -3
- package/dist/files/viewer/file-source.d.ts +3 -1
- package/dist/files/viewer/use-file-content-state.js +27 -8
- package/dist/files/viewer/viewer-actions.js +2 -2
- package/dist/i18n/en.d.ts +355 -12
- package/dist/i18n/en.js +360 -17
- package/dist/internal/desktop/desktop-back-button.js +5 -3
- package/dist/internal/files/types.d.ts +2 -0
- package/dist/internal/goals/goal-meta.d.ts +30 -0
- package/dist/internal/goals/goal-meta.js +55 -0
- package/dist/internal/hooks/use-auto-scroll.js +5 -3
- package/dist/internal/lib/query-client-singleton.d.ts +9 -3
- package/dist/internal/lib/query-client-singleton.js +18 -12
- package/dist/internal/notifications/notification-rows.js +2 -1
- package/dist/internal/outcomes/outcome-types.d.ts +5 -0
- package/dist/internal/share/session-shares-modal.js +3 -7
- package/dist/internal/stores/kortix-computer-store.js +14 -2
- package/dist/internal/stores/session-hover-store.d.ts +32 -0
- package/dist/internal/stores/session-hover-store.js +38 -0
- package/dist/internal/stores/sidebar-nav-store.d.ts +1 -1
- package/dist/internal/stores/sidebar-nav-store.js +3 -1
- package/dist/lib/routes.d.ts +2 -0
- package/dist/lib/routes.js +1 -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/map.js +2 -1
- package/dist/primitives/preview-image.js +4 -2
- package/dist/primitives/settings-section-header.d.ts +1 -1
- 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/session/composer/agent-selector.js +6 -1
- package/dist/session/composer/attachment-tile.d.ts +0 -2
- package/dist/session/composer/attachment-tile.js +0 -8
- package/dist/session/composer/clipboard-files.d.ts +2 -1
- package/dist/session/composer/composer-chat-input.js +5 -3
- package/dist/session/composer/composer-draft-recovery.d.ts +1 -2
- package/dist/session/composer/composer-draft-recovery.js +0 -13
- package/dist/session/composer/composer-logic.js +8 -7
- package/dist/session/composer/context-ring.d.ts +1 -1
- package/dist/session/composer/context-ring.js +2 -2
- package/dist/session/composer/context-usage.d.ts +3 -6
- package/dist/session/composer/context-usage.js +3 -6
- package/dist/session/composer/draft/composer-draft.js +4 -4
- package/dist/session/composer/inbox-row-claims.d.ts +2 -2
- package/dist/session/composer/inbox-row-claims.js +1 -1
- package/dist/session/composer/pasted-text.js +3 -2
- package/dist/session/composer/queue-projection.d.ts +3 -2
- package/dist/session/composer/queued-prompt-edit.d.ts +1 -1
- package/dist/session/composer/reasoning-effort-selector.d.ts +2 -1
- package/dist/session/composer/session-composer-readiness.d.ts +7 -22
- package/dist/session/composer/session-composer-readiness.js +12 -40
- package/dist/session/composer/stores/queued-draft-store.js +3 -2
- package/dist/session/composer/token-progress.d.ts +1 -1
- package/dist/session/composer/token-progress.js +2 -2
- package/dist/session/header/reminder-parts.d.ts +11 -2
- package/dist/session/header/reminder-parts.js +9 -4
- package/dist/session/header/session-goal-indicator.d.ts +1 -0
- package/dist/session/header/session-goal-indicator.js +41 -0
- package/dist/session/header/session-site-header.js +2 -1
- package/dist/session/media/sandbox-url-detector.js +0 -7
- package/dist/session/outcomes/change-request-outcomes.js +24 -3
- package/dist/session/overrides/provider-secret-pool-editor.js +2 -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/session-action-panel-column.js +11 -1
- package/dist/session/panel/session-audit-shared.d.ts +0 -4
- package/dist/session/panel/session-audit-shared.js +1 -1
- package/dist/session/panel/session-changes-shared.d.ts +2 -1
- package/dist/session/panel/session-panel-provider.js +6 -2
- package/dist/session/panel/shared/use-panel-derivations.js +6 -4
- package/dist/session/panel/use-session-panel-layout.js +7 -5
- package/dist/session/print/session-print-header.js +9 -8
- package/dist/session/prompts/session-permission-prompt.js +6 -3
- package/dist/session/status/provisioning-failure.d.ts +2 -1
- package/dist/session/status/session-error-banner.js +8 -6
- package/dist/session/status/session-starting-loader.d.ts +24 -31
- package/dist/session/status/session-starting-loader.js +32 -81
- package/dist/session/thread/chat-minimap-items.d.ts +4 -4
- package/dist/session/thread/first-prompt-handover.d.ts +3 -2
- package/dist/session/thread/message-parsing.d.ts +3 -3
- package/dist/session/thread/session-chat.js +15 -19
- package/dist/session/thread/session-history-scroll.d.ts +2 -1
- package/dist/session/thread/session-history.js +3 -1
- package/dist/session/thread/session-thread.js +1 -1
- 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/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/pty-connection.d.ts +5 -4
- package/dist/terminal/session-terminal-panel.js +3 -5
- package/dist/theme/theme.js +2 -1
- package/dist/workspace/index.d.ts +1 -0
- package/dist/workspace/index.js +1 -0
- package/dist/workspace/projects/modal/github-import-pickers.js +7 -6
- package/dist/workspace/review/review-center.js +13 -10
- package/dist/workspace/saved-session-skeleton-shape.d.ts +4 -3
- package/dist/workspace/saved-session-skeleton.d.ts +5 -1
- package/dist/workspace/saved-session-skeleton.js +5 -4
- package/dist/workspace/session-workspace.js +9 -14
- 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-goals-nav.d.ts +6 -0
- package/dist/workspace/sidebar/footer/project-goals-nav.js +26 -0
- 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/modal/session-embed-section.d.ts +25 -0
- package/dist/workspace/sidebar/modal/session-embed-section.js +71 -0
- package/dist/workspace/sidebar/modal/share-session-modal.js +2 -2
- package/dist/workspace/sidebar/nav-row-meta.js +14 -2
- package/dist/workspace/sidebar/project-nav-items.js +3 -0
- package/dist/workspace/sidebar/project-session-list.js +12 -3
- package/dist/workspace/sidebar/session-brief-hover-card.js +5 -8
- package/package.json +4 -4
- package/skills/kortix-ui/references/modules.md +2 -0
- package/dist/session/status/use-readiness-settling.d.ts +0 -18
- package/dist/session/status/use-readiness-settling.js +0 -43
- package/dist/workspace/sidebar/modal/session-public-link-section.d.ts +0 -13
- package/dist/workspace/sidebar/modal/session-public-link-section.js +0 -66
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Header chip for a session that works on a goal (flag `goals`). Renders
|
|
5
|
+
* nothing unless the session is a goal's main session, worker or verifier.
|
|
6
|
+
* The popover says which, and shows the goal's status, health, progress, the
|
|
7
|
+
* heartbeat's last and next wake, and what the goal waits on a person for,
|
|
8
|
+
* with a link to the goal page. The dot on the chip turns orange when the goal
|
|
9
|
+
* needs attention, so a broken goal is visible from its own session.
|
|
10
|
+
*/
|
|
11
|
+
import { HoverPrefetchLink } from '../../internal/components/hover-prefetch-link.js';
|
|
12
|
+
import { useNow } from '../../internal/reminders/use-refetch-after-fire.js';
|
|
13
|
+
import { Button } from '../../primitives/button.js';
|
|
14
|
+
import { Popover, PopoverContent, PopoverTrigger } from '../../primitives/popover.js';
|
|
15
|
+
import { StatusDot } from '../../primitives/status.js';
|
|
16
|
+
import { useLocale, useTranslations } from '../../i18n/index.js';
|
|
17
|
+
import { useFeatureFlag, useSessionGoal } from '@kortix/sdk/react';
|
|
18
|
+
import { TargetIcon } from '@phosphor-icons/react';
|
|
19
|
+
import { useState } from 'react';
|
|
20
|
+
import { useSessionIds } from '../../providers/session-ids.js';
|
|
21
|
+
import { formatGoalTime, GoalHealthDot, goalHref, goalOpenAsks, goalProgress, GoalStatusBadge, useGoalIssueText, } from '../../internal/goals/goal-meta.js';
|
|
22
|
+
export function SessionGoalIndicator() {
|
|
23
|
+
const t = useTranslations('goals');
|
|
24
|
+
const locale = useLocale();
|
|
25
|
+
const now = useNow();
|
|
26
|
+
const { projectId, sessionId } = useSessionIds();
|
|
27
|
+
const gate = useFeatureFlag(projectId, 'goals');
|
|
28
|
+
const { goal, role } = useSessionGoal(gate.enabled ? projectId : null, sessionId);
|
|
29
|
+
const issueText = useGoalIssueText();
|
|
30
|
+
const [open, setOpen] = useState(false);
|
|
31
|
+
if (!goal || !role || !projectId)
|
|
32
|
+
return null;
|
|
33
|
+
const doc = goal.goal;
|
|
34
|
+
const progress = goalProgress(doc);
|
|
35
|
+
const asks = goalOpenAsks(doc);
|
|
36
|
+
const href = goalHref(projectId, goal);
|
|
37
|
+
const attention = goal.health.state === 'attention';
|
|
38
|
+
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "icon", "aria-label": t('sessionChip'), className: "relative", "data-testid": "session-goal-chip", children: [_jsx(TargetIcon, { className: "size-4" }), attention ? _jsx(StatusDot, { tone: "warning", className: "absolute top-1 right-1" }) : null] }) }), _jsxs(PopoverContent, { align: "end", sideOffset: 8, className: "w-80 overflow-hidden p-0", children: [_jsxs("div", { className: "flex flex-col gap-2 border-b px-4 pt-4 pb-3", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("h3", { className: "text-foreground text-sm font-medium", children: t('sessionChip') }), _jsx(GoalStatusBadge, { status: doc?.status })] }), _jsx("p", { className: "text-foreground line-clamp-2 text-sm", children: doc?.objective ?? goal.slug }), _jsx("p", { className: "text-muted-foreground text-xs", children: t(`sessionRole.${role}`) })] }), _jsxs("div", { className: "flex flex-col gap-2 px-4 py-3 text-xs", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-1", children: _jsx(GoalHealthDot, { goal: goal }) }), _jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [_jsx("span", { className: "text-foreground", children: t(`health.${goal.health.state}`) }), goal.health.issues.map((issue) => (_jsx("span", { className: "text-muted-foreground", children: issueText(issue, goal) }, issue)))] })] }), _jsxs("dl", { className: "text-muted-foreground grid grid-cols-[auto_1fr] gap-x-3 gap-y-1", children: [_jsx("dt", { children: t('progress') }), _jsx("dd", { className: "text-foreground tabular-nums", children: t('criteria', { met: progress.met, total: progress.total }) }), goal.health.state !== 'closed' ? (_jsxs(_Fragment, { children: [_jsx("dt", { children: t('heartbeat') }), _jsx("dd", { className: "text-foreground", children: goal.heartbeat?.state === 'active' && goal.heartbeat.next_fire_at
|
|
39
|
+
? t('nextWake', { time: formatGoalTime(goal.heartbeat.next_fire_at, locale, now) })
|
|
40
|
+
: t('noWake') })] })) : null, goal.heartbeat?.last_fired_at ? (_jsxs(_Fragment, { children: [_jsx("dt", { children: t('lastWakeLabel') }), _jsx("dd", { className: "text-foreground", children: formatGoalTime(goal.heartbeat.last_fired_at, locale, now) })] })) : null] }), asks.length > 0 ? (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("span", { className: "text-muted-foreground", children: t('needsYou') }), _jsx("ul", { className: "flex flex-col gap-1", children: asks.map((ask) => (_jsxs("li", { className: "text-foreground flex justify-between gap-2", children: [_jsx("span", { className: "min-w-0 truncate", children: ask.title }), ask.due ? _jsx("span", { className: "text-muted-foreground shrink-0", children: formatGoalTime(ask.due, locale, now) }) : null] }, ask.id))) })] })) : null] }), href ? (_jsx("div", { className: "border-t px-4 py-2", children: _jsx(Button, { asChild: true, variant: "ghost", size: "sm", className: "w-full justify-center", children: _jsx(HoverPrefetchLink, { href: href, onClick: () => setOpen(false), children: t('openGoal') }) }) })) : null] })] }));
|
|
41
|
+
}
|
|
@@ -22,6 +22,7 @@ import { SessionParticipantStack } from '../participants/session-participants.js
|
|
|
22
22
|
import { PreviousRepositoryNotice } from '../status/previous-repository-session.js';
|
|
23
23
|
import { SessionConfigIndicator, SessionConfigReloadConfirm, } from './session-config-indicator.js';
|
|
24
24
|
import { SessionPendingApprovalsIndicator } from './session-pending-approvals-indicator.js';
|
|
25
|
+
import { SessionGoalIndicator } from './session-goal-indicator.js';
|
|
25
26
|
import { SessionRemindersIndicator } from './session-reminders-indicator.js';
|
|
26
27
|
import { SessionTitleInput } from './session-title-input.js';
|
|
27
28
|
import { SubagentHoverCard, subagentTitle } from './subagent-hover-card.js';
|
|
@@ -281,7 +282,7 @@ export function SessionSiteHeader({ sessionId, sessionTitle, isMobileView, leadi
|
|
|
281
282
|
return;
|
|
282
283
|
renameFromMenu.current = false;
|
|
283
284
|
e.preventDefault();
|
|
284
|
-
}, children: sessionActionItems })] })] }), leadingAction] }), _jsxs("div", { className: cn('pointer-events-auto flex items-center gap-1.5', sidebarHidden && 'h-[var(--kx-titlebar-control-size)]'), children: [_jsx(SessionChangesIndicator, { sessionId: sessionId }), _jsx(SessionPendingApprovalsIndicator, { sessionId: sessionId }), isProjectSession ? _jsx(SessionRemindersIndicator, { runtimeSessionId: sessionId }) : null, isProjectSession && (_jsx(SessionConfigIndicator, { projectId: projectId, sessionId: projectSessionId, chatSessionId: sessionId, baseRef: projectSession?.base_ref, reload: reloadConfig.reload, isPending: reloadConfig.isPending, phase: reloadConfig.phase, canReload: canManageLifecycle })), _jsx("div", { className: "hidden items-center gap-1.5 lg:flex", children: devTools.map(({ view, label, Icon }) => (_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: label, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": label, onClick: () => openSessionQuickView(view, 'header'), className: "text-foreground/80 hover:text-foreground cursor-pointer transition-colors active:scale-[0.96]", children: _jsx(Icon, { className: "h-4 w-4" }) }) }, view))) }), _jsxs(DropdownMenu, { children: [_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: tI18nHardcoded.raw('i18nComplete.text96f0c06bbcb7'), children: _jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": tI18nHardcoded.raw('i18nComplete.text96f0c06bbcb7'), className: "text-foreground/80 hover:text-foreground cursor-pointer transition-colors active:scale-[0.96] lg:hidden", children: _jsx(Code2, { className: "h-4 w-4" }) }) }) }), _jsx(DropdownMenuContent, { align: "end", className: "w-44", children: devTools.map(({ view, label, Icon }) => (_jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => openSessionQuickView(view, 'header'), children: [_jsx(Icon, {}), label] }, view))) })] }), isProjectSession && projectSession && (_jsx(SessionParticipantStack, { participants: sessionParticipants })), isProjectSession && projectSession && ShareSessionModal && (_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: shareLabel, open: isMobileViewport ? undefined : false, children: _jsxs(Button, { variant: "ghost", size: "xs", "aria-label": shareLabel, onClick: () => setShareOpen(true), className: "cursor-pointer rounded-md active:scale-[0.96] max-md:w-7 max-md:has-[>svg]:px-0", children: [_jsx(Share, {}), _jsx("span", { className: "hidden md:inline", children: tI18nHardcoded.raw('i18nComplete.text29887a5ff984') })] }) })), isMobileViewport && (_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: tI18nHardcoded.raw('i18nComplete.text2534086de9fe'), children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": tI18nHardcoded.raw('i18nComplete.text2534086de9fe'), "aria-expanded": isActionPanelOpen, onClick: toggleActionPanel, className: "text-foreground/80 hover:text-foreground cursor-pointer transition-colors active:scale-[0.96]", children: _jsxs("span", { className: "relative inline-flex", children: [_jsx(CaretDoubleLeftIcon, { className: "size-4" }), readyChip?.sessionId === sessionId && !isActionPanelOpen && (_jsx("span", { className: "bg-kortix-green ring-background absolute -top-1 -right-1 size-2 rounded-full ring-2", "aria-hidden": true }))] }) }) }))] })] }), isProjectSession && _jsx(PreviousRepositoryNotice, {})] }), _jsx(ExportTranscriptModal, { sessionId: sessionId, kortixSessionScope: isProjectSession ? `${projectId}/${projectSessionId}` : undefined, open: exportOpen, onOpenChange: setExportOpen }), _jsx(CompactModal, { sessionId: sessionId, open: compactOpen, onOpenChange: setCompactOpen }), isProjectSession && (_jsxs(_Fragment, { children: [_jsx(SessionConfigReloadConfirm, { busyReason: reloadConfig.busyReason, isPending: reloadConfig.isPending, onConfirm: () => reloadConfig.reload({ force: true }), onDismiss: reloadConfig.clearBusy }), ShareSessionModal && (_jsx(ShareSessionModal, { projectId: projectId, session: projectSession, open: shareOpen, onOpenChange: setShareOpen, onSaved: () => queryClient.invalidateQueries({
|
|
285
|
+
}, children: sessionActionItems })] })] }), leadingAction] }), _jsxs("div", { className: cn('pointer-events-auto flex items-center gap-1.5', sidebarHidden && 'h-[var(--kx-titlebar-control-size)]'), children: [_jsx(SessionChangesIndicator, { sessionId: sessionId }), _jsx(SessionPendingApprovalsIndicator, { sessionId: sessionId }), isProjectSession ? _jsx(SessionGoalIndicator, {}) : null, isProjectSession ? _jsx(SessionRemindersIndicator, { runtimeSessionId: sessionId }) : null, isProjectSession && (_jsx(SessionConfigIndicator, { projectId: projectId, sessionId: projectSessionId, chatSessionId: sessionId, baseRef: projectSession?.base_ref, reload: reloadConfig.reload, isPending: reloadConfig.isPending, phase: reloadConfig.phase, canReload: canManageLifecycle })), _jsx("div", { className: "hidden items-center gap-1.5 lg:flex", children: devTools.map(({ view, label, Icon }) => (_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: label, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": label, onClick: () => openSessionQuickView(view, 'header'), className: "text-foreground/80 hover:text-foreground cursor-pointer transition-colors active:scale-[0.96]", children: _jsx(Icon, { className: "h-4 w-4" }) }) }, view))) }), _jsxs(DropdownMenu, { children: [_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: tI18nHardcoded.raw('i18nComplete.text96f0c06bbcb7'), children: _jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": tI18nHardcoded.raw('i18nComplete.text96f0c06bbcb7'), className: "text-foreground/80 hover:text-foreground cursor-pointer transition-colors active:scale-[0.96] lg:hidden", children: _jsx(Code2, { className: "h-4 w-4" }) }) }) }), _jsx(DropdownMenuContent, { align: "end", className: "w-44", children: devTools.map(({ view, label, Icon }) => (_jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => openSessionQuickView(view, 'header'), children: [_jsx(Icon, {}), label] }, view))) })] }), isProjectSession && projectSession && (_jsx(SessionParticipantStack, { participants: sessionParticipants })), isProjectSession && projectSession && ShareSessionModal && (_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: shareLabel, open: isMobileViewport ? undefined : false, children: _jsxs(Button, { variant: "ghost", size: "xs", "aria-label": shareLabel, onClick: () => setShareOpen(true), className: "cursor-pointer rounded-md active:scale-[0.96] max-md:w-7 max-md:has-[>svg]:px-0", children: [_jsx(Share, {}), _jsx("span", { className: "hidden md:inline", children: tI18nHardcoded.raw('i18nComplete.text29887a5ff984') })] }) })), isMobileViewport && (_jsx(Hint, { side: "bottom", sideOffset: 4, delayDuration: 300, label: tI18nHardcoded.raw('i18nComplete.text2534086de9fe'), children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": tI18nHardcoded.raw('i18nComplete.text2534086de9fe'), "aria-expanded": isActionPanelOpen, onClick: toggleActionPanel, className: "text-foreground/80 hover:text-foreground cursor-pointer transition-colors active:scale-[0.96]", children: _jsxs("span", { className: "relative inline-flex", children: [_jsx(CaretDoubleLeftIcon, { className: "size-4" }), readyChip?.sessionId === sessionId && !isActionPanelOpen && (_jsx("span", { className: "bg-kortix-green ring-background absolute -top-1 -right-1 size-2 rounded-full ring-2", "aria-hidden": true }))] }) }) }))] })] }), isProjectSession && _jsx(PreviousRepositoryNotice, {})] }), _jsx(ExportTranscriptModal, { sessionId: sessionId, kortixSessionScope: isProjectSession ? `${projectId}/${projectSessionId}` : undefined, open: exportOpen, onOpenChange: setExportOpen }), _jsx(CompactModal, { sessionId: sessionId, open: compactOpen, onOpenChange: setCompactOpen }), isProjectSession && (_jsxs(_Fragment, { children: [_jsx(SessionConfigReloadConfirm, { busyReason: reloadConfig.busyReason, isPending: reloadConfig.isPending, onConfirm: () => reloadConfig.reload({ force: true }), onDismiss: reloadConfig.clearBusy }), ShareSessionModal && (_jsx(ShareSessionModal, { projectId: projectId, session: projectSession, open: shareOpen, onOpenChange: setShareOpen, onSaved: () => queryClient.invalidateQueries({
|
|
285
286
|
queryKey: qk.project.sessionsScope(projectId ?? ''),
|
|
286
287
|
}) })), _jsx(SessionDeleteModal, { projectId: projectId, sessionId: projectSessionId, sessionLabel: projectTitle, open: deleteOpen, onOpenChange: setDeleteOpen, onDeleted: () => router.push(uiRoutes().project(projectId)) })] }))] }));
|
|
287
288
|
}
|
|
@@ -102,7 +102,6 @@ function InlineIframePreview({ proxyUrl, port }) {
|
|
|
102
102
|
}
|
|
103
103
|
function SandboxUrlChip({ detected, proxyUrl, }) {
|
|
104
104
|
const tHardcodedUi = useTranslations('hardcodedUi');
|
|
105
|
-
const [copied, setCopied] = useState(false);
|
|
106
105
|
const tabId = `preview:${detected.port}`;
|
|
107
106
|
const tabHref = `/p/${detected.port}`;
|
|
108
107
|
const internalUrl = toInternalUrl(detected.port, detected.path);
|
|
@@ -120,12 +119,6 @@ function SandboxUrlChip({ detected, proxyUrl, }) {
|
|
|
120
119
|
}),
|
|
121
120
|
}, undefined, shallowNavigate);
|
|
122
121
|
}, [tabId, tHardcodedUi, detected.port, tabHref, proxyUrl, internalUrl, shallowNavigate]);
|
|
123
|
-
const handleCopyUrl = useCallback(() => {
|
|
124
|
-
navigator.clipboard.writeText(proxyUrl).then(() => {
|
|
125
|
-
setCopied(true);
|
|
126
|
-
setTimeout(() => setCopied(false), 2000);
|
|
127
|
-
});
|
|
128
|
-
}, [proxyUrl]);
|
|
129
122
|
const openPreview = useOpenPreviewLink();
|
|
130
123
|
const handleOpenExternal = useCallback(() => {
|
|
131
124
|
if (openPreview?.(null, detected.originalUrl, proxyUrl))
|
|
@@ -51,6 +51,27 @@ export function changeRequestOutcomes(crs, sessionId, tI18nComplete) {
|
|
|
51
51
|
// a server that sends `null` should degrade to the fallback sentence
|
|
52
52
|
// rather than throw inside a transcript render.
|
|
53
53
|
const summary = cr.description?.trim();
|
|
54
|
+
// An agent revises its work by closing this request and opening the next
|
|
55
|
+
// on the same branch. The card says so and opens the replacement
|
|
56
|
+
// (KRTX-3031). The card itself stays this request's: id and reference.
|
|
57
|
+
const replacement = cr.status === 'closed' ? (cr.superseded_by ?? null) : null;
|
|
58
|
+
const status = replacement
|
|
59
|
+
? {
|
|
60
|
+
label: tI18nComplete
|
|
61
|
+
? tI18nComplete('textddcd87c21894', { number: replacement.number })
|
|
62
|
+
: `Superseded by #${replacement.number}`,
|
|
63
|
+
tone: word.tone,
|
|
64
|
+
}
|
|
65
|
+
: { label: word.label, tone: word.tone };
|
|
66
|
+
const action = replacement
|
|
67
|
+
? {
|
|
68
|
+
label: tI18nComplete
|
|
69
|
+
? tI18nComplete('texta8ef31fa7d25', { number: replacement.number })
|
|
70
|
+
: `View #${replacement.number}`,
|
|
71
|
+
intent: 'open',
|
|
72
|
+
target: replacement.cr_id,
|
|
73
|
+
}
|
|
74
|
+
: { label: word.action, intent: 'open' };
|
|
54
75
|
return {
|
|
55
76
|
id: `cr:${cr.cr_id}`,
|
|
56
77
|
kind: 'change_request',
|
|
@@ -62,7 +83,7 @@ export function changeRequestOutcomes(crs, sessionId, tI18nComplete) {
|
|
|
62
83
|
// Kept for the modal, which has room for it. The card no longer renders
|
|
63
84
|
// a description; see `outcome-card.tsx`.
|
|
64
85
|
description: summary || tI18nComplete?.raw('text5c58b6d31ba9') || 'Ready for you to look over.',
|
|
65
|
-
status
|
|
86
|
+
status,
|
|
66
87
|
at: epoch(cr.created_at),
|
|
67
88
|
// The number is a REFERENCE, not a name — it belongs in the quiet line
|
|
68
89
|
// with the status. `into <base_ref>` is gone: a branch name is the one
|
|
@@ -73,8 +94,8 @@ export function changeRequestOutcomes(crs, sessionId, tI18nComplete) {
|
|
|
73
94
|
? tI18nComplete('texta4fc39798651', { number: cr.number })
|
|
74
95
|
: `Change request #${cr.number}`,
|
|
75
96
|
],
|
|
76
|
-
action
|
|
77
|
-
resourceHref: `?cr=${cr.cr_id}`,
|
|
97
|
+
action,
|
|
98
|
+
resourceHref: `?cr=${replacement?.cr_id ?? cr.cr_id}`,
|
|
78
99
|
};
|
|
79
100
|
});
|
|
80
101
|
}
|
|
@@ -57,9 +57,10 @@ function PoolChoices({ projectId, providers, providerId, onProviderChange, keys,
|
|
|
57
57
|
const personalChatGpt = providerId === 'codex' && personalKeys;
|
|
58
58
|
const available = new Set(keys.map((secret) => secret.secret_id));
|
|
59
59
|
const unavailable = selected.filter((secretId) => !available.has(secretId));
|
|
60
|
+
const selectedIds = new Set(selected);
|
|
60
61
|
return _jsxs(_Fragment, { children: [_jsxs(Field, { className: "gap-1.5", children: [_jsx(FieldLabel, { htmlFor: id, children: t('provider') }), _jsxs(Select, { value: providerId, onValueChange: onProviderChange, disabled: disabled, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: providers.map((provider) => _jsx(SelectItem, { value: provider, children: provider === 'codex' ? 'ChatGPT' : LLM_PROVIDER_BY_ID.get(provider)?.label ?? provider }, provider)) })] })] }), _jsx("p", { className: "text-muted-foreground text-xs", "aria-live": "polite", children: configured
|
|
61
62
|
? selected.length ? t(selected.length === 1 ? 'selectedOneForSession' : 'selectedForSession', { count: selected.length }) : t('providerDisabled')
|
|
62
|
-
: t(personalChatGpt ? 'personalChatGptDefault' : 'projectDefault') }), unavailable.length > 0 && _jsxs("div", { className: "space-y-1", role: "status", children: [_jsx("p", { className: "text-muted-foreground text-xs", children: t('unavailableKeys', { count: unavailable.length }) }), _jsx(Button, { size: "sm", variant: "outline-ghost", disabled: disabled || readOnly, onClick: () => onChange(selected.filter((secretId) => available.has(secretId))), children: t('removeUnavailable') })] }), _jsx("div", { className: "max-h-44 space-y-1 overflow-y-auto", children: keys.map((secret) => _jsxs("label", { className: "hover:bg-hover has-[:focus-visible]:ring-ring flex min-h-10 items-center gap-2 rounded-md px-2 py-2 text-sm has-[:focus-visible]:ring-2 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50", children: [_jsx(Checkbox, { checked:
|
|
63
|
+
: t(personalChatGpt ? 'personalChatGptDefault' : 'projectDefault') }), unavailable.length > 0 && _jsxs("div", { className: "space-y-1", role: "status", children: [_jsx("p", { className: "text-muted-foreground text-xs", children: t('unavailableKeys', { count: unavailable.length }) }), _jsx(Button, { size: "sm", variant: "outline-ghost", disabled: disabled || readOnly, onClick: () => onChange(selected.filter((secretId) => available.has(secretId))), children: t('removeUnavailable') })] }), _jsx("div", { className: "max-h-44 space-y-1 overflow-y-auto", children: keys.map((secret) => _jsxs("label", { className: "hover:bg-hover has-[:focus-visible]:ring-ring flex min-h-10 items-center gap-2 rounded-md px-2 py-2 text-sm has-[:focus-visible]:ring-2 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50", children: [_jsx(Checkbox, { checked: selectedIds.has(secret.secret_id), disabled: disabled || readOnly || (!selectedIds.has(secret.secret_id) && selected.length >= 10), onCheckedChange: (checked) => onChange(checked === true ? [...selected, secret.secret_id] : selected.filter((value) => value !== secret.secret_id)) }), _jsx("span", { className: "text-foreground min-w-0 break-words", children: secret.label }), providerId === 'codex' && needsReconnection(secret) && (_jsx("span", { className: "text-muted-foreground ml-auto shrink-0 text-xs", children: t('needsReconnection') }))] }, secret.secret_id)) }), selected.length >= 10 && _jsx("p", { className: "text-muted-foreground text-xs", role: "status", children: t('selectionLimit') }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [configured && !readOnly && _jsx(Button, { size: "sm", variant: "secondary", disabled: disabled, onClick: onReset, children: t(personalChatGpt ? 'resetPersonalChatGptDefault' : 'resetDefault') }), providerId === 'codex'
|
|
63
64
|
? _jsx(ChatGptAccountsButton, { projectId: projectId, variant: "outline-ghost", action: "manage", disabled: disabled })
|
|
64
65
|
: !modelsHref
|
|
65
66
|
? null
|
|
@@ -24,13 +24,9 @@ export const AdvancedPanel = memo(function AdvancedPanel({ sessionId, messages,
|
|
|
24
24
|
// 'live' follows the latest tool as new ones stream in; 'manual' pins the
|
|
25
25
|
// user's chosen index until they navigate back to the latest.
|
|
26
26
|
const [mode, setMode] = useState('live');
|
|
27
|
-
// Live-follow + clamp when the list grows/shrinks
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return;
|
|
31
|
-
setIndex((i) => (mode === 'live' ? count - 1 : Math.min(i, count - 1)));
|
|
32
|
-
}, [count, mode]);
|
|
33
|
-
const safeIndex = Math.min(index, Math.max(0, count - 1));
|
|
27
|
+
// Live-follow + clamp when the list grows/shrinks, derived in render so a
|
|
28
|
+
// streamed call never shows the previous one for a frame.
|
|
29
|
+
const safeIndex = Math.max(0, Math.min(mode === 'live' ? count - 1 : index, count - 1));
|
|
34
30
|
const current = calls[safeIndex];
|
|
35
31
|
const atLatest = safeIndex >= count - 1;
|
|
36
32
|
const isLive = atLatest && mode === 'live';
|
|
@@ -40,7 +40,7 @@ import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore
|
|
|
40
40
|
import { PreviewLoadingOverlay, SandboxAddressBar, } from '../../../browser/sandbox-browser-chrome.js';
|
|
41
41
|
import { BrowserLanding } from '../../../browser/browser-landing.js';
|
|
42
42
|
import { CloseButton, DetailSidebarToggle } from './detail-view.js';
|
|
43
|
-
import { PREVIEW_MAX_WAIT_MS, PREVIEW_PROBE_INTERVAL_MS, previewErrorReason, previewLoadSuccessState, previewLoadVerdict, runtimeSandboxHealth, sandboxRecents, shouldArmLoadTimeout, shouldKeepProbingPort, } from './easy-panel-logic.js';
|
|
43
|
+
import { PREVIEW_MAX_WAIT_MS, PREVIEW_SLOW_MS, PREVIEW_PROBE_INTERVAL_MS, previewErrorReason, previewSlowReason, previewLoadSuccessState, previewLoadVerdict, runtimeSandboxHealth, sandboxRecents, shouldArmLoadTimeout, shouldKeepProbingPort, } from './easy-panel-logic.js';
|
|
44
44
|
import { PanelWidthButton, ViewerActions } from '../../../files/viewer/viewer-actions.js';
|
|
45
45
|
// zustand v5's own hook feeds React's `useSyncExternalStore` a
|
|
46
46
|
// `getServerSnapshot` pinned to `getInitialState()` — correct for real SSR
|
|
@@ -94,6 +94,8 @@ export function AppPreview({ url, name, projectId, shareContext, onClose, onSend
|
|
|
94
94
|
const [refreshKey, setRefreshKey] = useState(0);
|
|
95
95
|
const [isLoading, setIsLoading] = useState(true);
|
|
96
96
|
const [hasError, setHasError] = useState(false);
|
|
97
|
+
// Still loading past PREVIEW_SLOW_MS: the overlay says so and offers Reload.
|
|
98
|
+
const [slow, setSlow] = useState(false);
|
|
97
99
|
const addressRef = useRef(null);
|
|
98
100
|
const isMobile = useIsMobile();
|
|
99
101
|
// Copy link mints a PUBLIC share and copies `/share/session/{token}`.
|
|
@@ -180,13 +182,21 @@ export function AppPreview({ url, name, projectId, shareContext, onClose, onSend
|
|
|
180
182
|
if (alive)
|
|
181
183
|
decide('unknown', PREVIEW_MAX_WAIT_MS);
|
|
182
184
|
}, PREVIEW_MAX_WAIT_MS);
|
|
185
|
+
// A stalled request fires no onLoad and the probe cannot see it: say so
|
|
186
|
+
// instead of spinning silently until the deadline (KRTX-3015).
|
|
187
|
+
const slowTimer = setTimeout(() => {
|
|
188
|
+
if (alive)
|
|
189
|
+
setSlow(true);
|
|
190
|
+
}, PREVIEW_SLOW_MS);
|
|
183
191
|
void tick();
|
|
184
192
|
return () => {
|
|
185
193
|
alive = false;
|
|
186
194
|
controller.abort();
|
|
187
195
|
clearTimeout(deadline);
|
|
196
|
+
clearTimeout(slowTimer);
|
|
188
197
|
if (timer)
|
|
189
198
|
clearTimeout(timer);
|
|
199
|
+
setSlow(false);
|
|
190
200
|
};
|
|
191
201
|
}, [isLoading, refreshKey, noApp, hasPreview, previewUrl]);
|
|
192
202
|
// Nothing to load, so land the cursor on the address bar — the fastest way
|
|
@@ -234,7 +244,7 @@ export function AppPreview({ url, name, projectId, shareContext, onClose, onSend
|
|
|
234
244
|
return;
|
|
235
245
|
track('app_opened_new_tab');
|
|
236
246
|
window.open(previewUrl, '_blank', 'noopener,noreferrer');
|
|
237
|
-
}, className: "size-7 shrink-0 active:scale-[0.96]", children: _jsx(ArrowSquareOutIcon, { className: "size-3.5" }) }) }), _jsx(PanelWidthButton, { isMobile: isMobile }), _jsx(CloseButton, { onClose: onClose })] })] }), _jsxs("div", { className: "relative min-h-0 flex-1 overflow-hidden", children: [isLoading && hasPreview && !noApp && (_jsx(PreviewLoadingOverlay, { label: tI18nComplete.raw('textc4cf2b2ccb5d') })), (hasError || hostPreview.failed) && !noApp && (_jsx(ErrorState, { icon: WarningIcon, size: "sm", className: "bg-background absolute inset-0 z-10", title: tI18nComplete('textba601cdb484b', { value0: title }),
|
|
247
|
+
}, className: "size-7 shrink-0 active:scale-[0.96]", children: _jsx(ArrowSquareOutIcon, { className: "size-3.5" }) }) }), _jsx(PanelWidthButton, { isMobile: isMobile }), _jsx(CloseButton, { onClose: onClose })] })] }), _jsxs("div", { className: "relative min-h-0 flex-1 overflow-hidden", children: [isLoading && hasPreview && !noApp && (_jsx(PreviewLoadingOverlay, { label: tI18nComplete.raw('textc4cf2b2ccb5d'), detail: slow ? previewSlowReason({ port }) : undefined, action: slow ? (_jsx(Button, { variant: "outline", size: "sm", className: "gap-1.5", onClick: reload, children: tI18nComplete.raw('textbdc090ec61e3') })) : undefined })), (hasError || hostPreview.failed) && !noApp && (_jsx(ErrorState, { icon: WarningIcon, size: "sm", className: "bg-background absolute inset-0 z-10", title: tI18nComplete('textba601cdb484b', { value0: title }),
|
|
238
248
|
/* The single most common cause, said plainly: the agent started the
|
|
239
249
|
server a moment ago and it isn't listening yet. Only a SETTLED
|
|
240
250
|
`dead` verdict earns the stopped-workspace wording — see
|
|
@@ -183,5 +183,5 @@ export declare function shouldAutoOpenPayoff(args: {
|
|
|
183
183
|
interactedThisRun: boolean;
|
|
184
184
|
}): boolean;
|
|
185
185
|
export { aspectChangedWidth, fitSplitPercent, resolveSideSize } from '../session-panel-sizing.js';
|
|
186
|
-
export { PREVIEW_MAX_WAIT_MS, PREVIEW_PROBE_INTERVAL_MS, PREVIEW_UNREACHABLE_WINDOW_MS, previewErrorReason, previewLoadSuccessState, previewLoadVerdict, runtimeSandboxHealth, shouldArmLoadTimeout, shouldKeepProbingPort } from './preview-load.js';
|
|
186
|
+
export { PREVIEW_MAX_WAIT_MS, PREVIEW_PROBE_INTERVAL_MS, PREVIEW_SLOW_MS, PREVIEW_UNREACHABLE_WINDOW_MS, previewErrorReason, previewLoadSuccessState, previewLoadVerdict, previewSlowReason, runtimeSandboxHealth, shouldArmLoadTimeout, shouldKeepProbingPort } from './preview-load.js';
|
|
187
187
|
export type { PreviewProbe, SandboxHealth } from './preview-load.js';
|
|
@@ -238,4 +238,4 @@ export function shouldAutoOpenPayoff(args) {
|
|
|
238
238
|
!args.interactedThisRun);
|
|
239
239
|
}
|
|
240
240
|
export { aspectChangedWidth, fitSplitPercent, resolveSideSize } from '../session-panel-sizing.js';
|
|
241
|
-
export { PREVIEW_MAX_WAIT_MS, PREVIEW_PROBE_INTERVAL_MS, PREVIEW_UNREACHABLE_WINDOW_MS, previewErrorReason, previewLoadSuccessState, previewLoadVerdict, runtimeSandboxHealth, shouldArmLoadTimeout, shouldKeepProbingPort } from './preview-load.js';
|
|
241
|
+
export { PREVIEW_MAX_WAIT_MS, PREVIEW_PROBE_INTERVAL_MS, PREVIEW_SLOW_MS, PREVIEW_UNREACHABLE_WINDOW_MS, previewErrorReason, previewLoadSuccessState, previewLoadVerdict, previewSlowReason, runtimeSandboxHealth, shouldArmLoadTimeout, shouldKeepProbingPort } from './preview-load.js';
|
|
@@ -145,7 +145,8 @@ export function OutputRows({ outputs, onOpenOutput, initialShowAll, }) {
|
|
|
145
145
|
const deliverables = outputs.filter((o) => !isScaffoldingOutput(o));
|
|
146
146
|
const base = deliverables.length > 0 ? deliverables : outputs;
|
|
147
147
|
const visible = showAll ? outputs : base.slice(0, VISIBLE_LIMIT);
|
|
148
|
-
const
|
|
148
|
+
const visibleSet = new Set(visible);
|
|
149
|
+
const hiddenRows = outputs.filter((o) => !visibleSet.has(o));
|
|
149
150
|
const hidden = hiddenRows.length;
|
|
150
151
|
const hiddenNoun = `${hiddenRows.every((o) => o.kind === 'file') ? 'file' : 'item'}${hidden === 1 ? '' : 's'}`;
|
|
151
152
|
// Grouping only ever organizes the expanded long list (see
|
|
@@ -18,7 +18,7 @@ import { Empty, EmptyContent, EmptyDescription, EmptyMedia } from '../../../prim
|
|
|
18
18
|
import { cn } from '../../../lib/utils.js';
|
|
19
19
|
import { CaretRightIcon as ChevronRight } from '@phosphor-icons/react';
|
|
20
20
|
import { m, useReducedMotion } from 'motion/react';
|
|
21
|
-
import {
|
|
21
|
+
import { useState } from 'react';
|
|
22
22
|
/** Full-width row trigger, clipped by the parent's `overflow-hidden` so its square corners never peek past the card's rounded-md border. */
|
|
23
23
|
const HEADER_CLASS = cn('flex min-h-11 w-full items-center justify-between gap-2 rounded-none px-3.5 py-2 text-left', 'transition-[background-color,transform] active:scale-[0.998]', 'hover:bg-muted-foreground/[0.04]');
|
|
24
24
|
function CardTitleRow({ title, count, indicator, subtitle, chevron, }) {
|
|
@@ -31,10 +31,12 @@ export function PanelCard({ title, count, indicator, subtitle, children, emptyAr
|
|
|
31
31
|
// `defaultExpanded` can flip true after mount (e.g. "auto-open Outputs the
|
|
32
32
|
// moment a run finishes with something to show"). One-way sync: force open
|
|
33
33
|
// when that happens, but never fight a user's manual collapse afterwards.
|
|
34
|
-
|
|
34
|
+
const [prevDefaultExpanded, setPrevDefaultExpanded] = useState(defaultExpanded);
|
|
35
|
+
if (defaultExpanded !== prevDefaultExpanded) {
|
|
36
|
+
setPrevDefaultExpanded(defaultExpanded);
|
|
35
37
|
if (defaultExpanded)
|
|
36
38
|
setExpanded(true);
|
|
37
|
-
}
|
|
39
|
+
}
|
|
38
40
|
return (_jsxs(Disclosure, { open: expanded, onOpenChange: setExpanded, variant: "outline", className: cn('bg-pane text-popover-foreground border-border overflow-hidden rounded-[calc(var(--radius)-3px)] border ease-out', fill ? 'flex min-h-0 flex-col' : 'shrink-0'), transition: transition, children: [_jsx(DisclosureTrigger, { variant: "outline", children: _jsx("div", { className: cn(HEADER_CLASS, 'cursor-pointer'), children: _jsx(CardTitleRow, { title: title, count: count, indicator: indicator, subtitle: subtitle, chevron: _jsxs("span", { className: "flex shrink-0 items-center gap-0.5", children: [headerAction && (
|
|
39
41
|
// Isolated from the toggle on both input paths: `stopPropagation`
|
|
40
42
|
// on click covers the mouse/tap case, and on keydown stops the
|
|
@@ -91,6 +91,20 @@ export declare const PREVIEW_UNREACHABLE_WINDOW_MS = 10000;
|
|
|
91
91
|
* 50% margin. It is a last resort, not the normal path — with a working probe
|
|
92
92
|
* a dead port resolves in ~{@link PREVIEW_UNREACHABLE_WINDOW_MS}. */
|
|
93
93
|
export declare const PREVIEW_MAX_WAIT_MS = 90000;
|
|
94
|
+
/** How long the frame may load before the pane says it is slow, ms.
|
|
95
|
+
*
|
|
96
|
+
* A frame whose request stalls (the app has not answered yet, or a
|
|
97
|
+
* single-threaded server is busy with another client) fires no `onLoad`, and
|
|
98
|
+
* the port probe cannot see that. The pane then showed a bare "Loading
|
|
99
|
+
* preview…" until {@link PREVIEW_MAX_WAIT_MS} (KRTX-3015). Past this point it
|
|
100
|
+
* keeps waiting but says so, and offers Reload. Fifteen seconds is past the
|
|
101
|
+
* probe's {@link PREVIEW_UNREACHABLE_WINDOW_MS}, so a dead port has already
|
|
102
|
+
* failed by then, and well inside a cold compile, which only earns a line. */
|
|
103
|
+
export declare const PREVIEW_SLOW_MS = 15000;
|
|
104
|
+
/** The line under the loading spinner once the preview is slow. */
|
|
105
|
+
export declare function previewSlowReason(input: {
|
|
106
|
+
port: number;
|
|
107
|
+
}): string;
|
|
94
108
|
/** One probe's verdict about the port. Aliased from the SDK's own type rather
|
|
95
109
|
* than restated, so the two can never drift apart. */
|
|
96
110
|
export type PreviewProbe = PreviewPortProbe;
|
|
@@ -63,6 +63,21 @@ export const PREVIEW_UNREACHABLE_WINDOW_MS = 10_000;
|
|
|
63
63
|
* 50% margin. It is a last resort, not the normal path — with a working probe
|
|
64
64
|
* a dead port resolves in ~{@link PREVIEW_UNREACHABLE_WINDOW_MS}. */
|
|
65
65
|
export const PREVIEW_MAX_WAIT_MS = 90_000;
|
|
66
|
+
/** How long the frame may load before the pane says it is slow, ms.
|
|
67
|
+
*
|
|
68
|
+
* A frame whose request stalls (the app has not answered yet, or a
|
|
69
|
+
* single-threaded server is busy with another client) fires no `onLoad`, and
|
|
70
|
+
* the port probe cannot see that. The pane then showed a bare "Loading
|
|
71
|
+
* preview…" until {@link PREVIEW_MAX_WAIT_MS} (KRTX-3015). Past this point it
|
|
72
|
+
* keeps waiting but says so, and offers Reload. Fifteen seconds is past the
|
|
73
|
+
* probe's {@link PREVIEW_UNREACHABLE_WINDOW_MS}, so a dead port has already
|
|
74
|
+
* failed by then, and well inside a cold compile, which only earns a line. */
|
|
75
|
+
export const PREVIEW_SLOW_MS = 15_000;
|
|
76
|
+
/** The line under the loading spinner once the preview is slow. */
|
|
77
|
+
export function previewSlowReason(input) {
|
|
78
|
+
const app = input.port > 0 ? `The app on port ${input.port}` : 'The app';
|
|
79
|
+
return `${app} is taking a while to answer. Reload to ask again.`;
|
|
80
|
+
}
|
|
66
81
|
/**
|
|
67
82
|
* Whether `AppPreview` should keep waiting or show "Couldn't load".
|
|
68
83
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback,
|
|
3
|
+
import { useCallback, useState } from 'react';
|
|
4
4
|
import { ActionNavigator } from '../shared/action-navigator.js';
|
|
5
5
|
import { clampIndex } from '../shared/action-navigator-logic.js';
|
|
6
6
|
import { ToolParts } from './detail-view.js';
|
|
@@ -15,12 +15,7 @@ focusCallId, }) {
|
|
|
15
15
|
// Arriving from a chat click pins the view to that call; 'live' would drag
|
|
16
16
|
// it straight back to the newest part on the next streaming update.
|
|
17
17
|
const [mode, setMode] = useState(focusIndex >= 0 ? 'manual' : 'live');
|
|
18
|
-
|
|
19
|
-
if (calls.length === 0)
|
|
20
|
-
return;
|
|
21
|
-
setIndex((i) => (mode === 'live' ? calls.length - 1 : clampIndex(i, calls.length)));
|
|
22
|
-
}, [calls.length, mode]);
|
|
23
|
-
const safeIndex = clampIndex(index, calls.length);
|
|
18
|
+
const safeIndex = clampIndex(mode === 'live' ? calls.length - 1 : index, calls.length);
|
|
24
19
|
const current = calls[safeIndex];
|
|
25
20
|
const atLatest = safeIndex >= calls.length - 1;
|
|
26
21
|
// Stable identity so ActionNavigator's keydown effect doesn't re-subscribe
|
|
@@ -120,8 +120,18 @@ hotkey = true, } = {}) {
|
|
|
120
120
|
toggleRight();
|
|
121
121
|
}
|
|
122
122
|
};
|
|
123
|
+
// The desktop shell sends the same key from inside a cross-origin iframe
|
|
124
|
+
// (the Browser preview), where this window never sees it (KRTX-2944).
|
|
125
|
+
const onCommand = (e) => {
|
|
126
|
+
if (e.detail === 'toggle-right-panel')
|
|
127
|
+
toggleRight();
|
|
128
|
+
};
|
|
123
129
|
window.addEventListener('keydown', onKey);
|
|
124
|
-
|
|
130
|
+
window.addEventListener('kortix-desktop-command', onCommand);
|
|
131
|
+
return () => {
|
|
132
|
+
window.removeEventListener('keydown', onKey);
|
|
133
|
+
window.removeEventListener('kortix-desktop-command', onCommand);
|
|
134
|
+
};
|
|
125
135
|
}, [shouldHandleHotkey, toggleRight]);
|
|
126
136
|
// No provider means no panel to render: `SessionChat` also renders read-only
|
|
127
137
|
// inside `sub-session-modal.tsx`, outside `SessionLayout`. Mobile has no
|
|
@@ -15,10 +15,6 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { type SessionAudit, type SessionAuditAction } from '@kortix/sdk';
|
|
17
17
|
import { type QueryClient } from '@tanstack/react-query';
|
|
18
|
-
/** One poll cadence for the shared session-audit query, so both surfaces (panel
|
|
19
|
-
* + header nudge) agree regardless of which mounts first. Pauses in background
|
|
20
|
-
* tabs (react-query's refetchIntervalInBackground defaults to false). */
|
|
21
|
-
export declare const SESSION_AUDIT_REFETCH_MS = 15000;
|
|
22
18
|
export declare function sessionAuditKey(projectId: string | undefined, sessionId: string | undefined): readonly ["session-audit", string, string];
|
|
23
19
|
/** A gated action still awaiting a human decision (unresolved `pending_approval`). */
|
|
24
20
|
export declare function isPendingAction(a: SessionAuditAction): boolean;
|
|
@@ -21,7 +21,7 @@ import { useInfiniteQuery, useMutation, useQuery, useQueryClient, } from '@tanst
|
|
|
21
21
|
/** One poll cadence for the shared session-audit query, so both surfaces (panel
|
|
22
22
|
* + header nudge) agree regardless of which mounts first. Pauses in background
|
|
23
23
|
* tabs (react-query's refetchIntervalInBackground defaults to false). */
|
|
24
|
-
|
|
24
|
+
const SESSION_AUDIT_REFETCH_MS = 15_000;
|
|
25
25
|
export function sessionAuditKey(projectId, sessionId) {
|
|
26
26
|
return ['session-audit', projectId ?? '', sessionId ?? ''];
|
|
27
27
|
}
|
|
@@ -28,7 +28,7 @@ export declare const CHANGE_STATUS_META: Record<string, {
|
|
|
28
28
|
* working-tree-only read drops to zero the moment the agent commits, and the
|
|
29
29
|
* badge and the CTA vanish while the work is still not in the base version.
|
|
30
30
|
*/
|
|
31
|
-
|
|
31
|
+
interface SessionChanges {
|
|
32
32
|
/** Every file this version changed against its base. Never undefined. */
|
|
33
33
|
files: VcsFileDiff[];
|
|
34
34
|
count: number;
|
|
@@ -55,3 +55,4 @@ export declare function useOpenChangeRequest(chatSessionId: string | undefined,
|
|
|
55
55
|
asking: boolean;
|
|
56
56
|
openChangeRequest: () => Promise<void>;
|
|
57
57
|
};
|
|
58
|
+
export {};
|
|
@@ -30,6 +30,7 @@ import { SessionAuditPanel } from './session-audit-panel.js';
|
|
|
30
30
|
import { SessionRemindersPanel } from './session-reminders-panel.js';
|
|
31
31
|
import { SessionFilesExplorer } from './session-files-explorer.js';
|
|
32
32
|
import { useSandboxProxy } from '../../internal/hooks/use-sandbox-proxy.js';
|
|
33
|
+
import { OutputPathsContext } from '../../markdown/code/output-paths-context.js';
|
|
33
34
|
import { useIsMobile } from '../../hooks/use-mobile.js';
|
|
34
35
|
import { track } from '../../lib/telemetry.js';
|
|
35
36
|
import { parseLocalhostUrl } from '@kortix/sdk';
|
|
@@ -358,7 +359,6 @@ export function SessionPanelProvider({ sessionId, isSessionBusy = false, project
|
|
|
358
359
|
});
|
|
359
360
|
setPanelSplit(split);
|
|
360
361
|
}, [
|
|
361
|
-
sessionId,
|
|
362
362
|
getServiceUrl,
|
|
363
363
|
closeDetail,
|
|
364
364
|
openDetail,
|
|
@@ -536,6 +536,10 @@ export function SessionPanelProvider({ sessionId, isSessionBusy = false, project
|
|
|
536
536
|
detail,
|
|
537
537
|
memoryKey,
|
|
538
538
|
]);
|
|
539
|
+
// Inline code that names an output by its bare filename opens it (KRTX-3023).
|
|
540
|
+
// Keyed by content, so a re-derivation with the same paths re-renders no text.
|
|
541
|
+
const outputPathsKey = files.flatMap((f) => (f.path ? [f.path] : [])).join('\n');
|
|
542
|
+
const outputPaths = useMemo(() => (outputPathsKey ? outputPathsKey.split('\n') : []), [outputPathsKey]);
|
|
539
543
|
const value = useMemo(() => ({
|
|
540
544
|
sessionId,
|
|
541
545
|
projectId,
|
|
@@ -577,5 +581,5 @@ export function SessionPanelProvider({ sessionId, isSessionBusy = false, project
|
|
|
577
581
|
openAudit,
|
|
578
582
|
openReminders,
|
|
579
583
|
]);
|
|
580
|
-
return _jsx(SessionPanelContext.Provider, { value: value, children: children });
|
|
584
|
+
return (_jsx(SessionPanelContext.Provider, { value: value, children: _jsx(OutputPathsContext.Provider, { value: outputPaths, children: children }) }));
|
|
581
585
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useMemo, useRef } from 'react';
|
|
1
|
+
import { useLayoutEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { useSessionLogThread } from '@kortix/sdk/react';
|
|
3
3
|
import { samePartsList } from '../../turn/same-parts.js';
|
|
4
4
|
import { deriveIsRunning } from '../easy/easy-panel-logic.js';
|
|
@@ -11,9 +11,11 @@ import { deriveRunOutcome } from './run-outcome.js';
|
|
|
11
11
|
/** The previous list while its elements are unchanged, so memos keyed on it hold across a text delta. */
|
|
12
12
|
function useSameList(next) {
|
|
13
13
|
const ref = useRef(next);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const list = samePartsList(ref.current, next) ? ref.current : next;
|
|
15
|
+
useLayoutEffect(() => {
|
|
16
|
+
ref.current = list;
|
|
17
|
+
});
|
|
18
|
+
return list;
|
|
17
19
|
}
|
|
18
20
|
export function usePanelDerivations({ sessionId, projectId, projectSessionId, isSessionBusy }) {
|
|
19
21
|
// The root thread's messages: the `fromV1` live view of the v1 rows while
|
|
@@ -59,7 +59,7 @@ function usePanelTransitions(panelGroupRef) {
|
|
|
59
59
|
panels.forEach((panel) => {
|
|
60
60
|
panel.style.transition = 'flex 300ms cubic-bezier(0.4, 0, 0.2, 1)';
|
|
61
61
|
});
|
|
62
|
-
}, []);
|
|
62
|
+
}, [panelGroupRef]);
|
|
63
63
|
const disablePanelTransition = useCallback(() => {
|
|
64
64
|
const el = panelGroupRef.current;
|
|
65
65
|
if (!el)
|
|
@@ -68,7 +68,7 @@ function usePanelTransitions(panelGroupRef) {
|
|
|
68
68
|
panels.forEach((panel) => {
|
|
69
69
|
panel.style.transition = 'none';
|
|
70
70
|
});
|
|
71
|
-
}, []);
|
|
71
|
+
}, [panelGroupRef]);
|
|
72
72
|
return { enablePanelTransition, disablePanelTransition };
|
|
73
73
|
}
|
|
74
74
|
// Keep the box current so a measurement landing at any moment has a real
|
|
@@ -95,7 +95,7 @@ function usePanelBoxObserver({ isMobile, panelGroupRef, panelBoxRef, }) {
|
|
|
95
95
|
});
|
|
96
96
|
observer.observe(el);
|
|
97
97
|
return () => observer.disconnect();
|
|
98
|
-
}, [isMobile]);
|
|
98
|
+
}, [isMobile, panelBoxRef, panelGroupRef]);
|
|
99
99
|
}
|
|
100
100
|
function usePanelSideSize({ isExpanded, isEasy, panelSplit, panelAspect, panelBoxRef, }) {
|
|
101
101
|
// Easy mode opens at the panel's MINIMUM width (35/65): the cards are a
|
|
@@ -125,7 +125,7 @@ function usePanelSideSize({ isExpanded, isEasy, panelSplit, panelAspect, panelBo
|
|
|
125
125
|
panelAspect,
|
|
126
126
|
panelSplit,
|
|
127
127
|
panelBox: panelBoxRef.current,
|
|
128
|
-
}), [isExpanded, isEasy, panelAspect, panelSplit]);
|
|
128
|
+
}), [isExpanded, isEasy, panelAspect, panelSplit, panelBoxRef]);
|
|
129
129
|
const mainSize = 100 - sideSize;
|
|
130
130
|
return { sideSize, mainSize };
|
|
131
131
|
}
|
|
@@ -185,6 +185,8 @@ function usePanelGlide(input) {
|
|
|
185
185
|
panelAspect,
|
|
186
186
|
sideSize,
|
|
187
187
|
mainSize,
|
|
188
|
+
mainPanelRef,
|
|
189
|
+
sidePanelRef,
|
|
188
190
|
]);
|
|
189
191
|
return isAnimating;
|
|
190
192
|
}
|
|
@@ -218,5 +220,5 @@ function usePanelGlideFrames({ isAnimating, enablePanelTransition, sideSize, mai
|
|
|
218
220
|
mainPanelRef.current?.resize(mainSize);
|
|
219
221
|
});
|
|
220
222
|
return () => cancelAnimationFrame(raf);
|
|
221
|
-
}, [isAnimating, enablePanelTransition, sideSize, mainSize]);
|
|
223
|
+
}, [isAnimating, enablePanelTransition, sideSize, mainSize, mainPanelRef, sidePanelRef]);
|
|
222
224
|
}
|
|
@@ -14,16 +14,17 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
14
14
|
* entry wins over the raw agent name.
|
|
15
15
|
*/
|
|
16
16
|
import { useUiHostConfig } from '../../providers/ui-provider.js';
|
|
17
|
+
const printedAtFormat = new Intl.DateTimeFormat('en-GB', {
|
|
18
|
+
day: 'numeric',
|
|
19
|
+
month: 'long',
|
|
20
|
+
year: 'numeric',
|
|
21
|
+
hour: '2-digit',
|
|
22
|
+
minute: '2-digit',
|
|
23
|
+
hour12: false,
|
|
24
|
+
});
|
|
17
25
|
/** `16 September 2026 at 20:41` — unambiguous across locales, no slashes. */
|
|
18
26
|
export function formatPrintedAt(date) {
|
|
19
|
-
return
|
|
20
|
-
day: 'numeric',
|
|
21
|
-
month: 'long',
|
|
22
|
-
year: 'numeric',
|
|
23
|
-
hour: '2-digit',
|
|
24
|
-
minute: '2-digit',
|
|
25
|
-
hour12: false,
|
|
26
|
-
}).format(date);
|
|
27
|
+
return printedAtFormat.format(date);
|
|
27
28
|
}
|
|
28
29
|
/** The meta line under the title: the parts that exist, separated by a middot. */
|
|
29
30
|
export function printHeaderMeta(input) {
|
|
@@ -78,6 +78,9 @@ function DetailShell({ expandable, expanded, onToggle, children, }) {
|
|
|
78
78
|
function PendingLabel({ pending, children }) {
|
|
79
79
|
return (_jsxs("span", { className: "relative inline-flex items-center justify-center", children: [_jsx("span", { className: cn('inline-flex items-center', pending && 'invisible'), children: children }), pending ? (_jsx("span", { className: "absolute inset-0 inline-flex items-center justify-center", children: _jsx(Loading, { className: "size-3 shrink-0" }) })) : null] }));
|
|
80
80
|
}
|
|
81
|
+
function DecisionButton({ pending, children, ...props }) {
|
|
82
|
+
return (_jsx(Button, { size: "xs", ...props, children: _jsx(PendingLabel, { pending: pending, children: children }) }));
|
|
83
|
+
}
|
|
81
84
|
export function SessionPermissionPrompt({ sessionId, agentName, permissions, onReply, }) {
|
|
82
85
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
83
86
|
// Only the /projects/[id]/sessions/[sessionId] route has a project in scope —
|
|
@@ -215,9 +218,9 @@ export function SessionPermissionPrompt({ sessionId, agentName, permissions, onR
|
|
|
215
218
|
// Only a detail that actually expands is a control. Rendering
|
|
216
219
|
// the flat case as a `<button>` too put a dead stop in the tab
|
|
217
220
|
// order of every permission row.
|
|
218
|
-
_jsxs(DetailShell, { expandable: expandable, expanded: expanded, onToggle: () => toggleExpanded(p.id), children: [_jsx("code", { title: expandable ? undefined : detail, className: cn('text-muted-foreground font-mono text-xs', expanded ? 'wrap-break-word whitespace-pre-wrap' : 'truncate'), children: detail }), expandable ? (_jsx(CaretDownIcon, { className: cn('text-muted-foreground mt-0.5 size-3 shrink-0 transition-transform duration-150', expanded && 'rotate-180') })) : null] })) : null] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [_jsx(
|
|
219
|
-
}) }), _jsxs("div", { className: "border-border flex items-center gap-2 border-t px-3 py-2", children: [_jsx("span", { className: "text-muted-foreground text-xs", children: tI18nComplete.raw('text16324ed5894b') }), _jsx(
|
|
221
|
+
_jsxs(DetailShell, { expandable: expandable, expanded: expanded, onToggle: () => toggleExpanded(p.id), children: [_jsx("code", { title: expandable ? undefined : detail, className: cn('text-muted-foreground font-mono text-xs', expanded ? 'wrap-break-word whitespace-pre-wrap' : 'truncate'), children: detail }), expandable ? (_jsx(CaretDownIcon, { className: cn('text-muted-foreground mt-0.5 size-3 shrink-0 transition-transform duration-150', expanded && 'rotate-180') })) : null] })) : null] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [_jsx(DecisionButton, { variant: "outline", disabled: !!busy, onClick: () => void reply(p.id, 'reject'), pending: busy === `${p.id}:reject`, children: tI18nComplete.raw('text05a2d7332eb9') }), _jsx(DecisionButton, { variant: "muted", title: tI18nComplete.raw('text403595dd63ea'), disabled: !!busy, onClick: () => void reply(p.id, 'always'), pending: busy === `${p.id}:always`, children: tI18nComplete.raw('textec9f8a9109ec') }), _jsx(DecisionButton, { variant: "default", disabled: !!busy, onClick: () => void reply(p.id, 'once'), pending: busy === `${p.id}:once`, children: tI18nComplete.raw('text168511d24d9e') })] })] }, p.id));
|
|
222
|
+
}) }), _jsxs("div", { className: "border-border flex items-center gap-2 border-t px-3 py-2", children: [_jsx("span", { className: "text-muted-foreground text-xs", children: tI18nComplete.raw('text16324ed5894b') }), _jsx(DecisionButton, { variant: "ghost", className: "text-muted-foreground hover:text-foreground", title: tI18nComplete.raw('text2c1a881dfca8'), disabled: !!busy, onClick: () => void allowAllForSession(), pending: busy === 'session-all', children: tI18nComplete.raw('text1f05c7f5b64d') })] }), canWriteConfig.allowed && agentBlock ? (
|
|
220
223
|
// Deliberately set apart from the one-off buttons above: these WRITE the
|
|
221
224
|
// project's permission config — every future session stops asking.
|
|
222
|
-
_jsxs("div", { className: "bg-muted/40 border-border flex flex-wrap items-center gap-2 border-t px-3 py-2", children: [_jsxs("span", { className: "text-muted-foreground text-xs", children: [tI18nComplete.raw('text689997c79ba6'), ' ', _jsx("span", { className: "opacity-70", children: tI18nComplete.raw('textea73705db65f') }), ":"] }), uniqueTypes.map((type) => (
|
|
225
|
+
_jsxs("div", { className: "bg-muted/40 border-border flex flex-wrap items-center gap-2 border-t px-3 py-2", children: [_jsxs("span", { className: "text-muted-foreground text-xs", children: [tI18nComplete.raw('text689997c79ba6'), ' ', _jsx("span", { className: "opacity-70", children: tI18nComplete.raw('textea73705db65f') }), ":"] }), uniqueTypes.map((type) => (_jsxs(DecisionButton, { variant: "ghost", className: "text-muted-foreground hover:text-foreground", disabled: !!busy, onClick: () => void allowInConfig(type), pending: busy === `config:${type}`, children: [tI18nComplete.raw('text78facd7b499c'), PERMISSION_LABELS[type] || type, "\""] }, type))), _jsx(DecisionButton, { variant: "ghost", className: "text-muted-foreground hover:text-foreground", disabled: !!busy, onClick: () => void allowInConfig('*'), pending: busy === 'config:*', children: tI18nComplete.raw('text84f8c40b31a7') })] })) : null] }));
|
|
223
226
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { UiTranslator } from '../../i18n/index.js';
|
|
2
2
|
import type { PendingSessionPrompt } from '@kortix/sdk';
|
|
3
|
-
|
|
3
|
+
interface ProvisioningFailurePresentation {
|
|
4
4
|
title: string;
|
|
5
5
|
message: string;
|
|
6
6
|
retryable: boolean;
|
|
@@ -20,3 +20,4 @@ export declare function pendingSessionPromptFromMetadata(metadata: Record<string
|
|
|
20
20
|
* and Retry working identically on either path.
|
|
21
21
|
*/
|
|
22
22
|
export declare function pendingSessionPromptForRecovery(sessionId: string, metadata: Record<string, unknown> | null | undefined): PendingSessionPrompt | null;
|
|
23
|
+
export {};
|