@vertesia/ui 1.4.0-dev.20260629.130134Z → 1.4.1

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.
Files changed (196) hide show
  1. package/lib/core/components/SidePanel.js +1 -1
  2. package/lib/core/components/SidePanel.js.map +1 -1
  3. package/lib/core/components/shadcn/button.d.ts +10 -3
  4. package/lib/core/components/shadcn/button.d.ts.map +1 -1
  5. package/lib/core/components/shadcn/button.js +41 -9
  6. package/lib/core/components/shadcn/button.js.map +1 -1
  7. package/lib/core/components/shadcn/modal/dialog.js +1 -1
  8. package/lib/core/components/shadcn/modal/dialog.js.map +1 -1
  9. package/lib/core/components/shadcn/resizeable.d.ts +1 -1
  10. package/lib/core/components/shadcn/resizeable.d.ts.map +1 -1
  11. package/lib/core/components/shadcn/resizeable.js +12 -2
  12. package/lib/core/components/shadcn/resizeable.js.map +1 -1
  13. package/lib/core/components/shadcn/tabs.d.ts +12 -1
  14. package/lib/core/components/shadcn/tabs.d.ts.map +1 -1
  15. package/lib/core/components/shadcn/tabs.js +12 -4
  16. package/lib/core/components/shadcn/tabs.js.map +1 -1
  17. package/lib/core/components/shadcn/theme/ThemeSwitcher.js +1 -1
  18. package/lib/core/components/shadcn/theme/ThemeSwitcher.js.map +1 -1
  19. package/lib/core/hooks/useIntersectionObserver.d.ts.map +1 -1
  20. package/lib/core/hooks/useIntersectionObserver.js +3 -1
  21. package/lib/core/hooks/useIntersectionObserver.js.map +1 -1
  22. package/lib/features/agent/chat/AgentChatPlaybackControls.d.ts.map +1 -1
  23. package/lib/features/agent/chat/AgentChatPlaybackControls.js +2 -2
  24. package/lib/features/agent/chat/AgentChatPlaybackControls.js.map +1 -1
  25. package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts.map +1 -1
  26. package/lib/features/agent/chat/AgentRequestInputOverlay.js +2 -2
  27. package/lib/features/agent/chat/AgentRequestInputOverlay.js.map +1 -1
  28. package/lib/features/agent/chat/AgentRightPanel.js +1 -1
  29. package/lib/features/agent/chat/AgentRightPanel.js.map +1 -1
  30. package/lib/features/agent/chat/AskUserWidget.d.ts.map +1 -1
  31. package/lib/features/agent/chat/AskUserWidget.js +4 -3
  32. package/lib/features/agent/chat/AskUserWidget.js.map +1 -1
  33. package/lib/features/agent/chat/ImageLightbox.d.ts.map +1 -1
  34. package/lib/features/agent/chat/ImageLightbox.js +1 -1
  35. package/lib/features/agent/chat/ImageLightbox.js.map +1 -1
  36. package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  37. package/lib/features/agent/chat/ModernAgentConversation.js +57 -12
  38. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  39. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +8 -1
  40. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  41. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +45 -36
  42. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  43. package/lib/features/agent/chat/ModernAgentOutput/Header.d.ts.map +1 -1
  44. package/lib/features/agent/chat/ModernAgentOutput/Header.js +4 -2
  45. package/lib/features/agent/chat/ModernAgentOutput/Header.js.map +1 -1
  46. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
  47. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +7 -3
  48. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
  49. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
  50. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +2 -1
  51. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
  52. package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.d.ts.map +1 -1
  53. package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js +158 -8
  54. package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js.map +1 -1
  55. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts +6 -0
  56. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts.map +1 -1
  57. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js +15 -1
  58. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js.map +1 -1
  59. package/lib/features/agent/chat/hooks/useFileProcessing.d.ts +1 -0
  60. package/lib/features/agent/chat/hooks/useFileProcessing.d.ts.map +1 -1
  61. package/lib/features/agent/chat/hooks/useFileProcessing.js +37 -3
  62. package/lib/features/agent/chat/hooks/useFileProcessing.js.map +1 -1
  63. package/lib/features/magic-pdf/AnnotatedImageSlider.js +13 -13
  64. package/lib/features/magic-pdf/AnnotatedImageSlider.js.map +1 -1
  65. package/lib/features/magic-pdf/DownloadPopover.js +2 -2
  66. package/lib/features/magic-pdf/DownloadPopover.js.map +1 -1
  67. package/lib/features/magic-pdf/MagicPdfView.js +3 -3
  68. package/lib/features/magic-pdf/MagicPdfView.js.map +1 -1
  69. package/lib/features/oauth/McpConnectionsButton.d.ts.map +1 -1
  70. package/lib/features/oauth/McpConnectionsButton.js +2 -2
  71. package/lib/features/oauth/McpConnectionsButton.js.map +1 -1
  72. package/lib/features/oauth/McpConnectionsDialog.d.ts.map +1 -1
  73. package/lib/features/oauth/McpConnectionsDialog.js +1 -1
  74. package/lib/features/oauth/McpConnectionsDialog.js.map +1 -1
  75. package/lib/features/oauth/RemoteMcpConnectionButton.d.ts.map +1 -1
  76. package/lib/features/oauth/RemoteMcpConnectionButton.js +1 -1
  77. package/lib/features/oauth/RemoteMcpConnectionButton.js.map +1 -1
  78. package/lib/features/pdf-viewer/PdfPageSlider.js +2 -2
  79. package/lib/features/pdf-viewer/PdfPageSlider.js.map +1 -1
  80. package/lib/features/pdf-viewer/SimplePdfViewer.d.ts.map +1 -1
  81. package/lib/features/pdf-viewer/SimplePdfViewer.js +3 -3
  82. package/lib/features/pdf-viewer/SimplePdfViewer.js.map +1 -1
  83. package/lib/features/store/objects/DocumentQuickFilter.d.ts.map +1 -1
  84. package/lib/features/store/objects/DocumentQuickFilter.js +5 -5
  85. package/lib/features/store/objects/DocumentQuickFilter.js.map +1 -1
  86. package/lib/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
  87. package/lib/features/store/objects/DocumentSearchResults.js +2 -2
  88. package/lib/features/store/objects/DocumentSearchResults.js.map +1 -1
  89. package/lib/features/store/objects/components/ContentDispositionButton.d.ts.map +1 -1
  90. package/lib/features/store/objects/components/ContentDispositionButton.js +1 -1
  91. package/lib/features/store/objects/components/ContentDispositionButton.js.map +1 -1
  92. package/lib/features/store/objects/components/ContentOverview.js +3 -3
  93. package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
  94. package/lib/features/store/objects/components/DocumentIcon.js +1 -1
  95. package/lib/features/store/objects/components/VectorSearchWidget.js +1 -1
  96. package/lib/features/store/objects/components/VectorSearchWidget.js.map +1 -1
  97. package/lib/features/store/objects/layout/renderers.d.ts.map +1 -1
  98. package/lib/features/store/objects/layout/renderers.js +2 -2
  99. package/lib/features/store/objects/layout/renderers.js.map +1 -1
  100. package/lib/features/store/objects/selection/SelectionActions.js +1 -1
  101. package/lib/features/store/objects/selection/SelectionActions.js.map +1 -1
  102. package/lib/features/store/objects/selection/actions/StartWorkflowComponent.js +4 -4
  103. package/lib/features/store/objects/selection/actions/StartWorkflowComponent.js.map +1 -1
  104. package/lib/features/store/types/ContentObjectTypesTable.d.ts.map +1 -1
  105. package/lib/features/store/types/ContentObjectTypesTable.js +4 -4
  106. package/lib/features/store/types/ContentObjectTypesTable.js.map +1 -1
  107. package/lib/features/store/types/SelectContentTypeModal.js +1 -1
  108. package/lib/features/store/types/SelectContentTypeModal.js.map +1 -1
  109. package/lib/features/user/UserInfo.js +7 -3
  110. package/lib/features/user/UserInfo.js.map +1 -1
  111. package/lib/router/HistoryNavigator.d.ts.map +1 -1
  112. package/lib/router/HistoryNavigator.js +5 -0
  113. package/lib/router/HistoryNavigator.js.map +1 -1
  114. package/lib/router/Nav.d.ts.map +1 -1
  115. package/lib/router/Nav.js +9 -1
  116. package/lib/router/Nav.js.map +1 -1
  117. package/lib/vertesia-ui-core.js +1 -1
  118. package/lib/vertesia-ui-core.js.map +1 -1
  119. package/lib/vertesia-ui-features.js +1 -1
  120. package/lib/vertesia-ui-features.js.map +1 -1
  121. package/lib/vertesia-ui-router.js +1 -1
  122. package/lib/vertesia-ui-router.js.map +1 -1
  123. package/lib/vertesia-ui-widgets.js +1 -1
  124. package/lib/vertesia-ui-widgets.js.map +1 -1
  125. package/lib/widgets/form/Form.js +1 -1
  126. package/lib/widgets/form/Form.js.map +1 -1
  127. package/lib/widgets/json-view/JSONSwitcher.js +1 -1
  128. package/lib/widgets/json-view/JSONSwitcher.js.map +1 -1
  129. package/lib/widgets/json-view/JSONView.js +6 -6
  130. package/lib/widgets/json-view/JSONView.js.map +1 -1
  131. package/lib/widgets/monacoEditor/MonacoEditor.d.ts.map +1 -1
  132. package/lib/widgets/monacoEditor/MonacoEditor.js +29 -0
  133. package/lib/widgets/monacoEditor/MonacoEditor.js.map +1 -1
  134. package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts +9 -0
  135. package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts.map +1 -1
  136. package/lib/widgets/xml-viewer/hooks/useXMLViewer.js +20 -17
  137. package/lib/widgets/xml-viewer/hooks/useXMLViewer.js.map +1 -1
  138. package/package.json +29 -30
  139. package/src/__tests__/a11y.test.tsx +73 -0
  140. package/src/core/components/SidePanel.tsx +1 -1
  141. package/src/core/components/shadcn/button.tsx +67 -12
  142. package/src/core/components/shadcn/modal/dialog.tsx +1 -1
  143. package/src/core/components/shadcn/resizeable.test.tsx +77 -0
  144. package/src/core/components/shadcn/resizeable.tsx +28 -2
  145. package/src/core/components/shadcn/tabs.test.tsx +65 -0
  146. package/src/core/components/shadcn/tabs.tsx +25 -3
  147. package/src/core/components/shadcn/theme/ThemeSwitcher.tsx +1 -1
  148. package/src/core/hooks/useIntersectionObserver.tsx +3 -1
  149. package/src/features/agent/chat/AgentChatPlaybackControls.tsx +0 -5
  150. package/src/features/agent/chat/AgentRequestInputOverlay.test.tsx +31 -0
  151. package/src/features/agent/chat/AgentRequestInputOverlay.tsx +6 -2
  152. package/src/features/agent/chat/AgentRightPanel.tsx +1 -1
  153. package/src/features/agent/chat/AskUserWidget.test.tsx +49 -0
  154. package/src/features/agent/chat/AskUserWidget.tsx +32 -22
  155. package/src/features/agent/chat/ImageLightbox.tsx +0 -1
  156. package/src/features/agent/chat/ModernAgentConversation.test.tsx +246 -1
  157. package/src/features/agent/chat/ModernAgentConversation.tsx +62 -17
  158. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +6 -1
  159. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +81 -49
  160. package/src/features/agent/chat/ModernAgentOutput/Header.tsx +3 -3
  161. package/src/features/agent/chat/ModernAgentOutput/MessageInput.test.tsx +25 -0
  162. package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +5 -3
  163. package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +4 -1
  164. package/src/features/agent/chat/ModernAgentOutput/WorkstreamTabs.tsx +328 -42
  165. package/src/features/agent/chat/ModernAgentOutput/requestInputMessages.test.ts +80 -0
  166. package/src/features/agent/chat/ModernAgentOutput/requestInputMessages.ts +21 -1
  167. package/src/features/agent/chat/hooks/useFileProcessing.test.tsx +70 -0
  168. package/src/features/agent/chat/hooks/useFileProcessing.ts +37 -6
  169. package/src/features/magic-pdf/AnnotatedImageSlider.tsx +62 -66
  170. package/src/features/magic-pdf/DownloadPopover.tsx +2 -2
  171. package/src/features/magic-pdf/MagicPdfView.tsx +3 -3
  172. package/src/features/oauth/McpConnectionsButton.tsx +0 -2
  173. package/src/features/oauth/McpConnectionsDialog.tsx +0 -1
  174. package/src/features/oauth/RemoteMcpConnectionButton.tsx +0 -1
  175. package/src/features/pdf-viewer/PdfPageSlider.tsx +5 -5
  176. package/src/features/pdf-viewer/SimplePdfViewer.tsx +11 -12
  177. package/src/features/store/objects/DocumentQuickFilter.tsx +13 -15
  178. package/src/features/store/objects/DocumentSearchResults.tsx +3 -6
  179. package/src/features/store/objects/components/ContentDispositionButton.tsx +5 -1
  180. package/src/features/store/objects/components/ContentOverview.tsx +11 -11
  181. package/src/features/store/objects/components/DocumentIcon.tsx +1 -1
  182. package/src/features/store/objects/components/VectorSearchWidget.tsx +2 -2
  183. package/src/features/store/objects/layout/renderers.tsx +1 -2
  184. package/src/features/store/objects/selection/SelectionActions.tsx +1 -1
  185. package/src/features/store/objects/selection/actions/StartWorkflowComponent.tsx +6 -5
  186. package/src/features/store/types/ContentObjectTypesTable.tsx +12 -14
  187. package/src/features/store/types/SelectContentTypeModal.tsx +2 -2
  188. package/src/features/user/UserInfo.tsx +7 -7
  189. package/src/router/HistoryNavigator.ts +5 -0
  190. package/src/router/Nav.tsx +12 -3
  191. package/src/widgets/form/Form.tsx +1 -1
  192. package/src/widgets/json-view/JSONSwitcher.tsx +2 -2
  193. package/src/widgets/json-view/JSONView.tsx +7 -7
  194. package/src/widgets/monacoEditor/MonacoEditor.tsx +29 -0
  195. package/src/widgets/xml-viewer/hooks/useXMLViewer.test.ts +26 -0
  196. package/src/widgets/xml-viewer/hooks/useXMLViewer.ts +23 -18
