@seed-ship/mcp-ui-solid 4.3.7 → 4.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +63 -0
  3. package/dist/components/UIResourceRenderer.cjs +66 -55
  4. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  5. package/dist/components/UIResourceRenderer.d.ts +6 -0
  6. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  7. package/dist/components/UIResourceRenderer.js +66 -55
  8. package/dist/components/UIResourceRenderer.js.map +1 -1
  9. package/dist/index.cjs +3 -0
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +4 -1
  12. package/dist/index.d.ts +4 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +4 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/services/chat-bus.cjs +22 -0
  17. package/dist/services/chat-bus.cjs.map +1 -1
  18. package/dist/services/chat-bus.d.ts +16 -1
  19. package/dist/services/chat-bus.d.ts.map +1 -1
  20. package/dist/services/chat-bus.js +22 -0
  21. package/dist/services/chat-bus.js.map +1 -1
  22. package/dist/stores/scratchpad-store.cjs.map +1 -1
  23. package/dist/stores/scratchpad-store.d.ts +7 -0
  24. package/dist/stores/scratchpad-store.d.ts.map +1 -1
  25. package/dist/stores/scratchpad-store.js.map +1 -1
  26. package/dist/testing/index.cjs +19 -0
  27. package/dist/testing/index.cjs.map +1 -0
  28. package/dist/testing/index.d.ts +35 -0
  29. package/dist/testing/index.d.ts.map +1 -0
  30. package/dist/testing/index.js +19 -0
  31. package/dist/testing/index.js.map +1 -0
  32. package/dist/types/chat-bus.d.ts +58 -14
  33. package/dist/types/chat-bus.d.ts.map +1 -1
  34. package/package.json +1 -1
  35. package/src/components/UIResourceRenderer.tsx +25 -8
  36. package/src/index.ts +7 -1
  37. package/src/services/chat-bus.test.ts +80 -2
  38. package/src/services/chat-bus.ts +42 -0
  39. package/src/stores/scratchpad-store.ts +7 -0
  40. package/src/testing/index.test.ts +66 -0
  41. package/src/testing/index.ts +50 -0
  42. package/src/types/chat-bus.ts +58 -11
  43. package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,65 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [4.3.9] - 2026-04-14
9
+
10
+ ### Added — Sprint 52 multi-agent primitives
11
+
12
+ #### Type additions (all non-breaking)
13
+ - **`ChoicePromptConfig.options[].metadata?: Record<string, unknown>`** (G1) — free-form metadata on prompt choices (confidence, source, tags...). Opaque to the default renderer, preserved through `showChatPrompt → ChatPromptResponse` roundtrip. Use a custom ChoiceBody wrapper to display it.
14
+ - **`ClarificationEvent.options[].metadata?: Record<string, unknown>`** (G3) — same extension point on clarification events. Legacy `file_id?: number` deprecated in JSDoc (removal in v5.0.0).
15
+ - **`ClarificationEvent.type?: string`** (G3) — free-form type tag for host routing (e.g. `'intent_disambiguate'`, `'file_select'`).
16
+
17
+ #### New helper: `clarificationToPromptConfig()` (G11)
18
+ - Universal bridge converting `ClarificationEvent` → `ChatPromptConfig` for any MCP-UI consumer
19
+ - Transparent `file_id` legacy migration into `metadata.file_id`
20
+ - Explicit `metadata.file_id` takes precedence over legacy field
21
+ - Agnostic — zero Deposium-specific concepts
22
+ - Exported from `@seed-ship/mcp-ui-solid`
23
+
24
+ #### New testing entry point: `createMockChatBus()` (G6)
25
+ - New sub-module `src/testing/` with `createMockChatBus({ promptResponses, onShowChatPrompt })`
26
+ - Pre-programs `showChatPrompt` responses in FIFO order for flow tests
27
+ - Spy hook on `onShowChatPrompt` for assertions
28
+ - Throws helpful error when the queue is exhausted
29
+
30
+ ### Removed — dead code (G7)
31
+ - **BREAKING (theoretical, never implemented)**: `ChatPromptConfig.type = 'select'` removed from the union type. `SelectPromptConfig` interface removed + export dropped from `src/index.ts`. The `'select'` variant was declared in v4.0 but `ChatPrompt.tsx` never had a rendering branch — it was dead code. Use `'form'` with a single `select` field, or `'choice'` for visual picks.
32
+
33
+ ### Documented — known limitations (G5, G8, G9, G10)
34
+ - **`ChatPromptResponse.dismissed`** (G5) — full semantics in JSDoc: X icon/Cancel → `true`, explicit click/submit → `undefined`, AbortSignal → Promise rejection (host responsibility until v4.4.0).
35
+ - **Scratchpad store is a singleton** (G8) — two `ScratchpadPanel` instances share state. Documented as known limitation. Factory `createScratchpadStore()` planned for v4.4.0.
36
+ - **`showChatPrompt` is not re-entrant** (G9) — calling it while another prompt is active leaks the previous Promise. Documented in JSDoc. Host apps must queue/dismiss manually. Auto-reject planned for v4.4.0.
37
+ - **`correlationId` is host-propagated** (G10) — README recipe. mcp-ui does not auto-forward the ID; host SSE parsers must thread it into subsequent event emissions.
38
+
39
+ ### Documented — integration recipes (G2)
40
+ - **Bridging external clarification events** — new README section showing the `onClarificationNeeded → clarificationToPromptConfig → showChatPrompt` flow, with metadata preservation and opaque type tags.
41
+
42
+ ### Documentation catch-up
43
+ - Backfilled `CHANGELOG.md` entries for 4.3.6, 4.3.7, 4.3.8 (previously missing).
44
+
45
+ ## [4.3.8] - 2026-04-11
46
+
47
+ ### Added
48
+ - **Search term highlighting** — matched query terms are wrapped in `<mark>` tags across all visible cells. `bg-yellow-200` in light mode, `bg-[#222F49]` in dark mode. New `highlightQuery` helper skips HTML tag content to preserve markup.
49
+
50
+ ### Fixed
51
+ - **Fullscreen phantom scrollbar** — removed `h-full` from the table wrapper in expanded mode so it shrinks to content. No more empty space or unnecessary scrollbar when rows don't fill the viewport.
52
+
53
+ ## [4.3.7] - 2026-04-11
54
+
55
+ ### Changed
56
+ - **Prev/Next pagination is now the default** — replaced the progressive "show more" mode with unified Prev/Next navigation for consistency between chat and fullscreen views.
57
+ - **Page size selector in fullscreen** — dropdown with 10 / 30 / 60 / 100 / All options.
58
+ - **Fullscreen table fills the viewport** via `calc(100vh - 180px)`.
59
+ - **Header contrast** — thead background bumped from `bg-gray-50` to `bg-gray-100` for better visibility in chat view.
60
+
61
+ ## [4.3.6] - 2026-04-11
62
+
63
+ ### Fixed
64
+ - **Opaque sticky header** — changed from `bg-gray-900/50` (translucent) to `bg-gray-900` (opaque) so the header remains readable over chat bubbles behind it.
65
+ - **Compact search input** — `max-w-xs min-w-[200px]` instead of `w-full` so the filter field doesn't span the entire table width.
66
+
8
67
  ## [4.3.5] - 2026-04-11
