@seed-ship/mcp-ui-solid 4.2.0 → 4.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-ship/mcp-ui-solid",
3
- "version": "4.2.0",
3
+ "version": "4.2.1",
4
4
  "description": "SolidJS components for rendering MCP-generated UI resources",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -525,15 +525,83 @@ const EmbeddedFormSection: Component<{
525
525
  onAction?: (action: string, data?: unknown) => void
526
526
  onSubmit?: (sectionId: string, values: Record<string, unknown>) => void
527
527
  }> = (props) => {
528
- const [formData, setFormData] = createSignal<Record<string, any>>({})
529
528
  const [dynamicOptions, setDynamicOptions] = createSignal<Record<string, Array<{ label: string; value: string }>>>({})
530
529
 
531
530
  const config = () => {
532
531
  const c = props.content as any
533
- return { fields: c?.fields || [], submitLabel: c?.submitLabel || 'Submit' }
532
+ return {
533
+ fields: c?.fields || [],
534
+ submitLabel: c?.submitLabel || 'Submit',
535
+ autoSubmitDelay: c?.autoSubmitDelay as number | undefined,
536
+ }
537
+ }
538
+
539
+ // Initialize form data with prefill values (v4.2.0)
540
+ const buildInitial = () => {
541
+ const initial: Record<string, any> = {}
542
+ for (const field of config().fields) {
543
+ initial[field.name] = field.prefill ?? field.defaultValue ?? ''
544
+ }
545
+ return initial
546
+ }
547
+
548
+ const [formData, setFormData] = createSignal<Record<string, any>>(buildInitial())
549
+
550
+ // Re-init when content changes (streaming updates)
551
+ createEffect(() => {
552
+ const fields = config().fields
553
+ if (fields.length > 0) {
554
+ setFormData((prev) => {
555
+ const next = { ...prev }
556
+ for (const field of fields) {
557
+ // Only apply prefill if the user hasn't changed the field yet
558
+ if (field.prefill != null && (next[field.name] === undefined || next[field.name] === '')) {
559
+ next[field.name] = field.prefill
560
+ }
561
+ }
562
+ return next
563
+ })
564
+ }
565
+ })
566
+
567
+ // Auto-submit countdown (v4.2.0)
568
+ const [countdown, setCountdown] = createSignal<number | null>(null)
569
+ let countdownTimer: ReturnType<typeof setInterval> | null = null
570
+ const [userInteracted, setUserInteracted] = createSignal(false)
571
+
572
+ const cancelCountdown = () => {
573
+ if (countdownTimer) { clearInterval(countdownTimer); countdownTimer = null }
574
+ setCountdown(null)
534
575
  }
535
576
 
536
- const updateField = (name: string, value: any) => setFormData(prev => ({ ...prev, [name]: value }))
577
+ onCleanup(() => cancelCountdown())
578
+
579
+ createEffect(() => {
580
+ const delay = config().autoSubmitDelay
581
+ if (!delay || userInteracted()) return
582
+ const allRequiredPrefilled = config().fields
583
+ .filter((f: any) => f.required)
584
+ .every((f: any) => f.prefill != null)
585
+ if (!allRequiredPrefilled) return
586
+
587
+ let remaining = Math.ceil(delay / 1000)
588
+ setCountdown(remaining)
589
+ countdownTimer = setInterval(() => {
590
+ remaining--
591
+ if (remaining <= 0) {
592
+ cancelCountdown()
593
+ const form = document.querySelector(`#scratchpad-form-${props.sectionId}`) as HTMLFormElement | null
594
+ if (form) form.requestSubmit()
595
+ } else {
596
+ setCountdown(remaining)
597
+ }
598
+ }, 1000)
599
+ })
600
+
601
+ const updateField = (name: string, value: any) => {
602
+ if (!userInteracted()) { setUserInteracted(true); cancelCountdown() }
603
+ setFormData(prev => ({ ...prev, [name]: value }))
604
+ }
537
605
 
538
606
  // depends_on reactive (#9)
539
607
  createEffect(() => {
@@ -587,7 +655,7 @@ const EmbeddedFormSection: Component<{
587
655
  }
588
656
 
589
657
  return (
590
- <form onSubmit={handleSubmit} class="flex flex-col gap-3">
658
+ <form id={`scratchpad-form-${props.sectionId}`} onSubmit={handleSubmit} class="flex flex-col gap-3">
591
659
  <For each={config().fields}>
592
660
  {(field) => (
593
661
  <FormFieldRenderer
@@ -598,6 +666,20 @@ const EmbeddedFormSection: Component<{
598
666
  />
599
667
  )}
600
668
  </For>
669
+ <Show when={countdown() != null}>
670
+ <div class="flex items-center gap-3 p-2 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-md text-sm">
671
+ <span class="text-blue-700 dark:text-blue-300">
672
+ {config().submitLabel} in {countdown()}s...
673
+ </span>
674
+ <button
675
+ type="button"
676
+ onClick={() => { cancelCountdown(); setUserInteracted(true) }}
677
+ class="text-blue-600 dark:text-blue-400 underline hover:text-blue-800 dark:hover:text-blue-200"
678
+ >
679
+ Cancel
680
+ </button>
681
+ </div>
682
+ </Show>
601
683
  <div class="flex justify-end">
602
684
  <button type="submit" class="px-4 py-2 text-sm font-medium rounded-lg text-white bg-blue-600 hover:bg-blue-700 transition-colors">
603
685
  {config().submitLabel}