@ai-matrx/records-ui 0.108.0 → 0.109.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.
package/dist/index.js CHANGED
@@ -854,7 +854,55 @@ var REVERSE_COLUMN_PREFIX = "linked:";
854
854
 
855
855
  // src/gridPage.ts
856
856
  import { isRuleFilter } from "@ai-matrx/records";
857
- import { dateFilterBounds, dateFilterDayBounds } from "@ai-matrx/design-system/data-table";
857
+ import { dateFilterBounds, dateFilterDayBounds, dayInstantWindow } from "@ai-matrx/design-system/data-table";
858
+
859
+ // src/personTime.ts
860
+ import { useSyncExternalStore } from "react";
861
+ var zone = null;
862
+ var listeners = /* @__PURE__ */ new Set();
863
+ function valid(z) {
864
+ if (typeof z !== "string" || !z.trim()) return false;
865
+ try {
866
+ new Intl.DateTimeFormat("en-US", { timeZone: z });
867
+ return true;
868
+ } catch {
869
+ return false;
870
+ }
871
+ }
872
+ function setPersonTimeZone(next) {
873
+ const clean = valid(next) ? next : null;
874
+ if (clean === zone) return;
875
+ zone = clean;
876
+ listeners.forEach((l) => l());
877
+ }
878
+ function getPersonTimeZone() {
879
+ return zone;
880
+ }
881
+ function usePersonTimeZone() {
882
+ return useSyncExternalStore(
883
+ (cb) => {
884
+ listeners.add(cb);
885
+ return () => listeners.delete(cb);
886
+ },
887
+ () => zone,
888
+ () => null
889
+ );
890
+ }
891
+ function personWallNow(at = /* @__PURE__ */ new Date()) {
892
+ if (!zone) return at;
893
+ const parts = new Intl.DateTimeFormat("en-US", {
894
+ timeZone: zone,
895
+ year: "numeric",
896
+ month: "2-digit",
897
+ day: "2-digit",
898
+ hour: "2-digit",
899
+ minute: "2-digit",
900
+ second: "2-digit",
901
+ hourCycle: "h23"
902
+ }).formatToParts(at);
903
+ const n = (t) => Number(parts.find((p) => p.type === t)?.value ?? 0);
904
+ return new Date(n("year"), n("month") - 1, n("day"), n("hour"), n("minute"), n("second"));
905
+ }
858
906
 
859
907
  // src/choiceLooks.ts
