@rakun-kit/manager-react 1.7.1 → 1.7.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.
Files changed (40) hide show
  1. package/README.md +448 -445
  2. package/dist/cjs/components/media/previews/Previews.js +66 -2
  3. package/dist/cjs/components/media/previews/Previews.js.map +1 -1
  4. package/dist/cjs/components/media/previews/dialogs/ExpandedPreviewDialog.js +12 -4
  5. package/dist/cjs/components/media/previews/dialogs/ExpandedPreviewDialog.js.map +1 -1
  6. package/dist/cjs/components/media/previews/hooks/useMediaPreviewRenderer.js +1 -1
  7. package/dist/cjs/components/media/previews/hooks/useMediaPreviewRenderer.js.map +1 -1
  8. package/dist/cjs/i18n/catalog.js +11 -0
  9. package/dist/cjs/i18n/catalog.js.map +1 -1
  10. package/dist/cjs/lib/media.js.map +1 -1
  11. package/dist/cjs/router/dashboard/[contentType]/[edit]/ContentTypeEdit.js +1 -1
  12. package/dist/cjs/router/dashboard/[contentType]/[edit]/ContentTypeEdit.js.map +1 -1
  13. package/dist/cjs/router/dashboard/[contentType]/[edit]/_fields/ListField/ListUI.js +1 -1
  14. package/dist/cjs/router/dashboard/[contentType]/[edit]/_fields/ListField/ListUI.js.map +1 -1
  15. package/dist/docs/index.md +429 -426
  16. package/dist/esm/components/media/previews/Previews.d.ts.map +1 -1
  17. package/dist/esm/components/media/previews/Previews.js +66 -2
  18. package/dist/esm/components/media/previews/Previews.js.map +1 -1
  19. package/dist/esm/components/media/previews/dialogs/ExpandedPreviewDialog.d.ts +5 -1
  20. package/dist/esm/components/media/previews/dialogs/ExpandedPreviewDialog.d.ts.map +1 -1
  21. package/dist/esm/components/media/previews/dialogs/ExpandedPreviewDialog.js +12 -4
  22. package/dist/esm/components/media/previews/dialogs/ExpandedPreviewDialog.js.map +1 -1
  23. package/dist/esm/components/media/previews/hooks/useMediaPreviewRenderer.d.ts.map +1 -1
  24. package/dist/esm/components/media/previews/hooks/useMediaPreviewRenderer.js +1 -1
  25. package/dist/esm/components/media/previews/hooks/useMediaPreviewRenderer.js.map +1 -1
  26. package/dist/esm/i18n/catalog.d.ts +33 -0
  27. package/dist/esm/i18n/catalog.d.ts.map +1 -1
  28. package/dist/esm/i18n/catalog.js +11 -0
  29. package/dist/esm/i18n/catalog.js.map +1 -1
  30. package/dist/esm/lib/media.d.ts +2 -0
  31. package/dist/esm/lib/media.d.ts.map +1 -1
  32. package/dist/esm/lib/media.js.map +1 -1
  33. package/dist/esm/router/dashboard/[contentType]/[edit]/ContentTypeEdit.d.ts.map +1 -1
  34. package/dist/esm/router/dashboard/[contentType]/[edit]/ContentTypeEdit.js +1 -1
  35. package/dist/esm/router/dashboard/[contentType]/[edit]/ContentTypeEdit.js.map +1 -1
  36. package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/ListField/ListUI.js +1 -1
  37. package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/ListField/ListUI.js.map +1 -1
  38. package/dist/styles.css +17 -2
  39. package/dist/tsconfig.build.tsbuildinfo +1 -1
  40. package/package.json +1 -1
