@ai-matrx/records-ui 0.115.39 → 0.115.40

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.cjs CHANGED
@@ -6029,9 +6029,12 @@ function withheldNoticeOf(hidden, key) {
6029
6029
  return notice && typeof notice === "object" ? notice : null;
6030
6030
  }
6031
6031
  function withheldSentence(field2, notice) {
6032
- const says = notice.says;
6033
- if (typeof says === "string" && says.trim() !== "") return says.trim();
6032
+ const extra = notice;
6033
+ if (typeof extra.says === "string" && extra.says.trim() !== "") return extra.says.trim();
6034
6034
  const name = fieldName(field2);
6035
+ if (typeof extra.part === "string" && extra.part.trim() !== "" && typeof extra.who === "string" && extra.who.trim() !== "") {
6036
+ return `${name} is in ${extra.part.trim()} \u2014 ${extra.who.trim()} see it.`;
6037
+ }
6035
6038
  const reason = notice.reason ? ` It is ${notice.reason}` : "";
6036
6039
  const needs = notice.needs ? `${reason ? "," : " It"} and reading it needs ${notice.needs}` : "";
6037
6040
  const or = notice.or ? `${needs || reason ? ", or" : " It needs"} ${notice.or}` : "";
@@ -17000,6 +17003,7 @@ function useMergedGridControls(args) {
17000
17003
  } = args;
17001
17004
  const client = (0, import_react101.useRecordsClient)();
17002
17005
  const linkRecord = useRecordsUi().linkRecord;
17006
+ const columnAccess = useRecordsUi().columnAccess;
17003
17007
  const hrefForRecord = useRecordsUi().hrefForRecord;
17004
17008
  const tableVersion = (0, recordVersions_exports.useTableVersion)(client, tableId, table);
17005
17009
  const mutation = (0, import_react101.useFieldMutation)();
@@ -17181,6 +17185,14 @@ function useMergedGridControls(args) {
17181
17185
  writeLook({ ...rest, ...next.size > 0 ? { wrapColumns: [...next] } : {} });
17182
17186
  }
17183
17187
  });
