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

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,23 @@
1
1
  # @basictech/react
2
2
 
3
+ ## 0.12.0-beta.2
4
+
5
+ ### Minor Changes
6
+
7
+ - 85388da: Add Base UI-backed account, auth, sync-status, avatar, and sharing components with optional scoped
8
+ styles and an appearance provider. Include a React DOM peer dependency for the portaled UI.
9
+ Add app-token project-profile read/update/reset APIs, React hook and settings editor, per-account
10
+ menu badges, and an optional avatar sync indicator.
11
+ Expose the components and project-profile hook through the Next.js client entry.
12
+ Share the saved-account registry between REST and sync modes, support add-account sign-in,
13
+ and keep cached display identity up to date after profile saves. Include read-only account
14
+ diagnostics with clipboard export and suppress sync badges in REST mode.
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies [85388da]
19
+ - @basictech/core@0.12.0-beta.2
20
+
3
21
  ## 0.12.0-beta.1
4
22
 
5
23
  ### Minor Changes
package/README.md CHANGED
@@ -328,6 +328,93 @@ With the default `renderWhileLoading={false}`, hooks under the Provider first re
328
328
  client is ready. Setting it to `true` is useful for an application shell that reads `isReady` and
329
329
  owns its loading UI.
330
330
 
331
+ ## Prebuilt components
332
+
333
+ Drop-in account and sharing UI uses Base UI's accessible menu, avatar, and dialog primitives.
334
+ Components use the nearest `BasicProvider` (including `basic.Provider`); they do not create a
335
+ second auth client. Import the optional stylesheet once. Omit it to supply your own styles.
336
+
337
+ ```tsx
338
+ import {
339
+ BasicUIProvider, SignInButton, UserButton, AuthStatus, SyncStatus, SharesModal,
340
+ } from '@basictech/react'
341
+ import '@basictech/react/styles.css'
342
+ import { basic } from './basic'
343
+
344
+ export function AccountTools() {
345
+ return (
346
+ <basic.Provider>
347
+ <BasicUIProvider appearance={{ theme: 'light', accent: '#354f45', radius: '8px' }}>
348
+ <SignInButton />
349
+ <UserButton allowAddAccount />
350
+ <AuthStatus />
351
+ <SyncStatus />
352
+ <SharesModal scope={[{ table: 'todos', recordIds: ['todo-1'] }]} />
353
+ </BasicUIProvider>
354
+ </basic.Provider>
355
+ )
356
+ }
357
+ ```
358
+
359
+ | Component | Options and behavior |
360
+ | --- | --- |
361
+ | `BasicButton` | Native button props/ref; `variant="solid\|outline\|ghost"`. Defaults to `type="button"`. |
362
+ | `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. |
364
+ | `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
+ | `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
+ | `AuthStatus` | Loading, signed-in, guest, signed-out, recovering, and expired session labels. UI visibility is not authorization. |
367
+ | `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
372
+ `finalFocus` for externally controlled use. `trigger={null}` omits the trigger. Custom trigger
373
+ components must forward their ref and DOM props. Dialogs trap focus and close with Escape.
374
+
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.
379
+
380
+ For controlled previews, components accept complete hook results via `auth`, `accounts`, `sync`,
381
+ or `shares` as appropriate. These replace rendered state **and action callbacks**; a BasicProvider
382
+ is still required and hooks still subscribe/read. Do not use these props as an authorization
383
+ boundary. Real mutations remain subject to SDK and server permissions.
384
+
385
+ Share creation requires an explicit nonempty table/record scope and defaults to viewer access.
386
+ The list is labelled **all permissions**, not filtered to the new-invitation scope. Revocation cannot
387
+ retract downloaded data. Signing out removes the account's local cache and pending edits.
388
+ The incoming-invitations section links to Basic ID: inbox/accept/decline APIs require owner
389
+ credentials, not app tokens. Server project-profile GET/PATCH/DELETE APIs exist at
390
+ `/account/:project_id/profile` with `app:profile:read/update/delete` grants respectively.
391
+ Configure those requested OAuth scopes and reauthorize existing sessions when needed.
392
+ The shared menu shows sync status and any expiry warning with the current account in its header.
393
+ Inactive accounts show saved expiry state, not the active account's sync status.
394
+ Both `UserButton` and `UserAvatar` accept `showSyncBadge` (default follows SDK sync mode); circles use
395
+ green for synced, blue for connecting/pending, orange for errors, gray for offline/local/idle.
396
+ The demo's `?page=components` gallery offers live behavior, isolated simulated actions, design
397
+ controls, and a copyable usage recipe. Live OAuth/shares require a configured signed-in account.
398
+
399
+ ### Project profile API
400
+
401
+ `basic.useProjectProfile()` (also exported as `useProjectProfile`) returns `data`, `isLoading`,
402
+ `error`, `refresh()`, `update(patch)`, and `reset()`. Pass `false` to disable automatic reads.
403
+ `data` is `{ profile: { name?, avatar_url?, status? }, meta? }` or null before loading/sign-in.
404
+ The core client exposes the same operations directly:
405
+
406
+ ```ts
407
+ await basic.client.getProjectProfile()
408
+ await basic.client.updateProjectProfile({ name: 'Name in this app', status: null })
409
+ await basic.client.resetProjectProfile()
410
+ ```
411
+
412
+ Requests always target the configured client ID using the active app token. Omitted fields stay
413
+ unchanged; null removes an override. Metadata objects merge on the server; `meta: null` clears
414
+ metadata. Reset removes all project overrides and metadata. Universal profile data is untouched.
415
+ Permission failures propagate as SDK errors. Profile responses are not written into the local
416
+ account registry; consumers needing the updated project identity should use this hook's data.
417
+
331
418
  ## Hook reference
