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
3 changes: 3 additions & 0 deletions .markdownlint-cli2.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
"CONTRIBUTING.md",
"AGENTS.md",
"CHANGELOG.md",
"THEMING.md",
"EMBED_API.md",
"SECURITY.md",
"docs/tutorials/*.md"
],
"ignores": [
Expand Down
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added (Theming guide -- 2026-06-04)

- New top-level `THEMING.md` documents the two-layer design-token system
(`src/editor/styles/tokens.css`), the runtime `html[data-theme]` light/dark model
(`theme.ts`, `se-theme-toggle`, `svgedit-themechange`, startup precedence, persistence),
and the `hex-guard` tokens-only color policy with its `hex-guard-allow` escape hatch.
Cross-linked from `README`, `CONTRIBUTING`, and `STYLE` §8; added to markdownlint.

### Changed (Documentation accuracy pass -- 2026-06-04)

- Reconciled the fork docs with the shipped code: `README` (tutorials no longer "rewrite
pending"; e2e is Chromium + Firefox), `CONTRIBUTING` (`scripts/run-e2e.ts`;
`feat/ts-migration` marked historical; no jQuery), `SECURITY` (`elix` → `lit`), and `STYLE`
§8/§9 (markdownlint scope; that it runs in `npm run lint`; 12.B–D doc sweep complete).
`Events.md` now states the embed API has shipped.
- Reconciled `EMBED_API.md` with `src/embed`: theme applies via `html[data-theme]` (not the
removed `body.theme-*`) and is `light`/`dark` only; the default `prompt` returns its default
value with no built-in input dialog (hosts must register a handler); documented `setPalette`
and `__setPalette`; corrected the wildcard-origin and error-code text to match the code.
- `EMBED_API.md` and `SECURITY.md` brought under markdownlint (reflowed to the 100-col cap;
tables, headings, and fences normalized) — all fork Markdown docs are now linted.

### Removed (Dead jPickerShim -- 2026-06-04)

- Deleted `src/editor/components/jgraduate/jPickerShim.ts` — orphaned by the jGraduate Lit
rewrite, with zero importers anywhere in the repo. The color picker reaches `se-color-picker`
directly through `se-gradient-editor`.

### Fixed (Embed API — error codes + wildcard warning -- 2026-06-04)

- The embed client and server now `console.warn` when `allowedOrigins` includes the wildcard `*`
(origin checking disabled — dev/test only). (`src/embed/client.ts`, `src/embed/server.ts`)
- `ERROR_CODES.PROTOCOL_VERSION_MISMATCH` is now attached to the `editor.ready` rejection, and
`DIALOG_HANDLER_TIMEOUT` to the timeout `error` event — both were defined but never attached.

**Verification:** `npm run lint` clean — eslint, markdownlint (16 files), hex-guard; 84/84 embed
unit tests pass (`vitest run embed`).

### Added (M2 -- light/dark theme toggle + persistence -- 2026-06-02)

- New top-bar `se-theme-toggle` (sun/moon) — one-click light/dark switch. (M2 / #96)
Expand Down
9 changes: 5 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ npm run lint # lint only
packages/svgcanvas/ svgcanvas workspace package (core SVG manipulation)
src/editor/ editor UI shell (chrome, panels, dialogs)
src/editor/extensions/ built-in editor extensions
scripts/ build + copy-static helpers, run-e2e.mjs
scripts/ build + copy-static helpers, run-e2e.ts
tests/ Vitest unit suite + Playwright e2e
docs/ AUDIT, design specs, plans, manual checklists
```
Expand All @@ -60,10 +60,11 @@ TODO #19.

General conventions:

- Vanilla DOM + web components — no React; new code avoids jQuery (legacy jQuery files are being
migrated out).
- Vanilla DOM + web components — no React, no jQuery.
- ES modules throughout; no CommonJS.
- 2-space indentation, single quotes.
- Colors/theming: use the semantic design tokens — see [`THEMING.md`](./THEMING.md). The hex-guard
blocks raw color outside `tokens.css`.

## Tests

Expand Down Expand Up @@ -97,7 +98,7 @@ See [`STYLE.md` § 6](./STYLE.md#6-commit-messages) for conventions.
## Branch Strategy

- `master` — releases and stable work
- `feat/ts-migration` — the ongoing JS → TS migration (long-lived feature branch, draft PR #1)
- `feat/ts-migration` — the now-merged JS → TS migration, kept for historical reference
- Short-lived feature branches off `master` for everything else

## Reporting Bugs
Expand Down
205 changes: 136 additions & 69 deletions EMBED_API.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Active redevelopment. The fork is being shaped per a locked scope directive:

- **Standalone distribution** — Velopack installers (Windows + Linux) and a Docker image
- **Light/dark theming** — token-driven design system with a one-click light/dark toggle
(persisted; follows the OS preference by default)
(persisted; follows the OS preference by default). See [THEMING.md](THEMING.md).
- **Iframe-embeddable** — clean drop-in for Control Menu and other hosts, with a documented
`EMBED_API.md` (postMessage RPC, two-way theme sync, swatch-palette injection)
- **Core JS → TypeScript** migration committed
Expand Down Expand Up @@ -42,10 +42,10 @@ npm test # lint + vitest unit suite + Playwright e2e
| `src/editor/` | Editor shell — top-level UI, menus, panels, dialogs, extensions, locale shim |
| `packages/svgcanvas/` | Core SVG canvas engine (the engine the editor sits on top of) |
| `tests/unit/` | Vitest unit tests (jsdom) |
| `tests/e2e/` | Playwright end-to-end tests (Chromium) |
| `tests/e2e/` | Playwright end-to-end tests (Chromium + Firefox) |
| `scripts/` | Build / test runner scripts |
| `_reference/embed-api-v6/` | Preserved V6-era embed API code, kept as design input for the upcoming V7+ embed API |
| `docs/tutorials/` | Editor / Canvas / API / Events / FAQ tutorials (rewrite pending — see TODO #12.D) |
| `docs/tutorials/` | Editor / Canvas / API / Events / FAQ tutorials |
| `CHANGELOG.md` | Keep a Changelog format; live source of what's changed |

## Path tool keys
Expand Down
19 changes: 13 additions & 6 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,27 @@ When reporting, please provide:

- **Acknowledgement:** within **72 hours** of receipt
- **Triage and initial assessment:** within one week
- **Fix and disclosure timeline:** discussed with the reporter on a per-issue basis, depending on severity and complexity
- **Fix and disclosure timeline:** discussed with the reporter on a per-issue basis,
depending on severity and complexity

## Supported Versions

Security fixes target the latest commit on `master`. Older commits and pre-release tags are not maintained.

## Scope

In scope: the svgedit SVG editor web app, its build pipeline (Vite, `scripts/`), the extension loader, the embed/iframe entry points, and the export modules (SVG, PNG, PDF).
In scope: the svgedit SVG editor web app, its build pipeline (Vite, `scripts/`), the extension
loader, the embed/iframe entry points, and the export modules (SVG, PNG, PDF).

Out of scope:
- Vulnerabilities in upstream dependencies (jspdf, dompurify, elix, vite, etc.) that have not been amplified by svgedit's usage — report those upstream to the respective project.
- Browser engine bugs (XSS, sandbox escapes, etc.) that surface in any web app, not specifically svgedit.
- Self-XSS or issues requiring the victim to paste attacker-controlled SVG into devtools or the source editor.
- Issues affecting only the upstream [SVG-Edit/svgedit](https://github.com/SVG-Edit/svgedit) project that haven't been carried into this fork — report those upstream.

- Vulnerabilities in upstream dependencies (jspdf, dompurify, lit, vite, etc.) that have not been
amplified by svgedit's usage — report those upstream to the respective project.
- Browser engine bugs (XSS, sandbox escapes, etc.) that surface in any web app, not
specifically svgedit.
- Self-XSS or issues requiring the victim to paste attacker-controlled SVG into devtools or the
source editor.
- Issues affecting only the upstream [SVG-Edit/svgedit](https://github.com/SVG-Edit/svgedit)
project that haven't been carried into this fork — report those upstream.

Thanks for helping keep the project safe.
19 changes: 11 additions & 8 deletions STYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,12 +277,15 @@ Honor system primary. Tooling catches markdown-formatting drift on files we choo

**Tools:**

- **markdownlint-cli2** — config in `.markdownlint.jsonc`; runner config in
`.markdownlint-cli2.jsonc`. Currently scoped to `STYLE.md` only; sub-project 12.B
expands to README, CONTRIBUTING, `docs/tutorials/`.
- **markdownlint-cli2** — style rules in `.markdownlint.jsonc`; runner globs in
`.markdownlint-cli2.jsonc` (the authoritative scope list). Covers the fork's top-level
Markdown docs and `docs/tutorials/*.md`; `node_modules/`, `dist/`, and `docs/superpowers/**`
are excluded.
- **eslint** — existing config covers TS/JS line length and comment formatting.
- **No new required CI checks.** Lint runs locally via `npm run lint:md`; not yet
wired into `npm run lint` (avoids day-one CI failures on existing docs).
- **hex-guard** — `scripts/check-no-raw-hex.mjs` (run as `npm run lint:hex`) fails on raw
colors outside `src/editor/styles/tokens.css`. See [`THEMING.md`](./THEMING.md).
- **`npm run lint`** chains `eslint .` → `markdownlint-cli2` → `npm run lint:hex`, and runs as
the `pretest` step, so `npm test` gates on it. `npm run lint:md` runs markdownlint alone.

**Contributor workflow:**

Expand All @@ -303,6 +306,6 @@ The following are NOT governed by this guide:
already enforces.
- **Third-party JSDoc** in `node_modules/` — obviously.

Existing docs (`README.md`, `CONTRIBUTING.md`, `docs/tutorials/*.md`, in-tree
JSDoc) are NOT retro-edited as part of the initial STYLE.md ship. Sub-project 12.B
applies these rules to existing docs in a follow-up sweep.
Existing docs (`README.md`, `CONTRIBUTING.md`, `docs/tutorials/*.md`, in-tree JSDoc) were
brought under these rules by the follow-up sweeps — sub-project 12.B (brand hygiene:
README/CONTRIBUTING), 12.C (targeted JSDoc → TS conversion), and 12.D (tutorials rewrite).
106 changes: 106 additions & 0 deletions THEMING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Theming and design tokens

How svgedit's look is themed: a two-layer CSS custom-property token system, a runtime light/dark
toggle, and the lint guard that keeps colors token-only. Read this before touching chrome styles
or adding UI.

The token source is `src/editor/styles/tokens.css`, the runtime theme module is
`src/editor/styles/theme.ts`, and the lint guard is `scripts/check-no-raw-hex.mjs`.

## The token system

Tokens live in `src/editor/styles/tokens.css` in two layers:

- **Primitives** — the raw palette: cool-grey and teal ramps (`--se-grey-25` … `--se-grey-950`,
`--se-teal-100` … `--se-teal-800`), near-black/near-white ink (`--se-ink`, `--se-paper`), and
status hues (`--se-red-*`, `--se-amber-*`, `--se-green-*`, `--se-sky-*`). Theme-independent.
- **Semantic** — role-based tokens mapped onto primitives: `--se-bg`, `--se-surface`,
`--se-surface-2`, `--se-canvas`, `--se-border`, `--se-border-strong`, `--se-text`,
`--se-text-muted`, `--se-text-subtle`, `--se-accent` (+ `-hover` / `-active` / `-subtle`),
`--se-on-accent`, `--se-danger`, `--se-warn`, `--se-success`, `--se-info`, `--se-focus-ring`,
`--se-scrim`, and `--se-shadow-*`.

**Components consume semantic tokens only.** Theming works by remapping the semantic layer:
`:root` holds the light values, and `html[data-theme="dark"]` re-points the *same* semantic
tokens at darker primitives. Primitives are never overridden per-theme.

Non-color scales sit alongside the tokens and are also theme-independent: the font stack
(`--se-font-sans`), type sizes (`--se-text-xs` … `--se-text-lg`), weights (`--se-fw-*`), line
heights (`--se-lh-*`), spacing (`--se-space-1` … `--se-space-7`), radii
(`--se-radius-sm`/`md`/`lg`/`pill`), and control sizes (`--se-control-h`, `--se-tool-size`,
`--se-toolbar-h`).

## How theming works at runtime

The active theme is the `data-theme` attribute on `<html>`: `:root` holds the light values and
`html[data-theme="dark"]` activates the dark set. `src/editor/styles/theme.ts` owns the
transitions:

- `applyTheme(theme)` sets `html[data-theme]` and dispatches a `svgedit-themechange` `CustomEvent`
(`detail: { theme }`).
- `toggleTheme()` flips light↔dark and applies it — it does **not** persist; the caller does.
- `resolveInitialTheme(stored)` returns a stored `'light'`/`'dark'`, else the OS
`prefers-color-scheme`; `applyInitialTheme(stored)` applies it.
- `getCurrentTheme()` / `getSystemTheme()` read the current and OS themes.

**Startup precedence** (`editorInit.ts`): `?theme=` URL param > stored pref > system. The URL
value is a per-load override and is not persisted.

**The toggle** `se-theme-toggle` (`src/editor/components/seThemeToggle.ts`) calls `toggleTheme()`
on click and emits a `toggle-theme` event; `MainMenu.ts` listens and persists the choice via
`ConfigObj.pref('theme')` (subject to the editor's storage opt-in, like any pref). The toggle
re-syncs its own sun/moon icon by listening for `svgedit-themechange`.

**Re-theming non-CSS surfaces.** Anything painted outside CSS must re-read its colors on
`svgedit-themechange`. The Rulers (Canvas 2D) are the worked example — `Rulers.ts` redraws on the
event so tick ink follows `--se-text`:

```js
document.addEventListener('svgedit-themechange', () => this.updateRulers())
```

The embed bundle mirrors this contract (`html[data-theme]` + `svgedit-themechange`); hosts drive
it via `?theme=` and `__setTheme`. See `EMBED_API.md`.

## Using and adding tokens

In component styles (including Lit `static styles`), reference semantic tokens — never raw color:

```css
button {
background: var(--se-surface);
color: var(--se-text);
border: 1px solid var(--se-border);
border-radius: var(--se-radius-sm);
}
```

To add a token: add (or reuse) a **primitive** in `tokens.css`, then add a **semantic** token in
both the `:root` block and the `html[data-theme="dark"]` block. Components reference only the
semantic name, so one addition themes automatically.

## The hex-guard

`scripts/check-no-raw-hex.mjs` enforces the tokens-only rule. It runs in `npm run lint` (as
`npm run lint:hex`, with `--error`) and fails the build on a raw color found anywhere under
`src/` in:

- any line of a `.css` file, or
- any line inside a `css` tagged-template literal in a `.ts` file (Lit `static styles`).

It flags hex (`#rgb` … `#rrggbbaa`), color functions (`rgb()`, `rgba()`, `hsl()`, `hsla()`), and a
set of CSS color keywords used as values (`color: black`, etc.). It does **not** flag
`transparent`, `currentColor`, `inherit`, `initial`, `unset`, `none`, or `var(--…)`. Only
`src/editor/styles/tokens.css` is allowlisted. Markdown and non-`css` TypeScript are out of scope,
so prose examples like this doc's are never flagged.

**Escape hatch.** A line containing the comment marker `hex-guard-allow` is skipped. Reserve it
for color that is intentionally not a theme token — user/functional color the editor renders
literally:

```css
.swatch-none { background: #fff; } /* hex-guard-allow: user palette swatch, not chrome */
```

Intentionally exempt today: color pickers and gradient editors, palette swatches, and
contrast-critical selection handles — these carry user/functional color, not themeable chrome.
5 changes: 3 additions & 2 deletions docs/tutorials/Events.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ document.addEventListener('svgEditorReady', () => {
})
```

A clean host-facing API for driving the embedded editor is planned separately
(the embed-API work; it will ship as `EMBED_API.md`).
A clean host-facing API for driving the embedded editor ships separately as
`EMBED_API.md` at the repo root (postMessage RPC, chrome control, two-way theme sync,
dialog hooks).

## Within-frame editor callbacks (`svgEditor.ready`)

Expand Down
Loading
Loading