9
68
 
10
69
  ### Fixed — Sticky Table Header on Scroll
package/README.md CHANGED
@@ -347,6 +347,65 @@ function ChatInterface() {
347
347
 
348
348
  See [Chat Bus documentation](https://github.com/theseedship/mcp-ui#chat-bus--agent-interactions-experimental) for the full event/command reference.
349
349
 
350
+ ### Bridging external clarification events (v4.3.9)
351
+
352
+ When your MCP server emits a clarification event via SSE (e.g. a `_pause`
353
+ frame asking the user to disambiguate intent), convert it to a `ChatPrompt`
354
+ using the universal `clarificationToPromptConfig` helper — no app-specific
355
+ glue required:
356
+
357
+ ```tsx
358
+ import { clarificationToPromptConfig } from '@seed-ship/mcp-ui-solid'
359
+
360
+ // In your SSE parser, when you decode a clarification frame:
361
+ bus.events.emit('onClarificationNeeded', {
362
+ streamKey: 'main',
363
+ clarification: {
364
+ question: 'Which space do you mean?',
365
+ options: [
366
+ { value: 'sp-1', label: 'Immobilier', metadata: { confidence: 0.9 } },
367
+ { value: 'sp-2', label: 'Santé', metadata: { confidence: 0.6 } },
368
+ ],
369
+ type: 'intent_disambiguate', // opaque tag for host routing
370
+ },
371
+ })
372
+
373
+ // Wire the event to a prompt:
374
+ bus.events.on('onClarificationNeeded', async ({ clarification }) => {
375
+ const response = await bus.commands.exec(
376
+ 'showChatPrompt',
377
+ clarificationToPromptConfig(clarification)
378
+ )
379
+ // POST response.value to your /api/agent-resume endpoint, etc.
380
+ })
381
+ ```
382
+
383
+ Legacy `option.file_id` is automatically migrated into `metadata.file_id`.
384
+ Arbitrary `metadata` (confidence scores, source tags, ...) flows through
385
+ unchanged and can be rendered by a custom `ChoiceBody` wrapper.
386
+
387
+ ### ChatPromptResponse — dismissed / aborted / answered (v4.3.9)
388
+
389
+ Every `ChatPrompt` resolves to one of three outcomes:
390
+
391
+ | Outcome | How | `response.dismissed` | Promise |
392
+ |---------|-----|----------------------|---------|
393
+ | Explicit answer | Click a choice / submit a form | `undefined` | resolves |
394
+ | Dismissed | Click the X icon, click Cancel (confirm type) | `true` | resolves |
395
+ | Aborted | Host app rejects the Promise via `AbortSignal` | *(never resolves)* | rejects |
396
+
397
+ > **v4.3.9 limitation:** `ChatPrompt` does not listen to `AbortSignal` yet.
398
+ > Host apps are responsible for wiring `signal.addEventListener('abort', ...)`
399
+ > to `Promise.reject`. A built-in helper lands in v4.4.0.
400
+
401
+ ### correlationId — host-propagated (v4.3.9)
402
+
403
+ `ChatEventBase.correlationId` is opaque to mcp-ui. When an agent calls
404
+ `sendPrompt('...')` the returned string is a correlation ID — the host app's
405
+ SSE parser must forward this value into every subsequent event emission
406
+ (`onToken`, `onStreamEnd`, etc.) so agents can match responses back to their
407
+ original prompts. mcp-ui does not auto-propagate it across the bus.
408
+
350
409
  ## ScratchpadPanel — HITL/AITL Shared Workspace (`@experimental`)
351
410
 
352
411
  A shared workspace where agent and human collaborate in real-time. 18 section types:
@@ -438,8 +497,12 @@ import {
438
497
  ChatBusProvider, useChatBus,
439
498
  dispatchScratchpad, useScratchpadState,
440
499
  createChatBus, createEventEmitter, createCommandHandler,
500
+ clarificationToPromptConfig, // v4.3.9 — universal ClarificationEvent → ChatPromptConfig bridge
441
501
  } from '@seed-ship/mcp-ui-solid'
442
502
 
503
+ // Testing utilities (v4.3.9)
504
+ import { createMockChatBus } from '@seed-ship/mcp-ui-solid'
505
+
443
506
  // Validation + Security
444
507
  import {
445
508
  validateComponent, validateLayout,
@@ -21,7 +21,7 @@ const ExpandableWrapper = require("./ExpandableWrapper.cjs");
21
21
  const RenderContext = require("./RenderContext.cjs");
22
22
  const useAction = require("../hooks/useAction.cjs");
23
23
  const marked_esm = require("../node_modules/.pnpm/marked@16.4.2/node_modules/marked/lib/marked.esm.cjs");
24
- var _tmpl$ = /* @__PURE__ */ web.template(`<svg class="w-3 h-3 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z">`), _tmpl$2 = /* @__PURE__ */ web.template(`<button>`), _tmpl$3 = /* @__PURE__ */ web.template(`<svg class="w-3 h-3 text-green-500"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M5 13l4 4L19 7">`), _tmpl$4 = /* @__PURE__ */ web.template(`<div class="p-4 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"><p class="text-red-500 dark:text-red-400 text-sm">Invalid chart data: missing data.datasets`), _tmpl$5 = /* @__PURE__ */ web.template(`<div class="absolute inset-0 flex items-center justify-center"><div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600">`), _tmpl$6 = /* @__PURE__ */ web.template(`<div class="absolute inset-0 flex items-center justify-center p-4"><div class=text-center><p class="text-red-600 dark:text-red-400 text-sm font-medium">Chart Error</p><p class="text-gray-600 dark:text-gray-400 text-xs mt-1">`), _tmpl$7 = /* @__PURE__ */ web.template(`<h3 class="text-sm font-semibold text-gray-900 dark:text-white mb-3">`), _tmpl$8 = /* @__PURE__ */ web.template(`<div class="w-full h-full p-4"><!$><!/><div class="w-full h-full"role=img><img class="w-full h-auto max-h-[300px] object-contain">`), _tmpl$9 = /* @__PURE__ */ web.template(`<div class="relative w-full h-full min-h-[300px] bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><!$><!/><!$><!/><!$><!/>`), _tmpl$0 = /* @__PURE__ */ web.template(`<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">`), _tmpl$1 = /* @__PURE__ */ web.template(`<tr>`), _tmpl$10 = /* @__PURE__ */ web.template(`<td class="px-6 py-4 text-sm text-gray-700 dark:text-gray-200 whitespace-normal break-words leading-relaxed first:pl-6 last:pr-6"><div>`), _tmpl$11 = /* @__PURE__ */ web.template(`<tbody class="bg-white dark:bg-gray-800 relative">`), _tmpl$12 = /* @__PURE__ */ web.template(`<button class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">Copy TSV`), _tmpl$13 = /* @__PURE__ */ web.template(`<button class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">Download CSV`), _tmpl$14 = /* @__PURE__ */ web.template(`<button class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">Download JSON`), _tmpl$15 = /* @__PURE__ */ web.template(`<div class="absolute right-0 mt-1 w-36 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg py-1 text-sm"><!$><!/><!$><!/><!$><!/>`), _tmpl$16 = /* @__PURE__ */ web.template(`<div class="absolute right-10 top-2 z-10"><button class="opacity-60 hover:opacity-100 px-2 py-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm"title="Export table"aria-label="Export table"><svg class="w-3 h-3 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg></button><!$><!/>`), _tmpl$17 = /* @__PURE__ */ web.template(`<span class="ml-2 text-xs font-normal text-gray-400">(virtualized: <!$><!/> rows)`), _tmpl$18 = /* @__PURE__ */ web.template(`<h3 class="text-sm font-semibold text-gray-900 dark:text-white mb-3"><!$><!/><!$><!/>`), _tmpl$19 = /* @__PURE__ */ web.template(`<button type=button class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-sm"aria-label="Clear search">&times;`), _tmpl$20 = /* @__PURE__ */ web.template(`<div class="relative mb-3"><span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none text-sm">🔍</span><input type=text class="w-full max-w-xs min-w-[200px] pl-8 pr-8 py-1.5 text-sm border border-gray-200 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none"><!$><!/>`), _tmpl$21 = /* @__PURE__ */ web.template(`<p class="text-xs text-gray-500 dark:text-gray-400 mb-2"><!$><!/> result<!$><!/> on <!$><!/>`), _tmpl$22 = /* @__PURE__ */ web.template(`<div class="mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400"><span>Showing <!$><!/> - <!$><!/> of <!$><!/>`), _tmpl$23 = /* @__PURE__ */ web.template(`<select class="ml-2 px-1 py-0.5 text-xs border border-gray-200 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300">`), _tmpl$24 = /* @__PURE__ */ web.template(`<span class=text-gray-400>/ page`), _tmpl$25 = /* @__PURE__ */ web.template(`<div class="mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400"><span><!$><!/>&ndash;<!$><!/> / <!$><!/></span><div class="flex items-center gap-2"><button class="px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed transition-colors">&#x25C0;</button><span><!$><!/> / <!$><!/></span><button class="px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed transition-colors">&#x25B6;</button><!$><!/>`), _tmpl$26 = /* @__PURE__ */ web.template(`<div class="relative w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden group"><!$><!/><div class=p-4><!$><!/><!$><!/><div class=overflow-x-auto role=region tabindex=0><table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700 border-separate border-spacing-0"><thead class="bg-gray-100 dark:bg-gray-900 sticky top-0 z-10"><tr></tr></thead><!$><!/></table></div><!$><!/><!$><!/>`), _tmpl$27 = /* @__PURE__ */ web.template(`<th scope=col class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider border-b border-gray-200 dark:border-gray-700 first:pl-6 last:pr-6 bg-gray-100 dark:bg-gray-900 cursor-pointer select-none hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors"><span class="inline-flex items-center gap-1"><!$><!/><span class="text-[10px] leading-none">`), _tmpl$28 = /* @__PURE__ */ web.template(`<option>`), _tmpl$29 = /* @__PURE__ */ web.template(`<span class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400">`), _tmpl$30 = /* @__PURE__ */ web.template(`<div class="mt-3 flex items-center"><span><!$><!/> <!$><!/>%`), _tmpl$31 = /* @__PURE__ */ web.template(`<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">`), _tmpl$32 = /* @__PURE__ */ web.template(`<div class="relative w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 group"><!$><!/><div class="flex flex-col h-full justify-between"><div><p class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide"></p><div class="mt-2 flex items-baseline"><p class="text-2xl font-semibold text-gray-900 dark:text-white"></p><!$><!/></div></div><!$><!/><!$><!/>`), _tmpl$33 = /* @__PURE__ */ web.template(`<div class="relative w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 group"><!$><!/><div>`), _tmpl$34 = /* @__PURE__ */ web.template(`<div class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden flex flex-col"><div class="flex-1 flex items-center justify-center p-4 bg-gray-50 dark:bg-gray-900 min-h-[200px]"><a target=_blank rel="noopener noreferrer"class=cursor-zoom-in><img class="max-w-full max-h-[400px] object-contain rounded shadow-sm hover:opacity-90 transition-opacity"loading=lazy></a></div><div class="p-3 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800"><p class="text-sm text-gray-600 dark:text-gray-400 text-center italic">`, true, false, false), _tmpl$35 = /* @__PURE__ */ web.template(`<div class="px-4 py-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900"><h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$36 = /* @__PURE__ */ web.template(`<div class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden flex flex-col"><!$><!/><iframe class="w-full border-0 flex-1"loading=lazy>`, true, false, false), _tmpl$37 = /* @__PURE__ */ web.template(`<figcaption class="p-3 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800"><p class="text-sm text-gray-600 dark:text-gray-400 text-center">`), _tmpl$38 = /* @__PURE__ */ web.template(`<figure><div class="flex-1 flex items-center justify-center p-4 bg-gray-50 dark:bg-gray-900 min-h-[200px]"><a target=_blank rel="noopener noreferrer"class="cursor-zoom-in focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded"><img class="max-w-full max-h-[500px] object-contain rounded shadow-sm hover:opacity-95 transition-opacity"loading=lazy></a></div><!$><!/>`, true, false, false), _tmpl$39 = /* @__PURE__ */ web.template(`<p class="text-xs text-gray-500 dark:text-gray-400 truncate">`), _tmpl$40 = /* @__PURE__ */ web.template(`<a target=_blank rel="noopener noreferrer"><div class="p-2 bg-blue-50 dark:bg-blue-900/30 rounded-full text-blue-600 dark:text-blue-400 group-hover:bg-blue-100 dark:group-hover:bg-blue-900/50 shrink-0 transition-colors"aria-hidden=true><svg xmlns=http://www.w3.org/2000/svg class="w-5 h-5"viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg></div><div class="flex-1 min-w-0"><h4 class="text-sm font-medium text-gray-900 dark:text-white truncate group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors"></h4><!$><!/></div><svg xmlns=http://www.w3.org/2000/svg class="w-4 h-4 text-gray-400 group-hover:text-gray-600 dark:group-hover:text-gray-300 shrink-0 transition-colors"viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round aria-hidden=true><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1=10 y1=14 x2=21 y2=3>`), _tmpl$41 = /* @__PURE__ */ web.template(`<div class="w-full h-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4"><p class="text-sm font-medium text-red-900 dark:text-red-100">Validation Error</p><p class="text-xs text-red-700 dark:text-red-300 mt-1">`), _tmpl$42 = /* @__PURE__ */ web.template(`<span aria-hidden=true>`), _tmpl$43 = /* @__PURE__ */ web.template(`<a rel="noopener noreferrer"><!$><!/><!$><!/>`), _tmpl$44 = /* @__PURE__ */ web.template(`<span class="animate-spin h-4 w-4 border-2 border-current border-t-transparent rounded-full"aria-hidden=true>`), _tmpl$45 = /* @__PURE__ */ web.template(`<button><!$><!/><!$><!/><!$><!/>`), _tmpl$46 = /* @__PURE__ */ web.template(`<p class="text-xs text-red-600 dark:text-red-400 mt-1">Type: <!$><!/>`), _tmpl$47 = /* @__PURE__ */ web.template(`<div class=mt-3><p class="text-xs font-medium text-red-700 dark:text-red-300">Suggestions:</p><ul class="mt-1 text-xs text-red-600 dark:text-red-400 list-disc list-inside">`), _tmpl$48 = /* @__PURE__ */ web.template(`<p class="text-xs text-red-500 dark:text-red-500 mt-2">`), _tmpl$49 = /* @__PURE__ */ web.template(`<div class="relative w-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 group"><!$><!/><div class="flex items-start gap-3"><div class="p-2 bg-red-100 dark:bg-red-900/40 rounded-full shrink-0"><svg class="w-5 h-5 text-red-600 dark:text-red-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg></div><div class="flex-1 min-w-0"><h4 class="text-sm font-semibold text-red-800 dark:text-red-200">Tool Error: <!$><!/></h4><p class="text-sm text-red-700 dark:text-red-300 mt-1"></p><!$><!/><!$><!/><!$><!/>`), _tmpl$50 = /* @__PURE__ */ web.template(`<li>`), _tmpl$51 = /* @__PURE__ */ web.template(`<div class="px-4 py-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900"><h3 class="text-sm font-semibold text-gray-900 dark:text-white capitalize">`), _tmpl$52 = /* @__PURE__ */ web.template(`<div class="w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><!$><!/><div class="p-4 prose prose-sm dark:prose-invert max-w-none">`), _tmpl$53 = /* @__PURE__ */ web.template(`<div><div class="grid gap-4"></div><!$><!/>`), _tmpl$54 = /* @__PURE__ */ web.template(`<div>`);
24
+ var _tmpl$ = /* @__PURE__ */ web.template(`<svg class="w-3 h-3 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z">`), _tmpl$2 = /* @__PURE__ */ web.template(`<button>`), _tmpl$3 = /* @__PURE__ */ web.template(`<svg class="w-3 h-3 text-green-500"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M5 13l4 4L19 7">`), _tmpl$4 = /* @__PURE__ */ web.template(`<div class="p-4 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"><p class="text-red-500 dark:text-red-400 text-sm">Invalid chart data: missing data.datasets`), _tmpl$5 = /* @__PURE__ */ web.template(`<div class="absolute inset-0 flex items-center justify-center"><div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600">`), _tmpl$6 = /* @__PURE__ */ web.template(`<div class="absolute inset-0 flex items-center justify-center p-4"><div class=text-center><p class="text-red-600 dark:text-red-400 text-sm font-medium">Chart Error</p><p class="text-gray-600 dark:text-gray-400 text-xs mt-1">`), _tmpl$7 = /* @__PURE__ */ web.template(`<h3 class="text-sm font-semibold text-gray-900 dark:text-white mb-3">`), _tmpl$8 = /* @__PURE__ */ web.template(`<div class="w-full h-full p-4"><!$><!/><div class="w-full h-full"role=img><img class="w-full h-auto max-h-[300px] object-contain">`), _tmpl$9 = /* @__PURE__ */ web.template(`<div class="relative w-full h-full min-h-[300px] bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><!$><!/><!$><!/><!$><!/>`), _tmpl$0 = /* @__PURE__ */ web.template(`<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">`), _tmpl$1 = /* @__PURE__ */ web.template(`<tr>`), _tmpl$10 = /* @__PURE__ */ web.template(`<td class="px-6 py-4 text-sm text-gray-700 dark:text-gray-200 whitespace-normal break-words leading-relaxed first:pl-6 last:pr-6"><div>`), _tmpl$11 = /* @__PURE__ */ web.template(`<tbody class="bg-white dark:bg-gray-800 relative">`), _tmpl$12 = /* @__PURE__ */ web.template(`<button class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">Copy TSV`), _tmpl$13 = /* @__PURE__ */ web.template(`<button class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">Download CSV`), _tmpl$14 = /* @__PURE__ */ web.template(`<button class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">Download JSON`), _tmpl$15 = /* @__PURE__ */ web.template(`<div class="absolute right-0 mt-1 w-36 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg py-1 text-sm"><!$><!/><!$><!/><!$><!/>`), _tmpl$16 = /* @__PURE__ */ web.template(`<div class="absolute right-10 top-2 z-10"><button class="opacity-60 hover:opacity-100 px-2 py-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm"title="Export table"aria-label="Export table"><svg class="w-3 h-3 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg></button><!$><!/>`), _tmpl$17 = /* @__PURE__ */ web.template(`<span class="ml-2 text-xs font-normal text-gray-400">(virtualized: <!$><!/> rows)`), _tmpl$18 = /* @__PURE__ */ web.template(`<h3 class="text-sm font-semibold text-gray-900 dark:text-white mb-3"><!$><!/><!$><!/>`), _tmpl$19 = /* @__PURE__ */ web.template(`<button type=button class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-sm"aria-label="Clear search">&times;`), _tmpl$20 = /* @__PURE__ */ web.template(`<div class="relative mb-3"><span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none text-sm">🔍</span><input type=text class="w-full max-w-xs min-w-[200px] pl-8 pr-8 py-1.5 text-sm border border-gray-200 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none"><!$><!/>`), _tmpl$21 = /* @__PURE__ */ web.template(`<p class="text-xs text-gray-500 dark:text-gray-400 mb-2"><!$><!/> result<!$><!/> on <!$><!/>`), _tmpl$22 = /* @__PURE__ */ web.template(`<div class="mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400"><span>Showing <!$><!/> - <!$><!/> of <!$><!/>`), _tmpl$23 = /* @__PURE__ */ web.template(`<select class="ml-2 px-1 py-0.5 text-xs border border-gray-200 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300">`), _tmpl$24 = /* @__PURE__ */ web.template(`<span class=text-gray-400>/ page`), _tmpl$25 = /* @__PURE__ */ web.template(`<div class="mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400"><span><!$><!/>&ndash;<!$><!/> / <!$><!/></span><div class="flex items-center gap-2"><button class="px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed transition-colors">&#x25C0;</button><span><!$><!/> / <!$><!/></span><button class="px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed transition-colors">&#x25B6;</button><!$><!/>`), _tmpl$26 = /* @__PURE__ */ web.template(`<div><!$><!/><div class=p-4><!$><!/><!$><!/><div class=overflow-x-auto role=region tabindex=0><table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700 border-separate border-spacing-0"><thead class="bg-gray-100 dark:bg-gray-900 sticky top-0 z-10"><tr></tr></thead><!$><!/></table></div><!$><!/><!$><!/>`), _tmpl$27 = /* @__PURE__ */ web.template(`<th scope=col class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider border-b border-gray-200 dark:border-gray-700 first:pl-6 last:pr-6 bg-gray-100 dark:bg-gray-900 cursor-pointer select-none hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors"><span class="inline-flex items-center gap-1"><!$><!/><span class="text-[10px] leading-none">`), _tmpl$28 = /* @__PURE__ */ web.template(`<option>`), _tmpl$29 = /* @__PURE__ */ web.template(`<span class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400">`), _tmpl$30 = /* @__PURE__ */ web.template(`<div class="mt-3 flex items-center"><span><!$><!/> <!$><!/>%`), _tmpl$31 = /* @__PURE__ */ web.template(`<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">`), _tmpl$32 = /* @__PURE__ */ web.template(`<div class="relative w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 group"><!$><!/><div class="flex flex-col h-full justify-between"><div><p class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide"></p><div class="mt-2 flex items-baseline"><p class="text-2xl font-semibold text-gray-900 dark:text-white"></p><!$><!/></div></div><!$><!/><!$><!/>`), _tmpl$33 = /* @__PURE__ */ web.template(`<div class="relative w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 group"><!$><!/><div>`), _tmpl$34 = /* @__PURE__ */ web.template(`<div class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden flex flex-col"><div class="flex-1 flex items-center justify-center p-4 bg-gray-50 dark:bg-gray-900 min-h-[200px]"><a target=_blank rel="noopener noreferrer"class=cursor-zoom-in><img class="max-w-full max-h-[400px] object-contain rounded shadow-sm hover:opacity-90 transition-opacity"loading=lazy></a></div><div class="p-3 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800"><p class="text-sm text-gray-600 dark:text-gray-400 text-center italic">`, true, false, false), _tmpl$35 = /* @__PURE__ */ web.template(`<div class="px-4 py-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900"><h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$36 = /* @__PURE__ */ web.template(`<div class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden flex flex-col"><!$><!/><iframe class="w-full border-0 flex-1"loading=lazy>`, true, false, false), _tmpl$37 = /* @__PURE__ */ web.template(`<figcaption class="p-3 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800"><p class="text-sm text-gray-600 dark:text-gray-400 text-center">`), _tmpl$38 = /* @__PURE__ */ web.template(`<figure><div class="flex-1 flex items-center justify-center p-4 bg-gray-50 dark:bg-gray-900 min-h-[200px]"><a target=_blank rel="noopener noreferrer"class="cursor-zoom-in focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded"><img class="max-w-full max-h-[500px] object-contain rounded shadow-sm hover:opacity-95 transition-opacity"loading=lazy></a></div><!$><!/>`, true, false, false), _tmpl$39 = /* @__PURE__ */ web.template(`<p class="text-xs text-gray-500 dark:text-gray-400 truncate">`), _tmpl$40 = /* @__PURE__ */ web.template(`<a target=_blank rel="noopener noreferrer"><div class="p-2 bg-blue-50 dark:bg-blue-900/30 rounded-full text-blue-600 dark:text-blue-400 group-hover:bg-blue-100 dark:group-hover:bg-blue-900/50 shrink-0 transition-colors"aria-hidden=true><svg xmlns=http://www.w3.org/2000/svg class="w-5 h-5"viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg></div><div class="flex-1 min-w-0"><h4 class="text-sm font-medium text-gray-900 dark:text-white truncate group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors"></h4><!$><!/></div><svg xmlns=http://www.w3.org/2000/svg class="w-4 h-4 text-gray-400 group-hover:text-gray-600 dark:group-hover:text-gray-300 shrink-0 transition-colors"viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round aria-hidden=true><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1=10 y1=14 x2=21 y2=3>`), _tmpl$41 = /* @__PURE__ */ web.template(`<div class="w-full h-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4"><p class="text-sm font-medium text-red-900 dark:text-red-100">Validation Error</p><p class="text-xs text-red-700 dark:text-red-300 mt-1">`), _tmpl$42 = /* @__PURE__ */ web.template(`<span aria-hidden=true>`), _tmpl$43 = /* @__PURE__ */ web.template(`<a rel="noopener noreferrer"><!$><!/><!$><!/>`), _tmpl$44 = /* @__PURE__ */ web.template(`<span class="animate-spin h-4 w-4 border-2 border-current border-t-transparent rounded-full"aria-hidden=true>`), _tmpl$45 = /* @__PURE__ */ web.template(`<button><!$><!/><!$><!/><!$><!/>`), _tmpl$46 = /* @__PURE__ */ web.template(`<p class="text-xs text-red-600 dark:text-red-400 mt-1">Type: <!$><!/>`), _tmpl$47 = /* @__PURE__ */ web.template(`<div class=mt-3><p class="text-xs font-medium text-red-700 dark:text-red-300">Suggestions:</p><ul class="mt-1 text-xs text-red-600 dark:text-red-400 list-disc list-inside">`), _tmpl$48 = /* @__PURE__ */ web.template(`<p class="text-xs text-red-500 dark:text-red-500 mt-2">`), _tmpl$49 = /* @__PURE__ */ web.template(`<div class="relative w-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 group"><!$><!/><div class="flex items-start gap-3"><div class="p-2 bg-red-100 dark:bg-red-900/40 rounded-full shrink-0"><svg class="w-5 h-5 text-red-600 dark:text-red-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg></div><div class="flex-1 min-w-0"><h4 class="text-sm font-semibold text-red-800 dark:text-red-200">Tool Error: <!$><!/></h4><p class="text-sm text-red-700 dark:text-red-300 mt-1"></p><!$><!/><!$><!/><!$><!/>`), _tmpl$50 = /* @__PURE__ */ web.template(`<li>`), _tmpl$51 = /* @__PURE__ */ web.template(`<div class="px-4 py-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900"><h3 class="text-sm font-semibold text-gray-900 dark:text-white capitalize">`), _tmpl$52 = /* @__PURE__ */ web.template(`<div class="w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><!$><!/><div class="p-4 prose prose-sm dark:prose-invert max-w-none">`), _tmpl$53 = /* @__PURE__ */ web.template(`<div><div class="grid gap-4"></div><!$><!/>`), _tmpl$54 = /* @__PURE__ */ web.template(`<div>`);
25
25
  function CopyButton(props) {
26
26
  const [copied, setCopied] = solidJs.createSignal(false);
27
27
  const handleCopy = async () => {
@@ -208,6 +208,17 @@ function ChartRenderer(props) {
208
208
  }
209
209
  });
210
210
  }
211
+ function highlightQuery(html, query) {
212
+ const q = query.trim();
213
+ if (!q) return html;
214
+ const escaped = q.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
215
+ const regex = new RegExp(`(${escaped})`, "gi");
216
+ return html.replace(/(<[^>]+>)|([^<]+)/g, (_m, tag, text) => {
217
+ if (tag) return tag;
218
+ if (!text) return "";
219
+ return text.replace(regex, '<mark class="bg-yellow-200 dark:bg-[#222F49] text-inherit rounded px-0.5">$1</mark>');
220
+ });
221
+ }
211
222
  function renderCellValue(value) {
212
223
  if (value === null || value === void 0) {
213
224
  return "-";
@@ -506,7 +517,7 @@ ${dataRows}`;
506
517
  },
507
518
  children: (column) => (() => {
508
519
  var _el$23 = web.getNextElement(_tmpl$10), _el$24 = _el$23.firstChild;
509
- web.effect(() => web.setProperty(_el$24, "innerHTML", renderCellValue(row[column.key])));
520
+ web.effect(() => web.setProperty(_el$24, "innerHTML", highlightQuery(renderCellValue(row[column.key]), debouncedQuery())));
510
521
  return _el$23;
511
522
  })()
512
523
  }));
@@ -539,7 +550,7 @@ ${dataRows}`;
539
550
  },
540
551
  children: (column) => (() => {
541
552
  var _el$27 = web.getNextElement(_tmpl$10), _el$28 = _el$27.firstChild;
542
- web.effect(() => web.setProperty(_el$28, "innerHTML", renderCellValue(row[column.key])));
553
+ web.effect(() => web.setProperty(_el$28, "innerHTML", highlightQuery(renderCellValue(row[column.key]), debouncedQuery())));
543
554
  return _el$27;
544
555
  })()
545
556
  }));
@@ -705,15 +716,15 @@ ${dataRows}`;
705
716
  web.insert(_el$123, () => column.label, _el$126, _co$29);
706
717
  web.insert(_el$124, () => sortIndicator(column.key));
707
718
  web.effect((_p$) => {
708
- var _v$11 = column.width ? {
719
+ var _v$12 = column.width ? {
709
720
  width: column.width
710
- } : {}, _v$12 = `Sort by ${column.label}`, _v$13 = {
721
+ } : {}, _v$13 = `Sort by ${column.label}`, _v$14 = {
711
722
  "opacity-30": sortKey() !== column.key,
712
723
  "opacity-100 text-blue-600 dark:text-blue-400": sortKey() === column.key
713
724
  };
714
- _p$.e = web.style(_el$122, _v$11, _p$.e);
715
- _v$12 !== _p$.t && web.setAttribute(_el$122, "title", _p$.t = _v$12);
716
- _p$.a = web.classList(_el$124, _v$13, _p$.a);
725
+ _p$.e = web.style(_el$122, _v$12, _p$.e);
726
+ _v$13 !== _p$.t && web.setAttribute(_el$122, "title", _p$.t = _v$13);
727
+ _p$.a = web.classList(_el$124, _v$14, _p$.a);
717
728
  return _p$;
718
729
  }, {
719
730
  e: void 0,
@@ -797,24 +808,23 @@ ${dataRows}`;
797
808
  }
798
809
  }), _el$119, _co$27);
