@smartspace/chat-ui 1.14.4-dev.4755c72 → 1.14.4-dev.5c029a3

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, 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) => {
@@ -365,6 +365,19 @@ var MessageValueType = /* @__PURE__ */ ((MessageValueType2) => {
365
365
  MessageValueType2["INPUT"] = "Input";
366
366
  return MessageValueType2;
367
367
  })(MessageValueType || {});
368
+ var MessageResponseSourceType = /* @__PURE__ */ ((MessageResponseSourceType2) => {
369
+ MessageResponseSourceType2["BlobInternal"] = "BlobInternal";
370
+ MessageResponseSourceType2["WebExternal"] = "WebExternal";
371
+ MessageResponseSourceType2["File"] = "File";
372
+ MessageResponseSourceType2["URL"] = "URL";
373
+ return MessageResponseSourceType2;
374
+ })(MessageResponseSourceType || {});
375
+ var MessageAttribution = /* @__PURE__ */ ((MessageAttribution2) => {
376
+ MessageAttribution2["Supported"] = "supported";
377
+ MessageAttribution2["Partial"] = "partial";
378
+ MessageAttribution2["Unsupported"] = "unsupported";
379
+ return MessageAttribution2;
380
+ })(MessageAttribution || {});
368
381
 
369
382
  // src/domains/threads/queryKeys.ts
370
383
  var THREAD_LIST_PAGE_SIZE = 30;
@@ -2430,6 +2443,31 @@ function useFlowRunVariables(flowRunId) {
2430
2443
  enabled: !!flowRunId
2431
2444
  });
