Data-driven parks: one heightAt(x,z) evaluated from element JSON drives the editor viewport, collision, test ride, and exported game levels. Ships with Paddo ported from bookquoy, a MODELBEAST panel (gen images, cut bg, image->3D, place farm GLBs as props), image decals + reference underlay tracing, a park design linter (docs/DESIGN_PRINCIPLES.md), THPS-style instant test ride, and a level.js codegen that drops straight into bookquoy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
105 lines
5.1 KiB
HTML
105 lines
5.1 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>SKATEMAKER PRO — park builder</title>
|
|
<style>
|
|
:root{ --bg:#14181e; --panel:#1c222b; --panel2:#232b36; --ink:#dbe4ee; --dim:#7d8a99;
|
|
--gold:#ffd93b; --gold2:#b8860b; --line:#2e3947; --danger:#ff6b6b; }
|
|
*{box-sizing:border-box}
|
|
body{margin:0;overflow:hidden;background:var(--bg);color:var(--ink);
|
|
font:13px/1.4 "SF Mono",Menlo,monospace}
|
|
#app{display:grid;height:100vh;
|
|
grid-template-rows:46px 1fr 26px;
|
|
grid-template-columns:170px 1fr 230px;
|
|
grid-template-areas:"top top top" "pal view insp" "status status status"}
|
|
#topbar{grid-area:top;display:flex;align-items:center;gap:8px;padding:0 12px;
|
|
background:var(--panel);border-bottom:1px solid var(--line)}
|
|
.logo{font-weight:bold;font-size:16px;letter-spacing:1px;margin-right:6px;color:var(--ink)}
|
|
.logo b{color:var(--gold)}
|
|
#palette{grid-area:pal;overflow-y:auto;background:var(--panel);
|
|
border-right:1px solid var(--line);padding:10px}
|
|
#viewport{grid-area:view;position:relative;min-width:0;min-height:0}
|
|
#viewport canvas{display:block}
|
|
#inspector{grid-area:insp;overflow-y:auto;background:var(--panel);
|
|
border-left:1px solid var(--line);padding:10px}
|
|
#status{grid-area:status;display:flex;align-items:center;padding:0 12px;
|
|
background:var(--panel);border-top:1px solid var(--line);color:var(--dim);
|
|
white-space:nowrap;overflow:hidden}
|
|
#drawer{display:none;position:fixed;left:178px;bottom:34px;width:400px;max-height:56vh;
|
|
flex-direction:column;gap:8px;overflow-y:auto;background:var(--panel2);
|
|
border:1px solid var(--line);border-radius:8px;padding:12px;z-index:20;
|
|
box-shadow:0 12px 40px #000a}
|
|
.ptitle{color:var(--dim);font-size:11px;letter-spacing:2px;margin:14px 0 6px}
|
|
.ptitle:first-child{margin-top:0}
|
|
.ptitle .dim{letter-spacing:0;color:#5a6673}
|
|
.pgrid{display:grid;grid-template-columns:1fr 1fr;gap:5px}
|
|
.pcol{display:flex;flex-direction:column;gap:5px}
|
|
.pbtn{background:var(--panel2);border:1px solid var(--line);color:var(--ink);
|
|
font:inherit;font-size:11px;padding:7px 6px;border-radius:5px;cursor:pointer;
|
|
letter-spacing:.5px}
|
|
.pbtn:hover{border-color:var(--gold);color:var(--gold)}
|
|
.pbtn.active{background:var(--gold);color:#1a1a1a;border-color:var(--gold);font-weight:bold}
|
|
.pbtn.accent{border-color:var(--gold2);color:var(--gold)}
|
|
.pbtn.danger{color:var(--danger)}
|
|
.pbtn.wide{width:100%}
|
|
.pbtn.test{font-weight:bold}
|
|
.snaprow{display:flex;gap:6px;align-items:center;color:var(--dim);font-size:11px;
|
|
padding:4px 2px;cursor:pointer}
|
|
.ititle{font-weight:bold;color:var(--gold);letter-spacing:1px;margin-bottom:8px}
|
|
.ihint{color:var(--dim);font-size:11px;margin:8px 0}
|
|
.irow{display:flex;align-items:center;gap:6px;margin:5px 0}
|
|
.irow>label{width:66px;flex-shrink:0;color:var(--dim);font-size:11px}
|
|
.numwrap{display:flex;align-items:center;gap:5px;flex:1;min-width:0}
|
|
input[type=range]{flex:1;min-width:0;accent-color:var(--gold)}
|
|
input.num{width:52px;background:var(--panel2);border:1px solid var(--line);
|
|
color:var(--ink);font:inherit;font-size:11px;padding:2px 4px;border-radius:4px}
|
|
input.text,select{background:var(--panel2);border:1px solid var(--line);color:var(--ink);
|
|
font:inherit;font-size:12px;padding:4px 6px;border-radius:4px;min-width:0}
|
|
input.parkname{width:150px;color:var(--gold);font-weight:bold}
|
|
input[type=color]{background:none;border:1px solid var(--line);border-radius:4px;
|
|
width:44px;height:24px;padding:1px}
|
|
.ibtns{display:flex;gap:6px;margin-top:12px}
|
|
.ibtns .pbtn{flex:1}
|
|
.checklist{display:flex;flex-direction:column;gap:5px}
|
|
.checkitem{background:var(--panel);border:1px solid var(--line);border-radius:5px;
|
|
padding:7px 9px;font-size:12px;color:#f0c674}
|
|
.checkitem.link{cursor:pointer}
|
|
.checkitem.link:hover{border-color:var(--gold)}
|
|
.checkok{color:#7bd88f;padding:8px}
|
|
.mbgen{display:flex;flex-wrap:wrap;gap:6px}
|
|
.mbgen input{flex:1 1 100%}
|
|
.mbstatus{color:var(--dim);font-size:11px;flex:1;align-self:center}
|
|
.mblist{display:flex;flex-direction:column;gap:4px;max-height:30vh;overflow-y:auto}
|
|
.mbitem{display:flex;align-items:center;gap:6px;background:var(--panel);
|
|
border:1px solid var(--line);border-radius:5px;padding:5px 8px}
|
|
.mbname{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="app">
|
|
<div id="topbar"></div>
|
|
<div id="palette"></div>
|
|
<div id="viewport"></div>
|
|
<div id="inspector"></div>
|
|
<div id="status">booting…</div>
|
|
</div>
|
|
<div id="drawer"></div>
|
|
|
|
<script type="importmap">
|
|
{"imports":{"three":"https://unpkg.com/three@0.160.0/build/three.module.js",
|
|
"three/addons/":"https://unpkg.com/three@0.160.0/examples/jsm/"}}
|
|
</script>
|
|
<script type="module">
|
|
import { initEditor } from './js/editor.js';
|
|
import { initUI } from './js/ui.js';
|
|
import { attachTestRide } from './js/testride.js';
|
|
|
|
const ed = initEditor(document.getElementById('viewport'));
|
|
attachTestRide(ed);
|
|
ed.boot().then(() => initUI(ed));
|
|
window.__ed = ed; // headless testing hook
|
|
</script>
|
|
</body>
|
|
</html>
|