Skip to content

feat(tutorial): add interactive tutorial with Svelte REPL#555

Open
gka wants to merge 50 commits into
mainfrom
docs/tutorial
Open

feat(tutorial): add interactive tutorial with Svelte REPL#555
gka wants to merge 50 commits into
mainfrom
docs/tutorial

Conversation

@gka

@gka gka commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Interactive Tutorial (/tutorial)

  • Full step-by-step tutorial at `/tutorial` with an in-browser REPL editor + live preview
  • 7 chapters, 30+ exercises covering: getting started, marks (Dot, Tick, Line), transforms (stack, jitter), scales, axes & grids, and faceting
  • Each exercise has a starting state (`app-a/`) and a solution (`app-b/`) the user can reveal
  • "Auto-merge" mode: exercises that don't have an `app-a` inherit the previous step's solution as starting state
  • Shiki-based diff notation in prose: `+++added+++` / `---deleted---` highlight code changes inline
  • File tabs above the editor for multi-file exercises; SvelteKit `+`-prefixed files are hidden
  • Dark mode support for prose code blocks, editor, and preview pane

Custom REPL package (packages/repl/)

  • Self-contained `@svelteplot/repl` package (adapted from the Svelte REPL source) instead of relying on `@sveltejs/repl`
  • In-browser Rollup bundler worker with plugins: CSV, JSON, CommonJS, loop protection, TypeScript type stripping, and more
  • In-browser Svelte compiler worker
  • CodeMirror 6 editor with Svelte autocomplete

New library features

  • `svgFilter` prop added to all applicable marks (Area, Arrow, Dot, Image, Raster, RuleX/Y, TickY, Trail, Vector, and path/rect helpers)
  • `light-dark()` CSS color function support for marks

Bug fixes

  • AxisX and AxisY now automatically skip NaN and duplicate tick values
  • Fixed AxisY label alignment
  • Fixed global facet options (`fx`/`fy` scale propagation)

Docs & Examples

  • Inline examples rendered directly inside mark and transform reference pages via new `InlineExamplesGrid` component
  • New examples: rug plot, global facet scatter, tick-x-faceted, convex hull with blur
  • Extensive doc page rewrites across marks, transforms, features, and introduction pages
  • New polls dataset (`static/data/polls.csv`) and updated penguins CSV

Build & CI

  • `vite.config.js`: set `worker.format: 'es'` to fix IIFE/code-splitting incompatibility with the bundler web worker
  • OxLint CI: run `svelte-kit sync` before linting to generate required `.svelte-kit` types
  • Removed stray `.svelte-kit/tsconfig.json` from the repo root

Test plan

  • `pnpm run build` completes without errors
  • `/tutorial` loads; REPL editor and preview render correctly
  • Navigating between exercises resets the editor state
  • "Solve" button applies the solution; "Reset" reverts it
  • Auto-merge: exercises without `app-a` start from the previous exercise's solution
  • File tabs appear for multi-file exercises; SvelteKit `+` files are hidden
  • Diff notation (`+++foo+++`) renders highlighted in tutorial prose
  • Dark mode: prose code blocks, editor, and preview all switch themes
  • `svgFilter` prop works on Dot, Area, Line marks
  • AxisX/Y skip NaN ticks and deduplicate ticks
  • New examples (rug plot, global-facet, hull-blur) render correctly

🤖 Generated with Claude Code

@netlify

netlify Bot commented Apr 26, 2026

Copy link
Copy Markdown

Deploy Preview for svelteplot ready!

Name Link
🔨 Latest commit 38bf42b
🔍 Latest deploy log https://app.netlify.com/projects/svelteplot/deploys/6a2f928920d90d0008a28d71
😎 Deploy Preview https://deploy-preview-555--svelteplot.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions

Copy link
Copy Markdown

📦 Preview package for this PR is published!

Version: 0.14.2-pr-555.0

Install it with:

npm install svelteplot@pr-555
# or install the specific version
npm install svelteplot@0.14.2-pr-555.0

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e21aa48faf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