2432
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";
2433
2471
  function iconFor(label) {
2434
2472
  const name = (label ?? "").toLowerCase();
2435
2473
  if (/search|web|browse|internet/.test(name)) return Globe;
@@ -2445,7 +2483,8 @@ var BooleanRenderer = ({
2445
2483
  errors,
2446
2484
  uischema,
2447
2485
  visible,
2448
- enabled
2486
+ enabled,
2487
+ config
2449
2488
  }) => {
2450
2489
  const onToggle = useCallback(() => {
2451
2490
  handleChange(path2, !data);
@@ -2456,13 +2495,17 @@ var BooleanRenderer = ({
2456
2495
  const hasError = !!errors && errors.length > 0;
2457
2496
  const isChecked = Boolean(data);
2458
2497
  const Icon = iconFor(label);
2498
+ const surface = (config ?? {}).surface ?? "form";
2499
+ const scale = scaleFor(surface);
2459
2500
  const tooltip = [label, description, hasError ? errors : null].filter(Boolean).join(" \u2014 ");
2460
- return /* @__PURE__ */ jsxs(
2501
+ const toggle = /* @__PURE__ */ jsx(
2461
2502
  "button",
2462
2503
  {
2463
2504
  id: `toggle-${path2}`,
2464
2505
  type: "button",
2465
- "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,
2466
2509
  "aria-label": label,
2467
2510
  title: tooltip || void 0,
2468
2511
  onClick: onToggle,
@@ -2472,16 +2515,28 @@ var BooleanRenderer = ({
2472
2515
  e.currentTarget.blur();
2473
2516
  }
2474
2517
  },
2475
- 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" : ""}`,
2476
- 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: [
2477
2524
  /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
2478
2525
  label && // Desktop-first: the app and the package each ship a full Tailwind
2479
- // build, so a base `hidden` can out-order `sm:inline`. `max-sm:hidden`
2480
- // is the form that survives both cascades.
2481
- /* @__PURE__ */ jsx("span", { className: "inline max-w-[9rem] truncate max-sm:hidden", children: label })
2482
- ]
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) })
2483
2530
  }
2484
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
+ ] });
2485
2540
  };
2486
2541
  var booleanRendererTester = rankWith(40, (uischema, schema) => {
2487
2542
  if (uischema.type !== "Control") return false;
@@ -2491,6 +2546,104 @@ var booleanRendererTester = rankWith(40, (uischema, schema) => {
2491
2546
  return fieldSchema?.type === "boolean";
2492
2547
  });
2493
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
+ }
2494
2647
  function hasConst(x) {
2495
2648
  return !!x && typeof x === "object" && "const" in x;
2496
2649
  }
@@ -2529,185 +2682,109 @@ var DropdownRenderer = ({
2529
2682
  label,
2530
2683
  description,
2531
2684
  errors,
2532
- uischema
2685
+ config,
2686
+ uischema,
2687
+ visible
2533
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
+ });
2534
2702
  const options = toOptions(schema);
2535
- const handleSelectionChange = useCallback(
2536
- (event) => {
2537
- handleChange(path2, event.target.value);
2703
+ const select = useCallback(
2704
+ (value) => {
2705
+ handleChange(path2, value);
2706
+ close();
2538
2707
  },
2539
- [handleChange, path2]
2708
+ [handleChange, path2, close]
2540
2709
  );
2541
- const displayValue = data ?? "";
2710
+ if (!visible) return null;
2542
2711
  const readOnly = uischema?.access === "Read";
2543
2712
  const isDisabled = !enabled || readOnly;
2544
- return /* @__PURE__ */ jsxs(
2545
- 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",
2546
2721
  {
2547
- variant: "outlined",
2548
- size: "small",
2549
- fullWidth: true,
2550
- error: !!errors,
2722
+ id: triggerId,
2723
+ ref: triggerRef,
2724
+ type: "button",
2551
2725
  disabled: isDisabled,
2552
- className: "compact-field",
2553
- sx: {
2554
- minWidth: "220px"
2555
- },
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)}`,
2556
2736
  children: [
2737
+ isBar && label && /* @__PURE__ */ jsx("span", { className: `inline ${pillLabelClass} max-sm:hidden`, children: label }),
2557
2738
  /* @__PURE__ */ jsx(
2558
- InputLabel,
2739
+ "span",
2559
2740
  {
2560
- id: `${path2}-label`,
2561
- sx: {
2562
- color: "#6b7280",
2563
- fontSize: "0.875rem",
2564
- fontWeight: 500,
2565
- "&.Mui-focused": {
2566
- color: "#6366f1"
2567
- },
2568
- "&.Mui-error": {
2569
- color: "#ef4444"
2570
- }
2571
- },
2572
- children: label
2741
+ className: isBar ? pillValueClass : `min-w-0 flex-1 truncate ${valueText ? "" : "text-muted-foreground"}`,
2742
+ children: valueText || (isBar ? "\u2014" : placeholder)
2573
2743
  }
2574
2744
  ),
2575
2745
  /* @__PURE__ */ jsx(
2576
- Select,
2746
+ ChevronDown,
2577
2747
  {
2578
- labelId: `${path2}-label`,
2579
- value: displayValue,
2580
- onChange: handleSelectionChange,
2581
- label,
2582
- sx: {
2583
- "& .MuiOutlinedInput-root": {
2584
- backgroundColor: "#fafafa",
2585
- borderRadius: "8px",
2586
- transition: "all 0.2s ease-in-out",
2587
- height: "40px",
2588
- // Same height as model dropdown
2589
- "& fieldset": {
2590
- borderColor: "#e5e7eb",
2591
- borderWidth: "1px"
2592
- },
2593
- "&:hover": {
2594
- backgroundColor: "#ffffff",
2595
- "& fieldset": {
2596
- borderColor: "#9ca3af"
2597
- }
2598
- },
2599
- "&.Mui-focused": {
2600
- backgroundColor: "#ffffff",
2601
- "& fieldset": {
2602
- borderColor: "#6366f1",
2603
- borderWidth: "2px"
2604
- }
2605
- },
2606
- "&.Mui-error": {
2607
- "& fieldset": {
2608
- borderColor: "#ef4444"
2609
- }
2610
- }
2611
- },
2612
- "& .MuiSelect-select": {
2613
- backgroundColor: "#fafafa",
2614
- borderRadius: "8px",
2615
- transition: "all 0.2s ease-in-out",
2616
- height: "40px",
2617
- display: "flex",
2618
- alignItems: "center",
2619
- paddingRight: "32px !important",
2620
- "&:hover": {
2621
- backgroundColor: "#ffffff"
2622
- },
2623
- "&.Mui-focused": {
2624
- backgroundColor: "#ffffff"
2625
- }
2626
- },
2627
- "& .MuiOutlinedInput-notchedOutline": {
2628
- borderColor: "#e5e7eb",
2629
- borderWidth: "1px",
2630
- borderRadius: "8px"
2631
- },
2632
- "&:hover .MuiOutlinedInput-notchedOutline": {
2633
- borderColor: "#9ca3af"
2634
- },
2635
- "&.Mui-focused .MuiOutlinedInput-notchedOutline": {
2636
- borderColor: "#6366f1",
2637
- borderWidth: "2px"
2638
- },
2639
- "&.Mui-error .MuiOutlinedInput-notchedOutline": {
2640
- borderColor: "#ef4444"
2641
- },
2642
- "& .MuiSelect-icon": {
2643
- color: "#9ca3af",
2644
- "&:hover": {
2645
- color: "#6b7280"
2646
- }
2647
- }
2648
- },
2649
- MenuProps: {
2650
- PaperProps: {
2651
- sx: {
2652
- borderRadius: "8px",
2653
- boxShadow: "0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
2654
- border: "1px solid #e5e7eb",
2655
- marginTop: "4px",
2656
- maxHeight: "280px"
2657
- }
2658
- },
2659
- MenuListProps: {
2660
- sx: {
2661
- padding: 0
2662
- }
2663
- }
2664
- },
2665
- children: options.map((option, index) => {
2666
- const raw2 = option.const;
2667
- const key = typeof raw2 === "string" || typeof raw2 === "number" ? String(raw2) : String(index);
2668
- const value = typeof raw2 === "string" || typeof raw2 === "number" ? raw2 : String(raw2 ?? index);
2669
- return /* @__PURE__ */ jsx(
2670
- MenuItem,
2671
- {
2672
- value,
2673
- sx: {
2674
- padding: "12px 16px",
2675
- fontSize: "0.875rem",
2676
- borderBottom: "1px solid #f3f4f6",
2677
- "&:last-child": {
2678
- borderBottom: "none"
2679
- },
2680
- "&:hover": {
2681
- backgroundColor: "#f8fafc"
2682
- },
2683
- "&.Mui-selected": {
2684
- backgroundColor: "#eff6ff",
2685
- "&:hover": {
2686
- backgroundColor: "#dbeafe"
2687
- }
2688
- }
2689
- },
2690
- children: option.title ?? String(raw2)
2691
- },
2692
- key
2693
- );
2694
- })
2695
- }
2696
- ),
2697
- (description || errors) && /* @__PURE__ */ jsx(
2698
- "div",
2699
- {
2700
- style: {
2701
- fontSize: "0.75rem",
2702
- marginTop: "4px",
2703
- color: errors ? "#ef4444" : "#6b7280"
2704
- },
2705
- children: errors || description
2748
+ className: `shrink-0 opacity-50 ${isBar ? "h-3 w-3" : "h-4 w-4"}`
2706
2749
  }
2707
2750
  )
2708
2751
  ]
2709
2752
  }
2710
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
+ ] });
2711
2788
  };
2712
2789
  var dropdownRendererTester = rankWith(
2713
2790
  90,
@@ -2725,8 +2802,8 @@ var dropdownRendererTester = rankWith(
2725
2802
  return false;
2726
2803
  }
2727
2804
  const hasDropdownOptions = !!(fieldSchema.oneOf || fieldSchema.anyOf || fieldSchema.enum && Array.isArray(fieldSchema.enum));
2728
- const isModelSelector = typeof fieldSchema === "object" && fieldSchema && fieldSchema["x-model-selector"] === true || fieldSchema.title === "ModelId" || fieldSchema.format === "uuid";
2729
- 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;
2730
2807
  }
2731
2808
  );
2732
2809
  var DropdownRendererControl = withJsonFormsControlProps(DropdownRenderer);
@@ -2761,7 +2838,8 @@ var JsonEditorRenderer = ({
2761
2838
  uischema,
2762
2839
  visible,
2763
2840
  enabled,
2764
- required
2841
+ required,
2842
+ config
2765
2843
  }) => {
2766
2844
  const [jsonValue, setJsonValue] = useState("");
2767
2845
  const [displayedParseError, setDisplayedParseError] = useState(
@@ -2815,43 +2893,19 @@ var JsonEditorRenderer = ({
2815
2893
  }
2816
2894
  const readOnly = uischema?.access === "Read";
2817
2895
  const isDisabled = !enabled || readOnly;
2896
+ const scale = scaleFor(
2897
+ (config ?? {}).surface ?? "form"
2898
+ );
2818
2899
  return /* @__PURE__ */ jsxs("div", { style: { marginBottom: "1rem" }, children: [
2819
- label && /* @__PURE__ */ jsxs(
2820
- "label",
2821
- {
2822
- style: {
2823
- display: "block",
2824
- color: "#475569",
2825
- fontSize: "0.875rem",
2826
- fontWeight: 500,
2827
- marginBottom: "0.375rem"
2828
- },
2829
- children: [
2830
- label,
2831
- required && /* @__PURE__ */ jsx("span", { style: { color: "#ef4444", marginLeft: "0.25rem" }, children: "*" })
2832
- ]
2833
- }
2834
- ),
2835
- description && /* @__PURE__ */ jsx(
2836
- "div",
2837
- {
2838
- style: {
2839
- color: "#6b7280",
2840
- fontSize: "0.75rem",
2841
- marginBottom: "0.5rem"
2842
- },
2843
- children: description
2844
- }
2845
- ),
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 }),
2846
2905
  /* @__PURE__ */ jsx(
2847
2906
  "div",
2848
2907
  {
2849
- style: {
2850
- border: errors || displayedParseError ? "1px solid #ef4444" : "1px solid #d1d5db",
2851
- borderRadius: "6px",
2852
- overflow: "hidden",
2853
- opacity: isDisabled ? 0.6 : 1
2854
- },
2908
+ className: `overflow-hidden rounded-md border ${errors || displayedParseError ? "border-destructive" : "border-input"} ${isDisabled ? "opacity-60" : ""}`,
2855
2909
  children: /* @__PURE__ */ jsx(
2856
2910
  AceEditor,
2857
2911
  {
@@ -2884,28 +2938,8 @@ var JsonEditorRenderer = ({
2884
2938
  )
2885
2939
  }
2886
2940
  ),
2887
- displayedParseError && /* @__PURE__ */ jsx(
2888
- "div",
2889
- {
2890
- style: {
2891
- color: "#ef4444",
2892
- fontSize: "0.75rem",
2893
- marginTop: "0.25rem"
2894
- },
2895
- children: displayedParseError
2896
- }
2897
- ),
2898
- errors && /* @__PURE__ */ jsx(
2899
- "div",
2900
- {
2901
- style: {
2902
- color: "#ef4444",
2903
- fontSize: "0.75rem",
2904
- marginTop: "0.25rem"
2905
- },
2906
- children: errors
2907
- }
2908
- )
2941
+ displayedParseError && /* @__PURE__ */ jsx("div", { className: fieldErrorClass, children: displayedParseError }),
2942
+ errors && /* @__PURE__ */ jsx("div", { className: fieldErrorClass, children: errors })
2909
2943
  ] });
2910
2944
  };
2911
2945
  var jsonEditorTester = rankWith(
@@ -2996,14 +3030,31 @@ var ModelIdRenderer = ({
2996
3030
  description,
2997
3031
  errors,
2998
3032
  uischema,
2999
- visible
3033
+ visible,
3034
+ config
3000
3035
  }) => {
3001
- const [isOpen, setIsOpen] = useState(false);
3002
3036
  const [searchValue, setSearchValue] = useState("");
3003
3037
  const [debouncedSearchValue, setDebouncedSearchValue] = useState("");
3004
- const containerRef = useRef(null);
3038
+ const triggerRef = useRef(null);
3005
3039
  const menuRef = useRef(null);
3006
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
+ });
3007
3058
  const debounceTimerRef = useRef(
3008
3059
  void 0
3009
3060
  );
@@ -3042,35 +3093,13 @@ var ModelIdRenderer = ({
3042
3093
  handleChange(path2, model.id);
3043
3094
  setSearchValue("");
3044
3095
  setDebouncedSearchValue("");
3045
- setIsOpen(false);
3096
+ closeMenu();
3046
3097
  },
3047
- [handleChange, path2]
3098
+ [handleChange, path2, closeMenu]
3048
3099
  );
3049
- const close = useCallback(() => {
3050
- setIsOpen(false);
3051
- setSearchValue("");
3052
- }, []);
3053
3100
  useEffect(() => {
3054
- if (!isOpen) return;
3055
- const onPointerDown = (event) => {
3056
- const target = event.target;
3057
- if (containerRef.current && !containerRef.current.contains(target) && !(menuRef.current && menuRef.current.contains(target))) {
3058
- close();
3059
- }
3060
- };
3061
- const onKeyDown = (event) => {
3062
- if (event.key === "Escape") close();
3063
- };
3064
- const onResize = () => close();
3065
- document.addEventListener("mousedown", onPointerDown);
3066
- document.addEventListener("keydown", onKeyDown);
3067
- window.addEventListener("resize", onResize);
3068
- return () => {
3069
- document.removeEventListener("mousedown", onPointerDown);
3070
- document.removeEventListener("keydown", onKeyDown);
3071
- window.removeEventListener("resize", onResize);
3072
- };
3073
- }, [isOpen, close]);
3101
+ if (!isOpen) setSearchValue("");
3102
+ }, [isOpen]);
3074
3103
  useEffect(() => {
3075
3104
  if (isOpen) searchRef.current?.focus();
3076
3105
  }, [isOpen]);
@@ -3079,53 +3108,58 @@ var ModelIdRenderer = ({
3079
3108
  const isDisabled = !enabled || readOnly;
3080
3109
  const hasError = !!errors && errors.length > 0;
3081
3110
  const selectedName = selectedModel ? selectedModel.displayName || selectedModel.name || "" : "";
3082
- const triggerText = selectedName || label || "Select model";
3111
+ const triggerText = selectedName || "Select model";
3083
3112
  const iconSrc = getModelIcon(selectedModel);
3084
3113
  const tooltip = [selectedName || label, description, hasError ? errors : null].filter(Boolean).join(" \u2014 ");
3085
- const anchorRect = isOpen ? containerRef.current?.getBoundingClientRect() : void 0;
3086
- return /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", ref: containerRef, children: [
3087
- /* @__PURE__ */ jsxs(
3088
- "button",
3089
- {
3090
- type: "button",
3091
- onClick: () => setIsOpen((v) => !v),
3092
- disabled: isDisabled,
3093
- "aria-expanded": isOpen,
3094
- "aria-haspopup": "listbox",
3095
- "aria-label": label || "Select model",
3096
- title: tooltip || void 0,
3097
- 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" : ""}`,
3098
- children: [
3099
- iconSrc ? /* @__PURE__ */ jsx(
3100
- "img",
3101
- {
3102
- src: iconSrc,
3103
- alt: "",
3104
- className: "h-3.5 w-3.5 shrink-0 object-contain"
3105
- }
3106
- ) : /* @__PURE__ */ jsx(Cpu, { className: "h-3.5 w-3.5 shrink-0" }),
3107
- /* @__PURE__ */ jsx("span", { className: "inline max-w-[9rem] truncate max-sm:hidden", children: triggerText }),
3108
- /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 shrink-0 opacity-60" })
3109
- ]
3110
- }
3111
- ),
3112
- isOpen && anchorRect && createPortal(
3113
- /* @__PURE__ */ jsxs(
3114
- "div",
3115
- {
3116
- ref: menuRef,
3117
- role: "listbox",
3118
- "aria-label": label || "Models",
3119
- className: "fixed z-50 w-72 max-w-[calc(100vw-2rem)] overflow-hidden rounded-xl border border-border bg-popover shadow-lg",
3120
- style: {
3121
- left: Math.max(
3122
- 8,
3123
- Math.min(anchorRect.left, window.innerWidth - 288 - 8)
3124
- ),
3125
- bottom: window.innerHeight - anchorRect.top + 8
3126
- },
3127
- children: [
3128
- /* @__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(
3129
3163
  "input",
3130
3164
  {
3131
3165
  ref: searchRef,
@@ -3137,7 +3171,7 @@ var ModelIdRenderer = ({
3137
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"
3138
3172
  }
3139
3173
  ) }),
3140
- /* @__PURE__ */ jsxs("div", { className: "max-h-72 overflow-y-auto", children: [
3174
+ /* @__PURE__ */ jsxs("div", { className: "min-h-0 overflow-y-auto", children: [
3141
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" }) }),
3142
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" }),
3143
3177
  listModels.map((model) => {
@@ -3150,7 +3184,7 @@ var ModelIdRenderer = ({
3150
3184
  role: "option",
3151
3185
  "aria-selected": active,
3152
3186
  onClick: () => handleSelect(model),
3153
- className: "flex w-full items-center gap-2.5 px-3 py-2 text-left transition-colors hover:bg-secondary/60",
3187
+ className: popoverRowClass,
3154
3188
  children: [
3155
3189
  /* @__PURE__ */ jsx(
3156
3190
  Check,
@@ -3176,12 +3210,11 @@ var ModelIdRenderer = ({
3176
3210
  );
3177
3211
  })
3178
3212
  ] })
3179
- ]
3180
- }
3181
- ),
3182
- document.body
3183
- )
3184
- ] });
3213
+ ] })
3214
+ )
3215
+ ]
3216
+ }
3217
+ );
3185
3218
  };
