Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions arena_console.html
Original file line number Diff line number Diff line change
Expand Up @@ -1521,7 +1521,7 @@
</div>
<div id="log"></div>
</div>
<footer id="footer">Arena Console v8 | 2026-07-02 01:06 ET</footer>
<footer id="footer">Arena Console v9 | 2026-07-05 17:10 ET</footer>
</div>

<!-- Paste-a-frame modal -->
Expand Down Expand Up @@ -4014,22 +4014,25 @@
nameSel.value = entries[0].name;
tpPat.value = entries[0].idx;
lockPat(false);
// Cheap metadata preview via GET_PATTERN_INFO (0x88) — a small
// framed read, NOT the 0x84 bulk download that stalled the link
// and had to be disabled here. (Full-frame thumbnail is deferred;
// see renderSdPreview / the .pat-meta-thumb slot.)
renderSdMetaCard(entries[0].idx);
// Full-frame thumbnail via 0x84 (renderSdPreview) — re-enabled now
// that the controller's GET_PATTERN_FILE handler is chunked/
// non-blocking with a bounded stall recovery (issue #16), instead
// of the old single-call handler that could wedge the link on a
// stalled host. Requires that firmware fix to be flashed — an
// older controller can still wedge the link here.
renderSdPreview(entries[0].idx);
};