860
908
  import {
@@ -1207,9 +1255,10 @@ function clauseFor(f, value, now = /* @__PURE__ */ new Date()) {
1207
1255
  return { rule: value.negated ? not(range) : range };
1208
1256
  }
1209
1257
  case "date": {
1210
- const moments = dateFilterBounds(value.preset ? { ...value, anchor: now.toISOString() } : value, now);
1258
+ const zone2 = getPersonTimeZone() ?? void 0;
1259
+ const moments = dateFilterBounds(value.preset ? { ...value, anchor: now.toISOString() } : value, now, zone2);
1211
1260
  const holdsDays = String((f.config ?? {})["kind"] ?? "") === "date";
1212
- const bounds = holdsDays ? dateFilterDayBounds(moments) : moments;
1261
+ const bounds = holdsDays ? dateFilterDayBounds(moments, zone2) : moments;
1213
1262
  if (bounds.since === void 0 && bounds.until === void 0) return null;
1214
1263
  return {
1215
1264
  flat: {
@@ -1225,11 +1274,10 @@ function clauseFor(f, value, now = /* @__PURE__ */ new Date()) {
1225
1274
  }
1226
1275
  }
1227
1276
  function dayWindow(day, holdsDays) {
1277
+ if (!holdsDays) return dayInstantWindow(day, getPersonTimeZone() ?? void 0);
1228
1278
  const [y, m, d] = day.split("-").map(Number);
1229
- const start = new Date(y, (m ?? 1) - 1, d ?? 1);
1230
- const next = new Date(y, (m ?? 1) - 1, (d ?? 1) + 1);
1231
- const ymd = (t) => `${t.getFullYear()}-${String(t.getMonth() + 1).padStart(2, "0")}-${String(t.getDate()).padStart(2, "0")}`;
1232
- return holdsDays ? { from: ymd(start), to: ymd(next) } : { from: start.toISOString(), to: next.toISOString() };
1279
+ const ymd = (t) => `${t.getUTCFullYear()}-${String(t.getUTCMonth() + 1).padStart(2, "0")}-${String(t.getUTCDate()).padStart(2, "0")}`;
1280
+ return { from: ymd(new Date(Date.UTC(y, (m ?? 1) - 1, d ?? 1))), to: ymd(new Date(Date.UTC(y, (m ?? 1) - 1, (d ?? 1) + 1))) };
1233
1281
  }
1234
1282
  function storeQuestion(fields, base, columnFilters, now = /* @__PURE__ */ new Date()) {
1235
1283
  const refused = [];
@@ -3852,10 +3900,9 @@ import { useRecordsClient as useRecordsClient9 } from "@ai-matrx/records/react";
3852
3900
  import { Button as Button13, Checkbox as Checkbox4, Popover as Popover5, PopoverContent as PopoverContent5, PopoverTrigger as PopoverTrigger5, cn as cn20 } from "@ai-matrx/design-system";
3853
3901
 
3854
3902
  // src/presentation.ts
3903
+ import { parseTableStyle, tableStyleIsEmpty } from "@ai-matrx/design-system/data-table/table-style";
3855
3904
  import {
3856
- COLOR_ROTATION,
3857
- parseTableStyle,
3858
- tableStyleIsEmpty
3905
+ COLOR_ROTATION
3859
3906
  } from "@ai-matrx/design-system/data-table";
3860
3907
  import { getFieldFormat } from "@ai-matrx/design-system/field-formats";
3861
3908
  import { newerKindOf as newerKindOf2, ratingMaxOf } from "@ai-matrx/records";
@@ -5671,56 +5718,6 @@ function formatControl(args) {
5671
5718
  import { useState as useState18 } from "react";
5672
5719
  import { Popover as Popover2, PopoverContent as PopoverContent2, PopoverTrigger as PopoverTrigger2, cn as cn15 } from "@ai-matrx/design-system";
5673
5720
  import { readTypedDate, readTypedTime, typedDateText, typedTimeText } from "@ai-matrx/records";
5674
-
5675
- // src/personTime.ts
5676
- import { useSyncExternalStore } from "react";
5677
- var zone = null;
5678
- var listeners = /* @__PURE__ */ new Set();
5679
- function valid(z) {
5680
- if (typeof z !== "string" || !z.trim()) return false;
5681
- try {
5682
- new Intl.DateTimeFormat("en-US", { timeZone: z });
5683
- return true;
5684
- } catch {
5685
- return false;
5686
- }
5687
- }
5688
- function setPersonTimeZone(next) {
5689
- const clean = valid(next) ? next : null;
5690
- if (clean === zone) return;
5691
- zone = clean;
5692
- listeners.forEach((l) => l());
5693
- }
5694
- function getPersonTimeZone() {
5695
- return zone;
5696
- }
5697
- function usePersonTimeZone() {
5698
- return useSyncExternalStore(
5699
- (cb) => {
5700
- listeners.add(cb);
5701
- return () => listeners.delete(cb);
5702
- },
5703
- () => zone,
5704
- () => null
5705
- );
5706
- }
5707
- function personWallNow(at = /* @__PURE__ */ new Date()) {
5708
- if (!zone) return at;
5709
- const parts = new Intl.DateTimeFormat("en-US", {
5710
- timeZone: zone,
5711
- year: "numeric",
5712
- month: "2-digit",
5713
- day: "2-digit",
5714
- hour: "2-digit",
5715
- minute: "2-digit",
5716
- second: "2-digit",
5717
- hourCycle: "h23"
5718
- }).formatToParts(at);
5719
- const n = (t) => Number(parts.find((p) => p.type === t)?.value ?? 0);
5720
- return new Date(n("year"), n("month") - 1, n("day"), n("hour"), n("minute"), n("second"));
5721
- }
5722
-
5723
- // src/typedDateHelper.tsx
5724
5721
  import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
5725
5722
  var WEEK = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
5726
5723
  var MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
@@ -7033,6 +7030,473 @@ import { Badge as Badge5, cn as cn19 } from "@ai-matrx/design-system";
7033
7030
 
7034
7031
  // src/embeddedLook.tsx
7035
7032
  import { createContext as createContext5, useContext as useContext5 } from "react";
7033
+
7034
+ // src/firstPage.ts
7035
+ import { asRecordsDataSource, createRecordsClient, recordingDataSource } from "@ai-matrx/records/core";
7036
+
7037
+ // src/viewKeys.ts
7038
+ var STORE_KEEPS_TIMELINE = true;
7039
+ var ALL = ["grid", "kanban", "calendar", "gallery", ...STORE_KEEPS_TIMELINE ? ["timeline"] : [], "list"];
7040
+ var HIDES = ["grid", "gallery", "kanban", "calendar", ...STORE_KEEPS_TIMELINE ? ["timeline"] : [], "list"];
7041
+ var GRIDS = ["grid"];
7042
+ var VIEW_KEYS = [
7043
+ { path: "layout", shape: "kind", layouts: ALL, writer: "caller" },
7044
+ { path: "filters", shape: "filter", layouts: ALL, writer: "caller" },
7045
+ { path: "where", shape: "rule", layouts: ALL, writer: "caller" },
7046
+ { path: "group_field", shape: "field", layouts: ["kanban", ...STORE_KEEPS_TIMELINE ? ["timeline"] : []], writer: "caller" },
7047
+ { path: "swimlane_field", shape: "field:lane", layouts: ["kanban"], writer: "caller" },
7048
+ { path: "collapsed_columns", shape: "values", layouts: ["kanban"], writer: "caller" },
7049
+ { path: "measure", shape: "field:number", layouts: ["kanban"], writer: "caller" },
7050
+ { path: "date_field", shape: "field:date", layouts: ["calendar"], writer: "caller" },
7051
+ ...STORE_KEEPS_TIMELINE ? [
7052
+ { path: "start_field", shape: "field:date", layouts: ["timeline"], writer: "caller" },
7053
+ { path: "end_field", shape: "field:date", layouts: ["calendar", "timeline"], writer: "caller" }
7054
+ ] : [],
7055
+ { path: "image_field", shape: "field", layouts: ["gallery"], writer: "caller" },
7056
+ { path: "sorts", shape: "sorts", layouts: ALL, writer: "caller" },
7057
+ { path: "rule_id", shape: "uuid", layouts: ALL, writer: "caller" },
7058
+ { path: "is_default", shape: "boolean", layouts: ALL, writer: "caller" },
7059
+ { path: "presentation", shape: "presentation", layouts: ALL, writer: "caller" },
7060
+ { path: "presentation.style", shape: "style", layouts: ALL, writer: "caller" },
7061
+ { path: "presentation.formats", shape: "object", layouts: GRIDS, writer: "caller" },
7062
+ { path: "presentation.frozen", shape: "fields", layouts: GRIDS, writer: "caller" },
7063
+ { path: "presentation.grouping", shape: "grouping", layouts: ["grid", "gallery", "list"], writer: "caller" },
7064
+ { path: "presentation.widths", shape: "widths", layouts: GRIDS, writer: "caller" },
7065
+ { path: "presentation.rowHeight", shape: "row_height", layouts: GRIDS, writer: "caller" },
7066
+ { path: "presentation.hiddenFields", shape: "fields", layouts: HIDES, writer: "caller" },
7067
+ { path: "presentation.cardHidden", shape: "fields", layouts: ["kanban", "calendar", ...STORE_KEEPS_TIMELINE ? ["timeline"] : [], "list", "gallery"], writer: "caller" },
7068
+ { path: "presentation.columnOrder", shape: "fields", layouts: GRIDS, writer: "caller" },
7069
+ { path: "presentation.gallerySize", shape: "gallery_size", layouts: ["gallery"], writer: "caller" },
7070
+ { path: "presentation.wrap", shape: "boolean", layouts: GRIDS, writer: "caller" },
7071
+ { path: "presentation.wrapColumns", shape: "fields", layouts: GRIDS, writer: "caller" },
7072
+ { path: "presentation.fit", shape: "fit", layouts: GRIDS, writer: "caller" },
7073
+ { path: "presentation.freezeFirst", shape: "boolean", layouts: GRIDS, writer: "caller" },
7074
+ { path: "presentation.footer", shape: "footer", layouts: GRIDS, writer: "caller" },
7075
+ { path: "presentation.summaries", shape: "summaries", layouts: GRIDS, writer: "caller" },
7076
+ { path: "grid", shape: "grid", layouts: GRIDS, writer: "caller" },
7077
+ { path: "hidden_fields", shape: "input", layouts: HIDES, writer: "input" },
7078
+ { path: "table_id", shape: "server", layouts: ALL, writer: "server" },
7079
+ { path: "order", shape: "server", layouts: GRIDS, writer: "server" },
7080
+ { path: "moved_from", shape: "server", layouts: ALL, writer: "server" }
7081
+ ];
7082
+ var STORE_KEPT_LAYOUTS = ["grid", "kanban", "calendar", "gallery", ...STORE_KEEPS_TIMELINE ? ["timeline"] : [], "list"];
7083
+ function storeKeepsLayout(layout) {
7084
+ return STORE_KEPT_LAYOUTS.includes(layout);
7085
+ }
7086
+ function viewKeysFor(layout) {
7087
+ return VIEW_KEYS.filter((k) => k.layouts.includes(layout)).map((k) => k.path);
7088
+ }
7089
+ var KINDS = STORE_KEPT_LAYOUTS;
7090
+ function viewSettings(definition, rowFilters) {
7091
+ const data = definition ?? {};
7092
+ const text2 = (key) => {
7093
+ const v = data[key];
7094
+ return typeof v === "string" && v !== "" ? v : null;
7095
+ };
7096
+ const layout = String(data["layout"] ?? "grid");
7097
+ const flat = rowFilters ?? (isObject2(data["filters"]) ? data["filters"] : null);
7098
+ const moved = data["moved_from"];
7099
+ return {
7100
+ layout: KINDS.includes(layout) ? layout : "grid",
7101
+ filters: flat && Object.keys(flat).length > 0 ? flat : null,
7102
+ where: isRule(data["where"]) ? data["where"] : null,
7103
+ groupField: text2("group_field"),
7104
+ swimlaneField: text2("swimlane_field"),
7105
+ collapsedColumns: Array.isArray(data["collapsed_columns"]) ? data["collapsed_columns"].filter((v) => ["string", "number", "boolean"].includes(typeof v)).map(String) : [],
7106
+ measure: text2("measure"),
7107
+ dateField: text2("date_field"),
7108
+ startField: text2("start_field"),
7109
+ endField: text2("end_field"),
7110
+ imageField: text2("image_field"),
7111
+ sorts: parseSorts(data["sorts"]),
7112
+ ruleId: text2("rule_id"),
7113
+ isDefault: data["is_default"] === true || data["is_default"] === "true",
7114
+ presentation: parseGridPresentation(data["presentation"]),
7115
+ movedFrom: isObject2(moved) ? moved : null
7116
+ };
7117
+ }
7118
+ function viewSettingsFor(settings, layout) {
7119
+ const reads = new Set(viewKeysFor(layout));
7120
+ const out = { layout: settings.layout };
7121
+ const take = (key, path) => {
7122
+ if (reads.has(path)) out[key] = settings[key];
7123
+ };
7124
+ take("filters", "filters");
7125
+ take("where", "where");
7126
+ take("groupField", "group_field");
7127
+ take("swimlaneField", "swimlane_field");
7128
+ take("collapsedColumns", "collapsed_columns");
7129
+ take("measure", "measure");
7130
+ take("dateField", "date_field");
7131
+ take("startField", "start_field");
7132
+ take("endField", "end_field");
7133
+ take("imageField", "image_field");
7134
+ take("sorts", "sorts");
7135
+ take("ruleId", "rule_id");
7136
+ take("isDefault", "is_default");
7137
+ const p = settings.presentation;
7138
+ const look = {};
7139
+ for (const [key, value] of Object.entries(p)) {
7140
+ if (reads.has(`presentation.${key}`)) look[key] = value;
7141
+ }
7142
+ out.presentation = look;
7143
+ return out;
7144
+ }
7145
+ function viewSettingsPatch(patch) {
7146
+ const out = {};
7147
+ const field2 = (key, path) => {
7148
+ if (patch[key] !== void 0) out[path] = patch[key] || null;
7149
+ };
7150
+ if (patch.layout !== void 0 && storeKeepsLayout(patch.layout)) out["layout"] = patch.layout;
7151
+ field2("groupField", "group_field");
7152
+ field2("swimlaneField", "swimlane_field");
7153
+ field2("measure", "measure");
7154
+ field2("dateField", "date_field");
7155
+ if (STORE_KEEPS_TIMELINE) {
7156
+ field2("startField", "start_field");
7157
+ field2("endField", "end_field");
7158
+ }
7159
+ field2("imageField", "image_field");
7160
+ field2("ruleId", "rule_id");
7161
+ if (patch.collapsedColumns !== void 0) out["collapsed_columns"] = patch.collapsedColumns ?? [];
7162
+ if (patch.sorts !== void 0) out["sorts"] = patch.sorts ?? [];
7163
+ if (patch.where !== void 0) out["where"] = patch.where ?? null;
7164
+ if (patch.isDefault !== void 0) out["is_default"] = patch.isDefault === true;
7165
+ if (patch.presentation !== void 0) {
7166
+ out["presentation"] = !patch.presentation || presentationIsEmpty(patch.presentation) ? null : JSON.parse(presentationDocument(patch.presentation));
7167
+ }
7168
+ return out;
7169
+ }
7170
+ function isObject2(raw) {
7171
+ return Boolean(raw) && typeof raw === "object" && !Array.isArray(raw);
7172
+ }
7173
+ function isRule(raw) {
7174
+ if (!isObject2(raw)) return false;
7175
+ const node = raw;
7176
+ return typeof node.op === "string" && Array.isArray(node.args);
7177
+ }
7178
+ function parseSorts(raw) {
7179
+ let list = raw;
7180
+ if (typeof raw === "string") {
7181
+ try {
7182
+ list = raw.trim() === "" ? [] : JSON.parse(raw);
7183
+ } catch {
7184
+ return [];
7185
+ }
7186
+ }
7187
+ if (!Array.isArray(list)) return [];
7188
+ return list.filter((s) => isObject2(s) && typeof s.field === "string").map((s) => ({ field: s.field, direction: s.direction === "desc" ? "desc" : "asc" }));
7189
+ }
7190
+
7191
+ // src/views.ts
7192
+ var VIEW_LAYOUTS = ["grid", "kanban", "calendar", "gallery", "timeline", "list"];
7193
+ var LAYOUT_LABEL = {
7194
+ grid: "Grid",
7195
+ kanban: "Kanban",
7196
+ calendar: "Calendar",
7197
+ gallery: "Gallery",
7198
+ timeline: "Timeline",
7199
+ list: "List"
7200
+ };
7201
+ function wordsList(words2) {
7202
+ const clean = words2.map((w) => w.trim()).filter((w) => w !== "");
7203
+ if (clean.length <= 1) return clean.join("");
7204
+ return `${clean.slice(0, -1).join(", ")} and ${clean[clean.length - 1]}`;
7205
+ }
7206
+ var VIEW_NEEDS = {
7207
+ grid: null,
7208
+ // A list reads nothing a table can lack: rows, titled, grouped only when the person chooses.
7209
+ list: null,
7210
+ kanban: {
7211
+ kind: "choice",
7212
+ viewKey: "groupField",
7213
+ blocking: true,
7214
+ starter: { label: "Status", type: "select", options: ["To do", "In progress", "Done"] },
7215
+ pick: "Pick the choice column the board's columns come from.",
7216
+ missing: "This table has no choice column to make the board's columns from.",
7217
+ does: (label) => `Make it work adds a ${label} column with the choices below and groups the board by it.`,
7218
+ ask: (label, options) => `Add a choice column called ${label} to this table${options.length > 0 ? ` with the choices ${wordsList(options)}` : ""}, then group the Kanban board by it.`
7219
+ },
7220
+ calendar: {
7221
+ kind: "date",
7222
+ viewKey: "dateField",
7223
+ blocking: true,
7224
+ starter: { label: "Date", type: "datetime" },
7225
+ pick: "Pick the date column the calendar places records by.",
7226
+ missing: "This table has no date column to place records on.",
7227
+ does: (label) => `Make it work adds a ${label} column and lays the calendar out by it.`,
7228
+ ask: (label) => `Add a date column called ${label} to this table, then lay the Calendar out by it.`
7229
+ },
7230
+ gallery: {
7231
+ kind: "file",
7232
+ viewKey: "imageField",
7233
+ blocking: false,
7234
+ starter: { label: "Photo", type: "attachment" },
7235
+ pick: "Pick the file column each card shows as its picture.",
7236
+ missing: "The cards have no picture because this table has no file column.",
7237
+ does: (label) => `Make it work adds a ${label} column and shows it on each card.`,
7238
+ ask: (label) => `Add a file column called ${label} to this table, then show it as the picture on each Gallery card.`
7239
+ },
7240
+ timeline: {
7241
+ kind: "date",
7242
+ viewKey: "dateField",
7243
+ blocking: true,
7244
+ starter: { label: "Start", type: "datetime" },
7245
+ pick: "Pick the date column each bar starts on.",
7246
+ missing: "This table has no date column to place records on.",
7247
+ does: (label) => `Make it work adds a ${label} column and lays the timeline out by it.`,
7248
+ ask: (label) => `Add a date column called ${label} to this table, then lay the Timeline out by it.`
7249
+ }
7250
+ };
7251
+ function freeColumnName(wanted, taken) {
7252
+ const held = new Set(taken.map((t) => t.trim().toLowerCase()));
7253
+ const base = wanted.trim() === "" ? "Column" : wanted.trim();
7254
+ if (!held.has(base.toLowerCase())) return base;
7255
+ for (let n = 2; n < 100; n += 1) {
7256
+ const next = `${base} ${n}`;
7257
+ if (!held.has(next.toLowerCase())) return next;
7258
+ }
7259
+ return base;
7260
+ }
7261
+ function viewOrderOf(definition) {
7262
+ const raw = definition?.["order"];
7263
+ return raw === "manual" ? "manual" : raw === "sorted" ? "sorted" : null;
7264
+ }
7265
+ function defaultViewOf(views) {
7266
+ return views.find((v) => v.isDefault);
7267
+ }
7268
+ var LAYOUT_FIELD_KIND = {
7269
+ grid: null,
7270
+ kanban: "choice",
7271
+ calendar: "date",
7272
+ gallery: "file",
7273
+ timeline: "date",
7274
+ list: null
7275
+ };
7276
+ var PAGE_VIEWS = [...VIEW_LAYOUTS, "dashboards", "archived"];
7277
+ var PAGE_VIEW_LABEL = {
7278
+ ...LAYOUT_LABEL,
7279
+ dashboards: "Dashboards",
7280
+ archived: "Archived"
7281
+ };
7282
+ var PAGE_VIEW_SPELLINGS = {
7283
+ grid: "grid",
7284
+ table: "grid",
7285
+ rows: "grid",
7286
+ list: "list",
7287
+ kanban: "kanban",
7288
+ board: "kanban",
7289
+ pipeline: "kanban",
7290
+ calendar: "calendar",
7291
+ month: "calendar",
7292
+ schedule: "calendar",
7293
+ gallery: "gallery",
7294
+ cards: "gallery",
7295
+ timeline: "timeline",
7296
+ gantt: "timeline",
7297
+ dashboards: "dashboards",
7298
+ dashboard: "dashboards",
7299
+ charts: "dashboards",
7300
+ archived: "archived",
7301
+ archive: "archived",
7302
+ // The grid's row action says "Delete" and the store soft-deletes, so the word
7303
+ // a person types is as likely to be the one the button used as the one this
7304
+ // view is called. Both land in the same place; neither dies.
7305
+ deleted: "archived",
7306
+ trash: "archived",
7307
+ removed: "archived"
7308
+ };
7309
+ function pageViewFromParam(raw) {
7310
+ if (typeof raw !== "string") return null;
7311
+ const token = raw.trim().toLowerCase();
7312
+ if (token === "") return null;
7313
+ return PAGE_VIEW_SPELLINGS[token] ?? null;
7314
+ }
7315
+ function unknownViewLine(raw) {
7316
+ return `The link asked for a view called \u201C${raw}\u201D, which this table has no view for, so you are looking at ${PAGE_VIEW_LABEL.grid}. The views this table has are ${PAGE_VIEWS.map((v) => PAGE_VIEW_LABEL[v]).join(", ")} \u2014 pick one above and the address follows it.`;
7317
+ }
7318
+ function viewDocument(spec) {
7319
+ const out = {
7320
+ // A layout the store does not keep yet is not sent: the view is saved as a grid (`STORE_KEPT_LAYOUTS`).
7321
+ layout: storeKeepsLayout(spec.layout) ? spec.layout : "grid",
7322
+ sorts: spec.sorts ?? [],
7323
+ is_default: spec.isDefault === true
7324
+ };
7325
+ if (spec.groupField) out["group_field"] = spec.groupField;
7326
+ if (spec.swimlaneField) out["swimlane_field"] = spec.swimlaneField;
7327
+ if (spec.collapsedColumns && spec.collapsedColumns.length > 0) out["collapsed_columns"] = spec.collapsedColumns;
7328
+ if (spec.measure) out["measure"] = spec.measure;
7329
+ if (spec.dateField) out["date_field"] = spec.dateField;
7330
+ if (STORE_KEEPS_TIMELINE && spec.startField) out["start_field"] = spec.startField;
7331
+ if (STORE_KEEPS_TIMELINE && spec.endField) out["end_field"] = spec.endField;
7332
+ if (spec.imageField) out["image_field"] = spec.imageField;
7333
+ if (spec.ruleId) out["rule_id"] = spec.ruleId;
7334
+ if (spec.where) out["where"] = spec.where;
7335
+ if (spec.presentation && !presentationIsEmpty(spec.presentation)) {
7336
+ out["presentation"] = JSON.parse(presentationDocument(spec.presentation));
7337
+ }
7338
+ return out;
7339
+ }
7340
+ function viewFromRow(row) {
7341
+ const data = row.definition ?? {};
7342
+ const k = viewSettings(data, row.filters && Object.keys(row.filters).length > 0 ? row.filters : null);
7343
+ const tableFromDefinition = typeof data["table_id"] === "string" && data["table_id"] !== "" ? data["table_id"] : null;
7344
+ return {
7345
+ id: row.view_id,
7346
+ name: row.name || "Untitled view",
7347
+ subject: row.table_id ?? tableFromDefinition ?? "",
7348
+ layout: k.layout,
7349
+ groupField: k.groupField,
7350
+ swimlaneField: k.swimlaneField,
7351
+ collapsedColumns: k.collapsedColumns,
7352
+ measure: k.measure,
7353
+ dateField: k.dateField,
7354
+ startField: k.startField,
7355
+ endField: k.endField,
7356
+ imageField: k.imageField,
7357
+ sorts: k.sorts,
7358
+ order: viewOrderOf(data),
7359
+ ruleId: k.ruleId,
7360
+ ...k.filters ? { filters: k.filters } : {},
7361
+ where: k.where,
7362
+ // The older table's own default view, carried in by the mover, IS this table's default —
7363
+ // the mover wrote the row's column, which `custom.views` does not return. Read as anything
7364
+ // else, the page would seed a second "All records" beside it and open on that.
7365
+ isDefault: k.isDefault || k.movedFrom?.["kind"] === "default_view",
7366
+ presentation: k.presentation,
7367
+ movedFrom: k.movedFrom
7368
+ };
7369
+ }
7370
+ async function declareView(client, spec) {
7371
+ return client.viewDeclare({
7372
+ table_id: spec.subject,
7373
+ spec: {
7374
+ name: spec.name,
7375
+ ...spec.filters ? { filters: spec.filters } : {},
7376
+ definition: viewDocument(spec)
7377
+ }
7378
+ });
7379
+ }
7380
+ async function saveViewPatch(client, tableId, viewId, patch) {
7381
+ const definition = viewPatchDocument(patch);
7382
+ const renamed = patch.name !== void 0 && patch.name.trim() !== "";
7383
+ const refiltered = patch.filters !== void 0;
7384
+ return client.viewDeclare({
7385
+ table_id: tableId,
7386
+ spec: {
7387
+ view_id: viewId,
7388
+ ...renamed ? { name: patch.name.trim() } : {},
7389
+ ...refiltered ? { filters: patch.filters ?? null } : {},
7390
+ definition
7391
+ }
7392
+ });
7393
+ }
7394
+ function viewPatchDocument(patch) {
7395
+ const { name: _name, subject: _subject, filters: _filters, ...keys } = patch;
7396
+ return viewSettingsPatch(keys);
7397
+ }
7398
+ function viewPatchFor(view, patch) {
7399
+ if (!view.isDefault) return patch;
7400
+ const { layout: _layout, isDefault: _isDefault, ...kept } = patch;
7401
+ return kept;
7402
+ }
7403
+ async function designateView(client, tableId, viewId, layout) {
7404
+ return client.viewDesignate({ table_id: tableId, view_id: viewId, layout });
7405
+ }
7406
+
7407
+ // src/viewLook.ts
7408
+ var LOOK_KEYS = [
7409
+ "layout",
7410
+ "groupField",
7411
+ "swimlaneField",
7412
+ "collapsedColumns",
7413
+ "measure",
7414
+ "dateField",
7415
+ "startField",
7416
+ "endField",
7417
+ "imageField",
7418
+ "sorts",
7419
+ "where",
7420
+ "presentation"
7421
+ ];
7422
+ function lookPart(patch) {
7423
+ const out = {};
7424
+ for (const key of LOOK_KEYS) {
7425
+ if (patch[key] !== void 0) out[key] = patch[key];
7426
+ }
7427
+ return out;
7428
+ }
7429
+ function lookDocument(look) {
7430
+ return viewSettingsPatch(look);
7431
+ }
7432
+ function lookFromDocument(doc) {
7433
+ const data = doc ?? {};
7434
+ const read = viewSettings(data);
7435
+ const out = {};
7436
+ const has = (key) => Object.prototype.hasOwnProperty.call(data, key);
7437
+ if (has("layout") && VIEW_LAYOUTS.includes(String(data["layout"]))) {
7438
+ out.layout = data["layout"];
7439
+ }
7440
+ if (has("group_field")) out.groupField = read.groupField;
7441
+ if (has("swimlane_field")) out.swimlaneField = read.swimlaneField;
7442
+ if (has("collapsed_columns")) out.collapsedColumns = read.collapsedColumns;
7443
+ if (has("measure")) out.measure = read.measure;
7444
+ if (has("date_field")) out.dateField = read.dateField;
7445
+ if (has("start_field")) out.startField = read.startField;
7446
+ if (has("end_field")) out.endField = read.endField;
7447
+ if (has("image_field")) out.imageField = read.imageField;
7448
+ if (has("sorts")) out.sorts = read.sorts;
7449
+ if (has("where")) out.where = read.where;
7450
+ if (has("presentation")) out.presentation = parseGridPresentation(data["presentation"]);
7451
+ return out;
7452
+ }
7453
+ function applyLook(view, look) {
7454
+ if (!look) return view;
7455
+ return { ...view, ...look };
7456
+ }
7457
+ function lookDiffers(view, look) {
7458
+ if (!look) return false;
7459
+ const shown4 = applyLook(view, look);
7460
+ return LOOK_KEYS.some(
7461
+ (key) => look[key] !== void 0 && JSON.stringify(shown4[key] ?? null) !== JSON.stringify(view[key] ?? null)
7462
+ );
7463
+ }
7464
+ function saveToViewPlan(view, look) {
7465
+ const patch = viewPatchFor(view, look);
7466
+ const left = {};
7467
+ if (look.layout !== void 0 && patch.layout === void 0 && look.layout !== view.layout) left.layout = look.layout;
7468
+ return { patch, left: Object.keys(left).length > 0 ? left : null };
7469
+ }
7470
+ async function readLooks(client, tableId) {
7471
+ const answered = await client.viewLookRead({ table_id: tableId });
7472
+ if (!answered.ok) return answered;
7473
+ const out = {};
7474
+ for (const [viewId, row] of Object.entries(answered.data ?? {})) {
7475
+ out[viewId] = lookFromDocument(row?.look ?? null);
7476
+ }
7477
+ return { ok: true, data: out };
7478
+ }
7479
+ async function keepLook(client, tableId, viewId, look) {
7480
+ const doc = look ? lookDocument(look) : null;
7481
+ return client.viewLookSet({ table_id: tableId, view_id: viewId, look: doc && Object.keys(doc).length > 0 ? doc : null });
7482
+ }
7483
+ var LOOK_WORDS = {
7484
+ /** The reason, for a tooltip or a screen reader — never a line on the page (interface-text). */
7485
+ yours: "You changed how this view looks. Only you see it.",
7486
+ /** The visible label for the same fact. */
7487
+ yoursLabel: "Only you see this",
7488
+ saveToView: "Save to view",
7489
+ saveAsNew: "Save as new view",
7490
+ reset: "Reset to view",
7491
+ /** The one button your look's doors fold into when the toolbar row has no room for three. */
7492
+ folded: "Your look",
7493
+ newName: "Name for the new view"
7494
+ };
7495
+
7496
+ // src/firstPage.ts
7497
+ var EMBEDDED_PAGE_SIZE = 50;
7498
+
7499
+ // src/embeddedLook.tsx
7036
7500
  var EMBEDDED_LOOK = { blankEmpty: true, choiceDot: true, plainRelation: true };
7037
7501
  var EmbeddedLookContext = createContext5(null);
7038
7502
  var EmbeddedLookProvider = EmbeddedLookContext.Provider;
@@ -7050,7 +7514,6 @@ var EMBEDDED_TABLE_CLASS = [
7050
7514
  "[&_thead_th:last-child]:border-r-0"
7051
7515
  ].join(" ");
7052
7516
  var EMBEDDED_PLAIN_FOOTER_CLASS = "[&_tfoot]:bg-transparent [&_tfoot]:border-t-0";
7053
- var EMBEDDED_PAGE_SIZE = 50;
7054
7517
 
7055
7518
  // src/enumLabels.ts
7056
7519
  function exhaustiveValuesOf() {
@@ -15196,9 +15659,17 @@ function useMergedGridControls(args) {
15196
15659
  const row = rows.find((r) => r.id === rowId);
15197
15660
  if (row) args.duplicate(row);
15198
15661
  },
15662
+ // ARCHIVE FROM THE ROW MENU NEVER FAILS SILENTLY (Spaces round 33): the confirm opens once the menu
15663
+ // layer has closed — opened from inside the menu's own select callback, the closing menu's focus
15664
+ // return dismissed the dialog the moment it opened (inside a contenteditable host every time), so the
15665
+ // press did nothing. A row the page no longer holds says so.
15199
15666
  archiveRow: (rowId) => {
15200
15667
  const row = rows.find((r) => r.id === rowId);
15201
- if (row) args.askDelete(row);
15668
+ if (!row) {
15669
+ notify?.error("This record is no longer on the page. Open it to archive it.");
15670
+ return;
15671
+ }
15672
+ afterMenuLayerCloses(() => args.askDelete(row));
15202
15673
  },
15203
15674
  ...args.onOpenRecord ? { openRecord: (rowId) => args.onOpenRecord?.(rowId) } : {},
15204
15675
  ...args.onAskWhoChanged ? { askWhoChanged: (columnId2, rowId) => args.onAskWhoChanged?.(columnId2, rowId) } : {},
@@ -15557,6 +16028,10 @@ function backLinkSentence(column, readers = [], tableName2 = null) {
15557
16028
  if (readers.length === 0) return base;
15558
16029
  return `${readersSentence(readers.length, tableName2)} ${base}`;
15559
16030
  }
16031
+ function afterMenuLayerCloses(next) {
16032
+ if (typeof window === "undefined") return next();
16033
+ requestAnimationFrame(() => requestAnimationFrame(next));
16034
+ }
15560
16035
  function selectOpenCellText() {
15561
16036
  if (typeof window === "undefined") return;
15562
16037
  requestAnimationFrame(
@@ -21970,378 +22445,6 @@ import { useEffect as useEffect50, useMemo as useMemo35, useRef as useRef35, use
21970
22445
  import { useRecordsClient as useRecordsClient38 } from "@ai-matrx/records/react";
21971
22446
  import { stableFilterKey as stableFilterKey2 } from "@ai-matrx/records";
21972
22447
  import { Button as Button41, ConfirmDialog as ConfirmDialog4, Popover as Popover7, PopoverContent as PopoverContent7, PopoverTrigger as PopoverTrigger7, cn as cn35 } from "@ai-matrx/design-system";
21973
-
21974
- // src/viewKeys.ts
21975
- var STORE_KEEPS_TIMELINE = true;
21976
- var ALL = ["grid", "kanban", "calendar", "gallery", ...STORE_KEEPS_TIMELINE ? ["timeline"] : [], "list"];
21977
- var HIDES = ["grid", "gallery", "kanban", "calendar", ...STORE_KEEPS_TIMELINE ? ["timeline"] : [], "list"];
21978
- var GRIDS = ["grid"];
21979
- var VIEW_KEYS = [
21980
- { path: "layout", shape: "kind", layouts: ALL, writer: "caller" },
21981
- { path: "filters", shape: "filter", layouts: ALL, writer: "caller" },
21982
- { path: "where", shape: "rule", layouts: ALL, writer: "caller" },
21983
- { path: "group_field", shape: "field", layouts: ["kanban", ...STORE_KEEPS_TIMELINE ? ["timeline"] : []], writer: "caller" },
21984
- { path: "swimlane_field", shape: "field:lane", layouts: ["kanban"], writer: "caller" },
21985
- { path: "collapsed_columns", shape: "values", layouts: ["kanban"], writer: "caller" },
21986
- { path: "measure", shape: "field:number", layouts: ["kanban"], writer: "caller" },
21987
- { path: "date_field", shape: "field:date", layouts: ["calendar"], writer: "caller" },
21988
- ...STORE_KEEPS_TIMELINE ? [
21989
- { path: "start_field", shape: "field:date", layouts: ["timeline"], writer: "caller" },
21990
- { path: "end_field", shape: "field:date", layouts: ["calendar", "timeline"], writer: "caller" }
21991
- ] : [],
21992
- { path: "image_field", shape: "field", layouts: ["gallery"], writer: "caller" },
21993
- { path: "sorts", shape: "sorts", layouts: ALL, writer: "caller" },
21994
- { path: "rule_id", shape: "uuid", layouts: ALL, writer: "caller" },
21995
- { path: "is_default", shape: "boolean", layouts: ALL, writer: "caller" },
21996
- { path: "presentation", shape: "presentation", layouts: ALL, writer: "caller" },
21997
- { path: "presentation.style", shape: "style", layouts: ALL, writer: "caller" },
21998
- { path: "presentation.formats", shape: "object", layouts: GRIDS, writer: "caller" },
21999
- { path: "presentation.frozen", shape: "fields", layouts: GRIDS, writer: "caller" },
22000
- { path: "presentation.grouping", shape: "grouping", layouts: ["grid", "gallery", "list"], writer: "caller" },
22001
- { path: "presentation.widths", shape: "widths", layouts: GRIDS, writer: "caller" },
22002
- { path: "presentation.rowHeight", shape: "row_height", layouts: GRIDS, writer: "caller" },
22003
- { path: "presentation.hiddenFields", shape: "fields", layouts: HIDES, writer: "caller" },
22004
- { path: "presentation.cardHidden", shape: "fields", layouts: ["kanban", "calendar", ...STORE_KEEPS_TIMELINE ? ["timeline"] : [], "list", "gallery"], writer: "caller" },
22005
- { path: "presentation.columnOrder", shape: "fields", layouts: GRIDS, writer: "caller" },
22006
- { path: "presentation.gallerySize", shape: "gallery_size", layouts: ["gallery"], writer: "caller" },
22007
- { path: "presentation.wrap", shape: "boolean", layouts: GRIDS, writer: "caller" },
22008
- { path: "presentation.wrapColumns", shape: "fields", layouts: GRIDS, writer: "caller" },
22009
- { path: "presentation.fit", shape: "fit", layouts: GRIDS, writer: "caller" },
22010
- { path: "presentation.freezeFirst", shape: "boolean", layouts: GRIDS, writer: "caller" },
22011
- { path: "presentation.footer", shape: "footer", layouts: GRIDS, writer: "caller" },
22012
- { path: "presentation.summaries", shape: "summaries", layouts: GRIDS, writer: "caller" },
22013
- { path: "grid", shape: "grid", layouts: GRIDS, writer: "caller" },
22014
- { path: "hidden_fields", shape: "input", layouts: HIDES, writer: "input" },
22015
- { path: "table_id", shape: "server", layouts: ALL, writer: "server" },
22016
- { path: "order", shape: "server", layouts: GRIDS, writer: "server" },
22017
- { path: "moved_from", shape: "server", layouts: ALL, writer: "server" }
22018
- ];
22019
- var STORE_KEPT_LAYOUTS = ["grid", "kanban", "calendar", "gallery", ...STORE_KEEPS_TIMELINE ? ["timeline"] : [], "list"];
22020
- function storeKeepsLayout(layout) {
22021
- return STORE_KEPT_LAYOUTS.includes(layout);
22022
- }
22023
- function viewKeysFor(layout) {
22024
- return VIEW_KEYS.filter((k) => k.layouts.includes(layout)).map((k) => k.path);
22025
- }
22026
- var KINDS = STORE_KEPT_LAYOUTS;
22027
- function viewSettings(definition, rowFilters) {
22028
- const data = definition ?? {};
22029
- const text2 = (key) => {
22030
- const v = data[key];
22031
- return typeof v === "string" && v !== "" ? v : null;
22032
- };
22033
- const layout = String(data["layout"] ?? "grid");
22034
- const flat = rowFilters ?? (isObject2(data["filters"]) ? data["filters"] : null);
22035
- const moved = data["moved_from"];
22036
- return {
22037
- layout: KINDS.includes(layout) ? layout : "grid",
22038
- filters: flat && Object.keys(flat).length > 0 ? flat : null,
22039
- where: isRule(data["where"]) ? data["where"] : null,
22040
- groupField: text2("group_field"),
22041
- swimlaneField: text2("swimlane_field"),
22042
- collapsedColumns: Array.isArray(data["collapsed_columns"]) ? data["collapsed_columns"].filter((v) => ["string", "number", "boolean"].includes(typeof v)).map(String) : [],
22043
- measure: text2("measure"),
22044
- dateField: text2("date_field"),
22045
- startField: text2("start_field"),
22046
- endField: text2("end_field"),
22047
- imageField: text2("image_field"),
22048
- sorts: parseSorts(data["sorts"]),
22049
- ruleId: text2("rule_id"),
22050
- isDefault: data["is_default"] === true || data["is_default"] === "true",
22051
- presentation: parseGridPresentation(data["presentation"]),
22052
- movedFrom: isObject2(moved) ? moved : null
22053
- };
22054
- }
22055
- function viewSettingsFor(settings, layout) {
22056
- const reads = new Set(viewKeysFor(layout));
22057
- const out = { layout: settings.layout };
22058
- const take = (key, path) => {
22059
- if (reads.has(path)) out[key] = settings[key];
22060
- };
22061
- take("filters", "filters");
22062
- take("where", "where");
22063
- take("groupField", "group_field");
22064
- take("swimlaneField", "swimlane_field");
22065
- take("collapsedColumns", "collapsed_columns");
22066
- take("measure", "measure");
22067
- take("dateField", "date_field");
22068
- take("startField", "start_field");
22069
- take("endField", "end_field");
22070
- take("imageField", "image_field");
22071
- take("sorts", "sorts");
22072
- take("ruleId", "rule_id");
22073
- take("isDefault", "is_default");
22074
- const p = settings.presentation;
22075
- const look = {};
22076
- for (const [key, value] of Object.entries(p)) {
22077
- if (reads.has(`presentation.${key}`)) look[key] = value;
22078
- }
22079
- out.presentation = look;
22080
- return out;
22081
- }
22082
- function viewSettingsPatch(patch) {
22083
- const out = {};
22084
- const field2 = (key, path) => {
22085
- if (patch[key] !== void 0) out[path] = patch[key] || null;
22086
- };
22087
- if (patch.layout !== void 0 && storeKeepsLayout(patch.layout)) out["layout"] = patch.layout;
22088
- field2("groupField", "group_field");
22089
- field2("swimlaneField", "swimlane_field");
22090
- field2("measure", "measure");
22091
- field2("dateField", "date_field");
22092
- if (STORE_KEEPS_TIMELINE) {
22093
- field2("startField", "start_field");
22094
- field2("endField", "end_field");
22095
- }
22096
- field2("imageField", "image_field");
22097
- field2("ruleId", "rule_id");
22098
- if (patch.collapsedColumns !== void 0) out["collapsed_columns"] = patch.collapsedColumns ?? [];
22099
- if (patch.sorts !== void 0) out["sorts"] = patch.sorts ?? [];
22100
- if (patch.where !== void 0) out["where"] = patch.where ?? null;
22101
- if (patch.isDefault !== void 0) out["is_default"] = patch.isDefault === true;
22102
- if (patch.presentation !== void 0) {
22103
- out["presentation"] = !patch.presentation || presentationIsEmpty(patch.presentation) ? null : JSON.parse(presentationDocument(patch.presentation));
22104
- }
22105
- return out;
22106
- }
22107
- function isObject2(raw) {
22108
- return Boolean(raw) && typeof raw === "object" && !Array.isArray(raw);
22109
- }
22110
- function isRule(raw) {
22111
- if (!isObject2(raw)) return false;
22112
- const node = raw;
22113
- return typeof node.op === "string" && Array.isArray(node.args);
22114
- }
22115
- function parseSorts(raw) {
22116
- let list = raw;
22117
- if (typeof raw === "string") {
22118
- try {
22119
- list = raw.trim() === "" ? [] : JSON.parse(raw);
22120
- } catch {
22121
- return [];
22122
- }
22123
- }
22124
- if (!Array.isArray(list)) return [];
22125
- return list.filter((s) => isObject2(s) && typeof s.field === "string").map((s) => ({ field: s.field, direction: s.direction === "desc" ? "desc" : "asc" }));
22126
- }
22127
-
22128
- // src/views.ts
22129
- var VIEW_LAYOUTS = ["grid", "kanban", "calendar", "gallery", "timeline", "list"];
22130
- var LAYOUT_LABEL = {
22131
- grid: "Grid",
22132
- kanban: "Kanban",
22133
- calendar: "Calendar",
22134
- gallery: "Gallery",
22135
- timeline: "Timeline",
22136
- list: "List"
22137
- };
22138
- function wordsList(words2) {
22139
- const clean = words2.map((w) => w.trim()).filter((w) => w !== "");
22140
- if (clean.length <= 1) return clean.join("");
22141
- return `${clean.slice(0, -1).join(", ")} and ${clean[clean.length - 1]}`;
22142
- }
22143
- var VIEW_NEEDS = {
22144
- grid: null,
22145
- // A list reads nothing a table can lack: rows, titled, grouped only when the person chooses.
22146
- list: null,
22147
- kanban: {
22148
- kind: "choice",
22149
- viewKey: "groupField",
22150
- blocking: true,
22151
- starter: { label: "Status", type: "select", options: ["To do", "In progress", "Done"] },
22152
- pick: "Pick the choice column the board's columns come from.",
22153
- missing: "This table has no choice column to make the board's columns from.",
22154
- does: (label) => `Make it work adds a ${label} column with the choices below and groups the board by it.`,
22155
- ask: (label, options) => `Add a choice column called ${label} to this table${options.length > 0 ? ` with the choices ${wordsList(options)}` : ""}, then group the Kanban board by it.`
22156
- },
22157
- calendar: {
22158
- kind: "date",
22159
- viewKey: "dateField",
22160
- blocking: true,
22161
- starter: { label: "Date", type: "datetime" },
22162
- pick: "Pick the date column the calendar places records by.",
22163
- missing: "This table has no date column to place records on.",
22164
- does: (label) => `Make it work adds a ${label} column and lays the calendar out by it.`,
22165
- ask: (label) => `Add a date column called ${label} to this table, then lay the Calendar out by it.`
22166
- },
22167
- gallery: {
22168
- kind: "file",
22169
- viewKey: "imageField",
22170
- blocking: false,
22171
- starter: { label: "Photo", type: "attachment" },
22172
- pick: "Pick the file column each card shows as its picture.",
22173
- missing: "The cards have no picture because this table has no file column.",
22174
- does: (label) => `Make it work adds a ${label} column and shows it on each card.`,
22175
- ask: (label) => `Add a file column called ${label} to this table, then show it as the picture on each Gallery card.`
22176
- },
22177
- timeline: {
22178
- kind: "date",
22179
- viewKey: "dateField",
22180
- blocking: true,
22181
- starter: { label: "Start", type: "datetime" },
22182
- pick: "Pick the date column each bar starts on.",
22183
- missing: "This table has no date column to place records on.",
22184
- does: (label) => `Make it work adds a ${label} column and lays the timeline out by it.`,
22185
- ask: (label) => `Add a date column called ${label} to this table, then lay the Timeline out by it.`
22186
- }
22187
- };
22188
- function freeColumnName(wanted, taken) {
22189
- const held = new Set(taken.map((t) => t.trim().toLowerCase()));
22190
- const base = wanted.trim() === "" ? "Column" : wanted.trim();
22191
- if (!held.has(base.toLowerCase())) return base;
22192
- for (let n = 2; n < 100; n += 1) {
22193
- const next = `${base} ${n}`;
22194
- if (!held.has(next.toLowerCase())) return next;
22195
- }
22196
- return base;
22197
- }
22198
- function viewOrderOf(definition) {
22199
- const raw = definition?.["order"];
22200
- return raw === "manual" ? "manual" : raw === "sorted" ? "sorted" : null;
22201
- }
22202
- function defaultViewOf(views) {
22203
- return views.find((v) => v.isDefault);
22204
- }
22205
- var LAYOUT_FIELD_KIND = {
22206
- grid: null,
22207
- kanban: "choice",
22208
- calendar: "date",
22209
- gallery: "file",
22210
- timeline: "date",
22211
- list: null
22212
- };
22213
- var PAGE_VIEWS = [...VIEW_LAYOUTS, "dashboards", "archived"];
22214
- var PAGE_VIEW_LABEL = {
22215
- ...LAYOUT_LABEL,
22216
- dashboards: "Dashboards",
22217
- archived: "Archived"
22218
- };
22219
- var PAGE_VIEW_SPELLINGS = {
22220
- grid: "grid",
22221
- table: "grid",
22222
- rows: "grid",
22223
- list: "list",
22224
- kanban: "kanban",
22225
- board: "kanban",
22226
- pipeline: "kanban",
22227
- calendar: "calendar",
22228
- month: "calendar",
22229
- schedule: "calendar",
22230
- gallery: "gallery",
22231
- cards: "gallery",
22232
- timeline: "timeline",
22233
- gantt: "timeline",
22234
- dashboards: "dashboards",
22235
- dashboard: "dashboards",
22236
- charts: "dashboards",
22237
- archived: "archived",
22238
- archive: "archived",
22239
- // The grid's row action says "Delete" and the store soft-deletes, so the word
22240
- // a person types is as likely to be the one the button used as the one this
22241
- // view is called. Both land in the same place; neither dies.
22242
- deleted: "archived",
22243
- trash: "archived",
22244
- removed: "archived"
22245
- };
22246
- function pageViewFromParam(raw) {
22247
- if (typeof raw !== "string") return null;
22248
- const token = raw.trim().toLowerCase();
22249
- if (token === "") return null;
22250
- return PAGE_VIEW_SPELLINGS[token] ?? null;
22251
- }
22252
- function unknownViewLine(raw) {
22253
- return `The link asked for a view called \u201C${raw}\u201D, which this table has no view for, so you are looking at ${PAGE_VIEW_LABEL.grid}. The views this table has are ${PAGE_VIEWS.map((v) => PAGE_VIEW_LABEL[v]).join(", ")} \u2014 pick one above and the address follows it.`;
22254
- }
22255
- function viewDocument(spec) {
22256
- const out = {
22257
- // A layout the store does not keep yet is not sent: the view is saved as a grid (`STORE_KEPT_LAYOUTS`).
22258
- layout: storeKeepsLayout(spec.layout) ? spec.layout : "grid",
22259
- sorts: spec.sorts ?? [],
22260
- is_default: spec.isDefault === true
22261
- };
22262
- if (spec.groupField) out["group_field"] = spec.groupField;
22263
- if (spec.swimlaneField) out["swimlane_field"] = spec.swimlaneField;
22264
- if (spec.collapsedColumns && spec.collapsedColumns.length > 0) out["collapsed_columns"] = spec.collapsedColumns;
22265
- if (spec.measure) out["measure"] = spec.measure;
22266
- if (spec.dateField) out["date_field"] = spec.dateField;
22267
- if (STORE_KEEPS_TIMELINE && spec.startField) out["start_field"] = spec.startField;
22268
- if (STORE_KEEPS_TIMELINE && spec.endField) out["end_field"] = spec.endField;
22269
- if (spec.imageField) out["image_field"] = spec.imageField;
22270
- if (spec.ruleId) out["rule_id"] = spec.ruleId;
22271
- if (spec.where) out["where"] = spec.where;
22272
- if (spec.presentation && !presentationIsEmpty(spec.presentation)) {
22273
- out["presentation"] = JSON.parse(presentationDocument(spec.presentation));
22274
- }
22275
- return out;
22276
- }
22277
- function viewFromRow(row) {
22278
- const data = row.definition ?? {};
22279
- const k = viewSettings(data, row.filters && Object.keys(row.filters).length > 0 ? row.filters : null);
22280
- const tableFromDefinition = typeof data["table_id"] === "string" && data["table_id"] !== "" ? data["table_id"] : null;
22281
- return {
22282
- id: row.view_id,
22283
- name: row.name || "Untitled view",
22284
- subject: row.table_id ?? tableFromDefinition ?? "",
22285
- layout: k.layout,
22286
- groupField: k.groupField,
22287
- swimlaneField: k.swimlaneField,
22288
- collapsedColumns: k.collapsedColumns,
22289
- measure: k.measure,
22290
- dateField: k.dateField,
22291
- startField: k.startField,
22292
- endField: k.endField,
22293
- imageField: k.imageField,
22294
- sorts: k.sorts,
22295
- order: viewOrderOf(data),
22296
- ruleId: k.ruleId,
22297
- ...k.filters ? { filters: k.filters } : {},
22298
- where: k.where,
22299
- // The older table's own default view, carried in by the mover, IS this table's default —
22300
- // the mover wrote the row's column, which `custom.views` does not return. Read as anything
22301
- // else, the page would seed a second "All records" beside it and open on that.
22302
- isDefault: k.isDefault || k.movedFrom?.["kind"] === "default_view",
22303
- presentation: k.presentation,
22304
- movedFrom: k.movedFrom
22305
- };
22306
- }
22307
- async function declareView(client, spec) {
22308
- return client.viewDeclare({
22309
- table_id: spec.subject,
22310
- spec: {
22311
- name: spec.name,
22312
- ...spec.filters ? { filters: spec.filters } : {},
22313
- definition: viewDocument(spec)
22314
- }
22315
- });
22316
- }
22317
- async function saveViewPatch(client, tableId, viewId, patch) {
22318
- const definition = viewPatchDocument(patch);
22319
- const renamed = patch.name !== void 0 && patch.name.trim() !== "";
22320
- const refiltered = patch.filters !== void 0;
22321
- return client.viewDeclare({
22322
- table_id: tableId,
22323
- spec: {
22324
- view_id: viewId,
22325
- ...renamed ? { name: patch.name.trim() } : {},
22326
- ...refiltered ? { filters: patch.filters ?? null } : {},
22327
- definition
22328
- }
22329
- });
22330
- }
22331
- function viewPatchDocument(patch) {
22332
- const { name: _name, subject: _subject, filters: _filters, ...keys } = patch;
22333
- return viewSettingsPatch(keys);
22334
- }
22335
- function viewPatchFor(view, patch) {
22336
- if (!view.isDefault) return patch;
22337
- const { layout: _layout, isDefault: _isDefault, ...kept } = patch;
22338
- return kept;
22339
- }
22340
- async function designateView(client, tableId, viewId, layout) {
22341
- return client.viewDesignate({ table_id: tableId, view_id: viewId, layout });
22342
- }
22343
-
22344
- // src/handOrder.tsx
22345
22448
  import { jsx as jsx71, jsxs as jsxs64 } from "react/jsx-runtime";
22346
22449
  function sortArrow(direction) {
22347
22450
  return direction === "asc" ? "\u2191" : "\u2193";
@@ -24795,7 +24898,7 @@ import {
24795
24898
  } from "@ai-matrx/records/data/react";
24796
24899
 
24797
24900
  // ../entity-data/src/core/client.ts
24798
- import { asRecordsDataSource } from "@ai-matrx/records/core";
24901
+ import { asRecordsDataSource as asRecordsDataSource2 } from "@ai-matrx/records/core";
24799
24902
 
24800
24903
  // ../entity-data/src/core/errors.ts
24801
24904
  import { mapPgError as mapPgError2 } from "@ai-matrx/records/core";
@@ -24844,7 +24947,7 @@ function refusal2(code, message, hint) {
24844
24947
  var OP_ID_HEADER = "x-matrx-op-id";
24845
24948
  var ENTITY_DOOR_SCHEMA = "platform";
24846
24949
  function createEntityClient(options) {
24847
- const source = asRecordsDataSource(options.dataSource ?? options.supabase);
24950
+ const source = asRecordsDataSource2(options.dataSource ?? options.supabase);
24848
24951
  let warnedHeaderless = false;
24849
24952
  function withHeaders(pending, headers) {
24850
24953
  if (!headers) return;
@@ -30435,95 +30538,6 @@ function useSystemTable(spec) {
30435
30538
  return state;
30436
30539
  }
30437
30540
 
30438
- // src/viewLook.ts
30439
- var LOOK_KEYS = [
30440
- "layout",
30441
- "groupField",
30442
- "swimlaneField",
30443
- "collapsedColumns",
30444
- "measure",
30445
- "dateField",
30446
- "startField",
30447
- "endField",
30448
- "imageField",
30449
- "sorts",
30450
- "where",
30451
- "presentation"
30452
- ];
30453
- function lookPart(patch) {
30454
- const out = {};
30455
- for (const key of LOOK_KEYS) {
30456
- if (patch[key] !== void 0) out[key] = patch[key];
30457
- }
30458
- return out;
30459
- }
30460
- function lookDocument(look) {
30461
- return viewSettingsPatch(look);
30462
- }
30463
- function lookFromDocument(doc) {
30464
- const data = doc ?? {};
30465
- const read = viewSettings(data);
30466
- const out = {};
30467
- const has = (key) => Object.prototype.hasOwnProperty.call(data, key);
30468
- if (has("layout") && VIEW_LAYOUTS.includes(String(data["layout"]))) {
30469
- out.layout = data["layout"];
30470
- }
30471
- if (has("group_field")) out.groupField = read.groupField;
30472
- if (has("swimlane_field")) out.swimlaneField = read.swimlaneField;
30473
- if (has("collapsed_columns")) out.collapsedColumns = read.collapsedColumns;
30474
- if (has("measure")) out.measure = read.measure;
30475
- if (has("date_field")) out.dateField = read.dateField;
30476
- if (has("start_field")) out.startField = read.startField;
30477
- if (has("end_field")) out.endField = read.endField;
30478
- if (has("image_field")) out.imageField = read.imageField;
30479
- if (has("sorts")) out.sorts = read.sorts;
30480
- if (has("where")) out.where = read.where;
30481
- if (has("presentation")) out.presentation = parseGridPresentation(data["presentation"]);
30482
- return out;
30483
- }
30484
- function applyLook(view, look) {
30485
- if (!look) return view;
30486
- return { ...view, ...look };
30487
- }
30488
- function lookDiffers(view, look) {
30489
- if (!look) return false;
30490
- const shown4 = applyLook(view, look);
30491
- return LOOK_KEYS.some(
30492
- (key) => look[key] !== void 0 && JSON.stringify(shown4[key] ?? null) !== JSON.stringify(view[key] ?? null)
30493
- );
30494
- }
30495
- function saveToViewPlan(view, look) {
30496
- const patch = viewPatchFor(view, look);
30497
- const left = {};
30498
- if (look.layout !== void 0 && patch.layout === void 0 && look.layout !== view.layout) left.layout = look.layout;
30499
- return { patch, left: Object.keys(left).length > 0 ? left : null };
30500
- }
30501
- async function readLooks(client, tableId) {
30502
- const answered = await client.viewLookRead({ table_id: tableId });
30503
- if (!answered.ok) return answered;
30504
- const out = {};
30505
- for (const [viewId, row] of Object.entries(answered.data ?? {})) {
30506
- out[viewId] = lookFromDocument(row?.look ?? null);
30507
- }
30508
- return { ok: true, data: out };
30509
- }
30510
- async function keepLook(client, tableId, viewId, look) {
30511
- const doc = look ? lookDocument(look) : null;
30512
- return client.viewLookSet({ table_id: tableId, view_id: viewId, look: doc && Object.keys(doc).length > 0 ? doc : null });
30513
- }
30514
- var LOOK_WORDS = {
30515
- /** The reason, for a tooltip or a screen reader — never a line on the page (interface-text). */
30516
- yours: "You changed how this view looks. Only you see it.",
30517
- /** The visible label for the same fact. */
30518
- yoursLabel: "Only you see this",
30519
- saveToView: "Save to view",
30520
- saveAsNew: "Save as new view",
30521
- reset: "Reset to view",
30522
- /** The one button your look's doors fold into when the toolbar row has no room for three. */
30523
- folded: "Your look",
30524
- newName: "Name for the new view"
30525
- };
30526
-
30527
30541
  // src/ViewSwitcher.tsx
30528
30542
  import { useEffect as useEffect76, useMemo as useMemo56, useState as useState89 } from "react";
30529
30543
  import { mayDrag as mayDrag4, useFields as useFields18, useRecordPage as useRecordPage2, useRecordsClient as useRecordsClient64, useTable as useTable17 } from "@ai-matrx/records/react";
@@ -35232,7 +35246,7 @@ function ArchivedPortals({
35232
35246
  }
35233
35247
 
35234
35248
  // src/ViewBar.tsx
35235
- import { useCallback as useCallback42, useEffect as useEffect79, useState as useState92 } from "react";
35249
+ import { useCallback as useCallback42, useEffect as useEffect79, useRef as useRef48, useState as useState92 } from "react";
35236
35250
  import { useRecordsClient as useRecordsClient67 } from "@ai-matrx/records/react";
35237
35251
  import { Badge as Badge14, Button as Button70, Input as Input6, Popover as Popover10, PopoverContent as PopoverContent10, PopoverTrigger as PopoverTrigger10, Skeleton as Skeleton15, cn as cn58 } from "@ai-matrx/design-system";
35238
35252
 
@@ -35284,16 +35298,20 @@ function seedIsSatisfied(seed, views) {
35284
35298
  if (views.some((v) => v.name === seed.name)) return true;
35285
35299
  return seed.isDefault === true && views.some((v) => v.isDefault);
35286
35300
  }
35287
- function ViewBar({ tableId, activeViewId, onActiveView, seed, settings, onViews, className }) {
35301
+ function ViewBar({ tableId, activeViewId, onActiveView, seed, settings, onViews, initialViews, className }) {
35288
35302
  const client = useRecordsClient67();
35289
35303
  const claimSeed = useSeedGuard();
35290
35304
  const table = useTable19(tableId);
35291
35305
  const rights = useTableRights(table.data);
35292
35306
  const notify = useRecordsUi().notify;
35293
- const [views, setViews] = useState92(null);
35307
+ const [views, setViews] = useState92(() => initialViews ?? null);
35294
35308
  const [error, setError] = useState92(null);
35295
35309
  const [unavailable, setUnavailable] = useState92(false);
35296
- const [active, setActive] = useState92(activeViewId ?? null);
35310
+ const [active, setActive] = useState92(() => {
35311
+ if (!initialViews || initialViews.length === 0) return activeViewId ?? null;
35312
+ return (initialViews.find((v) => v.id === activeViewId) ?? defaultViewOf(initialViews) ?? initialViews[0]).id;
35313
+ });
35314
+ const readOnMount = useRef48(initialViews === void 0);
35297
35315
  const [naming, setNaming] = useState92(false);
35298
35316
  const [draftName, setDraftName] = useState92("");
35299
35317
  const [open, setOpen] = useState92(false);
@@ -35340,6 +35358,10 @@ function ViewBar({ tableId, activeViewId, onActiveView, seed, settings, onViews,
35340
35358
  onViews?.(mine);
35341
35359
  }, [client, readViews, tableId, seedKey]);
35342
35360
  useEffect79(() => {
35361
+ if (!readOnMount.current) {
35362
+ readOnMount.current = true;
35363
+ return;
35364
+ }
35343
35365
  void load();
35344
35366
  }, [load]);
35345
35367
  useEffect79(() => {
@@ -35557,7 +35579,7 @@ function ViewBar({ tableId, activeViewId, onActiveView, seed, settings, onViews,
35557
35579
  }
35558
35580
 
35559
35581
  // src/ProposalRow.tsx
35560
- import { useEffect as useEffect80, useRef as useRef48, useState as useState93 } from "react";
35582
+ import { useEffect as useEffect80, useRef as useRef49, useState as useState93 } from "react";
35561
35583
  import { useRecordsClient as useRecordsClient68 } from "@ai-matrx/records/react";
35562
35584
  import { Badge as Badge15, Button as Button71, cn as cn59 } from "@ai-matrx/design-system";
35563
35585
  import { jsx as jsx113, jsxs as jsxs103 } from "react/jsx-runtime";
@@ -35575,7 +35597,7 @@ function ProposalRow({
35575
35597
  const [settled, setSettled] = useState93(outcome ?? null);
35576
35598
  const [busy, setBusy] = useState93(false);
35577
35599
  const [confirming, setConfirming] = useState93(false);
35578
- const drawnVersion = useRef48(null);
35600
+ const drawnVersion = useRef49(null);
35579
35601
  const needsVersion = change.act === "update" && Boolean(change.record) && change.version === void 0 && !onApply && !settled;
35580
35602
  useEffect80(() => {
35581
35603
  if (needsVersion && change.record && !drawnVersion.current) drawnVersion.current = readVersionNow(client, change.record);
@@ -35665,7 +35687,7 @@ function ProposalRow({
35665
35687
  }
35666
35688
 
35667
35689
  // src/ActionInbox.tsx
35668
- import { useCallback as useCallback44, useEffect as useEffect82, useMemo as useMemo58, useRef as useRef49, useState as useState95 } from "react";
35690
+ import { useCallback as useCallback44, useEffect as useEffect82, useMemo as useMemo58, useRef as useRef50, useState as useState95 } from "react";
35669
35691
  import { useRecordsClient as useRecordsClient70 } from "@ai-matrx/records/react";
35670
35692
  import { Badge as Badge17, Button as Button73, Checkbox as Checkbox11, Skeleton as Skeleton17, cn as cn61 } from "@ai-matrx/design-system";
35671
35693
  import { toggleSelection as toggleSelection2 } from "@ai-matrx/design-system/data-table";
@@ -36567,9 +36589,9 @@ function ActionInbox({
36567
36589
  const [outcome, setOutcome] = useState95({});
36568
36590
  const [busy, setBusy] = useState95(null);
36569
36591
  const [cursor, setCursor] = useState95(0);
36570
- const listRef = useRef49(null);
36592
+ const listRef = useRef50(null);
36571
36593
  const [selected, setSelected] = useState95(/* @__PURE__ */ new Set());
36572
- const anchor = useRef49(null);
36594
+ const anchor = useRef50(null);
36573
36595
  const [deciding, setDeciding] = useState95(false);
36574
36596
  const [batchSaid, setBatchSaid] = useState95(null);
36575
36597
  const [view, setView] = useState95("inbox");
@@ -36633,9 +36655,9 @@ function ActionInbox({
36633
36655
  [client, load]
36634
36656
  );
36635
36657
  const [held, setHeld] = useState95({});
36636
- const holds2 = useRef49(/* @__PURE__ */ new Map());
36637
- const lastHold = useRef49(null);
36638
- const holdSeq = useRef49(0);
36658
+ const holds2 = useRef50(/* @__PURE__ */ new Map());
36659
+ const lastHold = useRef50(null);
36660
+ const holdSeq = useRef50(0);
36639
36661
  const [, tick] = useState95(0);
36640
36662
  useEffect82(() => {
36641
36663
  if (Object.keys(held).length === 0) return;
@@ -36692,7 +36714,7 @@ function ActionInbox({
36692
36714
  flush();
36693
36715
  };
36694
36716
  }, []);
36695
- const resumed = useRef49(false);
36717
+ const resumed = useRef50(false);
36696
36718
  useEffect82(() => {
36697
36719
  if (resumed.current || items === null || view !== "inbox") return;
36698
36720
  resumed.current = true;
@@ -37313,7 +37335,7 @@ function ActionInbox({
37313
37335
  }
37314
37336
 
37315
37337
  // src/HistoryPanel.tsx
37316
- import { useCallback as useCallback45, useEffect as useEffect83, useRef as useRef50, useState as useState96 } from "react";
37338
+ import { useCallback as useCallback45, useEffect as useEffect83, useRef as useRef51, useState as useState96 } from "react";
37317
37339
  import {
37318
37340
  useFields as useFields20,
37319
37341
  useRecordChangeRevision,
@@ -37333,9 +37355,9 @@ function HistoryPanel({ tableId, recordId, onAskAboutField, className }) {
37333
37355
  const [error, setError] = useState96(null);
37334
37356
  const [open, setOpen] = useState96(null);
37335
37357
  const [pending, setPending] = useState96({ phase: "idle" });
37336
- const seenAtPreview = useRef50(null);
37358
+ const seenAtPreview = useRef51(null);
37337
37359
  const [said2, setSaid] = useState96(null);
37338
- const listRef = useRef50(null);
37360
+ const listRef = useRef51(null);
37339
37361
  const changed = useRecordChangeRevision(recordId);
37340
37362
  const load = useCallback45(async () => {
37341
37363
  const answered = await client.recordHistory({ record_id: recordId });
@@ -37629,7 +37651,7 @@ function say(value, field2) {
37629
37651
  }
37630
37652
 
37631
37653
  // src/CommentThread.tsx
37632
- import { useCallback as useCallback46, useEffect as useEffect84, useMemo as useMemo59, useRef as useRef51, useState as useState97 } from "react";
37654
+ import { useCallback as useCallback46, useEffect as useEffect84, useMemo as useMemo59, useRef as useRef52, useState as useState97 } from "react";
37633
37655
  import {
37634
37656
  useFields as useFields21,
37635
37657
  useRecordsClient as useRecordsClient72,
@@ -37653,7 +37675,7 @@ function CommentThread({ tableId, recordId, fieldKey, className }) {
37653
37675
  const [people, setPeople] = useState97([]);
37654
37676
  const [mentionQuery, setMentionQuery] = useState97(null);
37655
37677
  const [picked, setPicked] = useState97([]);
37656
- const box = useRef51(null);
37678
+ const box = useRef52(null);
37657
37679
  const load = useCallback46(async () => {
37658
37680
  const answered = await client.commentThread({ record_id: recordId, include_resolved: showResolved });
37659
37681
  if (!answered.ok) {
@@ -37873,7 +37895,7 @@ function Line({
37873
37895
  }
37874
37896
 
37875
37897
  // src/FieldHistoryPanel.tsx
37876
- import { useCallback as useCallback47, useEffect as useEffect85, useRef as useRef52, useState as useState98 } from "react";
37898
+ import { useCallback as useCallback47, useEffect as useEffect85, useRef as useRef53, useState as useState98 } from "react";
37877
37899
  import {
37878
37900
  useFields as useFields22,
37879
37901
  useRecordsClient as useRecordsClient73,
@@ -37897,7 +37919,7 @@ function FieldHistoryPanel({
37897
37919
  const [rows, setRows] = useState98(null);
37898
37920
  const [error, setError] = useState98(null);
37899
37921
  const [pending, setPending] = useState98({ phase: "idle" });
37900
- const seenAtPreview = useRef52(null);
37922
+ const seenAtPreview = useRef53(null);
37901
37923
  const [said2, setSaid] = useState98(null);
37902
37924
  const load = useCallback47(async () => {
37903
37925
  const answered = await client.fieldHistory(
@@ -38631,13 +38653,13 @@ function useMeasureWords(key, tableId) {
38631
38653
  import {
38632
38654
  useEffect as useEffect88,
38633
38655
  useId as useId4,
38634
- useRef as useRef53,
38656
+ useRef as useRef54,
38635
38657
  useState as useState101
38636
38658
  } from "react";
38637
38659
  import { cn as cn65 } from "@ai-matrx/design-system";
38638
38660
  import { jsx as jsx119, jsxs as jsxs109 } from "react/jsx-runtime";
38639
38661
  function useMeasuredWidth(fallback = 480) {
38640
- const ref = useRef53(null);
38662
+ const ref = useRef54(null);
38641
38663
  const [width, setWidth] = useState101(fallback);
38642
38664
  useEffect88(() => {
38643
38665
  const node = ref.current;
@@ -39369,7 +39391,7 @@ function FormResultsPanel({ form, className }) {
39369
39391
  }
39370
39392
 
39371
39393
  // src/PortalBuilder.tsx
39372
- import { useCallback as useCallback48, useEffect as useEffect90, useMemo as useMemo63, useRef as useRef54, useState as useState103 } from "react";
39394
+ import { useCallback as useCallback48, useEffect as useEffect90, useMemo as useMemo63, useRef as useRef55, useState as useState103 } from "react";
39373
39395
  import { useRecordsClient as useRecordsClient74, useTables as useTables4 } from "@ai-matrx/records/react";
39374
39396
  import { BasicInput as BasicInput22, Button as Button78, Checkbox as Checkbox12, Label as Label18, Separator as Separator10, Skeleton as Skeleton22, cn as cn68 } from "@ai-matrx/design-system";
39375
39397
  import { Fragment as Fragment49, jsx as jsx122, jsxs as jsxs112 } from "react/jsx-runtime";
@@ -39459,7 +39481,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
39459
39481
  if (on) void loadFields(t.id);
39460
39482
  }
39461
39483
  const [linking, setLinking] = useState103(null);
39462
- const linkingNow = useRef54(null);
39484
+ const linkingNow = useRef55(null);
39463
39485
  async function addLinkBack(id) {
39464
39486
  const clients = (tables.data ?? []).find((t) => t.id === clientTableId);
39465
39487
  if (!clientTableId || !clients) return;
@@ -39486,7 +39508,7 @@ function PortalBuilder({ tableId, portalId, onSaved, onClose, className }) {
39486
39508
  linkingNow.current = null;
39487
39509
  setLinking(null);
39488
39510
  }
39489
- const autoLinked = useRef54(/* @__PURE__ */ new Set());
39511
+ const autoLinked = useRef55(/* @__PURE__ */ new Set());
39490
39512
  useEffect90(() => {
39491
39513
  if (linking !== null || !clientTableId) return;
39492
39514
  for (const e of Object.values(exposures)) {
@@ -41708,7 +41730,7 @@ function SubscriptionsPanel({ tableId, activeRuleId, className }) {
41708
41730
  // src/FormBuilder.tsx
41709
41731
  import { copyText as copyText7 } from "@ai-matrx/kit/clipboard";
41710
41732
  import { ErrorBox as ErrorBox7 } from "@ai-matrx/design-system";
41711
- import { useCallback as useCallback54, useEffect as useEffect97, useMemo as useMemo68, useRef as useRef56, useState as useState111 } from "react";
41733
+ import { useCallback as useCallback54, useEffect as useEffect97, useMemo as useMemo68, useRef as useRef57, useState as useState111 } from "react";
41712
41734
  import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
41713
41735
  import { SortableContext, arrayMove, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
41714
41736
  import { CSS as CSS2 } from "@dnd-kit/utilities";
@@ -41977,7 +41999,7 @@ function EmbedSnippets({ url, label }) {
41977
41999
  }
41978
42000
 
41979
42001
  // src/FormRunner.tsx
41980
- import { useCallback as useCallback53, useEffect as useEffect96, useMemo as useMemo67, useRef as useRef55, useState as useState110 } from "react";
42002
+ import { useCallback as useCallback53, useEffect as useEffect96, useMemo as useMemo67, useRef as useRef56, useState as useState110 } from "react";
41981
42003
  import { useFields as useFields24, useOptionalRecordsClient as useOptionalRecordsClient14 } from "@ai-matrx/records/react";
41982
42004
  import { Button as Button85, Label as Label22, Progress, Skeleton as Skeleton27, cn as cn77, formThemeClasses } from "@ai-matrx/design-system";
41983
42005
  import { humanizeIdentifier as humanizeIdentifier18 } from "@ai-matrx/kit/text-case";
@@ -42094,11 +42116,11 @@ function FormStage({
42094
42116
  const routes = formRoutes(form);
42095
42117
  const [routeKnown, setRouteKnown] = useState110(() => !routes || !whichAsked);
42096
42118
  const [stopped, setStopped] = useState110(null);
42097
- const answersRef = useRef55(answers);
42119
+ const answersRef = useRef56(answers);
42098
42120
  answersRef.current = answers;
42099
42121
  const choose = autoAdvance ?? form.autoAdvance ?? true;
42100
- const decoy = useRef55("");
42101
- const stage = useRef55(null);
42122
+ const decoy = useRef56("");
42123
+ const stage = useRef56(null);
42102
42124
  const questions = useMemo67(() => {
42103
42125
  const byKey = new Map((fields ?? []).map((f) => [f.key, f]));
42104
42126
  return (form.questions ?? []).map((q) => {
@@ -42161,13 +42183,13 @@ function FormStage({
42161
42183
  };
42162
42184
  }, [whichAsked, questions, answers]);
42163
42185
  const live = questions.filter((q) => !hidden[q.key]);
42164
- const told = useRef55(answers);
42186
+ const told = useRef56(answers);
42165
42187
  useEffect96(() => {
42166
42188
  if (told.current === answers) return;
42167
42189
  told.current = answers;
42168
42190
  onAnswersChange?.(answers);
42169
42191
  }, [answers, onAnswersChange]);
42170
- const resumeAt = useRef55(
42192
+ const resumeAt = useRef56(
42171
42193
  resumed && initialAnswers && Object.keys(initialAnswers).length > 0 ? questions.find((q) => {
42172
42194
  const v = initialAnswers[q.key];
42173
42195
  return v === void 0 || v === null || v === "";
@@ -42281,9 +42303,9 @@ function FormStage({
42281
42303
  if (stillMissing) refuse(stillMissing, refusalFor(stillMissing));
42282
42304
  else void submit();
42283
42305
  }
42284
- const routing = useRef55(false);
42306
+ const routing = useRef56(false);
42285
42307
  const [routingNow, setRoutingNow] = useState110(false);
42286
- const typedAhead = useRef55("");
42308
+ const typedAhead = useRef56("");
42287
42309
  useEffect96(() => {
42288
42310
  if (!routingNow) return;
42289
42311
  const hold = (event) => {
@@ -42317,7 +42339,7 @@ function FormStage({
42317
42339
  useEffect96(() => {
42318
42340
  if (!routingNow) pourTypedAhead();
42319
42341
  }, [routingNow, at, pourTypedAhead]);
42320
- const advanceRef = useRef55(advance);
42342
+ const advanceRef = useRef56(advance);
42321
42343
  advanceRef.current = advance;
42322
42344
  const chosen = useCallback53(() => {
42323
42345
  if (!oneAtATime || !choose) return;
@@ -42346,7 +42368,7 @@ function FormStage({
42346
42368
  input?.focus();
42347
42369
  pourTypedAhead();
42348
42370
  }, [index, done, started]);
42349
- const startTold = useRef55(false);
42371
+ const startTold = useRef56(false);
42350
42372
  useEffect96(() => {
42351
42373
  if (startTold.current || !onStart) return;
42352
42374
  if (Object.values(answers).some((v) => v !== void 0 && v !== null && v !== "")) {
@@ -42354,7 +42376,7 @@ function FormStage({
42354
42376
  onStart();
42355
42377
  }
42356
42378
  }, [answers, onStart]);
42357
- const shownTold = useRef55(/* @__PURE__ */ new Set());
42379
+ const shownTold = useRef56(/* @__PURE__ */ new Set());
42358
42380
  useEffect96(() => {
42359
42381
  if (!onQuestionShown || !started || done) return;
42360
42382
  for (const q of oneAtATime ? current ? [current] : [] : live) {
@@ -42729,7 +42751,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
42729
42751
  const [restoredAt, setRestoredAt] = useState111(null);
42730
42752
  const [showResults, setShowResults] = useState111(false);
42731
42753
  const [adding, setAdding] = useState111(false);
42732
- const keysBeforeAdd = useRef56(null);
42754
+ const keysBeforeAdd = useRef57(null);
42733
42755
  const [makeOnMount, setMakeOnMount] = useState111("waiting");
42734
42756
  const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
42735
42757
  const load = useCallback54(async () => {
@@ -43846,7 +43868,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
43846
43868
  }
43847
43869
 
43848
43870
  // src/DocRender.tsx
43849
- import { useCallback as useCallback56, useEffect as useEffect99, useRef as useRef57, useState as useState113 } from "react";
43871
+ import { useCallback as useCallback56, useEffect as useEffect99, useRef as useRef58, useState as useState113 } from "react";
43850
43872
  import { useRecordsClient as useRecordsClient82 } from "@ai-matrx/records/react";
43851
43873
  import { Button as Button88, Skeleton as Skeleton30, cn as cn80 } from "@ai-matrx/design-system";
43852
43874
  import { jsx as jsx135, jsxs as jsxs125 } from "react/jsx-runtime";
@@ -43857,7 +43879,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
43857
43879
  const [showing, setShowing] = useState113(null);
43858
43880
  const [error, setError] = useState113(null);
43859
43881
  const [busy, setBusy] = useState113(null);
43860
- const paper = useRef57(null);
43882
+ const paper = useRef58(null);
43861
43883
  const load = useCallback56(async () => {
43862
43884
  const [body, held] = await Promise.all([
43863
43885
  client.docRenderBody({ template_id: templateId, record_id: recordId }),
@@ -44553,7 +44575,7 @@ import { RecordsProvider as RecordsProvider2, useFields as useFields28, useRecor
44553
44575
  import { Button as Button91, CreatablePicker as CreatablePicker2 } from "@ai-matrx/design-system";
44554
44576
 
44555
44577
  // src/RecordsMount.tsx
44556
- import { useRef as useRef58 } from "react";
44578
+ import { useRef as useRef59 } from "react";
44557
44579
  import { RecordsProvider } from "@ai-matrx/records/react";
44558
44580
  import { jsx as jsx139 } from "react/jsx-runtime";
44559
44581
  var STORE_DECIDES_REASON = "This host offers exactly what the record store says this person may do: the level comes back with the table on the read door, so a control they cannot use is never drawn in the first place.";
@@ -44571,7 +44593,7 @@ function RecordsMount({
44571
44593
  return /* @__PURE__ */ jsx139(RecordsProvider, { config, children: /* @__PURE__ */ jsx139(RecordsUiProvider, { value: bound2, children: /* @__PURE__ */ jsx139(RecordLabelProvider, { children }) }) });
44572
44594
  }
44573
44595
  function useShallowStableHost(host) {
44574
- const held = useRef58(null);
44596
+ const held = useRef59(null);
44575
44597
  const next = host ?? {};
44576
44598
  const previous = held.current;
44577
44599
  if (previous !== null && sameParts(previous, next)) return previous;
@@ -45690,7 +45712,7 @@ function TargetControl({
45690
45712
 
45691
45713
  // src/QuestionPanel.tsx
45692
45714
  import { copyText as copyText8 } from "@ai-matrx/kit/clipboard";
45693
- import { useEffect as useEffect104, useMemo as useMemo72, useRef as useRef59, useState as useState119 } from "react";
45715
+ import { useEffect as useEffect104, useMemo as useMemo72, useRef as useRef60, useState as useState119 } from "react";
45694
45716
  import { Badge as Badge23, BasicInput as BasicInput32, Button as Button94, Checkbox as Checkbox16, Label as Label25, Skeleton as Skeleton34, cn as cn85 } from "@ai-matrx/design-system";
45695
45717
  import { useFields as useFields31, useRecordsClient as useRecordsClient87 } from "@ai-matrx/records/react";
45696
45718
 
@@ -46142,7 +46164,7 @@ function QuestionPanel({ tableId, activeQuestionId, draft, className }) {
46142
46164
  const [saveError, setSaveError] = useState119(null);
46143
46165
  const [saving, setSaving] = useState119(false);
46144
46166
  const [linkMissed, setLinkMissed] = useState119(false);
46145
- const openedFromLink = useRef59(false);
46167
+ const openedFromLink = useRef60(false);
46146
46168
  useEffect104(() => {
46147
46169
  let live = true;
46148
46170
  void questionsOf(client, tableId).then((answer2) => {
@@ -47886,12 +47908,12 @@ function FormsPanel({ tableId, activeFormId, className }) {
47886
47908
  }
47887
47909
 
47888
47910
  // src/CaptureSheet.tsx
47889
- import { useCallback as useCallback63, useEffect as useEffect108, useRef as useRef61, useState as useState124 } from "react";
47911
+ import { useCallback as useCallback63, useEffect as useEffect108, useRef as useRef62, useState as useState124 } from "react";
47890
47912
  import { useFields as useFields34, useRecordsClient as useRecordsClient92, useTable as useTable34 } from "@ai-matrx/records/react";
47891
47913
  import { Button as Button99, Input as Input8, Skeleton as Skeleton39, Textarea as Textarea7, cn as cn89 } from "@ai-matrx/design-system";
47892
47914
 
47893
47915
  // src/CaptureRun.tsx
47894
- import { useCallback as useCallback62, useEffect as useEffect107, useMemo as useMemo74, useRef as useRef60, useState as useState123 } from "react";
47916
+ import { useCallback as useCallback62, useEffect as useEffect107, useMemo as useMemo74, useRef as useRef61, useState as useState123 } from "react";
47895
47917
  import {
47896
47918
  coerceTypedAnswer as coerceTypedAnswer3,
47897
47919
  coercionLabel,
@@ -47949,7 +47971,7 @@ function CaptureRun({ sheetId, face: given, className }) {
47949
47971
  const [items, setItems] = useState123([]);
47950
47972
  const [lastSynced, setLastSynced] = useState123(null);
47951
47973
  const [sending, setSending] = useState123(false);
47952
- const queueRef = useRef60(null);
47974
+ const queueRef = useRef61(null);
47953
47975
  useEffect107(() => {
47954
47976
  const q2 = openCaptureQueue({
47955
47977
  onChange: (c, all) => {
@@ -48329,7 +48351,7 @@ function AdHocCaptureSheet({
48329
48351
  const [error, setError] = useState124(null);
48330
48352
  const [uploadError, setUploadError] = useState124(null);
48331
48353
  const [flushing, setFlushing] = useState124(false);
48332
- const queue = useRef61([]);
48354
+ const queue = useRef62([]);
48333
48355
  const keep = useCallback63(
48334
48356
  async (next) => {
48335
48357
  queue.current = next;
@@ -48871,9 +48893,9 @@ function useEmbedHandshake(args) {
48871
48893
 
48872
48894
  // src/TablePage.tsx
48873
48895
  import { copyText as copyText12 } from "@ai-matrx/kit/clipboard";
48874
- import { useCallback as useCallback68, useEffect as useEffect114, useMemo as useMemo78, useRef as useRef62, useState as useState134 } from "react";
48896
+ import { useCallback as useCallback68, useEffect as useEffect114, useMemo as useMemo78, useRef as useRef63, useState as useState134 } from "react";
48875
48897
  import { archivedByLine as archivedByLine2 } from "@ai-matrx/records";
48876
- import { useFields as useFields37, useRecordsClient as useRecordsClient97, useTable as useTable36 } from "@ai-matrx/records/react";
48898
+ import { useFields as useFields37, useRecordsClient as useRecordsClient97, useSeededAnswer, useTable as useTable36 } from "@ai-matrx/records/react";
48877
48899
  import {
48878
48900
  Badge as Badge25,
48879
48901
  BottomSheet,
@@ -49921,9 +49943,29 @@ function TablePage({
49921
49943
  const askedInWords = filter ? filterInWords(filter, pageFields.data ?? []) : null;
49922
49944
  const organizationId = useRecordsClient97().config.organizationId;
49923
49945
  const publicReadOnly = usePublicReadOnly(client, organizationId, tableId, table.data, rights);
49924
- const [view, setView] = useState134(null);
49925
- const [looks, setLooks] = useState134(void 0);
49946
+ const seededOpening = useSeededAnswer(
49947
+ `table-page-opening:${tableId}`,
49948
+ async (c) => {
49949
+ const [listed, looked] = await Promise.all([c.views({ table_id: tableId }), readLooks(c, tableId)]);
49950
+ if (!listed.ok) return listed;
49951
+ if (!looked.ok) return looked;
49952
+ const mine = listed.data.map(viewFromRow).filter((v) => v.subject === tableId);
49953
+ return { ok: true, data: { views: mine, looks: looked.data } };
49954
+ }
49955
+ );
49956
+ const seededViews = seededOpening && (seedViews ?? [defaultView(tableId)]).every((spec) => seedIsSatisfied(spec, seededOpening.views)) ? seededOpening.views : void 0;
49957
+ const [view, setView] = useState134(() => {
49958
+ if (!seededViews || seededViews.length === 0) return null;
49959
+ const asked3 = savedViewFromParam(activeView);
49960
+ return seededViews.find((v) => v.id === asked3) ?? defaultViewOf(seededViews) ?? seededViews[0];
49961
+ });
49962
+ const [looks, setLooks] = useState134(() => seededViews ? seededOpening?.looks : void 0);
49963
+ const looksSeededFor = useRef63(seededViews && seededOpening ? tableId : null);
49926
49964
  useEffect114(() => {
49965
+ if (looksSeededFor.current === tableId) {
49966
+ looksSeededFor.current = null;
49967
+ return;
49968
+ }
49927
49969
  let live = true;
49928
49970
  setLooks(void 0);
49929
49971
  void readLooks(client, tableId).then((answered) => {
@@ -49943,9 +49985,9 @@ function TablePage({
49943
49985
  const questionsFromLink = questionsFromParam(activeView);
49944
49986
  const openedByPackage = openingView(savedFromLink || questionsFromLink ? null : activeView, activeDashboardId);
49945
49987
  const opened = questionsFromLink ? { ...openedByPackage, main: "questions" } : openedByPackage;
49946
- const [views, setViews] = useState134(void 0);
49988
+ const [views, setViews] = useState134(() => seededViews);
49947
49989
  const [pickedViewId, setPickedViewId] = useState134(savedFromLink);
49948
- const viewSeen = useRef62(null);
49990
+ const viewSeen = useRef63(null);
49949
49991
  const [viewLinkMissed, setViewLinkMissed] = useState134(false);
49950
49992
  const railAsked = typeof activeRail === "string" ? activeRail.trim() : "";
49951
49993
  const railUnknown = railAsked !== "" && railFromParam(railAsked) === null ? railAsked : null;
@@ -50016,7 +50058,7 @@ function TablePage({
50016
50058
  lookDiffers: view ? lookDiffers(view, look) : false
50017
50059
  };
50018
50060
  const shownSignature = JSON.stringify(shownReport);
50019
- const reportRef = useRef62(onShownViewChange);
50061
+ const reportRef = useRef63(onShownViewChange);
50020
50062
  reportRef.current = onShownViewChange;
50021
50063
  useEffect114(() => {
50022
50064
  reportRef.current?.(JSON.parse(shownSignature));
@@ -50062,7 +50104,7 @@ function TablePage({
50062
50104
  },
50063
50105
  [client, tableId, view, looks]
50064
50106
  );
50065
- const arrival = useRef62(viewAddress ?? null);
50107
+ const arrival = useRef63(viewAddress ?? null);
50066
50108
  const [gridQuery, setGridQuery] = useState134(null);
50067
50109
  const [addressResetEpoch, setAddressResetEpoch] = useState134(0);
50068
50110
  const reportGrid = useCallback68((q) => {
@@ -50073,7 +50115,7 @@ function TablePage({
50073
50115
  () => ({ initial: arrival.current, report: reportGrid, resetEpoch: addressResetEpoch }),
50074
50116
  [reportGrid, addressResetEpoch]
50075
50117
  );
50076
- const hiddenApplied = useRef62(false);
50118
+ const hiddenApplied = useRef63(false);
50077
50119
  useEffect114(() => {
50078
50120
  const hidden = arrival.current?.hidden;
50079
50121
  if (hiddenApplied.current || !view || !looks || !hidden || hidden.length === 0) return;
@@ -50094,9 +50136,9 @@ function TablePage({
50094
50136
  const gridPending = !isViewAddressEmpty({ ...arrival.current ?? {}, hidden: [] });
50095
50137
  const addressReady = Boolean(view && looks) && (gridQuery !== null || !gridPending);
50096
50138
  const addressSignature = JSON.stringify(viewAddressToParams(addressNow));
50097
- const addressReportRef = useRef62(onViewAddressChange);
50139
+ const addressReportRef = useRef63(onViewAddressChange);
50098
50140
  addressReportRef.current = onViewAddressChange;
50099
- const addressSent = useRef62(null);
50141
+ const addressSent = useRef63(null);
50100
50142
  useEffect114(() => {
50101
50143
  if (!addressReady) return;
50102
50144
  if (addressSent.current === null && addressSignature === JSON.stringify(viewAddressToParams(arrival.current))) {
@@ -50325,9 +50367,9 @@ function TablePage({
50325
50367
  }
50326
50368
  });
50327
50369
  const openedEntry = ACTION_PRESS.find((entry) => entry.press.main !== void 0 && entry.press.main === main) ?? ACTION_PRESS.find((entry) => entry.press.rail !== void 0 && entry.press.rail === rail) ?? null;
50328
- const actionsRef = useRef62(actions);
50370
+ const actionsRef = useRef63(actions);
50329
50371
  actionsRef.current = actions;
50330
- const onActionsRef = useRef62(onActions);
50372
+ const onActionsRef = useRef63(onActions);
50331
50373
  onActionsRef.current = onActions;
50332
50374
  useEffect114(() => {
50333
50375
  onActionsRef.current?.(actionsRef.current);
@@ -50479,6 +50521,7 @@ function TablePage({
50479
50521
  }
50480
50522
  },
50481
50523
  onViews: setViews,
50524
+ ...seededViews && viewsEpoch === 0 ? { initialViews: seededViews } : {},
50482
50525
  seed: seedViews ?? [defaultView(tableId)],
50483
50526
  settings: view && main === "records" && shownLayout === "grid" ? /* @__PURE__ */ jsxs147("div", { className: "flex flex-col gap-0.5 px-1 py-1", role: "radiogroup", "aria-label": FOOTER_WORDS.label, children: [
50484
50527
  /* @__PURE__ */ jsx158("span", { className: "px-1 pb-0.5 text-[11px] font-medium text-muted-foreground", children: FOOTER_WORDS.label }),