@jini-ai/ui 0.4.7 → 0.4.9
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/README.md +2 -0
- package/dist/features/admin-widgets/components/InfoTip.d.ts +23 -2
- package/dist/features/admin-widgets/components/InfoTip.hooks.d.ts +22 -2
- package/dist/features/admin-widgets/components/InfoTip.hooks.js +75 -28
- package/dist/features/admin-widgets/components/InfoTip.js +12 -4
- package/dist/features/admin-widgets/components/InfoTip.rules.d.ts +72 -0
- package/dist/features/admin-widgets/components/InfoTip.rules.js +68 -0
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.d.ts +36 -0
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.d.ts +58 -0
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +66 -0
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.js +24 -0
- package/dist/features/admin-widgets/components/useDebouncedHtmlPreview.hooks.d.ts +23 -0
- package/dist/features/admin-widgets/components/useDebouncedHtmlPreview.hooks.js +28 -0
- package/dist/features/admin-widgets/index.d.ts +9 -0
- package/dist/features/admin-widgets/index.js +5 -0
- package/dist/features/execution/constants.js +11 -10
- package/dist/features/execution/http-adapter.d.ts +3 -1
- package/dist/features/execution/http-adapter.js +9 -4
- package/dist/features/execution/index.d.ts +1 -1
- package/dist/features/execution/index.js +1 -1
- package/dist/features/execution/react/components/ByokProviderForm.d.ts +18 -1
- package/dist/features/execution/react/components/ByokProviderForm.js +14 -16
- package/dist/features/execution/react/components/LocalCliAgentCard.d.ts +5 -1
- package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -87
- package/dist/features/execution/react/components/LocalCliAgentList.d.ts +4 -1
- package/dist/features/execution/react/components/LocalCliAgentList.js +13 -4
- package/dist/features/execution/react/components/LocalCliAgentSettings.d.ts +20 -0
- package/dist/features/execution/react/components/LocalCliAgentSettings.js +20 -0
- package/dist/features/execution/react/hooks/use-byok-field-validation.hooks.d.ts +24 -0
- package/dist/features/execution/react/hooks/use-byok-field-validation.hooks.js +27 -0
- package/dist/features/execution/react/hooks/useExecutionTab.js +10 -1
- package/dist/features/execution/react/hooks/useLocalCliAgentCard.d.ts +48 -0
- package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +110 -0
- package/dist/features/execution/react/hooks/useLocalCliAgentList.d.ts +19 -0
- package/dist/features/execution/react/hooks/useLocalCliAgentList.js +13 -0
- package/dist/features/execution/rules.d.ts +13 -0
- package/dist/features/execution/rules.js +14 -0
- package/dist/features/execution/types.d.ts +2 -0
- package/dist/features/html-editor/canvas-embed-placeholders.d.ts +13 -1
- package/dist/features/html-editor/canvas-embed-placeholders.js +61 -12
- package/dist/features/html-editor/dom.d.ts +5 -0
- package/dist/features/html-editor/dom.js +10 -0
- package/dist/features/html-editor/editor-blocks.d.ts +71 -0
- package/dist/features/html-editor/editor-blocks.js +185 -0
- package/dist/features/html-editor/editor-serialization.d.ts +24 -0
- package/dist/features/html-editor/editor-serialization.js +110 -0
- package/dist/features/html-editor/index.d.ts +5 -0
- package/dist/features/html-editor/index.js +3 -0
- package/dist/features/html-editor/protected-elements.d.ts +64 -0
- package/dist/features/html-editor/protected-elements.js +189 -0
- package/dist/features/html-editor/react/components/InteractiveHtmlEditor.d.ts +6 -4
- package/dist/features/html-editor/react/components/InteractiveHtmlEditor.js +3 -3
- package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.d.ts +9 -1
- package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +107 -196
- package/dist/features/html-editor/source-splice.d.ts +23 -0
- package/dist/features/html-editor/source-splice.js +45 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ar.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.bn.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.de.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.en.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.es.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.fa.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.fr.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.hi.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.hu.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.id.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.it.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ja.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ko.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.pl.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.pt-BR.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ru.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.th.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.tr.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.uk.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ur.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +4 -0
- package/dist/features/interactive-ui/interactive-ui.css +1 -1
- package/dist/features/interactive-ui/providers/recharts/bar-chart.js +1 -1
- package/dist/features/interactive-ui/providers/recharts/line-chart.js +1 -1
- package/dist/features/interactive-ui/providers/shadcn/button.d.ts +1 -1
- package/dist/features/mcp-ui/escape.js +2 -6
- package/dist/features/mcp-ui/surfaces/bridge.js +4 -2
- package/dist/features/mcp-ui/surfaces/document.d.ts +11 -4
- package/dist/features/mcp-ui/surfaces/document.js +25 -3
- package/dist/features/mcp-ui/surfaces/outcome.js +1 -1
- package/dist/features/media-providers/constants.d.ts +0 -7
- package/dist/features/media-providers/constants.js +7 -15
- package/dist/features/media-providers/react/components/MediaProvidersTab.js +3 -2
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +6 -3
- package/dist/features/memory/dependencies.d.ts +2 -0
- package/dist/features/memory/dependencies.js +9 -25
- package/dist/features/memory/index.d.ts +1 -0
- package/dist/features/memory/index.js +1 -0
- package/dist/features/memory/react/MemoryPorts.d.ts +17 -0
- package/dist/features/memory/react/MemoryPorts.js +15 -0
- package/dist/features/memory/react/hooks/useMemoryConfig.hooks.d.ts +2 -2
- package/dist/features/memory/react/hooks/useMemoryConfig.hooks.js +7 -7
- package/dist/features/memory/react/hooks/useMemoryConnectors.hooks.d.ts +2 -3
- package/dist/features/memory/react/hooks/useMemoryConnectors.hooks.js +4 -5
- package/dist/features/memory/react/hooks/useMemoryEntries.hooks.d.ts +2 -3
- package/dist/features/memory/react/hooks/useMemoryEntries.hooks.js +4 -5
- package/dist/features/memory/react/hooks/useMemoryExtractions.hooks.js +2 -2
- package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
- package/dist/features/panel-kit/fetch-query/adapter.react.js +2 -2
- package/dist/features/panel-kit/fetch-query/adapter.tanstack.d.ts +1 -1
- package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +35 -13
- package/dist/features/panel-kit/fetch-query/binding.react.d.ts +1 -1
- package/dist/features/panel-kit/fetch-query/environment.browser.d.ts +1 -1
- package/dist/features/panel-kit/fetch-query/index.d.ts +4 -2
- package/dist/features/panel-kit/fetch-query/tanstack.d.ts +4 -2
- package/dist/features/panel-kit/helpers/standing-draft-recovery-message.d.ts +3 -1
- package/dist/features/panel-kit/helpers/standing-draft-recovery-message.js +9 -2
- package/dist/features/settings/dialog/styles/settings-dialog.css +169 -21
- package/dist/features/tabbed-dialog/styles/tabbed-dialog.css +91 -0
- package/dist/react/components/RemixIcon.d.ts +1 -41
- package/dist/react/components/RemixIcon.js +3 -176
- package/dist/react/components/RemixIcon.stylesheet.d.ts +84 -0
- package/dist/react/components/RemixIcon.stylesheet.js +208 -0
- package/dist/react/components/remixicon-font-recovery.d.ts +55 -0
- package/dist/react/components/remixicon-font-recovery.js +110 -0
- package/dist/styles/admin.css +8 -2
- package/package.json +12 -7
- package/dist/features/panel-kit/fetch-query/cache.d.ts +0 -125
- package/dist/features/panel-kit/fetch-query/cache.js +0 -232
- package/dist/features/panel-kit/fetch-query/types.d.ts +0 -93
- package/dist/features/panel-kit/fetch-query/types.js +0 -2
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { agentHandleProps, agentSubHandle } from '@jini-ai/agentic';
|
|
3
|
+
import { useT } from '../../../i18n/index.js';
|
|
4
|
+
import { CUSTOM_MODEL_SENTINEL } from '../../constants.js';
|
|
5
|
+
import { AgentCliEnvFields } from './AgentCliEnvFields.js';
|
|
6
|
+
import { SearchableModelSelect } from './SearchableModelSelect.js';
|
|
7
|
+
/**
|
|
8
|
+
* The selected CLI's settings, stacked inside its own card.
|
|
9
|
+
*
|
|
10
|
+
* Owner, 2026-10-09: both the full-row card and the separate settings strip
|
|
11
|
+
* spread selection beyond its own column. Keep full-width controls and a
|
|
12
|
+
* visible provenance sentence inside the card so only that card grows taller.
|
|
13
|
+
*/
|
|
14
|
+
export function LocalCliAgentSettings({ agent, config, card, cliEnvFields, onModelChange, onEnvChange, agentHandle, }) {
|
|
15
|
+
const t = useT();
|
|
16
|
+
const { reasoning, source, sourceLabel, sourceHint, result, repair, repairField } = card;
|
|
17
|
+
const { hasModels, hasReasoning, reasoningOptions, reasoningValue, reasoningDisabled, allowCustomModel, customActive, modelChoices, selectValue, customModelInputValue, } = reasoning;
|
|
18
|
+
return (_jsxs("div", { className: "jini-agent-settings", role: "group", "aria-label": agent.label, "data-testid": `jini-agent-settings-${agent.id}`, children: [_jsxs("div", { className: "jini-agent-settings-row", children: [hasModels ? (_jsxs("div", { className: "jini-agent-settings-field jini-agent-settings-model", children: [_jsxs("div", { className: "jini-agent-settings-label", children: [_jsx("span", { className: "jini-field-label", children: t('Model') }), sourceLabel ? (_jsx("span", { className: `jini-agent-model-source ${source}`, children: sourceLabel })) : null] }), _jsx(SearchableModelSelect, { className: "jini-input jini-agent-model-select", ariaLabel: t('Model'), searchPlaceholder: t('Search models'), testId: `jini-agent-model-${agent.id}`, searchInputTestId: `jini-agent-model-search-${agent.id}`, ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'model' }) } : {}), value: selectValue, models: modelChoices, additionalOptions: allowCustomModel ? [{ value: CUSTOM_MODEL_SENTINEL, label: t('Custom…') }] : undefined, onChange: card.selectModel }), sourceHint ? _jsx("p", { className: "jini-field-hint", children: sourceHint }) : null] })) : null, customActive ? (_jsxs("label", { className: "jini-agent-settings-field", children: [_jsx("span", { className: "jini-field-label", children: t('Custom model id') }), _jsx("input", { className: "jini-input jini-agent-settings-custom", type: "text", "data-testid": `jini-agent-model-custom-${agent.id}`, value: customModelInputValue, placeholder: t('e.g. my-fine-tuned-model'), spellCheck: false, onChange: (event) => onModelChange(agent.id, event.target.value.trim()), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'custom-model', role: 'field', label: t('Custom model id') }) }) })] })) : null, hasReasoning ? (_jsxs("label", { className: "jini-agent-settings-field", children: [_jsx("span", { className: "jini-field-label", children: t('Reasoning effort') }), _jsx("select", { className: "jini-input jini-agent-settings-reasoning", "data-testid": `jini-agent-reasoning-${agent.id}`, value: reasoningValue, disabled: reasoningDisabled, onChange: (event) => card.selectReasoning(event.target.value), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'reasoning', role: 'field', label: t('Reasoning effort') }) }), children: reasoningOptions.map((option) => (_jsx("option", { value: option.id, children: option.label }, option.id))) })] })) : null] }), result ? (_jsx("p", { className: 'jini-agent-card-test-result is-' + result.status, role: result.status === 'error' ? 'alert' : 'status', children: result.message ?? (result.status === 'ok' ? t('Agent is ready.') : t('Agent check failed')) })) : null, repair && repairField ? (_jsxs("div", { className: "jini-agent-card-path-repair", children: [_jsx("span", { className: "jini-field-hint", children: t('A custom path override did not work, so this used the automatically detected binary instead.') }), _jsxs("div", { className: "jini-agent-card-path-repair-actions", children: [repair.canUseDetected ? (_jsx("button", { type: "button", className: "jini-btn", "data-testid": `jini-agent-path-repair-use-${agent.id}`, onClick: () => onEnvChange(agent.id, repairField.envKey, repair.detectedPath), children: t('Use detected path') })) : null, _jsx("button", { type: "button", className: "jini-btn jini-btn-ghost", "data-testid": `jini-agent-path-repair-clear-${agent.id}`, onClick: () => onEnvChange(agent.id, repairField.envKey, ''), children: t('Clear custom path') })] })] })) : null, _jsx(AgentCliEnvFields, { agentId: agent.id, fields: cliEnvFields, config: config, onChange: onEnvChange })] }));
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=LocalCliAgentSettings.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ByokConfig, ProviderPreset } from '../../types.js';
|
|
2
|
+
type Field = 'apiKey' | 'baseUrl' | 'model';
|
|
3
|
+
/** Keeps required-field gates immediate while optionally waiting for blur to paint errors.
|
|
4
|
+
* Stored keys satisfy only the key requirement; touching one provider never marks another.
|
|
5
|
+
* @complexity Time/space: O(1), bounded by the three required fields.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useByokFieldValidation(required: {
|
|
8
|
+
config: ByokConfig;
|
|
9
|
+
preset: ProviderPreset | null;
|
|
10
|
+
apiKeyStoredExternally: boolean;
|
|
11
|
+
}, optional?: {
|
|
12
|
+
deferMissingFeedback?: boolean;
|
|
13
|
+
}): {
|
|
14
|
+
missing: Set<import("../../types.js").ByokRequiredField>;
|
|
15
|
+
baseUrlInvalid: boolean;
|
|
16
|
+
isMissing: ({ field }: {
|
|
17
|
+
field: Field;
|
|
18
|
+
}, _options?: Record<string, never>) => boolean;
|
|
19
|
+
markTouched: ({ field }: {
|
|
20
|
+
field: Field;
|
|
21
|
+
}, _options?: Record<string, never>) => void;
|
|
22
|
+
};
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=use-byok-field-validation.hooks.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { isBaseUrlInvalid, missingRequiredFields } from '../../rules.js';
|
|
3
|
+
/** Keeps required-field gates immediate while optionally waiting for blur to paint errors.
|
|
4
|
+
* Stored keys satisfy only the key requirement; touching one provider never marks another.
|
|
5
|
+
* @complexity Time/space: O(1), bounded by the three required fields.
|
|
6
|
+
*/
|
|
7
|
+
export function useByokFieldValidation(required, optional = {}) {
|
|
8
|
+
const { config, preset, apiKeyStoredExternally } = required;
|
|
9
|
+
const [touched, setTouched] = useState({ providerId: config.providerId, fields: new Set() });
|
|
10
|
+
const missing = new Set(missingRequiredFields(config, preset));
|
|
11
|
+
if (apiKeyStoredExternally)
|
|
12
|
+
missing.delete('apiKey');
|
|
13
|
+
const baseUrlInvalid = isBaseUrlInvalid(config);
|
|
14
|
+
return {
|
|
15
|
+
missing,
|
|
16
|
+
baseUrlInvalid,
|
|
17
|
+
isMissing: ({ field }, _options = {}) => (missing.has(field) || (field === 'baseUrl' && baseUrlInvalid)) &&
|
|
18
|
+
(!optional.deferMissingFeedback || (touched.providerId === config.providerId && touched.fields.has(field))),
|
|
19
|
+
markTouched: ({ field }, _options = {}) => {
|
|
20
|
+
setTouched(current => ({
|
|
21
|
+
providerId: config.providerId,
|
|
22
|
+
fields: new Set([...(current.providerId === config.providerId ? current.fields : []), field]),
|
|
23
|
+
}));
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=use-byok-field-validation.hooks.js.map
|
|
@@ -37,14 +37,17 @@ export function useExecutionTab({ port, autoDetect = true, describeProbeError =
|
|
|
37
37
|
/** The host's describer as of the latest render — see `UseExecutionTabOptions.describeProbeError`. */
|
|
38
38
|
const describeProbeErrorRef = useRef(describeProbeError);
|
|
39
39
|
describeProbeErrorRef.current = describeProbeError;
|
|
40
|
+
const detectionTimer = useRef(undefined);
|
|
40
41
|
const alive = useRef(true);
|
|
41
42
|
useEffect(() => {
|
|
42
43
|
alive.current = true;
|
|
43
44
|
return () => {
|
|
44
45
|
alive.current = false;
|
|
46
|
+
clearTimeout(detectionTimer.current);
|
|
45
47
|
};
|
|
46
48
|
}, []);
|
|
47
49
|
const runDetection = useCallback((detect) => {
|
|
50
|
+
clearTimeout(detectionTimer.current);
|
|
48
51
|
const ticket = ++detectionTicket.current;
|
|
49
52
|
const isCurrent = () => alive.current && detectionTicket.current === ticket;
|
|
50
53
|
setScan({ status: 'scanning' });
|
|
@@ -54,12 +57,18 @@ export function useExecutionTab({ port, autoDetect = true, describeProbeError =
|
|
|
54
57
|
const sorted = sortDetectedAgents(found);
|
|
55
58
|
setAgents(sorted);
|
|
56
59
|
setScan({ status: 'ok', count: sorted.filter((agent) => agent.installed).length });
|
|
60
|
+
if (found.some(agent => agent.discoveryPending)) {
|
|
61
|
+
detectionTimer.current = setTimeout(() => {
|
|
62
|
+
if (isCurrent())
|
|
63
|
+
runDetection(() => port.detectLocalAgents());
|
|
64
|
+
}, 1000);
|
|
65
|
+
}
|
|
57
66
|
}, (error) => {
|
|
58
67
|
if (!isCurrent())
|
|
59
68
|
return;
|
|
60
69
|
setScan({ status: 'error', message: describeErrorMessage(error) });
|
|
61
70
|
});
|
|
62
|
-
}, []);
|
|
71
|
+
}, [port]);
|
|
63
72
|
useEffect(() => {
|
|
64
73
|
if (!autoDetect)
|
|
65
74
|
return;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { agentExecutableRepairState } from '../../rules.js';
|
|
2
|
+
import type { AgentCliEnvFieldSpec, AgentTestState, DetectedAgent, LocalCliConfig } from '../../types.js';
|
|
3
|
+
import { type UseReasoningControlResult } from './useReasoningControl.js';
|
|
4
|
+
type Translate = (key: string) => string;
|
|
5
|
+
export interface UseLocalCliAgentCardInput {
|
|
6
|
+
agent: DetectedAgent;
|
|
7
|
+
config: LocalCliConfig;
|
|
8
|
+
agentTest: AgentTestState;
|
|
9
|
+
cliEnvFields: readonly AgentCliEnvFieldSpec[];
|
|
10
|
+
onModelChange: (agentId: string, model: string) => void;
|
|
11
|
+
onReasoningChange: (agentId: string, reasoning: string) => void;
|
|
12
|
+
onRescan?: (() => void) | undefined;
|
|
13
|
+
/** Only its presence matters here: a Test button alone is enough to show the settings strip. */
|
|
14
|
+
onTest?: ((agent: DetectedAgent) => void) | undefined;
|
|
15
|
+
t: Translate;
|
|
16
|
+
}
|
|
17
|
+
export interface UseLocalCliAgentCardResult {
|
|
18
|
+
reasoning: UseReasoningControlResult;
|
|
19
|
+
/** Whether the selected card's settings strip has anything to show; without this it rendered
|
|
20
|
+
* as an empty bordered box for a CLI with no models, effort, Test or env fields. */
|
|
21
|
+
hasSettings: boolean;
|
|
22
|
+
/** `agent.modelsSource`, passed through for the badge's modifier class. */
|
|
23
|
+
source: DetectedAgent['modelsSource'];
|
|
24
|
+
sourceLabel: string;
|
|
25
|
+
sourceHint: string;
|
|
26
|
+
testing: boolean;
|
|
27
|
+
result: Extract<AgentTestState, {
|
|
28
|
+
status: 'ok' | 'error';
|
|
29
|
+
}> | null;
|
|
30
|
+
repair: ReturnType<typeof agentExecutableRepairState>;
|
|
31
|
+
repairField: AgentCliEnvFieldSpec | null;
|
|
32
|
+
diagnosticHandlers: {
|
|
33
|
+
onRescan: (() => void) | undefined;
|
|
34
|
+
onOpenInstall: (() => void) | undefined;
|
|
35
|
+
onOpenDocs: (() => void) | undefined;
|
|
36
|
+
};
|
|
37
|
+
/** The model picker's change handler, including its "Custom…" sentinel. */
|
|
38
|
+
selectModel: (nextValue: string) => void;
|
|
39
|
+
/** The reasoning picker's change handler, including the model-suffix shape. */
|
|
40
|
+
selectReasoning: (nextValue: string) => void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Everything `LocalCliAgentCard` and its settings strip derive from one agent, kept out of the
|
|
44
|
+
* `.tsx` so both render from the same values.
|
|
45
|
+
*/
|
|
46
|
+
export declare function useLocalCliAgentCard({ agent, config, agentTest, cliEnvFields, onModelChange, onReasoningChange, onRescan, onTest, t, }: UseLocalCliAgentCardInput): UseLocalCliAgentCardResult;
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=useLocalCliAgentCard.d.ts.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { CUSTOM_MODEL_SENTINEL } from '../../constants.js';
|
|
3
|
+
import { agentExecutableRepairState, binPathEnvField, cliEnvFieldsForAgent } from '../../rules.js';
|
|
4
|
+
import { useReasoningControl } from './useReasoningControl.js';
|
|
5
|
+
/**
|
|
6
|
+
* Everything `LocalCliAgentCard` and its settings strip derive from one agent, kept out of the
|
|
7
|
+
* `.tsx` so both render from the same values.
|
|
8
|
+
*/
|
|
9
|
+
export function useLocalCliAgentCard({ agent, config, agentTest, cliEnvFields, onModelChange, onReasoningChange, onRescan, onTest, t, }) {
|
|
10
|
+
// Whether the operator explicitly opened the free-text "Custom" entry via
|
|
11
|
+
// the model picker's own "Custom…" option — distinct from "the saved model
|
|
12
|
+
// simply isn't in the known list" (`shouldShowCustomModelInput` covers
|
|
13
|
+
// both, but only THIS one needs to be remembered as a UI-local toggle,
|
|
14
|
+
// since it is not itself persisted config: see `LocalCliConfig`, which has
|
|
15
|
+
// nowhere for "the operator is mid-typing a custom id" to live). Scoped to
|
|
16
|
+
// this card instance, so switching to a different agent and back does not
|
|
17
|
+
// leak one agent's custom-mode toggle onto another.
|
|
18
|
+
const [explicitCustomMode, setExplicitCustomMode] = useState(false);
|
|
19
|
+
const reasoning = useReasoningControl({
|
|
20
|
+
agent,
|
|
21
|
+
config,
|
|
22
|
+
explicitCustomMode,
|
|
23
|
+
onReasoningChange: (next) => onReasoningChange(agent.id, next),
|
|
24
|
+
});
|
|
25
|
+
// `modelsSource` is optional in the contract, so an absent value means the
|
|
26
|
+
// host did not say where the list came from — NOT that it came from a
|
|
27
|
+
// built-in fallback. Defaulting to 'fallback' rendered "Built-in list" over
|
|
28
|
+
// a list that may well be live, and told the operator to Rescan to fix a
|
|
29
|
+
// problem they do not have. Unknown provenance shows no badge and no claim.
|
|
30
|
+
const source = agent.modelsSource;
|
|
31
|
+
const sourceLabel = source === 'live' ? t('Live from CLI') : source === 'fallback' ? t('Built-in list') : '';
|
|
32
|
+
const sourceHint = source === 'live'
|
|
33
|
+
? t("Model list comes from this CLI. Default uses the CLI's own config.")
|
|
34
|
+
: source === 'fallback'
|
|
35
|
+
? t('Showing built-in defaults. Click Rescan to pull live models from the CLI.')
|
|
36
|
+
: '';
|
|
37
|
+
// The test result belongs to whichever agent was probed, so a stale verdict
|
|
38
|
+
// from a previously-tested CLI never renders under this one.
|
|
39
|
+
const testing = agentTest.status === 'testing' && agentTest.agentId === agent.id;
|
|
40
|
+
const result = (agentTest.status === 'ok' || agentTest.status === 'error') && agentTest.agentId === agent.id
|
|
41
|
+
? agentTest
|
|
42
|
+
: null;
|
|
43
|
+
// The "use the binary that actually ran" / "clear the override" repair
|
|
44
|
+
// affordance — only meaningful when a successful test silently fell back
|
|
45
|
+
// off a configured override, AND this agent's catalog names which field
|
|
46
|
+
// that override lives in. See `agentExecutableRepairState`'s doc.
|
|
47
|
+
//
|
|
48
|
+
// `AgentTestState`'s 'ok' variant spells success as `status: 'ok'`, not an
|
|
49
|
+
// `ok: true` field — `agentExecutableRepairState` takes the latter (it
|
|
50
|
+
// mirrors `ExecutionPort.testAgent`'s raw resolved shape, one layer below
|
|
51
|
+
// this state union), so the two must be translated explicitly here rather
|
|
52
|
+
// than passing `result` straight through.
|
|
53
|
+
const repair = result?.status === 'ok'
|
|
54
|
+
? agentExecutableRepairState({
|
|
55
|
+
ok: true,
|
|
56
|
+
usedExecutableSource: result.usedExecutableSource,
|
|
57
|
+
detectedExecutablePath: result.detectedExecutablePath,
|
|
58
|
+
})
|
|
59
|
+
: null;
|
|
60
|
+
const repairField = repair ? binPathEnvField(cliEnvFields, agent.id) : null;
|
|
61
|
+
const openUrl = (url) => {
|
|
62
|
+
if (!url)
|
|
63
|
+
return;
|
|
64
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
65
|
+
};
|
|
66
|
+
const diagnosticHandlers = {
|
|
67
|
+
onRescan,
|
|
68
|
+
onOpenInstall: agent.installUrl ? () => openUrl(agent.installUrl) : undefined,
|
|
69
|
+
onOpenDocs: agent.docsUrl ? () => openUrl(agent.docsUrl) : undefined,
|
|
70
|
+
};
|
|
71
|
+
const selectModel = (nextValue) => {
|
|
72
|
+
if (nextValue === CUSTOM_MODEL_SENTINEL) {
|
|
73
|
+
setExplicitCustomMode(true);
|
|
74
|
+
onModelChange(agent.id, '');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
setExplicitCustomMode(false);
|
|
78
|
+
onModelChange(agent.id, reasoning.resolveNextModelId(nextValue));
|
|
79
|
+
};
|
|
80
|
+
const selectReasoning = (nextValue) => {
|
|
81
|
+
// A model-suffix agent has no reasoning value of its own to record: the choice
|
|
82
|
+
// is which model id runs, so it goes back out through `onModelChange`.
|
|
83
|
+
const suffixModelId = reasoning.resolveReasoningModelId(nextValue);
|
|
84
|
+
if (suffixModelId !== null) {
|
|
85
|
+
onModelChange(agent.id, suffixModelId);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
onReasoningChange(agent.id, nextValue);
|
|
89
|
+
};
|
|
90
|
+
const hasSettings = reasoning.hasModels ||
|
|
91
|
+
reasoning.hasReasoning ||
|
|
92
|
+
Boolean(onTest) ||
|
|
93
|
+
result !== null ||
|
|
94
|
+
cliEnvFieldsForAgent(cliEnvFields, agent.id).length > 0;
|
|
95
|
+
return {
|
|
96
|
+
reasoning,
|
|
97
|
+
hasSettings,
|
|
98
|
+
source,
|
|
99
|
+
sourceLabel,
|
|
100
|
+
sourceHint,
|
|
101
|
+
testing,
|
|
102
|
+
result,
|
|
103
|
+
repair,
|
|
104
|
+
repairField,
|
|
105
|
+
diagnosticHandlers,
|
|
106
|
+
selectModel,
|
|
107
|
+
selectReasoning,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=useLocalCliAgentCard.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DetectedAgent } from '../../types.js';
|
|
2
|
+
export interface UseLocalCliAgentListResult {
|
|
3
|
+
/** Installed CLIs plus the current pick: always rendered. */
|
|
4
|
+
visible: DetectedAgent[];
|
|
5
|
+
/** Not-installed CLIs: rendered only while `showCollapsed`. */
|
|
6
|
+
collapsed: DetectedAgent[];
|
|
7
|
+
showCollapsed: boolean;
|
|
8
|
+
toggleCollapsed: () => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Disclosure state for the CLI grid's not-installed tail. Collapsed by default: those cards
|
|
12
|
+
* cannot be picked until installed, and a dozen of them pushed every setting below the grid
|
|
13
|
+
* off-screen. See `partitionAgentsByInstalled` for what stays visible.
|
|
14
|
+
*/
|
|
15
|
+
export declare function useLocalCliAgentList({ agents, selectedAgentId }: {
|
|
16
|
+
agents: readonly DetectedAgent[];
|
|
17
|
+
selectedAgentId: string | null;
|
|
18
|
+
}): UseLocalCliAgentListResult;
|
|
19
|
+
//# sourceMappingURL=useLocalCliAgentList.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { partitionAgentsByInstalled } from '../../rules.js';
|
|
3
|
+
/**
|
|
4
|
+
* Disclosure state for the CLI grid's not-installed tail. Collapsed by default: those cards
|
|
5
|
+
* cannot be picked until installed, and a dozen of them pushed every setting below the grid
|
|
6
|
+
* off-screen. See `partitionAgentsByInstalled` for what stays visible.
|
|
7
|
+
*/
|
|
8
|
+
export function useLocalCliAgentList({ agents, selectedAgentId }) {
|
|
9
|
+
const [showCollapsed, setShowCollapsed] = useState(false);
|
|
10
|
+
const { visible, collapsed } = partitionAgentsByInstalled(agents, selectedAgentId);
|
|
11
|
+
return { visible, collapsed, showCollapsed, toggleCollapsed: () => setShowCollapsed((open) => !open) };
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=useLocalCliAgentList.js.map
|
|
@@ -125,6 +125,19 @@ export declare function sortDetectedAgents<T extends {
|
|
|
125
125
|
installed: boolean;
|
|
126
126
|
label: string;
|
|
127
127
|
}>(agents: readonly T[]): readonly T[];
|
|
128
|
+
/**
|
|
129
|
+
* Splits the CLI grid into what to show up front and what sits behind the "Show N not installed"
|
|
130
|
+
* toggle. Installed CLIs are the ones an operator can pick, so a long tail of uninstalled cards
|
|
131
|
+
* buried every setting below the grid. The selected agent always stays visible even when it is
|
|
132
|
+
* not installed (a stale pick after an uninstall), so the current choice is never hidden.
|
|
133
|
+
*/
|
|
134
|
+
export declare function partitionAgentsByInstalled<T extends {
|
|
135
|
+
id: string;
|
|
136
|
+
installed: boolean;
|
|
137
|
+
}>(agents: readonly T[], selectedAgentId: string | null | undefined): {
|
|
138
|
+
visible: T[];
|
|
139
|
+
collapsed: T[];
|
|
140
|
+
};
|
|
128
141
|
/**
|
|
129
142
|
* Strips a redundant trailing agent name from a CLI's `--version` output, so
|
|
130
143
|
* "1.2.3 (Claude Code)" renders as "1.2.3" beside a card already titled
|
|
@@ -282,6 +282,20 @@ export function sortDetectedAgents(agents) {
|
|
|
282
282
|
return a.label.localeCompare(b.label);
|
|
283
283
|
});
|
|
284
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* Splits the CLI grid into what to show up front and what sits behind the "Show N not installed"
|
|
287
|
+
* toggle. Installed CLIs are the ones an operator can pick, so a long tail of uninstalled cards
|
|
288
|
+
* buried every setting below the grid. The selected agent always stays visible even when it is
|
|
289
|
+
* not installed (a stale pick after an uninstall), so the current choice is never hidden.
|
|
290
|
+
*/
|
|
291
|
+
export function partitionAgentsByInstalled(agents, selectedAgentId) {
|
|
292
|
+
const visible = [];
|
|
293
|
+
const collapsed = [];
|
|
294
|
+
for (const agent of agents) {
|
|
295
|
+
(agent.installed || agent.id === selectedAgentId ? visible : collapsed).push(agent);
|
|
296
|
+
}
|
|
297
|
+
return { visible, collapsed };
|
|
298
|
+
}
|
|
285
299
|
/**
|
|
286
300
|
* Strips a redundant trailing agent name from a CLI's `--version` output, so
|
|
287
301
|
* "1.2.3 (Claude Code)" renders as "1.2.3" beside a card already titled
|
|
@@ -182,6 +182,8 @@ export type AgentSupportsCustomModel = boolean;
|
|
|
182
182
|
* the route boundary throws away data the card is built to render.
|
|
183
183
|
*/
|
|
184
184
|
export interface DetectedAgent {
|
|
185
|
+
/** Cached inventory is usable now; retry a normal read until background discovery settles. */
|
|
186
|
+
discoveryPending?: boolean;
|
|
185
187
|
id: string;
|
|
186
188
|
label: string;
|
|
187
189
|
installed: boolean;
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
* Re-entrancy (defensive, mirroring `canvas-content-wrapper.ts`'s own `WRAPPER_ROOT_MARKER` guard): a
|
|
37
37
|
* marker already sitting inside a placeholder card (`el.closest('[' + CARD_ROOT_MARKER + ']')`) is
|
|
38
38
|
* skipped, so a second call — or a caller's own `load` handler firing twice — cannot nest a second card
|
|
39
|
-
* around an already-decorated marker.
|
|
39
|
+
* around an already-decorated marker. Only a runtime card's direct marker is described again, to
|
|
40
|
+
* refresh its footprint after theme styles settle; card chrome is always skipped.
|
|
40
41
|
*/
|
|
41
42
|
/** What to show on one embed's placeholder card — supplied per-element by the host's own `describe`
|
|
42
43
|
* callback, since only the host knows the marker convention (attribute name, JSON shape, and how to
|
|
@@ -47,7 +48,18 @@ export interface CanvasEmbedPlaceholderDescriptor {
|
|
|
47
48
|
/** Human-identifiable target, shown under the heading — e.g. a name, or a truncated id, or a note
|
|
48
49
|
* that none was set. Never fetched by this module; the host decides what's cheap enough to show. */
|
|
49
50
|
identityLabel: string;
|
|
51
|
+
/** Runtime blocks use the same card with a bounded view-only footprint and locked guidance. */
|
|
52
|
+
presentation?: 'embed' | 'runtime';
|
|
53
|
+
guidanceLabel?: string;
|
|
54
|
+
inline?: boolean | undefined;
|
|
55
|
+
declaredHeight?: number | undefined;
|
|
50
56
|
}
|
|
57
|
+
/** Finite positive declared/native geometry wins, otherwise use the bounded runtime fallback. */
|
|
58
|
+
export declare function editorBlockHeight({ declaredHeight, nativeHeight, inline }: {
|
|
59
|
+
declaredHeight?: number | undefined;
|
|
60
|
+
nativeHeight?: number | undefined;
|
|
61
|
+
inline?: boolean | undefined;
|
|
62
|
+
}, _options?: object): number;
|
|
51
63
|
/**
|
|
52
64
|
* Scans every element under `body` once and, for each one `describe` recognizes (returns a descriptor
|
|
53
65
|
* for), replaces its visible slot with an explicit placeholder card — see this file's header for the
|
|
@@ -36,12 +36,20 @@
|
|
|
36
36
|
* Re-entrancy (defensive, mirroring `canvas-content-wrapper.ts`'s own `WRAPPER_ROOT_MARKER` guard): a
|
|
37
37
|
* marker already sitting inside a placeholder card (`el.closest('[' + CARD_ROOT_MARKER + ']')`) is
|
|
38
38
|
* skipped, so a second call — or a caller's own `load` handler firing twice — cannot nest a second card
|
|
39
|
-
* around an already-decorated marker.
|
|
39
|
+
* around an already-decorated marker. Only a runtime card's direct marker is described again, to
|
|
40
|
+
* refresh its footprint after theme styles settle; card chrome is always skipped.
|
|
40
41
|
*/
|
|
42
|
+
/** Finite positive declared/native geometry wins, otherwise use the bounded runtime fallback. */
|
|
43
|
+
export function editorBlockHeight({ declaredHeight, nativeHeight, inline }, _options = {}) {
|
|
44
|
+
const height = [declaredHeight, nativeHeight].find(value => typeof value === 'number' && Number.isFinite(value) && value > 0);
|
|
45
|
+
return Math.max(inline ? 48 : 96, Math.min(inline ? 160 : 960, height ?? (inline ? 64 : 280)));
|
|
46
|
+
}
|
|
41
47
|
/** Marks a placeholder card's own root, so a later pass (this function called again, or a re-entrant
|
|
42
48
|
* `load`) can recognize an already-decorated marker via `el.closest` and skip it — see this file's
|
|
43
49
|
* header. */
|
|
44
50
|
const CARD_ROOT_MARKER = 'data-jini-embed-placeholder-root';
|
|
51
|
+
/** Canvas-only flag permitting footprint refreshes without describing embed cards or chrome again. */
|
|
52
|
+
const CARD_RUNTIME_MARKER = 'data-jini-embed-placeholder-runtime';
|
|
45
53
|
/** Flags every child the card itself builds (icon, text), so the one unflagged child is always the
|
|
46
54
|
* marker. {@link OVERLAY_RULE} and the stale-card check in `removeStaleCards` both rely on it. */
|
|
47
55
|
const CARD_CHROME_MARKER = 'data-jini-embed-placeholder-chrome';
|
|
@@ -110,20 +118,22 @@ function buildPlaceholderIcon(doc) {
|
|
|
110
118
|
* `textContent`, never string-concatenated markup, so a marker's own config values (which flow into
|
|
111
119
|
* `descriptor.identityLabel`) can never be interpreted as HTML. */
|
|
112
120
|
function buildPlaceholderCard(doc, descriptor) {
|
|
113
|
-
const card = doc.createElement('div');
|
|
121
|
+
const card = doc.createElement(descriptor.inline ? 'span' : 'div');
|
|
114
122
|
card.setAttribute(CARD_ROOT_MARKER, '');
|
|
123
|
+
if (descriptor.presentation === 'runtime')
|
|
124
|
+
card.setAttribute(CARD_RUNTIME_MARKER, '');
|
|
115
125
|
card.setAttribute('style', CARD_STYLE);
|
|
116
126
|
const text = doc.createElement('div');
|
|
117
127
|
text.setAttribute(CARD_CHROME_MARKER, '');
|
|
118
128
|
const heading = doc.createElement('div');
|
|
119
129
|
heading.setAttribute('style', 'font-weight:600;');
|
|
120
|
-
heading.textContent = `Placeholder — ${descriptor.kindLabel}`;
|
|
130
|
+
heading.textContent = descriptor.presentation === 'runtime' ? descriptor.kindLabel : `Placeholder — ${descriptor.kindLabel}`;
|
|
121
131
|
const identity = doc.createElement('div');
|
|
122
132
|
identity.setAttribute('style', 'opacity:.8;margin-top:2px;');
|
|
123
133
|
identity.textContent = descriptor.identityLabel;
|
|
124
134
|
const note = doc.createElement('div');
|
|
125
135
|
note.setAttribute('style', 'opacity:.6;margin-top:4px;font-size:11px;');
|
|
126
|
-
note.textContent = 'Shown here for editing only — the real content renders on the published page.';
|
|
136
|
+
note.textContent = descriptor.guidanceLabel ?? 'Shown here for editing only — the real content renders on the published page.';
|
|
127
137
|
text.append(heading, identity, note);
|
|
128
138
|
const icon = buildPlaceholderIcon(doc);
|
|
129
139
|
icon.setAttribute(CARD_CHROME_MARKER, '');
|
|
@@ -156,9 +166,10 @@ function buildPlaceholderCard(doc, descriptor) {
|
|
|
156
166
|
* behavior every existing caller of this module had before this function existed.
|
|
157
167
|
*/
|
|
158
168
|
function applyResolvedMaxWidth(marker, card) {
|
|
159
|
-
|
|
169
|
+
const win = marker.ownerDocument.defaultView;
|
|
170
|
+
if (!win)
|
|
160
171
|
return;
|
|
161
|
-
const maxWidth = getComputedStyle(marker).maxWidth;
|
|
172
|
+
const maxWidth = win.getComputedStyle(marker).maxWidth;
|
|
162
173
|
if (maxWidth && maxWidth !== 'none')
|
|
163
174
|
card.style.setProperty('max-width', maxWidth);
|
|
164
175
|
}
|
|
@@ -210,15 +221,20 @@ export function applyCanvasEmbedPlaceholders(body, describe) {
|
|
|
210
221
|
// `body.childNodes` for the same reason.
|
|
211
222
|
const candidates = Array.from(body.querySelectorAll('*'));
|
|
212
223
|
for (const el of candidates) {
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
224
|
+
// Skip existing embed cards and all chrome before calling the host. Runtime cards still
|
|
225
|
+
// re-describe their direct marker so theme changes can refresh its bounded footprint.
|
|
226
|
+
const existingCard = el.closest(`[${CARD_ROOT_MARKER}]`);
|
|
227
|
+
if (existingCard && (!existingCard.hasAttribute(CARD_RUNTIME_MARKER)
|
|
228
|
+
|| el.parentElement !== existingCard || el.hasAttribute(CARD_CHROME_MARKER)))
|
|
218
229
|
continue;
|
|
219
230
|
const descriptor = describe(el);
|
|
220
231
|
if (!descriptor)
|
|
221
232
|
continue;
|
|
233
|
+
if (existingCard) {
|
|
234
|
+
if (descriptor.presentation === 'runtime')
|
|
235
|
+
applyRuntimeFootprint(el, existingCard, descriptor);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
222
238
|
const parent = el.parentNode;
|
|
223
239
|
if (!parent)
|
|
224
240
|
continue; // Unreachable for a live `body` descendant; defensive only.
|
|
@@ -229,12 +245,45 @@ export function applyCanvasEmbedPlaceholders(body, describe) {
|
|
|
229
245
|
// author set that `CARD_STYLE` doesn't touch (like `max-width`) still applies unopposed. Guarded
|
|
230
246
|
// on presence so a marker with no `style` attribute at all is a no-op, not a leading `;`.
|
|
231
247
|
const authoredStyle = el.getAttribute('style');
|
|
232
|
-
if (authoredStyle)
|
|
248
|
+
if (authoredStyle && descriptor.presentation !== 'runtime')
|
|
233
249
|
card.setAttribute('style', `${authoredStyle};${card.getAttribute('style')}`);
|
|
234
250
|
applyResolvedMaxWidth(el, card);
|
|
251
|
+
if (descriptor.presentation === 'runtime')
|
|
252
|
+
applyRuntimeFootprint(el, card, descriptor);
|
|
235
253
|
parent.insertBefore(card, el);
|
|
236
254
|
card.appendChild(el); // Moves the SAME node (auto-detaches from `parent`) — never cloned.
|
|
237
255
|
ensureOverlayRule(doc);
|
|
238
256
|
}
|
|
239
257
|
}
|
|
258
|
+
/** Geometry belongs to canvas DOM only; arbitrary transforms/min-height cannot defeat the clamp. */
|
|
259
|
+
function applyRuntimeFootprint(el, card, descriptor) {
|
|
260
|
+
// Re-measure the authored shell after theme links settle, without our selection overlay's
|
|
261
|
+
// !important auto-height masking its declarations. No component/model styles are changed.
|
|
262
|
+
const overlay = el.ownerDocument.head.querySelector(`style[${OVERLAY_STYLE_MARKER}]`);
|
|
263
|
+
const sheet = overlay?.sheet;
|
|
264
|
+
const wasDisabled = sheet?.disabled;
|
|
265
|
+
let resolved;
|
|
266
|
+
try {
|
|
267
|
+
if (sheet)
|
|
268
|
+
sheet.disabled = true;
|
|
269
|
+
resolved = el.ownerDocument.defaultView?.getComputedStyle(el).height;
|
|
270
|
+
}
|
|
271
|
+
finally {
|
|
272
|
+
if (sheet)
|
|
273
|
+
sheet.disabled = wasDisabled ?? false;
|
|
274
|
+
}
|
|
275
|
+
const native = el.getAttribute('height');
|
|
276
|
+
const declared = descriptor.declaredHeight ?? (resolved?.endsWith('px') ? Number.parseFloat(resolved) : undefined);
|
|
277
|
+
card.style.height = `${editorBlockHeight({ declaredHeight: declared, nativeHeight: native ? Number(native) : undefined, inline: descriptor.inline }, {})}px`;
|
|
278
|
+
card.style.minHeight = '0';
|
|
279
|
+
card.style.maxHeight = descriptor.inline ? '160px' : '960px';
|
|
280
|
+
card.style.overflow = 'hidden';
|
|
281
|
+
if (descriptor.inline) {
|
|
282
|
+
card.style.display = 'inline-flex';
|
|
283
|
+
card.style.verticalAlign = 'middle';
|
|
284
|
+
}
|
|
285
|
+
// The marker is the selectable hit surface, never an authored layout/transform or media view.
|
|
286
|
+
el.style.setProperty('transform', 'none', 'important');
|
|
287
|
+
el.style.setProperty('overflow', 'hidden', 'important');
|
|
288
|
+
}
|
|
240
289
|
//# sourceMappingURL=canvas-embed-placeholders.js.map
|
|
@@ -12,4 +12,9 @@
|
|
|
12
12
|
* that checks attributes should use this instead of `el.hasAttribute` directly.
|
|
13
13
|
*/
|
|
14
14
|
export declare function hasAttributeOnAnyNodeShape(el: Element, attr: string): boolean;
|
|
15
|
+
/** DOM-free compatibility view for the existing host attribute describer/predicate seam. */
|
|
16
|
+
export declare function sourceElementAttributeView({ tagName, attributes }: {
|
|
17
|
+
tagName: string;
|
|
18
|
+
attributes: Readonly<Record<string, string>>;
|
|
19
|
+
}, _options?: object): Element;
|
|
15
20
|
//# sourceMappingURL=dom.d.ts.map
|
|
@@ -17,4 +17,14 @@ export function hasAttributeOnAnyNodeShape(el, attr) {
|
|
|
17
17
|
const attributes = el.attributes;
|
|
18
18
|
return !!attributes && Object.prototype.hasOwnProperty.call(attributes, attr);
|
|
19
19
|
}
|
|
20
|
+
/** DOM-free compatibility view for the existing host attribute describer/predicate seam. */
|
|
21
|
+
export function sourceElementAttributeView({ tagName, attributes }, _options = {}) {
|
|
22
|
+
return {
|
|
23
|
+
tagName: tagName.toUpperCase(),
|
|
24
|
+
nodeType: 1,
|
|
25
|
+
attributes,
|
|
26
|
+
getAttribute: (name) => attributes[name.toLowerCase()] ?? null,
|
|
27
|
+
hasAttribute: (name) => Object.prototype.hasOwnProperty.call(attributes, name.toLowerCase()),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
20
30
|
//# sourceMappingURL=dom.js.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { CanvasEmbedPlaceholderDescriptor } from './canvas-embed-placeholders.js';
|
|
2
|
+
import { type ElementOuterRange, type SourceElement } from './source-splice.js';
|
|
3
|
+
export type EditorBlockReason = 'kuinetic' | 'canvas' | 'autoplay-video' | 'scripted' | 'embed';
|
|
4
|
+
export interface EditorBlock extends ElementOuterRange {
|
|
5
|
+
readonly tagName: string;
|
|
6
|
+
readonly id?: string | undefined;
|
|
7
|
+
readonly reason: EditorBlockReason;
|
|
8
|
+
readonly descriptor: CanvasEmbedPlaceholderDescriptor;
|
|
9
|
+
readonly element: SourceElement;
|
|
10
|
+
}
|
|
11
|
+
export interface EditorBlockOptions {
|
|
12
|
+
/** Attribute-only compatibility view, matching the existing DOM/parsed-node host seam. */
|
|
13
|
+
describeHostElement?: ((el: Element) => CanvasEmbedPlaceholderDescriptor | undefined) | undefined;
|
|
14
|
+
isProtectedElement?: ((el: Element) => boolean) | undefined;
|
|
15
|
+
translate?: ((key: string) => string) | undefined;
|
|
16
|
+
}
|
|
17
|
+
export interface EditorHtmlTarget {
|
|
18
|
+
/** Zero-based occurrence among source elements sharing tagName and (when supplied) id. */
|
|
19
|
+
occurrenceIndex: number;
|
|
20
|
+
tagName: string;
|
|
21
|
+
id?: string;
|
|
22
|
+
}
|
|
23
|
+
export type RequestEditHtml = (target: EditorHtmlTarget, options: object) => void | Promise<void>;
|
|
24
|
+
/** Outermost disjoint roots; explicit live islands veto aggregation but never host embed safety. */
|
|
25
|
+
export declare function detectEditorBlocks({ html }: {
|
|
26
|
+
html: string;
|
|
27
|
+
}, options?: EditorBlockOptions): {
|
|
28
|
+
ok: true;
|
|
29
|
+
blocks: EditorBlock[];
|
|
30
|
+
elements: SourceElement[];
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
problem: string;
|
|
34
|
+
};
|
|
35
|
+
export interface RawSourceInstance extends ElementOuterRange {
|
|
36
|
+
readonly originalHtml: string;
|
|
37
|
+
readonly openingTag: string;
|
|
38
|
+
readonly tagName: string;
|
|
39
|
+
readonly id?: string | undefined;
|
|
40
|
+
readonly nonvisual: boolean;
|
|
41
|
+
readonly descriptor?: CanvasEmbedPlaceholderDescriptor;
|
|
42
|
+
}
|
|
43
|
+
export interface PreparedEditorSource {
|
|
44
|
+
readonly ok: true;
|
|
45
|
+
readonly html: string;
|
|
46
|
+
readonly markerAttribute: string;
|
|
47
|
+
readonly instances: readonly RawSourceInstance[];
|
|
48
|
+
readonly canvasCss: string;
|
|
49
|
+
}
|
|
50
|
+
export type EditorSourcePlan = PreparedEditorSource | {
|
|
51
|
+
readonly ok: false;
|
|
52
|
+
readonly html: string;
|
|
53
|
+
readonly problem: string;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Capture before GrapesJS parses. Only a mount-private lookup token reaches the parser; authored
|
|
57
|
+
* source stays in model metadata. Style/script atoms use inert spans, never executable source tags.
|
|
58
|
+
*/
|
|
59
|
+
export declare function prepareEditorSource({ html }: {
|
|
60
|
+
html: string;
|
|
61
|
+
}, options?: EditorBlockOptions): EditorSourcePlan;
|
|
62
|
+
/** Exact authored opening tag and its source offsets; no regex-based HTML parsing or id guessing. */
|
|
63
|
+
export declare function findEditorBlockOpening({ html, target }: {
|
|
64
|
+
html: string;
|
|
65
|
+
target: EditorHtmlTarget;
|
|
66
|
+
}, _options?: object): {
|
|
67
|
+
start: number;
|
|
68
|
+
end: number;
|
|
69
|
+
openingTag: string;
|
|
70
|
+
} | undefined;
|
|
71
|
+
//# sourceMappingURL=editor-blocks.d.ts.map
|