Move docs/ package mirror into document-navigator plugin

Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 01:10:00 +01:00
parent fe3fedf42e
commit 9bf56a806b
6022 changed files with 12437 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
# `<FileAccessList />`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`<FileAccessList />` renders a read-only list of share recipients with
an optional revoke action per row. It is the lightweight surface used
inside file detail panels and partition sidebars where the full
`<ShareDialog />` would be overkill. The component is purely
presentational — recipient data flows in via `props.recipients` and
mutations exit via the `onRevoke(grantId)` callback. There is no
internal state, no engine dependency, and no async lifecycle.
## Features
- Renders one `<li>` per recipient with `data-grant-id` and
`data-revoked` attributes for external styling and scripting.
- Permission label rendered from the `primaryPermission` projection
computed by `useSharing` (no recompute here).
- Optional `onRevoke` — when omitted, the revoke button is hidden and
the component is strictly read-only.
- Keyboard accessibility: revoke button is a native `<button>`, plus
Delete / Backspace shortcuts trigger revoke when the button is
focused.
- Empty state: when `recipients` is empty, renders a polite
`aria-live="polite"` status message rather than an empty list.
## Usage: Basic Example
```tsx
import { FileAccessList } from "@suaveplan/drive-react-sharing";
<FileAccessList recipients={sharing.recipients} />;
```
## Usage: Advanced Example
```tsx
function Panel() {
const sharing = useSharing(/* ... */);
return (
<FileAccessList
recipients={sharing.recipients}
onRevoke={(id) => sharing.removeRecipient(id)}
revokeLabel="Remove"
emptyLabel="No collaborators yet"
listLabel="Document collaborators"
/>
);
}
```
## API Reference
- `FileAccessListProps`:
- `recipients: readonly ShareRecipient[]` — required.
- `onRevoke?: (grantId: string) => void` — optional revoke callback.
- `revokeLabel?: string` — defaults to `"Revoke"`.
- `emptyLabel?: string` — defaults to `"No recipients"`.
- `listLabel?: string` — defaults to `"Recipients with access"`.
- `className?: string`, `style?: CSSProperties` — root overrides.
- `FileAccessList(props): ReactElement` — pure functional component.
## Implementation Notes
The component intentionally renders a `<button>` rather than a
right-click context menu so revoke is reachable via keyboard alone.
The Delete / Backspace shortcuts fire `onRevoke` directly without
requiring a confirmation step — consumers that need a confirmation
flow should compose their own modal in front of the callback.

View File

@@ -0,0 +1,69 @@
# `<PartitionBadge />`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`<PartitionBadge />` renders a compact label visualising a partition's
classification — `personal`, `shared`, `public`, or `encrypted` — plus
an optional padlock indicator when the partition is encrypted. It is a
pure presentational component with no engine dependencies, suitable for
list rows, sidebar headers, and breadcrumbs.
## Features
- Four partition classifications matching the engine's `PartitionType`
union.
- Automatic padlock SVG when `type === "encrypted"` or `encrypted` prop
is explicit-true.
- Inline-styled by default so the component drops into any tree without
a stylesheet. `className` / `style` props override.
- Accessibility: `role="status"`, `aria-label` mentioning the type and
encryption state, decorative SVG marked `aria-hidden="true"`.
## Usage: Basic Example
```tsx
import { PartitionBadge } from "@suaveplan/drive-react-sharing";
<PartitionBadge type="shared" />;
```
## Usage: Advanced Example
```tsx
<PartitionBadge
type="encrypted"
label="E2E"
className="my-badge"
style={{ marginInlineStart: 12 }}
/>
```
`encrypted` forces the lock icon even when the type is something other
than `encrypted` — useful for representing a shared partition whose
policy mandates encryption.
```tsx
<PartitionBadge type="shared" encrypted />
```
## API Reference
- `PartitionBadgeProps`:
- `type: PartitionType` — required, drives label + colour.
- `encrypted?: boolean` — optional, forces lock icon.
- `label?: string` — optional, overrides the default text.
- `className?: string` — applied to root `<span>`.
- `style?: CSSProperties` — merged onto default style.
- `PartitionBadge(props): ReactElement` — pure functional component.
## Implementation Notes
Colours are hard-coded HEX values with a 10% alpha background, chosen
to ensure 4.5:1 WCAG contrast against the colour foreground. Consumers
that require theme-aware colouring should pass an explicit `style`
override rather than monkey-patching the defaults. The `data-partition-type`
attribute is set so external stylesheets can target each variant
without depending on the inline `style` prop.

