@prefecthq/prefab-ui-playground 0.19.0 → 0.19.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/playground.html +58 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prefecthq/prefab-ui-playground",
3
- "version": "0.19.0",
3
+ "version": "0.19.1",
4
4
  "description": "Interactive Prefab playground — runs Python in the browser via Pyodide.",
5
5
  "homepage": "https://prefab.prefect.io",
6
6
  "repository": {
package/playground.html CHANGED
@@ -15283,6 +15283,7 @@ class Form(ContainerComponent):
15283
15283
  fields_only: Literal[True],
15284
15284
  submit_label: str = "Submit",
15285
15285
  on_submit: Action | list[Action] | None = None,
15286
+ defaults: dict[str, Any] | None = None,
15286
15287
  css_class: str | None = None,
15287
15288
  ) -> list[Any]: ...
15288
15289
 
@@ -15295,6 +15296,7 @@ class Form(ContainerComponent):
15295
15296
  fields_only: Literal[False] = ...,
15296
15297
  submit_label: str = "Submit",
15297
15298
  on_submit: Action | list[Action] | None = None,
15299
+ defaults: dict[str, Any] | None = None,
15298
15300
  css_class: str | None = None,
15299
15301
  ) -> Form: ...
15300
15302
 
@@ -15306,6 +15308,7 @@ class Form(ContainerComponent):
15306
15308
  fields_only: bool = False,
15307
15309
  submit_label: str = "Submit",
15308
15310
  on_submit: Action | list[Action] | None = None,
15311
+ defaults: dict[str, Any] | None = None,
15309
15312
  css_class: str | None = None,
15310
15313
  ) -> Form | list[Any]:
15311
15314
  """Generate a form from a Pydantic model.
@@ -15364,11 +15367,23 @@ class Form(ContainerComponent):
15364
15367
  submit_label: Text for the submit button.
15365
15368
  on_submit: Action(s) fired on submit. A \`CallTool\` with no
15366
15369
  arguments gets auto-filled from model fields.
15370
+ defaults: Runtime values keyed by field name. These override the
15371
+ model's class-level \`Field(default=...)\` for this render only,
15372
+ letting callers (e.g. LLM agents) pre-populate the form with
15373
+ context-specific values. Unknown keys raise \`ValueError\`.
15367
15374
  css_class: Additional CSS classes on the form container.
15368
15375
  """
15369
15376
  from prefab_ui.components.base import _component_stack
15370
15377
  from prefab_ui.components.button import Button
15371
15378
 
15379
+ if defaults:
15380
+ unknown = set(defaults) - set(model.model_fields)
15381
+ if unknown:
15382
+ raise ValueError(
15383
+ f"defaults contains unknown field(s) for "
15384
+ f"{model.__name__}: {sorted(unknown)}"
15385
+ )
15386
+
15372
15387
  if fields_only:
15373
15388
  # Suppress auto-parenting during creation to avoid duplicates
15374
15389
  # (Labels/Inputs would otherwise auto-parent to both the
@@ -15378,7 +15393,7 @@ class Form(ContainerComponent):
15378
15393
  with defer():
15379
15394
  children: list[Any] = []
15380
15395
  for name, field_info in model.model_fields.items():
15381
- component = _field_to_component(name, field_info)
15396
+ component = _field_to_component(name, field_info, defaults)
15382
15397
  if component is not None:
15383
15398
  children.append(component)
15384
15399
 
@@ -15400,7 +15415,7 @@ class Form(ContainerComponent):
15400
15415
  children = []
15401
15416
 
15402
15417
  for name, field_info in model.model_fields.items():
15403
- component = _field_to_component(name, field_info)
15418
+ component = _field_to_component(name, field_info, defaults)
15404
15419
  if component is not None:
15405
15420
  children.append(component)
15406
15421
 
