@coffer-org/plugin-webchat 4.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as __fedReact from "react";
2
- import { Dialog, DialogContent, getPluginRoutes, getSiteLinks, getSiteShelves, uploadFile, useMinWidth } from "@coffer-org/web-ui";
2
+ import { Dialog, DialogContent, classifySiteHref, uploadFile, useMinWidth } from "@coffer-org/web-ui";
3
3
  import { createContext, createElement, forwardRef, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { useLocation, useNavigate } from "react-router-dom";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -23722,54 +23722,6 @@ function remarkGfm(options) {
23722
23722
  toMarkdownExtensions.push(gfmToMarkdown(settings));
23723
23723
  }
23724
23724
  //#endregion
23725
- //#region src/ui/internal-link.ts
23726
- var SEGMENT = /^[A-Za-z0-9._~%-]+$/;
23727
- /** Returns captured ':param' values on a match, or null. '*' is legal only as the
23728
- * last pattern segment, so this branch may return immediately without reading got to the end. */
23729
- function matchesPattern(pathname, pattern) {
23730
- const got = pathname.split("/").filter(Boolean);
23731
- const want = pattern.split("/").filter(Boolean);
23732
- const captured = {};
23733
- for (let i = 0; i < want.length; i++) {
23734
- const w = want[i];
23735
- if (w === "*") return got.length >= i ? captured : null;
23736
- const g = got[i];
23737
- if (g === void 0) return null;
23738
- if (w.startsWith(":")) {
23739
- if (!SEGMENT.test(g)) return null;
23740
- captured[w.slice(1)] = g;
23741
- } else if (w !== g) return null;
23742
- }
23743
- return got.length === want.length ? captured : null;
23744
- }
23745
- function classifyHref(href, origin, routes, pairs = []) {
23746
- const raw = href.trim();
23747
- if (!raw || raw.startsWith("//")) return { kind: "text" };
23748
- if (!raw.startsWith("/") && !/^https?:\/\//i.test(raw)) return { kind: "text" };
23749
- let url;
23750
- try {
23751
- url = new URL(raw, origin);
23752
- } catch {
23753
- return { kind: "text" };
23754
- }
23755
- if (url.origin !== origin) return {
23756
- kind: "external",
23757
- href: url.href
23758
- };
23759
- for (const r of routes) {
23760
- const captured = matchesPattern(url.pathname, r.pattern);
23761
- if (!captured) continue;
23762
- if (pairs.length && captured["library"] !== void 0 && captured["shelf"] !== void 0) {
23763
- if (!pairs.some((p) => p.library === captured["library"] && p.shelf === captured["shelf"])) return { kind: "text" };
23764
- }
23765
- return {
23766
- kind: "internal",
23767
- to: `${url.pathname}${url.search}${url.hash}`
23768
- };
23769
- }
23770
- return { kind: "text" };
23771
- }
23772
- //#endregion
23773
23725
  //#region src/ui/chat-nav.tsx
23774
23726
  /** What to do with the widget when the user follows a link from a response.
23775
23727
  * ChatWidget supplies the callback (it owns mode/open), avoiding a prop
@@ -23811,8 +23763,7 @@ function ChatTable(props) {
23811
23763
  function ChatLink({ href, children }) {
23812
23764
  const onNavigate = useChatNav();
23813
23765
  const navigate = useNavigate();
23814
- const routes = [...getSiteLinks(), ...getPluginRoutes().map((r) => ({ pattern: `/${r.path}` }))];
23815
- const kind = classifyHref(href ?? "", window.location.origin, routes, getSiteShelves());
23766
+ const kind = classifySiteHref(href ?? "");
23816
23767
  if (kind.kind === "internal") return /* @__PURE__ */ jsx("a", {
23817
23768
  href: kind.to,
23818
23769
  onClick: (event) => {
@@ -23966,15 +23917,6 @@ function starLadder(rows, multi) {
23966
23917
  }
23967
23918
  return byLevel.every((r) => r !== void 0) ? byLevel : null;
23968
23919
  }
23969
- /** The stars as a picture: `filled` of `max` lit. Same palette as the `f.rating` widget
23970
- * (filled yellow, empty at border colour), so the gesture reads the same in both places. */
23971
- function Stars({ filled, max, size }) {
23972
- return /* @__PURE__ */ jsx(Fragment, { children: Array.from({ length: max }, (_, i) => /* @__PURE__ */ jsx(Star, {
23973
- size,
23974
- "aria-hidden": true,
23975
- className: i < filled ? "shrink-0 fill-yellow-400 text-yellow-400" : "shrink-0 text-border"
23976
- }, i)) });
23977
- }
23978
23920
  /** Flatten the catalog to one list: with a single registered agent the agent
23979
23921
  * level is noise, and with several the agent name simply prefixes its presets. */
23980
23922
  function AgentPicker({ agents, defaultAgentId, selection, onChange }) {
@@ -24001,9 +23943,6 @@ function AgentPicker({ agents, defaultAgentId, selection, onChange }) {
24001
23943
  document.removeEventListener("keydown", onKeyDown);
24002
23944
  };
24003
23945
  }, [open]);
24004
- useEffect(() => {
24005
- if (!open) setPreview(null);
24006
- }, [open]);
24007
23946
  const multi = agents.length > 1;
24008
23947
  const rows = agents.flatMap((a) => a.presets.map((p) => ({
24009
23948
  agentId: a.id,
@@ -24017,43 +23956,19 @@ function AgentPicker({ agents, defaultAgentId, selection, onChange }) {
24017
23956
  if (rows.length === 0) return null;
24018
23957
  const current = rows.find((r) => r.agentId === selection.agentId && r.presetId === selection.presetId) ?? rows.find((r) => r.agentId === defaultAgentId && r.isDefault) ?? rows.find((r) => r.isDefault) ?? rows[0];
24019
23958
  const ladder = starLadder(rows, multi);
24020
- const level = ladder ? ladder.indexOf(current) + 1 : 0;
24021
- const shown = ladder ? preview ?? level : 0;
24022
- const shownRow = ladder ? ladder[shown - 1] : void 0;
24023
- const levelName = (row, n) => `${row.label} · ${t("webchat.level", {
24024
- value: n,
24025
- max: ladder.length
24026
- })}`;
24027
- return /* @__PURE__ */ jsxs("div", {
24028
- className: "relative",
24029
- ref: rootRef,
24030
- children: [/* @__PURE__ */ jsx("button", {
24031
- ref: triggerRef,
24032
- type: "button",
24033
- "aria-haspopup": "menu",
24034
- "aria-expanded": open,
24035
- className: "flex items-center gap-1 rounded-md px-2 py-1 text-xs text-muted hover:text-text focus-visible:outline-2 focus-visible:outline-accent",
24036
- onClick: () => setOpen((v) => !v),
24037
- ...ladder ? { "aria-label": levelName(current, level) } : {},
24038
- children: ladder ? /* @__PURE__ */ jsx("span", {
24039
- className: "flex items-center gap-0.5",
24040
- children: /* @__PURE__ */ jsx(Stars, {
24041
- filled: level,
24042
- max: ladder.length,
24043
- size: 14
24044
- })
24045
- }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
24046
- className: "max-w-40 truncate",
24047
- children: current.label
24048
- }), /* @__PURE__ */ jsx(ChevronDown, {
24049
- size: 12,
24050
- className: "shrink-0"
24051
- })] })
24052
- }), open && (ladder ? /* @__PURE__ */ jsxs("div", {
24053
- className: "absolute bottom-full left-0 z-10 mb-1 rounded-md border border-border bg-elevated px-2 py-1.5 shadow-lg",
23959
+ if (ladder) {
23960
+ const level = ladder.indexOf(current) + 1;
23961
+ const shown = preview ?? level;
23962
+ const previewRow = preview === null ? void 0 : ladder[preview - 1];
23963
+ const levelName = (row, n) => `${row.label} · ${t("webchat.level", {
23964
+ value: n,
23965
+ max: ladder.length
23966
+ })}`;
23967
+ return /* @__PURE__ */ jsxs("div", {
23968
+ className: "flex min-w-0 items-center gap-1.5",
24054
23969
  children: [/* @__PURE__ */ jsx("ul", {
24055
23970
  "aria-label": t("webchat.agentPicker"),
24056
- className: "flex items-center",
23971
+ className: "flex shrink-0 items-center",
24057
23972
  onPointerLeave: () => setPreview(null),
24058
23973
  children: ladder.map((r, i) => {
24059
23974
  const n = i + 1;
@@ -24061,29 +23976,45 @@ function AgentPicker({ agents, defaultAgentId, selection, onChange }) {
24061
23976
  type: "button",
24062
23977
  ...r === current ? { "aria-current": true } : {},
24063
23978
  "aria-label": levelName(r, n),
24064
- className: "flex p-0.5 focus-visible:outline-2 focus-visible:outline-accent",
23979
+ className: "flex rounded p-1 focus-visible:outline-2 focus-visible:outline-accent",
24065
23980
  onPointerEnter: () => setPreview(n),
24066
23981
  onFocus: () => setPreview(n),
24067
23982
  onBlur: () => setPreview(null),
24068
- onClick: () => {
24069
- setOpen(false);
24070
- onChange({
24071
- agentId: r.agentId,
24072
- presetId: r.presetId
24073
- });
24074
- },
23983
+ onClick: () => onChange({
23984
+ agentId: r.agentId,
23985
+ presetId: r.presetId
23986
+ }),
24075
23987
  children: /* @__PURE__ */ jsx(Star, {
24076
- size: 20,
23988
+ size: 16,
24077
23989
  "aria-hidden": true,
24078
23990
  className: n <= shown ? "fill-yellow-400 text-yellow-400" : "text-border"
24079
23991
  })
24080
23992
  }) }, `${r.agentId}/${r.presetId}`);
24081
23993
  })
24082
- }), /* @__PURE__ */ jsx("p", {
24083
- className: "mt-0.5 text-center text-xs text-muted",
24084
- children: shownRow?.hint || shownRow?.label
23994
+ }), previewRow && /* @__PURE__ */ jsx("span", {
23995
+ className: "min-w-0 truncate text-xs text-muted",
23996
+ children: previewRow.hint || previewRow.label
24085
23997
  })]
24086
- }) : /* @__PURE__ */ jsx("ul", {
23998
+ });
23999
+ }
24000
+ return /* @__PURE__ */ jsxs("div", {
24001
+ className: "relative",
24002
+ ref: rootRef,
24003
+ children: [/* @__PURE__ */ jsxs("button", {
24004
+ ref: triggerRef,
24005
+ type: "button",
24006
+ "aria-haspopup": "menu",
24007
+ "aria-expanded": open,
24008
+ className: "flex items-center gap-1 rounded-md px-2 py-1 text-xs text-muted hover:text-text focus-visible:outline-2 focus-visible:outline-accent",
24009
+ onClick: () => setOpen((v) => !v),
24010
+ children: [/* @__PURE__ */ jsx("span", {
24011
+ className: "max-w-40 truncate",
24012
+ children: current.label
24013
+ }), /* @__PURE__ */ jsx(ChevronDown, {
24014
+ size: 12,
24015
+ className: "shrink-0"
24016
+ })]
24017
+ }), open && /* @__PURE__ */ jsx("ul", {
24087
24018
  "aria-label": t("webchat.agentPicker"),
24088
24019
  className: "absolute bottom-full left-0 z-10 mb-1 max-h-64 w-64 max-w-[calc(100vw-2rem)] overflow-y-auto rounded-md border border-border bg-elevated py-1 shadow-lg",
24089
24020
  children: rows.map((r) => {
@@ -24116,7 +24047,7 @@ function AgentPicker({ agents, defaultAgentId, selection, onChange }) {
24116
24047
  ]
24117
24048
  }) }, `${r.agentId}/${r.presetId}`);
24118
24049
  })
24119
- }))]
24050
+ })]
24120
24051
  });