View File

@@ -0,0 +1,108 @@
# `<ShareDialog />`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`<ShareDialog />` renders the modal sharing UI for a partition. It
exposes a recipients list, an add-recipient form (text input plus
permission select), and a revoke button per row. The component is
controlled — `open`, `recipients`, `loading`, and `errorMessage` are
all owned by the parent. Wire it to `useSharing` for live engine
integration or hand it static props in Storybook.
## Features
- Controlled `open` prop — renders `null` when closed so callers can
conditionally mount.
- Focus moves to the recipient input on open.
- ESC key closes the dialog. Backdrop click also closes; clicks
inside the dialog do NOT close (stopPropagation).
- ARIA: `role="dialog"`, `aria-modal="true"`, `aria-labelledby` tied to
the title heading. Error region uses `role="alert"`.
- Recipient list reuses the same `data-grant-id` row contract as
`<FileAccessList />`.
- Localisable: pass `labels` to override every string. Permission
options localise individually.
## Usage: Basic Example
```tsx
import { ShareDialog } from "@suaveplan/drive-react-sharing";
import { useState } from "react";
function Trigger() {
const sharing = useSharing(/* ... */);
const [open, setOpen] = useState(false);
return (
<>
<button type="button" onClick={() => setOpen(true)}>Share</button>
<ShareDialog
open={open}
recipients={sharing.recipients}
onAdd={(id, p) => sharing.addRecipient(id, p)}
onRemove={(id) => sharing.removeRecipient(id)}
onClose={() => setOpen(false)}
/>
</>
);
}
```
## Usage: Advanced Example
Custom labels for a localised application:
```tsx
<ShareDialog
open={open}
recipients={sharing.recipients}
loading={sharing.loading}
errorMessage={sharing.error?.message}
defaultPermission="write"
onAdd={(id, p) => sharing.addRecipient(id, p)}
onRemove={(id) => sharing.removeRecipient(id)}
onClose={() => setOpen(false)}
labels={{
title: "Partager",
add: "Ajouter",
revoke: "Révoquer",
empty: "Aucun destinataire",
permissionOptions: {
read: "Lecture",
write: "Écriture",
delete: "Suppression",
share: "Partage",
admin: "Administrateur",
},
}}
/>;
```
## API Reference
- `ShareDialogProps`:
- `open: boolean` — required.
- `recipients: readonly ShareRecipient[]` — required.
- `onAdd(recipientId, permission): void | Promise<void>` — required.
- `onRemove(grantId): void | Promise<void>` — required.
- `onClose(): void` — required.
- `title?: string` — overrides `labels.title`.
- `loading?: boolean` — disables interactive controls.
- `errorMessage?: string` — renders `role="alert"` banner.
- `defaultPermission?: SharePermission` — initial select value.
- `labels?: Partial<ShareDialogLabels>` — string overrides.
- `ShareDialogLabels` — exported interface; permission options
override individually.
## Implementation Notes
The dialog is intentionally a controlled component because the open
state is usually tied to other UI in the parent (an avatar stack, a
"Share" button, a routing flag). Storybook can drive the entire surface
with mocked callbacks. The error region is rendered only when
`errorMessage` is a non-empty string — passing an undefined or
zero-length string hides it. The submit button is disabled when the
recipient draft is empty so users cannot accidentally fire `onAdd`
with whitespace.

View File

