@ai-matrx/records-ui 0.101.11 → 0.101.14
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 +12 -0
- package/dist/index.cjs +138 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +138 -32
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records-ui
|
|
2
2
|
|
|
3
|
+
## 0.101.14
|
|
4
|
+
|
|
5
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
6
|
+
`git diff npm/records-ui/v0.101.11..npm/records-ui/v0.101.14 -- apps/shared/records-ui`).
|
|
7
|
+
No source changes intended and no consumer action required.
|
|
8
|
+
|
|
9
|
+
## 0.101.12
|
|
10
|
+
|
|
11
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
12
|
+
`git diff npm/records-ui/v0.101.11..npm/records-ui/v0.101.12 -- apps/shared/records-ui`).
|
|
13
|
+
No source changes intended and no consumer action required.
|
|
14
|
+
|
|
3
15
|
## 0.101.11
|
|
4
16
|
|
|
5
17
|
- **The Action Inbox key hints answer to the inbox's width, not the screen's.** In a ~360px window on
|
package/dist/index.cjs
CHANGED
|
@@ -13732,6 +13732,44 @@ function replanPastedBlock(plan, args, change = {}) {
|
|
|
13732
13732
|
droppedLines: (plan.droppedLines ?? 0) + dropped
|
|
13733
13733
|
});
|
|
13734
13734
|
}
|
|
13735
|
+
function storedValueAgrees(field2, stored, written, options) {
|
|
13736
|
+
const empty = (v) => v === void 0 || v === null || v === "";
|
|
13737
|
+
if (empty(stored) || empty(written)) return empty(stored) && empty(written);
|
|
13738
|
+
if (field2 !== void 0) {
|
|
13739
|
+
if (isDateField(field2)) {
|
|
13740
|
+
if (hasTimePart(written)) {
|
|
13741
|
+
const a = new Date(stored).getTime();
|
|
13742
|
+
const b = new Date(written).getTime();
|
|
13743
|
+
if (!Number.isNaN(a) && !Number.isNaN(b)) return a === b;
|
|
13744
|
+
} else {
|
|
13745
|
+
const dayOf = (v) => typeof v === "string" && /^\d{4}-\d{2}-\d{2}/.test(v.trim()) ? v.trim().slice(0, 10) : calendarDayOf(v);
|
|
13746
|
+
const a = dayOf(stored);
|
|
13747
|
+
const b = dayOf(written);
|
|
13748
|
+
if (a !== null && b !== null) return a === b;
|
|
13749
|
+
}
|
|
13750
|
+
}
|
|
13751
|
+
const kind = editorKindFor(field2);
|
|
13752
|
+
if (kind === "number" || kind === "currency" || kind === "percent") {
|
|
13753
|
+
const a = typeof stored === "number" ? stored : Number(String(stored).trim());
|
|
13754
|
+
const b = typeof written === "number" ? written : Number(String(written).trim());
|
|
13755
|
+
if (Number.isFinite(a) && Number.isFinite(b)) return a === b;
|
|
13756
|
+
}
|
|
13757
|
+
if (options !== void 0 && typeof stored === "string" && typeof written === "string") {
|
|
13758
|
+
const option = (v) => options.byWord.get(v.trim().toLowerCase()) ?? v.trim().toLowerCase();
|
|
13759
|
+
return option(stored) === option(written);
|
|
13760
|
+
}
|
|
13761
|
+
}
|
|
13762
|
+
return JSON.stringify(stored) === JSON.stringify(written);
|
|
13763
|
+
}
|
|
13764
|
+
function columnsTheGridTurnedAway(turnedAway, columnKeys, columnName, already) {
|
|
13765
|
+
const said = /* @__PURE__ */ new Map();
|
|
13766
|
+
for (const refused of turnedAway) {
|
|
13767
|
+
if (!columnKeys.includes(refused.columnId)) continue;
|
|
13768
|
+
if (already.some((r) => r.column === columnName(refused.columnId))) continue;
|
|
13769
|
+
said.set(refused.columnId, refused.reason);
|
|
13770
|
+
}
|
|
13771
|
+
return [...said].map(([columnId, why]) => ({ fromLine: 0, column: columnName(columnId), raw: "", why }));
|
|
13772
|
+
}
|
|
13735
13773
|
|
|
13736
13774
|
// src/gridControls.tsx
|
|
13737
13775
|
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
@@ -15503,12 +15541,7 @@ function PastePreview({
|
|
|
15503
15541
|
plan.refusals.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { "data-matrx-paste-refusals": true, className: "flex flex-col gap-1", children: [
|
|
15504
15542
|
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)("p", { className: "text-xs font-medium", children: plan.refusals.length === 1 ? "One cell will be left alone:" : `${plan.refusals.length} cells will be left alone:` }),
|
|
15505
15543
|
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)("ul", { className: "flex flex-col gap-1", children: plan.refusals.slice(0, 12).map((refusal3, index) => /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("li", { className: "text-xs text-muted-foreground", children: [
|
|
15506
|
-
/* @__PURE__ */ (0, import_jsx_runtime51.
|
|
15507
|
-
"Row ",
|
|
15508
|
-
refusal3.fromLine,
|
|
15509
|
-
", ",
|
|
15510
|
-
refusal3.column
|
|
15511
|
-
] }),
|
|
15544
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "font-medium", children: refusal3.fromLine > 0 ? `Row ${refusal3.fromLine}, ${refusal3.column}` : refusal3.column }),
|
|
15512
15545
|
" \u2014 ",
|
|
15513
15546
|
refusal3.why
|
|
15514
15547
|
] }, index)) }),
|
|
@@ -21280,19 +21313,9 @@ function GridBody({
|
|
|
21280
21313
|
pastRightEdge: sheetPlan.pastRightEdge,
|
|
21281
21314
|
...headed ? { header: { unmatched: headed.unmatched } } : {}
|
|
21282
21315
|
});
|
|
21283
|
-
|
|
21284
|
-
|
|
21285
|
-
|
|
21286
|
-
notWritable.set(refused.columnId, refused.reason);
|
|
21287
|
-
}
|
|
21288
|
-
for (const [columnId, why] of notWritable) {
|
|
21289
|
-
built.refusals.push({
|
|
21290
|
-
fromLine: 0,
|
|
21291
|
-
column: columnLabel(fieldList, columnId),
|
|
21292
|
-
raw: "",
|
|
21293
|
-
why
|
|
21294
|
-
});
|
|
21295
|
-
}
|
|
21316
|
+
built.refusals.push(
|
|
21317
|
+
...columnsTheGridTurnedAway(sheetPlan.refusals, columnKeys, (id) => columnLabel(fieldList, id), built.refusals)
|
|
21318
|
+
);
|
|
21296
21319
|
if (merged && built.cells > 0 && built.creates === 0 && built.refusals.length === 0 && built.pastRightEdge === 0 && (built.header?.unmatched.length ?? 0) === 0 && built.unmatchedOptions.length === 0) {
|
|
21297
21320
|
const outcome = await pasteAtOnce.current(built);
|
|
21298
21321
|
if (outcome.refusals.length > 0 || outcome.missing > 0) {
|
|
@@ -21393,20 +21416,21 @@ function GridBody({
|
|
|
21393
21416
|
const reread = await client.listByIds({ table_id: tableId, ids: expected.map((e) => e.recordId) });
|
|
21394
21417
|
let readBack = 0;
|
|
21395
21418
|
let missing = 0;
|
|
21419
|
+
const fieldOf2 = new Map((fields.data ?? []).map((f) => [f.key, f]));
|
|
21396
21420
|
const refusals = [];
|
|
21397
21421
|
if (!reread.ok) refusals.push(`The rows were written and this could not read them back to check: ${refusalSentence(reread.error)}`);
|
|
21398
21422
|
const seen = new Map((reread.ok ? reread.data : []).map((r) => [r.id, r.document ?? {}]));
|
|
21399
21423
|
for (const entry of expected) {
|
|
21400
21424
|
const stored = seen.get(entry.recordId);
|
|
21401
21425
|
for (const [key, value] of Object.entries(entry.document)) {
|
|
21402
|
-
if (stored !== void 0 &&
|
|
21426
|
+
if (stored !== void 0 && storedValueAgrees(fieldOf2.get(key), stored[key], value, choiceOptions.current.get(key))) readBack += 1;
|
|
21403
21427
|
else missing += 1;
|
|
21404
21428
|
}
|
|
21405
21429
|
}
|
|
21406
21430
|
records.reload();
|
|
21407
21431
|
return { written: lines, readBack, missing, refusals };
|
|
21408
21432
|
},
|
|
21409
|
-
[rows, client, tableId, editing, undo, records, versions]
|
|
21433
|
+
[rows, client, tableId, editing, undo, records, versions, fields.data]
|
|
21410
21434
|
);
|
|
21411
21435
|
const pasteAtOnce = (0, import_react107.useRef)(applyPasteAsOne);
|
|
21412
21436
|
pasteAtOnce.current = applyPasteAsOne;
|
|
@@ -21481,6 +21505,7 @@ function GridBody({
|
|
|
21481
21505
|
}
|
|
21482
21506
|
let readBack = 0;
|
|
21483
21507
|
let missing = 0;
|
|
21508
|
+
const fieldOf2 = new Map((fields.data ?? []).map((f) => [f.key, f]));
|
|
21484
21509
|
const seen = /* @__PURE__ */ new Map();
|
|
21485
21510
|
const READ_BACK_PAGE = 500;
|
|
21486
21511
|
for (let offset = 0; ; offset += READ_BACK_PAGE) {
|
|
@@ -21500,7 +21525,7 @@ function GridBody({
|
|
|
21500
21525
|
for (const entry of expected) {
|
|
21501
21526
|
const stored = seen.get(entry.recordId);
|
|
21502
21527
|
for (const [key, value] of Object.entries(entry.document)) {
|
|
21503
|
-
if (stored !== void 0 &&
|
|
21528
|
+
if (stored !== void 0 && storedValueAgrees(fieldOf2.get(key), stored[key], value, choiceOptions.current.get(key))) {
|
|
21504
21529
|
readBack += 1;
|
|
21505
21530
|
} else {
|
|
21506
21531
|
missing += 1;
|
|
@@ -21509,7 +21534,7 @@ function GridBody({
|
|
|
21509
21534
|
}
|
|
21510
21535
|
records.reload();
|
|
21511
21536
|
return { written, readBack, missing, refusals };
|
|
21512
|
-
}, [pastePlan, client, tableId, records, merged, applyPasteAsOne, versions]);
|
|
21537
|
+
}, [pastePlan, client, tableId, records, merged, applyPasteAsOne, versions, fields.data]);
|
|
21513
21538
|
const columnIds = (0, import_react107.useMemo)(() => ordered.map((field2) => field2.key), [ordered]);
|
|
21514
21539
|
const lookWrites = (0, import_react107.useRef)(null);
|
|
21515
21540
|
const writeLook = (0, import_react107.useCallback)(
|
|
@@ -23132,6 +23157,7 @@ var import_records40 = require("@ai-matrx/records");
|
|
|
23132
23157
|
var import_design_system67 = require("@ai-matrx/design-system");
|
|
23133
23158
|
|
|
23134
23159
|
// src/newTableColumns.ts
|
|
23160
|
+
var import_templates = require("@ai-matrx/records/templates");
|
|
23135
23161
|
var NEW_COLUMN_TYPES = [
|
|
23136
23162
|
{ type: "text", words: "Text" },
|
|
23137
23163
|
{ type: "rich_text", words: "Formatted text" },
|
|
@@ -23149,7 +23175,80 @@ var NEW_COLUMN_TYPES = [
|
|
|
23149
23175
|
];
|
|
23150
23176
|
var MOST_NEW_COLUMNS = 40;
|
|
23151
23177
|
var BULLET = /^\s*(?:[-*•]|\d+[.)])\s+/;
|
|
23152
|
-
|
|
23178
|
+
var TYPE_WORD_ALIASES = {
|
|
23179
|
+
choice: "select",
|
|
23180
|
+
dropdown: "select",
|
|
23181
|
+
status: "select",
|
|
23182
|
+
list: "select",
|
|
23183
|
+
choices: "multi_select",
|
|
23184
|
+
tags: "multi_select",
|
|
23185
|
+
money: "currency",
|
|
23186
|
+
boolean: "checkbox",
|
|
23187
|
+
bool: "checkbox",
|
|
23188
|
+
"yes/no": "checkbox",
|
|
23189
|
+
"yes / no": "checkbox",
|
|
23190
|
+
link: "url",
|
|
23191
|
+
integer: "number",
|
|
23192
|
+
decimal: "number",
|
|
23193
|
+
float: "number",
|
|
23194
|
+
string: "text",
|
|
23195
|
+
long_text: "rich_text",
|
|
23196
|
+
"formatted text": "rich_text",
|
|
23197
|
+
"date and time": "datetime",
|
|
23198
|
+
"date time": "datetime"
|
|
23199
|
+
};
|
|
23200
|
+
function typeFromWord(raw) {
|
|
23201
|
+
const word = raw.trim().toLowerCase();
|
|
23202
|
+
if (word === "") return void 0;
|
|
23203
|
+
const type = TYPE_WORD_ALIASES[word] ?? NEW_COLUMN_TYPES.find((t) => t.type === word.replace(/[\s-]+/g, "_"))?.type;
|
|
23204
|
+
if (!type) return void 0;
|
|
23205
|
+
return import_templates.RESOLVABLE_FIELD_TYPE_WORDS.includes(type === "date" ? "datetime" : type) ? type : void 0;
|
|
23206
|
+
}
|
|
23207
|
+
function maskBrackets(text) {
|
|
23208
|
+
let depth = 0;
|
|
23209
|
+
let out = "";
|
|
23210
|
+
for (const ch of text) {
|
|
23211
|
+
if (ch === "(" || ch === "[") {
|
|
23212
|
+
depth++;
|
|
23213
|
+
out += depth === 1 ? ch : "\0";
|
|
23214
|
+
} else if ((ch === ")" || ch === "]") && depth > 0) {
|
|
23215
|
+
depth--;
|
|
23216
|
+
out += depth === 0 ? ch : "\0";
|
|
23217
|
+
} else out += depth > 0 ? "\0" : ch;
|
|
23218
|
+
}
|
|
23219
|
+
return out;
|
|
23220
|
+
}
|
|
23221
|
+
function splitOutsideBrackets(text, sep) {
|
|
23222
|
+
const masked = maskBrackets(text);
|
|
23223
|
+
const parts = [];
|
|
23224
|
+
let from = 0;
|
|
23225
|
+
const re = new RegExp(sep.source, sep.flags.includes("g") ? sep.flags : sep.flags + "g");
|
|
23226
|
+
for (const m of masked.matchAll(re)) {
|
|
23227
|
+
if (m[0] === "") continue;
|
|
23228
|
+
parts.push(text.slice(from, m.index));
|
|
23229
|
+
from = m.index + m[0].length;
|
|
23230
|
+
}
|
|
23231
|
+
parts.push(text.slice(from));
|
|
23232
|
+
return parts;
|
|
23233
|
+
}
|
|
23234
|
+
function specFromPart(part) {
|
|
23235
|
+
const clean = (t) => t.replace(/^["'\s]+|["'.\s]+$/g, "").replace(/\s+/g, " ");
|
|
23236
|
+
const m = /^(.*?)\s*[(\[]([^()\[\]]*)[)\]]\s*$/.exec(part.trim());
|
|
23237
|
+
if (m) {
|
|
23238
|
+
const inner2 = m[2];
|
|
23239
|
+
const colon = inner2.indexOf(":");
|
|
23240
|
+
const type = typeFromWord(colon >= 0 ? inner2.slice(0, colon) : inner2);
|
|
23241
|
+
const name = clean(m[1]);
|
|
23242
|
+
if (type && name !== "") {
|
|
23243
|
+
const options = colon >= 0 ? [...new Set(inner2.slice(colon + 1).split(/\s*[,;|]\s*/).map(clean).filter((o) => o !== ""))] : void 0;
|
|
23244
|
+
return { label: name, type, ...options && options.length > 0 && (type === "select" || type === "multi_select") ? { options } : {} };
|
|
23245
|
+
}
|
|
23246
|
+
}
|
|
23247
|
+
const withoutPairs = part.replace(/\([^()\[\]]*\)|\[[^()\[\]]*\]/g, "");
|
|
23248
|
+
const label = /[()\[\]]/.test(withoutPairs) ? part.replace(/[()\[\]]/g, " ") : part;
|
|
23249
|
+
return { label: clean(label) };
|
|
23250
|
+
}
|
|
23251
|
+
function specsFrom(text) {
|
|
23153
23252
|
const trimmed = text.trim();
|
|
23154
23253
|
if (trimmed === "") return [];
|
|
23155
23254
|
let parts;
|
|
@@ -23160,23 +23259,28 @@ function columnNamesFrom(text) {
|
|
|
23160
23259
|
parts = trimmed.split(/\r?\n/).map((line) => line.replace(BULLET, ""));
|
|
23161
23260
|
} else {
|
|
23162
23261
|
let line = trimmed;
|
|
23163
|
-
const
|
|
23262
|
+
const masked = maskBrackets(line);
|
|
23263
|
+
const colon = masked.indexOf(":");
|
|
23164
23264
|
if (colon >= 0) line = line.slice(colon + 1);
|
|
23165
23265
|
else {
|
|
23166
|
-
const withAt =
|
|
23266
|
+
const withAt = masked.toLowerCase().lastIndexOf(" with ");
|
|
23167
23267
|
if (withAt >= 0) line = line.slice(withAt + " with ".length);
|
|
23168
23268
|
}
|
|
23169
|
-
parts = line
|
|
23269
|
+
parts = splitOutsideBrackets(line, /\s*[,;|]\s*|\s+and\s+|\s*&\s*/i);
|
|
23170
23270
|
}
|
|
23171
|
-
const
|
|
23271
|
+
const specs = parts.map(specFromPart).filter((c) => c.label !== "").map((c) => c.label === c.label.toLowerCase() ? { ...c, label: c.label[0].toUpperCase() + c.label.slice(1) } : c);
|
|
23172
23272
|
const seen = /* @__PURE__ */ new Set();
|
|
23173
|
-
const once =
|
|
23174
|
-
const k =
|
|
23273
|
+
const once = specs.filter((c) => {
|
|
23274
|
+
const k = c.label.toLowerCase();
|
|
23175
23275
|
if (seen.has(k)) return false;
|
|
23176
23276
|
seen.add(k);
|
|
23177
23277
|
return true;
|
|
23178
23278
|
});
|
|
23179
|
-
|
|
23279
|
+
const labels = uniqueLabels(once.map((c) => c.label));
|
|
23280
|
+
return once.map((c, i) => ({ ...c, label: labels[i] })).slice(0, MOST_NEW_COLUMNS);
|
|
23281
|
+
}
|
|
23282
|
+
function columnNamesFrom(text) {
|
|
23283
|
+
return specsFrom(text).map((c) => c.label);
|
|
23180
23284
|
}
|
|
23181
23285
|
var STATUS_CHOICES = ["Not started", "In progress", "Done"];
|
|
23182
23286
|
var PRIORITY_CHOICES = ["Low", "Medium", "High"];
|
|
@@ -23201,7 +23305,9 @@ function columnFromName(label) {
|
|
|
23201
23305
|
return { label, type: "text" };
|
|
23202
23306
|
}
|
|
23203
23307
|
function columnsFrom(text) {
|
|
23204
|
-
return
|
|
23308
|
+
return specsFrom(text).map(
|
|
23309
|
+
(c) => c.type ? { label: c.label, type: c.type, ...c.options && c.options.length > 0 ? { options: c.options } : {} } : columnFromName(c.label)
|
|
23310
|
+
);
|
|
23205
23311
|
}
|
|
23206
23312
|
function fieldsForNewColumns(columns) {
|
|
23207
23313
|
const keys = /* @__PURE__ */ new Set();
|