@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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Button } from '../../primitives/button.js';
|
|
4
|
+
import { Field, FieldGroup, FieldLabel } from '../../primitives/field.js';
|
|
5
|
+
import { Input } from '../../primitives/input.js';
|
|
6
|
+
import { InputGroup, InputGroupAddon, InputGroupInput } from '../../primitives/input-group.js';
|
|
7
|
+
import Loading from '../../primitives/loading.js';
|
|
8
|
+
import { useTranslations } from '../../i18n/index.js';
|
|
9
|
+
import { getConnectorAppForm, HostBoundaryError, submitConnectorAppForm, } from '@kortix/sdk';
|
|
10
|
+
import { ArrowUpRightIcon, CheckIcon, ClockCountdownIcon, LinkBreakIcon, WarningCircleIcon, } from '@phosphor-icons/react';
|
|
11
|
+
import { useEffect, useState } from 'react';
|
|
12
|
+
import { setupLinkApiBase } from './api-base.js';
|
|
13
|
+
import { StatusNotice } from './secret-intake-form.js';
|
|
14
|
+
// Machine values, not prose: Shopify serves every store at <handle>.myshopify.com.
|
|
15
|
+
const SHOPIFY_HOST_SUFFIX = '.myshopify.com';
|
|
16
|
+
const EXAMPLE_STORE_HANDLE = 'acme';
|
|
17
|
+
const DEV_DASHBOARD_URL = 'https://dev.shopify.com/dashboard';
|
|
18
|
+
/** Long enough to read the confirmation before the window closes itself. */
|
|
19
|
+
const CLOSE_AFTER_MS = 1_500;
|
|
20
|
+
function statusOf(cause) {
|
|
21
|
+
return cause instanceof HostBoundaryError ? cause.status : null;
|
|
22
|
+
}
|
|
23
|
+
function DashboardLink({ children }) {
|
|
24
|
+
return (_jsxs("a", { href: DEV_DASHBOARD_URL, target: "_blank", rel: "noopener noreferrer", className: "group text-kortix-base focus-visible:ring-ring inline-flex items-baseline gap-0.5 rounded-sm font-medium outline-none focus-visible:ring-2", children: [_jsx("span", { className: "decoration-kortix-base/40 group-hover:decoration-kortix-base underline underline-offset-2", children: children }), _jsx(ArrowUpRightIcon, { className: "size-3 shrink-0 self-center" })] }));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The form behind an own-app connect link (`/connect/app/[token]`): the app's
|
|
28
|
+
* client credentials and the store, for a Composio toolkit that runs on the
|
|
29
|
+
* customer's own app. Shopify is the one such app today.
|
|
30
|
+
*
|
|
31
|
+
* The page opens in the connect popup. The window that opened it polls the
|
|
32
|
+
* connection, so after a success this window only confirms and closes itself
|
|
33
|
+
* (or follows the native app's return link).
|
|
34
|
+
*/
|
|
35
|
+
export function AppCredentialsForm({ token }) {
|
|
36
|
+
const t = useTranslations('connectAppForm');
|
|
37
|
+
const base = setupLinkApiBase();
|
|
38
|
+
const [phase, setPhase] = useState('loading');
|
|
39
|
+
const [info, setInfo] = useState(null);
|
|
40
|
+
const [store, setStore] = useState('');
|
|
41
|
+
const [clientId, setClientId] = useState('');
|
|
42
|
+
const [clientSecret, setClientSecret] = useState('');
|
|
43
|
+
const [connectedAs, setConnectedAs] = useState(null);
|
|
44
|
+
const [message, setMessage] = useState(null);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
let cancelled = false;
|
|
47
|
+
getConnectorAppForm(token, { backendUrl: base }).then((body) => {
|
|
48
|
+
if (cancelled)
|
|
49
|
+
return;
|
|
50
|
+
setInfo(body);
|
|
51
|
+
setConnectedAs(body.connected_as);
|
|
52
|
+
setPhase(body.connected ? 'done' : 'ready');
|
|
53
|
+
}, (cause) => {
|
|
54
|
+
if (cancelled)
|
|
55
|
+
return;
|
|
56
|
+
const status = statusOf(cause);
|
|
57
|
+
setMessage(cause instanceof Error ? cause.message : null);
|
|
58
|
+
setPhase(status === 410 ? 'expired' : status === 404 ? 'invalid' : status === 409 ? 'stale' : 'error');
|
|
59
|
+
});
|
|
60
|
+
return () => {
|
|
61
|
+
cancelled = true;
|
|
62
|
+
};
|
|
63
|
+
}, [base, token]);
|
|
64
|
+
async function submit() {
|
|
65
|
+
if (!store.trim() || !clientId.trim() || !clientSecret.trim()) {
|
|
66
|
+
setMessage(t.raw('missingFields'));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
setPhase('submitting');
|
|
70
|
+
setMessage(null);
|
|
71
|
+
try {
|
|
72
|
+
const result = await submitConnectorAppForm(token, { store: store.trim(), clientId: clientId.trim(), clientSecret: clientSecret.trim() }, { backendUrl: base });
|
|
73
|
+
setConnectedAs(result.connected_as);
|
|
74
|
+
setPhase('done');
|
|
75
|
+
window.setTimeout(() => {
|
|
76
|
+
if (result.success_redirect_uri)
|
|
77
|
+
window.location.assign(result.success_redirect_uri);
|
|
78
|
+
else
|
|
79
|
+
window.close();
|
|
80
|
+
}, CLOSE_AFTER_MS);
|
|
81
|
+
}
|
|
82
|
+
catch (cause) {
|
|
83
|
+
const status = statusOf(cause);
|
|
84
|
+
setMessage(cause instanceof Error ? cause.message : t.raw('failed'));
|
|
85
|
+
if (status === 410)
|
|
86
|
+
setPhase('expired');
|
|
87
|
+
else if (status === 409)
|
|
88
|
+
setPhase('stale');
|
|
89
|
+
else
|
|
90
|
+
setPhase('ready');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const app = info?.name ?? 'Shopify';
|
|
94
|
+
switch (phase) {
|
|
95
|
+
case 'loading':
|
|
96
|
+
return (_jsxs("div", { role: "status", className: "text-muted-foreground flex items-center justify-center gap-2 py-8 text-sm", children: [_jsx(Loading, { className: "size-4 shrink-0" }), t.raw('loading')] }));
|
|
97
|
+
case 'expired':
|
|
98
|
+
return (_jsx(StatusNotice, { icon: ClockCountdownIcon, tone: "warning", title: t.raw('expiredTitle'), description: t.raw('startAgain') }));
|
|
99
|
+
case 'invalid':
|
|
100
|
+
return (_jsx(StatusNotice, { icon: LinkBreakIcon, tone: "destructive", title: t.raw('invalidTitle'), description: t.raw('startAgain') }));
|
|
101
|
+
case 'stale':
|
|
102
|
+
return (_jsx(StatusNotice, { icon: LinkBreakIcon, tone: "warning", title: t.raw('staleTitle'), description: message ?? t.raw('startAgain') }));
|
|
103
|
+
case 'error':
|
|
104
|
+
return _jsx(StatusNotice, { icon: WarningCircleIcon, tone: "destructive", title: message ?? t.raw('failed') });
|
|
105
|
+
case 'done':
|
|
106
|
+
return (_jsx(StatusNotice, { icon: CheckIcon, tone: "success", title: t('connectedTitle', { app }), description: connectedAs ? t('connectedDescription', { store: connectedAs }) : t.raw('closeWindow') }));
|
|
107
|
+
}
|
|
108
|
+
const submitting = phase === 'submitting';
|
|
109
|
+
return (_jsxs("div", { className: "space-y-5", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-foreground text-sm font-medium", children: t.raw('stepsTitle') }), _jsxs("ol", { className: "text-muted-foreground list-decimal space-y-1 pl-4 text-xs text-pretty", children: [_jsx("li", { children: t.rich('step1', { link: (chunks) => _jsx(DashboardLink, { children: chunks }) }) }), _jsx("li", { children: t.raw('step2') }), _jsx("li", { children: t.raw('step3') }), _jsx("li", { children: t.raw('step4') })] }), _jsx("p", { className: "text-muted-foreground text-xs text-pretty", children: t.raw('sameOrganization') })] }), _jsxs(FieldGroup, { className: "gap-5", children: [_jsxs(Field, { className: "space-y-2", children: [_jsx(FieldLabel, { htmlFor: "app-form-store", className: "text-foreground", children: t.raw('storeLabel') }), _jsxs(InputGroup, { children: [_jsx(InputGroupInput, { id: "app-form-store", name: "store", autoComplete: "off", spellCheck: false,
|
|
110
|
+
// 16px below `sm` so iOS Safari does not zoom the page on focus.
|
|
111
|
+
className: "font-mono max-sm:text-base", placeholder: EXAMPLE_STORE_HANDLE, value: store, disabled: submitting, onChange: (e) => setStore(e.target.value) }), _jsx(InputGroupAddon, { align: "inline-end", children: SHOPIFY_HOST_SUFFIX })] })] }), _jsxs(Field, { className: "space-y-2", children: [_jsx(FieldLabel, { htmlFor: "app-form-client-id", className: "text-foreground", children: t.raw('clientIdLabel') }), _jsx(Input, { id: "app-form-client-id", name: "client_id", autoComplete: "off", spellCheck: false, className: "font-mono max-sm:text-base", value: clientId, disabled: submitting, onChange: (e) => setClientId(e.target.value) })] }), _jsxs(Field, { className: "space-y-2", children: [_jsx(FieldLabel, { htmlFor: "app-form-client-secret", className: "text-foreground", children: t.raw('clientSecretLabel') }), _jsx(Input, { id: "app-form-client-secret", name: "client_secret", type: "password", autoComplete: "off", spellCheck: false, className: "font-mono max-sm:text-base", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", value: clientSecret, disabled: submitting, onChange: (e) => setClientSecret(e.target.value), onKeyDown: (e) => {
|
|
112
|
+
if (e.key === 'Enter')
|
|
113
|
+
void submit();
|
|
114
|
+
} })] })] }), _jsxs("div", { className: "space-y-3", children: [message ? (_jsx("p", { role: "alert", className: "text-destructive text-xs text-pretty", children: message })) : null, _jsxs(Button, { type: "button", className: "w-full", onClick: submit, disabled: submitting, children: [submitting ? _jsx(Loading, { className: "size-4 shrink-0" }) : null, submitting ? t.raw('connecting') : t('submit', { app })] }), _jsx("p", { className: "text-muted-foreground text-center text-xs text-pretty", children: t.raw('secretNote') })] })] }));
|
|
115
|
+
}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import type { OutcomeTone } from '../../internal/outcomes/outcome-types.js';
|
|
2
|
+
import { type Icon } from '@phosphor-icons/react';
|
|
3
|
+
import { type ReactElement } from 'react';
|
|
4
|
+
/** Every phase that is not the form: tinted tile, title, one muted line. */
|
|
5
|
+
export declare function StatusNotice({ icon: Glyph, tone, title, description, }: {
|
|
6
|
+
icon: Icon;
|
|
7
|
+
tone: OutcomeTone;
|
|
8
|
+
title: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
}): ReactElement;
|
|
1
11
|
/**
|
|
2
12
|
* Renders the fields an agent-minted secret link asks for, and submits the
|
|
3
13
|
* values the human types. Shared by the public /secret-intake/[token] page and
|
|
@@ -14,7 +14,7 @@ import { Fragment, useEffect, useState } from 'react';
|
|
|
14
14
|
import { classifySetupLinkError, describeLinkExpiry, describeWithheldSecrets, splitTextLinks, } from './util.js';
|
|
15
15
|
import { setupLinkApiBase } from './api-base.js';
|
|
16
16
|
/** Every phase that is not the form: tinted tile, title, one muted line. */
|
|
17
|
-
function StatusNotice({ icon: Glyph, tone, title, description, }) {
|
|
17
|
+
export function StatusNotice({ icon: Glyph, tone, title, description, }) {
|
|
18
18
|
const tint = outcomeTint(tone);
|
|
19
19
|
return (_jsxs("div", { role: "status", className: "flex flex-col items-center gap-3 py-8 text-center", children: [_jsx("span", { className: cn('flex size-9 items-center justify-center rounded-sm ring-1', tint.ring, tint.bg), children: _jsx(Glyph, { weight: "fill", className: cn('size-5', tint.fg) }) }), _jsxs("div", { className: "max-w-xs space-y-1", children: [_jsx("p", { className: "text-foreground text-sm font-medium", children: title }), description ? (_jsx("p", { className: "text-muted-foreground text-xs text-pretty", children: description })) : null] })] }));
|
|
20
20
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
1
2
|
import type { Icon } from './kortix-icons.js';
|
|
2
3
|
import { type Icon as IconType } from '@phosphor-icons/react';
|
|
3
4
|
/**
|
|
@@ -79,6 +80,8 @@ export interface EntityAvatarProps {
|
|
|
79
80
|
icon?: Icon | IconType;
|
|
80
81
|
size?: EntityAvatarSize;
|
|
81
82
|
className?: string;
|
|
83
|
+
/** Shows on hover, through `Hint`: who or what this avatar stands for. */
|
|
84
|
+
hint?: ReactNode;
|
|
82
85
|
}
|
|
83
|
-
export declare function EntityAvatar({ label, glyph, emoji, icon: IconComponent, size, className, }: EntityAvatarProps): import("react").JSX.Element;
|
|
86
|
+
export declare function EntityAvatar({ hint, label, glyph, emoji, icon: IconComponent, size, className, }: EntityAvatarProps): import("react").JSX.Element;
|
|
84
87
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Hint } from './hint.js';
|
|
3
4
|
import { emojiTint } from './emoji-tint.js';
|
|
4
5
|
import { glyphFace } from './glyph-face.js';
|
|
5
6
|
import { glyphForeground, glyphTint } from './glyph-tint.js';
|
|
@@ -48,12 +49,12 @@ function resolveGlyph(glyph) {
|
|
|
48
49
|
const GlyphComponent = glyphFace(glyph.name);
|
|
49
50
|
return GlyphComponent ? { GlyphComponent, color: glyph.color } : null;
|
|
50
51
|
}
|
|
51
|
-
export function EntityAvatar({ label, glyph, emoji, icon: IconComponent, size = 'md', className, }) {
|
|
52
|
+
export function EntityAvatar({ hint, label, glyph, emoji, icon: IconComponent, size = 'md', className, }) {
|
|
52
53
|
const sizes = SIZE_MAP[size];
|
|
53
54
|
const initial = (label?.trim()?.charAt(0) || '?').toUpperCase();
|
|
54
55
|
const chalk = chalkColors(`${label?.trim()}` || initial);
|
|
55
56
|
const resolvedGlyph = resolveGlyph(glyph);
|
|
56
|
-
|
|
57
|
+
const tile = (_jsx("span", { "data-slot": "entity-avatar",
|
|
57
58
|
// chalkColors() is an inline style, so it beats any class a caller
|
|
58
59
|
// passes. A glyph or an emoji is already the colour — sitting either on
|
|
59
60
|
// a saturated hash-derived pastel reads as noise, and in dark mode that
|
|
@@ -103,4 +104,5 @@ export function EntityAvatar({ label, glyph, emoji, icon: IconComponent, size =
|
|
|
103
104
|
// before the label that says the same thing. Same treatment as the
|
|
104
105
|
// picker trigger in packages/ui/src/workspace/projects/modal/project-icon-field.tsx.
|
|
105
106
|
_jsx("span", { "aria-hidden": true, className: "leading-none", children: emoji })) : IconComponent ? (_jsx(IconComponent, { className: sizes.icon })) : (initial) }));
|
|
107
|
+
return hint ? (_jsx(Hint, { side: "top", sideOffset: 6, alignOffset: 0, label: hint, children: tile })) : (tile);
|
|
106
108
|
}
|
|
@@ -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;
|
|
@@ -42,6 +42,8 @@ export interface UserAvatarProps {
|
|
|
42
42
|
className?: string;
|
|
43
43
|
ring?: boolean;
|
|
44
44
|
variant?: 'default' | 'primary';
|
|
45
|
+
/** Shows on hover, through `Hint`: usually the person's name or email. */
|
|
46
|
+
hint?: React.ReactNode;
|
|
45
47
|
}
|
|
46
|
-
export declare function UserAvatar({ email, name, avatarUrl, size, variant, className, ring, }: UserAvatarProps): React.JSX.Element;
|
|
48
|
+
export declare function UserAvatar({ hint, email, name, avatarUrl, size, variant, className, ring, }: UserAvatarProps): React.JSX.Element;
|
|
47
49
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Hint } from './hint.js';
|
|
3
4
|
import { Avatar, AvatarFallback, AvatarImage } from './avatar.js';
|
|
4
5
|
import { cn } from '../lib/utils.js';
|
|
5
6
|
import { AVATAR_INK, avatarFill } from '../_vendor/kortix-shared.js';
|
|
@@ -34,13 +35,14 @@ const SIZE_MAP = {
|
|
|
34
35
|
lg: { box: 'size-10', text: 'text-sm', initials: 2 },
|
|
35
36
|
xl: { box: 'size-14', text: 'text-base', initials: 2 },
|
|
36
37
|
};
|
|
37
|
-
export function UserAvatar({ email, name, avatarUrl, size = 'md', variant = 'default', className, ring = false, }) {
|
|
38
|
+
export function UserAvatar({ hint, email, name, avatarUrl, size = 'md', variant = 'default', className, ring = false, }) {
|
|
38
39
|
const sizes = SIZE_MAP[size] ?? SIZE_MAP.md;
|
|
39
40
|
const initials = React.useMemo(() => initialsFromIdentity(name ?? undefined, email || '').slice(0, sizes.initials), [name, email, sizes.initials]);
|
|
40
41
|
// Avatar v2: a solid fill keyed on the email, so a rename keeps the colour.
|
|
41
42
|
const fill = avatarFill(email || name || '');
|
|
42
|
-
|
|
43
|
+
const avatar = (_jsxs(Avatar, { className: cn(sizes.box, 'shrink-0 overflow-hidden rounded-sm p-0 font-medium', ring && 'ring-background ring-2',
|
|
43
44
|
// A photo keeps an edge on a light page: a 1px inset hairline over it.
|
|
44
45
|
avatarUrl &&
|
|
45
46
|
'after:ring-border after:pointer-events-none after:absolute after:inset-0 after:rounded-sm after:ring-1 after:ring-inset', variant === 'primary' && 'bg-primary text-primary-foreground', className), children: [avatarUrl ? _jsx(AvatarImage, { src: avatarUrl, alt: name || email }) : null, _jsx(AvatarFallback, { className: cn('rounded-sm font-medium', sizes.text), style: { backgroundColor: fill, color: AVATAR_INK }, children: initials || '?' })] }));
|
|
47
|
+
return hint ? (_jsx(Hint, { side: "top", sideOffset: 6, alignOffset: 0, label: hint, children: avatar })) : (avatar);
|
|
46
48
|
}
|
|
@@ -145,7 +145,7 @@ export interface UiHostConfig {
|
|
|
145
145
|
terminalEnabled: boolean;
|
|
146
146
|
/**
|
|
147
147
|
* The Terminal panel offers "Connect from your machine": the deployment's
|
|
148
|
-
* CLI installer (`<origin>/install`) and `kortix sessions
|
|
148
|
+
* CLI installer (`<origin>/install`) and `kortix sessions shell`. A host
|
|
149
149
|
* that serves no installer and ships no Kortix CLI sets false, and the bar
|
|
150
150
|
* is hidden. Default: true.
|
|
151
151
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Agent } from '@kortix/sdk/react';
|
|
2
2
|
/** The label `AgentSelector` shows for a selection (null = nothing picked). Other surfaces that name the agent reuse it. */
|
|
3
3
|
export declare function agentDisplayLabel(agents: Agent[], selectedAgent: string | null): string;
|
|
4
|
-
export declare function AgentSelector({ agents, selectedAgent, onSelect, disabled, triggerLabelClassName, unavailableHint, open: controlledOpen, onOpenChange, }: {
|
|
4
|
+
export declare function AgentSelector({ agents, selectedAgent, onSelect, disabled, triggerLabelClassName, unavailableHint, open: controlledOpen, onOpenChange, appearance, }: {
|
|
5
5
|
agents: Agent[];
|
|
6
6
|
selectedAgent: string | null;
|
|
7
7
|
onSelect: (agentName: string | null) => void;
|
|
@@ -17,4 +17,10 @@ export declare function AgentSelector({ agents, selectedAgent, onSelect, disable
|
|
|
17
17
|
unavailableHint?: string | null;
|
|
18
18
|
open?: boolean;
|
|
19
19
|
onOpenChange?: (open: boolean) => void;
|
|
20
|
+
/**
|
|
21
|
+
* `chip` is the composer's borderless toolbar chip. `field` is a form field:
|
|
22
|
+
* the full-width `h-9` box every `SelectTrigger` in a form draws, so the
|
|
23
|
+
* picker reads as one more field instead of a chip inside a frame.
|
|
24
|
+
*/
|
|
25
|
+
appearance?: 'chip' | 'field';
|
|
20
26
|
}): import("react").JSX.Element;
|
|
@@ -7,8 +7,10 @@ import Hint from '../../primitives/hint.js';
|
|
|
7
7
|
import { useTranslations } from '../../i18n/index.js';
|
|
8
8
|
import { useUiHostConfig } from '../../providers/ui-provider.js';
|
|
9
9
|
import { cn } from '../../lib/utils.js';
|
|
10
|
+
import { TRIGGER_CARET_CLASS, triggerVariants } from '../../primitives/trigger-variants.js';
|
|
10
11
|
import { capitalizeWords, isMetaAgentName, META_AGENT_DISPLAY_NAME } from '../../_vendor/kortix-shared.js';
|
|
11
|
-
import { CaretDownIcon, CheckIcon
|
|
12
|
+
import { CaretDownIcon, CheckIcon } from '@phosphor-icons/react';
|
|
13
|
+
import { AgentMark } from '../../internal/components/agent-mark.js';
|
|
12
14
|
import { useEffect, useMemo, useState } from 'react';
|
|
13
15
|
import { composerSelectableAgents } from './composer-agent-access.js';
|
|
14
16
|
/**
|
|
@@ -36,7 +38,7 @@ export function agentDisplayLabel(agents, selectedAgent) {
|
|
|
36
38
|
return 'Agent';
|
|
37
39
|
return isMetaAgentName(name) ? META_AGENT_DISPLAY_NAME : capitalizeWords(name);
|
|
38
40
|
}
|
|
39
|
-
export function AgentSelector({ agents, selectedAgent, onSelect, disabled = false, triggerLabelClassName, unavailableHint, open: controlledOpen, onOpenChange, }) {
|
|
41
|
+
export function AgentSelector({ agents, selectedAgent, onSelect, disabled = false, triggerLabelClassName, unavailableHint, open: controlledOpen, onOpenChange, appearance = 'chip', }) {
|
|
40
42
|
const tHardcodedUi = useTranslations('hardcodedUi');
|
|
41
43
|
const t = useTranslations('threads');
|
|
42
44
|
const [internalOpen, setInternalOpen] = useState(false);
|
|
@@ -84,7 +86,13 @@ export function AgentSelector({ agents, selectedAgent, onSelect, disabled = fals
|
|
|
84
86
|
// matching the resolver's own first-accessible pre-selection.
|
|
85
87
|
const displayedName = resolveDisplayedAgentName(primaryAgents, selectedAgent);
|
|
86
88
|
const displayName = displayedName ? label(displayedName) : agentDisplayLabel(offered, selectedAgent);
|
|
87
|
-
|
|
89
|
+
// The trigger leads with the agent's pixel mark, the same face it has on the
|
|
90
|
+
// Agents page and in every row below. Only for a real agent: a placeholder
|
|
91
|
+
// name such as a trigger's `default` would get a mark of its own, and that
|
|
92
|
+
// face would belong to no agent.
|
|
93
|
+
const triggerMark = displayedName && primaryAgents.some((agent) => agent.name === displayedName) ? (_jsx(AgentMark, { slug: displayedName })) : null;
|
|
94
|
+
const field = appearance === 'field';
|
|
95
|
+
const fieldTrigger = (caret, props) => (_jsxs("button", { type: "button", className: cn(triggerVariants(), 'h-9 w-full'), ...props, children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [triggerMark, _jsx("span", { className: "truncate", children: displayName })] }), caret ? _jsx(CaretDownIcon, { className: cn(TRIGGER_CARET_CLASS, 'size-4') }) : null] }));
|
|
88
96
|
/**
|
|
89
97
|
* One agent: name, its type, its description, and a check when it is the
|
|
90
98
|
* one in use.
|
|
@@ -111,10 +119,7 @@ export function AgentSelector({ agents, selectedAgent, onSelect, disabled = fals
|
|
|
111
119
|
className: cn('items-start gap-2 py-2', isSelected && 'bg-primary/[0.06]'), onSelect: () => {
|
|
112
120
|
onSelect(agent.name);
|
|
113
121
|
setOpen(false);
|
|
114
|
-
}, children: [meta && (
|
|
115
|
-
// Meta is the platform agent that runs the others, so its row leads
|
|
116
|
-
// with its own mark instead of reading as one more peer in the list.
|
|
117
|
-
_jsx("span", { className: "bg-muted flex size-8 shrink-0 items-center justify-center rounded-md", children: _jsx(SparkleIcon, { weight: "fill", className: "text-foreground size-4" }) })), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [_jsx("span", { className: "text-foreground truncate text-sm font-medium", children: label(agent.name) }), meta && (_jsx(Badge, { variant: "outline", size: "xs", className: "shrink-0 font-normal", children: tHardcodedUi.raw('i18nComplete.textc78ffe195710') }))] }), agent.description && (_jsx("p", { className: "text-muted-foreground mt-0.5 truncate text-xs", children: agent.description }))] }), isSelected && (
|
|
122
|
+
}, children: [agent.description ? (_jsx(AgentMark, { slug: agent.name, size: 28, tile: 36 })) : (_jsx(AgentMark, { slug: agent.name, size: 14 })), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [_jsx("span", { className: "text-foreground truncate text-sm font-medium", children: label(agent.name) }), meta && (_jsx(Badge, { variant: "outline", size: "xs", className: "shrink-0 font-normal", children: tHardcodedUi.raw('i18nComplete.textc78ffe195710') }))] }), agent.description && (_jsx("p", { className: "text-muted-foreground truncate text-xs", children: agent.description }))] }), isSelected && (
|
|
118
123
|
// `mt-0.5` optically centres the 16px check against the first line
|
|
119
124
|
// rather than the top edge of the text box.
|
|
120
125
|
_jsx(CheckIcon, { className: "text-foreground mt-0.5 size-4 shrink-0" }))] }, agent.name));
|
|
@@ -135,7 +140,7 @@ export function AgentSelector({ agents, selectedAgent, onSelect, disabled = fals
|
|
|
135
140
|
* open — in exactly the state where its words matter.
|
|
136
141
|
*/
|
|
137
142
|
if (primaryAgents.length === 0) {
|
|
138
|
-
const trigger = (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", disabled: true, "aria-label": unavailableHint ?? undefined, className: "text-muted-foreground rounded-lg hit-area-y-3", children: [_jsx("span", { className: cn('max-w-[100px] truncate', triggerLabelClassName), children: displayName }), _jsx(CaretDownIcon, { className: "size-3" })] }));
|
|
143
|
+
const trigger = field ? (fieldTrigger(false, { disabled: true, 'aria-label': unavailableHint ?? undefined })) : (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", disabled: true, "aria-label": unavailableHint ?? undefined, className: "text-muted-foreground rounded-lg hit-area-y-3", children: [_jsx("span", { className: cn('max-w-[100px] truncate', triggerLabelClassName), children: displayName }), _jsx(CaretDownIcon, { className: "size-3" })] }));
|
|
139
144
|
if (!unavailableHint)
|
|
140
145
|
return trigger;
|
|
141
146
|
return (_jsx(Hint, { side: "top", label: unavailableHint, children: _jsx("span", { className: "inline-flex", children: trigger }) }));
|
|
@@ -148,7 +153,12 @@ export function AgentSelector({ agents, selectedAgent, onSelect, disabled = fals
|
|
|
148
153
|
* text every inert control on this rail wears, and the disabled attribute.
|
|
149
154
|
*/
|
|
150
155
|
if (disabled) {
|
|
151
|
-
|
|
156
|
+
if (field)
|
|
157
|
+
return fieldTrigger(false, { disabled: true });
|
|
158
|
+
return (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", disabled: true, className: "text-muted-foreground rounded-lg hit-area-y-3", children: [triggerMark, _jsx("span", { className: cn('max-w-[100px] truncate', triggerLabelClassName), children: displayName })] }));
|
|
152
159
|
}
|
|
153
|
-
return (_jsxs(CommandPopover, { open: open, onOpenChange: setOpen, children: [_jsx(CommandPopoverTrigger, { children: _jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": t('selectAgent'), className: "text-foreground/70 rounded-lg hit-area-y-3", children: [
|
|
160
|
+
return (_jsxs(CommandPopover, { open: open, onOpenChange: setOpen, children: [_jsx(CommandPopoverTrigger, { children: field ? (fieldTrigger(true, { 'aria-label': t('selectAgent') })) : (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": t('selectAgent'), className: "text-foreground/70 rounded-lg hit-area-y-3", children: [triggerMark, _jsx("span", { className: cn('max-w-[100px] truncate', triggerLabelClassName), children: displayName }), _jsx(CaretDownIcon, { className: cn('size-3 transition-transform duration-200 ease-out', open && 'rotate-180') })] })) }), _jsxs(CommandPopoverContent, { side: "top", align: "start", sideOffset: 8, className: "w-[min(300px,calc(100vw-1.5rem))]", children: [_jsx("div", { className: showSearch ? undefined : 'sr-only', children: _jsx(CommandInput, { compact: true, placeholder: tHardcodedUi.raw('componentsSessionSessionChatInput.line231JsxAttrPlaceholderSearchAgents'), value: search, onValueChange: setSearch }) }), _jsxs(CommandList, { className: "max-h-[320px]", children: [filteredPrimary.length > 0 && (
|
|
161
|
+
// `gap-px` between rows: a selected row and a hovered row next to it
|
|
162
|
+
// otherwise draw one merged grey block.
|
|
163
|
+
_jsxs(CommandGroup, { forceMount: true, className: "[&_[cmdk-group-items]]:flex [&_[cmdk-group-items]]:flex-col [&_[cmdk-group-items]]:gap-px", children: [filteredMeta.map((agent) => renderAgentItem(agent, true)), filteredMeta.length > 0 && filteredProject.length > 0 && (_jsx(CommandSeparator, { className: "my-1" })), filteredProject.map((agent) => renderAgentItem(agent, false))] })), filteredPrimary.length === 0 && search.trim() && (_jsxs("div", { className: "text-muted-foreground/50 py-8 text-center text-xs", children: [tHardcodedUi.raw('componentsSessionSessionChatInput.line273JsxTextNoAgentsMatchLdquo'), search.trim(), tHardcodedUi.raw('componentsSessionSessionChatInput.line273JsxTextRdquo')] }))] })] })] }));
|
|
154
164
|
}
|
|
@@ -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
|
}
|
|
@@ -13,6 +13,11 @@ export interface ContextBreakdown {
|
|
|
13
13
|
* compaction, whose usage is its summary reply's) with a positive token total.
|
|
14
14
|
* `sessionLogThreadMetrics(...).context` picks the same message, but prices the
|
|
15
15
|
* whole thread to do it.
|
|
16
|
+
*
|
|
17
|
+
* `reasoning` is a SUBSET of `output` (pi-ai `Usage`, and the same convention in
|
|
18
|
+
* OpenCode and the Kortix transcript): adding it again reads a session at 107%
|
|
19
|
+
* of a window the model has not filled, and hides the compaction that should
|
|
20
|
+
* have run. It stays in the breakdown for display, outside `total`.
|
|
16
21
|
*/
|
|
17
22
|
export declare function getLastAssistantTokenBreakdown(messages: SessionLogMessage[] | undefined): ContextBreakdown;
|
|
18
23
|
export declare function getLastAssistantTokenTotal(messages: SessionLogMessage[] | undefined): number;
|
|
@@ -22,7 +27,7 @@ export declare function contextTone(ratio: number): StatusTone;
|
|
|
22
27
|
export declare function getContextLimit(models: FlatModel[] | undefined, selectedModel: {
|
|
23
28
|
providerID: string;
|
|
24
29
|
modelID: string;
|
|
25
|
-
} | null | undefined): number;
|
|
30
|
+
} | null | undefined): number | null;
|
|
26
31
|
export declare function getSelectedModelName(models: FlatModel[] | undefined, selectedModel: {
|
|
27
32
|
providerID: string;
|
|
28
33
|
modelID: string;
|
|
@@ -34,7 +39,8 @@ export interface ContextReading {
|
|
|
34
39
|
}
|
|
35
40
|
export interface ContextUsage extends ContextReading {
|
|
36
41
|
breakdown: ContextBreakdown;
|
|
37
|
-
|
|
42
|
+
/** The model's context window; null when no source knows it. */
|
|
43
|
+
limit: number | null;
|
|
38
44
|
ratio: number;
|
|
39
45
|
modelName: string | null;
|
|
40
46
|
}
|
|
@@ -42,7 +48,3 @@ export declare function getContextUsage(messages: SessionLogMessage[] | undefine
|
|
|
42
48
|
providerID: string;
|
|
43
49
|
modelID: string;
|
|
44
50
|
} | null): ContextUsage;
|
|
45
|
-
export declare function getContextReading(messages: SessionLogMessage[] | undefined, models?: FlatModel[], selectedModel?: {
|
|
46
|
-
providerID: string;
|
|
47
|
-
modelID: string;
|
|
48
|
-
} | null): ContextReading;
|
|
@@ -10,6 +10,11 @@ const EMPTY_BREAKDOWN = {
|
|
|
10
10
|
* compaction, whose usage is its summary reply's) with a positive token total.
|
|
11
11
|
* `sessionLogThreadMetrics(...).context` picks the same message, but prices the
|
|
12
12
|
* whole thread to do it.
|
|
13
|
+
*
|
|
14
|
+
* `reasoning` is a SUBSET of `output` (pi-ai `Usage`, and the same convention in
|
|
15
|
+
* OpenCode and the Kortix transcript): adding it again reads a session at 107%
|
|
16
|
+
* of a window the model has not filled, and hides the compaction that should
|
|
17
|
+
* have run. It stays in the breakdown for display, outside `total`.
|
|
13
18
|
*/
|
|
14
19
|
export function getLastAssistantTokenBreakdown(messages) {
|
|
15
20
|
if (!messages)
|
|
@@ -25,7 +30,7 @@ export function getLastAssistantTokenBreakdown(messages) {
|
|
|
25
30
|
const output = t.output;
|
|
26
31
|
const reasoning = t.reasoning ?? 0;
|
|
27
32
|
const cache = t.cache_read + t.cache_write;
|
|
28
|
-
const total = input + output +
|
|
33
|
+
const total = input + output + cache;
|
|
29
34
|
if (total > 0)
|
|
30
35
|
return { input, output, reasoning, cache, total };
|
|
31
36
|
}
|
|
@@ -49,7 +54,8 @@ export function getContextLimit(models, selectedModel) {
|
|
|
49
54
|
if (model?.contextWindow && model.contextWindow > 0)
|
|
50
55
|
return model.contextWindow;
|
|
51
56
|
}
|
|
52
|
-
|
|
57
|
+
// Unknown: the meter shows what is used and no invented window.
|
|
58
|
+
return null;
|
|
53
59
|
}
|
|
54
60
|
export function getSelectedModelName(models, selectedModel) {
|
|
55
61
|
if (!selectedModel || !models)
|
|
@@ -77,7 +83,7 @@ export function formatContextCount(tokens) {
|
|
|
77
83
|
export function getContextUsage(messages, models, selectedModel) {
|
|
78
84
|
const breakdown = getLastAssistantTokenBreakdown(messages);
|
|
79
85
|
const limit = getContextLimit(models, selectedModel);
|
|
80
|
-
const ratio = breakdown.total > 0 ? Math.min(breakdown.total / limit, 1) : 0;
|
|
86
|
+
const ratio = limit && breakdown.total > 0 ? Math.min(breakdown.total / limit, 1) : 0;
|
|
81
87
|
return {
|
|
82
88
|
percent: Math.round(ratio * 100),
|
|
83
89
|
tone: contextTone(ratio),
|
|
@@ -87,7 +93,3 @@ export function getContextUsage(messages, models, selectedModel) {
|
|
|
87
93
|
modelName: getSelectedModelName(models, selectedModel),
|
|
88
94
|
};
|
|
89
95
|
}
|
|
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 {};
|