@elixpo/lixsketch 5.6.2 → 5.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/mcp/fileStore.js", "../../src/mcp/scene.js", "../../src/mcp/stdioTransport.js"],
4
- "sourcesContent": ["import { mkdir, open, readFile, rename, stat, unlink } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\nimport { createEmptyScene, validateScene } from './scene.js';\n\nconst MAX_SCENE_FILE_BYTES = 20 * 1024 * 1024;\n\nexport class FileSceneStore {\n constructor(filePath) {\n if (!filePath) throw new Error('A scene file path is required');\n this.filePath = resolve(filePath);\n this.writeChain = Promise.resolve();\n }\n\n async read() {\n try {\n const details = await stat(this.filePath);\n if (details.size > MAX_SCENE_FILE_BYTES) {\n throw new Error(`Scene file exceeds the ${MAX_SCENE_FILE_BYTES / 1024 / 1024} MB MCP limit`);\n }\n const source = await readFile(this.filePath, 'utf8');\n const scene = JSON.parse(source);\n const validation = validateScene(scene);\n if (!validation.valid) throw new Error(`Invalid scene file: ${validation.errors.join('; ')}`);\n return scene;\n } catch (error) {\n if (error?.code !== 'ENOENT') throw error;\n const scene = createEmptyScene();\n await this.write(scene);\n return scene;\n }\n }\n\n async write(scene) {\n const validation = validateScene(scene);\n if (!validation.valid) throw new Error(`Refusing to store invalid scene: ${validation.errors.join('; ')}`);\n this.writeChain = this.writeChain.then(async () => {\n await mkdir(dirname(this.filePath), { recursive: true });\n const temporary = `${this.filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;\n try {\n const serialized = `${JSON.stringify(scene, null, 2)}\\n`;\n if (Buffer.byteLength(serialized, 'utf8') > MAX_SCENE_FILE_BYTES) {\n throw new Error(`Scene file exceeds the ${MAX_SCENE_FILE_BYTES / 1024 / 1024} MB MCP limit`);\n }\n const handle = await open(temporary, 'wx', 0o600);\n try {\n await handle.writeFile(serialized, 'utf8');\n await handle.sync();\n } finally {\n await handle.close();\n }\n await rename(temporary, this.filePath);\n } catch (error) {\n await unlink(temporary).catch(() => {});\n throw error;\n }\n });\n await this.writeChain;\n return structuredClone(scene);\n }\n\n async info() {\n try {\n const details = await stat(this.filePath);\n return { path: this.filePath, sizeBytes: details.size, updatedAt: details.mtime.toISOString() };\n } catch (error) {\n if (error?.code === 'ENOENT') return { path: this.filePath, sizeBytes: 0, updatedAt: null };\n throw error;\n }\n }\n}\n", "const FORMAT = 'lixsketch';\nconst VERSION = 1;\nconst MAX_SHAPES = 5000;\nconst MAX_OPERATIONS = 500;\nconst SCENE_TYPES = new Set(['rectangle', 'circle', 'line', 'arrow', 'freehandStroke', 'frame', 'text', 'code', 'image', 'icon']);\nconst WRITABLE_TYPES = new Set(['rectangle', 'circle', 'line', 'arrow', 'freehandStroke', 'frame', 'text']);\n\nconst clone = (value) => JSON.parse(JSON.stringify(value));\nconst finite = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;\nconst positive = (value, fallback = 1) => Math.max(1, finite(value, fallback));\n\nexport function createEmptyScene(name = 'MCP Canvas') {\n return {\n format: FORMAT,\n version: VERSION,\n sessionID: `mcp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,\n name: String(name || 'MCP Canvas').trim().slice(0, 72),\n createdAt: new Date().toISOString(),\n viewport: { x: 0, y: 0, width: 1280, height: 720 },\n zoom: 1,\n mcpRevision: 0,\n shapes: [],\n };\n}\n\nexport function validateScene(scene) {\n const errors = [];\n if (!scene || typeof scene !== 'object') return { valid: false, errors: ['Scene must be an object'] };\n if (scene.format !== FORMAT) errors.push(`Scene format must be \"${FORMAT}\"`);\n if (scene.version !== VERSION) errors.push(`Scene version must be ${VERSION}`);\n if (!Array.isArray(scene.shapes)) errors.push('Scene shapes must be an array');\n if (Array.isArray(scene.shapes) && scene.shapes.length > MAX_SHAPES) errors.push(`Scene exceeds ${MAX_SHAPES} shapes`);\n const ids = new Set();\n for (const [index, shape] of (scene.shapes || []).entries()) {\n if (!shape || typeof shape !== 'object') { errors.push(`Shape ${index} must be an object`); continue; }\n if (!SCENE_TYPES.has(shape.type)) errors.push(`Shape ${index} has unsupported type \"${shape.type}\"`);\n if (!shape.shapeID || typeof shape.shapeID !== 'string') errors.push(`Shape ${index} is missing shapeID`);\n else if (ids.has(shape.shapeID)) errors.push(`Duplicate shapeID \"${shape.shapeID}\"`);\n else ids.add(shape.shapeID);\n validateShapeGeometry(shape, index, errors);\n }\n return { valid: errors.length === 0, errors };\n}\n\nfunction escapeXml(value) {\n return String(value).replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('\"', '&quot;').replaceAll(\"'\", '&apos;');\n}\n\nfunction normalizeOptions(value = {}) {\n return {\n roughness: Math.max(0, Math.min(3, finite(value.roughness, 1.2))),\n stroke: typeof value.stroke === 'string' ? value.stroke : '#8b76d6',\n strokeWidth: Math.max(0.5, Math.min(20, finite(value.strokeWidth, 2))),\n fill: typeof value.fill === 'string' ? value.fill : 'transparent',\n fillStyle: typeof value.fillStyle === 'string' ? value.fillStyle : 'solid',\n opacity: Math.max(0, Math.min(1, finite(value.opacity, 1))),\n };\n}\n\nfunction createTextShape(input, shapeID) {\n const x = finite(input.x), y = finite(input.y), rotation = finite(input.rotation);\n const text = String(input.text || '').slice(0, 10000);\n const fontSize = Math.max(8, Math.min(160, finite(input.fontSize, 20)));\n const color = typeof input.color === 'string' ? input.color : '#e8e3f3';\n const family = typeof input.fontFamily === 'string' ? input.fontFamily.slice(0, 80) : 'lixFont';\n const transform = `translate(${x}, ${y})${rotation ? ` rotate(${rotation}, 0, 0)` : ''}`;\n const lines = text.split('\\n').map((line, index) => `<tspan x=\"0\" dy=\"${index === 0 ? 0 : '1.2em'}\">${escapeXml(line || ' ')}</tspan>`).join('');\n return {\n shapeID, type: 'text', x, y, rotation,\n mcpText: text, mcpFontSize: fontSize, mcpColor: color, mcpFontFamily: family,\n groupHTML: `<g id=\"${escapeXml(shapeID)}\" data-type=\"text-group\" data-x=\"${x}\" data-y=\"${y}\" transform=\"${transform}\"><text id=\"${escapeXml(shapeID)}-text\" x=\"0\" y=\"0\" fill=\"${escapeXml(color)}\" font-size=\"${fontSize}\" font-family=\"${escapeXml(family)}\" dominant-baseline=\"hanging\" white-space=\"pre\" pointer-events=\"painted\" data-type=\"text\" data-initial-size=\"${fontSize}\" data-initial-font=\"${escapeXml(family)}\" data-initial-color=\"${escapeXml(color)}\">${lines}</text></g>`,\n };\n}\n\nexport function normalizeShape(input, existingIds = new Set()) {\n if (!input || typeof input !== 'object') throw new Error('Shape must be an object');\n if (!WRITABLE_TYPES.has(input.type)) throw new Error(`Shape type \"${input.type}\" is read-only through MCP`);\n let shapeID = String(input.shapeID || `${input.type}-${crypto.randomUUID()}`).slice(0, 120);\n while (existingIds.has(shapeID)) shapeID = `${input.type}-${crypto.randomUUID()}`;\n const base = { shapeID, type: input.type, rotation: finite(input.rotation), options: normalizeOptions(input.options), groupId: input.groupId || null, parentFrame: input.parentFrame || null, docBlockIds: [] };\n switch (input.type) {\n case 'rectangle': return { ...base, x: finite(input.x), y: finite(input.y), width: positive(input.width, 160), height: positive(input.height, 90) };\n case 'circle': return { ...base, x: finite(input.x), y: finite(input.y), rx: positive(input.rx, 60), ry: positive(input.ry, 60) };\n case 'line': return { ...base, startPoint: point(input.startPoint), endPoint: point(input.endPoint, 120), isCurved: Boolean(input.isCurved), controlPoint: input.controlPoint ? point(input.controlPoint) : null };\n case 'arrow': return { ...base, startPoint: point(input.startPoint), endPoint: point(input.endPoint, 120), arrowHeadStyle: input.arrowHeadStyle || 'triangle', arrowOutlineStyle: input.arrowOutlineStyle || 'solid', arrowCurved: Boolean(input.arrowCurved), arrowCurveAmount: finite(input.arrowCurveAmount, 0.2) };\n case 'freehandStroke': {\n const points = (Array.isArray(input.points) ? input.points : []).slice(0, 4096).map((entry) => [finite(entry?.[0]), finite(entry?.[1]), finite(entry?.[2], 0.5)]);\n if (points.length < 2) throw new Error('freehandStroke requires at least two points');\n return { ...base, points };\n }\n case 'frame': return { ...base, x: finite(input.x), y: finite(input.y), width: positive(input.width, 640), height: positive(input.height, 360), frameName: String(input.frameName || 'Frame').slice(0, 80), fillStyle: input.fillStyle || 'transparent', fillColor: input.fillColor || '#1e1e28', gridSize: positive(input.gridSize, 20), containedShapeIDs: [] };\n case 'text': return { ...base, ...createTextShape(input, shapeID) };\n default: throw new Error(`Unsupported shape type \"${input.type}\"`);\n }\n}\n\nfunction point(value, fallbackX = 0) {\n return { x: finite(value?.x, fallbackX), y: finite(value?.y) };\n}\n\nfunction translateShape(shape, dx, dy) {\n const moved = clone(shape);\n if (moved.startPoint) { moved.startPoint.x += dx; moved.startPoint.y += dy; }\n if (moved.endPoint) { moved.endPoint.x += dx; moved.endPoint.y += dy; }\n if (moved.controlPoint) { moved.controlPoint.x += dx; moved.controlPoint.y += dy; }\n if (moved.controlPoint1) { moved.controlPoint1.x += dx; moved.controlPoint1.y += dy; }\n if (moved.controlPoint2) { moved.controlPoint2.x += dx; moved.controlPoint2.y += dy; }\n if (Array.isArray(moved.points)) moved.points = moved.points.map((p) => [p[0] + dx, p[1] + dy, ...p.slice(2)]);\n if (Number.isFinite(moved.x)) moved.x += dx;\n if (Number.isFinite(moved.y)) moved.y += dy;\n if (moved.type === 'text' && moved.groupHTML) {\n moved.groupHTML = moved.groupHTML\n .replace(/data-x=\"[^\"]*\"/, `data-x=\"${moved.x}\"`)\n .replace(/data-y=\"[^\"]*\"/, `data-y=\"${moved.y}\"`)\n .replace(/transform=\"translate\\([^)]*\\)/, `transform=\"translate(${moved.x}, ${moved.y})`);\n }\n if (moved.type === 'code' && moved.groupHTML) {\n moved.groupHTML = moved.groupHTML\n .replace(/data-x=\"[^\"]*\"/, `data-x=\"${moved.x}\"`)\n .replace(/data-y=\"[^\"]*\"/, `data-y=\"${moved.y}\"`)\n .replace(/transform=\"translate\\([^)]*\\)/, `transform=\"translate(${moved.x}, ${moved.y})`);\n }\n if (moved.type === 'icon' && moved.elementHTML) {\n moved.elementHTML = moved.elementHTML\n .replace(/\\bx=\"[^\"]*\"/, `x=\"${moved.x}\"`)\n .replace(/\\by=\"[^\"]*\"/, `y=\"${moved.y}\"`);\n }\n return moved;\n}\n\nexport function applyScenePatch(sceneInput, operations, { expectedRevision, dryRun = false } = {}) {\n const scene = clone(sceneInput);\n const check = validateScene(scene);\n if (!check.valid) throw new Error(`Invalid scene: ${check.errors.join('; ')}`);\n if (!Array.isArray(operations) || operations.length === 0) throw new Error('At least one operation is required');\n if (operations.length > MAX_OPERATIONS) throw new Error(`Patch exceeds ${MAX_OPERATIONS} operations`);\n const revision = Number(scene.mcpRevision || 0);\n if (expectedRevision !== undefined && Number(expectedRevision) !== revision) throw new Error(`Revision conflict: expected ${expectedRevision}, current ${revision}`);\n const changedIds = new Set();\n for (const operation of operations) {\n if (!operation || typeof operation !== 'object') throw new Error('Each operation must be an object');\n if (operation.op === 'add') {\n if (scene.shapes.length >= MAX_SHAPES) throw new Error(`Scene exceeds ${MAX_SHAPES} shapes`);\n const ids = new Set(scene.shapes.map((shape) => shape.shapeID));\n const shape = normalizeShape(operation.shape, ids);\n scene.shapes.push(shape); changedIds.add(shape.shapeID);\n } else if (operation.op === 'update') {\n const index = scene.shapes.findIndex((shape) => shape.shapeID === operation.shapeID);\n if (index < 0) throw new Error(`Shape \"${operation.shapeID}\" was not found`);\n const immutable = scene.shapes[index];\n scene.shapes[index] = applyShapeChanges(immutable, operation.changes || {}); changedIds.add(immutable.shapeID);\n } else if (operation.op === 'delete') {\n const ids = new Set(Array.isArray(operation.shapeIDs) ? operation.shapeIDs : [operation.shapeID]);\n const before = scene.shapes.length;\n scene.shapes = scene.shapes.filter((shape) => !ids.has(shape.shapeID));\n if (scene.shapes.length === before) throw new Error('No requested shapes were found');\n scene.shapes.forEach((shape) => {\n if (ids.has(shape.parentFrame)) shape.parentFrame = null;\n if (Array.isArray(shape.containedShapeIDs)) shape.containedShapeIDs = shape.containedShapeIDs.filter((id) => !ids.has(id));\n });\n ids.forEach((id) => changedIds.add(id));\n } else if (operation.op === 'translate') {\n const ids = new Set(operation.shapeIDs || []), dx = finite(operation.dx), dy = finite(operation.dy);\n if (!ids.size) throw new Error('translate requires shapeIDs');\n scene.shapes = scene.shapes.map((shape) => ids.has(shape.shapeID) ? translateShape(shape, dx, dy) : shape);\n ids.forEach((id) => changedIds.add(id));\n } else if (operation.op === 'rename_canvas') {\n scene.name = String(operation.name || '').trim().slice(0, 72) || scene.name;\n } else throw new Error(`Unsupported operation \"${operation.op}\"`);\n }\n scene.mcpRevision = revision + 1;\n scene.updatedAt = new Date().toISOString();\n const result = validateScene(scene);\n if (!result.valid) throw new Error(`Patch produced an invalid scene: ${result.errors.join('; ')}`);\n return { scene, revision: scene.mcpRevision, dryRun: Boolean(dryRun), changedShapeIDs: [...changedIds] };\n}\n\nfunction applyShapeChanges(shape, changes) {\n if (!changes || typeof changes !== 'object' || Array.isArray(changes)) throw new Error('Shape changes must be an object');\n const allowed = {\n rectangle: ['x', 'y', 'width', 'height', 'rotation', 'options', 'groupId', 'parentFrame'],\n circle: ['x', 'y', 'rx', 'ry', 'rotation', 'options', 'groupId', 'parentFrame'],\n line: ['startPoint', 'endPoint', 'controlPoint', 'isCurved', 'options', 'groupId', 'parentFrame'],\n arrow: ['startPoint', 'endPoint', 'controlPoint1', 'controlPoint2', 'arrowHeadStyle', 'arrowOutlineStyle', 'arrowCurved', 'arrowCurveAmount', 'options', 'groupId', 'parentFrame'],\n freehandStroke: ['points', 'rotation', 'options', 'groupId', 'parentFrame'],\n frame: ['x', 'y', 'width', 'height', 'rotation', 'frameName', 'fillStyle', 'fillColor', 'gridSize', 'options', 'groupId', 'parentFrame'],\n text: ['x', 'y', 'rotation', 'text', 'fontSize', 'color', 'fontFamily', 'groupId', 'parentFrame'],\n }[shape.type] || [];\n const rejected = Object.keys(changes).filter((key) => !allowed.includes(key));\n if (rejected.length) throw new Error(`Cannot update ${shape.type} fields: ${rejected.join(', ')}`);\n if (shape.type === 'text') {\n const text = changes.text ?? shape.mcpText ?? extractText(shape.groupHTML);\n return { ...shape, ...createTextShape({ x: changes.x ?? shape.x, y: changes.y ?? shape.y, rotation: changes.rotation ?? shape.rotation, text, fontSize: changes.fontSize ?? shape.mcpFontSize, color: changes.color ?? shape.mcpColor, fontFamily: changes.fontFamily ?? shape.mcpFontFamily }, shape.shapeID), groupId: changes.groupId ?? shape.groupId, parentFrame: changes.parentFrame ?? shape.parentFrame };\n }\n const copy = { ...clone(shape), ...clone(changes), shapeID: shape.shapeID, type: shape.type };\n if (changes.options) copy.options = { ...(shape.options || {}), ...normalizeOptions({ ...(shape.options || {}), ...changes.options }) };\n return copy;\n}\n\nfunction extractText(groupHTML = '') {\n return String(groupHTML).replace(/<[^>]+>/g, ' ').replace(/\\s+/g, ' ').trim();\n}\n\nfunction validateShapeGeometry(shape, index, errors) {\n const numbers = [];\n if (['rectangle', 'frame', 'text', 'code', 'image', 'icon'].includes(shape.type)) numbers.push(['x', shape.x], ['y', shape.y]);\n if (['rectangle', 'frame', 'image', 'icon'].includes(shape.type)) numbers.push(['width', shape.width], ['height', shape.height]);\n if (shape.type === 'circle') numbers.push(['x', shape.x], ['y', shape.y], ['rx', shape.rx], ['ry', shape.ry]);\n if (shape.startPoint) numbers.push(['startPoint.x', shape.startPoint.x], ['startPoint.y', shape.startPoint.y]);\n if (shape.endPoint) numbers.push(['endPoint.x', shape.endPoint.x], ['endPoint.y', shape.endPoint.y]);\n for (const [field, value] of numbers) if (!Number.isFinite(Number(value))) errors.push(`Shape ${index} has invalid ${field}`);\n if (['rectangle', 'frame', 'image', 'icon'].includes(shape.type) && (Number(shape.width) <= 0 || Number(shape.height) <= 0)) errors.push(`Shape ${index} must have positive dimensions`);\n if (shape.type === 'circle' && (Number(shape.rx) <= 0 || Number(shape.ry) <= 0)) errors.push(`Shape ${index} must have positive radii`);\n if (shape.type === 'freehandStroke' && (!Array.isArray(shape.points) || shape.points.length < 2 || shape.points.length > 4096)) errors.push(`Shape ${index} has invalid freehand points`);\n if (shape.type === 'text' && typeof shape.groupHTML !== 'string') errors.push(`Shape ${index} is missing text markup`);\n if (shape.type === 'code' && typeof shape.groupHTML !== 'string') errors.push(`Shape ${index} is missing code markup`);\n if (shape.type === 'image' && typeof shape.href !== 'string') errors.push(`Shape ${index} is missing image href`);\n if (shape.type === 'icon' && typeof shape.elementHTML !== 'string') errors.push(`Shape ${index} is missing icon markup`);\n}\n\nexport function getSceneSummary(scene) {\n const counts = {};\n for (const shape of scene.shapes || []) counts[shape.type] = (counts[shape.type] || 0) + 1;\n return { name: scene.name, format: scene.format, version: scene.version, revision: Number(scene.mcpRevision || 0), shapeCount: scene.shapes?.length || 0, counts, bounds: getSceneBounds(scene) };\n}\n\nexport function getSceneBounds(scene) {\n const boxes = (scene.shapes || []).map(shapeBounds).filter(Boolean);\n if (!boxes.length) return null;\n const minX = Math.min(...boxes.map((b) => b.x)), minY = Math.min(...boxes.map((b) => b.y));\n const maxX = Math.max(...boxes.map((b) => b.x + b.width)), maxY = Math.max(...boxes.map((b) => b.y + b.height));\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\nexport function shapeBounds(shape) {\n if (shape.type === 'circle') return { x: shape.x - shape.rx, y: shape.y - shape.ry, width: shape.rx * 2, height: shape.ry * 2 };\n if (shape.startPoint && shape.endPoint) {\n const x = Math.min(shape.startPoint.x, shape.endPoint.x), y = Math.min(shape.startPoint.y, shape.endPoint.y);\n return { x, y, width: Math.abs(shape.endPoint.x - shape.startPoint.x), height: Math.abs(shape.endPoint.y - shape.startPoint.y) };\n }\n if (Array.isArray(shape.points) && shape.points.length) {\n const xs = shape.points.map((p) => p[0]), ys = shape.points.map((p) => p[1]);\n return { x: Math.min(...xs), y: Math.min(...ys), width: Math.max(...xs) - Math.min(...xs), height: Math.max(...ys) - Math.min(...ys) };\n }\n if (shape.type === 'text') return { x: finite(shape.x), y: finite(shape.y), width: 160, height: 32 };\n return { x: finite(shape.x), y: finite(shape.y), width: positive(shape.width), height: positive(shape.height) };\n}\n\nexport function mergeTemplateScene(sceneInput, templateInput, { x, y } = {}) {\n const scene = clone(sceneInput), template = clone(templateInput);\n const validation = validateScene(template);\n if (!validation.valid) throw new Error(`Template scene is invalid: ${validation.errors.join('; ')}`);\n if (scene.shapes.length + template.shapes.length > MAX_SHAPES) throw new Error(`Imported template would exceed ${MAX_SHAPES} shapes`);\n const bounds = getSceneBounds(template) || { x: 0, y: 0 };\n const targetX = finite(x, scene.viewport?.x || 0), targetY = finite(y, scene.viewport?.y || 0);\n const dx = targetX - bounds.x, dy = targetY - bounds.y;\n const idMap = new Map(template.shapes.map((shape) => [shape.shapeID, `${shape.type}-${crypto.randomUUID()}`]));\n const imported = template.shapes.map((shape) => {\n const moved = translateShape(shape, dx, dy);\n moved.shapeID = idMap.get(shape.shapeID);\n if (moved.parentFrame) moved.parentFrame = idMap.get(moved.parentFrame) || null;\n if (Array.isArray(moved.containedShapeIDs)) moved.containedShapeIDs = moved.containedShapeIDs.map((id) => idMap.get(id)).filter(Boolean);\n if (moved.startAttachmentID) moved.startAttachmentID = idMap.get(moved.startAttachmentID) || null;\n if (moved.endAttachmentID) moved.endAttachmentID = idMap.get(moved.endAttachmentID) || null;\n if (moved.groupHTML) moved.groupHTML = moved.groupHTML.split(shape.shapeID).join(moved.shapeID);\n if (moved.elementHTML) moved.elementHTML = moved.elementHTML.split(shape.shapeID).join(moved.shapeID);\n return moved;\n });\n scene.shapes.push(...imported);\n scene.mcpRevision = Number(scene.mcpRevision || 0) + 1;\n scene.updatedAt = new Date().toISOString();\n return { scene, revision: scene.mcpRevision, importedShapeIDs: imported.map((shape) => shape.shapeID) };\n}\n\nexport const MCP_LIMITS = Object.freeze({ maxShapes: MAX_SHAPES, maxOperations: MAX_OPERATIONS });\n", "import { createInterface } from 'node:readline';\n\nconst MAX_MESSAGE_BYTES = 10 * 1024 * 1024;\n\nexport function serveLixSketchStdio(server, { input = process.stdin, output = process.stdout } = {}) {\n if (!server?.handleRequest) throw new Error('A LixSketch MCP server is required');\n const lines = createInterface({ input, crlfDelay: Infinity, terminal: false });\n let processing = Promise.resolve();\n let resolveClosed;\n const closed = new Promise((resolve) => { resolveClosed = resolve; });\n const send = (message) => output.write(`${JSON.stringify(message)}\\n`);\n\n lines.on('line', (line) => {\n processing = processing.then(async () => {\n if (!line.trim()) return;\n if (Buffer.byteLength(line, 'utf8') > MAX_MESSAGE_BYTES) {\n send({ jsonrpc: '2.0', id: null, error: { code: -32600, message: 'MCP request exceeds the 10 MB limit' } });\n return;\n }\n let request;\n try { request = JSON.parse(line); }\n catch { send({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } }); return; }\n if (request.jsonrpc !== '2.0' || typeof request.method !== 'string') {\n if (request.id !== undefined) send({ jsonrpc: '2.0', id: request.id ?? null, error: { code: -32600, message: 'Invalid Request' } });\n return;\n }\n try {\n const result = await server.handleRequest(request);\n if (request.id !== undefined && result !== undefined) send({ jsonrpc: '2.0', id: request.id, result });\n } catch (error) {\n if (request.id !== undefined) send({ jsonrpc: '2.0', id: request.id, error: { code: Number(error?.code) || -32603, message: error?.message || 'Internal error' } });\n }\n }).catch((error) => {\n process.stderr.write(`LixSketch MCP transport error: ${error?.message || error}\\n`);\n });\n });\n lines.once('close', () => processing.finally(resolveClosed));\n\n return {\n closed,\n close: async () => {\n lines.close();\n await processing;\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { mkdir, open, readFile, rename, stat, unlink } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\nimport { createEmptyScene, validateScene } from './scene.js';\n\nconst MAX_SCENE_FILE_BYTES = 20 * 1024 * 1024;\n\nexport class FileSceneStore {\n constructor(filePath) {\n if (!filePath) throw new Error('A scene file path is required');\n this.filePath = resolve(filePath);\n this.writeChain = Promise.resolve();\n }\n\n async read() {\n try {\n const details = await stat(this.filePath);\n if (details.size > MAX_SCENE_FILE_BYTES) {\n throw new Error(`Scene file exceeds the ${MAX_SCENE_FILE_BYTES / 1024 / 1024} MB MCP limit`);\n }\n const source = await readFile(this.filePath, 'utf8');\n const scene = JSON.parse(source);\n const validation = validateScene(scene);\n if (!validation.valid) throw new Error(`Invalid scene file: ${validation.errors.join('; ')}`);\n return scene;\n } catch (error) {\n if (error?.code !== 'ENOENT') throw error;\n const scene = createEmptyScene();\n await this.write(scene);\n return scene;\n }\n }\n\n async write(scene) {\n const validation = validateScene(scene);\n if (!validation.valid) throw new Error(`Refusing to store invalid scene: ${validation.errors.join('; ')}`);\n this.writeChain = this.writeChain.then(async () => {\n await mkdir(dirname(this.filePath), { recursive: true });\n const temporary = `${this.filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;\n try {\n const serialized = `${JSON.stringify(scene, null, 2)}\\n`;\n if (Buffer.byteLength(serialized, 'utf8') > MAX_SCENE_FILE_BYTES) {\n throw new Error(`Scene file exceeds the ${MAX_SCENE_FILE_BYTES / 1024 / 1024} MB MCP limit`);\n }\n const handle = await open(temporary, 'wx', 0o600);\n try {\n await handle.writeFile(serialized, 'utf8');\n await handle.sync();\n } finally {\n await handle.close();\n }\n await rename(temporary, this.filePath);\n } catch (error) {\n await unlink(temporary).catch(() => {});\n throw error;\n }\n });\n await this.writeChain;\n return structuredClone(scene);\n }\n\n async info() {\n try {\n const details = await stat(this.filePath);\n return { path: this.filePath, sizeBytes: details.size, updatedAt: details.mtime.toISOString() };\n } catch (error) {\n if (error?.code === 'ENOENT') return { path: this.filePath, sizeBytes: 0, updatedAt: null };\n throw error;\n }\n }\n}\n", "const FORMAT = 'lixsketch';\nconst VERSION = 1;\nconst MAX_SHAPES = 5000;\nconst MAX_OPERATIONS = 500;\nconst SCENE_TYPES = new Set(['rectangle', 'circle', 'line', 'arrow', 'freehandStroke', 'frame', 'text', 'code', 'image', 'icon']);\nconst WRITABLE_TYPES = new Set(['rectangle', 'circle', 'line', 'arrow', 'freehandStroke', 'frame', 'text']);\n\nconst clone = (value) => JSON.parse(JSON.stringify(value));\nconst finite = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;\nconst positive = (value, fallback = 1) => Math.max(1, finite(value, fallback));\n\nexport function createEmptyScene(name = 'MCP Canvas') {\n return {\n format: FORMAT,\n version: VERSION,\n sessionID: `mcp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,\n name: String(name || 'MCP Canvas').trim().slice(0, 72),\n createdAt: new Date().toISOString(),\n viewport: { x: 0, y: 0, width: 1280, height: 720 },\n zoom: 1,\n mcpRevision: 0,\n shapes: [],\n };\n}\n\nexport function validateScene(scene) {\n const errors = [];\n if (!scene || typeof scene !== 'object') return { valid: false, errors: ['Scene must be an object'] };\n if (scene.format !== FORMAT) errors.push(`Scene format must be \"${FORMAT}\"`);\n if (scene.version !== VERSION) errors.push(`Scene version must be ${VERSION}`);\n if (!Array.isArray(scene.shapes)) errors.push('Scene shapes must be an array');\n if (Array.isArray(scene.shapes) && scene.shapes.length > MAX_SHAPES) errors.push(`Scene exceeds ${MAX_SHAPES} shapes`);\n const ids = new Set();\n for (const [index, shape] of (scene.shapes || []).entries()) {\n if (!shape || typeof shape !== 'object') { errors.push(`Shape ${index} must be an object`); continue; }\n if (!SCENE_TYPES.has(shape.type)) errors.push(`Shape ${index} has unsupported type \"${shape.type}\"`);\n if (!shape.shapeID || typeof shape.shapeID !== 'string') errors.push(`Shape ${index} is missing shapeID`);\n else if (ids.has(shape.shapeID)) errors.push(`Duplicate shapeID \"${shape.shapeID}\"`);\n else ids.add(shape.shapeID);\n validateShapeGeometry(shape, index, errors);\n }\n return { valid: errors.length === 0, errors };\n}\n\nfunction escapeXml(value) {\n return String(value).replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('\"', '&quot;').replaceAll(\"'\", '&apos;');\n}\n\nfunction normalizeOptions(value = {}) {\n return {\n roughness: Math.max(0, Math.min(3, finite(value.roughness, 1.2))),\n stroke: typeof value.stroke === 'string' ? value.stroke : '#8b76d6',\n strokeWidth: Math.max(0.5, Math.min(20, finite(value.strokeWidth, 2))),\n fill: typeof value.fill === 'string' ? value.fill : 'transparent',\n fillStyle: typeof value.fillStyle === 'string' ? value.fillStyle : 'solid',\n opacity: Math.max(0, Math.min(1, finite(value.opacity, 1))),\n };\n}\n\nfunction createTextShape(input, shapeID) {\n const x = finite(input.x), y = finite(input.y), rotation = finite(input.rotation);\n const text = String(input.text || '').slice(0, 10000);\n const fontSize = Math.max(8, Math.min(160, finite(input.fontSize, 20)));\n const color = typeof input.color === 'string' ? input.color : '#e8e3f3';\n const family = typeof input.fontFamily === 'string' ? input.fontFamily.slice(0, 80) : 'lixFont';\n const transform = `translate(${x}, ${y})${rotation ? ` rotate(${rotation}, 0, 0)` : ''}`;\n const lines = text.split('\\n').map((line, index) => `<tspan x=\"0\" dy=\"${index === 0 ? 0 : '1.2em'}\">${escapeXml(line || ' ')}</tspan>`).join('');\n return {\n shapeID, type: 'text', x, y, rotation,\n mcpText: text, mcpFontSize: fontSize, mcpColor: color, mcpFontFamily: family,\n groupHTML: `<g id=\"${escapeXml(shapeID)}\" data-type=\"text-group\" data-x=\"${x}\" data-y=\"${y}\" transform=\"${transform}\"><text id=\"${escapeXml(shapeID)}-text\" x=\"0\" y=\"0\" fill=\"${escapeXml(color)}\" font-size=\"${fontSize}\" font-family=\"${escapeXml(family)}\" dominant-baseline=\"hanging\" white-space=\"pre\" pointer-events=\"painted\" data-type=\"text\" data-initial-size=\"${fontSize}\" data-initial-font=\"${escapeXml(family)}\" data-initial-color=\"${escapeXml(color)}\">${lines}</text></g>`,\n };\n}\n\nexport function normalizeShape(input, existingIds = new Set()) {\n if (!input || typeof input !== 'object') throw new Error('Shape must be an object');\n if (!WRITABLE_TYPES.has(input.type)) throw new Error(`Shape type \"${input.type}\" is read-only through MCP`);\n let shapeID = String(input.shapeID || `${input.type}-${crypto.randomUUID()}`).slice(0, 120);\n while (existingIds.has(shapeID)) shapeID = `${input.type}-${crypto.randomUUID()}`;\n const base = { shapeID, type: input.type, rotation: finite(input.rotation), options: normalizeOptions(input.options), groupId: input.groupId || null, parentFrame: input.parentFrame || null, docBlockIds: [] };\n switch (input.type) {\n case 'rectangle': return { ...base, x: finite(input.x), y: finite(input.y), width: positive(input.width, 160), height: positive(input.height, 90) };\n case 'circle': return { ...base, x: finite(input.x), y: finite(input.y), rx: positive(input.rx, 60), ry: positive(input.ry, 60) };\n case 'line': return { ...base, startPoint: point(input.startPoint), endPoint: point(input.endPoint, 120), isCurved: Boolean(input.isCurved), controlPoint: input.controlPoint ? point(input.controlPoint) : null };\n case 'arrow': return { ...base, startPoint: point(input.startPoint), endPoint: point(input.endPoint, 120), arrowHeadStyle: input.arrowHeadStyle || 'triangle', arrowOutlineStyle: input.arrowOutlineStyle || 'solid', arrowCurved: Boolean(input.arrowCurved), arrowCurveAmount: finite(input.arrowCurveAmount, 0.2) };\n case 'freehandStroke': {\n const points = (Array.isArray(input.points) ? input.points : []).slice(0, 4096).map((entry) => [finite(entry?.[0]), finite(entry?.[1]), finite(entry?.[2], 0.5)]);\n if (points.length < 2) throw new Error('freehandStroke requires at least two points');\n return { ...base, points };\n }\n case 'frame': return { ...base, x: finite(input.x), y: finite(input.y), width: positive(input.width, 640), height: positive(input.height, 360), frameName: String(input.frameName || 'Frame').slice(0, 80), fillStyle: input.fillStyle || 'transparent', fillColor: input.fillColor || '#1e1e28', gridSize: positive(input.gridSize, 20), containedShapeIDs: [] };\n case 'text': return { ...base, ...createTextShape(input, shapeID) };\n default: throw new Error(`Unsupported shape type \"${input.type}\"`);\n }\n}\n\nfunction point(value, fallbackX = 0) {\n return { x: finite(value?.x, fallbackX), y: finite(value?.y) };\n}\n\nfunction translateShape(shape, dx, dy) {\n const moved = clone(shape);\n if (moved.startPoint) { moved.startPoint.x += dx; moved.startPoint.y += dy; }\n if (moved.endPoint) { moved.endPoint.x += dx; moved.endPoint.y += dy; }\n if (moved.controlPoint) { moved.controlPoint.x += dx; moved.controlPoint.y += dy; }\n if (moved.controlPoint1) { moved.controlPoint1.x += dx; moved.controlPoint1.y += dy; }\n if (moved.controlPoint2) { moved.controlPoint2.x += dx; moved.controlPoint2.y += dy; }\n if (Array.isArray(moved.points)) moved.points = moved.points.map((p) => [p[0] + dx, p[1] + dy, ...p.slice(2)]);\n if (Number.isFinite(moved.x)) moved.x += dx;\n if (Number.isFinite(moved.y)) moved.y += dy;\n if (moved.type === 'text' && moved.groupHTML) {\n moved.groupHTML = moved.groupHTML\n .replace(/data-x=\"[^\"]*\"/, `data-x=\"${moved.x}\"`)\n .replace(/data-y=\"[^\"]*\"/, `data-y=\"${moved.y}\"`)\n .replace(/transform=\"translate\\([^)]*\\)/, `transform=\"translate(${moved.x}, ${moved.y})`);\n }\n if (moved.type === 'code' && moved.groupHTML) {\n moved.groupHTML = moved.groupHTML\n .replace(/data-x=\"[^\"]*\"/, `data-x=\"${moved.x}\"`)\n .replace(/data-y=\"[^\"]*\"/, `data-y=\"${moved.y}\"`)\n .replace(/transform=\"translate\\([^)]*\\)/, `transform=\"translate(${moved.x}, ${moved.y})`);\n }\n if (moved.type === 'icon' && moved.elementHTML) {\n moved.elementHTML = moved.elementHTML\n .replace(/\\bx=\"[^\"]*\"/, `x=\"${moved.x}\"`)\n .replace(/\\by=\"[^\"]*\"/, `y=\"${moved.y}\"`);\n }\n return moved;\n}\n\nexport function applyScenePatch(sceneInput, operations, { expectedRevision, dryRun = false } = {}) {\n const scene = clone(sceneInput);\n const check = validateScene(scene);\n if (!check.valid) throw new Error(`Invalid scene: ${check.errors.join('; ')}`);\n if (!Array.isArray(operations) || operations.length === 0) throw new Error('At least one operation is required');\n if (operations.length > MAX_OPERATIONS) throw new Error(`Patch exceeds ${MAX_OPERATIONS} operations`);\n const revision = Number(scene.mcpRevision || 0);\n if (expectedRevision !== undefined && Number(expectedRevision) !== revision) throw new Error(`Revision conflict: expected ${expectedRevision}, current ${revision}`);\n const changedIds = new Set();\n for (const operation of operations) {\n if (!operation || typeof operation !== 'object') throw new Error('Each operation must be an object');\n if (operation.op === 'add') {\n if (scene.shapes.length >= MAX_SHAPES) throw new Error(`Scene exceeds ${MAX_SHAPES} shapes`);\n const ids = new Set(scene.shapes.map((shape) => shape.shapeID));\n const shape = normalizeShape(operation.shape, ids);\n scene.shapes.push(shape); changedIds.add(shape.shapeID);\n } else if (operation.op === 'update') {\n const index = scene.shapes.findIndex((shape) => shape.shapeID === operation.shapeID);\n if (index < 0) throw new Error(`Shape \"${operation.shapeID}\" was not found`);\n const immutable = scene.shapes[index];\n scene.shapes[index] = applyShapeChanges(immutable, operation.changes || {}); changedIds.add(immutable.shapeID);\n } else if (operation.op === 'delete') {\n const ids = new Set(Array.isArray(operation.shapeIDs) ? operation.shapeIDs : [operation.shapeID]);\n const before = scene.shapes.length;\n scene.shapes = scene.shapes.filter((shape) => !ids.has(shape.shapeID));\n if (scene.shapes.length === before) throw new Error('No requested shapes were found');\n scene.shapes.forEach((shape) => {\n if (ids.has(shape.parentFrame)) shape.parentFrame = null;\n if (Array.isArray(shape.containedShapeIDs)) shape.containedShapeIDs = shape.containedShapeIDs.filter((id) => !ids.has(id));\n });\n ids.forEach((id) => changedIds.add(id));\n } else if (operation.op === 'translate') {\n const ids = new Set(operation.shapeIDs || []), dx = finite(operation.dx), dy = finite(operation.dy);\n if (!ids.size) throw new Error('translate requires shapeIDs');\n scene.shapes = scene.shapes.map((shape) => ids.has(shape.shapeID) ? translateShape(shape, dx, dy) : shape);\n ids.forEach((id) => changedIds.add(id));\n } else if (operation.op === 'rename_canvas') {\n scene.name = String(operation.name || '').trim().slice(0, 72) || scene.name;\n } else throw new Error(`Unsupported operation \"${operation.op}\"`);\n }\n reconcileFrameContainment(scene);\n scene.mcpRevision = revision + 1;\n scene.updatedAt = new Date().toISOString();\n const result = validateScene(scene);\n if (!result.valid) throw new Error(`Patch produced an invalid scene: ${result.errors.join('; ')}`);\n return { scene, revision: scene.mcpRevision, dryRun: Boolean(dryRun), changedShapeIDs: [...changedIds] };\n}\n\nfunction reconcileFrameContainment(scene) {\n const frames = new Map(scene.shapes.filter((shape) => shape.type === 'frame').map((shape) => [shape.shapeID, shape]));\n for (const frame of frames.values()) frame.containedShapeIDs = [];\n for (const shape of scene.shapes) {\n if (!shape.parentFrame) continue;\n const frame = frames.get(shape.parentFrame);\n if (!frame) throw new Error(`Shape \"${shape.shapeID}\" references missing frame \"${shape.parentFrame}\"`);\n frame.containedShapeIDs.push(shape.shapeID);\n }\n}\n\nfunction applyShapeChanges(shape, changes) {\n if (!changes || typeof changes !== 'object' || Array.isArray(changes)) throw new Error('Shape changes must be an object');\n const allowed = {\n rectangle: ['x', 'y', 'width', 'height', 'rotation', 'options', 'groupId', 'parentFrame'],\n circle: ['x', 'y', 'rx', 'ry', 'rotation', 'options', 'groupId', 'parentFrame'],\n line: ['startPoint', 'endPoint', 'controlPoint', 'isCurved', 'options', 'groupId', 'parentFrame'],\n arrow: ['startPoint', 'endPoint', 'controlPoint1', 'controlPoint2', 'arrowHeadStyle', 'arrowOutlineStyle', 'arrowCurved', 'arrowCurveAmount', 'options', 'groupId', 'parentFrame'],\n freehandStroke: ['points', 'rotation', 'options', 'groupId', 'parentFrame'],\n frame: ['x', 'y', 'width', 'height', 'rotation', 'frameName', 'fillStyle', 'fillColor', 'gridSize', 'options', 'groupId', 'parentFrame'],\n text: ['x', 'y', 'rotation', 'text', 'fontSize', 'color', 'fontFamily', 'groupId', 'parentFrame'],\n }[shape.type] || [];\n const rejected = Object.keys(changes).filter((key) => !allowed.includes(key));\n if (rejected.length) throw new Error(`Cannot update ${shape.type} fields: ${rejected.join(', ')}`);\n if (shape.type === 'text') {\n const text = changes.text ?? shape.mcpText ?? extractText(shape.groupHTML);\n return { ...shape, ...createTextShape({ x: changes.x ?? shape.x, y: changes.y ?? shape.y, rotation: changes.rotation ?? shape.rotation, text, fontSize: changes.fontSize ?? shape.mcpFontSize, color: changes.color ?? shape.mcpColor, fontFamily: changes.fontFamily ?? shape.mcpFontFamily }, shape.shapeID), groupId: changes.groupId ?? shape.groupId, parentFrame: changes.parentFrame ?? shape.parentFrame };\n }\n const copy = { ...clone(shape), ...clone(changes), shapeID: shape.shapeID, type: shape.type };\n if (changes.options) copy.options = { ...(shape.options || {}), ...normalizeOptions({ ...(shape.options || {}), ...changes.options }) };\n return copy;\n}\n\nfunction extractText(groupHTML = '') {\n return String(groupHTML).replace(/<[^>]+>/g, ' ').replace(/\\s+/g, ' ').trim();\n}\n\nfunction validateShapeGeometry(shape, index, errors) {\n const numbers = [];\n if (['rectangle', 'frame', 'text', 'code', 'image', 'icon'].includes(shape.type)) numbers.push(['x', shape.x], ['y', shape.y]);\n if (['rectangle', 'frame', 'image', 'icon'].includes(shape.type)) numbers.push(['width', shape.width], ['height', shape.height]);\n if (shape.type === 'circle') numbers.push(['x', shape.x], ['y', shape.y], ['rx', shape.rx], ['ry', shape.ry]);\n if (shape.startPoint) numbers.push(['startPoint.x', shape.startPoint.x], ['startPoint.y', shape.startPoint.y]);\n if (shape.endPoint) numbers.push(['endPoint.x', shape.endPoint.x], ['endPoint.y', shape.endPoint.y]);\n for (const [field, value] of numbers) if (!Number.isFinite(Number(value))) errors.push(`Shape ${index} has invalid ${field}`);\n if (['rectangle', 'frame', 'image', 'icon'].includes(shape.type) && (Number(shape.width) <= 0 || Number(shape.height) <= 0)) errors.push(`Shape ${index} must have positive dimensions`);\n if (shape.type === 'circle' && (Number(shape.rx) <= 0 || Number(shape.ry) <= 0)) errors.push(`Shape ${index} must have positive radii`);\n if (shape.type === 'freehandStroke' && (!Array.isArray(shape.points) || shape.points.length < 2 || shape.points.length > 4096)) errors.push(`Shape ${index} has invalid freehand points`);\n if (shape.type === 'text' && typeof shape.groupHTML !== 'string') errors.push(`Shape ${index} is missing text markup`);\n if (shape.type === 'code' && typeof shape.groupHTML !== 'string') errors.push(`Shape ${index} is missing code markup`);\n if (shape.type === 'image' && typeof shape.href !== 'string') errors.push(`Shape ${index} is missing image href`);\n if (shape.type === 'icon' && typeof shape.elementHTML !== 'string') errors.push(`Shape ${index} is missing icon markup`);\n}\n\nexport function getSceneSummary(scene) {\n const counts = {};\n for (const shape of scene.shapes || []) counts[shape.type] = (counts[shape.type] || 0) + 1;\n return { name: scene.name, format: scene.format, version: scene.version, revision: Number(scene.mcpRevision || 0), shapeCount: scene.shapes?.length || 0, counts, bounds: getSceneBounds(scene) };\n}\n\nexport function getSceneBounds(scene) {\n const boxes = (scene.shapes || []).map(shapeBounds).filter(Boolean);\n if (!boxes.length) return null;\n const minX = Math.min(...boxes.map((b) => b.x)), minY = Math.min(...boxes.map((b) => b.y));\n const maxX = Math.max(...boxes.map((b) => b.x + b.width)), maxY = Math.max(...boxes.map((b) => b.y + b.height));\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\nexport function shapeBounds(shape) {\n if (shape.type === 'circle') return { x: shape.x - shape.rx, y: shape.y - shape.ry, width: shape.rx * 2, height: shape.ry * 2 };\n if (shape.startPoint && shape.endPoint) {\n const x = Math.min(shape.startPoint.x, shape.endPoint.x), y = Math.min(shape.startPoint.y, shape.endPoint.y);\n return { x, y, width: Math.abs(shape.endPoint.x - shape.startPoint.x), height: Math.abs(shape.endPoint.y - shape.startPoint.y) };\n }\n if (Array.isArray(shape.points) && shape.points.length) {\n const xs = shape.points.map((p) => p[0]), ys = shape.points.map((p) => p[1]);\n return { x: Math.min(...xs), y: Math.min(...ys), width: Math.max(...xs) - Math.min(...xs), height: Math.max(...ys) - Math.min(...ys) };\n }\n if (shape.type === 'text') return { x: finite(shape.x), y: finite(shape.y), width: 160, height: 32 };\n return { x: finite(shape.x), y: finite(shape.y), width: positive(shape.width), height: positive(shape.height) };\n}\n\nexport function mergeTemplateScene(sceneInput, templateInput, { x, y } = {}) {\n const scene = clone(sceneInput), template = clone(templateInput);\n const validation = validateScene(template);\n if (!validation.valid) throw new Error(`Template scene is invalid: ${validation.errors.join('; ')}`);\n if (scene.shapes.length + template.shapes.length > MAX_SHAPES) throw new Error(`Imported template would exceed ${MAX_SHAPES} shapes`);\n const bounds = getSceneBounds(template) || { x: 0, y: 0 };\n const targetX = finite(x, scene.viewport?.x || 0), targetY = finite(y, scene.viewport?.y || 0);\n const dx = targetX - bounds.x, dy = targetY - bounds.y;\n const idMap = new Map(template.shapes.map((shape) => [shape.shapeID, `${shape.type}-${crypto.randomUUID()}`]));\n const imported = template.shapes.map((shape) => {\n const moved = translateShape(shape, dx, dy);\n moved.shapeID = idMap.get(shape.shapeID);\n if (moved.parentFrame) moved.parentFrame = idMap.get(moved.parentFrame) || null;\n if (Array.isArray(moved.containedShapeIDs)) moved.containedShapeIDs = moved.containedShapeIDs.map((id) => idMap.get(id)).filter(Boolean);\n if (moved.startAttachmentID) moved.startAttachmentID = idMap.get(moved.startAttachmentID) || null;\n if (moved.endAttachmentID) moved.endAttachmentID = idMap.get(moved.endAttachmentID) || null;\n if (moved.groupHTML) moved.groupHTML = moved.groupHTML.split(shape.shapeID).join(moved.shapeID);\n if (moved.elementHTML) moved.elementHTML = moved.elementHTML.split(shape.shapeID).join(moved.shapeID);\n return moved;\n });\n scene.shapes.push(...imported);\n scene.mcpRevision = Number(scene.mcpRevision || 0) + 1;\n scene.updatedAt = new Date().toISOString();\n return { scene, revision: scene.mcpRevision, importedShapeIDs: imported.map((shape) => shape.shapeID) };\n}\n\nexport const MCP_LIMITS = Object.freeze({ maxShapes: MAX_SHAPES, maxOperations: MAX_OPERATIONS });\n", "import { createInterface } from 'node:readline';\n\nconst MAX_MESSAGE_BYTES = 10 * 1024 * 1024;\n\nexport function serveLixSketchStdio(server, { input = process.stdin, output = process.stdout } = {}) {\n if (!server?.handleRequest) throw new Error('A LixSketch MCP server is required');\n const lines = createInterface({ input, crlfDelay: Infinity, terminal: false });\n let processing = Promise.resolve();\n let resolveClosed;\n const closed = new Promise((resolve) => { resolveClosed = resolve; });\n const send = (message) => output.write(`${JSON.stringify(message)}\\n`);\n\n lines.on('line', (line) => {\n processing = processing.then(async () => {\n if (!line.trim()) return;\n if (Buffer.byteLength(line, 'utf8') > MAX_MESSAGE_BYTES) {\n send({ jsonrpc: '2.0', id: null, error: { code: -32600, message: 'MCP request exceeds the 10 MB limit' } });\n return;\n }\n let request;\n try { request = JSON.parse(line); }\n catch { send({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } }); return; }\n if (request.jsonrpc !== '2.0' || typeof request.method !== 'string') {\n if (request.id !== undefined) send({ jsonrpc: '2.0', id: request.id ?? null, error: { code: -32600, message: 'Invalid Request' } });\n return;\n }\n try {\n const result = await server.handleRequest(request);\n if (request.id !== undefined && result !== undefined) send({ jsonrpc: '2.0', id: request.id, result });\n } catch (error) {\n if (request.id !== undefined) send({ jsonrpc: '2.0', id: request.id, error: { code: Number(error?.code) || -32603, message: error?.message || 'Internal error' } });\n }\n }).catch((error) => {\n process.stderr.write(`LixSketch MCP transport error: ${error?.message || error}\\n`);\n });\n });\n lines.once('close', () => processing.finally(resolveClosed));\n\n return {\n closed,\n close: async () => {\n lines.close();\n await processing;\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["resolve"]
7
7
  }