799
810
  web.effect((_p$) => {
800
- var _v$0 = isVirtualizing() ? {
811
+ var _v$0 = `relative w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden group ${isExpanded() ? "" : "h-full"}`, _v$1 = isVirtualizing() ? {
801
812
  "max-height": "500px",
802
813
  "overflow-y": "auto"
803
- } : clientVisibleRows().length > 8 ? {
804
- "max-height": isExpanded() ? "calc(100vh - 180px)" : "400px",
805
- "overflow-y": "auto"
806
- } : isExpanded() ? {
807
- "max-height": "calc(100vh - 180px)",
814
+ } : !isExpanded() && clientVisibleRows().length > 8 ? {
815
+ "max-height": "400px",
808
816
  "overflow-y": "auto"
809
- } : {}, _v$1 = tableParams.title || "Data table", _v$10 = tableParams.title ? `${tableId}-title` : void 0;
810
- _p$.e = web.style(_el$70, _v$0, _p$.e);
811
- _v$1 !== _p$.t && web.setAttribute(_el$70, "aria-label", _p$.t = _v$1);
812
- _v$10 !== _p$.a && web.setAttribute(_el$71, "aria-labelledby", _p$.a = _v$10);
817
+ } : {}, _v$10 = tableParams.title || "Data table", _v$11 = tableParams.title ? `${tableId}-title` : void 0;
818
+ _v$0 !== _p$.e && web.className(_el$29, _p$.e = _v$0);
819
+ _p$.t = web.style(_el$70, _v$1, _p$.t);
820
+ _v$10 !== _p$.a && web.setAttribute(_el$70, "aria-label", _p$.a = _v$10);
821
+ _v$11 !== _p$.o && web.setAttribute(_el$71, "aria-labelledby", _p$.o = _v$11);
813
822
  return _p$;
814
823
  }, {
815
824
  e: void 0,
816
825
  t: void 0,
817
- a: void 0
826
+ a: void 0,
827
+ o: void 0
818
828
  });
