saterminal 0.3.2 → 0.5.0
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/README.md +168 -4
- package/data/question-bank.json.zst +0 -0
- package/package.json +16 -10
- package/src/cli/commands/_app.tsx +10 -0
- package/src/cli/commands/config/index.tsx +21 -0
- package/src/cli/commands/config/reset.tsx +12 -0
- package/src/cli/commands/config/set.tsx +34 -0
- package/src/cli/commands/focus.tsx +12 -0
- package/src/cli/commands/history.tsx +21 -0
- package/src/cli/commands/index.tsx +7 -0
- package/src/cli/commands/review.tsx +7 -0
- package/src/cli/commands/show.tsx +36 -0
- package/src/cli/commands/stats.tsx +15 -0
- package/src/cli/commands/weak.tsx +13 -0
- package/src/cli/components/command-action.tsx +23 -0
- package/src/cli/components/report.tsx +16 -0
- package/src/cli/index.ts +10 -0
- package/src/cli/report-options.ts +19 -0
- package/src/cli/reports/focus.ts +20 -0
- package/src/cli/reports/history.ts +21 -0
- package/src/cli/reports/question.ts +24 -0
- package/src/cli/reports/stats.ts +66 -0
- package/src/cli/reports/terminal-format.ts +28 -0
- package/src/cli/reports/weaknesses.ts +36 -0
- package/src/database/focus-repository.ts +26 -0
- package/src/database/index.ts +28 -0
- package/src/database/migrations.ts +100 -0
- package/src/database/progress-repository.ts +90 -0
- package/src/local-data/paths.ts +17 -0
- package/src/local-data/setup.ts +25 -0
- package/src/practice/answer-question.ts +25 -0
- package/src/practice/question-queue.ts +58 -0
- package/src/preferences/index.ts +131 -0
- package/src/preferences/preferences.schema.json +45 -0
- package/src/progress/activity.ts +55 -0
- package/src/progress/attempt.ts +70 -0
- package/src/progress/history.ts +34 -0
- package/src/progress/review-queue.ts +44 -0
- package/src/progress/statistics.ts +35 -0
- package/src/progress/weaknesses.ts +44 -0
- package/src/questions/focus.ts +77 -0
- package/src/questions/local-bank.ts +133 -0
- package/src/questions/practice-sat.ts +14 -0
- package/src/questions/question.ts +25 -0
- package/src/questions/taxonomy.ts +56 -0
- package/src/text/duration.ts +4 -0
- package/src/{text.ts → text/html.ts} +3 -119
- package/src/text/media.ts +16 -0
- package/src/text/rich-text.ts +37 -0
- package/src/text/wrap.ts +59 -0
- package/src/tui/app.tsx +243 -0
- package/src/tui/components/chrome.tsx +39 -0
- package/src/tui/components/question-content.tsx +91 -0
- package/src/tui/hooks/use-terminal-size.ts +16 -0
- package/src/tui/screens/detail.tsx +20 -0
- package/src/tui/screens/focus.tsx +244 -0
- package/src/tui/screens/history.tsx +33 -0
- package/src/tui/screens/practice.tsx +159 -0
- package/src/tui/screens/result.tsx +169 -0
- package/src/tui/screens/setup.tsx +17 -0
- package/src/tui/screens/summary.tsx +16 -0
- package/src/api.ts +0 -113
- package/src/cli.ts +0 -917
- package/src/focus.ts +0 -231
- package/src/main.ts +0 -13
- package/src/progress.ts +0 -48
- package/src/state.ts +0 -346
- package/src/tui/app.ts +0 -134
- package/src/tui/focus-grid.ts +0 -113
- package/src/tui/frame.ts +0 -210
- package/src/tui/history.ts +0 -6
- package/src/tui/input.ts +0 -442
- package/src/tui/kit.ts +0 -9
- package/src/tui/layout.ts +0 -25
- package/src/tui/pane-content.ts +0 -150
- package/src/tui/question.ts +0 -46
- package/src/tui/render.ts +0 -627
- package/src/tui/timer.ts +0 -43
- package/src/tui/types.ts +0 -51
- package/src/tui/viewport.ts +0 -60
- package/src/tui.ts +0 -1
- package/src/types/terminal-kit.d.ts +0 -121
- package/src/types.ts +0 -69
package/src/tui/types.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { Attempt, Focus, PracticeQuestion } from "../types.ts";
|
|
2
|
-
import type { TextSegment } from "../text.ts";
|
|
3
|
-
import type { PaneId } from "./viewport.ts";
|
|
4
|
-
|
|
5
|
-
export type View = "setup" | "focus" | "loading" | "practice" | "review" | "history" | "summary" | "detail" | "error";
|
|
6
|
-
|
|
7
|
-
export type AppState = {
|
|
8
|
-
attempts: Map<string, Attempt>;
|
|
9
|
-
skippedIds: Set<string>;
|
|
10
|
-
nextQuestion?: Promise<PracticeQuestion | undefined>;
|
|
11
|
-
reviewMode?: boolean;
|
|
12
|
-
reviewQuestionIds?: string[];
|
|
13
|
-
focus: Focus;
|
|
14
|
-
focusIndex: number;
|
|
15
|
-
focusColumn: number;
|
|
16
|
-
focusRow: number;
|
|
17
|
-
view: View;
|
|
18
|
-
question?: PracticeQuestion;
|
|
19
|
-
selected: number;
|
|
20
|
-
questionScroll: number;
|
|
21
|
-
answerScroll: number;
|
|
22
|
-
activePane: PaneId;
|
|
23
|
-
elapsedMs: number;
|
|
24
|
-
timerStartedAt?: number;
|
|
25
|
-
timerPaused: boolean;
|
|
26
|
-
timerHidden: boolean;
|
|
27
|
-
lastAnswer?: string;
|
|
28
|
-
lastCorrect?: boolean;
|
|
29
|
-
detailQuestion?: PracticeQuestion;
|
|
30
|
-
historyIndex: number;
|
|
31
|
-
notice?: string;
|
|
32
|
-
error?: string;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export type KeyData = {
|
|
36
|
-
code?: string | Buffer | number;
|
|
37
|
-
codepoint?: number;
|
|
38
|
-
isCharacter?: boolean;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export type DisplayRow = {
|
|
42
|
-
segments: TextSegment[];
|
|
43
|
-
bold?: boolean;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export type PaneLayout = {
|
|
47
|
-
leftX: number;
|
|
48
|
-
leftWidth: number;
|
|
49
|
-
rightX: number;
|
|
50
|
-
rightWidth: number;
|
|
51
|
-
};
|
package/src/tui/viewport.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
export type PaneId = "question" | "answers";
|
|
2
|
-
|
|
3
|
-
export type PaneViewport = {
|
|
4
|
-
scroll: number;
|
|
5
|
-
height: number;
|
|
6
|
-
contentRows: number;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export function alternatePane(pane: PaneId): PaneId {
|
|
10
|
-
return pane === "question" ? "answers" : "question";
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function maxScroll(viewport: PaneViewport): number {
|
|
14
|
-
return Math.max(0, viewport.contentRows - viewport.height);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function clampScroll(viewport: PaneViewport): number {
|
|
18
|
-
return Math.max(0, Math.min(viewport.scroll, maxScroll(viewport)));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function scrollBy(viewport: PaneViewport, delta: number): number {
|
|
22
|
-
return clampScroll({ ...viewport, scroll: viewport.scroll + delta });
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function scrollPage(viewport: PaneViewport, direction: 1 | -1): number {
|
|
26
|
-
return scrollBy(viewport, direction * Math.max(1, viewport.height - 1));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function scrollToEdge(viewport: PaneViewport, edge: "top" | "bottom"): number {
|
|
30
|
-
return edge === "top" ? 0 : maxScroll(viewport);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function ensureRowVisible(viewport: PaneViewport, row: number): number {
|
|
34
|
-
const scroll = clampScroll(viewport);
|
|
35
|
-
if (row < scroll) {
|
|
36
|
-
return clampScroll({ ...viewport, scroll: row });
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const lastVisible = scroll + viewport.height - 1;
|
|
40
|
-
if (row > lastVisible) {
|
|
41
|
-
return clampScroll({ ...viewport, scroll: row - viewport.height + 1 });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return scroll;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function ensureRangeVisible(viewport: PaneViewport, start: number, end: number): number {
|
|
48
|
-
const scroll = clampScroll(viewport);
|
|
49
|
-
const lastVisible = scroll + viewport.height - 1;
|
|
50
|
-
|
|
51
|
-
if (start < scroll) {
|
|
52
|
-
return clampScroll({ ...viewport, scroll: start });
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (end > lastVisible) {
|
|
56
|
-
return clampScroll({ ...viewport, scroll: end - viewport.height + 1 });
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return scroll;
|
|
60
|
-
}
|
package/src/tui.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { runTui } from "./tui/app.ts";
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
declare module "terminal-kit" {
|
|
2
|
-
type KeyData = {
|
|
3
|
-
code?: string | Buffer | number;
|
|
4
|
-
codepoint?: number;
|
|
5
|
-
isCharacter?: boolean;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export type Terminal = {
|
|
9
|
-
width: number;
|
|
10
|
-
height: number;
|
|
11
|
-
clear(): Terminal;
|
|
12
|
-
moveTo(x: number, y: number): Terminal;
|
|
13
|
-
bold: Terminal & Record<string, Terminal>;
|
|
14
|
-
gray: Terminal;
|
|
15
|
-
cyan: Terminal;
|
|
16
|
-
green: Terminal;
|
|
17
|
-
red: Terminal;
|
|
18
|
-
yellow: Terminal;
|
|
19
|
-
underline: Terminal;
|
|
20
|
-
inverse: Terminal;
|
|
21
|
-
defaultColor: Terminal;
|
|
22
|
-
italic: Terminal;
|
|
23
|
-
styleReset(): Terminal;
|
|
24
|
-
eraseLineAfter(): Terminal;
|
|
25
|
-
fullscreen(enabled: boolean): void;
|
|
26
|
-
hideCursor(enabled?: boolean): void;
|
|
27
|
-
grabInput(enabled?: boolean): void;
|
|
28
|
-
processExit(code: number): void;
|
|
29
|
-
on(event: "key", listener: (name: string, matches?: string[], data?: KeyData) => void): void;
|
|
30
|
-
on(event: "resize", listener: (width: number, height: number) => void): void;
|
|
31
|
-
off(event: "resize", listener: (width: number, height: number) => void): void;
|
|
32
|
-
(value: string): void;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export type DocumentOptions = {
|
|
36
|
-
inlineTerm?: Terminal;
|
|
37
|
-
eventSource?: Terminal;
|
|
38
|
-
outputDst?: Terminal;
|
|
39
|
-
outputX?: number;
|
|
40
|
-
outputY?: number;
|
|
41
|
-
outputWidth?: number;
|
|
42
|
-
outputHeight?: number;
|
|
43
|
-
backgroundAttr?: Record<string, unknown>;
|
|
44
|
-
noInput?: boolean;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export type MenuItem = {
|
|
48
|
-
content: string;
|
|
49
|
-
value: string;
|
|
50
|
-
disabled?: boolean;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export type ColumnMenuMultiOptions = {
|
|
54
|
-
parent?: unknown;
|
|
55
|
-
id?: string;
|
|
56
|
-
x?: number;
|
|
57
|
-
y?: number;
|
|
58
|
-
width?: number;
|
|
59
|
-
items?: MenuItem[];
|
|
60
|
-
value?: Record<string, boolean>;
|
|
61
|
-
multiLineItems?: boolean;
|
|
62
|
-
master?: MenuItem;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export type WindowOptions = {
|
|
66
|
-
parent?: unknown;
|
|
67
|
-
title?: string;
|
|
68
|
-
x?: number;
|
|
69
|
-
y?: number;
|
|
70
|
-
width?: number;
|
|
71
|
-
height?: number;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export type TextOptions = {
|
|
75
|
-
parent?: unknown;
|
|
76
|
-
content?: string | string[];
|
|
77
|
-
x?: number;
|
|
78
|
-
y?: number;
|
|
79
|
-
width?: number;
|
|
80
|
-
height?: number;
|
|
81
|
-
attr?: Record<string, unknown>;
|
|
82
|
-
contentHasMarkup?: boolean | "ansi" | "legacyAnsi";
|
|
83
|
-
noDraw?: boolean;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
export class Document {
|
|
87
|
-
constructor(options: DocumentOptions);
|
|
88
|
-
clear(): void;
|
|
89
|
-
draw(): void;
|
|
90
|
-
destroy(): void;
|
|
91
|
-
focusNext(): void;
|
|
92
|
-
giveFocusTo(element: unknown, type?: string): void;
|
|
93
|
-
on(event: "key", listener: (key: string) => void): void;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export class ColumnMenuMulti {
|
|
97
|
-
constructor(options: ColumnMenuMultiOptions);
|
|
98
|
-
value: Record<string, boolean>;
|
|
99
|
-
setValue(value: Record<string, boolean>, noDraw?: boolean): void;
|
|
100
|
-
on(event: "itemToggle", listener: (key: string, enabled: boolean) => void): void;
|
|
101
|
-
on(event: "submit", listener: (...args: unknown[]) => void): void;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export class Window {
|
|
105
|
-
constructor(options: WindowOptions);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export class Text {
|
|
109
|
-
constructor(options: TextOptions);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const terminalKit: {
|
|
113
|
-
terminal: Terminal;
|
|
114
|
-
Document: typeof Document;
|
|
115
|
-
ColumnMenuMulti: typeof ColumnMenuMulti;
|
|
116
|
-
Window: typeof Window;
|
|
117
|
-
Text: typeof Text;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
export default terminalKit;
|
|
121
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
export type Outcome = "correct" | "incorrect" | "corrected";
|
|
2
|
-
|
|
3
|
-
export type Attempt = {
|
|
4
|
-
question_id: string;
|
|
5
|
-
outcome: Outcome;
|
|
6
|
-
updated_at: string;
|
|
7
|
-
elapsed_seconds: number;
|
|
8
|
-
difficulty?: string;
|
|
9
|
-
domain?: string;
|
|
10
|
-
domain_desc?: string;
|
|
11
|
-
skill?: string;
|
|
12
|
-
skill_desc?: string;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export type AttemptEvent = {
|
|
16
|
-
question_id: string;
|
|
17
|
-
correct: boolean;
|
|
18
|
-
answered_at: string;
|
|
19
|
-
elapsed_seconds: number;
|
|
20
|
-
difficulty: string;
|
|
21
|
-
domain: string;
|
|
22
|
-
domain_desc?: string;
|
|
23
|
-
skill: string;
|
|
24
|
-
skill_desc?: string;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export type SummaryRow = {
|
|
28
|
-
metric: string;
|
|
29
|
-
value: string;
|
|
30
|
-
updated_at: string;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export type Difficulty = "E" | "M" | "H";
|
|
34
|
-
export type Domain = "INI" | "CAS" | "EOI" | "SEC";
|
|
35
|
-
export type Skill = "CID" | "INF" | "COE" | "WIC" | "TSP" | "CTC" | "SYN" | "TRA" | "BOU" | "FSS";
|
|
36
|
-
|
|
37
|
-
export type Focus = {
|
|
38
|
-
difficulties: Difficulty[];
|
|
39
|
-
domains: Domain[];
|
|
40
|
-
skills: Skill[];
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export type QuestionMeta = {
|
|
44
|
-
questionId: string;
|
|
45
|
-
uId: string;
|
|
46
|
-
external_id: string;
|
|
47
|
-
difficulty: string;
|
|
48
|
-
primary_class_cd: string;
|
|
49
|
-
primary_class_cd_desc?: string;
|
|
50
|
-
program?: string;
|
|
51
|
-
skill_cd: string;
|
|
52
|
-
skill_desc?: string;
|
|
53
|
-
score_band_range_cd?: number;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export type QuestionDetail = {
|
|
57
|
-
externalid: string;
|
|
58
|
-
type: string;
|
|
59
|
-
stimulus?: string;
|
|
60
|
-
stem: string;
|
|
61
|
-
answerOptions: Record<string, string>;
|
|
62
|
-
correct_answer: string[];
|
|
63
|
-
rationale?: string;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
export type PracticeQuestion = {
|
|
67
|
-
meta: QuestionMeta;
|
|
68
|
-
detail: QuestionDetail;
|
|
69
|
-
};
|