@@ -110,10 +110,10 @@ export function SelectContentTypeModal({
110
110
  )}
111
111
  </ModalBody>
112
112
  <ModalFooter>
113
- <Button variant="ghost" onClick={handleClose} alt={t('modal.cancel')}>
113
+ <Button variant="ghost" onClick={handleClose} title={t('modal.cancel')}>
114
114
  {t('modal.cancel')}
115
115
  </Button>
116
- <Button onClick={handleConfirm} alt={t('store.confirmSelection')}>
116
+ <Button onClick={handleConfirm} title={t('store.confirmSelection')}>
117
117
  {t('modal.confirm')}
118
118
  </Button>
119
119
  </ModalFooter>
@@ -140,7 +140,7 @@ function EmailAgentAvatar({ email, showTitle = false, size = 'md' }: EmailAgentA
140
140
 
141
141
  const description = (
142
142
  <>
143
- <div>{t('user.serviceAccountDescription')}</div>
143
+ <div>{t('user.agentOnBehalfOf')}</div>
144
144
  <div className="text-muted text-sm">
145
145
  <span className="font-semibold">Email:</span> {email}
146
146
  </div>
@@ -158,11 +158,7 @@ function EmailAgentAvatar({ email, showTitle = false, size = 'md' }: EmailAgentA
158
158
  />
159
159
  <Avatar name={email} size={size} className="border-2 border-white dark:border-gray-800" />
160
160
  </div>
161
- {showTitle && (
162
- <div className="text-sm font-semibold truncate">
163
- {t('user.agentOnBehalfOf')} : {email}
164
- </div>
165
- )}
161
+ {showTitle && <div className="text-sm font-semibold truncate">Agent&#8194;({email})</div>}
166
162
  </div>
167
163
  </UserPopoverPanel>
168
164
  );
@@ -260,7 +256,11 @@ function AgentAvatar({
260
256
  </div>
261
257
  {showTitle && (
262
258
  <div className="text-sm font-semibold truncate">
263
- {user ? `Agent (${user.name || user.email})` : apiKey ? `Agent (${apiKey.name})` : title}
259
+ {user
260
+ ? `Agent${'\u2003'}(${user.name || user.email})`
261
+ : apiKey
262
+ ? `Agent (${apiKey.name})`
263
+ : title}
264
264
  </div>
265
265
  )}
266
266
  </div>
@@ -233,6 +233,11 @@ export class HistoryNavigator {
233
233
  this.fireLocationChange(new AfterLocationChangeEvent(type, to, state));
234
234
  };
235
235
  const _linkNavListener = (ev: MouseEvent) => {
236
+ // Let the browser handle modifier / non-primary clicks natively (open in new tab/window,
237
+ // download). Intercepting them here would preventDefault those affordances.
238
+ if (ev.defaultPrevented || ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey || ev.button !== 0) {
239
+ return;
240
+ }
236
241
  const target = ev.target as HTMLElement;
237
242
  // Skip anchors with download attribute or blob: URLs - they are file downloads, not navigation
238
243
  if (
@@ -1,6 +1,15 @@
1
- import type { SyntheticEvent } from 'react';
1
+ import type { MouseEvent, SyntheticEvent } from 'react';
2
2
  import { useNavigate, useRouterContext } from './Router';
3
3
 
4
+ /**
5
+ * True when the click carries a modifier the browser uses for alternate link behavior
6
+ * (open in new tab/window, download) or is not a primary-button click. Such clicks must fall
7
+ * through to the native anchor so the browser can handle them instead of the SPA router.
8
+ */
9
+ function isModifiedClick(ev: MouseEvent): boolean {
10
+ return ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey || ev.button !== 0;
11
+ }
12
+
4
13
  /**
5
14
  * Wraps a <a href="..."> and perform the navigation to href through the router.
6
15
  */
@@ -76,8 +85,8 @@ export function NavLink({
76
85
  const isExternal = /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith('//') || (!!target && target !== '_self');
77
86
  const isInternal = !isAnchorOrEmpty && !isExternal;
78
87
  const resolvedHref = !skipStickyParams && isInternal ? router.getTopRouter().navigator.addStickyParams(href) : href;
79
- const _onClick = (ev: SyntheticEvent) => {
80
- if (ev.defaultPrevented || !isInternal) {
88
+ const _onClick = (ev: MouseEvent) => {
89
+ if (ev.defaultPrevented || !isInternal || isModifiedClick(ev)) {
81
90
  return;
82
91
  }
83
92
  ev.stopPropagation();
@@ -184,7 +184,7 @@ function ListItem({ list, object, onDelete, disabled }: ListItemProps) {
184
184
  return (
185
185
  <div className="flex gap-2 w-full">
186
186
  <div className="flex-1">{renderItemProperty(object, editor)}</div>
187
- <Button variant="ghost" onClick={onDelete} disabled={disabled} alt="Delete">
187
+ <Button variant="ghost" onClick={onDelete} disabled={disabled} title="Delete">
188
188
  <Trash2 className="size-4 text-destructive" />
189
189
  </Button>
190
190
  </div>
@@ -14,7 +14,7 @@ export function JSONSwitcher({
14
14
  <Button
15
15
  variant={viewCode ? 'ghost' : 'primary'}
16
16
  size="sm"
17
- alt="Preview properties"
17
+ title="Preview properties"
18
18
  onClick={() => setViewCode(false)}
19
19
  >
20
20
  {title}
@@ -22,7 +22,7 @@ export function JSONSwitcher({
22
22
  <Button
23
23
  variant={viewCode ? 'primary' : 'ghost'}
24
24
  size="sm"
25
- alt="View in JSON format"
25
+ title="View in JSON format"
26
26
  onClick={() => setViewCode(true)}
27
27
  >
28
28
  JSON
@@ -35,7 +35,7 @@ interface PropertyTitleProps {
35
35
  name: string;
36
36
  }
37
37
  function PropertyTitle({ name }: PropertyTitleProps) {
38
- return <div className="text-md font-semibold">{computeTitleFromName(name)}</div>;
38
+ return <div className="text-sm font-semibold">{computeTitleFromName(name)}</div>;
39
39
  }
40
40
 
41
41
  interface BlockElementProps {
@@ -64,14 +64,14 @@ function PropertyElement({ name, value }: PropertyElementProps) {
64
64
  switch (info.type) {
65
65
  case ValueType.Inline:
66
66
  return (
67
- <div className="w-full flex gap-2">
67
+ <div className="w-full flex gap-2 text-sm">
68
68
  <PropertyTitle name={`${name}:`} />
69
69
  <p>{info.value}</p>
70
70
  </div>
71
71
  );
72
72
  case ValueType.Paragraph:
73
73
  return (
74
- <div>
74
+ <div className="w-full flex flex-col gap-2 text-sm">
75
75
  <PropertyTitle name={name} />
76
76
  <p>{info.value}</p>
77
77
  </div>
@@ -80,14 +80,14 @@ function PropertyElement({ name, value }: PropertyElementProps) {
80
80
  return (
81
81
  <div className="prose dark:prose-invert">
82
82
  <PropertyTitle name={name} />
83
- <div className="vprose dark:prose-invert">{info.value}</div>
83
+ <div className="vprose prose-sm dark:prose-invert">{info.value}</div>
84
84
  </div>
85
85
  );
86
86
  case ValueType.Array:
87
87
  return <ArrayProperty name={name} value={value as JSONArray} />;
88
88
  case ValueType.Object:
89
89
  return (
90
- <div>
90
+ <div className="w-full flex flex-col gap-2 text-sm">
91
91
  <PropertyTitle name={name} />
92
92
  <BlockElement className="mt-2">
93
93
  {Object.entries(value as JSONObject).map(([key, value]) => (
@@ -164,8 +164,8 @@ function ItemProperty({ index, value, useBullet }: ItemPropertyProps) {
164
164
  break;
165
165
  }
166
166
  return (
167
- <div className="flex gap-4 hover:bg-slate-50 dark:hover:bg-slate-800 py-2 pe-2 ps-4">
168
- <div className="font-semibold text-gray-600 dark:text-gray-400">{bullet}</div>
167
+ <div className="flex gap-4 hover:bg-muted py-2 pe-2 ps-4">
168
+ <div className="font-semibold text-muted">{bullet}</div>
169
169
  <div>{content}</div>
170
170
  </div>
171
171
  );
@@ -8,6 +8,33 @@ import { registerCustomFoldingProviders } from './foldingProviders.js';
8
8
 
9
9
  export type Monaco = typeof monaco;
10
10
 
11
+ /**
12
+ * Shared, body-level host for Monaco "overflow widgets" — the suggestion, hover, and
13
+ * parameter-hint popups. With `fixedOverflowWidgets` enabled these are `position: fixed`; a CSS
14
+ * `transform` on any ancestor (e.g. a modal centered with `translate(-50%, -50%)`) becomes their
15
+ * containing block and drags the popups away from the cursor, sometimes off-screen. Hosting them in
16
+ * an untransformed node appended to `document.body` keeps them anchored to the caret. The node
17
+ * carries the `monaco-editor` class so Monaco's (global) theme styles apply, and a high z-index so
18
+ * the popups render above modals/overlays. One shared node is reused by every editor instance.
19
+ */
20
+ let overflowWidgetsNode: HTMLElement | undefined;
21
+ function getOverflowWidgetsNode(): HTMLElement | undefined {
22
+ if (typeof document === 'undefined') return undefined;
23
+ if (!overflowWidgetsNode) {
24
+ const node = document.createElement('div');
25
+ node.className = 'monaco-editor';
26
+ // `absolute` + z-index establishes a stacking context above modals without creating a
27
+ // containing block for the fixed-positioned popups (only transform/filter/etc. would).
28
+ node.style.position = 'absolute';
29
+ node.style.top = '0';
30
+ node.style.left = '0';
31
+ node.style.zIndex = '10000';
32
+ document.body.appendChild(node);
33
+ overflowWidgetsNode = node;
34
+ }
35
+ return overflowWidgetsNode;
36
+ }
37
+
11
38
  export interface IEditorApi {
12
39
  getValue(): string;
13
40
  setValue(value?: string): void;
@@ -226,6 +253,8 @@ export function MonacoEditor({
226
253
  tabSize: 2,
227
254
  insertSpaces: true,
228
255
  fixedOverflowWidgets: true, // Hover/diagnostic popovers float outside the editor bounds
256
+ // Host the fixed popups in a body-level node so a modal's CSS transform doesn't offset them.
257
+ overflowWidgetsDomNode: getOverflowWidgetsNode(),
229
258
  glyphMargin: true, // Enable better error reporting
230
259
  renderValidationDecorations: 'on', // Show error squiggles
231
260
  renderLineHighlight: 'line', // Highlight entire line for errors
@@ -0,0 +1,26 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { parseXML } from './useXMLViewer';
3
+
4
+ describe('parseXML', () => {
5
+ it('parses valid XML', () => {
6
+ const result = parseXML('<root><item id="1">value</item></root>');
7
+
8
+ expect(result.valid).toBe(true);
9
+ expect(result.json).not.toBeNull();
10
+ });
11
+
12
+ it('allows parsererror elements in valid XML content', () => {
13
+ const result = parseXML('<root><parsererror>expected content</parsererror></root>');
14
+
15
+ expect(result.valid).toBe(true);
16
+ expect(result.json).not.toBeNull();
17
+ });
18
+
19
+ it('rejects invalid XML', () => {
20
+ const result = parseXML('<root><item></root>');
21
+
22
+ expect(result.valid).toBe(false);
23
+ expect(result.json).toBeNull();
24
+ expect(result.errorMessage).toContain('Fail to parse:');
25
+ });
26
+ });
@@ -1,5 +1,4 @@
1
1
  import { XMLParser } from 'fast-xml-parser';
2
- import { SyntaxValidator } from 'fast-xml-validator';
3
2
  import { useMemo } from 'react';
4
3
  import { ATTRIBUTE_CDATA, ATTRIBUTE_COMMENT } from '../constants';
5
4
 
@@ -13,24 +12,30 @@ const parser = new XMLParser({
13
12
  parseTagValue: false,
14
13
  });
15
14
 
16
- export default function useXMLViewer(xml: string) {
17
- return useMemo(() => {
18
- try {
19
- const validationResult = SyntaxValidator.validate(xml);
20
- if (validationResult !== true) {
21
- throw new Error(validationResult.err.msg || 'Invalid XML!');
22
- }
23
-
24
- const json = parser.parse(xml);
15
+ export function parseXML(xml: string) {
16
+ try {
17
+ const validationDocument = new DOMParser().parseFromString(xml, 'application/xml');
18
+ const parserError =
19
+ validationDocument.documentElement.localName === 'parsererror'
20
+ ? validationDocument.documentElement
21
+ : undefined;
22
+ if (parserError) {
23
+ throw new Error(parserError.textContent?.trim() || 'Invalid XML!');
24
+ }
25
25
 
26
- if (typeof xml === 'string' && xml.trim().length > 0 && json.length === 0) {
27
- throw new Error('Invalid XML!');
28
- }
26
+ const json = parser.parse(xml);
29
27
 
30
- return { json, valid: true };
31
- } catch (e) {
32
- const error = e as Error;
33
- return { json: null, valid: false, errorMessage: `Fail to parse: ${error.message}` };
28
+ if (xml.trim().length > 0 && json.length === 0) {
29
+ throw new Error('Invalid XML!');
34
30
  }
35
- }, [xml]);
31
+
32
+ return { json, valid: true };
33
+ } catch (error: unknown) {
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ return { json: null, valid: false, errorMessage: `Fail to parse: ${message}` };
36
+ }
37
+ }
38
+
39
+ export default function useXMLViewer(xml: string) {
40
+ return useMemo(() => parseXML(xml), [xml]);
36
41
  }