3186
3219
  var modelIdRendererTester = rankWith(
3187
3220
  100,
@@ -3216,6 +3249,7 @@ var NumberRenderer = ({
3216
3249
  uischema,
3217
3250
  visible,
3218
3251
  enabled,
3252
+ config,
3219
3253
  required
3220
3254
  }) => {
3221
3255
  const isInteger = schema?.type === "integer";
@@ -3244,40 +3278,58 @@ var NumberRenderer = ({
3244
3278
  const max = fieldSchema?.maximum;
3245
3279
  const step = isInteger ? 1 : fieldSchema?.multipleOf ?? "any";
3246
3280
  const tooltip = [label, description, hasError ? errors : null].filter(Boolean).join(" \u2014 ");
3247
- return /* @__PURE__ */ jsxs(
3248
- "div",
3281
+ const surface = (config ?? {}).surface ?? "form";
3282
+ const scale = scaleFor(surface);
3283
+ const input = /* @__PURE__ */ jsx(
3284
+ "input",
3249
3285
  {
3250
- 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" : ""}`,
3251
- title: tooltip || void 0,
3252
- children: [
3253
- label && /* @__PURE__ */ jsxs(
3254
- "label",
3255
- {
3256
- htmlFor: `number-${path2}`,
3257
- className: `whitespace-nowrap ${hasError ? "text-destructive" : "text-muted-foreground"}`,
3258
- children: [
3259
- label,
3260
- required && /* @__PURE__ */ jsx("span", { className: "ml-1 text-destructive", children: "*" })
3261
- ]
3262
- }
3263
- ),
3264
- /* @__PURE__ */ jsx(
3265
- "input",
3266
- {
3267
- id: `number-${path2}`,
3268
- type: "number",
3269
- value: data ?? "",
3270
- onChange: handleInputChange,
3271
- disabled: isDisabled,
3272
- min,
3273
- max,
3274
- step,
3275
- className: "w-14 border-0 bg-transparent p-0 text-xs font-medium tabular-nums text-foreground outline-none disabled:cursor-not-allowed"
3276
- }
3277
- )
3278
- ]
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)
3279
3295
  }
3280
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
+ ] });
3281
3333
  };
3282
3334
  var numberRendererTester = rankWith(
3283
3335
  40,
@@ -3293,6 +3345,14 @@ var numberRendererTester = rankWith(
3293
3345
  }
3294
3346
  );
3295
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;
3296
3356
  var TextareaRenderer = ({
3297
3357
  data,
3298
3358
  handleChange,
@@ -3304,28 +3364,49 @@ var TextareaRenderer = ({
3304
3364
  uischema,
3305
3365
  visible,
3306
3366
  enabled,
3307
- required
3367
+ required,
3368
+ config
3308
3369
  }) => {
3309
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));
3310
3386
  const autoResize = useCallback(() => {
3311
- if (textareaRef.current) {
3312
- textareaRef.current.style.height = "auto";
3313
- textareaRef.current.style.height = `${Math.min(
3314
- Math.max(textareaRef.current.scrollHeight, 80),
3315
- // Minimum height of 80px
3316
- 240
3317
- // Maximum height of 240px
3318
- )}px`;
3319
- }
3320
- }, []);
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]);
3321
3395
  useEffect(() => {
3322
3396
  autoResize();
3323
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]);
3324
3406
  const handleInputChange = useCallback(
3325
3407
  (event) => {
3326
- const newValue = event.target.value;
3327
- handleChange(path2, newValue);
3328
- setTimeout(autoResize, 0);
3408
+ handleChange(path2, event.target.value);
3409
+ autoResize();
3329
3410
  },
3330
3411
  [handleChange, path2, autoResize]
3331
3412
  );
