@basictech/react 0.12.0-beta.2 → 0.12.0-beta.4

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,5 +1,40 @@
1
1
  # @basictech/react
2
2
 
3
+ ## 0.12.0-beta.4
4
+
5
+ ### Minor Changes
6
+
7
+ - 0cd2283: Add useShareInvites for app-authorized invitation listing, inspection, acceptance, decline, and deletion with account-switch isolation and decision refresh. Add a drop-in ShareInvites inbox and ShareInvitesModal, integrate the inbox into account settings and SharesModal, and expose display-only ShareRecipientAvatar/ShareRecipientAvatars using the existing attributed local Marble renderer. Re-export the new APIs through the Next.js client entry point.
8
+
9
+ Use compact invite rows with right-aligned decisions and expandable details. Resolve public sender handles with DID/handle round-trip verification through client.shares.resolveContactHandle; omit routine pending labels, app IDs, and redundant subtitles.
10
+
11
+ Add useShareRecipients(dids) and its bound factory hook for verified public handles, deduplicated lookups, partial-error recovery, refresh, and stale-result isolation. Add ShareRecipientLabel for a compact avatar plus handle, and handle titles on avatar-only recipients. Reuse the existing local Marble renderer and upstream MIT attribution; no new dependencies or public photo lookup.
12
+
13
+ Reject saved or in-flight invite decisions when the hook switches SDK clients, even if both clients use the same local account ID. Do not refresh the replacement client's inbox for a stale decision.
14
+
15
+ ### Patch Changes
16
+
17
+ - b5969b8: Label unnamed anonymous profiles as `Local User` and render locally adapted Marble avatars for
18
+ missing or failed profile images, seeded by DID or stable local account ID. Include upstream
19
+ attribution in the published package without adding a runtime dependency.
20
+ - Updated dependencies [bbfb849]
21
+ - Updated dependencies [0cd2283]
22
+ - Updated dependencies [b5969b8]
23
+ - @basictech/core@0.12.0-beta.4
24
+
25
+ ## 0.12.0-beta.3
26
+
27
+ ### Minor Changes
28
+
29
+ - fc5772a: Add app-owned menu actions and links with optional icons to UserAvatar, UserButton, and UserMenu.
30
+ Add a base background color to BasicUIProvider, derive borders and hover backgrounds from it,
31
+ and choose black or white text for hex accent colors. Preserve appearance in menu and modal
32
+ portals. Document component selection, simple theming, menu extensions, and Base UI dependencies.
33
+
34
+ ### Patch Changes
35
+
36
+ - @basictech/core@0.12.0-beta.3
37
+
3
38
  ## 0.12.0-beta.2
4
39
 
5
40
  ### Minor Changes
package/README.md CHANGED
@@ -18,14 +18,19 @@ its package metadata.
18
18
 
19
19
  ## Install
20
20
 
21
- This README describes the `0.12.0-beta.1` release candidate. After publication, install
21
+ This README describes `0.12.0-beta.3`. After publication, install
22
22
  matching versions of the React SDK and the schema package you import directly:
23
23
 
24
24
  ```bash
25
- npm install @basictech/react@0.12.0-beta.1 @basictech/schema@0.12.0-beta.1
25
+ npm install @basictech/react@0.12.0-beta.3 @basictech/schema@0.12.0-beta.3
26
26
  ```
27
27
 
28
- React is a peer dependency and must already be installed by your application.
28
+ React and React DOM (18+) are peer dependencies and must already be installed by your application.
29
+ Use Node.js 24+ for development, CI, and builds to match the supported engine range.
30
+ `@base-ui/react` (`^1.8.0`) is installed automatically as a dependency, not a peer.
31
+ We import its `avatar`, `dialog`, `menu`, and `tabs` subpaths and leave them external in the SDK
32
+ build. Your app's bundler resolves those imports; we do not bundle the entire Base UI library
33
+ inside the SDK. Installing the dependency does install the Base UI package on disk.
29
34
 
30
35
  ## Auth update guide
31
36
 
@@ -33,9 +38,9 @@ Upgrade the packages you use together. npm's `latest` channel remains on `0.11.0
33
38
  does not contain all of the API changes described here:
34
39
 
35
40
  ```bash
36
- npm install @basictech/react@0.12.0-beta.1 @basictech/schema@0.12.0-beta.1
41
+ npm install @basictech/react@0.12.0-beta.3 @basictech/schema@0.12.0-beta.3
37
42
  # If you import core directly, update that dependency too:
38
- npm install @basictech/core@0.12.0-beta.1
43
+ npm install @basictech/core@0.12.0-beta.3
39
44
  ```
40
45
 
41
46
  Keep the public packages on matching versions. No browser storage reset or persisted-profile
@@ -334,6 +339,10 @@ Drop-in account and sharing UI uses Base UI's accessible menu, avatar, and dialo
334
339
  Components use the nearest `BasicProvider` (including `basic.Provider`); they do not create a
335
340
  second auth client. Import the optional stylesheet once. Omit it to supply your own styles.
336
341
 
