@refrakt-md/editor 0.8.1 → 0.8.2
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/app/dist/assets/{index-BgCNqcSo.js → index-80NtMar1.js} +1 -1
- package/app/dist/assets/index-B6H6LF1M.css +1 -0
- package/app/dist/assets/{index-BLuaHLN3.js → index-BDj1XPol.js} +1 -1
- package/app/dist/assets/{index-D_Y6J00B.js → index-BXe1fKaT.js} +1 -1
- package/app/dist/assets/{index-ZLvRNfLb.js → index-BfxTGrHB.js} +1 -1
- package/app/dist/assets/{index-D3TQo8gu.js → index-Bn8ajfVl.js} +1 -1
- package/app/dist/assets/{index-DgIg-QAA.js → index-CCkzIGTi.js} +2 -2
- package/app/dist/assets/{index-COIPZ34u.js → index-CXeK-dZx.js} +1 -1
- package/app/dist/assets/{index-DW2zI-Ss.js → index-CaRBCHaX.js} +1 -1
- package/app/dist/assets/index-Cd12jZId.js +479 -0
- package/app/dist/assets/{index-DmY6uqAw.js → index-Cgbvx23V.js} +1 -1
- package/app/dist/assets/{index-CW02bulk.js → index-D5ucdUTo.js} +1 -1
- package/app/dist/assets/{index-BwFn9q4x.js → index-DGYxLhpR.js} +1 -1
- package/app/dist/assets/{index-CqHjo2YT.js → index-DNJBunzP.js} +1 -1
- package/app/dist/assets/{index-CeU_s7BB.js → index-DNtuldOx.js} +1 -1
- package/app/dist/assets/{index-C72UC2ga.js → index-DQUOY-pF.js} +1 -1
- package/app/dist/assets/{index-CXFMPmtf.js → index-DskvyNKT.js} +1 -1
- package/app/dist/assets/{index-BBinZAiy.js → index-aPeHMqUX.js} +1 -1
- package/app/dist/assets/{index-DVM3uoxc.js → index-dGztG-54.js} +1 -1
- package/app/dist/assets/{index-DzHt8ZRh.js → index-xo7v6nRB.js} +1 -1
- package/app/dist/index.html +2 -2
- package/app/src/lib/api/client.ts +32 -0
- package/app/src/lib/components/ActionEditPopover.svelte +41 -19
- package/app/src/lib/components/BlockCard.svelte +74 -17
- package/app/src/lib/components/BlockEditPanel.svelte +142 -9
- package/app/src/lib/components/BlockEditor.svelte +154 -2
- package/app/src/lib/components/CodeEditPopover.svelte +281 -63
- package/app/src/lib/components/ContentModelTree.svelte +340 -67
- package/app/src/lib/components/IconPickerPopover.svelte +389 -0
- package/app/src/lib/components/ImageEditPopover.svelte +519 -0
- package/app/src/lib/components/InlineEditPopover.svelte +79 -56
- package/app/src/lib/components/RuneAttributes.svelte +51 -0
- package/app/src/lib/editor/block-parser.ts +152 -7
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +129 -1
- package/dist/server.js.map +1 -1
- package/package.json +6 -6
- package/app/dist/assets/index-BD2EBUrQ.css +0 -1
- package/app/dist/assets/index-BlAOhWAQ.js +0 -453
|
@@ -19,9 +19,14 @@
|
|
|
19
19
|
removeFieldContent,
|
|
20
20
|
appendListItem,
|
|
21
21
|
removeListItem,
|
|
22
|
+
reorderListItem,
|
|
23
|
+
appendGreedyItem,
|
|
24
|
+
removeGreedyItem,
|
|
25
|
+
reorderGreedyItem,
|
|
26
|
+
splitListItems,
|
|
22
27
|
} from '../editor/block-parser.js';
|
|
23
|
-
import { resolveContentStructure } from '../editor/content-model-resolver.js';
|
|
24
|
-
import type { SectionMapping } from '../editor/section-mapper.js';
|
|
28
|
+
import { resolveContentStructure, type ResolvedField } from '../editor/content-model-resolver.js';
|
|
29
|
+
import type { SectionMapping, CommandMapping } from '../editor/section-mapper.js';
|
|
25
30
|
import { stripInlineMarkdown } from '../editor/inline-markdown.js';
|
|
26
31
|
import RuneAttributes from './RuneAttributes.svelte';
|
|
27
32
|
import ContentTree from './ContentTree.svelte';
|
|
@@ -39,9 +44,10 @@
|
|
|
39
44
|
onremove: () => void;
|
|
40
45
|
onclose: () => void;
|
|
41
46
|
oneditfield?: (dataName: string, inlineSource: string, rect: DOMRect, mapping: SectionMapping) => void;
|
|
47
|
+
oneditcode?: (code: string, language: string, rect: DOMRect, mapping: CommandMapping) => void;
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
let { block, runeMap, runes, aggregated = {}, initialRuneIndex = null, onupdate, onremove, onclose, oneditfield }: Props = $props();
|
|
50
|
+
let { block, runeMap, runes, aggregated = {}, initialRuneIndex = null, onupdate, onremove, onclose, oneditfield, oneditcode }: Props = $props();
|
|
45
51
|
|
|
46
52
|
let label = $derived(blockLabel(block));
|
|
47
53
|
|
|
@@ -220,18 +226,54 @@
|
|
|
220
226
|
applyFieldChange(content => removeFieldContent(content, resolvedStructure!, fieldName, zoneName));
|
|
221
227
|
}
|
|
222
228
|
|
|
229
|
+
function findResolvedField(fieldName: string, zoneName?: string): ResolvedField | null {
|
|
230
|
+
if (!resolvedStructure) return null;
|
|
231
|
+
if (resolvedStructure.type === 'sequence') {
|
|
232
|
+
return resolvedStructure.fields.find(f => f.name === fieldName) ?? null;
|
|
233
|
+
}
|
|
234
|
+
if (resolvedStructure.type === 'delimited' && zoneName) {
|
|
235
|
+
const zone = resolvedStructure.zones.find(z => z.name === zoneName);
|
|
236
|
+
return zone?.fields.find(f => f.name === fieldName) ?? null;
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function isGreedyItemField(field: ResolvedField): boolean {
|
|
242
|
+
return field.greedy && field.match !== 'any';
|
|
243
|
+
}
|
|
244
|
+
|
|
223
245
|
function handleAppendItem(fieldName: string, zoneName?: string) {
|
|
224
246
|
if (!resolvedStructure) return;
|
|
225
|
-
|
|
247
|
+
const field = findResolvedField(fieldName, zoneName);
|
|
248
|
+
if (field && isGreedyItemField(field)) {
|
|
249
|
+
applyFieldChange(content => appendGreedyItem(content, resolvedStructure!, fieldName, zoneName));
|
|
250
|
+
} else {
|
|
251
|
+
applyFieldChange(content => appendListItem(content, resolvedStructure!, fieldName, zoneName));
|
|
252
|
+
}
|
|
226
253
|
}
|
|
227
254
|
|
|
228
255
|
function handleRemoveListItem(fieldName: string, itemIndex: number, zoneName?: string) {
|
|
229
256
|
if (!resolvedStructure) return;
|
|
230
|
-
|
|
257
|
+
const field = findResolvedField(fieldName, zoneName);
|
|
258
|
+
if (field && isGreedyItemField(field)) {
|
|
259
|
+
applyFieldChange(content => removeGreedyItem(content, resolvedStructure!, fieldName, itemIndex, zoneName));
|
|
260
|
+
} else {
|
|
261
|
+
applyFieldChange(content => removeListItem(content, resolvedStructure!, fieldName, itemIndex, zoneName));
|
|
262
|
+
}
|
|
231
263
|
}
|
|
232
264
|
|
|
233
|
-
function
|
|
234
|
-
if (!resolvedStructure
|
|
265
|
+
function handleReorderListItem(fieldName: string, fromIndex: number, toIndex: number, zoneName?: string) {
|
|
266
|
+
if (!resolvedStructure) return;
|
|
267
|
+
const field = findResolvedField(fieldName, zoneName);
|
|
268
|
+
if (field && isGreedyItemField(field)) {
|
|
269
|
+
applyFieldChange(content => reorderGreedyItem(content, resolvedStructure!, fieldName, fromIndex, toIndex, zoneName));
|
|
270
|
+
} else {
|
|
271
|
+
applyFieldChange(content => reorderListItem(content, resolvedStructure!, fieldName, fromIndex, toIndex, zoneName));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function handleEditField(fieldName: string, rect: DOMRect, zoneName?: string, nodeIndex?: number) {
|
|
276
|
+
if (!resolvedStructure) return;
|
|
235
277
|
// Find the field in the resolved structure
|
|
236
278
|
let field;
|
|
237
279
|
if (resolvedStructure.type === 'sequence') {
|
|
@@ -240,9 +282,30 @@
|
|
|
240
282
|
const zone = resolvedStructure.zones.find(z => z.name === zoneName);
|
|
241
283
|
field = zone?.fields.find(f => f.name === fieldName);
|
|
242
284
|
}
|
|
243
|
-
if (!field || !field.filled
|
|
285
|
+
if (!field || !field.filled) return;
|
|
286
|
+
|
|
287
|
+
// Pick the target node — use nodeIndex for greedy fields, default to single-node fields
|
|
288
|
+
const targetIndex = nodeIndex ?? 0;
|
|
289
|
+
if (targetIndex >= field.nodes.length) return;
|
|
290
|
+
if (nodeIndex === undefined && field.nodes.length !== 1) return;
|
|
291
|
+
|
|
292
|
+
const targetNode = field.nodes[targetIndex];
|
|
293
|
+
const source = targetNode.source;
|
|
294
|
+
|
|
295
|
+
// Fence nodes → open code editor instead of inline text editor
|
|
296
|
+
if (targetNode.type === 'fence' && oneditcode) {
|
|
297
|
+
const code = targetNode.fenceCode ?? '';
|
|
298
|
+
const language = targetNode.fenceLanguage ?? '';
|
|
299
|
+
const opener = source.split('\n')[0];
|
|
300
|
+
const delimMatch = opener.match(/^(`{3,}|~{3,})/);
|
|
301
|
+
const delimiter = delimMatch ? delimMatch[1] : '```';
|
|
302
|
+
const mapping: CommandMapping = { source, code, language, opener, delimiter };
|
|
303
|
+
oneditcode(code, language, rect, mapping);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (!oneditfield) return;
|
|
244
308
|
|
|
245
|
-
const source = field.nodes[0].source;
|
|
246
309
|
const trimmed = source.trim();
|
|
247
310
|
|
|
248
311
|
// Strip markdown prefix (heading markers, blockquote markers)
|
|
@@ -270,6 +333,70 @@
|
|
|
270
333
|
oneditfield(fieldName, inlineContent, rect, mapping);
|
|
271
334
|
}
|
|
272
335
|
|
|
336
|
+
function handleEditListItem(fieldName: string, itemIndex: number, rect: DOMRect, zoneName?: string) {
|
|
337
|
+
if (!resolvedStructure || !oneditfield) return;
|
|
338
|
+
// Find the field in the resolved structure
|
|
339
|
+
let field;
|
|
340
|
+
if (resolvedStructure.type === 'sequence') {
|
|
341
|
+
field = resolvedStructure.fields.find(f => f.name === fieldName);
|
|
342
|
+
} else if (resolvedStructure.type === 'delimited' && zoneName) {
|
|
343
|
+
const zone = resolvedStructure.zones.find(z => z.name === zoneName);
|
|
344
|
+
field = zone?.fields.find(f => f.name === fieldName);
|
|
345
|
+
}
|
|
346
|
+
if (!field || !field.filled || field.nodes.length === 0) return;
|
|
347
|
+
|
|
348
|
+
const listNode = field.nodes[0];
|
|
349
|
+
const items = splitListItems(listNode.source);
|
|
350
|
+
if (itemIndex < 0 || itemIndex >= items.length) return;
|
|
351
|
+
|
|
352
|
+
const itemSource = items[itemIndex];
|
|
353
|
+
// Strip the list marker to get inline content
|
|
354
|
+
const markerMatch = itemSource.match(/^([-*+]\s+|\d+\.\s+)/);
|
|
355
|
+
const prefix = markerMatch ? markerMatch[1] : '';
|
|
356
|
+
const inlineContent = markerMatch ? itemSource.slice(prefix.length) : itemSource;
|
|
357
|
+
|
|
358
|
+
const mapping: SectionMapping = {
|
|
359
|
+
dataName: `${fieldName}[${itemIndex}]`,
|
|
360
|
+
text: stripInlineMarkdown(inlineContent),
|
|
361
|
+
source: itemSource,
|
|
362
|
+
sourcePrefix: prefix,
|
|
363
|
+
inlineSource: inlineContent,
|
|
364
|
+
};
|
|
365
|
+
oneditfield(`${fieldName}[${itemIndex}]`, inlineContent, rect, mapping);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function handleNavigateRune(fieldName: string, nodeIndex: number, zoneName?: string) {
|
|
369
|
+
if (!resolvedStructure) return;
|
|
370
|
+
|
|
371
|
+
// Find the field
|
|
372
|
+
let field;
|
|
373
|
+
if (resolvedStructure.type === 'sequence') {
|
|
374
|
+
field = resolvedStructure.fields.find(f => f.name === fieldName);
|
|
375
|
+
} else if (resolvedStructure.type === 'delimited' && zoneName) {
|
|
376
|
+
const zone = resolvedStructure.zones.find(z => z.name === zoneName);
|
|
377
|
+
field = zone?.fields.find(f => f.name === fieldName);
|
|
378
|
+
}
|
|
379
|
+
if (!field || nodeIndex < 0 || nodeIndex >= field.nodes.length) return;
|
|
380
|
+
|
|
381
|
+
const targetNode = field.nodes[nodeIndex];
|
|
382
|
+
if (targetNode.type !== 'rune') return;
|
|
383
|
+
|
|
384
|
+
// Find this node's index in the effectiveContentTree
|
|
385
|
+
const tree = effectiveContentTree;
|
|
386
|
+
const treeIndex = tree.findIndex(n => n.source === targetNode.source);
|
|
387
|
+
if (treeIndex === -1) return;
|
|
388
|
+
|
|
389
|
+
// Navigate to the nested rune
|
|
390
|
+
if (activeNode?.type === 'rune') {
|
|
391
|
+
activePath = [...activePath, treeIndex];
|
|
392
|
+
} else {
|
|
393
|
+
activePath = [treeIndex];
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Switch to settings tab to show the nested rune's settings
|
|
397
|
+
activeTab = 'settings';
|
|
398
|
+
}
|
|
399
|
+
|
|
273
400
|
// ── Edit handlers ────────────────────────────────────────────
|
|
274
401
|
|
|
275
402
|
function handleHeadingTextChange(text: string) {
|
|
@@ -558,7 +685,10 @@
|
|
|
558
685
|
onremovefield={handleRemoveField}
|
|
559
686
|
onappenditem={handleAppendItem}
|
|
560
687
|
onremovelistitem={handleRemoveListItem}
|
|
688
|
+
onreorderlistitem={handleReorderListItem}
|
|
561
689
|
oneditfield={handleEditField}
|
|
690
|
+
oneditlistitem={handleEditListItem}
|
|
691
|
+
onnavigaterune={handleNavigateRune}
|
|
562
692
|
onfieldselect={handleFieldSelect}
|
|
563
693
|
{selectedField}
|
|
564
694
|
/>
|
|
@@ -732,6 +862,8 @@
|
|
|
732
862
|
.edit-panel {
|
|
733
863
|
display: flex;
|
|
734
864
|
flex-direction: column;
|
|
865
|
+
flex: 1;
|
|
866
|
+
min-height: 0;
|
|
735
867
|
}
|
|
736
868
|
|
|
737
869
|
.edit-panel__top {
|
|
@@ -885,6 +1017,7 @@
|
|
|
885
1017
|
.edit-panel__content-editor {
|
|
886
1018
|
display: flex;
|
|
887
1019
|
flex-direction: column;
|
|
1020
|
+
flex-shrink: 0;
|
|
888
1021
|
overflow: hidden;
|
|
889
1022
|
margin-left: calc(-1 * var(--ed-space-5));
|
|
890
1023
|
margin-right: calc(-1 * var(--ed-space-5));
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
type ParsedBlock,
|
|
13
13
|
type RuneBlock,
|
|
14
14
|
} from '../editor/block-parser.js';
|
|
15
|
-
import { findSectionMapping, applySectionEdit, findActionMapping, applyActionEdit, findCommandMapping, applyCommandEdit, type SectionMapping, type ActionMapping, type CommandMapping } from '../editor/section-mapper.js';
|
|
15
|
+
import { findSectionMapping, applySectionEdit, findActionMapping, applyActionEdit, findCommandMapping, applyCommandEdit, applyLanguageEdit, findImageMapping, applyImageEdit, findIconMapping, applyIconEdit, type SectionMapping, type ActionMapping, type CommandMapping, type ImageMapping, type IconMapping } from '../editor/section-mapper.js';
|
|
16
16
|
import { stripInlineMarkdown } from '../editor/inline-markdown.js';
|
|
17
17
|
import { editorState } from '../state/editor.svelte.js';
|
|
18
18
|
import BlockCard from './BlockCard.svelte';
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
import InlineEditPopover from './InlineEditPopover.svelte';
|
|
24
24
|
import ActionEditPopover from './ActionEditPopover.svelte';
|
|
25
25
|
import CodeEditPopover from './CodeEditPopover.svelte';
|
|
26
|
+
import ImageEditPopover from './ImageEditPopover.svelte';
|
|
27
|
+
import IconPickerPopover from './IconPickerPopover.svelte';
|
|
26
28
|
|
|
27
29
|
interface Props {
|
|
28
30
|
bodyContent: string;
|
|
@@ -456,6 +458,30 @@
|
|
|
456
458
|
return;
|
|
457
459
|
}
|
|
458
460
|
|
|
461
|
+
if (info.editType === 'image') {
|
|
462
|
+
const imgSrc = info.href ?? '';
|
|
463
|
+
const mapping = findImageMapping(rb.innerContent, imgSrc);
|
|
464
|
+
if (!mapping) return;
|
|
465
|
+
|
|
466
|
+
imageEdit = {
|
|
467
|
+
blockIndex: index,
|
|
468
|
+
mapping,
|
|
469
|
+
};
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (info.editType === 'icon') {
|
|
474
|
+
const iconName = info.iconName ?? '';
|
|
475
|
+
const mapping = findIconMapping(rb.innerContent, iconName);
|
|
476
|
+
if (!mapping) return;
|
|
477
|
+
|
|
478
|
+
iconEdit = {
|
|
479
|
+
blockIndex: index,
|
|
480
|
+
mapping,
|
|
481
|
+
};
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
|
|
459
485
|
// Default: inline text editing
|
|
460
486
|
const mapping = findSectionMapping(rb.innerContent, info.dataName, info.text);
|
|
461
487
|
if (!mapping) return;
|
|
@@ -580,10 +606,103 @@
|
|
|
580
606
|
commandEdit = null;
|
|
581
607
|
}
|
|
582
608
|
|
|
609
|
+
function handleLanguageChange(newLanguage: string) {
|
|
610
|
+
if (!commandEdit) return;
|
|
611
|
+
const block = blocks[commandEdit.blockIndex];
|
|
612
|
+
if (block.type !== 'rune') return;
|
|
613
|
+
const rb = block as RuneBlock;
|
|
614
|
+
|
|
615
|
+
const newInner = applyLanguageEdit(rb.innerContent, commandEdit.mapping, newLanguage);
|
|
616
|
+
const updated: RuneBlock = { ...rb, innerContent: newInner, source: '' };
|
|
617
|
+
updated.source = rebuildRuneSource(updated);
|
|
618
|
+
|
|
619
|
+
// Update the mapping to reflect the new language and opener
|
|
620
|
+
const afterDelimiter = commandEdit.mapping.opener.slice(commandEdit.mapping.delimiter.length);
|
|
621
|
+
const infoString = afterDelimiter.replace(/^\w*/, '').trim();
|
|
622
|
+
const newOpener = commandEdit.mapping.delimiter + newLanguage + (infoString ? ' ' + infoString : '');
|
|
623
|
+
const newSource = newOpener + '\n' + commandEdit.mapping.code + '\n' + commandEdit.mapping.delimiter;
|
|
624
|
+
|
|
625
|
+
commandEdit = {
|
|
626
|
+
...commandEdit,
|
|
627
|
+
mapping: {
|
|
628
|
+
...commandEdit.mapping,
|
|
629
|
+
language: newLanguage,
|
|
630
|
+
opener: newOpener,
|
|
631
|
+
source: newSource,
|
|
632
|
+
},
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
handleUpdateBlock(commandEdit.blockIndex, updated);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// ── Image editing ────────────────────────────────────────────
|
|
639
|
+
|
|
640
|
+
let imageEdit: {
|
|
641
|
+
blockIndex: number;
|
|
642
|
+
mapping: ImageMapping;
|
|
643
|
+
} | null = $state(null);
|
|
644
|
+
|
|
645
|
+
function handleImageEditChange(newSrc: string, newAlt: string) {
|
|
646
|
+
if (!imageEdit) return;
|
|
647
|
+
const block = blocks[imageEdit.blockIndex];
|
|
648
|
+
if (block.type !== 'rune') return;
|
|
649
|
+
const rb = block as RuneBlock;
|
|
650
|
+
|
|
651
|
+
const newInner = applyImageEdit(rb.innerContent, imageEdit.mapping, newAlt, newSrc);
|
|
652
|
+
const updated: RuneBlock = { ...rb, innerContent: newInner, source: '' };
|
|
653
|
+
updated.source = rebuildRuneSource(updated);
|
|
654
|
+
|
|
655
|
+
handleUpdateBlock(imageEdit.blockIndex, updated);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function handleImageRemove() {
|
|
659
|
+
if (!imageEdit) return;
|
|
660
|
+
const blockIndex = imageEdit.blockIndex;
|
|
661
|
+
const block = blocks[blockIndex];
|
|
662
|
+
if (block.type !== 'rune') return;
|
|
663
|
+
const rb = block as RuneBlock;
|
|
664
|
+
|
|
665
|
+
const newInner = rb.innerContent.replace(imageEdit.mapping.source + '\n', '').replace(imageEdit.mapping.source, '');
|
|
666
|
+
const updated: RuneBlock = { ...rb, innerContent: newInner, source: '' };
|
|
667
|
+
updated.source = rebuildRuneSource(updated);
|
|
668
|
+
|
|
669
|
+
imageEdit = null;
|
|
670
|
+
handleUpdateBlock(blockIndex, updated);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function closeImageEdit() {
|
|
674
|
+
imageEdit = null;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// ── Icon editing ────────────────────────────────────────────
|
|
678
|
+
|
|
679
|
+
let iconEdit: {
|
|
680
|
+
blockIndex: number;
|
|
681
|
+
mapping: IconMapping;
|
|
682
|
+
} | null = $state(null);
|
|
683
|
+
|
|
684
|
+
function handleIconEditChange(newIconName: string) {
|
|
685
|
+
if (!iconEdit) return;
|
|
686
|
+
const block = blocks[iconEdit.blockIndex];
|
|
687
|
+
if (block.type !== 'rune') return;
|
|
688
|
+
const rb = block as RuneBlock;
|
|
689
|
+
|
|
690
|
+
const newInner = applyIconEdit(rb.innerContent, iconEdit.mapping, newIconName);
|
|
691
|
+
const updated: RuneBlock = { ...rb, innerContent: newInner, source: '' };
|
|
692
|
+
updated.source = rebuildRuneSource(updated);
|
|
693
|
+
|
|
694
|
+
handleUpdateBlock(iconEdit.blockIndex, updated);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
function closeIconEdit() {
|
|
698
|
+
iconEdit = null;
|
|
699
|
+
}
|
|
700
|
+
|
|
583
701
|
// ── Field edit from Structure tab ──────────────────────────────
|
|
584
702
|
|
|
585
703
|
function handleFieldEdit(dataName: string, inlineSource: string, rect: DOMRect, mapping: SectionMapping) {
|
|
586
704
|
if (activeIndex === null) return;
|
|
705
|
+
commandEdit = null;
|
|
587
706
|
inlineEdit = {
|
|
588
707
|
blockIndex: activeIndex,
|
|
589
708
|
dataName,
|
|
@@ -593,6 +712,16 @@
|
|
|
593
712
|
};
|
|
594
713
|
}
|
|
595
714
|
|
|
715
|
+
function handleFieldCodeEdit(code: string, language: string, rect: DOMRect, mapping: CommandMapping) {
|
|
716
|
+
if (activeIndex === null) return;
|
|
717
|
+
inlineEdit = null;
|
|
718
|
+
commandEdit = {
|
|
719
|
+
blockIndex: activeIndex,
|
|
720
|
+
rect,
|
|
721
|
+
mapping,
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
|
|
596
725
|
// Group runes by category for the insert menu
|
|
597
726
|
let runesByCategory = $derived.by(() => {
|
|
598
727
|
const map = new Map<string, RuneInfo[]>();
|
|
@@ -741,6 +870,7 @@
|
|
|
741
870
|
onremove={() => { const idx = activeIndex!; activeIndex = null; anchorPoint = null; pendingRuneIndex = null; handleRemoveBlock(idx); }}
|
|
742
871
|
onclose={() => { activeIndex = null; anchorPoint = null; pendingRuneIndex = null; }}
|
|
743
872
|
oneditfield={handleFieldEdit}
|
|
873
|
+
oneditcode={handleFieldCodeEdit}
|
|
744
874
|
/>
|
|
745
875
|
{/key}
|
|
746
876
|
{/if}
|
|
@@ -783,10 +913,30 @@
|
|
|
783
913
|
code={commandEdit.mapping.code}
|
|
784
914
|
language={commandEdit.mapping.language}
|
|
785
915
|
onchange={handleCommandEditChange}
|
|
916
|
+
onlanguagechange={handleLanguageChange}
|
|
786
917
|
onremove={handleCommandRemove}
|
|
787
918
|
onclose={closeCommandEdit}
|
|
788
919
|
/>
|
|
789
920
|
{/if}
|
|
921
|
+
|
|
922
|
+
{#if imageEdit}
|
|
923
|
+
<ImageEditPopover
|
|
924
|
+
currentSrc={imageEdit.mapping.src}
|
|
925
|
+
currentAlt={imageEdit.mapping.alt}
|
|
926
|
+
onchange={(src, alt) => { handleImageEditChange(src, alt); closeImageEdit(); }}
|
|
927
|
+
onremove={handleImageRemove}
|
|
928
|
+
onclose={closeImageEdit}
|
|
929
|
+
/>
|
|
930
|
+
{/if}
|
|
931
|
+
|
|
932
|
+
{#if iconEdit}
|
|
933
|
+
<IconPickerPopover
|
|
934
|
+
icons={themeConfig?.icons ?? {}}
|
|
935
|
+
currentIcon={iconEdit.mapping.name}
|
|
936
|
+
onchange={(name) => { handleIconEditChange(name); closeIconEdit(); }}
|
|
937
|
+
onclose={closeIconEdit}
|
|
938
|
+
/>
|
|
939
|
+
{/if}
|
|
790
940
|
</div>
|
|
791
941
|
|
|
792
942
|
|
|
@@ -1005,7 +1155,9 @@
|
|
|
1005
1155
|
.block-editor__popover {
|
|
1006
1156
|
position: fixed;
|
|
1007
1157
|
width: 420px;
|
|
1008
|
-
overflow
|
|
1158
|
+
overflow: hidden;
|
|
1159
|
+
display: flex;
|
|
1160
|
+
flex-direction: column;
|
|
1009
1161
|
background: var(--ed-surface-0);
|
|
1010
1162
|
border-radius: var(--ed-radius-lg);
|
|
1011
1163
|
border: 1px solid var(--ed-border-default);
|