@@ -3335,89 +3416,34 @@ var TextareaRenderer = ({
3335
3416
  const readOnly = uischema?.access === "Read";
3336
3417
  const isDisabled = !enabled || readOnly;
3337
3418
  const hasError = errors && errors.length > 0;
3338
- const textareaOptions = schema["ui:textarea"] ?? {};
3339
3419
  const placeholder = textareaOptions.placeholder || schema?.description || `Enter ${label?.toLowerCase() || "text"}...`;
3340
- const minRows = textareaOptions.minRows || 3;
3341
3420
  return /* @__PURE__ */ jsxs("div", { className: "ss-jsonforms-field ss-jsonforms-textarea", children: [
3342
- label && /* @__PURE__ */ jsxs(
3343
- "label",
3344
- {
3345
- style: {
3346
- display: "block",
3347
- color: hasError ? "#ef4444" : "#475569",
3348
- fontSize: "0.875rem",
3349
- fontWeight: 500,
3350
- marginBottom: "0.375rem"
3351
- },
3352
- children: [
3353
- label,
3354
- required && /* @__PURE__ */ jsx("span", { style: { color: "#ef4444", marginLeft: "0.25rem" }, children: "*" })
3355
- ]
3356
- }
3357
- ),
3358
- description && /* @__PURE__ */ jsx(
3359
- "div",
3360
- {
3361
- style: {
3362
- color: "#6b7280",
3363
- fontSize: "0.75rem",
3364
- marginBottom: "0.5rem"
3365
- },
3366
- children: description
3367
- }
3368
- ),
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 }),
3369
3426
  /* @__PURE__ */ jsx(
3370
3427
  "textarea",
3371
3428
  {
3429
+ id: path2,
3372
3430
  ref: textareaRef,
3373
3431
  value: data || "",
3374
3432
  onChange: handleInputChange,
3375
3433
  placeholder,
3376
3434
  disabled: isDisabled,
3377
3435
  rows: minRows,
3436
+ className: `resize-y ${fieldControlClass(scaleName, !!hasError)}`,
3378
3437
  style: {
3379
- width: "100%",
3380
- minHeight: "80px",
3381
- maxHeight: "240px",
3382
- resize: "vertical",
3383
- padding: "0.75rem",
3384
- border: hasError ? "2px solid #ef4444" : "1px solid #d1d5db",
3385
- borderRadius: "6px",
3386
- fontSize: "16px",
3387
- lineHeight: "1.5",
3388
- fontFamily: "inherit",
3389
- backgroundColor: isDisabled ? "#f9fafb" : "#ffffff",
3390
- color: isDisabled ? "#9ca3af" : "#111827",
3391
- outline: "none",
3392
- transition: "border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out",
3393
- 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.
3394
3442
  WebkitTextSizeAdjust: "100%"
3395
- },
3396
- onFocus: (e) => {
3397
- if (!hasError) {
3398
- e.target.style.borderColor = "#6366f1";
3399
- e.target.style.boxShadow = "0 0 0 1px #6366f1";
3400
- }
3401
- },
3402
- onBlur: (e) => {
3403
- if (!hasError) {
3404
- e.target.style.borderColor = "#d1d5db";
3405
- e.target.style.boxShadow = "none";
3406
- }
3407
3443
  }
3408
3444
  }
3409
3445
  ),
3410
- hasError && /* @__PURE__ */ jsx(
3411
- "div",
3412
- {
3413
- style: {
3414
- color: "#ef4444",
3415
- fontSize: "0.75rem",
3416
- marginTop: "0.25rem"
3417
- },
3418
- children: errors
3419
- }
3420
- )
3446
+ hasError && /* @__PURE__ */ jsx("div", { className: fieldErrorClass, children: errors })
3421
3447
  ] });
