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

View File

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

View File

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

View File

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

View File

@@ -230,111 +230,6 @@ export class SpacetimeController {
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 ---
@@ -378,26 +273,19 @@ export class SpacetimeController {
onDrag(pct);
};
wrapper.addEventListener('pointerdown', (e) => {
wrapper.setPointerCapture(e.pointerId);
wrapper.addEventListener('mousedown', (e) => {
isTrackDragging = true;
this.playMode = 'paused';
update(e);
});
wrapper.addEventListener('pointermove', (e) => {
window.addEventListener('mousemove', (e) => {
if (isTrackDragging) update(e);
});
const stopDrag = (e) => {
if (isTrackDragging) {
window.addEventListener('mouseup', () => {
isTrackDragging = false;
wrapper.releasePointerCapture(e.pointerId);
}
};
wrapper.addEventListener('pointerup', stopDrag);
wrapper.addEventListener('pointercancel', stopDrag);
});
}
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 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 + 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, pos.y + half, half, half);
};

View File

@@ -36,7 +36,7 @@ export class World {
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 = [];

View File

@@ -1,3 +1,13 @@
export default {
base: "/secret-knowledge/",
import {dirname, resolve} from 'node:path'
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']
}
}
});