@sciflow/editor-start 0.0.1-beta2 → 0.0.3

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 (44) hide show
  1. package/README.md +49 -2
  2. package/THIRD_PARTY_LICENSES +9 -0
  3. package/bin/extract-translations.mjs +54 -0
  4. package/dist/bundle/sciflow-editor.css +1 -1
  5. package/dist/bundle/sciflow-editor.js +9821 -16643
  6. package/dist/index.d.ts +7 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +6 -3
  9. package/dist/lib/citation-source-adapter.d.ts +1 -1
  10. package/dist/lib/citation-source-adapter.d.ts.map +1 -1
  11. package/dist/lib/citation-source-adapter.js +1 -1
  12. package/dist/lib/drag-preview.d.ts.map +1 -1
  13. package/dist/lib/drag-preview.js +9 -2
  14. package/dist/lib/editor-element.d.ts +14 -3
  15. package/dist/lib/editor-element.d.ts.map +1 -1
  16. package/dist/lib/editor-element.js +25 -1
  17. package/dist/lib/format-bar.d.ts +19 -2
  18. package/dist/lib/format-bar.d.ts.map +1 -1
  19. package/dist/lib/format-bar.js +303 -59
  20. package/dist/lib/ghost-cursor.d.ts +10 -0
  21. package/dist/lib/ghost-cursor.d.ts.map +1 -0
  22. package/dist/lib/ghost-cursor.js +59 -0
  23. package/dist/lib/icons-generated.d.ts +14 -0
  24. package/dist/lib/icons-generated.d.ts.map +1 -0
  25. package/dist/lib/icons-generated.js +60 -0
  26. package/dist/lib/outline.d.ts +8 -5
  27. package/dist/lib/outline.d.ts.map +1 -1
  28. package/dist/lib/outline.js +80 -35
  29. package/dist/lib/reference-list.d.ts +10 -2
  30. package/dist/lib/reference-list.d.ts.map +1 -1
  31. package/dist/lib/reference-list.js +45 -9
  32. package/dist/lib/selection-editor.d.ts +16 -0
  33. package/dist/lib/selection-editor.d.ts.map +1 -1
  34. package/dist/lib/selection-editor.js +291 -148
  35. package/dist/lib/selection-ui-renderer.d.ts +1 -0
  36. package/dist/lib/selection-ui-renderer.d.ts.map +1 -1
  37. package/dist/lib/selection-ui-renderer.js +3 -3
  38. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  39. package/package.json +24 -17
  40. package/dist/demo/demo.js +0 -56
  41. package/dist/demo/index.html +0 -169
  42. package/dist/lib/citation-drop.d.ts +0 -25
  43. package/dist/lib/citation-drop.d.ts.map +0 -1
  44. package/dist/lib/citation-drop.js +0 -53
@@ -17,17 +17,31 @@ import { __decorate } from "tslib";
17
17
  *
18
18
  * The component code below is annotated to map those steps to specific hooks.
19
19
  */
20
- import { css, html, LitElement, unsafeCSS } from 'lit';
20
+ import { css, html, LitElement, nothing, unsafeCSS } from 'lit';
21
21
  import { customElement, property, state } from 'lit/decorators.js';
22
22
  import { classMap } from 'lit/directives/class-map.js';
23
+ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
24
+ import { t, LOCALE_CHANGE_EVENT } from '@sciflow/editor-core';
25
+ import { materialIcons } from './icons-generated.js';
23
26
  import formatBarCss from './format-bar.css?inline';
24
27
  import { applyThemeStylesToRoot, subscribeToSciFlowTheme } from './theme.js';
28
+ /* Inline SVG icons for each style option (16×16). */
29
+ const styleIcons = {
30
+ paragraph: html `<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><rect x="2" y="3" width="12" height="1.5" rx=".75"/><rect x="2" y="7" width="10" height="1.5" rx=".75"/><rect x="2" y="11" width="8" height="1.5" rx=".75"/></svg>`,
31
+ 'heading-1': html `<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><text x="1" y="13" font-size="13" font-weight="700" font-family="system-ui,sans-serif">H<tspan font-size="10">1</tspan></text></svg>`,
32
+ 'heading-2': html `<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><text x="1" y="13" font-size="13" font-weight="700" font-family="system-ui,sans-serif">H<tspan font-size="10">2</tspan></text></svg>`,
33
+ 'heading-3': html `<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><text x="1" y="13" font-size="13" font-weight="700" font-family="system-ui,sans-serif">H<tspan font-size="10">3</tspan></text></svg>`,
34
+ blockquote: html `<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M3.5 4C2.67 4 2 4.67 2 5.5v2C2 8.33 2.67 9 3.5 9H5l-1 3h1.6l1-3h.4V5.5C7 4.67 6.33 4 5.5 4h-2zm6 0C8.67 4 8 4.67 8 5.5v2C8 8.33 8.67 9 9.5 9H11l-1 3h1.6l1-3h.4V5.5c0-.83-.67-1.5-1.5-1.5h-2z"/></svg>`,
35
+ };
25
36
  const STYLE_OPTIONS = [
26
- { label: 'Paragraph', value: 'paragraph' },
27
- { label: 'Heading 1', value: 'heading-1', level: 1 },
28
- { label: 'Heading 2', value: 'heading-2', level: 2 },
29
- { label: 'Heading 3', value: 'heading-3', level: 3 },
37
+ { get label() { return t('formatBar.styleParagraph'); }, value: 'paragraph', cssClass: 'style-option--paragraph' },
38
+ { get label() { return t('formatBar.styleHeading1'); }, value: 'heading-1', level: 1, cssClass: 'style-option--h1' },
39
+ { get label() { return t('formatBar.styleHeading2'); }, value: 'heading-2', level: 2, cssClass: 'style-option--h2' },
40
+ { get label() { return t('formatBar.styleHeading3'); }, value: 'heading-3', level: 3, cssClass: 'style-option--h3' },
41
+ { get label() { return t('cmd.toggleBlockquote.label'); }, value: 'blockquote', cssClass: 'style-option--blockquote', command: 'toggleBlockquote' },
30
42
  ];
43
+ /** SVG chevron shared by dropdown triggers. */
44
+ const chevronSvg = html `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="6 9 12 15 18 9"></polyline></svg>`;
31
45
  /**
32
46
  * Custom element that renders the demo formatting toolbar.
33
47
  *
@@ -46,6 +60,8 @@ let SciFlowFormatBarElement = class SciFlowFormatBarElement extends LitElement {
46
60
  /** Reference to the editor element. May be passed directly via property binding. */
47
61
  this.editor = null;
48
62
  this.selectionVersion = 0;
63
+ this.styleMenuOpen = false;
64
+ this.insertMenuOpen = false;
49
65
  this.resolvedEditor = null;
50
66
  this.themeStyleElements = [];
51
67
  }
