<script lang="ts">
import type { Box, Position } from '#shared/canvas.util';
import type CanvasNodeEditor from './canvas/CanvasNodeEditor.vue';
type Direction = 'bottom' | 'top' | 'left' | 'right';
const rotation: Record<Direction, string> = {
    top: "180",
    bottom: "0",
    left: "90",
    right: "270"
};
type Element = { type: 'node' | 'edge', id: string };

interface ActionMap {
    move: Position;
    edit: string;
    resize: Box;
    remove: CanvasNode | undefined;
    create: CanvasNode | undefined;
    property: CanvasNode;
}
type Action = keyof ActionMap;
interface HistoryEvent<T extends Action = Action>
{
    event: T;
    actions: HistoryAction<T>[];
}
interface HistoryAction<T extends Action>
{
    element: string;
    from: ActionMap[T];
    to: ActionMap[T];
}

const cancelEvent = (e: Event) => e.preventDefault();
const stopPropagation = (e: Event) => e.stopImmediatePropagation();
function getID(length: number)
{
    for (var id = [], i = 0; i < length; i++)
        id.push((16 * Math.random() | 0).toString(16));
    return id.join("");
}
function center(touches: TouchList): Position
{
    const pos = { x: 0, y: 0 };

    for(const touch of touches)
    {
        pos.x += touch.clientX;
        pos.y += touch.clientY;
    }

    pos.x /= touches.length;
    pos.y /= touches.length;
    return pos;
}
function distance(touches: TouchList): number
{
    const [A, B] = touches;
    return Math.hypot(B.clientX - A.clientX, B.clientY - A.clientY);
}
function contains(group: CanvasNode, node: CanvasNode): boolean
{
    return group.x < node.x && group.y < node.y && group.x + group.width > node.x + node.width && group.y + group.height > node.y + node.height;
}
</script>

<script setup lang="ts">
import { Icon } from '@iconify/vue/dist/iconify.js';
import { clamp } from '#shared/general.utils';
import type { CanvasContent, CanvasNode } from '~/types/canvas';
import { labelCenter, getPath } from '#shared/canvas.util';

const canvas = defineModel<CanvasContent>({ required: true, });

const dispX = ref(0), dispY = ref(0), minZoom = ref(0.1), zoom = ref(0.5);
const focusing = ref<string>(), editing = ref<string>();
const canvasRef = useTemplateRef('canvasRef'), transformRef = useTemplateRef('transformRef');
const nodes = useTemplateRef<InstanceType<typeof CanvasNodeEditor>[]>('nodes');

const xTween = useTween(dispX, linear, updateTransform), yTween = useTween(dispY, linear, updateTransform), zoomTween = useTween(zoom, linear, updateTransform);

const focusedNode = computed(() => nodes.value?.find(e => !!e && e.id === focusing.value)), editedNode = computed(() => nodes.value?.find(e => !!e && e.id === editing.value));

const edges = computed(() => {
    return canvas.value.edges?.map(e => { 
        const from = canvas.value.nodes!.find(f => f.id === e.fromNode), to = canvas.value.nodes!.find(f => f.id === e.toNode);
        const path = getPath(from!, e.fromSide, to!, e.toSide)!;
        return { ...e, from, to, path };
    });
});

const history = ref<HistoryEvent[]>([]);
const historyPos = ref(-1);
const historyCursor = computed(() => history.value.length > 0 && historyPos.value > -1 ? history.value[historyPos.value] : undefined);

const reset = (_: MouseEvent) => {
    zoom.value = minZoom.value;
    zoomTween.refresh();

    dispX.value = 0;
    xTween.refresh();
    dispY.value = 0;
    yTween.refresh();

    updateTransform();
}

