@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
|
@@ -54,10 +54,15 @@ function formActions(spec) {
|
|
|
54
54
|
actions.push({ id: 'cancel', label: spec.cancel.label, variant: 'neutral' });
|
|
55
55
|
return actions;
|
|
56
56
|
}
|
|
57
|
+
/** Case- and whitespace-insensitive equality: a field label that only repeats the card title is the same question. */
|
|
58
|
+
function sameText(a, b) {
|
|
59
|
+
const norm = (s) => s.trim().replace(/\s+/g, ' ').toLowerCase();
|
|
60
|
+
return norm(a) === norm(b);
|
|
61
|
+
}
|
|
57
62
|
/** Header, context, the `<form>` itself and the status region. @complexity O(n) in the rendered length. */
|
|
58
63
|
function formBodyHtml(spec, text) {
|
|
59
64
|
const controls = spec.fields
|
|
60
|
-
.map((field) => renderFieldControl(field, { showSecret: text.showSecret, hideSecret: text.hideSecret }))
|
|
65
|
+
.map((field) => renderFieldControl(field, { showSecret: text.showSecret, hideSecret: text.hideSecret }, { labelHidden: sameText(field.label, spec.title) }))
|
|
61
66
|
.join('\n');
|
|
62
67
|
const warning = spec.warning === undefined ? '' : `<p class="mcpui-warning">${escapeHtml(spec.warning)}</p>`;
|
|
63
68
|
return [
|
|
@@ -79,7 +79,8 @@ export function applyHostSurfaceTokens(html, tokens) {
|
|
|
79
79
|
if (close === -1)
|
|
80
80
|
return html;
|
|
81
81
|
const at = close + '</style>'.length;
|
|
82
|
-
const
|
|
82
|
+
const declarations = entries.map(([name, value]) => ` ${name}: ${value};`).join('\n');
|
|
83
|
+
const rule = `\n<style ${HOST_THEME_STYLE_ATTRIBUTE}>\n:root {\n${declarations}\n}\n</style>`;
|
|
83
84
|
return `${html.slice(0, at)}${rule}${html.slice(at)}`;
|
|
84
85
|
}
|
|
85
86
|
//# sourceMappingURL=host-theme.js.map
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
*/
|
|
45
45
|
import { createUIResource } from '../resource.js';
|
|
46
46
|
import { escapeHtml, escapeJsValue } from '../escape.js';
|
|
47
|
-
import { renderActions, renderDetailList,
|
|
47
|
+
import { renderActions, renderDetailList, renderSurfaceDocument, renderSurfaceHeader, SURFACE_SCRIPT_PRELUDE, SURFACE_STATUS_ELEMENT_ID, } from './document.js';
|
|
48
48
|
const DEFAULT_APP = { appName: 'jini-mcp-ui-outcome', appVersion: '1' };
|
|
49
49
|
const DEFAULT_OPEN_LINK_LABEL = 'Open site';
|
|
50
50
|
const OPEN_LINK_ACTION_ID = 'open-link';
|
|
@@ -23,6 +23,8 @@ export interface SelectProps {
|
|
|
23
23
|
/** Label for that leading empty option. Defaults to an em-dash. */
|
|
24
24
|
readonly placeholder?: string;
|
|
25
25
|
readonly disabled?: boolean;
|
|
26
|
+
/** Visually hide the label (it repeats the card title); it stays the accessible name. */
|
|
27
|
+
readonly labelHidden?: boolean;
|
|
26
28
|
}
|
|
27
29
|
/**
|
|
28
30
|
* Renders one enumerated field.
|
|
@@ -17,8 +17,9 @@ import { fieldDescribedBy, fieldElementId, renderFieldLabel } from './document.j
|
|
|
17
17
|
*/
|
|
18
18
|
export function renderSelect(props) {
|
|
19
19
|
const id = fieldElementId(props.name);
|
|
20
|
+
const placeholderSelected = props.value === undefined ? 'selected ' : '';
|
|
20
21
|
const placeholderOption = props.required === true
|
|
21
|
-
? `\n <option value="" ${
|
|
22
|
+
? `\n <option value="" ${placeholderSelected}disabled>${escapeHtml(props.placeholder ?? '—')}</option>`
|
|
22
23
|
: '';
|
|
23
24
|
const options = props.options
|
|
24
25
|
.map((option) => {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** One fact. Values and previous snapshots must be JSON-serializable; strings remain supported. */
|
|
2
|
+
export interface SurfaceDetail {
|
|
3
|
+
readonly label: string;
|
|
4
|
+
readonly value: unknown;
|
|
5
|
+
/** Presence enables a diff, including an explicitly absent previous override. */
|
|
6
|
+
readonly previous?: unknown;
|
|
7
|
+
/** Explicit, optionally translated labels keyed by dotted object path. */
|
|
8
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
9
|
+
/** Optional secondary context beneath the label, using the shared muted hint style. */
|
|
10
|
+
readonly description?: string;
|
|
11
|
+
/**
|
|
12
|
+
* `code` discloses a monospace `<pre>` that keeps line breaks and scrolls inside its own box
|
|
13
|
+
* past a fixed height, so long SQL or JSON remains readable without stretching the dialog past
|
|
14
|
+
* its frame. `status` reserves one line for a short outcome value. Omitted (or `text`) permits
|
|
15
|
+
* value-sensitive presentation, preserving plain inline strings for short prose.
|
|
16
|
+
*/
|
|
17
|
+
readonly format?: 'text' | 'code' | 'status';
|
|
18
|
+
}
|
|
19
|
+
type ValueContext = {
|
|
20
|
+
value: unknown;
|
|
21
|
+
path: string;
|
|
22
|
+
labels: Readonly<Record<string, string>>;
|
|
23
|
+
format?: SurfaceDetail['format'];
|
|
24
|
+
};
|
|
25
|
+
/** Native details owns keyboard interaction; mirror its state for explicit ARIA consumers. */
|
|
26
|
+
export declare const DETAIL_DISCLOSURE_SCRIPT = "document.addEventListener(\"toggle\", function (event) {\n var disclosure = event.target;\n if (!disclosure.matches || !disclosure.matches(\"details.mcpui-detail-disclosure\")) return;\n disclosure.querySelector(\"summary\").setAttribute(\"aria-expanded\", disclosure.open ? \"true\" : \"false\");\n}, true);";
|
|
27
|
+
/** Humanized English fallback; caller labels remain verbatim. @complexity O(k) key length. */
|
|
28
|
+
export declare function surfaceDetailLabel({ key, path, labels }: {
|
|
29
|
+
key: string;
|
|
30
|
+
path: string;
|
|
31
|
+
labels: ValueContext['labels'];
|
|
32
|
+
}, _options?: {}): string;
|
|
33
|
+
/** One reusable detail body. Raw JSON is escaped text, never a script or HTML fragment.
|
|
34
|
+
* @returns HTML for JSON-compatible caller data; JSON.stringify errors propagate for invalid data.
|
|
35
|
+
* @complexity O(n * depth) with a previous snapshot, otherwise O(n), in serialized bytes. */
|
|
36
|
+
export declare function renderStructuredDetail({ detail }: {
|
|
37
|
+
detail: SurfaceDetail;
|
|
38
|
+
}, _options?: {}): string;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=structured-details.d.ts.map
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/** Shared value presentation for confirmation, form and outcome details. Never interprets HTML. */
|
|
2
|
+
import { escapeHtml } from '../escape.js';
|
|
3
|
+
/** Native details owns keyboard interaction; mirror its state for explicit ARIA consumers. */
|
|
4
|
+
export const DETAIL_DISCLOSURE_SCRIPT = `document.addEventListener("toggle", function (event) {
|
|
5
|
+
var disclosure = event.target;
|
|
6
|
+
if (!disclosure.matches || !disclosure.matches("details.mcpui-detail-disclosure")) return;
|
|
7
|
+
disclosure.querySelector("summary").setAttribute("aria-expanded", disclosure.open ? "true" : "false");
|
|
8
|
+
}, true);`;
|
|
9
|
+
/** Humanized English fallback; caller labels remain verbatim. @complexity O(k) key length. */
|
|
10
|
+
export function surfaceDetailLabel({ key, path, labels }, _options = {}) {
|
|
11
|
+
if (Object.hasOwn(labels, path))
|
|
12
|
+
return labels[path];
|
|
13
|
+
const words = key.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/([A-Z])([A-Z][a-z])/g, '$1 $2').replace(/[_-]+/g, ' ').toLowerCase();
|
|
14
|
+
const acronyms = words.replace(/\b(url|css|html|json|api|id)\b/g, word => word.toUpperCase());
|
|
15
|
+
return acronyms.charAt(0).toUpperCase() + acronyms.slice(1);
|
|
16
|
+
}
|
|
17
|
+
function isRecord(value) {
|
|
18
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
19
|
+
}
|
|
20
|
+
function disclosure({ summary, body, kind = '' }, _options = {}) {
|
|
21
|
+
return `<details class="mcpui-detail-disclosure ${kind}"><summary aria-expanded="false">${escapeHtml(summary)}</summary>${body}</details>`;
|
|
22
|
+
}
|
|
23
|
+
/** JSON scalar display; missing override values use the same empty marker as null. */
|
|
24
|
+
function scalarText({ value }, _options = {}) {
|
|
25
|
+
if (value === undefined || value === null || value === '')
|
|
26
|
+
return '—';
|
|
27
|
+
if (typeof value === 'boolean')
|
|
28
|
+
return value ? 'On' : 'Off';
|
|
29
|
+
return String(value);
|
|
30
|
+
}
|
|
31
|
+
/** Classifies content for a disclosure, preserving the original text bytes in the body. */
|
|
32
|
+
function codeKind({ value, path, format }, _options = {}) {
|
|
33
|
+
const hasDeclaration = value.split('}').slice(0, -1).some(part => {
|
|
34
|
+
const open = part.indexOf('{');
|
|
35
|
+
return open >= 0 && part.indexOf(':', open + 1) >= 0;
|
|
36
|
+
});
|
|
37
|
+
if (/css$/i.test(path) || (hasDeclaration && !/^\s*[\[{]/.test(value)))
|
|
38
|
+
return 'CSS';
|
|
39
|
+
if (/^\s*</.test(value))
|
|
40
|
+
return 'HTML';
|
|
41
|
+
if (/^\s*[\[{]/.test(value))
|
|
42
|
+
return 'JSON';
|
|
43
|
+
if (format === 'code')
|
|
44
|
+
return 'Code';
|
|
45
|
+
return /[\r\n]/.test(value) ? 'Text' : undefined;
|
|
46
|
+
}
|
|
47
|
+
/** Escapes all text once at the HTML sink. Long prose uses a three-line summary. */
|
|
48
|
+
function renderText({ value, path, format }, _options = {}) {
|
|
49
|
+
const text = escapeHtml(value);
|
|
50
|
+
const kind = codeKind({ value, path, format });
|
|
51
|
+
if (kind) {
|
|
52
|
+
const lines = value.split(/\r\n|\r|\n/).length;
|
|
53
|
+
return disclosure({ summary: `${kind} · ${lines} ${lines === 1 ? 'line' : 'lines'}`, body: `<pre class="mcpui-code">${text}</pre>`, kind: 'mcpui-detail-code' });
|
|
54
|
+
}
|
|
55
|
+
if (value.length <= 80)
|
|
56
|
+
return text;
|
|
57
|
+
return `<details class="mcpui-detail-disclosure mcpui-detail-long"><summary aria-expanded="false"><span class="mcpui-detail-preview">${text}</span><span class="mcpui-detail-more">Show more</span><span class="mcpui-detail-less">Show less</span></summary><div class="mcpui-detail-full">${text}</div></details>`;
|
|
58
|
+
}
|
|
59
|
+
/** Arrays keep scalar items compact; nested or long items use readable bullet rows. */
|
|
60
|
+
function renderArray({ value, ...context }, _options = {}) {
|
|
61
|
+
if (value.length === 0)
|
|
62
|
+
return '<span class="mcpui-detail-value">—</span>';
|
|
63
|
+
const compact = value.every(item => item === null || ['boolean', 'number', 'string'].includes(typeof item))
|
|
64
|
+
&& value.every(item => String(item).length <= 80 && !/[\r\n<{[]/.test(String(item)));
|
|
65
|
+
if (compact)
|
|
66
|
+
return renderText({ ...context, value: value.map(item => scalarText({ value: item })).join(', ') });
|
|
67
|
+
const items = value.map((item, index) => {
|
|
68
|
+
const path = context.path ? `${context.path}.${index}` : String(index);
|
|
69
|
+
return `<li>${renderValue({ ...context, value: item, path })}</li>`;
|
|
70
|
+
});
|
|
71
|
+
const rest = items.length <= 5 ? '' : disclosure({ summary: `Show more (${items.length - 5} items)`, body: `<ul class="mcpui-detail-array">${items.slice(5).join('')}</ul>` });
|
|
72
|
+
return `<ul class="mcpui-detail-array">${items.slice(0, 5).join('')}</ul>${rest}`;
|
|
73
|
+
}
|
|
74
|
+
function objectRow({ key, context, body }, _options = {}) {
|
|
75
|
+
return `<div class="mcpui-detail-row"><dt>${escapeHtml(surfaceDetailLabel({ key, path: context.path, labels: context.labels }))}</dt><dd>${body}</dd></div>`;
|
|
76
|
+
}
|
|
77
|
+
/** Nested objects stay definition lists, indented once per actual group. @complexity O(n) rendered bytes. */
|
|
78
|
+
function renderObject({ value, ...context }, _options = {}) {
|
|
79
|
+
const entries = Object.entries(value);
|
|
80
|
+
if (entries.length === 0)
|
|
81
|
+
return '<span class="mcpui-detail-value">—</span>';
|
|
82
|
+
const rows = entries.map(([key, item]) => {
|
|
83
|
+
const child = { ...context, value: item, path: context.path ? `${context.path}.${key}` : key };
|
|
84
|
+
return objectRow({ key, context: child, body: renderValue(child) });
|
|
85
|
+
}).join('');
|
|
86
|
+
return `<dl class="mcpui-detail-group">${rows}</dl>`;
|
|
87
|
+
}
|
|
88
|
+
/** Pure recursive rendering, O(n) in output bytes and O(depth) traversal stack; no external I/O. */
|
|
89
|
+
function renderValue(context, _options = {}) {
|
|
90
|
+
const { value } = context;
|
|
91
|
+
if (Array.isArray(value))
|
|
92
|
+
return renderArray({ ...context, value });
|
|
93
|
+
if (isRecord(value))
|
|
94
|
+
return renderObject({ ...context, value });
|
|
95
|
+
if (typeof value === 'string' && value !== '')
|
|
96
|
+
return renderText({ ...context, value });
|
|
97
|
+
return escapeHtml(scalarText({ value }));
|
|
98
|
+
}
|
|
99
|
+
/** Object order is immaterial; array order and missing keys matter to an approval diff. */
|
|
100
|
+
function equalValue({ before, after }, _options = {}) {
|
|
101
|
+
if (Object.is(before, after))
|
|
102
|
+
return true;
|
|
103
|
+
if (Array.isArray(before) && Array.isArray(after)) {
|
|
104
|
+
return before.length === after.length && before.every((item, index) => equalValue({ before: item, after: after[index] }));
|
|
105
|
+
}
|
|
106
|
+
if (!isRecord(before) || !isRecord(after))
|
|
107
|
+
return false;
|
|
108
|
+
const keys = Object.keys(before);
|
|
109
|
+
return keys.length === Object.keys(after).length && keys.every(key => Object.hasOwn(after, key) && equalValue({ before: before[key], after: after[key] }));
|
|
110
|
+
}
|
|
111
|
+
/** A missing JSON key must never read an inherited property such as __proto__ or constructor. */
|
|
112
|
+
function ownValue({ record, key }, _options = {}) {
|
|
113
|
+
return Object.hasOwn(record, key) ? record[key] : undefined;
|
|
114
|
+
}
|
|
115
|
+
/** Presence needs its own label: null and missing otherwise both display an empty marker. */
|
|
116
|
+
function renderFieldDiff({ child, previous, value, key }, _options = {}) {
|
|
117
|
+
const body = renderDiff(child);
|
|
118
|
+
if (Object.hasOwn(previous, key) && Object.hasOwn(value, key))
|
|
119
|
+
return body;
|
|
120
|
+
const label = Object.hasOwn(value, key) ? 'Added' : 'Removed';
|
|
121
|
+
return `<span class="mcpui-hint">${label}</span>${body}`;
|
|
122
|
+
}
|
|
123
|
+
/** Partition by equality before rendering so unchanged fields never crowd out changes.
|
|
124
|
+
* @complexity O(n * depth) worst-case nested comparison work; O(n) markup space. No reads or mutation. */
|
|
125
|
+
function renderObjectDiff(context, _options = {}) {
|
|
126
|
+
const { previous, value } = context;
|
|
127
|
+
const keys = [...new Set([...Object.keys(value), ...Object.keys(previous)])];
|
|
128
|
+
const changed = [];
|
|
129
|
+
const unchanged = [];
|
|
130
|
+
for (const key of keys) {
|
|
131
|
+
const child = { ...context, previous: ownValue({ record: previous, key }), value: ownValue({ record: value, key }), path: context.path ? `${context.path}.${key}` : key };
|
|
132
|
+
const same = Object.hasOwn(previous, key) === Object.hasOwn(value, key) && equalValue({ before: child.previous, after: child.value });
|
|
133
|
+
const body = same ? renderValue(child) : renderFieldDiff({ child, previous, value, key });
|
|
134
|
+
(same ? unchanged : changed).push(objectRow({ key, context: child, body }));
|
|
135
|
+
}
|
|
136
|
+
const rest = unchanged.length === 0 ? '' : disclosure({ summary: `Unchanged fields (${unchanged.length})`, body: `<dl class="mcpui-detail-group">${unchanged.join('')}</dl>`, kind: 'mcpui-detail-unchanged' });
|
|
137
|
+
const changes = changed.length === 0 ? '' : `<dl class="mcpui-detail-group">${changed.join('')}</dl>`;
|
|
138
|
+
return changes + rest || '<span class="mcpui-detail-value">—</span>';
|
|
139
|
+
}
|
|
140
|
+
/** Recurse only through object pairs; atomic and array updates show explicit before/after values. */
|
|
141
|
+
function renderDiff(context, _options = {}) {
|
|
142
|
+
if (isRecord(context.previous) && isRecord(context.value))
|
|
143
|
+
return renderObjectDiff({ ...context, previous: context.previous, value: context.value });
|
|
144
|
+
return `<div class="mcpui-detail-change"><div><span class="mcpui-hint">Before</span>${renderValue({ ...context, value: context.previous })}</div><div><span class="mcpui-hint">After</span>${renderValue(context)}</div></div>`;
|
|
145
|
+
}
|
|
146
|
+
/** One reusable detail body. Raw JSON is escaped text, never a script or HTML fragment.
|
|
147
|
+
* @returns HTML for JSON-compatible caller data; JSON.stringify errors propagate for invalid data.
|
|
148
|
+
* @complexity O(n * depth) with a previous snapshot, otherwise O(n), in serialized bytes. */
|
|
149
|
+
export function renderStructuredDetail({ detail }, _options = {}) {
|
|
150
|
+
const context = { value: detail.value, path: '', labels: detail.labels ?? {}, format: detail.format };
|
|
151
|
+
const hasPrevious = Object.hasOwn(detail, 'previous');
|
|
152
|
+
const body = hasPrevious ? renderDiff({ ...context, previous: detail.previous }) : renderValue(context);
|
|
153
|
+
const raw = hasPrevious ? { previous: detail.previous, value: detail.value } : detail.value;
|
|
154
|
+
const showRaw = hasPrevious || (typeof detail.value === 'object' && detail.value !== null) || body.startsWith('<details ');
|
|
155
|
+
if (!showRaw)
|
|
156
|
+
return body;
|
|
157
|
+
return body + disclosure({ summary: 'View raw', body: `<pre class="mcpui-code">${escapeHtml(JSON.stringify(raw, null, 2))}</pre>`, kind: 'mcpui-detail-raw' });
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=structured-details.js.map
|
|
@@ -16,6 +16,8 @@ export interface TextInputProps extends Partial<SecretInputText> {
|
|
|
16
16
|
readonly hint?: string;
|
|
17
17
|
readonly required?: boolean;
|
|
18
18
|
readonly disabled?: boolean;
|
|
19
|
+
/** Visually hide the label (it repeats the card title); it stays the accessible name. */
|
|
20
|
+
readonly labelHidden?: boolean;
|
|
19
21
|
/** `'number'` also constrains the on-screen keyboard on touch devices, which `'text'` would not. */
|
|
20
22
|
readonly inputType?: 'text' | 'number';
|
|
21
23
|
/** Renders as `<input type="password">`, or a masked textarea when multiline — either way with
|
|
@@ -13,7 +13,7 @@ function optionalAttribute(name, value) {
|
|
|
13
13
|
return '';
|
|
14
14
|
return ` ${name}="${escapeHtml(String(value))}"`;
|
|
15
15
|
}
|
|
16
|
-
function booleanAttribute(name, value) {
|
|
16
|
+
function booleanAttribute(name, value = false) {
|
|
17
17
|
return value === true ? ` ${name}` : '';
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ToolDescriptor } from '@jini-ai/core';
|
|
2
|
+
type ApprovalText = (required: {
|
|
3
|
+
key: string;
|
|
4
|
+
}) => string;
|
|
5
|
+
type ApprovalDescriptor = Pick<ToolDescriptor, 'id' | 'metadata' | 'inputSchema'>;
|
|
6
|
+
/** Derive approval copy from the registered catalog, with the shared humanized-key fallback.
|
|
7
|
+
* The host translates copy; supplied input is never evaluated as a template or action metadata.
|
|
8
|
+
* @returns A question title, declared summary, confirm label and structured detail labels.
|
|
9
|
+
* @complexity O(n * depth + k) time/space in schema paths and tool-name bytes; no effects. */
|
|
10
|
+
export declare function toolApprovalPresentation({ descriptor }: {
|
|
11
|
+
descriptor: ApprovalDescriptor;
|
|
12
|
+
}, { text }: {
|
|
13
|
+
text: ApprovalText;
|
|
14
|
+
}): {
|
|
15
|
+
title: string;
|
|
16
|
+
summary?: string;
|
|
17
|
+
confirmLabel: string;
|
|
18
|
+
labels: Readonly<Record<string, string>>;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=tool-approval.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { isNonArrayRecord } from '@jini-ai/core';
|
|
2
|
+
import { surfaceDetailLabel } from './structured-details.js';
|
|
3
|
+
function nonBlank({ value }, _options = {}) {
|
|
4
|
+
return typeof value === 'string' ? value.trim() || undefined : undefined;
|
|
5
|
+
}
|
|
6
|
+
function record({ value }, _options = {}) {
|
|
7
|
+
const candidate = { value };
|
|
8
|
+
return isNonArrayRecord(candidate) ? candidate.value : {};
|
|
9
|
+
}
|
|
10
|
+
/** Project inline JSON-schema property titles into the existing dotted-path label contract.
|
|
11
|
+
* Only `title`: `description` is guidance written for the model (often a paragraph), not a label;
|
|
12
|
+
* untitled fields keep the renderer's humanized-key fallback. No reference resolution or validation.
|
|
13
|
+
* @complexity O(n * depth) time and space in property-path bytes; no effects or input mutation. */
|
|
14
|
+
function schemaLabels({ schema, path = '' }, { text }) {
|
|
15
|
+
const properties = record({ value: record({ value: schema }).properties });
|
|
16
|
+
return Object.fromEntries(Object.entries(properties).flatMap(([key, property]) => {
|
|
17
|
+
const field = record({ value: property });
|
|
18
|
+
const fieldPath = path ? `${path}.${key}` : key;
|
|
19
|
+
const label = nonBlank({ value: field.title });
|
|
20
|
+
return [
|
|
21
|
+
...(label ? [[fieldPath, text({ key: label })]] : []),
|
|
22
|
+
...Object.entries(schemaLabels({ schema: field, path: fieldPath }, { text })),
|
|
23
|
+
];
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
/** Derive approval copy from the registered catalog, with the shared humanized-key fallback.
|
|
27
|
+
* The host translates copy; supplied input is never evaluated as a template or action metadata.
|
|
28
|
+
* @returns A question title, declared summary, confirm label and structured detail labels.
|
|
29
|
+
* @complexity O(n * depth + k) time/space in schema paths and tool-name bytes; no effects. */
|
|
30
|
+
export function toolApprovalPresentation({ descriptor }, { text }) {
|
|
31
|
+
const fallback = surfaceDetailLabel({ key: descriptor.id.replace(/\./g, ' '), path: '', labels: {} });
|
|
32
|
+
const action = text({ key: nonBlank({ value: descriptor.metadata?.title }) ?? fallback });
|
|
33
|
+
const title = /[??؟]$/.test(action) ? action : text({ key: '{action}?' }).replace('{action}', () => action);
|
|
34
|
+
const summary = nonBlank({ value: descriptor.metadata?.summary });
|
|
35
|
+
return {
|
|
36
|
+
title,
|
|
37
|
+
...(summary ? { summary: text({ key: summary }) } : {}),
|
|
38
|
+
confirmLabel: text({ key: nonBlank({ value: descriptor.metadata?.actionVerb }) ?? 'Confirm' }),
|
|
39
|
+
labels: schemaLabels({ schema: descriptor.inputSchema }, { text }),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=tool-approval.js.map
|
|
@@ -201,7 +201,8 @@ export function useMediaProvidersTab({ port, catalogPolicy, initialProviders })
|
|
|
201
201
|
})();
|
|
202
202
|
}, [flushNow]);
|
|
203
203
|
const fetchAndReconcile = useCallback((migrateOnFirstUpload) => {
|
|
204
|
-
|
|
204
|
+
loadTicket.current += 1;
|
|
205
|
+
const ticket = loadTicket.current;
|
|
205
206
|
const isCurrent = () => alive.current && loadTicket.current === ticket;
|
|
206
207
|
// Captured for the MIGRATION decision only — `shouldSyncLocalProvidersToDaemon` asks "did the
|
|
207
208
|
// operator already have local data before this fetch answered", which is a question about the
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export function createAsyncCommitGuard() {
|
|
2
2
|
let currentRevision = 0;
|
|
3
3
|
return {
|
|
4
|
-
begin: () =>
|
|
4
|
+
begin: () => {
|
|
5
|
+
currentRevision += 1;
|
|
6
|
+
return currentRevision;
|
|
7
|
+
},
|
|
5
8
|
capture: () => currentRevision,
|
|
6
9
|
invalidate: () => {
|
|
7
10
|
currentRevision += 1;
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
// translated copy is product content); every `t(...)` call below uses a
|
|
11
11
|
// plain-English default instead.
|
|
12
12
|
import { connectorAppLabel } from './constants.js';
|
|
13
|
+
const KIND_LABEL_KEYS = {
|
|
14
|
+
heuristic: 'Heuristic',
|
|
15
|
+
connector: 'Connected apps',
|
|
16
|
+
};
|
|
13
17
|
export function describeConnectorReadIssue(result) {
|
|
14
18
|
const failed = result.connectors.filter((connector) => connector.status === 'failed');
|
|
15
19
|
const skipped = result.connectors.filter((connector) => connector.status === 'skipped');
|
|
@@ -212,7 +216,7 @@ export function describeRecord(record, t) {
|
|
|
212
216
|
// that was the only writer at the time, so labelling them as such
|
|
213
217
|
// keeps the history list legible after upgrading.
|
|
214
218
|
const kind = record.kind ?? 'llm';
|
|
215
|
-
const kindLabel =
|
|
219
|
+
const kindLabel = t(KIND_LABEL_KEYS[kind] ?? 'Model');
|
|
216
220
|
return { phaseLabel, reasonLabel, kindLabel, tone };
|
|
217
221
|
}
|
|
218
222
|
export function formatRelativeTime(at, now) {
|
|
@@ -22,7 +22,9 @@ function connectorRowState({ connector, context }) {
|
|
|
22
22
|
const connecting = context.connectingConnectorIds.has(connector.id);
|
|
23
23
|
const authorizationPending = context.pendingConnectorAuthIds.has(connector.id);
|
|
24
24
|
const connectError = context.connectorConnectErrors[connector.id];
|
|
25
|
-
const
|
|
25
|
+
const statusUnresolved = context.connectorsLoading && !isStatusResolved({ connector, context });
|
|
26
|
+
const idle = !connected && !authorizationPending && !connectError && !connecting;
|
|
27
|
+
const checkingStatus = statusUnresolved && idle;
|
|
26
28
|
return {
|
|
27
29
|
connected,
|
|
28
30
|
selected: context.selectedConnectorIds.has(connector.id) && connected,
|
|
@@ -13,6 +13,6 @@ import { useT } from '../../../i18n/index.js';
|
|
|
13
13
|
import { renderMarkdown } from '../render-markdown.js';
|
|
14
14
|
export function MemoryEntryCard({ entry, previewId, previewBody, onOpenPreview, onStartEdit, onDelete, agentHandle, }) {
|
|
15
15
|
const t = useT();
|
|
16
|
-
return (_jsxs("div", { className: "library-card", children: [_jsxs("div", { className: "library-card-info", children: [_jsx("div", { className: "library-card-title-row", children: _jsx("span", { className: "library-card-name", children: entry.name }) }), _jsx("div", { className: "library-card-desc", children: entry.description || '—' })] }), _jsxs("div", { className: "memory-card-actions", children: [_jsx("button", { type: "button", className: "library-card-expand", onClick: () => onOpenPreview(entry.id), title: t('Preview'), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'preview', role: 'button', label: t('Preview') }) }), children: _jsx(Icon, { name: previewId === entry.id ? 'chevron-down' : 'chevron-right', size: 14 }) }), _jsx("button", { type: "button", className: "ghost library-card-action", onClick: () => onStartEdit(entry.id), title: t('Edit'), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'edit', role: 'button', label: t('Edit') }) }), children: _jsx(Icon, { name: "edit", size: 14 }) }), _jsx("button", { type: "button", className: "ghost library-card-action", onClick: () => onDelete(entry.id), title: t('Delete'), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'delete', role: 'button', label: t('Delete') }) }), children: _jsx(Icon, { name: "close", size: 14 }) })] }), previewId === entry.id && (
|
|
16
|
+
return (_jsxs("div", { className: "library-card", children: [_jsxs("div", { className: "library-card-info", children: [_jsx("div", { className: "library-card-title-row", children: _jsx("span", { className: "library-card-name", children: entry.name }) }), _jsx("div", { className: "library-card-desc", children: entry.description || '—' })] }), _jsxs("div", { className: "memory-card-actions", children: [_jsx("button", { type: "button", className: "library-card-expand", onClick: () => onOpenPreview(entry.id), title: t('Preview'), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'preview', role: 'button', label: t('Preview') }) }), children: _jsx(Icon, { name: previewId === entry.id ? 'chevron-down' : 'chevron-right', size: 14 }) }), _jsx("button", { type: "button", className: "ghost library-card-action", onClick: () => onStartEdit(entry.id), title: t('Edit'), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'edit', role: 'button', label: t('Edit') }) }), children: _jsx(Icon, { name: "edit", size: 14 }) }), _jsx("button", { type: "button", className: "ghost library-card-action", onClick: () => onDelete(entry.id), title: t('Delete'), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'delete', role: 'button', label: t('Delete') }) }), children: _jsx(Icon, { name: "close", size: 14 }) })] }), previewId === entry.id && (_jsxs("div", { className: "library-preview", style: { width: '100%' }, children: [previewBody === null ? _jsx("p", { children: t('Loading…') }) : null, previewBody ? _jsx("div", { className: "library-preview-body", children: renderMarkdown(previewBody) }) : null, previewBody === '' ? _jsx("p", { className: "hint", children: "\u2014" }) : null] }))] }));
|
|
17
17
|
}
|
|
18
18
|
//# sourceMappingURL=MemoryEntryCard.js.map
|
|
@@ -26,6 +26,8 @@ export function MemorySettingsPanel({ enabled, onToggleEnabled, topTab, onTopTab
|
|
|
26
26
|
// Scroll target for the saved-memory section; nothing outside this panel
|
|
27
27
|
// needs to reach it, so it's owned here rather than threaded through props.
|
|
28
28
|
const sectionRef = useRef(null);
|
|
29
|
-
|
|
29
|
+
const savedHandleProps = agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'saved' }) } : {};
|
|
30
|
+
const howHandleProps = agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'how' }) } : {};
|
|
31
|
+
return (_jsxs("div", { className: "memory-settings-panel", children: [_jsxs("section", { className: "memory-panel-head-card", children: [_jsxs("div", { className: "memory-panel-head-copy", children: [_jsx("h4", { children: t('Memory') }), _jsx("p", { className: "hint", children: t('Control whether saved facts, preferences, and project context are reused in future chats.') })] }), _jsxs("div", { className: "memory-panel-head-controls", children: [_jsxs("div", { className: "jini-seg-control memory-panel-seg", role: "tablist", "aria-label": t('Memory view'), style: { '--seg-cols': 2 }, children: [_jsx("button", { type: "button", role: "tab", "aria-selected": topTab === 'memories', className: 'jini-seg-btn' + (topTab === 'memories' ? ' active' : ''), onClick: () => onTopTabChange('memories'), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'view-memories', role: 'button', label: t('Memories') }) }), children: _jsx("span", { className: "jini-seg-title", children: t('Memories') }) }), _jsx("button", { type: "button", role: "tab", "aria-selected": topTab === 'how', className: 'jini-seg-btn' + (topTab === 'how' ? ' active' : ''), onClick: () => onTopTabChange('how'), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'view-how', role: 'button', label: t('How it works') }) }), children: _jsx("span", { className: "jini-seg-title", children: t('How it works') }) })] }), _jsx("button", { type: "button", className: "memory-icon-btn", onClick: onAdd, disabled: !onAdd, "aria-label": t('Add memory'), title: t('Add memory'), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'add', role: 'button', label: t('Add memory') }) }), children: _jsx(Icon, { name: "plus", size: 14 }) }), _jsx("button", { type: "button", className: "memory-icon-btn", onClick: onOpenAdvanced, disabled: !onOpenAdvanced, "aria-label": t('Advanced'), title: t('Advanced'), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'advanced', role: 'button', label: t('Advanced') }) }), children: _jsx(Icon, { name: "settings", size: 14 }) }), _jsxs("label", { className: "toggle-switch memory-panel-master-toggle", title: enabled ? t('On') : t('Off'), children: [_jsx("input", { type: "checkbox", "aria-label": t('Enable memory'), checked: enabled, onChange: (event) => onToggleEnabled(event.target.checked), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'enabled', role: 'checkbox', label: t('Enable memory') }) }) }), _jsx("span", { className: "toggle-slider" })] })] })] }), topTab === 'memories' ? (_jsx(MemoryList, { sectionRef: sectionRef, ...savedMemory, ...savedHandleProps })) : (_jsx(MemoryHowPanel, { ...howItWorks, ...howHandleProps }))] }));
|
|
30
32
|
}
|
|
31
33
|
//# sourceMappingURL=MemorySettingsPanel.js.map
|
|
@@ -61,12 +61,12 @@ function enqueueConfigWrite(queue, value, send, onSettled) {
|
|
|
61
61
|
thrown = error;
|
|
62
62
|
}
|
|
63
63
|
onSettled(ok, desired, queue.pending !== null);
|
|
64
|
-
|
|
64
|
+
settlers.forEach(settler => {
|
|
65
65
|
if (didThrow)
|
|
66
66
|
settler.reject(thrown);
|
|
67
67
|
else
|
|
68
68
|
settler.resolve();
|
|
69
|
-
}
|
|
69
|
+
});
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
finally {
|
|
@@ -159,11 +159,12 @@ export function useMemoryConfig(port) {
|
|
|
159
159
|
enabledConfirmedRef.current = value;
|
|
160
160
|
if (!hasNewerIntent)
|
|
161
161
|
setError(null);
|
|
162
|
+
return;
|
|
162
163
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
if (hasNewerIntent)
|
|
165
|
+
return;
|
|
166
|
+
setEnabled(enabledConfirmedRef.current);
|
|
167
|
+
setError(CONFIG_SAVE_ERROR_MESSAGE);
|
|
167
168
|
});
|
|
168
169
|
}, [port]);
|
|
169
170
|
// Map each hook key to its setter so a single optimistic-set + rollback path
|
|
@@ -186,11 +187,12 @@ export function useMemoryConfig(port) {
|
|
|
186
187
|
hookConfirmedRef.current[key] = value;
|
|
187
188
|
if (!hasNewerIntent)
|
|
188
189
|
setError(null);
|
|
190
|
+
return;
|
|
189
191
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
if (hasNewerIntent)
|
|
193
|
+
return;
|
|
194
|
+
setter(() => hookConfirmedRef.current[key]);
|
|
195
|
+
setError(CONFIG_SAVE_ERROR_MESSAGE);
|
|
194
196
|
});
|
|
195
197
|
}, [port, setters]);
|
|
196
198
|
const hookFlags = useMemo(() => ({
|
|
@@ -246,11 +246,8 @@ export function useMemoryConnectors(port, coord) {
|
|
|
246
246
|
const connectedMemoryConnectors = useMemo(() => memoryConnectors.filter((connector) => connector.status === 'connected'), [memoryConnectors]);
|
|
247
247
|
const selectedConnectedConnectorIds = useMemo(() => [...selectedConnectorIds].filter((id) => connectedMemoryConnectors.some((connector) => connector.id === id)), [selectedConnectorIds, connectedMemoryConnectors]);
|
|
248
248
|
const connectedCount = connectedMemoryConnectors.length;
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
: selectedConnectedConnectorIds.length === 0
|
|
252
|
-
? 'Select apps to scan'
|
|
253
|
-
: 'Scan selected apps';
|
|
249
|
+
const idleScanLabel = selectedConnectedConnectorIds.length === 0 ? 'Select apps to scan' : 'Scan selected apps';
|
|
250
|
+
const connectorScanLabel = connectorExtracting ? 'Scanning apps' : idleScanLabel;
|
|
254
251
|
const selectedConnectorSuggestions = useMemo(() => connectorSuggestions.filter((suggestion) => selectedSuggestionIds.has(suggestion.id)), [connectorSuggestions, selectedSuggestionIds]);
|
|
255
252
|
const connectorLoadError = connectorCatalogueLoadError ?? connectorStatusLoadError;
|
|
256
253
|
// Prune selection down to still-connected apps: disconnecting an app should
|
|
@@ -68,14 +68,15 @@ export function isRecentFailedConnectorExtraction(record, scanStartedAt) {
|
|
|
68
68
|
return record.kind === 'connector' && record.phase === 'failed' && record.startedAt >= scanStartedAt - 5_000;
|
|
69
69
|
}
|
|
70
70
|
export function describeConnectorSuggestionsSaveOutcome({ savedCount, totalCount, failure, }) {
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
const noun = savedCount === 1 ? 'memory' : 'memories';
|
|
72
|
+
const status = savedCount > 0 ? `Saved ${savedCount} ${noun} from connected apps.` : null;
|
|
73
|
+
return { status, error: connectorSaveError({ savedCount, totalCount, failure }) };
|
|
74
|
+
}
|
|
75
|
+
function connectorSaveError({ savedCount, totalCount, failure }) {
|
|
76
|
+
if (failure)
|
|
77
|
+
return failure instanceof Error ? failure.message : String(failure);
|
|
78
|
+
return savedCount !== totalCount
|
|
79
|
+
? `Saved ${savedCount} of ${totalCount} selected memories. Please try the remaining items again.`
|
|
80
|
+
: null;
|
|
80
81
|
}
|
|
81
82
|
//# sourceMappingURL=useMemoryConnectors.rules.js.map
|
|
@@ -148,7 +148,8 @@ export function useMemoryEntries(port, coord) {
|
|
|
148
148
|
setPreviewBody(null);
|
|
149
149
|
return;
|
|
150
150
|
}
|
|
151
|
-
|
|
151
|
+
previewRequestTokenRef.current += 1;
|
|
152
|
+
const token = previewRequestTokenRef.current;
|
|
152
153
|
setPreviewId(id);
|
|
153
154
|
setPreviewBody(null);
|
|
154
155
|
let entry;
|
|
@@ -173,7 +174,8 @@ export function useMemoryEntries(port, coord) {
|
|
|
173
174
|
setPreviewBody(entry?.body ?? '');
|
|
174
175
|
}, [previewId, port]);
|
|
175
176
|
const startEdit = useCallback(async (id) => {
|
|
176
|
-
|
|
177
|
+
editRequestTokenRef.current += 1;
|
|
178
|
+
const token = editRequestTokenRef.current;
|
|
177
179
|
let entry;
|
|
178
180
|
try {
|
|
179
181
|
entry = await port.fetchMemoryEntry(id);
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
// stay here: the tool-cursor-aware single-connector merge this slice's
|
|
17
17
|
// discovery hydration needs, and the "mark pending authorization" transform.
|
|
18
18
|
import { applyConnectorStatuses, mergeConnectors } from '../connectors/rules.js';
|
|
19
|
+
import { omitKeys } from '../../utils/omit-keys.js';
|
|
19
20
|
/** Patch body for flipping the master memory switch. */
|
|
20
21
|
export function enabledPatch(enabled) {
|
|
21
22
|
return { enabled };
|
|
@@ -64,7 +65,7 @@ export function applyMemoryConnectorStatus(connector, status) {
|
|
|
64
65
|
* tracks pending state separately rather than mutating the connector's
|
|
65
66
|
* displayed status, so this transform is genuinely local to this slice. */
|
|
66
67
|
export function connectorWithPendingAuthorization(connector) {
|
|
67
|
-
const
|
|
68
|
+
const base = omitKeys(connector, ['accountLabel', 'lastError']);
|
|
68
69
|
return {
|
|
69
70
|
...base,
|
|
70
71
|
status: base.status === 'disabled' ? 'disabled' : 'available',
|
|
@@ -59,7 +59,7 @@ export function useFetchQuery(required, optional = {}) {
|
|
|
59
59
|
const fetch = useCallback(() => latest.current.fetch(), []);
|
|
60
60
|
useEffect(() => {
|
|
61
61
|
if (!enabled)
|
|
62
|
-
return;
|
|
62
|
+
return undefined;
|
|
63
63
|
const release = cache.observe({ entry: entry, observer: { fetch, staleTime, focus: refetchOnWindowFocus } });
|
|
64
64
|
void cache.load({ entry: entry, fetch: fetch, staleTime: staleTime });
|
|
65
65
|
return release;
|
|
@@ -84,7 +84,8 @@ export function useFetchMutation(required, optional = {}) {
|
|
|
84
84
|
useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []);
|
|
85
85
|
const [state, setState] = useState({ status: 'idle', error: null });
|
|
86
86
|
const mutate = useCallback(({ input }) => {
|
|
87
|
-
|
|
87
|
+
generation.current += 1;
|
|
88
|
+
const current = generation.current;
|
|
88
89
|
const { run, invalidates } = latest.current;
|
|
89
90
|
setState({ status: 'pending', error: null });
|
|
90
91
|
// Surface a rejected write immediately instead of silently retrying authorization or
|