eklavya 1.52.5 → 1.54.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/dist/assets/tutor/references/panel.md +4 -0
- package/dist/panel-state.js +6 -4
- package/dist/panel-state.js.map +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/hooks/panel/model.ts +94 -1
- package/dist/plugin/hooks/panel/register.tsx +138 -45
- package/dist/plugin/skills/tutor/references/panel.md +4 -0
- package/dist/tools/panel_tools.js +1 -1
- package/dist/tools/panel_tools.js.map +1 -1
- package/package.json +1 -1
|
@@ -27,6 +27,10 @@ Make one call to `present_question` instead:
|
|
|
27
27
|
misconception (rows one and three). Grade 3 is not available to a pick: only
|
|
28
28
|
typed words can show a hesitant "right".
|
|
29
29
|
- `more: true` only in an explicit round (the plan has more items after this one, as for `/eklavya:quiz`): the panel then offers a Next button. Leave it out otherwise. Once a round is planned the server tracks it: the last question's `more` is set false for you, and Next arrives as a prompt telling you to call `get_session_quiz_plan` with `resume_round: true`.
|
|
30
|
+
- **Mark the code.** The panel draws the stem, each `description` and the `explanation` as Markdown, so
|
|
31
|
+
put every function, call, class, file and flag in backticks (`` `tester.pumpWidget(widget)` ``,
|
|
32
|
+
`` `shouldRepaint` ``) and nothing else: no headings, lists or emphasis. Option `label`s are drawn as
|
|
33
|
+
plain text, so write them in words and leave the backticks out of them.
|
|
30
34
|
- `explanation`: one line saying why the right option is right. The panel shows
|
|
31
35
|
it after the answer, so it is the whole of your feedback.
|
|
32
36
|
|
package/dist/panel-state.js
CHANGED
|
@@ -13,11 +13,13 @@ import { isCowork } from './surface.js';
|
|
|
13
13
|
/** How long after its last `panel_sync` a mod still counts as present. */
|
|
14
14
|
export const PANEL_HEARTBEAT_TTL_MS = 90_000;
|
|
15
15
|
/**
|
|
16
|
-
* Host surfaces
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Host surfaces the planner may present the panel on. The terminal was verified
|
|
17
|
+
* (docs/verified-schemas.md); the Desktop Code tab is enabled but not yet
|
|
18
|
+
* observed. It is safe to try: a host that cannot seat the pane reports
|
|
19
|
+
* `placed: false` and the question stays on the card, and a host that never
|
|
20
|
+
* loads the mod never stamps a heartbeat.
|
|
19
21
|
*/
|
|
20
|
-
export const PANEL_SURFACES = ['terminal'];
|
|
22
|
+
export const PANEL_SURFACES = ['terminal', 'desktop'];
|
|
21
23
|
/** The phases in which a question is still waiting for its answer. */
|
|
22
24
|
export const OPEN_PHASES = "('pending','unplaced','grading')";
|
|
23
25
|
/** An unanswered question stops being open after this long (`panel.ts` expires it). */
|
package/dist/panel-state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panel-state.js","sourceRoot":"","sources":["../src/panel-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC;;;;;;;;;;GAUG;AAEH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAE7C
|
|
1
|
+
{"version":3,"file":"panel-state.js","sourceRoot":"","sources":["../src/panel-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eklavya",
|
|
3
3
|
"displayName": "Eklavya",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.54.0",
|
|
5
5
|
"description": "Learn while your agent works. Turns coding-agent generation time into adaptive, Socratic learning grounded in the code being written.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Ajay Kumar"
|
|
@@ -18,14 +18,96 @@ export const EMPTY: PanelState = {
|
|
|
18
18
|
/** Tier names as the dashboard shows them (`TIER` in `assets/dashboard.html`; a test keeps the two equal). */
|
|
19
19
|
export const TIER: Record<number, string> = { 1: 'recall', 2: 'mechanism', 3: 'judgement', 4: 'failure modes', 5: 'design' }
|
|
20
20
|
|
|
21
|
+
/** Brand verdigris (`--vd-300`): the selected card's border and the name. */
|
|
22
|
+
export const BRAND = '#79D5C4'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The logo: the Claude mascot (orange) holding an Eklavya bow and arrow (white), drawn on a
|
|
26
|
+
* grid where one cell is a quarter of a text character. Designed in the logo maker; a letter
|
|
27
|
+
* names its colour in LOGO_PALETTE and a dot is empty.
|
|
28
|
+
*/
|
|
29
|
+
export const LOGO_GRID = [
|
|
30
|
+
'.................dd.......',
|
|
31
|
+
'................d..d......',
|
|
32
|
+
'................d...d.....',
|
|
33
|
+
'...aaaaaaaaaaaa.d....d....',
|
|
34
|
+
'...aa.aaaaaa.aa.d.....d...',
|
|
35
|
+
'.aaaaaaaaaaaaaaadddddddddd',
|
|
36
|
+
'...aaaaaaaaaaaa.d.....d...',
|
|
37
|
+
'....a.a....a.a..d....d....',
|
|
38
|
+
'................d...d.....',
|
|
39
|
+
'................d..d......',
|
|
40
|
+
'.................dd.......',
|
|
41
|
+
'..........................',
|
|
42
|
+
] as const
|
|
43
|
+
export const LOGO_PALETTE: Record<string, string> = { a: '#D97757', d: '#FFFFFF' }
|
|
44
|
+
|
|
45
|
+
/** One stretch of text cells that share a colour pair. A text character holds two colours at most. */
|
|
46
|
+
export type LogoRun = { text: string; fg?: string; bg?: string }
|
|
47
|
+
|
|
48
|
+
/** Quadrant glyphs by which of the four cells (upper left 1, upper right 2, lower left 4, lower right 8) are filled. */
|
|
49
|
+
const QUADRANT = ' ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█'
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The grid as rows of text runs: each 2 by 2 block becomes one quadrant character, the
|
|
53
|
+
* first colour as its foreground and a second, if any, as its background. A third colour
|
|
54
|
+
* in one block is dropped, so the logo degrades and never throws.
|
|
55
|
+
*/
|
|
56
|
+
export function logoRows(grid: readonly string[] = LOGO_GRID, palette: Record<string, string> = LOGO_PALETTE): LogoRun[][] {
|
|
57
|
+
const out: LogoRun[][] = []
|
|
58
|
+
for (let cy = 0; cy < Math.ceil(grid.length / 2); cy++) {
|
|
59
|
+
const runs: LogoRun[] = []
|
|
60
|
+
for (let cx = 0; cx < Math.ceil((grid[0]?.length ?? 0) / 2); cx++) {
|
|
61
|
+
const quad = [grid[cy * 2]?.[cx * 2], grid[cy * 2]?.[cx * 2 + 1], grid[cy * 2 + 1]?.[cx * 2], grid[cy * 2 + 1]?.[cx * 2 + 1]].map(c => (c && c !== '.' ? c : ''))
|
|
62
|
+
const [first, second] = [...new Set(quad.filter(Boolean))]
|
|
63
|
+
let run: LogoRun = { text: ' ' }
|
|
64
|
+
if (first) {
|
|
65
|
+
const mask = quad.reduce((n, c, i) => (c === first ? n | (1 << i) : n), 0)
|
|
66
|
+
run = { text: QUADRANT[mask], fg: palette[first], bg: second ? palette[second] : undefined }
|
|
67
|
+
}
|
|
68
|
+
const last = runs[runs.length - 1]
|
|
69
|
+
if (last && last.fg === run.fg && last.bg === run.bg) last.text += run.text
|
|
70
|
+
else runs.push(run)
|
|
71
|
+
}
|
|
72
|
+
out.push(runs)
|
|
73
|
+
}
|
|
74
|
+
return out
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The name as the header sets it: spaced capitals, the nearest a terminal gets to a larger type size. */
|
|
78
|
+
/** A call such as `tester.tap(find.byType(X))`, nested up to three deep, a lowerCamelCase name or a snake_case name. */
|
|
79
|
+
const CODE_LIKE = /[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*\((?:[^()\n]|\((?:[^()\n]|\([^()\n]*\))*\))*\)|\b[a-z]{2,}(?:[A-Z][a-z0-9]+)+\b|\b[a-z0-9]+(?:_[a-z0-9]+)+\b/g
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Wraps code-looking words in backticks so the pane's Markdown draws them as code. A question
|
|
83
|
+
* is plain prose unless its author marked the code, and an old or careless one often is not.
|
|
84
|
+
* Text already in backticks is left as written.
|
|
85
|
+
*/
|
|
86
|
+
export function codify(text: string): string {
|
|
87
|
+
return text
|
|
88
|
+
.split(/(`[^`]*`)/)
|
|
89
|
+
.map((part, i) => (i % 2 ? part : part.replace(CODE_LIKE, '`$&`')))
|
|
90
|
+
.join('')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** What a Button label can show of Markdown: it keeps the words and drops the code and bold marks. */
|
|
94
|
+
export function plainLabel(text: string): string {
|
|
95
|
+
return text.replace(/`|\*\*/g, '')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const WORDMARK = 'EKLAVYA'.split('').join(' ')
|
|
99
|
+
|
|
100
|
+
/** How long a finished result stays before it closes itself, when no Next is waiting. */
|
|
101
|
+
export const CLOSE_MS = 15_000
|
|
102
|
+
|
|
21
103
|
/** Every sentence the learner reads, in one place. The handoff's table, verbatim. */
|
|
22
104
|
export const STR = {
|
|
23
105
|
brand: 'Eklavya',
|
|
24
|
-
powered: 'Powered by Claude',
|
|
25
106
|
submit: 'Submit answer',
|
|
26
107
|
skip: 'Skip question',
|
|
27
108
|
next: 'Next question',
|
|
28
109
|
done: 'Done',
|
|
110
|
+
autoClose: 'Closes by itself in 15 seconds.',
|
|
29
111
|
retry: 'Retry',
|
|
30
112
|
close: 'Close',
|
|
31
113
|
other: 'Other — explain in your own words',
|
|
@@ -41,6 +123,9 @@ export const STR = {
|
|
|
41
123
|
nothingTyped: 'Write your answer first, or pick one of the options.',
|
|
42
124
|
graderFailed: "We couldn't check that answer. Your text is saved here — Retry.",
|
|
43
125
|
explainer: 'A page explaining this is being written.',
|
|
126
|
+
focus: 'Ctrl+X then Tab moves the keys here.',
|
|
127
|
+
keys: '1–4 pick · o other · s submit · k skip · Esc back to prompt',
|
|
128
|
+
keysApp: '1–4 pick · o other · s submit · k skip',
|
|
44
129
|
off: 'The quiz panel is off. Turn it on with: eklavya config set quiz.panel true',
|
|
45
130
|
} as const
|
|
46
131
|
|
|
@@ -79,6 +164,14 @@ export function explainerBrief(x: ExplainBlock): string {
|
|
|
79
164
|
}
|
|
80
165
|
|
|
81
166
|
export const answerWas = (label: string): string => `The answer was: ${label}`
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The hint lines under the pane. Moving the keys with Ctrl+X then Tab and
|
|
170
|
+
* leaving with Esc are the terminal's; an app draws native buttons, so it gets
|
|
171
|
+
* the answer keys only.
|
|
172
|
+
*/
|
|
173
|
+
export const hintLines = (surface: string): string[] => (surface === 'terminal' ? [STR.focus, STR.keys] : [STR.keysApp])
|
|
174
|
+
|
|
82
175
|
export const unplacedNotice = (): string => `A question is waiting. ${REOPEN} to open it.`
|
|
83
176
|
export const levelLine = (level: string, project: string): string => `You've cleared ${level} on ${project}.`
|
|
84
177
|
|
|
@@ -2,21 +2,28 @@ import { atom, read, update } from 'claude-code'
|
|
|
2
2
|
import type { EngineInterface, Register } from 'claude-code'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
BRAND,
|
|
5
6
|
EMPTY,
|
|
6
7
|
EXPLAINER,
|
|
7
8
|
NEXT_PROMPT,
|
|
8
9
|
PANE,
|
|
9
10
|
REOPEN,
|
|
10
11
|
STR,
|
|
12
|
+
WORDMARK,
|
|
13
|
+
CLOSE_MS,
|
|
11
14
|
answerWas,
|
|
12
15
|
explainerBrief,
|
|
16
|
+
codify,
|
|
13
17
|
gradingRequest,
|
|
18
|
+
logoRows,
|
|
19
|
+
plainLabel,
|
|
14
20
|
levelLine,
|
|
15
21
|
parseVerdict,
|
|
16
22
|
payloadOf,
|
|
17
23
|
projectName,
|
|
18
24
|
topicLabel,
|
|
19
25
|
unplacedNotice,
|
|
26
|
+
hintLines,
|
|
20
27
|
} from './model'
|
|
21
28
|
import type { PanelPending, PanelQuestion, PanelState } from './types'
|
|
22
29
|
|
|
@@ -49,6 +56,8 @@ let busy = false
|
|
|
49
56
|
/** Bumped when the session ends: a submit that began before it is stale and must write nothing. */
|
|
50
57
|
let epoch = 0
|
|
51
58
|
let explainedAttempt = 0
|
|
59
|
+
/** The result closes itself after CLOSE_MS when nothing else is pending (no Next). */
|
|
60
|
+
let closer: { cancel: () => void } | null = null
|
|
52
61
|
|
|
53
62
|
/**
|
|
54
63
|
* One call to an Eklavya tool through the host's own connection, as the parsed
|
|
@@ -219,6 +228,9 @@ async function submit($: EngineInterface, wanted: PanelPending | null): Promise<
|
|
|
219
228
|
message: null,
|
|
220
229
|
}))
|
|
221
230
|
|
|
231
|
+
// The last question of a round has no Next: the result closes itself, as Done would.
|
|
232
|
+
if (!q.more) arm($, mine, q.question_id)
|
|
233
|
+
|
|
222
234
|
// A miss with explain_on_wrong on: the panel starts the explainer itself in
|
|
223
235
|
// the background, so no prompt shows in the transcript and nothing waits.
|
|
224
236
|
// Auto mode judges every spawn (it drops Agent allow rules), and its
|
|
@@ -270,6 +282,7 @@ export const register: Register = on => {
|
|
|
270
282
|
on('session.end', async ($, e, next) => {
|
|
271
283
|
try {
|
|
272
284
|
epoch += 1
|
|
285
|
+
disarm()
|
|
273
286
|
openedFor = null
|
|
274
287
|
busy = false
|
|
275
288
|
await update($, quiz, () => EMPTY)
|
|
@@ -332,22 +345,64 @@ export const register: Register = on => {
|
|
|
332
345
|
on('ui.render', { component: 'Pane', requestId: 'eklavya-quiz' }, async ($, e, next) => {
|
|
333
346
|
// Only our pane: every other pane, and everything else, is the host's.
|
|
334
347
|
if (e.requestId !== PANE) return next(e)
|
|
335
|
-
const { Box, Text, Button, Input } = $.ui.resolve(e) as any
|
|
348
|
+
const { Box, Text, Button, Input, Markdown } = $.ui.resolve(e) as any
|
|
336
349
|
const s: PanelState = await read($, quiz)
|
|
337
350
|
const q = s.question
|
|
338
351
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
352
|
+
// The mark is drawn in text, as Claude Code draws its own, so it shows in every terminal.
|
|
353
|
+
const logo = (
|
|
354
|
+
<Box flexDirection="column" flexShrink={0}>
|
|
355
|
+
{logoRows().map((runs, y) => (
|
|
356
|
+
<Box key={`logo-${y}`} flexDirection="row" flexShrink={0}>
|
|
357
|
+
{runs.map((run, x) => (
|
|
358
|
+
<Text key={`logo-${y}-${x}`} color={run.fg} backgroundColor={run.bg} wrap="truncate">
|
|
359
|
+
{run.text}
|
|
360
|
+
</Text>
|
|
361
|
+
))}
|
|
362
|
+
</Box>
|
|
363
|
+
))}
|
|
364
|
+
</Box>
|
|
365
|
+
)
|
|
366
|
+
// A list tile: the mark on the left; the name over the topic beside it.
|
|
367
|
+
const head = (subtitle?: string) => (
|
|
368
|
+
<Box flexDirection="row" alignItems="center" gap={2}>
|
|
369
|
+
{logo}
|
|
370
|
+
<Box flexDirection="column" flexShrink={1}>
|
|
371
|
+
<Text bold color={BRAND}>
|
|
372
|
+
{WORDMARK}
|
|
373
|
+
</Text>
|
|
374
|
+
{subtitle ? <Text>{subtitle}</Text> : null}
|
|
375
|
+
</Box>
|
|
376
|
+
</Box>
|
|
377
|
+
)
|
|
378
|
+
const hint = (
|
|
379
|
+
<Box flexDirection="column">
|
|
380
|
+
{hintLines(e.surface).map(line => (
|
|
381
|
+
<Text key={line} dimColor>
|
|
382
|
+
{line}
|
|
383
|
+
</Text>
|
|
384
|
+
))}
|
|
385
|
+
</Box>
|
|
386
|
+
)
|
|
387
|
+
// One answer: a bordered block. The Button holds the whole answer text, so a press anywhere on it
|
|
388
|
+
// selects; the border and a tick show the pick, and nothing implies it is right.
|
|
389
|
+
const card = (id: string, selected: boolean, button: unknown, body: unknown[], roomy = false) => (
|
|
390
|
+
<Box
|
|
391
|
+
key={`card-${id}`}
|
|
392
|
+
flexDirection="column"
|
|
393
|
+
paddingX={1}
|
|
394
|
+
paddingY={roomy ? 1 : 0}
|
|
395
|
+
gap={roomy ? 1 : 0}
|
|
396
|
+
borderStyle="single"
|
|
397
|
+
borderColor={selected ? BRAND : 'inactive'}
|
|
398
|
+
>
|
|
399
|
+
{button}
|
|
400
|
+
{body}
|
|
345
401
|
</Box>
|
|
346
402
|
)
|
|
347
|
-
const hint = <Text dimColor>Ctrl+X then Tab moves focus here. Enter presses a button. Esc returns to the prompt.</Text>
|
|
348
403
|
const frame = (...body: unknown[]) => (
|
|
349
404
|
<Box flexDirection="column" paddingX={1} gap={1}>
|
|
350
|
-
{head}
|
|
405
|
+
{head(q ? topicLabel(q) : undefined)}
|
|
351
406
|
{body}
|
|
352
407
|
{hint}
|
|
353
408
|
</Box>
|
|
@@ -364,25 +419,24 @@ export const register: Register = on => {
|
|
|
364
419
|
)
|
|
365
420
|
}
|
|
366
421
|
|
|
367
|
-
const
|
|
368
|
-
const stem = <Text bold>{q.stem}</Text>
|
|
422
|
+
const stem = <Markdown text={codify(q.stem)} />
|
|
369
423
|
|
|
370
424
|
if (s.step === 'feedback' || s.step === 'skipped') {
|
|
371
425
|
const r = s.result
|
|
372
426
|
if (s.step === 'skipped' || !r) {
|
|
373
|
-
return frame(
|
|
427
|
+
return frame(<Text>{STR.skipped}</Text>, q.more ? null : <Text dimColor>{STR.autoClose}</Text>, <Button key="done" label={STR.done} variant="primary" hotkey="d" onPress={() => done($)} />)
|
|
374
428
|
}
|
|
375
429
|
const right = r.correct === true
|
|
376
430
|
return frame(
|
|
377
|
-
topic,
|
|
378
431
|
stem,
|
|
379
432
|
<Text bold color={right ? 'suggestion' : 'error'}>
|
|
380
433
|
{right ? STR.correct : STR.wrong}
|
|
381
434
|
</Text>,
|
|
382
435
|
right || !r.correct_label ? null : <Text>{answerWas(r.correct_label)}</Text>,
|
|
383
|
-
r.explanation ? <
|
|
436
|
+
r.explanation ? <Markdown text={codify(r.explanation)} /> : null,
|
|
384
437
|
r.level_up ? <Text color="suggestion">{levelLine(r.level_up.from, projectName(q.repo))}</Text> : null,
|
|
385
438
|
r.explain ? <Text dimColor>{STR.explainer}</Text> : null,
|
|
439
|
+
q.more ? null : <Text dimColor>{STR.autoClose}</Text>,
|
|
386
440
|
<Box flexDirection="row" gap={2}>
|
|
387
441
|
{q.more ? <Button key="next" label={STR.next} hotkey="n" onPress={() => askNext($)} /> : null}
|
|
388
442
|
<Button key="done" label={STR.done} variant="primary" hotkey="d" onPress={() => done($)} />
|
|
@@ -393,45 +447,51 @@ export const register: Register = on => {
|
|
|
393
447
|
const locked = s.step === 'grading'
|
|
394
448
|
// Submit looks disabled until there is something to send; pressing it then does nothing.
|
|
395
449
|
const hasDraft = s.draft.other ? s.draft.text.trim().length > 0 : s.draft.picked !== null
|
|
450
|
+
// Markdown marks cannot show inside a Button's label, so the code and bold marks are dropped there; the note keeps them.
|
|
451
|
+
const answer = (id: string, key: string, hotkey: string, label: string, chosen: boolean) => (
|
|
452
|
+
<Button
|
|
453
|
+
key={key}
|
|
454
|
+
plain
|
|
455
|
+
hotkey={hotkey}
|
|
456
|
+
label={`${chosen ? '✓ ' : ''}${label}`}
|
|
457
|
+
onPress={() => (locked ? undefined : id === 'other' ? chooseOther($) : pick($, id))}
|
|
458
|
+
/>
|
|
459
|
+
)
|
|
396
460
|
const options = q.options.map((o, i) => {
|
|
397
461
|
const chosen = !s.draft.other && s.draft.picked === o.id
|
|
398
|
-
return (
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
label={`${chosen ? '●' : '○'} ${o.label}`}
|
|
404
|
-
hotkey={String(i + 1)}
|
|
405
|
-
onPress={() => (locked ? undefined : pick($, o.id))}
|
|
406
|
-
/>
|
|
407
|
-
{o.note ? <Text dimColor>{` ${o.note}`}</Text> : null}
|
|
408
|
-
</Box>
|
|
462
|
+
return card(
|
|
463
|
+
o.id,
|
|
464
|
+
chosen,
|
|
465
|
+
answer(o.id, `opt-${o.id}`, String(i + 1), plainLabel(o.label), chosen),
|
|
466
|
+
[o.note ? <Box key={`note-${o.id}`} paddingLeft={3}><Markdown text={codify(o.note)} dimColor /></Box> : null],
|
|
409
467
|
)
|
|
410
468
|
})
|
|
411
469
|
|
|
412
470
|
return frame(
|
|
413
|
-
topic,
|
|
414
471
|
stem,
|
|
415
|
-
<Box flexDirection="column"
|
|
472
|
+
<Box flexDirection="column">
|
|
416
473
|
{options}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
474
|
+
{card(
|
|
475
|
+
'other',
|
|
476
|
+
s.draft.other,
|
|
477
|
+
answer('other', 'opt-other', 'o', STR.other, s.draft.other),
|
|
478
|
+
[
|
|
479
|
+
s.draft.other ? (
|
|
480
|
+
<Box key="other-box" paddingLeft={3}>
|
|
481
|
+
<Input
|
|
482
|
+
key="other-text"
|
|
483
|
+
autoFocus
|
|
484
|
+
placeholder={STR.otherPlaceholder}
|
|
485
|
+
value={s.draft.text}
|
|
486
|
+
submitLabel="keep"
|
|
487
|
+
onInput={(value: string) => (locked ? undefined : typeOther($, value))}
|
|
488
|
+
onSubmit={(value: string) => (locked ? undefined : typeOther($, value))}
|
|
489
|
+
/>
|
|
490
|
+
</Box>
|
|
491
|
+
) : null,
|
|
492
|
+
],
|
|
493
|
+
s.draft.other,
|
|
494
|
+
)}
|
|
435
495
|
</Box>,
|
|
436
496
|
locked ? <Text dimColor>{STR.grading}</Text> : null,
|
|
437
497
|
s.step === 'error' && s.message ? <Text color="error">{s.message}</Text> : null,
|
|
@@ -452,6 +512,12 @@ export const register: Register = on => {
|
|
|
452
512
|
/** Selecting an option records a draft and nothing else: it never submits, grades or skips. */
|
|
453
513
|
async function pick($: EngineInterface, id: string): Promise<void> {
|
|
454
514
|
await update($, quiz, (s: PanelState) => (s.step === 'awaiting' || s.step === 'error' ? { ...s, draft: { ...s.draft, picked: id, other: false }, message: null } : s))
|
|
515
|
+
try {
|
|
516
|
+
// The host's focus mark follows the pick, so the highlighted answer is the chosen one even after a hotkey.
|
|
517
|
+
await $.ui.focus({ requestId: PANE, key: `opt-${id}` })
|
|
518
|
+
} catch {
|
|
519
|
+
/* Fail open: the pick stands without it. */
|
|
520
|
+
}
|
|
455
521
|
}
|
|
456
522
|
|
|
457
523
|
async function chooseOther($: EngineInterface): Promise<void> {
|
|
@@ -466,8 +532,34 @@ async function skip($: EngineInterface): Promise<void> {
|
|
|
466
532
|
await submit($, { kind: 'skip' })
|
|
467
533
|
}
|
|
468
534
|
|
|
535
|
+
/** Starts the auto-close for this result; any later Done, Next or new question cancels it. */
|
|
536
|
+
function arm($: EngineInterface, mine: number, id: string): void {
|
|
537
|
+
try {
|
|
538
|
+
closer?.cancel()
|
|
539
|
+
closer = $.clock.after(CLOSE_MS, async () => {
|
|
540
|
+
try {
|
|
541
|
+
closer = null
|
|
542
|
+
if (epoch !== mine) return
|
|
543
|
+
const s: PanelState = (await $.state.get(REF)).value ?? EMPTY
|
|
544
|
+
// Only the result it was started for: an answer-in-progress or a newer question stays.
|
|
545
|
+
if ((s.step === 'feedback' || s.step === 'skipped') && s.question?.question_id === id) await done($)
|
|
546
|
+
} catch {
|
|
547
|
+
/* Fail open. */
|
|
548
|
+
}
|
|
549
|
+
})
|
|
550
|
+
} catch {
|
|
551
|
+
/* No clock: the learner presses Done. */
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function disarm(): void {
|
|
556
|
+
closer?.cancel()
|
|
557
|
+
closer = null
|
|
558
|
+
}
|
|
559
|
+
|
|
469
560
|
/** Done closes the pane; another question may already be waiting, and then the pane comes straight back. */
|
|
470
561
|
async function done($: EngineInterface): Promise<void> {
|
|
562
|
+
disarm()
|
|
471
563
|
try {
|
|
472
564
|
openedFor = null
|
|
473
565
|
await update($, quiz, () => EMPTY)
|
|
@@ -480,6 +572,7 @@ async function done($: EngineInterface): Promise<void> {
|
|
|
480
572
|
|
|
481
573
|
/** Next asks the model, by a queued prompt, for the next question of an explicit round. */
|
|
482
574
|
async function askNext($: EngineInterface): Promise<void> {
|
|
575
|
+
disarm()
|
|
483
576
|
try {
|
|
484
577
|
openedFor = null
|
|
485
578
|
await update($, quiz, () => ({ ...EMPTY, step: 'loading' }))
|
|
@@ -27,6 +27,10 @@ Make one call to `present_question` instead:
|
|
|
27
27
|
misconception (rows one and three). Grade 3 is not available to a pick: only
|
|
28
28
|
typed words can show a hesitant "right".
|
|
29
29
|
- `more: true` only in an explicit round (the plan has more items after this one, as for `/eklavya:quiz`): the panel then offers a Next button. Leave it out otherwise. Once a round is planned the server tracks it: the last question's `more` is set false for you, and Next arrives as a prompt telling you to call `get_session_quiz_plan` with `resume_round: true`.
|
|
30
|
+
- **Mark the code.** The panel draws the stem, each `description` and the `explanation` as Markdown, so
|
|
31
|
+
put every function, call, class, file and flag in backticks (`` `tester.pumpWidget(widget)` ``,
|
|
32
|
+
`` `shouldRepaint` ``) and nothing else: no headings, lists or emphasis. Option `label`s are drawn as
|
|
33
|
+
plain text, so write them in words and leave the backticks out of them.
|
|
30
34
|
- `explanation`: one line saying why the right option is right. The panel shows
|
|
31
35
|
it after the answer, so it is the whole of your feedback.
|
|
32
36
|
|
|
@@ -10,7 +10,7 @@ export const presentQuestionTool = {
|
|
|
10
10
|
session_id: z.string().max(LIMITS.sessionId).optional().describe(SESSION_HINT),
|
|
11
11
|
cwd: z.string().max(LIMITS.cwd).optional().describe(CWD_HINT),
|
|
12
12
|
slug: z.string().max(LIMITS.slug).describe('The concept asked about.'),
|
|
13
|
-
question: z.string().min(1).max(LIMITS.question).describe('The question stem only: no options, no attribution, no settings line.'),
|
|
13
|
+
question: z.string().min(1).max(LIMITS.question).describe('The question stem only: no options, no attribution, no settings line. Markdown: put code names and calls in backticks.'),
|
|
14
14
|
options: z
|
|
15
15
|
.array(z.object({
|
|
16
16
|
label: z.string().min(1).max(LIMITS.option).describe('The option text itself, a complete claim in the option\'s own words: never a letter or a number (the panel numbers them). Labels must differ.'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panel_tools.js","sourceRoot":"","sources":["../../src/tools/panel_tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAuD,MAAM,aAAa,CAAC;AAC3H,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAgB,MAAM,YAAY,CAAC;AAE1E,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,MAAM,CAAC,MAAM,mBAAmB,GAAY;IAC1C,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,2CAA2C;IAClD,WAAW,EACT,k0BAAk0B;IACp0B,WAAW,EAAE;QACX,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;QAC9E,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC7D,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,0BAA0B,CAAC;QACtE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"panel_tools.js","sourceRoot":"","sources":["../../src/tools/panel_tools.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED