dsh-plugin-lookatstudy 0.12.0 → 0.12.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.
package/lib/client.js CHANGED
@@ -22,7 +22,14 @@ window.__ModuleLoader__.load({
22
22
  * fonts, and radii come from `--dsw-*` tokens wherever the concept exists.
23
23
  */
24
24
  const STUDY_CSS = `
25
- .lks-root{font-family:var(--dsw-font-family)}
25
+ /* State inks: small colored text must clear WCAG AA (4.5:1), but the host's
26
+ raw state tokens (success/warn primaries) sit at 2.3–2.6:1 on light
27
+ backgrounds, and success/error *label* aliases are not published by this
28
+ host at all (the tv-chip used to reference them and silently inherit).
29
+ Mixing the state color toward label-primary keeps the hue while pulling
30
+ luminance to the readable pole — and works in BOTH themes, because
31
+ label-primary is always the current theme's high-contrast ink. */
32
+ .lks-root{--lks-warn-ink:color-mix(in srgb,var(--dsw-alias-state-warn-label) 45%,var(--dsw-alias-label-primary));--lks-ok-ink:color-mix(in srgb,var(--dsw-alias-state-success-primary) 40%,var(--dsw-alias-label-primary));--lks-err-ink:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,var(--dsw-alias-label-primary));font-family:var(--dsw-font-family)}
26
33
  .lks-root :where(button){font-family:var(--dsw-font-family);cursor:pointer;border:none;background:none;padding:0}
27
34
  .lks-root :where(a){color:var(--dsw-alias-state-business-primary)}
28
35
 
@@ -42,9 +49,9 @@ window.__ModuleLoader__.load({
42
49
  /* Direction carrier (a container query cannot style the container itself). */
43
50
  .lks-body{flex:1;min-height:0;min-width:0;display:flex;gap:16px}
44
51
  .lks-col{display:flex;flex-direction:column;min-width:0}
45
- .lks-colhead{flex:none;font-size:12px;font-weight:600;color:var(--dsw-alias-label-tertiary);letter-spacing:.06em;text-transform:uppercase;margin-bottom:10px}
52
+ .lks-colhead{flex:none;font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary);letter-spacing:.06em;text-transform:uppercase;margin-bottom:10px}
46
53
  .lks-col-rail{flex:0 1 260px;min-width:180px;overflow-y:auto;padding:2px 6px}
47
- .lks-col-tutor{flex:0 1 auto;width:min(100%,var(--dsh-composer-card-max-width,780px),42cqi);min-width:360px}
54
+ .lks-col-tutor{flex:0 1 auto;width:min(100%,var(--dsh-composer-card-max-width,780px),42cqi);min-width:360px;overflow:hidden}
48
55
  .lks-col-bb{flex:1 1 0;min-width:320px;overflow-y:auto;padding:2px 6px}
49
56
  /* While the study view is mounted in wide mode, the host composer card is
50
57
  shifted under the tutor column (the transcript's width axis) instead of the
@@ -60,7 +67,7 @@ window.__ModuleLoader__.load({
60
67
  .lks-switch{display:none;flex-direction:row;gap:2px;align-self:center;margin-bottom:10px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:3px}
61
68
  .lks-switch-btn{border-radius:999px;padding:5px 18px;font-size:14px;color:var(--dsw-alias-label-tertiary);background:transparent;transition:background .12s ease,color .12s ease}
62
69
  .lks-switch-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}
63
- .lks-switch-btn.on{background:var(--dsw-alias-state-business-primary);color:#fff;font-weight:600}
70
+ .lks-switch-btn.on{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 86%,#000);color:#fff;font-weight:600}
64
71
  @container lksstudy (max-width: 1220px){
65
72
  .lks-body{flex-direction:column}
66
73
  .lks-switch{display:flex}
@@ -77,37 +84,37 @@ window.__ModuleLoader__.load({
77
84
  .lks-rail-head .lks-rail-select{flex:1;min-width:0;margin-bottom:0}
78
85
  .lks-rail-head .lks-btn{padding:3px 8px;font-size:13px}
79
86
  .lks-rail-title{font-size:16px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
80
- .lks-rail-sub{font-size:13px;color:var(--dsw-alias-label-tertiary);margin:4px 0 6px}
87
+ .lks-rail-sub{font-size:13px;color:var(--dsw-alias-label-secondary);margin:4px 0 6px}
81
88
  .lks-masterybar{height:5px;border-radius:3px;background:var(--dsw-alias-bg-layer-3);overflow:hidden;margin-bottom:8px}
82
- .lks-masterybar i{display:block;height:100%;background:var(--dsw-alias-state-business-primary);transition:width .3s ease}
89
+ .lks-masterybar i{display:block;height:100%;width:100%;background:var(--dsw-alias-state-success-primary);transform-origin:0 50%;transition:transform .3s ease}
83
90
  .lks-masterybar.gold i{background:var(--dsw-alias-state-warn-primary)}
84
91
  .lks-search{width:100%;box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-primary);font-family:inherit;font-size:13.5px;padding:5px 9px;margin-bottom:4px}
85
92
  .lks-search:focus{outline:none;border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
86
- .lks-sec-num{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;border-radius:50%;background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label);font-size:11px;font-weight:700;margin-right:4px}
87
- .lks-tag.due{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label)}
93
+ .lks-sec-num{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;border-radius:50%;background:var(--dsw-alias-state-warn-tertiary);color:var(--lks-warn-ink);font-size:11px;font-weight:700;margin-right:4px}
94
+ .lks-tag.due{background:var(--dsw-alias-state-warn-tertiary);color:var(--lks-warn-ink)}
88
95
  .lks-import{margin-top:8px}
89
96
  .lks-import .lks-inputrow{margin-top:0}
90
97
  .lks-inputrow{display:flex;gap:8px;align-items:flex-end;flex:none;margin-top:8px}
91
98
  .lks-input{flex:1;min-width:0;resize:none;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;color:var(--dsw-alias-label-primary);font-family:inherit;font-size:14px;line-height:1.6;padding:8px 10px}
92
99
  .lks-input:focus{outline:none;border-color:var(--dsw-alias-state-business-primary)}
93
100
  .lks-input:disabled{opacity:.55}
94
- .lks-import-hint{font-size:12.5px;color:var(--dsw-alias-label-tertiary);margin-top:6px;line-height:1.7}
101
+ .lks-import-hint{font-size:12.5px;color:var(--dsw-alias-label-secondary);margin-top:6px;line-height:1.7}
95
102
  .lks-duebox{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:10px 12px;margin:8px 0 14px;font-size:13.5px}
96
103
  .lks-duebox .lks-due-item{color:var(--dsw-alias-label-secondary);margin-top:4px;display:flex;justify-content:space-between;gap:10px}
97
- .lks-duebox .lks-over{color:var(--dsw-alias-state-warn-label);flex:none}
104
+ .lks-duebox .lks-over{color:var(--lks-warn-ink);flex:none}
98
105
 
99
- .lks-sec{font-size:12px;color:var(--dsw-alias-label-tertiary);text-transform:uppercase;letter-spacing:.05em;margin:14px 0 4px}
106
+ .lks-sec{font-size:12px;color:var(--dsw-alias-label-secondary);text-transform:uppercase;letter-spacing:.05em;margin:14px 0 4px}
100
107
  /* Collapsible section head: a real button (keyboard can toggle it), sticky so
101
108
  the current chapter stays identified while 4000px of nodes scroll under it. */
102
- .lks-sechead{display:flex;align-items:center;gap:6px;width:100%;box-sizing:border-box;font-size:12px;font-weight:600;color:var(--dsw-alias-label-tertiary);text-transform:uppercase;letter-spacing:.05em;text-align:left;padding:6px 4px;margin:14px 0 4px;border-radius:6px;position:sticky;top:0;z-index:1;background:var(--dsw-alias-bg-base)}
109
+ .lks-sechead{display:flex;align-items:center;gap:6px;width:100%;box-sizing:border-box;font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary);text-transform:uppercase;letter-spacing:.05em;text-align:left;padding:6px 4px;margin:14px 0 4px;border-radius:6px;position:sticky;top:0;z-index:1;background:var(--dsw-alias-bg-base)}
103
110
  .lks-sechead:hover{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover)}
104
- .lks-sechead:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}
111
+ .lks-sechead:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}
105
112
  .lks-sechead-t{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
106
113
  .lks-sechead-c{flex:none;font-size:10px}
107
114
  .lks-sechead-n{flex:none;font-size:11px;text-transform:none;letter-spacing:0}
108
115
  .lks-node{display:flex;align-items:center;gap:8px;width:100%;text-align:left;padding:6px 9px;border-radius:8px;margin:1px 0;cursor:pointer}
109
116
  .lks-node:hover{background:var(--dsw-alias-interactive-bg-hover)}
110
- .lks-node:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}
117
+ .lks-node:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}
111
118
  .lks-node.focus{background:var(--dsw-alias-bg-layer-3);outline:1px solid var(--dsw-alias-border-l2)}
112
119
  .lks-node .lks-g{width:18px;text-align:center;flex:none}
113
120
  .lks-node .lks-t{flex:1;font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@@ -117,13 +124,13 @@ window.__ModuleLoader__.load({
117
124
  .lks-node[aria-disabled='true']{opacity:.45;cursor:not-allowed}
118
125
  .lks-node[aria-disabled='true']:hover{background:none}
119
126
  .lks-tag{font-size:11px;border-radius:4px;padding:0 4px;flex:none}
120
- .lks-tag.weak{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label)}
121
- .lks-tag.fric{background:var(--dsw-alias-state-error-primary);color:#fff}
127
+ .lks-tag.weak{background:var(--dsw-alias-state-warn-tertiary);color:var(--lks-warn-ink)}
128
+ .lks-tag.fric{background:var(--dsw-alias-state-error-tertiary);color:var(--lks-err-ink)}
122
129
  .lks-bar{width:44px;height:4px;border-radius:2px;background:var(--dsw-alias-bg-layer-3);overflow:hidden;flex:none}
123
- .lks-bar i{display:block;height:100%;background:var(--dsw-alias-state-success-primary)}
124
- .lks-pct{font-size:12px;color:var(--dsw-alias-label-tertiary);flex:none;width:32px;text-align:right}
130
+ .lks-bar i{display:block;height:100%;width:100%;background:var(--dsw-alias-state-success-primary);transform-origin:0 50%}
131
+ .lks-pct{font-size:12px;color:var(--dsw-alias-label-secondary);flex:none;width:32px;text-align:right}
125
132
 
126
- .lks-empty{color:var(--dsw-alias-label-tertiary);text-align:center;padding:48px 8px;font-size:14px;line-height:2}
133
+ .lks-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:48px 8px;font-size:14px;line-height:2}
127
134
  .lks-actbar{display:flex;flex-direction:row;justify-content:flex-end;flex:none;padding-bottom:8px}
128
135
 
129
136
  /* ── middle column: the tutor ── */
@@ -132,31 +139,43 @@ window.__ModuleLoader__.load({
132
139
  .lks-msg.user{align-self:flex-end;white-space:pre-wrap;background:var(--dsw-alias-state-business-tertiary);color:var(--dsw-alias-label-primary-bluish)}
133
140
  .lks-msg.assistant{align-self:flex-start;background:var(--dsw-alias-bg-layer-2)}
134
141
  .lks-msg.streaming{opacity:.7}
135
- .lks-msg.thinking{align-self:flex-start;display:inline-flex;align-items:center;gap:8px;font-size:13px;color:var(--dsw-alias-label-tertiary);background:none;border-radius:0;padding:2px}
136
- .lks-msg.tool{align-self:flex-start;font-size:12px;color:var(--dsw-alias-label-tertiary);background:none;border-radius:0;padding:1px 2px}
137
- .lks-msg.tool.ok{color:var(--dsw-alias-state-success-primary)}
138
- .lks-msg.tool.bad{color:var(--dsw-alias-state-error-primary)}
142
+ .lks-msg.thinking{align-self:flex-start;display:inline-flex;align-items:center;gap:8px;font-size:13px;color:var(--dsw-alias-label-secondary);background:none;border-radius:0;padding:2px}
139
143
  .lks-msg.error{align-self:stretch;color:var(--dsw-alias-state-error-primary);font-size:13px;background:none;border-radius:0;padding:1px 2px}
140
144
  /* quiz-taking surface: the tutor's A–D options become clickable answers on the last settled reply */
141
145
  .lks-turn{display:flex;flex-direction:column;align-items:flex-start;max-width:94%}
142
146
  .lks-quiz{display:flex;flex-direction:column;gap:6px;width:100%;margin:2px 0 4px}
143
147
  .lks-opt{display:flex;align-items:baseline;gap:10px;text-align:left;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:10px;padding:8px 14px;font-size:14px;line-height:1.5;color:var(--dsw-alias-label-secondary);cursor:pointer;font-family:inherit;transition:border-color .12s ease,color .12s ease,background .12s ease}
144
148
  .lks-opt:hover{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-state-business-tertiary)}
145
- .lks-opt:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}
149
+ .lks-opt:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}
146
150
  .lks-optletter{font-weight:700;color:var(--dsw-alias-label-primary-bluish);flex:none}
147
151
 
148
152
  /* soul pills (native tool-row trigger language: 28px transparent, tinted active) */
149
153
  .lks-pills{display:inline-flex;align-items:center;height:28px}
150
154
  .lks-pill{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border:none;border-radius:24px;background:transparent;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px;font-weight:500;transition:background .12s ease,color .12s ease}
151
155
  .lks-pill:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}
152
- .lks-pill:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}
156
+ .lks-pill:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}
153
157
  .lks-pill.on{background:var(--dsw-alias-state-business-tertiary);color:var(--dsw-alias-label-primary-bluish)}
154
158
  .lks-pill.on:hover{background:var(--dsw-alias-state-business-tertiary);color:var(--dsw-alias-label-primary-bluish)}
155
159
 
160
+ /* SVG glyph alignment (status icons, tag badges, dock segments, icon buttons) */
161
+ .lks-g svg{display:block;margin:0 auto}
162
+ .lks-tag{display:inline-flex;align-items:center;gap:2px;line-height:1}
163
+ .lks-dockseg{display:inline-flex;align-items:center;gap:3px}
164
+ .lks-dock-due svg,.lks-dock-streak svg{flex:none}
165
+ .lks-dock-due.lks-muted svg,.lks-dock-streak.lks-muted svg{opacity:.75}
166
+ .lks-btn svg,.lks-viewtab svg,.lks-sechead svg{flex:none}
167
+ .lks-viewtab svg,.lks-btn svg{align-self:center}
168
+
156
169
  /* ic_ds_* glyph carriers + busy spinner (native .8s linear spin) */
157
170
  @keyframes lks-spin{to{transform:rotate(360deg)}}
158
171
  .lks-spin{animation:lks-spin .8s linear infinite}
159
172
 
173
+ /* dormant state: pills/starters stay visible but inert (aria-disabled, same
174
+ contract as locked lesson rows — focusable so the hint explains why) */
175
+ .lks-pill[aria-disabled='true'],.lks-starter[aria-disabled='true']{opacity:.45;cursor:not-allowed}
176
+ .lks-pill[aria-disabled='true']:hover,.lks-starter[aria-disabled='true']:hover{background:transparent;border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary)}
177
+ .lks-dormant{font-size:13px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);border:1px dashed var(--dsw-alias-border-l2);border-radius:10px;padding:10px 12px;margin:8px 0;line-height:1.7}
178
+
160
179
  /* starter chips */
161
180
  .lks-dock{display:flex;align-items:center;flex-wrap:wrap;gap:6px;padding:0 2px 6px;flex:none}
162
181
  .lks-starter{border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:4px 12px;font-size:13px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);transition:border-color .12s ease,color .12s ease}
@@ -168,7 +187,8 @@ window.__ModuleLoader__.load({
168
187
  .lks-banner{display:flex;align-items:center;gap:10px;background:var(--dsw-alias-state-business-tertiary);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:10px 14px;margin:8px 0;font-size:14px;flex:none}
169
188
  .lks-banner .lks-why{flex:1;color:var(--dsw-alias-label-secondary)}
170
189
  .lks-btn{display:inline-flex;align-items:center;gap:6px;border-radius:8px;padding:6px 14px;font-size:13.5px;font-weight:600;flex:none}
171
- .lks-btn.primary{background:var(--dsw-alias-state-business-primary);color:#fff}
190
+ .lks-btn:focus-visible,.lks-starter:focus-visible,.lks-switch-btn:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}
191
+ .lks-btn.primary{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 86%,#000);color:#fff}
172
192
  .lks-btn.primary:hover{filter:brightness(1.1)}
173
193
  .lks-btn.primary:disabled{opacity:.5;cursor:default;filter:none}
174
194
  .lks-btn.ghost{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l2)}
@@ -176,10 +196,18 @@ window.__ModuleLoader__.load({
176
196
 
177
197
  /* ── right column: blackboard ── */
178
198
  .lks-lessonhead h2{font-size:18px;margin:0 0 4px;font-weight:700}
179
- .lks-lessonhead .lks-meta{color:var(--dsw-alias-label-tertiary);font-size:13px;margin-bottom:4px}
199
+ .lks-lessonhead .lks-meta{color:var(--dsw-alias-label-secondary);font-size:13px;margin-bottom:4px}
180
200
  .lks-chips{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0}
181
201
  .lks-chip{border-radius:999px;padding:2px 10px;font-size:12.5px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary)}
182
- .lks-chip.weak{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label)}
202
+ .lks-chip.weak{background:var(--dsw-alias-state-warn-tertiary);color:var(--lks-warn-ink)}
203
+
204
+ /* blackboard view tabs (same joined-capsule button language as the narrow
205
+ mode switcher — buttons, not tabs, against the host's view tabs above) */
206
+ .lks-viewtabs{display:flex;gap:2px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:3px;width:max-content;margin:10px 0}
207
+ .lks-viewtab{border-radius:999px;padding:4px 16px;font-size:13.5px;color:var(--dsw-alias-label-secondary);background:transparent;transition:background .12s ease,color .12s ease}
208
+ .lks-viewtab:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
209
+ .lks-viewtab.on{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 86%,#000);color:#fff;font-weight:600}
210
+ .lks-viewtab:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}
183
211
 
184
212
  /* rendered markdown (host-sanitized lesson bodies and assistant replies) */
185
213
  .lks-prose{font-size:16px;line-height:1.75}
@@ -204,11 +232,15 @@ window.__ModuleLoader__.load({
204
232
  /* Cornell notes zones */
205
233
  .lks-bb-notes{margin-top:14px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l1)}
206
234
  .lks-zone{margin-bottom:14px}
207
- .lks-zone h4{font-size:14px;color:var(--dsw-alias-label-secondary);margin:0 0 8px;font-weight:600}
235
+ .lks-zone-h{font-size:14px;color:var(--dsw-alias-label-secondary);margin:0 0 8px;font-weight:600}
208
236
  .lks-note{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:10px 14px;margin-bottom:8px}
209
237
  .lks-note .lks-note-src{float:right;font-size:11.5px;color:var(--dsw-alias-label-secondary)}
210
238
  .lks-note .lks-note-title{font-weight:600;font-size:13px}
211
- .lks-note .lks-note-text{margin-top:4px;white-space:pre-wrap;font-size:13px;color:var(--dsw-alias-label-secondary)}
239
+ .lks-note .lks-note-text{margin-top:4px;font-size:13px;color:var(--dsw-alias-label-secondary);line-height:1.65}
240
+ .lks-note .lks-note-text p{margin:4px 0}
241
+ .lks-note .lks-note-text table{border-collapse:collapse;margin:6px 0;display:block;max-width:100%;overflow-x:auto}
242
+ .lks-note .lks-note-text th,.lks-note .lks-note-text td{border:1px solid var(--dsw-alias-border-l2);padding:4px 8px;font-size:12.5px}
243
+ .lks-note .lks-note-text code{font-family:var(--dsw-font-markdown-code);background:var(--dsw-alias-bg-layer-3);border-radius:4px;padding:1px 4px;font-size:.95em}
212
244
  .lks-note .lks-note-q{margin-top:6px;color:var(--dsw-alias-label-tertiary);font-size:12px;border-left:2px solid var(--dsw-alias-state-warn-primary);padding-left:8px}
213
245
 
214
246
  /* inline error text (write-action failures) */
@@ -217,24 +249,25 @@ window.__ModuleLoader__.load({
217
249
  /* settings section (settings.section entry inside the host settings shell) */
218
250
  .lks-settings{display:flex;flex-direction:column;gap:18px;font-family:var(--dsw-font-family);color:var(--dsw-alias-label-primary)}
219
251
  .lks-set-row h3{margin:0 0 4px;font-size:15px;font-weight:600}
220
- .lks-set-hint{margin:0 0 10px;font-size:13px;color:var(--dsw-alias-label-tertiary)}
252
+ .lks-set-hint{margin:0 0 10px;font-size:13px;color:var(--dsw-alias-label-secondary)}
221
253
  .lks-set-state{margin-left:10px;font-size:13px;color:var(--dsw-alias-label-secondary)}
222
254
  .lks-set-stats{margin:0;padding-left:18px;font-size:14px;line-height:1.9}
223
255
  .lks-set-path{font-family:var(--dsw-font-markdown-code);font-size:12.5px;background:var(--dsw-alias-bg-layer-3);border-radius:6px;padding:3px 8px;word-break:break-all}
224
256
 
225
257
  /* composer dock status pill (conversation.composer.dock entry) */
226
- .lks-dockpill{display:inline-flex;gap:10px;align-items:center;font-size:12px;color:var(--dsw-alias-label-tertiary)}
258
+ .lks-dockpill{display:inline-flex;gap:10px;align-items:center;font-size:12px;color:var(--dsw-alias-label-secondary)}
227
259
  .lks-dockseg{white-space:nowrap}
228
- .lks-dock-due{color:var(--dsw-alias-state-warn-label)}
260
+ .lks-dock-due{color:var(--lks-warn-ink)}
229
261
  .lks-dock-streak{color:var(--dsw-alias-state-business-primary)}
262
+ .lks-dockseg.lks-muted{color:var(--dsw-alias-label-secondary)}
230
263
 
231
264
  /* keyed tool.call.toolview cards (conversation tab tool rows) */
232
265
  .lks-tv{display:flex;flex-direction:column;gap:4px;padding:6px 10px;border-radius:10px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);font-size:13px}
233
266
  .lks-tv.err{border-color:var(--dsw-alias-state-error-primary)}
234
267
  .lks-tv-head{font-weight:600;color:var(--dsw-alias-label-secondary);font-size:12px}
235
268
  .lks-tv-chip{align-self:flex-start;border-radius:999px;padding:2px 10px;font-size:12.5px;font-weight:600}
236
- .lks-tv-chip.ok{background:var(--dsw-alias-state-success-tertiary);color:var(--dsw-alias-state-success-label)}
237
- .lks-tv-chip.bad{background:var(--dsw-alias-state-error-tertiary);color:var(--dsw-alias-state-error-label)}
269
+ .lks-tv-chip.ok{background:var(--dsw-alias-state-success-tertiary);color:var(--lks-ok-ink)}
270
+ .lks-tv-chip.bad{background:var(--dsw-alias-state-error-tertiary);color:var(--lks-err-ink)}
238
271
  .lks-tv-line{color:var(--dsw-alias-label-secondary);line-height:1.6;white-space:pre-wrap}
239
272
  `;
240
273
  /**
@@ -260,6 +293,11 @@ window.__ModuleLoader__.load({
260
293
  * importing the host package root would drag its CSS modules into the
261
294
  * inlined bundle. Re-sync these three paths if the host icon set redesigns
262
295
  * them; scripts/verify.mjs pins the path data.
296
+ *
297
+ * 2026-09-01 critique round: seven more ic_ds glyphs vendored (play/plus/
298
+ * trash/refresh/globe/goal/warning) plus plugin-original learning-domain
299
+ * companions (lock/star/book/crown/bolt/power/pin/flame) drawn in the same
300
+ * language — those are NOT ic_ds, see their section comment below.
263
301
  * @module dsh-plugin-lookatstudy/client/icons
264
302
  */
265
303
  /** ic_ds_think_outline_16 */
@@ -309,6 +347,249 @@ window.__ModuleLoader__.load({
309
347
  fill: "currentColor"
310
348
  }));
311
349
  }
