stilo-editor 0.1.0 → 0.2.1

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 (57) hide show
  1. package/README.md +76 -129
  2. package/dist/content.css +238 -1
  3. package/dist/core/editor.d.ts +15 -3
  4. package/dist/core/extension-manager.d.ts +3 -3
  5. package/dist/core/extension.d.ts +6 -1
  6. package/dist/core/helpers/block-style.d.ts +25 -0
  7. package/dist/core/helpers/input-rules.d.ts +8 -1
  8. package/dist/core/helpers/lists.d.ts +10 -2
  9. package/dist/core/helpers/marks.d.ts +14 -0
  10. package/dist/core/helpers/nodes.d.ts +12 -0
  11. package/dist/core/helpers/word-count.d.ts +14 -0
  12. package/dist/core/html/css.d.ts +16 -1
  13. package/dist/core/html/paste/base-styles.d.ts +7 -0
  14. package/dist/core/html/paste/google-docs.d.ts +7 -0
  15. package/dist/core/html/paste/index.d.ts +9 -0
  16. package/dist/core/html/paste/table-widths.d.ts +7 -0
  17. package/dist/core/html/paste/word.d.ts +8 -0
  18. package/dist/core/html/serialize.d.ts +2 -2
  19. package/dist/extensions/code-block/index.d.ts +24 -0
  20. package/dist/extensions/default-extensions/index.d.ts +11 -0
  21. package/dist/extensions/heading/index.d.ts +2 -1
  22. package/dist/extensions/image/image-view.d.ts +27 -0
  23. package/dist/extensions/image/index.d.ts +29 -0
  24. package/dist/extensions/index.d.ts +10 -1
  25. package/dist/extensions/line-height/index.d.ts +18 -0
  26. package/dist/extensions/list-item/index.d.ts +1 -1
  27. package/dist/extensions/paragraph/index.d.ts +4 -4
  28. package/dist/extensions/search/index.d.ts +55 -0
  29. package/dist/extensions/table/index.d.ts +70 -0
  30. package/dist/extensions/task-item/index.d.ts +24 -0
  31. package/dist/extensions/task-item/task-item-view.d.ts +24 -0
  32. package/dist/extensions/task-list/index.d.ts +11 -0
  33. package/dist/extensions/text-align/index.d.ts +18 -0
  34. package/dist/extensions/text-case/index.d.ts +14 -0
  35. package/dist/extensions/text-style/font-size.d.ts +21 -0
  36. package/dist/extensions/text-style/index.d.ts +18 -2
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.js +1 -1
  39. package/dist/src-CV6pgfTS.js +2662 -0
  40. package/dist/style.css +599 -1
  41. package/dist/ui/components/dropdown.d.ts +9 -0
  42. package/dist/ui/components/find-bar.d.ts +24 -0
  43. package/dist/ui/components/font-size-input.d.ts +21 -0
  44. package/dist/ui/components/icon-menu.d.ts +20 -0
  45. package/dist/ui/components/status-bar.d.ts +19 -0
  46. package/dist/ui/components/table-actions.d.ts +25 -0
  47. package/dist/ui/components/table-handles.d.ts +16 -0
  48. package/dist/ui/components/table-menu.d.ts +21 -0
  49. package/dist/ui/i18n/types.d.ts +1 -1
  50. package/dist/ui/index.d.ts +6 -2
  51. package/dist/ui/toolbar/fonts.d.ts +8 -0
  52. package/dist/ui/toolbar/items.d.ts +23 -2
  53. package/dist/ui/toolbar/types.d.ts +67 -4
  54. package/dist/ui.css +361 -0
  55. package/dist/ui.js +1361 -303
  56. package/package.json +19 -9
  57. package/dist/src-3tAxKI0c.js +0 -1316
package/dist/style.css CHANGED
@@ -96,6 +96,60 @@ li.ProseMirror-selectednode:after {
96
96
  display: block;
97
97
  }
