fix(liveview): nest docker children, apply theme & custom style

- Live view rebuilt parent map from proxmox/group only, so docker_container
  (and any vm/lxc with container_mode=true) lost parentId and rendered
  outside its host. Match App.tsx logic: include any node with
  container_mode=true.
- Live view ignored viewport.theme_id and custom_style on load, so the
  shared canvas always rendered in default theme. Apply both like the
  authenticated canvas does.
- Backend /liveview response now includes custom_style from CanvasState.

Adds regression tests on both ends.
This commit is contained in:
Pouzor
2026-05-16 21:49:55 +02:00
parent be705f0cb9
commit 5630e7d202
4 changed files with 82 additions and 3 deletions
+8 -3
View File
@@ -29,7 +29,7 @@ import { nodeTypes } from '@/components/canvas/nodes/nodeTypes'
import { edgeTypes } from '@/components/canvas/edges/edgeTypes'
import { deserializeApiNode, deserializeApiEdge, type ApiNode, type ApiEdge } from '@/utils/canvasSerializer'
import { liveviewApi } from '@/api/client'
import type { NodeData } from '@/types'
import type { NodeData, CustomStyleDef } from '@/types'
const STANDALONE = import.meta.env.VITE_STANDALONE === 'true'
const STORAGE_KEY = 'homelable_canvas'
@@ -40,6 +40,8 @@ function LiveViewCanvas() {
const { nodes, edges, loadCanvas, fitViewPending, clearFitViewPending } = useCanvasStore()
const { fitView } = useReactFlow()
const activeTheme = useThemeStore((s) => s.activeTheme)
const setTheme = useThemeStore((s) => s.setTheme)
const setCustomStyle = useThemeStore((s) => s.setCustomStyle)
const theme = THEMES[activeTheme]
// Derive initial view state synchronously (avoids calling setState inside an effect):
// - standalone → always ready (localStorage, no key required)
@@ -73,9 +75,12 @@ function LiveViewCanvas() {
const { nodes: apiNodes, edges: apiEdges } = res.data
const proxmoxMap = new Map<string, boolean>(
(apiNodes as ApiNode[])
.filter((n: ApiNode) => n.type === 'proxmox' || n.type === 'group')
.map((n: ApiNode) => [n.id, n.type === 'group' ? true : n.container_mode !== false])
.filter((n: ApiNode) => n.type === 'group' || n.container_mode === true)
.map((n: ApiNode) => [n.id, true])
)
const savedTheme = res.data.viewport?.theme_id
if (savedTheme) setTheme(savedTheme)
if (res.data.custom_style) setCustomStyle(res.data.custom_style as CustomStyleDef)
loadCanvas(
(apiNodes as ApiNode[]).map((n) => deserializeApiNode(n, proxmoxMap)),
(apiEdges as ApiEdge[]).map(deserializeApiEdge),