A node recorded as its own parent (`parent_id = id`) made the child walk in `translateWaypointsForMovedNodes` recurse forever. The resulting stack overflow was thrown inside the `onNodesChange` reducer, so the whole state update was discarded: the node still selected but would never move. It also renders unparented, sitting wherever its stored coordinates put it rather than inside the container it appears to belong to. Guard the walk, then close every path that could write the row: - `propagate` keeps a `walked` set, matching the cycle guard `orderParentsFirst` already carries. Covers longer cycles (a -> b -> a) too, not just self-parent. - `updateNode` / `addNode` drop a `parent_id` equal to the node's own id. The key is removed rather than nulled, so the rest of the edit still lands and an existing real parent is left alone. - `importYaml` resolves parents by label, so a node naming its own label — or a duplicate label mapping back to it — imported as its own parent. Warn and skip. - `node_dedupe` re-pointed every child of a duplicate at the canonical node, including the canonical node itself when it had been nested under one of its own duplicates. Detach instead, mirroring the self-loop edge deletion below it. - `NodeSave` and the node PATCH route normalize it away. Dropped rather than rejected: a canvas that already carries the bad row must still be able to save, and a 422 would cost the user the whole save. `_repair_self_parent_nodes` clears what is already persisted at startup. The real parent is not recoverable from the row, so NULL returns the node to the top level where the user can re-nest it. Idempotent, and never fatal to boot. A longer cycle is deliberately left alone by the repair — there is no single right link to cut, and the runtime guard keeps the canvas usable either way. ha-relevant: yes
React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- @vitejs/plugin-react uses Babel (or oxc when used in rolldown-vite) for Fast Refresh
- @vitejs/plugin-react-swc uses SWC for Fast Refresh
React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see this documentation.
Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
You can also install eslint-plugin-react-x and eslint-plugin-react-dom for React-specific lint rules:
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])