Files
tolaria/src/components/SingleEditorView.tsx

656 lines
20 KiB
TypeScript
Raw Normal View History

2026-04-13 22:45:25 +02:00
import { useEffect, useCallback, useMemo, useRef, useContext } from 'react'
import { trackEvent } from '../lib/telemetry'
import {
useCreateBlockNote,
SuggestionMenuController,
BlockNoteViewRaw,
ComponentsContext,
DeleteLinkButton,
EditLinkButton,
LinkToolbar,
LinkToolbarController,
SideMenuController,
useComponentsContext,
useDictionary,
type LinkToolbarProps,
} from '@blocknote/react'
2026-04-13 22:45:25 +02:00
import { components } from '@blocknote/mantine'
import { MantineContext, MantineProvider } from '@mantine/core'
import { ExternalLink } from 'lucide-react'
2026-04-24 22:26:07 +02:00
import { useDocumentThemeMode } from '../hooks/useDocumentThemeMode'
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
import { useEditorTheme } from '../hooks/useTheme'
import { useImageDrop } from '../hooks/useImageDrop'
import { buildTypeEntryMap } from '../utils/typeColors'
import { preFilterWikilinks, deduplicateByPath, MIN_QUERY_LENGTH } from '../utils/wikilinkSuggestions'
import { filterPersonMentions, PERSON_MENTION_MIN_QUERY } from '../utils/personMentionSuggestions'
import { attachClickHandlers, enrichSuggestionItems } from '../utils/suggestionEnrichment'
import { openExternalUrl } from '../utils/url'
2026-04-26 03:01:10 +02:00
import { observeNativeTextAssistanceDisabled } from '../lib/nativeTextAssistance'
import { getRuntimeStyleNonce } from '../lib/runtimeStyleNonce'
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
import { WikilinkSuggestionMenu, type WikilinkSuggestionItem } from './WikilinkSuggestionMenu'
import type { VaultEntry } from '../types'
import { _wikilinkEntriesRef } from './editorSchema'
import { useBlockNoteSideMenuHoverGuard } from './blockNoteSideMenuHoverGuard'
import { getTolariaSlashMenuItems } from './tolariaEditorFormattingConfig'
import {
TolariaFormattingToolbar,
TolariaFormattingToolbarController,
} from './tolariaEditorFormatting'
import { TolariaSideMenu } from './tolariaBlockNoteSideMenu'
import { useEditorLinkActivation } from './useEditorLinkActivation'
import { findNearestTextCursorBlock } from './blockNoteCursorTarget'
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
const TEST_TABLE_MARKDOWN = `| Head 1 | Head 2 | Head 3 |
| --- | --- | --- |
| A | B | C |
| D | E | F |
`
const CONTAINER_CLICK_IGNORE_SELECTOR = [
'[contenteditable="true"]',
'.bn-formatting-toolbar',
'.bn-link-toolbar',
'.bn-side-menu',
'.bn-form-popover',
'[role="menu"]',
'[role="dialog"]',
].join(', ')
const TOOLBAR_MOUSE_DOWN_ALLOW_SELECTOR = [
'[role="menu"]',
'[role="dialog"]',
'button[aria-haspopup]',
'input',
'textarea',
'[contenteditable="true"]',
].join(', ')
type TestTableBlock = {
type?: string
content?: { type?: string; columnWidths?: Array<number | null> }
}
type SuggestionAction = () => void
type SuggestionItemWithClick = { onItemClick?: SuggestionAction }
function isEditorReadyForSuggestionAction(
editor: ReturnType<typeof useCreateBlockNote>,
container: HTMLElement | null,
) {
if (!container?.isConnected) return false
const editorElement = editor.domElement
if (!(editorElement instanceof HTMLElement)) return true
return editorElement.isConnected && container.contains(editorElement)
}
function runSuggestionActionSafely({
action,
container,
editor,
}: {
action: SuggestionAction
container: HTMLElement | null
editor: ReturnType<typeof useCreateBlockNote>
}) {
if (!isEditorReadyForSuggestionAction(editor, container)) return
try {
action()
} catch (error) {
console.warn('[editor] Ignored stale suggestion menu action:', error)
}
}
function guardSuggestionMenuItems<T extends SuggestionItemWithClick>(
items: T[],
runEditorAction: (action: SuggestionAction) => void,
): T[] {
return items.map((item) => {
if (!item.onItemClick) return item
const onItemClick = item.onItemClick
return {
...item,
onItemClick: () => runEditorAction(onItemClick),
}
})
}
2026-04-13 22:45:25 +02:00
function SharedContextBlockNoteView(props: React.ComponentProps<typeof BlockNoteViewRaw>) {
const { children, className, theme, ...rest } = props
2026-04-13 22:45:25 +02:00
const mantineContext = useContext(MantineContext)
const colorScheme = theme === 'dark' ? 'dark' : 'light'
2026-04-13 22:45:25 +02:00
const view = (
<ComponentsContext.Provider value={components}>
<BlockNoteViewRaw
{...rest}
className={['bn-mantine', className].filter(Boolean).join(' ')}
data-mantine-color-scheme={colorScheme}
theme={theme}
>
{children}
</BlockNoteViewRaw>
2026-04-13 22:45:25 +02:00
</ComponentsContext.Provider>
)
if (mantineContext) return view
return (
<MantineProvider
// BlockNote scopes Mantine defaults under `.bn-mantine` instead of `:root`.
2026-04-13 22:45:25 +02:00
withCssVariables={false}
getStyleNonce={getRuntimeStyleNonce}
2026-04-13 22:45:25 +02:00
getRootElement={() => undefined}
>
{view}
</MantineProvider>
)
}
function shouldAllowToolbarMouseDown(target: HTMLElement) {
return Boolean(target.closest(TOOLBAR_MOUSE_DOWN_ALLOW_SELECTOR))
}
function handleToolbarMouseDownCapture(
event: Pick<React.MouseEvent<HTMLElement>, 'target' | 'preventDefault'>,
) {
if (!(event.target instanceof HTMLElement) || shouldAllowToolbarMouseDown(event.target)) {
return
}
event.preventDefault()
}
function TolariaOpenLinkButton({ url }: Pick<LinkToolbarProps, 'url'>) {
const Components = useComponentsContext()!
const dict = useDictionary()
const handleOpen = useCallback(() => {
void openExternalUrl(url).catch((error) => {
console.warn('[link] Failed to open URL from toolbar:', error)
})
}, [url])
return (
<Components.LinkToolbar.Button
className="bn-button"
label={dict.link_toolbar.open.tooltip}
mainTooltip={dict.link_toolbar.open.tooltip}
isSelected={false}
onClick={handleOpen}
icon={<ExternalLink size={16} />}
/>
)
}
function TolariaLinkToolbar(props: LinkToolbarProps) {
return (
<LinkToolbar {...props}>
<EditLinkButton
url={props.url}
text={props.text}
range={props.range}
setToolbarOpen={props.setToolbarOpen}
setToolbarPositionFrozen={props.setToolbarPositionFrozen}
/>
<TolariaOpenLinkButton url={props.url} />
<DeleteLinkButton
range={props.range}
setToolbarOpen={props.setToolbarOpen}
/>
</LinkToolbar>
)
}
function applySeededColumnWidths(
parsedBlocks: Array<TestTableBlock>,
columnWidths?: Array<number | null>,
) {
2026-04-13 22:45:25 +02:00
if (!columnWidths) return
const tableBlock = parsedBlocks[0]
2026-04-13 22:45:25 +02:00
if (tableBlock?.type !== 'table') return
2026-04-12 12:29:13 +02:00
2026-04-13 22:45:25 +02:00
const tableContent = tableBlock.content
if (tableContent?.type !== 'tableContent') return
2026-04-12 12:29:13 +02:00
tableContent.columnWidths = [...columnWidths]
}
async function seedEditorWithTestTable(
editor: ReturnType<typeof useCreateBlockNote>,
columnWidths?: Array<number | null>,
) {
const parsedBlocks = await Promise.resolve(
editor.tryParseMarkdownToBlocks(TEST_TABLE_MARKDOWN),
) as Array<TestTableBlock>
applySeededColumnWidths(parsedBlocks, columnWidths)
const tableHtml = editor.blocksToHTMLLossy([
...parsedBlocks,
{ type: 'paragraph', content: [], children: [] },
] as typeof editor.document)
editor._tiptapEditor.commands.setContent(tableHtml)
editor.focus()
}
function useSeedBlockNoteTableBridge(editor: ReturnType<typeof useCreateBlockNote>) {
useEffect(() => {
const seedBlockNoteTable = (columnWidths?: Array<number | null>) => (
seedEditorWithTestTable(editor, columnWidths)
)
window.__laputaTest = {
...window.__laputaTest,
seedBlockNoteTable,
}
return () => {
if (window.__laputaTest?.seedBlockNoteTable === seedBlockNoteTable) {
delete window.__laputaTest.seedBlockNoteTable
}
}
}, [editor])
}
function shouldIgnoreContainerClick(target: HTMLElement) {
return Boolean(target.closest(CONTAINER_CLICK_IGNORE_SELECTOR))
}
function normalizeSuggestionQuery(query: string, triggerCharacter: string): string {
return query.startsWith(triggerCharacter)
? query.slice(triggerCharacter.length)
: query
}
function isSelectionInsideElement(element: HTMLElement): boolean {
const selection = window.getSelection()
const anchorNode = selection?.anchorNode ?? null
const anchorElement = anchorNode instanceof Element ? anchorNode : anchorNode?.parentElement ?? null
return Boolean(anchorElement && element.contains(anchorElement))
}
2026-04-24 20:13:34 +02:00
const TITLE_HEADING_SELECTOR = 'h1, [data-content-type="heading"][data-level="1"], [data-content-type="heading"]:not([data-level])'
const TITLE_HEADING_WRAPPER_SELECTOR = '.bn-block-outer, .bn-block'
2026-04-27 18:31:51 +02:00
const CODE_BLOCK_SELECTOR = '[data-content-type="codeBlock"]'
function nodeElement(node: Node | null): HTMLElement | null {
if (!node) return null
if (node instanceof HTMLElement) return node
return node.parentElement
}
function hasSingleActiveRange(selection: Selection | null): selection is Selection {
return Boolean(selection && selection.rangeCount === 1 && !selection.isCollapsed)
}
function closestCodeBlockInContainer(options: {
range: Range
container: HTMLElement
}): HTMLElement | null {
const { range, container } = options
const codeBlock = nodeElement(range.commonAncestorContainer)
?.closest<HTMLElement>(CODE_BLOCK_SELECTOR)
return codeBlock && container.contains(codeBlock) ? codeBlock : null
}
function nodeBelongsToElement(node: Node, element: HTMLElement): boolean {
const elementNode = nodeElement(node)
return Boolean(elementNode && element.contains(elementNode))
}
function rangeBelongsToElement(range: Range, element: HTMLElement): boolean {
return nodeBelongsToElement(range.startContainer, element)
&& nodeBelongsToElement(range.endContainer, element)
}
function selectedCodeBlockRange(options: {
selection: Selection | null
container: HTMLElement
}): Range | null {
const { selection, container } = options
if (!hasSingleActiveRange(selection)) return null
const range = selection.getRangeAt(0)
const codeBlock = closestCodeBlockInContainer({ range, container })
if (!codeBlock || !rangeBelongsToElement(range, codeBlock)) return null
return range
}
function selectedCodeBlockText(options: {
selection: Selection | null
container: HTMLElement
}): string | null {
const range = selectedCodeBlockRange(options)
if (!range) return null
return options.selection?.toString() || range.cloneContents().textContent || ''
}
2026-04-24 20:13:34 +02:00
function findTitleHeadingElement(target: HTMLElement): HTMLElement | null {
const directHeading = target.closest<HTMLElement>(TITLE_HEADING_SELECTOR)
if (directHeading) return directHeading
const titleWrapper = target.closest<HTMLElement>(TITLE_HEADING_WRAPPER_SELECTOR)
return titleWrapper?.querySelector<HTMLElement>(TITLE_HEADING_SELECTOR) ?? null
}
function queueTitleHeadingCursorRepair(
target: HTMLElement,
editor: ReturnType<typeof useCreateBlockNote>,
): boolean {
2026-04-24 20:13:34 +02:00
const titleHeading = findTitleHeadingElement(target)
if (!titleHeading) return false
queueMicrotask(() => {
if (isSelectionInsideElement(titleHeading)) return
const firstBlock = editor.document[0]
if (firstBlock?.type !== 'heading') return
try {
editor.setTextCursorPosition(firstBlock.id, 'end')
} catch {
return
}
editor.focus()
})
return true
}
function useEditorContainerClickHandler(options: {
editable: boolean
editor: ReturnType<typeof useCreateBlockNote>
}) {
const { editable, editor } = options
return useCallback((e: React.MouseEvent<HTMLDivElement>) => {
if (!editable) return
const target = e.target as HTMLElement
if (queueTitleHeadingCursorRepair(target, editor)) return
if (shouldIgnoreContainerClick(target)) return
const blocks = editor.document
if (blocks.length > 0) {
const targetBlock = findNearestTextCursorBlock(blocks, blocks.length - 1)
if (targetBlock) {
try {
editor.setTextCursorPosition(targetBlock.id, 'end')
} catch {
// Ignore transient BlockNote selection errors and at least restore focus.
}
}
}
editor.focus()
}, [editor, editable])
}
function useCompositionAwareEditorChange(options: {
containerRef: React.RefObject<HTMLDivElement | null>
onChange?: () => void
}) {
const { containerRef, onChange } = options
const onChangeRef = useRef(onChange)
const composingRef = useRef(false)
const pendingChangeRef = useRef(false)
useEffect(() => {
onChangeRef.current = onChange
}, [onChange])
useEffect(() => {
const container = containerRef.current
if (!container) return
const flushPendingChange = () => {
if (composingRef.current || !pendingChangeRef.current) return
pendingChangeRef.current = false
onChangeRef.current?.()
}
const handleCompositionStart = () => {
composingRef.current = true
}
const handleCompositionEnd = () => {
composingRef.current = false
queueMicrotask(flushPendingChange)
}
container.addEventListener('compositionstart', handleCompositionStart, true)
container.addEventListener('compositionend', handleCompositionEnd, true)
return () => {
container.removeEventListener('compositionstart', handleCompositionStart, true)
container.removeEventListener('compositionend', handleCompositionEnd, true)
}
}, [containerRef])
return useCallback(() => {
if (composingRef.current) {
pendingChangeRef.current = true
return
}
pendingChangeRef.current = false
onChangeRef.current?.()
}, [])
}
2026-04-27 18:31:51 +02:00
function handleCodeBlockCopy(event: React.ClipboardEvent<HTMLDivElement>) {
const codeText = selectedCodeBlockText({
selection: window.getSelection(),
container: event.currentTarget,
})
if (codeText === null) return
event.clipboardData.setData('text/plain', codeText)
event.preventDefault()
}
function buildBaseSuggestionItems(entries: VaultEntry[]) {
return deduplicateByPath(entries.map(entry => ({
title: entry.title,
aliases: [...new Set([entry.filename.replace(/\.md$/, ''), ...entry.aliases])],
group: entry.isA || 'Note',
entryType: entry.isA,
entryTitle: entry.title,
path: entry.path,
})))
}
function useInsertWikilink(
editor: ReturnType<typeof useCreateBlockNote>,
runEditorAction: (action: SuggestionAction) => void,
) {
return useCallback((target: string) => {
runEditorAction(() => {
editor.insertInlineContent([
{ type: 'wikilink' as const, props: { target } },
" ",
], { updateSelection: true })
trackEvent('wikilink_inserted')
})
}, [editor, runEditorAction])
}
function useSuggestionMenuItems(options: {
baseItems: ReturnType<typeof buildBaseSuggestionItems>
editor: ReturnType<typeof useCreateBlockNote>
insertWikilink: (target: string) => void
runEditorAction: (action: SuggestionAction) => void
typeEntryMap: Record<string, VaultEntry>
vaultPath?: string
}) {
const {
baseItems,
editor,
insertWikilink,
runEditorAction,
typeEntryMap,
vaultPath,
} = options
const buildItems = useCallback((query: string, triggerCharacter: '[[' | '@') => {
const normalizedQuery = normalizeSuggestionQuery(query, triggerCharacter)
const minLength = triggerCharacter === '[[' ? MIN_QUERY_LENGTH : PERSON_MENTION_MIN_QUERY
if (normalizedQuery.length < minLength) return null
const candidates = triggerCharacter === '[['
? preFilterWikilinks(baseItems, normalizedQuery)
: filterPersonMentions(baseItems, normalizedQuery)
const items = attachClickHandlers(candidates, insertWikilink, vaultPath ?? '')
return guardSuggestionMenuItems(
enrichSuggestionItems(items, normalizedQuery, typeEntryMap),
runEditorAction,
)
}, [baseItems, insertWikilink, runEditorAction, typeEntryMap, vaultPath])
const getWikilinkItems = useCallback(async (query: string): Promise<WikilinkSuggestionItem[]> => (
buildItems(query, '[[') ?? []
), [buildItems])
const getPersonMentionItems = useCallback(async (query: string): Promise<WikilinkSuggestionItem[]> => (
buildItems(query, '@') ?? []
), [buildItems])
const getSlashMenuItems = useCallback(async (query: string) => {
try {
return guardSuggestionMenuItems(
await Promise.resolve(getTolariaSlashMenuItems(editor, query)),
runEditorAction,
)
} catch (error) {
console.warn('[editor] Ignored stale slash menu query:', error)
return []
}
}, [editor, runEditorAction])
return {
getWikilinkItems,
getPersonMentionItems,
getSlashMenuItems,
}
}
fix: drag-and-drop images into editor — add drop overlay and copy-to-attachments (#150) * feat: add copy_image_to_vault Rust command for native drag-drop Adds a new Tauri command that copies an image file from a source path (provided by Tauri's drag-drop event) directly into vault/attachments/. More efficient than base64 encoding for filesystem drag-drop. Also adds core:webview:allow-on-drag-drop-event permission. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: handle Tauri native drag-drop for filesystem images Listen for Tauri's onDragDropEvent to intercept OS-level file drops that bypass the webview's HTML5 DnD API. When image files are dropped: 1. Copy to vault/attachments via copy_image_to_vault command 2. Insert image block into BlockNote at cursor position Also passes vaultPath through Editor → EditorContent → SingleEditorView so the hook can invoke the Tauri command. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove nonexistent drag-drop permission from capabilities The onDragDropEvent API works through core:event:default (included in core:default), not a separate webview permission. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: correct DragDropEvent type handling for 'over' events Tauri's DragDropEvent discriminated union only provides `paths` on 'drop' events, not 'over'. Show drag overlay unconditionally on 'over' since we can't filter by image paths at that stage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * design: drag-drop-images wireframes (idle + drag-over overlay) * style: rustfmt mcp.rs and image.rs --------- Co-authored-by: Test <test@test.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-28 21:31:47 +01:00
/** Insert an image block after the current cursor position. */
function useInsertImageCallback(editor: ReturnType<typeof useCreateBlockNote>) {
const editorRef = useRef(editor)
useEffect(() => { editorRef.current = editor }, [editor])
return useCallback((url: string) => {
const e = editorRef.current
const cursorBlock = e.getTextCursorPosition().block
e.insertBlocks([{ type: 'image' as const, props: { url } }], cursorBlock, 'after')
}, [])
}
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
/** Single BlockNote editor view — content is swapped via replaceBlocks */
export function SingleEditorView({ editor, entries, onNavigateWikilink, onChange, vaultPath, editable = true }: {
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
editor: ReturnType<typeof useCreateBlockNote>
entries: VaultEntry[]
onNavigateWikilink: (target: string) => void
onChange?: () => void
fix: drag-and-drop images into editor — add drop overlay and copy-to-attachments (#150) * feat: add copy_image_to_vault Rust command for native drag-drop Adds a new Tauri command that copies an image file from a source path (provided by Tauri's drag-drop event) directly into vault/attachments/. More efficient than base64 encoding for filesystem drag-drop. Also adds core:webview:allow-on-drag-drop-event permission. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: handle Tauri native drag-drop for filesystem images Listen for Tauri's onDragDropEvent to intercept OS-level file drops that bypass the webview's HTML5 DnD API. When image files are dropped: 1. Copy to vault/attachments via copy_image_to_vault command 2. Insert image block into BlockNote at cursor position Also passes vaultPath through Editor → EditorContent → SingleEditorView so the hook can invoke the Tauri command. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove nonexistent drag-drop permission from capabilities The onDragDropEvent API works through core:event:default (included in core:default), not a separate webview permission. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: correct DragDropEvent type handling for 'over' events Tauri's DragDropEvent discriminated union only provides `paths` on 'drop' events, not 'over'. Show drag overlay unconditionally on 'over' since we can't filter by image paths at that stage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * design: drag-drop-images wireframes (idle + drag-over overlay) * style: rustfmt mcp.rs and image.rs --------- Co-authored-by: Test <test@test.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-28 21:31:47 +01:00
vaultPath?: string
editable?: boolean
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
}) {
const { cssVars } = useEditorTheme()
2026-04-24 22:26:07 +02:00
const themeMode = useDocumentThemeMode()
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
const containerRef = useRef<HTMLDivElement>(null)
const handleContainerClick = useEditorContainerClickHandler({ editable, editor })
const handleEditorChange = useCompositionAwareEditorChange({ containerRef, onChange })
fix: drag-and-drop images into editor — add drop overlay and copy-to-attachments (#150) * feat: add copy_image_to_vault Rust command for native drag-drop Adds a new Tauri command that copies an image file from a source path (provided by Tauri's drag-drop event) directly into vault/attachments/. More efficient than base64 encoding for filesystem drag-drop. Also adds core:webview:allow-on-drag-drop-event permission. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: handle Tauri native drag-drop for filesystem images Listen for Tauri's onDragDropEvent to intercept OS-level file drops that bypass the webview's HTML5 DnD API. When image files are dropped: 1. Copy to vault/attachments via copy_image_to_vault command 2. Insert image block into BlockNote at cursor position Also passes vaultPath through Editor → EditorContent → SingleEditorView so the hook can invoke the Tauri command. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove nonexistent drag-drop permission from capabilities The onDragDropEvent API works through core:event:default (included in core:default), not a separate webview permission. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: correct DragDropEvent type handling for 'over' events Tauri's DragDropEvent discriminated union only provides `paths` on 'drop' events, not 'over'. Show drag overlay unconditionally on 'over' since we can't filter by image paths at that stage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * design: drag-drop-images wireframes (idle + drag-over overlay) * style: rustfmt mcp.rs and image.rs --------- Co-authored-by: Test <test@test.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-28 21:31:47 +01:00
const onImageUrl = useInsertImageCallback(editor)
const { isDragOver } = useImageDrop({ containerRef, onImageUrl, vaultPath })
useBlockNoteSideMenuHoverGuard(containerRef)
useEditorLinkActivation(containerRef, onNavigateWikilink)
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
useEffect(() => {
_wikilinkEntriesRef.current = entries
}, [entries])
2026-04-26 03:01:10 +02:00
useEffect(() => {
const container = containerRef.current
if (!container) return
return observeNativeTextAssistanceDisabled(container)
}, [])
useSeedBlockNoteTableBridge(editor)
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
const typeEntryMap = useMemo(() => buildTypeEntryMap(entries), [entries])
const baseItems = useMemo(() => buildBaseSuggestionItems(entries), [entries])
const runEditorAction = useCallback((action: SuggestionAction) => {
runSuggestionActionSafely({
action,
container: containerRef.current,
editor,
})
}, [editor])
const insertWikilink = useInsertWikilink(editor, runEditorAction)
const {
getWikilinkItems,
getPersonMentionItems,
getSlashMenuItems,
} = useSuggestionMenuItems({
baseItems,
editor,
insertWikilink,
runEditorAction,
typeEntryMap,
vaultPath,
})
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
return (
2026-04-27 18:31:51 +02:00
<div ref={containerRef} className={`editor__blocknote-container${isDragOver ? ' editor__blocknote-container--drag-over' : ''}`} style={cssVars as React.CSSProperties} onClick={handleContainerClick} onCopyCapture={handleCodeBlockCopy}>
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
{isDragOver && (
<div className="editor__drop-overlay">
<div className="editor__drop-overlay-label">Drop image here</div>
</div>
)}
2026-04-13 22:45:25 +02:00
<SharedContextBlockNoteView
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
editor={editor}
2026-04-24 22:26:07 +02:00
theme={themeMode}
onChange={handleEditorChange}
editable={editable}
formattingToolbar={false}
linkToolbar={false}
slashMenu={false}
sideMenu={false}
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
>
<SideMenuController sideMenu={TolariaSideMenu} />
<TolariaFormattingToolbarController
formattingToolbar={TolariaFormattingToolbar}
floatingUIOptions={{
elementProps: {
onMouseDownCapture: handleToolbarMouseDownCapture,
},
}}
/>
<LinkToolbarController
linkToolbar={TolariaLinkToolbar}
floatingUIOptions={{
elementProps: {
onMouseDownCapture: handleToolbarMouseDownCapture,
},
}}
/>
<SuggestionMenuController
triggerCharacter="/"
getItems={getSlashMenuItems}
/>
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
<SuggestionMenuController
triggerCharacter="[["
getItems={getWikilinkItems}
suggestionMenuComponent={WikilinkSuggestionMenu}
onItemClick={(item: WikilinkSuggestionItem) => runEditorAction(item.onItemClick)}
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
/>
<SuggestionMenuController
triggerCharacter="@"
getItems={getPersonMentionItems}
suggestionMenuComponent={WikilinkSuggestionMenu}
onItemClick={(item: WikilinkSuggestionItem) => runEditorAction(item.onItemClick)}
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
/>
2026-04-13 22:45:25 +02:00
</SharedContextBlockNoteView>
refactor: decompose Editor.tsx, NoteList.tsx, Sidebar.tsx — CodeScene hotspots (#126) * refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-27 15:27:25 +01:00
</div>
)
}