devcoach 2.7.0-next.600.g5b3bf2c → 2.7.0-next.604.g26d3007
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/assets/references/course.md +85 -25
- package/assets/static/course-frame.js +23 -5
- package/assets/static/course-viewer.js +15 -2
- package/assets/static/style.css +87 -32
- package/dist/{app-SXNKI36F.js → app-HYACQCHI.js} +185 -143
- package/dist/bin.js +1 -1
- package/dist/{chunk-TZLTNO5R.js → chunk-75PGZUMS.js} +1 -1
- package/dist/{chunk-TPWMKLW6.js → chunk-HFG5R5JB.js} +1 -1
- package/dist/{chunk-IHWMGSKA.js → chunk-ZFX3B4HE.js} +1 -1
- package/dist/{commands-CZC7SSRE.js → commands-JRR4AYBW.js} +2 -2
- package/dist/{hooks-6OH4WVAY.js → hooks-HLUPV2BO.js} +1 -1
- package/dist/{server-3VH24C4V.js → server-7J2GDOFJ.js} +1 -1
- package/package.json +1 -1
|
@@ -86,9 +86,10 @@ under 900 px the menu becomes a strip above the content. Routing by `location.ha
|
|
|
86
86
|
(`#step-N`, no hash → step 1): on `hashchange` show that section, scroll to the top. Every
|
|
87
87
|
section ends with *← Previous · Next →* buttons and the line *"Stuck? Ask in the chat — that's
|
|
88
88
|
where the course is being taught."* Mark `:root[data-embedded] nav.steps { display: none }`:
|
|
89
|
-
the dashboard injects `data-embedded`, has its own step list, makes the canvas transparent
|
|
90
|
-
lifts the `max-width` of `.course`, `section` and `header.intro` to use the width it has
|
|
91
|
-
keep those class names
|
|
89
|
+
the dashboard injects `data-embedded`, has its own step list, makes the canvas transparent,
|
|
90
|
+
lifts the `max-width` of `.course`, `section` and `header.intro` to use the width it has, and
|
|
91
|
+
sets `data-theme` from its own light/dark toggle — so keep those class names and the
|
|
92
|
+
`data-theme` selectors.
|
|
92
93
|
|
|
93
94
|
**Each section**, in order: **Goal** (one line) → **The idea** (short, one concept, an analogy
|
|
94
95
|
where it helps) → **Worked example** → **Try it** (see the ladder below) → **Check yourself**
|
|
@@ -109,35 +110,56 @@ translated into JS just to make it runnable:
|
|
|
109
110
|
output** (typed or picked, then Reveal), **fill the blank**, or **spot the bug** (3–4
|
|
110
111
|
candidates). No button may pretend to execute Java, Rust or Go.
|
|
111
112
|
|
|
112
|
-
**Design.** One measure (~68ch), generous spacing,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
**Design — conform to the dashboard, do not copy it.** One measure (~68ch), generous spacing,
|
|
114
|
+
quiet Tufte-like typography, light + dark. Every colour is a **token** on `:root`; nothing is
|
|
115
|
+
hard-coded further down (no stray `#fff` on a button, no grey typed by hand in a rule):
|
|
116
|
+
|
|
117
|
+
- **Neutrals are the dashboard's** — `--bg --panel --text --muted --line --code-bg`, with the
|
|
118
|
+
values of the skeleton. They are what makes an embedded course sit on the dashboard's canvas
|
|
119
|
+
without a seam (embedded, the viewer sets these six itself, so keep the names).
|
|
120
|
+
- **The accent is yours** — the brand teal of the skeleton by default, or ONE accent that suits
|
|
121
|
+
the subject (Rust's orange, Swift's orange-red, a database blue…), as long as `--accent` reads
|
|
122
|
+
at 4.5:1 or better on `--panel` in both modes and `--on-accent` reads on `--accent`.
|
|
123
|
+
- `--ok` / `--warn` keep their meaning (right / wrong, safe / dangerous). Colours inside a
|
|
124
|
+
diagram or an illustration are free.
|
|
125
|
+
|
|
126
|
+
It must also pass as a Claude artifact page: `<title>` is a 2–4 word name (the long title is
|
|
127
|
+
the `<h1>`), dark tokens are set under `@media (prefers-color-scheme: dark)` guarded by
|
|
128
|
+
`:root:not([data-theme="light"])` and again under `:root[data-theme="dark"]`, `body` has an
|
|
129
|
+
explicit background, 16 px side gutters at phone width, no horizontal page scroll.
|
|
130
|
+
|
|
131
|
+
**Theme switch.** Opened on its own (the file, the artifact) the document follows the OS until
|
|
132
|
+
the reader uses its switch: a `<button class="theme-switch">` in the step menu that sets
|
|
133
|
+
`data-theme` on the root to `dark` or `light`, shows the **current** scheme, carries an
|
|
134
|
+
`aria-label` saying what a click does, and remembers the choice with `localStorage` inside
|
|
135
|
+
`try/catch` (storage is unavailable in a sandboxed open — the switch must still work for the
|
|
136
|
+
visit). Embedded in the dashboard the switch is hidden and the stored choice is NOT applied:
|
|
137
|
+
the dashboard's own toggle drives `data-theme` there.
|
|
138
|
+
|
|
139
|
+
Skeleton (adapt, keep the ids, the class names and the token names):
|
|
121
140
|
|
|
122
141
|
```html
|
|
123
142
|
<!doctype html><html lang="en"><head><meta charset="utf-8">
|
|
124
143
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
125
144
|
<title>Node error events</title>
|
|
126
145
|
<style>
|
|
127
|
-
:root { --bg:#
|
|
128
|
-
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --bg:#
|
|
129
|
-
:root[data-theme="dark"] { --bg:#
|
|
146
|
+
:root { --bg:#f9fafb; --panel:#ffffff; --text:#111827; --muted:#6b7280; --line:#e5e7eb; --code-bg:#f3f4f6; --accent:#0f766e; --accent-soft:#f0fdfa; --on-accent:#ffffff; --ok:#047857; --ok-soft:#d1fae5; --warn:#be123c; --warn-soft:#ffe4e6; --log-bg:#1e1f22; --log-text:#dfe1e5; }
|
|
147
|
+
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --bg:#1e1f22; --panel:#2b2d30; --text:#dfe1e5; --muted:#9da0a8; --line:#43454a; --code-bg:#1e1f22; --accent:#5eead4; --accent-soft:#134e4a; --on-accent:#0b2b28; --ok:#5fad65; --ok-soft:#1f3a24; --warn:#f97583; --warn-soft:#4a1f26; --log-bg:#18191b; --log-text:#dfe1e5; } }
|
|
148
|
+
:root[data-theme="dark"] { --bg:#1e1f22; --panel:#2b2d30; --text:#dfe1e5; --muted:#9da0a8; --line:#43454a; --code-bg:#1e1f22; --accent:#5eead4; --accent-soft:#134e4a; --on-accent:#0b2b28; --ok:#5fad65; --ok-soft:#1f3a24; --warn:#f97583; --warn-soft:#4a1f26; --log-bg:#18191b; --log-text:#dfe1e5; }
|
|
130
149
|
body { margin:0; background:var(--bg); color:var(--text); font:16px/1.6 system-ui, sans-serif; }
|
|
131
150
|
.course { display:grid; grid-template-columns:16rem minmax(0,1fr); gap:24px; max-width:1100px; margin:0 auto; padding:24px 16px; }
|
|
132
151
|
nav.steps { position:sticky; top:16px; align-self:start; } nav.steps a { display:block; padding:6px 10px; border-radius:8px; color:var(--muted); text-decoration:none; }
|
|
133
152
|
nav.steps a.current { background:var(--accent-soft); color:var(--accent); }
|
|
134
|
-
|
|
135
|
-
|
|
153
|
+
.theme-switch { margin-top:12px; background:transparent; color:var(--muted); border:1px solid var(--line); border-radius:8px; padding:4px 10px; cursor:pointer; }
|
|
154
|
+
:root[data-embedded] nav.steps, :root[data-embedded] .theme-switch { display:none } :root[data-embedded] .course { grid-template-columns:minmax(0,1fr); }
|
|
155
|
+
section { display:none; max-width:68ch; background:var(--panel); border:1px solid var(--line); border-radius:12px; padding:22px 24px; } section.current { display:block; }
|
|
156
|
+
button.primary { background:var(--accent); color:var(--on-accent); border:1px solid var(--accent); }
|
|
136
157
|
@media (max-width: 900px) { .course { grid-template-columns:minmax(0,1fr); } nav.steps { position:static; display:flex; flex-wrap:wrap; gap:4px; } }
|
|
137
|
-
/* .goal, .try, .check, .pager
|
|
158
|
+
/* .goal, .try, .check, .pager … — tokens only */
|
|
138
159
|
</style></head><body>
|
|
139
160
|
<div class="course">
|
|
140
|
-
<nav class="steps"><a href="#step-1">1 · Sums</a><a href="#step-2">2 · Powers</a
|
|
161
|
+
<nav class="steps"><a href="#step-1">1 · Sums</a><a href="#step-2">2 · Powers</a>
|
|
162
|
+
<button class="theme-switch" type="button"></button></nav>
|
|
141
163
|
<main>
|
|
142
164
|
<section id="step-1"><h2>1 · Sums</h2><p class="goal">…</p> … <div class="check">…<button>Reveal</button></div>
|
|
143
165
|
<div class="pager"><a href="#step-1">← Previous</a><a href="#step-2">Next →</a></div>
|
|
@@ -148,17 +170,55 @@ Skeleton (adapt, keep the ids and the class names):
|
|
|
148
170
|
<script>
|
|
149
171
|
// show one section: on load + hashchange, toggle .current on the section and its nav link, scrollTo(0,0)
|
|
150
172
|
// reveal buttons; the live examples (new Function inside try/catch) or the simulations
|
|
173
|
+
// theme switch — standalone only; embedded, the dashboard sets data-theme:
|
|
174
|
+
(function () {
|
|
175
|
+
var root = document.documentElement, btn = document.querySelector(".theme-switch"), KEY = "course-theme";
|
|
176
|
+
var isDark = function () {
|
|
177
|
+
var t = root.getAttribute("data-theme");
|
|
178
|
+
return t ? t === "dark" : matchMedia("(prefers-color-scheme: dark)").matches;
|
|
179
|
+
};
|
|
180
|
+
var paint = function () {
|
|
181
|
+
btn.textContent = isDark() ? "🌙 Dark" : "☀️ Light";
|
|
182
|
+
btn.setAttribute("aria-label", isDark() ? "Dark theme — switch to light" : "Light theme — switch to dark");
|
|
183
|
+
};
|
|
184
|
+
if (window.parent === window) {
|
|
185
|
+
try { var saved = localStorage.getItem(KEY); if (saved === "dark" || saved === "light") root.setAttribute("data-theme", saved); } catch (e) {}
|
|
186
|
+
}
|
|
187
|
+
btn.addEventListener("click", function () {
|
|
188
|
+
var next = isDark() ? "light" : "dark";
|
|
189
|
+
root.setAttribute("data-theme", next);
|
|
190
|
+
try { localStorage.setItem(KEY, next); } catch (e) {}
|
|
191
|
+
paint();
|
|
192
|
+
});
|
|
193
|
+
matchMedia("(prefers-color-scheme: dark)").addEventListener("change", paint);
|
|
194
|
+
paint();
|
|
195
|
+
})();
|
|
151
196
|
</script></body></html>
|
|
152
197
|
```
|
|
153
198
|
|
|
154
|
-
### 4b. Share as an artifact — an offer,
|
|
199
|
+
### 4b. Share as an artifact — an offer, with the disclosure
|
|
200
|
+
|
|
201
|
+
Publishing is the one moment course content **leaves the machine**: the Artifact tool uploads
|
|
202
|
+
a copy of the document to claude.ai, under the user's account. The offer must say so — a
|
|
203
|
+
"private" link describes who can open it, not where the file lives.
|
|
155
204
|
|
|
156
205
|
After the last `add_course_step`, **if the Artifact tool is available**, offer ONCE, as a
|
|
157
|
-
choice
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
206
|
+
choice, in these terms:
|
|
207
|
+
|
|
208
|
+
> *Publish this course as a Claude artifact too? That **uploads a copy to claude.ai** — it
|
|
209
|
+
> leaves this machine. Only you can open it until you share the link.*
|
|
210
|
+
|
|
211
|
+
- A course usually carries the user's own work (examples come from `seed_context`: project and
|
|
212
|
+
repository names, file paths, snippets, the bug that started it). When this one does, name
|
|
213
|
+
what it contains **in the same question** — *"It mentions lolcoach and the hang you
|
|
214
|
+
debugged."* — so the yes is an informed one. Never publish without that yes.
|
|
215
|
+
- **Yes** → publish `document_path` as it is (`icon: "course"`, `description` = the goal) and
|
|
216
|
+
reply with the link in one line. **No**, or no Artifact tool → nothing, and do not ask again.
|
|
217
|
+
- The local file stays the course; the artifact is a copy for sharing. The user can ask for it
|
|
218
|
+
any time ("publish the course as an artifact") — the same disclosure applies each time, and
|
|
219
|
+
to every republish after an edit. devcoach stores nothing about the artifact, so it cannot
|
|
220
|
+
delete it either: if asked to take it down, use the Artifact tool or point the user to their
|
|
221
|
+
artifacts on claude.ai.
|
|
162
222
|
|
|
163
223
|
## 5. Teach — one step per message
|
|
164
224
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// Injected by the dashboard at the end of every course document it serves (never part of the
|
|
2
2
|
// file on disk). Inside the sandboxed frame the parent cannot measure the document, so the
|
|
3
3
|
// document reports its own height and the step it is showing; the parent grows the frame to fit
|
|
4
|
-
// (the page scrolls, never the frame) and moves its step highlight.
|
|
5
|
-
//
|
|
4
|
+
// (the page scrolls, never the frame) and moves its step highlight. The dashboard in turn tells
|
|
5
|
+
// the document which theme it is in: framed, the document only sees the OS preference, not the
|
|
6
|
+
// dashboard's toggle. Standalone opens (a pasted URL, the artifact copy) never run this:
|
|
7
|
+
// `parent === window` exits at once, and the document's own theme switch is in charge.
|
|
6
8
|
(function () {
|
|
7
9
|
if (window.parent === window) return;
|
|
8
10
|
var root = document.documentElement;
|
|
@@ -10,12 +12,19 @@
|
|
|
10
12
|
// Embedded, the page canvas is the dashboard's: let it show through (sections keep their own
|
|
11
13
|
// panel colours), so the frame reads as part of the page instead of a box inside it.
|
|
12
14
|
root.style.background = "transparent";
|
|
13
|
-
// …
|
|
14
|
-
//
|
|
15
|
+
// …use the width the dashboard gives it (the reading-column caps of the contract's `.course` /
|
|
16
|
+
// `section` / `header.intro` are for the standalone page), and take the NEUTRALS from the
|
|
17
|
+
// dashboard palette so panels and text sit on its canvas without a seam. Accent and semantic
|
|
18
|
+
// tokens stay the document's own: a course conforms to the palette, it does not copy it.
|
|
15
19
|
var style = document.createElement("style");
|
|
16
20
|
style.textContent =
|
|
17
21
|
":root[data-embedded] .course{max-width:none;padding:4px 0 32px}" +
|
|
18
|
-
":root[data-embedded] section,:root[data-embedded] header.intro{max-width:none}"
|
|
22
|
+
":root[data-embedded] section,:root[data-embedded] header.intro{max-width:none}" +
|
|
23
|
+
":root[data-embedded] .theme-switch{display:none}" +
|
|
24
|
+
':root[data-embedded][data-theme="light"]{--bg:#f9fafb;--panel:#ffffff;--text:#111827;' +
|
|
25
|
+
"--muted:#6b7280;--line:#e5e7eb;--code-bg:#f3f4f6}" +
|
|
26
|
+
':root[data-embedded][data-theme="dark"]{--bg:#1e1f22;--panel:#2b2d30;--text:#dfe1e5;' +
|
|
27
|
+
"--muted:#9da0a8;--line:#43454a;--code-bg:#1e1f22}";
|
|
19
28
|
(document.head || root).appendChild(style);
|
|
20
29
|
function clearBody() { document.body.style.background = "transparent"; }
|
|
21
30
|
if (document.body) clearBody(); else document.addEventListener("DOMContentLoaded", clearBody);
|
|
@@ -27,6 +36,15 @@
|
|
|
27
36
|
"*",
|
|
28
37
|
);
|
|
29
38
|
}
|
|
39
|
+
// The dashboard answers every report with its theme (the first report is the handshake).
|
|
40
|
+
window.addEventListener("message", function (event) {
|
|
41
|
+
var data = event.data;
|
|
42
|
+
if (event.source !== window.parent || !data || data.type !== "devcoach:theme") return;
|
|
43
|
+
if (data.theme !== "dark" && data.theme !== "light") return;
|
|
44
|
+
if (root.getAttribute("data-theme") === data.theme) return;
|
|
45
|
+
root.setAttribute("data-theme", data.theme);
|
|
46
|
+
report();
|
|
47
|
+
});
|
|
30
48
|
window.addEventListener("load", report);
|
|
31
49
|
window.addEventListener("hashchange", report);
|
|
32
50
|
if (typeof ResizeObserver === "function") new ResizeObserver(report).observe(root);
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// Course page: the sandboxed frame reports its height and current step (course-frame.js, injected
|
|
2
2
|
// by the server); this side sizes the frame to the document so the page scrolls, keeps the step
|
|
3
3
|
// list in sync, and turns step clicks into in-frame navigation (a fragment navigation of the
|
|
4
|
-
// cross-origin frame) instead of a page reload.
|
|
4
|
+
// cross-origin frame) instead of a page reload. It also tells the frame which theme the
|
|
5
|
+
// dashboard is in: the framed document cannot see the toggle, only the OS preference.
|
|
5
6
|
(function () {
|
|
6
7
|
var frame = document.getElementById("course-frame");
|
|
7
8
|
var list = document.getElementById("course-steps");
|
|
8
9
|
if (!frame || !list) return;
|
|
9
|
-
var CURRENT = ["bg-
|
|
10
|
+
var CURRENT = ["bg-accent-50", "dark:bg-accent-900/30"];
|
|
10
11
|
var shown = null;
|
|
11
12
|
|
|
12
13
|
function select(anchor) {
|
|
@@ -22,6 +23,15 @@
|
|
|
22
23
|
});
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
// The frame's origin is opaque (sandbox without allow-same-origin): "*" is the only target
|
|
27
|
+
// that reaches it, and the payload is one word.
|
|
28
|
+
function sendTheme() {
|
|
29
|
+
if (!frame.contentWindow) return;
|
|
30
|
+
var dark = document.documentElement.classList.contains("dark");
|
|
31
|
+
frame.contentWindow.postMessage({ type: "devcoach:theme", theme: dark ? "dark" : "light" }, "*");
|
|
32
|
+
}
|
|
33
|
+
document.addEventListener("dc:theme", sendTheme);
|
|
34
|
+
|
|
25
35
|
function scrollToFrame() {
|
|
26
36
|
var top = frame.getBoundingClientRect().top + window.scrollY - 16;
|
|
27
37
|
window.scrollTo({ top: Math.max(0, top), behavior: "smooth" });
|
|
@@ -35,6 +45,9 @@
|
|
|
35
45
|
frame.style.height = Math.min(50000, Math.max(320, Math.ceil(height))) + "px";
|
|
36
46
|
}
|
|
37
47
|
if (typeof data.anchor === "string") select(data.anchor);
|
|
48
|
+
// Every report is answered with the theme: the first one is the handshake, so the message
|
|
49
|
+
// can never arrive before the frame listens.
|
|
50
|
+
sendTheme();
|
|
38
51
|
});
|
|
39
52
|
|
|
40
53
|
list.addEventListener("click", function (event) {
|
package/assets/static/style.css
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
/* devcoach — style overrides with light/dark theme support */
|
|
2
2
|
|
|
3
|
+
/* ── Palette tokens ─────────────────────────────────────────────────────────
|
|
4
|
+
The one place colours are decided. Values are space-separated RGB triplets so Tailwind can
|
|
5
|
+
apply its opacity modifiers: layout() in src/web/views.ts points Tailwind's `gray` and
|
|
6
|
+
`accent` scales at these variables (`rgb(var(--dc-gray-900) / <alpha-value>)`), which is how
|
|
7
|
+
every `dark:bg-gray-900` in the views picks the palette up without being rewritten.
|
|
8
|
+
- Light neutrals: Tailwind's own gray.
|
|
9
|
+
- Dark neutrals: an IDE ramp (JetBrains New UI dark) — page → panel → raised → border.
|
|
10
|
+
- Accent: the brand teal of the wordmark and the docs site, one step darker from 500 up so
|
|
11
|
+
white text on accent-600 and accent-600 text on white both clear 4.5:1.
|
|
12
|
+
tests/theme.test.ts holds the contrast floor for the pairs the UI relies on. */
|
|
13
|
+
|
|
14
|
+
:root {
|
|
15
|
+
--dc-gray-50: 249 250 251;
|
|
16
|
+
--dc-gray-100: 243 244 246;
|
|
17
|
+
--dc-gray-200: 229 231 235;
|
|
18
|
+
--dc-gray-300: 209 213 219;
|
|
19
|
+
--dc-gray-400: 156 163 175;
|
|
20
|
+
--dc-gray-500: 107 114 128;
|
|
21
|
+
--dc-gray-600: 75 85 99;
|
|
22
|
+
--dc-gray-700: 55 65 81;
|
|
23
|
+
--dc-gray-800: 31 41 55;
|
|
24
|
+
--dc-gray-900: 17 24 39;
|
|
25
|
+
--dc-gray-950: 3 7 18;
|
|
26
|
+
|
|
27
|
+
--dc-accent-50: 240 253 250;
|
|
28
|
+
--dc-accent-100: 204 251 241;
|
|
29
|
+
--dc-accent-200: 153 246 228;
|
|
30
|
+
--dc-accent-300: 94 234 212;
|
|
31
|
+
--dc-accent-400: 45 212 191;
|
|
32
|
+
--dc-accent-500: 13 148 136;
|
|
33
|
+
--dc-accent-600: 15 118 110;
|
|
34
|
+
--dc-accent-700: 17 94 89;
|
|
35
|
+
--dc-accent-800: 19 78 74;
|
|
36
|
+
--dc-accent-900: 15 61 58;
|
|
37
|
+
--dc-accent-950: 4 47 46;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.dark {
|
|
41
|
+
--dc-gray-50: 245 246 247;
|
|
42
|
+
--dc-gray-100: 236 238 241;
|
|
43
|
+
--dc-gray-200: 223 225 229; /* text */
|
|
44
|
+
--dc-gray-300: 188 190 196;
|
|
45
|
+
--dc-gray-400: 157 160 168; /* muted text */
|
|
46
|
+
--dc-gray-500: 144 150 160; /* secondary text */
|
|
47
|
+
--dc-gray-600: 111 115 122; /* disabled, faint */
|
|
48
|
+
--dc-gray-700: 67 69 74; /* borders */
|
|
49
|
+
--dc-gray-800: 57 59 64; /* raised: inputs, hover rows */
|
|
50
|
+
--dc-gray-900: 43 45 48; /* panels, nav, cards */
|
|
51
|
+
--dc-gray-950: 30 31 34; /* page canvas */
|
|
52
|
+
}
|
|
53
|
+
|
|
3
54
|
/* ── Theme variables ────────────────────────────────────────────────────── */
|
|
4
55
|
|
|
5
56
|
:root {
|
|
@@ -17,10 +68,10 @@
|
|
|
17
68
|
--md-pre-bg: #f8fafc;
|
|
18
69
|
--md-pre-border: #e2e8f0;
|
|
19
70
|
--md-blockquote-color: #6b7280;
|
|
20
|
-
--md-blockquote-border:
|
|
71
|
+
--md-blockquote-border: rgb(var(--dc-accent-500));
|
|
21
72
|
--md-hr: #e5e7eb;
|
|
22
|
-
--md-link:
|
|
23
|
-
--md-link-hover:
|
|
73
|
+
--md-link: rgb(var(--dc-accent-600));
|
|
74
|
+
--md-link-hover: rgb(var(--dc-accent-500));
|
|
24
75
|
--md-table-th-bg: #f9fafb;
|
|
25
76
|
--md-table-th-color: #374151;
|
|
26
77
|
--md-table-border: #e5e7eb;
|
|
@@ -34,36 +85,40 @@
|
|
|
34
85
|
}
|
|
35
86
|
|
|
36
87
|
.dark {
|
|
37
|
-
--scrollbar-track:
|
|
38
|
-
--scrollbar-thumb:
|
|
39
|
-
--scrollbar-thumb-hover:
|
|
40
|
-
|
|
41
|
-
--md-text:
|
|
42
|
-
--md-heading:
|
|
43
|
-
--md-heading-h3:
|
|
44
|
-
--md-h2-border:
|
|
45
|
-
--md-code-bg:
|
|
88
|
+
--scrollbar-track: rgb(var(--dc-gray-950));
|
|
89
|
+
--scrollbar-thumb: rgb(var(--dc-gray-700));
|
|
90
|
+
--scrollbar-thumb-hover: rgb(var(--dc-gray-600));
|
|
91
|
+
|
|
92
|
+
--md-text: rgb(var(--dc-gray-200));
|
|
93
|
+
--md-heading: rgb(var(--dc-gray-100));
|
|
94
|
+
--md-heading-h3: rgb(var(--dc-gray-300));
|
|
95
|
+
--md-h2-border: rgb(var(--dc-gray-700));
|
|
96
|
+
--md-code-bg: rgb(var(--dc-gray-800));
|
|
46
97
|
--md-code-color: #f472b6;
|
|
47
|
-
--md-code-border:
|
|
48
|
-
--md-pre-bg:
|
|
49
|
-
--md-pre-border:
|
|
50
|
-
--md-blockquote-color:
|
|
51
|
-
--md-blockquote-border:
|
|
52
|
-
--md-hr:
|
|
53
|
-
--md-link:
|
|
54
|
-
--md-link-hover:
|
|
55
|
-
--md-table-th-bg:
|
|
56
|
-
--md-table-th-color:
|
|
57
|
-
--md-table-border:
|
|
58
|
-
--md-table-td-color:
|
|
59
|
-
--md-table-even-bg:
|
|
60
|
-
--md-strong:
|
|
98
|
+
--md-code-border: rgb(var(--dc-gray-700));
|
|
99
|
+
--md-pre-bg: rgb(var(--dc-gray-950));
|
|
100
|
+
--md-pre-border: rgb(var(--dc-gray-700));
|
|
101
|
+
--md-blockquote-color: rgb(var(--dc-gray-400));
|
|
102
|
+
--md-blockquote-border: rgb(var(--dc-accent-400));
|
|
103
|
+
--md-hr: rgb(var(--dc-gray-700));
|
|
104
|
+
--md-link: rgb(var(--dc-accent-300));
|
|
105
|
+
--md-link-hover: rgb(var(--dc-accent-200));
|
|
106
|
+
--md-table-th-bg: rgb(var(--dc-gray-800));
|
|
107
|
+
--md-table-th-color: rgb(var(--dc-gray-300));
|
|
108
|
+
--md-table-border: rgb(var(--dc-gray-700));
|
|
109
|
+
--md-table-td-color: rgb(var(--dc-gray-200));
|
|
110
|
+
--md-table-even-bg: rgb(var(--dc-gray-800) / 0.4);
|
|
111
|
+
--md-strong: rgb(var(--dc-gray-100));
|
|
61
112
|
--md-em: #c4b5fd;
|
|
62
113
|
|
|
63
|
-
--check-border:
|
|
64
|
-
--check-bg:
|
|
114
|
+
--check-border: rgb(var(--dc-gray-600));
|
|
115
|
+
--check-bg: rgb(var(--dc-gray-900));
|
|
65
116
|
}
|
|
66
117
|
|
|
118
|
+
/* Code blocks: keep the highlighter's token colours, sit them on the editor background. */
|
|
119
|
+
.dark .markdown-body pre code.hljs,
|
|
120
|
+
.dark .hljs { background: var(--md-pre-bg); }
|
|
121
|
+
|
|
67
122
|
/* ── Scrollbar ──────────────────────────────────────────────────────────── */
|
|
68
123
|
|
|
69
124
|
::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
@@ -184,15 +239,15 @@
|
|
|
184
239
|
transition: background-color 0.12s, border-color 0.12s;
|
|
185
240
|
}
|
|
186
241
|
.dc-check:checked {
|
|
187
|
-
background-color:
|
|
188
|
-
border-color:
|
|
242
|
+
background-color: rgb(var(--dc-accent-600));
|
|
243
|
+
border-color: rgb(var(--dc-accent-600));
|
|
189
244
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 10.5l3.5 3.5L15 7'/%3E%3C/svg%3E");
|
|
190
245
|
background-size: 72%;
|
|
191
246
|
background-position: center;
|
|
192
247
|
background-repeat: no-repeat;
|
|
193
248
|
}
|
|
194
249
|
.dc-check:focus-visible {
|
|
195
|
-
outline: 2px solid
|
|
250
|
+
outline: 2px solid rgb(var(--dc-accent-500));
|
|
196
251
|
outline-offset: 2px;
|
|
197
252
|
}
|
|
198
253
|
|
|
@@ -220,7 +275,7 @@
|
|
|
220
275
|
}
|
|
221
276
|
.dc-resize-handle:hover::after,
|
|
222
277
|
.dc-resizing .dc-resize-handle:active::after {
|
|
223
|
-
background-color:
|
|
278
|
+
background-color: rgb(var(--dc-accent-500) / 0.85);
|
|
224
279
|
}
|
|
225
280
|
.dc-resizing {
|
|
226
281
|
user-select: none;
|