@jini-ai/ui 0.5.5 → 0.6.10
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 +91 -2
- package/dist/core/theme/validation.js +40 -38
- package/dist/features/a2ui/renderer.js +15 -13
- package/dist/features/about/react/components/AboutTab.js +64 -34
- package/dist/features/about/rules.d.ts +2 -2
- package/dist/features/about/rules.js +156 -129
- package/dist/features/admin-widgets/components/ImagePreviewModal.d.ts +4 -3
- package/dist/features/admin-widgets/components/ImagePreviewModal.js +6 -10
- package/dist/features/admin-widgets/components/InfoTip.d.ts +3 -2
- package/dist/features/admin-widgets/components/InfoTip.hooks.js +2 -0
- package/dist/features/admin-widgets/components/InfoTip.js +34 -7
- package/dist/features/admin-widgets/components/InfoTip.rules.d.ts +13 -0
- package/dist/features/admin-widgets/components/InfoTip.rules.js +43 -10
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.d.ts +30 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.d.ts +40 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.js +128 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.js +18 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.d.ts +14 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.js +12 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.rules.d.ts +20 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.rules.js +21 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.d.ts +18 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.d.ts +16 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.js +45 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.js +18 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.d.ts +32 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.js +26 -0
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +29 -14
- package/dist/features/admin-widgets/index.d.ts +14 -2
- package/dist/features/admin-widgets/index.js +7 -1
- package/dist/features/admin-widgets/styles/admin-widgets.css +1 -0
- package/dist/features/asset-grid/react/components/AssetCard.js +7 -1
- package/dist/features/asset-grid/react/components/AssetGrid.d.ts +1 -1
- package/dist/features/asset-grid/react/components/AssetGrid.js +38 -18
- package/dist/features/asset-grid/react/components/useAssetGridFilters.d.ts +21 -0
- package/dist/features/asset-grid/react/components/useAssetGridFilters.js +19 -0
- package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +16 -11
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.d.ts +2 -0
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +35 -11
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.d.ts +6 -6
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.js +10 -129
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.d.ts +50 -0
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.js +95 -0
- package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.d.ts +56 -0
- package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.js +80 -0
- package/dist/features/asset-tree-browser/rules.js +40 -22
- package/dist/features/browser-chrome/rules.js +74 -70
- package/dist/features/charts/browser.js +11 -5
- package/dist/features/charts/react.js +2 -2
- package/dist/features/charts/table.js +2 -2
- package/dist/features/command-palette/react/hooks/useCommandPalette.js +3 -9
- package/dist/features/connectors/ConnectorsBrowser.d.ts +4 -4
- package/dist/features/connectors/ConnectorsBrowser.js +26 -14
- package/dist/features/connectors/components/ConnectorCard.js +67 -38
- package/dist/features/connectors/components/ConnectorDetailDrawer.js +62 -31
- package/dist/features/connectors/components/ConnectorGrid.js +11 -6
- package/dist/features/connectors/hooks/useConnectorAuthorization.js +6 -0
- package/dist/features/connectors/hooks/useConnectorCatalog.d.ts +2 -0
- package/dist/features/connectors/hooks/useConnectorCatalog.js +32 -13
- package/dist/features/connectors/hooks/useWiredConnectorsBrowser.d.ts +3 -3
- package/dist/features/connectors/rules.d.ts +8 -1
- package/dist/features/connectors/rules.js +42 -20
- package/dist/features/connectors/types.d.ts +11 -0
- package/dist/features/execution/constants.d.ts +4 -10
- package/dist/features/execution/constants.js +9 -94
- package/dist/features/execution/react/components/ByokProviderForm.d.ts +2 -4
- package/dist/features/execution/react/components/ByokProviderForm.js +140 -43
- package/dist/features/execution/react/components/ExecutionTab.d.ts +1 -2
- package/dist/features/execution/react/components/ExecutionTab.js +52 -30
- package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -3
- package/dist/features/execution/react/components/LocalCliAgentList.js +14 -3
- package/dist/features/execution/react/components/LocalCliAgentSettings.js +14 -3
- package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +54 -36
- package/dist/features/execution/react/hooks/useReasoningControl.js +114 -78
- package/dist/features/execution/rules.d.ts +2 -2
- package/dist/features/execution/rules.js +31 -15
- package/dist/features/execution/types.d.ts +4 -42
- package/dist/features/execution/types.js +0 -17
- package/dist/features/external-mcp/react/components/ExternalMcpTab.d.ts +6 -2
- package/dist/features/external-mcp/react/components/ExternalMcpTab.js +9 -2
- package/dist/features/file-dropzone/react/components/FileDropzone.js +24 -25
- package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.d.ts +2 -2
- package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.js +35 -30
- package/dist/features/file-dropzone/react/components/FileDropzoneThumbnailGrid.js +30 -24
- package/dist/features/file-dropzone/react/components/FileDropzoneZone.d.ts +14 -0
- package/dist/features/file-dropzone/react/components/FileDropzoneZone.js +26 -0
- package/dist/features/html-editor/canvas-content-wrapper.js +13 -13
- package/dist/features/html-editor/canvas-embed-placeholders.js +46 -37
- package/dist/features/html-editor/css.js +76 -69
- package/dist/features/html-editor/editor-blocks.js +47 -33
- package/dist/features/html-editor/editor-serialization.js +6 -5
- package/dist/features/html-editor/protected-elements.js +39 -28
- package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +4 -4
- package/dist/features/html-editor/source-splice.js +51 -30
- package/dist/features/i18n/dictionaries/settings-dialog.ja.js +1 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +1 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +1 -0
- package/dist/features/i18n/locale.js +20 -16
- package/dist/features/iframe-pool/react/dom-sync.js +8 -6
- package/dist/features/integrations/react/components/CodexInstallToggleButton.js +19 -4
- package/dist/features/integrations/react/components/IntegrationsTab.js +60 -14
- package/dist/features/interactive-ui/interactive-ui.css +1 -1
- package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -7
- package/dist/features/lexical-rich-text-editor/mention-parser.js +60 -57
- package/dist/features/lexical-rich-text-editor/react/hooks/useKeyboardCommands.js +33 -38
- package/dist/features/lexical-rich-text-editor/rules.js +87 -74
- package/dist/features/lexical-rich-text-editor/serialize.js +27 -25
- package/dist/features/mcp-ui/secret-card.js +111 -61
- package/dist/features/mcp-ui/surfaces/confirmation.js +39 -21
- package/dist/features/mcp-ui/surfaces/document.d.ts +1 -1
- package/dist/features/mcp-ui/surfaces/document.js +10 -2
- package/dist/features/mcp-ui/surfaces/fields.js +30 -50
- package/dist/features/mcp-ui/surfaces/form.d.ts +6 -0
- package/dist/features/mcp-ui/surfaces/form.js +43 -21
- package/dist/features/mcp-ui/surfaces/outcome.js +30 -20
- package/dist/features/mcp-ui/surfaces/text-input.js +33 -21
- package/dist/features/mcp-ui/surfaces/tokens.d.ts +6 -1
- package/dist/features/mcp-ui/surfaces/tokens.js +5 -2
- package/dist/features/media-providers/constants.d.ts +1 -0
- package/dist/features/media-providers/constants.js +2 -81
- package/dist/features/media-providers/index.d.ts +1 -1
- package/dist/features/media-providers/ports.d.ts +2 -0
- package/dist/features/media-providers/react/components/MediaProviderCard.d.ts +22 -0
- package/dist/features/media-providers/react/components/MediaProviderCard.js +50 -0
- package/dist/features/media-providers/react/components/MediaProvidersTab.d.ts +5 -6
- package/dist/features/media-providers/react/components/MediaProvidersTab.js +40 -72
- package/dist/features/media-providers/react/components/media-providers-tab-labels.d.ts +11 -0
- package/dist/features/media-providers/react/components/media-providers-tab-labels.js +57 -0
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.d.ts +4 -2
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +37 -23
- package/dist/features/media-providers/rules.js +17 -10
- package/dist/features/media-providers/types.d.ts +4 -22
- package/dist/features/media-providers/types.js +3 -8
- package/dist/features/memory/formatters.js +127 -92
- package/dist/features/memory/react/components/MemoryConnectedPanel.js +92 -36
- package/dist/features/memory/react/components/MemoryList.js +37 -18
- package/dist/features/memory/react/hooks/useMemoryExtractions.store.js +42 -36
- package/dist/features/notifications/react/components/NotificationsTab.js +28 -28
- package/dist/features/observability/long-task.d.ts +0 -12
- package/dist/features/observability/long-task.js +15 -19
- package/dist/features/observability/resource-error.d.ts +0 -10
- package/dist/features/observability/resource-error.js +18 -13
- package/dist/features/observability/stuck-run.js +10 -6
- package/dist/features/observability/telemetry-url.d.ts +6 -0
- package/dist/features/observability/telemetry-url.js +16 -0
- package/dist/features/observability/white-screen.js +12 -24
- package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
- package/dist/features/panel-kit/fetch-query/adapter.react.js +4 -2
- package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +46 -33
- package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +3 -1
- package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +3 -1
- package/dist/features/privacy/react/components/PrivacyTab.js +24 -13
- package/dist/features/progress-card/components/ProgressCard.js +7 -3
- package/dist/features/progress-card/reference-adapters.js +59 -54
- package/dist/features/project-locations/react/components/ProjectLocationsTab.js +24 -15
- package/dist/features/project-locations/react/hooks/useProjectLocationsTab.d.ts +8 -0
- package/dist/features/project-locations/react/hooks/useProjectLocationsTab.js +31 -23
- package/dist/features/resource-dashboard/react/components/ResourceBoard.js +6 -47
- package/dist/features/resource-dashboard/react/components/ResourceBoardView.d.ts +2 -2
- package/dist/features/resource-dashboard/react/components/ResourceBoardView.js +3 -6
- package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.d.ts +15 -0
- package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.js +35 -0
- package/dist/features/resource-dashboard/react/components/ResourceCard.js +5 -16
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.d.ts +34 -0
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +34 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowList.js +3 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +2 -2
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.d.ts +19 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +11 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.d.ts +1 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +4 -4
- package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.d.ts +10 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.js +20 -0
- package/dist/features/resource-dashboard/react/components/resourceBoardProps.d.ts +48 -0
- package/dist/features/resource-dashboard/react/components/resourceBoardProps.js +70 -0
- package/dist/features/resource-dashboard/react/components/resourceRowListProps.d.ts +19 -0
- package/dist/features/resource-dashboard/react/components/resourceRowListProps.js +14 -0
- package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.d.ts +16 -0
- package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.js +13 -0
- package/dist/features/resource-dashboard/rules.js +2 -8
- package/dist/features/sketch-editor/dom.js +150 -99
- package/dist/features/sketch-editor/react/components/SketchEditor.js +26 -15
- package/dist/features/skills/react/components/SkillDraftForm.js +13 -19
- package/dist/features/skills/react/components/SkillDraftForm.labels.d.ts +31 -0
- package/dist/features/skills/react/components/SkillDraftForm.labels.js +54 -0
- package/dist/features/skills/react/components/SkillRow.js +38 -18
- package/dist/features/skills/react/components/SkillRow.labels.d.ts +7 -0
- package/dist/features/skills/react/components/SkillRow.labels.js +18 -0
- package/dist/features/skills/react/components/SkillsTab.js +24 -14
- package/dist/features/skills/react/components/SkillsTab.labels.d.ts +7 -0
- package/dist/features/skills/react/components/SkillsTab.labels.js +11 -0
- package/dist/features/skills/rules.js +32 -23
- package/dist/features/source-config-list/agent-handles.js +2 -1
- package/dist/features/source-config-list/dependencies.js +53 -49
- package/dist/features/source-config-list/index.d.ts +3 -3
- package/dist/features/source-config-list/react/components/SourceConfigAddForm.d.ts +6 -5
- package/dist/features/source-config-list/react/components/SourceConfigAddForm.js +38 -7
- package/dist/features/source-config-list/react/components/SourceConfigField.js +69 -25
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.d.ts +6 -5
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +76 -20
- package/dist/features/source-config-list/react/components/SourceConfigListView.d.ts +5 -3
- package/dist/features/source-config-list/react/components/SourceConfigListView.js +33 -3
- package/dist/features/source-config-list/react/components/SourceConfigTestControl.js +17 -1
- package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.d.ts +11 -0
- package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.js +12 -0
- package/dist/features/source-config-list/types.d.ts +14 -0
- package/dist/features/tab-launcher-menu/react/components/TabLauncherMenu.js +25 -1
- package/dist/features/tab-launcher-menu/react/hooks/useTabLauncherMenu.js +15 -16
- package/dist/features/tab-strip/react/components/TabBar.js +8 -1
- package/dist/features/tabbed-dialog/react/components/TabbedDialog.js +8 -30
- package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.d.ts +15 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.js +8 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.d.ts +12 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.js +6 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.d.ts +27 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.js +12 -0
- package/dist/features/tabbed-dialog/react/components/tabbedDialogView.d.ts +43 -0
- package/dist/features/tabbed-dialog/react/components/tabbedDialogView.js +59 -0
- package/dist/features/version-manager/dependencies.d.ts +3 -1
- package/dist/features/version-manager/dependencies.js +3 -2
- package/dist/features/version-manager/react/components/VersionManagerModal.js +10 -6
- package/dist/features/version-manager/react/hooks/useVersionManager.js +7 -4
- package/dist/features/viewer-shell/react/components/CommentSidePanel.js +103 -50
- package/dist/features/viewer-shell/react/components/MarkdownSplitPane.js +28 -8
- package/dist/features/viewer-shell/react/hooks/useCommentReorder.js +22 -16
- package/dist/features/viewer-shell/rules.js +19 -23
- package/dist/react/components/CustomSelect.d.ts +38 -1
- package/dist/react/components/CustomSelect.js +94 -38
- package/dist/react/components/OnboardingDropdown.js +20 -8
- package/dist/react/components/RemixIcon.stylesheet.js +3 -1
- package/dist/react/components/Toast.js +13 -2
- package/dist/react/components/TooltipLayer.js +5 -14
- package/dist/react/components/WorkingDirPicker.js +78 -50
- package/dist/react/components/remixicon-font-recovery.js +38 -30
- package/dist/react/components/select-popover.d.ts +8 -6
- package/dist/react/components/select-popover.js +33 -29
- package/dist/react/mcp-ui/McpUiHost.d.ts +1 -1
- package/dist/react/mcp-ui/McpUiHost.js +10 -11
- package/dist/react/mcp-ui/host-message-source.d.ts +14 -2
- package/dist/react/mcp-ui/host-message-source.js +15 -5
- package/dist/react/mcp-ui/mcp-ui-host-frame.d.ts +41 -0
- package/dist/react/mcp-ui/mcp-ui-host-frame.js +39 -0
- package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +97 -69
- package/dist/renderers/preview-modal-shell/react/components/PreviewModalShell.js +59 -30
- package/dist/renderers/react/components/ArtifactView.js +9 -6
- package/dist/renderers/srcdoc/build.d.ts +4 -1
- package/dist/renderers/srcdoc/build.js +62 -48
- package/dist/styles/admin.css +29 -14
- package/dist/styles/lightbox.css +142 -0
- package/dist/styles/variables.css +128 -0
- package/dist/theme/apply.js +9 -5
- package/dist/utils/auto-open-file.d.ts +2 -1
- package/dist/utils/auto-open-file.js +69 -85
- package/dist/utils/markdown-scroll-sync.js +108 -87
- package/dist/utils/polygon-selection.js +25 -26
- package/dist/utils/smooth-scroll-to-top.d.ts +1 -1
- package/dist/utils/smooth-scroll-to-top.js +2 -32
- package/dist/utils/sse.js +26 -26
- package/dist/utils/unit-bezier.d.ts +7 -0
- package/dist/utils/unit-bezier.js +38 -0
- package/package.json +9 -12
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.d.ts +0 -13
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.js +0 -46
|
@@ -18,15 +18,32 @@ import { renderCheckbox } from './checkbox.js';
|
|
|
18
18
|
import { renderChoiceGroup } from './choice-group.js';
|
|
19
19
|
import { renderSelect } from './select.js';
|
|
20
20
|
import { renderTextInput } from './text-input.js';
|
|
21
|
-
/**
|
|
21
|
+
/** The listed properties that are present on `source`, in listed order.
|
|
22
|
+
* Only the properties present on a given field are forwarded, because `exactOptionalPropertyTypes` makes `{ hint: undefined }` and `{}` different types.
|
|
23
|
+
* @complexity O(k) in the key count. */
|
|
24
|
+
function present(source, keys) {
|
|
25
|
+
const picked = {};
|
|
26
|
+
for (const key of keys) {
|
|
27
|
+
const value = source[key];
|
|
28
|
+
if (value !== undefined)
|
|
29
|
+
picked[key] = value;
|
|
30
|
+
}
|
|
31
|
+
return picked;
|
|
32
|
+
}
|
|
22
33
|
function base(field) {
|
|
23
|
-
return {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
return { name: field.name, label: field.label, ...present(field, ['hint', 'required', 'disabled']) };
|
|
35
|
+
}
|
|
36
|
+
/** One-of-N, drawn as radios or a select per {@link EnumField.presentation}. @complexity O(n) in the rendered length. */
|
|
37
|
+
function renderEnumField(field) {
|
|
38
|
+
if (field.presentation === 'radio') {
|
|
39
|
+
return renderChoiceGroup({
|
|
40
|
+
...base(field),
|
|
41
|
+
selection: 'single',
|
|
42
|
+
options: field.options,
|
|
43
|
+
...(field.value === undefined ? {} : { value: [field.value] }),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return renderSelect({ ...base(field), options: field.options, ...present(field, ['value', 'placeholder']) });
|
|
30
47
|
}
|
|
31
48
|
/**
|
|
32
49
|
* Renders the control fragment for one field, dispatching on its kind.
|
|
@@ -39,52 +56,15 @@ function base(field) {
|
|
|
39
56
|
export function renderFieldControl(field, text) {
|
|
40
57
|
switch (field.kind) {
|
|
41
58
|
case 'string':
|
|
42
|
-
return renderTextInput({
|
|
43
|
-
...base(field),
|
|
44
|
-
...text,
|
|
45
|
-
...(field.value === undefined ? {} : { value: field.value }),
|
|
46
|
-
...(field.placeholder === undefined ? {} : { placeholder: field.placeholder }),
|
|
47
|
-
...(field.multiline === undefined ? {} : { multiline: field.multiline }),
|
|
48
|
-
...(field.rows === undefined ? {} : { rows: field.rows }),
|
|
49
|
-
...(field.secret === undefined ? {} : { secret: field.secret }),
|
|
50
|
-
});
|
|
59
|
+
return renderTextInput({ ...base(field), ...text, ...present(field, ['value', 'placeholder', 'multiline', 'rows', 'secret']) });
|
|
51
60
|
case 'number':
|
|
52
|
-
return renderTextInput({
|
|
53
|
-
...base(field),
|
|
54
|
-
inputType: 'number',
|
|
55
|
-
...(field.value === undefined ? {} : { value: field.value }),
|
|
56
|
-
...(field.placeholder === undefined ? {} : { placeholder: field.placeholder }),
|
|
57
|
-
...(field.min === undefined ? {} : { min: field.min }),
|
|
58
|
-
...(field.max === undefined ? {} : { max: field.max }),
|
|
59
|
-
...(field.step === undefined ? {} : { step: field.step }),
|
|
60
|
-
});
|
|
61
|
+
return renderTextInput({ ...base(field), inputType: 'number', ...present(field, ['value', 'placeholder', 'min', 'max', 'step']) });
|
|
61
62
|
case 'boolean':
|
|
62
|
-
return renderCheckbox({
|
|
63
|
-
...base(field),
|
|
64
|
-
...(field.value === undefined ? {} : { value: field.value }),
|
|
65
|
-
});
|
|
63
|
+
return renderCheckbox({ ...base(field), ...present(field, ['value']) });
|
|
66
64
|
case 'enum':
|
|
67
|
-
|
|
68
|
-
return renderChoiceGroup({
|
|
69
|
-
...base(field),
|
|
70
|
-
selection: 'single',
|
|
71
|
-
options: field.options,
|
|
72
|
-
...(field.value === undefined ? {} : { value: [field.value] }),
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
return renderSelect({
|
|
76
|
-
...base(field),
|
|
77
|
-
options: field.options,
|
|
78
|
-
...(field.value === undefined ? {} : { value: field.value }),
|
|
79
|
-
...(field.placeholder === undefined ? {} : { placeholder: field.placeholder }),
|
|
80
|
-
});
|
|
65
|
+
return renderEnumField(field);
|
|
81
66
|
case 'multi-enum':
|
|
82
|
-
return renderChoiceGroup({
|
|
83
|
-
...base(field),
|
|
84
|
-
selection: 'multiple',
|
|
85
|
-
options: field.options,
|
|
86
|
-
...(field.value === undefined ? {} : { value: field.value }),
|
|
87
|
-
});
|
|
67
|
+
return renderChoiceGroup({ ...base(field), selection: 'multiple', options: field.options, ...present(field, ['value']) });
|
|
88
68
|
}
|
|
89
69
|
}
|
|
90
70
|
/** Projects fields down to what the inline script needs, so the generated JS carries no labels, hints, or option lists it will never read. */
|
|
@@ -42,6 +42,12 @@ export interface FormSurfaceSpec {
|
|
|
42
42
|
/** Styles the submit button as destructive. */
|
|
43
43
|
readonly danger?: boolean;
|
|
44
44
|
readonly submitLabel: string;
|
|
45
|
+
/** Alternative submit buttons collect the same fields, with explicit action params. */
|
|
46
|
+
readonly additionalSubmitActions?: readonly {
|
|
47
|
+
readonly id: string;
|
|
48
|
+
readonly label: string;
|
|
49
|
+
readonly params: Readonly<Record<string, unknown>>;
|
|
50
|
+
}[];
|
|
45
51
|
/** The tool the collected values are sent to. */
|
|
46
52
|
readonly toolName: string;
|
|
47
53
|
/**
|
|
@@ -31,51 +31,61 @@ import { DEFAULT_SECRET_INPUT_TEXT } from './text-input.js';
|
|
|
31
31
|
const DEFAULT_APP = { appName: 'jini-mcp-ui-form', appVersion: '1' };
|
|
32
32
|
/** The form element's id, fixed so the emitted script can find it without a fragile selector. */
|
|
33
33
|
const FORM_ELEMENT_ID = 'mcpui-form';
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
|
|
38
|
-
* @returns The full HTML string.
|
|
39
|
-
* @complexity O(n) in the number of fields plus the rendered length.
|
|
40
|
-
*/
|
|
41
|
-
export function renderFormDocument(spec) {
|
|
42
|
-
// A base param with a secret field's name would leak the same value through the emitted script,
|
|
43
|
-
// even though the collected value overrides it at submit time. Reject that hidden prefill too.
|
|
34
|
+
/** A base param with a secret field's name would leak the same value through the emitted script,
|
|
35
|
+
* even though the collected value overrides it at submit time. Reject that hidden prefill too.
|
|
36
|
+
* @complexity O(n) in the field count. @throws Error with fixed text, never the field name or value. */
|
|
37
|
+
function assertNoSecretBaseParam(spec) {
|
|
44
38
|
for (const field of spec.fields) {
|
|
45
39
|
if (field.kind === 'string' && field.secret === true && spec.baseParams?.[field.name] !== undefined) {
|
|
46
40
|
throw new Error('Secret fields cannot have a pre-filled value. Omit value.');
|
|
47
41
|
}
|
|
48
42
|
}
|
|
49
|
-
|
|
43
|
+
}
|
|
44
|
+
/** Submit, any alternative submits, then cancel. @complexity O(a) in the action count. */
|
|
45
|
+
function formActions(spec) {
|
|
50
46
|
// Not `type: 'submit'` — see the runtime script below for why a submit-typed button inside this
|
|
51
47
|
// particular `<form>` cannot work at all in production.
|
|
52
48
|
const actions = [
|
|
53
49
|
{ id: 'submit', label: spec.submitLabel, variant: spec.danger === true ? 'danger' : 'primary' },
|
|
54
50
|
];
|
|
51
|
+
for (const action of spec.additionalSubmitActions ?? [])
|
|
52
|
+
actions.push({ id: `submit:${action.id}`, label: action.label, variant: 'neutral' });
|
|
55
53
|
if (spec.cancel !== undefined)
|
|
56
54
|
actions.push({ id: 'cancel', label: spec.cancel.label, variant: 'neutral' });
|
|
55
|
+
return actions;
|
|
56
|
+
}
|
|
57
|
+
/** Header, context, the `<form>` itself and the status region. @complexity O(n) in the rendered length. */
|
|
58
|
+
function formBodyHtml(spec, text) {
|
|
57
59
|
const controls = spec.fields
|
|
58
60
|
.map((field) => renderFieldControl(field, { showSecret: text.showSecret, hideSecret: text.hideSecret }))
|
|
59
61
|
.join('\n');
|
|
60
62
|
const warning = spec.warning === undefined ? '' : `<p class="mcpui-warning">${escapeHtml(spec.warning)}</p>`;
|
|
61
|
-
|
|
63
|
+
return [
|
|
62
64
|
renderSurfaceHeader(spec.description === undefined ? { title: spec.title } : { title: spec.title, description: spec.description }),
|
|
63
65
|
renderDetailList(spec.details ?? []),
|
|
64
|
-
`<form id="${FORM_ELEMENT_ID}" novalidate>\n<fieldset class="mcpui-fields">\n${controls}\n</fieldset>\n${warning}\n${renderActions(
|
|
66
|
+
`<form id="${FORM_ELEMENT_ID}" novalidate>\n<fieldset class="mcpui-fields">\n${controls}\n</fieldset>\n${warning}\n${renderActions(formActions(spec))}\n</form>`,
|
|
65
67
|
renderStatusRegion(),
|
|
66
68
|
]
|
|
67
69
|
.filter((fragment) => fragment !== '')
|
|
68
70
|
.join('\n');
|
|
69
|
-
|
|
71
|
+
}
|
|
72
|
+
/** The cancel tool call, or null for a cancel that only tears the surface down. @complexity O(1). */
|
|
73
|
+
function cancelPlanOf(spec) {
|
|
74
|
+
return spec.cancel?.toolName === undefined
|
|
70
75
|
? null
|
|
71
76
|
: { toolName: spec.cancel.toolName, params: spec.cancel.params ?? {} };
|
|
72
|
-
|
|
77
|
+
}
|
|
78
|
+
/** The inline runtime script that reads, validates and submits the fields. @complexity O(n) in the field count plus the script length. */
|
|
79
|
+
function formScript(spec, text) {
|
|
80
|
+
const cancelPlan = cancelPlanOf(spec);
|
|
81
|
+
return `(function () {
|
|
73
82
|
"use strict";
|
|
74
83
|
${SURFACE_SCRIPT_PRELUDE}
|
|
75
84
|
var FIELDS = ${escapeJsValue(toFieldReadSpecs(spec.fields))};
|
|
76
85
|
var TOOL = ${escapeJsValue(spec.toolName)};
|
|
77
86
|
var BASE_PARAMS = ${escapeJsValue(spec.baseParams ?? {})};
|
|
78
87
|
var CANCEL = ${escapeJsValue(cancelPlan)};
|
|
88
|
+
var SUBMIT_ACTIONS = ${escapeJsValue(Object.fromEntries((spec.additionalSubmitActions ?? []).map(action => [`submit:${action.id}`, action.params])))};
|
|
79
89
|
var TEXT = ${escapeJsValue(text)};
|
|
80
90
|
var form = document.getElementById(${escapeJsValue(FORM_ELEMENT_ID)});
|
|
81
91
|
|
|
@@ -186,7 +196,7 @@ ${SURFACE_SCRIPT_PRELUDE}
|
|
|
186
196
|
// says the dialog is no longer pending, where a retry cannot succeed (see reportCallFailure).
|
|
187
197
|
var pending = false;
|
|
188
198
|
|
|
189
|
-
function runSubmit() {
|
|
199
|
+
function runSubmit(actionParams) {
|
|
190
200
|
if (pending) return;
|
|
191
201
|
var params = {};
|
|
192
202
|
var key;
|
|
@@ -201,6 +211,7 @@ ${SURFACE_SCRIPT_PRELUDE}
|
|
|
201
211
|
setStatus(TEXT.missingPrefix + missing.join(", "), "invalid");
|
|
202
212
|
return;
|
|
203
213
|
}
|
|
214
|
+
for (key in actionParams) if (Object.prototype.hasOwnProperty.call(actionParams, key)) params[key] = actionParams[key];
|
|
204
215
|
pending = true;
|
|
205
216
|
setBusy(true);
|
|
206
217
|
setStatus(TEXT.working, "pending");
|
|
@@ -225,14 +236,14 @@ ${SURFACE_SCRIPT_PRELUDE}
|
|
|
225
236
|
var type = (target.getAttribute("type") || "text").toLowerCase();
|
|
226
237
|
if (type === "checkbox" || type === "radio") return;
|
|
227
238
|
event.preventDefault();
|
|
228
|
-
runSubmit();
|
|
239
|
+
runSubmit({});
|
|
229
240
|
});
|
|
230
241
|
|
|
231
242
|
for (var b = 0; b < actionButtons.length; b++) {
|
|
232
243
|
var action = actionButtons[b].getAttribute("data-mcpui-action");
|
|
233
|
-
if (action === "submit") {
|
|
244
|
+
if (action === "submit" || Object.prototype.hasOwnProperty.call(SUBMIT_ACTIONS, action)) {
|
|
234
245
|
actionButtons[b].addEventListener("click", function (event) {
|
|
235
|
-
if (event.isTrusted === true) runSubmit();
|
|
246
|
+
if (event.isTrusted === true) runSubmit(SUBMIT_ACTIONS[event.currentTarget.getAttribute("data-mcpui-action")] || {});
|
|
236
247
|
});
|
|
237
248
|
continue;
|
|
238
249
|
}
|
|
@@ -256,10 +267,21 @@ ${SURFACE_SCRIPT_PRELUDE}
|
|
|
256
267
|
});
|
|
257
268
|
}
|
|
258
269
|
}());`;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Renders the form dialog as a complete, self-contained HTML document.
|
|
273
|
+
*
|
|
274
|
+
* @param spec - See {@link FormSurfaceSpec}.
|
|
275
|
+
* @returns The full HTML string.
|
|
276
|
+
* @complexity O(n) in the number of fields plus the rendered length.
|
|
277
|
+
*/
|
|
278
|
+
export function renderFormDocument(spec) {
|
|
279
|
+
assertNoSecretBaseParam(spec);
|
|
280
|
+
const text = { ...DEFAULT_SURFACE_STATUS_TEXT, ...DEFAULT_SECRET_INPUT_TEXT, ...spec.text };
|
|
259
281
|
return renderSurfaceDocument({
|
|
260
282
|
title: spec.title,
|
|
261
|
-
bodyHtml,
|
|
262
|
-
script,
|
|
283
|
+
bodyHtml: formBodyHtml(spec, text),
|
|
284
|
+
script: formScript(spec, text),
|
|
263
285
|
app: spec.app ?? DEFAULT_APP,
|
|
264
286
|
...(spec.lang === undefined ? {} : { lang: spec.lang }),
|
|
265
287
|
...(spec.tokens === undefined ? {} : { tokens: spec.tokens }),
|
|
@@ -48,6 +48,33 @@ import { renderActions, renderDetailList, renderStatusRegion, renderSurfaceDocum
|
|
|
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';
|
|
51
|
+
/** The status region's `data-state` for an outcome state. @complexity O(1). */
|
|
52
|
+
function statusStateOf(state) {
|
|
53
|
+
if (state === 'success')
|
|
54
|
+
return 'done';
|
|
55
|
+
return state === 'partial' ? 'partial' : 'failed';
|
|
56
|
+
}
|
|
57
|
+
/** The open-link button, or nothing without a link. @complexity O(n) in the label length. */
|
|
58
|
+
function renderOpenLinkActions(spec) {
|
|
59
|
+
if (spec.openLinkUrl === undefined)
|
|
60
|
+
return '';
|
|
61
|
+
return renderActions([{ id: OPEN_LINK_ACTION_ID, label: spec.openLinkLabel ?? DEFAULT_OPEN_LINK_LABEL, variant: spec.state === 'success' ? 'primary' : 'neutral' }]);
|
|
62
|
+
}
|
|
63
|
+
/** The inline script: wires the open-link button, or nothing at all. @complexity O(n) in the URL length. */
|
|
64
|
+
function outcomeScript(openLinkUrl) {
|
|
65
|
+
// No prelude at all when there is nothing to wire up — a pure-display result needs no bridge
|
|
66
|
+
// calls and no `[data-mcpui-action]` scan.
|
|
67
|
+
if (openLinkUrl === undefined)
|
|
68
|
+
return '(function () { "use strict"; }());';
|
|
69
|
+
return `(function () {
|
|
70
|
+
"use strict";
|
|
71
|
+
${SURFACE_SCRIPT_PRELUDE}
|
|
72
|
+
var OPEN_URL = ${escapeJsValue(openLinkUrl)};
|
|
73
|
+
for (var i = 0; i < actionButtons.length; i++) {
|
|
74
|
+
actionButtons[i].addEventListener("click", function () { api.openLink(OPEN_URL); });
|
|
75
|
+
}
|
|
76
|
+
}());`;
|
|
77
|
+
}
|
|
51
78
|
/**
|
|
52
79
|
* Renders the outcome document as a complete, self-contained HTML document.
|
|
53
80
|
*
|
|
@@ -56,40 +83,23 @@ const OPEN_LINK_ACTION_ID = 'open-link';
|
|
|
56
83
|
* @complexity O(n) in the rendered length.
|
|
57
84
|
*/
|
|
58
85
|
export function renderOutcomeDocument(spec) {
|
|
59
|
-
const statusState = spec.state === 'success' ? 'done' : spec.state === 'partial' ? 'partial' : 'failed';
|
|
60
86
|
// Rendered directly into the status region rather than left for the script to set — this document
|
|
61
87
|
// has nothing further to wait on (unlike a confirmation, which starts idle and only reaches "done"
|
|
62
88
|
// after a click), so the true outcome should be visible from the FIRST paint, not a moment after
|
|
63
89
|
// the script runs.
|
|
64
|
-
const statusHtml = `<p class="mcpui-status" id="${SURFACE_STATUS_ELEMENT_ID}" role="status" aria-live="polite" data-state="${
|
|
65
|
-
const hasOpenLink = spec.openLinkUrl !== undefined;
|
|
66
|
-
const actionsHtml = hasOpenLink
|
|
67
|
-
? renderActions([{ id: OPEN_LINK_ACTION_ID, label: spec.openLinkLabel ?? DEFAULT_OPEN_LINK_LABEL, variant: spec.state === 'success' ? 'primary' : 'neutral' }])
|
|
68
|
-
: '';
|
|
90
|
+
const statusHtml = `<p class="mcpui-status" id="${SURFACE_STATUS_ELEMENT_ID}" role="status" aria-live="polite" data-state="${statusStateOf(spec.state)}">${escapeHtml(spec.message)}</p>`;
|
|
69
91
|
const bodyHtml = [
|
|
70
92
|
renderSurfaceHeader(spec.description === undefined ? { title: spec.title } : { title: spec.title, description: spec.description }),
|
|
71
93
|
renderDetailList(spec.details ?? [], { layout: 'outcome' }),
|
|
72
94
|
statusHtml,
|
|
73
|
-
|
|
95
|
+
renderOpenLinkActions(spec),
|
|
74
96
|
]
|
|
75
97
|
.filter((fragment) => fragment !== '')
|
|
76
98
|
.join('\n');
|
|
77
|
-
const script = hasOpenLink
|
|
78
|
-
? `(function () {
|
|
79
|
-
"use strict";
|
|
80
|
-
${SURFACE_SCRIPT_PRELUDE}
|
|
81
|
-
var OPEN_URL = ${escapeJsValue(spec.openLinkUrl)};
|
|
82
|
-
for (var i = 0; i < actionButtons.length; i++) {
|
|
83
|
-
actionButtons[i].addEventListener("click", function () { api.openLink(OPEN_URL); });
|
|
84
|
-
}
|
|
85
|
-
}());`
|
|
86
|
-
// No prelude at all when there is nothing to wire up — a pure-display result needs no bridge
|
|
87
|
-
// calls and no `[data-mcpui-action]` scan.
|
|
88
|
-
: '(function () { "use strict"; }());';
|
|
89
99
|
return renderSurfaceDocument({
|
|
90
100
|
title: spec.title,
|
|
91
101
|
bodyHtml,
|
|
92
|
-
script,
|
|
102
|
+
script: outcomeScript(spec.openLinkUrl),
|
|
93
103
|
app: spec.app ?? DEFAULT_APP,
|
|
94
104
|
...(spec.lang === undefined ? {} : { lang: spec.lang }),
|
|
95
105
|
...(spec.tokens === undefined ? {} : { tokens: spec.tokens }),
|
|
@@ -29,6 +29,36 @@ function resolveInputType(props) {
|
|
|
29
29
|
return 'number';
|
|
30
30
|
return props.secret === true ? 'password' : 'text';
|
|
31
31
|
}
|
|
32
|
+
/** The reveal toggle a multiline secret carries. @complexity O(n) in the label lengths. */
|
|
33
|
+
function renderSecretToggle(props, id) {
|
|
34
|
+
const showSecret = props.showSecret ?? DEFAULT_SECRET_INPUT_TEXT.showSecret;
|
|
35
|
+
const hideSecret = props.hideSecret ?? DEFAULT_SECRET_INPUT_TEXT.hideSecret;
|
|
36
|
+
return `<button type="button" class="mcpui-button" data-mcpui-secret-toggle="${escapeHtml(id)}" data-mcpui-show-secret="${escapeHtml(showSecret)}" data-mcpui-hide-secret="${escapeHtml(hideSecret)}" aria-controls="${escapeHtml(id)}" aria-pressed="false"${booleanAttribute('disabled', props.disabled)}>${escapeHtml(showSecret)}</button>`;
|
|
37
|
+
}
|
|
38
|
+
/** The `<textarea>` control, masked with a reveal toggle when secret. @complexity O(n) in the rendered length. */
|
|
39
|
+
function renderTextarea(props, required) {
|
|
40
|
+
const secret = props.secret === true;
|
|
41
|
+
return `<textarea class="mcpui-textarea"${required.common}${optionalAttribute('rows', props.rows)}` +
|
|
42
|
+
// Password inputs strip line breaks. CSS masking keeps the textarea's actual value intact,
|
|
43
|
+
// and disables browser text assistance that could retain or alter a pasted credential.
|
|
44
|
+
(secret ? ' data-mcpui-secret style="-webkit-text-security: disc;" autocomplete="off" spellcheck="false"' : '') +
|
|
45
|
+
`>${props.value === undefined ? '' : escapeHtml(String(props.value))}</textarea>` +
|
|
46
|
+
(secret ? renderSecretToggle(props, required.id) : '');
|
|
47
|
+
}
|
|
48
|
+
/** The single-line `<input>` control. @complexity O(n) in the rendered length. */
|
|
49
|
+
function renderInput(props, required) {
|
|
50
|
+
const { type, common } = required;
|
|
51
|
+
return `<input class="mcpui-input" type="${type}"${common}` +
|
|
52
|
+
optionalAttribute('value', props.value) +
|
|
53
|
+
// A stored secret (e.g. a cloud provider's access key or a database password) must never be
|
|
54
|
+
// autofilled with a saved website login. `new-password`, not `off`: Chrome ignores `off` on
|
|
55
|
+
// password inputs by design and fills the saved login anyway.
|
|
56
|
+
(type === 'password' ? optionalAttribute('autocomplete', 'new-password') : '') +
|
|
57
|
+
(type === 'number'
|
|
58
|
+
? optionalAttribute('min', props.min) + optionalAttribute('max', props.max) + optionalAttribute('step', props.step)
|
|
59
|
+
: '') +
|
|
60
|
+
'>';
|
|
61
|
+
}
|
|
32
62
|
/**
|
|
33
63
|
* Renders one free-text field: its label, hint, and control, wrapped in `.mcpui-field`.
|
|
34
64
|
*
|
|
@@ -49,27 +79,9 @@ export function renderTextInput(props) {
|
|
|
49
79
|
booleanAttribute('disabled', props.disabled) +
|
|
50
80
|
fieldDescribedBy(props);
|
|
51
81
|
const type = resolveInputType(props);
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
? `<textarea class="mcpui-textarea"${common}${optionalAttribute('rows', props.rows)}` +
|
|
56
|
-
// Password inputs strip line breaks. CSS masking keeps the textarea's actual value intact,
|
|
57
|
-
// and disables browser text assistance that could retain or alter a pasted credential.
|
|
58
|
-
(secretMultiline ? ' data-mcpui-secret style="-webkit-text-security: disc;" autocomplete="off" spellcheck="false"' : '') +
|
|
59
|
-
`>${props.value === undefined ? '' : escapeHtml(String(props.value))}</textarea>` +
|
|
60
|
-
(secretMultiline
|
|
61
|
-
? `<button type="button" class="mcpui-button" data-mcpui-secret-toggle="${escapeHtml(id)}" data-mcpui-show-secret="${escapeHtml(props.showSecret ?? DEFAULT_SECRET_INPUT_TEXT.showSecret)}" data-mcpui-hide-secret="${escapeHtml(props.hideSecret ?? DEFAULT_SECRET_INPUT_TEXT.hideSecret)}" aria-controls="${escapeHtml(id)}" aria-pressed="false"${booleanAttribute('disabled', props.disabled)}>${escapeHtml(props.showSecret ?? DEFAULT_SECRET_INPUT_TEXT.showSecret)}</button>`
|
|
62
|
-
: '')
|
|
63
|
-
: `<input class="mcpui-input" type="${type}"${common}` +
|
|
64
|
-
optionalAttribute('value', props.value) +
|
|
65
|
-
// A stored secret (e.g. a cloud provider's access key or a database password) must never be
|
|
66
|
-
// autofilled with a saved website login. `new-password`, not `off`: Chrome ignores `off` on
|
|
67
|
-
// password inputs by design and fills the saved login anyway.
|
|
68
|
-
(type === 'password' ? optionalAttribute('autocomplete', 'new-password') : '') +
|
|
69
|
-
(isNumber
|
|
70
|
-
? optionalAttribute('min', props.min) + optionalAttribute('max', props.max) + optionalAttribute('step', props.step)
|
|
71
|
-
: '') +
|
|
72
|
-
'>';
|
|
82
|
+
const control = props.multiline === true && type !== 'number'
|
|
83
|
+
? renderTextarea(props, { id, common })
|
|
84
|
+
: renderInput(props, { type, common });
|
|
73
85
|
return `<div class="mcpui-field">\n${renderFieldLabel(props)}\n${control}\n</div>`;
|
|
74
86
|
}
|
|
75
87
|
//# sourceMappingURL=text-input.js.map
|
|
@@ -100,7 +100,12 @@ export declare const FORCE_LIGHT_SURFACE_THEME = true;
|
|
|
100
100
|
* @returns A `:root { … }` block, then a `:root:root { … }` block holding the overrides (when any),
|
|
101
101
|
* followed by a `@media (prefers-color-scheme: dark) { :root { … } }` block unless {@link FORCE_LIGHT_SURFACE_THEME} suppresses it, ready to paste into the first
|
|
102
102
|
* `<style>` tag.
|
|
103
|
+
* @param options.forceLight - Defaults to {@link FORCE_LIGHT_SURFACE_THEME}. Exposed so the dark
|
|
104
|
+
* variant this function still knows how to emit is reachable (and verified) rather than dead code
|
|
105
|
+
* behind a module constant; every production caller keeps the default.
|
|
103
106
|
* @complexity O(n) in the number of tokens.
|
|
104
107
|
*/
|
|
105
|
-
export declare function renderTokenBlock(overrides?: Partial<Record<SurfaceTokenName, string
|
|
108
|
+
export declare function renderTokenBlock(overrides?: Partial<Record<SurfaceTokenName, string>>, { forceLight }?: {
|
|
109
|
+
forceLight?: boolean;
|
|
110
|
+
}): string;
|
|
106
111
|
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -148,9 +148,12 @@ export const FORCE_LIGHT_SURFACE_THEME = true;
|
|
|
148
148
|
* @returns A `:root { … }` block, then a `:root:root { … }` block holding the overrides (when any),
|
|
149
149
|
* followed by a `@media (prefers-color-scheme: dark) { :root { … } }` block unless {@link FORCE_LIGHT_SURFACE_THEME} suppresses it, ready to paste into the first
|
|
150
150
|
* `<style>` tag.
|
|
151
|
+
* @param options.forceLight - Defaults to {@link FORCE_LIGHT_SURFACE_THEME}. Exposed so the dark
|
|
152
|
+
* variant this function still knows how to emit is reachable (and verified) rather than dead code
|
|
153
|
+
* behind a module constant; every production caller keeps the default.
|
|
151
154
|
* @complexity O(n) in the number of tokens.
|
|
152
155
|
*/
|
|
153
|
-
export function renderTokenBlock(overrides = {}) {
|
|
156
|
+
export function renderTokenBlock(overrides = {}, { forceLight = FORCE_LIGHT_SURFACE_THEME } = {}) {
|
|
154
157
|
const declare = (entries) => entries.map(([name, value]) => ` ${name}: ${value};`).join('\n');
|
|
155
158
|
const notOverridden = ([name]) => !(name in overrides);
|
|
156
159
|
const light = declare([
|
|
@@ -162,7 +165,7 @@ export function renderTokenBlock(overrides = {}) {
|
|
|
162
165
|
const overrideEntries = Object.entries(overrides);
|
|
163
166
|
const overrideBlock = overrideEntries.length === 0 ? '' : `\n:root:root {\n${declare(overrideEntries)}\n}`;
|
|
164
167
|
const lightBlock = `:root {\n${light}\n}${overrideBlock}`;
|
|
165
|
-
if (
|
|
168
|
+
if (forceLight)
|
|
166
169
|
return lightBlock;
|
|
167
170
|
const dark = declare([
|
|
168
171
|
...Object.entries(SURFACE_TOKENS_DARK).filter(notOverridden),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { MediaProviderOption } from './types.js';
|
|
2
|
+
/** @deprecated Supply a host catalog. The former starter set is exported by integrations/media-providers/catalog. */
|
|
2
3
|
export declare const DEFAULT_MEDIA_PROVIDER_CATALOG: readonly MediaProviderOption[];
|
|
3
4
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1,82 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* A small, vendor-shaped starting catalog — same convention as `execution/constants.ts`'s
|
|
4
|
-
* `DEFAULT_PROVIDER_PRESETS`. Sourced by driving Open Design's own Settings → Media providers
|
|
5
|
-
* panel directly (Playwright) and transcribing its real provider list, base URLs, and default
|
|
6
|
-
* models. Pass your own `catalog` to `MediaProvidersTab` to replace this wholesale; Zana or any
|
|
7
|
-
* other host is expected to do so once it has its own provider roster.
|
|
8
|
-
*/
|
|
9
|
-
const starterCatalog = [
|
|
10
|
-
{
|
|
11
|
-
id: 'nano-banana',
|
|
12
|
-
label: 'Nano Banana',
|
|
13
|
-
models: ['gemini-3.1-flash-image-preview'],
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
id: 'aihubmix',
|
|
17
|
-
label: 'AIHubMix',
|
|
18
|
-
models: ['gemini-3.1-flash-image-preview'],
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
id: 'custom-image-api',
|
|
22
|
-
label: 'Custom Media API',
|
|
23
|
-
models: ['gemini-3.1-flash-image-preview'],
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
id: 'elevenlabs',
|
|
27
|
-
label: 'ElevenLabs',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
id: 'fal-ai',
|
|
31
|
-
label: 'Fal.ai',
|
|
32
|
-
models: ['gemini-3.1-flash-image-preview'],
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: 'fishaudio',
|
|
36
|
-
label: 'FishAudio',
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
id: 'imagerouter',
|
|
40
|
-
label: 'ImageRouter',
|
|
41
|
-
models: ['gemini-3.1-flash-image-preview'],
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
id: 'leonardo-ai',
|
|
45
|
-
label: 'Leonardo.ai',
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
id: 'minimax',
|
|
49
|
-
label: 'MiniMax',
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
id: 'openai',
|
|
53
|
-
label: 'OpenAI',
|
|
54
|
-
models: ['gpt-image-2', 'dall-e-3'],
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
id: 'openrouter',
|
|
58
|
-
label: 'OpenRouter',
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
id: 'senseaudio',
|
|
62
|
-
label: 'SenseAudio',
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
id: 'tavily-search',
|
|
66
|
-
label: 'Tavily Search',
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
id: 'volcengine-ark',
|
|
70
|
-
label: 'Volcengine Ark (Doubao)',
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
id: 'xai-grok-imagine',
|
|
74
|
-
label: 'xAI Grok Imagine',
|
|
75
|
-
},
|
|
76
|
-
];
|
|
77
|
-
export const DEFAULT_MEDIA_PROVIDER_CATALOG = starterCatalog.map((entry) => {
|
|
78
|
-
const id = canonicalMediaProviderId({ id: entry.id });
|
|
79
|
-
const provider = findProvider({ id });
|
|
80
|
-
return { ...entry, id, label: provider.label, ...(provider.defaultBaseUrl ? { defaultBaseUrl: provider.defaultBaseUrl } : {}) };
|
|
81
|
-
});
|
|
1
|
+
/** @deprecated Supply a host catalog. The former starter set is exported by integrations/media-providers/catalog. */
|
|
2
|
+
export const DEFAULT_MEDIA_PROVIDER_CATALOG = [];
|
|
82
3
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { DEFAULT_MEDIA_PROVIDER_CATALOG } from './constants.js';
|
|
2
2
|
export { createFakeMediaProvidersPort, type FakeMediaProvidersPortOptions } from './dependencies.js';
|
|
3
|
-
export type { MediaProvidersPort } from './ports.js';
|
|
3
|
+
export type { MediaProvidersPort, MediaProviderCatalogPolicy } from './ports.js';
|
|
4
4
|
export { hasAnyConfiguredProvider, hasRecoverableFields, isEntryEmpty, isEntryPresent, isMarkerOnlyEntry, maskedKeyLabel, mergeDaemonProviders, resolveProviderBaseUrl, shouldSyncLocalProvidersToDaemon, sortProvidersByConfigured, isProviderBaseUrlInvalid, invalidBaseUrlProviderIds, } from './rules.js';
|
|
5
5
|
export type { MediaProviderCredentials, MediaProviderMap, MediaProviderOption, MediaProvidersLoadState, MediaProvidersSaveState, } from './types.js';
|
|
6
6
|
export { useMediaProvidersTab } from './react/hooks/useMediaProvidersTab.js';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Catalog policy is UI-owned; the shared contract lives once in core. */
|
|
2
|
+
export type { MediaProviderCatalogPolicy } from '@jini-ai/core';
|
|
1
3
|
import type { MediaProviderMap } from './types.js';
|
|
2
4
|
/**
|
|
3
5
|
* The daemon-side transport this tab needs: reading the server's current
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { I18nContextValue } from '../../../i18n/index.js';
|
|
2
|
+
import type { MediaProviderCredentials, MediaProviderOption } from '../../types.js';
|
|
3
|
+
import type { MediaProviderEditPatch } from '../hooks/useMediaProvidersTab.js';
|
|
4
|
+
import type { ResolvedMediaProvidersTabLabels } from './media-providers-tab-labels.js';
|
|
5
|
+
type Translate = I18nContextValue['t'];
|
|
6
|
+
export interface MediaProviderCardProps {
|
|
7
|
+
option: MediaProviderOption;
|
|
8
|
+
entry: MediaProviderCredentials;
|
|
9
|
+
labels: ResolvedMediaProvidersTabLabels;
|
|
10
|
+
t: Translate;
|
|
11
|
+
pending: boolean;
|
|
12
|
+
keyVisible: boolean;
|
|
13
|
+
showDividerBefore: boolean;
|
|
14
|
+
providerHandle: string | undefined;
|
|
15
|
+
onUpdate: (patch: MediaProviderEditPatch) => void;
|
|
16
|
+
onClear: () => void;
|
|
17
|
+
onToggleKeyVisible: () => void;
|
|
18
|
+
}
|
|
19
|
+
/** One provider's credential card: API key, base URL, optional model, clear, and the base-URL hint. */
|
|
20
|
+
export declare function MediaProviderCard(props: MediaProviderCardProps): import("react").JSX.Element;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=MediaProviderCard.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment } from 'react';
|
|
3
|
+
import { agentHandleProps } from '@jini-ai/agentic';
|
|
4
|
+
import { Icon } from '../../../../react/components/Icon.js';
|
|
5
|
+
import { isEntryPresent, isMarkerOnlyEntry, isProviderBaseUrlInvalid, maskedKeyLabel, resolveProviderBaseUrl, } from '../../rules.js';
|
|
6
|
+
/** One provider's credential card: API key, base URL, optional model, clear, and the base-URL hint. */
|
|
7
|
+
export function MediaProviderCard(props) {
|
|
8
|
+
const { option, entry, labels, t, providerHandle, onUpdate } = props;
|
|
9
|
+
const saved = isMarkerOnlyEntry(entry);
|
|
10
|
+
// Guaranteed non-null: `isMarkerOnlyEntry` only holds when a server
|
|
11
|
+
// marker is present, and `maskedKeyLabel` always resolves a string
|
|
12
|
+
// for exactly that case — see both functions' doc comments.
|
|
13
|
+
const maskedLabel = saved ? maskedKeyLabel(entry) : null;
|
|
14
|
+
const fields = { option, entry, labels, providerHandle, onUpdate };
|
|
15
|
+
return (_jsxs(Fragment, { children: [props.showDividerBefore ? _jsx("hr", { className: "jini-media-provider-divider" }) : null, _jsxs("div", { className: "jini-media-provider-card", children: [_jsx(CardHead, { option: option, labels: labels, t: t, maskedLabel: maskedLabel, pending: props.pending }), _jsxs("div", { className: "jini-media-provider-fields", children: [_jsx(ApiKeyField, { ...fields, maskedLabel: maskedLabel, keyVisible: props.keyVisible, onToggleKeyVisible: props.onToggleKeyVisible }), _jsx(BaseUrlField, { ...fields }), option.models && option.models.length > 0 ? _jsx(ModelField, { ...fields, models: option.models }) : null, _jsx("button", { type: "button", className: "jini-button jini-button-ghost", disabled: !isEntryPresent(entry), "aria-label": `${option.label} ${labels.clearLabel}`, onClick: props.onClear, ...agentHandleProps({}, { base: providerHandle, ...({ action: 'clear', role: 'button', label: `${option.label} ${labels.clearLabel}` }) }), children: labels.clearLabel })] }), _jsx(BaseUrlHint, { entry: entry, option: option, labels: labels, t: t })] })] }));
|
|
16
|
+
}
|
|
17
|
+
function CardHead({ option, labels, t, maskedLabel, pending, }) {
|
|
18
|
+
// `maskedLabel` is non-null exactly when the entry is marker-only ("saved").
|
|
19
|
+
const saved = maskedLabel !== null;
|
|
20
|
+
return (_jsxs("div", { className: "jini-media-provider-card-head", children: [_jsx("strong", { children: option.label }), saved ? (
|
|
21
|
+
// `maskedLabel` is guaranteed non-null here — see its
|
|
22
|
+
// definition above — so no fallback is reachable to test.
|
|
23
|
+
_jsx("span", { className: "jini-field-status-badge jini-field-status-badge-success", children: t(labels.savedWithMaskTemplate, { mask: maskedLabel }) })) : null, !saved && pending ? _jsx("span", { className: "jini-field-status-badge", children: labels.unsavedLabel }) : null] }));
|
|
24
|
+
}
|
|
25
|
+
function ApiKeyField({ option, entry, labels, providerHandle, onUpdate, maskedLabel, keyVisible, onToggleKeyVisible, }) {
|
|
26
|
+
return (_jsxs("label", { className: "jini-media-provider-field jini-media-provider-field--secret", children: [_jsx("span", { className: "jini-field-label jini-sr-only", children: labels.apiKeyLabel }), _jsx("input", { className: "jini-input", type: keyVisible ? 'text' : 'password',
|
|
27
|
+
/* `new-password`, NOT `"off"` — Chrome deliberately ignores `off` on
|
|
28
|
+
credential-shaped fields, which is how a saved password gets autofilled into
|
|
29
|
+
an API-key box. Same defect, same fix, same reasoning as
|
|
30
|
+
`ByokProviderForm`'s own API-key input; see its comment. */
|
|
31
|
+
autoComplete: "new-password", spellCheck: false, placeholder: maskedLabel ?? labels.apiKeyPlaceholder, "aria-label": `${option.label} ${labels.apiKeyLabel}`, value: entry.apiKey ?? '', onChange: (event) => onUpdate({ apiKey: event.target.value }), ...agentHandleProps({}, { base: providerHandle, ...({ action: 'api-key', role: 'field', label: `${option.label} ${labels.apiKeyLabel}` }) }) }), _jsx("button", { type: "button", className: "jini-input-affix-btn", "aria-pressed": keyVisible, "aria-label": `${option.label} ${keyVisible ? labels.hideKeyLabel : labels.showKeyLabel}`, onClick: onToggleKeyVisible, ...agentHandleProps({}, { base: providerHandle, ...({ action: 'reveal-key', role: 'button', label: `${option.label} ${labels.showKeyLabel}` }) }), children: _jsx(Icon, { name: keyVisible ? 'eye-off' : 'eye', size: 14 }) })] }));
|
|
32
|
+
}
|
|
33
|
+
function BaseUrlField({ option, entry, labels, providerHandle, onUpdate }) {
|
|
34
|
+
return (_jsxs("label", { className: "jini-media-provider-field", children: [_jsx("span", { className: "jini-field-label jini-sr-only", children: labels.baseUrlLabel }), _jsx("input", { className: "jini-input", type: "url", inputMode: "url", spellCheck: false, placeholder: option.defaultBaseUrl || labels.baseUrlPlaceholder, "aria-label": `${option.label} ${labels.baseUrlLabel}`, value: entry.baseUrl ?? '', "aria-invalid": isProviderBaseUrlInvalid(entry) || undefined, onChange: (event) => onUpdate({ baseUrl: event.target.value }), ...agentHandleProps({}, { base: providerHandle, ...({ action: 'base-url', role: 'field', label: `${option.label} ${labels.baseUrlLabel}` }) }) })] }));
|
|
35
|
+
}
|
|
36
|
+
function ModelField({ option, entry, labels, providerHandle, onUpdate, models }) {
|
|
37
|
+
const modelListId = `jini-media-provider-models-${option.id}`;
|
|
38
|
+
return (_jsxs("label", { className: "jini-media-provider-field", children: [_jsx("span", { className: "jini-field-label jini-sr-only", children: labels.modelLabel }), _jsx("input", { className: "jini-input", list: modelListId, spellCheck: false, placeholder: labels.modelPlaceholder, "aria-label": `${option.label} ${labels.modelLabel}`, value: entry.model ?? '', onChange: (event) => onUpdate({ model: event.target.value }), ...agentHandleProps({}, { base: providerHandle, ...({ action: 'model', role: 'field', label: `${option.label} ${labels.modelLabel}` }) }) }), _jsx("datalist", { id: modelListId, children: models.map((model) => (_jsx("option", { value: model }, model))) })] }));
|
|
39
|
+
}
|
|
40
|
+
/** The invalid-URL alert, else the "uses X by default" hint while the field is blank, else nothing. */
|
|
41
|
+
function BaseUrlHint({ entry, option, labels, t, }) {
|
|
42
|
+
if (isProviderBaseUrlInvalid(entry)) {
|
|
43
|
+
return (_jsx("span", { className: "jini-field-hint jini-hint-error", role: "alert", children: labels.baseUrlInvalidLabel }));
|
|
44
|
+
}
|
|
45
|
+
const effectiveBaseUrl = resolveProviderBaseUrl(entry, option.defaultBaseUrl);
|
|
46
|
+
if ((entry.baseUrl ?? '').trim() || !effectiveBaseUrl)
|
|
47
|
+
return null;
|
|
48
|
+
return _jsx("span", { className: "jini-field-hint", children: t(labels.baseUrlDefaultHintTemplate, { url: effectiveBaseUrl }) });
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=MediaProviderCard.js.map
|