nameSel.addEventListener('change', () => {
const name = nameSel.value;
const sdIdx = sdNameToIndex[name];
if (sdIdx != null) {
// SD card mode: set the pattern index directly + show cheap
// 0x88 metadata (renderSdMetaCard). The 0x84 bulk preview stays
// disabled — it stalled the link.
// SD card mode: set the pattern index directly + show the
// full-frame thumbnail (renderSdPreview, 0x84 — see the
// __populateFromSdCard comment above for why this is safe
// again).
tpPat.value = sdIdx;
renderSdMetaCard(sdIdx);
renderSdPreview(sdIdx);
} else if (activeManifest) {
// Manifest mode: resolve name → index + preview via manifest.
applyPick(name);
Expand Down
43 changes: 29 additions & 14 deletions arena_studio.html
Original file line number Diff line number Diff line change
Expand Up @@ -2917,7 +2917,7 @@ <h2 id="modalTitle">Import error</h2>
</div>

<footer id="footer">
<span class="foot-left">Arena Studio v0.66 | 2026-07-10 20:52 ET · <a href="https://github.com/reiserlab/webDisplayTools" target="_blank" rel="noopener" title="webDisplayTools on GitHub — source, issues, and release notes (docs/development/arena-studio-release-notes.md)">GitHub</a></span>
<span class="foot-left">Arena Studio v0.67 | 2026-07-21 16:25 ET · <a href="https://github.com/reiserlab/webDisplayTools" target="_blank" rel="noopener" title="webDisplayTools on GitHub — source, issues, and release notes (docs/development/arena-studio-release-notes.md)">GitHub</a></span>
<!-- Course-repo quick-links: open protocols / logs / patterns in a new tab.
Hrefs built from the configured repo + bench id (updateGhQuickLinks). -->
<span id="ghQuickLinks" title="Open the course repo on GitHub (new tab)">
Expand Down Expand Up @@ -6431,19 +6431,32 @@ <h2 id="modalTitle">Import error</h2>
// LIVE SD LISTING whenever one exists — the card is the source of truth for
// what index a name has (uploads/deletes re-order it). Falls back to the
// built-in web manifest when never connected.
// · PREVIEW BYTES can only come from the web side (built-in library, or bytes
// seen this session via Upload ▾ / an opened course-repo protocol) — the
// thumbnail is best-effort, matched by filename; an SD pattern whose bytes
// were never seen this session simply has no thumb.
// · PREVIEW BYTES prefer the web side (built-in library, or bytes seen this
// session via Upload ▾ / an opened course-repo protocol), matched by
// filename; when connected, any other SD pattern falls back to a live 0x84
// fetch (patByteSource). The thumbnail stays best-effort either way.
const PS = window.PatternSet;
const PP = window.PatPreview;
const BUILTIN_BASE = './patterns/g6_2x10/';
Studio.patternSet = null;
const webPreviewBySdName = new Map(); // filename → () => Promise<ArrayBuffer>
const repoSharedPreviewKeys = new Set(); // sources currently wired from course-repo patterns/
function patByteSource(sd) {
const f = webPreviewBySdName.get(sd);
return f ? f() : Promise.reject(new Error('no local copy of ' + sd));
// In-memory sources first (logical-name tolerant via webBytesForName). `idx`
// (1-based SD index) is the fallback path: a pattern already on the card
// from a prior session has no cached source above, so fetch it live via 0x84
// — the controller's GET_PATTERN_FILE handler is chunked/non-blocking with a
// bounded stall recovery (issue #16), so this is safe on fixed firmware (an
// older controller can still wedge the link here). Same call the SD table's
// Download button already makes.
function patByteSource(sd, idx) {
const f = Studio.webBytesForName(sd);
if (f) return f();
if (!Studio.session || !Studio.session.connected || idx == null) {
return Promise.reject(new Error('no local copy of ' + sd));
}
return Studio.session
.sendBulkRead(Wire.encodeGetPatternFile(idx), { timeoutMs: 60000 })
.then((u8) => u8.buffer);
}
let patPreviewCache = {};

Expand Down Expand Up @@ -6695,14 +6708,15 @@ <h2 id="modalTitle">Import error</h2>
// because it stores files pre-prefixed. Colocated sources are re-wired after
// a reload by initFromUrl → rewireRepoPatternPreviews.
const src = Studio.webBytesForName(p.sd_name);
host.title = src
const canFetchLive = Studio.session && Studio.session.connected && p.index != null;
host.title = src || canFetchLive
? 'Pattern preview (multi-frame patterns animate automatically)'
: 'No preview — this pattern is on the SD card but its bytes were not seen this session (upload it via Upload ▾, or open its course-repo protocol, for a thumbnail)';
: 'No preview — connect to the controller for a live thumbnail, or upload the set / open its course-repo protocol';
try {
let sampled = patPreviewCache[name];
if (!sampled) {
if (!src) return;
const ab = await src();
if (!src && !canFetchLive) return;
const ab = await patByteSource(p.sd_name, p.index);
const parsed = PatParser.parsePatFile(ab);
sampled = PP.samplePreviewFrames(parsed, arenaForConfig(m.arenaConfig), {
renderIcon: generatePatternIcon,
Expand Down Expand Up @@ -6842,8 +6856,9 @@ <h2 id="modalTitle">Import error</h2>
const o = sel.selectedOptions && sel.selectedOptions[0];
applyPatPick(sel.value, o && o.dataset.idx != null ? Number(o.dataset.idx) : null);
});
// (Pattern-set previews now come from device memory → Upload ▾, which
// registers uploaded bytes as preview sources — see Studio.registerUploadedPreviews.)
// (Pattern-set previews prefer in-memory bytes — Upload ▾ registers those,
// see Studio.registerUploadedPreviews — and fall back to a live 0x84 fetch
// via patByteSource for any other connected SD-card pattern.)
}

// ---- stream frame (host-streamed test figures) — ported from arena_console ----
Expand Down
11 changes: 11 additions & 0 deletions docs/development/arena-studio-release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ The Studio's footer used to carry the full changelog inline; it now shows one li
history lives here. Newest first. (Per-session engineering detail stays in
`arena-studio-handover.md` and the design docs — this file is the user-facing what-changed list.)

## v0.67 — 2026-07-21 · Console picker previews any connected SD-card pattern

- **The Console pattern picker now shows a thumbnail for patterns already on
the SD card from a prior session.** When a pattern's bytes are not in memory
(this-session upload, built-in library, loaded course-repo protocol, or the
linked repo's shared `patterns/`), the preview falls back to a live
`GET_PATTERN_FILE` (0x84) fetch, the same call the SD table's Download
button makes. Safe on firmware with the chunked, non-blocking 0x84 handler
(reiserlab/LED-Display_G6_Firmware_Arena#16); an older controller can still
wedge the link on this fetch.

## v0.66 — 2026-07-10 · Crisp edges applied consistently across all frames

- **The crisper-border treatment now applies throughout, not just the main
Expand Down
2 changes: 1 addition & 1 deletion tests/test-arena-studio-alt.js
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ check(
alt.includes("const ALT_BUILD_STAMP = '2026-07-13 18:55 ET';") &&
alt.includes("stampNode.nodeValue = ALT_TOOL_VERSION + ' | ' + ALT_BUILD_STAMP + ' · ';") &&
alt.includes('Studio.TOOL_VERSION = ALT_TOOL_VERSION;') &&
studio.includes('Arena Studio v0.66 | 2026-07-10 20:52 ET')
studio.includes('Arena Studio v0.67 | 2026-07-21 16:25 ET')
);

console.log('=== scoped presentation ===');
Expand Down
Loading