eklavya 1.9.2 → 1.9.4

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.
@@ -4,95 +4,126 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Eklavya — learning progress</title>
7
- <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect width='24' height='24' rx='5' fill='%23049D66'/%3E%3Cg fill='none' stroke='%23fff' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M7 5a11 11 0 0 1 0 14'/%3E%3Cpath d='M6 12h12'/%3E%3Cpath d='M14 9l3 3-3 3'/%3E%3C/g%3E%3C/svg%3E">
7
+ <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect width='24' height='24' fill='%230E6E66'/%3E%3Cg fill='none' stroke='%23fff' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M7 5a11 11 0 0 1 0 14'/%3E%3Cpath d='M6 12h12'/%3E%3Cpath d='M14 9l3 3-3 3'/%3E%3C/g%3E%3C/svg%3E">
8
+ <script>
9
+ /* Ground selection, shared with the site: same localStorage key, same default.
10
+ Runs before the stylesheet so the page never paints one ground then swaps.
11
+ Ink is the default and an absent choice stays ink — deliberately not the
12
+ reader's OS setting, because this is a terminal tool. */
13
+ (function () {
14
+ window.eklavyaGround = {
15
+ read: function () {
16
+ try { return localStorage.getItem('eklavya-ground') === 'paper' ? 'paper' : 'ink'; }
17
+ catch (e) { return 'ink'; }
18
+ },
19
+ apply: function (mode) {
20
+ document.documentElement.setAttribute('data-mode', mode);
21
+ document.querySelectorAll('[data-ground]').forEach(function (b) {
22
+ b.setAttribute('aria-pressed', String(b.dataset.ground === mode));
23
+ });
24
+ return mode;
25
+ },
26
+ set: function (mode) {
27
+ try { localStorage.setItem('eklavya-ground', mode); } catch (e) {}
28
+ this.apply(mode);
29
+ }
30
+ };
31
+ window.eklavyaGround.apply(window.eklavyaGround.read());
32
+ })();
33
+ </script>
8
34
  <link rel="stylesheet" href="/tokens.css">
9
35
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
36
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
- <link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
37
+ <link href="https://fonts.googleapis.com/css2?family=Archivo:wght@600;800;900&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
12
38
  <style>
13
39
  *, *::before, *::after { box-sizing: border-box; }
14
- body { margin:0; font-family:var(--font-sans); background:var(--bg-subtle); color:var(--fg);
15
- font-size:var(--text-base); line-height:var(--lh-base); }
40
+ body { margin:0; font-family:var(--font-body); background:var(--bg); color:var(--ink);
41
+ font-size:14.5px; line-height:1.65;
42
+ background-image:
43
+ linear-gradient(var(--grid) 1px, transparent 1px),
44
+ linear-gradient(90deg, var(--grid) 1px, transparent 1px);
45
+ background-size:72px 72px; }
46
+ ::selection { background:var(--spot); color:var(--spot-ink); }
16
47
  .wrap { max-width:1140px; margin:0 auto; padding:0 var(--space-8); }
17
48
  code, .mono { font-family:var(--font-mono); font-size:var(--text-sm); }
18
49
 
19
50
  /* ---------- header ---------- */
20
- header { background:var(--neutral-900); color:var(--on-dark); padding:var(--space-8) 0 var(--space-10); }
51
+ header { border-bottom:1px solid var(--line-2); padding:var(--space-8) 0 var(--space-10); }
21
52
  .bar { display:flex; align-items:center; gap:var(--space-4); flex-wrap:wrap; }
