aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorMetroWind <chris.corsair@gmail.com>2025-08-30 16:24:35 -0700
committerMetroWind <chris.corsair@gmail.com>2025-08-30 16:24:35 -0700
commit6a6cc2f22e806be895f9fddc9ed6cabab395b6cb (patch)
treedcc41b9bb566d900c7cb743de4c6aea276e4748e /scripts
parent71745d48cf310fb853d134b4fae5039ea15e9631 (diff)
Add plan serialization
Diffstat (limited to 'scripts')
-rw-r--r--scripts/lib.js30
-rw-r--r--scripts/main.js46
2 files changed, 71 insertions, 5 deletions
diff --git a/scripts/lib.js b/scripts/lib.js
index 4e4850e..9a5e957 100644
--- a/scripts/lib.js
+++ b/scripts/lib.js
@@ -2,9 +2,39 @@ const h = preact.h;
2const CELL_SIZE = 32; 2const CELL_SIZE = 32;
3const GRID_SIZE_X = 21; 3const GRID_SIZE_X = 21;
4const GRID_SIZE_Y = 21; 4const GRID_SIZE_Y = 21;
5const FLOOR_COUNT = 15;
5 6
6const ASSET_SVG_PROPS = {"width": CELL_SIZE, "height": CELL_SIZE, 7const ASSET_SVG_PROPS = {"width": CELL_SIZE, "height": CELL_SIZE,
7 "version": "1.1"}; 8 "version": "1.1"};
8const ASSET_WHITE_BG = h("rect", {"x": 0, "y": 0, "width": CELL_SIZE, 9const ASSET_WHITE_BG = h("rect", {"x": 0, "y": 0, "width": CELL_SIZE,
9 "height": CELL_SIZE, "fill": "white", 10 "height": CELL_SIZE, "fill": "white",
10 "stroke-width": 0 }); 11 "stroke-width": 0 });
12
13// Bytes is a Uint8Array
14async function compressBytes(bytes)
15{
16 let blob = new Blob([bytes]);
17 const ds = new CompressionStream("deflate");
18 const compressed = blob.stream().pipeThrough(ds);
19 let compressed_blob = await new Response(compressed).blob();
20 const reader = new FileReader();
21 return new Promise((resolve, _) => {
22 reader.onloadend = (event) => {
23 const result = event.target.result;
24 resolve(result.replace(/^data:.+;base64,/, '')
25 .replaceAll("/", "-").replaceAll("+", "_"));
26 }
27 reader.readAsDataURL(compressed_blob);
28 });
29}
30
31// Decompress into Uint8Array
32function decompressBytes(s)
33{
34 const decoded = window.atob(s.replaceAll("_", "+").replaceAll("-", "/"));
35 const decoded_array = Uint8Array.from(decoded, c => c.charCodeAt(0));
36 let blob = new Blob([decoded_array]);
37 const cs = new DecompressionStream("deflate");
38 const decompressed = blob.stream().pipeThrough(cs);
39 return new Response(decompressed).bytes();
40}
diff --git a/scripts/main.js b/scripts/main.js
index 01301a1..7347f37 100644
--- a/scripts/main.js
+++ b/scripts/main.js
@@ -3,8 +3,15 @@ const DEFAULT_APP_STATE = {
3 plan: new Array(GRID_SIZE_X * GRID_SIZE_Y).fill(0), 3 plan: new Array(GRID_SIZE_X * GRID_SIZE_Y).fill(0),
4 // mouse_state can be “normal”, or “dnd”. 4 // mouse_state can be “normal”, or “dnd”.
5 mouse_state: "normal", 5 mouse_state: "normal",
6 plan_code: "",
6}; 7};
7 8
9async function serializePlan(app_state)
10{
11 let rest = new Array(GRID_SIZE_X * GRID_SIZE_Y * (FLOOR_COUNT - 1)).fill(0);
12 return compressBytes(Uint8Array.from(app_state.plan.concat(rest)));
13}
14
8function genGrid(x_count, y_count, cell_size) 15function genGrid(x_count, y_count, cell_size)
9{ 16{
10 let lines = []; 17 let lines = [];
@@ -62,7 +69,6 @@ function PlanGridView({content, mouse_state})
62 } 69 }
63 70
64 let hilight_box = null; 71 let hilight_box = null;
65 console.debug(mouse_coord);
66 72
67 if(mouse_coord !== null && mouse_state != "dnd") 73 if(mouse_coord !== null && mouse_state != "dnd")
68 { 74 {
@@ -130,7 +136,13 @@ function App({initial_state})
130 let new_state = structuredClone(state); 136 let new_state = structuredClone(state);
131 new_state.plan[cell_index] = asset_index; 137 new_state.plan[cell_index] = asset_index;
132 new_state.mouse_state = "normal"; 138 new_state.mouse_state = "normal";
133 setState(new_state); 139 serializePlan(new_state).then((s) => {
140 new_state.plan_code = s;
141 setState(new_state);
142 const url = new URL(location);
143 url.searchParams.set("plan", s);
144 window.history.pushState({}, "", url);
145 });
134 } 146 }
135 147
136 function onClickSaveImg() 148 function onClickSaveImg()
@@ -157,13 +169,37 @@ function App({initial_state})
157 } 169 }
158 170
159 return h("div", {}, 171 return h("div", {},
172 h("h2", {}, `Floor ${state.floor}`),
160 h(AssetsView, {on_drag_begin: onDragAssetBegin, 173 h(AssetsView, {on_drag_begin: onDragAssetBegin,
161 on_drag_end: onDragAssetEnd}), 174 on_drag_end: onDragAssetEnd}),
162 h(PlanView, {plan: state.plan, mouse_state: state.mouse_state}), 175 h(PlanView, {plan: state.plan, mouse_state: state.mouse_state}),
163 h("div", {}, 176 h("div", {},
164 h("a", {"href": "javascript:void(0);", onclick: onClickSaveImg}, 177 h("a", {"href": "javascript:void(0);", onclick: onClickSaveImg},
165 "Open Image!"))); 178 "Open Image!")),
179 h("div", {},
180 h("textarea", {readonly: true}, state.plan_code)),
181 );
166} 182}
167 183
168preact.render(h(App, {initial_state: DEFAULT_APP_STATE}), 184function render(app_state)
169 document.getElementById("Plan")); 185{
186 preact.render(h(App, {initial_state: app_state}),
187 document.getElementById("Plan"));
188}
189
190const paramsString = window.location.search;
191const searchParams = new URLSearchParams(paramsString);
192const encoded_plan = searchParams.get("plan");
193if(encoded_plan === null)
194{
195 render(DEFAULT_APP_STATE);
196}
197else
198{
199 decompressBytes(encoded_plan).then(a => {
200 let state = structuredClone(DEFAULT_APP_STATE);
201 state.plan = Array.from(a.subarray(0, GRID_SIZE_X * GRID_SIZE_Y));
202 state.plan_code = encoded_plan;
203 render(state);
204 });
205}