@iloveagents/foundry-web-ui 0.3.0 → 0.4.0

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 (223) hide show
  1. package/README.md +14 -5
  2. package/dist/components/ag-ui-runtime-provider.d.ts +64 -0
  3. package/dist/components/ag-ui-runtime-provider.js +71 -0
  4. package/dist/components/app-brand.d.ts +6 -0
  5. package/dist/components/app-brand.js +13 -0
  6. package/dist/components/assistant-chat.d.ts +26 -0
  7. package/dist/components/assistant-chat.js +93 -0
  8. package/dist/components/chat-attachments.d.ts +25 -0
  9. package/dist/components/chat-attachments.js +20 -0
  10. package/dist/components/chat-bubble.d.ts +1 -0
  11. package/dist/components/chat-bubble.js +103 -0
  12. package/dist/components/chat-header.d.ts +2 -0
  13. package/dist/components/chat-header.js +16 -0
  14. package/dist/components/client-tool-executor.d.ts +12 -0
  15. package/dist/components/client-tool-executor.js +108 -0
  16. package/dist/components/collection-empty-state.d.ts +9 -0
  17. package/dist/components/collection-empty-state.js +6 -0
  18. package/dist/components/collection-filter-bar.d.ts +19 -0
  19. package/dist/components/collection-filter-bar.js +28 -0
  20. package/dist/components/collection-search-input.d.ts +6 -0
  21. package/dist/components/collection-search-input.js +8 -0
  22. package/dist/components/collection-skeleton.d.ts +6 -0
  23. package/dist/components/collection-skeleton.js +10 -0
  24. package/dist/components/collection-sort-menu.d.ts +10 -0
  25. package/dist/components/collection-sort-menu.js +9 -0
  26. package/dist/components/collection-surface.d.ts +8 -0
  27. package/dist/components/collection-surface.js +5 -0
  28. package/dist/components/collection-toolbar.d.ts +9 -0
  29. package/dist/components/collection-toolbar.js +5 -0
  30. package/dist/components/collection-view-toggle.d.ts +7 -0
  31. package/dist/components/collection-view-toggle.js +17 -0
  32. package/dist/components/composer-submit-bridge.d.ts +1 -0
  33. package/dist/components/composer-submit-bridge.js +15 -0
  34. package/dist/components/confirm-dialog.d.ts +11 -0
  35. package/dist/components/confirm-dialog.js +6 -0
  36. package/dist/components/confirmation-card.d.ts +14 -0
  37. package/dist/components/confirmation-card.js +12 -0
  38. package/dist/components/context-badges.d.ts +16 -0
  39. package/dist/components/context-badges.js +56 -0
  40. package/dist/components/context-bar.d.ts +41 -0
  41. package/dist/components/context-bar.js +123 -0
  42. package/dist/components/form-dialog.d.ts +12 -0
  43. package/dist/components/form-dialog.js +13 -0
  44. package/dist/components/global-selection-popover.d.ts +7 -0
  45. package/dist/components/global-selection-popover.js +167 -0
  46. package/dist/components/infinite-scroll-sentinel.d.ts +6 -0
  47. package/dist/components/infinite-scroll-sentinel.js +25 -0
  48. package/dist/components/json-viewer.d.ts +10 -0
  49. package/dist/components/json-viewer.js +80 -0
  50. package/dist/components/loading-indicator.d.ts +4 -0
  51. package/dist/components/loading-indicator.js +22 -0
  52. package/dist/components/markdown-text.d.ts +13 -0
  53. package/dist/components/markdown-text.js +221 -0
  54. package/dist/components/name-dialog.d.ts +16 -0
  55. package/dist/components/name-dialog.js +23 -0
  56. package/dist/components/selection-popover.d.ts +12 -0
  57. package/dist/components/selection-popover.js +111 -0
  58. package/dist/components/show-document-tool-ui.d.ts +1 -0
  59. package/dist/components/show-document-tool-ui.js +53 -0
  60. package/dist/components/sidebar.d.ts +4 -0
  61. package/dist/components/sidebar.js +904 -0
  62. package/dist/components/surface-card.d.ts +9 -0
  63. package/dist/components/surface-card.js +33 -0
  64. package/dist/components/theme-runtime-provider.d.ts +15 -0
  65. package/dist/components/theme-runtime-provider.js +48 -0
  66. package/dist/components/theme-toggle.d.ts +2 -0
  67. package/dist/components/theme-toggle.js +19 -0
  68. package/dist/components/tool-call-card.d.ts +27 -0
  69. package/dist/components/tool-call-card.js +13 -0
  70. package/dist/components/tool-fallback.d.ts +26 -0
  71. package/dist/components/tool-fallback.js +129 -0
  72. package/dist/components/tool-panel-layout.d.ts +10 -0
  73. package/dist/components/tool-panel-layout.js +12 -0
  74. package/dist/components/tool-panel.d.ts +1 -0
  75. package/dist/components/tool-panel.js +89 -0
  76. package/dist/components/tooltip-icon-button.d.ts +7 -0
  77. package/dist/components/tooltip-icon-button.js +9 -0
  78. package/dist/components/user-menu.d.ts +2 -0
  79. package/dist/components/user-menu.js +14 -0
  80. package/dist/index.d.ts +68 -0
  81. package/dist/index.js +83 -0
  82. package/dist/lib/ag-ui-adapter.d.ts +29 -0
  83. package/dist/lib/ag-ui-adapter.js +373 -0
  84. package/dist/lib/app-store.d.ts +115 -0
  85. package/dist/lib/app-store.js +199 -0
  86. package/dist/lib/attachment-adapter.d.ts +20 -0
  87. package/dist/lib/attachment-adapter.js +99 -0
  88. package/dist/lib/auth-provider.d.ts +16 -0
  89. package/dist/lib/auth-provider.js +88 -0
  90. package/dist/lib/chat-bubble-store.d.ts +30 -0
  91. package/dist/lib/chat-bubble-store.js +23 -0
  92. package/dist/lib/chat-lifecycle-store.d.ts +10 -0
  93. package/{src/lib/chat-lifecycle-store.ts → dist/lib/chat-lifecycle-store.js} +3 -13
  94. package/dist/lib/client-tools.d.ts +11 -0
  95. package/dist/lib/client-tools.js +274 -0
  96. package/dist/lib/composer-submit-store.d.ts +12 -0
  97. package/dist/lib/composer-submit-store.js +17 -0
  98. package/dist/lib/dev-store.d.ts +38 -0
  99. package/dist/lib/dev-store.js +32 -0
  100. package/dist/lib/nav-config.d.ts +159 -0
  101. package/dist/lib/nav-config.js +162 -0
  102. package/dist/lib/nav-dnd.d.ts +8 -0
  103. package/dist/lib/nav-dnd.js +9 -0
  104. package/dist/lib/nav-store.d.ts +45 -0
  105. package/dist/lib/nav-store.js +49 -0
  106. package/dist/lib/selection-context.d.ts +21 -0
  107. package/dist/lib/selection-context.js +34 -0
  108. package/dist/lib/sidebar-store.d.ts +23 -0
  109. package/dist/lib/sidebar-store.js +98 -0
  110. package/dist/lib/theme-runtime.d.ts +130 -0
  111. package/dist/lib/theme-runtime.js +479 -0
  112. package/dist/lib/theme-store.d.ts +15 -0
  113. package/dist/lib/theme-store.js +53 -0
  114. package/dist/lib/tool-panel-store.d.ts +48 -0
  115. package/dist/lib/tool-panel-store.js +69 -0
  116. package/dist/lib/use-new-conversation.d.ts +8 -0
  117. package/dist/lib/use-new-conversation.js +62 -0
  118. package/dist/lib/use-page-tools.d.ts +27 -0
  119. package/{src/lib/use-page-tools.ts → dist/lib/use-page-tools.js} +11 -13
  120. package/dist/styles.css +142 -0
  121. package/dist/ui/collapsible.d.ts +5 -0
  122. package/{src/ui/collapsible.tsx → dist/ui/collapsible.js} +0 -2
  123. package/dist/ui/dialog.d.ts +15 -0
  124. package/dist/ui/dialog.js +25 -0
  125. package/dist/ui/dropdown-menu.d.ts +7 -0
  126. package/dist/ui/dropdown-menu.js +19 -0
  127. package/dist/ui/popover.d.ts +7 -0
  128. package/dist/ui/popover.js +10 -0
  129. package/dist/ui/select.d.ts +4 -0
  130. package/dist/ui/select.js +9 -0
  131. package/dist/ui/tooltip.d.ts +7 -0
  132. package/dist/ui/tooltip.js +10 -0
  133. package/package.json +31 -12
  134. package/AGENTS.md +0 -59
  135. package/CHANGELOG.md +0 -203
  136. package/CLAUDE.md +0 -1
  137. package/src/__tests__/no-spaces-imports.test.ts +0 -59
  138. package/src/__tests__/open-core-guards.test.ts +0 -307
  139. package/src/__tests__/theme-runtime.test.ts +0 -81
  140. package/src/__tests__/tool-fallback.test.tsx +0 -109
  141. package/src/components/__tests__/context-bar.test.ts +0 -112
  142. package/src/components/ag-ui-runtime-provider.tsx +0 -170
  143. package/src/components/app-brand.tsx +0 -38
  144. package/src/components/assistant-chat.tsx +0 -461
  145. package/src/components/chat-attachments.tsx +0 -68
  146. package/src/components/chat-bubble.tsx +0 -373
  147. package/src/components/chat-header.tsx +0 -44
  148. package/src/components/client-tool-executor.tsx +0 -230
  149. package/src/components/collection-empty-state.tsx +0 -37
  150. package/src/components/collection-filter-bar.tsx +0 -168
  151. package/src/components/collection-search-input.tsx +0 -41
  152. package/src/components/collection-skeleton.tsx +0 -55
  153. package/src/components/collection-sort-menu.tsx +0 -62
  154. package/src/components/collection-surface.tsx +0 -46
  155. package/src/components/collection-toolbar.tsx +0 -33
  156. package/src/components/collection-view-toggle.tsx +0 -61
  157. package/src/components/composer-submit-bridge.tsx +0 -19
  158. package/src/components/confirm-dialog.tsx +0 -49
  159. package/src/components/confirmation-card.tsx +0 -32
  160. package/src/components/context-badges.tsx +0 -136
  161. package/src/components/context-bar.tsx +0 -238
  162. package/src/components/form-dialog.tsx +0 -56
  163. package/src/components/global-selection-popover.tsx +0 -266
  164. package/src/components/infinite-scroll-sentinel.tsx +0 -48
  165. package/src/components/json-viewer.tsx +0 -232
  166. package/src/components/loading-indicator.tsx +0 -40
  167. package/src/components/markdown-text-citations.test.tsx +0 -108
  168. package/src/components/markdown-text.tsx +0 -508
  169. package/src/components/name-dialog.tsx +0 -87
  170. package/src/components/selection-popover.tsx +0 -156
  171. package/src/components/show-document-tool-ui.tsx +0 -90
  172. package/src/components/sidebar.test.tsx +0 -611
  173. package/src/components/sidebar.tsx +0 -1689
  174. package/src/components/surface-card.tsx +0 -45
  175. package/src/components/theme-runtime-provider.tsx +0 -93
  176. package/src/components/theme-toggle.tsx +0 -27
  177. package/src/components/tool-call-card.tsx +0 -126
  178. package/src/components/tool-fallback.tsx +0 -169
  179. package/src/components/tool-panel-layout.tsx +0 -36
  180. package/src/components/tool-panel.tsx +0 -233
  181. package/src/components/tooltip-icon-button.tsx +0 -28
  182. package/src/components/user-menu.tsx +0 -60
  183. package/src/index.ts +0 -185
  184. package/src/lib/__tests__/ag-ui-adapter.test.ts +0 -273
  185. package/src/lib/__tests__/app-store.test.ts +0 -405
  186. package/src/lib/__tests__/attachment-adapter.test.ts +0 -48
  187. package/src/lib/__tests__/chat-bubble-store.test.ts +0 -67
  188. package/src/lib/__tests__/client-tools.test.ts +0 -124
  189. package/src/lib/__tests__/dev-store.test.ts +0 -78
  190. package/src/lib/__tests__/nav-config.test.ts +0 -135
  191. package/src/lib/__tests__/nav-dnd.test.ts +0 -24
  192. package/src/lib/__tests__/nav-store.test.ts +0 -59
  193. package/src/lib/__tests__/selection-context.test.ts +0 -114
  194. package/src/lib/__tests__/sidebar-store.test.ts +0 -144
  195. package/src/lib/__tests__/theme-store.test.ts +0 -83
  196. package/src/lib/__tests__/tool-panel-store.test.ts +0 -61
  197. package/src/lib/__tests__/use-page-context.test.ts +0 -58
  198. package/src/lib/ag-ui-adapter.ts +0 -412
  199. package/src/lib/app-store.ts +0 -361
  200. package/src/lib/attachment-adapter.ts +0 -92
  201. package/src/lib/auth-provider.tsx +0 -144
  202. package/src/lib/chat-bubble-store.ts +0 -46
  203. package/src/lib/client-tools.ts +0 -293
  204. package/src/lib/composer-submit-store.ts +0 -32
  205. package/src/lib/dev-store.ts +0 -69
  206. package/src/lib/nav-config.ts +0 -329
  207. package/src/lib/nav-dnd.ts +0 -14
  208. package/src/lib/nav-store.ts +0 -89
  209. package/src/lib/selection-context.ts +0 -65
  210. package/src/lib/sidebar-store.ts +0 -113
  211. package/src/lib/theme-runtime.ts +0 -660
  212. package/src/lib/theme-store.ts +0 -67
  213. package/src/lib/tool-panel-store.ts +0 -121
  214. package/src/lib/use-new-conversation.test.tsx +0 -168
  215. package/src/lib/use-new-conversation.ts +0 -64
  216. package/src/ui/dialog.tsx +0 -109
  217. package/src/ui/dropdown-menu.tsx +0 -75
  218. package/src/ui/popover.tsx +0 -34
  219. package/src/ui/select.tsx +0 -15
  220. package/src/ui/tooltip.tsx +0 -30
  221. package/src/vite-env.d.ts +0 -1
  222. package/tsconfig.json +0 -16
  223. package/vitest.config.ts +0 -8
