@abraca/dabra 1.8.2 → 2.0.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 (37) hide show
  1. package/dist/abracadabra-provider.cjs +12722 -9050
  2. package/dist/abracadabra-provider.cjs.map +1 -1
  3. package/dist/abracadabra-provider.esm.js +12683 -9061
  4. package/dist/abracadabra-provider.esm.js.map +1 -1
  5. package/dist/index.d.ts +1485 -118
  6. package/package.json +1 -1
  7. package/src/AbracadabraBaseProvider.ts +51 -2
  8. package/src/AbracadabraClient.ts +516 -66
  9. package/src/AbracadabraProvider.ts +22 -7
  10. package/src/AbracadabraWS.ts +1 -1
  11. package/src/ChatClient.ts +193 -113
  12. package/src/ContentManager.ts +228 -0
  13. package/src/CryptoIdentityKeystore.ts +3 -3
  14. package/src/DocConverters.ts +1862 -0
  15. package/src/DocKeyManager.ts +60 -12
  16. package/src/DocTypes.ts +628 -0
  17. package/src/DocUtils.ts +89 -0
  18. package/src/DocumentManager.ts +319 -0
  19. package/src/E2EAbracadabraProvider.ts +189 -0
  20. package/src/EncryptedChatClient.ts +173 -0
  21. package/src/EncryptedY.ts +2 -2
  22. package/src/FileBlobStore.ts +10 -0
  23. package/src/IdentityDoc.ts +25 -0
  24. package/src/MetaManager.ts +100 -0
  25. package/src/MnemonicKeyDerivation.ts +4 -4
  26. package/src/NotificationsClient.ts +120 -98
  27. package/src/OutgoingMessages/SubdocMessage.ts +2 -2
  28. package/src/RpcClient.ts +659 -0
  29. package/src/TreeManager.ts +473 -0
  30. package/src/TreeTimestamps.ts +28 -25
  31. package/src/index.ts +71 -1
  32. package/src/messageRecord.ts +121 -0
  33. package/src/types.ts +174 -16
  34. package/src/webrtc/AbracadabraWebRTC.ts +2 -2
  35. package/src/webrtc/DataChannelRouter.ts +2 -2
  36. package/src/webrtc/E2EEChannel.ts +3 -3
  37. package/src/webrtc/FileTransferChannel.ts +9 -2
