@seed-ship/mcp-ui-solid 4.1.0 → 4.2.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.
@@ -5,7 +5,7 @@
5
5
  * Sprint 4: Form state persistence
6
6
  */
7
7
 
8
- import { Component, createSignal, For, Show, onMount, createEffect } from 'solid-js'
8
+ import { Component, createSignal, For, Show, onMount, createEffect, onCleanup } from 'solid-js'
9
9
  import { FormFieldRenderer } from './FormFieldRenderer'
10
10
  import type { UIComponent, FormComponentParams, FormFieldParams } from '../types'
11
11
  import { useAction } from '../hooks/useAction'
@@ -56,11 +56,43 @@ export const FormRenderer: Component<FormRendererProps> = (props) => {
56
56
  })
57
57
  }
58
58
 
59
- // Initialize form data with default values
59
+ // Auto-submit countdown state (v4.2.0)
60
+ const [countdown, setCountdown] = createSignal<number | null>(null)
61
+ let countdownTimer: ReturnType<typeof setInterval> | null = null
62
+ const [userInteracted, setUserInteracted] = createSignal(false)
63
+
64
+ const cancelCountdown = () => {
65
+ if (countdownTimer) {
66
+ clearInterval(countdownTimer)
67
+ countdownTimer = null
68
+ }
69
+ setCountdown(null)
70
+ }
71
+
72
+ const handleUserInteraction = () => {
73
+ if (!userInteracted()) {
74
+ setUserInteracted(true)
75
+ cancelCountdown()
76
+ }
77
+ }
78
+
79
+ onCleanup(() => cancelCountdown())
80
+
81
+ /**
82
+ * Check if all required fields have prefill values
83
+ */
84
+ const allRequiredPrefilled = (): boolean => {
85
+ return params().fields
86
+ .filter((f) => f.required)
87
+ .every((f) => f.prefill != null)
88
+ }
89
+
90
+ // Initialize form data with default values, applying prefill (v4.2.0)
60
91
  const initializeForm = (clearStorage = false) => {
61
92
  const initial: Record<string, any> = {}
62
93
  for (const field of params().fields) {
63
- initial[field.name] = field.defaultValue ?? getFieldDefault(field.type)
94
+ // prefill takes priority over defaultValue
95
+ initial[field.name] = field.prefill ?? field.defaultValue ?? getFieldDefault(field.type)
64
96
  }
65
97
  setFormData(initial)
66
98
  setErrors({})
@@ -91,7 +123,29 @@ export const FormRenderer: Component<FormRendererProps> = (props) => {
91
123
  }
92
124
  })
93
125
 
126
+ // Auto-submit countdown (v4.2.0)
127
+ createEffect(() => {
128
+ const delay = params().autoSubmitDelay
129
+ if (!delay || !allRequiredPrefilled() || userInteracted()) return
130
+
131
+ let remaining = Math.ceil(delay / 1000)
132
+ setCountdown(remaining)
133
+
134
+ countdownTimer = setInterval(() => {
135
+ remaining--
136
+ if (remaining <= 0) {
137
+ cancelCountdown()
138
+ // Trigger submit programmatically
139
+ const form = document.querySelector(`#form-${props.component.id}`) as HTMLFormElement | null
140
+ if (form) form.requestSubmit()
141
+ } else {
142
+ setCountdown(remaining)
143
+ }
144
+ }, 1000)
145
+ })
146
+
94
147
  const handleFieldChange = (name: string, value: any) => {
148
+ handleUserInteraction()
95
149
  setFormData((prev) => ({ ...prev, [name]: value }))
96
150
  // Clear error on change
97
151
  if (errors()[name]) {
@@ -179,7 +233,7 @@ export const FormRenderer: Component<FormRendererProps> = (props) => {
179
233
  </h3>
180
234
  </Show>
181
235
 
182
- <form onSubmit={handleSubmit} noValidate>
236
+ <form id={`form-${props.component.id}`} onSubmit={handleSubmit} noValidate>
183
237
  <div class={layoutClass()}>
184
238
  <For each={params().fields}>
185
239
  {(field) => (
@@ -203,6 +257,22 @@ export const FormRenderer: Component<FormRendererProps> = (props) => {
203
257
  </div>
204
258
  </Show>
205
259
 
260
+ {/* Auto-submit countdown (v4.2.0) */}
261
+ <Show when={countdown() != null}>
262
+ <div class="mt-4 flex items-center gap-3 p-3 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-md">
263
+ <span class="text-sm text-blue-700 dark:text-blue-300">
264
+ {params().submitLabel || 'Submit'} in {countdown()}s...
265
+ </span>
266
+ <button
267
+ type="button"
268
+ onClick={() => { cancelCountdown(); setUserInteracted(true) }}
269
+ class="text-sm text-blue-600 dark:text-blue-400 underline hover:text-blue-800 dark:hover:text-blue-200"
270
+ >
271
+ Cancel
272
+ </button>
273
+ </div>
274
+ </Show>
275
+
206
276
  <div class="flex gap-2 pt-4 mt-4 border-t border-gray-200 dark:border-gray-700">
207
277
  <button
208
278
  type="submit"
@@ -323,6 +323,9 @@ export interface ShowWhenCondition {
323
323
  /**
324
324
  * Form field parameters
325
325
  */
326
+ /** How a prefilled value was obtained */
327
+ export type PrefillSource = 'user' | 'detected' | 'inferred' | 'default'
328
+
326
329
  export interface FormFieldParams {
327
330
  name: string
328
331
  type: FormFieldType
@@ -333,6 +336,16 @@ export interface FormFieldParams {
333
336
  disabled?: boolean
334
337
  defaultValue?: any
335
338
 
339
+ // Prefill — pre-populated value with source tracking (v4.2.0)
340
+ /** Pre-filled value. Field renders with this value instead of empty. */
341
+ prefill?: string | string[]
342
+ /** Human-readable display for prefilled value (e.g. "Rhône — déduit de Lyon") */
343
+ displayHint?: string
344
+ /** How this value was obtained. Drives visual treatment. */
345
+ source?: PrefillSource
346
+ /** If true, field is visually muted (but still editable on click/focus). */
347
+ muted?: boolean
348
+
336
349
  // Text/textarea specific
337
350
  minLength?: number
338
351
  maxLength?: number
@@ -428,6 +441,11 @@ export interface FormComponentParams {
428
441
  * Custom CSS class (Sprint 7)
429
442
  */
430
443
  className?: string
444
+ /**
445
+ * Auto-submit countdown in ms when all required fields are prefilled (v4.2.0).
446
+ * Shows a countdown with cancel button. Stops if user interacts.
447
+ */
448
+ autoSubmitDelay?: number
431
449
  }
432
450
 
433
451
  /**