332
419
 
333
420
  The schema-bound factory returns every hook below. Bound hooks are preferred because they reject
@@ -412,14 +499,16 @@ replicas and returns to another local profile or a fresh anonymous one when enab
412
499
  - `accounts: BasicAccount[]` lists local profiles with per-account auth summaries.
413
500
  - `activeAccount: BasicAccount | null` is this tab's selected profile and auth summary.
414
501
  - `switchAccount(id): Promise<void>` changes the active profile in this tab.
415
- - `addAccount(): Promise<BasicProfile>` creates and activates a new anonymous profile from which to
416
- start another sign-in.
502
+ - `addAccount({ signIn?: boolean }): Promise<BasicProfile>` creates and activates a new profile.
503
+ Omit options to stay local; pass `{ signIn: true }` to validate redirect configuration first and
504
+ start sign-in in the new profile. The prebuilt menu uses this sign-in option.
417
505
  - `removeAccount(id): Promise<void>` removes that profile and its local credentials/replicas. If it
418
506
  is active, this follows the sign-out path. It does not delete the server account.
419
507
 
420
508
  `BasicProfile` contains `id`, `kind: 'anon' | 'account'`, `storagePrefix`, `createdAt`,
421
- `lastActiveAt`, and optional/null `did`, `handle`, `email`, `name`, and `picture`. Multi-account and
422
- `addAccount()` require sync mode with anonymous profiles enabled.
509
+ `lastActiveAt`, and optional/null `did`, `handle`, `email`, `name`, and `picture`. Saved accounts and
510
+ switching work in REST and sync modes. `addAccount({ signIn: true })` works in either mode;
511
+ `addAccount()` without sign-in still requires sync mode with anonymous profiles enabled.
423
512
 
