Something went wrong. Try again.
inkfinite (an infinite canvas/whiteboard) monorepo ink.stormlightlabs.org
svelte rust tauri svg canvas perfect-freehand
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748import { pathLength } from './path-metrics';import type { EditorState } from './reactivity';import { getSelectedShapes } from './reactivity';import type { PathShape, TextShape } from './editor-model';
/** Return the selected text and supporting path when the attachment command applies. */export function textPathSelectionTargets(state: EditorState): { text: TextShape; path: PathShape } | null { if (state.ui.selectionIds.length !== 2) return null; const selected = getSelectedShapes(state); const text = selected.find((shape): shape is TextShape => shape.type === 'text'); const path = selected.find((shape): shape is PathShape => shape.type === 'path'); return text && path && text.pageId === path.pageId ? { text, path } : null;}
/** Whether the current selection can attach text to a native path. */export function canTextPathSelection(state: EditorState): boolean { return textPathSelectionTargets(state) !== null;}
/** Attach the selected text to the selected path as one immutable editor update. */export function attachTextPathSelection(state: EditorState): EditorState | null { const targets = textPathSelectionTargets(state); if (!targets) return null; const { text, path } = targets; const textPath = { pathId: path.id, offset: pathLength(path.props) / 2, align: 'center' as const, side: 'left' as const, direction: 'forward' as const }; const nextText = { ...text, props: { ...text.props, textPath } }; return { ...state, doc: { ...state.doc, shapes: { ...state.doc.shapes, [text.id]: nextText } } };}
/** Remove an existing text-path attachment without changing its text content. */export function detachTextPath(state: EditorState, textId: string): EditorState | null { const text = state.doc.shapes[textId]; if (!text || text.type !== 'text' || !text.props.textPath) return null; return { ...state, doc: { ...state.doc, shapes: { ...state.doc.shapes, [textId]: { ...text, props: { ...text.props, textPath: undefined } } } } };}