@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
|
@@ -22,7 +22,6 @@ import { useAgentAuthority, } from '../../access/access/agent-principals.js';
|
|
|
22
22
|
import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
|
|
23
23
|
import { getProjectDetail } from '@kortix/sdk';
|
|
24
24
|
import { contract, qk } from '@kortix/sdk/react';
|
|
25
|
-
import { ArrowUpRightIcon } from '@phosphor-icons/react';
|
|
26
25
|
import { useQuery } from '@tanstack/react-query';
|
|
27
26
|
export function AgentAuthorityCard({ projectId, agentName, grant, }) {
|
|
28
27
|
const t = useI18nTranslations('agentPrincipals');
|
|
@@ -39,7 +38,7 @@ export function AgentAuthorityCard({ projectId, agentName, grant, }) {
|
|
|
39
38
|
: authority.declared.length === 0
|
|
40
39
|
? t('declaredNone')
|
|
41
40
|
: t('declaredCount', { count: authority.declared.length });
|
|
42
|
-
return (_jsx("div", { "data-testid": "agent-authority", children: _jsxs(EditorSection, { title: t('title'), description: t('description'),
|
|
41
|
+
return (_jsx("div", { "data-testid": "agent-authority", children: _jsxs(EditorSection, { title: t('title'), description: t('description'), children: [_jsx(SettingRow, { label: t('kortixPermissions'), help: t('kortixPermissionsHelp', { agent: agentName }), children: _jsx("span", { className: "text-foreground block text-sm sm:text-right", children: declaredLabel }) }), _jsx(SettingRow, { label: t('ceiling'), help: t('ceilingHelp'), children: _jsxs("div", { className: "flex flex-wrap items-center gap-1.5 sm:justify-end", "data-testid": "agent-ceiling", children: [ceiling.kind === 'loading' ? (_jsx(Skeleton, { className: "h-5 w-32" })) : ceiling.kind === 'hidden' ? (_jsx("span", { className: "text-muted-foreground text-sm", children: t('ceilingHidden') })) : ceiling.kind === 'default' ? (_jsx("span", { className: "text-foreground text-sm", children: t('ceilingDefault') })) : (ceiling.roleKeys.map((key) => (_jsx(Badge, { variant: "outline", size: "sm", className: "capitalize", children: key }, key)))), accountId && ceiling.kind !== 'hidden' && ceiling.kind !== 'loading' ? (_jsx(Button, { asChild: true, variant: "secondary", size: "sm", children: _jsx(HubLink, { to: hubTarget(accountId, { tab: 'access-projects', project: projectId }), children: t('changeCeiling') }) })) : null] }) }), _jsx(SettingRow, { label: t('humanOnly'), help: t('humanOnlyHelp'), children: _jsx("span", { className: "text-foreground block text-sm sm:text-right", children: t('humanOnlyValue') }) }), _jsxs(SettingBlock, { label: t('effective'), help: t('effectiveHelp'), children: [_jsx("ul", { className: "flex flex-wrap gap-1", "data-testid": "agent-effective-permissions", children: authority.effective.map((permission) => (_jsx("li", { children: _jsx(Badge, { variant: "outline", size: "xs", className: "font-mono", children: permission }) }, permission))) }), authority.outsideCeiling.length > 0 ? (_jsx(DroppedList, { label: t('outsideCeiling'), permissions: authority.outsideCeiling, testId: "agent-outside-ceiling" })) : null, authority.humanOnly.length > 0 ? (_jsx(DroppedList, { label: t('droppedHumanOnly'), permissions: authority.humanOnly, testId: "agent-dropped-human-only" })) : null] })] }) }));
|
|
43
42
|
}
|
|
44
43
|
function DroppedList({ label, permissions, testId, }) {
|
|
45
44
|
return (_jsxs("div", { className: "space-y-1.5", "data-testid": testId, children: [_jsx("p", { className: "text-muted-foreground text-xs", children: label }), _jsx("ul", { className: "flex flex-wrap gap-1", children: permissions.map((permission) => (_jsx("li", { children: _jsx(Badge, { variant: "muted", size: "xs", className: "font-mono line-through", children: permission }) }, permission))) })] }));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* The v1 fallback cards of the agent page, plus the scope-checklist helpers
|
|
@@ -19,10 +19,10 @@ import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
|
|
|
19
19
|
*/
|
|
20
20
|
import { Badge } from '../../primitives/badge.js';
|
|
21
21
|
import { Button } from '../../primitives/button.js';
|
|
22
|
-
import { Label } from '../../primitives/label.js';
|
|
23
22
|
import Loading from '../../primitives/loading.js';
|
|
24
23
|
import { errorToast, successToast } from '../../primitives/toast.js';
|
|
25
24
|
import { ModelSelector } from '../../session/models/model-selector.js';
|
|
25
|
+
import { EditorSection, SettingRow } from '../sections/view/agent-editor-primitives.js';
|
|
26
26
|
import { PROJECT_ACTIONS } from '../../internal/lib/project-actions.js';
|
|
27
27
|
import { useProjectCan } from '../../internal/lib/use-project-can.js';
|
|
28
28
|
import { cn } from '../../internal/lib/utils.js';
|
|
@@ -51,7 +51,11 @@ export function AgentModel({ projectId, agentName }) {
|
|
|
51
51
|
? (models.find((x) => x.providerID === m.providerID && x.modelID === m.modelID)?.modelName ??
|
|
52
52
|
`${m.providerID}/${m.modelID}`)
|
|
53
53
|
: null;
|
|
54
|
-
|
|
54
|
+
const help = explicit ? (_jsxs(_Fragment, { children: [tI18nComplete.raw('text40124896532d'), ' ', _jsx("span", { className: "text-foreground font-medium", children: nameOf(explicit) }), "."] })) : (_jsxs(_Fragment, { children: [tI18nComplete.raw('text46e3bd02feee'), resolved ? (_jsxs(_Fragment, { children: [' ', "(", _jsx("span", { className: "text-foreground font-medium", children: nameOf(resolved) }), ")"] })) : null, tI18nComplete.raw('textd5bf3036405d')] }));
|
|
55
|
+
return (_jsx(EditorSection, { title: tI18nComplete.raw('text5e2c614c23f0'), description: help, lead: true, children: _jsx(SettingRow, { label: tI18nComplete.raw('text34455bf1d857'), children: canManage ? (_jsxs("div", { className: "flex items-center gap-2 sm:justify-end", children: [explicit ? (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
|
|
56
|
+
void defaults.clearAgentDefault(agentName);
|
|
57
|
+
successToast(tI18nComplete('text86781ce4c5b4', { value0: agentName }));
|
|
58
|
+
}, children: tI18nComplete.raw('textbc5b45ae7b60') })) : null, _jsx(ModelSelector, { models: models, providers: providers, selectedModel: explicit, onSelect: (m) => {
|
|
55
59
|
if (m) {
|
|
56
60
|
void defaults.setAgentDefault(agentName, m);
|
|
57
61
|
successToast(`${agentName} → ${nameOf(m)}`);
|
|
@@ -59,10 +63,7 @@ export function AgentModel({ projectId, agentName }) {
|
|
|
59
63
|
else {
|
|
60
64
|
void defaults.clearAgentDefault(agentName);
|
|
61
65
|
}
|
|
62
|
-
} })
|
|
63
|
-
void defaults.clearAgentDefault(agentName);
|
|
64
|
-
successToast(tI18nComplete('text86781ce4c5b4', { value0: agentName }));
|
|
65
|
-
}, children: tI18nComplete.raw('textbc5b45ae7b60') })) : null] })) : (_jsx(Badge, { variant: "outline", size: "sm", className: "font-mono", children: nameOf(resolved) ?? tI18nComplete.raw('text1f0a5246c481') })), _jsx("p", { className: "text-muted-foreground text-xs leading-relaxed text-pretty", children: explicit ? (_jsxs(_Fragment, { children: [tI18nComplete.raw('text40124896532d'), ' ', _jsx("span", { className: "text-foreground font-medium", children: nameOf(explicit) }), "."] })) : (_jsxs(_Fragment, { children: [tI18nComplete.raw('text46e3bd02feee'), resolved ? (_jsxs(_Fragment, { children: [' ', "(", _jsx("span", { className: "text-foreground font-medium", children: nameOf(resolved) }), ")"] })) : null, tI18nComplete.raw('textd5bf3036405d')] })) })] }));
|
|
66
|
+
} })] })) : (_jsx("span", { className: "text-foreground block truncate text-sm sm:text-right", children: nameOf(resolved) ?? tI18nComplete.raw('text1f0a5246c481') })) }) }));
|
|
66
67
|
}
|
|
67
68
|
/**
|
|
68
69
|
* An agent's manifest allowlist (`agents:` in kortix.yaml, or the legacy
|
|
@@ -125,13 +126,13 @@ function AgentScopeCard({ projectId, agentName, scope, }) {
|
|
|
125
126
|
});
|
|
126
127
|
// Non-managers get the read-only mirror (the old presentation).
|
|
127
128
|
if (!canManage) {
|
|
128
|
-
return (_jsxs("div", { className: "bg-popover space-y-3 rounded-md border px-4 py-4", children: [
|
|
129
|
+
return (_jsx(EditorSection, { title: tI18nComplete.raw('textec5ba0abb717'), bare: true, children: _jsxs("div", { className: "bg-popover space-y-3 rounded-md border px-4 py-4", children: [_jsxs("dl", { className: "space-y-2", children: [_jsx(ScopeRow, { label: tI18nComplete.raw('textd8707d411d99'), value: scope.env }), _jsx(ScopeRow, { label: tI18nComplete.raw('textc3d2e79ebdd0'), value: scope.connectors }), _jsx(ScopeRow, { label: tI18nComplete.raw('text8f12a6e05e6d'), value: scope.kortix_permissions ?? scope.kortix_cli })] }), _jsx("p", { className: "text-muted-foreground text-xs leading-relaxed text-pretty", children: tI18nComplete.raw('textac4c418e412f') })] }) }));
|
|
129
130
|
}
|
|
130
|
-
return (_jsxs("div", { className: "bg-popover space-y-4 rounded-md border px-4 py-4", children: [_jsx(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
return (_jsx(EditorSection, { title: tI18nComplete.raw('textec5ba0abb717'), bare: true, children: _jsxs("div", { className: "bg-popover space-y-4 rounded-md border px-4 py-4", children: [_jsx(ScopeEditor, { label: tI18nComplete.raw('textd8707d411d99'), allLabel: tI18nComplete.raw('text922315177668'), emptyLabel: tI18nComplete.raw('text82a37b8c2266'), value: env, options: secretOptions, match: "case-insensitive", onChange: setEnv }, `env-${editorNonce}`), _jsx(ScopeEditor, { label: tI18nComplete.raw('textc3d2e79ebdd0'), allLabel: tI18nComplete.raw('texta2e8754e4899'), emptyLabel: tI18nComplete.raw('text389fb5217392'), value: connectors, options: connectorOptions, onChange: setConnectors }, `connectors-${editorNonce}`), _jsx("dl", { children: _jsx(ScopeRow, { label: tI18nComplete.raw('text8f12a6e05e6d'), value: scope.kortix_permissions ?? scope.kortix_cli }) }), dirty ? (_jsxs("div", { className: "border-border/50 flex items-center justify-end gap-2 border-t pt-3", children: [_jsx(Button, { variant: "ghost", size: "sm", disabled: save.isPending, onClick: () => {
|
|
132
|
+
setEnv(scope.env);
|
|
133
|
+
setConnectors(scope.connectors);
|
|
134
|
+
setEditorNonce((n) => n + 1);
|
|
135
|
+
}, children: tI18nComplete.raw('textdaee7606b339') }), _jsxs(Button, { size: "sm", disabled: save.isPending, onClick: () => save.mutate(), children: [save.isPending && _jsx(Loading, { className: "size-3.5 shrink-0" }), tI18nComplete.raw('text1509f561f241')] })] })) : null] }) }));
|
|
135
136
|
}
|
|
136
137
|
/**
|
|
137
138
|
* The Secrets checklist, built from the project's secrets.
|
|
@@ -233,7 +234,7 @@ function ScopeEditor({ label, allLabel, emptyLabel, value, options, match = 'exa
|
|
|
233
234
|
return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("span", { className: "text-muted-foreground text-xs", children: label }), _jsx("div", { className: "border-border/70 inline-flex shrink-0 overflow-hidden rounded-md border", children: ['all', 'specific', 'none'].map((m) => (_jsx("button", { type: "button", onClick: () => pick(m), className: cn('px-2.5 py-1 text-xs capitalize', 'transition-[color,background-color] active:scale-[0.96]', mode === m
|
|
234
235
|
? 'bg-secondary text-foreground font-medium'
|
|
235
236
|
: 'text-muted-foreground hover:bg-muted/50'), children: m }, m))) })] }), mode === 'all' ? (_jsx("p", { className: "text-muted-foreground text-xs text-pretty", children: allLabel })) : null, mode === 'specific' &&
|
|
236
|
-
(rows.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-xs", children: emptyLabel })) : (_jsx("div", { className: "border-border/60 max-h-44 overflow-y-auto rounded-md border p-1", children: rows.map((o) => (_jsxs("button", { type: "button", "aria-pressed": o.selected, onClick: () => toggle(o.id), className: cn('flex w-full items-center gap-2 rounded px-2 py-1 text-left text-xs transition-colors', o.selected ? 'bg-secondary' : 'hover:bg-muted/50'), children: [_jsx("span", { className: cn('flex size-4 shrink-0 items-center justify-center rounded border', o.selected
|
|
237
|
+
(rows.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-xs", children: emptyLabel })) : (_jsx("div", { className: "border-border/60 max-h-44 overflow-y-auto rounded-md border p-1", children: rows.map((o) => (_jsxs("button", { type: "button", "aria-pressed": o.selected, onClick: () => toggle(o.id), className: cn('flex w-full items-center gap-2 rounded-sm px-2 py-1 text-left text-xs transition-colors', o.selected ? 'bg-secondary' : 'hover:bg-muted/50'), children: [_jsx("span", { className: cn('flex size-4 shrink-0 items-center justify-center rounded-sm border', o.selected
|
|
237
238
|
? 'border-foreground bg-foreground text-background'
|
|
238
239
|
: 'border-border/70'), children: o.selected && _jsx(Check, { className: "size-3" }) }), _jsx("span", { className: "min-w-0 flex-1 truncate font-mono", children: o.label }), o.hint ? (_jsx("span", { className: "text-muted-foreground max-w-[40%] shrink-0 truncate font-mono", children: o.hint })) : null, o.orphan && (_jsx("span", { className: "text-kortix-orange", children: tI18nComplete.raw('textffa63583dfa6') }))] }, o.id))) })))] }));
|
|
239
240
|
}
|
|
@@ -3,14 +3,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { uiRoutes } from '../../lib/routes.js';
|
|
4
4
|
import { EmptyState } from '../../primitives/empty-state.js';
|
|
5
5
|
import { ErrorState } from '../../primitives/error-state.js';
|
|
6
|
-
import { EditorSection } from '../sections/view/agent-editor-primitives.js';
|
|
6
|
+
import { EditorList, EditorSection } from '../sections/view/agent-editor-primitives.js';
|
|
7
7
|
import { Button } from '../../primitives/button.js';
|
|
8
8
|
import { Skeleton } from '../../primitives/skeleton.js';
|
|
9
9
|
import { Switch } from '../../primitives/switch.js';
|
|
10
10
|
import { errorToast } from '../../primitives/toast.js';
|
|
11
11
|
import { useDriveFolder, useFolderAccess, useProjectDrive, useShareFolder, useUnshareFolder, } from '../../files/drives/use-drives.js';
|
|
12
12
|
import { useTranslations } from '../../i18n/index.js';
|
|
13
|
-
import { HardDriveIcon } from '@phosphor-icons/react';
|
|
14
13
|
import { UiLink as Link } from '../../providers/ui-provider.js';
|
|
15
14
|
/**
|
|
16
15
|
* Which shared drives an agent gets, on the agent's page, beside Apps and
|
|
@@ -27,7 +26,7 @@ export function AgentDrivesSection({ projectId, agentName }) {
|
|
|
27
26
|
const drives = (root.data?.entries ?? []).filter((e) => e.type === 'dir' && !/^\/?Users(\/|$)/.test(e.path));
|
|
28
27
|
let body;
|
|
29
28
|
if (drive.isLoading || root.isLoading) {
|
|
30
|
-
body = (
|
|
29
|
+
body = (_jsx(EditorList, { children: [0, 1].map((i) => (_jsx("li", { className: "flex items-center gap-3 px-4 py-3", children: _jsx(Skeleton, { className: "h-4 w-40 rounded-sm" }) }, i))) }));
|
|
31
30
|
}
|
|
32
31
|
else if (drive.isError || root.isError || !drive.data) {
|
|
33
32
|
body = (_jsx(ErrorState, { size: "sm", title: t('loadError'), action: _jsx(Button, { variant: "outline", size: "sm", onClick: () => void root.refetch(), children: t('retry') }) }));
|
|
@@ -36,9 +35,9 @@ export function AgentDrivesSection({ projectId, agentName }) {
|
|
|
36
35
|
body = _jsx(EmptyState, { size: "sm", title: t('noCompanyDrives') });
|
|
37
36
|
}
|
|
38
37
|
else {
|
|
39
|
-
body = (_jsx(
|
|
38
|
+
body = (_jsx(EditorList, { children: drives.map((entry) => (_jsx(AgentDriveRow, { driveId: drive.data.driveId, entry: entry, agentName: agentName }, entry.path))) }));
|
|
40
39
|
}
|
|
41
|
-
return (_jsx(EditorSection, { title: t('customizeTitle'), description: t('accessAgentsCompanyHint'), trailing: driveHref ? (_jsx(Button, { asChild: true, variant: "
|
|
40
|
+
return (_jsx(EditorSection, { title: t('customizeTitle'), description: t('accessAgentsCompanyHint'), trailing: driveHref ? (_jsx(Button, { asChild: true, variant: "secondary", size: "sm", children: _jsx(Link, { href: driveHref, children: t('driveLabel') }) })) : undefined, lead: true, bare: true, children: body }));
|
|
42
41
|
}
|
|
43
42
|
function AgentDriveRow({ driveId, entry, agentName }) {
|
|
44
43
|
const t = useTranslations('drives');
|
|
@@ -63,5 +62,5 @@ function AgentDriveRow({ driveId, entry, agentName }) {
|
|
|
63
62
|
errorToast(t('shareFailed'));
|
|
64
63
|
}
|
|
65
64
|
};
|
|
66
|
-
return (_jsxs("li", { className: "
|
|
65
|
+
return (_jsxs("li", { className: "flex items-center gap-3 px-4 py-2.5", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-foreground truncate text-sm font-medium", children: entry.name }), _jsx("p", { className: "text-muted-foreground truncate font-mono text-xs", children: inherited ? t('everyoneInProject') : `/drives${path}` })] }), access.isLoading ? (_jsx(Skeleton, { className: "h-5 w-9 rounded-full" })) : (_jsx(Switch, { checked: !!own || inherited, disabled: !canManage || inherited || pending, onCheckedChange: (v) => void toggle(v), "aria-label": t('levelFor', { name: entry.name }) }))] }));
|
|
67
66
|
}
|
|
@@ -3,75 +3,79 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
3
3
|
import { uiRoutes } from '../../lib/routes.js';
|
|
4
4
|
import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
|
|
5
5
|
/**
|
|
6
|
-
* The agent editor's Access pages — Skills, Connectors, Secrets
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* select mode: its checkbox says whether the agent is granted the thing, its
|
|
11
|
-
* body opens the thing itself — `EntityDetailModal` for a skill,
|
|
12
|
-
* `ConnectorModal` for a connector, `ProjectSecretDialog` for a secret.
|
|
6
|
+
* The agent editor's Access pages — Skills, Connectors, Secrets, Apps. Each
|
|
7
|
+
* opens the SAME modal as the project-wide tab of the same name:
|
|
8
|
+
* `EntityDetailModal` for a skill, `ConnectorModal` for a connector,
|
|
9
|
+
* `ProjectSecretDialog` for a secret.
|
|
13
10
|
*
|
|
14
|
-
*
|
|
15
|
-
* `grant-mode-field.tsx
|
|
16
|
-
*
|
|
11
|
+
* Layout is the Settings page's: the pane title, one All · Pick · None row
|
|
12
|
+
* (`grant-mode-field.tsx`), then the catalog as one bordered list. The list
|
|
13
|
+
* stays on screen in every mode, so a person can still open any entry.
|
|
17
14
|
*/
|
|
18
15
|
import { Badge } from '../../primitives/badge.js';
|
|
19
16
|
import { Button } from '../../primitives/button.js';
|
|
17
|
+
import { Checkbox } from '../../primitives/checkbox.js';
|
|
18
|
+
import { Skeleton } from '../../primitives/skeleton.js';
|
|
20
19
|
import { EmptyState } from '../../primitives/empty-state.js';
|
|
21
20
|
import { ErrorState } from '../../primitives/error-state.js';
|
|
22
21
|
import { connectorDisplayName } from '../connectors/connector-filter.js';
|
|
23
|
-
import { ConnectorAppIcon,
|
|
22
|
+
import { ConnectorAppIcon, ConnectorStatusBadge, } from '../connectors/connector-identity.js';
|
|
24
23
|
import { ConnectorModal } from '../connectors/detail/connector-modal.js';
|
|
25
24
|
import { providerLabel } from '../connectors/provider-label.js';
|
|
26
|
-
import { CatalogCard } from '../shared/catalog/catalog-card.js';
|
|
27
|
-
import { CatalogGridSkeleton } from '../shared/catalog/catalog-grid.js';
|
|
28
25
|
import { detailSelection } from '../shared/detail-selection.js';
|
|
29
26
|
import { EntityDetailModal } from '../shared/entity/entity-modal.js';
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import { EditorSection } from '../sections/view/agent-editor-primitives.js';
|
|
27
|
+
import { ManageButton, RequiredConnectorToggle, } from '../sections/view/agent-editor-access-fields.js';
|
|
28
|
+
import { EditorList, EditorSection } from '../sections/view/agent-editor-primitives.js';
|
|
33
29
|
import { GrantModeField } from '../sections/view/grant-mode-field.js';
|
|
34
30
|
import { buildRows, ProjectSecretDialog, } from '../sections/view/secrets-view.js';
|
|
35
31
|
import { toArray } from '../shared/utils.js';
|
|
36
32
|
import { PROJECT_ACTIONS } from '../../internal/lib/project-actions.js';
|
|
33
|
+
import { cn } from '../../internal/lib/utils.js';
|
|
37
34
|
import { useProjectCan } from '../../internal/lib/use-project-can.js';
|
|
38
35
|
import { listConnectors, listProjectSecrets, } from '@kortix/sdk';
|
|
39
36
|
import { contract, qk, useProjectAccountId, useProjectApps, } from '@kortix/sdk/react';
|
|
40
|
-
import {
|
|
37
|
+
import { PlusIcon } from '@phosphor-icons/react';
|
|
41
38
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
42
39
|
import { useState } from 'react';
|
|
43
40
|
import { appGrantRows } from './agent-apps-grant.js';
|
|
44
41
|
/**
|
|
45
|
-
* All · Pick · None over
|
|
46
|
-
*
|
|
47
|
-
*
|
|
42
|
+
* All · Pick · None over the project's catalog, as one bordered list. The
|
|
43
|
+
* checkbox exists only in Pick mode, where it is the control; in All and
|
|
44
|
+
* None the mode row above already answers for every row. A granted id the
|
|
45
|
+
* catalog no longer has still renders, flagged, so a stale grant is visible
|
|
46
|
+
* rather than silently dropped.
|
|
48
47
|
*/
|
|
49
|
-
function GrantCatalog({ value, onChange,
|
|
48
|
+
function GrantCatalog({ value, onChange, items, isLoading, isError, onRetry, empty, trailingFor, }) {
|
|
50
49
|
const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
|
|
51
|
-
return (_jsx(GrantModeField, { value: value, onChange: onChange,
|
|
52
|
-
if (isLoading)
|
|
53
|
-
return _jsx(
|
|
50
|
+
return (_jsx(GrantModeField, { value: value, onChange: onChange, alwaysRender: true, children: ({ selected, toggle, mode }) => {
|
|
51
|
+
if (isLoading) {
|
|
52
|
+
return (_jsx(EditorList, { children: [0, 1, 2].map((i) => (_jsx("li", { className: "flex items-center gap-3 px-4 py-3", children: _jsx(Skeleton, { className: "h-4 w-40 rounded-sm" }) }, i))) }));
|
|
53
|
+
}
|
|
54
54
|
if (isError) {
|
|
55
55
|
return (_jsx(ErrorState, { size: "sm", title: tI18nComplete.raw('text3f2d97c61a7e'), action: onRetry ? (_jsx(Button, { variant: "outline", size: "sm", onClick: onRetry, children: tI18nComplete.raw('text942087cc2d41') })) : undefined }));
|
|
56
56
|
}
|
|
57
57
|
const known = new Set(items.map((i) => i.id));
|
|
58
|
-
const orphans = [...selected]
|
|
58
|
+
const orphans = [...selected]
|
|
59
|
+
.filter((id) => !known.has(id))
|
|
60
|
+
.map((id) => ({
|
|
61
|
+
id,
|
|
62
|
+
title: _jsx("span", { className: "font-mono", children: id }),
|
|
63
|
+
description: tI18nComplete.raw('textfecc36d012be'),
|
|
64
|
+
status: (_jsx(Badge, { variant: "destructive", size: "xs", children: tI18nComplete.raw('text6be36ca49ee8') })),
|
|
65
|
+
}));
|
|
59
66
|
if (items.length === 0 && orphans.length === 0)
|
|
60
67
|
return _jsx(_Fragment, { children: empty });
|
|
61
|
-
|
|
62
|
-
return (_jsxs("div", { className: "grid gap-2", children: [items.map((item) => (_jsx(CatalogCard, { ...item.card, trailing: _jsxs("span", { className: "flex items-center gap-1.5", children: [item.card.trailing, mode === 'pick' && selected.has(item.id) ? trailingFor?.(item.id) : null] }), onClick: item.onOpen, select: {
|
|
63
|
-
checked: checked(item.id),
|
|
64
|
-
onCheckedChange: () => toggle(item.id),
|
|
65
|
-
disabled: mode !== 'pick',
|
|
66
|
-
label: tI18nComplete('textd4170e74c3b0', { value0: item.id }),
|
|
67
|
-
} }, item.id))), orphans.map((id) => (_jsx(CatalogCard, { title: _jsx("span", { className: "font-mono", children: id }), description: tI18nComplete.raw('textfecc36d012be'), badges: _jsx(Badge, { variant: "destructive", size: "xs", children: tI18nComplete.raw('text6be36ca49ee8') }), select: {
|
|
68
|
-
checked: true,
|
|
69
|
-
onCheckedChange: () => toggle(id),
|
|
70
|
-
disabled: mode !== 'pick',
|
|
71
|
-
label: tI18nComplete('textd4170e74c3b0', { value0: id }),
|
|
72
|
-
} }, id)))] }));
|
|
68
|
+
return (_jsx(EditorList, { children: [...items, ...orphans].map((item) => (_jsx(GrantRow, { item: item, mode: mode, checked: selected.has(item.id), onToggle: () => toggle(item.id), checkLabel: tI18nComplete('textd4170e74c3b0', { value0: item.id }), trailing: mode === 'pick' && selected.has(item.id) ? trailingFor?.(item.id) : null }, item.id))) }));
|
|
73
69
|
} }));
|
|
74
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* One catalog row. The checkbox and the body are two sibling controls, never
|
|
73
|
+
* one nested in the other: the checkbox grants, the body opens the entity.
|
|
74
|
+
*/
|
|
75
|
+
function GrantRow({ item, mode, checked, onToggle, checkLabel, trailing, }) {
|
|
76
|
+
const body = (_jsxs(_Fragment, { children: [item.leading, _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "text-foreground block truncate text-sm font-medium", children: item.title }), item.description ? (_jsx("span", { className: "text-muted-foreground block truncate text-xs", children: item.description })) : null] })] }));
|
|
77
|
+
return (_jsxs("li", { className: cn('flex min-h-13 items-center gap-3 px-4 py-2 transition-colors duration-fast', item.onOpen && 'hover:bg-hover', mode === 'none' && 'opacity-60'), children: [mode === 'pick' ? (_jsx(Checkbox, { variant: "black", checked: checked, onCheckedChange: onToggle, "aria-label": checkLabel })) : null, item.onOpen ? (_jsx("button", { type: "button", onClick: item.onOpen, className: "focus-visible:ring-ring/50 flex min-w-0 flex-1 items-center gap-3 rounded-sm text-left focus-visible:ring-2 focus-visible:outline-none", children: body })) : (_jsx("span", { className: "flex min-w-0 flex-1 items-center gap-3", children: body })), item.status || trailing ? (_jsxs("span", { className: "flex shrink-0 items-center gap-2", children: [item.status, trailing] })) : null] }));
|
|
78
|
+
}
|
|
75
79
|
// ─── Skills ──────────────────────────────────────────────────────────────
|
|
76
80
|
export function SkillsGrantPage({ projectId, config, editor, }) {
|
|
77
81
|
const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
|
|
@@ -82,11 +86,12 @@ export function SkillsGrantPage({ projectId, config, editor, }) {
|
|
|
82
86
|
record: skills.find((skill) => skill.path === selectedPath),
|
|
83
87
|
isSuccess: true,
|
|
84
88
|
});
|
|
85
|
-
return (_jsxs(_Fragment, { children: [_jsx(EditorSection, { title: tI18nComplete.raw('text66d0f523a379'), description: tI18nComplete.raw('text836cd8e8a791'), trailing: _jsx(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
return (_jsxs(_Fragment, { children: [_jsx(EditorSection, { title: tI18nComplete.raw('text66d0f523a379'), description: tI18nComplete.raw('text836cd8e8a791'), trailing: _jsx(ManageButton, { tab: "skills" }), lead: true, bare: true, children: _jsx(GrantCatalog, { value: editor.draft.skills, onChange: (v) => editor.set('skills', v), items: skills.map((skill) => ({
|
|
90
|
+
id: skill.name,
|
|
91
|
+
title: skill.name,
|
|
92
|
+
description: skill.description,
|
|
93
|
+
onOpen: () => setSelectedPath(skill.path),
|
|
94
|
+
})), empty: _jsx(EmptyState, { size: "sm", title: tI18nComplete.raw('text32ae9b80f832'), description: tI18nComplete.raw('text62d220bbfcc8') }) }) }), _jsx(EntityDetailModal, { projectId: projectId, entity: detail.record, kind: "skill", open: detail.open, isResolving: detail.isResolving, onOpenChange: (next) => {
|
|
90
95
|
if (!next)
|
|
91
96
|
setSelectedPath(null);
|
|
92
97
|
} })] }));
|
|
@@ -114,7 +119,7 @@ export function ConnectorsGrantPage({ projectId, editor, }) {
|
|
|
114
119
|
setDetailSlug(null);
|
|
115
120
|
const invalidate = () => queryClient.invalidateQueries({ queryKey: qk.project.connectors(projectId) });
|
|
116
121
|
const { draft, set } = editor;
|
|
117
|
-
return (_jsxs(_Fragment, { children: [_jsx(EditorSection, { title: tI18nComplete.raw('textc3d2e79ebdd0'), description: tI18nComplete.raw('textc174fc077197'), trailing: _jsx(
|
|
122
|
+
return (_jsxs(_Fragment, { children: [_jsx(EditorSection, { title: tI18nComplete.raw('textc3d2e79ebdd0'), description: tI18nComplete.raw('textc174fc077197'), trailing: _jsx(ManageButton, { tab: "connectors" }), lead: true, bare: true, children: _jsxs("div", { className: "space-y-3", children: [_jsx(GrantCatalog, { value: draft.connectors, onChange: (v) => {
|
|
118
123
|
set('connectors', v);
|
|
119
124
|
const required = draft.connectors_required ?? [];
|
|
120
125
|
const picked = new Set(Array.isArray(v) ? v : []);
|
|
@@ -124,15 +129,12 @@ export function ConnectorsGrantPage({ projectId, editor, }) {
|
|
|
124
129
|
if (kept.length !== required.length) {
|
|
125
130
|
set('connectors_required', kept.length ? kept : undefined);
|
|
126
131
|
}
|
|
127
|
-
},
|
|
132
|
+
}, isLoading: connectorsQuery.isLoading, isError: connectorsQuery.isError, onRetry: () => void connectorsQuery.refetch(), items: connectors.map((connector) => ({
|
|
128
133
|
id: connector.slug,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
badges: _jsx(ConnectorStatusBadge, { connector: connector }),
|
|
134
|
-
trailing: connectorSetupStatus(connector) === 'connected' ? (_jsx(ConnectorConnectedMark, {})) : undefined,
|
|
135
|
-
},
|
|
134
|
+
leading: _jsx(ConnectorAppIcon, { connector: connector, size: "sm" }),
|
|
135
|
+
title: connectorDisplayName(connector),
|
|
136
|
+
description: `${providerLabel(connector.provider)} · ${connector.actions.length} ${connector.actions.length === 1 ? 'action' : 'actions'}`,
|
|
137
|
+
status: _jsx(ConnectorStatusBadge, { connector: connector }),
|
|
136
138
|
onOpen: () => setDetailSlug(connector.slug),
|
|
137
139
|
})), trailingFor: (slug) => (_jsx(RequiredConnectorToggle, { active: draft.connectors_required?.includes(slug) === true, onToggle: () => {
|
|
138
140
|
const current = draft.connectors_required ?? [];
|
|
@@ -160,16 +162,13 @@ export function SecretsGrantPage({ projectId, editor }) {
|
|
|
160
162
|
open: false,
|
|
161
163
|
row: null,
|
|
162
164
|
});
|
|
163
|
-
return (_jsxs(_Fragment, { children: [_jsx(EditorSection, { title: tI18nComplete.raw('textd8707d411d99'), description: tI18nComplete.raw('textbb74cc7dce98'), trailing:
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
},
|
|
171
|
-
onOpen: () => setDialog({ open: true, row }),
|
|
172
|
-
})), empty: _jsx(EmptyState, { icon: KeyIcon, size: "sm", title: tI18nComplete.raw('text5aa40906d187'), description: tI18nComplete.raw('textfc7605717e58'), action: canWrite ? (_jsxs(Button, { variant: "outline", size: "sm", className: "gap-1.5", onClick: () => setDialog({ open: true, row: null }), children: [_jsx(PlusIcon, { className: "size-4" }), tI18nComplete.raw('text98aa52b34c8f')] })) : undefined }) }) }) }), _jsx(ProjectSecretDialog, { projectId: projectId, row: dialog.row, open: dialog.open, onOpenChange: (open) => setDialog((d) => ({ ...d, open })) })] }));
|
|
165
|
+
return (_jsxs(_Fragment, { children: [_jsx(EditorSection, { title: tI18nComplete.raw('textd8707d411d99'), description: tI18nComplete.raw('textbb74cc7dce98'), trailing: canWrite ? (_jsxs(Button, { variant: "secondary", size: "sm", className: "gap-1.5", onClick: () => setDialog({ open: true, row: null }), children: [_jsx(PlusIcon, { className: "size-4" }), tI18nComplete.raw('text18fdd549b2ed')] })) : undefined, lead: true, bare: true, children: _jsx(GrantCatalog, { value: editor.draft.secrets, onChange: (v) => editor.set('secrets', v), isLoading: secretsQuery.isLoading, isError: secretsQuery.isError, onRetry: () => void secretsQuery.refetch(), items: rows.map((row) => ({
|
|
166
|
+
id: row.identifier,
|
|
167
|
+
title: _jsx("span", { className: "font-mono", children: row.identifier }),
|
|
168
|
+
description: row.purpose ?? (row.key !== row.identifier ? `Env var ${row.key}` : null),
|
|
169
|
+
status: !row.configured || row.system ? (_jsxs(_Fragment, { children: [!row.configured ? (_jsx(Badge, { variant: "destructive", size: "xs", children: tI18nComplete.raw('text4fd0272a5628') })) : null, row.system ? (_jsx(Badge, { variant: "muted", size: "xs", children: tI18nComplete.raw('text6725e7bbcd28') })) : null] })) : undefined,
|
|
170
|
+
onOpen: () => setDialog({ open: true, row }),
|
|
171
|
+
})), empty: _jsx(EmptyState, { size: "sm", title: tI18nComplete.raw('text5aa40906d187'), description: tI18nComplete.raw('textfc7605717e58'), action: canWrite ? (_jsxs(Button, { variant: "outline", size: "sm", className: "gap-1.5", onClick: () => setDialog({ open: true, row: null }), children: [_jsx(PlusIcon, { className: "size-4" }), tI18nComplete.raw('text98aa52b34c8f')] })) : undefined }) }) }), _jsx(ProjectSecretDialog, { projectId: projectId, row: dialog.row, open: dialog.open, onOpenChange: (open) => setDialog((d) => ({ ...d, open })) })] }));
|
|
173
172
|
}
|
|
174
173
|
// ─── Apps ────────────────────────────────────────────────────────────────
|
|
175
174
|
/**
|
|
@@ -190,13 +189,12 @@ export function AppsGrantPage({ projectId, editor }) {
|
|
|
190
189
|
const t = useI18nTranslations('agentApps');
|
|
191
190
|
const appsQuery = useProjectApps(projectId);
|
|
192
191
|
const rows = appGrantRows(appsQuery.data);
|
|
193
|
-
return (_jsx(EditorSection, { title: t('title'), description: t('description'), trailing: _jsx(
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
})), empty: _jsx(EmptyState, { icon: GlobeIcon, size: "sm", title: t('emptyTitle'), description: t('emptyDescription') }) }), _jsx("p", { className: "text-muted-foreground text-xs text-pretty", children: t('grantNote') })] }) }));
|
|
192
|
+
return (_jsx(EditorSection, { title: t('title'), description: t('description'), trailing: _jsx(ManageButton, { href: (id) => uiRoutes().apps(id) }), lead: true, bare: true, children: _jsx("div", { className: "space-y-3", "data-testid": "agent-apps-grant", children: _jsx(GrantCatalog, { value: editor.draft.apps, onChange: (v) => editor.set('apps', v), isLoading: appsQuery.isLoading, isError: appsQuery.isError, onRetry: () => void appsQuery.refetch(), items: rows.map((row) => ({
|
|
193
|
+
id: row.id,
|
|
194
|
+
title: _jsx("span", { className: "font-mono", children: row.id }),
|
|
195
|
+
description: row.name === row.id ? null : row.name,
|
|
196
|
+
// The grant decides admission for a restricted or private App, the
|
|
197
|
+
// normal case. Only an App that admits agents without it is flagged.
|
|
198
|
+
status: row.needsGrant ? undefined : (_jsx(Badge, { variant: "muted", size: "xs", children: t('openWithoutGrant') })),
|
|
199
|
+
})), empty: _jsx(EmptyState, { size: "sm", title: t('emptyTitle'), description: t('emptyDescription') }) }) }) }));
|
|
202
200
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export declare const PROTOTYPE_ENABLED: boolean;
|
|
3
|
+
export declare const NAV_VARIANTS: readonly [{
|
|
4
|
+
readonly key: "rail";
|
|
5
|
+
}, {
|
|
6
|
+
readonly key: "tabs";
|
|
7
|
+
}, {
|
|
8
|
+
readonly key: "groups";
|
|
9
|
+
}, {
|
|
10
|
+
readonly key: "inline";
|
|
11
|
+
}];
|
|
12
|
+
export type NavVariant = (typeof NAV_VARIANTS)[number]['key'];
|
|
13
|
+
export declare function useNavVariant(): NavVariant;
|
|
14
|
+
export interface NavItem {
|
|
15
|
+
key: string;
|
|
16
|
+
label: string;
|
|
17
|
+
group: string;
|
|
18
|
+
}
|
|
19
|
+
interface NavProps {
|
|
20
|
+
groups: {
|
|
21
|
+
group: string;
|
|
22
|
+
items: NavItem[];
|
|
23
|
+
}[];
|
|
24
|
+
section: string;
|
|
25
|
+
href: (key: string) => string;
|
|
26
|
+
/** The current value per topic — "All", "2 picked", the model name. */
|
|
27
|
+
summaries?: Partial<Record<string, string>>;
|
|
28
|
+
}
|
|
29
|
+
export declare function GroupedUnderlineTabs({ groups, section, href }: NavProps): import("react").JSX.Element;
|
|
30
|
+
export declare function GroupSwitchTabs({ groups, section, href }: NavProps): import("react").JSX.Element;
|
|
31
|
+
export declare function InlineVerticalTabs({ groups, section, href, summaries, children, }: NavProps & {
|
|
32
|
+
children: ReactNode;
|
|
33
|
+
}): import("react").JSX.Element;
|
|
34
|
+
export declare function PrototypeSwitcher(): import("react").JSX.Element | null;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* PROTOTYPE — throwaway. Answers one question: what replaces the agent page's
|
|
5
|
+
* left rail? Rendered on the real agent page, switched with `?variant=`:
|
|
6
|
+
*
|
|
7
|
+
* rail — today's left rail (the baseline; no param)
|
|
8
|
+
* tabs — grouped underline tabs under the header, no rail
|
|
9
|
+
* groups — General / Access / Runtime switch + that group's tabs
|
|
10
|
+
* inline — vertical tabs beside the content column, each with its value
|
|
11
|
+
*
|
|
12
|
+
* Read-only navigation over the same `?section=` links; nothing here mutates.
|
|
13
|
+
* Once a variant wins, fold it into `agent-page.tsx` properly and delete this
|
|
14
|
+
* file and the switcher. Dev builds only (`PROTOTYPE_ENABLED`).
|
|
15
|
+
*/
|
|
16
|
+
import { CaretLeftIcon, CaretRightIcon } from '@phosphor-icons/react';
|
|
17
|
+
import { useCallback, useEffect } from 'react';
|
|
18
|
+
import { useTranslations } from '../../i18n/index.js';
|
|
19
|
+
import { cn } from '../../internal/lib/utils.js';
|
|
20
|
+
import { Tabs, TabsList, TabsTrigger } from '../../primitives/tabs.js';
|
|
21
|
+
import { UiLink as Link, useUiPathname as usePathname, useUiRouter as useRouter, useUiSearchParams as useSearchParams, } from '../../providers/ui-provider.js';
|
|
22
|
+
export const PROTOTYPE_ENABLED = process.env.NODE_ENV !== 'production';
|
|
23
|
+
export const NAV_VARIANTS = [{ key: 'rail' }, { key: 'tabs' }, { key: 'groups' }, { key: 'inline' }];
|
|
24
|
+
export function useNavVariant() {
|
|
25
|
+
const raw = useSearchParams().get('variant');
|
|
26
|
+
if (!PROTOTYPE_ENABLED)
|
|
27
|
+
return 'rail';
|
|
28
|
+
return NAV_VARIANTS.some((v) => v.key === raw) ? raw : 'rail';
|
|
29
|
+
}
|
|
30
|
+
/** `?section=` link that keeps `?variant=` so switching topics stays in the variant. */
|
|
31
|
+
function useVariantHref(href) {
|
|
32
|
+
const variant = useSearchParams().get('variant');
|
|
33
|
+
return (key) => (variant ? `${href(key)}&variant=${variant}` : href(key));
|
|
34
|
+
}
|
|
35
|
+
// ─── tabs: grouped underline tabs ──────────────────────────────────────────
|
|
36
|
+
export function GroupedUnderlineTabs({ groups, section, href }) {
|
|
37
|
+
const to = useVariantHref(href);
|
|
38
|
+
return (_jsx("nav", { className: "border-border/60 flex shrink-0 items-end gap-8 overflow-x-auto border-b px-5 pt-1", children: groups.map((g) => (_jsxs("div", { className: "flex shrink-0 flex-col gap-1", children: [_jsx("span", { className: "text-muted-foreground px-2 text-xs", children: g.group }), _jsx(Tabs, { value: section, children: _jsx(TabsList, { type: "underline", className: "h-auto gap-1 border-b-0", children: g.items.map((item) => (_jsx(TabsTrigger, { value: item.key, asChild: true, className: "w-fit flex-none px-2", children: _jsx(Link, { href: to(item.key), prefetch: true, children: item.label }) }, item.key))) }) })] }, g.group))) }));
|
|
39
|
+
}
|
|
40
|
+
// ─── groups: group switch + sub tabs ───────────────────────────────────────
|
|
41
|
+
export function GroupSwitchTabs({ groups, section, href }) {
|
|
42
|
+
const to = useVariantHref(href);
|
|
43
|
+
const active = groups.find((g) => g.items.some((i) => i.key === section)) ?? groups[0];
|
|
44
|
+
return (_jsxs("nav", { className: "border-border/60 flex shrink-0 items-center gap-5 overflow-x-auto border-b px-5 py-2", children: [_jsx(Tabs, { value: active?.group, children: _jsx(TabsList, { children: groups.map((g) => (_jsx(TabsTrigger, { value: g.group, asChild: true, className: "w-fit flex-none", children: _jsx(Link, { href: to(g.items[0].key), prefetch: true, children: g.group }) }, g.group))) }) }), _jsx(Tabs, { value: section, children: _jsx(TabsList, { type: "underline", className: "h-auto gap-1 border-b-0", children: active?.items.map((item) => (_jsx(TabsTrigger, { value: item.key, asChild: true, className: "w-fit flex-none px-2", children: _jsx(Link, { href: to(item.key), prefetch: true, children: item.label }) }, item.key))) }) })] }));
|
|
45
|
+
}
|
|
46
|
+
// ─── inline: vertical tabs beside the content, with values ────────────────
|
|
47
|
+
export function InlineVerticalTabs({ groups, section, href, summaries, children, }) {
|
|
48
|
+
const to = useVariantHref(href);
|
|
49
|
+
return (_jsxs("div", { className: "mx-auto flex w-full max-w-5xl items-start gap-14 px-4 py-10 pb-32 lg:py-14", children: [_jsx("nav", { className: "sticky top-0 flex w-52 shrink-0 flex-col gap-4", children: groups.map((g) => (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground px-2.5 pb-1 text-xs", children: g.group }), g.items.map((item) => {
|
|
50
|
+
const isActive = item.key === section;
|
|
51
|
+
return (_jsxs(Link, { href: to(item.key), prefetch: true, "aria-current": isActive ? 'page' : undefined, className: cn('flex items-center justify-between gap-3 rounded-md px-2.5 py-1.5 text-sm transition-none', isActive
|
|
52
|
+
? 'bg-active text-foreground font-medium'
|
|
53
|
+
: 'text-muted-foreground hover:bg-hover hover:text-foreground'), children: [_jsx("span", { className: "truncate", children: item.label }), summaries?.[item.key] ? (_jsx("span", { className: "text-muted-foreground shrink-0 text-xs font-normal tabular-nums", children: summaries[item.key] })) : null] }, item.key));
|
|
54
|
+
})] }, g.group))) }), _jsx("div", { className: "min-w-0 max-w-2xl flex-1", children: children })] }));
|
|
55
|
+
}
|
|
56
|
+
// ─── the floating switcher ────────────────────────────────────────────────
|
|
57
|
+
export function PrototypeSwitcher() {
|
|
58
|
+
const router = useRouter();
|
|
59
|
+
const pathname = usePathname();
|
|
60
|
+
const search = useSearchParams();
|
|
61
|
+
const current = useNavVariant();
|
|
62
|
+
const t = useTranslations('agentNavPrototype');
|
|
63
|
+
const labels = {
|
|
64
|
+
rail: t('variantRail'),
|
|
65
|
+
tabs: t('variantTabs'),
|
|
66
|
+
groups: t('variantGroups'),
|
|
67
|
+
inline: t('variantInline'),
|
|
68
|
+
};
|
|
69
|
+
const index = NAV_VARIANTS.findIndex((v) => v.key === current);
|
|
70
|
+
const go = useCallback((step) => {
|
|
71
|
+
const next = NAV_VARIANTS[(index + step + NAV_VARIANTS.length) % NAV_VARIANTS.length];
|
|
72
|
+
const params = new URLSearchParams(search.toString());
|
|
73
|
+
if (next.key === 'rail')
|
|
74
|
+
params.delete('variant');
|
|
75
|
+
else
|
|
76
|
+
params.set('variant', next.key);
|
|
77
|
+
router.replace(`${pathname}?${params.toString()}`, { scroll: false });
|
|
78
|
+
}, [index, pathname, router, search]);
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
const onKey = (event) => {
|
|
81
|
+
const target = event.target;
|
|
82
|
+
if (target?.closest('input, textarea, select, [contenteditable="true"]'))
|
|
83
|
+
return;
|
|
84
|
+
if (event.key === 'ArrowLeft')
|
|
85
|
+
go(-1);
|
|
86
|
+
if (event.key === 'ArrowRight')
|
|
87
|
+
go(1);
|
|
88
|
+
};
|
|
89
|
+
window.addEventListener('keydown', onKey);
|
|
90
|
+
return () => window.removeEventListener('keydown', onKey);
|
|
91
|
+
}, [go]);
|
|
92
|
+
if (!PROTOTYPE_ENABLED)
|
|
93
|
+
return null;
|
|
94
|
+
return (_jsx("div", { className: "pointer-events-none fixed inset-x-0 bottom-20 z-50 flex justify-center", children: _jsxs("div", { className: "bg-foreground text-background pointer-events-auto flex items-center gap-1 rounded-full p-1 shadow-lg", children: [_jsx("button", { type: "button", "aria-label": t('previousVariant'), onClick: () => go(-1), className: "flex size-7 items-center justify-center rounded-full hover:bg-background/15", children: _jsx(CaretLeftIcon, { className: "size-3.5" }) }), _jsxs("span", { className: "min-w-56 px-2 text-center text-xs tabular-nums", children: [String.fromCharCode(65 + index), " \u00B7 ", labels[NAV_VARIANTS[index].key]] }), _jsx("button", { type: "button", "aria-label": t('nextVariant'), onClick: () => go(1), className: "flex size-7 items-center justify-center rounded-full hover:bg-background/15", children: _jsx(CaretRightIcon, { className: "size-3.5" }) })] }) }));
|
|
95
|
+
}
|