docblocks-react-ui 0.1.1 → 0.3.0

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,201 @@
1
1
  # docblocks-react-ui
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 5f5ab54: The presence selection's visibility over the room stands grant-gated (the
8
+ privacy stand): the `EditorProvider`'s additive `presence` prop carries
9
+ the local user's identity (`userId` / the optional `name`) and the
10
+ optional `PresenceCrypto` cipher; the new `usePresenceSharing()` hook
11
+ drives the local grant — the session-state default is **off** (the
12
+ selection does not publish, only the light identity frame the roster
13
+ needs), and the consumer flips it to `everyone` (the plaintext wire
14
+ shape, the legacy stand) or to a targeted user-id list (the selection
15
+ publishes as opaque ciphertext entries, one per grant member — the
16
+ entries carry no target id, a recipient opens only the entry addressed
17
+ to it, an unaddressed peer sees the sharer's identity but never the
18
+ selection). The library orchestrates the publish gating, the receive's
19
+ decrypt, and the per-peer sequence guard over the async decrypt; the
20
+ cryptography and the sharing's UI are the consumer's stand (the package
21
+ ships no built-in control). A provider without the `presence` prop keeps
22
+ the legacy stand (the anonymous plaintext selection over the room).
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies [5971d30]
27
+ - docblocks-core@0.1.3
28
+
29
+ ## 0.2.0
30
+
31
+ ### Minor Changes
32
+
33
+ - bcc306e: The `Editor`'s page view is now the motion row layer (the former
34
+ `MotionEditor`'s merge): every page block is its own `Reorder.Item` row —
35
+ the drag-reorder (one drag = one range move of the dragged block's
36
+ derived subtree), the insert/remove's layout animation, the rendered
37
+ list marker per row. Consumers rendering `<Editor>` get the animated,
38
+ draggable page without a code change; the `view` / `formatPalette` props
39
+ stand. The `MotionEditor` export and the `./motion` subpath are removed
40
+ (the base `Editor` is the motion editor — the previous split stood the
41
+ base entry without any reorder path, so a consumer taking it got a
42
+ non-reorderable editor).
43
+
44
+ The three peers are now REQUIRED (pre-1.0 breaking): `framer-motion`
45
+ (the page view's row layer), `mermaid` (the `./mermaid` subpath), and
46
+ `pdfjs-dist` (the `./pdf` subpath) lose their `optional: true` — the
47
+ install must carry all of them. The `./mermaid` and `./pdf` subpaths'
48
+ wiring stays opt-in (the `pdfPreview` prop / the `code` block renderer);
49
+ the required peers guarantee availability only. The static row rendering
50
+ stands for the table cells and the canvas's page miniature (the
51
+ `BlockGroups`'s stand); the `BlockView`/`BlockNodeView` renderers are
52
+ unchanged.
53
+ - d62b662: The editor's UI strings stand localized (en + ru). The language is
54
+ consumer-driven: the package reads the `<html lang>` attribute (the `ru*`
55
+ prefix stands the Russian stand; the absent or unknown language falls
56
+ back to English) and follows the attribute's change at runtime. There is
57
+ no `locale` prop.
58
+
59
+ The package runs its own `i18next` instance over the shipped catalogs —
60
+ `i18next` and `react-i18next` are new runtime dependencies — under the
61
+ `EditorProvider`'s subtree (and the consumer-placed `DocHeader`): the
62
+ consumer's global `i18next` singleton stands untouched, and the editor's
63
+ `useTranslation` never conflicts with it. The English's values keep the
64
+ former's literals as-is (the zero visual diff over the English's
65
+ language).
66
+
67
+ Not localized (the technical stand): the code language's names, the font
68
+ family's and weight's names, the avatar emoji picker's accessible names,
69
+ and the technical glyphs. Parameterized strings use the `{param}`
70
+ interpolation (the single braces' stand).
71
+ - 0e798be: The page row's delete affordance: the hover's trash icon at the row's
72
+ right edge (every page type — the text rows' own wrapper, the non-text
73
+ rows' row-level stand over the motion view; the edit's mode only).
74
+ The deletion was previously discoverable only through the less obvious
75
+ path (the gutter's select + the Delete key). The click is a true
76
+ deletion (the Delete key's image special case — the in-place conversion
77
+ to an empty paragraph — stands for the key gesture only), the row's
78
+ whole blocks selection is deleted when the row is part of one (the
79
+ Delete key's gesture parity), and the gesture stands the document
80
+ root's trailing-paragraph guarantee + the focus's parking as the Delete
81
+ key's path (the focus never drops to the document).
82
+ - 74e0abd: Add the standalone's database's surface (the consumer's own page, not a
83
+ document block): the `DatabaseTable` component (the live grid over the
84
+ bridge's table id — the same `useDatabaseTable`'s data stand as the
85
+ document block) + the `useTableList` hook (the `listTables`'s catalog
86
+ read + the `createTable` / `renameTable` mutations over the fresh's
87
+ `operationId` per op; the catalog's auto-refresh stands over the own's
88
+ successful mutations + the manual's `refresh`). The `DatabaseTable`'s
89
+ view's settings (`filter`, the `columnOrder`, the `hiddenColumns`, the
90
+ `columnWidths`) stand the internal's state seeded from the optional
91
+ `view` prop + persisted over the `onViewChange`'s patch callback (the
92
+ controlled's mode stands absent — one patch per change, as the block's
93
+ `updateBlock` stand). The `readOnly`'s stand gates the editing's paths
94
+ (the block's read-only's parity). The grid's `GridBody` stands extracted
95
+ from the document's context (the view's attrs' write is the callback's
96
+ stand — the block's wrapper keeps one `updateBlock` per change, no
97
+ behavior's diff).
98
+
99
+ ### Patch Changes
100
+
101
+ - a89733e: The external-form database's history and boundary stands:
102
+
103
+ - the undo / redo's in-flight window — a forward write that commits while
104
+ the undo's inverse (or the redo's forward) is in flight stands the
105
+ history's invariants: the undo's path consumes the undone op (popped from
106
+ the undo stack, not moved to the redo stack — the fresh write cleared the
107
+ redo side, and the re-commit over the newer entry would clobber it), and
108
+ the redo's path re-enters the op on the undo stack at the call's position
109
+ (below the window's writes, not on top — the undo order's inversion is
110
+ closed, the first undo reverts the newest write).
111
+ - the operation's rejections stand the structured `BridgeError` (the code's
112
+ retry / the clear's decision stands the first-class property, not the
113
+ `cause`'s dig): a rejection that carries no machine code stands the
114
+ transport-level `unavailable` (the safe recovery is the same-operationId's
115
+ reconciliation, not a blind new operation).
116
+ - the cell-value equality stands the element-wise comparison over the
117
+ composite value (the multi-select's array) — the identity stand committed
118
+ the no-op over the same content and pushed the undo entry.
119
+ - the enum's options catalog stands the full page set (the cursor's
120
+ continuation to the tail) over the single page's `limit: 200` — the
121
+ truncated catalog over 200 options stood the silent-ignored delete of the
122
+ truncated option over the commit's diff.
123
+ - the column form's commit failures stand the logged error + the catalog's
124
+ re-read (the partial stand over the move's second commit), the silent
125
+ catches are gone.
126
+ - ff90f80: The external-form database stand's hardening: the bridge tables' picker
127
+ guards the `listTables` rejection (the unhandled rejection stands the
128
+ logged error + the empty list stand), and the hook's undo / redo drops
129
+ the replaced entry by identity (the in-flight inverse over the history's
130
+ mutation no longer drops the fresh entry — the double-apply and the
131
+ stale restore are closed). The database JSDoc stands the actual
132
+ contract: the mixed paste's description is self-contained, and the lost
133
+ response's reconciliation is the consumer's stand over the bridge's
134
+ `getOperationStatus` (the UI's convergence is the rebaseline).
135
+ - 590f5e7: The editor root's focus-ring stand: the root is a focus park (the
136
+ drag's release and the gutter's delete park the focus there so the
137
+ keyboard's input stays scoped over the editor) and is reached
138
+ programmatically only (tabIndex -1) — the browser's default focus ring
139
+ stood the visible ring over the whole document after a drag's release.
140
+ The ring is suppressed (`outline: none` on the focused root); the focus
141
+ itself stays, and the individual controls keep their own focus styles.
142
+ - a1564a2: The embed and frame's render stands:
143
+
144
+ - the sync-embed's stale's dynamic stand — the ref's block removed from the
145
+ master over the embed's mount now stands the broken's transition (the
146
+ master's change re-stands the block to the render-time's check; the
147
+ master's undo of the removal stands the resolved's back stand).
148
+ - the canvas frame's window references stand memoized (the frame's drag
149
+ re-renders the frame on every pointermove — the fresh array / Set
150
+ references broke the window's copies' memo and re-rendered every copy
151
+ over the drag's pass; the page-frame block's window stands the same
152
+ stand).
153
+ - the pdf worker's asset stands the degradation's signal: the unreachable
154
+ worker's asset (the bundler's rewritten reference, the hosting's dropped
155
+ asset) stands the logged warning over the silent native-viewer's
156
+ fallback.
157
+ - the `docblocks-core`'s peer range stands `^0.1.0` (the required peer's
158
+ version stand over the `*` — the packages stand lockstep over the 0.x
159
+ case, the breaking core's minor stands the range's bump over the same
160
+ release).
161
+ - ecb2a91: The paste's markdown link re-gate stands the mid-string control's
162
+ degrade: an href with a tab / LF / CR between the scheme's letters
163
+ (`java<tab>script:…`) no longer passes the gate — the check runs over
164
+ the WHATWG URL parser's full input normalization (the control is
165
+ removed from any position before the scheme detection, the browser's
166
+ parity). The run degrades to plain text over the unsafe href (the core's
167
+ gate is the single stand; this package's mirror passes it through).
168
+ - d162a1a: The paste surface's hardening: the markdown paste's link stand re-checks
169
+ the href over the core's public `isSafeLinkHref` itself at both the raw
170
+ parse and the entity-decode pass (the trimmed form — the browser trims
171
+ the leading C0 controls before the scheme detection, so the parser's
172
+ mirror stands the writer's contract: the `<a href>` tag, the exposed
173
+ entity's scheme, and the entity-hidden leading C0 degrade the run to
174
+ plain text instead of aborting the multi-block paste over the core's
175
+ strict mark rejection); and the clipboard's HTML serializes the mark
176
+ values escaped (the hostile remote data's stand — the attribute break
177
+ over the third-party paste target is closed).
178
+ - f907ee1: The editor store and the flat page derivation stand the registry's
179
+ own-property lookups (the `constructor`, `toString`, ... names are
180
+ unknown types, not the inherited `Object.prototype` members): a hostile
181
+ type named by a prototype key stands absent in the page sequence (the
182
+ cross-minor's stand — no renderer lookup over the inherited member, no
183
+ render-error stand, the data preserved in the export), and the seed and
184
+ the diff share one reader's posture (the store's filter and the
185
+ `getBlock`'s own-property stand agree on the block's presence).
186
+ - ab836e5: The selector's escape stand: the three block-content lookups (the range
187
+ extend's target, the gesture's stale-restore focus, the format
188
+ toolbar's type-change exclude) build the `data-block-id` selector over
189
+ `CSS.escape` — the full parity with the package's other lookup points,
190
+ where a crafted block id (a quote / bracket) would otherwise stand a
191
+ `SyntaxError` from `querySelector` in the keyboard / gesture handlers.
192
+ - Updated dependencies [bf92634]
193
+ - Updated dependencies [b66e45e]
194
+ - Updated dependencies [c5a38d2]
195
+ - Updated dependencies [769c12a]
196
+ - Updated dependencies [72fdfd3]
197
+ - docblocks-core@0.1.2
198
+
3
199
  ## 0.1.1
