Skip to content

Commit 28f792f

Browse files
nodeeeeeeclaude
andcommitted
Add note language selector to Pipeline/Generate pages; fix Chinese terminal output
- Add --language en/zh CLI argument to note_generation.py that overrides the NOTE_LANGUAGE constant at runtime - Add language dropdown to Pipeline page (beside course name) and Generate page, passing --language to the CLI command - Fix Chinese text in terminal print/tqdm.write statements in _print_score() and generate_with_iteration() — all terminal output is now English regardless of note language - Add 26 tests covering: no CJK in print statements (AST-based scan of all 6 scripts), --language flag acceptance/rejection/override, prompt selection by language, skip logic for transcribe/align/generate, Electron UI language dropdown presence and CLI integration Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4dce332 commit 28f792f

3 files changed

Lines changed: 481 additions & 17 deletions

File tree

electron/renderer/app.js

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const State = {
1818
detail: '7',
1919
courseName: '',
2020
lecFilter: '',
21+
language: 'en',
2122
stealth: false,
2223
force: false,
2324
steps: { mat: true, vid: true, trans: true, align: true, gen: true },
@@ -602,9 +603,18 @@ function buildPipeline() {
602603
${mkSwitch('pp-stealth', 'Slack mode for downloads', ps.stealth)}
603604
<hr class="divider">
604605
<span class="label">Note generation</span>
605-
<div class="field">
606-
<label class="label">Course name for notes</label>
607-
<input id="pp-course-name" class="input-text" type="text" value="${esc(ps.courseName)}">
606+
<div class="row" style="gap:8px">
607+
<div class="field" style="flex:1">
608+
<label class="label">Course name for notes</label>
609+
<input id="pp-course-name" class="input-text" type="text" value="${esc(ps.courseName)}">
610+
</div>
611+
<div class="field" style="min-width:110px">
612+
<label class="label">Language</label>
613+
<select id="pp-language" class="select-ctrl">
614+
<option value="en"${ps.language === 'en' ? ' selected' : ''}>English</option>
615+
<option value="zh"${ps.language === 'zh' ? ' selected' : ''}>中文</option>
616+
</select>
617+
</div>
608618
</div>
609619
<div class="row center" style="gap:8px;margin-top:8px">
610620
<span class="slider-value" id="pp-detail-val">${esc(ps.detail)}</span>
@@ -858,9 +868,18 @@ function buildGenerate() {
858868
${mkCard(`
859869
<span class="label">Course</span>
860870
<select id="gen-course" class="select-ctrl">${courseOptions()}</select>
861-
<div class="field" style="margin-top:8px">
862-
<label class="label">Course name</label>
863-
<input id="gen-course-name" class="input-text" type="text">
871+
<div class="row" style="gap:8px;margin-top:8px">
872+
<div class="field" style="flex:1">
873+
<label class="label">Course name</label>
874+
<input id="gen-course-name" class="input-text" type="text">
875+
</div>
876+
<div class="field" style="min-width:110px">
877+
<label class="label">Language</label>
878+
<select id="gen-language" class="select-ctrl">
879+
<option value="en" selected>English</option>
880+
<option value="zh">中文</option>
881+
</select>
882+
</div>
864883
</div>
865884
<div class="field" style="margin-top:8px">
866885
<label class="label">Lecture</label>
@@ -1413,6 +1432,9 @@ async function attachPageHandlers() {
14131432
document.getElementById('pp-lec-filter')?.addEventListener('input', e => {
14141433
State.pipeline.lecFilter = e.target.value;
14151434
});
1435+
document.getElementById('pp-language')?.addEventListener('change', e => {
1436+
State.pipeline.language = e.target.value;
1437+
});
14161438
document.getElementById('pp-stealth')?.addEventListener('change', e => {
14171439
State.pipeline.stealth = e.target.checked;
14181440
});
@@ -1445,6 +1467,7 @@ async function attachPageHandlers() {
14451467
const force = document.getElementById('pp-force')?.checked;
14461468
const name = document.getElementById('pp-course-name')?.value.trim() || '';
14471469
const detail = document.getElementById('pp-detail')?.value || '7';
1470+
const lang = document.getElementById('pp-language')?.value || 'en';
14481471
const lf = document.getElementById('pp-lec-filter')?.value.trim() || '';
14491472
const steps = [
14501473
['dl_mat', document.getElementById('pp-step-mat')?.checked],
@@ -1480,7 +1503,7 @@ async function attachPageHandlers() {
14801503
chain.push(['Align', c]);
14811504
}
14821505
if (steps.includes('generate')) {
1483-
const c = [python, paths.generate, '--course', cid, '--course-name', name || courseNameFromId(cid), '--detail', detail, '--per-video'];
1506+
const c = [python, paths.generate, '--course', cid, '--course-name', name || courseNameFromId(cid), '--detail', detail, '--language', lang, '--per-video'];
14841507
if (lf) c.push('--lectures', lf);
14851508
if (force) c.push('--force');
14861509
chain.push(['Generate notes', c]);
@@ -1703,12 +1726,13 @@ async function attachPageHandlers() {
17031726
const paths = await window.api.getScriptsPaths();
17041727
const name = document.getElementById('gen-course-name')?.value.trim() || courseNameFromId(cid);
17051728
const detail = document.getElementById('gen-detail')?.value || '7';
1729+
const lang = document.getElementById('gen-language')?.value || 'en';
17061730
const force = document.getElementById('gen-force')?.checked;
17071731
const merge = document.getElementById('gen-merge')?.checked;
17081732
const iter = document.getElementById('gen-iterate')?.checked;
17091733
const lecNum = document.getElementById('gen-lec-select')?.value; // '' = all
17101734

1711-
const cmd = [python, paths.generate, '--course', cid, '--course-name', name, '--detail', detail];
1735+
const cmd = [python, paths.generate, '--course', cid, '--course-name', name, '--detail', detail, '--language', lang];
17121736
if (lecNum) cmd.push('--lectures', lecNum);
17131737
if (force) cmd.push('--force');
17141738
if (merge) cmd.push('--merge-only');

note_generation.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,17 +1016,17 @@ def self_score(all_slides: list[SlideInfo], full_notes: str,
10161016
def _print_score(scores: dict, label: str) -> None:
10171017
st = scores.get("stats", {})
10181018
tqdm.write(f"\n ┌──────────────────────────────────────────────┐")
1019-
tqdm.write(f" │ 自评分: {label[:36]:36s}│")
1019+
tqdm.write(f" │ Score: {label[:38]:38s}│")
10201020
tqdm.write(f" ├──────────────────────────────────────────────┤")
1021-
tqdm.write(f" │ 覆盖率 {scores['coverage']:4.1f}/10 "
1021+
tqdm.write(f" │ Coverage {scores['coverage']:4.1f}/10 "
10221022
f"({st.get('note_words','?')} / ~{st.get('expected_words','?')} words) │")
1023-
tqdm.write(f" │ 术语准确率 {scores['terminology']:4.1f}/10 "
1023+
tqdm.write(f" │ Terminology{scores['terminology']:4.1f}/10 "
10241024
f"({st.get('term_hits','?')}/{st.get('term_total','?')} terms) │")
1025-
tqdm.write(f" │ 重点标注 {scores['callouts']:4.1f}/10 "
1025+
tqdm.write(f" │ Callouts {scores['callouts']:4.1f}/10 "
10261026
f"({st.get('callouts_written','?')}/{st.get('callouts_needed','?')} callouts) │")
1027-
tqdm.write(f" │ 代码块 {scores['code_blocks']:4.1f}/10 "
1027+
tqdm.write(f" │ Code blocks{scores['code_blocks']:4.1f}/10 "
10281028
f"({st.get('code_blocks','?')}/{st.get('code_slides','?')} code slides) │")
1029-
tqdm.write(f" │ 综合评分 {scores['overall']:4.2f}/10 │")
1029+
tqdm.write(f" │ Overall {scores['overall']:4.2f}/10 │")
10301030
tqdm.write(f" └──────────────────────────────────────────────┘")
10311031

10321032

@@ -1388,15 +1388,15 @@ def generate_with_iteration(
13881388
best_path = path
13891389

13901390
if overall >= QUALITY_TARGET:
1391-
tqdm.write(f"\n达到目标 {QUALITY_TARGET} (得分={overall:.2f})")
1391+
tqdm.write(f"\nTarget {QUALITY_TARGET} reached (score={overall:.2f})")
13921392
break
13931393
if rnd < max_rounds:
1394-
tqdm.write(f"\n 得分 {overall:.2f} < {QUALITY_TARGET},提升详细度后重试…")
1394+
tqdm.write(f"\n Score {overall:.2f} < {QUALITY_TARGET}, raising detail and retrying…")
13951395
versioned = path.with_name(f"{path.stem}_r{rnd}{path.suffix}")
13961396
path.rename(versioned)
13971397
detail = min(detail + 2, 10)
13981398

1399-
tqdm.write(f"\n 最佳: {best_path} (得分={best_score:.2f})")
1399+
tqdm.write(f"\n Best: {best_path} (score={best_score:.2f})")
14001400
return best_path
14011401

14021402

@@ -1583,8 +1583,16 @@ def main() -> None:
15831583
help="Filter lectures, e.g. '1-5' or '1,2,3'")
15841584
parser.add_argument("--per-video", action="store_true",
15851585
help="Generate one note file per video/lecture instead of one combined note")
1586+
parser.add_argument("--language", metavar="LANG", default=None,
1587+
choices=["en", "zh"],
1588+
help="Note language: en (English) or zh (Chinese). "
1589+
"Overrides the NOTE_LANGUAGE constant.")
15861590
args = parser.parse_args()
15871591

1592+
if args.language:
1593+
global NOTE_LANGUAGE
1594+
NOTE_LANGUAGE = args.language
1595+
15881596
if args.course:
15891597
course_dir = COURSE_DATA_DIR / args.course
15901598
course_name = args.course_name or f"CS{args.course}"

0 commit comments

Comments
 (0)