commit 7758bce2a0d184b500bd53756db66724f06b010c
Author: root <root@hub.scroll.pub> Date: 2025-01-15 23:52:13 +0000 Subject: Initial commit diff --git a/index.scroll b/index.scroll new file mode 100644 index 0000000..b5a98b9 --- /dev/null +++ b/index.scroll @@ -0,0 +1,78 @@ +buildHtml +baseUrl https://viz.scroll.pub +metaTags +editButton /edit.html +title Graph Visualizer +style.css +header + class hero + nav + div GraphViz + class logo + div + class nav-links + a Create + href #canvas + a Examples + href #examples + a Help + href #help +main + section + id canvas + class graph-workspace + div + class controls + button New Node + id addNode + button New Edge + id addEdge + button Clear + id clear + canvas + id graphCanvas + section + id examples + class example-section + h2 Example Graphs + div + class example-grid + div + class example-card + h3 Binary Tree + canvas + class mini-canvas + data-type binary + div + class example-card + h3 Network + canvas + class mini-canvas + data-type network + div + class example-card + h3 Circle + canvas + class mini-canvas + data-type circle + section + id help + class help-section + h2 How to Use + div + class instructions + p Click "New Node" to add vertices + p Click "New Edge" then select two nodes to connect + p Drag nodes to reposition + p Double-click to delete +footer + div + class footer-content + p Built with Scroll + div + class footer-links + a Tutorial + href tutorial.html + a API + href api.html +script.js \ No newline at end of file diff --git a/prompt.json b/prompt.json new file mode 100644 index 0000000..fa65f17 --- /dev/null +++ b/prompt.json @@ -0,0 +1,20 @@ +{ + "id": "msg_01LpW7qwkBZE1F8vs2zWjQHq", + "type": "message", + "role": "assistant", + "model": "claude-3-5-sonnet-20241022", + "content": [ + { + "type": "text", + "text": "For a graph visualization website, I suggest the domain \"viz.scroll.pub\" - it's short, memorable, and clearly indicates the purpose.\n\n---domain---\nviz.scroll.pub\n---index.scroll---\nbuildHtml\nbaseUrl https://viz.scroll.pub\nmetaTags\neditButton /edit.html\ntitle Graph Visualizer\nstyle.css\nheader\n class hero\n nav\n div GraphViz\n class logo\n div\n class nav-links\n a Create\n href #canvas\n a Examples\n href #examples\n a Help\n href #help\nmain\n section\n id canvas\n class graph-workspace\n div\n class controls\n button New Node\n id addNode\n button New Edge\n id addEdge\n button Clear\n id clear\n canvas\n id graphCanvas\n section\n id examples\n class example-section\n h2 Example Graphs\n div\n class example-grid\n div\n class example-card\n h3 Binary Tree\n canvas\n class mini-canvas\n data-type binary\n div\n class example-card\n h3 Network\n canvas\n class mini-canvas\n data-type network\n div\n class example-card\n h3 Circle\n canvas\n class mini-canvas\n data-type circle\n section\n id help\n class help-section\n h2 How to Use\n div\n class instructions\n p Click \"New Node\" to add vertices\n p Click \"New Edge\" then select two nodes to connect\n p Drag nodes to reposition\n p Double-click to delete\nfooter\n div\n class footer-content\n p Built with Scroll\n div\n class footer-links\n a Tutorial\n href tutorial.html\n a API\n href api.html\nscript.js\n---style.css---\n:root {\n --primary-color: #2a6b9c;\n --secondary-color: #3498db;\n --accent-color: #e74c3c;\n --background-color: #f5f6fa;\n --text-color: #2c3e50;\n --shadow: 0 4px 6px rgba(0,0,0,0.1);\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: 'Segoe UI', system-ui, sans-serif;\n line-height: 1.6;\n color: var(--text-color);\n background: var(--background-color);\n}\n\n.hero {\n background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));\n padding: 1rem;\n color: white;\n}\n\nnav {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1rem;\n max-width: 1200px;\n margin: 0 auto;\n}\n\n.logo {\n font-size: 1.5rem;\n font-weight: bold;\n text-shadow: 2px 2px 4px rgba(0,0,0,0.2);\n}\n\n.nav-links {\n display: flex;\n gap: 2rem;\n}\n\n.nav-links a {\n color: white;\n text-decoration: none;\n transition: all 0.3s ease;\n}\n\n.nav-links a:hover {\n transform: translateY(-2px);\n text-shadow: 0 0 8px rgba(255,255,255,0.5);\n}\n\n.graph-workspace {\n padding: 2rem;\n background: white;\n border-radius: 8px;\n box-shadow: var(--shadow);\n margin: 2rem auto;\n max-width: 1200px;\n}\n\n#graphCanvas {\n width: 100%;\n height: 600px;\n border: 2px solid var(--secondary-color);\n border-radius: 4px;\n margin-top: 1rem;\n}\n\n.controls {\n display: flex;\n gap: 1rem;\n margin-bottom: 1rem;\n}\n\nbutton {\n padding: 0.5rem 1rem;\n border: none;\n border-radius: 4px;\n background: var(--secondary-color);\n color: white;\n cursor: pointer;\n transition: all 0.3s ease;\n}\n\nbutton:hover {\n background: var(--primary-color);\n transform: translateY(-2px);\n}\n\n.example-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n gap: 2rem;\n padding: 2rem;\n}\n\n.example-card {\n background: white;\n padding: 1rem;\n border-radius: 8px;\n box-shadow: var(--shadow);\n transition: all 0.3s ease;\n}\n\n.example-card:hover {\n transform: translateY(-4px);\n box-shadow: 0 6px 12px rgba(0,0,0,0.15);\n}\n\n.mini-canvas {\n width: 100%;\n height: 200px;\n border: 1px solid var(--secondary-color);\n border-radius: 4px;\n}\n\n.help-section {\n background: white;\n padding: 2rem;\n margin: 2rem auto;\n max-width: 1200px;\n border-radius: 8px;\n box-shadow: var(--shadow);\n}\n\nfooter {\n background: var(--primary-color);\n color: white;\n padding: 2rem;\n margin-top: 4rem;\n}\n\n.footer-content {\n max-width: 1200px;\n margin: 0 auto;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.footer-links a {\n color: white;\n text-decoration: none;\n margin-left: 2rem;\n}\n\n@media (max-width: 768px) {\n .nav-links {\n gap: 1rem;\n }\n \n .controls {\n flex-direction: column;\n }\n \n .example-grid {\n grid-template-columns: 1fr;\n }\n \n .footer-content {\n flex-direction: column;\n gap: 1rem;\n text-align: center;\n }\n}\n---script.js---\nclass GraphVisualizer {\n constructor() {\n this.canvas = document.getElementById('graphCanvas');\n this.ctx = this.canvas.getContext('2d');\n this.nodes = [];\n this.edges = [];\n this.isDragging = false;\n this.selectedNode = null;\n this.mode = 'node';\n \n this.initializeCanvas();\n this.addEventListeners();\n this.animate();\n }\n\n initializeCanvas() {\n this.resizeCanvas();\n window.addEventListener('resize', () => this.resizeCanvas());\n }\n\n resizeCanvas() {\n this.canvas.width = this.canvas.offsetWidth;\n this.canvas.height = this.canvas.offsetHeight;\n }\n\n addEventListeners() {\n document.getElementById('addNode').addEventListener('click', () => this.mode = 'node');\n document.getElementById('addEdge').addEventListener('click', () => this.mode = 'edge');\n document.getElementById('clear').addEventListener('click', () => this.clear());\n\n this.canvas.addEventListener('mousedown', (e) => this.handleMouseDown(e));\n this.canvas.addEventListener('mousemove', (e) => this.handleMouseMove(e));\n this.canvas.addEventListener('mouseup', () => this.handleMouseUp());\n this.canvas.addEventListener('dblclick', (e) => this.handleDoubleClick(e));\n }\n\n getMousePos(e) {\n const rect = this.canvas.getBoundingClientRect();\n return {\n x: e.clientX - rect.left,\n y: e.clientY - rect.top\n };\n }\n\n handleMouseDown(e) {\n const pos = this.getMousePos(e);\n const clickedNode = this.findNodeAtPosition(pos);\n\n if (clickedNode) {\n if (this.mode === 'edge' && this.selectedNode) {\n this.addEdge(this.selectedNode, clickedNode);\n this.selectedNode = null;\n } else if (this.mode === 'edge') {\n this.selectedNode = clickedNode;\n } else {\n this.isDragging = true;\n this.selectedNode = clickedNode;\n }\n } else if (this.mode === 'node') {\n this.addNode(pos.x, pos.y);\n }\n }\n\n handleMouseMove(e) {\n if (this.isDragging && this.selectedNode) {\n const pos = this.getMousePos(e);\n this.selectedNode.x = pos.x;\n this.selectedNode.y = pos.y;\n }\n }\n\n handleMouseUp() {\n this.isDragging = false;\n }\n\n handleDoubleClick(e) {\n const pos = this.getMousePos(e);\n const node = this.findNodeAtPosition(pos);\n if (node) {\n this.removeNode(node);\n }\n }\n\n addNode(x, y) {\n this.nodes.push({\n x,\n y,\n radius: 20,\n color: this.getRandomColor()\n });\n }\n\n addEdge(node1, node2) {\n if (node1 !== node2) {\n this.edges.push({\n from: node1,\n to: node2\n });\n }\n }\n\n removeNode(node) {\n const index = this.nodes.indexOf(node);\n if (index > -1) {\n this.nodes.splice(index, 1);\n this.edges = this.edges.filter(edge => \n edge.from !== node && edge.to !== node\n );\n }\n }\n\n clear() {\n this.nodes = [];\n this.edges = [];\n this.selectedNode = null;\n }\n\n findNodeAtPosition(pos) {\n return this.nodes.find(node => \n Math.hypot(node.x - pos.x, node.y - pos.y) < node.radius\n );\n }\n\n getRandomColor() {\n const colors = ['#2ecc71', '#3498db', '#e74c3c', '#f1c40f', '#9b59b6'];\n return colors[Math.floor(Math.random() * colors.length)];\n }\n\n draw() {\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n\n // Draw edges\n this.edges.forEach(edge => {\n this.ctx.beginPath();\n this.ctx.moveTo(edge.from.x, edge.from.y);\n this.ctx.lineTo(edge.to.x, edge.to.y);\n this.ctx.strokeStyle = '#95a5a6';\n this.ctx.lineWidth = 2;\n this.ctx.stroke();\n });\n\n // Draw nodes\n this.nodes.forEach(node => {\n this.ctx.beginPath();\n this.ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);\n this.ctx.fillStyle = node === this.selectedNode ? '#e74c3c' : node.color;\n this.ctx.fill();\n this.ctx.strokeStyle = '#2c3e50';\n this.ctx.lineWidth = 2;\n this.ctx.stroke();\n });\n }\n\n animate() {\n this.draw();\n requestAnimationFrame(() => this.animate());\n }\n}\n\ndocument.addEventListener('DOMContentLoaded', () => {\n new GraphVisualizer();\n});\n---end---\n\nThis implementation creates an interactive graph visualization tool with the following features:\n- Add nodes by clicking the \"New Node\" button and clicking on the canvas\n- Add edges by clicking the \"New Edge\" button and selecting two nodes\n- Drag nodes to reposition them\n- Double-click nodes to delete them\n- Clear the entire graph with the \"Clear\" button\n- Responsive design that works on both desktop and mobile\n- Smooth animations and transitions\n- Professional color scheme and modern UI\n- Accessible controls and semantic HTML\n- Canvas-based rendering for optimal performance\n\nThe CSS includes modern features like CSS variables, fluid layouts, and smooth transitions. The JavaScript implementation uses classes and modern ES6+ features while maintaining broad browser compatibility." + } + ], + "stop_reason": "end_turn", + "stop_sequence": null, + "usage": { + "input_tokens": 818, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 3210 + } +} \ No newline at end of file diff --git a/readme.scroll b/readme.scroll new file mode 100644 index 0000000..5828ea3 --- /dev/null +++ b/readme.scroll @@ -0,0 +1,3 @@ +# viz.scroll.pub + +website generated by Claude from prompt: graph viz \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..304b4cd --- /dev/null +++ b/script.js @@ -0,0 +1,162 @@ +class GraphVisualizer { + constructor() { + this.canvas = document.getElementById('graphCanvas'); + this.ctx = this.canvas.getContext('2d'); + this.nodes = []; + this.edges = []; + this.isDragging = false; + this.selectedNode = null; + this.mode = 'node'; + + this.initializeCanvas(); + this.addEventListeners(); + this.animate(); + } + + initializeCanvas() { + this.resizeCanvas(); + window.addEventListener('resize', () => this.resizeCanvas()); + } + + resizeCanvas() { + this.canvas.width = this.canvas.offsetWidth; + this.canvas.height = this.canvas.offsetHeight; + } + + addEventListeners() { + document.getElementById('addNode').addEventListener('click', () => this.mode = 'node'); + document.getElementById('addEdge').addEventListener('click', () => this.mode = 'edge'); + document.getElementById('clear').addEventListener('click', () => this.clear()); + + this.canvas.addEventListener('mousedown', (e) => this.handleMouseDown(e)); + this.canvas.addEventListener('mousemove', (e) => this.handleMouseMove(e)); + this.canvas.addEventListener('mouseup', () => this.handleMouseUp()); + this.canvas.addEventListener('dblclick', (e) => this.handleDoubleClick(e)); + } + + getMousePos(e) { + const rect = this.canvas.getBoundingClientRect(); + return { + x: e.clientX - rect.left, + y: e.clientY - rect.top + }; + } + + handleMouseDown(e) { + const pos = this.getMousePos(e); + const clickedNode = this.findNodeAtPosition(pos); + + if (clickedNode) { + if (this.mode === 'edge' && this.selectedNode) { + this.addEdge(this.selectedNode, clickedNode); + this.selectedNode = null; + } else if (this.mode === 'edge') { + this.selectedNode = clickedNode; + } else { + this.isDragging = true; + this.selectedNode = clickedNode; + } + } else if (this.mode === 'node') { + this.addNode(pos.x, pos.y); + } + } + + handleMouseMove(e) { + if (this.isDragging && this.selectedNode) { + const pos = this.getMousePos(e); + this.selectedNode.x = pos.x; + this.selectedNode.y = pos.y; + } + } + + handleMouseUp() { + this.isDragging = false; + } + + handleDoubleClick(e) { + const pos = this.getMousePos(e); + const node = this.findNodeAtPosition(pos); + if (node) { + this.removeNode(node); + } + } + + addNode(x, y) { + this.nodes.push({ + x, + y, + radius: 20, + color: this.getRandomColor() + }); + } + + addEdge(node1, node2) { + if (node1 !== node2) { + this.edges.push({ + from: node1, + to: node2 + }); + } + } + + removeNode(node) { + const index = this.nodes.indexOf(node); + if (index > -1) { + this.nodes.splice(index, 1); + this.edges = this.edges.filter(edge => + edge.from !== node && edge.to !== node + ); + } + } + + clear() { + this.nodes = []; + this.edges = []; + this.selectedNode = null; + } + + findNodeAtPosition(pos) { + return this.nodes.find(node => + Math.hypot(node.x - pos.x, node.y - pos.y) < node.radius + ); + } + + getRandomColor() { + const colors = ['#2ecc71', '#3498db', '#e74c3c', '#f1c40f', '#9b59b6']; + return colors[Math.floor(Math.random() * colors.length)]; + } + + draw() { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + + // Draw edges + this.edges.forEach(edge => { + this.ctx.beginPath(); + this.ctx.moveTo(edge.from.x, edge.from.y); + this.ctx.lineTo(edge.to.x, edge.to.y); + this.ctx.strokeStyle = '#95a5a6'; + this.ctx.lineWidth = 2; + this.ctx.stroke(); + }); + + // Draw nodes + this.nodes.forEach(node => { + this.ctx.beginPath(); + this.ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2); + this.ctx.fillStyle = node === this.selectedNode ? '#e74c3c' : node.color; + this.ctx.fill(); + this.ctx.strokeStyle = '#2c3e50'; + this.ctx.lineWidth = 2; + this.ctx.stroke(); + }); + } + + animate() { + this.draw(); + requestAnimationFrame(() => this.animate()); + } +} + +document.addEventListener('DOMContentLoaded', () => { + new GraphVisualizer(); +}); \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..5055d5a --- /dev/null +++ b/style.css @@ -0,0 +1,173 @@ +:root { + --primary-color: #2a6b9c; + --secondary-color: #3498db; + --accent-color: #e74c3c; + --background-color: #f5f6fa; + --text-color: #2c3e50; + --shadow: 0 4px 6px rgba(0,0,0,0.1); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', system-ui, sans-serif; + line-height: 1.6; + color: var(--text-color); + background: var(--background-color); +} + +.hero { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + padding: 1rem; + color: white; +} + +nav { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem; + max-width: 1200px; + margin: 0 auto; +} + +.logo { + font-size: 1.5rem; + font-weight: bold; + text-shadow: 2px 2px 4px rgba(0,0,0,0.2); +} + +.nav-links { + display: flex; + gap: 2rem; +} + +.nav-links a { + color: white; + text-decoration: none; + transition: all 0.3s ease; +} + +.nav-links a:hover { + transform: translateY(-2px); + text-shadow: 0 0 8px rgba(255,255,255,0.5); +} + +.graph-workspace { + padding: 2rem; + background: white; + border-radius: 8px; + box-shadow: var(--shadow); + margin: 2rem auto; + max-width: 1200px; +} + +#graphCanvas { + width: 100%; + height: 600px; + border: 2px solid var(--secondary-color); + border-radius: 4px; + margin-top: 1rem; +} + +.controls { + display: flex; + gap: 1rem; + margin-bottom: 1rem; +} + +button { + padding: 0.5rem 1rem; + border: none; + border-radius: 4px; + background: var(--secondary-color); + color: white; + cursor: pointer; + transition: all 0.3s ease; +} + +button:hover { + background: var(--primary-color); + transform: translateY(-2px); +} + +.example-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; + padding: 2rem; +} + +.example-card { + background: white; + padding: 1rem; + border-radius: 8px; + box-shadow: var(--shadow); + transition: all 0.3s ease; +} + +.example-card:hover { + transform: translateY(-4px); + box-shadow: 0 6px 12px rgba(0,0,0,0.15); +} + +.mini-canvas { + width: 100%; + height: 200px; + border: 1px solid var(--secondary-color); + border-radius: 4px; +} + +.help-section { + background: white; + padding: 2rem; + margin: 2rem auto; + max-width: 1200px; + border-radius: 8px; + box-shadow: var(--shadow); +} + +footer { + background: var(--primary-color); + color: white; + padding: 2rem; + margin-top: 4rem; +} + +.footer-content { + max-width: 1200px; + margin: 0 auto; + display: flex; + justify-content: space-between; + align-items: center; +} + +.footer-links a { + color: white; + text-decoration: none; + margin-left: 2rem; +} + +@media (max-width: 768px) { + .nav-links { + gap: 1rem; + } + + .controls { + flex-direction: column; + } + + .example-grid { + grid-template-columns: 1fr; + } + + .footer-content { + flex-direction: column; + gap: 1rem; + text-align: center; + } +} \ No newline at end of file