@vertesia/ui 1.5.0-dev.20260725.083715Z → 1.5.0-dev.20260804.124748Z
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/lib/core/hooks/useScrollableSearch.d.ts +1 -1
- package/lib/core/hooks/useScrollableSearch.d.ts.map +1 -1
- package/lib/core/hooks/useScrollableSearch.js +15 -2
- package/lib/core/hooks/useScrollableSearch.js.map +1 -1
- package/lib/env/index.d.ts +4 -0
- package/lib/env/index.d.ts.map +1 -1
- package/lib/env/index.js.map +1 -1
- package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts +1 -1
- package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts.map +1 -1
- package/lib/features/agent/chat/AgentRequestInputOverlay.js +3 -8
- package/lib/features/agent/chat/AgentRequestInputOverlay.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.d.ts +8 -1
- package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.js +53 -17
- package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.d.ts +2 -1
- package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.js +25 -10
- package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +24 -9
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js +15 -3
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/Header.js +2 -2
- package/lib/features/agent/chat/ModernAgentOutput/Header.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts +2 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +2 -2
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +2 -2
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts +9 -1
- package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js +97 -2
- package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js.map +1 -1
- package/lib/features/agent/chat/hooks/useAgentStream.d.ts.map +1 -1
- package/lib/features/agent/chat/hooks/useAgentStream.js +12 -3
- package/lib/features/agent/chat/hooks/useAgentStream.js.map +1 -1
- package/lib/features/agent/chat/index.d.ts +1 -1
- package/lib/features/agent/chat/index.d.ts.map +1 -1
- package/lib/features/agent/chat/index.js.map +1 -1
- package/lib/features/facets/PromptsFacetsNav.d.ts +8 -6
- package/lib/features/facets/PromptsFacetsNav.d.ts.map +1 -1
- package/lib/features/facets/PromptsFacetsNav.js +8 -3
- package/lib/features/facets/PromptsFacetsNav.js.map +1 -1
- package/lib/features/store/objects/components/ContentOverview.js +4 -1
- package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
- package/lib/features/store/objects/components/useContentPanelHooks.d.ts +31 -3
- package/lib/features/store/objects/components/useContentPanelHooks.d.ts.map +1 -1
- package/lib/features/store/objects/search/DocumentSearchContext.d.ts +128 -6
- package/lib/features/store/objects/search/DocumentSearchContext.d.ts.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionContext.d.ts +2 -2
- package/lib/features/store/objects/selection/ObjectsActionContext.d.ts.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionSpec.d.ts +2 -2
- package/lib/features/store/objects/selection/ObjectsActionSpec.d.ts.map +1 -1
- package/lib/features/store/objects/selection/SelectionActions.d.ts +2 -2
- package/lib/features/store/objects/selection/SelectionActions.d.ts.map +1 -1
- package/lib/features/store/objects/upload/DocumentUploadModal.js.map +1 -1
- package/lib/features/store/types/IntakePolicyEditor.d.ts +2 -2
- package/lib/features/store/types/IntakePolicyEditor.d.ts.map +1 -1
- package/lib/features/store/types/IntakePolicyEditor.js +5 -1
- package/lib/features/store/types/IntakePolicyEditor.js.map +1 -1
- package/lib/features/store/types/IntakePolicyForm.d.ts.map +1 -1
- package/lib/features/store/types/IntakePolicyForm.js +2 -2
- package/lib/features/store/types/IntakePolicyForm.js.map +1 -1
- package/lib/features/store/types/ObjectSchemaEditor.d.ts +2 -2
- package/lib/features/store/types/ObjectSchemaEditor.d.ts.map +1 -1
- package/lib/features/store/types/SelectContentType.d.ts +2 -2
- package/lib/features/store/types/SelectContentType.d.ts.map +1 -1
- package/lib/features/store/types/SelectContentType.js.map +1 -1
- package/lib/features/store/types/SelectContentTypeModal.d.ts +2 -2
- package/lib/features/store/types/SelectContentTypeModal.d.ts.map +1 -1
- package/lib/features/store/types/SelectContentTypeModal.js.map +1 -1
- package/lib/features/store/types/TableLayoutEditor.d.ts +2 -2
- package/lib/features/store/types/TableLayoutEditor.d.ts.map +1 -1
- package/lib/features/store/types/TypeRegistry.d.ts +1267 -6
- package/lib/features/store/types/TypeRegistry.d.ts.map +1 -1
- package/lib/features/store/types/TypeRegistry.js +5 -0
- package/lib/features/store/types/TypeRegistry.js.map +1 -1
- package/lib/features/store/types/intake-policy-editor.logic.d.ts +9 -1
- package/lib/features/store/types/intake-policy-editor.logic.d.ts.map +1 -1
- package/lib/features/store/types/intake-policy-editor.logic.js +9 -1
- package/lib/features/store/types/intake-policy-editor.logic.js.map +1 -1
- package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts +1251 -1
- package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts.map +1 -1
- package/lib/features/store/types/typeCatalogCache.d.ts +2 -2
- package/lib/features/store/types/typeCatalogCache.d.ts.map +1 -1
- package/lib/features/store/types/typeCatalogCache.js +4 -0
- package/lib/features/store/types/typeCatalogCache.js.map +1 -1
- package/lib/features/user/UserInfo.d.ts +118 -7
- package/lib/features/user/UserInfo.d.ts.map +1 -1
- package/lib/i18n/locales/ar.json +2 -1
- package/lib/i18n/locales/de.json +2 -1
- package/lib/i18n/locales/en.json +5 -1
- package/lib/i18n/locales/es.json +2 -1
- package/lib/i18n/locales/fr.json +2 -1
- package/lib/i18n/locales/it.json +2 -1
- package/lib/i18n/locales/ja.json +2 -1
- package/lib/i18n/locales/ko.json +2 -1
- package/lib/i18n/locales/pt.json +2 -1
- package/lib/i18n/locales/ru.json +2 -1
- package/lib/i18n/locales/tr.json +2 -1
- package/lib/i18n/locales/zh-TW.json +2 -1
- package/lib/i18n/locales/zh.json +2 -1
- package/lib/session/UserSession.d.ts +18 -4
- package/lib/session/UserSession.d.ts.map +1 -1
- package/lib/shell/apps/AppInstallationProvider.d.ts +222 -2
- package/lib/shell/apps/AppInstallationProvider.d.ts.map +1 -1
- package/lib/shell/login/InviteAcceptModal.js.map +1 -1
- package/lib/vertesia-ui-core.js +1 -1
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-features.js +34 -34
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-i18n.js +13 -13
- package/lib/vertesia-ui-widgets.js +18 -18
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/lib/widgets/monacoEditor/MonacoEditor.d.ts +7 -1
- package/lib/widgets/monacoEditor/MonacoEditor.d.ts.map +1 -1
- package/lib/widgets/monacoEditor/MonacoEditor.js +62 -6
- package/lib/widgets/monacoEditor/MonacoEditor.js.map +1 -1
- package/package.json +31 -31
- package/src/core/hooks/useScrollableSearch.test.tsx +139 -0
- package/src/core/hooks/useScrollableSearch.tsx +17 -3
- package/src/env/index.ts +2 -0
- package/src/features/agent/chat/AgentChatPlaybackControls.test.tsx +2 -0
- package/src/features/agent/chat/AgentRequestInputOverlay.test.tsx +51 -6
- package/src/features/agent/chat/AgentRequestInputOverlay.tsx +5 -7
- package/src/features/agent/chat/ModernAgentConversation.test.tsx +195 -3
- package/src/features/agent/chat/ModernAgentConversation.tsx +106 -22
- package/src/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.tsx +75 -20
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +112 -0
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +62 -10
- package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.tsx +36 -4
- package/src/features/agent/chat/ModernAgentOutput/Header.tsx +2 -2
- package/src/features/agent/chat/ModernAgentOutput/MessageInput.test.tsx +71 -0
- package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +3 -1
- package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +18 -4
- package/src/features/agent/chat/ModernAgentOutput/requestInputMessages.test.ts +95 -0
- package/src/features/agent/chat/ModernAgentOutput/requestInputMessages.ts +111 -1
- package/src/features/agent/chat/hooks/useAgentStream.test.tsx +57 -0
- package/src/features/agent/chat/hooks/useAgentStream.ts +12 -3
- package/src/features/agent/chat/index.ts +1 -0
- package/src/features/facets/PromptsFacetsNav.tsx +17 -9
- package/src/features/store/objects/components/ContentOverview.tsx +4 -1
- package/src/features/store/objects/selection/ObjectsActionContext.tsx +2 -2
- package/src/features/store/objects/selection/ObjectsActionSpec.ts +2 -2
- package/src/features/store/objects/selection/SelectionActions.tsx +2 -2
- package/src/features/store/objects/upload/DocumentUploadModal.tsx +2 -2
- package/src/features/store/types/IntakePolicyEditor.tsx +12 -4
- package/src/features/store/types/IntakePolicyForm.tsx +25 -0
- package/src/features/store/types/ObjectSchemaEditor.tsx +2 -2
- package/src/features/store/types/SelectContentType.tsx +9 -9
- package/src/features/store/types/SelectContentTypeModal.tsx +5 -5
- package/src/features/store/types/TableLayoutEditor.tsx +2 -2
- package/src/features/store/types/TypeRegistry.ts +8 -3
- package/src/features/store/types/intake-policy-editor.logic.ts +10 -2
- package/src/features/store/types/typeCatalogCache.ts +10 -3
- package/src/i18n/locales/ar.json +2 -1
- package/src/i18n/locales/de.json +2 -1
- package/src/i18n/locales/en.json +5 -1
- package/src/i18n/locales/es.json +2 -1
- package/src/i18n/locales/fr.json +2 -1
- package/src/i18n/locales/it.json +2 -1
- package/src/i18n/locales/ja.json +2 -1
- package/src/i18n/locales/ko.json +2 -1
- package/src/i18n/locales/pt.json +2 -1
- package/src/i18n/locales/ru.json +2 -1
- package/src/i18n/locales/tr.json +2 -1
- package/src/i18n/locales/zh-TW.json +2 -1
- package/src/i18n/locales/zh.json +2 -1
- package/src/shell/login/InviteAcceptModal.tsx +4 -4
- package/src/widgets/markdown/ArtifactEditingSurface.test.tsx +13 -9
- package/src/widgets/monacoEditor/MonacoEditor.tsx +83 -7
|
@@ -35,7 +35,13 @@ interface MonacoEditorProps {
|
|
|
35
35
|
onMount?: (editor: monaco.editor.IStandaloneCodeEditor, monaco: typeof import('monaco-editor')) => void;
|
|
36
36
|
defaultValue?: string;
|
|
37
37
|
useCustomFolding?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* JSON Schema applied to this editor's content (language must be 'json'). Drives
|
|
40
|
+
* completion, hover documentation, and validation squiggles. Scoped to this editor
|
|
41
|
+
* instance only — other JSON editors keep their own (or no) schema.
|
|
42
|
+
*/
|
|
43
|
+
jsonSchema?: object;
|
|
38
44
|
}
|
|
39
|
-
export declare function MonacoEditor({ onChange, value, className, editorRef, language, path, debounceTimeout, options, beforeMount, onMount, defaultValue, useCustomFolding, }: MonacoEditorProps): import("react").JSX.Element;
|
|
45
|
+
export declare function MonacoEditor({ onChange, value, className, editorRef, language, path, debounceTimeout, options, beforeMount, onMount, defaultValue, useCustomFolding, jsonSchema, }: MonacoEditorProps): import("react").JSX.Element;
|
|
40
46
|
export {};
|
|
41
47
|
//# sourceMappingURL=MonacoEditor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MonacoEditor.d.ts","sourceRoot":"","sources":["../../../src/widgets/monacoEditor/MonacoEditor.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,KAAK,SAAS,EAAqD,MAAM,OAAO,CAAC;AAG1F,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC;AA6BnC,MAAM,WAAW,UAAU;IACvB,QAAQ,IAAI,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;
|
|
1
|
+
{"version":3,"file":"MonacoEditor.d.ts","sourceRoot":"","sources":["../../../src/widgets/monacoEditor/MonacoEditor.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,KAAK,SAAS,EAAqD,MAAM,OAAO,CAAC;AAG1F,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC;AA6BnC,MAAM,WAAW,UAAU;IACvB,QAAQ,IAAI,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAoCD,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE;QACH,GAAG,EAAE;YACD,QAAQ,IAAI,MAAM,CAAC;YACnB,MAAM,EAAE,MAAM,CAAC;SAClB,CAAC;KACL,CAAC;CACL;AAED,qBAAa,UAAW,YAAW,UAAU;IAErC,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,SAAS;gBADT,SAAS,EAAE,MAAM,MAAM,EACvB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;IAG9C,QAAQ;IAIR,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM;CAG1B;AAED,UAAU,iBAAiB;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,oCAAoC,CAAC;IAC7D,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,eAAe,CAAC,KAAK,IAAI,CAAC;IAC/D,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE,MAAM,EAAE,cAAc,eAAe,CAAC,KAAK,IAAI,CAAC;IACxG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,YAAY,CAAC,EACzB,QAAQ,EACR,KAAK,EACL,SAAS,EACT,SAAS,EACT,QAAuB,EACvB,IAAI,EACJ,eAAmB,EACnB,OAAY,EACZ,WAAW,EACX,OAAO,EACP,YAAY,EACZ,gBAAwB,EACxB,UAAU,GACb,EAAE,iBAAiB,+BAoOnB"}
|
|
@@ -32,6 +32,27 @@ function getOverflowWidgetsNode() {
|
|
|
32
32
|
}
|
|
33
33
|
return overflowWidgetsNode;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Registry of JSON schemas keyed by model path. Monaco's JSON language service is configured
|
|
37
|
+
* globally (per language, not per editor), so every mounted editor's schema must be re-applied
|
|
38
|
+
* together — each entry is scoped to its own model via `fileMatch`, so editors never see each
|
|
39
|
+
* other's schema.
|
|
40
|
+
*/
|
|
41
|
+
const jsonSchemaRegistry = new Map();
|
|
42
|
+
let jsonSchemaModelCounter = 0;
|
|
43
|
+
function applyJsonSchemas(monacoInstance) {
|
|
44
|
+
const jsonDefaults = monacoInstance.languages
|
|
45
|
+
.json?.jsonDefaults;
|
|
46
|
+
jsonDefaults?.setDiagnosticsOptions({
|
|
47
|
+
validate: true,
|
|
48
|
+
enableSchemaRequest: false,
|
|
49
|
+
schemas: Array.from(jsonSchemaRegistry.entries()).map(([modelPath, schema]) => ({
|
|
50
|
+
uri: `vertesia://schemas/${encodeURIComponent(modelPath)}`,
|
|
51
|
+
fileMatch: [modelPath],
|
|
52
|
+
schema,
|
|
53
|
+
})),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
35
56
|
export class VEditorApi {
|
|
36
57
|
VgetValue;
|
|
37
58
|
VsetValue;
|
|
@@ -46,10 +67,14 @@ export class VEditorApi {
|
|
|
46
67
|
this.VsetValue(value || '');
|
|
47
68
|
}
|
|
48
69
|
}
|
|
49
|
-
export function MonacoEditor({ onChange, value, className, editorRef, language = 'javascript', path, debounceTimeout = 0, options = {}, beforeMount, onMount, defaultValue, useCustomFolding = false, }) {
|
|
70
|
+
export function MonacoEditor({ onChange, value, className, editorRef, language = 'javascript', path, debounceTimeout = 0, options = {}, beforeMount, onMount, defaultValue, useCustomFolding = false, jsonSchema, }) {
|
|
50
71
|
const [editorValue, setEditorValue] = useState(value || defaultValue || '');
|
|
51
72
|
const editorInstanceRef = useRef(null);
|
|
52
73
|
const monacoInstanceRef = useRef(null);
|
|
74
|
+
// Stable per-instance model path: required to scope a jsonSchema to THIS editor's model.
|
|
75
|
+
// Callers may pass an explicit `path`; otherwise one is generated when a schema is set.
|
|
76
|
+
const [generatedPath] = useState(() => jsonSchema ? `inmemory://vertesia/json/${++jsonSchemaModelCounter}.json` : undefined);
|
|
77
|
+
const modelPath = path ?? generatedPath;
|
|
53
78
|
const { theme } = useTheme();
|
|
54
79
|
const resolvedTheme = theme === 'system' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme;
|
|
55
80
|
const effectiveValue = value || defaultValue || '';
|
|
@@ -114,6 +139,10 @@ export function MonacoEditor({ onChange, value, className, editorRef, language =
|
|
|
114
139
|
const handleEditorDidMount = useCallback((editor, monacoInstance) => {
|
|
115
140
|
editorInstanceRef.current = editor;
|
|
116
141
|
monacoInstanceRef.current = monacoInstance;
|
|
142
|
+
if (jsonSchema && modelPath && language === 'json') {
|
|
143
|
+
jsonSchemaRegistry.set(modelPath, jsonSchema);
|
|
144
|
+
applyJsonSchemas(monacoInstance);
|
|
145
|
+
}
|
|
117
146
|
if (useCustomFolding) {
|
|
118
147
|
registerCustomFoldingProviders(monacoInstance);
|
|
119
148
|
}
|
|
@@ -138,9 +167,26 @@ export function MonacoEditor({ onChange, value, className, editorRef, language =
|
|
|
138
167
|
}
|
|
139
168
|
// Call custom onMount if provided
|
|
140
169
|
onMount?.(editor, monacoInstance);
|
|
141
|
-
}, [onMount, resolvedTheme, useCustomFolding, foldAllCodeBlocks]);
|
|
142
|
-
//
|
|
170
|
+
}, [onMount, resolvedTheme, useCustomFolding, foldAllCodeBlocks, jsonSchema, modelPath, language]);
|
|
171
|
+
// Unregister this editor's schema on unmount so the global JSON diagnostics config
|
|
172
|
+
// doesn't accumulate schemas for dead models.
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
return () => {
|
|
175
|
+
if (jsonSchema && modelPath && jsonSchemaRegistry.delete(modelPath) && monacoInstanceRef.current) {
|
|
176
|
+
applyJsonSchemas(monacoInstanceRef.current);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}, [jsonSchema, modelPath]);
|
|
180
|
+
// Apply external `value`/`defaultValue` changes to the model — but NEVER while the user is
|
|
181
|
+
// actively typing. If the editor has focus, an incoming value is almost always a stale echo of
|
|
182
|
+
// this editor's own (possibly debounced or reformatted) onChange output flowing back in through
|
|
183
|
+
// a controlled `value` prop. Re-applying it would jump the caret and drop the characters typed
|
|
184
|
+
// during the round-trip. We skip while focused; genuine external updates land once the editor is
|
|
185
|
+
// blurred. Explicit programmatic updates go through the editor ref (`setValue`) and are exempt.
|
|
143
186
|
useEffect(() => {
|
|
187
|
+
if (editorInstanceRef.current?.hasTextFocus()) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
144
190
|
setEditorValue((currentValue) => {
|
|
145
191
|
if (effectiveValue === currentValue) {
|
|
146
192
|
return currentValue;
|
|
@@ -151,10 +197,13 @@ export function MonacoEditor({ onChange, value, className, editorRef, language =
|
|
|
151
197
|
return effectiveValue;
|
|
152
198
|
});
|
|
153
199
|
}, [effectiveValue]);
|
|
154
|
-
// Re-fold code blocks when value
|
|
200
|
+
// Re-fold code blocks when the value changes externally — same focus guard, so folding never
|
|
201
|
+
// moves the selection out from under an actively typing user.
|
|
155
202
|
useEffect(() => {
|
|
156
203
|
if (!effectiveValue || !useCustomFolding || !editorInstanceRef.current || !monacoInstanceRef.current)
|
|
157
204
|
return;
|
|
205
|
+
if (editorInstanceRef.current.hasTextFocus())
|
|
206
|
+
return;
|
|
158
207
|
const editor = editorInstanceRef.current;
|
|
159
208
|
const monacoInstance = monacoInstanceRef.current;
|
|
160
209
|
const timer = setTimeout(() => foldAllCodeBlocks(editor, monacoInstance), 300);
|
|
@@ -182,7 +231,7 @@ export function MonacoEditor({ onChange, value, className, editorRef, language =
|
|
|
182
231
|
renderValidationDecorations: 'on', // Show error squiggles
|
|
183
232
|
renderLineHighlight: 'line', // Highlight entire line for errors
|
|
184
233
|
hover: {
|
|
185
|
-
enabled:
|
|
234
|
+
enabled: 'on',
|
|
186
235
|
delay: 100,
|
|
187
236
|
}, // Enable hover for error messages
|
|
188
237
|
quickSuggestions: {
|
|
@@ -192,6 +241,13 @@ export function MonacoEditor({ onChange, value, className, editorRef, language =
|
|
|
192
241
|
}, // Show problems panel information
|
|
193
242
|
...options,
|
|
194
243
|
};
|
|
195
|
-
return (_jsx("div", { className: clsx(className, 'w-full h-full!'), children: _jsx(Editor, { className: "h-full w-full", height: "100%", theme: resolvedTheme === 'dark' ? 'vs-dark' : 'light', language: language, path:
|
|
244
|
+
return (_jsx("div", { className: clsx(className, 'w-full h-full!'), children: _jsx(Editor, { className: "h-full w-full", height: "100%", theme: resolvedTheme === 'dark' ? 'vs-dark' : 'light', language: language, path: modelPath, onChange: handleEditorChange, onMount: handleEditorDidMount, beforeMount: beforeMount, options: defaultOptions,
|
|
245
|
+
// Uncontrolled: seed the initial content once via `defaultValue` and never bind a
|
|
246
|
+
// reactive `value`. @monaco-editor/react re-applies a changed `value` prop through
|
|
247
|
+
// `executeEdits(..., forceMoveMarkers: true)` whenever it differs from the live
|
|
248
|
+
// model, snapping the caret to the END of the content — the cursor jump we are
|
|
249
|
+
// eliminating whenever a caller feeds its own onChange output back into `value`.
|
|
250
|
+
// All external updates instead flow through the focus-guarded sync effect above.
|
|
251
|
+
defaultValue: effectiveValue }) }));
|
|
196
252
|
}
|
|
197
253
|
//# sourceMappingURL=MonacoEditor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MonacoEditor.js","sourceRoot":"","sources":["../../../src/widgets/monacoEditor/MonacoEditor.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,OAAO,EAAkB,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC1F,OAAO,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAIvE;;;;;;;;GAQG;AACH,IAAI,mBAA4C,CAAC;AACjD,SAAS,sBAAsB;IAC3B,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO,SAAS,CAAC;IACtD,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,CAAC,SAAS,GAAG,eAAe,CAAC;QACjC,sFAAsF;QACtF,uFAAuF;QACvF,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,UAAU,CAAC;QACjC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC;QACrB,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC;QACtB,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAChC,mBAAmB,GAAG,IAAI,CAAC;IAC/B,CAAC;IACD,OAAO,mBAAmB,CAAC;AAC/B,CAAC;
|
|
1
|
+
{"version":3,"file":"MonacoEditor.js","sourceRoot":"","sources":["../../../src/widgets/monacoEditor/MonacoEditor.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertesia/ui",
|
|
3
|
-
"version": "1.5.0-dev.
|
|
3
|
+
"version": "1.5.0-dev.20260804.124748Z",
|
|
4
4
|
"description": "Vertesia UI components and and hooks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -24,24 +24,24 @@
|
|
|
24
24
|
"@floating-ui/dom": "^1.8.0",
|
|
25
25
|
"@floating-ui/react": "^0.27.20",
|
|
26
26
|
"@monaco-editor/react": "^4.7.0",
|
|
27
|
-
"@radix-ui/react-checkbox": "^1.3.
|
|
28
|
-
"@radix-ui/react-collapsible": "^1.1.
|
|
29
|
-
"@radix-ui/react-dialog": "^1.1.
|
|
30
|
-
"@radix-ui/react-direction": "^1.1.
|
|
31
|
-
"@radix-ui/react-dismissable-layer": "^1.1.
|
|
32
|
-
"@radix-ui/react-dropdown-menu": "^2.1.
|
|
33
|
-
"@radix-ui/react-focus-guards": "^1.1.
|
|
34
|
-
"@radix-ui/react-focus-scope": "^1.1.
|
|
35
|
-
"@radix-ui/react-label": "^2.1.
|
|
36
|
-
"@radix-ui/react-popover": "^1.1.
|
|
37
|
-
"@radix-ui/react-portal": "^1.1.
|
|
38
|
-
"@radix-ui/react-radio-group": "^1.4.
|
|
39
|
-
"@radix-ui/react-separator": "^1.1.
|
|
40
|
-
"@radix-ui/react-slider": "^1.4.
|
|
41
|
-
"@radix-ui/react-slot": "^1.3.
|
|
42
|
-
"@radix-ui/react-switch": "^1.3.
|
|
43
|
-
"@radix-ui/react-tabs": "^1.1.
|
|
44
|
-
"@radix-ui/react-tooltip": "^1.2.
|
|
27
|
+
"@radix-ui/react-checkbox": "^1.3.11",
|
|
28
|
+
"@radix-ui/react-collapsible": "^1.1.20",
|
|
29
|
+
"@radix-ui/react-dialog": "^1.1.23",
|
|
30
|
+
"@radix-ui/react-direction": "^1.1.4",
|
|
31
|
+
"@radix-ui/react-dismissable-layer": "^1.1.19",
|
|
32
|
+
"@radix-ui/react-dropdown-menu": "^2.1.24",
|
|
33
|
+
"@radix-ui/react-focus-guards": "^1.1.6",
|
|
34
|
+
"@radix-ui/react-focus-scope": "^1.1.16",
|
|
35
|
+
"@radix-ui/react-label": "^2.1.15",
|
|
36
|
+
"@radix-ui/react-popover": "^1.1.23",
|
|
37
|
+
"@radix-ui/react-portal": "^1.1.17",
|
|
38
|
+
"@radix-ui/react-radio-group": "^1.4.7",
|
|
39
|
+
"@radix-ui/react-separator": "^1.1.15",
|
|
40
|
+
"@radix-ui/react-slider": "^1.4.7",
|
|
41
|
+
"@radix-ui/react-slot": "^1.3.3",
|
|
42
|
+
"@radix-ui/react-switch": "^1.3.7",
|
|
43
|
+
"@radix-ui/react-tabs": "^1.1.21",
|
|
44
|
+
"@radix-ui/react-tooltip": "^1.2.16",
|
|
45
45
|
"ajv": "^8.20.0",
|
|
46
46
|
"ajv-formats": "^3.0.1",
|
|
47
47
|
"aria-hidden": "^1.2.6",
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
"framer-motion": "^12.42.2",
|
|
57
57
|
"i18next": "^26.3.6",
|
|
58
58
|
"jwt-decode": "^4.0.0",
|
|
59
|
-
"katex": "^0.
|
|
59
|
+
"katex": "^0.18.1",
|
|
60
60
|
"lodash-es": "^4.18.1",
|
|
61
|
-
"lucide-react": "^1.
|
|
61
|
+
"lucide-react": "^1.27.0",
|
|
62
62
|
"mermaid": "^11.16.0",
|
|
63
|
-
"monaco-editor": "^0.
|
|
63
|
+
"monaco-editor": "^0.56.0",
|
|
64
64
|
"motion": "^12.42.2",
|
|
65
65
|
"papaparse": "^5.5.4",
|
|
66
66
|
"react": "19.2.7",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"react-date-picker": "^12.0.2",
|
|
69
69
|
"react-dom": "19.2.7",
|
|
70
70
|
"react-error-boundary": "^6.1.2",
|
|
71
|
-
"react-i18next": "^17.0.
|
|
71
|
+
"react-i18next": "^17.0.11",
|
|
72
72
|
"react-markdown": "^10.1.0",
|
|
73
73
|
"react-pdf": "^10.4.1",
|
|
74
74
|
"react-remove-scroll": "^2.7.2",
|
|
@@ -86,14 +86,14 @@
|
|
|
86
86
|
"tailwind-merge": "^3.6.0",
|
|
87
87
|
"ts-md5": "^2.0.1",
|
|
88
88
|
"unist-util-visit": "^5.1.0",
|
|
89
|
-
"vega": "^6.
|
|
89
|
+
"vega": "^6.3.1",
|
|
90
90
|
"vega-embed": "^7.1.0",
|
|
91
91
|
"vega-lite": "^6.4.3",
|
|
92
|
-
"@vertesia/client": "1.5.0-dev.
|
|
93
|
-
"@vertesia/common": "1.5.0-dev.
|
|
94
|
-
"@vertesia/fusion-ux": "1.5.0-dev.
|
|
95
|
-
"@vertesia/rich-text": "1.5.0-dev.
|
|
96
|
-
"@vertesia/json": "1.5.0-dev.
|
|
92
|
+
"@vertesia/client": "1.5.0-dev.20260804.124748Z",
|
|
93
|
+
"@vertesia/common": "1.5.0-dev.20260804.124748Z",
|
|
94
|
+
"@vertesia/fusion-ux": "1.5.0-dev.20260804.124748Z",
|
|
95
|
+
"@vertesia/rich-text": "1.5.0-dev.20260804.124748Z",
|
|
96
|
+
"@vertesia/json": "1.5.0-dev.20260804.124748Z"
|
|
97
97
|
},
|
|
98
98
|
"devDependencies": {
|
|
99
99
|
"@testing-library/react": "^16.3.2",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"@types/react-dom": "19.2.3",
|
|
107
107
|
"axe-core": "^4.12.1",
|
|
108
108
|
"culori": "^4.0.2",
|
|
109
|
-
"i18next-cli": "^1.66.
|
|
109
|
+
"i18next-cli": "^1.66.2",
|
|
110
110
|
"jsdom": "^29.1.1",
|
|
111
111
|
"rolldown": "1.2.0",
|
|
112
112
|
"typescript": "^6.0.3",
|
|
@@ -184,7 +184,7 @@
|
|
|
184
184
|
"url": "https://github.com/vertesia/composableai.git",
|
|
185
185
|
"directory": "packages/ui"
|
|
186
186
|
},
|
|
187
|
-
"gitHead": "
|
|
187
|
+
"gitHead": "87ac9166db2b38bd1be6e37adf0414ea83e402ee",
|
|
188
188
|
"scripts": {
|
|
189
189
|
"clean:lib": "rimraf ./lib ./tsconfig.tsbuildinfo",
|
|
190
190
|
"clean": "rimraf ./node_modules ./lib ./tsconfig.tsbuildinfo",
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { act, render } from '@testing-library/react';
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
import { beforeAll, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { type ScrollableSearchResult, useDefaultScrollableSearch } from './useScrollableSearch';
|
|
5
|
+
|
|
6
|
+
beforeAll(() => {
|
|
7
|
+
// jsdom has no IntersectionObserver; the hook only needs it constructible.
|
|
8
|
+
vi.stubGlobal(
|
|
9
|
+
'IntersectionObserver',
|
|
10
|
+
class {
|
|
11
|
+
observe() {}
|
|
12
|
+
unobserve() {}
|
|
13
|
+
disconnect() {}
|
|
14
|
+
},
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Regression tests for the infinite request loop: callers pass an INLINE search
|
|
20
|
+
* closure (new identity every render). The fetch effect must not depend on that
|
|
21
|
+
* identity, otherwise every completed request re-triggers the effect forever.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
type Item = { id: number };
|
|
25
|
+
|
|
26
|
+
function flush(): Promise<void> {
|
|
27
|
+
// Drain the microtask queue so pending search promises settle.
|
|
28
|
+
return act(() => Promise.resolve());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function Harness({
|
|
32
|
+
searchFn,
|
|
33
|
+
onApi,
|
|
34
|
+
}: {
|
|
35
|
+
searchFn: (payload: unknown, offset: number, limit: number) => Promise<Item[]>;
|
|
36
|
+
onApi: (api: ScrollableSearchResult<Item, unknown, number>) => void;
|
|
37
|
+
}) {
|
|
38
|
+
const loadMoreRef = useRef<HTMLDivElement>(null);
|
|
39
|
+
// Unrelated state to force extra re-renders, as a real page would.
|
|
40
|
+
const [, setTick] = useState(0);
|
|
41
|
+
const api = useDefaultScrollableSearch<Item, unknown>({
|
|
42
|
+
// Inline closure ON PURPOSE — new identity on every render, like real callers.
|
|
43
|
+
async search(payload, offset, limit) {
|
|
44
|
+
return searchFn(payload, offset, limit);
|
|
45
|
+
},
|
|
46
|
+
pageSize: 2,
|
|
47
|
+
nextPageTrigger: loadMoreRef,
|
|
48
|
+
payload: {},
|
|
49
|
+
});
|
|
50
|
+
onApi(api);
|
|
51
|
+
return (
|
|
52
|
+
<div>
|
|
53
|
+
<button type="button" data-testid="rerender" onClick={() => setTick((t) => t + 1)} />
|
|
54
|
+
<div ref={loadMoreRef} />
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function setup(searchFn: (payload: unknown, offset: number, limit: number) => Promise<Item[]>) {
|
|
60
|
+
let api!: ScrollableSearchResult<Item, unknown, number>;
|
|
61
|
+
const utils = render(
|
|
62
|
+
<Harness
|
|
63
|
+
searchFn={searchFn}
|
|
64
|
+
onApi={(a) => {
|
|
65
|
+
api = a;
|
|
66
|
+
}}
|
|
67
|
+
/>,
|
|
68
|
+
);
|
|
69
|
+
return { ...utils, getApi: () => api };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe('useDefaultScrollableSearch', () => {
|
|
73
|
+
it('fetches exactly once on mount despite inline search closures and re-renders', async () => {
|
|
74
|
+
const searchFn = vi.fn(async () => [{ id: 1 }, { id: 2 }]);
|
|
75
|
+
const { getApi } = setup(searchFn);
|
|
76
|
+
|
|
77
|
+
await flush();
|
|
78
|
+
expect(searchFn).toHaveBeenCalledTimes(1);
|
|
79
|
+
expect(getApi().result).toEqual([{ id: 1 }, { id: 2 }]);
|
|
80
|
+
|
|
81
|
+
// Extra render cycles (state updates elsewhere on the page) must NOT re-fetch.
|
|
82
|
+
await flush();
|
|
83
|
+
await flush();
|
|
84
|
+
expect(searchFn).toHaveBeenCalledTimes(1);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('searchMore appends the next page (single extra request)', async () => {
|
|
88
|
+
const pages: Item[][] = [[{ id: 1 }, { id: 2 }], [{ id: 3 }]];
|
|
89
|
+
const searchFn = vi.fn(async (_p: unknown, offset: number) => pages[offset / 2] ?? []);
|
|
90
|
+
const { getApi } = setup(searchFn);
|
|
91
|
+
await flush();
|
|
92
|
+
|
|
93
|
+
act(() => getApi().searchMore());
|
|
94
|
+
await flush();
|
|
95
|
+
|
|
96
|
+
expect(searchFn).toHaveBeenCalledTimes(2);
|
|
97
|
+
expect(getApi().result).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]);
|
|
98
|
+
// Page of 1 < pageSize still probes once more only when triggered — no auto loop.
|
|
99
|
+
await flush();
|
|
100
|
+
expect(searchFn).toHaveBeenCalledTimes(2);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('refresh() re-fetches even on the first page (same payload, same page)', async () => {
|
|
104
|
+
const searchFn = vi.fn(async () => [{ id: 1 }]);
|
|
105
|
+
const { getApi } = setup(searchFn);
|
|
106
|
+
await flush();
|
|
107
|
+
expect(searchFn).toHaveBeenCalledTimes(1);
|
|
108
|
+
|
|
109
|
+
act(() => getApi().refresh());
|
|
110
|
+
await flush();
|
|
111
|
+
expect(searchFn).toHaveBeenCalledTimes(2);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('empty result stops pagination (hasMore=false) and does not re-fetch', async () => {
|
|
115
|
+
const searchFn = vi.fn(async () => []);
|
|
116
|
+
const { getApi } = setup(searchFn);
|
|
117
|
+
await flush();
|
|
118
|
+
|
|
119
|
+
expect(searchFn).toHaveBeenCalledTimes(1);
|
|
120
|
+
expect(getApi().hasMore).toBe(false);
|
|
121
|
+
|
|
122
|
+
act(() => getApi().searchMore());
|
|
123
|
+
await flush();
|
|
124
|
+
// nextPage is null — searchMore is a no-op.
|
|
125
|
+
expect(searchFn).toHaveBeenCalledTimes(1);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('a failing search sets error once and does not retry in a loop', async () => {
|
|
129
|
+
const searchFn = vi.fn(async () => {
|
|
130
|
+
throw new Error('boom');
|
|
131
|
+
});
|
|
132
|
+
const { getApi } = setup(searchFn);
|
|
133
|
+
await flush();
|
|
134
|
+
await flush();
|
|
135
|
+
|
|
136
|
+
expect(getApi().error?.message).toBe('boom');
|
|
137
|
+
expect(searchFn).toHaveBeenCalledTimes(1);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -48,7 +48,7 @@ interface ScrollableSearchOptions<ResultT, PayloadT, PageT = number> {
|
|
|
48
48
|
nextPageTrigger: React.RefObject<HTMLElement | null>;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
interface ScrollableSearchResult<ResultT, PayloadT, PageT = number> {
|
|
51
|
+
export interface ScrollableSearchResult<ResultT, PayloadT, PageT = number> {
|
|
52
52
|
/**
|
|
53
53
|
* Initiates a new search with the given payload
|
|
54
54
|
* @param payload The search payload
|
|
@@ -105,15 +105,27 @@ export function useScrollableSearch<ResultT, PayloadT, PageT = number>(
|
|
|
105
105
|
const [results, setResults] = useState<ResultT[]>([]);
|
|
106
106
|
const [nextPage, setNextPage] = useState<PageT | null>(null);
|
|
107
107
|
const [isSearching, setIsSearching] = useState(false);
|
|
108
|
+
// Bumped by search()/refresh() so the fetch effect re-runs even when the
|
|
109
|
+
// payload/page state is unchanged (e.g. refresh on the first page).
|
|
110
|
+
const [refreshTick, setRefreshTick] = useState(0);
|
|
108
111
|
|
|
109
112
|
// Track current request to prevent stale results
|
|
110
113
|
const requestIdRef = useRef(0);
|
|
111
114
|
|
|
115
|
+
// Latest-ref for the search function: callers routinely pass an inline
|
|
116
|
+
// closure, which gets a new identity on every render. Depending on it in
|
|
117
|
+
// the fetch effect made every completed request re-trigger the effect —
|
|
118
|
+
// an infinite request loop. The ref always points at the latest closure
|
|
119
|
+
// without participating in the effect's dependencies.
|
|
120
|
+
const searchFnRef = useRef(opts.search);
|
|
121
|
+
searchFnRef.current = opts.search;
|
|
122
|
+
|
|
112
123
|
const search = (payload: PayloadT) => {
|
|
113
124
|
setPage(null);
|
|
114
125
|
setResults([]); // Clear old results immediately
|
|
115
126
|
setNextPage(null);
|
|
116
127
|
setLastPayload(payload);
|
|
128
|
+
setRefreshTick((tick) => tick + 1);
|
|
117
129
|
};
|
|
118
130
|
|
|
119
131
|
const searchMore = () => {
|
|
@@ -122,13 +134,15 @@ export function useScrollableSearch<ResultT, PayloadT, PageT = number>(
|
|
|
122
134
|
}
|
|
123
135
|
};
|
|
124
136
|
|
|
137
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: refreshTick is an intentional external trigger — refresh() bumps it to re-run the fetch when payload/page are unchanged
|
|
125
138
|
useEffect(() => {
|
|
126
139
|
// Increment request ID to mark previous requests as stale
|
|
127
140
|
requestIdRef.current += 1;
|
|
128
141
|
const currentRequestId = requestIdRef.current;
|
|
129
142
|
|
|
130
143
|
setIsSearching(true);
|
|
131
|
-
|
|
144
|
+
searchFnRef
|
|
145
|
+
.current(lastPayload, page, pageSize)
|
|
132
146
|
.then((r) => {
|
|
133
147
|
// Only update state if this is still the current request
|
|
134
148
|
if (currentRequestId !== requestIdRef.current) {
|
|
@@ -158,7 +172,7 @@ export function useScrollableSearch<ResultT, PayloadT, PageT = number>(
|
|
|
158
172
|
setIsSearching(false);
|
|
159
173
|
}
|
|
160
174
|
});
|
|
161
|
-
}, [...dependencies, lastPayload,
|
|
175
|
+
}, [...dependencies, lastPayload, refreshTick, page, pageSize]);
|
|
162
176
|
|
|
163
177
|
// Intersection observer for infinite scrolling
|
|
164
178
|
useIntersectionObserver(
|
package/src/env/index.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface EnvProps {
|
|
|
17
17
|
sts: string; // Security Token Service endpoint
|
|
18
18
|
git?: string; // Smart HTTP app source git endpoint
|
|
19
19
|
mcp?: string;
|
|
20
|
+
/** Appgen app-gateway endpoint (serves live development previews and app bundles). */
|
|
21
|
+
gateway?: string;
|
|
20
22
|
};
|
|
21
23
|
firebase?: {
|
|
22
24
|
apiKey: string;
|
|
@@ -24,6 +24,8 @@ describe('AgentChatPlaybackControls', () => {
|
|
|
24
24
|
<AgentChatPlaybackControls cursor="live" messages={messages} onChangeCursor={onChangeCursor} />,
|
|
25
25
|
);
|
|
26
26
|
|
|
27
|
+
expect(screen.getByText('Debug')).toBeTruthy();
|
|
28
|
+
|
|
27
29
|
fireEvent.click(screen.getByRole('button', { name: 'Jump to first message' }));
|
|
28
30
|
|
|
29
31
|
expect(onChangeCursor).toHaveBeenCalledWith(0);
|
|
@@ -1,22 +1,30 @@
|
|
|
1
|
-
import { fireEvent, screen } from '@testing-library/react';
|
|
1
|
+
import { fireEvent, screen, waitFor } from '@testing-library/react';
|
|
2
2
|
import { AgentMessageType } from '@vertesia/common';
|
|
3
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
4
|
import { renderWithProviders } from '../../../__tests__/test-utils.js';
|
|
5
5
|
import { AgentRequestInputOverlay } from './AgentRequestInputOverlay';
|
|
6
6
|
import type { RequestInputMessageWithUx } from './ModernAgentOutput/requestInputMessages';
|
|
7
7
|
|
|
8
|
+
const mocks = vi.hoisted(() => ({
|
|
9
|
+
getCollectionStatus: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
|
|
8
12
|
vi.mock('@vertesia/ui/session', () => ({
|
|
9
13
|
useUserSession: () => ({
|
|
10
14
|
client: {
|
|
11
15
|
remoteMcpConnections: {
|
|
12
|
-
getCollectionStatus:
|
|
16
|
+
getCollectionStatus: mocks.getCollectionStatus,
|
|
13
17
|
},
|
|
14
18
|
},
|
|
15
19
|
}),
|
|
16
20
|
}));
|
|
17
21
|
|
|
18
22
|
vi.mock('../../oauth/RemoteMcpConnectionButton.js', () => ({
|
|
19
|
-
RemoteMcpConnectionButton: () =>
|
|
23
|
+
RemoteMcpConnectionButton: ({ onAuthChange }: { onAuthChange: () => void }) => (
|
|
24
|
+
<button type="button" onClick={onAuthChange}>
|
|
25
|
+
Connect
|
|
26
|
+
</button>
|
|
27
|
+
),
|
|
20
28
|
}));
|
|
21
29
|
|
|
22
30
|
function createMcpRequestMessage(): RequestInputMessageWithUx {
|
|
@@ -27,6 +35,7 @@ function createMcpRequestMessage(): RequestInputMessageWithUx {
|
|
|
27
35
|
workstream_id: 'main',
|
|
28
36
|
message: 'Connect to Jira to continue.',
|
|
29
37
|
details: {
|
|
38
|
+
request_id: 'request-mcp-1',
|
|
30
39
|
ux: {
|
|
31
40
|
mcp_connect: {
|
|
32
41
|
app_install_id: 'app1',
|
|
@@ -46,6 +55,7 @@ function createToolApprovalRequestMessage(): RequestInputMessageWithUx {
|
|
|
46
55
|
workstream_id: 'main',
|
|
47
56
|
message: 'Approve Write Artifact: quotes.md?',
|
|
48
57
|
details: {
|
|
58
|
+
request_id: 'write_artifact:name:quotes.md',
|
|
49
59
|
tool_approval: {
|
|
50
60
|
tool_name: 'write_artifact',
|
|
51
61
|
tool_title: 'Write Artifact',
|
|
@@ -74,6 +84,11 @@ function createToolApprovalRequestMessage(): RequestInputMessageWithUx {
|
|
|
74
84
|
}
|
|
75
85
|
|
|
76
86
|
describe('AgentRequestInputOverlay', () => {
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
vi.clearAllMocks();
|
|
89
|
+
mocks.getCollectionStatus.mockResolvedValue({ authenticated: true });
|
|
90
|
+
});
|
|
91
|
+
|
|
77
92
|
it('lets the user decline an MCP connection request', () => {
|
|
78
93
|
const onSendMessage = vi.fn();
|
|
79
94
|
|
|
@@ -86,7 +101,29 @@ describe('AgentRequestInputOverlay', () => {
|
|
|
86
101
|
fireEvent.click(screen.getByRole('button', { name: /decline/i }));
|
|
87
102
|
|
|
88
103
|
expect(onSendMessage).toHaveBeenCalledTimes(1);
|
|
89
|
-
expect(onSendMessage).toHaveBeenCalledWith("I don't want to connect to Jira. Continue without it."
|
|
104
|
+
expect(onSendMessage).toHaveBeenCalledWith("I don't want to connect to Jira. Continue without it.", {
|
|
105
|
+
request_input_response: { request_id: 'request-mcp-1' },
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('correlates a successful MCP connection response', async () => {
|
|
110
|
+
const onMcpConnected = vi.fn();
|
|
111
|
+
|
|
112
|
+
renderWithProviders(
|
|
113
|
+
<AgentRequestInputOverlay
|
|
114
|
+
message={createMcpRequestMessage()}
|
|
115
|
+
onSendMessage={vi.fn()}
|
|
116
|
+
onMcpConnected={onMcpConnected}
|
|
117
|
+
/>,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
fireEvent.click(screen.getByRole('button', { name: 'Connect' }));
|
|
121
|
+
|
|
122
|
+
await waitFor(() => {
|
|
123
|
+
expect(onMcpConnected).toHaveBeenCalledWith(expect.objectContaining({ collection_id: 'jira' }), {
|
|
124
|
+
request_input_response: { request_id: 'request-mcp-1' },
|
|
125
|
+
});
|
|
126
|
+
});
|
|
90
127
|
});
|
|
91
128
|
|
|
92
129
|
it('sends a commented tool approval denial with structured metadata', () => {
|
|
@@ -107,6 +144,9 @@ describe('AgentRequestInputOverlay', () => {
|
|
|
107
144
|
decision: 'deny_with_feedback',
|
|
108
145
|
approval_key: 'write_artifact:name:quotes.md',
|
|
109
146
|
},
|
|
147
|
+
request_input_response: {
|
|
148
|
+
request_id: 'write_artifact:name:quotes.md',
|
|
149
|
+
},
|
|
110
150
|
});
|
|
111
151
|
});
|
|
112
152
|
|
|
@@ -125,6 +165,9 @@ describe('AgentRequestInputOverlay', () => {
|
|
|
125
165
|
decision: 'allow_for_run',
|
|
126
166
|
approval_key: 'write_artifact:name:quotes.md',
|
|
127
167
|
},
|
|
168
|
+
request_input_response: {
|
|
169
|
+
request_id: 'write_artifact:name:quotes.md',
|
|
170
|
+
},
|
|
128
171
|
});
|
|
129
172
|
});
|
|
130
173
|
|
|
@@ -138,7 +181,9 @@ describe('AgentRequestInputOverlay', () => {
|
|
|
138
181
|
fireEvent.click(screen.getByRole('button', { name: 'Allow once' }));
|
|
139
182
|
|
|
140
183
|
expect(onSendMessage).toHaveBeenCalledTimes(1);
|
|
141
|
-
expect(onSendMessage).toHaveBeenCalledWith('allow_once'
|
|
184
|
+
expect(onSendMessage).toHaveBeenCalledWith('allow_once', {
|
|
185
|
+
request_input_response: { request_id: 'write_artifact:name:quotes.md' },
|
|
186
|
+
});
|
|
142
187
|
});
|
|
143
188
|
|
|
144
189
|
it('renders legacy field-prefixed tool approval prompts with a friendly target', () => {
|