342
+ **Which one should I use?** `UserButton` gives you the avatar, name, handle, and menu.
343
+ Choose `UserAvatar` for the same menu with an avatar-only trigger. `UserMenu` is the shared
344
+ component underneath both; you normally do not need to render it separately.
345
+
337
346
  ```tsx
338
347
  import {
339
348
  BasicUIProvider, SignInButton, UserButton, AuthStatus, SyncStatus, SharesModal,
@@ -360,22 +369,83 @@ export function AccountTools() {
360
369
  | --- | --- |
361
370
  | `BasicButton` | Native button props/ref; `variant="solid\|outline\|ghost"`. Defaults to `type="button"`. |
362
371
  | `SignInButton`, `SignOutButton` | Native button props and variant, pending/error feedback. Sign-in accepts `input` (handle/DID); `onClick.preventDefault()` cancels the action. Expired sessions can reauthorize or sign out. |
363
- | `UserAvatar` | Borderless avatar trigger for the shared user menu. Current account or explicit `profile` (`null` means guest); `size`, `className`, `style`; image failure falls back to initials. Sync badge defaults on in sync mode; `showSyncBadge={false}` hides it. REST mode never shows a sync badge. |
372
+ | `UserAvatar` | Borderless avatar trigger for the shared user menu. Current account or explicit `profile` (`null` means guest); `size`, `className`, `style`; a missing or failed image falls back to a locally adapted, deterministic Marble avatar seeded by DID, then account ID. The package ships its upstream attribution in `THIRD_PARTY_NOTICES.md`. Sync badge defaults on in sync mode; `showSyncBadge={false}` hides it. REST mode never shows a sync badge. |
364
373
  | `UserButton` | Avatar, name, handle, and dropdown icon with a bordered hoverable trigger. `shape="rounded\|square"` defaults to rounded. Opens the shared user menu. Local accounts never show a handle. |
365
374
  | `UserMenu` | Shared menu used by both triggers; `trigger="avatar\|button"` defaults to avatar. Current account at top, Manage account, other saved accounts, and auth actions. The switch section appears only when other accounts exist. Only expired auth gets an `Expired` warning; no signed-in badge. `showSyncStatus={false}` hides the header's sync label; REST mode always hides sync badges. `allowAddAccount` defaults to true, starting sign-in in a new profile in REST or sync mode. Local accounts offer Clear account with a destructive-action confirmation. Optional `accountSettingsUrl`. |
366
375
  | `AuthStatus` | Loading, signed-in, guest, signed-out, recovering, and expired session labels. UI visibility is not authorization. |
367
376
  | `SyncStatus` | Connectivity, pending counts, and conflict/rejection count; optional `source` for a repo or mount. An online queue is not labelled synced. |
368
- | `AccountSettingsModal` | Profile tab edits the project display name with Cancel/Save changes; drafts survive tab switches. Advanced shows read-only DID, PDS URL, local account/storage identifiers, auth/write state, and sync counts. No tokens or credentials are rendered. Local accounts show explicit DID/PDS fallbacks. `projectProfile` supplies a complete preview hook result. Optional `accountSettingsUrl` links to universal identity settings; `children` adds host-owned Profile content. |
369
- | `SharesModal` | Same identity-header layout with Outgoing/Incoming tabs. Required explicit `scope`, optional `repo` (default: `'default'`). Viewer/editor invitations by handle/DID, outgoing list, refresh, confirmed cancellation/revocation. Incoming acceptance and mount management remain in Basic ID. |
370
-
371
- Both modals accept `open`/`onOpenChange`, an optional button element or label as `trigger`, and
377
+ | `AccountSettingsModal` | Profile tab edits the project display name with Cancel/Save changes; drafts survive tab switches. Invitations loads the in-app inbox when selected (`showInvites={false}` hides it). Advanced shows read-only DID, PDS URL, local account/storage identifiers, auth/write state, and sync counts. No tokens or credentials are rendered. Local accounts show explicit DID/PDS fallbacks. `projectProfile` supplies a complete preview hook result. Optional `accountSettingsUrl` links to universal identity settings; `children` adds host-owned Profile content. |
378
+ | `SharesModal` | Same identity-header layout with Outgoing/Incoming tabs. Required explicit `scope`, optional `repo` (default: `'default'`). Viewer/editor invitations by handle/DID, outgoing list, refresh, confirmed cancellation/revocation. Incoming shows the in-app invitation inbox with accept/decline. |
379
+ | `ShareInvites` | Compact “Share Invites” list with sender handle/avatar, permissions, and right-aligned accept/decline actions. Details expands scope, expiry, and sender DID. Routine pending status, app IDs, and extra subtitles are omitted; expiry, compatibility, and completed decisions remain visible. Optional `invites` supplies a complete hook result. |
380
+ | `ShareInvitesModal` | Standalone dialog wrapping `ShareInvites`; no outgoing scope required. Supports the same `invites` override. |
381
+ | `ShareRecipientAvatar` | Display-only avatar with required `recipient: { did, name?, handle?, picture? }` and optional `size` (32 by default), native span attributes, and styles. Stable DID-seeded Marble fallback; no provider, menu, sync badge, or profile lookup. |
382
+ | `ShareRecipientAvatars` | DID-deduplicated avatar stack with `recipients`, `size`, and `max` (4 by default); accessible overflow count. Names/photos are app-supplied, never inferred from share titles. |
383
+ | `ShareRecipientLabel` | Avatar with visible `@handle`, falling back to a supplied name or “Unknown user”. Same `recipient` and native span props as the avatar; `size` defaults to 24. Long handles wrap. Canonical DID is available as the title, not the visible label. No provider or lookup required. |
384
+
385
+ All modals accept `open`/`onOpenChange`, an optional button element or label as `trigger`, and
372
386
  `finalFocus` for externally controlled use. `trigger={null}` omits the trigger. Custom trigger
373
387
  components must forward their ref and DOM props. Dialogs trap focus and close with Escape.
374
388
 
375
- `BasicUIProvider` accepts `appearance: { theme, accent, radius, density }`; density is
376
- `'comfortable'` or `'compact'`. Nested providers inherit appearance; portaled menus/dialogs retain
377
- it. Choose an accent with sufficient contrast against white for solid buttons. Fonts inherit from
378
- the host. Override the prefixed `basic-*` classes and `--basic-*` CSS tokens for finer styling.
389
+ ```tsx
390
+ import { ShareInvitesModal, ShareInvites, ShareRecipientAvatars } from '@basictech/react'
391
+
392
+ // Inside basic.Provider; choose a standalone dialog or an inline inbox:
393
+ <ShareInvitesModal trigger="View invitations" />
394
+ <ShareInvites />
395
+
396
+ // Display-only recipient avatars also work outside the provider:
397
+ <ShareRecipientAvatars recipients={[
398
+ { did: 'did:web:alice.example', name: 'Alice' },
399
+ { did: 'did:web:bob.example' },
400
+ ]} max={4} />
401
+ ```
402
+
403
+ The user menu's account settings also includes Invitations by default; pass `showInvites={false}`
404
+ to `UserButton`, `UserAvatar`, or `UserMenu` to hide it. These APIs are also exported from
405
+ `@basictech/nextjs/client`. Keep the upstream attribution shipped with the Marble renderer.
406
+
407
+ ### Simple colors and custom menu items
408
+
409
+ `appearance.base`, automatic accent contrast, and `menuItems` require `0.12.0-beta.3` or newer;
410
+ they are not available in beta.2.
411
+
412
+ Start with `theme: 'light'` or `'dark'`. Optionally set a `base` background and an `accent`.
413
+ Light mode uses black text; dark mode uses white text, with muted shades for secondary text.
414
+ Borders and hover backgrounds follow the base color. Choose a light base for light mode and a
415
+ dark base for dark mode. Omit the accent for neutral black/white buttons. For an accent, use
416
+ `#RGB` or `#RRGGBB`: solid buttons automatically choose black or white text for contrast.
417
+ Other CSS accent values retain white foreground text; check their contrast yourself.
418
+
419
+ ```tsx
420
+ import { BasicUIProvider, UserButton } from '@basictech/react'
421
+ import '@basictech/react/styles.css'
422
+
423
+ // Inside your existing BasicProvider. openPreferences is your app's callback.
424
+ <BasicUIProvider appearance={{ theme: 'dark', base: '#201d17', accent: '#d4af37' }}>
425
+ <UserButton menuItems={[
426
+ { id: 'preferences', label: 'Preferences', onClick: openPreferences },
427
+ { id: 'about', label: 'About', href: '/about' },
428
+ ]} />
429
+ </BasicUIProvider>
430
+ ```
431
+
432
+ `UserAvatar`, `UserButton`, and `UserMenu` all accept `menuItems`. Each item has a unique `id`,
433
+ `label`, either `href` or `onClick`, and optional `icon` (a React node) and `disabled`.
434
+ Icons are decorative, sized to 16px, and can come from your app's existing icon library:
435
+ `{ id: 'preferences', label: 'Preferences', icon: <SettingsIcon />, onClick: openPreferences }`.
436
+ Items appear after Manage account and before Sign out/Clear account. Selecting an item closes
437
+ the menu; async action failures use the SDK's dismissible error dialog. Use `onClick` to call
438
+ your router when you need client-side navigation rather than a normal link.
439
+
440
+ Nested appearance providers inherit settings, including in portaled menus and modals.
441
+ Pass your app's current light/dark mode to `theme`; there is no separate theme listener.
442
+ Existing `radius` and `density: 'comfortable' | 'compact'` options remain available.
443
+ Fonts inherit from the host. No icon pack or additional provider is needed.
444
+
445
+ To replace custom auth UI, keep your existing BasicProvider, import the stylesheet once,
446
+ and replace the old avatar/dropdown with `UserButton` or `UserAvatar`. Move app-owned actions
447
+ into `menuItems`; account switching and sign-out remain SDK-managed. `accountSettingsUrl`
448
+ only adds a link inside the built-in settings modal—it does not replace Manage account.
379
449
 