function addAction<T extends Action = Action>(event: T, actions: HistoryAction<T>[])
{
    historyPos.value++;
    history.value.splice(historyPos.value, history.value.length - historyPos.value);
    history.value[historyPos.value] = { event, actions };
}
onMounted(() => {
    let lastX = 0, lastY = 0, lastDistance = 0;
    let box = canvasRef.value?.getBoundingClientRect()!;
    const dragMove = (e: MouseEvent) => {
        dispX.value -= (lastX - e.clientX) / zoom.value;
        dispY.value -= (lastY - e.clientY) / zoom.value;
        lastX = e.clientX;
        lastY = e.clientY;

        xTween.refresh();
        yTween.refresh();

        updateTransform();
    };
    const dragEnd = (e: MouseEvent) => {
        window.removeEventListener('mouseup', dragEnd);
        window.removeEventListener('mousemove', dragMove);
    };
    canvasRef.value?.addEventListener('mouseenter', () => { 
        window.addEventListener('wheel', cancelEvent, { passive: false });
        document.addEventListener('gesturestart', cancelEvent);
        document.addEventListener('gesturechange', cancelEvent);

        canvasRef.value?.addEventListener('mouseleave', () => {
            window.removeEventListener('wheel', cancelEvent);
            document.removeEventListener('gesturestart', cancelEvent);
            document.removeEventListener('gesturechange', cancelEvent);
        });
    })
    window.addEventListener('resize', () => box = canvasRef.value?.getBoundingClientRect()!);
    canvasRef.value?.addEventListener('mousedown', (e) => {
        if(e.button === 1)
        {
            lastX = e.clientX;
            lastY = e.clientY;

            window.addEventListener('mouseup', dragEnd, { passive: true });
            window.addEventListener('mousemove', dragMove, { passive: true });
        }
    }, { passive: true });
    canvasRef.value?.addEventListener('wheel', (e) => {
        if((zoom.value >= 3 && e.deltaY < 0) || (zoom.value <= minZoom.value && e.deltaY > 0))
            return;

        const diff = Math.exp(e.deltaY * -0.001);
        const centerX = (box.x + box.width / 2), centerY = (box.y + box.height / 2);
        const mousex = centerX - e.clientX, mousey = centerY - e.clientY;

        xTween.update(dispX.value - (mousex / (diff * zoom.value) - mousex / zoom.value), 250);
        yTween.update(dispY.value - (mousey / (diff * zoom.value) - mousey / zoom.value), 250);

        zoomTween.update(clamp(zoom.value * diff, minZoom.value, 3), 250);

        updateTransform();
    }, { passive: true });
    canvasRef.value?.addEventListener('touchstart', (e) => {
        ({ x: lastX, y: lastY } = center(e.touches));

        if(e.touches.length > 1)
        {
            lastDistance = distance(e.touches);
        }

        canvasRef.value?.addEventListener('touchend', touchend, { passive: true });
        canvasRef.value?.addEventListener('touchcancel', touchcancel, { passive: true });
        canvasRef.value?.addEventListener('touchmove', touchmove, { passive: true });
    }, { passive: true });
    const touchend = (e: TouchEvent) => {
        if(e.touches.length > 1)
        {
            ({ x: lastX, y: lastY } = center(e.touches));
        }

        canvasRef.value?.removeEventListener('touchend', touchend);
        canvasRef.value?.removeEventListener('touchcancel', touchcancel);
        canvasRef.value?.removeEventListener('touchmove', touchmove);
    };
    const touchcancel = (e: TouchEvent) => {
        if(e.touches.length > 1)
        {
            ({ x: lastX, y: lastY } = center(e.touches));
        }

        canvasRef.value?.removeEventListener('touchend', touchend);
        canvasRef.value?.removeEventListener('touchcancel', touchcancel);
        canvasRef.value?.removeEventListener('touchmove', touchmove);
    };
    const touchmove = (e: TouchEvent) => {
        const pos = center(e.touches);
        dispX.value -= (lastX - pos.x) / zoom.value;
        dispY.value -= (lastY - pos.y) / zoom.value;
        lastX = pos.x;
        lastY = pos.y;

        if(e.touches.length === 2)
        {
            const dist = distance(e.touches);
            const diff = dist / lastDistance;
            
            zoom.value = clamp(zoom.value * diff, minZoom.value, 3); //@TODO
        }

        zoomTween.refresh();
        xTween.refresh();
        yTween.refresh();

        updateTransform();
    };

    updateTransform();
});

