diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..5938097
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,7 @@
+wasm-pack build --release stackline-wasm --target web --out-dir ../editor-solidjs/stackline-wasm
+
+[ -d editor-solidjs/font ] && rm editor-solidjs/font
+ln -s ../font editor-solidjs/font
+
+[ -f editor-solidjs/prime.json ] && rm editor-solidjs/prime.json
+ln -s ../stackline/tests/other/prime.json editor-solidjs/prime.json
diff --git a/editor-solidjs/.gitignore b/editor-solidjs/.gitignore
index 75c2320..a62e2d7 100644
--- a/editor-solidjs/.gitignore
+++ b/editor-solidjs/.gitignore
@@ -2,3 +2,4 @@ node_modules
dist
stackline-wasm
font
+prime.json
diff --git a/editor-solidjs/index.html b/editor-solidjs/index.html
index c0c774b..0bae665 100644
--- a/editor-solidjs/index.html
+++ b/editor-solidjs/index.html
@@ -2,7 +2,7 @@
-
+
Stackline web Editor
diff --git a/editor-solidjs/src/Editor.jsx b/editor-solidjs/src/Editor.jsx
index 772f037..06bc64f 100644
--- a/editor-solidjs/src/Editor.jsx
+++ b/editor-solidjs/src/Editor.jsx
@@ -11,7 +11,7 @@ import RightPane from "./RightPane.jsx";
import {World, Pane} from "../stackline-wasm/stackline_wasm.js";
-let json = await (await fetch("/stackline-wasm/prime.json")).json();
+let json = await (await fetch("/prime.json")).json();
export default function Editor() {
let [world, setWorld] = createSignal(World.deserialize(json), {equals: false});
diff --git a/editor-solidjs/src/Editor.module.css b/editor-solidjs/src/Editor.module.css
index e7f1755..b78c79c 100644
--- a/editor-solidjs/src/Editor.module.css
+++ b/editor-solidjs/src/Editor.module.css
@@ -5,4 +5,5 @@
flex-direction: row;
align-items: stretch;
overflow-y: hidden;
+ flex-wrap: nowrap;
}
diff --git a/editor-solidjs/src/LeftPane.module.css b/editor-solidjs/src/LeftPane.module.css
index 42c8da9..2e50180 100644
--- a/editor-solidjs/src/LeftPane.module.css
+++ b/editor-solidjs/src/LeftPane.module.css
@@ -4,6 +4,7 @@
padding: 1.5em 1em;
font-family: monospace;
font-size: 15px;
+ flex-shrink: 0;
overflow-y: scroll;
}
diff --git a/editor-solidjs/src/MiddlePane.jsx b/editor-solidjs/src/MiddlePane.jsx
index 8ee5bd0..f14088b 100644
--- a/editor-solidjs/src/MiddlePane.jsx
+++ b/editor-solidjs/src/MiddlePane.jsx
@@ -46,7 +46,9 @@ export default function MiddlePane(props) {
y: 0,
click_x: 0,
click_y: 0,
- hovering: false
+ hovering: false,
+ down: false,
+ touches: [],
});
createEffect(() => {
@@ -239,10 +241,18 @@ export default function MiddlePane(props) {
function resize_cb() {
if (!canvas.element) return;
setCanvas((obj) => {
+ let dpr = window.devicePixelRatio ?? 1;
+
+ let width = Math.round(container.clientWidth * dpr);
+ let height = Math.round(container.clientHeight * dpr);
+
+ obj.element.style.width = `${width / dpr}px`;
+ obj.element.style.height = `${height / dpr}px`;
+
return {
...obj,
- width: obj.element.width = canvas.element.clientWidth,
- height: obj.element.height = canvas.element.clientHeight,
+ width: obj.element.width = width,
+ height: obj.element.height = height,
};
});
}
@@ -251,13 +261,13 @@ export default function MiddlePane(props) {
function set_resize() {
setTimeout(() => {
- resize_listener.observe(canvas.element);
+ resize_listener.observe(container);
}, 0);
}
function remove_resize() {
setTimeout(() => {
- resize_listener.unobserve(canvas.element);
+ resize_listener.unobserve(container);
}, 0);
}
@@ -291,8 +301,9 @@ export default function MiddlePane(props) {
onMount={() => {mounted = true}}
onCleanup={() => {mounted = false}}
onMouseDown={(evt) => {
- let mouse_x = evt.clientX - container.offsetLeft;
- let mouse_y = evt.clientY - container.offsetTop;
+ let dpr = window.devicePixelRatio ?? 1;
+ let mouse_x = (evt.clientX - container.offsetLeft) * dpr;
+ let mouse_y = (evt.clientY - container.offsetTop) * dpr;
setMouse((mouse) => {
return {
...mouse,
@@ -306,21 +317,23 @@ export default function MiddlePane(props) {
});
}}
onMouseMove={(evt) => {
+ let dpr = window.devicePixelRatio ?? 1;
setMouse("hovering", true);
if (mouse.down) {
// TODO: numerically stable solution
- setView("cx", view.cx + (evt.clientX - container.offsetLeft) - mouse.x);
- setView("cy", view.cy + (evt.clientY - container.offsetTop) - mouse.y);
+ setView("cx", view.cx + (evt.clientX - container.offsetLeft) * dpr - mouse.x);
+ setView("cy", view.cy + (evt.clientY - container.offsetTop) * dpr - mouse.y);
}
- setMouse("x", evt.clientX - container.offsetLeft);
- setMouse("y", evt.clientY - container.offsetTop);
+ setMouse("x", (evt.clientX - container.offsetLeft) * dpr);
+ setMouse("y", (evt.clientY - container.offsetTop) * dpr);
}}
onMouseUp={(evt) => {
+ let dpr = window.devicePixelRatio ?? 1;
setMouse("down", false);
- setMouse("x", evt.clientX - container.offsetLeft);
- setMouse("y", evt.clientY - container.offsetTop);
+ setMouse("x", (evt.clientX - container.offsetLeft) * dpr);
+ setMouse("y", (evt.clientY - container.offsetTop) * dpr);
let dist = Math.sqrt((mouse.x - mouse.click_x) ** 2 + (mouse.y - mouse.click_y) ** 2);
if (dist < 10) {
@@ -335,9 +348,92 @@ export default function MiddlePane(props) {
setMouse("down", false);
setMouse("hovering", false);
}}
+ onTouchStart={(evt) => {
+ let dpr = window.devicePixelRatio ?? 1;
+ setMouse("hovering", false);
+
+ setMouse("touches", [...evt.touches].map(touch => ({
+ x: (touch.clientX - container.offsetLeft) * dpr,
+ y: (touch.clientY - container.offsetTop) * dpr,
+ identifier: touch.identifier,
+ })));
+
+ if (evt.touches.length === 1) {
+ setMouse("down", true);
+ setMouse("x", mouse.touches[0].x);
+ setMouse("y", mouse.touches[0].y);
+ setMouse("click_x", mouse.touches[0].x);
+ setMouse("click_y", mouse.touches[0].y);
+ } else {
+ setMouse("down", false);
+ }
+ }}
+ onTouchEnd={(evt) => {
+ let identifiers = [...evt.changedTouches].map(t => t.identifier);
+ setMouse("touches", (arr) => {
+ return arr.filter(touch => !identifiers.includes(touch.identifier));
+ });
+
+ if (evt.touches.length === 1) {
+ setMouse("down", true);
+ setMouse("x", mouse.touches[0].x);
+ setMouse("y", mouse.touches[0].y);
+ setMouse("click_x", mouse.touches[0].x);
+ setMouse("click_y", mouse.touches[0].y);
+ } else {
+ setMouse("down", false);
+ }
+ }}
+ oncapture:touchmove={(evt) => {
+ evt.preventDefault();
+ let dpr = window.devicePixelRatio ?? 1;
+ let touches = [...evt.touches].map(touch => ({
+ x: (touch.clientX - container.offsetLeft) * dpr,
+ y: (touch.clientY - container.offsetTop) * dpr,
+ identifier: touch.identifier,
+ }));
+
+ if (evt.touches.length === 1) {
+ let mouse_x = (evt.touches[0].clientX - container.offsetLeft) * dpr;
+ let mouse_y = (evt.touches[0].clientY - container.offsetTop) * dpr;
+
+ setView("cx", view.cx + mouse_x - mouse.x);
+ setView("cy", view.cy + mouse_y - mouse.y);
+
+ setMouse("x", mouse_x);
+ setMouse("y", mouse_y);
+ } else if (evt.touches.length === 2) {
+ let mx = (touches[0].x + touches[1].x) / 2;
+ let my = (touches[0].y + touches[1].y) / 2;
+ let dx = mx - (mouse.touches[0].x + mouse.touches[1].x) / 2;
+ let dy = my - (mouse.touches[0].y + mouse.touches[1].y) / 2;
+
+ let prev_dist = Math.sqrt(
+ (mouse.touches[0].x - mouse.touches[1].x) ** 2
+ + (mouse.touches[0].y - mouse.touches[1].y) ** 2
+ );
+
+ let dist = Math.sqrt(
+ (touches[0].x - touches[1].x) ** 2 + (touches[0].y - touches[1].y) ** 2
+ );
+
+ let old_zoom = view.zoom;
+ setView("zoom", Math.min(Math.max(view.zoom + Math.log2(dist / prev_dist), 0.0), 4.0));
+
+ let delta = Math.ceil(Math.pow(2, view.zoom)) / Math.ceil(Math.pow(2, old_zoom));
+
+ mx -= canvas.width / 2;
+ my -= canvas.height / 2;
+
+ setView("cx", (view.cx - mx) * delta + mx + dx);
+ setView("cy", (view.cy - my) * delta + my + dy);
+ }
+
+ setMouse("touches", touches);
+ }}
onWheel={(evt) => {
let old_zoom = view.zoom;
- let zoom = view.zoom + event.deltaY * ZOOM_STRENGTH;
+ let zoom = view.zoom + evt.deltaY * ZOOM_STRENGTH;
zoom = Math.min(Math.max(zoom, 0.0), 4.0);
setView("zoom", zoom);
@@ -356,12 +452,13 @@ export default function MiddlePane(props) {
setSettings("selected", getHovered());
}}
onDragOver={(evt) => {
+ let dpr = window.devicePixelRatio ?? 1;
evt.preventDefault();
container.classList.add(styles.dragging);
setMouse("hovering", true);
- setMouse("x", evt.clientX - container.offsetLeft);
- setMouse("y", evt.clientY - container.offsetTop);
+ setMouse("x", (evt.clientX - container.offsetLeft) * dpr);
+ setMouse("y", (evt.clientY - container.offsetTop) * dpr);
}}
onDragLeave={(evt) => {
setMouse("hovering", false);
diff --git a/editor-solidjs/src/MiddlePane.module.css b/editor-solidjs/src/MiddlePane.module.css
index feeae49..e106ac3 100644
--- a/editor-solidjs/src/MiddlePane.module.css
+++ b/editor-solidjs/src/MiddlePane.module.css
@@ -1,12 +1,17 @@
.MiddlePane {
flex-grow: 1;
+ flex-shrink: 1;
background: #202027;
position: relative;
+ height: 100vh;
+ width: 100%;
+ overflow-x: hidden;
+ overflow-y: hidden;
}
.canvas {
- width: 100%;
- height: 100vh;
+ /* width: 100%; */
+ /* height: 100vh; */
}
.MiddlePane.dragging::after {
diff --git a/editor-solidjs/src/RightPane.module.css b/editor-solidjs/src/RightPane.module.css
index d5491a1..3928f99 100644
--- a/editor-solidjs/src/RightPane.module.css
+++ b/editor-solidjs/src/RightPane.module.css
@@ -6,6 +6,7 @@
padding: 1.5em 1em;
font-family: monospace;
font-size: 15px;
+ flex-shrink: 0;
}
.gray, .empty {
diff --git a/editor-solidjs/src/TilePreset.jsx b/editor-solidjs/src/TilePreset.jsx
index d78e42c..6b266f9 100644
--- a/editor-solidjs/src/TilePreset.jsx
+++ b/editor-solidjs/src/TilePreset.jsx
@@ -1,4 +1,4 @@
-import {createMemo, createUniqueId} from "solid-js";
+import {createEffect, createMemo, createUniqueId} from "solid-js";
import {Pane} from "../stackline-wasm/stackline_wasm.js";
@@ -21,6 +21,8 @@ export default function TilePreset(props) {
let id = createUniqueId();
+ let preview;
+
return (
-
{rendered().text}
+
{rendered().text}
{props.name}
);
}
diff --git a/editor-solidjs/src/index.css b/editor-solidjs/src/index.css
index f6aaede..7550300 100644
--- a/editor-solidjs/src/index.css
+++ b/editor-solidjs/src/index.css
@@ -1,4 +1,5 @@
body {
margin: 0;
height: 100vh;
+ overflow: hidden;
}