From a596b0fc2f8cda71da1932a0116a87761a694905 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 22:31:57 +0000 Subject: [PATCH 1/9] Replace Ace with Monaco in ResourceEditor + XML/FHIRPath enhancements Agent-Logs-Url: https://github.com/brianpos/fhirpath-lab/sessions/1443ec82-56dc-47c1-9a15-52ddc6c3d401 Co-authored-by: brianpos <1557083+brianpos@users.noreply.github.com> --- vue3-src/app/components/ResourceEditor.vue | 583 +++++++++++++-------- vue3-src/app/utils/monaco-fhirpath.ts | 187 +++++++ vue3-src/package-lock.json | 34 +- vue3-src/package.json | 3 +- 4 files changed, 557 insertions(+), 250 deletions(-) create mode 100644 vue3-src/app/utils/monaco-fhirpath.ts diff --git a/vue3-src/app/components/ResourceEditor.vue b/vue3-src/app/components/ResourceEditor.vue index 503d273e..1067eb8e 100644 --- a/vue3-src/app/components/ResourceEditor.vue +++ b/vue3-src/app/components/ResourceEditor.vue @@ -59,41 +59,20 @@ -
- +
+ diff --git a/vue3-src/app/utils/monaco-fhirpath.ts b/vue3-src/app/utils/monaco-fhirpath.ts new file mode 100644 index 00000000..aadcf6d6 --- /dev/null +++ b/vue3-src/app/utils/monaco-fhirpath.ts @@ -0,0 +1,187 @@ +/** + * Monaco Monarch language definition for FHIRPath / FHIR Mapping Language (FML). + * + * Ported from `helpers/fhirpath_highlighter.ts` (Ace highlight rules) so that the + * vue3-src Monaco-based ResourceEditor highlights FML maps and FHIRPath expressions + * with the same token semantics as the legacy Ace editor. + * + * Language id: `fhirpath` + * Theme rules below are merged into the default Monaco "vs" theme via + * `monaco.editor.defineTheme` so the FHIRPath tokens render with sensible colours + * even though they are not part of Monaco's built-in token palette. + */ +import type * as monacoType from 'monaco-editor/esm/vs/editor/editor.api' + +let registered = false + +export function setupFhirPathLanguage(monaco: typeof monacoType): void { + if (registered) return + registered = true + + monaco.languages.register({ id: 'fhirpath' }) + + monaco.languages.setLanguageConfiguration('fhirpath', { + comments: { lineComment: '//', blockComment: ['/*', '*/'] }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'] + ], + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: "'", close: "'", notIn: ['string'] }, + { open: '`', close: '`', notIn: ['string'] } + ], + surroundingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: "'", close: "'" }, + { open: '`', close: '`' } + ] + }) + + // Monarch tokenizer ported from helpers/fhirpath_highlighter.ts + monaco.languages.setMonarchTokensProvider('fhirpath', { + defaultToken: '', + tokenPostfix: '.fhirpath', + + existence: ['empty', 'exists', 'all', 'allTrue', 'anyTrue', 'allFalse', 'anyFalse', 'subsetOf', 'supersetOf', 'count', 'distinct', 'isDistinct'], + filtering: ['where', 'select', 'repeat', 'ofType'], + subsetting: ['single', 'first', 'last', 'tail', 'skip', 'take', 'intersect', 'exclude'], + combining: ['union', 'combine'], + conversion: ['iif', 'toBoolean', 'convertsToBoolean', 'toInteger', 'convertsToInteger', 'toDate', 'convertsToDate', 'toDateTime', 'convertsToDateTime', 'toDecimal', 'convertsToDecimal', 'toQuantity', 'convertsToQuantity', 'toString', 'convertsToString', 'toTime', 'convertsToTime'], + strings: ['indexOf', 'substring', 'startsWith', 'endsWith', 'contains', 'upper', 'lower', 'replace', 'matches', 'replaceMatches', 'length', 'toChars', 'split', 'join', 'encode', 'decode'], + math: ['abs', 'ceiling', 'exp', 'floor', 'ln', 'log', 'power', 'round', 'sqrt', 'truncate'], + treenav: ['children', 'descendants'], + utility: ['trace', 'now', 'timeOfDay', 'today', 'not'], + types: ['as', 'is'], + keywords: ['as', 'is', 'in', 'contains', 'and', 'or', 'xor', 'implies'], + mathKeywords: ['div', 'mod'], + units: ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'year', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'], + + tokenizer: { + root: [ + // Strings (single-quoted) + [/'/, { token: 'string.quote', next: '@fhirString' }], + // Backtick identifiers + [/`/, { token: 'identifier.quote', next: '@bstring' }], + + // Numbers / quantities / temporals (order matters: most specific first) + [/0[xX][0-9a-fA-F]+\b/, 'number.hex'], + [/\b\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b\s+'/, { token: 'number.quantity', next: '@fhirQString' }], + [/\b\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b\s+(?:years|months|weeks|days|hours|minutes|seconds|milliseconds|year|month|week|day|hour|minute|second|millisecond)\b/, 'number.quantity'], + [/\b\d+L\b/, 'number'], + [/\b\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/, 'number.float'], + [/@T[0-9][0-9]:[0-9][0-9](?::[0-9][0-9])?(?:\.[0-9]+)?/, 'string.time'], + [/@[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(?::[0-9][0-9])?(?:\.[0-9]+)?(?:Z|[+\-][0-9]{2}:[0-9]{2})/, 'string.datetime'], + [/@[0-9]{4}(?:-[0-9]{2}(?:-[0-9]{2})?)?/, 'string.date'], + + // Boolean literals + [/\b(?:true|false)\b/, 'constant.language'], + + // Comments + [/\/\/.*$/, 'comment'], + [/\/\*/, { token: 'comment', next: '@blockComment' }], + + // Function names followed by `(` + [/\b(empty|exists|all|allTrue|anyTrue|allFalse|anyFalse|subsetOf|supersetOf|count|distinct|isDistinct)\b(?=\s*\()/, 'support.function.existence'], + [/\b(where|select|repeat|ofType)\b(?=\s*\()/, 'support.function.filtering'], + [/\b(single|first|last|tail|skip|take|intersect|exclude)\b(?=\s*\()/, 'support.function.subsetting'], + [/\b(union|combine)\b(?=\s*\()/, 'support.function.combining'], + [/\b(iif|toBoolean|convertsToBoolean|toInteger|convertsToInteger|toDate|convertsToDate|toDateTime|convertsToDateTime|toDecimal|convertsToDecimal|toQuantity|convertsToQuantity|toString|convertsToString|toTime|convertsToTime)\b(?=\s*\()/, 'support.function.conversion'], + [/\b(indexOf|substring|startsWith|endsWith|contains|upper|lower|replace|matches|replaceMatches|length|toChars|split|join|encode|decode)\b(?=\s*\()/, 'support.function.strings'], + [/\b(abs|ceiling|exp|floor|ln|log|power|round|sqrt|truncate)\b(?=\s*\()/, 'support.function.math'], + [/\b(children|descendants)\b(?=\s*\()/, 'support.function.treenav'], + [/\b(trace|now|timeOfDay|today|not)\b(?=\s*\()/, 'support.function.utility'], + [/\b(as|is)\b(?=\s*\()/, 'support.function.types'], + [/\baggregate\b/, 'support.function.aggregates'], + + // Keywords + [/\b(?:as|is|in|contains|and|or|xor|implies)\b/, 'keyword'], + [/\$this\b/, 'variable.language.this'], + [/\$index\b/, 'variable.language.index'], + [/\$total\b/, 'variable.language.total'], + [/\b(?:div|mod)\b/, 'keyword.operator.math'], + + // External constants (%foo or %`escaped name`) + [/%`/, { token: 'variable.parameter', next: '@fhirVariable' }], + [/%\w+/, 'variable.parameter'], + + // Operators / punctuation + [/[!=<>~]+|[+\-*|]/, 'operator'], + [/[,]/, 'delimiter'], + [/\./, 'delimiter.dot'], + [/[(){}[\]]/, '@brackets'], + + // Identifiers + [/[A-Za-z_][A-Za-z0-9_]*/, 'identifier'], + + // Whitespace + [/\s+/, 'white'] + ], + + fhirString: [ + [/\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\/fnrt'])/, 'string.escape'], + [/'/, { token: 'string.quote', next: '@pop' }], + [/./, 'string'] + ], + + fhirQString: [ + [/\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\/fnrt'])/, 'string.escape'], + [/'/, { token: 'number.quantity', next: '@pop' }], + [/./, 'number.quantity'] + ], + + bstring: [ + [/\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\/fnrt`])/, 'string.escape'], + [/`/, { token: 'identifier.quote', next: '@pop' }], + [/./, 'identifier'] + ], + + fhirVariable: [ + [/\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|["\\/fnrt`])/, 'string.escape'], + [/`/, { token: 'variable.parameter', next: '@pop' }], + [/./, 'variable.parameter'] + ], + + blockComment: [ + [/\*\//, { token: 'comment', next: '@pop' }], + [/./, 'comment'] + ] + } + } as monacoType.languages.IMonarchLanguage) + + // Provide colours for the custom token classes (Monaco "vs" theme has no defaults + // for `support.function.*`, `number.quantity`, etc.). + monaco.editor.defineTheme('fhirpath-lab', { + base: 'vs', + inherit: true, + rules: [ + { token: 'support.function.existence', foreground: '795E26', fontStyle: 'bold' }, + { token: 'support.function.filtering', foreground: '795E26', fontStyle: 'bold' }, + { token: 'support.function.subsetting', foreground: '795E26' }, + { token: 'support.function.combining', foreground: '795E26' }, + { token: 'support.function.conversion', foreground: '267F99' }, + { token: 'support.function.strings', foreground: '795E26' }, + { token: 'support.function.math', foreground: '795E26' }, + { token: 'support.function.treenav', foreground: '795E26' }, + { token: 'support.function.utility', foreground: '795E26' }, + { token: 'support.function.types', foreground: 'AF00DB' }, + { token: 'support.function.aggregates', foreground: '795E26' }, + { token: 'variable.language.this', foreground: '0070C1', fontStyle: 'italic' }, + { token: 'variable.language.index', foreground: '0070C1', fontStyle: 'italic' }, + { token: 'variable.language.total', foreground: '0070C1', fontStyle: 'italic' }, + { token: 'variable.parameter', foreground: '001080' }, + { token: 'keyword.operator.math', foreground: 'AF00DB' }, + { token: 'number.quantity', foreground: '098658' }, + { token: 'string.time', foreground: '098658' }, + { token: 'string.date', foreground: '098658' }, + { token: 'string.datetime', foreground: '098658' }, + { token: 'delimiter.dot', foreground: '000000' } + ], + colors: {} + }) +} diff --git a/vue3-src/package-lock.json b/vue3-src/package-lock.json index 1763b875..f5bd3be6 100644 --- a/vue3-src/package-lock.json +++ b/vue3-src/package-lock.json @@ -12,7 +12,6 @@ "@nuxt/fonts": "^0.14.0", "@types/fhir": "^0.0.41", "@types/lz-string": "^1.5.0", - "ace-builds": "^1.43.6", "antlr4": "^4.13.2", "axios": "^1.16.0", "fhir-extension-helpers": "^0.3.0", @@ -21,10 +20,10 @@ "luxon": "^3.7.2", "lz-string": "^1.5.0", "marked": "^14.1.4", + "monaco-editor": "^0.52.2", "nuxt": "^4.4.2", "vue": "^3.5.32", "vue-router": "^5.0.4", - "vue3-ace-editor": "^2.2.4", "vuetify": "^4.0.5", "vuetify-nuxt-module": "^0.19.5", "xml-formatter": "^3.7.0" @@ -4077,12 +4076,6 @@ "node": ">=6.5" } }, - "node_modules/ace-builds": { - "version": "1.43.6", - "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.43.6.tgz", - "integrity": "sha512-L1ddibQ7F3vyXR2k2fg+I8TQTPWVA6CKeDQr/h2+8CeyTp3W6EQL8xNFZRTztuP8xNOAqL3IYPqdzs31GCjDvg==", - "license": "BSD-3-Clause" - }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -7995,6 +7988,12 @@ "integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==", "license": "MIT" }, + "node_modules/monaco-editor": { + "version": "0.52.2", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz", + "integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==", + "license": "MIT" + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -9339,12 +9338,6 @@ "regexp-tree": "bin/regexp-tree" } }, - "node_modules/resize-observer-polyfill": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", - "license": "MIT" - }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -11245,19 +11238,6 @@ } } }, - "node_modules/vue3-ace-editor": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/vue3-ace-editor/-/vue3-ace-editor-2.2.4.tgz", - "integrity": "sha512-FZkEyfpbH068BwjhMyNROxfEI8135Sc+x8ouxkMdCNkuj/Tuw83VP/gStFQqZHqljyX9/VfMTCdTqtOnJZGN8g==", - "license": "MIT", - "dependencies": { - "resize-observer-polyfill": "^1.5.1" - }, - "peerDependencies": { - "ace-builds": "*", - "vue": "^3" - } - }, "node_modules/vuetify": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-4.0.5.tgz", diff --git a/vue3-src/package.json b/vue3-src/package.json index 039459e3..9fed661b 100644 --- a/vue3-src/package.json +++ b/vue3-src/package.json @@ -15,7 +15,6 @@ "@nuxt/fonts": "^0.14.0", "@types/fhir": "^0.0.41", "@types/lz-string": "^1.5.0", - "ace-builds": "^1.43.6", "antlr4": "^4.13.2", "axios": "^1.16.0", "fhir-extension-helpers": "^0.3.0", @@ -24,10 +23,10 @@ "luxon": "^3.7.2", "lz-string": "^1.5.0", "marked": "^14.1.4", + "monaco-editor": "^0.52.2", "nuxt": "^4.4.2", "vue": "^3.5.32", "vue-router": "^5.0.4", - "vue3-ace-editor": "^2.2.4", "vuetify": "^4.0.5", "vuetify-nuxt-module": "^0.19.5", "xml-formatter": "^3.7.0" From 7bc6721ac66fcbceb9a5dc6298281aa52d8c41b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 22:34:53 +0000 Subject: [PATCH 2/9] Address code review: tighten regex guards and drop dead fallback Agent-Logs-Url: https://github.com/brianpos/fhirpath-lab/sessions/1443ec82-56dc-47c1-9a15-52ddc6c3d401 Co-authored-by: brianpos <1557083+brianpos@users.noreply.github.com> --- vue3-src/app/components/ResourceEditor.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vue3-src/app/components/ResourceEditor.vue b/vue3-src/app/components/ResourceEditor.vue index 1067eb8e..3d38e33a 100644 --- a/vue3-src/app/components/ResourceEditor.vue +++ b/vue3-src/app/components/ResourceEditor.vue @@ -241,8 +241,8 @@ function extractXmlErrorLocation(err: unknown, value: string): { line: number; c // "line N" + "column M" in the message. Extract them best-effort. const lineMatch = /[Ll]ine[:\s]+(\d+)/.exec(message) const colMatch = /[Cc]olumn[:\s]+(\d+)/.exec(message) - if (lineMatch) fallback.line = Math.max(1, parseInt(lineMatch[1]!, 10)) - if (colMatch) fallback.column = Math.max(1, parseInt(colMatch[1]!, 10)) + if (lineMatch && lineMatch[1]) fallback.line = Math.max(1, parseInt(lineMatch[1], 10)) + if (colMatch && colMatch[1]) fallback.column = Math.max(1, parseInt(colMatch[1], 10)) // Clamp to actual document bounds. const totalLines = value.split(/\r\n|\r|\n/).length if (fallback.line > totalLines) fallback.line = totalLines @@ -267,7 +267,7 @@ function walkXmlNodeForDiagnostics(node: IJsonNode, markers: monaco.editor.IMark ) { markers.push({ severity: monaco.MarkerSeverity.Warning, - message: `Unknown FHIR element '${child.text}' in ${node.DataType ?? 'parent'}`, + message: `Unknown FHIR element '${child.text}' in ${node.DataType}`, startLineNumber: child.position.line, startColumn: Math.max(1, child.position.column), endLineNumber: child.position.line, From a2e48f33d6be0bc5d8fe05829b46069528f66e41 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 23:01:43 +0000 Subject: [PATCH 3/9] Keep Monaco-backed ResourceEditor tabs mounted in single-pane mode Agent-Logs-Url: https://github.com/brianpos/fhirpath-lab/sessions/cd288ee8-7efa-4376-b201-41e03619d134 Co-authored-by: brianpos <1557083+brianpos@users.noreply.github.com> --- vue3-src/app/pages/fhirpath.vue | 4 ++++ vue3-src/app/pages/fml.vue | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/vue3-src/app/pages/fhirpath.vue b/vue3-src/app/pages/fhirpath.vue index 36f979c7..ddf5621a 100644 --- a/vue3-src/app/pages/fhirpath.vue +++ b/vue3-src/app/pages/fhirpath.vue @@ -589,6 +589,10 @@ const tabDetails = computed(() => [ tabSubName: "(test input)", show: true, enabled: true, + // Keep the Monaco-backed ResourceEditor mounted across single-pane tab + // switches; tearing it down and re-creating it on every switch is very + // expensive (workers + ResizeObserver + DOM) and can crash/hang the UI. + keepMounted: true, }, { iconName: "mdi-chart-box-multiple-outline", diff --git a/vue3-src/app/pages/fml.vue b/vue3-src/app/pages/fml.vue index 03c6a841..f4d045e3 100644 --- a/vue3-src/app/pages/fml.vue +++ b/vue3-src/app/pages/fml.vue @@ -224,6 +224,10 @@ const tabDetails = computed(() => [ tabName: "Map", show: true, enabled: true, + // Keep Monaco-backed ResourceEditor tabs mounted across single-pane tab + // switches; tearing down and recreating Monaco on every switch is very + // expensive (workers + ResizeObserver + DOM) and can crash/hang the UI. + keepMounted: true, }, { iconName: "mdi-clipboard-text-outline", @@ -232,6 +236,7 @@ const tabDetails = computed(() => [ tabHeaderName: "Test Input Resource", show: true, enabled: true, + keepMounted: true, }, { iconName: "mdi-tree-outline", @@ -240,6 +245,7 @@ const tabDetails = computed(() => [ title: "Logical Models Required\n(Structure Definitions)", show: true, enabled: true, + keepMounted: true, }, { iconName: "mdi-format-list-bulleted", @@ -254,6 +260,7 @@ const tabDetails = computed(() => [ title: "Map Output", show: true, enabled: true, + keepMounted: true, }, { iconName: "mdi-alert-circle-outline", @@ -275,6 +282,7 @@ const tabDetails = computed(() => [ tabName: "Debug", show: showAdvancedSettings.value, enabled: true, + keepMounted: true, }, ]) From 6730f5d47b0f16d160f41856542b91ae0cc9a573 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 23:30:09 +0000 Subject: [PATCH 4/9] Escape `@` in Monarch date/time regexes to fix attribute lookup error Agent-Logs-Url: https://github.com/brianpos/fhirpath-lab/sessions/1207fe21-66d6-4023-95f9-9e25bd562572 Co-authored-by: brianpos <1557083+brianpos@users.noreply.github.com> --- vue3-src/app/utils/monaco-fhirpath.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/vue3-src/app/utils/monaco-fhirpath.ts b/vue3-src/app/utils/monaco-fhirpath.ts index aadcf6d6..9dbf190f 100644 --- a/vue3-src/app/utils/monaco-fhirpath.ts +++ b/vue3-src/app/utils/monaco-fhirpath.ts @@ -75,9 +75,12 @@ export function setupFhirPathLanguage(monaco: typeof monacoType): void { [/\b\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b\s+(?:years|months|weeks|days|hours|minutes|seconds|milliseconds|year|month|week|day|hour|minute|second|millisecond)\b/, 'number.quantity'], [/\b\d+L\b/, 'number'], [/\b\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/, 'number.float'], - [/@T[0-9][0-9]:[0-9][0-9](?::[0-9][0-9])?(?:\.[0-9]+)?/, 'string.time'], - [/@[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(?::[0-9][0-9])?(?:\.[0-9]+)?(?:Z|[+\-][0-9]{2}:[0-9]{2})/, 'string.datetime'], - [/@[0-9]{4}(?:-[0-9]{2}(?:-[0-9]{2})?)?/, 'string.date'], + // Use a character class for the leading `@` so Monaco's Monarch tokenizer + // does not treat `@T`/`@[`... as an attribute reference (which produces + // "language definition does not contain attribute 'T'" errors). + [/[@]T[0-9][0-9]:[0-9][0-9](?::[0-9][0-9])?(?:\.[0-9]+)?/, 'string.time'], + [/[@][0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(?::[0-9][0-9])?(?:\.[0-9]+)?(?:Z|[+\-][0-9]{2}:[0-9]{2})/, 'string.datetime'], + [/[@][0-9]{4}(?:-[0-9]{2}(?:-[0-9]{2})?)?/, 'string.date'], // Boolean literals [/\b(?:true|false)\b/, 'constant.language'], From a8e873e8c47ceac86650f10d3c2d2b4419bdd9c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 23:51:33 +0000 Subject: [PATCH 5/9] Keep DEBUG tab mounted in fhirpath.vue (hosts Monaco ResourceEditor) Agent-Logs-Url: https://github.com/brianpos/fhirpath-lab/sessions/2e61d2d2-8508-45ed-9b77-8fd693cff2a8 Co-authored-by: brianpos <1557083+brianpos@users.noreply.github.com> --- vue3-src/app/pages/fhirpath.vue | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vue3-src/app/pages/fhirpath.vue b/vue3-src/app/pages/fhirpath.vue index ddf5621a..7b5ee807 100644 --- a/vue3-src/app/pages/fhirpath.vue +++ b/vue3-src/app/pages/fhirpath.vue @@ -627,6 +627,10 @@ const tabDetails = computed(() => [ tabName: "DEBUG", show: true, enabled: true, + // The DEBUG tab hosts a Monaco-backed ResourceEditor; keep it mounted + // across single-pane tab switches so Monaco isn't disposed/re-created + // on every switch (which can crash/hang the UI). + keepMounted: true, }, ]) From 78ce2742ca125477f72097a67401f8be324beea6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 22 May 2026 00:07:23 +0000 Subject: [PATCH 6/9] Stop deep-reactive proxy on Monaco editor that hung result navigation Agent-Logs-Url: https://github.com/brianpos/fhirpath-lab/sessions/a9354d9e-dd08-4ebe-8900-b0ff010d4e74 Co-authored-by: brianpos <1557083+brianpos@users.noreply.github.com> --- vue3-src/app/components/ResourceEditor.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vue3-src/app/components/ResourceEditor.vue b/vue3-src/app/components/ResourceEditor.vue index 3d38e33a..cf6f4d6a 100644 --- a/vue3-src/app/components/ResourceEditor.vue +++ b/vue3-src/app/components/ResourceEditor.vue @@ -66,7 +66,7 @@