@mosetta/editor 0.0.0-stage → 0.0.29

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 (115) hide show
  1. package/CHANGELOG.md +190 -0
  2. package/LICENSE +21 -0
  3. package/README.md +10 -2
  4. package/mosetta.json +6 -0
  5. package/package.json +35 -3
  6. package/prefabs/workbench/prefab.json +11 -0
  7. package/prefabs/workbench/versions/1.json +120 -0
  8. package/prefabs/workbench/versions/2.json +13 -0
  9. package/prefabs/workbench/versions/3.json +19 -0
  10. package/prefabs/workbench/versions/4.json +29 -0
  11. package/prefabs/workbench/versions/5.json +29 -0
  12. package/tiles/canvas/canvas.component.tsx +2038 -0
  13. package/tiles/canvas/canvas.css +973 -0
  14. package/tiles/canvas/canvasTools.ts +66 -0
  15. package/tiles/canvas/chromeNodes.ts +35 -0
  16. package/tiles/canvas/ghosts.ts +33 -0
  17. package/tiles/canvas/insertPopover.tsx +56 -0
  18. package/tiles/canvas/popoverPlacement.ts +104 -0
  19. package/tiles/canvas/popoverSize.ts +87 -0
  20. package/tiles/canvas/providerChrome.tsx +191 -0
  21. package/tiles/canvas/scrollWatch.ts +43 -0
  22. package/tiles/canvas/settingsPopover.tsx +257 -0
  23. package/tiles/canvas/settingsTabs.ts +78 -0
  24. package/tiles/canvas/toolPanel.tsx +86 -0
  25. package/tiles/content/autoBind.ts +61 -0
  26. package/tiles/content/blockPage.css +390 -0
  27. package/tiles/content/blockPage.tsx +379 -0
  28. package/tiles/content/blockPicker.css +215 -0
  29. package/tiles/content/blockPicker.tsx +501 -0
  30. package/tiles/content/blockUpgrades.ts +145 -0
  31. package/tiles/content/blockVariants.ts +185 -0
  32. package/tiles/content/blockVocabulary.ts +77 -0
  33. package/tiles/content/catalogVersions.ts +106 -0
  34. package/tiles/content/componentPreview.css +82 -0
  35. package/tiles/content/componentPreview.tsx +249 -0
  36. package/tiles/content/content.server.ts +593 -0
  37. package/tiles/content/contentCommon.ts +133 -0
  38. package/tiles/content/disconnected.ts +56 -0
  39. package/tiles/content/distributions.css +256 -0
  40. package/tiles/content/distributions.tsx +271 -0
  41. package/tiles/content/editingAutosave.ts +125 -0
  42. package/tiles/content/editingClipboard.ts +185 -0
  43. package/tiles/content/editingConflicts.ts +223 -0
  44. package/tiles/content/editingHotkeys.ts +59 -0
  45. package/tiles/content/editingNotices.ts +105 -0
  46. package/tiles/content/formRoot.ts +18 -0
  47. package/tiles/content/fuzzySearch.ts +121 -0
  48. package/tiles/content/mediaUpload.ts +55 -0
  49. package/tiles/content/moduleSiblings.ts +9 -0
  50. package/tiles/content/mountedTree.ts +136 -0
  51. package/tiles/content/nodeSettings.css +483 -0
  52. package/tiles/content/nodeSettings.tsx +963 -0
  53. package/tiles/content/pasteText.ts +131 -0
  54. package/tiles/content/prefabFields.tsx +158 -0
  55. package/tiles/content/prefabParts.ts +105 -0
  56. package/tiles/content/previewEnvironment.ts +46 -0
  57. package/tiles/content/projectConfig.ts +46 -0
  58. package/tiles/content/projectPrefabs.ts +57 -0
  59. package/tiles/content/publishModal.css +109 -0
  60. package/tiles/content/publishModal.tsx +92 -0
  61. package/tiles/content/refsEditing.ts +412 -0
  62. package/tiles/content/scaledStage.ts +63 -0
  63. package/tiles/content/searchField.css +32 -0
  64. package/tiles/content/searchField.tsx +32 -0
  65. package/tiles/content/settingsDraft.ts +178 -0
  66. package/tiles/content/settingsIssues.tsx +124 -0
  67. package/tiles/content/themeScope.ts +72 -0
  68. package/tiles/content/themeVariables.ts +47 -0
  69. package/tiles/content/tooltipPlacement.ts +17 -0
  70. package/tiles/content/tooltips.tsx +131 -0
  71. package/tiles/content/treeMounter.ts +33 -0
  72. package/tiles/content/updatesModal.css +124 -0
  73. package/tiles/content/updatesModal.tsx +110 -0
  74. package/tiles/content/useCtrlHeld.ts +24 -0
  75. package/tiles/content/useEditingSession.ts +115 -0
  76. package/tiles/content/useMountedTree.ts +47 -0
  77. package/tiles/content/workbenchScale.css +27 -0
  78. package/tiles/hostFallback/hostFallback.component.tsx +32 -0
  79. package/tiles/hostFallback/hostFallback.css +12 -0
  80. package/tiles/hostFallback/hostFallback.editor.tsx +10 -0
  81. package/tiles/page/page.component.tsx +7 -0
  82. package/tiles/page/page.script.tsx +5 -0
  83. package/tiles/pageModules/pageModules.component.tsx +740 -0
  84. package/tiles/pageModules/pageModules.css +383 -0
  85. package/tiles/pageModules/pageModules.icon.svg +1 -0
  86. package/tiles/pageModules/servedPage.ts +465 -0
  87. package/tiles/pagesPanel/pagesPanel.component.tsx +1645 -0
  88. package/tiles/pagesPanel/pagesPanel.css +713 -0
  89. package/tiles/pagesPanel/pagesPanel.editor.tsx +23 -0
  90. package/tiles/pagesPanel/pagesSearch.ts +194 -0
  91. package/tiles/pagesPanel/routeTree.ts +98 -0
  92. package/tiles/previewBridge/landing.ts +66 -0
  93. package/tiles/previewBridge/previewAgent.ts +457 -0
  94. package/tiles/previewBridge/previewBridge.component.tsx +137 -0
  95. package/tiles/previewBridge/previewBridge.editor.tsx +10 -0
  96. package/tiles/previewBridge/previewBridgeCommon.ts +172 -0
  97. package/tiles/previewBridge/standIn.ts +22 -0
  98. package/tiles/treeView/treeView.component.tsx +514 -0
  99. package/tiles/treeView/treeView.css +303 -0
  100. package/tiles/treeView/treeView.editor.tsx +12 -0
  101. package/tiles/workbench/assistant.css +401 -0
  102. package/tiles/workbench/assistant.tsx +516 -0
  103. package/tiles/workbench/assistantChat.ts +165 -0
  104. package/tiles/workbench/dictation.ts +354 -0
  105. package/tiles/workbench/projectInfo.ts +72 -0
  106. package/tiles/workbench/recent.ts +67 -0
  107. package/tiles/workbench/workbench.component.tsx +476 -0
  108. package/tiles/workbench/workbench.css +376 -0
  109. package/tiles/workbench/workbenchAddress.ts +202 -0
  110. package/tiles/workbench/workspace.ts +775 -0
  111. package/tiles/workbenchEditor/workbenchEditor.component.tsx +461 -0
  112. package/tiles/workbenchEditor/workbenchEditor.css +370 -0
  113. package/tiles/workbenchHome/workbenchHome.component.tsx +196 -0
  114. package/tiles/workbenchHome/workbenchHome.css +271 -0
  115. package/tsconfig.json +3 -0