380
450
  For controlled previews, components accept complete hook results via `auth`, `accounts`, `sync`,
381
451
  or `shares` as appropriate. These replace rendered state **and action callbacks**; a BasicProvider
@@ -434,6 +504,8 @@ unknown table names and infer fields in reads, writes, and queries.
434
504
  | `useStorageInfo` | `useStorageInfo(): UseStorageInfoResult` | Default repo storage use and quota |
435
505
  | `useMounts` | `useMounts(query?): UseMountsResult` | Incoming mounts and mount opening |
436
506
  | `useOutgoingShares` | `useOutgoingShares(): UseOutgoingSharesResult` | Outgoing share lifecycle |
507
+ | `useShareInvites` | `useShareInvites(enabled?): UseShareInvitesResult` | Incoming invitation list, inspection, and decisions |
508
+ | `useShareRecipients` | `useShareRecipients(dids: readonly string[]): UseShareRecipientsResult` | Public, verified handles for share recipients or senders |
437
509
  | `useBasic` | `useBasic(): UseBasicResult` | Auth plus common client, db, account, sync, and repo state |
438
510
 
439
511
  ### `basic.useAuth()`
@@ -992,9 +1064,113 @@ After a handle-based share is created successfully, the SDK stores a profile-loc
992
1064
  containing `{ did, handle }` in browser local storage. Use `getContactHandle(recipientDid)` to show
