@questpie/admin 3.3.0 → 3.4.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.
Files changed (76) hide show
  1. package/README.md +4 -6
  2. package/dist/client/blocks/block-renderer.d.mts +2 -2
  3. package/dist/client/builder/admin-types.d.mts +3 -3
  4. package/dist/client/builder/types/action-types.d.mts +1 -1
  5. package/dist/client/builder/types/collection-types.d.mts +59 -2
  6. package/dist/client/modules/admin.d.mts +3 -0
  7. package/dist/client/modules/admin.mjs +3 -0
  8. package/dist/client/preview/block-scope-context.d.mts +2 -2
  9. package/dist/client/preview/preview-banner.d.mts +2 -2
  10. package/dist/client/preview/preview-field.d.mts +4 -4
  11. package/dist/client/views/collection/list-view.mjs +830 -0
  12. package/dist/client/views/collection/outline.mjs +363 -0
  13. package/dist/client/views/collection/table-view.mjs +6 -3
  14. package/dist/client/views/layout/admin-layout.d.mts +15 -1
  15. package/dist/client/views/layout/admin-layout.mjs +95 -31
  16. package/dist/client.d.mts +6 -6
  17. package/dist/components/rich-text/rich-text-renderer.d.mts +2 -2
  18. package/dist/factories.d.mts +19 -0
  19. package/dist/factories.mjs +11 -0
  20. package/dist/fields.d.mts +4 -0
  21. package/dist/fields.mjs +5 -0
  22. package/dist/index.d.mts +6 -6
  23. package/dist/modules/admin.d.mts +10 -0
  24. package/dist/modules/admin.mjs +9 -0
  25. package/dist/modules/audit.d.mts +5 -0
  26. package/dist/modules/audit.mjs +5 -0
  27. package/dist/server/augmentation/form-layout.d.mts +57 -2
  28. package/dist/server/augmentation/index.d.mts +3 -1
  29. package/dist/server/augmentation/shell.d.mts +48 -0
  30. package/dist/server/augmentation.d.mts +2 -1
  31. package/dist/server/codegen/admin-client-template.mjs +11 -4
  32. package/dist/server/fields/blocks.d.mts +9 -2
  33. package/dist/server/fields/blocks.mjs +1 -1
  34. package/dist/server/fields/index.d.mts +2 -2
  35. package/dist/server/fields/index.mjs +2 -2
  36. package/dist/server/fields/rich-text.d.mts +9 -2
  37. package/dist/server/fields/rich-text.mjs +1 -1
  38. package/dist/server/modules/admin/.generated/module.d.mts +24 -19
  39. package/dist/server/modules/admin/.generated/module.mjs +5 -1
  40. package/dist/server/modules/admin/.generated/registries.d.mts +6 -4
  41. package/dist/server/modules/admin/client/.generated/module.d.mts +70 -70
  42. package/dist/server/modules/admin/client/.generated/module.mjs +3 -1
  43. package/dist/server/modules/admin/client/views/collection-form.d.mts +6 -0
  44. package/dist/server/modules/admin/client/views/collection-table.d.mts +6 -0
  45. package/dist/server/modules/admin/client/views/global-form.d.mts +6 -0
  46. package/dist/server/modules/admin/client/views/list-view.d.mts +6 -0
  47. package/dist/server/modules/admin/client/views/list-view.mjs +10 -0
  48. package/dist/server/modules/admin/collections/account.d.mts +46 -46
  49. package/dist/server/modules/admin/collections/admin-locks.d.mts +54 -54
  50. package/dist/server/modules/admin/collections/admin-preferences.d.mts +34 -34
  51. package/dist/server/modules/admin/collections/admin-saved-views.d.mts +47 -47
  52. package/dist/server/modules/admin/collections/apikey.d.mts +68 -68
  53. package/dist/server/modules/admin/collections/assets.d.mts +34 -34
  54. package/dist/server/modules/admin/collections/session.d.mts +38 -38
  55. package/dist/server/modules/admin/dto/admin-config.dto.mjs +17 -0
  56. package/dist/server/modules/admin/index.d.mts +30 -31
  57. package/dist/server/modules/admin/routes/admin-config.d.mts +0 -15
  58. package/dist/server/modules/admin/routes/admin-config.mjs +21 -5
  59. package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
  60. package/dist/server/modules/admin/routes/execute-action.mjs +18 -12
  61. package/dist/server/modules/admin/routes/i18n-helpers.d.mts +4 -0
  62. package/dist/server/modules/admin/routes/preview.d.mts +11 -11
  63. package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
  64. package/dist/server/modules/admin/routes/route-helpers.mjs +36 -1
  65. package/dist/server/modules/admin/routes/setup.d.mts +7 -14
  66. package/dist/server/modules/admin/routes/setup.mjs +16 -3
  67. package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
  68. package/dist/server/modules/admin/views/list-view.d.mts +8 -0
  69. package/dist/server/modules/admin/views/list-view.mjs +7 -0
  70. package/dist/server/modules/audit/collections/audit-log.d.mts +7 -2
  71. package/dist/server/modules/audit/index.d.mts +1 -1
  72. package/dist/server/plugin.d.mts +1 -1
  73. package/dist/server/plugin.mjs +28 -28
  74. package/dist/server.d.mts +5 -4
  75. package/dist/server.mjs +7 -7
  76. package/package.json +13 -3
