Back to Squad Table
SOURCE / PINNED RELEASE

Made of little things.

Squad Table

Release
d36201f8ed4f…
Author-recorded commit
7b9bb681d6ba…
License
LICENSE
Author’s source reference
nostr://npub1sj2vketepqw9v6fmge5fdzuj7atj9sv6qv2jss05n2z6rl64lcfqumdsnt/wss%3A%2F%2Fgit.napplet.soy%2F/n-c043b0c0f1d

Archive hash verified: 8c0604b31f00cc5a…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
    <title>Squad Table</title>
  </head>
  <body>
    <main id="app" class="app">
      <section id="lobby" class="lobby" aria-label="Tables">
        <div class="lobby-inner">
          <div class="lobby-intro">
            <p class="lobby-kicker">Tactical hex table</p>
            <p class="lobby-lede">
              Set up a squad-level scenario, move counters and roll dice with an opponent: live, or a few moves a day. You bring the
              rulebook; the table keeps the map, the dice and the log.
            </p>
          </div>

          <div class="lobby-grid">
            <div id="newForm" class="card card-new">
              <h2>New table</h2>
              <label class="field">
                <span>Scenario</span>
                <select id="newScenario"></select>
              </label>
              <p id="newBrief" class="hint"></p>
              <fieldset class="field">
                <legend>Your side</legend>
                <div id="newSide" class="seg" role="radiogroup"></div>
              </fieldset>
              <div class="row">
                <button id="createOnline" type="button" class="btn primary">Create online table</button>
                <button id="createLocal" type="button" class="btn">Hotseat on this device</button>
              </div>
              <p id="onlineNote" class="hint" aria-live="polite"></p>
            </div>

            <div id="joinForm" class="card">
              <h2>Join with a code</h2>
              <label class="field">
                <span>Table code from your opponent</span>
                <input id="joinCode" type="text" inputmode="url" spellcheck="false" placeholder="ASL1.…" />
              </label>
              <div class="row">
                <button id="joinButton" type="button" class="btn primary">Open table</button>
              </div>
              <p id="joinNote" class="hint" aria-live="polite"></p>
            </div>

            <section class="card card-list" aria-labelledby="myTablesTitle">
              <h2 id="myTablesTitle">Your tables</h2>
              <ul id="myTables" class="table-list"></ul>
              <p id="myTablesEmpty" class="hint">No tables yet. Create one or join with a code.</p>
            </section>
          </div>
          <p id="identityNote" class="hint identity-note" aria-live="polite"></p>
        </div>
      </section>

      <section id="table" class="table" aria-label="Game table" hidden>
        <header class="hud" role="toolbar" aria-label="Game controls">
          <button id="backButton" class="tool" type="button" title="Back to tables" aria-label="Back to tables">
            <svg viewBox="0 0 20 20" aria-hidden="true"><path d="M12.5 4 6.5 10l6 6" /></svg>
          </button>
          <div class="phase" aria-live="polite">
            <div class="phase-text">
              <span id="turnLabel" class="turn">T1</span>
              <span id="activeChip" class="chip"></span>
              <span id="phaseLabel" class="phase-name"></span>
            </div>
            <button id="endPhase" class="tool end-phase" type="button" title="End this phase. You can't go back.">End</button>
          </div>
          <div class="hud-tools">
            <button id="rollDR" class="tool dice" type="button" title="Roll two dice (DR)">DR</button>
            <button id="rolldr" class="tool dice" type="button" title="Roll one die (dr)">dr</button>
          </div>
          <div class="hud-right">
            <span id="seatChip" class="seat"></span>
            <span id="syncDot" class="sync" role="status"></span>
            <button id="panelButton" class="tool" type="button" aria-expanded="true" aria-controls="panel" title="Show panel">
              <svg viewBox="0 0 20 20" aria-hidden="true"><path d="M3 5h14M3 10h14M3 15h14" /></svg>
            </button>
          </div>
        </header>

        <div class="board-wrap">
          <div id="boardHost" class="board-host"></div>
          <div class="board-tools" role="group" aria-label="Map tools">
            <button id="losButton" class="tool" type="button" aria-pressed="false" title="Check line of sight">
              <svg viewBox="0 0 20 20" aria-hidden="true"><circle cx="4.5" cy="15.5" r="2" /><circle cx="15.5" cy="4.5" r="2" /><path d="M6 14 14 6" stroke-dasharray="2 2" /></svg>
              <span class="tool-label">LOS</span>
            </button>
            <button id="undoButton" class="tool" type="button" title="Undo your last change">
              <svg viewBox="0 0 20 20" aria-hidden="true"><path d="M7 5 3.5 8.5 7 12" /><path d="M4 8.5h7.5a4.5 4.5 0 0 1 0 9H9" /></svg>
              <span class="tool-label">Undo</span>
            </button>
            <button id="soundButton" class="tool" type="button" aria-pressed="false" title="Mute battle sounds">
              <svg viewBox="0 0 20 20" aria-hidden="true"><path d="M3 8h3l4-3v10l-4-3H3z" /><path class="wave" d="M13 7.5a3.5 3.5 0 0 1 0 5M15.5 5.5a6.5 6.5 0 0 1 0 9" /></svg>
              <span class="tool-label">Sound</span>
            </button>
          </div>
          <div class="zoom" role="group" aria-label="Zoom">
            <button id="zoomIn" class="tool" type="button" aria-label="Zoom in">+</button>
            <button id="zoomOut" class="tool" type="button" aria-label="Zoom out">−</button>
            <button id="zoomFit" class="tool" type="button" aria-label="Fit map">
              <svg viewBox="0 0 20 20" aria-hidden="true"><path d="M3 7V3h4M13 3h4v4M17 13v4h-4M7 17H3v-4" /></svg>
            </button>
          </div>
          <div id="modeBar" class="mode-bar" hidden>
            <span id="modeText"></span>
            <div id="modeActions" class="mode-actions"></div>
          </div>
          <div id="toast" class="toast" role="status" aria-live="polite" hidden></div>
          <div id="strip" class="strip" hidden>
            <div id="stripHex" class="strip-hex"></div>
            <div id="stripCounters" class="strip-counters"></div>
            <div class="strip-actions">
              <button id="stripMove" class="btn small primary" type="button">Move</button>
              <button id="stripMore" class="btn small" type="button">Details</button>
            </div>
          </div>
          <div id="diceTray" class="dice-tray" aria-live="polite" hidden></div>
        </div>

        <div id="tooSmall" class="too-small" role="status">
          <p><b>Make the frame larger to play.</b></p>
          <p id="tooSmallState"></p>
        </div>

        <aside id="panel" class="panel" aria-label="Table panel">
          <div class="tabs" role="tablist">
            <button class="tab" role="tab" data-tab="actions" aria-selected="true">Actions</button>
            <button class="tab" role="tab" data-tab="stack" aria-selected="false">Stack</button>
            <button class="tab" role="tab" data-tab="tray" aria-selected="false">Counters</button>
            <button class="tab" role="tab" data-tab="log" aria-selected="false">Log<span id="logBadge" class="badge-dot" hidden></span></button>
            <button class="tab" role="tab" data-tab="info" aria-selected="false">Table</button>
            <button id="panelClose" class="tool slim panel-close" type="button" aria-label="Close panel">
              <svg viewBox="0 0 20 20" aria-hidden="true"><path d="m5 5 10 10M15 5 5 15" /></svg>
            </button>
          </div>
          <div class="tab-body" data-body="actions" role="tabpanel">
            <div id="actionsPanel"></div>
          </div>
          <div class="tab-body" data-body="stack" role="tabpanel" hidden>
            <div id="stackPanel"></div>
          </div>
          <div class="tab-body" data-body="tray" role="tabpanel" hidden>
            <div id="trayPanel"></div>
          </div>
          <div class="tab-body tab-log" data-body="log" role="tabpanel" hidden>
            <ol id="logList" class="log"></ol>
            <div id="chatForm" class="chat">
              <input id="rollWhy" type="text" maxlength="60" placeholder="Roll for… (optional)" aria-label="What the next roll is for" />
              <div class="chat-row">
                <input id="chatInput" type="text" maxlength="280" placeholder="Message" aria-label="Message" />
                <button id="chatSend" class="btn small primary" type="button">Send</button>
              </div>
            </div>
          </div>
          <div class="tab-body" data-body="info" role="tabpanel" hidden>
            <div id="infoPanel"></div>
          </div>
        </aside>
      </section>
    </main>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>