993
1065
  the remembered handle; it returns `null` for DIDs that were shared directly or are not cached.
994
1066
 
995
- `manageUrl()` returns the Basic ID `/shares` handoff. Invite acceptance and ordinary mount
996
- management belong there. `client.shares.leave()` is intentionally owner-only and throws
997
- `OWNER_CREDENTIAL_REQUIRED` without an explicit owner adapter.
1067
+ `manageUrl()` remains an optional Basic ID `/shares` handoff. Invitations can now be managed
1068
+ inside apps with `useShareInvites()` or the prebuilt components. `client.shares.leave(mountId)`
1069
+ also supports matching app Shares write permission (or root Shares write permission).
1070
+
1071
+ ### `basic.useShareInvites()`
1072
+
1073
+ ```text
1074
+ {
1075
+ data: ShareInviteInfo[]
1076
+ isLoading: boolean
1077
+ error: BasicError | null
1078
+ refresh(): void
1079
+ get(id: string): Promise<ShareInviteInfo>
1080
+ accept(id: string): Promise<MountInfo>
1081
+ decline(id: string): Promise<ShareInviteInfo>
1082
+ delete(id: string): Promise<ShareInviteInfo>
1083
+ resolveContactHandle(did: string): Promise<string | null>
1084
+ }
1085
+ ```
1086
+
1087
+ Uses the signed-in account's existing core `client.shares` invitation methods with app credentials;
1088
+ no owner adapter or Basic ID navigation is needed. Reads require Shares read (or matching app)
1089
+ permission, and decisions require write permission. Results are app-scoped unless explicitly
1090
+ granted root Shares access. Permission/compatibility errors are not treated as an empty inbox.
1091
+ Pass `false` to defer loading until your inbox is visible; imperative methods still work.
1092
+
1093
+ `resolveContactHandle(did)` uses public DID-document and handle-resolution requests without
1094
+ credentials. It only returns an advertised `basic://` handle if resolving that handle points
1095
+ back to the same DID. Missing/unverified handles return `null`; network failures reject.
1096
+ The inbox loads handles independently of invitations, shows “Unknown sender” on failure, and
1097
+ keeps the canonical DID under Details. It never invents a handle from the DID or share title.
1098
+ Custom `invites` results must supply this resolver too; standalone recipient avatars still
1099
+ make no network requests.
1100
+
1101
+ `ShareInviteInfo` includes sender `originOwnerDid`, `appId`, `role`, `scope`, display titles,
1102
+ `acceptBy`, `state`, compatibility, and received/decision timestamps. Review these fields before
1103
+ acceptance: display titles describe data, not the sender's identity. Accepting creates a mount
1104
+ for data shared **with** the recipient; it does not grant access to the recipient's own data.
1105
+
1106
+ Pending invitations can be accepted, declined, or deleted. An `accepting` invitation can retry
1107
+ acceptance, but cannot be declined/deleted. Expired or incompatible invitations cannot be accepted;
1108
+ expired pending invitations can still be declined. The server enforces all permissions and states.
1109
+ Decisions refresh this hook's list even on failure, because acceptance may have partially advanced.
1110
+ Other hook instances are independent: refresh an existing mount list after acceptance and call
1111
+ `mounts.open(mount.id)` to access its data. For custom behavior, the inline/modal `invites` prop
1112
+ accepts a hook result with wrapped methods:
1113
+
1114
+ ```tsx
1115
+ function Inbox() {
1116
+ const invites = basic.useShareInvites()
1117
+ const mounts = basic.useMounts()
1118
+ return <ShareInvites invites={{
1119
+ ...invites,
1120
+ async accept(id) {
1121
+ const mount = await invites.accept(id)
1122
+ mounts.refresh()
1123
+ return mount
1124
+ },
1125
+ }} />
1126
+ }
1127
+ ```
1128
+
1129
+ An account switch immediately hides the previous account's data/errors. Stale decision callbacks
1130
+ and late results reject with `ACCOUNT_CHANGED`; this does not undo a request already sent to the
1131
+ server. Loading and action state in the components resets by local profile ID, including two
1132
+ profiles with the same DID. Custom UI must catch rejected imperative calls.
1133
+
1134
+ ### `basic.useShareRecipients()` and shared-user display
1135
+
1136
+ `useShareRecipients(dids)` returns `{ data: ShareRecipient[], isLoading, error, refresh }`.
1137
+ Pass outgoing `recipientDid` values, incoming `originOwnerDid` values, or any list of DIDs.
1138
+ For one person, pass `[did]`. The hook requires the provider but not sign-in: it uses public
1139
+ `client.shares.resolveContactHandle()` lookups with DID → handle → DID verification.
1140
+
1141
+ - DIDs are deduplicated in first-occurrence order. Recreating an equivalent array does not refetch.
1142
+ - `data` immediately contains `{ did, handle: null }` entries, so avatars can render while loading.
1143
+ - Once lookups settle, verified handles are filled in. Missing/unverified handles remain `null`.
1144
+ A failed request leaves that recipient's handle `null`, preserves the other results, and exposes
1145
+ the first failure (in input order) as `error`. `refresh()` retries the current list.
1146
+ - Input/client changes hide obsolete identities immediately and ignore late results. Each hook
1147
+ instance owns its requests; resolve once in a parent and reuse `data` across labels/stacks.
1148
+ - The Shares API has no public photo/name endpoint. The hook does not fetch those fields.
1149
+ You may supply `name` or `picture` from your app. Missing/loading/failed images use the existing
1150
+ DID-seeded local Marble avatar, with its MIT attribution retained.
1151
+
1152
+ ```tsx
1153
+ import { ShareRecipientAvatars, ShareRecipientLabel } from '@basictech/react'
1154
+ import '@basictech/react/styles.css'
1155
+ import { basic } from './basic'
1156
+
1157
+ function SharedWith({ dids }: { dids: readonly string[] }) {
1158
+ const { data, error, refresh } = basic.useShareRecipients(dids)
1159
+ return (
1160
+ <div>
1161
+ <ShareRecipientAvatars recipients={data} max={4} />
1162
+ <ul>{data.map((recipient) => (
1163
+ <li key={recipient.did}><ShareRecipientLabel recipient={recipient} /></li>
1164
+ ))}</ul>
1165
+ {error && <button onClick={refresh}>Retry handle lookup</button>}
1166
+ </div>
1167
+ )
1168
+ }
1169
+ ```
1170
+
1171
+ All three recipient components are display-only and accept the same `ShareRecipient` shape.
1172
+ They do not open the account menu or fetch identities themselves. In Next.js Client Components,
1173
+ import these components and `useShareRecipients` from `@basictech/nextjs/client`.
998
1174
 