function toggle_solution() {
const target = solved ? data.exercise.a : data.exercise.b;
workspace.set(make_items({ ...data.exercise.a, ...target }));
solved = !solved;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Set solved state from intent instead of toggling it

workspace.set(...) calls the configured onreset handler synchronously, and that handler already assigns solved = false. The subsequent solved = !solved therefore computes from a mutated value, so clicking Reset (while currently solved) immediately flips solved back to true, leaving the UI stuck in the solved state and preventing the Solve/Reset toggle from behaving correctly. Set solved explicitly from the chosen target (solution vs starter) rather than negating the current flag.

Useful? React with 👍 / 👎.

@github-actions

Copy link
Copy Markdown

📦 Preview package for this PR is published!

Version: 0.14.2-pr-555.1

Install it with:

npm install svelteplot@pr-555
# or install the specific version
npm install svelteplot@0.14.2-pr-555.1

@github-actions

Copy link
Copy Markdown

📦 Preview package for this PR is published!

Version: 0.14.2-pr-555.2

Install it with:

npm install svelteplot@pr-555
# or install the specific version
npm install svelteplot@0.14.2-pr-555.2

@github-actions

Copy link
Copy Markdown

📦 Preview package for this PR is published!

Version: 0.14.2-pr-555.3

Install it with:

npm install svelteplot@pr-555
# or install the specific version
npm install svelteplot@0.14.2-pr-555.3

@github-actions

Copy link
Copy Markdown

📦 Preview package for this PR is published!

Version: 0.14.2-pr-555.4

Install it with:

npm install svelteplot@pr-555
# or install the specific version
npm install svelteplot@0.14.2-pr-555.4

@github-actions

Copy link
Copy Markdown

📦 Preview package for this PR is published!

Version: 0.14.2-pr-555.5

Install it with:

npm install svelteplot@pr-555
# or install the specific version
npm install svelteplot@0.14.2-pr-555.5

@github-actions

Copy link
Copy Markdown

📦 Preview package for this PR is published!

Version: 0.14.2-pr-555.6

Install it with:

npm install svelteplot@pr-555
# or install the specific version
npm install svelteplot@0.14.2-pr-555.6

@github-actions

Copy link
Copy Markdown

📦 Preview package for this PR is published!

Version: 0.14.2-pr-555.7

Install it with:

npm install svelteplot@pr-555
# or install the specific version
npm install svelteplot@0.14.2-pr-555.7

gka and others added 12 commits April 27, 2026 22:30
Integrates an interactive tutorial into svelteplot.dev using a local copy
of @sveltejs/repl with a minimal site-kit shim, tutorial content from the
svelteplot workshop, and a full-width SvelteKit layout that reuses the
global navbar.

- packages/repl/: local copy of @sveltejs/repl with embedded site-kit shim
  (Checkbox, Dropdown, ScreenToggle, Toolbox, HoverMenu, codemirror, polyfills)
- src/content/tutorial/02-svelteplot/: tutorial exercises (6 chapters)
- src/lib/server/tutorial.ts: import.meta.glob-based exercise loader
- src/routes/tutorial/[...slug]/: SplitPane layout with Editor + Viewer
- Vite/SvelteKit aliases for @sveltejs/repl/* and @sveltejs/site-kit/* shims
- tsconfig.json excludes tutorial content files from type checking

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The condition `parts[2] !== 'index.md'` excluded all exercise paths since
every exercise's markdown is named index.md. Changed to check that neither
the chapter nor exercise segment is `+assets`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…h resolution

@rollup/plugin-alias uses prefix matching for string aliases, so
{ find: '@sveltejs/repl', ... } was matching @sveltejs/repl/editor and
replacing only the prefix, producing a non-existent path. The base package
now resolves via the workspace symlink in node_modules.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Content was reorganized from 02-svelteplot/ to 01-basics/, 02-marks/,
03-transforms/. The loader now discovers groups dynamically from
/src/content/tutorial/ and threads groupAssetPrefix through for shared
assets, keeping slugs as chapter/exercise.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Set \$sidebar = false in the tutorial layout using the theme's writable
store, which GlobalLayout reads with {#if \$sidebar}. Restore on destroy
so navigating away from /tutorial re-shows the sidebar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tailwindcss v4 uses a Rust/WASM backend. When Vite pre-bundles it, the
.wasm file ends up misplaced in .vite/deps/ and the browser gets a 404.
Since init_tailwind() is only called with tailwind:true (never in this
tutorial), excluding the package prevents the WASM from being loaded at all.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rollup/browser loads bindings_wasm_bg.wasm via new URL(..., import.meta.url).
When Vite pre-bundles it into .vite/deps/, the .wasm file is left behind in
node_modules, so the browser gets a 404. Excluding it keeps import.meta.url
pointing at the real package location where the WASM file exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ntax highlighting

- Add shikiDiffNotation transformer with inline +++/--- marker support via Shiki decorations API
- Add shikiFileHeader transformer for /// file: comment styling
- Fix CodeMirror syntax highlighting by defining --shiki-token-* CSS variables
- Add file tabs above CodeMirror editor, hiding SvelteKit +-prefixed files
- Make tutorial Viewer respect prefers-color-scheme dark mode
- Configure Prettier: 2-space indent for tutorial .svelte files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b27c0f4eb3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +132 to +134
Array.from(new Set(data)).filter(
(d) => !isNaN(d as number) && d !== null && d !== undefined
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve custom categorical ticks

When callers pass explicit ticks/data for a categorical axis, e.g. ticks={['Adelie', 'Gentoo']} or data={['A', 'B']}, this filter drops every non-numeric string because isNaN('Adelie') is true. That makes custom ticks unusable on band/point scales; only null/undefined and numeric NaN should be removed. The same regression is present in the matching AxisY change.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e356cf607b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

workspace.files
.filter((f): f is File => f.type === 'file' && f.name.startsWith('/src/lib/'))
.map((f) => ({ ...f, name: f.name.slice(9) })),
{ runes: true, svelte_version }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bundle tutorial examples against the local package

When any tutorial file imports svelteplot, this bundle call provides no alias for it, so the REPL worker treats it as a bare npm dependency and resolves/fetches the latest published package instead of the workspace source. In deployed docs that are ahead of npm (or for changes in this same PR), the preview can render different behavior than the lesson and local docs; pass an alias/include the local package files for svelteplot in these bundle options.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ffd211664d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

d={path}
class={styleClass}
{style}
filter={resolveProp(args.svgFilter, datum, undefined) as string | undefined} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass original datum to svgFilter accessors

For PathGroup-backed marks such as Hull, DelaunayMesh, and VoronoiMesh, datum here is the scaled wrapper ({ datum, x, y, ... }), while resolveProp accessors are expected to run against the original data row (as the style code and other svgFilter call sites do with datum.datum). If a caller uses svgFilter={(d) => ...} or a column-backed filter, it receives the wrapper or cannot find the column, so per-datum filters are silently omitted or computed from the wrong object.

Useful? React with 👍 / 👎.

…at tutorial markdown

isNaN('A') === true in JS, so the NaN-filtering guard on custom axis tick data was
incorrectly dropping all string ticks on ordinal scales. Now only filters values that
are numeric NaN.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

…/prefer-svelte-reactivity

SvelteSet is already reactive so $state wrappers are not needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f9790fe63

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +176 to +177
if (parts.length >= 2 && parts[0] !== '+assets' && parts[0] !== 'index.md') {
if (!acc.includes(parts[0])) acc.push(parts[0]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include nested tutorial lesson directories

For lessons nested below a section directory, this only records parts[0] as the exercise. In the added content, paths like src/content/tutorial/01-basics/02-marks/01-dot/01-symbol/index.md therefore become only the slug 02-marks/01-dot; load_exercise then looks for 01-dot/index.md and never exposes the actual 01-symbol, 02-dotx-doty, line, etc. lessons in entries() or the nav, so large parts of the new tutorial are omitted from the built site.

Useful? React with 👍 / 👎.

…f resolve()

- toggle_group/toggle_chapter and afterNavigate now mutate open_groups/open_chapters
  in place instead of reassigning, resolving the non_reactive_update warning without
  needing $state() wrappers that ESLint flags as unnecessary
- InlineExamplesGrid: replace resolve(dynamicString) with base+url since resolve()
  only accepts typed SvelteKit routes, not plain strings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

…Lint and svelte-check

The svelte/no-navigation-without-resolve ESLint rule requires resolve(),
but resolve() is typed to only accept known SvelteKit routes — not dynamic
strings. Using `as any` (same pattern as ExamplesPagePreview) satisfies both.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant