icoa-cli 2.19.398 → 2.19.400

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.
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Block until the sim cooldown clears, rendering a single-line countdown.
3
+ * Returns immediately when no cooldown is pending (the common case).
4
+ *
5
+ * @param line in-fiction label, e.g. "watchdog cooling down"
6
+ */
7
+ export declare function awaitRenderWindow(line: string): Promise<void>;
@@ -0,0 +1 @@
1
+ import chalk from"chalk";import{cooldownRemaining as o}from"./sim-cooldown.js";export async function awaitRenderWindow(t){let r=o();if(r<=0)return;const e=!0===process.stdout.isTTY;for(;r>0;){const s="█".repeat(Math.max(0,20-Math.ceil(r/3))).padEnd(20,"░"),a=chalk.gray(` ${t} `)+chalk.cyan(s)+chalk.gray(` ${r}s`);e?process.stdout.write("\r"+a):console.log(a),await new Promise(o=>setTimeout(o,1e3)),r=o()}e&&process.stdout.write("\r"+" ".repeat(60)+"\r")}
@@ -59,6 +59,21 @@ export declare function viewPqsTop(opts: {
59
59
  token: string;
60
60
  cardNumber: number;
61
61
  }): Promise<PqsTopResult>;
62
+ export type MyScoreEntry = {
63
+ card: number;
64
+ score: number;
65
+ tier: string;
66
+ };
67
+ export declare function fetchMyScores(opts: {
68
+ token: string;
69
+ cardLo: number;
70
+ cardHi: number;
71
+ }): Promise<{
72
+ ok: boolean;
73
+ scores?: MyScoreEntry[];
74
+ code?: string;
75
+ message?: string;
76
+ }>;
62
77
  export type HonorBoardEntry = {
63
78
  rank: number;
64
79
  score: number;
@@ -1 +1 @@
1
- import{getDeviceFingerprint as e}from"./access.js";import{LEARN_SERVER as t}from"./learn-curricula.js";export async function submitPqs(r){const a=t.replace(/\/$/,"")+"/api/icoa/learn/submit";try{const t=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),card_number:r.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(18e4)}),o=await t.json().catch(()=>({}));if(!t.ok||!o.success||!o.data)return{ok:!1,message:o.message||`HTTP ${t.status}`,code:o.code};const n=o.data;return{ok:!0,score:n.score,tier:n.tier,manipulation:n.manipulation,feedback:n.feedback,improve:n.improve,submissionIndex:n.submission_index,submissionsLeft:n.submissions_left,rank:n.rank,entrants:n.entrants,inTop3:n.in_top3,turns:n.turns,spent:n.spent,cap:n.cap}}catch(e){return{ok:!1,message:e instanceof Error&&"TimeoutError"===e.name?"PQS grading timed out — your conversation is safe on the server; try /submit again in a minute.":`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function viewPqsTop(r){const a=t.replace(/\/$/,"")+"/api/icoa/learn/view";try{const t=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),card_number:r.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),o=await t.json().catch(()=>({}));return t.ok&&o.success&&o.data?{ok:!0,top3:o.data.top3,myRank:o.data.my_rank,entrants:o.data.entrants}:{ok:!1,message:o.message||`HTTP ${t.status}`,code:o.code,myRank:o.rank,entrants:o.entrants}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function viewHonors(r){const a=t.replace(/\/$/,"")+"/api/icoa/learn/honors";try{const t=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),curriculum_id:r.curriculumId,card_number:r.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),o=await t.json().catch(()=>({}));return t.ok&&o.success&&o.data?{ok:!0,board:o.data.board,totals:o.data.totals,chapters:o.data.chapters,floor:o.data.floor}:{ok:!1,message:o.message||`HTTP ${t.status}`,code:o.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function streamLearnTutor(r,a){const o=t.replace(/\/$/,"")+"/api/icoa/learn/ai",n=new AbortController;let s=setTimeout(()=>n.abort(),6e4);const i=()=>{clearTimeout(s),s=setTimeout(()=>n.abort(),6e4)};try{const t=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),card_number:r.cardNumber,question:r.question,mode:r.mode,history:r.history?.slice(-6),device_fingerprint:e()}),signal:n.signal});if(!t.ok||!t.body){const e=await t.json().catch(()=>({}));return{ok:!1,answer:"",message:e.message||`HTTP ${t.status}`,code:e.code,spent:e.spent,cap:e.cap}}const s=t.body.getReader(),c=new TextDecoder;let d="",m="",u=null,p=null;for(;;){const e=await s.read();if(e.done)break;i(),d+=c.decode(e.value,{stream:!0});const t=d.split("\n");d=t.pop()??"";for(const e of t){const t=e.trim();if(!t.startsWith("data:"))continue;let r;try{r=JSON.parse(t.slice(5).trim())}catch{continue}r.r&&a({kind:"reasoning",text:r.r}),r.c&&(m+=r.c,a({kind:"content",text:r.c})),r.error&&(p=r.error),r.done&&(u=r)}}return p&&!m?{ok:!1,answer:"",message:p}:m?{ok:!0,answer:m,offtopic:!0===u?.offtopic,spent:u?.spent,cap:u?.cap}:{ok:!1,answer:"",message:"No answer received — try again."}}catch(e){return{ok:!1,answer:"",message:e instanceof Error&&"AbortError"===e.name?"AI tutor timed out (no data for 60s) — try again.":`Network error: ${e instanceof Error?e.message:String(e)}`}}finally{clearTimeout(s)}}
1
+ import{getDeviceFingerprint as e}from"./access.js";import{LEARN_SERVER as t}from"./learn-curricula.js";export async function submitPqs(r){const a=t.replace(/\/$/,"")+"/api/icoa/learn/submit";try{const t=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),card_number:r.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(18e4)}),o=await t.json().catch(()=>({}));if(!t.ok||!o.success||!o.data)return{ok:!1,message:o.message||`HTTP ${t.status}`,code:o.code};const n=o.data;return{ok:!0,score:n.score,tier:n.tier,manipulation:n.manipulation,feedback:n.feedback,improve:n.improve,submissionIndex:n.submission_index,submissionsLeft:n.submissions_left,rank:n.rank,entrants:n.entrants,inTop3:n.in_top3,turns:n.turns,spent:n.spent,cap:n.cap}}catch(e){return{ok:!1,message:e instanceof Error&&"TimeoutError"===e.name?"PQS grading timed out — your conversation is safe on the server; try /submit again in a minute.":`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function viewPqsTop(r){const a=t.replace(/\/$/,"")+"/api/icoa/learn/view";try{const t=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),card_number:r.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),o=await t.json().catch(()=>({}));return t.ok&&o.success&&o.data?{ok:!0,top3:o.data.top3,myRank:o.data.my_rank,entrants:o.data.entrants}:{ok:!1,message:o.message||`HTTP ${t.status}`,code:o.code,myRank:o.rank,entrants:o.entrants}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function fetchMyScores(r){const a=t.replace(/\/$/,"")+"/api/icoa/learn/myscores";try{const t=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),card_lo:r.cardLo,card_hi:r.cardHi,device_fingerprint:e()}),signal:AbortSignal.timeout(8e3)}),o=await t.json().catch(()=>({}));return t.ok&&o.success&&o.data?{ok:!0,scores:o.data.scores??[]}:{ok:!1,message:o.message||`HTTP ${t.status}`,code:o.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function viewHonors(r){const a=t.replace(/\/$/,"")+"/api/icoa/learn/honors";try{const t=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),curriculum_id:r.curriculumId,card_number:r.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),o=await t.json().catch(()=>({}));return t.ok&&o.success&&o.data?{ok:!0,board:o.data.board,totals:o.data.totals,chapters:o.data.chapters,floor:o.data.floor}:{ok:!1,message:o.message||`HTTP ${t.status}`,code:o.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function streamLearnTutor(r,a){const o=t.replace(/\/$/,"")+"/api/icoa/learn/ai",n=new AbortController;let s=setTimeout(()=>n.abort(),6e4);const i=()=>{clearTimeout(s),s=setTimeout(()=>n.abort(),6e4)};try{const t=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.token.toUpperCase(),card_number:r.cardNumber,question:r.question,mode:r.mode,history:r.history?.slice(-6),device_fingerprint:e()}),signal:n.signal});if(!t.ok||!t.body){const e=await t.json().catch(()=>({}));return{ok:!1,answer:"",message:e.message||`HTTP ${t.status}`,code:e.code,spent:e.spent,cap:e.cap}}const s=t.body.getReader(),c=new TextDecoder;let d="",m="",p=null,u=null;for(;;){const e=await s.read();if(e.done)break;i(),d+=c.decode(e.value,{stream:!0});const t=d.split("\n");d=t.pop()??"";for(const e of t){const t=e.trim();if(!t.startsWith("data:"))continue;let r;try{r=JSON.parse(t.slice(5).trim())}catch{continue}r.r&&a({kind:"reasoning",text:r.r}),r.c&&(m+=r.c,a({kind:"content",text:r.c})),r.error&&(u=r.error),r.done&&(p=r)}}return u&&!m?{ok:!1,answer:"",message:u}:m?{ok:!0,answer:m,offtopic:!0===p?.offtopic,spent:p?.spent,cap:p?.cap}:{ok:!1,answer:"",message:"No answer received — try again."}}catch(e){return{ok:!1,answer:"",message:e instanceof Error&&"AbortError"===e.name?"AI tutor timed out (no data for 60s) — try again.":`Network error: ${e instanceof Error?e.message:String(e)}`}}finally{clearTimeout(s)}}
@@ -0,0 +1,73 @@
1
+ export type ContestProblem = {
2
+ id: string;
3
+ title: string;
4
+ statement: string;
5
+ samples?: Array<{
6
+ in: string;
7
+ out: string;
8
+ }>;
9
+ };
10
+ export type ContestStartData = {
11
+ phase: number;
12
+ title?: string;
13
+ attempt: number;
14
+ resumed: boolean;
15
+ started_at: string;
16
+ deadline: string;
17
+ duration_minutes: number;
18
+ n: number;
19
+ need: number;
20
+ solved: string[];
21
+ problems: ContestProblem[];
22
+ };
23
+ export type ContestSubmitData = {
24
+ verdict: string;
25
+ late?: boolean;
26
+ already_ac?: boolean;
27
+ test?: number | null;
28
+ passed_tests?: number | null;
29
+ total_tests?: number | null;
30
+ time_ms?: number | null;
31
+ ac_count: number;
32
+ need: number;
33
+ n: number;
34
+ passed: boolean;
35
+ deadline?: string;
36
+ };
37
+ export type GatePhase = {
38
+ phase: number;
39
+ configured: boolean;
40
+ passed: boolean;
41
+ contest?: {
42
+ passed: boolean;
43
+ best_ac: number;
44
+ need: number;
45
+ n: number;
46
+ attempts: number;
47
+ };
48
+ research?: {
49
+ good_cards: number;
50
+ need_cards: number;
51
+ min_score: number;
52
+ passed: boolean;
53
+ };
54
+ };
55
+ export type GateData = {
56
+ gated: number;
57
+ unlocked_through_phase: number;
58
+ phases: GatePhase[];
59
+ };
60
+ type ApiResult<T> = {
61
+ ok: boolean;
62
+ data?: T;
63
+ code?: string;
64
+ message?: string;
65
+ };
66
+ export declare function contestStart(token: string, phase: number): Promise<ApiResult<ContestStartData>>;
67
+ export declare function contestSubmit(token: string, phase: number, problemId: string, code: string): Promise<ApiResult<ContestSubmitData>>;
68
+ export declare function contestReset(token: string, phase: number): Promise<ApiResult<{
69
+ reset_sessions: number;
70
+ }>>;
71
+ export declare function fetchGate(token: string): Promise<ApiResult<GateData>>;
72
+ export declare function timeLeft(deadlineIso: string): string | null;
73
+ export {};
@@ -0,0 +1 @@
1
+ import{getDeviceFingerprint as t}from"./access.js";import{LEARN_SERVER as e}from"./learn-curricula.js";async function o(o,r,a){const n=e.replace(/\/$/,"")+o;try{const e=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...r,device_fingerprint:t()}),signal:AbortSignal.timeout(a)}),o=await e.json().catch(()=>({}));return e.ok&&o.success&&void 0!==o.data?{ok:!0,data:o.data}:{ok:!1,message:o.message||`HTTP ${e.status}`,code:o.code}}catch(t){return{ok:!1,message:`Network error: ${t instanceof Error?t.message:String(t)}`}}}export function contestStart(t,e){return o("/api/icoa/learn/contest/start",{token:t.toUpperCase(),phase:e},15e3)}export function contestSubmit(t,e,r,a){return o("/api/icoa/learn/contest/submit",{token:t.toUpperCase(),phase:e,problem_id:r,code:a},12e4)}export function contestReset(t,e){return o("/api/icoa/learn/contest/reset",{token:t.toUpperCase(),phase:e},15e3)}export function fetchGate(t){return o("/api/icoa/learn/gate",{token:t.toUpperCase()},15e3)}export function timeLeft(t){const e=Date.parse(t);if(Number.isNaN(e))return null;const o=e-Date.now();if(o<=0)return null;const r=Math.floor(o/6e4),a=Math.floor(o%6e4/1e3);return`${r}:${String(a).padStart(2,"0")}`}
@@ -226,6 +226,7 @@ type ValidateResult = {
226
226
  message?: string;
227
227
  code?: string;
228
228
  aiEnabled?: boolean;
229
+ gated?: boolean;
229
230
  };
