@ai-matrx/records-ui 0.66.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 +43 -0
- package/dist/index.cjs +21 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +21 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records-ui
|
|
2
2
|
|
|
3
|
+
## 0.67.0
|
|
4
|
+
|
|
5
|
+
**The office can copy a waiting client's own link, from the panel where it invited them.**
|
|
6
|
+
(lane TAILS-5.)
|
|
7
|
+
|
|
8
|
+
* **`PortalsPanel` renders `CopyLink` beside a pending principal**, fed from that person's
|
|
9
|
+
`accept_path` (new in `@ai-matrx/records` 0.48.0). It is the SAME `CopyLink` this panel
|
|
10
|
+
already uses for the portal's own address — the one that shows the address to copy by hand
|
|
11
|
+
when a browser refuses the clipboard — not a second copy control.
|
|
12
|
+
* **A person with no link gets a sentence, not a missing button.** Somebody who is waiting and
|
|
13
|
+
has no `accept_path` was asked some other way (the server's magic-link route binds without an
|
|
14
|
+
invitation) or their link expired, and the panel says exactly that with what to do: invite
|
|
15
|
+
them again and a fresh one is made. Drawing nothing would read as a broken control; drawing a
|
|
16
|
+
dead button would be worse.
|
|
17
|
+
* **`PortalCardView` takes an optional `origin`** so the bare-mountable view can build a whole
|
|
18
|
+
URL, falling back to the browser's own origin. It still holds no client and no network, so
|
|
19
|
+
the blocking gate can keep mounting it against a recorded `custom.portal_card` answer.
|
|
20
|
+
|
|
21
|
+
**And the two buttons that read alike no longer do.** The Jobs toolbar's control that OPENS the
|
|
22
|
+
add-a-column panel read exactly `"Add field"`, and the panel's own SAVE button read exactly
|
|
23
|
+
`"Add field"` too — `FieldEditor` said `"Save field"` only when EDITING. The toolbar control is
|
|
24
|
+
a TOGGLE, so pressing it while the panel is open CLOSES the panel and throws away everything
|
|
25
|
+
typed into it: a person who had typed a column name, chosen a type and picked the table it
|
|
26
|
+
points at could lose all of it by pressing the words they were told to press, silently, with no
|
|
27
|
+
request ever sent. Measured on 2026-09-21 by a headless walk that lost the column twice and
|
|
28
|
+
spent a round blaming the store.
|
|
29
|
+
|
|
30
|
+
* The words now live once, in `fieldTypes.ts` beside `FIELD_TYPE_CHOICES` — the module that
|
|
31
|
+
already owns what a person READS about a column — as `ADD_FIELD_OPEN_LABEL` ("Add field"),
|
|
32
|
+
`ADD_FIELD_SAVE_LABEL` ("Create field") and `EDIT_FIELD_SAVE_LABEL` ("Save field"). Both
|
|
33
|
+
surfaces import them; neither writes the literal any more.
|
|
34
|
+
* **They are NOT in `@ai-matrx/design-system`, deliberately:** "field" and "column" are records
|
|
35
|
+
vocabulary and the design system knows nothing about either, so a labels module there would
|
|
36
|
+
be a domain word in a generic package that the next person adding a button would not find.
|
|
37
|
+
* `the-opener-and-the-committer-never-read-alike.test.tsx` is a forcing function, not a style
|
|
38
|
+
note: it asserts the strings differ, that the committer never says "add", and that neither
|
|
39
|
+
file carries the literal. Shown RED (2 of 3 failing, `expected 'Add field' not to be 'Add
|
|
40
|
+
field'`) with the collision planted, then GREEN.
|
|
41
|
+
|
|
42
|
+
**Consumer action:** none for a host that mounts `PortalsPanel`. A host that mounts
|
|
43
|
+
`PortalCardView` directly and serves the app from a known origin should pass `origin`; without
|
|
44
|
+
it the view reads `window.location.origin`, which is right in a browser and empty in SSR.
|
|
45
|
+
|
|
3
46
|
## 0.66.0
|
|
4
47
|
|
|
5
48
|
**A screen stops talking to a developer.** (lane TAILS-6.)
|
package/dist/index.cjs
CHANGED
|
@@ -868,6 +868,9 @@ function isRelationFieldType(id) {
|
|
|
868
868
|
function parityTypesWithNoExplanation() {
|
|
869
869
|
return import_records2.PARITY_FIELD_TYPES.map((row) => row.parity_type).filter((type) => !BY_ID.has(type));
|
|
870
870
|
}
|
|
871
|
+
var ADD_FIELD_OPEN_LABEL = "Add field";
|
|
872
|
+
var ADD_FIELD_SAVE_LABEL = "Create field";
|
|
873
|
+
var EDIT_FIELD_SAVE_LABEL = "Save field";
|
|
871
874
|
|
|
872
875
|
// src/parity.ts
|
|
873
876
|
var kernelId = (name) => import_records3.KERNEL_TABLES.find((t) => t.name === name)?.id ?? "";
|
|
@@ -5021,7 +5024,7 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
|
|
|
5021
5024
|
size: "sm",
|
|
5022
5025
|
disabled: mutation.saving || missing !== null,
|
|
5023
5026
|
onClick: () => void save(),
|
|
5024
|
-
children: mutation.saving ? "Saving\u2026" : field ?
|
|
5027
|
+
children: mutation.saving ? "Saving\u2026" : field ? EDIT_FIELD_SAVE_LABEL : ADD_FIELD_SAVE_LABEL
|
|
5025
5028
|
}
|
|
5026
5029
|
),
|
|
5027
5030
|
onCancel ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_design_system14.Button, { size: "sm", variant: "ghost", onClick: onCancel, children: "Cancel" }) : null,
|
|
@@ -9973,6 +9976,7 @@ function PortalsPanel({ tableId, className }) {
|
|
|
9973
9976
|
{
|
|
9974
9977
|
portalId: portal.portal_id,
|
|
9975
9978
|
...tableId ? { tableId } : {},
|
|
9979
|
+
origin,
|
|
9976
9980
|
onChanged: () => void load()
|
|
9977
9981
|
}
|
|
9978
9982
|
) : null
|
|
@@ -9980,7 +9984,7 @@ function PortalsPanel({ tableId, className }) {
|
|
|
9980
9984
|
}) })
|
|
9981
9985
|
] });
|
|
9982
9986
|
}
|
|
9983
|
-
function CopyLink({ url }) {
|
|
9987
|
+
function CopyLink({ url, what }) {
|
|
9984
9988
|
const [copied, setCopied] = (0, import_react68.useState)(false);
|
|
9985
9989
|
const [shown, setShown] = (0, import_react68.useState)(false);
|
|
9986
9990
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
@@ -10001,7 +10005,7 @@ function CopyLink({ url }) {
|
|
|
10001
10005
|
}
|
|
10002
10006
|
})();
|
|
10003
10007
|
},
|
|
10004
|
-
children: copied ? "Copied" : "Copy link"
|
|
10008
|
+
children: copied ? "Copied" : what ? `Copy ${what}` : "Copy link"
|
|
10005
10009
|
}
|
|
10006
10010
|
),
|
|
10007
10011
|
shown ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("p", { className: "mt-1.5 w-full break-all rounded border border-dashed border-border px-2 py-1 text-xs", children: [
|
|
@@ -10013,6 +10017,7 @@ function CopyLink({ url }) {
|
|
|
10013
10017
|
function PortalDetail({
|
|
10014
10018
|
portalId,
|
|
10015
10019
|
tableId,
|
|
10020
|
+
origin,
|
|
10016
10021
|
onChanged
|
|
10017
10022
|
}) {
|
|
10018
10023
|
const client = (0, import_react69.useRecordsClient)();
|
|
@@ -10062,6 +10067,7 @@ function PortalDetail({
|
|
|
10062
10067
|
PortalCardView,
|
|
10063
10068
|
{
|
|
10064
10069
|
card,
|
|
10070
|
+
origin,
|
|
10065
10071
|
...tableId ? { tableId } : {},
|
|
10066
10072
|
onRevoke: revoke,
|
|
10067
10073
|
onPreview: preview,
|
|
@@ -10084,6 +10090,7 @@ function PortalCardView({
|
|
|
10084
10090
|
onRevoke,
|
|
10085
10091
|
onPreview,
|
|
10086
10092
|
invite,
|
|
10093
|
+
origin,
|
|
10087
10094
|
className
|
|
10088
10095
|
}) {
|
|
10089
10096
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: (0, import_design_system33.cn)("mt-2.5 flex flex-col gap-2.5 border-t border-border pt-2.5", className), children: [
|
|
@@ -10114,7 +10121,7 @@ function PortalCardView({
|
|
|
10114
10121
|
] }, exposed.table_id))
|
|
10115
10122
|
] }),
|
|
10116
10123
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_design_system33.Separator, {}),
|
|
10117
|
-
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(People, { card, onRevoke, onPreview }),
|
|
10124
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(People, { card, onRevoke, onPreview, origin }),
|
|
10118
10125
|
invite ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
10119
10126
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_design_system33.Separator, {}),
|
|
10120
10127
|
invite
|
|
@@ -10124,8 +10131,10 @@ function PortalCardView({
|
|
|
10124
10131
|
function People({
|
|
10125
10132
|
card,
|
|
10126
10133
|
onRevoke,
|
|
10127
|
-
onPreview
|
|
10134
|
+
onPreview,
|
|
10135
|
+
origin
|
|
10128
10136
|
}) {
|
|
10137
|
+
const here = origin ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
10129
10138
|
const [askingToRevoke, setAskingToRevoke] = (0, import_react68.useState)(null);
|
|
10130
10139
|
const [busy, setBusy] = (0, import_react68.useState)(false);
|
|
10131
10140
|
const [said, setSaid] = (0, import_react68.useState)(null);
|
|
@@ -10167,6 +10176,7 @@ function People({
|
|
|
10167
10176
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "mt-0.5 break-all text-muted-foreground", children: person.email }),
|
|
10168
10177
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "mt-0.5 text-muted-foreground", children: state.why }),
|
|
10169
10178
|
person.is_active ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "mt-1.5 flex flex-wrap gap-1.5", children: [
|
|
10179
|
+
person.accept_path ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CopyLink, { url: `${here}${person.accept_path}`, what: "their link" }) : null,
|
|
10170
10180
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
10171
10181
|
import_design_system33.Button,
|
|
10172
10182
|
{
|
|
@@ -10178,6 +10188,11 @@ function People({
|
|
|
10178
10188
|
),
|
|
10179
10189
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_design_system33.Button, { size: "sm", variant: "ghost", onClick: () => setAskingToRevoke(person), children: "Remove their access" })
|
|
10180
10190
|
] }) : null,
|
|
10191
|
+
person.is_active && !person.signed_in && !person.accept_path ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("p", { className: "mt-1.5 text-muted-foreground", children: [
|
|
10192
|
+
"There is no link to copy for ",
|
|
10193
|
+
person.client || person.email,
|
|
10194
|
+
": they were asked some other way, or the link they were sent has expired. Invite them again and a fresh one is made."
|
|
10195
|
+
] }) : null,
|
|
10181
10196
|
asking ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "mt-1.5 flex flex-col gap-1.5 rounded border border-destructive/40 px-2 py-1.5", children: [
|
|
10182
10197
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-destructive", children: revokeConsequence(person, card.title) }),
|
|
10183
10198
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-wrap gap-1.5", children: [
|
|
@@ -14999,7 +15014,7 @@ function TablePage({
|
|
|
14999
15014
|
variant: surfaceChosen(surface, { rail: "field" }) ? "secondary" : "outline",
|
|
15000
15015
|
"aria-pressed": surfaceChosen(surface, { rail: "field" }),
|
|
15001
15016
|
onClick: () => press({ rail: "field" }),
|
|
15002
|
-
children:
|
|
15017
|
+
children: ADD_FIELD_OPEN_LABEL
|
|
15003
15018
|
}
|
|
15004
15019
|
),
|
|
15005
15020
|
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|