Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions extensions/code-tutor/canvas-kit/.kit-version.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "2026-07-04.1",
"syncedAt": "2026-07-05T16:16:06.308Z",
"version": "2026-07-05.1",
"syncedAt": "2026-07-06T05:14:31.801Z",
"source": "create-canvas-app/kit"
}
59 changes: 51 additions & 8 deletions extensions/code-tutor/canvas-kit/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,31 @@
*
* Baseline canvas styling built on the Primer design tokens the runtime injects
* onto the canvas document (see github-app src/lib/extensionCanvasThemeContract.json).
* Every token has a hardcoded dark fallback so the canvas still looks right if a
* token is ever missing. Author canvas-specific styles on top of these.
*
* Theme awareness. The tokens the host injects are computed from the *current*
* user theme, so a light-theme user gets light values and a dark-theme user gets
* dark ones. Author against the --ck-* aliases below and your canvas follows the
* user's theme for free. Two rules keep that honest:
* 1. Never hardcode a dark-only color. Use a --ck-* alias, an injected token, or
* color-mix() of one so the value tracks the theme. (--ck-bg-inset derives
* itself from --ck-bg-muted + --ck-fg for exactly this reason.)
* 2. The dark fallbacks below only apply when a token is missing. Because the
* host injects its theme *after* first paint, a light user would otherwise
* see a dark flash, so the block after :root follows the OS color scheme
* until the host takes over (it sets data-color-mode when it does).
*/

