Web-demo styling
This commit is contained in:
+42
-2
@@ -86,11 +86,18 @@ body {
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-columns: 22rem minmax(0, 1fr);
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-template-areas:
|
||||
"col-top col-viewer"
|
||||
"col-info col-viewer";
|
||||
gap: 2rem;
|
||||
padding: 2rem 1rem 1rem;
|
||||
max-width: 78rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#col-top { grid-area: col-top; }
|
||||
#col-viewer { grid-area: col-viewer; }
|
||||
#col-info { grid-area: col-info; }
|
||||
|
||||
/* Control cards, form fields, and action buttons. */
|
||||
.controls {
|
||||
@@ -181,6 +188,32 @@ button.secondary:hover {
|
||||
gap: 0.55rem;
|
||||
}
|
||||
|
||||
/* Collapsible cards: clicking the h2 toggles visibility of card body.
|
||||
Only cards with class .collapsible get this behavior. */
|
||||
.card.collapsible > h2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.card.collapsible > h2::after {
|
||||
content: "▾";
|
||||
font-size: 2rem;
|
||||
color: var(--muted);
|
||||
margin-left: auto;
|
||||
padding-left: 0.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.card.collapsible.collapsed > h2::after {
|
||||
content: "▸";
|
||||
}
|
||||
.card.collapsible > h2:hover::after {
|
||||
color: var(--off-fg);
|
||||
}
|
||||
.card.collapsible.collapsed > *:not(h2) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.note p, .small {
|
||||
color: var(--off-fg);
|
||||
font-size: 0.82rem;
|
||||
@@ -284,9 +317,16 @@ button.secondary:hover {
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
|
||||
/* Stack controls above the board on narrower screens. */
|
||||
/* On narrower screens, stack controls → viewer → info. */
|
||||
@media (max-width: 900px) {
|
||||
.layout { grid-template-columns: 1fr; }
|
||||
.layout {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto auto auto;
|
||||
grid-template-areas:
|
||||
"col-top"
|
||||
"col-viewer"
|
||||
"col-info";
|
||||
}
|
||||
.site-header { flex-direction: column; }
|
||||
}
|
||||
|
||||
|
||||
@@ -690,6 +690,16 @@ async function predict() {
|
||||
}
|
||||
|
||||
|
||||
/** Initialize collapsible cards: clicking the h2 toggles the .collapsed class. */
|
||||
function initCollapsibleCards() {
|
||||
document.querySelectorAll(".card.collapsible > h2").forEach((heading) => {
|
||||
const card = heading.parentElement;
|
||||
heading.addEventListener("click", () => {
|
||||
card.classList.toggle("collapsed");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Attach a listener only when optional markup exists. */
|
||||
function addListenerIfPresent(id, eventName, handler) {
|
||||
const element = $(id);
|
||||
@@ -717,6 +727,8 @@ async function init() {
|
||||
await setBoardBackground("tb2");
|
||||
syncAngleSelectors(40);
|
||||
|
||||
initCollapsibleCards();
|
||||
|
||||
addListenerIfPresent("gen-board", "change", async (e) => {
|
||||
syncBoardSelectors(e.target.value);
|
||||
await setBoardBackground(e.target.value);
|
||||
|
||||
+61
-80
@@ -4,10 +4,9 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>ClimbingBoardGPT</title>
|
||||
<link rel="stylesheet" href="/static/app.css?v=19" />
|
||||
<link rel="stylesheet" href="/static/app.css?v=20" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- Top-level status: the app script replaces this with model readiness. -->
|
||||
<header class="site-header">
|
||||
<div>
|
||||
<p class="eyebrow">ClimbingBoardGPT</p>
|
||||
@@ -18,9 +17,9 @@
|
||||
</header>
|
||||
|
||||
<main class="layout">
|
||||
<!-- Left column: generation controls, prediction controls, and project notes. -->
|
||||
<section class="controls">
|
||||
<div class="card">
|
||||
<!-- Left column, row 1: interactive controls (collapsible). -->
|
||||
<section class="controls" id="col-top">
|
||||
<div class="card collapsible" id="card-gen">
|
||||
<h2>Generate a climb</h2>
|
||||
<label>Board
|
||||
<select id="gen-board">
|
||||
@@ -41,7 +40,7 @@
|
||||
<button id="generate-btn">Generate</button>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card collapsible" id="card-predict">
|
||||
<h2>Predict grade</h2>
|
||||
<label>Board
|
||||
<select id="pred-board">
|
||||
@@ -58,7 +57,7 @@
|
||||
<button id="predict-btn" disabled>Predict pasted / clicked climb</button>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card collapsible" id="card-builder">
|
||||
<h2>Click holds to build a climb</h2>
|
||||
<label>Role for next clicked hold
|
||||
<select id="click-role">
|
||||
@@ -78,80 +77,10 @@
|
||||
</p>
|
||||
<ul id="builder-list" class="builder-list"></ul>
|
||||
</div>
|
||||
|
||||
<div class="card explain" id="explain-card">
|
||||
<h2>What the controls mean</h2>
|
||||
<dl>
|
||||
<dt>Temperature</dt>
|
||||
<dd>Sampling randomness. Lower values are more conservative; higher values are more exploratory.</dd>
|
||||
|
||||
<dt>Target V-grade</dt>
|
||||
<dd>The grade token given to the generator. The generated route is also checked by the grade predictor.</dd>
|
||||
|
||||
<dt>Known climb</dt>
|
||||
<dd>An exact match against the tokenized dataset: same board, same angle, and same hold-role set.</dd>
|
||||
|
||||
<dt>Validity</dt>
|
||||
<dd>A structural check: enough holds, no duplicate placements, at least one start, and at least one finish.</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="card note" id="model-disclaimer-card">
|
||||
<h2>Research demo caveat</h2>
|
||||
<p>
|
||||
This is an experimental model demo. Generated climbs and predicted grades may be wrong, especially at rare grades or sparse board/angle combinations.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card explain">
|
||||
<h2>How this works</h2>
|
||||
<p>
|
||||
Routes are converted into tokens such as
|
||||
<code><BOARD_TB2></code>, <code><ANGLE_40></code>,
|
||||
and <code><TB2_p652_start></code>.
|
||||
</p>
|
||||
<p>
|
||||
The generator samples a token sequence. The grade predictor removes the grade token and estimates difficulty from the board, angle, and hold-role tokens.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card note">
|
||||
<h2>Links</h2>
|
||||
<ul class="link-list">
|
||||
<li><a href="https://pawelsarkowicz.xyz" target="_blank" rel="noreferrer">pawelsarkowicz.xyz</a></li>
|
||||
<li><a href="https://github.com/psark007/ClimbingBoardGPT" target="_blank" rel="noreferrer">ClimbingBoardGPT repo</a></li>
|
||||
<li><a href="https://github.com/psark007/ClimbingBoardGPT/blob/main/LICENSE" target="_blank" rel="noreferrer">License</a></li>
|
||||
<li><a href="https://github.com/psark007/Tension-Board-2-Analysis" target="_blank" rel="noreferrer">Tension Board 2 Analysis repo</a></li>
|
||||
<li><a href="https://github.com/psark007/Kilter-Board-Analysis" target="_blank" rel="noreferrer">Kilter Board Analysis repo</a></li>
|
||||
<li><a href="https://tensionclimbing.com/products/tension-board-2" target="_blank" rel="noreferrer">Tension Board 2</a></li>
|
||||
<li><a href="https://settercloset.com/collections/kilter-board" target="_blank" rel="noreferrer">Kilter Board</a></li>
|
||||
<li><a href="https://github.com/karpathy/nanoGPT" target="_blank" rel="noreferrer">nanoGPT</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card note" id="angle-scope-card">
|
||||
<h2>Angle scope</h2>
|
||||
<p>
|
||||
The physical boards can be used at steeper angles than this demo exposes. This model snapshot is intentionally restricted to the angle range used in training/evaluation: TB2 up to 50° and Kilter up to 55°.
|
||||
</p>
|
||||
<p>
|
||||
The restriction avoids asking the models to extrapolate into sparse, noisier high-angle data where grades and route distributions are less reliable.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card note" id="data-acknowledgement-card">
|
||||
<h2>Data acknowledgement</h2>
|
||||
<p>
|
||||
Board layouts, hold metadata, and route data are derived from Tension Board 2 and Kilter Board datasets.
|
||||
This project is unaffiliated with Tension Climbing or Kilter.
|
||||
The route generator is inspired by Andrej Karpathy's
|
||||
<a href="https://github.com/karpathy/nanoGPT" target="_blank" rel="noreferrer">nanoGPT</a>.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Right column: generated/predicted route result and SVG board overlay. -->
|
||||
<section class="viewer">
|
||||
<section class="viewer" id="col-viewer">
|
||||
<div class="result-card">
|
||||
<div class="result-header">
|
||||
<h2 id="result-title">Choose a board and run a request</h2>
|
||||
@@ -175,9 +104,61 @@
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Left column, row 2: info cards (below board on mobile, left column on desktop). -->
|
||||
<section class="controls" id="col-info">
|
||||
<div class="card explain">
|
||||
<h2>What the controls mean</h2>
|
||||
<dl>
|
||||
<dt>Temperature</dt>
|
||||
<dd>Sampling randomness. Lower values are more conservative; higher values are more exploratory.</dd>
|
||||
<dt>Target V-grade</dt>
|
||||
<dd>The grade token given to the generator. The generated route is also checked by the grade predictor.</dd>
|
||||
<dt>Known climb</dt>
|
||||
<dd>An exact match against the tokenized dataset: same board, same angle, and same hold-role set.</dd>
|
||||
<dt>Validity</dt>
|
||||
<dd>A structural check: enough holds, no duplicate placements, at least one start, and at least one finish.</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="card note" id="model-disclaimer-card">
|
||||
<h2>Research demo caveat</h2>
|
||||
<p>This is an experimental model demo. Generated climbs and predicted grades may be wrong, especially at rare grades or sparse board/angle combinations.</p>
|
||||
</div>
|
||||
|
||||
<div class="card explain">
|
||||
<h2>How this works</h2>
|
||||
<p>Routes are converted into tokens such as <code><BOARD_TB2></code>, <code><ANGLE_40></code>, and <code><TB2_p652_start></code>.</p>
|
||||
<p>The generator samples a token sequence. The grade predictor removes the grade token and estimates difficulty from the board, angle, and hold-role tokens.</p>
|
||||
</div>
|
||||
|
||||
<div class="card note">
|
||||
<h2>Links</h2>
|
||||
<ul class="link-list">
|
||||
<li><a href="https://pawelsarkowicz.xyz" target="_blank" rel="noreferrer">pawelsarkowicz.xyz</a></li>
|
||||
<li><a href="https://github.com/psark007/ClimbingBoardGPT" target="_blank" rel="noreferrer">ClimbingBoardGPT repo</a></li>
|
||||
<li><a href="https://github.com/psark007/ClimbingBoardGPT/blob/main/LICENSE" target="_blank" rel="noreferrer">License</a></li>
|
||||
<li><a href="https://github.com/psark007/Tension-Board-2-Analysis" target="_blank" rel="noreferrer">Tension Board 2 Analysis repo</a></li>
|
||||
<li><a href="https://github.com/psark007/Kilter-Board-Analysis" target="_blank" rel="noreferrer">Kilter Board Analysis repo</a></li>
|
||||
<li><a href="https://tensionclimbing.com/products/tension-board-2" target="_blank" rel="noreferrer">Tension Board 2</a></li>
|
||||
<li><a href="https://settercloset.com/collections/kilter-board" target="_blank" rel="noreferrer">Kilter Board</a></li>
|
||||
<li><a href="https://github.com/karpathy/nanoGPT" target="_blank" rel="noreferrer">nanoGPT</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card note" id="angle-scope-card">
|
||||
<h2>Angle scope</h2>
|
||||
<p>The physical boards can be used at steeper angles than this demo exposes. This model snapshot is intentionally restricted to the angle range used in training/evaluation: TB2 up to 50° and Kilter up to 55°.</p>
|
||||
<p>The restriction avoids asking the models to extrapolate into sparse, noisier high-angle data where grades and route distributions are less reliable.</p>
|
||||
</div>
|
||||
|
||||
<div class="card note" id="data-acknowledgement-card">
|
||||
<h2>Data acknowledgement</h2>
|
||||
<p>Board layouts, hold metadata, and route data are derived from Tension Board 2 and Kilter Board datasets. This project is unaffiliated with Tension Climbing or Kilter. The route generator is inspired by Andrej Karpathy's <a href="https://github.com/karpathy/nanoGPT" target="_blank" rel="noreferrer">nanoGPT</a>.</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- External project links and license metadata. -->
|
||||
<footer class="site-footer">
|
||||
<span>© Pawel Sarkowicz</span>
|
||||
<a href="https://pawelsarkowicz.xyz" target="_blank" rel="noreferrer">pawelsarkowicz.xyz</a>
|
||||
@@ -187,4 +168,4 @@
|
||||
|
||||
<script src="/static/app.js?v=17"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
Reference in New Issue
Block a user