@smartspace/chat-ui 1.14.4-pr.443.60a79cd → 1.14.4-pr.447.d73b67e

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/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import MuiButton from '@mui/material/Button';
2
2
  import IconButton from '@mui/material/IconButton';
3
- import { Loader2, Cpu, ChevronDown, Check, Globe, Zap, SlidersHorizontal, X, Paperclip, Mic, Square, Send, Minimize2, AlertTriangle, FileImage, FileVideo, FileAudio, FileArchive, FileCode, FileSpreadsheet, Presentation, FileText, ChevronUp, ExternalLink, Download, ShieldAlert, Copy } from 'lucide-react';
4
- import * as React8 from 'react';
5
- import { createContext, forwardRef, useImperativeHandle, useRef, useState, useEffect, useMemo, useCallback, useContext, useSyncExternalStore } from 'react';
3
+ import { Loader2, ChevronDown, Check, Cpu, SlidersHorizontal, X, Globe, Zap, Paperclip, Mic, Square, Send, Minimize2, AlertTriangle, FileImage, FileVideo, FileAudio, FileArchive, FileCode, FileSpreadsheet, Presentation, FileText, ChevronUp, ExternalLink, Download, ShieldAlert, Copy } from 'lucide-react';
4
+ import * as React9 from 'react';
5
+ import React9__default, { createContext, forwardRef, useImperativeHandle, useRef, useState, useEffect, useMemo, useCallback, useContext, useSyncExternalStore } from 'react';
6
6
  import { createPortal } from 'react-dom';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import { useQuery, queryOptions, useQueryClient, useMutation, skipToken } from '@tanstack/react-query';
@@ -24,7 +24,6 @@ import '@milkdown/prose/inputrules';
24
24
  import { withJsonFormsControlProps, withJsonFormsLayoutProps, JsonForms, ResolvedJsonFormsDispatch } from '@jsonforms/react';
25
25
  import { rankWith, uiTypeIs, createAjv } from '@jsonforms/core';
26
26
  import { vanillaRenderers, vanillaCells } from '@jsonforms/vanilla-renderers';
27
- import { FormControl, InputLabel, Select, MenuItem, useTheme, Box, Skeleton as Skeleton$1 } from '@mui/material';
28
27
  import 'ace-builds/src-noconflict/ace';
29
28
  import 'ace-builds/src-noconflict/ext-language_tools';
30
29
  import 'ace-builds/src-noconflict/mode-json';
@@ -57,6 +56,7 @@ import relativeTime from 'dayjs/plugin/relativeTime';
57
56
  import utc from 'dayjs/plugin/utc';
58
57
  import { Slot } from '@radix-ui/react-slot';
59
58
  import { cva } from 'class-variance-authority';
59
+ import { useTheme, Box, Skeleton as Skeleton$1 } from '@mui/material';
60
60
 
61
61
  var __defProp = Object.defineProperty;
62
62
  var __export = (target, all5) => {
@@ -2443,6 +2443,31 @@ function useFlowRunVariables(flowRunId) {
2443
2443
  enabled: !!flowRunId
2444
2444
  });
2445
2445
  }