350
+ /** Vendored 2026-09-01 for the critique icon round (same figma source as the three above). */
351
+ /** IconPlayOutline16 */
352
+ function IconPlayOutline16({ size = 16, className }) {
353
+ return (0, react.createElement)("svg", {
354
+ width: size,
355
+ height: size,
356
+ className,
357
+ viewBox: "0 0 16 16",
358
+ fill: "none",
359
+ xmlns: "http://www.w3.org/2000/svg"
360
+ }, (0, react.createElement)("path", {
361
+ d: "M14.1446 8C14.1446 4.6062 11.3938 1.85539 8 1.85539C4.6062 1.85539 1.85539 4.6062 1.85539 8C1.85539 11.3938 4.6062 14.1446 8 14.1446C11.3938 14.1446 14.1446 11.3938 14.1446 8ZM15.511 8C15.511 12.148 12.148 15.511 8 15.511C3.85202 15.511 0.489014 12.148 0.489014 8C0.489014 3.85202 3.85202 0.489014 8 0.489014C12.148 0.489014 15.511 3.85202 15.511 8Z",
362
+ fill: "currentColor"
363
+ }), (0, react.createElement)("path", {
364
+ d: "M10.5617 8.42578C10.852 8.21614 10.852 7.78386 10.5617 7.57422L7.25708 5.18751C6.90974 4.93666 6.42436 5.18484 6.42436 5.61329V10.3867C6.42436 10.8152 6.90974 11.0633 7.25708 10.8125L10.5617 8.42578Z",
365
+ fill: "currentColor"
366
+ }));
367
+ }
368
+ /** IconPlusOutline16 */
369
+ function IconPlusOutline16({ size = 16, className }) {
370
+ return (0, react.createElement)("svg", {
371
+ width: size,
372
+ height: size,
373
+ className,
374
+ viewBox: "0 0 16 16",
375
+ fill: "none",
376
+ xmlns: "http://www.w3.org/2000/svg"
377
+ }, (0, react.createElement)("path", {
378
+ d: "M8.64453 1.5V7.34961H14.5V8.65039H8.64453V14.5H7.34473V8.65039H1.5V7.34961H7.34473V1.5H8.64453Z",
379
+ fill: "currentColor"
380
+ }));
381
+ }
382
+ /** IconTrashOutline16 */
383
+ function IconTrashOutline16({ size = 16, className }) {
384
+ return (0, react.createElement)("svg", {
385
+ width: size,
386
+ height: size,
387
+ className,
388
+ viewBox: "0 0 16 16",
389
+ fill: "none",
390
+ xmlns: "http://www.w3.org/2000/svg"
391
+ }, (0, react.createElement)("path", {
392
+ d: "M14.4782 4.84067L14.2138 10.1152C14.1102 12.1872 14.067 13.0115 13.3866 13.9607C13.1044 14.3546 12.7498 14.6912 12.3424 14.9535C11.8239 15.2872 11.2415 15.4316 10.5585 15.4998C9.88727 15.5668 9.04946 15.5656 7.99998 15.5656C6.95051 15.5656 6.1127 15.5668 5.44142 15.4998C4.75851 15.4316 4.17602 15.2872 3.65753 14.9535C3.25012 14.6912 2.89559 14.3546 2.61332 13.9607C1.93296 13.0115 1.88979 12.1872 1.78619 10.1152L1.52179 4.84067L2.89006 4.77277L3.15343 10.0463C3.26221 12.2218 3.32452 12.6015 3.72646 13.1624C3.90825 13.4161 4.13686 13.6334 4.39927 13.8023C4.66204 13.9714 5.00263 14.0792 5.57825 14.1367C6.16562 14.1953 6.92298 14.1963 7.99998 14.1963C9.07699 14.1963 9.83434 14.1953 10.4217 14.1367C10.9973 14.0792 11.3379 13.9714 11.6007 13.8023C11.8631 13.6334 12.0917 13.4161 12.2735 13.1624C12.6755 12.6015 12.7378 12.2218 12.8465 10.0463L13.1099 4.77277L14.4782 4.84067ZM5.43011 6.22849H6.7994V11.3909H5.43011V6.22849ZM9.20056 6.22849H10.5699V11.3909H9.20056V6.22849ZM8.53597 0.434431C9.17976 0.434431 9.6522 0.426926 10.0966 0.571258C10.2357 0.616451 10.3717 0.672554 10.502 0.738948C10.9182 0.951107 11.2464 1.29099 11.7015 1.74612L12.4978 2.54136H15.3742V3.91169H0.625732V2.54136H3.50218L4.29845 1.74612C4.75358 1.29099 5.08174 0.951107 5.49801 0.738948C5.62831 0.672554 5.76425 0.616451 5.90334 0.571258C6.34776 0.426926 6.82021 0.434431 7.46399 0.434431H8.53597ZM7.46399 1.80476C6.73208 1.80476 6.51641 1.81187 6.32617 1.87369C6.25545 1.89667 6.18668 1.92533 6.12041 1.95907C5.96398 2.03878 5.82348 2.16253 5.44142 2.54136H10.5585C10.1765 2.16253 10.036 2.03878 9.87955 1.95907C9.81329 1.92533 9.74452 1.89667 9.6738 1.87369C9.48356 1.81187 9.26789 1.80476 8.53597 1.80476H7.46399Z",
393
+ fill: "currentColor"
394
+ }));
395
+ }
396
+ /** IconRefreshOutline16 */
397
+ function IconRefreshOutline16({ size = 16, className }) {
398
+ return (0, react.createElement)("svg", {
399
+ width: size,
400
+ height: size,
401
+ className,
402
+ viewBox: "0 0 16 16",
403
+ fill: "none",
404
+ xmlns: "http://www.w3.org/2000/svg"
405
+ }, (0, react.createElement)("path", {
406
+ d: "M7.92136 0.349152C10.3744 0.349234 12.5564 1.5052 13.9557 3.29894L15.1281 2.12759C15.3303 1.92546 15.6767 2.06943 15.6767 2.35538V5.53923C15.6766 5.71626 15.5329 5.85976 15.3559 5.86002H12.171C11.8854 5.8597 11.7426 5.51465 11.9443 5.31249L12.9641 4.29056C11.8237 2.74305 9.98908 1.74106 7.92136 1.74097C4.46436 1.74097 1.66233 4.543 1.66233 8C1.66233 11.457 4.46436 14.259 7.92136 14.259C11.3782 14.2589 14.1804 11.4569 14.1804 8H15.5722C15.5722 12.2251 12.1465 15.6507 7.92136 15.6508C3.69614 15.6508 0.270508 12.2252 0.270508 8C0.270508 3.77478 3.69614 0.349152 7.92136 0.349152Z",
407
+ fill: "currentColor"
408
+ }));
409
+ }
410
+ /** IconGlobeOutline14 */
411
+ function IconGlobeOutline14({ size = 16, className }) {
412
+ return (0, react.createElement)("svg", {
413
+ width: size,
414
+ height: size,
415
+ className,
416
+ viewBox: "0 0 16 16",
417
+ fill: "none",
418
+ xmlns: "http://www.w3.org/2000/svg"
419
+ }, (0, react.createElement)("path", {
420
+ d: "M7.00018 0.353516C10.6708 0.353535 13.6468 3.32958 13.6469 7.00018C13.6468 10.6708 10.6708 13.6468 7.00018 13.6469C3.32957 13.6468 0.353535 10.6708 0.353516 7.00018C0.353535 3.32957 3.32957 0.353531 7.00018 0.353516ZM5.44643 7.59661C5.49463 8.97506 5.70762 10.191 6.02136 11.0793C6.20141 11.5891 6.40328 11.9585 6.59898 12.1889C6.79501 12.4196 6.93213 12.454 7.00018 12.454C7.06822 12.454 7.20533 12.4197 7.40138 12.1889C7.59708 11.9585 7.79895 11.589 7.979 11.0793C8.29274 10.191 8.50574 8.97506 8.55394 7.59661H5.44643ZM1.57861 7.59661C1.80785 9.70467 3.2386 11.4509 5.1715 12.1388C5.07135 11.9317 4.97972 11.7098 4.89746 11.477C4.53084 10.4391 4.30224 9.0828 4.25357 7.59661H1.57861ZM9.74679 7.59661C9.69813 9.0828 9.46952 10.4391 9.1029 11.477C9.0206 11.7099 8.92818 11.9316 8.82797 12.1388C10.7613 11.4511 12.1925 9.70496 12.4218 7.59661H9.74679ZM5.1706 1.8616C3.23814 2.54963 1.80876 4.29604 1.5795 6.40376H4.25357C4.30224 4.91756 4.53083 3.56129 4.89746 2.5234C4.97968 2.29066 5.07051 2.0686 5.1706 1.8616ZM7.00018 1.54637C6.93213 1.54638 6.79503 1.5807 6.59898 1.81145C6.40332 2.04177 6.20139 2.41058 6.02136 2.92012C5.70754 3.80851 5.49461 5.02499 5.44643 6.40376H8.55394C8.50575 5.025 8.29282 3.80851 7.979 2.92012C7.79898 2.41059 7.59705 2.04177 7.40138 1.81145C7.20531 1.58067 7.06823 1.54637 7.00018 1.54637ZM8.82887 1.8616C8.92902 2.0687 9.02064 2.29053 9.1029 2.5234C9.46953 3.56129 9.69812 4.91756 9.74679 6.40376H12.4209C12.1916 4.29575 10.7618 2.54943 8.82887 1.8616Z",
421
+ fill: "currentColor"
422
+ }));
423
+ }
424
+ /** IconGoalOutline16 */
425
+ function IconGoalOutline16({ size = 16, className }) {
426
+ return (0, react.createElement)("svg", {
427
+ width: size,
428
+ height: size,
429
+ className,
430
+ viewBox: "0 0 16 16",
431
+ fill: "none",
432
+ xmlns: "http://www.w3.org/2000/svg"
433
+ }, (0, react.createElement)("path", {
434
+ d: "M8 0C8.31451 0 8.62464 0.019379 8.92969 0.0546875C8.48228 0.403371 8.0952 0.825758 7.78809 1.30469C4.18586 1.41664 1.2998 4.37061 1.2998 8C1.2998 11.7003 4.29969 14.7002 8 14.7002C11.6297 14.7002 14.5829 11.8136 14.6943 8.21094C15.1734 7.90377 15.5956 7.51688 15.9443 7.06934C15.9797 7.37473 16 7.68512 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0ZM7.0166 3.6084C7.00658 3.73765 7 3.86817 7 4C7 4.31845 7.03098 4.62973 7.08789 4.93164C5.76489 5.32438 4.7998 6.54958 4.7998 8C4.7998 9.76731 6.23269 11.2002 8 11.2002C9.45065 11.2002 10.6749 10.2345 11.0674 8.91113C11.3696 8.96818 11.6812 9 12 9C12.1315 9 12.2617 8.99239 12.3906 8.98242C11.9423 10.995 10.1477 12.5 8 12.5C5.51472 12.5 3.5 10.4853 3.5 8C3.5 5.85255 5.00435 4.05702 7.0166 3.6084Z",
435
+ fill: "currentColor"
436
+ }), (0, react.createElement)("path", {
437
+ d: "M7.5 8.62109L9.12109 7",
438
+ fill: "currentColor"
439
+ }), (0, react.createElement)("path", {
440
+ d: "M9.08245 3.35798L11.8651 0.575334C11.895 0.545384 11.9463 0.56391 11.9502 0.606086L12.2362 3.69859C12.2384 3.72259 12.2574 3.74159 12.2814 3.74378L15.3697 4.02583C15.4119 4.02968 15.4305 4.08101 15.4005 4.11098L12.618 6.89351C12.6086 6.90289 12.5959 6.90816 12.5826 6.90816L9.11781 6.90815C9.09019 6.90816 9.06781 6.88577 9.06781 6.85816L9.06781 3.39333C9.06781 3.38007 9.07308 3.36735 9.08245 3.35798Z",
441
+ fill: "currentColor"
442
+ }));
443
+ }
444
+ /** IconWarningOutline16 */
445
+ function IconWarningOutline16({ size = 16, className }) {
446
+ return (0, react.createElement)("svg", {
447
+ width: size,
448
+ height: size,
449
+ className,
450
+ viewBox: "0 0 16 16",
451
+ fill: "none",
452
+ xmlns: "http://www.w3.org/2000/svg"
453
+ }, (0, react.createElement)("path", {
454
+ d: "M6.3002 3.32843L7.69986 3.32843L7.69986 7.79657H6.3002L6.3002 3.32843Z",
455
+ fill: "currentColor"
456
+ }), (0, react.createElement)("path", {
457
+ d: "M6.3002 9.01935H7.69986V10.6711H6.3002V9.01935Z",
458
+ fill: "currentColor"
459
+ }), (0, react.createElement)("path", {
460
+ d: "M12.6328 6.99976C12.6328 3.88874 10.111 1.36694 7 1.36694C3.88899 1.36695 1.3672 3.88875 1.36719 6.99976C1.36719 10.1108 3.88899 12.6326 7 12.6326C10.111 12.6326 12.6328 10.1108 12.6328 6.99976ZM13.8582 6.99976C13.8582 10.7873 10.7876 13.8579 7 13.8579C3.21244 13.8579 0.141846 10.7873 0.141846 6.99976C0.141857 3.2122 3.21245 0.141612 7 0.141602C10.7876 0.141602 13.8581 3.21219 13.8582 6.99976Z",
461
+ fill: "currentColor"
462
+ }));
463
+ }
464
+ /**
465
+ * Plugin-original companions for learning-domain glyphs the host set does not
466
+ * carry (lock/star/book/crown/bolt/power/pin/flame) — authored here in the
467
+ * same 16px currentColor filled language as the ic_ds set, but they are NOT
468
+ * ic_ds glyphs and must be re-drawn (not re-synced) if the style drifts.
469
+ */
470
+ /** IconLockFill16 */
471
+ function IconLockFill16({ size = 16, className }) {
472
+ return (0, react.createElement)("svg", {
473
+ width: size,
474
+ height: size,
475
+ className,
476
+ viewBox: "0 0 16 16",
477
+ fill: "none",
478
+ xmlns: "http://www.w3.org/2000/svg"
479
+ }, (0, react.createElement)("path", {
480
+ "fill-rule": "evenodd",
481
+ "clip-rule": "evenodd",
482
+ d: "M5 6.5V5a3 3 0 0 1 6 0v1.5h.6c.77 0 1.4.63 1.4 1.4v5.7c0 .77-.63 1.4-1.4 1.4H4.4A1.4 1.4 0 0 1 3 13.6V7.9c0-.77.63-1.4 1.4-1.4H5Zm1.444 0h3.112V5a1.556 1.556 0 0 0-3.112 0v1.5Z",
483
+ fill: "currentColor"
484
+ }));
485
+ }
486
+ /** IconStarFill16 */
487
+ function IconStarFill16({ size = 16, className }) {
488
+ return (0, react.createElement)("svg", {
489
+ width: size,
490
+ height: size,
491
+ className,
492
+ viewBox: "0 0 16 16",
493
+ fill: "none",
494
+ xmlns: "http://www.w3.org/2000/svg"
495
+ }, (0, react.createElement)("path", {
496
+ d: "M8 1.6l1.86 3.77 4.16.6-3.01 2.94.71 4.14L8 11.42l-3.72 1.96.71-4.15-3.01-2.93 4.16-.6L8 1.6Z",
497
+ fill: "currentColor"
498
+ }));
499
+ }
500
+ /** IconBookFill16 */
501
+ function IconBookFill16({ size = 16, className }) {
502
+ return (0, react.createElement)("svg", {
503
+ width: size,
504
+ height: size,
505
+ className,
506
+ viewBox: "0 0 16 16",
507
+ fill: "none",
508
+ xmlns: "http://www.w3.org/2000/svg"
509
+ }, (0, react.createElement)("path", {
510
+ "fill-rule": "evenodd",
511
+ "clip-rule": "evenodd",
512
+ d: "M8 3.1C6.6 2.15 4.65 1.75 2.5 1.75c-.5 0-1 .03-1.5.1v10.7c.5-.07 1-.1 1.5-.1 2.15 0 4.1.4 5.5 1.35 1.4-.95 3.35-1.35 5.5-1.35.5 0 1 .03 1.5.1V1.85c-.5-.07-1-.1-1.5-.1-2.15 0-4.1.4-5.5 1.35ZM7.2 4.9v7.3C5.9 11.55 4.3 11.3 2.6 11.3V3.45c1.7 0 3.3.25 4.6 1.45Zm1.6 0c1.3-1.2 2.9-1.45 4.6-1.45v7.85c-1.7 0-3.3.25-4.6.9V4.9Z",
513
+ fill: "currentColor"
514
+ }));
515
+ }
516
+ /** IconCrownFill16 */
517
+ function IconCrownFill16({ size = 16, className }) {
518
+ return (0, react.createElement)("svg", {
519
+ width: size,
520
+ height: size,
521
+ className,
522
+ viewBox: "0 0 16 16",
523
+ fill: "none",
524
+ xmlns: "http://www.w3.org/2000/svg"
525
+ }, (0, react.createElement)("path", {
526
+ d: "M1.7 11.2 1 4.9l3.6 2.2L8 2.6l3.4 4.5L15 4.9l-.7 6.3H1.7Zm.5 1.4h11.6v1.5H2.1v-1.5Z",
527
+ fill: "currentColor"
528
+ }));
529
+ }
530
+ /** IconBoltFill16 */
531
+ function IconBoltFill16({ size = 16, className }) {
532
+ return (0, react.createElement)("svg", {
533
+ width: size,
534
+ height: size,
535
+ className,
536
+ viewBox: "0 0 16 16",
537
+ fill: "none",
538
+ xmlns: "http://www.w3.org/2000/svg"
539
+ }, (0, react.createElement)("path", {
540
+ d: "M9.6 1 3.4 9.3h3.4L6.3 15l6.3-8.5H9.2L9.6 1Z",
541
+ fill: "currentColor"
542
+ }));
543
+ }
544
+ /** IconPowerFill16 */
545
+ function IconPowerFill16({ size = 16, className }) {
546
+ return (0, react.createElement)("svg", {
547
+ width: size,
548
+ height: size,
549
+ className,
550
+ viewBox: "0 0 16 16",
551
+ fill: "none",
552
+ xmlns: "http://www.w3.org/2000/svg"
553
+ }, (0, react.createElement)("path", {
554
+ d: "M7.2 1h1.6v5.6H7.2V1Z",
555
+ fill: "currentColor"
556
+ }), (0, react.createElement)("path", {
557
+ "fill-rule": "evenodd",
558
+ "clip-rule": "evenodd",
559
+ d: "M4.55 3.15A6 6 0 1 0 11.45 3.15L12.5 4.2a7.5 7.5 0 1 1-9 0l1.05-1.05Z",
560
+ fill: "currentColor"
561
+ }));
562
+ }
563
+ /** IconPinFill16 */
564
+ function IconPinFill16({ size = 16, className }) {
565
+ return (0, react.createElement)("svg", {
566
+ width: size,
567
+ height: size,
568
+ className,
569
+ viewBox: "0 0 16 16",
570
+ fill: "none",
571
+ xmlns: "http://www.w3.org/2000/svg"
572
+ }, (0, react.createElement)("path", {
573
+ "fill-rule": "evenodd",
574
+ "clip-rule": "evenodd",
575
+ d: "M8 1.4a4.8 4.8 0 0 1 4.8 4.8c0 3.4-4.8 8.4-4.8 8.4S3.2 9.6 3.2 6.2A4.8 4.8 0 0 1 8 1.4Zm0 2.7a2.1 2.1 0 1 0 0 4.2 2.1 2.1 0 0 0 0-4.2Z",
576
+ fill: "currentColor"
577
+ }));
578
+ }
579
+ /** IconFlameFill16 */
580
+ function IconFlameFill16({ size = 16, className }) {
581
+ return (0, react.createElement)("svg", {
582
+ width: size,
583
+ height: size,
584
+ className,
585
+ viewBox: "0 0 16 16",
586
+ fill: "none",
587
+ xmlns: "http://www.w3.org/2000/svg"
588
+ }, (0, react.createElement)("path", {
589
+ d: "M8.9 1.2c.4 2.5-.9 3.9-2.1 5.2C5.6 7.7 4.4 9 4.4 10.9a3.9 3.9 0 0 0 7.8 0c0-1.6-.7-2.9-1.5-4-.3.8-.8 1.4-1.6 1.7.5-2.4-.2-5.4-1.2-7.4Z",
590
+ fill: "currentColor"
591
+ }));
592
+ }
312
593
  //#endregion