3422
3448
  };
3423
3449
  var textareaRendererTester = rankWith(
@@ -3469,14 +3495,31 @@ var renderers = [
3469
3495
  ];
3470
3496
 
3471
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
+ }
3472
3512
  function buildSimpleSchemaAndUi(vars, threadVars, useDefaults) {
3473
3513
  const names = Object.keys(vars || {});
3474
3514
  const properties2 = {};
3475
3515
  const controls = [];
3516
+ const inlineOnly = [];
3476
3517
  const initialData = {};
3518
+ const $defs = {};
3477
3519
  for (const name of names) {
3478
3520
  const cfg = vars?.[name] || {};
3479
- const s2 = cfg.schema || {};
3521
+ const { $defs: varDefs, ...s2 } = cfg.schema || {};
3522
+ if (varDefs) Object.assign($defs, varDefs);
3480
3523
  properties2[name] = s2;
3481
3524
  const hasServerKey = threadVars !== void 0 && Object.prototype.hasOwnProperty.call(threadVars, name);
3482
3525
  const val = hasServerKey ? threadVars?.[name] : useDefaults ? s2.default : void 0;
@@ -3490,18 +3533,24 @@ function buildSimpleSchemaAndUi(vars, threadVars, useDefaults) {
3490
3533
  control.enabled = false;
3491
3534
  }
3492
3535
  controls.push(control);
3536
+ if (isModelSelector(properties2[name])) inlineOnly.push(control);
3493
3537
  }
3494
3538
  const schema = { type: "object", properties: properties2 };
3495
- const innerRow = {
3496
- type: "HorizontalLayout",
3497
- elements: controls,
3498
- options: { gap: "12px", alignItems: "flex-start" }
3499
- };
3500
- const ui = {
3501
- type: "VerticalLayout",
3502
- elements: [innerRow]
3539
+ if (Object.keys($defs).length > 0) schema.$defs = $defs;
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
3503
3553
  };
3504
- return { schema, uiSchema: ui, initialData };
3505
3554
  }