999
1175
  ### `basic.useMounts()` and mounted data
1000
1176
 
@@ -1182,7 +1358,8 @@ The unbound exports mirror the bound hooks:
1182
1358
  | `useQuery<V>(name, query?, { source? }?)` | Caller supplies a JSON object value type; name is a string |
1183
1359
  | `useSyncStatus(source?)`, `useSchemaStatus(source?)` | Same status results |
1184
1360
  | `useRepos()`, `useFiles()`, `useStorageInfo()` | Same repo/file results |
1185
- | `useMounts()`, `useOutgoingShares()` | Same share results |
1361
+ | `useMounts()`, `useOutgoingShares()`, `useShareInvites(enabled?)` | Same share results |
1362
+ | `useShareRecipients(dids)` | Same public identity result |
1186
1363
 
1187
1364
  Every unbound hook must run below `BasicProvider`; otherwise it throws `Basic hooks must be used
1188
1365
  within a <BasicProvider>`. Bound hooks likewise throw outside their own `basic.Provider`, because
@@ -1236,13 +1413,16 @@ Common application-facing codes include:
1236
1413
  | `STALE_WRITE`, `SCHEMA_VALIDATION_FAILED` | Server rejected a write; REST errors arrive as `ProblemError`, while sync terminal failures appear in `rejected` |
1237
1414
  | `ACCESS_TOKEN_ADOPTION_TIMEOUT`, `invalid_grant` | Cross-tab token adoption failed or reauthentication is required |
1238
1415
 
1239
- Data hooks (`useQuery`, `useFiles`, `useStorageInfo`, `useMounts`, and `useOutgoingShares`) use the
1240
- same `{ data, isLoading, error }` lifecycle. Files/mounts/outgoing also expose `refresh()`. Disabled
1241
- signed-out storage, mount, and outgoing-share hooks settle after client readiness with empty/null
1416
+ Data hooks (`useQuery`, `useFiles`, `useStorageInfo`, `useMounts`, `useOutgoingShares`, and
1417
+ `useShareInvites`) use the same `{ data, isLoading, error }` lifecycle. Files/mounts/outgoing/invites
1418
+ also expose `refresh()`. Disabled signed-out storage, mount, and share hooks settle after client readiness with empty/null
1242
1419
  data rather than remaining loading. Non-Basic data-hook failures are wrapped as `UNKNOWN_ERROR`;
1243
1420
  query failures use `QUERY_FAILED` as the fallback code. `useAuth().error` reflects auth lifecycle
1244
1421
  errors; imperative methods reject and should be caught at the call site.
1245
1422
 
1423
+ `useShareRecipients` has the same result/refresh shape but performs public identity lookup without
1424
+ an auth gate. Partial lookup failures preserve successful recipients instead of clearing the list.
1425
+
1246
1426
  ## Sync and REST modes
1247
1427
 
1248
1428
  The database and query shapes are shared, but the guarantees differ:
