@mosetta/editor 0.0.0-stage → 0.0.28

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 +185 -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,136 @@
1
+ // The MOUNTER's mechanism: instancing someone else's application into an element — plain logic,
2
+ // because nothing about it is react (code-conventions §7a). The DI face lives next door in
3
+ // treeMounter.ts, the react adapter is useMountedTree; this file depends on neither, so any
4
+ // binding — or a unit test — can drive it directly.
5
+ //
6
+ // What a MountedTree keeps is ONE application: a new spec with the same root hands the running
7
+ // application a new tree (IMountHandle.render), and only a different ROOT — a provider on another
8
+ // binding — or a different element is worth building a new application for.
9
+ import { Errors } from '@mosetta/core';
10
+ import type { DiContainer } from '@mosetta/di';
11
+ import type { ITilesHolder, IMountPreview } from '@mosetta/runtime/client';
12
+ import type { IMountHandle, IRootComponentSide } from '@mosetta/react';
13
+ import type { IBlueprintNode } from '@mosetta/core';
14
+
15
+ /**
16
+ * What a HOST shows in place of anything it mounted that failed to render: the editor's own
17
+ * fallback, at the editor's own version (`editor` — its reference, as `useComponent` names it).
18
+ */
19
+ export const hostFallbackOf = (editor: { namespace: string; version: string }): string => `_ns_${editor.namespace}_t_component_n_hostFallback_v_${editor.version}`;
20
+
21
+ export interface IMountSpec {
22
+ /** the root module that will mount this tree — its binding is what actually renders */
23
+ readonly rootReference: string;
24
+ readonly tree: IBlueprintNode;
25
+ /** the specimen declaration for the mounted application's container (IMountOptions.preview) */
26
+ readonly preview?: IMountPreview;
27
+ }
28
+
29
+ /** the synthetic page a host mounts: its root, its fallback (hostFallbackOf), and the subject inside */
30
+ export function hostedPage(rootReference: string, children: readonly IBlueprintNode[], fallback: string): IBlueprintNode {
31
+ return {
32
+ id: '__hosted',
33
+ reference: rootReference,
34
+ children: children as IBlueprintNode[],
35
+ ...{ pattern: '/', fallback },
36
+ };
37
+ }
38
+
39
+ /** the fallback a hosted page names, if any */
40
+ const fallbackOf = (tree: IBlueprintNode): string | null => {
41
+ const named = (tree as { fallback?: unknown }).fallback;
42
+ return typeof named === 'string' ? named : null;
43
+ };
44
+
45
+ export async function loadModule(holder: ITilesHolder, reference: string) {
46
+ const loaded = holder.loadModule ? await holder.loadModule(reference) : holder.getModule(reference);
47
+ return loaded?.error ? undefined : loaded;
48
+ }
49
+
50
+ /**
51
+ * One application, kept alive across specs. The element comes as a PROVIDER, not a node: loading
52
+ * the root takes time, and the element that asked may be gone — or replaced — by the time the
53
+ * modules arrive; the mount goes into whatever the provider answers then.
54
+ */
55
+ export class MountedTree {
56
+ private app: {
57
+ root: string;
58
+ element: HTMLElement;
59
+ preview?: IMountPreview;
60
+ handle: IMountHandle;
61
+ } | null = null;
62
+
63
+ /** every show takes a turn; a show that lost its turn mounts nothing */
64
+ private turn = 0;
65
+
66
+ constructor(
67
+ private readonly holder: ITilesHolder,
68
+ /** the HOST application's container — every mounted application gets it (the Unity door) */
69
+ private readonly parentDi?: DiContainer,
70
+ ) {}
71
+
72
+ /**
73
+ * Put the spec's tree into the provider's element. Resolves `true` when the tree is showing,
74
+ * `false` when the call was superseded (a newer show, cancel or dispose) or the element was
75
+ * gone; rejects only for a failure that is CURRENT — a superseded failure concerns no one.
76
+ */
77
+ async show(spec: IMountSpec, element: () => HTMLElement | null): Promise<boolean> {
78
+ const turn = ++this.turn;
79
+ const current = element();
80
+ if (!current) return false;
81
+ // An element the panel replaced looks exactly like a form that renders nothing: the old
82
+ // application keeps writing into a node no longer in the document. A different ROOT — a
83
+ // provider on another binding — is a second reason to start over, and a different PREVIEW
84
+ // declaration is the third: it is consumed ONCE, at mount (its services register into the
85
+ // newborn container), so a new one can only take effect in a new application. Hosts keep the
86
+ // declaration's identity stable for as long as they mean the same thing.
87
+ if (this.app
88
+ && (this.app.root !== spec.rootReference || this.app.element !== current
89
+ || this.app.preview !== spec.preview)) {
90
+ this.app.handle.unmount();
91
+ this.app = null;
92
+ }
93
+ if (this.app) {
94
+ this.app.handle.render(spec.tree);
95
+ return true;
96
+ }
97
+ let root;
98
+ try {
99
+ // the fallback rides along: the tree renderer reads it from the holder WITHOUT loading
100
+ // (it must not throw while handling an error), so it has to be there before anything fails
101
+ const fallback = fallbackOf(spec.tree);
102
+ [root] = await Promise.all([
103
+ loadModule(this.holder, spec.rootReference),
104
+ fallback ? loadModule(this.holder, fallback) : undefined,
105
+ ]);
106
+ } catch (caught) {
107
+ if (turn !== this.turn) return false;
108
+ throw Errors.toError(caught);
109
+ }
110
+ const module = root?.module as IRootComponentSide | undefined;
111
+ if (typeof module?.mount !== 'function') {
112
+ if (turn !== this.turn) return false;
113
+ throw new Error(`host: ${spec.rootReference} cannot mount anything`);
114
+ }
115
+ const fresh = element();
116
+ if (turn !== this.turn || !fresh) return false;
117
+ this.app = {
118
+ root: spec.rootReference,
119
+ element: fresh,
120
+ preview: spec.preview,
121
+ handle: module.mount(fresh, { tree: spec.tree, preview: spec.preview, parentDi: this.parentDi }),
122
+ };
123
+ return true;
124
+ }
125
+
126
+ /** forget any show still in flight — whatever supersedes it is already on its way */
127
+ cancel(): void {
128
+ this.turn++;
129
+ }
130
+
131
+ dispose(): void {
132
+ this.cancel();
133
+ this.app?.handle.unmount();
134
+ this.app = null;
135
+ }
136
+ }
@@ -0,0 +1,483 @@
1
+ .settings {
2
+ font: var(--mosetta-wb-font)/1.5 ui-sans-serif, system-ui, sans-serif;
3
+ }
4
+
5
+ .settings__hint {
6
+ color: #71717a;
7
+ padding: 12px;
8
+ }
9
+
10
+ .settings__target {
11
+ color: #71717a;
12
+ font-size: var(--mosetta-wb-font-xs);
13
+ padding: 4px 0;
14
+ }
15
+
16
+ .settings__ssr {
17
+ display: flex;
18
+ align-items: center;
19
+ gap: 6px;
20
+ padding: 6px 0;
21
+ color: #3f3f46;
22
+ cursor: pointer;
23
+ }
24
+
25
+ /* the root's block: a select with its words under it — the one choice made at creation before */
26
+ .settings__root {
27
+ display: flex;
28
+ flex-direction: column;
29
+ gap: 4px;
30
+ padding: 2px 0 8px;
31
+ }
32
+
33
+ .settings__root-label {
34
+ font-size: var(--mosetta-wb-font-xs);
35
+ font-weight: 500;
36
+ color: #71717a;
37
+ }
38
+
39
+ .settings__root-select {
40
+ height: var(--mosetta-wb-icon-box);
41
+ padding: 0 6px;
42
+ border: 1px solid #d4d4d8;
43
+ border-radius: 5px;
44
+ font: inherit;
45
+ font-size: var(--mosetta-wb-font-sm);
46
+ color: #27272a;
47
+ background: #fff;
48
+ }
49
+
50
+ .settings__root-hint {
51
+ font-size: var(--mosetta-wb-font-2xs);
52
+ color: #a1a1aa;
53
+ }
54
+
55
+ .settings__meta {
56
+ display: flex;
57
+ flex-direction: column;
58
+ gap: 6px;
59
+ padding: 6px 0;
60
+ }
61
+
62
+ .settings__meta-field {
63
+ display: flex;
64
+ flex-direction: column;
65
+ gap: 2px;
66
+ }
67
+
68
+ .settings__meta-flag {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: 6px;
72
+ cursor: pointer;
73
+ }
74
+
75
+ .settings__meta-label {
76
+ font-size: var(--mosetta-wb-font-xs);
77
+ color: #71717a;
78
+ }
79
+
80
+ .settings__meta-input {
81
+ font: inherit;
82
+ padding: 4px 8px;
83
+ border: 1px solid #e4e4e7;
84
+ border-radius: 6px;
85
+ background: transparent;
86
+ }
87
+
88
+ .settings__lang {
89
+ display: flex;
90
+ align-items: center;
91
+ flex-wrap: wrap;
92
+ gap: 6px;
93
+ padding: 6px 0;
94
+ border-bottom: 1px solid #e4e4e7;
95
+ }
96
+
97
+ .settings__lang-select {
98
+ font: inherit;
99
+ padding: 2px 6px;
100
+ border: 1px solid #e4e4e7;
101
+ border-radius: 6px;
102
+ background: transparent;
103
+ }
104
+
105
+ .settings__lang-missing {
106
+ font-size: var(--mosetta-wb-font-xs);
107
+ color: #71717a;
108
+ }
109
+
110
+ .settings__lang-translate {
111
+ font: inherit;
112
+ font-size: var(--mosetta-wb-font-sm);
113
+ padding: 2px 8px;
114
+ border: 1px solid #e4e4e7;
115
+ border-radius: 6px;
116
+ background: #fafafa;
117
+ cursor: pointer;
118
+ }
119
+
120
+ .settings__lang-translate:disabled {
121
+ opacity: 0.6;
122
+ cursor: default;
123
+ }
124
+
125
+ .settings__lang-error {
126
+ font-size: var(--mosetta-wb-font-xs);
127
+ color: #dc2626;
128
+ }
129
+
130
+ /* The form area with its reference gutter: the pickers sit to the right of the fields the form
131
+ marks with data-field — drawn by the editor, the form knows nothing of them. */
132
+ .settings__form {
133
+ position: relative;
134
+ }
135
+
136
+ /* the gutter the reference circles sit in — only for a form with a field that can have one */
137
+ .settings__form[data-bindable] {
138
+ padding-right: calc(var(--mosetta-wb-icon-box) + 8px);
139
+ }
140
+
141
+ .settings__ref {
142
+ position: absolute;
143
+ /* `top` is the field's middle, `left` just past its right edge — the circle centres on it */
144
+ transform: translateY(-50%);
145
+ width: var(--mosetta-wb-icon-box);
146
+ height: var(--mosetta-wb-icon-box);
147
+ padding: 0;
148
+ border: 1px solid #d4d4d8;
149
+ border-radius: 50%;
150
+ background: #ffffff;
151
+ color: #71717a;
152
+ cursor: pointer;
153
+ display: flex;
154
+ align-items: center;
155
+ justify-content: center;
156
+ }
157
+
158
+ .settings__ref::after {
159
+ content: '';
160
+ width: 6px;
161
+ height: 6px;
162
+ border-radius: 50%;
163
+ background: currentColor;
164
+ }
165
+
166
+ .settings__ref:hover {
167
+ border-color: #a1a1aa;
168
+ color: #3f3f46;
169
+ }
170
+
171
+ .settings__ref--bound {
172
+ border-color: #2563eb;
173
+ background: #eff6ff;
174
+ color: #2563eb;
175
+ }
176
+
177
+ /* What does not fit (settingsIssues.tsx): a plaque above the form on every tab, and the field it
178
+ is about in red — over a bound field's blue, since a wrong value matters more */
179
+ .settings__issues {
180
+ margin: 8px 12px;
181
+ padding: 8px 10px;
182
+ border: 1px solid #fecaca;
183
+ border-radius: 8px;
184
+ background: #fef2f2;
185
+ color: #991b1b;
186
+ font-size: var(--mosetta-wb-font-xs);
187
+ }
188
+
189
+ .settings__issues-title {
190
+ font-weight: 600;
191
+ margin-bottom: 4px;
192
+ }
193
+
194
+ .settings__issues-list {
195
+ margin: 0;
196
+ padding: 0;
197
+ list-style: none;
198
+ }
199
+
200
+ .settings__issue-row {
201
+ display: flex;
202
+ align-items: baseline;
203
+ gap: 8px;
204
+ }
205
+
206
+ .settings__issue-remove {
207
+ flex: none;
208
+ padding: 0 6px;
209
+ border: 1px solid #fca5a5;
210
+ border-radius: 6px;
211
+ background: #ffffff;
212
+ color: #991b1b;
213
+ font: inherit;
214
+ cursor: pointer;
215
+ }
216
+
217
+ .settings__issue-remove:hover {
218
+ border-color: #dc2626;
219
+ }
220
+
221
+ .settings__issue {
222
+ display: block;
223
+ flex: 1;
224
+ min-width: 0;
225
+ padding: 2px 0;
226
+ border: 0;
227
+ background: none;
228
+ color: inherit;
229
+ font: inherit;
230
+ text-align: left;
231
+ cursor: pointer;
232
+ }
233
+
234
+ .settings__issue:hover {
235
+ text-decoration: underline;
236
+ }
237
+
238
+ .settings__issue code {
239
+ font-weight: 600;
240
+ }
241
+
242
+ .settings__issues-note {
243
+ margin-top: 4px;
244
+ color: #b91c1c;
245
+ opacity: 0.8;
246
+ }
247
+
248
+ .settings__form [data-mosetta-invalid],
249
+ .settings__form [data-mosetta-invalid][data-mosetta-bound] {
250
+ border-color: #dc2626 !important;
251
+ box-shadow: 0 0 0 1px #dc2626 !important;
252
+ }
253
+
254
+ /* a bound field wears the editor's colour on its own border — its own shape and radius, whatever
255
+ the form drew (FieldRefs marks it); two pixels, so it reads at a glance */
256
+ .settings__form [data-mosetta-bound] {
257
+ border-color: #2563eb !important;
258
+ box-shadow: 0 0 0 1px #2563eb !important;
259
+ }
260
+
261
+ .settings__form [data-mosetta-bound]:focus-visible {
262
+ box-shadow: 0 0 0 2px #2563eb !important;
263
+ }
264
+
265
+ .settings__picker {
266
+ position: absolute;
267
+ left: 0;
268
+ right: 0;
269
+ z-index: 5;
270
+ max-height: 420px;
271
+ overflow: auto;
272
+ border: 1px solid #d4d4d8;
273
+ border-radius: 8px;
274
+ background: #ffffff;
275
+ box-shadow: 0 8px 24px #0000001f;
276
+ font: var(--mosetta-wb-font-sm)/1.4 ui-sans-serif, system-ui, sans-serif;
277
+ }
278
+
279
+ /* scoped under the picker, like the block picker's: the shared field's `flex: 1` must not win a tie */
280
+ .settings__picker .settings__picker-search-field {
281
+ flex: none;
282
+ width: 100%;
283
+ }
284
+
285
+ .settings__picker-search {
286
+ box-sizing: border-box;
287
+ width: 100%;
288
+ padding: 6px 8px;
289
+ border: 0;
290
+ border-bottom: 1px solid #e4e4e7;
291
+ font: inherit;
292
+ outline: none;
293
+ }
294
+
295
+ .settings__picker-group {
296
+ padding: 6px 8px 2px;
297
+ color: #52525b;
298
+ display: flex;
299
+ gap: 6px;
300
+ align-items: baseline;
301
+ }
302
+
303
+ .settings__picker-where {
304
+ color: #a1a1aa;
305
+ }
306
+
307
+ .settings__picker-row {
308
+ display: flex;
309
+ flex-direction: column;
310
+ gap: 2px;
311
+ width: 100%;
312
+ box-sizing: border-box;
313
+ padding: 6px 10px 6px 20px;
314
+ border: 0;
315
+ background: transparent;
316
+ font: inherit;
317
+ text-align: left;
318
+ cursor: pointer;
319
+ }
320
+
321
+ .settings__picker-row:hover {
322
+ background: #f4f4f5;
323
+ }
324
+
325
+ .settings__picker-row--current {
326
+ background: #eff6ff;
327
+ box-shadow: inset 2px 0 0 #2563eb;
328
+ }
329
+
330
+ /* what names the value — its key, and who reads it when the key does not say */
331
+ .settings__picker-key {
332
+ font-size: var(--mosetta-wb-font-xs);
333
+ color: #71717a;
334
+ }
335
+
336
+ .settings__picker-row--current .settings__picker-key {
337
+ color: #1d4ed8;
338
+ }
339
+
340
+ /* the value itself, whole: the author recognizes a text by reading it */
341
+ .settings__picker-value {
342
+ color: #18181b;
343
+ white-space: pre-wrap;
344
+ overflow-wrap: anywhere;
345
+ }
346
+
347
+ .settings__picker-action {
348
+ display: flex;
349
+ gap: 6px;
350
+ width: 100%;
351
+ padding: 6px 8px;
352
+ border: 0;
353
+ border-top: 1px solid #e4e4e7;
354
+ background: transparent;
355
+ font: inherit;
356
+ color: #52525b;
357
+ text-align: left;
358
+ cursor: pointer;
359
+ }
360
+
361
+ .settings__picker-action:hover {
362
+ background: #f4f4f5;
363
+ }
364
+
365
+ .settings__picker-empty {
366
+ padding: 6px 8px;
367
+ color: #a1a1aa;
368
+ }
369
+
370
+ /* the settings of a node, as one fieldset: disabled on a published revision (read-only) */
371
+ .settings__fieldset {
372
+ min-width: 0;
373
+ margin: 0;
374
+ padding: 0;
375
+ border: 0;
376
+ }
377
+
378
+ .settings__fieldset[disabled] {
379
+ opacity: 0.75;
380
+ }
381
+
382
+ /* a section of the editor's own settings: a tab's mark (settingsTabs.ts), no box of its own */
383
+ .settings__section {
384
+ display: contents;
385
+ }
386
+
387
+ /* A PREFAB's own block, selected on a page: a plaque over its read-only settings — whose block it
388
+ is, why it is not changed here, and the ways on (prefabParts.ts) */
389
+ .settings__part {
390
+ display: flex;
391
+ flex-direction: column;
392
+ gap: 8px;
393
+ margin: 2px 0 8px;
394
+ padding: 8px 10px;
395
+ border: 1px solid #c7d2fe;
396
+ border-radius: 6px;
397
+ background: #eef2ff;
398
+ }
399
+
400
+ .settings__part-text {
401
+ margin: 0;
402
+ color: #3730a3;
403
+ font-size: var(--mosetta-wb-font-sm);
404
+ }
405
+
406
+ .settings__part-actions {
407
+ display: flex;
408
+ flex-wrap: wrap;
409
+ gap: 6px;
410
+ }
411
+
412
+ .settings__part-action {
413
+ height: var(--mosetta-wb-icon-box);
414
+ padding: 0 10px;
415
+ border: 1px solid #c7d2fe;
416
+ border-radius: 5px;
417
+ background: #ffffff;
418
+ color: #3730a3;
419
+ font: inherit;
420
+ font-size: var(--mosetta-wb-font-sm);
421
+ cursor: pointer;
422
+ }
423
+
424
+ .settings__part-action:hover {
425
+ border-color: #818cf8;
426
+ }
427
+
428
+ .settings__part-action--main {
429
+ border-color: #4f46e5;
430
+ background: #4f46e5;
431
+ color: #ffffff;
432
+ }
433
+
434
+ .settings__part-action--main:hover {
435
+ background: #4338ca;
436
+ }
437
+
438
+ /* a block with a NEWER VERSION: the same plaque, in blue (blockUpgrades.ts) */
439
+ .settings__part--up {
440
+ border-color: #bfdbfe;
441
+ background: #eff6ff;
442
+ }
443
+
444
+ .settings__part--up .settings__part-text {
445
+ color: #1e40af;
446
+ }
447
+
448
+ /* a link among the plaque's actions reads like its buttons */
449
+ a.settings__part-action {
450
+ display: inline-flex;
451
+ align-items: center;
452
+ box-sizing: border-box;
453
+ text-decoration: none;
454
+ }
455
+
456
+ /* A block of a DISCONNECTED distribution: the same plaque, in a warning's colours (disconnected.ts) */
457
+ .settings__part--off {
458
+ border-color: #fcd34d;
459
+ background: #fffbeb;
460
+ }
461
+
462
+ .settings__part--off .settings__part-text {
463
+ color: #92400e;
464
+ }
465
+
466
+ .settings__part--off .settings__part-action {
467
+ border-color: #fcd34d;
468
+ color: #92400e;
469
+ }
470
+
471
+ .settings__part--off .settings__part-action:hover {
472
+ border-color: #f59e0b;
473
+ }
474
+
475
+ .settings__part--off .settings__part-action--main {
476
+ border-color: #b45309;
477
+ background: #b45309;
478
+ color: #ffffff;
479
+ }
480
+
481
+ .settings__part--off .settings__part-action--main:hover {
482
+ background: #92400e;
483
+ }