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.
68 lines
2.0 KiB
TypeScript
68 lines
2.0 KiB
TypeScript
/**
|
|
* 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
|
|
}
|
|
}
|