@@ -15446,6 +15461,25 @@ def _maybe_enrich_tool_call(
15446
15461
  return CallTool(**kwargs)
15447
15462
 
15448
15463
 
15464
+ def _format_date_default(value: Any, kind: type) -> str | None:
15465
+ """Serialize a date/time/datetime default to the HTML input format."""
15466
+ if value is None:
15467
+ return None
15468
+ if isinstance(value, str):
15469
+ return value
15470
+ if kind is datetime.datetime and isinstance(value, datetime.datetime):
15471
+ return value.strftime("%Y-%m-%dT%H:%M")
15472
+ if (
15473
+ kind is datetime.date
15474
+ and isinstance(value, datetime.date)
15475
+ and not isinstance(value, datetime.datetime)
15476
+ ):
15477
+ return value.isoformat()
15478
+ if kind is datetime.time and isinstance(value, datetime.time):
15479
+ return value.isoformat(timespec="minutes")
15480
+ return None
15481
+
15482
+
15449
15483
  def _humanize(name: str) -> str:
15450
15484
  """Convert snake_case field name to Title Case label."""
15451
15485
  return name.replace("_", " ").title()
@@ -15498,7 +15532,11 @@ def _get_ui_hints(field_info: FieldInfo) -> dict[str, Any]:
15498
15532
  return {}
15499
15533
 
15500
15534
 
15501
- def _field_to_component(name: str, field_info: FieldInfo) -> Any:
15535
+ def _field_to_component(
15536
+ name: str,
15537
+ field_info: FieldInfo,
15538
+ defaults: dict[str, Any] | None = None,
15539
+ ) -> Any:
15502
15540
  """Map a single Pydantic field to the appropriate form component(s)."""
15503
15541
  from prefab_ui.components.checkbox import Checkbox
15504
15542
  from prefab_ui.components.column import Column
@@ -15519,9 +15557,12 @@ def _field_to_component(name: str, field_info: FieldInfo) -> Any:
15519
15557
 
15520
15558
  label_text = field_info.title or _humanize(name)
15521
15559
  placeholder = field_info.description or label_text
15522
- default = (
15523
- field_info.default if field_info.default is not PydanticUndefined else None
15524
- )
15560
+ if defaults is not None and name in defaults:
15561
+ default = defaults[name]
15562
+ elif field_info.default is not PydanticUndefined:
15563
+ default = field_info.default
15564
+ else:
15565
+ default = None
15525
15566
  constraints = _extract_constraints(field_info)
15526
15567
  ui_hints = _get_ui_hints(field_info)
15527
15568
 
@@ -15584,26 +15625,34 @@ def _field_to_component(name: str, field_info: FieldInfo) -> Any:
15584
15625
 
15585
15626
  # date/time types → specialized input
15586
15627
  if inner is datetime.date:
15628
+ value = _format_date_default(default, datetime.date)
15587
15629
  col = Column(gap=2)
15588
15630
  col.children = [
15589
15631
  Label(label_text, optional=not required),
15590
- Input(input_type="date", name=name, required=required),
15632
+ Input(input_type="date", name=name, value=value, required=required),
15591
15633
  ]
15592
15634
  return col
15593
15635
 
15594
15636
  if inner is datetime.time:
15637
+ value = _format_date_default(default, datetime.time)
15595
15638
  col = Column(gap=2)
15596
15639
  col.children = [
15597
15640
  Label(label_text, optional=not required),
15598
- Input(input_type="time", name=name, required=required),
15641
+ Input(input_type="time", name=name, value=value, required=required),
15599
15642
  ]
15600
15643
  return col
15601
15644
 
15602
15645
  if inner is datetime.datetime:
15646
+ value = _format_date_default(default, datetime.datetime)
15603
15647
  col = Column(gap=2)
15604
15648
  col.children = [
15605
15649
  Label(label_text, optional=not required),
15606
- Input(input_type="datetime-local", name=name, required=required),
15650
+ Input(
15651
+ input_type="datetime-local",
15652
+ name=name,
15653
+ value=value,
15654
+ required=required,
15655
+ ),
15607
15656
  ]
15608
15657
  return col
15609
15658