@sciflow/editor-start 0.1.0 → 0.1.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.
- package/dist/bundle/sciflow-editor.css +1 -1
- package/dist/bundle/sciflow-editor.js +4201 -3973
- package/dist/lib/format-bar.d.ts +7 -0
- package/dist/lib/format-bar.d.ts.map +1 -1
- package/dist/lib/format-bar.js +16 -2
- package/dist/lib/selection-editor.d.ts +103 -2
- package/dist/lib/selection-editor.d.ts.map +1 -1
- package/dist/lib/selection-editor.js +222 -13
- package/package.json +7 -7
- package/dist/lib/default-features.d.ts +0 -21
- package/dist/lib/default-features.d.ts.map +0 -1
- package/dist/lib/default-features.js +0 -34
- package/dist/lib/test-fixtures/soak-documents.d.ts +0 -13
- package/dist/lib/test-fixtures/soak-documents.d.ts.map +0 -1
- package/dist/lib/test-fixtures/soak-documents.js +0 -69
package/dist/lib/format-bar.d.ts
CHANGED
|
@@ -75,6 +75,13 @@ export declare class SciFlowFormatBarElement extends LitElement {
|
|
|
75
75
|
private selectStyle;
|
|
76
76
|
private isBlockquoteActive;
|
|
77
77
|
private canUseCommand;
|
|
78
|
+
/**
|
|
79
|
+
* The trigger shows the localised "Insert" label next to the `+` icon: the
|
|
80
|
+
* word only appeared on the menu items before, leaving sighted users with a
|
|
81
|
+
* bare glyph. The visible label and `aria-label` are read from the same key,
|
|
82
|
+
* so they cannot diverge — and because `aria-label` replaces a button's text
|
|
83
|
+
* content for assistive tech, the word is still announced exactly once.
|
|
84
|
+
*/
|
|
78
85
|
private renderInsertDropdown;
|
|
79
86
|
private toggleInsertMenu;
|
|
80
87
|
private executeInsertCommand;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-bar.d.ts","sourceRoot":"","sources":["../../src/lib/format-bar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAa,UAAU,EAAsB,MAAM,KAAK,CAAC;AAOhE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAoChE;;;;;;;;;;;GAWG;AACH,qBACa,uBAAwB,SAAQ,UAAU;IACrD,oFAAoF;IAEpF,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAQ;IAE3C,gEAAgE;IAEhE,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;;;;;;OAQG;IAEH,YAAY,SAAM;IAGlB,OAAO,CAAC,gBAAgB,CAAK;IAG7B,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,cAAc,CAAS;IAE/B,OAAO,CAAC,cAAc,CAAqC;IAC3D,OAAO,CAAC,iBAAiB,CAAC,CAAgB;IAC1C,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,cAAc,CAAC,CAAgB;IACvC,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,kBAAkB,CAA0B;IACpD,OAAO,CAAC,mBAAmB,CAAC,CAA0B;IAEtD,OAAgB,MAAM,0BAAmC;IAEzD,uEAAuE;IAC9D,iBAAiB,IAAI,IAAI;IAoBlC,qDAAqD;IAC5C,oBAAoB,IAAI,IAAI;IAYrC;;;OAGG;cACgB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAMjF;;;OAGG;cACgB,MAAM;IAwCzB,OAAO,CAAC,mBAAmB;IAyD3B,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,oBAAoB;
|
|
1
|
+
{"version":3,"file":"format-bar.d.ts","sourceRoot":"","sources":["../../src/lib/format-bar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAa,UAAU,EAAsB,MAAM,KAAK,CAAC;AAOhE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAoChE;;;;;;;;;;;GAWG;AACH,qBACa,uBAAwB,SAAQ,UAAU;IACrD,oFAAoF;IAEpF,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAQ;IAE3C,gEAAgE;IAEhE,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;;;;;;OAQG;IAEH,YAAY,SAAM;IAGlB,OAAO,CAAC,gBAAgB,CAAK;IAG7B,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,cAAc,CAAS;IAE/B,OAAO,CAAC,cAAc,CAAqC;IAC3D,OAAO,CAAC,iBAAiB,CAAC,CAAgB;IAC1C,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,cAAc,CAAC,CAAgB;IACvC,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,kBAAkB,CAA0B;IACpD,OAAO,CAAC,mBAAmB,CAAC,CAA0B;IAEtD,OAAgB,MAAM,0BAAmC;IAEzD,uEAAuE;IAC9D,iBAAiB,IAAI,IAAI;IAoBlC,qDAAqD;IAC5C,oBAAoB,IAAI,IAAI;IAYrC;;;OAGG;cACgB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAMjF;;;OAGG;cACgB,MAAM;IAwCzB,OAAO,CAAC,mBAAmB;IAyD3B,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,aAAa;IAUrB;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;IA8D5B,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,0BAA0B;IAkBlC,OAAO,CAAC,yBAAyB;IAWjC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA2ChC;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IA+B9B,OAAO,CAAC,aAAa;IAkBrB,+DAA+D;IAC/D,OAAO,CAAC,qBAAqB;IAc7B,2DAA2D;IAC3D,OAAO,CAAC,gBAAgB;IAQxB,8EAA8E;IAC9E,OAAO,CAAC,gBAAgB;IA6BxB,mDAAmD;IACnD,OAAO,CAAC,kBAAkB;IA6B1B,yEAAyE;IACzE,OAAO,CAAC,mBAAmB;IA6C3B,gFAAgF;IAChF,OAAO,CAAC,iBAAiB;IAmBzB,wEAAwE;IACxE,OAAO,CAAC,kBAAkB;IAmB1B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAkB5B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAoB1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAuC3B;;;OAGG;IACH,OAAO,KAAK,cAAc,GAOzB;IAED;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IAY1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;CA2BzB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,mBAAmB,EAAE,uBAAuB,CAAC;KAC9C;CACF"}
|
package/dist/lib/format-bar.js
CHANGED
|
@@ -265,6 +265,13 @@ let SciFlowFormatBarElement = class SciFlowFormatBarElement extends LitElement {
|
|
|
265
265
|
/* --------------------------------------------------------- */
|
|
266
266
|
/* Insert dropdown (icon + chevron → popover) */
|
|
267
267
|
/* --------------------------------------------------------- */
|
|
268
|
+
/**
|
|
269
|
+
* The trigger shows the localised "Insert" label next to the `+` icon: the
|
|
270
|
+
* word only appeared on the menu items before, leaving sighted users with a
|
|
271
|
+
* bare glyph. The visible label and `aria-label` are read from the same key,
|
|
272
|
+
* so they cannot diverge — and because `aria-label` replaces a button's text
|
|
273
|
+
* content for assistive tech, the word is still announced exactly once.
|
|
274
|
+
*/
|
|
268
275
|
renderInsertDropdown(runner, metadata) {
|
|
269
276
|
const availability = runner?.available?.() ?? null;
|
|
270
277
|
// Collect block-level insert commands (media group + table insert + footnote)
|
|
@@ -285,25 +292,32 @@ let SciFlowFormatBarElement = class SciFlowFormatBarElement extends LitElement {
|
|
|
285
292
|
@click=${this.toggleInsertMenu}
|
|
286
293
|
>
|
|
287
294
|
<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>
|
|
295
|
+
<span class="insert-dropdown-label">${t('formatBar.insert')}</span>
|
|
288
296
|
<span class="dropdown-chevron">${chevronSvg}</span>
|
|
289
297
|
</button>
|
|
290
298
|
${this.insertMenuOpen
|
|
291
299
|
? html `
|
|
292
|
-
<div class="insert-dropdown-menu" role="menu">
|
|
300
|
+
<div class="insert-dropdown-menu" role="menu" aria-label=${t('formatBar.insert')}>
|
|
293
301
|
${insertCommands.map(([name, meta]) => {
|
|
294
302
|
const canExecute = availability?.[name] ? availability[name]() : true;
|
|
303
|
+
// The trigger above already says "Insert", so an item shows its
|
|
304
|
+
// short wording when the command offers one ("Figure", not
|
|
305
|
+
// "Insert figure"). The full label stays the accessible name,
|
|
306
|
+
// so nothing is lost for a screen-reader user.
|
|
307
|
+
const fullLabel = meta?.label ?? name;
|
|
295
308
|
return html `
|
|
296
309
|
<button
|
|
297
310
|
type="button"
|
|
298
311
|
role="menuitem"
|
|
299
312
|
class="insert-menu-item"
|
|
313
|
+
aria-label=${fullLabel}
|
|
300
314
|
?disabled=${!canExecute}
|
|
301
315
|
@click=${() => this.executeInsertCommand(name, runner)}
|
|
302
316
|
>
|
|
303
317
|
${meta?.icon && materialIcons[meta.icon]
|
|
304
318
|
? html `<span class="cmd-icon" aria-hidden="true">${unsafeHTML(materialIcons[meta.icon])}</span>`
|
|
305
319
|
: nothing}
|
|
306
|
-
${meta?.
|
|
320
|
+
${meta?.shortLabel ?? fullLabel}
|
|
307
321
|
</button>
|
|
308
322
|
`;
|
|
309
323
|
})}
|
|
@@ -96,9 +96,39 @@ export declare class SciFlowSelectionEditorElement extends LitElement {
|
|
|
96
96
|
/** Convenience getter mirroring `editor.getCommands()`. */
|
|
97
97
|
private getCommandRunner;
|
|
98
98
|
/**
|
|
99
|
-
*
|
|
99
|
+
* Whether the bound editor currently accepts edits. Every path in this panel
|
|
100
|
+
* that writes to the document asks first, and fails closed when there is no
|
|
101
|
+
* view to ask.
|
|
102
|
+
*
|
|
103
|
+
* A host can turn editing off at any time
|
|
104
|
+
* (`view.setProps({ editable: () => false })`) and expects the whole surface
|
|
105
|
+
* to go quiet — a side panel that still writes would make a read-only
|
|
106
|
+
* document change under the reader. `view.editable` is the same signal the
|
|
107
|
+
* editor's own key and click handling reads, so the panel cannot drift from
|
|
108
|
+
* the editing surface. The guards sit on the write itself rather than only on
|
|
109
|
+
* the controls: the panel re-renders on selection changes, so editability can
|
|
110
|
+
* flip while a button from the editable render is still on screen.
|
|
100
111
|
*/
|
|
112
|
+
private isEditorEditable;
|
|
113
|
+
/**
|
|
114
|
+
* The panel's own name for a node type in the active locale, or `null` when it
|
|
115
|
+
* has none. Cased for use inside a sentence, so callers that need a heading
|
|
116
|
+
* capitalise it themselves — English writes these names lower case, German
|
|
117
|
+
* capitalises every noun, and only the dictionary knows which.
|
|
118
|
+
*/
|
|
119
|
+
private getNodeTypeName;
|
|
101
120
|
private getNodeTypeLabel;
|
|
121
|
+
/**
|
|
122
|
+
* The node this panel is currently describing, read back from the live
|
|
123
|
+
* document, or `null` when the panel is describing a mark or nothing at all.
|
|
124
|
+
*/
|
|
125
|
+
private getSelectedNode;
|
|
126
|
+
/**
|
|
127
|
+
* A table is a `figure` whose first child is a `table` rather than an image,
|
|
128
|
+
* so the content is what tells a reader which one is selected — the node type
|
|
129
|
+
* is `figure` either way.
|
|
130
|
+
*/
|
|
131
|
+
private isTableFigure;
|
|
102
132
|
private renderAttributeField;
|
|
103
133
|
/**
|
|
104
134
|
* Convert a raw attribute name like 'text-align' to a human-readable label.
|
|
@@ -119,7 +149,18 @@ export declare class SciFlowSelectionEditorElement extends LitElement {
|
|
|
119
149
|
private unmountCitationSourceAdapter;
|
|
120
150
|
private handleDraftAttrChange;
|
|
121
151
|
/**
|
|
122
|
-
* Render the figure sidebar: type,
|
|
152
|
+
* Render the figure sidebar: type, image address, alt text.
|
|
153
|
+
*
|
|
154
|
+
* The image address is editable here because this panel is the only place a
|
|
155
|
+
* reader can change it: clicking an image selects the figure rather than
|
|
156
|
+
* opening a picker, so the address has to be reachable as an ordinary field.
|
|
157
|
+
* It is a plain text input, so a URL, a relative path or a data URI are all
|
|
158
|
+
* accepted; the value is written on `change` like every other draft attribute
|
|
159
|
+
* and committed with Apply.
|
|
160
|
+
*
|
|
161
|
+
* A table is a `figure` too, so the heading and the form's accessible name
|
|
162
|
+
* both come from the selected node rather than from the node type alone —
|
|
163
|
+
* otherwise a selected table would be titled "Figure".
|
|
123
164
|
*/
|
|
124
165
|
private renderFigureSidebar;
|
|
125
166
|
/**
|
|
@@ -155,8 +196,68 @@ export declare class SciFlowSelectionEditorElement extends LitElement {
|
|
|
155
196
|
private setDraftAttr;
|
|
156
197
|
private applyDraftAttrs;
|
|
157
198
|
private revertDraftAttrs;
|
|
199
|
+
/**
|
|
200
|
+
* Apply/Revert for pending attribute edits.
|
|
201
|
+
*
|
|
202
|
+
* Withheld when the editor is not editable: `applyDraftAttrs` refuses to write
|
|
203
|
+
* either way — that guard is the barrier — but offering an Apply that cannot
|
|
204
|
+
* act reads as a broken control rather than a read-only document.
|
|
205
|
+
*/
|
|
158
206
|
private renderDraftActions;
|
|
207
|
+
/**
|
|
208
|
+
* Controls that act on the selection itself rather than on its attributes.
|
|
209
|
+
*
|
|
210
|
+
* The delete action sits here, at the top of the panel, deliberately far from
|
|
211
|
+
* the Apply/Update primary at the bottom: a destructive verb never shares a
|
|
212
|
+
* region with the action a user is aiming for. It is a plain `<button>` with
|
|
213
|
+
* visible text, so it is keyboard reachable and its accessible name is the
|
|
214
|
+
* label itself. Removal is undoable through editor history, so it commits
|
|
215
|
+
* straight away rather than opening a confirmation step.
|
|
216
|
+
*/
|
|
159
217
|
private renderSelectionControls;
|
|
218
|
+
/**
|
|
219
|
+
* The node the delete action would remove, or `null` when there is none the
|
|
220
|
+
* panel may act on.
|
|
221
|
+
*
|
|
222
|
+
* Only a `NodeSelection` qualifies: a text range inside a caption or paragraph
|
|
223
|
+
* belongs to the editing surface, not to a panel-level "delete this element"
|
|
224
|
+
* verb. `deleteSelection` is probed with `dispatch` omitted, so the check
|
|
225
|
+
* cannot edit the document.
|
|
226
|
+
*
|
|
227
|
+
* A view that is not editable never yields a target. A host can turn editing
|
|
228
|
+
* off at any time (`view.setProps({ editable: () => false })`) and expects the
|
|
229
|
+
* whole surface to go quiet — a side panel that still removes content would
|
|
230
|
+
* make a read-only document lose it. `view.editable` is the same signal the
|
|
231
|
+
* editor's own key and click handling reads, so the panel cannot drift from
|
|
232
|
+
* the editing surface.
|
|
233
|
+
*
|
|
234
|
+
* Withheld while the panel is pinned, and that is a limitation rather than a
|
|
235
|
+
* design goal. A pinned panel stops following the editor selection and
|
|
236
|
+
* therefore stops re-rendering, while `elementInfo.position` is never remapped
|
|
237
|
+
* through the transaction mapping — so every pinned control already acts on a
|
|
238
|
+
* position that may have drifted, and the attribute fields cope only because
|
|
239
|
+
* they re-check the node type at that position before writing. Removal has no
|
|
240
|
+
* such recheck available: after one deletion the frozen panel would still show
|
|
241
|
+
* the control, now aimed at whatever moved into that position. Making the pin
|
|
242
|
+
* remap its stored position on every document change would fix this for all
|
|
243
|
+
* pinned controls at once; until then the destructive one stays out.
|
|
244
|
+
*/
|
|
245
|
+
private getDeletableNode;
|
|
246
|
+
/**
|
|
247
|
+
* Label for the delete action, naming what is about to be removed. The visible
|
|
248
|
+
* text is the button's accessible name, so naming the node here keeps the two
|
|
249
|
+
* identical — no `aria-label` to drift from what is on screen. Anything the
|
|
250
|
+
* panel has no name for falls back to the generic wording.
|
|
251
|
+
*/
|
|
252
|
+
private getDeleteLabel;
|
|
253
|
+
/**
|
|
254
|
+
* Remove the selected node through ProseMirror's own deletion command, so the
|
|
255
|
+
* change is a normal transaction and stays undoable and collab-safe. The state
|
|
256
|
+
* is read from the view at call time rather than reused from render, and the
|
|
257
|
+
* editability guard is repeated here: a host can switch the view to read-only
|
|
258
|
+
* after the button was rendered, and the rendered panel would not know.
|
|
259
|
+
*/
|
|
260
|
+
private deleteSelectedNode;
|
|
160
261
|
private togglePinSelection;
|
|
161
262
|
private handleGenericKeydown;
|
|
162
263
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection-editor.d.ts","sourceRoot":"","sources":["../../src/lib/selection-editor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAa,UAAU,
|
|
1
|
+
{"version":3,"file":"selection-editor.d.ts","sourceRoot":"","sources":["../../src/lib/selection-editor.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - Build reactive UI that stays in sync with editor state
|
|
14
14
|
*/
|
|
15
15
|
import { __decorate } from "tslib";
|
|
16
|
-
import { css, html, LitElement, unsafeCSS } from 'lit';
|
|
16
|
+
import { css, html, LitElement, nothing, unsafeCSS } from 'lit';
|
|
17
17
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
18
18
|
import { ref } from 'lit/directives/ref.js';
|
|
19
19
|
import { SourceField, t, LOCALE_CHANGE_EVENT } from '@sciflow/editor-core';
|
|
@@ -22,7 +22,7 @@ import { renderTexToSvg } from '@sciflow/editor-core/features/math';
|
|
|
22
22
|
import { Fragment as PMFragment } from 'prosemirror-model';
|
|
23
23
|
import { EditorState, NodeSelection } from 'prosemirror-state';
|
|
24
24
|
import { EditorView } from 'prosemirror-view';
|
|
25
|
-
import { baseKeymap, toggleMark } from 'prosemirror-commands';
|
|
25
|
+
import { baseKeymap, deleteSelection, toggleMark } from 'prosemirror-commands';
|
|
26
26
|
import { keymap } from 'prosemirror-keymap';
|
|
27
27
|
import { getCitationSourceAdapter, } from './citation-source-adapter.js';
|
|
28
28
|
import { collectDocumentOutline } from './outline.js';
|
|
@@ -30,6 +30,14 @@ import { renderNodeFromUiSchema } from './selection-ui-renderer.js';
|
|
|
30
30
|
import './selection-widget-host.js';
|
|
31
31
|
import selectionEditorCss from './selection-editor.css?inline';
|
|
32
32
|
import { applyThemeStylesToRoot, subscribeToSciFlowTheme } from './theme.js';
|
|
33
|
+
/**
|
|
34
|
+
* Attributes whose value is always text. Anything outside this set that looks
|
|
35
|
+
* like a number is coerced to one before it is written to the node, so a text
|
|
36
|
+
* attribute left out here silently changes type — an image address of `1200`
|
|
37
|
+
* or `3.14` would reach the document as a number and fail schema validation on
|
|
38
|
+
* export. Add a new text attribute here in the same commit that renders it.
|
|
39
|
+
*/
|
|
40
|
+
const TEXT_VALUED_ATTRS = new Set(['alt', 'src']);
|
|
33
41
|
/**
|
|
34
42
|
* Custom element that displays and edits the selected element's attributes.
|
|
35
43
|
*
|
|
@@ -717,11 +725,32 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
717
725
|
return this.resolvedEditor?.commands ?? null;
|
|
718
726
|
}
|
|
719
727
|
/**
|
|
720
|
-
*
|
|
728
|
+
* Whether the bound editor currently accepts edits. Every path in this panel
|
|
729
|
+
* that writes to the document asks first, and fails closed when there is no
|
|
730
|
+
* view to ask.
|
|
731
|
+
*
|
|
732
|
+
* A host can turn editing off at any time
|
|
733
|
+
* (`view.setProps({ editable: () => false })`) and expects the whole surface
|
|
734
|
+
* to go quiet — a side panel that still writes would make a read-only
|
|
735
|
+
* document change under the reader. `view.editable` is the same signal the
|
|
736
|
+
* editor's own key and click handling reads, so the panel cannot drift from
|
|
737
|
+
* the editing surface. The guards sit on the write itself rather than only on
|
|
738
|
+
* the controls: the panel re-renders on selection changes, so editability can
|
|
739
|
+
* flip while a button from the editable render is still on screen.
|
|
721
740
|
*/
|
|
722
|
-
|
|
741
|
+
isEditorEditable() {
|
|
742
|
+
return this.resolvedEditor?.editorView?.editable === true;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* The panel's own name for a node type in the active locale, or `null` when it
|
|
746
|
+
* has none. Cased for use inside a sentence, so callers that need a heading
|
|
747
|
+
* capitalise it themselves — English writes these names lower case, German
|
|
748
|
+
* capitalises every noun, and only the dictionary knows which.
|
|
749
|
+
*/
|
|
750
|
+
getNodeTypeName(type) {
|
|
723
751
|
const known = {
|
|
724
752
|
figure: () => t('selectionEditor.figure'),
|
|
753
|
+
table: () => t('selectionEditor.table'),
|
|
725
754
|
math: () => t('selectionEditor.math'),
|
|
726
755
|
citation: () => t('selectionEditor.citation'),
|
|
727
756
|
anchor: () => t('selectionEditor.hyperlink'),
|
|
@@ -729,9 +758,33 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
729
758
|
heading: () => t('selectionEditor.heading'),
|
|
730
759
|
paragraph: () => t('selectionEditor.paragraph'),
|
|
731
760
|
};
|
|
732
|
-
|
|
761
|
+
return known[type]?.() ?? null;
|
|
762
|
+
}
|
|
763
|
+
getNodeTypeLabel(type) {
|
|
764
|
+
const label = this.getNodeTypeName(type) ?? type.replace(/_/g, ' ');
|
|
733
765
|
return label.replace(/^\w/, (m) => m.toUpperCase());
|
|
734
766
|
}
|
|
767
|
+
/**
|
|
768
|
+
* The node this panel is currently describing, read back from the live
|
|
769
|
+
* document, or `null` when the panel is describing a mark or nothing at all.
|
|
770
|
+
*/
|
|
771
|
+
getSelectedNode() {
|
|
772
|
+
const view = this.resolvedEditor?.editorView;
|
|
773
|
+
const position = this.elementInfo?.position;
|
|
774
|
+
if (!view || position == null || position < 0 || position >= view.state.doc.content.size) {
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
return view.state.doc.nodeAt(position);
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* A table is a `figure` whose first child is a `table` rather than an image,
|
|
781
|
+
* so the content is what tells a reader which one is selected — the node type
|
|
782
|
+
* is `figure` either way.
|
|
783
|
+
*/
|
|
784
|
+
isTableFigure(node) {
|
|
785
|
+
const tableType = this.resolvedEditor?.editorView?.state.schema.nodes['table'];
|
|
786
|
+
return !!tableType && node?.firstChild?.type === tableType;
|
|
787
|
+
}
|
|
735
788
|
renderAttributeField(attrName, attrValue) {
|
|
736
789
|
const decodedValue = this.decodeSpecialAttribute(attrName, attrValue);
|
|
737
790
|
const renderedValue = decodedValue === null || decodedValue === undefined ? '' : String(decodedValue);
|
|
@@ -852,7 +905,7 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
852
905
|
if (attrName === 'source') {
|
|
853
906
|
parsedValue = this.encodeSourceField(value);
|
|
854
907
|
}
|
|
855
|
-
else if (attrName
|
|
908
|
+
else if (TEXT_VALUED_ATTRS.has(attrName)) {
|
|
856
909
|
parsedValue = value;
|
|
857
910
|
}
|
|
858
911
|
else if (value !== '') {
|
|
@@ -870,15 +923,50 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
870
923
|
this.setDraftAttr(attrName, parsedValue);
|
|
871
924
|
}
|
|
872
925
|
/**
|
|
873
|
-
* Render the figure sidebar: type,
|
|
926
|
+
* Render the figure sidebar: type, image address, alt text.
|
|
927
|
+
*
|
|
928
|
+
* The image address is editable here because this panel is the only place a
|
|
929
|
+
* reader can change it: clicking an image selects the figure rather than
|
|
930
|
+
* opening a picker, so the address has to be reachable as an ordinary field.
|
|
931
|
+
* It is a plain text input, so a URL, a relative path or a data URI are all
|
|
932
|
+
* accepted; the value is written on `change` like every other draft attribute
|
|
933
|
+
* and committed with Apply.
|
|
934
|
+
*
|
|
935
|
+
* A table is a `figure` too, so the heading and the form's accessible name
|
|
936
|
+
* both come from the selected node rather than from the node type alone —
|
|
937
|
+
* otherwise a selected table would be titled "Figure".
|
|
874
938
|
*/
|
|
875
939
|
renderFigureSidebar() {
|
|
876
940
|
const attrs = this.getEffectiveAttrs();
|
|
877
941
|
const alt = typeof attrs.alt === 'string' ? attrs.alt : '';
|
|
942
|
+
const src = typeof attrs.src === 'string' ? attrs.src : '';
|
|
943
|
+
const isTable = this.isTableFigure(this.getSelectedNode());
|
|
878
944
|
return html `
|
|
879
|
-
<div
|
|
945
|
+
<div
|
|
946
|
+
class="selection-editor-container selection-editor-figure"
|
|
947
|
+
role="form"
|
|
948
|
+
aria-label=${isTable ? t('selectionEditor.editTable') : t('selectionEditor.editFigure')}
|
|
949
|
+
@keydown=${this.handleGenericKeydown}
|
|
950
|
+
>
|
|
880
951
|
${this.renderSelectionControls()}
|
|
881
|
-
<p class="selection-editor-type-header"
|
|
952
|
+
<p class="selection-editor-type-header">
|
|
953
|
+
${isTable ? this.getNodeTypeLabel('table') : this.getNodeTypeLabel(this.elementInfo.type)}
|
|
954
|
+
</p>
|
|
955
|
+
${isTable
|
|
956
|
+
? nothing
|
|
957
|
+
: html `
|
|
958
|
+
<div class="selection-editor-field">
|
|
959
|
+
<label class="selection-editor-label" for="figure-src">${t('selectionEditor.imageUrl')}</label>
|
|
960
|
+
<input
|
|
961
|
+
type="text"
|
|
962
|
+
id="figure-src"
|
|
963
|
+
class="selection-editor-input"
|
|
964
|
+
.value=${src}
|
|
965
|
+
placeholder=${t('selectionEditor.urlPlaceholder')}
|
|
966
|
+
@change=${(e) => this.handleDraftAttrChange(e, 'src')}
|
|
967
|
+
/>
|
|
968
|
+
</div>
|
|
969
|
+
`}
|
|
882
970
|
<div class="selection-editor-field">
|
|
883
971
|
<label class="selection-editor-label" for="figure-alt">${t('selectionEditor.altText')}</label>
|
|
884
972
|
<input
|
|
@@ -886,7 +974,9 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
886
974
|
id="figure-alt"
|
|
887
975
|
class="selection-editor-input"
|
|
888
976
|
.value=${alt}
|
|
889
|
-
placeholder=${
|
|
977
|
+
placeholder=${isTable
|
|
978
|
+
? t('selectionEditor.tableAltTextPlaceholder')
|
|
979
|
+
: t('selectionEditor.altTextPlaceholder')}
|
|
890
980
|
@change=${(e) => this.handleDraftAttrChange(e, 'alt')}
|
|
891
981
|
/>
|
|
892
982
|
</div>
|
|
@@ -1040,6 +1130,9 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
1040
1130
|
});
|
|
1041
1131
|
}
|
|
1042
1132
|
applyMathDraft() {
|
|
1133
|
+
if (!this.isEditorEditable()) {
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1043
1136
|
if (!this.elementInfo || this.elementInfo.type !== 'math' || this.elementInfo.position === null || !this.mathDraft) {
|
|
1044
1137
|
return;
|
|
1045
1138
|
}
|
|
@@ -1140,6 +1233,9 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
1140
1233
|
`;
|
|
1141
1234
|
}
|
|
1142
1235
|
applyCitationDraft() {
|
|
1236
|
+
if (!this.isEditorEditable()) {
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1143
1239
|
if (!this.elementInfo ||
|
|
1144
1240
|
this.elementInfo.type !== 'citation' ||
|
|
1145
1241
|
this.elementInfo.position === null ||
|
|
@@ -1233,6 +1329,9 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
1233
1329
|
`;
|
|
1234
1330
|
}
|
|
1235
1331
|
applyCrossReferenceDraft() {
|
|
1332
|
+
if (!this.isEditorEditable()) {
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1236
1335
|
if (!this.elementInfo ||
|
|
1237
1336
|
this.elementInfo.type !== 'link' ||
|
|
1238
1337
|
this.elementInfo.position === null ||
|
|
@@ -1309,7 +1408,7 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
1309
1408
|
}
|
|
1310
1409
|
applyHyperlinkDraft() {
|
|
1311
1410
|
const view = this.resolvedEditor?.editorView;
|
|
1312
|
-
if (!view || !this.hyperlinkDraft)
|
|
1411
|
+
if (!view || !view.editable || !this.hyperlinkDraft)
|
|
1313
1412
|
return;
|
|
1314
1413
|
const { state } = view;
|
|
1315
1414
|
const anchorMarkType = state.schema.marks['anchor'];
|
|
@@ -1345,7 +1444,7 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
1345
1444
|
}
|
|
1346
1445
|
removeHyperlink() {
|
|
1347
1446
|
const view = this.resolvedEditor?.editorView;
|
|
1348
|
-
if (!view)
|
|
1447
|
+
if (!view || !view.editable)
|
|
1349
1448
|
return;
|
|
1350
1449
|
const { state } = view;
|
|
1351
1450
|
const anchorMarkType = state.schema.marks['anchor'];
|
|
@@ -1396,6 +1495,9 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
1396
1495
|
this.draftDirty = Boolean(this.draftAttrs && Object.keys(this.draftAttrs).length > 0);
|
|
1397
1496
|
}
|
|
1398
1497
|
applyDraftAttrs() {
|
|
1498
|
+
if (!this.isEditorEditable()) {
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1399
1501
|
if (!this.draftAttrs || !this.draftDirty || !this.elementInfo || this.elementInfo.position === null) {
|
|
1400
1502
|
return;
|
|
1401
1503
|
}
|
|
@@ -1417,8 +1519,15 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
1417
1519
|
revertDraftAttrs() {
|
|
1418
1520
|
this.resetDraftAttrs();
|
|
1419
1521
|
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Apply/Revert for pending attribute edits.
|
|
1524
|
+
*
|
|
1525
|
+
* Withheld when the editor is not editable: `applyDraftAttrs` refuses to write
|
|
1526
|
+
* either way — that guard is the barrier — but offering an Apply that cannot
|
|
1527
|
+
* act reads as a broken control rather than a read-only document.
|
|
1528
|
+
*/
|
|
1420
1529
|
renderDraftActions() {
|
|
1421
|
-
if (!this.draftDirty) {
|
|
1530
|
+
if (!this.draftDirty || !this.isEditorEditable()) {
|
|
1422
1531
|
return null;
|
|
1423
1532
|
}
|
|
1424
1533
|
return html `
|
|
@@ -1441,6 +1550,16 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
1441
1550
|
</div>
|
|
1442
1551
|
`;
|
|
1443
1552
|
}
|
|
1553
|
+
/**
|
|
1554
|
+
* Controls that act on the selection itself rather than on its attributes.
|
|
1555
|
+
*
|
|
1556
|
+
* The delete action sits here, at the top of the panel, deliberately far from
|
|
1557
|
+
* the Apply/Update primary at the bottom: a destructive verb never shares a
|
|
1558
|
+
* region with the action a user is aiming for. It is a plain `<button>` with
|
|
1559
|
+
* visible text, so it is keyboard reachable and its accessible name is the
|
|
1560
|
+
* label itself. Removal is undoable through editor history, so it commits
|
|
1561
|
+
* straight away rather than opening a confirmation step.
|
|
1562
|
+
*/
|
|
1444
1563
|
renderSelectionControls() {
|
|
1445
1564
|
return html `
|
|
1446
1565
|
<div class="selection-editor-controls">
|
|
@@ -1452,9 +1571,99 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
1452
1571
|
>
|
|
1453
1572
|
${this.pinSelection ? t('selectionEditor.unpinSelection') : t('selectionEditor.pinSelection')}
|
|
1454
1573
|
</button>
|
|
1574
|
+
${(() => {
|
|
1575
|
+
const deletable = this.getDeletableNode();
|
|
1576
|
+
return deletable
|
|
1577
|
+
? html `
|
|
1578
|
+
<button
|
|
1579
|
+
type="button"
|
|
1580
|
+
class="selection-editor-button selection-editor-button--secondary selection-editor-button--danger"
|
|
1581
|
+
@click=${this.deleteSelectedNode}
|
|
1582
|
+
>
|
|
1583
|
+
${this.getDeleteLabel(deletable)}
|
|
1584
|
+
</button>
|
|
1585
|
+
`
|
|
1586
|
+
: nothing;
|
|
1587
|
+
})()}
|
|
1455
1588
|
</div>
|
|
1456
1589
|
`;
|
|
1457
1590
|
}
|
|
1591
|
+
/**
|
|
1592
|
+
* The node the delete action would remove, or `null` when there is none the
|
|
1593
|
+
* panel may act on.
|
|
1594
|
+
*
|
|
1595
|
+
* Only a `NodeSelection` qualifies: a text range inside a caption or paragraph
|
|
1596
|
+
* belongs to the editing surface, not to a panel-level "delete this element"
|
|
1597
|
+
* verb. `deleteSelection` is probed with `dispatch` omitted, so the check
|
|
1598
|
+
* cannot edit the document.
|
|
1599
|
+
*
|
|
1600
|
+
* A view that is not editable never yields a target. A host can turn editing
|
|
1601
|
+
* off at any time (`view.setProps({ editable: () => false })`) and expects the
|
|
1602
|
+
* whole surface to go quiet — a side panel that still removes content would
|
|
1603
|
+
* make a read-only document lose it. `view.editable` is the same signal the
|
|
1604
|
+
* editor's own key and click handling reads, so the panel cannot drift from
|
|
1605
|
+
* the editing surface.
|
|
1606
|
+
*
|
|
1607
|
+
* Withheld while the panel is pinned, and that is a limitation rather than a
|
|
1608
|
+
* design goal. A pinned panel stops following the editor selection and
|
|
1609
|
+
* therefore stops re-rendering, while `elementInfo.position` is never remapped
|
|
1610
|
+
* through the transaction mapping — so every pinned control already acts on a
|
|
1611
|
+
* position that may have drifted, and the attribute fields cope only because
|
|
1612
|
+
* they re-check the node type at that position before writing. Removal has no
|
|
1613
|
+
* such recheck available: after one deletion the frozen panel would still show
|
|
1614
|
+
* the control, now aimed at whatever moved into that position. Making the pin
|
|
1615
|
+
* remap its stored position on every document change would fix this for all
|
|
1616
|
+
* pinned controls at once; until then the destructive one stays out.
|
|
1617
|
+
*/
|
|
1618
|
+
getDeletableNode() {
|
|
1619
|
+
const view = this.resolvedEditor?.editorView;
|
|
1620
|
+
if (!view || this.pinSelection || !view.editable) {
|
|
1621
|
+
return null;
|
|
1622
|
+
}
|
|
1623
|
+
const state = view.state;
|
|
1624
|
+
if (!(state.selection instanceof NodeSelection) || !deleteSelection(state)) {
|
|
1625
|
+
return null;
|
|
1626
|
+
}
|
|
1627
|
+
return state.selection.node;
|
|
1628
|
+
}
|
|
1629
|
+
/**
|
|
1630
|
+
* Label for the delete action, naming what is about to be removed. The visible
|
|
1631
|
+
* text is the button's accessible name, so naming the node here keeps the two
|
|
1632
|
+
* identical — no `aria-label` to drift from what is on screen. Anything the
|
|
1633
|
+
* panel has no name for falls back to the generic wording.
|
|
1634
|
+
*/
|
|
1635
|
+
getDeleteLabel(node) {
|
|
1636
|
+
const name = this.isTableFigure(node)
|
|
1637
|
+
? t('selectionEditor.table')
|
|
1638
|
+
: this.getNodeTypeName(node.type.name);
|
|
1639
|
+
return name
|
|
1640
|
+
? t('selectionEditor.deleteNamedElement').replace('{element}', name)
|
|
1641
|
+
: t('selectionEditor.deleteElement');
|
|
1642
|
+
}
|
|
1643
|
+
/**
|
|
1644
|
+
* Remove the selected node through ProseMirror's own deletion command, so the
|
|
1645
|
+
* change is a normal transaction and stays undoable and collab-safe. The state
|
|
1646
|
+
* is read from the view at call time rather than reused from render, and the
|
|
1647
|
+
* editability guard is repeated here: a host can switch the view to read-only
|
|
1648
|
+
* after the button was rendered, and the rendered panel would not know.
|
|
1649
|
+
*/
|
|
1650
|
+
deleteSelectedNode() {
|
|
1651
|
+
const view = this.resolvedEditor?.editorView;
|
|
1652
|
+
if (!view || !view.editable) {
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
const state = view.state;
|
|
1656
|
+
if (!(state.selection instanceof NodeSelection)) {
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
if (!deleteSelection(state, (tr) => view.dispatch(tr))) {
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
// Focus would otherwise be orphaned on a button that just disappeared.
|
|
1663
|
+
view.focus();
|
|
1664
|
+
const selection = view.state.selection;
|
|
1665
|
+
this.updateElementInfo({ anchor: selection.anchor, head: selection.head });
|
|
1666
|
+
}
|
|
1458
1667
|
togglePinSelection() {
|
|
1459
1668
|
this.pinSelection = !this.pinSelection;
|
|
1460
1669
|
if (!this.pinSelection && this.resolvedEditor?.editorView) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sciflow/editor-start",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://docs.sciflow.org",
|
|
@@ -96,15 +96,15 @@
|
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"dependencies": {
|
|
99
|
-
"@sciflow/schema-core": "0.1.
|
|
99
|
+
"@sciflow/schema-core": "0.1.1",
|
|
100
100
|
"lit": "^3.2.1",
|
|
101
101
|
"prosemirror-commands": "1.7.1",
|
|
102
102
|
"prosemirror-keymap": "1.2.3",
|
|
103
103
|
"tslib": "^2.3.0"
|
|
104
104
|
},
|
|
105
105
|
"peerDependencies": {
|
|
106
|
-
"@sciflow/editor-core": "0.1.
|
|
107
|
-
"@sciflow/schema-prosemirror": "0.1.
|
|
106
|
+
"@sciflow/editor-core": "0.1.1",
|
|
107
|
+
"@sciflow/schema-prosemirror": "0.1.1",
|
|
108
108
|
"prosemirror-model": "1.25.8",
|
|
109
109
|
"prosemirror-state": "1.4.4",
|
|
110
110
|
"prosemirror-view": "1.41.8"
|
|
@@ -114,9 +114,9 @@
|
|
|
114
114
|
"@codemirror/lang-xml": "^6.1.0",
|
|
115
115
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
116
116
|
"@material-symbols/svg-400": "^0.44.0",
|
|
117
|
-
"@sciflow/editor-core": "0.1.
|
|
118
|
-
"@sciflow/pandoc-web": "0.1.
|
|
119
|
-
"@sciflow/schema-prosemirror": "0.1.
|
|
117
|
+
"@sciflow/editor-core": "0.1.1",
|
|
118
|
+
"@sciflow/pandoc-web": "0.1.1",
|
|
119
|
+
"@sciflow/schema-prosemirror": "0.1.1",
|
|
120
120
|
"codemirror": "^6.0.2",
|
|
121
121
|
"jsdom": "^29.0.2",
|
|
122
122
|
"prosemirror-model": "1.25.8",
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { type Feature } from '@sciflow/editor-core';
|
|
2
|
-
/**
|
|
3
|
-
* The feature set `<sciflow-editor>` activates when no `features` property is
|
|
4
|
-
* set. Exported so consumers can *extend* rather than replace the defaults
|
|
5
|
-
* (resolves CHANGEREQUEST #1):
|
|
6
|
-
*
|
|
7
|
-
* ```ts
|
|
8
|
-
* import { DEFAULT_EDITOR_FEATURES } from '@sciflow/editor-start';
|
|
9
|
-
*
|
|
10
|
-
* editor.features = [...DEFAULT_EDITOR_FEATURES, myCustomFeature];
|
|
11
|
-
* ```
|
|
12
|
-
*
|
|
13
|
-
* Lives in its own module (rather than directly in the barrel `index.ts`) so
|
|
14
|
-
* `editor-element.ts` can import it without an `index.ts` → `editor-element.ts`
|
|
15
|
-
* → `index.ts` import cycle; the barrel re-exports it from here.
|
|
16
|
-
*
|
|
17
|
-
* `editor-element.ts`'s private `getDefaultFeatures()` delegates to this
|
|
18
|
-
* constant — keep the two in sync.
|
|
19
|
-
*/
|
|
20
|
-
export declare const DEFAULT_EDITOR_FEATURES: readonly Feature[];
|
|
21
|
-
//# sourceMappingURL=default-features.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"default-features.d.ts","sourceRoot":"","sources":["../../src/lib/default-features.ts"],"names":[],"mappings":"AAAA,OAAO,EAYL,KAAK,OAAO,EACb,MAAM,sBAAsB,CAAC;AAI9B;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,OAAO,EAarD,CAAC"}
|