3506
3555
  function useChatVariablesFormVm({
3507
3556
  workspace,
@@ -3516,26 +3565,26 @@ function useChatVariablesFormVm({
3516
3565
  const { mutate: updateVariableMutation } = useUpdateFlowRunVariable();
3517
3566
  const querySettled = !isLoading && (threadVars !== void 0 || isError);
3518
3567
  const shouldUseDefaults = isError || threadVars && Object.keys(threadVars).length === 0;
3519
- const built = React8.useMemo(() => {
3568
+ const built = React9.useMemo(() => {
3520
3569
  return buildSimpleSchemaAndUi(
3521
3570
  workspace.variables,
3522
3571
  threadVars,
3523
3572
  shouldUseDefaults ?? false
3524
3573
  );
3525
3574
  }, [workspace.variables, threadVars, shouldUseDefaults]);
3526
- const [data, setData] = React8.useState(null);
3527
- React8.useEffect(() => {
3575
+ const [data, setData] = React9.useState(null);
3576
+ React9.useEffect(() => {
3528
3577
  if (querySettled) {
3529
3578
  setData(built.initialData);
3530
3579
  setVariables(built.initialData);
3531
3580
  }
3532
3581
  }, [querySettled, built.initialData, setVariables]);
3533
- const ajv = React8.useMemo(() => createAjv({ useDefaults: false }), []);
3534
- const prevRef = React8.useRef(null);
3535
- React8.useEffect(() => {
3582
+ const ajv = React9.useMemo(() => createAjv({ useDefaults: false }), []);
3583
+ const prevRef = React9.useRef(null);
3584
+ React9.useEffect(() => {
3536
3585
  prevRef.current = data;
3537
3586
  }, [data]);
3538
- const onChange = React8.useCallback(
3587
+ const onChange = React9.useCallback(
3539
3588
  ({ data: next2 }) => {
3540
3589
  if (prevRef.current && !isDraftThreadId(threadId)) {
3541
3590
  const keys2 = Object.keys(workspace.variables || {});
@@ -3556,29 +3605,96 @@ function useChatVariablesFormVm({
3556
3605
  },
3557
3606
  [workspace.variables, setVariables, updateVariableMutation, threadId]
3558
3607
  );
3559
- const config = React8.useMemo(
3608
+ const barConfig = React9.useMemo(
3560
3609
  () => ({
3561
3610
  restrict: true,
3562
3611
  trim: false,
3563
3612
  showUnfocusedDescription: true,
3564
- hideRequiredAsterisk: true
3613
+ hideRequiredAsterisk: true,
3614
+ surface: "bar",
3615
+ minRows: 1,
3616
+ maxRows: 6
3565
3617
  }),
3566
3618
  []
3567
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
+ );
3568
3632
  return {
3569
3633
  schema: built.schema,
3570
- uiSchema: built.uiSchema,
3634
+ inlineUiSchema,
3635
+ overflowUiSchema,
3636
+ overflowCount: built.overflowControls.length,
3571
3637
  data,
3572
3638
  renderers,
3573
3639
  cells,
3574
3640
  ajv,
3575
3641
  onChange,
3576
- config,
3642
+ barConfig,
3643
+ panelConfig,
3577
3644
  isLoading,
3578
3645
  isReady: querySettled,
3579
3646
  isHydrated: data !== null
3580
3647
  };
3581
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
+ }
3582
3698
  var ChatVariablesForm = forwardRef(({ workspace, threadId, setVariables }, ref) => {
3583
3699
  const vm = useChatVariablesFormVm({ workspace, threadId, setVariables });
3584
3700
  useImperativeHandle(ref, () => ({
@@ -3594,19 +3710,32 @@ var ChatVariablesForm = forwardRef(({ workspace, threadId, setVariables }, ref)
3594
3710
  if (!vm.isHydrated) {
3595
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" }) });
3596
3712
  }
3597
- return /* @__PURE__ */ jsx("div", { className: "w-full jsonforms-compact", children: /* @__PURE__ */ jsx(
3598
- JsonForms,
3599
- {
3600
- schema: vm.schema,
3601
- uischema: vm.uiSchema,
3602
- data: vm.data,
3603
- renderers: vm.renderers,
3604
- cells: vm.cells,
3605
- ajv: vm.ajv,
3606
- onChange: vm.onChange,
3607
- config: vm.config
3608
- }
3609
- ) });
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
+ ] });
3610
3739
  });
3611
3740
 
3612
3741
  // src/domains/workspaces/queryKeys.ts
@@ -4078,7 +4207,7 @@ function MessageComposer(_props = {}) {
4078
4207
  disabled,
4079
4208
  placeholder: "What would you like to do?",
4080
4209
  className: "md-editor--bare px-5 pb-3 pt-4 text-sm",
4081
- minHeight: 48
4210
+ minHeight: 24
4082
4211
  },
4083
4212
  `composer-md-${editorKey}`
4084
4213
  ) }),
@@ -18941,6 +19070,79 @@ var getMessageErrorText = (error) => {
18941
19070
  }
18942
19071
  return STATUS_CODE_TEXT[error.code] ?? GENERIC_ERROR_TEXT;
18943
19072
  };
19073
+ var FileInfoSchema = z.object({
19074
+ id: z.string(),
19075
+ name: z.string()
19076
+ });
19077
+ z.object({
19078
+ workspaceId: z.string().optional(),
19079
+ threadId: z.string().optional()
19080
+ });
19081
+
19082
+ // src/domains/messages/schemas.ts
19083
+ var MessageResponseSourceSchema = z.object({
19084
+ index: z.number(),
19085
+ datasetItemId: z.string().optional(),
19086
+ containerItemId: z.string().nullish(),
19087
+ flowRunId: z.string().optional(),
19088
+ file: FileInfoSchema.nullish(),
19089
+ url: z.string().nullish(),
19090
+ sourceType: z.nativeEnum(MessageResponseSourceType),
19091
+ // WS3c citation grounding (additive; absent on older responses).
19092
+ uri: z.string().nullish(),
19093
+ citedText: z.string().nullish(),
19094
+ attribution: z.nativeEnum(MessageAttribution).nullish(),
19095
+ // Where the cited item lives at its source (SharePoint web link, HubSpot record page).
19096
+ // Distinct from `url` (the source *is* that URL): this rides File sources so the card
19097
+ // can offer "open at source" beside the download of our copy. Absent when unknown.
19098
+ webUrl: z.string().nullish()
19099
+ });
19100
+ var MessageResponseSchema = z.object({
19101
+ content: z.string(),
19102
+ messageId: z.string(),
19103
+ sources: z.array(MessageResponseSourceSchema).nullish(),
19104
+ isReplying: z.boolean().nullish().default(false),
19105
+ requestedJsonSchema: z.string().nullish()
19106
+ });
19107
+ var MessageErrorMessageSchema = z.object({
19108
+ code: z.number(),
19109
+ // Stable machine-readable category (e.g. "llm.rate_limit"). Accept both
19110
+ // spellings: app-api serialises camelCase, the ai-api origin field is
19111
+ // snake_case — the mapper coalesces them.
19112
+ errorCode: z.string().nullish(),
19113
+ error_code: z.string().nullish(),
19114
+ message: z.string().nullish(),
19115
+ data: z.string().nullish(),
19116
+ blockId: z.string().nullish()
19117
+ });
19118
+ var MessageSchema = z.object({
19119
+ id: z.string().nullish(),
19120
+ createdAt: DateFromApi,
19121
+ createdBy: z.string().nullish(),
19122
+ hasComments: z.boolean().default(false),
19123
+ createdByUserId: z.string().nullish(),
19124
+ messageThreadId: z.string().nullish(),
19125
+ errors: z.array(MessageErrorMessageSchema).nullish(),
19126
+ values: z.array(
19127
+ z.object({
19128
+ id: z.string(),
19129
+ name: z.string(),
19130
+ type: z.nativeEnum(MessageValueType),
19131
+ value: z.any(),
19132
+ // Can be string (for Output), array (for Input), or object (for Variables)
19133
+ channels: z.record(z.string(), z.number()),
19134
+ createdAt: DateFromApi,
19135
+ createdBy: z.string(),
19136
+ createdByUserId: z.string().nullish()
19137
+ })
19138
+ ).nullish(),
19139
+ optimistic: z.boolean().optional().default(false)
19140
+ });
19141
+ z.object({
19142
+ text: z.string().nullish(),
19143
+ image: FileInfoSchema.nullish()
19144
+ });
19145
+ z.array(MessageSchema);
18944
19146
 
18945
19147
  // src/domains/messages/statuses.ts
18946
19148
  var asRecord = (value) => {
@@ -19084,7 +19286,7 @@ var buttonVariants = cva(
19084
19286
  }
19085
19287
  }
19086
19288
  );
19087
- var Button = React8.forwardRef(
19289
+ var Button = React9.forwardRef(
19088
19290
  ({ className, variant, size, asChild = false, ...props }, ref) => {
19089
19291
  const Comp = asChild ? Slot : "button";
19090
19292
  return /* @__PURE__ */ jsx(
@@ -19547,6 +19749,7 @@ function ChatMessageSources({
19547
19749
  /* @__PURE__ */ jsx(SectionLabel, { children: "Files" }),
19548
19750
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: fileSources.map((source) => {
19549
19751
  const Icon = getFileIcon3(source.file.name);
19752
+ const sourceHref = source.webUrl ? getSafeUrl(source.webUrl) : null;
19550
19753
  if (!openIndexes.has(source.index)) {
19551
19754
  return /* @__PURE__ */ jsxs(
19552
19755
  "button",
@@ -19595,19 +19798,36 @@ function ChatMessageSources({
19595
19798
  )
19596
19799
  ] }),
19597
19800
  /* @__PURE__ */ jsx(CitedQuote, { text: source.citedText }),
19598
- /* @__PURE__ */ jsxs(
19599
- "button",
19600
- {
19601
- type: "button",
19602
- onClick: () => downloadFileMutation.mutate(source.file),
19603
- disabled: downloadFileMutation.isPending,
19604
- className: "inline-flex w-fit items-center gap-1 rounded-md border border-border/60 px-2 py-1 text-xs font-medium text-foreground transition-colors hover:bg-muted/40 disabled:cursor-not-allowed disabled:opacity-50",
19605
- children: [
19606
- /* @__PURE__ */ jsx(Download, { className: "h-3 w-3" }),
19607
- "Download"
19608
- ]
19609
- }
19610
- )
19801
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [
19802
+ sourceHref && /* @__PURE__ */ jsxs(
19803
+ "a",
19804
+ {
19805
+ href: sourceHref,
19806
+ target: "_blank",
19807
+ rel: "noreferrer",
19808
+ title: source.webUrl ?? void 0,
19809
+ className: "inline-flex w-fit items-center gap-1 rounded-md border border-border/60 px-2 py-1 text-xs font-medium text-foreground no-underline transition-colors hover:bg-muted/40",
19810
+ children: [
19811
+ /* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3" }),
19812
+ "Open in ",
19813
+ getHost(sourceHref)
19814
+ ]
19815
+ }
19816
+ ),
19817
+ /* @__PURE__ */ jsxs(
19818
+ "button",
19819
+ {
19820
+ type: "button",
19821
+ onClick: () => downloadFileMutation.mutate(source.file),
19822
+ disabled: downloadFileMutation.isPending,
19823
+ className: "inline-flex w-fit items-center gap-1 rounded-md border border-border/60 px-2 py-1 text-xs font-medium text-foreground transition-colors hover:bg-muted/40 disabled:cursor-not-allowed disabled:opacity-50",
19824
+ children: [
19825
+ /* @__PURE__ */ jsx(Download, { className: "h-3 w-3" }),
19826
+ "Download"
19827
+ ]
19828
+ }
19829
+ )
19830
+ ] })
19611
19831
  ]
19612
19832
  },
19613
19833
  `${source.file.id}-${source.index}`
@@ -19617,6 +19837,7 @@ function ChatMessageSources({
19617
19837
  ] })
19618
19838
  ] });
19619
19839
  }
19840
+ var USER_FORM_CONFIG = { surface: "form", minRows: 6, maxRows: 20 };
19620
19841
  var REVEAL_ON_HOVER = "opacity-100 focus-visible:opacity-100 [@media(hover:hover)]:opacity-0 [@media(hover:hover)]:group-hover:opacity-100";
19621
19842
  var MessageBubble = (props) => {
19622
19843
  const {
@@ -19670,7 +19891,7 @@ var MessageBubble = (props) => {
19670
19891
  );
19671
19892
  })
19672
19893
  ] }),
19673
- 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: [
19674
19895
  /* @__PURE__ */ jsx(
19675
19896
  JsonForms,
19676
19897
  {
@@ -19679,6 +19900,7 @@ var MessageBubble = (props) => {
19679
19900
  renderers,
19680
19901
  cells,
19681
19902
  readonly: userInput !== void 0,
19903
+ config: USER_FORM_CONFIG,
19682
19904
  onChange: ({ data, errors }) => {
19683
19905
  setResponseFormData(data);
19684
19906
  setResponseFormValid(!errors?.length);
@@ -19811,6 +20033,10 @@ function coerceSources(x) {
19811
20033
  if (!Array.isArray(x)) return [];
19812
20034
  return x.filter(isMessageResponseSource);
19813
20035
  }
20036
+ var ENVELOPE_KEYS = Object.keys(MessageResponseSchema.shape);
20037
+ function isResponseEnvelope(keys2) {
20038
+ return keys2.length > 0 && keys2.every((key) => ENVELOPE_KEYS.includes(key));
20039
+ }
19814
20040
  var MessageItem = ({
19815
20041
  message,
19816
20042
  isLive = false
@@ -19918,9 +20144,16 @@ var MessageItem = ({
19918
20144
  });
19919
20145
  } else if (name === "response" && typeof v.value === "object" && v.value !== null && !Array.isArray(v.value)) {
19920
20146
  const resp = v.value;
19921
- const isContentPart = "text" in resp || "image" in resp;
19922
- pushContent(groupContent, isContentPart ? resp : resp.content);
19923
- groupSources = coerceSources(resp.sources);
20147
+ const keys2 = Object.keys(resp);
20148
+ const isEnvelope = isResponseEnvelope(keys2);
20149
+ if ("sources" in resp && (isEnvelope || "text" in resp || "image" in resp)) {
20150
+ groupSources = coerceSources(resp.sources);
20151
+ }
20152
+ if (isEnvelope) {
20153
+ pushContent(groupContent, resp.content);
20154
+ } else if (keys2.length > 0) {
20155
+ pushContent(groupContent, resp);
20156
+ }
19924
20157
  } else {
19925
20158
  pushContent(groupContent, v.value);
19926
20159
  }
@@ -20146,6 +20379,7 @@ function MessageList({
20146
20379
  ] }) });
20147
20380
  }
20148
20381
  if (safeMessages.length === 0 && !hadMessagesBefore) {
20382
+ const descriptionParagraphs = (activeWorkspace?.summary ?? "").split(/\n+/).map((paragraph2) => paragraph2.trim()).filter(Boolean);
20149
20383
  return (
20150
20384
  // A new thread opens the way the design does: the greeting and the
20151
20385
  // composer travel together as one narrower block centred in the canvas.
@@ -20158,6 +20392,14 @@ function MessageList({
20158
20392
  "data-ss-layer": "message-list",
20159
20393
  children: /* @__PURE__ */ jsxs("div", { className: "mx-auto mb-6 w-full max-w-2xl px-4 text-center", children: [
20160
20394
  /* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold text-foreground", children: "What\u2019s on the agenda today?" }),
20395
+ descriptionParagraphs.length > 0 && /* @__PURE__ */ jsx(
20396
+ "div",
20397
+ {
20398
+ className: "mx-auto mt-2 max-w-lg space-y-1.5 text-sm text-muted-foreground",
20399
+ "data-ss-layer": "workspace-description",
20400
+ children: descriptionParagraphs.map((paragraph2, index) => /* @__PURE__ */ jsx("p", { children: paragraph2 }, index))
20401
+ }
20402
+ ),
20161
20403
  activeWorkspace?.firstPrompt && /* @__PURE__ */ jsx("div", { className: "chat-prose mt-3 text-center", children: /* @__PURE__ */ jsx(MessageMarkdown, { value: activeWorkspace.firstPrompt }) })
20162
20404
  ] })
20163
20405
  }
@@ -20188,7 +20430,7 @@ function MessageList({
20188
20430
  {
20189
20431
  ref: viewportRef,
20190
20432
  "data-ss-layer": "scroll-viewport",
20191
- className: "h-full w-full rounded-[inherit] overflow-y-auto",
20433
+ className: "h-full w-full rounded-[inherit] overflow-y-auto [&>div]:!block",
20192
20434
  onScroll: () => {
20193
20435
  if (!viewportRef.current) return;
20194
20436
  const viewport = viewportRef.current;