@octolearn/training-ui 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +413 -3
- package/THIRD-PARTY-NOTICES.md +211 -0
- package/dist/chunks/core-rJUDTpGS.js +592 -0
- package/dist/chunks/define-elements-CeSpZJXj.js +6111 -0
- package/dist/chunks/font-data-DlFp9eSH.js +8 -0
- package/dist/core.js +11 -0
- package/dist/define.js +52 -0
- package/dist/index.js +50 -0
- package/dist/octolearn-training-ui.bundle.js +6441 -0
- package/dist/types/base-element.d.ts +35 -0
- package/dist/types/content.d.ts +51 -0
- package/dist/types/core.d.ts +13 -0
- package/dist/types/d-icons.d.ts +10 -0
- package/dist/types/define-elements.d.ts +18 -0
- package/dist/types/define.d.ts +1 -0
- package/dist/types/diff.d.ts +34 -0
- package/dist/types/elements/arithmetic-question.d.ts +31 -0
- package/dist/types/elements/card.d.ts +43 -0
- package/dist/types/elements/choice-pair.d.ts +21 -0
- package/dist/types/elements/dictation-question.d.ts +51 -0
- package/dist/types/elements/feedback-toast.d.ts +29 -0
- package/dist/types/elements/feedback.d.ts +87 -0
- package/dist/types/elements/flashcard.d.ts +46 -0
- package/dist/types/elements/matching-round.d.ts +61 -0
- package/dist/types/elements/multiple-choice.d.ts +33 -0
- package/dist/types/elements/picture-question.d.ts +30 -0
- package/dist/types/elements/progress.d.ts +24 -0
- package/dist/types/elements/question-answer.d.ts +20 -0
- package/dist/types/elements/question-parts.d.ts +88 -0
- package/dist/types/elements/question.d.ts +37 -0
- package/dist/types/elements/text-answer.d.ts +57 -0
- package/dist/types/elements/tile-answer.d.ts +43 -0
- package/dist/types/elements/today-stats.d.ts +17 -0
- package/dist/types/elements/trainer-overlay.d.ts +45 -0
- package/dist/types/elements/trainer-panel.d.ts +46 -0
- package/dist/types/elements/trainer.d.ts +57 -0
- package/dist/types/elements/true-false.d.ts +37 -0
- package/dist/types/events.d.ts +53 -0
- package/dist/types/feedback-message.d.ts +34 -0
- package/dist/types/font-data.d.ts +2 -0
- package/dist/types/fonts.d.ts +9 -0
- package/dist/types/i18n/strings.d.ts +233 -0
- package/dist/types/icons.d.ts +21 -0
- package/dist/types/images.d.ts +2 -0
- package/dist/types/index.d.ts +32 -0
- package/dist/types/keys.d.ts +6 -0
- package/dist/types/session/third-party-transport.d.ts +22 -0
- package/dist/types/session/training-session.d.ts +167 -0
- package/dist/types/session/transport.d.ts +44 -0
- package/dist/types/speech.d.ts +71 -0
- package/dist/types/styles/base.d.ts +9 -0
- package/dist/types/styles/d-parts.d.ts +13 -0
- package/dist/types/styles/tokens.d.ts +95 -0
- package/dist/types/viewport.d.ts +35 -0
- package/package.json +67 -6
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { nothing, type TemplateResult } from 'lit';
|
|
2
|
+
import { QuestionElement } from './question-parts';
|
|
3
|
+
/**
|
|
4
|
+
* `<octolearn-question-answer>`: design D's "Vokabel" (or a plain question): the prompt in handwriting ("Wie heißt das auf
|
|
5
|
+
* Englisch?"), the word big with a read-aloud button, then the answer. When a new question arrives the cursor goes into the
|
|
6
|
+
* answer field (OC-188); `no-autofocus` leaves the focus where it is.
|
|
7
|
+
*
|
|
8
|
+
* @fires octolearn-answer - `{ answer, dontKnow? }`
|
|
9
|
+
* @fires octolearn-speak - cancelable, `{ text, reason: 'read', lang }`
|
|
10
|
+
*/
|
|
11
|
+
export declare class OctolearnQuestionAnswer extends QuestionElement {
|
|
12
|
+
render(): TemplateResult | typeof nothing;
|
|
13
|
+
/** A line above tiles or a Wortfragment, saying what to do. */
|
|
14
|
+
private instruction;
|
|
15
|
+
}
|
|
16
|
+
declare global {
|
|
17
|
+
interface HTMLElementTagNameMap {
|
|
18
|
+
'octolearn-question-answer': OctolearnQuestionAnswer;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
2
|
+
import { OctolearnElement } from '../base-element';
|
|
3
|
+
import type { FrontendContent, MatchingRound } from '../content';
|
|
4
|
+
import type { SpeakEventDetail } from '../events';
|
|
5
|
+
import { type TrainingLanguage, type TrainingStrings } from '../i18n/strings';
|
|
6
|
+
import { type SpeakOptions } from '../speech';
|
|
7
|
+
import type { FlashcardRevealer } from './flashcard';
|
|
8
|
+
/** The look shared by the question elements in design D. */
|
|
9
|
+
export declare const questionStyles: import("lit").CSSResult;
|
|
10
|
+
/**
|
|
11
|
+
* Base of the question elements. A new `content` is a new question: the answer field is emptied and the cursor goes into
|
|
12
|
+
* it (the first tile or button without a field); `no-autofocus` leaves the focus where it is. `speak()` reads text aloud through a cancelable `octolearn-speak` event, so a
|
|
13
|
+
* page with its own voice output can take over; otherwise the built-in SpeechController speaks and, if the browser
|
|
14
|
+
* blocks speech until the first tap, a "Abspielen" hint appears.
|
|
15
|
+
*
|
|
16
|
+
* While the visible area is short (a phone's keyboard is open) and the learner is in the answer, the question scrolls the
|
|
17
|
+
* card around it (or itself) to the top of what is visible, so the word or picture isn't hidden above the field.
|
|
18
|
+
*/
|
|
19
|
+
export declare class QuestionElement extends OctolearnElement {
|
|
20
|
+
static styles: import("lit").CSSResultGroup[];
|
|
21
|
+
/** The question as octolearn's API sends it. */
|
|
22
|
+
content?: FrontendContent;
|
|
23
|
+
/** Passed to the answer tiles: don't take the number keys. */
|
|
24
|
+
noKeys: boolean;
|
|
25
|
+
/** Fetches the answer of a Karteikarte (octolearn's web app), e.g. `content => session.revealFlashcard(content)`. */
|
|
26
|
+
revealFlashcard?: FlashcardRevealer;
|
|
27
|
+
/** Leave the focus where it is when a new question arrives (e.g. a game that keeps the keyboard). */
|
|
28
|
+
noAutofocus: boolean;
|
|
29
|
+
protected needsGesture: boolean;
|
|
30
|
+
protected readonly speech: import("..").SpeechController;
|
|
31
|
+
connectedCallback(): void;
|
|
32
|
+
disconnectedCallback(): void;
|
|
33
|
+
/** Reads text aloud, unless the page cancels the `octolearn-speak` event and speaks itself. */
|
|
34
|
+
protected speakText(text: string | null | undefined, reason: SpeakEventDetail['reason'], options?: SpeakOptions): void;
|
|
35
|
+
protected playPending(): void;
|
|
36
|
+
protected playHint(t: TrainingStrings): TemplateResult | typeof nothing;
|
|
37
|
+
protected updated(changed: PropertyValues<this>): void;
|
|
38
|
+
/** After the field took the focus (autofocus waits for the field's own update) and the layout settled. */
|
|
39
|
+
private revealWhenFocused;
|
|
40
|
+
/** The `<octolearn-card>` around the question, across shadow roots. */
|
|
41
|
+
private closestCard;
|
|
42
|
+
/** Puts the focus on the answer: the field, the first tile or the first button (e.g. when an overlay opens). */
|
|
43
|
+
focusAnswer(): void;
|
|
44
|
+
}
|
|
45
|
+
export interface AnswerSectionOptions {
|
|
46
|
+
content: FrontendContent;
|
|
47
|
+
lang: TrainingLanguage;
|
|
48
|
+
strings?: Partial<TrainingStrings>;
|
|
49
|
+
t: TrainingStrings;
|
|
50
|
+
/** Accessible name of the text field. */
|
|
51
|
+
inputLabel: string;
|
|
52
|
+
/** Show the field's name above it (D: "Auf Englisch", "Schreib das Wort"). */
|
|
53
|
+
visibleLabel?: boolean;
|
|
54
|
+
/** Accessible name of the answer tiles. */
|
|
55
|
+
choiceLabel: string;
|
|
56
|
+
inputType?: 'text' | 'number';
|
|
57
|
+
/** Language of the typed answer. */
|
|
58
|
+
inputLang?: string;
|
|
59
|
+
noKeys?: boolean;
|
|
60
|
+
/** Arithmetic: the task left of a number box. */
|
|
61
|
+
before?: TemplateResult;
|
|
62
|
+
/** A picture left of the field. */
|
|
63
|
+
media?: TemplateResult;
|
|
64
|
+
/** Karteikarte: fetches the answer. */
|
|
65
|
+
reveal?: FlashcardRevealer;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The answer part every question shares: the tiles for a multiple-choice question, the field otherwise. An unknown format
|
|
69
|
+
* shows nothing, like octolearn.
|
|
70
|
+
*/
|
|
71
|
+
export declare function answerSection(options: AnswerSectionOptions): TemplateResult | typeof nothing;
|
|
72
|
+
/** "en" → "Englisch" (in German texts), via the browser's language names; unknown codes stay as they are. */
|
|
73
|
+
export declare function languageName(code: string | null | undefined, lang: TrainingLanguage): string | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* What the card around a question shows: the format label ("Diktat", "Wähle die richtige Antwort") and, for vocabulary,
|
|
76
|
+
* the direction as chip ("Deutsch → Englisch").
|
|
77
|
+
*/
|
|
78
|
+
export declare function describeQuestion(content: FrontendContent, language?: string, overrides?: Partial<TrainingStrings>): {
|
|
79
|
+
label: string;
|
|
80
|
+
chip?: string;
|
|
81
|
+
};
|
|
82
|
+
/** What the card around a round of "Paare finden" shows: the label and the folder as chip. */
|
|
83
|
+
export declare function describeMatchingRound(round: MatchingRound, language?: string, overrides?: Partial<TrainingStrings>): {
|
|
84
|
+
label: string;
|
|
85
|
+
chip?: string;
|
|
86
|
+
};
|
|
87
|
+
/** "Wie heißt das auf Englisch?" when the target language is known, else octolearn's "Übersetze folgendes Wort:". */
|
|
88
|
+
export declare function vocabularyPrompt(content: FrontendContent, lang: TrainingLanguage, t: TrainingStrings): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type TemplateResult } from 'lit';
|
|
2
|
+
import { OctolearnElement } from '../base-element';
|
|
3
|
+
import type { FrontendContent } from '../content';
|
|
4
|
+
import type { FlashcardRevealer } from './flashcard';
|
|
5
|
+
/**
|
|
6
|
+
* `<octolearn-question>`: shows any question the API sends, picking the element by its answer format first, then by its
|
|
7
|
+
* learning mode, like octolearn's training page. The attributes of the specific elements (`settings`, `no-autospeak`,
|
|
8
|
+
* `no-autofocus`) are passed on.
|
|
9
|
+
*
|
|
10
|
+
* @fires octolearn-answer - `{ answer }`
|
|
11
|
+
* @fires octolearn-speak - see octolearn-dictation-question
|
|
12
|
+
* @fires octolearn-stop - see octolearn-dictation-question
|
|
13
|
+
* @fires octolearn-settings - see octolearn-dictation-question
|
|
14
|
+
*/
|
|
15
|
+
export declare class OctolearnQuestion extends OctolearnElement {
|
|
16
|
+
content?: FrontendContent;
|
|
17
|
+
settings: boolean;
|
|
18
|
+
noAutospeak: boolean;
|
|
19
|
+
noAutofocus: boolean;
|
|
20
|
+
/** Don't let the answer tiles take the number keys 1–9 (for a game that uses them itself). */
|
|
21
|
+
noKeys: boolean;
|
|
22
|
+
/** Fetches the answer of a Karteikarte (octolearn's web app), e.g. `content => session.revealFlashcard(content)`. */
|
|
23
|
+
revealFlashcard?: FlashcardRevealer;
|
|
24
|
+
/** The element showing the current question, if any. */
|
|
25
|
+
get questionElement(): HTMLElement | null;
|
|
26
|
+
/** Puts the focus on the answer of the current question. */
|
|
27
|
+
focusAnswer(): void;
|
|
28
|
+
/** For a dictation: puts the focus back on "Einstellungen" (after the page's voice settings close). */
|
|
29
|
+
focusSettings(): void;
|
|
30
|
+
render(): TemplateResult;
|
|
31
|
+
private renderQuestion;
|
|
32
|
+
}
|
|
33
|
+
declare global {
|
|
34
|
+
interface HTMLElementTagNameMap {
|
|
35
|
+
'octolearn-question': OctolearnQuestion;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type TemplateResult } from 'lit';
|
|
2
|
+
import { OctolearnElement } from '../base-element';
|
|
3
|
+
/**
|
|
4
|
+
* `<octolearn-text-answer>`: design D's typed answer. The big field, below it "Weiß ich nicht" and, on the right,
|
|
5
|
+
* "Enter drücken oder" + "Fertig". Enter (also a phone keyboard's Go key) sends; an empty answer is never sent and
|
|
6
|
+
* "Fertig" stays disabled until something is typed. "Weiß ich nicht" sends "?" with `dontKnow: true`, which the server
|
|
7
|
+
* grades wrong and answers with the solution. The field empties after sending.
|
|
8
|
+
*
|
|
9
|
+
* `variant="box"` is the arithmetic look: a square box for the number, with whatever is in the `before` slot (the task,
|
|
10
|
+
* "7 · 8 =") on its left.
|
|
11
|
+
*
|
|
12
|
+
* `variant="media"` puts whatever is in the `media` slot (a picture) left of the field, the buttons below both.
|
|
13
|
+
*
|
|
14
|
+
* With `hint` (a Wortfragment, OC-126: "S____e") the known letters stand big above the field, each missing one a dashed
|
|
15
|
+
* box (design D's Lückenwort look); screen readers hear "Hinweis: 6 Zeichen, beginnt mit „S“, endet mit „e“.".
|
|
16
|
+
*
|
|
17
|
+
* While the visible area is short (`short`: a phone's keyboard covers half the screen) it takes as little height as it
|
|
18
|
+
* can: a round send button at the end of the field instead of "Fertig", the visible label as placeholder, the picture
|
|
19
|
+
* small left of the field and "Weiß ich nicht" as a small text button below.
|
|
20
|
+
*
|
|
21
|
+
* @slot before - only with variant="box": shown left of the box
|
|
22
|
+
* @slot media - only with variant="media": shown left of the field
|
|
23
|
+
* @fires octolearn-answer - `{ answer, dontKnow? }`
|
|
24
|
+
*/
|
|
25
|
+
export declare class OctolearnTextAnswer extends OctolearnElement {
|
|
26
|
+
static styles: import("lit").CSSResultGroup[];
|
|
27
|
+
type: 'text' | 'number';
|
|
28
|
+
/** Accessible name of the field (visually hidden unless `visible-label`). */
|
|
29
|
+
label: string;
|
|
30
|
+
/** Show the label above the field (D: "Auf Englisch", "Schreib das Wort"). */
|
|
31
|
+
visibleLabel: boolean;
|
|
32
|
+
placeholder: string;
|
|
33
|
+
/** Language of what is typed, e.g. "en" for a vocabulary answer (spell checkers and screen readers use it). */
|
|
34
|
+
inputLang?: string;
|
|
35
|
+
variant: 'field' | 'box' | 'media';
|
|
36
|
+
/** Hide "Weiß ich nicht". */
|
|
37
|
+
noDontKnow: boolean;
|
|
38
|
+
value: string;
|
|
39
|
+
/** Wortfragment: first and last letter, "_" for each letter between. */
|
|
40
|
+
hint?: string | null;
|
|
41
|
+
private input?;
|
|
42
|
+
/** Puts the cursor into the field. */
|
|
43
|
+
focus(options?: FocusOptions): void;
|
|
44
|
+
render(): TemplateResult;
|
|
45
|
+
private renderActions;
|
|
46
|
+
/** Short: the send button is in the field; only "Weiß ich nicht", small, below it. */
|
|
47
|
+
private renderShortActions;
|
|
48
|
+
private renderHint;
|
|
49
|
+
private onInput;
|
|
50
|
+
private onSubmit;
|
|
51
|
+
private onDontKnow;
|
|
52
|
+
}
|
|
53
|
+
declare global {
|
|
54
|
+
interface HTMLElementTagNameMap {
|
|
55
|
+
'octolearn-text-answer': OctolearnTextAnswer;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
2
|
+
import { OctolearnElement } from '../base-element';
|
|
3
|
+
/**
|
|
4
|
+
* `<octolearn-tile-answer>`: design D's "Buchstabensalat" (OC-121…124): the shuffled letters or words of the answer, tapped
|
|
5
|
+
* into the right order. Above, the answer slots (the next one highlighted, the free ones dashed); below a dashed line the
|
|
6
|
+
* tiles, each keeping its place when used. Tapping a placed tile takes it out again; "Nochmal von vorn" empties the slots;
|
|
7
|
+
* "Fertig" sends once every tile is placed. Every tile is a button (touch, mouse, keyboard, no dragging, WCAG 2.5.7);
|
|
8
|
+
* with the focus inside, typing a letter places the next fitting tile, Backspace takes the last one out, Enter sends.
|
|
9
|
+
*
|
|
10
|
+
* The answer is the tiles joined with `separator`: "" for letters, " " for words. The server checks it like a typed
|
|
11
|
+
* answer.
|
|
12
|
+
*
|
|
13
|
+
* @fires octolearn-answer - `{ answer }`
|
|
14
|
+
*/
|
|
15
|
+
export declare class OctolearnTileAnswer extends OctolearnElement {
|
|
16
|
+
static styles: import("lit").CSSResultGroup[];
|
|
17
|
+
/** The shuffled tiles from the server (`content.tiles`). */
|
|
18
|
+
tiles: readonly string[] | null;
|
|
19
|
+
/** "" joins letters into a word, " " joins words into a sentence. */
|
|
20
|
+
separator: string;
|
|
21
|
+
private bank;
|
|
22
|
+
private placed;
|
|
23
|
+
private firstFreeTile?;
|
|
24
|
+
private get words();
|
|
25
|
+
get complete(): boolean;
|
|
26
|
+
/** The answer so far. */
|
|
27
|
+
get answer(): string;
|
|
28
|
+
/** Puts the focus on the first free tile, so typing works at once. */
|
|
29
|
+
focus(options?: FocusOptions): void;
|
|
30
|
+
protected willUpdate(changed: PropertyValues<this>): void;
|
|
31
|
+
render(): TemplateResult;
|
|
32
|
+
private place;
|
|
33
|
+
private takeOut;
|
|
34
|
+
private reset;
|
|
35
|
+
private submit;
|
|
36
|
+
/** A character places the first free tile that starts with it (the same case first), Backspace undoes, Enter sends. */
|
|
37
|
+
private onKeydown;
|
|
38
|
+
}
|
|
39
|
+
declare global {
|
|
40
|
+
interface HTMLElementTagNameMap {
|
|
41
|
+
'octolearn-tile-answer': OctolearnTileAnswer;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type TemplateResult } from 'lit';
|
|
2
|
+
import { OctolearnElement } from '../base-element';
|
|
3
|
+
/**
|
|
4
|
+
* `<octolearn-today-stats>`: today's right and wrong answers in design D: a small paper panel with the uppercase label
|
|
5
|
+
* and two counters, green with a check and red with a cross. Side by side when there is room, else stacked.
|
|
6
|
+
*/
|
|
7
|
+
export declare class OctolearnTodayStats extends OctolearnElement {
|
|
8
|
+
static styles: import("lit").CSSResultGroup[];
|
|
9
|
+
correct: number;
|
|
10
|
+
wrong: number;
|
|
11
|
+
render(): TemplateResult;
|
|
12
|
+
}
|
|
13
|
+
declare global {
|
|
14
|
+
interface HTMLElementTagNameMap {
|
|
15
|
+
'octolearn-today-stats': OctolearnTodayStats;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
2
|
+
import { OctolearnElement } from '../base-element';
|
|
3
|
+
import type { TrainingSession } from '../session/training-session';
|
|
4
|
+
/**
|
|
5
|
+
* `<octolearn-trainer-overlay>`: design D's question over a paused game. While `open`, a modal dialog covers the page:
|
|
6
|
+
* the game stays visible behind a dark, blurred layer with the note "Spiel pausiert", the card sits in the middle. The
|
|
7
|
+
* page behind is inert, so the keyboard and screen readers stay in the dialog; when it closes, the focus goes back to
|
|
8
|
+
* where it was.
|
|
9
|
+
*
|
|
10
|
+
* With `.session` the overlay shows an `<octolearn-trainer>` driven by that session; without one, it shows its children
|
|
11
|
+
* (e.g. your own `<octolearn-card>`). Escape fires `octolearn-close` and leaves the overlay open: the game decides
|
|
12
|
+
* whether the learner may skip the question (set `open = false`).
|
|
13
|
+
*
|
|
14
|
+
* @slot - the content when no session is set
|
|
15
|
+
* @fires octolearn-close - the learner pressed Escape (or the system's back gesture)
|
|
16
|
+
* @cssprop --octolearn-overlay-width - width of the card (default 760px)
|
|
17
|
+
*/
|
|
18
|
+
export declare class OctolearnTrainerOverlay extends OctolearnElement {
|
|
19
|
+
static styles: import("lit").CSSResultGroup[];
|
|
20
|
+
open: boolean;
|
|
21
|
+
/** The session that drives the trainer inside. */
|
|
22
|
+
session?: TrainingSession;
|
|
23
|
+
/** The note over the game (default "Spiel pausiert"); `status=""` hides it. */
|
|
24
|
+
status?: string;
|
|
25
|
+
/** Passed to the trainer: don't read a new dictation aloud by itself. */
|
|
26
|
+
noAutospeak: boolean;
|
|
27
|
+
/** Passed to the trainer: don't take the number and arrow keys. */
|
|
28
|
+
noKeys: boolean;
|
|
29
|
+
private dialog?;
|
|
30
|
+
/** Opens the overlay (same as `open = true`). */
|
|
31
|
+
show(): void;
|
|
32
|
+
/** Closes the overlay (same as `open = false`). */
|
|
33
|
+
close(): void;
|
|
34
|
+
protected updated(changed: PropertyValues<this>): void;
|
|
35
|
+
render(): TemplateResult;
|
|
36
|
+
private onKeydown;
|
|
37
|
+
private onCancel;
|
|
38
|
+
/** The browser closed the dialog anyway (it may, after repeated close requests): follow it. */
|
|
39
|
+
private onClose;
|
|
40
|
+
}
|
|
41
|
+
declare global {
|
|
42
|
+
interface HTMLElementTagNameMap {
|
|
43
|
+
'octolearn-trainer-overlay': OctolearnTrainerOverlay;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
2
|
+
import { OctolearnElement } from '../base-element';
|
|
3
|
+
import type { TrainingSession } from '../session/training-session';
|
|
4
|
+
/**
|
|
5
|
+
* `<octolearn-trainer-panel>`: design D's side panel next to a game that keeps running. On top the octolearn logo, the
|
|
6
|
+
* label "Lernfrage" and a star counter; below the question card; then the game's reward (slot `reward`, e.g. "Richtig
|
|
7
|
+
* = 1 Extraleben"); at the bottom Inkie saying how many questions are left in the round.
|
|
8
|
+
*
|
|
9
|
+
* With `.session` everything follows that session (card, stars = today's right answers, questions left); without one
|
|
10
|
+
* the panel shows its children in place of the card, and `stars` / `message` say what to show. The panel is 400 px
|
|
11
|
+
* wide by default; size it like any block element.
|
|
12
|
+
*
|
|
13
|
+
* @slot - the card when no session is set
|
|
14
|
+
* @slot reward - what the game gives for a right answer
|
|
15
|
+
*/
|
|
16
|
+
export declare class OctolearnTrainerPanel extends OctolearnElement {
|
|
17
|
+
static styles: import("lit").CSSResultGroup[];
|
|
18
|
+
/** The session that drives the panel. */
|
|
19
|
+
session?: TrainingSession;
|
|
20
|
+
/** The star counter; default: the session's right answers today. */
|
|
21
|
+
stars?: number;
|
|
22
|
+
/** Inkie's line; default: the questions left in the session's round ("Noch 2 Fragen in dieser Runde!"). */
|
|
23
|
+
message?: string;
|
|
24
|
+
/** Passed to the trainer: don't read a new dictation aloud by itself. */
|
|
25
|
+
noAutospeak: boolean;
|
|
26
|
+
/** Passed to the trainer: don't take the number and arrow keys (a running game usually needs them). */
|
|
27
|
+
noKeys: boolean;
|
|
28
|
+
/** Passed to the trainer: don't move the cursor into the answer field (the game keeps the keyboard). */
|
|
29
|
+
noAutofocus: boolean;
|
|
30
|
+
private hasReward;
|
|
31
|
+
private observed?;
|
|
32
|
+
private readonly onChange;
|
|
33
|
+
connectedCallback(): void;
|
|
34
|
+
disconnectedCallback(): void;
|
|
35
|
+
protected willUpdate(changed: PropertyValues<this>): void;
|
|
36
|
+
render(): TemplateResult;
|
|
37
|
+
/** "Noch 2 Fragen in dieser Runde!" from the session's stars; nothing while no question is asked. */
|
|
38
|
+
private roundMessage;
|
|
39
|
+
private onRewardChange;
|
|
40
|
+
private observe;
|
|
41
|
+
}
|
|
42
|
+
declare global {
|
|
43
|
+
interface HTMLElementTagNameMap {
|
|
44
|
+
'octolearn-trainer-panel': OctolearnTrainerPanel;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
2
|
+
import { OctolearnElement } from '../base-element';
|
|
3
|
+
import type { TrainingSession } from '../session/training-session';
|
|
4
|
+
/**
|
|
5
|
+
* `<octolearn-trainer>`: a whole training in design D's card, driven by a TrainingSession. It shows what the session is
|
|
6
|
+
* at: the question (any format), the feedback after an answer with "Weiter", a round of "Paare finden", or a message
|
|
7
|
+
* while loading, after an error ("Erneut versuchen"), when everything due is done ("Freiwillig weiterüben") and when
|
|
8
|
+
* nothing is left. Answers, "Weiter" and the pairs go to the session; the stars follow its round.
|
|
9
|
+
*
|
|
10
|
+
* Create the session with `holdForFeedback: true` to see the feedback in the card. Don't call session.answer() yourself
|
|
11
|
+
* as well: the trainer does. The question's events (octolearn-answer, octolearn-speak, …) still bubble out.
|
|
12
|
+
*
|
|
13
|
+
* ```js
|
|
14
|
+
* const session = new TrainingSession(transport, { holdForFeedback: true });
|
|
15
|
+
* trainer.session = session;
|
|
16
|
+
* await session.start();
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare class OctolearnTrainer extends OctolearnElement {
|
|
20
|
+
static styles: import("lit").CSSResultGroup[];
|
|
21
|
+
/** The session that drives the trainer. */
|
|
22
|
+
session?: TrainingSession;
|
|
23
|
+
/** Don't read a new dictation aloud by itself. */
|
|
24
|
+
noAutospeak: boolean;
|
|
25
|
+
/** Don't move the cursor into the answer field of a new question. */
|
|
26
|
+
noAutofocus: boolean;
|
|
27
|
+
/** Don't take the number and arrow keys (for a game that needs them itself). */
|
|
28
|
+
noKeys: boolean;
|
|
29
|
+
/** Show "Einstellungen" in a dictation (fires octolearn-settings). */
|
|
30
|
+
settings: boolean;
|
|
31
|
+
/** For the side panel: a card without header and Inkie, with a light shadow. */
|
|
32
|
+
compact: boolean;
|
|
33
|
+
/** A lighter shadow, for a trainer that sits on the page instead of over a game. */
|
|
34
|
+
flat: boolean;
|
|
35
|
+
private observed?;
|
|
36
|
+
private shownFeedback;
|
|
37
|
+
private readonly onChange;
|
|
38
|
+
private readonly reveal;
|
|
39
|
+
private readonly checkPair;
|
|
40
|
+
connectedCallback(): void;
|
|
41
|
+
disconnectedCallback(): void;
|
|
42
|
+
protected willUpdate(changed: PropertyValues<this>): void;
|
|
43
|
+
/** Puts the focus where the learner goes on: the answer, "Weiter" after an answer, or the first button. */
|
|
44
|
+
focusAnswer(): void;
|
|
45
|
+
/** For a dictation: puts the focus back on "Einstellungen" (after the page's voice settings close). */
|
|
46
|
+
focusSettings(): void;
|
|
47
|
+
protected updated(): void;
|
|
48
|
+
render(): TemplateResult | typeof nothing;
|
|
49
|
+
/** Loading, an error, everything due done, nothing left. */
|
|
50
|
+
private statusMessage;
|
|
51
|
+
private observe;
|
|
52
|
+
}
|
|
53
|
+
declare global {
|
|
54
|
+
interface HTMLElementTagNameMap {
|
|
55
|
+
'octolearn-trainer': OctolearnTrainer;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
2
|
+
import { OctolearnElement } from '../base-element';
|
|
3
|
+
/** What the server takes as the answer to a Richtig/Falsch question (OC-127). */
|
|
4
|
+
export declare const TRUE_ANSWER = "true";
|
|
5
|
+
export declare const FALSE_ANSWER = "false";
|
|
6
|
+
/**
|
|
7
|
+
* `<octolearn-true-false>`: design D's "Stimmt das?" (OC-127). The server sends a statement, the question's answer or
|
|
8
|
+
* another answer of the folder; the learner says whether it fits. Which one it is, the client never knows. "Stimmt" sends
|
|
9
|
+
* "true", "Stimmt nicht" "false"; ← and → choose too (not while typing, not with `no-keys`). The buttons lock after the
|
|
10
|
+
* choice until a new statement arrives.
|
|
11
|
+
*
|
|
12
|
+
* @fires octolearn-answer - `{ answer: 'true' | 'false' }`
|
|
13
|
+
*/
|
|
14
|
+
export declare class OctolearnTrueFalse extends OctolearnElement {
|
|
15
|
+
static styles: import("lit").CSSResultGroup[];
|
|
16
|
+
/** The answer to judge (`content.statement`); without one nothing is shown. */
|
|
17
|
+
statement?: string | null;
|
|
18
|
+
/** Language of the statement (the target language of a vocabulary). */
|
|
19
|
+
statementLang?: string;
|
|
20
|
+
/** Accessible name of the two buttons (default "Passt diese Antwort zur Frage?"). */
|
|
21
|
+
label?: string;
|
|
22
|
+
/** Don't react to ← and →. */
|
|
23
|
+
noKeys: boolean;
|
|
24
|
+
private chosen;
|
|
25
|
+
connectedCallback(): void;
|
|
26
|
+
disconnectedCallback(): void;
|
|
27
|
+
protected willUpdate(changed: PropertyValues<this>): void;
|
|
28
|
+
private get choices();
|
|
29
|
+
render(): TemplateResult | typeof nothing;
|
|
30
|
+
private choose;
|
|
31
|
+
private readonly onKeyDown;
|
|
32
|
+
}
|
|
33
|
+
declare global {
|
|
34
|
+
interface HTMLElementTagNameMap {
|
|
35
|
+
'octolearn-true-false': OctolearnTrueFalse;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { MatchingRoundItem } from './content';
|
|
2
|
+
/** Fired by every element that takes an answer; bubbles out of shadow roots. */
|
|
3
|
+
export interface AnswerEventDetail {
|
|
4
|
+
/** What the learner typed (trimmed) or chose. Correctness is decided by the server, never here. */
|
|
5
|
+
answer: string;
|
|
6
|
+
/** "Weiß ich nicht": `answer` is then "?", which the server grades wrong and answers with the solution. */
|
|
7
|
+
dontKnow?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export type AnswerEvent = CustomEvent<AnswerEventDetail>;
|
|
10
|
+
export declare const ANSWER_EVENT = "octolearn-answer";
|
|
11
|
+
/** What "Weiß ich nicht" sends: never a real answer, so the server grades it wrong and returns the solution. */
|
|
12
|
+
export declare const DONT_KNOW_ANSWER = "?";
|
|
13
|
+
/** Fired before anything is read aloud; cancel it to speak with your own voice output. */
|
|
14
|
+
export interface SpeakEventDetail {
|
|
15
|
+
/** The text to read aloud. */
|
|
16
|
+
text: string;
|
|
17
|
+
/**
|
|
18
|
+
* `auto`: a new dictation question arrived; `repeat`: the listen button; `slow`: "Langsam anhören"; `read`: the
|
|
19
|
+
* read-aloud button of a word, question or task.
|
|
20
|
+
*/
|
|
21
|
+
reason: 'auto' | 'repeat' | 'slow' | 'read';
|
|
22
|
+
/** A speed for this request (0.7 for `slow`). */
|
|
23
|
+
rate?: number;
|
|
24
|
+
/** The language of the text, when it is not the page's (a vocabulary word). */
|
|
25
|
+
lang?: string;
|
|
26
|
+
}
|
|
27
|
+
export type SpeakEvent = CustomEvent<SpeakEventDetail>;
|
|
28
|
+
/** Fired by `<octolearn-matching-round>` after the server checked a pair. */
|
|
29
|
+
export interface PairCheckedDetail {
|
|
30
|
+
item: MatchingRoundItem;
|
|
31
|
+
/** The answer the learner paired the question with. */
|
|
32
|
+
answer: string;
|
|
33
|
+
correct: boolean;
|
|
34
|
+
/** After a wrong pair: the question's real answer. */
|
|
35
|
+
correctAnswer?: string | null;
|
|
36
|
+
}
|
|
37
|
+
interface OctolearnEventMap {
|
|
38
|
+
'octolearn-answer': AnswerEvent;
|
|
39
|
+
'octolearn-speak': SpeakEvent;
|
|
40
|
+
'octolearn-stop': CustomEvent<Record<string, never>>;
|
|
41
|
+
'octolearn-settings': CustomEvent<Record<string, never>>;
|
|
42
|
+
'octolearn-continue': CustomEvent<Record<string, never>>;
|
|
43
|
+
'octolearn-pair': CustomEvent<PairCheckedDetail>;
|
|
44
|
+
'octolearn-round-done': CustomEvent<Record<string, never>>;
|
|
45
|
+
'octolearn-close': CustomEvent<Record<string, never>>;
|
|
46
|
+
}
|
|
47
|
+
declare global {
|
|
48
|
+
interface HTMLElementEventMap extends OctolearnEventMap {
|
|
49
|
+
}
|
|
50
|
+
interface DocumentEventMap extends OctolearnEventMap {
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { QueryFormat } from './content';
|
|
2
|
+
/** How a question of the round went (design D's stars). */
|
|
3
|
+
export type QuestionResult = 'right' | 'wrong';
|
|
4
|
+
export type FeedbackResult = 'correct' | 'wrong';
|
|
5
|
+
/** Everything the feedback shows about one evaluated answer; `TrainingSession` builds it (AnswerOutcome.feedback). */
|
|
6
|
+
export interface FeedbackMessage {
|
|
7
|
+
result: FeedbackResult;
|
|
8
|
+
/** What the learner answered. */
|
|
9
|
+
answer: string;
|
|
10
|
+
/** The solution, sent by the server after a wrong answer. */
|
|
11
|
+
correctAnswer?: string | null;
|
|
12
|
+
/** Accepted with a small typo. */
|
|
13
|
+
typo?: boolean;
|
|
14
|
+
/** Shown in brackets after "Richtig!" text instead of the typo note, e.g. the server's message. */
|
|
15
|
+
note?: string | null;
|
|
16
|
+
/** The learner pressed "Weiß ich nicht". */
|
|
17
|
+
dontKnow?: boolean;
|
|
18
|
+
/** The answer could not be sent or checked. */
|
|
19
|
+
unchecked?: boolean;
|
|
20
|
+
/** Learning points this answer earned (shown only after a right answer). */
|
|
21
|
+
points?: number;
|
|
22
|
+
/** Right answers in a row, this one included. */
|
|
23
|
+
streak?: number;
|
|
24
|
+
/** For a multiple-choice question: the options, shown again with the right one marked. */
|
|
25
|
+
options?: readonly string[] | null;
|
|
26
|
+
/** The question was a word (dictation, vocabulary): "das Wort kommt bald wieder", listen again. */
|
|
27
|
+
word?: boolean;
|
|
28
|
+
/** A dictation: the solution can be listened to again. */
|
|
29
|
+
listenable?: boolean;
|
|
30
|
+
/** How the question was answered; Richtig/Falsch and the Karteikarte show "Stimmt" / "Gewusst" instead of the token. */
|
|
31
|
+
queryFormat?: QueryFormat | null;
|
|
32
|
+
/** Toast only: how long the card stays, in ms. */
|
|
33
|
+
life?: number;
|
|
34
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Font family names of our own, so that a page's own "Montserrat" (other weights, other files) can't get mixed in. */
|
|
2
|
+
export declare const FONT_FAMILY_BODY = "Octolearn Montserrat";
|
|
3
|
+
export declare const FONT_FAMILY_HEADLINE = "Octolearn Headline";
|
|
4
|
+
/**
|
|
5
|
+
* Adds the fonts to the document. Font faces declared inside a shadow root are ignored by browsers, so they have to be
|
|
6
|
+
* added to the document; the FontFace API needs no <style> element (and so no 'unsafe-inline' in a page's CSP). A page
|
|
7
|
+
* whose CSP forbids data: fonts keeps the fallback fonts (Montserrat if the page has it, else sans-serif).
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadFonts(): Promise<void>;
|