First incomplete but working version
This commit is contained in:
commit
777d27a5d7
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
24
index.html
Normal file
24
index.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Local-First Geometry App</title>
|
||||
<link rel="stylesheet" href="src/style.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- Bereich für Buttons, um neue Formen anzulegen -->
|
||||
<div id="controls">
|
||||
<button id="addCircleButton">Kreis hinzufügen</button>
|
||||
<button id="addTriangleButton">Dreieck hinzufügen</button>
|
||||
</div>
|
||||
|
||||
<!-- Canvas, auf dem wir die Formen zeichnen -->
|
||||
<canvas id="shapesCanvas" width="600" height="400"></canvas>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
1681
package-lock.json
generated
Normal file
1681
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
package.json
Normal file
21
package.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "automerge-tutorial",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^6.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@automerge/automerge-repo": "^1.2.1",
|
||||
"@automerge/automerge-repo-network-websocket": "^1.2.1",
|
||||
"@automerge/automerge-repo-storage-indexeddb": "^1.2.1",
|
||||
"vite-plugin-top-level-await": "^1.4.4",
|
||||
"vite-plugin-wasm": "^3.4.1"
|
||||
}
|
||||
}
|
||||
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
50
src/canvasManager.js
Normal file
50
src/canvasManager.js
Normal file
@ -0,0 +1,50 @@
|
||||
// js/canvasManager.js
|
||||
|
||||
/**
|
||||
* Zeichnet alle Shapes auf dem Canvas neu
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {Array} shapesArray - Array der Shapes aus dem Dokument
|
||||
*/
|
||||
export function drawAllShapesOnCanvas(canvas, shapesArray) {
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
// Canvas leerräumen
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
shapesArray.forEach(shape => {
|
||||
if (shape.type === "circle") {
|
||||
drawCircle(ctx, shape);
|
||||
} else if (shape.type === "triangle") {
|
||||
drawTriangle(ctx, shape);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeichnet einen Kreis
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
* @param {Object} circle
|
||||
*/
|
||||
function drawCircle(ctx, circle) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(circle.x, circle.y, circle.radius, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = circle.color || "black";
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeichnet ein Dreieck
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
* @param {Object} triangle
|
||||
*/
|
||||
function drawTriangle(ctx, triangle) {
|
||||
const [p1, p2, p3] = triangle.coordinates;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(p1.x, p1.y);
|
||||
ctx.lineTo(p2.x, p2.y);
|
||||
ctx.lineTo(p3.x, p3.y);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = triangle.color || "black";
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
86
src/docManager.js
Normal file
86
src/docManager.js
Normal file
@ -0,0 +1,86 @@
|
||||
// docManager.js (Automerge 2.x style)
|
||||
|
||||
import { Repo } from "@automerge/automerge-repo"
|
||||
// For IndexedDB storage
|
||||
import { IndexedDBStorageAdapter } from '@automerge/automerge-repo-storage-indexeddb'
|
||||
import { BrowserWebSocketClientAdapter } from "@automerge/automerge-repo-network-websocket"
|
||||
|
||||
let handle
|
||||
|
||||
export function initRepo() {
|
||||
const repo = new Repo({
|
||||
// Choose a network adapter or omit if you’re doing pure local
|
||||
network: [new BrowserWebSocketClientAdapter("wss://automerge.rheinheim.fraction.ch")],
|
||||
|
||||
// Use IndexedDB instead of localStorage
|
||||
storage: new IndexedDBStorageAdapter({
|
||||
// optionally specify a database name (defaults to "automerge")
|
||||
databaseName: "myAutomergeDB"
|
||||
})
|
||||
})
|
||||
|
||||
// Now create or open a document
|
||||
handle = repo.create()
|
||||
|
||||
// Listen for 'change' events
|
||||
handle.on("change", ({doc}) => {
|
||||
console.log("Document changed!")
|
||||
// The up-to-date document is accessible via handle.doc
|
||||
console.log("Current doc state:", doc)
|
||||
})
|
||||
|
||||
// Initialize the doc with some data
|
||||
handle.change(doc => {
|
||||
doc.shapes = []
|
||||
})
|
||||
|
||||
return handle
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new circle to the document
|
||||
*/
|
||||
export function addCircle(x, y, radius) {
|
||||
handle.change(doc => {
|
||||
doc.shapes.push({
|
||||
id: Date.now(),
|
||||
type: "circle",
|
||||
x,
|
||||
y,
|
||||
radius,
|
||||
color: "red"
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Fügt dem Dokument ein Dreieck hinzu
|
||||
*/
|
||||
export function addTriangle(x1, y1, x2, y2, x3, y3) {
|
||||
handle.change(doc => {
|
||||
doc.shapes.push({
|
||||
id: Date.now(),
|
||||
type: "triangle",
|
||||
coordinates: [
|
||||
{ x: x1, y: y1 },
|
||||
{ x: x2, y: y2 },
|
||||
{ x: x3, y: y3 },
|
||||
],
|
||||
color: "blue"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a shape in the document
|
||||
*/
|
||||
export function moveShape(shapeId, newX, newY) {
|
||||
handle.change(doc => {
|
||||
const shape = doc.shapes.find(s => s.id === shapeId)
|
||||
if (shape) {
|
||||
shape.x = newX
|
||||
shape.y = newY
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
35
src/main.js
Normal file
35
src/main.js
Normal file
@ -0,0 +1,35 @@
|
||||
// main.js
|
||||
import { initRepo, addCircle, addTriangle, moveShape } from "./docManager.js"
|
||||
import { drawAllShapesOnCanvas } from "./canvasManager.js"
|
||||
|
||||
const canvas = document.getElementById("shapesCanvas")
|
||||
|
||||
// 1. Init the repo & document handle
|
||||
const handle = initRepo()
|
||||
// TODO is the whenReady() needed ?
|
||||
// await handle.whenReady()
|
||||
|
||||
|
||||
// 2. Subscribe so we can re-render whenever doc changes
|
||||
// Listen for 'change' events
|
||||
handle.on("change", ({doc}) => {
|
||||
console.log("Document changed! Drawallshapes", doc)
|
||||
drawAllShapesOnCanvas(canvas, doc.shapes)
|
||||
})
|
||||
|
||||
// 3. Buttons holen
|
||||
const addCircleButton = document.getElementById("addCircleButton");
|
||||
const addTriangleButton = document.getElementById("addTriangleButton");
|
||||
|
||||
// 4. Event Listener für Buttons
|
||||
addCircleButton.addEventListener("click", () => {
|
||||
// z. B. zufällige Position/Radius
|
||||
addCircle(100, 100, 30);
|
||||
// TODO render();
|
||||
});
|
||||
|
||||
addTriangleButton.addEventListener("click", () => {
|
||||
// z. B. zufällige Koordinaten
|
||||
addTriangle( 200, 200, 220, 220, 180, 220);
|
||||
// TODO render();
|
||||
});
|
||||
104
src/style.css
Normal file
104
src/style.css
Normal file
@ -0,0 +1,104 @@
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
canvas {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
#controls {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.vanilla:hover {
|
||||
filter: drop-shadow(0 0 2em #f7df1eaa);
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
19
vite.config.ts
Normal file
19
vite.config.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { defineConfig } from "vite"
|
||||
import wasm from "vite-plugin-wasm"
|
||||
import topLevelAwait from 'vite-plugin-top-level-await'
|
||||
|
||||
export default defineConfig({
|
||||
// customize this to your repo name for github pages deploy
|
||||
base: "",
|
||||
|
||||
build: {
|
||||
target: "esnext",
|
||||
},
|
||||
|
||||
plugins: [wasm(), topLevelAwait()],
|
||||
|
||||
worker: {
|
||||
format: "es",
|
||||
plugins: () => [wasm()],
|
||||
},
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user