@seed-ship/mcp-ui-solid 4.3.2 → 4.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-ship/mcp-ui-solid",
3
- "version": "4.3.2",
3
+ "version": "4.3.3",
4
4
  "description": "SolidJS components for rendering MCP-generated UI resources",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -375,35 +375,69 @@ function TableRenderer(props: {
375
375
  return sortDir() === 'asc' ? '\u2191' : '\u2193'
376
376
  }
377
377
 
378
+ // ─── Client-side search filter (v4.3.3) ─────────────────────
379
+ const [searchQuery, setSearchQuery] = createSignal('')
380
+ const [debouncedQuery, setDebouncedQuery] = createSignal('')
381
+ let searchTimer: ReturnType<typeof setTimeout> | null = null
382
+
383
+ const isSearchable = () => tableParams.searchable === true || (tableParams.searchable !== false && allRows().length > 10)
384
+ const searchPlaceholder = () => tableParams.searchPlaceholder || 'Rechercher dans le tableau...'
385
+
386
+ const handleSearch = (value: string) => {
387
+ setSearchQuery(value)
388
+ if (searchTimer) clearTimeout(searchTimer)
389
+ searchTimer = setTimeout(() => {
390
+ setDebouncedQuery(value)
391
+ setClientPage(0)
392
+ setProgressivePages(1)
393
+ }, 200)
394
+ }
395
+
396
+ /** Normalize string for accent-insensitive matching */
397
+ const normalize = (s: string) => s.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase()
398
+
399
+ const filteredRows = createMemo(() => {
400
+ const q = normalize(debouncedQuery())
401
+ if (!q) return sortedRows()
402
+ const cols = columns()
403
+ return sortedRows().filter((row: any) =>
404
+ cols.some((col: any) => {
405
+ const val = row[col.key]
406
+ if (val == null) return false
407
+ return normalize(String(val)).includes(q)
408
+ })
409
+ )
410
+ })
411
+
378
412
  // ─── Client-side pagination (v4.0.4, progressive mode v4.3.2) ─────
379
413
  const clientPageSize = () => tableParams.pageSize ?? 25
380
414
  const hasServerPagination = () => !!tableParams.pagination
381
415
  const isProgressiveMode = () => !!tableParams.showAllLabel
382
416
  const needsClientPagination = () =>
383
- !hasServerPagination() && clientPageSize() > 0 && sortedRows().length > clientPageSize()
417
+ !hasServerPagination() && clientPageSize() > 0 && filteredRows().length > clientPageSize()
384
418
  const [clientPage, setClientPage] = createSignal(tableParams.initialPage ?? 0)
385
419
  // Progressive mode: track how many pages to show (append)
386
420
  const [progressivePages, setProgressivePages] = createSignal(1)
387
- const clientTotalPages = () => needsClientPagination() ? Math.ceil(sortedRows().length / clientPageSize()) : 1
421
+ const clientTotalPages = () => needsClientPagination() ? Math.ceil(filteredRows().length / clientPageSize()) : 1
388
422
  const clientVisibleRows = createMemo(() => {
389
- if (!needsClientPagination()) return sortedRows()
423
+ if (!needsClientPagination()) return filteredRows()
390
424
  if (isProgressiveMode()) {
391
425
  // Progressive: show first N * pageSize rows
392
- return sortedRows().slice(0, progressivePages() * clientPageSize())
426
+ return filteredRows().slice(0, progressivePages() * clientPageSize())
393
427
  }
394
428
  const start = clientPage() * clientPageSize()
395
- return sortedRows().slice(start, start + clientPageSize())
429
+ return filteredRows().slice(start, start + clientPageSize())
396
430
  })
397
431
  const clientRangeStart = () => needsClientPagination()
398
432
  ? (isProgressiveMode() ? 1 : clientPage() * clientPageSize() + 1)
399
433
  : 1
400
434
  const clientRangeEnd = () => needsClientPagination()
401
435
  ? (isProgressiveMode()
402
- ? Math.min(progressivePages() * clientPageSize(), sortedRows().length)
403
- : Math.min((clientPage() + 1) * clientPageSize(), sortedRows().length))
404
- : sortedRows().length
436
+ ? Math.min(progressivePages() * clientPageSize(), filteredRows().length)
437
+ : Math.min((clientPage() + 1) * clientPageSize(), filteredRows().length))
438
+ : filteredRows().length
405
439
  const progressiveHasMore = () => isProgressiveMode() && needsClientPagination() && progressivePages() < clientTotalPages()
406
- const progressiveRemaining = () => sortedRows().length - progressivePages() * clientPageSize()
440
+ const progressiveRemaining = () => filteredRows().length - progressivePages() * clientPageSize()
407
441
  const showMoreLabel = () => tableParams.showAllLabel || 'Show more'
408
442
 
409
443
  // ─── Virtualization ──────────────────────────────────────
@@ -640,6 +674,33 @@ function TableRenderer(props: {
640
674
  </h3>
641
675
  </Show>
642
676
 
677
+ {/* Search input (v4.3.3) */}
678
+ <Show when={isSearchable()}>
679
+ <div class="relative mb-3">
680
+ <span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none text-sm">{'\uD83D\uDD0D'}</span>
681
+ <input
682
+ type="text"
683
+ value={searchQuery()}
684
+ onInput={(e) => handleSearch(e.currentTarget.value)}
685
+ placeholder={searchPlaceholder()}
686
+ class="w-full 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"
687
+ />
688
+ <Show when={searchQuery()}>
689
+ <button
690
+ type="button"
691
+ onClick={() => { handleSearch(''); setSearchQuery(''); setDebouncedQuery('') }}
692
+ 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"
693
+ aria-label="Clear search"
694
+ >&times;</button>
695
+ </Show>
696
+ </div>
697
+ <Show when={debouncedQuery() && filteredRows().length !== sortedRows().length}>
698
+ <p class="text-xs text-gray-500 dark:text-gray-400 mb-2">
699
+ {filteredRows().length} result{filteredRows().length !== 1 ? 's' : ''} on {sortedRows().length}
700
+ </p>
701
+ </Show>
702
+ </Show>
703
+
643
704
  <div
644
705
  ref={scrollContainerRef}
645
706
  class="overflow-x-auto"
@@ -704,7 +765,7 @@ function TableRenderer(props: {
704
765
  <Show when={needsClientPagination() && !isProgressiveMode()}>
705
766
  <div class="mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
706
767
  <span>
707
- Showing {clientRangeStart()}&ndash;{clientRangeEnd()} of {allRows().length.toLocaleString('fr-FR')}
768
+ Showing {clientRangeStart()}&ndash;{clientRangeEnd()} of {filteredRows().length.toLocaleString('fr-FR')}
708
769
  </span>
709
770
  <div class="flex items-center gap-1">
710
771
  <button
@@ -730,7 +791,7 @@ function TableRenderer(props: {
730
791
  <Show when={needsClientPagination() && isProgressiveMode()}>
731
792
  <div class="mt-3 flex flex-col items-center gap-2 text-xs text-gray-500 dark:text-gray-400">
732
793
  <span>
733
- {clientRangeStart()}&ndash;{clientRangeEnd()} of {allRows().length.toLocaleString('fr-FR')}
794
+ {clientRangeStart()}&ndash;{clientRangeEnd()} of {filteredRows().length.toLocaleString('fr-FR')}
734
795
  </span>
735
796
  <Show when={progressiveHasMore()}>
736
797
  <button