:root {
/* semantic aliases -> injected tokens (fallbacks = GitHub dark) */
--ck-bg: var(--background-color-default, #0d1117);
--ck-bg-muted: var(--background-color-muted, #161b22);
--ck-bg-inset: var(--background-color-emphasis, #010409);
/* Recessed control surface (tabs track, wells, chips). Derived from the two
theme-aware tokens above rather than --background-color-emphasis, which is a
high-contrast chip color that is *dark* in light mode (#25292e) and would
paint every inset surface as a dark box on a light canvas. This mix stays a
touch more prominent than a card in either theme (dark ~#363c43, light
~#d6d8db). */
--ck-bg-inset: color-mix(in srgb, var(--ck-bg-muted) 85%, var(--ck-fg) 15%);
--ck-fg: var(--text-color-default, #f0f6fc);
--ck-muted: var(--text-color-muted, #8b949e);
--ck-link: var(--text-color-link, #58a6ff);
Expand All @@ -33,6 +49,33 @@
--ck-gap: 12px;
}

/* Theme-aware fallback (see header note 2). The host injects its Primer tokens
only after first paint. Until then (and in any non-app context that never
injects) follow the webview's OS color scheme instead of forcing dark. Both
rules are gated on :not([data-color-mode]): the host sets that attribute
alongside its tokens and explicit color-scheme, so the instant it themes the
canvas these back off and the host is fully in control. --ck-bg-inset is
omitted on purpose; it derives from --ck-bg-muted + --ck-fg, which are
redefined here, so it tracks the light values automatically. */
:root:not([data-color-mode]) { color-scheme: light dark; }

@media (prefers-color-scheme: light) {
:root:not([data-color-mode]) {
--ck-bg: var(--background-color-default, #ffffff);
--ck-bg-muted: var(--background-color-muted, #f6f8fa);
--ck-fg: var(--text-color-default, #1f2328);
--ck-muted: var(--text-color-muted, #59636e);
--ck-link: var(--text-color-link, #0969da);
--ck-accent: var(--text-color-accent, #0969da);
--ck-danger: var(--text-color-danger, #d1242f);
--ck-success: var(--text-color-success, #1a7f37);
--ck-attention: var(--text-color-attention, #9a6700);
--ck-border: var(--border-color-default, #d1d9e0);
--ck-border-muted: var(--border-color-muted, #d1d9e0);
--ck-focus: var(--color-focus-outline, var(--outline-color-focus-default, #0969da));
}
}

* { box-sizing: border-box; }

html, body {
Expand Down Expand Up @@ -143,8 +186,8 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold
border: 1px solid var(--ck-border);
color: var(--ck-muted);
}
/* Generic semantic badge variants map your own statuses to these in the view
(e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add
/* Generic semantic badge variants (map your own statuses to these in the view;
e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add
app-specific status names here; this is the shared kit theme. */
.ck-badge-success { color: var(--ck-success); border-color: var(--border-color-success-emphasis, #238636); }
.ck-badge-accent { color: var(--text-color-accent, #a371f7); border-color: var(--border-color-accent-emphasis, #8957e5); }
Expand All @@ -170,7 +213,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold
.ck-empty { color: var(--ck-muted); text-align: center; padding: 28px 0; }

/* ---- loading: spinner + skeleton ----------------------------------------- */
/* Spinner: pair with a Lucide "loader-circle" icon
/* Spinner: pair with a Lucide "loader-circle" icon:
html`<${Icon} name="loader-circle" class="ck-spinner" />`. */
.ck-spinner { animation: ck-spin 0.9s linear infinite; transform-origin: center; }
@keyframes ck-spin { to { transform: rotate(360deg); } }
Expand All @@ -188,7 +231,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold
position: absolute;
inset: 0;
transform: translateX(-100%);
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06), transparent);
background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ck-fg) 8%, transparent), transparent);
animation: ck-shimmer 1.2s ease-in-out infinite;
}
@keyframes ck-shimmer { to { transform: translateX(100%); } }
Expand Down Expand Up @@ -217,7 +260,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold
/* ---- motion safety ------------------------------------------------------- */
/* Global guard: respect the OS "reduce motion" setting for every animation a
canvas adds (kit spinners/skeletons included). Author-added keyframes inherit
this automatically you do not need to repeat it per canvas. */
this automatically, so you do not need to repeat it per canvas. */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
Expand Down
2 changes: 1 addition & 1 deletion extensions/code-tutor/canvas-kit/version.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
// convention (the skill ships as files, not an npm version); a commit short-sha
// works too. Re-exported from client.mjs so a canvas can read it at runtime.

export const KIT_VERSION = "2026-07-04.1";
export const KIT_VERSION = "2026-07-05.1";
16 changes: 8 additions & 8 deletions extensions/code-tutor/web/app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ const LEVEL_COLOR = {
};

const CATEGORY_META = {
algorithm: { icon: "workflow", label: "Algorithm", color: "#58a6ff" },
"data-structure": { icon: "boxes", label: "Data structure", color: "#bc8cff" },
complexity: { icon: "gauge", label: "Complexity", color: "#e3b341" },
theory: { icon: "brain", label: "Theory", color: "#56d4dd" },
pattern: { icon: "layers", label: "Pattern", color: "#3fb950" },
paradigm: { icon: "git-fork", label: "Paradigm", color: "#f778ba" },
concurrency: { icon: "waypoints", label: "Concurrency", color: "#f0883e" },
system: { icon: "network", label: "System", color: "#a371f7" },
algorithm: { icon: "workflow", label: "Algorithm", color: "var(--color-data-blue-emphasis, #58a6ff)" },
"data-structure": { icon: "boxes", label: "Data structure", color: "var(--color-data-purple-emphasis, #bc8cff)" },
complexity: { icon: "gauge", label: "Complexity", color: "var(--color-data-yellow-emphasis, #e3b341)" },
theory: { icon: "brain", label: "Theory", color: "var(--color-data-teal-emphasis, #56d4dd)" },
pattern: { icon: "layers", label: "Pattern", color: "var(--color-data-green-emphasis, #3fb950)" },
paradigm: { icon: "git-fork", label: "Paradigm", color: "var(--color-data-pink-emphasis, #f778ba)" },
concurrency: { icon: "waypoints", label: "Concurrency", color: "var(--color-data-orange-emphasis, #f0883e)" },
system: { icon: "network", label: "System", color: "var(--color-data-plum-emphasis, #a371f7)" },
};
const catMeta = (c) => CATEGORY_META[c] ?? { icon: "book-open", label: c || "Topic", color: "var(--ck-accent)" };

Expand Down
22 changes: 12 additions & 10 deletions extensions/code-tutor/web/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@
--r-md: 9px;
--r-sm: 6px;

/* level palette */
--lvl-eli5: #3fb950;
--lvl-curious: #58a6ff;
--lvl-engineer: #d29922;
--lvl-wizard: #bc8cff;
/* Level palette. Semantic kit tokens (not raw hex) so each hue tracks the
user's theme: the injected values darken for contrast on light and brighten
on dark. Wizard uses the injected purple data token. */
--lvl-eli5: var(--ck-success);
--lvl-curious: var(--ck-accent);
--lvl-engineer: var(--ck-attention);
--lvl-wizard: var(--color-data-purple-emphasis, #8250df);

/* quality palette */
--q-good: #3fb950;
--q-ok: #d29922;
--q-bad: #f85149;
--q-good: var(--ck-success);
--q-ok: var(--ck-attention);
--q-bad: var(--ck-danger);
}

body { padding: 0; }
Expand Down Expand Up @@ -104,7 +106,7 @@ button.cs-chip:hover { border-color: var(--ck-accent); color: var(--ck-fg); }
.cs-slider input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none; appearance: none; width: 18px; height: 18px; border-radius: 50%;
background: var(--ck-bg); border: 3px solid var(--lvl); cursor: pointer;
box-shadow: 0 1px 4px rgba(0,0,0,0.4);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.28);
}
.cs-slider input[type="range"]::-moz-range-thumb {
width: 18px; height: 18px; border-radius: 50%; background: var(--ck-bg); border: 3px solid var(--lvl); cursor: pointer;
Expand Down Expand Up @@ -137,7 +139,7 @@ button.cs-chip:hover { border-color: var(--ck-accent); color: var(--ck-fg); }
background: color-mix(in srgb, var(--ck-bg-muted) 94%, transparent);
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
border-bottom: 1px solid var(--ck-border);
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.28);
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
transform: translateY(-110%); opacity: 0; visibility: hidden;
transition: transform 0.18s ease, opacity 0.18s ease, visibility 0.18s;
--lvl: var(--ck-accent);
Expand Down
4 changes: 2 additions & 2 deletions extensions/language-tutor/canvas-kit/.kit-version.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "2026-07-04.1",
"syncedAt": "2026-07-05T16:16:13.213Z",
"version": "2026-07-05.1",
"syncedAt": "2026-07-06T05:14:31.945Z",
"source": "create-canvas-app/kit"
}
59 changes: 51 additions & 8 deletions extensions/language-tutor/canvas-kit/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,31 @@
*
* Baseline canvas styling built on the Primer design tokens the runtime injects
* onto the canvas document (see github-app src/lib/extensionCanvasThemeContract.json).
* Every token has a hardcoded dark fallback so the canvas still looks right if a
* token is ever missing. Author canvas-specific styles on top of these.
*
* Theme awareness. The tokens the host injects are computed from the *current*
* user theme, so a light-theme user gets light values and a dark-theme user gets
* dark ones. Author against the --ck-* aliases below and your canvas follows the
* user's theme for free. Two rules keep that honest:
* 1. Never hardcode a dark-only color. Use a --ck-* alias, an injected token, or
* color-mix() of one so the value tracks the theme. (--ck-bg-inset derives
* itself from --ck-bg-muted + --ck-fg for exactly this reason.)
* 2. The dark fallbacks below only apply when a token is missing. Because the
* host injects its theme *after* first paint, a light user would otherwise
* see a dark flash, so the block after :root follows the OS color scheme
* until the host takes over (it sets data-color-mode when it does).
*/

:root {
/* semantic aliases -> injected tokens (fallbacks = GitHub dark) */
--ck-bg: var(--background-color-default, #0d1117);
--ck-bg-muted: var(--background-color-muted, #161b22);
--ck-bg-inset: var(--background-color-emphasis, #010409);
/* Recessed control surface (tabs track, wells, chips). Derived from the two
theme-aware tokens above rather than --background-color-emphasis, which is a
high-contrast chip color that is *dark* in light mode (#25292e) and would
paint every inset surface as a dark box on a light canvas. This mix stays a
touch more prominent than a card in either theme (dark ~#363c43, light
~#d6d8db). */
--ck-bg-inset: color-mix(in srgb, var(--ck-bg-muted) 85%, var(--ck-fg) 15%);
--ck-fg: var(--text-color-default, #f0f6fc);
--ck-muted: var(--text-color-muted, #8b949e);
--ck-link: var(--text-color-link, #58a6ff);
Expand All @@ -33,6 +49,33 @@
--ck-gap: 12px;
}

/* Theme-aware fallback (see header note 2). The host injects its Primer tokens
only after first paint. Until then (and in any non-app context that never
injects) follow the webview's OS color scheme instead of forcing dark. Both
rules are gated on :not([data-color-mode]): the host sets that attribute
alongside its tokens and explicit color-scheme, so the instant it themes the
canvas these back off and the host is fully in control. --ck-bg-inset is
omitted on purpose; it derives from --ck-bg-muted + --ck-fg, which are
redefined here, so it tracks the light values automatically. */
:root:not([data-color-mode]) { color-scheme: light dark; }

@media (prefers-color-scheme: light) {
:root:not([data-color-mode]) {
--ck-bg: var(--background-color-default, #ffffff);
--ck-bg-muted: var(--background-color-muted, #f6f8fa);
--ck-fg: var(--text-color-default, #1f2328);
--ck-muted: var(--text-color-muted, #59636e);
--ck-link: var(--text-color-link, #0969da);
--ck-accent: var(--text-color-accent, #0969da);
--ck-danger: var(--text-color-danger, #d1242f);
--ck-success: var(--text-color-success, #1a7f37);
--ck-attention: var(--text-color-attention, #9a6700);
--ck-border: var(--border-color-default, #d1d9e0);
--ck-border-muted: var(--border-color-muted, #d1d9e0);
--ck-focus: var(--color-focus-outline, var(--outline-color-focus-default, #0969da));
}
}

* { box-sizing: border-box; }

html, body {
Expand Down Expand Up @@ -143,8 +186,8 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold
border: 1px solid var(--ck-border);
color: var(--ck-muted);
}
/* Generic semantic badge variants map your own statuses to these in the view
(e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add
/* Generic semantic badge variants (map your own statuses to these in the view;
e.g. open -> ck-badge-success, decided -> ck-badge-accent). Do NOT add
app-specific status names here; this is the shared kit theme. */
.ck-badge-success { color: var(--ck-success); border-color: var(--border-color-success-emphasis, #238636); }
.ck-badge-accent { color: var(--text-color-accent, #a371f7); border-color: var(--border-color-accent-emphasis, #8957e5); }
Expand All @@ -170,7 +213,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold
.ck-empty { color: var(--ck-muted); text-align: center; padding: 28px 0; }

/* ---- loading: spinner + skeleton ----------------------------------------- */
/* Spinner: pair with a Lucide "loader-circle" icon
/* Spinner: pair with a Lucide "loader-circle" icon:
html`<${Icon} name="loader-circle" class="ck-spinner" />`. */
.ck-spinner { animation: ck-spin 0.9s linear infinite; transform-origin: center; }
@keyframes ck-spin { to { transform: rotate(360deg); } }
Expand All @@ -188,7 +231,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold
position: absolute;
inset: 0;
transform: translateX(-100%);
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06), transparent);
background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ck-fg) 8%, transparent), transparent);
animation: ck-shimmer 1.2s ease-in-out infinite;
}
@keyframes ck-shimmer { to { transform: translateX(100%); } }
Expand Down Expand Up @@ -217,7 +260,7 @@ h3 { font-size: var(--text-title-small, 1rem); font-weight: var(--ck-fw-semibold
/* ---- motion safety ------------------------------------------------------- */
/* Global guard: respect the OS "reduce motion" setting for every animation a
canvas adds (kit spinners/skeletons included). Author-added keyframes inherit
this automatically you do not need to repeat it per canvas. */
this automatically, so you do not need to repeat it per canvas. */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
Expand Down
2 changes: 1 addition & 1 deletion extensions/language-tutor/canvas-kit/version.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
// convention (the skill ships as files, not an npm version); a commit short-sha
// works too. Re-exported from client.mjs so a canvas can read it at runtime.

export const KIT_VERSION = "2026-07-04.1";
export const KIT_VERSION = "2026-07-05.1";
4 changes: 2 additions & 2 deletions extensions/news-aggregator/canvas-kit/.kit-version.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "2026-07-04.1",
"syncedAt": "2026-07-05T16:16:13.492Z",
"version": "2026-07-05.1",
"syncedAt": "2026-07-06T05:14:32.084Z",
"source": "create-canvas-app/kit"
}
Loading
Loading