@@ -0,0 +1,84 @@
# `drive-react-sharing` error codes
## Purpose
`@suaveplan/drive-react-sharing` raises every failure as a
`DriveReactSharingError` — a `SuaveplanError` subclass with a stable code
registry. Codes are namespaced under `DRIVE_REACT_SHARING.*` so consumers
can write exhaustive `switch (err.code) { ... }` matches at the call site
without depending on the underlying `@suaveplan/drive-sharing` engine
codes. The actual engine error (or any other thrown value) is preserved
on `cause` so a chain walker can still surface low-level diagnostics.
## Features
- Module-load-time registration with the global Suaveplan code registry.
- Brand symbol on every instance — defence-in-depth against cross-realm
`instanceof` failures via `DriveReactSharingError.is(value)`.
- Seven codes covering input validation (`INVALID_RECIPIENT`), engine
authorization failures (`PERMISSION_DENIED`, `GRANT_NOT_FOUND`), and
hook action wrapping (`ADD_FAILED`, `REVOKE_FAILED`, `UPDATE_FAILED`,
`REFRESH_FAILED`).
- Compatible with the `SuaveplanError.cause` chain so the original
`DriveShareError` thrown by the engine is recoverable.
## Usage: Basic Example
```ts
import {
DriveReactSharingError,
DRIVE_REACT_SHARING_CODES,
} from "@suaveplan/drive-react-sharing";
try {
await result.addRecipient("", "read");
} catch (err) {
if (DriveReactSharingError.is(err)) {
if (err.code === DRIVE_REACT_SHARING_CODES.INVALID_RECIPIENT) {
// Show inline form validation error
}
}
}
```
## Usage: Advanced Example
Wrapping an engine-level error and walking the cause chain:
```ts
import {
DriveReactSharingError,
DRIVE_REACT_SHARING_CODES,
} from "@suaveplan/drive-react-sharing";
import { DriveShareError } from "@suaveplan/drive-sharing";
try {
await result.addRecipient("user-42", "admin");
} catch (err) {
if (
DriveReactSharingError.is(err) &&
err.code === DRIVE_REACT_SHARING_CODES.ADD_FAILED &&
err.cause instanceof DriveShareError
) {
console.error("engine code:", err.cause.code);
}
}
```
## API Reference
- `DRIVE_REACT_SHARING_CODES` — frozen record of every code literal.
- `DriveReactSharingErrorCode` — union type of every code value.
- `class DriveReactSharingError extends SuaveplanError<DriveReactSharingErrorCode>`
- `new DriveReactSharingError(code, messageKey, options?)` — `options.cause`
preserves the original error.
- `static is(value): value is DriveReactSharingError` — brand-aware guard.
## Implementation Notes
The brand symbol is unique per module evaluation; consumers MUST NOT
construct a `DriveReactSharingError` outside this package (the
constructor is exported only to allow shared error helpers in this
workspace to throw it). The seven codes are intentionally split into
input-validation versus action-wrapper buckets so UI can render
field-level messages distinct from optimistic-update rollback toasts.

View File

