@cardinali-studio/adminkit 1.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 (181) hide show
  1. package/dist/api.d.ts +195 -0
  2. package/dist/api.js +118 -0
  3. package/dist/app/App.d.ts +3 -0
  4. package/dist/app/App.js +320 -0
  5. package/dist/app/app.css +2068 -0
  6. package/dist/app/index.d.ts +18 -0
  7. package/dist/app/index.js +33 -0
  8. package/dist/app/lib/bookings.d.ts +132 -0
  9. package/dist/app/lib/bookings.js +79 -0
  10. package/dist/app/lib/deliveries.d.ts +95 -0
  11. package/dist/app/lib/deliveries.js +104 -0
  12. package/dist/app/lib/delivery-drop.d.ts +1 -0
  13. package/dist/app/lib/delivery-drop.js +72 -0
  14. package/dist/app/lib/delivery-upload.d.ts +78 -0
  15. package/dist/app/lib/delivery-upload.js +585 -0
  16. package/dist/app/lib/groups.d.ts +9 -0
  17. package/dist/app/lib/groups.js +32 -0
  18. package/dist/app/lib/home.d.ts +11 -0
  19. package/dist/app/lib/home.js +67 -0
  20. package/dist/app/lib/overview.d.ts +82 -0
  21. package/dist/app/lib/overview.js +68 -0
  22. package/dist/app/lib/pages.d.ts +24 -0
  23. package/dist/app/lib/pages.js +156 -0
  24. package/dist/app/lib/publish.d.ts +18 -0
  25. package/dist/app/lib/publish.js +63 -0
  26. package/dist/app/lib/reorder.d.ts +3 -0
  27. package/dist/app/lib/reorder.js +31 -0
  28. package/dist/app/lib/requests.d.ts +10 -0
  29. package/dist/app/lib/requests.js +67 -0
  30. package/dist/app/lib/router.d.ts +1 -0
  31. package/dist/app/lib/router.js +14 -0
  32. package/dist/app/lib/settings.d.ts +29 -0
  33. package/dist/app/lib/settings.js +82 -0
  34. package/dist/app/lib/slug.d.ts +3 -0
  35. package/dist/app/lib/slug.js +23 -0
  36. package/dist/app/lib/text.d.ts +1 -0
  37. package/dist/app/lib/text.js +13 -0
  38. package/dist/app/lib/theme.d.ts +7 -0
  39. package/dist/app/lib/theme.js +43 -0
  40. package/dist/app/lib/upload-queue.d.ts +35 -0
  41. package/dist/app/lib/upload-queue.js +167 -0
  42. package/dist/app/routes/AboutEditor.d.ts +15 -0
  43. package/dist/app/routes/AboutEditor.js +122 -0
  44. package/dist/app/routes/Bookings.d.ts +21 -0
  45. package/dist/app/routes/Bookings.js +134 -0
  46. package/dist/app/routes/Deliveries.d.ts +37 -0
  47. package/dist/app/routes/Deliveries.js +563 -0
  48. package/dist/app/routes/GallerySectionsEditor.d.ts +12 -0
  49. package/dist/app/routes/GallerySectionsEditor.js +107 -0
  50. package/dist/app/routes/HomeEditor.d.ts +10 -0
  51. package/dist/app/routes/HomeEditor.js +188 -0
  52. package/dist/app/routes/Overview.d.ts +17 -0
  53. package/dist/app/routes/Overview.js +318 -0
  54. package/dist/app/routes/PageActions.d.ts +12 -0
  55. package/dist/app/routes/PageActions.js +93 -0
  56. package/dist/app/routes/PageEditor.d.ts +23 -0
  57. package/dist/app/routes/PageEditor.js +233 -0
  58. package/dist/app/routes/PageSeoEditor.d.ts +13 -0
  59. package/dist/app/routes/PageSeoEditor.js +102 -0
  60. package/dist/app/routes/PagesList.d.ts +10 -0
  61. package/dist/app/routes/PagesList.js +76 -0
  62. package/dist/app/routes/PortfolioWorksEditor.d.ts +12 -0
  63. package/dist/app/routes/PortfolioWorksEditor.js +139 -0
  64. package/dist/app/routes/Requests.d.ts +13 -0
  65. package/dist/app/routes/Requests.js +30 -0
  66. package/dist/app/routes/SiteSettingsEditor.d.ts +21 -0
  67. package/dist/app/routes/SiteSettingsEditor.js +66 -0
  68. package/dist/app/routes/UploadDock.d.ts +1 -0
  69. package/dist/app/routes/UploadDock.js +66 -0
  70. package/dist/app/routes/WorkshopEditionsEditor.d.ts +12 -0
  71. package/dist/app/routes/WorkshopEditionsEditor.js +101 -0
  72. package/dist/app/site.d.ts +37 -0
  73. package/dist/app/site.js +18 -0
  74. package/dist/app/ui/MediaSlot.d.ts +10 -0
  75. package/dist/app/ui/MediaSlot.js +20 -0
  76. package/dist/app/ui/OpenCard.d.ts +18 -0
  77. package/dist/app/ui/OpenCard.js +20 -0
  78. package/dist/app/ui/ParagraphEditor.d.ts +8 -0
  79. package/dist/app/ui/ParagraphEditor.js +37 -0
  80. package/dist/app/ui/dnd.d.ts +1 -0
  81. package/dist/app/ui/dnd.js +11 -0
  82. package/dist/app/ui/icons.d.ts +26 -0
  83. package/dist/app/ui/icons.js +45 -0
  84. package/dist/config.d.ts +5 -0
  85. package/dist/config.js +26 -0
  86. package/dist/form/MediaBrowser.d.ts +13 -0
  87. package/dist/form/MediaBrowser.js +24 -0
  88. package/dist/form/MediaPicker.d.ts +10 -0
  89. package/dist/form/MediaPicker.js +20 -0
  90. package/dist/form/SchemaForm.d.ts +21 -0
  91. package/dist/form/SchemaForm.js +67 -0
  92. package/dist/form/Trash.d.ts +1 -0
  93. package/dist/form/Trash.js +42 -0
  94. package/dist/form/fields/index.d.ts +1 -0
  95. package/dist/form/fields/index.js +110 -0
  96. package/dist/form/fields/objectList.d.ts +1 -0
  97. package/dist/form/fields/objectList.js +99 -0
  98. package/dist/form/index.d.ts +8 -0
  99. package/dist/form/index.js +8 -0
  100. package/dist/form/media/Lightbox.d.ts +4 -0
  101. package/dist/form/media/Lightbox.js +70 -0
  102. package/dist/form/media/MediaFolders.d.ts +20 -0
  103. package/dist/form/media/MediaFolders.js +116 -0
  104. package/dist/form/media/MediaGrid.d.ts +8 -0
  105. package/dist/form/media/MediaGrid.js +139 -0
  106. package/dist/form/media/UploadDialog.d.ts +4 -0
  107. package/dist/form/media/UploadDialog.js +37 -0
  108. package/dist/form/media/useMediaLibrary.d.ts +55 -0
  109. package/dist/form/media/useMediaLibrary.js +495 -0
  110. package/dist/form/prose/ProseBar.d.ts +21 -0
  111. package/dist/form/prose/ProseBar.js +111 -0
  112. package/dist/form/prose/ProseField.d.ts +11 -0
  113. package/dist/form/prose/ProseField.js +152 -0
  114. package/dist/form/prose/pages.d.ts +6 -0
  115. package/dist/form/prose/pages.js +13 -0
  116. package/dist/form/registry.d.ts +20 -0
  117. package/dist/form/registry.js +15 -0
  118. package/dist/frontmatter.d.ts +20 -0
  119. package/dist/frontmatter.js +202 -0
  120. package/dist/functions/_middleware.js +101 -0
  121. package/dist/functions/_shared/analytics-ga4.js +248 -0
  122. package/dist/functions/_shared/analytics-umami.js +119 -0
  123. package/dist/functions/_shared/assets.js +90 -0
  124. package/dist/functions/_shared/b2.js +300 -0
  125. package/dist/functions/_shared/delivery-crypto.js +99 -0
  126. package/dist/functions/_shared/delivery-purge.js +222 -0
  127. package/dist/functions/_shared/edge-cache.js +37 -0
  128. package/dist/functions/_shared/exif.js +53 -0
  129. package/dist/functions/_shared/github.js +138 -0
  130. package/dist/functions/_shared/imagesize.js +101 -0
  131. package/dist/functions/_shared/media-content.js +200 -0
  132. package/dist/functions/_shared/pending-media.js +98 -0
  133. package/dist/functions/_shared/refs.js +155 -0
  134. package/dist/functions/api/analytics.js +106 -0
  135. package/dist/functions/api/assets/delete.js +241 -0
  136. package/dist/functions/api/assets/folder.js +43 -0
  137. package/dist/functions/api/assets/folders.js +32 -0
  138. package/dist/functions/api/assets/move.js +253 -0
  139. package/dist/functions/api/assets/preview.js +17 -0
  140. package/dist/functions/api/assets/trash/delete.js +13 -0
  141. package/dist/functions/api/assets/trash/empty.js +16 -0
  142. package/dist/functions/api/assets/trash/restore.js +43 -0
  143. package/dist/functions/api/assets/trash.js +28 -0
  144. package/dist/functions/api/assets/upload.js +54 -0
  145. package/dist/functions/api/assets/usage.js +41 -0
  146. package/dist/functions/api/assets.js +94 -0
  147. package/dist/functions/api/deliveries.js +629 -0
  148. package/dist/functions/api/delivery/hero.js +21 -0
  149. package/dist/functions/api/delivery/image.js +35 -0
  150. package/dist/functions/api/delivery/multipart.js +261 -0
  151. package/dist/functions/api/pages/get.js +26 -0
  152. package/dist/functions/api/pages.js +354 -0
  153. package/dist/functions/api/publish.js +134 -0
  154. package/dist/functions/api/requests.js +57 -0
  155. package/dist/functions/api/settings.js +236 -0
  156. package/dist/functions/api/singleton.js +105 -0
  157. package/dist/functions/api/storage.js +125 -0
  158. package/dist/functions/index.js +124 -0
  159. package/dist/functions/site.js +38 -0
  160. package/dist/gallery.d.ts +28 -0
  161. package/dist/gallery.js +18 -0
  162. package/dist/index.d.ts +10 -0
  163. package/dist/index.js +10 -0
  164. package/dist/model.d.ts +2 -0
  165. package/dist/model.js +47 -0
  166. package/dist/normalize.d.ts +50 -0
  167. package/dist/normalize.js +120 -0
  168. package/dist/preview.d.ts +3 -0
  169. package/dist/preview.js +82 -0
  170. package/dist/prose.d.ts +17 -0
  171. package/dist/prose.js +205 -0
  172. package/dist/registries.d.ts +43 -0
  173. package/dist/registries.js +29 -0
  174. package/dist/serialize.d.ts +26 -0
  175. package/dist/serialize.js +136 -0
  176. package/dist/testing.d.ts +26 -0
  177. package/dist/testing.js +66 -0
  178. package/dist/theme.css +636 -0
  179. package/dist/yaml.d.ts +8 -0
  180. package/dist/yaml.js +90 -0
  181. package/package.json +97 -0
