Reference Picker: Drag-and-drop badges

- Make badges draggable with the mouse
  - Replace Insert and Replace modes with a single Apply action
  - Add a hint for the keyboard shortcut
  - Reorganize directory structure
This commit is contained in:
yu-i-i
2026-11-06 14:45:52 +01:00
parent 5c9fef4d03
commit ae07e781ab
15 changed files with 492 additions and 291 deletions
@@ -0,0 +1,63 @@
import React, { useEffect, useState } from 'react'
import { useCitationHint } from '../hooks/use-citation-hint'
import ReferencePickerModal from './reference-picker-modal'
import { citationRange } from '../extensions/reference-picker-keybinding'
import { useEditorViewContext } from '@/features/ide-react/context/editor-view-context'
type Picker = {
from: number
to: number
initialKeys: string[]
newCitation: boolean
}
export default function ReferencePickerController() {
const { view } = useEditorViewContext()
const [picker, setPicker] = useState<Picker | null>(null)
useCitationHint(view, picker === null)
useEffect(() => {
if (!view) return
const open = (event: Event) => {
const detail = (event as CustomEvent).detail
const range = detail?.braceFrom != null ? detail : citationRange(view)
const from = range?.braceFrom ?? view.state.selection.main.head
const to = range?.braceTo ?? from
const content = view.state.doc.sliceString(from, to)
const initialKeys = content.replace(/%[^\n]*/g, '').match(/[^\s,]+/g) ?? []
setPicker({ from, to, initialKeys, newCitation: !range })
}
window.addEventListener('reference:openPicker', open)
return () => window.removeEventListener('reference:openPicker', open)
}, [view])
if (!picker || !view) return null
function save(keys: string[]) {
if (!picker || !view) return
const { from, to, initialKeys, newCitation } = picker
const changed = keys.length !== initialKeys.length ||
keys.some((key, index) => key !== initialKeys[index])
if (newCitation || changed) {
const content = keys.join(', ')
const insert = newCitation ? `\\cite{${content}}` : content
view.dispatch({
changes: { from, to, insert },
selection: { anchor: newCitation ? from + insert.length - 1 : from + insert.length },
})
}
view.focus()
}
return (
<ReferencePickerModal
show
onClose={() => setPicker(null)}
onApply={save}
initialKeys={picker.initialKeys}
/>
)
}