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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
node_modules/
build
dist
preview
*.vsix
34 changes: 34 additions & 0 deletions README.vsce.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Pierre Theme

A clean light and dark theme for Visual Studio Code and Cursor, built for [Diffs.com](https://diffs.com).

## Installation

1. Open the Extensions view (`Cmd+Shift+X`)
2. Search for **Pierre Theme**
3. Click **Install**
4. Open the Command Palette (`Cmd+Shift+P`) → **Color Theme** → select **Pierre Light** or **Pierre Dark**

## Variants

| Variant | Description |
|---|---|
| Pierre Light | Light theme |
| Pierre Dark | Dark theme |

## Links

- [GitHub](https://github.com/pierrecomputer/theme)
- [Diffs.com](https://diffs.com)
- [pierre.computer](https://pierre.computer)


Also available for [Zed](https://zed.dev/extensions?query=pierre) and as an [npm package](https://www.npmjs.com/package/@pierre/theme) for use with [Shiki](https://shiki.style/).

## Previews

Pierre Dark Soft and Pierre Light Soft.

<img width="3468" height="2496" alt="pierre-dark-soft" src="https://github.com/user-attachments/assets/6bb6498d-a9a0-42e9-8b14-f9cbaba0317e" />

<img width="3468" height="2496" alt="pierre-light-soft" src="https://github.com/user-attachments/assets/fd4ea6e7-8a00-4a17-922a-b7437543ffa3" />
24 changes: 22 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@pierre/theme",
"displayName": "Pierre Theme",
"description": "Pierre theme for Shiki, VS Code, and more",
"version": "0.0.29",
"version": "1.0.0",
"publisher": "pierrecomputer",
"icon": "icon.png",
"galleryBanner": {
Expand Down Expand Up @@ -36,15 +36,26 @@
"uiTheme": "vs",
"path": "./themes/pierre-light.json"
},
{
"label": "Pierre Light Soft",
"uiTheme": "vs",
"path": "./themes/pierre-light-soft.json"
},
{
"label": "Pierre Dark",
"uiTheme": "vs-dark",
"path": "./themes/pierre-dark.json"
},
{
"label": "Pierre Dark Soft",
"uiTheme": "vs-dark",
"path": "./themes/pierre-dark-soft.json"
}
]
},
"scripts": {
"build": "ts-node src/build.ts",
"preview": "ts-node src/palette-preview.ts",
"test": "npm run build && ts-node src/test.ts",
"start": "nodemon --watch src --ext ts --exec npm run build",
"package": "ts-node src/package-vsix.ts",
Expand All @@ -60,7 +71,8 @@
"files": [
"dist",
"themes",
"icon.png"
"icon.png",
"LICENSE"
],
"exports": {
".": {
Expand All @@ -71,10 +83,18 @@
"types": "./dist/pierre-dark.d.mts",
"default": "./dist/pierre-dark.mjs"
},
"./pierre-dark-soft": {
"types": "./dist/pierre-dark-soft.d.mts",
"default": "./dist/pierre-dark-soft.mjs"
},
"./pierre-light": {
"types": "./dist/pierre-light.d.mts",
"default": "./dist/pierre-light.mjs"
},
"./pierre-light-soft": {
"types": "./dist/pierre-light-soft.d.mts",
"default": "./dist/pierre-light-soft.mjs"
},
"./pierre-dark-vibrant": {
"types": "./dist/pierre-dark-vibrant.d.mts",
"default": "./dist/pierre-dark-vibrant.mjs"
Expand Down
6 changes: 5 additions & 1 deletion src/build.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// src/build.ts
import { writeFileSync, mkdirSync } from "node:fs";
import { light as rolesLight, dark as rolesDark } from "./palette";
import { light as rolesLight, lightSoft as rolesLightSoft, dark as rolesDark, darkSoft as rolesDarkSoft } from "./palette";
import { makeTheme } from "./theme";
import { makeZedThemeFamily } from "./zed-theme";
import { convertRolesToP3 } from "./color-p3";
Expand All @@ -17,7 +17,9 @@ const rolesDarkP3 = convertRolesToP3(rolesDark);
// ============================================
const vscodeThemes = [
{ file: "themes/pierre-light.json", theme: makeTheme("Pierre Light", "light", rolesLight) },
{ file: "themes/pierre-light-soft.json", theme: makeTheme("Pierre Light Soft", "light", rolesLightSoft) },
{ file: "themes/pierre-dark.json", theme: makeTheme("Pierre Dark", "dark", rolesDark) },
{ file: "themes/pierre-dark-soft.json", theme: makeTheme("Pierre Dark Soft", "dark", rolesDarkSoft) },
{ file: "themes/pierre-light-vibrant.json", theme: makeTheme("Pierre Light Vibrant", "light", rolesLightP3) },
{ file: "themes/pierre-dark-vibrant.json", theme: makeTheme("Pierre Dark Vibrant", "dark", rolesDarkP3) }
];
Expand All @@ -32,7 +34,9 @@ for (const {file, theme} of vscodeThemes) {
// ============================================
const zedTheme = makeZedThemeFamily("Pierre", "pierrecomputer", [
{ name: "Pierre Light", appearance: "light", roles: rolesLight },
{ name: "Pierre Light Soft", appearance: "light", roles: rolesLightSoft },
{ name: "Pierre Dark", appearance: "dark", roles: rolesDark },
{ name: "Pierre Dark Soft", appearance: "dark", roles: rolesDarkSoft },
]);

writeFileSync("zed/themes/pierre.json", JSON.stringify(zedTheme, null, 2), "utf8");
Expand Down
24 changes: 20 additions & 4 deletions src/package-vsix.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { readFileSync, writeFileSync } from "fs";
import { readFileSync, writeFileSync, existsSync, renameSync } from "fs";
import { execSync } from "child_process";
import { join } from "path";

const pkgPath = join(__dirname, "..", "package.json");
const root = join(__dirname, "..");
const pkgPath = join(root, "package.json");
const readmePath = join(root, "README.md");
const vsceReadmePath = join(root, "README.vsce.md");
const readmeBackupPath = join(root, "README.md.bak");

const original = readFileSync(pkgPath, "utf-8");
const pkg = JSON.parse(original);

Expand All @@ -12,11 +17,22 @@ pkg.name = "pierre-theme";

console.log(`Temporarily renaming package: ${originalName} → ${pkg.name}\n`);

const hadReadme = existsSync(readmePath);

try {
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
execSync("vsce package", { stdio: "inherit", cwd: join(__dirname, "..") });

// Swap in the VSCE-specific README
if (hadReadme) renameSync(readmePath, readmeBackupPath);
renameSync(vsceReadmePath, readmePath);

execSync("vsce package", { stdio: "inherit", cwd: root });
} finally {
// Always restore original package.json
// Restore README files
renameSync(readmePath, vsceReadmePath);
if (hadReadme) renameSync(readmeBackupPath, readmePath);

// Restore original package.json
writeFileSync(pkgPath, original);
console.log(`\nRestored package name: ${originalName}`);
}
150 changes: 150 additions & 0 deletions src/palette-preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// src/palette-preview.ts
// Generates preview/palette.html — a swatch sheet of every palette in palette.ts.
import { writeFileSync, mkdirSync } from "node:fs";
import { palettes } from "./palette";

mkdirSync("preview", { recursive: true });

const sections = Object.entries(palettes)
.map(([name, scale]) => {
// JS reorders integer-indexed keys ahead of string keys, which would move
// "020" / "040" / "060" / "080" after "100". Sort numerically to restore
// the intended light-to-dark order.
const stops = Object.entries(scale).sort(([a], [b]) => Number(a) - Number(b));
const swatches = stops
.map(
([stop, hex]) =>
` <div class="swatch" style="background:${hex};color:contrast-color(${hex})">` +
`<span class="stop">${stop}</span>` +
`<span class="hex">${hex}</span>` +
`</div>`,
)
.join("\n");
return ` <section class="palette">
<div class="palette-name">
${name}
<span class="palette-count">${stops.length} stops</span>
</div>
<div class="swatches">
${swatches}
</div>
</section>`;
})
.join("\n");

const html = `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Pierre Palette</title>
<style>
:root {
color-scheme: light dark;
font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", system-ui, sans-serif;
--bg: #fafafa;
--fg: #171717;
--muted: #737373;
}

@media (prefers-color-scheme: dark) {
:root {
--bg: #0a0a0a;
--fg: #fafafa;
--muted: #8a8a8a;
}
}

* {
box-sizing: border-box;
}

body {
margin: 0;
padding: 32px 32px 64px;
background: var(--bg);
color: var(--fg);
}

header,
main {
max-width: 1400px;
margin: 0 auto;
}

header {
margin-bottom: 32px;
}

main {
display: flex;
flex-direction: column;
gap: 20px;
}

h1 {
margin: 0;
font-size: 12px;
font-weight: 600;
letter-spacing: 1px;
text-transform: uppercase;
color: var(--muted);
}

.palette-name {
display: flex;
align-items: baseline;
gap: 12px;
margin: 0 0 8px;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 13px;
font-weight: 600;
}

.palette-count {
font-size: 11px;
font-weight: 400;
color: var(--muted);
}

.swatches {
display: flex;
gap: 4px;
}

.swatch {
flex: 1 1 0;
min-width: 0;
height: 88px;
padding: 10px 12px;
border-radius: 8px;
display: flex;
flex-direction: column;
justify-content: space-between;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 11px;
line-height: 1.3;
}

.stop {
font-weight: 600;
}

.hex {
opacity: 0.75;
letter-spacing: 0.5px;
}
</style>
</head>
<body>
<header>
<h1>Pierre Palette</h1>
</header>
<main>
${sections}
</main>
</body>
</html>
`;

writeFileSync("preview/palette.html", html, "utf8");
console.log("Wrote preview/palette.html");
Loading
Loading