@zuilib/text-editor 0.11.1 → 0.12.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 (49) hide show
  1. package/CHANGELOG.md +174 -18
  2. package/DRAWING_FORMAT.md +23 -21
  3. package/README.md +485 -67
  4. package/dist/chunk-32RX4GZF.js +2 -0
  5. package/dist/chunk-3TZVOXWX.js +16 -0
  6. package/dist/chunk-5VRVC56Y.js +2 -0
  7. package/dist/chunk-7Z3CW6Q3.js +2 -0
  8. package/dist/chunk-A7MCBSAZ.js +2 -0
  9. package/dist/chunk-AGTPMKLK.js +2 -0
  10. package/dist/chunk-C7VEINKX.js +2 -0
  11. package/dist/chunk-LDVPFZCN.js +2 -0
  12. package/dist/chunk-N5JWZ2VW.js +9 -0
  13. package/dist/chunk-QKHMJQO3.js +2 -0
  14. package/dist/chunk-R5PL4Q7X.js +5 -0
  15. package/dist/chunk-SDICQI2B.js +2 -0
  16. package/dist/chunk-TGVEPLDM.js +1 -0
  17. package/dist/chunk-XP3MZRCW.js +2 -0
  18. package/dist/chunk-XRJNLGI4.js +2 -0
  19. package/dist/chunk-YFFSTMIR.js +2 -0
  20. package/dist/chunk-ZXZMYJ7F.js +2 -0
  21. package/dist/comment-markers-BrrrAdSL.d.ts +27 -0
  22. package/dist/comments.d.ts +68 -0
  23. package/dist/comments.js +2 -0
  24. package/dist/drawing-data-EWzEQ1_i.d.ts +111 -0
  25. package/dist/drawing-preset-Ag7a7F9h.d.ts +69 -0
  26. package/dist/drawing.d.ts +7 -0
  27. package/dist/drawing.js +2 -0
  28. package/dist/image-node-kR1Zf5kz.d.ts +70 -0
  29. package/dist/images.d.ts +66 -0
  30. package/dist/images.js +2 -0
  31. package/dist/index-A4S7bRwQ.d.ts +425 -0
  32. package/dist/index.d.ts +36 -1394
  33. package/dist/index.js +1 -7449
  34. package/dist/lexical.d.ts +117 -0
  35. package/dist/lexical.js +2 -0
  36. package/dist/markdown.d.ts +29 -0
  37. package/dist/markdown.js +2 -0
  38. package/dist/mention-node-eSsjUpaE.d.ts +44 -0
  39. package/dist/mentions-plugin-gio3_XKd.d.ts +74 -0
  40. package/dist/mentions.d.ts +5 -0
  41. package/dist/mentions.js +2 -0
  42. package/dist/mermaid-BwGHjwpk.d.ts +553 -0
  43. package/dist/paste.d.ts +45 -0
  44. package/dist/paste.js +2 -0
  45. package/dist/presets-BtNPH0pa.d.ts +78 -0
  46. package/dist/styles.css +402 -95
  47. package/dist/table-grid-D70tNhp2.d.ts +459 -0
  48. package/package.json +52 -7
  49. package/dist/index.css +0 -79
package/dist/styles.css CHANGED
@@ -43,12 +43,32 @@
43
43
  outline: none;
44
44
  }
45
45
 
46
+ /* Keyboard focus: an inset ring so it is visible without changing layout */
47
+ .zui-text-editor-content:focus-visible,
48
+ .zui-text-editor-textarea:focus-visible {
49
+ outline: none;
50
+ box-shadow: inset 0 0 0 2px var(--ring, var(--primary, currentColor));
51
+ }
52
+
53
+ .zui-text-editor-content[aria-invalid='true'],
54
+ .zui-text-editor-textarea[aria-invalid='true'] {
55
+ box-shadow: inset 0 0 0 1px var(--danger, currentColor);
56
+ }
57
+
58
+ /* Invalid and focused: the focus ring stays visible, the invalid line sits inside it */
59
+ .zui-text-editor-content[aria-invalid='true']:focus-visible,
60
+ .zui-text-editor-textarea[aria-invalid='true']:focus-visible {
61
+ box-shadow:
62
+ inset 0 0 0 2px var(--ring, var(--primary, currentColor)),
63
+ inset 0 0 0 3px var(--danger, currentColor);
64
+ }
65
+
46
66
  /*
47
67
  * Block width. Every top-level block sits in the text column and may bleed
48
68
  * past it by `--zui-text-editor-block-bleed` (0 unless set). Tables and
49
69
  * drawings default it to the pane's bleed (`full`); their `text` and
50
70
  * `content` settings pin it to 0 — as an inline style on the table (see
51
- * tableSettings.ts) and a class on the drawing canvas. Zero-specificity
71
+ * table-settings.ts) and a class on the drawing canvas. Zero-specificity
52
72
  * selectors, so one class rule overrides any block kind, e.g.
53
73
  * `.zui-code { --zui-text-editor-block-bleed: var(--zui-text-editor-bleed) }`.
54
74
  */