@@ -0,0 +1,29 @@
1
+ # Third-party notices
2
+
3
+ ## boring-avatars Marble renderer
4
+
5
+ The built-in Marble avatar fallback adapts the Marble renderer and required utility behavior from
6
+ boring-avatars 2.0.4, commit `89270f1d423a60a2fa2c241127e1744cba38aa38`:
7
+ https://github.com/boringdesigners/boring-avatars/tree/89270f1d423a60a2fa2c241127e1744cba38aa38
8
+
9
+ MIT License
10
+
11
+ Copyright (c) 2021 boringdesigners
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ReactElement, RefObject, ButtonHTMLAttributes, HTMLAttributes } from 'react';
3
- import { BasicSchema, BasicConfig, BasicClient, BasicAccount, BasicProfile, ReadOnlyReason, AuthStatus as AuthStatus$1, BasicError, AuthUser, BasicSyncStatus, BasicRejection, BasicConflict, SourceRef, SchemaStatus, BasicDb, Repo, FileRecord, MountFileInfo, StorageInfo, FileListQuery, MountInfo, MountHandle, OutgoingShareInfo, CreateOutgoingShareInput, MountsQuery, JsonObject, BasicRecord, Query, ProjectProfile, ProjectProfilePatch, MountViewerFiles, OwnerFiles, TableNames, Collection, InferValue, KeyValueStorage, TokenStore, AuthChannelContext, AuthMessageChannel, ReplicaStoreFactory, StoragePartition, ReplicaStore, UploadTransportAdapter } from '@basictech/core';
3
+ import { BasicSchema, BasicConfig, BasicClient, BasicAccount, BasicProfile, ReadOnlyReason, AuthStatus as AuthStatus$1, BasicError, AuthUser, BasicSyncStatus, BasicRejection, BasicConflict, SourceRef, SchemaStatus, BasicDb, Repo, FileRecord, MountFileInfo, StorageInfo, FileListQuery, MountInfo, MountHandle, OutgoingShareInfo, CreateOutgoingShareInput, MountsQuery, JsonObject, BasicRecord, Query, ProjectProfile, ProjectProfilePatch, ShareInviteInfo, MountViewerFiles, OwnerFiles, TableNames, Collection, InferValue, KeyValueStorage, TokenStore, AuthChannelContext, AuthMessageChannel, ReplicaStoreFactory, StoragePartition, ReplicaStore, UploadTransportAdapter } from '@basictech/core';
4
4
 
5
5
  type BrowserBasicConfig<S extends BasicSchema = BasicSchema> = BasicConfig<S>;
6
6
  /** Create a core client with the complete browser adapter set installed. */
@@ -133,6 +133,29 @@ interface UseProjectProfileResult extends AsyncResult<ProjectProfile | null> {
133
133
  }
134
134
  declare function useProjectProfile(enabled?: boolean): UseProjectProfileResult;
135
135
 
136
+ interface UseShareInvitesResult {
137
+ data: ShareInviteInfo[];
138
+ isLoading: boolean;
139
+ error: BasicError | null;
140
+ refresh(): void;
141
+ get(id: string): Promise<ShareInviteInfo>;
142
+ accept(id: string): Promise<MountInfo>;
143
+ decline(id: string): Promise<ShareInviteInfo>;
144
+ delete(id: string): Promise<ShareInviteInfo>;
145
+ resolveContactHandle(did: string): Promise<string | null>;
146
+ }
147
+ declare function useShareInvites(enabled?: boolean): UseShareInvitesResult;
148
+
149
+ /** Names/photos are optional app-supplied data, never inferred from a share title. */
150
+ interface ShareRecipient {
151
+ did: string;
152
+ name?: string | null;
153
+ handle?: string | null;
154
+ picture?: string | null;
155
+ }
156
+ type UseShareRecipientsResult = AsyncResult<ShareRecipient[]>;
157
+ declare function useShareRecipients(dids: readonly string[]): UseShareRecipientsResult;
158
+
136
159
  type CreateBasicConfig<S extends BasicSchema> = BrowserBasicConfig<S> & {
137
160
  schema: S;
138
161
  };
@@ -169,6 +192,8 @@ interface CreatedBasic<S extends BasicSchema> {
169
192
  useStorageInfo(): UseStorageInfoResult;
170
193
  useMounts(query?: MountsQuery): UseMountsResult<S>;
171
194
  useOutgoingShares(): UseOutgoingSharesResult;
195
+ useShareInvites(enabled?: boolean): UseShareInvitesResult;
196
+ useShareRecipients(dids: readonly string[]): UseShareRecipientsResult;
172
197
  }
173
198
  /** Create one browser client and a complete hook surface bound to its schema. */
174
199
  declare function createBasic<const S extends BasicSchema>(config: CreateBasicConfig<S>): CreatedBasic<S>;