17188
+ if (columnAccess) {
17189
+ out.push({
17190
+ id: "col-access",
17191
+ label: "Who can see and edit\u2026",
17192
+ disabledReason: rights.structure ? void 0 : rights.why("structure"),
17193
+ onSelect: () => setDialog({ kind: "access", field: field2 })
17194
+ });
17195
+ }
17184
17196
  out.push({
17185
17197
  id: "col-duplicate",
17186
17198
  label: "Duplicate property",
@@ -17462,6 +17474,7 @@ function useMergedGridControls(args) {
17462
17474
  }
17463
17475
  };
17464
17476
  }, [
17477
+ columnAccess,
17465
17478
  args,
17466
17479
  colours,
17467
17480
  fieldKeys,
@@ -17512,6 +17525,13 @@ function useMergedGridControls(args) {
17512
17525
  const close = () => setDialog(null);
17513
17526
  const dialogs = !enabled ? null : /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(import_jsx_runtime63.Fragment, { children: [
17514
17527
  colours.dialogs,
17528
+ dialog?.kind === "access" && columnAccess ? columnAccess({
17529
+ tableId,
17530
+ fieldId: dialog.field.id,
17531
+ name: fieldName(dialog.field),
17532
+ onClose: () => setDialog(null),
17533
+ onChanged: () => (0, import_react101.tableShapeChanged)(tableId)
17534
+ }) : null,
17515
17535
  /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(RowActionsDialog, { tableId, open: rowActionsOpen, onOpenChange: setRowActionsOpen }),
17516
17536
  /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
17517
17537
  RowLabelPicker,
@@ -20062,10 +20082,30 @@ function columnForField(field2, editing, mayWriteRow, whyNotRow, look, onAskWhoC
20062
20082
  children: "\u27F3"
20063
20083
  }
20064
20084
  ) : null;
20065
- const withSynced = (header) => syncedMark ? /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
20066
- syncedMark,
20067
- header
20068
- ] }) : header;
20085
+ const restricted = field2.sensitivity === "confidential" || field2.sensitivity === "restricted";
20086
+ const lockMark = restricted ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
20087
+ "span",
20088
+ {
20089
+ "data-records-restricted": field2.key,
20090
+ title: "Fewer people see this column",
20091
+ "aria-label": "Fewer people see this column",
20092
+ className: "inline-flex text-muted-foreground",
20093
+ children: /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("svg", { viewBox: "0 0 24 24", width: "11", height: "11", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
20094
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("rect", { x: "4", y: "11", width: "16", height: "10", rx: "2" }),
20095
+ /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("path", { d: "M8 11V7a4 4 0 0 1 8 0v4" })
20096
+ ] })
20097
+ }
20098
+ ) : null;
20099
+ const withSynced = (inner2) => {
20100
+ const header = lockMark ? /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
20101
+ inner2,
20102
+ lockMark
20103
+ ] }) : inner2;
20104
+ return syncedMark ? /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
20105
+ syncedMark,
20106
+ header
20107
+ ] }) : header;
20108
+ };
20069
20109
  return {
20070
20110
  id: field2.key,
20071
20111
  /*
@@ -21297,7 +21337,7 @@ function columnsSentence(mapped, total, unmapped) {
21297
21337
  if (unmapped === "ignore") return `${mapped} of ${total} columns go into this table \xB7 ${rest} left out`;
21298
21338
  return `${mapped} of ${total} go in now \xB7 ${rest} wait for approval with their values`;
21299
21339
  }
21300
- function ImportWizard({ tableId, source, onDone, onWrote, className }) {
21340
+ function ImportWizard({ tableId, source, onDone, onWrote, autoRun, className }) {
21301
21341
  const client = (0, import_react120.useRecordsClient)();
21302
21342
  const table = (0, import_react120.useTable)(tableId);
21303
21343
  const fields = (0, import_react120.useFields)(tableId);
@@ -21919,6 +21959,13 @@ function ImportWizard({ tableId, source, onDone, onWrote, className }) {
21919
21959
  const refusedByRules = ruleVerdicts.reduce((n, v) => n + v.refused, 0);
21920
21960
  const [goAheadAnyway, setGoAheadAnyway] = (0, import_react119.useState)(false);
21921
21961
  (0, import_react119.useEffect)(() => setGoAheadAnyway(false), [ruleVerdicts]);
21962
+ const [autoRan, setAutoRan] = (0, import_react119.useState)(false);
21963
+ (0, import_react119.useEffect)(() => {
21964
+ if (!autoRun || autoRan || !source || phase !== "ready" || !plan) return;
21965
+ if (busy || !rights.write || rows.length === 0 || titleMissing || ruleVerdicts.length > 0) return;
21966
+ setAutoRan(true);
21967
+ void run();
21968
+ }, [autoRun, autoRan, source, phase, plan, busy, rights.write, rows.length, titleMissing, ruleVerdicts, run]);
21922
21969
  const afterWrite = !busy && (phase === "done" || alreadyRan !== null || ledger !== null && ledger.seen > 0);
21923
21970
  return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: (0, import_design_system64.cn)("flex min-w-0 flex-col gap-2 text-xs", className), children: [
21924
21971
  /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: "flex items-center gap-2", children: [
@@ -28472,7 +28519,7 @@ function NewTableStart({
28472
28519
  /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(import_design_system74.Button, { disabled: !importReady || busy, onClick: () => void continueImport(), "data-new-table-continue": "", children: busy ? "Creating\u2026" : "Next: columns" })
28473
28520
  ] });
28474
28521
  } else {
28475
- body = /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { "data-new-table-wizard": "", children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(ImportWizard, { tableId: step2.tableId, source: step2.source, onDone: () => onOpenTable?.(step2.tableId) }) });
28522
+ body = /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { "data-new-table-wizard": "", children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(ImportWizard, { tableId: step2.tableId, source: step2.source, autoRun: step2.made, onDone: () => onOpenTable?.(step2.tableId) }) });
28476
28523
  footer = /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(import_jsx_runtime89.Fragment, { children: [
28477
28524
  !step2.made ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(import_design_system74.Button, { variant: "ghost", className: "mr-auto", onClick: () => setStep({ at: "import" }), children: "Back" }) : null,
28478
28525
  /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(import_design_system74.Button, { variant: "outline", onClick: () => onOpenTable?.(step2.tableId), "data-new-table-open": "", children: "Open the table" })
@@ -35236,6 +35283,7 @@ function TimelineView({
35236
35283
  const [dragging, setDragging] = (0, import_react207.useState)(null);
35237
35284
  const [linking, setLinking] = (0, import_react207.useState)(null);
35238
35285
  const [ganttBusy, setGanttBusy] = (0, import_react207.useState)(false);
35286
+ const [linkTarget, setLinkTarget] = (0, import_react207.useState)(null);
35239
35287
  const { placed, unscheduled } = (0, import_react207.useMemo)(() => {
35240
35288
  const out = [];
35241
35289
  const without = [];
@@ -35404,6 +35452,18 @@ function TimelineView({
35404
35452
  label: `${titleOf(target)} now waits on ${titleOf(source)}`
35405
35453
  });
35406
35454
  };
35455
+ const taskRowAt = (x, y) => {
35456
+ const canvas = scroller.current?.querySelector("[data-timeline-canvas]");
35457
+ const body2 = scroller.current;
35458
+ if (!canvas || !body2) return null;
35459
+ const box = canvas.getBoundingClientRect();
35460
+ const frame2 = body2.getBoundingClientRect();
35461
+ if (frame2.width === 0 || x < frame2.left || x > frame2.right) return null;
35462
+ const index = Math.floor((y - box.top - LANE_PAD / 2) / ROW_PX);
35463
+ if (index < 0) return null;
35464
+ const lane = laneRows[0];
35465
+ return lane?.packed.find((b) => b.row === index)?.p.row.id ?? null;
35466
+ };
35407
35467
  const beginLink = (p) => (e) => {
35408
35468
  if (e.button !== void 0 && e.button !== 0) return;
35409
35469
  e.stopPropagation();
@@ -35412,13 +35472,21 @@ function TimelineView({
35412
35472
  const x1 = box.left + box.width / 2;
35413
35473
  const y1 = box.top + box.height / 2;
35414
35474
  setLinking({ id: p.row.id, x1, y1, x2: e.clientX, y2: e.clientY });
35415
- const onMove = (ev) => setLinking((l) => l ? { ...l, x2: ev.clientX, y2: ev.clientY } : l);
35475
+ const onMove = (ev) => {
35476
+ const x = ev.clientX;
35477
+ const y = ev.clientY;
35478
+ setLinking((l) => l ? { ...l, x2: x, y2: y } : l);
35479
+ const over = taskRowAt(x, y);
35480
+ setLinkTarget(over && over !== p.row.id ? over : null);
35481
+ };
35416
35482
  const onUp = (ev) => {
35417
35483
  window.removeEventListener("pointermove", onMove);
35418
35484
  window.removeEventListener("pointerup", onUp);
35419
35485
  setLinking(null);
35486
+ setLinkTarget(null);
35487
+ const at = ev;
35420
35488
  const hit = ev.target?.closest?.("[data-timeline-bar]");
35421
- const id = hit?.getAttribute("data-timeline-bar");
35489
+ const id = taskRowAt(at.clientX, at.clientY) ?? hit?.getAttribute("data-timeline-bar");
35422
35490
  if (id && id !== p.row.id) connect(p.row.id, id);
35423
35491
  };
35424
35492
  window.addEventListener("pointermove", onMove);
@@ -35649,6 +35717,7 @@ function TimelineView({
35649
35717
  "data-timeline-span": `${first}..${last}`,
35650
35718
  ...p.marker ? { "data-timeline-marker": "" } : {},
35651
35719
  ...milestone ? { "data-timeline-milestone": "" } : {},
35720
+ ...linkTarget === p.row.id ? { "data-timeline-drop-target": "" } : {},
35652
35721
  ...onCritical ? { "data-timeline-critical": "" } : {},
35653
35722
  ...slip !== null ? { "data-timeline-slip": slip } : {},
35654
35723
  ...may ? {} : { "data-timeline-locked": "" },
@@ -35662,6 +35731,7 @@ function TimelineView({
35662
35731
  outside ? "overflow-visible" : "overflow-hidden",
35663
35732
  milestone ? "border-transparent bg-transparent" : color ? import_data_table22.CHOICE_CHIP_CLASS[color] : "bg-muted",
35664
35733
  onCritical ? "ring-2 ring-amber-500" : void 0,
35734
+ linkTarget === p.row.id ? "ring-2 ring-primary" : void 0,
35665
35735
  may ? "cursor-grab active:cursor-grabbing" : "cursor-pointer",
35666
35736
  dragging?.id === p.row.id ? "z-20 shadow-md" : "z-10",
35667
35737
  refused ? "ring-2 ring-destructive" : void 0
@@ -35743,7 +35813,7 @@ function TimelineView({
35743
35813
  /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: [
35744
35814
  settings,
35745
35815
  /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(import_design_system102.DropdownMenu, { children: [
35746
- /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_design_system102.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_design_system102.Button, { size: "sm", variant: ganttOn ? "secondary" : "outline", "aria-label": "Gantt options", "data-timeline-gantt-menu": "", children: "Gantt" }) }),
35816
+ /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_design_system102.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_design_system102.Button, { size: "sm", variant: "outline", className: ganttOn ? "bg-secondary" : void 0, "aria-label": "Gantt options", "data-timeline-gantt-menu": "", children: "Gantt" }) }),
35747
35817
  /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(import_design_system102.DropdownMenuContent, { align: "end", className: "min-w-48", children: [
35748
35818
  /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(import_design_system102.DropdownMenuCheckboxItem, { checked: ganttOn, onCheckedChange: (on) => setGantt({ mode: on === true }), children: "Gantt view" }),
35749
35819
  ganttOn ? /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(import_jsx_runtime120.Fragment, { children: [
@@ -35821,6 +35891,7 @@ function TimelineView({
35821
35891
  const ghost = baselineOf(b.p.row);
35822
35892
  const at = shown5(b.p);
35823
35893
  return /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)("div", { children: [
35894
+ linkTarget === b.p.row.id ? /* @__PURE__ */ (0, import_jsx_runtime120.jsx)("div", { "aria-hidden": "true", "data-timeline-drop-band": b.p.row.id, className: "pointer-events-none absolute inset-x-0 bg-primary/10", style: { top: LANE_PAD / 2 + b.row * ROW_PX, height: ROW_PX } }) : null,
35824
35895
  ghost ? /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(
35825
35896
  "div",
35826
35897
  {
@@ -39123,6 +39194,9 @@ function ArchivedPortals({
39123
39194
  var import_react230 = require("react");
39124
39195
  var import_react231 = require("@ai-matrx/records/react");
39125
39196
  var import_design_system112 = require("@ai-matrx/design-system");
39197
+ var import_core22 = require("@dnd-kit/core");
39198
+ var import_sortable = require("@dnd-kit/sortable");
39199
+ var import_utilities = require("@dnd-kit/utilities");
39126
39200
 
39127
39201
  // src/seedOnce.ts
39128
39202
  var import_react229 = require("react");
@@ -39144,7 +39218,7 @@ var NOOP3 = () => {
39144
39218
  };
39145
39219
  function viewActions(whyNot, host, locked = false, personal = false) {
39146
39220
  const one = (base2, run) => {
39147
- if (whyNot !== null) return { ...base2, disabledReason: whyNot, run: NOOP3 };
39221
+ if (whyNot !== null) return { ...base2, disabledReason: locked && base2.id !== "duplicate" && base2.id !== "lock" ? VIEW_LOCKED_REASON : whyNot, run: NOOP3 };
39148
39222
  if (!run) return { ...base2, disabledReason: ACTION_NOT_HERE_REASON, run: NOOP3 };
39149
39223
  return { ...base2, run };
39150
39224
  };
@@ -39187,6 +39261,25 @@ function seedIsSatisfied(seed, views) {
39187
39261
  if (views.some((v) => v.name === seed.name)) return true;
39188
39262
  return seed.isDefault === true && views.some((v) => v.isDefault);
39189
39263
  }
39264
+ function SectionBox({ id, children, ...rest }) {
39265
+ const { setNodeRef, isOver } = (0, import_core22.useDroppable)({ id });
39266
+ return /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("div", { ref: setNodeRef, className: (0, import_design_system112.cn)("rounded-sm", isOver && "bg-accent/40"), ...rest, children });
39267
+ }
39268
+ function SortableRow({ id, disabled, children }) {
39269
+ const { attributes, listeners: listeners2, setNodeRef, transform, transition, isDragging } = (0, import_sortable.useSortable)({ id, disabled });
39270
+ return /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(
39271
+ "div",
39272
+ {
39273
+ ref: setNodeRef,
39274
+ style: { transform: import_utilities.CSS.Transform.toString(transform), transition },
39275
+ className: (0, import_design_system112.cn)(isDragging && "relative z-10 opacity-80"),
39276
+ "data-view-row": id,
39277
+ ...disabled ? {} : attributes,
39278
+ ...disabled ? {} : listeners2,
39279
+ children
39280
+ }
39281
+ );
39282
+ }
39190
39283
  function ViewBar({ tableId, activeViewId, onActiveView, seed, settings, onViews, initialViews, className }) {
39191
39284
  const client = (0, import_react231.useRecordsClient)();
39192
39285
  const claimSeed = useSeedGuard();
@@ -39218,6 +39311,26 @@ function ViewBar({ tableId, activeViewId, onActiveView, seed, settings, onViews,
39218
39311
  }
39219
39312
  });
39220
39313
  const [renamingSection, setRenamingSection] = (0, import_react230.useState)(null);
39314
+ const [dragging, setDragging] = (0, import_react230.useState)(false);
39315
+ const justDragged = (0, import_react230.useRef)(false);
39316
+ const sensors = (0, import_core22.useSensors)((0, import_core22.useSensor)(import_core22.PointerSensor, { activationConstraint: { distance: 6 } }));
39317
+ async function dropped(event) {
39318
+ setDragging(false);
39319
+ justDragged.current = true;
39320
+ setTimeout(() => {
39321
+ justDragged.current = false;
39322
+ }, 250);
39323
+ if (!event.over || !views) return;
39324
+ const writes = (0, import_views2.planViewDrop)(views, String(event.active.id), String(event.over.id));
39325
+ for (const w of writes) {
39326
+ const written = await saveViewPatch(client, tableId, w.id, { position: w.position, ..."section" in w ? { section: w.section ?? null } : {} });
39327
+ if (!written.ok) {
39328
+ setError(written.error);
39329
+ break;
39330
+ }
39331
+ }
39332
+ if (writes.length > 0) await load();
39333
+ }
39221
39334
  const toggleShut = (name) => {
39222
39335
  setShut((all) => {
39223
39336
  const next = all.includes(name) ? all.filter((n) => n !== name) : [...all, name];
@@ -39430,83 +39543,89 @@ function ViewBar({ tableId, activeViewId, onActiveView, seed, settings, onViews,
39430
39543
  ) }),
39431
39544
  /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(import_design_system112.PopoverContent, { sizing: "content", align: "start", className: "w-64 p-1", "data-view-picker-content": "", children: [
39432
39545
  /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)("div", { role: "group", "aria-label": "Saved views", className: "flex max-h-[50dvh] flex-col overflow-y-auto", children: [
39433
- (0, import_views2.groupViewsBySection)(views).map((group) => /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)("div", { role: "group", "aria-label": group.name ?? "Views", "data-view-section": group.name ?? "", children: [
39434
- group.name !== null ? renamingSection?.from === group.name ? /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(
39435
- "form",
39436
- {
39437
- className: "flex items-center gap-1 px-1 py-1",
39438
- onSubmit: (e) => {
39439
- e.preventDefault();
39440
- void renameSection(group.name, renamingSection.draft);
39441
- },
39442
- children: [
39443
- /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(
39444
- import_design_system112.Input,
39445
- {
39446
- autoFocus: true,
39447
- "aria-label": "New name for this section",
39448
- value: renamingSection.draft,
39449
- onChange: (e) => setRenamingSection({ from: group.name, draft: e.target.value }),
39450
- onKeyDown: (e) => {
39451
- if (e.key === "Escape") {
39452
- e.preventDefault();
39453
- e.stopPropagation();
39454
- setRenamingSection(null);
39455
- }
39456
- },
39457
- className: "h-7 min-w-0 flex-1 text-xs"
39458
- }
39459
- ),
39460
- /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(import_design_system112.Button, { size: "sm", type: "submit", className: "h-7", children: "Save" })
39461
- ]
39462
- }
39463
- ) : /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)("div", { className: "group flex min-h-7 items-center gap-1 px-2 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: [
39464
- /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(
39546
+ /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(import_core22.DndContext, { sensors, collisionDetection: import_core22.closestCenter, onDragStart: () => setDragging(true), onDragCancel: () => setDragging(false), onDragEnd: (e) => void dropped(e), children: (() => {
39547
+ const groups = (0, import_views2.groupViewsBySection)(views);
39548
+ return groups.some((g) => g.name === null) || !dragging ? groups : [{ name: null, views: [] }, ...groups];
39549
+ })().map((group) => /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(SectionBox, { id: (0, import_views2.sectionDropId)(group.name), role: "group", "aria-label": group.name ?? "Views", "data-view-section": group.name ?? "", children: [
39550
+ group.name === null && group.views.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("span", { className: "block px-2 py-1.5 text-[11px] text-muted-foreground", children: "No section" }) : null,
39551
+ /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(import_sortable.SortableContext, { items: group.views.map((v) => v.id), strategy: import_sortable.verticalListSortingStrategy, children: [
39552
+ group.name !== null ? renamingSection?.from === group.name ? /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(
39553
+ "form",
39554
+ {
39555
+ className: "flex items-center gap-1 px-1 py-1",
39556
+ onSubmit: (e) => {
39557
+ e.preventDefault();
39558
+ void renameSection(group.name, renamingSection.draft);
39559
+ },
39560
+ children: [
39561
+ /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(
39562
+ import_design_system112.Input,
39563
+ {
39564
+ autoFocus: true,
39565
+ "aria-label": "New name for this section",
39566
+ value: renamingSection.draft,
39567
+ onChange: (e) => setRenamingSection({ from: group.name, draft: e.target.value }),
39568
+ onKeyDown: (e) => {
39569
+ if (e.key === "Escape") {
39570
+ e.preventDefault();
39571
+ e.stopPropagation();
39572
+ setRenamingSection(null);
39573
+ }
39574
+ },
39575
+ className: "h-7 min-w-0 flex-1 text-xs"
39576
+ }
39577
+ ),
39578
+ /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(import_design_system112.Button, { size: "sm", type: "submit", className: "h-7", children: "Save" })
39579
+ ]
39580
+ }
39581
+ ) : /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)("div", { className: "group flex min-h-7 items-center gap-1 px-2 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: [
39582
+ /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(
39583
+ "button",
39584
+ {
39585
+ type: "button",
39586
+ className: "flex min-w-0 flex-1 items-center gap-1 text-left",
39587
+ "aria-expanded": !shut.includes(group.name),
39588
+ "data-view-section-toggle": "",
39589
+ onClick: () => toggleShut(group.name),
39590
+ children: [
39591
+ /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("svg", { viewBox: "0 0 24 24", className: (0, import_design_system112.cn)("h-3 w-3 shrink-0", shut.includes(group.name) && "-rotate-90"), fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("path", { d: "m6 9 6 6 6-6" }) }),
39592
+ /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("span", { className: "truncate", children: group.name }),
39593
+ /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("span", { className: "shrink-0 font-normal normal-case", children: group.views.length })
39594
+ ]
39595
+ }
39596
+ ),
39597
+ rights.admin ? /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(import_jsx_runtime131.Fragment, { children: [
39598
+ /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("button", { type: "button", className: "shrink-0 normal-case hover:text-foreground", "data-view-section-rename": "", onClick: () => setRenamingSection({ from: group.name, draft: group.name }), children: "Rename" }),
39599
+ /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("button", { type: "button", className: "shrink-0 normal-case hover:text-destructive", "data-view-section-delete": "", title: "The views stay; they leave the section", onClick: () => void renameSection(group.name, null), children: "Delete" })
39600
+ ] }) : null
39601
+ ] }) : null,
39602
+ group.name !== null && shut.includes(group.name) && !group.views.some((v) => v.id === active) ? null : group.views.map((view) => /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(SortableRow, { id: view.id, disabled: !(rights.admin || view.personal) || view.presentation?.locked === true, children: /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(
39465
39603
  "button",
39466
39604
  {
39467
39605
  type: "button",
39468
- className: "flex min-w-0 flex-1 items-center gap-1 text-left",
39469
- "aria-expanded": !shut.includes(group.name),
39470
- "data-view-section-toggle": "",
39471
- onClick: () => toggleShut(group.name),
39606
+ "aria-pressed": view.id === active,
39607
+ className: (0, import_design_system112.cn)(
39608
+ "flex min-h-8 w-full items-center gap-2 rounded-sm px-2 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground",
39609
+ group.name !== null && "pl-4",
39610
+ view.id === active && "font-medium"
39611
+ ),
39612
+ onClick: () => {
39613
+ if (justDragged.current) return;
39614
+ setActive(view.id);
39615
+ onActiveView?.(view);
39616
+ setOpen(false);
39617
+ },
39618
+ title: view.ruleId ? "Membership is this view's Rule" : "The whole table",
39472
39619
  children: [
39473
- /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("svg", { viewBox: "0 0 24 24", className: (0, import_design_system112.cn)("h-3 w-3 shrink-0", shut.includes(group.name) && "-rotate-90"), fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("path", { d: "m6 9 6 6 6-6" }) }),
39474
- /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("span", { className: "truncate", children: group.name }),
39475
- /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("span", { className: "shrink-0 font-normal normal-case", children: group.views.length })
39620
+ /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("span", { className: "min-w-0 flex-1 truncate", children: view.name }),
39621
+ view.presentation?.locked === true ? /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(import_design_system112.Badge, { variant: "neutral", className: "shrink-0 px-1.5 py-0 text-[10px]", "data-view-locked": "", title: VIEW_LOCKED_REASON, children: "Locked" }) : null,
39622
+ view.personal ? /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(import_design_system112.Badge, { variant: "neutral", className: "shrink-0 px-1.5 py-0 text-[10px]", "data-view-personal": "", title: "Only you see this view", children: "Personal" }) : null,
39623
+ view.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(import_design_system112.Badge, { variant: "neutral", className: "shrink-0 px-1.5 py-0 text-[10px]", "data-view-default": "", children: "Default" }) : null
39476
39624
  ]
39477
39625
  }
39478
- ),
39479
- rights.admin ? /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(import_jsx_runtime131.Fragment, { children: [
39480
- /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("button", { type: "button", className: "shrink-0 normal-case hover:text-foreground", "data-view-section-rename": "", onClick: () => setRenamingSection({ from: group.name, draft: group.name }), children: "Rename" }),
39481
- /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("button", { type: "button", className: "shrink-0 normal-case hover:text-destructive", "data-view-section-delete": "", title: "The views stay; they leave the section", onClick: () => void renameSection(group.name, null), children: "Delete" })
39482
- ] }) : null
39483
- ] }) : null,
39484
- group.name !== null && shut.includes(group.name) && !group.views.some((v) => v.id === active) ? null : group.views.map((view) => /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(
39485
- "button",
39486
- {
39487
- type: "button",
39488
- "aria-pressed": view.id === active,
39489
- className: (0, import_design_system112.cn)(
39490
- "flex min-h-8 w-full items-center gap-2 rounded-sm px-2 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground",
39491
- group.name !== null && "pl-4",
39492
- view.id === active && "font-medium"
39493
- ),
39494
- onClick: () => {
39495
- setActive(view.id);
39496
- onActiveView?.(view);
39497
- setOpen(false);
39498
- },
39499
- title: view.ruleId ? "Membership is this view's Rule" : "The whole table",
39500
- children: [
39501
- /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("span", { className: "min-w-0 flex-1 truncate", children: view.name }),
39502
- view.presentation?.locked === true ? /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(import_design_system112.Badge, { variant: "neutral", className: "shrink-0 px-1.5 py-0 text-[10px]", "data-view-locked": "", title: VIEW_LOCKED_REASON, children: "Locked" }) : null,
39503
- view.personal ? /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(import_design_system112.Badge, { variant: "neutral", className: "shrink-0 px-1.5 py-0 text-[10px]", "data-view-personal": "", title: "Only you see this view", children: "Personal" }) : null,
39504
- view.isDefault ? /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(import_design_system112.Badge, { variant: "neutral", className: "shrink-0 px-1.5 py-0 text-[10px]", "data-view-default": "", children: "Default" }) : null
39505
- ]
39506
- },
39507
- view.id
39508
- ))
39509
- ] }, group.name ?? "__loose")),
39626
+ ) }, view.id))
39627
+ ] })
39628
+ ] }, group.name ?? "__loose")) }),
39510
39629
  views.length === 0 && !rights.admin ? /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("span", { className: "px-2 py-1 text-xs text-muted-foreground", children: rights.why("structure") }) : null
39511
39630
  ] }),
39512
39631
  /* @__PURE__ */ (0, import_jsx_runtime131.jsx)("div", { className: "mt-1 border-t border-border pt-1", children: naming ? /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(
@@ -44787,9 +44906,9 @@ function SubscriptionsPanel({ tableId, activeRuleId, className }) {
44787
44906
  var import_clipboard10 = require("@ai-matrx/kit/clipboard");
44788
44907
  var import_design_system132 = require("@ai-matrx/design-system");
44789
44908
  var import_react266 = require("react");
44790
- var import_core22 = require("@dnd-kit/core");
44791
- var import_sortable = require("@dnd-kit/sortable");
44792
- var import_utilities = require("@dnd-kit/utilities");
44909
+ var import_core23 = require("@dnd-kit/core");
44910
+ var import_sortable2 = require("@dnd-kit/sortable");
44911
+ var import_utilities2 = require("@dnd-kit/utilities");
44793
44912
  var import_react267 = require("@ai-matrx/records/react");
44794
44913
  var import_records67 = require("@ai-matrx/records");
44795
44914
  var import_design_system133 = require("@ai-matrx/design-system");
@@ -46420,9 +46539,9 @@ function QuestionList({
46420
46539
  onQuestions,
46421
46540
  onQuestion
46422
46541
  }) {
46423
- const sensors = (0, import_core22.useSensors)(
46424
- (0, import_core22.useSensor)(import_core22.PointerSensor, { activationConstraint: { distance: 4 } }),
46425
- (0, import_core22.useSensor)(import_core22.KeyboardSensor, { coordinateGetter: import_sortable.sortableKeyboardCoordinates })
46542
+ const sensors = (0, import_core23.useSensors)(
46543
+ (0, import_core23.useSensor)(import_core23.PointerSensor, { activationConstraint: { distance: 4 } }),
46544
+ (0, import_core23.useSensor)(import_core23.KeyboardSensor, { coordinateGetter: import_sortable2.sortableKeyboardCoordinates })
46426
46545
  );
46427
46546
  const askable = askableFields([...fields]);
46428
46547
  const unasked = askable.filter((f) => !draft.questions.some((q) => q.field === f.key));
@@ -46435,7 +46554,7 @@ function QuestionList({
46435
46554
  const from = draft.questions.findIndex((q) => q.field === event.active.id);
46436
46555
  const to = event.over ? draft.questions.findIndex((q) => q.field === event.over.id) : -1;
46437
46556
  if (from < 0 || to < 0 || from === to) return;
46438
- onQuestions((0, import_sortable.arrayMove)(draft.questions, from, to));
46557
+ onQuestions((0, import_sortable2.arrayMove)(draft.questions, from, to));
46439
46558
  }
46440
46559
  const notAskable = whyNotAskable([...fields]);
46441
46560
  const leftUnasked = requiredLeftUnasked([...fields], draft.questions.map((q) => q.field));
@@ -46444,7 +46563,7 @@ function QuestionList({
46444
46563
  /* @__PURE__ */ (0, import_jsx_runtime151.jsx)("span", { className: "font-medium text-foreground", children: "Questions" }),
46445
46564
  /* @__PURE__ */ (0, import_jsx_runtime151.jsx)("span", { className: "tabular-nums", children: draft.questions.length })
46446
46565
  ] }),
46447
- /* @__PURE__ */ (0, import_jsx_runtime151.jsx)(import_core22.DndContext, { sensors, collisionDetection: import_core22.closestCenter, onDragEnd: dragEnd, children: /* @__PURE__ */ (0, import_jsx_runtime151.jsx)(import_sortable.SortableContext, { items: draft.questions.map((q) => q.field), strategy: import_sortable.verticalListSortingStrategy, children: draft.questions.map((question, index) => /* @__PURE__ */ (0, import_jsx_runtime151.jsx)(
46566
+ /* @__PURE__ */ (0, import_jsx_runtime151.jsx)(import_core23.DndContext, { sensors, collisionDetection: import_core23.closestCenter, onDragEnd: dragEnd, children: /* @__PURE__ */ (0, import_jsx_runtime151.jsx)(import_sortable2.SortableContext, { items: draft.questions.map((q) => q.field), strategy: import_sortable2.verticalListSortingStrategy, children: draft.questions.map((question, index) => /* @__PURE__ */ (0, import_jsx_runtime151.jsx)(
46448
46567
  QuestionRow,
46449
46568
  {
46450
46569
  index,
@@ -46497,14 +46616,14 @@ function QuestionRow({
46497
46616
  onChange,
46498
46617
  onRemove
46499
46618
  }) {
46500
- const sortable = (0, import_sortable.useSortable)({ id: question.field });
46619
+ const sortable = (0, import_sortable2.useSortable)({ id: question.field });
46501
46620
  const [open, setOpen] = (0, import_react266.useState)(true);
46502
46621
  const name = field2 ? fieldName(field2) : question.field;
46503
46622
  return /* @__PURE__ */ (0, import_jsx_runtime151.jsxs)(
46504
46623
  "div",
46505
46624
  {
46506
46625
  ref: sortable.setNodeRef,
46507
- style: { transform: import_utilities.CSS.Transform.toString(sortable.transform), transition: sortable.transition },
46626
+ style: { transform: import_utilities2.CSS.Transform.toString(sortable.transform), transition: sortable.transition },
46508
46627
  className: (0, import_design_system133.cn)("rounded border bg-background px-2 py-1.5", sortable.isDragging && "z-10 shadow-md"),
46509
46628
  "data-records-form-question-row": question.field,
46510
46629
  children: [
@@ -47440,9 +47559,9 @@ var import_react280 = require("@ai-matrx/records/react");
47440
47559
  var import_design_system140 = require("@ai-matrx/design-system");
47441
47560
 
47442
47561
  // src/DashboardGrid.tsx
47443
- var import_core23 = require("@dnd-kit/core");
47444
- var import_sortable2 = require("@dnd-kit/sortable");
47445
- var import_utilities2 = require("@dnd-kit/utilities");
47562
+ var import_core24 = require("@dnd-kit/core");
47563
+ var import_sortable3 = require("@dnd-kit/sortable");
47564
+ var import_utilities3 = require("@dnd-kit/utilities");
47446
47565
  var import_design_system138 = require("@ai-matrx/design-system");
47447
47566
 
47448
47567
  // src/dashboardLayout.ts
@@ -47496,7 +47615,7 @@ function withBlockIds(blocks) {
47496
47615
  // src/DashboardGrid.tsx
47497
47616
  var import_jsx_runtime156 = require("react/jsx-runtime");
47498
47617
  var dashboardCollision = (args) => {
47499
- const under = (0, import_core23.pointerWithin)(args);
47618
+ const under = (0, import_core24.pointerWithin)(args);
47500
47619
  if (under.length > 0) return under;
47501
47620
  const at = args.collisionRect;
47502
47621
  return args.droppableContainers.flatMap((container) => {
@@ -47506,9 +47625,9 @@ var dashboardCollision = (args) => {
47506
47625
  }).sort((a, b) => a.data.value - b.data.value);
47507
47626
  };
47508
47627
  function DashboardGrid({ items, arranging, onMove, onSpan }) {
47509
- const sensors = (0, import_core23.useSensors)(
47510
- (0, import_core23.useSensor)(import_core23.PointerSensor, { activationConstraint: { distance: 4 } }),
47511
- (0, import_core23.useSensor)(import_core23.KeyboardSensor, { coordinateGetter: import_sortable2.sortableKeyboardCoordinates })
47628
+ const sensors = (0, import_core24.useSensors)(
47629
+ (0, import_core24.useSensor)(import_core24.PointerSensor, { activationConstraint: { distance: 4 } }),
47630
+ (0, import_core24.useSensor)(import_core24.KeyboardSensor, { coordinateGetter: import_sortable3.sortableKeyboardCoordinates })
47512
47631
  );
47513
47632
  const ids = items.map((item) => item.id);
47514
47633
  function dragEnd(event) {
@@ -47518,7 +47637,7 @@ function DashboardGrid({ items, arranging, onMove, onSpan }) {
47518
47637
  }
47519
47638
  const grid = /* @__PURE__ */ (0, import_jsx_runtime156.jsx)("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-12", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime156.jsx)(GridCell2, { item, index, arranging, onSpan }, item.id)) });
47520
47639
  if (!arranging) return grid;
47521
- return /* @__PURE__ */ (0, import_jsx_runtime156.jsx)(import_core23.DndContext, { sensors, collisionDetection: dashboardCollision, onDragEnd: dragEnd, children: /* @__PURE__ */ (0, import_jsx_runtime156.jsx)(import_sortable2.SortableContext, { items: ids, strategy: import_sortable2.rectSortingStrategy, children: grid }) });
47640
+ return /* @__PURE__ */ (0, import_jsx_runtime156.jsx)(import_core24.DndContext, { sensors, collisionDetection: dashboardCollision, onDragEnd: dragEnd, children: /* @__PURE__ */ (0, import_jsx_runtime156.jsx)(import_sortable3.SortableContext, { items: ids, strategy: import_sortable3.rectSortingStrategy, children: grid }) });
47522
47641
  }
47523
47642
  function GridCell2({
47524
47643
  item,
@@ -47526,8 +47645,8 @@ function GridCell2({
47526
47645
  arranging,
47527
47646
  onSpan
47528
47647
  }) {
47529
- const sortable = (0, import_sortable2.useSortable)({ id: item.id, disabled: !arranging });
47530
- const style = arranging ? { transform: import_utilities2.CSS.Transform.toString(sortable.transform), transition: sortable.transition } : void 0;
47648
+ const sortable = (0, import_sortable3.useSortable)({ id: item.id, disabled: !arranging });
47649
+ const style = arranging ? { transform: import_utilities3.CSS.Transform.toString(sortable.transform), transition: sortable.transition } : void 0;
47531
47650
  return /* @__PURE__ */ (0, import_jsx_runtime156.jsxs)(
47532
47651
  "div",
47533
47652
  {
@@ -49384,7 +49503,7 @@ function AgentAnswer({
49384
49503
  // src/PageScreen.tsx
49385
49504
  var import_react285 = require("react");
49386
49505
  var import_react286 = require("@ai-matrx/records/react");
49387
- var import_core24 = require("@ai-matrx/records/core");
49506
+ var import_core25 = require("@ai-matrx/records/core");
49388
49507
  var import_design_system143 = require("@ai-matrx/design-system");
49389
49508
  var import_forms4 = require("@ai-matrx/records/forms");
49390
49509
  var import_jsx_runtime161 = require("react/jsx-runtime");
@@ -49801,7 +49920,7 @@ function ListBlockView({
49801
49920
  ...sort.length > 0 ? { sort } : {}
49802
49921
  });
49803
49922
  const reload = page.reload;
49804
- (0, import_react285.useEffect)(() => (0, import_core24.onRecordChanged)(() => reload()), [reload]);
49923
+ (0, import_react285.useEffect)(() => (0, import_core25.onRecordChanged)(() => reload()), [reload]);
49805
49924
  const rows = nobodyIsMe ? [] : page.data?.rows ?? [];
49806
49925
  const columns = block.columns && block.columns.length > 0 ? block.columns.map((k) => all.find((f) => f.key === k)).filter((f) => !!f) : [...all].sort((a, b) => (a.sort ?? 100) - (b.sort ?? 100)).slice(0, DEFAULT_COLUMNS);
49807
49926
  const firstId = rows[0]?.id ?? null;
@@ -51430,9 +51549,9 @@ function CalendarSubscribeDialog({
51430
51549
 
51431
51550
  // src/viewShare.ts
51432
51551
  var import_clipboard15 = require("@ai-matrx/kit/clipboard");
51433
- var import_core25 = require("@ai-matrx/print/core");
51552
+ var import_core26 = require("@ai-matrx/print/core");
51434
51553
  var import_markdown = require("@ai-matrx/print/markdown");
51435
- var import_core26 = require("@ai-matrx/records/core");
51554
+ var import_core27 = require("@ai-matrx/records/core");
51436
51555
  var NO_VIEW_REASON = "No saved view yet";
51437
51556
  function viewLink(tableId, viewId, origin) {
51438
51557
  return `${tableLink(tableId, origin)}?view=${encodeURIComponent(viewId)}`;
@@ -51468,7 +51587,7 @@ async function downloadViewCsv(args) {
51468
51587
  const columns = viewColumns(fields, view.presentation);
51469
51588
  const rows = await rowsForFile({ client, host, fields: columns, records: read.rows, labels });
51470
51589
  const name = fileNameOf(args.tableName) || "records";
51471
- download(`${name}.csv`, "text/csv;charset=utf-8", (0, import_core26.exportCsv)(columns, rows));
51590
+ download(`${name}.csv`, "text/csv;charset=utf-8", (0, import_core27.exportCsv)(columns, rows));
51472
51591
  host.notify?.success(read.cut ? cutWords(read.rows.length) : "CSV downloaded");
51473
51592
  }
51474
51593
  function downloadMarkdownAction(args) {
@@ -51497,7 +51616,7 @@ async function readViewMarkdown(args) {
51497
51616
  if ("error" in read) return null;
51498
51617
  const columns = viewColumns(fields, view.presentation);
51499
51618
  const rows = await rowsForFile({ client, host, fields: columns, records: read.rows, labels });
51500
- return { markdown: (0, import_core26.exportMarkdown)(columns, rows), cut: read.cut, count: read.rows.length };
51619
+ return { markdown: (0, import_core27.exportMarkdown)(columns, rows), cut: read.cut, count: read.rows.length };
51501
51620
  }
51502
51621
  async function downloadViewMarkdown(args) {
51503
51622
  const table = await readViewMarkdown(args);
@@ -51555,7 +51674,7 @@ async function runTablePageAction(kind, args) {
51555
51674
  }
51556
51675
  const page = (0, import_markdown.renderMarkdownDocument)(markdown, { title, withPrintActions: kind === "html-preview" });
51557
51676
  if (kind === "html-preview") {
51558
- (0, import_core25.openPrintWindow)(page, title);
51677
+ (0, import_core26.openPrintWindow)(page, title);
51559
51678
  if (note) args.host.notify?.success(note);
51560
51679
  } else if (kind === "copy-html-page") {
51561
51680
  if (await (0, import_clipboard15.copyText)(page)) args.host.notify?.success(note ?? "HTML page copied");