819
829
  return _el$29;
820
830
  }
@@ -921,9 +931,9 @@ function TextRenderer(props) {
921
931
  position: "top-right"
922
932
  }), _el$155, _co$36);
923
933
  web.effect((_p$) => {
924
- var _v$14 = `prose prose-sm dark:prose-invert max-w-none ${textParams.className || ""}`, _v$15 = htmlContent();
925
- _v$14 !== _p$.e && web.className(_el$153, _p$.e = _v$14);
926
- _v$15 !== _p$.t && web.setProperty(_el$153, "innerHTML", _p$.t = _v$15);
934
+ var _v$15 = `prose prose-sm dark:prose-invert max-w-none ${textParams.className || ""}`, _v$16 = htmlContent();
935
+ _v$15 !== _p$.e && web.className(_el$153, _p$.e = _v$15);
936
+ _v$16 !== _p$.t && web.setProperty(_el$153, "innerHTML", _p$.t = _v$16);
927
937
  return _p$;
928
938
  }, {
929
939
  e: void 0,
@@ -936,10 +946,10 @@ function TextRenderer(props) {
936
946
  var _el$156 = web.getNextElement(_tmpl$34), _el$157 = _el$156.firstChild, _el$158 = _el$157.firstChild, _el$159 = _el$158.firstChild, _el$160 = _el$157.nextSibling, _el$161 = _el$160.firstChild;
937
947
  web.insert(_el$161, () => data().credit);
938
948
  web.effect((_p$) => {
939
- var _v$16 = data().linkUrl, _v$17 = data().imageUrl, _v$18 = data().alt;
940
- _v$16 !== _p$.e && web.setAttribute(_el$158, "href", _p$.e = _v$16);
941
- _v$17 !== _p$.t && web.setAttribute(_el$159, "src", _p$.t = _v$17);
942
- _v$18 !== _p$.a && web.setAttribute(_el$159, "alt", _p$.a = _v$18);
949
+ var _v$17 = data().linkUrl, _v$18 = data().imageUrl, _v$19 = data().alt;
950
+ _v$17 !== _p$.e && web.setAttribute(_el$158, "href", _p$.e = _v$17);
951
+ _v$18 !== _p$.t && web.setAttribute(_el$159, "src", _p$.t = _v$18);
952
+ _v$19 !== _p$.a && web.setAttribute(_el$159, "alt", _p$.a = _v$19);
943
953
  return _p$;
944
954
  }, {
945
955
  e: void 0,
@@ -965,11 +975,11 @@ function IframeRenderer(props) {
965
975
  }
966
976
  }), _el$167, _co$37);
967
977
  web.effect((_p$) => {
968
- var _v$19 = params.url, _v$20 = params.title || "Embedded content", _v$21 = `height: ${params.height || "400px"}; min-height: 300px;`, _v$22 = validation.getIframeSandbox(params.url);
969
- _v$19 !== _p$.e && web.setAttribute(_el$165, "src", _p$.e = _v$19);
970
- _v$20 !== _p$.t && web.setAttribute(_el$165, "title", _p$.t = _v$20);
971
- _p$.a = web.style(_el$165, _v$21, _p$.a);
972
- _v$22 !== _p$.o && web.setAttribute(_el$165, "sandbox", _p$.o = _v$22);
978
+ var _v$20 = params.url, _v$21 = params.title || "Embedded content", _v$22 = `height: ${params.height || "400px"}; min-height: 300px;`, _v$23 = validation.getIframeSandbox(params.url);
979
+ _v$20 !== _p$.e && web.setAttribute(_el$165, "src", _p$.e = _v$20);
980
+ _v$21 !== _p$.t && web.setAttribute(_el$165, "title", _p$.t = _v$21);
981
+ _p$.a = web.style(_el$165, _v$22, _p$.a);
982
+ _v$23 !== _p$.o && web.setAttribute(_el$165, "sandbox", _p$.o = _v$23);
973
983
  return _p$;
974
984
  }, {
975
985
  e: void 0,
@@ -995,12 +1005,12 @@ function ImageRenderer(props) {
995
1005
  }
996
1006
  }), _el$175, _co$38);
997
1007
  web.effect((_p$) => {
998
- var _v$23 = `w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden flex flex-col ${params.className || ""}`, _v$24 = params.url, _v$25 = `View full size: ${params.alt || "image"}`, _v$26 = params.url, _v$27 = params.alt || "Image";
999
- _v$23 !== _p$.e && web.className(_el$168, _p$.e = _v$23);
1000
- _v$24 !== _p$.t && web.setAttribute(_el$170, "href", _p$.t = _v$24);
1001
- _v$25 !== _p$.a && web.setAttribute(_el$170, "aria-label", _p$.a = _v$25);
1002
- _v$26 !== _p$.o && web.setAttribute(_el$171, "src", _p$.o = _v$26);
1003
- _v$27 !== _p$.i && web.setAttribute(_el$171, "alt", _p$.i = _v$27);
1008
+ var _v$24 = `w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden flex flex-col ${params.className || ""}`, _v$25 = params.url, _v$26 = `View full size: ${params.alt || "image"}`, _v$27 = params.url, _v$28 = params.alt || "Image";
1009
+ _v$24 !== _p$.e && web.className(_el$168, _p$.e = _v$24);
1010
+ _v$25 !== _p$.t && web.setAttribute(_el$170, "href", _p$.t = _v$25);
1011
+ _v$26 !== _p$.a && web.setAttribute(_el$170, "aria-label", _p$.a = _v$26);
1012
+ _v$27 !== _p$.o && web.setAttribute(_el$171, "src", _p$.o = _v$27);
1013
+ _v$28 !== _p$.i && web.setAttribute(_el$171, "alt", _p$.i = _v$28);
1004
1014
  return _p$;
1005
1015
  }, {
1006
1016
  e: void 0,
@@ -1029,10 +1039,10 @@ function LinkRenderer(props) {
1029
1039
  }
1030
1040
  }), _el$182, _co$39);