@@ -198,6 +223,9 @@ declare function useCollection<V extends JsonObject = JsonObject>(name: string,
198
223
 
199
224
  interface BasicAppearance {
200
225
  theme?: 'light' | 'dark';
226
+ /** Background color. Choose a light or dark base to match the theme. */
227
+ base?: string;
228
+ /** Hex colors get automatic black/white foreground contrast. */
201
229
  accent?: string;
202
230
  radius?: string;
203
231
  density?: 'compact' | 'comfortable';
@@ -218,16 +246,34 @@ interface AuthButtonProps extends BasicButtonProps {
218
246
  declare function SignInButton(props: AuthButtonProps): react.JSX.Element;
219
247
  declare function SignOutButton(props: AuthButtonProps): react.JSX.Element;
220
248
  interface UserAvatarProps extends HTMLAttributes<HTMLSpanElement> {
221
- profile?: (Pick<BasicProfile, 'name' | 'handle' | 'email' | 'picture'> & Partial<Pick<BasicProfile, 'kind'>>) | null;
249
+ profile?: (Pick<BasicProfile, 'name' | 'handle' | 'email' | 'picture'> & Partial<Pick<BasicProfile, 'id' | 'did' | 'kind'>>) | null;
222
250
  size?: number;
223
251
  showSyncBadge?: boolean;
224
252
  sync?: UseSyncStatusResult;
225
253
  accounts?: UseAccountsResult;
226
254
  auth?: UseAuthResult;
227
255
  projectProfile?: UseProjectProfileResult;
256
+ invites?: UseShareInvitesResult;
257
+ showInvites?: boolean;
228
258
  allowAddAccount?: boolean;
259
+ menuItems?: UserMenuItem[];
260
+ }
261
+ declare function UserAvatar({ accounts, auth, projectProfile, invites, showInvites, allowAddAccount, menuItems, ...avatarProps }: UserAvatarProps): react.JSX.Element;
262
+ interface ShareRecipientAvatarProps extends HTMLAttributes<HTMLSpanElement> {
263
+ recipient: ShareRecipient;
264
+ size?: number;
229
265
  }
230
- declare function UserAvatar({ accounts, auth, projectProfile, allowAddAccount, ...avatarProps }: UserAvatarProps): react.JSX.Element;
266
+ /** Display-only identity: no account menu, sync badge, provider, or profile lookup. */
267
+ declare function ShareRecipientAvatar({ recipient, ...props }: ShareRecipientAvatarProps): react.JSX.Element;
268
+ type ShareRecipientLabelProps = ShareRecipientAvatarProps;
269
+ /** Avatar and visible handle; accepts the same data as the avatar-only components. */
270
+ declare function ShareRecipientLabel({ recipient, size, className, ...props }: ShareRecipientLabelProps): react.JSX.Element;
271
+ interface ShareRecipientAvatarsProps extends HTMLAttributes<HTMLSpanElement> {
272
+ recipients: readonly ShareRecipient[];
273
+ size?: number;
274
+ max?: number;
275
+ }
276
+ declare function ShareRecipientAvatars({ recipients, size, max, className, ...props }: ShareRecipientAvatarsProps): react.JSX.Element;
231
277
  declare function AuthStatus({ auth: supplied, className, ...props }: HTMLAttributes<HTMLSpanElement> & {
232
278
  auth?: UseAuthResult;
233
279
  }): react.JSX.Element;
@@ -235,6 +281,19 @@ declare function SyncStatus({ sync: supplied, source, className, ...props }: HTM
235
281
  sync?: UseSyncStatusResult;
236
282
  source?: SourceRef;
237
283
  }): react.JSX.Element | null;
284
+ /** App-owned items appear after Manage account and before Sign out. */
285
+ type UserMenuItem = {
286
+ id: string;
287
+ label: string;
288
+ icon?: ReactNode;
289
+ disabled?: boolean;
290
+ } & ({
291
+ href: string;
292
+ onClick?: never;
293
+ } | {
294
+ onClick: () => void | Promise<void>;
295
+ href?: never;
296
+ });
238
297
  interface UserButtonProps {
239
298
  accounts?: UseAccountsResult;
240
299
  auth?: UseAuthResult;
@@ -245,17 +304,20 @@ interface UserButtonProps {
245
304
  showSyncBadge?: boolean;
246
305
  shape?: 'rounded' | 'square';
247
306
  projectProfile?: UseProjectProfileResult;
307
+ invites?: UseShareInvitesResult;
308
+ showInvites?: boolean;
248
309
  /** Show the action to sign in to another account. Defaults to true. */
249
310
  allowAddAccount?: boolean;
250
311
  className?: string;
251
312
  accountSettingsUrl?: string;
313
+ menuItems?: UserMenuItem[];
252
314
  }
253
315
  declare function UserButton(props: UserButtonProps): react.JSX.Element;
254
316
  interface UserMenuProps extends UserButtonProps {
255
317
  trigger?: 'avatar' | 'button';
256
318
  avatarProps?: UserAvatarProps;
257
319
  }
258
- declare function UserMenu({ accounts: supplied, auth: suppliedAuth, sync, showSyncStatus, showSyncBadge, shape, trigger, avatarProps, projectProfile, allowAddAccount, className, accountSettingsUrl, }: UserMenuProps): react.JSX.Element;
320
+ declare function UserMenu({ accounts: supplied, auth: suppliedAuth, sync, showSyncStatus, showSyncBadge, shape, trigger, avatarProps, projectProfile, invites, showInvites, allowAddAccount, className, accountSettingsUrl, menuItems, }: UserMenuProps): react.JSX.Element;
259
321
  interface BasicModalProps {
260
322
  open?: boolean;
261
323
  onOpenChange?: (open: boolean) => void;
@@ -267,21 +329,39 @@ interface AccountSettingsModalProps extends BasicModalProps {
267
329
  auth?: UseAuthResult;
268
330
  accounts?: UseAccountsResult;
269
331
  projectProfile?: UseProjectProfileResult;
332
+ invites?: UseShareInvitesResult;
333
+ /** Show the in-app invitation inbox. Defaults to true; loads only when selected. */
334
+ showInvites?: boolean;
270
335
  sync?: UseSyncStatusResult;
271
336
  accountSettingsUrl?: string;
272
337
  children?: ReactNode;
273
338
  }
274
- declare function AccountSettingsModal({ auth: suppliedAuth, accounts: suppliedAccounts, accountSettingsUrl, projectProfile, sync, children, ...props }: AccountSettingsModalProps): react.JSX.Element;
339
+ declare function AccountSettingsModal({ auth: suppliedAuth, accounts: suppliedAccounts, accountSettingsUrl, projectProfile, invites, showInvites, sync, children, ...props }: AccountSettingsModalProps): react.JSX.Element;
340
+ interface ShareInvitesProps extends HTMLAttributes<HTMLElement> {
341
+ auth?: UseAuthResult;
342
+ accounts?: UseAccountsResult;
343
+ /** Optional complete hook result, useful for previews or app-owned state. */
344
+ invites?: UseShareInvitesResult;
345
+ }
346
+ /** Inline invitation inbox, shared by standalone and account-settings dialogs. */
347
+ declare function ShareInvites({ auth: suppliedAuth, accounts: suppliedAccounts, invites, className, ...props }: ShareInvitesProps): react.JSX.Element;
348
+ interface ShareInvitesModalProps extends BasicModalProps {
349
+ auth?: UseAuthResult;
350
+ accounts?: UseAccountsResult;
351
+ invites?: UseShareInvitesResult;
352
+ }
353
+ declare function ShareInvitesModal({ auth, accounts, invites, ...props }: ShareInvitesModalProps): react.JSX.Element;
275
354
  interface SharesModalProps extends BasicModalProps {
276
355
  auth?: UseAuthResult;
277
356
  accounts?: UseAccountsResult;
278
357
  sync?: UseSyncStatusResult;
279
358
  shares?: UseOutgoingSharesResult;
359
+ invites?: UseShareInvitesResult;
280
360
  /** Explicit least-privilege scope; the component never infers all tables. */
281
361
  scope: CreateOutgoingShareInput['scope'];
282
362
  repo?: CreateOutgoingShareInput['repo'];
283
363
  }
284
- declare function SharesModal({ auth: supplied, accounts: suppliedAccounts, sync, shares, scope, repo, ...props }: SharesModalProps): react.JSX.Element;
364
+ declare function SharesModal({ auth: supplied, accounts: suppliedAccounts, sync, shares, invites, scope, repo, ...props }: SharesModalProps): react.JSX.Element;
285
365
 
286
366
  /** Synchronous browser Storage adapted to core's key-value contract. */
287
367
  declare class BrowserKeyValueStorage implements KeyValueStorage {
@@ -378,4 +458,4 @@ declare class PersistenceStore implements ReplicaStoreFactory {
378
458
  /** Browser multipart transport with upload progress, adapted from the Drive UI. */
379
459
  declare const browserUploadTransport: UploadTransportAdapter;
380
460
 
381
- export { type AccessTokenAdopter, AccountSettingsModal, type AccountSettingsModalProps, type AuthButtonProps, AuthStatus, type BasicAppearance, BasicButton, type BasicButtonProps, type BasicModalProps, BasicProvider, type BasicProviderProps, BasicUIProvider, type BoundBasicProviderProps, type BrowserBasicConfig, BrowserKeyValueStorage, type BrowserLockManager, type BrowserMessageChannel, BrowserTokenStore, type CreateBasicConfig, type CreatedBasic, PersistenceStore, type PersistenceStoreOptions, SharesModal, type SharesModalProps, SignInButton, SignOutButton, SyncStatus, type UseAccountsResult, type UseAuthResult, type UseBasicResult, type UseFilesResult, type UseMountsResult, type UseOutgoingSharesResult, type UseProjectProfileResult, type UseQueryResult, type UseReposResult, type UseStorageInfoResult, type UseSyncStatusResult, UserAvatar, type UserAvatarProps, UserButton, type UserButtonProps, UserMenu, type UserMenuProps, browserCurrentUrl, browserNavigate, browserReplaceUrl, browserStorage, browserUploadTransport, createBasic, createBasicClient, createBrowserAuthChannelFactory, createBrowserMessageChannel, useAccounts, useAuth, useBasic, useCollection, useDb, useFiles, useMounts, useOutgoingShares, useProjectProfile, useQuery, useRepos, useSchemaStatus, useStorageInfo, useSyncStatus };
461
+ export { type AccessTokenAdopter, AccountSettingsModal, type AccountSettingsModalProps, type AuthButtonProps, AuthStatus, type BasicAppearance, BasicButton, type BasicButtonProps, type BasicModalProps, BasicProvider, type BasicProviderProps, BasicUIProvider, type BoundBasicProviderProps, type BrowserBasicConfig, BrowserKeyValueStorage, type BrowserLockManager, type BrowserMessageChannel, BrowserTokenStore, type CreateBasicConfig, type CreatedBasic, PersistenceStore, type PersistenceStoreOptions, ShareInvites, ShareInvitesModal, type ShareInvitesModalProps, type ShareInvitesProps, type ShareRecipient, ShareRecipientAvatar, type ShareRecipientAvatarProps, ShareRecipientAvatars, type ShareRecipientAvatarsProps, ShareRecipientLabel, type ShareRecipientLabelProps, SharesModal, type SharesModalProps, SignInButton, SignOutButton, SyncStatus, type UseAccountsResult, type UseAuthResult, type UseBasicResult, type UseFilesResult, type UseMountsResult, type UseOutgoingSharesResult, type UseProjectProfileResult, type UseQueryResult, type UseReposResult, type UseShareInvitesResult, type UseShareRecipientsResult, type UseStorageInfoResult, type UseSyncStatusResult, UserAvatar, type UserAvatarProps, UserButton, type UserButtonProps, UserMenu, type UserMenuItem, type UserMenuProps, browserCurrentUrl, browserNavigate, browserReplaceUrl, browserStorage, browserUploadTransport, createBasic, createBasicClient, createBrowserAuthChannelFactory, createBrowserMessageChannel, useAccounts, useAuth, useBasic, useCollection, useDb, useFiles, useMounts, useOutgoingShares, useProjectProfile, useQuery, useRepos, useSchemaStatus, useShareInvites, useShareRecipients, useStorageInfo, useSyncStatus };