@@ -53,14 +69,27 @@ let SciFlowFormatBarElement = class SciFlowFormatBarElement extends LitElement {
53
69
  /** Subscribe to editor events once the toolbar attaches to the DOM. */
54
70
  connectedCallback() {
55
71
  super.connectedCallback();
72
+ if (!this.hasAttribute('role')) {
73
+ this.setAttribute('role', 'toolbar');
74
+ }
75
+ if (!this.hasAttribute('aria-label')) {
76
+ this.setAttribute('aria-label', t('formatBar.toolbarAriaLabel'));
77
+ }
56
78
  this.themeUnsub = subscribeToSciFlowTheme((cssTexts) => {
57
79
  this.themeStyleElements = applyThemeStylesToRoot(this.renderRoot, this.themeStyleElements, cssTexts);
58
80
  });
81
+ this.localeListener = () => this.requestUpdate();
82
+ document.addEventListener(LOCALE_CHANGE_EVENT, this.localeListener);
59
83
  this.resolveEditorReference();
60
84
  }
61
85
  /** Remove listeners when the toolbar is detached. */
62
86
  disconnectedCallback() {
63
87
  this.detachEditorListeners();
88
+ this.removeOutsideClickHandler();
89
+ if (this.localeListener) {
90
+ document.removeEventListener(LOCALE_CHANGE_EVENT, this.localeListener);
91
+ this.localeListener = undefined;
92
+ }
64
93
  this.themeUnsub?.();
65
94
  this.themeUnsub = undefined;
66
95
  super.disconnectedCallback();
@@ -80,55 +109,254 @@ let SciFlowFormatBarElement = class SciFlowFormatBarElement extends LitElement {
80
109
  */
81
110
  render() {
82
111
  const runner = this.getCommandRunner();
83
- const availableCommands = runner?.available?.() ?? null;
84
- const headingCommandsAvailable = typeof availableCommands?.setHeading === 'function' || typeof availableCommands?.setParagraph === 'function';
85
- const activeStyle = this.getActiveBlockStyle();
86
- const selectDisabled = !runner || !headingCommandsAvailable;
87
112
  const commandMetadata = runner?.commandsMeta?.() ?? {};
88
113
  const commandGroups = this.getCommandGroups(commandMetadata);
89
114
  const tableGroup = 'table';
90
- const primaryGroups = commandGroups.filter((group) => group !== tableGroup);
115
+ const mediaGroup = 'media';
116
+ const insertGroup = 'insert';
117
+ const blockGroup = 'block';
118
+ const primaryGroups = commandGroups.filter((group) => group !== tableGroup && group !== mediaGroup && group !== insertGroup && group !== blockGroup);
91
119
  const tableGroups = commandGroups.filter((group) => group === tableGroup);
92
120
  const selectionInTable = this.isSelectionInTable();
93
121
  return html `
94
- <select
95
- class="style-select"
96
- aria-label="Text style"
97
- .value=${activeStyle}
98
- ?disabled=${selectDisabled}
99
- @change=${this.handleStyleChange}
100
- >
101
- ${STYLE_OPTIONS.map((option) => html `
102
- <option
103
- value=${option.value}
104
- ?selected=${option.value === activeStyle}
105
- ?disabled=${!this.canUseStyleOption(option, availableCommands)}
106
- >
107
- ${option.label}
108
- </option>
109
- `)}
110
- </select>
111
- <div class="history-commands" aria-label="History commands">
112
- ${this.renderFixedCommandButton(runner, 'undo', 'undo', 'Undo', 'Undo last change')}
113
- ${this.renderFixedCommandButton(runner, 'redo', 'redo', 'Redo', 'Redo last change')}
122
+ <div class="command-group">
123
+ ${this.renderStyleDropdown(runner)}
124
+ ${this.renderInsertDropdown(runner, commandMetadata)}
114
125
  </div>
115
- <div class=${classMap({ 'command-rows': true, 'command-rows--table-expanded': selectionInTable })}>
116
- <div class="command-row command-row--primary">
117
- ${primaryGroups.map((group) => this.renderCommandGroup(runner, commandMetadata, group))}
118
- ${tableGroups.length && !selectionInTable
119
- ? html `<span class="table-commands ${primaryGroups.length ? 'table-commands--with-separator' : ''}">
120
- ${tableGroups.map((group) => this.renderCommandGroup(runner, commandMetadata, group, ['insertNativeTableFigure']))}
121
- </span>`
122
- : null}
126
+ ${primaryGroups.map((group) => html `
127
+ <div class="command-group">
128
+ ${this.renderCommandGroup(runner, commandMetadata, group)}
123
129
  </div>
124
- ${tableGroups.length && selectionInTable
125
- ? html `<div class="command-row command-row--table table-tools-expanded">
126
- ${tableGroups.map((group) => this.renderCommandGroup(runner, commandMetadata, group))}
127
- </div>`
128
- : null}
130
+ `)}
131
+ <div class="command-group">
132
+ ${this.renderFixedCommandButton(runner, 'undo', 'undo', t('formatBar.undo'), t('formatBar.undoDescription'))}
133
+ ${this.renderFixedCommandButton(runner, 'redo', 'redo', t('formatBar.redo'), t('formatBar.redoDescription'))}
134
+ </div>
135
+ ${tableGroups.length && selectionInTable
136
+ ? html `<div class="table-row" role="toolbar" aria-label=${t('formatBar.tableAriaLabel')}>
137
+ <span class="table-row-label">${t('formatBar.tableLabel')}</span>
138
+ ${this.renderTableSubGroups(runner, commandMetadata)}
139
+ </div>`
140
+ : nothing}
141
+ `;
142
+ }
143
+ /* --------------------------------------------------------- */
144
+ /* Style dropdown (Paragraph / Heading 1–3) */
145
+ /* --------------------------------------------------------- */
146
+ renderStyleDropdown(runner) {
147
+ const availableCommands = runner?.available?.() ?? null;
148
+ const headingCommandsAvailable = typeof availableCommands?.setHeading === 'function' || typeof availableCommands?.setParagraph === 'function';
149
+ const activeStyle = this.getActiveBlockStyle();
150
+ const selectDisabled = !runner || !headingCommandsAvailable;
151
+ const activeLabel = STYLE_OPTIONS.find((o) => o.value === activeStyle)?.label ?? t('formatBar.styleParagraph');
152
+ return html `
153
+ <div class="style-dropdown">
154
+ <button
155
+ type="button"
156
+ class="style-dropdown-trigger"
157
+ aria-label=${t('formatBar.textStyleAriaLabel')}
158
+ aria-haspopup="listbox"
159
+ aria-expanded=${this.styleMenuOpen ? 'true' : 'false'}
160
+ ?disabled=${selectDisabled}
161
+ @click=${this.toggleStyleMenu}
162
+ >
163
+ ${activeLabel}
164
+ <span class="dropdown-chevron">${chevronSvg}</span>
165
+ </button>
166
+ ${this.styleMenuOpen
167
+ ? html `
168
+ <div class="style-dropdown-menu" role="listbox" aria-label=${t('formatBar.textStyleAriaLabel')}>
169
+ ${STYLE_OPTIONS.map((option) => {
170
+ const isActive = option.command
171
+ ? this.isBlockquoteActive()
172
+ : option.value === activeStyle;
173
+ const canUse = option.command
174
+ ? this.canUseCommand(option.command, runner)
175
+ : this.canUseStyleOption(option, availableCommands);
176
+ return html `
177
+ <button
178
+ type="button"
179
+ role="option"
180
+ class=${classMap({
181
+ 'style-option': true,
182
+ [option.cssClass]: true,
183
+ active: isActive,
184
+ })}
185
+ aria-selected=${isActive ? 'true' : 'false'}
186
+ ?disabled=${!canUse}
187
+ @click=${() => this.selectStyle(option, runner)}
188
+ >
189
+ <span class="style-option-icon">${styleIcons[option.value]}</span>
190
+ ${option.label}
191
+ </button>
192
+ `;
193
+ })}
194
+ </div>
195
+ `
196
+ : nothing}
197
+ </div>
198
+ `;
199
+ }
200
+ toggleStyleMenu(e) {
201
+ e.stopPropagation();
202
+ this.insertMenuOpen = false;
203
+ this.styleMenuOpen = !this.styleMenuOpen;
204
+ if (this.styleMenuOpen) {
205
+ this.installOutsideClickHandler();
206
+ }
207
+ else {
208
+ this.removeOutsideClickHandler();
209
+ }
210
+ }
211
+ selectStyle(option, runner) {
212
+ this.styleMenuOpen = false;
213
+ this.removeOutsideClickHandler();
214
+ if (option.command) {
215
+ const command = runner?.commands?.[option.command];
216
+ if (typeof command === 'function') {
217
+ const result = command();
218
+ if (result) {
219
+ this.selectionVersion++;
220
+ this.requestUpdate();
221
+ }
222
+ }
223
+ }
224
+ else if (option.value === 'paragraph') {
225
+ this.handleSetParagraph();
226
+ }
227
+ else if (option.level) {
228
+ this.handleSetHeading(option.level);
229
+ }
230
+ }
231
+ isBlockquoteActive() {
232
+ const state = this.resolvedEditor?.editorView?.state;
233
+ if (!state)
234
+ return false;
235
+ const bqType = state.schema.nodes.blockquote;
236
+ if (!bqType)
237
+ return false;
238
+ const { from, to } = state.selection;
239
+ let found = false;
240
+ state.doc.nodesBetween(from, to, (node) => {
241
+ if (node.type === bqType) {
242
+ found = true;
243
+ return false;
244
+ }
245
+ return true;
246
+ });
247
+ return found;
248
+ }
249
+ canUseCommand(commandName, runner) {
250
+ const availability = runner?.available?.();
251
+ if (availability?.[commandName])
252
+ return availability[commandName]();
253
+ return typeof runner?.commands?.[commandName] === 'function';
254
+ }
255
+ /* --------------------------------------------------------- */
256
+ /* Insert dropdown (icon + chevron → popover) */
257
+ /* --------------------------------------------------------- */
258
+ renderInsertDropdown(runner, metadata) {
259
+ const availability = runner?.available?.() ?? null;
260
+ // Collect block-level insert commands (media group + table insert + footnote)
261
+ const insertCommands = Object.entries(metadata)
262
+ .filter(([name, meta]) => meta && (meta.group === 'media' || name === 'insertNativeTableFigure' || name === 'insertFootnote'))
263
+ .sort(([, a], [, b]) => (a?.order ?? 0) - (b?.order ?? 0));
264
+ if (insertCommands.length === 0) {
265
+ return null;
266
+ }
267
+ return html `
268
+ <div class="insert-dropdown">
269
+ <button
270
+ type="button"
271
+ class="insert-dropdown-trigger"
272
+ aria-label=${t('formatBar.insert')}
273
+ aria-haspopup="menu"
274
+ aria-expanded=${this.insertMenuOpen ? 'true' : 'false'}
275
+ @click=${this.toggleInsertMenu}
276
+ >
277
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
278
+ <span class="dropdown-chevron">${chevronSvg}</span>
279
+ </button>
280
+ ${this.insertMenuOpen
281
+ ? html `
282
+ <div class="insert-dropdown-menu" role="menu">
283
+ ${insertCommands.map(([name, meta]) => {
284
+ const canExecute = availability?.[name] ? availability[name]() : true;
285
+ return html `
286
+ <button
287
+ type="button"
288
+ role="menuitem"
289
+ class="insert-menu-item"
290
+ ?disabled=${!canExecute}
291
+ @click=${() => this.executeInsertCommand(name, runner)}
292
+ >
293
+ ${meta?.icon && materialIcons[meta.icon]
294
+ ? html `<span class="cmd-icon" aria-hidden="true">${unsafeHTML(materialIcons[meta.icon])}</span>`
295
+ : nothing}
296
+ ${meta?.label ?? name}
297
+ </button>
298
+ `;
299
+ })}
300
+ </div>
301
+ `
302
+ : nothing}
129
303
  </div>
130
304
  `;
131
305
  }
306
+ toggleInsertMenu(e) {
307
+ e.stopPropagation();
308
+ this.styleMenuOpen = false;
309
+ this.insertMenuOpen = !this.insertMenuOpen;
310
+ if (this.insertMenuOpen) {
311
+ this.installOutsideClickHandler();
312
+ }
313
+ else {
314
+ this.removeOutsideClickHandler();
315
+ }
316
+ }
317
+ executeInsertCommand(commandName, runner) {
318
+ this.insertMenuOpen = false;
319
+ this.removeOutsideClickHandler();
320
+ if (!runner)
321
+ return;
322
+ const command = runner.commands?.[commandName];
323
+ if (typeof command === 'function') {
324
+ const result = command();
325
+ if (result) {
326
+ this.selectionVersion++;
327
+ this.requestUpdate();
328
+ }
329
+ }
330
+ }
331
+ /* --------------------------------------------------------- */
332
+ /* Shared dropdown close-on-outside-click */
333
+ /* --------------------------------------------------------- */
334
+ installOutsideClickHandler() {
335
+ this.removeOutsideClickHandler();
336
+ this.outsideClickHandler = (e) => {
337
+ const path = e.composedPath();
338
+ if (!path.includes(this)) {
339
+ this.styleMenuOpen = false;
340
+ this.insertMenuOpen = false;
341
+ this.removeOutsideClickHandler();
342
+ }
343
+ };
344
+ // Use capture + next frame so the current click doesn't immediately close.
345
+ requestAnimationFrame(() => {
346
+ if (this.outsideClickHandler) {
347
+ document.addEventListener('click', this.outsideClickHandler, true);
348
+ }
349
+ });
350
+ }
351
+ removeOutsideClickHandler() {
352
+ if (this.outsideClickHandler) {
353
+ document.removeEventListener('click', this.outsideClickHandler, true);
354
+ this.outsideClickHandler = undefined;
355
+ }
356
+ }
357
+ /* --------------------------------------------------------- */
358
+ /* Fixed command buttons (undo / redo) */
359
+ /* --------------------------------------------------------- */
132
360
  /**
133
361
  * Render fixed toolbar commands that are not driven by metadata groups.
134
362
  */
@@ -158,10 +386,13 @@ let SciFlowFormatBarElement = class SciFlowFormatBarElement extends LitElement {
158
386
  data-tooltip=${description}
159
387
  @click=${handleClick}
160
388
  >
161
- <span class="material-symbols-rounded" aria-hidden="true">${icon}</span>
389
+ <span class="cmd-icon" aria-hidden="true">${unsafeHTML(materialIcons[icon] ?? '')}</span>
162
390
  </button>
163
391
  `;
164
392
  }
393
+ /* --------------------------------------------------------- */
394
+ /* Editor resolution & event wiring */
395
+ /* --------------------------------------------------------- */
165
396
  /**
166
397
  * Find the target editor element and attach listeners for selection changes.
167
398
  * This mirrors the wiring a framework component would perform in `useEffect`.
@@ -227,20 +458,9 @@ let SciFlowFormatBarElement = class SciFlowFormatBarElement extends LitElement {
227
458
  getCommandRunner() {
228
459
  return this.resolvedEditor?.commands ?? null;
229
460
  }
230
- /** Interpret the block-style dropdown and trigger the relevant commands. */
231
- handleStyleChange(event) {
232
- const select = event.target;
233
- const value = select.value;
234
- if (value === 'paragraph') {
235
- this.handleSetParagraph();
236
- return;
237
- }
238
- const parsed = Number.parseInt(value.split('-')[1] ?? '', 10);
239
- if (!Number.isInteger(parsed)) {
240
- return;
241
- }
242
- this.handleSetHeading(parsed);
243
- }
461
+ /* --------------------------------------------------------- */
462
+ /* Heading / paragraph commands */
463
+ /* --------------------------------------------------------- */
244
464
  /** Apply a specific heading level after ensuring the editor can handle it. */
245
465
  handleSetHeading(level) {
246
466
  const runner = this.getCommandRunner();
@@ -366,6 +586,24 @@ let SciFlowFormatBarElement = class SciFlowFormatBarElement extends LitElement {
366
586
  }
367
587
  return false;
368
588
  }
589
+ /**
590
+ * Render table commands split into logical sub-groups (columns, rows,
591
+ * merge/split, header toggles, navigation/delete) separated visually.
592
+ */
593
+ renderTableSubGroups(runner, metadata) {
594
+ const subGroups = [
595
+ ['insertNativeTableFigure'],
596
+ ['addColumnBefore', 'addColumnAfter', 'deleteColumn'],
597
+ ['addRowBefore', 'addRowAfter', 'deleteRow'],
598
+ ['mergeCells', 'splitCell'],
599
+ ['toggleHeaderRow', 'toggleHeaderColumn'],
600
+ ['goToNextCell', 'deleteTable'],
601
+ ];
602
+ return subGroups.map((names) => {
603
+ const buttons = this.renderCommandGroup(runner, metadata, 'table', names);
604
+ return html `<div class="command-group">${buttons}</div>`;
605
+ });
606
+ }
369
607
  /**
370
608
  * Render a set of command buttons that share the same metadata group.
371
609
  * Groups are sorted by each command's declared order so feature authors control layout.
@@ -420,7 +658,7 @@ let SciFlowFormatBarElement = class SciFlowFormatBarElement extends LitElement {
420
658
  @click=${handleClick}
421
659
  >
422
660
  ${meta.icon
423
- ? html `<span class="material-symbols-rounded" aria-hidden="true">${meta.icon}</span>`
661
+ ? html `<span class="cmd-icon" aria-hidden="true">${unsafeHTML(materialIcons[meta.icon] ?? '')}</span>`
424
662
  : meta.label ?? name}
425
663
  </button>
426
664
  `;
@@ -464,6 +702,12 @@ __decorate([
464
702
  __decorate([
465
703
  state()
466
704
  ], SciFlowFormatBarElement.prototype, "selectionVersion", void 0);
705
+ __decorate([
706
+ state()
707
+ ], SciFlowFormatBarElement.prototype, "styleMenuOpen", void 0);
708
+ __decorate([
709
+ state()
710
+ ], SciFlowFormatBarElement.prototype, "insertMenuOpen", void 0);
467
711
  SciFlowFormatBarElement = __decorate([
468
712
  customElement('sciflow-formatbar')
469
713
  ], SciFlowFormatBarElement);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @module ghost-cursor
3
+ *
4
+ * ProseMirror feature that keeps the cursor position visible when the editor
5
+ * loses focus. A thin blue bar is rendered at the last cursor position so
6
+ * the user always knows where the next insertion (e.g. Cite, Insert ref) will land.
7
+ */
8
+ import type { Feature } from '@sciflow/editor-core';
9
+ export declare const ghostCursorFeature: Feature;
10
+ //# sourceMappingURL=ghost-cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ghost-cursor.d.ts","sourceRoot":"","sources":["../../src/lib/ghost-cursor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAsDpD,eAAO,MAAM,kBAAkB,EAAE,OAKhC,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @module ghost-cursor
3
+ *
4
+ * ProseMirror feature that keeps the cursor position visible when the editor
5
+ * loses focus. A thin blue bar is rendered at the last cursor position so
6
+ * the user always knows where the next insertion (e.g. Cite, Insert ref) will land.
7
+ */
8
+ import { Plugin, PluginKey } from 'prosemirror-state';
9
+ import { Decoration, DecorationSet } from 'prosemirror-view';
10
+ const ghostCursorKey = new PluginKey('ghostCursor');
11
+ const ghostCursorPlugin = new Plugin({
12
+ key: ghostCursorKey,
13
+ state: {
14
+ init: () => true,
15
+ apply(tr, focused) {
16
+ const next = tr.getMeta(ghostCursorKey);
17
+ return typeof next === 'boolean' ? next : focused;
18
+ },
19
+ },
20
+ props: {
21
+ handleDOMEvents: {
22
+ blur(view) {
23
+ view.dispatch(view.state.tr.setMeta(ghostCursorKey, false));
24
+ return false;
25
+ },
26
+ focus(view) {
27
+ view.dispatch(view.state.tr.setMeta(ghostCursorKey, true));
28
+ return false;
29
+ },
30
+ },
31
+ decorations(state) {
32
+ const focused = ghostCursorKey.getState(state);
33
+ if (focused !== false)
34
+ return DecorationSet.empty;
35
+ const { selection } = state;
36
+ // For a range selection, highlight the selected text instead of showing a caret
37
+ if (!selection.empty) {
38
+ return DecorationSet.create(state.doc, [
39
+ Decoration.inline(selection.from, selection.to, {
40
+ class: 'prosemirror-ghost-selection',
41
+ }),
42
+ ]);
43
+ }
44
+ // For a collapsed cursor, insert a widget caret marker
45
+ const el = document.createElement('span');
46
+ el.className = 'prosemirror-ghost-cursor';
47
+ el.setAttribute('aria-hidden', 'true');
48
+ return DecorationSet.create(state.doc, [
49
+ Decoration.widget(selection.head, el, { side: 0, key: 'ghost-cursor' }),
50
+ ]);
51
+ },
52
+ },
53
+ });
54
+ export const ghostCursorFeature = {
55
+ name: 'ghost-cursor',
56
+ addPlugins() {
57
+ return [ghostCursorPlugin];
58
+ },
59
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Auto-generated – do not edit.
3
+ * Run `node scripts/generate-icons.mjs` to regenerate.
4
+ *
5
+ * Icons: Material Symbols Rounded, weight 400, unfilled.
6
+ * Source: @material-symbols/svg-400 (Apache License 2.0, Copyright Google LLC)
7
+ * https://github.com/google/material-design-icons
8
+ */
9
+ /**
10
+ * Map of icon names (as used in CommandMetadata.icon) → inline SVG markup.
11
+ * Only the icons actually used by the editor are included (~29 icons).
12
+ */
13
+ export declare const materialIcons: Record<string, string>;
14
+ //# sourceMappingURL=icons-generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons-generated.d.ts","sourceRoot":"","sources":["../../src/lib/icons-generated.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAwChD,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Auto-generated – do not edit.
3
+ * Run `node scripts/generate-icons.mjs` to regenerate.
4
+ *
5
+ * Icons: Material Symbols Rounded, weight 400, unfilled.
6
+ * Source: @material-symbols/svg-400 (Apache License 2.0, Copyright Google LLC)
7
+ * https://github.com/google/material-design-icons
8
+ */
9
+ /** SVG viewBox shared by all Material Symbols icons. */
10
+ const V = '0 -960 960 960';
11
+ /**
12
+ * Builds a complete inline SVG string for a Material Symbols icon.
13
+ * The SVG inherits `currentColor` via the `fill` attribute for easy theming.
14
+ */
15
+ const svg = (d) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${V}" fill="currentColor" aria-hidden="true">${d}</svg>`;
16
+ /**
17
+ * Map of icon names (as used in CommandMetadata.icon) → inline SVG markup.
18
+ * Only the icons actually used by the editor are included (~29 icons).
19
+ */
20
+ export const materialIcons = {
21
+ 'format_bold': svg(`<path d="M335-200q-25 0-42.5-17.5T275-260v-440q0-25 17.5-42.5T335-760h168q66 0 114.5 42T666-612q0 38-21 70t-56 49v6q43 14 69.5 50t26.5 81q0 68-52.5 112T510-200H335Zm26-76h144q38 0 66-25t28-63q0-37-28-62t-66-25H361v175Zm0-247h136q35 0 60.5-23t25.5-58q0-35-25.5-58.5T497-686H361v163Z"/>`),
22
+ 'format_italic': svg(`<path d="M264-199q-16.8 0-28.4-11.64-11.6-11.64-11.6-28.5t11.6-28.36Q247.2-279 264-279h94l139-409H378q-16.8 0-28.4-11.64-11.6-11.64-11.6-28.5t11.6-28.36Q361.2-768 378-768h300q16.8 0 28.4 11.64 11.6 11.64 11.6 28.5t-11.6 28.36Q694.8-688 678-688h-94L445-279h119q16.8 0 28.4 11.64 11.6 11.64 11.6 28.5t-11.6 28.36Q580.8-199 564-199H264Z"/>`),
23
+ 'format_quote': svg(`<path d="m275-287 67-115q-5 1-11 1.5t-11 .5q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 21-5.5 41T458-480L329-255q-4 8-11.5 11.5T302-240q-18 0-27-15.5t0-31.5Zm360 0 67-115q-5 1-11 1.5t-11 .5q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 21-5.5 41T818-480L689-255q-4 8-11.5 11.5T662-240q-18 0-27-15.5t0-31.5ZM376.5-503.5Q400-527 400-560t-23.5-56.5Q353-640 320-640t-56.5 23.5Q240-593 240-560t23.5 56.5Q287-480 320-480t56.5-23.5Zm360 0Q760-527 760-560t-23.5-56.5Q713-640 680-640t-56.5 23.5Q600-593 600-560t23.5 56.5Q647-480 680-480t56.5-23.5ZM680-560Zm-360 0Z"/>`),
24
+ 'format_list_bulleted': svg(`<path d="M407-198q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h403q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H407Zm0-252q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h403q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H407Zm0-253q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h403q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H407ZM189-161q-28.05 0-48.02-19Q121-199 121-227.5t19.5-48q19.5-19.5 48-19.5t47.5 19.98q19 19.97 19 48.02 0 27.23-19.39 46.61Q216.23-161 189-161Zm0-252q-28.05 0-48.02-19.5Q121-452 121-480t19.98-47.5Q160.95-547 189-547q27.23 0 46.61 19.5Q255-508 255-480t-19.39 47.5Q216.23-413 189-413Zm-48.5-272.5Q121-705 121-733t19.5-47.5Q160-800 188-800t47.5 19.5Q255-761 255-733t-19.5 47.5Q216-666 188-666t-47.5-19.5Z"/>`),
25
+ 'format_list_numbered': svg(`<path d="M150-80q-13 0-21.5-8.5T120-110q0-13 8.5-21.5T150-140h70v-30h-30q-13 0-21.5-8.5T160-200q0-13 8.5-21.5T190-230h30v-30h-70q-13 0-21.5-8.5T120-290q0-13 8.5-21.5T150-320h90q17 0 28.5 11.5T280-280v40q0 17-11.5 28.5T240-200q17 0 28.5 11.5T280-160v40q0 17-11.5 28.5T240-80h-90Zm-7-280q-9 0-16-7t-7-16v-87q0-17 11.5-28.5T160-510h60v-30h-70q-13 0-21.5-8.5T120-570q0-13 8.5-21.5T150-600h90q17 0 28.5 11.5T280-560v70q0 17-11.5 28.5T240-450h-60v30h70q13 0 21.5 8.5T280-390q0 13-8.5 21.5T250-360H143Zm45.5-288.5Q180-657 180-670v-150h-30q-13 0-21.5-8.5T120-850q0-13 8.5-21.5T150-880h68q9 0 15.5 6.5T240-858v188q0 13-8.5 21.5T210-640q-13 0-21.5-8.5ZM399-209q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h411q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H399Zm0-243q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h411q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H399Zm0-243q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h411q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H399Z"/>`),
26
+ 'format_indent_increase': svg(`<path d="M150-120q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm320-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h340q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H470Zm0-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h340q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H470Zm0-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h340q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H470ZM150-780q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm-5 412q-8 7-16.5 3.25T120-379v-205q0-9.92 9.5-13.46Q139-601 146-594l104 104q5 5 5 10.5t-5 10.5L145-368Z"/>`),
27
+ 'format_indent_decrease': svg(`<path d="M150-120q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm320-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h340q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H470Zm0-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h340q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H470Zm0-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h340q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H470ZM150-780q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm85 412L131-472q-5-4.64-5-10.82 0-6.18 5-11.18l105-100q8-7 16.5-3.46T261-584v205q0 10.5-9.5 14.25T235-368Z"/>`),
28
+ 'asterisk': svg(`<path d="M458.5-128.63Q450-137.25 450-150v-258L268-226q-8.8 9-20.9 9-12.1 0-21.1-8.84-9-8.84-9-21t9-21.16l182-182H150q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h259L226-693q-9-8.8-9-20.9 0-12.1 8.84-21.1 8.84-9 21-9t21.16 9l182 182v-257q0-12.75 8.68-21.38 8.67-8.62 21.5-8.62 12.82 0 21.32 8.62 8.5 8.63 8.5 21.38v258l183-183q8.8-9 20.9-9 12.1 0 21.1 8.84 9 8.84 9 21T735-693L552-510h258q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H553l182 182q9 8.8 9 20.9 0 12.1-8.84 21.1-8.84 9-21 9T693-226L510-409v259q0 12.75-8.68 21.37-8.67 8.63-21.5 8.63-12.82 0-21.32-8.63Z"/>`),
29
+ 'functions': svg(`<path d="M515-480 250-728q-4.62-4.7-7.31-10.57-2.69-5.86-2.69-12.13v-18q0-13.3 8.91-22.3 8.92-9 22.09-9h399q20.83 0 35.42 14.62Q720-770.76 720-749.88q0 20.88-14.58 35.38Q690.83-700 670-700H396l212 198q10 9.13 10 22.07Q618-467 608-458L396-260h274q20.83 0 35.42 14.62Q720-230.76 720-209.88q0 20.88-14.58 35.38Q690.83-160 670-160H263q-9.52 0-16.26-6.72-6.74-6.73-6.74-16.22V-213q0-4.94 1.5-9.47T247-230l268-250Z"/>`),
30
+ 'image': svg(`<path d="M180-120q-24 0-42-18t-18-42v-600q0-24 18-42t42-18h600q24 0 42 18t18 42v600q0 24-18 42t-42 18H180Zm0-60h600v-600H180v600Zm0 0v-600 600Zm86-97h429q8.5 0 12.75-8t-.75-16L590-457q-5-6-12-6t-12 6L446-302l-81-111q-5-6-12-6t-12 6l-86 112q-6 8-1.75 16t12.75 8Z"/>`),
31
+ 'link': svg(`<path d="M280-280q-83 0-141.5-58.5T80-480q0-83 58.5-141.5T280-680h140q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H280q-58.33 0-99.17 40.76-40.83 40.77-40.83 99Q140-422 180.83-381q40.84 41 99.17 41h140q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H280Zm75-170q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h250q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H355Zm185 170q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h140q58.33 0 99.17-40.76 40.83-40.77 40.83-99Q820-538 779.17-579q-40.84-41-99.17-41H540q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h140q83 0 141.5 58.5T880-480q0 83-58.5 141.5T680-280H540Z"/>`),
32
+ 'table_chart': svg(`<path d="M780-121H180q-24 0-42-18t-18-42v-599q0-24 18-42t42-18h600q24 0 42 18t18 42v599q0 24-18 42t-42 18ZM180-639h600v-141H180v141Zm130 60H180v398h130v-398Zm340 0v398h130v-398H650Zm-60 0H370v398h220v-398Z"/>`),
33
+ 'chevron_left': svg(`<path d="m406-481 177 177q9 9 8.5 21t-9.5 21q-9 9-21.5 9t-21.5-9L341-460q-5-5-7-10t-2-11q0-6 2-11t7-10l199-199q9-9 21.5-9t21.5 9q9 9 9 21.5t-9 21.5L406-481Z"/>`),
34
+ 'chevron_right': svg(`<path d="M530-481 353-658q-9-9-8.5-21t9.5-21q9-9 21.5-9t21.5 9l198 198q5 5 7 10t2 11q0 6-2 11t-7 10L396-261q-9 9-21 8.5t-21-9.5q-9-9-9-21.5t9-21.5l176-176Z"/>`),
35
+ 'delete': svg(`<path d="M261-120q-24.75 0-42.37-17.63Q201-155.25 201-180v-570h-11q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h158q0-13 8.63-21.5 8.62-8.5 21.37-8.5h204q12.75 0 21.38 8.62Q612-822.75 612-810h158q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5h-11v570q0 24.75-17.62 42.37Q723.75-120 699-120H261Zm438-630H261v570h438v-570ZM418.5-274.63q8.5-8.62 8.5-21.37v-339q0-12.75-8.68-21.38-8.67-8.62-21.5-8.62-12.82 0-21.32 8.62-8.5 8.63-8.5 21.38v339q0 12.75 8.68 21.37 8.67 8.63 21.5 8.63 12.82 0 21.32-8.63Zm166 0q8.5-8.62 8.5-21.37v-339q0-12.75-8.68-21.38-8.67-8.62-21.5-8.62-12.82 0-21.32 8.62-8.5 8.63-8.5 21.38v339q0 12.75 8.68 21.37 8.67 8.63 21.5 8.63 12.82 0 21.32-8.63ZM261-750v570-570Z"/>`),
36
+ 'remove': svg(`<path d="M230-450q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h500q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H230Z"/>`),
37
+ 'expand_less': svg(`<path d="M480-554 304-378q-9 9-21 8.5t-21-9.5q-9-9-9-21.5t9-21.5l197-197q9-9 21-9t21 9l198 198q9 9 9 21t-9 21q-9 9-21.5 9t-21.5-9L480-554Z"/>`),
38
+ 'expand_more': svg(`<path d="M469-358q-5-2-10-7L261-563q-9-9-8.5-21.5T262-606q9-9 21.5-9t21.5 9l175 176 176-176q9-9 21-8.5t21 9.5q9 9 9 21.5t-9 21.5L501-365q-5 5-10 7t-11 2q-6 0-11-2Z"/>`),
39
+ 'merge_type': svg(`<path d="M681.5-172.5Q669-172 660-181L463-378q-8-8-12.5-19.5T446-421v-303L345-623q-9 9-21.5 9t-21.5-9q-9-9-9-21.5t9-21.5l152-153q5-5 10.5-7t11.5-2q6 0 11 2t10 7l152 152q9 9 9 21.5t-9 21.5q-9 9-21.5 9.5T606-623L506-724v303l197 197q9 9 9 21t-9 21q-9 9-21.5 9.5ZM251-182q-9-9-9-21t9-21l119-120q9-9 21.5-9t21.5 9q9 9 9 21.5t-9 21.5L294-182q-9 9-21.5 9t-21.5-9Z"/>`),
40
+ 'call_split': svg(`<path d="M220-698v93q0 12.75-8.68 21.37-8.67 8.63-21.5 8.63-12.82 0-21.32-8.63-8.5-8.62-8.5-21.37v-165q0-12.75 8.63-21.38Q177.25-800 190-800h165q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5h-92l231 231q8 8 13 19.28 5 11.28 5 23.72v276q0 12.75-8.68 21.37-8.67 8.63-21.5 8.63-12.82 0-21.32-8.63-8.5-8.62-8.5-21.37v-276L220-698Zm520 0L586-545q-8.8 9-20.9 9-12.1 0-21.1-9-9-9-9-21.2 0-12.19 9-20.8l153-153h-92q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h165q12.75 0 21.38 8.62Q800-782.75 800-770v165q0 12.75-8.68 21.37-8.67 8.63-21.5 8.63-12.82 0-21.32-8.63-8.5-8.62-8.5-21.37v-93Z"/>`),
41
+ 'table_rows': svg(`<path d="M780-180v-157H180v157h600Zm0-217v-166H180v166h600Zm0-226v-157H180v157h600ZM180-120q-24 0-42-18t-18-42v-600q0-24 18-42t42-18h600q24 0 42 18t18 42v600q0 24-18 42t-42 18H180Z"/>`),
42
+ 'view_column': svg(`<path d="M120-260v-440q0-24.75 17.63-42.38Q155.25-760 180-760h600q24.75 0 42.38 17.62Q840-724.75 840-700v440q0 24.75-17.62 42.37Q804.75-200 780-200H180q-24.75 0-42.37-17.63Q120-235.25 120-260Zm60 0h160v-440H180v440Zm220 0h160v-440H400v440Zm220 0h160v-440H620v440Z"/>`),
43
+ 'keyboard_tab': svg(`<path d="M828.5-248.5Q820-257 820-270v-420q0-13 8.5-21.5T850-720q13 0 21.5 8.5T880-690v420q0 13-8.5 21.5T850-240q-13 0-21.5-8.5ZM606-450H110q-13 0-21.5-8.5T80-480q0-13 8.5-21.5T110-510h496L459-658q-9-9-9-21t9-21q9-9 21-9t21 9l199 199q5 5 7 10t2 11q0 6-2 11t-7 10L501-260q-9 9-21 9t-21-9q-9-9-9-21.5t9-21.5l147-147Z"/>`),
44
+ 'delete_sweep': svg(`<path d="M185-200q-24 0-42-18t-18-42v-415q-13 0-21.5-8.68-8.5-8.67-8.5-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h125v-15.14q0-12.86 8.63-21.36 8.62-8.5 21.37-8.5h75q12.75 0 21.38 8.62Q385-762.75 385-750v15h125q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5v415q0 24-18 42t-42 18H185Zm445-30q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h85q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5h-85Zm0-184q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h175q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H630Zm0-184q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h220q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H630Zm-445-77v415h265v-415H185Z"/>`),
45
+ 'format_align_left': svg(`<path d="M150-120q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm0-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h420q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm0-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm0-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h420q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm0-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Z"/>`),
46
+ 'format_align_center': svg(`<path d="M150-120q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm160-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h340q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H310ZM150-450q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm160-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h340q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H310ZM150-780q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Z"/>`),
47
+ 'format_align_right': svg(`<path d="M150-780q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm240 165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h420q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H390ZM150-450q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm240 165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h420q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H390ZM150-120q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Z"/>`),
48
+ 'format_align_justify': svg(`<path d="M150-120q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm0-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm0-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm0-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm0-165q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h660q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Z"/>`),
49
+ 'subscript': svg(`<path d="M799-160q-12.75 0-21.37-8.63Q769-177.25 769-190v-40q0-12.75 8.63-21.38Q786.25-260 799-260h91v-40H784q-6 0-10.5-4.5T769-315q0-6 4.5-10.5T784-330h106q12.75 0 21.38 8.62Q920-312.75 920-300v40q0 12.75-8.62 21.37Q902.75-230 890-230h-91v40h106q6 0 10.5 4.5T920-175q0 6-4.5 10.5T905-160H799Zm-499-80q-21.29 0-31.14-18.5Q259-277 270-295l150-236-136-213q-11-18-1.5-37t31.03-19q9.94 0 18.22 4.45T345-783l124 196 124-196q4.89-8.1 13.45-12.55 8.57-4.45 17.53-4.45 22.02 0 31.52 19t-1.5 37L518-531l150 236q11 18 .84 36.5-10.15 18.5-30.46 18.5-9.38 0-17.84-4.45Q612.08-248.9 607-257L469-474 331-257q-4.89 8.1-13.46 12.55Q308.97-240 300-240Z"/>`),
50
+ 'superscript': svg(`<path d="M799-630q-12.75 0-21.37-8.63Q769-647.25 769-660v-40q0-12.75 8.63-21.38Q786.25-730 799-730h91v-40H784q-6 0-10.5-4.5T769-785q0-6 4.5-10.5T784-800h106q12.75 0 21.38 8.62Q920-782.75 920-770v40q0 12.75-8.62 21.37Q902.75-700 890-700h-91v40h106q6 0 10.5 4.5T920-645q0 6-4.5 10.5T905-630H799ZM300-160q-21.29 0-31.14-18.5Q259-197 270-215l150-236-136-213q-11-18-1.5-37t31.03-19q9.94 0 18.22 4.45T345-703l124 196 124-196q4.89-8.1 13.45-12.55 8.57-4.45 17.53-4.45 22.02 0 31.52 19t-1.5 37L518-451l150 236q11 18 .84 36.5-10.15 18.5-30.46 18.5-9.38 0-17.84-4.45Q612.08-168.9 607-177L469-394 331-177q-4.89 8.1-13.46 12.55Q308.97-160 300-160Z"/>`),
51
+ 'undo': svg(`<path d="M289-200q-13 0-21.5-8.5T259-230q0-13 8.5-21.5T289-260h280q70 0 120.5-46.5T740-422q0-69-50.5-115.5T569-584H274l93 93q9 9 9 21t-9 21q-9 9-21 9t-21-9L181-593q-5-5-7-10t-2-11q0-6 2-11t7-10l144-144q9-9 21-9t21 9q9 9 9 21t-9 21l-93 93h294q95 0 163.5 64T800-422q0 94-68.5 158T568-200H289Z"/>`),
52
+ 'redo': svg(`<path d="M686-584H391q-70 0-120.5 46.5T220-422q0 69 50.5 115.5T391-260h280q13 0 21.5 8.5T701-230q0 13-8.5 21.5T671-200H392q-95 0-163.5-64T160-422q0-94 68.5-158T392-644h294l-93-93q-9-9-9-21t9-21q9-9 21-9t21 9l144 144q5 5 7 10t2 11q0 6-2 11t-7 10L635-449q-9 9-21 9t-21-9q-9-9-9-21t9-21l93-93Z"/>`),
53
+ 'info': svg(`<path d="M504.5-288.63q8.5-8.62 8.5-21.37v-180q0-12.75-8.68-21.38-8.67-8.62-21.5-8.62-12.82 0-21.32 8.62-8.5 8.63-8.5 21.38v180q0 12.75 8.68 21.37 8.67 8.63 21.5 8.63 12.82 0 21.32-8.63Zm-1-314.57q9.5-9.2 9.5-22.8 0-14.45-9.48-24.22-9.48-9.78-23.5-9.78t-23.52 9.78Q447-640.45 447-626q0 13.6 9.48 22.8 9.48 9.2 23.5 9.2t23.52-9.2ZM480.27-80q-82.74 0-155.5-31.5Q252-143 197.5-197.5t-86-127.34Q80-397.68 80-480.5t31.5-155.66Q143-709 197.5-763t127.34-85.5Q397.68-880 480.5-880t155.66 31.5Q709-817 763-763t85.5 127Q880-563 880-480.27q0 82.74-31.5 155.5Q817-252 763-197.68q-54 54.31-127 86Q563-80 480.27-80Zm.23-60Q622-140 721-239.5t99-241Q820-622 721.19-721T480-820q-141 0-240.5 98.81T140-480q0 141 99.5 240.5t241 99.5Zm-.5-340Z"/>`),
54
+ 'notifications': svg(`<path d="M190-200q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h50v-304q0-84 49.5-150.5T420-798v-22q0-25 17.5-42.5T480-880q25 0 42.5 17.5T540-820v22q81 17 130.5 83.5T720-564v304h50q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H190Zm290-302Zm0 422q-33 0-56.5-23.5T400-160h160q0 33-23.5 56.5T480-80ZM300-260h360v-304q0-75-52.5-127.5T480-744q-75 0-127.5 52.5T300-564v304Z"/>`),
55
+ 'arrow_back': svg(`<path d="m274-450 227 227q9 9 9 21t-9 21q-9 9-21 9t-21-9L181-459q-5-5-7-10t-2-11q0-6 2-11t7-10l278-278q9-9 21-9t21 9q9 9 9 21t-9 21L274-510h496q13 0 21.5 8.5T800-480q0 13-8.5 21.5T770-450H274Z"/>`),
56
+ 'menu_book': svg(`<path d="M248-300q53.57 0 104.28 12.5Q403-275 452-250v-427q-45-30-97.62-46.5Q301.76-740 248-740q-38 0-74.5 9.5T100-707v434q31-14 70.5-20.5T248-300Zm264 50q50-25 98-37.5T712-300q38 0 78.5 6t69.5 16v-429q-34-17-71.82-25-37.82-8-76.18-8-54 0-104.5 16.5T512-677v427Zm-44.5 77.5Q461-174 456-178q-47-29-99.85-45-52.84-16-108.15-16-36.54 0-71.77 9T106-208q-23.1 11-44.55-3Q40-225 40-251v-463q0-15 7-27.5T68-761q42-20 87.39-29.5 45.4-9.5 92.61-9.5 63 0 122.5 17T482-731q51-35 109.5-52T712-800q46.87 0 91.93 9.5Q849-781 891-761q14 7 21.5 19.5T920-714v463q0 27.89-22.5 42.45Q875-194 853-208q-34-14-69.23-22.5Q748.54-239 712-239q-54.27 0-106.14 16Q554-207 508-178q-5 4-11.5 5.5T482-171q-8 0-14.5-1.5ZM276-489Zm284-119q0-5.52 4.02-11.34 4.03-5.82 8.98-7.66 30-11 61.34-17 31.33-6 65.91-6 21.61 0 42.68 2.5T784-640q6 2 11 7.4 5 5.39 5 12.13 0 11.47-7.5 17.47-7.5 6-18.5 3-16.8-5-35.4-7.5Q720-610 700-610q-29 0-56 5.5T591-588q-14 5-22.5-.5T560-608Zm0 220q0-5.81 4.02-11.94 4.03-6.12 8.98-8.06 30-11 61.34-16.5 31.33-5.5 65.91-5.5 21.61 0 42.68 2.5T784-420q6 2 11 7.4 5 5.39 5 12.13 0 11.47-7.5 17.47-7.5 6-18.5 3-16.8-5-35.4-7.5Q720-390 700-390q-29 0-56 5t-53 16q-14 5-22.5 0t-8.5-19Zm0-110q0-5.52 4.02-11.34 4.03-5.82 8.98-7.66 30-11 61.34-17 31.33-6 65.91-6 21.61 0 42.68 2.5T784-530q6 2 11 7.4 5 5.39 5 12.13 0 11.47-7.5 17.47-7.5 6-18.5 3-16.8-5-35.4-7.5Q720-500 700-500q-29 0-56 5.5T591-478q-14 5-22.5-.5T560-498Z"/>`),
57
+ 'tune': svg(`<path d="M435.5-128.63Q427-137.25 427-150v-165q0-12.75 8.68-21.38 8.67-8.62 21.5-8.62 12.82 0 21.32 8.62 8.5 8.63 8.5 21.38v53h323q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H487v52q0 12.75-8.68 21.37-8.67 8.63-21.5 8.63-12.82 0-21.32-8.63ZM150-202q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h187q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm165.5-174.63Q307-385.25 307-398v-52H150q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h157v-54q0-12.75 8.68-21.38 8.67-8.62 21.5-8.62 12.82 0 21.32 8.62 8.5 8.63 8.5 21.38v166q0 12.75-8.68 21.37-8.67 8.63-21.5 8.63-12.82 0-21.32-8.63ZM457-450q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h353q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H457Zm144.5-173.63Q593-632.25 593-645v-165q0-12.75 8.68-21.38 8.67-8.62 21.5-8.62 12.82 0 21.32 8.62 8.5 8.63 8.5 21.38v52h157q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H653v53q0 12.75-8.68 21.37-8.67 8.63-21.5 8.63-12.82 0-21.32-8.63ZM150-698q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h353q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Z"/>`),
58
+ 'toc': svg(`<path d="M150-280q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h490q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm0-170q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h490q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm0-170q-12.75 0-21.37-8.68-8.63-8.67-8.63-21.5 0-12.82 8.63-21.32 8.62-8.5 21.37-8.5h490q12.75 0 21.38 8.68 8.62 8.67 8.62 21.5 0 12.82-8.62 21.32-8.63 8.5-21.38 8.5H150Zm659.5 340q-12.5 0-21-8.63-8.5-8.62-8.5-21.37 0-12 8.63-21 8.62-9 21.37-9 12 0 21 9t9 21.5q0 12.5-9 21t-21.5 8.5Zm0-170q-12.5 0-21-8.63-8.5-8.62-8.5-21.37 0-12 8.63-21 8.62-9 21.37-9 12 0 21 9t9 21.5q0 12.5-9 21t-21.5 8.5Zm0-170q-12.5 0-21-8.63-8.5-8.62-8.5-21.37 0-12 8.63-21 8.62-9 21.37-9 12 0 21 9t9 21.5q0 12.5-9 21t-21.5 8.5Z"/>`),
59
+ 'toggle_on': svg(`<path d="M280-240q-100 0-170-70T40-480q0-100 70-170t170-70h400q100 0 170 70t70 170q0 100-70 170t-170 70H280Zm0-60h400q75 0 127.5-52.5T860-480q0-75-52.5-127.5T680-660H280q-75 0-127.5 52.5T100-480q0 75 52.5 127.5T280-300Zm472.5-108.5Q782-438 782-480t-29.5-71.5Q723-581 681-581t-71.5 29.5Q580-522 580-480t29.5 71.5Q639-379 681-379t71.5-29.5ZM480-480Z"/>`),
60
+ };