@@ -0,0 +1,973 @@
1
+ .canvas-view {
2
+ --canvas-bar: calc(var(--mosetta-wb-icon-box) + 8px);
3
+
4
+ /* Room under the page for the control that adds a block AFTER the last one. That control stands
5
+ at the page's bottom edge, so a page whose content ends exactly at the bottom of the frame
6
+ leaves it nowhere to be: outside, where the surface clips it. The strip is both where it can
7
+ be drawn and the few pixels of scroll that bring the page's own end into view. */
8
+ --canvas-foot: 24px;
9
+
10
+ display: flex;
11
+ flex-direction: column;
12
+ height: 100%;
13
+ background: #ffffff;
14
+ }
15
+
16
+ /* Actions above the frame — and the room the topmost label needs: an outline's tag hangs above
17
+ what it names, so for a block at the very top of the page it used to be cut off by the panel's
18
+ edge. Overflow on the sides is accepted: pushing a label back inside would take more machinery
19
+ than it is worth. */
20
+ .canvas-view__bar {
21
+ display: flex;
22
+ box-sizing: border-box;
23
+ align-items: center;
24
+ gap: 2px;
25
+ height: var(--canvas-bar);
26
+ padding: 4px 6px;
27
+ border-bottom: 1px solid #e4e4e7;
28
+ background: #fafafa;
29
+ }
30
+
31
+ .canvas-view__action {
32
+ position: relative;
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ width: var(--mosetta-wb-icon-box);
37
+ height: var(--mosetta-wb-icon-box);
38
+ padding: 0;
39
+ border: 0;
40
+ border-radius: 4px;
41
+ background: none;
42
+ color: #52525b;
43
+ cursor: pointer;
44
+ }
45
+
46
+ .canvas-view__action svg {
47
+ width: var(--mosetta-wb-icon);
48
+ height: var(--mosetta-wb-icon);
49
+ }
50
+
51
+ .canvas-view__action:hover {
52
+ background: #e4e4e7;
53
+ }
54
+
55
+ /* the bar's links (the preview in a new tab) dress as its buttons */
56
+ a.canvas-view__action {
57
+ text-decoration: none;
58
+ }
59
+
60
+ /* a hairline between the desk's buttons (tree, open) and the frame's own */
61
+ .canvas-view__divider {
62
+ flex: none;
63
+ width: 1px;
64
+ height: calc(var(--mosetta-wb-icon-box) - 8px);
65
+ margin: 0 4px;
66
+ background: #d4d4d8;
67
+ }
68
+
69
+ .canvas-view__action--play {
70
+ color: #16a34a;
71
+ }
72
+
73
+ .canvas-view__action--picker {
74
+ color: #2563eb;
75
+ }
76
+
77
+ /* the canvas tools are unfolded */
78
+ .canvas-view__action--on {
79
+ background: #e4e4e7;
80
+ color: #18181b;
81
+ }
82
+
83
+ /* the inspector is off — the one state of this bar worth shouting about */
84
+ .canvas-view__action--off {
85
+ color: #dc2626;
86
+ }
87
+
88
+ /* an inspected published revision: the badge says why nothing on this canvas can be edited */
89
+ .canvas-view__readonly {
90
+ margin-left: 6px;
91
+ padding: 1px 7px;
92
+ border: 1px solid #d4d4d8;
93
+ border-radius: 9px;
94
+ font-size: var(--mosetta-wb-font-xs);
95
+ line-height: 16px;
96
+ color: #52525b;
97
+ background: #f4f4f5;
98
+ white-space: nowrap;
99
+ }
100
+
101
+ /* the DRAFT's badge, beside where a revision says it is read-only: which draft, and whether its
102
+ edits are on disk — a green dot when saved, amber while saving */
103
+ .canvas-view__readonly--draft {
104
+ display: inline-flex;
105
+ align-items: center;
106
+ gap: 5px;
107
+ border-color: #bbf7d0;
108
+ color: #166534;
109
+ background: #f0fdf4;
110
+ }
111
+
112
+ .canvas-view__readonly--saving {
113
+ border-color: #fde68a;
114
+ color: #92400e;
115
+ background: #fffbeb;
116
+ }
117
+
118
+ .canvas-view__save-dot {
119
+ flex: none;
120
+ width: 6px;
121
+ height: 6px;
122
+ border-radius: 50%;
123
+ background: #22c55e;
124
+ }
125
+
126
+ .canvas-view__readonly--saving .canvas-view__save-dot {
127
+ background: #f59e0b;
128
+ animation: canvas-view-pulse 1s ease-in-out infinite alternate;
129
+ }
130
+
131
+ @keyframes canvas-view-pulse {
132
+ to {
133
+ opacity: 0.35;
134
+ }
135
+ }
136
+
137
+ /* a revision's way to an edit: a draft of it — worded, like Publish is for a draft, but quieter */
138
+ /* the page's blocks have newer versions: a chip that opens the updates window (ADR-0061) */
139
+ .canvas-view__updates {
140
+ margin-left: 6px;
141
+ padding: 2px 10px;
142
+ border: 1px solid #bfdbfe;
143
+ border-radius: 9px;
144
+ font: inherit;
145
+ font-size: var(--mosetta-wb-font-xs);
146
+ font-weight: 600;
147
+ line-height: 16px;
148
+ color: #1d4ed8;
149
+ background: #eff6ff;
150
+ white-space: nowrap;
151
+ cursor: pointer;
152
+ }
153
+
154
+ .canvas-view__updates:hover {
155
+ border-color: #93c5fd;
156
+ }
157
+
158
+ .canvas-view__new-draft {
159
+ display: inline-flex;
160
+ align-items: center;
161
+ gap: 5px;
162
+ margin-left: 6px;
163
+ padding: 2px 10px 2px 8px;
164
+ border: 1px solid #2563eb;
165
+ border-radius: 9px;
166
+ font: inherit;
167
+ font-size: var(--mosetta-wb-font-xs);
168
+ font-weight: 600;
169
+ line-height: 16px;
170
+ color: #2563eb;
171
+ background: #ffffff;
172
+ white-space: nowrap;
173
+ cursor: pointer;
174
+ }
175
+
176
+ .canvas-view__new-draft:hover:not(:disabled) {
177
+ background: #eff6ff;
178
+ }
179
+
180
+ .canvas-view__new-draft:disabled {
181
+ border-color: #d4d4d8;
182
+ color: #a1a1aa;
183
+ cursor: default;
184
+ }
185
+
186
+ .canvas-view__new-draft svg,
187
+ .canvas-view__publish svg {
188
+ flex: none;
189
+ width: 13px;
190
+ height: 13px;
191
+ }
192
+
193
+ /* the draft's way to the site — the one worded button of the bar, beside the draft group */
194
+ .canvas-view__publish {
195
+ display: inline-flex;
196
+ align-items: center;
197
+ gap: 5px;
198
+ margin-left: 6px;
199
+ padding: 2px 10px 2px 8px;
200
+ border: 1px solid #2563eb;
201
+ border-radius: 9px;
202
+ font: inherit;
203
+ font-size: var(--mosetta-wb-font-xs);
204
+ font-weight: 600;
205
+ line-height: 16px;
206
+ color: #ffffff;
207
+ background: #2563eb;
208
+ white-space: nowrap;
209
+ cursor: pointer;
210
+ }
211
+
212
+ .canvas-view__publish:hover {
213
+ border-color: #1d4ed8;
214
+ background: #1d4ed8;
215
+ }
216
+
217
+ .canvas-view__publish:disabled {
218
+ border-color: #d4d4d8;
219
+ color: #a1a1aa;
220
+ background: #f4f4f5;
221
+ cursor: default;
222
+ }
223
+
224
+ /* the draft group (copy/paste/undo/redo) keeps to the right edge: blue while the action has
225
+ something to act on, gray while it has not — the colour IS the whole status UI */
226
+ .canvas-view__action--right {
227
+ margin-left: auto;
228
+ }
229
+
230
+ .canvas-view__action--history {
231
+ color: #2563eb;
232
+ }
233
+
234
+ .canvas-view__action--history:disabled {
235
+ color: #a1a1aa;
236
+ cursor: default;
237
+ }
238
+
239
+ .canvas-view__action--history:disabled:hover {
240
+ background: none;
241
+ }
242
+
243
+ /* The frame lives in the stage's CONTENT box, the overlay in its padding box — so the foot strip
244
+ below the frame is drawable while the frame itself ends above it. Its colour is the bar's: the
245
+ page is bracketed by two neutral strips, and neither is mistaken for the page. */
246
+ .canvas-view__stage {
247
+ position: relative;
248
+ box-sizing: border-box;
249
+ flex: 1;
250
+ min-height: 0;
251
+ padding-bottom: var(--canvas-foot);
252
+ background: #fafafa;
253
+ }
254
+
255
+ /* The page over everything else. Fixed rather than moved into another container on purpose:
256
+ re-parenting an iframe reloads it, and "look at the page" must not restart the page.
257
+ No foot strip either: it is room for a control that is not drawn here (nothing of the editor
258
+ is), and full screen means the page gets the whole screen. */
259
+ .canvas-view--fullscreen .canvas-view__stage {
260
+ position: fixed;
261
+ inset: 0;
262
+ z-index: 2147483000;
263
+ padding-bottom: 0;
264
+ background: #ffffff;
265
+ }
266
+
267
+ .canvas-view__close {
268
+ position: absolute;
269
+ top: 8px;
270
+ right: 8px;
271
+ z-index: 1;
272
+ display: flex;
273
+ align-items: center;
274
+ justify-content: center;
275
+ width: calc(var(--mosetta-wb-icon-sm) + 4px);
276
+ height: calc(var(--mosetta-wb-icon-sm) + 4px);
277
+ padding: 0;
278
+ border: 0;
279
+ border-radius: 4px;
280
+ background: #dc2626;
281
+ color: #ffffff;
282
+ cursor: pointer;
283
+ }
284
+
285
+ .canvas-view__close svg {
286
+ width: var(--mosetta-wb-icon-sm);
287
+ height: var(--mosetta-wb-icon-sm);
288
+ }
289
+
290
+ .canvas-view__frame {
291
+ display: block;
292
+ width: 100%;
293
+ height: 100%;
294
+ border: 0;
295
+ }
296
+
297
+ .canvas-view__empty {
298
+ color: #71717a;
299
+ font: var(--mosetta-wb-font)/1.5 ui-sans-serif, system-ui, sans-serif;
300
+ padding: 12px;
301
+ }
302
+
303
+ /* The drawing surface: covers the frame exactly, clips whatever hangs outside it, and lets every
304
+ event through — hit-testing happens inside the preview, on the real DOM.
305
+ It CLIPS but never moves: the layer inside is what slides with the page. Sliding the clipping
306
+ box itself takes the visible window with it, and outlines start disappearing towards the edge
307
+ you scrolled away from. */
308
+ .canvas-view__overlay {
309
+ position: absolute;
310
+ top: calc(-1 * var(--canvas-bar));
311
+ right: 0;
312
+ bottom: 0;
313
+ left: 0;
314
+ overflow: hidden;
315
+ pointer-events: none;
316
+ }
317
+
318
+ /* What actually follows the page's scroll (see the canvas module: written straight to the node).
319
+ It starts at the FRAME's top, not at the surface's — the surface reaches a bar's height higher
320
+ so that a label belonging to the topmost block has somewhere to hang; every coordinate the
321
+ preview reports is still measured from the frame. */
322
+ .canvas-view__layer {
323
+ position: absolute;
324
+ top: var(--canvas-bar);
325
+ right: 0;
326
+ bottom: 0;
327
+ left: 0;
328
+ }
329
+
330
+ /* Layers, from the back: the map of resting places, then the block outlines (a selection has to
331
+ be visible over the places it offers), then the place being pointed at, then the control that
332
+ points at it. Under the pointer the green has to close around its own control — over the blue
333
+ outline and over the selection's labels, or the two borders cut each other. */
334
+ .canvas-view__outline {
335
+ position: absolute;
336
+ top: 0;
337
+ left: 0;
338
+ z-index: 1;
339
+ box-sizing: border-box;
340
+ }
341
+
342
+ .canvas-view__outline--hover {
343
+ outline: 1px dashed #60a5fa;
344
+ outline-offset: -1px;
345
+ background: rgb(96 165 250 / 6%);
346
+ }
347
+
348
+ .canvas-view__outline--select {
349
+ outline: 2px solid #2563eb;
350
+ outline-offset: -2px;
351
+ }
352
+
353
+ /* The PICK (Ctrl) mode wears YELLOW — the semantic colour of picking parts of the tree for
354
+ copy/paste. Same shapes as the blue pair: the mode changes the colour, never the language. */
355
+ .canvas-view__outline--hover-pick {
356
+ outline: 1px dashed #facc15;
357
+ outline-offset: -1px;
358
+ background: rgb(250 204 21 / 8%);
359
+ }
360
+
361
+ .canvas-view__outline--pick {
362
+ outline: 2px solid #eab308;
363
+ outline-offset: -2px;
364
+ }
365
+
366
+ /* everything a pick covers is PICKED, so it wears the same yellow — thinner, so the branch root
367
+ (what was actually clicked) still reads at a glance */
368
+ .canvas-view__outline--pick-covered {
369
+ outline: 1px solid #eab308;
370
+ outline-offset: -1px;
371
+ }
372
+
373
+ /* GHOSTS — the places a block may go, as green tabs cut from the same sheet as the selection's
374
+ frame and tags: on top a segment of the corner strip (the strip's own rounding applies), below a
375
+ tab hanging from the bottom edge, left and right tabs on those edges; a free tag of its own inside
376
+ an empty slot or at the page's ends. Green, not the blue of the selection: those point at a block
377
+ that is there, this one adds one. */
378
+ .canvas-view__ghost {
379
+ display: flex;
380
+ flex: none;
381
+ box-sizing: border-box;
382
+ align-items: center;
383
+ justify-content: center;
384
+ gap: 4px;
385
+ padding: 1px 6px;
386
+ border: 0;
387
+ background: #059669;
388
+ color: #ffffff;
389
+ font: var(--mosetta-wb-font-xs)/16px ui-sans-serif, system-ui, sans-serif;
390
+ white-space: nowrap;
391
+ cursor: pointer;
392
+ pointer-events: auto;
393
+ }
394
+
395
+ .canvas-view__ghost:hover {
396
+ background: #047857;
397
+ }
398
+
399
+ .canvas-view__ghost-plus {
400
+ font-weight: 700;
401
+ }
402
+
403
+ /* free: a tag standing on its own over the page */
404
+ .canvas-view__ghost--free {
405
+ position: absolute;
406
+ top: 0;
407
+ left: 0;
408
+ z-index: 3;
409
+ border-radius: 3px;
410
+ }
411
+
412
+ /* the side tabs: as tall as the block, up to a few lines, a "+" on the edge */
413
+ .canvas-view__ghost--left,
414
+ .canvas-view__ghost--right {
415
+ width: 18px;
416
+ padding: 0;
417
+ }
418
+
419
+ .canvas-view__ghost--left {
420
+ border-radius: 3px 0 0 3px;
421
+ }
422
+
423
+ .canvas-view__ghost--right {
424
+ border-radius: 0 3px 3px 0;
425
+ }
426
+
427
+ /* where the side tabs hang: off the block's left or right edge, halfway down */
428
+ .canvas-view__edge {
429
+ position: absolute;
430
+ top: 50%;
431
+ display: flex;
432
+ transform: translateY(-50%);
433
+ }
434
+
435
+ .canvas-view__edge--left {
436
+ right: 100%;
437
+ }
438
+
439
+ .canvas-view__edge--right {
440
+ left: 100%;
441
+ }
442
+
443
+ /* the outline steps forward while its ghost is pointed at — the tooltip of a side tab must read */
444
+ .canvas-view__outline:has(.canvas-view__ghost:hover) {
445
+ z-index: 4;
446
+ }
447
+
448
+ /* THE LANDING SPACE: the very spot a picked block goes — room the preview made for it, framed in
449
+ violet (green says which block it joins, violet says where exactly) */
450
+ .canvas-view__landing {
451
+ position: absolute;
452
+ top: 0;
453
+ left: 0;
454
+ z-index: 2;
455
+ box-sizing: border-box;
456
+ border: 2px dashed #7c3aed;
457
+ border-radius: 4px;
458
+ background: rgb(124 58 237 / 10%);
459
+ }
460
+
461
+ .canvas-view__slot-area {
462
+ position: absolute;
463
+ top: 0;
464
+ left: 0;
465
+ z-index: 2;
466
+ box-sizing: border-box;
467
+ }
468
+
469
+ /* the map of where things can go — shown only while nothing in particular is being pointed at */
470
+ .canvas-view__slot-area--resting {
471
+ z-index: 0;
472
+ border: 1px dashed #6ee7b7;
473
+ background: rgb(5 150 105 / 5%);
474
+ }
475
+
476
+ /* The MARCHING BORDER: the one signal that a place is waiting for a block. The same border is
477
+ drawn twice — around the container in the preview and around the palette (see palette.css) — so the
478
+ two halves of one answer look like one thing. Painted as four gradients rather than a dashed
479
+ border because a border's dashes cannot be moved: it is the travel that catches the eye. */
480
+ .canvas-view__slot-area--target {
481
+ background-color: rgb(16 185 129 / 10%);
482
+ background-image:
483
+ repeating-linear-gradient(90deg, #10b981 0 3px, transparent 3px 6px),
484
+ repeating-linear-gradient(90deg, #10b981 0 3px, transparent 3px 6px),
485
+ repeating-linear-gradient(0deg, #10b981 0 3px, transparent 3px 6px),
486
+ repeating-linear-gradient(0deg, #10b981 0 3px, transparent 3px 6px);
487
+ background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
488
+ background-position: 0 0, 0 100%, 0 0, 100% 0;
489
+ background-size: 6px 1px, 6px 1px, 1px 6px, 1px 6px;
490
+ animation: mosetta-marching-border 1s linear infinite;
491
+ }
492
+
493
+ /* top rightwards, bottom leftwards, left upwards, right downwards — one circuit of the perimeter */
494
+ @keyframes mosetta-marching-border {
495
+ to {
496
+ background-position: 6px 0, -6px 100%, 0 -6px, 100% 6px;
497
+ }
498
+ }
499
+
500
+ @media (prefers-reduced-motion: reduce) {
501
+ .canvas-view__slot-area--target {
502
+ animation: none;
503
+ }
504
+ }
505
+
506
+
507
+ /* A label hanging off the top-left corner of what it names. MODIFIERS BELOW, NEVER ABOVE: they
508
+ carry the same specificity as this rule, so whichever comes last in the file wins — put one
509
+ above and it silently loses its own position back to the base. */
510
+ .canvas-view__tag {
511
+ position: absolute;
512
+ top: -18px;
513
+ left: -2px;
514
+ padding: 1px 6px;
515
+ border-radius: 3px 3px 0 0;
516
+ background: #2563eb;
517
+ color: #ffffff;
518
+ font: var(--mosetta-wb-font-xs)/16px ui-sans-serif, system-ui, sans-serif;
519
+ white-space: nowrap;
520
+ }
521
+
522
+ /* the name inside the chip (no room for it on its own over the frame): a shade apart from the
523
+ size and the buttons it leads */
524
+ .canvas-view__corner .canvas-view__tag--name {
525
+ background: #1d4ed8;
526
+ }
527
+
528
+ /* the size of the selected block, in the corner opposite its name */
529
+ .canvas-view__tag--size {
530
+ right: -2px;
531
+ left: auto;
532
+ }
533
+
534
+ /* The right corner of a SELECTION is a strip rather than a single tag: what the block measures, and
535
+ the one action that takes the whole block away. They share one rounded rectangle with nothing
536
+ between them — a red button standing alone over the page would read as page content, and the
537
+ overlay's whole point is that nothing on it belongs to the application. So the tags inside go
538
+ square and only the ENDS of the strip are rounded — by position, not by clipping: the button's
539
+ tooltip hangs below it, and an overflow:hidden here would cut it off.
540
+ Order matters (see the tag above): the square reset must come BEFORE the two ends, or it takes
541
+ their rounding back off.
542
+ Flush with the block's edge, NOT 2px past it like the name tag opposite: a block that reaches
543
+ the right side of the page puts that overhang outside the frame, where the surface clips it —
544
+ and a clipped square reads as a button whose icon sits off-centre. */
545
+ .canvas-view__corner {
546
+ position: absolute;
547
+ top: -18px;
548
+ right: 0;
549
+ display: flex;
550
+ align-items: stretch;
551
+ }
552
+
553
+ .canvas-view__corner .canvas-view__tag {
554
+ position: static;
555
+ border-radius: 0;
556
+ }
557
+
558
+ .canvas-view__corner > :first-child {
559
+ border-top-left-radius: 3px;
560
+ }
561
+
562
+ .canvas-view__corner > :last-child {
563
+ border-top-right-radius: 3px;
564
+ }
565
+
566
+ /* The destructive one — the only red on the overlay, and the only thing on it that is pressed.
567
+ A square: `flex: none` so it keeps its width whatever the strip is asked to fit into, and the
568
+ glyph centred by margins rather than by the box, which stays right even if the box is not. */
569
+ .canvas-view__tag-action {
570
+ position: relative;
571
+ display: flex;
572
+ flex: none;
573
+ width: 18px;
574
+ align-items: center;
575
+ justify-content: center;
576
+ padding: 0;
577
+ border: 0;
578
+ background: #dc2626;
579
+ color: #ffffff;
580
+ cursor: pointer;
581
+ pointer-events: auto;
582
+ }
583
+
584
+ .canvas-view__tag-action svg {
585
+ display: block;
586
+ width: 11px;
587
+ height: 11px;
588
+ margin: auto;
589
+ }
590
+
591
+ /* the bottom strip: the top one's twin, hanging from the bottom edge under the same corner */
592
+ .canvas-view__corner.canvas-view__corner--bottom {
593
+ top: 100%;
594
+ }
595
+
596
+ .canvas-view__corner.canvas-view__corner--bottom > * {
597
+ border-radius: 0 0 3px 3px;
598
+ }
599
+
600
+ /* A tooltip has to be readable, and this one hangs over the block, where the insertion control
601
+ also lives — which sits ABOVE the outlines by design (a place under the pointer wins over the
602
+ selection). The outline as a whole steps forward for as long as the button is pointed at, which
603
+ is the only moment the tooltip exists. */
604
+ .canvas-view__outline:has(.canvas-view__tag-action:hover) {
605
+ z-index: 4;
606
+ }
607
+
608
+ .canvas-view__tag-action:hover {
609
+ background: #b91c1c;
610
+ }
611
+
612
+ /* a place wears the same two corners as a block, in the colour of the frame around it */
613
+ .canvas-view__tag--slot {
614
+ background: #10b981;
615
+ }
616
+
617
+ .canvas-view__tag--landing {
618
+ background: #7c3aed;
619
+ }
620
+
621
+ /* the block's settings, folded behind the chip: blue like the chip, pressed while they are out */
622
+ .canvas-view__tag-action--settings {
623
+ background: #2563eb;
624
+ }
625
+
626
+ .canvas-view__tag-action--settings:hover {
627
+ background: #1d4ed8;
628
+ }
629
+
630
+ .canvas-view__tag-action--on {
631
+ background: #1e3a8a;
632
+ }
633
+
634
+ /* the settings asked for and their form still loading: the button turns while the card waits,
635
+ unseen, to appear whole where it stays (settingsPopover.tsx) */
636
+ .canvas-view__tag-action--loading svg {
637
+ visibility: hidden;
638
+ }
639
+
640
+ .canvas-view__tag-action--loading::after {
641
+ content: "";
642
+ position: absolute;
643
+ inset: 0;
644
+ width: 8px;
645
+ height: 8px;
646
+ margin: auto;
647
+ border: 1.5px solid rgb(255 255 255 / 35%);
648
+ border-top-color: #ffffff;
649
+ border-radius: 50%;
650
+ animation: canvas-view-spin 0.7s linear infinite;
651
+ }
652
+
653
+ @keyframes canvas-view-spin {
654
+ to {
655
+ transform: rotate(360deg);
656
+ }
657
+ }
658
+
659
+ /* The CANVAS TOOLS (providerChrome.tsx), unfolded by the cog of the bar: a column in the top-right
660
+ corner of the stage, over the overlay — the page's settings and the blocks' standing controls
661
+ (i18n's language, a theme's pick). The author's tools, not the page. */
662
+ /* an editor tool (ADR-0062): over the whole stage — the page, its frames, its popovers — while
663
+ the tree beside the canvas stays in sight; the tool draws its own head and body */
664
+ .canvas-view__tool {
665
+ position: absolute;
666
+ inset: 0;
667
+ z-index: 6;
668
+ display: flex;
669
+ flex-direction: column;
670
+ min-height: 0;
671
+ overflow: hidden;
672
+ background: #fff;
673
+ }
674
+
675
+ .canvas-view__tool > * {
676
+ flex: 1;
677
+ min-height: 0;
678
+ }
679
+
680
+ .canvas-view__tool-icon {
681
+ width: var(--mosetta-wb-icon);
682
+ height: var(--mosetta-wb-icon);
683
+ object-fit: contain;
684
+ }
685
+
686
+ .canvas-view__chrome {
687
+ position: absolute;
688
+ top: 8px;
689
+ right: 8px;
690
+ z-index: 3;
691
+ display: flex;
692
+ flex-direction: column;
693
+ gap: 6px;
694
+ align-items: flex-end;
695
+ pointer-events: auto;
696
+ }
697
+
698
+ .canvas-view__chrome-item {
699
+ padding: 2px 6px;
700
+ border: 1px solid #e4e4e7;
701
+ border-radius: 6px;
702
+ background: #ffffffee;
703
+ box-shadow: 0 1px 2px #0000000f;
704
+ font: var(--mosetta-wb-font-sm)/1.4 ui-sans-serif, system-ui, sans-serif;
705
+ }
706
+
707
+ .canvas-view__chrome-button {
708
+ padding: 4px 10px;
709
+ color: #3f3f46;
710
+ cursor: pointer;
711
+ }
712
+
713
+ .canvas-view__chrome-button:hover {
714
+ border-color: #a1a1aa;
715
+ }
716
+
717
+ .canvas-view__chrome-empty {
718
+ color: #71717a;
719
+ }
720
+
721
+ /* The selected block's settings, beside the block (see popoverPlacement.ts) — or, expanded, the
722
+ whole stage for a form that needs room. Over the overlay and the providers' chrome. */
723
+ .canvas-view__settings {
724
+ position: absolute;
725
+ z-index: 4;
726
+ box-sizing: border-box;
727
+ display: flex;
728
+ flex-direction: column;
729
+ /* the selection's own frame: square, blue, 2px — lapping over the block's, the two share the
730
+ line where they meet */
731
+ border: 2px solid #2563eb;
732
+ border-radius: 0;
733
+ background: #ffffff;
734
+ /* a shadow that falls away from the block, never across the seam */
735
+ box-shadow: 0 14px 28px -10px #00000040;
736
+ pointer-events: auto;
737
+ overflow: hidden;
738
+ }
739
+
740
+ /* the place under the block, riding on the seam: right in the head, before the close button */
741
+ .canvas-view__settings-seam-ghost {
742
+ display: flex;
743
+ margin-left: auto;
744
+ }
745
+
746
+ .canvas-view__settings-seam-ghost .canvas-view__ghost {
747
+ border-radius: 3px;
748
+ }
749
+
750
+ .canvas-view__settings-head .canvas-view__settings-seam-ghost + .canvas-view__settings-action {
751
+ margin-left: 4px;
752
+ }
753
+
754
+ /* while the grip is dragged, the form below must not catch the pointer or select its text */
755
+ .canvas-view__settings--resizing {
756
+ user-select: none;
757
+ }
758
+
759
+ /* a grip on the top edge sits in the head's left corner — the name steps aside for it */
760
+ .canvas-view__settings--grip-top .canvas-view__settings-head {
761
+ padding-left: 20px;
762
+ }
763
+
764
+ /* the grip sits in the popover's free corner (the one away from the block and the edge it hugs) */
765
+ .canvas-view__settings-grip {
766
+ position: absolute;
767
+ z-index: 1;
768
+ width: 16px;
769
+ height: 16px;
770
+ padding: 3px;
771
+ box-sizing: border-box;
772
+ color: #a1a1aa;
773
+ touch-action: none;
774
+ }
775
+
776
+ .canvas-view__settings-grip:hover,
777
+ .canvas-view__settings--resizing .canvas-view__settings-grip {
778
+ color: #52525b;
779
+ }
780
+
781
+ .canvas-view__settings-grip svg {
782
+ display: block;
783
+ width: 100%;
784
+ height: 100%;
785
+ }
786
+
787
+ .canvas-view__settings-grip--bottom-right {
788
+ right: 0;
789
+ bottom: 0;
790
+ cursor: nwse-resize;
791
+ }
792
+
793
+ .canvas-view__settings-grip--bottom-left {
794
+ left: 0;
795
+ bottom: 0;
796
+ cursor: nesw-resize;
797
+ }
798
+
799
+ .canvas-view__settings-grip--bottom-left svg {
800
+ transform: scaleX(-1);
801
+ }
802
+
803
+ .canvas-view__settings-grip--top-right {
804
+ right: 0;
805
+ top: 0;
806
+ cursor: nesw-resize;
807
+ }
808
+
809
+ .canvas-view__settings-grip--top-right svg {
810
+ transform: scaleY(-1);
811
+ }
812
+
813
+ .canvas-view__settings-grip--top-left {
814
+ left: 0;
815
+ top: 0;
816
+ cursor: nwse-resize;
817
+ }
818
+
819
+ .canvas-view__settings-grip--top-left svg {
820
+ transform: scale(-1);
821
+ }
822
+
823
+ .canvas-view__settings-head {
824
+ display: flex;
825
+ align-items: center;
826
+ gap: 6px;
827
+ padding: 6px 8px 6px 12px;
828
+ border-bottom: 1px solid #f4f4f5;
829
+ font: var(--mosetta-wb-font-sm)/1.4 ui-sans-serif, system-ui, sans-serif;
830
+ }
831
+
832
+ .canvas-view__settings-name {
833
+ font-weight: 600;
834
+ color: #18181b;
835
+ }
836
+
837
+ .canvas-view__settings-id {
838
+ color: #a1a1aa;
839
+ }
840
+
841
+ /* where the block comes from — quieter than its id, and the first to give way in a narrow card */
842
+ .canvas-view__settings-version {
843
+ display: inline-flex;
844
+ align-items: center;
845
+ gap: 6px;
846
+ min-width: 0;
847
+ overflow: hidden;
848
+ color: #a1a1aa;
849
+ font-size: var(--mosetta-wb-font-xs);
850
+ white-space: nowrap;
851
+ text-overflow: ellipsis;
852
+ }
853
+
854
+ .canvas-view__settings-version::before {
855
+ content: "·";
856
+ }
857
+
858
+ /* a newer version deployed: the tree's mark, in the head */
859
+ .canvas-view__settings-newer {
860
+ flex: none;
861
+ padding: 0 5px;
862
+ border-radius: 3px;
863
+ background: #dbeafe;
864
+ color: #1d4ed8;
865
+ }
866
+
867
+ .canvas-view__settings-action {
868
+ width: var(--mosetta-wb-icon-box);
869
+ height: var(--mosetta-wb-icon-box);
870
+ padding: 0;
871
+ border: 0;
872
+ border-radius: 6px;
873
+ background: transparent;
874
+ color: #52525b;
875
+ font-size: calc(var(--mosetta-wb-icon) - 1px);
876
+ line-height: var(--mosetta-wb-icon-box);
877
+ cursor: pointer;
878
+ }
879
+
880
+ .canvas-view__settings-action:first-of-type {
881
+ margin-left: auto;
882
+ }
883
+
884
+ .canvas-view__settings-action:hover {
885
+ background: #f4f4f5;
886
+ }
887
+
888
+ .canvas-view__settings-body {
889
+ flex: 1 1 auto;
890
+ min-height: 0;
891
+ overflow: auto;
892
+ padding: 4px 12px 12px;
893
+ }
894
+
895
+ /* The settings' TABS (settingsTabs.ts): a row under the head, the one shown underlined */
896
+ .canvas-view__settings-tabs {
897
+ display: flex;
898
+ gap: 2px;
899
+ padding: 0 8px;
900
+ border-bottom: 1px solid #f4f4f5;
901
+ }
902
+
903
+ .canvas-view__settings-tab {
904
+ margin-bottom: -1px;
905
+ padding: 6px 10px;
906
+ border: 0;
907
+ border-bottom: 2px solid transparent;
908
+ background: transparent;
909
+ color: #71717a;
910
+ font: var(--mosetta-wb-font-sm)/1.4 ui-sans-serif, system-ui, sans-serif;
911
+ cursor: pointer;
912
+ }
913
+
914
+ .canvas-view__settings-tab:hover {
915
+ color: #18181b;
916
+ }
917
+
918
+ .canvas-view__settings-tab--on {
919
+ border-bottom-color: #2563eb;
920
+ color: #18181b;
921
+ font-weight: 600;
922
+ }
923
+
924
+ /* One tab at a time: the sections named for the others step aside — on Other, everything the
925
+ catalog does not know as Main or Styles stays (a section's own name the catalog lacks) */
926
+ .canvas-view__settings-body[data-settings-tab="main"] [data-inspector-tab]:not([data-inspector-tab="main"]),
927
+ .canvas-view__settings-body[data-settings-tab="styles"] [data-inspector-tab]:not([data-inspector-tab="styles"]),
928
+ .canvas-view__settings-body[data-settings-tab="other"] [data-inspector-tab]:is([data-inspector-tab="main"], [data-inspector-tab="styles"]) {
929
+ display: none !important;
930
+ }
931
+
932
+ /* …and what a block's form leaves unnamed is its main settings: away on every other tab. An element
933
+ steps aside when it is outside every named section and holds none of them */
934
+ .canvas-view__settings-body:not([data-settings-tab="main"]) .settings__form-host :not([data-inspector-tab], [data-inspector-tab] *):not(:has([data-inspector-tab])) {
935
+ display: none !important;
936
+ }
937
+
938
+ /* the page's own settings, folded: a button in the corner until asked for */
939
+ /* the insert popover (insertPopover.tsx) wears the settings popover's frame; its body is the
940
+ block picker — the search stays put, the list scrolls */
941
+ .canvas-view__insert-body {
942
+ display: flex;
943
+ flex: 1 1 auto;
944
+ flex-direction: column;
945
+ min-height: 0;
946
+ padding-top: 8px;
947
+ }
948
+
949
+ .canvas-view__insert-body > .block-picker {
950
+ flex: 1 1 auto;
951
+ }
952
+
953
+ /* A wide field (a paragraph's text) widens the card; the form's other controls keep the width the
954
+ author wished for (--settings-wish-width, less the card's padding and — where a field can have
955
+ a reference circle — its gutter) — a select or the styler is not stretched along with a long text */
956
+ .canvas-view__settings .settings__form .dui-root > :not(:has([data-wide-field])) {
957
+ max-width: calc(var(--settings-wish-width, 340px) - 24px);
958
+ }
959
+
960
+ .canvas-view__settings .settings__form[data-bindable] .dui-root > :not(:has([data-wide-field])) {
961
+ max-width: calc(var(--settings-wish-width, 340px) - 64px);
962
+ }
963
+
964
+ /* out of the way for a moment — the page scrolls, or the block is out of sight: invisible and
965
+ not in the pointer's way, yet still there (its focus, its open picker, what is being typed) */
966
+ .canvas-view__settings--hidden {
967
+ opacity: 0;
968
+ pointer-events: none;
969
+ }
970
+
971
+ .canvas-view__settings {
972
+ transition: opacity 0.12s ease;
973
+ }