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
6 changes: 6 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
*.cs text eol=lf
*.csproj text eol=lf
*.js text eol=lf
*.mjs text eol=lf
*.ts text eol=lf
*.tsx text eol=lf
*.css text eol=lf
*.html text eol=lf
*.svg text eol=lf

# The sandbox exists to be read and experimented with, not to describe what
# this repository is written in.
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ dist/

# Personal, machine-local agent settings
.claude/settings.local.json
.claude/launch.json

# Sandbox build output. The projects are committed; what building them
# produces is not.
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ crate, ports and adapters.
| [`BACKLOG.md`](BACKLOG.md) | What is not built yet, and what was decided against. |
| [`README.md`](README.md) | The user-facing manual. |
| [`sandbox/`](sandbox) | Working projects per ecosystem, to try by hand and to read as examples. |
| [`website/`](website) | The landing page, and what its deployment still needs. Not part of the crate, and not versioned with it. |

Before changing behavior, read the relevant `DESIGN.md` section. Before
building something new, check `BACKLOG.md` — it may already be decided against,
Expand Down
7 changes: 7 additions & 0 deletions website/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Where the site will be served from. Both values are read at build time.
#
# VITE_BASE_PATH is the path every asset URL is prefixed with: "/" for a custom
# domain at the root, "/vump/" for a GitHub project site at okcodes.github.io.
# VITE_SITE_URL is the absolute origin used for canonical and social-card URLs.
VITE_BASE_PATH=/
VITE_SITE_URL=https://okcodes.github.io/vump
24 changes: 24 additions & 0 deletions website/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
34 changes: 34 additions & 0 deletions website/.oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["react", "typescript", "oxc", "jsx-a11y", "import", "promise"],
"categories": {
"correctness": "error",
"suspicious": "error",
"perf": "error",
"pedantic": "warn"
},
"rules": {
"react/rules-of-hooks": "error",
"react/only-export-components": [
"warn",
{
"allowConstantExport": true
}
],
"typescript/consistent-type-imports": "error",
"typescript/no-explicit-any": "error",
"eslint/no-console": [
"error",
{
"allow": ["warn", "error"]
}
],
"react/react-in-jsx-scope": "off",
"import/no-unassigned-import": "off",
"require-unicode-regexp": "off",
"max-lines-per-function": "off",
"react/no-array-index-key": "off",
"import/max-dependencies": "off"
},
"ignorePatterns": ["dist", "node_modules"]
}
3 changes: 3 additions & 0 deletions website/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist
node_modules
package-lock.json
7 changes: 7 additions & 0 deletions website/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "all",
"plugins": ["prettier-plugin-tailwindcss"]
}
143 changes: 143 additions & 0 deletions website/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# vump website

