@@ -7,6 +7,7 @@ import { useDetachedWindow } from './composables/useDetachedWindow.js'
77import { useSelection } from ' ./composables/useSelection.js'
88import { useCardDrag } from ' ./composables/useCardDrag.js'
99import { useSearch } from ' ./composables/useSearch.js'
10+ import { useInlineEdit } from ' ./composables/useInlineEdit.js'
1011import { nodeTypes , getImportanceLabel , getTypeIcon , getTypeColors , typeConfig , personIconSvg } from ' ./utils/constants.js'
1112import DetailPanel from ' ./components/DetailPanel.vue'
1213import GraphView from ' ./components/GraphView.vue'
@@ -215,14 +216,7 @@ function closeDetail() {
215216 detailPinned .value = false
216217}
217218
218- // Inline editing state
219- const editingCardId = ref (null )
220- const editingTitle = ref (' ' )
221-
222- // Inline notes-only editing (separate from full card editing)
223- const inlineNotesId = ref (null )
224- const inlineNotesText = ref (' ' )
225- const inlineNotesRef = ref (null )
219+ // Inline editing state is managed by useInlineEdit composable (initialized after flatChildren)
226220// Sensitive info visibility - restore from localStorage
227221const hideSensitive = ref (localStorage .getItem (' graphcore-hideSensitive' ) === ' true' )
228222
@@ -636,6 +630,35 @@ const {
636630 flatChildren
637631})
638632
633+ // Initialize inline editing composable
634+ const {
635+ editingCardId ,
636+ editingTitle ,
637+ inlineNotesId ,
638+ inlineNotesText ,
639+ inlineNotesRef ,
640+ startEditing ,
641+ saveEditing ,
642+ cancelEditing ,
643+ handleEditKeydown ,
644+ startInlineNotes ,
645+ saveInlineNotes ,
646+ cancelInlineNotes ,
647+ handleInlineNotesKeydown
648+ } = useInlineEdit ({
649+ findNode : (nodeId ) => flatChildren .value .find (n => n .id === nodeId),
650+ onSaveTitle: async (nodeId , newTitle ) => {
651+ await api .updateNode (nodeId, { title: newTitle })
652+ await loadChildren (currentContainerId .value )
653+ },
654+ onSaveNotes: async (nodeId , newNotes , { autoSave }) => {
655+ await api .updateNode (nodeId, { notes: newNotes })
656+ if (! autoSave) {
657+ await loadChildren (currentContainerId .value )
658+ }
659+ }
660+ })
661+
639662const contextTitle = computed (() => {
640663 if (currentContainer .value ) {
641664 return currentContainer .value .title
@@ -2213,140 +2236,15 @@ async function handleViewContextMenu({ event, node }) {
22132236 await showContextMenu (event , node)
22142237}
22152238
2216- // Inline editing functions
2217- function startEditing (node , e ) {
2218- e? .stopPropagation ()
2219- editingCardId .value = node .id
2220- editingTitle .value = node .title
2221- }
2222-
2223- async function saveEditing () {
2224- if (! editingCardId .value ) return
2225-
2226- const nodeId = editingCardId .value
2227- const originalNode = flatChildren .value .find (n => n .id === nodeId)
2228- if (! originalNode) {
2229- editingCardId .value = null
2230- return
2231- }
2232-
2233- // Only update if title changed
2234- if (editingTitle .value !== originalNode .title ) {
2235- try {
2236- await api .updateNode (nodeId, { title: editingTitle .value })
2237- await loadChildren (currentContainerId .value )
2238- } catch (e) {
2239- error .value = e .message
2240- }
2241- }
2242-
2243- editingCardId .value = null
2244- }
2245-
2246- function cancelEditing () {
2247- editingCardId .value = null
2248- editingTitle .value = ' '
2249- }
2250-
2251- function handleEditKeydown (e ) {
2252- if (e .key === ' Escape' ) {
2253- e .preventDefault ()
2254- cancelEditing ()
2255- } else if (e .key === ' Enter' && ! e .shiftKey ) {
2256- e .preventDefault ()
2257- saveEditing ()
2258- }
2259- }
2260-
2261- // Inline notes-only editing functions
2262- async function startInlineNotes (node , e ) {
2263- e? .stopPropagation ()
2264- inlineNotesId .value = node .id
2265- inlineNotesText .value = node .notes || ' '
2266- await nextTick ()
2267- // Handle both single ref and array of refs (when multiple textareas exist)
2268- const ref = inlineNotesRef .value
2269- if (Array .isArray (ref)) {
2270- ref[0 ]? .focus ()
2271- } else {
2272- ref? .focus ()
2273- }
2274- }
2275-
2276- // Debounced auto-save for inline notes
2277- let autoSaveTimeout = null
2278- async function autoSaveInlineNotes () {
2279- if (! inlineNotesId .value ) return
2280-
2281- const nodeId = inlineNotesId .value
2282- try {
2283- await api .updateNode (nodeId, { notes: inlineNotesText .value })
2284- } catch (e) {
2285- console .error (' Auto-save failed:' , e)
2286- }
2287- }
2288-
2289- function debouncedAutoSave () {
2290- if (autoSaveTimeout) clearTimeout (autoSaveTimeout)
2291- autoSaveTimeout = setTimeout (autoSaveInlineNotes, 500 ) // Save after 500ms of no typing
2292- }
2293-
2294- // Watch for notes changes and auto-save
2295- watch (inlineNotesText, (newValue , oldValue ) => {
2296- if (inlineNotesId .value && newValue !== oldValue) {
2297- debouncedAutoSave ()
2298- }
2299- })
2300-
2301- async function saveInlineNotes () {
2302- if (! inlineNotesId .value ) return
2303-
2304- // Clear any pending auto-save
2305- if (autoSaveTimeout) {
2306- clearTimeout (autoSaveTimeout)
2307- autoSaveTimeout = null
2308- }
2309-
2310- const nodeId = inlineNotesId .value
2311- const originalNode = flatChildren .value .find (n => n .id === nodeId)
2312- if (! originalNode) {
2313- inlineNotesId .value = null
2314- return
2315- }
2316-
2317- if (inlineNotesText .value !== (originalNode .notes || ' ' )) {
2318- try {
2319- await api .updateNode (nodeId, { notes: inlineNotesText .value })
2320- // Reload to get fresh data
2321- await loadChildren (currentContainerId .value )
2322- } catch (e) {
2323- error .value = e .message
2324- }
2325- }
2326-
2327- inlineNotesId .value = null
2328- }
2239+ // Inline editing functions (startEditing, saveEditing, cancelEditing, handleEditKeydown,
2240+ // startInlineNotes, saveInlineNotes, cancelInlineNotes, handleInlineNotesKeydown)
2241+ // are now provided by useInlineEdit composable initialized above
23292242
23302243function renderMarkdown (text ) {
23312244 if (! text) return ' '
23322245 return marked .parse (text)
23332246}
23342247
2335- function cancelInlineNotes () {
2336- inlineNotesId .value = null
2337- inlineNotesText .value = ' '
2338- }
2339-
2340- function handleInlineNotesKeydown (e ) {
2341- if (e .key === ' Escape' ) {
2342- e .preventDefault ()
2343- cancelInlineNotes ()
2344- } else if (e .key === ' Enter' && e .metaKey ) {
2345- e .preventDefault ()
2346- saveInlineNotes ()
2347- }
2348- }
2349-
23502248// Wrapper functions for tooltip - use composable
23512249function showCardTooltip (event , node ) {
23522250 // Don't show tooltip if editing
0 commit comments