function updateTransform()
{
    if(transformRef.value)
    {
        transformRef.value.style.transform = `scale3d(${zoomTween.current()}, ${zoomTween.current()}, 1) translate3d(${xTween.current()}px, ${yTween.current()}px, 0)`;
        transformRef.value.style.setProperty('--tw-scale', zoomTween.current().toString());
    }
}
function moveNode(ids: string[], deltax: number, deltay: number)
{
    const actions: HistoryAction<'move'>[] = [];
    for(const id of ids)
    {
        const node = canvas.value.nodes!.find(e => e.id === id)!;

        actions.push({ element: id, from: { x: node.x - deltax, y: node.y - deltay }, to: { x: node.x, y: node.y } });
    }

    addAction('move', actions);
}
function resizeNode(ids: string[], deltax: number, deltay: number, deltaw: number, deltah: number)
{
    const actions: HistoryAction<'resize'>[] = [];
    for(const id of ids)
    {
        const node = canvas.value.nodes!.find(e => e.id === id)!;

        actions.push({ element: id, from: { x: node.x - deltax, y: node.y - deltay, w: node.width - deltaw, h: node.height - deltah }, to: { x: node.x, y: node.y, w: node.width, h: node.height } });
    }

    addAction('resize', actions);
}
function selectNode(id: string)
{
    if(focusing.value !== id)
    {
        unselectNode();
    }

    focusing.value = id;

    focusedNode.value?.dom?.addEventListener('click', stopPropagation, { passive: true });
    canvasRef.value?.addEventListener('click', unselectNode, { once: true });
}
function editNode(id: string)
{
    editing.value = id;

    focusedNode.value?.dom?.addEventListener('wheel', stopPropagation, { passive: true });
    focusedNode.value?.dom?.addEventListener('dblclick', stopPropagation, { passive: true });
    canvasRef.value?.addEventListener('click', unselectNode, { once: true });
}
function createNode(e: MouseEvent)
{
    let box = canvasRef.value?.getBoundingClientRect()!;
    const node: CanvasNode = { id: getID(16), x: (e.layerX / zoom.value) - box.width / 2 - 50, y: (e.layerY / zoom.value) - box.height / 2 - 25, width: 100, height: 50, type: 'text' };

    if(!canvas.value.nodes)
        canvas.value.nodes = [node];
    else
        canvas.value.nodes.push(node);

    addAction('create', [{ element: node.id, from: undefined, to: node }]);
}
function removeNode(ids: string[])
{
    const actions: HistoryAction<'remove'>[] = [];
    unselectNode();

    for(const id of ids)
    {
        const index = canvas.value.nodes!.findIndex(e => e.id === id);
        actions.push({ element: id, from: canvas.value.nodes!.splice(index, 1)[0], to: undefined });
    }

    addAction('remove', actions);
}
function editNodeProperty<T extends keyof CanvasNode>(ids: string[], property: T, value: CanvasNode[T])
{
    const actions: HistoryAction<'remove'>[] = [];

    for(const id of ids)
    {
        const copy = JSON.parse(JSON.stringify(canvas.value.nodes!.find(e => e.id === id)!)) as CanvasNode;
        canvas.value.nodes!.find(e => e.id === id)![property] = value;
        actions.push({ element: id, from: copy, to: canvas.value.nodes!.find(e => e.id === id)! });
    }

    addAction('property', actions);
}