424
513
  ```tsx
425
514
  // file: AccountSwitcher.tsx
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { ReactNode, ReactElement } from 'react';
3
- import { BasicSchema, BasicConfig, BasicClient, BasicAccount, BasicProfile, ReadOnlyReason, AuthStatus, BasicError, AuthUser, BasicSyncStatus, BasicRejection, BasicConflict, SourceRef, SchemaStatus, BasicDb, Repo, FileRecord, MountFileInfo, StorageInfo, FileListQuery, MountInfo, MountHandle, OutgoingShareInfo, CreateOutgoingShareInput, MountsQuery, JsonObject, BasicRecord, Query, MountViewerFiles, OwnerFiles, TableNames, Collection, InferValue, KeyValueStorage, TokenStore, AuthChannelContext, AuthMessageChannel, ReplicaStoreFactory, StoragePartition, ReplicaStore, UploadTransportAdapter } from '@basictech/core';
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';
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. */
@@ -10,7 +10,9 @@ interface UseAccountsResult {
10
10
  accounts: BasicAccount[];
11
11
  activeAccount: BasicAccount | null;
12
12
  switchAccount(id: string): Promise<void>;
13
- addAccount(): Promise<BasicProfile>;
13
+ addAccount(options?: {
14
+ signIn?: boolean;
15
+ }): Promise<BasicProfile>;
14
16
  removeAccount(id: string): Promise<void>;
15
17
  }
16
18
  declare function useAccounts(): UseAccountsResult;
@@ -21,7 +23,7 @@ interface UseAuthResult {
21
23
  isAnonymous: boolean;
22
24
  canWrite: boolean;
23
25
  readOnlyReason: ReadOnlyReason | null;
24
- status: AuthStatus;
26
+ status: AuthStatus$1;
25
27
  /** Raw auth diagnostic; stable identity while its code and expired state are unchanged. */
26
28
  error: BasicError | null;
27
29
  user: AuthUser | null;
@@ -118,6 +120,19 @@ interface UseReposResult {
118
120
  }
119
121
  declare function useRepos(): UseReposResult;
120
122
 
123
+ interface AsyncResult<T> {
124
+ data: T;
125
+ isLoading: boolean;
126
+ error: BasicError | null;
127
+ refresh(): void;
128
+ }
129
+
130
+ interface UseProjectProfileResult extends AsyncResult<ProjectProfile | null> {
131
+ update(patch: ProjectProfilePatch): Promise<ProjectProfile>;
132
+ reset(): Promise<ProjectProfile>;
133
+ }
134
+ declare function useProjectProfile(enabled?: boolean): UseProjectProfileResult;
135
+
121
136
  type CreateBasicConfig<S extends BasicSchema> = BrowserBasicConfig<S> & {
122
137
  schema: S;
123
138
  };
@@ -147,6 +162,7 @@ interface CreatedBasic<S extends BasicSchema> {
147
162
  useSyncStatus(source?: SourceRef): UseSyncStatusResult;
148
163
  useSchemaStatus(source?: SourceRef): SchemaStatus;
149
164
  useRepos(): UseReposResult;
165
+ useProjectProfile(enabled?: boolean): UseProjectProfileResult;
150
166
  useFiles(query?: FileListQuery, options?: {
151
167
  source?: SourceRef;
152
168
  }): UseFilesResult;
@@ -180,6 +196,93 @@ declare function useCollection<V extends JsonObject = JsonObject>(name: string,
180
196
  source?: SourceRef;
181
197
  }): Collection<V>;
182
198
 
199
+ interface BasicAppearance {
200
+ theme?: 'light' | 'dark';
201
+ accent?: string;
202
+ radius?: string;
203
+ density?: 'compact' | 'comfortable';
204
+ }
205
+ /** Optional styling provider. CSS is opt-in via @basictech/react/styles.css. */
206
+ declare function BasicUIProvider({ appearance, children, }: {
207
+ appearance?: BasicAppearance;
208
+ children: ReactNode;
209
+ }): react.JSX.Element;
210
+ interface BasicButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
211
+ variant?: 'solid' | 'outline' | 'ghost';
212
+ }
213
+ declare const BasicButton: react.ForwardRefExoticComponent<BasicButtonProps & react.RefAttributes<HTMLButtonElement>>;
214
+ interface AuthButtonProps extends BasicButtonProps {
215
+ auth?: UseAuthResult;
216
+ input?: string;
217
+ }
218
+ declare function SignInButton(props: AuthButtonProps): react.JSX.Element;
219
+ declare function SignOutButton(props: AuthButtonProps): react.JSX.Element;
220
+ interface UserAvatarProps extends HTMLAttributes<HTMLSpanElement> {
221
+ profile?: (Pick<BasicProfile, 'name' | 'handle' | 'email' | 'picture'> & Partial<Pick<BasicProfile, 'kind'>>) | null;
222
+ size?: number;
223
+ showSyncBadge?: boolean;
224
+ sync?: UseSyncStatusResult;
225
+ accounts?: UseAccountsResult;
226
+ auth?: UseAuthResult;
227
+ projectProfile?: UseProjectProfileResult;
228
+ allowAddAccount?: boolean;
229
+ }
230
+ declare function UserAvatar({ accounts, auth, projectProfile, allowAddAccount, ...avatarProps }: UserAvatarProps): react.JSX.Element;
231
+ declare function AuthStatus({ auth: supplied, className, ...props }: HTMLAttributes<HTMLSpanElement> & {
232
+ auth?: UseAuthResult;
233
+ }): react.JSX.Element;
234
+ declare function SyncStatus({ sync: supplied, source, className, ...props }: HTMLAttributes<HTMLSpanElement> & {
235
+ sync?: UseSyncStatusResult;
236
+ source?: SourceRef;
237
+ }): react.JSX.Element | null;
238
+ interface UserButtonProps {
239
+ accounts?: UseAccountsResult;
240
+ auth?: UseAuthResult;
241
+ sync?: UseSyncStatusResult;
242
+ /** Show synchronization status in the account menu. Defaults to true. */
243
+ showSyncStatus?: boolean;
244
+ /** Defaults to enabled in sync mode. */
245
+ showSyncBadge?: boolean;
246
+ shape?: 'rounded' | 'square';
247
+ projectProfile?: UseProjectProfileResult;
248
+ /** Show the action to sign in to another account. Defaults to true. */
249
+ allowAddAccount?: boolean;
250
+ className?: string;
251
+ accountSettingsUrl?: string;
252
+ }
253
+ declare function UserButton(props: UserButtonProps): react.JSX.Element;
254
+ interface UserMenuProps extends UserButtonProps {
255
+ trigger?: 'avatar' | 'button';
256
+ avatarProps?: UserAvatarProps;
257
+ }
258
+ declare function UserMenu({ accounts: supplied, auth: suppliedAuth, sync, showSyncStatus, showSyncBadge, shape, trigger, avatarProps, projectProfile, allowAddAccount, className, accountSettingsUrl, }: UserMenuProps): react.JSX.Element;
259
+ interface BasicModalProps {
260
+ open?: boolean;
261
+ onOpenChange?: (open: boolean) => void;
262
+ finalFocus?: RefObject<HTMLElement>;
263
+ /** Supply a button element; null omits the trigger for controlled use. */
264
+ trigger?: ReactNode;
265
+ }
266
+ interface AccountSettingsModalProps extends BasicModalProps {
267
+ auth?: UseAuthResult;
268
+ accounts?: UseAccountsResult;
269
+ projectProfile?: UseProjectProfileResult;
270
+ sync?: UseSyncStatusResult;
271
+ accountSettingsUrl?: string;
272
+ children?: ReactNode;
273
+ }
274
+ declare function AccountSettingsModal({ auth: suppliedAuth, accounts: suppliedAccounts, accountSettingsUrl, projectProfile, sync, children, ...props }: AccountSettingsModalProps): react.JSX.Element;
275
+ interface SharesModalProps extends BasicModalProps {
276
+ auth?: UseAuthResult;
277
+ accounts?: UseAccountsResult;
278
+ sync?: UseSyncStatusResult;
279
+ shares?: UseOutgoingSharesResult;
280
+ /** Explicit least-privilege scope; the component never infers all tables. */
281
+ scope: CreateOutgoingShareInput['scope'];
282
+ repo?: CreateOutgoingShareInput['repo'];
283
+ }
284
+ declare function SharesModal({ auth: supplied, accounts: suppliedAccounts, sync, shares, scope, repo, ...props }: SharesModalProps): react.JSX.Element;
285
+
183
286
  /** Synchronous browser Storage adapted to core's key-value contract. */
184
287
  declare class BrowserKeyValueStorage implements KeyValueStorage {
185
288
  private readonly storage;
@@ -275,4 +378,4 @@ declare class PersistenceStore implements ReplicaStoreFactory {
275
378
  /** Browser multipart transport with upload progress, adapted from the Drive UI. */
276
379
  declare const browserUploadTransport: UploadTransportAdapter;
277
380
 
278
- export { type AccessTokenAdopter, BasicProvider, type BasicProviderProps, type BoundBasicProviderProps, type BrowserBasicConfig, BrowserKeyValueStorage, type BrowserLockManager, type BrowserMessageChannel, BrowserTokenStore, type CreateBasicConfig, type CreatedBasic, PersistenceStore, type PersistenceStoreOptions, type UseAccountsResult, type UseAuthResult, type UseBasicResult, type UseFilesResult, type UseMountsResult, type UseOutgoingSharesResult, type UseQueryResult, type UseReposResult, type UseStorageInfoResult, type UseSyncStatusResult, browserCurrentUrl, browserNavigate, browserReplaceUrl, browserStorage, browserUploadTransport, createBasic, createBasicClient, createBrowserAuthChannelFactory, createBrowserMessageChannel, useAccounts, useAuth, useBasic, useCollection, useDb, useFiles, useMounts, useOutgoingShares, useQuery, useRepos, useSchemaStatus, useStorageInfo, useSyncStatus };
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 };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { ReactNode, ReactElement } from 'react';
3
- import { BasicSchema, BasicConfig, BasicClient, BasicAccount, BasicProfile, ReadOnlyReason, AuthStatus, BasicError, AuthUser, BasicSyncStatus, BasicRejection, BasicConflict, SourceRef, SchemaStatus, BasicDb, Repo, FileRecord, MountFileInfo, StorageInfo, FileListQuery, MountInfo, MountHandle, OutgoingShareInfo, CreateOutgoingShareInput, MountsQuery, JsonObject, BasicRecord, Query, MountViewerFiles, OwnerFiles, TableNames, Collection, InferValue, KeyValueStorage, TokenStore, AuthChannelContext, AuthMessageChannel, ReplicaStoreFactory, StoragePartition, ReplicaStore, UploadTransportAdapter } from '@basictech/core';
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';
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. */
@@ -10,7 +10,9 @@ interface UseAccountsResult {
10
10
  accounts: BasicAccount[];
11
11
  activeAccount: BasicAccount | null;
12
12
  switchAccount(id: string): Promise<void>;
13
- addAccount(): Promise<BasicProfile>;
13
+ addAccount(options?: {
14
+ signIn?: boolean;
15
+ }): Promise<BasicProfile>;
14
16
  removeAccount(id: string): Promise<void>;
15
17
  }
16
18
  declare function useAccounts(): UseAccountsResult;
@@ -21,7 +23,7 @@ interface UseAuthResult {
21
23
  isAnonymous: boolean;
22
24
  canWrite: boolean;
23
25
  readOnlyReason: ReadOnlyReason | null;
24
- status: AuthStatus;
26
+ status: AuthStatus$1;
25
27
  /** Raw auth diagnostic; stable identity while its code and expired state are unchanged. */
26
28
  error: BasicError | null;
27
29
  user: AuthUser | null;
@@ -118,6 +120,19 @@ interface UseReposResult {
118
120
  }
119
121
  declare function useRepos(): UseReposResult;
120
122
 
123
+ interface AsyncResult<T> {
124
+ data: T;
125
+ isLoading: boolean;
126
+ error: BasicError | null;
127
+ refresh(): void;
128
+ }
129
+
130
+ interface UseProjectProfileResult extends AsyncResult<ProjectProfile | null> {
131
+ update(patch: ProjectProfilePatch): Promise<ProjectProfile>;
132
+ reset(): Promise<ProjectProfile>;
133
+ }
134
+ declare function useProjectProfile(enabled?: boolean): UseProjectProfileResult;
135
+
121
136
  type CreateBasicConfig<S extends BasicSchema> = BrowserBasicConfig<S> & {
122
137
  schema: S;
123
138
  };
@@ -147,6 +162,7 @@ interface CreatedBasic<S extends BasicSchema> {
147
162
  useSyncStatus(source?: SourceRef): UseSyncStatusResult;
148
163
  useSchemaStatus(source?: SourceRef): SchemaStatus;
149
164
  useRepos(): UseReposResult;
165
+ useProjectProfile(enabled?: boolean): UseProjectProfileResult;
150
166
  useFiles(query?: FileListQuery, options?: {
151
167
  source?: SourceRef;
152
168
  }): UseFilesResult;
@@ -180,6 +196,93 @@ declare function useCollection<V extends JsonObject = JsonObject>(name: string,
180
196
  source?: SourceRef;
181
197
  }): Collection<V>;
182
198
 
199
+ interface BasicAppearance {
200
+ theme?: 'light' | 'dark';
201
+ accent?: string;
202
+ radius?: string;
203
+ density?: 'compact' | 'comfortable';
204
+ }
205
+ /** Optional styling provider. CSS is opt-in via @basictech/react/styles.css. */
206
+ declare function BasicUIProvider({ appearance, children, }: {
207
+ appearance?: BasicAppearance;
208
+ children: ReactNode;
209
+ }): react.JSX.Element;
210
+ interface BasicButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
211
+ variant?: 'solid' | 'outline' | 'ghost';
212
+ }
213
+ declare const BasicButton: react.ForwardRefExoticComponent<BasicButtonProps & react.RefAttributes<HTMLButtonElement>>;
214
+ interface AuthButtonProps extends BasicButtonProps {
215
+ auth?: UseAuthResult;
216
+ input?: string;
217
+ }
218
+ declare function SignInButton(props: AuthButtonProps): react.JSX.Element;
219
+ declare function SignOutButton(props: AuthButtonProps): react.JSX.Element;
220
+ interface UserAvatarProps extends HTMLAttributes<HTMLSpanElement> {
221
+ profile?: (Pick<BasicProfile, 'name' | 'handle' | 'email' | 'picture'> & Partial<Pick<BasicProfile, 'kind'>>) | null;
222
+ size?: number;
223
+ showSyncBadge?: boolean;
224
+ sync?: UseSyncStatusResult;
225
+ accounts?: UseAccountsResult;
226
+ auth?: UseAuthResult;
227
+ projectProfile?: UseProjectProfileResult;
228
+ allowAddAccount?: boolean;
229
+ }
230
+ declare function UserAvatar({ accounts, auth, projectProfile, allowAddAccount, ...avatarProps }: UserAvatarProps): react.JSX.Element;
231
+ declare function AuthStatus({ auth: supplied, className, ...props }: HTMLAttributes<HTMLSpanElement> & {
232
+ auth?: UseAuthResult;
233
+ }): react.JSX.Element;
234
+ declare function SyncStatus({ sync: supplied, source, className, ...props }: HTMLAttributes<HTMLSpanElement> & {
235
+ sync?: UseSyncStatusResult;
236
+ source?: SourceRef;
237
+ }): react.JSX.Element | null;
238
+ interface UserButtonProps {
239
+ accounts?: UseAccountsResult;
240
+ auth?: UseAuthResult;
241
+ sync?: UseSyncStatusResult;
242
+ /** Show synchronization status in the account menu. Defaults to true. */
243
+ showSyncStatus?: boolean;
244
+ /** Defaults to enabled in sync mode. */
245
+ showSyncBadge?: boolean;
246
+ shape?: 'rounded' | 'square';
247
+ projectProfile?: UseProjectProfileResult;
248
+ /** Show the action to sign in to another account. Defaults to true. */
249
+ allowAddAccount?: boolean;
250
+ className?: string;
251
+ accountSettingsUrl?: string;
252
+ }
253
+ declare function UserButton(props: UserButtonProps): react.JSX.Element;
254
+ interface UserMenuProps extends UserButtonProps {
255
+ trigger?: 'avatar' | 'button';
256
+ avatarProps?: UserAvatarProps;
257
+ }
258
+ declare function UserMenu({ accounts: supplied, auth: suppliedAuth, sync, showSyncStatus, showSyncBadge, shape, trigger, avatarProps, projectProfile, allowAddAccount, className, accountSettingsUrl, }: UserMenuProps): react.JSX.Element;
259
+ interface BasicModalProps {
260
+ open?: boolean;
261
+ onOpenChange?: (open: boolean) => void;
262
+ finalFocus?: RefObject<HTMLElement>;
263
+ /** Supply a button element; null omits the trigger for controlled use. */
264
+ trigger?: ReactNode;
265
+ }
266
+ interface AccountSettingsModalProps extends BasicModalProps {
267
+ auth?: UseAuthResult;
268
+ accounts?: UseAccountsResult;
269
+ projectProfile?: UseProjectProfileResult;
270
+ sync?: UseSyncStatusResult;
271
+ accountSettingsUrl?: string;
272
+ children?: ReactNode;
273
+ }
274
+ declare function AccountSettingsModal({ auth: suppliedAuth, accounts: suppliedAccounts, accountSettingsUrl, projectProfile, sync, children, ...props }: AccountSettingsModalProps): react.JSX.Element;
275
+ interface SharesModalProps extends BasicModalProps {
276
+ auth?: UseAuthResult;
277
+ accounts?: UseAccountsResult;
278
+ sync?: UseSyncStatusResult;
279
+ shares?: UseOutgoingSharesResult;
280
+ /** Explicit least-privilege scope; the component never infers all tables. */
281
+ scope: CreateOutgoingShareInput['scope'];
282
+ repo?: CreateOutgoingShareInput['repo'];
283
+ }
284
+ declare function SharesModal({ auth: supplied, accounts: suppliedAccounts, sync, shares, scope, repo, ...props }: SharesModalProps): react.JSX.Element;
285
+
183
286
  /** Synchronous browser Storage adapted to core's key-value contract. */
184
287
  declare class BrowserKeyValueStorage implements KeyValueStorage {
185
288
  private readonly storage;
@@ -275,4 +378,4 @@ declare class PersistenceStore implements ReplicaStoreFactory {
275
378
  /** Browser multipart transport with upload progress, adapted from the Drive UI. */
276
379
  declare const browserUploadTransport: UploadTransportAdapter;
277
380
 
278
- export { type AccessTokenAdopter, BasicProvider, type BasicProviderProps, type BoundBasicProviderProps, type BrowserBasicConfig, BrowserKeyValueStorage, type BrowserLockManager, type BrowserMessageChannel, BrowserTokenStore, type CreateBasicConfig, type CreatedBasic, PersistenceStore, type PersistenceStoreOptions, type UseAccountsResult, type UseAuthResult, type UseBasicResult, type UseFilesResult, type UseMountsResult, type UseOutgoingSharesResult, type UseQueryResult, type UseReposResult, type UseStorageInfoResult, type UseSyncStatusResult, browserCurrentUrl, browserNavigate, browserReplaceUrl, browserStorage, browserUploadTransport, createBasic, createBasicClient, createBrowserAuthChannelFactory, createBrowserMessageChannel, useAccounts, useAuth, useBasic, useCollection, useDb, useFiles, useMounts, useOutgoingShares, useQuery, useRepos, useSchemaStatus, useStorageInfo, useSyncStatus };
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 };