98
98
 
99
+ /*
100
+ * prosemirror-tables/style/tables.css. Copyright (C) 2015-2016 by Marijn Haverbeke and others,
101
+ * MIT license. Adapted: one `cursor` value, the colors from the Stilo custom properties.
102
+ */
103
+ .ProseMirror .tableWrapper {
104
+ overflow-x: auto;
105
+ }
106
+
107
+ .ProseMirror table {
108
+ border-collapse: collapse;
109
+ table-layout: fixed;
110
+ width: 100%;
111
+ overflow: hidden;
112
+ }
113
+
114
+ .ProseMirror td,
115
+ .ProseMirror th {
116
+ vertical-align: top;
117
+ box-sizing: border-box;
118
+ position: relative;
119
+ }
120
+
121
+ .ProseMirror td:not([data-colwidth]):not(.column-resize-dragging),
122
+ .ProseMirror th:not([data-colwidth]):not(.column-resize-dragging) {
123
+ min-width: var(--default-cell-min-width);
124
+ }
125
+
126
+ .ProseMirror .column-resize-handle {
127
+ position: absolute;
128
+ right: -2px;
129
+ top: 0;
130
+ bottom: 0;
131
+ width: 4px;
132
+ z-index: 20;
133
+ background-color: var(--stilo-color-focus, #0969da);
134
+ pointer-events: none;
135
+ }
136
+
137
+ .ProseMirror.resize-cursor {
138
+ cursor: col-resize;
139
+ }
140
+
141
+ .ProseMirror .selectedCell:after {
142
+ z-index: 2;
143
+ position: absolute;
144
+ content: "";
145
+ left: 0;
146
+ right: 0;
147
+ top: 0;
148
+ bottom: 0;
149
+ background: var(--stilo-color-selected-cell, rgb(9 105 218 / 0.12));
150
+ pointer-events: none;
151
+ }
152
+
99
153
  /* Stilo content defaults */
100
154
 
101
155
  /* Lists: list items hold paragraphs, which must not add their own margins. */
@@ -107,10 +161,81 @@ li.ProseMirror-selectednode:after {
107
161
 
108
162
  .stilo-content li > p,
109
163
  .stilo-content li > ul,
110
- .stilo-content li > ol {
164
+ .stilo-content li > ol,
165
+ .stilo-content .stilo-task-item__content > p,
166
+ .stilo-content .stilo-task-item__content > ul,
167
+ .stilo-content .stilo-task-item__content > ol {
111
168
  margin: 0;
112
169
  }
113
170
 
171
+ /* Task lists: a checkbox in place of the bullet, done items struck through. In the editor the
172
+ item is a flex row: its <input> checkbox, then the content (ProseMirror needs a container that
173
+ holds only the content). Content shown elsewhere under the class stilo-content gets a box drawn
174
+ from data-checked instead. Either way the box is centered on the first line, not on the whole
175
+ item, which may hold more paragraphs and sub-lists. */
176
+ .stilo-content ul[data-type="taskList"] {
177
+ list-style: none;
178
+ }
179
+
180
+ .stilo-content.ProseMirror ul[data-type="taskList"] {
181
+ padding-left: 0.25em;
182
+ }
183
+
184
+ .stilo-content.ProseMirror ul[data-type="taskList"] > li {
185
+ display: flex;
186
+ gap: 0.5em;
187
+ }
188
+
189
+ .stilo-task-item__content {
190
+ flex: 1;
191
+ min-width: 0;
192
+ }
193
+
194
+ /* Form controls have their own font: inherit it, so that em and lh are those of the text. */
195
+ .stilo-task-item__checkbox {
196
+ flex: none;
197
+ width: 1em;
198
+ height: 1em;
199
+ margin: calc(0.5lh - 0.5em) 0 0;
200
+ font-size: inherit;
201
+ line-height: inherit;
202
+ accent-color: var(--stilo-color-focus, #0969da);
203
+ cursor: pointer;
204
+ }
205
+
206
+ .stilo-content:not(.ProseMirror) ul[data-type="taskList"] > li {
207
+ position: relative;
208
+ }
209
+
210
+ .stilo-content:not(.ProseMirror) ul[data-type="taskList"] > li::before {
211
+ content: "";
212
+ display: flex;
213
+ position: absolute;
214
+ top: calc(0.5lh - 0.5em);
215
+ left: -1.5em;
216
+ box-sizing: border-box;
217
+ width: 1em;
218
+ height: 1em;
219
+ align-items: center;
220
+ justify-content: center;
221
+ border: 1px solid var(--stilo-color-muted, #59636e);
222
+ border-radius: 0.2em;
223
+ color: #ffffff;
224
+ }
225
+
226
+ .stilo-content:not(.ProseMirror) ul[data-type="taskList"] > li[data-checked="true"]::before {
227
+ content: "\2713";
228
+ border-color: var(--stilo-color-focus, #0969da);
229
+ background: var(--stilo-color-focus, #0969da);
230
+ }
231
+
232
+ .stilo-content [data-checked="true"] > .stilo-task-item__content > p:first-child,
233
+ .stilo-content:not(.ProseMirror) [data-checked="true"] > p:first-child,
234
+ .stilo-content:not(.ProseMirror) [data-checked="true"]:not(:has(> p)) {
235
+ color: var(--stilo-color-muted, #59636e);
236
+ text-decoration: line-through;
237
+ }
238
+
114
239
  .stilo-content blockquote {
115
240
  margin: 0.5em 0;
116
241
  padding-left: 1em;
@@ -132,6 +257,109 @@ li.ProseMirror-selectednode:after {
132
257
  font-size: 0.9em;
133
258
  }
134
259
 
260
+ /* Code block: the <code> inside it drops the inline code box. */
261
+ .stilo-content pre {
262
+ margin: 0.5em 0;
263
+ padding: 0.75em 1em;
264
+ border-radius: 0.375em;
265
+ background: var(--stilo-color-hover, #eef1f4);
266
+ font-family: ui-monospace, monospace;
267
+ font-size: 0.9em;
268
+ line-height: 1.5;
269
+ }
270
+
271
+ .stilo-content pre code {
272
+ padding: 0;
273
+ border-radius: 0;
274
+ background: none;
275
+ font-size: inherit;
276
+ }
277
+
278
+ /* Tables: a cell holds blocks, whose outer margins would pad the cell twice. */
279
+ .stilo-content .tableWrapper {
280
+ margin: 1em 0;
281
+ }
282
+
283
+ .stilo-content td,
284
+ .stilo-content th {
285
+ padding: 6px 8px;
286
+ border: 1px solid var(--stilo-color-border, #d1d9e0);
287
+ }
288
+
289
+ .stilo-content th {
290
+ background: var(--stilo-color-table-header, #f6f8fa);
291
+ font-weight: 600;
292
+ text-align: start;
293
+ }
294
+
295
+ .stilo-content td > :first-child,
296
+ .stilo-content th > :first-child {
297
+ margin-top: 0;
298
+ }
299
+
300
+ /* The resize handle shown on hover is the last child of its cell: the block before it is the last
301
+ block, otherwise its margin would come back and the table grow under the mouse. */
302
+ .stilo-content td > :last-child,
303
+ .stilo-content th > :last-child,
304
+ .stilo-content td > :has(+ .column-resize-handle:last-child),
305
+ .stilo-content th > :has(+ .column-resize-handle:last-child) {
306
+ margin-bottom: 0;
307
+ }
308
+
309
+ /* Images: never wider than the content; with width and height they keep their ratio. */
310
+ .stilo-content img {
311
+ max-width: 100%;
312
+ height: auto;
313
+ }
314
+
315
+ /* Image in the editor: two side handles, inside the image (a top-level block clips what
316
+ overflows it, see "Large documents" below), shown while the image is selected. Only a selected
317
+ image is positioned: a positioned element paints over the caret placed right after it. */
318
+ .stilo-image {
319
+ display: inline-block;
320
+ max-width: 100%;
321
+ }
322
+
323
+ .stilo-image.ProseMirror-selectednode {
324
+ position: relative;
325
+ }
326
+
327
+ .stilo-image img {
328
+ display: block;
329
+ }
330
+
331
+ .stilo-image__handle {
332
+ display: none;
333
+ position: absolute;
334
+ top: 50%;
335
+ width: 6px;
336
+ height: min(48px, 50%);
337
+ min-height: 16px;
338
+ transform: translateY(-50%);
339
+ border: 1px solid #ffffff;
340
+ border-radius: 3px;
341
+ background: var(--stilo-color-text, #1f2328);
342
+ opacity: 0.7;
343
+ cursor: ew-resize;
344
+ touch-action: none;
345
+ }
346
+
347
+ .stilo-image__handle--left {
348
+ left: 6px;
349
+ }
350
+
351
+ .stilo-image__handle--right {
352
+ right: 6px;
353
+ }
354
+
355
+ .stilo-content[contenteditable="true"] .stilo-image.ProseMirror-selectednode .stilo-image__handle {
356
+ display: block;
357
+ }
358
+
359
+ .stilo-image__handle:hover {
360
+ opacity: 1;
361
+ }
362
+
135
363
  /* Placeholder: shown only while the content is editable. */
136
364
  .stilo-content[contenteditable="true"] .stilo-placeholder::before {
137
365
  content: attr(data-placeholder);
@@ -160,6 +388,15 @@ li.ProseMirror-selectednode:after {
160
388
  margin-bottom: 0;
161
389
  }
162
390
 
391
+ /* Find and replace (T-043): the matches and the selected one */
392
+ .stilo-search-match {
393
+ background: var(--stilo-color-search-match, #fff1a8);
394
+ }
395
+
396
+ .stilo-search-match--current {
397
+ background: var(--stilo-color-search-current, #ffb347);
398
+ }
399
+
163
400
  /* Stilo default UI styles. */
164
401
 
165
402
  /*
@@ -302,6 +539,11 @@ li.ProseMirror-selectednode:after {
302
539
  background: var(--stilo-color-active);
303
540
  }
304
541
 
542
+ /* The find button while its bar is open (T-043) */
543
+ .stilo-toolbar__button[data-item="find"][aria-expanded="true"] {
544
+ background: var(--stilo-color-active);
545
+ }
546
+
305
547
  .stilo-toolbar__button[aria-disabled="true"] {
306
548
  opacity: 0.4;
307
549
  cursor: default;
@@ -346,13 +588,24 @@ li.ProseMirror-selectednode:after {
346
588
  width: auto;
347
589
  min-width: 120px;
348
590
  justify-content: space-between;
591
+ gap: 6px;
349
592
  padding: 0 8px;
350
593
  }
351
594
 
595
+ .stilo-dropdown__trigger[data-item="fontFamily"] {
596
+ width: 120px;
597
+ }
598
+
599
+ /* A long value is truncated, the chevron keeps its size */
600
+ .stilo-dropdown__trigger .stilo-icon {
601
+ flex-shrink: 0;
602
+ }
603
+
352
604
  .stilo-dropdown__value {
353
605
  overflow: hidden;
354
606
  text-overflow: ellipsis;
355
607
  white-space: nowrap;
608
+ font-weight: 600;
356
609
  }
357
610
 
358
611
  .stilo-dropdown__menu {
@@ -434,6 +687,87 @@ li.ProseMirror-selectednode:after {
434
687
  font-weight: 600;
435
688
  }
436
689
 
690
+ /* Font size control: − field + */
691
+ .stilo-font-size {
692
+ display: inline-flex;
693
+ align-items: center;
694
+ gap: var(--stilo-toolbar-gap);
695
+ }
696
+
697
+ .stilo-font-size__input {
698
+ box-sizing: border-box;
699
+ width: 2.5em;
700
+ height: calc(var(--stilo-button-size) - 4px);
701
+ padding: 0 4px;
702
+ border: 1px solid var(--stilo-color-border);
703
+ border-radius: var(--stilo-radius);
704
+ background: var(--stilo-color-bg);
705
+ color: var(--stilo-color-text);
706
+ font-family: var(--stilo-font-family);
707
+ font-size: var(--stilo-font-size);
708
+ text-align: center;
709
+ font-weight: 600;
710
+ }
711
+
712
+ .stilo-font-size__input:focus-visible {
713
+ outline: 2px solid var(--stilo-color-focus);
714
+ outline-offset: -1px;
715
+ }
716
+
717
+ .stilo-font-size__input[aria-invalid="true"] {
718
+ border-color: var(--stilo-color-error);
719
+ }
720
+
721
+ .stilo-font-size__input[aria-disabled="true"] {
722
+ opacity: 0.4;
723
+ }
724
+
725
+ .stilo-font-size__list {
726
+ min-width: 4.5em;
727
+ max-height: 280px;
728
+ overflow-y: auto;
729
+ }
730
+
731
+ .stilo-font-size__option {
732
+ padding: 4px 12px;
733
+ border-radius: var(--stilo-radius);
734
+ cursor: pointer;
735
+ font-family: var(--stilo-font-family);
736
+ font-size: var(--stilo-font-size);
737
+ color: var(--stilo-color-text);
738
+ text-align: center;
739
+ }
740
+
741
+ .stilo-font-size__option:hover,
742
+ .stilo-font-size__option--active {
743
+ background: var(--stilo-color-hover);
744
+ }
745
+
746
+ .stilo-font-size__option[aria-selected="true"] {
747
+ background: var(--stilo-color-active);
748
+ }
749
+
750
+ /* Icon menu (text alignment): the options in a row. An icon dropdown (line spacing) has the same
751
+ trigger. */
752
+ .stilo-icon-menu__trigger,
753
+ .stilo-dropdown__trigger--icon {
754
+ width: auto;
755
+ min-width: var(--stilo-button-size);
756
+ gap: 2px;
757
+ padding: 0 4px;
758
+ }
759
+
760
+ .stilo-icon-menu {
761
+ display: flex;
762
+ gap: var(--stilo-toolbar-gap);
763
+ }
764
+
765
+ .stilo-icon-menu__trigger .stilo-icon:last-child,
766
+ .stilo-dropdown__trigger--icon .stilo-icon:last-child {
767
+ width: 12px;
768
+ height: 12px;
769
+ }
770
+
437
771
  /* Link popover */
438
772
  .stilo-link-popover__form {
439
773
  display: flex;
@@ -624,6 +958,132 @@ li.ProseMirror-selectednode:after {
624
958
  color: var(--stilo-color-muted);
625
959
  }
626
960
 
961
+ /* Table menu (T-045): insert grid of 10 columns (GRID_COLUMNS in table-menu.ts), operations */
962
+ .stilo-table-menu {
963
+ font-family: var(--stilo-font-family);
964
+ font-size: var(--stilo-font-size);
965
+ color: var(--stilo-color-text);
966
+ }
967
+
968
+ .stilo-table-menu__picker {
969
+ display: flex;
970
+ flex-direction: column;
971
+ gap: 6px;
972
+ padding: 4px;
973
+ }
974
+
975
+ .stilo-table-menu__picker[hidden],
976
+ .stilo-table-menu__actions[hidden] {
977
+ display: none;
978
+ }
979
+
980
+ .stilo-table-menu__grid {
981
+ display: grid;
982
+ grid-template-columns: repeat(10, 18px);
983
+ gap: 3px;
984
+ }
985
+
986
+ .stilo-table-menu__cell {
987
+ width: 18px;
988
+ height: 18px;
989
+ padding: 0;
990
+ border: 1px solid var(--stilo-color-border);
991
+ border-radius: 3px;
992
+ background: var(--stilo-color-bg);
993
+ cursor: pointer;
994
+ }
995
+
996
+ .stilo-table-menu__cell--active {
997
+ border-color: var(--stilo-color-focus);
998
+ background: var(--stilo-color-active);
999
+ }
1000
+
1001
+ .stilo-table-menu__cell:focus-visible {
1002
+ outline: 2px solid var(--stilo-color-focus);
1003
+ outline-offset: 1px;
1004
+ }
1005
+
1006
+ .stilo-table-menu__size {
1007
+ text-align: center;
1008
+ color: var(--stilo-color-muted);
1009
+ }
1010
+
1011
+ .stilo-table-menu__header {
1012
+ display: flex;
1013
+ align-items: center;
1014
+ gap: 6px;
1015
+ cursor: pointer;
1016
+ }
1017
+
1018
+ .stilo-table-menu__actions {
1019
+ min-width: 200px;
1020
+ }
1021
+
1022
+ .stilo-table-menu__actions .stilo-dropdown__option[aria-checked] {
1023
+ background: transparent;
1024
+ }
1025
+
1026
+ .stilo-table-menu__actions .stilo-dropdown__option[aria-checked]:hover,
1027
+ .stilo-table-menu__actions .stilo-dropdown__option[aria-checked]:focus-visible {
1028
+ background: var(--stilo-color-hover);
1029
+ }
1030
+
1031
+ .stilo-table-menu__actions .stilo-dropdown__option[aria-checked]::after {
1032
+ content: "✓";
1033
+ margin-left: auto;
1034
+ visibility: hidden;
1035
+ }
1036
+
1037
+ .stilo-table-menu__actions .stilo-dropdown__option[aria-checked="true"]::after {
1038
+ visibility: visible;
1039
+ }
1040
+
1041
+ /* Table handles (T-049): row, column and corner, over the editor */
1042
+ .stilo-table-handle {
1043
+ position: absolute;
1044
+ top: 0;
1045
+ left: 0;
1046
+ z-index: var(--stilo-z-popover);
1047
+ display: flex;
1048
+ align-items: center;
1049
+ justify-content: center;
1050
+ width: 18px;
1051
+ height: 18px;
1052
+ padding: 0;
1053
+ border: 1px solid var(--stilo-color-border);
1054
+ border-radius: var(--stilo-radius);
1055
+ background: var(--stilo-color-bg);
1056
+ color: var(--stilo-color-muted);
1057
+ cursor: pointer;
1058
+ }
1059
+
1060
+ .stilo-table-handle[hidden] {
1061
+ display: none;
1062
+ }
1063
+
1064
+ .stilo-table-handle:hover,
1065
+ .stilo-table-handle[aria-expanded="true"] {
1066
+ border-color: var(--stilo-color-focus);
1067
+ color: var(--stilo-color-focus);
1068
+ }
1069
+
1070
+ .stilo-table-handle .stilo-icon {
1071
+ width: 14px;
1072
+ height: 14px;
1073
+ }
1074
+
1075
+ .stilo-table-handle-menu {
1076
+ font-family: var(--stilo-font-family);
1077
+ font-size: var(--stilo-font-size);
1078
+ color: var(--stilo-color-text);
1079
+ }
1080
+
1081
+ .stilo-table-menu__separator {
1082
+ height: 1px;
1083
+ margin: 4px 0;
1084
+ background: var(--stilo-color-separator);
1085
+ }
1086
+
627
1087
  /* Color split button (T-026): the main part shows the last used color under its icon */
628
1088
  .stilo-split {
629
1089
  display: flex;
@@ -795,3 +1255,141 @@ li.ProseMirror-selectednode:after {
795
1255
  font: inherit;
796
1256
  cursor: pointer;
797
1257
  }
1258
+
1259
+ /*
1260
+ * Find bar (T-043): a floating panel at the top right, just under the toolbar, over the content
1261
+ * (the content does not move). It lives in the toolbar container, so it stays at the top with a
1262
+ * sticky toolbar; without `sticky` the container is made a positioned ancestor.
1263
+ */
1264
+ .stilo-find-bar-host:not(.stilo-toolbar-host) {
1265
+ position: relative;
1266
+ }
1267
+
1268
+ .stilo-find-bar {
1269
+ position: absolute;
1270
+ top: calc(100% + 4px);
1271
+ right: 8px;
1272
+ /* Under the popovers of the toolbar (character picker, colors, menus), which open over it. */
1273
+ z-index: calc(var(--stilo-z-popover) - 1);
1274
+ box-sizing: border-box;
1275
+ display: flex;
1276
+ flex-direction: column;
1277
+ gap: 4px;
1278
+ max-width: calc(100% - 16px);
1279
+ padding: 6px 8px;
1280
+ border: 1px solid var(--stilo-color-border);
1281
+ border-radius: var(--stilo-radius);
1282
+ background: var(--stilo-color-bg);
1283
+ box-shadow: var(--stilo-popover-shadow);
1284
+ color: var(--stilo-color-text);
1285
+ font-family: var(--stilo-font-family);
1286
+ font-size: var(--stilo-font-size);
1287
+ transition:
1288
+ opacity 0.15s ease,
1289
+ transform 0.15s ease,
1290
+ display 0.15s allow-discrete;
1291
+ }
1292
+
1293
+ /* Slides in from above; browsers without @starting-style show it at once. */
1294
+ @starting-style {
1295
+ .stilo-find-bar {
1296
+ opacity: 0;
1297
+ transform: translateY(-6px);
1298
+ }
1299
+ }
1300
+
1301
+ @media (prefers-reduced-motion: reduce) {
1302
+ .stilo-find-bar {
1303
+ transition: none;
1304
+ }
1305
+ }
1306
+
1307
+ .stilo-find-bar__row {
1308
+ display: flex;
1309
+ align-items: center;
1310
+ gap: 4px;
1311
+ }
1312
+
1313
+ .stilo-find-bar[hidden] {
1314
+ display: none;
1315
+ opacity: 0;
1316
+ transform: translateY(-6px);
1317
+ }
1318
+
1319
+ .stilo-find-bar__row[hidden] {
1320
+ display: none;
1321
+ }
1322
+
1323
+ .stilo-find-bar__input {
1324
+ box-sizing: border-box;
1325
+ width: 200px;
1326
+ min-width: 0;
1327
+ flex: 1 1 auto;
1328
+ padding: 4px 8px;
1329
+ border: 1px solid var(--stilo-color-border);
1330
+ border-radius: var(--stilo-radius);
1331
+ background: var(--stilo-color-bg);
1332
+ color: inherit;
1333
+ font: inherit;
1334
+ }
1335
+
1336
+ .stilo-find-bar__input:focus-visible {
1337
+ outline: 2px solid var(--stilo-color-focus);
1338
+ outline-offset: -1px;
1339
+ }
1340
+
1341
+ .stilo-find-bar__count {
1342
+ min-width: 7em;
1343
+ color: var(--stilo-color-muted);
1344
+ font-size: 0.9em;
1345
+ white-space: nowrap;
1346
+ }
1347
+
1348
+ .stilo-find-bar__button {
1349
+ display: flex;
1350
+ align-items: center;
1351
+ justify-content: center;
1352
+ min-width: 28px;
1353
+ height: 28px;
1354
+ padding: 0;
1355
+ border: none;
1356
+ border-radius: var(--stilo-radius);
1357
+ background: transparent;
1358
+ color: inherit;
1359
+ cursor: pointer;
1360
+ font: inherit;
1361
+ }
1362
+
1363
+ .stilo-find-bar__button:hover {
1364
+ background: var(--stilo-color-hover);
1365
+ }
1366
+
1367
+ .stilo-find-bar__button:focus-visible {
1368
+ outline: 2px solid var(--stilo-color-focus);
1369
+ outline-offset: 1px;
1370
+ }
1371
+
1372
+ .stilo-find-bar__toggle[aria-pressed="true"] {
1373
+ background: var(--stilo-color-active);
1374
+ }
1375
+
1376
+ .stilo-find-bar__button--text {
1377
+ padding: 0 10px;
1378
+ white-space: nowrap;
1379
+ border: 1px solid var(--stilo-color-border);
1380
+ background: var(--stilo-color-bg);
1381
+ }
1382
+
1383
+ /* Status bar (T-043): word and character count under the editor */
1384
+ .stilo-status-bar {
1385
+ display: flex;
1386
+ gap: 16px;
1387
+ padding: 4px 12px;
1388
+ border-top: 1px solid var(--stilo-color-separator);
1389
+ border-bottom-left-radius: inherit;
1390
+ border-bottom-right-radius: inherit;
1391
+ background: var(--stilo-color-toolbar-bg);
1392
+ color: var(--stilo-color-muted);
1393
+ font-family: var(--stilo-font-family);
1394
+ font-size: 12px;
1395
+ }
@@ -1,13 +1,20 @@
1
+ import { type IconNode } from 'lucide';
1
2
  /** One option as displayed: label and shortcut already resolved and formatted. */
2
3
  export interface DropdownOptionView {
3
4
  id: string;
4
5
  label: string;
5
6
  shortcut?: string;
7
+ /** The label is drawn in this font family. */
8
+ fontFamily?: string;
9
+ /** An action (`menuitem`), never checked; otherwise a `menuitemradio`. */
10
+ action?: boolean;
6
11
  }
7
12
  export interface DropdownOptions {
8
13
  /** Accessible name of the trigger and of the menu; shown when no option is active. */
9
14
  label: string;
10
15
  options: DropdownOptionView[];
16
+ /** Shown by the trigger instead of the value; the value stays in its accessible name. */
17
+ icon?: IconNode;
11
18
  /** The popover container (the toolbar element). */
12
19
  container: HTMLElement;
13
20
  onSelect(id: string): void;
@@ -15,6 +22,8 @@ export interface DropdownOptions {
15
22
  export interface DropdownState {
16
23
  activeId: string | null;
17
24
  disabledIds: ReadonlySet<string>;
25
+ /** Shown by the trigger when no option is active; default: the dropdown label. */
26
+ valueLabel?: string | null;
18
27
  }
19
28
  export interface Dropdown {
20
29
  /** The trigger button; the toolbar inserts it in its group. */
@@ -0,0 +1,24 @@
1
+ import type { Editor } from '../../index';
2
+ import type { Messages } from '../i18n/types';
3
+ /** Options of {@link createFindBar}. */
4
+ export interface FindBarOptions {
5
+ /** The find bar is appended inside it, after the toolbar. */
6
+ container: HTMLElement;
7
+ messages: Messages;
8
+ /** The toolbar button that toggles the bar: it gets `aria-expanded`. */
9
+ button?: HTMLElement | null;
10
+ }
11
+ /** The find and replace bar (T-043). */
12
+ export interface FindBar {
13
+ readonly element: HTMLElement;
14
+ readonly isOpen: boolean;
15
+ /** Shows the bar and focuses the find field, prefilled with the selected text. */
16
+ open(): void;
17
+ /** Hides the bar, clears the search and gives the focus back to the editor. */
18
+ close(): void;
19
+ /** Closes the bar when it is open, otherwise opens it. */
20
+ toggle(): void;
21
+ destroy(): void;
22
+ }
23
+ /** Creates the find bar (hidden). */
24
+ export declare function createFindBar(editor: Editor, options: FindBarOptions): FindBar;