2446
+
2447
+ // src/chat-variables/renders/fieldStyles.ts
2448
+ var scaleFor = (surface) => surface === "form" ? "comfortable" : "dense";
2449
+ var fieldLabelClass = (scale) => `block truncate font-medium text-muted-foreground ${scale === "dense" ? "mb-1 text-xs" : "mb-1.5 text-sm"}`;
2450
+ var CONTROL_BASE = "rounded-md border bg-background text-foreground transition-colors placeholder:text-muted-foreground ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60";
2451
+ var borderClass = (hasError) => hasError ? "border-destructive" : "border-input";
2452
+ var fieldControlClass = (scale, hasError) => `w-full ${CONTROL_BASE} ${borderClass(hasError)} ${scale === "dense" ? "px-2.5 py-1.5 text-xs" : "px-3 py-2 text-sm"}`;
2453
+ var fieldTriggerClass = (scale, hasError) => `w-full ${CONTROL_BASE} ${borderClass(
2454
+ hasError
2455
+ )} flex items-center justify-between gap-2 text-left ${scale === "dense" ? "h-8 px-2.5 text-xs" : "h-10 px-3 text-sm"}`;
2456
+ var fieldNumberClass = (scale, hasError) => `${CONTROL_BASE} ${borderClass(hasError)} tabular-nums ${scale === "dense" ? "h-8 w-20 px-2 text-xs" : "h-10 w-24 px-2.5 text-sm"}`;
2457
+ var fieldHintClass = "mt-1 text-xs text-muted-foreground";
2458
+ var fieldErrorClass = "mt-1 text-xs text-destructive";
2459
+ var fieldRowClass = "flex items-center justify-between gap-3";
2460
+ var fieldRowLabelClass = (scale) => `min-w-0 flex-1 truncate font-medium text-muted-foreground ${scale === "dense" ? "text-xs" : "text-sm"}`;
2461
+ var pillClass = (opts) => `flex h-8 shrink-0 items-center gap-1.5 rounded-full border px-2.5 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary ${opts.active ? "border-primary/30 bg-primary/10 text-primary" : "border-border/70 bg-transparent text-muted-foreground hover:bg-secondary"} ${opts.hasError ? "border-destructive text-destructive" : ""} ${opts.disabled ? "cursor-not-allowed opacity-60" : ""}`;
2462
+ var pillLabelClass = "max-w-[9rem] truncate";
2463
+ var pillValueClass = "max-w-[9rem] truncate text-foreground";
2464
+ var switchTrackClass = (on, disabled) => `peer inline-flex h-6 w-11 shrink-0 items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background ${on ? "bg-primary" : "bg-input"} ${disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"}`;
2465
+ var switchThumbClass = (on) => `pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform ${on ? "translate-x-5" : "translate-x-0"}`;
2466
+ var POPOVER_WIDTH_PX = 288;
2467
+ var POPOVER_MAX_HEIGHT_PX = 288;
2468
+ var popoverClass = "flex flex-col overflow-hidden rounded-xl border border-border bg-popover shadow-lg";
2469
+ var popoverRowClass = "flex w-full items-center gap-2.5 px-3 py-2 text-left transition-colors hover:bg-secondary/60";
2470
+ var popoverHeadingClass = "text-[10px] font-semibold uppercase tracking-wide text-muted-foreground";
2446
2471
  function iconFor(label) {
2447
2472
  const name = (label ?? "").toLowerCase();
2448
2473
  if (/search|web|browse|internet/.test(name)) return Globe;
@@ -2458,7 +2483,8 @@ var BooleanRenderer = ({
2458
2483
  errors,
2459
2484
  uischema,
2460
2485
  visible,
2461
- enabled
2486
+ enabled,
2487
+ config
2462
2488
  }) => {
2463
2489
  const onToggle = useCallback(() => {
2464
2490
  handleChange(path2, !data);
@@ -2469,13 +2495,17 @@ var BooleanRenderer = ({
2469
2495
  const hasError = !!errors && errors.length > 0;
2470
2496
  const isChecked = Boolean(data);
2471
2497
  const Icon = iconFor(label);
2498
+ const surface = (config ?? {}).surface ?? "form";
2499
+ const scale = scaleFor(surface);
2472
2500
  const tooltip = [label, description, hasError ? errors : null].filter(Boolean).join(" \u2014 ");
2473
- return /* @__PURE__ */ jsxs(
2501
+ const toggle = /* @__PURE__ */ jsx(
2474
2502
  "button",
2475
2503
  {
2476
2504
  id: `toggle-${path2}`,
2477
2505
  type: "button",
2478
- "aria-pressed": isChecked,
2506
+ role: surface === "bar" ? void 0 : "switch",
2507
+ "aria-pressed": surface === "bar" ? isChecked : void 0,
2508
+ "aria-checked": surface === "bar" ? void 0 : isChecked,
2479
2509
  "aria-label": label,
2480
2510
  title: tooltip || void 0,
2481
2511
  onClick: onToggle,
@@ -2485,16 +2515,28 @@ var BooleanRenderer = ({
2485
2515
  e.currentTarget.blur();
2486
2516
  }
2487
2517
  },
2488
- className: `boolean-switch flex h-8 shrink-0 items-center gap-1.5 rounded-full border px-2.5 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary ${isChecked ? "border-primary/30 bg-primary/10 text-primary" : "border-transparent bg-transparent text-muted-foreground hover:bg-secondary"} ${hasError ? "border-destructive text-destructive" : ""} ${isDisabled ? "cursor-not-allowed opacity-60" : ""}`,
2489
- children: [
2518
+ className: surface === "bar" ? `boolean-switch ${pillClass({
2519
+ active: isChecked,
2520
+ hasError,
2521
+ disabled: isDisabled
2522
+ })}` : switchTrackClass(isChecked, isDisabled),
2523
+ children: surface === "bar" ? /* @__PURE__ */ jsxs(Fragment, { children: [
2490
2524
  /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
2491
2525
  label && // Desktop-first: the app and the package each ship a full Tailwind
2492
- // build, so a base `hidden` can out-order `sm:inline`. `max-sm:hidden`
2493
- // is the form that survives both cascades.
2494
- /* @__PURE__ */ jsx("span", { className: "inline max-w-[9rem] truncate max-sm:hidden", children: label })
2495
- ]
2526
+ // build, so a base `hidden` can out-order `sm:inline`.
2527
+ // `max-sm:hidden` is the form that survives both cascades.
2528
+ /* @__PURE__ */ jsx("span", { className: `inline ${pillLabelClass} max-sm:hidden`, children: label })
2529
+ ] }) : /* @__PURE__ */ jsx("span", { className: switchThumbClass(isChecked) })
2496
2530
  }
2497
2531
  );
2532
+ if (surface === "bar") return toggle;
2533
+ return /* @__PURE__ */ jsxs("div", { className: "ss-jsonforms-field ss-jsonforms-boolean", children: [
2534
+ /* @__PURE__ */ jsxs("div", { className: fieldRowClass, children: [
2535
+ /* @__PURE__ */ jsx("label", { htmlFor: `toggle-${path2}`, className: fieldRowLabelClass(scale), children: label }),
2536
+ toggle
2537
+ ] }),
2538
+ hasError && /* @__PURE__ */ jsx("div", { className: fieldErrorClass, children: errors })
2539
+ ] });
2498
2540
  };
2499
2541
  var booleanRendererTester = rankWith(40, (uischema, schema) => {
2500
2542
  if (uischema.type !== "Control") return false;
@@ -2504,6 +2546,104 @@ var booleanRendererTester = rankWith(40, (uischema, schema) => {
2504
2546
  return fieldSchema?.type === "boolean";
2505
2547
  });
2506
2548
  var BooleanRendererControl = withJsonFormsControlProps(BooleanRenderer);
2549
+ var GAP_PX = 8;
2550
+ var MIN_TRIGGER_WIDTH_PX = 224;
2551
+ var MIN_PANEL_HEIGHT_PX = 180;
2552
+ var NestedPopoverContext = React9__default.createContext(null);
2553
+ function useAnchoredPopover({
2554
+ trigger,
2555
+ popover,
2556
+ width,
2557
+ maxHeight,
2558
+ role,
2559
+ label
2560
+ }) {
2561
+ const [isOpen, setIsOpen] = useState(false);
2562
+ const parentRegistry = useContext(NestedPopoverContext);
2563
+ const descendants = useRef(/* @__PURE__ */ new Set());
2564
+ const close = useCallback(() => setIsOpen(false), []);
2565
+ const toggle = useCallback(() => setIsOpen((open) => !open), []);
2566
+ const registry = useMemo(
2567
+ () => ({
2568
+ register: (el) => {
2569
+ descendants.current.add(el);
2570
+ parentRegistry?.register(el);
2571
+ },
2572
+ unregister: (el) => {
2573
+ descendants.current.delete(el);
2574
+ parentRegistry?.unregister(el);
2575
+ }
2576
+ }),
2577
+ [parentRegistry]
2578
+ );
2579
+ useEffect(() => {
2580
+ if (!isOpen || !parentRegistry) return;
2581
+ const el = popover.current;
2582
+ if (!el) return;
2583
+ parentRegistry.register(el);
2584
+ return () => parentRegistry.unregister(el);
2585
+ }, [isOpen, parentRegistry, popover]);
2586
+ useEffect(() => {
2587
+ if (!isOpen) return;
2588
+ const onPointerDown = (event) => {
2589
+ const target = event.target;
2590
+ if (trigger.current?.contains(target)) return;
2591
+ if (popover.current?.contains(target)) return;
2592
+ for (const el of descendants.current) {
2593
+ if (el.contains(target)) return;
2594
+ }
2595
+ close();
2596
+ };
2597
+ const onKeyDown = (event) => {
2598
+ if (event.key === "Escape") close();
2599
+ };
2600
+ document.addEventListener("mousedown", onPointerDown);
2601
+ document.addEventListener("keydown", onKeyDown);
2602
+ window.addEventListener("resize", close);
2603
+ return () => {
2604
+ document.removeEventListener("mousedown", onPointerDown);
2605
+ document.removeEventListener("keydown", onKeyDown);
2606
+ window.removeEventListener("resize", close);
2607
+ };
2608
+ }, [isOpen, close, trigger, popover]);
2609
+ const renderPopover = (children2) => {
2610
+ if (!isOpen) return null;
2611
+ const anchor = trigger.current?.getBoundingClientRect();
2612
+ if (!anchor) return null;
2613
+ const panelWidth = width === "trigger" ? Math.max(anchor.width, MIN_TRIGGER_WIDTH_PX) : width;
2614
+ const spaceAbove = anchor.top - GAP_PX * 2;
2615
+ const spaceBelow = window.innerHeight - anchor.bottom - GAP_PX * 2;
2616
+ const openAbove = spaceAbove >= MIN_PANEL_HEIGHT_PX || spaceAbove >= spaceBelow;
2617
+ const panelMaxHeight = Math.max(
2618
+ 0,
2619
+ Math.min(maxHeight, openAbove ? spaceAbove : spaceBelow)
2620
+ );
2621
+ return createPortal(
2622
+ /* @__PURE__ */ jsx(
2623
+ "div",
2624
+ {
2625
+ ref: popover,
2626
+ role,
2627
+ "aria-label": label,
2628
+ className: `fixed z-50 ${popoverClass}`,
2629
+ style: {
2630
+ width: panelWidth,
2631
+ maxWidth: `calc(100vw - ${GAP_PX * 4}px)`,
2632
+ left: Math.max(
2633
+ GAP_PX,
2634
+ Math.min(anchor.left, window.innerWidth - panelWidth - GAP_PX)
2635
+ ),
2636
+ ...openAbove ? { bottom: window.innerHeight - anchor.top + GAP_PX } : { top: anchor.bottom + GAP_PX },
2637
+ maxHeight: panelMaxHeight
2638
+ },
2639
+ children: /* @__PURE__ */ jsx(NestedPopoverContext.Provider, { value: registry, children: children2 })
2640
+ }
2641
+ ),
2642
+ document.body
2643
+ );
2644
+ };
2645
+ return { isOpen, close, toggle, renderPopover };
2646
+ }
2507
2647
  function hasConst(x) {
2508
2648
  return !!x && typeof x === "object" && "const" in x;
2509
2649
  }
@@ -2542,185 +2682,109 @@ var DropdownRenderer = ({
2542
2682
  label,
2543
2683
  description,
2544
2684
  errors,
2545
- uischema
2685
+ config,
2686
+ uischema,
2687
+ visible
2546
2688
  }) => {
2689
+ const triggerRef = useRef(null);
2690
+ const listRef = useRef(null);
2691
+ const surface = (config ?? {}).surface ?? "form";
2692
+ const scale = scaleFor(surface);
2693
+ const { isOpen, close, toggle, renderPopover } = useAnchoredPopover({
2694
+ trigger: triggerRef,
2695
+ popover: listRef,
2696
+ // A select's list lines up with its own control.
2697
+ width: "trigger",
2698
+ maxHeight: POPOVER_MAX_HEIGHT_PX,
2699
+ role: "listbox",
2700
+ label: label || "Options"
2701
+ });
2547
2702
  const options = toOptions(schema);
2548
- const handleSelectionChange = useCallback(
2549
- (event) => {
2550
- handleChange(path2, event.target.value);
2703
+ const select = useCallback(
2704
+ (value) => {
2705
+ handleChange(path2, value);
2706
+ close();
2551
2707
  },
2552
- [handleChange, path2]
2708
+ [handleChange, path2, close]
2553
2709
  );
2554
- const displayValue = data ?? "";
2710
+ if (!visible) return null;
2555
2711
  const readOnly = uischema?.access === "Read";
2556
2712
  const isDisabled = !enabled || readOnly;
2557
- return /* @__PURE__ */ jsxs(
2558
- FormControl,
2713
+ const hasError = !!errors;
2714
+ const selected = options.find((option) => option.const === data);
2715
+ const valueText = selected ? selected.title ?? String(selected.const) : data != null && data !== "" ? String(data) : "";
2716
+ const triggerId = `${path2}-trigger`;
2717
+ const isBar = surface === "bar";
2718
+ const placeholder = `Select ${(label || "value").toLowerCase()}\u2026`;
2719
+ const trigger = /* @__PURE__ */ jsxs(
2720
+ "button",
2559
2721
  {
2560
- variant: "outlined",
2561
- size: "small",
2562
- fullWidth: true,
2563
- error: !!errors,
2722
+ id: triggerId,
2723
+ ref: triggerRef,
2724
+ type: "button",
2564
2725
  disabled: isDisabled,
2565
- className: "compact-field",
2566
- sx: {
2567
- minWidth: "220px"
2568
- },
2726
+ onClick: toggle,
2727
+ "aria-haspopup": "listbox",
2728
+ "aria-expanded": isOpen,
2729
+ "aria-label": label,
2730
+ title: [label, valueText, hasError ? errors : null].filter(Boolean).join(" \u2014 ") || void 0,
2731
+ className: isBar ? `ss-jsonforms-select-trigger ${pillClass({
2732
+ active: isOpen,
2733
+ hasError,
2734
+ disabled: isDisabled
2735
+ })}` : `ss-jsonforms-select-trigger ${fieldTriggerClass(scale, hasError)}`,
2569
2736
  children: [
2737
+ isBar && label && /* @__PURE__ */ jsx("span", { className: `inline ${pillLabelClass} max-sm:hidden`, children: label }),
2570
2738
  /* @__PURE__ */ jsx(
2571
- InputLabel,
2739
+ "span",
2572
2740
  {
2573
- id: `${path2}-label`,
2574
- sx: {
2575
- color: "#6b7280",
2576
- fontSize: "0.875rem",
2577
- fontWeight: 500,
2578
- "&.Mui-focused": {
2579
- color: "#6366f1"
2580
- },
2581
- "&.Mui-error": {
2582
- color: "#ef4444"
2583
- }
2584
- },
2585
- children: label
2741
+ className: isBar ? pillValueClass : `min-w-0 flex-1 truncate ${valueText ? "" : "text-muted-foreground"}`,
2742
+ children: valueText || (isBar ? "\u2014" : placeholder)
2586
2743
  }
2587
2744
  ),
2588
2745
  /* @__PURE__ */ jsx(
2589
- Select,
2746
+ ChevronDown,
2590
2747
  {
2591
- labelId: `${path2}-label`,
2592
- value: displayValue,
2593
- onChange: handleSelectionChange,
2594
- label,
2595
- sx: {
2596
- "& .MuiOutlinedInput-root": {
2597
- backgroundColor: "#fafafa",
2598
- borderRadius: "8px",
2599
- transition: "all 0.2s ease-in-out",
2600
- height: "40px",
2601
- // Same height as model dropdown
2602
- "& fieldset": {
2603
- borderColor: "#e5e7eb",
2604
- borderWidth: "1px"
2605
- },
2606
- "&:hover": {
2607
- backgroundColor: "#ffffff",
2608
- "& fieldset": {
2609
- borderColor: "#9ca3af"
2610
- }
2611
- },
2612
- "&.Mui-focused": {
2613
- backgroundColor: "#ffffff",
2614
- "& fieldset": {
2615
- borderColor: "#6366f1",
2616
- borderWidth: "2px"
2617
- }
2618
- },
2619
- "&.Mui-error": {
2620
- "& fieldset": {
2621
- borderColor: "#ef4444"
2622
- }
2623
- }
2624
- },
2625
- "& .MuiSelect-select": {
2626
- backgroundColor: "#fafafa",
2627
- borderRadius: "8px",
2628
- transition: "all 0.2s ease-in-out",
2629
- height: "40px",
2630
- display: "flex",
2631
- alignItems: "center",
2632
- paddingRight: "32px !important",
2633
- "&:hover": {
2634
- backgroundColor: "#ffffff"
2635
- },
2636
- "&.Mui-focused": {
2637
- backgroundColor: "#ffffff"
2638
- }
2639
- },
2640
- "& .MuiOutlinedInput-notchedOutline": {
2641
- borderColor: "#e5e7eb",
2642
- borderWidth: "1px",
2643
- borderRadius: "8px"
2644
- },
2645
- "&:hover .MuiOutlinedInput-notchedOutline": {
2646
- borderColor: "#9ca3af"
2647
- },
2648
- "&.Mui-focused .MuiOutlinedInput-notchedOutline": {
2649
- borderColor: "#6366f1",
2650
- borderWidth: "2px"
2651
- },
2652
- "&.Mui-error .MuiOutlinedInput-notchedOutline": {
2653
- borderColor: "#ef4444"
2654
- },
2655
- "& .MuiSelect-icon": {
2656
- color: "#9ca3af",
2657
- "&:hover": {
2658
- color: "#6b7280"
2659
- }
2660
- }
2661
- },
2662
- MenuProps: {
2663
- PaperProps: {
2664
- sx: {
2665
- borderRadius: "8px",
2666
- boxShadow: "0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
2667
- border: "1px solid #e5e7eb",
2668
- marginTop: "4px",
2669
- maxHeight: "280px"
2670
- }
2671
- },
2672
- MenuListProps: {
2673
- sx: {
2674
- padding: 0
2675
- }
2676
- }
2677
- },
2678
- children: options.map((option, index) => {
2679
- const raw2 = option.const;
2680
- const key = typeof raw2 === "string" || typeof raw2 === "number" ? String(raw2) : String(index);
2681
- const value = typeof raw2 === "string" || typeof raw2 === "number" ? raw2 : String(raw2 ?? index);
2682
- return /* @__PURE__ */ jsx(
2683
- MenuItem,
2684
- {
2685
- value,
2686
- sx: {
2687
- padding: "12px 16px",
2688
- fontSize: "0.875rem",
2689
- borderBottom: "1px solid #f3f4f6",
2690
- "&:last-child": {
2691
- borderBottom: "none"
2692
- },
2693
- "&:hover": {
2694
- backgroundColor: "#f8fafc"
2695
- },
2696
- "&.Mui-selected": {
2697
- backgroundColor: "#eff6ff",
2698
- "&:hover": {
2699
- backgroundColor: "#dbeafe"
2700
- }
2701
- }
2702
- },
2703
- children: option.title ?? String(raw2)
2704
- },
2705
- key
2706
- );
2707
- })
2708
- }
2709
- ),
2710
- (description || errors) && /* @__PURE__ */ jsx(
2711
- "div",
2712
- {
2713
- style: {
2714
- fontSize: "0.75rem",
2715
- marginTop: "4px",
2716
- color: errors ? "#ef4444" : "#6b7280"
2717
- },
2718
- children: errors || description
2748
+ className: `shrink-0 opacity-50 ${isBar ? "h-3 w-3" : "h-4 w-4"}`
2719
2749
  }
2720
2750
  )
2721
2751
  ]
2722
2752
  }
2723
2753
  );
2754
+ return /* @__PURE__ */ jsxs("div", { className: "ss-jsonforms-field ss-jsonforms-select compact-field", children: [
2755
+ !isBar && label && /* @__PURE__ */ jsx("label", { htmlFor: triggerId, className: fieldLabelClass(scale), children: label }),
2756
+ trigger,
2757
+ !isBar && (errors || description) && /* @__PURE__ */ jsx("div", { className: errors ? fieldErrorClass : fieldHintClass, children: errors || description }),
2758
+ renderPopover(
2759
+ /* @__PURE__ */ jsxs("div", { className: "min-h-0 overflow-y-auto py-1", children: [
2760
+ options.length === 0 && /* @__PURE__ */ jsx("p", { className: "px-3 py-6 text-center text-xs text-muted-foreground", children: "No options available" }),
2761
+ options.map((option, index) => {
2762
+ const active = option.const === data;
2763
+ const text6 = option.title ?? String(option.const);
2764
+ return /* @__PURE__ */ jsxs(
2765
+ "button",
2766
+ {
2767
+ type: "button",
2768
+ role: "option",
2769
+ "aria-selected": active,
2770
+ onClick: () => select(option.const),
2771
+ className: popoverRowClass,
2772
+ children: [
2773
+ /* @__PURE__ */ jsx(
2774
+ Check,
2775
+ {
2776
+ className: `h-3.5 w-3.5 shrink-0 ${active ? "text-primary" : "opacity-0"}`
2777
+ }
2778
+ ),
2779
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm text-foreground", children: text6 })
2780
+ ]
2781
+ },
2782
+ typeof option.const === "string" || typeof option.const === "number" ? String(option.const) : `option-${index}`
2783
+ );
2784
+ })
2785
+ ] })
2786
+ )
2787
+ ] });
2724
2788
  };
2725
2789
  var dropdownRendererTester = rankWith(
2726
2790
  90,
@@ -2738,8 +2802,8 @@ var dropdownRendererTester = rankWith(
2738
2802
  return false;
2739
2803
  }
2740
2804
  const hasDropdownOptions = !!(fieldSchema.oneOf || fieldSchema.anyOf || fieldSchema.enum && Array.isArray(fieldSchema.enum));
2741
- const isModelSelector = typeof fieldSchema === "object" && fieldSchema && fieldSchema["x-model-selector"] === true || fieldSchema.title === "ModelId" || fieldSchema.format === "uuid";
2742
- return hasDropdownOptions && !isModelSelector;
2805
+ const isModelSelector2 = typeof fieldSchema === "object" && fieldSchema && fieldSchema["x-model-selector"] === true || fieldSchema.title === "ModelId" || fieldSchema.format === "uuid";
2806
+ return hasDropdownOptions && !isModelSelector2;
2743
2807
  }
2744
2808
  );
2745
2809
  var DropdownRendererControl = withJsonFormsControlProps(DropdownRenderer);
@@ -2774,7 +2838,8 @@ var JsonEditorRenderer = ({
2774
2838
  uischema,
2775
2839
  visible,
2776
2840
  enabled,
2777
- required
2841
+ required,
2842
+ config
2778
2843
  }) => {
2779
2844
  const [jsonValue, setJsonValue] = useState("");
2780
2845
  const [displayedParseError, setDisplayedParseError] = useState(
@@ -2828,43 +2893,19 @@ var JsonEditorRenderer = ({
2828
2893
  }
2829
2894
  const readOnly = uischema?.access === "Read";
2830
2895
  const isDisabled = !enabled || readOnly;
2896
+ const scale = scaleFor(
2897
+ (config ?? {}).surface ?? "form"
2898
+ );
2831
2899
  return /* @__PURE__ */ jsxs("div", { style: { marginBottom: "1rem" }, children: [
2832
- label && /* @__PURE__ */ jsxs(
2833
- "label",
2834
- {
2835
- style: {
2836
- display: "block",
2837
- color: "#475569",
2838
- fontSize: "0.875rem",
2839
- fontWeight: 500,
2840
- marginBottom: "0.375rem"
2841
- },
2842
- children: [
2843
- label,
2844
- required && /* @__PURE__ */ jsx("span", { style: { color: "#ef4444", marginLeft: "0.25rem" }, children: "*" })
2845
- ]
2846
- }
2847
- ),
2848
- description && /* @__PURE__ */ jsx(
2849
- "div",
2850
- {
2851
- style: {
2852
- color: "#6b7280",
2853
- fontSize: "0.75rem",
2854
- marginBottom: "0.5rem"
2855
- },
2856
- children: description
2857
- }
2858
- ),
2900
+ label && /* @__PURE__ */ jsxs("label", { className: fieldLabelClass(scale), children: [
2901
+ label,
2902
+ required && /* @__PURE__ */ jsx("span", { className: "ml-1 text-destructive", children: "*" })
2903
+ ] }),
2904
+ description && /* @__PURE__ */ jsx("div", { className: fieldHintClass, children: description }),
2859
2905
  /* @__PURE__ */ jsx(
2860
2906
  "div",
2861
2907
  {
2862
- style: {
2863
- border: errors || displayedParseError ? "1px solid #ef4444" : "1px solid #d1d5db",
2864
- borderRadius: "6px",
2865
- overflow: "hidden",
2866
- opacity: isDisabled ? 0.6 : 1
2867
- },
2908
+ className: `overflow-hidden rounded-md border ${errors || displayedParseError ? "border-destructive" : "border-input"} ${isDisabled ? "opacity-60" : ""}`,
2868
2909
  children: /* @__PURE__ */ jsx(
2869
2910
  AceEditor,
2870
2911
  {
@@ -2897,28 +2938,8 @@ var JsonEditorRenderer = ({
2897
2938
  )
2898
2939
  }
2899
2940
  ),
2900
- displayedParseError && /* @__PURE__ */ jsx(
2901
- "div",
2902
- {
2903
- style: {
2904
- color: "#ef4444",
2905
- fontSize: "0.75rem",
2906
- marginTop: "0.25rem"
2907
- },
2908
- children: displayedParseError
2909
- }
2910
- ),
2911
- errors && /* @__PURE__ */ jsx(
2912
- "div",
2913
- {
2914
- style: {
2915
- color: "#ef4444",
2916
- fontSize: "0.75rem",
2917
- marginTop: "0.25rem"
2918
- },
2919
- children: errors
2920
- }
2921
- )
2941
+ displayedParseError && /* @__PURE__ */ jsx("div", { className: fieldErrorClass, children: displayedParseError }),
2942
+ errors && /* @__PURE__ */ jsx("div", { className: fieldErrorClass, children: errors })
2922
2943
  ] });
2923
2944
  };
2924
2945
  var jsonEditorTester = rankWith(
@@ -3009,14 +3030,31 @@ var ModelIdRenderer = ({
3009
3030
  description,
3010
3031
  errors,
3011
3032
  uischema,
3012
- visible
3033
+ visible,
3034
+ config
3013
3035
  }) => {
3014
- const [isOpen, setIsOpen] = useState(false);
3015
3036
  const [searchValue, setSearchValue] = useState("");
3016
3037
  const [debouncedSearchValue, setDebouncedSearchValue] = useState("");
3017
- const containerRef = useRef(null);
3038
+ const triggerRef = useRef(null);
3018
3039
  const menuRef = useRef(null);
3019
3040
  const searchRef = useRef(null);
3041
+ const surface = (config ?? {}).surface ?? "bar";
3042
+ const scale = scaleFor(surface);
3043
+ const isBar = surface === "bar";
3044
+ const {
3045
+ isOpen,
3046
+ close: closeMenu,
3047
+ toggle,
3048
+ renderPopover
3049
+ } = useAnchoredPopover({
3050
+ trigger: triggerRef,
3051
+ popover: menuRef,
3052
+ // Wide enough for a provider glyph, a model name and its id underneath.
3053
+ width: POPOVER_WIDTH_PX,
3054
+ maxHeight: POPOVER_MAX_HEIGHT_PX,
3055
+ role: "listbox",
3056
+ label: "Models"
3057
+ });
3020
3058
  const debounceTimerRef = useRef(
3021
3059
  void 0
3022
3060
  );
@@ -3055,35 +3093,13 @@ var ModelIdRenderer = ({
3055
3093
  handleChange(path2, model.id);
3056
3094
  setSearchValue("");
3057
3095
  setDebouncedSearchValue("");
3058
- setIsOpen(false);
3096
+ closeMenu();
3059
3097
  },
3060
- [handleChange, path2]
3098
+ [handleChange, path2, closeMenu]
3061
3099
  );
3062
- const close = useCallback(() => {
3063
- setIsOpen(false);
3064
- setSearchValue("");
3065
- }, []);
3066
3100
  useEffect(() => {
3067
- if (!isOpen) return;
3068
- const onPointerDown = (event) => {
3069
- const target = event.target;
3070
- if (containerRef.current && !containerRef.current.contains(target) && !(menuRef.current && menuRef.current.contains(target))) {
3071
- close();
3072
- }
3073
- };
3074
- const onKeyDown = (event) => {
3075
- if (event.key === "Escape") close();
3076
- };
3077
- const onResize = () => close();
3078
- document.addEventListener("mousedown", onPointerDown);
3079
- document.addEventListener("keydown", onKeyDown);
3080
- window.addEventListener("resize", onResize);
3081
- return () => {
3082
- document.removeEventListener("mousedown", onPointerDown);
3083
- document.removeEventListener("keydown", onKeyDown);
3084
- window.removeEventListener("resize", onResize);
3085
- };
3086
- }, [isOpen, close]);
3101
+ if (!isOpen) setSearchValue("");
3102
+ }, [isOpen]);
3087
3103
  useEffect(() => {
3088
3104
  if (isOpen) searchRef.current?.focus();
3089
3105
  }, [isOpen]);
@@ -3092,53 +3108,58 @@ var ModelIdRenderer = ({
3092
3108
  const isDisabled = !enabled || readOnly;
3093
3109
  const hasError = !!errors && errors.length > 0;
3094
3110
  const selectedName = selectedModel ? selectedModel.displayName || selectedModel.name || "" : "";
3095
- const triggerText = selectedName || label || "Select model";
3111
+ const triggerText = selectedName || "Select model";
3096
3112
  const iconSrc = getModelIcon(selectedModel);
3097
3113
  const tooltip = [selectedName || label, description, hasError ? errors : null].filter(Boolean).join(" \u2014 ");
3098
- const anchorRect = isOpen ? containerRef.current?.getBoundingClientRect() : void 0;
3099
- return /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", ref: containerRef, children: [
3100
- /* @__PURE__ */ jsxs(
3101
- "button",
3102
- {
3103
- type: "button",
3104
- onClick: () => setIsOpen((v) => !v),
3105
- disabled: isDisabled,
3106
- "aria-expanded": isOpen,
3107
- "aria-haspopup": "listbox",
3108
- "aria-label": label || "Select model",
3109
- title: tooltip || void 0,
3110
- className: `flex h-8 shrink-0 items-center gap-1.5 rounded-full border px-2.5 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary ${isOpen ? "border-primary/30 bg-primary/10 text-primary" : "border-border/70 bg-transparent text-muted-foreground hover:bg-secondary"} ${hasError ? "border-destructive text-destructive" : ""} ${isDisabled ? "cursor-not-allowed opacity-60" : ""}`,
3111
- children: [
3112
- iconSrc ? /* @__PURE__ */ jsx(
3113
- "img",
3114
- {
3115
- src: iconSrc,
3116
- alt: "",
3117
- className: "h-3.5 w-3.5 shrink-0 object-contain"
3118
- }
3119
- ) : /* @__PURE__ */ jsx(Cpu, { className: "h-3.5 w-3.5 shrink-0" }),
3120
- /* @__PURE__ */ jsx("span", { className: "inline max-w-[9rem] truncate max-sm:hidden", children: triggerText }),
3121
- /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 shrink-0 opacity-60" })
3122
- ]
3123
- }
3124
- ),
3125
- isOpen && anchorRect && createPortal(
3126
- /* @__PURE__ */ jsxs(
3127
- "div",
3128
- {
3129
- ref: menuRef,
3130
- role: "listbox",
3131
- "aria-label": label || "Models",
3132
- className: "fixed z-50 w-72 max-w-[calc(100vw-2rem)] overflow-hidden rounded-xl border border-border bg-popover shadow-lg",
3133
- style: {
3134
- left: Math.max(
3135
- 8,
3136
- Math.min(anchorRect.left, window.innerWidth - 288 - 8)
3137
- ),
3138
- bottom: window.innerHeight - anchorRect.top + 8
3139
- },
3140
- children: [
3141
- /* @__PURE__ */ jsx("div", { className: "border-b border-border p-2", children: /* @__PURE__ */ jsx(
3114
+ const triggerLabel = !label || label === "ModelId" ? "Model" : label;
3115
+ const trigger = /* @__PURE__ */ jsxs(
3116
+ "button",
3117
+ {
3118
+ ref: triggerRef,
3119
+ id: `${path2}-trigger`,
3120
+ type: "button",
3121
+ onClick: toggle,
3122
+ disabled: isDisabled,
3123
+ "aria-expanded": isOpen,
3124
+ "aria-haspopup": "listbox",
3125
+ "aria-label": triggerLabel,
3126
+ title: tooltip || void 0,
3127
+ className: isBar ? pillClass({ active: isOpen, hasError, disabled: isDisabled }) : fieldTriggerClass(scale, hasError),
3128
+ children: [
3129
+ iconSrc ? /* @__PURE__ */ jsx(
3130
+ "img",
3131
+ {
3132
+ src: iconSrc,
3133
+ alt: "",
3134
+ className: "h-3.5 w-3.5 shrink-0 object-contain"
3135
+ }
3136
+ ) : /* @__PURE__ */ jsx(Cpu, { className: "h-3.5 w-3.5 shrink-0" }),
3137
+ /* @__PURE__ */ jsx(
3138
+ "span",
3139
+ {
3140
+ className: isBar ? `inline ${pillValueClass} max-sm:hidden` : "min-w-0 flex-1 truncate",
3141
+ children: triggerText
3142
+ }
3143
+ ),
3144
+ /* @__PURE__ */ jsx(
3145
+ ChevronDown,
3146
+ {
3147
+ className: `shrink-0 opacity-60 ${isBar ? "h-3 w-3" : "h-4 w-4"}`
3148
+ }
3149
+ )
3150
+ ]
3151
+ }
3152
+ );
3153
+ return /* @__PURE__ */ jsxs(
3154
+ "div",
3155
+ {
3156
+ className: isBar ? "ss-jsonforms-field ss-jsonforms-model relative shrink-0" : "ss-jsonforms-field ss-jsonforms-model compact-field",
3157
+ children: [
3158
+ !isBar && /* @__PURE__ */ jsx("label", { htmlFor: `${path2}-trigger`, className: fieldLabelClass(scale), children: triggerLabel }),
3159
+ trigger,
3160
+ renderPopover(
3161
+ /* @__PURE__ */ jsxs(Fragment, { children: [
3162
+ /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b border-border p-2", children: /* @__PURE__ */ jsx(
3142
3163
  "input",
3143
3164
  {
3144
3165
  ref: searchRef,
@@ -3150,7 +3171,7 @@ var ModelIdRenderer = ({
3150
3171
  className: "h-8 w-full rounded-md border border-border bg-background px-2 text-xs text-foreground outline-none placeholder:text-muted-foreground focus:border-primary/40"
3151
3172
  }
3152
3173
  ) }),
3153
- /* @__PURE__ */ jsxs("div", { className: "max-h-72 overflow-y-auto", children: [
3174
+ /* @__PURE__ */ jsxs("div", { className: "min-h-0 overflow-y-auto", children: [
3154
3175
  isLoading && listModels.length === 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-primary" }) }),
3155
3176
  !isLoading && listModels.length === 0 && /* @__PURE__ */ jsx("p", { className: "px-3 py-6 text-center text-xs text-muted-foreground", children: searchValue ? "No models found" : "No models available" }),
3156
3177
  listModels.map((model) => {
@@ -3163,7 +3184,7 @@ var ModelIdRenderer = ({
3163
3184
  role: "option",
3164
3185
  "aria-selected": active,
3165
3186
  onClick: () => handleSelect(model),
3166
- className: "flex w-full items-center gap-2.5 px-3 py-2 text-left transition-colors hover:bg-secondary/60",
3187
+ className: popoverRowClass,
3167
3188
  children: [
3168
3189
  /* @__PURE__ */ jsx(
3169
3190
  Check,
@@ -3189,12 +3210,11 @@ var ModelIdRenderer = ({
3189
3210
  );
3190
3211
  })
3191
3212
  ] })
3192
- ]
3193
- }
3194
- ),
3195
- document.body
3196
- )
3197
- ] });
3213
+ ] })
3214
+ )
3215
+ ]
3216
+ }
3217
+ );
3198
3218
  };
3199
3219
  var modelIdRendererTester = rankWith(
3200
3220
  100,
@@ -3229,6 +3249,7 @@ var NumberRenderer = ({
3229
3249
  uischema,
3230
3250
  visible,
3231
3251
  enabled,
3252
+ config,
3232
3253
  required
3233
3254
  }) => {
3234
3255
  const isInteger = schema?.type === "integer";
@@ -3257,40 +3278,58 @@ var NumberRenderer = ({
3257
3278
  const max = fieldSchema?.maximum;
3258
3279
  const step = isInteger ? 1 : fieldSchema?.multipleOf ?? "any";
3259
3280
  const tooltip = [label, description, hasError ? errors : null].filter(Boolean).join(" \u2014 ");
3260
- return /* @__PURE__ */ jsxs(
3261
- "div",
3281
+ const surface = (config ?? {}).surface ?? "form";
3282
+ const scale = scaleFor(surface);
3283
+ const input = /* @__PURE__ */ jsx(
3284
+ "input",
3262
3285
  {
3263
- className: `ss-jsonforms-field ss-jsonforms-number flex h-8 shrink-0 items-center gap-1.5 rounded-full border px-2.5 text-xs font-medium transition-colors focus-within:border-primary/40 ${hasError ? "border-destructive" : "border-border/70"} ${isDisabled ? "opacity-60" : ""}`,
3264
- title: tooltip || void 0,
3265
- children: [
3266
- label && /* @__PURE__ */ jsxs(
3267
- "label",
3268
- {
3269
- htmlFor: `number-${path2}`,
3270
- className: `whitespace-nowrap ${hasError ? "text-destructive" : "text-muted-foreground"}`,
3271
- children: [
3272
- label,
3273
- required && /* @__PURE__ */ jsx("span", { className: "ml-1 text-destructive", children: "*" })
3274
- ]
3275
- }
3276
- ),
3277
- /* @__PURE__ */ jsx(
3278
- "input",
3279
- {
3280
- id: `number-${path2}`,
3281
- type: "number",
3282
- value: data ?? "",
3283
- onChange: handleInputChange,
3284
- disabled: isDisabled,
3285
- min,
3286
- max,
3287
- step,
3288
- className: "w-14 border-0 bg-transparent p-0 text-xs font-medium tabular-nums text-foreground outline-none disabled:cursor-not-allowed"
3289
- }
3290
- )
3291
- ]
3286
+ id: `number-${path2}`,
3287
+ type: "number",
3288
+ value: data ?? "",
3289
+ onChange: handleInputChange,
3290
+ disabled: isDisabled,
3291
+ min,
3292
+ max,
3293
+ step,
3294
+ className: surface === "bar" ? "w-14 border-0 bg-transparent p-0 text-xs font-medium tabular-nums text-foreground outline-none disabled:cursor-not-allowed" : fieldNumberClass(scale, hasError)
3292
3295
  }
3293
3296
  );
3297
+ if (surface === "bar") {
3298
+ return /* @__PURE__ */ jsxs(
3299
+ "div",
3300
+ {
3301
+ className: `ss-jsonforms-field ss-jsonforms-number ${pillClass({
3302
+ hasError,
3303
+ disabled: isDisabled
3304
+ })}`,
3305
+ title: tooltip || void 0,
3306
+ children: [
3307
+ label && /* @__PURE__ */ jsxs(
3308
+ "label",
3309
+ {
3310
+ htmlFor: `number-${path2}`,
3311
+ className: `${pillLabelClass} whitespace-nowrap ${hasError ? "text-destructive" : "text-muted-foreground"}`,
3312
+ children: [
3313
+ label,
3314
+ required && /* @__PURE__ */ jsx("span", { className: "ml-1 text-destructive", children: "*" })
3315
+ ]
3316
+ }
3317
+ ),
3318
+ input
3319
+ ]
3320
+ }
3321
+ );
3322
+ }
3323
+ return /* @__PURE__ */ jsxs("div", { className: "ss-jsonforms-field ss-jsonforms-number", children: [
3324
+ /* @__PURE__ */ jsxs("div", { className: fieldRowClass, children: [
3325
+ /* @__PURE__ */ jsxs("label", { htmlFor: `number-${path2}`, className: fieldRowLabelClass(scale), children: [
3326
+ label,
3327
+ required && /* @__PURE__ */ jsx("span", { className: "ml-1 text-destructive", children: "*" })
3328
+ ] }),
3329
+ input
3330
+ ] }),
3331
+ hasError && /* @__PURE__ */ jsx("div", { className: fieldErrorClass, children: errors })
3332
+ ] });
3294
3333
  };
3295
3334
  var numberRendererTester = rankWith(
3296
3335
  40,
@@ -3306,6 +3345,14 @@ var numberRendererTester = rankWith(
3306
3345
  }
3307
3346
  );
3308
3347
  var NumberRendererControl = withJsonFormsControlProps(NumberRenderer);
3348
+ var SCALE = {
3349
+ comfortable: { lineHeight: 20, padding: 8 },
3350
+ dense: { lineHeight: 16, padding: 6 }
3351
+ };
3352
+ var rowsToPx = (rows, scale) => rows * scale.lineHeight + scale.padding * 2 + 2;
3353
+ var MAX_VIEWPORT_SHARE = 0.5;
3354
+ var DEFAULT_MIN_ROWS = 3;
3355
+ var DEFAULT_MAX_ROWS = 9;
3309
3356
  var TextareaRenderer = ({
3310
3357
  data,
3311
3358
  handleChange,
@@ -3317,28 +3364,49 @@ var TextareaRenderer = ({
3317
3364
  uischema,
3318
3365
  visible,
3319
3366
  enabled,
3320
- required
3367
+ required,
3368
+ config
3321
3369
  }) => {
3322
3370
  const textareaRef = useRef(null);
3371
+ const textareaOptions = schema["ui:textarea"] ?? {};
3372
+ const formConfig = config ?? {};
3373
+ const scaleName = scaleFor(formConfig.surface ?? "form");
3374
+ const scale = SCALE[scaleName];
3375
+ const minRows = textareaOptions.minRows ?? formConfig.minRows ?? DEFAULT_MIN_ROWS;
3376
+ const maxRows = Math.max(
3377
+ minRows,
3378
+ textareaOptions.maxRows ?? formConfig.maxRows ?? DEFAULT_MAX_ROWS
3379
+ );
3380
+ const minHeight = rowsToPx(minRows, scale);
3381
+ const maxHeight = rowsToPx(maxRows, scale);
3382
+ const [viewportCap, setViewportCap] = useState(
3383
+ () => typeof window === "undefined" ? Number.POSITIVE_INFINITY : Math.round(window.innerHeight * MAX_VIEWPORT_SHARE)
3384
+ );
3385
+ const ceiling = Math.max(minHeight, Math.min(maxHeight, viewportCap));
3323
3386
  const autoResize = useCallback(() => {
3324
- if (textareaRef.current) {
3325
- textareaRef.current.style.height = "auto";
3326
- textareaRef.current.style.height = `${Math.min(
3327
- Math.max(textareaRef.current.scrollHeight, 80),
3328
- // Minimum height of 80px
3329
- 240
3330
- // Maximum height of 240px
3331
- )}px`;
3332
- }
3333
- }, []);
3387
+ const el = textareaRef.current;
3388
+ if (!el) return;
3389
+ el.style.height = "auto";
3390
+ el.style.height = `${Math.min(
3391
+ Math.max(el.scrollHeight, minHeight),
3392
+ ceiling
3393
+ )}px`;
3394
+ }, [minHeight, ceiling]);
3334
3395
  useEffect(() => {
3335
3396
  autoResize();
3336
3397
  }, [data, autoResize]);
3398
+ useEffect(() => {
3399
+ const onResize = () => {
3400
+ setViewportCap(Math.round(window.innerHeight * MAX_VIEWPORT_SHARE));
3401
+ autoResize();
3402
+ };
3403
+ window.addEventListener("resize", onResize);
3404
+ return () => window.removeEventListener("resize", onResize);
3405
+ }, [autoResize]);
3337
3406
  const handleInputChange = useCallback(
3338
3407
  (event) => {
3339
- const newValue = event.target.value;
3340
- handleChange(path2, newValue);
3341
- setTimeout(autoResize, 0);
3408
+ handleChange(path2, event.target.value);
3409
+ autoResize();
3342
3410
  },
3343
3411
  [handleChange, path2, autoResize]
3344
3412
  );
@@ -3348,89 +3416,34 @@ var TextareaRenderer = ({
3348
3416
  const readOnly = uischema?.access === "Read";
3349
3417
  const isDisabled = !enabled || readOnly;
3350
3418
  const hasError = errors && errors.length > 0;
3351
- const textareaOptions = schema["ui:textarea"] ?? {};
3352
3419
  const placeholder = textareaOptions.placeholder || schema?.description || `Enter ${label?.toLowerCase() || "text"}...`;
3353
- const minRows = textareaOptions.minRows || 3;
3354
3420
  return /* @__PURE__ */ jsxs("div", { className: "ss-jsonforms-field ss-jsonforms-textarea", children: [
3355
- label && /* @__PURE__ */ jsxs(
3356
- "label",
3357
- {
3358
- style: {
3359
- display: "block",
3360
- color: hasError ? "#ef4444" : "#475569",
3361
- fontSize: "0.875rem",
3362
- fontWeight: 500,
3363
- marginBottom: "0.375rem"
3364
- },
3365
- children: [
3366
- label,
3367
- required && /* @__PURE__ */ jsx("span", { style: { color: "#ef4444", marginLeft: "0.25rem" }, children: "*" })
3368
- ]
3369
- }
3370
- ),
3371
- description && /* @__PURE__ */ jsx(
3372
- "div",
3373
- {
3374
- style: {
3375
- color: "#6b7280",
3376
- fontSize: "0.75rem",
3377
- marginBottom: "0.5rem"
3378
- },
3379
- children: description
3380
- }
3381
- ),
3421
+ label && /* @__PURE__ */ jsxs("label", { htmlFor: path2, className: fieldLabelClass(scaleName), children: [
3422
+ label,
3423
+ required && /* @__PURE__ */ jsx("span", { className: "ml-1 text-destructive", children: "*" })
3424
+ ] }),
3425
+ description && /* @__PURE__ */ jsx("div", { className: fieldHintClass, children: description }),
3382
3426
  /* @__PURE__ */ jsx(
3383
3427
  "textarea",
3384
3428
  {
3429
+ id: path2,
3385
3430
  ref: textareaRef,
3386
3431
  value: data || "",
3387
3432
  onChange: handleInputChange,
3388
3433
  placeholder,
3389
3434
  disabled: isDisabled,
3390
3435
  rows: minRows,
3436
+ className: `resize-y ${fieldControlClass(scaleName, !!hasError)}`,
3391
3437
  style: {
3392
- width: "100%",
3393
- minHeight: "80px",
3394
- maxHeight: "240px",
3395
- resize: "vertical",
3396
- padding: "0.75rem",
3397
- border: hasError ? "2px solid #ef4444" : "1px solid #d1d5db",
3398
- borderRadius: "6px",
3399
- fontSize: "16px",
3400
- lineHeight: "1.5",
3401
- fontFamily: "inherit",
3402
- backgroundColor: isDisabled ? "#f9fafb" : "#ffffff",
3403
- color: isDisabled ? "#9ca3af" : "#111827",
3404
- outline: "none",
3405
- transition: "border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out",
3406
- boxShadow: hasError ? "0 0 0 1px #ef4444" : "none",
3438
+ minHeight: `${minHeight}px`,
3439
+ maxHeight: `${ceiling}px`,
3440
+ // iOS zooms a focused control under 16px; the design's fields are
3441
+ // 14px, so opt out of the adjustment rather than the design.
3407
3442
  WebkitTextSizeAdjust: "100%"
3408
- },
3409
- onFocus: (e) => {
3410
- if (!hasError) {
3411
- e.target.style.borderColor = "#6366f1";
3412
- e.target.style.boxShadow = "0 0 0 1px #6366f1";
3413
- }
3414
- },
3415
- onBlur: (e) => {
3416
- if (!hasError) {
3417
- e.target.style.borderColor = "#d1d5db";
3418
- e.target.style.boxShadow = "none";
3419
- }
3420
3443
  }
3421
3444
  }
3422
3445
  ),
3423
- hasError && /* @__PURE__ */ jsx(
3424
- "div",
3425
- {
3426
- style: {
3427
- color: "#ef4444",
3428
- fontSize: "0.75rem",
3429
- marginTop: "0.25rem"
3430
- },
3431
- children: errors
3432
- }
3433
- )
3446
+ hasError && /* @__PURE__ */ jsx("div", { className: fieldErrorClass, children: errors })
3434
3447
  ] });
3435
3448
  };
3436
3449
  var textareaRendererTester = rankWith(
@@ -3482,10 +3495,25 @@ var renderers = [
3482
3495
  ];
3483
3496
 
3484
3497
  // src/chat-variables/VariablesForm.vm.ts
3498
+ var MAX_INLINE_VARIABLES = 3;
3499
+ var isModelSelector = (schema) => schema.title === "ModelId" || schema["x-model-selector"] === true;
3500
+ function rowLayout(controls) {
3501
+ const innerRow = {
3502
+ type: "HorizontalLayout",
3503
+ elements: controls,
3504
+ options: { gap: "12px", alignItems: "flex-start" }
3505
+ };
3506
+ const ui = {
3507
+ type: "VerticalLayout",
3508
+ elements: [innerRow]
3509
+ };
3510
+ return ui;
3511
+ }
3485
3512
  function buildSimpleSchemaAndUi(vars, threadVars, useDefaults) {
3486
3513
  const names = Object.keys(vars || {});
3487
3514
  const properties2 = {};
3488
3515
  const controls = [];
3516
+ const inlineOnly = [];
3489
3517
  const initialData = {};
3490
3518
  const $defs = {};
3491
3519
  for (const name of names) {
@@ -3505,19 +3533,24 @@ function buildSimpleSchemaAndUi(vars, threadVars, useDefaults) {
3505
3533
  control.enabled = false;
3506
3534
  }
3507
3535
  controls.push(control);
3536
+ if (isModelSelector(properties2[name])) inlineOnly.push(control);
3508
3537
  }
3509
3538
  const schema = { type: "object", properties: properties2 };
3510
3539
  if (Object.keys($defs).length > 0) schema.$defs = $defs;
3511
- const innerRow = {
3512
- type: "HorizontalLayout",
3513
- elements: controls,
3514
- options: { gap: "12px", alignItems: "flex-start" }
3515
- };
3516
- const ui = {
3517
- type: "VerticalLayout",
3518
- elements: [innerRow]
3540
+ if (controls.length <= MAX_INLINE_VARIABLES) {
3541
+ return {
3542
+ schema,
3543
+ inlineControls: controls,
3544
+ overflowControls: [],
3545
+ initialData
3546
+ };
3547
+ }
3548
+ return {
3549
+ schema,
3550
+ inlineControls: inlineOnly,
3551
+ overflowControls: controls.filter((c) => !inlineOnly.includes(c)),
3552
+ initialData
3519
3553
  };
3520
- return { schema, uiSchema: ui, initialData };
3521
3554
  }
3522
3555
  function useChatVariablesFormVm({
3523
3556
  workspace,
@@ -3532,26 +3565,26 @@ function useChatVariablesFormVm({
3532
3565
  const { mutate: updateVariableMutation } = useUpdateFlowRunVariable();
3533
3566
  const querySettled = !isLoading && (threadVars !== void 0 || isError);
3534
3567
  const shouldUseDefaults = isError || threadVars && Object.keys(threadVars).length === 0;
3535
- const built = React8.useMemo(() => {
3568
+ const built = React9.useMemo(() => {
3536
3569
  return buildSimpleSchemaAndUi(
3537
3570
  workspace.variables,
3538
3571
  threadVars,
3539
3572
  shouldUseDefaults ?? false
3540
3573
  );
3541
3574
  }, [workspace.variables, threadVars, shouldUseDefaults]);
3542
- const [data, setData] = React8.useState(null);
3543
- React8.useEffect(() => {
3575
+ const [data, setData] = React9.useState(null);
3576
+ React9.useEffect(() => {
3544
3577
  if (querySettled) {
3545
3578
  setData(built.initialData);
3546
3579
  setVariables(built.initialData);
3547
3580
  }
3548
3581
  }, [querySettled, built.initialData, setVariables]);
3549
- const ajv = React8.useMemo(() => createAjv({ useDefaults: false }), []);
3550
- const prevRef = React8.useRef(null);
3551
- React8.useEffect(() => {
3582
+ const ajv = React9.useMemo(() => createAjv({ useDefaults: false }), []);
3583
+ const prevRef = React9.useRef(null);
3584
+ React9.useEffect(() => {
3552
3585
  prevRef.current = data;
3553
3586
  }, [data]);
3554
- const onChange = React8.useCallback(
3587
+ const onChange = React9.useCallback(
3555
3588
  ({ data: next2 }) => {
3556
3589
  if (prevRef.current && !isDraftThreadId(threadId)) {
3557
3590
  const keys2 = Object.keys(workspace.variables || {});
@@ -3572,29 +3605,96 @@ function useChatVariablesFormVm({
3572
3605
  },
3573
3606
  [workspace.variables, setVariables, updateVariableMutation, threadId]
3574
3607
  );
3575
- const config = React8.useMemo(
3608
+ const barConfig = React9.useMemo(
3576
3609
  () => ({
3577
3610
  restrict: true,
3578
3611
  trim: false,
3579
3612
  showUnfocusedDescription: true,
3580
- hideRequiredAsterisk: true
3613
+ hideRequiredAsterisk: true,
3614
+ surface: "bar",
3615
+ minRows: 1,
3616
+ maxRows: 6
3581
3617
  }),
3582
3618
  []
3583
3619
  );
3620
+ const panelConfig = React9.useMemo(
3621
+ () => ({ ...barConfig, surface: "panel" }),
3622
+ [barConfig]
3623
+ );
3624
+ const inlineUiSchema = React9.useMemo(
3625
+ () => built.inlineControls.length ? rowLayout(built.inlineControls) : null,
3626
+ [built.inlineControls]
3627
+ );
3628
+ const overflowUiSchema = React9.useMemo(
3629
+ () => built.overflowControls.length ? rowLayout(built.overflowControls) : null,
3630
+ [built.overflowControls]
3631
+ );
3584
3632
  return {
3585
3633
  schema: built.schema,
3586
- uiSchema: built.uiSchema,
3634
+ inlineUiSchema,
3635
+ overflowUiSchema,
3636
+ overflowCount: built.overflowControls.length,
3587
3637
  data,
3588
3638
  renderers,
3589
3639
  cells,
3590
3640
  ajv,
3591
3641
  onChange,
3592
- config,
3642
+ barConfig,
3643
+ panelConfig,
3593
3644
  isLoading,
3594
3645
  isReady: querySettled,
3595
3646
  isHydrated: data !== null
3596
3647
  };
3597
3648
  }
3649
+ function VariablesOverflowPanel({ count, children: children2 }) {
3650
+ const triggerRef = useRef(null);
3651
+ const panelRef = useRef(null);
3652
+ const { isOpen, close, toggle, renderPopover } = useAnchoredPopover({
3653
+ trigger: triggerRef,
3654
+ popover: panelRef,
3655
+ width: POPOVER_WIDTH_PX,
3656
+ maxHeight: POPOVER_MAX_HEIGHT_PX,
3657
+ role: "dialog",
3658
+ label: "Workspace variables"
3659
+ });
3660
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3661
+ /* @__PURE__ */ jsxs(
3662
+ "button",
3663
+ {
3664
+ ref: triggerRef,
3665
+ type: "button",
3666
+ onClick: toggle,
3667
+ "aria-expanded": isOpen,
3668
+ "aria-haspopup": "dialog",
3669
+ "aria-label": `Variables (${count})`,
3670
+ className: `flex h-8 shrink-0 items-center gap-1.5 rounded-full border px-2.5 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary ${isOpen ? "border-primary/30 bg-primary/10 text-primary" : "border-border/70 bg-transparent text-muted-foreground hover:bg-secondary"}`,
3671
+ children: [
3672
+ /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-3.5 w-3.5 shrink-0" }),
3673
+ /* @__PURE__ */ jsx("span", { className: "inline max-sm:hidden", children: "Variables" }),
3674
+ /* @__PURE__ */ jsx("span", { className: "rounded-full bg-secondary px-1.5 text-[10px] leading-4 tabular-nums", children: count })
3675
+ ]
3676
+ }
3677
+ ),
3678
+ renderPopover(
3679
+ /* @__PURE__ */ jsxs(Fragment, { children: [
3680
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-between border-b border-border px-3 py-2", children: [
3681
+ /* @__PURE__ */ jsx("span", { className: popoverHeadingClass, children: "Variables" }),
3682
+ /* @__PURE__ */ jsx(
3683
+ "button",
3684
+ {
3685
+ type: "button",
3686
+ onClick: close,
3687
+ "aria-label": "Close variables",
3688
+ className: "inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
3689
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
3690
+ }
3691
+ )
3692
+ ] }),
3693
+ /* @__PURE__ */ jsx("div", { className: "ss-variables-panel min-h-0 overflow-y-auto p-3", children: children2 })
3694
+ ] })
3695
+ )
3696
+ ] });
3697
+ }
3598
3698
  var ChatVariablesForm = forwardRef(({ workspace, threadId, setVariables }, ref) => {
3599
3699
  const vm = useChatVariablesFormVm({ workspace, threadId, setVariables });
3600
3700
  useImperativeHandle(ref, () => ({
@@ -3610,19 +3710,32 @@ var ChatVariablesForm = forwardRef(({ workspace, threadId, setVariables }, ref)
3610
3710
  if (!vm.isHydrated) {
3611
3711
  return /* @__PURE__ */ jsx("div", { className: "flex justify-center items-center w-full h-8", children: /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-primary" }) });
3612
3712
  }
3613
- return /* @__PURE__ */ jsx("div", { className: "w-full jsonforms-compact", children: /* @__PURE__ */ jsx(
3614
- JsonForms,
3615
- {
3616
- schema: vm.schema,
3617
- uischema: vm.uiSchema,
3618
- data: vm.data,
3619
- renderers: vm.renderers,
3620
- cells: vm.cells,
3621
- ajv: vm.ajv,
3622
- onChange: vm.onChange,
3623
- config: vm.config
3624
- }
3625
- ) });
3713
+ const formProps = {
3714
+ schema: vm.schema,
3715
+ data: vm.data,
3716
+ renderers: vm.renderers,
3717
+ cells: vm.cells,
3718
+ ajv: vm.ajv,
3719
+ onChange: vm.onChange
3720
+ };
3721
+ return /* @__PURE__ */ jsxs("div", { className: "flex w-full min-w-0 items-center gap-1", children: [
3722
+ vm.inlineUiSchema && /* @__PURE__ */ jsx("div", { className: "min-w-0 jsonforms-compact", children: /* @__PURE__ */ jsx(
3723
+ JsonForms,
3724
+ {
3725
+ ...formProps,
3726
+ uischema: vm.inlineUiSchema,
3727
+ config: vm.barConfig
3728
+ }
3729
+ ) }),
3730
+ vm.overflowUiSchema && /* @__PURE__ */ jsx(VariablesOverflowPanel, { count: vm.overflowCount, children: /* @__PURE__ */ jsx("div", { className: "jsonforms-compact", children: /* @__PURE__ */ jsx(
3731
+ JsonForms,
3732
+ {
3733
+ ...formProps,
3734
+ uischema: vm.overflowUiSchema,
3735
+ config: vm.panelConfig
3736
+ }
3737
+ ) }) })
3738
+ ] });
3626
3739
  });
3627
3740
 
3628
3741
  // src/domains/workspaces/queryKeys.ts
@@ -4094,7 +4207,7 @@ function MessageComposer(_props = {}) {
4094
4207
  disabled,
4095
4208
  placeholder: "What would you like to do?",
4096
4209
  className: "md-editor--bare px-5 pb-3 pt-4 text-sm",
4097
- minHeight: 48
4210
+ minHeight: 24
4098
4211
  },
4099
4212
  `composer-md-${editorKey}`
4100
4213
  ) }),
@@ -19173,7 +19286,7 @@ var buttonVariants = cva(
19173
19286
  }
19174
19287
  }
19175
19288
  );
19176
- var Button = React8.forwardRef(
19289
+ var Button = React9.forwardRef(
19177
19290
  ({ className, variant, size, asChild = false, ...props }, ref) => {
19178
19291
  const Comp = asChild ? Slot : "button";
19179
19292
  return /* @__PURE__ */ jsx(
@@ -19724,6 +19837,7 @@ function ChatMessageSources({
19724
19837
  ] })
19725
19838
  ] });
19726
19839
  }
19840
+ var USER_FORM_CONFIG = { surface: "form", minRows: 6, maxRows: 20 };
19727
19841
  var REVEAL_ON_HOVER = "opacity-100 focus-visible:opacity-100 [@media(hover:hover)]:opacity-0 [@media(hover:hover)]:group-hover:opacity-100";
19728
19842
  var MessageBubble = (props) => {
19729
19843
  const {
@@ -19777,7 +19891,7 @@ var MessageBubble = (props) => {
19777
19891
  );
19778
19892
  })
19779
19893
  ] }),
19780
- showForm && /* @__PURE__ */ jsxs("div", { className: "mt-4 border-t border-border pt-4", children: [
19894
+ showForm && /* @__PURE__ */ jsxs("div", { className: "ss-chat-message__user-form mt-4 border-t border-border pt-4", children: [
19781
19895
  /* @__PURE__ */ jsx(
19782
19896
  JsonForms,
19783
19897
  {
@@ -19786,6 +19900,7 @@ var MessageBubble = (props) => {
19786
19900
  renderers,
19787
19901
  cells,
19788
19902
  readonly: userInput !== void 0,
19903
+ config: USER_FORM_CONFIG,
19789
19904
  onChange: ({ data, errors }) => {
19790
19905
  setResponseFormData(data);
19791
19906
  setResponseFormValid(!errors?.length);