@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.
Files changed (136) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.en.md +49 -0
  3. package/README.i18n.yaml +6 -0
  4. package/README.md +48 -2
  5. package/cordis.patch.yml +17 -0
  6. package/lib/client.js +66276 -0
  7. package/lib/index.js +3318 -0
  8. package/lib/profiles-BXM8xNUf.js +1608 -0
  9. package/lib/profiles-BrbeM-bi.js +1635 -0
  10. package/lib/profiles-CFrPQdfO.js +1692 -0
  11. package/lib/profiles-DxtzIUvd.js +1632 -0
  12. package/lib/profiles-O0ddEit3.js +1631 -0
  13. package/lib/profiles-Oj-zBAY9.js +1692 -0
  14. package/lib/profiles-gIEviEuO.js +1610 -0
  15. package/lib/store-CIcZgTsg.js +1456 -0
  16. package/lib/store-DfYOmS44.js +1418 -0
  17. package/lib/tool.js +1597 -0
  18. package/lib/typert.host.d.ts +3 -0
  19. package/lib/typert.host.js +3811 -0
  20. package/lib/typert.remote-client.d.ts +137 -0
  21. package/lib/typert.remote-client.js +3077 -0
  22. package/lib/types/cards.d.ts +41 -0
  23. package/lib/types/cards.js +111 -0
  24. package/lib/types/client/LibraryPane.d.ts +26 -0
  25. package/lib/types/client/LibraryPane.js +117 -0
  26. package/lib/types/client/bind/BindChip.d.ts +33 -0
  27. package/lib/types/client/bind/BindChip.js +102 -0
  28. package/lib/types/client/contract.d.ts +164 -0
  29. package/lib/types/client/contract.js +1 -0
  30. package/lib/types/client/covers.d.ts +36 -0
  31. package/lib/types/client/covers.js +66 -0
  32. package/lib/types/client/definition.d.ts +27 -0
  33. package/lib/types/client/definition.js +32 -0
  34. package/lib/types/client/icons-local.d.ts +41 -0
  35. package/lib/types/client/icons-local.js +25 -0
  36. package/lib/types/client/icons.d.ts +27 -0
  37. package/lib/types/client/icons.js +11 -0
  38. package/lib/types/client/index.d.ts +35 -0
  39. package/lib/types/client/index.js +219 -0
  40. package/lib/types/client/judge.d.ts +70 -0
  41. package/lib/types/client/judge.js +262 -0
  42. package/lib/types/client/locales.d.ts +553 -0
  43. package/lib/types/client/locales.js +1076 -0
  44. package/lib/types/client/look.d.ts +63 -0
  45. package/lib/types/client/look.js +64 -0
  46. package/lib/types/client/math.d.ts +24 -0
  47. package/lib/types/client/math.js +47 -0
  48. package/lib/types/client/mistakes.d.ts +21 -0
  49. package/lib/types/client/mistakes.js +28 -0
  50. package/lib/types/client/preset-visibility.d.ts +109 -0
  51. package/lib/types/client/preset-visibility.js +123 -0
  52. package/lib/types/client/quiz-drafts.d.ts +29 -0
  53. package/lib/types/client/quiz-drafts.js +50 -0
  54. package/lib/types/client/store.d.ts +109 -0
  55. package/lib/types/client/store.js +44 -0
  56. package/lib/types/client/titles.d.ts +17 -0
  57. package/lib/types/client/titles.js +23 -0
  58. package/lib/types/client/ui.d.ts +96 -0
  59. package/lib/types/client/ui.js +124 -0
  60. package/lib/types/client/views/book.d.ts +57 -0
  61. package/lib/types/client/views/book.js +164 -0
  62. package/lib/types/client/views/cards.d.ts +17 -0
  63. package/lib/types/client/views/cards.js +53 -0
  64. package/lib/types/client/views/home.d.ts +26 -0
  65. package/lib/types/client/views/home.js +263 -0
  66. package/lib/types/client/views/lesson.d.ts +42 -0
  67. package/lib/types/client/views/lesson.js +146 -0
  68. package/lib/types/client/views/path.d.ts +32 -0
  69. package/lib/types/client/views/path.js +64 -0
  70. package/lib/types/client/views/quiz.d.ts +42 -0
  71. package/lib/types/client/views/quiz.js +366 -0
  72. package/lib/types/client/views/station-quiz.d.ts +43 -0
  73. package/lib/types/client/views/station-quiz.js +203 -0
  74. package/lib/types/client/views/station.d.ts +18 -0
  75. package/lib/types/client/views/station.js +142 -0
  76. package/lib/types/image-refs.d.ts +35 -0
  77. package/lib/types/image-refs.js +71 -0
  78. package/lib/types/index.d.ts +51 -0
  79. package/lib/types/index.js +31 -0
  80. package/lib/types/mastery.d.ts +81 -0
  81. package/lib/types/mastery.js +110 -0
  82. package/lib/types/profiles.d.ts +31 -0
  83. package/lib/types/profiles.js +25 -0
  84. package/lib/types/remote.d.ts +355 -0
  85. package/lib/types/remote.js +332 -0
  86. package/lib/types/service/assess.d.ts +144 -0
  87. package/lib/types/service/assess.js +773 -0
  88. package/lib/types/service/bindings.d.ts +92 -0
  89. package/lib/types/service/bindings.js +198 -0
  90. package/lib/types/service/cards.d.ts +85 -0
  91. package/lib/types/service/cards.js +320 -0
  92. package/lib/types/service/core.d.ts +124 -0
  93. package/lib/types/service/core.js +169 -0
  94. package/lib/types/service/pages.d.ts +60 -0
  95. package/lib/types/service/pages.js +125 -0
  96. package/lib/types/service/roadmap.d.ts +99 -0
  97. package/lib/types/service/roadmap.js +284 -0
  98. package/lib/types/service/rules.d.ts +63 -0
  99. package/lib/types/service/rules.js +245 -0
  100. package/lib/types/service/shelf.d.ts +136 -0
  101. package/lib/types/service/shelf.js +422 -0
  102. package/lib/types/service.d.ts +42 -0
  103. package/lib/types/service.js +41 -0
  104. package/lib/types/store.d.ts +257 -0
  105. package/lib/types/store.js +1376 -0
  106. package/lib/types/tool-definition.d.ts +49 -0
  107. package/lib/types/tool-definition.js +49 -0
  108. package/lib/types/tool.d.ts +71 -0
  109. package/lib/types/tool.js +174 -0
  110. package/lib/types/tools/assess.d.ts +11 -0
  111. package/lib/types/tools/assess.js +82 -0
  112. package/lib/types/tools/binding.d.ts +11 -0
  113. package/lib/types/tools/binding.js +46 -0
  114. package/lib/types/tools/cards.d.ts +11 -0
  115. package/lib/types/tools/cards.js +100 -0
  116. package/lib/types/tools/kit.d.ts +215 -0
  117. package/lib/types/tools/kit.js +281 -0
  118. package/lib/types/tools/notes.d.ts +11 -0
  119. package/lib/types/tools/notes.js +40 -0
  120. package/lib/types/tools/page.d.ts +11 -0
  121. package/lib/types/tools/page.js +59 -0
  122. package/lib/types/tools/project.d.ts +11 -0
  123. package/lib/types/tools/project.js +67 -0
  124. package/lib/types/tools/quiz.d.ts +11 -0
  125. package/lib/types/tools/quiz.js +74 -0
  126. package/lib/types/tools/read.d.ts +11 -0
  127. package/lib/types/tools/read.js +127 -0
  128. package/lib/types/tools/resource.d.ts +11 -0
  129. package/lib/types/tools/resource.js +147 -0
  130. package/lib/types/tools/roadmap.d.ts +11 -0
  131. package/lib/types/tools/roadmap.js +102 -0
  132. package/lib/types/types.d.ts +752 -0
  133. package/lib/types/types.js +159 -0
  134. package/locale/en.json +6 -0
  135. package/locale/zh.json +6 -0
  136. package/package.json +135 -3
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Knowledge-point mastery — the one rule set the service, the per-turn coach
3
+ * context and the sidebar all read (proposal 2026-10-19-library-point-mastery).
4
+ * Pure: no store, no context; give it a station's node, the project's
5
+ * assessments and attempts.
6
+ *
7
+ * - A question is OPEN (待订正) when its newest non-void verdict — on its own
8
+ * paper, a re-sitting of that paper, or a retake re-asking it — failed.
9
+ * - A point is WEAK when a question is open, or a coach note is newer than
10
+ * its newest pass; UNTESTED with neither probes nor notes. Otherwise (none
11
+ * open, newest probe passed) it is MASTERED once enough evidence stands —
12
+ * at least MASTERY_MIN_RIGHT distinct questions right now, across at least
13
+ * MASTERY_MIN_SITTINGS sittings with a right answer — and PRACTICING (巩固中)
14
+ * until then: one lucky paper never masters a point.
15
+ * - Retakes (type "retake") re-ask open questions — one station's, or a
16
+ * whole project's (今日复习); each question resolves through its `origin`,
17
+ * so a cross-station retake credits every station it touched. Their
18
+ * verdicts move the points but never a station's pass record.
19
+ *
20
+ * @module @khorsheed/dsh-library/mastery
21
+ */
22
+ import type { LibraryAssessment, LibraryAttempt, LibraryPointChange, LibraryPointNote, LibraryPointState, LibraryQuizQuestion, RoadmapNode } from './types.ts';
23
+ /** Distinct questions that must stand right before a point counts as mastered. */
24
+ export declare const MASTERY_MIN_RIGHT = 3;
25
+ /** Separate sittings (papers or retakes) that must each have answered the point right. */
26
+ export declare const MASTERY_MIN_SITTINGS = 2;
27
+ /** One open question: where it came from, the question, when it was missed. */
28
+ export interface LibraryOpenMiss {
29
+ /** `${assessmentId}:${questionId}` of the ORIGINAL question (retakes resolve to it). */
30
+ readonly key: string;
31
+ readonly assessmentId: string;
32
+ readonly questionId: string;
33
+ readonly question: LibraryQuizQuestion;
34
+ /** When the newest (failing) verdict landed. */
35
+ readonly at: string;
36
+ }
37
+ /** One point's mastery, with what decided it. */
38
+ export interface LibraryPointMastery {
39
+ readonly id: string;
40
+ readonly title: string;
41
+ readonly state: LibraryPointState;
42
+ readonly open: readonly LibraryOpenMiss[];
43
+ readonly notes: readonly LibraryPointNote[];
44
+ /** The newest verdict on any question probing the point. */
45
+ readonly lastProbe?: {
46
+ readonly at: string;
47
+ readonly pass: boolean;
48
+ };
49
+ /** Distinct questions ever asked on the point, and how many of them stand right now (newest verdict passed). */
50
+ readonly asked: number;
51
+ readonly right: number;
52
+ /** Sittings in which at least one question on the point was answered right. */
53
+ readonly rightSittings: number;
54
+ /** Every sitting that touched the point, newest first: how many of its questions, how many right. */
55
+ readonly history: readonly LibraryPointSitting[];
56
+ }
57
+ /** One sitting's share of a point. */
58
+ export interface LibraryPointSitting {
59
+ readonly at: string;
60
+ /** A retake (订正) rather than a paper. */
61
+ readonly retake: boolean;
62
+ readonly total: number;
63
+ readonly right: number;
64
+ }
65
+ /** A station's mastery: every point, and every open question (pointless ones too). */
66
+ export interface LibraryStationMastery {
67
+ readonly points: readonly LibraryPointMastery[];
68
+ readonly open: readonly LibraryOpenMiss[];
69
+ }
70
+ /**
71
+ * The station's mastery from the ledger.
72
+ * @param node - the station (its points and their notes).
73
+ * @param assessments - the project's assessments (others are ignored).
74
+ * @param attempts - the project's attempts, any order.
75
+ */
76
+ export declare function stationMastery(node: RoadmapNode, assessments: readonly LibraryAssessment[], attempts: readonly LibraryAttempt[]): LibraryStationMastery;
77
+ /** The points whose state differs between two readings of the same station. */
78
+ export declare function masteryChanges(nodeId: string, before: LibraryStationMastery, after: LibraryStationMastery): LibraryPointChange[];
79
+ /** One line on why a point is where it is (event detail, context line). */
80
+ export declare function masteryReason(point: LibraryPointMastery): string;
81
+ //# sourceMappingURL=mastery.d.ts.map
@@ -0,0 +1,110 @@
1
+ /** Distinct questions that must stand right before a point counts as mastered. */
2
+ export const MASTERY_MIN_RIGHT = 3;
3
+ /** Separate sittings (papers or retakes) that must each have answered the point right. */
4
+ export const MASTERY_MIN_SITTINGS = 2;
5
+ /** Whether a verdict passes (partial credit counts at half or more). */
6
+ function passes(verdict) {
7
+ return verdict.pass && (verdict.ratio ?? 1) >= 0.5;
8
+ }
9
+ /**
10
+ * The station's mastery from the ledger.
11
+ * @param node - the station (its points and their notes).
12
+ * @param assessments - the project's assessments (others are ignored).
13
+ * @param attempts - the project's attempts, any order.
14
+ */
15
+ export function stationMastery(node, assessments, attempts) {
16
+ // The station's papers, plus every retake (a retake counts here only through questions whose origin is one of them).
17
+ const papers = new Map(assessments
18
+ .filter(assessment => assessment.scope.nodeId === node.id && assessment.type === 'quiz' && assessment.questions !== undefined)
19
+ .map(assessment => [assessment.id, assessment]));
20
+ const own = new Map([...papers, ...assessments
21
+ .filter(assessment => assessment.type === 'retake' && assessment.questions !== undefined)
22
+ .map(assessment => [assessment.id, assessment])]);
23
+ const probes = [];
24
+ for (const attempt of attempts) {
25
+ const assessment = attempt.assessmentId === undefined ? undefined : own.get(attempt.assessmentId);
26
+ if (assessment === undefined)
27
+ continue;
28
+ for (const verdict of attempt.verdicts) {
29
+ if (verdict.void === true)
30
+ continue;
31
+ const question = assessment.questions.find(candidate => candidate.id === verdict.task);
32
+ if (question === undefined)
33
+ continue;
34
+ if (assessment.type === 'retake' && (question.origin === undefined || !papers.has(question.origin.assessmentId)))
35
+ continue;
36
+ const originId = question.origin?.assessmentId ?? assessment.id;
37
+ const originQuestion = question.origin === undefined
38
+ ? question
39
+ : own.get(question.origin.assessmentId)?.questions?.find(candidate => candidate.id === question.origin.questionId) ?? question;
40
+ probes.push({ attemptId: attempt.id, retake: assessment.type === 'retake', key: `${originId}:${question.origin?.questionId ?? question.id}`, assessmentId: originId, question: originQuestion, pass: passes(verdict), at: attempt.at });
41
+ }
42
+ }
43
+ probes.sort((a, b) => a.at.localeCompare(b.at));
44
+ const newestByKey = new Map();
45
+ for (const probe of probes)
46
+ newestByKey.set(probe.key, probe);
47
+ const open = [...newestByKey.values()]
48
+ .filter(probe => !probe.pass)
49
+ .map(probe => ({ key: probe.key, assessmentId: probe.assessmentId, questionId: probe.question.id, question: probe.question, at: probe.at }))
50
+ .sort((a, b) => b.at.localeCompare(a.at));
51
+ const points = (node.points ?? []).map((point) => {
52
+ const mine = probes.filter(probe => probe.question.point === point.id);
53
+ const last = mine.at(-1);
54
+ const lastPass = [...mine].reverse().find(probe => probe.pass);
55
+ const pointOpen = open.filter(miss => miss.question.point === point.id);
56
+ const notes = point.notes ?? [];
57
+ const newestNote = notes.reduce((latest, note) => latest === undefined || note.at > latest ? note.at : latest, undefined);
58
+ const noteUnanswered = newestNote !== undefined && (lastPass === undefined || newestNote > lastPass.at);
59
+ const newest = new Map();
60
+ for (const probe of mine)
61
+ newest.set(probe.key, probe);
62
+ const right = [...newest.values()].filter(probe => probe.pass).length;
63
+ const rightSittings = new Set(mine.filter(probe => probe.pass).map(probe => probe.attemptId)).size;
64
+ const state = last === undefined && notes.length === 0
65
+ ? 'untested'
66
+ : pointOpen.length > 0 || last?.pass !== true || noteUnanswered
67
+ ? 'weak'
68
+ : right >= MASTERY_MIN_RIGHT && rightSittings >= MASTERY_MIN_SITTINGS ? 'mastered' : 'practicing';
69
+ const sittings = new Map();
70
+ for (const probe of mine) {
71
+ const sitting = sittings.get(probe.attemptId) ?? { at: probe.at, retake: probe.retake, total: 0, right: 0 };
72
+ sittings.set(probe.attemptId, { ...sitting, total: sitting.total + 1, right: sitting.right + (probe.pass ? 1 : 0) });
73
+ }
74
+ return {
75
+ id: point.id,
76
+ title: point.title,
77
+ state,
78
+ open: pointOpen,
79
+ notes,
80
+ ...(last === undefined ? {} : { lastProbe: { at: last.at, pass: last.pass } }),
81
+ asked: newest.size,
82
+ right,
83
+ rightSittings,
84
+ history: [...sittings.values()].sort((a, b) => b.at.localeCompare(a.at)),
85
+ };
86
+ });
87
+ return { points, open };
88
+ }
89
+ /** The points whose state differs between two readings of the same station. */
90
+ export function masteryChanges(nodeId, before, after) {
91
+ const previous = new Map(before.points.map(point => [point.id, point.state]));
92
+ return after.points.flatMap(point => {
93
+ const from = previous.get(point.id) ?? 'untested';
94
+ return from === point.state ? [] : [{ nodeId, pointId: point.id, title: point.title, from, to: point.state }];
95
+ });
96
+ }
97
+ /** One line on why a point is where it is (event detail, context line). */
98
+ export function masteryReason(point) {
99
+ const parts = [];
100
+ if (point.open.length > 0)
101
+ parts.push(`${point.open.length} open miss${point.open.length === 1 ? '' : 'es'}`);
102
+ const note = point.notes.at(-1);
103
+ if (note !== undefined)
104
+ parts.push(`note "${note.term ?? note.text}"`);
105
+ if (point.state === 'mastered' && point.lastProbe !== undefined)
106
+ parts.push(`${point.right} right across ${point.rightSittings} sittings`);
107
+ if (point.state === 'practicing')
108
+ parts.push(`${point.right} right across ${point.rightSittings} sitting${point.rightSittings === 1 ? '' : 's'}; mastered at ${MASTERY_MIN_RIGHT} across ${MASTERY_MIN_SITTINGS}`);
109
+ return parts.join('; ');
110
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Subject profiles — what varies by a project's template, in one place, read
3
+ * by the service, the per-turn coach context and the sidebar alike. Plain
4
+ * data: the sidebar maps `fill` onto its judge (judge.ts), the service maps
5
+ * `markKind` onto the cards it creates.
6
+ *
7
+ * A new subject is a new profile; a subject that needs its own UI can ship as
8
+ * a companion package providing one (proposal 2026-10-19-library-point-mastery).
9
+ *
10
+ * @module @khorsheed/dsh-library/profiles
11
+ */
12
+ import type { LibraryCardKind, LibraryTemplate } from './types.ts';
13
+ /** What a template changes. */
14
+ export interface LibraryProfile {
15
+ readonly template: LibraryTemplate;
16
+ /** What a photographed mark (and a single-word miss) becomes. */
17
+ readonly markKind: LibraryCardKind;
18
+ /** Whether knowledge points are the mastery unit (a coach note lands on a point; misses wait for 订正). */
19
+ readonly pointsDriven: boolean;
20
+ /** How a fill-in is judged: by text or value with math folding, or by spelling (UK/US, plurals). */
21
+ readonly fill: 'math' | 'spelling';
22
+ /** Whether answers preview and typeset math. */
23
+ readonly math: boolean;
24
+ /** One line for the coach's per-turn context. */
25
+ readonly coachHint: string;
26
+ }
27
+ /** Every template's profile; custom follows the subject profile. */
28
+ export declare const LIBRARY_PROFILES: Readonly<Record<LibraryTemplate, LibraryProfile>>;
29
+ /** A template's profile (an unknown template reads as subject). */
30
+ export declare function profileOf(template: LibraryTemplate | undefined): LibraryProfile;
31
+ //# sourceMappingURL=profiles.d.ts.map
@@ -0,0 +1,25 @@
1
+ const SUBJECT = {
2
+ template: 'subject',
3
+ markKind: 'concept',
4
+ pointsDriven: true,
5
+ fill: 'math',
6
+ math: true,
7
+ coachHint: 'Quiz the weak and untested points first, then the practicing ones; every question names its point; math in $…$; a fill key is a value (1/2), several blanks comma-separated.',
8
+ };
9
+ /** Every template's profile; custom follows the subject profile. */
10
+ export const LIBRARY_PROFILES = {
11
+ subject: SUBJECT,
12
+ custom: { ...SUBJECT, template: 'custom' },
13
+ language: {
14
+ template: 'language',
15
+ markKind: 'word',
16
+ pointsDriven: false,
17
+ fill: 'spelling',
18
+ math: false,
19
+ coachHint: 'The learner\'s marked words first (meaning in context, collocation, spelling); a word they miss becomes a card.',
20
+ },
21
+ };
22
+ /** A template's profile (an unknown template reads as subject). */
23
+ export function profileOf(template) {
24
+ return (template === undefined ? undefined : LIBRARY_PROFILES[template]) ?? SUBJECT;
25
+ }
@@ -0,0 +1,355 @@
1
+ /**
2
+ * The library Remote: the browser-facing wire face, and nothing else.
3
+ *
4
+ * Every method is a one-line delegation to {@link LibraryService}; the value it
5
+ * returns is the BARE domain value (a refusal is a value inside the protocol's
6
+ * own result envelope, never a second `{ ok }` object nested inside the first).
7
+ * Session-bound methods take the calling session's id first (the canvas
8
+ * precedent): that session is the one bound.
9
+ *
10
+ * v5: the shelf unified — books are resources (`getResource` / `putResource` /
11
+ * `removeResource`), quizzes are assessments (`listAssessments` /
12
+ * `getAssessment`), and every scope key is `resourceId`.
13
+ *
14
+ * @module @khorsheed/dsh-library/remote
15
+ */
16
+ import type { Context } from '@deepseek-ai/cordis';
17
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
18
+ import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
19
+ import type { LibraryAssessResult, LibraryAssessment, LibraryAttachmentRef, LibraryAttempt, LibraryBinding, LibraryBookKind, LibraryCapabilities, LibraryCard, LibraryCardInput, LibraryCardQuery, LibraryCardWriteResult, LibraryColor, LibraryIcon, LibraryProjectPatch, LibraryEvent, LibraryEventType, LibraryMark, LibraryOutcome, LibraryPage, LibraryProject, LibraryProjectSummary, LibraryResource, LibraryResourceKind, LibraryResourceOrigin, LibraryReviewRating, LibraryRoadmap, LibraryTemplate, LibraryTocSource, LibraryVerdict } from './types.ts';
20
+ declare module '@deepseek-ai/cordis' {
21
+ interface Context {
22
+ libraryRemote: LibraryRemoteService;
23
+ }
24
+ }
25
+ /** Reserved for future config-driven knobs; the service owns today's config. */
26
+ export interface LibraryRemoteConfig {
27
+ }
28
+ /** The wire namespace is `library`: the browser calls `remote.library.*`. */
29
+ export declare class LibraryRemoteService extends TypertRemoteService<LibraryRemoteConfig> {
30
+ static inject: string[];
31
+ constructor(ctx: Context, _config?: LibraryRemoteConfig);
32
+ /** The service core this face delegates to. */
33
+ private get core();
34
+ /** What this composition can do: storage and attachments, with the reason — the display contract. */
35
+ capabilities(): Promise<LibraryCapabilities>;
36
+ /** Every project with its shelf progress and card tallies. */
37
+ listProjects(): Promise<{
38
+ projects: LibraryProjectSummary[];
39
+ }>;
40
+ /** One project's summary plus its resources. */
41
+ getProject(request: {
42
+ id: string;
43
+ }): Promise<LibraryOutcome<{
44
+ project: LibraryProjectSummary;
45
+ resources: LibraryResource[];
46
+ }>>;
47
+ /** Create a project. */
48
+ createProject(request: {
49
+ name: string;
50
+ template?: LibraryTemplate;
51
+ templateNote?: string;
52
+ goal?: string;
53
+ color?: LibraryColor;
54
+ icon?: LibraryIcon;
55
+ }): Promise<LibraryOutcome<{
56
+ project: LibraryProject;
57
+ }>>;
58
+ /** Change a project's look, goal or name. */
59
+ updateProject(request: {
60
+ id: string;
61
+ patch: LibraryProjectPatch;
62
+ }): Promise<LibraryOutcome<{
63
+ project: LibraryProject;
64
+ }>>;
65
+ /** Delete a project and everything it holds. */
66
+ deleteProject(request: {
67
+ id: string;
68
+ }): Promise<LibraryOutcome<{
69
+ id: string;
70
+ }>>;
71
+ /** One shelf entry, whole. */
72
+ getResource(request: {
73
+ projectId: string;
74
+ resourceId: string;
75
+ }): Promise<LibraryOutcome<{
76
+ resource: LibraryResource;
77
+ }>>;
78
+ /** A project's shelf: textbooks in study order, then links oldest first. */
79
+ listResources(request: {
80
+ projectId: string;
81
+ }): Promise<{
82
+ resources: LibraryResource[];
83
+ }>;
84
+ /** Put one entry on the shelf (a thin link from the sidebar's add form). */
85
+ putResource(request: {
86
+ projectId: string;
87
+ resourceId?: string;
88
+ title: string;
89
+ kind: LibraryResourceKind;
90
+ url?: string;
91
+ note?: string;
92
+ origin?: LibraryResourceOrigin;
93
+ seq?: number;
94
+ toc?: readonly {
95
+ title: string;
96
+ part?: string;
97
+ }[];
98
+ tocSource?: LibraryTocSource;
99
+ bookKind?: LibraryBookKind;
100
+ short?: string;
101
+ extras?: readonly {
102
+ title: string;
103
+ note?: string;
104
+ where?: string;
105
+ when?: 'before' | 'reference';
106
+ }[];
107
+ author?: string;
108
+ edition?: string;
109
+ isbn?: string;
110
+ from?: {
111
+ projectId: string;
112
+ resourceId: string;
113
+ };
114
+ }): Promise<LibraryOutcome<{
115
+ resource: LibraryResource;
116
+ }>>;
117
+ /** Switch a textbook between 知识型 and 材料型. */
118
+ setBookKind(request: {
119
+ projectId: string;
120
+ resourceId: string;
121
+ bookKind: 'knowledge' | 'material';
122
+ }): Promise<LibraryOutcome<{
123
+ resource: LibraryResource;
124
+ }>>;
125
+ /** Move a textbook to another place in the study order. */
126
+ moveResource(request: {
127
+ projectId: string;
128
+ resourceId: string;
129
+ seq: number;
130
+ }): Promise<LibraryOutcome<{
131
+ resources: LibraryResource[];
132
+ }>>;
133
+ /** Remove a shelf entry (its cards stay, unhung). */
134
+ removeResource(request: {
135
+ projectId: string;
136
+ resourceId: string;
137
+ }): Promise<LibraryOutcome<{
138
+ id: string;
139
+ }>>;
140
+ /** One textbook's adopted roadmap and its pending draft. */
141
+ getRoadmap(request: {
142
+ projectId: string;
143
+ resourceId: string;
144
+ }): Promise<{
145
+ roadmap?: LibraryRoadmap;
146
+ draft?: LibraryRoadmap;
147
+ }>;
148
+ /** The learner's 采用. */
149
+ adoptRoadmap(request: {
150
+ projectId: string;
151
+ resourceId: string;
152
+ }): Promise<LibraryOutcome<{
153
+ roadmap: LibraryRoadmap;
154
+ }>>;
155
+ /** Drop a draft. */
156
+ discardDraft(request: {
157
+ projectId: string;
158
+ resourceId: string;
159
+ }): Promise<LibraryOutcome<{
160
+ resourceId: string;
161
+ }>>;
162
+ /** 「读完」 a read node. */
163
+ markNodeRead(request: {
164
+ projectId: string;
165
+ resourceId?: string;
166
+ nodeId: string;
167
+ }): Promise<LibraryOutcome<{
168
+ roadmap: LibraryRoadmap;
169
+ unlocked: string[];
170
+ }>>;
171
+ /** One resource's pages, optionally one node's. */
172
+ listPages(request: {
173
+ projectId: string;
174
+ resourceId: string;
175
+ nodeId?: string;
176
+ }): Promise<{
177
+ pages: LibraryPage[];
178
+ }>;
179
+ /** One page's photo, base64 — whitelisted to recorded pages. */
180
+ readPagePhoto(request: {
181
+ projectId: string;
182
+ pageId: string;
183
+ }): Promise<LibraryOutcome<{
184
+ data: string;
185
+ mediaType: string;
186
+ }>>;
187
+ /** Save a browser-uploaded photo into the attachment store. */
188
+ savePhoto(request: {
189
+ data: string;
190
+ mediaType: string;
191
+ }): Promise<LibraryOutcome<{
192
+ attachment: LibraryAttachmentRef;
193
+ }>>;
194
+ /** Record a recognized page and card its marks. */
195
+ recordPage(request: {
196
+ projectId: string;
197
+ resourceId: string;
198
+ nodeId?: string;
199
+ label?: string;
200
+ attachment: LibraryAttachmentRef;
201
+ text: string;
202
+ marks: readonly LibraryMark[];
203
+ }): Promise<LibraryOutcome<{
204
+ page: LibraryPage;
205
+ cards: LibraryCardWriteResult;
206
+ uncertain: string[];
207
+ }>>;
208
+ /** Cards, filtered and sorted. */
209
+ listCards(request: LibraryCardQuery): Promise<{
210
+ cards: LibraryCard[];
211
+ total: number;
212
+ }>;
213
+ /** Due cards across projects (or one). */
214
+ dueCards(request: {
215
+ projectId?: string;
216
+ limit?: number;
217
+ }): Promise<{
218
+ cards: (LibraryCard & {
219
+ projectId: string;
220
+ })[];
221
+ }>;
222
+ /** Record cards by hand. */
223
+ writeCards(request: {
224
+ projectId: string;
225
+ resourceId?: string;
226
+ cards: readonly LibraryCardInput[];
227
+ }): Promise<LibraryOutcome<LibraryCardWriteResult>>;
228
+ /** Edit one card. */
229
+ editCard(request: {
230
+ projectId: string;
231
+ cardId: string;
232
+ patch: {
233
+ term?: string;
234
+ gloss?: string;
235
+ userGloss?: string;
236
+ aliases?: readonly string[];
237
+ status?: LibraryCard['status'];
238
+ resourceId?: string | null;
239
+ };
240
+ }): Promise<LibraryOutcome<{
241
+ card: LibraryCard;
242
+ }>>;
243
+ /** The toast's 撤销. */
244
+ undoCards(request: {
245
+ projectId: string;
246
+ ids: readonly string[];
247
+ at: string;
248
+ }): Promise<LibraryOutcome<{
249
+ removed: number;
250
+ reverted: number;
251
+ }>>;
252
+ /** One flashcard review. */
253
+ reviewCard(request: {
254
+ projectId: string;
255
+ cardId: string;
256
+ rating: LibraryReviewRating;
257
+ }): Promise<LibraryOutcome<{
258
+ card: LibraryCard;
259
+ }>>;
260
+ /** A project's assessments, optionally one resource's / node's / one type's. */
261
+ listAssessments(request: {
262
+ projectId: string;
263
+ resourceId?: string;
264
+ nodeId?: string;
265
+ type?: 'quiz' | 'practice';
266
+ }): Promise<{
267
+ assessments: LibraryAssessment[];
268
+ }>;
269
+ /** One assessment, whole. */
270
+ getAssessment(request: {
271
+ projectId: string;
272
+ assessmentId: string;
273
+ }): Promise<LibraryOutcome<{
274
+ assessment: LibraryAssessment;
275
+ }>>;
276
+ /** Grade a sitting answered in the sidebar (choice / fill judged in the page). */
277
+ putAttempt(request: {
278
+ projectId: string;
279
+ assessmentId?: string;
280
+ quizId?: string;
281
+ nodeId?: string;
282
+ resourceId?: string;
283
+ verdicts: readonly LibraryVerdict[];
284
+ }): Promise<LibraryOutcome<LibraryAssessResult>>;
285
+ /** A project's graded sittings, newest first; `nodeId` narrows to one station's history, `assessmentId` to one assessment's. */
286
+ listAttempts(request: {
287
+ projectId: string;
288
+ nodeId?: string;
289
+ assessmentId?: string;
290
+ }): Promise<{
291
+ attempts: LibraryAttempt[];
292
+ }>;
293
+ /** The calling session's binding, or null. */
294
+ boundTarget(sessionId: SessionId): Promise<LibraryBinding | null>;
295
+ /** Bind the calling session to a project / resource (null unbinds). */
296
+ bindTarget(sessionId: SessionId, request: {
297
+ binding: LibraryBinding | null;
298
+ }): Promise<LibraryOutcome<{
299
+ binding: LibraryBinding | null;
300
+ }>>;
301
+ /** One project's audit trail, newest first. */
302
+ listEvents(request: {
303
+ projectId: string;
304
+ limit?: number;
305
+ types?: readonly LibraryEventType[];
306
+ }): Promise<{
307
+ events: LibraryEvent[];
308
+ }>;
309
+ /** 「教练最近记下」 across projects. */
310
+ /** The newest knowledge-point state changes (今日 「最近的变化」). */
311
+ recentChanges(request: {
312
+ limit?: number;
313
+ }): Promise<{
314
+ changes: (LibraryEvent & {
315
+ projectId: string;
316
+ projectName: string;
317
+ })[];
318
+ }>;
319
+ /** 订正: a retake paper over a station's open misses (all, or the keys named). */
320
+ createRetake(request: {
321
+ projectId: string;
322
+ resourceId?: string;
323
+ nodeId?: string;
324
+ keys?: readonly string[];
325
+ }): Promise<LibraryOutcome<{
326
+ assessment: LibraryAssessment;
327
+ }>>;
328
+ recentRecords(request: {
329
+ limit?: number;
330
+ }): Promise<{
331
+ records: (LibraryEvent & {
332
+ projectId: string;
333
+ projectName: string;
334
+ })[];
335
+ }>;
336
+ /** The newest event's timestamp anywhere — the pane polls it to re-read (no push channel). */
337
+ latestActivity(): Promise<{
338
+ at?: string;
339
+ session?: {
340
+ id: string;
341
+ at: string;
342
+ };
343
+ }>;
344
+ /** The whole-library tally. */
345
+ stats(): Promise<{
346
+ projects: number;
347
+ books: number;
348
+ cards: number;
349
+ due: number;
350
+ quizzes: number;
351
+ attempts: number;
352
+ }>;
353
+ }
354
+ export default LibraryRemoteService;
355
+ //# sourceMappingURL=remote.d.ts.map