transcript-viewer 0.6.1__tar.gz → 0.6.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/PKG-INFO +1 -1
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/pyproject.toml +1 -1
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/src/transcript_viewer/page.html +15 -13
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_style.py +37 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/uv.lock +1 -1
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/.coverage +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/.github/workflows/publish.yml +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/.github/workflows/test.yml +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/.gitignore +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/README.md +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/src/transcript_viewer/__init__.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/src/transcript_viewer/ai.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/src/transcript_viewer/cli.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/src/transcript_viewer/config.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/src/transcript_viewer/corpus.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/src/transcript_viewer/fetch.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/src/transcript_viewer/library.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/src/transcript_viewer/store.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/src/transcript_viewer/viewer.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/page.test.js +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_ai.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_cli.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_config.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_corpus.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_fetch.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_library.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_page.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_readme.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_store.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_stream.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_tidiness.py +0 -0
- {transcript_viewer-0.6.1 → transcript_viewer-0.6.2}/tests/test_viewer.py +0 -0
|
@@ -838,6 +838,8 @@ function pre(content) {
|
|
|
838
838
|
|
|
839
839
|
const esc=s=>String(s??"").replace(/[&<>"]/g,c=>({"&":"&","<":"<",">":">",'"':"""}[c]));
|
|
840
840
|
const num=n=>(n??0).toLocaleString();
|
|
841
|
+
/* How many sessions are in the library, said the same way everywhere. */
|
|
842
|
+
const showCount=()=>{count.textContent=`${num(INDEX.length)} session${INDEX.length===1?"":"s"}`};
|
|
841
843
|
/* One size formatter. Everything used to divide by 1048576 and say MB, so a
|
|
842
844
|
900-byte log read as "0.0 MB" and a 144 GB bucket as "147456.0 MB". Each step
|
|
843
845
|
keeps three significant figures, which is as much as anyone reads. */
|
|
@@ -870,7 +872,7 @@ function reveal(a){
|
|
|
870
872
|
async function openFiles(files){
|
|
871
873
|
if(!files||!files.length)return;
|
|
872
874
|
const names=[...files].map(f=>f.name);
|
|
873
|
-
count.textContent=`opening ${names.length} file${names.length>1?"s":""}…`;
|
|
875
|
+
count.textContent=`opening ${num(names.length)} file${names.length>1?"s":""}…`;
|
|
874
876
|
const problems=[];
|
|
875
877
|
let first=null;
|
|
876
878
|
for(const file of files){
|
|
@@ -884,7 +886,7 @@ async function openFiles(files){
|
|
|
884
886
|
}
|
|
885
887
|
const fresh=await fetch("/api/index").then(r=>r.json());
|
|
886
888
|
INDEX=fresh.sessions||[];GROUPS=fresh.groups||[];TAGS=fresh.tags||[];AI=fresh.ai||{};DOWNLOADS=fresh.downloads||DOWNLOADS;
|
|
887
|
-
|
|
889
|
+
showCount();drawList();
|
|
888
890
|
if(first!==null)pick(first);
|
|
889
891
|
if(problems.length)note(problems.join("\n"));
|
|
890
892
|
picker.value="";
|
|
@@ -1015,10 +1017,10 @@ async function openLibrary(){
|
|
|
1015
1017
|
try{
|
|
1016
1018
|
const d=await readIndex();
|
|
1017
1019
|
INDEX=d.sessions||[];GROUPS=d.groups||[];TAGS=d.tags||[];AI=d.ai||{};DOWNLOADS=d.downloads||DOWNLOADS;
|
|
1018
|
-
bootSay(
|
|
1020
|
+
bootSay(`Drawing ${num(INDEX.length)} sessions…`,0,0);
|
|
1019
1021
|
// Let that line paint before the work that blocks the thread.
|
|
1020
1022
|
await new Promise(r=>requestAnimationFrame(()=>requestAnimationFrame(r)));
|
|
1021
|
-
|
|
1023
|
+
showCount();drawList();showLibrary();
|
|
1022
1024
|
}catch(err){
|
|
1023
1025
|
// Leave it on screen: a blank window would say nothing at all.
|
|
1024
1026
|
bootSay("Could not open your library — "+((err&&err.message)||err),0,0);
|
|
@@ -1117,13 +1119,13 @@ function drawList(){
|
|
|
1117
1119
|
<button title="Remove these sessions from the library" class="dang"
|
|
1118
1120
|
onclick="event.stopPropagation();dropGroup('${esc(r.path)}')">✕</button>
|
|
1119
1121
|
</span>`}
|
|
1120
|
-
<span class="rc">${r.count}</span></div>`;
|
|
1122
|
+
<span class="rc">${num(r.count)}</span></div>`;
|
|
1121
1123
|
}).join("");
|
|
1122
1124
|
|
|
1123
1125
|
tagbar.innerHTML=TAGS.map(t=>{
|
|
1124
1126
|
const on=!!ACTIVE_TAGS[t.name];
|
|
1125
1127
|
return `<span class="pill tag${on?" on":""}" onclick="toggleTag('${esc(t.name)}')">
|
|
1126
|
-
${esc(t.name)}<b>${t.count}</b></span>`;
|
|
1128
|
+
${esc(t.name)}<b>${num(t.count)}</b></span>`;
|
|
1127
1129
|
}).join("");
|
|
1128
1130
|
}
|
|
1129
1131
|
|
|
@@ -1685,7 +1687,7 @@ async function planUrl(){
|
|
|
1685
1687
|
showLibrary();
|
|
1686
1688
|
closeUrl();
|
|
1687
1689
|
showProgress(0,0);
|
|
1688
|
-
if(result)note(`Added ${result.added} session${result.added===1?"":"s"} — ${result.into}`);
|
|
1690
|
+
if(result)note(`Added ${num(result.added)} session${result.added===1?"":"s"} — ${result.into}`);
|
|
1689
1691
|
return;
|
|
1690
1692
|
}
|
|
1691
1693
|
|
|
@@ -1712,8 +1714,8 @@ async function planUrl(){
|
|
|
1712
1714
|
const size=p.bytes?` · ${bytes(p.bytes)}`:"";
|
|
1713
1715
|
const sample=p.names.length?` — ${p.names.slice(0,3).join(", ")}${
|
|
1714
1716
|
p.count>3?", …":""}`:"";
|
|
1715
|
-
setUrlState(`${p.count} file${p.count===1?"":"s"} in ${p.label}${size}${sample}\n`
|
|
1716
|
-
+`→ ${p.into}`, `Download ${p.count}`);
|
|
1717
|
+
setUrlState(`${num(p.count)} file${p.count===1?"":"s"} in ${p.label}${size}${sample}\n`
|
|
1718
|
+
+`→ ${p.into}`, `Download ${num(p.count)}`);
|
|
1717
1719
|
}catch(e){
|
|
1718
1720
|
err.textContent=e.message;err.hidden=false;
|
|
1719
1721
|
setUrlState("","Look");PLANNED=null;
|
|
@@ -1874,7 +1876,7 @@ async function annotate(key,fields){
|
|
|
1874
1876
|
async function refreshIndex(){
|
|
1875
1877
|
const d=await fetch("/api/index").then(r=>r.json());
|
|
1876
1878
|
INDEX=d.sessions||[];GROUPS=d.groups||[];TAGS=d.tags||[];AI=d.ai||{};DOWNLOADS=d.downloads||DOWNLOADS;
|
|
1877
|
-
|
|
1879
|
+
showCount();
|
|
1878
1880
|
drawList();
|
|
1879
1881
|
if(!cur)showLibrary();
|
|
1880
1882
|
}
|
|
@@ -2277,7 +2279,7 @@ function branchNav(branches){
|
|
|
2277
2279
|
<span class="sid">${b.stepId}</span>
|
|
2278
2280
|
<b>${esc(label)}</b>
|
|
2279
2281
|
<span>${esc(x.description||"")||"—"}</span>
|
|
2280
|
-
<em>${b.sub?b.sub.steps.length+" steps":"external"}</em>
|
|
2282
|
+
<em>${b.sub?num(b.sub.steps.length)+" steps":"external"}</em>
|
|
2281
2283
|
</button>`;
|
|
2282
2284
|
}).join("")+`</div></div>`;
|
|
2283
2285
|
}
|
|
@@ -2305,7 +2307,7 @@ function step(s,ctx,depth,idx,prefix){
|
|
|
2305
2307
|
const on=starredSteps().includes(key);
|
|
2306
2308
|
let h=`<div class="step ${s.source}" id="step-${key}">
|
|
2307
2309
|
<div class="gut">
|
|
2308
|
-
<a class="sid" href="#step-${key}" title="step ${s.step_id}">${s.step_id}</a>
|
|
2310
|
+
<a class="sid" href="#step-${key}" title="step ${num(s.step_id)}">${num(s.step_id)}</a>
|
|
2309
2311
|
<span class="sstar${on?" on":""}" onclick="toggleStep(event,'${key}')"
|
|
2310
2312
|
title="${on?"Remove from favourites":"Add to favourites"}">
|
|
2311
2313
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="${on?"currentColor":"none"}"
|
|
@@ -2349,7 +2351,7 @@ function branch(ref,ctx,depth){
|
|
|
2349
2351
|
return `<details class="branch"${OPEN_ALL?" open":""}><summary>
|
|
2350
2352
|
<b>${esc(label)}</b>
|
|
2351
2353
|
<span class="desc">${esc(x.description||"")}</span>
|
|
2352
|
-
<span class="pill br">${sub.steps.length} steps</span>
|
|
2354
|
+
<span class="pill br">${num(sub.steps.length)} steps</span>
|
|
2353
2355
|
${depth?`<span class="pill">depth ${depth+1}</span>`:""}
|
|
2354
2356
|
</summary><div class="inner">
|
|
2355
2357
|
${sub.steps.map(s=>step(s,sub,depth+1,null,esc(sub.trajectory_id||"sub")+"-")).join("")}
|
|
@@ -110,3 +110,40 @@ def test_sizes_go_through_one_formatter():
|
|
|
110
110
|
)
|
|
111
111
|
assert "1048576" not in code, "a size is still being divided by hand"
|
|
112
112
|
assert code.count("function bytes(") == 1
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# Numbers a reader sees are formatted in one place, so a corpus of thousands
|
|
116
|
+
# reads as "2,258 sessions" rather than "2258 sessions". These are the shapes
|
|
117
|
+
# that reached the screen raw before anyone looked.
|
|
118
|
+
RAW_NUMBERS = (
|
|
119
|
+
(r"\.length\s*\+\s*[\"']", "a count concatenated onto a string"),
|
|
120
|
+
(r"\$\{[^}]*\.length\}\s*(?:session|step|file|result|match)", "${…length} beside a noun"),
|
|
121
|
+
(r"\$\{\s*[a-zA-Z_$][\w.$]*\.count\s*\}", "${…count}"),
|
|
122
|
+
(r"\$\{\s*[a-zA-Z_$][\w.$]*\.added\s*\}", "${…added}"),
|
|
123
|
+
(r"\$\{\s*s\.step_id\s*\}", "${s.step_id}"),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@pytest.mark.parametrize(("pattern", "what"), RAW_NUMBERS, ids=[w for _, w in RAW_NUMBERS])
|
|
128
|
+
def test_a_number_on_screen_goes_through_the_formatter(pattern, what):
|
|
129
|
+
script = PAGE[PAGE.index("<script>") : PAGE.rindex("</script>")]
|
|
130
|
+
hits = []
|
|
131
|
+
for i, line in enumerate(script.splitlines(), start=1):
|
|
132
|
+
stripped = line.strip()
|
|
133
|
+
if stripped.startswith("//") or stripped.startswith("*"):
|
|
134
|
+
continue
|
|
135
|
+
for match in re.finditer(pattern, line):
|
|
136
|
+
around = line[max(0, match.start() - 14) : match.end() + 3]
|
|
137
|
+
if "num(" in around or "bytes(" in around:
|
|
138
|
+
continue
|
|
139
|
+
hits.append(f"line {i}: {stripped[:90]}")
|
|
140
|
+
assert not hits, f"{what} without num(): " + "; ".join(hits)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_the_session_count_is_said_in_one_place():
|
|
144
|
+
"""It was written out three times, and one of them was already wrong."""
|
|
145
|
+
script = PAGE[PAGE.index("<script>") : PAGE.rindex("</script>")]
|
|
146
|
+
assert "const showCount=" in script, "the count should have a single home"
|
|
147
|
+
assert script.count('count.textContent=') <= 2, (
|
|
148
|
+
"the session count is being written in more than one place again"
|
|
149
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|