Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions canvases/dark-jsoncanvas/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
id: 22222222-2222-2222-2222-222222222222
main: main
387 changes: 387 additions & 0 deletions canvases/dark-jsoncanvas/main.dark
Original file line number Diff line number Diff line change
@@ -0,0 +1,387 @@
// JSON Canvas demonstration canvas
// This demonstrates the Stachu.JsonCanvas module with HTTP endpoints

[<HttpHandler("GET", "/ping")>]
let _handler _req =
Stdlib.Http.response (Stdlib.String.toBytes "JSON Canvas demo is running!") 200L


[<HttpHandler("GET", "/")>]
let _handler _req =
let html =
"<!DOCTYPE html>
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\">
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
<title>JSON Canvas Viewer</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
overflow: hidden;
background: #1e1e1e;
color: #fff;
}
#controls {
position: fixed;
top: 20px;
left: 20px;
background: rgba(40, 40, 40, 0.95);
padding: 15px;
border-radius: 8px;
z-index: 1000;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
max-width: 300px;
}
#controls h2 {
font-size: 16px;
margin-bottom: 12px;
color: #4fc3f7;
}
#controls button {
background: #4fc3f7;
color: #000;
border: none;
padding: 8px 16px;
margin: 4px;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
transition: all 0.2s;
}
#controls button:hover {
background: #81d4fa;
transform: translateY(-1px);
}
#canvas {
width: 100vw;
height: 100vh;
cursor: grab;
}
#canvas:active {
cursor: grabbing;
}
.node {
position: absolute;
background: #2e2e2e;
border: 2px solid #4fc3f7;
border-radius: 8px;
padding: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
cursor: move;
user-select: none;
transition: box-shadow 0.2s;
}
.node:hover {
box-shadow: 0 4px 16px rgba(79, 195, 247, 0.4);
z-index: 100;
}
.node.text-node {
background: linear-gradient(135deg, #2e2e2e 0%, #3a3a3a 100%);
}
.node.link-node {
background: linear-gradient(135deg, #1a237e 0%, #283593 100%);
border-color: #7986cb;
}
.node.file-node {
background: linear-gradient(135deg, #1b5e20 0%, #2e7d32 100%);
border-color: #81c784;
}
.node.group-node {
background: rgba(255, 152, 0, 0.1);
border-color: #ff9800;
border-style: dashed;
}
.node-content {
font-size: 14px;
line-height: 1.4;
}
.node-id {
font-size: 10px;
color: #999;
margin-top: 4px;
}
svg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
line.edge {
stroke: #4fc3f7;
stroke-width: 2;
opacity: 0.6;
}
.edge-label {
fill: #81d4fa;
font-size: 12px;
font-weight: 600;
}
</style>
</head>
<body>
<div id=\"controls\">
<h2>JSON Canvas Demo</h2>
<button onclick=\"loadExample()\">Load Example</button>
<button onclick=\"loadMindmap()\">Load Mindmap</button>
<button onclick=\"loadWorkflow()\">Load Workflow</button>
<button onclick=\"resetView()\">Reset View</button>
</div>
<div id=\"canvas\">
<svg id=\"edges\"></svg>
</div>
<script>
let offsetX = 0, offsetY = 0;
let isPanning = false;
let isDraggingNode = false;
let startX, startY;
let draggedNode = null;
let scale = 1;
const canvas = document.getElementById('canvas');

// Node dragging
document.addEventListener('mousedown', (e) => {
const node = e.target.closest('.node');
if (node) {
isDraggingNode = true;
draggedNode = node;
startX = e.clientX;
startY = e.clientY;
node.style.zIndex = '1000';
e.preventDefault();
return;
}

// Pan canvas if clicking on background
if (e.target === canvas || e.target.tagName === 'svg') {
isPanning = true;
startX = e.clientX - offsetX;
startY = e.clientY - offsetY;
}
});

document.addEventListener('mousemove', (e) => {
if (isDraggingNode && draggedNode) {
const dx = e.clientX - startX;
const dy = e.clientY - startY;
const currentX = parseFloat(draggedNode.dataset.x);
const currentY = parseFloat(draggedNode.dataset.y);
draggedNode.dataset.x = currentX + dx;
draggedNode.dataset.y = currentY + dy;
startX = e.clientX;
startY = e.clientY;
updateView();
e.preventDefault();
} else if (isPanning) {
offsetX = e.clientX - startX;
offsetY = e.clientY - startY;
updateView();
}
});

document.addEventListener('mouseup', () => {
if (draggedNode) {
draggedNode.style.zIndex = '';
}
isDraggingNode = false;
isPanning = false;
draggedNode = null;
});

function updateView() {
const nodes = document.querySelectorAll('.node');
nodes.forEach(node => {
const x = parseFloat(node.dataset.x);
const y = parseFloat(node.dataset.y);
node.style.transform = `translate(${x + offsetX}px, ${y + offsetY}px)`;
});
drawEdges();
}

function resetView() {
offsetX = window.innerWidth / 2 - 400;
offsetY = window.innerHeight / 2 - 300;
updateView();
}

function createCanvas(data) {
// Clear existing
document.querySelectorAll('.node').forEach(n => n.remove());
document.getElementById('edges').innerHTML = '';

// Create nodes
data.nodes.forEach(node => {
const div = document.createElement('div');
div.className = 'node';
div.dataset.id = node.id;
div.dataset.x = node.x;
div.dataset.y = node.y;

if (node.type === 'text') {
div.classList.add('text-node');
div.innerHTML = `<div class=\"node-content\">${node.text}</div><div class=\"node-id\">${node.id}</div>`;
} else if (node.type === 'link') {
div.classList.add('link-node');
div.innerHTML = `<div class=\"node-content\">🔗 <a href=\"${node.url}\" style=\"color:#81d4fa\">${node.url}</a></div><div class=\"node-id\">${node.id}</div>`;
} else if (node.type === 'file') {
div.classList.add('file-node');
div.innerHTML = `<div class=\"node-content\">📄 ${node.file}</div><div class=\"node-id\">${node.id}</div>`;
} else if (node.type === 'group') {
div.classList.add('group-node');
div.innerHTML = `<div class=\"node-content\">${node.label || 'Group'}</div><div class=\"node-id\">${node.id}</div>`;
}

div.style.width = node.width + 'px';
div.style.height = node.height + 'px';
canvas.appendChild(div);
});

window.canvasData = data;
resetView();
}

function drawEdges() {
const svg = document.getElementById('edges');
svg.innerHTML = '';

if (!window.canvasData) return;

window.canvasData.edges.forEach(edge => {
const fromNode = document.querySelector(`[data-id=\"${edge.fromNode}\"]`);
const toNode = document.querySelector(`[data-id=\"${edge.toNode}\"]`);

if (fromNode && toNode) {
const fromX = parseFloat(fromNode.dataset.x) + parseFloat(fromNode.style.width) / 2 + offsetX;
const fromY = parseFloat(fromNode.dataset.y) + parseFloat(fromNode.style.height) / 2 + offsetY;
const toX = parseFloat(toNode.dataset.x) + parseFloat(toNode.style.width) / 2 + offsetX;
const toY = parseFloat(toNode.dataset.y) + parseFloat(toNode.style.height) / 2 + offsetY;

const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('class', 'edge');
line.setAttribute('x1', fromX);
line.setAttribute('y1', fromY);
line.setAttribute('x2', toX);
line.setAttribute('y2', toY);
svg.appendChild(line);

if (edge.label) {
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
text.setAttribute('class', 'edge-label');
text.setAttribute('x', (fromX + toX) / 2);
text.setAttribute('y', (fromY + toY) / 2);
text.setAttribute('text-anchor', 'middle');
text.textContent = edge.label;
svg.appendChild(text);
}
}
});
}

async function loadExample() {
const response = await fetch('/api/example');
const data = await response.json();
createCanvas(data);
}

async function loadMindmap() {
const response = await fetch('/api/mindmap');
const data = await response.json();
createCanvas(data);
}

async function loadWorkflow() {
const response = await fetch('/api/workflow');
const data = await response.json();
createCanvas(data);
}

// Load example on startup
loadExample();
</script>
</body>
</html>"

Stdlib.Http.response (Stdlib.String.toBytes html) 200L


// Create a simple canvas with some nodes and edges
[<HttpHandler("GET", "/canvas/example")>]
let _handler _req =
let canvas = Stachu.JsonCanvas.Examples.basic ()
let nodeIds = Stachu.JsonCanvas.getNodeIds canvas
let nodeIdsStr = Stdlib.String.join nodeIds ", "
let stats = Stachu.JsonCanvas.countNodesByType canvas

let responseText =
$"Canvas created successfully!\n\nNode IDs: {nodeIdsStr}\n\nNode counts: {stats}\n\nTotal edges: {Builtin.int64ToString (Stdlib.List.length canvas.edges)}"

Stdlib.Http.response (Stdlib.String.toBytes responseText) 200L


// Get information about a canvas structure
[<HttpHandler("GET", "/canvas/info")>]
let _handler _req =
let canvas =
(Stachu.JsonCanvas.empty ())
|> Stachu.JsonCanvas.addNode (Stachu.JsonCanvas.Node.text "text1" 0L 0L 200L 100L "Text node 1")
|> Stachu.JsonCanvas.addNode (Stachu.JsonCanvas.Node.text "text2" 250L 0L 200L 100L "Text node 2")
|> Stachu.JsonCanvas.addNode (Stachu.JsonCanvas.Node.file "file1" 0L 150L 200L 100L "/path/to/file.md")
|> Stachu.JsonCanvas.addNode (Stachu.JsonCanvas.Node.link "link1" 250L 150L 200L 100L "https://darklang.com")

let stats = Stachu.JsonCanvas.countNodesByType canvas
let responseText = $"Canvas Statistics:\n{stats}\n\nTotal nodes: {Builtin.int64ToString (Stdlib.List.length canvas.nodes)}"

Stdlib.Http.response (Stdlib.String.toBytes responseText) 200L


// Demonstrate building a mind map structure
[<HttpHandler("GET", "/canvas/mindmap")>]
let _handler _req =
let mindmap = Stachu.JsonCanvas.Examples.mindmap ()
let nodeCount = Stdlib.List.length mindmap.nodes
let edgeCount = Stdlib.List.length mindmap.edges

let responseText =
$"Mind Map Created!\n\nNodes: {Builtin.int64ToString nodeCount}\nEdges: {Builtin.int64ToString edgeCount}\n\n{Stachu.JsonCanvas.countNodesByType mindmap}"

Stdlib.Http.response (Stdlib.String.toBytes responseText) 200L


// Demonstrate workflow/flowchart structure
[<HttpHandler("GET", "/canvas/workflow")>]
let _handler _req =
let workflow = Stachu.JsonCanvas.Examples.workflow ()

let responseText =
$"Workflow Canvas Created!\n\nThis demonstrates a simple HTTP request processing workflow.\n\nNodes: {Builtin.int64ToString (Stdlib.List.length workflow.nodes)}\nEdges: {Builtin.int64ToString (Stdlib.List.length workflow.edges)}"

Stdlib.Http.response (Stdlib.String.toBytes responseText) 200L


// JSON API Endpoints
[<HttpHandler("GET", "/api/example")>]
let _handler _req =
let canvas = Stachu.JsonCanvas.Examples.basic ()
let json = Stachu.JsonCanvas.Json.canvasToJson canvas

Stdlib.Http.response (Stdlib.String.toBytes json) 200L


[<HttpHandler("GET", "/api/mindmap")>]
let _handler _req =
let mindmap = Stachu.JsonCanvas.Examples.mindmap ()
let json = Stachu.JsonCanvas.Json.canvasToJson mindmap

Stdlib.Http.response (Stdlib.String.toBytes json) 200L


[<HttpHandler("GET", "/api/workflow")>]
let _handler _req =
let workflow = Stachu.JsonCanvas.Examples.workflow ()
let json = Stachu.JsonCanvas.Json.canvasToJson workflow

Stdlib.Http.response (Stdlib.String.toBytes json) 200L
Loading