22
- .mark { width:36px; height:36px; border-radius:var(--radius-md); background:var(--brand);
53
+ .mark { width:34px; height:34px; background:var(--spot); color:var(--spot-ink);
23
54
  display:grid; place-items:center; flex:none; }
24
- h1 { font-size:var(--text-xl); font-weight:var(--weight-bold); letter-spacing:-.02em; margin:0; }
25
- .sub { color:var(--on-dark-muted); font-size:var(--text-sm); margin:2px 0 0; }
55
+ h1 { font-family:var(--font-disp); font-size:26px; font-weight:var(--weight-extra);
56
+ letter-spacing:var(--tracking-tight); margin:0; }
57
+ .sub { color:var(--dim); font-size:var(--text-sm); margin:2px 0 0; }
26
58
  .spacer { flex:1 1 auto; }
27
- select { font:inherit; font-size:var(--text-sm); background:var(--neutral-800); color:var(--on-dark);
28
- border:1px solid var(--neutral-700); border-radius:var(--radius-md);
59
+ select { font:inherit; font-size:var(--text-sm); background:var(--panel); color:var(--ink);
60
+ border:1px solid var(--line-2); border-radius:0;
29
61
  padding:var(--space-2) var(--space-3); }
30
62
  select:focus-visible { outline:none; box-shadow:var(--ring); }
31
63
 
32
64
  /* ---------- stat tiles ---------- */
33
65
  .tiles { display:grid; grid-template-columns:repeat(4,1fr); gap:var(--space-4); margin-top:var(--space-6); }
34
- .tile { background:var(--neutral-800); border:1px solid var(--neutral-700); border-radius:var(--radius-lg);
66
+ .tile { background:var(--panel); border:1px solid var(--line-2); border-radius:0;
35
67
  padding:var(--space-5); }
36
68
  .tile .k { font-size:var(--text-xs); font-weight:var(--weight-bold); letter-spacing:.08em;
37
- text-transform:uppercase; color:var(--on-dark-muted); }
69
+ text-transform:uppercase; color:var(--dim); }
38
70
  .tile .v { font-size:var(--text-3xl); font-weight:var(--weight-extra,800); letter-spacing:-.025em;
39
71
  line-height:1.1; margin-top:var(--space-2); }
40
- .tile .n { font-size:var(--text-sm); color:var(--on-dark-muted); margin-top:var(--space-1); }
41
- .good { color:var(--green-300); }
72
+ .tile .n { font-size:var(--text-sm); color:var(--dim); margin-top:var(--space-1); }
73
+ .good { color:var(--spot); }
42
74
 
43
75
  /* ---------- cards ---------- */
44
76
  main { padding:var(--space-10) 0 var(--space-20); }
45
- section { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-lg);
77
+ section { background:var(--panel); border:1px solid var(--line); border-radius:0;
46
78
  padding:var(--space-6); margin-bottom:var(--space-6); }
47
79
  h2 { font-size:var(--text-md); font-weight:var(--weight-bold); letter-spacing:-.01em; margin:0; }
48
- .hint { color:var(--fg-subtle); font-size:var(--text-sm); margin:var(--space-1) 0 var(--space-5); }
80
+ .hint { color:var(--faint); font-size:var(--text-sm); margin:var(--space-1) 0 var(--space-5); }
49
81
  .grid2 { display:grid; grid-template-columns:1fr 1fr; gap:var(--space-6); }
50
82
  .grid2 > section { margin:0; }
51
83
 
52
84
  /* ---------- tables ---------- */
53
85
  table { width:100%; border-collapse:collapse; font-size:var(--text-sm); }
54
86
  th { text-align:left; font-size:var(--text-xs); font-weight:var(--weight-bold); letter-spacing:.08em;
55
- text-transform:uppercase; color:var(--fg-subtle); padding:0 var(--space-3) var(--space-2) 0;
56
- border-bottom:1px solid var(--border); white-space:nowrap; }
57
- td { padding:var(--space-3) var(--space-3) var(--space-3) 0; border-bottom:1px solid var(--border);
87
+ text-transform:uppercase; color:var(--faint); padding:0 var(--space-3) var(--space-2) 0;
88
+ border-bottom:1px solid var(--line); white-space:nowrap; }
89
+ td { padding:var(--space-3) var(--space-3) var(--space-3) 0; border-bottom:1px solid var(--line);
58
90
  vertical-align:top; }
59
91
  tr:last-child td { border-bottom:none; }
60
92
  .num { text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }
61
93
  .scroll { overflow-x:auto; }
62
94
 
63
95
  /* ---------- meters (sequential, one hue) ---------- */
64
- .meter { height:8px; border-radius:var(--radius-pill); background:var(--bg-sunken); overflow:hidden;
96
+ .meter { height:8px; border-radius:var(--radius-pill); background:var(--mass); overflow:hidden;
65
97
  min-width:80px; }
66
- .meter > i { display:block; height:100%; background:var(--brand); border-radius:var(--radius-pill); }
98
+ .meter > i { display:block; height:100%; background:var(--spot); border-radius:var(--radius-pill); }
67
99
  .meter.warn > i { background:var(--warning); }
68
100
 
69
101
  /* ---------- chart ---------- */
70
- .legend { display:flex; gap:var(--space-5); font-size:var(--text-sm); color:var(--fg-muted);
102
+ .legend { display:flex; gap:var(--space-5); font-size:var(--text-sm); color:var(--dim);
71
103
  margin-bottom:var(--space-4); flex-wrap:wrap; }
72
104
  .legend b { display:inline-block; width:10px; height:10px; border-radius:3px; margin-right:var(--space-2); }
73
105
  .chart { position:relative; }
74
106
  .chart svg { display:block; width:100%; height:200px; overflow:visible; }
75
- .gridline { stroke:var(--border); stroke-width:1; }
76
- .axis { font-family:var(--font-mono); font-size:10px; fill:var(--fg-faint); }
107
+ .gridline { stroke:var(--line); stroke-width:1; }
108
+ .axis { font-family:var(--font-mono); font-size:10px; fill:var(--faint-2); }
77
109
  .hit { fill:transparent; cursor:pointer; }
78
110
  .hit:hover + .col, .hit:focus-visible + .col { opacity:.75; }
79
111
  .tip { position:absolute; pointer-events:none; opacity:0; transform:translate(-50%,-100%);
80
- background:var(--neutral-900); color:var(--on-dark); border-radius:var(--radius-md);
81
- padding:var(--space-3); font-size:var(--text-xs); line-height:1.6; white-space:nowrap;
82
- box-shadow:var(--shadow-md); transition:opacity var(--duration-fast) var(--ease); z-index:2; }
112
+ background:var(--panel); color:var(--ink); border-radius:0;
113
+ padding:var(--space-3); font-size:var(--text-xs); line-height:1.6; white-space:nowrap; transition:opacity var(--duration-fast) var(--ease); z-index:2; }
83
114
  .tip.on { opacity:1; }
84
115
  .tip .r { display:flex; justify-content:space-between; gap:var(--space-4); }
85
116
 
86
117
  /* ---------- pills & misc ---------- */
87
118
  .pill { display:inline-block; font-family:var(--font-mono); font-size:var(--text-xs);
88
- padding:2px 8px; border-radius:var(--radius-pill); background:var(--bg-sunken);
89
- color:var(--fg-muted); white-space:nowrap; }
90
- .pill.due { background:var(--warning-bg); color:#8a5600; }
91
- .pill.ok { background:var(--brand-subtle); color:var(--green-700); }
92
- .ctx { color:var(--fg); }
93
- .slug { font-family:var(--font-mono); font-size:var(--text-xs); color:var(--fg-subtle); }
94
- .empty { color:var(--fg-subtle); font-size:var(--text-sm); }
95
- footer { color:var(--fg-subtle); font-size:var(--text-sm); text-align:center; padding-bottom:var(--space-10); }
119
+ padding:2px 8px; border-radius:var(--radius-pill); background:var(--mass);
120
+ color:var(--dim); white-space:nowrap; }
121
+ .pill.due { background:var(--mass); color:var(--warning); }
122
+ .pill.ok { background:var(--spot-soft); color:var(--spot); }
123
+ .ctx { color:var(--ink); }
124
+ .slug { font-family:var(--font-mono); font-size:var(--text-xs); color:var(--faint); }
125
+ .empty { color:var(--faint); font-size:var(--text-sm); }
126
+ footer { color:var(--faint); font-size:var(--text-sm); text-align:center; padding-bottom:var(--space-10); }
96
127
 
97
128
  @media (max-width:900px) {
98
129
  .tiles { grid-template-columns:repeat(2,1fr); }
@@ -104,6 +135,17 @@
104
135
  .tile .v { font-size:var(--text-2xl); }
105
136
  }
106
137
  @media (prefers-reduced-motion:reduce) { * { transition:none !important; animation:none !important; } }
138
+
139
+ /* ---------- the ground toggle ---------- */
140
+ .ground { display:flex; border:1px solid var(--line-2); flex:none; }
141
+ .ground button {
142
+ font-family:var(--font-mono); font-size:11px; letter-spacing:.12em;
143
+ text-transform:uppercase; background:none; border:0; color:var(--faint);
144
+ padding:6px 11px; cursor:pointer; line-height:1.4;
145
+ transition:background var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
146
+ }
147
+ .ground button:hover { color:var(--ink); }
148
+ .ground button[aria-pressed="true"] { background:var(--ink); color:var(--bg); }
107
149
  </style>
108
150
  </head>
109
151
  <body>
@@ -111,7 +153,7 @@
111
153
  <div class="wrap">
112
154
  <div class="bar">
113
155
  <div class="mark" aria-hidden="true">
114
- <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="1.8"
156
+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"
115
157
  stroke-linecap="round" stroke-linejoin="round">
116
158
  <path d="M5 3a13 13 0 0 1 0 18"/><path d="M5 3l14 9L5 21"/><path d="M3 12h18"/><path d="M15 8l4 4-4 4"/>
117
159
  </svg>
@@ -121,6 +163,10 @@
121
163
  <p class="sub" id="sub">Loading…</p>
122
164
  </div>
123
165
  <div class="spacer"></div>
166
+ <div class="ground" role="group" aria-label="Colour ground">
167
+ <button type="button" data-ground="ink" aria-pressed="true">ink</button>
168
+ <button type="button" data-ground="paper" aria-pressed="false">paper</button>
169
+ </div>
124
170
  <label class="sub" for="proj">Project</label>
125
171
  <select id="proj"><option value="*">All projects</option></select>
126
172
  </div>
@@ -133,9 +179,9 @@
133
179
  <h2>Answers over the last 30 days</h2>
134
180
  <p class="hint" id="chart-hint">Right, missed and skipped, per day.</p>
135
181
  <div class="legend">
136
- <span><b style="background:var(--green-600)"></b>Right</span>
182
+ <span><b style="background:var(--spot)"></b>Right</span>
137
183
  <span><b style="background:var(--warning)"></b>Missed</span>
138
- <span><b style="background:var(--neutral-400)"></b>Skipped</span>
184
+ <span><b style="background:var(--faint)"></b>Skipped</span>
139
185
  </div>
140
186
  <div class="chart"><div class="tip" id="tip"></div><svg id="chart" role="img"
141
187
  aria-label="Daily answers, split into right, missed and skipped"></svg></div>
@@ -262,11 +308,11 @@ function renderChart() {
262
308
  }
263
309
  // Stack order is fixed: right at the base, then missed, then skipped on top.
264
310
  // Segment colour never depends on rank, only on which measure it is.
265
- const SEGMENTS = [['passed', 'var(--green-600)'], ['missed', 'var(--warning)'], ['skipped', 'var(--neutral-400)']];
311
+ const SEGMENTS = [['passed', 'var(--spot)'], ['missed', 'var(--warning)'], ['skipped', 'var(--faint)']];
266
312
  const labelEvery = Math.max(1, Math.ceil(46 / step));
267
313
  days.forEach((d, i) => {
268
314
  const x = PAD_L + i * step + (step - bw) / 2;
269
- let acc = 0, seg = '', topColor = 'var(--green-600)';
315
+ let acc = 0, seg = '', topColor = 'var(--spot)';
270
316
  for (const [key, color] of SEGMENTS) {
271
317
  if (!d[key]) continue;
272
318
  const h = (d[key] / max) * (H - PAD_B - 8);
@@ -403,5 +449,10 @@ fetch('/api/state').then((r) => r.json()).then((s) => {
403
449
  addEventListener('resize', renderChart);
404
450
  }).catch((e) => { $('sub').textContent = 'Could not read the database: ' + e.message; });
405
451
  </script>
452
+ <script>
453
+ document.querySelectorAll('[data-ground]').forEach(function (b) {
454
+ b.addEventListener('click', function () { window.eklavyaGround.set(b.dataset.ground); });
455
+ });
456
+ </script>
406
457
  </body>
407
458
  </html>
@@ -34,14 +34,15 @@
34
34
  --vd-300: #79D5C4; /* accent on ink — 10.33:1 */
35
35
  --vd-400: #3FB8A3;
36
36
  --vd-500: #199688;
37
- --vd-600: #0E6E66; /* accent on paper — 5.22:1 */
38
- --vd-700: #0A5751;
37
+ --vd-600: #0E6E66;
38
+ --vd-700: #0A5751; /* accent on paper — 7.20:1 on the ground, 5.10:1 on a tint */
39
39
  --vd-800: #07403C;
40
40
  --vd-900: #052E2B;
41
41
 
42
42
  /* ---------- SEMANTIC (real errors and warnings only) ----------
43
- Kept off the accent hue so state never reads as branding. */
44
- --warning: #E8920C;
43
+ Kept off the accent hue so state never reads as branding. Both are
44
+ redefined per ground below: an orange that carries on ink is 2.3:1 on
45
+ cream, which fails as text and as a chart fill alike. */
45
46
  --error: #E5484D;
46
47
  --info: #4A9EFF;
47
48
 
@@ -128,7 +129,7 @@
128
129
 
129
130
  --ink: #EAE7E1; /* primary text */
130
131
  --dim: #A29C93; /* secondary */
131
- --faint: #77726A; /* captions, micro-labels */
132
+ --faint: #8B857B; /* captions, micro-labels — 4.89:1 on ink */
132
133
  --faint-2: #565249; /* disabled, placeholder */
133
134
 
134
135
  --line: #26262a; /* hairline */
@@ -142,6 +143,8 @@
142
143
  --glow: rgba(25,150,136,.22); /* the terminal's halo */
143
144
  --glow-low: rgba(25,150,136,.10);
144
145
  --shot-op: .28; /* the arrow scene, as ambient art */
146
+ --warning: #E8920C;
147
+ --code-bg: var(--mass); /* the well behind a code block */
145
148
  --shadow-lg: 0 12px 28px rgba(0,0,0,.45);
146
149
  --ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--spot);
147
150
  }
@@ -153,13 +156,13 @@
153
156
 
154
157
  --ink: #16150f;
155
158
  --dim: #57534a;
156
- --faint: #8a8578;
159
+ --faint: #635E52; /* 5.04:1 on --panel, the darkest ground it sits on */
157
160
  --faint-2: #a09a8c;
158
161
 
159
162
  --line: #e3dfd4;
160
163
  --line-2: #cec8b9;
161
164
 
162
- --spot: var(--vd-600);
165
+ --spot: var(--vd-700);
163
166
  --spot-ink: #ffffff;
164
167
  --spot-soft: color-mix(in srgb, var(--spot) 10%, transparent);
165
168
 
@@ -169,6 +172,11 @@
169
172
  --glow: rgba(14,110,102,.10);
170
173
  --glow-low: rgba(14,110,102,.05);
171
174
  --shot-op: .5;
175
+ --warning: #7A4B00; /* 5.21:1 on --mass */
176
+ /* A syntax theme's dimmest token (comments) lands at 3.9:1 on --mass. The
177
+ well is lightened rather than the theme repainted, which lifts every
178
+ token at once; the frame's border is what still reads it as a block. */
179
+ --code-bg: #EDEAE2;
172
180
  --shadow-lg: 0 12px 28px rgba(22,21,15,.10);
173
181
  --ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--spot);
174
182
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eklavya",
3
3
  "displayName": "Eklavya",
4
- "version": "1.9.2",
4
+ "version": "1.9.4",
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"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eklavya",
3
- "version": "1.9.2",
3
+ "version": "1.9.4",
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": {