eklavya 1.86.0 → 1.88.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 (58) hide show
  1. package/dist/assets/dashboard.html +56 -8
  2. package/dist/assets/mascot/expressions/celebrate-body.svg +24 -8
  3. package/dist/assets/mascot/expressions/celebrate-face.svg +24 -8
  4. package/dist/assets/mascot/expressions/confused-body.svg +24 -8
  5. package/dist/assets/mascot/expressions/confused-face.svg +24 -8
  6. package/dist/assets/mascot/expressions/cool-body.svg +85 -0
  7. package/dist/assets/mascot/expressions/cool-face.svg +59 -0
  8. package/dist/assets/mascot/expressions/curious-body.svg +24 -8
  9. package/dist/assets/mascot/expressions/curious-face.svg +24 -8
  10. package/dist/assets/mascot/expressions/determined-body.svg +85 -0
  11. package/dist/assets/mascot/expressions/determined-face.svg +59 -0
  12. package/dist/assets/mascot/expressions/excited-body.svg +24 -8
  13. package/dist/assets/mascot/expressions/excited-face.svg +24 -8
  14. package/dist/assets/mascot/expressions/focused-body.svg +24 -8
  15. package/dist/assets/mascot/expressions/focused-face.svg +24 -8
  16. package/dist/assets/mascot/expressions/happy-body.svg +24 -8
  17. package/dist/assets/mascot/expressions/happy-face.svg +24 -8
  18. package/dist/assets/mascot/expressions/hopeful-body.svg +85 -0
  19. package/dist/assets/mascot/expressions/hopeful-face.svg +59 -0
  20. package/dist/assets/mascot/expressions/idea-body.svg +85 -0
  21. package/dist/assets/mascot/expressions/idea-face.svg +59 -0
  22. package/dist/assets/mascot/expressions/laugh-body.svg +85 -0
  23. package/dist/assets/mascot/expressions/laugh-face.svg +59 -0
  24. package/dist/assets/mascot/expressions/love-body.svg +85 -0
  25. package/dist/assets/mascot/expressions/love-face.svg +59 -0
  26. package/dist/assets/mascot/expressions/nervous-body.svg +85 -0
  27. package/dist/assets/mascot/expressions/nervous-face.svg +59 -0
  28. package/dist/assets/mascot/expressions/neutral-body.svg +24 -8
  29. package/dist/assets/mascot/expressions/neutral-face.svg +24 -8
  30. package/dist/assets/mascot/expressions/proud-body.svg +85 -0
  31. package/dist/assets/mascot/expressions/proud-face.svg +59 -0
  32. package/dist/assets/mascot/expressions/relieved-body.svg +85 -0
  33. package/dist/assets/mascot/expressions/relieved-face.svg +59 -0
  34. package/dist/assets/mascot/expressions/sad-body.svg +24 -8
  35. package/dist/assets/mascot/expressions/sad-face.svg +24 -8
  36. package/dist/assets/mascot/expressions/shy-body.svg +85 -0
  37. package/dist/assets/mascot/expressions/shy-face.svg +59 -0
  38. package/dist/assets/mascot/expressions/sleepy-body.svg +24 -8
  39. package/dist/assets/mascot/expressions/sleepy-face.svg +24 -8
  40. package/dist/assets/mascot/expressions/starstruck-body.svg +85 -0
  41. package/dist/assets/mascot/expressions/starstruck-face.svg +59 -0
  42. package/dist/assets/mascot/expressions/surprised-body.svg +24 -8
  43. package/dist/assets/mascot/expressions/surprised-face.svg +24 -8
  44. package/dist/assets/mascot/expressions/thinking-body.svg +24 -8
  45. package/dist/assets/mascot/expressions/thinking-face.svg +24 -8
  46. package/dist/assets/mascot/expressions/wink-body.svg +24 -8
  47. package/dist/assets/mascot/expressions/wink-face.svg +24 -8
  48. package/dist/assets/mascot/expressions/worried-body.svg +24 -8
  49. package/dist/assets/mascot/expressions/worried-face.svg +24 -8
  50. package/dist/assets/mascot/loading.svg +59 -36
  51. package/dist/assets/mascot/mascot.css +24 -8
  52. package/dist/assets/mascot/mascot.js +64 -7
  53. package/dist/assets/mascot.html +15 -11
  54. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  55. package/dist/plugin/hooks/CLAUDE.md +1 -1
  56. package/dist/plugin/hooks/panel/model.ts +28 -0
  57. package/dist/plugin/hooks/panel/register.tsx +36 -4
  58. package/package.json +1 -1
@@ -3,6 +3,7 @@ import type { EngineInterface, Register } from 'claude-code'
3
3
 
