eklavya 1.53.0 → 1.54.1

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.
@@ -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 that have been seen to take an answer. Verified on the terminal
17
- * only (docs/verified-schemas.md, Stage 0); the Desktop Code tab is added here
18
- * when it has been tried, not before.
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). */
@@ -1 +1 @@
1
- {"version":3,"file":"panel-state.js","sourceRoot":"","sources":["../src/panel-state.ts"],"names":[],"mappings":"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"}
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.53.0",
4
+ "version": "1.54.1",
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"
@@ -74,6 +74,20 @@ export function logoRows(grid: readonly string[] = LOGO_GRID, palette: Record<st
74
74
  return out
75
75
  }
76
76
 
77
+ /** The grid as rows of boxes for a host whose font cannot tile glyphs: runs of one colour (or none) and their width in cells. */
78
+ export function boxRows(grid: readonly string[] = LOGO_GRID, palette: Record<string, string> = LOGO_PALETTE): { width: number; color?: string }[][] {
79
+ return grid.map(row => {
80
+ const runs: { width: number; color?: string }[] = []
81
+ for (const c of row) {
82
+ const color = c === '.' ? undefined : palette[c]
83
+ const last = runs[runs.length - 1]
84
+ if (last && last.color === color) last.width++
85
+ else runs.push({ width: 1, color })
86
+ }
87
+ return runs
88
+ })
89
+ }
90
+
77
91
  /** The name as the header sets it: spaced capitals, the nearest a terminal gets to a larger type size. */
78
92
  /** A call such as `tester.tap(find.byType(X))`, nested up to three deep, a lowerCamelCase name or a snake_case name. */
79
93
  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
@@ -90,6 +104,17 @@ export function codify(text: string): string {
90
104
  .join('')
91
105
  }
92
106
 
107
+ /** Block Markdown that bold marks around it would break: a fence, heading, list, quote, or bold already in it. */
108
+ const NOT_BOLDABLE = /```|\*\*|^\s*(?:#|[-*+>]|\d+[.)])\s/m
109
+
110
+ /** Bolds each plain paragraph of a question, so a blank line never sits inside a bold span. */
111
+ export function boldParagraphs(text: string): string {
112
+ return text
113
+ .split(/\n{2,}/)
114
+ .map(p => (p.trim() && !NOT_BOLDABLE.test(p) ? `**${p.trim()}**` : p))
115
+ .join('\n\n')
116
+ }
117
+
93
118
  /** What a Button label can show of Markdown: it keeps the words and drops the code and bold marks. */
94
119
  export function plainLabel(text: string): string {
95
120
  return text.replace(/`|\*\*/g, '')
@@ -125,6 +150,7 @@ export const STR = {
125
150
  explainer: 'A page explaining this is being written.',
126
151
  focus: 'Ctrl+X then Tab moves the keys here.',
127
152
  keys: '1–4 pick · o other · s submit · k skip · Esc back to prompt',
153
+ keysApp: '1–4 pick · o other · s submit · k skip',
128
154
  off: 'The quiz panel is off. Turn it on with: eklavya config set quiz.panel true',
129
155
  } as const
130
156
 
@@ -163,6 +189,14 @@ export function explainerBrief(x: ExplainBlock): string {
163
189
  }
164
190
 
165
191
  export const answerWas = (label: string): string => `The answer was: ${label}`
192
+
193
+ /**
194
+ * The hint lines under the pane. Moving the keys with Ctrl+X then Tab and
195
+ * leaving with Esc are the terminal's; an app draws native buttons, so it gets
196
+ * the answer keys only.
197
+ */
198
+ export const hintLines = (surface: string): string[] => (surface === 'terminal' ? [STR.focus, STR.keys] : [STR.keysApp])
199
+
166
200
  export const unplacedNotice = (): string => `A question is waiting. ${REOPEN} to open it.`
167
201
  export const levelLine = (level: string, project: string): string => `You've cleared ${level} on ${project}.`
168
202
 