@@ -0,0 +1,363 @@
1
+ //#region src/client/views/collection/outline.ts
2
+ const DEFAULT_MAX_DEPTH = 12;
3
+ const NO_VALUE = "No value";
4
+ function getId(value) {
5
+ if (typeof value === "string" || typeof value === "number") return String(value);
6
+ if (value && typeof value === "object") {
7
+ const id = value.id;
8
+ if (typeof id === "string" || typeof id === "number") return String(id);
9
+ }
10
+ return null;
11
+ }
12
+ function getPathValue(source, path) {
13
+ if (!path) return source;
14
+ const parts = path.split(".").filter(Boolean);
15
+ let current = source;
16
+ for (const part of parts) {
17
+ if (!current || typeof current !== "object") return void 0;
18
+ current = current[part];
19
+ }
20
+ return current;
21
+ }
22
+ function defaultLabelForValue(value) {
23
+ if (value === null || value === void 0 || value === "") return NO_VALUE;
24
+ if (Array.isArray(value)) return value.length ? value.map((item) => defaultLabelForValue(item)).join(", ") : NO_VALUE;
25
+ if (typeof value === "object") {
26
+ const record = value;
27
+ return String(record.title ?? record.name ?? record.label ?? record.id ?? NO_VALUE);
28
+ }
29
+ return String(value);
30
+ }
31
+ function stableGroupKey(value) {
32
+ if (value === null || value === void 0 || value === "") return "__empty__";
33
+ if (Array.isArray(value)) return value.map(stableGroupKey).join(",");
34
+ if (typeof value === "object") return getId(value) ?? JSON.stringify(value);
35
+ return String(value);
36
+ }
37
+ function shouldExpand(key, depth, outline, collapsedKeys) {
38
+ if (collapsedKeys.has(key)) return false;
39
+ const defaultExpanded = outline?.defaultExpanded ?? true;
40
+ if (defaultExpanded === true) return true;
41
+ if (defaultExpanded === false) return false;
42
+ return depth === 0;
43
+ }
44
+ function compareByOrder(a, b, order) {
45
+ if (Array.isArray(order)) {
46
+ const ai = order.indexOf(a.key);
47
+ const bi = order.indexOf(b.key);
48
+ if (ai !== -1 || bi !== -1) {
49
+ if (ai === -1) return 1;
50
+ if (bi === -1) return -1;
51
+ return ai - bi;
52
+ }
53
+ }
54
+ const result = a.label.localeCompare(b.label);
55
+ return order === "desc" ? -result : result;
56
+ }
57
+ function matchesWhere(edge, where) {
58
+ if (!where) return true;
59
+ return Object.entries(where).every(([key, expected]) => {
60
+ const actual = getPathValue(edge, key);
61
+ if (expected && typeof expected === "object" && !Array.isArray(expected)) {
62
+ if ("in" in expected) return (expected.in ?? []).map(String).includes(String(actual));
63
+ }
64
+ return String(actual) === String(expected);
65
+ });
66
+ }
67
+ function isRecord(value) {
68
+ return !!value && typeof value === "object" && !Array.isArray(value);
69
+ }
70
+ function buildFieldRows(level, docs, depth, levelIndex, ctx, scopeKey) {
71
+ const groups = /* @__PURE__ */ new Map();
72
+ for (const doc of docs) {
73
+ const value = getPathValue(doc, level.field);
74
+ const key = stableGroupKey(value);
75
+ const group = groups.get(key);
76
+ if (group) {
77
+ group.docs.push(doc);
78
+ continue;
79
+ }
80
+ groups.set(key, {
81
+ key,
82
+ label: ctx.labelForValue(value, level.labelField ?? level.field),
83
+ value,
84
+ docs: [doc]
85
+ });
86
+ }
87
+ return Array.from(groups.values()).sort((a, b) => compareByOrder(a, b, level.order)).flatMap((group) => {
88
+ const rowKey = `${scopeKey}/field:${level.field}:${group.key}`;
89
+ const expanded = shouldExpand(rowKey, depth, ctx.outline, ctx.collapsedKeys);
90
+ return [{
91
+ kind: "group",
92
+ key: rowKey,
93
+ label: group.label,
94
+ depth,
95
+ count: group.docs.length,
96
+ expandable: true,
97
+ collapsed: !expanded
98
+ }, ...expanded ? buildLevelRows(group.docs, depth + 1, levelIndex + 1, ctx, rowKey) : []];
99
+ });
100
+ }
101
+ function buildRelationFieldRows(level, docs, depth, levelIndex, ctx, scopeKey) {
102
+ const fieldPath = level.field ? `${level.relation}.${level.field}` : level.relation;
103
+ const groups = /* @__PURE__ */ new Map();
104
+ for (const doc of docs) {
105
+ const relationValue = getPathValue(doc, level.relation);
106
+ const value = level.field ? getPathValue(relationValue, level.field) : relationValue;
107
+ const key = stableGroupKey(value);
108
+ const group = groups.get(key);
109
+ if (group) {
110
+ group.docs.push(doc);
111
+ continue;
112
+ }
113
+ groups.set(key, {
114
+ key,
115
+ label: ctx.labelForValue(value, level.labelField ?? fieldPath),
116
+ docs: [doc]
117
+ });
118
+ }
119
+ return Array.from(groups.values()).sort((a, b) => compareByOrder(a, b, level.order)).flatMap((group) => {
120
+ const rowKey = `${scopeKey}/relation-field:${fieldPath}:${group.key}`;
121
+ const expanded = shouldExpand(rowKey, depth, ctx.outline, ctx.collapsedKeys);
122
+ return [{
123
+ kind: "group",
124
+ key: rowKey,
125
+ label: group.label,
126
+ depth,
127
+ count: group.docs.length,
128
+ expandable: true,
129
+ collapsed: !expanded
130
+ }, ...expanded ? buildLevelRows(group.docs, depth + 1, levelIndex + 1, ctx, rowKey) : []];
131
+ });
132
+ }
133
+ function getRepeatMaxDepth(repeat, fallback) {
134
+ if (!repeat) return 1;
135
+ if (typeof repeat === "object") return repeat.maxDepth ?? fallback;
136
+ return fallback;
137
+ }
138
+ function buildEdgeRows(level, docs, depth, levelIndex, ctx, scopeKey) {
139
+ const docsById = /* @__PURE__ */ new Map();
140
+ for (const doc of docs) {
141
+ const id = getId(doc);
142
+ if (id) docsById.set(id, doc);
143
+ }
144
+ const edgeDocs = (ctx.edgesByCollection[level.collection] ?? []).filter((edge) => matchesWhere(edge, level.where));
145
+ if (ctx.outline?.preserveMatchingBranches !== false) for (let pass = 0; pass < ctx.maxDepth; pass++) {
146
+ let changed = false;
147
+ for (const edge of edgeDocs) {
148
+ const parentValue = getPathValue(edge, level.parentField);
149
+ const childValue = getPathValue(edge, level.childField);
150
+ const parentId = getId(parentValue);
151
+ const childId = getId(childValue);
152
+ if (!parentId || !childId || !docsById.has(childId)) continue;
153
+ if (docsById.has(parentId) || !isRecord(parentValue)) continue;
154
+ docsById.set(parentId, parentValue);
155
+ changed = true;
156
+ }
157
+ if (!changed) break;
158
+ }
159
+ const outlineDocs = Array.from(docsById.values());
160
+ const childrenByParent = /* @__PURE__ */ new Map();
161
+ const childIds = /* @__PURE__ */ new Set();
162
+ const edgeSeen = /* @__PURE__ */ new Set();
163
+ for (const edge of edgeDocs) {
164
+ const parentId = getId(getPathValue(edge, level.parentField));
165
+ const childId = getId(getPathValue(edge, level.childField));
166
+ if (!parentId || !childId || parentId === childId) continue;
167
+ if (!docsById.has(childId)) continue;
168
+ const dedupeKey = `${parentId}:${childId}:${JSON.stringify(level.where ?? {})}`;
169
+ if (edgeSeen.has(dedupeKey)) continue;
170
+ edgeSeen.add(dedupeKey);
171
+ childIds.add(childId);
172
+ const children = childrenByParent.get(parentId) ?? [];
173
+ children.push({
174
+ childId,
175
+ edge
176
+ });
177
+ childrenByParent.set(parentId, children);
178
+ }
179
+ const roots = outlineDocs.filter((doc) => {
180
+ const id = getId(doc);
181
+ return !id || !childIds.has(id) || !docsById.has(id);
182
+ });
183
+ const maxRepeatDepth = getRepeatMaxDepth(level.repeat, ctx.maxDepth);
184
+ const rows = [];
185
+ const visited = /* @__PURE__ */ new Set();
186
+ function pushChildRows(parentId, nextDepth, branchDepth, ancestorIds) {
187
+ const children = childrenByParent.get(parentId) ?? [];
188
+ if (children.length === 0) return;
189
+ if (branchDepth >= maxRepeatDepth) return;
190
+ if (level.groupByEdgeField) {
191
+ const groups = /* @__PURE__ */ new Map();
192
+ for (const child of children) {
193
+ const value = getPathValue(child.edge, level.groupByEdgeField);
194
+ const key = stableGroupKey(value);
195
+ const group = groups.get(key);
196
+ if (group) group.children.push(child);
197
+ else groups.set(key, {
198
+ label: ctx.labelForValue(value, level.groupByEdgeField),
199
+ children: [child]
200
+ });
201
+ }
202
+ for (const [key, group] of groups) {
203
+ const rowKey = `${scopeKey}/edge-group:${level.collection}:${parentId}:${key}`;
204
+ const expanded = shouldExpand(rowKey, nextDepth, ctx.outline, ctx.collapsedKeys);
205
+ rows.push({
206
+ kind: "group",
207
+ key: rowKey,
208
+ label: group.label,
209
+ depth: nextDepth,
210
+ count: group.children.length,
211
+ expandable: true,
212
+ collapsed: !expanded
213
+ });
214
+ if (!expanded) continue;
215
+ for (const child of group.children) pushEdgeRecord(child.childId, nextDepth + 1, branchDepth + 1, ancestorIds);
216
+ }
217
+ return;
218
+ }
219
+ for (const child of children) pushEdgeRecord(child.childId, nextDepth, branchDepth + 1, ancestorIds);
220
+ }
221
+ function pushEdgeRecord(id, rowDepth, branchDepth, ancestorIds) {
222
+ const doc = docsById.get(id);
223
+ if (!doc || ancestorIds.has(id)) return;
224
+ const rowKey = `${scopeKey}/record:${id}`;
225
+ const hasChildren = (childrenByParent.get(id) ?? []).some((child) => docsById.has(child.childId));
226
+ const expanded = hasChildren && shouldExpand(rowKey, rowDepth, ctx.outline, ctx.collapsedKeys);
227
+ rows.push({
228
+ kind: "record",
229
+ key: rowKey,
230
+ id,
231
+ doc,
232
+ depth: rowDepth,
233
+ expandable: hasChildren,
234
+ collapsed: hasChildren ? !expanded : void 0
235
+ });
236
+ visited.add(id);
237
+ if (hasChildren && expanded) {
238
+ const nextAncestors = new Set(ancestorIds);
239
+ nextAncestors.add(id);
240
+ pushChildRows(id, rowDepth + 1, branchDepth, nextAncestors);
241
+ }
242
+ }
243
+ for (const doc of roots) {
244
+ const id = getId(doc);
245
+ if (!id) {
246
+ rows.push({
247
+ kind: "record",
248
+ key: `${scopeKey}/record:${rows.length}`,
249
+ id: String(rows.length),
250
+ doc,
251
+ depth
252
+ });
253
+ continue;
254
+ }
255
+ pushEdgeRecord(id, depth, 0, /* @__PURE__ */ new Set());
256
+ }
257
+ for (const doc of outlineDocs) {
258
+ const id = getId(doc);
259
+ if (id && !visited.has(id)) pushEdgeRecord(id, depth, 0, /* @__PURE__ */ new Set());
260
+ }
261
+ if (levelIndex + 1 >= ctx.levels.length) return rows;
262
+ return rows.flatMap((row) => row.kind === "record" ? [row, ...buildLevelRows([row.doc], row.depth + 1, levelIndex + 1, ctx, row.key)] : [row]);
263
+ }
264
+ function createPathNode(key, label) {
265
+ return {
266
+ key,
267
+ label,
268
+ children: /* @__PURE__ */ new Map(),
269
+ docs: []
270
+ };
271
+ }
272
+ function countPathNode(node) {
273
+ let count = node.docs.length;
274
+ for (const child of node.children.values()) count += countPathNode(child);
275
+ return count;
276
+ }
277
+ function buildPathRows(level, docs, depth, levelIndex, ctx, scopeKey) {
278
+ const root = createPathNode("path:root", "Root");
279
+ const separator = level.separator ?? "/";
280
+ const maxPathDepth = getRepeatMaxDepth(level.repeat, ctx.maxDepth);
281
+ for (const doc of docs) {
282
+ const segments = String(getPathValue(doc, level.field) ?? "").trim().split(separator).filter(Boolean).slice(0, maxPathDepth);
283
+ const folderSegments = level.syntheticFolders && segments.length > 0 ? segments.slice(0, Math.max(segments.length - 1, 0)) : segments;
284
+ let node = root;
285
+ let currentPath = "";
286
+ for (const segment of folderSegments) {
287
+ currentPath = currentPath ? `${currentPath}${separator}${segment}` : segment;
288
+ let child = node.children.get(segment);
289
+ if (!child) {
290
+ child = createPathNode(`${scopeKey}/path:${level.field}:${currentPath}`, segment);
291
+ node.children.set(segment, child);
292
+ }
293
+ node = child;
294
+ }
295
+ node.docs.push(doc);
296
+ }
297
+ function walk(node, rowDepth) {
298
+ const rows = [];
299
+ for (const child of Array.from(node.children.values()).sort((a, b) => a.label.localeCompare(b.label))) {
300
+ const expanded = shouldExpand(child.key, rowDepth, ctx.outline, ctx.collapsedKeys);
301
+ rows.push({
302
+ kind: "synthetic",
303
+ key: child.key,
304
+ label: child.label,
305
+ depth: rowDepth,
306
+ count: countPathNode(child),
307
+ expandable: true,
308
+ collapsed: !expanded
309
+ });
310
+ if (expanded) rows.push(...walk(child, rowDepth + 1));
311
+ }
312
+ for (const doc of node.docs) {
313
+ const id = getId(doc) ?? `${node.key}:${rows.length}`;
314
+ rows.push({
315
+ kind: "record",
316
+ key: `${scopeKey}/record:${id}`,
317
+ id,
318
+ doc,
319
+ depth: rowDepth
320
+ });
321
+ if (levelIndex + 1 < ctx.levels.length) rows.push(...buildLevelRows([doc], rowDepth + 1, levelIndex + 1, ctx, `${scopeKey}/record:${id}`));
322
+ }
323
+ return rows;
324
+ }
325
+ return walk(root, depth);
326
+ }
327
+ function buildLevelRows(docs, depth, levelIndex, ctx, scopeKey = "root") {
328
+ if (docs.length === 0) return [];
329
+ if (depth > ctx.maxDepth || levelIndex >= ctx.levels.length) return docs.map((doc, index) => {
330
+ const id = getId(doc) ?? `${depth}:${index}`;
331
+ return {
332
+ kind: "record",
333
+ key: `${scopeKey}/record:${id}`,
334
+ id,
335
+ doc,
336
+ depth
337
+ };
338
+ });
339
+ const level = ctx.levels[levelIndex];
340
+ if (level.kind === "field") return buildFieldRows(level, docs, depth, levelIndex, ctx, scopeKey);
341
+ if (level.kind === "relation-field") return buildRelationFieldRows(level, docs, depth, levelIndex, ctx, scopeKey);
342
+ if (level.kind === "edge") return buildEdgeRows(level, docs, depth, levelIndex, ctx, scopeKey);
343
+ return buildPathRows(level, docs, depth, levelIndex, ctx, scopeKey);
344
+ }
345
+ function buildOutlineRows({ docs, outline, edgesByCollection = {}, collapsedKeys, labelForValue = defaultLabelForValue, maxDepth }) {
346
+ const levels = outline?.levels?.filter(Boolean) ?? [];
347
+ const ctx = {
348
+ levels,
349
+ outline,
350
+ edgesByCollection,
351
+ collapsedKeys: new Set(collapsedKeys ?? []),
352
+ labelForValue,
353
+ maxDepth: maxDepth ?? outline?.maxDepth ?? DEFAULT_MAX_DEPTH
354
+ };
355
+ if (levels.length === 0) return buildLevelRows(docs, 0, 0, {
356
+ ...ctx,
357
+ levels: []
358
+ });
359
+ return buildLevelRows(docs, 0, 0, ctx);
360
+ }
361
+
362
+ //#endregion
363
+ export { buildOutlineRows };
@@ -252,11 +252,14 @@ function mapListSchemaToConfig(list) {
252
252
  if (list.columns?.length) config.columns = list.columns;
253
253
  if (list.defaultSort) config.defaultSort = list.defaultSort;
254
254
  if (list.orderable) config.orderable = list.orderable;
255
- if (list.searchable?.length) {
255
+ if (Array.isArray(list.searchable) && list.searchable.length) {
256
256
  config.searchFields = list.searchable;
257
257
  config.searchable = true;
258
- }
258
+ } else if (typeof list.searchable === "boolean") config.searchable = list.searchable;
259
+ if (list.filterable?.length) config.filterable = list.filterable;
259
260
  if (list.grouping?.fields?.length) config.grouping = list.grouping;
261
+ if (list.layout) config.layout = list.layout;
262
+ if (list.outline?.levels?.length) config.outline = list.outline;
260
263
  config.actions = mapListActionsToDefinitions(list.actions);
261
264
  return config;
262
265
  }
