icoa-cli 2.19.536 → 2.19.538
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/commands/type.js +1 -0
- package/dist/index.js +1 -1
- package/dist/lib/commands.js +1 -1
- package/dist/lib/learn-focus-log.js +1 -1
- package/dist/lib/learn-render.js +1 -1
- package/dist/lib/typing-pools.js +1 -0
- package/dist/lib/typing-test.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 o}from"../lib/config.js";import{httpFetch as e}from"../lib/http.js";import{LEARN_SERVER as t}from"../lib/learn-curricula.js";import{loadLearnState as s}from"../lib/learn-state.js";import{logCommand as r}from"../lib/logger.js";import{TYPING_DURATIONS as n,bestWpm as l,loadTypingHistory as p,parseTypingArgs as i,runTypingTest as y}from"../lib/typing-test.js";import{getCliVersion as a}from"../lib/version.js";async function c(o){const r=s(),n=r?.token;if(n&&!n.toUpperCase().endsWith("DEMO01")&&o.completed)try{await e(`${t.replace(/\/$/,"")}/api/icoa/learn/typing/result`,{method:"POST",headers:{"Content-Type":"application/json","X-ICOA-Client":a()},body:JSON.stringify({token:n.toUpperCase(),...o}),signal:AbortSignal.timeout(8e3)})}catch{}}export async function runType(e){const t=(o().language||"en").toLowerCase().startsWith("zh"),s=(e[0]||"").toLowerCase();if("history"===s||"list"===s||"log"===s||"best"===s)return void function(o){const e=p().filter(o=>o.completed);if(0!==e.length){console.log(chalk.bold.white(o?" ⌨ 最近成绩":" ⌨ Recent results"));for(const o of e.slice(-10).reverse())console.log(chalk.gray(` ${o.finished_at.slice(0,16).replace("T"," ")} `)+chalk.white(`${String(o.wpm).padStart(3)} WPM`)+chalk.gray(` ${o.accuracy}% ${o.pool} · ${o.seconds}s`));for(const e of["words","python","shell"]){const t=n.map(o=>`${o}s ${l(e,o)||"-"}`).join(" · ");console.log(chalk.gray(` ${o?"最好":"best"} ${e}: ${t}`))}}else console.log(chalk.gray(o?" 还没有成绩。敲 type 开始。":" No results yet. Type `type` to start."))}(t);if("help"===s||"?"===s)return void console.log(chalk.gray(t?" 用法:type [15|30|60] [words|python|shell] · type history":" usage: type [15|30|60] [words|python|shell] · type history"));const a=i(e);if("error"in a)return void console.log(chalk.yellow(t?` 不认识 ${a.error}。`:` Unknown ${a.error}.`)+chalk.gray(t?" 用法:type [15|30|60] [words|python|shell]":" usage: type [15|30|60] [words|python|shell]"));r(`type ${a.pool} ${a.seconds}`);const g=l(a.pool,a.seconds);console.log(),console.log(chalk.bold.cyan(t?" ⌨ 打字速度练习":" ⌨ Typing speed trainer")+chalk.gray(` ${a.pool} · ${a.seconds}s`+(g?t?` · 最好 ${g} WPM`:` · best ${g} WPM`:""))),console.log(chalk.gray(t?" 其他模式:type 15 · type 60 · type python · type shell · type history":" other modes: type 15 · type 60 · type python · type shell · type history")),await y({pool:a.pool,seconds:a.seconds,zh:t,onResult:c}),console.log(chalk.gray(t?" ▸ 再来一次:type · 成绩:type history":" ▸ again: type · results: type history")),console.log()}export function registerTypeCommand(o){o.command("type [args...]").description("Typing speed trainer: type [15|30|60] [words|python|shell]").action(async(o=[])=>{await runType(o)})}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as o}from"commander";import chalk from"chalk";import{registerCtfCommands as e}from"./commands/ctf.js";import{registerRefCommand as n}from"./commands/ref.js";import{registerShellCommand as s}from"./commands/shell.js";import{registerFilesCommand as r}from"./commands/files.js";import{registerViewCommand as t}from"./commands/view.js";import{registerConnectCommand as l}from"./commands/connect.js";import{registerChallengeCommand as i}from"./commands/challenge.js";import{registerDoctorCommand as a}from"./commands/doctor.js";import{registerNoteCommand as c}from"./commands/note.js";import{registerLogCommand as m}from"./commands/log.js";import{registerLangCommand as g}from"./commands/lang.js";import{registerSetupCommand as d}from"./commands/setup.js";import{registerEnvCommand as p}from"./commands/env.js";import{registerAienvCommand as y}from"./commands/aienv.js";import{registerIpynbCommand as h}from"./commands/ipynb.js";import{registerAi4ctfCommand as u}from"./commands/ai4ctf.js";import{registerExamCommand as f}from"./commands/exam.js";import{registerCtf4aiDemoCommand as w}from"./commands/ctf4ai-demo.js";import{registerThemeCommand as b}from"./commands/theme.js";import{registerLearnCommand as j}from"./commands/learn.js";import{registerCtf4VlaCommand as $}from"./commands/ctf4vla.js";import{registerDemo2Command as T}from"./commands/demo2.js";import{registerDemo4Command as v}from"./commands/demo4.js";import{registerDemo5Command as A}from"./commands/demo5.js";import{registerGfssmCommand as I}from"./commands/gfssm.js";import{registerSimCommand as E}from"./commands/sim.js";import{registerArenaCommand as C}from"./commands/arena.js";import{registerKaggleCommand as _}from"./commands/kaggle.js";import{registerSprintCommand as x}from"./commands/sprint.js";import{registerTeleloadCommand as S}from"./commands/teleload.js";import{registerBoardCommand as k}from"./commands/board.js";import{registerWmCommand as L}from"./commands/wm.js";import{getConfig as R,saveConfig as O}from"./lib/config.js";import{startRepl as F}from"./repl.js";import{RELAUNCH_CODE as N}from"./lib/menu-nav.js";import{setTerminalTheme as U}from"./lib/theme.js";import{cleanupTerminal as P}from"./lib/terminal-cleanup.js";import{checkForUpdates as M}from"./lib/update-check.js";import{detectIcoaInstalls as D,isWindowsBuildInWslShell as G}from"./lib/platform.js";import{ICOA_BIG as W}from"./lib/banner.js";import{readFileSync as H}from"node:fs";import{fileURLToPath as Y}from"node:url";import{dirname as J,join as V}from"node:path";const X=J(Y(import.meta.url)),q=JSON.parse(H(V(X,"..","package.json"),"utf-8")).version,z=chalk.cyan(" ─────────────────────────────────────────────────────"),K=`\n${z}\n\n${W.map(o=>` ${chalk.bold.white(o)}`).join("\n")}\n\n ${chalk.yellow("International Cyber Olympiad in AI 2026")}\n ${chalk.bold.magenta("The World's First AI-Native CLI Operating System")}\n ${chalk.bold.magenta("for Cybersecurity & AI Security Competition")}\n ${chalk.bold.magenta("and Olympiad for K-12")}\n\n ${chalk.green.bold("AI4CTF")} ${chalk.gray("[Day 1]")} ${chalk.white("AI as your teammate")}\n ${chalk.red.bold("CTF4AI")} ${chalk.gray("[Day 2]")} ${chalk.white("Challenge & evaluate AI systems")}\n ${chalk.cyan.bold("CTF4EAI")} ${chalk.gray("[Pioneer]")} ${chalk.white("Embodied AI security")}\n ${chalk.bold.yellow("AI is your teammate. AI is your target. AI is embodied.")}\n\n ${chalk.white("Sydney, Australia")} ${chalk.gray("Jun 27 - Jul 2, 2026")}\n ${chalk.cyan.underline("https://icoa2026.au")}\n\n ${chalk.gray(`CLI-Native Competition Terminal v${q}`)}\n\n${z}\n`;process.on("uncaughtException",o=>{"__REPL_NO_EXIT__"!==o.message&&(console.error(chalk.red("Error:"),o.message),process.exit(1))}),process.on("unhandledRejection",o=>{const e=o instanceof Error?o.message:String(o);"__REPL_NO_EXIT__"!==e&&(console.error(chalk.red("Error:"),e),process.exit(1))});{const o=process.argv.slice(2).some(o=>["-V","--version","-h","--help"].includes(o));if(!0===process.stdin.isTTY&&"1"!==process.env.ICOA_SUPERVISED&&!o){const{spawnSync:o}=await import("node:child_process");process.on("SIGINT",()=>{}),process.on("SIGTERM",()=>{});let e=0,n=!1;do{const s=n?process.argv.slice(1,2):process.argv.slice(1),r=o(process.execPath,s,{stdio:"inherit",env:{...process.env,ICOA_SUPERVISED:"1",...n?{ICOA_RELAUNCH:"1"}:{}}});if(r.signal){const{appendExitLog:o}=await import("./lib/exit-log.js");o("worker-signal",`signal=${r.signal}`),console.log(),console.log(chalk.yellow(` ⚠ icoa was stopped by signal ${r.signal}`)+chalk.gray(" — another process killed it (not a crash). Run icoa --resume to continue.")),e=0;break}if(e=r.status,0!==e&&e!==N){const{appendExitLog:o}=await import("./lib/exit-log.js");o("worker-exit",`code=${e}`);const n=null!==e&&e>128?e-128:0;console.log(),console.log(n?chalk.yellow(` ⚠ icoa was stopped by signal ${n}`)+chalk.gray(" — another process killed it (not a crash). Run icoa --resume to continue."):chalk.yellow(` ⚠ icoa exited unexpectedly (code ${e})`)+chalk.gray(" — see ~/.icoa/exit.log · run icoa --resume to continue."))}n=!0}while(e===N);P(),process.exit(e??0)}}const B=new o;if(B.name("icoa").version(q).description("ICOA CLI — CLI-Native CTF Competition Terminal").option("--resume","Resume previous session").option("--continue","Continue where you left off — jump straight to your last learn card / exam").action(async o=>{const e=R();U("high-contrast"===e.themeVariant?"high-contrast":"dark"),M(),function(){const o=D();if(o.length<=1)return;const e=o[0];if([...o.map(o=>o.version||"0.0.0")].sort((o,e)=>function(o,e){const n=o.split(".").map(o=>parseInt(o,10)||0),s=e.split(".").map(o=>parseInt(o,10)||0);for(let o=0;o<3;o++)if((n[o]||0)!==(s[o]||0))return(n[o]||0)-(s[o]||0);return 0}(e,o))[0]!==e.version){console.log(),console.log(chalk.yellow.bold(" ⚠ Multiple icoa installations detected on PATH:"));for(let e=0;e<o.length;e++){const n=o[e],s=n.version?`v${n.version}`:"(version unreadable)",r=0===e?chalk.yellow("→"):" ",t=0===e?chalk.gray(" ← currently running (older — shadowing newer install)"):chalk.gray(" ← shadowed");console.log(` ${r} ${chalk.cyan(n.path.padEnd(28))} ${chalk.white(s)}${t}`)}console.log(),console.log(chalk.yellow(" The first install on PATH wins. To use the newer one, remove the older:")),console.log(),e.pkgDir?console.log(` ${chalk.bold.cyan(`sudo rm -rf ${e.pkgDir} ${e.path}`)}`):console.log(` ${chalk.bold.cyan(`sudo rm -rf ${e.path} # also delete its node_modules dir`)}`),console.log(),console.log(chalk.gray(" Then re-run icoa to confirm version banner shows the newer release.")),console.log()}}();const n=process.env.LANG||process.env.LC_ALL||process.env.LC_CTYPE||"";if(!/UTF-?8/i.test(n))if("win32"===process.platform){let o="";try{const{execFileSync:e}=await import("node:child_process");o=e("chcp.com",[],{encoding:"utf-8",timeout:1500,stdio:["ignore","pipe","ignore"]}).trim()}catch{}o.includes("65001")||(console.log(chalk.yellow(`⚠ Windows terminal is not using UTF-8 (current: ${o||"unknown"}).`)),console.log(chalk.gray(' Non-English text (Ukrainian, Chinese, Japanese, etc.) may show as "?" or garbled glyphs.')),console.log(chalk.gray(" Fix (run before ")+chalk.cyan("icoa")+chalk.gray("): ")+chalk.cyan("chcp 65001")),console.log(chalk.gray(" Or stay in English inside the CLI: ")+chalk.cyan("lang en")),console.log())}else console.log(chalk.yellow(`⚠ Your terminal locale is not UTF-8 (LANG=${n||"(unset)"}).`)),console.log(chalk.gray(' Non-English text and box characters may display as "?" or garbled glyphs.')),console.log(chalk.gray(" Fix: ")+chalk.cyan("export LANG=en_US.UTF-8")+chalk.gray(" (or your locale, e.g. ")+chalk.cyan("zh_CN.UTF-8")+chalk.gray(", ")+chalk.cyan("uk_UA.UTF-8")+chalk.gray(")")),console.log();console.log(K);const s=!!o.resume||!!o.continue;if(process.argv.length<=2||s)return o.continue&&(process.env.ICOA_AUTO_RESUME="1"),s||"1"===process.env.ICOA_RELAUNCH||await async function(){const o=process.stdin;if(o.isTTY&&"function"==typeof o.setRawMode)return new Promise(e=>{let n=!1;const s=()=>{if(!n){n=!0,o.removeListener("data",r);try{o.setRawMode(!1)}catch{}o.pause(),e()}},r=()=>s();o.setRawMode(!0),o.resume(),o.once("data",r),console.log(chalk.gray(" (press any key to continue...)")),setTimeout(s,3e3)});await new Promise(o=>setTimeout(o,3e3))}(),void F(B,s)}),e(B),n(B),s(B),r(B),t(B),l(B),i(B),a(B),c(B),m(B),g(B),d(B),p(B),y(B),h(B),u(B),f(B),w(B),b(B),j(B),$(B),T(B),v(B),A(B),I(B),E(B),C(B),_(B),S(B),x(B),k(B),L(B),B.command("model",{hidden:!0}).argument("[name]","model name to switch to").action(o=>{const e=R().geminiModel||"gemini-2.5-flash-lite";o?(O({geminiModel:o}),console.log(),console.log(chalk.green(" Model switched: ")+chalk.gray(e)+chalk.white(" -> ")+chalk.bold.white(o)),console.log()):(console.log(),console.log(chalk.gray(" Current model: ")+chalk.white(e)),console.log(),console.log(chalk.gray(" Available models:")),console.log(chalk.bold.white(" Gemini 3.x (Latest)")),console.log(chalk.white(" model gemini-3.5-flash ")+chalk.gray("Newest stable, paid Tier 1+")),console.log(chalk.white(" model gemini-3-flash-preview ")+chalk.gray("Preview")),console.log(chalk.bold.white(" Gemini 2.5 (Stable)")),console.log(chalk.white(" model gemini-2.5-flash-lite ")+chalk.gray("Fastest, default, free tier")),console.log(chalk.white(" model gemini-2.5-flash ")+chalk.gray("Heavier, thinking enabled")),console.log(chalk.white(" model gemini-2.5-pro ")+chalk.gray("Strong reasoning, paid")),console.log(chalk.bold.white(" Open Source")),console.log(chalk.white(" model gemma-4-31b-it ")+chalk.gray("Free, open-source")),console.log(chalk.white(" model <any-model-id> ")+chalk.gray("Custom model")),console.log(),console.log(chalk.gray(" Translation uses gemini-3.5-flash for best quality.")),console.log())}),"1"===process.env.ICOA_RESET_STATE)try{const{clearExamState:o}=await import("./lib/exam-state.js");o(),console.log(chalk.yellow("⚠ ICOA_RESET_STATE=1 — local exam state wiped.")),console.log(chalk.gray(" (Token NOT revoked server-side. Re-enter a fresh token with `exam <token>`.)")),console.log()}catch(o){console.log(chalk.red("⚠ ICOA_RESET_STATE: could not clear state — ")+chalk.gray(String(o)))}B.hook("preAction",()=>{!function(){if(!G())return;const o="─".repeat(Math.max(24,Math.min(52,(process.stdout.columns||80)-4)));console.log(),console.log(chalk.yellow(` ${o}`)),console.log(chalk.yellow.bold(" ⚠ You ARE inside Ubuntu — but this icoa")),console.log(chalk.yellow.bold(" is the WINDOWS build.")),console.log(),console.log(chalk.gray(" WSL appends the Windows PATH, so ")+chalk.cyan("icoa")),console.log(chalk.gray(" found the Windows copy first. Almost")),console.log(chalk.gray(" nothing will work: no vi, no python3,")),console.log(chalk.gray(" env setup cannot install.")),console.log(),console.log(chalk.gray(" Running: ")+chalk.white(process.execPath)),console.log(),console.log(chalk.white(" Fix — inside Ubuntu, check npm is Linux FIRST:")),console.log(` ${chalk.bold.cyan("which npm")}${chalk.gray(" → must not be /mnt/c/...")}`),console.log(` ${chalk.bold.cyan("sudo npm install -g icoa-cli")}`),console.log(` ${chalk.bold.cyan("hash -r")}${chalk.gray(" → bash caches the old path")}`),console.log(chalk.gray(" Then restart icoa and check ")+chalk.bold.cyan("which icoa")),console.log(chalk.yellow(` ${o}`)),console.log()}()}),B.parse();
|
|
2
|
+
import{Command as o}from"commander";import chalk from"chalk";import{registerCtfCommands as e}from"./commands/ctf.js";import{registerRefCommand as n}from"./commands/ref.js";import{registerShellCommand as s}from"./commands/shell.js";import{registerFilesCommand as r}from"./commands/files.js";import{registerViewCommand as t}from"./commands/view.js";import{registerConnectCommand as l}from"./commands/connect.js";import{registerChallengeCommand as i}from"./commands/challenge.js";import{registerDoctorCommand as a}from"./commands/doctor.js";import{registerNoteCommand as c}from"./commands/note.js";import{registerLogCommand as m}from"./commands/log.js";import{registerLangCommand as g}from"./commands/lang.js";import{registerSetupCommand as d}from"./commands/setup.js";import{registerEnvCommand as p}from"./commands/env.js";import{registerAienvCommand as y}from"./commands/aienv.js";import{registerIpynbCommand as h}from"./commands/ipynb.js";import{registerAi4ctfCommand as u}from"./commands/ai4ctf.js";import{registerExamCommand as f}from"./commands/exam.js";import{registerCtf4aiDemoCommand as w}from"./commands/ctf4ai-demo.js";import{registerThemeCommand as b}from"./commands/theme.js";import{registerLearnCommand as j}from"./commands/learn.js";import{registerCtf4VlaCommand as $}from"./commands/ctf4vla.js";import{registerDemo2Command as T}from"./commands/demo2.js";import{registerDemo4Command as v}from"./commands/demo4.js";import{registerDemo5Command as A}from"./commands/demo5.js";import{registerGfssmCommand as I}from"./commands/gfssm.js";import{registerSimCommand as E}from"./commands/sim.js";import{registerArenaCommand as C}from"./commands/arena.js";import{registerKaggleCommand as _}from"./commands/kaggle.js";import{registerSprintCommand as x}from"./commands/sprint.js";import{registerTeleloadCommand as S}from"./commands/teleload.js";import{registerBoardCommand as k}from"./commands/board.js";import{registerTypeCommand as L}from"./commands/type.js";import{registerWmCommand as R}from"./commands/wm.js";import{getConfig as O,saveConfig as F}from"./lib/config.js";import{startRepl as N}from"./repl.js";import{RELAUNCH_CODE as U}from"./lib/menu-nav.js";import{setTerminalTheme as P}from"./lib/theme.js";import{cleanupTerminal as M}from"./lib/terminal-cleanup.js";import{checkForUpdates as D}from"./lib/update-check.js";import{detectIcoaInstalls as G,isWindowsBuildInWslShell as W}from"./lib/platform.js";import{ICOA_BIG as H}from"./lib/banner.js";import{readFileSync as Y}from"node:fs";import{fileURLToPath as J}from"node:url";import{dirname as V,join as X}from"node:path";const q=V(J(import.meta.url)),z=JSON.parse(Y(X(q,"..","package.json"),"utf-8")).version,K=chalk.cyan(" ─────────────────────────────────────────────────────"),B=`\n${K}\n\n${H.map(o=>` ${chalk.bold.white(o)}`).join("\n")}\n\n ${chalk.yellow("International Cyber Olympiad in AI 2026")}\n ${chalk.bold.magenta("The World's First AI-Native CLI Operating System")}\n ${chalk.bold.magenta("for Cybersecurity & AI Security Competition")}\n ${chalk.bold.magenta("and Olympiad for K-12")}\n\n ${chalk.green.bold("AI4CTF")} ${chalk.gray("[Day 1]")} ${chalk.white("AI as your teammate")}\n ${chalk.red.bold("CTF4AI")} ${chalk.gray("[Day 2]")} ${chalk.white("Challenge & evaluate AI systems")}\n ${chalk.cyan.bold("CTF4EAI")} ${chalk.gray("[Pioneer]")} ${chalk.white("Embodied AI security")}\n ${chalk.bold.yellow("AI is your teammate. AI is your target. AI is embodied.")}\n\n ${chalk.white("Sydney, Australia")} ${chalk.gray("Jun 27 - Jul 2, 2026")}\n ${chalk.cyan.underline("https://icoa2026.au")}\n\n ${chalk.gray(`CLI-Native Competition Terminal v${z}`)}\n\n${K}\n`;process.on("uncaughtException",o=>{"__REPL_NO_EXIT__"!==o.message&&(console.error(chalk.red("Error:"),o.message),process.exit(1))}),process.on("unhandledRejection",o=>{const e=o instanceof Error?o.message:String(o);"__REPL_NO_EXIT__"!==e&&(console.error(chalk.red("Error:"),e),process.exit(1))});{const o=process.argv.slice(2).some(o=>["-V","--version","-h","--help"].includes(o));if(!0===process.stdin.isTTY&&"1"!==process.env.ICOA_SUPERVISED&&!o){const{spawnSync:o}=await import("node:child_process");process.on("SIGINT",()=>{}),process.on("SIGTERM",()=>{});let e=0,n=!1;do{const s=n?process.argv.slice(1,2):process.argv.slice(1),r=o(process.execPath,s,{stdio:"inherit",env:{...process.env,ICOA_SUPERVISED:"1",...n?{ICOA_RELAUNCH:"1"}:{}}});if(r.signal){const{appendExitLog:o}=await import("./lib/exit-log.js");o("worker-signal",`signal=${r.signal}`),console.log(),console.log(chalk.yellow(` ⚠ icoa was stopped by signal ${r.signal}`)+chalk.gray(" — another process killed it (not a crash). Run icoa --resume to continue.")),e=0;break}if(e=r.status,0!==e&&e!==U){const{appendExitLog:o}=await import("./lib/exit-log.js");o("worker-exit",`code=${e}`);const n=null!==e&&e>128?e-128:0;console.log(),console.log(n?chalk.yellow(` ⚠ icoa was stopped by signal ${n}`)+chalk.gray(" — another process killed it (not a crash). Run icoa --resume to continue."):chalk.yellow(` ⚠ icoa exited unexpectedly (code ${e})`)+chalk.gray(" — see ~/.icoa/exit.log · run icoa --resume to continue."))}n=!0}while(e===U);M(),process.exit(e??0)}}const Q=new o;if(Q.name("icoa").version(z).description("ICOA CLI — CLI-Native CTF Competition Terminal").option("--resume","Resume previous session").option("--continue","Continue where you left off — jump straight to your last learn card / exam").action(async o=>{const e=O();P("high-contrast"===e.themeVariant?"high-contrast":"dark"),D(),function(){const o=G();if(o.length<=1)return;const e=o[0];if([...o.map(o=>o.version||"0.0.0")].sort((o,e)=>function(o,e){const n=o.split(".").map(o=>parseInt(o,10)||0),s=e.split(".").map(o=>parseInt(o,10)||0);for(let o=0;o<3;o++)if((n[o]||0)!==(s[o]||0))return(n[o]||0)-(s[o]||0);return 0}(e,o))[0]!==e.version){console.log(),console.log(chalk.yellow.bold(" ⚠ Multiple icoa installations detected on PATH:"));for(let e=0;e<o.length;e++){const n=o[e],s=n.version?`v${n.version}`:"(version unreadable)",r=0===e?chalk.yellow("→"):" ",t=0===e?chalk.gray(" ← currently running (older — shadowing newer install)"):chalk.gray(" ← shadowed");console.log(` ${r} ${chalk.cyan(n.path.padEnd(28))} ${chalk.white(s)}${t}`)}console.log(),console.log(chalk.yellow(" The first install on PATH wins. To use the newer one, remove the older:")),console.log(),e.pkgDir?console.log(` ${chalk.bold.cyan(`sudo rm -rf ${e.pkgDir} ${e.path}`)}`):console.log(` ${chalk.bold.cyan(`sudo rm -rf ${e.path} # also delete its node_modules dir`)}`),console.log(),console.log(chalk.gray(" Then re-run icoa to confirm version banner shows the newer release.")),console.log()}}();const n=process.env.LANG||process.env.LC_ALL||process.env.LC_CTYPE||"";if(!/UTF-?8/i.test(n))if("win32"===process.platform){let o="";try{const{execFileSync:e}=await import("node:child_process");o=e("chcp.com",[],{encoding:"utf-8",timeout:1500,stdio:["ignore","pipe","ignore"]}).trim()}catch{}o.includes("65001")||(console.log(chalk.yellow(`⚠ Windows terminal is not using UTF-8 (current: ${o||"unknown"}).`)),console.log(chalk.gray(' Non-English text (Ukrainian, Chinese, Japanese, etc.) may show as "?" or garbled glyphs.')),console.log(chalk.gray(" Fix (run before ")+chalk.cyan("icoa")+chalk.gray("): ")+chalk.cyan("chcp 65001")),console.log(chalk.gray(" Or stay in English inside the CLI: ")+chalk.cyan("lang en")),console.log())}else console.log(chalk.yellow(`⚠ Your terminal locale is not UTF-8 (LANG=${n||"(unset)"}).`)),console.log(chalk.gray(' Non-English text and box characters may display as "?" or garbled glyphs.')),console.log(chalk.gray(" Fix: ")+chalk.cyan("export LANG=en_US.UTF-8")+chalk.gray(" (or your locale, e.g. ")+chalk.cyan("zh_CN.UTF-8")+chalk.gray(", ")+chalk.cyan("uk_UA.UTF-8")+chalk.gray(")")),console.log();console.log(B);const s=!!o.resume||!!o.continue;if(process.argv.length<=2||s)return o.continue&&(process.env.ICOA_AUTO_RESUME="1"),s||"1"===process.env.ICOA_RELAUNCH||await async function(){const o=process.stdin;if(o.isTTY&&"function"==typeof o.setRawMode)return new Promise(e=>{let n=!1;const s=()=>{if(!n){n=!0,o.removeListener("data",r);try{o.setRawMode(!1)}catch{}o.pause(),e()}},r=()=>s();o.setRawMode(!0),o.resume(),o.once("data",r),console.log(chalk.gray(" (press any key to continue...)")),setTimeout(s,3e3)});await new Promise(o=>setTimeout(o,3e3))}(),void N(Q,s)}),e(Q),n(Q),s(Q),r(Q),t(Q),l(Q),i(Q),a(Q),c(Q),m(Q),g(Q),d(Q),p(Q),y(Q),h(Q),u(Q),f(Q),w(Q),b(Q),j(Q),$(Q),T(Q),v(Q),A(Q),I(Q),E(Q),C(Q),_(Q),S(Q),x(Q),k(Q),R(Q),L(Q),Q.command("model",{hidden:!0}).argument("[name]","model name to switch to").action(o=>{const e=O().geminiModel||"gemini-2.5-flash-lite";o?(F({geminiModel:o}),console.log(),console.log(chalk.green(" Model switched: ")+chalk.gray(e)+chalk.white(" -> ")+chalk.bold.white(o)),console.log()):(console.log(),console.log(chalk.gray(" Current model: ")+chalk.white(e)),console.log(),console.log(chalk.gray(" Available models:")),console.log(chalk.bold.white(" Gemini 3.x (Latest)")),console.log(chalk.white(" model gemini-3.5-flash ")+chalk.gray("Newest stable, paid Tier 1+")),console.log(chalk.white(" model gemini-3-flash-preview ")+chalk.gray("Preview")),console.log(chalk.bold.white(" Gemini 2.5 (Stable)")),console.log(chalk.white(" model gemini-2.5-flash-lite ")+chalk.gray("Fastest, default, free tier")),console.log(chalk.white(" model gemini-2.5-flash ")+chalk.gray("Heavier, thinking enabled")),console.log(chalk.white(" model gemini-2.5-pro ")+chalk.gray("Strong reasoning, paid")),console.log(chalk.bold.white(" Open Source")),console.log(chalk.white(" model gemma-4-31b-it ")+chalk.gray("Free, open-source")),console.log(chalk.white(" model <any-model-id> ")+chalk.gray("Custom model")),console.log(),console.log(chalk.gray(" Translation uses gemini-3.5-flash for best quality.")),console.log())}),"1"===process.env.ICOA_RESET_STATE)try{const{clearExamState:o}=await import("./lib/exam-state.js");o(),console.log(chalk.yellow("⚠ ICOA_RESET_STATE=1 — local exam state wiped.")),console.log(chalk.gray(" (Token NOT revoked server-side. Re-enter a fresh token with `exam <token>`.)")),console.log()}catch(o){console.log(chalk.red("⚠ ICOA_RESET_STATE: could not clear state — ")+chalk.gray(String(o)))}Q.hook("preAction",()=>{!function(){if(!W())return;const o="─".repeat(Math.max(24,Math.min(52,(process.stdout.columns||80)-4)));console.log(),console.log(chalk.yellow(` ${o}`)),console.log(chalk.yellow.bold(" ⚠ You ARE inside Ubuntu — but this icoa")),console.log(chalk.yellow.bold(" is the WINDOWS build.")),console.log(),console.log(chalk.gray(" WSL appends the Windows PATH, so ")+chalk.cyan("icoa")),console.log(chalk.gray(" found the Windows copy first. Almost")),console.log(chalk.gray(" nothing will work: no vi, no python3,")),console.log(chalk.gray(" env setup cannot install.")),console.log(),console.log(chalk.gray(" Running: ")+chalk.white(process.execPath)),console.log(),console.log(chalk.white(" Fix — inside Ubuntu, check npm is Linux FIRST:")),console.log(` ${chalk.bold.cyan("which npm")}${chalk.gray(" → must not be /mnt/c/...")}`),console.log(` ${chalk.bold.cyan("sudo npm install -g icoa-cli")}`),console.log(` ${chalk.bold.cyan("hash -r")}${chalk.gray(" → bash caches the old path")}`),console.log(chalk.gray(" Then restart icoa and check ")+chalk.bold.cyan("which icoa")),console.log(chalk.yellow(` ${o}`)),console.log()}()}),Q.parse();
|
package/dist/lib/commands.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const REGISTRY=[{name:"join",mapsTo:["ctf","join"],rest:!0,help:{order:0,section:"Competition",label:"join <url>",text:"Connect to CTFd"}},{name:"activate",mapsTo:["ctf","activate"],rest:!0},{name:"challenges",mapsTo:["ctf","challenges"],help:{order:1,section:"Competition",label:"challenges (ch)",text:"List challenges by category"}},{name:"ch",mapsTo:["ctf","challenges"]},{name:"open",mapsTo:["ctf","open"],rest:!0,help:{order:2,section:"Competition",label:"open <id>",text:"Read challenge + get next steps"}},{name:"submit",mapsTo:["ctf","submit"],rest:!0,help:{order:3,section:"Competition",label:"submit <id> <flag>",text:"Submit a flag"}},{name:"flag",mapsTo:["ctf","submit"],rest:!0},{name:"scoreboard",mapsTo:["ctf","scoreboard"],rest:!0,help:{order:4,section:"Competition",label:"scoreboard (sb)",text:"Live rankings"}},{name:"sb",mapsTo:["ctf","scoreboard"],rest:!0},{name:"status",mapsTo:["ctf","status"],help:{order:5,section:"Competition",label:"status",text:"Your score & timer"}},{name:"time",mapsTo:["ctf","time"],help:{order:6,section:"Competition",label:"time",text:"Countdown timer"}},{name:"ref",help:{order:8,section:"Tools",label:"ref [topic]",text:"Quick reference (linux, web, crypto...)"}},{name:"shell",help:{order:9,section:"Tools",label:"shell",text:"Interactive CTF shell (sandbox if present, else host)"}},{name:"files",help:{order:10,section:"Tools",label:"files <id>",text:"Download challenge files"}},{name:"view"},{name:"connect",help:{order:11,section:"Tools",label:"connect <id>",text:"Connect to remote target"}},{name:"challenge",subs:["start","stop","status"]},{name:"note",help:{order:12,section:"Tools",label:"note [text]",text:"Add a logbook entry (in-exam: tags current Q)"}},{name:"notes"},{name:"logbook",help:{order:13,section:"Tools",label:"logbook [n]",text:"View your logbook (in-exam: filter by Q)"}},{name:"log",help:{order:14,section:"Tools",label:"log",text:"Session history"}},{name:"man"},{name:"manual"},{name:"lang",help:{order:21,section:"System",label:"lang [code]",text:"Switch language (15 supported)"}},{name:"setup",help:{order:20,section:"System",label:"setup",text:"Configure settings"}},{name:"env"},{name:"aienv"},{name:"ipynb"},{name:"kaggle"},{name:"teleload"},{name:"pandahelpme31"},{name:"guide"},{name:"ai4ctf",help:{order:7,section:"AI Teammate",label:"ai4ctf",text:"Free-chat with your AI teammate"}},{name:"model"},{name:"ctf",subs:["challenges","open","submit","scoreboard","status","join","logout","time"]},{name:"exam",subs:["list","start","q","answer","submit","review","result","next","prev","mark","unmark","help"]},{name:"demo",mapsTo:["exam","demo"]},{name:"retry",mapsTo:["exam","demo-retry"]},{name:"nations",mapsTo:["exam","nations"]},{name:"next",mapsTo:["exam","next"]},{name:"prev",mapsTo:["exam","prev"]},{name:"continue"},{name:"logout",mapsTo:["ctf","logout"],help:{order:22,section:"System",label:"logout",text:"Disconnect"}},{name:"ctf4ai"},{name:"mark",mapsTo:["exam","mark"],rest:!0},{name:"unmark",mapsTo:["exam","unmark"],rest:!0},{name:"review",mapsTo:["exam","review"]},{name:"learn",subs:["start","continue"]},{name:"demo2"},{name:"demo4"},{name:"demo5"},{name:"gfssm"},{name:"ctf4eai"},{name:"ctf4vla"},{name:"theme"},{name:"sim"},{name:"arena",subs:["eai"]},{name:"eai"},{name:"wm"},{name:"board"},{name:"learn-pull"},{name:"doctor",help:{order:19,section:"System",label:"doctor",text:"Self-check: version, Python, server, tools"}},{name:"bg",nav:!0,help:{order:17,section:"System",label:"bg <command>",text:"Run a shell command in the background"}},{name:"tasks",nav:!0,help:{order:18,section:"System",label:"tasks [id]",text:"Background task panel / view output"}},{name:"drafts",nav:!0,help:{order:16,section:"System",label:"drafts",text:"Review staged draft answers"}},{name:"help",nav:!0},{name:"menu",nav:!0},{name:"quit",nav:!0},{name:"q",nav:!0},{name:"exit",nav:!0,help:{order:24,section:"System",label:"exit",text:"Quit (session saved)"}},{name:"back",nav:!0},{name:"resume",nav:!0,help:{order:15,section:"System",label:"resume",text:"Continue your last learn card / exam"}},{name:"clear",nav:!0,help:{order:23,section:"System",label:"clear",text:"Clear screen"}},{name:"cls",nav:!0}];export const surface=()=>REGISTRY.map(e=>e.name);export const dispatchable=()=>REGISTRY.filter(e=>!e.nav).map(e=>e.name);export function shortcutFor(e,t){const a=REGISTRY.find(t=>t.name===e&&t.mapsTo);return a?.mapsTo?a.rest?[...a.mapsTo,...t]:[...a.mapsTo]:null}export const subcommandsOf=e=>REGISTRY.find(t=>t.name===e)?.subs;export const helpRows=e=>REGISTRY.filter(t=>t.help?.section===e).sort((e,t)=>e.help.order-t.help.order).map(e=>({label:e.help.label,text:e.help.text}));
|
|
1
|
+
export const REGISTRY=[{name:"join",mapsTo:["ctf","join"],rest:!0,help:{order:0,section:"Competition",label:"join <url>",text:"Connect to CTFd"}},{name:"activate",mapsTo:["ctf","activate"],rest:!0},{name:"challenges",mapsTo:["ctf","challenges"],help:{order:1,section:"Competition",label:"challenges (ch)",text:"List challenges by category"}},{name:"ch",mapsTo:["ctf","challenges"]},{name:"open",mapsTo:["ctf","open"],rest:!0,help:{order:2,section:"Competition",label:"open <id>",text:"Read challenge + get next steps"}},{name:"submit",mapsTo:["ctf","submit"],rest:!0,help:{order:3,section:"Competition",label:"submit <id> <flag>",text:"Submit a flag"}},{name:"flag",mapsTo:["ctf","submit"],rest:!0},{name:"scoreboard",mapsTo:["ctf","scoreboard"],rest:!0,help:{order:4,section:"Competition",label:"scoreboard (sb)",text:"Live rankings"}},{name:"sb",mapsTo:["ctf","scoreboard"],rest:!0},{name:"status",mapsTo:["ctf","status"],help:{order:5,section:"Competition",label:"status",text:"Your score & timer"}},{name:"time",mapsTo:["ctf","time"],help:{order:6,section:"Competition",label:"time",text:"Countdown timer"}},{name:"ref",help:{order:8,section:"Tools",label:"ref [topic]",text:"Quick reference (linux, web, crypto...)"}},{name:"shell",help:{order:9,section:"Tools",label:"shell",text:"Interactive CTF shell (sandbox if present, else host)"}},{name:"files",help:{order:10,section:"Tools",label:"files <id>",text:"Download challenge files"}},{name:"view"},{name:"connect",help:{order:11,section:"Tools",label:"connect <id>",text:"Connect to remote target"}},{name:"challenge",subs:["start","stop","status"]},{name:"note",help:{order:12,section:"Tools",label:"note [text]",text:"Add a logbook entry (in-exam: tags current Q)"}},{name:"notes"},{name:"logbook",help:{order:13,section:"Tools",label:"logbook [n]",text:"View your logbook (in-exam: filter by Q)"}},{name:"log",help:{order:14,section:"Tools",label:"log",text:"Session history"}},{name:"man"},{name:"manual"},{name:"lang",help:{order:21,section:"System",label:"lang [code]",text:"Switch language (15 supported)"}},{name:"setup",help:{order:20,section:"System",label:"setup",text:"Configure settings"}},{name:"env"},{name:"aienv"},{name:"ipynb"},{name:"kaggle"},{name:"teleload"},{name:"pandahelpme31"},{name:"guide"},{name:"ai4ctf",help:{order:7,section:"AI Teammate",label:"ai4ctf",text:"Free-chat with your AI teammate"}},{name:"model"},{name:"ctf",subs:["challenges","open","submit","scoreboard","status","join","logout","time"]},{name:"exam",subs:["list","start","q","answer","submit","review","result","next","prev","mark","unmark","help"]},{name:"demo",mapsTo:["exam","demo"]},{name:"retry",mapsTo:["exam","demo-retry"]},{name:"nations",mapsTo:["exam","nations"]},{name:"next",mapsTo:["exam","next"]},{name:"prev",mapsTo:["exam","prev"]},{name:"continue"},{name:"logout",mapsTo:["ctf","logout"],help:{order:22,section:"System",label:"logout",text:"Disconnect"}},{name:"ctf4ai"},{name:"mark",mapsTo:["exam","mark"],rest:!0},{name:"unmark",mapsTo:["exam","unmark"],rest:!0},{name:"review",mapsTo:["exam","review"]},{name:"learn",subs:["start","continue"]},{name:"demo2"},{name:"demo4"},{name:"demo5"},{name:"gfssm"},{name:"ctf4eai"},{name:"ctf4vla"},{name:"theme"},{name:"sim"},{name:"arena",subs:["eai"]},{name:"eai"},{name:"wm"},{name:"type",subs:["15","30","60","words","python","shell","history"],help:{order:15,section:"Tools",label:"type [30]",text:"Typing speed trainer — WPM, live colours, no token"}},{name:"board"},{name:"learn-pull"},{name:"doctor",help:{order:19,section:"System",label:"doctor",text:"Self-check: version, Python, server, tools"}},{name:"bg",nav:!0,help:{order:17,section:"System",label:"bg <command>",text:"Run a shell command in the background"}},{name:"tasks",nav:!0,help:{order:18,section:"System",label:"tasks [id]",text:"Background task panel / view output"}},{name:"drafts",nav:!0,help:{order:16,section:"System",label:"drafts",text:"Review staged draft answers"}},{name:"help",nav:!0},{name:"menu",nav:!0},{name:"quit",nav:!0},{name:"q",nav:!0},{name:"exit",nav:!0,help:{order:24,section:"System",label:"exit",text:"Quit (session saved)"}},{name:"back",nav:!0},{name:"resume",nav:!0,help:{order:15,section:"System",label:"resume",text:"Continue your last learn card / exam"}},{name:"clear",nav:!0,help:{order:23,section:"System",label:"clear",text:"Clear screen"}},{name:"cls",nav:!0}];export const surface=()=>REGISTRY.map(e=>e.name);export const dispatchable=()=>REGISTRY.filter(e=>!e.nav).map(e=>e.name);export function shortcutFor(e,t){const a=REGISTRY.find(t=>t.name===e&&t.mapsTo);return a?.mapsTo?a.rest?[...a.mapsTo,...t]:[...a.mapsTo]:null}export const subcommandsOf=e=>REGISTRY.find(t=>t.name===e)?.subs;export const helpRows=e=>REGISTRY.filter(t=>t.help?.section===e).sort((e,t)=>e.help.order-t.help.order).map(e=>({label:e.help.label,text:e.help.text}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import chalk from"chalk";import{httpFetch as e}from"./http.js";import{LEARN_SERVER as t}from"./learn-curricula.js";import{learnW as s}from"./learn-render.js";import{getCliVersion as r}from"./version.js";export async function fetchFocusLog(s){if(s.toUpperCase().endsWith("DEMO01"))return{kind:"demo"};try{const a=await e(`${t.replace(/\/$/,"")}/api/icoa/learn/focus/log`,{method:"POST",headers:{"Content-Type":"application/json","X-ICOA-Client":r()},body:JSON.stringify({token:s.toUpperCase()}),signal:AbortSignal.timeout(15e3)}),o=await a.json();return o.success&&o.data&&Array.isArray(o.data.sessions)?{kind:"ok",log:o.data}:"demo"===o.code?{kind:"demo"}:{kind:"error",message:o.message||`HTTP ${a.status}`}}catch{return{kind:"offline"}}}export const FOCUS_LOG_RE=/^\/?(?:focus|timer)\s+(?:log|logs|history|list|records?)$|^\/?focus-?log$/i;function a(e){const t=Math.round(e/6e4);return t>=60?`${Math.floor(t/60)}h ${t%60}m`:`${t}m`}export function renderFocusLog(e,t,r,o){const n=r?"":"⏱ ",i=s(),c=[];return c.push(""),0===e.sessions.length?(c.push(chalk.bold.white(` ${n}
|
|
1
|
+
import chalk from"chalk";import{httpFetch as e}from"./http.js";import{LEARN_SERVER as t}from"./learn-curricula.js";import{learnW as s}from"./learn-render.js";import{getCliVersion as r}from"./version.js";export async function fetchFocusLog(s){if(s.toUpperCase().endsWith("DEMO01"))return{kind:"demo"};try{const a=await e(`${t.replace(/\/$/,"")}/api/icoa/learn/focus/log`,{method:"POST",headers:{"Content-Type":"application/json","X-ICOA-Client":r()},body:JSON.stringify({token:s.toUpperCase()}),signal:AbortSignal.timeout(15e3)}),o=await a.json();return o.success&&o.data&&Array.isArray(o.data.sessions)?{kind:"ok",log:o.data}:"demo"===o.code?{kind:"demo"}:{kind:"error",message:o.message||`HTTP ${a.status}`}}catch{return{kind:"offline"}}}export const FOCUS_LOG_RE=/^\/?(?:focus|timer)\s+(?:log|logs|history|list|records?)$|^\/?focus-?log$/i;function a(e){const t=Math.round(e/6e4);return t>=60?`${Math.floor(t/60)}h ${t%60}m`:`${t}m`}export function renderFocusLog(e,t,r,o){const n=r?"":"⏱ ",i=s(),c=[];return c.push(""),0===e.sessions.length?(c.push(chalk.bold.white(` ${n}${t?"还没有专注记录。":"No focus sessions yet."}`)),c.push(chalk.gray(t?" 输 focus 30(15–60 分钟)开始一段;打开任何课程都算;结束时报告会记在这里。":" Type focus 30 (15–60 min) to start one; any course you open counts; the report lands here.")),c.push(""),c.push(chalk.gray(` ${o}`)),c):(c.push(chalk.bold.white(` ${n}${t?"专注记录":"Focus log"}`)+chalk.gray(t?` · ${e.total_sessions} 段 · 共 ${a(6e4*e.total_min)} · 最近在前`:` · ${e.total_sessions} session${1===e.total_sessions?"":"s"} · ${a(6e4*e.total_min)} total · newest first`)),c.push(chalk.gray(` ${"─".repeat(Math.max(24,i-2))}`)),e.sessions.forEach((s,r)=>{const o=e.sessions.length-r,n=`${s.planned_min}m`,i=a(s.actual_ms),d=s.aborted?chalk.yellow(t?" 中断":" aborted"):s.early?chalk.gray(t?` 提前结束(${i}/${n})`:` ended early (${i}/${n})`):chalk.green(` ${i}`),u=s.ratings.filter(e=>null!=e).length?chalk.gray((t?" · 自评 ":" · self-rating ")+s.ratings.map(e=>function(e,t){return null==e?"–":t?["差","一般","好"][e]:["bad","ok","good"][e]}(e,t)).join(" / ")):"";c.push(chalk.white(` ${String(o).padStart(2)}. `)+chalk.bold.white(function(e,t){const s=new Date(e);if(Number.isNaN(s.getTime()))return e.slice(0,16);const r=e=>String(e).padStart(2,"0"),a=`${s.getFullYear()}-${r(s.getMonth()+1)}-${r(s.getDate())}`,o=`${r(s.getHours())}:${r(s.getMinutes())}`,n=e=>new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime(),i=Math.round((n(new Date)-n(s))/864e5);return`${i<=0?t?"今天":"today":1===i?t?"昨天":"yesterday":a} ${o}`}(s.started_at,t))+d+u);for(const e of s.segments){const s=null!=e.card_from&&null!=e.card_to?t?`卡 ${e.card_from}→${e.card_to}(${e.cards_viewed} 张)`:`cards ${e.card_from}→${e.card_to} (${e.cards_viewed})`:t?`${e.cards_viewed} 张卡`:`${e.cards_viewed} cards`,r=e.checks_answered?` · ${e.checks_correct}/${e.checks_answered} ✓`:"",a=e.practice_ac?t?` · 练习 AC ${e.practice_ac}`:` · practice AC ${e.practice_ac}`:"",o=e.median_card_ms?t?` · 每卡中位 ${Math.round(e.median_card_ms/1e3)}s`:` · median ${Math.round(e.median_card_ms/1e3)}s/card`:"",n=e.cards_under_3s?chalk.yellow(t?` · 3 秒内翻过 ${e.cards_under_3s}`:` · ${e.cards_under_3s} under 3s`):"",i=e.ai_questions?t?` · AI 提问 ${e.ai_questions}`:` · ${e.ai_questions} AI q`:"";c.push(chalk.gray(" ")+chalk.cyan(e.curriculum_id)+chalk.gray(` ${s}${r}${a}${o}`)+n+chalk.gray(i))}}),c.push(chalk.gray(` ${"─".repeat(Math.max(24,i-2))}`)),c.push(chalk.gray((t?" 再来一段:":" Another one: ")+chalk.bold.green("focus 30")+chalk.gray(t?"(15–60)· ":" (15–60) · ")+o)),c)}
|
package/dist/lib/learn-render.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import chalk from"chalk";import o from"string-width";import{localized as e}from"./learn-curricula.js";import{t as n}from"./learn-i18n.js";import{getConfig as t}from"./config.js";import{icoaBannerLines as l}from"./banner.js";import{renderCharts as r,detectCaps as a}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(o){return"normal"===o.learnWidth||"wide"===o.learnWidth||"wider"===o.learnWidth||"widest"===o.learnWidth?o.learnWidth:o.learnWide?"wide":"normal"}export function setLearnWidth(o){g=o}export function learnWidthCap(o=g){return"widest"===o?160:"wider"===o?120:"wide"===o?96:64}export function learnW(){const o=process.stdout.columns||69,e=Math.max(40,Math.min(64,o-5));return"normal"===g?e:Math.max(e,Math.min(o-6,learnWidthCap()))}const i=learnW;function d(o="─"){return o.repeat(i()+2)}function y(e,n){if(o(e)<=n)return e;let t=e;for(;t.length>0&&o(t)>Math.max(1,n-1);)t=t.slice(0,-1);return t.trimEnd()+"…"}function u(o,e,n=20){const t=e>0?o/e:0,l=Math.floor(t*n),r=n-l;return`${chalk.green("█".repeat(l))+chalk.gray("░".repeat(r))} ${(100*t).toFixed(1)}%`}function h(o){return o>=30?chalk.red("🔥🔥🔥 "+o+" days"):o>=7?chalk.yellow("🔥 "+o+" days"):o>=1?chalk.gray("· "+o+" day"+(o>1?"s":"")):chalk.gray("—")}export function renderWelcome(o,e,t){const l=c(),r=e.cardsCompleted.length,a=o.totalCards,s=a-r,g=o.modules.find(o=>e.currentCard>=o.cardRange[0]&&e.currentCard<=o.cardRange[1]),p=/demo/i.test(o.id);if(console.log(),console.log(chalk.cyan(" ╭"+d("═"))),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ║ ")+chalk.bold.white(" "+(o.name?.trim()||n("academy_title",l)))),console.log(chalk.cyan(" ║")),t)console.log(chalk.cyan(" ║ ")+chalk.white(" "+n(p?"welcome_new_demo":"welcome_new",l)));else{const o=new Date(e.lastSeenAt),t=Math.floor((Date.now()-o.getTime())/36e5),r=t<1?n("just_now",l):t<24?`${t}${n("ago_hours",l)}`:`${Math.floor(t/24)}${n("ago_days",l)}`;console.log(chalk.cyan(" ║ ")+chalk.white(` ${n("welcome_back",l)} ${r}`))}console.log(chalk.cyan(" ║"));const m=y(g?`${g.number}. ${g.name}`:"—",i()-14);console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("module",l).padEnd(11))+chalk.white(m)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("progress",l).padEnd(11))+u(r,a,Math.max(6,Math.min(20,i()-26-`${r}/${a}`.length)))+chalk.gray(` (${r}/${a})`)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("streak",l).padEnd(11))+h(e.streakDays)),s>0&&e.currentCard<=a&&console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("next_card",l).padEnd(11))+chalk.white(`#${e.currentCard} ${n("of",l)} ${a}`)),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ╰"+d("═"))),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────"));const b=0===r?n("continue_start",l):`${n("continue_resume",l)} ${e.currentCard}`;console.log(chalk.bold.green(" continue")+chalk.gray(" "+b)),console.log(chalk.yellow(" status")+chalk.gray(" "+n("status_full",l))),e.bookmarks.length>0&&console.log(chalk.yellow(" bookmarks")+chalk.gray(` ${e.bookmarks.length} ${n("bookmarks_desc",l)}`)),console.log(chalk.gray(" quit")+chalk.gray(" "+n("quit_desc",l))),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log()}function p(e,t){const l=c(),r=e.module,a=t.modules.find(o=>o.number===r),s=a?a.name:n("unknown_module",l),g=n("module",l).replace(":","").trim(),d=y(`${n("card",l)} ${e.number} / ${t.totalCards} · ${g} ${r} · ${s}`,i()-4);console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(d)+" "+chalk.cyan("─".repeat(Math.max(0,i()-o(d)-4))))}function m(){console.log(chalk.cyan(" ╰"+d())),console.log()}function b(o){console.log(chalk.cyan(" │ ")+chalk.white(o))}function f(){console.log(chalk.cyan(" │"))}const $=/[┌┐└┘├┤┬┴┼─│╔╗╚╝═║╠╣╦╩╬▓▒░█]/;function w(o){return/^(?: {4}|\t)/.test(o)||$.test(o)||/ {3,}/.test(o)}export function wrapMixed(e,n){const t=[];let l="",r=0,a=!1;for(const s of function(e){const n=[],t=e=>{let t="";const l=()=>{t&&(n.push({t:"atom",s:t}),t="")};for(const r of e)" "===r?(l(),n.push({t:"space"})):2===o(r)?(l(),n.push({t:"wide",s:r})):t+=r;l()},l=/`[^`]*`/g;let r,a=0;for(;null!==(r=l.exec(e));)t(e.slice(a,r.index)),n.push({t:"code",s:r[0]}),a=r.index+r[0].length;return t(e.slice(a)),n}(e)){if("space"===s.t){""!==l&&(a=!0);continue}const e=s.s,c=o("code"===s.t?e.replace(/`/g,""):e),g=a?1:0,i="atom"===s.t&&e.length<=2&&/^[.,;:!?)\]}…'"]+$/.test(e);""!==l&&!i&&r+g+c>n?(t.push(l),l=e,r=c,a=!1):(a&&(l+=" ",r+=1,a=!1),l+=e,r+=c)}return l&&t.push(l),t}const k=/\b(?:Common bug|Common mistake|Common error|Pitfall|Gotcha|Footgun)\b[::]?|\b(?:Warning|Caution)\b[::]|(?:常见错误|常见陷阱|常见误区|陷阱|注意|警告|误区)[::]/gi;function _(o,e){const n=e?chalk.bold.white:chalk.white;let t,l="",r=0;for(k.lastIndex=0;null!==(t=k.exec(o));)t.index>r&&(l+=n(o.slice(r,t.index))),l+=chalk.bold.yellow(t[0]),r=t.index+t[0].length;return r<o.length&&(l+=n(o.slice(r))),l}function x(o,e=!1){let n="",t=0;const l=/`([^`]*)`/g;let r;for(;null!==(r=l.exec(o));)r.index>t&&(n+=_(o.slice(t,r.index),e)),n+=chalk.cyan(r[1]),t=r.index+r[0].length;return t<o.length&&(n+=_(o.slice(t),e)),n}function C(o,e=!1){let n=!1;for(const t of o.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 o of wrapMixed(t,i()-4))console.log(chalk.cyan(" │ ")+` ${x(o,e)}`);else f()}function M(o,e){const n=[];for(const t of o.split("\n")){if(""===t){n.push("");continue}if(w(t)){n.push(t);continue}let o="";for(const l of t.split(" "))(o+" "+l).trim().length>e?(n.push(o.trim()),o=l):o=(o+" "+l).trim();o&&n.push(o)}return n}let v=!1;export function setLearnAiEnabled(o){v=o}let W=new Set;export function setLearnPqsViewCards(o){W=o}let z=new Map;export function setLearnHonorRanks(o){z=o}const j=["①","②","③","④","⑤","⑥","⑦","⑧","⑨","⑩"];let q=[];export function setLearnReplies(o){q=o}let A=[];export function setLearnFaqs(o){A=o}let S=null;export function setLearnHometask(o){S=o}let P="full";export function setLearnFooterProfile(o){P=o}let L=null;export function setLearnFooterExtra(o){L=o}function E(){const o=L?.();o&&console.log(chalk.gray(" "+o))}let R=null;export function setLearnPracticeChips(o){R=o}let N=null;export function setLearnPracticeCount(o){N=o}export function renderHonorWall(o){const e=c().startsWith("zh"),{totals:n,chapters:t}=o,l=n.gold+n.silver+n.bronze,r=(o,e)=>e>0?chalk.bold.white(`${o}×${e}`):chalk.gray(`${o}×0`);if(console.log(chalk.gray(" ─────────────────────────────────────────")),console.log(chalk.bold.white(e?" 🏅 奖牌墙 ":" 🏅 Medal wall ")+r("🥇",n.gold)+chalk.gray(" · ")+r("🥈",n.silver)+chalk.gray(" · ")+r("🥉",n.bronze)+chalk.gray(e?" (永久 — 挂上墙就不会摘下)":" (permanent — once earned, never removed)")),0===l){const n=null!=o.floor?`${o.floor}+`:e?"高分":"a high score";return console.log(chalk.gray(e?` 还没有奖牌 — 单卡提问评分 ${n} 并进入该卡前三,即可永久挂墙 🥇🥈🥉`:` No medals yet — score ${n} on a card and hold its top-3 to hang one 🥇🥈🥉`)),void console.log(chalk.gray(" ─────────────────────────────────────────"))}const a=t.map(o=>{const e=`Ch${o.chapter}`,n=(o.gold>0?`🥇${o.gold}`:"")+(o.silver>0?`🥈${o.silver}`:"")+(o.bronze>0?`🥉${o.bronze}`:"");return n?chalk.white(`${e} ${n}`):chalk.gray(`${e} —`)}).join(chalk.gray(" "));console.log(" "+a);const s=t.flatMap(o=>o.cards.map(e=>({...e,chapter:o.chapter})));s.sort((o,e)=>o.rank-e.rank||o.chapter-e.chapter||o.card-e.card);for(const o of s.slice(0,24)){const n=(o.since||"").slice(0,10);console.log(chalk.white(` ${o.icon} `)+chalk.gray(`Ch${o.chapter} · `)+chalk.white(e?`第 ${o.card} 张`:`card ${o.card}`)+chalk.gray(` · ${o.score}`)+(n?chalk.gray(e?` · 始于 ${n}`:` · since ${n}`):""))}s.length>24&&console.log(chalk.gray(e?` …还有 ${s.length-24} 枚`:` …and ${s.length-24} more`)),console.log(chalk.gray(" ─────────────────────────────────────────"))}export function renderKnowledgeCard(t,l){const d=c(),y=e(t,d);p(y,l),f();const u=wrapMixed(y.title,i()-4);for(const o of u)b(chalk.bold.yellow(o));b(chalk.gray("─".repeat(Math.min(Math.max(...u.map(e=>o(e))),i()))));const h=!!(t.chart_ids&&t.chart_ids.length>0);if(h&&r(t.chart_ids.slice(0,1),l.charts,a()),y.key_point&&""!==y.key_point.trim()?(h||f(),function(o,e,n){const t=c().startsWith("zh"),l=a().cols,r="-".repeat(Math.min(i(),Math.max(24,l-6))),s=t?"要点":"KEY POINT",g=t?`第 ${e} / ${n} 张`:`Card ${e} / ${n}`;console.log(chalk.cyan(" │ ")+chalk.green(r)),console.log(chalk.cyan(" │ ")+chalk.green(`${s} · ${g}`));for(const e of wrapMixed(o,i()-4))console.log(chalk.cyan(" │ ")+chalk.bold.green(" "+e));console.log(chalk.cyan(" │ ")+chalk.green(r)),f()}(y.key_point,y.number,l.totalCards)):h||f(),y.body.filter(o=>null!=o&&""!==o.trim()).forEach((o,e)=>{const n=0===e?o.match(/^([\s\S]*?(?:[.!?](?=\s|$)|[。!?]))(\s[\s\S]*)?$/):null;if(n&&n[1].trim().length<=180){C(n[1],!0);const o=n[2]?.trimStart();o&&C(o,!1)}else C(o,!1);f()}),y.icoaConnection){b(chalk.magenta(" "+n("icoa_connection",d))),b(chalk.gray(" "+"─".repeat(20)));for(const o of wrapMixed(y.icoaConnection,i()-4))b(" "+chalk.magenta(o));f()}if(t.check){const o=d.startsWith("zh"),e=t._zh?.checkStatement??t._zh?.check?.statement,n=o?e??t.check.statement:t.check.statement;for(const e of wrapMixed(o?"请回答 (输 y 或 n):":"Quick comprehension check (type y or n):",i()-8))b(chalk.cyan(" 🤔 "+e));b(chalk.gray(" "+"─".repeat(20)));for(const o of wrapMixed(n,i()-4))b(" "+chalk.white(o));f()}m(),function(e,n){const t=c().startsWith("zh"),l=a().cols,r="-".repeat(Math.min(i(),Math.max(24,l-6))),d=chalk.gray(" ");if("minimal"===P){const o=R?R(e.number):null;console.log(chalk.gray(" "+r)),console.log(chalk.gray(" "+(t?`第 ${e.number} / ${n.totalCards} 张`:`Card ${e.number} / ${n.totalCards}`))+(o?chalk.gray(t?" · 本卡练习 ":" · practice ")+o:""));const l=[];if(e.check?(l.push(chalk.bold.green("[y]")+chalk.gray(t?" 是":" yes")),l.push(chalk.bold.green("[n]")+chalk.gray(t?" 否":" no"))):l.push(chalk.bold.green("[ok]")+chalk.gray(t?" 下一张":" next")),o){const e=Math.max(1,(o.match(/\[\d\]/g)??[]).length),n=Array.from({length:e},(o,e)=>`[${e+1}]`).join("");l.push(chalk.bold.cyan(n)+chalk.gray(t?" 练习":" practice"))}return l.push(chalk.bold.cyan("[b]")+chalk.gray(t?" 上一张":" back")),l.push(chalk.bold.yellow("[e]")+chalk.gray(t?" 求助":" help")),l.push(chalk.gray("[quit]")+(t?chalk.gray(" 退出"):"")),console.log(" "+l.join(d)),E(),void console.log()}const y=[];e.check?(y.push(chalk.bold.green("[y]")+chalk.gray(t?" 是":" yes")),y.push(chalk.bold.green("[n]")+chalk.gray(t?" 否":" no"))):y.push(chalk.bold.green("[ok]")+chalk.gray(t?" 继续":" next")),v&&(y.push(chalk.bold.magenta("[/ai]")+chalk.gray(t?" 🐼 熊猫导师":" 🐼 Panda tutor")),y.push(chalk.bold.magenta("[/submit]")+chalk.gray(t?" 提问评分":" prompt score")));const u=z.get(e.number);var h;y.push(chalk.bold.magenta(u?`[/honors ${h=u,j[h-1]??`#${h}`}]`:"[/honors]")+chalk.gray(t?" 排行":" board")),"normal"===g?y.push(chalk.bold.cyan("[wide]")+chalk.gray(t?" 宽屏":" widen")):("wide"===g?y.push(chalk.bold.cyan("[wider]")+chalk.gray(t?" 更宽":" wider")):"wider"===g?(y.push(chalk.bold.cyan("[widest]")+chalk.gray(t?" 最宽":" widest")),y.push(chalk.bold.cyan("[wide]")+chalk.gray(t?" 回宽屏":" back to wide"))):y.push(chalk.bold.cyan("[wider]")+chalk.gray(t?" 回 wider":" back to wider")),y.push(chalk.bold.cyan("[normal]")+chalk.gray(t?" 标准":" standard")));const p=s(q,A,e.number,t),m=p.startsWith("💬");if(y.push((m?chalk.bold.yellow("[r]"):chalk.gray("[r]"))+(m?chalk.bold.white(` ${p}`):chalk.gray(` ${p}`))),y.push(chalk.bold.yellow("[e]")+chalk.gray(t?" 提问或报告":" ask or report")),S){const o=S,e=`${o.done}/${o.total}`,n=o.completedAt?chalk.bold.green:chalk.bold.cyan;y.push(n("[task]")+chalk.gray(t?` 本周 ${e}`:` week ${e}`))}y.push(chalk.bold.cyan("[b]")+chalk.gray(t?" 上一张":" back")),y.push(chalk.bold.cyan("[card N]")+chalk.gray(t?" 跳转":" jump")),y.push(chalk.gray("[reload]")+(t?chalk.gray(" 刷新"):"")),y.push(chalk.gray("[quit]")+(t?chalk.gray(" 退出"):""));const b=o(r);console.log(chalk.gray(" "+r)),console.log(chalk.gray(" "+(t?`第 ${e.number} / ${n.totalCards} 张`:`Card ${e.number} / ${n.totalCards}`)));let f="",$=0;for(const e of y){const n=o(e);f&&$+2+n>b?(console.log(" "+f),f=e,$=n):(f=f?f+d+e:e,$=f===e?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(" "+r));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 o of w)for(const e of wrapMixed(o,b))console.log(chalk.gray(" "+e));E(),console.log()}(t,l)}export function renderCheckFeedback(o,e,n,t=!1){const l=c().startsWith("zh");if(console.log(),n)console.log(chalk.green(" ✓ ")+chalk.white(l?"答对了。":"Correct."));else if(console.log(chalk.yellow(" ✗ ")+chalk.white(l?"记下了,继续。":"Noted — moving on.")),t){const e="y"===o.check?.answer?l?"是":"Yes":l?"否":"No";console.log(chalk.gray(" "+(l?"正确答案: ":"Answer: ")+e+". "+(l?"回看上面那段。":"Re-read the paragraph above.")))}const r=l?o._zh?.solution??o.solution:o.solution;if(r&&r.length>0){console.log(),console.log(chalk.cyan(" 📝 "+(l?"解答:":"Worked solution:")));for(const o of r){for(const e of wrapMixed(o,i()-4))console.log(chalk.white(" "+e));console.log()}}console.log()}function D(o){return[chalk.white(" "+(o?"编著:Charlie Zhu":"Created & edited by Charlie Zhu")),chalk.gray(" "+(o?"ICOA 2026 创始人 · 课程总架构师":"Founder & Chief Curriculum Architect · ICOA 2026"))]}export function renderPhaseComplete(o,e,n,t,r,a){const s=c().startsWith("zh"),[g,d]=n.cardRange,y=e.checkResults||{};let u=0,h=0;for(let o=g;o<=d;o++){const e=y[String(o)];e&&(u++,e.correct&&h++)}const p="-".repeat(48);console.log();for(const o of l(" "))console.log(o);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 m=u>0&&h===u?chalk.bold.green:chalk.bold.white;console.log(chalk.gray(" "+(s?"理解检查得分: ":"comprehension score: "))+m(`${h}/${u}`)+chalk.gray(0===u?s?" (本阶段未作答 y/n)":" (no y/n answered)":""));const b=String(d).length;let f=!1,$=0,w=0,k=0,_=0;for(let e=g;e<=d;e++){const n=o.cards.find(o=>o.number===e);if(!n)continue;const t=n,l=t.title??t.badge??"",r=(s?t._zh?.title??t._zh?.badge??l:l)||"",a=null!=n.check,c=y[String(e)];let g;g=c?c.correct?chalk.green("✓"):chalk.red("✗"):a?chalk.gray("○"):chalk.gray("·");const d=N?N(e):null;d&&(f=!0);const u=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 h=wrapMixed(r,i()-10-b-(d?6:0)),p=h[0]+(h.length>1?"…":"");console.log(chalk.gray(` ${String(e).padStart(b)} `)+g+u+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)`)),r){const o=o=>"green"===o?chalk.bold.green:"blue"===o?chalk.bold.cyan:"yellow"===o?chalk.bold.yellow:chalk.bold.red;console.log(chalk.bold.magenta(" ⭐ "+(s?"提问得分 (best): ":"Prompt scores (best): "))+(0===r.length?chalk.gray(s?"本阶段尚未提问 — 用 /ai 研究,再 /submit 评分":"none this phase — research with /ai, then /submit"):r.map(e=>chalk.gray((s?"卡":"#")+e.card+" ")+o(e.tier)(String(e.score))).join(chalk.gray(" · "))))}if(a&&!a.configured){const o=d-g+1>12;console.log(chalk.gray(o?s?" 🔒 本阶段通关标准制作中 — 很快更新(360 层第二阶段标准设计中)":" 🔒 Pass criteria for this phase are in production — coming soon (360-tier second-stage standard in design)":s?" 🔒 本阶段通关标准制作中 — 很快更新(标准范式:3 场练习赛 + 3 卡研究)":" 🔒 Pass criteria for this phase are in production — coming soon (standard: 3 arenas + 3 research cards)"))}if(a?.configured&&a.contest){const o=a.contest;if(o.slots&&o.slots.length>0)for(const e of o.slots){const o=chalk.bold.cyan(` ⚔ ${s?"练习赛":"arena"} ${n.number}-${e.slot}: `);e.passed?console.log(o+chalk.bold.green(`${e.best_ac}/${e.n} AC ✅`)):0===e.attempts?console.log(o+chalk.white(s?`${e.n} 题 · 过 ${e.need} 题 — `:`${e.n} problems · AC ${e.need} — `)+chalk.bold.green(`contest ${e.slot}`)):console.log(o+chalk.yellow(s?`最好 ${e.best_ac}/${e.n}(需 ${e.need})· 已试 ${e.attempts} 次 — `:`best ${e.best_ac}/${e.n} (need ${e.need}) · ${e.attempts} attempt(s) — `)+chalk.bold.green(`contest ${e.slot}`))}else{const e=chalk.bold.cyan(" ⚖ "+(s?"判分赛: ":"Contest: "));o.passed?console.log(e+chalk.bold.green(`${o.best_ac}/${o.n} AC ✅ `+(s?"已过关":"passed"))):0===o.attempts?console.log(e+chalk.white(s?`${o.n} 题 · 过 ${o.need} 题解锁 — 输入 `:`${o.n} problems · AC ${o.need} to pass — type `)+chalk.bold.green("contest")+chalk.white(s?" 开始":" to start")):console.log(e+chalk.yellow(s?`最好 ${o.best_ac}/${o.n} AC(需 ${o.need})· 已试 ${o.attempts} 次 — 输入 `:`best ${o.best_ac}/${o.n} AC (need ${o.need}) · ${o.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 o of D(s))console.log(o)}export function renderCurriculumComplete(o,e){const n=c().startsWith("zh"),t=e.checkResults||{},r="-".repeat(48);let a=0,s=0;console.log();for(const o of l(" "))console.log(o);console.log(),console.log(chalk.bold.green(" "+r)),console.log(chalk.bold.green(" 🏆 "+(n?`${o.name} — 全部完成!`:`${o.name} — complete!`))),console.log(chalk.bold.green(" "+r));for(const e of o.modules){const[o,l]=e.cardRange;let r=0,c=0;for(let e=o;e<=l;e++){const o=t[String(e)];o&&(r++,o.correct&&c++)}a+=r,s+=c;const g=r>0&&c===r?chalk.green:chalk.white;console.log(chalk.gray(" "+(n?`阶段 ${e.number}`:`Phase ${e.number}`).padEnd(8))+g(`${c}/${r}`.padEnd(7))+chalk.gray(e.name))}console.log(chalk.bold.green(" "+r)),console.log(chalk.bold.green(" "+(n?"总分 / overall: ":"Overall: "))+chalk.bold.white(`${s}/${a}`)),console.log(chalk.bold.green(" "+r));for(const o of D(n))console.log(o);console.log()}export function renderMCQCard(o,t){const l=c(),r=e(o,l);p(r,t),f(),b(chalk.bold.yellow("🎯 "+r.title)),b(chalk.gray("─".repeat(Math.min(r.title.length+4,i())))),f();for(const o of M(r.question,i()-4))b(" "+o);f();for(const o of["A","B","C","D"])b(chalk.cyan(` ${o}.`)+" "+chalk.white(r.options[o]));f(),m(),console.log(chalk.gray(" "+(l.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",l))),console.log(chalk.gray(" ")+chalk.bold.cyan("back")+chalk.gray(l.startsWith("zh")?" 上一张 · ":" previous card · ")+chalk.bold.cyan("card N")+chalk.gray(l.startsWith("zh")?" 跳转":" jump")),console.log()}export function renderMCQFeedback(o,t,l,r){const a=c(),s=e(o,a);console.log(),l?console.log(" "+chalk.bold.green(n("correct",a)+" ")+chalk.gray(`${n("one_point",a)} · ${t} = ${s.options[t]}`)):console.log(" "+chalk.bold.red(n("not_quite",a)+" ")+chalk.gray(`${n("you_chose",a)} ${t}; ${n("answer_is",a)} ${s.answer}.`)),console.log(),console.log(chalk.gray(" "+n("explanation",a)));for(const o of M(s.explanation,i()-4))console.log(chalk.gray(" "+o));const g=Object.values(r.mcqResults),d=g.filter(o=>o.correct).length;console.log(),console.log(chalk.gray(" "+n("mcq_accuracy_so_far",a)+" ")+chalk.white(`${d}/${g.length}`)),console.log(),l||(console.log(chalk.gray(" "+n("press_ok_continue",a)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",a))),console.log())}export function renderPracticalCard(o,t){const l=c(),r=e(o,l);p(r,t),f(),b(chalk.bold.yellow("🛠 "+r.title)),b(chalk.gray("─".repeat(Math.min(r.title.length+4,i())))),f();for(const o of M(r.task,i()-4))b(" "+o);if(r.starterCode){f(),b(chalk.gray(" "+n("starter_code",l))),f();for(const o of r.starterCode.split("\n"))b(" "+chalk.cyan(o))}f(),m(),console.log(chalk.gray(" "+n("try_in_sandbox",l)+" ")+chalk.bold.cyan("!python3")+chalk.gray(" "+n("drops_into_python",l))),console.log(chalk.gray(" "+n("when_done",l))),console.log(chalk.gray(" ")+chalk.bold.green("done")+chalk.gray(" "+n("done_desc",l))),console.log(chalk.gray(" ")+chalk.bold.yellow("skip")+chalk.gray(" "+n("skip_desc",l))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log()}export function renderPracticalSuccess(o){const t=c(),l=e(o,t);console.log(),console.log(" "+chalk.bold.green(n("practical_recorded",t))),console.log(),console.log(chalk.gray(" "+n("reference_answer",t)));for(const o of M(l.successHint,i()-4))console.log(chalk.gray(" "+o));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(o,t){const l=c(),r=e(o,l);p(r,t),f(),b(chalk.bold.yellow("🎬 "+r.title)),f();for(const o of M(r.description,i()-4))b(" "+o);f(),b(chalk.gray(" "+n("sim_requires",l))),f(),m(),console.log(chalk.gray(" ")+chalk.bold.cyan("sim")+chalk.gray(" "+n("sim_launch_desc",l))),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",l))),console.log(chalk.gray(" bookmark "+n("bookmark_desc",l))),console.log(chalk.gray(" back "+n("back_desc",l))),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",l))),console.log()}export function renderArenaTaskCard(o,n){const t=c(),l=t.startsWith("zh"),r=e(o,t);p(r,n),f(),b(chalk.bold.cyan("🏟 "+r.title)),b(chalk.gray(" "+r.origin+" · "+r.domain+" · "+r.metric)),f();for(const o of r.statement){for(const e of M(o,i()-4))b(" "+e);f()}b(chalk.bold.green(l?" 核心要点 — baseline → 解法的关键变化":" CORE DELTA — baseline → solution"));for(const o of r.core_delta)for(const e of M("• "+o,i()-6))b(" "+chalk.white(e));f(),b(chalk.gray((l?" 提交格式: ":" Submission: ")+r.submission_format)),r.hints&&b(chalk.gray(l?" 本题有 5 级提示 (a–e),进入 arena 后逐级解锁。":" 5 progressive hints (a–e) unlock inside the arena.")),f(),m(),console.log(chalk.gray(" ")+chalk.bold.cyan("arena "+r.task_id)+chalk.gray(l?" 动手做这道题(下载 / 提示 / 提交 / 榜单)":" open the arena (download / hints / submit / board)")),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(l?" 继续下一张":" continue to next")),console.log(chalk.gray(" back "+(l?"上一张":"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 "+(l?"退出":"exit"))),console.log()}export function renderComingSoonCard(o,n){const t=c(),l=t.startsWith("zh"),r=e(o,t);p(r,n),f(),b(chalk.bold.yellow("🔜 "+r.title)),f();const a=r.note||(l?"这道竞技场题目正在编写中,敬请期待。":"This arena task is coming soon.");for(const o of M(a,i()-4))b(" "+chalk.gray(o));f(),m(),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(l?" 继续":" 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 "+(l?"退出":"exit"))),console.log()}export function renderMilestone(o,t){const l=c(),r=e(o,l),a=o.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",l))),console.log(chalk.bold.yellow(" ║"));const s=` ${r.badge} ${r.emoji}`;console.log(chalk.bold.yellow(" ║ ")+chalk.bold.green(s)),console.log(chalk.bold.yellow(" ║ ")+chalk.gray(" ─".repeat(Math.max(1,Math.floor(r.badge.length/2+2))))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("in_wild_corresponds",l)));for(const o of M(r.realWorldLevel,i()-4))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("whats_next",l)));for(const o of M(r.unlockedNext,i()-4))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ╰"+d("═"))),console.log(),a?(console.log(chalk.gray(" "+n("demo_complete",l))),console.log(),console.log(chalk.gray(" "+n("unlock_full",l)+" ")+chalk.white(n("curriculum_name",l))+chalk.gray(",")),console.log(chalk.gray(" "+n("contact_team_leader",l)+" ")+chalk.bold.yellow("EA")+chalk.gray(" "+n("learn_token",l))),console.log(chalk.gray(" "+n("or_email",l)+" ")+chalk.cyan("australia@icoa2026.au")+chalk.gray(" "+n("for_partnership",l))),console.log(),console.log(chalk.gray(" "+n("type_quit",l)+" ")+chalk.bold.green("quit")+chalk.gray(" "+n("to_exit",l)+" ")+chalk.bold.green("status")+chalk.gray(" "+n("for_dashboard",l)))):(console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",l))),console.log(chalk.gray(" status "+n("status_full",l))),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",l)))),console.log()}export function renderStatus(e,t){const l=c(),r=t.cardsCompleted.length,a=e.totalCards,s=Object.values(t.mcqResults),g=s.filter(o=>o.correct).length;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(n("status_title",l))+" "+chalk.cyan("─".repeat(Math.max(4,Math.min(25,i()-4-o(n("status_title",l))))))),f(),b(" "+chalk.gray(n("total_progress",l)+" ")+u(r,a)+chalk.gray(` (${r}/${a})`)),b(" "+chalk.gray(n("streak",l)+" ")+h(t.streakDays)+chalk.gray(` (${n("longest",l)} ${t.longestStreak})`)),b(" "+chalk.gray(n("mcq_accuracy",l)+" ")+chalk.white(`${g}/${s.length}`)),b(" "+chalk.gray(n("practicals_done",l)+" ")+chalk.white(`${t.practicalsCompleted.length}`)),b(" "+chalk.gray(n("bookmarked",l)+" ")+chalk.white(`${t.bookmarks.length}`)),f();const d=n("module",l).replace(":","").trim();for(const o of e.modules){const[e,n]=o.cardRange,l=t.cardsCompleted.filter(o=>o>=e&&o<=n).length,r=n-e+1;b(" "+(l===r?chalk.green("✓"):l>0?chalk.yellow("▶"):chalk.gray("□"))+" "+chalk.gray(`${d} ${o.number}: `)+chalk.white(`${l}/${r}`)+chalk.gray(" "+o.name))}if(f(),t.achievements.length>0){b(" "+chalk.gray(n("achievements",l)));for(const o of t.achievements)b(" "+chalk.bold.yellow("★ ")+chalk.white(o))}else b(" "+chalk.gray(n("achievements_none",l)));f(),m()}
|
|
1
|
+
import chalk from"chalk";import o from"string-width";import{localized as e}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 l,detectCaps as a}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(o){return"normal"===o.learnWidth||"wide"===o.learnWidth||"wider"===o.learnWidth||"widest"===o.learnWidth?o.learnWidth:o.learnWide?"wide":"normal"}export function setLearnWidth(o){g=o}export function learnWidthCap(o=g){return"widest"===o?160:"wider"===o?120:"wide"===o?96:64}export function learnW(){const o=process.stdout.columns||69,e=Math.max(40,Math.min(64,o-5));return"normal"===g?e:Math.max(e,Math.min(o-6,learnWidthCap()))}const i=learnW;function d(o="─"){return o.repeat(i()+2)}function y(e,n){if(o(e)<=n)return e;let t=e;for(;t.length>0&&o(t)>Math.max(1,n-1);)t=t.slice(0,-1);return t.trimEnd()+"…"}function h(o,e,n=20){const t=e>0?o/e:0,r=Math.floor(t*n),l=n-r;return`${chalk.green("█".repeat(r))+chalk.gray("░".repeat(l))} ${(100*t).toFixed(1)}%`}function u(o){return o>=30?chalk.red("🔥🔥🔥 "+o+" days"):o>=7?chalk.yellow("🔥 "+o+" days"):o>=1?chalk.gray("· "+o+" day"+(o>1?"s":"")):chalk.gray("—")}export function renderWelcome(o,e,t){const r=c(),l=e.cardsCompleted.length,a=o.totalCards,s=a-l,g=o.modules.find(o=>e.currentCard>=o.cardRange[0]&&e.currentCard<=o.cardRange[1]),p=/demo/i.test(o.id);if(console.log(),console.log(chalk.cyan(" ╭"+d("═"))),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ║ ")+chalk.bold.white(" "+(o.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 o=new Date(e.lastSeenAt),t=Math.floor((Date.now()-o.getTime())/36e5),l=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)} ${l}`))}console.log(chalk.cyan(" ║"));const m=y(g?`${g.number}. ${g.name}`:"—",i()-14);console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("module",r).padEnd(11))+chalk.white(m)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("progress",r).padEnd(11))+h(l,a,Math.max(6,Math.min(20,i()-26-`${l}/${a}`.length)))+chalk.gray(` (${l}/${a})`)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("streak",r).padEnd(11))+u(e.streakDays)),s>0&&e.currentCard<=a&&console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("next_card",r).padEnd(11))+chalk.white(`#${e.currentCard} ${n("of",r)} ${a}`)),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ╰"+d("═"))),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────"));const b=0===l?n("continue_start",r):`${n("continue_resume",r)} ${e.currentCard}`;console.log(chalk.bold.green(" continue")+chalk.gray(" "+b)),console.log(chalk.yellow(" status")+chalk.gray(" "+n("status_full",r))),e.bookmarks.length>0&&console.log(chalk.yellow(" bookmarks")+chalk.gray(` ${e.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(e,t){const r=c(),l=e.module,a=t.modules.find(o=>o.number===l),s=a?a.name:n("unknown_module",r),g=n("module",r).replace(":","").trim(),d=y(`${n("card",r)} ${e.number} / ${t.totalCards} · ${g} ${l} · ${s}`,i()-4);console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(d)+" "+chalk.cyan("─".repeat(Math.max(0,i()-o(d)-4))))}function m(){console.log(chalk.cyan(" ╰"+d())),console.log()}function b(o){console.log(chalk.cyan(" │ ")+chalk.white(o))}function f(){console.log(chalk.cyan(" │"))}const $=/[┌┐└┘├┤┬┴┼─│╔╗╚╝═║╠╣╦╩╬▓▒░█]/;function w(o){return/^(?: {4}|\t)/.test(o)||$.test(o)||/ {3,}/.test(o)}export function wrapMixed(e,n){const t=[];let r="",l=0,a=!1;for(const s of function(e){const n=[],t=e=>{let t="";const r=()=>{t&&(n.push({t:"atom",s:t}),t="")};for(const l of e)" "===l?(r(),n.push({t:"space"})):2===o(l)?(r(),n.push({t:"wide",s:l})):t+=l;r()},r=/`[^`]*`/g;let l,a=0;for(;null!==(l=r.exec(e));)t(e.slice(a,l.index)),n.push({t:"code",s:l[0]}),a=l.index+l[0].length;return t(e.slice(a)),n}(e)){if("space"===s.t){""!==r&&(a=!0);continue}const e=s.s,c=o("code"===s.t?e.replace(/`/g,""):e),g=a?1:0,i="atom"===s.t&&e.length<=2&&/^[.,;:!?)\]}…'"]+$/.test(e);""!==r&&!i&&l+g+c>n?(t.push(r),r=e,l=c,a=!1):(a&&(r+=" ",l+=1,a=!1),r+=e,l+=c)}return r&&t.push(r),t}const _=/\b(?:Common bug|Common mistake|Common error|Pitfall|Gotcha|Footgun)\b[::]?|\b(?:Warning|Caution)\b[::]|(?:常见错误|常见陷阱|常见误区|陷阱|注意|警告|误区)[::]/gi;function k(o,e){const n=e?chalk.bold.white:chalk.white;let t,r="",l=0;for(_.lastIndex=0;null!==(t=_.exec(o));)t.index>l&&(r+=n(o.slice(l,t.index))),r+=chalk.bold.yellow(t[0]),l=t.index+t[0].length;return l<o.length&&(r+=n(o.slice(l))),r}function x(o,e=!1){let n="",t=0;const r=/`([^`]*)`/g;let l;for(;null!==(l=r.exec(o));)l.index>t&&(n+=k(o.slice(t,l.index),e)),n+=chalk.cyan(l[1]),t=l.index+l[0].length;return t<o.length&&(n+=k(o.slice(t),e)),n}function C(o,e=!1){let n=!1;for(const t of o.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 o of wrapMixed(t,i()-4))console.log(chalk.cyan(" │ ")+` ${x(o,e)}`);else f()}function M(o,e){const n=[];for(const t of o.split("\n")){if(""===t){n.push("");continue}if(w(t)){n.push(t);continue}let o="";for(const r of t.split(" "))(o+" "+r).trim().length>e?(n.push(o.trim()),o=r):o=(o+" "+r).trim();o&&n.push(o)}return n}let v=!1;export function setLearnAiEnabled(o){v=o}let W=new Set;export function setLearnPqsViewCards(o){W=o}let S=new Map;export function setLearnHonorRanks(o){S=o}const z=["①","②","③","④","⑤","⑥","⑦","⑧","⑨","⑩"];let j=[];export function setLearnReplies(o){j=o}let A=[];export function setLearnFaqs(o){A=o}let q=null;export function setLearnHometask(o){q=o}let P="full";export function setLearnFooterProfile(o){P=o}let E=null;export function setLearnFooterExtra(o){E=o}function L(){const o=E?.();o&&console.log(chalk.gray(" "+o))}let R=null;export function setLearnPracticeChips(o){R=o}let N=null;export function setLearnPracticeCount(o){N=o}export function renderHonorWall(o){const e=c().startsWith("zh"),{totals:n,chapters:t}=o,r=n.gold+n.silver+n.bronze,l=(o,e)=>e>0?chalk.bold.white(`${o}×${e}`):chalk.gray(`${o}×0`);if(console.log(chalk.gray(" ─────────────────────────────────────────")),console.log(chalk.bold.white(e?" 🏅 奖牌墙 ":" 🏅 Medal wall ")+l("🥇",n.gold)+chalk.gray(" · ")+l("🥈",n.silver)+chalk.gray(" · ")+l("🥉",n.bronze)+chalk.gray(e?" (永久 — 挂上墙就不会摘下)":" (permanent — once earned, never removed)")),0===r){const n=null!=o.floor?`${o.floor}+`:e?"高分":"a high score";return console.log(chalk.gray(e?` 还没有奖牌 — 单卡提问评分 ${n} 并进入该卡前三,即可永久挂墙 🥇🥈🥉`:` No medals yet — score ${n} on a card and hold its top-3 to hang one 🥇🥈🥉`)),void console.log(chalk.gray(" ─────────────────────────────────────────"))}const a=t.map(o=>{const e=`Ch${o.chapter}`,n=(o.gold>0?`🥇${o.gold}`:"")+(o.silver>0?`🥈${o.silver}`:"")+(o.bronze>0?`🥉${o.bronze}`:"");return n?chalk.white(`${e} ${n}`):chalk.gray(`${e} —`)}).join(chalk.gray(" "));console.log(" "+a);const s=t.flatMap(o=>o.cards.map(e=>({...e,chapter:o.chapter})));s.sort((o,e)=>o.rank-e.rank||o.chapter-e.chapter||o.card-e.card);for(const o of s.slice(0,24)){const n=(o.since||"").slice(0,10);console.log(chalk.white(` ${o.icon} `)+chalk.gray(`Ch${o.chapter} · `)+chalk.white(e?`第 ${o.card} 张`:`card ${o.card}`)+chalk.gray(` · ${o.score}`)+(n?chalk.gray(e?` · 始于 ${n}`:` · since ${n}`):""))}s.length>24&&console.log(chalk.gray(e?` …还有 ${s.length-24} 枚`:` …and ${s.length-24} more`)),console.log(chalk.gray(" ─────────────────────────────────────────"))}export function renderKnowledgeCard(t,r){const d=c(),y=e(t,d);p(y,r),f();const h=wrapMixed(y.title,i()-4);for(const o of h)b(chalk.bold.yellow(o));b(chalk.gray("─".repeat(Math.min(Math.max(...h.map(e=>o(e))),i()))));const u=!!(t.chart_ids&&t.chart_ids.length>0);if(u&&l(t.chart_ids.slice(0,1),r.charts,a()),y.key_point&&""!==y.key_point.trim()?(u||f(),function(o,e,n){const t=c().startsWith("zh"),r=a().cols,l="-".repeat(Math.min(i(),Math.max(24,r-6))),s=t?"要点":"KEY POINT",g=t?`第 ${e} / ${n} 张`:`Card ${e} / ${n}`;console.log(chalk.cyan(" │ ")+chalk.green(l)),console.log(chalk.cyan(" │ ")+chalk.green(`${s} · ${g}`));for(const e of wrapMixed(o,i()-4))console.log(chalk.cyan(" │ ")+chalk.bold.green(" "+e));console.log(chalk.cyan(" │ ")+chalk.green(l)),f()}(y.key_point,y.number,r.totalCards)):u||f(),y.body.filter(o=>null!=o&&""!==o.trim()).forEach((o,e)=>{const n=0===e?o.match(/^([\s\S]*?(?:[.!?](?=\s|$)|[。!?]))(\s[\s\S]*)?$/):null;if(n&&n[1].trim().length<=180){C(n[1],!0);const o=n[2]?.trimStart();o&&C(o,!1)}else C(o,!1);f()}),y.icoaConnection){b(chalk.magenta(" "+n("icoa_connection",d))),b(chalk.gray(" "+"─".repeat(20)));for(const o of wrapMixed(y.icoaConnection,i()-4))b(" "+chalk.magenta(o));f()}if(t.check){const o=d.startsWith("zh"),e=t._zh?.checkStatement??t._zh?.check?.statement,n=o?e??t.check.statement:t.check.statement;for(const e of wrapMixed(o?"请回答 (输 y 或 n):":"Quick comprehension check (type y or n):",i()-8))b(chalk.cyan(" 🤔 "+e));b(chalk.gray(" "+"─".repeat(20)));for(const o of wrapMixed(n,i()-4))b(" "+chalk.white(o));f()}m(),function(e,n){const t=c().startsWith("zh"),r=a().cols,l="-".repeat(Math.min(i(),Math.max(24,r-6))),d=chalk.gray(" ");if("minimal"===P){const o=R?R(e.number):null;console.log(chalk.gray(" "+l)),console.log(chalk.gray(" "+(t?`第 ${e.number} / ${n.totalCards} 张`:`Card ${e.number} / ${n.totalCards}`))+(o?chalk.gray(t?" · 本卡练习 ":" · practice ")+o:""));const r=[];if(e.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")),o){const e=Math.max(1,(o.match(/\[\d\]/g)??[]).length),n=Array.from({length:e},(o,e)=>`[${e+1}]`).join("");r.push(chalk.bold.cyan(n)+chalk.gray(t?" 练习":" practice"))}return 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)),L(),void console.log()}const y=[];e.check?(y.push(chalk.bold.green("[y]")+chalk.gray(t?" 是":" yes")),y.push(chalk.bold.green("[n]")+chalk.gray(t?" 否":" no"))):y.push(chalk.bold.green("[ok]")+chalk.gray(t?" 继续":" next")),v&&(y.push(chalk.bold.magenta("[/ai]")+chalk.gray(t?" 🐼 熊猫导师":" 🐼 Panda tutor")),y.push(chalk.bold.magenta("[/submit]")+chalk.gray(t?" 提问评分":" prompt score")));const h=S.get(e.number);var u;y.push(chalk.bold.magenta(h?`[/honors ${u=h,z[u-1]??`#${u}`}]`:"[/honors]")+chalk.gray(t?" 排行":" board")),"normal"===g?y.push(chalk.bold.cyan("[wide]")+chalk.gray(t?" 宽屏":" widen")):("wide"===g?y.push(chalk.bold.cyan("[wider]")+chalk.gray(t?" 更宽":" wider")):"wider"===g?(y.push(chalk.bold.cyan("[widest]")+chalk.gray(t?" 最宽":" widest")),y.push(chalk.bold.cyan("[wide]")+chalk.gray(t?" 回宽屏":" back to wide"))):y.push(chalk.bold.cyan("[wider]")+chalk.gray(t?" 回 wider":" back to wider")),y.push(chalk.bold.cyan("[normal]")+chalk.gray(t?" 标准":" standard")));const p=s(j,A,e.number,t),m=p.startsWith("💬");if(y.push((m?chalk.bold.yellow("[r]"):chalk.gray("[r]"))+(m?chalk.bold.white(` ${p}`):chalk.gray(` ${p}`))),y.push(chalk.bold.yellow("[e]")+chalk.gray(t?" 提问或报告":" ask or report")),q){const o=q,e=`${o.done}/${o.total}`,n=o.completedAt?chalk.bold.green:chalk.bold.cyan;y.push(n("[task]")+chalk.gray(t?` 本周 ${e}`:` week ${e}`))}y.push(chalk.bold.cyan("[b]")+chalk.gray(t?" 上一张":" back")),y.push(chalk.bold.cyan("[card N]")+chalk.gray(t?" 跳转":" jump")),y.push(chalk.gray("[reload]")+(t?chalk.gray(" 刷新"):"")),y.push(chalk.gray("[quit]")+(t?chalk.gray(" 退出"):""));const b=o(l);console.log(chalk.gray(" "+l)),console.log(chalk.gray(" "+(t?`第 ${e.number} / ${n.totalCards} 张`:`Card ${e.number} / ${n.totalCards}`)));let f="",$=0;for(const e of y){const n=o(e);f&&$+2+n>b?(console.log(" "+f),f=e,$=n):(f=f?f+d+e:e,$=f===e?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(" "+l));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 o of w)for(const e of wrapMixed(o,b))console.log(chalk.gray(" "+e));L(),console.log()}(t,r)}export function renderCheckFeedback(o,e,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 e="y"===o.check?.answer?r?"是":"Yes":r?"否":"No";console.log(chalk.gray(" "+(r?"正确答案: ":"Answer: ")+e+". "+(r?"回看上面那段。":"Re-read the paragraph above.")))}const l=r?o._zh?.solution??o.solution:o.solution;if(l&&l.length>0){console.log(),console.log(chalk.cyan(" 📝 "+(r?"解答:":"Worked solution:")));for(const o of l){for(const e of wrapMixed(o,i()-4))console.log(chalk.white(" "+e));console.log()}}console.log()}function F(o){return[chalk.white(" "+(o?"编著:Charlie Zhu":"Created & edited by Charlie Zhu")),chalk.gray(" "+(o?"ICOA 2026 创始人 · 课程总架构师":"Founder & Chief Curriculum Architect · ICOA 2026"))]}export function renderPhaseComplete(o,e,n,t,l,a){const s=c().startsWith("zh"),[g,d]=n.cardRange,y=e.checkResults||{};let h=0,u=0;for(let o=g;o<=d;o++){const e=y[String(o)];e&&(h++,e.correct&&u++)}const p="-".repeat(48);console.log();for(const o of r(" "))console.log(o);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 m=h>0&&u===h?chalk.bold.green:chalk.bold.white;console.log(chalk.gray(" "+(s?"理解检查得分: ":"comprehension score: "))+m(`${u}/${h}`)+chalk.gray(0===h?s?" (本阶段未作答 y/n)":" (no y/n answered)":""));const b=String(d).length;let f=!1,$=0,w=0,_=0,k=0;for(let e=g;e<=d;e++){const n=o.cards.find(o=>o.number===e);if(!n)continue;const t=n,r=t.title??t.badge??"",l=(s?t._zh?.title??t._zh?.badge??r:r)||"",a=null!=n.check,c=y[String(e)];let g;g=c?c.correct?chalk.green("✓"):chalk.red("✗"):a?chalk.gray("○"):chalk.gray("·");const d=N?N(e):null;d&&(f=!0);const h=d?(d.solved>=d.total?chalk.green:d.solved>0?chalk.yellow:chalk.gray)(` ⚔${d.solved}/${d.total}`):"";d&&($++,d.solved>=d.total&&w++,_+=d.solved,k+=d.total);const u=wrapMixed(l,i()-10-b-(d?6:0)),p=u[0]+(u.length>1?"…":"");console.log(chalk.gray(` ${String(e).padStart(b)} `)+g+h+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)`)),l){const o=o=>"green"===o?chalk.bold.green:"blue"===o?chalk.bold.cyan:"yellow"===o?chalk.bold.yellow:chalk.bold.red;console.log(chalk.bold.magenta(" ⭐ "+(s?"提问得分 (best): ":"Prompt scores (best): "))+(0===l.length?chalk.gray(s?"本阶段尚未提问 — 用 /ai 研究,再 /submit 评分":"none this phase — research with /ai, then /submit"):l.map(e=>chalk.gray((s?"卡":"#")+e.card+" ")+o(e.tier)(String(e.score))).join(chalk.gray(" · "))))}if(a&&!a.configured){const o=d-g+1>12;console.log(chalk.gray(o?s?" 🔒 本阶段通关标准制作中 — 很快更新(360 层第二阶段标准设计中)":" 🔒 Pass criteria for this phase are in production — coming soon (360-tier second-stage standard in design)":s?" 🔒 本阶段通关标准制作中 — 很快更新(标准范式:3 场练习赛 + 3 卡研究)":" 🔒 Pass criteria for this phase are in production — coming soon (standard: 3 arenas + 3 research cards)"))}if(a?.configured&&a.contest){const o=a.contest;if(o.slots&&o.slots.length>0)for(const e of o.slots){const o=chalk.bold.cyan(` ⚔ ${s?"练习赛":"arena"} ${n.number}-${e.slot}: `);e.passed?console.log(o+chalk.bold.green(`${e.best_ac}/${e.n} AC ✅`)):0===e.attempts?console.log(o+chalk.white(s?`${e.n} 题 · 过 ${e.need} 题 — `:`${e.n} problems · AC ${e.need} — `)+chalk.bold.green(`contest ${e.slot}`)):console.log(o+chalk.yellow(s?`最好 ${e.best_ac}/${e.n}(需 ${e.need})· 已试 ${e.attempts} 次 — `:`best ${e.best_ac}/${e.n} (need ${e.need}) · ${e.attempts} attempt(s) — `)+chalk.bold.green(`contest ${e.slot}`))}else{const e=chalk.bold.cyan(" ⚖ "+(s?"判分赛: ":"Contest: "));o.passed?console.log(e+chalk.bold.green(`${o.best_ac}/${o.n} AC ✅ `+(s?"已过关":"passed"))):0===o.attempts?console.log(e+chalk.white(s?`${o.n} 题 · 过 ${o.need} 题解锁 — 输入 `:`${o.n} problems · AC ${o.need} to pass — type `)+chalk.bold.green("contest")+chalk.white(s?" 开始":" to start")):console.log(e+chalk.yellow(s?`最好 ${o.best_ac}/${o.n} AC(需 ${o.need})· 已试 ${o.attempts} 次 — 输入 `:`best ${o.best_ac}/${o.n} AC (need ${o.need}) · ${o.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 o of F(s))console.log(o)}export function renderCurriculumComplete(o,e){const n=c().startsWith("zh"),t=e.checkResults||{},l="-".repeat(48);let a=0,s=0;console.log();for(const o of r(" "))console.log(o);console.log(),console.log(chalk.bold.green(" "+l)),console.log(chalk.bold.green(" 🏆 "+(n?`${o.name} — 全部完成!`:`${o.name} — complete!`))),console.log(chalk.bold.green(" "+l));for(const e of o.modules){const[o,r]=e.cardRange;let l=0,c=0;for(let e=o;e<=r;e++){const o=t[String(e)];o&&(l++,o.correct&&c++)}a+=l,s+=c;const g=l>0&&c===l?chalk.green:chalk.white;console.log(chalk.gray(" "+(n?`阶段 ${e.number}`:`Phase ${e.number}`).padEnd(8))+g(`${c}/${l}`.padEnd(7))+chalk.gray(e.name))}console.log(chalk.bold.green(" "+l)),console.log(chalk.bold.green(" "+(n?"总分 / overall: ":"Overall: "))+chalk.bold.white(`${s}/${a}`)),console.log(chalk.bold.green(" "+l));for(const o of F(n))console.log(o);console.log()}export function renderMCQCard(o,t){const r=c(),l=e(o,r);p(l,t),f(),b(chalk.bold.yellow("🎯 "+l.title)),b(chalk.gray("─".repeat(Math.min(l.title.length+4,i())))),f();for(const o of M(l.question,i()-4))b(" "+o);f();for(const o of["A","B","C","D"])b(chalk.cyan(` ${o}.`)+" "+chalk.white(l.options[o]));f(),m(),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(o,t,r,l){const a=c(),s=e(o,a);console.log(),r?console.log(" "+chalk.bold.green(n("correct",a)+" ")+chalk.gray(`${n("one_point",a)} · ${t} = ${s.options[t]}`)):console.log(" "+chalk.bold.red(n("not_quite",a)+" ")+chalk.gray(`${n("you_chose",a)} ${t}; ${n("answer_is",a)} ${s.answer}.`)),console.log(),console.log(chalk.gray(" "+n("explanation",a)));for(const o of M(s.explanation,i()-4))console.log(chalk.gray(" "+o));const g=Object.values(l.mcqResults),d=g.filter(o=>o.correct).length;console.log(),console.log(chalk.gray(" "+n("mcq_accuracy_so_far",a)+" ")+chalk.white(`${d}/${g.length}`)),console.log(),r||(console.log(chalk.gray(" "+n("press_ok_continue",a)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",a))),console.log())}export function renderPracticalCard(o,t){const r=c(),l=e(o,r);p(l,t),f(),b(chalk.bold.yellow("🛠 "+l.title)),b(chalk.gray("─".repeat(Math.min(l.title.length+4,i())))),f();for(const o of M(l.task,i()-4))b(" "+o);if(l.starterCode){f(),b(chalk.gray(" "+n("starter_code",r))),f();for(const o of l.starterCode.split("\n"))b(" "+chalk.cyan(o))}f(),m(),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(o){const t=c(),r=e(o,t);console.log(),console.log(" "+chalk.bold.green(n("practical_recorded",t))),console.log(),console.log(chalk.gray(" "+n("reference_answer",t)));for(const o of M(r.successHint,i()-4))console.log(chalk.gray(" "+o));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(o,t){const r=c(),l=e(o,r);p(l,t),f(),b(chalk.bold.yellow("🎬 "+l.title)),f();for(const o of M(l.description,i()-4))b(" "+o);f(),b(chalk.gray(" "+n("sim_requires",r))),f(),m(),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(o,n){const t=c(),r=t.startsWith("zh"),l=e(o,t);p(l,n),f(),b(chalk.bold.cyan("🏟 "+l.title)),b(chalk.gray(" "+l.origin+" · "+l.domain+" · "+l.metric)),f();for(const o of l.statement){for(const e of M(o,i()-4))b(" "+e);f()}b(chalk.bold.green(r?" 核心要点 — baseline → 解法的关键变化":" CORE DELTA — baseline → solution"));for(const o of l.core_delta)for(const e of M("• "+o,i()-6))b(" "+chalk.white(e));f(),b(chalk.gray((r?" 提交格式: ":" Submission: ")+l.submission_format)),l.hints&&b(chalk.gray(r?" 本题有 5 级提示 (a–e),进入 arena 后逐级解锁。":" 5 progressive hints (a–e) unlock inside the arena.")),f(),m(),console.log(chalk.gray(" ")+chalk.bold.cyan("arena "+l.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(o,n){const t=c(),r=t.startsWith("zh"),l=e(o,t);p(l,n),f(),b(chalk.bold.yellow("🔜 "+l.title)),f();const a=l.note||(r?"这道竞技场题目正在编写中,敬请期待。":"This arena task is coming soon.");for(const o of M(a,i()-4))b(" "+chalk.gray(o));f(),m(),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(o,t){const r=c(),l=e(o,r),a=o.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=` ${l.badge} ${l.emoji}`;console.log(chalk.bold.yellow(" ║ ")+chalk.bold.green(s)),console.log(chalk.bold.yellow(" ║ ")+chalk.gray(" ─".repeat(Math.max(1,Math.floor(l.badge.length/2+2))))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("in_wild_corresponds",r)));for(const o of M(l.realWorldLevel,i()-4))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("whats_next",r)));for(const o of M(l.unlockedNext,i()-4))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ╰"+d("═"))),console.log(),a?(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(e,t,r={}){const l=c(),a=l.startsWith("zh"),s=t.cardsCompleted.length,g=e.totalCards,d=t.checkResults||{},p=r.gate??null,$=!!r.gated,w=r.focus??"current",_=r.posForSlot??(o=>4===o?10:4*o),k=t.currentCard,x=e.modules.find(o=>k>=o.cardRange[0]&&k<=o.cardRange[1]),C=(e,n)=>e+" ".repeat(Math.max(0,n-o((o=>o.replace(/\x1b\[[0-9;]*m/g,""))(e))));console.log();const M=`${e.name} — ${a?"状态":"Status"}`;console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(M)+" "+chalk.cyan("─".repeat(Math.max(4,i()-2-o(M))))),f(),b(" "+chalk.gray(n("total_progress",l)+" ")+h(s,g)+chalk.gray(` (${s}/${g})`)+chalk.gray(" "+n("streak",l)+" ")+u(t.streakDays)),f();const v=o=>a?`阶段 ${o}`:`Phase ${o}`,W=Math.max(10,Math.min(28,i()-54));for(const o of e.modules){const[n,r]=o.cardRange,l=r-n+1,s=t.cardsCompleted.filter(o=>o>=n&&o<=r).length,c=$&&!!p&&o.number>p.unlocked_through_phase,g=p?.phases.find(e=>e.phase===o.number);let h=0,u=0,m=0;const f=[],M=[];let S=0,z=0,j=0,A=0;const q=[];for(let o=n;o<=r;o++){const n=e.cards.find(e=>e.number===o);if(null!=n?.check){h++;const e=d[String(o)];e?(u++,e.correct?m++:M.push(o)):f.push(o)}const t=N?N(o):null;t&&(S++,j+=t.solved,A+=t.total,t.solved>=t.total?z++:q.push(`${o}:${t.solved}/${t.total}`))}const P=g?.configured?g.contest?.slots??[]:[],E=o=>o.passed?chalk.green("✅"):o.attempts>0?chalk.yellow("❌"):chalk.gray("⬜"),L=s>=l&&(0===h||m===h)&&(0===S||z===S)&&(0===P.length||P.every(o=>o.passed))&&(!g?.research||g.research.passed),R=s>0||u>0||j>0||P.some(o=>o.attempts>0),F=c?chalk.gray("🔒"):L?chalk.green("✅"):o.number===x?.number?chalk.bold.cyan("▶ "):R?chalk.yellow("▶ "):chalk.gray("⬜"),D=y(o.name,W),O=[];if(O.push((s>=l?chalk.green:chalk.white)(`${s}/${l}`)+chalk.gray(a?"卡":"c")),h>0&&O.push(chalk.gray("✓")+(m===h?chalk.green:chalk.white)(`${m}/${h}`)),S>0&&O.push(chalk.gray("⚔")+(z===S?chalk.green:chalk.white)(`${j}/${A}`)),P.length>0&&O.push(chalk.gray(a?"赛":"arena ")+P.map(E).join("")),g?.research&&O.push(chalk.gray("⭐")+(g.research.passed?chalk.green:chalk.white)(`${g.research.good_cards}/${g.research.need_cards}`)),b(" "+F+" "+chalk.white(C(v(o.number),a?5:8))+chalk.gray(C(D,W+1))+O.join(chalk.gray(" · "))),c||"all"!==w&&w!==o.number&&("current"!==w||o.number!==x?.number)){if(!c&&R&&!L){const e=[],t=P.filter(o=>!o.passed).map(e=>(e.attempts>0?chalk.yellow:chalk.gray)(`${o.number}-${e.slot}@${a?"卡":"card "}${n+_(e.slot)-1}`));t.length&&e.push(chalk.gray(a?"赛 ":"arena ")+t.join(chalk.gray(" · ")));const r=(o,e)=>o.slice(0,8).join(e)+(o.length>8?chalk.gray(" +"+(o.length-8)):"");q.length&&e.push(chalk.gray("⚔ ")+chalk.white(r(q," "))),M.length&&e.push(chalk.red("✗ ")+chalk.white(r(M.map(String),","))),f.length&&e.push(chalk.gray("○ ")+chalk.white(r(f.map(String),","))),g?.research&&!g.research.passed&&e.push(chalk.gray(`⭐ ${g.research.good_cards}/${g.research.need_cards}`)),e.forEach((o,e)=>b(chalk.gray(0===e?a?" 待办: ":" todo: ":" ")+o))}}else{const t=String(r).length;for(let l=n;l<=r;l++){const r=e.cards.find(o=>o.number===l);if(!r)continue;const s=r,c=s.title??s.badge??"",g=(a?s._zh?.title??s._zh?.badge??c:c)||"",y=null!=r.check,h=d[String(l)],u=h?h.correct?chalk.green("✓"):chalk.red("✗"):y?chalk.gray("○"):chalk.gray("·"),p=N?N(l):null,m=p?(p.solved>=p.total?chalk.green:p.solved>0?chalk.yellow:chalk.gray)(`⚔${p.solved}/${p.total}`):" ",f=l===k?chalk.bold.cyan("▶"):" ",$=wrapMixed(g,Math.max(16,i()-14-t)),w=$[0]+($.length>1?"…":"");b(` ${f} `+chalk.gray(String(l).padStart(t))+" "+u+" "+C(m,5)+" "+chalk.white(w));const x=P.find(o=>n+_(o.slot)-1===l);if(x){const e=chalk.gray(` ${" ".repeat(t)} ⚔ ${a?"练习赛":"arena"} ${o.number}-${x.slot}: `);x.passed?b(e+chalk.bold.green(`${x.best_ac}/${x.n} AC ✅`)):0===x.attempts?b(e+chalk.gray("⬜ ")+chalk.white(a?`未开始 · ${x.n} 题 · 过 ${x.need} 题 → `:`not started · ${x.n} problems · AC ${x.need} → `)+chalk.bold.green(`contest ${x.slot}`)):b(e+chalk.yellow(a?`❌ 最好 ${x.best_ac}/${x.n}(需 ${x.need})· 已试 ${x.attempts} 次 → `:`❌ best ${x.best_ac}/${x.n} (need ${x.need}) · ${x.attempts} attempt(s) → `)+chalk.bold.green(`contest ${x.slot}`))}}g?.research&&!g.research.passed&&b(chalk.gray(a?` ⭐ 研究: ${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&&b(chalk.gray(a?" 🔒 本阶段通关标准制作中 — 很快更新":" 🔒 pass criteria for this phase in production — coming soon"))}}f(),b(chalk.gray(a?" ✓ 答对 ✗ 答错 ○ 未答 · ⚔ 练习 已过/共 · 赛 ✅ 过 ❌ 试过 ⬜ 未碰 · ⭐ 研究":" ✓ right ✗ wrong ○ unanswered · ⚔ practice solved/total · ⭐ research")),b(chalk.gray(a?" 展开某阶段: ":" arena ✅ passed ❌ tried ⬜ untouched · expand: ")+chalk.bold.green("status all")+chalk.gray(" / ")+chalk.bold.green("status N")),t.achievements.length>0&&b(" "+chalk.gray(n("achievements",l)+" ")+t.achievements.map(o=>chalk.bold.yellow("★ ")+chalk.white(o)).join(chalk.gray(" · "))),f(),m()}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const WORDS="the of and to in is you that it he was for on are as with his they at be this have from or one had by word but not what all were we when your can said there use an each which she do how their if will up other about out many then them these so some her would make like him into time has look two more write go see number no way could people my than first water been call who oil its now find long down day did get come made may part over new sound take only little work know place year live me back give most very after thing our just name good sentence man think say great where help through much before line right too mean old any same tell boy follow came want show also around form three small set put end does another well large must big even such because turn here why ask went men read need land different home us move try kind hand picture again change off play spell air away animal house point page letter mother answer found study still learn should world high every near add food between own below country plant last school father keep tree never start city earth eye light thought head under story saw left few while along might close something seem next hard open example begin life always those both paper together got group often run koala beach surf bush wombat emu sydney bondi manly cricket ferry harbour reef outback mate arvo".split(" ");export const PYTHON_LINES=['print("hello")',"x = 5",'name = "Ada"',"total = a + b * 2","nums = [1, 2, 3]","print(nums[0])","if x > 10:","for i in range(10):","def greet(name):","return x * 2","import math","from random import choice","while n > 0:",'scores = {"Ada": 9, "Sam": 7}','print(f"Hi {name}")','beaches = ["Bondi", "Manly"]',"last = word[-1]",'if name == "Ada":',"for beach in beaches:","total = add(3, 4)","area = width * height","if x != 3:","count += 1","print(len(word))","nums.append(4)","x, y = 1, 2","result = max(3, 7)","words = text.split()","if temp >= 30 and sunny:","import numpy as np","arr = np.zeros(10)","for k, v in scores.items():","n = int(input())","print(sum(nums) / len(nums))","def area(w, h):","return w * h",'lines = open("data.csv")',"try:","except ValueError:","with open(path) as f:"];export const SHELL_LINES=["pwd","ls -a","cd ~/icoa","cd ..","mkdir data","touch hello.py","python3 hello.py","python3 --version","python3 -m venv .venv","source .venv/bin/activate","pip install numpy","pip install pandas matplotlib","npm install -g icoa-cli","icoa env setup","icoa learn","practice 1","/read 1","/edit 1","/run 1","/submit 1",":wq",":q!","ls data/train.csv","python3 src/train.py data/train.csv","cat notes.txt","head -n 5 data.csv","git status","git add .",'git commit -m "first"',"echo $HOME","ls | grep py","vi solution.py","python3 -m pip install --upgrade pip","cd ~/icoa && icoa","mkdir notes && cd notes","clear","history","which python3","chmod +x run.sh","./run.sh"];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{existsSync as e,mkdirSync as t,readFileSync as o,writeFileSync as n}from"node:fs";import{homedir as r}from"node:os";import{join as s}from"node:path";import chalk from"chalk";import{PYTHON_LINES as c,SHELL_LINES as a,WORDS as l}from"./typing-pools.js";export const TYPING_DURATIONS=[15,30,60];const i=s(r(),".icoa","typing.json");export function loadTypingHistory(){try{if(!e(i))return[];const t=JSON.parse(o(i,"utf8"));return Array.isArray(t)?t:[]}catch{return[]}}export function bestWpm(e,t){return loadTypingHistory().filter(o=>o.pool===e&&o.seconds===t&&o.completed).reduce((e,t)=>Math.max(e,t.wpm),0)}function p(e){const t=e.slice();for(let e=t.length-1;e>0;e--){const o=Math.floor(Math.random()*(e+1));[t[e],t[o]]=[t[o],t[e]]}return t}export function makeStream(e){const t="words"===e?l:"python"===e?c:a,o=[];for(;o.length<400;)for(const e of p(t)){for(const t of e.split(" "))t&&o.push(t);if(o.length>=400)break}return o.map(e=>({text:e,typed:""}))}function d(e,t){let o="";const n=Math.max(e.text.length,e.typed.length);for(let r=0;r<n;r++){const n=e.text[r],s=e.typed[r];o+=void 0===s?t&&r===e.typed.length?chalk.bold.underline.yellow(n):chalk.gray(n):void 0===n?chalk.bgRed.white(s):s===n?chalk.green(n):chalk.red.underline(n)}return t&&e.typed.length>=e.text.length&&(o+=chalk.bold.yellow("_")),o}export function runTypingTest(o){return new Promise(c=>{const a=process.stdin,l=process.stdout,p=o.zh;if(!a.isTTY)return console.log(chalk.yellow(p?" 打字练习需要真实终端。":" The typing trainer needs a real terminal.")),void c(null);const h=!!a.isRaw,f=a.listeners("data").slice(),u=a.listeners("keypress").slice();for(const e of f)a.removeListener("data",e);for(const e of u)a.removeListener("keypress",e);a.setRawMode?.(!0),a.resume();let g=[],y=0,m=0,w=0,$=0,x=0,M=0,b=null,v=0,T=null,k=0,_=!1;const P=()=>m?(Date.now()-m)/1e3:0,S=()=>{let e=0,t=0,o=0;for(let n=0;n<=y&&n<g.length;n++){const r=g[n];if(!r.typed)continue;const s=Math.max(r.text.length,r.typed.length);for(let o=0;o<s;o++)void 0!==r.typed[o]&&(r.typed[o]===r.text[o]?e++:t++);n<y&&(o++,r.typed===r.text&&e++)}const n=Math.max(P(),1)/60;return{correct:e,wrong:t,wordsDone:o,wpm:Math.round(e/5/n),raw:Math.round((e+t)/5/n),acc:w?Math.round($/w*100):100}},W=()=>{const e=Math.max(0,o.seconds-Math.floor(P())),t=S(),n=`${o.pool} · ${o.seconds}s`,r=m?`⏱ ${e}`:p?"⏱ 敲第一个键开始":"⏱ starts on first key";return chalk.bold.cyan(" ⌨ type")+chalk.gray(` ${n} `)+chalk.bold.yellow(r)+chalk.gray(` WPM ${t.wpm} ${p?"准确":"acc"} ${t.acc}%`)},N=()=>{const e=Math.max(30,Math.min((process.stdout.columns||80)-4,72)),{lines:t,lineOf:o}=function(e,t){const o=[],n=[];let r=[],s=0;return e.forEach((e,c)=>{const a=Math.max(e.text.length,e.typed.length);r.length>0&&s+1+a>t&&(o.push(r),r=[],s=0),r.length>0&&(s+=1),s+=a,r.push(c),n[c]=o.length}),r.length&&o.push(r),{lines:o,lineOf:n}}(g,e),n=Math.max(0,Math.min(o[y]??0,t.length-3)),r=[];for(let e=n;e<Math.min(n+3,t.length);e++)r.push(` ${t[e].map(e=>d(g[e],e===y)).join(" ")}`);for(;r.length<3;)r.push("");const s=k>0?chalk.red(p?" ✗ 粘贴不算 — 请手敲。":" ✗ Paste does not count — type it."):chalk.gray(p?" Tab 重开 · Esc 退出":" Tab restart · Esc quit"),c=[W(),"",...r,"",s];v>0&&l.write(`[${v}A`);for(const e of c)l.write(`\r[2K${e}\n`);v=c.length},D=()=>{b&&clearInterval(b),b=null},I=()=>{g=makeStream(o.pool),y=0,m=0,w=0,$=0,x=0,M=0,k=0,D(),N()},O=async l=>{if(_)return;_=!0,D();const d=S(),g={pool:o.pool,seconds:o.seconds,wpm:d.wpm,raw_wpm:d.raw,accuracy:d.acc,chars_correct:d.correct,chars_wrong:d.wrong,words_done:d.wordsDone,backspaces:x,pastes:M,finished_at:(new Date).toISOString(),completed:l};if(N(),(()=>{D(),a.removeListener("data",R),a.setRawMode?.(h);for(const e of f)a.on("data",e);for(const e of u)a.on("keypress",e)})(),l||w>0){T=g;const c=bestWpm(o.pool,o.seconds);l&&function(o){try{const c=s(r(),".icoa");e(c)||t(c,{recursive:!0});const a=loadTypingHistory();a.push(o),n(i,JSON.stringify(a.slice(-50),null,1))}catch{}}(g),console.log(),console.log(chalk.bold.white(` ${g.wpm} WPM`)+chalk.gray(` raw ${g.raw_wpm} ${p?"准确率":"accuracy"} ${g.accuracy}% `)+chalk.green(`${g.chars_correct}`)+chalk.gray("/")+chalk.red(`${g.chars_wrong}`)+chalk.gray(p?" 字符":" chars")+(g.backspaces?chalk.gray(` ⌫ ${g.backspaces}`):"")+(g.pastes?chalk.red(` ${p?"粘贴":"pastes"} ${g.pastes}`):"")),l?0===c?console.log(chalk.green(p?" 🎉 第一次成绩记下了 — 下次超过它。":" 🎉 First result saved — beat it next time.")):g.wpm>c?console.log(chalk.bold.green(p?` 🎉 新纪录!之前最好 ${c} WPM`:` 🎉 New best! previous ${c} WPM`)):console.log(chalk.gray(p?` 最好成绩 ${c} WPM(${o.pool} · ${o.seconds}s)`:` best ${c} WPM (${o.pool} · ${o.seconds}s)`)):console.log(chalk.gray(p?" (提前退出 — 不计入最好成绩)":" (left early — not counted as a best)"));try{await(o.onResult?.(g))}catch{}}c(T)},R=e=>{if(_)return;const t=e.toString("utf8");if(""===t||""===t)return void O(!1);if("\t"===t)return void I();if(t.startsWith(""))return;if("\r"===t||"\n"===t)return;if(""===t||"\b"===t){const e=g[y];return e.typed.length>0?(e.typed=e.typed.slice(0,-1),x++):y>0&&g[y-1].typed!==g[y-1].text&&(y--,x++),void N()}const o=[...t].filter(e=>e>=" "&&""!==e);if(0!==o.length){if(o.length>=8)return M++,k=3,void N();for(const e of o)A(e);N()}},A=e=>{m||(m=Date.now(),b=setInterval(()=>{k>0&&k--,P()>=o.seconds?O(!0):N()},1e3)),w++;const t=g[y];if(" "===e){if(0===t.typed.length)return;return t.typed===t.text&&$++,y++,void(y>=g.length&&O(!0))}t.typed.length<t.text.length+8&&(t.typed+=e),t.text[t.typed.length-1]===e&&$++};console.log(),console.log(chalk.gray(p?" 照着敲,空格换下一个词,错字可以 Backspace 改。时间到自动出成绩。":" Type the words, Space moves on, Backspace fixes. The clock stops itself.")),a.on("data",R),I()})}export function parseTypingArgs(e){let t="words",o=30;for(const n of e){const e=n.toLowerCase().replace(/s$/,"");if("word"===e||"english"===e||"en"===e)t="words";else if("python"===e||"py"===e||"code"===e)t="python";else if("shell"===e||"sh"===e||"bash"===e||"terminal"===e)t="shell";else{if(!/^\d+$/.test(e))return{error:n};{const t=Number(e),r=TYPING_DURATIONS.find(e=>e===t);if(!r)return{error:`${n}: use 15, 30 or 60`};o=r}}}return{pool:t,seconds:o}}
|