@@ -1,48 +0,0 @@
1
- import { useEffect, useRef } from "react";
2
-
3
- import { cn } from "@iloveagents/foundry-web-primitives";
4
-
5
- export function InfiniteScrollSentinel({
6
- enabled,
7
- loading,
8
- onVisible,
9
- className,
10
- }: {
11
- enabled: boolean;
12
- loading?: boolean;
13
- onVisible: () => void;
14
- className?: string;
15
- }) {
16
- const ref = useRef<HTMLDivElement | null>(null);
17
- const pendingRef = useRef(false);
18
-
19
- useEffect(() => {
20
- if (!enabled || !loading) {
21
- pendingRef.current = false;
22
- }
23
- }, [enabled, loading]);
24
-
25
- useEffect(() => {
26
- if (!enabled || !ref.current) return;
27
- const observer = new IntersectionObserver(
28
- (entries) => {
29
- if (entries.some((entry) => entry.isIntersecting) && !pendingRef.current) {
30
- pendingRef.current = true;
31
- onVisible();
32
- }
33
- },
34
- { rootMargin: "240px 0px" },
35
- );
36
- observer.observe(ref.current);
37
- return () => observer.disconnect();
38
- }, [enabled, onVisible]);
39
-
40
- return (
41
- <div
42
- ref={ref}
43
- className={cn("flex h-12 items-center justify-center text-sm text-muted-foreground", className)}
44
- >
45
- {enabled ? (loading ? "Loading more..." : "Scroll for more") : null}
46
- </div>
47
- );
48
- }
@@ -1,232 +0,0 @@
1
- /**
2
- * JsonViewer — Collapsible JSON tree renderer.
3
- *
4
- * Fetches JSON from a URL and renders it as an interactive tree
5
- * with syntax coloring and expand/collapse controls.
6
- */
7
-
8
- import { useEffect, useState } from "react";
9
- import { ChevronDown, ChevronRight } from "lucide-react";
10
- import { cn } from "@iloveagents/foundry-web-primitives";
11
-
12
- export function JsonViewer({
13
- url,
14
- defaultExpanded = 2,
15
- }: {
16
- url: string;
17
- defaultExpanded?: number;
18
- }) {
19
- const [state, setState] = useState<{
20
- data: unknown;
21
- loading: boolean;
22
- error: boolean;
23
- }>({ data: null, loading: true, error: false });
24
-
25
- useEffect(() => {
26
- let cancelled = false;
27
- fetch(url)
28
- .then((r) => {
29
- if (!r.ok) throw new Error("Failed to fetch");
30
- return r.json();
31
- })
32
- .then((json) => {
33
- if (!cancelled) setState({ data: json, loading: false, error: false });
34
- })
35
- .catch(() => {
36
- if (!cancelled) setState({ data: null, loading: false, error: true });
37
- });
38
- return () => {
39
- cancelled = true;
40
- };
41
- }, [url]);
42
-
43
- const { data, loading, error } = state;
44
-
45
- if (loading) {
46
- return (
47
- <div className="flex-1 flex items-center justify-center text-sm text-muted-foreground p-12">
48
- Loading...
49
- </div>
50
- );
51
- }
52
-
53
- if (error || data == null) {
54
- return (
55
- <div className="flex-1 flex items-center justify-center text-sm text-muted-foreground p-12">
56
- Failed to load JSON
57
- </div>
58
- );
59
- }
60
-
61
- return (
62
- <div className="flex-1 overflow-auto p-6">
63
- <div className="font-mono text-sm max-w-5xl">
64
- <JsonNode value={data} depth={0} defaultExpanded={defaultExpanded} />
65
- </div>
66
- </div>
67
- );
68
- }
69
-
70
- // ---------------------------------------------------------------------------
71
- // JsonNode — recursive tree node
72
- // ---------------------------------------------------------------------------
73
-
74
- function JsonNode({
75
- label,
76
- value,
77
- depth,
78
- defaultExpanded,
79
- }: {
80
- label?: string;
81
- value: unknown;
82
- depth: number;
83
- defaultExpanded: number;
84
- }) {
85
- const [expanded, setExpanded] = useState(depth < defaultExpanded);
86
-
87
- if (value === null || value === undefined) {
88
- return (
89
- <span>
90
- {label != null && <KeyLabel text={label} />}
91
- <span className="text-muted-foreground italic">null</span>
92
- </span>
93
- );
94
- }
95
-
96
- if (typeof value === "boolean") {
97
- return (
98
- <span>
99
- {label != null && <KeyLabel text={label} />}
100
- <span className="text-[var(--syntax-boolean)]">{String(value)}</span>
101
- </span>
102
- );
103
- }
104
-
105
- if (typeof value === "number") {
106
- return (
107
- <span>
108
- {label != null && <KeyLabel text={label} />}
109
- <span className="text-[var(--syntax-number)]">{value}</span>
110
- </span>
111
- );
112
- }
113
-
114
- if (typeof value === "string") {
115
- const display = value.length > 200 ? value.slice(0, 200) + "..." : value;
116
- return (
117
- <span>
118
- {label != null && <KeyLabel text={label} />}
119
- <span className="text-[var(--syntax-string)]">"{display}"</span>
120
- </span>
121
- );
122
- }
123
-
124
- if (Array.isArray(value)) {
125
- if (value.length === 0) {
126
- return (
127
- <span>
128
- {label != null && <KeyLabel text={label} />}
129
- <span className="text-muted-foreground">[]</span>
130
- </span>
131
- );
132
- }
133
-
134
- return (
135
- <div>
136
- <ToggleButton expanded={expanded} onToggle={() => setExpanded(!expanded)}>
137
- {label != null && <KeyLabel text={label} />}
138
- <span className="text-muted-foreground ml-1">[{value.length} items]</span>
139
- </ToggleButton>
140
- {expanded && (
141
- <div className={cn("border-l border-border ml-2 pl-3", depth > 4 && "ml-1 pl-2")}>
142
- {value.map((item, i) => (
143
- <div key={i} className="py-0.5">
144
- <JsonNode
145
- label={String(i)}
146
- value={item}
147
- depth={depth + 1}
148
- defaultExpanded={defaultExpanded}
149
- />
150
- </div>
151
- ))}
152
- </div>
153
- )}
154
- </div>
155
- );
156
- }
157
-
158
- if (typeof value === "object") {
159
- const entries = Object.entries(value as Record<string, unknown>);
160
- if (entries.length === 0) {
161
- return (
162
- <span>
163
- {label != null && <KeyLabel text={label} />}
164
- <span className="text-muted-foreground">{"{}"}</span>
165
- </span>
166
- );
167
- }
168
-
169
- return (
170
- <div>
171
- <ToggleButton expanded={expanded} onToggle={() => setExpanded(!expanded)}>
172
- {label != null && <KeyLabel text={label} />}
173
- <span className="text-muted-foreground ml-1">
174
- {"{"}
175
- {entries.length} keys
176
- {"}"}
177
- </span>
178
- </ToggleButton>
179
- {expanded && (
180
- <div className={cn("border-l border-border ml-2 pl-3", depth > 4 && "ml-1 pl-2")}>
181
- {entries.map(([key, val]) => (
182
- <div key={key} className="py-0.5">
183
- <JsonNode
184
- label={key}
185
- value={val}
186
- depth={depth + 1}
187
- defaultExpanded={defaultExpanded}
188
- />
189
- </div>
190
- ))}
191
- </div>
192
- )}
193
- </div>
194
- );
195
- }
196
-
197
- return (
198
- <span>
199
- {label != null && <KeyLabel text={label} />}
200
- <span>{String(value)}</span>
201
- </span>
202
- );
203
- }
204
-
205
- function KeyLabel({ text }: { text: string }) {
206
- return <span className="text-[var(--syntax-key)]">{text}: </span>;
207
- }
208
-
209
- function ToggleButton({
210
- expanded,
211
- onToggle,
212
- children,
213
- }: {
214
- expanded: boolean;
215
- onToggle: () => void;
216
- children: React.ReactNode;
217
- }) {
218
- return (
219
- <button
220
- type="button"
221
- onClick={onToggle}
222
- className="inline-flex items-center gap-0.5 hover:bg-muted/50 rounded px-0.5 -ml-0.5"
223
- >
224
- {expanded ? (
225
- <ChevronDown className="size-3.5 text-muted-foreground" />
226
- ) : (
227
- <ChevronRight className="size-3.5 text-muted-foreground" />
228
- )}
229
- {children}
230
- </button>
231
- );
232
- }
@@ -1,40 +0,0 @@
1
- import { useStore } from "zustand";
2
- import { streamingStatusStore } from "@iloveagents/foundry-agent";
3
-
4
- function formatToolName(name: string): string {
5
- return name.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
6
- }
7
-
8
- /** Shimmer bar shown above the composer while the agent is running. */
9
- export function LoadingBar() {
10
- return (
11
- <div className="w-full h-px bg-primary/30 overflow-hidden">
12
- <div className="h-full w-1/4 bg-primary animate-[shimmer_1.5s_ease-in-out_infinite]" />
13
- </div>
14
- );
15
- }
16
-
17
- /** Inline status text shown in the message area while the agent is running. */
18
- export function LoadingIndicator() {
19
- const streaming = useStore(streamingStatusStore, (s) => s.streamingStatus);
20
-
21
- let label = "Thinking";
22
- if (streaming.status === "calling" && streaming.toolName) {
23
- label = `Running ${formatToolName(streaming.toolName)}`;
24
- } else if (streaming.status === "streaming") {
25
- label = "Generating response";
26
- }
27
-
28
- return (
29
- <div className="h-10 px-4">
30
- <div className="flex items-center gap-2 py-2 text-foreground leading-relaxed animate-in fade-in duration-300">
31
- <span className="text-sm text-muted-foreground">{label}</span>
32
- <div className="flex gap-1">
33
- <span className="size-1.5 rounded-full bg-primary/60 animate-[pulse_1.4s_ease-in-out_infinite]" />
34
- <span className="size-1.5 rounded-full bg-primary/60 animate-[pulse_1.4s_ease-in-out_0.2s_infinite]" />
35
- <span className="size-1.5 rounded-full bg-primary/60 animate-[pulse_1.4s_ease-in-out_0.4s_infinite]" />
36
- </div>
37
- </div>
38
- </div>
39
- );
40
- }
@@ -1,108 +0,0 @@
1
- import { act } from "react";
2
- import { createRoot, type Root } from "react-dom/client";
3
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
- import { citationStore, linkStore } from "@iloveagents/foundry-agent";
5
- import { injectCitations, markdownComponents } from "./markdown-text.tsx";
6
-
7
- describe("injectCitations", () => {
8
- let container: HTMLDivElement;
9
- let root: Root;
10
- let originalActEnvironment: boolean | undefined;
11
-
12
- beforeEach(() => {
13
- originalActEnvironment = globalThis.IS_REACT_ACT_ENVIRONMENT;
14
- globalThis.IS_REACT_ACT_ENVIRONMENT = true;
15
- citationStore.getState().clear();
16
- linkStore.getState().clear();
17
- citationStore.getState().setResults([
18
- {
19
- chunk_id: "chunk-1",
20
- entity_id: "entity-1",
21
- entity_name: "Document One",
22
- content: "One",
23
- page_number: 1,
24
- bounding_regions: "[]",
25
- score: 1,
26
- },
27
- {
28
- chunk_id: "chunk-2",
29
- entity_id: "entity-2",
30
- entity_name: "Document Two",
31
- content: "Two",
32
- page_number: 2,
33
- bounding_regions: "[]",
34
- score: 1,
35
- },
36
- ]);
37
- citationStore.getState().setHandler({ openCitation: vi.fn() });
38
- container = document.createElement("div");
39
- document.body.appendChild(container);
40
- root = createRoot(container);
41
- });
42
-
43
- afterEach(() => {
44
- act(() => {
45
- root.unmount();
46
- });
47
- container.remove();
48
- citationStore.getState().clear();
49
- linkStore.getState().clear();
50
- globalThis.IS_REACT_ACT_ENVIRONMENT = originalActEnvironment;
51
- });
52
-
53
- it("turns citation markers into clickable refs inside plain and nested text", async () => {
54
- await act(async () => {
55
- root.render(
56
- <p>
57
- {injectCitations([
58
- "Plain claim [1]. ",
59
- <strong key="nested">Nested claim [2].</strong>,
60
- ])}
61
- </p>,
62
- );
63
- });
64
-
65
- expect(container.querySelectorAll('[role="button"]')).toHaveLength(2);
66
- expect(container.querySelector("strong sup")?.textContent).toBe("[2]");
67
- });
68
-
69
- it("turns handled raw deep links into clickable anchors", async () => {
70
- const openLink = vi.fn();
71
- linkStore.getState().setHandler({
72
- normalizeHref: (href) => href,
73
- canHandle: (href) => href.startsWith("/spaces/"),
74
- openLink,
75
- });
76
-
77
- await act(async () => {
78
- root.render(<p>{injectCitations("Created /spaces/entity-1#block-b1.")}</p>);
79
- });
80
-
81
- const link = container.querySelector("a");
82
- expect(link?.getAttribute("href")).toBe("/spaces/entity-1#block-b1");
83
- expect(link?.textContent).toBe("/spaces/entity-1#block-b1");
84
-
85
- await act(async () => {
86
- link?.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
87
- });
88
-
89
- expect(openLink).toHaveBeenCalledWith("/spaces/entity-1#block-b1");
90
- });
91
-
92
- it("renders handled inline-code deep links as links", async () => {
93
- linkStore.getState().setHandler({
94
- normalizeHref: (href) => href,
95
- canHandle: (href) => href.startsWith("/spaces/"),
96
- openLink: vi.fn(),
97
- });
98
- const Code = markdownComponents.code;
99
-
100
- await act(async () => {
101
- root.render(<p>{Code ? <Code>/spaces/entity-1</Code> : null}</p>);
102
- });
103
-
104
- const link = container.querySelector("a");
105
- expect(link?.getAttribute("href")).toBe("/spaces/entity-1");
106
- expect(link?.className).toContain("font-mono");
107
- });
108
- });