feat: add node help system and registry for extensible node types

- Implemented a help system for different node types (config, render, variable, function) with detailed usage instructions.
- Created a node registry to manage node types, including registration, retrieval, and validation of connections between nodes.
- Defined central node and edge types for the application to streamline state management.
- Added Nunjucks autocomplete functionality to enhance user experience in template editing.
- Developed a syntax highlighting parser for PlantUML and Nunjucks within the CodeMirror editor.
- Registered built-in node types at application startup, including their default configurations and help entries.
- Introduced a theme context provider to manage light/dark mode preferences across the application.
- Created utility functions for class name management using clsx and tailwind-merge.
- Set up Tailwind CSS for styling with custom themes and responsive design.
- Configured Vite for development with proxy settings for backend API calls and Kroki diagram service.
This commit is contained in:
2026-03-09 20:04:31 +01:00
parent 11fd9cd54d
commit b3c2c6711f
67 changed files with 1286 additions and 2124 deletions

90
backend/src/index.js Normal file
View File

@@ -0,0 +1,90 @@
/**
* Minimal Express API: /api/todos CRUD (in-memory).
* No DB, no auth. CORS allowed for frontend. Production-ready env (PORT, CORS_ORIGIN).
*/
const express = require('express')
const cors = require('cors')
const PORT = Number(process.env.PORT) || 8080
const CORS_ORIGIN = process.env.CORS_ORIGIN || 'http://localhost:3000'
const app = express()
app.use(cors({ origin: CORS_ORIGIN }))
app.use(express.json())
// In-memory store (replace with JSON file or DB later)
let todos = [
{ id: '1', title: 'Sample todo', completed: false },
{ id: '2', title: 'Another item', completed: true },
]
let nextId = 3
/** GET /api/todos — list all */
app.get('/api/todos', (req, res) => {
try {
res.json(todos)
} catch (err) {
res.status(500).json({ error: err.message })
}
})
/** GET /api/todos/:id — get one */
app.get('/api/todos/:id', (req, res) => {
try {
const todo = todos.find((t) => t.id === req.params.id)
if (!todo) return res.status(404).json({ error: 'Not found' })
res.json(todo)
} catch (err) {
res.status(500).json({ error: err.message })
}
})
/** POST /api/todos — create */
app.post('/api/todos', (req, res) => {
try {
const { title, completed } = req.body ?? {}
const id = String(nextId++)
const todo = { id, title: title ?? '', completed: Boolean(completed) }
todos.push(todo)
res.status(201).json(todo)
} catch (err) {
res.status(500).json({ error: err.message })
}
})
/** PUT /api/todos/:id — update */
app.put('/api/todos/:id', (req, res) => {
try {
const idx = todos.findIndex((t) => t.id === req.params.id)
if (idx === -1) return res.status(404).json({ error: 'Not found' })
const { title, completed } = req.body ?? {}
if (title !== undefined) todos[idx].title = title
if (completed !== undefined) todos[idx].completed = Boolean(completed)
res.json(todos[idx])
} catch (err) {
res.status(500).json({ error: err.message })
}
})
/** DELETE /api/todos/:id — delete */
app.delete('/api/todos/:id', (req, res) => {
try {
const idx = todos.findIndex((t) => t.id === req.params.id)
if (idx === -1) return res.status(404).json({ error: 'Not found' })
const removed = todos.splice(idx, 1)[0]
res.json(removed)
} catch (err) {
res.status(500).json({ error: err.message })
}
})
/** Health check for Docker / orchestration */
app.get('/health', (req, res) => {
res.status(200).json({ ok: true })
})
app.listen(PORT, '0.0.0.0', () => {
console.log(`Backend listening on port ${PORT} (CORS: ${CORS_ORIGIN})`)
})