@ai-matrx/records-ui 0.65.0 → 0.67.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 +95 -0
- package/dist/index.cjs +94 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +124 -9
- package/dist/index.d.ts +124 -9
- package/dist/index.js +94 -19
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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 {
|
|
@@ -571,6 +608,9 @@ function isRelationFieldType(id) {
|
|
|
571
608
|
function parityTypesWithNoExplanation() {
|
|
572
609
|
return PARITY_FIELD_TYPES.map((row) => row.parity_type).filter((type) => !BY_ID.has(type));
|
|
573
610
|
}
|
|
611
|
+
var ADD_FIELD_OPEN_LABEL = "Add field";
|
|
612
|
+
var ADD_FIELD_SAVE_LABEL = "Create field";
|
|
613
|
+
var EDIT_FIELD_SAVE_LABEL = "Save field";
|
|
574
614
|
|
|
575
615
|
// src/parity.ts
|
|
576
616
|
var kernelId = (name) => KERNEL_TABLES.find((t) => t.name === name)?.id ?? "";
|
|
@@ -2482,7 +2522,7 @@ import {
|
|
|
2482
2522
|
cn as cn8
|
|
2483
2523
|
} from "@ai-matrx/design-system";
|
|
2484
2524
|
import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2485
|
-
var NO_ENRICH_REASON = "
|
|
2525
|
+
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.";
|
|
2486
2526
|
function cellState(cell) {
|
|
2487
2527
|
if (!cell || !cell.agent_owned) return "none";
|
|
2488
2528
|
if (cell.pinned) return "yours";
|
|
@@ -4781,7 +4821,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
4781
4821
|
size: "sm",
|
|
4782
4822
|
disabled: mutation.saving || missing !== null,
|
|
4783
4823
|
onClick: () => void save(),
|
|
4784
|
-
children: mutation.saving ? "Saving\u2026" : field ?
|
|
4824
|
+
children: mutation.saving ? "Saving\u2026" : field ? EDIT_FIELD_SAVE_LABEL : ADD_FIELD_SAVE_LABEL
|
|
4785
4825
|
}
|
|
4786
4826
|
),
|
|
4787
4827
|
onCancel ? /* @__PURE__ */ jsx17(Button12, { size: "sm", variant: "ghost", onClick: onCancel, children: "Cancel" }) : null,
|
|
@@ -5816,7 +5856,7 @@ import { useEffect as useEffect9, useMemo as useMemo14, useState as useState18 }
|
|
|
5816
5856
|
import { useRecordsClient as useRecordsClient12 } from "@ai-matrx/records/react";
|
|
5817
5857
|
import { Skeleton as Skeleton4, cn as cn16 } from "@ai-matrx/design-system";
|
|
5818
5858
|
import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
5819
|
-
var NO_CHAT_REASON = "
|
|
5859
|
+
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.";
|
|
5820
5860
|
function RecordChat({ tableId, recordId, className }) {
|
|
5821
5861
|
const client = useRecordsClient12();
|
|
5822
5862
|
const host = useRecordsUi();
|
|
@@ -9760,6 +9800,7 @@ function PortalsPanel({ tableId, className }) {
|
|
|
9760
9800
|
{
|
|
9761
9801
|
portalId: portal.portal_id,
|
|
9762
9802
|
...tableId ? { tableId } : {},
|
|
9803
|
+
origin,
|
|
9763
9804
|
onChanged: () => void load()
|
|
9764
9805
|
}
|
|
9765
9806
|
) : null
|
|
@@ -9767,7 +9808,7 @@ function PortalsPanel({ tableId, className }) {
|
|
|
9767
9808
|
}) })
|
|
9768
9809
|
] });
|
|
9769
9810
|
}
|
|
9770
|
-
function CopyLink({ url }) {
|
|
9811
|
+
function CopyLink({ url, what }) {
|
|
9771
9812
|
const [copied, setCopied] = useState33(false);
|
|
9772
9813
|
const [shown, setShown] = useState33(false);
|
|
9773
9814
|
return /* @__PURE__ */ jsxs32(Fragment16, { children: [
|
|
@@ -9788,7 +9829,7 @@ function CopyLink({ url }) {
|
|
|
9788
9829
|
}
|
|
9789
9830
|
})();
|
|
9790
9831
|
},
|
|
9791
|
-
children: copied ? "Copied" : "Copy link"
|
|
9832
|
+
children: copied ? "Copied" : what ? `Copy ${what}` : "Copy link"
|
|
9792
9833
|
}
|
|
9793
9834
|
),
|
|
9794
9835
|
shown ? /* @__PURE__ */ jsxs32("p", { className: "mt-1.5 w-full break-all rounded border border-dashed border-border px-2 py-1 text-xs", children: [
|
|
@@ -9800,6 +9841,7 @@ function CopyLink({ url }) {
|
|
|
9800
9841
|
function PortalDetail({
|
|
9801
9842
|
portalId,
|
|
9802
9843
|
tableId,
|
|
9844
|
+
origin,
|
|
9803
9845
|
onChanged
|
|
9804
9846
|
}) {
|
|
9805
9847
|
const client = useRecordsClient25();
|
|
@@ -9849,6 +9891,7 @@ function PortalDetail({
|
|
|
9849
9891
|
PortalCardView,
|
|
9850
9892
|
{
|
|
9851
9893
|
card,
|
|
9894
|
+
origin,
|
|
9852
9895
|
...tableId ? { tableId } : {},
|
|
9853
9896
|
onRevoke: revoke,
|
|
9854
9897
|
onPreview: preview,
|
|
@@ -9871,6 +9914,7 @@ function PortalCardView({
|
|
|
9871
9914
|
onRevoke,
|
|
9872
9915
|
onPreview,
|
|
9873
9916
|
invite,
|
|
9917
|
+
origin,
|
|
9874
9918
|
className
|
|
9875
9919
|
}) {
|
|
9876
9920
|
return /* @__PURE__ */ jsxs32("div", { className: cn30("mt-2.5 flex flex-col gap-2.5 border-t border-border pt-2.5", className), children: [
|
|
@@ -9901,7 +9945,7 @@ function PortalCardView({
|
|
|
9901
9945
|
] }, exposed.table_id))
|
|
9902
9946
|
] }),
|
|
9903
9947
|
/* @__PURE__ */ jsx36(Separator10, {}),
|
|
9904
|
-
/* @__PURE__ */ jsx36(People, { card, onRevoke, onPreview }),
|
|
9948
|
+
/* @__PURE__ */ jsx36(People, { card, onRevoke, onPreview, origin }),
|
|
9905
9949
|
invite ? /* @__PURE__ */ jsxs32(Fragment16, { children: [
|
|
9906
9950
|
/* @__PURE__ */ jsx36(Separator10, {}),
|
|
9907
9951
|
invite
|
|
@@ -9911,8 +9955,10 @@ function PortalCardView({
|
|
|
9911
9955
|
function People({
|
|
9912
9956
|
card,
|
|
9913
9957
|
onRevoke,
|
|
9914
|
-
onPreview
|
|
9958
|
+
onPreview,
|
|
9959
|
+
origin
|
|
9915
9960
|
}) {
|
|
9961
|
+
const here = origin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
9916
9962
|
const [askingToRevoke, setAskingToRevoke] = useState33(null);
|
|
9917
9963
|
const [busy, setBusy] = useState33(false);
|
|
9918
9964
|
const [said, setSaid] = useState33(null);
|
|
@@ -9954,6 +10000,7 @@ function People({
|
|
|
9954
10000
|
/* @__PURE__ */ jsx36("p", { className: "mt-0.5 break-all text-muted-foreground", children: person.email }),
|
|
9955
10001
|
/* @__PURE__ */ jsx36("p", { className: "mt-0.5 text-muted-foreground", children: state.why }),
|
|
9956
10002
|
person.is_active ? /* @__PURE__ */ jsxs32("div", { className: "mt-1.5 flex flex-wrap gap-1.5", children: [
|
|
10003
|
+
person.accept_path ? /* @__PURE__ */ jsx36(CopyLink, { url: `${here}${person.accept_path}`, what: "their link" }) : null,
|
|
9957
10004
|
/* @__PURE__ */ jsx36(
|
|
9958
10005
|
Button30,
|
|
9959
10006
|
{
|
|
@@ -9965,6 +10012,11 @@ function People({
|
|
|
9965
10012
|
),
|
|
9966
10013
|
/* @__PURE__ */ jsx36(Button30, { size: "sm", variant: "ghost", onClick: () => setAskingToRevoke(person), children: "Remove their access" })
|
|
9967
10014
|
] }) : null,
|
|
10015
|
+
person.is_active && !person.signed_in && !person.accept_path ? /* @__PURE__ */ jsxs32("p", { className: "mt-1.5 text-muted-foreground", children: [
|
|
10016
|
+
"There is no link to copy for ",
|
|
10017
|
+
person.client || person.email,
|
|
10018
|
+
": they were asked some other way, or the link they were sent has expired. Invite them again and a fresh one is made."
|
|
10019
|
+
] }) : null,
|
|
9968
10020
|
asking ? /* @__PURE__ */ jsxs32("div", { className: "mt-1.5 flex flex-col gap-1.5 rounded border border-destructive/40 px-2 py-1.5", children: [
|
|
9969
10021
|
/* @__PURE__ */ jsx36("span", { className: "text-destructive", children: revokeConsequence(person, card.title) }),
|
|
9970
10022
|
/* @__PURE__ */ jsxs32("div", { className: "flex flex-wrap gap-1.5", children: [
|
|
@@ -13810,7 +13862,7 @@ function CaptureRun({ sheetId, face: given, className }) {
|
|
|
13810
13862
|
// src/CaptureSheet.tsx
|
|
13811
13863
|
import { Fragment as Fragment25, jsx as jsx52, jsxs as jsxs48 } from "react/jsx-runtime";
|
|
13812
13864
|
var CAPTURE_MODES = ["reading", "photo", "voice"];
|
|
13813
|
-
var IN_MEMORY_QUEUE_REASON = "
|
|
13865
|
+
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.";
|
|
13814
13866
|
function mintClientKey() {
|
|
13815
13867
|
const c = globalThis.crypto;
|
|
13816
13868
|
if (typeof c?.randomUUID === "function") return `capture:${c.randomUUID()}`;
|
|
@@ -14686,6 +14738,10 @@ import { useCallback as useCallback37, useEffect as useEffect41, useState as use
|
|
|
14686
14738
|
import { useRecordsClient as useRecordsClient43, useTable as useTable22 } from "@ai-matrx/records/react";
|
|
14687
14739
|
import { Button as Button48, Separator as Separator12, Skeleton as Skeleton34, cn as cn51 } from "@ai-matrx/design-system";
|
|
14688
14740
|
import { Fragment as Fragment28, jsx as jsx58, jsxs as jsxs53 } from "react/jsx-runtime";
|
|
14741
|
+
function cameFromLine(label, groupField) {
|
|
14742
|
+
const grouped = groupField ? `grouped into the same columns the chart used` : `for this table`;
|
|
14743
|
+
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.`;
|
|
14744
|
+
}
|
|
14689
14745
|
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.";
|
|
14690
14746
|
var DEFAULT_VIEW_NAME = "All records";
|
|
14691
14747
|
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.";
|
|
@@ -14729,6 +14785,8 @@ function TablePage({
|
|
|
14729
14785
|
activeRecordId,
|
|
14730
14786
|
activeView,
|
|
14731
14787
|
onViewChanged,
|
|
14788
|
+
activeGroupField,
|
|
14789
|
+
cameFrom,
|
|
14732
14790
|
className
|
|
14733
14791
|
}) {
|
|
14734
14792
|
const client = useRecordsClient43();
|
|
@@ -14814,7 +14872,7 @@ function TablePage({
|
|
|
14814
14872
|
variant: surfaceChosen(surface, { rail: "field" }) ? "secondary" : "outline",
|
|
14815
14873
|
"aria-pressed": surfaceChosen(surface, { rail: "field" }),
|
|
14816
14874
|
onClick: () => press({ rail: "field" }),
|
|
14817
|
-
children:
|
|
14875
|
+
children: ADD_FIELD_OPEN_LABEL
|
|
14818
14876
|
}
|
|
14819
14877
|
),
|
|
14820
14878
|
/* @__PURE__ */ jsx58(
|
|
@@ -14922,10 +14980,24 @@ function TablePage({
|
|
|
14922
14980
|
] }),
|
|
14923
14981
|
main === "dashboards" ? /* @__PURE__ */ jsx58(DashboardCanvas, { tableId, activeDashboardId: activeDashboardId ?? null }) : /* @__PURE__ */ jsxs53(Fragment28, { children: [
|
|
14924
14982
|
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,
|
|
14983
|
+
cameFrom ? /* @__PURE__ */ jsx58(
|
|
14984
|
+
"p",
|
|
14985
|
+
{
|
|
14986
|
+
"data-testid": "came-from-a-number",
|
|
14987
|
+
className: "rounded-md border px-3 py-2 text-xs leading-relaxed text-muted-foreground",
|
|
14988
|
+
children: cameFromLine(cameFrom, activeGroupField ?? null)
|
|
14989
|
+
}
|
|
14990
|
+
) : null,
|
|
14925
14991
|
/* @__PURE__ */ jsx58(
|
|
14926
14992
|
ViewSwitcher,
|
|
14927
14993
|
{
|
|
14928
|
-
view: {
|
|
14994
|
+
view: {
|
|
14995
|
+
...view ?? defaultView(tableId),
|
|
14996
|
+
layout: shownLayout,
|
|
14997
|
+
// The address's field wins for this visit; silence keeps the
|
|
14998
|
+
// saved view's own choice, exactly as it always did.
|
|
14999
|
+
...activeGroupField ? { groupField: activeGroupField } : {}
|
|
15000
|
+
},
|
|
14929
15001
|
pageSize,
|
|
14930
15002
|
onLayoutChange: (layout) => {
|
|
14931
15003
|
setLayoutFromLink(layout);
|
|
@@ -15065,6 +15137,7 @@ export {
|
|
|
15065
15137
|
DocRender,
|
|
15066
15138
|
DocTemplate,
|
|
15067
15139
|
EMPTY_PRESENTATION,
|
|
15140
|
+
ENGINEER_TEXT,
|
|
15068
15141
|
EmbedFrame,
|
|
15069
15142
|
EnrichBadge,
|
|
15070
15143
|
EnrichPanel,
|
|
@@ -15188,6 +15261,7 @@ export {
|
|
|
15188
15261
|
blockFromSpec,
|
|
15189
15262
|
bodyForReading,
|
|
15190
15263
|
bodyFromKeys,
|
|
15264
|
+
cameFromLine,
|
|
15191
15265
|
cellState,
|
|
15192
15266
|
chooseSurface,
|
|
15193
15267
|
colorFromTheValue,
|
|
@@ -15219,6 +15293,7 @@ export {
|
|
|
15219
15293
|
hintIsMachineIdentity,
|
|
15220
15294
|
humanize,
|
|
15221
15295
|
idsOf,
|
|
15296
|
+
isEngineerText,
|
|
15222
15297
|
isId,
|
|
15223
15298
|
isMachineIdentity,
|
|
15224
15299
|
isPlainFieldType,
|