@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
|
@@ -66,7 +66,7 @@ import { errorToast, successToast } from '../../primitives/toast.js';
|
|
|
66
66
|
import { EmptyState } from '../../primitives/empty-state.js';
|
|
67
67
|
import { ErrorState } from '../../primitives/error-state.js';
|
|
68
68
|
import { configEntitySourcePath } from '../sections/component/config-entity-source-path.js';
|
|
69
|
-
import { AgentConfigSections, agentConfigSectionKeys, agentEditorOptionQueries, DEFAULT_AGENT_CONFIG_SECTION, isAgentConfigSectionKey, registryGrantOptions, useAgentDraft, useAgentEditorOptions, useLocalizedAgentConfigCatalog, } from '../sections/view/agent-editor.js';
|
|
69
|
+
import { AgentConfigSections, agentConfigSectionKeys, agentEditorOptionQueries, DEFAULT_AGENT_CONFIG_SECTION, grantSummary, isAgentConfigSectionKey, registryGrantOptions, useAgentDraft, useAgentEditorOptions, useLocalizedAgentConfigCatalog, } from '../sections/view/agent-editor.js';
|
|
70
70
|
import { formatMode, toArray } from '../shared/utils.js';
|
|
71
71
|
import { editConfigPrompt, useConfigureThread, } from '../use-configure-thread.js';
|
|
72
72
|
import { agentConfigNeedsUpgrade, useAgentConfig, useUpdateAgentConfig, } from '../../internal/hooks/projects/use-agent-config.js';
|
|
@@ -86,11 +86,13 @@ import { FadedScrollArea } from '../../primitives/faded-scroll-area.js';
|
|
|
86
86
|
import { SETTINGS_SIDEBAR_WIDTH_PX } from '../../internal/account-hub/account-settings-shell.js';
|
|
87
87
|
import { capabilityTabHref } from '../../internal/lib/capability-tab-routes.js';
|
|
88
88
|
import { useIsMobile } from '../../internal/hooks/index.js';
|
|
89
|
-
import { EditorSectionStyleProvider } from '../sections/view/agent-editor-primitives.js';
|
|
89
|
+
import { EditorSection, EditorSectionStyleProvider } from '../sections/view/agent-editor-primitives.js';
|
|
90
|
+
import { SettingsSectionHeader } from '../../primitives/settings-section-header.js';
|
|
90
91
|
import { AgentDrivesSection } from './agent-drives-section.js';
|
|
91
92
|
import { AgentAuthorityCard } from './agent-authority-card.js';
|
|
92
93
|
import { AgentModel, AgentScope } from './agent-detail-aside.js';
|
|
93
94
|
import { AppsGrantPage, ConnectorsGrantPage, SecretsGrantPage, SkillsGrantPage, } from './agent-grant-pages.js';
|
|
95
|
+
import { GroupedUnderlineTabs, GroupSwitchTabs, InlineVerticalTabs, PrototypeSwitcher, useNavVariant, } from './agent-nav.prototype.js';
|
|
94
96
|
import { AgentPeopleSection } from './agent-people-section.js';
|
|
95
97
|
import { AgentShareControl } from './agent-share-control.js';
|
|
96
98
|
import { AgentTriggersSection } from './agent-triggers-section.js';
|
|
@@ -162,7 +164,7 @@ export function AgentPage({ projectId, agentName }) {
|
|
|
162
164
|
* the one scroller; the rail is the Settings rail
|
|
163
165
|
* (`project-settings-page.tsx`), row for row.
|
|
164
166
|
*/
|
|
165
|
-
function AgentPageFrame({ header, section, sections, sectionHref, pane, footer, }) {
|
|
167
|
+
function AgentPageFrame({ header, section, sections, sectionHref, pane, footer, summaries, }) {
|
|
166
168
|
const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
|
|
167
169
|
const isMobile = useIsMobile();
|
|
168
170
|
const localizedConfig = useLocalizedAgentConfigCatalog();
|
|
@@ -184,7 +186,18 @@ function AgentPageFrame({ header, section, sections, sectionHref, pane, footer,
|
|
|
184
186
|
// The Settings rail's row dialect (`project-settings-page.tsx`).
|
|
185
187
|
'gap-2 px-2.5 py-1 font-normal transition-none has-[>svg]:px-2.5', 'text-foreground data-[state=inactive]:text-foreground hover:bg-hover hover:text-foreground', 'data-[state=active]:bg-active data-[state=active]:font-medium', '[&_svg]:text-muted-foreground data-[state=active]:[&_svg]:text-foreground'), children: _jsxs(Link, { href: sectionHref(item.key), prefetch: true, children: [_jsx(SectionIcon, { className: "size-4 shrink-0" }), _jsx("span", { className: cn(!horizontal && 'truncate'), children: item.label })] }) }, item.key));
|
|
186
188
|
};
|
|
187
|
-
|
|
189
|
+
// PROTOTYPE: `?variant=` swaps the rail for one of the candidate navs.
|
|
190
|
+
const variant = useNavVariant();
|
|
191
|
+
const navProps = {
|
|
192
|
+
groups,
|
|
193
|
+
section,
|
|
194
|
+
href: (key) => sectionHref(key),
|
|
195
|
+
summaries,
|
|
196
|
+
};
|
|
197
|
+
if (!isMobile && variant !== 'rail') {
|
|
198
|
+
return (_jsxs("div", { className: "relative flex h-full min-h-0 flex-1 flex-col", children: [_jsx("div", { className: cn('shrink-0 px-5 py-3', variant === 'inline' && 'border-border/60 border-b'), children: header }), variant === 'tabs' ? _jsx(GroupedUnderlineTabs, { ...navProps }) : null, variant === 'groups' ? _jsx(GroupSwitchTabs, { ...navProps }) : null, _jsx("main", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto", children: variant === 'inline' ? (_jsx(InlineVerticalTabs, { ...navProps, children: pane })) : (_jsx("div", { className: "mx-auto w-full max-w-2xl px-4 py-10 pb-32 lg:py-14 lg:pb-32", children: pane })) }), footer, _jsx(PrototypeSwitcher, {})] }));
|
|
199
|
+
}
|
|
200
|
+
return (_jsxs("div", { className: "relative flex h-full min-h-0 flex-1 flex-col", children: [_jsx("div", { className: "border-border/60 shrink-0 border-b px-5 py-3", children: header }), _jsxs("div", { className: cn('min-h-0 flex-1', isMobile ? 'flex flex-col overflow-hidden' : 'grid overflow-hidden'), style: isMobile ? undefined : { gridTemplateColumns: `${SETTINGS_SIDEBAR_WIDTH_PX}px 1fr` }, children: [isMobile ? (_jsx("nav", { "aria-label": tI18nComplete.raw('text7be318295927'), className: "border-border/60 flex h-auto shrink-0 items-center border-b", children: _jsx(FadedScrollArea, { orientation: "horizontal", fadeColor: "from-background", className: "min-w-0 flex-1 py-2", children: _jsx(Tabs, { value: section, className: "w-fit", children: _jsx(TabsList, { orientation: "horizontal", className: "w-fit", children: items.map((item) => trigger(item, true)) }) }) }) })) : (_jsx("aside", { className: "flex min-h-0 flex-col border-r bg-inherit", children: _jsx("nav", { "aria-label": tI18nComplete.raw('text7be318295927'), className: "flex min-h-0 flex-1 [scrollbar-width:none] flex-col gap-4 overflow-y-auto px-2 pt-3 pb-2 [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden", children: _jsx(Tabs, { value: section, orientation: "vertical", className: "space-y-3", children: groups.map((g) => (_jsxs("div", { children: [showGroupLabels ? (_jsx("div", { className: "text-muted-foreground flex h-7 items-center px-2.5 text-xs font-medium", children: g.group })) : null, _jsx(TabsList, { orientation: "vertical", className: "w-full", children: g.items.map((item) => trigger(item, false)) })] }, g.group))) }) }) })), _jsx("main", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto", children: _jsx("div", { className: "mx-auto w-full max-w-2xl px-4 py-10 pb-32 lg:py-14 lg:pb-32", children: pane }) })] }), footer, _jsx(PrototypeSwitcher, {})] }));
|
|
188
201
|
}
|
|
189
202
|
function CenteredState({ children }) {
|
|
190
203
|
return (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: _jsx("div", { className: "mx-auto w-full max-w-3xl px-6 py-16", children: children }) }));
|
|
@@ -237,7 +250,7 @@ function AgentActions({ projectId, agent, config, canWrite, }) {
|
|
|
237
250
|
: null;
|
|
238
251
|
const makeDefault = useDefaultAgent(projectId);
|
|
239
252
|
const pathname = useUiPathname();
|
|
240
|
-
return (_jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [_jsx(AgentShareControl, { projectId: projectId, agentName: agent.name, peopleHref: `${pathname}?section=people` }), startBlocked ? (_jsx(Hint, { label: startBlocked, children: _jsx("span", { className: "inline-flex", children: _jsxs(Button, { size: "sm", disabled: true, children: [_jsx(PlayIcon, { weight: "fill", className: "size-3.5 shrink-0" }), tI18nComplete.raw('textb1c52ee3677d')] }) }) })) : (_jsxs(Button, { size: "sm", onClick: () => startSession({ create: { agent_name: agent.name } }), children: [_jsx(PlayIcon, { weight: "fill", className: "size-3.5 shrink-0" }), tI18nComplete.raw('textb1c52ee3677d')] })), canWrite ? (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "
|
|
253
|
+
return (_jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [_jsx(AgentShareControl, { projectId: projectId, agentName: agent.name, peopleHref: `${pathname}?section=people` }), startBlocked ? (_jsx(Hint, { label: startBlocked, children: _jsx("span", { className: "inline-flex", children: _jsxs(Button, { size: "sm", disabled: true, children: [_jsx(PlayIcon, { weight: "fill", className: "size-3.5 shrink-0" }), tI18nComplete.raw('textb1c52ee3677d')] }) }) })) : (_jsxs(Button, { size: "sm", onClick: () => startSession({ create: { agent_name: agent.name } }), children: [_jsx(PlayIcon, { weight: "fill", className: "size-3.5 shrink-0" }), tI18nComplete.raw('textb1c52ee3677d')] })), canWrite ? (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "secondary", size: "icon-sm", "aria-label": tI18nComplete.raw('textf8d46c2570e7'), children: _jsx(DotsThreeIcon, { className: "size-4" }) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsxs(DropdownMenuItem, { disabled: isDefault || agent.enabled === false || makeDefault.isPending, onSelect: () => makeDefault.mutate(agent.name), children: [_jsx(StarIcon, { className: "size-4" }), isDefault
|
|
241
254
|
? tI18nComplete.raw('texte8cb80e5c5cb')
|
|
242
255
|
: tI18nComplete.raw('texte8f43f910a4e')] }), _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuItem, { disabled: configure.pending, onSelect: () => setConfirmEditSource(true), children: tI18nComplete.raw('text4930821dd07b') })] })] })) : null, _jsx(ConfirmDialog, { open: confirmEditSource, onOpenChange: setConfirmEditSource, title: tI18nComplete.raw('text24ba36a8ec86'), description: tI18nComplete('text13324ae62b23', { value0: agent.name }), confirmLabel: tI18nComplete.raw('text0bce1a892dec'), cancelLabel: tI18nComplete.raw('text19766ed6ccb2'), onConfirm: () => {
|
|
243
256
|
setConfirmEditSource(false);
|
|
@@ -248,7 +261,7 @@ function AgentActions({ projectId, agent, config, canWrite, }) {
|
|
|
248
261
|
function AgentChips({ agent, config, size, }) {
|
|
249
262
|
const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
|
|
250
263
|
const { showMode, isDefault, disabled } = agentStatus(agent, config);
|
|
251
|
-
return (_jsxs("span", { className: "flex items-center gap-1.5", children: [showMode ? (_jsx(Badge, { variant: "
|
|
264
|
+
return (_jsxs("span", { className: "flex items-center gap-1.5", children: [showMode ? (_jsx(Badge, { variant: "secondary", size: size, children: formatMode(agent.mode ?? '') })) : null, isDefault ? (_jsx(Badge, { variant: "secondary", size: size, children: tI18nComplete.raw('text21b111cbfe6e') })) : null, disabled ? (_jsx(Badge, { variant: "muted", size: size, children: tI18nComplete.raw('text75081b593d15') })) : null] }));
|
|
252
265
|
}
|
|
253
266
|
/**
|
|
254
267
|
* Breadcrumb, avatar, name, chips, actions — the full header at the top of
|
|
@@ -257,7 +270,7 @@ function AgentChips({ agent, config, size, }) {
|
|
|
257
270
|
*/
|
|
258
271
|
function AgentHeader({ projectId, agent, config, canWrite, children, }) {
|
|
259
272
|
const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
|
|
260
|
-
return (_jsxs("header", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("nav", { "aria-label": tI18nComplete.raw('text2bd873d6c734'), className: "flex min-w-0 items-center gap-1.5 text-sm", children: [_jsx(Link, { href: capabilityTabHref(projectId, 'agent'), prefetch: true, className: "text-muted-foreground hover:text-foreground shrink-0 transition-colors", children: tI18nComplete.raw('text279b44d2ab4b') }), _jsx(CaretRightIcon, { "aria-hidden": true, className: "text-muted-foreground
|
|
273
|
+
return (_jsxs("header", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("nav", { "aria-label": tI18nComplete.raw('text2bd873d6c734'), className: "flex min-w-0 items-center gap-1.5 text-sm", children: [_jsx(Link, { href: capabilityTabHref(projectId, 'agent'), prefetch: true, className: "text-muted-foreground hover:text-foreground shrink-0 transition-colors", children: tI18nComplete.raw('text279b44d2ab4b') }), _jsx(CaretRightIcon, { "aria-hidden": true, className: "text-muted-foreground size-3.5 shrink-0" }), _jsx("h1", { className: "text-foreground truncate text-sm font-semibold", children: isMetaAgentName(agent.name) ? META_AGENT_DISPLAY_NAME : capitalizeWords(agent.name) }), _jsx(AgentChips, { agent: agent, config: config, size: "xs" })] }), _jsx(AgentActions, { projectId: projectId, agent: agent, config: config, canWrite: canWrite })] }), children] }));
|
|
261
274
|
}
|
|
262
275
|
// ─── Editable body ─────────────────────────────────────────────────────────
|
|
263
276
|
/** What a v1 project, or a reader without write, can still see. */
|
|
@@ -289,6 +302,22 @@ function EditableAgentPage({ projectId, agent, config, initial, schemaVersion, }
|
|
|
289
302
|
const sections = useMemo(() => agentConfigSectionKeys(schemaVersion).filter((k) => drivesOn || k !== 'drives'), [schemaVersion, drivesOn]);
|
|
290
303
|
const registryOptions = useMemo(() => registryGrantOptions({ manifest_raw: config.manifest_raw, agents: toArray(config.agents) }, agent.name), [config.manifest_raw, config.agents, agent.name]);
|
|
291
304
|
const section = useAgentSection(sections);
|
|
305
|
+
// PROTOTYPE: the `inline` nav variant shows each topic's current value.
|
|
306
|
+
const summaries = useMemo(() => {
|
|
307
|
+
const d = editor.draft;
|
|
308
|
+
const grant = (v) => grantSummary(v, tI18nComplete).label;
|
|
309
|
+
return {
|
|
310
|
+
skills: grant(d.skills),
|
|
311
|
+
connectors: grant(d.connectors),
|
|
312
|
+
secrets: grant(d.secrets),
|
|
313
|
+
apps: grant(d.apps),
|
|
314
|
+
actions: grant(d.kortix_permissions ?? d.kortix_cli),
|
|
315
|
+
subagents: grant(d.subagents),
|
|
316
|
+
commands: grant(d.commands),
|
|
317
|
+
extensions: grant(d.extensions),
|
|
318
|
+
model: editor.oc.model ? (editor.oc.model.split('/').pop() ?? editor.oc.model) : 'Default',
|
|
319
|
+
};
|
|
320
|
+
}, [editor.draft, editor.oc.model, tI18nComplete]);
|
|
292
321
|
const onSave = useCallback(async () => {
|
|
293
322
|
if (!editor.isDirty || update.isPending)
|
|
294
323
|
return;
|
|
@@ -306,7 +335,7 @@ function EditableAgentPage({ projectId, agent, config, initial, schemaVersion, }
|
|
|
306
335
|
// shared across topics and nothing is lost.
|
|
307
336
|
const leaveGuard = useUnsavedChangesGuard(editor.isDirty, pathname);
|
|
308
337
|
useSaveShortcut(onSave);
|
|
309
|
-
return (_jsx(AgentPageFrame, { header: _jsx(AgentHeader, { projectId: projectId, agent: agent, config: config, canWrite: true }), section: section, sections: sections, sectionHref: sectionHrefFor(pathname), pane: _jsx(AgentConfigSections, { section: section, editor: editor, options: options, skillsOptions: skillsOptions, registryOptions: registryOptions, skills: _jsx(SkillsGrantPage, { projectId: projectId, config: config, editor: editor }), connectors: _jsx(ConnectorsGrantPage, { projectId: projectId, editor: editor }), secrets: _jsx(SecretsGrantPage, { projectId: projectId, editor: editor }), apps: _jsx(AppsGrantPage, { projectId: projectId, editor: editor }), drives: _jsx(AgentDrivesSection, { projectId: projectId, agentName: agent.name }), overview: _jsx(OverviewPane, { description: editor.oc.description ?? '', onDescriptionChange: (next) => editor.setOc('description', next), descriptionHelp: editor.oc.mode === 'subagent'
|
|
338
|
+
return (_jsx(AgentPageFrame, { header: _jsx(AgentHeader, { projectId: projectId, agent: agent, config: config, canWrite: true }), section: section, sections: sections, sectionHref: sectionHrefFor(pathname), summaries: summaries, pane: _jsx(AgentConfigSections, { section: section, editor: editor, options: options, skillsOptions: skillsOptions, registryOptions: registryOptions, skills: _jsx(SkillsGrantPage, { projectId: projectId, config: config, editor: editor }), connectors: _jsx(ConnectorsGrantPage, { projectId: projectId, editor: editor }), secrets: _jsx(SecretsGrantPage, { projectId: projectId, editor: editor }), apps: _jsx(AppsGrantPage, { projectId: projectId, editor: editor }), drives: _jsx(AgentDrivesSection, { projectId: projectId, agentName: agent.name }), overview: _jsx(OverviewPane, { description: editor.oc.description ?? '', onDescriptionChange: (next) => editor.setOc('description', next), descriptionHelp: editor.oc.mode === 'subagent'
|
|
310
339
|
? 'Required. This is how other agents decide to call it.'
|
|
311
340
|
: 'One line on what this agent is for. Other agents read it when picking a subagent.', prompt: editor.oc.prompt ?? '', onPromptChange: (next) => editor.setOc('prompt', next) }), triggers: _jsx(AgentTriggersSection, { projectId: projectId, agentName: agent.name, defaultAgent: config.default_agent ?? config.open_code_default_agent }), people: _jsx(AgentPeopleSection, { projectId: projectId, agentName: agent.name }), authority: _jsx(AgentAuthorityCard, { projectId: projectId, agentName: agent.name, grant: editor.draft.kortix_permissions ?? editor.draft.kortix_cli }) }), footer: _jsxs(_Fragment, { children: [_jsx(SaveBar, { dirty: editor.isDirty, pending: update.isPending, onDiscard: () => setConfirmDiscard(true), onSave: onSave }), _jsx(ConfirmDialog, { open: confirmDiscard, onOpenChange: setConfirmDiscard, title: tI18nComplete.raw('text3b13192b9d88'), description: tI18nComplete('text8a36cce34f68', { value0: capitalizeWords(agent.name) }), confirmLabel: tI18nComplete.raw('texteb1a70e39274'), cancelLabel: tI18nComplete.raw('texte76fd2add010'), confirmVariant: "destructive", onConfirm: () => {
|
|
312
341
|
setConfirmDiscard(false);
|
|
@@ -318,34 +347,40 @@ function EditableAgentPage({ projectId, agent, config, initial, schemaVersion, }
|
|
|
318
347
|
}
|
|
319
348
|
/**
|
|
320
349
|
* The Overview topic — what the agent IS: one line of description and the
|
|
321
|
-
* instructions
|
|
350
|
+
* instructions. Laid out like a Settings pane: the pane title, then each
|
|
351
|
+
* field under its own section heading. A field is its own surface; no box
|
|
352
|
+
* wraps a heading and a field together.
|
|
322
353
|
*/
|
|
323
354
|
function OverviewPane({ description, onDescriptionChange, descriptionHelp, prompt, onPromptChange, }) {
|
|
324
355
|
const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
|
|
325
|
-
|
|
356
|
+
const tEditor = useI18nTranslations('agentEditor');
|
|
357
|
+
return (_jsxs(_Fragment, { children: [_jsx(SettingsSectionHeader, { title: tEditor('overviewTitle'), className: "pb-1" }), _jsx(EditorSection, { title: tI18nComplete.raw('text526e0087cc3f'), description: descriptionHelp, bare: true, children: _jsx(Textarea, { "aria-label": tI18nComplete.raw('text526e0087cc3f'), value: description, placeholder: tI18nComplete.raw('text446f4eabf99f'), minHeight: 44, className: "text-sm", onChange: (e) => onDescriptionChange(e.target.value) }) }), _jsx(InstructionsSection, { value: prompt, onChange: onPromptChange })] }));
|
|
326
358
|
}
|
|
327
359
|
/**
|
|
328
360
|
* The instructions — the agent's system prompt, and the thing that most IS
|
|
329
361
|
* the agent. Two views of one field: Edit is the textarea, Preview renders
|
|
330
362
|
* the markdown the way the agent's source file reads. It opens on Preview
|
|
331
363
|
* when there is something to read and on Edit when there is not; the toggle
|
|
332
|
-
* is
|
|
333
|
-
* pages use for their filters.
|
|
364
|
+
* is the segmented `TabsList` at the section heading's right edge.
|
|
334
365
|
*/
|
|
335
|
-
function
|
|
366
|
+
function InstructionsSection({ value, onChange, }) {
|
|
336
367
|
const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
|
|
337
368
|
const [view, setView] = useState(value.trim() ? 'preview' : 'edit');
|
|
338
|
-
return (
|
|
369
|
+
return (_jsx(EditorSection, { title: tI18nComplete.raw('text934652dce41d'), description: tI18nComplete.raw('text5fe8921b712c'), trailing: _jsx(Tabs, { value: view, onValueChange: (next) => setView(next), children: _jsxs(TabsList, { "aria-label": tI18nComplete.raw('texte75bb2d0d554'), children: [_jsx(TabsTrigger, { value: "edit", children: tI18nComplete.raw('text464c4ffd019e') }), _jsx(TabsTrigger, { value: "preview", children: tI18nComplete.raw('text324b134f57c7') })] }) }), bare: true, children: view === 'edit' ? (_jsx(Textarea, { "aria-label": tI18nComplete.raw('text934652dce41d'), value: value, placeholder: tI18nComplete.raw('text1436469ad07f'), minHeight: 420, autoFocus: value.trim().length > 0, className: "font-mono text-xs leading-relaxed", onChange: (e) => onChange(e.target.value) })) : (_jsx("div", { className: "bg-popover min-h-[420px] rounded-md border px-5 py-4", children: _jsx(MarkdownWithFrontmatter, { content: value }) })) }));
|
|
339
370
|
}
|
|
340
371
|
/**
|
|
341
372
|
* The save bar. Exists only while there is something to save — an always-on
|
|
342
|
-
* disabled pair of buttons is chrome that never earns its row. It
|
|
343
|
-
* the
|
|
344
|
-
*
|
|
373
|
+
* disabled pair of buttons is chrome that never earns its row. It floats at
|
|
374
|
+
* the bottom centre of the page in the Drive selection bar's shape
|
|
375
|
+
* (`project-files/components/drive-selection-bar.tsx`): a `bg-popover` card
|
|
376
|
+
* as wide as its content — the state as a title over one muted line, then
|
|
377
|
+
* Discard and Save as a spaced pair (a confirm is never glued to its cancel).
|
|
378
|
+
* It sits outside both scrollers, so it is reachable
|
|
379
|
+
* from any point in a long instruction set.
|
|
345
380
|
*/
|
|
346
381
|
function SaveBar({ dirty, pending, onDiscard, onSave, }) {
|
|
347
382
|
const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
|
|
348
|
-
return (_jsx(AnimatePresence, { initial: false, children: dirty ? (_jsx(m.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: 8 }, transition: { type: 'spring', duration: 0.3, bounce: 0 }, className: "
|
|
383
|
+
return (_jsx(AnimatePresence, { initial: false, children: dirty ? (_jsx(m.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: 8 }, transition: { type: 'spring', duration: 0.3, bounce: 0 }, className: "pointer-events-none absolute inset-x-0 bottom-4 z-30 flex justify-center px-4", children: _jsxs("div", { className: "bg-popover text-popover-foreground border-border pointer-events-auto flex max-w-full items-center gap-3 rounded-lg border p-2 pl-3.5 shadow-md", children: [_jsxs("div", { className: "min-w-0", "aria-live": "polite", children: [_jsx("p", { className: "text-foreground truncate text-sm font-medium", children: tI18nComplete.raw('textefa5855ff509') }), _jsx("p", { className: "text-muted-foreground truncate text-xs", children: tI18nComplete.raw('textc8134fda3694') })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [_jsx(Button, { variant: "secondary", size: "sm", onClick: onDiscard, disabled: pending, children: tI18nComplete.raw('texteb1a70e39274') }), _jsxs(Button, { variant: "secondary", size: "sm", className: "gap-1.5", onClick: onSave, disabled: pending, children: [pending ? _jsx(Loading, { className: "size-3.5 shrink-0" }) : null, tI18nComplete.raw('text1509f561f241'), _jsx(Kbd, { children: tI18nComplete.raw('text8cd003e6301b') })] })] })] }) }, "save-bar")) : null }));
|
|
349
384
|
}
|
|
350
385
|
/**
|
|
351
386
|
* Mod+S saves. The page is a document you edit, and every editor a person
|
|
@@ -445,10 +480,10 @@ function ReadOnlyAgentPage({ projectId, agent, config, canWrite, showUpgradeHint
|
|
|
445
480
|
});
|
|
446
481
|
const pathname = useUiPathname();
|
|
447
482
|
const section = useAgentSection(isPlatform ? PLATFORM_SECTIONS : READ_ONLY_SECTIONS);
|
|
448
|
-
const source = (
|
|
483
|
+
const source = (_jsx(EditorSection, { title: tI18nComplete.raw('text934652dce41d'), description: _jsx("span", { className: "font-mono", children: sourcePath }), lead: true, bare: true, children: _jsx("div", { className: "bg-popover overflow-hidden rounded-md border", children: _jsx(EntityFilePane, { path: sourcePath, content: fileQuery.data?.content, isLoading: fileQuery.isLoading, isError: fileQuery.isError, error: fileQuery.error, onRetry: () => {
|
|
449
484
|
void fileQuery.refetch();
|
|
450
|
-
}, skeletonRows: 4, errorTitle: tI18nComplete.raw('text2f2f540650bf') })
|
|
451
|
-
return (_jsx(AgentPageFrame, { header: _jsx(AgentHeader, { projectId: projectId, agent: agent, config: config, canWrite: canWrite && !isPlatform, children: agent.description ? (_jsx("p", { className: "text-muted-foreground max-w-2xl text-sm text-pretty", children: agent.description })) : null }), section: section, sections: isPlatform ? PLATFORM_SECTIONS : READ_ONLY_SECTIONS, sectionHref: sectionHrefFor(pathname), pane: _jsx(EditorSectionStyleProvider, { value: "panel", children: _jsxs("div", { className: "space-y-
|
|
485
|
+
}, skeletonRows: 4, errorTitle: tI18nComplete.raw('text2f2f540650bf') }) }) }));
|
|
486
|
+
return (_jsx(AgentPageFrame, { header: _jsx(AgentHeader, { projectId: projectId, agent: agent, config: config, canWrite: canWrite && !isPlatform, children: agent.description ? (_jsx("p", { className: "text-muted-foreground max-w-2xl text-sm text-pretty", children: agent.description })) : null }), section: section, sections: isPlatform ? PLATFORM_SECTIONS : READ_ONLY_SECTIONS, sectionHref: sectionHrefFor(pathname), pane: _jsx(EditorSectionStyleProvider, { value: "panel", children: _jsxs("div", { className: "space-y-8", children: [isPlatform ? (_jsx(InfoBanner, { tone: "info", title: tI18nComplete.raw('text85b7a59cdbd5'), children: tI18nComplete.raw('textd9c0c74b514b') })) : showUpgradeHint ? (_jsx(InfoBanner, { tone: "info", title: tI18nComplete.raw('textdc7ab144ca89'), children: tUpgrades.rich('agentEditorHint', {
|
|
452
487
|
code: (chunks) => _jsx("span", { className: "font-mono", children: chunks }),
|
|
453
|
-
}) })) : null, isPlatform ? null : section === 'overview' ? (_jsxs(_Fragment, { children: [source, _jsx(AgentScope, { projectId: projectId, agentName: agent.name, scope: agent.scope })] })) : section === 'people' ? (_jsx(AgentPeopleSection, { projectId: projectId, agentName: agent.name })) : section === 'triggers' ? (_jsx(AgentTriggersSection, { projectId: projectId, agentName: agent.name, defaultAgent: config.default_agent ?? config.open_code_default_agent })) : section === 'actions' ? (_jsx(AgentAuthorityCard, { projectId: projectId, agentName: agent.name, grant: agent.scope?.kortix_permissions ?? agent.scope?.kortix_cli })) : (_jsxs("div", { className: "space-y-
|
|
488
|
+
}) })) : null, isPlatform ? null : section === 'overview' ? (_jsxs(_Fragment, { children: [source, _jsx(AgentScope, { projectId: projectId, agentName: agent.name, scope: agent.scope })] })) : section === 'people' ? (_jsx(AgentPeopleSection, { projectId: projectId, agentName: agent.name })) : section === 'triggers' ? (_jsx(AgentTriggersSection, { projectId: projectId, agentName: agent.name, defaultAgent: config.default_agent ?? config.open_code_default_agent })) : section === 'actions' ? (_jsx(AgentAuthorityCard, { projectId: projectId, agentName: agent.name, grant: agent.scope?.kortix_permissions ?? agent.scope?.kortix_cli })) : (_jsxs("div", { className: "space-y-3", children: [_jsx(AgentModel, { projectId: projectId, agentName: agent.name }), _jsx("p", { className: "text-muted-foreground text-xs text-pretty", children: tI18nComplete.raw('text9dee2a1acfd9') })] }))] }) }) }));
|
|
454
489
|
}
|
|
@@ -37,7 +37,8 @@ import { EntityAvatar } from '../../primitives/entity-avatar.js';
|
|
|
37
37
|
import { Skeleton } from '../../primitives/skeleton.js';
|
|
38
38
|
import { errorToast, successToast } from '../../primitives/toast.js';
|
|
39
39
|
import { UserAvatar } from '../../primitives/user-avatar.js';
|
|
40
|
-
import {
|
|
40
|
+
import { EmptyState } from '../../primitives/empty-state.js';
|
|
41
|
+
import { EditorList, EditorSection } from '../sections/view/agent-editor-primitives.js';
|
|
41
42
|
import { AccessDialog, } from '../../access/access/access-dialog.js';
|
|
42
43
|
import { builtinRole, customRole, ROLE_NONE } from '../../access/access/role-select.js';
|
|
43
44
|
import { PROJECT_ACTIONS } from '../../internal/lib/project-actions.js';
|
|
@@ -133,20 +134,20 @@ export function AgentPeopleSection({ projectId, agentName, }) {
|
|
|
133
134
|
});
|
|
134
135
|
setEditOpen(true);
|
|
135
136
|
};
|
|
136
|
-
return (_jsxs(EditorSection, { title: tI18nComplete.raw('text82d9af69e1a1'), description: tI18nComplete.raw('text96feb5be077d'),
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
137
|
+
return (_jsxs(EditorSection, { title: tI18nComplete.raw('text82d9af69e1a1'), description: tI18nComplete.raw('text96feb5be077d'), trailing: accountId ? (_jsxs(Button, { variant: "secondary", size: "sm", className: "gap-1.5", onClick: () => setGrantOpen(true), children: [_jsx(PlusIcon, { className: "size-4" }), tI18nComplete.raw('text8693768c7e08')] })) : undefined, lead: true, bare: true, children: [_jsx("div", { children: grantsQuery.isLoading ? (_jsx(EditorList, { children: [0, 1].map((i) => (_jsxs("li", { className: "flex items-center gap-3 px-4 py-2.5", children: [_jsx(Skeleton, { className: "size-8 rounded-md" }), _jsx(Skeleton, { className: "h-4 w-40 rounded-sm" })] }, i))) })) : assigned.length === 0 ? (_jsx(EmptyState, { size: "sm", title: tI18nComplete.raw('text011c4e48df01') })) : (_jsx(EditorList, { children: assigned.map((g) => {
|
|
138
|
+
const member = g.principal_type === 'member'
|
|
139
|
+
? accessQuery.data?.members.find((m) => m.user_id === g.principal_id)
|
|
140
|
+
: undefined;
|
|
141
|
+
return (_jsxs("li", { className: "flex items-center gap-3 px-4 py-2.5", children: [g.principal_type === 'group' ? (_jsx(EntityAvatar, { icon: UsersIcon, size: "md" })) : g.principal_type === 'project' ? (_jsx(EntityAvatar, { icon: UsersThreeIcon, size: "md" })) : (_jsx(UserAvatar, { email: member?.email ?? g.principal_label, size: "md" })), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "text-foreground block truncate text-sm font-medium", children: g.principal_type === 'project'
|
|
142
|
+
? tSharing('everyone', { project: g.principal_label })
|
|
143
|
+
: g.principal_label }), _jsxs("span", { className: "text-muted-foreground block truncate text-xs", children: [g.principal_type === 'project'
|
|
144
|
+
? tSharing('everyoneMeta')
|
|
145
|
+
: g.principal_type === 'group'
|
|
146
|
+
? tSharing('groupMeta')
|
|
147
|
+
: tSharing('memberMeta'), g.expires_at
|
|
148
|
+
? tI18nComplete('textdc40ace03968', {
|
|
149
|
+
value0: new Date(g.expires_at).toLocaleDateString(),
|
|
150
|
+
})
|
|
151
|
+
: '', g.orphaned ? tI18nComplete.raw('textc795f93da2e5') : ''] })] }), g.principal_type === 'project' ? (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setStopSharing(g), children: tSharing('removeEveryone') })) : (_jsxs(Button, { variant: "ghost", size: "sm", className: "gap-1.5", onClick: () => openEdit(g), disabled: !accountId, children: [_jsx(PencilSimpleIcon, { className: "size-3.5 shrink-0" }), tI18nComplete.raw('text464c4ffd019e')] }))] }, g.grant_id));
|
|
152
|
+
}) })) }), accountId ? (_jsx(AccessDialog, { open: grantOpen, onOpenChange: setGrantOpen, accountId: accountId, scope: { kind: 'project', projectId, projectName }, mode: { kind: 'grant' }, initialAgentIds: [agentName] })) : null, accountId && editTarget ? (_jsx(AccessDialog, { open: editOpen, onOpenChange: setEditOpen, accountId: accountId, scope: { kind: 'project', projectId, projectName }, mode: { kind: 'edit', principal: editTarget.principal, current: editTarget.current }, inheritedFrom: editTarget.inheritedFrom }, `${editTarget.principal.type}:${editTarget.principal.id}`)) : null, _jsx(ConfirmDialog, { open: stopSharing !== null, onOpenChange: (open) => !open && setStopSharing(null), title: tSharing('removeEveryoneTitle', { agent: agentName }), description: tSharing('removeEveryoneDescription', { agent: agentName }), confirmLabel: tSharing('removeEveryone'), confirmVariant: "destructive", isPending: stopSharingMutation.isPending, onConfirm: () => stopSharing && stopSharingMutation.mutate(stopSharing.grant_id) })] }));
|
|
152
153
|
}
|
|
@@ -28,7 +28,8 @@ import { Badge } from '../../primitives/badge.js';
|
|
|
28
28
|
import { Button } from '../../primitives/button.js';
|
|
29
29
|
import { ConfirmDialog } from '../../primitives/confirm-dialog.js';
|
|
30
30
|
import { Skeleton } from '../../primitives/skeleton.js';
|
|
31
|
-
import {
|
|
31
|
+
import { EmptyState } from '../../primitives/empty-state.js';
|
|
32
|
+
import { EditorList, EditorSection } from '../sections/view/agent-editor-primitives.js';
|
|
32
33
|
import { useProjectFeatureFlags } from '../../internal/lib/use-project-feature-flags.js';
|
|
33
34
|
import { cn } from '../../internal/lib/utils.js';
|
|
34
35
|
import { listProjectTriggers } from '@kortix/sdk';
|
|
@@ -89,10 +90,10 @@ export function AgentTriggersSection({ projectId, agentName, defaultAgent, }) {
|
|
|
89
90
|
setSelectedSlug(null);
|
|
90
91
|
},
|
|
91
92
|
});
|
|
92
|
-
return (_jsxs(EditorSection, { title: tI18nComplete.raw('texte62f2148a64d'), description: tI18nComplete.raw('text1a1e4d549e85'),
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
return (_jsxs(EditorSection, { title: tI18nComplete.raw('texte62f2148a64d'), description: tI18nComplete.raw('text1a1e4d549e85'), trailing: canCreate ? (_jsxs(Button, { variant: "secondary", size: "sm", className: "gap-1.5", onClick: () => setCreateOpen(true), children: [_jsx(PlusIcon, { className: "size-4" }), tI18nComplete.raw('text58fd308928dd')] })) : undefined, lead: true, bare: true, children: [_jsx("div", { children: triggersQuery.isLoading ? (_jsx(EditorList, { children: [0, 1].map((i) => (_jsxs("li", { className: "flex items-center gap-3 px-4 py-2.5", children: [_jsx(Skeleton, { className: "size-8 rounded-sm" }), _jsx(Skeleton, { className: "h-4 w-40 rounded-sm" })] }, i))) })) : mine.length === 0 ? (_jsx(EmptyState, { size: "sm", title: tI18nComplete.raw('textb254c6e02e1a') })) : (_jsx(EditorList, { children: mine.map((trigger) => {
|
|
94
|
+
const KindIcon = KIND_ICON[trigger.type] ?? TimerIcon;
|
|
95
|
+
return (_jsx("li", { children: _jsxs("button", { type: "button", onClick: () => setSelectedSlug(trigger.slug), className: cn('hover:bg-hover flex w-full items-center gap-3 px-4 py-2.5 text-left transition-colors duration-fast', 'focus-visible:bg-hover focus-visible:outline-none'), children: [_jsx("span", { className: cn('flex size-8 shrink-0 items-center justify-center rounded-sm', trigger.enabled ? 'bg-kortix-green/15' : 'bg-muted'), children: _jsx(KindIcon, { weight: "fill", className: cn('size-4', trigger.enabled ? 'text-kortix-green' : 'text-muted-foreground') }) }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "text-foreground block truncate text-sm font-medium", children: triggerName(trigger) }), _jsx("span", { className: "text-muted-foreground block truncate text-xs", children: describeWhen(trigger) })] }), !trigger.enabled ? (_jsx(Badge, { variant: "muted", size: "xs", children: tI18nComplete.raw('texte159b06187d3') })) : null] }) }, trigger.slug));
|
|
96
|
+
}) })) }), canCreate ? (_jsx(TriggerComposer, { projectId: projectId, open: createOpen, onOpenChange: setCreateOpen, initialAgent: agentName, onCreated: (slug) => {
|
|
96
97
|
setCreateOpen(false);
|
|
97
98
|
void invalidate();
|
|
98
99
|
// The composer toasts the creation; straight into the new entry, as the Triggers tab does.
|
|
@@ -46,6 +46,20 @@ export type CatalogEntry = (CatalogEntryFields & {
|
|
|
46
46
|
});
|
|
47
47
|
/** Native platform provider. Each member's paired machine is one of its accounts. */
|
|
48
48
|
export declare function computersCatalogEntry(tI18nComplete: UiTranslator): CatalogEntry;
|
|
49
|
+
/**
|
|
50
|
+
* The icon URL a Discover record's card loads.
|
|
51
|
+
*
|
|
52
|
+
* A share of the catalogue's records carry a `domain` and no `icon` — the
|
|
53
|
+
* upstream feed only holds a logo for entries it could scrape one for. The
|
|
54
|
+
* card for such a record used to fall straight to a generic globe glyph, which
|
|
55
|
+
* is exactly the "inconsistent or missing imagery" a connector catalogue
|
|
56
|
+
* should not show when the app's own favicon is one well-known URL away. The
|
|
57
|
+
* same favicon service the marketing connectors page and `FaviconAvatar`
|
|
58
|
+
* already use resolves it; where the domain is a pseudo-host (`*.local`, the
|
|
59
|
+
* apis.guru feed's placeholders) it answers the generic globe image, which
|
|
60
|
+
* reads the same as the glyph it replaced.
|
|
61
|
+
*/
|
|
62
|
+
export declare function discoverEntryIcon(connector: Pick<DiscoverConnector, 'icon' | 'domain'>): string | null;
|
|
49
63
|
export declare function catalogEntryFromDiscover(connector: DiscoverConnector): CatalogEntry;
|
|
50
64
|
export declare function catalogEntryFromEasyConnect(app: PipedreamApp & {
|
|
51
65
|
provider?: 'composio' | 'pipedream';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { faviconUrlForHostname } from '../../../lib/favicon.js';
|
|
1
2
|
import { POPULAR_SECTION } from './connector-categories.js';
|
|
2
3
|
/** Native platform provider. Each member's paired machine is one of its accounts. */
|
|
3
4
|
export function computersCatalogEntry(tI18nComplete) {
|
|
@@ -12,6 +13,22 @@ export function computersCatalogEntry(tI18nComplete) {
|
|
|
12
13
|
popularity: null,
|
|
13
14
|
};
|
|
14
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* The icon URL a Discover record's card loads.
|
|
18
|
+
*
|
|
19
|
+
* A share of the catalogue's records carry a `domain` and no `icon` — the
|
|
20
|
+
* upstream feed only holds a logo for entries it could scrape one for. The
|
|
21
|
+
* card for such a record used to fall straight to a generic globe glyph, which
|
|
22
|
+
* is exactly the "inconsistent or missing imagery" a connector catalogue
|
|
23
|
+
* should not show when the app's own favicon is one well-known URL away. The
|
|
24
|
+
* same favicon service the marketing connectors page and `FaviconAvatar`
|
|
25
|
+
* already use resolves it; where the domain is a pseudo-host (`*.local`, the
|
|
26
|
+
* apis.guru feed's placeholders) it answers the generic globe image, which
|
|
27
|
+
* reads the same as the glyph it replaced.
|
|
28
|
+
*/
|
|
29
|
+
export function discoverEntryIcon(connector) {
|
|
30
|
+
return connector.icon ?? faviconUrlForHostname(connector.domain);
|
|
31
|
+
}
|
|
15
32
|
export function catalogEntryFromDiscover(connector) {
|
|
16
33
|
return {
|
|
17
34
|
source: 'discover',
|
|
@@ -20,7 +37,7 @@ export function catalogEntryFromDiscover(connector) {
|
|
|
20
37
|
slug: connector.slug,
|
|
21
38
|
name: connector.name,
|
|
22
39
|
description: connector.description,
|
|
23
|
-
icon: connector
|
|
40
|
+
icon: discoverEntryIcon(connector),
|
|
24
41
|
categories: connector.categories,
|
|
25
42
|
popularity: connector.popularity,
|
|
26
43
|
};
|
|
@@ -10,6 +10,34 @@ export interface CatalogInstall {
|
|
|
10
10
|
pendingKey: string | null;
|
|
11
11
|
onInstall: (entry: CatalogEntry) => void;
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* A catalog favicon, or a neutral glyph tile when the record has none or its
|
|
15
|
+
* URL fails to load.
|
|
16
|
+
*
|
|
17
|
+
* A plain `<img>`, not `next/image`. These are third-party favicons on
|
|
18
|
+
* arbitrary hosts, so the loader was already bypassed with `unoptimized` — which
|
|
19
|
+
* left `fill` costing an absolutely-positioned child inside a `relative`
|
|
20
|
+
* wrapper, per card, for no optimisation in return. Native `loading="lazy"`
|
|
21
|
+
* defers every icon below the fold, which is most of them on a browse page.
|
|
22
|
+
*
|
|
23
|
+
* `width`/`height` are set so the box is reserved before the image arrives and
|
|
24
|
+
* the grid never reflows around a late favicon.
|
|
25
|
+
*
|
|
26
|
+
* The broken-image state is the fallback the null case already had: a logo
|
|
27
|
+
* URL that 404s used to render the browser's broken-image glyph inside the
|
|
28
|
+
* tile, which is worse than no imagery at all. `onError` swaps to the same
|
|
29
|
+
* glyph tile — the same fallback a record with no icon gets, so "missing" and
|
|
30
|
+
* "unavailable" look identical. Mirrors `ConnectorAppIcon`'s broken state.
|
|
31
|
+
*
|
|
32
|
+
* The latch is per URL (`failedUrl`, not a boolean): a URL that failed stays
|
|
33
|
+
* failed, while a swapped URL gets a fresh try without any call-site key —
|
|
34
|
+
* merge-search swaps a merged twin's icon to the managed logo as catalogue
|
|
35
|
+
* pages stream in.
|
|
36
|
+
*/
|
|
37
|
+
export declare function ConnectorIcon({ icon, computer, }: {
|
|
38
|
+
icon: string | null;
|
|
39
|
+
computer?: boolean;
|
|
40
|
+
}): import("react").JSX.Element;
|
|
13
41
|
/**
|
|
14
42
|
* The catalogue body: one flat, paginated, searchable grid.
|
|
15
43
|
*
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useTranslations } from '../../../i18n/index.js';
|
|
4
4
|
import { GlobeIcon, MonitorIcon } from '@phosphor-icons/react';
|
|
5
|
-
import { memo } from 'react';
|
|
5
|
+
import { memo, useState } from 'react';
|
|
6
6
|
import { Badge } from '../../../primitives/badge.js';
|
|
7
7
|
import { Button } from '../../../primitives/button.js';
|
|
8
8
|
import Loading from '../../../primitives/loading.js';
|
|
@@ -43,7 +43,8 @@ function CatalogAffordance({ entry, connected, install, }) {
|
|
|
43
43
|
disabled: !install.ready || install.pendingKey !== null, "data-testid": "catalog-add" }));
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
|
-
* A catalog favicon, or a neutral glyph tile when the record has none
|
|
46
|
+
* A catalog favicon, or a neutral glyph tile when the record has none or its
|
|
47
|
+
* URL fails to load.
|
|
47
48
|
*
|
|
48
49
|
* A plain `<img>`, not `next/image`. These are third-party favicons on
|
|
49
50
|
* arbitrary hosts, so the loader was already bypassed with `unoptimized` — which
|
|
@@ -53,12 +54,24 @@ function CatalogAffordance({ entry, connected, install, }) {
|
|
|
53
54
|
*
|
|
54
55
|
* `width`/`height` are set so the box is reserved before the image arrives and
|
|
55
56
|
* the grid never reflows around a late favicon.
|
|
57
|
+
*
|
|
58
|
+
* The broken-image state is the fallback the null case already had: a logo
|
|
59
|
+
* URL that 404s used to render the browser's broken-image glyph inside the
|
|
60
|
+
* tile, which is worse than no imagery at all. `onError` swaps to the same
|
|
61
|
+
* glyph tile — the same fallback a record with no icon gets, so "missing" and
|
|
62
|
+
* "unavailable" look identical. Mirrors `ConnectorAppIcon`'s broken state.
|
|
63
|
+
*
|
|
64
|
+
* The latch is per URL (`failedUrl`, not a boolean): a URL that failed stays
|
|
65
|
+
* failed, while a swapped URL gets a fresh try without any call-site key —
|
|
66
|
+
* merge-search swaps a merged twin's icon to the managed logo as catalogue
|
|
67
|
+
* pages stream in.
|
|
56
68
|
*/
|
|
57
|
-
function ConnectorIcon({ icon, computer = false }) {
|
|
58
|
-
|
|
69
|
+
export function ConnectorIcon({ icon, computer = false, }) {
|
|
70
|
+
const [failedUrl, setFailedUrl] = useState(null);
|
|
71
|
+
if (!icon || failedUrl === icon) {
|
|
59
72
|
return (_jsx("span", { className: "bg-card flex size-9 shrink-0 items-center justify-center rounded-sm", children: computer ? _jsx(MonitorIcon, { className: "size-5" }) : _jsx(GlobeIcon, { className: "size-5" }) }));
|
|
60
73
|
}
|
|
61
|
-
return (_jsx("span", { className: "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-sm", children: _jsx("img", { src: icon, alt: "", width: 36, height: 36, loading: "lazy", decoding: "async", referrerPolicy: "no-referrer", className: "size-9 object-contain" }) }));
|
|
74
|
+
return (_jsx("span", { className: "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-sm", children: _jsx("img", { src: icon, alt: "", width: 36, height: 36, loading: "lazy", decoding: "async", referrerPolicy: "no-referrer", className: "size-9 object-contain", onError: () => setFailedUrl(icon) }) }));
|
|
62
75
|
}
|
|
63
76
|
/**
|
|
64
77
|
* One catalogue card: icon and title on the bare page — no border, no
|
|
@@ -14,7 +14,7 @@ import { EntityAvatar } from '../../../primitives/entity-avatar.js';
|
|
|
14
14
|
import { Skeleton } from '../../../primitives/skeleton.js';
|
|
15
15
|
import { EmptyState } from '../../../primitives/empty-state.js';
|
|
16
16
|
import { ErrorState } from '../../../primitives/error-state.js';
|
|
17
|
-
import { appNameKey, catalogEntryConnectors, } from '../catalog/catalog-entry.js';
|
|
17
|
+
import { appNameKey, catalogEntryConnectors, discoverEntryIcon, } from '../catalog/catalog-entry.js';
|
|
18
18
|
import { listConnectCatalogPage, useConnectProviderStatus, } from '../catalog/use-catalog.js';
|
|
19
19
|
import { connectorDisplayName, connectorSummary, } from '../connector-filter.js';
|
|
20
20
|
import { appRefFromLocation, connectorHref, connectorsHref, } from '../connector-routes.js';
|
|
@@ -37,6 +37,22 @@ const SURFACE_ROW = 'flex items-center gap-3 px-4 py-3';
|
|
|
37
37
|
/** One bordered group of rows, divided. Shared by both lists on the page. */
|
|
38
38
|
const ROW_GROUP = 'bg-popover divide-y overflow-hidden rounded-md border';
|
|
39
39
|
const surfaceLabel = (kind) => (kind === 'openapi' ? 'OpenAPI' : kind.toUpperCase());
|
|
40
|
+
/**
|
|
41
|
+
* The app page's icon tile (`ConnectorAppIcon`, size `lg`): the resolved
|
|
42
|
+
* favicon, or the glyph tile when the record has none — and the same glyph
|
|
43
|
+
* tile when the URL fails to load, which used to render the browser's
|
|
44
|
+
* broken-image glyph beside the title. Mirrors the catalogue card's
|
|
45
|
+
* `ConnectorIcon`. The latch is per URL (`failedUrl`), so the late-arriving
|
|
46
|
+
* managed logo — which swaps the URL — gets a fresh try without a call-site
|
|
47
|
+
* key.
|
|
48
|
+
*/
|
|
49
|
+
function AppIcon({ icon, name }) {
|
|
50
|
+
const [failedUrl, setFailedUrl] = useState(null);
|
|
51
|
+
if (!icon || failedUrl === icon) {
|
|
52
|
+
return _jsx(EntityAvatar, { icon: GlobeIcon, size: "lg", label: name });
|
|
53
|
+
}
|
|
54
|
+
return (_jsx("span", { className: "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-sm", children: _jsx("img", { src: icon, alt: "", width: 36, height: 36, referrerPolicy: "no-referrer", className: "size-9 object-contain", onError: () => setFailedUrl(icon) }) }));
|
|
55
|
+
}
|
|
40
56
|
/**
|
|
41
57
|
* `/projects/<id>/customize/connectors/<app>` — one catalogue app: what it is,
|
|
42
58
|
* the one Install button, the connectors this project already has for it, and
|
|
@@ -148,9 +164,7 @@ export function AppPage({ projectId, appSegment }) {
|
|
|
148
164
|
infoCells.push({ key: 'runs', label: t('infoRunsOver'), value: primary.kind });
|
|
149
165
|
if (waysCount > 1)
|
|
150
166
|
infoCells.push({ key: 'ways', label: t('waysToConnect'), value: String(waysCount) });
|
|
151
|
-
return (_jsxs(DetailPageShell, { children: [_jsx(Button, { asChild: true, variant: "text", size: "sm", className: "-ml-1 w-fit px-0 has-[>svg]:px-0", children: _jsxs(Link, { href: connectorsHref(projectId), children: [_jsx(CaretLeftIcon, { className: "size-3.5 shrink-0" }), tI18nComplete.raw('textc3d2e79ebdd0')] }) }), _jsxs("header", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [resolved.icon ? (
|
|
152
|
-
// The connector page's icon tile (`ConnectorAppIcon`, size `lg`).
|
|
153
|
-
_jsx("span", { className: "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-sm", children: _jsx("img", { src: resolved.icon, alt: "", width: 36, height: 36, referrerPolicy: "no-referrer", className: "size-9 object-contain" }) })) : (_jsx(EntityAvatar, { icon: GlobeIcon, size: "lg", label: resolved.name })), _jsx("h1", { className: "text-foreground min-w-0 text-xl font-medium text-balance wrap-break-word", children: resolved.name })] }), primary ? (_jsx("div", { className: "shrink-0", children: installMenu('header', primary.target, 'default', resolved.name) })) : null] }), infoCells.length > 0 || resolved.description ? (_jsxs("div", { className: "bg-popover divide-border divide-y overflow-hidden rounded-md border", children: [infoCells.length > 0 ? (_jsx("dl", { className: "flex flex-wrap gap-x-8 gap-y-3 px-4 py-3", children: infoCells.map((cell) => (_jsxs("div", { className: "min-w-0 space-y-0.5", children: [_jsxs("dt", { className: "text-muted-foreground flex h-4 items-center justify-between gap-2 text-xs", children: [_jsx("span", { children: cell.label }), cell.labelAddon] }), _jsx("dd", { className: "text-foreground truncate text-sm font-medium", children: cell.value })] }, cell.key))) })) : null, resolved.description ? (_jsxs("div", { className: "space-y-1.5 px-4 py-4", children: [_jsx("h2", { className: "text-muted-foreground text-xs", children: t('overview') }), _jsx("p", { className: "text-foreground text-sm text-pretty", children: resolved.description })] })) : null] })) : null, inProject.length > 0 ? (_jsxs("section", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("h2", { className: "text-foreground text-sm font-medium", children: projectName ? t('installedIn', { project: projectName }) : t('installedHere') }), _jsx("p", { className: "text-muted-foreground text-xs", children: t('installedHint') })] }), _jsx("ul", { className: ROW_GROUP, children: inProject.map((connector) => {
|
|
167
|
+
return (_jsxs(DetailPageShell, { children: [_jsx(Button, { asChild: true, variant: "text", size: "sm", className: "-ml-1 w-fit px-0 has-[>svg]:px-0", children: _jsxs(Link, { href: connectorsHref(projectId), children: [_jsx(CaretLeftIcon, { className: "size-3.5 shrink-0" }), tI18nComplete.raw('textc3d2e79ebdd0')] }) }), _jsxs("header", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [_jsx(AppIcon, { icon: resolved.icon, name: resolved.name }), _jsx("h1", { className: "text-foreground min-w-0 text-xl font-medium text-balance wrap-break-word", children: resolved.name })] }), primary ? (_jsx("div", { className: "shrink-0", children: installMenu('header', primary.target, 'default', resolved.name) })) : null] }), infoCells.length > 0 || resolved.description ? (_jsxs("div", { className: "bg-popover divide-border divide-y overflow-hidden rounded-md border", children: [infoCells.length > 0 ? (_jsx("dl", { className: "flex flex-wrap gap-x-8 gap-y-3 px-4 py-3", children: infoCells.map((cell) => (_jsxs("div", { className: "min-w-0 space-y-0.5", children: [_jsxs("dt", { className: "text-muted-foreground flex h-4 items-center justify-between gap-2 text-xs", children: [_jsx("span", { children: cell.label }), cell.labelAddon] }), _jsx("dd", { className: "text-foreground truncate text-sm font-medium", children: cell.value })] }, cell.key))) })) : null, resolved.description ? (_jsxs("div", { className: "space-y-1.5 px-4 py-4", children: [_jsx("h2", { className: "text-muted-foreground text-xs", children: t('overview') }), _jsx("p", { className: "text-foreground text-sm text-pretty", children: resolved.description })] })) : null] })) : null, inProject.length > 0 ? (_jsxs("section", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("h2", { className: "text-foreground text-sm font-medium", children: projectName ? t('installedIn', { project: projectName }) : t('installedHere') }), _jsx("p", { className: "text-muted-foreground text-xs", children: t('installedHint') })] }), _jsx("ul", { className: ROW_GROUP, children: inProject.map((connector) => {
|
|
154
168
|
const tone = connectorStatusTone(connector);
|
|
155
169
|
return (_jsx("li", { children: _jsxs(Link, { href: connectorHref(projectId, connector.slug, { app }), className: "hover:bg-muted/50 flex items-center gap-3 px-4 py-3 transition-colors", children: [_jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "text-foreground block truncate text-sm font-medium", children: connectorDisplayName(connector) }), _jsx("span", { className: "text-muted-foreground block truncate text-xs", children: connectorSummary(connector, providerLabel(connector.provider)) })] }), _jsx("span", { className: "flex w-28 shrink-0 items-center", children: _jsx(ConnectorStatusBadge, { tone: tone }) }), _jsxs("span", { className: "text-muted-foreground flex w-16 shrink-0 items-center justify-end gap-1 text-xs", children: [t('open'), _jsx(CaretRightIcon, { "aria-hidden": true, className: "size-4" })] })] }) }, connector.slug));
|
|
156
170
|
}) })] })) : null, resolved.surfaces.length === 0 && resolved.references.length === 0 ? (_jsx(EmptyState, { icon: GlobeIcon, size: "sm", title: tI18nComplete.raw('text363579e1d66b'), description: tI18nComplete.raw('text4d286e47d580') })) : null, resolved.surfaces.length > 1 || resolved.references.length > 0 ? (_jsxs("section", { className: "space-y-3", children: [_jsx("h2", { className: "text-foreground text-sm font-medium", children: t('waysToConnect') }), _jsxs("ul", { className: ROW_GROUP, children: [resolved.surfaces.map((surface) => (_jsxs("li", { className: SURFACE_ROW, children: [_jsxs("span", { className: "flex min-w-0 flex-1 items-center gap-1.5", children: [_jsx("span", { className: "text-foreground truncate text-sm font-medium", children: surface.label }), surface.kind ? (_jsx(Badge, { variant: "muted", size: "xs", children: surface.kind })) : null] }), installMenu(surface.key, surface.target, 'secondary', surface.label)] }, surface.key))), resolved.references.map((reference) => (_jsxs("li", { className: SURFACE_ROW, children: [_jsxs("span", { className: "flex min-w-0 flex-1 items-center gap-1.5", children: [_jsx("span", { className: "text-foreground truncate text-sm font-medium", children: reference.label }), _jsx(Badge, { variant: "muted", size: "xs", children: reference.kind })] }), reference.href ? (_jsx(Button, { asChild: true, variant: "outline", size: "sm", className: "shrink-0", children: _jsxs("a", { href: reference.href, target: "_blank", rel: "noreferrer", children: [tI18nComplete.raw('text7af023c43013'), _jsx(ArrowSquareOutIcon, { className: "size-3.5 shrink-0" })] }) })) : (_jsx(Badge, { variant: "secondary", size: "sm", children: tI18nComplete.raw('textdf0453d185c4') }))] }, reference.key)))] })] })) : null, mcpApp ? (_jsx(AddMcpServerDialog, { projectId: projectId, app: mcpApp, open: true, onOpenChange: (open) => !open && setMcpApp(null), takenSlugs: connectors.map((connector) => connector.slug), onAdded: () => void connectorsQuery.refetch() }, mcpApp.url)) : null] }));
|
|
@@ -231,7 +245,7 @@ function useAppData(projectId, app) {
|
|
|
231
245
|
name: managed?.name ?? detail.item.name,
|
|
232
246
|
description: detail.item.description ?? managed?.description ?? null,
|
|
233
247
|
// The managed logo is sharp where the catalogue's favicon is not.
|
|
234
|
-
icon: managed?.imgSrc ?? detail.item
|
|
248
|
+
icon: managed?.imgSrc ?? discoverEntryIcon(detail.item),
|
|
235
249
|
surfaces: [
|
|
236
250
|
...installableVariants(detail.variants).map((variant, index) => ({
|
|
237
251
|
key: `${variant.kind}:${variant.id}`,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AdminConnector } from '@kortix/sdk';
|
|
2
2
|
interface ConnectorModalProps {
|
|
3
3
|
projectId: string;
|
|
4
4
|
/** The connector to show, or `null` when it has not resolved yet. `open` is
|
|
@@ -6,10 +6,8 @@ interface ConnectorModalProps {
|
|
|
6
6
|
connector: AdminConnector | null;
|
|
7
7
|
canWrite: boolean;
|
|
8
8
|
open: boolean;
|
|
9
|
-
/** Open on a selection whose record is still loading
|
|
10
|
-
*
|
|
11
|
-
* the modal is present from the first frame instead of appearing on its own
|
|
12
|
-
* once the list lands. */
|
|
9
|
+
/** Open on a selection whose record is still loading. Renders the shell so
|
|
10
|
+
* the modal is present from the first frame. */
|
|
13
11
|
isResolving?: boolean;
|
|
14
12
|
onOpenChange: (open: boolean) => void;
|
|
15
13
|
/** Refetch every authorization-derived query. Every mutation below calls it. */
|
|
@@ -18,15 +16,14 @@ interface ConnectorModalProps {
|
|
|
18
16
|
onRemoved: () => void;
|
|
19
17
|
}
|
|
20
18
|
/**
|
|
21
|
-
*
|
|
19
|
+
* One connector over the agent editor, without leaving it (a navigation would
|
|
20
|
+
* drop the agent's unsaved draft). The body IS the connector page
|
|
21
|
+
* (`ConnectorDetailView`): the same header, status strip, notice, tabs and
|
|
22
|
+
* split credential sheet, so the modal and the page cannot drift. The modal
|
|
23
|
+
* adds only a close button and keeps its tab in state instead of the URL.
|
|
22
24
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* Right: the active tab.
|
|
26
|
-
*
|
|
27
|
-
* `ConnectorModalBody` is keyed on `connector.slug` so picking a different card
|
|
28
|
-
* while the modal stays open resets the active tab without remounting
|
|
29
|
-
* `Modal`/`ModalContent` (which would replay the open animation).
|
|
25
|
+
* The view is keyed on `connector.slug` so picking a different connector while
|
|
26
|
+
* the modal stays open resets the tab without replaying the open animation.
|
|
30
27
|
*/
|
|
31
28
|
export declare function ConnectorModal({ projectId, connector, canWrite, open, isResolving, onOpenChange, onChanged, onRemoved, }: ConnectorModalProps): import("react").JSX.Element;
|
|
32
29
|
export {};
|