@ai-matrx/sharing 0.1.9 → 0.1.11

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 CHANGED
@@ -1,9 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.11
4
+
5
+ - **One manual-copy dialog: `useShare` asks the host (2026-10-22, summon adoption X11).** A refused clipboard no longer opens a `ClipboardFallbackDialog` that `useShare` mounted itself; it calls the share sheet's new `manualCopy` port (`configureShareSheetUi({ manualCopy })`, also exported as `manualCopy` from the sheet host), whose default is kit `openClipboardFallback`, i.e. the host's ONE manual-copy dialog (the web app's summon overlay through its kit bridge, `<ClipboardFallbackHost />` anywhere else). `copy`/`share` still resolve `"manual"` and never flip `copied`. `UseShareResult.fallbackDialog` is now always `null` and `@deprecated`; `ShareLinkPanel` no longer renders it. Sharing's bundle no longer imports `ClipboardFallbackDialog`. Test: `src/react/__tests__/manual-copy-goes-through-the-host.test.tsx` (port, kit default, no-own-dialog scan; red on the old `useShare`). **Consumer action:** remove `{fallbackDialog}` from JSX when convenient (it renders nothing now; removal of the field comes later). A host must have a manual-copy host registered (the web app's `ClipboardFallbackBridge`, or `<ClipboardFallbackHost />`), or pass `manualCopy`. matrx-frontend: `features/sharing/hooks/__tests__/useShare.test.tsx` and `scripts/__tests__/clipboard-success-gating.behavior.test.ts` ("useShare copy fallback") assert the old hand-mounted dialog and must be updated (builder-X11.md).
6
+
7
+ ## 0.1.10
8
+
9
+ - **The access screens' stand-ins announce themselves (2026-10-22, round 3).** Every default of `configureAccessUi` the host did not replace (`Link`, `router`, `searchParam`, `confirm`, `Textarea`, `renderUserIdentity`, `renderEntityRef`, `renderErrorMenu`, and the quiet app features `entityRegistry`, `recordUnavailableCapture`, `settingRequestActions`, `resourceActionRequests`, `presentOrganizationRefusal`, `overlays`' close) now says so: one console notice per field naming `configureAccessUi({ <field> })`, the field in `data-access-ui-ports` on `<html>`, and `data-access-ui-host="unconfigured"` + `data-access-ui-port` on what the default draws (the same pattern as the share sheet). New test seam `resetAccessUiStandInNotices`. Guard: `src/access/react/env.stand-ins.test.tsx`. **Consumer action:** none; a host that sees a notice supplies the named field.
10
+
3
11
  ## 0.1.9
4
12
 
5
- UNVERIFIED: this release has no Unreleased entry and the source comparison could not be made (the caller supplied no source comparison). Whether source changed is unknown; consumer action is unknown.
6
- Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/sharing/src`.
13
+ SOURCE CHANGED WITH NO RELEASE NOTE. This release shipped source changes that nobody described in an Unreleased entry,
14
+ so they may include breaking API changes. Consumer action is UNKNOWN: read the diff before upgrading.
15
+ Diff: `git diff ed3f4f552d 973cc3e9a3 -- apps/shared/sharing/src`
16
+
17
+ Changed shipped source files (2):
18
+ - `apps/shared/sharing/src/react/sheet/ShareModal.tsx`
19
+ - `apps/shared/sharing/src/react/sheet/useKeepFocusInDialog.ts`
7
20
 
8
21
  ## 0.1.8
9
22
 
@@ -62,6 +62,7 @@ __export(react_exports, {
62
62
  offerOwnerChoices: () => offerOwnerChoices,
63
63
  refreshHiddenByMe: () => refreshHiddenByMe,
64
64
  requestCantRemove: () => requestCantRemove,
65
+ resetAccessUiStandInNotices: () => resetAccessUiStandInNotices,
65
66
  useAccessGate: () => useAccessGate,
66
67
  useAccessStates: () => useAccessStates,
67
68
  useAccessUi: () => useAccessUi,
@@ -84,9 +85,33 @@ var DEFAULT_NAV_ITEM_CLASSES = {
84
85
  selected: "bg-[var(--shell-nav-selected-bg)] text-[var(--shell-nav-selected-text)] shadow-[var(--shell-nav-selected-shadow)] font-semibold",
85
86
  unselected: "text-[var(--shell-nav-unselected-text)] hover:text-[var(--shell-nav-text-hover)] hover:bg-[var(--matrx-glass-bg-hover)]"
86
87
  };
88
+ var announcedPorts = /* @__PURE__ */ new Set();
89
+ function announceDefault(port, consequence) {
90
+ if (announcedPorts.has(port)) return;
91
+ announcedPorts.add(port);
92
+ if (typeof console !== "undefined") {
93
+ console.warn(
94
+ `[@ai-matrx/sharing/access/react] The access screens are using their built-in stand-in for "${port}": ${consequence}. Pass it with configureAccessUi({ ${port} }) once at app boot.`
95
+ );
96
+ }
97
+ if (typeof document !== "undefined") {
98
+ const root = document.documentElement;
99
+ const ports = new Set((root.getAttribute("data-access-ui-ports") ?? "").split(",").filter(Boolean));
100
+ ports.add(port);
101
+ root.setAttribute("data-access-ui-ports", [...ports].join(","));
102
+ }
103
+ }
104
+ function resetAccessUiStandInNotices() {
105
+ announcedPorts.clear();
106
+ if (typeof document !== "undefined") document.documentElement.removeAttribute("data-access-ui-ports");
107
+ }
108
+ function standInAttrs(port) {
109
+ return { "data-access-ui-host": "unconfigured", "data-access-ui-port": port };
110
+ }
87
111
  var DefaultLink = (0, import_react.forwardRef)(
88
112
  function DefaultLink2(props, ref) {
89
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { ref, ...props });
113
+ (0, import_react.useEffect)(() => announceDefault("Link", "links are plain anchors (a full page load, no client navigation)"), []);
114
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { ref, ...standInAttrs("Link"), ...props });
90
115
  }
91
116
  );
92
117
  var AccessLink = (0, import_react.forwardRef)(
@@ -95,28 +120,35 @@ var AccessLink = (0, import_react.forwardRef)(
95
120
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, { ref, ...props });
96
121
  }
97
122
  );
123
+ var ROUTER_CONSEQUENCE = "navigation uses the browser (full page loads; refresh reloads the tab)";
98
124
  var DEFAULT_ROUTER = {
99
125
  push: (href) => {
126
+ announceDefault("router", ROUTER_CONSEQUENCE);
100
127
  if (typeof window !== "undefined") window.location.assign(href);
101
128
  },
102
129
  back: () => {
130
+ announceDefault("router", ROUTER_CONSEQUENCE);
103
131
  if (typeof window !== "undefined") window.history.back();
104
132
  },
105
133
  refresh: () => {
134
+ announceDefault("router", ROUTER_CONSEQUENCE);
106
135
  if (typeof window !== "undefined") window.location.reload();
107
136
  }
108
137
  };
109
138
  function defaultSearchParam(name) {
139
+ announceDefault("searchParam", "query parameters are read from window.location");
110
140
  if (typeof window === "undefined") return null;
111
141
  return new URLSearchParams(window.location.search).get(name);
112
142
  }
113
143
  function DefaultTextarea(props) {
114
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_design_system.Textarea, { ...props });
144
+ (0, import_react.useEffect)(() => announceDefault("Textarea", "the message box is the plain design-system textarea (no voice, no agent actions)"), []);
145
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_design_system.Textarea, { ...standInAttrs("Textarea"), ...props });
115
146
  }
116
147
  function DefaultUserIdentity({ user, size = "md", avatarOnly }) {
148
+ (0, import_react.useEffect)(() => announceDefault("renderUserIdentity", "people are drawn as an avatar and a name (no profile door)"), []);
117
149
  const name = user.displayName ?? "Someone";
118
150
  const box = size === "sm" ? "h-7 w-7" : "h-9 w-9";
119
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "inline-flex min-w-0 items-center gap-2", children: [
151
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { ...standInAttrs("renderUserIdentity"), className: "inline-flex min-w-0 items-center gap-2", children: [
120
152
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_design_system.Avatar, { className: box, children: [
121
153
  user.avatarUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_design_system.AvatarImage, { src: user.avatarUrl, alt: name }) : null,
122
154
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_design_system.AvatarFallback, { className: "text-[11px]", children: (0, import_format.getInitials)(user.displayName) })
@@ -125,7 +157,16 @@ function DefaultUserIdentity({ user, size = "md", avatarOnly }) {
125
157
  ] });
126
158
  }
127
159
  function DefaultEntityRef({ name, className }) {
128
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className, children: name });
160
+ (0, import_react.useEffect)(() => announceDefault("renderEntityRef", "records are drawn as a plain name (not a door to the record)"), []);
161
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...standInAttrs("renderEntityRef"), className, children: name });
162
+ }
163
+ function DefaultErrorMenu() {
164
+ (0, import_react.useEffect)(() => announceDefault("renderErrorMenu", "errors show no copy / fix-request menu"), []);
165
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { hidden: true, ...standInAttrs("renderErrorMenu") });
166
+ }
167
+ function defaultConfirm(options) {
168
+ announceDefault("confirm", "confirmations use the kit confirm (the design-system ConfirmDialogHost must be mounted)");
169
+ return (0, import_confirm_opener.confirm)(options);
129
170
  }
130
171
  var moduleEnv = {};
131
172
  var moduleVersion = 0;
@@ -162,21 +203,33 @@ function resolveAccessUi(base, override) {
162
203
  Link: env.Link ?? DefaultLink,
163
204
  router: env.router ?? DEFAULT_ROUTER,
164
205
  searchParam: env.searchParam ?? defaultSearchParam,
165
- confirm: env.confirm ?? import_confirm_opener.confirm,
206
+ confirm: env.confirm ?? defaultConfirm,
166
207
  Textarea: env.Textarea ?? DefaultTextarea,
167
208
  renderUserIdentity: env.renderUserIdentity ?? ((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DefaultUserIdentity, { ...p })),
168
209
  renderEntityRef: env.renderEntityRef ?? ((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DefaultEntityRef, { ...p })),
169
- renderErrorMenu: env.renderErrorMenu ?? (() => null),
210
+ renderErrorMenu: env.renderErrorMenu ?? (() => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DefaultErrorMenu, {})),
170
211
  navItemClasses: env.navItemClasses ?? DEFAULT_NAV_ITEM_CLASSES,
171
212
  manualRequestActionKey: env.manualRequestActionKey ?? REQUEST_ACCESS_MANUAL_ACTION,
172
- tryGetEntityInfo: (token) => env.entityRegistry?.tryGetEntityInfo(token) ?? null,
213
+ tryGetEntityInfo: (token) => {
214
+ if (!env.entityRegistry) announceDefault("entityRegistry", "owners and records render as plain names (no labels or links)");
215
+ return env.entityRegistry?.tryGetEntityInfo(token) ?? null;
216
+ },
173
217
  restoreFromTrash: async (token, id) => {
174
218
  if (!env.trash) throw notConfigured("trash");
175
219
  await env.trash.restore(token, id);
176
220
  },
177
- resolveRecordUnavailableCapture: (error, resolution) => env.recordUnavailableCapture?.resolve(error, resolution),
178
- getSettingRequestAction: (key) => env.settingRequestActions?.get(key),
179
- getResourceActionRequestAction: (key) => env.resourceActionRequests?.get(key),
221
+ resolveRecordUnavailableCapture: (error, resolution) => {
222
+ if (!env.recordUnavailableCapture) announceDefault("recordUnavailableCapture", "a captured unavailable read is not reconciled");
223
+ env.recordUnavailableCapture?.resolve(error, resolution);
224
+ },
225
+ getSettingRequestAction: (key) => {
226
+ if (!env.settingRequestActions) announceDefault("settingRequestActions", "setting-change requests show without their primary action button");
227
+ return env.settingRequestActions?.get(key);
228
+ },
229
+ getResourceActionRequestAction: (key) => {
230
+ if (!env.resourceActionRequests) announceDefault("resourceActionRequests", "resource-action requests show without their primary action button");
231
+ return env.resourceActionRequests?.get(key);
232
+ },
180
233
  createCmsAccessRequest: async (args) => {
181
234
  if (!env.cms) throw notConfigured("cms");
182
235
  return env.cms.createAccessRequest(args);
@@ -189,12 +242,18 @@ function resolveAccessUi(base, override) {
189
242
  if (!env.reportFeedback) throw notConfigured("reportFeedback");
190
243
  return env.reportFeedback(input);
191
244
  },
192
- presentOrganizationRefusal: (error, options) => env.presentOrganizationRefusal?.(error, options) ?? false,
245
+ presentOrganizationRefusal: (error, options) => {
246
+ if (!env.presentOrganizationRefusal) announceDefault("presentOrganizationRefusal", "a refusal for a missing organization is shown as a plain error");
247
+ return env.presentOrganizationRefusal?.(error, options) ?? false;
248
+ },
193
249
  openOverlay: (overlayId, instanceId, data) => {
194
250
  if (!env.overlays) throw notConfigured("overlays");
195
251
  env.overlays.open(overlayId, instanceId, data);
196
252
  },
197
- closeOverlay: (overlayId, instanceId) => env.overlays?.close(overlayId, instanceId)
253
+ closeOverlay: (overlayId, instanceId) => {
254
+ if (!env.overlays) announceDefault("overlays", "no overlay can be closed (none was ever opened)");
255
+ env.overlays?.close(overlayId, instanceId);
256
+ }
198
257
  };
199
258
  return ui;
200
259
  }