@kortix/ui 0.14.6 → 0.14.8
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 +490 -382
- package/dist/_vendor/shared/runtime-versions.d.ts +4 -0
- package/dist/access/access/access-dialog-share.js +4 -7
- package/dist/access/access/access-dialog.js +2 -1
- package/dist/access/access/principal-picker.js +6 -5
- package/dist/customize/agents/agent-authority-card.js +1 -2
- package/dist/customize/agents/agent-cover-card.d.ts +17 -0
- package/dist/customize/agents/agent-cover-card.js +13 -0
- 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 +68 -70
- package/dist/customize/agents/agent-page.js +27 -19
- 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 +43 -49
- 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/detail/app-page.js +19 -5
- 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/migrate-to-v2/migration-prompt.d.ts +1 -0
- package/dist/customize/migrate-to-v2/migration-prompt.js +1 -0
- 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/channels-view.js +2 -6
- package/dist/customize/sections/view/gateway/gateway-routing.js +3 -3
- 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/tabs/general-tab.d.ts +9 -2
- package/dist/customize/settings/tabs/general-tab.js +33 -11
- 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/new-entity-menu.js +4 -1
- package/dist/customize/skills/skills-page.js +4 -5
- package/dist/customize/triggers/schedule/then-fields.js +1 -5
- package/dist/customize/triggers/schedule-view.js +4 -2
- package/dist/files/project/api/runtime-files.js +3 -2
- package/dist/files/project/file-source.js +18 -1
- package/dist/files/project/hooks/use-file-content.js +2 -0
- 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 +12 -2
- package/dist/files/viewer/file-source.d.ts +3 -1
- package/dist/files/viewer/use-file-content-state.js +26 -7
- package/dist/files/viewer/viewer-actions.js +2 -2
- package/dist/i18n/en.d.ts +393 -12
- package/dist/i18n/en.js +400 -19
- package/dist/internal/components/agent-mark.d.ts +38 -0
- package/dist/internal/components/agent-mark.js +38 -0
- 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/lib/pixel-mark.d.ts +33 -0
- package/dist/internal/lib/pixel-mark.js +152 -0
- package/dist/internal/lib/query-client-singleton.d.ts +9 -3
- package/dist/internal/lib/query-client-singleton.js +18 -12
- package/dist/internal/lib/use-project-feature-flags.js +3 -3
- 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/genui/components/inline.d.ts +1 -1
- package/dist/markdown/setup-links/app-credentials-form.d.ts +12 -0
- package/dist/markdown/setup-links/app-credentials-form.js +115 -0
- package/dist/markdown/setup-links/secret-intake-form.d.ts +10 -0
- package/dist/markdown/setup-links/secret-intake-form.js +1 -1
- package/dist/primitives/entity-avatar.d.ts +4 -1
- package/dist/primitives/entity-avatar.js +4 -2
- package/dist/primitives/settings-section-header.d.ts +1 -1
- package/dist/primitives/user-avatar.d.ts +3 -1
- package/dist/primitives/user-avatar.js +4 -2
- package/dist/providers/ui-provider.d.ts +1 -1
- package/dist/session/composer/agent-selector.d.ts +7 -1
- package/dist/session/composer/agent-selector.js +20 -10
- 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-draft-recovery.d.ts +1 -2
- package/dist/session/composer/composer-draft-recovery.js +0 -13
- 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 +8 -6
- package/dist/session/composer/context-usage.js +9 -7
- 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/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/token-progress.d.ts +1 -1
- package/dist/session/composer/token-progress.js +6 -6
- package/dist/session/header/export-transcript-modal.d.ts +3 -2
- package/dist/session/header/export-transcript-modal.js +6 -3
- 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/models/model-selector.d.ts +3 -1
- package/dist/session/models/model-selector.js +3 -2
- package/dist/session/panel/easy/plan-card.d.ts +2 -0
- package/dist/session/panel/easy/plan-card.js +2 -0
- 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/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/plan-surface.d.ts +1 -0
- package/dist/session/thread/plan-surface.js +1 -0
- package/dist/session/thread/session-chat.js +5 -12
- package/dist/session/thread/session-context-metrics.d.ts +15 -0
- package/dist/session/thread/session-context-metrics.js +10 -1
- package/dist/session/thread/session-context-modal.d.ts +4 -2
- package/dist/session/thread/session-context-modal.js +16 -9
- package/dist/session/thread/session-history-scroll.d.ts +2 -1
- package/dist/session/thread/session-thread.js +1 -1
- package/dist/session/thread/transcript.d.ts +4 -0
- package/dist/session/thread/transcript.js +18 -5
- package/dist/session/thread/turn-share.d.ts +16 -0
- package/dist/session/thread/turn-share.js +162 -0
- package/dist/session/tool/tools/apply-patch-tool.d.ts +1 -0
- package/dist/session/tool/tools/apply-patch-tool.js +1 -0
- package/dist/session/tool/tools/pty-kill-tool.d.ts +1 -0
- package/dist/session/tool/tools/pty-kill-tool.js +1 -0
- package/dist/session/tool/tools/pty-read-tool.d.ts +2 -0
- package/dist/session/tool/tools/pty-read-tool.js +2 -0
- package/dist/session/tool/tools/pty-spawn-tool.d.ts +1 -0
- package/dist/session/tool/tools/pty-spawn-tool.js +1 -0
- package/dist/session/tool/tools/pty-write-tool.d.ts +1 -0
- package/dist/session/tool/tools/pty-write-tool.js +1 -0
- package/dist/session/tool/tools/todo-write-tool.d.ts +1 -0
- package/dist/session/tool/tools/todo-write-tool.js +1 -0
- package/dist/session/turn/message-author-label.js +6 -2
- package/dist/session/turn/plan-anchor.d.ts +3 -0
- package/dist/session/turn/plan-anchor.js +3 -0
- package/dist/session/turn/plan-card.d.ts +7 -0
- package/dist/session/turn/plan-card.js +7 -0
- package/dist/styles.css +1 -1
- package/dist/terminal/pty-connection.d.ts +5 -4
- package/dist/terminal/session-terminal-connect-bar.js +2 -1
- package/dist/workspace/index.d.ts +1 -0
- package/dist/workspace/index.js +1 -0
- 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 +4 -5
- 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/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 +33 -9
- package/package.json +12 -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
|
@@ -9,7 +9,7 @@ export type PtyCloseAction = 'ended' | 'reconnect' | 'replace';
|
|
|
9
9
|
* terminal asks `GET /kortix/pty`, which passes the same control-plane gate and
|
|
10
10
|
* returns a readable body. That GET never wakes a box.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
type PtyAttachProbe = 'reachable' | 'not-ready' | 'unreachable';
|
|
13
13
|
/** `null` = the PTY list answered. Anything else is the error it threw. */
|
|
14
14
|
export declare function classifyPtyAttachProbe(error: unknown): PtyAttachProbe;
|
|
15
15
|
/**
|
|
@@ -36,10 +36,10 @@ export declare const PTY_WAKE_RETRY_MS = 2000;
|
|
|
36
36
|
export declare const PTY_WAKE_DEADLINE_MS: number;
|
|
37
37
|
/** Consecutive non-wake failures before the terminal stops and offers Retry. */
|
|
38
38
|
export declare const PTY_MAX_ATTACH_FAILURES = 5;
|
|
39
|
-
|
|
39
|
+
type PtyAttachPause = 'asleep' | 'failed';
|
|
40
40
|
/** What the attach overlay shows. `null` means the shell is live. */
|
|
41
41
|
export type AttachPhase = 'connecting' | 'waking' | 'reconnecting' | PtyAttachPause | null;
|
|
42
|
-
|
|
42
|
+
type PtyAttachStep = {
|
|
43
43
|
kind: 'retry';
|
|
44
44
|
delayMs: number;
|
|
45
45
|
phase: 'waking' | 'reconnecting';
|
|
@@ -56,7 +56,7 @@ export declare function nextPtyAttachStep(input: {
|
|
|
56
56
|
/** How long the box has reported not-ready while a wake was armed. */
|
|
57
57
|
wakingForMs: number;
|
|
58
58
|
}): PtyAttachStep;
|
|
59
|
-
|
|
59
|
+
type TerminalPanelState = 'connecting' | 'error' | 'empty' | 'terminal';
|
|
60
60
|
export declare function deriveTerminalPanelState(input: {
|
|
61
61
|
hasServerUrl: boolean;
|
|
62
62
|
serverWaitExpired: boolean;
|
|
@@ -81,3 +81,4 @@ export declare function classifyPtyClose(input: {
|
|
|
81
81
|
}): PtyCloseAction;
|
|
82
82
|
/** Prevent a bad runtime from creating terminals forever in a replacement loop. */
|
|
83
83
|
export declare function shouldAutoReplaceTerminal(replacementAttempt: number): boolean;
|
|
84
|
+
export {};
|
|
@@ -25,7 +25,8 @@ export function SessionTerminalConnectBar({ projectSessionId }) {
|
|
|
25
25
|
export function SessionTerminalConnectSteps({ id, projectSessionId, }) {
|
|
26
26
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
27
27
|
const installCmd = useDeploymentCliInstallCommand(undefined);
|
|
28
|
-
|
|
28
|
+
// A shell through the Kortix PTY: it works on every harness. `sessions connect` attaches OpenCode's own TUI and fails on pi.
|
|
29
|
+
const connectCmd = `kortix sessions shell ${projectSessionId}`;
|
|
29
30
|
return (_jsxs("ol", { id: id, className: "space-y-3 pr-1.5 pb-3 pl-3", children: [_jsx(CommandStep, { label: tI18nComplete.raw('textd236e9cd730b'), command: installCmd }), _jsx(CommandStep, { label: tI18nComplete.raw('text842bad27af42'), command: connectCmd })] }));
|
|
30
31
|
}
|
|
31
32
|
function CommandStep({ label, command }) {
|
|
@@ -23,3 +23,4 @@ export { ProjectSessionsView } from './sessions/project-sessions-view.js';
|
|
|
23
23
|
export { ProjectSessionList } from './sidebar/project-session-list.js';
|
|
24
24
|
export { ProjectSidebar } from './sidebar/project-sidebar.js';
|
|
25
25
|
export { ShareSessionModal } from './sidebar/modal/share-session-modal.js';
|
|
26
|
+
export { formatGoalTime, GoalHealthDot, goalHref, goalNeedsYou, goalOpenAsks, goalProgress, GoalStatusBadge, useGoalClock, useGoalIssueText, } from '../internal/goals/goal-meta.js';
|
package/dist/workspace/index.js
CHANGED
|
@@ -23,3 +23,4 @@ export { ProjectSessionsView } from './sessions/project-sessions-view.js';
|
|
|
23
23
|
export { ProjectSessionList } from './sidebar/project-session-list.js';
|
|
24
24
|
export { ProjectSidebar } from './sidebar/project-sidebar.js';
|
|
25
25
|
export { ShareSessionModal } from './sidebar/modal/share-session-modal.js';
|
|
26
|
+
export { formatGoalTime, GoalHealthDot, goalHref, goalNeedsYou, goalOpenAsks, goalProgress, GoalStatusBadge, useGoalClock, useGoalIssueText, } from '../internal/goals/goal-meta.js';
|
|
@@ -23,12 +23,12 @@ export declare const LINE_WIDTHS: readonly ["w-full", "w-11/12", "w-5/6"];
|
|
|
23
23
|
export declare const LAST_LINE_WIDTHS: readonly ["w-1/3", "w-2/5", "w-1/2", "w-3/5", "w-2/3", "w-3/4"];
|
|
24
24
|
/** Tailwind's `animate-pulse` period, which the `Skeleton` primitive runs. */
|
|
25
25
|
export declare const PULSE_PERIOD_MS = 2000;
|
|
26
|
-
|
|
26
|
+
interface SkeletonLine {
|
|
27
27
|
width: string;
|
|
28
28
|
/** The row's place in the pulse wave; see `pulseDelayMs`. */
|
|
29
29
|
phase: number;
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
type SkeletonParagraph = {
|
|
32
32
|
kind: 'text';
|
|
33
33
|
lines: SkeletonLine[];
|
|
34
34
|
}
|
|
@@ -37,7 +37,7 @@ export type SkeletonParagraph = {
|
|
|
37
37
|
kind: 'block';
|
|
38
38
|
phase: number;
|
|
39
39
|
};
|
|
40
|
-
|
|
40
|
+
interface SkeletonTurn {
|
|
41
41
|
prompt: SkeletonLine & {
|
|
42
42
|
tall: boolean;
|
|
43
43
|
};
|
|
@@ -63,3 +63,4 @@ export declare function savedSessionSkeletonShape(sessionId: string): SkeletonSh
|
|
|
63
63
|
* its cycle, so every row is on screen and moving from the first frame.
|
|
64
64
|
*/
|
|
65
65
|
export declare function pulseDelayMs(phase: number, phases: number): number;
|
|
66
|
+
export {};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { SessionStartStage } from '@kortix/sdk';
|
|
2
|
-
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
export declare function SavedSessionSkeleton({ projectId, sessionId, stage, status, }: {
|
|
3
4
|
projectId: string;
|
|
4
5
|
/** The route's session id. */
|
|
5
6
|
sessionId: string;
|
|
6
7
|
stage: SessionStartStage;
|
|
8
|
+
/** Drawn in place of the skeleton rows: there is no saved conversation to
|
|
9
|
+
* stand in for, only the wake's status. */
|
|
10
|
+
status?: ReactNode;
|
|
7
11
|
}): import("react").JSX.Element;
|
|
@@ -15,8 +15,9 @@ import { useMemo } from 'react';
|
|
|
15
15
|
* The control plane keeps a saved copy of every session's conversation and
|
|
16
16
|
* answers in one round trip; the computer takes 5-240 s to wake. So a session
|
|
17
17
|
* that has a saved copy opens on this — its header, skeleton turns, and the
|
|
18
|
-
* composer's outline — and the conversation replaces it in one step.
|
|
19
|
-
*
|
|
18
|
+
* composer's outline — and the conversation replaces it in one step. A session
|
|
19
|
+
* with nothing to read (`resolveResumeOverlay`) gets the same frame with a
|
|
20
|
+
* `status` in place of the rows.
|
|
20
21
|
*
|
|
21
22
|
* Built from the geometry the real chat uses (`SessionLayout`, `SessionBodyRow`,
|
|
22
23
|
* `SESSION_TRANSCRIPT_CLASS`, `COMPOSER_SHELL_CLASS`), so the crossfade into
|
|
@@ -36,8 +37,8 @@ import { useMemo } from 'react';
|
|
|
36
37
|
* open waits 0.3-2.3 s for the server's copy, so its rows come 200 ms later.
|
|
37
38
|
*/
|
|
38
39
|
const APPEAR_AFTER_A_MOMENT = 'animate-in fade-in fill-mode-both duration-normal delay-[var(--duration-moderate)]';
|
|
39
|
-
export function SavedSessionSkeleton({ projectId, sessionId, stage, }) {
|
|
40
|
+
export function SavedSessionSkeleton({ projectId, sessionId, stage, status, }) {
|
|
40
41
|
const t = useTranslations('sessionPage');
|
|
41
42
|
const shape = useMemo(() => savedSessionSkeletonShape(sessionId), [sessionId]);
|
|
42
|
-
return (_jsx(SessionLayout, { sessionId: sessionId, projectId: projectId, projectSessionId: sessionId, transient: true, bootStage: stage === 'ready' ? null : stage, children: _jsxs("div", { className: "bg-background relative flex h-full flex-col", children: [_jsx(SessionSiteHeader, { sessionId: sessionId, sessionTitle: "" }), _jsxs(SessionBodyRow, { transient: true, children: [_jsx("div", { className: "relative z-10 min-h-0 flex-1 overflow-hidden", children: _jsx("div", { role: "status", "aria-busy": "true", "aria-label": t('loadingConversation'), "data-testid": "saved-session-skeleton", className: cn(SESSION_TRANSCRIPT_CLASS, APPEAR_AFTER_A_MOMENT), children: _jsx(SavedSessionSkeletonRows, { shape: shape }) }) }), _jsx("div", { "aria-hidden": true, className: cn(COMPOSER_SHELL_CLASS, 'pb-8', APPEAR_AFTER_A_MOMENT), children: _jsx(SkeletonBar, { phase: shape.composerPhase, phases: shape.phases, className: "h-28 w-full rounded-xl" }) })] })] }) }));
|
|
43
|
+
return (_jsx(SessionLayout, { sessionId: sessionId, projectId: projectId, projectSessionId: sessionId, transient: true, bootStage: stage === 'ready' ? null : stage, children: _jsxs("div", { className: "bg-background relative flex h-full flex-col", children: [_jsx(SessionSiteHeader, { sessionId: sessionId, sessionTitle: "" }), _jsxs(SessionBodyRow, { transient: true, children: [_jsx("div", { className: "relative z-10 min-h-0 flex-1 overflow-hidden", children: status ?? (_jsx("div", { role: "status", "aria-busy": "true", "aria-label": t('loadingConversation'), "data-testid": "saved-session-skeleton", className: cn(SESSION_TRANSCRIPT_CLASS, APPEAR_AFTER_A_MOMENT), children: _jsx(SavedSessionSkeletonRows, { shape: shape }) })) }), _jsx("div", { "aria-hidden": true, className: cn(COMPOSER_SHELL_CLASS, 'pb-8', APPEAR_AFTER_A_MOMENT), children: _jsx(SkeletonBar, { phase: shape.composerPhase, phases: shape.phases, className: "h-28 w-full rounded-xl" }) })] })] }) }));
|
|
43
44
|
}
|
|
@@ -106,10 +106,9 @@ function useSessionPanelSurfaces({ sessionId, projectId, projectSessionId, bootS
|
|
|
106
106
|
setIsSidePanelOpen(!isSidePanelOpen);
|
|
107
107
|
}, [isSidePanelOpen, setIsSidePanelOpen]);
|
|
108
108
|
const panelHeader = (_jsx(PanelHeaderSwitcher, { view: effectiveView, onChangeView: (v) => setPanelView(sessionId, v), isSidePanelOpen: isSidePanelOpen, onTogglePanel: handleTogglePanel, auditBadge: auditPendingCount, onToggleMode: togglePanelMode }));
|
|
109
|
-
// While booting, the panel is JUST the
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
// centered. The runtime-coupled views (Actions/Files/Terminal/Browser) need a
|
|
109
|
+
// While booting, the panel is JUST the wake status — empty unless the wake
|
|
110
|
+
// needs attention, and then dead-center in the card — with no header bar.
|
|
111
|
+
// The runtime-coupled views (Actions/Files/Terminal/Browser) need a
|
|
113
112
|
// live sandbox, so they only render once booted.
|
|
114
113
|
//
|
|
115
114
|
// Easy mode has no header either: it is the three cards and nothing else. No
|
|
@@ -120,7 +119,7 @@ function useSessionPanelSurfaces({ sessionId, projectId, projectSessionId, bootS
|
|
|
120
119
|
// own close DISCARDS the detail (it is done with), while ⌘I only puts it
|
|
121
120
|
// away — press it again and the same detail comes back.
|
|
122
121
|
const effectivePanelHeader = booting || isEasy ? null : panelHeader;
|
|
123
|
-
const effectivePanelBody = booting ? (_jsx(SessionStartingLoader, {
|
|
122
|
+
const effectivePanelBody = booting ? (_jsx(SessionStartingLoader, { projectId: projectId, sessionId: projectSessionId })) : (panelBody);
|
|
124
123
|
return { effectivePanelHeader, effectivePanelBody };
|
|
125
124
|
}
|
|
126
125
|
function useSessionPanelBody({ sessionId, projectId, projectSessionId, isEasy, effectiveView, }) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The Goals row shows only with the `goals` flag on. */
|
|
2
|
+
export declare function useGoalsRowVisible(projectId: string | undefined): boolean;
|
|
3
|
+
/** The Goals row, with the count of goals that wait on a person or need attention. */
|
|
4
|
+
export declare function ProjectGoalsNavItem({ projectId }: {
|
|
5
|
+
projectId: string;
|
|
6
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { uiRoutes } from '../../../lib/routes.js';
|
|
4
|
+
import { ProjectNavRow } from '../../../internal/layout/project-nav-row.js';
|
|
5
|
+
import { CountBadge } from '../../../primitives/count-badge.js';
|
|
6
|
+
import { useTranslations } from '../../../i18n/index.js';
|
|
7
|
+
import { useFeatureFlag, useProjectGoals } from '@kortix/sdk/react';
|
|
8
|
+
import { TargetIcon } from '@phosphor-icons/react';
|
|
9
|
+
import { useUiPathname } from '../../../providers/ui-provider.js';
|
|
10
|
+
import { goalNeedsYou } from '../../../internal/goals/goal-meta.js';
|
|
11
|
+
/** The Goals row shows only with the `goals` flag on. */
|
|
12
|
+
export function useGoalsRowVisible(projectId) {
|
|
13
|
+
return useFeatureFlag(projectId, 'goals').enabled;
|
|
14
|
+
}
|
|
15
|
+
/** The Goals row, with the count of goals that wait on a person or need attention. */
|
|
16
|
+
export function ProjectGoalsNavItem({ projectId }) {
|
|
17
|
+
const t = useTranslations('sidebar');
|
|
18
|
+
const pathname = useUiPathname();
|
|
19
|
+
const visible = useGoalsRowVisible(projectId);
|
|
20
|
+
const goals = useProjectGoals(visible ? projectId : null);
|
|
21
|
+
const needsYou = (goals.data ?? []).filter(goalNeedsYou).length;
|
|
22
|
+
const href = projectId ? uiRoutes().goals(projectId) : null;
|
|
23
|
+
if (!href || !visible)
|
|
24
|
+
return null;
|
|
25
|
+
return (_jsxs(ProjectNavRow, { active: pathname?.startsWith(href) === true, label: t('goals'), href: href, prefetch: true, children: [_jsx("span", { className: "shrink-0", children: _jsx(TargetIcon, {}) }), t('goals'), needsYou > 0 ? (_jsx("span", { className: "ml-auto flex shrink-0 items-center gap-1", children: _jsx(CountBadge, { count: needsYou, tone: "waiting" }) })) : null] }));
|
|
26
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The iframe snippet a host page pastes: the ACTUAL session page
|
|
3
|
+
* (`/projects/:projectId/sessions/:sessionId`), not a separate widget or
|
|
4
|
+
* viewer. Pure, so the test can pin the exact HTML without a browser.
|
|
5
|
+
*
|
|
6
|
+
* The snippet is static HTML by construction: the URL is built from ids, and
|
|
7
|
+
* the `title` attribute is a fixed string. Never interpolate a session title
|
|
8
|
+
* into the attribute — an unescaped quote would break out of it on the HOST
|
|
9
|
+
* page, which does not share this app's CSP.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sessionEmbedSnippet(projectId: string, sessionId: string, origin: string): string | null;
|
|
12
|
+
/**
|
|
13
|
+
* "Embed" in the Share dialog: one copy button for the iframe snippet above.
|
|
14
|
+
* Framing the session page is allowed by the CSP `frame-ancestors` rule on
|
|
15
|
+
* that route (next.config.ts); the viewer inside the frame is the app's own
|
|
16
|
+
* auth. Same-site embeds require an existing signed-in session; cross-site
|
|
17
|
+
* embeds cannot sign in inside the frame (SameSite=Lax and /auth's CSP).
|
|
18
|
+
*
|
|
19
|
+
* Mounted inside `ModalContent`, so `window` is read at render time of an open
|
|
20
|
+
* dialog, never during SSR.
|
|
21
|
+
*/
|
|
22
|
+
export declare function SessionEmbedSection({ projectId, sessionId, }: {
|
|
23
|
+
projectId: string;
|
|
24
|
+
sessionId: string;
|
|
25
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Button } from '../../../primitives/button.js';
|
|
4
|
+
import { Label } from '../../../primitives/label.js';
|
|
5
|
+
import { errorToast, successToast } from '../../../primitives/toast.js';
|
|
6
|
+
import { useTranslations } from '../../../i18n/index.js';
|
|
7
|
+
import { CheckIcon, CodeIcon } from '@phosphor-icons/react';
|
|
8
|
+
import { useEffect, useRef, useState } from 'react';
|
|
9
|
+
/**
|
|
10
|
+
* The iframe snippet a host page pastes: the ACTUAL session page
|
|
11
|
+
* (`/projects/:projectId/sessions/:sessionId`), not a separate widget or
|
|
12
|
+
* viewer. Pure, so the test can pin the exact HTML without a browser.
|
|
13
|
+
*
|
|
14
|
+
* The snippet is static HTML by construction: the URL is built from ids, and
|
|
15
|
+
* the `title` attribute is a fixed string. Never interpolate a session title
|
|
16
|
+
* into the attribute — an unescaped quote would break out of it on the HOST
|
|
17
|
+
* page, which does not share this app's CSP.
|
|
18
|
+
*/
|
|
19
|
+
export function sessionEmbedSnippet(projectId, sessionId, origin) {
|
|
20
|
+
if (!projectId || !sessionId || !origin)
|
|
21
|
+
return null;
|
|
22
|
+
const src = `${origin}/projects/${encodeURIComponent(projectId)}/sessions/${encodeURIComponent(sessionId)}`;
|
|
23
|
+
return `<iframe src="${src}" style="height:640px;width:100%;border:0;border-radius:12px" title="Kortix session" loading="lazy"></iframe>`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* "Embed" in the Share dialog: one copy button for the iframe snippet above.
|
|
27
|
+
* Framing the session page is allowed by the CSP `frame-ancestors` rule on
|
|
28
|
+
* that route (next.config.ts); the viewer inside the frame is the app's own
|
|
29
|
+
* auth. Same-site embeds require an existing signed-in session; cross-site
|
|
30
|
+
* embeds cannot sign in inside the frame (SameSite=Lax and /auth's CSP).
|
|
31
|
+
*
|
|
32
|
+
* Mounted inside `ModalContent`, so `window` is read at render time of an open
|
|
33
|
+
* dialog, never during SSR.
|
|
34
|
+
*/
|
|
35
|
+
export function SessionEmbedSection({ projectId, sessionId, }) {
|
|
36
|
+
const t = useTranslations('hardcodedUi.sessionEmbed');
|
|
37
|
+
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
38
|
+
const [copied, setCopied] = useState(false);
|
|
39
|
+
const copiedTimer = useRef(null);
|
|
40
|
+
const mounted = useRef(false);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
mounted.current = true;
|
|
43
|
+
return () => {
|
|
44
|
+
mounted.current = false;
|
|
45
|
+
if (copiedTimer.current)
|
|
46
|
+
clearTimeout(copiedTimer.current);
|
|
47
|
+
};
|
|
48
|
+
}, []);
|
|
49
|
+
const origin = typeof window === 'undefined' ? null : window.location.origin;
|
|
50
|
+
const snippet = origin ? sessionEmbedSnippet(projectId, sessionId, origin) : null;
|
|
51
|
+
const copy = async () => {
|
|
52
|
+
if (!snippet)
|
|
53
|
+
return;
|
|
54
|
+
try {
|
|
55
|
+
await navigator.clipboard.writeText(snippet);
|
|
56
|
+
if (!mounted.current)
|
|
57
|
+
return;
|
|
58
|
+
setCopied(true);
|
|
59
|
+
successToast(t('copied'));
|
|
60
|
+
if (copiedTimer.current)
|
|
61
|
+
clearTimeout(copiedTimer.current);
|
|
62
|
+
copiedTimer.current = setTimeout(() => setCopied(false), 2000);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
if (!mounted.current)
|
|
66
|
+
return;
|
|
67
|
+
errorToast(tI18nComplete.raw('text167c96824acb'));
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
return (_jsxs("section", { className: "space-y-2", "data-testid": "session-embed", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: t('sectionTitle') }), _jsx("p", { className: "text-muted-foreground text-xs", children: t('sectionDescription') })] }), _jsxs(Button, { variant: "outline", size: "sm", disabled: !snippet, onClick: copy, children: [copied ? (_jsx(CheckIcon, { className: "text-kortix-green size-4 shrink-0" })) : (_jsx(CodeIcon, { className: "size-4 shrink-0" })), t('copy')] })] }));
|
|
71
|
+
}
|
|
@@ -13,7 +13,7 @@ import { sessionOversightQueryKey } from '../../../access/iam/session-oversight-
|
|
|
13
13
|
import { ShieldCheckIcon } from '@phosphor-icons/react';
|
|
14
14
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
15
15
|
import { useEffect, useState } from 'react';
|
|
16
|
-
import {
|
|
16
|
+
import { SessionEmbedSection } from './session-embed-section.js';
|
|
17
17
|
import { sessionAccessSummary, sessionAccessView } from './share-session-access.js';
|
|
18
18
|
import { refreshAfterShare } from './share-session-cache.js';
|
|
19
19
|
/**
|
|
@@ -105,5 +105,5 @@ export function ShareSessionModal({ projectId, session, open, onOpenChange, onSa
|
|
|
105
105
|
// Read-only, not hidden: a person a session was shared with should
|
|
106
106
|
// still be able to see who else is in it. Withholding that is what
|
|
107
107
|
// sent people to the editable dialog in the first place.
|
|
108
|
-
_jsx("p", { className: "text-foreground text-sm", "data-testid": "session-access-summary", children: session ? sessionAccessSummary(session) : null })), oversightOn ? (_jsx(InfoBanner, { tone: "neutral", icon: ShieldCheckIcon, "data-testid": "session-oversight-disclosure", children: tOversight.raw('shareDisclosure') })) : null, view.canEdit && session && publicSharesEnabled ? (_jsx(
|
|
108
|
+
_jsx("p", { className: "text-foreground text-sm", "data-testid": "session-access-summary", children: session ? sessionAccessSummary(session) : null })), oversightOn ? (_jsx(InfoBanner, { tone: "neutral", icon: ShieldCheckIcon, "data-testid": "session-oversight-disclosure", children: tOversight.raw('shareDisclosure') })) : null, view.canEdit && session && publicSharesEnabled ? (_jsx(SessionEmbedSection, { projectId: projectId, sessionId: session.session_id })) : null] }), _jsxs(ModalFooter, { className: "sm:justify-between", children: [_jsx(Button, { variant: "outline-ghost", size: "sm", className: "w-full sm:w-auto", onClick: () => onOpenChange(false), disabled: save.isPending, children: view.canEdit ? 'Cancel' : 'Close' }), view.canEdit && (_jsxs(Button, { size: "sm", onClick: () => save.mutate(), disabled: save.isPending || !isSharingComplete(sharing) || blockedSelection, className: "w-full sm:w-auto", children: [save.isPending && _jsx(Loading, {}), tI18nHardcoded.raw('i18nComplete.text1509f561f241')] }))] })] }) }));
|
|
109
109
|
}
|
|
@@ -3,11 +3,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { uiRoutes } from '../../lib/routes.js';
|
|
4
4
|
import { capabilityTabHref } from '../../internal/lib/capability-tab-routes.js';
|
|
5
5
|
import { useAppsRowVisible } from './footer/project-apps-nav.js';
|
|
6
|
+
import { useGoalsRowVisible } from './footer/project-goals-nav.js';
|
|
6
7
|
import { useRoomsRowVisible } from './footer/project-rooms-nav.js';
|
|
7
8
|
import { filesRow } from '../../internal/layout/files-nav-label.js';
|
|
8
9
|
import { CustomizeGlyph, useProjectNavRows, } from '../../internal/layout/project-settings-nav.js';
|
|
9
10
|
import { useTranslations } from '../../i18n/index.js';
|
|
10
|
-
import { AppWindowIcon, ChatsTeardropIcon, FoldersIcon, HardDrivesIcon } from '@phosphor-icons/react';
|
|
11
|
+
import { AppWindowIcon, ChatsTeardropIcon, FoldersIcon, HardDrivesIcon, TargetIcon } from '@phosphor-icons/react';
|
|
11
12
|
import { useMemo } from 'react';
|
|
12
13
|
/**
|
|
13
14
|
* Label, glyph, destination and availability of every customizable nav row,
|
|
@@ -21,12 +22,14 @@ export function useNavRowMeta(projectId) {
|
|
|
21
22
|
const { customize: customizeTab, files: filesTarget } = useProjectNavRows(projectId);
|
|
22
23
|
const appsVisible = useAppsRowVisible(projectId);
|
|
23
24
|
const roomsVisible = useRoomsRowVisible(projectId);
|
|
25
|
+
const goalsVisible = useGoalsRowVisible(projectId);
|
|
24
26
|
// Memoized on the gates, not rebuilt per render: the Customize sidebar
|
|
25
27
|
// modal memoizes its rows on this object, and a fresh one per render would
|
|
26
28
|
// re-render every row on every drag frame.
|
|
27
29
|
return useMemo(() => {
|
|
28
30
|
const appsHref = uiRoutes().apps(projectId);
|
|
29
31
|
const roomsHref = uiRoutes().rooms(projectId);
|
|
32
|
+
const goalsHref = uiRoutes().goals(projectId);
|
|
30
33
|
const files = filesTarget ? filesRow(projectId, filesTarget) : null;
|
|
31
34
|
return {
|
|
32
35
|
...(customizeTab
|
|
@@ -56,6 +59,15 @@ export function useNavRowMeta(projectId) {
|
|
|
56
59
|
},
|
|
57
60
|
}
|
|
58
61
|
: {}),
|
|
62
|
+
...(goalsVisible && goalsHref
|
|
63
|
+
? {
|
|
64
|
+
goals: {
|
|
65
|
+
label: t('goals'),
|
|
66
|
+
icon: _jsx(TargetIcon, {}),
|
|
67
|
+
href: goalsHref,
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
: {}),
|
|
59
71
|
...(files
|
|
60
72
|
? {
|
|
61
73
|
files: {
|
|
@@ -66,5 +78,5 @@ export function useNavRowMeta(projectId) {
|
|
|
66
78
|
}
|
|
67
79
|
: {}),
|
|
68
80
|
};
|
|
69
|
-
}, [t, tI18nComplete, projectId, customizeTab, appsVisible, roomsVisible, filesTarget]);
|
|
81
|
+
}, [t, tI18nComplete, projectId, customizeTab, appsVisible, roomsVisible, goalsVisible, filesTarget]);
|
|
70
82
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { ProjectAppsNavItem } from './footer/project-apps-nav.js';
|
|
4
|
+
import { ProjectGoalsNavItem } from './footer/project-goals-nav.js';
|
|
4
5
|
import { ProjectRoomsNavItem } from './footer/project-rooms-nav.js';
|
|
5
6
|
import { ProjectFilesNavItem } from './footer/project-files-nav.js';
|
|
6
7
|
import { ProjectCustomizeNavItem } from '../../internal/layout/project-settings-nav.js';
|
|
@@ -95,5 +96,7 @@ function NavItem({ entry, projectId }) {
|
|
|
95
96
|
return _jsx(ProjectFilesNavItem, { projectId: projectId });
|
|
96
97
|
case 'rooms':
|
|
97
98
|
return _jsx(ProjectRoomsNavItem, { projectId: projectId });
|
|
99
|
+
case 'goals':
|
|
100
|
+
return _jsx(ProjectGoalsNavItem, { projectId: projectId });
|
|
98
101
|
}
|
|
99
102
|
}
|
|
@@ -39,8 +39,8 @@ import { selectExpandedIds, useSessionExpandedStore } from '../../internal/store
|
|
|
39
39
|
import { selectCollapsedSections, selectGroupMode, selectHiddenSections, selectLabelFilters, selectOrderMode, selectSourceFilters, selectStatusFilters, useSessionFilterStore, } from '../../internal/stores/session-filter-store.js';
|
|
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
|
-
import { qk, useProjectSession, useProjectSessions, useRuntimeSupports, useSessionChildren, } from '@kortix/sdk/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';
|
|
42
|
+
import { qk, useProjectSession, useProjectSessions, useRuntimeSupports, useSessionChildren, useFeatureFlag, useSessionGoal, } from '@kortix/sdk/react';
|
|
43
|
+
import { ArchiveIcon, ArrowsSplitIcon, CaretRightIcon, DotsThreeIcon, PushPinIcon, PushPinSlashIcon, RobotIcon, GitForkIcon, PencilSimpleIcon, ArrowCounterClockwiseIcon as RotateCcw, ShareIcon as Share, SparkleIcon, TargetIcon, 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';
|
|
@@ -549,7 +549,7 @@ function ProjectSessionRow({ session, href, isActive, isSwitching, onNavigate, d
|
|
|
549
549
|
// same `reviewCount` prop, so they agree by construction rather than by luck.
|
|
550
550
|
const displayStatus = sessionDisplayStatus(session, reviewCount);
|
|
551
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 })] }))] }));
|
|
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 })) }), _jsx(SessionGoalMark, { projectId: session.project_id, sessionId: session.session_id }), 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
|
|
@@ -602,6 +602,15 @@ function ProjectSubsessionRow({ id, title, href, isActive, createdAt, updatedAt,
|
|
|
602
602
|
: '';
|
|
603
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 })] }) }) }));
|
|
604
604
|
}
|
|
605
|
+
/** A target beside a session that runs a goal (flag `goals`); the hint names the goal. */
|
|
606
|
+
function SessionGoalMark({ projectId, sessionId }) {
|
|
607
|
+
const tGoals = useTranslations('goals');
|
|
608
|
+
const flag = useFeatureFlag(projectId, 'goals');
|
|
609
|
+
const { goal, role } = useSessionGoal(flag.enabled ? projectId : null, sessionId);
|
|
610
|
+
if (!goal || role !== 'coordinator')
|
|
611
|
+
return null;
|
|
612
|
+
return (_jsx(Hint, { side: "top", label: `${tGoals('sessionChip')}: ${goal.goal?.objective ?? goal.slug}`, children: _jsx("span", { className: "text-muted-foreground flex size-4 shrink-0 items-center justify-center", "data-testid": "session-goal-mark", children: _jsx(TargetIcon, { className: "size-3.5" }) }) }));
|
|
613
|
+
}
|
|
605
614
|
function SessionStatusDot({ session, reviewCount = 0, waitingOn, changeRequests, }) {
|
|
606
615
|
const display = sessionDisplayStatus(session, reviewCount);
|
|
607
616
|
return (_jsx("div", { className: "flex size-4 shrink-0 items-center justify-center", children: _jsx(SessionStatusMark, { status: display, waitingOn: waitingOn, overdue: isReviewOverdue(changeRequests) }) }));
|
|
@@ -11,7 +11,7 @@ import { menuRow } from '../../primitives/menu-recipe.js';
|
|
|
11
11
|
import { CR_ID_PREFIX } from '../review/review-actions.js';
|
|
12
12
|
import { reviewHref } from '../../internal/lib/capability-tab-routes.js';
|
|
13
13
|
import { useTranslations } from '../../i18n/index.js';
|
|
14
|
-
import { useSessionHoverStore } from '../../internal/stores/session-hover-store.js';
|
|
14
|
+
import { sessionRowHoverHandlers, useSessionHoverStore } from '../../internal/stores/session-hover-store.js';
|
|
15
15
|
import { ArrowsSplitIcon, GitDiffIcon, RobotIcon } from '@phosphor-icons/react';
|
|
16
16
|
import { formatDistanceToNowStrict } from 'date-fns';
|
|
17
17
|
import { Slot } from 'radix-ui';
|
|
@@ -150,6 +150,33 @@ export function SessionBriefCardHost() {
|
|
|
150
150
|
el.style.transform = `translate(${rect.right}px, ${top}px)`;
|
|
151
151
|
shown.current = true;
|
|
152
152
|
}, [visible, activeSessionId, anchor, via]);
|
|
153
|
+
// Native listeners, not React's `onPointerEnter/Leave`. The card's content is
|
|
154
|
+
// portaled in from the row, so in React's tree it is the row's child, not
|
|
155
|
+
// this wrapper's: React fired the wrapper's leave the moment the pointer
|
|
156
|
+
// crossed from the gap onto the content, and the card closed under a pointer
|
|
157
|
+
// on its way to the Review link. The DOM tree is the one the pointer is in.
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
const el = wrapperRef.current;
|
|
160
|
+
if (!el)
|
|
161
|
+
return;
|
|
162
|
+
const active = () => useSessionHoverStore.getState().activeSessionId;
|
|
163
|
+
const onEnter = () => {
|
|
164
|
+
const id = active();
|
|
165
|
+
if (id)
|
|
166
|
+
openSession(id);
|
|
167
|
+
};
|
|
168
|
+
const onLeave = () => {
|
|
169
|
+
const id = active();
|
|
170
|
+
if (id)
|
|
171
|
+
closeSession(id);
|
|
172
|
+
};
|
|
173
|
+
el.addEventListener('pointerenter', onEnter);
|
|
174
|
+
el.addEventListener('pointerleave', onLeave);
|
|
175
|
+
return () => {
|
|
176
|
+
el.removeEventListener('pointerenter', onEnter);
|
|
177
|
+
el.removeEventListener('pointerleave', onLeave);
|
|
178
|
+
};
|
|
179
|
+
}, [openSession, closeSession]);
|
|
153
180
|
// WCAG 1.4.13: dismissible without moving the pointer. A scroll moves the
|
|
154
181
|
// row away from the card, so it closes too, unless it scrolls the card itself.
|
|
155
182
|
useEffect(() => {
|
|
@@ -169,7 +196,7 @@ export function SessionBriefCardHost() {
|
|
|
169
196
|
window.removeEventListener('resize', dismiss);
|
|
170
197
|
};
|
|
171
198
|
}, [visible, dismiss]);
|
|
172
|
-
return createPortal(_jsx("div", { ref: wrapperRef, "data-slot": "session-brief-card", className: cn('fixed top-0 left-0 pl-3.5 starting:opacity-0', !visible && 'hidden'), style: { zIndex: floatingZ(depth) },
|
|
199
|
+
return createPortal(_jsx("div", { ref: wrapperRef, "data-slot": "session-brief-card", className: cn('fixed top-0 left-0 pl-3.5 starting:opacity-0', !visible && 'hidden'), style: { zIndex: floatingZ(depth) }, children: _jsx("div", { ref: setSlot, className: cn(FLOATING_PANEL_SURFACE, 'w-72 overflow-hidden rounded-md shadow-xs') }) }), container ?? document.body);
|
|
173
200
|
}
|
|
174
201
|
/**
|
|
175
202
|
* A row's trigger plus its brief, rendered into the shared card while the row
|
|
@@ -180,20 +207,17 @@ export function SessionBriefCardHost() {
|
|
|
180
207
|
function SessionBriefCard({ id, children, content, }) {
|
|
181
208
|
const open = useSessionHoverStore((state) => state.activeSessionId === id);
|
|
182
209
|
const slot = useSessionHoverStore((state) => state.slot);
|
|
183
|
-
const openSession = useSessionHoverStore((state) => state.openSession);
|
|
184
|
-
const closeSession = useSessionHoverStore((state) => state.closeSession);
|
|
185
210
|
// A row can be filtered out, or the sidebar unmounted, while its card shows.
|
|
186
211
|
// Without this the store keeps naming a row that no longer exists and the
|
|
187
|
-
// group is still warm the next time the list mounts.
|
|
212
|
+
// group is still warm the next time the list mounts. A row unmounted under
|
|
213
|
+
// the pointer gets no pointerleave, so its click's suppression ends here too.
|
|
188
214
|
useEffect(() => () => {
|
|
189
215
|
const store = useSessionHoverStore.getState();
|
|
190
216
|
if (store.activeSessionId === id)
|
|
191
217
|
store.dismiss();
|
|
218
|
+
store.release(id);
|
|
192
219
|
}, [id]);
|
|
193
|
-
return (_jsxs(_Fragment, { children: [_jsx(Slot.Root, { "data-state": open ? 'open' : 'closed',
|
|
194
|
-
if (event.pointerType !== 'touch')
|
|
195
|
-
openSession(id, event.currentTarget, 'pointer');
|
|
196
|
-
}, onPointerLeave: () => closeSession(id), onFocus: (event) => openSession(id, event.currentTarget, 'focus'), onBlur: () => closeSession(id), children: children }), open && slot ? createPortal(content, slot) : null] }));
|
|
220
|
+
return (_jsxs(_Fragment, { children: [_jsx(Slot.Root, { "data-state": open ? 'open' : 'closed', ...sessionRowHoverHandlers(id), children: children }), open && slot ? createPortal(content, slot) : null] }));
|
|
197
221
|
}
|
|
198
222
|
export function SessionBriefHoverCard({ sessionId, children, ...brief }) {
|
|
199
223
|
const dismiss = useSessionHoverStore((state) => state.dismiss);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kortix/ui",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.8",
|
|
4
4
|
"description": "Kortix UI: the shared React component library for apps/web, Kortix Apps and SDK hosts. Tailwind v4 classes, themed by CSS variables.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Elastic-2.0",
|
|
@@ -911,6 +911,10 @@
|
|
|
911
911
|
"types": "./dist/internal/capture/use-capture-area.d.ts",
|
|
912
912
|
"import": "./dist/internal/capture/use-capture-area.js"
|
|
913
913
|
},
|
|
914
|
+
"./internal/components/agent-mark": {
|
|
915
|
+
"types": "./dist/internal/components/agent-mark.d.ts",
|
|
916
|
+
"import": "./dist/internal/components/agent-mark.js"
|
|
917
|
+
},
|
|
914
918
|
"./internal/components/hover-prefetch-link": {
|
|
915
919
|
"types": "./dist/internal/components/hover-prefetch-link.d.ts",
|
|
916
920
|
"import": "./dist/internal/components/hover-prefetch-link.js"
|
|
@@ -1463,6 +1467,10 @@
|
|
|
1463
1467
|
"types": "./dist/markdown/setup-links/api-base.d.ts",
|
|
1464
1468
|
"import": "./dist/markdown/setup-links/api-base.js"
|
|
1465
1469
|
},
|
|
1470
|
+
"./markdown/setup-links/app-credentials-form": {
|
|
1471
|
+
"types": "./dist/markdown/setup-links/app-credentials-form.d.ts",
|
|
1472
|
+
"import": "./dist/markdown/setup-links/app-credentials-form.js"
|
|
1473
|
+
},
|
|
1466
1474
|
"./markdown/setup-links/approval-decision": {
|
|
1467
1475
|
"types": "./dist/markdown/setup-links/approval-decision.d.ts",
|
|
1468
1476
|
"import": "./dist/markdown/setup-links/approval-decision.js"
|
|
@@ -2157,7 +2165,7 @@
|
|
|
2157
2165
|
"@extend-ai/react-docx": "^0.7.5",
|
|
2158
2166
|
"@extend-ai/react-xlsx": "0.13.4",
|
|
2159
2167
|
"@glideapps/glide-data-grid": "6.0.4-alpha24",
|
|
2160
|
-
"@kortix/llm-catalog": "0.14.
|
|
2168
|
+
"@kortix/llm-catalog": "0.14.8",
|
|
2161
2169
|
"@lezer/highlight": "1.2.3",
|
|
2162
2170
|
"@paper-design/shaders-react": "^0.0.76",
|
|
2163
2171
|
"@phosphor-icons/react": "2.1.10",
|
|
@@ -2214,7 +2222,7 @@
|
|
|
2214
2222
|
"heic2any": "0.0.4",
|
|
2215
2223
|
"i18next": "^26.3.6",
|
|
2216
2224
|
"jszip": "^3.10.1",
|
|
2217
|
-
"katex": "^0.
|
|
2225
|
+
"katex": "^0.18.7",
|
|
2218
2226
|
"maplibre-gl": "6.12.0",
|
|
2219
2227
|
"mermaid": "^11.12.0",
|
|
2220
2228
|
"motion": "^12.40.0",
|
|
@@ -2246,7 +2254,7 @@
|
|
|
2246
2254
|
"zustand": "^5.0.3"
|
|
2247
2255
|
},
|
|
2248
2256
|
"peerDependencies": {
|
|
2249
|
-
"@kortix/sdk": "^0.14.
|
|
2257
|
+
"@kortix/sdk": "^0.14.8",
|
|
2250
2258
|
"@tanstack/react-query": "^5.75.2",
|
|
2251
2259
|
"@types/react": ">=18",
|
|
2252
2260
|
"react": ">=19",
|
|
@@ -373,6 +373,8 @@ export function Sessions({ projectId }: { projectId: string }) {
|
|
|
373
373
|
}
|
|
374
374
|
```
|
|
375
375
|
|
|
376
|
+
Goals (behind the `goals` flag): `GoalStatusBadge` (a goal's status), `GoalHealthDot` (green running, orange needs attention, grey closed), `useGoalIssueText` (one sentence per health issue), `goalProgress` (met and total criteria), `goalOpenAsks` (asks waiting on a person), `goalNeedsYou` (an open ask or a health issue), `formatGoalTime` (the reminders time format), `goalHref` (the goal's page, or null), `useGoalClock` (the current time, re-read every 30 s).
|
|
377
|
+
|
|
376
378
|
Names: `ProjectPageHeader`, `useCapabilityAccess`, `useProjectNavRows`, `SidebarToggle`, `activeCapabilityTab`, `agentHref`, `capabilityTabHref`, `channelsHref`, `remindersHref`, `reviewHref`, `detectManifestVersion`, `manifestFilename`, `manifestNeedsUpgrade`, `useProjectManifestVersion`, `ManifestVersion`, `CUSTOMIZE_SECTION_ACCESS`, `CUSTOMIZE_SECTION_GATE_ACTIONS`, `PROJECT_ACTIONS`, `isCustomizeSectionVisible`, `CustomizeSection`, `ProjectAction`, `usePermission`, `usePermissions`, `usePermissionsFor`, `PROJECT_PAGE_ACTIONS`, `useProjectCan`, `useProjectCans`, `useProjectPageCans`, `CanResult`, `formatFireTime`, `nearestFire`, `reminderTitle`, `scheduleLabel`, `ProjectHomeWallpaper`, `ProjectHomeWelcomeBody`, `InstantSessionShell`, `ProjectFilesView`, `ProjectHome`, `ProjectHomeSendOptions`, `useProjectHomeSend`, `UseProjectHomeSendOptions`, `SavedSessionSkeleton`, `SessionLayout`, `SessionWorkspace`, `SessionLayoutProps`, `SessionWorkspaceProps`, `ProjectSessionsView`, `ProjectSessionList`, `ProjectSidebar`, `ShareSessionModal`
|
|
377
379
|
|
|
378
380
|
## `@kortix/ui/access`
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* How long a freshly mounted session may say NOTHING about its runtime.
|
|
3
|
-
*
|
|
4
|
-
* Long enough to cover the first health probe + `GET .../turn` round trip on a
|
|
5
|
-
* healthy session (both answer well inside a second locally and on dev), short
|
|
6
|
-
* enough that a genuinely parked box still announces its wake promptly. The
|
|
7
|
-
* cost of being wrong in one direction is a false "your session dropped" flash
|
|
8
|
-
* on every page load; in the other, a real wake notice arrives ~1.5s later.
|
|
9
|
-
*/
|
|
10
|
-
export declare const READINESS_SETTLE_MS = 1500;
|
|
11
|
-
/**
|
|
12
|
-
* True while the runtime has not been contacted yet AND the mount is young.
|
|
13
|
-
*
|
|
14
|
-
* Feeds `sessionComposerReadiness({ settling })`, whose fallback would
|
|
15
|
-
* otherwise assert "Waking this session up…" the instant a page loads — the
|
|
16
|
-
* one claim nothing had checked. See that input's docstring.
|
|
17
|
-
*/
|
|
18
|
-
export declare function useReadinessSettling(uncontacted: boolean, windowMs?: number): boolean;
|