313
594
  //#region src/client/data.ts
314
595
  /**
@@ -1313,19 +1594,19 @@ window.__ModuleLoader__.load({
1313
1594
  "pane.tutor": "导师",
1314
1595
  "pane.bb": "黑板",
1315
1596
  "viewtab.teach": "讲解",
1316
- "viewtab.cmap": "🕸 概念图",
1317
- "viewtab.cmap.title": "本课概念关系(ELK 布局)",
1597
+ "viewtab.cmap": "概念图",
1598
+ "viewtab.cmap.title": "本课概念之间的关系图",
1318
1599
  "loading": "加载中…",
1319
1600
  "start.title": "一键准备学习:建立学习工作区、开启会话并让导师就位",
1320
1601
  "start.go": "开始学习",
1321
1602
  "start.enter": "进入学习",
1322
1603
  "start.busy": "正在准备学习区…",
1323
1604
  "soul.direct": "直讲",
1324
- "soul.direct.hint": "direct 精讲:先讲清楚,再确认懂没懂",
1605
+ "soul.direct.hint": "精讲:先讲清楚,再确认懂没懂",
1325
1606
  "soul.guide": "引导",
1326
- "soul.guide.hint": "guide 引导:让你自己往前推一步,导师递台阶",
1607
+ "soul.guide.hint": "引导:让你自己往前推一步,导师递台阶",
1327
1608
  "soul.practice": "实战",
1328
- "soul.practice.hint": "practice 实战:在真实世界的乱问题里学",
1609
+ "soul.practice.hint": "实战:在真实世界的乱问题里学",
1329
1610
  "zone.understand": "🧠 理解区 — 知识结构",
1330
1611
  "zone.record": "📝 记录区 — 我的话",
1331
1612
  "zone.practice": "✍️ 练习区 — 答题日志",
@@ -1335,24 +1616,25 @@ window.__ModuleLoader__.load({
1335
1616
  "rail.empty.placeholder": "GitHub 仓库链接,如 microsoft/AI-For-Beginners",
1336
1617
  "rail.empty.button": "导入",
1337
1618
  "rail.empty.demo": "导入示例课程",
1338
- "rail.mastered": "{mastered}/{total} 已掌握",
1619
+ "rail.mastered": "毕业 {mastered}/{total} ",
1339
1620
  "rail.avg": "平均掌握度 {pct}%",
1340
1621
  "rail.avg.none": "尚无掌握度数据",
1341
1622
  "rail.search": "搜索课时…(多关键词空格分隔)",
1342
1623
  "rail.locate.title": "在课程树中定位当前焦点课时(自动展开所在章节)",
1343
- "rail.locate": "📍 回到当前课时",
1344
- "rail.due": "🔁 待复习 {count}",
1624
+ "rail.locate": "回到当前课时",
1625
+ "rail.due": "待复习 {count}",
1345
1626
  "rail.due.over": "超{days}天",
1346
1627
  "rail.due.start": "开始复习",
1347
1628
  "rail.due.tag": "这课时的复习今天到期(SM-2)",
1348
1629
  "rail.delete": "删除本课程",
1349
1630
  "rail.delete.confirm": "确认删除?",
1350
- "rail.delete.title.confirm": "再点一次确认删除(含全部进度与笔记)",
1631
+ "rail.delete.title.confirm": "再点一次确认删除(含全部进度与笔记)。反悔前可先在设置页备份状态文件",
1351
1632
  "rail.section.collapse": "折叠本章节",
1352
1633
  "rail.section.expand": "展开本章节({count} 课时)",
1353
1634
  "rail.section.count": "{count} 课",
1354
1635
  "rail.lesson.opening": "正在打开课时会话…",
1355
- "rail.import.toggle": "+ 导入课程",
1636
+ "rail.lesson.openHint": "(点击将打开本课专属会话)",
1637
+ "rail.import.toggle": "导入课程",
1356
1638
  "rail.import.close": "收起导入",
1357
1639
  "tag.weak": "{count} 个薄弱知识点,测验会优先考察",
1358
1640
  "tag.friction": "{count} 次卡点记录(你说\"不懂\"时导师记下的)",
@@ -1366,12 +1648,11 @@ window.__ModuleLoader__.load({
1366
1648
  "chip.unattributed": "未归因",
1367
1649
  "chip.correct": "✓ 答对 · {concept}",
1368
1650
  "chip.wrong": "✗ 答错 · {concept}",
1369
- "chip.import": "📦 导入课程",
1370
- "chip.concepts": "🧠 提炼知识点",
1371
1651
  "row.thinking": "导师思考中…",
1372
1652
  "row.thinking.title": "导师正在推理,回复马上就来",
1373
1653
  "quiz.title": "点击选项作答,也可以直接打字回答",
1374
1654
  "quiz.answer": "选 {letter}:{text}",
1655
+ "tutor.dormant": "学习模式已关闭 — 点右上「▶ 开始学习」开启导师,按钮恢复可用。",
1375
1656
  "tutor.empty": "对话会出现在这里",
1376
1657
  "tutor.empty.hint": "在下方输入框和导师说话",
1377
1658
  "proposal.text": "导师提议你已掌握「{lesson}」:{rationale}",
@@ -1380,6 +1661,7 @@ window.__ModuleLoader__.load({
1380
1661
  "bb.empty": "黑板还空着",
1381
1662
  "bb.empty.hint": "在左侧课程树选择一课",
1382
1663
  "bb.mastery": "掌握度 {pct}%",
1664
+ "bb.strategy": "本课学法",
1383
1665
  "bb.fallback.cmap.empty": "概念图需要先由导师定义本课概念",
1384
1666
  "bb.fallback.cmap": "概念图渲染不可用(布局引擎加载失败) — 已回退讲解视图",
1385
1667
  "bb.notes": "笔记",
@@ -1408,8 +1690,8 @@ window.__ModuleLoader__.load({
1408
1690
  "settings.stateFile": "状态文件",
1409
1691
  "settings.stateFile.hint": "学习进度存放于本机 JSON,换机可迁移",
1410
1692
  "dock.title": "学习状态:待复习 {due} · 连续 {streak} 天 · Lv{level} — 点上方「学习」页签进入",
1411
- "dock.due": "{count}",
1412
- "dock.streak": "🔥{days}d",
1693
+ "dock.due": "{count}",
1694
+ "dock.streak": "{days}",
1413
1695
  "dock.lv": "Lv{level}",
1414
1696
  "prompt.import": "导入课程:用 study_import_github 抓取 {url}",
1415
1697
  "prompt.lesson": "学习「{title}」:用 study_lesson 打开这一课开始学习。",
@@ -1428,19 +1710,19 @@ window.__ModuleLoader__.load({
1428
1710
  "pane.tutor": "Tutor",
1429
1711
  "pane.bb": "Board",
1430
1712
  "viewtab.teach": "Teach",
1431
- "viewtab.cmap": "🕸 Concepts",
1432
- "viewtab.cmap.title": "The lesson's concept relations (ELK layout)",
1713
+ "viewtab.cmap": "Concepts",
1714
+ "viewtab.cmap.title": "The lesson's concepts relate",
1433
1715
  "loading": "Loading…",
1434
1716
  "start.title": "One click to study: workspace + session + tutor, all set up",
1435
1717
  "start.go": "Start learning",
1436
1718
  "start.enter": "Enter study",
1437
1719
  "start.busy": "Preparing the study area…",
1438
1720
  "soul.direct": "Direct",
1439
- "soul.direct.hint": "direct: explain first, then check understanding",
1721
+ "soul.direct.hint": "Explain first, then check understanding",
1440
1722
  "soul.guide": "Guide",
1441
- "soul.guide.hint": "guide: you push one step forward, the tutor hands you the stairs",
1723
+ "soul.guide.hint": "You push one step forward; the tutor hands you the stairs",
1442
1724
  "soul.practice": "Practice",
1443
- "soul.practice.hint": "practice: learn inside real messy problems",
1725
+ "soul.practice.hint": "Learn inside real, messy problems",
1444
1726
  "zone.understand": "🧠 Understand — knowledge structure",
1445
1727
  "zone.record": "📝 Record — in my own words",
1446
1728
  "zone.practice": "✍️ Practice — answer log",
@@ -1450,24 +1732,25 @@ window.__ModuleLoader__.load({
1450
1732
  "rail.empty.placeholder": "GitHub repo link, e.g. microsoft/AI-For-Beginners",
1451
1733
  "rail.empty.button": "Import",
1452
1734
  "rail.empty.demo": "Import the demo course",
1453
- "rail.mastered": "{mastered}/{total} mastered",
1735
+ "rail.mastered": "{mastered}/{total} lessons graduated",
1454
1736
  "rail.avg": "avg mastery {pct}%",
1455
1737
  "rail.avg.none": "no mastery data yet",
1456
1738
  "rail.search": "Search lessons… (space-separated keywords)",
1457
1739
  "rail.locate.title": "Locate the current focus lesson in the tree (auto-expands its section)",
1458
- "rail.locate": "📍 Back to current lesson",
1459
- "rail.due": "🔁 {count} due",
1740
+ "rail.locate": "Back to current lesson",
1741
+ "rail.due": "{count} due",
1460
1742
  "rail.due.over": "{days}d overdue",
1461
1743
  "rail.due.start": "Start reviewing",
1462
1744
  "rail.due.tag": "this lesson's review is due today (SM-2)",
1463
1745
  "rail.delete": "Delete this course",
1464
1746
  "rail.delete.confirm": "Delete?",
1465
- "rail.delete.title.confirm": "Click again to confirm (all progress and notes included)",
1747
+ "rail.delete.title.confirm": "Click again to confirm (all progress and notes included). Back up via the state file in Settings first",
1466
1748
  "rail.section.collapse": "Collapse this section",
1467
1749
  "rail.section.expand": "Expand this section ({count} lessons)",
1468
1750
  "rail.section.count": "{count} lessons",
1469
1751
  "rail.lesson.opening": "Opening the lesson session…",
1470
- "rail.import.toggle": " Import a course",
1752
+ "rail.lesson.openHint": " (opens this lesson's own session)",
1753
+ "rail.import.toggle": "Import a course",
1471
1754
  "rail.import.close": "Collapse import",
1472
1755
  "tag.weak": "{count} weak concepts — quizzes target them first",
1473
1756
  "tag.friction": "{count} friction marks (logged when you said you were stuck)",
@@ -1481,12 +1764,11 @@ window.__ModuleLoader__.load({
1481
1764
  "chip.unattributed": "unattributed",
1482
1765
  "chip.correct": "✓ correct · {concept}",
1483
1766
  "chip.wrong": "✗ wrong · {concept}",
1484
- "chip.import": "📦 course import",
1485
- "chip.concepts": "🧠 concepts defined",
1486
1767
  "row.thinking": "tutor is thinking…",
1487
1768
  "row.thinking.title": "The tutor is reasoning; the reply is coming",
1488
1769
  "quiz.title": "Click an option to answer, or just type",
1489
1770
  "quiz.answer": "Choose {letter}: {text}",
1771
+ "tutor.dormant": "Study mode is off — click \"▶ Start learning\" above to wake the tutor and re-enable these controls.",
1490
1772
  "tutor.empty": "The conversation will appear here",
1491
1773
  "tutor.empty.hint": "Talk to the tutor in the input below",
1492
1774
  "proposal.text": "The tutor proposes you have mastered \"{lesson}\": {rationale}",
@@ -1495,6 +1777,7 @@ window.__ModuleLoader__.load({
1495
1777
  "bb.empty": "The blackboard is empty",
1496
1778
  "bb.empty.hint": "Pick a lesson in the course tree on the left",
1497
1779
  "bb.mastery": "mastery {pct}%",
1780
+ "bb.strategy": "Approach",
1498
1781
  "bb.fallback.cmap.empty": "The concept map needs the tutor to define this lesson's concepts first",
1499
1782
  "bb.fallback.cmap": "Concept map unavailable (layout engine failed to load) — fell back to the teach view",
1500
1783
  "bb.notes": "Notes",
@@ -1523,8 +1806,8 @@ window.__ModuleLoader__.load({
1523
1806
  "settings.stateFile": "State file",
1524
1807
  "settings.stateFile.hint": "Your progress lives in a local JSON file — copy it to migrate machines",
1525
1808
  "dock.title": "Study status: {due} due · {streak}d streak · Lv{level} — open the Study tab above to continue",
1526
- "dock.due": "{count}",
1527
- "dock.streak": "🔥{days}d",
1809
+ "dock.due": "{count}",
1810
+ "dock.streak": "{days}d",
1528
1811
  "dock.lv": "Lv{level}",
1529
1812
  "prompt.import": "Import a course: fetch {url} with study_import_github",
1530
1813
  "prompt.lesson": "Study \"{title}\": open this lesson with study_lesson and start teaching.",
@@ -1601,10 +1884,11 @@ window.__ModuleLoader__.load({
1601
1884
  * The study tab: one `conversation.view` entry rendering the whole plugin —
1602
1885
  * a simplified LookatStudy inside dsh, three columns in the tab. Left: the
1603
1886
  * course map (selector, tree, due box, one-click demo import). Middle: the
1604
- * tutor conversation — a read-only mini transcript folded from the session
1887
+ * tutor conversation — a read-only transcript folded from the session
1605
1888
  * snapshot (user text, assistant text rendered through the plugin's own
1606
- * markdown pipeline, tool chips) plus the soul pills, starters, pending
1607
- * proposal banner, and a reverse-channel input. Right: the blackboard
1889
+ * markdown pipeline; tool calls stay in the host conversation's toolview
1890
+ * cards a mirrored second record diverged) plus the soul pills, starters,
1891
+ * pending proposal banner, and a reverse-channel input. Right: the blackboard —
1608
1892
  * focus-lesson 讲解 and the Cornell 笔记 zones. Nothing outside this tab
1609
1893
  * touches dsh chrome.
1610
1894
  * @module dsh-plugin-lookatstudy/client/views
@@ -1633,13 +1917,13 @@ window.__ModuleLoader__.load({
1633
1917
  ["record", "zone.record"],
1634
1918
  ["practice", "zone.practice"]
1635
1919
  ];
1636
- /** Status glyph for one lesson row (LookatStudy's map icons). */
1637
- function glyph(kind, status) {
1638
- if (kind === "exam") return "🎯";
1639
- if (status === "mastered") return "👑";
1640
- if (status === "in_progress") return "📖";
1641
- if (status === "available") return "⭐";
1642
- return "🔒";
1920
+ /** Status icon for one lesson row (LookatStudy's map icon semantics, SVG glyphs). */
1921
+ function statusIcon(kind, status) {
1922
+ if (kind === "exam") return (0, react.createElement)(IconGoalOutline16, { size: 14 });
1923
+ if (status === "mastered") return (0, react.createElement)(IconCrownFill16, { size: 14 });
1924
+ if (status === "in_progress") return (0, react.createElement)(IconBookFill16, { size: 14 });
1925
+ if (status === "available") return (0, react.createElement)(IconStarFill16, { size: 14 });
1926
+ return (0, react.createElement)(IconLockFill16, { size: 14 });
1643
1927
  }
1644
1928
  /** LookatStudy's exam gate: an exam node opens only when every sibling study lesson reached mastery ≥50%. */
1645
1929
  function examOpen(lessons) {
@@ -1734,40 +2018,6 @@ window.__ModuleLoader__.load({
1734
2018
  flush();
1735
2019
  return runs.at(-1) ?? [];
1736
2020
  }
1737
- /**
1738
- * Learning-aware label for one settled tool call; null keeps the generic
1739
- * 🔧-name chip. Pure (translator defaults to the active one, per call).
1740
- * @param name - tool call name.
1741
- * @param argsRaw - the call's raw JSON args.
1742
- * @param t - translator.
1743
- */
1744
- function toolChipLabel(name, argsRaw, t = tr) {
1745
- if (name === "study_record_answer") {
1746
- let concept = t("chip.unattributed");
1747
- let correct = false;
1748
- try {
1749
- const args = JSON.parse(argsRaw);
1750
- if (typeof args.concept === "string" && args.concept !== "") concept = args.concept;
1751
- correct = args.correct === true;
1752
- } catch {}
1753
- return correct ? {
1754
- label: t("chip.correct", { concept }),
1755
- tone: "ok"
1756
- } : {
1757
- label: t("chip.wrong", { concept }),
1758
- tone: "bad"
1759
- };
1760
- }
1761
- if (name === "study_import_github" || name === "study_import_markdown" || name === "study_import_folder") return {
1762
- label: t("chip.import"),
1763
- tone: "ok"
1764
- };
1765
- if (name === "study_define_concepts") return {
1766
- label: t("chip.concepts"),
1767
- tone: "ok"
1768
- };
1769
- return null;
1770
- }
1771
2021
  function textOf(blocks) {
1772
2022
  return blocks.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n\n").trim();
1773
2023
  }
@@ -1775,8 +2025,10 @@ window.__ModuleLoader__.load({
1775
2025
  return blocks.filter((b) => b.kind === "text").map((b) => b.text).join("\n\n").trim();
1776
2026
  }
1777
2027
  /**
1778
- * Fold conversation nodes (plus the streaming partial) into render rows;
1779
- * tool activity condenses to one muted chip per settled call.
2028
+ * Fold conversation nodes (plus the streaming partial) into render rows.
2029
+ * Tool results are skipped: the host conversation renders the rich
2030
+ * `tool.call.toolview` cards, and the tutor column stays a pure persona
2031
+ * surface (a transcript mirror there grew a second, diverging record).
1780
2032
  * @param nodes - finalized conversation nodes from the snapshot.
1781
2033
  * @param partial - the in-flight assistant partial, or null.
1782
2034
  * @param t - translator (defaults to the active one, per call).
@@ -1804,14 +2056,6 @@ window.__ModuleLoader__.load({
1804
2056
  });
1805
2057
  break;
1806
2058
  }
1807
- case "tool-result":
1808
- rows.push({
1809
- key: `t${node.seq}`,
1810
- role: "tool",
1811
- text: node.call?.name ?? node.callId,
1812
- call: node.call
1813
- });
1814
- break;
1815
2059
  case "turn-error": rows.push({
1816
2060
  key: `e${node.seq}`,
1817
2061
  role: "error",
@@ -1932,7 +2176,7 @@ window.__ModuleLoader__.load({
1932
2176
  onClick: () => {
1933
2177
  activate(!data.active);
1934
2178
  }
1935
- }, data.active ? tr("active.on") : tr("active.off"))), (0, react.createElement)("div", { className: "lks-body" }, (0, react.createElement)("div", { className: "lks-switch" }, ...[
2179
+ }, data.active ? (0, react.createElement)(IconPowerFill16, { size: 13 }) : (0, react.createElement)(IconPlayOutline16, { size: 13 }), data.active ? tr("active.on") : tr("active.off"))), (0, react.createElement)("div", { className: "lks-body" }, (0, react.createElement)("div", { className: "lks-switch" }, ...[
1936
2180
  {
1937
2181
  id: "rail",
1938
2182
  labelKey: "pane.rail"
@@ -1948,6 +2192,7 @@ window.__ModuleLoader__.load({
1948
2192
  ].map((p) => (0, react.createElement)("button", {
1949
2193
  key: p.id,
1950
2194
  className: `lks-switch-btn${pane === p.id ? " on" : ""}`,
2195
+ "aria-pressed": String(pane === p.id),
1951
2196
  onClick: () => {
1952
2197
  setPane(p.id);
1953
2198
  try {
@@ -2015,6 +2260,21 @@ window.__ModuleLoader__.load({
2015
2260
  const [selectedCourse, setSelectedCourse] = (0, react.useState)("");
2016
2261
  const [query, setQuery] = (0, react.useState)("");
2017
2262
  const [confirmDelete, setConfirmDelete] = (0, react.useState)(false);
2263
+ (0, react.useEffect)(() => {
2264
+ if (!confirmDelete) return;
2265
+ const disarm = () => {
2266
+ setConfirmDelete(false);
2267
+ };
2268
+ const onKey = (e) => {
2269
+ if (e.key === "Escape") disarm();
2270
+ };
2271
+ window.addEventListener("click", disarm);
2272
+ window.addEventListener("keydown", onKey);
2273
+ return () => {
2274
+ window.removeEventListener("click", disarm);
2275
+ window.removeEventListener("keydown", onKey);
2276
+ };
2277
+ }, [confirmDelete]);
2018
2278
  const [showImport, setShowImport] = (0, react.useState)(false);
2019
2279
  /** Per-section open overrides (courseId/sectionTitle → open?), on top of the frontier default. */
2020
2280
  const [secOpen, setSecOpen] = (0, react.useState)({});
@@ -2041,10 +2301,14 @@ window.__ModuleLoader__.load({
2041
2301
  }, ...data.courses.map((c) => (0, react.createElement)("option", {
2042
2302
  key: c.courseId,
2043
2303
  value: c.courseId
2044
- }, c.title))) : (0, react.createElement)("div", { className: "lks-rail-title" }, course.title), (0, react.createElement)("button", {
2304
+ }, c.title))) : (0, react.createElement)("div", {
2305
+ className: "lks-rail-title",
2306
+ title: course.title
2307
+ }, course.title), (0, react.createElement)("button", {
2045
2308
  className: `lks-btn ${confirmDelete ? "primary" : "ghost"}`,
2046
2309
  title: confirmDelete ? tr("rail.delete.title.confirm") : tr("rail.delete"),
2047
- onClick: () => {
2310
+ onClick: (e) => {
2311
+ e.stopPropagation();
2048
2312
  if (!confirmDelete) {
2049
2313
  setConfirmDelete(true);
2050
2314
  return;
@@ -2054,13 +2318,13 @@ window.__ModuleLoader__.load({
2054
2318
  setSelectedCourse("");
2055
2319
  }, reportError);
2056
2320
  }
2057
- }, confirmDelete ? tr("rail.delete.confirm") : "🗑")), (0, react.createElement)("div", { className: "lks-rail-sub" }, tr("rail.mastered", {
2321
+ }, confirmDelete ? tr("rail.delete.confirm") : (0, react.createElement)(IconTrashOutline16, { size: 14 }))), (0, react.createElement)("div", { className: "lks-rail-sub" }, tr("rail.mastered", {
2058
2322
  mastered: course.mastered,
2059
2323
  total: course.total
2060
2324
  })), (0, react.createElement)("div", {
2061
2325
  className: `lks-masterybar${course.avgMasteryPct === 100 ? " gold" : ""}`,
2062
2326
  title: course.avgMasteryPct === null ? tr("rail.avg.none") : tr("rail.avg", { pct: course.avgMasteryPct })
2063
- }, (0, react.createElement)("i", { style: { width: `${course.avgMasteryPct ?? 0}%` } })), (0, react.createElement)("input", {
2327
+ }, (0, react.createElement)("i", { style: { transform: `scaleX(${(course.avgMasteryPct ?? 0) / 100})` } })), (0, react.createElement)("input", {
2064
2328
  className: "lks-search",
2065
2329
  type: "search",
2066
2330
  placeholder: tr("rail.search"),
@@ -2107,7 +2371,7 @@ window.__ModuleLoader__.load({
2107
2371
  });
2108
2372
  }, 60);
2109
2373
  }
2110
- }, tr("rail.locate")) : null, data.dueCount > 0 ? (0, react.createElement)("div", { className: "lks-duebox" }, tr("rail.due", { count: data.dueCount }), ...data.due.map((d) => (0, react.createElement)("div", {
2374
+ }, (0, react.createElement)(IconPinFill16, { size: 13 }), tr("rail.locate")) : null, data.dueCount > 0 ? (0, react.createElement)("div", { className: "lks-duebox" }, (0, react.createElement)(IconRefreshOutline16, { size: 13 }), tr("rail.due", { count: data.dueCount }), ...data.due.map((d) => (0, react.createElement)("div", {
2111
2375
  key: d.lessonId,
2112
2376
  className: "lks-due-item"
2113
2377
  }, (0, react.createElement)("span", null, d.lessonTitle), d.overdueDays > 0 ? (0, react.createElement)("span", { className: "lks-over" }, tr("rail.due.over", { days: d.overdueDays })) : null)), (0, react.createElement)("button", {
@@ -2141,7 +2405,7 @@ window.__ModuleLoader__.load({
2141
2405
  type: "button",
2142
2406
  className: `lks-node${lesson.focus ? " focus" : ""}`,
2143
2407
  "aria-disabled": locked || void 0,
2144
- title: jumping === lesson.id ? tr("rail.lesson.opening") : statusTitle(lesson.kind, locked ? "locked" : lesson.status),
2408
+ title: jumping === lesson.id ? tr("rail.lesson.opening") : `${lesson.title} — ${statusTitle(lesson.kind, locked ? "locked" : lesson.status)}${locked || lesson.kind === "exam" ? "" : tr("rail.lesson.openHint")}`,
2145
2409
  onClick: () => {
2146
2410
  if (locked) return;
2147
2411
  if (lesson.kind === "exam") {
@@ -2151,19 +2415,22 @@ window.__ModuleLoader__.load({
2151
2415
  }
2152
2416
  openLessonThread(lesson);
2153
2417
  }
2154
- }, (0, react.createElement)("span", { className: "lks-g" }, jumping === lesson.id ? "⏳" : glyph(lesson.kind, locked ? "locked" : lesson.status)), (0, react.createElement)("span", { className: "lks-t" }, lesson.title), lesson.due ? (0, react.createElement)("span", {
2418
+ }, (0, react.createElement)("span", { className: "lks-g" }, jumping === lesson.id ? (0, react.createElement)(IconLoadingOutline16, {
2419
+ size: 14,
2420
+ className: "lks-spin"
2421
+ }) : statusIcon(lesson.kind, locked ? "locked" : lesson.status)), (0, react.createElement)("span", { className: "lks-t" }, lesson.title), lesson.due ? (0, react.createElement)("span", {
2155
2422
  className: "lks-tag due",
2156
2423
  title: tr("rail.due.tag")
2157
- }, "🔁") : null, lesson.weakConcepts > 0 ? (0, react.createElement)("span", {
2424
+ }, (0, react.createElement)(IconRefreshOutline16, { size: 11 })) : null, lesson.weakConcepts > 0 ? (0, react.createElement)("span", {
2158
2425
  className: "lks-tag weak",
2159
2426
  title: tr("tag.weak", { count: lesson.weakConcepts })
2160
- }, `⚡${lesson.weakConcepts}`) : null, lesson.frictionCount > 0 ? (0, react.createElement)("span", {
2427
+ }, (0, react.createElement)(IconBoltFill16, { size: 10 }), String(lesson.weakConcepts)) : null, lesson.frictionCount > 0 ? (0, react.createElement)("span", {
2161
2428
  className: "lks-tag fric",
2162
2429
  title: tr("tag.friction", { count: lesson.frictionCount })
2163
- }, `😣${lesson.frictionCount}`) : null, lesson.masteryPct !== null ? (0, react.createElement)("span", {
2430
+ }, (0, react.createElement)(IconWarningOutline16, { size: 10 }), String(lesson.frictionCount)) : null, lesson.masteryPct !== null ? (0, react.createElement)("span", {
2164
2431
  className: "lks-bar",
2165
2432
  title: tr("tag.mastery", { pct: lesson.masteryPct })
2166
- }, (0, react.createElement)("i", { style: { width: `${lesson.masteryPct}%` } })) : null, lesson.masteryPct !== null ? (0, react.createElement)("span", {
2433
+ }, (0, react.createElement)("i", { style: { transform: `scaleX(${lesson.masteryPct / 100})` } })) : null, lesson.masteryPct !== null ? (0, react.createElement)("span", {
2167
2434
  className: "lks-pct",
2168
2435
  title: tr("tag.mastery.short")
2169
2436
  }, `${lesson.masteryPct}%`) : null);
@@ -2174,7 +2441,7 @@ window.__ModuleLoader__.load({
2174
2441
  onClick: () => {
2175
2442
  setShowImport(!showImport);
2176
2443
  }
2177
- }, showImport ? tr("rail.import.close") : tr("rail.import.toggle")), showImport ? (0, react.createElement)(ImportRow, { send }) : null);
2444
+ }, showImport ? tr("rail.import.close") : (0, react.createElement)(IconPlusOutline16, { size: 13 }), tr("rail.import.toggle")), showImport ? (0, react.createElement)(ImportRow, { send }) : null);
2178
2445
  })();
2179
2446
  return (0, react.createElement)("div", { className: "lks-col lks-col-rail" }, (0, react.createElement)("div", { className: "lks-colhead" }, tr("col.rail")), body, (0, react.createElement)(ActionError, { error }));
2180
2447
  }
@@ -2184,17 +2451,6 @@ window.__ModuleLoader__.load({
2184
2451
  key: row.key,
2185
2452
  className: "lks-msg user"
2186
2453
  }, row.text);
2187
- if (row.role === "tool") {
2188
- const chip = row.call === void 0 || row.call === null ? null : toolChipLabel(row.call.name, row.call.argsRaw);
2189
- if (chip !== null) return (0, react.createElement)("div", {
2190
- key: row.key,
2191
- className: `lks-msg tool ${chip.tone}`
2192
- }, chip.label);
2193
- return (0, react.createElement)("div", {
2194
- key: row.key,
2195
- className: "lks-msg tool"
2196
- }, `🔧 ${row.text}`);
2197
- }
2198
2454
  if (row.role === "error") return (0, react.createElement)("div", {
2199
2455
  key: row.key,
2200
2456
  className: "lks-msg error"
@@ -2248,6 +2504,7 @@ window.__ModuleLoader__.load({
2248
2504
  }, [rows.length]);
2249
2505
  const proposal = data?.pendingProposals[0] ?? null;
2250
2506
  const lesson = data?.lesson ?? null;
2507
+ const dormant = data?.active !== true;
2251
2508
  /** Mode switches are the only fallible write left here (host route); surface failures inline. */
2252
2509
  const fire = (action) => {
2253
2510
  action.then(() => {
@@ -2256,10 +2513,10 @@ window.__ModuleLoader__.load({
2256
2513
  setError(err instanceof Error ? err.message : String(err));
2257
2514
  });
2258
2515
  };
2259
- return (0, react.createElement)("div", { className: "lks-col lks-col-tutor" }, (0, react.createElement)("div", { className: "lks-colhead" }, tr("col.tutor")), (0, react.createElement)("div", {
2516
+ return (0, react.createElement)("div", { className: "lks-col lks-col-tutor" }, (0, react.createElement)("div", { className: "lks-colhead" }, tr("col.tutor")), dormant ? (0, react.createElement)("div", { className: "lks-dormant" }, tr("tutor.dormant")) : null, (0, react.createElement)("div", {
2260
2517
  className: "lks-transcript",
2261
2518
  ref: scrollRef
2262
- }, rows.length === 0 ? (0, react.createElement)("div", { className: "lks-empty" }, tr("tutor.empty"), (0, react.createElement)("br"), tr("tutor.empty.hint")) : rows.map((row, i) => chatRowElement(row, i === lastAssistant ? { send } : void 0))), proposal !== null ? (0, react.createElement)("div", { className: "lks-banner" }, (0, react.createElement)("span", null, "🎓"), (0, react.createElement)("span", { className: "lks-why" }, tr("proposal.text", {
2519
+ }, rows.length === 0 && !dormant ? (0, react.createElement)("div", { className: "lks-empty" }, tr("tutor.empty"), (0, react.createElement)("br"), tr("tutor.empty.hint")) : rows.map((row, i) => chatRowElement(row, !dormant && i === lastAssistant ? { send } : void 0))), proposal !== null ? (0, react.createElement)("div", { className: "lks-banner" }, (0, react.createElement)("span", null, "🎓"), (0, react.createElement)("span", { className: "lks-why" }, tr("proposal.text", {
2263
2520
  lesson: proposal.lessonTitle,
2264
2521
  rationale: proposal.rationale
2265
2522
  })), (0, react.createElement)("button", {
@@ -2281,9 +2538,11 @@ window.__ModuleLoader__.load({
2281
2538
  }, ...MODES$1.map((mode) => (0, react.createElement)("button", {
2282
2539
  key: mode.id,
2283
2540
  className: `lks-pill${data?.mode === mode.id ? " on" : ""}`,
2284
- title: tr(mode.hintKey),
2541
+ "aria-pressed": String(data?.mode === mode.id),
2542
+ "aria-disabled": dormant || void 0,
2543
+ title: dormant ? tr("tutor.dormant") : tr(mode.hintKey),
2285
2544
  onClick: () => {
2286
- fire(setMode(mode.id));
2545
+ if (!dormant) fire(setMode(mode.id));
2287
2546
  }
2288
2547
  }, tr(mode.labelKey)))), lesson !== null && lesson.starters.length > 0 ? (0, react.createElement)("div", {
2289
2548
  className: "lks-dock",
@@ -2291,9 +2550,10 @@ window.__ModuleLoader__.load({
2291
2550
  }, ...lesson.starters.map((s) => (0, react.createElement)("button", {
2292
2551
  key: s.label,
2293
2552
  className: "lks-starter",
2294
- title: s.message,
2553
+ "aria-disabled": dormant || void 0,
2554
+ title: dormant ? tr("tutor.dormant") : s.message,
2295
2555
  onClick: () => {
2296
- send(s.message);
2556
+ if (!dormant) send(s.message);
2297
2557
  }
2298
2558
  }, s.label))) : null, (0, react.createElement)(ActionError, { error }));
2299
2559
  }
@@ -2324,21 +2584,23 @@ window.__ModuleLoader__.load({
2324
2584
  lesson?.lessonId,
2325
2585
  lesson?.concepts.length
2326
2586
  ]);
2327
- const body = lesson === null ? (0, react.createElement)("div", { className: "lks-empty" }, tr("bb.empty"), (0, react.createElement)("br"), tr("bb.empty.hint")) : (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "lks-lessonhead" }, (0, react.createElement)("h2", null, lesson.title), (0, react.createElement)("div", { className: "lks-meta" }, `${lesson.courseTitle} · ${lesson.status}` + (lesson.masteryPct === null ? "" : ` · ${tr("bb.mastery", { pct: lesson.masteryPct })}`) + ` · ${lesson.strategy}`), lesson.concepts.length > 0 ? (0, react.createElement)("div", { className: "lks-chips" }, ...lesson.concepts.map((c) => (0, react.createElement)("span", {
2587
+ const body = lesson === null ? (0, react.createElement)("div", { className: "lks-empty" }, tr("bb.empty"), (0, react.createElement)("br"), tr("bb.empty.hint")) : (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "lks-lessonhead" }, (0, react.createElement)("h2", null, lesson.title), (0, react.createElement)("div", { className: "lks-meta" }, `${lesson.courseTitle} · ${statusTitle("study", lesson.status)}` + (lesson.masteryPct === null ? "" : ` · ${tr("bb.mastery", { pct: lesson.masteryPct })}`)), (0, react.createElement)("div", { className: "lks-meta" }, `${tr("bb.strategy")}:${lesson.strategy}`), lesson.concepts.length > 0 ? (0, react.createElement)("div", { className: "lks-chips" }, ...lesson.concepts.map((c) => (0, react.createElement)("span", {
2328
2588
  key: c.title,
2329
2589
  className: `lks-chip${c.weak ? " weak" : ""}`
2330
2590
  }, `${c.title} ${c.masteryPct}%${c.weak ? " ⚡" : ""}`))) : null), (0, react.createElement)("div", { className: "lks-viewtabs" }, (0, react.createElement)("button", {
2331
2591
  className: `lks-viewtab${pane === "teach" ? " on" : ""}`,
2592
+ "aria-pressed": String(pane === "teach"),
2332
2593
  onClick: () => {
2333
2594
  setPane("teach");
2334
2595
  }
2335
2596
  }, tr("viewtab.teach")), (0, react.createElement)("button", {
2336
2597
  className: `lks-viewtab${pane === "cmap" ? " on" : ""}`,
2598
+ "aria-pressed": String(pane === "cmap"),
2337
2599
  title: tr("viewtab.cmap.title"),
2338
2600
  onClick: () => {
2339
2601
  setPane("cmap");
2340
2602
  }
2341
- }, tr("viewtab.cmap"))), pane === "teach" ? (0, react.createElement)("div", {
2603
+ }, (0, react.createElement)(IconGlobeOutline14, { size: 13 }), tr("viewtab.cmap"))), pane === "teach" ? (0, react.createElement)("div", {
2342
2604
  className: "lks-prose",
2343
2605
  ref: proseRef,
2344
2606
  dangerouslySetInnerHTML: { __html: lesson.html }
@@ -2351,10 +2613,13 @@ window.__ModuleLoader__.load({
2351
2613
  }, tr("bb.notes.empty")) : ZONES.filter(([zone]) => lesson.notes.some((n) => n.zone === zone)).map(([zone, labelKey]) => (0, react.createElement)("div", {
2352
2614
  key: zone,
2353
2615
  className: "lks-zone"
2354
- }, (0, react.createElement)("h4", null, tr(labelKey)), ...lesson.notes.filter((n) => n.zone === zone).map((n) => (0, react.createElement)("div", {
2616
+ }, (0, react.createElement)("div", { className: "lks-zone-h" }, tr(labelKey)), ...lesson.notes.filter((n) => n.zone === zone).map((n) => (0, react.createElement)("div", {
2355
2617
  key: n.id,
2356
2618
  className: "lks-note"
2357
- }, (0, react.createElement)("span", { className: "lks-note-src" }, n.source), (0, react.createElement)("div", { className: "lks-note-title" }, n.title), (0, react.createElement)("div", { className: "lks-note-text" }, n.text), n.quote !== null ? (0, react.createElement)("div", { className: "lks-note-q" }, `“${n.quote}”`) : null))))));
2619
+ }, (0, react.createElement)("span", { className: "lks-note-src" }, n.source), (0, react.createElement)("div", { className: "lks-note-title" }, n.title), (0, react.createElement)("div", {
2620
+ className: "lks-note-text",
2621
+ dangerouslySetInnerHTML: { __html: renderMarkdown(n.text) }
2622
+ }), n.quote !== null ? (0, react.createElement)("div", { className: "lks-note-q" }, `“${n.quote}”`) : null))))));
2358
2623
  return (0, react.createElement)("div", { className: "lks-col lks-col-bb" }, (0, react.createElement)("div", { className: "lks-colhead" }, tr("col.bb")), body);
2359
2624
  }
2360
2625
  //#endregion
@@ -2517,20 +2782,23 @@ window.__ModuleLoader__.load({
2517
2782
  * button's limitation), so the tooltip points at the Study tab instead.
2518
2783
  * @module dsh-plugin-lookatstudy/client/dock
2519
2784
  */
2520
- /** Pure projection of the pill's segments (testable without React). */
2785
+ /** Pure projection of the pill's segments (testable without React). `muted` marks zero-value segments — an empty state must not wear a warning color. */
2521
2786
  function dockSegments(progress) {
2522
2787
  return [
2523
2788
  {
2524
2789
  key: "due",
2525
- text: tr("dock.due", { count: progress.dueCount })
2790
+ text: tr("dock.due", { count: progress.dueCount }),
2791
+ muted: progress.dueCount === 0
2526
2792
  },
2527
2793
  {
2528
2794
  key: "streak",
2529
- text: tr("dock.streak", { days: progress.streak })
2795
+ text: tr("dock.streak", { days: progress.streak }),
2796
+ muted: progress.streak === 0
2530
2797
  },
2531
2798
  {
2532
2799
  key: "lv",
2533
- text: tr("dock.lv", { level: progress.level })
2800
+ text: tr("dock.lv", { level: progress.level }),
2801
+ muted: false
2534
2802
  }
2535
2803
  ];
2536
2804
  }
@@ -2552,8 +2820,8 @@ window.__ModuleLoader__.load({
2552
2820
  })
2553
2821
  }, ...segments.map((s) => (0, react.createElement)("span", {
2554
2822
  key: s.key,
2555
- className: `lks-dockseg lks-dock-${s.key}`
2556
- }, s.text)));
2823
+ className: `lks-dockseg lks-dock-${s.key}${s.muted ? " lks-muted" : ""}`
2824
+ }, s.key === "due" ? (0, react.createElement)(IconBoltFill16, { size: 11 }) : s.key === "streak" ? (0, react.createElement)(IconFlameFill16, { size: 11 }) : null, s.text)));
2557
2825
  }
2558
2826
  //#endregion
2559
2827
  //#region src/client/toolviews.tsx