* feat(electron): update electron-builder.yml to specify artifact naming convention for Windows builds. This change enhances clarity and consistency in the build output.
* feat(electron): implement a file logger for the main process
- Added a new logger module that writes logs to `{userData}/logs/main.log` with daily rotation and retains logs for the last 7 days.
- Integrated logging functionality into the main process, replacing console error messages with log entries for better error tracking and debugging.
- Introduced IPC handlers to retrieve the log directory path for user access.
- Updated the preload and main files to utilize the new logging system, enhancing overall application reliability and maintainability.
* feat(mcp): introduce layered design workflow and selection management
- Added a new layered design workflow with tools: `design_skeleton`, `design_content`, and `design_refine` for improved multi-section design generation.
- Implemented segmented prompt retrieval in `get_design_prompt` to allow focused access to design knowledge.
- Created new API endpoints for managing canvas selection state with `selection.get` and `selection.post`, enabling real-time updates and synchronization.
- Enhanced document management with live selection fetching and improved node operations for better design content handling.
- Updated README and CLAUDE.md to reflect new features and usage guidelines for the layered design process.
60 lines
1.8 KiB
TypeScript
60 lines
1.8 KiB
TypeScript
/**
|
|
* In-memory sync state for MCP ↔ Renderer real-time communication.
|
|
* Shared across Nitro API endpoints: GET/POST /api/mcp/document, GET /api/mcp/events.
|
|
*/
|
|
|
|
import type { PenDocument } from '../../src/types/pen'
|
|
import type { ServerResponse } from 'node:http'
|
|
|
|
let currentDocument: PenDocument | null = null
|
|
let documentVersion = 0
|
|
let currentSelection: string[] = []
|
|
let currentActivePageId: string | null = null
|
|
|
|
interface SSEClient {
|
|
id: string
|
|
res: ServerResponse
|
|
}
|
|
|
|
const clients = new Map<string, SSEClient>()
|
|
|
|
export function getSyncDocument(): { doc: PenDocument | null; version: number } {
|
|
return { doc: currentDocument, version: documentVersion }
|
|
}
|
|
|
|
export function setSyncDocument(doc: PenDocument, sourceClientId?: string): number {
|
|
currentDocument = doc
|
|
documentVersion++
|
|
broadcast({ type: 'document:update', version: documentVersion, document: doc }, sourceClientId)
|
|
return documentVersion
|
|
}
|
|
|
|
export function getSyncSelection(): { selectedIds: string[]; activePageId: string | null } {
|
|
return { selectedIds: currentSelection, activePageId: currentActivePageId }
|
|
}
|
|
|
|
export function setSyncSelection(selectedIds: string[], activePageId?: string | null): void {
|
|
currentSelection = selectedIds
|
|
if (activePageId !== undefined) currentActivePageId = activePageId
|
|
}
|
|
|
|
export function registerSSEClient(id: string, res: ServerResponse): void {
|
|
clients.set(id, { id, res })
|
|
}
|
|
|
|
export function unregisterSSEClient(id: string): void {
|
|
clients.delete(id)
|
|
}
|
|
|
|
function broadcast(payload: Record<string, unknown>, excludeClientId?: string): void {
|
|
const data = `data: ${JSON.stringify(payload)}\n\n`
|
|
for (const [id, client] of clients) {
|
|
if (id === excludeClientId) continue
|
|
try {
|
|
if (!client.res.closed) client.res.write(data)
|
|
} catch {
|
|
clients.delete(id)
|
|
}
|
|
}
|
|
}
|