import { createBrowserId } from './ids.js'; import { createComponentSubgraph, stripRuntimeFromNode } from './nodeRegistry.js'; export const COMPONENT_LIBRARY_KEY = 'nodes_ui_flow_component_library_v1'; function clonePlainData(value) { return JSON.parse(JSON.stringify(value)); } function normalizeTemplate(template = {}) { return { id: typeof template.id === 'string' && template.id ? template.id : createBrowserId('component_template'), title: typeof template.title === 'string' && template.title.trim() ? template.title.trim() : 'Component', description: typeof template.description === 'string' ? template.description : '', subgraph: template.subgraph && typeof template.subgraph === 'object' ? clonePlainData(template.subgraph) : clonePlainData(createComponentSubgraph()), savedAt: typeof template.savedAt === 'number' ? template.savedAt : Date.now(), }; } export function normalizeComponentTemplates(templates = []) { if (!Array.isArray(templates)) { return []; } return templates .map((template) => normalizeTemplate(template)) .sort((left, right) => right.savedAt - left.savedAt); } export function mergeComponentTemplates(...sources) { const merged = []; const seen = new Set(); sources .flatMap((source) => normalizeComponentTemplates(source)) .forEach((template) => { if (seen.has(template.id)) { return; } seen.add(template.id); merged.push(template); }); return merged; } export function loadComponentLibrary() { if (typeof window === 'undefined') { return []; } try { const raw = window.localStorage.getItem(COMPONENT_LIBRARY_KEY); if (!raw) { return []; } const parsed = JSON.parse(raw); if (!Array.isArray(parsed)) { return []; } return normalizeComponentTemplates(parsed); } catch (error) { console.error('Failed to load component library:', error); return []; } } export function persistComponentLibrary(templates) { if (typeof window === 'undefined') { return; } const normalized = normalizeComponentTemplates(templates); window.localStorage.setItem(COMPONENT_LIBRARY_KEY, JSON.stringify(normalized)); } export function upsertComponentTemplateFromNode(templates, node) { const persistedNode = stripRuntimeFromNode(node); const nextTemplate = normalizeTemplate({ id: persistedNode.data?.libraryId || createBrowserId('component_template'), title: persistedNode.data?.title, description: persistedNode.data?.description, subgraph: persistedNode.data?.subgraph, savedAt: Date.now(), }); const nextTemplates = [ nextTemplate, ...templates.filter((template) => template.id !== nextTemplate.id), ]; return { template: nextTemplate, templates: nextTemplates, }; } export function buildComponentNodeDataFromTemplate(template) { const normalized = normalizeTemplate(template); return clonePlainData({ title: normalized.title, description: normalized.description, subgraph: normalized.subgraph, libraryId: normalized.id, }); }