@burdenoff/fe-libs 2026.601.11 → 2026.601.13

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.
Files changed (35) hide show
  1. package/dist/shared/actors/index.js +1 -1
  2. package/dist/shared/config/shellRoutes.d.ts.map +1 -1
  3. package/dist/shared/config/shellRoutes.js +7 -0
  4. package/dist/shared/tours/toursContext/useTourMutations.d.ts.map +1 -1
  5. package/dist/shared/tours/toursContext/useTourMutations.js +1 -10
  6. package/dist/shared.js +77 -77
  7. package/dist/sharing/AcceptSharePage.d.ts +28 -0
  8. package/dist/sharing/AcceptSharePage.d.ts.map +1 -0
  9. package/dist/sharing/AcceptSharePage.js +270 -0
  10. package/dist/sharing/MemberPicker.d.ts +26 -0
  11. package/dist/sharing/MemberPicker.d.ts.map +1 -0
  12. package/dist/sharing/MemberPicker.js +110 -0
  13. package/dist/sharing/PermissionSelect.d.ts +19 -0
  14. package/dist/sharing/PermissionSelect.d.ts.map +1 -0
  15. package/dist/sharing/PermissionSelect.js +30 -0
  16. package/dist/sharing/ShareDialog.d.ts +26 -0
  17. package/dist/sharing/ShareDialog.d.ts.map +1 -0
  18. package/dist/sharing/ShareDialog.js +318 -0
  19. package/dist/sharing/SharedWithList.d.ts +18 -0
  20. package/dist/sharing/SharedWithList.d.ts.map +1 -0
  21. package/dist/sharing/SharedWithList.js +184 -0
  22. package/dist/sharing/index.d.ts +9 -0
  23. package/dist/sharing/index.d.ts.map +1 -0
  24. package/dist/sharing/shareUrl.d.ts +20 -0
  25. package/dist/sharing/shareUrl.d.ts.map +1 -0
  26. package/dist/sharing/shareUrl.js +39 -0
  27. package/dist/sharing/types.d.ts +94 -0
  28. package/dist/sharing/types.d.ts.map +1 -0
  29. package/dist/sharing/types.js +25 -0
  30. package/dist/sharing/useWorkspaceDirectory.d.ts +43 -0
  31. package/dist/sharing/useWorkspaceDirectory.d.ts.map +1 -0
  32. package/dist/sharing/useWorkspaceDirectory.js +117 -0
  33. package/dist/sharing.d.ts +2 -0
  34. package/dist/sharing.js +9 -0
  35. package/package.json +5 -1