1031
1041
  web.effect((_p$) => {
1032
- var _v$28 = params.url, _v$29 = `${params.label || "Link"}: ${params.description || params.url} (opens in new tab)`, _v$30 = `flex items-center gap-3 p-4 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors group h-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${params.className || ""}`;
1033
- _v$28 !== _p$.e && web.setAttribute(_el$176, "href", _p$.e = _v$28);
1034
- _v$29 !== _p$.t && web.setAttribute(_el$176, "aria-label", _p$.t = _v$29);
1035
- _v$30 !== _p$.a && web.className(_el$176, _p$.a = _v$30);
1042
+ var _v$29 = params.url, _v$30 = `${params.label || "Link"}: ${params.description || params.url} (opens in new tab)`, _v$31 = `flex items-center gap-3 p-4 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors group h-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${params.className || ""}`;
1043
+ _v$29 !== _p$.e && web.setAttribute(_el$176, "href", _p$.e = _v$29);
1044
+ _v$30 !== _p$.t && web.setAttribute(_el$176, "aria-label", _p$.t = _v$30);
1045
+ _v$31 !== _p$.a && web.className(_el$176, _p$.a = _v$31);
1036
1046
  return _p$;
1037
1047
  }, {
1038
1048
  e: void 0,
@@ -1319,13 +1329,13 @@ function ActionRenderer(props) {
1319
1329
  }), _el$189, _co$40);