4
4
  import {
5
5
  BRAND,
6
+ DEMO_PNG,
6
7
  PICKED,
7
8
  TEACH,
8
9
  EMPTY,
@@ -17,6 +18,8 @@ import {
17
18
  answerWas,
18
19
  demoLast,
19
20
  demoResult,
21
+ demoSvg,
22
+ demoSvgWithPng,
20
23
  explainerBrief,
21
24
  codify,
22
25
  gradingRequest,
@@ -414,9 +417,10 @@ export const register: Register = on => {
414
417
  on('ui.message', { component: 'Pane' }, async ($, e, next) => {
415
418
  if (e.requestId === PANE) {
416
419
  try {
420
+ const started = Date.now()
417
421
  const id = (e.data as { pick?: unknown } | null)?.pick
418
422
  const s: PanelState = (await $.state.get(REF)).value ?? EMPTY
419
- if (typeof id === 'string' && s.question?.options.some(o => o.id === id)) await pick($, id, 'whole card')
423
+ if (typeof id === 'string' && s.question?.options.some(o => o.id === id)) await pick($, id, 'whole card', started)
420
424
  } catch {
421
425
  /* Fail open: the card stays as it was. */
422
426
  }
@@ -438,7 +442,7 @@ export const register: Register = on => {
438
442
  on('ui.render', { component: 'Pane', requestId: 'eklavya-quiz' }, async ($, e, next) => {
439
443
  // Only our pane: every other pane, and everything else, is the host's.
440
444
  if (e.requestId !== PANE) return next(e)
441
- const { Box, Text, Button, Input, Markdown, Client } = $.ui.resolve(e) as any
445
+ const { Box, Text, Button, Input, Markdown, Client, Svg, Image } = $.ui.resolve(e) as any
442
446
  const s: PanelState = await read($, quiz)
443
447
  const q = s.question
444
448
 
@@ -619,6 +623,31 @@ export const register: Register = on => {
619
623
  )
620
624
  })
621
625
 
626
+ // The demo's picture test, under Teach me. The terminal has Image and no Svg; every other surface has Svg and no Image
627
+ // (a tree with an element its surface lacks is replaced whole by the host's own pane, so each surface gets only its own).
628
+ const experiment = s.demo ? (
629
+ <Box key="experiment" flexDirection="column" gap={1} borderStyle="dashed" borderColor="warning" paddingX={1} paddingY={app ? 1 : 0}>
630
+ <Text color="warning" bold>
631
+ {STR.experimentBanner}
632
+ </Text>
633
+ {app ? (
634
+ <Box flexDirection="column" gap={1}>
635
+ <Text dimColor>{STR.experimentSvg}</Text>
636
+ <Svg alt={STR.experimentImageAlt} width={208} height={96} source={demoSvg()} />
637
+ <Text dimColor>{STR.experimentSvgPng}</Text>
638
+ <Svg alt={STR.experimentImageAlt} width={208} height={96} source={demoSvgWithPng()} />
639
+ </Box>
640
+ ) : (
641
+ <Box flexDirection="column">
642
+ <Text dimColor wrap="wrap">
643
+ {STR.experimentTerminal}
644
+ </Text>
645
+ <Image key="experiment-image" source={{ png: DEMO_PNG }} columns={26} rows={6} alt={STR.experimentImageAlt} />
646
+ </Box>
647
+ )}
648
+ </Box>
649
+ ) : null
650
+
622
651
  return frame(
623
652
  stem,
624
653
  <Box flexDirection="column" gap={app ? 2 : 0} marginTop={app ? 1 : 0}>
@@ -673,6 +702,7 @@ export const register: Register = on => {
673
702
  </Button>
674
703
  </Box>
675
704
  ),
705
+ experiment,
676
706
  )
677
707
  })
678
708
  }
@@ -683,12 +713,14 @@ async function setStyle($: EngineInterface, style: 'button' | 'client'): Promise
683
713
  }
684
714
 
685
715
  /** Selecting an option records a draft and nothing else: it never submits, grades or skips. */
686
- async function pick($: EngineInterface, id: string, via = 'button'): Promise<void> {
716
+ async function pick($: EngineInterface, id: string, via = 'button', since?: number): Promise<void> {
687
717
  await update($, quiz, (s: PanelState) =>
688
718
  s.step === 'awaiting' || s.step === 'error'
689
- ? { ...s, draft: { ...s.draft, picked: id, other: false }, message: null, ...(s.demo ? { demo: { ...s.demo, last: demoLast(`${id} picked (${via})`) } } : {}) }
719
+ ? { ...s, draft: { ...s.draft, picked: id, other: false }, message: null, ...(s.demo ? { demo: { ...s.demo, last: demoLast(`${id} picked (${via}${since === undefined ? '' : `, ${Date.now() - since} ms in the mod`})`) } } : {}) }
690
720
  : s,
691
721
  )
722
+ // A whole card has no Button to focus, so asking the host to find `opt-…` would only make the pick wait.
723
+ if (via !== 'button') return
692
724
  try {
693
725
  // The host's focus mark follows the pick, so the highlighted answer is the chosen one even after a hotkey.
694
726
  await $.ui.focus({ requestId: PANE, key: `opt-${id}` })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eklavya",
3
- "version": "1.86.0",
3
+ "version": "1.88.0",
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": {