@khorsheed/dsh-library 0.0.0-stage → 0.1.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/CHANGELOG.md +14 -0
- package/README.en.md +49 -0
- package/README.i18n.yaml +6 -0
- package/README.md +48 -2
- package/cordis.patch.yml +17 -0
- package/lib/client.js +66276 -0
- package/lib/index.js +3318 -0
- package/lib/profiles-BXM8xNUf.js +1608 -0
- package/lib/profiles-BrbeM-bi.js +1635 -0
- package/lib/profiles-CFrPQdfO.js +1692 -0
- package/lib/profiles-DxtzIUvd.js +1632 -0
- package/lib/profiles-O0ddEit3.js +1631 -0
- package/lib/profiles-Oj-zBAY9.js +1692 -0
- package/lib/profiles-gIEviEuO.js +1610 -0
- package/lib/store-CIcZgTsg.js +1456 -0
- package/lib/store-DfYOmS44.js +1418 -0
- package/lib/tool.js +1597 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +3811 -0
- package/lib/typert.remote-client.d.ts +137 -0
- package/lib/typert.remote-client.js +3077 -0
- package/lib/types/cards.d.ts +41 -0
- package/lib/types/cards.js +111 -0
- package/lib/types/client/LibraryPane.d.ts +26 -0
- package/lib/types/client/LibraryPane.js +117 -0
- package/lib/types/client/bind/BindChip.d.ts +33 -0
- package/lib/types/client/bind/BindChip.js +102 -0
- package/lib/types/client/contract.d.ts +164 -0
- package/lib/types/client/contract.js +1 -0
- package/lib/types/client/covers.d.ts +36 -0
- package/lib/types/client/covers.js +66 -0
- package/lib/types/client/definition.d.ts +27 -0
- package/lib/types/client/definition.js +32 -0
- package/lib/types/client/icons-local.d.ts +41 -0
- package/lib/types/client/icons-local.js +25 -0
- package/lib/types/client/icons.d.ts +27 -0
- package/lib/types/client/icons.js +11 -0
- package/lib/types/client/index.d.ts +35 -0
- package/lib/types/client/index.js +219 -0
- package/lib/types/client/judge.d.ts +70 -0
- package/lib/types/client/judge.js +262 -0
- package/lib/types/client/locales.d.ts +553 -0
- package/lib/types/client/locales.js +1076 -0
- package/lib/types/client/look.d.ts +63 -0
- package/lib/types/client/look.js +64 -0
- package/lib/types/client/math.d.ts +24 -0
- package/lib/types/client/math.js +47 -0
- package/lib/types/client/mistakes.d.ts +21 -0
- package/lib/types/client/mistakes.js +28 -0
- package/lib/types/client/preset-visibility.d.ts +109 -0
- package/lib/types/client/preset-visibility.js +123 -0
- package/lib/types/client/quiz-drafts.d.ts +29 -0
- package/lib/types/client/quiz-drafts.js +50 -0
- package/lib/types/client/store.d.ts +109 -0
- package/lib/types/client/store.js +44 -0
- package/lib/types/client/titles.d.ts +17 -0
- package/lib/types/client/titles.js +23 -0
- package/lib/types/client/ui.d.ts +96 -0
- package/lib/types/client/ui.js +124 -0
- package/lib/types/client/views/book.d.ts +57 -0
- package/lib/types/client/views/book.js +164 -0
- package/lib/types/client/views/cards.d.ts +17 -0
- package/lib/types/client/views/cards.js +53 -0
- package/lib/types/client/views/home.d.ts +26 -0
- package/lib/types/client/views/home.js +263 -0
- package/lib/types/client/views/lesson.d.ts +42 -0
- package/lib/types/client/views/lesson.js +146 -0
- package/lib/types/client/views/path.d.ts +32 -0
- package/lib/types/client/views/path.js +64 -0
- package/lib/types/client/views/quiz.d.ts +42 -0
- package/lib/types/client/views/quiz.js +366 -0
- package/lib/types/client/views/station-quiz.d.ts +43 -0
- package/lib/types/client/views/station-quiz.js +203 -0
- package/lib/types/client/views/station.d.ts +18 -0
- package/lib/types/client/views/station.js +142 -0
- package/lib/types/image-refs.d.ts +35 -0
- package/lib/types/image-refs.js +71 -0
- package/lib/types/index.d.ts +51 -0
- package/lib/types/index.js +31 -0
- package/lib/types/mastery.d.ts +81 -0
- package/lib/types/mastery.js +110 -0
- package/lib/types/profiles.d.ts +31 -0
- package/lib/types/profiles.js +25 -0
- package/lib/types/remote.d.ts +355 -0
- package/lib/types/remote.js +332 -0
- package/lib/types/service/assess.d.ts +144 -0
- package/lib/types/service/assess.js +773 -0
- package/lib/types/service/bindings.d.ts +92 -0
- package/lib/types/service/bindings.js +198 -0
- package/lib/types/service/cards.d.ts +85 -0
- package/lib/types/service/cards.js +320 -0
- package/lib/types/service/core.d.ts +124 -0
- package/lib/types/service/core.js +169 -0
- package/lib/types/service/pages.d.ts +60 -0
- package/lib/types/service/pages.js +125 -0
- package/lib/types/service/roadmap.d.ts +99 -0
- package/lib/types/service/roadmap.js +284 -0
- package/lib/types/service/rules.d.ts +63 -0
- package/lib/types/service/rules.js +245 -0
- package/lib/types/service/shelf.d.ts +136 -0
- package/lib/types/service/shelf.js +422 -0
- package/lib/types/service.d.ts +42 -0
- package/lib/types/service.js +41 -0
- package/lib/types/store.d.ts +257 -0
- package/lib/types/store.js +1376 -0
- package/lib/types/tool-definition.d.ts +49 -0
- package/lib/types/tool-definition.js +49 -0
- package/lib/types/tool.d.ts +71 -0
- package/lib/types/tool.js +174 -0
- package/lib/types/tools/assess.d.ts +11 -0
- package/lib/types/tools/assess.js +82 -0
- package/lib/types/tools/binding.d.ts +11 -0
- package/lib/types/tools/binding.js +46 -0
- package/lib/types/tools/cards.d.ts +11 -0
- package/lib/types/tools/cards.js +100 -0
- package/lib/types/tools/kit.d.ts +215 -0
- package/lib/types/tools/kit.js +281 -0
- package/lib/types/tools/notes.d.ts +11 -0
- package/lib/types/tools/notes.js +40 -0
- package/lib/types/tools/page.d.ts +11 -0
- package/lib/types/tools/page.js +59 -0
- package/lib/types/tools/project.d.ts +11 -0
- package/lib/types/tools/project.js +67 -0
- package/lib/types/tools/quiz.d.ts +11 -0
- package/lib/types/tools/quiz.js +74 -0
- package/lib/types/tools/read.d.ts +11 -0
- package/lib/types/tools/read.js +127 -0
- package/lib/types/tools/resource.d.ts +11 -0
- package/lib/types/tools/resource.js +147 -0
- package/lib/types/tools/roadmap.d.ts +11 -0
- package/lib/types/tools/roadmap.js +102 -0
- package/lib/types/types.d.ts +752 -0
- package/lib/types/types.js +159 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +135 -3
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The library's domain vocabulary: projects, resources, roadmaps, nodes,
|
|
3
|
+
* assessments, attempts, photo pages, knowledge cards and session bindings.
|
|
4
|
+
* Everything here is plain serializable data — the store (`store.ts`)
|
|
5
|
+
* persists exactly these shapes under `$DSH_HOME/state/dsh-library/`, and the
|
|
6
|
+
* Remote face (`remote.ts`) and the model tools (`tool-definition.ts`) both
|
|
7
|
+
* speak them.
|
|
8
|
+
*
|
|
9
|
+
* The shapes follow the proposal `2026-10-16-library-v5-model` (v5: the
|
|
10
|
+
* six-layer core — Project → Resource → Roadmap → Node → Assessment →
|
|
11
|
+
* Attempt, with Card / Page / Event as the trace layer):
|
|
12
|
+
*
|
|
13
|
+
* - a RESOURCE is anything on the project's shelf. Its `toc` is the
|
|
14
|
+
* thick/thin switch: a resource WITH a contents (a book, a course with
|
|
15
|
+
* episodes) can carry a roadmap, be covered by chapter, be quizzed and be
|
|
16
|
+
* photographed; one without is a thin link (an article, a video, a tool)
|
|
17
|
+
* that only enters a path whole. Nothing is ever uploaded — a thick
|
|
18
|
+
* resource registers title, kind and a contents the coach verified on the
|
|
19
|
+
* web (or took from a photo);
|
|
20
|
+
* - a ROADMAP belongs to exactly one thick resource — a TEXTBOOK (proposal
|
|
21
|
+
* `2026-10-19-library-book-paths`: the project carries no path of its
|
|
22
|
+
* own). Textbooks sit on the shelf in study order (`seq`), and come from
|
|
23
|
+
* the learner, from the coach's pick, or from the coach's own pen
|
|
24
|
+
* (`origin`). A node draws on COVERS in one shape — `{ ref, locator? }`,
|
|
25
|
+
* where ref is a resource id, a bare url, or `topic:…` (plain coached
|
|
26
|
+
* teaching) and locator counts contents rows ("1-5,7"); the path's own
|
|
27
|
+
* textbook is the main cover, anything else rides along as a side
|
|
28
|
+
* reference. A path need not cover the whole contents. The coach writes DRAFTS, the
|
|
29
|
+
* learner adopts one, and the adopted path's state machine has three edges
|
|
30
|
+
* only (`locked → current → done`);
|
|
31
|
+
* - a NODE's `gate` says how it completes: `quiz` (an assessed quiz at the
|
|
32
|
+
* pass ratio), `acceptance` (a hands-on station — the learner submits
|
|
33
|
+
* evidence, the coach judges one verdict per rubric line), `read` (an
|
|
34
|
+
* optional side stop that 「读完」 completes and never gates);
|
|
35
|
+
* - an ASSESSMENT is the graded thing: `quiz` carries questions, `practice`
|
|
36
|
+
* carries a station's materialized rubric (stable line ids; a line may
|
|
37
|
+
* reference another assessment that machine-checks it). Hung on a node, on
|
|
38
|
+
* a resource (a book-level paper), or on nothing (free practice). A
|
|
39
|
+
* station's paper history chains by `supersededBy`;
|
|
40
|
+
* - an ATTEMPT is one graded sitting and names only its `assessmentId` (a
|
|
41
|
+
* verdict's `task` is a question id or a rubric line id, so rewording never
|
|
42
|
+
* orphans history);
|
|
43
|
+
* - a PAGE is one photographed page of a thick resource: its recognized text
|
|
44
|
+
* plus the learner's marks;
|
|
45
|
+
* - a CARD (知识卡片) is the learner's own memory: a word or a concept met in a
|
|
46
|
+
* photo, asked about in a chat, or missed in an assessment — one card per
|
|
47
|
+
* thing, sources stacked, with a weakness score (0–3) beside its Leitner
|
|
48
|
+
* mastery;
|
|
49
|
+
* - an EVENT is the append-only audit trail.
|
|
50
|
+
*
|
|
51
|
+
* @module @khorsheed/dsh-library/types
|
|
52
|
+
*/
|
|
53
|
+
/** The state-root directory name under `$DSH_HOME/state`. */
|
|
54
|
+
export declare const STATE_ROOT_SEGMENT = "dsh-library";
|
|
55
|
+
/**
|
|
56
|
+
* The on-disk schema version of `state.json`. v1 (uploaded books, project
|
|
57
|
+
* roadmaps, the vocab book) was never deployed; a v1 root reads as empty. v2
|
|
58
|
+
* roots (per-book roadmaps with `chapterRanges`) and v3/v4 roots (the
|
|
59
|
+
* book/resource split, cover unions, quiz entities) all read tolerantly —
|
|
60
|
+
* every normalize* function reads the old shapes — and rewrite to v5 on their
|
|
61
|
+
* next write. There is no offline migration pass.
|
|
62
|
+
*/
|
|
63
|
+
export declare const LIBRARY_STATE_VERSION = 5;
|
|
64
|
+
/** The Leitner intervals in days, indexed by box (box 5 reuses the last rung). */
|
|
65
|
+
export declare const LEITNER_INTERVAL_DAYS: readonly number[];
|
|
66
|
+
/** The highest Leitner box. */
|
|
67
|
+
export declare const MAX_BOX = 5;
|
|
68
|
+
/** The highest weakness score (three bars in the UI). */
|
|
69
|
+
export declare const MAX_WEAKNESS = 3;
|
|
70
|
+
/** The default node pass threshold (an assessment ratio at or above it completes the node). */
|
|
71
|
+
export declare const DEFAULT_PASS_RATIO = 0.8;
|
|
72
|
+
/** A refusal's code vocabulary — the values the client and the tools switch on. */
|
|
73
|
+
export type LibraryErrorCode = 'not-found' | 'exists' | 'invalid' | 'corrupt' | 'io' | 'storage-unavailable' | 'evidence-gate' | 'state-machine' | 'unavailable' | 'ref-incomplete' | 'unbound';
|
|
74
|
+
/** A domain refusal (never a throw across the Remote wire). */
|
|
75
|
+
export interface LibraryRefusal {
|
|
76
|
+
readonly ok: false;
|
|
77
|
+
readonly error: LibraryErrorCode;
|
|
78
|
+
/** The human/model-readable reason, written to teach the correct call. */
|
|
79
|
+
readonly message: string;
|
|
80
|
+
}
|
|
81
|
+
/** The outcome every mutating verb answers. */
|
|
82
|
+
export type LibraryOutcome<T> = ({
|
|
83
|
+
readonly ok: true;
|
|
84
|
+
} & T) | LibraryRefusal;
|
|
85
|
+
/**
|
|
86
|
+
* Capability handshake: what a client may rely on in this composition. A
|
|
87
|
+
* read-only DISPLAY contract — enforcement stays at the write gates
|
|
88
|
+
* (`storage-unavailable` / `unavailable` refusals), exactly as before. The
|
|
89
|
+
* shape evolves additively only: new fields are always optional, so a
|
|
90
|
+
* consumer written against protocolVersion 1 keeps working (the reader's
|
|
91
|
+
* ReaderCapabilities rule).
|
|
92
|
+
*/
|
|
93
|
+
export interface LibraryCapabilities {
|
|
94
|
+
readonly protocolVersion: 1;
|
|
95
|
+
/** Whether the state root accepts writes; when false every write refuses `storage-unavailable`. */
|
|
96
|
+
readonly hasStorage: boolean;
|
|
97
|
+
/** Why persistence is unavailable, when it is (the store's probe message). */
|
|
98
|
+
readonly storageUnavailableReason?: string;
|
|
99
|
+
/** Whether an attachment store is mounted on the Context; when false the image routes refuse `unavailable`. */
|
|
100
|
+
readonly hasAttachments: boolean;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* What a project's marks mean. `language`: an underline is a word or phrase,
|
|
104
|
+
* quizzed by meaning-in-context, paraphrase and spelling; `subject`: an
|
|
105
|
+
* underline is a concept, quizzed by explanation, contrast and application;
|
|
106
|
+
* `custom`: the learner said how (in `templateNote`).
|
|
107
|
+
*/
|
|
108
|
+
export type LibraryTemplate = 'language' | 'subject' | 'custom';
|
|
109
|
+
/** What editing a project may change; an empty goal clears it. */
|
|
110
|
+
export interface LibraryProjectPatch {
|
|
111
|
+
readonly name?: string;
|
|
112
|
+
readonly goal?: string;
|
|
113
|
+
readonly template?: LibraryTemplate;
|
|
114
|
+
readonly color?: LibraryColor;
|
|
115
|
+
readonly icon?: LibraryIcon;
|
|
116
|
+
}
|
|
117
|
+
/** The preset project colors (the sidebar maps each to a light and a dark tone). */
|
|
118
|
+
export declare const LIBRARY_COLORS: readonly ["ember", "amber", "moss", "teal", "sky", "indigo", "plum", "rose"];
|
|
119
|
+
export type LibraryColor = typeof LIBRARY_COLORS[number];
|
|
120
|
+
/** The preset project icons. */
|
|
121
|
+
export declare const LIBRARY_ICONS: readonly ["globe", "brain", "book", "flask", "atom", "leaf", "pen", "music", "scale", "chart", "code", "compass"];
|
|
122
|
+
export type LibraryIcon = typeof LIBRARY_ICONS[number];
|
|
123
|
+
/** A template's default look, until the learner picks another. */
|
|
124
|
+
export declare const TEMPLATE_LOOK: Readonly<Record<LibraryTemplate, {
|
|
125
|
+
color: LibraryColor;
|
|
126
|
+
icon: LibraryIcon;
|
|
127
|
+
}>>;
|
|
128
|
+
/** One learning project (e.g. 「神经科学」). */
|
|
129
|
+
export interface LibraryProject {
|
|
130
|
+
readonly id: string;
|
|
131
|
+
readonly name: string;
|
|
132
|
+
readonly template: LibraryTemplate;
|
|
133
|
+
readonly color?: LibraryColor;
|
|
134
|
+
readonly icon?: LibraryIcon;
|
|
135
|
+
/** The learner's own words for a custom template. */
|
|
136
|
+
readonly templateNote?: string;
|
|
137
|
+
/** The learner's goal (「学术类 7 分,12 月 14 日」). */
|
|
138
|
+
readonly goal?: string;
|
|
139
|
+
readonly createdAt: string;
|
|
140
|
+
}
|
|
141
|
+
/** One resource's row on the project page. */
|
|
142
|
+
export interface LibraryResourceProgress {
|
|
143
|
+
readonly id: string;
|
|
144
|
+
readonly title: string;
|
|
145
|
+
readonly kind: LibraryResourceKind;
|
|
146
|
+
/** A textbook's place in the study order (1-based); absent on a thin link. */
|
|
147
|
+
readonly seq?: number;
|
|
148
|
+
/** Where the entry came from. */
|
|
149
|
+
readonly origin?: LibraryResourceOrigin;
|
|
150
|
+
/** Adopted-roadmap progress; only a thick resource (toc) can carry one. */
|
|
151
|
+
readonly roadmap?: {
|
|
152
|
+
readonly done: number;
|
|
153
|
+
readonly total: number;
|
|
154
|
+
};
|
|
155
|
+
/** The first current node, when one is in progress. */
|
|
156
|
+
readonly current?: {
|
|
157
|
+
readonly id: string;
|
|
158
|
+
readonly title: string;
|
|
159
|
+
};
|
|
160
|
+
/** A draft is waiting for the learner's 采用. */
|
|
161
|
+
readonly hasDraft: boolean;
|
|
162
|
+
/** The waiting draft's station count. */
|
|
163
|
+
readonly draftTotal?: number;
|
|
164
|
+
}
|
|
165
|
+
/** The project list's row: the project plus its cheap aggregates. */
|
|
166
|
+
export interface LibraryProjectSummary extends LibraryProject {
|
|
167
|
+
/** Every shelf entry (thick and thin) with its progress. */
|
|
168
|
+
readonly resources: readonly LibraryResourceProgress[];
|
|
169
|
+
/** Live cards (not deleted). */
|
|
170
|
+
readonly cardCount: number;
|
|
171
|
+
/** Live, unmastered cards due for review right now. */
|
|
172
|
+
readonly dueCount: number;
|
|
173
|
+
/** When anything last happened in the project (its newest event, else its creation) — the list sorts by it. */
|
|
174
|
+
readonly lastActiveAt: string;
|
|
175
|
+
/** Open misses (待订正) across every station of the project's textbooks. */
|
|
176
|
+
readonly openMisses?: number;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* What a THICK resource's content IS. `knowledge` (textbooks, popular
|
|
180
|
+
* science): the content is domain knowledge, so the coach may quiz from what
|
|
181
|
+
* it knows. `material` (exam papers, novels, anthologies): the content is
|
|
182
|
+
* specific text the model cannot recall — quizzing the resource's content
|
|
183
|
+
* needs a photo or a web quote.
|
|
184
|
+
*/
|
|
185
|
+
export type LibraryBookKind = 'knowledge' | 'material';
|
|
186
|
+
/** One table-of-contents row. */
|
|
187
|
+
export interface LibraryTocEntry {
|
|
188
|
+
/** 1-based position — what cover locators count. */
|
|
189
|
+
readonly n: number;
|
|
190
|
+
readonly title: string;
|
|
191
|
+
/** The part / test / unit it sits under, when the resource groups chapters. */
|
|
192
|
+
readonly part?: string;
|
|
193
|
+
}
|
|
194
|
+
/** Where the table of contents came from — shown on the roadmap draft card. */
|
|
195
|
+
export interface LibraryTocSource {
|
|
196
|
+
readonly by: 'web' | 'model' | 'photo';
|
|
197
|
+
readonly url?: string;
|
|
198
|
+
readonly verifiedAt?: string;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Something in a thick resource worth knowing but not worth a stop on the
|
|
202
|
+
* path — how to use the book, the answer key, transcripts, appendices. Shown
|
|
203
|
+
* in the resource page's 「开始前 / 附录」 strip.
|
|
204
|
+
*/
|
|
205
|
+
export interface LibraryResourceExtra {
|
|
206
|
+
readonly title: string;
|
|
207
|
+
/** One line from the coach: what it is for. */
|
|
208
|
+
readonly note?: string;
|
|
209
|
+
/** Where it is in the resource (「p.152」). */
|
|
210
|
+
readonly where?: string;
|
|
211
|
+
readonly when: 'before' | 'reference';
|
|
212
|
+
}
|
|
213
|
+
/** A thick resource's book-ish metadata, kept off the link fields. */
|
|
214
|
+
export interface LibraryResourceMeta {
|
|
215
|
+
readonly bookKind?: LibraryBookKind;
|
|
216
|
+
/** The cover's abbreviation (「剑5」「PNS」); derived from the title when absent. */
|
|
217
|
+
readonly short?: string;
|
|
218
|
+
readonly extras?: readonly LibraryResourceExtra[];
|
|
219
|
+
readonly author?: string;
|
|
220
|
+
readonly edition?: string;
|
|
221
|
+
readonly isbn?: string;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Where a shelf entry came from: `learner` — the learner's own (a book they
|
|
225
|
+
* hold, a link they handed over); `picked` — an existing book / course / link
|
|
226
|
+
* the coach chose; `authored` — a textbook the coach wrote itself (its
|
|
227
|
+
* contents are the coach's own outline, its 讲义 are the text). Legacy
|
|
228
|
+
* `addedBy` reads as learner / picked.
|
|
229
|
+
*/
|
|
230
|
+
export declare const LIBRARY_RESOURCE_ORIGINS: readonly ["learner", "picked", "authored"];
|
|
231
|
+
export type LibraryResourceOrigin = typeof LIBRARY_RESOURCE_ORIGINS[number];
|
|
232
|
+
/** What a shelf entry IS: the shape of the thing it is or points at. */
|
|
233
|
+
export declare const LIBRARY_RESOURCE_KINDS: readonly ["book", "article", "video", "repo", "tool", "course", "hardware"];
|
|
234
|
+
export type LibraryResourceKind = typeof LIBRARY_RESOURCE_KINDS[number];
|
|
235
|
+
/**
|
|
236
|
+
* One entry on a project's shelf — the v5 unified resource. A BOOK is the
|
|
237
|
+
* thick case: `kind: "book"` plus a table of contents (and its book-ish
|
|
238
|
+
* detail in `meta`). Any other kind with a `toc` is thick too (a course with
|
|
239
|
+
* episodes routes by chapter); without a `toc` it is a thin link — no
|
|
240
|
+
* roadmap, never covered by chapter, never quizzed by content, no pages.
|
|
241
|
+
*/
|
|
242
|
+
export interface LibraryResource {
|
|
243
|
+
readonly id: string;
|
|
244
|
+
readonly title: string;
|
|
245
|
+
readonly kind: LibraryResourceKind;
|
|
246
|
+
/** A thin link's address; a thick resource may carry one (the web copy). */
|
|
247
|
+
readonly url?: string;
|
|
248
|
+
/** The coach's one line: why it is on the shelf (「热身站的读物,只看两节」). */
|
|
249
|
+
readonly note?: string;
|
|
250
|
+
/** Where it came from. */
|
|
251
|
+
readonly origin?: LibraryResourceOrigin;
|
|
252
|
+
/** A textbook's place in the project's study order, 1-based and gapless; thin links carry none. */
|
|
253
|
+
readonly seq?: number;
|
|
254
|
+
/** The thick/thin switch: present (and non-empty) = thick. */
|
|
255
|
+
readonly toc?: readonly LibraryTocEntry[];
|
|
256
|
+
readonly tocSource?: LibraryTocSource;
|
|
257
|
+
readonly meta?: LibraryResourceMeta;
|
|
258
|
+
readonly createdAt: string;
|
|
259
|
+
}
|
|
260
|
+
/** Whether a resource is thick (routable, coverable by chapter, quizzable, photographable). */
|
|
261
|
+
export declare function isThickResource(resource: {
|
|
262
|
+
toc?: readonly LibraryTocEntry[];
|
|
263
|
+
}): boolean;
|
|
264
|
+
/** The three node states; the only legal edges are locked→current→done. */
|
|
265
|
+
export type RoadmapNodeState = 'locked' | 'current' | 'done';
|
|
266
|
+
/**
|
|
267
|
+
* How a node completes. `quiz` nodes are the path's levels: they gate on an
|
|
268
|
+
* assessed quiz at the pass ratio. `acceptance` nodes are hands-on stations:
|
|
269
|
+
* they gate on acceptance — the learner submits evidence, the coach judges
|
|
270
|
+
* one verdict per rubric line. `read` nodes are side stops (an overview worth
|
|
271
|
+
* reading, not testing): always open, complete by 「读完」, and never a
|
|
272
|
+
* dependency of a level. How-to-use pages, answer keys and appendices are not
|
|
273
|
+
* nodes at all — they are resource extras.
|
|
274
|
+
*/
|
|
275
|
+
export type RoadmapNodeGate = 'quiz' | 'acceptance' | 'read';
|
|
276
|
+
/** A table-of-contents span, 1-based and inclusive. */
|
|
277
|
+
export interface LibraryChapterRange {
|
|
278
|
+
readonly from: number;
|
|
279
|
+
readonly to: number;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* The locator's text form: comma-joined spans ("1-5,7,9-12" — a one-row span
|
|
283
|
+
* is the bare number). Contents-row spans are what cover locators count.
|
|
284
|
+
*/
|
|
285
|
+
export declare function formatLocator(ranges: readonly LibraryChapterRange[]): string;
|
|
286
|
+
/** Parse a locator ("1-5,7") into spans; bad segments drop out. */
|
|
287
|
+
export declare function parseLocator(locator: string): LibraryChapterRange[];
|
|
288
|
+
/**
|
|
289
|
+
* What one node draws on, in one shape. `ref` is self-describing:
|
|
290
|
+
*
|
|
291
|
+
* - a resource id (`r_…`, and legacy `b_…` books) — a shelf entry, thick ones
|
|
292
|
+
* with a `locator` of contents rows;
|
|
293
|
+
* - a bare url (`https://…`) — a web text not on the shelf;
|
|
294
|
+
* - `topic:…` — plain coached teaching (the station IS the coach's lecture).
|
|
295
|
+
*
|
|
296
|
+
* `by`/`verifiedAt` record who vouched for the cover and when (a resource's
|
|
297
|
+
* own tocSource records its contents' provenance; a topic needs none).
|
|
298
|
+
*/
|
|
299
|
+
export interface LibraryNodeCover {
|
|
300
|
+
readonly ref: string;
|
|
301
|
+
/** The contents rows drawn on ("1-5,7") — thick resources only. */
|
|
302
|
+
readonly locator?: string;
|
|
303
|
+
/** A url cover's display title. */
|
|
304
|
+
readonly title?: string;
|
|
305
|
+
/** web: fetched and checked; model: from the coach's memory, unverified; user: the learner gave it. */
|
|
306
|
+
readonly by?: 'web' | 'model' | 'user';
|
|
307
|
+
readonly verifiedAt?: string;
|
|
308
|
+
}
|
|
309
|
+
/** What a cover's ref points at. */
|
|
310
|
+
export type LibraryCoverKind = 'resource' | 'url' | 'topic';
|
|
311
|
+
/** Discriminate a cover by its ref's form (a resource id resolves against the shelf). */
|
|
312
|
+
export declare function coverKind(cover: Pick<LibraryNodeCover, 'ref'>): LibraryCoverKind;
|
|
313
|
+
/** A topic cover's teaching text (the ref minus its `topic:` prefix). */
|
|
314
|
+
export declare function coverTopic(cover: Pick<LibraryNodeCover, 'ref'>): string | undefined;
|
|
315
|
+
/** A node's quiz ledger, updated by every assessed attempt that names it. */
|
|
316
|
+
export interface LibraryQuizStats {
|
|
317
|
+
readonly attempts: number;
|
|
318
|
+
readonly lastRatio?: number;
|
|
319
|
+
readonly bestRatio?: number;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* One knowledge point a quiz station tests (考点). Points are authored on the
|
|
323
|
+
* roadmap draft and reviewed at adoption — the learner's consent checkpoint
|
|
324
|
+
* covers the split. Questions reference a point by id, so a reworded title
|
|
325
|
+
* never orphans mastery; per-point state aggregates off the newest verdict of
|
|
326
|
+
* any question probing it.
|
|
327
|
+
*/
|
|
328
|
+
export interface LibraryPoint {
|
|
329
|
+
/** Stable slug (letters, digits, . _ -) — the mastery key. */
|
|
330
|
+
readonly id: string;
|
|
331
|
+
/** The point as the learner reads it (「极限的定义」). */
|
|
332
|
+
readonly title: string;
|
|
333
|
+
/** What the coach noticed the learner stuck on here (a subject project's weak point lands on its point, not on a card). */
|
|
334
|
+
readonly notes?: readonly LibraryPointNote[];
|
|
335
|
+
}
|
|
336
|
+
/** One coach note on a knowledge point: the learner stuck on it. */
|
|
337
|
+
export interface LibraryPointNote {
|
|
338
|
+
readonly text: string;
|
|
339
|
+
/** The short name of the confusion, when the coach gave one (「反函数 vs 倒数」). */
|
|
340
|
+
readonly term?: string;
|
|
341
|
+
readonly at: string;
|
|
342
|
+
/** The session it came from. */
|
|
343
|
+
readonly session?: string;
|
|
344
|
+
}
|
|
345
|
+
/** A knowledge point's state (mastery.ts decides it from the ledger and the notes). */
|
|
346
|
+
export type LibraryPointState = 'untested' | 'weak' | 'practicing' | 'mastered';
|
|
347
|
+
/** A point whose state moved because of a write. */
|
|
348
|
+
export interface LibraryPointChange {
|
|
349
|
+
readonly nodeId: string;
|
|
350
|
+
readonly pointId: string;
|
|
351
|
+
readonly title: string;
|
|
352
|
+
readonly from: LibraryPointState;
|
|
353
|
+
readonly to: LibraryPointState;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* The station's 讲义 — the coach's distilled teaching, persisted on the node so
|
|
357
|
+
* the learner re-reads it in the sidebar instead of scrolling the chat. On a
|
|
358
|
+
* 讲授 (topic) station it IS the textbook; on a book station it is marginalia
|
|
359
|
+
* (the explanation, the learner's own stuck points, the pitfalls) and never a
|
|
360
|
+
* substitute for the book. Coach-written only (library_notes).
|
|
361
|
+
*/
|
|
362
|
+
export interface LibraryNodeNotes {
|
|
363
|
+
/** Markdown body. */
|
|
364
|
+
readonly text: string;
|
|
365
|
+
readonly updatedAt: string;
|
|
366
|
+
/** 1-based — the Nth version (「第 N 次更新」). */
|
|
367
|
+
readonly revisions: number;
|
|
368
|
+
}
|
|
369
|
+
/** One roadmap node. */
|
|
370
|
+
export interface RoadmapNode {
|
|
371
|
+
readonly id: string;
|
|
372
|
+
readonly title: string;
|
|
373
|
+
/** What the learner should be able to do after this node. */
|
|
374
|
+
readonly goals: readonly string[];
|
|
375
|
+
/** Node ids that must be `done` before this one may leave `locked`. */
|
|
376
|
+
readonly dependsOn: readonly string[];
|
|
377
|
+
/** What the station draws on — at least one cover. */
|
|
378
|
+
readonly covers: readonly LibraryNodeCover[];
|
|
379
|
+
readonly state: RoadmapNodeState;
|
|
380
|
+
/** How the node completes: quiz | acceptance | read. */
|
|
381
|
+
readonly gate: RoadmapNodeGate;
|
|
382
|
+
/** Quiz-gated nodes only — a read / acceptance node never carries one. */
|
|
383
|
+
readonly quizStats?: LibraryQuizStats;
|
|
384
|
+
/** Quiz-gated nodes only: the station's knowledge points (3-6), drafted with the roadmap and reviewed at adoption. Quiz questions reference them by id. */
|
|
385
|
+
readonly points?: readonly LibraryPoint[];
|
|
386
|
+
/** Acceptance nodes only: the rubric lines as authored (the draft's and the display's source of truth; adoption materializes them into the node's practice assessment). */
|
|
387
|
+
readonly acceptance?: readonly LibraryAcceptanceInput[];
|
|
388
|
+
/** The station's 讲义, once the coach has taught it. */
|
|
389
|
+
readonly notes?: LibraryNodeNotes;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* One acceptance line as authored: a bare line the coach judges, or
|
|
393
|
+
* `{ line, ref }` — a machine-checked line that auto-passes when the
|
|
394
|
+
* referenced assessment's latest sitting passes the roadmap's pass ratio.
|
|
395
|
+
*/
|
|
396
|
+
export type LibraryAcceptanceInput = string | {
|
|
397
|
+
readonly line: string;
|
|
398
|
+
readonly ref?: string;
|
|
399
|
+
};
|
|
400
|
+
/** One rubric line of an acceptance station, with its stable positional id. */
|
|
401
|
+
export interface LibraryAcceptanceLine {
|
|
402
|
+
/** Stable id: L + 1-based position — verdicts name it, so a replan rewording the line never orphans history. */
|
|
403
|
+
readonly id: string;
|
|
404
|
+
readonly line: string;
|
|
405
|
+
/** The assessment that machine-checks this line. */
|
|
406
|
+
readonly ref?: string;
|
|
407
|
+
}
|
|
408
|
+
/** An acceptance station's rubric: its acceptance lines with stable positional ids (L1, L2, …). */
|
|
409
|
+
export declare function acceptanceLines(node: {
|
|
410
|
+
acceptance?: readonly LibraryAcceptanceInput[];
|
|
411
|
+
}): LibraryAcceptanceLine[];
|
|
412
|
+
/** A roadmap — the adopted path (`roadmaps/<key>.json`) or a draft (`roadmaps/<key>.draft.json`). */
|
|
413
|
+
export interface LibraryRoadmap {
|
|
414
|
+
/**
|
|
415
|
+
* The textbook whose path this is. Always present on anything written
|
|
416
|
+
* since the book-paths cut; a legacy project-level path (absent) is never
|
|
417
|
+
* read back.
|
|
418
|
+
*/
|
|
419
|
+
readonly primaryResourceId?: string;
|
|
420
|
+
readonly status: 'draft' | 'adopted';
|
|
421
|
+
readonly nodes: readonly RoadmapNode[];
|
|
422
|
+
/** The pass threshold (default {@link DEFAULT_PASS_RATIO}). */
|
|
423
|
+
readonly passRatio?: number;
|
|
424
|
+
/** The coach's one-line pacing note on the draft card (「每周 3 章,约 4 周」). */
|
|
425
|
+
readonly note?: string;
|
|
426
|
+
readonly generatedAt: string;
|
|
427
|
+
readonly adoptedAt?: string;
|
|
428
|
+
}
|
|
429
|
+
/** What a question aims at. */
|
|
430
|
+
export interface LibraryQuestionTarget {
|
|
431
|
+
readonly kind: 'marked' | 'asked' | 'missed' | 'topic';
|
|
432
|
+
/** The cards it probes (required for marked / asked / missed). */
|
|
433
|
+
readonly cardIds?: readonly string[];
|
|
434
|
+
}
|
|
435
|
+
/** What a question rests on. */
|
|
436
|
+
export interface LibraryQuestionEvidence {
|
|
437
|
+
readonly kind: 'photo' | 'web' | 'knowledge';
|
|
438
|
+
/** photo: the page whose text holds the quote. */
|
|
439
|
+
readonly pageId?: string;
|
|
440
|
+
/** web: where the quote is from. */
|
|
441
|
+
readonly url?: string;
|
|
442
|
+
/** photo / web: the passage, verbatim. */
|
|
443
|
+
readonly quote?: string;
|
|
444
|
+
}
|
|
445
|
+
/** One quiz question. */
|
|
446
|
+
export interface LibraryQuizQuestion {
|
|
447
|
+
readonly id: string;
|
|
448
|
+
/** `choice` carries options; `fill` / `free` are open answers. */
|
|
449
|
+
readonly kind: 'choice' | 'fill' | 'free';
|
|
450
|
+
readonly prompt: string;
|
|
451
|
+
readonly options?: readonly string[];
|
|
452
|
+
readonly answer: string;
|
|
453
|
+
/** Why the answer is right — shown after answering. */
|
|
454
|
+
readonly explanation?: string;
|
|
455
|
+
/** The gated station's point this question probes (a point id) — required once the station carries points; mastery aggregates by it. */
|
|
456
|
+
readonly point?: string;
|
|
457
|
+
/** retake papers: the question this one re-asks. */
|
|
458
|
+
readonly origin?: {
|
|
459
|
+
readonly assessmentId: string;
|
|
460
|
+
readonly questionId: string;
|
|
461
|
+
};
|
|
462
|
+
readonly target: LibraryQuestionTarget;
|
|
463
|
+
readonly evidence: LibraryQuestionEvidence;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Where a quiz's questions may come from, as the learner chose it: `mine` —
|
|
467
|
+
* only the learner's own cards (marked, asked, missed); `book` — only the
|
|
468
|
+
* chapter's content; `mixed` — both.
|
|
469
|
+
*/
|
|
470
|
+
export type LibraryQuizFocus = 'mine' | 'book' | 'mixed';
|
|
471
|
+
/**
|
|
472
|
+
* The graded thing (`assessments/<id>.json`). `quiz` carries questions;
|
|
473
|
+
* `practice` carries an acceptance station's materialized rubric (the node's
|
|
474
|
+
* `acceptance` stays the authoring source; this snapshot is what verdicts
|
|
475
|
+
* name, so a replan rewording the lines never orphans the station's history).
|
|
476
|
+
* Scope: a node (its gate's paper), a resource (a book-level paper), or
|
|
477
|
+
* nothing (free practice). A station's paper history chains by
|
|
478
|
+
* `supersededBy` (the newest is the current one).
|
|
479
|
+
*/
|
|
480
|
+
export interface LibraryAssessment {
|
|
481
|
+
readonly id: string;
|
|
482
|
+
/** quiz: a paper; practice: an acceptance rubric; retake: a station's open misses, re-asked (never counts toward passing). */
|
|
483
|
+
readonly type: 'quiz' | 'practice' | 'retake';
|
|
484
|
+
readonly scope: {
|
|
485
|
+
/** The roadmap node this assessment gates, when it is a node assessment. */
|
|
486
|
+
readonly nodeId?: string;
|
|
487
|
+
/** The resource whose content is quizzed; absent + nodeId present = a project-path station, both absent = free practice. */
|
|
488
|
+
readonly resourceId?: string;
|
|
489
|
+
};
|
|
490
|
+
/** quiz: the learner's chosen source (absent on assessments stored before the choice existed = mixed). */
|
|
491
|
+
readonly focus?: LibraryQuizFocus;
|
|
492
|
+
/** quiz: the questions. */
|
|
493
|
+
readonly questions?: readonly LibraryQuizQuestion[];
|
|
494
|
+
/** practice: the materialized rubric (stable line ids). */
|
|
495
|
+
readonly rubric?: readonly LibraryAcceptanceLine[];
|
|
496
|
+
/** The assessment that replaced this one (a station's newer paper). */
|
|
497
|
+
readonly supersededBy?: string;
|
|
498
|
+
readonly createdAt: string;
|
|
499
|
+
}
|
|
500
|
+
/** One graded answer, the eval family's `dataseek.verdict/1` shape (+ the void flag). */
|
|
501
|
+
export interface LibraryVerdict {
|
|
502
|
+
/** The question id or rubric line id this verdict judges. */
|
|
503
|
+
readonly task: string;
|
|
504
|
+
readonly criterion: string;
|
|
505
|
+
readonly pass: boolean;
|
|
506
|
+
/** Partial credit (defaults to pass ? 1 : 0). */
|
|
507
|
+
readonly ratio?: number;
|
|
508
|
+
readonly evidence: string;
|
|
509
|
+
/** auto: the machine's verdict on a ref acceptance line (never accepted from a caller). */
|
|
510
|
+
readonly by: 'llm' | 'self' | 'user' | 'auto';
|
|
511
|
+
/** The learner flagged the question as broken (「这题有问题」): it counts for nothing. */
|
|
512
|
+
readonly void?: boolean;
|
|
513
|
+
/** A missed question with no target card: the card to create for it. */
|
|
514
|
+
readonly missedCard?: {
|
|
515
|
+
readonly term: string;
|
|
516
|
+
readonly gloss: string;
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
/** One graded sitting (`attempts.jsonl` line). */
|
|
520
|
+
export interface LibraryAttempt {
|
|
521
|
+
readonly id: string;
|
|
522
|
+
/** The assessment judged. Always set on v5 writes. */
|
|
523
|
+
readonly assessmentId?: string;
|
|
524
|
+
/**
|
|
525
|
+
* Legacy v4 key: the acceptance station accepted, before practice rubrics
|
|
526
|
+
* materialized as assessments. Read compatibility only — new attempts never
|
|
527
|
+
* carry it; readers resolve it through the node's current practice
|
|
528
|
+
* assessment.
|
|
529
|
+
*/
|
|
530
|
+
readonly nodeId?: string;
|
|
531
|
+
readonly verdicts: readonly LibraryVerdict[];
|
|
532
|
+
/** Credit over the non-void verdicts — the node's unlock input. */
|
|
533
|
+
readonly ratio: number;
|
|
534
|
+
readonly at: string;
|
|
535
|
+
}
|
|
536
|
+
/** One card a sitting changed. */
|
|
537
|
+
export interface LibraryCardUpdate {
|
|
538
|
+
readonly id: string;
|
|
539
|
+
readonly term: string;
|
|
540
|
+
readonly weakness: number;
|
|
541
|
+
readonly mastery: LibraryMastery;
|
|
542
|
+
readonly created?: boolean;
|
|
543
|
+
}
|
|
544
|
+
/** What an assess call changed, reported back to the caller. */
|
|
545
|
+
export interface LibraryAssessResult {
|
|
546
|
+
readonly attempt: LibraryAttempt;
|
|
547
|
+
readonly cardUpdates: readonly LibraryCardUpdate[];
|
|
548
|
+
/** Nodes that left `locked` because of this attempt. */
|
|
549
|
+
readonly unlocked: readonly string[];
|
|
550
|
+
/** The node this assessment gated, when it completed. */
|
|
551
|
+
readonly completed?: string;
|
|
552
|
+
readonly passRatio: number;
|
|
553
|
+
/** Knowledge points whose state this sitting moved. */
|
|
554
|
+
readonly pointChanges?: readonly LibraryPointChange[];
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* A host attachment's durable reference — the photo is never copied (the
|
|
558
|
+
* attachment store is content-addressed), and these fields are what
|
|
559
|
+
* `attachments.readImage` verifies against.
|
|
560
|
+
*/
|
|
561
|
+
export interface LibraryAttachmentRef {
|
|
562
|
+
readonly attachmentId: string;
|
|
563
|
+
readonly mediaType: string;
|
|
564
|
+
readonly bytes: number;
|
|
565
|
+
readonly width: number;
|
|
566
|
+
readonly height: number;
|
|
567
|
+
}
|
|
568
|
+
/** One mark the learner made on a page. */
|
|
569
|
+
export interface LibraryMark {
|
|
570
|
+
/** The underlined word / phrase / concept, as printed. */
|
|
571
|
+
readonly text: string;
|
|
572
|
+
/** The sentence it sits in, as printed. */
|
|
573
|
+
readonly sentence?: string;
|
|
574
|
+
/** The learner's handwritten gloss beside it, transcribed. */
|
|
575
|
+
readonly handNote?: string;
|
|
576
|
+
/** The recognizer's in-context meaning (a word) or one-line explanation (a concept). */
|
|
577
|
+
readonly gloss?: string;
|
|
578
|
+
/** The recognizer could not tell (a strike-through? a stray line?) — never auto-carded. */
|
|
579
|
+
readonly uncertain?: boolean;
|
|
580
|
+
}
|
|
581
|
+
/** One photographed page (a `pages/<resourceId>.jsonl` line). */
|
|
582
|
+
export interface LibraryPage {
|
|
583
|
+
readonly id: string;
|
|
584
|
+
/** The thick resource photographed. */
|
|
585
|
+
readonly resourceId: string;
|
|
586
|
+
readonly nodeId?: string;
|
|
587
|
+
/** What the learner calls it (「p.16」). */
|
|
588
|
+
readonly label?: string;
|
|
589
|
+
readonly attachment: LibraryAttachmentRef;
|
|
590
|
+
/** The page's recognized printed text — a photo-evidence quote is checked against it. */
|
|
591
|
+
readonly text: string;
|
|
592
|
+
readonly marks: readonly LibraryMark[];
|
|
593
|
+
/** The cards this page's marks became or merged into. */
|
|
594
|
+
readonly cardIds: readonly string[];
|
|
595
|
+
readonly at: string;
|
|
596
|
+
}
|
|
597
|
+
/** A card is a word (language projects) or a concept (anything else). */
|
|
598
|
+
export type LibraryCardKind = 'word' | 'concept';
|
|
599
|
+
/** Where a card's evidence of weakness came from. */
|
|
600
|
+
export type LibraryCardSourceType = 'photo' | 'chat' | 'quiz' | 'manual';
|
|
601
|
+
/** One source stacked on a card. */
|
|
602
|
+
export interface LibraryCardSource {
|
|
603
|
+
readonly type: LibraryCardSourceType;
|
|
604
|
+
/** photo: the page id; quiz: the assessment id; chat: the session id when known. */
|
|
605
|
+
readonly ref?: string;
|
|
606
|
+
/** What happened, in a few words (「说老是记混」「T/F/NG 第 9 题」). */
|
|
607
|
+
readonly note?: string;
|
|
608
|
+
/** This source raised the weakness by one (an undo lowers it back). */
|
|
609
|
+
readonly weakened?: boolean;
|
|
610
|
+
readonly at: string;
|
|
611
|
+
}
|
|
612
|
+
/** One sentence the card was met in. */
|
|
613
|
+
export interface LibraryCardContext {
|
|
614
|
+
readonly sentence: string;
|
|
615
|
+
readonly resourceId?: string;
|
|
616
|
+
readonly pageId?: string;
|
|
617
|
+
readonly nodeId?: string;
|
|
618
|
+
}
|
|
619
|
+
/** One card's review mastery: the Leitner box, the next review moment, the streak. */
|
|
620
|
+
export interface LibraryMastery {
|
|
621
|
+
readonly box: number;
|
|
622
|
+
readonly dueAt: string;
|
|
623
|
+
readonly streak: number;
|
|
624
|
+
}
|
|
625
|
+
/** One knowledge card (知识卡片). */
|
|
626
|
+
export interface LibraryCard {
|
|
627
|
+
readonly id: string;
|
|
628
|
+
readonly kind: LibraryCardKind;
|
|
629
|
+
readonly term: string;
|
|
630
|
+
/** Other spellings / names the same card answers to (instalments, 不应期). */
|
|
631
|
+
readonly aliases: readonly string[];
|
|
632
|
+
/** The meaning: the in-context sense for a word, a one-line explanation for a concept. */
|
|
633
|
+
readonly gloss: string;
|
|
634
|
+
/** The learner's own handwritten gloss, kept apart from the model's. */
|
|
635
|
+
readonly userGloss?: string;
|
|
636
|
+
readonly contexts: readonly LibraryCardContext[];
|
|
637
|
+
/** Hung on a resource when it came from one; absent = project-level (「不挂书」). */
|
|
638
|
+
readonly resourceId?: string;
|
|
639
|
+
readonly sources: readonly LibraryCardSource[];
|
|
640
|
+
/** 0–3: how often the learner got stuck here (asked again, answered wrong). */
|
|
641
|
+
readonly weakness: number;
|
|
642
|
+
readonly mastery: LibraryMastery;
|
|
643
|
+
readonly status: 'active' | 'mastered' | 'deleted';
|
|
644
|
+
readonly createdAt: string;
|
|
645
|
+
readonly updatedAt: string;
|
|
646
|
+
}
|
|
647
|
+
/** One card to record — the shared input of `library_cards` and `library_page`. */
|
|
648
|
+
export interface LibraryCardInput {
|
|
649
|
+
readonly kind: LibraryCardKind;
|
|
650
|
+
readonly term: string;
|
|
651
|
+
readonly aliases?: readonly string[];
|
|
652
|
+
readonly gloss: string;
|
|
653
|
+
readonly userGloss?: string;
|
|
654
|
+
readonly context?: {
|
|
655
|
+
readonly sentence: string;
|
|
656
|
+
readonly pageId?: string;
|
|
657
|
+
readonly nodeId?: string;
|
|
658
|
+
};
|
|
659
|
+
readonly source: {
|
|
660
|
+
readonly type: LibraryCardSourceType;
|
|
661
|
+
readonly ref?: string;
|
|
662
|
+
readonly note?: string;
|
|
663
|
+
};
|
|
664
|
+
/** Hang it on this resource (defaults to the call's resource, when one applies). */
|
|
665
|
+
readonly resourceId?: string;
|
|
666
|
+
/** The learner showed they are stuck: raise weakness by one. */
|
|
667
|
+
readonly weaken?: boolean;
|
|
668
|
+
/** Decide a near-duplicate: merge into this existing card … */
|
|
669
|
+
readonly mergeInto?: string;
|
|
670
|
+
/** … or keep it as a new card on purpose. */
|
|
671
|
+
readonly asNew?: boolean;
|
|
672
|
+
}
|
|
673
|
+
/** A card the store did not write because it looks like an existing one. */
|
|
674
|
+
export interface LibraryCardPending {
|
|
675
|
+
readonly index: number;
|
|
676
|
+
readonly term: string;
|
|
677
|
+
readonly candidates: readonly {
|
|
678
|
+
readonly id: string;
|
|
679
|
+
readonly term: string;
|
|
680
|
+
readonly gloss: string;
|
|
681
|
+
}[];
|
|
682
|
+
}
|
|
683
|
+
/** What a card write did. */
|
|
684
|
+
export interface LibraryCardWriteResult {
|
|
685
|
+
readonly added: readonly LibraryCard[];
|
|
686
|
+
readonly merged: readonly LibraryCard[];
|
|
687
|
+
/** Near-duplicates held back — resend each with mergeInto or asNew. */
|
|
688
|
+
readonly pending: readonly LibraryCardPending[];
|
|
689
|
+
}
|
|
690
|
+
/** The filters `listCards` (the sidebar and `library_read · cards`) understands. */
|
|
691
|
+
export interface LibraryCardQuery {
|
|
692
|
+
readonly projectId: string;
|
|
693
|
+
readonly query?: string;
|
|
694
|
+
readonly resourceId?: string;
|
|
695
|
+
/** Cards met in this node (a context or a source page on it). */
|
|
696
|
+
readonly nodeId?: string;
|
|
697
|
+
readonly kind?: LibraryCardKind;
|
|
698
|
+
readonly source?: LibraryCardSourceType;
|
|
699
|
+
readonly weakMin?: number;
|
|
700
|
+
readonly due?: boolean;
|
|
701
|
+
/** Include mastered cards (default: active only). */
|
|
702
|
+
readonly includeMastered?: boolean;
|
|
703
|
+
readonly limit?: number;
|
|
704
|
+
/** `weak` (default): weakness, then due date; `recent`: newest first; `due`: due date. */
|
|
705
|
+
readonly sort?: 'weak' | 'recent' | 'due';
|
|
706
|
+
}
|
|
707
|
+
/** A learner's self-rating on a review flashcard. */
|
|
708
|
+
export type LibraryReviewRating = 'forgot' | 'fuzzy' | 'recalled';
|
|
709
|
+
/** Which project (and optionally which resource) one session works on. */
|
|
710
|
+
export interface LibraryBinding {
|
|
711
|
+
readonly projectId: string;
|
|
712
|
+
readonly resourceId?: string;
|
|
713
|
+
/** The project this session was bound to before a switch — what was said there is not this project's material. */
|
|
714
|
+
readonly previousProjectId?: string;
|
|
715
|
+
}
|
|
716
|
+
/** The audit ledger's event types (`events.jsonl`, append-only). */
|
|
717
|
+
export type LibraryEventType = 'resource.added' | 'roadmap.drafted' | 'roadmap.adopted' | 'page.recorded' | 'cards.recorded' | 'card.edited' | 'quiz.created' | 'attempt.assessed' | 'node.completed' | 'node.unlocked' | 'node.noted' | 'point.changed';
|
|
718
|
+
/** One event: a timestamp, a type, and a small summary per type. */
|
|
719
|
+
export interface LibraryEvent {
|
|
720
|
+
readonly at: string;
|
|
721
|
+
readonly type: LibraryEventType;
|
|
722
|
+
/** The object the event is about (a resource / assessment / node / page id). */
|
|
723
|
+
readonly id?: string;
|
|
724
|
+
/** Its display name, so a reader needs no join (cards.recorded: the terms). */
|
|
725
|
+
readonly title?: string;
|
|
726
|
+
readonly resourceId?: string;
|
|
727
|
+
/** cards.recorded: where they came from (the 「教练最近记下」 filter). */
|
|
728
|
+
readonly source?: LibraryCardSourceType;
|
|
729
|
+
/** cards.recorded from a chat: the session that noted them (its sidebar toasts them). */
|
|
730
|
+
readonly session?: string;
|
|
731
|
+
/** cards.recorded: the card ids, so a toast can undo them. */
|
|
732
|
+
readonly ids?: readonly string[];
|
|
733
|
+
/** Small numeric summaries (nodes / questions / added / merged). */
|
|
734
|
+
readonly count?: number;
|
|
735
|
+
readonly count2?: number;
|
|
736
|
+
readonly ratio?: number;
|
|
737
|
+
/** point.changed: the station and the states. */
|
|
738
|
+
readonly nodeId?: string;
|
|
739
|
+
readonly from?: LibraryPointState;
|
|
740
|
+
readonly to?: LibraryPointState;
|
|
741
|
+
/** point.changed: why, in one line (「2 题待订正」「教练备注:…」). */
|
|
742
|
+
readonly detail?: string;
|
|
743
|
+
}
|
|
744
|
+
/** The event vocabulary, for tests and for writers who must not invent types. */
|
|
745
|
+
export declare const LIBRARY_EVENT_TYPES: readonly LibraryEventType[];
|
|
746
|
+
/** Ids become path segments — keep them inside a safe alphabet (mission rule). */
|
|
747
|
+
export declare const LIBRARY_ID_PATTERN: RegExp;
|
|
748
|
+
/** Whether an id is usable as a path segment. */
|
|
749
|
+
export declare function isLibraryId(id: string): boolean;
|
|
750
|
+
/** A native fs failure's message, with its code, for the wire. */
|
|
751
|
+
export declare function libraryErrorMessage(error: unknown): string;
|
|
752
|
+
//# sourceMappingURL=types.d.ts.map
|