feat: Replace DB component tools with filesystem-based user-components/
Components are now .vue files in user-components/<folder>/ parsed at runtime. Replaces 6 DB MCP tools with 2 (list_fs_components, load_fs_component). Adds vue-parser, fs-components API, and file watcher for live reload.
This commit is contained in:
67
server/services/handlers/components-handler.ts
Normal file
67
server/services/handlers/components-handler.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Components Handler
|
||||
* Watches user-components/ for .vue and .json file changes
|
||||
* and broadcasts notifications via the sync server.
|
||||
*/
|
||||
|
||||
import { watch, mkdirSync, existsSync, type FSWatcher } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { USER_COMPONENTS_DIR } from '../../config'
|
||||
|
||||
let componentsWatcher: FSWatcher | null = null
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null
|
||||
const DEBOUNCE_MS = 400
|
||||
|
||||
export function setupComponentsWatcher(workingDir: string, broadcast: (message: string, filter?: (ws: any) => boolean) => void) {
|
||||
const componentsDir = join(workingDir, USER_COMPONENTS_DIR)
|
||||
|
||||
// Auto-create directory if it doesn't exist
|
||||
if (!existsSync(componentsDir)) {
|
||||
try {
|
||||
mkdirSync(componentsDir, { recursive: true })
|
||||
} catch (e: any) {
|
||||
console.error(`[Components] Failed to create ${componentsDir}: ${e.message}`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
componentsWatcher = watch(componentsDir, { recursive: true }, (_, filename) => {
|
||||
if (!filename) return
|
||||
|
||||
// Only watch .vue and .json files
|
||||
if (!filename.endsWith('.vue') && !filename.endsWith('.json')) return
|
||||
|
||||
// Debounce
|
||||
if (debounceTimer) clearTimeout(debounceTimer)
|
||||
debounceTimer = setTimeout(() => {
|
||||
// Extract folder name from path (first segment)
|
||||
const folder = filename.split(/[/\\]/)[0] || ''
|
||||
const file = filename.split(/[/\\]/).pop() || filename
|
||||
|
||||
console.log(`[Components] Change: ${filename}`)
|
||||
broadcast(JSON.stringify({
|
||||
type: 'component-change',
|
||||
folder,
|
||||
file,
|
||||
timestamp: Date.now()
|
||||
}))
|
||||
}, DEBOUNCE_MS)
|
||||
})
|
||||
|
||||
console.log(`[Components] Watching ${componentsDir}`)
|
||||
} catch (e: any) {
|
||||
console.error(`[Components] Watch failed: ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
export function cleanupComponentsWatcher() {
|
||||
if (componentsWatcher) {
|
||||
componentsWatcher.close()
|
||||
componentsWatcher = null
|
||||
}
|
||||
if (debounceTimer) {
|
||||
clearTimeout(debounceTimer)
|
||||
debounceTimer = null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user