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
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,35 @@ https://github.com/owner/repo/blob/main/src/index.ts#L1-L10
</p>
```

## ダークモード

ダークテーマ対応サイト向けに、GitHub Dark Default テーマ準拠の別バンドルを用意しています。

```html
<script src="https://cdn.jsdelivr.net/gh/hirano00o/git-code-embed@v0/dist/git-code-embed-dark.min.js"></script>
```

### はてなブログ(ダークテーマ)での使い方

管理画面の **設定 → 詳細設定** の `<head>` 要素欄に、ダーク版の URL を貼り付けます。

```html
<script src="https://cdn.jsdelivr.net/gh/hirano00o/git-code-embed@v0/dist/git-code-embed-dark.min.js" defer></script>
```

### ビルド

```bash
# ライトモード(デフォルト) → dist/git-code-embed.min.js
npm run build

# ダークモード → dist/git-code-embed-dark.min.js
npm run build:dark

# 両方同時にビルド
npm run build:all
```

## 既知の制限

### スラッシュを含むブランチ名(例: `feature/my-branch`)は未対応
Expand Down
180 changes: 180 additions & 0 deletions dist/git-code-embed-dark.min.js

Large diffs are not rendered by default.

85 changes: 44 additions & 41 deletions dist/git-code-embed.min.js

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions esbuild.config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import * as esbuild from "esbuild";

const dark = process.argv.includes("--dark");
const theme = dark ? "dark" : "light";
const outfile = dark
? "dist/git-code-embed-dark.min.js"
: "dist/git-code-embed.min.js";

esbuild
.build({
entryPoints: ["src/main.ts"],
bundle: true,
minify: true,
format: "iife",
outfile: "dist/git-code-embed.min.js",
outfile,
target: ["es2020", "chrome80", "firefox78", "safari14"],
platform: "browser",
define: {
__THEME__: JSON.stringify(theme),
},
})
.then(() => {
console.log("Build complete: dist/git-code-embed.min.js");
console.log(`Build complete: ${outfile}`);
})
.catch((err: unknown) => {
console.error(err);
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"main": "dist/git-code-embed.min.js",
"scripts": {
"build": "tsx esbuild.config.ts",
"build:dark": "tsx esbuild.config.ts --dark",
"build:all": "tsx esbuild.config.ts && tsx esbuild.config.ts --dark",
"test": "vitest run",
"test:watch": "vitest"
},
Expand Down
126 changes: 91 additions & 35 deletions src/styles.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
/**
* CSS injected into the page as a single <style> tag.
* All class names use the `gce-` prefix to avoid collisions with host pages.
*
* Build-time constant __THEME__ selects the color palette.
* Dead code elimination removes the unused palette from the bundle.
*/
export const CSS = `

declare const __THEME__: string;

export const LIGHT_COLORS = `
:root {
--gce-line-height: 1.5em;
--gce-font-size: 13px;
--gce-font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
--gce-ui-font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
--gce-bg: #f6f8fa;
--gce-border: #d0d7de;
--gce-header-bg: #f6f8fa;
Expand All @@ -18,6 +20,90 @@ export const CSS = `
--gce-code-bg: #ffffff;
}