The landing page for [vump](https://github.com/okcodes/vump). One page, built
to static files and destined for GitHub Pages.

```bash
npm install
npm run dev # http://localhost:5173
npm run check # typecheck, lint, format, build — what CI will run
```

| Script | Does |
| ------------------------- | ------------------------------------------------------------- |
| `dev` | Vite dev server with hot reload |
| `build` | Type-checks, then writes `dist/` |
| `preview` | Serves `dist/` as a deploy would |
| `typecheck` | `tsc -b`, no emit |
| `lint` | oxlint; any warning fails |
| `format` / `format:check` | Prettier over everything |
| `check` | All four, in order |
| `og` | Regenerates `public/og.png` and `public/apple-touch-icon.png` |

## What is where

| Path | Holds |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `src/content/` | Every fact the page states: terminal runs, tracked files, version rules, exit codes, links. Nothing here renders. |
| `src/components/sections/` | One file per numbered section, in page order. |
| `src/components/ui/` | The primitives: terminal pane, code block, section frame, reveal, buttons. |
| `src/lib/` | Hooks and the two small engines — a segment model for command output, and a highlighter for the four snippet languages. |
| `src/index.css` | The whole design system: palette, type, motion. |
| `scripts/og.mjs` | Renders the social card from the same values, offline. |

**Content is separate from layout on purpose.** Every terminal pane on the page
reproduces what the binary actually prints — the marks, the column alignment,
the wording of the errors. When vump's output changes, `src/content/runs.ts` is
the only file to revisit.

**The page states no version number.** The release chip fetches the newest
release from the GitHub API as the page loads, and renders nothing when that
fails. A version written into a marketing site is a version nobody bumps, which
is the defect vump exists to prevent.

## Design system

Colours are semantic roles — `ground`, `ink`, `muted`, `line`, `signal` — defined
once in `src/index.css` and flipped by a `data-theme` attribute on the document
element. No component names a literal colour, so the two themes cannot drift
apart. A script in `index.html` resolves the theme before first paint; an
explicit choice outranks the system preference.

Terminal surfaces are held dark in both themes. Rendered command output should
read as output, not as a styled quotation.

The accent is spent on one thing: what changed. A new version number, a passing
mark, a highlighted line in a snippet. Amber marks a refusal, red a failure, and
nothing else is coloured at all.

## Lint configuration

Four rules are off in `.oxlintrc.json`, each because it does not fit this
codebase rather than because it was inconvenient:

| Rule | Why |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `react/no-array-index-key` | Rendered lines and tokens are static arrays that never reorder; the index is the correct identity. |
| `max-lines-per-function` | Counts JSX as logic. A section component is markup, not complexity. |
| `import/max-dependencies` | `App.tsx` composes every section; that is its whole job. |
| `require-unicode-regexp` | The highlighter's patterns run over ASCII snippets written in this repository, and `u` mode outlaws the escapes they need. |

Everything else — correctness, suspicious, perf and pedantic — is on, and a
warning fails the run.

## Deploying to GitHub Pages

Not wired up yet, deliberately: the domain is not settled, and the base path is
baked into every asset URL at build time.

Two values decide it, both in `.env`:

| Variable | For a custom domain | For `okcodes.github.io/vump` |
| ---------------- | --------------------- | -------------------------------- |
| `VITE_BASE_PATH` | `/` | `/vump/` |
| `VITE_SITE_URL` | `https://your.domain` | `https://okcodes.github.io/vump` |

A custom domain also needs `public/CNAME` holding the bare hostname, which ships
to `dist/` untouched. `public/.nojekyll` is already there, so Pages serves the
build as-is instead of running it through Jekyll.

When the domain is decided, add `.github/workflows/website.yml`:

```yaml
name: website

on:
push:
branches: [main]
paths: ['website/**', '.github/workflows/website.yml']
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: website
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
cache-dependency-path: website/package-lock.json
- run: npm ci
- run: npm run check
- uses: actions/upload-pages-artifact@v3
with:
path: website/dist

deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- id: deploy
uses: actions/deploy-pages@v4
```

Then set Pages → Source → GitHub Actions in the repository settings.

The site is not versioned and vump does not track its `package.json`, which
declares no version at all. It ships when `main` moves, not when a tag does.
73 changes: 73 additions & 0 deletions website/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!doctype html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

<title>vump — one version, every file, proven at the tag</title>
<meta
name="description"
content="vump keeps every version number in a repository in step, and fails the build before a tag that disagrees with its source can cost you anything."
/>
<link rel="canonical" href="%VITE_SITE_URL%/" />

<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
<meta name="theme-color" content="#0c0e12" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="#f7f7f8" media="(prefers-color-scheme: light)" />

<meta property="og:type" content="website" />
<meta property="og:site_name" content="vump" />
<meta property="og:title" content="vump — one version, every file, proven at the tag" />
<meta
property="og:description"
content="Keep every version number in a repository in step, and fail the build before a tag that disagrees with its source can cost you anything."
/>
<meta property="og:url" content="%VITE_SITE_URL%/" />
<meta property="og:image" content="%VITE_SITE_URL%/og.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="vump — one version, every file, proven at the tag" />
<meta
name="twitter:description"
content="Keep every version number in a repository in step, and fail the build before a tag that disagrees with its source can cost you anything."
/>
<meta name="twitter:image" content="%VITE_SITE_URL%/og.png" />

<script>
// Runs before first paint: a theme resolved in React would flash the wrong
// one first. An explicit choice wins over the system preference, and dark
// is the fallback when neither says anything.
(function () {
try {
var stored = localStorage.getItem('vump-theme');
var system = window.matchMedia('(prefers-color-scheme: light)').matches
? 'light'
: 'dark';
document.documentElement.dataset.theme =
stored === 'light' || stored === 'dark' ? stored : system;
} catch (_) {
document.documentElement.dataset.theme = 'dark';
}
})();
</script>

<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "vump",
"applicationCategory": "DeveloperApplication",
"operatingSystem": "macOS, Linux, Windows",
"description": "Keep semver version numbers in sync across the files of a repository, and verify in CI that a released tag matches what is recorded in source.",
"url": "https://github.com/okcodes/vump",
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" }
}
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading