@kortix/ui 0.14.6 → 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/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 +68 -70
- 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 +57 -22
- package/dist/customize/agents/agent-people-section.js +18 -17
- package/dist/customize/agents/agent-triggers-section.js +6 -5
- 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/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 +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/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 +345 -12
- package/dist/i18n/en.js +350 -17
- 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/query-client-singleton.d.ts +9 -3
- package/dist/internal/lib/query-client-singleton.js +18 -12
- 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/primitives/settings-section-header.d.ts +1 -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-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 +3 -6
- package/dist/session/composer/context-usage.js +3 -6
- 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 +2 -2
- 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/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/session-chat.js +3 -10
- package/dist/session/thread/session-history-scroll.d.ts +2 -1
- package/dist/session/thread/session-thread.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/terminal/pty-connection.d.ts +5 -4
- 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 +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
|
@@ -16,13 +16,13 @@ import { isShareLive, publicShareUrl, shareListState, useRevokePublicShare, useS
|
|
|
16
16
|
import { cn } from '../../lib/utils.js';
|
|
17
17
|
function ShareRow({ share, onRevoke, isRevoking, }) {
|
|
18
18
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
19
|
-
const tTranscript = useTranslations('hardcodedUi.publicTranscriptShare');
|
|
20
19
|
const [copied, setCopied] = useState(false);
|
|
21
20
|
const live = isShareLive(share);
|
|
22
21
|
// Read at render time: `publicShareUrl` is null outside a browser (SSR).
|
|
23
22
|
const url = publicShareUrl(share.public_path);
|
|
24
23
|
const isFile = share.resource_type === 'file';
|
|
25
|
-
// A transcript share
|
|
24
|
+
// A legacy transcript share (revoked; the feature is removed) still carries
|
|
25
|
+
// no port and no file: its label is the row's own meta.
|
|
26
26
|
const isTranscript = share.resource_type === 'transcript';
|
|
27
27
|
const Icon = isFile ? FileText : isTranscript ? ChatText : Globe;
|
|
28
28
|
const copy = async () => {
|
|
@@ -33,11 +33,7 @@ function ShareRow({ share, onRevoke, isRevoking, }) {
|
|
|
33
33
|
successToast(tI18nComplete.raw('textd0f24de8dbc6'));
|
|
34
34
|
setTimeout(() => setCopied(false), 2000);
|
|
35
35
|
};
|
|
36
|
-
return (_jsxs("li", { className: cn('group bg-popover flex items-center gap-3 rounded-md border px-4 py-2.5 transition-colors', !live && 'opacity-60'), children: [_jsx("span", { className: cn('flex size-9 shrink-0 items-center justify-center rounded-sm', live ? 'bg-kortix-green/15' : 'bg-muted'), children: _jsx(Icon, { className: cn('size-5', live ? 'text-kortix-green' : 'text-muted-foreground') }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "truncate text-sm font-medium", children: share.label }), !live && (_jsx(Badge, { variant: "outline", size: "xs", children: share.revoked_at ? 'Revoked' : 'Expired' }))] }), _jsx("p", { className: "text-muted-foreground truncate text-xs", children: isFile
|
|
37
|
-
? share.file_path
|
|
38
|
-
: isTranscript
|
|
39
|
-
? tTranscript('rowMeta')
|
|
40
|
-
: `Port ${share.port}${share.path}` })] }), live && (_jsxs("span", { className: "flex shrink-0 items-center gap-1", children: [_jsx(Hint, { label: copied ? 'Copied' : tI18nComplete.raw('textdbf362d4f210'), side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": tI18nComplete.raw('textdbf362d4f210'), disabled: !url, onClick: () => void copy(), className: "size-8 active:scale-[0.96]", children: copied ? (_jsx(Check, { className: "text-kortix-green size-4" })) : (_jsx(Link2, { className: "size-4" })) }) }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: onRevoke, disabled: isRevoking, children: [isRevoking ? _jsx(Loading, { className: "size-4 shrink-0" }) : null, tI18nComplete.raw('text87e6d00bbf53')] })] }))] }));
|
|
36
|
+
return (_jsxs("li", { className: cn('group bg-popover flex items-center gap-3 rounded-md border px-4 py-2.5 transition-colors', !live && 'opacity-60'), children: [_jsx("span", { className: cn('flex size-9 shrink-0 items-center justify-center rounded-sm', live ? 'bg-kortix-green/15' : 'bg-muted'), children: _jsx(Icon, { className: cn('size-5', live ? 'text-kortix-green' : 'text-muted-foreground') }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "truncate text-sm font-medium", children: share.label }), !live && (_jsx(Badge, { variant: "outline", size: "xs", children: share.revoked_at ? 'Revoked' : 'Expired' }))] }), _jsx("p", { className: "text-muted-foreground truncate text-xs", children: isFile ? share.file_path : isTranscript ? share.label : `Port ${share.port}${share.path}` })] }), live && (_jsxs("span", { className: "flex shrink-0 items-center gap-1", children: [_jsx(Hint, { label: copied ? 'Copied' : tI18nComplete.raw('textdbf362d4f210'), side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": tI18nComplete.raw('textdbf362d4f210'), disabled: !url, onClick: () => void copy(), className: "size-8 active:scale-[0.96]", children: copied ? (_jsx(Check, { className: "text-kortix-green size-4" })) : (_jsx(Link2, { className: "size-4" })) }) }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: onRevoke, disabled: isRevoking, children: [isRevoking ? _jsx(Loading, { className: "size-4 shrink-0" }) : null, tI18nComplete.raw('text87e6d00bbf53')] })] }))] }));
|
|
41
37
|
}
|
|
42
38
|
export function SessionSharesModal({ projectId, sessionId, open, onOpenChange, }) {
|
|
43
39
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
@@ -173,6 +173,20 @@ export const useKortixComputerStore = create()(devtools((set, get) => ({
|
|
|
173
173
|
get().setIsActionPanelOpen(true);
|
|
174
174
|
},
|
|
175
175
|
setActiveSession: (sessionId, continuity) => {
|
|
176
|
+
const nextProjectSessionId = continuity?.projectSessionId ?? null;
|
|
177
|
+
const nextIsTransient = continuity?.transient ?? false;
|
|
178
|
+
// The boot shell of the session whose real chat already owns the right
|
|
179
|
+
// side. The two layers mount in either order, and the chat holds the
|
|
180
|
+
// panel's content: a shell that took over kept the right side after it
|
|
181
|
+
// faded out, so ⌘I reopened the cards instead of the detail it had
|
|
182
|
+
// just hidden (KRTX-2944). Not a session change, so nothing moves.
|
|
183
|
+
if (nextIsTransient &&
|
|
184
|
+
nextProjectSessionId !== null &&
|
|
185
|
+
get()._activeSessionId !== null &&
|
|
186
|
+
!get()._activeIsTransient &&
|
|
187
|
+
get()._activeProjectSessionId === nextProjectSessionId) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
176
190
|
// A quick-view is an intent about the session it was made in — it must
|
|
177
191
|
// not replay when some other session mounts later. Cleared even on the
|
|
178
192
|
// no-op re-activation path below, or a request planted for another
|
|
@@ -182,8 +196,6 @@ export const useKortixComputerStore = create()(devtools((set, get) => ({
|
|
|
182
196
|
set({ pendingQuickView: null });
|
|
183
197
|
}
|
|
184
198
|
const prev = get()._activeSessionId;
|
|
185
|
-
const nextProjectSessionId = continuity?.projectSessionId ?? null;
|
|
186
|
-
const nextIsTransient = continuity?.transient ?? false;
|
|
187
199
|
if (prev === sessionId) {
|
|
188
200
|
// Same layout re-activating (e.g. the shell flipping transient→ready
|
|
189
201
|
// state, or a tab refocus): keep the continuity bookkeeping fresh so
|
|
@@ -59,6 +59,38 @@ interface SessionHoverState {
|
|
|
59
59
|
closeSession: (sessionId: string) => void;
|
|
60
60
|
/** Immediate, for a click that opens something else and for unmount. */
|
|
61
61
|
dismiss: () => void;
|
|
62
|
+
/**
|
|
63
|
+
* The row whose session a click just opened. Its card would only describe
|
|
64
|
+
* the page on screen, so it stays shut until the pointer leaves the row.
|
|
65
|
+
* Unselected, like `warm`.
|
|
66
|
+
*/
|
|
67
|
+
suppressedSessionId: string | null;
|
|
68
|
+
suppress: (sessionId: string) => void;
|
|
69
|
+
release: (sessionId: string) => void;
|
|
62
70
|
}
|
|
63
71
|
export declare const useSessionHoverStore: import("zustand").UseBoundStore<import("zustand").StoreApi<SessionHoverState>>;
|
|
72
|
+
type RowTarget = {
|
|
73
|
+
matches?: (selector: string) => boolean;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* A session row's hover wiring (KRTX-2847). The press focuses the row and the
|
|
77
|
+
* pointer rests on it, so without the click handler every switch showed the
|
|
78
|
+
* card of the session just opened, 200 ms later, until the page took focus.
|
|
79
|
+
* Focus leaving the row releases it only when the pointer is elsewhere: the
|
|
80
|
+
* new page takes focus while the pointer still rests on the row.
|
|
81
|
+
*/
|
|
82
|
+
export declare function sessionRowHoverHandlers(id: string): {
|
|
83
|
+
onPointerEnter: (event: {
|
|
84
|
+
pointerType: string;
|
|
85
|
+
currentTarget: RowTarget;
|
|
86
|
+
}) => void;
|
|
87
|
+
onPointerLeave: () => void;
|
|
88
|
+
onFocus: (event: {
|
|
89
|
+
currentTarget: RowTarget;
|
|
90
|
+
}) => void;
|
|
91
|
+
onBlur: (event: {
|
|
92
|
+
currentTarget: RowTarget;
|
|
93
|
+
}) => void;
|
|
94
|
+
onClick: () => void;
|
|
95
|
+
};
|
|
64
96
|
export {};
|
|
@@ -65,8 +65,11 @@ export const useSessionHoverStore = create((set, get) => ({
|
|
|
65
65
|
via: 'pointer',
|
|
66
66
|
slot: null,
|
|
67
67
|
warm: false,
|
|
68
|
+
suppressedSessionId: null,
|
|
68
69
|
setSlot: (slot) => set({ slot }),
|
|
69
70
|
openSession: (sessionId, anchor, via = 'pointer') => {
|
|
71
|
+
if (get().suppressedSessionId === sessionId)
|
|
72
|
+
return;
|
|
70
73
|
closeTimer = cancel(closeTimer);
|
|
71
74
|
warmTimer = cancel(warmTimer);
|
|
72
75
|
// A pending open belongs to whichever row the pointer has just left.
|
|
@@ -106,4 +109,39 @@ export const useSessionHoverStore = create((set, get) => ({
|
|
|
106
109
|
warmTimer = cancel(warmTimer);
|
|
107
110
|
set({ activeSessionId: null, warm: false });
|
|
108
111
|
},
|
|
112
|
+
suppress: (sessionId) => {
|
|
113
|
+
set({ suppressedSessionId: sessionId });
|
|
114
|
+
get().dismiss();
|
|
115
|
+
},
|
|
116
|
+
release: (sessionId) => {
|
|
117
|
+
if (get().suppressedSessionId === sessionId)
|
|
118
|
+
set({ suppressedSessionId: null });
|
|
119
|
+
},
|
|
109
120
|
}));
|
|
121
|
+
/**
|
|
122
|
+
* A session row's hover wiring (KRTX-2847). The press focuses the row and the
|
|
123
|
+
* pointer rests on it, so without the click handler every switch showed the
|
|
124
|
+
* card of the session just opened, 200 ms later, until the page took focus.
|
|
125
|
+
* Focus leaving the row releases it only when the pointer is elsewhere: the
|
|
126
|
+
* new page takes focus while the pointer still rests on the row.
|
|
127
|
+
*/
|
|
128
|
+
export function sessionRowHoverHandlers(id) {
|
|
129
|
+
const store = () => useSessionHoverStore.getState();
|
|
130
|
+
return {
|
|
131
|
+
onPointerEnter: (event) => {
|
|
132
|
+
if (event.pointerType !== 'touch')
|
|
133
|
+
store().openSession(id, event.currentTarget, 'pointer');
|
|
134
|
+
},
|
|
135
|
+
onPointerLeave: () => {
|
|
136
|
+
store().release(id);
|
|
137
|
+
store().closeSession(id);
|
|
138
|
+
},
|
|
139
|
+
onFocus: (event) => store().openSession(id, event.currentTarget, 'focus'),
|
|
140
|
+
onBlur: (event) => {
|
|
141
|
+
if (!event.currentTarget.matches?.(':hover'))
|
|
142
|
+
store().release(id);
|
|
143
|
+
store().closeSession(id);
|
|
144
|
+
},
|
|
145
|
+
onClick: () => store().suppress(id),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* (`customize-sidebar-modal.tsx` syncs it). A row a project does not offer (a flag is off, a permission is denied)
|
|
9
9
|
* keeps its place here and simply does not render.
|
|
10
10
|
*/
|
|
11
|
-
export declare const SIDEBAR_NAV_ITEM_IDS: readonly ["customize", "apps", "files", "rooms"];
|
|
11
|
+
export declare const SIDEBAR_NAV_ITEM_IDS: readonly ["customize", "apps", "files", "rooms", "goals"];
|
|
12
12
|
export type SidebarNavItemId = (typeof SIDEBAR_NAV_ITEM_IDS)[number];
|
|
13
13
|
/**
|
|
14
14
|
* `never` moves the row into the More menu (`project-nav-items.tsx`), so its
|
|
@@ -13,7 +13,7 @@ import { registerPersistedStore, resetPersistedStore } from './persisted-store-r
|
|
|
13
13
|
*/
|
|
14
14
|
// Review (2026-10-17) and Reminders (2026-10-19) left the sidebar for Customize
|
|
15
15
|
// tabs. A saved layout that still names one drops the id in `normalizeSidebarNav`.
|
|
16
|
-
export const SIDEBAR_NAV_ITEM_IDS = ['customize', 'apps', 'files', 'rooms'];
|
|
16
|
+
export const SIDEBAR_NAV_ITEM_IDS = ['customize', 'apps', 'files', 'rooms', 'goals'];
|
|
17
17
|
/** The layout before anyone customizes it. */
|
|
18
18
|
export const SIDEBAR_NAV_DEFAULTS = [
|
|
19
19
|
{ id: 'customize', visibility: 'always' },
|
|
@@ -22,6 +22,8 @@ export const SIDEBAR_NAV_DEFAULTS = [
|
|
|
22
22
|
// Rooms (2026-10-19, flag `agent_rooms`): chat rooms for sessions and people.
|
|
23
23
|
// In the More menu until the person pins it (2026-10-20).
|
|
24
24
|
{ id: 'rooms', visibility: 'never' },
|
|
25
|
+
// Goals (2026-10-21, flag `goals`): objectives agents own until verified done.
|
|
26
|
+
{ id: 'goals', visibility: 'always' },
|
|
25
27
|
];
|
|
26
28
|
function isItemId(value) {
|
|
27
29
|
return SIDEBAR_NAV_ITEM_IDS.includes(value);
|
package/dist/lib/routes.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export interface UiRoutes {
|
|
|
22
22
|
apps: (projectId: string) => string | null;
|
|
23
23
|
/** The project's chat rooms (flag `agent_rooms`). */
|
|
24
24
|
rooms: (projectId: string) => string | null;
|
|
25
|
+
/** The project's goals (flag `goals`); a goal is `<goals>/<scope>/<slug>`. */
|
|
26
|
+
goals: (projectId: string) => string | null;
|
|
25
27
|
members: (projectId: string) => string | null;
|
|
26
28
|
/** The project settings page, on its sandbox section. */
|
|
27
29
|
sandboxSettings: (projectId: string) => string | null;
|
package/dist/lib/routes.js
CHANGED
|
@@ -6,6 +6,7 @@ const DEFAULT_ROUTES = {
|
|
|
6
6
|
drive: (p) => `/projects/${p}/drive`,
|
|
7
7
|
apps: (p) => `/projects/${p}/apps`,
|
|
8
8
|
rooms: (p) => `/projects/${p}/rooms`,
|
|
9
|
+
goals: (p) => `/projects/${p}/goals`,
|
|
9
10
|
members: (p) => `/projects/${p}/members`,
|
|
10
11
|
sandboxSettings: (p) => `/projects/${p}/config?section=sandbox`,
|
|
11
12
|
customize: (p, path) => `/projects/${p}/customize${path ? `/${path}` : ''}`,
|
|
@@ -7,7 +7,7 @@ import type { ReactNode } from 'react';
|
|
|
7
7
|
*/
|
|
8
8
|
export declare function SettingsSectionHeader({ title, description, action, className, }: {
|
|
9
9
|
title: string;
|
|
10
|
-
description?:
|
|
10
|
+
description?: ReactNode;
|
|
11
11
|
action?: ReactNode;
|
|
12
12
|
className?: string;
|
|
13
13
|
}): import("react").JSX.Element;
|
|
@@ -51,8 +51,6 @@ export declare function splitFilenameForTile(name: string): {
|
|
|
51
51
|
head: string;
|
|
52
52
|
tail: string;
|
|
53
53
|
} | null;
|
|
54
|
-
/** The one-string form, for places that cannot render two lines (tooltips, tests). */
|
|
55
|
-
export declare function middleTruncateFilename(name: string, max?: number): string;
|
|
56
54
|
/** A tile previews the head of the paste only: the clamp shows four lines, and the DOM need not hold 100 kB. */
|
|
57
55
|
export declare const PASTE_PREVIEW_CHARS = 400;
|
|
58
56
|
export interface AttachmentTileProps {
|
|
@@ -58,14 +58,6 @@ export function splitFilenameForTile(name) {
|
|
|
58
58
|
return null;
|
|
59
59
|
return { head: name.slice(0, -NAME_TAIL_CHARS), tail: name.slice(-NAME_TAIL_CHARS) };
|
|
60
60
|
}
|
|
61
|
-
/** The one-string form, for places that cannot render two lines (tooltips, tests). */
|
|
62
|
-
export function middleTruncateFilename(name, max = 22) {
|
|
63
|
-
if (name.length <= max)
|
|
64
|
-
return name;
|
|
65
|
-
const tail = Math.min(NAME_TAIL_CHARS, Math.floor((max - 1) / 2));
|
|
66
|
-
const head = max - 1 - tail;
|
|
67
|
-
return `${name.slice(0, head)}…${name.slice(-tail)}`;
|
|
68
|
-
}
|
|
69
61
|
/** A tile previews the head of the paste only: the clamp shows four lines, and the DOM need not hold 100 kB. */
|
|
70
62
|
export const PASTE_PREVIEW_CHARS = 400;
|
|
71
63
|
/**
|
|
@@ -2,7 +2,7 @@ export interface ClipboardItemLike {
|
|
|
2
2
|
readonly kind: string;
|
|
3
3
|
getAsFile(): File | null;
|
|
4
4
|
}
|
|
5
|
-
|
|
5
|
+
interface ClipboardPayload {
|
|
6
6
|
readonly files: ArrayLike<File>;
|
|
7
7
|
readonly items: ArrayLike<ClipboardItemLike>;
|
|
8
8
|
}
|
|
@@ -14,3 +14,4 @@ export interface ClipboardPayload {
|
|
|
14
14
|
* handle the text itself.
|
|
15
15
|
*/
|
|
16
16
|
export declare function extractClipboardFiles(data: ClipboardPayload | null | undefined): File[];
|
|
17
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JSONContent } from '@tiptap/core';
|
|
2
|
-
import type { AttachedFile
|
|
2
|
+
import type { AttachedFile } from './session-chat-input.js';
|
|
3
3
|
export declare function mergeFailedSubmissionText(current: string, submitted: string): string;
|
|
4
4
|
/**
|
|
5
5
|
* `mergeFailedSubmissionText`'s counterpart for the ProseMirror document
|
|
@@ -39,4 +39,3 @@ export declare function mergeFailedSubmissionText(current: string, submitted: st
|
|
|
39
39
|
*/
|
|
40
40
|
export declare function mergeFailedSubmissionDocument(current: JSONContent, currentIsEmpty: boolean, submitted: JSONContent, submittedIsEmpty: boolean): JSONContent;
|
|
41
41
|
export declare function mergeFailedSubmissionFiles(current: AttachedFile[], submitted: AttachedFile[]): AttachedFile[];
|
|
42
|
-
export declare function mergeFailedSubmissionMentions(current: TrackedMention[], submitted: TrackedMention[]): TrackedMention[];
|
|
@@ -68,16 +68,3 @@ export function mergeFailedSubmissionFiles(current, submitted) {
|
|
|
68
68
|
return true;
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
|
-
function mentionKey(mention) {
|
|
72
|
-
return `${mention.kind}:${mention.value ?? ''}:${mention.label}`;
|
|
73
|
-
}
|
|
74
|
-
export function mergeFailedSubmissionMentions(current, submitted) {
|
|
75
|
-
const seen = new Set();
|
|
76
|
-
return [...submitted, ...current].filter((mention) => {
|
|
77
|
-
const key = mentionKey(mention);
|
|
78
|
-
if (seen.has(key))
|
|
79
|
-
return false;
|
|
80
|
-
seen.add(key);
|
|
81
|
-
return true;
|
|
82
|
-
});
|
|
83
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ContextReading } from './context-usage.js';
|
|
2
|
-
export {
|
|
2
|
+
export { getContextUsage } from './context-usage.js';
|
|
3
3
|
export type { ContextReading, ContextUsage } from './context-usage.js';
|
|
4
4
|
export declare function ContextRing({ percent, tone, className }: ContextReading & {
|
|
5
5
|
className?: string;
|
|
@@ -9,13 +9,13 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
9
9
|
* the control it opens. Extracted here so neither surface re-derives the
|
|
10
10
|
* ring's look on its own.
|
|
11
11
|
*
|
|
12
|
-
* `
|
|
12
|
+
* `getContextUsage` is the pure derivation (percent + tone) over the same
|
|
13
13
|
* exported helpers the hover card uses — pure so it has tests that can fail
|
|
14
14
|
* (this repo's `bun test` has no DOM to assert the SVG itself).
|
|
15
15
|
*/
|
|
16
16
|
import { ProgressRing } from '../../primitives/progress-ring.js';
|
|
17
17
|
import { STATUS_TEXT } from '../../primitives/status.js';
|
|
18
|
-
export {
|
|
18
|
+
export { getContextUsage } from './context-usage.js';
|
|
19
19
|
export function ContextRing({ percent, tone, className }) {
|
|
20
20
|
return (_jsx(ProgressRing, { value: percent, className: className, progressClassName: STATUS_TEXT[tone] }));
|
|
21
21
|
}
|
|
@@ -22,7 +22,7 @@ export declare function contextTone(ratio: number): StatusTone;
|
|
|
22
22
|
export declare function getContextLimit(models: FlatModel[] | undefined, selectedModel: {
|
|
23
23
|
providerID: string;
|
|
24
24
|
modelID: string;
|
|
25
|
-
} | null | undefined): number;
|
|
25
|
+
} | null | undefined): number | null;
|
|
26
26
|
export declare function getSelectedModelName(models: FlatModel[] | undefined, selectedModel: {
|
|
27
27
|
providerID: string;
|
|
28
28
|
modelID: string;
|
|
@@ -34,7 +34,8 @@ export interface ContextReading {
|
|
|
34
34
|
}
|
|
35
35
|
export interface ContextUsage extends ContextReading {
|
|
36
36
|
breakdown: ContextBreakdown;
|
|
37
|
-
|
|
37
|
+
/** The model's context window; null when no source knows it. */
|
|
38
|
+
limit: number | null;
|
|
38
39
|
ratio: number;
|
|
39
40
|
modelName: string | null;
|
|
40
41
|
}
|
|
@@ -42,7 +43,3 @@ export declare function getContextUsage(messages: SessionLogMessage[] | undefine
|
|
|
42
43
|
providerID: string;
|
|
43
44
|
modelID: string;
|
|
44
45
|
} | null): ContextUsage;
|
|
45
|
-
export declare function getContextReading(messages: SessionLogMessage[] | undefined, models?: FlatModel[], selectedModel?: {
|
|
46
|
-
providerID: string;
|
|
47
|
-
modelID: string;
|
|
48
|
-
} | null): ContextReading;
|
|
@@ -49,7 +49,8 @@ export function getContextLimit(models, selectedModel) {
|
|
|
49
49
|
if (model?.contextWindow && model.contextWindow > 0)
|
|
50
50
|
return model.contextWindow;
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
// Unknown: the meter shows what is used and no invented window.
|
|
53
|
+
return null;
|
|
53
54
|
}
|
|
54
55
|
export function getSelectedModelName(models, selectedModel) {
|
|
55
56
|
if (!selectedModel || !models)
|
|
@@ -77,7 +78,7 @@ export function formatContextCount(tokens) {
|
|
|
77
78
|
export function getContextUsage(messages, models, selectedModel) {
|
|
78
79
|
const breakdown = getLastAssistantTokenBreakdown(messages);
|
|
79
80
|
const limit = getContextLimit(models, selectedModel);
|
|
80
|
-
const ratio = breakdown.total > 0 ? Math.min(breakdown.total / limit, 1) : 0;
|
|
81
|
+
const ratio = limit && breakdown.total > 0 ? Math.min(breakdown.total / limit, 1) : 0;
|
|
81
82
|
return {
|
|
82
83
|
percent: Math.round(ratio * 100),
|
|
83
84
|
tone: contextTone(ratio),
|
|
@@ -87,7 +88,3 @@ export function getContextUsage(messages, models, selectedModel) {
|
|
|
87
88
|
modelName: getSelectedModelName(models, selectedModel),
|
|
88
89
|
};
|
|
89
90
|
}
|
|
90
|
-
export function getContextReading(messages, models, selectedModel) {
|
|
91
|
-
const { percent, tone } = getContextUsage(messages, models, selectedModel);
|
|
92
|
-
return { percent, tone };
|
|
93
|
-
}
|
|
@@ -30,7 +30,7 @@ import type { SessionPrompt } from '@kortix/sdk';
|
|
|
30
30
|
* row that provably went out. Every refusal below is a case where the message
|
|
31
31
|
* could belong to something else.
|
|
32
32
|
*/
|
|
33
|
-
|
|
33
|
+
interface FirstTurnClaim {
|
|
34
34
|
/** The row that owns the message. */
|
|
35
35
|
promptId: string;
|
|
36
36
|
/** The transcript id it is on screen under (the re-minted one). */
|
|
@@ -61,4 +61,4 @@ export declare function claimFirstTurnRow(input: {
|
|
|
61
61
|
/** `transcriptUserMessageIds` as built from ids alone. */
|
|
62
62
|
claimedIds: ReadonlySet<string>;
|
|
63
63
|
}): FirstTurnClaim | null;
|
|
64
|
-
export
|
|
64
|
+
export {};
|
|
@@ -49,7 +49,7 @@ const PROMPT_TEXT_PREVIEW_CHARS = 2000;
|
|
|
49
49
|
function normalise(text) {
|
|
50
50
|
return text.replace(/\s+/g, ' ').trim();
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
function promptTextMatches(rowText, messageText) {
|
|
53
53
|
const row = normalise(rowText);
|
|
54
54
|
if (!row)
|
|
55
55
|
return false;
|
|
@@ -27,7 +27,7 @@ export declare function isFirstPromptRow(prompt: Pick<SessionPrompt, 'client_mes
|
|
|
27
27
|
* refuses removal.
|
|
28
28
|
* - `failed`: delivery gave up; retry or remove.
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
type QueueRowState = 'sending' | 'queued' | 'delivering' | 'failed';
|
|
31
31
|
export interface QueueRow {
|
|
32
32
|
/** The inbox `prompt_id`, or `draft:<clientMessageId>` before the POST. */
|
|
33
33
|
id: string;
|
|
@@ -62,7 +62,7 @@ export interface QueueRow {
|
|
|
62
62
|
* `null` when the visible words are not one run, so no edit is offered. */
|
|
63
63
|
editText: string | null;
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
interface QueueProjection {
|
|
66
66
|
/** In delivery order: the server's rows first, then this tab's unsent drafts. */
|
|
67
67
|
rows: QueueRow[];
|
|
68
68
|
/** Rows the Stop hold is pausing — counted even when the row is on screen in
|
|
@@ -100,3 +100,4 @@ export declare function composerSendDelivery(placement: 'transcript' | 'composer
|
|
|
100
100
|
placement: 'transcript' | 'composer';
|
|
101
101
|
delivery?: SessionPromptDelivery;
|
|
102
102
|
};
|
|
103
|
+
export {};
|
|
@@ -13,7 +13,7 @@ import type { QueueRow } from './queue-projection.js';
|
|
|
13
13
|
* with its text parts only, so every file on the message was lost.
|
|
14
14
|
*/
|
|
15
15
|
/** A queued message open in the composer. */
|
|
16
|
-
|
|
16
|
+
interface QueuedPromptEdit {
|
|
17
17
|
promptId: string;
|
|
18
18
|
clientMessageId?: string;
|
|
19
19
|
/** The row's whole text as the server holds it: file and mention markup included. */
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* own; duplicates (a runtime + catalog merge slip) collapse to one entry.
|
|
6
6
|
*/
|
|
7
7
|
export declare function reasoningEffortChoices(variants: readonly string[] | undefined, canApply: boolean): string[];
|
|
8
|
-
|
|
8
|
+
interface ReasoningEffortSelectorProps {
|
|
9
9
|
/** The selected model's variant ids (`Object.keys(model.variants)`). */
|
|
10
10
|
variants: readonly string[] | undefined;
|
|
11
11
|
/** The session's current variant, or null = model default. */
|
|
@@ -34,3 +34,4 @@ export interface ReasoningEffortSelectorProps {
|
|
|
34
34
|
* free, where the command palette would need them re-implemented.
|
|
35
35
|
*/
|
|
36
36
|
export declare function ReasoningEffortSelector({ variants, selectedVariant, onVariantChange, open: openProp, onOpenChange, }: ReasoningEffortSelectorProps): import("react").JSX.Element | null;
|
|
37
|
+
export {};
|
|
@@ -86,6 +86,8 @@ export declare function serverHoldsOpenTurn(working: {
|
|
|
86
86
|
}): boolean;
|
|
87
87
|
export declare function sessionComposerReadiness(input: {
|
|
88
88
|
runtimeReady: boolean;
|
|
89
|
+
/** A prompt sent from this page is waiting for the computer. Its turn
|
|
90
|
+
* already shows it working; do not ask for another message. */
|
|
89
91
|
pendingPrompt?: boolean;
|
|
90
92
|
/** A queued status already describes this send; do not ask for another. */
|
|
91
93
|
pendingDelivery?: boolean;
|
|
@@ -116,8 +118,8 @@ export declare function sessionComposerReadiness(input: {
|
|
|
116
118
|
/**
|
|
117
119
|
* This page's own `/start` is bringing the computer up: its stage is
|
|
118
120
|
* `provisioning` or `starting`, or it has not answered yet. A stopped
|
|
119
|
-
* computer is then WAKING, not idle
|
|
120
|
-
*
|
|
121
|
+
* computer is then WAKING, not idle, and the composer must not claim the
|
|
122
|
+
* next message is what wakes it.
|
|
121
123
|
*/
|
|
122
124
|
starting?: boolean;
|
|
123
125
|
/**
|
|
@@ -129,28 +131,11 @@ export declare function sessionComposerReadiness(input: {
|
|
|
129
131
|
* either too short (the notice flashed over a live session) or too long (a
|
|
130
132
|
* genuinely parked box announced itself late).
|
|
131
133
|
*
|
|
132
|
-
* `unknown` and `connecting` are WAITS. Only `waking`
|
|
133
|
-
* saying the box is down
|
|
134
|
+
* `unknown` and `connecting` are WAITS. Only `waking` with no wake under
|
|
135
|
+
* way — the control plane saying the box is down, and nothing bringing it
|
|
136
|
+
* up — earns a notice: the asleep one.
|
|
134
137
|
*/
|
|
135
138
|
connection?: SessionConnection;
|
|
136
|
-
/**
|
|
137
|
-
* Nothing has been CONTACTED yet — the runtime probe has not answered and
|
|
138
|
-
* `GET .../turn` has not landed, and the mount is young enough that this is
|
|
139
|
-
* ordinary startup rather than a wait.
|
|
140
|
-
*
|
|
141
|
-
* On a cold mount every other input here is false: not ready, no open turn,
|
|
142
|
-
* not unreachable, not stalled. The fallback below then asserted the one
|
|
143
|
-
* thing nobody had checked — that the session is asleep — and a page that had
|
|
144
|
-
* just reloaded under a working agent showed "Waking this session up…" for a
|
|
145
|
-
* couple of seconds before the transcript resumed streaming (screen
|
|
146
|
-
* recording, dev, 2026-08-23). To the person watching, the session dropped
|
|
147
|
-
* and came back. It never went anywhere; we simply had not looked yet.
|
|
148
|
-
*
|
|
149
|
-
* Withholding a GUESS, never a fact: an unreachable box and a stalled boot
|
|
150
|
-
* are observations and still speak immediately, and so does a live server
|
|
151
|
-
* turn. Only the "must be waking, then" fallback waits to be sure.
|
|
152
|
-
*/
|
|
153
|
-
settling?: boolean;
|
|
154
139
|
}): SessionComposerReadiness;
|
|
155
140
|
/**
|
|
156
141
|
* The last turn's shimmer, decided once in `SessionChat` and passed down to
|
|
@@ -60,8 +60,7 @@ export function serverHoldsOpenTurn(working) {
|
|
|
60
60
|
* cannot drain, and saying nothing at all.
|
|
61
61
|
*
|
|
62
62
|
* Not "waking": nothing is booting. It names the fact (contact lost mid-turn)
|
|
63
|
-
* and what a send does,
|
|
64
|
-
* button stays live.
|
|
63
|
+
* and what a send does, because the send button stays live.
|
|
65
64
|
*/
|
|
66
65
|
const RUNTIME_UNREACHABLE_NOTICE = SESSION_NOTICE.unreachableMidTurn;
|
|
67
66
|
export function sessionComposerReadiness(input) {
|
|
@@ -93,51 +92,24 @@ export function sessionComposerReadiness(input) {
|
|
|
93
92
|
retryable: true,
|
|
94
93
|
};
|
|
95
94
|
}
|
|
96
|
-
if (input.pendingPrompt) {
|
|
97
|
-
return {
|
|
98
|
-
ready: false,
|
|
99
|
-
notice: SESSION_NOTICE.starting,
|
|
100
|
-
retryable: false,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
95
|
if (input.connection === 'waking') {
|
|
104
96
|
// A PARKED / idle box: the control plane says the sandbox is not up, and
|
|
105
97
|
// nothing is actively booting — a genuine boot-stall is caught by `stalled`
|
|
106
98
|
// above, and the SDK no longer arms the stall clock for a parked box
|
|
107
99
|
// (`setOpenCodeHealth({ parked })`), so this never escalates to an infinite
|
|
108
100
|
// "taking longer than usual" spinner. The box resumes on the next SEND, so
|
|
109
|
-
// say exactly that: an honest idle state
|
|
110
|
-
// session
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
retryable: false,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
// A wait is not a fault. `unknown` means nothing has answered; `connecting`
|
|
118
|
-
// means the control plane says the box is UP and the runtime simply has not
|
|
119
|
-
// reached us. Neither is the state this notice describes, and asserting it
|
|
120
|
-
// from them is what put "Waking this session up…" over a live, streaming
|
|
121
|
-
// session on every reload.
|
|
122
|
-
if (input.connection === 'unknown' ||
|
|
123
|
-
input.connection === 'connecting' ||
|
|
124
|
-
input.connection === 'live') {
|
|
125
|
-
return { ready: false, notice: null, retryable: false };
|
|
126
|
-
}
|
|
127
|
-
if (input.settling) {
|
|
128
|
-
// Legacy path for a caller with no connection projection yet: say nothing
|
|
129
|
-
// while the mount is young. See `settling`.
|
|
130
|
-
return { ready: false, notice: null, retryable: false };
|
|
101
|
+
// say exactly that: an honest idle state (RC-3). A wake already under way
|
|
102
|
+
// says nothing: opening the session wakes its computer in the background,
|
|
103
|
+
// and a send goes out as soon as it is up.
|
|
104
|
+
const waking = input.pendingPrompt || input.pendingDelivery || input.starting;
|
|
105
|
+
return { ready: false, notice: waking ? null : SESSION_NOTICE.idle, retryable: false };
|
|
131
106
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
notice: SESSION_NOTICE.waking,
|
|
139
|
-
retryable: false,
|
|
140
|
-
};
|
|
107
|
+
// A wait is not a fault, and a routine wake is silent: `unknown` means
|
|
108
|
+
// nothing has answered, `connecting` that the box is up and the runtime has
|
|
109
|
+
// not reached us yet, and a waking box comes up in the background while a
|
|
110
|
+
// send queues. A wake that goes wrong speaks through `stalled` or
|
|
111
|
+
// `unreachable` above.
|
|
112
|
+
return { ready: false, notice: null, retryable: false };
|
|
141
113
|
}
|
|
142
114
|
/**
|
|
143
115
|
* The last turn's shimmer, decided once in `SessionChat` and passed down to
|
|
@@ -25,7 +25,7 @@ export declare function contextUsageHeadlineKind(tone: StatusTone): UsageHeadlin
|
|
|
25
25
|
*/
|
|
26
26
|
export declare function ContextUsageCard({ breakdown, limit, ratio, tone, modelName, interactive, onViewDetails, }: {
|
|
27
27
|
breakdown: ContextBreakdown;
|
|
28
|
-
limit: number;
|
|
28
|
+
limit: number | null;
|
|
29
29
|
ratio: number;
|
|
30
30
|
tone: StatusTone;
|
|
31
31
|
modelName: string | null;
|
|
@@ -64,7 +64,7 @@ export function ContextUsageCard({ breakdown, limit, ratio, tone, modelName, int
|
|
|
64
64
|
const title = t(HEADLINE_TITLE_KEY[headline]);
|
|
65
65
|
const tipKey = HEADLINE_TIP_KEY[headline];
|
|
66
66
|
const tip = tipKey ? t(tipKey) : null;
|
|
67
|
-
const remaining = Math.max(limit - breakdown.total, 0);
|
|
67
|
+
const remaining = limit === null ? null : Math.max(limit - breakdown.total, 0);
|
|
68
68
|
const rows = BREAKDOWN_ROWS.filter((row) => breakdown[row.key] > 0);
|
|
69
69
|
// The bar draws in from 0 on open instead of appearing pre-filled. The card
|
|
70
70
|
// mounts only when the HoverCard opens, so this is an entrance, not a
|
|
@@ -75,7 +75,7 @@ export function ContextUsageCard({ breakdown, limit, ratio, tone, modelName, int
|
|
|
75
75
|
const frame = requestAnimationFrame(() => setDrawn(true));
|
|
76
76
|
return () => cancelAnimationFrame(frame);
|
|
77
77
|
}, []);
|
|
78
|
-
return (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-baseline justify-between gap-3", children: [_jsx("p", { className: cn('text-sm font-medium text-pretty', headline === 'healthy' ? 'text-foreground' : STATUS_TEXT[tone]), children: title }), _jsx("span", { className: cn('shrink-0 text-xs font-medium tabular-nums', headline === 'healthy' ? 'text-muted-foreground' : STATUS_TEXT[tone]), children: t('percentFull', { percent }) })] }), _jsx(Progress, { value: drawn ? percent : 0, "aria-label": t('meterLabel'), className: "bg-foreground/10 h-1.5", indicatorClassName: cn(STATUS_DOT[tone], 'motion-reduce:transition-none') }), _jsxs("div", { className: "flex items-baseline justify-between gap-3 text-xs", children: [_jsx("span", { className: "text-muted-foreground tabular-nums", children: t('usedCount', { used: formatContextCount(breakdown.total) }) }), _jsx("span", { className: "text-foreground font-medium tabular-nums", children: t('remainingCount', { remaining: formatContextCount(remaining) }) })] })] }), rows.length > 0 ? (_jsx("dl", { className: "grid grid-cols-[1fr_auto] gap-x-4 gap-y-1.5 border-t pt-3 text-xs", children: rows.map((row) => (_jsxs(Fragment, { children: [_jsx("dt", { className: "text-muted-foreground truncate", children: t(row.labelKey) }), _jsx("dd", { className: "text-foreground text-end font-medium tabular-nums", children: formatContextCount(breakdown[row.key]) })] }, row.key))) })) : null, _jsxs("p", { className: "text-muted-foreground flex items-baseline justify-between gap-3 border-t pt-3 text-xs", children: [_jsx("span", { className: "truncate", children: modelName ?? t('modelUnknown') }), _jsx("span", { className: "shrink-0 tabular-nums", children: t('windowMeta', { limit: formatContextCount(limit) }) })] }), tip ? (_jsxs("p", { className: "text-muted-foreground flex gap-1.5 text-xs text-pretty", children: [_jsx(WarningIcon, { weight: "fill", className: cn('mt-0.5 size-3.5 shrink-0', STATUS_TEXT[tone]) }), _jsx("span", { children: tip })] })) : null, interactive && onViewDetails ? (_jsxs(Button, { type: "button", variant: "accent", size: "sm", onClick: (e) => {
|
|
78
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-baseline justify-between gap-3", children: [_jsx("p", { className: cn('text-sm font-medium text-pretty', headline === 'healthy' ? 'text-foreground' : STATUS_TEXT[tone]), children: title }), _jsx("span", { className: cn('shrink-0 text-xs font-medium tabular-nums', headline === 'healthy' ? 'text-muted-foreground' : STATUS_TEXT[tone]), children: t('percentFull', { percent }) })] }), _jsx(Progress, { value: drawn ? percent : 0, "aria-label": t('meterLabel'), className: "bg-foreground/10 h-1.5", indicatorClassName: cn(STATUS_DOT[tone], 'motion-reduce:transition-none') }), _jsxs("div", { className: "flex items-baseline justify-between gap-3 text-xs", children: [_jsx("span", { className: "text-muted-foreground tabular-nums", children: t('usedCount', { used: formatContextCount(breakdown.total) }) }), remaining !== null ? (_jsx("span", { className: "text-foreground font-medium tabular-nums", children: t('remainingCount', { remaining: formatContextCount(remaining) }) })) : null] })] }), rows.length > 0 ? (_jsx("dl", { className: "grid grid-cols-[1fr_auto] gap-x-4 gap-y-1.5 border-t pt-3 text-xs", children: rows.map((row) => (_jsxs(Fragment, { children: [_jsx("dt", { className: "text-muted-foreground truncate", children: t(row.labelKey) }), _jsx("dd", { className: "text-foreground text-end font-medium tabular-nums", children: formatContextCount(breakdown[row.key]) })] }, row.key))) })) : null, _jsxs("p", { className: "text-muted-foreground flex items-baseline justify-between gap-3 border-t pt-3 text-xs", children: [_jsx("span", { className: "truncate", children: modelName ?? t('modelUnknown') }), limit !== null ? (_jsx("span", { className: "shrink-0 tabular-nums", children: t('windowMeta', { limit: formatContextCount(limit) }) })) : null] }), tip ? (_jsxs("p", { className: "text-muted-foreground flex gap-1.5 text-xs text-pretty", children: [_jsx(WarningIcon, { weight: "fill", className: cn('mt-0.5 size-3.5 shrink-0', STATUS_TEXT[tone]) }), _jsx("span", { children: tip })] })) : null, interactive && onViewDetails ? (_jsxs(Button, { type: "button", variant: "accent", size: "sm", onClick: (e) => {
|
|
79
79
|
e.stopPropagation();
|
|
80
80
|
onViewDetails();
|
|
81
81
|
}, className: "w-full justify-between rounded-sm px-2.5 text-xs active:scale-[0.97]", children: [t('viewDetails'), _jsx(CaretRightIcon, { className: "text-muted-foreground size-3.5" })] })) : null] }));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function SessionGoalIndicator(): import("react").JSX.Element | null;
|