const unselectNode = () => {
    if(focusing.value !== undefined)
    {
        focusedNode.value?.dom?.removeEventListener('click', stopPropagation);
        focusedNode.value?.unselect();
    }
    focusing.value = undefined;

    if(editing.value !== undefined)
    {
        editedNode.value?.dom?.removeEventListener('wheel', stopPropagation);
        editedNode.value?.dom?.removeEventListener('dblclick', stopPropagation);
        editedNode.value?.dom?.removeEventListener('click', stopPropagation);
        editedNode.value?.unselect();
    }
    editing.value = undefined;
};
const undo = () => {
    if(!historyCursor.value)
        return;

    for(const action of historyCursor.value.actions)
    {
        const node = canvas.value.nodes!.find(e => e.id === action.element)!;
        switch(historyCursor.value.event)
        {
            case 'move':
            {
                const a = action as HistoryAction<'move'>;
                node.x = a.from.x;
                node.y = a.from.y;
                break;
            }
            case 'resize':
            {
                const a = action as HistoryAction<'resize'>;
                node.x = a.from.x;
                node.y = a.from.y;
                node.width = a.from.w;
                node.height = a.from.h;
                break;
            }
            case 'edit':
            {
                const a = action as HistoryAction<'edit'>;
                node.label = a.from;
                break;
            }
            case 'create':
            {
                const a = action as HistoryAction<'create'>;
                const index = canvas.value.nodes!.findIndex(e => e.id === action.element);
                canvas.value.nodes!.splice(index, 1);
                break;
            }
            case 'remove':
            {
                const a = action as HistoryAction<'remove'>;
                canvas.value.nodes!.push(a.from!);
                break;
            }
            case 'property':
            {
                const a = action as HistoryAction<'property'>;
                const index = canvas.value.nodes!.findIndex(e => e.id === action.element);
                canvas.value.nodes![index] = a.from;
                break;
            }
        }
    }

    historyPos.value--;

    console.log(historyPos.value, history.value.length);
};
const redo = () => {
    if(!history.value || history.value.length - 1 <= historyPos.value)
        return;

    historyPos.value++;
    
    if(!historyCursor.value)
    {
        historyPos.value--;
        return;
    }

    for(const action of historyCursor.value.actions)
    {
        const node = canvas.value.nodes!.find(e => e.id === action.element)!;
        switch(historyCursor.value.event)
        {
            case 'move':
            {
                const a = action as HistoryAction<'move'>;
                node.x = a.to.x;
                node.y = a.to.y;
                break;
            }
            case 'resize':
            {
                const a = action as HistoryAction<'resize'>;
                node.x = a.to.x;
                node.y = a.to.y;
                node.width = a.to.w;
                node.height = a.to.h;
                break;
            }
            case 'edit':
            {
                const a = action as HistoryAction<'edit'>;
                node.label = a.to;
                break;
            }
            case 'create':
            {
                const a = action as HistoryAction<'remove'>;
                canvas.value.nodes!.push(a.to!);
                break;
            }
            case 'remove':
            {
                const a = action as HistoryAction<'remove'>;
                const index = canvas.value.nodes!.findIndex(e => e.id === action.element);
                canvas.value.nodes!.splice(index, 1);
                break;
            }
            case 'property':
            {
                const a = action as HistoryAction<'property'>;
                const index = canvas.value.nodes!.findIndex(e => e.id === action.element);
                canvas.value.nodes![index] = a.to;
                break;
            }
        }
    }

    console.log(historyPos.value, history.value.length);
};

useShortcuts({
    meta_z: undo,
    meta_y: redo,
    Delete: () => { if(focusing.value !== undefined) { removeNode([focusing.value]) } }
});
</script>

