icoa-cli 2.19.591 → 2.19.593
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.
- package/dist/commands/ai4ctf.js +1 -1
- package/dist/commands/ctf4ai-demo.js +1 -1
- package/dist/commands/ctf4vla.js +1 -1
- package/dist/commands/exam.js +1 -1
- package/dist/commands/learn.js +1 -1
- package/dist/lib/learn-check.js +1 -0
- package/dist/lib/learn-contest.js +1 -1
- package/dist/lib/learn-render.js +1 -1
- package/dist/lib/learn-team.js +1 -0
- package/dist/repl.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import chalk from"chalk";import{getConfig as t}from"./config.js";import{learnCheck as e,posForArenaSlot as r}from"./learn-contest.js";import{loadCurriculumById as o}from"./learn-curricula.js";import{renderStatus as s}from"./learn-render.js";import{newLearnState as a,stateFromServerEvents as n}from"./learn-state.js";import{normalizeTokenBody as l}from"./token-format.js";export const CHECK_RE=/^\/?check(?:\s+(.*))?$/i;export async function runLearnCheck(i,c,u={}){const d=(t().language||"en").toLowerCase().startsWith("zh"),g=c.trim().split(/\s+/).filter(Boolean),m=(g[0]||"").replace(/^(icoa\s+)?learn\s+/i,"").toUpperCase(),f=(g[1]||"").toLowerCase(),p="all"===f?"all":/^\d+$/.test(f)?Number(f):"current";if(!m||!/^[A-Z0-9]{6,16}$/.test(m))return void console.log(chalk.gray(d?" 用法:":" Usage: ")+chalk.white("check <TOKEN>")+chalk.gray(d?" —— 看这枚 token 的学习状态(同 status)":" — that learner's status (same view as status)"));if(m===i.toUpperCase())return void console.log(chalk.gray(d?" 这是你自己的 token —— 直接敲 status。":" That is your own token — just type status."));console.log(chalk.gray(d?" 查询中…":" looking up…"));let h=await e(i,m);if(!h.ok&&"unknown_target"===h.code){const t=l(m);t!==m&&(h=await e(i,t))}if(!h.ok||!h.data){const t="unknown_target"===h.code?d?"服务器不认识这枚 token。":"The server does not know that token.":h.message||(d?"查询失败。":"Lookup failed.");return void console.log(chalk.yellow(" ✗ "+t))}const v=h.data,y=u.curriculum&&u.curriculum.id===v.curriculum_id?u.curriculum:await o(v.curriculum_id,v.target);if(!y)return void console.log(chalk.yellow(d?` ✗ 拿不到课程 ${v.curriculum_id}(离线?)—— 只能报:${v.events.length} 条进度事件。`:` ✗ Could not load curriculum ${v.curriculum_id} (offline?) — only ${v.events.length} progress events on record.`));const $=n(v.target,y.id,y.totalCards,v.events)??a(v.target,y.id,y.totalCards);{const t=[...new Set(v.events.map(t=>Math.floor(Date.parse(t.timestamp)/864e5)).filter(t=>!Number.isNaN(t)))].sort((t,e)=>e-t);let e=t.length?1:0;for(let r=1;r<t.length&&t[r]===t[r-1]-1;r++)e++;$.streakDays=e}const _=new Map;for(const t of v.gate.practice??[]){let e=_.get(t.slot);e||(e=new Set,_.set(t.slot,e));for(const r of t.solved)e.add(r)}const k=v.events.reduce((t,e)=>t+(Number(e.time_on_card_ms)||0),0);var w;console.log(),console.log(chalk.cyan(" check ")+chalk.bold.white(v.target)+chalk.gray(" → ")+chalk.white(v.curriculum_id)+chalk.gray(v.issued_to?` · ${v.issued_to}`:"")+("active"!==v.status?chalk.red(` · ${v.status}`):"")+chalk.gray(v.valid_until?` · ${d?"有效至":"valid until"} ${String(v.valid_until).slice(0,10)}`:"")),console.log(chalk.gray(` ${d?"最近活动":"last active"} ${w=v.last_seen,w?w.replace("T"," ").slice(0,16)+" UTC":"—"} · ${v.events.length} ${d?"条事件":"events"} · ${d?"卡上时长":"time on cards"} ${(t=>{const e=Math.round(t/6e4);return e>=60?`${Math.floor(e/60)}h ${e%60}m`:`${e}m`})(k)}`)),s(y,$,{gate:v.gate,gated:!!v.gate.gated,focus:p,posForSlot:r,practiceCount:t=>{const e=function(t){const e=t?.practice;return e&&Array.isArray(e.problems)&&0!==e.problems.length?[...e.problems.filter(t=>t.is_signature),...e.problems.filter(t=>!t.is_signature).sort((t,e)=>(t.difficulty??0)-(e.difficulty??0)||(t.order??0)-(e.order??0))]:null}(y.cards.find(e=>e.number===t));if(!e)return null;const r=_.get(100+t);return{solved:e.filter(t=>{return r?.has((e=t.py_id,`ref:${Buffer.from(e,"utf-8").toString("base64")}`))||r?.has(t.py_id);var e}).length,total:e.length}}})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getDeviceFingerprint as e}from"./access.js";import{LEARN_SERVER as t}from"./learn-curricula.js";async function o(o,n,r){const a=t.replace(/\/$/,"")+o;try{const t=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...n,device_fingerprint:e()}),signal:AbortSignal.timeout(r)}),o=await t.json().catch(()=>({}));return t.ok&&o.success&&void 0!==o.data?{ok:!0,data:o.data}:{ok:!1,message:o.message||`HTTP ${t.status}`,code:o.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export function contestStart(e,t,n,r){return o("/api/icoa/learn/contest/start",{token:e.toUpperCase(),phase:t,...null!=n?{slot:n}:{},...r?{practice:!0}:{}},15e3)}export function contestSubmit(e,t,n,r,a,i){return o("/api/icoa/learn/contest/submit",{token:e.toUpperCase(),phase:t,problem_id:n,code:r,...null!=a?{slot:a}:{},...i?{dyn:i}:{}},12e4)}export function contestReset(e,t,n){return o("/api/icoa/learn/contest/reset",{token:e.toUpperCase(),phase:t,...null!=n?{slot:n}:{}},15e3)}export function duckBoard(e,t,n={}){return o("/api/icoa/learn/duck/board",{token:e.toUpperCase(),problem_id:t,...n},15e3)}export function fetchGate(e){return o("/api/icoa/learn/gate",{token:e.toUpperCase()},15e3)}export function linkSuggest(e){return o("/api/icoa/learn/link",{token:e.toUpperCase()},2e4)}export function linkByIndex(e,t){return o("/api/icoa/learn/link",{token:e.toUpperCase(),index:t},2e4)}export function linkToken(e,t){return o("/api/icoa/learn/link",{token:e.toUpperCase(),other_token:t.toUpperCase()},2e4)}export function timeLeft(e){const t=Date.parse(e);if(Number.isNaN(t))return null;const o=t-Date.now();if(o<=0)return null;const n=Math.floor(o/6e4),r=Math.floor(o%6e4/1e3);return`${n}:${String(r).padStart(2,"0")}`}export function arenaSlotAtPos(e){return e%4==0?e/4:10===e?4:0}export function posForArenaSlot(e){return 4===e?10:4*e}
|
|
1
|
+
import{getDeviceFingerprint as e}from"./access.js";import{LEARN_SERVER as t}from"./learn-curricula.js";async function o(o,n,r){const a=t.replace(/\/$/,"")+o;try{const t=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...n,device_fingerprint:e()}),signal:AbortSignal.timeout(r)}),o=await t.json().catch(()=>({}));return t.ok&&o.success&&void 0!==o.data?{ok:!0,data:o.data}:{ok:!1,message:o.message||`HTTP ${t.status}`,code:o.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export function learnPost(e,t,n){return o(e,t,n)}export function contestStart(e,t,n,r){return o("/api/icoa/learn/contest/start",{token:e.toUpperCase(),phase:t,...null!=n?{slot:n}:{},...r?{practice:!0}:{}},15e3)}export function contestSubmit(e,t,n,r,a,i){return o("/api/icoa/learn/contest/submit",{token:e.toUpperCase(),phase:t,problem_id:n,code:r,...null!=a?{slot:a}:{},...i?{dyn:i}:{}},12e4)}export function contestReset(e,t,n){return o("/api/icoa/learn/contest/reset",{token:e.toUpperCase(),phase:t,...null!=n?{slot:n}:{}},15e3)}export function duckBoard(e,t,n={}){return o("/api/icoa/learn/duck/board",{token:e.toUpperCase(),problem_id:t,...n},15e3)}export function fetchGate(e){return o("/api/icoa/learn/gate",{token:e.toUpperCase()},15e3)}export function learnCheck(e,t){return o("/api/icoa/learn/check",{token:e.toUpperCase(),target:t.toUpperCase()},15e3)}export function linkSuggest(e){return o("/api/icoa/learn/link",{token:e.toUpperCase()},2e4)}export function linkByIndex(e,t){return o("/api/icoa/learn/link",{token:e.toUpperCase(),index:t},2e4)}export function linkToken(e,t){return o("/api/icoa/learn/link",{token:e.toUpperCase(),other_token:t.toUpperCase()},2e4)}export function timeLeft(e){const t=Date.parse(e);if(Number.isNaN(t))return null;const o=t-Date.now();if(o<=0)return null;const n=Math.floor(o/6e4),r=Math.floor(o%6e4/1e3);return`${n}:${String(r).padStart(2,"0")}`}export function arenaSlotAtPos(e){return e%4==0?e/4:10===e?4:0}export function posForArenaSlot(e){return 4===e?10:4*e}
|
package/dist/lib/learn-render.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import chalk from"chalk";import e from"string-width";import{localized as o}from"./learn-curricula.js";import{t as n}from"./learn-i18n.js";import{getConfig as t}from"./config.js";import{icoaBannerLines as r}from"./banner.js";import{renderCharts as a,detectCaps as l}from"./render-card.js";import{replyChipLabel as s}from"./learn-replies.js";function c(){return(t().language||"en").toLowerCase()}let g="normal";export function resolveLearnWidth(e){return"normal"===e.learnWidth||"wide"===e.learnWidth||"wider"===e.learnWidth||"widest"===e.learnWidth?e.learnWidth:e.learnWide?"wide":"normal"}export function setLearnWidth(e){g=e}export function learnWidthCap(e=g){return"widest"===e?160:"wider"===e?120:"wide"===e?96:64}export function learnW(){const e=process.stdout.columns||69,o=Math.max(40,Math.min(64,e-5));return"normal"===g?o:Math.max(o,Math.min(e-6,learnWidthCap()))}const i=learnW;function d(e="─"){return e.repeat(i()+2)}function h(o,n){if(e(o)<=n)return o;let t=o;for(;t.length>0&&e(t)>Math.max(1,n-1);)t=t.slice(0,-1);return t.trimEnd()+"…"}function y(e,o,n=20){const t=o>0?e/o:0,r=Math.floor(t*n),a=n-r;return`${chalk.green("█".repeat(r))+chalk.gray("░".repeat(a))} ${(100*t).toFixed(1)}%`}function u(e){return e>=30?chalk.red("🔥🔥🔥 "+e+" days"):e>=7?chalk.yellow("🔥 "+e+" days"):e>=1?chalk.gray("· "+e+" day"+(e>1?"s":"")):chalk.gray("—")}export function renderWelcome(e,o,t){const r=c(),a=o.cardsCompleted.length,l=e.totalCards,s=l-a,g=e.modules.find(e=>o.currentCard>=e.cardRange[0]&&o.currentCard<=e.cardRange[1]),p=/demo/i.test(e.id);if(console.log(),console.log(chalk.cyan(" ╭"+d("═"))),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ║ ")+chalk.bold.white(" "+(e.name?.trim()||n("academy_title",r)))),console.log(chalk.cyan(" ║")),t)console.log(chalk.cyan(" ║ ")+chalk.white(" "+n(p?"welcome_new_demo":"welcome_new",r)));else{const e=new Date(o.lastSeenAt),t=Math.floor((Date.now()-e.getTime())/36e5),a=t<1?n("just_now",r):t<24?`${t}${n("ago_hours",r)}`:`${Math.floor(t/24)}${n("ago_days",r)}`;console.log(chalk.cyan(" ║ ")+chalk.white(` ${n("welcome_back",r)} ${a}`))}console.log(chalk.cyan(" ║"));const b=h(g?`${g.number}. ${g.name}`:"—",i()-14);console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("module",r).padEnd(11))+chalk.white(b)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("progress",r).padEnd(11))+y(a,l,Math.max(6,Math.min(20,i()-26-`${a}/${l}`.length)))+chalk.gray(` (${a}/${l})`)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("streak",r).padEnd(11))+u(o.streakDays)),s>0&&o.currentCard<=l&&console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("next_card",r).padEnd(11))+chalk.white(`#${o.currentCard} ${n("of",r)} ${l}`)),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ╰"+d("═"))),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────"));const m=0===a?n("continue_start",r):`${n("continue_resume",r)} ${o.currentCard}`;console.log(chalk.bold.green(" continue")+chalk.gray(" "+m)),console.log(chalk.yellow(" status")+chalk.gray(" "+n("status_full",r))),o.bookmarks.length>0&&console.log(chalk.yellow(" bookmarks")+chalk.gray(` ${o.bookmarks.length} ${n("bookmarks_desc",r)}`)),console.log(chalk.gray(" quit")+chalk.gray(" "+n("quit_desc",r))),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log()}function p(o,t){const r=c(),a=o.module,l=t.modules.find(e=>e.number===a),s=l?l.name:n("unknown_module",r),g=n("module",r).replace(":","").trim(),d=h(`${n("card",r)} ${o.number} / ${t.totalCards} · ${g} ${a} · ${s}`,i()-4);console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(d)+" "+chalk.cyan("─".repeat(Math.max(0,i()-e(d)-4))))}function b(){console.log(chalk.cyan(" ╰"+d())),console.log()}function m(e){console.log(chalk.cyan(" │ ")+chalk.white(e))}function f(){console.log(chalk.cyan(" │"))}const $=/[┌┐└┘├┤┬┴┼─│╔╗╚╝═║╠╣╦╩╬▓▒░█]/;function w(e){return/^(?: {4}|\t)/.test(e)||$.test(e)||/ {3,}/.test(e)}export function wrapMixed(o,n){const t=[];let r="",a=0,l=!1;for(const s of function(o){const n=[],t=o=>{let t="";const r=()=>{t&&(n.push({t:"atom",s:t}),t="")};for(const a of o)" "===a?(r(),n.push({t:"space"})):2===e(a)?(r(),n.push({t:"wide",s:a})):t+=a;r()},r=/`[^`]*`/g;let a,l=0;for(;null!==(a=r.exec(o));)t(o.slice(l,a.index)),n.push({t:"code",s:a[0]}),l=a.index+a[0].length;return t(o.slice(l)),n}(o)){if("space"===s.t){""!==r&&(l=!0);continue}const o=s.s,c=e("code"===s.t?o.replace(/`/g,""):o),g=l?1:0,i="atom"===s.t&&o.length<=2&&/^[.,;:!?)\]}…'"]+$/.test(o);""!==r&&!i&&a+g+c>n?(t.push(r),r=o,a=c,l=!1):(l&&(r+=" ",a+=1,l=!1),r+=o,a+=c)}return r&&t.push(r),t}const k=/\b(?:Common bug|Common mistake|Common error|Pitfall|Gotcha|Footgun)\b[::]?|\b(?:Warning|Caution)\b[::]|(?:常见错误|常见陷阱|常见误区|陷阱|注意|警告|误区)[::]/gi;function _(e,o){const n=o?chalk.bold.white:chalk.white;let t,r="",a=0;for(k.lastIndex=0;null!==(t=k.exec(e));)t.index>a&&(r+=n(e.slice(a,t.index))),r+=chalk.bold.yellow(t[0]),a=t.index+t[0].length;return a<e.length&&(r+=n(e.slice(a))),r}function x(e,o=!1){let n="",t=0;const r=/`([^`]*)`/g;let a;for(;null!==(a=r.exec(e));)a.index>t&&(n+=_(e.slice(t,a.index),o)),n+=chalk.cyan(a[1]),t=a.index+a[0].length;return t<e.length&&(n+=_(e.slice(t),o)),n}function C(e,o=!1){let n=!1;for(const t of e.split("\n"))if(/^\s*```/.test(t))n=!n;else if(n)console.log(chalk.cyan(" │ ")+chalk.cyan(` ${t}`));else if(""!==t)if(w(t))console.log(chalk.cyan(" │ ")+chalk.white(t));else for(const e of wrapMixed(t,i()-4))console.log(chalk.cyan(" │ ")+` ${x(e,o)}`);else f()}function v(e,o){const n=[];for(const t of e.split("\n")){if(""===t){n.push("");continue}if(w(t)){n.push(t);continue}let e="";for(const r of t.split(" "))(e+" "+r).trim().length>o?(n.push(e.trim()),e=r):e=(e+" "+r).trim();e&&n.push(e)}return n}let M=!1;export function setLearnAiEnabled(e){M=e}let S=new Set;export function setLearnPqsViewCards(e){S=e}let A=new Map;export function setLearnHonorRanks(e){A=e}const W=["①","②","③","④","⑤","⑥","⑦","⑧","⑨","⑩"];let j=[];export function setLearnReplies(e){j=e}let z=[];export function setLearnFaqs(e){z=e}let L=null;export function setLearnHometask(e){L=e}let R="full";export function setLearnFooterProfile(e){R=e}let P=null;export function setLearnFooterExtra(e){P=e}function q(){const e=P?.();e&&console.log(chalk.gray(" "+e))}let E=null;export function setLearnPracticeChips(e){E=e}let T=!1;export function setLearnShellLane(e){T=e}let N=!1,O=new Map;export function setLearnArenaCards(e){O=new Map(e.map(e=>[e.card,e.slot]))}export function setLearnArenaLane(e){N=e}export function learnCheckpointVerb(){return D?"duck":null}let D=!1;export function setLearnDuckLane(e){D=e}let F=null;export function setLearnPracticeCount(e){F=e}export function renderHonorWall(e){const o=c().startsWith("zh"),{totals:n,chapters:t}=e,r=n.gold+n.silver+n.bronze,a=(e,o)=>o>0?chalk.bold.white(`${e}×${o}`):chalk.gray(`${e}×0`);if(console.log(chalk.gray(" ─────────────────────────────────────────")),console.log(chalk.bold.white(o?" 🏅 奖牌墙 ":" 🏅 Medal wall ")+a("🥇",n.gold)+chalk.gray(" · ")+a("🥈",n.silver)+chalk.gray(" · ")+a("🥉",n.bronze)+chalk.gray(o?" (永久 — 挂上墙就不会摘下)":" (permanent — once earned, never removed)")),0===r){const n=null!=e.floor?`${e.floor}+`:o?"高分":"a high score";return console.log(chalk.gray(o?` 还没有奖牌 — 单卡提问评分 ${n} 并进入该卡前三,即可永久挂墙 🥇🥈🥉`:` No medals yet — score ${n} on a card and hold its top-3 to hang one 🥇🥈🥉`)),void console.log(chalk.gray(" ─────────────────────────────────────────"))}const l=t.map(e=>{const o=`Ch${e.chapter}`,n=(e.gold>0?`🥇${e.gold}`:"")+(e.silver>0?`🥈${e.silver}`:"")+(e.bronze>0?`🥉${e.bronze}`:"");return n?chalk.white(`${o} ${n}`):chalk.gray(`${o} —`)}).join(chalk.gray(" "));console.log(" "+l);const s=t.flatMap(e=>e.cards.map(o=>({...o,chapter:e.chapter})));s.sort((e,o)=>e.rank-o.rank||e.chapter-o.chapter||e.card-o.card);for(const e of s.slice(0,24)){const n=(e.since||"").slice(0,10);console.log(chalk.white(` ${e.icon} `)+chalk.gray(`Ch${e.chapter} · `)+chalk.white(o?`第 ${e.card} 张`:`card ${e.card}`)+chalk.gray(` · ${e.score}`)+(n?chalk.gray(o?` · 始于 ${n}`:` · since ${n}`):""))}s.length>24&&console.log(chalk.gray(o?` …还有 ${s.length-24} 枚`:` …and ${s.length-24} more`)),console.log(chalk.gray(" ─────────────────────────────────────────"))}export function renderKnowledgeCard(t,r){const d=c(),h=o(t,d);p(h,r),f();const y=wrapMixed(h.title,i()-4);for(const e of y)m(chalk.bold.yellow(e));m(chalk.gray("─".repeat(Math.min(Math.max(...y.map(o=>e(o))),i()))));const u=!!(t.chart_ids&&t.chart_ids.length>0);if(u&&a(t.chart_ids.slice(0,1),r.charts,l()),h.key_point&&""!==h.key_point.trim()?(u||f(),function(e,o,n){const t=c().startsWith("zh"),r=l().cols,a="-".repeat(Math.min(i(),Math.max(24,r-6))),s=t?"要点":"KEY POINT",g=t?`第 ${o} / ${n} 张`:`Card ${o} / ${n}`;console.log(chalk.cyan(" │ ")+chalk.green(a)),console.log(chalk.cyan(" │ ")+chalk.green(`${s} · ${g}`));for(const o of wrapMixed(e,i()-4))console.log(chalk.cyan(" │ ")+chalk.bold.green(" "+o));console.log(chalk.cyan(" │ ")+chalk.green(a)),f()}(h.key_point,h.number,r.totalCards)):u||f(),h.body.filter(e=>null!=e&&""!==e.trim()).forEach((e,o)=>{const n=0===o?e.match(/^([\s\S]*?(?:[.!?](?=\s|$)|[。!?]))(\s[\s\S]*)?$/):null;if(n&&n[1].trim().length<=180){C(n[1],!0);const e=n[2]?.trimStart();e&&C(e,!1)}else C(e,!1);f()}),h.icoaConnection){m(chalk.magenta(" "+n("icoa_connection",d))),m(chalk.gray(" "+"─".repeat(20)));for(const e of wrapMixed(h.icoaConnection,i()-4))m(" "+chalk.magenta(e));f()}if(t.check){const e=d.startsWith("zh"),o=t._zh?.checkStatement??t._zh?.check?.statement,n=e?o??t.check.statement:t.check.statement;for(const o of wrapMixed(e?"请回答 (输 y 或 n):":"Quick comprehension check (type y or n):",i()-8))m(chalk.cyan(" 🤔 "+o));m(chalk.gray(" "+"─".repeat(20)));for(const e of wrapMixed(n,i()-4))m(" "+chalk.white(e));f()}b(),function(o,n){const t=c().startsWith("zh"),r=l().cols,a="-".repeat(Math.min(i(),Math.max(24,r-6))),d=chalk.gray(" ");if("minimal"===R){const e=E?E(o.number):null;console.log(chalk.gray(" "+a)),console.log(chalk.gray(" "+(t?`第 ${o.number} / ${n.totalCards} 张`:`Card ${o.number} / ${n.totalCards}`))+(e?chalk.gray(t?" · 本卡练习 ":" · practice ")+e:""));const r=[];if(o.check?(r.push(chalk.bold.green("[y]")+chalk.gray(t?" 是":" yes")),r.push(chalk.bold.green("[n]")+chalk.gray(t?" 否":" no"))):r.push(chalk.bold.green("[ok]")+chalk.gray(t?" 下一张":" next")),e){const o=Math.max(1,(e.match(/\[\d\]/g)??[]).length),n=Array.from({length:o},(e,o)=>`[${o+1}]`).join("");r.push(chalk.bold.cyan(n)+chalk.gray(t?" 练习":" practice"))}T&&r.push(chalk.bold.magenta("[sh]")+chalk.gray(t?" 实验台":" shell"));const l=O.get(o.number);return N&&l?r.push(chalk.bold.magenta(`[contest ${l}]`)+chalk.gray(t?" 检查点":" checkpoint")):N&&0===O.size&&r.push(chalk.bold.magenta("[battle]")+chalk.gray(t?" 进竞技场":" arena")),D&&r.push(chalk.bold.magenta("[duck]")+chalk.gray(t?" 跑仿真":" sim bench")),o.flow&&o.flow.length>0&&r.push(chalk.bold.magenta("[flow]")+chalk.gray(t?" 互动走一遍":" walk it")),r.push(chalk.bold.cyan("[b]")+chalk.gray(t?" 上一张":" back")),r.push(chalk.bold.yellow("[e]")+chalk.gray(t?" 求助":" help")),r.push(chalk.gray("[quit]")+(t?chalk.gray(" 退出"):"")),console.log(" "+r.join(d)),q(),void console.log()}const h=[];o.check?(h.push(chalk.bold.green("[y]")+chalk.gray(t?" 是":" yes")),h.push(chalk.bold.green("[n]")+chalk.gray(t?" 否":" no"))):h.push(chalk.bold.green("[ok]")+chalk.gray(t?" 继续":" next")),o.flow&&o.flow.length>0&&h.push(chalk.bold.magenta("[flow]")+chalk.gray(t?" 互动走一遍":" walk it")),M&&(h.push(chalk.bold.magenta("[/ai]")+chalk.gray(t?" 🐼 熊猫导师":" 🐼 Panda tutor")),h.push(chalk.bold.magenta("[/submit]")+chalk.gray(t?" 提问评分":" prompt score")));const y=A.get(o.number);var u;h.push(chalk.bold.magenta(y?`[/honors ${u=y,W[u-1]??`#${u}`}]`:"[/honors]")+chalk.gray(t?" 排行":" board")),"normal"===g?h.push(chalk.bold.cyan("[wide]")+chalk.gray(t?" 宽屏":" widen")):("wide"===g?h.push(chalk.bold.cyan("[wider]")+chalk.gray(t?" 更宽":" wider")):"wider"===g?(h.push(chalk.bold.cyan("[widest]")+chalk.gray(t?" 最宽":" widest")),h.push(chalk.bold.cyan("[wide]")+chalk.gray(t?" 回宽屏":" back to wide"))):h.push(chalk.bold.cyan("[wider]")+chalk.gray(t?" 回 wider":" back to wider")),h.push(chalk.bold.cyan("[normal]")+chalk.gray(t?" 标准":" standard")));const p=s(j,z,o.number,t),b=p.startsWith("💬");if(h.push((b?chalk.bold.yellow("[r]"):chalk.gray("[r]"))+(b?chalk.bold.white(` ${p}`):chalk.gray(` ${p}`))),h.push(chalk.bold.yellow("[e]")+chalk.gray(t?" 提问或报告":" ask or report")),L){const e=L,o=`${e.done}/${e.total}`,n=e.completedAt?chalk.bold.green:chalk.bold.cyan;h.push(n("[task]")+chalk.gray(t?` 本周 ${o}`:` week ${o}`))}h.push(chalk.bold.cyan("[b]")+chalk.gray(t?" 上一张":" back")),h.push(chalk.bold.cyan("[card N]")+chalk.gray(t?" 跳转":" jump")),h.push(chalk.gray("[reload]")+(t?chalk.gray(" 刷新"):"")),h.push(chalk.gray("[quit]")+(t?chalk.gray(" 退出"):""));const m=e(a);console.log(chalk.gray(" "+a)),console.log(chalk.gray(" "+(t?`第 ${o.number} / ${n.totalCards} 张`:`Card ${o.number} / ${n.totalCards}`)));let f="",$=0;for(const o of h){const n=e(o);f&&$+2+n>m?(console.log(" "+f),f=o,$=n):(f=f?f+d+o:o,$=f===o?n:$+2+n)}f&&console.log(" "+f),console.log(" "+chalk.magenta("✍ ")+chalk.magenta("[/py]")+chalk.gray(t?" Python 草稿本":" Python scratchpad")+d+chalk.magenta("[/pylist]")+chalk.gray(t?" 我的草稿":" my files")),console.log(chalk.gray(" "+a));const w=[t?"e = 就这张卡提问,或报告问题":"e = ask about this card, or report a problem",t?"更多: bookmark / unbookmark · status · lang zh/en · link":"more: bookmark / unbookmark · status · lang zh/en · link"];for(const e of w)for(const o of wrapMixed(e,m))console.log(chalk.gray(" "+o));q(),console.log()}(t,r)}export function renderCheckFeedback(e,o,n,t=!1){const r=c().startsWith("zh");if(console.log(),n)console.log(chalk.green(" ✓ ")+chalk.white(r?"答对了。":"Correct."));else if(console.log(chalk.yellow(" ✗ ")+chalk.white(r?"记下了,继续。":"Noted — moving on.")),t){const o="y"===e.check?.answer?r?"是":"Yes":r?"否":"No";console.log(chalk.gray(" "+(r?"正确答案: ":"Answer: ")+o+". "+(r?"回看上面那段。":"Re-read the paragraph above.")))}const a=r?e._zh?.solution??e.solution:e.solution;if(a&&a.length>0){console.log(),console.log(chalk.cyan(" 📝 "+(r?"解答:":"Worked solution:")));for(const e of a){for(const o of wrapMixed(e,i()-4))console.log(chalk.white(" "+o));console.log()}}console.log()}function I(e){return[chalk.white(" "+(e?"编著:Charlie Zhu":"Created & edited by Charlie Zhu")),chalk.gray(" "+(e?"ICOA 2026 创始人 · 课程总架构师":"Founder & Chief Curriculum Architect · ICOA 2026"))]}export function renderPhaseComplete(e,o,n,t,a,l){const s=c().startsWith("zh"),[g,d]=n.cardRange,h=o.checkResults||{};let y=0,u=0;for(let e=g;e<=d;e++){const o=h[String(e)];o&&(y++,o.correct&&u++)}const p="-".repeat(48);console.log();for(const e of r(" "))console.log(e);console.log(),console.log(chalk.bold.yellow(" "+p)),console.log(chalk.bold.yellow(" "+(s?`阶段 ${n.number} 完成`:`Phase ${n.number} complete`))+chalk.gray(" · ")+chalk.white(n.name));const b=y>0&&u===y?chalk.bold.green:chalk.bold.white;console.log(chalk.gray(" "+(s?"理解检查得分: ":"comprehension score: "))+b(`${u}/${y}`)+chalk.gray(0===y?s?" (本阶段未作答 y/n)":" (no y/n answered)":""));const m=String(d).length;let f=!1,$=0,w=0,k=0,_=0;for(let o=g;o<=d;o++){const n=e.cards.find(e=>e.number===o);if(!n)continue;const t=n,r=t.title??t.badge??"",a=(s?t._zh?.title??t._zh?.badge??r:r)||"",l=null!=n.check,c=h[String(o)];let g;g=c?c.correct?chalk.green("✓"):chalk.red("✗"):l?chalk.gray("○"):chalk.gray("·");const d=F?F(o):null;d&&(f=!0);const y=d?(d.solved>=d.total?chalk.green:d.solved>0?chalk.yellow:chalk.gray)(` ⚔${d.solved}/${d.total}`):"";d&&($++,d.solved>=d.total&&w++,k+=d.solved,_+=d.total);const u=wrapMixed(a,i()-10-m-(d?6:0)),p=u[0]+(u.length>1?"…":"");console.log(chalk.gray(` ${String(o).padStart(m)} `)+g+y+chalk.white(" "+p))}if(console.log(chalk.gray(s?" ✓ 答对 · ✗ 答错 · ○ 未作答 · 无 y/n 的卡不计分":" ✓ correct · ✗ wrong · ○ unanswered · cards without a y/n are unscored")),f&&console.log(chalk.bold.cyan(" ⚔ "+(s?"卡片练习: ":"practice: "))+(w>=$?chalk.bold.green:chalk.bold.white)(`${w}/${$}`)+chalk.gray(s?` 卡全完成 · ${k}/${_} 题 AC · 行内 ⚔已过/共(绿 完成 · 黄 部分 · 灰 未碰)`:` cards complete · ${k}/${_} problems AC · ⚔solved/total per row (green done · yellow partial · gray untouched)`)),a){const e=e=>"green"===e?chalk.bold.green:"blue"===e?chalk.bold.cyan:"yellow"===e?chalk.bold.yellow:chalk.bold.red;console.log(chalk.bold.magenta(" ⭐ "+(s?"提问得分 (best): ":"Prompt scores (best): "))+(0===a.length?chalk.gray(s?"本阶段尚未提问 — 用 /ai 研究,再 /submit 评分":"none this phase — research with /ai, then /submit"):a.map(o=>chalk.gray((s?"卡":"#")+o.card+" ")+e(o.tier)(String(o.score))).join(chalk.gray(" · "))))}if(l&&!l.configured){const o=d-g+1>12,n="fill"===e.practiceMode;console.log(chalk.gray(o?s?" 🔒 本阶段通关标准制作中 — 很快更新(360 层第二阶段标准设计中)":" 🔒 Pass criteria for this phase are in production — coming soon (360-tier second-stage standard in design)":n?s?" 🔒 本阶段 3 场判分赛制作中 — 很快更新;赛场挂在卡 4 / 8 / 12,3 场全过解锁下一阶段(本轨无 research)":" 🔒 This phase’s 3 judged arenas are in production — coming soon; they hang on cards 4 / 8 / 12 and all 3 unlock the next phase (no research on this track)":s?" 🔒 本阶段通关标准制作中 — 很快更新(标准范式:3 场练习赛 + 3 卡研究)":" 🔒 Pass criteria for this phase are in production — coming soon (standard: 3 arenas + 3 research cards)"))}if(l?.configured&&l.contest){const e=l.contest;if(e.slots&&e.slots.length>0)for(const o of e.slots){const e=chalk.bold.cyan(` ⚔ ${s?"练习赛":"arena"} ${n.number}-${o.slot}: `);o.passed?console.log(e+chalk.bold.green(`${o.best_ac}/${o.n} AC ✅`)):0===o.attempts?console.log(e+chalk.white(s?`${o.n} 题 · 过 ${o.need} 题 — `:`${o.n} problems · AC ${o.need} — `)+chalk.bold.green(`contest ${o.slot}`)):console.log(e+chalk.yellow(s?`最好 ${o.best_ac}/${o.n}(需 ${o.need})· 已试 ${o.attempts} 次 — `:`best ${o.best_ac}/${o.n} (need ${o.need}) · ${o.attempts} attempt(s) — `)+chalk.bold.green(`contest ${o.slot}`))}else{const o=chalk.bold.cyan(" ⚖ "+(s?"判分赛: ":"Contest: "));e.passed?console.log(o+chalk.bold.green(`${e.best_ac}/${e.n} AC ✅ `+(s?"已过关":"passed"))):0===e.attempts?console.log(o+chalk.white(s?`${e.n} 题 · 过 ${e.need} 题解锁 — 输入 `:`${e.n} problems · AC ${e.need} to pass — type `)+chalk.bold.green("contest")+chalk.white(s?" 开始":" to start")):console.log(o+chalk.yellow(s?`最好 ${e.best_ac}/${e.n} AC(需 ${e.need})· 已试 ${e.attempts} 次 — 输入 `:`best ${e.best_ac}/${e.n} AC (need ${e.need}) · ${e.attempts} attempt(s) — type `)+chalk.bold.green("contest")+chalk.yellow(s?" 再战":" to retry"))}}t&&console.log(chalk.gray(" "+(s?"下一阶段: ":"next: "))+chalk.bold.cyan(s?`阶段 ${t.number}`:`Phase ${t.number}`)+chalk.gray(" · ")+chalk.white(t.name)),console.log(chalk.bold.yellow(" "+p));for(const e of I(s))console.log(e)}export function renderCurriculumComplete(e,o){const n=c().startsWith("zh"),t=o.checkResults||{},a="-".repeat(48);let l=0,s=0;console.log();for(const e of r(" "))console.log(e);console.log(),console.log(chalk.bold.green(" "+a)),console.log(chalk.bold.green(" 🏆 "+(n?`${e.name} — 全部完成!`:`${e.name} — complete!`))),console.log(chalk.bold.green(" "+a));for(const o of e.modules){const[e,r]=o.cardRange;let a=0,c=0;for(let o=e;o<=r;o++){const e=t[String(o)];e&&(a++,e.correct&&c++)}l+=a,s+=c;const g=a>0&&c===a?chalk.green:chalk.white;console.log(chalk.gray(" "+(n?`阶段 ${o.number}`:`Phase ${o.number}`).padEnd(8))+g(`${c}/${a}`.padEnd(7))+chalk.gray(o.name))}console.log(chalk.bold.green(" "+a)),console.log(chalk.bold.green(" "+(n?"总分 / overall: ":"Overall: "))+chalk.bold.white(`${s}/${l}`)),console.log(chalk.bold.green(" "+a));for(const e of I(n))console.log(e);console.log()}export function renderMCQCard(e,t){const r=c(),a=o(e,r);p(a,t),f(),m(chalk.bold.yellow("🎯 "+a.title)),m(chalk.gray("─".repeat(Math.min(a.title.length+4,i())))),f();for(const e of v(a.question,i()-4))m(" "+e);f();for(const e of["A","B","C","D"])m(chalk.cyan(` ${e}.`)+" "+chalk.white(a.options[e]));f(),b(),console.log(chalk.gray(" "+(r.startsWith("zh")?"输入 ":"Type "))+chalk.bold.green("A")+chalk.gray(" / ")+chalk.bold.green("B")+chalk.gray(" / ")+chalk.bold.green("C")+chalk.gray(" / ")+chalk.bold.green("D")+chalk.gray(" "+n("type_to_answer",r))),console.log(chalk.gray(" ")+chalk.bold.cyan("back")+chalk.gray(r.startsWith("zh")?" 上一张 · ":" previous card · ")+chalk.bold.cyan("card N")+chalk.gray(r.startsWith("zh")?" 跳转":" jump")),console.log()}export function renderMCQFeedback(e,t,r,a){const l=c(),s=o(e,l);console.log(),r?console.log(" "+chalk.bold.green(n("correct",l)+" ")+chalk.gray(`${n("one_point",l)} · ${t} = ${s.options[t]}`)):console.log(" "+chalk.bold.red(n("not_quite",l)+" ")+chalk.gray(`${n("you_chose",l)} ${t}; ${n("answer_is",l)} ${s.answer}.`)),console.log(),console.log(chalk.gray(" "+n("explanation",l)));for(const e of v(s.explanation,i()-4))console.log(chalk.gray(" "+e));const g=Object.values(a.mcqResults),d=g.filter(e=>e.correct).length;console.log(),console.log(chalk.gray(" "+n("mcq_accuracy_so_far",l)+" ")+chalk.white(`${d}/${g.length}`)),console.log(),r||(console.log(chalk.gray(" "+n("press_ok_continue",l)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",l))),console.log())}export function renderPracticalCard(e,t){const r=c(),a=o(e,r);p(a,t),f(),m(chalk.bold.yellow("🛠 "+a.title)),m(chalk.gray("─".repeat(Math.min(a.title.length+4,i())))),f();for(const e of v(a.task,i()-4))m(" "+e);if(a.starterCode){f(),m(chalk.gray(" "+n("starter_code",r))),f();for(const e of a.starterCode.split("\n"))m(" "+chalk.cyan(e))}f(),b(),console.log(chalk.gray(" "+n("try_in_sandbox",r)+" ")+chalk.bold.cyan("!python3")+chalk.gray(" "+n("drops_into_python",r))),console.log(chalk.gray(" "+n("when_done",r))),console.log(chalk.gray(" ")+chalk.bold.green("done")+chalk.gray(" "+n("done_desc",r))),console.log(chalk.gray(" ")+chalk.bold.yellow("skip")+chalk.gray(" "+n("skip_desc",r))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log()}export function renderPracticalSuccess(e){const t=c(),r=o(e,t);console.log(),console.log(" "+chalk.bold.green(n("practical_recorded",t))),console.log(),console.log(chalk.gray(" "+n("reference_answer",t)));for(const e of v(r.successHint,i()-4))console.log(chalk.gray(" "+e));console.log(),console.log(chalk.gray(" "+n("press_ok_continue",t)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",t))),console.log()}export function renderSimDemoCard(e,t){const r=c(),a=o(e,r);p(a,t),f(),m(chalk.bold.yellow("🎬 "+a.title)),f();for(const e of v(a.description,i()-4))m(" "+e);f(),m(chalk.gray(" "+n("sim_requires",r))),f(),b(),console.log(chalk.gray(" ")+chalk.bold.cyan("sim")+chalk.gray(" "+n("sim_launch_desc",r))),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",r))),console.log(chalk.gray(" bookmark "+n("bookmark_desc",r))),console.log(chalk.gray(" back "+n("back_desc",r))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",r))),console.log()}export function renderArenaTaskCard(e,n){const t=c(),r=t.startsWith("zh"),a=o(e,t);p(a,n),f(),m(chalk.bold.cyan("🏟 "+a.title)),m(chalk.gray(" "+a.origin+" · "+a.domain+" · "+a.metric)),f();for(const e of a.statement){for(const o of v(e,i()-4))m(" "+o);f()}m(chalk.bold.green(r?" 核心要点 — baseline → 解法的关键变化":" CORE DELTA — baseline → solution"));for(const e of a.core_delta)for(const o of v("• "+e,i()-6))m(" "+chalk.white(o));f(),m(chalk.gray((r?" 提交格式: ":" Submission: ")+a.submission_format)),a.hints&&m(chalk.gray(r?" 本题有 5 级提示 (a–e),进入 arena 后逐级解锁。":" 5 progressive hints (a–e) unlock inside the arena.")),f(),b(),console.log(chalk.gray(" ")+chalk.bold.cyan("arena "+a.task_id)+chalk.gray(r?" 动手做这道题(下载 / 提示 / 提交 / 榜单)":" open the arena (download / hints / submit / board)")),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(r?" 继续下一张":" continue to next")),console.log(chalk.gray(" back "+(r?"上一张":"previous"))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+(r?"退出":"exit"))),console.log()}export function renderComingSoonCard(e,n){const t=c(),r=t.startsWith("zh"),a=o(e,t);p(a,n),f(),m(chalk.bold.yellow("🔜 "+a.title)),f();const l=a.note||(r?"这道竞技场题目正在编写中,敬请期待。":"This arena task is coming soon.");for(const e of v(l,i()-4))m(" "+chalk.gray(e));f(),b(),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(r?" 继续":" continue")),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+(r?"退出":"exit"))),console.log()}export function renderMilestone(e,t){const r=c(),a=o(e,r),l=e.number===t.totalCards;console.log(),console.log(chalk.bold.yellow(" ╭"+d("═"))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+chalk.white(" "+n("milestone_header",r))),console.log(chalk.bold.yellow(" ║"));const s=` ${a.badge} ${a.emoji}`;console.log(chalk.bold.yellow(" ║ ")+chalk.bold.green(s)),console.log(chalk.bold.yellow(" ║ ")+chalk.gray(" ─".repeat(Math.max(1,Math.floor(a.badge.length/2+2))))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("in_wild_corresponds",r)));for(const e of v(a.realWorldLevel,i()-4))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(e));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("whats_next",r)));for(const e of v(a.unlockedNext,i()-4))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(e));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ╰"+d("═"))),console.log(),l?(console.log(chalk.gray(" "+n("demo_complete",r))),console.log(),console.log(chalk.gray(" "+n("unlock_full",r)+" ")+chalk.white(n("curriculum_name",r))+chalk.gray(",")),console.log(chalk.gray(" "+n("contact_team_leader",r)+" ")+chalk.bold.yellow("EA")+chalk.gray(" "+n("learn_token",r))),console.log(chalk.gray(" "+n("or_email",r)+" ")+chalk.cyan("australia@icoa2026.au")+chalk.gray(" "+n("for_partnership",r))),console.log(),console.log(chalk.gray(" "+n("type_quit",r)+" ")+chalk.bold.green("quit")+chalk.gray(" "+n("to_exit",r)+" ")+chalk.bold.green("status")+chalk.gray(" "+n("for_dashboard",r)))):(console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",r))),console.log(chalk.gray(" status "+n("status_full",r))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",r)))),console.log()}export function renderStatus(o,t,r={}){const a=c(),l=a.startsWith("zh"),s=t.cardsCompleted.length,g=o.totalCards,d=t.checkResults||{},p=r.gate??null,$=!!r.gated,w=r.focus??"current",k=r.posForSlot??(e=>4===e?10:4*e),_=t.currentCard,x=o.modules.find(e=>_>=e.cardRange[0]&&_<=e.cardRange[1]),C=(o,n)=>o+" ".repeat(Math.max(0,n-e((e=>e.replace(/\x1b\[[0-9;]*m/g,""))(o))));console.log();const v=`${o.name} — ${l?"状态":"Status"}`;console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(v)+" "+chalk.cyan("─".repeat(Math.max(4,i()-2-e(v))))),f(),m(" "+chalk.gray(n("total_progress",a)+" ")+y(s,g)+chalk.gray(` (${s}/${g})`)+chalk.gray(" "+n("streak",a)+" ")+u(t.streakDays));{let n=0,a=0,s=0,c=0,g=0,h=0,y=!1;for(const e of o.modules){for(let t=e.cardRange[0];t<=e.cardRange[1];t++){const e=F?F(t):null;e&&(n++,e.solved>=e.total&&a++);const r=o.cards.find(e=>e.number===t);null!=r?.check&&(s++,d[String(t)]?.correct&&c++)}const t=p?.phases.find(o=>o.phase===e.number);if(t?.configured)for(const e of t.contest?.slots??[])g++,e.passed&&h++;t?.research&&(y=!0)}const u=(e,o)=>(e>=o?chalk.green:chalk.white)(`${e}/${o}`),b=[];n&&b.push(chalk.gray(l?"⚔ 练习卡 ":"⚔ practice cards ")+u(a,n)),g&&b.push(chalk.gray(l?"赛场 ":"arena ")+u(h,g)),s&&b.push(chalk.gray(l?"✓ 自测 ":"✓ checks ")+u(c,s)),b.length&&m(" "+b.join(chalk.gray(" "))),r._anyResearch=y;{let n=0;const r=[];let a=0,c=0,y=0;for(const e of o.modules)for(let l=e.cardRange[0];l<=e.cardRange[1];l++){c++,t.cardsCompleted.includes(l)&&a++;const e=o.cards.find(e=>e.number===l);null!=e?.check&&d[String(l)]&&y++;const s=F?F(l):null;s&&s.solved<s.total&&(n+=s.total-s.solved,r.push(l))}const u=l?"▸ 通关还差 ":"▸ to finish ",p=[];if(a<c&&p.push(chalk.gray(l?"卡片 ":"cards ")+chalk.white(""+(c-a))+chalk.gray(l?" 张未读":" unread")),s&&y<s&&p.push(chalk.gray("✓ ")+chalk.white(""+(s-y))+chalk.gray(l?" 题自测未答":" checks unanswered")),n>0){const o=l?`练习还差 ${n} 题`:`${n} practice problem${n>1?"s":""}`,t=l?"(卡 ":" (cards ",a=")",s=" │ ⚔ ",c=i()-e(s)-e(u)-e(o)-e(t+a),g=e=>{const o=[];let n=0;for(const t of r){const r=String(t).length+(o.length?3:0);if(n+r>e)break;n+=r,o.push(t)}return o};let d=g(c);d.length<r.length&&(d=g(c-e(` +${r.length}`))),!d.length&&r.length&&(d=[r[0]]);const h=r.length-d.length,y=d.join(" · ")+(h>0?` +${h}`:"");p.push(chalk.gray("⚔ ")+chalk.white(o)+chalk.gray(t+y+a))}g&&h<g&&p.push(chalk.gray(l?"赛 ":"arena ")+chalk.white(""+(g-h))+chalk.gray(l?" 场未过":" to pass"));const b=a>0;b&&p.length?p.forEach((o,n)=>m(" "+(0===n?chalk.bold.yellow(u):" ".repeat(e(u)))+o)):b&&!p.length&&m(" "+chalk.bold.green(l?"✅ 通关条件已全部达成":"✅ every completion gate met"))}}f();const M=e=>l?`阶段 ${e}`:`Phase ${e}`,S=Math.max(10,Math.min(28,i()-54));for(const e of o.modules){const[n,r]=e.cardRange,a=r-n+1,s=t.cardsCompleted.filter(e=>e>=n&&e<=r).length,c=$&&!!p&&e.number>p.unlocked_through_phase,g=p?.phases.find(o=>o.phase===e.number);let y=0,u=0,b=0;const f=[],v=[];let A=0,W=0,j=0;const z=[];for(let e=n;e<=r;e++){const n=o.cards.find(o=>o.number===e);if(null!=n?.check){y++;const o=d[String(e)];o?(u++,o.correct?b++:v.push(e)):f.push(e)}const t=F?F(e):null;t&&(A++,j+=t.solved,t.solved>=t.total?W++:z.push(`${e}:${t.solved}/${t.total}`))}const L=g?.configured?g.contest?.slots??[]:[],R=e=>e.passed?chalk.green("✅"):e.attempts>0?chalk.yellow("❌"):chalk.gray("⬜"),P=s>=a&&(0===y||b===y)&&(0===A||W===A)&&(0===L.length||L.every(e=>e.passed))&&(!g?.research||g.research.passed),q=s>0||u>0||j>0||L.some(e=>e.attempts>0),E=c?chalk.gray("🔒"):P?chalk.green("✅"):e.number===x?.number?chalk.bold.cyan("▶ "):q?chalk.yellow("▶ "):chalk.gray("⬜"),T=h(e.name,S),N=[];if(N.push((s>=a?chalk.green:chalk.white)(`${s}/${a}`)+chalk.gray(l?"卡":"c")),y>0&&N.push(chalk.gray("✓")+(b===y?chalk.green:chalk.white)(`${b}/${y}`)),A>0&&N.push(chalk.gray("⚔")+(W===A?chalk.green:chalk.white)(`${W}/${A}`)+chalk.gray(l?"卡":"c")),L.length>0&&N.push(chalk.gray(l?"赛":"arena ")+L.map(R).join("")),g?.research&&N.push(chalk.gray("⭐")+(g.research.passed?chalk.green:chalk.white)(`${g.research.good_cards}/${g.research.need_cards}`)),m(" "+E+" "+chalk.white(C(M(e.number),l?5:8))+chalk.gray(C(T,S+1))+N.join(chalk.gray(" · "))),c||"all"!==w&&w!==e.number&&("current"!==w||e.number!==x?.number)){if(!c&&q&&!P){const o=[],t=L.filter(e=>!e.passed).map(o=>(o.attempts>0?chalk.yellow:chalk.gray)(`${e.number}-${o.slot}@${l?"卡":"card "}${n+k(o.slot)-1}`));t.length&&o.push(chalk.gray(l?"赛 ":"arena ")+t.join(chalk.gray(" · ")));const r=(e,o)=>e.slice(0,8).join(o)+(e.length>8?chalk.gray(" +"+(e.length-8)):"");z.length&&o.push(chalk.gray("⚔ ")+chalk.white(r(z," "))),v.length&&o.push(chalk.red("✗ ")+chalk.white(r(v.map(String),","))),f.length&&o.push(chalk.gray("○ ")+chalk.white(r(f.map(String),","))),g?.research&&!g.research.passed&&o.push(chalk.gray(`⭐ ${g.research.good_cards}/${g.research.need_cards}`)),o.forEach((e,o)=>m(chalk.gray(0===o?l?" 待办: ":" todo: ":" ")+e))}}else{const t=String(r).length;for(let s=n;s<=r;s++){const r=o.cards.find(e=>e.number===s);if(!r)continue;const c=r,g=c.title??c.badge??"",h=(l?c._zh?.title??c._zh?.badge??g:g)||"",y=null!=r.check,u=d[String(s)],p=u?u.correct?chalk.green("✓"):chalk.red("✗"):y?chalk.gray("○"):chalk.gray("·"),b=F?F(s):null,f=b?(b.solved>=b.total?chalk.green:b.solved>0?chalk.yellow:chalk.gray)(`⚔${b.solved}/${b.total}`):" ",$=s===_?chalk.bold.cyan("▶"):" ",w=wrapMixed(h,Math.max(16,i()-14-t)),x=w[0]+(w.length>1?"…":"");m(` ${$} `+chalk.gray(String(s).padStart(t))+" "+p+" "+C(f,5)+" "+chalk.white(x));const v=12===a?L.find(e=>n+k(e.slot)-1===s):void 0;if(v){const o=chalk.gray(` ${" ".repeat(t)} ⚔ ${l?"练习赛":"arena"} ${e.number}-${v.slot}: `);v.passed?m(o+chalk.bold.green(`${v.best_ac}/${v.n} AC ✅`)):0===v.attempts?m(o+chalk.gray("⬜ ")+chalk.white(l?`未开始 · ${v.n} 题 · 过 ${v.need} 题 → `:`not started · ${v.n} problems · AC ${v.need} → `)+chalk.bold.green(`contest ${v.slot}`)):m(o+chalk.yellow(l?`❌ 最好 ${v.best_ac}/${v.n}(需 ${v.need})· 已试 ${v.attempts} 次 → `:`❌ best ${v.best_ac}/${v.n} (need ${v.need}) · ${v.attempts} attempt(s) → `)+chalk.bold.green(`contest ${v.slot}`))}}if(12!==a)for(const o of L){const n=chalk.gray(` ⚔ ${l?"练习赛":"arena"} ${e.number}-${o.slot}: `);o.passed?m(n+chalk.bold.green(`${o.best_ac}/${o.n} AC ✅`)):0===o.attempts?m(n+chalk.gray("⬜ ")+chalk.white(l?`未开始 · ${o.n} 题 · 过 ${o.need} 题 → `:`not started · ${o.n} problems · AC ${o.need} → `)+chalk.bold.green(`contest ${o.slot}`)):m(n+chalk.yellow(l?`❌ 最好 ${o.best_ac}/${o.n}(需 ${o.need})· 已试 ${o.attempts} 次 → `:`❌ best ${o.best_ac}/${o.n} (need ${o.need}) · ${o.attempts} attempt(s) → `)+chalk.bold.green(`contest ${o.slot}`))}g?.research&&!g.research.passed&&m(chalk.gray(l?` ⭐ 研究: ${g.research.good_cards}/${g.research.need_cards} 卡达 ${g.research.min_score} 分 — /ai 研究后 /submit`:` ⭐ research: ${g.research.good_cards}/${g.research.need_cards} cards at ${g.research.min_score}+ — /ai then /submit`)),g&&!g.configured&&m(chalk.gray(l?" 🔒 本阶段通关标准制作中 — 很快更新":" 🔒 pass criteria for this phase in production — coming soon"))}}f(),m(chalk.gray(l?" ✓ 答对 ✗ 答错 ○ 未答 · ⚔ 练习 已过/共 · 赛 ✅ 过 ❌ 试过 ⬜ 未碰"+(r._anyResearch?" · ⭐ 研究":""):" ✓ right ✗ wrong ○ unanswered · ⚔ practice solved/total"+(r._anyResearch?" · ⭐ research":""))),m(chalk.gray(l?" 展开某阶段: ":" arena ✅ passed ❌ tried ⬜ untouched · expand: ")+chalk.bold.green("status all")+chalk.gray(" / ")+chalk.bold.green("status N")),t.achievements.length>0&&m(" "+chalk.gray(n("achievements",a)+" ")+t.achievements.map(e=>chalk.bold.yellow("★ ")+chalk.white(e)).join(chalk.gray(" · "))),f(),b()}function B(e){try{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,e)}catch{}}export function renderArenaIntro(){const e=c().startsWith("zh"),o=learnCheckpointVerb(),n=!!process.stdout.isTTY&&!/^(C|POSIX)$/i.test(process.env.LANG||"")&&!process.env.ICOA_TEST_BOOT_MODE,t=e?[chalk.bold.yellow(" ⚔ 你读完 4 张卡了 — 前面是第一个检查点。"),chalk.white(" ⚔ 每 4 张卡有一场判分赛:")+chalk.bold.yellow("卡 4 / 8 / 12")+chalk.white(",一个阶段共 3 场。"),...o?[chalk.white(" · 敲 ")+chalk.bold.green(o)+chalk.white(" 进竞技场 — ")+chalk.bold.green("battle"===o?"play":"run")+chalk.white(" 本机试跑,")+chalk.bold.green("submit")+chalk.white(" 交给判分机;第一次 submit 时计时开始。")]:[chalk.white(" · 敲 ")+chalk.bold.green("contest 1")+chalk.white(" 开始 — 计时器同时开始,题目在赛场里读。")],chalk.white(" · 判分机会真的跑你的代码,不是对答案。"),chalk.white(" · 3 场全过 → 解锁下一阶段。"),chalk.gray(" ▸ 现在不想打?敲 ")+chalk.bold.green("next")+chalk.gray(" 继续读卡,赛场一直在那儿等你。")]:[chalk.bold.yellow(" ⚔ Four cards down — a checkpoint is waiting."),chalk.white(" ⚔ Every 4 cards there is a judged arena: ")+chalk.bold.yellow("cards 4 / 8 / 12")+chalk.white(", three per phase."),...o?[chalk.white(" · Type ")+chalk.bold.green(o)+chalk.white(" to enter the arena — ")+chalk.bold.green("battle"===o?"play":"run")+chalk.white(" to try it here, ")+chalk.bold.green("submit")+chalk.white(" to send it to the judge; the clock starts at your first submit.")]:[chalk.white(" · Type ")+chalk.bold.green("contest 1")+chalk.white(" to start — the clock starts with it; the problems are read inside.")],chalk.white(" · The judge really runs your code. It is not an answer key."),chalk.white(" · Pass all three → the next phase unlocks."),chalk.gray(" ▸ Not now? Type ")+chalk.bold.green("next")+chalk.gray(" and keep reading — the arena waits for you.")];console.log();for(const e of t)console.log(e),n&&B(200);console.log()}
|
|
1
|
+
import chalk from"chalk";import e from"string-width";import{localized as o}from"./learn-curricula.js";import{t as n}from"./learn-i18n.js";import{getConfig as t}from"./config.js";import{icoaBannerLines as r}from"./banner.js";import{renderCharts as a,detectCaps as l}from"./render-card.js";import{replyChipLabel as s}from"./learn-replies.js";function c(){return(t().language||"en").toLowerCase()}let g="normal";export function resolveLearnWidth(e){return"normal"===e.learnWidth||"wide"===e.learnWidth||"wider"===e.learnWidth||"widest"===e.learnWidth?e.learnWidth:e.learnWide?"wide":"normal"}export function setLearnWidth(e){g=e}export function learnWidthCap(e=g){return"widest"===e?160:"wider"===e?120:"wide"===e?96:64}export function learnW(){const e=process.stdout.columns||69,o=Math.max(40,Math.min(64,e-5));return"normal"===g?o:Math.max(o,Math.min(e-6,learnWidthCap()))}const i=learnW;function d(e="─"){return e.repeat(i()+2)}function h(o,n){if(e(o)<=n)return o;let t=o;for(;t.length>0&&e(t)>Math.max(1,n-1);)t=t.slice(0,-1);return t.trimEnd()+"…"}function y(e,o,n=20){const t=o>0?e/o:0,r=Math.floor(t*n),a=n-r;return`${chalk.green("█".repeat(r))+chalk.gray("░".repeat(a))} ${(100*t).toFixed(1)}%`}function u(e){return e>=30?chalk.red("🔥🔥🔥 "+e+" days"):e>=7?chalk.yellow("🔥 "+e+" days"):e>=1?chalk.gray("· "+e+" day"+(e>1?"s":"")):chalk.gray("—")}export function renderWelcome(e,o,t){const r=c(),a=o.cardsCompleted.length,l=e.totalCards,s=l-a,g=e.modules.find(e=>o.currentCard>=e.cardRange[0]&&o.currentCard<=e.cardRange[1]),p=/demo/i.test(e.id);if(console.log(),console.log(chalk.cyan(" ╭"+d("═"))),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ║ ")+chalk.bold.white(" "+(e.name?.trim()||n("academy_title",r)))),console.log(chalk.cyan(" ║")),t)console.log(chalk.cyan(" ║ ")+chalk.white(" "+n(p?"welcome_new_demo":"welcome_new",r)));else{const e=new Date(o.lastSeenAt),t=Math.floor((Date.now()-e.getTime())/36e5),a=t<1?n("just_now",r):t<24?`${t}${n("ago_hours",r)}`:`${Math.floor(t/24)}${n("ago_days",r)}`;console.log(chalk.cyan(" ║ ")+chalk.white(` ${n("welcome_back",r)} ${a}`))}console.log(chalk.cyan(" ║"));const b=h(g?`${g.number}. ${g.name}`:"—",i()-14);console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("module",r).padEnd(11))+chalk.white(b)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("progress",r).padEnd(11))+y(a,l,Math.max(6,Math.min(20,i()-26-`${a}/${l}`.length)))+chalk.gray(` (${a}/${l})`)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("streak",r).padEnd(11))+u(o.streakDays)),s>0&&o.currentCard<=l&&console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("next_card",r).padEnd(11))+chalk.white(`#${o.currentCard} ${n("of",r)} ${l}`)),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ╰"+d("═"))),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────"));const m=0===a?n("continue_start",r):`${n("continue_resume",r)} ${o.currentCard}`;console.log(chalk.bold.green(" continue")+chalk.gray(" "+m)),console.log(chalk.yellow(" status")+chalk.gray(" "+n("status_full",r))),o.bookmarks.length>0&&console.log(chalk.yellow(" bookmarks")+chalk.gray(` ${o.bookmarks.length} ${n("bookmarks_desc",r)}`)),console.log(chalk.gray(" quit")+chalk.gray(" "+n("quit_desc",r))),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log()}function p(o,t){const r=c(),a=o.module,l=t.modules.find(e=>e.number===a),s=l?l.name:n("unknown_module",r),g=n("module",r).replace(":","").trim(),d=h(`${n("card",r)} ${o.number} / ${t.totalCards} · ${g} ${a} · ${s}`,i()-4);console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(d)+" "+chalk.cyan("─".repeat(Math.max(0,i()-e(d)-4))))}function b(){console.log(chalk.cyan(" ╰"+d())),console.log()}function m(e){console.log(chalk.cyan(" │ ")+chalk.white(e))}function f(){console.log(chalk.cyan(" │"))}const $=/[┌┐└┘├┤┬┴┼─│╔╗╚╝═║╠╣╦╩╬▓▒░█]/;function w(e){return/^(?: {4}|\t)/.test(e)||$.test(e)||/ {3,}/.test(e)}export function wrapMixed(o,n){const t=[];let r="",a=0,l=!1;for(const s of function(o){const n=[],t=o=>{let t="";const r=()=>{t&&(n.push({t:"atom",s:t}),t="")};for(const a of o)" "===a?(r(),n.push({t:"space"})):2===e(a)?(r(),n.push({t:"wide",s:a})):t+=a;r()},r=/`[^`]*`/g;let a,l=0;for(;null!==(a=r.exec(o));)t(o.slice(l,a.index)),n.push({t:"code",s:a[0]}),l=a.index+a[0].length;return t(o.slice(l)),n}(o)){if("space"===s.t){""!==r&&(l=!0);continue}const o=s.s,c=e("code"===s.t?o.replace(/`/g,""):o),g=l?1:0,i="atom"===s.t&&o.length<=2&&/^[.,;:!?)\]}…'"]+$/.test(o);""!==r&&!i&&a+g+c>n?(t.push(r),r=o,a=c,l=!1):(l&&(r+=" ",a+=1,l=!1),r+=o,a+=c)}return r&&t.push(r),t}const k=/\b(?:Common bug|Common mistake|Common error|Pitfall|Gotcha|Footgun)\b[::]?|\b(?:Warning|Caution)\b[::]|(?:常见错误|常见陷阱|常见误区|陷阱|注意|警告|误区)[::]/gi;function _(e,o){const n=o?chalk.bold.white:chalk.white;let t,r="",a=0;for(k.lastIndex=0;null!==(t=k.exec(e));)t.index>a&&(r+=n(e.slice(a,t.index))),r+=chalk.bold.yellow(t[0]),a=t.index+t[0].length;return a<e.length&&(r+=n(e.slice(a))),r}function x(e,o=!1){let n="",t=0;const r=/`([^`]*)`/g;let a;for(;null!==(a=r.exec(e));)a.index>t&&(n+=_(e.slice(t,a.index),o)),n+=chalk.cyan(a[1]),t=a.index+a[0].length;return t<e.length&&(n+=_(e.slice(t),o)),n}function C(e,o=!1){let n=!1;for(const t of e.split("\n"))if(/^\s*```/.test(t))n=!n;else if(n)console.log(chalk.cyan(" │ ")+chalk.cyan(` ${t}`));else if(""!==t)if(w(t))console.log(chalk.cyan(" │ ")+chalk.white(t));else for(const e of wrapMixed(t,i()-4))console.log(chalk.cyan(" │ ")+` ${x(e,o)}`);else f()}function v(e,o){const n=[];for(const t of e.split("\n")){if(""===t){n.push("");continue}if(w(t)){n.push(t);continue}let e="";for(const r of t.split(" "))(e+" "+r).trim().length>o?(n.push(e.trim()),e=r):e=(e+" "+r).trim();e&&n.push(e)}return n}let M=!1;export function setLearnAiEnabled(e){M=e}let S=new Set;export function setLearnPqsViewCards(e){S=e}let A=new Map;export function setLearnHonorRanks(e){A=e}const W=["①","②","③","④","⑤","⑥","⑦","⑧","⑨","⑩"];let j=[];export function setLearnReplies(e){j=e}let z=[];export function setLearnFaqs(e){z=e}let L=null;export function setLearnHometask(e){L=e}let R="full";export function setLearnFooterProfile(e){R=e}let P=null;export function setLearnFooterExtra(e){P=e}function q(){const e=P?.();e&&console.log(chalk.gray(" "+e))}let E=null;export function setLearnPracticeChips(e){E=e}let T=!1;export function setLearnShellLane(e){T=e}let N=!1,O=new Map;export function setLearnArenaCards(e){O=new Map(e.map(e=>[e.card,e.slot]))}export function setLearnArenaLane(e){N=e}export function learnCheckpointVerb(){return D?"duck":null}let D=!1;export function setLearnDuckLane(e){D=e}let F=null;export function setLearnPracticeCount(e){F=e}export function renderHonorWall(e){const o=c().startsWith("zh"),{totals:n,chapters:t}=e,r=n.gold+n.silver+n.bronze,a=(e,o)=>o>0?chalk.bold.white(`${e}×${o}`):chalk.gray(`${e}×0`);if(console.log(chalk.gray(" ─────────────────────────────────────────")),console.log(chalk.bold.white(o?" 🏅 奖牌墙 ":" 🏅 Medal wall ")+a("🥇",n.gold)+chalk.gray(" · ")+a("🥈",n.silver)+chalk.gray(" · ")+a("🥉",n.bronze)+chalk.gray(o?" (永久 — 挂上墙就不会摘下)":" (permanent — once earned, never removed)")),0===r){const n=null!=e.floor?`${e.floor}+`:o?"高分":"a high score";return console.log(chalk.gray(o?` 还没有奖牌 — 单卡提问评分 ${n} 并进入该卡前三,即可永久挂墙 🥇🥈🥉`:` No medals yet — score ${n} on a card and hold its top-3 to hang one 🥇🥈🥉`)),void console.log(chalk.gray(" ─────────────────────────────────────────"))}const l=t.map(e=>{const o=`Ch${e.chapter}`,n=(e.gold>0?`🥇${e.gold}`:"")+(e.silver>0?`🥈${e.silver}`:"")+(e.bronze>0?`🥉${e.bronze}`:"");return n?chalk.white(`${o} ${n}`):chalk.gray(`${o} —`)}).join(chalk.gray(" "));console.log(" "+l);const s=t.flatMap(e=>e.cards.map(o=>({...o,chapter:e.chapter})));s.sort((e,o)=>e.rank-o.rank||e.chapter-o.chapter||e.card-o.card);for(const e of s.slice(0,24)){const n=(e.since||"").slice(0,10);console.log(chalk.white(` ${e.icon} `)+chalk.gray(`Ch${e.chapter} · `)+chalk.white(o?`第 ${e.card} 张`:`card ${e.card}`)+chalk.gray(` · ${e.score}`)+(n?chalk.gray(o?` · 始于 ${n}`:` · since ${n}`):""))}s.length>24&&console.log(chalk.gray(o?` …还有 ${s.length-24} 枚`:` …and ${s.length-24} more`)),console.log(chalk.gray(" ─────────────────────────────────────────"))}export function renderKnowledgeCard(t,r){const d=c(),h=o(t,d);p(h,r),f();const y=wrapMixed(h.title,i()-4);for(const e of y)m(chalk.bold.yellow(e));m(chalk.gray("─".repeat(Math.min(Math.max(...y.map(o=>e(o))),i()))));const u=!!(t.chart_ids&&t.chart_ids.length>0);if(u&&a(t.chart_ids.slice(0,1),r.charts,l()),h.key_point&&""!==h.key_point.trim()?(u||f(),function(e,o,n){const t=c().startsWith("zh"),r=l().cols,a="-".repeat(Math.min(i(),Math.max(24,r-6))),s=t?"要点":"KEY POINT",g=t?`第 ${o} / ${n} 张`:`Card ${o} / ${n}`;console.log(chalk.cyan(" │ ")+chalk.green(a)),console.log(chalk.cyan(" │ ")+chalk.green(`${s} · ${g}`));for(const o of wrapMixed(e,i()-4))console.log(chalk.cyan(" │ ")+chalk.bold.green(" "+o));console.log(chalk.cyan(" │ ")+chalk.green(a)),f()}(h.key_point,h.number,r.totalCards)):u||f(),h.body.filter(e=>null!=e&&""!==e.trim()).forEach((e,o)=>{const n=0===o?e.match(/^([\s\S]*?(?:[.!?](?=\s|$)|[。!?]))(\s[\s\S]*)?$/):null;if(n&&n[1].trim().length<=180){C(n[1],!0);const e=n[2]?.trimStart();e&&C(e,!1)}else C(e,!1);f()}),h.icoaConnection){m(chalk.magenta(" "+n("icoa_connection",d))),m(chalk.gray(" "+"─".repeat(20)));for(const e of wrapMixed(h.icoaConnection,i()-4))m(" "+chalk.magenta(e));f()}if(t.check){const e=d.startsWith("zh"),o=t._zh?.checkStatement??t._zh?.check?.statement,n=e?o??t.check.statement:t.check.statement;for(const o of wrapMixed(e?"请回答 (输 y 或 n):":"Quick comprehension check (type y or n):",i()-8))m(chalk.cyan(" 🤔 "+o));m(chalk.gray(" "+"─".repeat(20)));for(const e of wrapMixed(n,i()-4))m(" "+chalk.white(e));f()}b(),function(o,n){const t=c().startsWith("zh"),r=l().cols,a="-".repeat(Math.min(i(),Math.max(24,r-6))),d=chalk.gray(" ");if("minimal"===R){const e=E?E(o.number):null;console.log(chalk.gray(" "+a)),console.log(chalk.gray(" "+(t?`第 ${o.number} / ${n.totalCards} 张`:`Card ${o.number} / ${n.totalCards}`))+(e?chalk.gray(t?" · 本卡练习 ":" · practice ")+e:""));const r=[];if(o.check?(r.push(chalk.bold.green("[y]")+chalk.gray(t?" 是":" yes")),r.push(chalk.bold.green("[n]")+chalk.gray(t?" 否":" no"))):r.push(chalk.bold.green("[ok]")+chalk.gray(t?" 下一张":" next")),e){const o=Math.max(1,(e.match(/\[\d\]/g)??[]).length),n=Array.from({length:o},(e,o)=>`[${o+1}]`).join("");r.push(chalk.bold.cyan(n)+chalk.gray(t?" 练习":" practice"))}T&&r.push(chalk.bold.magenta("[sh]")+chalk.gray(t?" 实验台":" shell"));const l=O.get(o.number);return N&&l?r.push(chalk.bold.magenta(`[contest ${l}]`)+chalk.gray(t?" 检查点":" checkpoint")):N&&0===O.size&&r.push(chalk.bold.magenta("[battle]")+chalk.gray(t?" 进竞技场":" arena")),D&&r.push(chalk.bold.magenta("[duck]")+chalk.gray(t?" 跑仿真":" sim bench")),o.flow&&o.flow.length>0&&r.push(chalk.bold.magenta("[flow]")+chalk.gray(t?" 互动走一遍":" walk it")),r.push(chalk.bold.cyan("[b]")+chalk.gray(t?" 上一张":" back")),r.push(chalk.bold.yellow("[e]")+chalk.gray(t?" 求助":" help")),r.push(chalk.gray("[quit]")+(t?chalk.gray(" 退出"):"")),console.log(" "+r.join(d)),q(),void console.log()}const h=[];o.check?(h.push(chalk.bold.green("[y]")+chalk.gray(t?" 是":" yes")),h.push(chalk.bold.green("[n]")+chalk.gray(t?" 否":" no"))):h.push(chalk.bold.green("[ok]")+chalk.gray(t?" 继续":" next")),o.flow&&o.flow.length>0&&h.push(chalk.bold.magenta("[flow]")+chalk.gray(t?" 互动走一遍":" walk it")),M&&(h.push(chalk.bold.magenta("[/ai]")+chalk.gray(t?" 🐼 熊猫导师":" 🐼 Panda tutor")),h.push(chalk.bold.magenta("[/submit]")+chalk.gray(t?" 提问评分":" prompt score")));const y=A.get(o.number);var u;h.push(chalk.bold.magenta(y?`[/honors ${u=y,W[u-1]??`#${u}`}]`:"[/honors]")+chalk.gray(t?" 排行":" board")),"normal"===g?h.push(chalk.bold.cyan("[wide]")+chalk.gray(t?" 宽屏":" widen")):("wide"===g?h.push(chalk.bold.cyan("[wider]")+chalk.gray(t?" 更宽":" wider")):"wider"===g?(h.push(chalk.bold.cyan("[widest]")+chalk.gray(t?" 最宽":" widest")),h.push(chalk.bold.cyan("[wide]")+chalk.gray(t?" 回宽屏":" back to wide"))):h.push(chalk.bold.cyan("[wider]")+chalk.gray(t?" 回 wider":" back to wider")),h.push(chalk.bold.cyan("[normal]")+chalk.gray(t?" 标准":" standard")));const p=s(j,z,o.number,t),b=p.startsWith("💬");if(h.push((b?chalk.bold.yellow("[r]"):chalk.gray("[r]"))+(b?chalk.bold.white(` ${p}`):chalk.gray(` ${p}`))),h.push(chalk.bold.yellow("[e]")+chalk.gray(t?" 提问或报告":" ask or report")),L){const e=L,o=`${e.done}/${e.total}`,n=e.completedAt?chalk.bold.green:chalk.bold.cyan;h.push(n("[task]")+chalk.gray(t?` 本周 ${o}`:` week ${o}`))}h.push(chalk.bold.cyan("[b]")+chalk.gray(t?" 上一张":" back")),h.push(chalk.bold.cyan("[card N]")+chalk.gray(t?" 跳转":" jump")),h.push(chalk.gray("[reload]")+(t?chalk.gray(" 刷新"):"")),h.push(chalk.gray("[quit]")+(t?chalk.gray(" 退出"):""));const m=e(a);console.log(chalk.gray(" "+a)),console.log(chalk.gray(" "+(t?`第 ${o.number} / ${n.totalCards} 张`:`Card ${o.number} / ${n.totalCards}`)));let f="",$=0;for(const o of h){const n=e(o);f&&$+2+n>m?(console.log(" "+f),f=o,$=n):(f=f?f+d+o:o,$=f===o?n:$+2+n)}f&&console.log(" "+f),console.log(" "+chalk.magenta("✍ ")+chalk.magenta("[/py]")+chalk.gray(t?" Python 草稿本":" Python scratchpad")+d+chalk.magenta("[/pylist]")+chalk.gray(t?" 我的草稿":" my files")),console.log(chalk.gray(" "+a));const w=[t?"e = 就这张卡提问,或报告问题":"e = ask about this card, or report a problem",t?"更多: bookmark / unbookmark · status · lang zh/en · link":"more: bookmark / unbookmark · status · lang zh/en · link"];for(const e of w)for(const o of wrapMixed(e,m))console.log(chalk.gray(" "+o));q(),console.log()}(t,r)}export function renderCheckFeedback(e,o,n,t=!1){const r=c().startsWith("zh");if(console.log(),n)console.log(chalk.green(" ✓ ")+chalk.white(r?"答对了。":"Correct."));else if(console.log(chalk.yellow(" ✗ ")+chalk.white(r?"记下了,继续。":"Noted — moving on.")),t){const o="y"===e.check?.answer?r?"是":"Yes":r?"否":"No";console.log(chalk.gray(" "+(r?"正确答案: ":"Answer: ")+o+". "+(r?"回看上面那段。":"Re-read the paragraph above.")))}const a=r?e._zh?.solution??e.solution:e.solution;if(a&&a.length>0){console.log(),console.log(chalk.cyan(" 📝 "+(r?"解答:":"Worked solution:")));for(const e of a){for(const o of wrapMixed(e,i()-4))console.log(chalk.white(" "+o));console.log()}}console.log()}function I(e){return[chalk.white(" "+(e?"编著:Charlie Zhu":"Created & edited by Charlie Zhu")),chalk.gray(" "+(e?"ICOA 2026 创始人 · 课程总架构师":"Founder & Chief Curriculum Architect · ICOA 2026"))]}export function renderPhaseComplete(e,o,n,t,a,l){const s=c().startsWith("zh"),[g,d]=n.cardRange,h=o.checkResults||{};let y=0,u=0;for(let e=g;e<=d;e++){const o=h[String(e)];o&&(y++,o.correct&&u++)}const p="-".repeat(48);console.log();for(const e of r(" "))console.log(e);console.log(),console.log(chalk.bold.yellow(" "+p)),console.log(chalk.bold.yellow(" "+(s?`阶段 ${n.number} 完成`:`Phase ${n.number} complete`))+chalk.gray(" · ")+chalk.white(n.name));const b=y>0&&u===y?chalk.bold.green:chalk.bold.white;console.log(chalk.gray(" "+(s?"理解检查得分: ":"comprehension score: "))+b(`${u}/${y}`)+chalk.gray(0===y?s?" (本阶段未作答 y/n)":" (no y/n answered)":""));const m=String(d).length;let f=!1,$=0,w=0,k=0,_=0;for(let o=g;o<=d;o++){const n=e.cards.find(e=>e.number===o);if(!n)continue;const t=n,r=t.title??t.badge??"",a=(s?t._zh?.title??t._zh?.badge??r:r)||"",l=null!=n.check,c=h[String(o)];let g;g=c?c.correct?chalk.green("✓"):chalk.red("✗"):l?chalk.gray("○"):chalk.gray("·");const d=F?F(o):null;d&&(f=!0);const y=d?(d.solved>=d.total?chalk.green:d.solved>0?chalk.yellow:chalk.gray)(` ⚔${d.solved}/${d.total}`):"";d&&($++,d.solved>=d.total&&w++,k+=d.solved,_+=d.total);const u=wrapMixed(a,i()-10-m-(d?6:0)),p=u[0]+(u.length>1?"…":"");console.log(chalk.gray(` ${String(o).padStart(m)} `)+g+y+chalk.white(" "+p))}if(console.log(chalk.gray(s?" ✓ 答对 · ✗ 答错 · ○ 未作答 · 无 y/n 的卡不计分":" ✓ correct · ✗ wrong · ○ unanswered · cards without a y/n are unscored")),f&&console.log(chalk.bold.cyan(" ⚔ "+(s?"卡片练习: ":"practice: "))+(w>=$?chalk.bold.green:chalk.bold.white)(`${w}/${$}`)+chalk.gray(s?` 卡全完成 · ${k}/${_} 题 AC · 行内 ⚔已过/共(绿 完成 · 黄 部分 · 灰 未碰)`:` cards complete · ${k}/${_} problems AC · ⚔solved/total per row (green done · yellow partial · gray untouched)`)),a){const e=e=>"green"===e?chalk.bold.green:"blue"===e?chalk.bold.cyan:"yellow"===e?chalk.bold.yellow:chalk.bold.red;console.log(chalk.bold.magenta(" ⭐ "+(s?"提问得分 (best): ":"Prompt scores (best): "))+(0===a.length?chalk.gray(s?"本阶段尚未提问 — 用 /ai 研究,再 /submit 评分":"none this phase — research with /ai, then /submit"):a.map(o=>chalk.gray((s?"卡":"#")+o.card+" ")+e(o.tier)(String(o.score))).join(chalk.gray(" · "))))}if(l&&!l.configured){const o=d-g+1>12,n="fill"===e.practiceMode;console.log(chalk.gray(o?s?" 🔒 本阶段通关标准制作中 — 很快更新(360 层第二阶段标准设计中)":" 🔒 Pass criteria for this phase are in production — coming soon (360-tier second-stage standard in design)":n?s?" 🔒 本阶段 3 场判分赛制作中 — 很快更新;赛场挂在卡 4 / 8 / 12,3 场全过解锁下一阶段(本轨无 research)":" 🔒 This phase’s 3 judged arenas are in production — coming soon; they hang on cards 4 / 8 / 12 and all 3 unlock the next phase (no research on this track)":s?" 🔒 本阶段通关标准制作中 — 很快更新(标准范式:3 场练习赛 + 3 卡研究)":" 🔒 Pass criteria for this phase are in production — coming soon (standard: 3 arenas + 3 research cards)"))}if(l?.configured&&l.contest){const e=l.contest;if(e.slots&&e.slots.length>0)for(const o of e.slots){const e=chalk.bold.cyan(` ⚔ ${s?"练习赛":"arena"} ${n.number}-${o.slot}: `);o.passed?console.log(e+chalk.bold.green(`${o.best_ac}/${o.n} AC ✅`)):0===o.attempts?console.log(e+chalk.white(s?`${o.n} 题 · 过 ${o.need} 题 — `:`${o.n} problems · AC ${o.need} — `)+chalk.bold.green(`contest ${o.slot}`)):console.log(e+chalk.yellow(s?`最好 ${o.best_ac}/${o.n}(需 ${o.need})· 已试 ${o.attempts} 次 — `:`best ${o.best_ac}/${o.n} (need ${o.need}) · ${o.attempts} attempt(s) — `)+chalk.bold.green(`contest ${o.slot}`))}else{const o=chalk.bold.cyan(" ⚖ "+(s?"判分赛: ":"Contest: "));e.passed?console.log(o+chalk.bold.green(`${e.best_ac}/${e.n} AC ✅ `+(s?"已过关":"passed"))):0===e.attempts?console.log(o+chalk.white(s?`${e.n} 题 · 过 ${e.need} 题解锁 — 输入 `:`${e.n} problems · AC ${e.need} to pass — type `)+chalk.bold.green("contest")+chalk.white(s?" 开始":" to start")):console.log(o+chalk.yellow(s?`最好 ${e.best_ac}/${e.n} AC(需 ${e.need})· 已试 ${e.attempts} 次 — 输入 `:`best ${e.best_ac}/${e.n} AC (need ${e.need}) · ${e.attempts} attempt(s) — type `)+chalk.bold.green("contest")+chalk.yellow(s?" 再战":" to retry"))}}t&&console.log(chalk.gray(" "+(s?"下一阶段: ":"next: "))+chalk.bold.cyan(s?`阶段 ${t.number}`:`Phase ${t.number}`)+chalk.gray(" · ")+chalk.white(t.name)),console.log(chalk.bold.yellow(" "+p));for(const e of I(s))console.log(e)}export function renderCurriculumComplete(e,o){const n=c().startsWith("zh"),t=o.checkResults||{},a="-".repeat(48);let l=0,s=0;console.log();for(const e of r(" "))console.log(e);console.log(),console.log(chalk.bold.green(" "+a)),console.log(chalk.bold.green(" 🏆 "+(n?`${e.name} — 全部完成!`:`${e.name} — complete!`))),console.log(chalk.bold.green(" "+a));for(const o of e.modules){const[e,r]=o.cardRange;let a=0,c=0;for(let o=e;o<=r;o++){const e=t[String(o)];e&&(a++,e.correct&&c++)}l+=a,s+=c;const g=a>0&&c===a?chalk.green:chalk.white;console.log(chalk.gray(" "+(n?`阶段 ${o.number}`:`Phase ${o.number}`).padEnd(8))+g(`${c}/${a}`.padEnd(7))+chalk.gray(o.name))}console.log(chalk.bold.green(" "+a)),console.log(chalk.bold.green(" "+(n?"总分 / overall: ":"Overall: "))+chalk.bold.white(`${s}/${l}`)),console.log(chalk.bold.green(" "+a));for(const e of I(n))console.log(e);console.log()}export function renderMCQCard(e,t){const r=c(),a=o(e,r);p(a,t),f(),m(chalk.bold.yellow("🎯 "+a.title)),m(chalk.gray("─".repeat(Math.min(a.title.length+4,i())))),f();for(const e of v(a.question,i()-4))m(" "+e);f();for(const e of["A","B","C","D"])m(chalk.cyan(` ${e}.`)+" "+chalk.white(a.options[e]));f(),b(),console.log(chalk.gray(" "+(r.startsWith("zh")?"输入 ":"Type "))+chalk.bold.green("A")+chalk.gray(" / ")+chalk.bold.green("B")+chalk.gray(" / ")+chalk.bold.green("C")+chalk.gray(" / ")+chalk.bold.green("D")+chalk.gray(" "+n("type_to_answer",r))),console.log(chalk.gray(" ")+chalk.bold.cyan("back")+chalk.gray(r.startsWith("zh")?" 上一张 · ":" previous card · ")+chalk.bold.cyan("card N")+chalk.gray(r.startsWith("zh")?" 跳转":" jump")),console.log()}export function renderMCQFeedback(e,t,r,a){const l=c(),s=o(e,l);console.log(),r?console.log(" "+chalk.bold.green(n("correct",l)+" ")+chalk.gray(`${n("one_point",l)} · ${t} = ${s.options[t]}`)):console.log(" "+chalk.bold.red(n("not_quite",l)+" ")+chalk.gray(`${n("you_chose",l)} ${t}; ${n("answer_is",l)} ${s.answer}.`)),console.log(),console.log(chalk.gray(" "+n("explanation",l)));for(const e of v(s.explanation,i()-4))console.log(chalk.gray(" "+e));const g=Object.values(a.mcqResults),d=g.filter(e=>e.correct).length;console.log(),console.log(chalk.gray(" "+n("mcq_accuracy_so_far",l)+" ")+chalk.white(`${d}/${g.length}`)),console.log(),r||(console.log(chalk.gray(" "+n("press_ok_continue",l)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",l))),console.log())}export function renderPracticalCard(e,t){const r=c(),a=o(e,r);p(a,t),f(),m(chalk.bold.yellow("🛠 "+a.title)),m(chalk.gray("─".repeat(Math.min(a.title.length+4,i())))),f();for(const e of v(a.task,i()-4))m(" "+e);if(a.starterCode){f(),m(chalk.gray(" "+n("starter_code",r))),f();for(const e of a.starterCode.split("\n"))m(" "+chalk.cyan(e))}f(),b(),console.log(chalk.gray(" "+n("try_in_sandbox",r)+" ")+chalk.bold.cyan("!python3")+chalk.gray(" "+n("drops_into_python",r))),console.log(chalk.gray(" "+n("when_done",r))),console.log(chalk.gray(" ")+chalk.bold.green("done")+chalk.gray(" "+n("done_desc",r))),console.log(chalk.gray(" ")+chalk.bold.yellow("skip")+chalk.gray(" "+n("skip_desc",r))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log()}export function renderPracticalSuccess(e){const t=c(),r=o(e,t);console.log(),console.log(" "+chalk.bold.green(n("practical_recorded",t))),console.log(),console.log(chalk.gray(" "+n("reference_answer",t)));for(const e of v(r.successHint,i()-4))console.log(chalk.gray(" "+e));console.log(),console.log(chalk.gray(" "+n("press_ok_continue",t)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",t))),console.log()}export function renderSimDemoCard(e,t){const r=c(),a=o(e,r);p(a,t),f(),m(chalk.bold.yellow("🎬 "+a.title)),f();for(const e of v(a.description,i()-4))m(" "+e);f(),m(chalk.gray(" "+n("sim_requires",r))),f(),b(),console.log(chalk.gray(" ")+chalk.bold.cyan("sim")+chalk.gray(" "+n("sim_launch_desc",r))),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",r))),console.log(chalk.gray(" bookmark "+n("bookmark_desc",r))),console.log(chalk.gray(" back "+n("back_desc",r))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",r))),console.log()}export function renderArenaTaskCard(e,n){const t=c(),r=t.startsWith("zh"),a=o(e,t);p(a,n),f(),m(chalk.bold.cyan("🏟 "+a.title)),m(chalk.gray(" "+a.origin+" · "+a.domain+" · "+a.metric)),f();for(const e of a.statement){for(const o of v(e,i()-4))m(" "+o);f()}m(chalk.bold.green(r?" 核心要点 — baseline → 解法的关键变化":" CORE DELTA — baseline → solution"));for(const e of a.core_delta)for(const o of v("• "+e,i()-6))m(" "+chalk.white(o));f(),m(chalk.gray((r?" 提交格式: ":" Submission: ")+a.submission_format)),a.hints&&m(chalk.gray(r?" 本题有 5 级提示 (a–e),进入 arena 后逐级解锁。":" 5 progressive hints (a–e) unlock inside the arena.")),f(),b(),console.log(chalk.gray(" ")+chalk.bold.cyan("arena "+a.task_id)+chalk.gray(r?" 动手做这道题(下载 / 提示 / 提交 / 榜单)":" open the arena (download / hints / submit / board)")),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(r?" 继续下一张":" continue to next")),console.log(chalk.gray(" back "+(r?"上一张":"previous"))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+(r?"退出":"exit"))),console.log()}export function renderComingSoonCard(e,n){const t=c(),r=t.startsWith("zh"),a=o(e,t);p(a,n),f(),m(chalk.bold.yellow("🔜 "+a.title)),f();const l=a.note||(r?"这道竞技场题目正在编写中,敬请期待。":"This arena task is coming soon.");for(const e of v(l,i()-4))m(" "+chalk.gray(e));f(),b(),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(r?" 继续":" continue")),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+(r?"退出":"exit"))),console.log()}export function renderMilestone(e,t){const r=c(),a=o(e,r),l=e.number===t.totalCards;console.log(),console.log(chalk.bold.yellow(" ╭"+d("═"))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+chalk.white(" "+n("milestone_header",r))),console.log(chalk.bold.yellow(" ║"));const s=` ${a.badge} ${a.emoji}`;console.log(chalk.bold.yellow(" ║ ")+chalk.bold.green(s)),console.log(chalk.bold.yellow(" ║ ")+chalk.gray(" ─".repeat(Math.max(1,Math.floor(a.badge.length/2+2))))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("in_wild_corresponds",r)));for(const e of v(a.realWorldLevel,i()-4))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(e));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("whats_next",r)));for(const e of v(a.unlockedNext,i()-4))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(e));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ╰"+d("═"))),console.log(),l?(console.log(chalk.gray(" "+n("demo_complete",r))),console.log(),console.log(chalk.gray(" "+n("unlock_full",r)+" ")+chalk.white(n("curriculum_name",r))+chalk.gray(",")),console.log(chalk.gray(" "+n("contact_team_leader",r)+" ")+chalk.bold.yellow("EA")+chalk.gray(" "+n("learn_token",r))),console.log(chalk.gray(" "+n("or_email",r)+" ")+chalk.cyan("australia@icoa2026.au")+chalk.gray(" "+n("for_partnership",r))),console.log(),console.log(chalk.gray(" "+n("type_quit",r)+" ")+chalk.bold.green("quit")+chalk.gray(" "+n("to_exit",r)+" ")+chalk.bold.green("status")+chalk.gray(" "+n("for_dashboard",r)))):(console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",r))),console.log(chalk.gray(" status "+n("status_full",r))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",r)))),console.log()}export function renderStatus(o,t,r={}){const a=c(),l=a.startsWith("zh"),s=t.cardsCompleted.length,g=o.totalCards,d=t.checkResults||{},p=r.gate??null,$=!!r.gated,w=r.focus??"current",k=r.posForSlot??(e=>4===e?10:4*e),_=e=>r.practiceCount?r.practiceCount(e):F?F(e):null,x=t.currentCard,C=o.modules.find(e=>x>=e.cardRange[0]&&x<=e.cardRange[1]),v=(o,n)=>o+" ".repeat(Math.max(0,n-e((e=>e.replace(/\x1b\[[0-9;]*m/g,""))(o))));console.log();const M=`${o.name} — ${l?"状态":"Status"}`;console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(M)+" "+chalk.cyan("─".repeat(Math.max(4,i()-2-e(M))))),f(),m(" "+chalk.gray(n("total_progress",a)+" ")+y(s,g)+chalk.gray(` (${s}/${g})`)+chalk.gray(" "+n("streak",a)+" ")+u(t.streakDays));{let n=0,a=0,s=0,c=0,g=0,h=0,y=!1;for(const e of o.modules){for(let t=e.cardRange[0];t<=e.cardRange[1];t++){const e=_(t);e&&(n++,e.solved>=e.total&&a++);const r=o.cards.find(e=>e.number===t);null!=r?.check&&(s++,d[String(t)]?.correct&&c++)}const t=p?.phases.find(o=>o.phase===e.number);if(t?.configured)for(const e of t.contest?.slots??[])g++,e.passed&&h++;t?.research&&(y=!0)}const u=(e,o)=>(e>=o?chalk.green:chalk.white)(`${e}/${o}`),b=[];n&&b.push(chalk.gray(l?"⚔ 练习卡 ":"⚔ practice cards ")+u(a,n)),g&&b.push(chalk.gray(l?"赛场 ":"arena ")+u(h,g)),s&&b.push(chalk.gray(l?"✓ 自测 ":"✓ checks ")+u(c,s)),b.length&&m(" "+b.join(chalk.gray(" "))),r._anyResearch=y;{let n=0;const r=[];let a=0,c=0,y=0;for(const e of o.modules)for(let l=e.cardRange[0];l<=e.cardRange[1];l++){c++,t.cardsCompleted.includes(l)&&a++;const e=o.cards.find(e=>e.number===l);null!=e?.check&&d[String(l)]&&y++;const s=_(l);s&&s.solved<s.total&&(n+=s.total-s.solved,r.push(l))}const u=l?"▸ 通关还差 ":"▸ to finish ",p=[];if(a<c&&p.push(chalk.gray(l?"卡片 ":"cards ")+chalk.white(""+(c-a))+chalk.gray(l?" 张未读":" unread")),s&&y<s&&p.push(chalk.gray("✓ ")+chalk.white(""+(s-y))+chalk.gray(l?" 题自测未答":" checks unanswered")),n>0){const o=l?`练习还差 ${n} 题`:`${n} practice problem${n>1?"s":""}`,t=l?"(卡 ":" (cards ",a=")",s=" │ ⚔ ",c=i()-e(s)-e(u)-e(o)-e(t+a),g=e=>{const o=[];let n=0;for(const t of r){const r=String(t).length+(o.length?3:0);if(n+r>e)break;n+=r,o.push(t)}return o};let d=g(c);d.length<r.length&&(d=g(c-e(` +${r.length}`))),!d.length&&r.length&&(d=[r[0]]);const h=r.length-d.length,y=d.join(" · ")+(h>0?` +${h}`:"");p.push(chalk.gray("⚔ ")+chalk.white(o)+chalk.gray(t+y+a))}g&&h<g&&p.push(chalk.gray(l?"赛 ":"arena ")+chalk.white(""+(g-h))+chalk.gray(l?" 场未过":" to pass"));const b=a>0;b&&p.length?p.forEach((o,n)=>m(" "+(0===n?chalk.bold.yellow(u):" ".repeat(e(u)))+o)):b&&!p.length&&m(" "+chalk.bold.green(l?"✅ 通关条件已全部达成":"✅ every completion gate met"))}}f();const S=e=>l?`阶段 ${e}`:`Phase ${e}`,A=Math.max(10,Math.min(28,i()-54));for(const e of o.modules){const[n,r]=e.cardRange,a=r-n+1,s=t.cardsCompleted.filter(e=>e>=n&&e<=r).length,c=$&&!!p&&e.number>p.unlocked_through_phase,g=p?.phases.find(o=>o.phase===e.number);let y=0,u=0,b=0;const f=[],M=[];let W=0,j=0,z=0;const L=[];for(let e=n;e<=r;e++){const n=o.cards.find(o=>o.number===e);if(null!=n?.check){y++;const o=d[String(e)];o?(u++,o.correct?b++:M.push(e)):f.push(e)}const t=_(e);t&&(W++,z+=t.solved,t.solved>=t.total?j++:L.push(`${e}:${t.solved}/${t.total}`))}const R=g?.configured?g.contest?.slots??[]:[],P=e=>e.passed?chalk.green("✅"):e.attempts>0?chalk.yellow("❌"):chalk.gray("⬜"),q=s>=a&&(0===y||b===y)&&(0===W||j===W)&&(0===R.length||R.every(e=>e.passed))&&(!g?.research||g.research.passed),E=s>0||u>0||z>0||R.some(e=>e.attempts>0),T=c?chalk.gray("🔒"):q?chalk.green("✅"):e.number===C?.number?chalk.bold.cyan("▶ "):E?chalk.yellow("▶ "):chalk.gray("⬜"),N=h(e.name,A),O=[];if(O.push((s>=a?chalk.green:chalk.white)(`${s}/${a}`)+chalk.gray(l?"卡":"c")),y>0&&O.push(chalk.gray("✓")+(b===y?chalk.green:chalk.white)(`${b}/${y}`)),W>0&&O.push(chalk.gray("⚔")+(j===W?chalk.green:chalk.white)(`${j}/${W}`)+chalk.gray(l?"卡":"c")),R.length>0&&O.push(chalk.gray(l?"赛":"arena ")+R.map(P).join("")),g?.research&&O.push(chalk.gray("⭐")+(g.research.passed?chalk.green:chalk.white)(`${g.research.good_cards}/${g.research.need_cards}`)),m(" "+T+" "+chalk.white(v(S(e.number),l?5:8))+chalk.gray(v(N,A+1))+O.join(chalk.gray(" · "))),c||"all"!==w&&w!==e.number&&("current"!==w||e.number!==C?.number)){if(!c&&E&&!q){const o=[],t=R.filter(e=>!e.passed).map(o=>(o.attempts>0?chalk.yellow:chalk.gray)(`${e.number}-${o.slot}@${l?"卡":"card "}${n+k(o.slot)-1}`));t.length&&o.push(chalk.gray(l?"赛 ":"arena ")+t.join(chalk.gray(" · ")));const r=(e,o)=>e.slice(0,8).join(o)+(e.length>8?chalk.gray(" +"+(e.length-8)):"");L.length&&o.push(chalk.gray("⚔ ")+chalk.white(r(L," "))),M.length&&o.push(chalk.red("✗ ")+chalk.white(r(M.map(String),","))),f.length&&o.push(chalk.gray("○ ")+chalk.white(r(f.map(String),","))),g?.research&&!g.research.passed&&o.push(chalk.gray(`⭐ ${g.research.good_cards}/${g.research.need_cards}`)),o.forEach((e,o)=>m(chalk.gray(0===o?l?" 待办: ":" todo: ":" ")+e))}}else{const t=String(r).length;for(let s=n;s<=r;s++){const r=o.cards.find(e=>e.number===s);if(!r)continue;const c=r,g=c.title??c.badge??"",h=(l?c._zh?.title??c._zh?.badge??g:g)||"",y=null!=r.check,u=d[String(s)],p=u?u.correct?chalk.green("✓"):chalk.red("✗"):y?chalk.gray("○"):chalk.gray("·"),b=_(s),f=b?(b.solved>=b.total?chalk.green:b.solved>0?chalk.yellow:chalk.gray)(`⚔${b.solved}/${b.total}`):" ",$=s===x?chalk.bold.cyan("▶"):" ",w=wrapMixed(h,Math.max(16,i()-14-t)),C=w[0]+(w.length>1?"…":"");m(` ${$} `+chalk.gray(String(s).padStart(t))+" "+p+" "+v(f,5)+" "+chalk.white(C));const M=12===a?R.find(e=>n+k(e.slot)-1===s):void 0;if(M){const o=chalk.gray(` ${" ".repeat(t)} ⚔ ${l?"练习赛":"arena"} ${e.number}-${M.slot}: `);M.passed?m(o+chalk.bold.green(`${M.best_ac}/${M.n} AC ✅`)):0===M.attempts?m(o+chalk.gray("⬜ ")+chalk.white(l?`未开始 · ${M.n} 题 · 过 ${M.need} 题 → `:`not started · ${M.n} problems · AC ${M.need} → `)+chalk.bold.green(`contest ${M.slot}`)):m(o+chalk.yellow(l?`❌ 最好 ${M.best_ac}/${M.n}(需 ${M.need})· 已试 ${M.attempts} 次 → `:`❌ best ${M.best_ac}/${M.n} (need ${M.need}) · ${M.attempts} attempt(s) → `)+chalk.bold.green(`contest ${M.slot}`))}}if(12!==a)for(const o of R){const n=chalk.gray(` ⚔ ${l?"练习赛":"arena"} ${e.number}-${o.slot}: `);o.passed?m(n+chalk.bold.green(`${o.best_ac}/${o.n} AC ✅`)):0===o.attempts?m(n+chalk.gray("⬜ ")+chalk.white(l?`未开始 · ${o.n} 题 · 过 ${o.need} 题 → `:`not started · ${o.n} problems · AC ${o.need} → `)+chalk.bold.green(`contest ${o.slot}`)):m(n+chalk.yellow(l?`❌ 最好 ${o.best_ac}/${o.n}(需 ${o.need})· 已试 ${o.attempts} 次 → `:`❌ best ${o.best_ac}/${o.n} (need ${o.need}) · ${o.attempts} attempt(s) → `)+chalk.bold.green(`contest ${o.slot}`))}g?.research&&!g.research.passed&&m(chalk.gray(l?` ⭐ 研究: ${g.research.good_cards}/${g.research.need_cards} 卡达 ${g.research.min_score} 分 — /ai 研究后 /submit`:` ⭐ research: ${g.research.good_cards}/${g.research.need_cards} cards at ${g.research.min_score}+ — /ai then /submit`)),g&&!g.configured&&m(chalk.gray(l?" 🔒 本阶段通关标准制作中 — 很快更新":" 🔒 pass criteria for this phase in production — coming soon"))}}f(),m(chalk.gray(l?" ✓ 答对 ✗ 答错 ○ 未答 · ⚔ 练习 已过/共 · 赛 ✅ 过 ❌ 试过 ⬜ 未碰"+(r._anyResearch?" · ⭐ 研究":""):" ✓ right ✗ wrong ○ unanswered · ⚔ practice solved/total"+(r._anyResearch?" · ⭐ research":""))),m(chalk.gray(l?" 展开某阶段: ":" arena ✅ passed ❌ tried ⬜ untouched · expand: ")+chalk.bold.green("status all")+chalk.gray(" / ")+chalk.bold.green("status N")),t.achievements.length>0&&m(" "+chalk.gray(n("achievements",a)+" ")+t.achievements.map(e=>chalk.bold.yellow("★ ")+chalk.white(e)).join(chalk.gray(" · "))),f(),b()}function B(e){try{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,e)}catch{}}export function renderArenaIntro(){const e=c().startsWith("zh"),o=learnCheckpointVerb(),n=!!process.stdout.isTTY&&!/^(C|POSIX)$/i.test(process.env.LANG||"")&&!process.env.ICOA_TEST_BOOT_MODE,t=e?[chalk.bold.yellow(" ⚔ 你读完 4 张卡了 — 前面是第一个检查点。"),chalk.white(" ⚔ 每 4 张卡有一场判分赛:")+chalk.bold.yellow("卡 4 / 8 / 12")+chalk.white(",一个阶段共 3 场。"),...o?[chalk.white(" · 敲 ")+chalk.bold.green(o)+chalk.white(" 进竞技场 — ")+chalk.bold.green("battle"===o?"play":"run")+chalk.white(" 本机试跑,")+chalk.bold.green("submit")+chalk.white(" 交给判分机;第一次 submit 时计时开始。")]:[chalk.white(" · 敲 ")+chalk.bold.green("contest 1")+chalk.white(" 开始 — 计时器同时开始,题目在赛场里读。")],chalk.white(" · 判分机会真的跑你的代码,不是对答案。"),chalk.white(" · 3 场全过 → 解锁下一阶段。"),chalk.gray(" ▸ 现在不想打?敲 ")+chalk.bold.green("next")+chalk.gray(" 继续读卡,赛场一直在那儿等你。")]:[chalk.bold.yellow(" ⚔ Four cards down — a checkpoint is waiting."),chalk.white(" ⚔ Every 4 cards there is a judged arena: ")+chalk.bold.yellow("cards 4 / 8 / 12")+chalk.white(", three per phase."),...o?[chalk.white(" · Type ")+chalk.bold.green(o)+chalk.white(" to enter the arena — ")+chalk.bold.green("battle"===o?"play":"run")+chalk.white(" to try it here, ")+chalk.bold.green("submit")+chalk.white(" to send it to the judge; the clock starts at your first submit.")]:[chalk.white(" · Type ")+chalk.bold.green("contest 1")+chalk.white(" to start — the clock starts with it; the problems are read inside.")],chalk.white(" · The judge really runs your code. It is not an answer key."),chalk.white(" · Pass all three → the next phase unlocks."),chalk.gray(" ▸ Not now? Type ")+chalk.bold.green("next")+chalk.gray(" and keep reading — the arena waits for you.")];console.log();for(const e of t)console.log(e),n&&B(200);console.log()}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import chalk from"chalk";import{getConfig as e}from"./config.js";import{learnPost as o}from"./learn-contest.js";export const TEAM_RE=/^\/?(?:teammeeting|team-meeting|meeting|tm)(?:\s+(.*))?$/i;const t=(e,t,n={})=>o(`/api/icoa/learn/team/${e}`,{token:t.toUpperCase(),...n},2e4);export function askOnReadline(e){return o=>new Promise(t=>{const n=o=>{e.off("close",a),t(o.trim())},a=()=>{e.off("line",n),t(null)};e.once("line",n),e.once("close",a),e.setPrompt(o),e.prompt()})}const n=()=>(e().language||"en").toLowerCase().startsWith("zh"),a=()=>Math.max(48,Math.min(process.stdout.columns||80,110)),s=e=>{let o=0;for(const t of e){const e=t.codePointAt(0)??0;o+=e>65535||e>=11904&&e<=42191||e>=44032&&e<=55203||e>=63744&&e<=64255?2:1}return o},r=(e,o)=>e+" ".repeat(Math.max(0,o-s(e))),l=new Set(["back","b","quit","q","exit"]);function i(e){const o=new Date(e);if(Number.isNaN(o.getTime()))return"";const t=String(o.getHours()).padStart(2,"0"),n=String(o.getMinutes()).padStart(2,"0");return(Date.now()-o.getTime())/864e5<6?`${["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][o.getDay()]} ${t}:${n}`:`${String(o.getMonth()+1).padStart(2,"0")}-${String(o.getDate()).padStart(2,"0")} ${t}:${n}`}function c(e,o){const t=[];let n="";for(const a of e.split(" "))n&&s(n)+1+s(a)>o?(t.push(n),n=a):n=n?`${n} ${a}`:a;return n&&t.push(n),t.length?t:[""]}const m=(e,o)=>e+(o?` ${o}`:"");function g(e,o){const t=m(e.emoji,e.suffix),n=(o?chalk.bold.cyan(r(t,11)):chalk.bold.white(r(t,11)))+chalk.gray(` #${String(e.id).padEnd(4)} ${i(e.ts)} `),s=" ".repeat(18+i(e.ts).length+2),l=a()-s.length-1;if(e.quote){const o=`↳ #${e.reply_to} ${m(e.quote.emoji,e.quote.suffix)}: ${e.quote.text}${e.quote.text.length>=60?"…":""}`;console.log(n+chalk.gray(o.slice(0,l)));for(const o of c(e.text,l))console.log(s+chalk.white(o))}else{const o=c(e.text,l);console.log(n+chalk.white(o[0]));for(const e of o.slice(1))console.log(s+chalk.white(e))}}function d(e){console.log(chalk.gray(n()?` 直接打字 = 发言 · re 12 <文字> 回复 #12 · 回车/new 刷新 · more 往前翻 · who 成员 · rules 版规 · back 离开 (${e})`:` type to post · re 12 <text> reply to #12 · Enter/new refresh · more older · who · rules · back to leave (${e})`))}function u(e){console.log(chalk.bold.white(n()?" 版规":" Board rules"));for(const o of e)console.log(chalk.gray(" · ")+chalk.white(o))}export async function runTeamMeeting(e,o){const s=n(),r=await t("status",e);if(!r.ok||!r.data)return console.log(chalk.yellow(function(e,o){const t=n();switch(e){case"needs_ftc":return t?" 队伍会议室凭 FTC 课程 token 进入 —— 先 redeem ftc-96,再回来敲 teammeeting。":" Team meeting opens with an FTC course token — redeem ftc-96 first, then type teammeeting again.";case"demo_not_supported":return t?" demo token 进不了队伍会议室。":" Demo tokens cannot enter a team meeting.";default:return" "+(o||(t?"连不上服务器。":"Could not reach the server."))}}(r.code,r.message))),"back";let i=r.data.member;const c=r.data.rules??[];if(!i){console.log(),console.log(chalk.bold.white(s?" 队伍会议室":" Team meeting")+chalk.gray(s?" —— 你们 FTC 队伍的纯文字留言板,全队和教练都看得到。":" — a text-only board for your FTC team, visible to the whole team and the coach.")),u(c),console.log(chalk.gray(s?" 每一步都可以敲 back 取消。":" Type back at any step to cancel.")),console.log();let n="",a="",g="";const d=r.data.emojis;for(let c=0;c<8;c++){if(!n){const e=await o(chalk.bold.cyan(s?" 队号(如 24961): ":" Team number (e.g. 24961): "));if(null===e||l.has(e.toLowerCase()))return"back";const t=e.replace(/\D/g,"");if(!t){console.log(chalk.gray(s?" 只要数字,例如 24961。":" Digits only, e.g. 24961."));continue}n=t}if(!a){console.log(chalk.gray(s?" 选一个 emoji 当你的头像(以后改不了):":" Pick your emoji — it leads your name and cannot be changed:"));const e=8;for(let o=0;o<d.length;o+=e)console.log(" "+d.slice(o,o+e).map((e,t)=>chalk.white(String(o+t+1).padStart(2))+" "+e).join(" "));r.data.reserved.length&&console.log(chalk.gray(` ${r.data.reserved.join(" ")} reserved`));const t=await o(chalk.bold.cyan(s?" 编号: ":" Number: "));if(null===t||l.has(t.toLowerCase()))return"back";const n=Number(t),i=Number.isInteger(n)&&n>=1&&n<=d.length?d[n-1]:d.includes(t)?t:"";if(!i){console.log(chalk.gray(s?` 敲 1-${d.length} 里的一个数字。`:` Type a number from 1 to ${d.length}.`));continue}a=i}const c=await o(chalk.bold.cyan(s?` ${a} 后面的后缀(≤6 个字母,别用真名;直接回车 = 不要后缀): `:` Suffix after ${a} (up to 6 letters, not your real name; Enter for none): `));if(null===c||l.has(c.toLowerCase()))return"back";g=c;const u=await t("join",e,{team_id:n,emoji:a,suffix:g});if(u.ok&&u.data){i=u.data.member,console.log(chalk.green(" ✓ ")+chalk.white(s?`你在队伍 ${i.team_id} 里是 ${m(i.emoji,i.suffix)} —— 这个身份不能再改。`:`You are ${m(i.emoji,i.suffix)} in team ${i.team_id} — this cannot be changed.`));break}if(console.log(chalk.yellow(" "+(u.message||"Could not join."))),"no_room"===u.code)n="";else if("bad_emoji"===u.code||"reserved"===u.code)a="";else{if("already_member"===u.code){const o=await t("status",e);i=o.data?.member??null;break}if("judge_unavailable"===u.code)return"back"}}if(!i)return"back"}const f=i.team_id,h=m(i.emoji,i.suffix),b=chalk.bold.magenta(`icoa meeting ${f}> `);let y=0,w=0;const p=e=>{for(const o of e)g(o,m(o.emoji,o.suffix)===h),(!w||o.id<w)&&(w=o.id),o.id>y&&(y=o.id)},$=async o=>{const n=await t("list",e,o);return n.ok&&n.data?n.data:(console.log(chalk.yellow(" "+(n.message||(s?"拉取失败。":"Could not load the board.")))),null)};console.log();const k=await $({limit:20});console.log(chalk.bold.white(` ${s?"队伍":"Team"} ${f} ${s?"会议室":"meeting"}`)+chalk.gray(` — ${s?"你是":"you are"} `)+chalk.bold.cyan(h)+chalk.gray(` · ${k?.members??"?"} ${s?"名成员":"members"} · ${s?"纯文字,全队可见":"text only, visible to the whole team"}`)),u(c),console.log(chalk.gray(" "+"─".repeat(a()-4))),k&&(k.messages.length?p(k.messages):console.log(chalk.gray(s?" 还没有留言 —— 你来开个头。":" No messages yet — start the board.")),y=Math.max(y,k.last_id)),console.log(chalk.gray(" "+"─".repeat(a()-4))),d(f);const x=async e=>{const o=await $({since_id:y,limit:50});o&&(o.messages.length?p(o.messages):e||console.log(chalk.gray(s?" (没有新留言)":" (nothing new)")))};for(;;){const n=await o(b);if(null===n)return"back";const a=n.trim(),r=a.toLowerCase();if(l.has(r))return"back";if("menu"===r||"home"===r)return"menu";if(""===r||"new"===r||"refresh"===r||"r"===r){await x(!1);continue}if("more"===r||"older"===r){const e=await $({before_id:w||void 0,limit:20});e&&(e.messages.length?p(e.messages):console.log(chalk.gray(s?" (到头了)":" (that is the beginning)")));continue}if("who"===r||"members"===r){const o=await t("who",e);o.ok&&o.data?console.log(chalk.gray(` ${o.data.members.length} ${s?"名成员":"members"}: `)+o.data.members.map(e=>m(e.emoji,e.suffix)).join(" ")):console.log(chalk.yellow(" "+(o.message||"Could not load members.")));continue}if("rules"===r){u(c);continue}if("help"===r||"?"===r){d(f);continue}let i,g=a;const h=a.match(/^(?:re|reply)\s+#?(\d+)\s+(.+)$/i)??a.match(/^#(\d+)\s+(.+)$/);if(h)i=Number(h[1]),g=h[2].trim();else if(/^(?:re|reply)\s+#?\d+$/i.test(a)||/^#\d+$/.test(a)){console.log(chalk.gray(s?" 回复要带内容:re 12 你的话":" A reply needs words: re 12 your text"));continue}const y=await t("post",e,{text:g,reply_to:i});if(!y.ok||!y.data){"muted"===y.code?console.log(chalk.yellow(s?" 你被暂时禁言了几分钟(连续两条被拦)。":" You are muted for a few minutes (two blocked messages in a row).")):console.log(chalk.yellow(" "+(y.message||(s?"没发出去。":"Not posted."))));continue}const k=y.data;"block"!==k.verdict?"drop"!==k.verdict?(k.redacted&&console.log(chalk.gray(s?` · 已发出,个人信息已用 ******* 遮掉(${k.redacted} 处)。`:` · Posted — personal details masked as ******* (${k.redacted}).`)),await x(!0)):console.log(chalk.gray(s?" · 没上墙 —— 留言板只放和队伍有关的事(日程、机器人、代码、鼓励)。":" · Not posted — the board is for team things (schedules, the robot, the code, encouragement).")):(console.log(chalk.yellow(s?" ✗ 这条没发出去 —— 读起来带攻击性。版规第一条:尊重。":" ✗ Not posted — that reads as hostile. Rule one: be respectful.")),k.muted_until&&console.log(chalk.yellow(s?" 已暂时禁言几分钟。":" You are muted for a few minutes.")))}}
|