@ai-matrx/records-ui 0.63.0 → 0.66.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -0
- package/dist/index.cjs +398 -307
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +213 -17
- package/dist/index.d.ts +213 -17
- package/dist/index.js +392 -295
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -80,12 +80,12 @@ var NO_RIGHTS = tableRights(NOT_ANSWERED_YET);
|
|
|
80
80
|
function tableRightsAt(level) {
|
|
81
81
|
return tableRights(whatYouMayDo(level, true));
|
|
82
82
|
}
|
|
83
|
-
var NO_SAVED_VIEWS_REASON = "
|
|
84
|
-
var NO_MEMBERS_REASON = "
|
|
85
|
-
var NO_SHARE_REASON = "
|
|
86
|
-
var NO_OPEN_RECORDS_REASON = "These numbers do not click through yet. Every one of them is a count of real records
|
|
87
|
-
var NO_REASK_REASON = "
|
|
88
|
-
var NO_UPLOAD_REASON = "
|
|
83
|
+
var NO_SAVED_VIEWS_REASON = "There are no saved views here yet, so there is nothing to be told about. Save a view of this table first and it will be offered here.";
|
|
84
|
+
var NO_MEMBERS_REASON = "This app cannot offer the people in your organization on this screen yet, so there is nobody to pick. Type the person's name into a text field for now, and tell us \u2014 it is on our list, not yours.";
|
|
85
|
+
var NO_SHARE_REASON = "Sharing is not available on this screen, so there is no Share button here rather than one that opens nothing. Share this table from the tables list instead.";
|
|
86
|
+
var NO_OPEN_RECORDS_REASON = "These numbers do not click through yet. Every one of them is a count of real records \u2014 open the records view and group it by the same field to see them. It is on our list, not yours.";
|
|
87
|
+
var NO_REASK_REASON = "Changing this dashboard by describing it is not switched on here yet, so this box is off rather than one that goes nowhere. Add and remove the blocks by hand above. It is on our list, not yours.";
|
|
88
|
+
var NO_UPLOAD_REASON = "This screen cannot take a file yet, so there is no chooser here rather than one that loses what you pick. Put a link to the file in a text field for now. It is on our list, not yours.";
|
|
89
89
|
var HostContext = createContext(null);
|
|
90
90
|
function RecordsUiProvider({ value, children }) {
|
|
91
91
|
const bound = useMemo(() => value, [value]);
|
|
@@ -160,6 +160,10 @@ var TITLE = {
|
|
|
160
160
|
not_supported: "That cannot be done here",
|
|
161
161
|
store_limit: "This has got as big as it can",
|
|
162
162
|
store_not_served: "This organization does not keep its data here",
|
|
163
|
+
unreachable: "We could not reach your data",
|
|
164
|
+
needs_approval: "This is waiting on someone's approval",
|
|
165
|
+
stale_read: "Your view of this is a moment behind",
|
|
166
|
+
timed_out: "That took too long to answer",
|
|
163
167
|
internal: "Something went wrong at our end"
|
|
164
168
|
};
|
|
165
169
|
var REMEDY = {
|
|
@@ -174,8 +178,35 @@ var REMEDY = {
|
|
|
174
178
|
not_supported: "There is another way to do this \u2014 ask, and we will point you at it.",
|
|
175
179
|
store_limit: "Split it up, or ask us to raise the ceiling for your organization.",
|
|
176
180
|
store_not_served: "An administrator of this organization turns the record store on from the data settings screen.",
|
|
181
|
+
// 🚨 NOTHING WAS WRITTEN AND NOTHING WAS REFUSED (lane TAILS-6). The call
|
|
182
|
+
// never arrived, so "try again" is the honest instruction rather than a
|
|
183
|
+
// consolation — and saying nothing changed is the half a person actually
|
|
184
|
+
// worries about.
|
|
185
|
+
unreachable: "Nothing was changed. Check your connection and open this again.",
|
|
186
|
+
needs_approval: "Nothing was changed yet. The people who can approve it have been asked.",
|
|
187
|
+
stale_read: "Open this again in a moment and it will be up to date.",
|
|
188
|
+
timed_out: "Nothing was changed. Try a smaller page, or try again in a moment.",
|
|
177
189
|
internal: "Nothing you did caused this. Try again, and tell us if it keeps happening."
|
|
178
190
|
};
|
|
191
|
+
var ENGINEER_TEXT = [
|
|
192
|
+
/@ai-matrx\//,
|
|
193
|
+
/\bSQLSTATE\b/i,
|
|
194
|
+
/\bPostgREST\b/i,
|
|
195
|
+
/\bIndexedDB\b/i,
|
|
196
|
+
// `<RecordsUiProvider>` — a component, on a screen.
|
|
197
|
+
/<\/?[A-Z][A-Za-z0-9]*\s*\/?>/,
|
|
198
|
+
// `savedViews`, `openRecords`, `onReask` — an identifier, however it is
|
|
199
|
+
// quoted. English does not write camelCase; code does.
|
|
200
|
+
/\b[a-z][a-z0-9]*[A-Z][A-Za-z0-9]*\b/,
|
|
201
|
+
// A backticked token is a thing you type into an editor, not a thing you do.
|
|
202
|
+
/`[A-Za-z_][A-Za-z0-9_.]*`/,
|
|
203
|
+
// "bind X on Y", "pass X", "import X" — an instruction to whoever maintains
|
|
204
|
+
// the app, arriving on the screen of somebody who cannot act on it.
|
|
205
|
+
/\bbind\s+[`'"A-Za-z]/i
|
|
206
|
+
];
|
|
207
|
+
function isEngineerText(text) {
|
|
208
|
+
return ENGINEER_TEXT.some((shape) => shape.test(text));
|
|
209
|
+
}
|
|
179
210
|
function withoutMachineClauses(sentence2) {
|
|
180
211
|
const dropped = [];
|
|
181
212
|
const footnotes = [];
|
|
@@ -213,11 +244,17 @@ function refusalForAPerson(error) {
|
|
|
213
244
|
const message = plainSentence(String(error.message ?? ""));
|
|
214
245
|
const hint = plainSentence(String(error.hint ?? ""));
|
|
215
246
|
const title = TITLE[code] ?? "The store refused this";
|
|
216
|
-
const
|
|
217
|
-
const
|
|
247
|
+
const hintIsForEngineers = isEngineerText(String(error.hint ?? ""));
|
|
248
|
+
const remedy = (hintIsForEngineers ? null : hint.kept) ?? REMEDY[code] ?? REMEDY["internal"];
|
|
249
|
+
const messageIsForEngineers = isEngineerText(String(error.message ?? ""));
|
|
250
|
+
const said = (messageIsForEngineers ? "" : message.kept ?? "").trim();
|
|
218
251
|
const forEngineers = [
|
|
219
252
|
error.sqlstate ? `SQLSTATE ${error.sqlstate}.` : null,
|
|
220
|
-
|
|
253
|
+
// The client's own note about a class it did not recognise, or a call that
|
|
254
|
+
// never reached the store. It is rendered out of sight, never as a sentence.
|
|
255
|
+
error.diagnostic ? error.diagnostic : null,
|
|
256
|
+
messageIsForEngineers ? `Message: ${error.message}` : null,
|
|
257
|
+
error.hint && (hintIsForEngineers || hint.kept !== error.hint) ? `Hint: ${error.hint}` : null,
|
|
221
258
|
message.dropped.length > 0 || hint.dropped.length > 0 ? `Dropped from the person's view: ${[...message.dropped, ...hint.dropped].join(" | ")}` : null
|
|
222
259
|
].filter(Boolean).join(" ");
|
|
223
260
|
return {
|
|
@@ -524,7 +561,11 @@ import {
|
|
|
524
561
|
} from "@ai-matrx/design-system";
|
|
525
562
|
|
|
526
563
|
// src/parity.ts
|
|
527
|
-
import {
|
|
564
|
+
import {
|
|
565
|
+
KERNEL_TABLES,
|
|
566
|
+
PARITY_FIELD_TYPES as PARITY_FIELD_TYPES2,
|
|
567
|
+
fieldKindFor
|
|
568
|
+
} from "@ai-matrx/records";
|
|
528
569
|
|
|
529
570
|
// src/fieldTypes.ts
|
|
530
571
|
import { PARITY_FIELD_TYPES } from "@ai-matrx/records";
|
|
@@ -591,50 +632,9 @@ var PARITY_LABEL = {
|
|
|
591
632
|
var PARITY_MADE_OF = Object.fromEntries(
|
|
592
633
|
PARITY_FIELD_TYPES2.map((p) => [p.parity_type, p.made_of])
|
|
593
634
|
);
|
|
635
|
+
var KERNEL = { file: KERNEL_FILE_TABLE, person: KERNEL_PERSON_TABLE };
|
|
594
636
|
function editorKindFor(field) {
|
|
595
|
-
|
|
596
|
-
const config = field.config ?? {};
|
|
597
|
-
switch (field.type) {
|
|
598
|
-
// LIMITS-FIX 2026-09-21: the tick box is a BEHAVIOUR of its own, so its editor is
|
|
599
|
-
// decided by the behaviour alone — no format, no unit, no target to read. A record
|
|
600
|
-
// that never answered holds no key at all, and `CheckboxControl` draws that third
|
|
601
|
-
// state rather than pretending it is an unticked box.
|
|
602
|
-
case "boolean":
|
|
603
|
-
return "checkbox";
|
|
604
|
-
case "text":
|
|
605
|
-
if (format === "email") return "email";
|
|
606
|
-
if (format === "phone") return "phone";
|
|
607
|
-
if (format === "url") return "url";
|
|
608
|
-
if (format === "json" || config["json"] === true) return "json";
|
|
609
|
-
if (format === "long" || config["multiline"] === true) return "long_text";
|
|
610
|
-
return "text";
|
|
611
|
-
case "range": {
|
|
612
|
-
const kind = String(config["kind"] ?? "number");
|
|
613
|
-
if (kind === "date" || kind === "datetime") return "datetime";
|
|
614
|
-
if (format === "currency") return "currency";
|
|
615
|
-
if (format === "percent" || field.unit === "%") return "percent";
|
|
616
|
-
return "number";
|
|
617
|
-
}
|
|
618
|
-
case "list":
|
|
619
|
-
return field.multi ? "multi_select" : "select";
|
|
620
|
-
case "relation":
|
|
621
|
-
return relationEditor(field);
|
|
622
|
-
case "formula": {
|
|
623
|
-
const via = config["via"];
|
|
624
|
-
if (via && config["agg"]) return "rollup";
|
|
625
|
-
if (via) return "lookup";
|
|
626
|
-
return "formula";
|
|
627
|
-
}
|
|
628
|
-
default:
|
|
629
|
-
return "text";
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
function relationEditor(field) {
|
|
633
|
-
const parity = String(field.config?.["parity"] ?? "");
|
|
634
|
-
if (parity === "attachment" || parity === "member") return parity;
|
|
635
|
-
if (field.relation_target === KERNEL_FILE_TABLE) return "attachment";
|
|
636
|
-
if (field.relation_target === KERNEL_PERSON_TABLE) return "member";
|
|
637
|
-
return "relation";
|
|
637
|
+
return fieldKindFor(field, KERNEL);
|
|
638
638
|
}
|
|
639
639
|
function fieldTypeLabel(field) {
|
|
640
640
|
const kind = editorKindFor(field);
|
|
@@ -1541,18 +1541,137 @@ function useLabelWait(field) {
|
|
|
1541
1541
|
return field ? waiting(field) : null;
|
|
1542
1542
|
}
|
|
1543
1543
|
|
|
1544
|
-
// src/
|
|
1545
|
-
|
|
1546
|
-
function
|
|
1547
|
-
|
|
1548
|
-
|
|
1544
|
+
// src/enumLabels.ts
|
|
1545
|
+
function exhaustiveValuesOf() {
|
|
1546
|
+
return function(values) {
|
|
1547
|
+
return values;
|
|
1548
|
+
};
|
|
1549
1549
|
}
|
|
1550
|
-
var
|
|
1550
|
+
var WORK_DUE_STATES = exhaustiveValuesOf()([
|
|
1551
|
+
"overdue",
|
|
1552
|
+
"due_today",
|
|
1553
|
+
"scheduled",
|
|
1554
|
+
"undated",
|
|
1555
|
+
"finished"
|
|
1556
|
+
]);
|
|
1557
|
+
var DUE_STATE_LABEL = {
|
|
1558
|
+
overdue: "Overdue",
|
|
1559
|
+
due_today: "Today",
|
|
1560
|
+
scheduled: "Scheduled",
|
|
1561
|
+
undated: "No date",
|
|
1562
|
+
finished: "Done"
|
|
1563
|
+
};
|
|
1564
|
+
var WORK_INBOX_KINDS = exhaustiveValuesOf()(["assignment", "approval", "proposal"]);
|
|
1565
|
+
var WORK_INBOX_KIND_LABEL = {
|
|
1566
|
+
assignment: "Yours",
|
|
1567
|
+
approval: "Approve",
|
|
1568
|
+
proposal: "Agent"
|
|
1569
|
+
};
|
|
1570
|
+
var CHECKLIST_REQUIREMENT_KINDS = exhaustiveValuesOf()([
|
|
1571
|
+
"none",
|
|
1572
|
+
"note",
|
|
1573
|
+
"answer",
|
|
1574
|
+
"record_field",
|
|
1575
|
+
"form",
|
|
1576
|
+
"document"
|
|
1577
|
+
]);
|
|
1578
|
+
var CHECKLIST_REQUIREMENT_LABEL = {
|
|
1579
|
+
none: "",
|
|
1580
|
+
note: "Say what you did",
|
|
1581
|
+
answer: "Fill this in",
|
|
1582
|
+
record_field: "This has to be filled in on the record",
|
|
1583
|
+
form: "This step is a form",
|
|
1584
|
+
document: "This step is a document"
|
|
1585
|
+
};
|
|
1586
|
+
var FIELD_SENSITIVITY_LABEL = {
|
|
1587
|
+
public: "Anyone who can open the record",
|
|
1588
|
+
internal: "People in this organization",
|
|
1589
|
+
confidential: "People who have been given the record",
|
|
1590
|
+
restricted: "Owners and administrators only"
|
|
1591
|
+
};
|
|
1592
|
+
var CONTEXT_POLICY_LABEL = {
|
|
1593
|
+
include: "Yes, an assistant can read it",
|
|
1594
|
+
summarize: "Only a summary of it",
|
|
1595
|
+
on_request: "Only when somebody asks for it",
|
|
1596
|
+
exclude: "No, keep it away from assistants"
|
|
1597
|
+
};
|
|
1598
|
+
var STAGE_RULE_ON_FAIL_LABEL = {
|
|
1599
|
+
refuse: "Turn it away",
|
|
1600
|
+
require_approval: "Ask for approval"
|
|
1601
|
+
};
|
|
1602
|
+
var SUBSCRIPTION_CADENCES = exhaustiveValuesOf()(["instant", "hourly", "daily", "weekly"]);
|
|
1603
|
+
var SUBSCRIPTION_CADENCE_LABEL = {
|
|
1604
|
+
instant: "as it happens",
|
|
1605
|
+
hourly: "hourly summary",
|
|
1606
|
+
daily: "daily summary",
|
|
1607
|
+
weekly: "weekly summary"
|
|
1608
|
+
};
|
|
1609
|
+
var SUBSCRIPTION_CHANNEL_VALUES = ["in_app", "email", "sms"];
|
|
1610
|
+
var SUBSCRIPTION_CHANNEL_LABEL = {
|
|
1611
|
+
in_app: "in the app",
|
|
1612
|
+
email: "by email \u2014 only if an address is on the account",
|
|
1613
|
+
sms: "by text \u2014 only if a number is on the account"
|
|
1614
|
+
};
|
|
1615
|
+
var ROLLUP_AGG_VALUES = ["count", "sum", "avg", "min", "max"];
|
|
1616
|
+
var ROLLUP_AGG_LABEL = {
|
|
1617
|
+
count: "How many there are",
|
|
1618
|
+
sum: "The total of",
|
|
1619
|
+
avg: "The average of",
|
|
1620
|
+
min: "The smallest",
|
|
1621
|
+
max: "The largest"
|
|
1622
|
+
};
|
|
1623
|
+
var FORMULA_OP_VALUES = ["concat", "add", "sub", "mul", "div"];
|
|
1624
|
+
var FORMULA_OP_LABEL = {
|
|
1625
|
+
concat: "Join them together",
|
|
1626
|
+
add: "Add them up",
|
|
1627
|
+
sub: "Take the second from the first",
|
|
1628
|
+
mul: "Multiply them",
|
|
1629
|
+
div: "Divide the first by the second"
|
|
1630
|
+
};
|
|
1631
|
+
var FORMULA_OP_JOINS = {
|
|
1632
|
+
concat: true,
|
|
1633
|
+
add: true,
|
|
1634
|
+
sub: false,
|
|
1635
|
+
mul: true,
|
|
1636
|
+
div: false
|
|
1637
|
+
};
|
|
1638
|
+
var FIELD_RULE_KIND_VALUES = ["min", "max", "length", "pattern", "equals_field", "differs_from_field"];
|
|
1639
|
+
var FIELD_RULE_KIND_LABEL = {
|
|
1640
|
+
min: "At least",
|
|
1641
|
+
max: "At most",
|
|
1642
|
+
length: "No longer than",
|
|
1643
|
+
pattern: "Looks like",
|
|
1644
|
+
equals_field: "Same as the column",
|
|
1645
|
+
differs_from_field: "Different from the column"
|
|
1646
|
+
};
|
|
1647
|
+
var PROPOSED_CHANGE_ACT_VALUES = ["add", "update", "remove"];
|
|
1648
|
+
var PROPOSED_CHANGE_ACT_LABEL = {
|
|
1649
|
+
add: "Add",
|
|
1650
|
+
update: "Update",
|
|
1651
|
+
remove: "Remove"
|
|
1652
|
+
};
|
|
1653
|
+
var BOOKING_PAGE_STATE_VALUES = ["draft", "open", "closed", "full"];
|
|
1654
|
+
var BOOKING_PAGE_STATE_LABEL = {
|
|
1655
|
+
draft: "Not open yet \u2014 nobody can book until you open it.",
|
|
1656
|
+
open: "Open. Anyone with the link can book a time.",
|
|
1657
|
+
closed: "Closed. The link still works and says you are not taking bookings.",
|
|
1658
|
+
full: "Every time you offered is taken."
|
|
1659
|
+
};
|
|
1660
|
+
var ABSENCE_WORD_VALUES = ["never asked", "none", "refused", "conflicting"];
|
|
1661
|
+
var ABSENCE_WORD_LABEL = {
|
|
1551
1662
|
"never asked": "never asked",
|
|
1552
1663
|
none: "none",
|
|
1553
1664
|
refused: "refused",
|
|
1554
1665
|
conflicting: "conflicting"
|
|
1555
1666
|
};
|
|
1667
|
+
|
|
1668
|
+
// src/values.tsx
|
|
1669
|
+
import { Fragment as Fragment3, jsx as jsx9 } from "react/jsx-runtime";
|
|
1670
|
+
function envelopeFor(document2, key) {
|
|
1671
|
+
const block = document2?._values;
|
|
1672
|
+
return block?.[key];
|
|
1673
|
+
}
|
|
1674
|
+
var ABSENCE_WORDS = ABSENCE_WORD_LABEL;
|
|
1556
1675
|
function renderValue(field, value, document2, labels, stillWaitingFor) {
|
|
1557
1676
|
const envelope = envelopeFor(document2, field.key);
|
|
1558
1677
|
if (envelope?.absent) {
|
|
@@ -2400,7 +2519,7 @@ import {
|
|
|
2400
2519
|
cn as cn8
|
|
2401
2520
|
} from "@ai-matrx/design-system";
|
|
2402
2521
|
import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2403
|
-
var NO_ENRICH_REASON = "
|
|
2522
|
+
var NO_ENRICH_REASON = "Having an agent fill this column is not switched on here yet, so this panel does not offer it rather than offering something that cannot happen. Everything else on this panel works. It is on our list, not yours.";
|
|
2404
2523
|
function cellState(cell) {
|
|
2405
2524
|
if (!cell || !cell.agent_owned) return "none";
|
|
2406
2525
|
if (cell.pinned) return "yours";
|
|
@@ -2982,7 +3101,7 @@ function confirmLabel(plan, noun, nounPlural) {
|
|
|
2982
3101
|
|
|
2983
3102
|
// src/pasteValues.ts
|
|
2984
3103
|
import { predictValueRefusals } from "@ai-matrx/records/core";
|
|
2985
|
-
import { optionKey as optionKey2 } from "@ai-matrx/records";
|
|
3104
|
+
import { coerceTypedAnswer, optionKey as optionKey2 } from "@ai-matrx/records";
|
|
2986
3105
|
function choiceOptionsOf(options) {
|
|
2987
3106
|
const byWord = /* @__PURE__ */ new Map();
|
|
2988
3107
|
const labels = [];
|
|
@@ -2999,113 +3118,11 @@ function choiceOptionsOf(options) {
|
|
|
2999
3118
|
}
|
|
3000
3119
|
return { byWord, labels };
|
|
3001
3120
|
}
|
|
3002
|
-
var TRUE_WORDS = /* @__PURE__ */ new Set(["true", "yes", "y", "1", "\u2713", "x", "checked", "on"]);
|
|
3003
|
-
var FALSE_WORDS = /* @__PURE__ */ new Set(["false", "no", "n", "0", "", "unchecked", "off"]);
|
|
3004
3121
|
function valueFromPastedText(field, raw, options) {
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
switch (kind) {
|
|
3010
|
-
case "checkbox": {
|
|
3011
|
-
const word = text.toLowerCase();
|
|
3012
|
-
if (TRUE_WORDS.has(word)) return { value: true };
|
|
3013
|
-
if (FALSE_WORDS.has(word)) return { value: word === "" ? null : false };
|
|
3014
|
-
return {
|
|
3015
|
-
refusal: `${label} is a tick box, so it takes yes or no \u2014 \u201C${raw}\u201D is neither. Paste TRUE/FALSE, yes/no or 1/0.`
|
|
3016
|
-
};
|
|
3017
|
-
}
|
|
3018
|
-
case "number":
|
|
3019
|
-
case "currency":
|
|
3020
|
-
case "percent": {
|
|
3021
|
-
const negative = /^\(.*\)$/.test(text);
|
|
3022
|
-
const bare = text.replace(/^\(|\)$/g, "").replace(/[\s,]/g, "").replace(/^[^\d.\-+]+/, "").replace(/[^\d.\-+eE]+$/, "");
|
|
3023
|
-
const n = Number(bare);
|
|
3024
|
-
if (bare === "" || Number.isNaN(n)) {
|
|
3025
|
-
return {
|
|
3026
|
-
refusal: `${label} holds a number, and \u201C${raw}\u201D is not one. Paste digits \u2014 a currency symbol, a percent sign, commas and brackets for a negative are all fine.`
|
|
3027
|
-
};
|
|
3028
|
-
}
|
|
3029
|
-
return { value: negative ? -n : n };
|
|
3030
|
-
}
|
|
3031
|
-
case "datetime": {
|
|
3032
|
-
const wantsTime = String(field.config?.["kind"] ?? "date") === "datetime";
|
|
3033
|
-
const plainDay = /^\s*(\d{4})-(\d{2})-(\d{2})\s*$/.exec(raw);
|
|
3034
|
-
if (!wantsTime && plainDay) return { value: `${plainDay[1]}-${plainDay[2]}-${plainDay[3]}` };
|
|
3035
|
-
const at = Date.parse(text);
|
|
3036
|
-
if (Number.isNaN(at)) {
|
|
3037
|
-
return {
|
|
3038
|
-
refusal: `${label} holds a date, and \u201C${raw}\u201D is not one this can read. Paste it as 2026-09-21, or as your spreadsheet's own date format.`
|
|
3039
|
-
};
|
|
3040
|
-
}
|
|
3041
|
-
const parsed = new Date(at);
|
|
3042
|
-
if (wantsTime) return { value: parsed.toISOString() };
|
|
3043
|
-
const y = parsed.getFullYear();
|
|
3044
|
-
const m = String(parsed.getMonth() + 1).padStart(2, "0");
|
|
3045
|
-
const d = String(parsed.getDate()).padStart(2, "0");
|
|
3046
|
-
return { value: `${y}-${m}-${d}` };
|
|
3047
|
-
}
|
|
3048
|
-
case "json": {
|
|
3049
|
-
try {
|
|
3050
|
-
return { value: JSON.parse(text) };
|
|
3051
|
-
} catch {
|
|
3052
|
-
return {
|
|
3053
|
-
refusal: `${label} holds a document, and \u201C${raw}\u201D is not valid JSON. Paste the whole object, braces and all.`
|
|
3054
|
-
};
|
|
3055
|
-
}
|
|
3056
|
-
}
|
|
3057
|
-
// A LIST, A PERSON, A FILE OR ANOTHER RECORD IS AN IDENTITY, NOT A WORD.
|
|
3058
|
-
// The store keeps the id of the option or the record, and a word off a
|
|
3059
|
-
// clipboard is not that id — writing the word would put a string where an
|
|
3060
|
-
// id belongs and every filter, colour and rollup built on that column would
|
|
3061
|
-
// stop agreeing with it. So the cell is left alone and the person is told
|
|
3062
|
-
// which control does hold it.
|
|
3063
|
-
case "select":
|
|
3064
|
-
case "multi_select": {
|
|
3065
|
-
if (!options) {
|
|
3066
|
-
return {
|
|
3067
|
-
refusal: `${label} is a list and its options have not been read yet, so nothing can be matched to \u201C${raw}\u201D. Reopen the paste once the column's choices have loaded.`
|
|
3068
|
-
};
|
|
3069
|
-
}
|
|
3070
|
-
const wanted = kind === "multi_select" ? text.split(/[,;]/).map((w) => w.trim()).filter((w) => w.length > 0) : [text];
|
|
3071
|
-
if (wanted.length === 0) return { value: kind === "multi_select" ? [] : null };
|
|
3072
|
-
const matched = [];
|
|
3073
|
-
const unmatched = [];
|
|
3074
|
-
for (const word of wanted) {
|
|
3075
|
-
const hit = options.byWord.get(word.toLowerCase());
|
|
3076
|
-
if (hit === void 0) unmatched.push(word);
|
|
3077
|
-
else matched.push(hit);
|
|
3078
|
-
}
|
|
3079
|
-
if (unmatched.length > 0) {
|
|
3080
|
-
const offered = options.labels.length > 0 ? options.labels.join(", ") : "it has none yet";
|
|
3081
|
-
return {
|
|
3082
|
-
refusal: `${label} does not have ${unmatched.map((w) => `\u201C${w}\u201D`).join(" or ")} among its options. It offers: ${offered}.`,
|
|
3083
|
-
unmatched
|
|
3084
|
-
};
|
|
3085
|
-
}
|
|
3086
|
-
return { value: kind === "multi_select" ? matched : matched[0] };
|
|
3087
|
-
}
|
|
3088
|
-
case "member":
|
|
3089
|
-
return {
|
|
3090
|
-
refusal: `${label} names a person in this organization, so it is chosen rather than typed. Open the cell and pick them.`
|
|
3091
|
-
};
|
|
3092
|
-
case "attachment":
|
|
3093
|
-
return {
|
|
3094
|
-
refusal: `${label} holds a file, which cannot arrive on a clipboard. Open the cell and attach it.`
|
|
3095
|
-
};
|
|
3096
|
-
case "relation":
|
|
3097
|
-
return {
|
|
3098
|
-
refusal: `${label} points at another record, so it is chosen rather than typed. Open the cell and pick the record.`
|
|
3099
|
-
};
|
|
3100
|
-
case "formula":
|
|
3101
|
-
case "lookup":
|
|
3102
|
-
case "rollup":
|
|
3103
|
-
return {
|
|
3104
|
-
refusal: `${label} is worked out by the system, so nothing can be pasted into it.`
|
|
3105
|
-
};
|
|
3106
|
-
default:
|
|
3107
|
-
return { value: raw };
|
|
3108
|
-
}
|
|
3122
|
+
return coerceTypedAnswer(field, raw, {
|
|
3123
|
+
label: fieldName(field),
|
|
3124
|
+
...options ? { options } : {}
|
|
3125
|
+
});
|
|
3109
3126
|
}
|
|
3110
3127
|
function planPastedBlock(args) {
|
|
3111
3128
|
const byKey = new Map(args.fields.map((f) => [f.key, f]));
|
|
@@ -4282,19 +4299,23 @@ import {
|
|
|
4282
4299
|
} from "@ai-matrx/records/react";
|
|
4283
4300
|
import { Button as Button11, Separator as Separator3, cn as cn12 } from "@ai-matrx/design-system";
|
|
4284
4301
|
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4285
|
-
var
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4302
|
+
var ADDABLE_TYPE_IDS = [
|
|
4303
|
+
"text",
|
|
4304
|
+
"long_text",
|
|
4305
|
+
"number",
|
|
4306
|
+
"currency",
|
|
4307
|
+
"percent",
|
|
4308
|
+
"datetime",
|
|
4309
|
+
"email",
|
|
4310
|
+
"phone",
|
|
4311
|
+
"url",
|
|
4312
|
+
"member",
|
|
4313
|
+
"attachment"
|
|
4297
4314
|
];
|
|
4315
|
+
var ADDABLE_TYPES = ADDABLE_TYPE_IDS.map((value) => ({
|
|
4316
|
+
value,
|
|
4317
|
+
label: fieldTypeChoice(value)?.label ?? value
|
|
4318
|
+
}));
|
|
4298
4319
|
function asField(f) {
|
|
4299
4320
|
return {
|
|
4300
4321
|
id: f.id,
|
|
@@ -4473,41 +4494,20 @@ import {
|
|
|
4473
4494
|
Separator as Separator4,
|
|
4474
4495
|
cn as cn13
|
|
4475
4496
|
} from "@ai-matrx/design-system";
|
|
4497
|
+
import { FIELD_SENSITIVITIES, CONTEXT_POLICIES } from "@ai-matrx/records";
|
|
4476
4498
|
import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4477
|
-
var RULE_KINDS = [
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
{ value: "restricted", label: "Owners and administrators only" }
|
|
4490
|
-
];
|
|
4491
|
-
var AGENT_VISIBILITY = [
|
|
4492
|
-
{ value: "include", label: "Yes, an assistant can read it" },
|
|
4493
|
-
{ value: "summarize", label: "Only a summary of it" },
|
|
4494
|
-
{ value: "on_request", label: "Only when somebody asks for it" },
|
|
4495
|
-
{ value: "exclude", label: "No, keep it away from assistants" }
|
|
4496
|
-
];
|
|
4497
|
-
var ROLLUP_OPERATIONS = [
|
|
4498
|
-
{ value: "count", label: "How many there are" },
|
|
4499
|
-
{ value: "sum", label: "The total of" },
|
|
4500
|
-
{ value: "avg", label: "The average of" },
|
|
4501
|
-
{ value: "min", label: "The smallest" },
|
|
4502
|
-
{ value: "max", label: "The largest" }
|
|
4503
|
-
];
|
|
4504
|
-
var FORMULA_OPERATIONS = [
|
|
4505
|
-
{ value: "concat", label: "Join them together", joins: true },
|
|
4506
|
-
{ value: "add", label: "Add them up", joins: true },
|
|
4507
|
-
{ value: "sub", label: "Take the second from the first", joins: false },
|
|
4508
|
-
{ value: "mul", label: "Multiply them", joins: true },
|
|
4509
|
-
{ value: "div", label: "Divide the first by the second", joins: false }
|
|
4510
|
-
];
|
|
4499
|
+
var RULE_KINDS = FIELD_RULE_KIND_VALUES.map((kind) => ({ kind, label: FIELD_RULE_KIND_LABEL[kind] }));
|
|
4500
|
+
var SENSITIVITIES = FIELD_SENSITIVITIES.map((value) => ({ value, label: FIELD_SENSITIVITY_LABEL[value] }));
|
|
4501
|
+
var AGENT_VISIBILITY = CONTEXT_POLICIES.map((value) => ({ value, label: CONTEXT_POLICY_LABEL[value] }));
|
|
4502
|
+
var ROLLUP_OPERATIONS = ROLLUP_AGG_VALUES.map((value) => ({
|
|
4503
|
+
value,
|
|
4504
|
+
label: ROLLUP_AGG_LABEL[value]
|
|
4505
|
+
}));
|
|
4506
|
+
var FORMULA_OPERATIONS = FORMULA_OP_VALUES.map((value) => ({
|
|
4507
|
+
value,
|
|
4508
|
+
label: FORMULA_OP_LABEL[value],
|
|
4509
|
+
joins: FORMULA_OP_JOINS[value]
|
|
4510
|
+
}));
|
|
4511
4511
|
function keyFor(label) {
|
|
4512
4512
|
const token = label.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 48);
|
|
4513
4513
|
if (token === "") return "";
|
|
@@ -5072,6 +5072,9 @@ var CONDITION_OPS = [
|
|
|
5072
5072
|
{ op: "gt", label: "is more than" },
|
|
5073
5073
|
{ op: "lt", label: "is less than" }
|
|
5074
5074
|
];
|
|
5075
|
+
function pointsAtARecord(f) {
|
|
5076
|
+
return !!f?.field && f.field.type === "relation" && !!f.field.relation_target;
|
|
5077
|
+
}
|
|
5075
5078
|
function conditionIsSimple(expr) {
|
|
5076
5079
|
if (expr === null || expr === void 0) return true;
|
|
5077
5080
|
const node = expr;
|
|
@@ -5142,6 +5145,7 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
5142
5145
|
const op = node?.op ?? "";
|
|
5143
5146
|
const onField = node?.args?.[0]?.field ?? "";
|
|
5144
5147
|
const value = node?.args?.[1]?.const ?? "";
|
|
5148
|
+
const chosen = fields.find((f) => f.id === onField);
|
|
5145
5149
|
function write(next) {
|
|
5146
5150
|
const chosenOp = next.op ?? op;
|
|
5147
5151
|
const chosenField = next.field ?? onField;
|
|
@@ -5180,7 +5184,24 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
5180
5184
|
children: CONDITION_OPS.map((c) => /* @__PURE__ */ jsx18("option", { value: c.op, children: c.label }, c.op))
|
|
5181
5185
|
}
|
|
5182
5186
|
),
|
|
5183
|
-
op
|
|
5187
|
+
op === "present" ? null : pointsAtARecord(chosen) ? (
|
|
5188
|
+
// A RECORD IS PICKED BY ITS WORDS, NEVER TYPED AS AN ID. The same picker the
|
|
5189
|
+
// cell uses, on the same `relation_words_many` the cell reads, so a filter and
|
|
5190
|
+
// the column it filters can never disagree about what a record is called. It
|
|
5191
|
+
// is asked for ONE record even when the column holds several: a clause compares
|
|
5192
|
+
// against one value, and a picker that let somebody choose three would be
|
|
5193
|
+
// writing an expression this row cannot draw back.
|
|
5194
|
+
/* @__PURE__ */ jsx18(
|
|
5195
|
+
RelationPicker,
|
|
5196
|
+
{
|
|
5197
|
+
field: { ...chosen.field, multi: false },
|
|
5198
|
+
value: value === "" ? null : value,
|
|
5199
|
+
onChange: (next) => write({ value: next === null || next === void 0 ? "" : String(next) }),
|
|
5200
|
+
id: "condition-value-record",
|
|
5201
|
+
className: "min-w-[8rem]"
|
|
5202
|
+
}
|
|
5203
|
+
)
|
|
5204
|
+
) : /* @__PURE__ */ jsx18(
|
|
5184
5205
|
BasicInput6,
|
|
5185
5206
|
{
|
|
5186
5207
|
className: "h-8 w-28 text-xs",
|
|
@@ -5188,7 +5209,7 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
5188
5209
|
value: String(value ?? ""),
|
|
5189
5210
|
onChange: (e) => write({ value: conditionValue(e.target.value) })
|
|
5190
5211
|
}
|
|
5191
|
-
)
|
|
5212
|
+
)
|
|
5192
5213
|
] }) : null
|
|
5193
5214
|
] });
|
|
5194
5215
|
}
|
|
@@ -5348,10 +5369,10 @@ var NO_RULES_YET = "Nothing has to be true to get here yet.";
|
|
|
5348
5369
|
var NEEDS_A_SENTENCE = "Write the sentence somebody reads when this stops them, the way you would say it out loud.";
|
|
5349
5370
|
var NOT_DRAWN_HERE = "This part of the rule is not something this builder can draw yet";
|
|
5350
5371
|
var ON_FAIL = [
|
|
5351
|
-
{ value: "refuse", label:
|
|
5372
|
+
{ value: "refuse", label: STAGE_RULE_ON_FAIL_LABEL.refuse, note: "The card stays where it is and the person is told why." },
|
|
5352
5373
|
{
|
|
5353
5374
|
value: "require_approval",
|
|
5354
|
-
label:
|
|
5375
|
+
label: STAGE_RULE_ON_FAIL_LABEL.require_approval,
|
|
5355
5376
|
note: "The card stays where it is, and the people who can approve it are asked. Nobody is turned away."
|
|
5356
5377
|
}
|
|
5357
5378
|
];
|
|
@@ -5402,7 +5423,7 @@ function StageRulesSection({ tableId, stage, className }) {
|
|
|
5402
5423
|
const stageLabel = stages.find((s) => s.key === stageKey)?.label ?? stageKey ?? "";
|
|
5403
5424
|
const gates = useMemo13(() => stageKey ? gatesOf(pipeline, stageKey) : [], [pipeline, stageKey]);
|
|
5404
5425
|
const conditionFields = useMemo13(
|
|
5405
|
-
() => (fields.data ?? []).map((f) => ({ id: String(f.id), key: f.key, label: fieldName(f) })),
|
|
5426
|
+
() => (fields.data ?? []).map((f) => ({ id: String(f.id), key: f.key, label: fieldName(f), field: f })),
|
|
5406
5427
|
[fields.data]
|
|
5407
5428
|
);
|
|
5408
5429
|
useEffect8(() => {
|
|
@@ -5832,7 +5853,7 @@ import { useEffect as useEffect9, useMemo as useMemo14, useState as useState18 }
|
|
|
5832
5853
|
import { useRecordsClient as useRecordsClient12 } from "@ai-matrx/records/react";
|
|
5833
5854
|
import { Skeleton as Skeleton4, cn as cn16 } from "@ai-matrx/design-system";
|
|
5834
5855
|
import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
5835
|
-
var NO_CHAT_REASON = "
|
|
5856
|
+
var NO_CHAT_REASON = "Chatting about this record is not switched on here yet, so this panel is empty rather than showing a box that goes nowhere. It is on our list, not yours.";
|
|
5836
5857
|
function RecordChat({ tableId, recordId, className }) {
|
|
5837
5858
|
const client = useRecordsClient12();
|
|
5838
5859
|
const host = useRecordsUi();
|
|
@@ -7103,7 +7124,7 @@ function offerableFields(all, want) {
|
|
|
7103
7124
|
return all.filter((field) => {
|
|
7104
7125
|
const kind = editorKindFor(field);
|
|
7105
7126
|
if (want === "choice") return kind === "select" || kind === "multi_select";
|
|
7106
|
-
return kind === "datetime"
|
|
7127
|
+
return kind === "datetime";
|
|
7107
7128
|
});
|
|
7108
7129
|
}
|
|
7109
7130
|
function useStageField(tableId) {
|
|
@@ -7521,11 +7542,7 @@ import { useState as useState26 } from "react";
|
|
|
7521
7542
|
import { useRecordsClient as useRecordsClient18 } from "@ai-matrx/records/react";
|
|
7522
7543
|
import { Badge as Badge7, Button as Button22, cn as cn22 } from "@ai-matrx/design-system";
|
|
7523
7544
|
import { jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
7524
|
-
var ACT_WORD =
|
|
7525
|
-
add: "Add",
|
|
7526
|
-
update: "Update",
|
|
7527
|
-
remove: "Remove"
|
|
7528
|
-
};
|
|
7545
|
+
var ACT_WORD = PROPOSED_CHANGE_ACT_LABEL;
|
|
7529
7546
|
function ProposalRow({
|
|
7530
7547
|
change,
|
|
7531
7548
|
outcome,
|
|
@@ -7632,21 +7649,8 @@ import { useCallback as useCallback12, useEffect as useEffect15, useMemo as useM
|
|
|
7632
7649
|
import { useRecordsClient as useRecordsClient19, useTable as useTable9 } from "@ai-matrx/records/react";
|
|
7633
7650
|
import { Badge as Badge8, BasicInput as BasicInput8, BasicTextarea as BasicTextarea4, Button as Button23, Skeleton as Skeleton9, cn as cn23 } from "@ai-matrx/design-system";
|
|
7634
7651
|
import { Fragment as Fragment13, jsx as jsx29, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
7635
|
-
var DUE_WORD =
|
|
7636
|
-
|
|
7637
|
-
due_today: "Today",
|
|
7638
|
-
scheduled: "Scheduled",
|
|
7639
|
-
undated: "No date",
|
|
7640
|
-
finished: "Done"
|
|
7641
|
-
};
|
|
7642
|
-
var REQUIRES_WORD = {
|
|
7643
|
-
none: "",
|
|
7644
|
-
note: "Say what you did",
|
|
7645
|
-
answer: "Fill this in",
|
|
7646
|
-
record_field: "This has to be filled in on the record",
|
|
7647
|
-
form: "This step is a form",
|
|
7648
|
-
document: "This step is a document"
|
|
7649
|
-
};
|
|
7652
|
+
var DUE_WORD = DUE_STATE_LABEL;
|
|
7653
|
+
var REQUIRES_WORD = CHECKLIST_REQUIREMENT_LABEL;
|
|
7650
7654
|
var NO_TEMPLATES = "No checklist has been written for this table yet. Ask an agent for one in a sentence \u2014 the steps, who each belongs to, and how many days in each is due \u2014 and it is stored whole, judged before a single step is written.";
|
|
7651
7655
|
function ChecklistRunner({
|
|
7652
7656
|
tableId,
|
|
@@ -8354,18 +8358,8 @@ function roleKey(said) {
|
|
|
8354
8358
|
// src/ActionInbox.tsx
|
|
8355
8359
|
import { jsx as jsx30, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
8356
8360
|
var ACTION_KINDS = ["approval", "assignment", "proposal"];
|
|
8357
|
-
var KIND_WORD =
|
|
8358
|
-
|
|
8359
|
-
approval: "Approve",
|
|
8360
|
-
proposal: "Agent"
|
|
8361
|
-
};
|
|
8362
|
-
var DUE_WORD2 = {
|
|
8363
|
-
overdue: "Overdue",
|
|
8364
|
-
due_today: "Today",
|
|
8365
|
-
scheduled: "Scheduled",
|
|
8366
|
-
undated: "No date",
|
|
8367
|
-
finished: "Finished"
|
|
8368
|
-
};
|
|
8361
|
+
var KIND_WORD = WORK_INBOX_KIND_LABEL;
|
|
8362
|
+
var DUE_WORD2 = DUE_STATE_LABEL;
|
|
8369
8363
|
function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className }) {
|
|
8370
8364
|
const client = useRecordsClient20();
|
|
8371
8365
|
const [items, setItems] = useState28(null);
|
|
@@ -9617,18 +9611,18 @@ function BuildOrAsk({
|
|
|
9617
9611
|
buildLabel,
|
|
9618
9612
|
className
|
|
9619
9613
|
}) {
|
|
9614
|
+
const mayAsk = mayBuild && Boolean(onAsk);
|
|
9620
9615
|
return /* @__PURE__ */ jsxs31("div", { className: cn29("rounded-md border border-dashed p-3", className), children: [
|
|
9621
9616
|
/* @__PURE__ */ jsx35("p", { className: "text-xs text-muted-foreground", children }),
|
|
9622
|
-
/* @__PURE__ */ jsxs31("div", { className: "mt-2.5 flex flex-wrap items-center gap-2", children: [
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
] }),
|
|
9626
|
-
|
|
9617
|
+
mayBuild ? /* @__PURE__ */ jsxs31("div", { className: "mt-2.5 flex flex-wrap items-center gap-2", children: [
|
|
9618
|
+
mayAsk ? /* @__PURE__ */ jsx35(Button29, { size: "sm", variant: "outline", onClick: onAsk, children: "Ask an agent" }) : null,
|
|
9619
|
+
/* @__PURE__ */ jsx35(Button29, { size: "sm", onClick: onBuild, children: buildLabel })
|
|
9620
|
+
] }) : null,
|
|
9621
|
+
!mayBuild ? whyNot ? /* @__PURE__ */ jsx35("p", { className: "mt-2 text-xs text-muted-foreground", children: whyNot }) : null : mayAsk ? /* @__PURE__ */ jsxs31("p", { className: "mt-2 text-xs text-muted-foreground", children: [
|
|
9627
9622
|
"You would say something like: \u201C",
|
|
9628
9623
|
suggestion,
|
|
9629
9624
|
"\u201D"
|
|
9630
|
-
] }) : /* @__PURE__ */ jsx35("p", { className: "mt-2 text-xs text-muted-foreground", children: NO_AGENT_PORT })
|
|
9631
|
-
!mayBuild && whyNot ? /* @__PURE__ */ jsx35("p", { className: "mt-2 text-xs text-muted-foreground", children: whyNot }) : null
|
|
9625
|
+
] }) : /* @__PURE__ */ jsx35("p", { className: "mt-2 text-xs text-muted-foreground", children: NO_AGENT_PORT })
|
|
9632
9626
|
] });
|
|
9633
9627
|
}
|
|
9634
9628
|
|
|
@@ -9667,30 +9661,41 @@ function PortalsPanel({ tableId, className }) {
|
|
|
9667
9661
|
const client = useRecordsClient25();
|
|
9668
9662
|
const host = useRecordsUi();
|
|
9669
9663
|
const [portals, setPortals] = useState33(null);
|
|
9664
|
+
const [exposures, setExposures] = useState33([]);
|
|
9670
9665
|
const [listError, setListError] = useState33(null);
|
|
9671
9666
|
const [openId, setOpenId] = useState33(null);
|
|
9672
9667
|
const [building, setBuilding] = useState33(false);
|
|
9668
|
+
const [adding, setAdding] = useState33(null);
|
|
9673
9669
|
const load = useCallback18(async () => {
|
|
9674
|
-
const answered = await client.portals();
|
|
9670
|
+
const [answered, mapped] = await Promise.all([client.portals(), client.portalTables()]);
|
|
9675
9671
|
if (!answered.ok) {
|
|
9676
9672
|
setListError(answered.error);
|
|
9677
9673
|
setPortals([]);
|
|
9674
|
+
setExposures([]);
|
|
9678
9675
|
return;
|
|
9679
9676
|
}
|
|
9680
9677
|
setListError(null);
|
|
9681
9678
|
setPortals(answered.data);
|
|
9679
|
+
setExposures(mapped.ok ? mapped.data : []);
|
|
9682
9680
|
}, [client]);
|
|
9683
9681
|
useEffect21(() => {
|
|
9684
9682
|
void load();
|
|
9685
9683
|
}, [load]);
|
|
9686
9684
|
if (portals === null) return /* @__PURE__ */ jsx36(Skeleton15, { className: cn30("h-32 w-full", className) });
|
|
9685
|
+
const onATable = Boolean(tableId);
|
|
9686
|
+
const holdingThis = new Set(
|
|
9687
|
+
exposures.filter((e) => e.table_id === tableId).map((e) => e.portal_id)
|
|
9688
|
+
);
|
|
9689
|
+
const shown = onATable ? portals.filter((p) => holdingThis.has(p.portal_id)) : portals;
|
|
9690
|
+
const couldTakeIt = onATable ? portals.filter((p) => !holdingThis.has(p.portal_id)) : [];
|
|
9691
|
+
const elsewhere = couldTakeIt.length;
|
|
9687
9692
|
const origin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
9688
9693
|
return /* @__PURE__ */ jsxs32("section", { className: cn30("flex flex-col gap-3", className), children: [
|
|
9689
9694
|
/* @__PURE__ */ jsxs32("header", { className: "flex items-center gap-2", children: [
|
|
9690
9695
|
/* @__PURE__ */ jsx36("h3", { className: "text-sm font-medium", children: "Portals" }),
|
|
9691
|
-
/* @__PURE__ */ jsx36("span", { className: "text-xs text-muted-foreground", children:
|
|
9696
|
+
/* @__PURE__ */ jsx36("span", { className: "text-xs text-muted-foreground", children: shown.length === 0 ? "none for this table" : `${shown.length}` }),
|
|
9692
9697
|
/* @__PURE__ */ jsx36("div", { className: "flex-1" }),
|
|
9693
|
-
|
|
9698
|
+
shown.length > 0 ? /* @__PURE__ */ jsx36(Button30, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a portal" }) : null
|
|
9694
9699
|
] }),
|
|
9695
9700
|
listError ? /* @__PURE__ */ jsx36(RefusalNotice, { error: listError }) : null,
|
|
9696
9701
|
building ? /* @__PURE__ */ jsx36(
|
|
@@ -9703,7 +9708,19 @@ function PortalsPanel({ tableId, className }) {
|
|
|
9703
9708
|
onClose: () => setBuilding(false)
|
|
9704
9709
|
}
|
|
9705
9710
|
) : null,
|
|
9706
|
-
|
|
9711
|
+
adding ? /* @__PURE__ */ jsx36(
|
|
9712
|
+
PortalBuilder,
|
|
9713
|
+
{
|
|
9714
|
+
...tableId ? { tableId } : {},
|
|
9715
|
+
portalId: adding,
|
|
9716
|
+
onSaved: () => {
|
|
9717
|
+
setAdding(null);
|
|
9718
|
+
void load();
|
|
9719
|
+
},
|
|
9720
|
+
onClose: () => setAdding(null)
|
|
9721
|
+
}
|
|
9722
|
+
) : null,
|
|
9723
|
+
shown.length === 0 && !listError && !building && !adding ? /* @__PURE__ */ jsxs32(
|
|
9707
9724
|
BuildOrAsk,
|
|
9708
9725
|
{
|
|
9709
9726
|
mayBuild: true,
|
|
@@ -9712,11 +9729,37 @@ function PortalsPanel({ tableId, className }) {
|
|
|
9712
9729
|
onAsk: () => host.onAskForOne?.({ kind: "portal", tableId, suggestion: PORTAL_SUGGESTION })
|
|
9713
9730
|
} : {},
|
|
9714
9731
|
suggestion: PORTAL_SUGGESTION,
|
|
9715
|
-
buildLabel: "Build the portal",
|
|
9716
|
-
children:
|
|
9732
|
+
buildLabel: elsewhere > 0 ? "Build a new portal" : "Build the portal",
|
|
9733
|
+
children: [
|
|
9734
|
+
"A portal is how one of your clients signs in and sees the records that name them \u2014 their jobs, their invoices \u2014 and nothing else.",
|
|
9735
|
+
elsewhere > 0 ? /* @__PURE__ */ jsxs32(Fragment16, { children: [
|
|
9736
|
+
" ",
|
|
9737
|
+
"This table is not in",
|
|
9738
|
+
" ",
|
|
9739
|
+
elsewhere === 1 ? "the portal this organization already has" : `any of the ${elsewhere} portals this organization already has`,
|
|
9740
|
+
"."
|
|
9741
|
+
] }) : null
|
|
9742
|
+
]
|
|
9717
9743
|
}
|
|
9718
9744
|
) : null,
|
|
9719
|
-
/* @__PURE__ */
|
|
9745
|
+
shown.length === 0 && !listError && !building && !adding && elsewhere > 0 ? /* @__PURE__ */ jsxs32("div", { className: "flex flex-col gap-1.5 rounded-md border border-dashed border-border p-2.5", children: [
|
|
9746
|
+
/* @__PURE__ */ jsx36("p", { className: "text-xs text-muted-foreground", children: "Or add this table to a portal your clients already sign in to:" }),
|
|
9747
|
+
/* @__PURE__ */ jsx36("div", { className: "flex flex-wrap gap-1.5", children: couldTakeIt.map((portal) => /* @__PURE__ */ jsxs32(
|
|
9748
|
+
Button30,
|
|
9749
|
+
{
|
|
9750
|
+
size: "sm",
|
|
9751
|
+
variant: "outline",
|
|
9752
|
+
onClick: () => setAdding(portal.portal_id),
|
|
9753
|
+
children: [
|
|
9754
|
+
"Add to \u201C",
|
|
9755
|
+
portal.title,
|
|
9756
|
+
"\u201D"
|
|
9757
|
+
]
|
|
9758
|
+
},
|
|
9759
|
+
portal.portal_id
|
|
9760
|
+
)) })
|
|
9761
|
+
] }) : null,
|
|
9762
|
+
/* @__PURE__ */ jsx36("ul", { className: "flex flex-col gap-2", children: shown.map((portal) => {
|
|
9720
9763
|
const url = `${origin}${portalPath(portal.slug)}`;
|
|
9721
9764
|
const open = openId === portal.portal_id;
|
|
9722
9765
|
return /* @__PURE__ */ jsxs32("li", { className: "rounded-md border border-border bg-card p-2.5", children: [
|
|
@@ -10443,11 +10486,7 @@ function whenItFires(subscription) {
|
|
|
10443
10486
|
const every = subscription.cadence === "hourly" ? "an hourly summary" : subscription.cadence === "weekly" ? "a weekly summary" : "a daily summary";
|
|
10444
10487
|
return subscription.schedule ? `${every}, ${subscription.schedule}` : every;
|
|
10445
10488
|
}
|
|
10446
|
-
var CHANNEL_WORDS2 =
|
|
10447
|
-
in_app: "in the app",
|
|
10448
|
-
email: "by email \u2014 only if an address is on the account",
|
|
10449
|
-
sms: "by text \u2014 only if a number is on the account"
|
|
10450
|
-
};
|
|
10489
|
+
var CHANNEL_WORDS2 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
10451
10490
|
var DIGEST_SUGGESTION = "Email me a summary of this every Monday at 8 in the morning.";
|
|
10452
10491
|
function SubscriptionsPanel({ tableId, className }) {
|
|
10453
10492
|
const client = useRecordsClient27();
|
|
@@ -11223,7 +11262,12 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
|
|
|
11223
11262
|
Condition,
|
|
11224
11263
|
{
|
|
11225
11264
|
question,
|
|
11226
|
-
fieldKeys: fields.data ?? []
|
|
11265
|
+
fieldKeys: (fields.data ?? []).map((f) => ({
|
|
11266
|
+
id: String(f.id),
|
|
11267
|
+
key: f.key,
|
|
11268
|
+
label: f.label || f.key,
|
|
11269
|
+
field: f
|
|
11270
|
+
})),
|
|
11227
11271
|
onChange: (showIf) => patchQuestion(index, { showIf })
|
|
11228
11272
|
}
|
|
11229
11273
|
)
|
|
@@ -11961,17 +12005,8 @@ import { useCallback as useCallback26, useEffect as useEffect30, useState as use
|
|
|
11961
12005
|
import { useRecordsClient as useRecordsClient32, useTable as useTable16 } from "@ai-matrx/records/react";
|
|
11962
12006
|
import { Button as Button38, Skeleton as Skeleton23, cn as cn39 } from "@ai-matrx/design-system";
|
|
11963
12007
|
import { jsx as jsx45, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
11964
|
-
var CADENCE_WORDS2 =
|
|
11965
|
-
|
|
11966
|
-
hourly: "hourly summary",
|
|
11967
|
-
daily: "daily summary",
|
|
11968
|
-
weekly: "weekly summary"
|
|
11969
|
-
};
|
|
11970
|
-
var CHANNEL_WORDS3 = {
|
|
11971
|
-
in_app: "in the app",
|
|
11972
|
-
email: "by email",
|
|
11973
|
-
sms: "by text"
|
|
11974
|
-
};
|
|
12008
|
+
var CADENCE_WORDS2 = SUBSCRIPTION_CADENCE_LABEL;
|
|
12009
|
+
var CHANNEL_WORDS3 = SUBSCRIPTION_CHANNEL_LABEL;
|
|
11975
12010
|
function NotifyRuleEditor({ tableId, seed, className }) {
|
|
11976
12011
|
const client = useRecordsClient32();
|
|
11977
12012
|
const host = useRecordsUi();
|
|
@@ -13248,12 +13283,7 @@ function bookingSuggestion(tableName2) {
|
|
|
13248
13283
|
return `Let clients book a 30-minute ${subject} slot on Tuesday and Thursday afternoons.`;
|
|
13249
13284
|
}
|
|
13250
13285
|
var NO_ADMIN2 = "Opening a booking page lets people with no account take time in your calendar, so it needs the admin level on the table the appointments land in.";
|
|
13251
|
-
var STATE_WORDS =
|
|
13252
|
-
draft: "Not open yet \u2014 nobody can book until you open it.",
|
|
13253
|
-
open: "Open. Anyone with the link can book a time.",
|
|
13254
|
-
closed: "Closed. The link still works and says you are not taking bookings.",
|
|
13255
|
-
full: "Every time you offered is taken."
|
|
13256
|
-
};
|
|
13286
|
+
var STATE_WORDS = BOOKING_PAGE_STATE_LABEL;
|
|
13257
13287
|
function BookingSlots({ tableId, className }) {
|
|
13258
13288
|
const client = useRecordsClient36();
|
|
13259
13289
|
const host = useRecordsUi();
|
|
@@ -13277,10 +13307,21 @@ function BookingSlots({ tableId, className }) {
|
|
|
13277
13307
|
void load();
|
|
13278
13308
|
}, [load]);
|
|
13279
13309
|
const subjectIds = useMemo30(
|
|
13280
|
-
() => Array.from(
|
|
13281
|
-
|
|
13310
|
+
() => Array.from(
|
|
13311
|
+
/* @__PURE__ */ new Set([
|
|
13312
|
+
...(pages ?? []).map((p) => p.table_id),
|
|
13313
|
+
// THE EMPTY STATE ASKS THE SAME QUESTION. There is no page to read a
|
|
13314
|
+
// table off yet, and the empty state's two buttons need the answer
|
|
13315
|
+
// about the table this rail is standing in — otherwise the rail
|
|
13316
|
+
// offers a viewer both ways into an act the door will refuse.
|
|
13317
|
+
...tableId ? [tableId] : []
|
|
13318
|
+
])
|
|
13319
|
+
),
|
|
13320
|
+
[pages, tableId]
|
|
13282
13321
|
);
|
|
13283
13322
|
const levels = useMyLevels2(subjectIds);
|
|
13323
|
+
const levelsKnown = !levels.loading && Boolean(levels.data);
|
|
13324
|
+
const mayBuildHere = levelsKnown && Boolean(tableId) && levels.data?.[tableId] === "admin";
|
|
13284
13325
|
const toggle = useCallback30(
|
|
13285
13326
|
async (page) => {
|
|
13286
13327
|
setBusy(page.form_id);
|
|
@@ -13329,7 +13370,8 @@ function BookingSlots({ tableId, className }) {
|
|
|
13329
13370
|
pages.length === 0 && !building ? tableId ? /* @__PURE__ */ jsx50(
|
|
13330
13371
|
BuildOrAsk,
|
|
13331
13372
|
{
|
|
13332
|
-
mayBuild:
|
|
13373
|
+
mayBuild: mayBuildHere,
|
|
13374
|
+
whyNot: mayBuildHere ? null : levelsKnown ? NO_ADMIN2 : null,
|
|
13333
13375
|
onBuild: () => setBuilding(true),
|
|
13334
13376
|
...host.onAskForOne ? {
|
|
13335
13377
|
onAsk: () => host.onAskForOne?.({
|
|
@@ -13437,6 +13479,8 @@ import { Button as Button44, Input as Input4, Skeleton as Skeleton29, Textarea a
|
|
|
13437
13479
|
// src/CaptureRun.tsx
|
|
13438
13480
|
import { useCallback as useCallback31, useEffect as useEffect35, useMemo as useMemo31, useRef as useRef13, useState as useState47 } from "react";
|
|
13439
13481
|
import {
|
|
13482
|
+
coerceTypedAnswer as coerceTypedAnswer2,
|
|
13483
|
+
coercionLabel,
|
|
13440
13484
|
openCaptureQueue
|
|
13441
13485
|
} from "@ai-matrx/records";
|
|
13442
13486
|
import { useRecordsClient as useRecordsClient37 } from "@ai-matrx/records/react";
|
|
@@ -13742,7 +13786,11 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
13742
13786
|
value: String(answers[q.field] ?? ""),
|
|
13743
13787
|
onChange: (e) => {
|
|
13744
13788
|
const raw = e.target.value;
|
|
13745
|
-
|
|
13789
|
+
const answered2 = field ? coerceTypedAnswer2(field, raw, { label: q.ask ?? coercionLabel(field) }) : { value: raw };
|
|
13790
|
+
setAnswers((held) => ({
|
|
13791
|
+
...held,
|
|
13792
|
+
[q.field]: raw === "" ? "" : "refusal" in answered2 ? raw : answered2.value
|
|
13793
|
+
}));
|
|
13746
13794
|
setMissing(null);
|
|
13747
13795
|
}
|
|
13748
13796
|
}
|
|
@@ -13799,7 +13847,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
13799
13847
|
// src/CaptureSheet.tsx
|
|
13800
13848
|
import { Fragment as Fragment25, jsx as jsx52, jsxs as jsxs48 } from "react/jsx-runtime";
|
|
13801
13849
|
var CAPTURE_MODES = ["reading", "photo", "voice"];
|
|
13802
|
-
var IN_MEMORY_QUEUE_REASON = "
|
|
13850
|
+
var IN_MEMORY_QUEUE_REASON = "Anything you capture while offline is held only in this page until it can be sent, and it is lost if you close the tab. Keep this page open until you are back on a connection. It is said here rather than discovered later.";
|
|
13803
13851
|
function mintClientKey() {
|
|
13804
13852
|
const c = globalThis.crypto;
|
|
13805
13853
|
if (typeof c?.randomUUID === "function") return `capture:${c.randomUUID()}`;
|
|
@@ -14675,6 +14723,10 @@ import { useCallback as useCallback37, useEffect as useEffect41, useState as use
|
|
|
14675
14723
|
import { useRecordsClient as useRecordsClient43, useTable as useTable22 } from "@ai-matrx/records/react";
|
|
14676
14724
|
import { Button as Button48, Separator as Separator12, Skeleton as Skeleton34, cn as cn51 } from "@ai-matrx/design-system";
|
|
14677
14725
|
import { Fragment as Fragment28, jsx as jsx58, jsxs as jsxs53 } from "react/jsx-runtime";
|
|
14726
|
+
function cameFromLine(label, groupField) {
|
|
14727
|
+
const grouped = groupField ? `grouped into the same columns the chart used` : `for this table`;
|
|
14728
|
+
return `You came here from \u201C${label}\u201D. This is every record ${grouped} \u2014 not only the ones that number counted \u2014 so the column you clicked is here with the rest beside it.`;
|
|
14729
|
+
}
|
|
14678
14730
|
var TABLE_NOT_REACHABLE = "This table is not in the organization you are working in, so there is nothing here to show. Switch to the organization that owns it and open it again \u2014 or it may have been deleted.";
|
|
14679
14731
|
var DEFAULT_VIEW_NAME = "All records";
|
|
14680
14732
|
var VIEW_NOT_SAVED_YET = "This table has no saved view yet, so the layout you pick here is not being kept. Save one from the bar above and it is remembered.";
|
|
@@ -14718,6 +14770,8 @@ function TablePage({
|
|
|
14718
14770
|
activeRecordId,
|
|
14719
14771
|
activeView,
|
|
14720
14772
|
onViewChanged,
|
|
14773
|
+
activeGroupField,
|
|
14774
|
+
cameFrom,
|
|
14721
14775
|
className
|
|
14722
14776
|
}) {
|
|
14723
14777
|
const client = useRecordsClient43();
|
|
@@ -14911,10 +14965,24 @@ function TablePage({
|
|
|
14911
14965
|
] }),
|
|
14912
14966
|
main === "dashboards" ? /* @__PURE__ */ jsx58(DashboardCanvas, { tableId, activeDashboardId: activeDashboardId ?? null }) : /* @__PURE__ */ jsxs53(Fragment28, { children: [
|
|
14913
14967
|
opened.unknown ? /* @__PURE__ */ jsx58("p", { className: "rounded-md border border-amber-600/40 bg-amber-500/5 px-3 py-2 text-xs leading-relaxed text-amber-700 dark:border-amber-400/40 dark:text-amber-300", children: unknownViewLine(opened.unknown) }) : null,
|
|
14968
|
+
cameFrom ? /* @__PURE__ */ jsx58(
|
|
14969
|
+
"p",
|
|
14970
|
+
{
|
|
14971
|
+
"data-testid": "came-from-a-number",
|
|
14972
|
+
className: "rounded-md border px-3 py-2 text-xs leading-relaxed text-muted-foreground",
|
|
14973
|
+
children: cameFromLine(cameFrom, activeGroupField ?? null)
|
|
14974
|
+
}
|
|
14975
|
+
) : null,
|
|
14914
14976
|
/* @__PURE__ */ jsx58(
|
|
14915
14977
|
ViewSwitcher,
|
|
14916
14978
|
{
|
|
14917
|
-
view: {
|
|
14979
|
+
view: {
|
|
14980
|
+
...view ?? defaultView(tableId),
|
|
14981
|
+
layout: shownLayout,
|
|
14982
|
+
// The address's field wins for this visit; silence keeps the
|
|
14983
|
+
// saved view's own choice, exactly as it always did.
|
|
14984
|
+
...activeGroupField ? { groupField: activeGroupField } : {}
|
|
14985
|
+
},
|
|
14918
14986
|
pageSize,
|
|
14919
14987
|
onLayoutChange: (layout) => {
|
|
14920
14988
|
setLayoutFromLink(layout);
|
|
@@ -15016,8 +15084,12 @@ function TablePage({
|
|
|
15016
15084
|
] });
|
|
15017
15085
|
}
|
|
15018
15086
|
export {
|
|
15087
|
+
ABSENCE_WORD_LABEL,
|
|
15088
|
+
ABSENCE_WORD_VALUES,
|
|
15019
15089
|
ACTION_KINDS,
|
|
15020
15090
|
ActionInbox,
|
|
15091
|
+
BOOKING_PAGE_STATE_LABEL,
|
|
15092
|
+
BOOKING_PAGE_STATE_VALUES,
|
|
15021
15093
|
BookingBuilder,
|
|
15022
15094
|
BookingSlots,
|
|
15023
15095
|
BuildOrAsk,
|
|
@@ -15025,7 +15097,10 @@ export {
|
|
|
15025
15097
|
CHART_KINDS,
|
|
15026
15098
|
CHART_KIND_LABEL,
|
|
15027
15099
|
CHART_NEEDS,
|
|
15100
|
+
CHECKLIST_REQUIREMENT_KINDS,
|
|
15101
|
+
CHECKLIST_REQUIREMENT_LABEL,
|
|
15028
15102
|
CONDITION_OPS,
|
|
15103
|
+
CONTEXT_POLICY_LABEL,
|
|
15029
15104
|
CaptureRun,
|
|
15030
15105
|
CaptureSheet,
|
|
15031
15106
|
ChartBlock,
|
|
@@ -15041,17 +15116,25 @@ export {
|
|
|
15041
15116
|
CustomFieldsSection,
|
|
15042
15117
|
DEFAULT_FIELDS,
|
|
15043
15118
|
DEFAULT_VIEW_NAME,
|
|
15119
|
+
DUE_STATE_LABEL,
|
|
15044
15120
|
DashboardCanvas,
|
|
15045
15121
|
DigestScheduler,
|
|
15046
15122
|
DocRender,
|
|
15047
15123
|
DocTemplate,
|
|
15048
15124
|
EMPTY_PRESENTATION,
|
|
15125
|
+
ENGINEER_TEXT,
|
|
15049
15126
|
EmbedFrame,
|
|
15050
15127
|
EnrichBadge,
|
|
15051
15128
|
EnrichPanel,
|
|
15052
15129
|
ExportMenu,
|
|
15130
|
+
FIELD_RULE_KIND_LABEL,
|
|
15131
|
+
FIELD_RULE_KIND_VALUES,
|
|
15132
|
+
FIELD_SENSITIVITY_LABEL,
|
|
15053
15133
|
FIELD_TYPE_CHOICES,
|
|
15054
15134
|
FIELD_TYPE_GROUPS,
|
|
15135
|
+
FORMULA_OP_JOINS,
|
|
15136
|
+
FORMULA_OP_LABEL,
|
|
15137
|
+
FORMULA_OP_VALUES,
|
|
15055
15138
|
FORM_FLOWS,
|
|
15056
15139
|
FROZEN_COLUMN_WIDTH,
|
|
15057
15140
|
FieldControl,
|
|
@@ -15099,6 +15182,8 @@ export {
|
|
|
15099
15182
|
PAGE_VIEW_LABEL,
|
|
15100
15183
|
PARITY_LABEL,
|
|
15101
15184
|
PARITY_MADE_OF,
|
|
15185
|
+
PROPOSED_CHANGE_ACT_LABEL,
|
|
15186
|
+
PROPOSED_CHANGE_ACT_VALUES,
|
|
15102
15187
|
Peek,
|
|
15103
15188
|
PersonPicker,
|
|
15104
15189
|
PipelineBoard,
|
|
@@ -15109,6 +15194,8 @@ export {
|
|
|
15109
15194
|
ProposalRow,
|
|
15110
15195
|
ProvenanceBadge,
|
|
15111
15196
|
PublicViewPage,
|
|
15197
|
+
ROLLUP_AGG_LABEL,
|
|
15198
|
+
ROLLUP_AGG_VALUES,
|
|
15112
15199
|
RecordChat,
|
|
15113
15200
|
RecordChip,
|
|
15114
15201
|
RecordForm,
|
|
@@ -15124,8 +15211,13 @@ export {
|
|
|
15124
15211
|
SAY_WHAT_YOU_ARE_WAITING_FOR_AFTER_MS,
|
|
15125
15212
|
SERIES_COLORS,
|
|
15126
15213
|
SOMEBODY,
|
|
15214
|
+
STAGE_RULE_ON_FAIL_LABEL,
|
|
15127
15215
|
STORE_ANSWERS_THESE,
|
|
15128
15216
|
STORE_DECIDES_REASON,
|
|
15217
|
+
SUBSCRIPTION_CADENCES,
|
|
15218
|
+
SUBSCRIPTION_CADENCE_LABEL,
|
|
15219
|
+
SUBSCRIPTION_CHANNEL_LABEL,
|
|
15220
|
+
SUBSCRIPTION_CHANNEL_VALUES,
|
|
15129
15221
|
ShareControl,
|
|
15130
15222
|
SignBlock,
|
|
15131
15223
|
StageRulesSection,
|
|
@@ -15143,6 +15235,9 @@ export {
|
|
|
15143
15235
|
ViewBar,
|
|
15144
15236
|
ViewSwitcher,
|
|
15145
15237
|
WITHHELD_RECORD_LABEL,
|
|
15238
|
+
WORK_DUE_STATES,
|
|
15239
|
+
WORK_INBOX_KINDS,
|
|
15240
|
+
WORK_INBOX_KIND_LABEL,
|
|
15146
15241
|
WhoChangedSource,
|
|
15147
15242
|
actorBadge,
|
|
15148
15243
|
actorWords,
|
|
@@ -15151,6 +15246,7 @@ export {
|
|
|
15151
15246
|
blockFromSpec,
|
|
15152
15247
|
bodyForReading,
|
|
15153
15248
|
bodyFromKeys,
|
|
15249
|
+
cameFromLine,
|
|
15154
15250
|
cellState,
|
|
15155
15251
|
chooseSurface,
|
|
15156
15252
|
colorFromTheValue,
|
|
@@ -15182,6 +15278,7 @@ export {
|
|
|
15182
15278
|
hintIsMachineIdentity,
|
|
15183
15279
|
humanize,
|
|
15184
15280
|
idsOf,
|
|
15281
|
+
isEngineerText,
|
|
15185
15282
|
isId,
|
|
15186
15283
|
isMachineIdentity,
|
|
15187
15284
|
isPlainFieldType,
|