refactor: extract wikilink utils and DiffView from Editor.tsx
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
||||
Sparkle,
|
||||
DotsThree,
|
||||
} from '@phosphor-icons/react'
|
||||
import { splitFrontmatter, preProcessWikilinks, injectWikilinks, countWords } from '../utils/wikilinks'
|
||||
import './Editor.css'
|
||||
import './EditorTheme.css'
|
||||
|
||||
@@ -84,67 +85,7 @@ const schema = BlockNoteSchema.create({
|
||||
},
|
||||
})
|
||||
|
||||
/** Strip YAML frontmatter from markdown, returning [frontmatter, body] */
|
||||
function splitFrontmatter(content: string): [string, string] {
|
||||
if (!content.startsWith('---')) return ['', content]
|
||||
const end = content.indexOf('\n---', 3)
|
||||
if (end === -1) return ['', content]
|
||||
let to = end + 4
|
||||
if (content[to] === '\n') to++
|
||||
return [content.slice(0, to), content.slice(to)]
|
||||
}
|
||||
|
||||
// Wikilink placeholder tokens for markdown round-trip
|
||||
const WL_START = '\u2039WIKILINK:'
|
||||
const WL_END = '\u203A'
|
||||
const WL_RE = new RegExp(`${WL_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([^${WL_END}]+)${WL_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'g')
|
||||
|
||||
/** Pre-process markdown: replace [[target]] with placeholder tokens */
|
||||
function preProcessWikilinks(md: string): string {
|
||||
return md.replace(/\[\[([^\]]+)\]\]/g, (_m, target) => `${WL_START}${target}${WL_END}`)
|
||||
}
|
||||
|
||||
/** Walk blocks and replace placeholder text with wikilink inline content */
|
||||
function injectWikilinks(blocks: any[]): any[] {
|
||||
return blocks.map(block => {
|
||||
if (block.content && Array.isArray(block.content)) {
|
||||
block.content = expandWikilinksInContent(block.content)
|
||||
}
|
||||
if (block.children && Array.isArray(block.children)) {
|
||||
block.children = injectWikilinks(block.children)
|
||||
}
|
||||
return block
|
||||
})
|
||||
}
|
||||
|
||||
function expandWikilinksInContent(content: any[]): any[] {
|
||||
const result: any[] = []
|
||||
for (const item of content) {
|
||||
if (item.type === 'text' && typeof item.text === 'string' && item.text.includes(WL_START)) {
|
||||
const text = item.text as string
|
||||
let lastIndex = 0
|
||||
WL_RE.lastIndex = 0
|
||||
let match
|
||||
while ((match = WL_RE.exec(text)) !== null) {
|
||||
if (match.index > lastIndex) {
|
||||
result.push({ ...item, text: text.slice(lastIndex, match.index) })
|
||||
}
|
||||
result.push({
|
||||
type: 'wikilink',
|
||||
props: { target: match[1] },
|
||||
content: undefined,
|
||||
})
|
||||
lastIndex = match.index + match[0].length
|
||||
}
|
||||
if (lastIndex < text.length) {
|
||||
result.push({ ...item, text: text.slice(lastIndex) })
|
||||
}
|
||||
} else {
|
||||
result.push(item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
// Wikilink utilities and splitFrontmatter extracted to ../utils/wikilinks.ts
|
||||
|
||||
// DiffView extracted to ./DiffView.tsx
|
||||
|
||||
@@ -216,13 +157,6 @@ function BlockNoteTab({ content, entries, onNavigateWikilink }: { content: strin
|
||||
)
|
||||
}
|
||||
|
||||
function countWords(content: string): number {
|
||||
const [, body] = splitFrontmatter(content)
|
||||
const text = body.replace(/[#*_\[\]`>~\-|]/g, '').trim()
|
||||
if (!text) return 0
|
||||
return text.split(/\s+/).filter(Boolean).length
|
||||
}
|
||||
|
||||
export function Editor({
|
||||
tabs, activeTabPath, entries, onSwitchTab, onCloseTab, onNavigateWikilink, onLoadDiff, isModified, onCreateNote,
|
||||
inspectorCollapsed, onToggleInspector, inspectorWidth, onInspectorResize,
|
||||
|
||||
68
src/utils/wikilinks.ts
Normal file
68
src/utils/wikilinks.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
// Wikilink placeholder tokens for markdown round-trip
|
||||
const WL_START = '\u2039WIKILINK:'
|
||||
const WL_END = '\u203A'
|
||||
const WL_RE = new RegExp(`${WL_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([^${WL_END}]+)${WL_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'g')
|
||||
|
||||
/** Pre-process markdown: replace [[target]] with placeholder tokens */
|
||||
export function preProcessWikilinks(md: string): string {
|
||||
return md.replace(/\[\[([^\]]+)\]\]/g, (_m, target) => `${WL_START}${target}${WL_END}`)
|
||||
}
|
||||
|
||||
/** Walk blocks and replace placeholder text with wikilink inline content */
|
||||
export function injectWikilinks(blocks: any[]): any[] {
|
||||
return blocks.map(block => {
|
||||
if (block.content && Array.isArray(block.content)) {
|
||||
block.content = expandWikilinksInContent(block.content)
|
||||
}
|
||||
if (block.children && Array.isArray(block.children)) {
|
||||
block.children = injectWikilinks(block.children)
|
||||
}
|
||||
return block
|
||||
})
|
||||
}
|
||||
|
||||
function expandWikilinksInContent(content: any[]): any[] {
|
||||
const result: any[] = []
|
||||
for (const item of content) {
|
||||
if (item.type !== 'text' || typeof item.text !== 'string' || !item.text.includes(WL_START)) {
|
||||
result.push(item)
|
||||
continue
|
||||
}
|
||||
const text = item.text as string
|
||||
let lastIndex = 0
|
||||
WL_RE.lastIndex = 0
|
||||
let match
|
||||
while ((match = WL_RE.exec(text)) !== null) {
|
||||
if (match.index > lastIndex) {
|
||||
result.push({ ...item, text: text.slice(lastIndex, match.index) })
|
||||
}
|
||||
result.push({
|
||||
type: 'wikilink',
|
||||
props: { target: match[1] },
|
||||
content: undefined,
|
||||
})
|
||||
lastIndex = match.index + match[0].length
|
||||
}
|
||||
if (lastIndex < text.length) {
|
||||
result.push({ ...item, text: text.slice(lastIndex) })
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** Strip YAML frontmatter from markdown, returning [frontmatter, body] */
|
||||
export function splitFrontmatter(content: string): [string, string] {
|
||||
if (!content.startsWith('---')) return ['', content]
|
||||
const end = content.indexOf('\n---', 3)
|
||||
if (end === -1) return ['', content]
|
||||
let to = end + 4
|
||||
if (content[to] === '\n') to++
|
||||
return [content.slice(0, to), content.slice(to)]
|
||||
}
|
||||
|
||||
export function countWords(content: string): number {
|
||||
const [, body] = splitFrontmatter(content)
|
||||
const text = body.replace(/[#*_\[\]`>~\-|]/g, '').trim()
|
||||
if (!text) return 0
|
||||
return text.split(/\s+/).filter(Boolean).length
|
||||
}
|
||||
Reference in New Issue
Block a user