24121
24052
  }
24122
24053
  //#endregion
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ export default definePlugin({
12
12
  label: 'webchat.settings.reasoning_display',
13
13
  default: 'live',
14
14
  strict: true,
15
- view: { noSearch: true },
15
+ noSearch: true,
16
16
  options: [
17
17
  { value: 'live', title: 'webchat.settings.reasoning_display_live' },
18
18
  { value: 'off', title: 'webchat.settings.reasoning_display_off' },
@@ -1 +1,2 @@
1
1
  export declare const WEB_FORMAT: string;
2
+ export declare function webChannelSystem(): Promise<string>;
@@ -1,3 +1,4 @@
1
+ import { frontendInstructions } from '@coffer-org/server/frontend-agent';
1
2
  export const WEB_FORMAT = [
2
3
  'CRITICAL OUTPUT FORMAT — MARKDOWN ONLY. Format the reply strictly as Markdown (GFM). NEVER emit raw HTML tags — they are stripped before rendering and their content may be lost.',
3
4
  'Inline: **bold**, *italic*, `inline code`, ~~strikethrough~~. Links: [text](https://…).',
@@ -8,3 +9,7 @@ export const WEB_FORMAT = [
8
9
  'The reply is read in a narrow chat panel (~360px) — keep it short and skimmable: short paragraphs, lists over prose, narrow tables.',
9
10
  'Use Unicode symbols and emoji to structure replies: arrows (→), status marks (✅ ⚠️ ❌ ⏳), topic emoji (📅 💰 🏥 🛒 🌱 …).',
10
11
  ].join('\n');
12
+ export async function webChannelSystem() {
13
+ const site = await frontendInstructions('');
14
+ return [WEB_FORMAT, site].filter(Boolean).join('\n\n');
15
+ }
@@ -3,7 +3,7 @@ import { handleIncoming, liveAgentId } from '@coffer-org/server/orchestrator';
3
3
  import { chatIdFor, recordUser, buildChain, history, readSelectionForTurn } from "./chain-store.js";
4
4
  import { policy } from "./config.js";
5
5
  import { makeStreamingConnector } from "./connector.js";
6
- import { WEB_FORMAT } from "./format.js";
6
+ import { webChannelSystem } from "./format.js";
7
7
  import { loadReasoningDisplay } from "./settings.js";
8
8
  export function pageContext(ctx) {
9
9
  if (typeof ctx !== 'object' || ctx === null)
@@ -47,11 +47,11 @@ export async function sendAction(body, ctx, deps = {}) {
47
47
  emit: ctx.emit,
48
48
  display: await loadReasoningDisplay(),
49
49
  });
50
- const volatileSystem = pageContext(body['context']);
50
+ const turnContext = pageContext(body['context']);
51
51
  await doHandle(connector, {
52
52
  connectorId: 'webchat',
53
- channelSystem: WEB_FORMAT,
54
- ...(volatileSystem ? { volatileSystem } : {}),
53
+ channelSystem: await webChannelSystem(),
54
+ ...(turnContext ? { turnContext } : {}),
55
55
  ...(agentId ? { agentId } : {}),
56
56
  ...(selection.presetId ? { presetId: selection.presetId } : {}),
57
57
  chatId,
package/dist/schema.js CHANGED
@@ -4082,6 +4082,23 @@ function typeErr(code = "invalid_type") {
4082
4082
  function reqTypeErr() {
4083
4083
  return { error: (iss) => iss.code === "invalid_type" ? iss.input === void 0 ? vmsg("required") : vmsg("invalid_type") : void 0 };
4084
4084
  }
4085
+ /**
4086
+ * A string schema whose CONTENT checks only speak once the value is known to be a string.
4087
+ *
4088
+ * zod's length checks are not type-guarded: `min`/`max` read `input.length`, and an ARRAY has
4089
+ * one. So `z.string().min(1)` answers `[]` with `invalid_type` AND `too_small` — two messages
4090
+ * about a single wrongness, both of which reach the user, since `mutate.ts` and `extend-io.ts`
4091
+ * turn every issue into its own `ValidationError`. (A number answers with `invalid_type`
4092
+ * alone, having no `length` to read — so the redundancy was array-shaped and invisible until
4093
+ * the golden matrix started recording the whole issue list instead of `issues[0]`.)
4094
+ *
4095
+ * Piping puts the type question first and alone: `add` never runs on a non-string. Pass the
4096
+ * content checks as a builder rather than chaining them onto the result, because a `ZodPipe`
4097
+ * has no `.min`/`.max`/`.regex` to chain.
4098
+ */
4099
+ function stringContent(add) {
4100
+ return string$1(reqTypeErr()).pipe(add(string$1()));
4101
+ }
4085
4102
  /** Parse a JSON string, else return the native value (object/array) as-is.
4086
4103
  * Tolerant input for native form-state AND legacy JSON-string payloads.
4087
4104
  * A string that fails JSON.parse is returned unchanged — callers detect
@@ -4709,18 +4726,36 @@ var UNITS_MAP = {
4709
4726
  label: "core.units.ppm"
4710
4727
  }]
4711
4728
  };
4729
+ /**
4730
+ * Resolve a unit spec — a named scale or an explicit option list — to its options.
4731
+ *
4732
+ * Throws rather than returning `undefined`, because both ways of reaching `undefined` here
4733
+ * produce a field that CONSTRUCTS and then crashes when someone tries to save through it:
4734
+ * `measured`'s unit role closes over these options and calls `.some(…)` on them inside its
4735
+ * refine, so a missing spec or a typo'd scale name turns a user's save into a
4736
+ * `Cannot read properties of undefined` instead of a validation message. The golden matrix
4737
+ * recorded exactly that for `f.measured({})`.
4738
+ *
4739
+ * A declaration error belongs at declaration time, where the plugin author sees it and the
4740
+ * message can name the scale they meant — the same reason the host-services seam fails at
4741
+ * registration rather than at first render.
4742
+ */
4712
4743
  function resolveUnits(u) {
4713
- return typeof u === "string" ? UNITS_MAP[u] : u;
4744
+ if (typeof u === "string") {
4745
+ const known = UNITS_MAP[u];
4746
+ if (!known) throw new Error(`units: unknown scale '${u}' (known: ${Object.keys(UNITS_MAP).sort().join(", ")})`);
4747
+ return known;
4748
+ }
4749
+ if (!Array.isArray(u)) throw new Error("units: a unit spec is required — a named scale, or an explicit list of options");
4750
+ return u;
4714
4751
  }
4715
4752
  //#endregion
4716
4753
  //#region ../sdk/src/fields/normalize.ts
4717
4754
  function normalizeOpts(rawIn) {
4718
4755
  const raw = rawIn;
4756
+ if (raw["view"] !== void 0) throw new Error("[normalizeOpts] `view` is gone — presentational options live in `ui: {}` (see CLAUDE.md § field author options and the field-types skill).");
4719
4757
  const r = raw.rules ?? {};
4720
- const v = {
4721
- ...raw.view ?? {},
4722
- ...raw.ui ?? {}
4723
- };
4758
+ const v = raw.ui ?? {};
4724
4759
  const config = {};
4725
4760
  if (r.min != null && typeof r.min === "number") config.min = r.min;
4726
4761
  if (r.max != null && typeof r.max === "number") config.max = r.max;
@@ -4729,7 +4764,7 @@ function normalizeOpts(rawIn) {
4729
4764
  if (r.messageKey != null) config.messageKey = r.messageKey;
4730
4765
  if (r.granularity != null) config.granularity = r.granularity;
4731
4766
  if (r.language != null) config.language = r.language;
4732
- const { rules: _r, view: _v, ui: _ui, ...flat } = raw;
4767
+ const { rules: _r, ui: _ui, ...flat } = raw;
4733
4768
  const result = {
4734
4769
  ...flat,
4735
4770
  ...Object.keys(config).length ? { config } : {},
@@ -4744,7 +4779,6 @@ function normalizeOpts(rawIn) {
4744
4779
  step: r.step,
4745
4780
  hidden: v.hidden,
4746
4781
  noEditControl: v.noEditControl,
4747
- noSearch: v.noSearch,
4748
4782
  display: v.display,
4749
4783
  kind: v.kind,
4750
4784
  icon: v.icon,
@@ -4752,7 +4786,11 @@ function normalizeOpts(rawIn) {
4752
4786
  ...v.keyLabel !== void 0 ? { keyLabel: v.keyLabel } : {},
4753
4787
  ...v.valueLabel !== void 0 ? { valueLabel: v.valueLabel } : {},
4754
4788
  compareWith: v.compareWith,
4755
- span: v.span
4789
+ span: v.span,
4790
+ emphasis: v.emphasis,
4791
+ noLabel: v.noLabel,
4792
+ role: v.role,
4793
+ editor: v.editor
4756
4794
  };
4757
4795
  return Object.fromEntries(Object.entries(result).filter(([, val]) => val !== void 0));
4758
4796
  }
@@ -5002,7 +5040,7 @@ function internalApiToken(o) {
5002
5040
  label: o.label,
5003
5041
  multiple: true,
5004
5042
  rules: { unique: ["name"] },
5005
- view: { kind: "internalApiToken" },
5043
+ ui: { kind: "internalApiToken" },
5006
5044
  fields: {
5007
5045
  name: string({}),
5008
5046
  token: password({}),
@@ -5028,7 +5066,7 @@ function internalOauthGrants(o) {
5028
5066
  scope: "nest",
5029
5067
  label: o.label,
5030
5068
  multiple: true,
5031
- view: { kind: "internalOauthGrants" },
5069
+ ui: { kind: "internalOauthGrants" },
5032
5070
  fields: {
5033
5071
  clientName: string({}),
5034
5072
  createdAt: string({}),
@@ -5655,10 +5693,8 @@ function country(raw) {
5655
5693
  ...raw,
5656
5694
  options: "countries",
5657
5695
  strict: true,
5658
- view: {
5659
- ...raw.view ?? {},
5660
- noSearch: true
5661
- }
5696
+ ui: { ...raw.ui ?? {} },
5697
+ noSearch: true
5662
5698
  });
5663
5699
  }
5664
5700
  /**
@@ -5682,10 +5718,8 @@ function currency(raw) {
5682
5718
  ...raw,
5683
5719
  options: "currencies",
5684
5720
  strict: true,
5685
- view: {
5686
- ...raw.view ?? {},
5687
- noSearch: true
5688
- }
5721
+ ui: { ...raw.ui ?? {} },
5722
+ noSearch: true
5689
5723
  });
5690
5724
  }
5691
5725
  /**
@@ -5845,7 +5879,7 @@ function bento(opts) {
5845
5879
  label: opts.label,
5846
5880
  icon: opts.icon,
5847
5881
  fields,
5848
- view: { kind: "bento" }
5882
+ ui: { kind: "bento" }
5849
5883
  }),
5850
5884
  view: {
5851
5885
  kind: "bento",
@@ -5874,7 +5908,7 @@ function split(opts) {
5874
5908
  return {
5875
5909
  ...group({
5876
5910
  fields: merge("split", opts.columns[0], opts.columns[1]),
5877
- view: { kind: "split" }
5911
+ ui: { kind: "split" }
5878
5912
  }),
5879
5913
  view: {
5880
5914
  kind: "split",
@@ -5898,7 +5932,7 @@ function grid(opts) {
5898
5932
  label: opts.label,
5899
5933
  icon: opts.icon,
5900
5934
  fields: opts.fields,
5901
- view: { kind: "grid" }
5935
+ ui: { kind: "grid" }
5902
5936
  }),
5903
5937
  view: {
5904
5938
  kind: "grid",
@@ -5923,7 +5957,7 @@ function spread(opts) {
5923
5957
  return {
5924
5958
  ...group({
5925
5959
  fields: merge("spread", visual, quote, opts.facts),
5926
- view: { kind: "spread" }
5960
+ ui: { kind: "spread" }
5927
5961
  }),
5928
5962
  view: {
5929
5963
  kind: "spread",
@@ -5948,7 +5982,7 @@ function aside(opts) {
5948
5982
  return {
5949
5983
  ...group({
5950
5984
  fields: merge("aside", opts.fields, opts.aside),
5951
- view: { kind: "aside" }
5985
+ ui: { kind: "aside" }
5952
5986
  }),
5953
5987
  view: {
5954
5988
  kind: "aside",
@@ -5980,7 +6014,7 @@ function tabs(opts) {
5980
6014
  return {
5981
6015
  ...group({
5982
6016
  fields,
5983
- view: { kind: "tabs" }
6017
+ ui: { kind: "tabs" }
5984
6018
  }),
5985
6019
  view: {
5986
6020
  kind: "tabs",
@@ -6012,7 +6046,7 @@ function accordion(opts) {
6012
6046
  return {
6013
6047
  ...group({
6014
6048
  fields,
6015
- view: { kind: "accordion" }
6049
+ ui: { kind: "accordion" }
6016
6050
  }),
6017
6051
  view: {
6018
6052
  kind: "accordion",
@@ -6042,7 +6076,7 @@ function prose(opts) {
6042
6076
  label: opts.label,
6043
6077
  icon: opts.icon,
6044
6078
  fields: opts.fields,
6045
- view: { kind: "prose" }
6079
+ ui: { kind: "prose" }
6046
6080
  }),
6047
6081
  view: {
6048
6082
  kind: "prose",
@@ -6068,7 +6102,7 @@ function plaque(opts) {
6068
6102
  label: opts.label,
6069
6103
  icon: opts.icon,
6070
6104
  fields: merge("plaque", title, opts.subtitle),
6071
- view: { kind: "plaque" }
6105
+ ui: { kind: "plaque" }
6072
6106
  }),
6073
6107
  view: {
6074
6108
  kind: "plaque",
@@ -6091,7 +6125,7 @@ function ledger(opts) {
6091
6125
  label: opts.label,
6092
6126
  icon: opts.icon,
6093
6127
  fields: opts.fields,
6094
- view: { kind: "ledger" }
6128
+ ui: { kind: "ledger" }
6095
6129
  }),
6096
6130
  view: { kind: "ledger" }
6097
6131
  };
@@ -6110,7 +6144,7 @@ function stats(opts) {
6110
6144
  label: opts.label,
6111
6145
  icon: opts.icon,
6112
6146
  fields: opts.fields,
6113
- view: { kind: "stats" }
6147
+ ui: { kind: "stats" }
6114
6148
  }),
6115
6149
  view: { kind: "stats" }
6116
6150
  };
@@ -6129,7 +6163,7 @@ function facets(opts) {
6129
6163
  label: opts.label,
6130
6164
  icon: opts.icon,
6131
6165
  fields: opts.fields,
6132
- view: { kind: "facets" }
6166
+ ui: { kind: "facets" }
6133
6167
  }),
6134
6168
  view: { kind: "facets" }
6135
6169
  };
@@ -6148,7 +6182,7 @@ function terminal(opts) {
6148
6182
  label: opts.label,
6149
6183
  icon: opts.icon,
6150
6184
  fields: opts.fields,
6151
- view: { kind: "terminal" }
6185
+ ui: { kind: "terminal" }
6152
6186
  }),
6153
6187
  view: {
6154
6188
  kind: "terminal",
@@ -6170,7 +6204,7 @@ function callout(opts) {
6170
6204
  label: opts.label,
6171
6205
  icon: opts.icon,
6172
6206
  fields: opts.fields,
6173
- view: { kind: "callout" }
6207
+ ui: { kind: "callout" }
6174
6208
  }),
6175
6209
  view: {
6176
6210
  kind: "callout",
@@ -6195,7 +6229,7 @@ function compare(opts) {
6195
6229
  label: opts.label,
6196
6230
  icon: opts.icon,
6197
6231
  fields: merge("compare", opts.left, opts.right),
6198
- view: { kind: "compare" }
6232
+ ui: { kind: "compare" }
6199
6233
  }),
6200
6234
  view: {
6201
6235
  kind: "compare",
@@ -6226,7 +6260,7 @@ function figure(opts) {
6226
6260
  return {
6227
6261
  ...group({
6228
6262
  fields: merge("figure", image, caption),
6229
- view: { kind: "figure" }
6263
+ ui: { kind: "figure" }
6230
6264
  }),
6231
6265
  view: {
6232
6266
  kind: "figure",
@@ -6251,7 +6285,7 @@ function epigraph(opts) {
6251
6285
  return {
6252
6286
  ...group({
6253
6287
  fields: merge("epigraph", source, attribution),
6254
- view: { kind: "epigraph" }
6288
+ ui: { kind: "epigraph" }
6255
6289
  }),
6256
6290
  view: {
6257
6291
  kind: "epigraph",
@@ -6277,7 +6311,7 @@ function masthead(opts) {
6277
6311
  return {
6278
6312
  ...group({
6279
6313
  fields: merge("masthead", overline, title, opts.meta),
6280
- view: { kind: "masthead" }
6314
+ ui: { kind: "masthead" }
6281
6315
  }),
6282
6316
  view: {
6283
6317
  kind: "masthead",
@@ -6301,7 +6335,7 @@ function timeline(opts) {
6301
6335
  label: opts.label,
6302
6336
  icon: opts.icon,
6303
6337
  fields: opts.fields,
6304
- view: { kind: "timeline" }
6338
+ ui: { kind: "timeline" }
6305
6339
  }),
6306
6340
  view: { kind: "timeline" }
6307
6341
  };
@@ -6320,7 +6354,7 @@ function countdown(opts) {
6320
6354
  ...group({
6321
6355
  label: opts.label,
6322
6356
  fields: source,
6323
- view: { kind: "countdown" }
6357
+ ui: { kind: "countdown" }
6324
6358
  }),
6325
6359
  view: {
6326
6360
  kind: "countdown",
@@ -6343,7 +6377,7 @@ function deck(opts) {
6343
6377
  ...group({
6344
6378
  label: opts.label,
6345
6379
  fields: source,
6346
- view: { kind: "deck" }
6380
+ ui: { kind: "deck" }
6347
6381
  }),
6348
6382
  view: {
6349
6383
  kind: "deck",
@@ -6365,7 +6399,7 @@ function people(opts) {
6365
6399
  ...group({
6366
6400
  label: opts.label,
6367
6401
  fields: source,
6368
- view: { kind: "people" }
6402
+ ui: { kind: "people" }
6369
6403
  }),
6370
6404
  view: {
6371
6405
  kind: "people",
@@ -6391,7 +6425,7 @@ function identity(opts) {
6391
6425
  return {
6392
6426
  ...group({
6393
6427
  fields: merge("identity", avatar, name, role, opts.channels),
6394
- view: { kind: "identity" }
6428
+ ui: { kind: "identity" }
6395
6429
  }),
6396
6430
  view: {
6397
6431
  kind: "identity",
@@ -6417,7 +6451,7 @@ function score(opts) {
6417
6451
  ...group({
6418
6452
  label: opts.label,
6419
6453
  fields: merge("score", source, verdict),
6420
- view: { kind: "score" }
6454
+ ui: { kind: "score" }
6421
6455
  }),
6422
6456
  view: {
6423
6457
  kind: "score",
@@ -6442,7 +6476,7 @@ function status(opts) {
6442
6476
  ...group({
6443
6477
  label: opts.label,
6444
6478
  fields: merge("status", source, since),
6445
- view: { kind: "status" }
6479
+ ui: { kind: "status" }
6446
6480
  }),
6447
6481
  view: {
6448
6482
  kind: "status",
@@ -6466,7 +6500,7 @@ function meter(opts) {
6466
6500
  ...group({
6467
6501
  label: opts.label,
6468
6502
  fields: merge("meter", source, of),
6469
- view: { kind: "meter" }
6503
+ ui: { kind: "meter" }
6470
6504
  }),
6471
6505
  view: {
6472
6506
  kind: "meter",
@@ -6493,7 +6527,7 @@ function receipt(opts) {
6493
6527
  ...group({
6494
6528
  label: opts.label,
6495
6529
  fields: merge("receipt", opts.fields, total),
6496
- view: { kind: "receipt" }
6530
+ ui: { kind: "receipt" }
6497
6531
  }),
6498
6532
  view: {
6499
6533
  kind: "receipt",
@@ -6516,7 +6550,7 @@ function balance(opts) {
6516
6550
  ...group({
6517
6551
  label: opts.label,
6518
6552
  fields: source,
6519
- view: { kind: "balance" }
6553
+ ui: { kind: "balance" }
6520
6554
  }),
6521
6555
  view: {
6522
6556
  kind: "balance",
@@ -6544,7 +6578,7 @@ function route(opts) {
6544
6578
  return {
6545
6579
  ...group({
6546
6580
  fields: merge("route", from, to, depart, arrive, duration),
6547
- view: { kind: "route" }
6581
+ ui: { kind: "route" }
6548
6582
  }),
6549
6583
  view: {
6550
6584
  kind: "route",
@@ -6693,7 +6727,7 @@ function nutrition(opts) {
6693
6727
  ...group({
6694
6728
  label: opts.label,
6695
6729
  fields: merge("nutrition", energy, opts.fields),
6696
- view: { kind: "nutrition" }
6730
+ ui: { kind: "nutrition" }
6697
6731
  }),
6698
6732
  view: {
6699
6733
  kind: "nutrition",
@@ -6719,7 +6753,7 @@ function specimen(opts) {
6719
6753
  return {
6720
6754
  ...group({
6721
6755
  fields: merge("specimen", title, subtitle, opts.conditions),
6722
- view: { kind: "specimen" }
6756
+ ui: { kind: "specimen" }
6723
6757
  }),
6724
6758
  view: {
6725
6759
  kind: "specimen",
@@ -6796,7 +6830,7 @@ function idcard(opts) {
6796
6830
  return {
6797
6831
  ...group({
6798
6832
  fields: merge("idcard", overline, number, opts.meta),
6799
- view: { kind: "idcard" }
6833
+ ui: { kind: "idcard" }
6800
6834
  }),
6801
6835
  view: {
6802
6836
  kind: "idcard",
@@ -6822,7 +6856,7 @@ function properties(opts) {
6822
6856
  label: opts.label,
6823
6857
  icon: opts.icon,
6824
6858
  fields: opts.fields,
6825
- view: { kind: "properties" }
6859
+ ui: { kind: "properties" }
6826
6860
  }),
6827
6861
  view: {
6828
6862
  kind: "properties",
@@ -6844,7 +6878,7 @@ function stack(opts) {
6844
6878
  label: opts.label,
6845
6879
  icon: opts.icon,
6846
6880
  fields: opts.fields,
6847
- view: { kind: "stack" }
6881
+ ui: { kind: "stack" }
6848
6882
  }),
6849
6883
  view: { kind: "stack" }
6850
6884
  };
@@ -7725,8 +7759,9 @@ var hasChildren = (x) => Array.isArray(x.fields);
7725
7759
  * @example f.group({ scope: 'nest', label: 'mod.fields.contact', fields: { name: f.string({}) } })
7726
7760
  */
7727
7761
  function group(o) {
7762
+ if (o["view"] !== void 0) throw new Error("[group] `view` is gone — presentational options live in `ui: {}` (see CLAUDE.md § field author options and the field-types skill).");
7728
7763
  const r = o.rules ?? {};
7729
- const v = o.view ?? {};
7764
+ const v = o.ui ?? {};
7730
7765
  const fields = materializeTree(o.fields);
7731
7766
  const scope = o.scope ?? "hoist";
7732
7767
  if (scope === "nest") {
@@ -7772,7 +7807,7 @@ function row(o) {
7772
7807
  multiple: o.multiple,
7773
7808
  required: o.required,
7774
7809
  rules: o.rules,
7775
- view: { display: "scroll" }
7810
+ ui: { display: "scroll" }
7776
7811
  });
7777
7812
  }
7778
7813
  /**
@@ -7800,7 +7835,7 @@ function sheet(o) {
7800
7835
  label: o.label,
7801
7836
  icon: o.icon,
7802
7837
  fields: rows,
7803
- view: { display: "sheet" }
7838
+ ui: { display: "sheet" }
7804
7839
  });
7805
7840
  }
7806
7841
  /**
@@ -7819,7 +7854,7 @@ function url(o) {
7819
7854
  scope: "nest",
7820
7855
  label: o.label,
7821
7856
  required: o.required,
7822
- view: { kind: "url" },
7857
+ ui: { kind: "url" },
7823
7858
  fields: {
7824
7859
  scheme: string({}),
7825
7860
  username: string({}),
@@ -7961,13 +7996,15 @@ registerType("string", {
7961
7996
  build(o) {
7962
7997
  const cfg = o.config ?? {};
7963
7998
  const min = cfg.min ?? (o.required === true ? 1 : 0);
7964
- let s = string$1(reqTypeErr());
7965
- if (min > 0) s = s.min(min, { message: vmsg("min_length", { min }) });
7966
- if (cfg.max != null) s = s.max(cfg.max, { message: vmsg("max_length", { max: cfg.max }) });
7967
- if (cfg.pattern) s = s.regex(new RegExp(cfg.pattern), { message: vmsg("pattern", cfg.messageKey ? { messageKey: cfg.messageKey } : void 0) });
7968
7999
  return {
7969
8000
  column: "text",
7970
- zod: s,
8001
+ zod: stringContent((str) => {
8002
+ let c = str;
8003
+ if (min > 0) c = c.min(min, { message: vmsg("min_length", { min }) });
8004
+ if (cfg.max != null) c = c.max(cfg.max, { message: vmsg("max_length", { max: cfg.max }) });
8005
+ if (cfg.pattern) c = c.regex(new RegExp(cfg.pattern), { message: vmsg("pattern", cfg.messageKey ? { messageKey: cfg.messageKey } : void 0) });
8006
+ return c;
8007
+ }),
7971
8008
  hints: {
7972
8009
  minLength: cfg.min,
7973
8010
  maxLength: cfg.max
@@ -7988,9 +8025,7 @@ function string(o) {
7988
8025
  }
7989
8026
  /** Shared by localDir/localFile's build(): a server path, min-length-1 when required. */
7990
8027
  function pathZod(o) {
7991
- let s = string$1(reqTypeErr());
7992
- if (o.required === true) s = s.min(1, { message: vmsg("min_length", { min: 1 }) });
7993
- return s;
8028
+ return stringContent((s) => o.required === true ? s.min(1, { message: vmsg("min_length", { min: 1 }) }) : s);
7994
8029
  }
7995
8030
  registerType("localDir", {
7996
8031
  kind: "localDir",
@@ -8652,7 +8687,8 @@ var MEASURED_ROLES = (opts) => {
8652
8687
  label,
8653
8688
  required,
8654
8689
  value,
8655
- view: viewOf(d?.opts),
8690
+ noSearch: d?.opts.noSearch,
8691
+ ui: uiOf(d?.opts),
8656
8692
  options: opts.units,
8657
8693
  strict: true
8658
8694
  });
@@ -9021,7 +9057,7 @@ function periodMkSubDecl(granularity) {
9021
9057
  * (`mkSubDecl`), so it writes its own `normalize` rather than using `role()`. A CORRECTED
9022
9058
  * filling still keeps the composite's own granularity (`canonical.opts`) — the same
9023
9059
  * `date`/`datetime` config the role's own default carries — with the author's
9024
- * label/required/view/value layered on top, exactly like `retypeTo`'s default.
9060
+ * label/required/ui/value layered on top, exactly like `retypeTo`'s default.
9025
9061
  */
9026
9062
  function periodRole(key, label, mkSubDecl) {
9027
9063
  const def = {
@@ -9037,7 +9073,8 @@ function periodRole(key, label, mkSubDecl) {
9037
9073
  label: l ?? canonical.opts.label,
9038
9074
  required,
9039
9075
  value,
9040
- view: viewOf(d.opts)
9076
+ noSearch: d.opts.noSearch,
9077
+ ui: uiOf(d.opts)
9041
9078
  });
9042
9079
  }
9043
9080
  };
@@ -9542,15 +9579,14 @@ function toClient(field) {
9542
9579
  * open: a magnitude may be real/int/rating, a unit may be a select or a relation.
9543
9580
  */
9544
9581
  /**
9545
- * `NormalizedOpts` (what `FieldDecl.opts` actually is) has no single `view` property — the
9546
- * author's `view`/`ui` are flattened into individual fields (`hidden`, `display`, `kind`, …)
9547
- * by `normalizeOpts`. A correction that wants to carry "view" forward has to collect those
9548
- * flat fields back into the nested shape `declare()`'s own `normalizeOpts` call expects.
9582
+ * `NormalizedOpts` (what `FieldDecl.opts` actually is) has no single `ui` property — the
9583
+ * author's `ui` is flattened into individual fields (`hidden`, `display`, `kind`, …)
9584
+ * by `normalizeOpts`. A correction that wants to carry presentation forward has to collect
9585
+ * those flat fields back into the nested shape `declare()`'s own `normalizeOpts` call expects.
9549
9586
  */
9550
- var VIEW_KEYS = [
9587
+ var UI_KEYS = [
9551
9588
  "hidden",
9552
9589
  "noEditControl",
9553
- "noSearch",
9554
9590
  "display",
9555
9591
  "kind",
9556
9592
  "icon",
@@ -9561,11 +9597,11 @@ var VIEW_KEYS = [
9561
9597
  "span"
9562
9598
  ];
9563
9599
  /** The author's presentation opts, re-nested from `opts`'s flat fields — `undefined` when
9564
- * none were set, so a correction never adds an empty `view: {}` no filling ever had. */
9565
- function viewOf(opts) {
9600
+ * none were set, so a correction never adds an empty `ui: {}` no filling ever had. */
9601
+ function uiOf(opts) {
9566
9602
  if (!opts) return void 0;
9567
9603
  const v = {};
9568
- for (const k of VIEW_KEYS) if (opts[k] !== void 0) v[k] = opts[k];
9604
+ for (const k of UI_KEYS) if (opts[k] !== void 0) v[k] = opts[k];
9569
9605
  return Object.keys(v).length ? v : void 0;
9570
9606
  }
9571
9607
  /** A filling built from a ported factory arrives as a FieldDecl — build it into the
@@ -9588,7 +9624,7 @@ function accepts(def, d) {
9588
9624
  }
9589
9625
  /**
9590
9626
  * The correction every role gets for free: keep what the author said ABOUT the field
9591
- * (`label`, `required`, `view`, `value` — a constant or a ComputeFn), replace the type, and
9627
+ * (`label`, `required`, `ui`, `value` — a constant or a ComputeFn), replace the type, and
9592
9628
  * drop what belonged to the OLD type (`rules` — a number's min/max mean nothing to a
9593
9629
  * picture — and `multiple`, because a composite role owns exactly one column). A role whose
9594
9630
  * own default needs more than a bare factory (`measured`'s unit, `period`'s endpoints)
@@ -9601,7 +9637,8 @@ function retypeTo(factory) {
9601
9637
  label,
9602
9638
  required,
9603
9639
  value,
9604
- view: viewOf(d?.opts)
9640
+ noSearch: d?.opts.noSearch,
9641
+ ui: uiOf(d?.opts)
9605
9642
  });
9606
9643
  };
9607
9644
  }
@@ -9784,56 +9821,13 @@ function partColumns(parts) {
9784
9821
  }
9785
9822
  return cols;
9786
9823
  }
9787
- /**
9788
- * A composite's write-time row shape, DERIVED from its resolved parts. `shape` carries
9789
- * the composite's DEFAULT schema per role — the literal the factory writes by hand
9790
- * (`{ value: numSchema, unit: z.string().refine(…) }`) — and each part decides which
9791
- * schema guards its (role-named) key:
9792
- *
9793
- * - a plain STORED part is keyed by its role name, matching the value object and the
9794
- * column `<field>__<role>` that `flattenEmbeddedAt`/`nestEmbeddedAt` read. A role left
9795
- * to its default filling keeps the default schema — byte-identical to the hand-written
9796
- * literal, so every existing structure/unit/range code and message is preserved.
9797
- * - an OVERRIDDEN stored part is validated by its OWN zod (`meta.zod`) instead of the
9798
- * default schema for that role. This is what makes a RETYPED filling real: an
9799
- * `f.int({})` magnitude rejects 5.5 (its own `int` check) and gets an integer column,
9800
- * and an `f.relation({…})` currency accepts a record id instead of being measured
9801
- * against an ISO-4217 string rule it was never meant to satisfy. The filling also
9802
- * brings its own optionality and its own min/max — an author replacing a filling
9803
- * takes over that role's constraints, so `f.real({ rules: { min: 0 } })` is how a
9804
- * replaced magnitude keeps a bound.
9805
- * - a COMPUTED-AND-STORED part ('computedStored') is keyed by its role name like any
9806
- * other part, but its schema is `z.unknown().optional()`: the SERVER owns the value, so
9807
- * whatever the client sends there is ignored — `stripServerOwnedParts` deletes it a
9808
- * moment later and the write path recomputes the column. It is stripped rather than
9809
- * `z.never()`-rejected because a read hands the client that key (it is a real column,
9810
- * present in every response), so rejecting it would break a read → PATCH-the-whole-object
9811
- * round trip.
9812
- * - a CLIENT-OWNED stored part of a composite that is not `required: true` also accepts
9813
- * an explicit `null` (`tolerateNull`), which is what closes the read → write round trip
9814
- * for a PARTIALLY FILLED composite. A SELECT returns every sub-column, so a record whose
9815
- * magnitude column is set and whose unit column is empty reads back as
9816
- * `{value: 900, unit: null}` (`nestEmbeddedAt` drops the key only when EVERY column is
9817
- * empty — `0` is a value), and feeding that exact object back used to fail with
9818
- * `measured_structure`: the write path refused the shape its own read produced. The null
9819
- * slot now parses through and is written back as NULL, so the round trip is closed and
9820
- * clearing ONE role of a filled composite works instead of erroring.
9821
- * NULL, not absence: a read emits every stored role (empty ones as `null`), so `null` is
9822
- * how "this record has no value there" arrives, while an ABSENT key is a writer that
9823
- * never mentioned a role it owns — which stays the structure error it has always been
9824
- * (`{unit: 'kg'}` with no magnitude, `{lat: 50}` with no longitude). That is the
9825
- * all-or-nothing rule for a DECLARATION, and it is untouched.
9826
- * A `required: true` composite keeps every client-owned slot MANDATORY, null included —
9827
- * "tolerate null" must not turn a required field optional, so an empty or half-empty
9828
- * value still fails with the same structure code it fails with today. A filling the
9829
- * author declared `required` keeps its own mandate too.
9830
- */
9831
- function partsRowShape(shape, parts, tolerateNull = false) {
9824
+ function partsRowShape(shape, parts, tolerance = "strict") {
9832
9825
  const out = { ...shape };
9833
9826
  for (const p of parts) if (p.mode === "computedStored") out[p.key] = unknown().optional();
9834
9827
  else {
9835
9828
  const own = p.overridden ? p.meta.zod : shape[p.role] ?? unknown().optional();
9836
- out[p.key] = tolerateNull && !(p.overridden && p.meta.required === true) ? own.nullable() : own;
9829
+ const relaxed = tolerance !== "strict" && !(p.overridden && p.meta.required === true);
9830
+ out[p.key] = relaxed ? tolerance === "null-or-missing" ? own.nullish() : own.nullable() : own;
9837
9831
  }
9838
9832
  return out;
9839
9833
  }
@@ -9874,16 +9868,17 @@ function stripServerOwnedParts(value, parts) {
9874
9868
  function buildComposite(def, opts, parts) {
9875
9869
  const required = opts.required ?? false;
9876
9870
  const roleZod = def.roleZod(opts, parts);
9877
- const rowSchema = object(partsRowShape(roleZod, parts, required !== true));
9878
- const absentRowSchema = required === true ? object(partsRowShape(roleZod, parts, true)) : rowSchema;
9871
+ const rowSchema = object(partsRowShape(roleZod, parts, required === true ? "strict" : "null"));
9872
+ const absentRowSchema = required === true ? object(partsRowShape(roleZod, parts, "null-or-missing")) : rowSchema;
9879
9873
  const roleKeys = (which) => parts.filter((p) => p.mode === "stored" && (which?.(p) ?? true)).map(partValueKey);
9880
9874
  const absenceKeys = def.absenceRoles ? roleKeys((p) => def.absenceRoles.includes(p.role)) : void 0;
9881
9875
  const clientKeys = def.absenceRoles ? roleKeys() : void 0;
9882
9876
  const zod = unknown().transform((raw, ctx) => {
9883
9877
  const parsed = jsonValue(raw);
9884
9878
  const p = parsed;
9879
+ const isRow = p == null || typeof p === "object" && !Array.isArray(p);
9885
9880
  /** Every one of `keys` empty in this value — `null`/absent alike. */
9886
- const allEmpty = (keys) => p == null || typeof p === "object" && keys.length > 0 && keys.every((k) => p[k] == null);
9881
+ const allEmpty = (keys) => isRow && (p == null || keys.length > 0 && keys.every((k) => p[k] == null));
9887
9882
  let absent = false;
9888
9883
  if (absenceKeys && clientKeys) {
9889
9884
  absent = allEmpty(absenceKeys);
@@ -10132,7 +10127,7 @@ var src_default = definePlugin({
10132
10127
  label: "webchat.settings.reasoning_display",
10133
10128
  default: "live",
10134
10129
  strict: true,
10135
- view: { noSearch: true },
10130
+ noSearch: true,
10136
10131
  options: [{
10137
10132
  value: "live",
10138
10133
  title: "webchat.settings.reasoning_display_live"
package/dist/web.js CHANGED
@@ -11,7 +11,7 @@ import { definePluginUI } from "@coffer-org/web-ui";
11
11
  var ui_default = definePluginUI({ slots: [{
12
12
  slot: "overlay",
13
13
  id: "webchat",
14
- load: () => import("./ChatWidget-Bs8zrQVZ.js")
14
+ load: () => import("./ChatWidget-uAyA3y0W.js")
15
15
  }] });
16
16
  //#endregion
17
17
  export { ui_default as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffer-org/plugin-webchat",
3
- "version": "4.0.0",
3
+ "version": "5.0.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"
@@ -27,8 +27,8 @@
27
27
  "test:ui": "vitest run --root ."
28
28
  },
29
29
  "dependencies": {
30
- "@coffer-org/sdk": "^4.0.0",
31
- "@coffer-org/server": "^4.0.0",
30
+ "@coffer-org/sdk": "^5.0.0",
31
+ "@coffer-org/server": "^5.0.0",
32
32
  "lucide-react": "^1.17.0",
33
33
  "react-markdown": "^10.1.0",
34
34
  "remark-gfm": "^4.0.1"