@coffer-org/plugin-webchat 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6040,7 +6040,7 @@ var getDefaultConfig = () => {
6040
6040
  var twMerge = /*#__PURE__*/ createTailwindMerge(getDefaultConfig);
6041
6041
  //#endregion
6042
6042
  //#region ../web/lib/utils.ts
6043
- /** Об'єднує класи, розв'язуючи конфлікти Tailwind (shadcn-конвенція). */
6043
+ /** Merge classes while resolving Tailwind conflicts (shadcn convention). */
6044
6044
  function cn(...inputs) {
6045
6045
  return twMerge(clsx(inputs));
6046
6046
  }
@@ -6054,7 +6054,7 @@ var DialogOverlay = React.forwardRef(({ className, ...props }, ref) => /* @__PUR
6054
6054
  ...props
6055
6055
  }));
6056
6056
  DialogOverlay.displayName = DialogOverlay$1.displayName;
6057
- /** Респонсивний контент: bottom-sheet на мобільному, центрований modal на desktop. */
6057
+ /** Responsive content: bottom sheet on mobile, centered modal on desktop. */
6058
6058
  var DialogContent = React.forwardRef(({ className, children, hideClose, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(DialogContent$1, {
6059
6059
  ref,
6060
6060
  className: cn("fixed z-50 flex flex-col border border-border bg-surface text-text shadow-xl", "inset-x-0 bottom-0 max-h-[88dvh] overflow-y-auto rounded-t-2xl", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom data-[state=closed]:duration-200", "md:inset-x-auto md:bottom-auto md:left-1/2 md:top-1/2 md:max-h-[85dvh] md:w-full md:max-w-xl md:-translate-x-1/2 md:-translate-y-1/2 md:rounded-2xl", "md:data-[state=open]:slide-in-from-bottom-0 md:data-[state=closed]:slide-out-to-bottom-0 md:data-[state=open]:zoom-in-95 md:data-[state=closed]:zoom-out-95", className),
@@ -6083,14 +6083,13 @@ DialogDescription.displayName = DialogDescription$1.displayName;
6083
6083
  //#endregion
6084
6084
  //#region ../web/hooks/use-media-query.ts
6085
6085
  /**
6086
- * Стан media-запиту як реактивне значення.
6086
+ * Media-query state as a reactive value.
6087
6087
  *
6088
- * Потрібен там, де CSS-приховування (`hidden list:block`) недостатньо: приховані
6089
- * елементи все одно в DOM і все одно рендеряться. Для списку на 700 записів це
6090
- * подвійна робота — таблиця і картки одночасно.
6088
+ * Needed where CSS hiding (`hidden list:block`) is insufficient: hidden elements remain in the
6089
+ * DOM and still render. For a list of 700 records, that means doing the work twice — table and cards.
6091
6090
  *
6092
- * `fallback` відповідь, коли `matchMedia` недоступний (jsdom у тестах): за
6093
- * замовчуванням «широкий екран», щоб тести бачили десктопну розмітку.
6091
+ * `fallback` is the answer when `matchMedia` is unavailable (jsdom tests): default to a
6092
+ * “wide screen” so tests see the desktop layout.
6094
6093
  */
6095
6094
  function useMediaQuery(query, fallback = true) {
6096
6095
  return useSyncExternalStore(useCallback((onChange) => {
@@ -6104,8 +6103,8 @@ function useMediaQuery(query, fallback = true) {
6104
6103
  }, [query, fallback]), () => fallback);
6105
6104
  }
6106
6105
  /**
6107
- * Значення брейкпоінта з `@theme` (globals.css) щоб JS і CSS не розʼїхались.
6108
- * Tailwind v4 публікує їх як CSS-змінні на :root. Без CSS (тести) `fallback`.
6106
+ * Breakpoint value from `@theme` (globals.css), so JS and CSS do not drift.
6107
+ * Tailwind v4 publishes them as CSS variables on :root. Without CSS (tests), use `fallback`.
6109
6108
  */
6110
6109
  function breakpointPx(name, fallback) {
6111
6110
  if (typeof window === "undefined" || typeof getComputedStyle !== "function") return fallback;
@@ -6113,7 +6112,7 @@ function breakpointPx(name, fallback) {
6113
6112
  const n = Number.parseInt(raw.trim(), 10);
6114
6113
  return Number.isFinite(n) && n > 0 ? n : fallback;
6115
6114
  }
6116
- /** `(min-width: …)` для брейкпоінта з теми. */
6115
+ /** `(min-width: …)` for a theme breakpoint. */
6117
6116
  function useMinWidth(name, fallback) {
6118
6117
  return useMediaQuery(`(min-width: ${breakpointPx(name, fallback)}px)`, true);
6119
6118
  }
@@ -6147,14 +6146,13 @@ async function* readSse(body) {
6147
6146
  }
6148
6147
  //#endregion
6149
6148
  //#region src/ui/chat-api.ts
6150
- /** Транспорт веб-чату. Куки сесії їдуть автоматично (same-origin), тому окремої
6151
- * авторизації тут немає гейт стоїть на сервері. */
6149
+ /** Web-chat transport. Session cookies are sent automatically (same-origin), so there is no
6150
+ * separate authorization here; the gate is on the server. */
6152
6151
  var BASE = "/api/plugins/webchat/user";
6153
6152
  /**
6154
- * Кидає на будь-якій відмові, зокрема 404 «плагін вимкнули вже ПІСЛЯ того, як
6155
- * бандл віджета завантажився». Окремого `null` для цього випадку немає навмисно:
6156
- * викликач однаково лишає поточний список розмов, тож два коди повернення
6157
- * означали б рівно одне.
6153
+ * Throws on any rejection, including 404 (“plugin was disabled AFTER the widget bundle
6154
+ * loaded”). There is deliberately no separate `null` for this case: the caller keeps
6155
+ * the current thread list either way, so two return codes would mean the same thing.
6158
6156
  */
6159
6157
  async function fetchThreads() {
6160
6158
  const r = await fetch(`${BASE}/threads`, {
@@ -6358,8 +6356,8 @@ function useChat() {
6358
6356
  }
6359
6357
  //#endregion
6360
6358
  //#region src/ui/use-page-context.ts
6361
- /** Контекст поточної сторінки для агента. Роути SPA: `/:library/:type[/:id]`;
6362
- * системні гілки (settings, browse) віддаємо лише шляхом. */
6359
+ /** Current-page context for the agent. SPA routes: `/:library/:type[/:id]`;
6360
+ * system branches (settings, browse) are returned by path only. */
6363
6361
  function usePageContext() {
6364
6362
  const { pathname } = useLocation();
6365
6363
  const parts = pathname.split("/").filter(Boolean);
@@ -29005,9 +29003,8 @@ function remarkGfm(options) {
29005
29003
  //#endregion
29006
29004
  //#region src/ui/internal-link.ts
29007
29005
  var SEGMENT = /^[A-Za-z0-9._~%-]+$/;
29008
- /** Повертає захоплені значення ':param' при збігу, або null. '*' легальний
29009
- * лише останнім сегментом шаблону тому ця гілка може повернутись одразу,
29010
- * не дочитуючи got до кінця. */
29006
+ /** Returns captured ':param' values on a match, or null. '*' is legal only as the
29007
+ * last pattern segment, so this branch may return immediately without reading got to the end. */
29011
29008
  function matchesPattern(pathname, pattern) {
29012
29009
  const got = pathname.split("/").filter(Boolean);
29013
29010
  const want = pattern.split("/").filter(Boolean);
@@ -29053,9 +29050,9 @@ function classifyHref(href, origin, routes, pairs = []) {
29053
29050
  }
29054
29051
  //#endregion
29055
29052
  //#region src/ui/chat-nav.tsx
29056
- /** Що робити з віджетом, коли користувач пішов по лінку з відповіді.
29057
- * Колбек дає ChatWidget (він володіє mode/open) щоб не тягнути проп
29058
- * через ChatMessages і Bubble. */
29053
+ /** What to do with the widget when the user follows a link from a response.
29054
+ * ChatWidget supplies the callback (it owns mode/open), avoiding a prop
29055
+ * threaded through ChatMessages and Bubble. */
29059
29056
  var ChatNavContext = createContext(() => {});
29060
29057
  function ChatNavProvider({ onNavigate, children }) {
29061
29058
  return /* @__PURE__ */ jsx(ChatNavContext.Provider, {
@@ -29119,18 +29116,17 @@ function ChatMessages({ messages, pending, pendingReasoning }) {
29119
29116
  }, m.msgId)), pending !== null && /* @__PURE__ */ jsx(Bubble, {
29120
29117
  role: "assistant",
29121
29118
  text: pending || t("webchat.thinking"),
29122
- reasoning: pendingReasoning,
29123
- live: true
29119
+ reasoning: pendingReasoning
29124
29120
  })]
29125
29121
  });
29126
29122
  }
29127
29123
  /**
29128
- * memo: під час стріму батько ререндериться на КОЖНУ дельту (кілька разів на
29129
- * секунду). Без memo кожен такт заново рендерив би (і, отже, заново парсив
29130
- * markdown) усіх уже готових повідомлень, поки жива лише одна бульбашка знизу.
29131
- * Props тут примітиви, тож дефолтного поверхневого порівняння досить.
29124
+ * memo: during streaming, the parent re-renders on EVERY delta (several times per
29125
+ * second). Without memo, each tick would re-render (and therefore re-parse markdown)
29126
+ * for every completed message while only one bubble at the bottom is changing.
29127
+ * The props are primitives, so the default shallow comparison is sufficient.
29132
29128
  */
29133
- var Bubble = memo(function Bubble({ role, text, reasoning, live }) {
29129
+ var Bubble = memo(function Bubble({ role, text, reasoning }) {
29134
29130
  const { t } = useTranslation();
29135
29131
  if (role === "user") return /* @__PURE__ */ jsx("div", {
29136
29132
  className: "self-end max-w-[85%] rounded-2xl rounded-br-sm bg-accent/15 px-3 py-2 text-sm whitespace-pre-wrap break-words",
@@ -29139,7 +29135,6 @@ var Bubble = memo(function Bubble({ role, text, reasoning, live }) {
29139
29135
  return /* @__PURE__ */ jsxs("div", {
29140
29136
  className: "self-start max-w-[95%] min-w-0 rounded-2xl rounded-bl-sm bg-elevated px-3 py-2 text-sm break-words",
29141
29137
  children: [reasoning ? /* @__PURE__ */ jsxs("details", {
29142
- open: live,
29143
29138
  className: "mb-2 rounded-lg border border-border px-2 py-1",
29144
29139
  children: [/* @__PURE__ */ jsx("summary", {
29145
29140
  className: "cursor-pointer select-none text-xs text-muted",
@@ -29217,9 +29212,9 @@ function ChatThreadList({ threads, activeId, onOpen, onNew, className = "w-[220p
29217
29212
  }
29218
29213
  //#endregion
29219
29214
  //#region src/ui/ChatWidget.tsx
29220
- /** Що робити з віджетом після переходу по лінку з відповіді. На desktop панель
29221
- * 380px не заважає читати сторінку (а модаль із затемненням заважає, тож
29222
- * згортається в неї); нижче nav-брейкпоінта панель з'їдає майже екран. */
29215
+ /** What to do with the widget after following a link from a response. On desktop a 380px
29216
+ * panel does not obstruct the page (a darkened modal does, so it collapses into it);
29217
+ * below the nav breakpoint the panel consumes nearly the whole screen. */
29223
29218
  function nextModeAfterNav(wideNav) {
29224
29219
  return wideNav ? "panel" : "bubble";
29225
29220
  }
package/dist/schema.js CHANGED
@@ -4391,11 +4391,11 @@ function resolveUnits(u) {
4391
4391
  //#endregion
4392
4392
  //#region ../sdk/src/currencies.ts
4393
4393
  /**
4394
- * Валюти з вбудованого `Intl` (ISO 4217) БЕЗ hand-list і без npm-залежності.
4395
- * Коди `Intl.supportedValuesOf('currency')`; символи/назви локалізовані через
4394
+ * Currencies from built-in `Intl` (ISO 4217), with NO hand list or npm dependency.
4395
+ * Codes come from `Intl.supportedValuesOf('currency')`; symbols/names are localized through
4396
4396
  * `Intl.NumberFormat`/`Intl.DisplayNames`.
4397
4397
  */
4398
- /** Усі ISO 4217 коди. Fallback кілька основних (на випадок старого рантайму). */
4398
+ /** All ISO 4217 codes. Fallback: several major codes for older runtimes. */
4399
4399
  var CURRENCY_CODES = (() => {
4400
4400
  try {
4401
4401
  return Intl.supportedValuesOf("currency");
@@ -4410,26 +4410,26 @@ var CURRENCY_CODES = (() => {
4410
4410
  }
4411
4411
  })();
4412
4412
  var CODE_SET = new Set(CURRENCY_CODES);
4413
- /** Чи валідний ISO 4217 код. */
4413
+ /** Whether an ISO 4217 code is valid. */
4414
4414
  var isCurrencyCode = (c) => CODE_SET.has(c);
4415
4415
  //#endregion
4416
4416
  //#region ../sdk/src/fields/validation.ts
4417
- /** Структуроване повідомлення для zod: JSON {code, params}. Декодує mutate.ts. */
4417
+ /** Structured message for zod: JSON {code, params}. Decoded by mutate.ts. */
4418
4418
  function vmsg(code, params) {
4419
4419
  return JSON.stringify(params ? {
4420
4420
  code,
4421
4421
  params
4422
4422
  } : { code });
4423
4423
  }
4424
- /** v4 error-map: повідомлення для відсутнього значення (колишній required_error). */
4424
+ /** v4 error-map: message for a missing value (formerly required_error). */
4425
4425
  function reqErr(code = "required") {
4426
4426
  return { error: (iss) => iss.input === void 0 ? vmsg(code) : void 0 };
4427
4427
  }
4428
- /** v4 error-map: повідомлення для невалідного типу (колишній invalid_type_error). */
4428
+ /** v4 error-map: message for an invalid type (formerly invalid_type_error). */
4429
4429
  function typeErr(code = "invalid_type") {
4430
4430
  return { error: (iss) => iss.code === "invalid_type" ? vmsg(code) : void 0 };
4431
4431
  }
4432
- /** v4 error-map: required + invalid_type разом (колишні required_error + invalid_type_error). */
4432
+ /** v4 error-map: required + invalid_type together (formerly required_error + invalid_type_error). */
4433
4433
  function reqTypeErr() {
4434
4434
  return { error: (iss) => iss.code === "invalid_type" ? iss.input === void 0 ? vmsg("required") : vmsg("invalid_type") : void 0 };
4435
4435
  }
@@ -4447,10 +4447,10 @@ function jsonValue(raw) {
4447
4447
  return raw;
4448
4448
  }
4449
4449
  /**
4450
- * Фабрика для полів, що зберігають JSON і валідуються вкладеною zod-схемою.
4451
- * Приймає нативне object/array (native form-state) АБО JSON-рядок (legacy).
4452
- * Single-parse через jsonValue → inner.safeParse → issue `code`; рядок, що не
4453
- * розпарсився, лишається рядкомкод 'json'.
4450
+ * Factory for fields that store JSON and validate it with a nested zod schema.
4451
+ * Accepts a native object/array (native form state) OR a JSON string (legacy).
4452
+ * Single parse through jsonValue → inner.safeParse → issue `code`; an unparseable
4453
+ * string remains a string code 'json'.
4454
4454
  */
4455
4455
  function jsonRefined(inner, code) {
4456
4456
  return unknown().superRefine((raw, ctx) => {
@@ -4522,16 +4522,16 @@ function normalizeOpts(rawIn) {
4522
4522
  //#endregion
4523
4523
  //#region ../sdk/src/field-presets.ts
4524
4524
  /**
4525
- * Пресети полів тонкі обгортки над базовими примітивами з fields.ts.
4525
+ * Field presets are thin wrappers around the primitives in fields.ts.
4526
4526
  *
4527
- * Кожен пресет = один kind (один віджет). Пресети не приймають `format` —
4528
- * вони самі є семантичними типами. min/max/step через `config`.
4527
+ * Each preset = one kind (one widget). Presets do not accept `format`;
4528
+ * they are semantic types themselves. min/max/step go through `config`.
4529
4529
  *
4530
- * Пресети додаються до `f` через `composeF`, який гарантує, що жоден пресет
4531
- * не перебиває примітив.
4530
+ * Presets are added to `f` through `composeF`, which guarantees no preset
4531
+ * overrides a primitive.
4532
4532
  *
4533
- * Циклічний імпорт з fields.ts безпечний: фабрики/хелпери hoisted-декларації,
4534
- * а пресети викликають їх лише в тілі своїх функцій.
4533
+ * The cyclic import from fields.ts is safe: factories/helpers are hoisted declarations,
4534
+ * and presets call them only inside their function bodies.
4535
4535
  */
4536
4536
  function email(raw) {
4537
4537
  const o = normalizeOpts(raw);
@@ -4705,9 +4705,9 @@ function link(raw) {
4705
4705
  }, o.multiple ?? false));
4706
4706
  }
4707
4707
  var TEL_RE = /^\+?[\d\s()-]{4,}$/;
4708
- /** Loose URL: будь-який scheme:// АБО host-з-крапкою (+опц. порт/шлях). Без пробілів. */
4708
+ /** Loose URL: any scheme:// OR dotted host (optional port/path). No spaces. */
4709
4709
  var LINK_RE = /^([a-z][a-z0-9+.-]*:\/\/\S+|[\w-]+(\.[\w-]+)+(:\d+)?(\/\S*)?)$/i;
4710
- /** CSS named colors (CSS Color Module L4) для f.colorname. */
4710
+ /** CSS named colors (CSS Color Module L4) for f.colorname. */
4711
4711
  var CSS_COLOR_NAMES = /* @__PURE__ */ new Set([
4712
4712
  "aliceblue",
4713
4713
  "antiquewhite",
@@ -4966,7 +4966,7 @@ function reminder(raw) {
4966
4966
  }
4967
4967
  var _real = real;
4968
4968
  var _int = int;
4969
- /** Відсоток 0..100 — real з rules:{min:0,max:100}. */
4969
+ /** Percentage 0..100 — real with rules:{min:0,max:100}. */
4970
4970
  function percent(o) {
4971
4971
  return _real({
4972
4972
  ...o,
@@ -4977,7 +4977,7 @@ function percent(o) {
4977
4977
  }
4978
4978
  });
4979
4979
  }
4980
- /** Рік — int з rules:{min:1900,max:2100}; межі можна перекрити через rules.min/max. */
4980
+ /** Year — int with rules:{min:1900,max:2100}; bounds can be overridden via rules.min/max. */
4981
4981
  function year(o) {
4982
4982
  return _int({
4983
4983
  ...o,
@@ -6027,13 +6027,6 @@ function wrapKey(opts, meta) {
6027
6027
  role: opts.role
6028
6028
  }
6029
6029
  };
6030
- if (opts.pinned) m = {
6031
- ...m,
6032
- hints: {
6033
- ...m.hints,
6034
- pinned: true
6035
- }
6036
- };
6037
6030
  if (opts.default !== void 0) m = {
6038
6031
  ...m,
6039
6032
  default: opts.default
package/dist/web.js CHANGED
@@ -11,7 +11,7 @@ import { definePluginUI } from "@coffer-org/web-sdk";
11
11
  var ui_default = definePluginUI({ slots: [{
12
12
  slot: "overlay",
13
13
  id: "webchat",
14
- load: () => import("./ChatWidget-B5heKPXv.js")
14
+ load: () => import("./ChatWidget-U4vyG1gt.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": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"
@@ -26,9 +26,9 @@
26
26
  "test:ui": "vitest run --root ."
27
27
  },
28
28
  "dependencies": {
29
- "@coffer-org/plugin-orchestrator": "^2.0.0",
30
- "@coffer-org/sdk": "^2.0.0",
31
- "@coffer-org/server": "^2.0.1",
29
+ "@coffer-org/plugin-orchestrator": "^2.1.1",
30
+ "@coffer-org/sdk": "^2.1.1",
31
+ "@coffer-org/server": "^2.2.2",
32
32
  "react-markdown": "^10.1.0",
33
33
  "remark-gfm": "^4.0.1"
34
34
  },