mirror of
https://github.com/litruv/lit.ruv.wtf.git
synced 2026-07-24 02:36:02 +10:00
346 lines
18 KiB
HTML
346 lines
18 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<title>Lit.ruv.wtf - Interactive Node Graph</title>
|
||
<meta name="description" content="Personal site of Lit.ruv.wtf. Graph editor edition">
|
||
|
||
<!-- Favicons -->
|
||
<link rel="icon" type="image/png" sizes="16x16" href="logos/16px.png">
|
||
<link rel="icon" type="image/png" sizes="32x32" href="logos/32px.png">
|
||
<link rel="icon" type="image/png" sizes="48x48" href="logos/48px.png">
|
||
<link rel="icon" type="image/png" sizes="64x64" href="logos/64px.png">
|
||
<link rel="icon" type="image/png" sizes="128x128" href="logos/128px.png">
|
||
<link rel="icon" type="image/png" sizes="256x256" href="logos/256px.png">
|
||
<link rel="apple-touch-icon" sizes="180x180" href="logos/256px.png">
|
||
|
||
<!-- Open Graph / Social Media -->
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://lit.ruv.wtf/">
|
||
<meta property="og:title" content="Lit.ruv.wtf - Interactive Node Graph">
|
||
<meta property="og:description" content="Personal site of Lit.ruv.wtf. Graph editor edition">
|
||
<meta property="og:image" content="https://lit.ruv.wtf/banner.webp">
|
||
|
||
<!-- Twitter Card -->
|
||
<meta name="twitter:card" content="summary_large_image">
|
||
<meta name="twitter:url" content="https://lit.ruv.wtf/">
|
||
<meta name="twitter:title" content="Lit.ruv.wtf - Interactive Node Graph">
|
||
<meta name="twitter:description" content="Personal site of Lit.ruv.wtf. Graph editor edition">
|
||
<meta name="twitter:image" content="https://lit.ruv.wtf/banner.webp">
|
||
|
||
<!-- Preconnect to external domains -->
|
||
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
|
||
<link rel="preconnect" href="https://unpkg.com" crossorigin>
|
||
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com">
|
||
<link rel="dns-prefetch" href="https://unpkg.com">
|
||
|
||
<link rel="stylesheet" href="styles/main.css">
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css">
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-typescript.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-jsx.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-css.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-json.min.js"></script>
|
||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-bash.min.js"></script>
|
||
<script defer src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>
|
||
</head>
|
||
<body>
|
||
<div id="workspaceCanvas" class="workspace-canvas">
|
||
<nav class="quick-links">
|
||
<a href="/docs/" class="quick-link">docs</a>
|
||
<a href="https://github.com/litruv" target="_blank" rel="noopener" class="quick-link">github</a>
|
||
<a href="https://bsky.app/profile/lit.mates.dev" target="_blank" rel="noopener" class="quick-link">bluesky</a>
|
||
<a href="/materials" class="quick-link">materials</a>
|
||
</nav>
|
||
|
||
<svg id="connectionLayer" class="connection-layer"></svg>
|
||
<div id="worldLayer" class="world-layer">
|
||
<div id="nodeLayer" class="node-layer"></div>
|
||
</div>
|
||
|
||
<div class="hud">
|
||
<button id="resetViewBtn" class="hud-btn" title="Reset View">
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
|
||
<polyline points="9 22 9 12 15 12 15 22"></polyline>
|
||
</svg>
|
||
</button>
|
||
<button id="draggableToggle" class="hud-btn-drag" title="Toggle Dragging">
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M12 19l7-7 3 3-7 7-3-3z"></path>
|
||
<path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"></path>
|
||
<path d="M2 2l7.586 7.586"></path>
|
||
<circle cx="11" cy="11" r="2"></circle>
|
||
</svg>
|
||
<div class="hud-tooltip">
|
||
alt+click - disconnect pin
|
||
</div>
|
||
</button>
|
||
</div>
|
||
|
||
<div id="debugPanel" class="debug-panel collapsed">
|
||
<div class="debug-header">
|
||
<div class="debug-title">Debug</div>
|
||
<button class="debug-toggle-btn" title="Toggle debug panel" aria-label="Toggle debug panel">
|
||
<svg class="debug-eye-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path class="eye-visible" d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
||
<circle class="eye-visible" cx="12" cy="12" r="3"></circle>
|
||
<path class="eye-hidden" d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" style="display: none;"></path>
|
||
<line class="eye-hidden" x1="1" y1="1" x2="23" y2="23" style="display: none;"></line>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="debug-row">
|
||
<span class="debug-label">Zoom</span>
|
||
<span class="debug-value debug-zoom">1.000</span>
|
||
</div>
|
||
<div class="debug-row">
|
||
<span class="debug-label">DPR</span>
|
||
<span class="debug-value debug-dpr">1.0</span>
|
||
</div>
|
||
<div class="debug-row">
|
||
<span class="debug-label">Viewport</span>
|
||
<span class="debug-value debug-viewport">0 × 0</span>
|
||
</div>
|
||
<div class="debug-row">
|
||
<span class="debug-label">Center</span>
|
||
<span class="debug-value debug-center">0.0, 0.0</span>
|
||
</div>
|
||
<div class="debug-row">
|
||
<span class="debug-label">Viewbox</span>
|
||
<span class="debug-value debug-viewbox">—</span>
|
||
</div>
|
||
<div class="debug-row">
|
||
<span class="debug-label">NodeAnchor</span>
|
||
<span class="debug-value debug-anchor">—</span>
|
||
</div>
|
||
<div class="debug-divider"></div>
|
||
<label class="debug-row debug-check-row">
|
||
<span class="debug-label">Draggable</span>
|
||
<input class="debug-draggable-cb" type="checkbox" />
|
||
</label>
|
||
<label class="debug-row debug-check-row">
|
||
<span class="debug-label">WorldBoxes</span>
|
||
<input class="debug-worldbox-cb" type="checkbox" />
|
||
</label>
|
||
<label class="debug-row debug-check-row">
|
||
<span class="debug-label">Viewbox</span>
|
||
<input class="debug-viewbox-cb" type="checkbox" />
|
||
</label>
|
||
<div class="debug-divider"></div>
|
||
<button class="debug-copy-json-btn" title="Copy current graph as JSON">Copy JSON</button>
|
||
<button class="debug-show-properties-btn" title="Open properties panel">Show Properties</button>
|
||
<div class="debug-row debug-active-row" hidden>
|
||
<span class="debug-label debug-active-label">—</span>
|
||
<span class="debug-value debug-active-pos">0.0, 0.0</span>
|
||
<button class="debug-copy-btn" title="Copy graph.json">Copy graph</button>
|
||
</div>
|
||
|
||
<div class="debug-divider" id="debugNodeEditorDivider" hidden></div>
|
||
<div id="debugNodeEditor" class="debug-node-editor" hidden>
|
||
<div class="debug-subtitle">Node Editor</div>
|
||
<div class="debug-row">
|
||
<span class="debug-label">ID</span>
|
||
<input class="debug-input debug-node-id" type="text" readonly />
|
||
</div>
|
||
<div class="debug-row">
|
||
<span class="debug-label">Type</span>
|
||
<input class="debug-input debug-node-type" type="text" readonly />
|
||
</div>
|
||
<div class="debug-row">
|
||
<span class="debug-label">Title</span>
|
||
<input class="debug-input debug-node-title" type="text" />
|
||
</div>
|
||
<div class="debug-row">
|
||
<span class="debug-label">X</span>
|
||
<input class="debug-input debug-node-x" type="number" step="1" />
|
||
</div>
|
||
<div class="debug-row">
|
||
<span class="debug-label">Y</span>
|
||
<input class="debug-input debug-node-y" type="number" step="1" />
|
||
</div>
|
||
<div class="debug-row">
|
||
<span class="debug-label">Width</span>
|
||
<input class="debug-input debug-node-width" type="number" step="1" />
|
||
</div>
|
||
|
||
<div class="debug-subtitle">Inputs</div>
|
||
<div id="debugInputsList" class="debug-pins-list"></div>
|
||
<button class="debug-add-btn debug-add-input-btn">+ Add Input</button>
|
||
|
||
<div class="debug-subtitle">Outputs</div>
|
||
<div id="debugOutputsList" class="debug-pins-list"></div>
|
||
<button class="debug-add-btn debug-add-output-btn">+ Add Output</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Properties Editor Panel (Right Side) -->
|
||
<div id="propertiesPanel" class="properties-panel collapsed">
|
||
<button class="properties-panel-tab" title="Open properties panel" aria-label="Open properties panel">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="3" width="7" height="7"></rect>
|
||
<rect x="14" y="3" width="7" height="7"></rect>
|
||
<rect x="14" y="14" width="7" height="7"></rect>
|
||
<rect x="3" y="14" width="7" height="7"></rect>
|
||
</svg>
|
||
</button>
|
||
<div class="properties-header">
|
||
<h2 class="properties-title">Properties</h2>
|
||
<button class="properties-toggle-btn" title="Close properties panel" aria-label="Close properties panel">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="15 18 9 12 15 6"></polyline>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="properties-content">
|
||
<!-- Node Properties Section -->
|
||
<div id="nodePropertiesSection" class="properties-section" style="display: none;">
|
||
<div class="section-header">
|
||
<h3>Node</h3>
|
||
</div>
|
||
<div class="section-content">
|
||
<div class="prop-row">
|
||
<label class="prop-label">ID</label>
|
||
<input type="text" class="prop-input" id="propNodeId" readonly />
|
||
</div>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Type</label>
|
||
<input type="text" class="prop-input" id="propNodeType" readonly />
|
||
</div>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Title</label>
|
||
<input type="text" class="prop-input" id="propNodeTitle" />
|
||
</div>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Position X</label>
|
||
<input type="number" class="prop-input" id="propNodeX" step="20" />
|
||
</div>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Position Y</label>
|
||
<input type="number" class="prop-input" id="propNodeY" step="20" />
|
||
</div>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Width</label>
|
||
<input type="number" class="prop-input" id="propNodeWidth" placeholder="Auto" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Focus Options Section -->
|
||
<div id="focusOptionsSection" class="properties-section" style="display: none;">
|
||
<div class="section-header collapsible">
|
||
<h3>Focus Options</h3>
|
||
<button class="section-toggle">▼</button>
|
||
</div>
|
||
<div class="section-content">
|
||
<div class="prop-row">
|
||
<label class="prop-label">Duration (ms)</label>
|
||
<input type="number" class="prop-input" id="propFocusDuration" placeholder="550" />
|
||
</div>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Anchor X</label>
|
||
<input type="number" class="prop-input" id="propFocusAnchorX" step="0.1" min="0" max="1" placeholder="0.5" />
|
||
</div>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Anchor Y</label>
|
||
<input type="number" class="prop-input" id="propFocusAnchorY" step="0.1" min="0" max="1" placeholder="0.5" />
|
||
</div>
|
||
|
||
<div class="prop-divider"></div>
|
||
<h4 class="prop-subtitle">Default WorldBox</h4>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Width</label>
|
||
<input type="number" class="prop-input" id="propWorldBoxWidth" placeholder="Auto" />
|
||
</div>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Height</label>
|
||
<input type="number" class="prop-input" id="propWorldBoxHeight" placeholder="Auto" />
|
||
</div>
|
||
|
||
<div class="prop-divider"></div>
|
||
<h4 class="prop-subtitle">Responsive Breakpoints</h4>
|
||
<div id="breakpointsList" class="breakpoints-list"></div>
|
||
<button class="prop-btn-add" id="addBreakpointBtn">+ Add Breakpoint</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Graph Settings Section -->
|
||
<div id="graphSettingsSection" class="properties-section">
|
||
<div class="section-header collapsible">
|
||
<h3>Graph Settings</h3>
|
||
<button class="section-toggle">▼</button>
|
||
</div>
|
||
<div class="section-content">
|
||
<div class="prop-row">
|
||
<label class="prop-label">Draggable</label>
|
||
<input type="checkbox" class="prop-checkbox" id="propGraphDraggable" />
|
||
</div>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Intro Duration (ms)</label>
|
||
<input type="number" class="prop-input" id="propIntroDuration" placeholder="700" />
|
||
</div>
|
||
|
||
<div class="prop-divider"></div>
|
||
<h4 class="prop-subtitle">Initial Viewport</h4>
|
||
<div class="prop-row">
|
||
<label class="prop-label">X</label>
|
||
<input type="number" class="prop-input" id="propInitViewX" />
|
||
</div>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Y</label>
|
||
<input type="number" class="prop-input" id="propInitViewY" />
|
||
</div>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Width</label>
|
||
<input type="number" class="prop-input" id="propInitViewWidth" />
|
||
</div>
|
||
<div class="prop-row">
|
||
<label class="prop-label">Height</label>
|
||
<input type="number" class="prop-input" id="propInitViewHeight" />
|
||
</div>
|
||
|
||
<div class="prop-divider"></div>
|
||
<button class="prop-btn-primary" id="applySettingsBtn">Apply Settings</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- No Selection Message -->
|
||
<div id="noSelectionMessage" class="no-selection">
|
||
<p>Select a node to edit its properties</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<template id="nodeTemplate">
|
||
<article class="blueprint-node" draggable="false">
|
||
<header class="node-header">
|
||
<div class="node-title"></div>
|
||
</header>
|
||
<section class="node-io node-inputs"></section>
|
||
<section class="node-io node-outputs"></section>
|
||
</article>
|
||
</template>
|
||
|
||
<template id="pinTemplate">
|
||
<div class="pin" draggable="false">
|
||
<span class="pin-label"></span>
|
||
<span class="pin-handle" tabindex="-1"></span>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- WorldBox Overlay Layer -->
|
||
<svg id="worldBoxLayer" class="worldbox-layer" style="display: none;"></svg>
|
||
|
||
<!-- Viewbox Overlay Layer -->
|
||
<svg id="viewboxLayer" class="viewbox-layer" style="display: none;"></svg>
|
||
|
||
<!-- JSON Editor Modal -->
|
||
<script type="module" src="scripts/main.js"></script>
|
||
</body>
|
||
</html>
|