package/dist/theme.css ADDED
@@ -0,0 +1,636 @@
1
+ /* Tema dell'admin — SOLE custom property e le regole che le usano.
2
+ *
3
+ * Questi sono i **default del motore**, non l'aspetto di un cliente. Il sito che monta il kit
4
+ * li sovrascrive nel suo `admin/src/theme.css`, che viene importato DOPO: i default restano
5
+ * qui perché il kit deve funzionare da solo, e una variabile che un cliente non ridefinisce
6
+ * non deve dare un pannello rotto senza nessun errore.
7
+ *
8
+ * La regola che tiene in piedi la separazione: se una richiesta di aspetto non si può esprimere
9
+ * ridefinendo una variabile, la risposta giusta NON è modificare un componente qui dentro — è
10
+ * aggiungere una variabile, così la ottengono tutti i siti. Un componente modificato per un
11
+ * cliente è il primo passo verso un fork del motore.
12
+ *
13
+ * Le classi sono prefissate `ak-` per non incrociare né site.css né il vecchio admin.css.
14
+ *
15
+ * NB: questo file si consuma da `dist/` (l'export del pacchetto è `"./theme.css":
16
+ * "./dist/theme.css"`, la copia la fa `npm run build` di adminkit), quindi dopo averlo toccato
17
+ * serve `npm run build:pkgs`. Il `theme.css` del cliente invece lo compila Vite insieme
18
+ * all'admin: i due file si comportano diversamente, ed è già costata una modifica che sembrava
19
+ * non avere effetto.
20
+ */
21
+
22
+ :root {
23
+ color-scheme: light;
24
+ --ak-bg: #fbfbfa;
25
+ --ak-panel: #ffffff;
26
+ --ak-ink: #1a1a1a;
27
+ --ak-ink-quiet: #6b6b6b;
28
+ --ak-line: #e4e4e2;
29
+ --ak-accent: #b94732;
30
+ --ak-accent-ink: #ffffff;
31
+ --ak-accent-hover: #a33d2a;
32
+ --ak-danger: #b00020;
33
+ --ak-radius: 6px;
34
+ --ak-gap: 14px;
35
+ --ak-shadow: 0 1px 2px rgba(0, 0, 0, .05);
36
+
37
+ /* Il guscio a tre colonne. Stanno qui e non nell'app perché sono la leva con cui un altro
38
+ sito cambia pelle senza toccare un componente: la colonna scura, la sua larghezza, il grigio
39
+ dell'elenco. */
40
+ --ak-rail-w: 208px;
41
+ --ak-col-w: 272px;
42
+ --ak-rail-bg: #17181a;
43
+ --ak-rail-ink: #9b9b98;
44
+ --ak-rail-ink-on: #ffffff;
45
+ --ak-rail-hover: rgba(255, 255, 255, .06);
46
+ --ak-rail-on: rgba(255, 255, 255, .11);
47
+ --ak-rail-line: rgba(255, 255, 255, .10);
48
+ --ak-col-bg: #f4f4f2;
49
+ /* Due nomi usati da tempo in `.bk-*` (Prenotazioni) sempre con un fallback hardcoded
50
+ (`var(--ak-muted,#777)`, `var(--ak-paper,#fff)`), perché non erano mai stati dichiarati
51
+ davvero: senza una vera custom property qui, quel fallback è tutto ciò che Prenotazioni
52
+ vedrà mai, tema scuro compreso. Sono alias, non nuovi concetti — non aggiungono un quinto
53
+ piano alla gerarchia delle superfici, si limitano a dare un nome che `.bk-*` già usa a due
54
+ token che esistono già. */
55
+ --ak-muted: var(--ak-ink-quiet);
56
+ --ak-paper: var(--ak-panel);
57
+ /* Fra `--ak-ink-quiet` e `--ak-line`: usata da alcune regole (`.dl-state`, `.dl-total`,
58
+ `.ov-stat-quiet`) da prima che fosse mai dichiarata — con la sola varianti col fallback
59
+ `var(--ak-ink-faint, var(--ak-ink-quiet))` funzionavano per caso, le altre restavano senza
60
+ valore. */
61
+ --ak-ink-faint: #9a9a94;
62
+ /* Lato della cella nella griglia dei media: cambia quante foto entrano per riga. */
63
+ --ak-cell: 152px;
64
+ /* Il passaggio del mouse è un velo, la SELEZIONE è un pannello bianco con la sua barra
65
+ d'accento: se i due stati si somigliano, l'elenco smette di dire dove sei. */
66
+ --ak-hover: rgba(0, 0, 0, .045);
67
+ --ak-font: ui-sans-serif, system-ui, -apple-system, "Helvetica Neue", sans-serif;
68
+ --ak-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
69
+ --ak-fs: 14px;
70
+ --ak-fs-small: 12px;
71
+
72
+ /* ---- Superfici senza tratti -------------------------------------------------------------
73
+ La direzione nuova del pannello: a separare sono il colore del fondo e l'ombra, non un
74
+ bordo di un pixel. Un contorno disegna una scatola, e una schermata piena di scatole si
75
+ legge come un modulo da compilare invece che come il contenuto che rappresenta.
76
+
77
+ QUATTRO piani, e non di più — al quinto la gerarchia smette di leggersi:
78
+
79
+ --ak-ground il foglio sotto tutto: PIÙ PROFONDO di `--ak-bg`, ed è questa la leva
80
+ che dà contrasto a una superficie bianca. Con un fondo quasi bianco
81
+ anche lui, una card resta appesa alla sola ombra e sparisce.
82
+ --ak-panel la superficie sollevata (già dichiarata sopra: bianca)
83
+ --ak-inset un blocco incassato DENTRO una superficie
84
+ --ak-inset-deep il pozzetto: vaschette dei segmentati, segnaposto delle immagini
85
+
86
+ Le ombre hanno sempre DUE componenti: una vicina che definisce il bordo e una lontana e
87
+ molto diluita che dà l'altezza. Con la sola vicina una card sembra stampata sul foglio
88
+ invece che appoggiata sopra; con la sola lontana sembra sporca.
89
+
90
+ Stanno qui e non in `admin/src/app.css` perché sono decisioni di aspetto, e questo file è
91
+ l'unica leva di personalizzazione per sito: un cliente con un'identità più fredda cambia
92
+ questi quattro valori e si porta dietro ogni schermata che li usa. Erano dentro `.pw`
93
+ (l'editor dei lavori) finché a usarli era una sola schermata; dalla seconda, una copia
94
+ sarebbe stata la prima occasione per farle divergere.
95
+
96
+ NB: questo file si consuma da `dist/`, non da `src/` — dopo averlo toccato serve
97
+ `npm run build:pkgs`, o l'admin continua a usare gli stili vecchi senza dare errore. */
98
+ --ak-ground: #efeeea;
99
+ --ak-inset: #f4f4f1;
100
+ --ak-inset-deep: #e7e7e2;
101
+ --ak-lift-1: 0 1px 2px rgba(20, 20, 24, .07), 0 5px 12px -8px rgba(20, 20, 24, .28);
102
+ --ak-lift-2: 0 1px 2px rgba(20, 20, 24, .06), 0 10px 24px -14px rgba(20, 20, 24, .35);
103
+ --ak-lift-3: 0 2px 6px rgba(20, 20, 24, .07), 0 28px 56px -28px rgba(20, 20, 24, .45);
104
+ /* L'anello di focus sostituisce il bordo che non c'è: senza, un campo riempito non ha modo
105
+ di dire dove sta il cursore. */
106
+ --ak-ring: 0 0 0 3px color-mix(in srgb, var(--ak-accent) 20%, transparent);
107
+ }
108
+
109
+ /* ---- Tema scuro --------------------------------------------------------------------------
110
+ Stessa gerarchia di elevazione del chiaro (ground più profondo di bg, bg più profondo di
111
+ panel), ma invertita in luminanza: in un'interfaccia scura una superficie sale illuminandosi,
112
+ non ombreggiandosi — l'ombra da sola smette di leggersi su un fondo già quasi nero.
113
+
114
+ Non tutto si ridefinisce. Il rail è già scuro anche nel tema chiaro (`--ak-rail-bg`), quindi
115
+ il suo contrasto interno non dipende da questo blocco; le dimensioni (`--ak-radius`,
116
+ `--ak-gap`, le larghezze, `--ak-cell`, i font) non hanno un colore da invertire. `--ak-ring`
117
+ non si ridefinisce nemmeno lui: è già `color-mix(…, var(--ak-accent) …)`, quindi eredita da
118
+ solo il nuovo accento sotto.
119
+
120
+ L'accento non cambia solo tono: `--ak-accent-ink` passa da bianco a un quasi-nero caldo,
121
+ perché serve sia come testo (link, barra di selezione) sia come sfondo pieno di un bottone
122
+ (`.ak-btn-primary`) — un solo hex chiaro non regge bene entrambi i ruoli su un fondo quasi
123
+ nero. Valori verificati per contrasto WCAG (soglia AA 4.5:1 testo, 3:1 componenti UI): ink su
124
+ bg/panel/ground 13-15:1, ink-quiet 6-7:1, ink-faint 4.6-5.3:1, accento come testo 4.7-5.1:1,
125
+ accent-ink su accento pieno 5.5:1.
126
+
127
+ Un sito che in futuro vuole un accento scuro diverso dal default deve scrivere il proprio
128
+ override nello STESSO selettore scoped (`:root[data-theme="dark"]`), non in un `:root`
129
+ semplice: quest'ultimo (specificità 0,1,0) perde sempre contro questo (0,2,0), a prescindere
130
+ dall'ordine di import — un override scritto nel modo sbagliato sparirebbe senza errore non
131
+ appena si passa al tema scuro. */
132
+ :root[data-theme="dark"] {
133
+ color-scheme: dark;
134
+ --ak-ground: #16171a;
135
+ --ak-bg: #1c1d20;
136
+ --ak-col-bg: #1a1b1e;
137
+ --ak-inset: #1a1b1e;
138
+ --ak-inset-deep: #131417;
139
+ --ak-panel: #232428;
140
+
141
+ --ak-line: rgba(255, 255, 255, .12);
142
+ --ak-hover: rgba(255, 255, 255, .06);
143
+ --ak-shadow: 0 1px 2px rgba(0, 0, 0, .4), inset 0 1px 0 rgba(255, 255, 255, .05);
144
+ --ak-lift-1: 0 1px 2px rgba(0, 0, 0, .5), 0 6px 16px -8px rgba(0, 0, 0, .6);
145
+ --ak-lift-2: 0 1px 2px rgba(0, 0, 0, .45), 0 12px 28px -14px rgba(0, 0, 0, .65);
146
+ --ak-lift-3: 0 2px 6px rgba(0, 0, 0, .5), 0 30px 60px -28px rgba(0, 0, 0, .7);
147
+
148
+ --ak-ink: #ececea;
149
+ --ak-ink-quiet: #a3a3a0;
150
+ --ak-ink-faint: #8c8c86;
151
+
152
+ --ak-accent: #d9713f;
153
+ --ak-accent-ink: #241209;
154
+ --ak-accent-hover: #c0602f;
155
+ --ak-danger: #ef6a5c;
156
+ }
157
+
158
+ .ak-form { display: flex; flex-direction: column; gap: calc(var(--ak-gap) * 1.4); }
159
+
160
+ .ak-group {
161
+ border: 1px solid var(--ak-line); border-radius: var(--ak-radius);
162
+ padding: var(--ak-gap); margin: 0;
163
+ display: flex; flex-direction: column; gap: var(--ak-gap);
164
+ background: var(--ak-panel);
165
+ }
166
+ .ak-legend {
167
+ font: 600 var(--ak-fs-small)/1 var(--ak-font);
168
+ text-transform: uppercase; letter-spacing: .08em; color: var(--ak-ink-quiet);
169
+ padding: 0 6px;
170
+ }
171
+
172
+ .ak-field { display: flex; flex-direction: column; gap: 5px; }
173
+ .ak-field-inline { flex-direction: row; align-items: center; gap: 8px; }
174
+ .ak-label { font: 500 var(--ak-fs)/1.3 var(--ak-font); color: var(--ak-ink); }
175
+ .ak-hint { font: 400 var(--ak-fs-small)/1.4 var(--ak-font); color: var(--ak-ink-quiet); }
176
+ .ak-error { font: 500 var(--ak-fs-small)/1.4 var(--ak-font); color: var(--ak-danger); }
177
+
178
+ .ak-input {
179
+ font: 400 var(--ak-fs)/1.4 var(--ak-font);
180
+ color: var(--ak-ink); background: var(--ak-panel);
181
+ border: 1px solid var(--ak-line); border-radius: var(--ak-radius);
182
+ padding: 8px 10px; width: 100%;
183
+ }
184
+ /* Le caselle e i cursori prendono l'accento del sito invece del blu di sistema. */
185
+ input, select, textarea, button { accent-color: var(--ak-accent); }
186
+
187
+ .ak-input:focus-visible { outline: 2px solid var(--ak-accent); outline-offset: 1px; }
188
+ .ak-textarea { resize: vertical; font-family: var(--ak-font-mono); font-size: var(--ak-fs-small); }
189
+
190
+ .ak-btn {
191
+ font: 500 var(--ak-fs-small)/1 var(--ak-font);
192
+ padding: 7px 12px; border-radius: var(--ak-radius);
193
+ border: 1px solid var(--ak-line); background: var(--ak-panel); color: var(--ak-ink);
194
+ cursor: pointer;
195
+ }
196
+ a.ak-btn { text-decoration: none; display: inline-flex; align-items: center; }
197
+ .ak-btn:hover { border-color: var(--ak-ink-quiet); }
198
+ .ak-btn:disabled { opacity: .5; cursor: default; }
199
+ .ak-btn:disabled:hover { border-color: var(--ak-line); }
200
+ .ak-btn-quiet { color: var(--ak-ink-quiet); }
201
+ /* Il bottone dell'azione principale della schermata — «Salva e pubblica», «Crea». Ce n'è al
202
+ massimo UNO visibile per volta: se due gesti si contendono l'accento, l'accento non indica più
203
+ niente. */
204
+ .ak-btn-primary {
205
+ background: var(--ak-accent); border-color: var(--ak-accent); color: var(--ak-accent-ink);
206
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
207
+ }
208
+ .ak-btn-primary:hover:not(:disabled) { background: var(--ak-accent-hover); border-color: var(--ak-accent-hover); }
209
+ .ak-btn-primary:disabled:hover { border-color: var(--ak-accent); }
210
+ .ak-btn-block { width: 100%; }
211
+ .ak-icon {
212
+ border: 0; background: transparent; cursor: pointer; color: var(--ak-ink-quiet);
213
+ font: 500 var(--ak-fs)/1 var(--ak-font); padding: 2px 6px;
214
+ }
215
+ .ak-icon:hover { color: var(--ak-ink); }
216
+
217
+ .ak-thumb {
218
+ display: block; width: 100%; aspect-ratio: 1; object-fit: cover;
219
+ border-radius: var(--ak-radius); background: var(--ak-line);
220
+ }
221
+ .ak-thumb-empty { opacity: .5; }
222
+ .ak-media-one { display: flex; gap: var(--ak-gap); align-items: flex-start; }
223
+ .ak-media-one .ak-thumb { width: 96px; }
224
+ .ak-media-actions { display: flex; flex-direction: column; gap: 6px; }
225
+ .ak-media-grid {
226
+ display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
227
+ gap: 8px; margin-bottom: 8px;
228
+ }
229
+ .ak-media-item { margin: 0; }
230
+ .ak-media-item-bar { display: flex; justify-content: space-between; align-items: center; }
231
+ /* Voce nascosta (widget r2Curated): resta nell'elenco e al suo posto, ma non va online.
232
+ Si vede spenta, non sparita — è tutta la differenza fra "nascosta" e "tolta". La barra dei
233
+ bottoni resta a piena opacità, altrimenti quello per rimetterla online sarebbe il meno
234
+ leggibile della riga. */
235
+ .ak-media-item.is-hidden .ak-thumb { opacity: .28; filter: grayscale(1); }
236
+
237
+ /* ---- Media: il guscio del modale di scelta ------------------------------------------ */
238
+ .ak-spacer { flex: 1; }
239
+
240
+ .mb-modal {
241
+ position: fixed; inset: 0; z-index: 50; background: rgba(0, 0, 0, .35);
242
+ display: grid; place-items: center; padding: 4vh 4vw;
243
+ }
244
+ .mb-modal-panel {
245
+ background: var(--ak-bg); border-radius: var(--ak-radius);
246
+ width: 100%; max-width: 1100px; max-height: 92vh; overflow: auto; padding: var(--ak-gap);
247
+ }
248
+
249
+ .mb { display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: var(--ak-gap); align-items: start; }
250
+ .mb-side { position: sticky; top: 0; }
251
+ .mb-main { min-width: 0; }
252
+
253
+ /* ---- Media: le cartelle -------------------------------------------------------------- */
254
+ .mf { display: flex; flex-direction: column; }
255
+ .mf-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; }
256
+ .mf-item {
257
+ position: relative; display: flex; align-items: center;
258
+ border: 1px solid transparent; border-radius: 6px; background: transparent;
259
+ }
260
+ .mf-open {
261
+ flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; text-align: left;
262
+ border: 0; background: transparent; cursor: pointer;
263
+ padding: 8px 10px; font: 500 13px/1.3 var(--ak-font); color: var(--ak-ink);
264
+ }
265
+ .mf-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
266
+ .mf-count { font: 400 var(--ak-fs-small)/1 var(--ak-font); color: var(--ak-ink-quiet); font-variant-numeric: tabular-nums; }
267
+ .mf-item:hover { background: var(--ak-hover); }
268
+ .mf-item.is-on {
269
+ background: var(--ak-panel); border-color: var(--ak-line);
270
+ box-shadow: var(--ak-shadow), inset 2px 0 0 var(--ak-accent);
271
+ }
272
+ .mf-item.is-on .mf-name { font-weight: 600; }
273
+ /* Bersaglio di uno spostamento. Deve gridare: un drop andato nel posto sbagliato sposta file
274
+ veri dentro il bucket, e la strada indietro è un altro drop. */
275
+ .mf-item.is-over {
276
+ background: var(--ak-panel); border-color: var(--ak-accent); border-style: dashed;
277
+ color: var(--ak-accent);
278
+ }
279
+ .mf-item.is-over .mf-count { color: var(--ak-accent); }
280
+ /* «Senza cartella» è un'altra cosa dalle cartelle: sopra la riga, non nell'elenco. */
281
+ .mf-loose { margin-bottom: 10px; }
282
+ .mf-list + .mf-list { padding-top: 12px; border-top: 1px solid var(--ak-line); }
283
+ .mf-new { margin-top: 10px; align-self: flex-start; }
284
+ /* Compare solo mentre si trascina una cartella: è il bersaglio del primo livello, che prima non
285
+ esisteva — l'unico modo era indovinare che «Tutte» lo fosse. */
286
+ .mf-root-drop {
287
+ margin-top: 10px; padding: 12px 10px; border-radius: var(--ak-radius);
288
+ border: 2px dashed var(--ak-line); text-align: center;
289
+ font: 500 var(--ak-fs-small)/1.3 var(--ak-font); color: var(--ak-ink-quiet);
290
+ }
291
+ .mf-root-drop.is-over { border-color: var(--ak-accent); color: var(--ak-accent); background: var(--ak-panel); }
292
+ /* Il cestino chiude la colonna, staccato: è un posto dove si va, non una cartella fra le altre. */
293
+ .mf-tail { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--ak-line); }
294
+ .mf-ico { flex: none; color: var(--ak-ink-quiet); }
295
+ .mf-item.is-on .mf-ico { color: var(--ak-accent); }
296
+
297
+ .mf-actions { position: relative; flex: none; }
298
+ .mf-more {
299
+ display: flex; border: 0; background: transparent; cursor: pointer; border-radius: 4px;
300
+ color: var(--ak-ink-quiet); padding: 4px 6px; opacity: 0;
301
+ font: 500 var(--ak-fs-small)/1 var(--ak-font);
302
+ }
303
+ .mf-item:hover .mf-more, .mf-item.is-on .mf-more, .mf-more:focus-visible { opacity: 1; }
304
+ .mf-more:hover { background: var(--ak-bg); color: var(--ak-ink); }
305
+ .mf-menu {
306
+ position: absolute; top: calc(100% + 2px); z-index: 30;
307
+ min-width: 200px; padding: 4px; display: flex; flex-direction: column; gap: 1px;
308
+ background: var(--ak-panel); border: 1px solid var(--ak-line); border-radius: 8px;
309
+ box-shadow: 0 12px 30px rgba(0, 0, 0, .16);
310
+ }
311
+ .mf-menu.is-left { right: 0; left: auto; }
312
+ .mf-menu.is-right { left: 0; right: auto; }
313
+ .mf-menu > button {
314
+ border: 0; background: transparent; cursor: pointer; text-align: left; border-radius: 5px;
315
+ padding: 7px 9px; font: 400 13px/1.2 var(--ak-font); color: var(--ak-ink);
316
+ }
317
+ .mf-menu > button:hover { background: var(--ak-bg); }
318
+ .mf-menu > button.mf-danger { color: var(--ak-danger); }
319
+ .mf-menu-form { min-width: 260px; padding: 10px; gap: 8px; }
320
+ .mf-menu-row { display: flex; gap: 6px; }
321
+
322
+ /* ---- Media: la griglia ---------------------------------------------------------------- */
323
+ /* `min-height: 100%`: la zona che riceve i file trascinati è alta quanto il pannello, non
324
+ quanto il suo contenuto. In una cartella vuota — cioè proprio quando si sta caricando — era
325
+ una striscia di quaranta pixel in cima, e tutto il resto dello schermo non riceveva niente. */
326
+ .mg-wrap { display: flex; flex-direction: column; gap: 12px; min-width: 0; min-height: 100%; position: relative; }
327
+ .mg-wrap.is-dropping { outline: 2px dashed var(--ak-accent); outline-offset: -6px; border-radius: var(--ak-radius); }
328
+ .mg-dropzone {
329
+ position: absolute; inset: 0; z-index: 5; pointer-events: none;
330
+ display: grid; place-items: center; border-radius: var(--ak-radius);
331
+ background: rgba(255, 255, 255, .82);
332
+ font: 500 var(--ak-fs)/1.4 var(--ak-font); color: var(--ak-ink);
333
+ }
334
+ .mg-bar { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; min-height: 32px; }
335
+ .mg-all { display: flex; align-items: center; gap: 8px; cursor: pointer; font: 500 13px/1 var(--ak-font); }
336
+ .mg-all input { width: 15px; height: 15px; margin: 0; }
337
+ .mg-act {
338
+ border: 0; background: transparent; cursor: pointer; padding: 4px 0;
339
+ font: 500 13px/1 var(--ak-font); color: var(--ak-accent);
340
+ }
341
+ .mg-act:hover:not(:disabled) { text-decoration: underline; }
342
+ .mg-act:disabled { color: var(--ak-ink-quiet); cursor: default; }
343
+ .mg-act.is-danger { color: var(--ak-danger); }
344
+ .mg-menu-wrap { position: relative; display: inline-flex; }
345
+ .mg-menu {
346
+ position: absolute; top: calc(100% + 4px); left: 0; z-index: 30;
347
+ min-width: 190px; max-height: 320px; overflow: auto; padding: 4px;
348
+ display: flex; flex-direction: column; gap: 1px;
349
+ background: var(--ak-panel); border: 1px solid var(--ak-line); border-radius: 8px;
350
+ box-shadow: 0 12px 30px rgba(0, 0, 0, .16);
351
+ }
352
+ .mg-menu > button {
353
+ display: flex; justify-content: space-between; gap: 12px;
354
+ border: 0; background: transparent; cursor: pointer; text-align: left; border-radius: 5px;
355
+ padding: 7px 9px; font: 400 13px/1.2 var(--ak-font); color: var(--ak-ink);
356
+ }
357
+ .mg-menu > button:hover { background: var(--ak-bg); }
358
+ .mg-sort { font: inherit; border: 1px solid var(--ak-line); border-radius: 4px; padding: 2px 4px; }
359
+ .mg-chips { display: flex; flex-wrap: wrap; gap: 6px; }
360
+ .mg-section { display: flex; flex-direction: column; gap: 10px; }
361
+ .mg-section-head { display: flex; align-items: center; gap: 14px; }
362
+ .mg-section-title {
363
+ margin: 0; font: 600 11px/1 var(--ak-font); text-transform: uppercase; letter-spacing: .09em;
364
+ color: var(--ak-ink-quiet);
365
+ }
366
+ /* L'avanzamento di un'operazione lunga non è una nota a margine: è la risposta alla domanda
367
+ «sta facendo qualcosa?». Se non si vede, chi guarda ricarica la pagina. */
368
+ .mg-progress {
369
+ margin: 0; padding: 8px 12px; border-radius: var(--ak-radius);
370
+ background: var(--ak-panel); border: 1px solid var(--ak-accent);
371
+ font: 500 var(--ak-fs-small)/1.4 var(--ak-font); color: var(--ak-ink);
372
+ }
373
+
374
+ /* Celle di ingombro uguale, immagine INTERA dentro. Prima erano quadrati con `object-fit: cover`:
375
+ un verticale sembrava un quadrato e per riconoscere una foto bisognava aprirla. */
376
+ .mg { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--ak-cell), 1fr)); gap: 18px 14px; }
377
+ .mg-item { margin: 0; min-width: 0; display: flex; flex-direction: column; gap: 7px; }
378
+ .mg-frame {
379
+ position: relative; height: var(--ak-cell); cursor: pointer;
380
+ display: flex; align-items: center; justify-content: center;
381
+ }
382
+ .mg-img {
383
+ max-width: 100%; max-height: 100%; width: auto; height: auto; display: block;
384
+ background: var(--ak-line); box-shadow: 0 1px 4px rgba(0, 0, 0, .16);
385
+ }
386
+ /* La cornice della selezione è DECORAZIONE: `pointer-events: none` o si prende lei il click.
387
+ Essendo un ::after del frame viene disegnata dopo la casella, quindi le stava sopra: premere
388
+ la spunta di un elemento già selezionato non lo deselezionava, lo apriva. */
389
+ .mg-item.is-on .mg-frame::after {
390
+ content: ""; position: absolute; inset: -6px; pointer-events: none;
391
+ border: 2px solid var(--ak-accent); border-radius: 4px;
392
+ }
393
+ .mg-check {
394
+ position: absolute; left: 4px; bottom: 4px; z-index: 3; width: 20px; height: 20px; border-radius: 4px;
395
+ display: none; align-items: center; justify-content: center; cursor: pointer;
396
+ border: 1px solid var(--ak-line); background: rgba(255, 255, 255, .94);
397
+ font: 700 12px/1 var(--ak-font); color: var(--ak-accent-ink);
398
+ }
399
+ .mg-item:hover .mg-check, .mg-item.is-on .mg-check { display: flex; }
400
+ .mg-item.is-on .mg-check { background: var(--ak-accent); border-color: var(--ak-accent); }
401
+ .mg-cap {
402
+ font: 400 11px/1.3 var(--ak-font); color: var(--ak-ink-quiet); text-align: center;
403
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
404
+ }
405
+
406
+ /* ---- Media: una cartella dentro la griglia ------------------------------------------- */
407
+ /* Occupa uno slot come una foto: è contenuto anche lei, si seleziona e si trascina — e una cosa
408
+ che si prende e si sposta deve avere la dimensione di un bersaglio, non di un chip. */
409
+ .mg-folder { position: relative; }
410
+ .mg-folder .mg-frame {
411
+ border: 1px solid var(--ak-line); border-radius: var(--ak-radius); background: var(--ak-panel);
412
+ }
413
+ .mg-folder:hover .mg-frame { border-color: var(--ak-ink-quiet); }
414
+ .mg-folder-ico { width: 58%; height: auto; fill: var(--ak-line); stroke: var(--ak-ink-quiet); stroke-width: 1.2; }
415
+ .mg-folder .mg-cap { font-weight: 500; color: var(--ak-ink); }
416
+ /* Bersaglio di un drop: la cartella sta per ricevere qualcosa. */
417
+ .mg-folder.is-over .mg-frame {
418
+ border: 2px dashed var(--ak-accent); background: var(--ak-bg);
419
+ }
420
+ .mg-folder.is-over .mg-folder-ico { fill: var(--ak-panel); stroke: var(--ak-accent); }
421
+ .mg-folder-menu { position: absolute; top: 6px; right: 6px; z-index: 4; }
422
+ .mg-folder-menu .mf-more { background: rgba(255, 255, 255, .92); border: 1px solid var(--ak-line); }
423
+ /* Erano invisibili: `.mf-more` nasce con opacity 0 e la regola che lo accende guarda `.mf-item`,
424
+ cioè la riga della colonna di sinistra. Dentro una cella della griglia non c'è nessun
425
+ `.mf-item`, quindi il menu restava spento anche al passaggio del mouse. */
426
+ .mg-folder:hover .mg-folder-menu .mf-more,
427
+ .mg-folder.is-on .mg-folder-menu .mf-more,
428
+ .mg-folder-menu .mf-more:focus-visible { opacity: 1; }
429
+ .mg-foot {
430
+ position: sticky; bottom: 0; z-index: 2;
431
+ display: flex; align-items: center; gap: 8px; padding: 10px 12px;
432
+ background: var(--ak-panel); border: 1px solid var(--ak-line); border-radius: var(--ak-radius);
433
+ }
434
+
435
+ /* ---- Media: la foto a schermo intero -------------------------------------------------- */
436
+ .lb { position: fixed; inset: 0; z-index: 60; background: #000; display: flex; flex-direction: column; }
437
+ .lb-top {
438
+ flex: none; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px;
439
+ padding: 10px 14px; color: #fff;
440
+ }
441
+ .lb-id { display: flex; flex-direction: column; align-items: center; gap: 2px; }
442
+ .lb-name { font: 500 13px/1.2 var(--ak-font); }
443
+ .lb-count { font: 400 12px/1 var(--ak-font); opacity: .6; }
444
+ .lb-tools { display: flex; justify-content: flex-end; gap: 2px; }
445
+ .lb-btn {
446
+ display: inline-flex; align-items: center; gap: 6px; text-decoration: none;
447
+ border: 0; background: transparent; color: #fff; cursor: pointer; border-radius: 6px;
448
+ padding: 8px 10px; font: 500 13px/1 var(--ak-font);
449
+ }
450
+ .lb-btn:hover { background: rgba(255, 255, 255, .13); }
451
+ .lb-btn.is-on { background: rgba(255, 255, 255, .2); }
452
+ .lb-tools .lb-btn { min-width: 34px; justify-content: center; }
453
+ .lb-stage {
454
+ flex: 1; min-height: 0; position: relative;
455
+ display: flex; align-items: center; justify-content: center; padding: 0 66px 22px;
456
+ }
457
+ .lb-img { max-width: 100%; max-height: 100%; object-fit: contain; }
458
+ .lb-nav {
459
+ position: absolute; top: 50%; transform: translateY(-50%);
460
+ width: 44px; height: 44px; border-radius: 50%; border: 0; cursor: pointer;
461
+ background: rgba(255, 255, 255, .14); color: #fff; font: 400 24px/1 var(--ak-font);
462
+ }
463
+ .lb-nav:hover:not(:disabled) { background: rgba(255, 255, 255, .26); }
464
+ .lb-nav:disabled { opacity: .2; cursor: default; }
465
+ .lb-prev { left: 12px; }
466
+ .lb-next { right: 12px; }
467
+ .lb-info {
468
+ position: absolute; right: 14px; top: 58px; width: 290px; z-index: 2;
469
+ background: rgba(22, 22, 22, .94); border: 1px solid rgba(255, 255, 255, .16);
470
+ border-radius: 8px; padding: 12px 14px; color: #eaeaea;
471
+ }
472
+ .lb-info dl { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 5px 14px; font: 400 12px/1.4 var(--ak-font); }
473
+ .lb-info dt { color: #9b9b98; }
474
+ .lb-info dd { margin: 0; word-break: break-word; }
475
+
476
+ /* ---- Media: il dialogo di caricamento ------------------------------------------------- */
477
+ .up { position: fixed; inset: 0; z-index: 70; background: rgba(0, 0, 0, .42); display: grid; place-items: center; padding: 5vh 5vw; }
478
+ .up-panel {
479
+ position: relative; width: 100%; max-width: 620px; padding: 30px 28px 24px;
480
+ display: flex; flex-direction: column; gap: 12px;
481
+ background: var(--ak-panel); border-radius: 10px; box-shadow: 0 24px 60px rgba(0, 0, 0, .3);
482
+ }
483
+ .up-close {
484
+ position: absolute; top: 8px; right: 12px; border: 0; background: transparent; cursor: pointer;
485
+ color: var(--ak-ink-quiet); font: 400 22px/1 var(--ak-font);
486
+ }
487
+ .up-drop {
488
+ display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 40px 20px;
489
+ border: 2px dashed var(--ak-line); border-radius: 8px; background: var(--ak-bg);
490
+ }
491
+ .up-drop.is-over { border-color: var(--ak-accent); background: #fdf4f2; }
492
+ .up-cloud { color: var(--ak-ink-quiet); }
493
+ .up-hint { margin: 0; font: 400 13px/1.4 var(--ak-font); color: var(--ak-ink-quiet); }
494
+ .up-dest { margin: 0; font: 400 var(--ak-fs-small)/1.4 var(--ak-font); color: var(--ak-ink-quiet); }
495
+
496
+ /* ---- Cestino ------------------------------------------------------------------------ */
497
+ .tr { display: flex; flex-direction: column; gap: 10px; }
498
+ .tr-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
499
+ .tr-row {
500
+ display: flex; align-items: center; gap: 10px; padding: 8px 10px;
501
+ background: var(--ak-panel); border: 1px solid var(--ak-line); border-radius: var(--ak-radius);
502
+ }
503
+ .tr-thumb { width: 44px; height: 44px; object-fit: cover; border-radius: 4px; background: var(--ak-line); }
504
+ .tr-name { font: 500 var(--ak-fs-small)/1.3 var(--ak-font); }
505
+ .tr-orig { flex: 1; font: 400 11px/1.3 var(--ak-font-mono); color: var(--ak-ink-quiet);
506
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
507
+
508
+ /* ---- objectList (l'unico widget ricorsivo) ------------------------------------------- */
509
+ .ol-obj { display: flex; flex-direction: column; gap: var(--ak-gap); }
510
+ .ol-list {
511
+ border: 1px dashed var(--ak-line); border-radius: var(--ak-radius);
512
+ padding: 10px; margin: 0; display: flex; flex-direction: column; gap: 10px;
513
+ }
514
+ .ol-item {
515
+ border: 1px solid var(--ak-line); border-radius: var(--ak-radius);
516
+ padding: 10px; display: flex; flex-direction: column; gap: 10px; background: var(--ak-bg);
517
+ }
518
+ .ol-item-bar { display: flex; align-items: center; gap: 4px; }
519
+
520
+ /* ---- Anteprima locale (pannello di Regia) ------------------------------------------- */
521
+ .rg { display: flex; flex-direction: column; gap: 10px; max-width: 720px; }
522
+ .rg-top {
523
+ display: flex; align-items: center; gap: 10px; padding: 10px 12px;
524
+ background: var(--ak-panel); border: 1px solid var(--ak-line); border-radius: var(--ak-radius);
525
+ }
526
+ .rg-name { font: 600 var(--ak-fs-small)/1.3 var(--ak-font); }
527
+ .rg-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--ak-ink-quiet); flex: none; }
528
+ .rg-dot-running { background: #3faa72; }
529
+ .rg-dot-starting { background: #c08a2a; }
530
+ .rg-dot-crashed, .rg-dot-failed { background: #b34a3f; }
531
+ .rg-lan {
532
+ display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap;
533
+ padding: 10px 12px; border: 1px solid var(--ak-line); border-radius: var(--ak-radius);
534
+ }
535
+ .rg-lan-urls { flex: 1 1 16rem; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
536
+ .rg-qr { flex: none; width: 128px; height: 128px; border-radius: 4px; }
537
+
538
+ /* ---- Il campo di prosa (testo formattato) ----------------------------------------------
539
+ Un `contenteditable`, non una textarea: chi usa il pannello vede il grassetto in grassetto e
540
+ i link come link, mai gli asterischi. Vedi form/prose/ProseField.tsx.
541
+
542
+ Il carattere è quello del testo, NON `--ak-font-mono`: `.ak-textarea` lo imposta a spaziatura
543
+ fissa perché lì dentro c'era markdown da leggere carattere per carattere, e qui invece si
544
+ guarda un testo. Va sovrascritto, altrimenti una biografia sembra un file di configurazione.
545
+ ---------------------------------------------------------------------------------------- */
546
+ .ak-prose-field {
547
+ font-family: var(--ak-font); white-space: pre-wrap; overflow-wrap: break-word;
548
+ cursor: text; resize: none; height: auto;
549
+ }
550
+ .ak-prose-field:focus { outline: 0; box-shadow: var(--ak-ring); }
551
+ .ak-prose-field p { margin: 0; }
552
+ .ak-prose-field p + p { margin-top: .85em; }
553
+ .ak-prose-field strong { font-weight: 650; }
554
+ /* I collegamenti si vedono che lo sono: è metà del senso di mostrare il testo formattato. */
555
+ .ak-prose-field a { color: var(--ak-accent); text-decoration: underline; text-underline-offset: 2px; }
556
+ /* Il segnaposto: un campo vuoto contiene comunque un `<p><br></p>`, quindi `:empty` non
557
+ scatterebbe mai — lo stato «vuoto» lo dichiara il componente, che conosce il valore. */
558
+ .ak-prose-field.is-empty::before {
559
+ content: attr(data-placeholder); display: block; white-space: pre-wrap;
560
+ color: color-mix(in srgb, var(--ak-ink-quiet) 75%, transparent); pointer-events: none;
561
+ }
562
+
563
+ /* ---- La barra di formattazione sopra la selezione -------------------------------------
564
+ Appartiene alla PAGINA e non al campo: è disegnata in un portal su `document.body` e
565
+ posizionata con `position: fixed` da `ProseArea`, che sa dov'è la selezione. Dentro il campo
566
+ sarebbe prigioniera del suo `overflow`. Vedi form/prose/ProseBar.tsx.
567
+
568
+ `--ak-prose-bar-h` è dichiarata qui e LETTA DAL CODICE (la costante `BAR_H`): serve a
569
+ decidere se la barra ci sta sopra la selezione prima di disegnarla, e misurarla davvero
570
+ vorrebbe dire un salto visibile a ogni selezione. Se cambia l'altezza, cambia là. --------- */
571
+ .ak-prose-bar {
572
+ --ak-prose-bar-h: 40px;
573
+ position: fixed; z-index: 60; height: var(--ak-prose-bar-h);
574
+ display: flex; align-items: center; gap: 2px; padding: 0 6px;
575
+ border-radius: 10px; background: var(--ak-panel); box-shadow: var(--ak-lift-3);
576
+ /* Il testo sotto si continua a selezionare col mouse: la barra non intercetta i movimenti
577
+ del puntatore fuori dai suoi bottoni. */
578
+ pointer-events: auto;
579
+ }
580
+ .ak-prose-btn {
581
+ display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px;
582
+ border: 0; border-radius: 7px; background: transparent; color: var(--ak-ink);
583
+ cursor: pointer; font: 500 13px/1 var(--ak-font);
584
+ }
585
+ .ak-prose-btn:hover { background: var(--ak-inset); }
586
+ .ak-prose-btn:focus-visible { outline: 0; box-shadow: var(--ak-ring); }
587
+ .ak-prose-btn.is-bold { font-weight: 700; }
588
+ .ak-prose-btn.is-italic { font-style: italic; font-family: Georgia, serif; }
589
+ /* Il link acceso quando la selezione è già dentro un collegamento: è la sola cosa che
590
+ distingue «aggiungi» da «modifica» prima di aprire il dialogo. */
591
+ .ak-prose-btn.is-on { color: var(--ak-accent); }
592
+ .ak-prose-sep { width: 1px; height: 18px; margin: 0 4px; background: var(--ak-line); }
593
+
594
+ /* Il dialogo del collegamento. Non riusa `.pe-modal` del pannello: quello vive in app.css, che
595
+ adminkit non conosce — il motore non può dipendere dal foglio di chi lo monta. */
596
+ .ak-prose-modal {
597
+ position: fixed; inset: 0; z-index: 70; display: grid; place-items: center;
598
+ background: rgba(0, 0, 0, .35); padding: 20px;
599
+ }
600
+ .ak-prose-card {
601
+ width: min(460px, 100%); border-radius: 14px; background: var(--ak-panel);
602
+ box-shadow: var(--ak-lift-3); overflow: hidden;
603
+ }
604
+ .ak-prose-card header {
605
+ display: flex; align-items: center; justify-content: space-between;
606
+ padding: 16px 18px 12px;
607
+ }
608
+ .ak-prose-card h2 { margin: 0; font: 600 15px/1.2 var(--ak-font); }
609
+ .ak-prose-card header button {
610
+ border: 0; background: transparent; color: var(--ak-ink-quiet); cursor: pointer;
611
+ font-size: 20px; line-height: 1; padding: 0 4px;
612
+ }
613
+ /* Le tre schede in una vaschetta incassata: è una scelta fra tre, non tre bottoni. Stessa
614
+ grammatica di `.seg` nel pannello. */
615
+ .ak-prose-tabs {
616
+ display: flex; gap: 2px; margin: 0 18px; padding: 2px;
617
+ border-radius: 9px; background: var(--ak-inset-deep, var(--ak-inset));
618
+ }
619
+ .ak-prose-tab {
620
+ flex: 1; height: 30px; border: 0; border-radius: 7px; background: transparent;
621
+ color: var(--ak-ink-quiet); cursor: pointer; font: 500 12px/1 var(--ak-font);
622
+ }
623
+ .ak-prose-tab:hover { color: var(--ak-ink); }
624
+ .ak-prose-tab.is-on { background: var(--ak-panel); color: var(--ak-ink); box-shadow: var(--ak-lift-1); }
625
+ .ak-prose-body { padding: 16px 18px 4px; }
626
+ .ak-prose-body .ak-input { width: 100%; }
627
+ .ak-prose-card footer {
628
+ display: flex; align-items: center; justify-content: space-between; gap: 12px;
629
+ padding: 16px 18px 18px;
630
+ }
631
+ .ak-prose-card footer > div { display: flex; gap: 8px; margin-left: auto; }
632
+ .ak-prose-unlink {
633
+ border: 0; background: transparent; color: var(--ak-danger); cursor: pointer;
634
+ font: 500 12px/1 var(--ak-font); padding: 6px 2px;
635
+ }
636
+ .ak-prose-unlink:hover { text-decoration: underline; }
package/dist/yaml.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export declare class YamlEmitError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ /** Stringa YAML fra doppi apici, sicura su una riga sola. */
5
+ export declare function yamlStr(value: unknown): string;
6
+ /** Scalare non quotato dove è sicuro (numeri, booleani), quotato altrimenti. */
7
+ export declare function yamlScalar(value: unknown): string;
8
+ export declare function toYamlLines(value: unknown, indent?: number, maxDepth?: number): string[];