icoa-cli 2.19.461 → 2.19.463

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.
@@ -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?o.learnWidth:o.learnWide?"wide":"normal"}export function setLearnWidth(o){g=o}export function learnWidthCap(o=g){return"wider"===o?120:"wide"===o?96:64}export function learnW(){if("normal"===g)return 64;const o=process.stdout.columns||64;return Math.max(64,Math.min(o-6,learnWidthCap()))}const i=learnW;function d(o="─"){return o.repeat(i()+2)}function y(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 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 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]),i=/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(i?"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 h=g?`${g.number}. ${g.name}`:"—";console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("module",l).padEnd(11))+chalk.white(h)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("progress",l).padEnd(11))+y(r,a)+chalk.gray(` (${r}/${a})`)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("streak",l).padEnd(11))+u(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 m=0===r?n("continue_start",l):`${n("continue_resume",l)} ${e.currentCard}`;console.log(chalk.bold.green(" continue")+chalk.gray(" "+m)),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 h(o,e){const t=c(),l=o.module,r=e.modules.find(o=>o.number===l),a=r?r.name:n("unknown_module",t),s=n("module",t).replace(":","").trim(),g=`${n("card",t)} ${o.number} / ${e.totalCards} · ${s} ${l} · ${a}`;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(g)+" "+chalk.cyan("─".repeat(Math.max(0,i()-g.length-4))))}function m(){console.log(chalk.cyan(" ╰"+d())),console.log()}function b(o){console.log(chalk.cyan(" │ ")+chalk.white(o))}function p(){console.log(chalk.cyan(" │"))}const f=/[┌┐└┘├┤┬┴┼─│╔╗╚╝═║╠╣╦╩╬▓▒░█]/;function $(o){return/^(?: {4}|\t)/.test(o)||f.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 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 t,l="",r=0;for(w.lastIndex=0;null!==(t=w.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 k(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 x(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($(t))console.log(chalk.cyan(" │ ")+chalk.white(t));else for(const o of wrapMixed(t,i()-4))console.log(chalk.cyan(" │ ")+` ${k(o,e)}`);else p()}function C(o,e){const n=[];for(const t of o.split("\n")){if(""===t){n.push("");continue}if($(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 M=!1;export function setLearnAiEnabled(o){M=o}let W=new Set;export function setLearnPqsViewCards(o){W=o}let v=new Map;export function setLearnHonorRanks(o){v=o}const z=["①","②","③","④","⑤","⑥","⑦","⑧","⑨","⑩"];let q=[];export function setLearnReplies(o){q=o}let j=[];export function setLearnFaqs(o){j=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);h(y,l),p();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 f=!!(t.chart_ids&&t.chart_ids.length>0);if(f&&r(t.chart_ids.slice(0,1),l.charts,a()),y.key_point&&""!==y.key_point.trim()?(f||p(),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)),p()}(y.key_point,y.number,l.totalCards)):f||p(),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){x(n[1],!0);const o=n[2]?.trimStart();o&&x(o,!1)}else x(o,!1);p()}),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));p()}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;b(chalk.cyan(" 🤔 "+(o?"请回答 (输 y 或 n):":"Quick comprehension check (type y or n):"))),b(chalk.gray(" "+"─".repeat(20)));for(const o of wrapMixed(n,i()-4))b(" "+chalk.white(o));p()}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(" "),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")),M&&(y.push(chalk.bold.magenta("[/ai]")+chalk.gray(t?" 🐼 熊猫导师":" 🐼 Panda tutor")),y.push(chalk.bold.magenta("[/submit]")+chalk.gray(t?" 提问评分":" prompt score")));const u=v.get(e.number);var h;y.push(chalk.bold.magenta(u?`[/honors ${h=u,z[h-1]??`#${h}`}]`:"[/honors]")+chalk.gray(t?" 排行":" board")),"normal"===g?y.push(chalk.bold.cyan("[wide]")+chalk.gray(t?" 宽屏":" widen")):(y.push("wide"===g?chalk.bold.cyan("[wider]")+chalk.gray(t?" 更宽":" wider"):chalk.bold.cyan("[wide]")+chalk.gray(t?" 窄一点":" narrower")),y.push(chalk.bold.cyan("[normal]")+chalk.gray(t?" 64列":" 64-col")));const m=s(q,j,e.number,t),b=m.startsWith("💬");y.push((b?chalk.bold.yellow("[r]"):chalk.gray("[r]"))+(b?chalk.bold.white(` ${m}`):chalk.gray(` ${m}`))),y.push(chalk.bold.yellow("[e]")+chalk.gray(t?" 报告问题":" report")),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 p=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>p?(console.log(" "+f),f=e,$=n):(f=f?f+d+e:e,$=f===e?n:$+2+n)}f&&console.log(" "+f),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,p))console.log(chalk.gray(" "+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 S(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 m="-".repeat(48);console.log();for(const o of l(" "))console.log(o);console.log(),console.log(chalk.bold.yellow(" "+m)),console.log(chalk.bold.yellow(" "+(s?`阶段 ${n.number} 完成`:`Phase ${n.number} complete`))+chalk.gray(" · ")+chalk.white(n.name));const b=u>0&&h===u?chalk.bold.green:chalk.bold.white;console.log(chalk.gray(" "+(s?"理解检查得分: ":"comprehension score: "))+b(`${h}/${u}`)+chalk.gray(0===u?s?" (本阶段未作答 y/n)":" (no y/n answered)":""));const p=String(d).length;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=wrapMixed(r,i()-10-p),u=d[0]+(d.length>1?"…":"");console.log(chalk.gray(` ${String(e).padStart(p)} `)+g+chalk.white(" "+u))}if(console.log(chalk.gray(s?" ✓ 答对 · ✗ 答错 · ○ 未作答 · 无 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(" ⭐ "+(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?.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(" "+m));for(const o of S(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 S(n))console.log(o);console.log()}export function renderMCQCard(o,t){const l=c(),r=e(o,l);h(r,t),p(),b(chalk.bold.yellow("🎯 "+r.title)),b(chalk.gray("─".repeat(Math.min(r.title.length+4,i())))),p();for(const o of C(r.question,i()-4))b(" "+o);p();for(const o of["A","B","C","D"])b(chalk.cyan(` ${o}.`)+" "+chalk.white(r.options[o]));p(),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 C(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);h(r,t),p(),b(chalk.bold.yellow("🛠 "+r.title)),b(chalk.gray("─".repeat(Math.min(r.title.length+4,i())))),p();for(const o of C(r.task,i()-4))b(" "+o);if(r.starterCode){p(),b(chalk.gray(" "+n("starter_code",l))),p();for(const o of r.starterCode.split("\n"))b(" "+chalk.cyan(o))}p(),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 C(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);h(r,t),p(),b(chalk.bold.yellow("🎬 "+r.title)),p();for(const o of C(r.description,i()-4))b(" "+o);p(),b(chalk.gray(" "+n("sim_requires",l))),p(),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);h(r,n),p(),b(chalk.bold.cyan("🏟 "+r.title)),b(chalk.gray(" "+r.origin+" · "+r.domain+" · "+r.metric)),p();for(const o of r.statement){for(const e of C(o,i()-4))b(" "+e);p()}b(chalk.bold.green(l?" 核心要点 — baseline → 解法的关键变化":" CORE DELTA — baseline → solution"));for(const o of r.core_delta)for(const e of C("• "+o,i()-6))b(" "+chalk.white(e));p(),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.")),p(),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);h(r,n),p(),b(chalk.bold.yellow("🔜 "+r.title)),p();const a=r.note||(l?"这道竞技场题目正在编写中,敬请期待。":"This arena task is coming soon.");for(const o of C(a,i()-4))b(" "+chalk.gray(o));p(),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 C(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 C(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(o,e){const t=c(),l=e.cardsCompleted.length,r=o.totalCards,a=Object.values(e.mcqResults),s=a.filter(o=>o.correct).length;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(n("status_title",t))+" "+chalk.cyan("─".repeat(25))),p(),b(" "+chalk.gray(n("total_progress",t)+" ")+y(l,r)+chalk.gray(` (${l}/${r})`)),b(" "+chalk.gray(n("streak",t)+" ")+u(e.streakDays)+chalk.gray(` (${n("longest",t)} ${e.longestStreak})`)),b(" "+chalk.gray(n("mcq_accuracy",t)+" ")+chalk.white(`${s}/${a.length}`)),b(" "+chalk.gray(n("practicals_done",t)+" ")+chalk.white(`${e.practicalsCompleted.length}`)),b(" "+chalk.gray(n("bookmarked",t)+" ")+chalk.white(`${e.bookmarks.length}`)),p();const g=n("module",t).replace(":","").trim();for(const n of o.modules){const[o,t]=n.cardRange,l=e.cardsCompleted.filter(e=>e>=o&&e<=t).length,r=t-o+1;b(" "+(l===r?chalk.green("✓"):l>0?chalk.yellow("▶"):chalk.gray("□"))+" "+chalk.gray(`${g} ${n.number}: `)+chalk.white(`${l}/${r}`)+chalk.gray(" "+n.name))}if(p(),e.achievements.length>0){b(" "+chalk.gray(n("achievements",t)));for(const o of e.achievements)b(" "+chalk.bold.yellow("★ ")+chalk.white(o))}else b(" "+chalk.gray(n("achievements_none",t)));p(),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 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?o.learnWidth:o.learnWide?"wide":"normal"}export function setLearnWidth(o){g=o}export function learnWidthCap(o=g){return"wider"===o?120:"wide"===o?96:64}export function learnW(){if("normal"===g)return 64;const o=process.stdout.columns||64;return Math.max(64,Math.min(o-6,learnWidthCap()))}const i=learnW;function d(o="─"){return o.repeat(i()+2)}function y(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 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 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]),i=/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(i?"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 h=g?`${g.number}. ${g.name}`:"—";console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("module",l).padEnd(11))+chalk.white(h)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("progress",l).padEnd(11))+y(r,a)+chalk.gray(` (${r}/${a})`)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("streak",l).padEnd(11))+u(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 m=0===r?n("continue_start",l):`${n("continue_resume",l)} ${e.currentCard}`;console.log(chalk.bold.green(" continue")+chalk.gray(" "+m)),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 h(o,e){const t=c(),l=o.module,r=e.modules.find(o=>o.number===l),a=r?r.name:n("unknown_module",t),s=n("module",t).replace(":","").trim(),g=`${n("card",t)} ${o.number} / ${e.totalCards} · ${s} ${l} · ${a}`;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(g)+" "+chalk.cyan("─".repeat(Math.max(0,i()-g.length-4))))}function m(){console.log(chalk.cyan(" ╰"+d())),console.log()}function b(o){console.log(chalk.cyan(" │ ")+chalk.white(o))}function p(){console.log(chalk.cyan(" │"))}const f=/[┌┐└┘├┤┬┴┼─│╔╗╚╝═║╠╣╦╩╬▓▒░█]/;function $(o){return/^(?: {4}|\t)/.test(o)||f.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 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 t,l="",r=0;for(w.lastIndex=0;null!==(t=w.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 k(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 x(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($(t))console.log(chalk.cyan(" │ ")+chalk.white(t));else for(const o of wrapMixed(t,i()-4))console.log(chalk.cyan(" │ ")+` ${k(o,e)}`);else p()}function C(o,e){const n=[];for(const t of o.split("\n")){if(""===t){n.push("");continue}if($(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 M=!1;export function setLearnAiEnabled(o){M=o}let W=new Set;export function setLearnPqsViewCards(o){W=o}let v=new Map;export function setLearnHonorRanks(o){v=o}const z=["①","②","③","④","⑤","⑥","⑦","⑧","⑨","⑩"];let q=[];export function setLearnReplies(o){q=o}let j=[];export function setLearnFaqs(o){j=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);h(y,l),p();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 f=!!(t.chart_ids&&t.chart_ids.length>0);if(f&&r(t.chart_ids.slice(0,1),l.charts,a()),y.key_point&&""!==y.key_point.trim()?(f||p(),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)),p()}(y.key_point,y.number,l.totalCards)):f||p(),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){x(n[1],!0);const o=n[2]?.trimStart();o&&x(o,!1)}else x(o,!1);p()}),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));p()}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;b(chalk.cyan(" 🤔 "+(o?"请回答 (输 y 或 n):":"Quick comprehension check (type y or n):"))),b(chalk.gray(" "+"─".repeat(20)));for(const o of wrapMixed(n,i()-4))b(" "+chalk.white(o));p()}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(" "),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")),M&&(y.push(chalk.bold.magenta("[/ai]")+chalk.gray(t?" 🐼 熊猫导师":" 🐼 Panda tutor")),y.push(chalk.bold.magenta("[/submit]")+chalk.gray(t?" 提问评分":" prompt score")));const u=v.get(e.number);var h;y.push(chalk.bold.magenta(u?`[/honors ${h=u,z[h-1]??`#${h}`}]`:"[/honors]")+chalk.gray(t?" 排行":" board")),"normal"===g?y.push(chalk.bold.cyan("[wide]")+chalk.gray(t?" 宽屏":" widen")):(y.push("wide"===g?chalk.bold.cyan("[wider]")+chalk.gray(t?" 更宽":" wider"):chalk.bold.cyan("[wide]")+chalk.gray(t?" 窄一点":" narrower")),y.push(chalk.bold.cyan("[normal]")+chalk.gray(t?" 64列":" 64-col")));const m=s(q,j,e.number,t),b=m.startsWith("💬");y.push((b?chalk.bold.yellow("[r]"):chalk.gray("[r]"))+(b?chalk.bold.white(` ${m}`):chalk.gray(` ${m}`))),y.push(chalk.bold.yellow("[e]")+chalk.gray(t?" 提问或报告":" ask or report")),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 p=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>p?(console.log(" "+f),f=e,$=n):(f=f?f+d+e:e,$=f===e?n:$+2+n)}f&&console.log(" "+f),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,p))console.log(chalk.gray(" "+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 S(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 m="-".repeat(48);console.log();for(const o of l(" "))console.log(o);console.log(),console.log(chalk.bold.yellow(" "+m)),console.log(chalk.bold.yellow(" "+(s?`阶段 ${n.number} 完成`:`Phase ${n.number} complete`))+chalk.gray(" · ")+chalk.white(n.name));const b=u>0&&h===u?chalk.bold.green:chalk.bold.white;console.log(chalk.gray(" "+(s?"理解检查得分: ":"comprehension score: "))+b(`${h}/${u}`)+chalk.gray(0===u?s?" (本阶段未作答 y/n)":" (no y/n answered)":""));const p=String(d).length;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=wrapMixed(r,i()-10-p),u=d[0]+(d.length>1?"…":"");console.log(chalk.gray(` ${String(e).padStart(p)} `)+g+chalk.white(" "+u))}if(console.log(chalk.gray(s?" ✓ 答对 · ✗ 答错 · ○ 未作答 · 无 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(" ⭐ "+(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?.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(" "+m));for(const o of S(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 S(n))console.log(o);console.log()}export function renderMCQCard(o,t){const l=c(),r=e(o,l);h(r,t),p(),b(chalk.bold.yellow("🎯 "+r.title)),b(chalk.gray("─".repeat(Math.min(r.title.length+4,i())))),p();for(const o of C(r.question,i()-4))b(" "+o);p();for(const o of["A","B","C","D"])b(chalk.cyan(` ${o}.`)+" "+chalk.white(r.options[o]));p(),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 C(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);h(r,t),p(),b(chalk.bold.yellow("🛠 "+r.title)),b(chalk.gray("─".repeat(Math.min(r.title.length+4,i())))),p();for(const o of C(r.task,i()-4))b(" "+o);if(r.starterCode){p(),b(chalk.gray(" "+n("starter_code",l))),p();for(const o of r.starterCode.split("\n"))b(" "+chalk.cyan(o))}p(),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 C(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);h(r,t),p(),b(chalk.bold.yellow("🎬 "+r.title)),p();for(const o of C(r.description,i()-4))b(" "+o);p(),b(chalk.gray(" "+n("sim_requires",l))),p(),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);h(r,n),p(),b(chalk.bold.cyan("🏟 "+r.title)),b(chalk.gray(" "+r.origin+" · "+r.domain+" · "+r.metric)),p();for(const o of r.statement){for(const e of C(o,i()-4))b(" "+e);p()}b(chalk.bold.green(l?" 核心要点 — baseline → 解法的关键变化":" CORE DELTA — baseline → solution"));for(const o of r.core_delta)for(const e of C("• "+o,i()-6))b(" "+chalk.white(e));p(),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.")),p(),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);h(r,n),p(),b(chalk.bold.yellow("🔜 "+r.title)),p();const a=r.note||(l?"这道竞技场题目正在编写中,敬请期待。":"This arena task is coming soon.");for(const o of C(a,i()-4))b(" "+chalk.gray(o));p(),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 C(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 C(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(o,e){const t=c(),l=e.cardsCompleted.length,r=o.totalCards,a=Object.values(e.mcqResults),s=a.filter(o=>o.correct).length;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(n("status_title",t))+" "+chalk.cyan("─".repeat(25))),p(),b(" "+chalk.gray(n("total_progress",t)+" ")+y(l,r)+chalk.gray(` (${l}/${r})`)),b(" "+chalk.gray(n("streak",t)+" ")+u(e.streakDays)+chalk.gray(` (${n("longest",t)} ${e.longestStreak})`)),b(" "+chalk.gray(n("mcq_accuracy",t)+" ")+chalk.white(`${s}/${a.length}`)),b(" "+chalk.gray(n("practicals_done",t)+" ")+chalk.white(`${e.practicalsCompleted.length}`)),b(" "+chalk.gray(n("bookmarked",t)+" ")+chalk.white(`${e.bookmarks.length}`)),p();const g=n("module",t).replace(":","").trim();for(const n of o.modules){const[o,t]=n.cardRange,l=e.cardsCompleted.filter(e=>e>=o&&e<=t).length,r=t-o+1;b(" "+(l===r?chalk.green("✓"):l>0?chalk.yellow("▶"):chalk.gray("□"))+" "+chalk.gray(`${g} ${n.number}: `)+chalk.white(`${l}/${r}`)+chalk.gray(" "+n.name))}if(p(),e.achievements.length>0){b(" "+chalk.gray(n("achievements",t)));for(const o of e.achievements)b(" "+chalk.bold.yellow("★ ")+chalk.white(o))}else b(" "+chalk.gray(n("achievements_none",t)));p(),m()}
@@ -1 +1 @@
1
- export function sanitizeReplyText(e){return String(e??"").replace(/\u001b\[[0-9;]*[A-Za-z]/g,"").replace(/[\u0000-\u0009\u000b-\u001f\u007f]/g,"")}export function parseReplies(e){if(!Array.isArray(e))return[];const t=[];for(const r of e){if(!r||"object"!=typeof r)continue;const e=r;if("number"==typeof e.id&&"number"==typeof e.card_number&&(t.push({id:e.id,curriculumId:"string"==typeof e.curriculum_id?e.curriculum_id:"",cardNumber:e.card_number,myText:sanitizeReplyText("string"==typeof e.my_text?e.my_text:""),sentAt:"string"==typeof e.sent_at?e.sent_at:"",replyText:"string"==typeof e.reply_text?sanitizeReplyText(e.reply_text):void 0,repliedBy:"string"==typeof e.replied_by?sanitizeReplyText(e.replied_by):void 0,repliedAt:"string"==typeof e.replied_at?e.replied_at:void 0,seen:1===e.seen||!0===e.seen}),t.length>=50))break}return t}export function parseFaqs(e){if(!Array.isArray(e))return[];const t=[];for(const r of e){if(!r||"object"!=typeof r)continue;const e=r;if("number"==typeof e.card_number&&"string"==typeof e.title&&"string"==typeof e.answer&&(t.push({cardNumber:e.card_number,title:sanitizeReplyText(e.title),answer:sanitizeReplyText(e.answer),repliedBy:"string"==typeof e.replied_by?sanitizeReplyText(e.replied_by):void 0,repliedAt:"string"==typeof e.replied_at?e.replied_at:void 0}),t.length>=50))break}return t}export function faqsForCard(e,t){return e.filter(e=>e.cardNumber===t)}export function shortDay(e){const t=Date.parse(e);if(Number.isNaN(t))return"";const r=new Date(t),n=e=>String(e).padStart(2,"0");return`${n(r.getMonth()+1)}-${n(r.getDate())}`}export function replySignature(e,t){const r=t?.trim()?t.trim():"";if(!r)return"";const n=e?Date.parse(e):Number.NaN;return Number.isNaN(n)?`— ${r} 🐼`:`— ${r} 🐼 · ${new Intl.DateTimeFormat("en-AU",{timeZone:"Australia/Sydney",hour:"2-digit",minute:"2-digit",hour12:!1}).format(new Date(n))} Sydney`}export function repliesForCard(e,t){return e.filter(e=>e.cardNumber===t)}export function parseReplyCommand(e){if("r"!==e&&"reply"!==e&&"replies"!==e&&!/^(r|reply|replies)\s+(\d+|all)$/.test(e))return null;const t=e.replace(/^(reply|replies|r)\s*/,"").trim();return"all"===t?{target:"all"}:""===t?{target:"current"}:{target:Number(t)}}export function unreadReplies(e){return e.filter(e=>!!e.replyText&&!e.seen)}export function replyPushLine(e,t){const r=unreadReplies(e);if(0===r.length)return null;const n=Array.from(new Set(r.map(e=>e.cardNumber))).sort((e,t)=>e-t),s=n[0];if(1===n.length){const e=s;return t?`💬 你有 ${r.length} 条回复 — 第 ${e} 张卡 · 输 card ${e} 去看`:`💬 You have ${r.length} ${r.length>1?"replies":"reply"} — Card ${e} · type card ${e}`}const o=n.slice(0,4),i=n.length-o.length,p=o.join(" / ");if(t){const e=i>0?`,还有 ${i} 张未列出`:"";return`💬 你有 ${r.length} 条回复 — 第 ${p} 张卡${e} · 输 card ${s} 去看`}const u=i>0?` / …+${i} more`:"";return`💬 You have ${r.length} replies — Cards ${p}${u} · type card ${s}`}export function replyChipLabel(e,t,r,n){const s=repliesForCard(e,r),o=unreadReplies(s).length;if(o>0)return n?`💬 ${o} 条回复`:`💬 ${o} ${o>1?"replies":"reply"}`;const i=faqsForCard(t,r).length;return i>0?n?`📌 ${i} 个常见问题`:`📌 ${i} ${i>1?"questions":"question"}`:s.length>0?n?`我的报告 (${s.length})`:`my reports (${s.length})`:n?"常见问题":"questions"}function e(e,t,r){const n=[];for(const s of e.split("\n")){let e="";for(const o of s.split(/\s+/).filter(Boolean))e&&e.length+1+o.length>t?(n.push(r+e),e=o):e=e?`${e} ${o}`:o;n.push(r+e)}return n}function t(t,r,n,s,o){const i=[r?"📌 这张卡的常见问题":"📌 Questions on this card",s];if(t.length>0){for(const r of t)i.push(...e(`Q: ${r.title}`,n,"")),i.push(...e(r.answer,n-3," ")),i.push("");return i}return i.push(r?"这里还没有内容。":"Nothing shared here yet."),i.push(""),i.push(...e(r?"有人问出好问题时,答案会贴在这里给所有人看。":"When someone asks a good question about this card, the answer gets posted here for everyone.",n,"")),o&&(i.push(""),i.push(...e(r?"按 e 可以就这张卡提问,或报告问题。":"Press e to ask about this card, or to report a problem.",n,"")),i.push(r?"不是每条都会有回复,但每条都有人看。":"Not every report gets a reply — but every one is read.")),i}export function renderReplyLines(r,n,s,o,i){const p=Math.max(32,Math.min(i,96)),u="-".repeat(p);if("all"===s){if(0===r.length)return o?["你还没在这门课报过问题。按 e 可以报告本卡的错误或提问。"]:["You have not reported anything on this course yet. Press e to report a problem or ask about this card."];const t=[o?"💬 我的全部报告":"💬 All your reports",u];for(const n of r){const r=o?`第 ${n.cardNumber} 张卡 · `:`Card ${n.cardNumber} · `;if(n.replyText){t.push(o?`${r}已回复 · ${shortDay(n.repliedAt??"")} · ${n.repliedBy??""}`:`${r}replied · ${shortDay(n.repliedAt??"")} · ${n.repliedBy??""}`),t.push(o?` 你写的(${shortDay(n.sentAt)}):`:` you wrote (${shortDay(n.sentAt)}):`),t.push(...e(n.myText,p-6," ")),t.push(...e(`-> ${n.replyText}`,p-4," "));const s=replySignature(n.repliedAt,n.repliedBy);s&&t.push(` ${s}`)}else t.push(o?`${r}待回复 · ${shortDay(n.sentAt)} 已送达`:`${r}waiting · sent ${shortDay(n.sentAt)}`),t.push(o?" 你写的:":" you wrote:"),t.push(...e(n.myText,p-6," "));t.push("")}return t.push(u),t.push(o?"不是每条都会有回复,但每条都有人看。":"Not every report gets a reply — but every one is read."),t.push(o?"e = 在这张卡再报一条":"e = report something else on this card"),t}const a=repliesForCard(r,s),l=faqsForCard(n,s);if(0===a.length)return t(l,o,p,u,!0);const y=[o?`💬 第 ${s} 张卡 — 我的报告`:`💬 Card ${s} — your reports`,u],h=[...a].reverse();for(const t of h){if(t.replyText){y.push(o?`已回复 · ${shortDay(t.repliedAt??"")} · ${t.repliedBy??""}`:`replied · ${shortDay(t.repliedAt??"")} · ${t.repliedBy??""}`),y.push(o?` 你写的(${shortDay(t.sentAt)}):`:` you wrote (${shortDay(t.sentAt)}):`),y.push(...e(t.myText,p-6," ")),y.push(...e(`-> ${t.replyText}`,p-4," "));const r=replySignature(t.repliedAt,t.repliedBy);r&&y.push(` ${r}`)}else y.push(o?`待回复 · ${shortDay(t.sentAt)} 已送达`:`waiting · sent ${shortDay(t.sentAt)}`),y.push(o?" 你写的:":" you wrote:"),y.push(...e(t.myText,p-6," "));y.push("")}return y.push(u),y.push(o?"不是每条都会有回复,但每条都有人看。":"Not every report gets a reply — but every one is read."),y.push(o?"e = 在这张卡再报一条":"e = report something else on this card"),y.push(""),y.push(...t(l,o,p,u,!1)),y}
1
+ export function sanitizeReplyText(e){return String(e??"").replace(/\u001b\[[0-9;]*[A-Za-z]/g,"").replace(/[\u0000-\u0009\u000b-\u001f\u007f]/g,"")}export function parseReplies(e){if(!Array.isArray(e))return[];const t=[];for(const r of e){if(!r||"object"!=typeof r)continue;const e=r;if("number"==typeof e.id&&"number"==typeof e.card_number&&(t.push({id:e.id,curriculumId:"string"==typeof e.curriculum_id?e.curriculum_id:"",cardNumber:e.card_number,myText:sanitizeReplyText("string"==typeof e.my_text?e.my_text:""),sentAt:"string"==typeof e.sent_at?e.sent_at:"",replyText:"string"==typeof e.reply_text?sanitizeReplyText(e.reply_text):void 0,repliedBy:"string"==typeof e.replied_by?sanitizeReplyText(e.replied_by):void 0,repliedAt:"string"==typeof e.replied_at?e.replied_at:void 0,seen:1===e.seen||!0===e.seen}),t.length>=50))break}return t}export function parseFaqs(e){if(!Array.isArray(e))return[];const t=[];for(const r of e){if(!r||"object"!=typeof r)continue;const e=r;if("number"==typeof e.card_number&&"string"==typeof e.title&&"string"==typeof e.answer&&(t.push({cardNumber:e.card_number,title:sanitizeReplyText(e.title),answer:sanitizeReplyText(e.answer),repliedBy:"string"==typeof e.replied_by?sanitizeReplyText(e.replied_by):void 0,repliedAt:"string"==typeof e.replied_at?e.replied_at:void 0}),t.length>=50))break}return t}export function faqsForCard(e,t){return e.filter(e=>e.cardNumber===t)}export function shortDay(e){const t=Date.parse(e);if(Number.isNaN(t))return"";const r=new Date(t),n=e=>String(e).padStart(2,"0");return`${n(r.getMonth()+1)}-${n(r.getDate())}`}export function replySignature(e,t){const r=t?.trim()?t.trim():"";if(!r)return"";const n=e?Date.parse(e):Number.NaN;return Number.isNaN(n)?`— ${r} 🐼`:`— ${r} 🐼 · ${new Intl.DateTimeFormat("en-AU",{timeZone:"Australia/Sydney",hour:"2-digit",minute:"2-digit",hour12:!1}).format(new Date(n))} Sydney`}export function repliesForCard(e,t){return e.filter(e=>e.cardNumber===t)}export function parseReplyCommand(e){if("r"!==e&&"reply"!==e&&"replies"!==e&&!/^(r|reply|replies)\s+(\d+|all)$/.test(e))return null;const t=e.replace(/^(reply|replies|r)\s*/,"").trim();return"all"===t?{target:"all"}:""===t?{target:"current"}:{target:Number(t)}}export function unreadReplies(e){return e.filter(e=>!!e.replyText&&!e.seen)}export function replyPushLine(e,t){const r=unreadReplies(e);if(0===r.length)return null;const n=Array.from(new Set(r.map(e=>e.cardNumber))).sort((e,t)=>e-t),s=n[0];if(1===n.length){const e=s;return t?`💬 你有 ${r.length} 条回复 — 第 ${e} 张卡 · 输 card ${e} 去看`:`💬 You have ${r.length} ${r.length>1?"replies":"reply"} — Card ${e} · type card ${e}`}const o=n.slice(0,4),i=n.length-o.length,p=o.join(" / ");if(t){const e=i>0?`,还有 ${i} 张未列出`:"";return`💬 你有 ${r.length} 条回复 — 第 ${p} 张卡${e} · 输 card ${s} 去看`}const u=i>0?` / …+${i} more`:"";return`💬 You have ${r.length} replies — Cards ${p}${u} · type card ${s}`}export function replyChipLabel(e,t,r,n){const s=repliesForCard(e,r),o=unreadReplies(s).length;if(o>0)return n?`💬 ${o} 条回复`:`💬 ${o} ${o>1?"replies":"reply"}`;const i=faqsForCard(t,r).length;return i>0?n?`📌 ${i} 个常见问题`:`📌 ${i} ${i>1?"questions":"question"}`:s.length>0?n?`我的报告 (${s.length})`:`my reports (${s.length})`:n?"常见问题":"questions"}function e(e,t,r){const n=[];for(const s of e.split("\n")){let e="";for(const o of s.split(/\s+/).filter(Boolean))e&&e.length+1+o.length>t?(n.push(r+e),e=o):e=e?`${e} ${o}`:o;n.push(r+e)}return n}function t(t,r,n,s,o){const i=[r?"📌 这张卡的常见问题":"📌 Questions on this card",s];if(t.length>0){for(const r of t)i.push(...e(`Q: ${r.title}`,n,"")),i.push(...e(r.answer,n-3," ")),i.push("");return i}return i.push(r?"这里还没有内容。":"Nothing shared here yet."),i.push(""),i.push(...e(r?"有人问出好问题时,答案会贴在这里给所有人看。":"When someone asks a good question about this card, the answer gets posted here for everyone.",n,"")),o&&(i.push(""),i.push(...e(r?"按 e 可以就这张卡提问,或报告问题。":"Press e to ask about this card, or to report a problem.",n,"")),i.push(r?"不是每条都会有回复,但每条都有人看。":"Not every report gets a reply — but every one is read.")),i}export function renderReplyLines(r,n,s,o,i){const p=Math.max(32,Math.min(i,96)),u="-".repeat(p);if("all"===s){if(0===r.length)return o?["你还没在这门课报过问题。按 e 可以报告本卡的错误或提问。"]:["You have not reported anything on this course yet. Press e to report a problem or ask about this card."];const t=[o?"💬 我的全部报告":"💬 All your reports",u];for(const n of r){const r=o?`第 ${n.cardNumber} 张卡 · `:`Card ${n.cardNumber} · `;if(n.replyText){t.push(o?`${r}已回复 · ${shortDay(n.repliedAt??"")} · ${n.repliedBy??""}`:`${r}replied · ${shortDay(n.repliedAt??"")} · ${n.repliedBy??""}`),t.push(o?` 你写的(${shortDay(n.sentAt)}):`:` you wrote (${shortDay(n.sentAt)}):`),t.push(...e(n.myText,p-6," ")),t.push(...e(`-> ${n.replyText}`,p-4," "));const s=replySignature(n.repliedAt,n.repliedBy);s&&t.push(` ${s}`)}else t.push(o?`${r}待回复 · ${shortDay(n.sentAt)} 已送达`:`${r}waiting · sent ${shortDay(n.sentAt)}`),t.push(o?" 你写的:":" you wrote:"),t.push(...e(n.myText,p-6," "));t.push("")}return t.push(u),t.push(o?"不是每条都会有回复,但每条都有人看。":"Not every report gets a reply — but every one is read."),t.push(o?"e = 在这张卡再提问或报告一条":"e = ask or report something else on this card"),t}const a=repliesForCard(r,s),l=faqsForCard(n,s);if(0===a.length)return t(l,o,p,u,!0);const y=[o?`💬 第 ${s} 张卡 — 我的报告`:`💬 Card ${s} — your reports`,u],h=[...a].reverse();for(const t of h){if(t.replyText){y.push(o?`已回复 · ${shortDay(t.repliedAt??"")} · ${t.repliedBy??""}`:`replied · ${shortDay(t.repliedAt??"")} · ${t.repliedBy??""}`),y.push(o?` 你写的(${shortDay(t.sentAt)}):`:` you wrote (${shortDay(t.sentAt)}):`),y.push(...e(t.myText,p-6," ")),y.push(...e(`-> ${t.replyText}`,p-4," "));const r=replySignature(t.repliedAt,t.repliedBy);r&&y.push(` ${r}`)}else y.push(o?`待回复 · ${shortDay(t.sentAt)} 已送达`:`waiting · sent ${shortDay(t.sentAt)}`),y.push(o?" 你写的:":" you wrote:"),y.push(...e(t.myText,p-6," "));y.push("")}return y.push(u),y.push(o?"不是每条都会有回复,但每条都有人看。":"Not every report gets a reply — but every one is read."),y.push(o?"e = 在这张卡再提问或报告一条":"e = ask or report something else on this card"),y.push(""),y.push(...t(l,o,p,u,!1)),y}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.461",
3
+ "version": "2.19.463",
4
4
  "description": "ICOA CLI — The world's first CLI-native cyber & AI security olympiad terminal: AI4CTF (Day 1), CTF4AI (Day 2), VLA4CTF (Pioneer Round — embodied AI)",
5
5
  "type": "module",
6
6
  "bin": {