1 Commits

Author SHA1 Message Date
e4a3bd0ce2 wip vite lib, typst markup 2026-07-07 22:49:09 -04:00
10 changed files with 76 additions and 173 deletions

35
main.typ Normal file
View File

@@ -0,0 +1,35 @@
#let put(x, y, z, ..args) = {
(
"p",
str(x),
str(y),
str(z),
..args.pos().map(str),
..args.named().pairs().map((u, v) => str(u) + "=" + str(v)),
).join(" ")
}
#html.html({
html.head({
html.link(rel: "stylesheet", href: "./mc-diorama.css")
html.script(src: "./mc-diorama.mjs", type: "module")
})
html.body({
html.main([
= hello world
])
html.elem("mc-diorama", {
html.elem("mc-camera")
html.elem(
"mc-world",
"
p 0 0 0 stone;
p 0 0 1 piston facing=up extended=false &
p 1 0 0 redstone_block;
",
)
})
})
})

40
package-lock.json generated
View File

@@ -1,14 +1,14 @@
{ {
"name": "wireless", "name": "mc-diorama",
"version": "0.0.0", "version": "0.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "wireless", "name": "mc-diorama",
"version": "0.0.0", "version": "0.1.0",
"devDependencies": { "devDependencies": {
"vite": "^8.1.1" "vite": "^8.1.3"
} }
}, },
"node_modules/@emnapi/core": { "node_modules/@emnapi/core": {
@@ -167,9 +167,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -187,9 +184,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -207,9 +201,6 @@
"ppc64" "ppc64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -227,9 +218,6 @@
"s390x" "s390x"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -247,9 +235,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -267,9 +252,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -553,9 +535,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -577,9 +556,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -601,9 +577,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -625,9 +598,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [

View File

@@ -1,14 +1,13 @@
{ {
"name": "wireless", "name": "mc-diorama",
"version": "0.0.0", "version": "0.1.0",
"private": true, "files": [
"type": "module", "dist"
"scripts": { ],
"dev": "vite", "module": "./dist/mc-diorama.es.js",
"build": "vite build", "exports": {
"preview": "vite preview" ".": {
}, "import": "./dist/mc-diorama.es.js"
"devDependencies": { }
"vite": "^8.1.1"
} }
} }

View File

@@ -190,7 +190,6 @@ mc-diorama {
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
touch-action: none;
& .track-inner { & .track-inner {
position: relative; position: relative;

View File

@@ -8,7 +8,7 @@ import {SpacetimeController} from './controller.js';
window.mcEngine = new Engine(); window.mcEngine = new Engine();
document.body.appendChild(window.mcEngine.canvas) document.body.appendChild(window.mcEngine.canvas)
export class MCWorldElement extends HTMLElement { class MCWorldElement extends HTMLElement {
connectedCallback() { connectedCallback() {
if (this.world) return; if (this.world) return;
// Read text content directly, allowing inline scripting // Read text content directly, allowing inline scripting
@@ -17,7 +17,7 @@ export class MCWorldElement extends HTMLElement {
} }
} }
export class MCFrameElement extends HTMLElement { class MCFrameElement extends HTMLElement {
connectedCallback() { connectedCallback() {
// Use a microtask to ensure children are parsed // Use a microtask to ensure children are parsed
setTimeout(() => this.init(), 0); setTimeout(() => this.init(), 0);
@@ -42,7 +42,7 @@ export class MCFrameElement extends HTMLElement {
} }
} }
export class MCDioramaElement extends HTMLElement { class MCDioramaElement extends HTMLElement {
connectedCallback() { connectedCallback() {
setTimeout(() => this.init(), 0); setTimeout(() => this.init(), 0);
} }

View File

@@ -230,111 +230,6 @@ export class SpacetimeController {
this.syncUI(); this.syncUI();
} }
}); });
// --- MOBILE TOUCH LOGIC ("Wait and See" Scroll vs Camera) ---
this.touchEngaged = false;
this.touchTimer = null;
this.touchStartPos = new Map();
this.lastTouchPos = new Map();
this.canvas.addEventListener('touchstart', (e) => {
if (!this.hasSpatial) return;
for (let i = 0; i < e.changedTouches.length; i++) {
const t = e.changedTouches[i];
this.touchStartPos.set(t.identifier, {x: t.clientX, y: t.clientY});
this.lastTouchPos.set(t.identifier, {x: t.clientX, y: t.clientY});
}
clearTimeout(this.touchTimer);
// Instantly engage if multi-touch (pinch/pan). No wait required!
if (e.touches.length >= 2) {
this.touchEngaged = true;
this.hasDragged = true;
if (e.cancelable) e.preventDefault();
} else {
// Single touch: wait to see if it's a page scroll
this.touchEngaged = false;
this.touchTimer = setTimeout(() => {
this.touchEngaged = true;
this.hasDragged = true;
}, 200);
}
}, {passive: false});
this.canvas.addEventListener('touchmove', (e) => {
if (!this.hasSpatial) return;
if (!this.touchEngaged) {
// Check if they are swiping quickly before the timer completes (1 finger).
let maxDist = 0;
for (let i = 0; i < e.touches.length; i++) {
const t = e.touches[i];
const start = this.touchStartPos.get(t.identifier);
if (start) {
maxDist = Math.max(maxDist, Math.hypot(t.clientX - start.x, t.clientY - start.y));
}
}
if (maxDist > 10) clearTimeout(this.touchTimer);
return; // Let the browser scroll the page
}
// --- CAMERA IS ENGAGED ---
if (e.cancelable) e.preventDefault(); // Block native pinch-zoom and scrolling
this.hasDragged = true;
if (e.touches.length === 1) {
// 1 Finger: Orbit
const t = e.touches[0];
const last = this.lastTouchPos.get(t.identifier);
if (last) {
this.orbit(t.clientX - last.x, t.clientY - last.y);
notifyCameraChange();
}
this.lastTouchPos.set(t.identifier, {x: t.clientX, y: t.clientY});
} else if (e.touches.length === 2) {
// 2 Fingers: Pan and Zoom
const t1 = e.touches[0];
const t2 = e.touches[1];
const l1 = this.lastTouchPos.get(t1.identifier);
const l2 = this.lastTouchPos.get(t2.identifier);
if (l1 && l2) {
// Zoom (Pinch distance delta)
const lastDist = Math.hypot(l1.x - l2.x, l1.y - l2.y);
const currentDist = Math.hypot(t1.clientX - t2.clientX, t1.clientY - t2.clientY);
this.zoomRatio(lastDist / currentDist);
// Pan (Center point delta)
const lastCx = (l1.x + l2.x) / 2;
const lastCy = (l1.y + l2.y) / 2;
const cx = (t1.clientX + t2.clientX) / 2;
const cy = (t1.clientY + t2.clientY) / 2;
this.pan(cx - lastCx, cy - lastCy);
notifyCameraChange();
}
this.lastTouchPos.set(t1.identifier, {x: t1.clientX, y: t1.clientY});
this.lastTouchPos.set(t2.identifier, {x: t2.clientX, y: t2.clientY});
}
}, {passive: false});
const handleTouchEnd = (e) => {
for (let i = 0; i < e.changedTouches.length; i++) {
const t = e.changedTouches[i];
this.touchStartPos.delete(t.identifier);
this.lastTouchPos.delete(t.identifier);
}
if (e.touches.length === 0) {
clearTimeout(this.touchTimer);
this.touchEngaged = false;
}
};
this.canvas.addEventListener('touchend', handleTouchEnd);
this.canvas.addEventListener('touchcancel', handleTouchEnd);
} }
// --- TEMPORAL CONTROL --- // --- TEMPORAL CONTROL ---
@@ -378,26 +273,19 @@ export class SpacetimeController {
onDrag(pct); onDrag(pct);
}; };
wrapper.addEventListener('pointerdown', (e) => { wrapper.addEventListener('mousedown', (e) => {
wrapper.setPointerCapture(e.pointerId);
isTrackDragging = true; isTrackDragging = true;
this.playMode = 'paused'; this.playMode = 'paused';
update(e); update(e);
}); });
wrapper.addEventListener('pointermove', (e) => { window.addEventListener('mousemove', (e) => {
if (isTrackDragging) update(e); if (isTrackDragging) update(e);
}); });
const stopDrag = (e) => { window.addEventListener('mouseup', () => {
if (isTrackDragging) {
isTrackDragging = false; isTrackDragging = false;
wrapper.releasePointerCapture(e.pointerId); });
}
};
wrapper.addEventListener('pointerup', stopDrag);
wrapper.addEventListener('pointercancel', stopDrag);
} }
bindTimelineEvents() { bindTimelineEvents() {

2
src/lib.js Normal file
View File

@@ -0,0 +1,2 @@
import './components.js';
import './components.css';

View File

@@ -54,10 +54,10 @@ export class TextureHandler {
const drawMissing = () => { const drawMissing = () => {
const half = this.cellSize / 2; const half = this.cellSize / 2;
this.ctx.fillStyle = '#33333366'; // Gray this.ctx.fillStyle = '#666666FF'; // Gray
this.ctx.fillRect(pos.x, pos.y, half, half); this.ctx.fillRect(pos.x, pos.y, half, half);
this.ctx.fillRect(pos.x + half, pos.y + half, half, half); this.ctx.fillRect(pos.x + half, pos.y + half, half, half);
this.ctx.fillStyle = '#00000066'; // Black this.ctx.fillStyle = '#333333FF'; // Black
this.ctx.fillRect(pos.x + half, pos.y, half, half); this.ctx.fillRect(pos.x + half, pos.y, half, half);
this.ctx.fillRect(pos.x, pos.y + half, half, half); this.ctx.fillRect(pos.x, pos.y + half, half, half);
}; };

View File

@@ -36,7 +36,7 @@ export class World {
capturedInitialState = true; capturedInitialState = true;
}; };
const lines = script.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#')); const lines = script.split(/\n|;|(?<=&)/).map(l => l.trim()).filter(l => l && !l.startsWith('#'));
let currentBatch = []; let currentBatch = [];

View File

@@ -1,3 +1,13 @@
export default { import {dirname, resolve} from 'node:path'
base: "/secret-knowledge/", import {defineConfig} from 'vite'
}
export default defineConfig({
build: {
lib: {
entry: resolve(import.meta.dirname, 'src/lib.js'),
name: 'mc-diorama',
fileName: 'mc-diorama',
formats: ['es']
}
}
});