@@ -0,0 +1,126 @@
# `useSharing` hook
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`useSharing` is the React-tier binding for `@suaveplan/drive-sharing`. It
holds a local list of `ShareRecipient` rows that mirror the active grants
on a partition, and exposes four async actions (`addRecipient`,
`removeRecipient`, `updatePermission`, `refresh`) that delegate to the
real engine — no in-memory stub, no mocks. After every successful action
the hook re-syncs from `GrantManager.list({ partitionId, active: true })`
so optimistic UI never drifts from the engine's truth.
## Features
- Wires directly to live `PartitionManager`, `GrantManager`, and
`TokenManager` instances supplied by the caller. The package does not
own a partition store.
- AbortSignal threaded through every action. A pre-aborted signal raises
a `DriveReactSharingError` before any engine mutation.
- Surfaces failures via both `result.error` (for inline rendering) and
`throw` (for promise-chain orchestration). Underlying engine errors
are preserved on `cause`.
- Emits three OTel counters: `drive.react.sharing.recipients_added_total`,
`drive.react.sharing.recipients_removed_total`,
`drive.react.sharing.permissions_updated_total{permission}`.
- Empty-string recipient ids raise `INVALID_RECIPIENT` synchronously.
- Permission update is a revoke-then-create round-trip because the engine
has no atomic mutate API on `GrantManager`.
## Usage: Basic Example
```tsx
import {
GrantManager,
InMemoryPartitionStore,
KeyRing,
PartitionManager,
TokenManager,
userId,
} from "@suaveplan/drive-sharing";
import { useSharing } from "@suaveplan/drive-react-sharing";
const partitions = new PartitionManager({
store: new InMemoryPartitionStore(),
keyRing: new KeyRing(),
});
const grants = new GrantManager();
const tokens = new TokenManager();
function PanelWidget({ partitionId }: { partitionId: string }) {
const sharing = useSharing({
partitionId,
grantorId: userId("alice"),
partitions,
grants,
tokens,
});
return (
<button
type="button"
onClick={() => sharing.addRecipient("bob", "read")}
>
Add bob
</button>
);
}
```
## Usage: Advanced Example
Composing with `AbortController` so a parent dialog can cancel inflight
mutations on close:
```tsx
import { useEffect } from "react";
function Composer({
partitionId,
sharing,
}: {
partitionId: string;
sharing: ReturnType<typeof useSharing>;
}) {
useEffect(() => {
const ctrl = new AbortController();
return () => ctrl.abort();
}, [partitionId]);
async function bulkPromote(ids: readonly string[]): Promise<void> {
const ctrl = new AbortController();
for (const id of ids) {
await sharing.updatePermission(id, "admin", ctrl.signal);
}
}
return null;
}
```
## API Reference
- `useSharing(options: UseSharingOptions): UseSharingResult`
- `options.partitionId: string` — partition the grants belong to.
- `options.grantorId: UserId` — typically the current user.
- `options.partitions: PartitionManager` — engine partition manager.
- `options.grants: GrantManager` — engine grant manager.
- `options.tokens: TokenManager` — engine token manager.
- `options.initialRecipients?: readonly ShareRecipient[]` — optional
SSR / Storybook seed; when omitted the hook seeds from
`grants.list({ partitionId, active: true })`.
- Returns `UseSharingResult` — `{ recipients, loading, error,
addRecipient(id, perm, signal?), removeRecipient(grantId, signal?),
updatePermission(grantId, perm, signal?), refresh(signal?) }`.
## Implementation Notes
The recipient list is refreshed by re-reading from the engine after
every mutation rather than spliced in place. This keeps the hook free
of merge-conflict logic when multiple components share the same
`GrantManager`. `primaryPermission` on each row is computed at projection
time from a fixed tier ranking (`read < write < delete < share <
admin`); consumers should still consult the full `permissions` array
when making entitlement decisions, but UI badges can render the primary
tier directly.

View File

@@ -0,0 +1,75 @@
# OTel instruments
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`@suaveplan/drive-react-sharing` emits three counters covering every
mutation the `useSharing` hook can dispatch. They are useful both for
operational dashboards (how often are admins adding versus removing
recipients?) and for product analytics (which permission tier is
trending?). Instruments are created lazily so this module is safe to
import inside a tree-shaken bundle even when the consumer never
configures a `MeterProvider`.
## Features
- Lazy instrument creation — first `getDriveReactSharingInstruments()`
call resolves the meter and caches the counter triple.
- `resetDriveReactSharingInstruments()` clears the cache so tests can
swap a fresh meter provider per case without leaking instruments.
- Counter names follow the `drive.react.sharing.*_total` convention so
Prometheus / OTLP backends can aggregate the family cheanly.
## Usage: Basic Example
```ts
import { getDriveReactSharingInstruments } from "@suaveplan/drive-react-sharing";
const { recipientsAddedTotal } = getDriveReactSharingInstruments();
recipientsAddedTotal.add(1, { partition: "shared" });
```
## Usage: Advanced Example
Swap meter providers between integration test cases:
```ts
import { metrics } from "@opentelemetry/api";
import { resetDriveReactSharingInstruments } from "@suaveplan/drive-react-sharing";
afterEach(() => {
metrics.disable();
resetDriveReactSharingInstruments();
});
```
## API Reference
- `interface DriveReactSharingInstruments` — `{ recipientsAddedTotal, recipientsRemovedTotal, permissionsUpdatedTotal }`.
- `getDriveReactSharingInstruments(): DriveReactSharingInstruments` — lazy accessor.
- `resetDriveReactSharingInstruments(): void` — test-only reset.
## Implementation Notes
The instruments are deliberately Counters (not Histograms) because each
event is a single mutation with no associated duration distribution.
Permission updates carry a `permission` attribute so dashboards can
distinguish `read` / `write` / `admin` trends; the other counters carry
no attributes by default so cardinality remains bounded.
Lazy creation matters because every project consumer that pulls in the
React-tier sharing surface should pay zero instrument overhead until it
actually mutates a grant. The cache lives at module scope so the first
caller materialises the counters and every subsequent caller pays only
a single property lookup. Tests must call `resetDriveReactSharingInstruments`
between cases when they swap the global `MeterProvider`; otherwise the
cached counters retain references to the prior provider's meter and
new metric points never surface in the swapped backend.
The decision to scope updates by `permission` attribute rather than by
recipient id keeps cardinality at exactly five values matching the
permission union (`read` / `write` / `delete` / `share` / `admin`),
which is friendly to Prometheus label cardinality budgets and avoids
the runaway-tag pitfall common in user-id-attributed counters.

