qwerty-cli 0.0.1-alpha.19 → 0.0.1-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ConfigEditor-YG5K2WT2.js +2 -0
- package/dist/{ConfigEditor-YYZQ7TUJ.js.map → ConfigEditor-YG5K2WT2.js.map} +1 -1
- package/dist/PracticeScreen-BRPEUQJ6.js +2 -0
- package/dist/PracticeScreen-BRPEUQJ6.js.map +1 -0
- package/dist/StatsViewer-43ATH3OG.js +2 -0
- package/dist/{StatsViewer-JIFWFPDL.js.map → StatsViewer-43ATH3OG.js.map} +1 -1
- package/dist/WordLookup-LQMQVORJ.js +2 -0
- package/dist/WordLookup-LQMQVORJ.js.map +1 -0
- package/dist/chunk-MHLVBOJU.js +3 -0
- package/dist/chunk-MHLVBOJU.js.map +1 -0
- package/dist/chunk-UEJCQKZ2.js +2 -0
- package/dist/chunk-UEJCQKZ2.js.map +1 -0
- package/dist/chunk-WRO5XX35.js +2 -0
- package/dist/chunk-WRO5XX35.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/menu.impl-6DPHV36B.js +2 -0
- package/dist/{menu.impl-ZUAMXKFD.js.map → menu.impl-6DPHV36B.js.map} +1 -1
- package/dist/practice.impl-HILWI6GI.js +2 -0
- package/dist/{practice.impl-2PPCBBK3.js.map → practice.impl-HILWI6GI.js.map} +1 -1
- package/dist/word.impl-VVSIGCKS.js +2 -0
- package/dist/word.impl-VVSIGCKS.js.map +1 -0
- package/package.json +1 -1
- package/dist/ConfigEditor-YYZQ7TUJ.js +0 -2
- package/dist/PracticeScreen-PXPBME6H.js +0 -2
- package/dist/PracticeScreen-PXPBME6H.js.map +0 -1
- package/dist/StatsViewer-JIFWFPDL.js +0 -2
- package/dist/WordLookup-6JFXQYSQ.js +0 -2
- package/dist/WordLookup-6JFXQYSQ.js.map +0 -1
- package/dist/chunk-QG7ZTS2G.js +0 -2
- package/dist/chunk-QG7ZTS2G.js.map +0 -1
- package/dist/chunk-RR77GWZ3.js +0 -3
- package/dist/chunk-RR77GWZ3.js.map +0 -1
- package/dist/menu.impl-ZUAMXKFD.js +0 -2
- package/dist/practice.impl-2PPCBBK3.js +0 -2
- package/dist/word.impl-EJNGBD7U.js +0 -2
- package/dist/word.impl-EJNGBD7U.js.map +0 -1
package/dist/chunk-RR77GWZ3.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{a as L,b as R,c as I}from"./chunk-6ROGUGNX.js";import{a as C,b as k}from"./chunk-MFGIEKBU.js";import{b as $,e as z}from"./chunk-GULN5HRV.js";import{a as B,b as D,c as w,e as H}from"./chunk-QG7ZTS2G.js";import{a as P,b as M,c as E,d as N,f as a}from"./chunk-VIOZNKSK.js";var _="\x1B[?1049h\x1B[?25l\x1B[2J\x1B[H",F="\x1B[?25h\x1B[?1049l",v=!1,W=!1;function O(){if(W)return;W=!0;let e=()=>{if(v){try{process.stdout.write(F)}catch{}v=!1}};process.once("exit",e),process.once("SIGINT",()=>{e(),process.exit(130)}),process.once("SIGTERM",()=>{e(),process.exit(143)})}function Y(){v||process.stdout.isTTY&&process.env.QWERTY_NO_ALTSCREEN!=="1"&&(O(),process.stdout.write(_),v=!0)}function A(){if(v){try{process.stdout.write(F)}catch{}v=!1}}import{Suspense as ne,lazy as T,useRef as ie}from"react";import{Box as se,Text as ae,useApp as ce,useInput as ue}from"ink";import{useEffect as Q,useState as X}from"react";import{Box as U,useStdout as Z}from"ink";import{jsx as ee}from"react/jsx-runtime";function j({children:e}){let{stdout:t}=Z(),[s,r]=X(()=>({rows:t?.rows??24,cols:t?.columns??80}));return Q(()=>{Y();let n=()=>{r({rows:process.stdout.rows??24,cols:process.stdout.columns??80})};return process.stdout.on("resize",n),()=>{process.stdout.off("resize",n),A()}},[]),ee(U,{width:s.cols,height:s.rows,flexDirection:"column",children:e})}import{useState as te}from"react";import{Box as y,Text as f,useApp as re,useInput as oe}from"ink";import{jsx as g,jsxs as x}from"react/jsx-runtime";function q({cfg:e}){let[t,s]=te(0),{exit:r}=re(),n=M(),h=R(),p=N(),l=D(e.defaultDict),o=p.mainMenu.items,S=async c=>{if(!e.defaultDict){n.navigate({name:"dict",params:{pickerMode:"choose-then-practice"}});return}let m=z(await $(),e.defaultDict);if(c){I({type:"stealth",dictId:e.defaultDict,chapterIndex:m,mode:e.defaultMode}),r();return}n.navigate({name:"practice",params:{dictId:e.defaultDict,chapterIndex:m,mode:e.defaultMode,stealth:c}})},d=[{key:"p",label:o.practiceLabel,hint:e.defaultDict?o.practiceHintWith(H(l,24)):o.practiceHintNone,run:()=>{S(e.stealth==="default")}}];(e.stealth==="menu"||e.stealth==="default")&&d.push({key:"b",label:o.stealthLabel,hint:o.stealthHint,run:()=>{S(!0)}}),d.push({key:"d",label:o.dictLabel,hint:o.dictHint,run:()=>n.navigate({name:"dict"})},{key:"w",label:o.wordLabel,hint:o.wordHint,run:()=>n.navigate({name:"word"})},{key:"s",label:o.statsLabel,hint:o.statsHint,run:()=>n.navigate({name:"stats"})},{key:"c",label:o.configLabel,hint:o.configHint,run:()=>n.navigate({name:"config"})},{key:"q",label:o.quitLabel,hint:o.quitHint,run:()=>r()});let J=Math.max(...d.map(c=>w(c.label)))+4;return oe((c,m)=>{if(m.escape){r();return}if(m.upArrow&&s(u=>(u-1+d.length)%d.length),m.downArrow&&s(u=>(u+1)%d.length),m.return){d[t].run();return}if(c==="?"){n.navigate({name:"help"});return}for(let u of d)if(c===u.key){u.run();return}}),x(y,{flexDirection:"column",paddingX:2,paddingY:1,width:"100%",children:[e.stealth!=="default"&&x(y,{children:[g(f,{bold:!0,color:a.accent,children:p.app.title}),x(f,{color:a.muted,children:[" \xB7 ",p.app.subtitle]})]}),g(y,{marginTop:e.stealth==="default"?0:2,flexDirection:"column",children:d.map((c,m)=>{let u=m===t,K=" ".repeat(Math.max(0,J-w(c.label)));return x(y,{children:[g(f,{color:u?a.accent:a.muted,children:u?"\u258C ":" "}),x(f,{color:u?a.accent:a.muted,children:["[",c.key,"]"]}),g(f,{children:" "}),x(f,{bold:u,color:u?a.text:a.muted,children:[c.label,K]}),g(f,{color:a.muted,children:c.hint})]},c.key)})}),g(y,{marginTop:2,children:x(f,{color:a.muted,children:[p.mainMenu.hint," \xB7 ",p.mainMenu.helpHint]})}),h.warning&&g(y,{marginTop:1,children:g(f,{color:a.warning,children:p.audio.noPlayer})})]})}import{jsx as i}from"react/jsx-runtime";var pe=T(()=>import("./PracticeScreen-PXPBME6H.js").then(e=>({default:e.PracticeScreen}))),le=T(()=>import("./DictBrowser-I6AEYY2I.js").then(e=>({default:e.DictBrowser}))),de=T(()=>import("./ConfigEditor-YYZQ7TUJ.js").then(e=>({default:e.ConfigEditor}))),me=T(()=>import("./StatsViewer-JIFWFPDL.js").then(e=>({default:e.StatsViewer}))),fe=T(()=>import("./WordLookup-6JFXQYSQ.js").then(e=>({default:e.WordLookup}))),he=T(()=>import("./HelpScreen-IKHDZGTG.js").then(e=>({default:e.HelpScreen})));function ge(){return i(se,{alignItems:"center",justifyContent:"center",width:"100%",height:"100%",children:i(ae,{color:a.muted,children:"\u2026"})})}function Xe({initial:e,initialCfg:t,inline:s=!1}){return i(C,{initialCfg:t,children:i(xe,{children:i(B,{children:i(L,{disabled:!t.sounds.master,children:i(P,{initial:e,children:s?i(V,{inline:!0}):i(j,{children:i(V,{})})})})})})})}function xe({children:e}){let{cfg:t}=k();return i(E,{pref:t.language,children:e})}function be(e){if(e.name==="practice"){let t=e.params;return`practice:${t.dictId}:${t.chapterIndex}:${t.mode}:${t.stealth?"s":"n"}`}return e.name}function V({inline:e=!1}){let t=M(),{cfg:s}=k(),{exit:r}=ce(),n=ie(null);ue((o,S)=>{S.ctrl&&o==="c"&&r()});let h=t.current,p=be(h);n.current!==p&&(!e&&process.stdout.isTTY&&process.stdout.write("\x1B[2J\x1B[H"),n.current=p);let l=(()=>{switch(h.name){case"main":return i(q,{cfg:s});case"practice":return i(pe,{params:h.params});case"dict":return i(le,{params:h.params});case"config":return i(de,{});case"stats":return i(me,{});case"word":return i(fe,{});case"help":return i(he,{})}})();return i(ne,{fallback:i(ge,{}),children:l})}import b from"chalk";import Se from"boxen";function nt(){A()}function G(e,t){let s=Math.floor(e/1e3),r=Math.floor(s/60),n=s%60;return t==="zh"?r===0?`${n} \u79D2`:`${r} \u5206 ${n} \u79D2`:r===0?`${n}s`:`${r}m ${n}s`}function we(e){return e>=90?b.green:e<75?b.dim:t=>t}function ve(e,t,s){let r=[],n=[t.report.duration];e.chaptersCompleted===0?n.push(t.report.notPracticed):(n.push(t.report.practiced,t.report.chapters,t.report.words,t.report.accuracy,t.report.wpm),e.newMistakeWords>0&&n.push(t.report.newMistakes));let h=Math.max(...n.map(w)),p=o=>o+" ".repeat(Math.max(0,h-w(o))),l=(o,S)=>`${b.dim(p(o))} ${S}`;if(r.push(l(t.report.duration,G(e.totalDurationMs,s))),e.chaptersCompleted===0)r.push(b.dim(t.report.notPracticed));else{r.push(l(t.report.practiced,G(e.practiceMs,s))),r.push(l(t.report.chapters,String(e.chaptersCompleted))),r.push(l(t.report.words,String(e.wordCount)));let o=Math.round(e.accuracy*1e3)/10;r.push(l(t.report.accuracy,we(o)(`${o}%`))),r.push(l(t.report.wpm,String(e.wpm))),e.newMistakeWords>0&&r.push(l(t.report.newMistakes,b.red(String(e.newMistakeWords))))}return r.push(""),r.push(b.dim.italic(t.report.farewell)),r}function it(e,t,s){if(e.startedAt===null&&e.chaptersCompleted===0)return;let r=ve(e,t,s).join(`
|
|
2
|
-
`);console.log(Se(r,{title:b.bold.cyan(t.report.title),titleAlignment:"left",borderStyle:"round",borderColor:"gray",padding:{top:0,bottom:0,left:3,right:3},margin:{top:1,bottom:1,left:2,right:0}}))}export{Y as a,Xe as b,nt as c,it as d};
|
|
3
|
-
//# sourceMappingURL=chunk-RR77GWZ3.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/util/altscreen.ts","../src/ui/App.tsx","../src/ui/Fullscreen.tsx","../src/ui/screens/MainMenu.tsx","../src/util/report.ts"],"sourcesContent":["// Shared alt-screen state. Both the command entry point (which writes\n// ENTER before render() to avoid Ink's first frame leaking onto the main\n// screen) and Fullscreen.tsx (which used to write ENTER inside useEffect)\n// route through this module so enter/leave are idempotent.\n\nconst ENTER = '\\x1b[?1049h\\x1b[?25l\\x1b[2J\\x1b[H';\nconst LEAVE = '\\x1b[?25h\\x1b[?1049l';\n\nlet active = false;\nlet signalsRegistered = false;\n\nfunction ensureSignals(): void {\n if (signalsRegistered) return;\n signalsRegistered = true;\n const cleanup = () => {\n if (!active) return;\n try {\n process.stdout.write(LEAVE);\n } catch {\n // stream may be closed\n }\n active = false;\n };\n process.once('exit', cleanup);\n process.once('SIGINT', () => {\n cleanup();\n process.exit(130);\n });\n process.once('SIGTERM', () => {\n cleanup();\n process.exit(143);\n });\n}\n\nexport function enterAltScreen(): void {\n if (active) return;\n if (!process.stdout.isTTY) return;\n if (process.env.QWERTY_NO_ALTSCREEN === '1') return;\n ensureSignals();\n process.stdout.write(ENTER);\n active = true;\n}\n\nexport function leaveAltScreen(): void {\n if (!active) return;\n try {\n process.stdout.write(LEAVE);\n } catch {\n // stream may be closed\n }\n active = false;\n}\n\nexport function isAltScreenActive(): boolean {\n return active;\n}\n","import { Suspense, lazy, useRef, type ReactNode } from 'react';\nimport { Box, Text, useApp, useInput } from 'ink';\nimport { NavProvider, useNav, type ScreenFrame } from './nav.js';\nimport { Fullscreen } from './Fullscreen.js';\nimport { AudioStatusProvider } from './audio-context.js';\nimport { AppStateProvider, useAppState } from './app-state.js';\nimport { StringsProvider } from '../i18n/context.js';\nimport { RegistryProvider } from './registry-context.js';\nimport { MainMenu } from './screens/MainMenu.js';\nimport { PALETTE } from './components/BigWord.js';\nimport type { Config } from '../infra/config-store.js';\n\n// MainMenu stays eager — it's the initial screen for `qwerty` (no args) and\n// must render instantly. All other screens are lazy so their module graphs\n// (DictBrowser pulls registry helpers, StatsViewer pulls stats domain, etc.)\n// only load when the user navigates to them.\nconst PracticeScreen = lazy(() =>\n import('./screens/PracticeScreen.js').then((m) => ({ default: m.PracticeScreen })),\n);\nconst DictBrowser = lazy(() =>\n import('./screens/DictBrowser.js').then((m) => ({ default: m.DictBrowser })),\n);\nconst ConfigEditor = lazy(() =>\n import('./screens/ConfigEditor.js').then((m) => ({ default: m.ConfigEditor })),\n);\nconst StatsViewer = lazy(() =>\n import('./screens/StatsViewer.js').then((m) => ({ default: m.StatsViewer })),\n);\nconst WordLookup = lazy(() =>\n import('./screens/WordLookup.js').then((m) => ({ default: m.WordLookup })),\n);\nconst HelpScreen = lazy(() =>\n import('./screens/HelpScreen.js').then((m) => ({ default: m.HelpScreen })),\n);\n\nfunction LazyFallback() {\n return (\n <Box alignItems=\"center\" justifyContent=\"center\" width=\"100%\" height=\"100%\">\n <Text color={PALETTE.muted}>…</Text>\n </Box>\n );\n}\n\nexport function App({\n initial,\n initialCfg,\n inline = false,\n}: {\n initial: ScreenFrame;\n initialCfg: Config;\n inline?: boolean;\n}) {\n return (\n <AppStateProvider initialCfg={initialCfg}>\n <LangBridge>\n <RegistryProvider>\n <AudioStatusProvider disabled={!initialCfg.sounds.master}>\n <NavProvider initial={initial}>\n {inline ? <Router inline /> : <Fullscreen><Router /></Fullscreen>}\n </NavProvider>\n </AudioStatusProvider>\n </RegistryProvider>\n </LangBridge>\n </AppStateProvider>\n );\n}\n\nfunction LangBridge({ children }: { children: ReactNode }) {\n const { cfg } = useAppState();\n return <StringsProvider pref={cfg.language}>{children}</StringsProvider>;\n}\n\nfunction screenKey(frame: ScreenFrame): string {\n if (frame.name === 'practice') {\n const p = frame.params;\n return `practice:${p.dictId}:${p.chapterIndex}:${p.mode}:${p.stealth ? 's' : 'n'}`;\n }\n return frame.name;\n}\n\nfunction Router({ inline = false }: { inline?: boolean }) {\n const nav = useNav();\n const { cfg } = useAppState();\n const { exit } = useApp();\n const lastKeyRef = useRef<string | null>(null);\n\n useInput((input, key) => {\n if (key.ctrl && input === 'c') exit();\n });\n\n const frame = nav.current;\n const key = screenKey(frame);\n if (lastKeyRef.current !== key) {\n if (!inline && process.stdout.isTTY) process.stdout.write('\\x1b[2J\\x1b[H');\n lastKeyRef.current = key;\n }\n\n const screen = (() => {\n switch (frame.name) {\n case 'main':\n return <MainMenu cfg={cfg} />;\n case 'practice':\n return <PracticeScreen params={frame.params} />;\n case 'dict':\n return <DictBrowser params={frame.params} />;\n case 'config':\n return <ConfigEditor />;\n case 'stats':\n return <StatsViewer />;\n case 'word':\n return <WordLookup />;\n case 'help':\n return <HelpScreen />;\n }\n })();\n\n return <Suspense fallback={<LazyFallback />}>{screen}</Suspense>;\n}\n","import { useEffect, useState, type ReactNode } from 'react';\nimport { Box, useStdout } from 'ink';\nimport { enterAltScreen, leaveAltScreen } from '../util/altscreen.js';\n\nexport function Fullscreen({ children }: { children: ReactNode }) {\n const { stdout } = useStdout();\n const [size, setSize] = useState(() => ({\n rows: stdout?.rows ?? 24,\n cols: stdout?.columns ?? 80,\n }));\n\n useEffect(() => {\n // enterAltScreen is idempotent — if the command entry already wrote the\n // ENTER sequence pre-render (the normal path for menu / non-stealth\n // practice), this is a no-op. Acts as a safety net for any code path\n // that mounts the App without pre-render alt-screen setup.\n enterAltScreen();\n\n const onResize = () => {\n setSize({ rows: process.stdout.rows ?? 24, cols: process.stdout.columns ?? 80 });\n };\n process.stdout.on('resize', onResize);\n\n return () => {\n process.stdout.off('resize', onResize);\n leaveAltScreen();\n };\n }, []);\n\n return (\n <Box width={size.cols} height={size.rows} flexDirection=\"column\">\n {children}\n </Box>\n );\n}\n","import { useState } from 'react';\nimport { Box, Text, useApp, useInput } from 'ink';\nimport { useNav } from '../nav.js';\nimport { useAudioStatus } from '../audio-context.js';\nimport { useStrings } from '../../i18n/context.js';\nimport { useDictName } from '../registry-context.js';\nimport { PALETTE } from '../components/BigWord.js';\nimport { visibleWidth } from '../../util/text.js';\nimport { truncateName } from '../../util/dict-name.js';\nimport { setPostExitAction } from '../../util/post-exit-action.js';\nimport { loadSessions, resumeChapterFor } from '../../domain/stats.js';\nimport type { Config } from '../../infra/config-store.js';\n\ntype Item = { key: string; label: string; hint: string; run: () => void };\n\nexport function MainMenu({ cfg }: { cfg: Config }) {\n const [selected, setSelected] = useState(0);\n const { exit } = useApp();\n const nav = useNav();\n const audio = useAudioStatus();\n const t = useStrings();\n const defaultDictName = useDictName(cfg.defaultDict);\n\n const m = t.mainMenu.items;\n const startPractice = async (stealth: boolean) => {\n if (!cfg.defaultDict) {\n nav.navigate({ name: 'dict', params: { pickerMode: 'choose-then-practice' } });\n return;\n }\n const chapterIndex = resumeChapterFor(await loadSessions(), cfg.defaultDict);\n // Stealth needs Ink rendered in inline mode (no alt-screen), but the menu\n // is currently running inside an alt-screen render(). Hand the dict +\n // mode off via a single-shot store and exit Ink; the CLI entry point\n // consumes the action and re-renders inline.\n if (stealth) {\n setPostExitAction({\n type: 'stealth',\n dictId: cfg.defaultDict,\n chapterIndex,\n mode: cfg.defaultMode,\n });\n exit();\n return;\n }\n nav.navigate({\n name: 'practice',\n params: {\n dictId: cfg.defaultDict,\n chapterIndex,\n mode: cfg.defaultMode,\n stealth,\n },\n });\n };\n\n const items: Item[] = [\n {\n key: 'p',\n label: m.practiceLabel,\n hint: cfg.defaultDict\n ? m.practiceHintWith(truncateName(defaultDictName, 24))\n : m.practiceHintNone,\n run: () => void startPractice(cfg.stealth === 'default'),\n },\n ];\n\n if (cfg.stealth === 'menu' || cfg.stealth === 'default') {\n items.push({\n key: 'b',\n label: m.stealthLabel,\n hint: m.stealthHint,\n run: () => void startPractice(true),\n });\n }\n\n items.push(\n { key: 'd', label: m.dictLabel, hint: m.dictHint, run: () => nav.navigate({ name: 'dict' }) },\n { key: 'w', label: m.wordLabel, hint: m.wordHint, run: () => nav.navigate({ name: 'word' }) },\n { key: 's', label: m.statsLabel, hint: m.statsHint, run: () => nav.navigate({ name: 'stats' }) },\n { key: 'c', label: m.configLabel, hint: m.configHint, run: () => nav.navigate({ name: 'config' }) },\n { key: 'q', label: m.quitLabel, hint: m.quitHint, run: () => exit() },\n );\n\n const labelW = Math.max(...items.map((it) => visibleWidth(it.label))) + 4;\n\n useInput((input, key) => {\n if (key.escape) {\n exit();\n return;\n }\n if (key.upArrow) setSelected((i) => (i - 1 + items.length) % items.length);\n if (key.downArrow) setSelected((i) => (i + 1) % items.length);\n if (key.return) {\n items[selected]!.run();\n return;\n }\n if (input === '?') {\n nav.navigate({ name: 'help' });\n return;\n }\n for (const it of items) {\n if (input === it.key) {\n it.run();\n return;\n }\n }\n });\n\n return (\n <Box flexDirection=\"column\" paddingX={2} paddingY={1} width=\"100%\">\n {cfg.stealth !== 'default' && (\n <Box>\n <Text bold color={PALETTE.accent}>\n {t.app.title}\n </Text>\n <Text color={PALETTE.muted}> · {t.app.subtitle}</Text>\n </Box>\n )}\n\n <Box marginTop={cfg.stealth === 'default' ? 0 : 2} flexDirection=\"column\">\n {items.map((it, i) => {\n const active = i === selected;\n const pad = ' '.repeat(Math.max(0, labelW - visibleWidth(it.label)));\n return (\n <Box key={it.key}>\n <Text color={active ? PALETTE.accent : PALETTE.muted}>{active ? '▌ ' : ' '}</Text>\n <Text color={active ? PALETTE.accent : PALETTE.muted}>[{it.key}]</Text>\n <Text>{' '}</Text>\n <Text bold={active} color={active ? PALETTE.text : PALETTE.muted}>\n {it.label}\n {pad}\n </Text>\n <Text color={PALETTE.muted}>{it.hint}</Text>\n </Box>\n );\n })}\n </Box>\n\n <Box marginTop={2}>\n <Text color={PALETTE.muted}>\n {t.mainMenu.hint}\n {' · '}\n {t.mainMenu.helpHint}\n </Text>\n </Box>\n\n {audio.warning && (\n <Box marginTop={1}>\n <Text color={PALETTE.warning}>{t.audio.noPlayer}</Text>\n </Box>\n )}\n </Box>\n );\n}\n","import chalk from 'chalk';\nimport boxen from 'boxen';\nimport type { SessionReport } from '../infra/session-tracker.js';\nimport type { Strings } from '../i18n/strings.js';\nimport { leaveAltScreen } from './altscreen.js';\nimport { visibleWidth } from './text.js';\n\nexport function ensureMainScreen(): void {\n leaveAltScreen();\n}\n\nfunction fmtDuration(ms: number, lang: 'zh' | 'en'): string {\n const total = Math.floor(ms / 1000);\n const m = Math.floor(total / 60);\n const s = total % 60;\n if (lang === 'zh') {\n if (m === 0) return `${s} 秒`;\n return `${m} 分 ${s} 秒`;\n }\n if (m === 0) return `${s}s`;\n return `${m}m ${s}s`;\n}\n\nfunction accColor(pct: number): (s: string) => string {\n if (pct >= 90) return chalk.green;\n if (pct < 75) return chalk.dim;\n return (s) => s;\n}\n\nfunction buildLines(r: SessionReport, t: Strings, lang: 'zh' | 'en'): string[] {\n const lines: string[] = [];\n\n // Align labels in a single visual column. Use the longest label's\n // visible width as the target (CJK counted at 2 cols by visibleWidth).\n const allLabels: string[] = [t.report.duration];\n if (r.chaptersCompleted === 0) {\n allLabels.push(t.report.notPracticed);\n } else {\n allLabels.push(\n t.report.practiced,\n t.report.chapters,\n t.report.words,\n t.report.accuracy,\n t.report.wpm,\n );\n if (r.newMistakeWords > 0) allLabels.push(t.report.newMistakes);\n }\n const labelW = Math.max(...allLabels.map(visibleWidth));\n const padLabel = (s: string) =>\n s + ' '.repeat(Math.max(0, labelW - visibleWidth(s)));\n const row = (label: string, value: string) =>\n `${chalk.dim(padLabel(label))} ${value}`;\n\n lines.push(row(t.report.duration, fmtDuration(r.totalDurationMs, lang)));\n\n if (r.chaptersCompleted === 0) {\n lines.push(chalk.dim(t.report.notPracticed));\n } else {\n lines.push(row(t.report.practiced, fmtDuration(r.practiceMs, lang)));\n lines.push(row(t.report.chapters, String(r.chaptersCompleted)));\n lines.push(row(t.report.words, String(r.wordCount)));\n const accPct = Math.round(r.accuracy * 1000) / 10;\n lines.push(row(t.report.accuracy, accColor(accPct)(`${accPct}%`)));\n lines.push(row(t.report.wpm, String(r.wpm)));\n if (r.newMistakeWords > 0) {\n lines.push(row(t.report.newMistakes, chalk.red(String(r.newMistakeWords))));\n }\n }\n\n lines.push('');\n lines.push(chalk.dim.italic(t.report.farewell));\n return lines;\n}\n\nexport function printSessionReport(r: SessionReport, t: Strings, lang: 'zh' | 'en'): void {\n if (r.startedAt === null && r.chaptersCompleted === 0) return;\n const content = buildLines(r, t, lang).join('\\n');\n console.log(\n boxen(content, {\n title: chalk.bold.cyan(t.report.title),\n titleAlignment: 'left',\n borderStyle: 'round',\n borderColor: 'gray',\n padding: { top: 0, bottom: 0, left: 3, right: 3 },\n margin: { top: 1, bottom: 1, left: 2, right: 0 },\n }),\n );\n}\n"],"mappings":"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","names":["ENTER","LEAVE","active","signalsRegistered","ensureSignals","cleanup","enterAltScreen","leaveAltScreen","Suspense","lazy","useRef","Box","Text","useApp","useInput","useEffect","useState","Box","useStdout","jsx","Fullscreen","children","stdout","useStdout","size","setSize","useState","useEffect","enterAltScreen","onResize","leaveAltScreen","Box","useState","Box","Text","useApp","useInput","jsx","jsxs","MainMenu","cfg","selected","setSelected","useState","exit","useApp","nav","useNav","audio","useAudioStatus","t","useStrings","defaultDictName","useDictName","m","startPractice","stealth","chapterIndex","resumeChapterFor","loadSessions","setPostExitAction","items","truncateName","labelW","it","visibleWidth","useInput","input","key","i","Box","Text","PALETTE","active","pad","jsx","PracticeScreen","lazy","m","DictBrowser","ConfigEditor","StatsViewer","WordLookup","HelpScreen","LazyFallback","Box","Text","PALETTE","App","initial","initialCfg","inline","AppStateProvider","LangBridge","RegistryProvider","AudioStatusProvider","NavProvider","Router","Fullscreen","children","cfg","useAppState","StringsProvider","screenKey","frame","p","nav","useNav","exit","useApp","lastKeyRef","useRef","useInput","input","key","screen","MainMenu","Suspense","chalk","boxen","ensureMainScreen","leaveAltScreen","fmtDuration","ms","lang","total","m","s","accColor","pct","chalk","buildLines","r","lines","allLabels","labelW","visibleWidth","padLabel","row","label","value","accPct","printSessionReport","content","boxen"]}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{a as o,b as s,c as p,d as c}from"./chunk-RR77GWZ3.js";import{d as n,g as a,i as m}from"./chunk-6ROGUGNX.js";import"./chunk-BIBS2Q3E.js";import"./chunk-MFGIEKBU.js";import{a as i}from"./chunk-VTIB5Q36.js";import"./chunk-GULN5HRV.js";import"./chunk-QG7ZTS2G.js";import{e}from"./chunk-VIOZNKSK.js";import"./chunk-NA5UNUVL.js";import"./chunk-E6BBQALJ.js";import{render as g}from"ink";import{createElement as h}from"react";async function y(){o();let r=await i();a();let{waitUntilExit:f}=g(h(s,{initial:{name:"main"},initialCfg:r}),{patchConsole:!1,exitOnCtrlC:!1});await f(),p();let t=n();if(t?.type==="stealth"){let{runPractice:u}=await import("./practice.impl-2PPCBBK3.js");await u(t.dictId,{chapter:t.chapterIndex+1,mode:t.mode,stealth:!0});return}let{lang:l,t:d}=e(r.language);c(m(),d,l)}export{y as runMainMenuImpl};
|
|
2
|
-
//# sourceMappingURL=menu.impl-ZUAMXKFD.js.map
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{a as c,b as g,c as h,d as x}from"./chunk-RR77GWZ3.js";import{f as m,g as l,i as u}from"./chunk-6ROGUGNX.js";import"./chunk-BIBS2Q3E.js";import"./chunk-MFGIEKBU.js";import{a}from"./chunk-VTIB5Q36.js";import{b as f,e as p}from"./chunk-GULN5HRV.js";import"./chunk-QG7ZTS2G.js";import{e as d}from"./chunk-VIOZNKSK.js";import"./chunk-NA5UNUVL.js";import"./chunk-E6BBQALJ.js";import s from"chalk";import{render as v}from"ink";import{createElement as E}from"react";var C=["order","dictation","review","random","loop"];function P(o){return C.includes(o)}async function Y(o,t){if(!process.stdout.isTTY){console.error(s.red("Practice requires an interactive TTY.")),process.exitCode=1;return}let e=await a(),i=o??e.defaultDict;if(!i){console.error(s.red("No dictionary specified. Pass an id or set config.defaultDict.")),process.exitCode=1;return}let n=t.mode??e.defaultMode;if(!P(n)){console.error(s.red(`Invalid mode "${n}". Valid: ${C.join(", ")}`)),process.exitCode=1;return}let M=t.chapter!==void 0?Math.max(0,Number(t.chapter)-1):p(await f(),i),r=t.stealth===!0||e.stealth==="default";r||c(),l();let{waitUntilExit:S}=v(E(g,{initial:{name:"practice",params:{dictId:i,chapterIndex:M,mode:n,stealth:r}},initialCfg:e,inline:r}),{patchConsole:!1,exitOnCtrlC:!1});if(await S(),r||h(),m()){process.stdout.write("\x1B[3F\x1B[0J");return}if(r)return;let{lang:w,t:b}=d(e.language);x(u(),b,w)}export{Y as runPractice};
|
|
2
|
-
//# sourceMappingURL=practice.impl-2PPCBBK3.js.map
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{c as w}from"./chunk-EBAA2ZKH.js";import"./chunk-VTIB5Q36.js";import{a as h}from"./chunk-G3DQB7FI.js";import{d as g}from"./chunk-NA5UNUVL.js";import{a as u}from"./chunk-E6BBQALJ.js";import i from"chalk";import{readdir as k}from"fs/promises";async function j(){try{return(await k(u.dictsDir)).filter(s=>s.endsWith(".json")&&!s.endsWith(".meta.json")).map(s=>s.replace(/\.json$/,""))}catch{return[]}}async function N(c,s){let p=c.toLowerCase(),a=await j();if(a.length===0){console.log(i.yellow("No local dictionaries. Run `qwerty dict pull <id>` first."));return}let l=[];for(let o of a){let t=await w(o);if(t)for(let n of t){let e=n.name.toLowerCase();(s.exact?e===p:e.includes(p))&&l.push({dictId:o,word:n})}}if(l.length===0){console.log(i.yellow(`No matches for "${c}" in ${a.length} local dictionaries`));return}let d=new Map;for(let o of l){let t=d.get(o.word.name)??[];t.push(o),d.set(o.word.name,t)}let y=await h();for(let[o,t]of d){let n=t[0].word;console.log(),console.log(i.bold.white(o));let e=n.usphone?`US /${n.usphone}/`:"",m=n.ukphone?`UK /${n.ukphone}/`:"";(e||m)&&console.log(i.dim(` ${[e,m].filter(Boolean).join(" ")}`));for(let r of n.trans??[])console.log(i.cyan(` \xB7 ${r}`));let $=await Promise.all(t.map(async r=>(await g(r.dictId))?.name??r.dictId));console.log(i.dim(` in: ${$.join(", ")}`));let f=y[o];f&&console.log(i.dim(` mistakes: ${f.count} (last ${f.lastSeen.slice(0,10)})`))}console.log()}export{N as runWordLookup};
|
|
2
|
-
//# sourceMappingURL=word.impl-EJNGBD7U.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/word.impl.ts"],"sourcesContent":["import chalk from 'chalk';\nimport { readdir } from 'node:fs/promises';\nimport { paths } from '../infra/paths.js';\nimport { loadLocalDictionary } from '../infra/dict-downloader.js';\nimport { loadMistakes } from '../domain/mistakes.js';\nimport { findEntry } from '../infra/registry-store.js';\nimport type { Word } from '../domain/dictionary.js';\n\nasync function listLocalDictIds(): Promise<string[]> {\n try {\n const files = await readdir(paths.dictsDir);\n return files\n .filter((f) => f.endsWith('.json') && !f.endsWith('.meta.json'))\n .map((f) => f.replace(/\\.json$/, ''));\n } catch {\n return [];\n }\n}\n\nexport async function runWordLookup(keyword: string, opts: { exact?: boolean }): Promise<void> {\n const q = keyword.toLowerCase();\n const ids = await listLocalDictIds();\n if (ids.length === 0) {\n console.log(chalk.yellow('No local dictionaries. Run `qwerty dict pull <id>` first.'));\n return;\n }\n\n type Hit = { dictId: string; word: Word };\n const hits: Hit[] = [];\n for (const id of ids) {\n const words = await loadLocalDictionary(id);\n if (!words) continue;\n for (const w of words) {\n const wl = w.name.toLowerCase();\n const match = opts.exact ? wl === q : wl.includes(q);\n if (match) hits.push({ dictId: id, word: w });\n }\n }\n\n if (hits.length === 0) {\n console.log(\n chalk.yellow(`No matches for \"${keyword}\" in ${ids.length} local dictionaries`),\n );\n return;\n }\n\n // Group hits by word name; one block per distinct word.\n const byName = new Map<string, Hit[]>();\n for (const h of hits) {\n const arr = byName.get(h.word.name) ?? [];\n arr.push(h);\n byName.set(h.word.name, arr);\n }\n\n const book = await loadMistakes();\n for (const [name, group] of byName) {\n const first = group[0]!.word;\n console.log();\n console.log(chalk.bold.white(name));\n const us = first.usphone ? `US /${first.usphone}/` : '';\n const uk = first.ukphone ? `UK /${first.ukphone}/` : '';\n if (us || uk) console.log(chalk.dim(` ${[us, uk].filter(Boolean).join(' ')}`));\n for (const t of first.trans ?? []) console.log(chalk.cyan(` · ${t}`));\n const sources = await Promise.all(\n group.map(async (h) => {\n const reg = await findEntry(h.dictId);\n return reg?.name ?? h.dictId;\n }),\n );\n console.log(chalk.dim(` in: ${sources.join(', ')}`));\n const mistake = book[name];\n if (mistake) {\n console.log(\n chalk.dim(` mistakes: ${mistake.count} (last ${mistake.lastSeen.slice(0, 10)})`),\n );\n }\n }\n console.log();\n}\n"],"mappings":"4LAAA,OAAOA,MAAW,QAClB,OAAS,WAAAC,MAAe,cAOxB,eAAeC,GAAsC,CACnD,GAAI,CAEF,OADc,MAAMC,EAAQC,EAAM,QAAQ,GAEvC,OAAQC,GAAMA,EAAE,SAAS,OAAO,GAAK,CAACA,EAAE,SAAS,YAAY,CAAC,EAC9D,IAAKA,GAAMA,EAAE,QAAQ,UAAW,EAAE,CAAC,CACxC,MAAQ,CACN,MAAO,CAAC,CACV,CACF,CAEA,eAAsBC,EAAcC,EAAiBC,EAA0C,CAC7F,IAAMC,EAAIF,EAAQ,YAAY,EACxBG,EAAM,MAAMR,EAAiB,EACnC,GAAIQ,EAAI,SAAW,EAAG,CACpB,QAAQ,IAAIC,EAAM,OAAO,2DAA2D,CAAC,EACrF,MACF,CAGA,IAAMC,EAAc,CAAC,EACrB,QAAWC,KAAMH,EAAK,CACpB,IAAMI,EAAQ,MAAMC,EAAoBF,CAAE,EAC1C,GAAKC,EACL,QAAWE,KAAKF,EAAO,CACrB,IAAMG,EAAKD,EAAE,KAAK,YAAY,GAChBR,EAAK,MAAQS,IAAOR,EAAIQ,EAAG,SAASR,CAAC,IACxCG,EAAK,KAAK,CAAE,OAAQC,EAAI,KAAMG,CAAE,CAAC,CAC9C,CACF,CAEA,GAAIJ,EAAK,SAAW,EAAG,CACrB,QAAQ,IACND,EAAM,OAAO,mBAAmBJ,CAAO,QAAQG,EAAI,MAAM,qBAAqB,CAChF,EACA,MACF,CAGA,IAAMQ,EAAS,IAAI,IACnB,QAAWC,KAAKP,EAAM,CACpB,IAAMQ,EAAMF,EAAO,IAAIC,EAAE,KAAK,IAAI,GAAK,CAAC,EACxCC,EAAI,KAAKD,CAAC,EACVD,EAAO,IAAIC,EAAE,KAAK,KAAMC,CAAG,CAC7B,CAEA,IAAMC,EAAO,MAAMC,EAAa,EAChC,OAAW,CAACC,EAAMC,CAAK,IAAKN,EAAQ,CAClC,IAAMO,EAAQD,EAAM,CAAC,EAAG,KACxB,QAAQ,IAAI,EACZ,QAAQ,IAAIb,EAAM,KAAK,MAAMY,CAAI,CAAC,EAClC,IAAMG,EAAKD,EAAM,QAAU,OAAOA,EAAM,OAAO,IAAM,GAC/CE,EAAKF,EAAM,QAAU,OAAOA,EAAM,OAAO,IAAM,IACjDC,GAAMC,IAAI,QAAQ,IAAIhB,EAAM,IAAI,KAAK,CAACe,EAAIC,CAAE,EAAE,OAAO,OAAO,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,EAChF,QAAWC,KAAKH,EAAM,OAAS,CAAC,EAAG,QAAQ,IAAId,EAAM,KAAK,UAAOiB,CAAC,EAAE,CAAC,EACrE,IAAMC,EAAU,MAAM,QAAQ,IAC5BL,EAAM,IAAI,MAAOL,IACH,MAAMW,EAAUX,EAAE,MAAM,IACxB,MAAQA,EAAE,MACvB,CACH,EACA,QAAQ,IAAIR,EAAM,IAAI,SAASkB,EAAQ,KAAK,IAAI,CAAC,EAAE,CAAC,EACpD,IAAME,EAAUV,EAAKE,CAAI,EACrBQ,GACF,QAAQ,IACNpB,EAAM,IAAI,eAAeoB,EAAQ,KAAK,UAAUA,EAAQ,SAAS,MAAM,EAAG,EAAE,CAAC,GAAG,CAClF,CAEJ,CACA,QAAQ,IAAI,CACd","names":["chalk","readdir","listLocalDictIds","readdir","paths","f","runWordLookup","keyword","opts","q","ids","chalk","hits","id","words","loadLocalDictionary","w","wl","byName","h","arr","book","loadMistakes","name","group","first","us","uk","t","sources","findEntry","mistake"]}
|