Spaces:
Sleeping
Sleeping
| 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, | |
| }); | |
| } | |