@@ -1543,4 +1546,4 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
1543
1546
  }
1544
1547
 
1545
1548
  //#endregion
1546
- export { TableView as default };
1549
+ export { UploadCollectionButton, TableView as default, mapListSchemaToConfig, stringifyGroupValue };
@@ -1,3 +1,5 @@
1
+ import { ServerAdminShellRailConfig } from "../../../server/augmentation/shell.mjs";
2
+ import "../../../server/augmentation.mjs";
1
3
  import { AdminToasterProps } from "../../components/ui/sonner.mjs";
2
4
  import { AdminSidebarProps } from "./admin-sidebar.mjs";
3
5
  import { AdminTheme } from "./admin-theme.mjs";
@@ -73,6 +75,18 @@ interface AdminLayoutSharedProps {
73
75
  */
74
76
  layoutMode?: LayoutMode;
75
77
  }
78
+ interface AdminShellRailProps {
79
+ /** Current route path, usually the browser pathname. */
80
+ activeRoute?: string;
81
+ /** Admin base path, usually "/admin". */
82
+ basePath: string;
83
+ /** Resolved rail placement. */
84
+ placement: "left" | "right";
85
+ /** Raw rail config from server admin config. */
86
+ config: ServerAdminShellRailConfig;
87
+ /** Navigate function from the admin runtime. */
88
+ navigate: (path: string) => void;
89
+ }
76
90
  interface AdminLayoutProps extends AdminLayoutSharedProps {
77
91
  /**
78
92
  * Brand name for sidebar.
@@ -127,4 +141,4 @@ declare function AdminLayout({
127
141
  layoutMode
128
142
  }: AdminLayoutProps): React.ReactElement;
129
143
  //#endregion
130
- export { AdminLayout, AdminLayoutSharedProps };
144
+ export { AdminLayout, AdminLayoutSharedProps, AdminShellRailProps };
@@ -1,6 +1,8 @@
1
1
  import { useSafeI18n } from "../../i18n/hooks.mjs";
2
2
  import { cn } from "../../lib/utils.mjs";
3
3
  import { useAdminStore } from "../../runtime/provider.mjs";
4
+ import { ComponentRenderer } from "../../components/component-renderer.mjs";
5
+ import { useAdminConfig } from "../../hooks/use-admin-config.mjs";
4
6
  import { shouldHandleAdminShortcut } from "../../utils/keyboard-shortcuts.mjs";
5
7
  import { SidebarInset, SidebarProvider } from "../../components/ui/sidebar.mjs";
6
8
  import { Toaster } from "../../components/ui/sonner.mjs";
@@ -29,6 +31,54 @@ function useLayoutProps(props) {
29
31
  navigate: props.navigate ?? storeNavigate
30
32
  };
31
33
  }
34
+ function normalizeRoute(route) {
35
+ return route.replace(/\/+$/, "") || "/";
36
+ }
37
+ function resolveRouteRule(rule, basePath) {
38
+ if (rule.startsWith("/")) return normalizeRoute(rule);
39
+ return normalizeRoute(`${basePath}/${rule.replace(/^\/+/, "")}`);
40
+ }
41
+ function routeMatchesRule(activeRoute, rule, basePath, match) {
42
+ if (!activeRoute) return false;
43
+ const active = normalizeRoute(activeRoute);
44
+ const target = resolveRouteRule(rule, basePath);
45
+ if (match === "exact") return active === target;
46
+ return active === target || active.startsWith(`${target}/`);
47
+ }
48
+ function shouldRenderShellRail(config, activeRoute, basePath) {
49
+ const routes = config.routes;
50
+ if (!routes) return true;
51
+ const match = routes.match ?? "prefix";
52
+ if (!(!routes.include?.length || routes.include.some((rule) => routeMatchesRule(activeRoute, rule, basePath, match)))) return false;
53
+ return !routes.exclude?.some((rule) => routeMatchesRule(activeRoute, rule, basePath, match));
54
+ }
55
+ function toCssLength(value) {
56
+ if (value === void 0) return void 0;
57
+ return typeof value === "number" ? `${value}px` : value;
58
+ }
59
+ function AdminShellRail({ config, activeRoute, basePath, navigate }) {
60
+ const placement = config.placement ?? "left";
61
+ const style = {
62
+ width: toCssLength(config.width ?? 320),
63
+ minWidth: toCssLength(config.minWidth ?? config.width ?? 280),
64
+ maxWidth: toCssLength(config.maxWidth)
65
+ };
66
+ return /* @__PURE__ */ jsx("aside", {
67
+ className: cn("qa-admin-layout__secondary-rail bg-background h-svh min-h-0 shrink-0 flex-col overflow-hidden", config.hiddenOnMobile === false ? "flex" : "hidden md:flex", placement === "left" ? "border-border-subtle border-r" : "border-border-subtle border-l", config.className),
68
+ "data-placement": placement,
69
+ style,
70
+ children: /* @__PURE__ */ jsx(ComponentRenderer, {
71
+ reference: config.component,
72
+ additionalProps: {
73
+ activeRoute,
74
+ basePath,
75
+ placement,
76
+ config,
77
+ navigate
78
+ }
79
+ })
80
+ });
81
+ }
32
82
  /**
33
83
  * AdminLayout Component
34
84
  *
@@ -61,6 +111,15 @@ function AdminLayout({ LinkComponent, activeRoute, basePath = "/admin", brandNam
61
111
  const [isSearchOpen, setIsSearchOpen] = React.useState(false);
62
112
  const openSearch = React.useCallback(() => setIsSearchOpen(true), []);
63
113
  const closeSearch = React.useCallback(() => setIsSearchOpen(false), []);
114
+ const { data: serverConfig } = useAdminConfig();
115
+ const currentActiveRoute = activeRoute ?? (typeof window !== "undefined" ? window.location.pathname : void 0);
116
+ const secondaryRailConfig = serverConfig?.shell?.secondaryRail;
117
+ const secondaryRail = !!secondaryRailConfig && shouldRenderShellRail(secondaryRailConfig, currentActiveRoute, basePath) && secondaryRailConfig ? /* @__PURE__ */ jsx(AdminShellRail, {
118
+ config: secondaryRailConfig,
119
+ activeRoute: currentActiveRoute,
120
+ basePath,
121
+ navigate
122
+ }) : null;
64
123
  React.useEffect(() => {
65
124
  const down = (e) => {
66
125
  if (shouldHandleAdminShortcut(e, { key: "k" })) {
@@ -90,38 +149,43 @@ function AdminLayout({ LinkComponent, activeRoute, basePath = "/admin", brandNam
90
149
  /* @__PURE__ */ jsxs(SidebarProvider, {
91
150
  defaultOpen: !sidebarCollapsedProp,
92
151
  className: "qa-admin-layout__sidebar-wrapper bg-sidebar mx-auto h-svh max-w-[1920px] overflow-hidden",
93
- children: [/* @__PURE__ */ jsx(AdminSidebar, {
94
- LinkComponent,
95
- activeRoute,
96
- basePath,
97
- brandName,
98
- theme,
99
- setTheme,
100
- showThemeToggle,
101
- onSearchOpen: openSearch,
102
- ...sidebarProps
103
- }), /* @__PURE__ */ jsxs(SidebarInset, {
104
- className: "qa-admin-layout__content bg-background flex h-svh flex-col overflow-hidden md:rounded-tl-2xl",
105
- children: [
106
- shouldShowHeader && header && /* @__PURE__ */ jsx("header", {
107
- className: "qa-admin-layout__header border-border-subtle border-b",
108
- children: header
109
- }),
110
- /* @__PURE__ */ jsx("main", {
111
- id: "main-content",
112
- className: "qa-admin-layout__main min-w-0 flex-1 overflow-y-auto",
113
- tabIndex: -1,
114
- children: /* @__PURE__ */ jsx("div", {
115
- className: cn("qa-admin-layout__main-content min-w-0", layoutMode === "default" && "mx-auto max-w-5xl px-3 pt-1 pb-6 md:px-4 md:pt-2 md:pb-8", layoutMode === "wide" && "px-3 pt-1 pb-6 md:px-4 md:pt-2 md:pb-8", layoutMode === "full" && "px-2 pt-1 pb-6 md:px-3 md:pb-8", layoutMode === "immersive" && "p-0"),
116
- children
152
+ children: [
153
+ /* @__PURE__ */ jsx(AdminSidebar, {
154
+ LinkComponent,
155
+ activeRoute: currentActiveRoute,
156
+ basePath,
157
+ brandName,
158
+ theme,
159
+ setTheme,
160
+ showThemeToggle,
161
+ onSearchOpen: openSearch,
162
+ ...sidebarProps
163
+ }),
164
+ secondaryRailConfig?.placement !== "right" && secondaryRail,
165
+ /* @__PURE__ */ jsxs(SidebarInset, {
166
+ className: "qa-admin-layout__content bg-background flex h-svh flex-col overflow-hidden md:rounded-tl-2xl",
167
+ children: [
168
+ shouldShowHeader && header && /* @__PURE__ */ jsx("header", {
169
+ className: "qa-admin-layout__header border-border-subtle border-b",
170
+ children: header
171
+ }),
172
+ /* @__PURE__ */ jsx("main", {
173
+ id: "main-content",
174
+ className: "qa-admin-layout__main min-w-0 flex-1 overflow-y-auto",
175
+ tabIndex: -1,
176
+ children: /* @__PURE__ */ jsx("div", {
177
+ className: cn("qa-admin-layout__main-content min-w-0", layoutMode === "default" && "mx-auto max-w-5xl px-3 pt-1 pb-6 md:px-4 md:pt-2 md:pb-8", layoutMode === "wide" && "px-3 pt-1 pb-6 md:px-4 md:pt-2 md:pb-8", layoutMode === "full" && "px-2 pt-1 pb-6 md:px-3 md:pb-8", layoutMode === "immersive" && "p-0"),
178
+ children
179
+ })
180
+ }),
181
+ shouldShowFooter && footer && /* @__PURE__ */ jsx("footer", {
182
+ className: "qa-admin-layout__footer border-border-subtle border-t",
183
+ children: footer
117
184
  })
118
- }),
119
- shouldShowFooter && footer && /* @__PURE__ */ jsx("footer", {
120
- className: "qa-admin-layout__footer border-border-subtle border-t",
121
- children: footer
122
- })
123
- ]
124
- })]
185
+ ]
186
+ }),
187
+ secondaryRailConfig?.placement === "right" && secondaryRail
188
+ ]
125
189
  }),
126
190
  /* @__PURE__ */ jsx(Toaster, {
127
191
  theme,
package/dist/client.d.mts CHANGED
@@ -1,11 +1,11 @@
1
- import { BaseWidgetAdminMeta, WidgetTypeRegistry } from "./augmentation.mjs";
2
1
  import { I18nContext, I18nText } from "./client/i18n/types.mjs";
3
2
  import { BaseFieldProps, IconComponent, MaybeLazyComponent } from "./client/builder/types/common.mjs";
3
+ import { FormViewDefinition, ListViewDefinition, ViewDefinition, ViewKind, view } from "./client/builder/view/view.mjs";
4
+ import { Badge, BadgeProps, ComponentRenderer, ComponentRendererProps, ComponentRendererRegistry, IconifyIcon, IconifyIconProps, createIconRenderer, isComponentReference, resolveIconElement } from "./client/components/component-renderer.mjs";
4
5
  import { FieldDefinition, FieldInstance, configureField, field } from "./client/builder/field/field.mjs";
5
6
  import { PageDefinition, page } from "./client/builder/page/page.mjs";
6
- import { AnyWidgetConfig, BaseWidgetConfig, ChartWidgetConfig, CustomWidgetConfig, ProgressWidgetConfig, QuickActionsWidgetConfig, RecentItemsWidgetConfig, StatsWidgetConfig, TableWidgetConfig, TimelineWidgetConfig, ValueWidgetConfig, WidgetComponentProps, WidgetConfig } from "./client/builder/types/widget-types.mjs";
7
- import { FormViewDefinition, ListViewDefinition, ViewDefinition, ViewKind, view } from "./client/builder/view/view.mjs";
8
7
  import { WidgetDefinition, widget } from "./client/builder/widget/widget.mjs";
8
+ import { AnyWidgetConfig, BaseWidgetConfig, ChartWidgetConfig, CustomWidgetConfig, ProgressWidgetConfig, QuickActionsWidgetConfig, RecentItemsWidgetConfig, StatsWidgetConfig, TableWidgetConfig, TimelineWidgetConfig, ValueWidgetConfig, WidgetComponentProps, WidgetConfig } from "./client/builder/types/widget-types.mjs";
9
9
  import { Admin, AppAdmin, InferAdminCMS } from "./client/builder/admin.mjs";
10
10
  import { createAdminClient } from "./client/create-admin-client.mjs";
11
11
  import { TypedHooks, createTypedHooks } from "./client/hooks/typed-hooks.mjs";
@@ -15,7 +15,7 @@ import { BrandLogoConfig, BrandingConfig } from "./client/types/admin-config.mjs
15
15
  import { AdminProvider, AdminProviderProps, AdminState, AdminStore, selectAdmin, selectBasePath, selectClient, selectNavigate, useAdminStore } from "./client/runtime/provider.mjs";
16
16
  import { useShallow } from "./client/runtime/index.mjs";
17
17
  import { AdminSidebarBrandProps, AdminSidebarNavItemProps } from "./client/views/layout/admin-sidebar.mjs";
18
- import { AdminLayout } from "./client/views/layout/admin-layout.mjs";
18
+ import { AdminLayout, AdminShellRailProps } from "./client/views/layout/admin-layout.mjs";
19
19
  import { AdminLayoutProvider } from "./client/views/layout/admin-layout-provider.mjs";
20
20
  import { AdminRouter, AdminRouterProps } from "./client/views/layout/admin-router.mjs";
21
21
  import { BlockCategory, BlockContent, BlockNode, BlockRendererProps as BlockRendererProps$1, EMPTY_BLOCK_CONTENT, isBlockContent } from "./client/blocks/types.mjs";
@@ -33,7 +33,6 @@ import { UseCollectionPreviewOptions, UseCollectionPreviewResult, useCollectionP
33
33
  import "./client/preview/index.mjs";
34
34
  import { AuthGuard } from "./client/components/auth/auth-guard.mjs";
35
35
  import { AuthLoading } from "./client/components/auth/auth-loading.mjs";
36
- import { Badge, BadgeProps, ComponentRenderer, ComponentRendererProps, ComponentRendererRegistry, IconifyIcon, IconifyIconProps, createIconRenderer, isComponentReference, resolveIconElement } from "./client/components/component-renderer.mjs";
37
36
  import { AcceptInviteForm } from "./client/views/auth/accept-invite-form.mjs";
38
37
  import { AuthDefaultLogo, AuthLayout, AuthLayoutProps } from "./client/views/auth/auth-layout.mjs";
39
38
  import { BrandLogoMark, BrandLogoMarkProps } from "./client/components/brand-logo.mjs";
@@ -61,6 +60,7 @@ import { SetupStatus, useSetupStatus } from "./client/hooks/use-setup-status.mjs
61
60
  import { useSidebarSearchParam } from "./client/hooks/use-sidebar-search-param.mjs";
62
61
  import { AdminLink } from "./client/components/admin-link.mjs";
63
62
  import { FlagConfig, getCountryCode, getFlagConfig, getFlagUrl } from "./client/utils/locale-to-flag.mjs";
63
+ import { BaseWidgetAdminMeta, WidgetTypeRegistry } from "./augmentation.mjs";
64
64
  import { CollectionFormViewProps, CollectionListViewProps, GlobalFormViewProps } from "./client/builder/types/views.mjs";
65
65
  import { buildValidationSchema, buildZodFromIntrospection, createFormSchema } from "./client/builder/validation.mjs";
66
66
  import { FocusContextValue, FocusProvider, FocusProviderProps, FocusState, parsePreviewFieldPath, scrollFieldIntoView, useFocus, useFocusOptional, useIsBlockFocused, useIsFieldFocused } from "./client/contexts/focus-context.mjs";
@@ -105,4 +105,4 @@ type CollectionFieldKeys<TApp extends QuestpieApp, TCollectionName extends strin
105
105
  select: infer TSelect;
106
106
  } ? keyof TSelect : never : never : never;
107
107
  //#endregion
108
- export { AcceptInviteForm, AcceptInvitePage, Admin, AdminLayout, AdminLayoutProvider, AdminLink, AdminProvider, type AdminProviderProps, AdminRouter, type AdminRouterProps, type AdminSidebarBrandProps, type AdminSidebarNavItemProps, type AdminState, type AdminStore, type AdminToPreviewMessage, AnyQuestpieClient, type AnyWidgetConfig, type AppAdmin, AuthDefaultLogo, AuthGuard, AuthLayout, type AuthLayoutProps, AuthLoading, Badge, type BadgeProps, type BaseFieldProps, type BaseWidgetAdminMeta, type BaseWidgetConfig, type BlockCategory, type BlockClickedMessage, type BlockContent, type BlockNode, BlockRenderer, type BlockRendererProps as BlockRendererComponentProps, type BlockRendererProps$1 as BlockRendererProps, type BlockScopeContextValue, BlockScopeProvider, type BlockScopeProviderProps, type BrandLogoConfig, BrandLogoMark, type BrandLogoMarkProps, type BrandSnapshot, type BrandingConfig, type ChartWidgetConfig, CollectionFieldKeys, type CollectionFormViewProps, CollectionItem, type CollectionListViewProps, CollectionNames, type CommitMessage, type ComponentRegistry, ComponentRenderer, type ComponentRendererProps, type ComponentRendererRegistry, type CustomWidgetConfig, DashboardPage, EMPTY_BLOCK_CONTENT, type FieldClickedMessage, type FieldComponentProps, type FieldDefinition, type FieldInstance, type FieldValueEditedMessage, type FlagConfig, type FocusContextValue, type FocusFieldMessage, FocusProvider, type FocusProviderProps, type FocusState, ForgotPasswordForm, ForgotPasswordPage, type FormViewConfig, type FormViewDefinition, type FullResyncMessage, type GlobalFormViewProps, GlobalNames, type I18nContext, type I18nText, type IconComponent, IconifyIcon, type IconifyIconProps, type InferAdminCMS, type InitSnapshotMessage, InvitePage, type ListViewDefinition, LoginForm, LoginPage, type MaybeLazyComponent, type OptionItem, type PageDefinition, type PatchAppliedMessage, type PatchBatchMessage, PreviewBanner, type PreviewBannerProps, type PreviewConfig, PreviewField, type PreviewFieldProps, type PreviewPatchOp, PreviewProvider, type PreviewReadyMessage, type PreviewRefreshMessage, type PreviewToAdminMessage, type ProgressWidgetConfig, type QuickActionsWidgetConfig, type ReactiveFieldResult, type ReactiveFieldState, type RecentItemsWidgetConfig, type RefreshCompleteMessage, ResetPasswordForm, ResetPasswordPage, type ResyncRequestMessage, RichTextEditor, type RichTextEditorProps, RichTextRenderer, type RichTextStyles, type ScopeContextValue, type ScopeOption, ScopePicker, type ScopePickerProps, ScopeProvider, type ScopeProviderProps, type SelectBlockMessage, SetupForm, type SetupFormValues, SetupPage, type SetupPageProps, type SetupStatus, StandalonePreviewField, type StatsWidgetConfig, type TableWidgetConfig, type TimelineWidgetConfig, type TipTapDoc, type TipTapNode, type TypedHooks, type UseCollectionPreviewOptions, type UseCollectionPreviewResult, type UseFieldOptionsOptions, type UseFieldOptionsResult, type UseReactiveFieldsOptions, type UseReactiveFieldsResult, type ValueWidgetConfig, type ViewDefinition, type ViewKind, type WidgetComponentProps, type WidgetConfig, type WidgetDefinition, type WidgetTypeRegistry, buildValidationSchema, buildZodFromIntrospection, configureField, createAdminAuthClient, createAdminClient, createFormSchema, createIconRenderer, createScopedFetch, createTypedHooks, field, getCountryCode, getFlagConfig, getFlagUrl, isAdminToPreviewMessage, isBlockContent, isComponentReference, isPreviewToAdminMessage, page, parsePreviewFieldPath, resolveIconElement, scrollFieldIntoView, selectAdmin, selectBasePath, selectClient, selectNavigate, useAdminStore, useAuthClient, useBlockScope, useBrand, useBrandSnapshotRef, useCollectionCreate, useCollectionDelete, useCollectionItem, useCollectionList, useCollectionPreview, useCollectionRestore, useCollectionRevertVersion, useCollectionUpdate, useCollectionVersions, useCurrentUser, useFieldOptions, useFocus, useFocusOptional, useGlobal, useGlobalRevertVersion, useGlobalUpdate, useGlobalVersions, useIsBlockFocused, useIsDesktop, useIsFieldFocused, useIsMobile, useMediaQuery, usePreviewContext, useReactiveFields, useResolveFieldPath, useScope, useScopeSafe, useScopedFetch, useSearchParamToggle, useServerWidgetData, useSetupStatus, useShallow, useSidebarSearchParam, view, widget };
108
+ export { AcceptInviteForm, AcceptInvitePage, Admin, AdminLayout, AdminLayoutProvider, AdminLink, AdminProvider, type AdminProviderProps, AdminRouter, type AdminRouterProps, type AdminShellRailProps, type AdminSidebarBrandProps, type AdminSidebarNavItemProps, type AdminState, type AdminStore, type AdminToPreviewMessage, AnyQuestpieClient, type AnyWidgetConfig, type AppAdmin, AuthDefaultLogo, AuthGuard, AuthLayout, type AuthLayoutProps, AuthLoading, Badge, type BadgeProps, type BaseFieldProps, type BaseWidgetAdminMeta, type BaseWidgetConfig, type BlockCategory, type BlockClickedMessage, type BlockContent, type BlockNode, BlockRenderer, type BlockRendererProps as BlockRendererComponentProps, type BlockRendererProps$1 as BlockRendererProps, type BlockScopeContextValue, BlockScopeProvider, type BlockScopeProviderProps, type BrandLogoConfig, BrandLogoMark, type BrandLogoMarkProps, type BrandSnapshot, type BrandingConfig, type ChartWidgetConfig, CollectionFieldKeys, type CollectionFormViewProps, CollectionItem, type CollectionListViewProps, CollectionNames, type CommitMessage, type ComponentRegistry, ComponentRenderer, type ComponentRendererProps, type ComponentRendererRegistry, type CustomWidgetConfig, DashboardPage, EMPTY_BLOCK_CONTENT, type FieldClickedMessage, type FieldComponentProps, type FieldDefinition, type FieldInstance, type FieldValueEditedMessage, type FlagConfig, type FocusContextValue, type FocusFieldMessage, FocusProvider, type FocusProviderProps, type FocusState, ForgotPasswordForm, ForgotPasswordPage, type FormViewConfig, type FormViewDefinition, type FullResyncMessage, type GlobalFormViewProps, GlobalNames, type I18nContext, type I18nText, type IconComponent, IconifyIcon, type IconifyIconProps, type InferAdminCMS, type InitSnapshotMessage, InvitePage, type ListViewDefinition, LoginForm, LoginPage, type MaybeLazyComponent, type OptionItem, type PageDefinition, type PatchAppliedMessage, type PatchBatchMessage, PreviewBanner, type PreviewBannerProps, type PreviewConfig, PreviewField, type PreviewFieldProps, type PreviewPatchOp, PreviewProvider, type PreviewReadyMessage, type PreviewRefreshMessage, type PreviewToAdminMessage, type ProgressWidgetConfig, type QuickActionsWidgetConfig, type ReactiveFieldResult, type ReactiveFieldState, type RecentItemsWidgetConfig, type RefreshCompleteMessage, ResetPasswordForm, ResetPasswordPage, type ResyncRequestMessage, RichTextEditor, type RichTextEditorProps, RichTextRenderer, type RichTextStyles, type ScopeContextValue, type ScopeOption, ScopePicker, type ScopePickerProps, ScopeProvider, type ScopeProviderProps, type SelectBlockMessage, SetupForm, type SetupFormValues, SetupPage, type SetupPageProps, type SetupStatus, StandalonePreviewField, type StatsWidgetConfig, type TableWidgetConfig, type TimelineWidgetConfig, type TipTapDoc, type TipTapNode, type TypedHooks, type UseCollectionPreviewOptions, type UseCollectionPreviewResult, type UseFieldOptionsOptions, type UseFieldOptionsResult, type UseReactiveFieldsOptions, type UseReactiveFieldsResult, type ValueWidgetConfig, type ViewDefinition, type ViewKind, type WidgetComponentProps, type WidgetConfig, type WidgetDefinition, type WidgetTypeRegistry, buildValidationSchema, buildZodFromIntrospection, configureField, createAdminAuthClient, createAdminClient, createFormSchema, createIconRenderer, createScopedFetch, createTypedHooks, field, getCountryCode, getFlagConfig, getFlagUrl, isAdminToPreviewMessage, isBlockContent, isComponentReference, isPreviewToAdminMessage, page, parsePreviewFieldPath, resolveIconElement, scrollFieldIntoView, selectAdmin, selectBasePath, selectClient, selectNavigate, useAdminStore, useAuthClient, useBlockScope, useBrand, useBrandSnapshotRef, useCollectionCreate, useCollectionDelete, useCollectionItem, useCollectionList, useCollectionPreview, useCollectionRestore, useCollectionRevertVersion, useCollectionUpdate, useCollectionVersions, useCurrentUser, useFieldOptions, useFocus, useFocusOptional, useGlobal, useGlobalRevertVersion, useGlobalUpdate, useGlobalVersions, useIsBlockFocused, useIsDesktop, useIsFieldFocused, useIsMobile, useMediaQuery, usePreviewContext, useReactiveFields, useResolveFieldPath, useScope, useScopeSafe, useScopedFetch, useSearchParamToggle, useServerWidgetData, useSetupStatus, useShallow, useSidebarSearchParam, view, widget };
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime20 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime19 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/components/rich-text/rich-text-renderer.d.ts
4
4
  /**
@@ -98,6 +98,6 @@ declare function RichTextRenderer({
98
98
  content,
99
99
  styles: customStyles,
100
100
  className
101
- }: RichTextRendererProps): react_jsx_runtime20.JSX.Element | null;
101
+ }: RichTextRendererProps): react_jsx_runtime19.JSX.Element | null;
102
102
  //#endregion
103
103
  export { RichTextRenderer, RichTextStyles, TipTapDoc, TipTapNode };