@kortix/ui 0.14.5 → 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/access/access/access-dialog.js +8 -5
- package/dist/access/access/copy-row.js +2 -2
- package/dist/access/sharing-picker.js +2 -1
- package/dist/browser/app-in-browser.js +5 -3
- package/dist/browser/browser-panel.js +6 -6
- package/dist/browser/sandbox-browser-chrome.d.ts +4 -1
- package/dist/browser/sandbox-browser-chrome.js +4 -3
- package/dist/customize/agents/agent-access.d.ts +20 -0
- package/dist/customize/agents/agent-access.js +17 -0
- 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 +73 -76
- 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 +59 -23
- package/dist/customize/agents/agent-people-section.js +18 -17
- package/dist/customize/agents/agent-triggers-section.js +6 -5
- package/dist/customize/agents/agents-page.js +18 -1
- package/dist/customize/connectors/add-mcp/add-mcp-server.js +4 -2
- 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/connector-routes.d.ts +3 -1
- package/dist/customize/connectors/connector-routes.js +18 -0
- package/dist/customize/connectors/connectors-page.js +23 -4
- package/dist/customize/connectors/detail/app-page.js +19 -5
- package/dist/customize/connectors/detail/connector-advanced-tools.js +4 -6
- 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/providers/provider-accordion.js +2 -1
- package/dist/customize/providers/provider-connect.js +2 -1
- package/dist/customize/sections/component/telegram-channel-row.js +1 -1
- package/dist/customize/sections/connector-connection-modal.js +11 -9
- package/dist/customize/sections/connectors-view.js +11 -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 +18 -21
- 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/rail-copy-context.js +5 -3
- 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/sandbox-template-form.js +9 -12
- package/dist/customize/settings/tabs/transfer-workspace-dialog.d.ts +4 -0
- package/dist/customize/settings/tabs/transfer-workspace-dialog.js +82 -0
- package/dist/customize/shared/capability-tabs.js +2 -1
- package/dist/customize/skills/skills-page.js +3 -5
- package/dist/customize/triggers/schedule/event-trigger-copy.js +3 -2
- package/dist/customize/triggers/schedule/schedule-builder.js +7 -4
- package/dist/customize/triggers/schedule/trigger-composer.js +5 -2
- package/dist/customize/triggers/schedule/when-event.js +1 -1
- package/dist/diff/diff-view.js +9 -10
- package/dist/files/editor/code-editor.js +2 -3
- package/dist/files/project/api/runtime-files.js +3 -2
- package/dist/files/project/components/change-request-detail-dialog.d.ts +3 -1
- package/dist/files/project/components/change-request-detail-dialog.js +3 -5
- package/dist/files/project/components/change-request-status-badge.d.ts +12 -0
- package/dist/files/project/components/change-request-status-badge.js +27 -0
- package/dist/files/project/components/change-requests-panel.js +1 -1
- package/dist/files/project/components/drive-explorer.js +2 -1
- package/dist/files/project/components/drive-grid-view.js +11 -7
- package/dist/files/project/components/drive-list-view.js +4 -4
- package/dist/files/project/components/file-breadcrumbs.js +1 -1
- package/dist/files/project/components/file-explorer-page.js +1 -1
- package/dist/files/project/components/file-history-popover.js +1 -6
- package/dist/files/project/components/file-search.js +6 -1
- package/dist/files/project/components/open-change-request-dialog.js +2 -1
- package/dist/files/project/components/version-selector-logic.d.ts +11 -0
- package/dist/files/project/components/version-selector-logic.js +26 -0
- package/dist/files/project/components/version-selector.js +11 -20
- package/dist/files/project/file-source.js +18 -1
- package/dist/files/project/hooks/use-file-content.js +2 -0
- package/dist/files/project/hooks/use-server-health.js +17 -15
- package/dist/files/renderers/docx/docx-viewer.js +0 -5
- package/dist/files/renderers/image-renderer.js +6 -24
- package/dist/files/renderers/pdf/pdf-viewer.js +1 -1
- package/dist/files/renderers/presentation/FullScreenPresentationViewer.js +2 -11
- package/dist/files/renderers/show-content-renderer.js +1 -1
- package/dist/files/renderers/sqlite-renderer.js +8 -3
- package/dist/files/renderers/xlsx/xlsx-viewer.js +15 -16
- package/dist/files/tree/components/file-tree-item.js +1 -1
- 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 +13 -3
- package/dist/files/viewer/file-source.d.ts +3 -1
- package/dist/files/viewer/use-file-content-state.js +27 -8
- package/dist/files/viewer/viewer-actions.js +2 -2
- package/dist/i18n/en.d.ts +355 -12
- package/dist/i18n/en.js +360 -17
- package/dist/internal/desktop/desktop-back-button.js +5 -3
- 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/hooks/use-auto-scroll.js +5 -3
- package/dist/internal/lib/query-client-singleton.d.ts +9 -3
- package/dist/internal/lib/query-client-singleton.js +18 -12
- package/dist/internal/notifications/notification-rows.js +2 -1
- package/dist/internal/outcomes/outcome-types.d.ts +5 -0
- package/dist/internal/share/session-shares-modal.js +3 -7
- package/dist/internal/stores/kortix-computer-store.js +14 -2
- package/dist/internal/stores/session-hover-store.d.ts +32 -0
- package/dist/internal/stores/session-hover-store.js +38 -0
- package/dist/internal/stores/sidebar-nav-store.d.ts +1 -1
- package/dist/internal/stores/sidebar-nav-store.js +3 -1
- package/dist/lib/routes.d.ts +2 -0
- package/dist/lib/routes.js +1 -0
- package/dist/markdown/code/inline-code.js +8 -0
- package/dist/markdown/code/output-paths-context.d.ts +18 -0
- package/dist/markdown/code/output-paths-context.js +35 -0
- package/dist/markdown/genui/components/charts.js +6 -5
- package/dist/primitives/map.js +2 -1
- package/dist/primitives/preview-image.js +4 -2
- package/dist/primitives/settings-section-header.d.ts +1 -1
- package/dist/primitives/textarea.js +1 -1
- package/dist/primitives/typed-title.js +12 -14
- package/dist/primitives/typing-animation.js +4 -2
- package/dist/session/composer/agent-selector.js +6 -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-chat-input.js +5 -3
- 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/composer-logic.js +8 -7
- 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/draft/composer-draft.js +4 -4
- 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/pasted-text.js +3 -2
- 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/stores/queued-draft-store.js +3 -2
- package/dist/session/composer/token-progress.d.ts +1 -1
- package/dist/session/composer/token-progress.js +2 -2
- package/dist/session/header/reminder-parts.d.ts +11 -2
- package/dist/session/header/reminder-parts.js +9 -4
- 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/media/sandbox-url-detector.js +0 -7
- package/dist/session/outcomes/change-request-outcomes.js +24 -3
- package/dist/session/overrides/provider-secret-pool-editor.js +2 -1
- package/dist/session/panel/advanced/advanced-panel.js +3 -7
- package/dist/session/panel/easy/app-preview.js +12 -2
- package/dist/session/panel/easy/easy-panel-logic.d.ts +1 -1
- package/dist/session/panel/easy/easy-panel-logic.js +1 -1
- package/dist/session/panel/easy/outputs-card.js +2 -1
- package/dist/session/panel/easy/panel-card.js +5 -3
- package/dist/session/panel/easy/preview-load.d.ts +14 -0
- package/dist/session/panel/easy/preview-load.js +15 -0
- package/dist/session/panel/easy/step-detail-body.js +2 -7
- 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/panel/session-panel-provider.js +6 -2
- package/dist/session/panel/shared/use-panel-derivations.js +6 -4
- package/dist/session/panel/use-session-panel-layout.js +7 -5
- package/dist/session/print/session-print-header.js +9 -8
- 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 +15 -19
- package/dist/session/thread/session-history-scroll.d.ts +2 -1
- package/dist/session/thread/session-history.js +3 -1
- package/dist/session/thread/session-thread.js +1 -1
- package/dist/session/thread/transcript.js +13 -7
- package/dist/session/tool/shared/file-list.js +19 -2
- package/dist/session/tool/shared/infrastructure-contexts.d.ts +7 -0
- package/dist/session/tool/shared/infrastructure-contexts.js +7 -0
- package/dist/session/tool/shared/infrastructure-output.js +9 -5
- package/dist/session/tool/shared/infrastructure-row-header.js +9 -1
- package/dist/session/tool/shared/infrastructure-rows.js +7 -5
- package/dist/session/tool/tools/bash-tool.js +10 -6
- package/dist/session/tool/tools/web-search-tool.js +25 -3
- package/dist/session/turn/recovered-failures.d.ts +8 -0
- package/dist/session/turn/recovered-failures.js +32 -0
- package/dist/session/turn/streaming-cadence.js +5 -8
- package/dist/styles.css +1 -1
- package/dist/terminal/pty-connection.d.ts +5 -4
- package/dist/terminal/session-terminal-panel.js +3 -5
- package/dist/theme/theme.js +2 -1
- package/dist/workspace/index.d.ts +1 -0
- package/dist/workspace/index.js +1 -0
- package/dist/workspace/projects/modal/github-import-pickers.js +7 -6
- package/dist/workspace/review/review-center.js +13 -10
- 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 +9 -14
- package/dist/workspace/sessions/deleted-session-row.js +5 -2
- package/dist/workspace/sidebar/footer/project-computer-nav.js +6 -4
- 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/footer/project-mcp-nav.js +6 -2
- package/dist/workspace/sidebar/modal/rename-session-modal.js +8 -3
- 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
|
@@ -8,7 +8,8 @@ import { contract, FRESHNESS, qk, useFeatureFlag, useProjectAccountId } from '@k
|
|
|
8
8
|
import { MagnifyingGlassIcon, PlugIcon, PlugsConnectedIcon } from '@phosphor-icons/react';
|
|
9
9
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
10
10
|
import { lazyClient } from '../../lib/lazy-client.js';
|
|
11
|
-
import { useUiPathname, useUiRouter, useUiSearchParams } from '../../providers/ui-provider.js';
|
|
11
|
+
import { useUiPathname, useUiRouter, useUiSearchParams, useUiShallowNavigate, } from '../../providers/ui-provider.js';
|
|
12
|
+
import { useDebounce } from '../../hooks/use-debounce.js';
|
|
12
13
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
13
14
|
import { PoliciesPanel } from '../../workspace/projects/policies-panel.js';
|
|
14
15
|
import { Button } from '../../primitives/button.js';
|
|
@@ -37,7 +38,7 @@ import { useTunnelConnections } from '@kortix/sdk/react';
|
|
|
37
38
|
import { useSlashFocus } from '../../internal/hooks/index.js';
|
|
38
39
|
import { connectorDisplayName, connectorSummary, filterConnectors, } from './connector-filter.js';
|
|
39
40
|
import { ConnectorMovesBanner } from './connector-moves-banner.js';
|
|
40
|
-
import { appHref, appRefFromEntry, connectorHref, legacyDetailRedirect } from './connector-routes.js';
|
|
41
|
+
import { appHref, appRefFromEntry, connectorHref, connectorSearchFromParams, legacyDetailRedirect, withConnectorSearch, } from './connector-routes.js';
|
|
41
42
|
import { useInstall } from './install/use-install.js';
|
|
42
43
|
/**
|
|
43
44
|
* The click-gated Add form, split out of this route's initial chunk.
|
|
@@ -172,7 +173,11 @@ export function ConnectorsPage({ projectId }) {
|
|
|
172
173
|
const canWrite = useProjectCan(projectId, PROJECT_ACTIONS.PROJECT_CONNECTOR_WRITE, { accountId }).allowed ===
|
|
173
174
|
true;
|
|
174
175
|
const queryClient = useQueryClient();
|
|
175
|
-
const
|
|
176
|
+
const { search, router, pathname, replaceParams, setScope, rulesOpen, setRulesOpen } = useConnectorNavigation(projectId);
|
|
177
|
+
// The search lives in `?q=`: opening a connector unmounts this page, and
|
|
178
|
+
// browser Back must bring the same results back (KRTX-3018).
|
|
179
|
+
const [query, setQuery] = useState(() => connectorSearchFromParams(search));
|
|
180
|
+
useSearchInUrl(query, search, pathname);
|
|
176
181
|
const [panel, setPanel] = useState(null);
|
|
177
182
|
// The catalogue app whose own MCP server the MCP dialog connects. Null for
|
|
178
183
|
// a server the user pastes (New → MCP server).
|
|
@@ -180,7 +185,6 @@ export function ConnectorsPage({ projectId }) {
|
|
|
180
185
|
const [computerOpen, setComputerOpen] = useState(false);
|
|
181
186
|
const tSharing = useI18nTranslations('accessSharing');
|
|
182
187
|
const tPages = useI18nTranslations('connectorPages');
|
|
183
|
-
const { search, router, pathname, replaceParams, setScope, rulesOpen, setRulesOpen } = useConnectorNavigation(projectId);
|
|
184
188
|
const connectorsQuery = useQuery({
|
|
185
189
|
queryKey: qk.project.connectors(projectId),
|
|
186
190
|
queryFn: () => listConnectors(projectId, { includeSchemas: false }),
|
|
@@ -374,6 +378,21 @@ export function ConnectorsPage({ projectId }) {
|
|
|
374
378
|
router.push(connectorHref(projectId, slug, { addAccount }));
|
|
375
379
|
} }) })] }) })] }) }), _jsxs(SplitSheetContent, { children: [_jsxs(SplitSheetHeader, { children: [_jsx(SplitSheetTitle, { children: tI18nComplete.raw('text1d59a5e09714') }), _jsx(SplitSheetDescription, { children: tI18nComplete.raw('text014d10bd3c64') })] }), _jsx(SplitSheetBody, { children: _jsx(PoliciesPanel, { projectId: projectId }) })] })] }));
|
|
376
380
|
}
|
|
381
|
+
/**
|
|
382
|
+
* Mirrors the settled search into `?q=` with a shallow replace: no history
|
|
383
|
+
* entry per keystroke, and no server round trip for a view already on screen.
|
|
384
|
+
*/
|
|
385
|
+
function useSearchInUrl(query, search, pathname) {
|
|
386
|
+
const shallowNavigate = useUiShallowNavigate();
|
|
387
|
+
const { debouncedValue } = useDebounce(query.trim(), 300);
|
|
388
|
+
useEffect(() => {
|
|
389
|
+
if (connectorSearchFromParams(search) === debouncedValue)
|
|
390
|
+
return;
|
|
391
|
+
const params = withConnectorSearch(new URLSearchParams(search?.toString() ?? ''), debouncedValue);
|
|
392
|
+
const suffix = params.toString();
|
|
393
|
+
shallowNavigate(suffix ? `${pathname}?${suffix}` : pathname, { replace: true });
|
|
394
|
+
}, [debouncedValue, search, pathname, shallowNavigate]);
|
|
395
|
+
}
|
|
377
396
|
function useConnectorNavigation(projectId) {
|
|
378
397
|
const search = useUiSearchParams();
|
|
379
398
|
const router = useUiRouter();
|
|
@@ -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}`,
|
|
@@ -49,13 +49,11 @@ export function ConnectorAdvancedTools({ connector, advancedRules, toolRules, lo
|
|
|
49
49
|
// re-fired whenever a rule was added or removed, so the panel sprang back open
|
|
50
50
|
// moments after the user collapsed it, which read as the disclosure opening by
|
|
51
51
|
// itself. After the first reveal the panel is the user's to control.
|
|
52
|
-
const revealedExistingRules =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return;
|
|
56
|
-
revealedExistingRules.current = true;
|
|
52
|
+
const [revealedExistingRules, setRevealedExistingRules] = useState(false);
|
|
53
|
+
if (!revealedExistingRules && advancedRules.length > 0) {
|
|
54
|
+
setRevealedExistingRules(true);
|
|
57
55
|
setAdvancedOpen(true);
|
|
58
|
-
}
|
|
56
|
+
}
|
|
59
57
|
const savePatternRules = () => onSave([...toolRules, ...draftToRules(draft)]);
|
|
60
58
|
return (_jsx(_Fragment, { children: hasTools || advancedRules.length > 0 ? (_jsxs(Disclosure, { variant: "outline", className: "overflow-hidden", open: advancedOpen, onOpenChange: setAdvancedOpen, children: [_jsx(DisclosureTrigger, { variant: "outline", children: _jsxs(Button, { variant: "popover", children: [tI18nComplete.raw('text9f088dbebd6c'), advancedRules.length > 0 ? (_jsx(Badge, { variant: "secondary", size: "sm", children: advancedRules.length })) : null, _jsx("span", { className: "text-muted-foreground ml-auto text-xs font-normal", children: tI18nComplete.raw('text59da2efb2423') })] }) }), _jsx(DisclosureContent, { variant: "outline", contentClassName: "border-border border-t", children: _jsxs("div", { className: "space-y-5 px-4 py-5", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-foreground text-sm font-medium", children: tI18nComplete.raw('text594bdd4c19b2') }), _jsx("p", { className: "text-muted-foreground mt-0.5 text-xs text-pretty", children: tI18nComplete.raw('textf900377c2048') })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [sensitivePending ? _jsx(Loading, { className: "size-4 shrink-0" }) : null, _jsx(Switch, { checked: connector.sensitive === true, onCheckedChange: onSensitive, disabled: frozen || sensitivePending, "aria-label": tI18nComplete.raw('text594bdd4c19b2') })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: tI18nComplete.raw('text380c0b05bcfc') }), _jsxs("p", { className: "text-muted-foreground text-xs text-pretty", children: [tI18nComplete.raw('textde00adeaf21b'), _jsx("code", { className: "font-mono", children: "delete_*" }), tI18nComplete.raw('text3a13855164e7'), _jsx("code", { className: "font-mono", children: "/^send/i" }), tI18nComplete.raw('texte45481e75a5e')] }), draft.map((row) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { value: row.match, placeholder: tI18nComplete.raw('text74ad536dca05'), variant: "popover", size: "xs", className: "flex-1 font-mono", "aria-label": tI18nComplete.raw('text5c9c672f4cd3'), disabled: frozen, onChange: (event) => setDraft((rows) => rows.map((candidate) => candidate.id === row.id
|
|
61
59
|
? { ...candidate, match: event.target.value }
|
|
@@ -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 {};
|
|
@@ -1,117 +1,34 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useTranslations } from '../../../i18n/index.js';
|
|
4
|
-
import { listPipedreamApps } from '@kortix/sdk';
|
|
5
|
-
import { KeyIcon, PlusIcon } from '@phosphor-icons/react';
|
|
6
4
|
import { VisuallyHidden } from '@radix-ui/react-visually-hidden';
|
|
7
|
-
import { useQuery } from '@tanstack/react-query';
|
|
8
|
-
import { useState } from 'react';
|
|
9
|
-
import { Badge } from '../../../primitives/badge.js';
|
|
10
5
|
import { Button } from '../../../primitives/button.js';
|
|
11
|
-
import {
|
|
12
|
-
import Loading from '../../../primitives/loading.js';
|
|
13
|
-
import { Modal, ModalBody, ModalClose, ModalContent, ModalHeader, ModalTitle, } from '../../../primitives/modal.js';
|
|
6
|
+
import { Modal, ModalClose, ModalContent, ModalTitle } from '../../../primitives/modal.js';
|
|
14
7
|
import { Skeleton } from '../../../primitives/skeleton.js';
|
|
15
|
-
import { Tabs, TabsList, TabsTrigger } from '../../../primitives/tabs.js';
|
|
16
|
-
import { ComputerConnectModal } from '../../../internal/tunnel/computer-connect.js';
|
|
17
|
-
import { SetCredentialModal } from '../../sections/connectors-view.js';
|
|
18
|
-
import { ConnectorAppIcon, ConnectorStatusBadge, } from '../connector-identity.js';
|
|
19
|
-
import { cn } from '../../../internal/lib/utils.js';
|
|
20
|
-
import { ButtonGroup } from '../../../primitives/button-group.js';
|
|
21
8
|
import { Close } from '../../../icons/close.js';
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
import { useConnectorEventTriggers } from './connector-triggers.js';
|
|
26
|
-
import { useConnectorDetail } from './use-connector-detail.js';
|
|
9
|
+
import { ConnectorDetailView } from './connector-page.js';
|
|
10
|
+
/** The scroll column inside the modal: the page's column, tighter. */
|
|
11
|
+
const MODAL_COLUMN = 'w-full space-y-6 px-6 py-6';
|
|
27
12
|
/**
|
|
28
|
-
*
|
|
13
|
+
* One connector over the agent editor, without leaving it (a navigation would
|
|
14
|
+
* drop the agent's unsaved draft). The body IS the connector page
|
|
15
|
+
* (`ConnectorDetailView`): the same header, status strip, notice, tabs and
|
|
16
|
+
* split credential sheet, so the modal and the page cannot drift. The modal
|
|
17
|
+
* adds only a close button and keeps its tab in state instead of the URL.
|
|
29
18
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* Right: the active tab.
|
|
33
|
-
*
|
|
34
|
-
* `ConnectorModalBody` is keyed on `connector.slug` so picking a different card
|
|
35
|
-
* while the modal stays open resets the active tab without remounting
|
|
36
|
-
* `Modal`/`ModalContent` (which would replay the open animation).
|
|
19
|
+
* The view is keyed on `connector.slug` so picking a different connector while
|
|
20
|
+
* the modal stays open resets the tab without replaying the open animation.
|
|
37
21
|
*/
|
|
38
22
|
export function ConnectorModal({ projectId, connector, canWrite, open, isResolving = false, onOpenChange, onChanged, onRemoved, }) {
|
|
39
|
-
|
|
23
|
+
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
24
|
+
return (_jsx(Modal, { open: open, onOpenChange: onOpenChange, children: _jsx(ModalContent, { className: "bg-background flex max-h-[92vh] min-h-[85vh] flex-col space-y-0 overflow-hidden p-0 lg:max-w-5xl", "aria-describedby": undefined, showCloseButton: false, children: connector ? (_jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: _jsx(ConnectorDetailView, { projectId: projectId, connector: connector, canWrite: canWrite, onChanged: onChanged, onRemoved: onChanged, onLeave: onRemoved, urlState: false, columnClassName: MODAL_COLUMN, wrapTitle: (title) => _jsx(ModalTitle, { asChild: true, children: title }), headerEnd: _jsx(ModalClose, { asChild: true, children: _jsx(Button, { variant: "secondary", size: "icon-sm", className: "shrink-0", "aria-label": tI18nComplete.raw('text7d9eb7acb13e'), children: _jsx(Close, { className: "text-foreground size-4 stroke-1" }) }) }) }, connector.slug) })) : isResolving ? (_jsx(ConnectorModalSkeleton, {})) : null }) }));
|
|
40
25
|
}
|
|
41
26
|
/**
|
|
42
|
-
* The shell
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* relaying the modal out from under the pointer.
|
|
46
|
-
*
|
|
47
|
-
* The `ModalTitle` is real and visually hidden: Radix's Dialog needs an
|
|
48
|
-
* accessible name at all times, and a screen reader announcing "Loading
|
|
49
|
-
* connector" is the honest answer during this window.
|
|
27
|
+
* The shell while the selection resolves. Shape-matched to the view — icon and
|
|
28
|
+
* name, the strip, the tabs, two rows — so the handover fills it in place.
|
|
29
|
+
* The visually hidden title gives the dialog its accessible name meanwhile.
|
|
50
30
|
*/
|
|
51
31
|
function ConnectorModalSkeleton() {
|
|
52
32
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
53
|
-
return (_jsxs(
|
|
54
|
-
}
|
|
55
|
-
function ConnectorModalBody({ projectId, connector, canWrite, onChanged, onRemoved, }) {
|
|
56
|
-
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
57
|
-
const { displayName, isManagedProvider, isChannel, isComputer, connectionsQuery, connected, canManageConnections, headerCta, connectPending, refreshAccounts, replaceSoleAccount, startPrivateSession, credentialTarget, setCredentialTarget, computerOpen, setComputerOpen, } = useConnectorDetail({ projectId, connector, canWrite, onChanged });
|
|
58
|
-
// Opens the account list's "Add account" dialog: name and who may use it.
|
|
59
|
-
const [addRequest, setAddRequest] = useState(0);
|
|
60
|
-
const tSharing = useTranslations('accessSharing');
|
|
61
|
-
const isPipedream = connector.provider === 'pipedream';
|
|
62
|
-
const eventTriggers = useConnectorEventTriggers(projectId, connector);
|
|
63
|
-
const tabs = connectorTabs(connector, { canWrite, hasEvents: eventTriggers.hasEvents });
|
|
64
|
-
const [selectedTab, setSelectedTab] = useState('accounts');
|
|
65
|
-
const tab = tabs.includes(selectedTab) ? selectedTab : (tabs[0] ?? 'accounts');
|
|
66
|
-
// Best-effort catalogue description. Never blocks first paint — the header
|
|
67
|
-
// renders without it, then fills in. That is the main open-latency fix:
|
|
68
|
-
// previously this query sat in the critical path of feeling "ready".
|
|
69
|
-
const appDescriptionQuery = useQuery({
|
|
70
|
-
queryKey: ['connector-app-description', projectId, connector.slug, displayName],
|
|
71
|
-
queryFn: async () => {
|
|
72
|
-
const result = await listPipedreamApps(projectId, displayName);
|
|
73
|
-
const match = result.apps.find((app) => foldKey(app.slug) === foldKey(connector.slug) ||
|
|
74
|
-
foldKey(app.name) === foldKey(displayName));
|
|
75
|
-
return match?.description ?? null;
|
|
76
|
-
},
|
|
77
|
-
enabled: isPipedream,
|
|
78
|
-
staleTime: 5 * 60_000,
|
|
79
|
-
});
|
|
80
|
-
const appDescription = isPipedream ? (appDescriptionQuery.data ?? null) : null;
|
|
81
|
-
const addAccountMenu = isComputer ? (_jsxs(Button, { size: "sm", className: "gap-1.5", onClick: () => setComputerOpen(true), children: [_jsx(PlusIcon, { className: "size-4 shrink-0", weight: "bold" }), tI18nComplete.raw('text1a2303ede074')] })) : (_jsxs(Button, { size: "sm", className: "gap-1.5", onClick: () => {
|
|
82
|
-
setSelectedTab('accounts');
|
|
83
|
-
setAddRequest((n) => n + 1);
|
|
84
|
-
}, disabled: connectPending, children: [_jsx(PlusIcon, { className: "size-4 shrink-0", weight: "bold" }), tSharing('addAccount')] }));
|
|
85
|
-
return (_jsxs(_Fragment, { children: [_jsxs(ModalHeader, { className: "flex-row items-start gap-2.5 border-b pb-4", children: [_jsx("span", { className: "p-1", children: _jsx(ConnectorAppIcon, { connector: connector, size: "lg" }) }), _jsxs("div", { className: "min-w-0 flex-1 space-y-0", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-x-2 gap-y-1", children: [_jsx(ModalTitle, { className: "truncate text-lg font-semibold", children: displayName }), _jsx(ConnectorStatusBadge, { connector: connector })] }), appDescription ? (_jsx("p", { className: "text-muted-foreground text-sm text-pretty", children: appDescription })) : null] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(ButtonGroup, { className: "shrink-0", children: [headerCta === 'connect' ? addAccountMenu : null, headerCta === 'finish' ? (_jsxs(Button, { size: "sm", variant: "outline", className: "gap-1.5 active:scale-[0.96]", onClick: () => setSelectedTab('accounts'), children: [_jsx(KeyIcon, { className: "size-4 shrink-0" }), isManagedProvider
|
|
86
|
-
? tI18nComplete.raw('text1a2303ede074')
|
|
87
|
-
: tI18nComplete.raw('text3d6627454174')] })) : null, headerCta === 'replace' ? (_jsxs(Button, { size: "sm", variant: "outline", className: "gap-1.5 active:scale-[0.96]", onClick: replaceSoleAccount, disabled: connectPending, children: [isManagedProvider ? (connectPending ? (_jsx(Loading, { className: "size-4 shrink-0" })) : null) : (_jsx(KeyIcon, { className: "size-4 shrink-0" })), isManagedProvider
|
|
88
|
-
? tI18nComplete.raw('textbf8a9eab9e7e')
|
|
89
|
-
: tI18nComplete.raw('text54483ce856e0')] })) : null] }), _jsx(ModalClose, { asChild: true, children: _jsx(Button, { variant: "secondary", size: "icon", className: "size-8 shrink-0 rounded-md", "aria-label": tI18nComplete.raw('text7d9eb7acb13e'), children: _jsx(Close, { className: "text-foreground size-4 stroke-1" }) }) })] })] }), _jsxs(ModalBody, { className: "flex max-h-[70vh] flex-col overflow-hidden p-0", children: [!connected && !isChannel && !isComputer && connectionsQuery.isSuccess ? (_jsx(InfoBanner, { tone: "warning", title: tI18nComplete('text6506d9ea4341', { value0: displayName }), className: "shrink-0 rounded-none border-x-0 border-t-0", action: headerCta === 'connect' ? (addAccountMenu) : headerCta === 'finish' ? (_jsx(Button, { size: "sm", variant: "outline", onClick: () => setSelectedTab('accounts'), children: isManagedProvider
|
|
90
|
-
? tI18nComplete.raw('text1a2303ede074')
|
|
91
|
-
: tI18nComplete.raw('text3d6627454174') })) : undefined, children: canWrite
|
|
92
|
-
? tI18nComplete.raw('text929505ef815a')
|
|
93
|
-
: tI18nComplete.raw('text6a05ddf8cca1') })) : null, _jsxs(Tabs, { value: tab, onValueChange: (next) => setSelectedTab(next), className: "flex min-h-0 flex-1 flex-col gap-0 overflow-y-auto lg:flex-row lg:overflow-hidden", children: [_jsx(TabsList, { type: "underline", underlineSize: "md", size: "sm", "aria-label": `${displayName} sections`, className: cn('h-auto w-full shrink-0 justify-start gap-1 rounded-none px-2', 'overflow-x-auto', 'lg:border-border lg:h-full lg:w-64 lg:flex-col lg:items-stretch lg:gap-0.5', 'lg:overflow-x-visible lg:overflow-y-auto lg:border-r lg:border-b-0 lg:p-3', 'lg:**:data-[slot=tabs-trigger]:after:hidden', 'lg:**:data-[slot=tabs-trigger]:h-auto lg:**:data-[slot=tabs-trigger]:w-full', 'lg:**:data-[slot=tabs-trigger]:justify-between lg:**:data-[slot=tabs-trigger]:rounded-md', 'lg:**:data-[slot=tabs-trigger]:px-3 lg:**:data-[slot=tabs-trigger]:py-2', 'lg:**:data-[slot=tabs-trigger]:data-[state=active]:bg-primary/6', 'lg:**:data-[slot=tabs-trigger]:data-[state=active]:font-medium', 'lg:**:data-[slot=tabs-trigger]:data-[state=inactive]:hover:bg-primary/3'), children: tabs.map((value) => (_jsxs(TabsTrigger, { value: value, className: "w-fit flex-none gap-2 px-3 py-2.5 active:scale-[0.98] lg:w-full", children: [tI18nComplete.raw(CONNECTOR_TAB_LABEL_KEY[value]), value === 'triggers' ? (_jsx(Badge, { variant: "secondary", size: "sm", children: eventTriggers.rows.length })) : null] }, value))) }), _jsx("div", { className: "bg-popover min-w-0 flex-1 overflow-hidden overflow-y-auto px-5 py-4 lg:px-6", children: _jsx(ConnectorDetailTabs, { connectionsQuery: connectionsQuery, accounts: {
|
|
94
|
-
projectId,
|
|
95
|
-
connector,
|
|
96
|
-
displayName,
|
|
97
|
-
canWrite,
|
|
98
|
-
canManageConnections,
|
|
99
|
-
onChanged,
|
|
100
|
-
onStartSession: startPrivateSession,
|
|
101
|
-
addRequest,
|
|
102
|
-
onAddRequestHandled: () => setAddRequest(0),
|
|
103
|
-
onRemoved,
|
|
104
|
-
}, settings: {
|
|
105
|
-
projectId: projectId,
|
|
106
|
-
connector: connector,
|
|
107
|
-
displayName: displayName,
|
|
108
|
-
onChanged: onChanged,
|
|
109
|
-
onRemoved: onRemoved,
|
|
110
|
-
} }) })] })] }), _jsx(SetCredentialModal, { projectId: projectId, connector: credentialTarget ? connector : null, connectionId: credentialTarget?.connectionId ?? null, owner: credentialTarget?.owner ?? 'me', open: credentialTarget !== null, onOpenChange: (open) => {
|
|
111
|
-
if (!open)
|
|
112
|
-
setCredentialTarget(null);
|
|
113
|
-
}, onSaved: () => {
|
|
114
|
-
setCredentialTarget(null);
|
|
115
|
-
refreshAccounts();
|
|
116
|
-
} }), isComputer ? (_jsx(ComputerConnectModal, { projectId: projectId, open: computerOpen, onOpenChange: setComputerOpen, onConnected: refreshAccounts })) : null] }));
|
|
33
|
+
return (_jsxs("div", { className: MODAL_COLUMN, "aria-busy": true, children: [_jsx(VisuallyHidden, { children: _jsx(ModalTitle, { children: tI18nComplete.raw('text0c21b0363778') }) }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Skeleton, { className: "size-9 rounded-md" }), _jsx(Skeleton, { className: "h-6 w-48 rounded-sm" })] }), _jsx(Skeleton, { className: "h-16 w-full rounded-md" }), _jsx(Skeleton, { className: "h-9 w-64 rounded-md" }), _jsx(Skeleton, { className: "h-20 w-full rounded-md" })] }));
|
|
117
34
|
}
|
|
@@ -72,8 +72,12 @@ export function noticeButton(notice, target, managed) {
|
|
|
72
72
|
}
|
|
73
73
|
/** A failure line split for the log row: `MCP tools/list failed: HTTP 401` → `401` + `MCP tools/list failed`. */
|
|
74
74
|
export function splitNoticeReason(reason) {
|
|
75
|
-
const match =
|
|
75
|
+
const match = /\bHTTP (\d{3})\b/.exec(reason);
|
|
76
76
|
if (!match)
|
|
77
77
|
return { code: null, detail: reason };
|
|
78
|
-
|
|
78
|
+
// Drop the `: ` before the status with a loop: a `[:\s]*` regex prefix is quadratic on long whitespace runs.
|
|
79
|
+
let end = match.index;
|
|
80
|
+
while (end > 0 && /[:\s]/.test(reason[end - 1]))
|
|
81
|
+
end--;
|
|
82
|
+
return { code: match[1], detail: (reason.slice(0, end) + reason.slice(match.index + match[0].length)).trim() };
|
|
79
83
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type AdminConnector } from '@kortix/sdk';
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
1
3
|
/** The column `CapabilityPageShell` gives the list page beside this one. */
|
|
2
4
|
/**
|
|
3
5
|
* `/projects/<id>/customize/connectors/<app>/<connector>` — one connector, as
|
|
@@ -12,6 +14,34 @@ export declare function ConnectorPage({ projectId, appSegment, connectorSlug, }:
|
|
|
12
14
|
appSegment: string;
|
|
13
15
|
connectorSlug: string;
|
|
14
16
|
}): import("react").JSX.Element;
|
|
17
|
+
export interface ConnectorDetailViewProps {
|
|
18
|
+
projectId: string;
|
|
19
|
+
connector: AdminConnector;
|
|
20
|
+
canWrite: boolean;
|
|
21
|
+
onChanged: () => void;
|
|
22
|
+
/** The connector was deleted: refresh everything but its own queries. */
|
|
23
|
+
onRemoved: () => void;
|
|
24
|
+
/** Above the header: the page's Back link. */
|
|
25
|
+
back?: ReactNode;
|
|
26
|
+
/** At the header's right edge, after its actions: the modal's close button. */
|
|
27
|
+
headerEnd?: ReactNode;
|
|
28
|
+
/** Wraps the `h1`: the modal passes `ModalTitle asChild` for its accessible name. */
|
|
29
|
+
wrapTitle?: (title: ReactNode) => ReactNode;
|
|
30
|
+
/** The view's own connector is gone: leave it (the page routes back, the
|
|
31
|
+
* modal closes). */
|
|
32
|
+
onLeave: () => void;
|
|
33
|
+
/** The tab and the install hand-offs live in the URL on the page. The modal
|
|
34
|
+
* keeps its tab in state, so it never rewrites its host page's URL. */
|
|
35
|
+
urlState?: boolean;
|
|
36
|
+
/** The scroll column's classes; the modal uses tighter padding. */
|
|
37
|
+
columnClassName?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* One connector — the strip, its notice, and the Accounts / Tools / Settings
|
|
41
|
+
* tabs. The page (`/connectors/<app>/<connector>`) and the agent editor's
|
|
42
|
+
* connector modal render this same view, so the two cannot drift.
|
|
43
|
+
*/
|
|
44
|
+
export declare function ConnectorDetailView({ projectId, connector, canWrite, onChanged, onRemoved, back, headerEnd, wrapTitle, onLeave, urlState, columnClassName, }: ConnectorDetailViewProps): import("react").JSX.Element;
|
|
15
45
|
/**
|
|
16
46
|
* The strip's bottom row when something is wrong: a one-line title, the raw
|
|
17
47
|
* failure as a quiet log line, and the buttons that fix it. No icon tile; the
|
|
@@ -99,7 +99,17 @@ export function ConnectorPage({ projectId, appSegment, connectorSlug, }) {
|
|
|
99
99
|
}
|
|
100
100
|
return (_jsx(ConnectorPageBody, { projectId: projectId, connector: connector, canWrite: canWrite, backHref: backHref, onChanged: invalidate, onRemoved: invalidateAfterRemove }, connector.slug));
|
|
101
101
|
}
|
|
102
|
-
function ConnectorPageBody({
|
|
102
|
+
function ConnectorPageBody({ backHref, ...props }) {
|
|
103
|
+
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
104
|
+
const router = useUiRouter();
|
|
105
|
+
return (_jsx(ConnectorDetailView, { ...props, back: _jsx(Button, { asChild: true, variant: "text", size: "sm", className: "-ml-1 w-fit px-0 has-[>svg]:px-0", children: _jsxs(Link, { href: backHref, children: [_jsx(CaretLeftIcon, { className: "size-3.5 shrink-0" }), tI18nComplete.raw('textc3d2e79ebdd0')] }) }), onLeave: () => router.push(connectorsHref(props.projectId, 'connected')) }));
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* One connector — the strip, its notice, and the Accounts / Tools / Settings
|
|
109
|
+
* tabs. The page (`/connectors/<app>/<connector>`) and the agent editor's
|
|
110
|
+
* connector modal render this same view, so the two cannot drift.
|
|
111
|
+
*/
|
|
112
|
+
export function ConnectorDetailView({ projectId, connector, canWrite, onChanged, onRemoved, back, headerEnd, wrapTitle = (title) => title, onLeave, urlState = true, columnClassName = PAGE_COLUMN, }) {
|
|
103
113
|
const t = useTranslations('connectorPages');
|
|
104
114
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
105
115
|
const tSharing = useTranslations('accessSharing');
|
|
@@ -116,19 +126,22 @@ function ConnectorPageBody({ projectId, connector, canWrite, backHref, onChanged
|
|
|
116
126
|
// The tab is in the URL, so a link opens the right one.
|
|
117
127
|
const eventTriggers = useConnectorEventTriggers(projectId, connector);
|
|
118
128
|
const tabs = connectorTabs(connector, { canWrite, hasEvents: eventTriggers.hasEvents });
|
|
119
|
-
const
|
|
129
|
+
const [localTab, setLocalTab] = useState('accounts');
|
|
130
|
+
const requestedTab = urlState ? search?.get('tab') : localTab;
|
|
120
131
|
const tab = tabs.find((value) => value === requestedTab) ?? tabs[0] ?? 'accounts';
|
|
121
|
-
const setTab = (next) =>
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
132
|
+
const setTab = (next) => urlState
|
|
133
|
+
? replaceParams((params) => {
|
|
134
|
+
if (next === 'accounts')
|
|
135
|
+
params.delete('tab');
|
|
136
|
+
else
|
|
137
|
+
params.set('tab', next);
|
|
138
|
+
})
|
|
139
|
+
: setLocalTab(next);
|
|
127
140
|
// The install hand-off: `?connect=<connection id>` opens credential entry
|
|
128
141
|
// for that account once, then leaves the URL. The id is untrusted, so
|
|
129
142
|
// `connectHandoff` opens only one of THIS connector's accounts and takes the
|
|
130
143
|
// owner from the row; the URL's `owner` param is stripped, never read.
|
|
131
|
-
const connectId = search?.get('connect') ?? null;
|
|
144
|
+
const connectId = urlState ? (search?.get('connect') ?? null) : null;
|
|
132
145
|
// Same cache entry as the probe in `useConnectorDetail`; read here for its
|
|
133
146
|
// loading state, so a shared account is not refused before the answer lands.
|
|
134
147
|
const accountId = useProjectAccountId(projectId);
|
|
@@ -195,7 +208,7 @@ function ConnectorPageBody({ projectId, connector, canWrite, backHref, onChanged
|
|
|
195
208
|
};
|
|
196
209
|
// Right after Install (`?add=1`): ask for the first account once, then
|
|
197
210
|
// leave the URL. Adjusted during render, not in an effect.
|
|
198
|
-
const addParam = search?.get('add') === '1';
|
|
211
|
+
const addParam = urlState && search?.get('add') === '1';
|
|
199
212
|
const [addHandled, setAddHandled] = useState(false);
|
|
200
213
|
if (addParam && !addHandled && canWrite && !isChannel) {
|
|
201
214
|
setAddHandled(true);
|
|
@@ -301,7 +314,7 @@ function ConnectorPageBody({ projectId, connector, canWrite, backHref, onChanged
|
|
|
301
314
|
setCredentialTarget(null);
|
|
302
315
|
};
|
|
303
316
|
const addAccountControl = isComputer ? (_jsxs(Button, { size: "sm", className: "gap-1.5", onClick: () => setComputerOpen(true), children: [_jsx(PlusIcon, { className: "size-4 shrink-0" }), tI18nComplete.raw('text1a2303ede074')] })) : (_jsxs(Button, { size: "sm", className: "gap-1.5", onClick: requestAdd, disabled: connectPending, children: [_jsx(PlusIcon, { className: "size-4 shrink-0" }), tSharing('addAccount')] }));
|
|
304
|
-
return (_jsxs(SplitSheet, { open: sheetOpen, onOpenChange: closeCredential, size: "lg", className: "flex-1", children: [_jsx(SplitSheetMain, { children: _jsxs("div", { className:
|
|
317
|
+
return (_jsxs(SplitSheet, { open: sheetOpen, onOpenChange: closeCredential, size: "lg", className: "flex-1", children: [_jsx(SplitSheetMain, { children: _jsxs("div", { className: columnClassName, children: [back, _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(ConnectorAppIcon, { connector: connector, size: "lg" }), wrapTitle(_jsx("h1", { ref: headingRef, tabIndex: -1, className: "text-foreground min-w-0 text-xl font-medium text-balance wrap-break-word", children: displayName }))] }), _jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2", children: [isComputer && headerCta === 'connect' ? addAccountControl : null, connected ? (_jsx(Button, { size: "sm", variant: "outline", onClick: () => startPrivateSession(), children: t('newSession') })) : null, headerEnd] })] }), _jsxs("div", { className: "bg-popover divide-border divide-y overflow-hidden rounded-md border", children: [_jsx("dl", { className: "divide-border flex divide-x", children: stripCells.map((cell) => (_jsxs("div", { className: "min-w-0 flex-1 space-y-0.5 px-4 py-2.5", children: [_jsxs("dt", { className: "text-muted-foreground flex h-4 items-center justify-between gap-2 text-xs", children: [_jsx("span", { className: "truncate", children: cell.label }), cell.labelAddon] }), _jsx("dd", { className: "text-foreground flex min-w-0 items-center gap-1.5 truncate text-sm font-medium", children: cell.value })] }, cell.key))) }), notice.kind !== 'none' ? (_jsxs(ConnectorNoticeRow, { title: t(NOTICE_TITLE[notice.kind], { name: displayName }), detail: notice.kind === 'error' && notice.reason ? (_jsx(NoticeLogLine, { reason: notice.reason })) : (_jsx("p", { className: "text-muted-foreground text-xs text-pretty", children: t(notice.kind === 'needs_credential' && isManagedProvider
|
|
305
318
|
? 'noticeReconnectBody'
|
|
306
319
|
: NOTICE_BODY[notice.kind], { name: displayName }) })), children: [notice.kind === 'error' && canWrite ? (_jsxs(Button, { size: "sm", variant: "ghost", className: "gap-1.5", onClick: () => retry.mutate(), disabled: retry.isPending, children: [retry.isPending ? _jsx(Loading, { className: "size-4 shrink-0" }) : null, tI18nComplete.raw('text942087cc2d41')] })) : null, fix?.kind === 'add_account' ? (addAccountControl) : fix?.kind === 'sign_in' ? (_jsxs(Button, { size: "sm", className: "gap-1.5", onClick: runFix, disabled: signIn.isPending, children: [signIn.isPending ? _jsx(SessionDotMatrix, { size: 14, className: "shrink-0" }) : null, t('noticeSignInAction')] })) : fix ? (
|
|
307
320
|
// Fix it here: credential entry for a direct account, the
|
|
@@ -321,7 +334,7 @@ function ConnectorPageBody({ projectId, connector, canWrite, backHref, onChanged
|
|
|
321
334
|
onStartSession: startPrivateSession,
|
|
322
335
|
addRequest,
|
|
323
336
|
onAddRequestHandled: () => setAddRequest(0),
|
|
324
|
-
onRemoved:
|
|
337
|
+
onRemoved: onLeave,
|
|
325
338
|
onSetCredential: setCredentialTarget,
|
|
326
339
|
onConnect: needsSignIn ? connectAccount : undefined,
|
|
327
340
|
showAccountInfo: true,
|
|
@@ -331,7 +344,7 @@ function ConnectorPageBody({ projectId, connector, canWrite, backHref, onChanged
|
|
|
331
344
|
displayName: displayName,
|
|
332
345
|
onChanged: onChanged,
|
|
333
346
|
onRemoved: () => {
|
|
334
|
-
|
|
347
|
+
onLeave();
|
|
335
348
|
onRemoved();
|
|
336
349
|
},
|
|
337
350
|
} })] })] }) }), credentialTarget ? (
|
|
@@ -217,6 +217,7 @@ export function ProviderAccordion({ projectId, canWrite = false, enabled = true,
|
|
|
217
217
|
connectedIds,
|
|
218
218
|
search,
|
|
219
219
|
});
|
|
220
|
+
const disabledProviders = new Set(access.data?.enforced === true ? access.data.disabledProviders : []);
|
|
220
221
|
const rows = ordered.map((entry) => {
|
|
221
222
|
const method = providerMethod(entry);
|
|
222
223
|
return {
|
|
@@ -233,7 +234,7 @@ export function ProviderAccordion({ projectId, canWrite = false, enabled = true,
|
|
|
233
234
|
? 'connected'
|
|
234
235
|
: 'none',
|
|
235
236
|
via: connectedVia(entry.id, connectedIds),
|
|
236
|
-
disabled:
|
|
237
|
+
disabled: disabledProviders.has(entry.id),
|
|
237
238
|
};
|
|
238
239
|
});
|
|
239
240
|
return {
|
|
@@ -235,6 +235,7 @@ export function ProviderConnect({ projectId, canWrite = false, enabled = true, s
|
|
|
235
235
|
connectedIds,
|
|
236
236
|
search,
|
|
237
237
|
});
|
|
238
|
+
const disabledProviders = new Set(access.data?.enforced === true ? access.data.disabledProviders : []);
|
|
238
239
|
const rows = ordered.map((entry) => {
|
|
239
240
|
const method = providerMethod(entry);
|
|
240
241
|
return {
|
|
@@ -252,7 +253,7 @@ export function ProviderConnect({ projectId, canWrite = false, enabled = true, s
|
|
|
252
253
|
? 'connected'
|
|
253
254
|
: 'none',
|
|
254
255
|
via: connectedVia(entry.id, connectedIds),
|
|
255
|
-
disabled:
|
|
256
|
+
disabled: disabledProviders.has(entry.id),
|
|
256
257
|
};
|
|
257
258
|
});
|
|
258
259
|
return {
|
|
@@ -56,7 +56,7 @@ function TelegramConnectForm({ projectId, onDone }) {
|
|
|
56
56
|
function TelegramUsersForm({ projectId, initial, onDone, }) {
|
|
57
57
|
const t = useTranslations('hardcodedUi.i18nComplete');
|
|
58
58
|
const save = useSetTelegramAllowedUsers();
|
|
59
|
-
const [users, setUsers] = useState(initial.join(', '));
|
|
59
|
+
const [users, setUsers] = useState(() => initial.join(', '));
|
|
60
60
|
const ids = parseTelegramUserIds(users);
|
|
61
61
|
return (_jsxs("form", { className: "space-y-4", onSubmit: (event) => {
|
|
62
62
|
event.preventDefault();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
|
|
4
|
-
import {
|
|
4
|
+
import { useState } from 'react';
|
|
5
5
|
import { Button } from '../../primitives/button.js';
|
|
6
6
|
import { Disclosure, DisclosureContent, DisclosureTrigger } from '../../primitives/disclosure.js';
|
|
7
7
|
import { Field, FieldDescription, FieldGroup, FieldLabel } from '../../primitives/field.js';
|
|
@@ -35,14 +35,16 @@ export function ConnectorConnectionModal({ open, idPrefix, title, initialName, i
|
|
|
35
35
|
const [slug, setSlug] = useState(initialSlug);
|
|
36
36
|
const [slugEdited, setSlugEdited] = useState(false);
|
|
37
37
|
const [optionsOpen, setOptionsOpen] = useState(false);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
const [resetFor, setResetFor] = useState(null);
|
|
39
|
+
if (!resetFor || resetFor[0] !== proposedName || resetFor[1] !== initialSlug || resetFor[2] !== open) {
|
|
40
|
+
setResetFor([proposedName, initialSlug, open]);
|
|
41
|
+
if (open) {
|
|
42
|
+
setName(proposedName);
|
|
43
|
+
setSlug(initialSlug);
|
|
44
|
+
setSlugEdited(false);
|
|
45
|
+
setOptionsOpen(false);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
46
48
|
const slugAvailable = isConnectorConnectionSlugAvailable(slug, existingSlugs);
|
|
47
49
|
const slugInvalid = slug.length > 0 && !slugAvailable;
|
|
48
50
|
const slugDescriptionId = `${idPrefix}-slug-description`;
|