@@ -1,426 +1,429 @@
1
- # `@rakun-kit/manager-react` AI usage manual
2
-
3
- This package is the publishable React manager, its transport clients,
4
- navigation abstraction, locale runtime, plugin API and CSS. Most Next.js apps
5
- should use `@rakun-kit/next/manager`; use this package directly for custom
6
- React hosts.
7
-
8
- Read the core manual at
9
- `node_modules/@rakun-kit/core/dist/docs/index.md` before changing manager/server
10
- contracts.
11
-
12
- ## Install and mount
13
-
14
- ```sh
15
- bun add @rakun-kit/manager-react react react-dom
16
- ```
17
-
18
- ```tsx
19
- import { ManagerBrowserApp, createHttpManagerClient } from '@rakun-kit/manager-react'
20
- import '@rakun-kit/manager-react/styles.css'
21
-
22
- const client = createHttpManagerClient({ baseUrl: '/api/rakun' })
23
-
24
- export function ManagerPage() {
25
- return (
26
- <ManagerBrowserApp client={client} pathname={window.location.pathname} basePath="/backend" />
27
- )
28
- }
29
- ```
30
-
31
- Import the stylesheet exactly once. `ManagerBrowserApp` owns browser-history
32
- navigation. Use `ManagerRuntimeApp` when the host supplies navigation.
33
-
34
- Built-in screens are split by manager route and loaded on first visit. Lucide
35
- names configured for content-type and module-picker icons remain unrestricted;
36
- the manager accepts kebab-case or PascalCase names and loads the selected icon
37
- on demand.
38
-
39
- ## Realtime synchronization
40
-
41
- The runtime loads `platform.realtime` from `manager.uiLocales` and selects
42
- polling or SSE without a separate client-side transport setting. Set
43
- `realtimeBaseUrl` to the manager API base URL so package-relative endpoints
44
- resolve correctly:
45
-
46
- ```tsx
47
- <ManagerBrowserApp
48
- client={client}
49
- realtimeBaseUrl="/api/rakun"
50
- pathname={window.location.pathname}
51
- />
52
- ```
53
-
54
- The default `sseRealtime()` metadata resolves to
55
- `<realtimeBaseUrl>/realtime`; core bootstrap configuration does not repeat the
56
- API base path.
57
-
58
- Core mutations publish stable topics for collaboration documents, locale
59
- variants, versions, comments, and notifications. `useManagerSyncQuery` and
60
- `useSync` invalidate their exact TanStack Query key after a matching event.
61
- The `syncIntervalMs` option is used only by the polling provider; SSE does not
62
- create periodic refresh timers. SSE multiplexes all active topics onto one
63
- `EventSource`, so opening multiple editors does not exhaust the browser's
64
- per-origin connection pool.
65
-
66
- Custom compositions may pass a `RealtimeMetadata` or `RealtimeProvider` to
67
- `ManagerProvider` / `ManagerAppProvider`. The root package exports
68
- `pollingRealtime`, `sseRealtime`, `realtimeFromMetadata`, `useSync`, and
69
- `useManagerSyncQuery`. Prefer the runtime bootstrap path so the core platform
70
- remains the source of truth.
71
-
72
- ## Clients and navigation
73
-
74
- - HTTP: `createHttpManagerClient` from `/client/http`.
75
- - tRPC: `createTrpcManagerClient` from `/client/trpc`.
76
- - Custom transport: `createManagerClient` from `/client/request`.
77
- - Router bridge: `createPathManagerNavigation` from `/state/navigation`.
78
-
79
- Hosts can use `preloadManagerPath` to prefetch built-in route chunks from their
80
- own router or link component.
81
-
82
- Use operation names and payloads from Rakun contracts; do not recreate endpoint
83
- types in host code. Keep `baseUrl` (API) distinct from `basePath` (manager UI).
84
-
85
- ## Link field picker
86
-
87
- The `f.link()` editor always shows Title and Destination inputs. A typed or
88
- pasted URL is stored as `{ href, title }`. Opening the destination picker
89
- exposes the home page and configured page-route types with their content type
90
- icons; selecting an item fills an empty title from its manager label and stores
91
- `{ routeId, contentTypeId, title }` so core can resolve localized paths.
92
- Persisted string values are normalized to `{ href, title: '' }` when loaded;
93
- untitled internal references also remain editable.
94
-
95
- Compound arrays such as `f.array(f.link())` render each value as a reorderable
96
- card in the Info editor. Use the drag handle to change their persisted order;
97
- current nested input values are preserved while an item is moved.
98
-
99
- `f.menu()` renders an ordered navigation tree for headers and similar modules.
100
- Each item reuses the internal/direct link picker and may contain unlimited child
101
- levels. Drag vertically to reorder; drag horizontally to nest or outdent. The
102
- level buttons expose equivalent keyboard actions. Removing a parent removes
103
- its complete subtree, and editor-only drag identifiers are never persisted.
104
-
105
- ## Media uploads
106
-
107
- The manager preserves Unicode original file names when uploading media. Names
108
- with accents, non-Latin scripts, or emoji are encoded into ASCII for the binary
109
- upload headers and restored by core before metadata is saved.
110
-
111
- The optimization popover has a Format selector for Image and Video settings.
112
- Images retain their output-format, quality, preview, and responsive-size
113
- controls. Videos expose their own quality and are always converted to both MP4
114
- and WebM for browser compatibility.
115
-
116
- When optimization is enabled, right-click an image or video and select
117
- `Reimport` to regenerate its optimized outputs. The action keeps the existing
118
- media ID, title, alt text, folder, and content references. You can also
119
- multi-select images and videos and use the selection toolbar reimport action.
120
- For an image, the same context menu offers `Replace image`. Choose a new image
121
- and confirm to replace the underlying file while keeping the media ID, details,
122
- folder and every reference to it across the site. Referencing pages are
123
- revalidated when the project has route revalidation configured.
124
- The image editor can crop to a rectangle or circle, rotate, and flip the saved
125
- copy horizontally or vertically.
126
- Optimization is enabled by default in the media library; generated image
127
- previews are stored as inline `data:image/...` strings on the media record for
128
- LQIP use.
129
-
130
- Gallery and file-field image previews use `@rakun-kit/react` `Image` with a
131
- layout-appropriate `sizes` value so responsive variants are selected when
132
- available.
133
-
134
- ## Manager plugins
135
-
136
- ```tsx
137
- import {
138
- defineRakunManagerPlugin,
139
- ManagerBrowserApp,
140
- } from '@rakun-kit/manager-react'
141
-
142
- const plugin = defineRakunManagerPlugin({
143
- id: '@acme/analytics',
144
- routes: [{
145
- id: 'dashboard',
146
- path: '/analytics',
147
- component: AnalyticsScreen,
148
- permissions: ['plugin.analytics.view'],
149
- }],
150
- sidebar: [{
151
- id: 'analytics',
152
- title: 'analytics.title',
153
- routeId: 'dashboard',
154
- position: 'primary',
155
- group: 'Plugins',
156
- }],
157
- })
158
-
159
- <ManagerBrowserApp plugins={[plugin]} {...props} />
160
- ```
161
-
162
- Plugins can contribute routes, sidebar entries, custom field editors and
163
- RichText extensions. Use exported plugin types from
164
- `@rakun-kit/manager-react/plugins`. Plugin ids, route ids, field editor ids,
165
- Lexical node types and RichText plugin ids must be unique.
166
-
167
- Dynamic Data behavior for custom editors comes from the encoded field
168
- `config.capabilities`, not from the editor id. `valueKind` controls source and
169
- target compatibility; `dynamic.properties` exposes object leaves;
170
- `mapProperties` renders them as separate mapping targets; `relation` enables
171
- nested content-type fields; and `collection` enables homogeneous or
172
- heterogeneous per-item mappings. A plugin editor therefore gains the standard
173
- Dynamic Data control by declaring its behavior in its core field factory.
174
-
175
- In Next.js, plugin registration belongs in a `'use client'` wrapper around
176
- `RakunManagerClientPage`; pass the wrapper to `RakunManagerPage` as
177
- `managerComponent`.
178
-
179
- ## Languages and copy
180
-
181
- English is built in. Additional server-provided UI packs come from
182
- `@rakun-kit/manager-locales` and are configured in core `managerLanguages`.
183
- Content editing locale and manager UI locale are separate states.
184
-
185
- Built-in manager copy must use the static manager translation catalog. Project
186
- labels may use arbitrary project keys. Fields resolve `field.<fieldName>` and
187
- layout slots resolve `layoutModule.<layoutKey>` with fallbacks.
188
-
189
- The authenticated sidebar places Visit site at the top-right of its logo block.
190
- It opens the validated SEO `siteUrl` in a new tab and falls back to `/` on the
191
- current manager host when the setting is empty. Custom dashboard layout
192
- renderers receive `siteUrl` in their layout props and should preserve an
193
- equivalent action. On a published routed document, the edit toolbar also shows
194
- View page for the exact selected language and locale variant. Draft, hidden,
195
- trashed, and route-less documents do not show that contextual action.
196
-
197
- ## Content and template tabs
198
-
199
- Saved content edit screens load an independent Yjs working document for the
200
- exact document `_id`. Field changes are synchronized incrementally over the
201
- manager HTTP client and the toolbar reports shared unsaved changes. Another
202
- editor on the same published document or draft sees that shared working state;
203
- an editor on a different draft uses a different room. Leaving and reopening the
204
- screen reloads the working state from the collaboration backend.
205
-
206
- The edit header shows one presence avatar per open browser tab, including
207
- multiple tabs owned by the same user. It keeps two avatars visible and collapses
208
- additional viewers into a `+N` badge; hovering or focusing the group lists every
209
- viewer and their focused field. Fields focused by another tab receive a visible
210
- highlight. Presence is ephemeral and disappears after a tab leaves or its
211
- connection expires. With SSE, the authenticated stream and its server heartbeat
212
- maintain that presence without periodic client-side collaboration sync calls;
213
- the polling provider refreshes it as part of its normal poll.
214
-
215
- Each collaborative room is also persisted locally with IndexedDB, isolated by
216
- manager user and room id. A previously opened document or shared Template loads
217
- its local Yjs state first and then exchanges updates with core. Edits made while
218
- the collaboration endpoint is unavailable remain in IndexedDB and are sent
219
- when connectivity returns; the toolbar reports that state as offline and
220
- locally stored. The first visit still needs the normal authenticated manager
221
- read to authorize and describe the document.
222
-
223
- Save first flushes pending Yjs updates and then asks core to persist a snapshot
224
- of the server-side working document. Until it succeeds, public/web reads and
225
- revalidation remain on the previous saved snapshot. Unsaved therefore means
226
- shared-but-not-committed, not changes owned by one browser. Draft promotion is
227
- still separate and uses the saved draft snapshot.
228
-
229
- The edit toolbar can discard changes through the shared confirmation dialog.
230
- Discard restores the last saved content snapshot for every editor; when the
231
- shared Template also has unsaved work, the confirmation states that it will be
232
- restored too. The save menu can save the current unsaved form as a separately
233
- named, unassigned draft variant copy or create an independent draft copy.
234
-
235
- Routeable documents keep the live preview visible as the main editing surface
236
- and collapse the manager's global navigation when opened. Editors can reopen
237
- that navigation from the normal header control. On wide screens, fields and the
238
- available Info, Content, Template, SEO, layout, variant, and history sections
239
- live in a resizable sidebar with compact vertical navigation and a preview-first
240
- initial split. Content and Template expose their module tree in an overlay
241
- navigator so it can be used for selection and reordering without permanently
242
- narrowing module forms. Module card actions use a separate wrapping row. On
243
- tablet-sized screens, the editor opens as an overlay while the preview remains
244
- the underlying canvas. On mobile, Rakun hides the preview, renders the editor as
245
- the main surface, and changes the section rail into a horizontally scrollable
246
- top bar.
247
-
248
- Opening the automatic preview reads the current working state without showing
249
- Save validation errors. Explicit preview refreshes and Save still validate the
250
- form. Existing relation selections remain available while their options load.
251
- Simple arrays share their field values directly; manager-only row identities
252
- are never included in collaborative or saved link arrays.
253
-
254
- IndexedDB persistence does not replace Save: local and shared CRDT updates are
255
- working state, while MongoDB and public/web reads change only after the server
256
- save operation succeeds. Save therefore requires connectivity.
257
-
258
- The shared Template tab also uses Yjs, but in a separate room keyed only by
259
- content type. Consequently, editors who opened different documents collaborate
260
- on the same template. Its changes remain shared but uncommitted until Save,
261
- which persists the template snapshot through the normal validated template
262
- flow. It is never copied into the current document's room.
263
-
264
- This scope intentionally excludes create-form content before its first save,
265
- route-layout modules, Settings, literals, users, comments, reviews, and other
266
- manager forms. The shared Template remains collaborative even while reached
267
- from a create form because it is an existing type-level resource. Automatic
268
- translation changes the content document's shared working state but does not
269
- save it.
270
-
271
- Variant creation, language assignment, promotion, trash, and restore remain
272
- validated server mutations rather than CRDT fields. While editors are open,
273
- the Variants tab and the editor's locale assignments refresh automatically, so
274
- a variant created by one user appears for the others without reloading the
275
- page.
276
-
277
- For a routeable content type with `iterator`, Content edits the `_iterator`
278
- modules unique to the current document. Template is enabled automatically and
279
- edits one shared composition for every document of that type. Both tabs offer
280
- the same configured iterator modules. The Template module picker additionally
281
- adds a built-in Content item at its root and inside blocks fields; it marks the
282
- exact position where the current document's Content modules are inserted.
283
-
284
- Template changes require the content type's `updateAny` permission and are
285
- saved with optimistic revision checks. Preview includes unsaved changes from
286
- both tabs.
287
-
288
- Fields configured with core `.help(text)` show a compact help icon beside their
289
- label. Activating it opens the guidance in a popover, including inside iterator
290
- and nested blocks modules. The manager resolves a translation key through its
291
- active locale and falls back to displaying the supplied string directly.
292
-
293
- ## Route SEO analysis
294
-
295
- The SEO tab of routeable content has separate Metadata and Analysis views.
296
- Generate report creates a temporary preview snapshot from the current form and
297
- shared template, then inspects the rendered page through the preview bridge.
298
- The report checks title and description lengths, H1 usage, heading order,
299
- missing image `alt` attributes, canonical URL, document language, `noindex`,
300
- Open Graph coverage, and rendered JSON-LD validity. It also renders
301
- representative Google and sharing previews plus the page's heading outline and
302
- the resolved JSON-LD blocks with their detected schema types. The indexability result uses the
303
- content's resolved SEO value and ignores `noindex` added solely to protect the
304
- temporary preview URL from indexing. When the canonical field is empty, Rakun
305
- derives it from Settings → SEO `siteUrl` and the resolved page route; the report
306
- identifies a missing `siteUrl` separately.
307
-
308
- SEO reports are advisory and do not claim to predict search rankings. Reports
309
- generated for saved documents are persisted with their date, score and rendered
310
- findings when the editor has `content.SeoSettings.own`; the site-wide SEO screen
311
- uses that history to show recent page reports. The document's Analysis view also
312
- lists its previous reports for the active language and route; selecting one
313
- restores the saved report, while Back to current report returns to the live
314
- result. Preview analysis requires the route to have manager preview configured.
315
- The preview adapter must support Rakun's SEO-analysis bridge messages; the
316
- official Next adapter does so automatically.
317
-
318
- The secondary sidebar exposes a separate SEO screen above the media library for
319
- users with SEO settings read access. Its Generate report action scans published
320
- documents from every readable routeable content type in the active language,
321
- checks stored metadata, custom Open Graph completeness and duplicate titles or
322
- descriptions, then persists a dated `SeoAudit` snapshot. A page without its own
323
- title or description uses the corresponding global default from Settings → SEO
324
- without a warning on the configured home page; other pages receive a fallback
325
- warning instead of a missing-metadata error. Previous site scores
326
- remain visible as a paginated evolution history; recent page reports use an
327
- independent bounded pagination. `SeoAudit` shares the `SeoSettings`
328
- permission resource, so `own` controls generation and `readAny` controls shared
329
- history visibility.
330
-
331
- Rakun's `StructuredData` iterator module can be placed in Content or a shared
332
- Template. Its typed fields support normal dynamic data mappings; the Custom
333
- variant accepts an arbitrary JSON object. Invalid custom JSON remains visible in
334
- the preview report as an error instead of disappearing silently.
335
-
336
- ## Dynamic data mappings
337
-
338
- List bindings use a type-aware mapping editor. When a mapped target property is
339
- itself a `blocks` field, choose `Nested list` to open another collection, query,
340
- filter, and mapping editor for that property. Nested query conditions can select
341
- `Current item` to read the parent source item at that level, or `Current
342
- document` to read the root document. This supports mappings such as Category ->
343
- gallery item -> Project -> image card, and the editor can repeat the flow for
344
- deeper block structures.
345
-
346
- Homogeneous arrays of links or relations can use either `Direct field` for the
347
- complete array or `Nested list` for per-item mapping. This also covers core
348
- `relation().multiple()` fields. Link items expose `title` and `href`, while
349
- relation items expose their target content type fields; their result remains a
350
- flat array rather than the heterogeneous `{ name, value }` shape used by
351
- `blocks`.
352
-
353
- Core `.min(count)` and `.max(count)` limits on `f.array(...)` and `.multiple()`
354
- relation, file, select, and content-reference fields appear below the manager
355
- control with its live item count. The control reports minimum/maximum validation
356
- errors and prevents adding or confirming items beyond the maximum.
357
-
358
- Target link fields are expanded into visible `<field>.title` and `<field>.href`
359
- rows. Each property can select its own compatible source and core reconstructs
360
- the mapped link object.
361
-
362
- In the SEO tab, `Current document` exposes compatible fields from the complete
363
- document (including fields edited in Info), while the reserved `_seo` relation
364
- itself stays excluded as a source. On create forms, string fields configured in
365
- core with `.seo('<seoField>')` appear as normal preconfigured dynamic bindings;
366
- editors can replace or clear them before the first save.
367
-
368
- ## llms.txt settings
369
-
370
- Settings → AI content edits the optional site-level `llms.txt` guide. Publishing is off
371
- by default. Editors may override the SEO-derived site title and summary, add
372
- Markdown guidance, and curate ordered sections of internal pages or external
373
- URLs. Sections marked as Secondary content appear as titled subsections under
374
- `## Optional`; Rakun never copies the complete sitemap into this document automatically. Internal page choices
375
- are limited to published documents when their content type uses publication
376
- states.
377
-
378
- ## Confirmations
379
-
380
- Use the shared async confirm API instead of ad-hoc yes/no dialogs:
381
-
382
- ```tsx
383
- import { confirm, useConfirm } from '@rakun-kit/manager-react'
384
-
385
- const result = await confirm({
386
- title: 'Delete item',
387
- description: 'This cannot be undone.',
388
- confirmLabel: 'Delete',
389
- variant: 'destructive',
390
- onConfirm: async () => {
391
- await deleteItem()
392
- },
393
- })
394
-
395
- // 'confirmed' | 'cancelled' | 'dismissed'
396
- if (result !== 'confirmed') return
397
-
398
- // Or boolean helper:
399
- if (!(await confirm.yes({ title: 'Continue?' }))) return
400
- ```
401
-
402
- `ConfirmProvider` is mounted by `ManagerRuntimeApp` / `ManagerBrowserApp`.
403
- `onConfirm` keeps the dialog open with a loading button until the promise
404
- settles; thrown errors leave it open.
405
-
406
- ## Public entrypoints
407
-
408
- - `@rakun-kit/manager-react`: manager apps, providers and common helpers.
409
- - `/client/http`, `/client/request`, `/client/trpc`: manager transports.
410
- - `/app/runtime-app`: runtime/browser app components.
411
- - `/state/navigation`, `/state/theme`, `/link`: host integration primitives.
412
- - `/plugins`, `/rich-text`: plugin and RichText extension contracts.
413
- - `/i18n`: locale runtime, catalog and locale types.
414
- - `confirm` / `useConfirm` / `ConfirmProvider`: shared async confirmation dialogs.
415
- - `/styles.css`: required bundled styles.
416
-
417
- ## Agent constraints
418
-
419
- - This package can run outside Next.js; do not assume Next navigation or server
420
- components.
421
- - Do not import the main React entrypoint into server bootstrap just to obtain
422
- locale types or data.
423
- - Permission-gate protected UI, and handle loading, empty and error states.
424
- - Use existing manager components and operations before adding parallel APIs.
425
- - Keep manager/core schemas synchronized when an operation changes.
426
- - Do not hardcode user-facing manager copy.
1
+ # `@rakun-kit/manager-react` AI usage manual
2
+
3
+ This package is the publishable React manager, its transport clients,
4
+ navigation abstraction, locale runtime, plugin API and CSS. Most Next.js apps
5
+ should use `@rakun-kit/next/manager`; use this package directly for custom
6
+ React hosts.
7
+
8
+ Read the core manual at
9
+ `node_modules/@rakun-kit/core/dist/docs/index.md` before changing manager/server
10
+ contracts.
11
+
12
+ ## Install and mount
13
+
14
+ ```sh
15
+ bun add @rakun-kit/manager-react react react-dom
16
+ ```
17
+
18
+ ```tsx
19
+ import { ManagerBrowserApp, createHttpManagerClient } from '@rakun-kit/manager-react'
20
+ import '@rakun-kit/manager-react/styles.css'
21
+
22
+ const client = createHttpManagerClient({ baseUrl: '/api/rakun' })
23
+
24
+ export function ManagerPage() {
25
+ return (
26
+ <ManagerBrowserApp client={client} pathname={window.location.pathname} basePath="/backend" />
27
+ )
28
+ }
29
+ ```
30
+
31
+ Import the stylesheet exactly once. `ManagerBrowserApp` owns browser-history
32
+ navigation. Use `ManagerRuntimeApp` when the host supplies navigation.
33
+
34
+ Built-in screens are split by manager route and loaded on first visit. Lucide
35
+ names configured for content-type and module-picker icons remain unrestricted;
36
+ the manager accepts kebab-case or PascalCase names and loads the selected icon
37
+ on demand.
38
+
39
+ ## Realtime synchronization
40
+
41
+ The runtime loads `platform.realtime` from `manager.uiLocales` and selects
42
+ polling or SSE without a separate client-side transport setting. Set
43
+ `realtimeBaseUrl` to the manager API base URL so package-relative endpoints
44
+ resolve correctly:
45
+
46
+ ```tsx
47
+ <ManagerBrowserApp
48
+ client={client}
49
+ realtimeBaseUrl="/api/rakun"
50
+ pathname={window.location.pathname}
51
+ />
52
+ ```
53
+
54
+ The default `sseRealtime()` metadata resolves to
55
+ `<realtimeBaseUrl>/realtime`; core bootstrap configuration does not repeat the
56
+ API base path.
57
+
58
+ Core mutations publish stable topics for collaboration documents, locale
59
+ variants, versions, comments, and notifications. `useManagerSyncQuery` and
60
+ `useSync` invalidate their exact TanStack Query key after a matching event.
61
+ The `syncIntervalMs` option is used only by the polling provider; SSE does not
62
+ create periodic refresh timers. SSE multiplexes all active topics onto one
63
+ `EventSource`, so opening multiple editors does not exhaust the browser's
64
+ per-origin connection pool.
65
+
66
+ Custom compositions may pass a `RealtimeMetadata` or `RealtimeProvider` to
67
+ `ManagerProvider` / `ManagerAppProvider`. The root package exports
68
+ `pollingRealtime`, `sseRealtime`, `realtimeFromMetadata`, `useSync`, and
69
+ `useManagerSyncQuery`. Prefer the runtime bootstrap path so the core platform
70
+ remains the source of truth.
71
+
72
+ ## Clients and navigation
73
+
74
+ - HTTP: `createHttpManagerClient` from `/client/http`.
75
+ - tRPC: `createTrpcManagerClient` from `/client/trpc`.
76
+ - Custom transport: `createManagerClient` from `/client/request`.
77
+ - Router bridge: `createPathManagerNavigation` from `/state/navigation`.
78
+
79
+ Hosts can use `preloadManagerPath` to prefetch built-in route chunks from their
80
+ own router or link component.
81
+
82
+ Use operation names and payloads from Rakun contracts; do not recreate endpoint
83
+ types in host code. Keep `baseUrl` (API) distinct from `basePath` (manager UI).
84
+
85
+ ## Link field picker
86
+
87
+ The `f.link()` editor always shows Title and Destination inputs. A typed or
88
+ pasted URL is stored as `{ href, title }`. Opening the destination picker
89
+ exposes the home page and configured page-route types with their content type
90
+ icons; selecting an item fills an empty title from its manager label and stores
91
+ `{ routeId, contentTypeId, title }` so core can resolve localized paths.
92
+ Persisted string values are normalized to `{ href, title: '' }` when loaded;
93
+ untitled internal references also remain editable.
94
+
95
+ Compound arrays such as `f.array(f.link())` render each value as a reorderable
96
+ card in the Info editor. Use the drag handle to change their persisted order;
97
+ current nested input values are preserved while an item is moved.
98
+
99
+ `f.menu()` renders an ordered navigation tree for headers and similar modules.
100
+ Each item reuses the internal/direct link picker and may contain unlimited child
101
+ levels. Drag vertically to reorder; drag horizontally to nest or outdent. The
102
+ level buttons expose equivalent keyboard actions. Removing a parent removes
103
+ its complete subtree, and editor-only drag identifiers are never persisted.
104
+
105
+ ## Media uploads
106
+
107
+ The manager preserves Unicode original file names when uploading media. Names
108
+ with accents, non-Latin scripts, or emoji are encoded into ASCII for the binary
109
+ upload headers and restored by core before metadata is saved.
110
+
111
+ The optimization popover has a Format selector for Image and Video settings.
112
+ Images retain their output-format, quality, preview, and responsive-size
113
+ controls. Videos expose their own quality and are always converted to both MP4
114
+ and WebM for browser compatibility.
115
+
116
+ When optimization is enabled, right-click an image or video and select
117
+ `Reimport` to regenerate its optimized outputs. The action keeps the existing
118
+ media ID, title, alt text, folder, and content references. You can also
119
+ multi-select images and videos and use the selection toolbar reimport action.
120
+ For an image, the same context menu offers `Replace image`. Choose a new image
121
+ and confirm to replace the underlying file while keeping the media ID, details,
122
+ folder and every reference to it across the site. Referencing pages are
123
+ revalidated when the project has route revalidation configured.
124
+ The image editor can crop to a rectangle or circle, rotate, and flip the saved
125
+ copy horizontally or vertically.
126
+ In a video's details panel, choose an existing gallery image as its poster or
127
+ enter a video second and generate one. Generated frames are saved as images in
128
+ the same media folder. The poster can also be removed from the video.
129
+ Optimization is enabled by default in the media library; generated image
130
+ previews are stored as inline `data:image/...` strings on the media record for
131
+ LQIP use.
132
+
133
+ Gallery and file-field image previews use `@rakun-kit/react` `Image` with a
134
+ layout-appropriate `sizes` value so responsive variants are selected when
135
+ available.
136
+
137
+ ## Manager plugins
138
+
139
+ ```tsx
140
+ import {
141
+ defineRakunManagerPlugin,
142
+ ManagerBrowserApp,
143
+ } from '@rakun-kit/manager-react'
144
+
145
+ const plugin = defineRakunManagerPlugin({
146
+ id: '@acme/analytics',
147
+ routes: [{
148
+ id: 'dashboard',
149
+ path: '/analytics',
150
+ component: AnalyticsScreen,
151
+ permissions: ['plugin.analytics.view'],
152
+ }],
153
+ sidebar: [{
154
+ id: 'analytics',
155
+ title: 'analytics.title',
156
+ routeId: 'dashboard',
157
+ position: 'primary',
158
+ group: 'Plugins',
159
+ }],
160
+ })
161
+
162
+ <ManagerBrowserApp plugins={[plugin]} {...props} />
163
+ ```
164
+
165
+ Plugins can contribute routes, sidebar entries, custom field editors and
166
+ RichText extensions. Use exported plugin types from
167
+ `@rakun-kit/manager-react/plugins`. Plugin ids, route ids, field editor ids,
168
+ Lexical node types and RichText plugin ids must be unique.
169
+
170
+ Dynamic Data behavior for custom editors comes from the encoded field
171
+ `config.capabilities`, not from the editor id. `valueKind` controls source and
172
+ target compatibility; `dynamic.properties` exposes object leaves;
173
+ `mapProperties` renders them as separate mapping targets; `relation` enables
174
+ nested content-type fields; and `collection` enables homogeneous or
175
+ heterogeneous per-item mappings. A plugin editor therefore gains the standard
176
+ Dynamic Data control by declaring its behavior in its core field factory.
177
+
178
+ In Next.js, plugin registration belongs in a `'use client'` wrapper around
179
+ `RakunManagerClientPage`; pass the wrapper to `RakunManagerPage` as
180
+ `managerComponent`.
181
+
182
+ ## Languages and copy
183
+
184
+ English is built in. Additional server-provided UI packs come from
185
+ `@rakun-kit/manager-locales` and are configured in core `managerLanguages`.
186
+ Content editing locale and manager UI locale are separate states.
187
+
188
+ Built-in manager copy must use the static manager translation catalog. Project
189
+ labels may use arbitrary project keys. Fields resolve `field.<fieldName>` and
190
+ layout slots resolve `layoutModule.<layoutKey>` with fallbacks.
191
+
192
+ The authenticated sidebar places Visit site at the top-right of its logo block.
193
+ It opens the validated SEO `siteUrl` in a new tab and falls back to `/` on the
194
+ current manager host when the setting is empty. Custom dashboard layout
195
+ renderers receive `siteUrl` in their layout props and should preserve an
196
+ equivalent action. On a published routed document, the edit toolbar also shows
197
+ View page for the exact selected language and locale variant. Draft, hidden,
198
+ trashed, and route-less documents do not show that contextual action.
199
+
200
+ ## Content and template tabs
201
+
202
+ Saved content edit screens load an independent Yjs working document for the
203
+ exact document `_id`. Field changes are synchronized incrementally over the
204
+ manager HTTP client and the toolbar reports shared unsaved changes. Another
205
+ editor on the same published document or draft sees that shared working state;
206
+ an editor on a different draft uses a different room. Leaving and reopening the
207
+ screen reloads the working state from the collaboration backend.
208
+
209
+ The edit header shows one presence avatar per open browser tab, including
210
+ multiple tabs owned by the same user. It keeps two avatars visible and collapses
211
+ additional viewers into a `+N` badge; hovering or focusing the group lists every
212
+ viewer and their focused field. Fields focused by another tab receive a visible
213
+ highlight. Presence is ephemeral and disappears after a tab leaves or its
214
+ connection expires. With SSE, the authenticated stream and its server heartbeat
215
+ maintain that presence without periodic client-side collaboration sync calls;
216
+ the polling provider refreshes it as part of its normal poll.
217
+
218
+ Each collaborative room is also persisted locally with IndexedDB, isolated by
219
+ manager user and room id. A previously opened document or shared Template loads
220
+ its local Yjs state first and then exchanges updates with core. Edits made while
221
+ the collaboration endpoint is unavailable remain in IndexedDB and are sent
222
+ when connectivity returns; the toolbar reports that state as offline and
223
+ locally stored. The first visit still needs the normal authenticated manager
224
+ read to authorize and describe the document.
225
+
226
+ Save first flushes pending Yjs updates and then asks core to persist a snapshot
227
+ of the server-side working document. Until it succeeds, public/web reads and
228
+ revalidation remain on the previous saved snapshot. Unsaved therefore means
229
+ shared-but-not-committed, not changes owned by one browser. Draft promotion is
230
+ still separate and uses the saved draft snapshot.
231
+
232
+ The edit toolbar can discard changes through the shared confirmation dialog.
233
+ Discard restores the last saved content snapshot for every editor; when the
234
+ shared Template also has unsaved work, the confirmation states that it will be
235
+ restored too. The save menu can save the current unsaved form as a separately
236
+ named, unassigned draft variant copy or create an independent draft copy.
237
+
238
+ Routeable documents keep the live preview visible as the main editing surface
239
+ and collapse the manager's global navigation when opened. Editors can reopen
240
+ that navigation from the normal header control. On wide screens, fields and the
241
+ available Info, Content, Template, SEO, layout, variant, and history sections
242
+ live in a resizable sidebar with compact vertical navigation and a preview-first
243
+ initial split. Content and Template expose their module tree in an overlay
244
+ navigator so it can be used for selection and reordering without permanently
245
+ narrowing module forms. Module card actions use a separate wrapping row. On
246
+ tablet-sized screens, the editor opens as an overlay while the preview remains
247
+ the underlying canvas. On mobile, Rakun hides the preview, renders the editor as
248
+ the main surface, and changes the section rail into a horizontally scrollable
249
+ top bar.
250
+
251
+ Opening the automatic preview reads the current working state without showing
252
+ Save validation errors. Explicit preview refreshes and Save still validate the
253
+ form. Existing relation selections remain available while their options load.
254
+ Simple arrays share their field values directly; manager-only row identities
255
+ are never included in collaborative or saved link arrays.
256
+
257
+ IndexedDB persistence does not replace Save: local and shared CRDT updates are
258
+ working state, while MongoDB and public/web reads change only after the server
259
+ save operation succeeds. Save therefore requires connectivity.
260
+
261
+ The shared Template tab also uses Yjs, but in a separate room keyed only by
262
+ content type. Consequently, editors who opened different documents collaborate
263
+ on the same template. Its changes remain shared but uncommitted until Save,
264
+ which persists the template snapshot through the normal validated template
265
+ flow. It is never copied into the current document's room.
266
+
267
+ This scope intentionally excludes create-form content before its first save,
268
+ route-layout modules, Settings, literals, users, comments, reviews, and other
269
+ manager forms. The shared Template remains collaborative even while reached
270
+ from a create form because it is an existing type-level resource. Automatic
271
+ translation changes the content document's shared working state but does not
272
+ save it.
273
+
274
+ Variant creation, language assignment, promotion, trash, and restore remain
275
+ validated server mutations rather than CRDT fields. While editors are open,
276
+ the Variants tab and the editor's locale assignments refresh automatically, so
277
+ a variant created by one user appears for the others without reloading the
278
+ page.
279
+
280
+ For a routeable content type with `iterator`, Content edits the `_iterator`
281
+ modules unique to the current document. Template is enabled automatically and
282
+ edits one shared composition for every document of that type. Both tabs offer
283
+ the same configured iterator modules. The Template module picker additionally
284
+ adds a built-in Content item at its root and inside blocks fields; it marks the
285
+ exact position where the current document's Content modules are inserted.
286
+
287
+ Template changes require the content type's `updateAny` permission and are
288
+ saved with optimistic revision checks. Preview includes unsaved changes from
289
+ both tabs.
290
+
291
+ Fields configured with core `.help(text)` show a compact help icon beside their
292
+ label. Activating it opens the guidance in a popover, including inside iterator
293
+ and nested blocks modules. The manager resolves a translation key through its
294
+ active locale and falls back to displaying the supplied string directly.
295
+
296
+ ## Route SEO analysis
297
+
298
+ The SEO tab of routeable content has separate Metadata and Analysis views.
299
+ Generate report creates a temporary preview snapshot from the current form and
300
+ shared template, then inspects the rendered page through the preview bridge.
301
+ The report checks title and description lengths, H1 usage, heading order,
302
+ missing image `alt` attributes, canonical URL, document language, `noindex`,
303
+ Open Graph coverage, and rendered JSON-LD validity. It also renders
304
+ representative Google and sharing previews plus the page's heading outline and
305
+ the resolved JSON-LD blocks with their detected schema types. The indexability result uses the
306
+ content's resolved SEO value and ignores `noindex` added solely to protect the
307
+ temporary preview URL from indexing. When the canonical field is empty, Rakun
308
+ derives it from Settings → SEO `siteUrl` and the resolved page route; the report
309
+ identifies a missing `siteUrl` separately.
310
+
311
+ SEO reports are advisory and do not claim to predict search rankings. Reports
312
+ generated for saved documents are persisted with their date, score and rendered
313
+ findings when the editor has `content.SeoSettings.own`; the site-wide SEO screen
314
+ uses that history to show recent page reports. The document's Analysis view also
315
+ lists its previous reports for the active language and route; selecting one
316
+ restores the saved report, while Back to current report returns to the live
317
+ result. Preview analysis requires the route to have manager preview configured.
318
+ The preview adapter must support Rakun's SEO-analysis bridge messages; the
319
+ official Next adapter does so automatically.
320
+
321
+ The secondary sidebar exposes a separate SEO screen above the media library for
322
+ users with SEO settings read access. Its Generate report action scans published
323
+ documents from every readable routeable content type in the active language,
324
+ checks stored metadata, custom Open Graph completeness and duplicate titles or
325
+ descriptions, then persists a dated `SeoAudit` snapshot. A page without its own
326
+ title or description uses the corresponding global default from Settings → SEO
327
+ without a warning on the configured home page; other pages receive a fallback
328
+ warning instead of a missing-metadata error. Previous site scores
329
+ remain visible as a paginated evolution history; recent page reports use an
330
+ independent bounded pagination. `SeoAudit` shares the `SeoSettings`
331
+ permission resource, so `own` controls generation and `readAny` controls shared
332
+ history visibility.
333
+
334
+ Rakun's `StructuredData` iterator module can be placed in Content or a shared
335
+ Template. Its typed fields support normal dynamic data mappings; the Custom
336
+ variant accepts an arbitrary JSON object. Invalid custom JSON remains visible in
337
+ the preview report as an error instead of disappearing silently.
338
+
339
+ ## Dynamic data mappings
340
+
341
+ List bindings use a type-aware mapping editor. When a mapped target property is
342
+ itself a `blocks` field, choose `Nested list` to open another collection, query,
343
+ filter, and mapping editor for that property. Nested query conditions can select
344
+ `Current item` to read the parent source item at that level, or `Current
345
+ document` to read the root document. This supports mappings such as Category ->
346
+ gallery item -> Project -> image card, and the editor can repeat the flow for
347
+ deeper block structures.
348
+
349
+ Homogeneous arrays of links or relations can use either `Direct field` for the
350
+ complete array or `Nested list` for per-item mapping. This also covers core
351
+ `relation().multiple()` fields. Link items expose `title` and `href`, while
352
+ relation items expose their target content type fields; their result remains a
353
+ flat array rather than the heterogeneous `{ name, value }` shape used by
354
+ `blocks`.
355
+
356
+ Core `.min(count)` and `.max(count)` limits on `f.array(...)` and `.multiple()`
357
+ relation, file, select, and content-reference fields appear below the manager
358
+ control with its live item count. The control reports minimum/maximum validation
359
+ errors and prevents adding or confirming items beyond the maximum.
360
+
361
+ Target link fields are expanded into visible `<field>.title` and `<field>.href`
362
+ rows. Each property can select its own compatible source and core reconstructs
363
+ the mapped link object.
364
+
365
+ In the SEO tab, `Current document` exposes compatible fields from the complete
366
+ document (including fields edited in Info), while the reserved `_seo` relation
367
+ itself stays excluded as a source. On create forms, string fields configured in
368
+ core with `.seo('<seoField>')` appear as normal preconfigured dynamic bindings;
369
+ editors can replace or clear them before the first save.
370
+
371
+ ## llms.txt settings
372
+
373
+ Settings → AI content edits the optional site-level `llms.txt` guide. Publishing is off
374
+ by default. Editors may override the SEO-derived site title and summary, add
375
+ Markdown guidance, and curate ordered sections of internal pages or external
376
+ URLs. Sections marked as Secondary content appear as titled subsections under
377
+ `## Optional`; Rakun never copies the complete sitemap into this document automatically. Internal page choices
378
+ are limited to published documents when their content type uses publication
379
+ states.
380
+
381
+ ## Confirmations
382
+
383
+ Use the shared async confirm API instead of ad-hoc yes/no dialogs:
384
+
385
+ ```tsx
386
+ import { confirm, useConfirm } from '@rakun-kit/manager-react'
387
+
388
+ const result = await confirm({
389
+ title: 'Delete item',
390
+ description: 'This cannot be undone.',
391
+ confirmLabel: 'Delete',
392
+ variant: 'destructive',
393
+ onConfirm: async () => {
394
+ await deleteItem()
395
+ },
396
+ })
397
+
398
+ // 'confirmed' | 'cancelled' | 'dismissed'
399
+ if (result !== 'confirmed') return
400
+
401
+ // Or boolean helper:
402
+ if (!(await confirm.yes({ title: 'Continue?' }))) return
403
+ ```
404
+
405
+ `ConfirmProvider` is mounted by `ManagerRuntimeApp` / `ManagerBrowserApp`.
406
+ `onConfirm` keeps the dialog open with a loading button until the promise
407
+ settles; thrown errors leave it open.
408
+
409
+ ## Public entrypoints
410
+
411
+ - `@rakun-kit/manager-react`: manager apps, providers and common helpers.
412
+ - `/client/http`, `/client/request`, `/client/trpc`: manager transports.
413
+ - `/app/runtime-app`: runtime/browser app components.
414
+ - `/state/navigation`, `/state/theme`, `/link`: host integration primitives.
415
+ - `/plugins`, `/rich-text`: plugin and RichText extension contracts.
416
+ - `/i18n`: locale runtime, catalog and locale types.
417
+ - `confirm` / `useConfirm` / `ConfirmProvider`: shared async confirmation dialogs.
418
+ - `/styles.css`: required bundled styles.
419
+
420
+ ## Agent constraints
421
+
422
+ - This package can run outside Next.js; do not assume Next navigation or server
423
+ components.
424
+ - Do not import the main React entrypoint into server bootstrap just to obtain
425
+ locale types or data.
426
+ - Permission-gate protected UI, and handle loading, empty and error states.
427
+ - Use existing manager components and operations before adding parallel APIs.
428
+ - Keep manager/core schemas synchronized when an operation changes.
429
+ - Do not hardcode user-facing manager copy.