@@ -95,7 +115,7 @@
95
115
  height: 1.5rem;
96
116
  margin-bottom: 0.25rem;
97
117
  border: none;
98
- border-radius: 0.375rem;
118
+ border-radius: var(--radius-sm, 0.375rem);
99
119
  background: transparent;
100
120
  color: inherit;
101
121
  opacity: 0.55;
@@ -111,7 +131,7 @@
111
131
  display: flex;
112
132
  flex-direction: column;
113
133
  gap: 1px;
114
- border-left: 1px solid color-mix(in srgb, currentColor 12%, transparent);
134
+ border-inline-start: 1px solid color-mix(in srgb, currentColor 12%, transparent);
115
135
  }
116
136
 
117
137
  .zui-text-editor-outline-item {
@@ -124,7 +144,7 @@
124
144
  opacity: 0.6;
125
145
  font-size: 0.8125rem;
126
146
  line-height: 1.35;
127
- text-align: left;
147
+ text-align: start;
128
148
  cursor: pointer;
129
149
  overflow: hidden;
130
150
  text-overflow: ellipsis;
@@ -138,7 +158,7 @@
138
158
  .zui-text-editor-outline-item.is-active {
139
159
  opacity: 1;
140
160
  font-weight: 600;
141
- box-shadow: inset 2px 0 0 #6965db;
161
+ box-shadow: inset 2px 0 0 var(--primary, currentColor);
142
162
  }
143
163
 
144
164
  .zui-text-editor-outline-empty {
@@ -156,15 +176,15 @@
156
176
 
157
177
  .zui-text-editor-fold {
158
178
  position: absolute;
159
- /* Just left of the text column; 0.45rem with the default gutter */
160
- left: calc(var(--zui-text-editor-gutter, 2rem) - 1.55rem);
179
+ /* Just before the text column; 0.45rem with the default gutter */
180
+ inset-inline-start: calc(var(--zui-text-editor-gutter, 2rem) - 1.55rem);
161
181
  display: inline-flex;
162
182
  align-items: center;
163
183
  justify-content: center;
164
184
  width: 1.125rem;
165
185
  height: 1.125rem;
166
186
  border: none;
167
- border-radius: 0.25rem;
187
+ border-radius: var(--radius-sm, 0.25rem);
168
188
  background: transparent;
169
189
  color: inherit;
170
190
  opacity: 0;
@@ -197,7 +217,7 @@
197
217
  .zui-text-editor-placeholder {
198
218
  position: absolute;
199
219
  top: 1.5rem;
200
- left: var(--zui-text-editor-gutter, 2rem);
220
+ inset-inline-start: var(--zui-text-editor-gutter, 2rem);
201
221
  pointer-events: none;
202
222
  user-select: none;
203
223
  }
@@ -205,14 +225,14 @@
205
225
  /* Checklist styles */
206
226
  .zui-checklist {
207
227
  list-style: none;
208
- margin-left: 0;
209
- padding-left: 0;
228
+ margin-inline-start: 0;
229
+ padding-inline-start: 0;
210
230
  }
211
231
 
212
232
  .zui-checklist li[role='checkbox'] {
213
233
  position: relative;
214
- padding-left: 1.75rem;
215
- margin-left: 0;
234
+ padding-inline-start: 1.75rem;
235
+ margin-inline-start: 0;
216
236
  cursor: pointer;
217
237
  list-style: none;
218
238
  }
@@ -220,12 +240,12 @@
220
240
  .zui-checklist li[role='checkbox']::before {
221
241
  content: '';
222
242
  position: absolute;
223
- left: 0;
243
+ inset-inline-start: 0;
224
244
  top: 0.25em;
225
245
  width: 1rem;
226
246
  height: 1rem;
227
247
  border: 2px solid currentColor;
228
- border-radius: 0.2rem;
248
+ border-radius: var(--radius-sm, 0.2rem);
229
249
  opacity: 0.5;
230
250
  background: transparent;
231
251
  }
@@ -238,7 +258,7 @@
238
258
  .zui-checklist li[role='checkbox'][aria-checked='true']::after {
239
259
  content: '';
240
260
  position: absolute;
241
- left: 0.3rem;
261
+ inset-inline-start: 0.3rem;
242
262
  top: 0.35em;
243
263
  width: 0.4rem;
244
264
  height: 0.7rem;
@@ -254,8 +274,8 @@
254
274
  margin-block: 0 1rem;
255
275
  padding: 0.75rem 1rem;
256
276
  border: 1px solid currentColor;
257
- border-radius: 0.375rem;
258
- border-left-width: 3px;
277
+ border-radius: var(--radius-md, 0.375rem);
278
+ border-inline-start-width: 3px;
259
279
  font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas,
260
280
  'Liberation Mono', monospace;
261
281
  font-size: 0.8125rem;
@@ -263,6 +283,20 @@
263
283
  opacity: 0.7;
264
284
  }
265
285
 
286
+ /* Polite live region for row / column / fold announcements */
287
+ .zui-text-editor-live {
288
+ position: absolute;
289
+ width: 1px;
290
+ height: 1px;
291
+ margin: -1px;
292
+ padding: 0;
293
+ overflow: hidden;
294
+ clip: rect(0 0 0 0);
295
+ clip-path: inset(50%);
296
+ white-space: nowrap;
297
+ border: 0;
298
+ }
299
+
266
300
  /* Toolbar */
267
301
  .zui-text-editor-toolbar {
268
302
  position: sticky;
@@ -284,7 +318,7 @@
284
318
  width: 1.75rem;
285
319
  height: 1.75rem;
286
320
  border: none;
287
- border-radius: 0.375rem;
321
+ border-radius: var(--radius-sm, 0.375rem);
288
322
  background: transparent;
289
323
  color: inherit;
290
324
  opacity: 0.65;
@@ -326,9 +360,9 @@
326
360
  border-collapse: separate;
327
361
  border-spacing: 0;
328
362
  border: 1px solid color-mix(in srgb, currentColor 15%, transparent);
329
- border-radius: 0.625rem;
363
+ border-radius: var(--radius-lg, 0.625rem);
330
364
  overflow: hidden;
331
- /* Density presets set these custom properties inline (tableSettings.ts) */
365
+ /* Density presets set these custom properties inline (table-settings.ts) */
332
366
  font-size: var(--zui-table-font-size, 0.875rem);
333
367
  table-layout: fixed;
334
368
  }
@@ -336,13 +370,13 @@
336
370
  .zui-table-cell {
337
371
  padding: var(--zui-table-cell-padding, 0.5rem 0.75rem);
338
372
  border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent);
339
- border-right: 1px solid color-mix(in srgb, currentColor 10%, transparent);
373
+ border-inline-end: 1px solid color-mix(in srgb, currentColor 10%, transparent);
340
374
  vertical-align: top;
341
- text-align: left;
375
+ text-align: start;
342
376
  }
343
377
 
344
378
  .zui-table-cell:last-child {
345
- border-right: none;
379
+ border-inline-end: none;
346
380
  }
347
381
 
348
382
  .zui-table tr:last-child > .zui-table-cell {
@@ -362,18 +396,18 @@
362
396
  .zui-table-selected,
363
397
  .zui-table-cell.zui-table-selected {
364
398
  outline: none;
365
- background: color-mix(in srgb, var(--primary, #6965db) 14%, transparent);
399
+ background: color-mix(in srgb, var(--primary, currentColor) 14%, transparent);
366
400
  }
367
401
 
368
402
  /*
369
403
  * Floating table controls (positioned within .zui-text-editor-main, so they
370
- * scroll with the document; TableControlsPlugin.tsx).
404
+ * scroll with the document; table-controls-plugin.tsx).
371
405
  *
372
406
  * The layout presets sit above the table the caret is in, aligned with
373
407
  * its right edge and stacked above the column rail (the plugin sets `top`
374
408
  * to the rail's top; the transform lifts the toolbar clear of it). The
375
409
  * settings themselves are an inline style on the <table> (see
376
- * tableSettings.ts).
410
+ * table-settings.ts).
377
411
  */
378
412
  .zui-table-settings {
379
413
  position: absolute;
@@ -386,7 +420,7 @@
386
420
  border-radius: var(--radius-md, 0.5rem);
387
421
  background: var(--popover, #ffffff);
388
422
  color: var(--popover-foreground, inherit);
389
- box-shadow: var(--shadow-medium, 0 1px 4px rgba(0, 0, 0, 0.08));
423
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
390
424
  transform: translateY(calc(-100% - 0.25rem));
391
425
  }
392
426
 
@@ -402,27 +436,28 @@
402
436
  .zui-table-rail {
403
437
  position: absolute;
404
438
  z-index: 4;
405
- --zui-table-rail-accent: var(--primary, #6965db);
439
+ --zui-table-rail-accent: var(--primary, currentColor);
406
440
  --zui-table-rail-accent-foreground: var(--primary-foreground, #ffffff);
407
- --zui-table-rail-danger: var(--destructive, #dc2626);
408
- --zui-table-rail-danger-foreground: var(--destructive-foreground, #ffffff);
441
+ --zui-table-rail-danger: var(--danger, #dc2626);
442
+ --zui-table-rail-danger-foreground: var(--danger-foreground, #ffffff);
409
443
  }
410
444
 
411
445
  .zui-table-rail-marker {
412
446
  position: absolute;
413
- border-radius: 999px;
447
+ border-radius: var(--radius-full, 999px);
414
448
  background: var(--zui-table-rail-accent);
415
449
  opacity: 0.55;
416
450
  pointer-events: none;
417
451
  transition:
418
452
  top 140ms cubic-bezier(0.2, 0, 0, 1),
419
453
  left 140ms cubic-bezier(0.2, 0, 0, 1),
454
+ inset-inline-start 140ms cubic-bezier(0.2, 0, 0, 1),
420
455
  width 140ms cubic-bezier(0.2, 0, 0, 1),
421
456
  height 140ms cubic-bezier(0.2, 0, 0, 1);
422
457
  }
423
458
 
424
459
  .zui-table-rail.is-row .zui-table-rail-marker {
425
- right: 5px;
460
+ inset-inline-end: 5px;
426
461
  width: 3px;
427
462
  }
428
463
 
@@ -440,14 +475,15 @@
440
475
  height: 20px;
441
476
  padding: 0;
442
477
  border: 1px solid var(--border, color-mix(in srgb, currentColor 14%, transparent));
443
- border-radius: 999px;
478
+ border-radius: var(--radius-full, 999px);
444
479
  background: var(--popover, #ffffff);
445
480
  color: var(--popover-foreground, inherit);
446
- box-shadow: var(--shadow-medium, 0 1px 4px rgba(0, 0, 0, 0.08));
481
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
447
482
  cursor: pointer;
448
483
  transition:
449
484
  top 140ms cubic-bezier(0.2, 0, 0, 1),
450
485
  left 140ms cubic-bezier(0.2, 0, 0, 1),
486
+ inset-inline-start 140ms cubic-bezier(0.2, 0, 0, 1),
451
487
  opacity 140ms ease,
452
488
  background-color 120ms ease,
453
489
  color 120ms ease,
@@ -456,7 +492,7 @@
456
492
  }
457
493
 
458
494
  .zui-table-rail.is-row .zui-table-rail-handle {
459
- left: 2px;
495
+ inset-inline-start: 2px;
460
496
  transform: translateY(-50%);
461
497
  }
462
498
 
@@ -498,6 +534,7 @@
498
534
  transition:
499
535
  top 140ms cubic-bezier(0.2, 0, 0, 1),
500
536
  left 140ms cubic-bezier(0.2, 0, 0, 1),
537
+ inset-inline-start 140ms cubic-bezier(0.2, 0, 0, 1),
501
538
  opacity 120ms ease;
502
539
  }
503
540
 
@@ -559,14 +596,14 @@
559
596
  --zui-drawing-chrome: var(--background, #ffffff);
560
597
  --zui-drawing-popover: var(--popover, #ffffff);
561
598
  --zui-drawing-popover-foreground: var(--popover-foreground, var(--zui-drawing-foreground));
562
- --zui-drawing-danger: var(--destructive, #dc2626);
599
+ --zui-drawing-danger: var(--danger, #dc2626);
563
600
  --zui-drawing-success: var(--success, #15803d);
564
601
  --zui-drawing-radius: var(--radius-lg, 0.75rem);
565
602
  --zui-drawing-control-radius: var(--radius-sm, 0.375rem);
566
- --zui-drawing-shadow: var(--shadow-medium, 0 4px 6px -1px rgba(0, 0, 0, 0.1));
603
+ --zui-drawing-shadow: var(--shadow-2, 0 4px 6px -1px rgba(0, 0, 0, 0.1));
567
604
  --zui-drawing-font: ui-sans-serif, system-ui, sans-serif;
568
- --zui-drawing-surface: #ffffff;
569
- --zui-drawing-grid: rgba(0, 0, 0, 0.09);
605
+ --zui-drawing-surface: var(--zui-drawing-surface-color, #ffffff);
606
+ --zui-drawing-grid: var(--zui-drawing-grid-color, rgba(0, 0, 0, 0.09));
570
607
  --zui-drawing-grid-size: 20px;
571
608
  --zui-drawing-dark-filter: invert(93%) hue-rotate(180deg);
572
609
  --zui-drawing-hover: color-mix(in srgb, currentColor 8%, transparent);
@@ -683,7 +720,7 @@
683
720
  }
684
721
 
685
722
  .zui-drawing-toolbar-group-end {
686
- margin-left: auto;
723
+ margin-inline-start: auto;
687
724
  }
688
725
 
689
726
  .zui-drawing-toolbar-divider {
@@ -741,11 +778,11 @@
741
778
 
742
779
  .zui-drawing-caret {
743
780
  position: absolute;
744
- right: 0.25rem;
781
+ inset-inline-end: 0.25rem;
745
782
  bottom: 0.25rem;
746
783
  width: 0;
747
784
  height: 0;
748
- border-left: 4px solid transparent;
785
+ border-inline-start: 4px solid transparent;
749
786
  border-top: 4px solid currentColor;
750
787
  opacity: 0.7;
751
788
  }
@@ -757,7 +794,7 @@
757
794
  .zui-drawing-popover {
758
795
  position: absolute;
759
796
  top: calc(100% + 0.25rem);
760
- left: 0;
797
+ inset-inline-start: 0;
761
798
  z-index: 7;
762
799
  display: flex;
763
800
  flex-direction: column;
@@ -782,7 +819,7 @@
782
819
  background: transparent;
783
820
  color: inherit;
784
821
  font-size: 0.8125rem;
785
- text-align: left;
822
+ text-align: start;
786
823
  cursor: pointer;
787
824
  transition: background-color 0.12s ease;
788
825
  }
@@ -815,8 +852,8 @@
815
852
  display: flex;
816
853
  align-items: center;
817
854
  gap: 0.25rem;
818
- padding-left: 0.5rem;
819
- border-left: 1px solid color-mix(in srgb, currentColor 15%, transparent);
855
+ padding-inline-start: 0.5rem;
856
+ border-inline-start: 1px solid color-mix(in srgb, currentColor 15%, transparent);
820
857
  }
821
858
 
822
859
  .zui-drawing-props-group {
@@ -826,12 +863,12 @@
826
863
  }
827
864
 
828
865
  .zui-drawing-props-group + .zui-drawing-props-group {
829
- padding-left: 0.5rem;
830
- border-left: 1px solid color-mix(in srgb, currentColor 15%, transparent);
866
+ padding-inline-start: 0.5rem;
867
+ border-inline-start: 1px solid color-mix(in srgb, currentColor 15%, transparent);
831
868
  }
832
869
 
833
870
  .zui-drawing-swatch-label {
834
- margin: 0 0.25rem 0 0.125rem;
871
+ margin-inline: 0.125rem 0.25rem;
835
872
  font-size: 0.6875rem;
836
873
  letter-spacing: 0.02em;
837
874
  color: var(--zui-drawing-muted-foreground);
@@ -844,7 +881,7 @@
844
881
  margin: 0 0.125rem;
845
882
  padding: 0;
846
883
  border: 2px solid rgba(0, 0, 0, 0.18);
847
- border-radius: 999px;
884
+ border-radius: var(--radius-full, 999px);
848
885
  cursor: pointer;
849
886
  transition:
850
887
  transform 0.12s ease,
@@ -877,10 +914,7 @@
877
914
  /* Inline text editing */
878
915
  .zui-drawing-overlay {
879
916
  position: absolute;
880
- top: 0;
881
- left: 0;
882
- right: 0;
883
- bottom: 0;
917
+ inset: 0;
884
918
  transform-origin: top left;
885
919
  pointer-events: none;
886
920
  }
@@ -891,7 +925,7 @@
891
925
  margin: 0;
892
926
  padding: 0;
893
927
  border: 0;
894
- border-radius: 3px;
928
+ border-radius: var(--radius-sm, 3px);
895
929
  outline: 1.5px dashed var(--zui-drawing-accent);
896
930
  outline-offset: 3px;
897
931
  background: color-mix(in srgb, var(--zui-drawing-surface) 85%, transparent);
@@ -919,7 +953,7 @@
919
953
  width: 1.375rem;
920
954
  height: 1.375rem;
921
955
  border-radius: var(--zui-drawing-control-radius);
922
- color: #1e1e1e;
956
+ color: var(--zui-drawing-foreground);
923
957
  opacity: 0.35;
924
958
  cursor: ns-resize;
925
959
  touch-action: none;
@@ -945,6 +979,62 @@
945
979
  filter: var(--zui-drawing-dark-filter);
946
980
  }
947
981
 
982
+ /* Link bubble (link-bubble-plugin.tsx): under the link the caret is in */
983
+ .zui-text-editor-link-bubble {
984
+ position: absolute;
985
+ z-index: 6;
986
+ display: flex;
987
+ align-items: center;
988
+ gap: 0.25rem;
989
+ padding: 0.25rem;
990
+ border: 1px solid var(--border, color-mix(in srgb, currentColor 12%, transparent));
991
+ border-radius: var(--radius-md, 0.5rem);
992
+ background: var(--popover, #ffffff);
993
+ color: var(--popover-foreground, inherit);
994
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
995
+ }
996
+
997
+ .zui-text-editor-link-input {
998
+ min-width: 14rem;
999
+ padding: 0.25rem 0.5rem;
1000
+ border: 1px solid var(--border, color-mix(in srgb, currentColor 15%, transparent));
1001
+ border-radius: var(--radius-sm, 0.375rem);
1002
+ background: var(--background, transparent);
1003
+ color: inherit;
1004
+ font: inherit;
1005
+ font-size: 0.8125rem;
1006
+ }
1007
+
1008
+ .zui-text-editor-link-input:focus-visible {
1009
+ outline: none;
1010
+ box-shadow: 0 0 0 2px var(--ring, var(--primary, currentColor));
1011
+ }
1012
+
1013
+ .zui-text-editor-link-input[aria-invalid='true'] {
1014
+ border-color: var(--danger, currentColor);
1015
+ }
1016
+
1017
+ .zui-text-editor-link-bubble .zui-text-editor-toolbar-button {
1018
+ width: auto;
1019
+ padding-inline: 0.5rem;
1020
+ font-size: 0.8125rem;
1021
+ }
1022
+
1023
+ /* Character counter (character-count.tsx); its limit announcements use .zui-text-editor-live */
1024
+ .zui-text-editor-count {
1025
+ position: sticky;
1026
+ bottom: 0;
1027
+ padding: 0.25rem var(--zui-text-editor-gutter, 2rem);
1028
+ text-align: end;
1029
+ font-size: 0.75rem;
1030
+ opacity: 0.6;
1031
+ }
1032
+
1033
+ .zui-text-editor-count.is-over {
1034
+ opacity: 1;
1035
+ color: var(--danger, currentColor);
1036
+ }
1037
+
948
1038
  .zui-text-editor-textarea {
949
1039
  width: 100%;
950
1040
  height: 100%;
@@ -963,55 +1053,57 @@
963
1053
 
964
1054
  /*
965
1055
  * Code block syntax highlighting. Each semantic token type (see
966
- * src/code/types.ts) gets one class and one color custom property, so a host
1056
+ * src/code/grammar.ts) gets one class and one color custom property, so a host
967
1057
  * theme restyles the palette by overriding `--zui-code-<type>` on `.zui-code`
968
1058
  * (or any ancestor) — light and dark defaults follow the `.dark` convention.
969
1059
  * Operators and punctuation inherit the text color on purpose: fewer colors,
970
- * calmer blocks.
1060
+ * calmer blocks. The defaults read `--zui-code-<type>-light` /
1061
+ * `--zui-code-<type>-dark` (defined by @zuilib/tokens) before their
1062
+ * built-in fallbacks.
971
1063
  */
972
1064
  .zui-code {
973
- --zui-code-comment: #6e7781;
974
- --zui-code-string: #0a3069;
975
- --zui-code-number: #0550ae;
976
- --zui-code-keyword: #cf222e;
977
- --zui-code-builtin: #6639ba;
978
- --zui-code-type: #953800;
979
- --zui-code-function: #8250df;
980
- --zui-code-property: #0550ae;
981
- --zui-code-variable: #953800;
982
- --zui-code-constant: #0550ae;
1065
+ --zui-code-comment: var(--zui-code-comment-light, #6e7781);
1066
+ --zui-code-string: var(--zui-code-string-light, #0a3069);
1067
+ --zui-code-number: var(--zui-code-number-light, #0550ae);
1068
+ --zui-code-keyword: var(--zui-code-keyword-light, #cf222e);
1069
+ --zui-code-builtin: var(--zui-code-builtin-light, #6639ba);
1070
+ --zui-code-type: var(--zui-code-type-light, #953800);
1071
+ --zui-code-function: var(--zui-code-function-light, #8250df);
1072
+ --zui-code-property: var(--zui-code-property-light, #0550ae);
1073
+ --zui-code-variable: var(--zui-code-variable-light, #953800);
1074
+ --zui-code-constant: var(--zui-code-constant-light, #0550ae);
983
1075
  --zui-code-operator: currentColor;
984
1076
  --zui-code-punctuation: currentColor;
985
- --zui-code-tag: #116329;
986
- --zui-code-attr: #0550ae;
987
- --zui-code-regex: #0a3069;
988
- --zui-code-meta: #6639ba;
989
- --zui-code-inserted: #116329;
990
- --zui-code-inserted-background: #dafbe1;
991
- --zui-code-deleted: #82071e;
992
- --zui-code-deleted-background: #ffebe9;
1077
+ --zui-code-tag: var(--zui-code-tag-light, #116329);
1078
+ --zui-code-attr: var(--zui-code-attr-light, #0550ae);
1079
+ --zui-code-regex: var(--zui-code-regex-light, #0a3069);
1080
+ --zui-code-meta: var(--zui-code-meta-light, #6639ba);
1081
+ --zui-code-inserted: var(--zui-code-inserted-light, #116329);
1082
+ --zui-code-inserted-background: var(--zui-code-inserted-background-light, #dafbe1);
1083
+ --zui-code-deleted: var(--zui-code-deleted-light, #82071e);
1084
+ --zui-code-deleted-background: var(--zui-code-deleted-background-light, #ffebe9);
993
1085
  tab-size: 2;
994
1086
  }
995
1087
 
996
1088
  .dark .zui-code {
997
- --zui-code-comment: #8b949e;
998
- --zui-code-string: #a5d6ff;
999
- --zui-code-number: #79c0ff;
1000
- --zui-code-keyword: #ff7b72;
1001
- --zui-code-builtin: #d2a8ff;
1002
- --zui-code-type: #ffa657;
1003
- --zui-code-function: #d2a8ff;
1004
- --zui-code-property: #79c0ff;
1005
- --zui-code-variable: #ffa657;
1006
- --zui-code-constant: #79c0ff;
1007
- --zui-code-tag: #7ee787;
1008
- --zui-code-attr: #79c0ff;
1009
- --zui-code-regex: #a5d6ff;
1010
- --zui-code-meta: #d2a8ff;
1011
- --zui-code-inserted: #7ee787;
1012
- --zui-code-inserted-background: rgba(63, 185, 80, 0.15);
1013
- --zui-code-deleted: #ffa198;
1014
- --zui-code-deleted-background: rgba(248, 81, 73, 0.15);
1089
+ --zui-code-comment: var(--zui-code-comment-dark, #8b949e);
1090
+ --zui-code-string: var(--zui-code-string-dark, #a5d6ff);
1091
+ --zui-code-number: var(--zui-code-number-dark, #79c0ff);
1092
+ --zui-code-keyword: var(--zui-code-keyword-dark, #ff7b72);
1093
+ --zui-code-builtin: var(--zui-code-builtin-dark, #d2a8ff);
1094
+ --zui-code-type: var(--zui-code-type-dark, #ffa657);
1095
+ --zui-code-function: var(--zui-code-function-dark, #d2a8ff);
1096
+ --zui-code-property: var(--zui-code-property-dark, #79c0ff);
1097
+ --zui-code-variable: var(--zui-code-variable-dark, #ffa657);
1098
+ --zui-code-constant: var(--zui-code-constant-dark, #79c0ff);
1099
+ --zui-code-tag: var(--zui-code-tag-dark, #7ee787);
1100
+ --zui-code-attr: var(--zui-code-attr-dark, #79c0ff);
1101
+ --zui-code-regex: var(--zui-code-regex-dark, #a5d6ff);
1102
+ --zui-code-meta: var(--zui-code-meta-dark, #d2a8ff);
1103
+ --zui-code-inserted: var(--zui-code-inserted-dark, #7ee787);
1104
+ --zui-code-inserted-background: var(--zui-code-inserted-background-dark, rgba(63, 185, 80, 0.15));
1105
+ --zui-code-deleted: var(--zui-code-deleted-dark, #ffa198);
1106
+ --zui-code-deleted-background: var(--zui-code-deleted-background-dark, rgba(248, 81, 73, 0.15));
1015
1107
  }
1016
1108
 
1017
1109
  .zui-token-comment { color: var(--zui-code-comment); font-style: italic; }
@@ -1049,3 +1141,218 @@
1049
1141
  font-variation-settings: 'CASL' 1, 'CRSV' 1, 'MONO' 0, 'wght' 440, 'slnt' -3;
1050
1142
  letter-spacing: 0.01em;
1051
1143
  }
1144
+
1145
+ /*
1146
+ * First-party plugins (mentions, comments, images). Appended to styles.css
1147
+ * at build time; every color comes from the host theme tokens.
1148
+ */
1149
+
1150
+ /* Mentions (MentionNode + MentionsPlugin) */
1151
+ .zui-mention {
1152
+ display: inline;
1153
+ padding-inline: 0.2em;
1154
+ border-radius: var(--radius-sm, 0.375rem);
1155
+ background: color-mix(in srgb, var(--primary, currentColor) 12%, transparent);
1156
+ color: var(--primary, inherit);
1157
+ font-weight: 500;
1158
+ white-space: nowrap;
1159
+ }
1160
+
1161
+ .zui-text-editor-mentions {
1162
+ position: absolute;
1163
+ z-index: 6;
1164
+ min-width: 12rem;
1165
+ max-width: 20rem;
1166
+ padding: 0.25rem;
1167
+ border: 1px solid var(--border, color-mix(in srgb, currentColor 12%, transparent));
1168
+ border-radius: var(--radius-md, 0.5rem);
1169
+ background: var(--popover, var(--background, #ffffff));
1170
+ color: var(--popover-foreground, inherit);
1171
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
1172
+ font-size: 0.875rem;
1173
+ }
1174
+
1175
+ .zui-text-editor-mention-option {
1176
+ display: flex;
1177
+ align-items: baseline;
1178
+ gap: 0.5rem;
1179
+ padding: 0.375rem 0.5rem;
1180
+ border-radius: var(--radius-sm, 0.375rem);
1181
+ cursor: pointer;
1182
+ }
1183
+
1184
+ .zui-text-editor-mention-option.is-active {
1185
+ background: var(--accent, color-mix(in srgb, currentColor 8%, transparent));
1186
+ color: var(--accent-foreground, inherit);
1187
+ }
1188
+
1189
+ .zui-text-editor-mention-hint,
1190
+ .zui-text-editor-mention-empty {
1191
+ color: var(--muted-foreground, inherit);
1192
+ opacity: 0.8;
1193
+ font-size: 0.8125rem;
1194
+ }
1195
+
1196
+ .zui-text-editor-mention-empty {
1197
+ padding: 0.375rem 0.5rem;
1198
+ }
1199
+
1200
+ /* Comments (MarkNode + CommentsPlugin) */
1201
+ .zui-comment {
1202
+ background: color-mix(in srgb, var(--zui-comment-color, var(--warning, #eab308)) 28%, transparent);
1203
+ border-block-end: 2px solid color-mix(in srgb, var(--zui-comment-color, var(--warning, #eab308)) 70%, transparent);
1204
+ transition: background-color 120ms ease;
1205
+ }
1206
+
1207
+ .zui-comment.is-resolved {
1208
+ background: transparent;
1209
+ border-block-end-color: color-mix(in srgb, currentColor 20%, transparent);
1210
+ }
1211
+
1212
+ .zui-comment-overlap {
1213
+ background: color-mix(in srgb, var(--zui-comment-color, var(--warning, #eab308)) 45%, transparent);
1214
+ }
1215
+
1216
+ .zui-text-editor-comment-add {
1217
+ position: absolute;
1218
+ z-index: 6;
1219
+ }
1220
+
1221
+ .zui-text-editor-comment-add .zui-text-editor-toolbar-button {
1222
+ width: auto;
1223
+ gap: 0.375rem;
1224
+ padding-inline: 0.5rem;
1225
+ border: 1px solid var(--border, color-mix(in srgb, currentColor 12%, transparent));
1226
+ border-radius: var(--radius-md, 0.5rem);
1227
+ background: var(--popover, var(--background, #ffffff));
1228
+ color: var(--popover-foreground, inherit);
1229
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
1230
+ font-size: 0.8125rem;
1231
+ }
1232
+
1233
+ .zui-text-editor-comments {
1234
+ position: absolute;
1235
+ z-index: 6;
1236
+ display: flex;
1237
+ flex-direction: column;
1238
+ gap: 0.375rem;
1239
+ min-width: 14rem;
1240
+ max-width: 22rem;
1241
+ padding: 0.5rem;
1242
+ border: 1px solid var(--border, color-mix(in srgb, currentColor 12%, transparent));
1243
+ border-radius: var(--radius-md, 0.5rem);
1244
+ background: var(--popover, var(--background, #ffffff));
1245
+ color: var(--popover-foreground, inherit);
1246
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
1247
+ font-size: 0.8125rem;
1248
+ }
1249
+
1250
+ .zui-text-editor-comment-quote {
1251
+ overflow: hidden;
1252
+ text-overflow: ellipsis;
1253
+ white-space: nowrap;
1254
+ font-style: italic;
1255
+ color: var(--muted-foreground, inherit);
1256
+ }
1257
+
1258
+ .zui-text-editor-comment.is-resolved .zui-text-editor-comment-quote {
1259
+ text-decoration: line-through;
1260
+ }
1261
+
1262
+ .zui-text-editor-comment-state {
1263
+ font-style: normal;
1264
+ font-weight: 500;
1265
+ }
1266
+
1267
+ .zui-text-editor-comment-actions {
1268
+ display: flex;
1269
+ gap: 0.25rem;
1270
+ margin-block-start: 0.25rem;
1271
+ }
1272
+
1273
+ .zui-text-editor-comment-actions .zui-text-editor-toolbar-button {
1274
+ width: auto;
1275
+ padding-inline: 0.5rem;
1276
+ font-size: 0.8125rem;
1277
+ }
1278
+
1279
+ /* Images (ImageNode + ImagesPlugin) */
1280
+ .zui-image {
1281
+ display: inline-block;
1282
+ max-width: 100%;
1283
+ vertical-align: middle;
1284
+ }
1285
+
1286
+ .zui-paragraph > .zui-image:only-child {
1287
+ display: block;
1288
+ margin-block: 0.5rem;
1289
+ }
1290
+
1291
+ .zui-image-frame {
1292
+ position: relative;
1293
+ display: inline-block;
1294
+ max-width: 100%;
1295
+ border-radius: var(--radius-md, 0.5rem);
1296
+ }
1297
+
1298
+ .zui-image-frame img {
1299
+ display: block;
1300
+ max-width: 100%;
1301
+ height: auto;
1302
+ border-radius: inherit;
1303
+ }
1304
+
1305
+ .zui-image-frame.is-selected {
1306
+ box-shadow: 0 0 0 2px var(--ring, var(--primary, currentColor));
1307
+ }
1308
+
1309
+ .zui-image-alt {
1310
+ position: absolute;
1311
+ inset-inline-start: 0;
1312
+ inset-block-start: calc(100% + 0.25rem);
1313
+ z-index: 6;
1314
+ display: flex;
1315
+ padding: 0.25rem;
1316
+ border: 1px solid var(--border, color-mix(in srgb, currentColor 12%, transparent));
1317
+ border-radius: var(--radius-md, 0.5rem);
1318
+ background: var(--popover, var(--background, #ffffff));
1319
+ color: var(--popover-foreground, inherit);
1320
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
1321
+ }
1322
+
1323
+ .zui-image-pending {
1324
+ display: inline-flex;
1325
+ align-items: center;
1326
+ justify-content: center;
1327
+ width: 8rem;
1328
+ height: 5rem;
1329
+ border: 1px dashed var(--border, color-mix(in srgb, currentColor 25%, transparent));
1330
+ border-radius: var(--radius-md, 0.5rem);
1331
+ background: var(--muted, color-mix(in srgb, currentColor 5%, transparent));
1332
+ vertical-align: middle;
1333
+ }
1334
+
1335
+ .zui-image-spinner {
1336
+ width: 1.25rem;
1337
+ height: 1.25rem;
1338
+ border: 2px solid color-mix(in srgb, currentColor 20%, transparent);
1339
+ border-block-start-color: var(--primary, currentColor);
1340
+ border-radius: var(--radius-full, 9999px);
1341
+ animation: zui-image-spin 0.8s linear infinite;
1342
+ }
1343
+
1344
+ @keyframes zui-image-spin {
1345
+ to {
1346
+ transform: rotate(360deg);
1347
+ }
1348
+ }
1349
+
1350
+ @media (prefers-reduced-motion: reduce) {
1351
+ .zui-comment {
1352
+ transition: none;
1353
+ }
1354
+ .zui-image-spinner {
1355
+ animation: none;
1356
+ border-block-start-color: currentColor;
1357
+ }
1358
+ }