.chunk.json per phrase — what `griff manifest`
+// ingests (it collects individual .chunk.json files, one ChunkMeta each).
+// Staggered so the browser does not coalesce the rapid downloads.
+function downloadEachPhrase() {
if (splitChunks.length === 0) { capMsg('split into phrases first', true); return; }
- // Stagger the saves so the browser does not coalesce the rapid clicks.
splitChunks.forEach((ch, i) =>
setTimeout(() => saveBlob(`${ch.id}.chunk.json`, ch.chunk), i * 120));
- capMsg(`saving ${splitChunks.length} phrase chunk(s)…`, false);
+ capMsg(`saving ${splitChunks.length} .chunk.json file(s) — for griff manifest`, false);
+}
+
+// Bundles every phrase chunk into a single JSON array — one file for sharing or
+// review instead of N. NOTE: `griff manifest` reads individual .chunk.json files,
+// not this array, so use "each phrase" for the corpus pipeline (#77).
+function downloadBundle() {
+ if (splitChunks.length === 0) { capMsg('split into phrases first', true); return; }
+ const base = els.capId.value.trim() || (splitChunks[0].id || 'phrases').replace(/_p\d+$/, '');
+ const chunks = splitChunks.map((ch) => {
+ try { return JSON.parse(ch.chunk); } catch (_) { return { id: ch.id, error: 'unparseable chunk' }; }
+ });
+ saveBlob(`${base}.chunks.json`, JSON.stringify(chunks, null, 2));
+ capMsg(`saved ${base}.chunks.json · ${chunks.length} phrase(s) in one file — for sharing/review`, false);
}
// ---- drawing ----
@@ -520,7 +549,8 @@ function bind() {
els.splitPlay.addEventListener('click', () => { renderPhrase(); play(); });
els.splitStop.addEventListener('click', stop);
els.splitDownload.addEventListener('click', downloadPhrase);
- els.splitDownloadAll.addEventListener('click', downloadAllPhrases);
+ els.splitDownloadEach.addEventListener('click', downloadEachPhrase);
+ els.splitDownloadAll.addEventListener('click', downloadBundle);
els.dbgCopy.addEventListener('click', copyDebug);
els.dbgClear.addEventListener('click', clearDebug);
window.addEventListener('resize', () => draw());
diff --git a/web/static/index.html b/web/static/index.html
index 9ce97f47..ba2ecf27 100644
--- a/web/static/index.html
+++ b/web/static/index.html
@@ -169,14 +169,21 @@ Capture · chunk.json
+
-
+
+
+
+ each phrase → one .chunk.json per phrase for
+ griff manifest; all (one JSON) → a single
+ .chunks.json array for sharing/review.
+
diff --git a/web/static/style.css b/web/static/style.css
index c42a5716..2228cd53 100644
--- a/web/static/style.css
+++ b/web/static/style.css
@@ -150,6 +150,11 @@ h2 { font-size: 1.05rem; margin: 0 0 0.1rem; }
height: 20px;
accent-color: var(--accent);
}
+/* Read-only per-phrase tags (detected, not split input): ticked, dimmed chips
+ tinted with part-A blue to set them apart from the green capture inputs. */
+.tagsread { margin: 4px 0 2px; }
+.tagsread label { color: var(--muted); }
+.tagsread input[type="checkbox"] { accent-color: var(--a); }
label.row { flex-flow: row; display: flex; align-items: center; gap: 10px; color: var(--ink); }
label.row input[type="checkbox"] { width: 22px; height: 22px; accent-color: var(--accent); }
#capBounds { display: block; margin: 2px; min-height: 1em; }