<template>
    <div ref="canvasRef" class="absolute top-0 left-0 overflow-hidden w-full h-full touch-none" :style="{ '--zoom-multiplier': (1 / Math.pow(zoom, 0.7)) }" @dblclick.left.self="createNode">
        <div class="flex flex-col absolute sm:top-2 top-10 left-2 z-[35] overflow-hidden gap-4" @click="stopPropagation">
            <div class="border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10">
                <Tooltip message="Zoom avant" side="right">
                    <div @click="zoomTween.update(clamp(zoom * 1.1, minZoom, 3), 250); updateTransform()" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer">
                        <Icon icon="radix-icons:plus" />
                    </div>
                </Tooltip>
                <Tooltip message="Reset" side="right">
                    <div @click="zoom = 1; zoomTween.refresh(); updateTransform();" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer">
                        <Icon icon="radix-icons:reload" />
                    </div>
                </Tooltip>
                <Tooltip message="Tout contenir" side="right">
                    <div @click="reset" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer">
                        <Icon icon="radix-icons:corners" />
                    </div>
                </Tooltip>
                <Tooltip message="Zoom arrière" side="right">
                    <div @click="zoomTween.update(clamp(zoom / 1.1, minZoom, 3), 250); updateTransform()" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer">
                        <Icon icon="radix-icons:minus" />
                    </div>
                </Tooltip>
            </div>
            <div class="border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10">
                <Tooltip message="Annuler (Ctrl+Z)" side="right">
                    <div @click="undo" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer" :class="{ 'text-light-50 dark:text-dark-50 !cursor-default hover:bg-transparent dark:hover:bg-transparent': historyPos === -1 }">
                        <Icon icon="ph:arrow-bend-up-left" />
                    </div>
                </Tooltip>
                <Tooltip message="Retablir (Ctrl+Y)" side="right">
                    <div @click="redo" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer" :class="{ 'text-light-50 dark:text-dark-50 !cursor-default hover:bg-transparent dark:hover:bg-transparent': historyPos === history.length - 1 }">
                        <Icon icon="ph:arrow-bend-up-right" />
                    </div>
                </Tooltip>
            </div>
            <div class="border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10">
                <Tooltip message="Aide" side="right">
                    <Dialog title="Aide" iconClose>
                        <template #trigger>
                            <div class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer">
                                <Icon icon="radix-icons:question-mark-circled" />
                            </div>
                        </template>
                        <template #default>
                            <div class="flex flex-row justify-between px-4">
                                <div class="flex flex-col gap-2">
                                    <ProseH4>Ordinateur</ProseH4>
                                    <div class="flex items-center"><Icon icon="ph:mouse-left-click-fill" class="w-6 h-6"/>: Selectionner</div>
                                    <div class="flex items-center"><Icon icon="ph:mouse-left-click-fill" class="w-6 h-6"/><Icon icon="ph:mouse-left-click-fill" class="w-6 h-6"/>: Modifier</div>
                                    <div class="flex items-center"><Icon icon="ph:mouse-middle-click-fill" class="w-6 h-6"/>: Déplacer</div>
                                    <div class="flex items-center"><Icon icon="ph:mouse-right-click-fill" class="w-6 h-6"/>: Menu</div>
                                </div>
                                <div class="flex flex-col gap-2">
                                    <ProseH4>Mobile</ProseH4>
                                    <div class="flex items-center"><Icon icon="ph:hand-tap" class="w-6 h-6"/>: Selectionner</div>
                                    <div class="flex items-center"><Icon icon="ph:hand-tap" class="w-6 h-6"/><Icon icon="ph:hand-tap" class="w-6 h-6"/>: Modifier</div>
                                    <div class="flex items-center"><Icon icon="mdi:gesture-pinch" class="w-6 h-6"/>: Zoomer</div>
                                    <div class="flex items-center"><Icon icon="ph:hand-tap" class="w-6 h-6"/> maintenu: Menu</div>
                                </div>
                            </div>
                        </template>
                    </Dialog>
                </Tooltip>
            </div>
        </div>
        <div ref="transformRef" :style="{
            'transform-origin': 'center center',
        }" class="h-full">
            <div class="absolute top-0 left-0 w-full h-full pointer-events-none *:pointer-events-auto *:select-none touch-none">
                <div v-if="focusing !== undefined && focusedNode !== undefined" class="absolute z-20 origin-bottom" :style="{transform: `translate(${focusedNode.x}px, ${focusedNode.y}px) translateY(-100%) translateY(-12px) translateX(-50%) translateX(${focusedNode.width / 2}px) scale(calc(1 / var(--tw-scale)))`}">
                    <div class="border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10 flex flex-row">
                        <PopoverRoot>
                            <PopoverTrigger asChild>
                                <div @click="stopPropagation">
                                    <Tooltip message="Couleur" side="top">
                                        <div class="w-10 h-10 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer">
                                            <Icon icon="ph:palette" class="w-6 h-6" />
                                        </div>
                                    </Tooltip>
                                </div>
                            </PopoverTrigger>
                            <PopoverPortal disabled>
                                <PopoverContent align="center" side="top" class="bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 m-2">
                                    <div class="border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10 flex flex-row *:cursor-pointer">
                                        <div @click="editNodeProperty([focusing], 'color', undefined)" class="p-2 hover:bg-light-35 hover:dark:bg-dark-35">
                                            <span class="bg-light-40 dark:bg-dark-40 w-4 h-4 block"></span>
                                        </div>
                                        <div @click="editNodeProperty([focusing], 'color', { class: 'red' })" class="p-2 hover:bg-light-35 hover:dark:bg-dark-35">
                                            <span class="bg-light-red dark:bg-dark-red w-4 h-4 block"></span>
                                        </div>
                                        <div @click="editNodeProperty([focusing], 'color', { class: 'orange' })" class="p-2 hover:bg-light-35 hover:dark:bg-dark-35">
                                            <span class="bg-light-orange dark:bg-dark-orange w-4 h-4 block"></span>
                                        </div>
                                        <div @click="editNodeProperty([focusing], 'color', { class: 'yellow' })" class="p-2 hover:bg-light-35 hover:dark:bg-dark-35">
                                            <span class="bg-light-yellow dark:bg-dark-yellow w-4 h-4 block"></span>
                                        </div>
                                        <div @click="editNodeProperty([focusing], 'color', { class: 'green' })" class="p-2 hover:bg-light-35 hover:dark:bg-dark-35">
                                            <span class="bg-light-green dark:bg-dark-green w-4 h-4 block"></span>
                                        </div>
                                        <div @click="editNodeProperty([focusing], 'color', { class: 'cyan' })" class="p-2 hover:bg-light-35 hover:dark:bg-dark-35">
                                            <span class="bg-light-cyan dark:bg-dark-cyan w-4 h-4 block"></span>
                                        </div>
                                        <div @click="editNodeProperty([focusing], 'color', { class: 'purple' })" class="p-2 hover:bg-light-35 hover:dark:bg-dark-35">
                                            <span class="bg-light-purple dark:bg-dark-purple w-4 h-4 block"></span>
                                        </div>
                                        <label>
                                            <div @click="stopPropagation" class="p-2 hover:bg-light-35 hover:dark:bg-dark-35">
                                                <span style="background: conic-gradient(red, yellow, green, blue, purple, red)" class="w-4 h-4 block relative"></span><input @change="(e: Event) => editNodeProperty([focusing!], 'color', { hex: (e.target as HTMLInputElement).value })" type="color" class="appearance-none w-0 h-0 absolute" />
                                            </div>
                                        </label>
                                    </div>
                                </PopoverContent>
                            </PopoverPortal>
                        </PopoverRoot>
                        <Tooltip message="Supprimer" side="top">
                            <div @click="removeNode([focusing])" class="w-10 h-10 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer">
                                <Icon icon="radix-icons:trash" class="text-light-red dark:text-dark-red w-6 h-6" />
                            </div>
                        </Tooltip>
                    </div>
                </div>
                <div>
                    <CanvasNodeEditor v-for="(node, index) of canvas.nodes" :key="node.id" ref="nodes" :node="node" :index="index" :zoom="zoom" @select="selectNode" @edit="editNode" @move="(i, x, y) => moveNode([i], x, y)" @resize="(i, x, y, w, h) => resizeNode([i], x, y, w, h)" />
                </div>
                <template v-for="edge of edges">
                    <div :key="edge.id" v-if="edge.label" class="absolute z-10"
                        :style="{ transform: labelCenter(edge.from!, edge.fromSide, edge.to!, edge.toSide) }">
                        <div class="relative bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 px-4 py-2 -translate-x-[50%] -translate-y-[50%]">{{ edge.label }}</div>
                    </div>
                </template>
                <svg class="absolute top-0 left-0 w-1 h-1 overflow-visible">
                    <g v-for="edge of edges" :key="edge.id" :style="{'--canvas-color': edge.color?.hex}" class="z-0">
                        <path :style="`stroke-linecap: butt; stroke-width: calc(3px * var(--zoom-multiplier));`" :class="edge.color?.class ? `stroke-light-${edge.color.class} dark:stroke-dark-${edge.color.class}` : ((edge.color && edge.color?.hex !== undefined) ? 'stroke-[color:var(--canvas-color)]' : 'stroke-light-40 dark:stroke-dark-40')" class="fill-none stroke-[4px]" :d="edge.path.path"></path>
                        <g :style="`transform: translate(${edge.path.to.x}px, ${edge.path.to.y}px) scale(var(--zoom-multiplier)) rotate(${rotation[edge.path.side]}deg);`">
                            <polygon :class="edge.color?.class ? `fill-light-${edge.color.class} dark:fill-dark-${edge.color.class}` : ((edge.color && edge.color?.hex !== undefined) ? 'fill-[color:var(--canvas-color)]' : 'fill-light-40 dark:fill-dark-40')" points="0,0 6.5,10.4 -6.5,10.4"></polygon>
                        </g>
                    </g>
                </svg>
            </div>
        </div>
    </div>
</template>