@repraze/kernelkit 0.1.0-alpha.3 → 0.1.0-alpha.4
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 +17 -0
- package/dist/components/search-dialog.d.ts +6 -1
- package/dist/components/search-dialog.d.ts.map +1 -1
- package/dist/components/search-dialog.js +13 -1
- package/dist/components/search-dialog.js.map +1 -1
- package/dist/docs/doc-kit.d.ts +72 -0
- package/dist/docs/doc-kit.d.ts.map +1 -0
- package/dist/docs/doc-kit.js +45 -0
- package/dist/docs/doc-kit.js.map +1 -0
- package/dist/docs/doc-page.d.ts +27 -0
- package/dist/docs/doc-page.d.ts.map +1 -0
- package/dist/docs/doc-page.js +33 -0
- package/dist/docs/doc-page.js.map +1 -0
- package/dist/docs/docs-index.d.ts +60 -0
- package/dist/docs/docs-index.d.ts.map +1 -0
- package/dist/docs/docs-index.js +45 -0
- package/dist/docs/docs-index.js.map +1 -0
- package/dist/docs/docs-nav.d.ts +13 -0
- package/dist/docs/docs-nav.d.ts.map +1 -0
- package/dist/docs/docs-nav.js +8 -0
- package/dist/docs/docs-nav.js.map +1 -0
- package/dist/docs/docs-search.d.ts +19 -0
- package/dist/docs/docs-search.d.ts.map +1 -0
- package/dist/docs/docs-search.js +33 -0
- package/dist/docs/docs-search.js.map +1 -0
- package/dist/docs/index.d.ts +7 -0
- package/dist/docs/index.d.ts.map +1 -0
- package/dist/docs/index.js +7 -0
- package/dist/docs/index.js.map +1 -0
- package/dist/docs/review-matrix.d.ts +25 -0
- package/dist/docs/review-matrix.d.ts.map +1 -0
- package/dist/docs/review-matrix.js +31 -0
- package/dist/docs/review-matrix.js.map +1 -0
- package/dist/hooks/use-theme-palette.d.ts +8 -0
- package/dist/hooks/use-theme-palette.d.ts.map +1 -0
- package/dist/hooks/use-theme-palette.js +24 -0
- package/dist/hooks/use-theme-palette.js.map +1 -0
- package/dist/provider/theme-provider.d.ts +10 -1
- package/dist/provider/theme-provider.d.ts.map +1 -1
- package/dist/provider/theme-provider.js +23 -8
- package/dist/provider/theme-provider.js.map +1 -1
- package/dist/site/brand.d.ts +20 -0
- package/dist/site/brand.d.ts.map +1 -0
- package/dist/site/brand.js +17 -0
- package/dist/site/brand.js.map +1 -0
- package/dist/site/github-icon.d.ts +4 -0
- package/dist/site/github-icon.d.ts.map +1 -0
- package/dist/site/github-icon.js +6 -0
- package/dist/site/github-icon.js.map +1 -0
- package/dist/site/index.d.ts +6 -0
- package/dist/site/index.d.ts.map +1 -0
- package/dist/site/index.js +6 -0
- package/dist/site/index.js.map +1 -0
- package/dist/site/suite-footer.d.ts +24 -0
- package/dist/site/suite-footer.d.ts.map +1 -0
- package/dist/site/suite-footer.js +19 -0
- package/dist/site/suite-footer.js.map +1 -0
- package/dist/site/suite-header.d.ts +40 -0
- package/dist/site/suite-header.d.ts.map +1 -0
- package/dist/site/suite-header.js +19 -0
- package/dist/site/suite-header.js.map +1 -0
- package/dist/site/use-page-title.d.ts +3 -0
- package/dist/site/use-page-title.d.ts.map +1 -0
- package/dist/site/use-page-title.js +8 -0
- package/dist/site/use-page-title.js.map +1 -0
- package/dist/vite/index.d.ts +65 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +242 -0
- package/dist/vite/index.js.map +1 -0
- package/package.json +39 -3
- package/src/components/search-dialog.tsx +14 -1
- package/src/docs/doc-kit.tsx +176 -0
- package/src/docs/doc-page.tsx +126 -0
- package/src/docs/docs-index.ts +108 -0
- package/src/docs/docs-nav.tsx +47 -0
- package/src/docs/docs-search.tsx +88 -0
- package/src/docs/index.ts +6 -0
- package/src/docs/review-matrix.tsx +67 -0
- package/src/hooks/use-theme-palette.ts +25 -0
- package/src/provider/theme-provider.tsx +34 -9
- package/src/site/brand.tsx +41 -0
- package/src/site/github-icon.tsx +10 -0
- package/src/site/index.ts +5 -0
- package/src/site/suite-footer.tsx +68 -0
- package/src/site/suite-header.tsx +114 -0
- package/src/site/use-page-title.ts +8 -0
- package/src/styles/kernelkit.css +2 -0
- package/src/styles/site.css +15 -0
- package/src/vite/client.d.ts +11 -0
- package/src/vite/index.ts +298 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/**
|
|
3
|
+
* Vite plugins shared by the suite's sites (Node-side: no React, nothing here reaches the browser).
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* import {docsMetaPlugin, faviconPlugin, spaFallback, suiteSourceConditions, themeScriptPlugin} from "@repraze/kernelkit/vite";
|
|
7
|
+
* ```
|
|
8
|
+
*
|
|
9
|
+
* `vite` is only imported for its types; `typescript` is loaded lazily by docsMetaPlugin / extractFunction.
|
|
10
|
+
*/
|
|
11
|
+
import { lstatSync, readdirSync, readFileSync } from "node:fs";
|
|
12
|
+
import { copyFile } from "node:fs/promises";
|
|
13
|
+
import { createRequire } from "node:module";
|
|
14
|
+
import { dirname, join, resolve } from "node:path";
|
|
15
|
+
import { themeScript } from "../provider/theme.js";
|
|
16
|
+
/**
|
|
17
|
+
* Inline the kit's pre-paint theme script into index.html, so the stored mode, contrast, accent and radius
|
|
18
|
+
* apply before first paint. Replaces the marker comment, or goes first in <head> when there is none.
|
|
19
|
+
*/
|
|
20
|
+
export function themeScriptPlugin({ storageKey, defaults, marker = "<!-- kernelkit:theme-script -->", prelude, }) {
|
|
21
|
+
const source = [prelude, themeScript({ storageKey, defaults })].filter(Boolean).join("\n");
|
|
22
|
+
return {
|
|
23
|
+
name: "kernelkit-theme-script",
|
|
24
|
+
transformIndexHtml: (html) => html.includes(marker)
|
|
25
|
+
? html.replace(marker, `<script>${source}</script>`)
|
|
26
|
+
: { html, tags: [{ tag: "script", children: source, injectTo: "head-prepend" }] },
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// ─── favicon ────────────────────────────────────────────────────────────────────────────────────
|
|
30
|
+
/**
|
|
31
|
+
* Serve (dev) and emit (build) `favicon.svg` from the brand mark's SVG, so the icon never drifts from the
|
|
32
|
+
* logo. Link it with `href="%BASE_URL%favicon.svg"` so it works under a Pages base path.
|
|
33
|
+
*/
|
|
34
|
+
export function faviconPlugin(svg, fileName = "favicon.svg") {
|
|
35
|
+
const source = typeof svg === "function" ? svg() : svg;
|
|
36
|
+
return {
|
|
37
|
+
name: "kernelkit-favicon",
|
|
38
|
+
configureServer(server) {
|
|
39
|
+
server.middlewares.use((req, res, next) => {
|
|
40
|
+
if (req.url?.split("?")[0]?.endsWith(`/${fileName}`)) {
|
|
41
|
+
res.setHeader("Content-Type", "image/svg+xml");
|
|
42
|
+
res.end(source);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
next();
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
generateBundle() {
|
|
49
|
+
this.emitFile({ type: "asset", fileName, source });
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// ─── SPA fallback ───────────────────────────────────────────────────────────────────────────────
|
|
54
|
+
/** GitHub Pages has no SPA rewrites: copy index.html to 404.html so deep links load the app. */
|
|
55
|
+
export function spaFallback() {
|
|
56
|
+
return {
|
|
57
|
+
name: "kernelkit-spa-fallback",
|
|
58
|
+
apply: "build",
|
|
59
|
+
async writeBundle({ dir }) {
|
|
60
|
+
if (dir) {
|
|
61
|
+
await copyFile(join(dir, "index.html"), join(dir, "404.html"));
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// ─── linked suite packages ──────────────────────────────────────────────────────────────────────
|
|
67
|
+
/** Is `node_modules/<name>` a symlink (npm link, a workspace), looking up from `root`? */
|
|
68
|
+
function isLinked(name, root) {
|
|
69
|
+
for (let dir = resolve(root);; dir = dirname(dir)) {
|
|
70
|
+
try {
|
|
71
|
+
return lstatSync(join(dir, "node_modules", name)).isSymbolicLink();
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// not installed here: look in the parent
|
|
75
|
+
}
|
|
76
|
+
if (dirname(dir) === dir) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const DEDUPE = ["react", "react-dom", "@base-ui/react", "cmdk", "lucide-react"];
|
|
82
|
+
/**
|
|
83
|
+
* For each suite package (`@repraze/kernelkit`…) that is symlinked into node_modules (`npm link`, a
|
|
84
|
+
* workspace): serve it from its TypeScript sources (its `@repraze/source` exports condition), unbundled,
|
|
85
|
+
* so edits there hot-reload; dedupe React-context packages, since a linked package sits outside this tree.
|
|
86
|
+
* Installed from npm: nothing changes, its built dist is used and pre-bundled like any dependency.
|
|
87
|
+
*/
|
|
88
|
+
export function suiteSourceConditions(packages, options = {}) {
|
|
89
|
+
const { dedupe = DEDUPE, condition = "@repraze/source" } = options;
|
|
90
|
+
return {
|
|
91
|
+
name: "kernelkit-suite-source-conditions",
|
|
92
|
+
enforce: "pre",
|
|
93
|
+
config(config) {
|
|
94
|
+
const linked = packages.filter((name) => isLinked(name, config.root ?? process.cwd()));
|
|
95
|
+
if (!linked.length) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
resolve: { conditions: [condition, ...DEFAULT_CLIENT_CONDITIONS], dedupe },
|
|
100
|
+
optimizeDeps: { exclude: linked },
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** Vite's `defaultClientConditions`, inlined so this module needs no runtime import of vite. */
|
|
106
|
+
const DEFAULT_CLIENT_CONDITIONS = ["module", "browser", "development|production"];
|
|
107
|
+
// ─── docs manifest and demo sources ─────────────────────────────────────────────────────────────
|
|
108
|
+
const require = createRequire(import.meta.url);
|
|
109
|
+
let typescript;
|
|
110
|
+
/** TypeScript, loaded on first use (the sites already have it): only docs sites pay for it. */
|
|
111
|
+
const ts = () => (typescript ??= require("typescript"));
|
|
112
|
+
/** Static value of a string/number expression: literals, no-substitution templates, "a" + "b". */
|
|
113
|
+
function literal(node) {
|
|
114
|
+
const t = ts();
|
|
115
|
+
if (t.isStringLiteral(node) || t.isNoSubstitutionTemplateLiteral(node)) {
|
|
116
|
+
return node.text;
|
|
117
|
+
}
|
|
118
|
+
if (t.isNumericLiteral(node)) {
|
|
119
|
+
return Number(node.text);
|
|
120
|
+
}
|
|
121
|
+
if (t.isParenthesizedExpression(node)) {
|
|
122
|
+
return literal(node.expression);
|
|
123
|
+
}
|
|
124
|
+
if (t.isBinaryExpression(node) && node.operatorToken.kind === t.SyntaxKind.PlusToken) {
|
|
125
|
+
const a = literal(node.left);
|
|
126
|
+
const b = literal(node.right);
|
|
127
|
+
return a !== undefined && b !== undefined ? `${a}${b}` : undefined;
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
/** A docs page's manifest entry, read from its `defineDoc({...})` call without running it. */
|
|
132
|
+
export function readDocMeta(file, slug, source = readFileSync(file, "utf8")) {
|
|
133
|
+
const t = ts();
|
|
134
|
+
const sf = t.createSourceFile(file, source, t.ScriptTarget.Latest, true);
|
|
135
|
+
let meta;
|
|
136
|
+
const visit = (node) => {
|
|
137
|
+
if (!meta &&
|
|
138
|
+
t.isCallExpression(node) &&
|
|
139
|
+
t.isIdentifier(node.expression) &&
|
|
140
|
+
node.expression.text === "defineDoc" &&
|
|
141
|
+
node.arguments[0] &&
|
|
142
|
+
t.isObjectLiteralExpression(node.arguments[0])) {
|
|
143
|
+
const props = {};
|
|
144
|
+
for (const p of node.arguments[0].properties) {
|
|
145
|
+
if (t.isPropertyAssignment(p) && t.isIdentifier(p.name)) {
|
|
146
|
+
props[p.name.text] = literal(p.initializer);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (typeof props.title !== "string" || typeof props.category !== "string") {
|
|
150
|
+
throw new Error(`${file}: defineDoc needs literal "title" and "category" (read at build time)`);
|
|
151
|
+
}
|
|
152
|
+
meta = {
|
|
153
|
+
slug,
|
|
154
|
+
title: props.title,
|
|
155
|
+
category: props.category,
|
|
156
|
+
description: typeof props.description === "string" ? props.description : "",
|
|
157
|
+
...(typeof props.order === "number" ? { order: props.order } : {}),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
t.forEachChild(node, visit);
|
|
161
|
+
};
|
|
162
|
+
visit(sf);
|
|
163
|
+
if (!meta) {
|
|
164
|
+
throw new Error(`${file}: no defineDoc({...}) call found`);
|
|
165
|
+
}
|
|
166
|
+
return meta;
|
|
167
|
+
}
|
|
168
|
+
/** Each top-level function or const of a file, by name, as written (without `export `). */
|
|
169
|
+
export function extractFunctions(source, fileName = "doc.tsx") {
|
|
170
|
+
const t = ts();
|
|
171
|
+
const sf = t.createSourceFile(fileName, source, t.ScriptTarget.Latest, true, t.ScriptKind.TSX);
|
|
172
|
+
const out = {};
|
|
173
|
+
const text = (node) => source.slice(node.getStart(sf), node.getEnd()).replace(/^export /, "");
|
|
174
|
+
for (const statement of sf.statements) {
|
|
175
|
+
if (t.isFunctionDeclaration(statement) && statement.name) {
|
|
176
|
+
out[statement.name.text] = text(statement);
|
|
177
|
+
}
|
|
178
|
+
else if (t.isVariableStatement(statement)) {
|
|
179
|
+
for (const decl of statement.declarationList.declarations) {
|
|
180
|
+
if (t.isIdentifier(decl.name)) {
|
|
181
|
+
out[decl.name.text] = text(statement);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* A demo's own source: its declaration from `export function Name` (or `export const Name`) to its
|
|
190
|
+
* closing brace, parsed with TypeScript, so code after it (a `Notes()` helper, constants) stays out.
|
|
191
|
+
*/
|
|
192
|
+
export function extractFunction(source, name) {
|
|
193
|
+
return extractFunctions(source)[name] ?? "";
|
|
194
|
+
}
|
|
195
|
+
const META_ID = "virtual:docs-meta";
|
|
196
|
+
const META_RESOLVED = `\0${META_ID}`;
|
|
197
|
+
const SOURCES_QUERY = "demo-sources";
|
|
198
|
+
/**
|
|
199
|
+
* The docs build step, for `@repraze/kernelkit/docs`:
|
|
200
|
+
* - `virtual:docs-meta`: title, category, description and order of every page in `docsDir`, read
|
|
201
|
+
* statically from its `defineDoc({...})` call, so the nav and search don't pull every page's demos into
|
|
202
|
+
* the main bundle;
|
|
203
|
+
* - `<page>.tsx?demo-sources`: `{functions}`, each demo's own source for its Code tab (see extractFunction).
|
|
204
|
+
*
|
|
205
|
+
* Types: add `@repraze/kernelkit/vite/client` to the site's tsconfig `types`.
|
|
206
|
+
*/
|
|
207
|
+
export function docsMetaPlugin(docsDir) {
|
|
208
|
+
const dir = resolve(docsDir);
|
|
209
|
+
return {
|
|
210
|
+
name: "kernelkit-docs-meta",
|
|
211
|
+
enforce: "pre",
|
|
212
|
+
resolveId: (id) => (id === META_ID ? META_RESOLVED : undefined),
|
|
213
|
+
load(id) {
|
|
214
|
+
if (id === META_RESOLVED) {
|
|
215
|
+
const files = readdirSync(dir).filter((f) => f.endsWith(".tsx"));
|
|
216
|
+
const metas = files.map((f) => {
|
|
217
|
+
const file = join(dir, f);
|
|
218
|
+
this.addWatchFile(file);
|
|
219
|
+
return readDocMeta(file, f.replace(/\.tsx$/, ""));
|
|
220
|
+
});
|
|
221
|
+
return `export default ${JSON.stringify(metas)};`;
|
|
222
|
+
}
|
|
223
|
+
const [file = "", query = ""] = id.split("?");
|
|
224
|
+
if (query.split("&").includes(SOURCES_QUERY)) {
|
|
225
|
+
this.addWatchFile(file);
|
|
226
|
+
const functions = extractFunctions(readFileSync(file, "utf8"), file);
|
|
227
|
+
return { code: `export default ${JSON.stringify({ functions })};`, moduleType: "js" };
|
|
228
|
+
}
|
|
229
|
+
return undefined;
|
|
230
|
+
},
|
|
231
|
+
// a docs page added, removed or retitled → refresh the manifest
|
|
232
|
+
handleHotUpdate({ file, server }) {
|
|
233
|
+
if (file.startsWith(dir)) {
|
|
234
|
+
const mod = server.moduleGraph.getModuleById(META_RESOLVED);
|
|
235
|
+
if (mod) {
|
|
236
|
+
server.moduleGraph.invalidateModule(mod);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@repraze/kernelkit",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.4",
|
|
4
4
|
"description": "Modern, techy React components: every shadcn component on Base UI, Tailwind v4 and cva, themed by TermiColor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -56,7 +56,26 @@
|
|
|
56
56
|
"types": "./dist/provider/theme.d.ts",
|
|
57
57
|
"default": "./dist/provider/theme.js"
|
|
58
58
|
},
|
|
59
|
+
"./docs": {
|
|
60
|
+
"@repraze/source": "./src/docs/index.ts",
|
|
61
|
+
"types": "./dist/docs/index.d.ts",
|
|
62
|
+
"default": "./dist/docs/index.js"
|
|
63
|
+
},
|
|
64
|
+
"./site": {
|
|
65
|
+
"@repraze/source": "./src/site/index.ts",
|
|
66
|
+
"types": "./dist/site/index.d.ts",
|
|
67
|
+
"default": "./dist/site/index.js"
|
|
68
|
+
},
|
|
69
|
+
"./vite": {
|
|
70
|
+
"@repraze/source": "./src/vite/index.ts",
|
|
71
|
+
"types": "./dist/vite/index.d.ts",
|
|
72
|
+
"default": "./dist/vite/index.js"
|
|
73
|
+
},
|
|
74
|
+
"./vite/client": {
|
|
75
|
+
"types": "./src/vite/client.d.ts"
|
|
76
|
+
},
|
|
59
77
|
"./styles.css": "./src/styles/kernelkit.css",
|
|
78
|
+
"./site.css": "./src/styles/site.css",
|
|
60
79
|
"./package.json": "./package.json"
|
|
61
80
|
},
|
|
62
81
|
"files": [
|
|
@@ -83,7 +102,11 @@
|
|
|
83
102
|
"input-otp": "^1.5.0",
|
|
84
103
|
"react-resizable-panels": "^4.19.1",
|
|
85
104
|
"@tanstack/react-virtual": "^3.14.26",
|
|
86
|
-
"@shadcn/react": "^0.3.1"
|
|
105
|
+
"@shadcn/react": "^0.3.1",
|
|
106
|
+
"@fontsource-variable/geist": "^5.2.0",
|
|
107
|
+
"@fontsource-variable/jetbrains-mono": "^5.2.0",
|
|
108
|
+
"typescript": ">=5.0",
|
|
109
|
+
"vite": ">=7.0"
|
|
87
110
|
},
|
|
88
111
|
"peerDependenciesMeta": {
|
|
89
112
|
"recharts": {
|
|
@@ -115,6 +138,18 @@
|
|
|
115
138
|
},
|
|
116
139
|
"@shadcn/react": {
|
|
117
140
|
"optional": true
|
|
141
|
+
},
|
|
142
|
+
"@fontsource-variable/geist": {
|
|
143
|
+
"optional": true
|
|
144
|
+
},
|
|
145
|
+
"@fontsource-variable/jetbrains-mono": {
|
|
146
|
+
"optional": true
|
|
147
|
+
},
|
|
148
|
+
"typescript": {
|
|
149
|
+
"optional": true
|
|
150
|
+
},
|
|
151
|
+
"vite": {
|
|
152
|
+
"optional": true
|
|
118
153
|
}
|
|
119
154
|
},
|
|
120
155
|
"dependencies": {
|
|
@@ -141,6 +176,7 @@
|
|
|
141
176
|
"input-otp": "^1.5.0",
|
|
142
177
|
"react-resizable-panels": "^4.19.1",
|
|
143
178
|
"@tanstack/react-virtual": "^3.14.26",
|
|
144
|
-
"@shadcn/react": "^0.3.1"
|
|
179
|
+
"@shadcn/react": "^0.3.1",
|
|
180
|
+
"vite": "^8.3.6"
|
|
145
181
|
}
|
|
146
182
|
}
|
|
@@ -84,4 +84,17 @@ function SearchFooter({className, ...props}: React.ComponentProps<"div">) {
|
|
|
84
84
|
);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
/**
|
|
88
|
+
* The suite's search filter for `<Command filter={substringFilter}>`: a plain substring match (prefix
|
|
89
|
+
* matches rank first). cmdk's default fuzzy match finds "t…a…b" in nearly every title.
|
|
90
|
+
*/
|
|
91
|
+
function substringFilter(value: string, search: string, keywords?: string[]) {
|
|
92
|
+
const haystack = [value, ...(keywords ?? [])].join(" ").toLowerCase();
|
|
93
|
+
const needle = search.trim().toLowerCase();
|
|
94
|
+
if (haystack.startsWith(needle)) {
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
return haystack.includes(needle) ? 0.5 : 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export {SearchDialog, SearchFooter, SearchItem, substringFilter};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CodeBlock,
|
|
5
|
+
CodeBlockActions,
|
|
6
|
+
CodeBlockContent,
|
|
7
|
+
CodeBlockCopyButton,
|
|
8
|
+
CodeBlockHeader,
|
|
9
|
+
CodeBlockTitle,
|
|
10
|
+
} from "@repraze/kernelkit/components/code-block";
|
|
11
|
+
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@repraze/kernelkit/components/table";
|
|
12
|
+
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@repraze/kernelkit/components/tabs";
|
|
13
|
+
import {useHighlightedCode} from "@repraze/kernelkit/hooks/use-highlighted-code";
|
|
14
|
+
import {cn} from "cn";
|
|
15
|
+
import type * as React from "react";
|
|
16
|
+
|
|
17
|
+
export interface PropRow {
|
|
18
|
+
name: string;
|
|
19
|
+
type: string;
|
|
20
|
+
default?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DemoDef {
|
|
25
|
+
title: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
component: React.ComponentType;
|
|
28
|
+
/** Preview frame: "center" (default), "start" (top-left), "full" (no padding), "tall". */
|
|
29
|
+
layout?: "center" | "start" | "full" | "tall";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A docs page: one `defineDoc({...})` per file in the site's docs folder (read statically by docsMetaPlugin). */
|
|
33
|
+
export interface DocDef<Category extends string = string> {
|
|
34
|
+
title: string;
|
|
35
|
+
category: Category;
|
|
36
|
+
description: string;
|
|
37
|
+
/** A link badge after the category in the page header: the primitive it wraps, the source file it documents. */
|
|
38
|
+
source?: {label: string; href: string};
|
|
39
|
+
demos: DemoDef[];
|
|
40
|
+
props?: {component: string; rows: PropRow[]}[];
|
|
41
|
+
/** Freeform content after the demos. */
|
|
42
|
+
notes?: React.ComponentType;
|
|
43
|
+
/** The notes' own h2 sections (`id` on each), listed in the table of contents after the demos. */
|
|
44
|
+
sections?: {id: string; title: string}[];
|
|
45
|
+
/** Sort order inside the category (lower first, default 50). */
|
|
46
|
+
order?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface DocEntry<Category extends string = string> extends DocDef<Category> {
|
|
50
|
+
slug: string;
|
|
51
|
+
/** Source of each demo's own function, by demo index. */
|
|
52
|
+
demoSources: string[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Declare a docs page. The site narrows the categories once:
|
|
57
|
+
*
|
|
58
|
+
* ```ts
|
|
59
|
+
* export const CATEGORIES = ["Foundations", "Components"] as const;
|
|
60
|
+
* export const defineDoc = (doc: DocDef<(typeof CATEGORIES)[number]>) => doc;
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function defineDoc<Category extends string>(doc: DocDef<Category>): DocDef<Category> {
|
|
64
|
+
return doc;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function slugify(text: string) {
|
|
68
|
+
return text
|
|
69
|
+
.toLowerCase()
|
|
70
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
71
|
+
.replace(/^-|-$/g, "");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** A highlighted, copyable code sample (the kit's CodeBlock), titled with its language by default. */
|
|
75
|
+
export function Code({
|
|
76
|
+
code,
|
|
77
|
+
language = "tsx",
|
|
78
|
+
title,
|
|
79
|
+
className,
|
|
80
|
+
}: {
|
|
81
|
+
code: string;
|
|
82
|
+
language?: string;
|
|
83
|
+
title?: string;
|
|
84
|
+
className?: string;
|
|
85
|
+
}) {
|
|
86
|
+
const lines = useHighlightedCode(code, language);
|
|
87
|
+
return (
|
|
88
|
+
<CodeBlock className={className}>
|
|
89
|
+
<CodeBlockHeader>
|
|
90
|
+
<CodeBlockTitle>{title ?? language}</CodeBlockTitle>
|
|
91
|
+
<CodeBlockActions>
|
|
92
|
+
<CodeBlockCopyButton value={code} />
|
|
93
|
+
</CodeBlockActions>
|
|
94
|
+
</CodeBlockHeader>
|
|
95
|
+
<CodeBlockContent className="max-h-[480px] text-[13px]">
|
|
96
|
+
{lines.map((line, i) => (
|
|
97
|
+
<span key={i} className="block min-h-lh">
|
|
98
|
+
{line}
|
|
99
|
+
</span>
|
|
100
|
+
))}
|
|
101
|
+
</CodeBlockContent>
|
|
102
|
+
</CodeBlock>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const layoutClass: Record<NonNullable<DemoDef["layout"]>, string> = {
|
|
107
|
+
center: "flex min-h-56 items-center justify-center p-8",
|
|
108
|
+
start: "flex min-h-56 items-start justify-start p-8",
|
|
109
|
+
full: "overflow-hidden",
|
|
110
|
+
tall: "flex min-h-[28rem] items-center justify-center p-8",
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/** One demo: its title and description, then Preview and Code tabs. The section's id is the slugified title. */
|
|
114
|
+
export function Demo({demo, code}: {demo: DemoDef; code: string}) {
|
|
115
|
+
const Component = demo.component;
|
|
116
|
+
return (
|
|
117
|
+
<section className="flex scroll-mt-24 flex-col gap-3" id={slugify(demo.title)}>
|
|
118
|
+
<div className="flex flex-col gap-1">
|
|
119
|
+
<h3 className="font-mono text-sm font-medium">{demo.title}</h3>
|
|
120
|
+
{demo.description ? <p className="text-sm text-muted-foreground">{demo.description}</p> : null}
|
|
121
|
+
</div>
|
|
122
|
+
<Tabs defaultValue="preview">
|
|
123
|
+
<TabsList>
|
|
124
|
+
<TabsTrigger value="preview">Preview</TabsTrigger>
|
|
125
|
+
<TabsTrigger value="code">Code</TabsTrigger>
|
|
126
|
+
</TabsList>
|
|
127
|
+
<TabsContent value="preview">
|
|
128
|
+
<div
|
|
129
|
+
data-slot="demo-frame"
|
|
130
|
+
className={cn("rounded-xl border bg-card", layoutClass[demo.layout ?? "center"])}
|
|
131
|
+
>
|
|
132
|
+
<Component />
|
|
133
|
+
</div>
|
|
134
|
+
</TabsContent>
|
|
135
|
+
<TabsContent value="code">
|
|
136
|
+
<Code code={code} />
|
|
137
|
+
</TabsContent>
|
|
138
|
+
</Tabs>
|
|
139
|
+
</section>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** A component's props: name, type, default and description. */
|
|
144
|
+
export function PropsTable({component, rows}: {component: string; rows: PropRow[]}) {
|
|
145
|
+
return (
|
|
146
|
+
<section className="flex flex-col gap-3">
|
|
147
|
+
<h3 className="font-mono text-sm font-medium">{component}</h3>
|
|
148
|
+
<div className="overflow-hidden rounded-xl border bg-card">
|
|
149
|
+
<Table>
|
|
150
|
+
<TableHeader>
|
|
151
|
+
<TableRow>
|
|
152
|
+
<TableHead>Prop</TableHead>
|
|
153
|
+
<TableHead>Type</TableHead>
|
|
154
|
+
<TableHead>Default</TableHead>
|
|
155
|
+
<TableHead>Description</TableHead>
|
|
156
|
+
</TableRow>
|
|
157
|
+
</TableHeader>
|
|
158
|
+
<TableBody>
|
|
159
|
+
{rows.map((row) => (
|
|
160
|
+
<TableRow key={row.name}>
|
|
161
|
+
<TableCell className="font-mono text-xs text-primary-text">{row.name}</TableCell>
|
|
162
|
+
<TableCell className="font-mono text-xs whitespace-normal">{row.type}</TableCell>
|
|
163
|
+
<TableCell className="font-mono text-xs text-muted-foreground">
|
|
164
|
+
{row.default ?? "-"}
|
|
165
|
+
</TableCell>
|
|
166
|
+
<TableCell className="text-sm whitespace-normal text-muted-foreground">
|
|
167
|
+
{row.description}
|
|
168
|
+
</TableCell>
|
|
169
|
+
</TableRow>
|
|
170
|
+
))}
|
|
171
|
+
</TableBody>
|
|
172
|
+
</Table>
|
|
173
|
+
</div>
|
|
174
|
+
</section>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {Badge} from "@repraze/kernelkit/components/badge";
|
|
4
|
+
import {
|
|
5
|
+
PageHeader,
|
|
6
|
+
PageHeaderDescription,
|
|
7
|
+
PageHeaderEyebrow,
|
|
8
|
+
PageHeaderTitle,
|
|
9
|
+
} from "@repraze/kernelkit/components/page-header";
|
|
10
|
+
import {Pager, PagerLink} from "@repraze/kernelkit/components/pager";
|
|
11
|
+
import {SiteAside, SiteLayout, SiteMain, SiteSidebar} from "@repraze/kernelkit/components/site-layout";
|
|
12
|
+
import {Toc, TocItem, TocList, TocTitle, useTocActive} from "@repraze/kernelkit/components/toc";
|
|
13
|
+
import {ExternalLinkIcon} from "lucide-react";
|
|
14
|
+
import type * as React from "react";
|
|
15
|
+
|
|
16
|
+
import {Demo, type DocEntry, PropsTable, slugify} from "./doc-kit";
|
|
17
|
+
import type {DocsIndex} from "./docs-index";
|
|
18
|
+
|
|
19
|
+
/** A router link to a docs page, used as a `render` element: `(slug) => <Link to="/docs/$slug" params={{slug}} />`. */
|
|
20
|
+
export type DocLinkFor = (slug: string) => React.ReactElement;
|
|
21
|
+
|
|
22
|
+
/** The table of contents of a page: its demos, the notes' sections, then the API. */
|
|
23
|
+
export function docToc(doc: DocEntry): {id: string; title: string}[] {
|
|
24
|
+
return [
|
|
25
|
+
...doc.demos.map((demo) => ({id: slugify(demo.title), title: demo.title})),
|
|
26
|
+
...(doc.sections ?? []),
|
|
27
|
+
...(doc.props?.length ? [{id: "api", title: "API"}] : []),
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface DocPageProps<Category extends string = string> {
|
|
32
|
+
/** The loaded page (`index.loadDoc(slug)`, usually from the route's loader). */
|
|
33
|
+
doc: DocEntry<Category>;
|
|
34
|
+
index: DocsIndex<Category>;
|
|
35
|
+
linkFor: DocLinkFor;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A docs page in the suite's layout (GUIDELINES §7b): PageHeader (category, source badge, title,
|
|
40
|
+
* description), the demos, the notes, the API tables and the Pager in SiteMain; the Toc in SiteAside.
|
|
41
|
+
*/
|
|
42
|
+
export function DocPage<Category extends string>({doc, index, linkFor}: DocPageProps<Category>) {
|
|
43
|
+
const {prev, next} = index.neighbours(doc.slug);
|
|
44
|
+
const Notes = doc.notes;
|
|
45
|
+
const sections = docToc(doc);
|
|
46
|
+
const active = useTocActive(sections.map((s) => s.id));
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<SiteMain>
|
|
51
|
+
<PageHeader>
|
|
52
|
+
<PageHeaderEyebrow>
|
|
53
|
+
<Badge variant="outline">{doc.category}</Badge>
|
|
54
|
+
{doc.source ? (
|
|
55
|
+
<Badge
|
|
56
|
+
variant="secondary"
|
|
57
|
+
className="font-mono"
|
|
58
|
+
render={<a href={doc.source.href} target="_blank" rel="noreferrer" />}
|
|
59
|
+
>
|
|
60
|
+
{doc.source.label} <ExternalLinkIcon />
|
|
61
|
+
</Badge>
|
|
62
|
+
) : null}
|
|
63
|
+
</PageHeaderEyebrow>
|
|
64
|
+
<PageHeaderTitle>{doc.title}</PageHeaderTitle>
|
|
65
|
+
<PageHeaderDescription>{doc.description}</PageHeaderDescription>
|
|
66
|
+
</PageHeader>
|
|
67
|
+
|
|
68
|
+
{doc.demos.map((demo, i) => (
|
|
69
|
+
<Demo key={demo.title} demo={demo} code={doc.demoSources[i] ?? ""} />
|
|
70
|
+
))}
|
|
71
|
+
|
|
72
|
+
{Notes ? (
|
|
73
|
+
<div className="flex flex-col gap-4">
|
|
74
|
+
<Notes />
|
|
75
|
+
</div>
|
|
76
|
+
) : null}
|
|
77
|
+
|
|
78
|
+
{doc.props?.length ? (
|
|
79
|
+
<section id="api" className="flex scroll-mt-24 flex-col gap-6">
|
|
80
|
+
<h2 className="font-heading text-xl font-semibold">API</h2>
|
|
81
|
+
{doc.props.map((p) => (
|
|
82
|
+
<PropsTable key={p.component} {...p} />
|
|
83
|
+
))}
|
|
84
|
+
</section>
|
|
85
|
+
) : null}
|
|
86
|
+
|
|
87
|
+
<Pager>
|
|
88
|
+
{prev ? (
|
|
89
|
+
<PagerLink direction="previous" render={linkFor(prev.slug)}>
|
|
90
|
+
{prev.title}
|
|
91
|
+
</PagerLink>
|
|
92
|
+
) : null}
|
|
93
|
+
{next ? (
|
|
94
|
+
<PagerLink direction="next" render={linkFor(next.slug)}>
|
|
95
|
+
{next.title}
|
|
96
|
+
</PagerLink>
|
|
97
|
+
) : null}
|
|
98
|
+
</Pager>
|
|
99
|
+
</SiteMain>
|
|
100
|
+
{sections.length > 1 ? (
|
|
101
|
+
<SiteAside>
|
|
102
|
+
<Toc>
|
|
103
|
+
<TocTitle>On this page</TocTitle>
|
|
104
|
+
<TocList>
|
|
105
|
+
{sections.map((s) => (
|
|
106
|
+
<TocItem key={s.id} href={`#${s.id}`} active={s.id === active}>
|
|
107
|
+
{s.title}
|
|
108
|
+
</TocItem>
|
|
109
|
+
))}
|
|
110
|
+
</TocList>
|
|
111
|
+
</Toc>
|
|
112
|
+
</SiteAside>
|
|
113
|
+
) : null}
|
|
114
|
+
</>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** The suite's docs layout: SiteLayout with the docs nav in a sticky SiteSidebar, then the page (which adds its Toc). */
|
|
119
|
+
export function DocsLayout({nav, children}: {nav: React.ReactNode; children: React.ReactNode}) {
|
|
120
|
+
return (
|
|
121
|
+
<SiteLayout>
|
|
122
|
+
<SiteSidebar>{nav}</SiteSidebar>
|
|
123
|
+
{children}
|
|
124
|
+
</SiteLayout>
|
|
125
|
+
);
|
|
126
|
+
}
|