@@ -0,0 +1,184 @@
1
+ import { cn as e } from "../lib/utils.js";
2
+ import { Button as t } from "../ui/button.js";
3
+ import { Badge as n } from "../ui/badge.js";
4
+ import { ActorIdentity as r } from "../shared/actors/ActorIdentity.js";
5
+ import { PermissionSelect as i } from "./PermissionSelect.js";
6
+ import { useActorProfiles as a } from "../shared/actors/useActorProfiles.js";
7
+ import { copyTextToClipboard as o, normalizeShareUrl as s } from "./shareUrl.js";
8
+ import { useState as c } from "react";
9
+ import { Check as l, Clock as u, Copy as d, Link2 as f, Trash2 as p } from "lucide-react";
10
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
11
+ //#region src/sharing/SharedWithList.tsx
12
+ function g(e) {
13
+ switch (e) {
14
+ case "ACCEPTED": return {
15
+ variant: "success",
16
+ label: "Accepted"
17
+ };
18
+ case "PENDING": return {
19
+ variant: "warning",
20
+ label: "Pending"
21
+ };
22
+ case "EXPIRED": return {
23
+ variant: "secondary",
24
+ label: "Expired"
25
+ };
26
+ case "REVOKED": return {
27
+ variant: "secondary",
28
+ label: "Revoked"
29
+ };
30
+ default: return {
31
+ variant: "info",
32
+ label: e
33
+ };
34
+ }
35
+ }
36
+ function _(e) {
37
+ let t = new Date(e);
38
+ if (Number.isNaN(t.getTime())) return "";
39
+ let n = t.getTime() - Date.now();
40
+ if (n <= 0) return "Expired";
41
+ let r = Math.floor(n / (1e3 * 60 * 60));
42
+ return r > 24 ? `${Math.floor(r / 24)}d left` : r > 0 ? `${r}h left` : `${Math.floor(n / (1e3 * 60))}m left`;
43
+ }
44
+ function v(e, t) {
45
+ if (e.granteeUserId) return t[e.granteeUserId] || {
46
+ actorId: e.granteeUserId,
47
+ actorType: "user",
48
+ displayName: e.granteeEmail ?? `User ${e.granteeUserId.slice(0, 8)}`,
49
+ secondaryLabel: e.granteeEmail ?? e.granteeUserId,
50
+ email: e.granteeEmail ?? void 0,
51
+ resolved: !!e.granteeEmail
52
+ };
53
+ let n = e.granteeEmail ?? "Pending invite";
54
+ return {
55
+ actorId: e.id,
56
+ actorType: "user",
57
+ displayName: n,
58
+ secondaryLabel: n,
59
+ email: e.granteeEmail ?? void 0,
60
+ resolved: !!e.granteeEmail
61
+ };
62
+ }
63
+ var y = ({ controller: y, workspaceId: b, authToken: x, apiGatewayUrl: S, className: C }) => {
64
+ let { grants: w, links: T, capabilities: E } = y, [D, O] = c(null), k = a(w.filter((e) => !!e.granteeUserId).map((e) => ({
65
+ actorId: e.granteeUserId,
66
+ actorType: "user"
67
+ })), {
68
+ workspaceId: b,
69
+ authToken: x,
70
+ apiGatewayUrl: S
71
+ }), A = async (e) => {
72
+ e.shareUrl && await o(s(e.shareUrl)).then(() => !0, () => !1) && (O(e.id), setTimeout(() => O((t) => t === e.id ? null : t), 2e3));
73
+ };
74
+ return w.length > 0 || T.length > 0 ? /* @__PURE__ */ h("div", {
75
+ className: e("space-y-3", C),
76
+ "data-testid": "shared-with-list",
77
+ children: [w.length > 0 ? /* @__PURE__ */ h("div", {
78
+ className: "space-y-2",
79
+ children: [/* @__PURE__ */ m("h4", {
80
+ className: "text-xs font-medium uppercase tracking-wider text-text-tertiary",
81
+ children: "People with access"
82
+ }), w.map((e) => {
83
+ let a = g(e.status);
84
+ return /* @__PURE__ */ h("div", {
85
+ "data-testid": "share-row",
86
+ className: "flex items-center justify-between gap-3 rounded-md border border-border-subtle bg-bg-surface px-3 py-2",
87
+ children: [/* @__PURE__ */ h("div", {
88
+ className: "flex min-w-0 flex-1 items-center gap-2",
89
+ children: [/* @__PURE__ */ m(r, {
90
+ profile: v(e, k),
91
+ variant: "inline",
92
+ hideIdDrilldown: !0
93
+ }), /* @__PURE__ */ m(n, {
94
+ variant: a.variant,
95
+ children: a.label
96
+ })]
97
+ }), /* @__PURE__ */ h("div", {
98
+ className: "flex items-center gap-2",
99
+ children: [y.updateRole ? /* @__PURE__ */ m(i, {
100
+ value: e.role,
101
+ roles: E.roles,
102
+ onChange: (t) => {
103
+ y.updateRole?.(e.id, t);
104
+ },
105
+ className: "h-8 w-36",
106
+ testId: `share-row-role-${e.id}`
107
+ }) : /* @__PURE__ */ m("span", {
108
+ className: "text-sm text-text-secondary",
109
+ children: e.role
110
+ }), y.revoke ? /* @__PURE__ */ m(t, {
111
+ type: "button",
112
+ variant: "ghost",
113
+ size: "icon-sm",
114
+ "data-testid": "share-row-revoke",
115
+ "aria-label": "Revoke access",
116
+ onClick: () => {
117
+ y.revoke?.(e.id);
118
+ },
119
+ className: "text-text-secondary hover:text-status-error-text",
120
+ children: /* @__PURE__ */ m(p, { className: "size-4" })
121
+ }) : null]
122
+ })]
123
+ }, e.id);
124
+ })]
125
+ }) : null, T.length > 0 ? /* @__PURE__ */ h("div", {
126
+ className: "space-y-2",
127
+ children: [/* @__PURE__ */ m("h4", {
128
+ className: "text-xs font-medium uppercase tracking-wider text-text-tertiary",
129
+ children: "Public links"
130
+ }), T.map((e) => /* @__PURE__ */ h("div", {
131
+ "data-testid": "share-row",
132
+ className: "flex items-center justify-between gap-3 rounded-md border border-border-subtle bg-bg-surface px-3 py-2",
133
+ children: [/* @__PURE__ */ h("div", {
134
+ className: "flex min-w-0 flex-1 items-center gap-2",
135
+ children: [
136
+ /* @__PURE__ */ m(f, { className: "size-4 shrink-0 text-action-primary-text" }),
137
+ /* @__PURE__ */ m("span", {
138
+ className: "truncate text-sm text-text-primary",
139
+ children: e.shareUrl ? s(e.shareUrl) : "Public link"
140
+ }),
141
+ e.expiresAt ? /* @__PURE__ */ h("span", {
142
+ className: "inline-flex items-center gap-1 whitespace-nowrap text-xs text-text-secondary",
143
+ children: [/* @__PURE__ */ m(u, { className: "size-3" }), _(e.expiresAt)]
144
+ }) : null
145
+ ]
146
+ }), /* @__PURE__ */ h("div", {
147
+ className: "flex items-center gap-1",
148
+ children: [e.shareUrl ? /* @__PURE__ */ m(t, {
149
+ type: "button",
150
+ variant: "ghost",
151
+ size: "icon-sm",
152
+ "data-testid": "share-copy-link",
153
+ "aria-label": "Copy link",
154
+ onClick: () => {
155
+ A(e);
156
+ },
157
+ className: "text-text-secondary hover:text-action-primary-text",
158
+ children: D === e.id ? /* @__PURE__ */ m(l, { className: "size-4 text-status-success-text" }) : /* @__PURE__ */ m(d, { className: "size-4" })
159
+ }) : null, y.revokeLink ? /* @__PURE__ */ m(t, {
160
+ type: "button",
161
+ variant: "ghost",
162
+ size: "icon-sm",
163
+ "data-testid": "share-row-revoke",
164
+ "aria-label": "Revoke link",
165
+ onClick: () => {
166
+ y.revokeLink?.(e.id);
167
+ },
168
+ className: "text-text-secondary hover:text-status-error-text",
169
+ children: /* @__PURE__ */ m(p, { className: "size-4" })
170
+ }) : null]
171
+ })]
172
+ }, e.id))]
173
+ }) : null]
174
+ }) : /* @__PURE__ */ m("div", {
175
+ className: e("rounded-md border border-border-subtle bg-bg-sunken px-3 py-4", C),
176
+ "data-testid": "shared-with-list",
177
+ children: /* @__PURE__ */ m("p", {
178
+ className: "text-center text-sm text-text-secondary",
179
+ children: "No one has access yet."
180
+ })
181
+ });
182
+ };
183
+ //#endregion
184
+ export { y as SharedWithList };
@@ -0,0 +1,9 @@
1
+ export { ROLE_DESCRIPTIONS, ROLE_LABELS, SHARE_ROLES, roleDescription, roleLabel, type ShareCapabilities, type ShareController, type ShareGrantView, type ShareResourceRef, type ShareRole, type ShareStatus, type ShareVisibility, } from './types';
2
+ export { ShareDialog, type ShareDialogProps } from './ShareDialog';
3
+ export { MemberPicker, type MemberPickerProps, type SelectedMember } from './MemberPicker';
4
+ export { PermissionSelect, type PermissionSelectProps } from './PermissionSelect';
5
+ export { SharedWithList, type SharedWithListProps } from './SharedWithList';
6
+ export { AcceptSharePage, type AcceptSharePageProps, type SharePreview } from './AcceptSharePage';
7
+ export { useWorkspaceDirectory, type UseWorkspaceDirectoryParams, type UseWorkspaceDirectoryResult, type WorkspaceDirectoryMember, } from './useWorkspaceDirectory';
8
+ export { copyTextToClipboard, escapeHtmlAttribute, normalizeShareUrl } from './shareUrl';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sharing/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,WAAW,EACX,eAAe,EACf,SAAS,EACT,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGlG,OAAO,EACL,qBAAqB,EACrB,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,GAC9B,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Share-URL helpers, promoted from the VibeControls MFE so every product can
3
+ * normalize/copy share links consistently. Generic: the allowlist covers
4
+ * product-specific share prefixes plus the generic `/share/accept/` route.
5
+ */
6
+ /**
7
+ * Rewrite a same-surface share URL onto the current window origin. Only paths
8
+ * in {@link SHARE_PATH_ALLOWLIST} are rewritten; everything else (and any
9
+ * non-http(s) protocol, or SSR with no `window`) is returned verbatim.
10
+ */
11
+ export declare function normalizeShareUrl(url: string): string;
12
+ /** Escape a string for safe interpolation into an HTML attribute value. */
13
+ export declare function escapeHtmlAttribute(value: string): string;
14
+ /**
15
+ * Copy text to the clipboard with a robust fallback. Prefers the async
16
+ * Clipboard API; falls back to a hidden `<textarea>` + `execCommand('copy')`
17
+ * for older browsers / insecure contexts. Rejects if the copy fails.
18
+ */
19
+ export declare function copyTextToClipboard(text: string): Promise<void>;
20
+ //# sourceMappingURL=shareUrl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shareUrl.d.ts","sourceRoot":"","sources":["../../src/sharing/shareUrl.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAcrD;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBrE"}
@@ -0,0 +1,39 @@
1
+ //#region src/sharing/shareUrl.ts
2
+ var e = [
3
+ "/share/accept/",
4
+ "/vibecontrols/",
5
+ "/files/share/"
6
+ ];
7
+ function t(t) {
8
+ if (typeof window > "u") return t;
9
+ try {
10
+ let n = new URL(t, window.location.origin);
11
+ if ((n.protocol === "http:" || n.protocol === "https:") && e.some((e) => n.pathname.startsWith(e))) return `${window.location.origin}${n.pathname}${n.search}${n.hash}`;
12
+ } catch {
13
+ return t;
14
+ }
15
+ return t;
16
+ }
17
+ function n(e) {
18
+ return e.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
19
+ }
20
+ async function r(e) {
21
+ try {
22
+ if (typeof navigator < "u" && navigator.clipboard) {
23
+ await navigator.clipboard.writeText(e);
24
+ return;
25
+ }
26
+ throw Error("Clipboard API unavailable");
27
+ } catch {
28
+ if (typeof document > "u") throw Error("Clipboard copy failed");
29
+ let t = document.createElement("textarea");
30
+ t.value = e, t.style.position = "fixed", t.style.opacity = "0", document.body.appendChild(t);
31
+ try {
32
+ if (t.select(), !document.execCommand("copy")) throw Error("Clipboard copy failed");
33
+ } finally {
34
+ document.body.removeChild(t);
35
+ }
36
+ }
37
+ }
38
+ //#endregion
39
+ export { r as copyTextToClipboard, n as escapeHtmlAttribute, t as normalizeShareUrl };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Generic sharing binding contract.
3
+ *
4
+ * These types are the *load-bearing* contract between the presentational
5
+ * sharing UI in `@burdenoff/fe-libs/sharing` and the consuming microfrontend.
6
+ * The fe-libs components are presentational + state-machine only — they never
7
+ * import GraphQL codegen hooks. Each MFE implements a {@link ShareController}
8
+ * (wiring its own queries/mutations) and injects it into the components.
9
+ */
10
+ /** Capability/permission level a grant confers on the grantee. */
11
+ export type ShareRole = 'VIEWER' | 'COMMENTER' | 'EDITOR' | 'MANAGER';
12
+ /** Visibility mode of the shared resource. */
13
+ export type ShareVisibility = 'PRIVATE' | 'SHARED_WITH_USERS' | 'SHARED_WITH_VIBE' | 'PUBLIC_LINK';
14
+ /** Lifecycle status of an individual grant. */
15
+ export type ShareStatus = 'PENDING' | 'ACCEPTED' | 'REVOKED' | 'EXPIRED';
16
+ /**
17
+ * Describes the resource being shared. `type` is an opaque, product-defined
18
+ * string (e.g. `"file"`, `"vibe"`, `"session"`); the UI only uses it for
19
+ * display + copy. `vibeId` gates the "Everyone with Vibe" mode.
20
+ */
21
+ export interface ShareResourceRef {
22
+ type: string;
23
+ id: string;
24
+ name: string;
25
+ vibeId?: string | null;
26
+ ownerId?: string | null;
27
+ visibility?: ShareVisibility;
28
+ }
29
+ /**
30
+ * A single share grant (a user invite or a public link), as projected for
31
+ * the UI. The controller maps its own backend shape onto this view.
32
+ */
33
+ export interface ShareGrantView {
34
+ id: string;
35
+ granteeUserId?: string | null;
36
+ granteeEmail?: string | null;
37
+ role: ShareRole;
38
+ status: ShareStatus;
39
+ visibility: ShareVisibility;
40
+ expiresAt?: string | null;
41
+ token?: string | null;
42
+ shareUrl?: string | null;
43
+ createdAt?: string;
44
+ }
45
+ /** Roles + descriptions surfaced in selectors. */
46
+ export interface ShareCapabilities {
47
+ supportsUsers: boolean;
48
+ supportsLink: boolean;
49
+ supportsVibeShare: boolean;
50
+ supportsExpiry: boolean;
51
+ roles: ShareRole[];
52
+ }
53
+ /**
54
+ * The controller contract. The MFE implements this (typically by wrapping its
55
+ * generated GraphQL hooks) and passes it to {@link ShareDialog} /
56
+ * {@link SharedWithList}. Only `loading`, `grants`, `links`, `refetch`,
57
+ * `createUserShare`, and `capabilities` are required; the rest are optional
58
+ * and the UI feature-detects them (hiding/disabling controls when absent).
59
+ */
60
+ export interface ShareController {
61
+ loading: boolean;
62
+ grants: ShareGrantView[];
63
+ links: ShareGrantView[];
64
+ refetch: () => void;
65
+ createUserShare(input: {
66
+ email: string;
67
+ role: ShareRole;
68
+ message?: string;
69
+ expiresAt?: string;
70
+ }): Promise<void>;
71
+ createLinkShare?(input: {
72
+ role: ShareRole;
73
+ expiresAt?: string | null;
74
+ }): Promise<{
75
+ shareUrl: string;
76
+ }>;
77
+ updateRole?(grantId: string, role: ShareRole): Promise<void>;
78
+ revoke?(grantId: string): Promise<void>;
79
+ revokeLink?(grantId: string): Promise<void>;
80
+ setVisibility?(visibility: ShareVisibility): Promise<void>;
81
+ currentVisibility?: ShareVisibility;
82
+ capabilities: ShareCapabilities;
83
+ }
84
+ /** Canonical, ordered list of roles (low → high privilege). */
85
+ export declare const SHARE_ROLES: readonly ShareRole[];
86
+ /** Human-friendly label per role. */
87
+ export declare const ROLE_LABELS: Record<ShareRole, string>;
88
+ /** One-line description of what each role can do. */
89
+ export declare const ROLE_DESCRIPTIONS: Record<ShareRole, string>;
90
+ /** Resolve a display label for a role, falling back to the raw value. */
91
+ export declare function roleLabel(role: ShareRole): string;
92
+ /** Resolve a description for a role, falling back to an empty string. */
93
+ export declare function roleDescription(role: ShareRole): string;
94
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sharing/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ //#region src/sharing/types.ts
2
+ var e = [
3
+ "VIEWER",
4
+ "COMMENTER",
5
+ "EDITOR",
6
+ "MANAGER"
7
+ ], t = {
8
+ VIEWER: "Viewer",
9
+ COMMENTER: "Commenter",
10
+ EDITOR: "Editor",
11
+ MANAGER: "Manager"
12
+ }, n = {
13
+ VIEWER: "Can view",
14
+ COMMENTER: "Can view and comment",
15
+ EDITOR: "Can view, comment, and edit",
16
+ MANAGER: "Full access, including sharing and settings"
17
+ };
18
+ function r(e) {
19
+ return t[e] ?? e;
20
+ }
21
+ function i(e) {
22
+ return n[e] ?? "";
23
+ }
24
+ //#endregion
25
+ export { n as ROLE_DESCRIPTIONS, t as ROLE_LABELS, e as SHARE_ROLES, i as roleDescription, r as roleLabel };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * A workspace member entry, resolved to a human-friendly identity for the
3
+ * sharing autocomplete. `email` may be empty when the directory only exposes a
4
+ * userId (the picker still allows free-typing an email to invite).
5
+ */
6
+ export interface WorkspaceDirectoryMember {
7
+ id: string;
8
+ name: string;
9
+ email: string;
10
+ avatar?: string;
11
+ }
12
+ export interface UseWorkspaceDirectoryParams {
13
+ /** Workspace whose members are listed. */
14
+ workspaceId: string;
15
+ /** Override the active profile's access token. */
16
+ authToken?: string;
17
+ /**
18
+ * Override the WORKSPACE gateway base URL. Resolved like `useActorProfiles`:
19
+ * an explicit workspace gateway is honored, otherwise the URL is derived from
20
+ * the current hostname (LOCAL/ALPHA/PROD).
21
+ */
22
+ apiGatewayUrl?: string;
23
+ }
24
+ export interface UseWorkspaceDirectoryResult {
25
+ members: WorkspaceDirectoryMember[];
26
+ /** Update the case-insensitive client-side filter query. */
27
+ search: (q: string) => void;
28
+ loading: boolean;
29
+ /** Whether the backend reported more members beyond the loaded page. */
30
+ hasMore: boolean;
31
+ /** Load the next page of members (no-op while loading or when no more). */
32
+ fetchMore: () => void;
33
+ }
34
+ /**
35
+ * Autocomplete data source for the sharing MemberPicker. Mirrors the resolution
36
+ * strategy of `useActorProfiles`: it lists `workspaceMembers` (ids only) and
37
+ * resolves human identities via `workspaceMemberIdentities`, against the
38
+ * WORKSPACE gateway with a workspace token attached. Identity-resolution
39
+ * failures are best-effort and fall back to a short-id label rather than
40
+ * surfacing an error toast — the picker still works for free-typed emails.
41
+ */
42
+ export declare function useWorkspaceDirectory(params: UseWorkspaceDirectoryParams): UseWorkspaceDirectoryResult;
43
+ //# sourceMappingURL=useWorkspaceDirectory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useWorkspaceDirectory.d.ts","sourceRoot":"","sources":["../../src/sharing/useWorkspaceDirectory.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,4DAA4D;IAC5D,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAsDD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,2BAA2B,GAClC,2BAA2B,CA4J7B"}
@@ -0,0 +1,117 @@
1
+ import { resolveGatewayUrls as e } from "../shared/config/gatewayUrls.js";
2
+ import { getStoredAuthToken as t, graphqlFetch as n } from "../shared/graphql/fetch.js";
3
+ import { useCallback as r, useEffect as i, useMemo as a, useRef as o, useState as s } from "react";
4
+ //#region src/sharing/useWorkspaceDirectory.ts
5
+ var c = 50, l = "\n query SharingWorkspaceMembers($workspaceId: ID!, $pagination: PaginationInput) {\n workspaceMembers(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n userId\n }\n total\n hasMore\n }\n }\n", u = "\n query SharingWorkspaceMemberIdentities($userIds: [ID!]!, $workspaceId: ID) {\n workspaceMemberIdentities(userIds: $userIds, workspaceId: $workspaceId) {\n userId\n displayName\n email\n avatar\n }\n }\n";
6
+ function d(e) {
7
+ return e.length <= 12 ? e : `${e.slice(0, 8)}…${e.slice(-4)}`;
8
+ }
9
+ function f(e) {
10
+ return /(?:gw-wspace|graphqlworkspaces|workspaces\.)/.test(e);
11
+ }
12
+ function p(p) {
13
+ let { workspaceId: m, authToken: h, apiGatewayUrl: g } = p, _ = h ?? t() ?? void 0, v = a(() => {
14
+ try {
15
+ let t = e();
16
+ return g && f(g) ? g : t.workspaceBaseUrl;
17
+ } catch {
18
+ return;
19
+ }
20
+ }, [g]), [y, b] = s([]), [x, S] = s(!1), [C, w] = s(!1), [T, E] = s(""), [D, O] = s(0), k = `${m}|${v ?? ""}`, A = o(k);
21
+ A.current !== k && (A.current = k), i(() => {
22
+ b([]), w(!1), O(0);
23
+ }, [k]), i(() => {
24
+ if (!m || !v) return;
25
+ let e = !1;
26
+ async function t() {
27
+ S(!0);
28
+ try {
29
+ let t = await n({
30
+ gateway: "workspace",
31
+ baseUrl: v,
32
+ authToken: _,
33
+ workspaceId: m,
34
+ workspaceToken: !0,
35
+ query: l,
36
+ operationName: "SharingWorkspaceMembers",
37
+ suppressGlobalErrorEvent: !0,
38
+ variables: {
39
+ workspaceId: m,
40
+ pagination: {
41
+ limit: c,
42
+ offset: D * c
43
+ }
44
+ }
45
+ });
46
+ if (e) return;
47
+ let r = t.data?.workspaceMembers, i = r?.items ?? [], a = [...new Set(i.map((e) => e.userId))], o = /* @__PURE__ */ new Map();
48
+ if (a.length > 0) try {
49
+ let t = await n({
50
+ gateway: "workspace",
51
+ baseUrl: v,
52
+ authToken: _,
53
+ workspaceId: m,
54
+ workspaceToken: !0,
55
+ query: u,
56
+ operationName: "SharingWorkspaceMemberIdentities",
57
+ suppressGlobalErrorEvent: !0,
58
+ variables: {
59
+ userIds: a,
60
+ workspaceId: m
61
+ }
62
+ });
63
+ if (e) return;
64
+ for (let e of t.data?.workspaceMemberIdentities ?? []) o.set(e.userId, {
65
+ displayName: e.displayName,
66
+ email: e.email,
67
+ avatar: e.avatar
68
+ });
69
+ } catch {}
70
+ let s = a.map((e) => {
71
+ let t = o.get(e);
72
+ return {
73
+ id: e,
74
+ name: t?.displayName?.trim() || t?.email?.trim() || d(e),
75
+ email: t?.email?.trim() ?? "",
76
+ avatar: t?.avatar ?? void 0
77
+ };
78
+ });
79
+ if (e) return;
80
+ b((e) => {
81
+ if (D === 0) return s;
82
+ let t = new Set(e.map((e) => e.id));
83
+ return [...e, ...s.filter((e) => !t.has(e.id))];
84
+ }), w(!!r?.hasMore);
85
+ } catch {
86
+ e || w(!1);
87
+ } finally {
88
+ e || S(!1);
89
+ }
90
+ }
91
+ return t(), () => {
92
+ e = !0;
93
+ };
94
+ }, [
95
+ m,
96
+ v,
97
+ _,
98
+ D
99
+ ]);
100
+ let j = r((e) => {
101
+ E(e);
102
+ }, []), M = r(() => {
103
+ S((e) => (!e && C && O((e) => e + 1), e));
104
+ }, [C]);
105
+ return {
106
+ members: a(() => {
107
+ let e = T.trim().toLowerCase();
108
+ return e ? y.filter((t) => t.name.toLowerCase().includes(e) || t.email.toLowerCase().includes(e)) : y;
109
+ }, [y, T]),
110
+ search: j,
111
+ loading: x,
112
+ hasMore: C,
113
+ fetchMore: M
114
+ };
115
+ }
116
+ //#endregion
117
+ export { p as useWorkspaceDirectory };
@@ -0,0 +1,2 @@
1
+ export * from './sharing/index'
2
+ export {}
@@ -0,0 +1,9 @@
1
+ import { ROLE_DESCRIPTIONS as e, ROLE_LABELS as t, SHARE_ROLES as n, roleDescription as r, roleLabel as i } from "./sharing/types.js";
2
+ import { useWorkspaceDirectory as a } from "./sharing/useWorkspaceDirectory.js";
3
+ import { MemberPicker as o } from "./sharing/MemberPicker.js";
4
+ import { PermissionSelect as s } from "./sharing/PermissionSelect.js";
5
+ import { copyTextToClipboard as c, escapeHtmlAttribute as l, normalizeShareUrl as u } from "./sharing/shareUrl.js";
6
+ import { SharedWithList as d } from "./sharing/SharedWithList.js";
7
+ import { ShareDialog as f } from "./sharing/ShareDialog.js";
8
+ import { AcceptSharePage as p } from "./sharing/AcceptSharePage.js";
9
+ export { p as AcceptSharePage, o as MemberPicker, s as PermissionSelect, e as ROLE_DESCRIPTIONS, t as ROLE_LABELS, n as SHARE_ROLES, f as ShareDialog, d as SharedWithList, c as copyTextToClipboard, l as escapeHtmlAttribute, u as normalizeShareUrl, r as roleDescription, i as roleLabel, a as useWorkspaceDirectory };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/fe-libs",
3
- "version": "2026.601.11",
3
+ "version": "2026.601.13",
4
4
  "description": "Burdenoff frontend primitives and domain libraries",
5
5
  "type": "module",
6
6
  "imports": {
@@ -22,6 +22,10 @@
22
22
  "types": "./dist/ui.d.ts",
23
23
  "import": "./dist/ui.js"
24
24
  },
25
+ "./sharing": {
26
+ "types": "./dist/sharing.d.ts",
27
+ "import": "./dist/sharing.js"
28
+ },
25
29
  "./chrome": {
26
30
  "types": "./dist/chrome.d.ts",
27
31
  "import": "./dist/chrome.js"