dsh-plugin-lookatstudy 0.11.1 → 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/README.md +0 -1
- package/lib/client.js +454 -255
- package/lib/client.js.map +1 -1
- package/lib/epub-parser-oH96guBW.mjs +306 -0
- package/lib/{html-article-C6nhqJbX.mjs → html-article-Da8ksU0i.mjs} +35 -1
- package/lib/index.mjs +71 -40
- package/lib/{pptx-parser-CD5pR2cj.mjs → pptx-parser-B9IDkiGM.mjs} +50 -2
- package/package.json +1 -1
- package/lib/epub-parser-DvlKap-d.mjs +0 -112
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
|
-
.
|
|
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-
|
|
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-
|
|
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-
|
|
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(--
|
|
87
|
-
.lks-tag.due{background:var(--dsw-alias-state-warn-tertiary);color:var(--
|
|
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-
|
|
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(--
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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(--
|
|
121
|
-
.lks-tag.fric{background:var(--dsw-alias-state-error-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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-
|
|
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(--
|
|
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}
|
|
@@ -195,7 +223,7 @@ window.__ModuleLoader__.load({
|
|
|
195
223
|
.lks-prose ul,.lks-prose ol{padding-left:22px;margin:6px 0}
|
|
196
224
|
.lks-prose img{max-width:100%}
|
|
197
225
|
/* width guards for CDN-rendered artifacts (both teach and diagram panes are
|
|
198
|
-
.lks-prose): mermaid/
|
|
226
|
+
.lks-prose): mermaid/elk SVGs carry fixed width attributes; KaTeX
|
|
199
227
|
display math can exceed the column; both must degrade to fit/scroll
|
|
200
228
|
internally instead of stretching the blackboard. */
|
|
201
229
|
.lks-prose svg{max-width:100%;height:auto}
|
|
@@ -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
|
|
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;
|
|
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-
|
|
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-
|
|
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(--
|
|
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(--
|
|
237
|
-
.lks-tv-chip.bad{background:var(--dsw-alias-state-error-tertiary);color:var(--
|
|
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
|
/**
|
|
@@ -651,7 +932,7 @@ window.__ModuleLoader__.load({
|
|
|
651
932
|
docker: "dockerfile"
|
|
652
933
|
};
|
|
653
934
|
let deps = {};
|
|
654
|
-
const CDN
|
|
935
|
+
const CDN = {
|
|
655
936
|
katexJs: "https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js",
|
|
656
937
|
katexCss: "https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css",
|
|
657
938
|
shiki: "https://esm.sh/shiki@1.29.2",
|
|
@@ -677,11 +958,11 @@ window.__ModuleLoader__.load({
|
|
|
677
958
|
if (!document.querySelector(`link[data-lks-katex]`)) {
|
|
678
959
|
const link = document.createElement("link");
|
|
679
960
|
link.rel = "stylesheet";
|
|
680
|
-
link.href = CDN
|
|
961
|
+
link.href = CDN.katexCss;
|
|
681
962
|
link.setAttribute("data-lks-katex", "1");
|
|
682
963
|
document.head.append(link);
|
|
683
964
|
}
|
|
684
|
-
if (window.katex === void 0) await loadScript(CDN
|
|
965
|
+
if (window.katex === void 0) await loadScript(CDN.katexJs);
|
|
685
966
|
const katex = window.katex;
|
|
686
967
|
if (katex === void 0) throw new Error("katex missing after load");
|
|
687
968
|
return katex;
|
|
@@ -692,7 +973,7 @@ window.__ModuleLoader__.load({
|
|
|
692
973
|
function defaultLoadShiki() {
|
|
693
974
|
shikiPromise ??= import(
|
|
694
975
|
/* @vite-ignore */
|
|
695
|
-
CDN
|
|
976
|
+
CDN.shiki
|
|
696
977
|
).then((mod) => {
|
|
697
978
|
const create = mod.createHighlighter;
|
|
698
979
|
if (create === void 0) throw new Error("shiki createHighlighter missing");
|
|
@@ -723,12 +1004,12 @@ window.__ModuleLoader__.load({
|
|
|
723
1004
|
mermaidPromise ??= (async () => {
|
|
724
1005
|
const mermaid = (await import(
|
|
725
1006
|
/* @vite-ignore */
|
|
726
|
-
CDN
|
|
1007
|
+
CDN.mermaid
|
|
727
1008
|
)).default;
|
|
728
1009
|
try {
|
|
729
1010
|
const elk = await import(
|
|
730
1011
|
/* @vite-ignore */
|
|
731
|
-
CDN
|
|
1012
|
+
CDN.mermaidElk
|
|
732
1013
|
);
|
|
733
1014
|
mermaid.registerLayoutLoaders(elk.default);
|
|
734
1015
|
} catch {}
|
|
@@ -868,36 +1149,6 @@ window.__ModuleLoader__.load({
|
|
|
868
1149
|
} catch {}
|
|
869
1150
|
}
|
|
870
1151
|
//#endregion
|
|
871
|
-
//#region src/vendor/mindmap-markdown.ts
|
|
872
|
-
/**
|
|
873
|
-
* mindmap-markdown —— 讲解 markdown → 思维导图源文本的纯函数预处理(v0.21)。
|
|
874
|
-
*
|
|
875
|
-
* 思维导图是**结构概览**:代码块整段进脑图节点只是噪声(markmap-lib 还会为它
|
|
876
|
-
* 内嵌 hljs 高亮 HTML),图片也无处安放。喂给 markmap 前剥离:
|
|
877
|
-
* - 围栏代码块(``` 或 ~~~,含缩进围栏)→ 单行「代码块」占位(保住结构位置)
|
|
878
|
-
* - 图片  → alt 文字
|
|
879
|
-
* - HTML 注释整段剥除
|
|
880
|
-
* 其余(标题层级/列表/粗体/链接文字)原样保留 —— 层级就是脑图骨架。
|
|
881
|
-
*/
|
|
882
|
-
function mindmapMarkdown(md) {
|
|
883
|
-
const out = [];
|
|
884
|
-
let fence = null;
|
|
885
|
-
let fenceLen = 0;
|
|
886
|
-
for (const line of md.split("\n")) {
|
|
887
|
-
const open = /^(\s*)(`{3,}|~{3,})/.exec(line);
|
|
888
|
-
if (fence == null) {
|
|
889
|
-
if (open) {
|
|
890
|
-
fence = (open[2] ?? "")[0] ?? "`";
|
|
891
|
-
fenceLen = (open[2] ?? "").length;
|
|
892
|
-
out.push(`${open[1] ?? ""}- 代码块`);
|
|
893
|
-
continue;
|
|
894
|
-
}
|
|
895
|
-
out.push(line);
|
|
896
|
-
} else if (new RegExp(`^\\s*\\${fence === "~" ? "~" : "`"}{${fenceLen},}\\s*$`).test(line)) fence = null;
|
|
897
|
-
}
|
|
898
|
-
return out.join("\n").replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/<!--[\s\S]*?-->/g, "");
|
|
899
|
-
}
|
|
900
|
-
//#endregion
|
|
901
1152
|
//#region src/vendor/cmap-elk-layout.ts
|
|
902
1153
|
/** 字符宽度估算:CJK 全宽,ASCII 约 0.58em,其余(BBOP…)同 ASCII。 */
|
|
903
1154
|
function estTextWidth(text, fontSize = 13) {
|
|
@@ -1263,52 +1514,17 @@ window.__ModuleLoader__.load({
|
|
|
1263
1514
|
//#endregion
|
|
1264
1515
|
//#region src/client/diagrams.ts
|
|
1265
1516
|
/**
|
|
1266
|
-
* Blackboard diagram views (SPEC Phase 2): the lesson
|
|
1267
|
-
*
|
|
1268
|
-
*
|
|
1269
|
-
*
|
|
1270
|
-
*
|
|
1517
|
+
* Blackboard diagram views (SPEC Phase 2): the lesson concept map (ELK layered
|
|
1518
|
+
* layout via the vendored cmap-elk-layout + CDN elkjs, draw.io-flavored SVG
|
|
1519
|
+
* skin rendered plugin-side). Degrades honestly: any CDN/ELK failure leaves a
|
|
1520
|
+
* one-line notice instead of a broken pane.
|
|
1521
|
+
*
|
|
1522
|
+
* The lesson mind map view was RETIRED with upstream v0.26.0 (2026-09-01
|
|
1523
|
+
* port): body text that has headings already shows its structure, text without
|
|
1524
|
+
* headings yields a first-line-truncated node graph nobody can read — both
|
|
1525
|
+
* lose — and this concept map already covers the semantic-structure need at
|
|
1526
|
+
* higher quality. Its three CDN imports went with it.
|
|
1271
1527
|
*/
|
|
1272
|
-
const CDN = {
|
|
1273
|
-
markmapLib: "https://esm.sh/markmap-lib@0.18.12",
|
|
1274
|
-
markmapView: "https://esm.sh/markmap-view@0.18.10"
|
|
1275
|
-
};
|
|
1276
|
-
let markmapPromise = null;
|
|
1277
|
-
async function loadMarkmap() {
|
|
1278
|
-
markmapPromise ??= (async () => {
|
|
1279
|
-
const lib = await import(
|
|
1280
|
-
/* @vite-ignore */
|
|
1281
|
-
CDN.markmapLib
|
|
1282
|
-
);
|
|
1283
|
-
const view = await import(
|
|
1284
|
-
/* @vite-ignore */
|
|
1285
|
-
CDN.markmapView
|
|
1286
|
-
);
|
|
1287
|
-
const Transformer = lib.Transformer ?? {};
|
|
1288
|
-
const d3 = await import(
|
|
1289
|
-
/* @vite-ignore */
|
|
1290
|
-
"https://esm.sh/d3@7"
|
|
1291
|
-
);
|
|
1292
|
-
return {
|
|
1293
|
-
transformer: new Transformer(),
|
|
1294
|
-
Markmap: view.Markmap,
|
|
1295
|
-
d3
|
|
1296
|
-
};
|
|
1297
|
-
})();
|
|
1298
|
-
return markmapPromise;
|
|
1299
|
-
}
|
|
1300
|
-
/** Render the lesson markdown as a mind map into an empty container. */
|
|
1301
|
-
async function renderMindmap(container, markdown) {
|
|
1302
|
-
const { transformer, Markmap, d3 } = await loadMarkmap();
|
|
1303
|
-
const { root } = transformer.transform(mindmapMarkdown(markdown));
|
|
1304
|
-
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1305
|
-
svg.setAttribute("style", "width:100%;min-height:420px");
|
|
1306
|
-
container.append(svg);
|
|
1307
|
-
window["markmap"] = { d3 };
|
|
1308
|
-
const mm = Markmap.create(svg, { autoFit: true });
|
|
1309
|
-
mm.setData(root);
|
|
1310
|
-
mm.fit();
|
|
1311
|
-
}
|
|
1312
1528
|
/** Lesson concept map: the lesson node + its concepts (weak ones accented),
|
|
1313
1529
|
* laid out by the vendored ELK pipeline, skinned draw.io-style (plugin-original,
|
|
1314
1530
|
* upstream's skin is coupled to its token pipeline). Mastery shades the nodes. */
|
|
@@ -1378,21 +1594,19 @@ window.__ModuleLoader__.load({
|
|
|
1378
1594
|
"pane.tutor": "导师",
|
|
1379
1595
|
"pane.bb": "黑板",
|
|
1380
1596
|
"viewtab.teach": "讲解",
|
|
1381
|
-
"viewtab.
|
|
1382
|
-
"viewtab.cmap": "
|
|
1383
|
-
"viewtab.mind.title": "本课结构一图流(markmap)",
|
|
1384
|
-
"viewtab.cmap.title": "本课概念关系(ELK 布局)",
|
|
1597
|
+
"viewtab.cmap": "概念图",
|
|
1598
|
+
"viewtab.cmap.title": "本课概念之间的关系图",
|
|
1385
1599
|
"loading": "加载中…",
|
|
1386
1600
|
"start.title": "一键准备学习:建立学习工作区、开启会话并让导师就位",
|
|
1387
1601
|
"start.go": "开始学习",
|
|
1388
1602
|
"start.enter": "进入学习",
|
|
1389
1603
|
"start.busy": "正在准备学习区…",
|
|
1390
1604
|
"soul.direct": "直讲",
|
|
1391
|
-
"soul.direct.hint": "
|
|
1605
|
+
"soul.direct.hint": "精讲:先讲清楚,再确认懂没懂",
|
|
1392
1606
|
"soul.guide": "引导",
|
|
1393
|
-
"soul.guide.hint": "
|
|
1607
|
+
"soul.guide.hint": "引导:让你自己往前推一步,导师递台阶",
|
|
1394
1608
|
"soul.practice": "实战",
|
|
1395
|
-
"soul.practice.hint": "
|
|
1609
|
+
"soul.practice.hint": "实战:在真实世界的乱问题里学",
|
|
1396
1610
|
"zone.understand": "🧠 理解区 — 知识结构",
|
|
1397
1611
|
"zone.record": "📝 记录区 — 我的话",
|
|
1398
1612
|
"zone.practice": "✍️ 练习区 — 答题日志",
|
|
@@ -1402,24 +1616,25 @@ window.__ModuleLoader__.load({
|
|
|
1402
1616
|
"rail.empty.placeholder": "GitHub 仓库链接,如 microsoft/AI-For-Beginners",
|
|
1403
1617
|
"rail.empty.button": "导入",
|
|
1404
1618
|
"rail.empty.demo": "导入示例课程",
|
|
1405
|
-
"rail.mastered": "{mastered}/{total}
|
|
1619
|
+
"rail.mastered": "毕业 {mastered}/{total} 课",
|
|
1406
1620
|
"rail.avg": "平均掌握度 {pct}%",
|
|
1407
1621
|
"rail.avg.none": "尚无掌握度数据",
|
|
1408
1622
|
"rail.search": "搜索课时…(多关键词空格分隔)",
|
|
1409
1623
|
"rail.locate.title": "在课程树中定位当前焦点课时(自动展开所在章节)",
|
|
1410
|
-
"rail.locate": "
|
|
1411
|
-
"rail.due": "
|
|
1624
|
+
"rail.locate": "回到当前课时",
|
|
1625
|
+
"rail.due": "待复习 {count}",
|
|
1412
1626
|
"rail.due.over": "超{days}天",
|
|
1413
1627
|
"rail.due.start": "开始复习",
|
|
1414
1628
|
"rail.due.tag": "这课时的复习今天到期(SM-2)",
|
|
1415
1629
|
"rail.delete": "删除本课程",
|
|
1416
1630
|
"rail.delete.confirm": "确认删除?",
|
|
1417
|
-
"rail.delete.title.confirm": "再点一次确认删除(含全部进度与笔记)",
|
|
1631
|
+
"rail.delete.title.confirm": "再点一次确认删除(含全部进度与笔记)。反悔前可先在设置页备份状态文件",
|
|
1418
1632
|
"rail.section.collapse": "折叠本章节",
|
|
1419
1633
|
"rail.section.expand": "展开本章节({count} 课时)",
|
|
1420
1634
|
"rail.section.count": "{count} 课",
|
|
1421
1635
|
"rail.lesson.opening": "正在打开课时会话…",
|
|
1422
|
-
"rail.
|
|
1636
|
+
"rail.lesson.openHint": "(点击将打开本课专属会话)",
|
|
1637
|
+
"rail.import.toggle": "导入课程",
|
|
1423
1638
|
"rail.import.close": "收起导入",
|
|
1424
1639
|
"tag.weak": "{count} 个薄弱知识点,测验会优先考察",
|
|
1425
1640
|
"tag.friction": "{count} 次卡点记录(你说\"不懂\"时导师记下的)",
|
|
@@ -1433,12 +1648,11 @@ window.__ModuleLoader__.load({
|
|
|
1433
1648
|
"chip.unattributed": "未归因",
|
|
1434
1649
|
"chip.correct": "✓ 答对 · {concept}",
|
|
1435
1650
|
"chip.wrong": "✗ 答错 · {concept}",
|
|
1436
|
-
"chip.import": "📦 导入课程",
|
|
1437
|
-
"chip.concepts": "🧠 提炼知识点",
|
|
1438
1651
|
"row.thinking": "导师思考中…",
|
|
1439
1652
|
"row.thinking.title": "导师正在推理,回复马上就来",
|
|
1440
1653
|
"quiz.title": "点击选项作答,也可以直接打字回答",
|
|
1441
1654
|
"quiz.answer": "选 {letter}:{text}",
|
|
1655
|
+
"tutor.dormant": "学习模式已关闭 — 点右上「▶ 开始学习」开启导师,按钮恢复可用。",
|
|
1442
1656
|
"tutor.empty": "对话会出现在这里",
|
|
1443
1657
|
"tutor.empty.hint": "在下方输入框和导师说话",
|
|
1444
1658
|
"proposal.text": "导师提议你已掌握「{lesson}」:{rationale}",
|
|
@@ -1447,7 +1661,7 @@ window.__ModuleLoader__.load({
|
|
|
1447
1661
|
"bb.empty": "黑板还空着",
|
|
1448
1662
|
"bb.empty.hint": "在左侧课程树选择一课",
|
|
1449
1663
|
"bb.mastery": "掌握度 {pct}%",
|
|
1450
|
-
"bb.
|
|
1664
|
+
"bb.strategy": "本课学法",
|
|
1451
1665
|
"bb.fallback.cmap.empty": "概念图需要先由导师定义本课概念",
|
|
1452
1666
|
"bb.fallback.cmap": "概念图渲染不可用(布局引擎加载失败) — 已回退讲解视图",
|
|
1453
1667
|
"bb.notes": "笔记",
|
|
@@ -1471,11 +1685,13 @@ window.__ModuleLoader__.load({
|
|
|
1471
1685
|
"settings.stats.xp": "XP {xp} · Lv{level}({pct}%)",
|
|
1472
1686
|
"settings.stats.today": "今日 {xp}/{goal}",
|
|
1473
1687
|
"settings.stats.streak": "连续 {days} 天(最长 {best},剩冻结 {freeze})",
|
|
1688
|
+
"settings.about": "关于",
|
|
1689
|
+
"settings.about.hint": "当前运行的构建版本,点击查看该版更新内容。",
|
|
1474
1690
|
"settings.stateFile": "状态文件",
|
|
1475
1691
|
"settings.stateFile.hint": "学习进度存放于本机 JSON,换机可迁移",
|
|
1476
1692
|
"dock.title": "学习状态:待复习 {due} · 连续 {streak} 天 · Lv{level} — 点上方「学习」页签进入",
|
|
1477
|
-
"dock.due": "
|
|
1478
|
-
"dock.streak": "
|
|
1693
|
+
"dock.due": "{count}",
|
|
1694
|
+
"dock.streak": "{days}天",
|
|
1479
1695
|
"dock.lv": "Lv{level}",
|
|
1480
1696
|
"prompt.import": "导入课程:用 study_import_github 抓取 {url}",
|
|
1481
1697
|
"prompt.lesson": "学习「{title}」:用 study_lesson 打开这一课开始学习。",
|
|
@@ -1494,21 +1710,19 @@ window.__ModuleLoader__.load({
|
|
|
1494
1710
|
"pane.tutor": "Tutor",
|
|
1495
1711
|
"pane.bb": "Board",
|
|
1496
1712
|
"viewtab.teach": "Teach",
|
|
1497
|
-
"viewtab.
|
|
1498
|
-
"viewtab.cmap": "
|
|
1499
|
-
"viewtab.mind.title": "The lesson as one markmap graph",
|
|
1500
|
-
"viewtab.cmap.title": "The lesson's concept relations (ELK layout)",
|
|
1713
|
+
"viewtab.cmap": "Concepts",
|
|
1714
|
+
"viewtab.cmap.title": "The lesson's concepts relate",
|
|
1501
1715
|
"loading": "Loading…",
|
|
1502
1716
|
"start.title": "One click to study: workspace + session + tutor, all set up",
|
|
1503
1717
|
"start.go": "Start learning",
|
|
1504
1718
|
"start.enter": "Enter study",
|
|
1505
1719
|
"start.busy": "Preparing the study area…",
|
|
1506
1720
|
"soul.direct": "Direct",
|
|
1507
|
-
"soul.direct.hint": "
|
|
1721
|
+
"soul.direct.hint": "Explain first, then check understanding",
|
|
1508
1722
|
"soul.guide": "Guide",
|
|
1509
|
-
"soul.guide.hint": "
|
|
1723
|
+
"soul.guide.hint": "You push one step forward; the tutor hands you the stairs",
|
|
1510
1724
|
"soul.practice": "Practice",
|
|
1511
|
-
"soul.practice.hint": "
|
|
1725
|
+
"soul.practice.hint": "Learn inside real, messy problems",
|
|
1512
1726
|
"zone.understand": "🧠 Understand — knowledge structure",
|
|
1513
1727
|
"zone.record": "📝 Record — in my own words",
|
|
1514
1728
|
"zone.practice": "✍️ Practice — answer log",
|
|
@@ -1518,24 +1732,25 @@ window.__ModuleLoader__.load({
|
|
|
1518
1732
|
"rail.empty.placeholder": "GitHub repo link, e.g. microsoft/AI-For-Beginners",
|
|
1519
1733
|
"rail.empty.button": "Import",
|
|
1520
1734
|
"rail.empty.demo": "Import the demo course",
|
|
1521
|
-
"rail.mastered": "{mastered}/{total}
|
|
1735
|
+
"rail.mastered": "{mastered}/{total} lessons graduated",
|
|
1522
1736
|
"rail.avg": "avg mastery {pct}%",
|
|
1523
1737
|
"rail.avg.none": "no mastery data yet",
|
|
1524
1738
|
"rail.search": "Search lessons… (space-separated keywords)",
|
|
1525
1739
|
"rail.locate.title": "Locate the current focus lesson in the tree (auto-expands its section)",
|
|
1526
|
-
"rail.locate": "
|
|
1527
|
-
"rail.due": "
|
|
1740
|
+
"rail.locate": "Back to current lesson",
|
|
1741
|
+
"rail.due": "{count} due",
|
|
1528
1742
|
"rail.due.over": "{days}d overdue",
|
|
1529
1743
|
"rail.due.start": "Start reviewing",
|
|
1530
1744
|
"rail.due.tag": "this lesson's review is due today (SM-2)",
|
|
1531
1745
|
"rail.delete": "Delete this course",
|
|
1532
1746
|
"rail.delete.confirm": "Delete?",
|
|
1533
|
-
"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",
|
|
1534
1748
|
"rail.section.collapse": "Collapse this section",
|
|
1535
1749
|
"rail.section.expand": "Expand this section ({count} lessons)",
|
|
1536
1750
|
"rail.section.count": "{count} lessons",
|
|
1537
1751
|
"rail.lesson.opening": "Opening the lesson session…",
|
|
1538
|
-
"rail.
|
|
1752
|
+
"rail.lesson.openHint": " (opens this lesson's own session)",
|
|
1753
|
+
"rail.import.toggle": "Import a course",
|
|
1539
1754
|
"rail.import.close": "Collapse import",
|
|
1540
1755
|
"tag.weak": "{count} weak concepts — quizzes target them first",
|
|
1541
1756
|
"tag.friction": "{count} friction marks (logged when you said you were stuck)",
|
|
@@ -1549,12 +1764,11 @@ window.__ModuleLoader__.load({
|
|
|
1549
1764
|
"chip.unattributed": "unattributed",
|
|
1550
1765
|
"chip.correct": "✓ correct · {concept}",
|
|
1551
1766
|
"chip.wrong": "✗ wrong · {concept}",
|
|
1552
|
-
"chip.import": "📦 course import",
|
|
1553
|
-
"chip.concepts": "🧠 concepts defined",
|
|
1554
1767
|
"row.thinking": "tutor is thinking…",
|
|
1555
1768
|
"row.thinking.title": "The tutor is reasoning; the reply is coming",
|
|
1556
1769
|
"quiz.title": "Click an option to answer, or just type",
|
|
1557
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.",
|
|
1558
1772
|
"tutor.empty": "The conversation will appear here",
|
|
1559
1773
|
"tutor.empty.hint": "Talk to the tutor in the input below",
|
|
1560
1774
|
"proposal.text": "The tutor proposes you have mastered \"{lesson}\": {rationale}",
|
|
@@ -1563,7 +1777,7 @@ window.__ModuleLoader__.load({
|
|
|
1563
1777
|
"bb.empty": "The blackboard is empty",
|
|
1564
1778
|
"bb.empty.hint": "Pick a lesson in the course tree on the left",
|
|
1565
1779
|
"bb.mastery": "mastery {pct}%",
|
|
1566
|
-
"bb.
|
|
1780
|
+
"bb.strategy": "Approach",
|
|
1567
1781
|
"bb.fallback.cmap.empty": "The concept map needs the tutor to define this lesson's concepts first",
|
|
1568
1782
|
"bb.fallback.cmap": "Concept map unavailable (layout engine failed to load) — fell back to the teach view",
|
|
1569
1783
|
"bb.notes": "Notes",
|
|
@@ -1587,11 +1801,13 @@ window.__ModuleLoader__.load({
|
|
|
1587
1801
|
"settings.stats.xp": "XP {xp} · Lv{level} ({pct}%)",
|
|
1588
1802
|
"settings.stats.today": "Today {xp}/{goal}",
|
|
1589
1803
|
"settings.stats.streak": "Streak {days}d (best {best}, {freeze} freeze left)",
|
|
1804
|
+
"settings.about": "About",
|
|
1805
|
+
"settings.about.hint": "The version of the running build — click to see its release notes.",
|
|
1590
1806
|
"settings.stateFile": "State file",
|
|
1591
1807
|
"settings.stateFile.hint": "Your progress lives in a local JSON file — copy it to migrate machines",
|
|
1592
1808
|
"dock.title": "Study status: {due} due · {streak}d streak · Lv{level} — open the Study tab above to continue",
|
|
1593
|
-
"dock.due": "
|
|
1594
|
-
"dock.streak": "
|
|
1809
|
+
"dock.due": "{count}",
|
|
1810
|
+
"dock.streak": "{days}d",
|
|
1595
1811
|
"dock.lv": "Lv{level}",
|
|
1596
1812
|
"prompt.import": "Import a course: fetch {url} with study_import_github",
|
|
1597
1813
|
"prompt.lesson": "Study \"{title}\": open this lesson with study_lesson and start teaching.",
|
|
@@ -1668,10 +1884,11 @@ window.__ModuleLoader__.load({
|
|
|
1668
1884
|
* The study tab: one `conversation.view` entry rendering the whole plugin —
|
|
1669
1885
|
* a simplified LookatStudy inside dsh, three columns in the tab. Left: the
|
|
1670
1886
|
* course map (selector, tree, due box, one-click demo import). Middle: the
|
|
1671
|
-
* tutor conversation — a read-only
|
|
1887
|
+
* tutor conversation — a read-only transcript folded from the session
|
|
1672
1888
|
* snapshot (user text, assistant text rendered through the plugin's own
|
|
1673
|
-
* markdown pipeline
|
|
1674
|
-
*
|
|
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 —
|
|
1675
1892
|
* focus-lesson 讲解 and the Cornell 笔记 zones. Nothing outside this tab
|
|
1676
1893
|
* touches dsh chrome.
|
|
1677
1894
|
* @module dsh-plugin-lookatstudy/client/views
|
|
@@ -1700,13 +1917,13 @@ window.__ModuleLoader__.load({
|
|
|
1700
1917
|
["record", "zone.record"],
|
|
1701
1918
|
["practice", "zone.practice"]
|
|
1702
1919
|
];
|
|
1703
|
-
/** Status
|
|
1704
|
-
function
|
|
1705
|
-
if (kind === "exam") return
|
|
1706
|
-
if (status === "mastered") return
|
|
1707
|
-
if (status === "in_progress") return
|
|
1708
|
-
if (status === "available") return
|
|
1709
|
-
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 });
|
|
1710
1927
|
}
|
|
1711
1928
|
/** LookatStudy's exam gate: an exam node opens only when every sibling study lesson reached mastery ≥50%. */
|
|
1712
1929
|
function examOpen(lessons) {
|
|
@@ -1801,40 +2018,6 @@ window.__ModuleLoader__.load({
|
|
|
1801
2018
|
flush();
|
|
1802
2019
|
return runs.at(-1) ?? [];
|
|
1803
2020
|
}
|
|
1804
|
-
/**
|
|
1805
|
-
* Learning-aware label for one settled tool call; null keeps the generic
|
|
1806
|
-
* 🔧-name chip. Pure (translator defaults to the active one, per call).
|
|
1807
|
-
* @param name - tool call name.
|
|
1808
|
-
* @param argsRaw - the call's raw JSON args.
|
|
1809
|
-
* @param t - translator.
|
|
1810
|
-
*/
|
|
1811
|
-
function toolChipLabel(name, argsRaw, t = tr) {
|
|
1812
|
-
if (name === "study_record_answer") {
|
|
1813
|
-
let concept = t("chip.unattributed");
|
|
1814
|
-
let correct = false;
|
|
1815
|
-
try {
|
|
1816
|
-
const args = JSON.parse(argsRaw);
|
|
1817
|
-
if (typeof args.concept === "string" && args.concept !== "") concept = args.concept;
|
|
1818
|
-
correct = args.correct === true;
|
|
1819
|
-
} catch {}
|
|
1820
|
-
return correct ? {
|
|
1821
|
-
label: t("chip.correct", { concept }),
|
|
1822
|
-
tone: "ok"
|
|
1823
|
-
} : {
|
|
1824
|
-
label: t("chip.wrong", { concept }),
|
|
1825
|
-
tone: "bad"
|
|
1826
|
-
};
|
|
1827
|
-
}
|
|
1828
|
-
if (name === "study_import_github" || name === "study_import_markdown" || name === "study_import_folder") return {
|
|
1829
|
-
label: t("chip.import"),
|
|
1830
|
-
tone: "ok"
|
|
1831
|
-
};
|
|
1832
|
-
if (name === "study_define_concepts") return {
|
|
1833
|
-
label: t("chip.concepts"),
|
|
1834
|
-
tone: "ok"
|
|
1835
|
-
};
|
|
1836
|
-
return null;
|
|
1837
|
-
}
|
|
1838
2021
|
function textOf(blocks) {
|
|
1839
2022
|
return blocks.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n\n").trim();
|
|
1840
2023
|
}
|
|
@@ -1842,8 +2025,10 @@ window.__ModuleLoader__.load({
|
|
|
1842
2025
|
return blocks.filter((b) => b.kind === "text").map((b) => b.text).join("\n\n").trim();
|
|
1843
2026
|
}
|
|
1844
2027
|
/**
|
|
1845
|
-
* Fold conversation nodes (plus the streaming partial) into render rows
|
|
1846
|
-
*
|
|
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).
|
|
1847
2032
|
* @param nodes - finalized conversation nodes from the snapshot.
|
|
1848
2033
|
* @param partial - the in-flight assistant partial, or null.
|
|
1849
2034
|
* @param t - translator (defaults to the active one, per call).
|
|
@@ -1871,14 +2056,6 @@ window.__ModuleLoader__.load({
|
|
|
1871
2056
|
});
|
|
1872
2057
|
break;
|
|
1873
2058
|
}
|
|
1874
|
-
case "tool-result":
|
|
1875
|
-
rows.push({
|
|
1876
|
-
key: `t${node.seq}`,
|
|
1877
|
-
role: "tool",
|
|
1878
|
-
text: node.call?.name ?? node.callId,
|
|
1879
|
-
call: node.call
|
|
1880
|
-
});
|
|
1881
|
-
break;
|
|
1882
2059
|
case "turn-error": rows.push({
|
|
1883
2060
|
key: `e${node.seq}`,
|
|
1884
2061
|
role: "error",
|
|
@@ -1999,7 +2176,7 @@ window.__ModuleLoader__.load({
|
|
|
1999
2176
|
onClick: () => {
|
|
2000
2177
|
activate(!data.active);
|
|
2001
2178
|
}
|
|
2002
|
-
}, 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" }, ...[
|
|
2003
2180
|
{
|
|
2004
2181
|
id: "rail",
|
|
2005
2182
|
labelKey: "pane.rail"
|
|
@@ -2015,6 +2192,7 @@ window.__ModuleLoader__.load({
|
|
|
2015
2192
|
].map((p) => (0, react.createElement)("button", {
|
|
2016
2193
|
key: p.id,
|
|
2017
2194
|
className: `lks-switch-btn${pane === p.id ? " on" : ""}`,
|
|
2195
|
+
"aria-pressed": String(pane === p.id),
|
|
2018
2196
|
onClick: () => {
|
|
2019
2197
|
setPane(p.id);
|
|
2020
2198
|
try {
|
|
@@ -2082,6 +2260,21 @@ window.__ModuleLoader__.load({
|
|
|
2082
2260
|
const [selectedCourse, setSelectedCourse] = (0, react.useState)("");
|
|
2083
2261
|
const [query, setQuery] = (0, react.useState)("");
|
|
2084
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]);
|
|
2085
2278
|
const [showImport, setShowImport] = (0, react.useState)(false);
|
|
2086
2279
|
/** Per-section open overrides (courseId/sectionTitle → open?), on top of the frontier default. */
|
|
2087
2280
|
const [secOpen, setSecOpen] = (0, react.useState)({});
|
|
@@ -2108,10 +2301,14 @@ window.__ModuleLoader__.load({
|
|
|
2108
2301
|
}, ...data.courses.map((c) => (0, react.createElement)("option", {
|
|
2109
2302
|
key: c.courseId,
|
|
2110
2303
|
value: c.courseId
|
|
2111
|
-
}, c.title))) : (0, react.createElement)("div", {
|
|
2304
|
+
}, c.title))) : (0, react.createElement)("div", {
|
|
2305
|
+
className: "lks-rail-title",
|
|
2306
|
+
title: course.title
|
|
2307
|
+
}, course.title), (0, react.createElement)("button", {
|
|
2112
2308
|
className: `lks-btn ${confirmDelete ? "primary" : "ghost"}`,
|
|
2113
2309
|
title: confirmDelete ? tr("rail.delete.title.confirm") : tr("rail.delete"),
|
|
2114
|
-
onClick: () => {
|
|
2310
|
+
onClick: (e) => {
|
|
2311
|
+
e.stopPropagation();
|
|
2115
2312
|
if (!confirmDelete) {
|
|
2116
2313
|
setConfirmDelete(true);
|
|
2117
2314
|
return;
|
|
@@ -2121,13 +2318,13 @@ window.__ModuleLoader__.load({
|
|
|
2121
2318
|
setSelectedCourse("");
|
|
2122
2319
|
}, reportError);
|
|
2123
2320
|
}
|
|
2124
|
-
}, confirmDelete ? tr("rail.delete.confirm") :
|
|
2321
|
+
}, confirmDelete ? tr("rail.delete.confirm") : (0, react.createElement)(IconTrashOutline16, { size: 14 }))), (0, react.createElement)("div", { className: "lks-rail-sub" }, tr("rail.mastered", {
|
|
2125
2322
|
mastered: course.mastered,
|
|
2126
2323
|
total: course.total
|
|
2127
2324
|
})), (0, react.createElement)("div", {
|
|
2128
2325
|
className: `lks-masterybar${course.avgMasteryPct === 100 ? " gold" : ""}`,
|
|
2129
2326
|
title: course.avgMasteryPct === null ? tr("rail.avg.none") : tr("rail.avg", { pct: course.avgMasteryPct })
|
|
2130
|
-
}, (0, react.createElement)("i", { style: {
|
|
2327
|
+
}, (0, react.createElement)("i", { style: { transform: `scaleX(${(course.avgMasteryPct ?? 0) / 100})` } })), (0, react.createElement)("input", {
|
|
2131
2328
|
className: "lks-search",
|
|
2132
2329
|
type: "search",
|
|
2133
2330
|
placeholder: tr("rail.search"),
|
|
@@ -2174,7 +2371,7 @@ window.__ModuleLoader__.load({
|
|
|
2174
2371
|
});
|
|
2175
2372
|
}, 60);
|
|
2176
2373
|
}
|
|
2177
|
-
}, 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", {
|
|
2178
2375
|
key: d.lessonId,
|
|
2179
2376
|
className: "lks-due-item"
|
|
2180
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", {
|
|
@@ -2208,7 +2405,7 @@ window.__ModuleLoader__.load({
|
|
|
2208
2405
|
type: "button",
|
|
2209
2406
|
className: `lks-node${lesson.focus ? " focus" : ""}`,
|
|
2210
2407
|
"aria-disabled": locked || void 0,
|
|
2211
|
-
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")}`,
|
|
2212
2409
|
onClick: () => {
|
|
2213
2410
|
if (locked) return;
|
|
2214
2411
|
if (lesson.kind === "exam") {
|
|
@@ -2218,19 +2415,22 @@ window.__ModuleLoader__.load({
|
|
|
2218
2415
|
}
|
|
2219
2416
|
openLessonThread(lesson);
|
|
2220
2417
|
}
|
|
2221
|
-
}, (0, react.createElement)("span", { className: "lks-g" }, jumping === lesson.id ?
|
|
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", {
|
|
2222
2422
|
className: "lks-tag due",
|
|
2223
2423
|
title: tr("rail.due.tag")
|
|
2224
|
-
},
|
|
2424
|
+
}, (0, react.createElement)(IconRefreshOutline16, { size: 11 })) : null, lesson.weakConcepts > 0 ? (0, react.createElement)("span", {
|
|
2225
2425
|
className: "lks-tag weak",
|
|
2226
2426
|
title: tr("tag.weak", { count: lesson.weakConcepts })
|
|
2227
|
-
},
|
|
2427
|
+
}, (0, react.createElement)(IconBoltFill16, { size: 10 }), String(lesson.weakConcepts)) : null, lesson.frictionCount > 0 ? (0, react.createElement)("span", {
|
|
2228
2428
|
className: "lks-tag fric",
|
|
2229
2429
|
title: tr("tag.friction", { count: lesson.frictionCount })
|
|
2230
|
-
},
|
|
2430
|
+
}, (0, react.createElement)(IconWarningOutline16, { size: 10 }), String(lesson.frictionCount)) : null, lesson.masteryPct !== null ? (0, react.createElement)("span", {
|
|
2231
2431
|
className: "lks-bar",
|
|
2232
2432
|
title: tr("tag.mastery", { pct: lesson.masteryPct })
|
|
2233
|
-
}, (0, react.createElement)("i", { style: {
|
|
2433
|
+
}, (0, react.createElement)("i", { style: { transform: `scaleX(${lesson.masteryPct / 100})` } })) : null, lesson.masteryPct !== null ? (0, react.createElement)("span", {
|
|
2234
2434
|
className: "lks-pct",
|
|
2235
2435
|
title: tr("tag.mastery.short")
|
|
2236
2436
|
}, `${lesson.masteryPct}%`) : null);
|
|
@@ -2241,7 +2441,7 @@ window.__ModuleLoader__.load({
|
|
|
2241
2441
|
onClick: () => {
|
|
2242
2442
|
setShowImport(!showImport);
|
|
2243
2443
|
}
|
|
2244
|
-
}, 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);
|
|
2245
2445
|
})();
|
|
2246
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 }));
|
|
2247
2447
|
}
|
|
@@ -2251,17 +2451,6 @@ window.__ModuleLoader__.load({
|
|
|
2251
2451
|
key: row.key,
|
|
2252
2452
|
className: "lks-msg user"
|
|
2253
2453
|
}, row.text);
|
|
2254
|
-
if (row.role === "tool") {
|
|
2255
|
-
const chip = row.call === void 0 || row.call === null ? null : toolChipLabel(row.call.name, row.call.argsRaw);
|
|
2256
|
-
if (chip !== null) return (0, react.createElement)("div", {
|
|
2257
|
-
key: row.key,
|
|
2258
|
-
className: `lks-msg tool ${chip.tone}`
|
|
2259
|
-
}, chip.label);
|
|
2260
|
-
return (0, react.createElement)("div", {
|
|
2261
|
-
key: row.key,
|
|
2262
|
-
className: "lks-msg tool"
|
|
2263
|
-
}, `🔧 ${row.text}`);
|
|
2264
|
-
}
|
|
2265
2454
|
if (row.role === "error") return (0, react.createElement)("div", {
|
|
2266
2455
|
key: row.key,
|
|
2267
2456
|
className: "lks-msg error"
|
|
@@ -2315,6 +2504,7 @@ window.__ModuleLoader__.load({
|
|
|
2315
2504
|
}, [rows.length]);
|
|
2316
2505
|
const proposal = data?.pendingProposals[0] ?? null;
|
|
2317
2506
|
const lesson = data?.lesson ?? null;
|
|
2507
|
+
const dormant = data?.active !== true;
|
|
2318
2508
|
/** Mode switches are the only fallible write left here (host route); surface failures inline. */
|
|
2319
2509
|
const fire = (action) => {
|
|
2320
2510
|
action.then(() => {
|
|
@@ -2323,10 +2513,10 @@ window.__ModuleLoader__.load({
|
|
|
2323
2513
|
setError(err instanceof Error ? err.message : String(err));
|
|
2324
2514
|
});
|
|
2325
2515
|
};
|
|
2326
|
-
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", {
|
|
2327
2517
|
className: "lks-transcript",
|
|
2328
2518
|
ref: scrollRef
|
|
2329
|
-
}, 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", {
|
|
2330
2520
|
lesson: proposal.lessonTitle,
|
|
2331
2521
|
rationale: proposal.rationale
|
|
2332
2522
|
})), (0, react.createElement)("button", {
|
|
@@ -2348,9 +2538,11 @@ window.__ModuleLoader__.load({
|
|
|
2348
2538
|
}, ...MODES$1.map((mode) => (0, react.createElement)("button", {
|
|
2349
2539
|
key: mode.id,
|
|
2350
2540
|
className: `lks-pill${data?.mode === mode.id ? " on" : ""}`,
|
|
2351
|
-
|
|
2541
|
+
"aria-pressed": String(data?.mode === mode.id),
|
|
2542
|
+
"aria-disabled": dormant || void 0,
|
|
2543
|
+
title: dormant ? tr("tutor.dormant") : tr(mode.hintKey),
|
|
2352
2544
|
onClick: () => {
|
|
2353
|
-
fire(setMode(mode.id));
|
|
2545
|
+
if (!dormant) fire(setMode(mode.id));
|
|
2354
2546
|
}
|
|
2355
2547
|
}, tr(mode.labelKey)))), lesson !== null && lesson.starters.length > 0 ? (0, react.createElement)("div", {
|
|
2356
2548
|
className: "lks-dock",
|
|
@@ -2358,9 +2550,10 @@ window.__ModuleLoader__.load({
|
|
|
2358
2550
|
}, ...lesson.starters.map((s) => (0, react.createElement)("button", {
|
|
2359
2551
|
key: s.label,
|
|
2360
2552
|
className: "lks-starter",
|
|
2361
|
-
|
|
2553
|
+
"aria-disabled": dormant || void 0,
|
|
2554
|
+
title: dormant ? tr("tutor.dormant") : s.message,
|
|
2362
2555
|
onClick: () => {
|
|
2363
|
-
send(s.message);
|
|
2556
|
+
if (!dormant) send(s.message);
|
|
2364
2557
|
}
|
|
2365
2558
|
}, s.label))) : null, (0, react.createElement)(ActionError, { error }));
|
|
2366
2559
|
}
|
|
@@ -2376,42 +2569,38 @@ window.__ModuleLoader__.load({
|
|
|
2376
2569
|
enhanceRendered(el).catch(() => {});
|
|
2377
2570
|
}, [pane, lesson?.html]);
|
|
2378
2571
|
(0, react.useEffect)(() => {
|
|
2379
|
-
if (pane !== "
|
|
2572
|
+
if (pane !== "cmap" || diagRef.current === null || lesson === null) return;
|
|
2380
2573
|
const el = diagRef.current;
|
|
2381
2574
|
el.textContent = "";
|
|
2382
|
-
(
|
|
2575
|
+
(lesson.concepts.length === 0 ? Promise.reject(/* @__PURE__ */ new Error("no concepts")) : renderLessonConceptMap(el, lesson.title, lesson.concepts.map((c) => ({
|
|
2383
2576
|
title: c.title,
|
|
2384
2577
|
masteryPct: c.masteryPct
|
|
2385
2578
|
})))).catch((err) => {
|
|
2386
2579
|
console.error("lks diagram pane failed:", pane, err);
|
|
2387
|
-
el.textContent =
|
|
2580
|
+
el.textContent = lesson.concepts.length === 0 ? tr("bb.fallback.cmap.empty") : tr("bb.fallback.cmap");
|
|
2388
2581
|
});
|
|
2389
2582
|
}, [
|
|
2390
2583
|
pane,
|
|
2391
2584
|
lesson?.lessonId,
|
|
2392
2585
|
lesson?.concepts.length
|
|
2393
2586
|
]);
|
|
2394
|
-
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 })}`)
|
|
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", {
|
|
2395
2588
|
key: c.title,
|
|
2396
2589
|
className: `lks-chip${c.weak ? " weak" : ""}`
|
|
2397
2590
|
}, `${c.title} ${c.masteryPct}%${c.weak ? " ⚡" : ""}`))) : null), (0, react.createElement)("div", { className: "lks-viewtabs" }, (0, react.createElement)("button", {
|
|
2398
2591
|
className: `lks-viewtab${pane === "teach" ? " on" : ""}`,
|
|
2592
|
+
"aria-pressed": String(pane === "teach"),
|
|
2399
2593
|
onClick: () => {
|
|
2400
2594
|
setPane("teach");
|
|
2401
2595
|
}
|
|
2402
2596
|
}, tr("viewtab.teach")), (0, react.createElement)("button", {
|
|
2403
|
-
className: `lks-viewtab${pane === "mind" ? " on" : ""}`,
|
|
2404
|
-
title: tr("viewtab.mind.title"),
|
|
2405
|
-
onClick: () => {
|
|
2406
|
-
setPane("mind");
|
|
2407
|
-
}
|
|
2408
|
-
}, tr("viewtab.mind")), (0, react.createElement)("button", {
|
|
2409
2597
|
className: `lks-viewtab${pane === "cmap" ? " on" : ""}`,
|
|
2598
|
+
"aria-pressed": String(pane === "cmap"),
|
|
2410
2599
|
title: tr("viewtab.cmap.title"),
|
|
2411
2600
|
onClick: () => {
|
|
2412
2601
|
setPane("cmap");
|
|
2413
2602
|
}
|
|
2414
|
-
}, 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", {
|
|
2415
2604
|
className: "lks-prose",
|
|
2416
2605
|
ref: proseRef,
|
|
2417
2606
|
dangerouslySetInnerHTML: { __html: lesson.html }
|
|
@@ -2424,10 +2613,13 @@ window.__ModuleLoader__.load({
|
|
|
2424
2613
|
}, tr("bb.notes.empty")) : ZONES.filter(([zone]) => lesson.notes.some((n) => n.zone === zone)).map(([zone, labelKey]) => (0, react.createElement)("div", {
|
|
2425
2614
|
key: zone,
|
|
2426
2615
|
className: "lks-zone"
|
|
2427
|
-
}, (0, react.createElement)("
|
|
2616
|
+
}, (0, react.createElement)("div", { className: "lks-zone-h" }, tr(labelKey)), ...lesson.notes.filter((n) => n.zone === zone).map((n) => (0, react.createElement)("div", {
|
|
2428
2617
|
key: n.id,
|
|
2429
2618
|
className: "lks-note"
|
|
2430
|
-
}, (0, react.createElement)("span", { className: "lks-note-src" }, n.source), (0, react.createElement)("div", { className: "lks-note-title" }, n.title), (0, react.createElement)("div", {
|
|
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))))));
|
|
2431
2623
|
return (0, react.createElement)("div", { className: "lks-col lks-col-bb" }, (0, react.createElement)("div", { className: "lks-colhead" }, tr("col.bb")), body);
|
|
2432
2624
|
}
|
|
2433
2625
|
//#endregion
|
|
@@ -2572,7 +2764,11 @@ window.__ModuleLoader__.load({
|
|
|
2572
2764
|
days: progress.streak,
|
|
2573
2765
|
best: progress.longestStreak,
|
|
2574
2766
|
freeze: progress.freezeCount
|
|
2575
|
-
})))), (0, react.createElement)("section", { className: "lks-set-row" }, (0, react.createElement)("h3", null, tr("settings.
|
|
2767
|
+
})))), (0, react.createElement)("section", { className: "lks-set-row" }, (0, react.createElement)("h3", null, tr("settings.about")), (0, react.createElement)("p", { className: "lks-set-hint" }, tr("settings.about.hint")), data?.version ? (0, react.createElement)("a", {
|
|
2768
|
+
href: `https://github.com/Kaiji-Z/dsh-plugin-lookatstudy/releases/tag/v${data.version}`,
|
|
2769
|
+
target: "_blank",
|
|
2770
|
+
rel: "noreferrer"
|
|
2771
|
+
}, `v${data.version}`) : null), (0, react.createElement)("section", { className: "lks-set-row" }, (0, react.createElement)("h3", null, tr("settings.stateFile")), (0, react.createElement)("p", { className: "lks-set-hint" }, tr("settings.stateFile.hint")), data === null ? null : (0, react.createElement)("code", { className: "lks-set-path" }, data.statePath)), (0, react.createElement)(ActionError, { error }));
|
|
2576
2772
|
}
|
|
2577
2773
|
//#endregion
|
|
2578
2774
|
//#region src/client/dock.tsx
|
|
@@ -2586,20 +2782,23 @@ window.__ModuleLoader__.load({
|
|
|
2586
2782
|
* button's limitation), so the tooltip points at the Study tab instead.
|
|
2587
2783
|
* @module dsh-plugin-lookatstudy/client/dock
|
|
2588
2784
|
*/
|
|
2589
|
-
/** 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. */
|
|
2590
2786
|
function dockSegments(progress) {
|
|
2591
2787
|
return [
|
|
2592
2788
|
{
|
|
2593
2789
|
key: "due",
|
|
2594
|
-
text: tr("dock.due", { count: progress.dueCount })
|
|
2790
|
+
text: tr("dock.due", { count: progress.dueCount }),
|
|
2791
|
+
muted: progress.dueCount === 0
|
|
2595
2792
|
},
|
|
2596
2793
|
{
|
|
2597
2794
|
key: "streak",
|
|
2598
|
-
text: tr("dock.streak", { days: progress.streak })
|
|
2795
|
+
text: tr("dock.streak", { days: progress.streak }),
|
|
2796
|
+
muted: progress.streak === 0
|
|
2599
2797
|
},
|
|
2600
2798
|
{
|
|
2601
2799
|
key: "lv",
|
|
2602
|
-
text: tr("dock.lv", { level: progress.level })
|
|
2800
|
+
text: tr("dock.lv", { level: progress.level }),
|
|
2801
|
+
muted: false
|
|
2603
2802
|
}
|
|
2604
2803
|
];
|
|
2605
2804
|
}
|
|
@@ -2621,8 +2820,8 @@ window.__ModuleLoader__.load({
|
|
|
2621
2820
|
})
|
|
2622
2821
|
}, ...segments.map((s) => (0, react.createElement)("span", {
|
|
2623
2822
|
key: s.key,
|
|
2624
|
-
className: `lks-dockseg lks-dock-${s.key}`
|
|
2625
|
-
}, 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)));
|
|
2626
2825
|
}
|
|
2627
2826
|
//#endregion
|
|
2628
2827
|
//#region src/client/toolviews.tsx
|