@@ -0,0 +1,628 @@
1
+ /**
2
+ * Canonical type definitions for Abracadabra document tree entries, page
3
+ * metadata, and the static page-type catalog.
4
+ *
5
+ * These types were previously scattered across `mcp/converters/types.ts` and
6
+ * `mcp/converters/page-types.ts`. By living in the provider package they
7
+ * become first-class SDK types that any consumer (`@abraca/dabra`) can import
8
+ * directly, removing the need for fragile relative-path re-exports.
9
+ */
10
+
11
+ // ── Meta field primitives ────────────────────────────────────────────────────
12
+
13
+ export type MetaFieldType
14
+ = | 'datetimerange'
15
+ | 'daterange'
16
+ | 'timerange'
17
+ | 'datetime'
18
+ | 'date'
19
+ | 'time'
20
+ | 'slider'
21
+ | 'number'
22
+ | 'toggle'
23
+ | 'select'
24
+ | 'multiselect'
25
+ | 'colorPreset'
26
+ | 'colorPicker'
27
+ | 'location'
28
+ | 'icon'
29
+ | 'textarea'
30
+ | 'url'
31
+ | 'rating'
32
+ | 'tags'
33
+ | 'members'
34
+
35
+ export interface UserMetaField {
36
+ id: string
37
+ type: string
38
+ label?: string
39
+ key?: string
40
+ latKey?: string
41
+ lngKey?: string
42
+ startKey?: string
43
+ endKey?: string
44
+ allDayKey?: string
45
+ presets?: string[]
46
+ options?: string[]
47
+ min?: number
48
+ max?: number
49
+ step?: number
50
+ unit?: string
51
+ default?: boolean
52
+ }
53
+
54
+ // ── PageMeta — the full union of all metadata fields ─────────────────────────
55
+
56
+ export interface PageMeta extends Record<string, unknown> {
57
+ // Universal display
58
+ color?: string
59
+ icon?: string
60
+ subtitle?: string
61
+ note?: string
62
+
63
+ // Datetime
64
+ datetimeStart?: string
65
+ datetimeEnd?: string
66
+ allDay?: boolean
67
+ dateStart?: string
68
+ dateEnd?: string
69
+ timeStart?: string
70
+ timeEnd?: string
71
+ dateTaken?: string
72
+
73
+ // Task/status
74
+ checked?: boolean
75
+ priority?: number
76
+ status?: string
77
+ taskProgress?: number
78
+ rating?: number
79
+ tags?: string[]
80
+ members?: { id: string; label: string }[]
81
+
82
+ // Contact/value
83
+ url?: string
84
+ email?: string
85
+ phone?: string
86
+ number?: number
87
+ unit?: string
88
+
89
+ // Cover image
90
+ coverUploadId?: string
91
+ coverDocId?: string
92
+ coverMimeType?: string
93
+
94
+ // Geo/Map
95
+ geoType?: 'marker' | 'line' | 'measure'
96
+ geoLat?: number
97
+ geoLng?: number
98
+ geoDescription?: string
99
+
100
+ // Dashboard
101
+ deskX?: number
102
+ deskY?: number
103
+ deskZ?: number
104
+ deskMode?: string
105
+
106
+ // Mindmap
107
+ mmX?: number
108
+ mmY?: number
109
+
110
+ // Graph
111
+ graphX?: number
112
+ graphY?: number
113
+ graphPinned?: boolean
114
+
115
+ // Spatial (3D) — spatial uses the universal `color` key (no spColor)
116
+ spShape?: string
117
+ spOpacity?: number
118
+ spX?: number
119
+ spY?: number
120
+ spZ?: number
121
+ spRX?: number
122
+ spRY?: number
123
+ spRZ?: number
124
+ spSX?: number
125
+ spSY?: number
126
+ spSZ?: number
127
+ spModelUploadId?: string
128
+ spModelDocId?: string
129
+
130
+ // Slides
131
+ slidesTransition?: string
132
+ slidesTheme?: string
133
+
134
+ // Media metadata (extracted on import)
135
+ mediaDuration?: number
136
+ mediaWidth?: number
137
+ mediaHeight?: number
138
+ mediaCamera?: string
139
+ mediaLens?: string
140
+ mediaIso?: number
141
+ mediaFocalLength?: number
142
+ mediaAperture?: number
143
+ mediaShutterSpeed?: string
144
+ mediaArtist?: string
145
+ mediaAlbum?: string
146
+ mediaGenre?: string
147
+ mediaYear?: number
148
+
149
+ // Sheets cell formatting
150
+ bold?: boolean
151
+ italic?: boolean
152
+ textColor?: string
153
+ bgColor?: string
154
+ align?: string
155
+ formula?: string
156
+
157
+ // Coder plugin (per-file)
158
+ fileType?: string
159
+ entry?: boolean
160
+
161
+ // Renderer config (set on the page doc itself, not children)
162
+ kanbanColumnWidth?: string
163
+ galleryColumns?: number
164
+ galleryAspect?: string
165
+ galleryCardStyle?: string
166
+ galleryShowLabels?: boolean
167
+ gallerySortBy?: string
168
+ calendarView?: string
169
+ calendarWeekStart?: string
170
+ calendarShowWeekNumbers?: boolean
171
+ tableMode?: string
172
+ tableSortKey?: string
173
+ tableSortDir?: string
174
+ tableColumns?: any[]
175
+ tableColumnWidths?: Record<string, number>
176
+ tableColumnOrder?: string[]
177
+ timelineZoom?: string
178
+ timelinePixelsPerDay?: number
179
+ timelineCenterDate?: string
180
+ checklistFilter?: string
181
+ checklistSort?: string
182
+ mapShowLabels?: boolean
183
+ spatialGridVisible?: boolean
184
+ graphSpacing?: string
185
+ graphShowLabels?: boolean
186
+ graphEdgeThickness?: string
187
+ mmSpacing?: string
188
+ showRefEdges?: boolean
189
+ chartType?: string
190
+ chartMetric?: string
191
+ chartColorScheme?: string
192
+ chartLimit?: number
193
+ chartShowLegend?: boolean
194
+ chartShowValues?: boolean
195
+ mediaRepeat?: string
196
+ mediaShuffle?: boolean
197
+ sheetsDefaultColWidth?: number
198
+ sheetsDefaultRowHeight?: number
199
+ sheetsShowGridlines?: boolean
200
+ sheetsColumnWidths?: Record<string, number>
201
+ sheetsRowHeights?: Record<string, number>
202
+ sheetsFreezeRows?: number
203
+ sheetsFreezeCols?: number
204
+
205
+ // Internal
206
+ _metaFields?: UserMetaField[]
207
+ _metaInitialized?: boolean
208
+ }
209
+
210
+ // ── Tree entry (flat row in the doc-tree Y.Map) ──────────────────────────────
211
+
212
+ export interface TreeEntry {
213
+ id: string
214
+ label: string
215
+ parentId: string | null
216
+ order: number
217
+ type?: string
218
+ meta?: PageMeta
219
+ createdAt?: number
220
+ updatedAt?: number
221
+ }
222
+
223
+ // ── Tree node (nested, for buildTree output) ─────────────────────────────────
224
+
225
+ export interface TreeNode {
226
+ id: string
227
+ label: string
228
+ type?: string
229
+ meta?: PageMeta
230
+ order: number
231
+ children: TreeNode[]
232
+ }
233
+
234
+ // ── Search result (from TreeManager.find) ────────────────────────────────────
235
+
236
+ export interface TreeSearchResult {
237
+ id: string
238
+ label: string
239
+ type?: string
240
+ meta?: PageMeta
241
+ /** Ancestor labels from root → parent. */
242
+ path: string[]
243
+ }
244
+
245
+ // ── Page type catalog ────────────────────────────────────────────────────────
246
+
247
+ export interface PageTypeMetaField {
248
+ type: MetaFieldType
249
+ label?: string
250
+ /** single-value fields */
251
+ key?: string
252
+ /** location */
253
+ latKey?: string
254
+ lngKey?: string
255
+ /** ranges */
256
+ startKey?: string
257
+ endKey?: string
258
+ allDayKey?: string
259
+ /** color preset */
260
+ presets?: string[]
261
+ /** icon / select / tags */
262
+ options?: string[]
263
+ /** slider / number */
264
+ min?: number
265
+ max?: number
266
+ step?: number
267
+ unit?: string
268
+ /** toggle default */
269
+ default?: boolean
270
+ }
271
+
272
+ export interface PageTypeInfo {
273
+ key: string
274
+ label: string
275
+ icon: string
276
+ description?: string
277
+ /** true = core type (always available); false = requires plugin */
278
+ core: boolean
279
+ plugin?: string
280
+ supportsChildren: boolean
281
+ childLabel?: string
282
+ grandchildLabel?: string
283
+ /** -1 = unlimited depth */
284
+ defaultDepth?: number
285
+ /** Fields that apply to this doc's descendants (children, grandchildren, ...) */
286
+ metaSchema?: PageTypeMetaField[]
287
+ /** Fields written to this doc's own meta on first render (renderer config) */
288
+ defaultMetaFields?: PageTypeMetaField[]
289
+ }
290
+
291
+ // ── Geo type meta schemas ────────────────────────────────────────────────────
292
+
293
+ const GEO_COLOR_PRESETS = ['#3b82f6', '#f97316', '#22c55e', '#ef4444', '#a855f7']
294
+ const GEO_ICON_OPTIONS = [
295
+ 'map-pin', 'star', 'flag', 'home', 'building-2', 'coffee', 'utensils',
296
+ 'camera', 'heart', 'zap', 'triangle-alert', 'car', 'plane', 'anchor',
297
+ 'tree-pine', 'mountain', 'waves', 'shield', 'crosshair', 'circle-dot',
298
+ 'bookmark', 'gem', 'radio', 'compass',
299
+ ]
300
+
301
+ export const GEO_TYPE_META_SCHEMAS: Record<string, PageTypeMetaField[]> = {
302
+ marker: [
303
+ { type: 'location', latKey: 'geoLat', lngKey: 'geoLng', label: 'Location' },
304
+ { type: 'icon', key: 'icon', options: GEO_ICON_OPTIONS, label: 'Icon' },
305
+ { type: 'colorPreset', key: 'color', presets: GEO_COLOR_PRESETS, label: 'Color' },
306
+ ],
307
+ line: [
308
+ { type: 'colorPreset', key: 'color', presets: GEO_COLOR_PRESETS, label: 'Color' },
309
+ ],
310
+ measure: [],
311
+ }
312
+
313
+ // ── Static page type registry ────────────────────────────────────────────────
314
+
315
+ export const PAGE_TYPES: Record<string, PageTypeInfo> = {
316
+ doc: {
317
+ key: 'doc',
318
+ label: 'Document',
319
+ icon: 'file-text',
320
+ description: 'Rich text document with real-time collaboration',
321
+ core: true,
322
+ supportsChildren: true,
323
+ },
324
+ prose: {
325
+ key: 'prose',
326
+ label: 'Prose',
327
+ icon: 'pen-tool',
328
+ description: 'Long-form prose with serif typography and a narrow readable measure',
329
+ core: true,
330
+ supportsChildren: true,
331
+ childLabel: 'Item',
332
+ defaultDepth: -1,
333
+ },
334
+ kanban: {
335
+ key: 'kanban',
336
+ label: 'Kanban',
337
+ icon: 'kanban',
338
+ description: 'Drag-and-drop task board with columns and cards',
339
+ core: true,
340
+ supportsChildren: true,
341
+ childLabel: 'Column',
342
+ grandchildLabel: 'Card',
343
+ defaultMetaFields: [
344
+ { type: 'select', key: 'kanbanColumnWidth', options: ['narrow', 'default', 'wide'], label: 'Column Width' },
345
+ ],
346
+ },
347
+ gallery: {
348
+ key: 'gallery',
349
+ label: 'Gallery',
350
+ icon: 'images',
351
+ description: 'Visual grid of items with rich content',
352
+ core: true,
353
+ supportsChildren: true,
354
+ childLabel: 'Item',
355
+ metaSchema: [
356
+ { type: 'location', latKey: 'geoLat', lngKey: 'geoLng', label: 'Location' },
357
+ { type: 'datetime', key: 'datetimeStart', label: 'Date' },
358
+ { type: 'tags', key: 'tags', label: 'Tags' },
359
+ { type: 'rating', key: 'rating', max: 5, label: 'Rating' },
360
+ { type: 'icon', key: 'icon', label: 'Icon' },
361
+ { type: 'colorPreset', key: 'color', presets: ['#6366f1', '#ec4899', '#f97316', '#22c55e', '#3b82f6', '#a855f7'], label: 'Color' },
362
+ ],
363
+ defaultMetaFields: [
364
+ { type: 'number', key: 'galleryColumns', min: 1, max: 6, step: 1, label: 'Columns' },
365
+ { type: 'select', key: 'galleryAspect', options: ['square', '4:3', '3:2', '16:9', 'free'], label: 'Aspect Ratio' },
366
+ { type: 'select', key: 'galleryCardStyle', options: ['default', 'compact', 'detailed'], label: 'Card Style' },
367
+ { type: 'toggle', key: 'galleryShowLabels', label: 'Show Labels' },
368
+ { type: 'select', key: 'gallerySortBy', options: ['manual', 'date', 'name', 'rating'], label: 'Sort' },
369
+ ],
370
+ },
371
+ table: {
372
+ key: 'table',
373
+ label: 'Table',
374
+ icon: 'table',
375
+ description: 'Collaborative spreadsheet with custom fields',
376
+ core: true,
377
+ supportsChildren: true,
378
+ childLabel: 'Column',
379
+ grandchildLabel: 'Row',
380
+ defaultMetaFields: [
381
+ { type: 'select', key: 'tableMode', options: ['hierarchy', 'flat'], label: 'Mode' },
382
+ { type: 'select', key: 'tableSortDir', options: ['asc', 'desc'], label: 'Sort Direction' },
383
+ ],
384
+ },
385
+ outline: {
386
+ key: 'outline',
387
+ label: 'Outline',
388
+ icon: 'list-tree',
389
+ description: 'Hierarchical outline with keyboard navigation',
390
+ core: true,
391
+ supportsChildren: true,
392
+ childLabel: 'Item',
393
+ defaultDepth: -1,
394
+ },
395
+ checklist: {
396
+ key: 'checklist',
397
+ label: 'Checklist',
398
+ icon: 'check-square',
399
+ description: 'Collaborative checklist with sub-tasks, drag-and-drop, and due dates',
400
+ core: true,
401
+ supportsChildren: true,
402
+ childLabel: 'Task',
403
+ defaultDepth: -1,
404
+ metaSchema: [
405
+ { type: 'toggle', key: 'checked', label: 'Done' },
406
+ { type: 'select', key: 'priority', options: ['none', 'low', 'medium', 'high'], label: 'Priority' },
407
+ { type: 'date', key: 'dateEnd', label: 'Due date' },
408
+ ],
409
+ defaultMetaFields: [
410
+ { type: 'select', key: 'checklistFilter', options: ['all', 'active', 'completed'], label: 'Filter' },
411
+ { type: 'select', key: 'checklistSort', options: ['manual', 'priority', 'due'], label: 'Sort' },
412
+ ],
413
+ },
414
+ graph: {
415
+ key: 'graph',
416
+ label: 'Graph',
417
+ icon: 'git-fork',
418
+ description: 'Force-directed knowledge graph — full document tree as nodes & edges',
419
+ core: true,
420
+ supportsChildren: true,
421
+ childLabel: 'Node',
422
+ defaultMetaFields: [
423
+ { type: 'toggle', key: 'showRefEdges', label: 'Show Ref Edges', default: true },
424
+ ],
425
+ },
426
+ timeline: {
427
+ key: 'timeline',
428
+ label: 'Timeline',
429
+ icon: 'gantt-chart',
430
+ description: 'Gantt-style project timeline with epics and tasks',
431
+ core: true,
432
+ supportsChildren: true,
433
+ childLabel: 'Epic',
434
+ grandchildLabel: 'Task',
435
+ metaSchema: [
436
+ { type: 'daterange', startKey: 'dateStart', endKey: 'dateEnd' },
437
+ { type: 'slider', key: 'taskProgress', min: 0, max: 100, label: 'Progress' },
438
+ { type: 'colorPreset', key: 'color', presets: ['#6366f1', '#818cf8', '#f97316', '#22c55e', '#3b82f6', '#a855f7'], label: 'Color' },
439
+ ],
440
+ },
441
+ calendar: {
442
+ key: 'calendar',
443
+ label: 'Calendar',
444
+ icon: 'calendar',
445
+ description: 'Event calendar with month, week, and day views',
446
+ core: true,
447
+ supportsChildren: true,
448
+ childLabel: 'Event',
449
+ metaSchema: [
450
+ { type: 'datetimerange', startKey: 'datetimeStart', endKey: 'datetimeEnd', allDayKey: 'allDay' },
451
+ { type: 'colorPreset', key: 'color', presets: ['#6366f1', '#ec4899', '#f97316', '#22c55e', '#3b82f6', '#a855f7'], label: 'Color' },
452
+ { type: 'icon', key: 'icon', label: 'Icon' },
453
+ ],
454
+ defaultMetaFields: [
455
+ { type: 'select', key: 'calendarWeekStart', options: ['sun', 'mon'], label: 'Week Starts' },
456
+ { type: 'select', key: 'calendarView', options: ['month', 'week', 'day'], label: 'Default View' },
457
+ { type: 'toggle', key: 'calendarShowWeekNumbers', label: 'Show Week Numbers' },
458
+ ],
459
+ },
460
+ map: {
461
+ key: 'map',
462
+ label: 'Map',
463
+ icon: 'map',
464
+ description: 'Collaborative world map with shared markers',
465
+ core: true,
466
+ supportsChildren: true,
467
+ childLabel: 'Location',
468
+ defaultMetaFields: [
469
+ { type: 'toggle', key: 'mapShowLabels', label: 'Show Labels', default: true },
470
+ ],
471
+ },
472
+ dashboard: {
473
+ key: 'dashboard',
474
+ label: 'Dashboard',
475
+ icon: 'layout-dashboard',
476
+ description: 'Arrange documents as draggable icons with optional widget views',
477
+ core: true,
478
+ supportsChildren: true,
479
+ childLabel: 'Item',
480
+ },
481
+ call: {
482
+ key: 'call',
483
+ label: 'Call',
484
+ icon: 'phone',
485
+ description: 'Video call room with live audio and screen sharing',
486
+ core: true,
487
+ supportsChildren: false,
488
+ },
489
+ chart: {
490
+ key: 'chart',
491
+ label: 'Chart',
492
+ icon: 'bar-chart-3',
493
+ description: 'Charts — manual data points or aggregation over document trees',
494
+ core: true,
495
+ supportsChildren: true,
496
+ childLabel: 'Data Point',
497
+ grandchildLabel: 'Data Point',
498
+ metaSchema: [
499
+ { type: 'number', key: 'number', step: 0.01, label: 'Value' },
500
+ { type: 'colorPreset', key: 'color', presets: ['#6366f1', '#ec4899', '#f97316', '#22c55e', '#3b82f6', '#a855f7', '#14b8a6', '#eab308'], label: 'Color' },
501
+ { type: 'tags', key: 'tags', label: 'Tags' },
502
+ ],
503
+ defaultMetaFields: [
504
+ { type: 'select', key: 'chartType', options: ['bar', 'stacked bar', 'line', 'donut', 'treemap'], label: 'Chart Type' },
505
+ { type: 'select', key: 'chartMetric', options: ['value', 'type', 'tag', 'status', 'priority', 'activity', 'completion'], label: 'Metric' },
506
+ { type: 'select', key: 'chartColorScheme', options: ['default', 'warm', 'cool', 'mono'], label: 'Colors' },
507
+ { type: 'number', key: 'chartLimit', min: 3, max: 30, step: 1, label: 'Max Items' },
508
+ { type: 'toggle', key: 'chartShowLegend', label: 'Show Legend', default: true },
509
+ { type: 'toggle', key: 'chartShowValues', label: 'Show Values' },
510
+ ],
511
+ },
512
+ sheets: {
513
+ key: 'sheets',
514
+ label: 'Sheets',
515
+ icon: 'grid-3x3',
516
+ description: 'Spreadsheet — cells, formulas, and formatting in a collaborative grid',
517
+ core: true,
518
+ supportsChildren: true,
519
+ childLabel: 'Column',
520
+ grandchildLabel: 'Cell',
521
+ defaultMetaFields: [
522
+ { type: 'number', key: 'sheetsDefaultColWidth', min: 40, max: 500, step: 10, label: 'Column Width' },
523
+ { type: 'number', key: 'sheetsDefaultRowHeight', min: 20, max: 100, step: 2, label: 'Row Height' },
524
+ { type: 'toggle', key: 'sheetsShowGridlines', label: 'Gridlines' },
525
+ ],
526
+ },
527
+ slides: {
528
+ key: 'slides',
529
+ label: 'Slides',
530
+ icon: 'presentation',
531
+ description: 'Presentation slides with two-axis navigation',
532
+ core: true,
533
+ supportsChildren: true,
534
+ childLabel: 'Slide',
535
+ grandchildLabel: 'Sub-slide',
536
+ metaSchema: [
537
+ { type: 'select', key: 'slidesTransition', options: ['none', 'fade', 'slide'], label: 'Transition' },
538
+ { type: 'colorPreset', key: 'color', presets: ['#6366f1', '#ec4899', '#f97316', '#22c55e', '#3b82f6', '#a855f7'], label: 'Accent' },
539
+ ],
540
+ defaultMetaFields: [
541
+ { type: 'select', key: 'slidesTheme', options: ['dark', 'light'], label: 'Theme' },
542
+ ],
543
+ },
544
+ overview: {
545
+ key: 'overview',
546
+ label: 'Overview',
547
+ icon: 'radar',
548
+ description: 'Space home — activity, people, stats, and health at a glance',
549
+ core: true,
550
+ supportsChildren: true,
551
+ childLabel: 'Page',
552
+ },
553
+
554
+ // Plugin-contributed types — require the named plugin to be enabled server-side.
555
+
556
+ spatial: {
557
+ key: 'spatial',
558
+ label: 'Spatial',
559
+ icon: 'box',
560
+ description: '3D scene with collaborative objects and real-time presence',
561
+ core: false,
562
+ plugin: 'spatial',
563
+ supportsChildren: true,
564
+ childLabel: 'Object',
565
+ grandchildLabel: 'Part',
566
+ defaultDepth: -1,
567
+ metaSchema: [
568
+ { type: 'select', key: 'spShape', options: ['box', 'sphere', 'cylinder', 'cone', 'plane', 'torus', 'glb'], label: 'Shape' },
569
+ { type: 'colorPreset', key: 'color', presets: ['#6366f1', '#ef4444', '#22c55e', '#3b82f6', '#f97316', '#a855f7', '#ec4899', '#14b8a6'], label: 'Color' },
570
+ { type: 'slider', key: 'spOpacity', min: 0, max: 100, label: 'Opacity' },
571
+ { type: 'number', key: 'spX', step: 0.1, label: 'X' },
572
+ { type: 'number', key: 'spY', step: 0.1, label: 'Y' },
573
+ { type: 'number', key: 'spZ', step: 0.1, label: 'Z' },
574
+ { type: 'number', key: 'spRX', min: -180, max: 180, step: 1, label: 'Rot X' },
575
+ { type: 'number', key: 'spRY', min: -180, max: 180, step: 1, label: 'Rot Y' },
576
+ { type: 'number', key: 'spRZ', min: -180, max: 180, step: 1, label: 'Rot Z' },
577
+ { type: 'number', key: 'spSX', min: 0.01, max: 100, step: 0.1, label: 'Scale X' },
578
+ { type: 'number', key: 'spSY', min: 0.01, max: 100, step: 0.1, label: 'Scale Y' },
579
+ { type: 'number', key: 'spSZ', min: 0.01, max: 100, step: 0.1, label: 'Scale Z' },
580
+ ],
581
+ defaultMetaFields: [
582
+ { type: 'toggle', key: 'spatialGridVisible', label: 'Show Grid', default: true },
583
+ ],
584
+ },
585
+ media: {
586
+ key: 'media',
587
+ label: 'Media',
588
+ icon: 'disc-3',
589
+ description: 'Media player with synced listening and playlists',
590
+ core: false,
591
+ plugin: 'media',
592
+ supportsChildren: true,
593
+ childLabel: 'Track',
594
+ defaultDepth: -1,
595
+ metaSchema: [
596
+ { type: 'tags', key: 'tags', label: 'Tags' },
597
+ ],
598
+ defaultMetaFields: [
599
+ { type: 'select', key: 'mediaRepeat', options: ['off', 'all', 'one'], label: 'Repeat' },
600
+ { type: 'toggle', key: 'mediaShuffle', label: 'Shuffle' },
601
+ ],
602
+ },
603
+ coder: {
604
+ key: 'coder',
605
+ label: 'Coder',
606
+ icon: 'code-2',
607
+ description: 'Collaborative multi-file coding environment',
608
+ core: false,
609
+ plugin: 'coder',
610
+ supportsChildren: true,
611
+ childLabel: 'File',
612
+ defaultDepth: -1,
613
+ metaSchema: [
614
+ { type: 'select', key: 'fileType', options: ['vue', 'ts', 'js', 'css', 'json', 'folder'], label: 'Type' },
615
+ { type: 'toggle', key: 'entry', label: 'Entry Point' },
616
+ ],
617
+ },
618
+ }
619
+
620
+ export const TYPE_ALIASES: Record<string, string> = {
621
+ desktop: 'dashboard',
622
+ }
623
+
624
+ export function resolvePageType(key: string | undefined): PageTypeInfo | undefined {
625
+ if (!key) return undefined
626
+ const resolved = TYPE_ALIASES[key] ?? key
627
+ return PAGE_TYPES[resolved]
628
+ }