openpencil/packages/core/src/editor/text.ts
박영호 ee2a7472ee
fix(text): handle Hangul IME composition (#319)
Co-authored-by: 05Park <im0505@kakao.com>
2026-06-06 07:21:56 +03:00

90 lines
2.3 KiB
TypeScript

import {
createTextEditSession,
resizeTextNodeForEdit,
snapshotTextNode,
textSnapshotChanged,
type TextEditSession
} from './text/session'
import type { EditorContext } from './types'
export function createTextActions(ctx: EditorContext) {
let activeSession: TextEditSession | null = null
function startTextEditing(nodeId: string) {
const te = ctx.getTextEditor()
if (ctx.state.editingTextId) commitTextEdit()
const node = ctx.graph.getNode(nodeId)
if (!node) return
activeSession = createTextEditSession(node)
ctx.state.editingTextId = nodeId
if (te) {
te.setRenderer(ctx.getRenderer())
te.start(node)
}
ctx.requestRender()
}
function commitTextEdit() {
const te = ctx.getTextEditor()
if (!te?.isActive) {
ctx.state.editingTextId = null
activeSession = null
return
}
const textState = te.state
if (!textState) {
te.stop()
ctx.state.editingTextId = null
activeSession = null
ctx.requestRender()
return
}
const result = { nodeId: textState.nodeId, text: textState.text }
const before = activeSession?.before ?? { text: '', styleRuns: [], size: {} }
const node = ctx.graph.getNode(result.nodeId)
const after = snapshotTextNode(node, result.text)
after.text = result.text
const sizeChanges =
before.text !== after.text ? resizeTextNodeForEdit(node, textState.paragraph) : {}
if (Object.keys(sizeChanges).length > 0) after.size = sizeChanges
const changed = textSnapshotChanged(before, after)
te.stop()
if (!changed) {
ctx.state.editingTextId = null
activeSession = null
ctx.requestRender()
return
}
ctx.graph.updateNode(result.nodeId, {
text: after.text,
styleRuns: after.styleRuns,
...sizeChanges
})
ctx.state.editingTextId = null
activeSession = null
ctx.undo.push({
label: 'Edit text',
forward: () => {
ctx.graph.updateNode(result.nodeId, {
text: after.text,
styleRuns: after.styleRuns,
...after.size
})
},
inverse: () => {
ctx.graph.updateNode(result.nodeId, {
text: before.text,
styleRuns: before.styleRuns,
...before.size
})
}
})
}
return { startTextEditing, commitTextEdit }
}