Skip to content

Commit 575593a

Browse files
kvapsclaude
andcommitted
feat: Esc cancels creation of a fresh node
Wrap insertSibling/insertParent/addChild so that any beginEdit fired during the call is treated as fresh-node creation, regardless of the order between the operation bus event and editTopic. On Esc the freshly inserted node is removed (which also unwinds the surgical markdown insert), restoring the previous state. Also drop leftover fitToView debug logging. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent e0ad808 commit 575593a

1 file changed

Lines changed: 54 additions & 13 deletions

File tree

src/components/MindMap.vue

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const store = useDocumentStore()
2020
const host = ref<HTMLDivElement | null>(null)
2121
let mind: MindElixirInstance | null = null
2222
let lastInternalMarkdown: string | null = null
23+
let freshNodeId: string | null = null
24+
let creatingNode = false
2325
const lastChildMap = new Map<string, string>()
2426
let refreshTimer: number | null = null
2527
let resizeObserver: ResizeObserver | null = null
@@ -60,19 +62,6 @@ function fitToView() {
6062
const ch = cRect.height
6163
const nw = nRect.width
6264
const nh = nRect.height
63-
const hostEl = host.value!
64-
const editorEl = hostEl.closest('.app')?.querySelector<HTMLElement>('.app__editor')
65-
console.log('[fitToView]', {
66-
cw, ch, nw, nh,
67-
hostWidth: hostEl.clientWidth,
68-
parentWidth: hostEl.parentElement?.clientWidth,
69-
appWidth: hostEl.closest('.app')?.clientWidth,
70-
appClass: hostEl.closest('.app')?.className,
71-
viewMode: store.viewMode,
72-
editorDisplay: editorEl?.style.display,
73-
editorWidth: editorEl?.clientWidth,
74-
editorComputedDisplay: editorEl ? getComputedStyle(editorEl).display : '?',
75-
})
7665
if (cw === 0 || ch === 0 || nw === 0 || nh === 0) return
7766
const padding = 0.9
7867
const scale = Math.min(1, Math.min(cw / nw, ch / nh) * padding)
@@ -189,6 +178,7 @@ function trySurgicalEdit(op: unknown): boolean {
189178
if (o.name === 'addChild') {
190179
const child = o.obj
191180
if (!child) return false
181+
freshNodeId = child.id
192182
const parent = child.parent as NodeObj | undefined
193183
if (!parent) return false
194184
const parentMeta = parent.metadata as NodeMeta | undefined
@@ -217,6 +207,7 @@ function trySurgicalEdit(op: unknown): boolean {
217207
if (o.name === 'insertSibling') {
218208
const sibling = o.obj
219209
if (!sibling) return false
210+
freshNodeId = sibling.id
220211
const parent = sibling.parent as NodeObj | undefined
221212
if (!parent || !parent.children) return false
222213
const idx = parent.children.findIndex((c) => c.id === sibling.id)
@@ -587,8 +578,30 @@ onMounted(() => {
587578
return null
588579
}
589580
})()
581+
const isFresh = creatingNode || freshNodeId === nodeObj.id
582+
freshNodeId = null
583+
let escaped = false
584+
const onKeydown = (e: KeyboardEvent) => {
585+
if (!(e.target instanceof HTMLElement)) return
586+
if (!e.target.closest('#input-box')) return
587+
if (e.key === 'Escape') escaped = true
588+
}
589+
document.addEventListener('keydown', onKeydown, { capture: true })
590590
const handleBlur = () => {
591+
document.removeEventListener('keydown', onKeydown, true)
591592
if (editingEl) editingEl.style.visibility = ''
593+
if (isFresh && escaped) {
594+
requestAnimationFrame(() => {
595+
if (!mind) return
596+
try {
597+
const el = MindElixir.E(nodeObj.id)
598+
if (el) mind.removeNodes([el])
599+
} catch {
600+
/* missing */
601+
}
602+
})
603+
return
604+
}
592605
setTimeout(() => {
593606
const text = (inputBox.innerText ?? '').trim()
594607
if (text !== '') return
@@ -651,6 +664,34 @@ onMounted(() => {
651664
mind.container.addEventListener('keydown', handleSpatialNav, { capture: true })
652665
mind.container.addEventListener('keydown', handleTypeToEdit, { capture: true })
653666
667+
const origInsertSibling = mind.insertSibling.bind(mind)
668+
mind.insertSibling = async function (type, el, node) {
669+
creatingNode = true
670+
try {
671+
return await origInsertSibling(type, el, node)
672+
} finally {
673+
creatingNode = false
674+
}
675+
}
676+
const origInsertParent = mind.insertParent.bind(mind)
677+
mind.insertParent = async function (el, node) {
678+
creatingNode = true
679+
try {
680+
return await origInsertParent(el, node)
681+
} finally {
682+
creatingNode = false
683+
}
684+
}
685+
const origAddChild = mind.addChild.bind(mind)
686+
mind.addChild = async function (el, node) {
687+
creatingNode = true
688+
try {
689+
return await origAddChild(el, node)
690+
} finally {
691+
creatingNode = false
692+
}
693+
}
694+
654695
const origRemoveNodes = mind.removeNodes.bind(mind)
655696
mind.removeNodes = async function (tpcs) {
656697
let nextId: string | null = null

0 commit comments

Comments
 (0)