@sciflow/editor-start 0.0.1-beta → 0.0.1-beta2
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/README.md +34 -0
- package/dist/bundle/sciflow-editor.css +1 -1
- package/dist/bundle/sciflow-editor.js +7643 -5555
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/citation-source-adapter.d.ts +22 -2
- package/dist/lib/citation-source-adapter.d.ts.map +1 -1
- package/dist/lib/citation-source-adapter.js +104 -48
- package/dist/lib/drag-preview.d.ts +13 -0
- package/dist/lib/drag-preview.d.ts.map +1 -0
- package/dist/lib/drag-preview.js +36 -0
- package/dist/lib/editor-element.d.ts +16 -1
- package/dist/lib/editor-element.d.ts.map +1 -1
- package/dist/lib/editor-element.js +107 -30
- 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 +79 -10
- package/dist/lib/outline.d.ts +10 -5
- package/dist/lib/outline.d.ts.map +1 -1
- package/dist/lib/outline.js +112 -1
- package/dist/lib/reference-list.d.ts.map +1 -1
- package/dist/lib/reference-list.js +2 -0
- package/dist/lib/selection-editor.d.ts +67 -5
- package/dist/lib/selection-editor.d.ts.map +1 -1
- package/dist/lib/selection-editor.js +1060 -125
- package/dist/lib/selection-transform-registry.d.ts +16 -0
- package/dist/lib/selection-transform-registry.d.ts.map +1 -0
- package/dist/lib/selection-transform-registry.js +24 -0
- package/dist/lib/selection-ui-renderer.d.ts +21 -0
- package/dist/lib/selection-ui-renderer.d.ts.map +1 -0
- package/dist/lib/selection-ui-renderer.js +244 -0
- package/dist/lib/selection-widget-host.d.ts +67 -0
- package/dist/lib/selection-widget-host.d.ts.map +1 -0
- package/dist/lib/selection-widget-host.js +151 -0
- package/dist/lib/selection-widget-registry.d.ts +16 -0
- package/dist/lib/selection-widget-registry.d.ts.map +1 -0
- package/dist/lib/selection-widget-registry.js +30 -0
- package/dist/lib/test-fixtures/soak-documents.d.ts +13 -0
- package/dist/lib/test-fixtures/soak-documents.d.ts.map +1 -0
- package/dist/lib/test-fixtures/soak-documents.js +69 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +9 -5
|
@@ -17,7 +17,16 @@ import { css, html, LitElement, 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 } from '@sciflow/editor-core';
|
|
20
|
+
import { renderTexToSvg } from '@sciflow/editor-core/features/math';
|
|
21
|
+
import { Fragment as PMFragment } from 'prosemirror-model';
|
|
22
|
+
import { EditorState, NodeSelection } from 'prosemirror-state';
|
|
23
|
+
import { EditorView } from 'prosemirror-view';
|
|
24
|
+
import { baseKeymap, toggleMark } from 'prosemirror-commands';
|
|
25
|
+
import { keymap } from 'prosemirror-keymap';
|
|
20
26
|
import { getCitationSourceAdapter, } from './citation-source-adapter.js';
|
|
27
|
+
import { collectDocumentOutline } from './outline.js';
|
|
28
|
+
import { renderNodeFromUiSchema } from './selection-ui-renderer.js';
|
|
29
|
+
import './selection-widget-host.js';
|
|
21
30
|
import selectionEditorCss from './selection-editor.css?inline';
|
|
22
31
|
import { applyThemeStylesToRoot, subscribeToSciFlowTheme } from './theme.js';
|
|
23
32
|
/**
|
|
@@ -37,9 +46,28 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
37
46
|
/** Custom adapter for editing citation source. When unset, uses the global adapter. */
|
|
38
47
|
this.citationSourceAdapter = null;
|
|
39
48
|
this.elementInfo = null;
|
|
49
|
+
/** Draft values for math sidebar (tex, style, label). Synced from node attrs when math is selected. */
|
|
50
|
+
this.mathDraft = null;
|
|
51
|
+
/** Last preview result for math (error/warnings). SVG is rendered into mathPreviewContainer. */
|
|
52
|
+
this.mathPreviewResult = null;
|
|
53
|
+
/** Draft values for citation sidebar (content + source). Applied only on Update. */
|
|
54
|
+
this.citationDraft = null;
|
|
55
|
+
/** Draft values for cross-reference sidebar (display text + target href). */
|
|
56
|
+
this.crossReferenceDraft = null;
|
|
57
|
+
this.draftAttrs = null;
|
|
58
|
+
this.draftDirty = false;
|
|
59
|
+
this.pinSelection = false;
|
|
40
60
|
this.resolvedEditor = null;
|
|
41
61
|
this.themeStyleElements = [];
|
|
42
62
|
this.citationSourceContainer = null;
|
|
63
|
+
this.mathPreviewContainer = null;
|
|
64
|
+
this.mathPreviewDebounce = null;
|
|
65
|
+
this.citationContentEditorContainer = null;
|
|
66
|
+
this.citationContentView = null;
|
|
67
|
+
this.citationContentEditorPosition = null;
|
|
68
|
+
this.selectionDebounce = null;
|
|
69
|
+
this.outlineTargetOptionsCacheDoc = null;
|
|
70
|
+
this.outlineTargetOptionsCache = [];
|
|
43
71
|
}
|
|
44
72
|
static { this.styles = css `${unsafeCSS(selectionEditorCss)}`; }
|
|
45
73
|
connectedCallback() {
|
|
@@ -51,7 +79,12 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
51
79
|
}
|
|
52
80
|
disconnectedCallback() {
|
|
53
81
|
super.disconnectedCallback();
|
|
82
|
+
this.destroyCitationContentView();
|
|
54
83
|
this.detachEditorListeners();
|
|
84
|
+
if (this.selectionDebounce) {
|
|
85
|
+
clearTimeout(this.selectionDebounce);
|
|
86
|
+
this.selectionDebounce = null;
|
|
87
|
+
}
|
|
55
88
|
this.themeUnsub?.();
|
|
56
89
|
this.themeUnsub = undefined;
|
|
57
90
|
}
|
|
@@ -60,40 +93,154 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
60
93
|
if (changedProperties.has('editor') || changedProperties.has('for')) {
|
|
61
94
|
this.resolveEditorReference();
|
|
62
95
|
}
|
|
63
|
-
if (changedProperties.has('elementInfo')
|
|
64
|
-
this.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
if (changedProperties.has('elementInfo')) {
|
|
97
|
+
if (this.elementInfo?.type === 'math') {
|
|
98
|
+
const attrs = this.elementInfo.attrs;
|
|
99
|
+
this.mathDraft = {
|
|
100
|
+
tex: attrs?.tex ?? '',
|
|
101
|
+
style: attrs?.style === 'display' ? 'display' : 'inline',
|
|
102
|
+
label: attrs?.label ?? '',
|
|
103
|
+
};
|
|
104
|
+
this.scheduleMathPreview();
|
|
105
|
+
this.citationDraft = null;
|
|
106
|
+
this.crossReferenceDraft = null;
|
|
107
|
+
this.resetDraftAttrs();
|
|
108
|
+
}
|
|
109
|
+
else if (this.elementInfo?.type === 'citation') {
|
|
110
|
+
const attrs = this.elementInfo.attrs;
|
|
111
|
+
const rawSource = attrs?.source;
|
|
112
|
+
const sourceEncoded = typeof rawSource === 'string' ? rawSource : SourceField.toString(rawSource ?? []) ?? null;
|
|
113
|
+
const content = this.getCitationNodeTextContent();
|
|
114
|
+
this.citationDraft = {
|
|
115
|
+
content: content ?? '',
|
|
116
|
+
sourceEncoded,
|
|
117
|
+
};
|
|
118
|
+
this.crossReferenceDraft = null;
|
|
119
|
+
this.mathDraft = null;
|
|
120
|
+
this.mathPreviewResult = null;
|
|
121
|
+
this.resetDraftAttrs();
|
|
122
|
+
if (this.mathPreviewDebounce) {
|
|
123
|
+
clearTimeout(this.mathPreviewDebounce);
|
|
124
|
+
this.mathPreviewDebounce = null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else if (this.elementInfo?.type === 'link') {
|
|
128
|
+
const attrs = this.elementInfo.attrs;
|
|
129
|
+
const href = typeof attrs?.href === 'string' ? attrs.href : '';
|
|
130
|
+
this.crossReferenceDraft = {
|
|
131
|
+
content: this.getLinkNodeTextContent() ?? '',
|
|
132
|
+
href,
|
|
133
|
+
};
|
|
134
|
+
this.mathDraft = null;
|
|
135
|
+
this.mathPreviewResult = null;
|
|
136
|
+
this.citationDraft = null;
|
|
137
|
+
this.resetDraftAttrs();
|
|
138
|
+
this.destroyCitationContentView();
|
|
139
|
+
if (this.mathPreviewDebounce) {
|
|
140
|
+
clearTimeout(this.mathPreviewDebounce);
|
|
141
|
+
this.mathPreviewDebounce = null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
this.mathDraft = null;
|
|
146
|
+
this.mathPreviewResult = null;
|
|
147
|
+
this.citationDraft = null;
|
|
148
|
+
this.crossReferenceDraft = null;
|
|
149
|
+
this.resetDraftAttrs();
|
|
150
|
+
this.destroyCitationContentView();
|
|
151
|
+
if (this.mathPreviewDebounce) {
|
|
152
|
+
clearTimeout(this.mathPreviewDebounce);
|
|
153
|
+
this.mathPreviewDebounce = null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (changedProperties.has('elementInfo') ||
|
|
158
|
+
changedProperties.has('citationDraft')) {
|
|
159
|
+
if (this.citationSourceContainer &&
|
|
160
|
+
this.elementInfo?.type === 'citation' &&
|
|
161
|
+
this.citationDraft) {
|
|
162
|
+
const items = SourceField.fromString(this.citationDraft.sourceEncoded ?? '') ?? [];
|
|
163
|
+
const adapter = this.citationSourceAdapter ?? getCitationSourceAdapter();
|
|
164
|
+
const refs = this.resolvedEditor?.references ?? [];
|
|
165
|
+
const context = {
|
|
166
|
+
onSourceChange: (encoded) => {
|
|
167
|
+
if (this.citationDraft) {
|
|
168
|
+
this.citationDraft = { ...this.citationDraft, sourceEncoded: encoded };
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
getReferences: () => refs.map((r) => ({ id: r.id, rawCitation: r.rawCitation })),
|
|
172
|
+
};
|
|
173
|
+
if (adapter.update) {
|
|
174
|
+
adapter.update(this.citationSourceContainer, items, context);
|
|
175
|
+
}
|
|
82
176
|
}
|
|
83
177
|
}
|
|
84
178
|
}
|
|
85
179
|
render() {
|
|
86
180
|
if (!this.elementInfo || !this.elementInfo.type) {
|
|
87
181
|
return html `
|
|
88
|
-
<div class="selection-editor-container">
|
|
182
|
+
<div class="selection-editor-container" @keydown=${this.handleGenericKeydown}>
|
|
89
183
|
<p class="selection-editor-placeholder">
|
|
90
184
|
No element selected. Click or select an element in the editor.
|
|
91
185
|
</p>
|
|
92
186
|
</div>
|
|
187
|
+
`;
|
|
188
|
+
}
|
|
189
|
+
if (this.elementInfo.type === 'math' && this.mathDraft) {
|
|
190
|
+
return this.renderMathSidebar();
|
|
191
|
+
}
|
|
192
|
+
if (this.elementInfo.type === 'citation' && this.citationDraft) {
|
|
193
|
+
return this.renderCitationSidebar();
|
|
194
|
+
}
|
|
195
|
+
if (this.elementInfo.type === 'link' && this.crossReferenceDraft) {
|
|
196
|
+
return this.renderCrossReferenceSidebar();
|
|
197
|
+
}
|
|
198
|
+
if (this.elementInfo.type === 'figure') {
|
|
199
|
+
return this.renderFigureSidebar();
|
|
200
|
+
}
|
|
201
|
+
const uiSchema = this.resolvedEditor?.getSelectionUiSchema?.();
|
|
202
|
+
const nodeFragment = uiSchema?.properties?.nodes?.[this.elementInfo.type];
|
|
203
|
+
const useSchemaDriven = nodeFragment &&
|
|
204
|
+
(this.elementInfo.type === 'heading' ||
|
|
205
|
+
this.elementInfo.type === 'figure');
|
|
206
|
+
if (useSchemaDriven && nodeFragment) {
|
|
207
|
+
const attrs = this.getEffectiveAttrs();
|
|
208
|
+
return html `
|
|
209
|
+
<div class="selection-editor-container">
|
|
210
|
+
${this.renderSelectionControls()}
|
|
211
|
+
<div class="selection-editor-field">
|
|
212
|
+
<span class="selection-editor-label">Type:</span>
|
|
213
|
+
<code class="selection-editor-value">${this.elementInfo.type}</code>
|
|
214
|
+
</div>
|
|
215
|
+
${this.elementInfo.id
|
|
216
|
+
? html `
|
|
217
|
+
<div class="selection-editor-field">
|
|
218
|
+
<span class="selection-editor-label">ID:</span>
|
|
219
|
+
<code class="selection-editor-value">${this.elementInfo.id}</code>
|
|
220
|
+
</div>
|
|
221
|
+
`
|
|
222
|
+
: ''}
|
|
223
|
+
${renderNodeFromUiSchema(nodeFragment, {
|
|
224
|
+
attrs,
|
|
225
|
+
onAttrChange: (attr, value) => this.setDraftAttr(attr, value),
|
|
226
|
+
getOptionsSource: (id) => {
|
|
227
|
+
if (id === 'references') {
|
|
228
|
+
const refs = this.resolvedEditor?.references ?? [];
|
|
229
|
+
return refs.map((r) => ({ id: r.id, label: r.rawCitation ?? r.id }));
|
|
230
|
+
}
|
|
231
|
+
if (id === 'outlineTargets') {
|
|
232
|
+
return this.getOutlineTargetOptions();
|
|
233
|
+
}
|
|
234
|
+
return [];
|
|
235
|
+
},
|
|
236
|
+
})}
|
|
237
|
+
${this.renderDraftActions()}
|
|
238
|
+
</div>
|
|
93
239
|
`;
|
|
94
240
|
}
|
|
95
241
|
return html `
|
|
96
|
-
<div class="selection-editor-container">
|
|
242
|
+
<div class="selection-editor-container" @keydown=${this.handleGenericKeydown}>
|
|
243
|
+
${this.renderSelectionControls()}
|
|
97
244
|
<!-- Type display -->
|
|
98
245
|
<div class="selection-editor-field">
|
|
99
246
|
<span class="selection-editor-label">Type:</span>
|
|
@@ -110,42 +257,70 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
110
257
|
`
|
|
111
258
|
: ''}
|
|
112
259
|
|
|
113
|
-
<!-- Attributes
|
|
260
|
+
<!-- Attributes -->
|
|
114
261
|
${this.elementInfo.attrs && Object.keys(this.elementInfo.attrs).length > 0
|
|
115
|
-
?
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
this.elementInfo.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
this.citationSourceContainer = el;
|
|
129
|
-
this.mountCitationSourceAdapter(el);
|
|
130
|
-
}
|
|
131
|
-
else if (this.citationSourceContainer) {
|
|
132
|
-
this.unmountCitationSourceAdapter(this.citationSourceContainer);
|
|
133
|
-
this.citationSourceContainer = null;
|
|
134
|
-
}
|
|
135
|
-
})}
|
|
136
|
-
></div>
|
|
262
|
+
? (() => {
|
|
263
|
+
const attrs = this.getEffectiveAttrs();
|
|
264
|
+
const filtered = Object.entries(attrs).filter(([key]) => key !== 'id' &&
|
|
265
|
+
key !== 'data' &&
|
|
266
|
+
!(this.elementInfo.type === 'citation' && key === 'source'));
|
|
267
|
+
if (this.elementInfo.type === 'heading' && filtered.length > 0) {
|
|
268
|
+
const levelEntry = filtered.find(([k]) => k === 'level');
|
|
269
|
+
const rest = filtered.filter(([k]) => k !== 'level');
|
|
270
|
+
return html `
|
|
271
|
+
${levelEntry
|
|
272
|
+
? html `
|
|
273
|
+
<div class="selection-editor-attrs">
|
|
274
|
+
${this.renderAttributeField(levelEntry[0], levelEntry[1])}
|
|
137
275
|
</div>
|
|
138
276
|
`
|
|
139
|
-
|
|
140
|
-
${
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
277
|
+
: ''}
|
|
278
|
+
${rest.length > 0
|
|
279
|
+
? html `
|
|
280
|
+
<details class="selection-editor-details">
|
|
281
|
+
<summary class="selection-editor-details-summary">More attributes</summary>
|
|
282
|
+
<div class="selection-editor-details-body">
|
|
283
|
+
${rest.map(([attrName, attrValue]) => this.renderAttributeField(attrName, attrValue))}
|
|
284
|
+
</div>
|
|
285
|
+
</details>
|
|
286
|
+
`
|
|
287
|
+
: ''}
|
|
288
|
+
`;
|
|
289
|
+
}
|
|
290
|
+
if (this.elementInfo.type === 'paragraph' && filtered.length > 0) {
|
|
291
|
+
const alignmentEntry = filtered.find(([k]) => k === 'text-align');
|
|
292
|
+
const rest = filtered.filter(([k]) => k !== 'text-align');
|
|
293
|
+
return html `
|
|
294
|
+
${alignmentEntry
|
|
295
|
+
? html `
|
|
296
|
+
<div class="selection-editor-attrs">
|
|
297
|
+
${this.renderAttributeField(alignmentEntry[0], alignmentEntry[1])}
|
|
298
|
+
</div>
|
|
299
|
+
`
|
|
300
|
+
: ''}
|
|
301
|
+
${rest.length > 0
|
|
302
|
+
? html `
|
|
303
|
+
<details class="selection-editor-details">
|
|
304
|
+
<summary class="selection-editor-details-summary">More attributes</summary>
|
|
305
|
+
<div class="selection-editor-details-body">
|
|
306
|
+
${rest.map(([attrName, attrValue]) => this.renderAttributeField(attrName, attrValue))}
|
|
307
|
+
</div>
|
|
308
|
+
</details>
|
|
309
|
+
`
|
|
310
|
+
: ''}
|
|
311
|
+
`;
|
|
312
|
+
}
|
|
313
|
+
return html `
|
|
314
|
+
<div class="selection-editor-field">
|
|
315
|
+
<span class="selection-editor-label">Attributes:</span>
|
|
316
|
+
<div class="selection-editor-attrs">
|
|
317
|
+
${filtered.map(([attrName, attrValue]) => this.renderAttributeField(attrName, attrValue))}
|
|
318
|
+
</div>
|
|
145
319
|
</div>
|
|
146
|
-
|
|
147
|
-
|
|
320
|
+
`;
|
|
321
|
+
})()
|
|
148
322
|
: ''}
|
|
323
|
+
${this.renderDraftActions()}
|
|
149
324
|
</div>
|
|
150
325
|
`;
|
|
151
326
|
}
|
|
@@ -165,13 +340,13 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
165
340
|
}
|
|
166
341
|
this.selectionListener = (event) => {
|
|
167
342
|
const selection = event.detail;
|
|
168
|
-
this.
|
|
343
|
+
this.scheduleSelectionUpdate(selection);
|
|
169
344
|
};
|
|
170
345
|
this.readyListener = () => {
|
|
171
346
|
// Re-evaluate selection when editor becomes ready
|
|
172
347
|
if (this.resolvedEditor?.editorView) {
|
|
173
348
|
const selection = this.resolvedEditor.editorView.state.selection;
|
|
174
|
-
this.
|
|
349
|
+
this.scheduleSelectionUpdate({
|
|
175
350
|
anchor: selection.anchor,
|
|
176
351
|
head: selection.head,
|
|
177
352
|
});
|
|
@@ -182,7 +357,7 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
182
357
|
// Initial evaluation
|
|
183
358
|
if (next.editorView) {
|
|
184
359
|
const selection = next.editorView.state.selection;
|
|
185
|
-
this.
|
|
360
|
+
this.scheduleSelectionUpdate({
|
|
186
361
|
anchor: selection.anchor,
|
|
187
362
|
head: selection.head,
|
|
188
363
|
});
|
|
@@ -220,6 +395,18 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
220
395
|
this.readyListener = undefined;
|
|
221
396
|
this.resolvedEditor = null;
|
|
222
397
|
}
|
|
398
|
+
scheduleSelectionUpdate(selection) {
|
|
399
|
+
if (this.pinSelection) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (this.selectionDebounce) {
|
|
403
|
+
clearTimeout(this.selectionDebounce);
|
|
404
|
+
}
|
|
405
|
+
this.selectionDebounce = setTimeout(() => {
|
|
406
|
+
this.selectionDebounce = null;
|
|
407
|
+
this.updateElementInfo(selection);
|
|
408
|
+
}, 100);
|
|
409
|
+
}
|
|
223
410
|
/**
|
|
224
411
|
* Extract element information from the current selection.
|
|
225
412
|
*/
|
|
@@ -229,32 +416,60 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
229
416
|
this.elementInfo = null;
|
|
230
417
|
return;
|
|
231
418
|
}
|
|
419
|
+
const sel = view.state.selection;
|
|
420
|
+
if (sel instanceof NodeSelection && sel.node.type.name === 'math') {
|
|
421
|
+
const attrs = sel.node.attrs;
|
|
422
|
+
this.elementInfo = {
|
|
423
|
+
type: 'math',
|
|
424
|
+
id: attrs?.id ?? null,
|
|
425
|
+
attrs: { ...attrs },
|
|
426
|
+
position: sel.from,
|
|
427
|
+
};
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
if (sel instanceof NodeSelection && sel.node.type.name === 'citation') {
|
|
431
|
+
const attrs = sel.node.attrs;
|
|
432
|
+
this.elementInfo = {
|
|
433
|
+
type: 'citation',
|
|
434
|
+
id: attrs?.id ?? null,
|
|
435
|
+
attrs: { ...attrs },
|
|
436
|
+
position: sel.from,
|
|
437
|
+
};
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
if (sel instanceof NodeSelection && sel.node.type.name === 'figure') {
|
|
441
|
+
const attrs = sel.node.attrs;
|
|
442
|
+
this.elementInfo = {
|
|
443
|
+
type: 'figure',
|
|
444
|
+
id: attrs?.id ?? null,
|
|
445
|
+
attrs: { ...attrs },
|
|
446
|
+
position: sel.from,
|
|
447
|
+
};
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
if (sel instanceof NodeSelection && sel.node.type.name === 'link') {
|
|
451
|
+
const attrs = sel.node.attrs;
|
|
452
|
+
this.elementInfo = {
|
|
453
|
+
type: 'link',
|
|
454
|
+
id: attrs?.id ?? null,
|
|
455
|
+
attrs: { ...attrs },
|
|
456
|
+
position: sel.from,
|
|
457
|
+
};
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
232
460
|
let elementInfo = null;
|
|
233
461
|
let nodePosition = null;
|
|
234
462
|
const anchor = selection?.anchor ?? 0;
|
|
235
|
-
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
nodePosition = anchor;
|
|
240
|
-
elementInfo = {
|
|
241
|
-
type: nodeAtPos.type.name,
|
|
242
|
-
id: nodeAtPos.attrs?.id ?? null,
|
|
243
|
-
attrs: nodeAtPos.attrs ?? null,
|
|
244
|
-
position: nodePosition,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
// If no block node at position, resolve the position and find the parent block node
|
|
249
|
-
const $pos = view.state.doc.resolve(anchor);
|
|
250
|
-
// Walk up the node tree to find the nearest block-level node
|
|
463
|
+
const $pos = view.state.doc.resolve(anchor);
|
|
464
|
+
const citationType = view.state.schema.nodes.citation;
|
|
465
|
+
// Prefer citation (inline) when cursor is inside one
|
|
466
|
+
if (citationType) {
|
|
251
467
|
for (let depth = $pos.depth; depth > 0; depth--) {
|
|
252
468
|
const node = $pos.node(depth);
|
|
253
|
-
|
|
254
|
-
if (node.type !== view.state.schema.topNodeType && node.type.spec.group === 'block') {
|
|
469
|
+
if (node.type === citationType) {
|
|
255
470
|
nodePosition = $pos.before(depth);
|
|
256
471
|
elementInfo = {
|
|
257
|
-
type:
|
|
472
|
+
type: 'citation',
|
|
258
473
|
id: node.attrs?.id ?? null,
|
|
259
474
|
attrs: node.attrs ?? null,
|
|
260
475
|
position: nodePosition,
|
|
@@ -262,19 +477,217 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
262
477
|
break;
|
|
263
478
|
}
|
|
264
479
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
480
|
+
}
|
|
481
|
+
// Prefer figure when cursor is inside one (e.g. in caption)
|
|
482
|
+
if (!elementInfo) {
|
|
483
|
+
const figureType = view.state.schema.nodes.figure;
|
|
484
|
+
if (figureType) {
|
|
485
|
+
for (let depth = $pos.depth; depth > 0; depth--) {
|
|
486
|
+
const node = $pos.node(depth);
|
|
487
|
+
if (node.type === figureType) {
|
|
488
|
+
nodePosition = $pos.before(depth);
|
|
489
|
+
elementInfo = {
|
|
490
|
+
type: 'figure',
|
|
491
|
+
id: node.attrs?.id ?? null,
|
|
492
|
+
attrs: node.attrs ?? null,
|
|
493
|
+
position: nodePosition,
|
|
494
|
+
};
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
// Prefer link (cross-reference) when cursor is inside one
|
|
501
|
+
if (!elementInfo) {
|
|
502
|
+
const linkType = view.state.schema.nodes.link;
|
|
503
|
+
if (linkType) {
|
|
504
|
+
for (let depth = $pos.depth; depth > 0; depth--) {
|
|
505
|
+
const node = $pos.node(depth);
|
|
506
|
+
if (node.type === linkType) {
|
|
507
|
+
nodePosition = $pos.before(depth);
|
|
508
|
+
elementInfo = {
|
|
509
|
+
type: 'link',
|
|
510
|
+
id: node.attrs?.id ?? null,
|
|
511
|
+
attrs: node.attrs ?? null,
|
|
512
|
+
position: nodePosition,
|
|
513
|
+
};
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (!elementInfo) {
|
|
520
|
+
const nodeAtPos = view.state.doc.nodeAt(anchor);
|
|
521
|
+
if (nodeAtPos && !nodeAtPos.isText) {
|
|
522
|
+
nodePosition = anchor;
|
|
268
523
|
elementInfo = {
|
|
269
|
-
type:
|
|
270
|
-
id:
|
|
271
|
-
attrs:
|
|
524
|
+
type: nodeAtPos.type.name,
|
|
525
|
+
id: nodeAtPos.attrs?.id ?? null,
|
|
526
|
+
attrs: nodeAtPos.attrs ?? null,
|
|
272
527
|
position: nodePosition,
|
|
273
528
|
};
|
|
274
529
|
}
|
|
530
|
+
else {
|
|
531
|
+
for (let depth = $pos.depth; depth > 0; depth--) {
|
|
532
|
+
const node = $pos.node(depth);
|
|
533
|
+
if (node.type !== view.state.schema.topNodeType && node.type.spec.group === 'block') {
|
|
534
|
+
nodePosition = $pos.before(depth);
|
|
535
|
+
elementInfo = {
|
|
536
|
+
type: node.type.name,
|
|
537
|
+
id: node.attrs?.id ?? null,
|
|
538
|
+
attrs: node.attrs ?? null,
|
|
539
|
+
position: nodePosition,
|
|
540
|
+
};
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (!elementInfo && $pos.parent && $pos.parent.type !== view.state.schema.topNodeType) {
|
|
545
|
+
nodePosition = $pos.before($pos.depth);
|
|
546
|
+
elementInfo = {
|
|
547
|
+
type: $pos.parent.type.name,
|
|
548
|
+
id: $pos.parent.attrs?.id ?? null,
|
|
549
|
+
attrs: $pos.parent.attrs ?? null,
|
|
550
|
+
position: nodePosition,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
}
|
|
275
554
|
}
|
|
276
555
|
this.elementInfo = elementInfo;
|
|
277
556
|
}
|
|
557
|
+
/** Get the selected citation node's text content from the main document (for draft init). */
|
|
558
|
+
getCitationNodeTextContent() {
|
|
559
|
+
const node = this.getCitationNode();
|
|
560
|
+
return node?.textContent ?? '';
|
|
561
|
+
}
|
|
562
|
+
/** Get the selected citation node's content as a Fragment (same schema as citation allows). */
|
|
563
|
+
getCitationNodeContentFragment() {
|
|
564
|
+
const node = this.getCitationNode();
|
|
565
|
+
return node?.content ?? PMFragment.empty;
|
|
566
|
+
}
|
|
567
|
+
/** Get the citation node at the current selection, or null. */
|
|
568
|
+
getCitationNode() {
|
|
569
|
+
const view = this.resolvedEditor?.editorView;
|
|
570
|
+
if (!view || !this.elementInfo || this.elementInfo.type !== 'citation' || this.elementInfo.position == null) {
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
const doc = view.state.doc;
|
|
574
|
+
const pos = this.elementInfo.position;
|
|
575
|
+
const nodeAt = doc.nodeAt(pos);
|
|
576
|
+
const citationType = view.state.schema.nodes.citation;
|
|
577
|
+
if (nodeAt?.type === citationType) {
|
|
578
|
+
return nodeAt;
|
|
579
|
+
}
|
|
580
|
+
const $pos = doc.resolve(pos);
|
|
581
|
+
for (let d = $pos.depth; d > 0; d--) {
|
|
582
|
+
const node = $pos.node(d);
|
|
583
|
+
if (node.type === citationType) {
|
|
584
|
+
return node;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
/** Get the selected cross-reference link node's text content from the main document. */
|
|
590
|
+
getLinkNodeTextContent() {
|
|
591
|
+
const node = this.getLinkNode();
|
|
592
|
+
return node?.textContent ?? '';
|
|
593
|
+
}
|
|
594
|
+
/** Get the selected cross-reference link node at current selection, or null. */
|
|
595
|
+
getLinkNode() {
|
|
596
|
+
const view = this.resolvedEditor?.editorView;
|
|
597
|
+
if (!view || !this.elementInfo || this.elementInfo.type !== 'link' || this.elementInfo.position == null) {
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
const doc = view.state.doc;
|
|
601
|
+
const pos = this.elementInfo.position;
|
|
602
|
+
const nodeAt = doc.nodeAt(pos);
|
|
603
|
+
const linkType = view.state.schema.nodes.link;
|
|
604
|
+
if (nodeAt?.type === linkType) {
|
|
605
|
+
return nodeAt;
|
|
606
|
+
}
|
|
607
|
+
const $pos = doc.resolve(pos);
|
|
608
|
+
for (let d = $pos.depth; d > 0; d--) {
|
|
609
|
+
const node = $pos.node(d);
|
|
610
|
+
if (node.type === linkType) {
|
|
611
|
+
return node;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
/** Build outline target options for link href dropdown (headings and figures with id). */
|
|
617
|
+
getOutlineTargetOptions() {
|
|
618
|
+
const view = this.resolvedEditor?.editorView;
|
|
619
|
+
const pmDoc = view?.state?.doc ?? null;
|
|
620
|
+
if (!pmDoc) {
|
|
621
|
+
this.outlineTargetOptionsCacheDoc = null;
|
|
622
|
+
this.outlineTargetOptionsCache = [];
|
|
623
|
+
return [];
|
|
624
|
+
}
|
|
625
|
+
if (this.outlineTargetOptionsCacheDoc === pmDoc) {
|
|
626
|
+
return this.outlineTargetOptionsCache;
|
|
627
|
+
}
|
|
628
|
+
const outline = collectDocumentOutline(null, pmDoc);
|
|
629
|
+
const options = [];
|
|
630
|
+
for (const h of outline.headings) {
|
|
631
|
+
const id = h.id?.trim();
|
|
632
|
+
if (!id)
|
|
633
|
+
continue;
|
|
634
|
+
const level = h.level != null ? `H${h.level}` : 'Heading';
|
|
635
|
+
const text = h.text?.trim() || id;
|
|
636
|
+
options.push({ id: `#${id}`, label: `${level}: ${text}` });
|
|
637
|
+
}
|
|
638
|
+
for (const f of outline.figures) {
|
|
639
|
+
const id = f.id?.trim();
|
|
640
|
+
if (!id)
|
|
641
|
+
continue;
|
|
642
|
+
const text = f.text?.trim() || id;
|
|
643
|
+
options.push({ id: `#${id}`, label: `Figure: ${text}` });
|
|
644
|
+
}
|
|
645
|
+
this.outlineTargetOptionsCacheDoc = pmDoc;
|
|
646
|
+
this.outlineTargetOptionsCache = options;
|
|
647
|
+
return options;
|
|
648
|
+
}
|
|
649
|
+
destroyCitationContentView() {
|
|
650
|
+
if (this.citationContentView) {
|
|
651
|
+
this.citationContentView.destroy();
|
|
652
|
+
this.citationContentView = null;
|
|
653
|
+
}
|
|
654
|
+
this.citationContentEditorContainer = null;
|
|
655
|
+
this.citationContentEditorPosition = null;
|
|
656
|
+
}
|
|
657
|
+
mountCitationContentEditor(container) {
|
|
658
|
+
const view = this.resolvedEditor?.editorView;
|
|
659
|
+
if (!view || !this.elementInfo || this.elementInfo.type !== 'citation' || this.elementInfo.position == null) {
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
const schema = view.state.schema;
|
|
663
|
+
const paragraph = schema.nodes.paragraph;
|
|
664
|
+
const docNode = schema.nodes.doc;
|
|
665
|
+
if (!paragraph || !docNode) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
const pos = this.elementInfo.position;
|
|
669
|
+
if (this.citationContentView && this.citationContentEditorPosition === pos && this.citationContentEditorContainer === container) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
this.destroyCitationContentView();
|
|
673
|
+
this.citationContentEditorContainer = container;
|
|
674
|
+
this.citationContentEditorPosition = pos;
|
|
675
|
+
const initialContent = this.getCitationNodeContentFragment();
|
|
676
|
+
const paragraphNode = paragraph.create(null, initialContent);
|
|
677
|
+
const stateDoc = docNode.create(null, [paragraphNode]);
|
|
678
|
+
const strong = schema.marks.strong;
|
|
679
|
+
const em = schema.marks.em;
|
|
680
|
+
const plugins = [
|
|
681
|
+
keymap(baseKeymap),
|
|
682
|
+
...(strong ? [keymap({ 'Mod-b': toggleMark(strong) })] : []),
|
|
683
|
+
...(em ? [keymap({ 'Mod-i': toggleMark(em) })] : []),
|
|
684
|
+
];
|
|
685
|
+
const state = EditorState.create({ doc: stateDoc, schema, plugins });
|
|
686
|
+
this.citationContentView = new EditorView(container, {
|
|
687
|
+
state,
|
|
688
|
+
editable: () => true,
|
|
689
|
+
});
|
|
690
|
+
}
|
|
278
691
|
/** Convenience getter mirroring `editor.getCommands()`. */
|
|
279
692
|
getCommandRunner() {
|
|
280
693
|
return this.resolvedEditor?.commands ?? null;
|
|
@@ -285,6 +698,27 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
285
698
|
renderAttributeField(attrName, attrValue) {
|
|
286
699
|
const decodedValue = this.decodeSpecialAttribute(attrName, attrValue);
|
|
287
700
|
const renderedValue = decodedValue === null || decodedValue === undefined ? '' : String(decodedValue);
|
|
701
|
+
if (attrName === 'level' && this.elementInfo?.type === 'heading') {
|
|
702
|
+
const level = Math.min(6, Math.max(1, parseInt(renderedValue, 10) || 1));
|
|
703
|
+
return html `
|
|
704
|
+
<div class="selection-editor-attr-field">
|
|
705
|
+
<label
|
|
706
|
+
class="selection-editor-attr-label"
|
|
707
|
+
for="selection-editor-attr-${attrName}"
|
|
708
|
+
>
|
|
709
|
+
${attrName}:
|
|
710
|
+
</label>
|
|
711
|
+
<select
|
|
712
|
+
id="selection-editor-attr-${attrName}"
|
|
713
|
+
class="selection-editor-input selection-editor-select"
|
|
714
|
+
.value=${String(level)}
|
|
715
|
+
@change=${(e) => this.handleDraftAttrChange(e, attrName)}
|
|
716
|
+
>
|
|
717
|
+
${[1, 2, 3, 4, 5, 6].map((n) => html `<option value="${n}">${n}</option>`)}
|
|
718
|
+
</select>
|
|
719
|
+
</div>
|
|
720
|
+
`;
|
|
721
|
+
}
|
|
288
722
|
return html `
|
|
289
723
|
<div class="selection-editor-attr-field">
|
|
290
724
|
<label
|
|
@@ -299,7 +733,7 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
299
733
|
class="selection-editor-input"
|
|
300
734
|
.value=${renderedValue}
|
|
301
735
|
placeholder="No value"
|
|
302
|
-
@change=${(e) => this.
|
|
736
|
+
@change=${(e) => this.handleDraftAttrChange(e, attrName)}
|
|
303
737
|
/>
|
|
304
738
|
</div>
|
|
305
739
|
`;
|
|
@@ -325,25 +759,33 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
325
759
|
}
|
|
326
760
|
/**
|
|
327
761
|
* Mount the citation source adapter UI when a citation node is selected.
|
|
762
|
+
* Uses draft context (onSourceChange) so changes apply only on Update.
|
|
328
763
|
*/
|
|
329
764
|
mountCitationSourceAdapter(container) {
|
|
330
765
|
if (!this.elementInfo || this.elementInfo.type !== 'citation') {
|
|
331
766
|
return;
|
|
332
767
|
}
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
768
|
+
const items = this.citationDraft != null
|
|
769
|
+
? (SourceField.fromString(this.citationDraft.sourceEncoded ?? '') ?? [])
|
|
770
|
+
: (() => {
|
|
771
|
+
const rawSource = this.elementInfo.attrs?.source;
|
|
772
|
+
return typeof rawSource === 'string'
|
|
773
|
+
? SourceField.fromString(rawSource)
|
|
774
|
+
: Array.isArray(rawSource)
|
|
775
|
+
? rawSource
|
|
776
|
+
: [];
|
|
777
|
+
})();
|
|
339
778
|
const adapter = this.citationSourceAdapter ?? getCitationSourceAdapter();
|
|
779
|
+
const refs = this.resolvedEditor?.references ?? [];
|
|
340
780
|
const context = {
|
|
341
|
-
|
|
342
|
-
this.
|
|
343
|
-
|
|
781
|
+
onSourceChange: (encoded) => {
|
|
782
|
+
if (this.citationDraft) {
|
|
783
|
+
this.citationDraft = { ...this.citationDraft, sourceEncoded: encoded };
|
|
784
|
+
}
|
|
344
785
|
},
|
|
786
|
+
getReferences: () => refs.map((r) => ({ id: r.id, rawCitation: r.rawCitation })),
|
|
345
787
|
};
|
|
346
|
-
adapter.render(container, items, context);
|
|
788
|
+
adapter.render(container, items.length > 0 ? items : [{ id: '' }], context);
|
|
347
789
|
}
|
|
348
790
|
/**
|
|
349
791
|
* Unmount the citation source adapter and clean up.
|
|
@@ -357,16 +799,16 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
357
799
|
container.innerHTML = '';
|
|
358
800
|
}
|
|
359
801
|
}
|
|
360
|
-
|
|
361
|
-
* Handle attribute field changes.
|
|
362
|
-
*/
|
|
363
|
-
handleAttrChange(event, attrName) {
|
|
802
|
+
handleDraftAttrChange(event, attrName) {
|
|
364
803
|
const input = event.target;
|
|
365
804
|
const value = input.value.trim();
|
|
366
805
|
let parsedValue = value;
|
|
367
806
|
if (attrName === 'source') {
|
|
368
807
|
parsedValue = this.encodeSourceField(value);
|
|
369
808
|
}
|
|
809
|
+
else if (attrName === 'alt') {
|
|
810
|
+
parsedValue = value;
|
|
811
|
+
}
|
|
370
812
|
else if (value !== '') {
|
|
371
813
|
const numValue = parseFloat(value);
|
|
372
814
|
if (!isNaN(numValue) && isFinite(numValue)) {
|
|
@@ -379,49 +821,521 @@ let SciFlowSelectionEditorElement = class SciFlowSelectionEditorElement extends
|
|
|
379
821
|
else {
|
|
380
822
|
parsedValue = null;
|
|
381
823
|
}
|
|
382
|
-
this.
|
|
824
|
+
this.setDraftAttr(attrName, parsedValue);
|
|
383
825
|
}
|
|
384
826
|
/**
|
|
385
|
-
*
|
|
386
|
-
* Preserves the 'data' attribute if it exists, even though it's not displayed.
|
|
827
|
+
* Render the figure sidebar: type, id, alt text.
|
|
387
828
|
*/
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
829
|
+
renderFigureSidebar() {
|
|
830
|
+
const attrs = this.getEffectiveAttrs();
|
|
831
|
+
const alt = typeof attrs.alt === 'string' ? attrs.alt : '';
|
|
832
|
+
return html `
|
|
833
|
+
<div class="selection-editor-container selection-editor-figure" role="form" aria-label="Edit figure" @keydown=${this.handleGenericKeydown}>
|
|
834
|
+
${this.renderSelectionControls()}
|
|
835
|
+
<div class="selection-editor-field">
|
|
836
|
+
<span class="selection-editor-label">Type:</span>
|
|
837
|
+
<code class="selection-editor-value">figure</code>
|
|
838
|
+
</div>
|
|
839
|
+
${this.elementInfo?.id
|
|
840
|
+
? html `
|
|
841
|
+
<div class="selection-editor-field">
|
|
842
|
+
<span class="selection-editor-label">ID:</span>
|
|
843
|
+
<code class="selection-editor-value">${this.elementInfo.id}</code>
|
|
844
|
+
</div>
|
|
845
|
+
`
|
|
846
|
+
: ''}
|
|
847
|
+
<div class="selection-editor-field">
|
|
848
|
+
<label class="selection-editor-label" for="figure-alt">Alt text</label>
|
|
849
|
+
<input
|
|
850
|
+
type="text"
|
|
851
|
+
id="figure-alt"
|
|
852
|
+
class="selection-editor-input"
|
|
853
|
+
.value=${alt}
|
|
854
|
+
placeholder="Describe the image for accessibility"
|
|
855
|
+
@change=${(e) => this.handleDraftAttrChange(e, 'alt')}
|
|
856
|
+
/>
|
|
857
|
+
</div>
|
|
858
|
+
${this.renderDraftActions()}
|
|
859
|
+
</div>
|
|
860
|
+
`;
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Render the math equation sidebar: TeX source, style, label, preview, status, Apply/Cancel.
|
|
864
|
+
*/
|
|
865
|
+
renderMathSidebar() {
|
|
866
|
+
const d = this.mathDraft;
|
|
867
|
+
const previewErr = this.mathPreviewResult?.error;
|
|
868
|
+
const previewWarnings = this.mathPreviewResult?.warnings ?? [];
|
|
869
|
+
return html `
|
|
870
|
+
<div
|
|
871
|
+
class="selection-editor-container selection-editor-math"
|
|
872
|
+
role="form"
|
|
873
|
+
aria-label="Edit equation"
|
|
874
|
+
@keydown=${this.handleMathKeydown}
|
|
875
|
+
>
|
|
876
|
+
${this.renderSelectionControls()}
|
|
877
|
+
<div class="selection-editor-field">
|
|
878
|
+
<span class="selection-editor-label">Type:</span>
|
|
879
|
+
<code class="selection-editor-value">math</code>
|
|
880
|
+
</div>
|
|
881
|
+
<div class="selection-editor-field">
|
|
882
|
+
<label class="selection-editor-label" for="math-tex">TeX</label>
|
|
883
|
+
<textarea
|
|
884
|
+
id="math-tex"
|
|
885
|
+
class="selection-editor-textarea"
|
|
886
|
+
.value=${d.tex}
|
|
887
|
+
placeholder="e.g. x^2 + y^2 = z^2"
|
|
888
|
+
rows="4"
|
|
889
|
+
aria-describedby=${previewErr ? 'math-preview-error' : previewWarnings.length ? 'math-preview-warnings' : undefined}
|
|
890
|
+
@input=${(e) => {
|
|
891
|
+
const t = e.target.value;
|
|
892
|
+
this.mathDraft = { ...this.mathDraft, tex: t };
|
|
893
|
+
this.scheduleMathPreview();
|
|
894
|
+
}}
|
|
895
|
+
></textarea>
|
|
896
|
+
</div>
|
|
897
|
+
<div class="selection-editor-field">
|
|
898
|
+
<label class="selection-editor-label" for="math-style">Style</label>
|
|
899
|
+
<select
|
|
900
|
+
id="math-style"
|
|
901
|
+
class="selection-editor-input"
|
|
902
|
+
.value=${d.style}
|
|
903
|
+
@change=${(e) => {
|
|
904
|
+
this.mathDraft = { ...this.mathDraft, style: e.target.value };
|
|
905
|
+
this.scheduleMathPreview();
|
|
906
|
+
}}
|
|
907
|
+
>
|
|
908
|
+
<option value="inline">Inline</option>
|
|
909
|
+
<option value="display">Display</option>
|
|
910
|
+
</select>
|
|
911
|
+
</div>
|
|
912
|
+
<div class="selection-editor-field">
|
|
913
|
+
<label class="selection-editor-label" for="math-label">Label (optional)</label>
|
|
914
|
+
<input
|
|
915
|
+
id="math-label"
|
|
916
|
+
type="text"
|
|
917
|
+
class="selection-editor-input"
|
|
918
|
+
.value=${d.label}
|
|
919
|
+
placeholder="eq:mylabel"
|
|
920
|
+
@input=${(e) => {
|
|
921
|
+
this.mathDraft = { ...this.mathDraft, label: e.target.value };
|
|
922
|
+
}}
|
|
923
|
+
/>
|
|
924
|
+
</div>
|
|
925
|
+
<div class="selection-editor-field">
|
|
926
|
+
<span class="selection-editor-label">Preview</span>
|
|
927
|
+
<div
|
|
928
|
+
class="selection-editor-math-preview"
|
|
929
|
+
${ref((el) => {
|
|
930
|
+
this.mathPreviewContainer = el ?? null;
|
|
931
|
+
if (el) {
|
|
932
|
+
this.runMathPreview();
|
|
933
|
+
}
|
|
934
|
+
})}
|
|
935
|
+
role="img"
|
|
936
|
+
aria-label="Equation preview"
|
|
937
|
+
></div>
|
|
938
|
+
${previewErr
|
|
939
|
+
? html `<p id="math-preview-error" class="selection-editor-math-error" role="alert">${previewErr}</p>`
|
|
940
|
+
: ''}
|
|
941
|
+
${previewWarnings.length
|
|
942
|
+
? html `
|
|
943
|
+
<p id="math-preview-warnings" class="selection-editor-math-warnings" role="status">
|
|
944
|
+
${previewWarnings.join(' ')}
|
|
945
|
+
</p>
|
|
946
|
+
`
|
|
947
|
+
: ''}
|
|
948
|
+
</div>
|
|
949
|
+
<div class="selection-editor-math-actions">
|
|
950
|
+
<button
|
|
951
|
+
type="button"
|
|
952
|
+
class="selection-editor-button selection-editor-button--primary"
|
|
953
|
+
@click=${this.applyMathDraft}
|
|
954
|
+
>
|
|
955
|
+
Apply
|
|
956
|
+
</button>
|
|
957
|
+
<span class="selection-editor-math-hint">Ctrl+Enter to apply, Escape to cancel</span>
|
|
958
|
+
</div>
|
|
959
|
+
<p class="selection-editor-math-docs">
|
|
960
|
+
<a
|
|
961
|
+
href="https://docs.mathjax.org/en/v4.0"
|
|
962
|
+
target="_blank"
|
|
963
|
+
rel="noopener noreferrer"
|
|
964
|
+
class="selection-editor-math-docs-link"
|
|
965
|
+
>MathJax 4 docs</a>
|
|
966
|
+
</p>
|
|
967
|
+
</div>
|
|
968
|
+
`;
|
|
969
|
+
}
|
|
970
|
+
scheduleMathPreview() {
|
|
971
|
+
if (this.mathPreviewDebounce) {
|
|
972
|
+
clearTimeout(this.mathPreviewDebounce);
|
|
973
|
+
}
|
|
974
|
+
this.mathPreviewDebounce = setTimeout(() => {
|
|
975
|
+
this.mathPreviewDebounce = null;
|
|
976
|
+
this.runMathPreview();
|
|
977
|
+
}, 300);
|
|
978
|
+
}
|
|
979
|
+
runMathPreview() {
|
|
980
|
+
if (!this.mathDraft || !this.mathPreviewContainer) {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
const tex = this.mathDraft.tex.trim() || '\\phantom{x}';
|
|
984
|
+
const display = this.mathDraft.style === 'display';
|
|
985
|
+
renderTexToSvg(tex, { display, metricsNode: this.mathPreviewContainer }).then((result) => {
|
|
986
|
+
if (!this.mathPreviewContainer) {
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
this.mathPreviewContainer.innerHTML = '';
|
|
990
|
+
if (result.svg) {
|
|
991
|
+
this.mathPreviewContainer.appendChild(result.svg);
|
|
992
|
+
this.mathPreviewResult = result.warnings?.length ? { warnings: result.warnings } : null;
|
|
993
|
+
}
|
|
994
|
+
else {
|
|
995
|
+
this.mathPreviewResult = {
|
|
996
|
+
...(result.error ? { error: result.error } : {}),
|
|
997
|
+
...(result.warnings?.length ? { warnings: result.warnings } : {}),
|
|
998
|
+
};
|
|
999
|
+
if (result.unavailable && result.error) {
|
|
1000
|
+
const p = document.createElement('p');
|
|
1001
|
+
p.className = 'selection-editor-math-error';
|
|
1002
|
+
p.setAttribute('role', 'alert');
|
|
1003
|
+
p.textContent = result.error;
|
|
1004
|
+
this.mathPreviewContainer.appendChild(p);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
this.requestUpdate();
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
applyMathDraft() {
|
|
1011
|
+
if (!this.elementInfo || this.elementInfo.type !== 'math' || this.elementInfo.position === null || !this.mathDraft) {
|
|
391
1012
|
return;
|
|
392
1013
|
}
|
|
393
1014
|
const runner = this.getCommandRunner();
|
|
394
|
-
|
|
395
|
-
|
|
1015
|
+
const setNodeAttrs = runner?.commands?.setNodeAttrs;
|
|
1016
|
+
if (!setNodeAttrs) {
|
|
396
1017
|
return;
|
|
397
1018
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
1019
|
+
const ok = setNodeAttrs(this.elementInfo.position, {
|
|
1020
|
+
tex: this.mathDraft.tex,
|
|
1021
|
+
style: this.mathDraft.style,
|
|
1022
|
+
label: this.mathDraft.label || undefined,
|
|
1023
|
+
});
|
|
1024
|
+
if (ok && this.resolvedEditor?.editorView) {
|
|
1025
|
+
const sel = this.resolvedEditor.editorView.state.selection;
|
|
1026
|
+
this.updateElementInfo({ anchor: sel.anchor, head: sel.head });
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
cancelMathDraft() {
|
|
1030
|
+
if (this.elementInfo?.type === 'math' && this.elementInfo.attrs) {
|
|
1031
|
+
const attrs = this.elementInfo.attrs;
|
|
1032
|
+
this.mathDraft = {
|
|
1033
|
+
tex: attrs.tex ?? '',
|
|
1034
|
+
style: attrs.style === 'display' ? 'display' : 'inline',
|
|
1035
|
+
label: attrs.label ?? '',
|
|
403
1036
|
};
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
1037
|
+
this.scheduleMathPreview();
|
|
1038
|
+
this.mathPreviewResult = null;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
handleMathKeydown(e) {
|
|
1042
|
+
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
|
|
1043
|
+
e.preventDefault();
|
|
1044
|
+
this.applyMathDraft();
|
|
1045
|
+
}
|
|
1046
|
+
else if (e.key === 'Escape') {
|
|
1047
|
+
e.preventDefault();
|
|
1048
|
+
this.cancelMathDraft();
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Render the citation sidebar: content editor, source fields, Update button.
|
|
1053
|
+
*/
|
|
1054
|
+
renderCitationSidebar() {
|
|
1055
|
+
return html `
|
|
1056
|
+
<div
|
|
1057
|
+
class="selection-editor-container selection-editor-citation"
|
|
1058
|
+
role="form"
|
|
1059
|
+
aria-label="Edit citation"
|
|
1060
|
+
@keydown=${this.handleCitationKeydown}
|
|
1061
|
+
>
|
|
1062
|
+
${this.renderSelectionControls()}
|
|
1063
|
+
<div class="selection-editor-field">
|
|
1064
|
+
<span class="selection-editor-label">Type:</span>
|
|
1065
|
+
<code class="selection-editor-value">citation</code>
|
|
1066
|
+
</div>
|
|
1067
|
+
<div class="selection-editor-field">
|
|
1068
|
+
<label class="selection-editor-label" for="citation-content">Raw citation text</label>
|
|
1069
|
+
<div
|
|
1070
|
+
id="citation-content"
|
|
1071
|
+
class="selection-editor-input selection-editor-citation-content selection-editor-citation-pm"
|
|
1072
|
+
role="textbox"
|
|
1073
|
+
aria-label="Raw citation text (supports bold and italic)"
|
|
1074
|
+
${ref((el) => {
|
|
1075
|
+
if (el) {
|
|
1076
|
+
this.mountCitationContentEditor(el);
|
|
410
1077
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
const selection = this.resolvedEditor.editorView.state.selection;
|
|
414
|
-
this.updateElementInfo({
|
|
415
|
-
anchor: selection.anchor,
|
|
416
|
-
head: selection.head,
|
|
417
|
-
});
|
|
1078
|
+
else {
|
|
1079
|
+
this.destroyCitationContentView();
|
|
418
1080
|
}
|
|
419
|
-
|
|
420
|
-
|
|
1081
|
+
})}
|
|
1082
|
+
></div>
|
|
1083
|
+
</div>
|
|
1084
|
+
<div class="selection-editor-attr-field">
|
|
1085
|
+
<span class="selection-editor-label">Semantic reference:</span>
|
|
1086
|
+
<div
|
|
1087
|
+
class="citation-source-adapter-root"
|
|
1088
|
+
${ref((el) => {
|
|
1089
|
+
if (el) {
|
|
1090
|
+
this.citationSourceContainer = el;
|
|
1091
|
+
this.mountCitationSourceAdapter(el);
|
|
1092
|
+
}
|
|
1093
|
+
else if (this.citationSourceContainer) {
|
|
1094
|
+
this.unmountCitationSourceAdapter(this.citationSourceContainer);
|
|
1095
|
+
this.citationSourceContainer = null;
|
|
421
1096
|
}
|
|
1097
|
+
})}
|
|
1098
|
+
></div>
|
|
1099
|
+
</div>
|
|
1100
|
+
<div class="selection-editor-citation-actions">
|
|
1101
|
+
<button
|
|
1102
|
+
type="button"
|
|
1103
|
+
class="selection-editor-button selection-editor-button--primary"
|
|
1104
|
+
@click=${this.applyCitationDraft}
|
|
1105
|
+
>
|
|
1106
|
+
Update
|
|
1107
|
+
</button>
|
|
1108
|
+
<span class="selection-editor-citation-hint">Ctrl+Enter to apply</span>
|
|
1109
|
+
</div>
|
|
1110
|
+
</div>
|
|
1111
|
+
`;
|
|
1112
|
+
}
|
|
1113
|
+
applyCitationDraft() {
|
|
1114
|
+
if (!this.elementInfo ||
|
|
1115
|
+
this.elementInfo.type !== 'citation' ||
|
|
1116
|
+
this.elementInfo.position === null ||
|
|
1117
|
+
!this.citationDraft) {
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
const runner = this.getCommandRunner();
|
|
1121
|
+
const updateCitationNode = runner?.commands?.updateCitationNode;
|
|
1122
|
+
if (!updateCitationNode) {
|
|
1123
|
+
console.warn('[sciflow-selection-editor] updateCitationNode command not available');
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
const content = this.citationContentView?.state?.doc?.firstChild?.content ?? this.citationDraft.content;
|
|
1127
|
+
const ok = updateCitationNode(this.elementInfo.position, {
|
|
1128
|
+
attrs: { source: this.citationDraft.sourceEncoded },
|
|
1129
|
+
content,
|
|
1130
|
+
});
|
|
1131
|
+
if (ok && this.resolvedEditor?.editorView) {
|
|
1132
|
+
const sel = this.resolvedEditor.editorView.state.selection;
|
|
1133
|
+
this.updateElementInfo({ anchor: sel.anchor, head: sel.head });
|
|
422
1134
|
}
|
|
423
|
-
|
|
424
|
-
|
|
1135
|
+
}
|
|
1136
|
+
handleCitationKeydown(e) {
|
|
1137
|
+
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
|
|
1138
|
+
e.preventDefault();
|
|
1139
|
+
this.applyCitationDraft();
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Render the cross-reference sidebar: display text + target, Update button.
|
|
1144
|
+
*/
|
|
1145
|
+
renderCrossReferenceSidebar() {
|
|
1146
|
+
const d = this.crossReferenceDraft;
|
|
1147
|
+
const options = this.getOutlineTargetOptions();
|
|
1148
|
+
const hasCurrentTarget = options.some((o) => o.id === d.href);
|
|
1149
|
+
return html `
|
|
1150
|
+
<div
|
|
1151
|
+
class="selection-editor-container selection-editor-citation"
|
|
1152
|
+
role="form"
|
|
1153
|
+
aria-label="Edit cross-reference"
|
|
1154
|
+
@keydown=${this.handleCrossReferenceKeydown}
|
|
1155
|
+
>
|
|
1156
|
+
${this.renderSelectionControls()}
|
|
1157
|
+
<div class="selection-editor-field">
|
|
1158
|
+
<span class="selection-editor-label">Type:</span>
|
|
1159
|
+
<code class="selection-editor-value">cross-reference</code>
|
|
1160
|
+
</div>
|
|
1161
|
+
<div class="selection-editor-field">
|
|
1162
|
+
<label class="selection-editor-label" for="crossref-content">Display text</label>
|
|
1163
|
+
<input
|
|
1164
|
+
id="crossref-content"
|
|
1165
|
+
type="text"
|
|
1166
|
+
class="selection-editor-input"
|
|
1167
|
+
.value=${d.content}
|
|
1168
|
+
placeholder="e.g. Chapter 1, Figure 1, Abstract"
|
|
1169
|
+
@input=${(e) => {
|
|
1170
|
+
this.crossReferenceDraft = {
|
|
1171
|
+
...this.crossReferenceDraft,
|
|
1172
|
+
content: e.target.value,
|
|
1173
|
+
};
|
|
1174
|
+
}}
|
|
1175
|
+
/>
|
|
1176
|
+
</div>
|
|
1177
|
+
<div class="selection-editor-field">
|
|
1178
|
+
<label class="selection-editor-label" for="crossref-target">Target</label>
|
|
1179
|
+
<select
|
|
1180
|
+
id="crossref-target"
|
|
1181
|
+
class="selection-editor-input selection-editor-select"
|
|
1182
|
+
.value=${d.href}
|
|
1183
|
+
@change=${(e) => {
|
|
1184
|
+
this.crossReferenceDraft = {
|
|
1185
|
+
...this.crossReferenceDraft,
|
|
1186
|
+
href: e.target.value,
|
|
1187
|
+
};
|
|
1188
|
+
}}
|
|
1189
|
+
>
|
|
1190
|
+
${!hasCurrentTarget && d.href
|
|
1191
|
+
? html `<option value="${d.href}">${d.href}</option>`
|
|
1192
|
+
: ''}
|
|
1193
|
+
${options.map((option) => html `<option value="${option.id}">${option.label ?? option.id}</option>`)}
|
|
1194
|
+
</select>
|
|
1195
|
+
</div>
|
|
1196
|
+
<div class="selection-editor-citation-actions">
|
|
1197
|
+
<button
|
|
1198
|
+
type="button"
|
|
1199
|
+
class="selection-editor-button selection-editor-button--primary"
|
|
1200
|
+
@click=${this.applyCrossReferenceDraft}
|
|
1201
|
+
>
|
|
1202
|
+
Update
|
|
1203
|
+
</button>
|
|
1204
|
+
<span class="selection-editor-citation-hint">Ctrl+Enter to apply</span>
|
|
1205
|
+
</div>
|
|
1206
|
+
</div>
|
|
1207
|
+
`;
|
|
1208
|
+
}
|
|
1209
|
+
applyCrossReferenceDraft() {
|
|
1210
|
+
if (!this.elementInfo ||
|
|
1211
|
+
this.elementInfo.type !== 'link' ||
|
|
1212
|
+
this.elementInfo.position === null ||
|
|
1213
|
+
!this.crossReferenceDraft) {
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
const runner = this.getCommandRunner();
|
|
1217
|
+
const updateCrossReferenceNode = runner?.commands?.updateCrossReferenceNode;
|
|
1218
|
+
if (!updateCrossReferenceNode) {
|
|
1219
|
+
console.warn('[sciflow-selection-editor] updateCrossReferenceNode command not available');
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
const ok = updateCrossReferenceNode(this.elementInfo.position, {
|
|
1223
|
+
attrs: { href: this.crossReferenceDraft.href },
|
|
1224
|
+
content: this.crossReferenceDraft.content,
|
|
1225
|
+
});
|
|
1226
|
+
if (ok && this.resolvedEditor?.editorView) {
|
|
1227
|
+
const sel = this.resolvedEditor.editorView.state.selection;
|
|
1228
|
+
this.updateElementInfo({ anchor: sel.anchor, head: sel.head });
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
handleCrossReferenceKeydown(e) {
|
|
1232
|
+
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
|
|
1233
|
+
e.preventDefault();
|
|
1234
|
+
this.applyCrossReferenceDraft();
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
resetDraftAttrs() {
|
|
1238
|
+
this.draftAttrs = null;
|
|
1239
|
+
this.draftDirty = false;
|
|
1240
|
+
}
|
|
1241
|
+
getEffectiveAttrs() {
|
|
1242
|
+
const base = (this.elementInfo?.attrs ?? {});
|
|
1243
|
+
if (!this.draftAttrs || Object.keys(this.draftAttrs).length === 0) {
|
|
1244
|
+
return base;
|
|
1245
|
+
}
|
|
1246
|
+
return { ...base, ...this.draftAttrs };
|
|
1247
|
+
}
|
|
1248
|
+
setDraftAttr(attrName, value) {
|
|
1249
|
+
const currentAttrs = (this.elementInfo?.attrs ?? {});
|
|
1250
|
+
const previous = currentAttrs[attrName] ?? null;
|
|
1251
|
+
const nextDraft = { ...(this.draftAttrs ?? {}) };
|
|
1252
|
+
if (previous === value) {
|
|
1253
|
+
delete nextDraft[attrName];
|
|
1254
|
+
}
|
|
1255
|
+
else {
|
|
1256
|
+
nextDraft[attrName] = value;
|
|
1257
|
+
}
|
|
1258
|
+
this.draftAttrs = Object.keys(nextDraft).length > 0 ? nextDraft : null;
|
|
1259
|
+
this.draftDirty = Boolean(this.draftAttrs && Object.keys(this.draftAttrs).length > 0);
|
|
1260
|
+
}
|
|
1261
|
+
applyDraftAttrs() {
|
|
1262
|
+
if (!this.draftAttrs || !this.draftDirty || !this.elementInfo || this.elementInfo.position === null) {
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
const runner = this.getCommandRunner();
|
|
1266
|
+
const setNodeAttrsCommand = runner?.commands?.setNodeAttrs;
|
|
1267
|
+
if (!setNodeAttrsCommand) {
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
const success = setNodeAttrsCommand(this.elementInfo.position, this.draftAttrs);
|
|
1271
|
+
if (success && this.resolvedEditor?.editorView) {
|
|
1272
|
+
const selection = this.resolvedEditor.editorView.state.selection;
|
|
1273
|
+
this.resetDraftAttrs();
|
|
1274
|
+
this.updateElementInfo({
|
|
1275
|
+
anchor: selection.anchor,
|
|
1276
|
+
head: selection.head,
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
revertDraftAttrs() {
|
|
1281
|
+
this.resetDraftAttrs();
|
|
1282
|
+
}
|
|
1283
|
+
renderDraftActions() {
|
|
1284
|
+
if (!this.draftDirty) {
|
|
1285
|
+
return null;
|
|
1286
|
+
}
|
|
1287
|
+
return html `
|
|
1288
|
+
<div class="selection-editor-actions">
|
|
1289
|
+
<button
|
|
1290
|
+
type="button"
|
|
1291
|
+
class="selection-editor-button selection-editor-button--primary"
|
|
1292
|
+
@click=${this.applyDraftAttrs}
|
|
1293
|
+
>
|
|
1294
|
+
Apply
|
|
1295
|
+
</button>
|
|
1296
|
+
<button
|
|
1297
|
+
type="button"
|
|
1298
|
+
class="selection-editor-button"
|
|
1299
|
+
@click=${this.revertDraftAttrs}
|
|
1300
|
+
>
|
|
1301
|
+
Revert
|
|
1302
|
+
</button>
|
|
1303
|
+
<span class="selection-editor-hint">Ctrl+Enter to apply, Escape to revert</span>
|
|
1304
|
+
</div>
|
|
1305
|
+
`;
|
|
1306
|
+
}
|
|
1307
|
+
renderSelectionControls() {
|
|
1308
|
+
return html `
|
|
1309
|
+
<div class="selection-editor-controls">
|
|
1310
|
+
<button
|
|
1311
|
+
type="button"
|
|
1312
|
+
class="selection-editor-button selection-editor-button--secondary"
|
|
1313
|
+
@click=${this.togglePinSelection}
|
|
1314
|
+
aria-pressed=${this.pinSelection ? 'true' : 'false'}
|
|
1315
|
+
>
|
|
1316
|
+
${this.pinSelection ? 'Unpin selection' : 'Pin selection'}
|
|
1317
|
+
</button>
|
|
1318
|
+
</div>
|
|
1319
|
+
`;
|
|
1320
|
+
}
|
|
1321
|
+
togglePinSelection() {
|
|
1322
|
+
this.pinSelection = !this.pinSelection;
|
|
1323
|
+
if (!this.pinSelection && this.resolvedEditor?.editorView) {
|
|
1324
|
+
const selection = this.resolvedEditor.editorView.state.selection;
|
|
1325
|
+
this.scheduleSelectionUpdate({
|
|
1326
|
+
anchor: selection.anchor,
|
|
1327
|
+
head: selection.head,
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
handleGenericKeydown(e) {
|
|
1332
|
+
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
|
|
1333
|
+
e.preventDefault();
|
|
1334
|
+
this.applyDraftAttrs();
|
|
1335
|
+
}
|
|
1336
|
+
else if (e.key === 'Escape') {
|
|
1337
|
+
e.preventDefault();
|
|
1338
|
+
this.revertDraftAttrs();
|
|
425
1339
|
}
|
|
426
1340
|
}
|
|
427
1341
|
/**
|
|
@@ -461,6 +1375,27 @@ __decorate([
|
|
|
461
1375
|
__decorate([
|
|
462
1376
|
state()
|
|
463
1377
|
], SciFlowSelectionEditorElement.prototype, "elementInfo", void 0);
|
|
1378
|
+
__decorate([
|
|
1379
|
+
state()
|
|
1380
|
+
], SciFlowSelectionEditorElement.prototype, "mathDraft", void 0);
|
|
1381
|
+
__decorate([
|
|
1382
|
+
state()
|
|
1383
|
+
], SciFlowSelectionEditorElement.prototype, "mathPreviewResult", void 0);
|
|
1384
|
+
__decorate([
|
|
1385
|
+
state()
|
|
1386
|
+
], SciFlowSelectionEditorElement.prototype, "citationDraft", void 0);
|
|
1387
|
+
__decorate([
|
|
1388
|
+
state()
|
|
1389
|
+
], SciFlowSelectionEditorElement.prototype, "crossReferenceDraft", void 0);
|
|
1390
|
+
__decorate([
|
|
1391
|
+
state()
|
|
1392
|
+
], SciFlowSelectionEditorElement.prototype, "draftAttrs", void 0);
|
|
1393
|
+
__decorate([
|
|
1394
|
+
state()
|
|
1395
|
+
], SciFlowSelectionEditorElement.prototype, "draftDirty", void 0);
|
|
1396
|
+
__decorate([
|
|
1397
|
+
state()
|
|
1398
|
+
], SciFlowSelectionEditorElement.prototype, "pinSelection", void 0);
|
|
464
1399
|
SciFlowSelectionEditorElement = __decorate([
|
|
465
1400
|
customElement('sciflow-selection-editor')
|
|
466
1401
|
], SciFlowSelectionEditorElement);
|