4
200
 
5
201
  ### Patch Changes
package/README.md CHANGED
@@ -12,11 +12,14 @@ package renders the document and provides the editing surface
12
12
  npm i docblocks-react-ui docblocks-core
13
13
  ```
14
14
 
15
- Peer dependencies: `docblocks-core`, `react` (19), `react-dom`. Optional
16
- peers, used only by their subpaths: `framer-motion` (`./motion`), `mermaid`
17
- (`./mermaid`), `pdfjs-dist` (`./pdf`). The package's own runtime dependencies
18
- (zustand, the Radix primitives, the shared ui runtime set) are declared, so
19
- a plain `npm i` is the whole install.
15
+ Peer dependencies: `docblocks-core`, `react` (19), `react-dom`, and the
16
+ required peers `framer-motion` (the Editor's page view is the motion row
17
+ layer — the drag-reorder + the layout animations), `mermaid` (the
18
+ `./mermaid` subpath's renderer), and `pdfjs-dist` (the `./pdf` subpath's
19
+ preview) — the install must carry all of them (npm enforces the required
20
+ peers). The package's own runtime dependencies (zustand, the Radix
21
+ primitives, the shared ui runtime set) are declared, so a plain `npm i`
22
+ is the whole install.
20
23
 
21
24
  The package is ESM-only (`import`, not `require`); it stands Node
22
25
  `^22.22.2 || ^24.15.0 || >=26.0.0` (the `engines` field).
@@ -106,6 +109,7 @@ mention catalog). The full props list:
106
109
  | `accessLevel?` | The consumer's user access level (default `editor`): `viewer` forces the `published` mode (the current state is never rendered to a reader). |
107
110
  | `mode?` | The document mode (default `edit`): `view` is a read-only live document; `published` renders the `publishedDoc` snapshot read-only. |
108
111
  | `publishedDoc?` | The published snapshot (a hydrated core `Doc`; must not change after mount — remount over `key` for a new snapshot). The consumer owns the publish action and the snapshot storage. |
112
+ | `presence?` | The local user's presence identity + the selection-sharing cipher (see Presence sharing below). Without the prop the editor keeps the legacy stand (the anonymous plaintext selection, always shared over the room). |
109
113
 
110
114
  Two rules for the optional props:
111
115
 
@@ -253,6 +257,62 @@ function App() {
253
257
  `updatedAt`); staleness (dropping stale peers) is a consumer concern —
254
258
  the payload's `updatedAt` (Unix ms) is the only input.
255
259
 
260
+ ### Presence sharing
261
+
262
+ The selection's visibility over the room is grant-gated (the privacy
263
+ stand): with the `presence` prop the grant defaults to **off** — the
264
+ selection does not publish, only the light identity frame (the roster). The
265
+ consumer flips the grant through the hook (session state — not document
266
+ data, not on the wire; it lives until the page reload or the provider's
267
+ unmount):
268
+
269
+ ```tsx
270
+ import { usePresenceSharing } from "docblocks-react-ui";
271
+
272
+ function ShareControl() {
273
+ const { sharing, setSharing, peers } = usePresenceSharing();
274
+ // sharing: "off" | "everyone" | string[] (the local grant — the full
275
+ // information stands over the sharer's pane: what is shared, and to whom)
276
+ // setSharing(mode) — a no-op over the read-only modes; a targeted grant
277
+ // requires the presence prop's userId + crypto (the degraded stand
278
+ // stands a console warn, no crash)
279
+ // peers: { peerId, userId?, name?, updatedAt, selectionVisible }[] —
280
+ // the live roster (the identity frames are always published)
281
+ return <button onClick={() => setSharing(sharing === "off" ? "everyone" : "off")}>{sharing}</button>;
282
+ }
283
+ ```
284
+
285
+ The grant's modes:
286
+
287
+ - **`off`** — the selection never publishes (the identity frame only).
288
+ - **`everyone`** — the plaintext `selection` (the legacy wire shape — the
289
+ peers of any version render it).
290
+ - **`[userId, …]`** (targeted) — the selection publishes as opaque
291
+ ciphertext entries (`selections`), one per grant member, produced by the
292
+ consumer's `PresenceCrypto` cipher. The entries carry **no target id**:
293
+ a recipient tries each entry with its own `decrypt`; an unaddressed peer
294
+ sees the sharer's identity in the roster but never the selection (and
295
+ never learns the audience).
296
+
297
+ ```ts
298
+ interface PresenceCrypto {
299
+ /** One opaque entry per grant member (grant order; no target id inside). */
300
+ encrypt(selection: PresenceSelection, grant: string[]): Promise<string[]>;
301
+ /** Null when the entry is not addressed to the local user (or broken). */
302
+ decrypt(entry: string): Promise<PresenceSelection | null>;
303
+ }
304
+ ```
305
+
306
+ The library orchestrates (publish gating, the debounce, the per-peer
307
+ sequence guard over the async decrypt, the roster) and renders the
308
+ accepted selections through the existing overlay; the cryptography and
309
+ the keys are the consumer's, and the sharing's UI (the toggle, the target
310
+ picker, the indicator) is the consumer's stand over the hook — the package
311
+ ships no built-in control. Without `crypto` the targeted mode degrades
312
+ (the setter stands a no-op); `off` / `everyone` stand regardless. A
313
+ provider **without** the `presence` prop keeps the legacy stand (the
314
+ anonymous plaintext selection over the room — the 0.2.0 behavior).
315
+
256
316
  ### Blobs
257
317
 
258
318
  The document holds only an opaque blob key (`image.src`); the bytes live
@@ -334,6 +394,49 @@ package root: `BridgeError` (the structured error type) with
334
394
  `isDatabaseBridge`, and `useDatabaseTable(bridge, tableId, filter?)` — the
335
395
  grid controller for a consumer-owned grid over the bridge.
336
396
 
397
+ ### Standalone database table
398
+
399
+ The database's UI stands outside any document as well — the
400
+ consumer-owned "tables section" (the user's tables management, not a
401
+ document block). The package ships the composable primitives; the
402
+ consumer assembles its own section (the list / the UX) over
403
+ `useTableList` and embeds the ready grid over the picked table:
404
+
405
+ ```tsx
406
+ import { DatabaseTable, useTableList } from "docblocks-react-ui";
407
+
408
+ function TablesSection({ bridge }: { bridge: DatabaseBridge }) {
409
+ const list = useTableList(bridge);
410
+ const [selectedId, setSelectedId] = useState<string | null>(null);
411
+ return (
412
+ <div>
413
+ {list.tables?.map((table) => (
414
+ <button key={table.id} onClick={() => setSelectedId(table.id)}>
415
+ {table.name}
416
+ </button>
417
+ ))}
418
+ {selectedId !== null && <DatabaseTable bridge={bridge} tableId={selectedId} />}
419
+ </div>
420
+ );
421
+ }
422
+ ```
423
+
424
+ - `DatabaseTable({ bridge, tableId, readOnly?, view?, onViewChange? })` —
425
+ the live grid over the bridge's table id (the same data stand as the
426
+ document block: the schema, the rows, the live events, the optimistic
427
+ cell layer). The view's settings (`filter`, `columnOrder`,
428
+ `hiddenColumns`, `columnWidths`) stand the internal state (seeded from
429
+ the optional `view` prop at mount); `onViewChange(patch)` receives one
430
+ patch per change for the consumer's persistence (the controlled mode
431
+ stands absent — the state is never external). `readOnly` gates the
432
+ editing's paths (the read-only block's parity).
433
+ - `useTableList(bridge)` — `{ tables, loading, error, refresh, create,
434
+ rename }`: the `listTables` catalog (the system tables stand out), the
435
+ `createTable` / `renameTable` mutations (the fresh `operationId` per op;
436
+ they reject with the `BridgeError`), and the catalog's re-read —
437
+ automatic over the hook's own successful mutations + manual over
438
+ `refresh` (the contract's catalog stands no push channel).
439
+
337
440
  ### Sync-embed
338
441
 
339
442
  A `sync-embed` block references another document by an opaque ref. The
@@ -382,7 +485,7 @@ const blockRenderers: BlockRendererMap = {
382
485
 
383
486
  `pdfPreview` is a component that renders a `pdf`-subtype image block's
384
487
  resolved blob as a client-side first-page preview. The `./pdf` subpath
385
- provides one over `pdfjs-dist` (an optional peer):
488
+ provides one over `pdfjs-dist` (a required peer):
386
489
 
387
490
  ```tsx