230
231
  export declare function validateEAToken(token: string): Promise<ValidateResult>;
231
232
  /**
@@ -1 +1 @@
1
- export function localized(r,e){if(!e.startsWith("zh")||!r._zh)return r;const t=r._zh,n={...r};for(const r of Object.keys(t))void 0!==t[r]&&(n[r]=t[r]);return n}export function isValidCurriculum(r){if(!r||"object"!=typeof r)return!1;const e=r;return"string"==typeof e.id&&"number"==typeof e.totalCards&&Array.isArray(e.cards)&&Array.isArray(e.modules)}import{readFileSync as r,writeFileSync as e,mkdirSync as t,existsSync as n,statSync as a,utimesSync as c}from"node:fs";import{join as i}from"node:path";import{homedir as o}from"node:os";import{getDeviceFingerprint as u}from"./access.js";import{getCliVersion as s}from"./version.js";const d=i(o(),".icoa","learn-cache");export const LEARN_SERVER="https://practice.icoa2026.au";function l(r){const e=r.replace(/[^A-Za-z0-9_-]/g,"_");return i(d,`${e}.json`)}function m(r){const e=r.replace(/[^A-Za-z0-9_-]/g,"_");return i(d,`${e}.etag`)}export async function loadCurriculumById(i,o){"ctf4eai-12"===i&&(i="LEARNDEMO01");const u=function(e){const t=l(e);if(!n(t))return null;try{const e=a(t),n=Date.now()-e.mtimeMs;return{curriculum:JSON.parse(r(t,"utf-8")),ageMs:n}}catch{return null}}(i);if(u&&u.ageMs<6048e5&&isValidCurriculum(u.curriculum))return u.curriculum;const f=await async function(e,t){const a=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/curriculum/"+encodeURIComponent(e),c={},i=function(e){const t=m(e);if(!n(t))return null;try{return r(t,"utf-8").trim()||null}catch{return null}}(e);i&&(c["If-None-Match"]=i),t&&(c["X-Learn-Token"]=t.toUpperCase()),c["X-ICOA-Client"]=s();try{const r=await fetch(a,{headers:c,signal:AbortSignal.timeout(6e4)});if(304===r.status)return{kind:"unchanged"};if(!r.ok)return{kind:"error"};const e=await r.json();if(!e.success||!e.data)return{kind:"error"};if(!isValidCurriculum(e.data))return{kind:"error"};const t=r.headers.get("etag")||r.headers.get("ETag")||void 0;return{kind:"fresh",curriculum:e.data,etag:t||void 0}}catch{return{kind:"error"}}}(i,o);return"fresh"===f.kind?(function(r,n,a){try{t(d,{recursive:!0}),e(l(r),JSON.stringify(n)),a&&e(m(r),a)}catch{}}(i,f.curriculum,f.etag),f.curriculum):"unchanged"===f.kind&&u&&isValidCurriculum(u.curriculum)?(function(r){try{const e=l(r);if(n(e)){const r=Date.now()/1e3;c(e,r,r)}}catch{}}(i),u.curriculum):u&&isValidCurriculum(u.curriculum)?u.curriculum:null}export async function validateEAToken(r){const e=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/validate";try{const t=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.toUpperCase(),device_fingerprint:u()}),signal:AbortSignal.timeout(8e3)});if(!t.ok){const r=await t.json().catch(()=>({}));return{ok:!1,message:r.message||`HTTP ${t.status}`,code:r.code}}const n=await t.json();return n.success&&n.data?{ok:!0,curriculumId:n.data.curriculum_id,status:n.data.status,validUntil:n.data.valid_until,aiEnabled:1===n.data.ai_enabled||!0===n.data.ai_enabled}:{ok:!1,message:n.message||"Validation failed"}}catch(r){return{ok:!1,message:`Network error: ${r instanceof Error?r.message:String(r)}`}}}export async function syncProgress(r,e){if("LEARNDEMO01"===r.toUpperCase())return;const t=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/progress/"+r.toUpperCase();try{await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({card_number:e.card_number,event_type:e.event_type,mcq_answer:e.mcq_answer,mcq_correct:e.mcq_correct?1:0,check_answer:e.check_answer,check_correct:e.check_correct?1:0,time_on_card_ms:e.time_on_card_ms,device_fingerprint:u()}),signal:AbortSignal.timeout(5e3)})}catch{}}export async function syncCardFeedback(r,e){const t=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/feedback/"+r.toUpperCase();try{return(await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({curriculum_id:e.curriculum_id,card_number:e.card_number,feedback_text:e.feedback_text,time_on_card_ms:e.time_on_card_ms}),signal:AbortSignal.timeout(5e3)})).ok}catch{return!1}}
1
+ export function localized(r,e){if(!e.startsWith("zh")||!r._zh)return r;const t=r._zh,a={...r};for(const r of Object.keys(t))void 0!==t[r]&&(a[r]=t[r]);return a}export function isValidCurriculum(r){if(!r||"object"!=typeof r)return!1;const e=r;return"string"==typeof e.id&&"number"==typeof e.totalCards&&Array.isArray(e.cards)&&Array.isArray(e.modules)}import{readFileSync as r,writeFileSync as e,mkdirSync as t,existsSync as a,statSync as n,utimesSync as c}from"node:fs";import{join as i}from"node:path";import{homedir as o}from"node:os";import{getDeviceFingerprint as u}from"./access.js";import{getCliVersion as s}from"./version.js";const d=i(o(),".icoa","learn-cache");export const LEARN_SERVER="https://practice.icoa2026.au";function l(r){const e=r.replace(/[^A-Za-z0-9_-]/g,"_");return i(d,`${e}.json`)}function m(r){const e=r.replace(/[^A-Za-z0-9_-]/g,"_");return i(d,`${e}.etag`)}export async function loadCurriculumById(i,o){"ctf4eai-12"===i&&(i="LEARNDEMO01");const u=function(e){const t=l(e);if(!a(t))return null;try{const e=n(t),a=Date.now()-e.mtimeMs;return{curriculum:JSON.parse(r(t,"utf-8")),ageMs:a}}catch{return null}}(i);if(u&&u.ageMs<6048e5&&isValidCurriculum(u.curriculum))return u.curriculum;const f=await async function(e,t){const n=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/curriculum/"+encodeURIComponent(e),c={},i=function(e){const t=m(e);if(!a(t))return null;try{return r(t,"utf-8").trim()||null}catch{return null}}(e);i&&(c["If-None-Match"]=i),t&&(c["X-Learn-Token"]=t.toUpperCase()),c["X-ICOA-Client"]=s();try{const r=await fetch(n,{headers:c,signal:AbortSignal.timeout(6e4)});if(304===r.status)return{kind:"unchanged"};if(!r.ok)return{kind:"error"};const e=await r.json();if(!e.success||!e.data)return{kind:"error"};if(!isValidCurriculum(e.data))return{kind:"error"};const t=r.headers.get("etag")||r.headers.get("ETag")||void 0;return{kind:"fresh",curriculum:e.data,etag:t||void 0}}catch{return{kind:"error"}}}(i,o);return"fresh"===f.kind?(function(r,a,n){try{t(d,{recursive:!0}),e(l(r),JSON.stringify(a)),n&&e(m(r),n)}catch{}}(i,f.curriculum,f.etag),f.curriculum):"unchanged"===f.kind&&u&&isValidCurriculum(u.curriculum)?(function(r){try{const e=l(r);if(a(e)){const r=Date.now()/1e3;c(e,r,r)}}catch{}}(i),u.curriculum):u&&isValidCurriculum(u.curriculum)?u.curriculum:null}export async function validateEAToken(r){const e=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/validate";try{const t=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:r.toUpperCase(),device_fingerprint:u()}),signal:AbortSignal.timeout(8e3)});if(!t.ok){const r=await t.json().catch(()=>({}));return{ok:!1,message:r.message||`HTTP ${t.status}`,code:r.code}}const a=await t.json();return a.success&&a.data?{ok:!0,curriculumId:a.data.curriculum_id,status:a.data.status,validUntil:a.data.valid_until,aiEnabled:1===a.data.ai_enabled||!0===a.data.ai_enabled,gated:1===a.data.gated||!0===a.data.gated}:{ok:!1,message:a.message||"Validation failed"}}catch(r){return{ok:!1,message:`Network error: ${r instanceof Error?r.message:String(r)}`}}}export async function syncProgress(r,e){if("LEARNDEMO01"===r.toUpperCase())return;const t=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/progress/"+r.toUpperCase();try{await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({card_number:e.card_number,event_type:e.event_type,mcq_answer:e.mcq_answer,mcq_correct:e.mcq_correct?1:0,check_answer:e.check_answer,check_correct:e.check_correct?1:0,time_on_card_ms:e.time_on_card_ms,device_fingerprint:u()}),signal:AbortSignal.timeout(5e3)})}catch{}}export async function syncCardFeedback(r,e){const t=LEARN_SERVER.replace(/\/$/,"")+"/api/icoa/learn/feedback/"+r.toUpperCase();try{return(await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({curriculum_id:e.curriculum_id,card_number:e.card_number,feedback_text:e.feedback_text,time_on_card_ms:e.time_on_card_ms}),signal:AbortSignal.timeout(5e3)})).ok}catch{return!1}}
@@ -39,6 +39,25 @@ export declare function renderPhaseComplete(curriculum: Curriculum, state: Learn
39
39
  number: number;
40
40
  name: string;
41
41
  cardRange: [number, number];
42
+ }, pqsScores?: Array<{
43
+ card: number;
44
+ score: number;
45
+ tier: string;
46
+ }>, gatePhase?: {
47
+ configured: boolean;
48
+ contest?: {
49
+ passed: boolean;
50
+ best_ac: number;
51
+ need: number;
52
+ n: number;
53
+ attempts: number;
54
+ };
55
+ research?: {
56
+ good_cards: number;
57
+ need_cards: number;
58
+ min_score: number;
59
+ passed: boolean;
60
+ };
42
61
  }): void;
43
62
  export declare function renderCurriculumComplete(curriculum: Curriculum, state: LearnState): void;
44
63
  export declare function renderMCQCard(card: Extract<Card, {
@@ -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 l}from"./config.js";import{icoaBannerLines as t}from"./banner.js";import{renderCharts as r,detectCaps as c}from"./render-card.js";function a(){return(l().language||"en").toLowerCase()}const s=64;function g(o="─"){return o.repeat(66)}function i(o,e,n=20){const l=e>0?o/e:0,t=Math.floor(l*n),r=n-t;return`${chalk.green("█".repeat(t))+chalk.gray("░".repeat(r))} ${(100*l).toFixed(1)}%`}function d(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,l){const t=a(),r=e.cardsCompleted.length,c=o.totalCards,s=c-r,y=o.modules.find(o=>e.currentCard>=o.cardRange[0]&&e.currentCard<=o.cardRange[1]),u=/demo/i.test(o.id);if(console.log(),console.log(chalk.cyan(" ╭"+g("═"))),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ║ ")+chalk.bold.white(" "+(o.name?.trim()||n("academy_title",t)))),console.log(chalk.cyan(" ║")),l)console.log(chalk.cyan(" ║ ")+chalk.white(" "+n(u?"welcome_new_demo":"welcome_new",t)));else{const o=new Date(e.lastSeenAt),l=Math.floor((Date.now()-o.getTime())/36e5),r=l<1?n("just_now",t):l<24?`${l}${n("ago_hours",t)}`:`${Math.floor(l/24)}${n("ago_days",t)}`;console.log(chalk.cyan(" ║ ")+chalk.white(` ${n("welcome_back",t)} ${r}`))}console.log(chalk.cyan(" ║"));const h=y?`${y.number}. ${y.name}`:"—";console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("module",t).padEnd(11))+chalk.white(h)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("progress",t).padEnd(11))+i(r,c)+chalk.gray(` (${r}/${c})`)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("streak",t).padEnd(11))+d(e.streakDays)),s>0&&e.currentCard<=c&&console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("next_card",t).padEnd(11))+chalk.white(`#${e.currentCard} ${n("of",t)} ${c}`)),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ╰"+g("═"))),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────"));const m=0===r?n("continue_start",t):`${n("continue_resume",t)} ${e.currentCard}`;console.log(chalk.bold.green(" continue")+chalk.gray(" "+m)),console.log(chalk.yellow(" status")+chalk.gray(" "+n("status_full",t))),e.bookmarks.length>0&&console.log(chalk.yellow(" bookmarks")+chalk.gray(` ${e.bookmarks.length} ${n("bookmarks_desc",t)}`)),console.log(chalk.gray(" quit")+chalk.gray(" "+n("quit_desc",t))),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log()}function y(o,e){const l=a(),t=o.module,r=e.modules.find(o=>o.number===t),c=r?r.name:n("unknown_module",l),g=n("module",l).replace(":","").trim(),i=`${n("card",l)} ${o.number} / ${e.totalCards} · ${g} ${t} · ${c}`;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(i)+" "+chalk.cyan("─".repeat(Math.max(0,s-i.length-4))))}function u(){console.log(chalk.cyan(" ╰"+g())),console.log()}function h(o){console.log(chalk.cyan(" │ ")+chalk.white(o))}function m(){console.log(chalk.cyan(" │"))}const f=/[┌┐└┘├┤┬┴┼─│╔╗╚╝═║╠╣╦╩╬▓▒░█]/;function b(o){return/^(?: {4}|\t)/.test(o)||f.test(o)||/ {3,}/.test(o)}function p(e,n){const l=[];let t="",r=0,c=!1;for(const a of function(e){const n=[],l=e=>{let l="";const t=()=>{l&&(n.push({t:"atom",s:l}),l="")};for(const r of e)" "===r?(t(),n.push({t:"space"})):2===o(r)?(t(),n.push({t:"wide",s:r})):l+=r;t()},t=/`[^`]*`/g;let r,c=0;for(;null!==(r=t.exec(e));)l(e.slice(c,r.index)),n.push({t:"code",s:r[0]}),c=r.index+r[0].length;return l(e.slice(c)),n}(e)){if("space"===a.t){""!==t&&(c=!0);continue}const e=a.s,s=o("code"===a.t?e.replace(/`/g,""):e),g=c?1:0,i="atom"===a.t&&e.length<=2&&/^[.,;:!?)\]}…'"]+$/.test(e);""!==t&&!i&&r+g+s>n?(l.push(t),t=e,r=s,c=!1):(c&&(t+=" ",r+=1,c=!1),t+=e,r+=s)}return t&&l.push(t),l}const w=/\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 l,t="",r=0;for(w.lastIndex=0;null!==(l=w.exec(o));)l.index>r&&(t+=n(o.slice(r,l.index))),t+=chalk.bold.yellow(l[0]),r=l.index+l[0].length;return r<o.length&&(t+=n(o.slice(r))),t}function _(o,e=!1){let n="",l=0;const t=/`([^`]*)`/g;let r;for(;null!==(r=t.exec(o));)r.index>l&&(n+=$(o.slice(l,r.index),e)),n+=chalk.cyan(r[1]),l=r.index+r[0].length;return l<o.length&&(n+=$(o.slice(l),e)),n}function k(o,e=!1){let n=!1;for(const l of o.split("\n"))if(/^\s*```/.test(l))n=!n;else if(n)console.log(chalk.cyan(" │ ")+chalk.cyan(` ${l}`));else if(""!==l)if(b(l))console.log(chalk.cyan(" │ ")+chalk.white(l));else for(const o of p(l,60))console.log(chalk.cyan(" │ ")+` ${_(o,e)}`);else m()}function C(o,e){const n=[];for(const l of o.split("\n")){if(""===l){n.push("");continue}if(b(l)){n.push(l);continue}let o="";for(const t of l.split(" "))(o+" "+t).trim().length>e?(n.push(o.trim()),o=t):o=(o+" "+t).trim();o&&n.push(o)}return n}let x=!1;export function setLearnAiEnabled(o){x=o}let v=new Set;export function setLearnPqsViewCards(o){v=o}export function renderHonorWall(o){const e=a().startsWith("zh"),{totals:n,chapters:l}=o,t=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===t){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 c=l.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(" "+c);const s=l.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(l,t){const g=a(),i=e(l,g);y(i,t),m();const d=p(i.title,60);for(const o of d)h(chalk.bold.yellow(o));h(chalk.gray("─".repeat(Math.min(Math.max(...d.map(e=>o(e))),s))));const f=!!(l.chart_ids&&l.chart_ids.length>0);if(f&&r(l.chart_ids.slice(0,1),t.charts,c()),i.key_point&&""!==i.key_point.trim()?(f||m(),function(o,e,n){const l=a().startsWith("zh"),t=c().cols,r="-".repeat(Math.min(s,Math.max(24,t-6))),g=l?"要点":"KEY POINT",i=l?`第 ${e} / ${n} 张`:`Card ${e} / ${n}`;console.log(chalk.cyan(" │ ")+chalk.green(r)),console.log(chalk.cyan(" │ ")+chalk.green(`${g} · ${i}`));for(const e of p(o,60))console.log(chalk.cyan(" │ ")+chalk.bold.green(" "+e));console.log(chalk.cyan(" │ ")+chalk.green(r)),m()}(i.key_point,i.number,t.totalCards)):f||m(),i.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){k(n[1],!0);const o=n[2]?.trimStart();o&&k(o,!1)}else k(o,!1);m()}),i.icoaConnection){h(chalk.magenta(" "+n("icoa_connection",g))),h(chalk.gray(" "+"─".repeat(20)));for(const o of p(i.icoaConnection,60))h(" "+chalk.magenta(o));m()}if(l.check){const o=g.startsWith("zh"),e=l._zh?.checkStatement??l._zh?.check?.statement,n=o?e??l.check.statement:l.check.statement;h(chalk.cyan(" 🤔 "+(o?"请回答 (输 y 或 n):":"Quick comprehension check (type y or n):"))),h(chalk.gray(" "+"─".repeat(20)));for(const o of p(n,60))h(" "+chalk.white(o));m()}u(),function(o,e){const n=a().startsWith("zh"),l=c().cols,t="-".repeat(Math.min(s,Math.max(24,l-6))),r=chalk.gray(" "),g=[];o.check?(g.push(chalk.bold.green("[y]")+chalk.gray(n?" 是":" yes")),g.push(chalk.bold.green("[n]")+chalk.gray(n?" 否":" no"))):g.push(chalk.bold.green("[ok]")+chalk.gray(n?" 继续":" next")),x&&(g.push(chalk.bold.magenta("[/ai]")+chalk.gray(n?" AI导师":" AI tutor")),g.push(chalk.bold.magenta("[/submit]")+chalk.gray(n?" 提问评分":" prompt score")),v.has(o.number)&&g.push(chalk.bold.magenta("[/view]")+chalk.gray(n?" 榜单":" top-3"))),g.push(chalk.bold.yellow("[e]")+chalk.gray(n?" 报告问题":" report")),g.push(chalk.bold.cyan("[b]")+chalk.gray(n?" 上一张":" back")),g.push(chalk.bold.cyan("[card N]")+chalk.gray(n?" 跳转":" jump")),g.push(chalk.gray("[quit]")+chalk.gray(n?" 退出":" quit")),console.log(chalk.gray(" "+t)),console.log(chalk.gray(" "+(n?`第 ${o.number} / ${e.totalCards} 张`:`Card ${o.number} / ${e.totalCards}`))),console.log(" "+g.join(r)),console.log(chalk.gray(" "+t)),console.log(chalk.gray(" "+(n?"e = 报告本卡错误,不算作答;提交后回到本题继续":"e = report a problem with this card (not an answer); you return here after"))),console.log(chalk.gray(" "+(n?"更多: bookmark · status · /honors 奖牌墙 · lang zh/en":"more: bookmark · status · /honors · lang zh/en"))),console.log()}(l,t)}export function renderCheckFeedback(o,e,n,l=!1){const t=a().startsWith("zh");if(console.log(),n)console.log(chalk.green(" ✓ ")+chalk.white(t?"答对了。":"Correct."));else if(console.log(chalk.yellow(" ✗ ")+chalk.white(t?"记下了,继续。":"Noted — moving on.")),l){const e="y"===o.check?.answer?t?"是":"Yes":t?"否":"No";console.log(chalk.gray(" "+(t?"正确答案: ":"Answer: ")+e+". "+(t?"回看上面那段。":"Re-read the paragraph above.")))}const r=t?o._zh?.solution??o.solution:o.solution;if(r&&r.length>0){console.log(),console.log(chalk.cyan(" 📝 "+(t?"解答:":"Worked solution:")));for(const o of r){for(const e of p(o,60))console.log(chalk.white(" "+e));console.log()}}console.log()}function q(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,l){const r=a().startsWith("zh"),[c,s]=n.cardRange,g=e.checkResults||{};let i=0,d=0;for(let o=c;o<=s;o++){const e=g[String(o)];e&&(i++,e.correct&&d++)}const y="-".repeat(48);console.log();for(const o of t(" "))console.log(o);console.log(),console.log(chalk.bold.yellow(" "+y)),console.log(chalk.bold.yellow(" "+(r?`阶段 ${n.number} 完成`:`Phase ${n.number} complete`))+chalk.gray(" · ")+chalk.white(n.name));const u=i>0&&d===i?chalk.bold.green:chalk.bold.white;console.log(chalk.gray(" "+(r?"理解检查得分: ":"comprehension score: "))+u(`${d}/${i}`)+chalk.gray(0===i?r?" (本阶段未作答 y/n)":" (no y/n answered)":"")),l&&console.log(chalk.gray(" "+(r?"下一阶段: ":"next: "))+chalk.bold.cyan(r?`阶段 ${l.number}`:`Phase ${l.number}`)+chalk.gray(" · ")+chalk.white(l.name)),console.log(chalk.bold.yellow(" "+y));for(const o of q(r))console.log(o)}export function renderCurriculumComplete(o,e){const n=a().startsWith("zh"),l=e.checkResults||{},r="-".repeat(48);let c=0,s=0;console.log();for(const o of t(" "))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,t]=e.cardRange;let r=0,a=0;for(let e=o;e<=t;e++){const o=l[String(e)];o&&(r++,o.correct&&a++)}c+=r,s+=a;const g=r>0&&a===r?chalk.green:chalk.white;console.log(chalk.gray(" "+(n?`阶段 ${e.number}`:`Phase ${e.number}`).padEnd(8))+g(`${a}/${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}/${c}`)),console.log(chalk.bold.green(" "+r));for(const o of q(n))console.log(o);console.log()}export function renderMCQCard(o,l){const t=a(),r=e(o,t);y(r,l),m(),h(chalk.bold.yellow("🎯 "+r.title)),h(chalk.gray("─".repeat(Math.min(r.title.length+4,s)))),m();for(const o of C(r.question,60))h(" "+o);m();for(const o of["A","B","C","D"])h(chalk.cyan(` ${o}.`)+" "+chalk.white(r.options[o]));m(),u(),console.log(chalk.gray(" "+(t.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",t))),console.log()}export function renderMCQFeedback(o,l,t,r){const c=a(),s=e(o,c);console.log(),t?console.log(" "+chalk.bold.green(n("correct",c)+" ")+chalk.gray(`${n("one_point",c)} · ${l} = ${s.options[l]}`)):console.log(" "+chalk.bold.red(n("not_quite",c)+" ")+chalk.gray(`${n("you_chose",c)} ${l}; ${n("answer_is",c)} ${s.answer}.`)),console.log(),console.log(chalk.gray(" "+n("explanation",c)));for(const o of C(s.explanation,60))console.log(chalk.gray(" "+o));const g=Object.values(r.mcqResults),i=g.filter(o=>o.correct).length;console.log(),console.log(chalk.gray(" "+n("mcq_accuracy_so_far",c)+" ")+chalk.white(`${i}/${g.length}`)),console.log(),t||(console.log(chalk.gray(" "+n("press_ok_continue",c)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",c))),console.log())}export function renderPracticalCard(o,l){const t=a(),r=e(o,t);y(r,l),m(),h(chalk.bold.yellow("🛠 "+r.title)),h(chalk.gray("─".repeat(Math.min(r.title.length+4,s)))),m();for(const o of C(r.task,60))h(" "+o);if(r.starterCode){m(),h(chalk.gray(" "+n("starter_code",t))),m();for(const o of r.starterCode.split("\n"))h(" "+chalk.cyan(o))}m(),u(),console.log(chalk.gray(" "+n("try_in_sandbox",t)+" ")+chalk.bold.cyan("!python3")+chalk.gray(" "+n("drops_into_python",t))),console.log(chalk.gray(" "+n("when_done",t))),console.log(chalk.gray(" ")+chalk.bold.green("done")+chalk.gray(" "+n("done_desc",t))),console.log(chalk.gray(" ")+chalk.bold.yellow("skip")+chalk.gray(" "+n("skip_desc",t))),console.log(chalk.gray(" card N jump to a card by number")),console.log()}export function renderPracticalSuccess(o){const l=a(),t=e(o,l);console.log(),console.log(" "+chalk.bold.green(n("practical_recorded",l))),console.log(),console.log(chalk.gray(" "+n("reference_answer",l)));for(const o of C(t.successHint,60))console.log(chalk.gray(" "+o));console.log(),console.log(chalk.gray(" "+n("press_ok_continue",l)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",l))),console.log()}export function renderSimDemoCard(o,l){const t=a(),r=e(o,t);y(r,l),m(),h(chalk.bold.yellow("🎬 "+r.title)),m();for(const o of C(r.description,60))h(" "+o);m(),h(chalk.gray(" "+n("sim_requires",t))),m(),u(),console.log(chalk.gray(" ")+chalk.bold.cyan("sim")+chalk.gray(" "+n("sim_launch_desc",t))),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",t))),console.log(chalk.gray(" bookmark "+n("bookmark_desc",t))),console.log(chalk.gray(" back "+n("back_desc",t))),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",t))),console.log()}export function renderArenaTaskCard(o,n){const l=a(),t=l.startsWith("zh"),r=e(o,l);y(r,n),m(),h(chalk.bold.cyan("🏟 "+r.title)),h(chalk.gray(" "+r.origin+" · "+r.domain+" · "+r.metric)),m();for(const o of r.statement){for(const e of C(o,60))h(" "+e);m()}h(chalk.bold.green(t?" 核心要点 — baseline → 解法的关键变化":" CORE DELTA — baseline → solution"));for(const o of r.core_delta)for(const e of C("• "+o,58))h(" "+chalk.white(e));m(),h(chalk.gray((t?" 提交格式: ":" Submission: ")+r.submission_format)),r.hints&&h(chalk.gray(t?" 本题有 5 级提示 (a–e),进入 arena 后逐级解锁。":" 5 progressive hints (a–e) unlock inside the arena.")),m(),u(),console.log(chalk.gray(" ")+chalk.bold.cyan("arena "+r.task_id)+chalk.gray(t?" 动手做这道题(下载 / 提示 / 提交 / 榜单)":" open the arena (download / hints / submit / board)")),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(t?" 继续下一张":" continue to next")),console.log(chalk.gray(" back "+(t?"上一张":"previous"))),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+(t?"退出":"exit"))),console.log()}export function renderComingSoonCard(o,n){const l=a(),t=l.startsWith("zh"),r=e(o,l);y(r,n),m(),h(chalk.bold.yellow("🔜 "+r.title)),m();const c=r.note||(t?"这道竞技场题目正在编写中,敬请期待。":"This arena task is coming soon.");for(const o of C(c,60))h(" "+chalk.gray(o));m(),u(),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(t?" 继续":" continue")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+(t?"退出":"exit"))),console.log()}export function renderMilestone(o,l){const t=a(),r=e(o,t),c=o.number===l.totalCards;console.log(),console.log(chalk.bold.yellow(" ╭"+g("═"))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+chalk.white(" "+n("milestone_header",t))),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",t)));for(const o of C(r.realWorldLevel,60))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("whats_next",t)));for(const o of C(r.unlockedNext,60))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ╰"+g("═"))),console.log(),c?(console.log(chalk.gray(" "+n("demo_complete",t))),console.log(),console.log(chalk.gray(" "+n("unlock_full",t)+" ")+chalk.white(n("curriculum_name",t))+chalk.gray(",")),console.log(chalk.gray(" "+n("contact_team_leader",t)+" ")+chalk.bold.yellow("EA")+chalk.gray(" "+n("learn_token",t))),console.log(chalk.gray(" "+n("or_email",t)+" ")+chalk.cyan("australia@icoa2026.au")+chalk.gray(" "+n("for_partnership",t))),console.log(),console.log(chalk.gray(" "+n("type_quit",t)+" ")+chalk.bold.green("quit")+chalk.gray(" "+n("to_exit",t)+" ")+chalk.bold.green("status")+chalk.gray(" "+n("for_dashboard",t)))):(console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",t))),console.log(chalk.gray(" status "+n("status_full",t))),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",t)))),console.log()}export function renderStatus(o,e){const l=a(),t=e.cardsCompleted.length,r=o.totalCards,c=Object.values(e.mcqResults),s=c.filter(o=>o.correct).length;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(n("status_title",l))+" "+chalk.cyan("─".repeat(25))),m(),h(" "+chalk.gray(n("total_progress",l)+" ")+i(t,r)+chalk.gray(` (${t}/${r})`)),h(" "+chalk.gray(n("streak",l)+" ")+d(e.streakDays)+chalk.gray(` (${n("longest",l)} ${e.longestStreak})`)),h(" "+chalk.gray(n("mcq_accuracy",l)+" ")+chalk.white(`${s}/${c.length}`)),h(" "+chalk.gray(n("practicals_done",l)+" ")+chalk.white(`${e.practicalsCompleted.length}`)),h(" "+chalk.gray(n("bookmarked",l)+" ")+chalk.white(`${e.bookmarks.length}`)),m();const g=n("module",l).replace(":","").trim();for(const n of o.modules){const[o,l]=n.cardRange,t=e.cardsCompleted.filter(e=>e>=o&&e<=l).length,r=l-o+1;h(" "+(t===r?chalk.green("✓"):t>0?chalk.yellow("▶"):chalk.gray("□"))+" "+chalk.gray(`${g} ${n.number}: `)+chalk.white(`${t}/${r}`)+chalk.gray(" "+n.name))}if(m(),e.achievements.length>0){h(" "+chalk.gray(n("achievements",l)));for(const o of e.achievements)h(" "+chalk.bold.yellow("★ ")+chalk.white(o))}else h(" "+chalk.gray(n("achievements_none",l)));m(),u()}
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 l}from"./config.js";import{icoaBannerLines as t}from"./banner.js";import{renderCharts as r,detectCaps as s}from"./render-card.js";function a(){return(l().language||"en").toLowerCase()}const c=64;function g(o="─"){return o.repeat(66)}function i(o,e,n=20){const l=e>0?o/e:0,t=Math.floor(l*n),r=n-t;return`${chalk.green("█".repeat(t))+chalk.gray("░".repeat(r))} ${(100*l).toFixed(1)}%`}function d(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,l){const t=a(),r=e.cardsCompleted.length,s=o.totalCards,c=s-r,y=o.modules.find(o=>e.currentCard>=o.cardRange[0]&&e.currentCard<=o.cardRange[1]),u=/demo/i.test(o.id);if(console.log(),console.log(chalk.cyan(" ╭"+g("═"))),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ║ ")+chalk.bold.white(" "+(o.name?.trim()||n("academy_title",t)))),console.log(chalk.cyan(" ║")),l)console.log(chalk.cyan(" ║ ")+chalk.white(" "+n(u?"welcome_new_demo":"welcome_new",t)));else{const o=new Date(e.lastSeenAt),l=Math.floor((Date.now()-o.getTime())/36e5),r=l<1?n("just_now",t):l<24?`${l}${n("ago_hours",t)}`:`${Math.floor(l/24)}${n("ago_days",t)}`;console.log(chalk.cyan(" ║ ")+chalk.white(` ${n("welcome_back",t)} ${r}`))}console.log(chalk.cyan(" ║"));const h=y?`${y.number}. ${y.name}`:"—";console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("module",t).padEnd(11))+chalk.white(h)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("progress",t).padEnd(11))+i(r,s)+chalk.gray(` (${r}/${s})`)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("streak",t).padEnd(11))+d(e.streakDays)),c>0&&e.currentCard<=s&&console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("next_card",t).padEnd(11))+chalk.white(`#${e.currentCard} ${n("of",t)} ${s}`)),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ╰"+g("═"))),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────"));const m=0===r?n("continue_start",t):`${n("continue_resume",t)} ${e.currentCard}`;console.log(chalk.bold.green(" continue")+chalk.gray(" "+m)),console.log(chalk.yellow(" status")+chalk.gray(" "+n("status_full",t))),e.bookmarks.length>0&&console.log(chalk.yellow(" bookmarks")+chalk.gray(` ${e.bookmarks.length} ${n("bookmarks_desc",t)}`)),console.log(chalk.gray(" quit")+chalk.gray(" "+n("quit_desc",t))),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log()}function y(o,e){const l=a(),t=o.module,r=e.modules.find(o=>o.number===t),s=r?r.name:n("unknown_module",l),g=n("module",l).replace(":","").trim(),i=`${n("card",l)} ${o.number} / ${e.totalCards} · ${g} ${t} · ${s}`;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(i)+" "+chalk.cyan("─".repeat(Math.max(0,c-i.length-4))))}function u(){console.log(chalk.cyan(" ╰"+g())),console.log()}function h(o){console.log(chalk.cyan(" │ ")+chalk.white(o))}function m(){console.log(chalk.cyan(" │"))}const b=/[┌┐└┘├┤┬┴┼─│╔╗╚╝═║╠╣╦╩╬▓▒░█]/;function f(o){return/^(?: {4}|\t)/.test(o)||b.test(o)||/ {3,}/.test(o)}function p(e,n){const l=[];let t="",r=0,s=!1;for(const a of function(e){const n=[],l=e=>{let l="";const t=()=>{l&&(n.push({t:"atom",s:l}),l="")};for(const r of e)" "===r?(t(),n.push({t:"space"})):2===o(r)?(t(),n.push({t:"wide",s:r})):l+=r;t()},t=/`[^`]*`/g;let r,s=0;for(;null!==(r=t.exec(e));)l(e.slice(s,r.index)),n.push({t:"code",s:r[0]}),s=r.index+r[0].length;return l(e.slice(s)),n}(e)){if("space"===a.t){""!==t&&(s=!0);continue}const e=a.s,c=o("code"===a.t?e.replace(/`/g,""):e),g=s?1:0,i="atom"===a.t&&e.length<=2&&/^[.,;:!?)\]}…'"]+$/.test(e);""!==t&&!i&&r+g+c>n?(l.push(t),t=e,r=c,s=!1):(s&&(t+=" ",r+=1,s=!1),t+=e,r+=c)}return t&&l.push(t),l}const $=/\b(?:Common bug|Common mistake|Common error|Pitfall|Gotcha|Footgun)\b[::]?|\b(?:Warning|Caution)\b[::]|(?:常见错误|常见陷阱|常见误区|陷阱|注意|警告|误区)[::]/gi;function w(o,e){const n=e?chalk.bold.white:chalk.white;let l,t="",r=0;for($.lastIndex=0;null!==(l=$.exec(o));)l.index>r&&(t+=n(o.slice(r,l.index))),t+=chalk.bold.yellow(l[0]),r=l.index+l[0].length;return r<o.length&&(t+=n(o.slice(r))),t}function _(o,e=!1){let n="",l=0;const t=/`([^`]*)`/g;let r;for(;null!==(r=t.exec(o));)r.index>l&&(n+=w(o.slice(l,r.index),e)),n+=chalk.cyan(r[1]),l=r.index+r[0].length;return l<o.length&&(n+=w(o.slice(l),e)),n}function k(o,e=!1){let n=!1;for(const l of o.split("\n"))if(/^\s*```/.test(l))n=!n;else if(n)console.log(chalk.cyan(" │ ")+chalk.cyan(` ${l}`));else if(""!==l)if(f(l))console.log(chalk.cyan(" │ ")+chalk.white(l));else for(const o of p(l,60))console.log(chalk.cyan(" │ ")+` ${_(o,e)}`);else m()}function C(o,e){const n=[];for(const l of o.split("\n")){if(""===l){n.push("");continue}if(f(l)){n.push(l);continue}let o="";for(const t of l.split(" "))(o+" "+t).trim().length>e?(n.push(o.trim()),o=t):o=(o+" "+t).trim();o&&n.push(o)}return n}let x=!1;export function setLearnAiEnabled(o){x=o}let v=new Set;export function setLearnPqsViewCards(o){v=o}export function renderHonorWall(o){const e=a().startsWith("zh"),{totals:n,chapters:l}=o,t=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===t){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 s=l.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(" "+s);const c=l.flatMap(o=>o.cards.map(e=>({...e,chapter:o.chapter})));c.sort((o,e)=>o.rank-e.rank||o.chapter-e.chapter||o.card-e.card);for(const o of c.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}`):""))}c.length>24&&console.log(chalk.gray(e?` …还有 ${c.length-24} 枚`:` …and ${c.length-24} more`)),console.log(chalk.gray(" ─────────────────────────────────────────"))}export function renderKnowledgeCard(l,t){const g=a(),i=e(l,g);y(i,t),m();const d=p(i.title,60);for(const o of d)h(chalk.bold.yellow(o));h(chalk.gray("─".repeat(Math.min(Math.max(...d.map(e=>o(e))),c))));const b=!!(l.chart_ids&&l.chart_ids.length>0);if(b&&r(l.chart_ids.slice(0,1),t.charts,s()),i.key_point&&""!==i.key_point.trim()?(b||m(),function(o,e,n){const l=a().startsWith("zh"),t=s().cols,r="-".repeat(Math.min(c,Math.max(24,t-6))),g=l?"要点":"KEY POINT",i=l?`第 ${e} / ${n} 张`:`Card ${e} / ${n}`;console.log(chalk.cyan(" │ ")+chalk.green(r)),console.log(chalk.cyan(" │ ")+chalk.green(`${g} · ${i}`));for(const e of p(o,60))console.log(chalk.cyan(" │ ")+chalk.bold.green(" "+e));console.log(chalk.cyan(" │ ")+chalk.green(r)),m()}(i.key_point,i.number,t.totalCards)):b||m(),i.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){k(n[1],!0);const o=n[2]?.trimStart();o&&k(o,!1)}else k(o,!1);m()}),i.icoaConnection){h(chalk.magenta(" "+n("icoa_connection",g))),h(chalk.gray(" "+"─".repeat(20)));for(const o of p(i.icoaConnection,60))h(" "+chalk.magenta(o));m()}if(l.check){const o=g.startsWith("zh"),e=l._zh?.checkStatement??l._zh?.check?.statement,n=o?e??l.check.statement:l.check.statement;h(chalk.cyan(" 🤔 "+(o?"请回答 (输 y 或 n):":"Quick comprehension check (type y or n):"))),h(chalk.gray(" "+"─".repeat(20)));for(const o of p(n,60))h(" "+chalk.white(o));m()}u(),function(o,e){const n=a().startsWith("zh"),l=s().cols,t="-".repeat(Math.min(c,Math.max(24,l-6))),r=chalk.gray(" "),g=[];o.check?(g.push(chalk.bold.green("[y]")+chalk.gray(n?" 是":" yes")),g.push(chalk.bold.green("[n]")+chalk.gray(n?" 否":" no"))):g.push(chalk.bold.green("[ok]")+chalk.gray(n?" 继续":" next")),x&&(g.push(chalk.bold.magenta("[/ai]")+chalk.gray(n?" AI导师":" AI tutor")),g.push(chalk.bold.magenta("[/submit]")+chalk.gray(n?" 提问评分":" prompt score")),v.has(o.number)&&g.push(chalk.bold.magenta("[/view]")+chalk.gray(n?" 榜单":" top-3"))),g.push(chalk.bold.yellow("[e]")+chalk.gray(n?" 报告问题":" report")),g.push(chalk.bold.cyan("[b]")+chalk.gray(n?" 上一张":" back")),g.push(chalk.bold.cyan("[card N]")+chalk.gray(n?" 跳转":" jump")),g.push(chalk.gray("[quit]")+chalk.gray(n?" 退出":" quit")),console.log(chalk.gray(" "+t)),console.log(chalk.gray(" "+(n?`第 ${o.number} / ${e.totalCards} 张`:`Card ${o.number} / ${e.totalCards}`))),console.log(" "+g.join(r)),console.log(chalk.gray(" "+t)),console.log(chalk.gray(" "+(n?"e = 报告本卡错误,不算作答;提交后回到本题继续":"e = report a problem with this card (not an answer); you return here after"))),console.log(chalk.gray(" "+(n?"更多: bookmark · status · /honors 奖牌墙 · lang zh/en":"more: bookmark · status · /honors · lang zh/en"))),console.log()}(l,t)}export function renderCheckFeedback(o,e,n,l=!1){const t=a().startsWith("zh");if(console.log(),n)console.log(chalk.green(" ✓ ")+chalk.white(t?"答对了。":"Correct."));else if(console.log(chalk.yellow(" ✗ ")+chalk.white(t?"记下了,继续。":"Noted — moving on.")),l){const e="y"===o.check?.answer?t?"是":"Yes":t?"否":"No";console.log(chalk.gray(" "+(t?"正确答案: ":"Answer: ")+e+". "+(t?"回看上面那段。":"Re-read the paragraph above.")))}const r=t?o._zh?.solution??o.solution:o.solution;if(r&&r.length>0){console.log(),console.log(chalk.cyan(" 📝 "+(t?"解答:":"Worked solution:")));for(const o of r){for(const e of p(o,60))console.log(chalk.white(" "+e));console.log()}}console.log()}function z(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,l,r,s){const c=a().startsWith("zh"),[g,i]=n.cardRange,d=e.checkResults||{};let y=0,u=0;for(let o=g;o<=i;o++){const e=d[String(o)];e&&(y++,e.correct&&u++)}const h="-".repeat(48);console.log();for(const o of t(" "))console.log(o);console.log(),console.log(chalk.bold.yellow(" "+h)),console.log(chalk.bold.yellow(" "+(c?`阶段 ${n.number} 完成`:`Phase ${n.number} complete`))+chalk.gray(" · ")+chalk.white(n.name));const m=y>0&&u===y?chalk.bold.green:chalk.bold.white;console.log(chalk.gray(" "+(c?"理解检查得分: ":"comprehension score: "))+m(`${u}/${y}`)+chalk.gray(0===y?c?" (本阶段未作答 y/n)":" (no y/n answered)":""));const b=String(i).length;for(let e=g;e<=i;e++){const n=o.cards.find(o=>o.number===e);if(!n)continue;const l=n,t=l.title??l.badge??"",r=(c?l._zh?.title??l._zh?.badge??t:t)||"",s=null!=n.check,a=d[String(e)];let g;g=a?a.correct?chalk.green("✓"):chalk.red("✗"):s?chalk.gray("○"):chalk.gray("·");const i=p(r,54-b),y=i[0]+(i.length>1?"…":"");console.log(chalk.gray(` ${String(e).padStart(b)} `)+g+chalk.white(" "+y))}if(console.log(chalk.gray(c?" ✓ 答对 · ✗ 答错 · ○ 未作答 · 无 y/n 的卡不计分":" ✓ correct · ✗ wrong · ○ unanswered · cards without a y/n are unscored")),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(" ⭐ "+(c?"提问得分 (best): ":"Prompt scores (best): "))+(0===r.length?chalk.gray(c?"本阶段尚未提问 — 用 /ai 研究,再 /submit 评分":"none this phase — research with /ai, then /submit"):r.map(e=>chalk.gray((c?"卡":"#")+e.card+" ")+o(e.tier)(String(e.score))).join(chalk.gray(" · "))))}if(s?.configured&&s.contest){const o=s.contest,e=chalk.bold.cyan(" ⚖ "+(c?"判分赛: ":"Contest: "));o.passed?console.log(e+chalk.bold.green(`${o.best_ac}/${o.n} AC ✅ `+(c?"已过关":"passed"))):0===o.attempts?console.log(e+chalk.white(c?`${o.n} 题 · 过 ${o.need} 题解锁 — 输入 `:`${o.n} problems · AC ${o.need} to pass — type `)+chalk.bold.green("contest")+chalk.white(c?" 开始":" to start")):console.log(e+chalk.yellow(c?`最好 ${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(c?" 再战":" to retry"))}l&&console.log(chalk.gray(" "+(c?"下一阶段: ":"next: "))+chalk.bold.cyan(c?`阶段 ${l.number}`:`Phase ${l.number}`)+chalk.gray(" · ")+chalk.white(l.name)),console.log(chalk.bold.yellow(" "+h));for(const o of z(c))console.log(o)}export function renderCurriculumComplete(o,e){const n=a().startsWith("zh"),l=e.checkResults||{},r="-".repeat(48);let s=0,c=0;console.log();for(const o of t(" "))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,t]=e.cardRange;let r=0,a=0;for(let e=o;e<=t;e++){const o=l[String(e)];o&&(r++,o.correct&&a++)}s+=r,c+=a;const g=r>0&&a===r?chalk.green:chalk.white;console.log(chalk.gray(" "+(n?`阶段 ${e.number}`:`Phase ${e.number}`).padEnd(8))+g(`${a}/${r}`.padEnd(7))+chalk.gray(e.name))}console.log(chalk.bold.green(" "+r)),console.log(chalk.bold.green(" "+(n?"总分 / overall: ":"Overall: "))+chalk.bold.white(`${c}/${s}`)),console.log(chalk.bold.green(" "+r));for(const o of z(n))console.log(o);console.log()}export function renderMCQCard(o,l){const t=a(),r=e(o,t);y(r,l),m(),h(chalk.bold.yellow("🎯 "+r.title)),h(chalk.gray("─".repeat(Math.min(r.title.length+4,c)))),m();for(const o of C(r.question,60))h(" "+o);m();for(const o of["A","B","C","D"])h(chalk.cyan(` ${o}.`)+" "+chalk.white(r.options[o]));m(),u(),console.log(chalk.gray(" "+(t.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",t))),console.log()}export function renderMCQFeedback(o,l,t,r){const s=a(),c=e(o,s);console.log(),t?console.log(" "+chalk.bold.green(n("correct",s)+" ")+chalk.gray(`${n("one_point",s)} · ${l} = ${c.options[l]}`)):console.log(" "+chalk.bold.red(n("not_quite",s)+" ")+chalk.gray(`${n("you_chose",s)} ${l}; ${n("answer_is",s)} ${c.answer}.`)),console.log(),console.log(chalk.gray(" "+n("explanation",s)));for(const o of C(c.explanation,60))console.log(chalk.gray(" "+o));const g=Object.values(r.mcqResults),i=g.filter(o=>o.correct).length;console.log(),console.log(chalk.gray(" "+n("mcq_accuracy_so_far",s)+" ")+chalk.white(`${i}/${g.length}`)),console.log(),t||(console.log(chalk.gray(" "+n("press_ok_continue",s)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",s))),console.log())}export function renderPracticalCard(o,l){const t=a(),r=e(o,t);y(r,l),m(),h(chalk.bold.yellow("🛠 "+r.title)),h(chalk.gray("─".repeat(Math.min(r.title.length+4,c)))),m();for(const o of C(r.task,60))h(" "+o);if(r.starterCode){m(),h(chalk.gray(" "+n("starter_code",t))),m();for(const o of r.starterCode.split("\n"))h(" "+chalk.cyan(o))}m(),u(),console.log(chalk.gray(" "+n("try_in_sandbox",t)+" ")+chalk.bold.cyan("!python3")+chalk.gray(" "+n("drops_into_python",t))),console.log(chalk.gray(" "+n("when_done",t))),console.log(chalk.gray(" ")+chalk.bold.green("done")+chalk.gray(" "+n("done_desc",t))),console.log(chalk.gray(" ")+chalk.bold.yellow("skip")+chalk.gray(" "+n("skip_desc",t))),console.log(chalk.gray(" card N jump to a card by number")),console.log()}export function renderPracticalSuccess(o){const l=a(),t=e(o,l);console.log(),console.log(" "+chalk.bold.green(n("practical_recorded",l))),console.log(),console.log(chalk.gray(" "+n("reference_answer",l)));for(const o of C(t.successHint,60))console.log(chalk.gray(" "+o));console.log(),console.log(chalk.gray(" "+n("press_ok_continue",l)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",l))),console.log()}export function renderSimDemoCard(o,l){const t=a(),r=e(o,t);y(r,l),m(),h(chalk.bold.yellow("🎬 "+r.title)),m();for(const o of C(r.description,60))h(" "+o);m(),h(chalk.gray(" "+n("sim_requires",t))),m(),u(),console.log(chalk.gray(" ")+chalk.bold.cyan("sim")+chalk.gray(" "+n("sim_launch_desc",t))),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",t))),console.log(chalk.gray(" bookmark "+n("bookmark_desc",t))),console.log(chalk.gray(" back "+n("back_desc",t))),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",t))),console.log()}export function renderArenaTaskCard(o,n){const l=a(),t=l.startsWith("zh"),r=e(o,l);y(r,n),m(),h(chalk.bold.cyan("🏟 "+r.title)),h(chalk.gray(" "+r.origin+" · "+r.domain+" · "+r.metric)),m();for(const o of r.statement){for(const e of C(o,60))h(" "+e);m()}h(chalk.bold.green(t?" 核心要点 — baseline → 解法的关键变化":" CORE DELTA — baseline → solution"));for(const o of r.core_delta)for(const e of C("• "+o,58))h(" "+chalk.white(e));m(),h(chalk.gray((t?" 提交格式: ":" Submission: ")+r.submission_format)),r.hints&&h(chalk.gray(t?" 本题有 5 级提示 (a–e),进入 arena 后逐级解锁。":" 5 progressive hints (a–e) unlock inside the arena.")),m(),u(),console.log(chalk.gray(" ")+chalk.bold.cyan("arena "+r.task_id)+chalk.gray(t?" 动手做这道题(下载 / 提示 / 提交 / 榜单)":" open the arena (download / hints / submit / board)")),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(t?" 继续下一张":" continue to next")),console.log(chalk.gray(" back "+(t?"上一张":"previous"))),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+(t?"退出":"exit"))),console.log()}export function renderComingSoonCard(o,n){const l=a(),t=l.startsWith("zh"),r=e(o,l);y(r,n),m(),h(chalk.bold.yellow("🔜 "+r.title)),m();const s=r.note||(t?"这道竞技场题目正在编写中,敬请期待。":"This arena task is coming soon.");for(const o of C(s,60))h(" "+chalk.gray(o));m(),u(),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(t?" 继续":" continue")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+(t?"退出":"exit"))),console.log()}export function renderMilestone(o,l){const t=a(),r=e(o,t),s=o.number===l.totalCards;console.log(),console.log(chalk.bold.yellow(" ╭"+g("═"))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+chalk.white(" "+n("milestone_header",t))),console.log(chalk.bold.yellow(" ║"));const c=` ${r.badge} ${r.emoji}`;console.log(chalk.bold.yellow(" ║ ")+chalk.bold.green(c)),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",t)));for(const o of C(r.realWorldLevel,60))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("whats_next",t)));for(const o of C(r.unlockedNext,60))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ╰"+g("═"))),console.log(),s?(console.log(chalk.gray(" "+n("demo_complete",t))),console.log(),console.log(chalk.gray(" "+n("unlock_full",t)+" ")+chalk.white(n("curriculum_name",t))+chalk.gray(",")),console.log(chalk.gray(" "+n("contact_team_leader",t)+" ")+chalk.bold.yellow("EA")+chalk.gray(" "+n("learn_token",t))),console.log(chalk.gray(" "+n("or_email",t)+" ")+chalk.cyan("australia@icoa2026.au")+chalk.gray(" "+n("for_partnership",t))),console.log(),console.log(chalk.gray(" "+n("type_quit",t)+" ")+chalk.bold.green("quit")+chalk.gray(" "+n("to_exit",t)+" ")+chalk.bold.green("status")+chalk.gray(" "+n("for_dashboard",t)))):(console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",t))),console.log(chalk.gray(" status "+n("status_full",t))),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",t)))),console.log()}export function renderStatus(o,e){const l=a(),t=e.cardsCompleted.length,r=o.totalCards,s=Object.values(e.mcqResults),c=s.filter(o=>o.correct).length;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(n("status_title",l))+" "+chalk.cyan("─".repeat(25))),m(),h(" "+chalk.gray(n("total_progress",l)+" ")+i(t,r)+chalk.gray(` (${t}/${r})`)),h(" "+chalk.gray(n("streak",l)+" ")+d(e.streakDays)+chalk.gray(` (${n("longest",l)} ${e.longestStreak})`)),h(" "+chalk.gray(n("mcq_accuracy",l)+" ")+chalk.white(`${c}/${s.length}`)),h(" "+chalk.gray(n("practicals_done",l)+" ")+chalk.white(`${e.practicalsCompleted.length}`)),h(" "+chalk.gray(n("bookmarked",l)+" ")+chalk.white(`${e.bookmarks.length}`)),m();const g=n("module",l).replace(":","").trim();for(const n of o.modules){const[o,l]=n.cardRange,t=e.cardsCompleted.filter(e=>e>=o&&e<=l).length,r=l-o+1;h(" "+(t===r?chalk.green("✓"):t>0?chalk.yellow("▶"):chalk.gray("□"))+" "+chalk.gray(`${g} ${n.number}: `)+chalk.white(`${t}/${r}`)+chalk.gray(" "+n.name))}if(m(),e.achievements.length>0){h(" "+chalk.gray(n("achievements",l)));for(const o of e.achievements)h(" "+chalk.bold.yellow("★ ")+chalk.white(o))}else h(" "+chalk.gray(n("achievements_none",l)));m(),u()}