View File

@@ -0,0 +1,79 @@
# Canonical sharing types
## Purpose
This module is the single source of truth for the React-tier sharing
type surface. It re-exports the canonical `SharePermission`,
`PartitionType`, `Grant`, `UserId`, and `PartitionId` types directly
from `@suaveplan/drive-sharing` — the engine package owns the literal
sets — and adds three projection types (`ShareRecipient`, `AccessEntry`,
`UseSharingOptions` / `UseSharingResult`) tailored to the UI surface.
On main, three overlapping permission names existed: `SharePermission`,
`AccessPermission`, and `SharingPermission`. suaveplan consolidates these
into the engine-owned `SharePermission` and refuses to ship a second
alias.
## Features
- Single canonical re-export of `SharePermission` covering every
permission tier accepted by the engine (`read | write | delete | share | admin`).
- `ShareRecipient` UI projection — flattens an engine `Grant` plus
primary permission for badge rendering.
- `AccessEntry` — read-only row used by `<FileAccessList />`.
- `UseSharingOptions` / `UseSharingResult` — wire the hook to live
engine instances (`PartitionManager`, `GrantManager`, `TokenManager`)
without forcing consumers to import three packages.
## Usage: Basic Example
```ts
import type {
SharePermission,
ShareRecipient,
} from "@suaveplan/drive-react-sharing";
const tier: SharePermission = "write";
function render(recipients: readonly ShareRecipient[]): void {
/* ... */
}
```
## Usage: Advanced Example
```ts
import type {
UseSharingOptions,
UseSharingResult,
} from "@suaveplan/drive-react-sharing";
function buildOptions(
partitionId: string,
grantor: UseSharingOptions["grantorId"],
deps: Pick<UseSharingOptions, "partitions" | "grants" | "tokens">
): UseSharingOptions {
return { partitionId, grantorId: grantor, ...deps };
}
function summarize(result: UseSharingResult): string {
return `${result.recipients.length} recipients (${result.loading ? "loading" : "idle"})`;
}
```
## API Reference
- `type SharePermission = "read" | "write" | "delete" | "share" | "admin"`
(re-exported from `@suaveplan/drive-sharing`).
- `type PartitionType = "personal" | "shared" | "public" | "encrypted"`.
- `interface ShareRecipient` — UI projection of a `Grant`.
- `interface AccessEntry` — read-only access-list row.
- `interface UseSharingOptions` — `{ partitionId, grantorId, partitions, grants, tokens, initialRecipients? }`.
- `interface UseSharingResult` — `{ recipients, loading, error, addRecipient, removeRecipient, updatePermission, refresh }`.
## Implementation Notes
`ShareRecipient.primaryPermission` is the highest tier present in the
flat `permissions` array — chosen for badge colour selection. The hook
computes this when constructing recipients from the engine, so consumers
should treat the field as authoritative for display purposes but still
consult the full `permissions` array for entitlement decisions.