388
491
  import { PdfFirstPage } from "docblocks-react-ui/pdf";
@@ -432,6 +535,25 @@ insert menu, the drag / resize / nudge / fit / draw sessions) or
432
535
  programmatically through the core's block commands. The viewport's zoom
433
536
  and pan are view state — they never reach the document.
434
537
 
538
+ ### Localization
539
+
540
+ The editor's UI strings ship localized (en + ru). The language is
541
+ consumer-driven — the package reads the `<html lang>` attribute (the `ru*`
542
+ prefix stands the Russian stand; the absent / unknown language falls back
543
+ to English) and follows the attribute's change at runtime (the editor
544
+ re-renders over the flip). There is no `locale` prop — the document's
545
+ language is the consumer's, set on `<html>`.
546
+
547
+ The package runs its **own** `i18next` instance (the shipped catalogs,
548
+ `i18next` + `react-i18next` are runtime dependencies) under the
549
+ `EditorProvider`'s subtree — a consumer's global `i18next` singleton stands
550
+ untouched, and the editor's `useTranslation` never leaks into (or out of)
551
+ the consumer's setup.
552
+
553
+ Not localized (the technical stand): the code language names, the font
554
+ family / weight names, the avatar emoji picker's accessible names, and the
555
+ technical glyphs.
556
+
435
557
  ### Subtree view