1320
1330
  web.insert(_el$186, () => params.label, _el$191, _co$41);
1321
1331
  web.effect((_p$) => {
1322
- var _v$31 = params.url || "#", _v$32 = params.url ? "_blank" : void 0, _v$33 = params.ariaLabel || params.label, _v$34 = `inline-flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500
1332
+ var _v$32 = params.url || "#", _v$33 = params.url ? "_blank" : void 0, _v$34 = params.ariaLabel || params.label, _v$35 = `inline-flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500
1323
1333
  ${params.variant === "primary" ? "bg-blue-600 text-white hover:bg-blue-700" : params.variant === "outline" ? "border border-gray-300 text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800" : "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300"}
1324
1334
  ${params.className || ""}`;
1325
- _v$31 !== _p$.e && web.setAttribute(_el$186, "href", _p$.e = _v$31);
1326
- _v$32 !== _p$.t && web.setAttribute(_el$186, "target", _p$.t = _v$32);
1327
- _v$33 !== _p$.a && web.setAttribute(_el$186, "aria-label", _p$.a = _v$33);
1328
- _v$34 !== _p$.o && web.className(_el$186, _p$.o = _v$34);
1335
+ _v$32 !== _p$.e && web.setAttribute(_el$186, "href", _p$.e = _v$32);
1336
+ _v$33 !== _p$.t && web.setAttribute(_el$186, "target", _p$.t = _v$33);
1337
+ _v$34 !== _p$.a && web.setAttribute(_el$186, "aria-label", _p$.a = _v$34);
1338
+ _v$35 !== _p$.o && web.className(_el$186, _p$.o = _v$35);
1329
1339
  return _p$;
1330
1340
  }, {
1331
1341
  e: void 0,
@@ -1360,16 +1370,16 @@ function ActionRenderer(props) {
1360
1370
  }), _el$198, _co$43);
1361
1371
  web.insert(_el$192, () => params.label, _el$200, _co$44);
1362
1372
  web.effect((_p$) => {
1363
- var _v$35 = params.action === "submit" ? "submit" : "button", _v$36 = isDisabled(), _v$37 = isExecuting() && params.action === "tool-call", _v$38 = params.ariaLabel || params.label, _v$39 = `inline-flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500
1373
+ var _v$36 = params.action === "submit" ? "submit" : "button", _v$37 = isDisabled(), _v$38 = isExecuting() && params.action === "tool-call", _v$39 = params.ariaLabel || params.label, _v$40 = `inline-flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500
1364
1374
  ${params.variant === "primary" ? "bg-blue-600 text-white hover:bg-blue-700 shadow-sm" : params.variant === "secondary" ? "bg-gray-100 text-gray-900 hover:bg-gray-200 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600" : params.variant === "outline" ? "border border-gray-300 text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800" : params.variant === "danger" ? "bg-red-600 text-white hover:bg-red-700" : "bg-transparent text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-800"}
1365
1375
  ${isDisabled() ? "opacity-50 cursor-not-allowed" : ""}
1366
1376
  ${params.size === "sm" ? "px-3 py-1.5 text-xs" : params.size === "lg" ? "px-6 py-3 text-base" : ""}
1367
1377
  ${params.className || ""}`;
1368
- _v$35 !== _p$.e && web.setAttribute(_el$192, "type", _p$.e = _v$35);
1369
- _v$36 !== _p$.t && web.setProperty(_el$192, "disabled", _p$.t = _v$36);
1370
- _v$37 !== _p$.a && web.setAttribute(_el$192, "aria-busy", _p$.a = _v$37);
1371
- _v$38 !== _p$.o && web.setAttribute(_el$192, "aria-label", _p$.o = _v$38);
1372
- _v$39 !== _p$.i && web.className(_el$192, _p$.i = _v$39);
1378
+ _v$36 !== _p$.e && web.setAttribute(_el$192, "type", _p$.e = _v$36);
1379
+ _v$37 !== _p$.t && web.setProperty(_el$192, "disabled", _p$.t = _v$37);
1380
+ _v$38 !== _p$.a && web.setAttribute(_el$192, "aria-busy", _p$.a = _v$38);
1381
+ _v$39 !== _p$.o && web.setAttribute(_el$192, "aria-label", _p$.o = _v$39);
1382
+ _v$40 !== _p$.i && web.className(_el$192, _p$.i = _v$40);
1373
1383
  return _p$;
1374
1384
  }, {
1375
1385
  e: void 0,
@@ -1581,9 +1591,9 @@ const UIResourceRenderer = (props) => {
1581
1591
  }
1582
1592
  }), _el$236, _co$52);
1583
1593
  web.effect((_p$) => {
1584
- var _v$40 = `w-full ${props.class || ""}`, _v$41 = gridContainerStyle();
1585
- _v$40 !== _p$.e && web.className(_el$233, _p$.e = _v$40);
1586
- _p$.t = web.style(_el$234, _v$41, _p$.t);
1594
+ var _v$41 = `w-full ${props.class || ""}`, _v$42 = gridContainerStyle();
1595
+ _v$41 !== _p$.e && web.className(_el$233, _p$.e = _v$41);
1596
+ _p$.t = web.style(_el$234, _v$42, _p$.t);
1587
1597
  return _p$;
1588
1598
  }, {
1589
1599
  e: void 0,
@@ -1595,5 +1605,6 @@ const UIResourceRenderer = (props) => {
1595
1605
  };
1596
1606
  web.delegateEvents(["click", "input"]);
1597
1607
  exports.UIResourceRenderer = UIResourceRenderer;
1608
+ exports.highlightQuery = highlightQuery;
1598
1609
  exports.renderCellValue = renderCellValue;
1599
1610
  //# sourceMappingURL=UIResourceRenderer.cjs.map