create-eziwiki 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/bin/create-eziwiki.mjs +83 -0
- package/lib/scaffold.mjs +230 -0
- package/lib/scaffold.test.mjs +270 -0
- package/package.json +38 -0
- package/template/README.md +39 -0
- package/template/app/[...slug]/page.tsx +162 -0
- package/template/app/error.tsx +77 -0
- package/template/app/global-error.tsx +76 -0
- package/template/app/globals.css +44 -0
- package/template/app/graph/page.tsx +62 -0
- package/template/app/layout.tsx +164 -0
- package/template/app/not-found.tsx +48 -0
- package/template/app/page.tsx +34 -0
- package/template/app/robots.ts +20 -0
- package/template/app/sitemap.ts +48 -0
- package/template/components/ThemeToggle.tsx +77 -0
- package/template/components/graph/GraphView.tsx +156 -0
- package/template/components/layout/Backlinks.tsx +42 -0
- package/template/components/layout/Breadcrumb.tsx +88 -0
- package/template/components/layout/MobileMenu.tsx +299 -0
- package/template/components/layout/NavigationButtons.tsx +87 -0
- package/template/components/layout/PageLayout.tsx +89 -0
- package/template/components/layout/Sidebar.tsx +376 -0
- package/template/components/layout/TabBar.tsx +312 -0
- package/template/components/layout/TabBarSkeleton.tsx +12 -0
- package/template/components/layout/TabInitializer.tsx +99 -0
- package/template/components/layout/TableOfContents.tsx +138 -0
- package/template/components/markdown/CodeCopy.tsx +65 -0
- package/template/components/markdown/MarkdownContent.tsx +38 -0
- package/template/components/markdown/PageTransition.tsx +56 -0
- package/template/components/providers/UrlMapProvider.tsx +68 -0
- package/template/components/search/SearchDialog.tsx +286 -0
- package/template/components/search/SearchTrigger.tsx +41 -0
- package/template/content/guides/_meta.json +4 -0
- package/template/content/guides/writing.md +82 -0
- package/template/content/intro.md +29 -0
- package/template/eslintignore +7 -0
- package/template/eslintrc.js +40 -0
- package/template/gitignore +40 -0
- package/template/lib/basePath.test.ts +120 -0
- package/template/lib/basePath.ts +108 -0
- package/template/lib/cache.ts +36 -0
- package/template/lib/content/registry.ts +311 -0
- package/template/lib/content/resolver.ts +109 -0
- package/template/lib/graph/build.ts +214 -0
- package/template/lib/graph/layout.test.ts +189 -0
- package/template/lib/graph/layout.ts +247 -0
- package/template/lib/markdown/languages.test.ts +85 -0
- package/template/lib/markdown/languages.ts +103 -0
- package/template/lib/markdown/rehype-plugins.ts +240 -0
- package/template/lib/markdown/remark-wikilink.ts +141 -0
- package/template/lib/markdown/render.ts +175 -0
- package/template/lib/markdown/wikilink.test.ts +91 -0
- package/template/lib/markdown/wikilink.ts +85 -0
- package/template/lib/navigation/auto.ts +227 -0
- package/template/lib/navigation/builder.test.ts +129 -0
- package/template/lib/navigation/builder.ts +122 -0
- package/template/lib/navigation/hash.ts +32 -0
- package/template/lib/navigation/url.test.ts +88 -0
- package/template/lib/navigation/url.ts +108 -0
- package/template/lib/navigation/urlMap.ts +81 -0
- package/template/lib/payload/schema.ts +81 -0
- package/template/lib/payload/types.ts +105 -0
- package/template/lib/payload/validator.ts +56 -0
- package/template/lib/search/build.ts +204 -0
- package/template/lib/search/client.ts +190 -0
- package/template/lib/search/tokenizer.test.ts +60 -0
- package/template/lib/search/tokenizer.ts +83 -0
- package/template/lib/search/types.ts +40 -0
- package/template/lib/site.ts +86 -0
- package/template/lib/store/searchStore.ts +26 -0
- package/template/lib/store/tabStore.ts +313 -0
- package/template/next-env.d.ts +5 -0
- package/template/next.config.js +43 -0
- package/template/package-lock.json +9933 -0
- package/template/package.json +69 -0
- package/template/payload/config.ts +34 -0
- package/template/postcss.config.js +6 -0
- package/template/prettierignore +7 -0
- package/template/prettierrc +8 -0
- package/template/public/favicon.svg +10 -0
- package/template/public/fonts/Pretandard/Pretendard-Bold.woff2 +0 -0
- package/template/public/fonts/Pretandard/Pretendard-Regular.woff2 +0 -0
- package/template/public/fonts/Pretandard/Pretendard-SemiBold.woff2 +0 -0
- package/template/public/fonts/SUITE/SUITE-Bold.woff2 +0 -0
- package/template/public/fonts/SUITE/SUITE-Regular.woff2 +0 -0
- package/template/public/fonts/SUITE/SUITE-SemiBold.woff2 +0 -0
- package/template/public/images/.gitkeep +0 -0
- package/template/scripts/build-search-index.ts +36 -0
- package/template/scripts/check-links.ts +40 -0
- package/template/scripts/show-urls.ts +48 -0
- package/template/scripts/validate-payload.ts +30 -0
- package/template/styles/markdown.css +167 -0
- package/template/styles/theme.css +65 -0
- package/template/tailwind.config.ts +156 -0
- package/template/tsconfig.json +32 -0
- package/template/vitest.config.ts +30 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-wiki",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Documentation site built with eziwiki",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "npm run build:search && next dev",
|
|
8
|
+
"build": "npm run validate:payload && npm run check:links && npm run build:search && next build",
|
|
9
|
+
"export": "npm run build",
|
|
10
|
+
"start": "next start",
|
|
11
|
+
"lint": "eslint . --ext .ts,.tsx",
|
|
12
|
+
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
|
|
13
|
+
"type-check": "tsc --noEmit",
|
|
14
|
+
"test": "vitest --run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"validate:payload": "tsx scripts/validate-payload.ts",
|
|
17
|
+
"show-urls": "tsx scripts/show-urls.ts",
|
|
18
|
+
"build:search": "tsx scripts/build-search-index.ts",
|
|
19
|
+
"check:links": "tsx scripts/check-links.ts",
|
|
20
|
+
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
|
21
|
+
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,css}\""
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@shikijs/rehype": "^4.3.1",
|
|
25
|
+
"ajv": "^8.12.0",
|
|
26
|
+
"ajv-formats": "^3.0.1",
|
|
27
|
+
"gray-matter": "^4.0.3",
|
|
28
|
+
"hast-util-to-string": "^3.0.1",
|
|
29
|
+
"katex": "^0.16.47",
|
|
30
|
+
"lucide-react": "^0.554.0",
|
|
31
|
+
"minisearch": "^7.2.0",
|
|
32
|
+
"next": "^14.2.0",
|
|
33
|
+
"react": "^18.3.0",
|
|
34
|
+
"react-dom": "^18.3.0",
|
|
35
|
+
"rehype-katex": "^7.0.1",
|
|
36
|
+
"rehype-raw": "^7.0.0",
|
|
37
|
+
"rehype-slug": "^6.0.0",
|
|
38
|
+
"rehype-stringify": "^10.0.1",
|
|
39
|
+
"remark-gfm": "^4.0.0",
|
|
40
|
+
"remark-math": "^6.0.0",
|
|
41
|
+
"remark-parse": "^11.0.0",
|
|
42
|
+
"remark-rehype": "^11.1.2",
|
|
43
|
+
"shiki": "^4.3.1",
|
|
44
|
+
"unified": "^11.0.5",
|
|
45
|
+
"unist-util-visit": "^5.1.0",
|
|
46
|
+
"zustand": "^5.0.8"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@tailwindcss/typography": "^0.5.10",
|
|
50
|
+
"@types/hast": "^3.0.5",
|
|
51
|
+
"@types/mdast": "^4.0.4",
|
|
52
|
+
"@types/node": "^20.11.0",
|
|
53
|
+
"@types/react": "^18.3.0",
|
|
54
|
+
"@types/react-dom": "^18.3.0",
|
|
55
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
56
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
57
|
+
"autoprefixer": "^10.4.17",
|
|
58
|
+
"eslint": "^8.56.0",
|
|
59
|
+
"eslint-config-next": "^14.2.0",
|
|
60
|
+
"eslint-config-prettier": "^9.1.0",
|
|
61
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
62
|
+
"postcss": "^8.4.35",
|
|
63
|
+
"prettier": "^3.2.5",
|
|
64
|
+
"tailwindcss": "^3.4.1",
|
|
65
|
+
"tsx": "^4.20.6",
|
|
66
|
+
"typescript": "^5.3.3",
|
|
67
|
+
"vitest": "^4.0.9"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Payload } from '@/lib/payload/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Site configuration.
|
|
5
|
+
*
|
|
6
|
+
* Navigation is optional: pages under `content/` are discovered automatically,
|
|
7
|
+
* ordered by their frontmatter `order` and grouped by folder. Add a
|
|
8
|
+
* `navigation` array here only when you want to override that.
|
|
9
|
+
*/
|
|
10
|
+
export const payload: Payload = {
|
|
11
|
+
global: {
|
|
12
|
+
title: 'My Wiki',
|
|
13
|
+
description: 'A documentation site built with eziwiki',
|
|
14
|
+
favicon: '/favicon.svg',
|
|
15
|
+
baseUrl: 'https://example.com',
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 'path' gives readable, indexable URLs (/guides/writing).
|
|
19
|
+
* 'hash' gives opaque ones (/a3f2e9d1-...), hiding the structure at the
|
|
20
|
+
* cost of SEO and shareable links.
|
|
21
|
+
*/
|
|
22
|
+
urlStrategy: 'path',
|
|
23
|
+
|
|
24
|
+
/** Publish pages found under content/ without listing them below. */
|
|
25
|
+
autoNavigation: true,
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
theme: {
|
|
29
|
+
primary: '#2563eb',
|
|
30
|
+
secondary: '#7c3aed',
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default payload;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
4
|
+
<stop offset="0%" style="stop-color:#6366f1;stop-opacity:1" />
|
|
5
|
+
<stop offset="100%" style="stop-color:#8b5cf6;stop-opacity:1" />
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect width="100" height="100" rx="22" fill="url(#grad)"/>
|
|
9
|
+
<text x="50" y="68" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="48" font-weight="700" fill="#ffffff" text-anchor="middle" letter-spacing="-2">ezi</text>
|
|
10
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generates the client search index into `public/`.
|
|
5
|
+
*
|
|
6
|
+
* Runs before `next dev` and `next build`. Writing a static JSON file — rather
|
|
7
|
+
* than exposing a route — keeps search working on any static host, with no
|
|
8
|
+
* server to query.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs/promises';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { buildSearchIndex } from '../lib/search/build';
|
|
14
|
+
import { SEARCH_INDEX_PATH } from '../lib/search/types';
|
|
15
|
+
|
|
16
|
+
async function main() {
|
|
17
|
+
console.log('🔍 Building search index...');
|
|
18
|
+
|
|
19
|
+
const index = await buildSearchIndex();
|
|
20
|
+
const outputPath = path.join(process.cwd(), 'public', SEARCH_INDEX_PATH);
|
|
21
|
+
const json = JSON.stringify(index);
|
|
22
|
+
|
|
23
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
24
|
+
await fs.writeFile(outputPath, json, 'utf-8');
|
|
25
|
+
|
|
26
|
+
const pages = new Set(index.docs.map((doc) => doc.path)).size;
|
|
27
|
+
const kb = (Buffer.byteLength(json) / 1024).toFixed(1);
|
|
28
|
+
|
|
29
|
+
console.log(`✅ Indexed ${index.docs.length} entries across ${pages} pages (${kb} kB)\n`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
main().catch((error) => {
|
|
33
|
+
console.error('❌ Failed to build search index:');
|
|
34
|
+
console.error(error);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reports links that point at no page.
|
|
5
|
+
*
|
|
6
|
+
* Runs as part of the build and reports rather than fails: a dangling link in
|
|
7
|
+
* one page is not a reason to block a deploy of the other twenty, and content
|
|
8
|
+
* is often written before the page it references exists. Pass `--strict` in CI
|
|
9
|
+
* to make it an error.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { getLinkGraph } from '../lib/graph/build';
|
|
13
|
+
|
|
14
|
+
const strict = process.argv.includes('--strict');
|
|
15
|
+
const { broken, nodes, edges } = getLinkGraph();
|
|
16
|
+
|
|
17
|
+
if (broken.length === 0) {
|
|
18
|
+
console.log(`🔗 Links OK — ${edges.length} links across ${nodes.length} pages\n`);
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log(`\n🔗 ${broken.length} unresolved link${broken.length === 1 ? '' : 's'}:\n`);
|
|
23
|
+
|
|
24
|
+
for (const link of broken) {
|
|
25
|
+
console.log(` content/${link.from}.md`);
|
|
26
|
+
|
|
27
|
+
if (link.reason === 'ambiguous') {
|
|
28
|
+
console.log(` [[${link.target}]] is ambiguous — matches ${link.candidates?.join(', ')}`);
|
|
29
|
+
console.log(' Use the full path to disambiguate.');
|
|
30
|
+
} else {
|
|
31
|
+
console.log(` [[${link.target}]] matches no page`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log();
|
|
36
|
+
|
|
37
|
+
if (strict) {
|
|
38
|
+
console.error('❌ Failing because --strict was passed.\n');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lists the URL of every page the site will build.
|
|
5
|
+
*
|
|
6
|
+
* Useful for finding the address of a hidden page, and for confirming what the
|
|
7
|
+
* configured URL strategy actually produces before deploying.
|
|
8
|
+
*
|
|
9
|
+
* Run with: npm run show-urls
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { payload } from '../payload/config';
|
|
13
|
+
import { getContentRegistry } from '../lib/content/registry';
|
|
14
|
+
import { getUrlMap } from '../lib/navigation/urlMap';
|
|
15
|
+
|
|
16
|
+
const { docs } = getContentRegistry();
|
|
17
|
+
const urlMap = getUrlMap();
|
|
18
|
+
const baseUrl = payload.global.baseUrl || 'http://localhost:3000';
|
|
19
|
+
|
|
20
|
+
console.log(`\n📋 Page URLs (strategy: ${urlMap.strategy})\n`);
|
|
21
|
+
console.log('='.repeat(80));
|
|
22
|
+
|
|
23
|
+
for (const doc of docs) {
|
|
24
|
+
const url = urlMap.toUrl[doc.path];
|
|
25
|
+
if (!url) continue;
|
|
26
|
+
|
|
27
|
+
console.log(`${doc.hidden ? '🔒 [HIDDEN]' : '📄'} ${doc.title}`);
|
|
28
|
+
console.log(` source → content/${doc.path}.md`);
|
|
29
|
+
console.log(` url → ${baseUrl}/${url}`);
|
|
30
|
+
console.log();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const hiddenCount = docs.filter((doc) => doc.hidden).length;
|
|
34
|
+
|
|
35
|
+
console.log('='.repeat(80));
|
|
36
|
+
console.log(`\nTotal pages: ${docs.length}`);
|
|
37
|
+
console.log(`Hidden pages: ${hiddenCount}`);
|
|
38
|
+
|
|
39
|
+
if (hiddenCount > 0) {
|
|
40
|
+
console.log('\n💡 Hidden pages are absent from navigation but reachable at the URL above.');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (urlMap.strategy === 'hash') {
|
|
44
|
+
console.log(
|
|
45
|
+
"\n💡 URLs are hashed. Set global.urlStrategy to 'path' in payload/config.ts for\n" +
|
|
46
|
+
' readable, indexable URLs.',
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build-time payload validation script
|
|
5
|
+
* This script validates the payload configuration against the JSON Schema
|
|
6
|
+
* and exits with an error code if validation fails.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { validatePayload } from '../lib/payload/validator';
|
|
10
|
+
import payload from '../payload/config';
|
|
11
|
+
|
|
12
|
+
function main() {
|
|
13
|
+
console.log('🔍 Validating payload configuration...\n');
|
|
14
|
+
|
|
15
|
+
const validation = validatePayload(payload);
|
|
16
|
+
|
|
17
|
+
if (!validation.valid) {
|
|
18
|
+
console.error('❌ Payload validation failed:\n');
|
|
19
|
+
validation.errors?.forEach((err) => {
|
|
20
|
+
console.error(` • ${err}`);
|
|
21
|
+
});
|
|
22
|
+
console.error('\nPlease fix the errors in payload/config.ts and try again.\n');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log('✅ Payload validation passed!\n');
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
main();
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Styles for build-time rendered Markdown.
|
|
3
|
+
*
|
|
4
|
+
* The document body is plain HTML produced by the unified pipeline, so these
|
|
5
|
+
* rules replace the per-element Tailwind classes the old runtime renderer
|
|
6
|
+
* applied. Typography comes from @tailwindcss/typography via `.prose`; what
|
|
7
|
+
* follows covers the parts it does not know about — Shiki output, code-block
|
|
8
|
+
* chrome, and anchor targets.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/* ---------------------------------------------------------------- code ---- */
|
|
12
|
+
|
|
13
|
+
.ezw-code {
|
|
14
|
+
margin: 1.5rem 0;
|
|
15
|
+
border-radius: 0.5rem;
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
border: 1px solid var(--color-border);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.ezw-code__bar {
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
justify-content: space-between;
|
|
24
|
+
padding: 0.5rem 1rem;
|
|
25
|
+
background: var(--color-code-bg);
|
|
26
|
+
border-bottom: 1px solid var(--color-border);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.ezw-code__lang {
|
|
30
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
31
|
+
font-size: 0.75rem;
|
|
32
|
+
text-transform: uppercase;
|
|
33
|
+
letter-spacing: 0.03em;
|
|
34
|
+
color: var(--color-text-muted);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.ezw-code__copy {
|
|
38
|
+
font-size: 0.75rem;
|
|
39
|
+
padding: 0.25rem 0.5rem;
|
|
40
|
+
border-radius: 0.25rem;
|
|
41
|
+
color: var(--color-text-muted);
|
|
42
|
+
background: transparent;
|
|
43
|
+
transition:
|
|
44
|
+
color 0.15s ease,
|
|
45
|
+
background-color 0.15s ease;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.ezw-code__copy:hover {
|
|
49
|
+
color: var(--color-text);
|
|
50
|
+
background: var(--color-sidebar-hover);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.ezw-code__copy[data-copied] {
|
|
54
|
+
color: #16a34a;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Shiki emits its own background and padding; strip the prose defaults so the
|
|
58
|
+
two do not fight over the same box. */
|
|
59
|
+
.ezw-code pre {
|
|
60
|
+
margin: 0;
|
|
61
|
+
padding: 1rem;
|
|
62
|
+
border-radius: 0;
|
|
63
|
+
overflow-x: auto;
|
|
64
|
+
font-size: 0.875rem;
|
|
65
|
+
line-height: 1.6;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.ezw-code pre code {
|
|
69
|
+
background: transparent;
|
|
70
|
+
padding: 0;
|
|
71
|
+
font-size: inherit;
|
|
72
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Typography adds quotation marks around inline code; they read as noise in
|
|
76
|
+
technical prose. */
|
|
77
|
+
.ezw-prose :where(code):not(:where(pre code))::before,
|
|
78
|
+
.ezw-prose :where(code):not(:where(pre code))::after {
|
|
79
|
+
content: none;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.ezw-prose :where(code):not(:where(pre code)) {
|
|
83
|
+
background: var(--color-code-bg);
|
|
84
|
+
color: var(--color-code-text);
|
|
85
|
+
padding: 0.15em 0.4em;
|
|
86
|
+
border-radius: 0.25rem;
|
|
87
|
+
font-weight: 400;
|
|
88
|
+
font-size: 0.9em;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* --------------------------------------------------------- shiki themes ---- */
|
|
92
|
+
|
|
93
|
+
/* Shiki is configured with `defaultColor: false`, so it writes both themes as
|
|
94
|
+
CSS variables on each token and neither is applied until one is selected
|
|
95
|
+
here. This is what makes theme switching instant and flash-free. */
|
|
96
|
+
.shiki,
|
|
97
|
+
.shiki span {
|
|
98
|
+
color: var(--shiki-light);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.shiki {
|
|
102
|
+
background-color: var(--shiki-light-bg);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
html.dark .shiki,
|
|
106
|
+
html.dark .shiki span {
|
|
107
|
+
color: var(--shiki-dark);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
html.dark .shiki {
|
|
111
|
+
background-color: var(--shiki-dark-bg);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* ----------------------------------------------------------- wikilinks ---- */
|
|
115
|
+
|
|
116
|
+
.ezw-wikilink {
|
|
117
|
+
text-decoration: none;
|
|
118
|
+
border-bottom: 1px solid color-mix(in srgb, var(--color-primary) 40%, transparent);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.ezw-wikilink:hover {
|
|
122
|
+
border-bottom-color: var(--color-primary);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* An unresolved link is styled as visibly broken rather than left to look like
|
|
126
|
+
working text — a dangling reference is a content bug worth surfacing. */
|
|
127
|
+
.ezw-broken-link {
|
|
128
|
+
font-style: normal;
|
|
129
|
+
color: #b91c1c;
|
|
130
|
+
border-bottom: 1px dashed currentColor;
|
|
131
|
+
cursor: help;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
html.dark .ezw-broken-link {
|
|
135
|
+
color: #f87171;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* ------------------------------------------------------------- anchors ---- */
|
|
139
|
+
|
|
140
|
+
/* The header is sticky, so an anchored heading would otherwise land underneath
|
|
141
|
+
it. */
|
|
142
|
+
.ezw-prose :where(h1, h2, h3, h4, h5, h6)[id] {
|
|
143
|
+
scroll-margin-top: 6rem;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* -------------------------------------------------------------- images ---- */
|
|
147
|
+
|
|
148
|
+
.ezw-img {
|
|
149
|
+
max-width: 100%;
|
|
150
|
+
height: auto;
|
|
151
|
+
border-radius: 0.5rem;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* --------------------------------------------------------------- misc ----- */
|
|
155
|
+
|
|
156
|
+
.ezw-prose table {
|
|
157
|
+
display: block;
|
|
158
|
+
overflow-x: auto;
|
|
159
|
+
white-space: nowrap;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@media (min-width: 640px) {
|
|
163
|
+
.ezw-prose table {
|
|
164
|
+
display: table;
|
|
165
|
+
white-space: normal;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme CSS Custom Properties
|
|
3
|
+
* Notion/Obsidian-inspired color palette with light and dark mode support
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
:root {
|
|
7
|
+
/* Primary colors - Blue accent for links and interactive elements */
|
|
8
|
+
--color-primary: #2563eb;
|
|
9
|
+
--color-primary-hover: #1d4ed8;
|
|
10
|
+
|
|
11
|
+
/* Secondary colors - Purple accent for highlights */
|
|
12
|
+
--color-secondary: #7c3aed;
|
|
13
|
+
--color-secondary-hover: #6d28d9;
|
|
14
|
+
|
|
15
|
+
/* Background colors */
|
|
16
|
+
--color-background: #ffffff;
|
|
17
|
+
|
|
18
|
+
/* Text colors */
|
|
19
|
+
--color-text: #1f2937;
|
|
20
|
+
--color-text-muted: #6b7280;
|
|
21
|
+
|
|
22
|
+
/* Sidebar colors */
|
|
23
|
+
--color-sidebar-bg: #f9fafb;
|
|
24
|
+
--color-sidebar-text: #374151;
|
|
25
|
+
--color-sidebar-hover: #f3f4f6;
|
|
26
|
+
--color-sidebar-active: #e5e7eb;
|
|
27
|
+
|
|
28
|
+
/* Code block colors */
|
|
29
|
+
--color-code-bg: #f3f4f6;
|
|
30
|
+
--color-code-text: #1f2937;
|
|
31
|
+
|
|
32
|
+
/* Border colors */
|
|
33
|
+
--color-border: #e5e7eb;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Dark mode color scheme - using .dark class */
|
|
37
|
+
.dark {
|
|
38
|
+
/* Primary colors - Lighter blue for better contrast */
|
|
39
|
+
--color-primary: #3b82f6;
|
|
40
|
+
--color-primary-hover: #60a5fa;
|
|
41
|
+
|
|
42
|
+
/* Secondary colors - Lighter purple */
|
|
43
|
+
--color-secondary: #8b5cf6;
|
|
44
|
+
--color-secondary-hover: #a78bfa;
|
|
45
|
+
|
|
46
|
+
/* Background colors */
|
|
47
|
+
--color-background: #111827;
|
|
48
|
+
|
|
49
|
+
/* Text colors */
|
|
50
|
+
--color-text: #f9fafb;
|
|
51
|
+
--color-text-muted: #9ca3af;
|
|
52
|
+
|
|
53
|
+
/* Sidebar colors */
|
|
54
|
+
--color-sidebar-bg: #1f2937;
|
|
55
|
+
--color-sidebar-text: #e5e7eb;
|
|
56
|
+
--color-sidebar-hover: #374151;
|
|
57
|
+
--color-sidebar-active: #4b5563;
|
|
58
|
+
|
|
59
|
+
/* Code block colors */
|
|
60
|
+
--color-code-bg: #374151;
|
|
61
|
+
--color-code-text: #f9fafb;
|
|
62
|
+
|
|
63
|
+
/* Border colors */
|
|
64
|
+
--color-border: #374151;
|
|
65
|
+
}
|