.gce-container .hljs-keyword,
.gce-container .hljs-selector-tag,
.gce-container .hljs-built_in,
.gce-container .hljs-name,
.gce-container .hljs-tag { color: #cf222e; }

.gce-container .hljs-string,
.gce-container .hljs-attr,
.gce-container .hljs-symbol,
.gce-container .hljs-bullet,
.gce-container .hljs-addition { color: #0a3069; }

.gce-container .hljs-title,
.gce-container .hljs-section,
.gce-container .hljs-type,
.gce-container .hljs-function { color: #8250df; }

.gce-container .hljs-variable,
.gce-container .hljs-template-variable { color: #953800; }

.gce-container .hljs-comment,
.gce-container .hljs-quote,
.gce-container .hljs-deletion,
.gce-container .hljs-meta { color: #6e7781; font-style: italic; }

.gce-container .hljs-number,
.gce-container .hljs-regexp,
.gce-container .hljs-literal,
.gce-container .hljs-doctag { color: #0550ae; }
`;

export const DARK_COLORS = `
:root {
--gce-bg: #161b22;
--gce-border: #30363d;
--gce-header-bg: #161b22;
--gce-header-text: #e6edf3;
--gce-header-link: #58a6ff;
--gce-lineno-color: #8b949e;
--gce-lineno-bg: #161b22;
--gce-code-bg: #0d1117;
}

.gce-container .hljs-keyword,
.gce-container .hljs-selector-tag,
.gce-container .hljs-built_in,
.gce-container .hljs-name,
.gce-container .hljs-tag { color: #ff7b72; }

.gce-container .hljs-string,
.gce-container .hljs-attr,
.gce-container .hljs-symbol,
.gce-container .hljs-bullet,
.gce-container .hljs-addition { color: #a5d6ff; }

.gce-container .hljs-title,
.gce-container .hljs-section,
.gce-container .hljs-type,
.gce-container .hljs-function { color: #d2a8ff; }

.gce-container .hljs-variable,
.gce-container .hljs-template-variable { color: #ffa657; }

.gce-container .hljs-comment,
.gce-container .hljs-quote,
.gce-container .hljs-deletion,
.gce-container .hljs-meta { color: #8b949e; font-style: italic; }

.gce-container .hljs-number,
.gce-container .hljs-regexp,
.gce-container .hljs-literal,
.gce-container .hljs-doctag { color: #79c0ff; }
`;

const THEME_COLORS = __THEME__ === "dark" ? DARK_COLORS : LIGHT_COLORS;

export const CSS = `
:root {
--gce-line-height: 1.5em;
--gce-font-size: 13px;
--gce-font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
--gce-ui-font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
}
${THEME_COLORS}
.gce-container,
.gce-container *,
.gce-container *::before,
Expand Down Expand Up @@ -139,36 +225,6 @@ export const CSS = `
text-decoration: none;
}

.gce-container .hljs-keyword,
.gce-container .hljs-selector-tag,
.gce-container .hljs-built_in,
.gce-container .hljs-name,
.gce-container .hljs-tag { color: #cf222e; }

.gce-container .hljs-string,
.gce-container .hljs-attr,
.gce-container .hljs-symbol,
.gce-container .hljs-bullet,
.gce-container .hljs-addition { color: #0a3069; }

.gce-container .hljs-title,
.gce-container .hljs-section,
.gce-container .hljs-type,
.gce-container .hljs-function { color: #8250df; }

.gce-container .hljs-variable,
.gce-container .hljs-template-variable { color: #953800; }

.gce-container .hljs-comment,
.gce-container .hljs-quote,
.gce-container .hljs-deletion,
.gce-container .hljs-meta { color: #6e7781; font-style: italic; }

.gce-container .hljs-number,
.gce-container .hljs-regexp,
.gce-container .hljs-literal,
.gce-container .hljs-doctag { color: #0550ae; }

.gce-container .hljs-emphasis { font-style: italic; }

.gce-container .hljs-strong { font-weight: bold; }
Expand Down
114 changes: 114 additions & 0 deletions test/styles.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { describe, expect, it } from "vitest";
import { CSS, DARK_COLORS, LIGHT_COLORS } from "../src/styles";

describe("LIGHT_COLORS", () => {
it("ライトテーマの背景色を含む", () => {
expect(LIGHT_COLORS).toContain("--gce-bg: #f6f8fa");
expect(LIGHT_COLORS).toContain("--gce-code-bg: #ffffff");
});

it("ライトテーマのボーダー色を含む", () => {
expect(LIGHT_COLORS).toContain("--gce-border: #d0d7de");
});

it("ライトテーマのヘッダー色を含む", () => {
expect(LIGHT_COLORS).toContain("--gce-header-text: #24292f");
expect(LIGHT_COLORS).toContain("--gce-header-link: #0969da");
});

it("ライトテーマの行番号色を含む", () => {
expect(LIGHT_COLORS).toContain("--gce-lineno-color: #6e7781");
expect(LIGHT_COLORS).toContain("--gce-lineno-bg: #f6f8fa");
});

it("ライトテーマの hljs キーワード色を含む", () => {
expect(LIGHT_COLORS).toContain("color: #cf222e");
});

it("ライトテーマの hljs 文字列色を含む", () => {
expect(LIGHT_COLORS).toContain("color: #0a3069");
});

it("ライトテーマの hljs タイトル色を含む", () => {
expect(LIGHT_COLORS).toContain("color: #8250df");
});

it("ライトテーマの hljs 変数色を含む", () => {
expect(LIGHT_COLORS).toContain("color: #953800");
});

it("ライトテーマの hljs コメント色を含む", () => {
expect(LIGHT_COLORS).toContain("color: #6e7781");
});

it("ライトテーマの hljs 数値色を含む", () => {
expect(LIGHT_COLORS).toContain("color: #0550ae");
});
});

describe("DARK_COLORS", () => {
it("ダークテーマの背景色を含む", () => {
expect(DARK_COLORS).toContain("--gce-bg: #161b22");
expect(DARK_COLORS).toContain("--gce-code-bg: #0d1117");
});

it("ダークテーマのボーダー色を含む", () => {
expect(DARK_COLORS).toContain("--gce-border: #30363d");
});

it("ダークテーマのヘッダー色を含む", () => {
expect(DARK_COLORS).toContain("--gce-header-text: #e6edf3");
expect(DARK_COLORS).toContain("--gce-header-link: #58a6ff");
});

it("ダークテーマの行番号色を含む", () => {
expect(DARK_COLORS).toContain("--gce-lineno-color: #8b949e");
expect(DARK_COLORS).toContain("--gce-lineno-bg: #161b22");
});

it("ダークテーマの hljs キーワード色を含む", () => {
expect(DARK_COLORS).toContain("color: #ff7b72");
});

it("ダークテーマの hljs 文字列色を含む", () => {
expect(DARK_COLORS).toContain("color: #a5d6ff");
});

it("ダークテーマの hljs タイトル色を含む", () => {
expect(DARK_COLORS).toContain("color: #d2a8ff");
});

it("ダークテーマの hljs 変数色を含む", () => {
expect(DARK_COLORS).toContain("color: #ffa657");
});

it("ダークテーマの hljs コメント色を含む", () => {
expect(DARK_COLORS).toContain("color: #8b949e");
});

it("ダークテーマの hljs 数値色を含む", () => {
expect(DARK_COLORS).toContain("color: #79c0ff");
});
});

describe("CSS (テーマ: light)", () => {
it("テーマ非依存の font-family 変数を含む", () => {
expect(CSS).toContain("--gce-font-family:");
expect(CSS).toContain("--gce-ui-font-family:");
});

it("テーマ非依存の line-height と font-size を含む", () => {
expect(CSS).toContain("--gce-line-height: 1.5em");
expect(CSS).toContain("--gce-font-size: 13px");
});

it("vitest の define により __THEME__ が light に解決され、ライトカラーを含む", () => {
expect(CSS).toContain("--gce-bg: #f6f8fa");
expect(CSS).not.toContain("--gce-bg: #161b22");
});

it("構造 CSS(box-sizing, flex 等)を含む", () => {
expect(CSS).toContain("box-sizing: border-box");
expect(CSS).toContain("display: flex");
});
});
3 changes: 3 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ export default defineConfig({
environment: "jsdom",
include: ["test/**/*.test.ts"],
},
define: {
__THEME__: JSON.stringify("light"),
},
});