@@ -16,6 +16,8 @@ import {
16
16
  codify,
17
17
  gradingRequest,
18
18
  logoRows,
19
+ boxRows,
20
+ boldParagraphs,
19
21
  plainLabel,
20
22
  levelLine,
21
23
  parseVerdict,
@@ -23,6 +25,7 @@ import {
23
25
  projectName,
24
26
  topicLabel,
25
27
  unplacedNotice,
28
+ hintLines,
26
29
  } from './model'
27
30
  import type { PanelPending, PanelQuestion, PanelState } from './types'
28
31
 
@@ -348,20 +351,38 @@ export const register: Register = on => {
348
351
  const s: PanelState = await read($, quiz)
349
352
  const q = s.question
350
353
 
351
- // The mark is drawn in text, as Claude Code draws its own, so it shows in every terminal.
352
- const logo = (
353
- <Box flexDirection="column" flexShrink={0}>
354
- {logoRows().map((runs, y) => (
355
- <Box key={`logo-${y}`} flexDirection="row" flexShrink={0}>
356
- {runs.map((run, x) => (
357
- <Text key={`logo-${y}-${x}`} color={run.fg} backgroundColor={run.bg} wrap="truncate">
358
- {run.text}
359
- </Text>
360
- ))}
361
- </Box>
362
- ))}
363
- </Box>
364
- )
354
+ // Any surface but the terminal is the desktop app's: it sets the question bold, spaces the answers
355
+ // apart and draws the mark in boxes. The terminal keeps its text layout.
356
+ const app = e.surface !== 'terminal'
357
+ // The mark is drawn in text, as Claude Code draws its own, so it shows in every terminal. The
358
+ // desktop app sets text in a proportional font with its own line height, so glyph cells do not
359
+ // tile there: it gets the same grid as filled boxes, one per run of a colour. A cell is one unit
360
+ // wide and half a unit tall, which is square in the app (checked live there; the host's
361
+ // fractional-height support is not documented, so a failure shows as a stretched mark).
362
+ const logo =
363
+ !app ? (
364
+ <Box flexDirection="column" flexShrink={0}>
365
+ {logoRows().map((runs, y) => (
366
+ <Box key={`logo-${y}`} flexDirection="row" flexShrink={0}>
367
+ {runs.map((run, x) => (
368
+ <Text key={`logo-${y}-${x}`} color={run.fg} backgroundColor={run.bg} wrap="truncate">
369
+ {run.text}
370
+ </Text>
371
+ ))}
372
+ </Box>
373
+ ))}
374
+ </Box>
375
+ ) : (
376
+ <Box flexDirection="column" flexShrink={0}>
377
+ {boxRows().map((runs, y) => (
378
+ <Box key={`logo-${y}`} flexDirection="row" flexShrink={0} height={0.5}>
379
+ {runs.map((run, x) => (
380
+ <Box key={`logo-${y}-${x}`} width={run.width} height={0.5} flexShrink={0} backgroundColor={run.color} />
381
+ ))}
382
+ </Box>
383
+ ))}
384
+ </Box>
385
+ )
365
386
  // A list tile: the mark on the left; the name over the topic beside it.
366
387
  const head = (subtitle?: string) => (
367
388
  <Box flexDirection="row" alignItems="center" gap={2}>
@@ -376,8 +397,11 @@ export const register: Register = on => {
376
397
  )
377
398
  const hint = (
378
399
  <Box flexDirection="column">
379
- <Text dimColor>{STR.focus}</Text>
380
- <Text dimColor>{STR.keys}</Text>
400
+ {hintLines(e.surface).map(line => (
401
+ <Text key={line} dimColor>
402
+ {line}
403
+ </Text>
404
+ ))}
381
405
  </Box>
382
406
  )
383
407
  // One answer: a bordered block. The Button holds the whole answer text, so a press anywhere on it
@@ -415,7 +439,7 @@ export const register: Register = on => {
415
439
  )
416
440
  }
417
441
 
418
- const stem = <Markdown text={codify(q.stem)} />
442
+ const stem = <Markdown text={app ? boldParagraphs(codify(q.stem)) : codify(q.stem)} />
419
443
 
420
444
  if (s.step === 'feedback' || s.step === 'skipped') {
421
445
  const r = s.result
@@ -465,7 +489,7 @@ export const register: Register = on => {
465
489
 
466
490
  return frame(
467
491
  stem,
468
- <Box flexDirection="column">
492
+ <Box flexDirection="column" gap={app ? 1 : 0}>
469
493
  {options}
470
494
  {card(
471
495
  'other',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eklavya",
3
- "version": "1.53.0",
3
+ "version": "1.54.1",
4
4
  "description": "Learn while your agent works — local knowledge graph, spaced repetition, and quiz gating for agent-assisted development.",
5
5
  "license": "MIT",
6
6
  "repository": {