436
558
 
437
559
  `rootBlockId` renders the entry list of one block instead of the document
@@ -460,6 +582,8 @@ value actually changes (the store's reference-stability contract):
460
582
  local state; `null` before the mount effect ran and after unmount).
461
583
  - `usePresence()` — the remote peers' ephemeral state (the selection +
462
584
  the `updatedAt` per peer).
585
+ - `usePresenceSharing()` — the selection-sharing grant (the session state
586
+ - the setter + the live roster; see Presence sharing).
463
587
  - `useMeta()` — the document meta (the reference is replaced only when
464
588
  the title changed).
465
589
  - `useEditorContext()` — the whole provider context (the store, the
@@ -474,8 +598,10 @@ value actually changes (the store's reference-stability contract):
474
598
  | `view` | `"page"` (default) or `"canvas"` — the rendering the consumer switches |
475
599
  | `formatPalette` | The format toolbar's color swatches (default `FORMAT_PALETTE`, exported) |
476
600
 
477
- `MotionEditor` (the `./motion` subpath) accepts the same props and adds the
478
- layout animations and drag-reorder (requires `framer-motion`).
601
+ The page view renders the flat page sequence — every page block is its
602
+ own row (the drag-reorder's pure framer `Reorder`, the insert/remove's
603
+ layout animation, the rendered list marker per row) — over the required
604
+ `framer-motion` peer.
479
605
 
480
606
  ## Subpath exports
481
607
 
@@ -484,12 +610,13 @@ layout animations and drag-reorder (requires `framer-motion`).
484
610
  | `docblocks-react-ui` | The editor, the provider, the hooks, the primitives (the entry) |
485
611
  | `docblocks-react-ui/styles.css` | The compiled stylesheet: the utility layer + the token seed + the `--docblocks-*` mapping + the structural rules |
486
612
  | `docblocks-react-ui/theme.css` | The pure token seed (standard tokens + the radius scale), no utility layer |
487
- | `docblocks-react-ui/motion` | `MotionEditor` (layout animations, insert/remove, drag-reorder; peer: `framer-motion`) |
488
613
  | `docblocks-react-ui/mermaid` | The `mermaid` renderer above `code { language: "mermaid" }` blocks (peer: `mermaid`) |
489
614
  | `docblocks-react-ui/pdf` | `PdfFirstPage` (the first-page preview over `pdfjs-dist`; the worker is copied to `dist/pdf/` at build time) |
490
615
 
491
- The base entry imports none of the optional peers, so consumers who skip a
492
- subpath ship no extra weight.
616
+ The `./mermaid` and `./pdf` subpaths' wiring is opt-in (the `pdfPreview`
617
+ prop / the `code` block renderer): a consumer who skips a subpath imports
618
+ nothing of its module. The peers themselves are required installs (the
619
+ page view's framer import included).
493
620
 
494
621
  ## Theming
495
622