@jini-ai/ui 0.6.15-rc.2 → 0.6.16
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/CHANGELOG.md +12 -0
- package/dist/browser/useDismissOnOutsideOrEscape.js +1 -1
- package/dist/browser/useGlobalKeydown.js +2 -2
- package/dist/browser/useModalWindowDragGuard.js +1 -1
- package/dist/core/theme/css-name.d.ts +3 -0
- package/dist/core/theme/css-name.js +5 -0
- package/dist/core/theme/stylesheet.js +2 -1
- package/dist/core/theme/validation.js +15 -4
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/features/about/rules.js +2 -1
- package/dist/features/admin-widgets/components/InfoTip.hooks.js +22 -12
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.js +1 -1
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.js +1 -1
- package/dist/features/admin-widgets/components/SeeMore/SeeMore.hooks.js +2 -2
- package/dist/features/admin-widgets/components/SeeMore/SeeMore.js +4 -2
- package/dist/features/admin-widgets/components/Select/Select.hooks.js +2 -1
- package/dist/features/admin-widgets/components/Select/Select.js +2 -1
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.js +4 -2
- package/dist/features/admin-widgets/components/useDebouncedHtmlPreview.hooks.js +3 -2
- package/dist/features/appearance/constants.d.ts +28 -1
- package/dist/features/appearance/constants.js +12 -9
- package/dist/features/appearance/react/components/AppearanceTab.js +4 -4
- package/dist/features/asset-grid/react/components/AssetGrid.js +8 -3
- package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +7 -6
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +8 -10
- package/dist/features/asset-grid/react/hooks/useRubberBandDrag.js +1 -1
- package/dist/features/asset-tree-browser/react/components/AssetTreeFileRow.js +6 -8
- package/dist/features/asset-tree-browser/react/hooks/useAssetTreeRowMenu.js +1 -1
- package/dist/features/asset-tree-browser/rules.js +1 -3
- package/dist/features/browser-chrome/rules.js +10 -8
- package/dist/features/charts/browser.js +2 -1
- package/dist/features/charts/contract.d.ts +4 -4
- package/dist/features/charts/react.js +5 -3
- package/dist/features/charts/wire.d.ts +19 -0
- package/dist/features/charts/wire.js +20 -2
- package/dist/features/connectors/ConnectorsBrowser.js +3 -1
- package/dist/features/connectors/hooks/useConnectorAuthorization.js +1 -1
- package/dist/features/connectors/hooks/useConnectorCatalog.js +2 -2
- package/dist/features/connectors/rules.js +10 -14
- package/dist/features/execution/constants.js +0 -1
- package/dist/features/execution/react/components/AgentDiagnosticRow.js +2 -0
- package/dist/features/execution/react/components/ByokProviderForm.js +1 -3
- package/dist/features/execution/react/components/LocalCliAgentSettings.js +2 -1
- package/dist/features/execution/react/hooks/use-byok-field-validation.hooks.js +6 -2
- package/dist/features/execution/react/hooks/useExecutionTab.js +9 -5
- package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +3 -6
- package/dist/features/execution/react/hooks/useReasoningControl.js +3 -3
- package/dist/features/execution/rules.js +23 -18
- package/dist/features/external-mcp/react/components/ExternalMcpTab.js +10 -1
- package/dist/features/file-dropzone/rules.js +3 -2
- package/dist/features/folder-path-drop/rules.d.ts +2 -3
- package/dist/features/folder-path-drop/rules.js +3 -4
- package/dist/features/html-editor/canvas-embed-placeholders.js +2 -3
- package/dist/features/html-editor/css.js +7 -5
- package/dist/features/html-editor/editor-blocks.js +60 -6
- package/dist/features/html-editor/protected-elements.js +5 -3
- package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +3 -3
- package/dist/features/html-editor/source-splice.js +6 -4
- package/dist/features/i18n/dictionaries/settings-dialog.ar.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.bn.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.de.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.en.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.es.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.fa.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.fr.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.hi.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.hu.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.id.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.it.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ja.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ko.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.pl.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.pt-BR.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ru.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.th.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.tr.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.uk.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ur.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +8 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +8 -0
- package/dist/features/iframe-pool/react/components/IframeKeepAliveProvider.js +5 -5
- package/dist/features/iframe-pool/react/dom-sync.js +2 -2
- package/dist/features/integrations/react/components/IntegrationsTab.js +3 -1
- package/dist/features/integrations/rules.js +6 -4
- package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -5
- package/dist/features/interactive-ui/table-data.js +1 -1
- package/dist/features/lexical-rich-text-editor/deserialize.js +2 -2
- package/dist/features/lexical-rich-text-editor/mention-parser.js +4 -4
- package/dist/features/lexical-rich-text-editor/react/components/RichTextInput.js +2 -1
- package/dist/features/lexical-rich-text-editor/react/hooks/useCaretFloatingLayerPosition.js +1 -1
- package/dist/features/lexical-rich-text-editor/react/hooks/useMentionColorStamping.js +1 -1
- package/dist/features/lexical-rich-text-editor/serialize.js +13 -10
- package/dist/features/list-detail-panel/react/components/ListDetailPanel.js +1 -1
- package/dist/features/mcp-ui/confirmation-store.js +3 -6
- package/dist/features/mcp-ui/surfaces/checkbox.d.ts +2 -0
- package/dist/features/mcp-ui/surfaces/choice-group.d.ts +2 -0
- package/dist/features/mcp-ui/surfaces/choice-group.js +2 -2
- package/dist/features/mcp-ui/surfaces/confirmation.d.ts +5 -0
- package/dist/features/mcp-ui/surfaces/confirmation.js +1 -0
- package/dist/features/mcp-ui/surfaces/document.d.ts +14 -16
- package/dist/features/mcp-ui/surfaces/document.js +46 -7
- package/dist/features/mcp-ui/surfaces/fields.d.ts +7 -1
- package/dist/features/mcp-ui/surfaces/fields.js +14 -11
- package/dist/features/mcp-ui/surfaces/form.js +6 -1
- package/dist/features/mcp-ui/surfaces/host-theme.js +2 -1
- package/dist/features/mcp-ui/surfaces/index.d.ts +1 -0
- package/dist/features/mcp-ui/surfaces/index.js +1 -0
- package/dist/features/mcp-ui/surfaces/outcome.js +1 -1
- package/dist/features/mcp-ui/surfaces/select.d.ts +2 -0
- package/dist/features/mcp-ui/surfaces/select.js +2 -1
- package/dist/features/mcp-ui/surfaces/structured-details.d.ts +40 -0
- package/dist/features/mcp-ui/surfaces/structured-details.js +159 -0
- package/dist/features/mcp-ui/surfaces/text-input.d.ts +2 -0
- package/dist/features/mcp-ui/surfaces/text-input.js +1 -1
- package/dist/features/mcp-ui/surfaces/tool-approval.d.ts +21 -0
- package/dist/features/mcp-ui/surfaces/tool-approval.js +42 -0
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +2 -1
- package/dist/features/memory/async-commit-guard.js +4 -1
- package/dist/features/memory/formatters.js +5 -1
- package/dist/features/memory/react/components/MemoryConnectedPanel.js +3 -1
- package/dist/features/memory/react/components/MemoryEntryCard.js +1 -1
- package/dist/features/memory/react/components/MemorySettingsPanel.js +3 -1
- package/dist/features/memory/react/hooks/useMemoryConfig.hooks.js +12 -10
- package/dist/features/memory/react/hooks/useMemoryConnectors.hooks.js +2 -5
- package/dist/features/memory/react/hooks/useMemoryConnectors.rules.js +10 -9
- package/dist/features/memory/react/hooks/useMemoryEntries.hooks.js +4 -2
- package/dist/features/memory/react/hooks/useMemoryFlash.hooks.js +1 -1
- package/dist/features/memory/rules.js +2 -1
- package/dist/features/panel-kit/fetch-query/adapter.react.js +3 -2
- package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +3 -2
- package/dist/features/panel-kit/fetch-query/binding.react.js +2 -1
- package/dist/features/panel-kit/helpers/standing-draft-recovery-message.js +13 -4
- package/dist/features/panel-kit/hooks/use-dirty-guard.hooks.js +1 -1
- package/dist/features/panel-kit/hooks/use-focus-trap.hooks.js +1 -1
- package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +14 -10
- package/dist/features/panel-kit/hooks/use-standing-draft-autosave.hooks.js +2 -2
- package/dist/features/progress-card/components/ProgressCard.d.ts +1 -1
- package/dist/features/progress-card/reference-adapters.js +23 -13
- package/dist/features/progress-card/rules.d.ts +0 -1
- package/dist/features/progress-card/rules.js +14 -20
- package/dist/features/progress-card/types.d.ts +1 -1
- package/dist/features/project-locations/dependencies.js +6 -1
- package/dist/features/project-locations/react/components/ProjectLocationsTab.js +2 -1
- package/dist/features/project-locations/rules.js +2 -1
- package/dist/features/resource-dashboard/react/components/ResourceBoardToolbar.js +8 -1
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +2 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +3 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +2 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +1 -1
- package/dist/features/resource-dashboard/react/hooks/useResourceBoard.js +1 -1
- package/dist/features/schedule-picker/rules.js +13 -7
- package/dist/features/sketch-editor/constants.d.ts +0 -6
- package/dist/features/sketch-editor/constants.js +17 -15
- package/dist/features/sketch-editor/dom.js +4 -7
- package/dist/features/sketch-editor/react/components/SketchMainMenu.js +2 -1
- package/dist/features/sketch-editor/react/components/SketchSaveStateBadge.js +2 -1
- package/dist/features/sketch-editor/react/hooks/useSketchDomEnhancements.js +1 -1
- package/dist/features/sketch-editor/rules.js +13 -12
- package/dist/features/skills/dependencies.js +9 -4
- package/dist/features/skills/react/components/SkillRow.js +2 -1
- package/dist/features/skills/react/components/SkillsTab.js +2 -1
- package/dist/features/skills/rules.js +3 -3
- package/dist/features/source-config-list/agent-handles.js +4 -3
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +3 -1
- package/dist/features/tab-strip/react/components/TabBar.hooks.js +4 -2
- package/dist/features/version-manager/react/components/VersionPreviewFrame.js +4 -1
- package/dist/features/version-manager/react/components/VersionSidebar.js +8 -6
- package/dist/features/version-manager/react/hooks/usePreviewCanvasSize.js +1 -1
- package/dist/features/version-manager/react/hooks/useVersionManager.js +5 -7
- package/dist/features/version-manager/rules.js +3 -5
- package/dist/features/viewer-shell/react/components/CommentSidePanel.js +2 -1
- package/dist/features/viewer-shell/react/components/SegmentedToggle.js +2 -1
- package/dist/features/viewer-shell/react/components/ViewerShell.js +2 -1
- package/dist/features/viewer-shell/rules.js +8 -7
- package/dist/react/components/AgentIcon.js +0 -1
- package/dist/react/components/BrandLogo.js +10 -2
- package/dist/react/components/CustomSelect.js +1 -1
- package/dist/react/components/EditorIcon.js +17 -14
- package/dist/react/components/Loading.js +2 -1
- package/dist/react/components/OnboardingChipField.js +5 -1
- package/dist/react/components/OnboardingDropdown.js +6 -4
- package/dist/react/components/PaletteTweaks.js +7 -6
- package/dist/react/components/PillButton.js +2 -1
- package/dist/react/components/PopoverMenu.js +2 -1
- package/dist/react/components/RemixIcon.js +2 -1
- package/dist/react/components/SecretInput.hooks.d.ts +2 -25
- package/dist/react/components/SecretInput.hooks.js +2 -24
- package/dist/react/components/SecretInput.js +5 -4
- package/dist/react/components/Toast.js +4 -2
- package/dist/react/components/TooltipLayer.js +4 -1
- package/dist/react/components/WorkingDirPicker.js +5 -4
- package/dist/react/components/agent-icon-data-uris.generated.js +0 -1
- package/dist/react/components/remixicon-font-recovery.js +9 -1
- package/dist/react/components/select-popover.js +2 -2
- package/dist/react/hooks/useBrandFonts.js +1 -1
- package/dist/react/hooks/useEdgeAutoScroll.js +1 -1
- package/dist/react/hooks/useInView.js +9 -9
- package/dist/react/hooks/useResizableSplitPane.js +12 -20
- package/dist/react/mcp-ui/useHostSurfaceTokens.js +1 -1
- package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +7 -1
- package/dist/renderers/annotation-canvas/react/hooks/useAnnotationCanvas.controller.js +4 -2
- package/dist/renderers/annotation-canvas/react/hooks/useAnnotationCanvas.js +9 -5
- package/dist/renderers/annotation-canvas/rules.js +9 -13
- package/dist/renderers/annotation-canvas/theme.js +5 -2
- package/dist/renderers/renderers/index.js +1 -1
- package/dist/renderers/renderers/markdown.js +4 -6
- package/dist/renderers/shiki.js +7 -1
- package/dist/theme/apply.js +7 -3
- package/dist/utils/class-names.d.ts +3 -0
- package/dist/utils/class-names.js +5 -0
- package/dist/utils/design-md.js +3 -3
- package/dist/utils/ime-composing.d.ts +1 -1
- package/dist/utils/ime-composing.js +1 -2
- package/dist/utils/localized-url.js +2 -11
- package/dist/utils/markdown-scroll-sync.js +1 -1
- package/dist/utils/omit-keys.d.ts +3 -0
- package/dist/utils/omit-keys.js +8 -0
- package/dist/utils/polygon-selection.js +1 -1
- package/dist/utils/sse.js +3 -0
- package/dist/utils/trim-chars.d.ts +13 -0
- package/dist/utils/trim-chars.js +30 -0
- package/dist/utils/unhandled-case.d.ts +10 -0
- package/dist/utils/unhandled-case.js +12 -0
- package/dist/utils/unit-bezier.js +1 -3
- package/dist/utils/zip.js +6 -10
- package/package.json +32 -29
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_HOME_NAVIGATION_ENTRY, EMPTY_URL, HISTORY_LIMIT } from './constants.js';
|
|
2
|
+
import { trimTrailingChars } from '../../utils/trim-chars.js';
|
|
2
3
|
function escapeRegExp(value) {
|
|
3
4
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
4
5
|
}
|
|
@@ -46,7 +47,7 @@ export function formatAddressDisplayParts(url, title, homeLabel = DEFAULT_HOME_N
|
|
|
46
47
|
const fallback = labelFromUrl(url, homeLabel);
|
|
47
48
|
if (cleanTitle === fallback || cleanTitle === url)
|
|
48
49
|
return { url };
|
|
49
|
-
return { url: url
|
|
50
|
+
return { url: trimTrailingChars(url, '/'), title: cleanTitle };
|
|
50
51
|
}
|
|
51
52
|
export function formatAddressDisplay(url, title, homeLabel = DEFAULT_HOME_NAVIGATION_ENTRY.title) {
|
|
52
53
|
const parts = formatAddressDisplayParts(url, title, homeLabel);
|
|
@@ -81,7 +82,7 @@ export function isHistoryUrl(url) {
|
|
|
81
82
|
return url !== EMPTY_URL && (isHttpLikeUrl(url) || /^file:\/\//i.test(url));
|
|
82
83
|
}
|
|
83
84
|
export function sameUrl(left, right) {
|
|
84
|
-
return left
|
|
85
|
+
return trimTrailingChars(left, '/') === trimTrailingChars(right, '/');
|
|
85
86
|
}
|
|
86
87
|
function cleanIconUrl(url) {
|
|
87
88
|
const value = url?.trim();
|
|
@@ -98,11 +99,11 @@ export function isHistoryEntry(value) {
|
|
|
98
99
|
if (typeof value !== 'object' || value == null || Array.isArray(value))
|
|
99
100
|
return false;
|
|
100
101
|
const record = value;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
if (typeof record.url !== 'string' || typeof record.title !== 'string')
|
|
103
|
+
return false;
|
|
104
|
+
if (typeof record.lastVisitedAt !== 'number' || typeof record.visitCount !== 'number')
|
|
105
|
+
return false;
|
|
106
|
+
return record.iconUrl === undefined || typeof record.iconUrl === 'string';
|
|
106
107
|
}
|
|
107
108
|
/** Namespace + scope key -> storage key. Namespace defaults to a generic Jini prefix (see `constants.ts`); scope key is host-supplied (e.g. a project or tab id). */
|
|
108
109
|
export function historyStorageKey(namespace, scopeKey) {
|
|
@@ -234,7 +235,8 @@ function navigationTitle({ url }, { title, homeLabel }) {
|
|
|
234
235
|
function findRejoinTarget({ state, url }, { replacePendingTarget, pendingTarget }) {
|
|
235
236
|
const { navigationStack: stack, navigationIndex: index } = state;
|
|
236
237
|
const current = stack[index];
|
|
237
|
-
|
|
238
|
+
const replacingCurrent = current && replacePendingTarget && pendingTarget && sameUrl(current.url, pendingTarget);
|
|
239
|
+
if (current && (sameUrl(current.url, url) || replacingCurrent)) {
|
|
238
240
|
return { index, entry: current };
|
|
239
241
|
}
|
|
240
242
|
return [index - 1, index + 1]
|
|
@@ -111,7 +111,8 @@ export function mountChart({ element, spec, registry }, options = {}) {
|
|
|
111
111
|
async update({ spec: nextSpec, theme = input.theme }, _options = {}) {
|
|
112
112
|
if (destroyed)
|
|
113
113
|
return;
|
|
114
|
-
|
|
114
|
+
revision += 1;
|
|
115
|
+
const updateRevision = revision;
|
|
115
116
|
input = { spec: nextSpec, theme };
|
|
116
117
|
showData();
|
|
117
118
|
const preferred = candidates()[0];
|
|
@@ -15,12 +15,12 @@ export declare const chartSpecSchema: z.ZodObject<{
|
|
|
15
15
|
color: z.ZodOptional<z.ZodString>;
|
|
16
16
|
}, "strict", z.ZodTypeAny, {
|
|
17
17
|
key: string;
|
|
18
|
-
label?: string | undefined;
|
|
19
18
|
color?: string | undefined;
|
|
19
|
+
label?: string | undefined;
|
|
20
20
|
}, {
|
|
21
21
|
key: string;
|
|
22
|
-
label?: string | undefined;
|
|
23
22
|
color?: string | undefined;
|
|
23
|
+
label?: string | undefined;
|
|
24
24
|
}>, "many">;
|
|
25
25
|
title: z.ZodOptional<z.ZodString>;
|
|
26
26
|
description: z.ZodOptional<z.ZodString>;
|
|
@@ -122,8 +122,8 @@ export declare const chartSpecSchema: z.ZodObject<{
|
|
|
122
122
|
data: Record<string, ChartValue>[];
|
|
123
123
|
series: {
|
|
124
124
|
key: string;
|
|
125
|
-
label?: string | undefined;
|
|
126
125
|
color?: string | undefined;
|
|
126
|
+
label?: string | undefined;
|
|
127
127
|
}[];
|
|
128
128
|
title?: string | undefined;
|
|
129
129
|
provider?: string | undefined;
|
|
@@ -154,8 +154,8 @@ export declare const chartSpecSchema: z.ZodObject<{
|
|
|
154
154
|
data: Record<string, ChartValue>[];
|
|
155
155
|
series: {
|
|
156
156
|
key: string;
|
|
157
|
-
label?: string | undefined;
|
|
158
157
|
color?: string | undefined;
|
|
158
|
+
label?: string | undefined;
|
|
159
159
|
}[];
|
|
160
160
|
title?: string | undefined;
|
|
161
161
|
provider?: string | undefined;
|
|
@@ -6,7 +6,7 @@ import { InteractiveUiRegistry } from '../interactive-ui/registry.js';
|
|
|
6
6
|
import { chartSpecSchema, DEFAULT_CHART_THEME } from './contract.js';
|
|
7
7
|
import { mountChart } from './browser.js';
|
|
8
8
|
import { chartCapability, chartProvidersByKind } from './registry.js';
|
|
9
|
-
import { chartWireManifests, legacyChartSpec } from './wire.js';
|
|
9
|
+
import { chartWireManifests, legacyChartSpec, withSeriesLabels } from './wire.js';
|
|
10
10
|
/** React owns only the container; mountChart owns its descendants and lifecycle. */
|
|
11
11
|
export function ChartHost({ spec, registry, theme = DEFAULT_CHART_THEME, siteProvider, onProviderError }) {
|
|
12
12
|
const element = useRef(null);
|
|
@@ -34,10 +34,12 @@ export function ChartHost({ spec, registry, theme = DEFAULT_CHART_THEME, sitePro
|
|
|
34
34
|
/** Known wire kinds use the established shapes. Other providers register spec-based entries without
|
|
35
35
|
* adding fields or kinds to the core grammar. This closure is built once per host snapshot.
|
|
36
36
|
*/
|
|
37
|
+
/** Wire kinds that predate spec-based entries and keep their established prop shapes. */
|
|
38
|
+
const LEGACY_CHART_KINDS = ['bar', 'line', 'pie'];
|
|
37
39
|
function chartComponent(kind, providers, options) {
|
|
38
40
|
return function RegisteredChart(props) {
|
|
39
|
-
const legacyKind =
|
|
40
|
-
const spec = legacyKind ? legacyChartSpec({ kind: legacyKind, props }) : chartSpecSchema.parse(props.spec);
|
|
41
|
+
const legacyKind = LEGACY_CHART_KINDS.find((legacy) => legacy === kind);
|
|
42
|
+
const spec = withSeriesLabels({ spec: legacyKind ? legacyChartSpec({ kind: legacyKind, props }) : chartSpecSchema.parse(props.spec) });
|
|
41
43
|
return _jsx(ChartHost, { spec: spec, registry: providers, ...options });
|
|
42
44
|
};
|
|
43
45
|
}
|
|
@@ -8,32 +8,44 @@ export declare const rechartsBarChartPropsSchema: z.ZodObject<{
|
|
|
8
8
|
data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
|
|
9
9
|
categoryKey: z.ZodString;
|
|
10
10
|
valueKey: z.ZodString;
|
|
11
|
+
/** The legend/tooltip name for `valueKey` ("Sign-ups"); omitted, the key is humanized. */
|
|
12
|
+
valueLabel: z.ZodOptional<z.ZodString>;
|
|
11
13
|
color: z.ZodOptional<z.ZodString>;
|
|
12
14
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
13
15
|
data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
|
|
14
16
|
categoryKey: z.ZodString;
|
|
15
17
|
valueKey: z.ZodString;
|
|
18
|
+
/** The legend/tooltip name for `valueKey` ("Sign-ups"); omitted, the key is humanized. */
|
|
19
|
+
valueLabel: z.ZodOptional<z.ZodString>;
|
|
16
20
|
color: z.ZodOptional<z.ZodString>;
|
|
17
21
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
18
22
|
data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
|
|
19
23
|
categoryKey: z.ZodString;
|
|
20
24
|
valueKey: z.ZodString;
|
|
25
|
+
/** The legend/tooltip name for `valueKey` ("Sign-ups"); omitted, the key is humanized. */
|
|
26
|
+
valueLabel: z.ZodOptional<z.ZodString>;
|
|
21
27
|
color: z.ZodOptional<z.ZodString>;
|
|
22
28
|
}, z.ZodTypeAny, "passthrough">>;
|
|
23
29
|
export declare const rechartsLineChartPropsSchema: z.ZodObject<{
|
|
24
30
|
data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
|
|
25
31
|
categoryKey: z.ZodString;
|
|
26
32
|
valueKey: z.ZodString;
|
|
33
|
+
/** The legend/tooltip name for `valueKey` ("Sign-ups"); omitted, the key is humanized. */
|
|
34
|
+
valueLabel: z.ZodOptional<z.ZodString>;
|
|
27
35
|
color: z.ZodOptional<z.ZodString>;
|
|
28
36
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
29
37
|
data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
|
|
30
38
|
categoryKey: z.ZodString;
|
|
31
39
|
valueKey: z.ZodString;
|
|
40
|
+
/** The legend/tooltip name for `valueKey` ("Sign-ups"); omitted, the key is humanized. */
|
|
41
|
+
valueLabel: z.ZodOptional<z.ZodString>;
|
|
32
42
|
color: z.ZodOptional<z.ZodString>;
|
|
33
43
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
34
44
|
data: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
|
|
35
45
|
categoryKey: z.ZodString;
|
|
36
46
|
valueKey: z.ZodString;
|
|
47
|
+
/** The legend/tooltip name for `valueKey` ("Sign-ups"); omitted, the key is humanized. */
|
|
48
|
+
valueLabel: z.ZodOptional<z.ZodString>;
|
|
37
49
|
color: z.ZodOptional<z.ZodString>;
|
|
38
50
|
}, z.ZodTypeAny, "passthrough">>;
|
|
39
51
|
export declare const rechartsPieChartPropsSchema: z.ZodObject<{
|
|
@@ -85,4 +97,11 @@ export declare function legacyChartSpec({ kind, props }: {
|
|
|
85
97
|
kind: 'bar' | 'line' | 'pie';
|
|
86
98
|
props: Record<string, unknown>;
|
|
87
99
|
}, _options?: Record<string, never>): ChartSpec;
|
|
100
|
+
/**
|
|
101
|
+
* Give every series a label, so a provider's legend and tooltip never show a raw data key
|
|
102
|
+
* (owner, 2026-10-30: a sign-ups chart's legend read "signups"). A label the caller passed wins.
|
|
103
|
+
*/
|
|
104
|
+
export declare function withSeriesLabels({ spec }: {
|
|
105
|
+
spec: ChartSpec;
|
|
106
|
+
}, _options?: Record<string, never>): ChartSpec;
|
|
88
107
|
//# sourceMappingURL=wire.d.ts.map
|
|
@@ -4,7 +4,9 @@ import { chartSpecSchema } from './contract.js';
|
|
|
4
4
|
/** Same passthrough/record reasoning as native data-table: A2UI action bindings and extra row data
|
|
5
5
|
* belong to the wire protocol, not a renderer. Retain their established shapes during extraction. */
|
|
6
6
|
export const rechartsBarChartPropsSchema = z.object({
|
|
7
|
-
data: z.array(z.record(z.unknown())).min(1), categoryKey: z.string(), valueKey: z.string(),
|
|
7
|
+
data: z.array(z.record(z.unknown())).min(1), categoryKey: z.string(), valueKey: z.string(),
|
|
8
|
+
/** The legend/tooltip name for `valueKey` ("Sign-ups"); omitted, the key is humanized. */
|
|
9
|
+
valueLabel: z.string().min(1).optional().describe('Human name for valueKey shown in the legend and tooltip, e.g. "Sign-ups". Defaults to the humanized key.'), color: z.string().optional(),
|
|
8
10
|
}).passthrough();
|
|
9
11
|
export const rechartsLineChartPropsSchema = rechartsBarChartPropsSchema;
|
|
10
12
|
export const rechartsPieChartPropsSchema = z.object({
|
|
@@ -31,8 +33,24 @@ export function legacyChartSpec({ kind, props }, _options = {}) {
|
|
|
31
33
|
const parsed = (kind === 'pie' ? rechartsPieChartPropsSchema : rechartsBarChartPropsSchema).parse(props);
|
|
32
34
|
return chartSpecSchema.parse({
|
|
33
35
|
kind, data: parsed.data, categoryKey: kind === 'pie' ? 'name' : parsed.categoryKey,
|
|
34
|
-
series: [{
|
|
36
|
+
series: [{
|
|
37
|
+
key: kind === 'pie' ? 'value' : parsed.valueKey,
|
|
38
|
+
...('valueLabel' in parsed && typeof parsed.valueLabel === 'string' ? { label: parsed.valueLabel } : {}),
|
|
39
|
+
...(parsed.color !== undefined ? { color: parsed.color } : {}),
|
|
40
|
+
}],
|
|
35
41
|
...(typeof parsed.provider === 'string' ? { provider: parsed.provider } : {}),
|
|
36
42
|
});
|
|
37
43
|
}
|
|
44
|
+
/** `signups` -> `Signups`, `sign_ups`/`signUps` -> `Sign ups`. Display only; the key still addresses the data. */
|
|
45
|
+
function humanizeSeriesKey(key) {
|
|
46
|
+
const words = key.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ').trim().toLowerCase();
|
|
47
|
+
return words ? words[0].toUpperCase() + words.slice(1) : key;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Give every series a label, so a provider's legend and tooltip never show a raw data key
|
|
51
|
+
* (owner, 2026-10-30: a sign-ups chart's legend read "signups"). A label the caller passed wins.
|
|
52
|
+
*/
|
|
53
|
+
export function withSeriesLabels({ spec }, _options = {}) {
|
|
54
|
+
return { ...spec, series: spec.series.map((series) => ({ ...series, label: series.label ?? humanizeSeriesKey(series.key) })) };
|
|
55
|
+
}
|
|
38
56
|
//# sourceMappingURL=wire.js.map
|
|
@@ -67,6 +67,8 @@ export function ConnectorsBrowser({ unlocked, providerTabs = DEFAULT_PROVIDER_TA
|
|
|
67
67
|
onCancelAuthorization: (connectorId) => void authorization.cancelAuthorization(connectorId),
|
|
68
68
|
onOpenExternalUrl: (url) => void deps.data.openExternalUrl(url),
|
|
69
69
|
};
|
|
70
|
-
|
|
70
|
+
const categoryLabelProps = getCategoryLabel ? { getCategoryLabel } : {};
|
|
71
|
+
const gateProps = gate ? { gate: { ...gate, onClick: handleGateClick } } : {};
|
|
72
|
+
return (_jsxs("div", { className: "tab-panel connectors-panel connectors-panel-embedded", children: [_jsxs("div", { className: "tab-panel-toolbar", children: [_jsx("div", { className: "toolbar-left connectors-heading", children: _jsxs("div", { children: [_jsx("h2", { children: t('Connectors') }), _jsx("p", { children: t('Connect third-party tools and services.') })] }) }), _jsxs("div", { className: "toolbar-right", children: [_jsx(ProviderTabBar, { tabs: providerTabs, selectedId: selectedProvider, onSelect: handleProviderTabSelect, ...subHandleProp({ base: agentHandle, action: 'provider' }) }), _jsx(ConnectorSearchBar, { value: filter, onChange: setFilter, disabled: !unlocked, onFocus: () => onProviderTabClick?.('search_connectors'), ...subHandleProp({ base: agentHandle, action: 'search' }) })] })] }), _jsx(ConnectorAlertList, { alerts: alerts, onOpenDetails: detail.openDetails }), catalog.loading ? (_jsx(CenteredLoader, { label: t('Loading…') })) : (_jsx(ConnectorGrid, { connectors: filteredConnectors, locked: !unlocked, hasNoResults: hasNoResults, searchQuery: filter, pendingConnectorAction: authorization.pendingConnectorAction, authorizationPending: authorization.pending, authorizationCancelFailed: authorization.cancelFailed, toolsLoaded: catalog.enriched, ...actions, onOpenDetails: detail.openDetails, ...categoryLabelProps, onClearSearch: () => setFilter(''), ...gateProps, ...subHandleProp({ base: agentHandle, action: 'connector' }) })), _jsx(ConnectorsDetailPane, { detail: detail, authorization: authorization, unlocked: unlocked, actions: actions, getCategoryLabel: getCategoryLabel, getDisplayableAccountLabel: getDisplayableAccountLabel, agentHandle: agentHandle })] }));
|
|
71
73
|
}
|
|
72
74
|
//# sourceMappingURL=ConnectorsBrowser.js.map
|
|
@@ -176,7 +176,7 @@ export function useConnectorAuthorization(port, authPendingStorage, authBridge,
|
|
|
176
176
|
// Poll statuses while any authorization is in flight.
|
|
177
177
|
useEffect(() => {
|
|
178
178
|
if (Object.keys(pending).length === 0)
|
|
179
|
-
return;
|
|
179
|
+
return undefined;
|
|
180
180
|
const interval = setInterval(() => {
|
|
181
181
|
setPending((curr) => pruneConnectorAuthorizationPending(curr, Date.now()));
|
|
182
182
|
void reloadStatuses();
|
|
@@ -42,10 +42,10 @@ export function useConnectorCatalog(port, options) {
|
|
|
42
42
|
if (!unlocked || !port.fetchConnectorEnrichment) {
|
|
43
43
|
setEnriched(false);
|
|
44
44
|
setEnriching(false);
|
|
45
|
-
return;
|
|
45
|
+
return undefined;
|
|
46
46
|
}
|
|
47
47
|
if (enriched)
|
|
48
|
-
return;
|
|
48
|
+
return undefined;
|
|
49
49
|
let cancelled = false;
|
|
50
50
|
setEnriching(true);
|
|
51
51
|
void (async () => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { omitKeys } from '../../utils/omit-keys.js';
|
|
1
2
|
/** Restricts the catalog to a single provider tab's `match` predicate, defaulting to the first tab. */
|
|
2
3
|
export function scopeConnectorsToProvider(connectors, providerTabs, selectedProviderId) {
|
|
3
4
|
const tab = providerTabs.find((p) => p.id === selectedProviderId) ?? providerTabs[0];
|
|
@@ -176,8 +177,7 @@ export function applyConnectorStatuses(current, statuses) {
|
|
|
176
177
|
const next = statuses[connector.id];
|
|
177
178
|
if (!next)
|
|
178
179
|
return connector;
|
|
179
|
-
|
|
180
|
-
return { ...base, ...next };
|
|
180
|
+
return { ...omitKeys(connector, ['accountLabel', 'lastError']), ...next };
|
|
181
181
|
});
|
|
182
182
|
}
|
|
183
183
|
export function getConnectorDisplayToolCount(connector) {
|
|
@@ -214,8 +214,7 @@ export function mergeConnectorToolPreview(current, next, append) {
|
|
|
214
214
|
};
|
|
215
215
|
if (next.toolsNextCursor !== undefined)
|
|
216
216
|
return { ...merged, toolsNextCursor: next.toolsNextCursor };
|
|
217
|
-
|
|
218
|
-
return withoutCursor;
|
|
217
|
+
return omitKeys(merged, ['toolsNextCursor']);
|
|
219
218
|
}
|
|
220
219
|
export function mergeConnectorActionResult(current, next) {
|
|
221
220
|
const toolCount = next.toolCount ?? current.toolCount;
|
|
@@ -355,17 +354,14 @@ export function toolsBadgeTranslation(count) {
|
|
|
355
354
|
return { key: '1 tool' };
|
|
356
355
|
return { key: '{count} tools', vars: { count } };
|
|
357
356
|
}
|
|
357
|
+
const STATUS_LABELS = {
|
|
358
|
+
available: 'Available',
|
|
359
|
+
connected: 'Connected',
|
|
360
|
+
error: 'Error',
|
|
361
|
+
disabled: 'Disabled',
|
|
362
|
+
};
|
|
358
363
|
export function statusLabel(status) {
|
|
359
|
-
|
|
360
|
-
case 'available':
|
|
361
|
-
return 'Available';
|
|
362
|
-
case 'connected':
|
|
363
|
-
return 'Connected';
|
|
364
|
-
case 'error':
|
|
365
|
-
return 'Error';
|
|
366
|
-
case 'disabled':
|
|
367
|
-
return 'Disabled';
|
|
368
|
-
}
|
|
364
|
+
return STATUS_LABELS[status];
|
|
369
365
|
}
|
|
370
366
|
export function defaultCategoryLabel(category) {
|
|
371
367
|
return category;
|
|
@@ -70,7 +70,6 @@ export const DEFAULT_AGENT_DESCRIPTIONS = {
|
|
|
70
70
|
'cursor-agent': 'Cursor command line',
|
|
71
71
|
opencode: 'Open-source agent CLI',
|
|
72
72
|
qwen: 'Qwen coding CLI',
|
|
73
|
-
copilot: 'GitHub coding CLI',
|
|
74
73
|
devin: 'Cognition terminal CLI',
|
|
75
74
|
kimi: 'Moonshot Kimi CLI',
|
|
76
75
|
qoder: 'Alibaba coding CLI',
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useT } from '../../../i18n/index.js';
|
|
3
3
|
import { agentDiagnosticTooltip } from '../../rules.js';
|
|
4
4
|
import { Icon } from '../../../../react/components/Icon.js';
|
|
5
|
+
import { unhandledCase } from '../../../../utils/unhandled-case.js';
|
|
5
6
|
/**
|
|
6
7
|
* Maps one typed fix intent to a concrete icon button. `'setEnv'`/
|
|
7
8
|
* `'clearEnv'`/`'launchOAuth'` intentionally resolve to no button — same as
|
|
@@ -31,6 +32,7 @@ function resolveAction(intent, handlers, labels) {
|
|
|
31
32
|
case 'launchOAuth':
|
|
32
33
|
return null;
|
|
33
34
|
}
|
|
35
|
+
return unhandledCase(intent);
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
36
38
|
* Presents a single agent diagnostic as "one-line reason + fix button(s)".
|
|
@@ -133,9 +133,7 @@ function ByokConnectionResult({ connectionTest, canTestConnection }) {
|
|
|
133
133
|
return null;
|
|
134
134
|
if (connectionTest.status !== 'ok' && connectionTest.status !== 'error')
|
|
135
135
|
return null;
|
|
136
|
-
return (_jsx("div", { className: "jini-byok-test-row", children: _jsx("span", { className: `jini-byok-test-status is-${connectionTest.status}`, role: connectionTest.status === 'error' ? 'alert' : 'status', children: connectionTest.
|
|
137
|
-
? (connectionTest.message !== undefined ? t(connectionTest.message) : t('Connection succeeded'))
|
|
138
|
-
: t(connectionTest.message) }) }));
|
|
136
|
+
return (_jsx("div", { className: "jini-byok-test-row", children: _jsx("span", { className: `jini-byok-test-status is-${connectionTest.status}`, role: connectionTest.status === 'error' ? 'alert' : 'status', children: t(connectionTest.message ?? 'Connection succeeded') }) }));
|
|
139
137
|
}
|
|
140
138
|
/** Discovery failure, as an inline hint below the Model row. Renders nothing otherwise. */
|
|
141
139
|
function ByokDiscoveryError({ modelDiscovery }) {
|
|
@@ -20,7 +20,8 @@ function LocalCliAgentModelControls({ agent, card, onModelChange, agentHandle })
|
|
|
20
20
|
const { reasoning, source, sourceLabel, sourceHint } = card;
|
|
21
21
|
const { hasModels, hasReasoning, reasoningOptions, reasoningValue, reasoningDisabled, allowCustomModel, customActive, modelChoices, selectValue, customModelInputValue, } = reasoning;
|
|
22
22
|
const additionalOptions = allowCustomModel ? [{ value: CUSTOM_MODEL_SENTINEL, label: t('Custom…') }] : undefined;
|
|
23
|
-
|
|
23
|
+
const modelHandleProps = agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'model' }) } : {};
|
|
24
|
+
return (_jsxs("div", { className: "jini-agent-settings-row", children: [hasModels ? (_jsxs("div", { className: "jini-agent-settings-field jini-agent-settings-model", children: [_jsxs("div", { className: "jini-agent-settings-label", children: [_jsx("span", { className: "jini-field-label", children: t('Model') }), sourceLabel ? (_jsx("span", { className: `jini-agent-model-source ${source}`, children: sourceLabel })) : null] }), _jsx(SearchableModelSelect, { className: "jini-input jini-agent-model-select", ariaLabel: t('Model'), searchPlaceholder: t('Search models'), testId: `jini-agent-model-${agent.id}`, searchInputTestId: `jini-agent-model-search-${agent.id}`, ...modelHandleProps, value: selectValue, models: modelChoices, additionalOptions: additionalOptions, onChange: card.selectModel }), sourceHint ? _jsx("p", { className: "jini-field-hint", children: sourceHint }) : null] })) : null, customActive ? (_jsxs("label", { className: "jini-agent-settings-field", children: [_jsx("span", { className: "jini-field-label", children: t('Custom model id') }), _jsx("input", { className: "jini-input jini-agent-settings-custom", type: "text", "data-testid": `jini-agent-model-custom-${agent.id}`, value: customModelInputValue, placeholder: t('e.g. my-fine-tuned-model'), spellCheck: false, onChange: (event) => onModelChange(agent.id, event.target.value.trim()), ...agentHandleProps({}, { base: agentHandle, action: 'custom-model', role: 'field', label: t('Custom model id') }) })] })) : null, hasReasoning ? (_jsxs("label", { className: "jini-agent-settings-field", children: [_jsx("span", { className: "jini-field-label", children: t('Reasoning effort') }), _jsx("select", { className: "jini-input jini-agent-settings-reasoning", "data-testid": `jini-agent-reasoning-${agent.id}`, value: reasoningValue, disabled: reasoningDisabled, onChange: (event) => card.selectReasoning(event.target.value), ...agentHandleProps({}, { base: agentHandle, action: 'reasoning', role: 'field', label: t('Reasoning effort') }), children: reasoningOptions.map((option) => (_jsx("option", { value: option.id, children: option.label }, option.id))) })] })) : null] }));
|
|
24
25
|
}
|
|
25
26
|
/** Probe evidence and repair actions stay together beneath the model controls. */
|
|
26
27
|
function LocalCliAgentFeedback({ agent, card, onEnvChange }) {
|
|
@@ -14,8 +14,12 @@ export function useByokFieldValidation(required, optional = {}) {
|
|
|
14
14
|
return {
|
|
15
15
|
missing,
|
|
16
16
|
baseUrlInvalid,
|
|
17
|
-
isMissing: ({ field }, _options = {}) =>
|
|
18
|
-
(
|
|
17
|
+
isMissing: ({ field }, _options = {}) => {
|
|
18
|
+
const invalid = missing.has(field) || (field === 'baseUrl' && baseUrlInvalid);
|
|
19
|
+
if (!invalid || !optional.deferMissingFeedback)
|
|
20
|
+
return invalid;
|
|
21
|
+
return touched.providerId === config.providerId && touched.fields.has(field);
|
|
22
|
+
},
|
|
19
23
|
markTouched: ({ field }, _options = {}) => {
|
|
20
24
|
setTouched(current => ({
|
|
21
25
|
providerId: config.providerId,
|
|
@@ -48,7 +48,8 @@ export function useExecutionTab({ port, autoDetect = true, describeProbeError =
|
|
|
48
48
|
}, []);
|
|
49
49
|
const runDetection = useCallback((detect) => {
|
|
50
50
|
clearTimeout(detectionTimer.current);
|
|
51
|
-
|
|
51
|
+
detectionTicket.current += 1;
|
|
52
|
+
const ticket = detectionTicket.current;
|
|
52
53
|
const isCurrent = () => alive.current && detectionTicket.current === ticket;
|
|
53
54
|
setScan({ status: 'scanning' });
|
|
54
55
|
detect().then((found) => {
|
|
@@ -60,7 +61,7 @@ export function useExecutionTab({ port, autoDetect = true, describeProbeError =
|
|
|
60
61
|
if (found.some(agent => agent.discoveryPending)) {
|
|
61
62
|
detectionTimer.current = setTimeout(() => {
|
|
62
63
|
if (isCurrent())
|
|
63
|
-
runDetection(
|
|
64
|
+
runDetection(port.detectLocalAgents.bind(port));
|
|
64
65
|
}, 1000);
|
|
65
66
|
}
|
|
66
67
|
}, (error) => {
|
|
@@ -81,7 +82,8 @@ export function useExecutionTab({ port, autoDetect = true, describeProbeError =
|
|
|
81
82
|
runDetection(() => rescanFn.call(port));
|
|
82
83
|
}, [port, runDetection]);
|
|
83
84
|
const testConnection = useCallback((config) => {
|
|
84
|
-
|
|
85
|
+
connectionTestTicket.current += 1;
|
|
86
|
+
const ticket = connectionTestTicket.current;
|
|
85
87
|
const isCurrent = () => alive.current && connectionTestTicket.current === ticket;
|
|
86
88
|
setConnectionTest({ status: 'testing' });
|
|
87
89
|
port.testConnection(config).then((result) => {
|
|
@@ -100,7 +102,8 @@ export function useExecutionTab({ port, autoDetect = true, describeProbeError =
|
|
|
100
102
|
const run = port.testAgent;
|
|
101
103
|
if (!run)
|
|
102
104
|
return;
|
|
103
|
-
|
|
105
|
+
agentTestTicket.current += 1;
|
|
106
|
+
const ticket = agentTestTicket.current;
|
|
104
107
|
const isCurrent = () => alive.current && agentTestTicket.current === ticket;
|
|
105
108
|
setAgentTest({ status: 'testing', agentId });
|
|
106
109
|
run.call(port, agentId, model).then((result) => {
|
|
@@ -128,7 +131,8 @@ export function useExecutionTab({ port, autoDetect = true, describeProbeError =
|
|
|
128
131
|
const listModels = port.listModels;
|
|
129
132
|
if (!listModels)
|
|
130
133
|
return;
|
|
131
|
-
|
|
134
|
+
modelDiscoveryTicket.current += 1;
|
|
135
|
+
const ticket = modelDiscoveryTicket.current;
|
|
132
136
|
const isCurrent = () => alive.current && modelDiscoveryTicket.current === ticket;
|
|
133
137
|
setModelDiscovery({ status: 'loading' });
|
|
134
138
|
listModels.call(port, config).then((found) => {
|
|
@@ -53,14 +53,11 @@ export function useLocalCliAgentCard({ agent, config, agentTest, cliEnvFields, o
|
|
|
53
53
|
}
|
|
54
54
|
onReasoningChange(agent.id, nextValue);
|
|
55
55
|
};
|
|
56
|
-
const hasSettings = reasoning.hasModels ||
|
|
57
|
-
|
|
58
|
-
Boolean(onTest) ||
|
|
59
|
-
result !== null ||
|
|
60
|
-
cliEnvFieldsForAgent(cliEnvFields, agent.id).length > 0;
|
|
56
|
+
const hasSettings = reasoning.hasModels || reasoning.hasReasoning || Boolean(onTest) || result !== null;
|
|
57
|
+
const hasEnvironmentSettings = cliEnvFieldsForAgent(cliEnvFields, agent.id).length > 0;
|
|
61
58
|
return {
|
|
62
59
|
reasoning,
|
|
63
|
-
hasSettings,
|
|
60
|
+
hasSettings: hasSettings || hasEnvironmentSettings,
|
|
64
61
|
source,
|
|
65
62
|
sourceLabel,
|
|
66
63
|
sourceHint,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect } from 'react';
|
|
2
2
|
import { CUSTOM_MODEL_SENTINEL } from '../../constants.js';
|
|
3
3
|
import { modelIdForReasoningLevel, pickerModelOptions, reasoningModelGroupFor, reasoningModelGroups, selectedAgentModel, selectedAgentReasoning, shouldShowCustomModelInput, splitReasoningModelId, } from '../../rules.js';
|
|
4
|
-
export function useReasoningControl(optionsOrAgent, maybeConfig, maybeExplicitCustomMode) {
|
|
4
|
+
export function useReasoningControl(optionsOrAgent, maybeConfig, maybeExplicitCustomMode = false) {
|
|
5
5
|
const { agent, config, explicitCustomMode = false, onReasoningChange } = reasoningControlOptions(optionsOrAgent, maybeConfig, maybeExplicitCustomMode);
|
|
6
6
|
const models = agent.models ?? [];
|
|
7
7
|
const hasModels = models.length > 0;
|
|
@@ -23,7 +23,7 @@ export function useReasoningControl(optionsOrAgent, maybeConfig, maybeExplicitCu
|
|
|
23
23
|
onReasoningChange('');
|
|
24
24
|
// `reasoningOptionIdsKey` stands in for `reasoningOptions` (a fresh array every render) so this
|
|
25
25
|
// only re-runs when the narrowed vocabulary actually changes, not on every unrelated re-render.
|
|
26
|
-
//
|
|
26
|
+
// Dependency list intentionally follows the lifecycle rationale above.
|
|
27
27
|
}, [flatReasoningOptions.length, onReasoningChange, config.reasoningByAgentId, agent.id, reasoningOptionIdsKey]);
|
|
28
28
|
const { allowCustomModel, knownModelIds, customActive } = customModelState({ agent, modelGroups, models, resolvedModel, explicitCustomMode });
|
|
29
29
|
// A model-suffix agent picks a BASE model here; the effort control below supplies the rest of the
|
|
@@ -83,7 +83,7 @@ export function useReasoningControl(optionsOrAgent, maybeConfig, maybeExplicitCu
|
|
|
83
83
|
}
|
|
84
84
|
/** The two call shapes (options object, or the older positional `agent, config, explicitCustomMode`)
|
|
85
85
|
* as one options object. */
|
|
86
|
-
function reasoningControlOptions(optionsOrAgent, maybeConfig, maybeExplicitCustomMode) {
|
|
86
|
+
function reasoningControlOptions(optionsOrAgent, maybeConfig, maybeExplicitCustomMode = false) {
|
|
87
87
|
if ('agent' in optionsOrAgent && 'config' in optionsOrAgent)
|
|
88
88
|
return optionsOrAgent;
|
|
89
89
|
return {
|
|
@@ -118,10 +118,10 @@ function foreignKeyOwner(key, preset, presets) {
|
|
|
118
118
|
const prefix = candidate.apiKeyPrefix;
|
|
119
119
|
if (!prefix || candidate.custom || candidate.id === preset.id)
|
|
120
120
|
continue;
|
|
121
|
-
if (prefix.length
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
if (prefix.length > bestClaim && key.startsWith(prefix)) {
|
|
122
|
+
owner = candidate;
|
|
123
|
+
bestClaim = prefix.length;
|
|
124
|
+
}
|
|
125
125
|
}
|
|
126
126
|
return owner;
|
|
127
127
|
}
|
|
@@ -328,9 +328,9 @@ export function cleanAgentVersionLabel(label, version) {
|
|
|
328
328
|
* binary path, so the card stays one line without discarding either.
|
|
329
329
|
*/
|
|
330
330
|
export function agentMetaLabel(agent, labels) {
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
331
|
+
const authUnresolved = agent.authStatus === 'missing' || agent.authStatus === 'unknown';
|
|
332
|
+
const pathTitle = agent.path ?? '';
|
|
333
|
+
const title = authUnresolved ? (agent.authMessage ?? pathTitle) : pathTitle;
|
|
334
334
|
if (!agent.installed)
|
|
335
335
|
return { text: labels.notInstalled, title };
|
|
336
336
|
if (agent.authStatus === 'missing')
|
|
@@ -505,10 +505,11 @@ export function pickerModelOptions({ models }, _optional = {}) {
|
|
|
505
505
|
const existing = choices.get(id);
|
|
506
506
|
if (!existing) {
|
|
507
507
|
choices.set(id, option);
|
|
508
|
+
continue;
|
|
508
509
|
}
|
|
509
|
-
|
|
510
|
+
// A later duplicate only upgrades an entry whose label was just its id.
|
|
511
|
+
if (existing.label === id && label !== id)
|
|
510
512
|
choices.set(id, { ...existing, label });
|
|
511
|
-
}
|
|
512
513
|
}
|
|
513
514
|
return [...choices.values()];
|
|
514
515
|
}
|
|
@@ -578,21 +579,25 @@ export function splitReasoningModelId(modelId, levelIds) {
|
|
|
578
579
|
* has no level, so nothing is stripped, and a vendor label that happens to end in an unrelated
|
|
579
580
|
* parenthetical keeps it.
|
|
580
581
|
*/
|
|
581
|
-
/** `Head (Inner)` with optional surrounding whitespace; group 1 is the head, group 2 the qualifier. */
|
|
582
|
-
const TRAILING_PARENTHETICAL = /^(.*?)\s*\(([^()]*)\)\s*$/;
|
|
583
582
|
function baseLabelForLevel(label, level) {
|
|
584
583
|
if (!level)
|
|
585
584
|
return label;
|
|
586
|
-
|
|
585
|
+
const trimmed = label.trimEnd();
|
|
586
|
+
if (!trimmed.endsWith(')'))
|
|
587
|
+
return label;
|
|
588
|
+
const open = trimmed.lastIndexOf('(');
|
|
589
|
+
if (open < 0)
|
|
590
|
+
return label;
|
|
591
|
+
const head = trimmed.slice(0, open).trimEnd();
|
|
592
|
+
const qualifier = trimmed.slice(open + 1, -1);
|
|
593
|
+
// A matched terminal group always supplies both parts; the old capture fallbacks were unreachable.
|
|
594
|
+
// The original head's dot did not span line breaks, and the qualifier excluded parentheses.
|
|
595
|
+
if (/[\r\n\u2028\u2029]/.test(head) || qualifier.includes(')'))
|
|
587
596
|
return label;
|
|
588
|
-
|
|
589
|
-
// `replace` (which yields a plain string) replaces the old `match[n] ?? ''` fallbacks, which no
|
|
590
|
-
// matching label could ever reach.
|
|
591
|
-
const inner = label.replace(TRAILING_PARENTHETICAL, '$2').trim().toLowerCase();
|
|
597
|
+
const inner = qualifier.trim().toLowerCase();
|
|
592
598
|
if (inner !== level.id.toLowerCase() && inner !== level.label.toLowerCase())
|
|
593
599
|
return label;
|
|
594
|
-
|
|
595
|
-
return stripped || label;
|
|
600
|
+
return head.trim() || label;
|
|
596
601
|
}
|
|
597
602
|
/**
|
|
598
603
|
* Groups an agent's model list by base model, recording for each base the effort levels it
|
|
@@ -48,7 +48,16 @@ export function ExternalMcpTab({ dependencies, fieldSpecs = MCP_SOURCE_FIELD_SPE
|
|
|
48
48
|
// A real load failure always wins over the host's static/decorative banner
|
|
49
49
|
// text — showing both would just be two banners saying overlapping things.
|
|
50
50
|
const banner = list.error ?? connectionError;
|
|
51
|
-
|
|
51
|
+
const draftTestResultProps = list.testResults[DRAFT_TEST_SCOPE] ? { testResult: list.testResults[DRAFT_TEST_SCOPE] } : {};
|
|
52
|
+
return (_jsxs("section", { className: "external-mcp-tab", children: [_jsxs("div", { className: "external-mcp-head", children: [_jsxs("div", { children: [_jsx("h3", { children: t('External MCP servers') }), _jsx("p", { className: "external-mcp-subtitle", children: t('Third-party tools for your coding agent.') })] }), _jsxs("button", { type: "button", className: "external-mcp-add-button", onClick: () => setFormOpen((open) => !open), "aria-expanded": formOpen, children: [_jsx(Icon, { name: "plus", size: 14 }), _jsx("span", { children: t('Add server') })] })] }), banner ? (_jsx("div", { className: "external-mcp-banner", role: "alert", children: t(banner) })) : null, formOpen ? (_jsx(SourceConfigAddForm, { fieldSpecs: fieldSpecs, values: addForm.values, validation: addForm.validation, submitAttempted: addForm.submitAttempted, submitting: addForm.submitting, submitError: addForm.submitError, onFieldChange: addForm.setField, onTrustChange: addForm.setTrust, onSubmit: () => void addForm.submit(), canTest: list.capabilities.canTest, testing: list.isPending(DRAFT_TEST_SCOPE, 'test'), ...draftTestResultProps, onTest: () => void list.test(undefined, addForm.values), addLabel: "Add server" })) : null, list.loading ? (_jsx("div", { className: "source-config-list-loading", role: "status", children: t('Loading…') })) : (_jsx(ExternalMcpSources, { list: list, fieldSpecs: fieldSpecs, trustOptions: trustOptions })), _jsx(ExternalMcpFooter, { saveStatusLabel: saveStatusLabel, configPath: configPath })] }));
|
|
53
|
+
}
|
|
54
|
+
/** The configured servers, or the empty-state hint when there are none yet. */
|
|
55
|
+
function ExternalMcpSources({ list, fieldSpecs, trustOptions, }) {
|
|
56
|
+
const t = useT();
|
|
57
|
+
if (list.sources.length === 0) {
|
|
58
|
+
return (_jsxs("div", { className: "external-mcp-empty", children: [_jsx("p", { className: "external-mcp-empty-title", children: t('No MCP servers configured.') }), _jsx("p", { className: "external-mcp-empty-hint", children: t('Click "Add server" to get started — pick a template or set up a custom stdio / HTTP server.') })] }));
|
|
59
|
+
}
|
|
60
|
+
return (_jsx("div", { className: "source-config-list-items", children: list.sources.map((source) => (_jsx(SourceConfigItemCard, { source: source, fieldSpecs: fieldSpecs, ...(trustOptions ? { trustOptions } : {}), capabilities: list.capabilities, removing: list.isPending(source.id, 'remove'), refreshing: list.isPending(source.id, 'refresh'), settingTrust: list.isPending(source.id, 'trust'), testing: list.isPending(source.id, 'test'), updating: list.isPending(source.id, 'update'), onRefresh: () => void list.refresh(source.id), onRemove: () => void list.remove(source.id), onTrustChange: (trust) => void list.setTrust(source.id, trust), onTest: () => void list.test(source.id), onUpdate: (patch) => void list.update(source.id, patch), ...(list.testResults[source.id] ? { testResult: list.testResults[source.id] } : {}) }, source.id))) }));
|
|
52
61
|
}
|
|
53
62
|
/** Renders the save-status footer as one unit; a path alone does not show it. */
|
|
54
63
|
function ExternalMcpFooter({ saveStatusLabel, configPath }) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* DOM-shaped value (`File`, event targets) arrives as a parameter.
|
|
4
4
|
*/
|
|
5
5
|
import { FILE_DROPZONE_KIND_BY_EXTENSION } from './constants.js';
|
|
6
|
+
import { trimChars } from '../../utils/trim-chars.js';
|
|
6
7
|
/** Lowercased extension (no dot), or `''` for an extensionless name. */
|
|
7
8
|
export function fileDropzoneExtension(file) {
|
|
8
9
|
return (/\.([a-z0-9]+)$/i.exec(file.name)?.[1] ?? '').toLowerCase();
|
|
@@ -29,7 +30,7 @@ export function fileDropzoneKind(file) {
|
|
|
29
30
|
}
|
|
30
31
|
/** Kinds renderable straight from an object URL (`blob:`). Fonts also need one (`FontFace` loads from a URL); text is read via `FileReader`/`Blob.text()` instead. */
|
|
31
32
|
export function fileDropzoneNeedsObjectUrl(kind) {
|
|
32
|
-
return
|
|
33
|
+
return ['image', 'video', 'audio', 'pdf', 'html', 'font'].includes(kind);
|
|
33
34
|
}
|
|
34
35
|
/** Uppercased, 4-char-capped extension for the glyph badge (e.g. `"a/b.jpeg"` -> `"JPEG"`, extensionless -> `"FILE"`). */
|
|
35
36
|
export function fileDropzoneExtensionLabel(file) {
|
|
@@ -50,7 +51,7 @@ export function fileDropzoneStagingKey(file) {
|
|
|
50
51
|
}
|
|
51
52
|
/** CSS-safe, collision-resistant `@font-face` family name for a staged font at `index`. */
|
|
52
53
|
export function fileDropzoneFontFamilyName(file, index) {
|
|
53
|
-
const slug = file.name.replace(/[^a-z0-9]+/gi, '-')
|
|
54
|
+
const slug = trimChars(file.name.replace(/[^a-z0-9]+/gi, '-'), '-').slice(0, 24);
|
|
54
55
|
return `jini-file-dropzone-font-${index}-${slug || 'font'}`;
|
|
55
56
|
}
|
|
56
57
|
/** Whether a selection is large enough (by count or total bytes) to warrant a loading affordance while it's staged. */
|
|
@@ -24,9 +24,8 @@ import type { CaptureFolderPathDropInput } from './types.js';
|
|
|
24
24
|
*/
|
|
25
25
|
export declare function folderPathsFromDataTransfer(dataTransfer: DataTransfer, port: FolderPathDropPort): string[];
|
|
26
26
|
/**
|
|
27
|
-
* The text a folder drop inserts: every path, space-joined, in drop order.
|
|
28
|
-
*
|
|
29
|
-
* or separators belongs here and nowhere else.
|
|
27
|
+
* The text a folder drop inserts: every path, space-joined, in drop order. Quote whitespace-containing
|
|
28
|
+
* paths so text consumers can distinguish the path from adjacent prose or secrets.
|
|
30
29
|
*
|
|
31
30
|
* @complexity Time/space: O(total path length).
|
|
32
31
|
*/
|