@marlinjai/clearify 1.7.3 → 1.9.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 +3 -3
- package/dist/node/{chunk-O3SK2A4H.js → chunk-77VHQV7W.js} +2 -2
- package/dist/node/{chunk-B3LCE7QA.js → chunk-GGUDZD4K.js} +3 -3
- package/dist/node/{chunk-BXEU64R6.js → chunk-IF6GI4WU.js} +12 -3
- package/dist/node/{chunk-3ZYZ5G6Q.js → chunk-PQMKRFBQ.js} +68 -2
- package/dist/node/{chunk-MWUXPDQK.js → chunk-QIAMD2Q6.js} +24 -0
- package/dist/node/{chunk-6PUBRHS7.js → chunk-QNC5QRIO.js} +55 -18
- package/dist/node/cli/index.js +3 -3
- package/dist/node/core/config.d.ts +2 -1
- package/dist/node/core/config.js +5 -3
- package/dist/node/core/navigation.js +1 -1
- package/dist/node/node/build.js +4 -4
- package/dist/node/node/check.js +3 -3
- package/dist/node/node/index.js +6 -6
- package/dist/node/node/init.d.ts +1 -0
- package/dist/node/node/init.js +1 -1
- package/dist/node/types/index.d.ts +19 -1
- package/dist/node/vite-plugin/index.js +3 -3
- package/package.json +2 -1
- package/src/theme/Layout.tsx +1 -0
- package/src/theme/MDXComponents.tsx +6 -0
- package/src/theme/Sidebar.tsx +34 -2
- package/src/theme/components/ProjectCard.tsx +134 -0
- package/src/theme/components/ProjectGrid.tsx +142 -0
- package/src/theme/components/StatusBadge.tsx +70 -0
- package/src/theme/components/index.ts +3 -0
package/README.md
CHANGED
|
@@ -20,19 +20,19 @@ An open-source documentation site generator. Turn markdown into beautiful docs.
|
|
|
20
20
|
Install Clearify as a dev dependency:
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
|
|
23
|
+
pnpm add -D @marlinjai/clearify
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
Scaffold your project:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
|
|
29
|
+
pnpm exec clearify init
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Start the dev server:
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
|
|
35
|
+
pnpm exec clearify dev
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
Your docs are live at `http://localhost:4747`.
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
loadUserConfig,
|
|
3
3
|
resolveConfig,
|
|
4
4
|
resolveSections
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-PQMKRFBQ.js";
|
|
6
6
|
import {
|
|
7
7
|
scanDocs
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-QNC5QRIO.js";
|
|
9
9
|
|
|
10
10
|
// src/node/check.ts
|
|
11
11
|
import { resolve } from "path";
|
|
@@ -8,15 +8,15 @@ import {
|
|
|
8
8
|
} from "./chunk-SCZZB7OE.js";
|
|
9
9
|
import {
|
|
10
10
|
clearifyPlugin
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-IF6GI4WU.js";
|
|
12
12
|
import {
|
|
13
13
|
loadUserConfig,
|
|
14
14
|
resolveConfig,
|
|
15
15
|
resolveSections
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-PQMKRFBQ.js";
|
|
17
17
|
import {
|
|
18
18
|
buildSectionData
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-QNC5QRIO.js";
|
|
20
20
|
|
|
21
21
|
// src/node/build.ts
|
|
22
22
|
import { build as viteBuild } from "vite";
|
|
@@ -4,11 +4,12 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
loadUserConfig,
|
|
6
6
|
resolveConfig,
|
|
7
|
-
resolveSections
|
|
8
|
-
|
|
7
|
+
resolveSections,
|
|
8
|
+
scanHubProjects
|
|
9
|
+
} from "./chunk-PQMKRFBQ.js";
|
|
9
10
|
import {
|
|
10
11
|
buildSectionData
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-QNC5QRIO.js";
|
|
12
13
|
|
|
13
14
|
// src/vite-plugin/index.ts
|
|
14
15
|
import { resolve } from "path";
|
|
@@ -26,6 +27,8 @@ var VIRTUAL_MERMAID_SVGS = "virtual:clearify/mermaid-svgs";
|
|
|
26
27
|
var RESOLVED_VIRTUAL_MERMAID_SVGS = "\0" + VIRTUAL_MERMAID_SVGS;
|
|
27
28
|
var VIRTUAL_OPENAPI_SPEC = "virtual:clearify/openapi-spec";
|
|
28
29
|
var RESOLVED_VIRTUAL_OPENAPI_SPEC = "\0" + VIRTUAL_OPENAPI_SPEC;
|
|
30
|
+
var VIRTUAL_HUB = "virtual:clearify/hub";
|
|
31
|
+
var RESOLVED_VIRTUAL_HUB = "\0" + VIRTUAL_HUB;
|
|
29
32
|
function findFirstNavPath(items) {
|
|
30
33
|
for (const item of items) {
|
|
31
34
|
if (item.path) return item.path;
|
|
@@ -218,6 +221,7 @@ ${routeEntries.join(",\n")}
|
|
|
218
221
|
userRoot = options.root ?? process.cwd();
|
|
219
222
|
const userConfig = await loadUserConfig(userRoot);
|
|
220
223
|
config = resolveConfig(userConfig, userRoot);
|
|
224
|
+
config = await scanHubProjects(config, userRoot);
|
|
221
225
|
const allSections = resolveSections(config, userRoot);
|
|
222
226
|
const isDev = resolvedViteConfig.command === "serve";
|
|
223
227
|
visibleSections = isDev ? allSections : allSections.filter((s) => !s.draft);
|
|
@@ -230,6 +234,7 @@ ${routeEntries.join(",\n")}
|
|
|
230
234
|
if (id === VIRTUAL_SEARCH_INDEX) return RESOLVED_VIRTUAL_SEARCH_INDEX;
|
|
231
235
|
if (id === VIRTUAL_MERMAID_SVGS) return RESOLVED_VIRTUAL_MERMAID_SVGS;
|
|
232
236
|
if (id === VIRTUAL_OPENAPI_SPEC) return RESOLVED_VIRTUAL_OPENAPI_SPEC;
|
|
237
|
+
if (id === VIRTUAL_HUB) return RESOLVED_VIRTUAL_HUB;
|
|
233
238
|
return null;
|
|
234
239
|
},
|
|
235
240
|
async load(id) {
|
|
@@ -251,6 +256,10 @@ ${routeEntries.join(",\n")}
|
|
|
251
256
|
if (id === RESOLVED_VIRTUAL_OPENAPI_SPEC) {
|
|
252
257
|
return await loadOpenAPISpec();
|
|
253
258
|
}
|
|
259
|
+
if (id === RESOLVED_VIRTUAL_HUB) {
|
|
260
|
+
const hubData = config.hub ?? null;
|
|
261
|
+
return `export default ${JSON.stringify(hubData)};`;
|
|
262
|
+
}
|
|
254
263
|
return null;
|
|
255
264
|
},
|
|
256
265
|
configureServer(server) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/core/config.ts
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { resolve, basename } from "path";
|
|
3
|
+
import { resolve, basename, dirname } from "path";
|
|
4
4
|
import { pathToFileURL } from "url";
|
|
5
5
|
import { existsSync, readFileSync, rmSync } from "fs";
|
|
6
6
|
import { tmpdir } from "os";
|
|
@@ -31,6 +31,31 @@ var OpenAPIConfigSchema = z.object({
|
|
|
31
31
|
basePath: z.string().default("/api"),
|
|
32
32
|
generatePages: z.boolean().default(true)
|
|
33
33
|
}).optional();
|
|
34
|
+
var HubProjectSchema = z.object({
|
|
35
|
+
name: z.string(),
|
|
36
|
+
description: z.string(),
|
|
37
|
+
href: z.string().optional(),
|
|
38
|
+
repo: z.string().optional(),
|
|
39
|
+
status: z.enum(["active", "beta", "planned", "deprecated"]).default("active"),
|
|
40
|
+
icon: z.string().optional(),
|
|
41
|
+
tags: z.array(z.string()).optional(),
|
|
42
|
+
group: z.string().optional()
|
|
43
|
+
});
|
|
44
|
+
var HubProjectPartialSchema = z.object({
|
|
45
|
+
description: z.string(),
|
|
46
|
+
href: z.string().optional(),
|
|
47
|
+
repo: z.string().optional(),
|
|
48
|
+
status: z.enum(["active", "beta", "planned", "deprecated"]).default("active"),
|
|
49
|
+
icon: z.string().optional(),
|
|
50
|
+
tags: z.array(z.string()).optional(),
|
|
51
|
+
group: z.string().optional(),
|
|
52
|
+
hubUrl: z.string().optional(),
|
|
53
|
+
hubName: z.string().optional()
|
|
54
|
+
}).optional();
|
|
55
|
+
var HubConfigSchema = z.object({
|
|
56
|
+
projects: z.array(HubProjectSchema).default([]),
|
|
57
|
+
scan: z.string().optional()
|
|
58
|
+
}).optional();
|
|
34
59
|
var ClearifyConfigSchema = z.object({
|
|
35
60
|
name: z.string().default("Documentation"),
|
|
36
61
|
docsDir: z.string().default("./docs"),
|
|
@@ -51,6 +76,8 @@ var ClearifyConfigSchema = z.object({
|
|
|
51
76
|
mermaid: MermaidConfigSchema,
|
|
52
77
|
openapi: OpenAPIConfigSchema,
|
|
53
78
|
links: z.record(z.string(), z.string()).optional(),
|
|
79
|
+
hub: HubConfigSchema,
|
|
80
|
+
hubProject: HubProjectPartialSchema,
|
|
54
81
|
customCss: z.string().optional(),
|
|
55
82
|
headTags: z.array(z.string()).optional()
|
|
56
83
|
});
|
|
@@ -194,10 +221,49 @@ function resolveSections(config, root) {
|
|
|
194
221
|
}
|
|
195
222
|
];
|
|
196
223
|
}
|
|
224
|
+
async function scanHubProjects(config, root) {
|
|
225
|
+
if (!config.hub?.scan) return config;
|
|
226
|
+
const { globbySync } = await import("globby");
|
|
227
|
+
const pattern = config.hub.scan;
|
|
228
|
+
const configPaths = globbySync(pattern, { cwd: root, absolute: true });
|
|
229
|
+
const scannedProjects = [];
|
|
230
|
+
for (const configPath of configPaths) {
|
|
231
|
+
try {
|
|
232
|
+
const childConfig = await loadUserConfig(dirname(configPath));
|
|
233
|
+
if (!childConfig.hubProject) continue;
|
|
234
|
+
const name = childConfig.name ?? basename(dirname(configPath));
|
|
235
|
+
const hp = childConfig.hubProject;
|
|
236
|
+
scannedProjects.push({
|
|
237
|
+
name,
|
|
238
|
+
description: hp.description,
|
|
239
|
+
href: hp.href ?? childConfig.siteUrl,
|
|
240
|
+
repo: hp.repo,
|
|
241
|
+
status: hp.status,
|
|
242
|
+
icon: hp.icon,
|
|
243
|
+
tags: hp.tags,
|
|
244
|
+
group: hp.group
|
|
245
|
+
});
|
|
246
|
+
} catch {
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const manualNames = new Set((config.hub.projects ?? []).map((p) => p.name));
|
|
250
|
+
const merged = [
|
|
251
|
+
...scannedProjects.filter((p) => !manualNames.has(p.name)),
|
|
252
|
+
...config.hub.projects ?? []
|
|
253
|
+
];
|
|
254
|
+
return {
|
|
255
|
+
...config,
|
|
256
|
+
hub: {
|
|
257
|
+
...config.hub,
|
|
258
|
+
projects: merged
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
197
262
|
|
|
198
263
|
export {
|
|
199
264
|
defaultConfig,
|
|
200
265
|
loadUserConfig,
|
|
201
266
|
resolveConfig,
|
|
202
|
-
resolveSections
|
|
267
|
+
resolveSections,
|
|
268
|
+
scanHubProjects
|
|
203
269
|
};
|
|
@@ -210,6 +210,30 @@ export default defineConfig({
|
|
|
210
210
|
} else {
|
|
211
211
|
console.log(" clearify.config.ts already exists, skipping");
|
|
212
212
|
}
|
|
213
|
+
if (!options.noClaudeRules) {
|
|
214
|
+
const claudeRulesDir = resolve(cwd, ".claude/rules");
|
|
215
|
+
const claudeRulesPath = resolve(claudeRulesDir, "clearify-docs.md");
|
|
216
|
+
if (!existsSync(claudeRulesPath)) {
|
|
217
|
+
mkdirSync(claudeRulesDir, { recursive: true });
|
|
218
|
+
const hasInternal = !options.noInternal;
|
|
219
|
+
const rulesContent = `# Clearify Documentation Rules
|
|
220
|
+
|
|
221
|
+
When making changes to this project, keep the documentation in sync:
|
|
222
|
+
|
|
223
|
+
1. **Changelog** \u2014 After any user-facing change, add a bullet under \`## [Unreleased]\` in \`CHANGELOG.md\` using [Keep a Changelog](https://keepachangelog.com/) format (\`Added\`, \`Changed\`, \`Fixed\`, \`Removed\`).
|
|
224
|
+
|
|
225
|
+
2. **Docs pages** \u2014 When a feature or behavior changes, update the relevant page in \`docs/public/\`. If a new feature has no page, create one.
|
|
226
|
+
|
|
227
|
+
3. **Config docs** \u2014 When config options are added or changed, update \`docs/public/configuration.md\` with the new option, type, default, and description.
|
|
228
|
+
${hasInternal ? `
|
|
229
|
+
4. **Architecture decisions** \u2014 For significant design decisions, add an entry to \`docs/internal/\` explaining the context, decision, and trade-offs.
|
|
230
|
+
` : ""}`;
|
|
231
|
+
writeFileSync(claudeRulesPath, rulesContent);
|
|
232
|
+
console.log(" Created .claude/rules/clearify-docs.md");
|
|
233
|
+
} else {
|
|
234
|
+
console.log(" .claude/rules/clearify-docs.md already exists, skipping");
|
|
235
|
+
}
|
|
236
|
+
}
|
|
213
237
|
console.log(`
|
|
214
238
|
Done! Next steps:
|
|
215
239
|
|
|
@@ -47,9 +47,20 @@ function scanDocs(docsDir, exclude = [], basePath = "/") {
|
|
|
47
47
|
}
|
|
48
48
|
function buildNavigation(docs, basePath = "/") {
|
|
49
49
|
const rootDir = basePath === "/" ? "/" : basePath.replace(/\/$/, "");
|
|
50
|
-
const
|
|
50
|
+
const indexPages = /* @__PURE__ */ new Map();
|
|
51
|
+
const nonIndexDocs = [];
|
|
51
52
|
for (const doc of docs) {
|
|
52
53
|
if (doc.routePath === rootDir || doc.routePath === "/") continue;
|
|
54
|
+
const parentDir = dirname(doc.routePath);
|
|
55
|
+
const fileName = basename(doc.filePath, extname(doc.filePath));
|
|
56
|
+
if (fileName === "index") {
|
|
57
|
+
indexPages.set(doc.routePath, doc);
|
|
58
|
+
} else {
|
|
59
|
+
nonIndexDocs.push(doc);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const groups = /* @__PURE__ */ new Map();
|
|
63
|
+
for (const doc of nonIndexDocs) {
|
|
53
64
|
const dir = dirname(doc.routePath);
|
|
54
65
|
if (!groups.has(dir)) groups.set(dir, []);
|
|
55
66
|
groups.get(dir).push(doc);
|
|
@@ -62,25 +73,51 @@ function buildNavigation(docs, basePath = "/") {
|
|
|
62
73
|
return a.routePath.localeCompare(b.routePath);
|
|
63
74
|
});
|
|
64
75
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
function buildGroupChildren(parentDir) {
|
|
77
|
+
const items = [];
|
|
78
|
+
const directFiles = groups.get(parentDir) ?? [];
|
|
79
|
+
for (const doc of directFiles) {
|
|
80
|
+
const item = { label: doc.frontmatter.title, path: doc.routePath };
|
|
81
|
+
if (doc.frontmatter.icon) item.icon = doc.frontmatter.icon;
|
|
82
|
+
items.push(item);
|
|
83
|
+
}
|
|
84
|
+
const childDirs = /* @__PURE__ */ new Set();
|
|
85
|
+
for (const dir of groups.keys()) {
|
|
86
|
+
if (dir !== parentDir && dirname(dir) === parentDir) {
|
|
87
|
+
childDirs.add(dir);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
for (const indexPath of indexPages.keys()) {
|
|
91
|
+
const dir = indexPath;
|
|
92
|
+
if (dirname(dir) === parentDir && !childDirs.has(dir)) {
|
|
93
|
+
childDirs.add(dir);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const sortedChildDirs = [...childDirs].sort((a, b) => {
|
|
97
|
+
const indexA = indexPages.get(a);
|
|
98
|
+
const indexB = indexPages.get(b);
|
|
99
|
+
const orderA = indexA?.frontmatter.order ?? 999;
|
|
100
|
+
const orderB = indexB?.frontmatter.order ?? 999;
|
|
101
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
102
|
+
return a.localeCompare(b);
|
|
80
103
|
});
|
|
81
|
-
|
|
104
|
+
for (const childDir of sortedChildDirs) {
|
|
105
|
+
const indexPage = indexPages.get(childDir);
|
|
106
|
+
const label = indexPage?.frontmatter.title ?? toTitleCase(basename(childDir));
|
|
107
|
+
const children = buildGroupChildren(childDir);
|
|
108
|
+
if (children.length > 0) {
|
|
109
|
+
const group = { label, children };
|
|
110
|
+
if (indexPage?.frontmatter.icon) group.icon = indexPage.frontmatter.icon;
|
|
111
|
+
items.push(group);
|
|
112
|
+
} else if (indexPage) {
|
|
113
|
+
const item = { label, path: indexPage.routePath };
|
|
114
|
+
if (indexPage.frontmatter.icon) item.icon = indexPage.frontmatter.icon;
|
|
115
|
+
items.push(item);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return items;
|
|
82
119
|
}
|
|
83
|
-
return
|
|
120
|
+
return buildGroupChildren(rootDir);
|
|
84
121
|
}
|
|
85
122
|
function buildRoutes(docs, sectionId) {
|
|
86
123
|
return docs.map((doc) => ({
|
package/dist/node/cli/index.js
CHANGED
|
@@ -18,9 +18,9 @@ cli.command("build", "Build static documentation site").action(async () => {
|
|
|
18
18
|
const { build } = await import("../node/index.js");
|
|
19
19
|
await build();
|
|
20
20
|
});
|
|
21
|
-
cli.command("init", "Scaffold a docs folder").option("--no-internal", "Skip creating the internal docs section").action(async (options) => {
|
|
21
|
+
cli.command("init", "Scaffold a docs folder").option("--no-internal", "Skip creating the internal docs section").option("--no-claude-rules", "Skip creating .claude/rules/clearify-docs.md").action(async (options) => {
|
|
22
22
|
const { init } = await import("../node/index.js");
|
|
23
|
-
await init({ noInternal: options.noInternal });
|
|
23
|
+
await init({ noInternal: options.noInternal, noClaudeRules: options.noClaudeRules });
|
|
24
24
|
});
|
|
25
25
|
cli.command("check", "Check for broken internal links").action(async () => {
|
|
26
26
|
const { checkLinks } = await import("../node/check.js");
|
|
@@ -37,5 +37,5 @@ cli.command("openapi:generate", "Generate OpenAPI spec from NestJS app").option(
|
|
|
37
37
|
});
|
|
38
38
|
});
|
|
39
39
|
cli.help();
|
|
40
|
-
cli.version("1.
|
|
40
|
+
cli.version("1.8.0");
|
|
41
41
|
cli.parse();
|
|
@@ -5,5 +5,6 @@ declare const defaultConfig: ClearifyConfig;
|
|
|
5
5
|
declare function loadUserConfig(root: string): Promise<Partial<ClearifyConfig>>;
|
|
6
6
|
declare function resolveConfig(userConfig?: Partial<ClearifyConfig>, root?: string): ClearifyConfig;
|
|
7
7
|
declare function resolveSections(config: ClearifyConfig, root: string): ResolvedSection[];
|
|
8
|
+
declare function scanHubProjects(config: ClearifyConfig, root: string): Promise<ClearifyConfig>;
|
|
8
9
|
|
|
9
|
-
export { defaultConfig, loadUserConfig, resolveConfig, resolveSections };
|
|
10
|
+
export { defaultConfig, loadUserConfig, resolveConfig, resolveSections, scanHubProjects };
|
package/dist/node/core/config.js
CHANGED
|
@@ -2,8 +2,9 @@ import {
|
|
|
2
2
|
defaultConfig,
|
|
3
3
|
loadUserConfig,
|
|
4
4
|
resolveConfig,
|
|
5
|
-
resolveSections
|
|
6
|
-
|
|
5
|
+
resolveSections,
|
|
6
|
+
scanHubProjects
|
|
7
|
+
} from "../chunk-PQMKRFBQ.js";
|
|
7
8
|
import {
|
|
8
9
|
defineConfig
|
|
9
10
|
} from "../chunk-V7LLYIRO.js";
|
|
@@ -12,5 +13,6 @@ export {
|
|
|
12
13
|
defineConfig,
|
|
13
14
|
loadUserConfig,
|
|
14
15
|
resolveConfig,
|
|
15
|
-
resolveSections
|
|
16
|
+
resolveSections,
|
|
17
|
+
scanHubProjects
|
|
16
18
|
};
|
package/dist/node/node/build.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildSite
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-GGUDZD4K.js";
|
|
4
4
|
import "../chunk-PRTER35L.js";
|
|
5
5
|
import "../chunk-SCZZB7OE.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-IF6GI4WU.js";
|
|
7
7
|
import "../chunk-B2Q23JW3.js";
|
|
8
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-PQMKRFBQ.js";
|
|
9
9
|
import "../chunk-V7LLYIRO.js";
|
|
10
|
-
import "../chunk-
|
|
10
|
+
import "../chunk-QNC5QRIO.js";
|
|
11
11
|
import "../chunk-5TD7NQIW.js";
|
|
12
12
|
export {
|
|
13
13
|
buildSite
|
package/dist/node/node/check.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
checkLinks
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-77VHQV7W.js";
|
|
4
|
+
import "../chunk-PQMKRFBQ.js";
|
|
5
5
|
import "../chunk-V7LLYIRO.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-QNC5QRIO.js";
|
|
7
7
|
import "../chunk-5TD7NQIW.js";
|
|
8
8
|
export {
|
|
9
9
|
checkLinks
|
package/dist/node/node/index.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildSite
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-GGUDZD4K.js";
|
|
4
4
|
import {
|
|
5
5
|
remarkMermaidToComponent
|
|
6
6
|
} from "../chunk-PRTER35L.js";
|
|
7
7
|
import "../chunk-SCZZB7OE.js";
|
|
8
8
|
import {
|
|
9
9
|
clearifyPlugin
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-IF6GI4WU.js";
|
|
11
11
|
import "../chunk-B2Q23JW3.js";
|
|
12
12
|
import {
|
|
13
13
|
init
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-QIAMD2Q6.js";
|
|
15
15
|
import {
|
|
16
16
|
checkLinks
|
|
17
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-77VHQV7W.js";
|
|
18
18
|
import {
|
|
19
19
|
loadUserConfig,
|
|
20
20
|
resolveConfig
|
|
21
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-PQMKRFBQ.js";
|
|
22
22
|
import "../chunk-V7LLYIRO.js";
|
|
23
|
-
import "../chunk-
|
|
23
|
+
import "../chunk-QNC5QRIO.js";
|
|
24
24
|
import "../chunk-5TD7NQIW.js";
|
|
25
25
|
|
|
26
26
|
// src/node/index.ts
|
package/dist/node/node/init.d.ts
CHANGED
package/dist/node/node/init.js
CHANGED
|
@@ -50,6 +50,8 @@ interface ClearifyConfig {
|
|
|
50
50
|
github?: string;
|
|
51
51
|
[key: string]: string | undefined;
|
|
52
52
|
};
|
|
53
|
+
hub?: HubConfig;
|
|
54
|
+
hubProject?: Omit<HubProject, 'name'>;
|
|
53
55
|
customCss?: string;
|
|
54
56
|
headTags?: string[];
|
|
55
57
|
}
|
|
@@ -75,6 +77,22 @@ interface RouteEntry {
|
|
|
75
77
|
componentPath?: string;
|
|
76
78
|
redirectTo?: string;
|
|
77
79
|
}
|
|
80
|
+
interface HubProject {
|
|
81
|
+
name: string;
|
|
82
|
+
description: string;
|
|
83
|
+
href?: string;
|
|
84
|
+
repo?: string;
|
|
85
|
+
status?: 'active' | 'beta' | 'planned' | 'deprecated';
|
|
86
|
+
icon?: string;
|
|
87
|
+
tags?: string[];
|
|
88
|
+
group?: string;
|
|
89
|
+
hubUrl?: string;
|
|
90
|
+
hubName?: string;
|
|
91
|
+
}
|
|
92
|
+
interface HubConfig {
|
|
93
|
+
projects: HubProject[];
|
|
94
|
+
scan?: string;
|
|
95
|
+
}
|
|
78
96
|
declare function defineConfig(config: Partial<ClearifyConfig>): Partial<ClearifyConfig>;
|
|
79
97
|
|
|
80
|
-
export { type ClearifyConfig, type NavigationItem, type PageFrontmatter, type ResolvedSection, type RouteEntry, type SectionConfig, type SectionNavigation, defineConfig };
|
|
98
|
+
export { type ClearifyConfig, type HubConfig, type HubProject, type NavigationItem, type PageFrontmatter, type ResolvedSection, type RouteEntry, type SectionConfig, type SectionNavigation, defineConfig };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
clearifyPlugin
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-IF6GI4WU.js";
|
|
4
4
|
import "../chunk-B2Q23JW3.js";
|
|
5
|
-
import "../chunk-
|
|
5
|
+
import "../chunk-PQMKRFBQ.js";
|
|
6
6
|
import "../chunk-V7LLYIRO.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-QNC5QRIO.js";
|
|
8
8
|
import "../chunk-5TD7NQIW.js";
|
|
9
9
|
export {
|
|
10
10
|
clearifyPlugin
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marlinjai/clearify",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "An open-source documentation site generator. Turn markdown into beautiful docs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
"tsup": "^8.3.6",
|
|
73
73
|
"typescript": "^5.7.3"
|
|
74
74
|
},
|
|
75
|
+
"packageManager": "pnpm@9.15.0",
|
|
75
76
|
"license": "MIT",
|
|
76
77
|
"author": "MarlinJai",
|
|
77
78
|
"repository": {
|
package/src/theme/Layout.tsx
CHANGED
|
@@ -13,6 +13,9 @@ import { Badge } from './components/Badge.js';
|
|
|
13
13
|
import { Tooltip } from './components/Tooltip.js';
|
|
14
14
|
import { Columns, Column } from './components/Columns.js';
|
|
15
15
|
import { Frame } from './components/Frame.js';
|
|
16
|
+
import { StatusBadge } from './components/StatusBadge.js';
|
|
17
|
+
import { ProjectCard } from './components/ProjectCard.js';
|
|
18
|
+
import { ProjectGrid } from './components/ProjectGrid.js';
|
|
16
19
|
import { CodeBlock } from './CodeBlock.js';
|
|
17
20
|
|
|
18
21
|
export const mdxComponents = {
|
|
@@ -35,6 +38,9 @@ export const mdxComponents = {
|
|
|
35
38
|
Columns,
|
|
36
39
|
Column,
|
|
37
40
|
Frame,
|
|
41
|
+
StatusBadge,
|
|
42
|
+
ProjectCard,
|
|
43
|
+
ProjectGrid,
|
|
38
44
|
// Override default pre element with CodeBlock
|
|
39
45
|
pre: (props: React.HTMLAttributes<HTMLPreElement>) => <CodeBlock {...props} />,
|
|
40
46
|
};
|
package/src/theme/Sidebar.tsx
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import React, { useState, useEffect, useRef } from 'react';
|
|
2
2
|
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
|
3
3
|
import clsx from 'clsx';
|
|
4
|
-
import type { NavigationItem, SectionNavigation } from '../types/index.js';
|
|
4
|
+
import type { NavigationItem, SectionNavigation, HubProject } from '../types/index.js';
|
|
5
5
|
|
|
6
6
|
interface SidebarProps {
|
|
7
7
|
sections: SectionNavigation[];
|
|
8
8
|
open: boolean;
|
|
9
9
|
onClose: () => void;
|
|
10
|
+
hubProject?: Omit<HubProject, 'name'>;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
function findActiveSection(pathname: string, sections: SectionNavigation[]): string {
|
|
@@ -103,7 +104,7 @@ function SectionSwitcher({
|
|
|
103
104
|
);
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
export function Sidebar({ sections, open, onClose }: SidebarProps) {
|
|
107
|
+
export function Sidebar({ sections, open, onClose, hubProject }: SidebarProps) {
|
|
107
108
|
const location = useLocation();
|
|
108
109
|
const navigate = useNavigate();
|
|
109
110
|
|
|
@@ -246,6 +247,37 @@ export function Sidebar({ sections, open, onClose }: SidebarProps) {
|
|
|
246
247
|
}}
|
|
247
248
|
className={clsx('clearify-sidebar', open && 'clearify-sidebar-open')}
|
|
248
249
|
>
|
|
250
|
+
{hubProject?.hubUrl && (
|
|
251
|
+
<a
|
|
252
|
+
href={hubProject.hubUrl}
|
|
253
|
+
style={{
|
|
254
|
+
display: 'flex',
|
|
255
|
+
alignItems: 'center',
|
|
256
|
+
gap: '0.375rem',
|
|
257
|
+
padding: '0.375rem 0.75rem',
|
|
258
|
+
margin: '0 0.5rem 0.5rem',
|
|
259
|
+
fontSize: '0.75rem',
|
|
260
|
+
fontWeight: 500,
|
|
261
|
+
color: 'var(--clearify-text-tertiary)',
|
|
262
|
+
textDecoration: 'none',
|
|
263
|
+
borderRadius: 'var(--clearify-radius-sm)',
|
|
264
|
+
transition: 'color 0.15s, background-color 0.15s',
|
|
265
|
+
}}
|
|
266
|
+
onMouseEnter={(e) => {
|
|
267
|
+
e.currentTarget.style.color = 'var(--clearify-primary)';
|
|
268
|
+
e.currentTarget.style.backgroundColor = 'var(--clearify-primary-soft)';
|
|
269
|
+
}}
|
|
270
|
+
onMouseLeave={(e) => {
|
|
271
|
+
e.currentTarget.style.color = 'var(--clearify-text-tertiary)';
|
|
272
|
+
e.currentTarget.style.backgroundColor = 'transparent';
|
|
273
|
+
}}
|
|
274
|
+
>
|
|
275
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
276
|
+
<polyline points="15 18 9 12 15 6" />
|
|
277
|
+
</svg>
|
|
278
|
+
{hubProject.hubName ?? 'Hub'}
|
|
279
|
+
</a>
|
|
280
|
+
)}
|
|
249
281
|
{showSwitcher && (
|
|
250
282
|
<SectionSwitcher
|
|
251
283
|
sections={sections}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Link } from 'react-router-dom';
|
|
3
|
+
import { StatusBadge } from './StatusBadge.js';
|
|
4
|
+
|
|
5
|
+
interface ProjectCardProps {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
href?: string;
|
|
9
|
+
repo?: string;
|
|
10
|
+
status?: 'active' | 'beta' | 'planned' | 'deprecated';
|
|
11
|
+
icon?: string;
|
|
12
|
+
tags?: string[];
|
|
13
|
+
children?: React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function ProjectCard({ name, description, href, repo, status, icon, tags, children }: ProjectCardProps) {
|
|
17
|
+
const content = (
|
|
18
|
+
<div
|
|
19
|
+
style={{
|
|
20
|
+
border: '1px solid var(--clearify-border)',
|
|
21
|
+
borderRadius: 'var(--clearify-radius-lg)',
|
|
22
|
+
padding: '1.375rem',
|
|
23
|
+
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
24
|
+
cursor: href ? 'pointer' : 'default',
|
|
25
|
+
height: '100%',
|
|
26
|
+
background: 'var(--clearify-bg)',
|
|
27
|
+
position: 'relative',
|
|
28
|
+
overflow: 'hidden',
|
|
29
|
+
display: 'flex',
|
|
30
|
+
flexDirection: 'column' as const,
|
|
31
|
+
}}
|
|
32
|
+
className="clearify-project-card"
|
|
33
|
+
>
|
|
34
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '0.625rem' }}>
|
|
35
|
+
{icon && (
|
|
36
|
+
<div
|
|
37
|
+
style={{
|
|
38
|
+
fontSize: '1.25rem',
|
|
39
|
+
width: 36,
|
|
40
|
+
height: 36,
|
|
41
|
+
display: 'flex',
|
|
42
|
+
alignItems: 'center',
|
|
43
|
+
justifyContent: 'center',
|
|
44
|
+
borderRadius: 'var(--clearify-radius-sm)',
|
|
45
|
+
background: 'var(--clearify-gradient-subtle)',
|
|
46
|
+
border: '1px solid var(--clearify-border)',
|
|
47
|
+
flexShrink: 0,
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
50
|
+
{icon}
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '0.375rem' }}>
|
|
54
|
+
{status && <StatusBadge status={status} />}
|
|
55
|
+
{repo && (
|
|
56
|
+
<a
|
|
57
|
+
href={repo}
|
|
58
|
+
target="_blank"
|
|
59
|
+
rel="noopener noreferrer"
|
|
60
|
+
onClick={(e) => e.stopPropagation()}
|
|
61
|
+
style={{
|
|
62
|
+
color: 'var(--clearify-text-secondary)',
|
|
63
|
+
display: 'inline-flex',
|
|
64
|
+
alignItems: 'center',
|
|
65
|
+
transition: 'color 0.15s',
|
|
66
|
+
}}
|
|
67
|
+
className="clearify-project-card-repo"
|
|
68
|
+
title="View repository"
|
|
69
|
+
>
|
|
70
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
|
71
|
+
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
|
|
72
|
+
</svg>
|
|
73
|
+
</a>
|
|
74
|
+
)}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div style={{ fontWeight: 600, marginBottom: '0.25rem', fontSize: '0.9375rem', letterSpacing: '-0.01em' }}>
|
|
79
|
+
{name}
|
|
80
|
+
</div>
|
|
81
|
+
<div style={{ fontSize: '0.8125rem', color: 'var(--clearify-text-secondary)', lineHeight: 1.55, flex: 1 }}>
|
|
82
|
+
{description}
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
{children && <div style={{ marginTop: '0.625rem', fontSize: '0.8125rem' }}>{children}</div>}
|
|
86
|
+
|
|
87
|
+
{tags && tags.length > 0 && (
|
|
88
|
+
<div style={{ display: 'flex', flexWrap: 'wrap' as const, gap: '0.375rem', marginTop: '0.75rem' }}>
|
|
89
|
+
{tags.map((tag) => (
|
|
90
|
+
<span
|
|
91
|
+
key={tag}
|
|
92
|
+
style={{
|
|
93
|
+
display: 'inline-block',
|
|
94
|
+
padding: '0.0625rem 0.4375rem',
|
|
95
|
+
borderRadius: '9999px',
|
|
96
|
+
fontSize: '0.6875rem',
|
|
97
|
+
fontWeight: 500,
|
|
98
|
+
background: 'rgba(107, 114, 128, 0.08)',
|
|
99
|
+
color: 'var(--clearify-text-secondary)',
|
|
100
|
+
fontFamily: 'var(--font-sans)',
|
|
101
|
+
}}
|
|
102
|
+
className="clearify-project-card-tag"
|
|
103
|
+
>
|
|
104
|
+
{tag}
|
|
105
|
+
</span>
|
|
106
|
+
))}
|
|
107
|
+
</div>
|
|
108
|
+
)}
|
|
109
|
+
|
|
110
|
+
<style>{`
|
|
111
|
+
.clearify-project-card:hover {
|
|
112
|
+
border-color: var(--clearify-border-strong) !important;
|
|
113
|
+
box-shadow: var(--clearify-shadow) !important;
|
|
114
|
+
transform: translateY(-1px);
|
|
115
|
+
}
|
|
116
|
+
.clearify-project-card-repo:hover {
|
|
117
|
+
color: var(--clearify-text) !important;
|
|
118
|
+
}
|
|
119
|
+
.dark .clearify-project-card-tag {
|
|
120
|
+
background: rgba(107, 114, 128, 0.15) !important;
|
|
121
|
+
}
|
|
122
|
+
`}</style>
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
if (href) {
|
|
127
|
+
if (href.startsWith('/')) {
|
|
128
|
+
return <Link to={href} style={{ textDecoration: 'none', color: 'inherit' }}>{content}</Link>;
|
|
129
|
+
}
|
|
130
|
+
return <a href={href} target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'none', color: 'inherit' }}>{content}</a>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return content;
|
|
134
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import hub from 'virtual:clearify/hub';
|
|
3
|
+
import { ProjectCard } from './ProjectCard.js';
|
|
4
|
+
import type { HubProject } from '../../types/index.js';
|
|
5
|
+
|
|
6
|
+
interface ProjectGridProps {
|
|
7
|
+
cols?: number;
|
|
8
|
+
children?: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function ProjectGridInner({ cols, projects }: { cols: number; projects: HubProject[] }) {
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
style={{
|
|
15
|
+
display: 'grid',
|
|
16
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
17
|
+
gap: '0.875rem',
|
|
18
|
+
}}
|
|
19
|
+
className="clearify-project-grid"
|
|
20
|
+
>
|
|
21
|
+
{projects.map((project) => (
|
|
22
|
+
<ProjectCard
|
|
23
|
+
key={project.name}
|
|
24
|
+
name={project.name}
|
|
25
|
+
description={project.description}
|
|
26
|
+
href={project.href}
|
|
27
|
+
repo={project.repo}
|
|
28
|
+
status={project.status}
|
|
29
|
+
icon={project.icon}
|
|
30
|
+
tags={project.tags}
|
|
31
|
+
/>
|
|
32
|
+
))}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function ProjectGrid({ cols = 3, children }: ProjectGridProps) {
|
|
38
|
+
const hasChildren = React.Children.count(children) > 0;
|
|
39
|
+
|
|
40
|
+
if (hasChildren) {
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
style={{
|
|
44
|
+
display: 'grid',
|
|
45
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
46
|
+
gap: '0.875rem',
|
|
47
|
+
marginBottom: '1.5rem',
|
|
48
|
+
}}
|
|
49
|
+
className="clearify-project-grid"
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
<style>{gridResponsiveStyles}</style>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const projects: HubProject[] = hub?.projects ?? [];
|
|
58
|
+
const hasGroups = projects.some((p) => p.group);
|
|
59
|
+
|
|
60
|
+
if (!hasGroups) {
|
|
61
|
+
return (
|
|
62
|
+
<div style={{ marginBottom: '1.5rem' }}>
|
|
63
|
+
<ProjectGridInner cols={cols} projects={projects} />
|
|
64
|
+
<style>{gridResponsiveStyles}</style>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Group projects by their group field, preserving insertion order
|
|
70
|
+
const groupOrder: string[] = [];
|
|
71
|
+
const grouped: Record<string, HubProject[]> = {};
|
|
72
|
+
const ungrouped: HubProject[] = [];
|
|
73
|
+
|
|
74
|
+
for (const project of projects) {
|
|
75
|
+
if (project.group) {
|
|
76
|
+
if (!grouped[project.group]) {
|
|
77
|
+
grouped[project.group] = [];
|
|
78
|
+
groupOrder.push(project.group);
|
|
79
|
+
}
|
|
80
|
+
grouped[project.group].push(project);
|
|
81
|
+
} else {
|
|
82
|
+
ungrouped.push(project);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div style={{ marginBottom: '1.5rem' }}>
|
|
88
|
+
{groupOrder.map((groupName) => (
|
|
89
|
+
<div key={groupName} style={{ marginBottom: '1.5rem' }}>
|
|
90
|
+
<h2
|
|
91
|
+
style={{
|
|
92
|
+
fontSize: '1.25rem',
|
|
93
|
+
fontWeight: 600,
|
|
94
|
+
marginBottom: '0.75rem',
|
|
95
|
+
marginTop: 0,
|
|
96
|
+
color: 'var(--clearify-text)',
|
|
97
|
+
letterSpacing: '-0.01em',
|
|
98
|
+
}}
|
|
99
|
+
>
|
|
100
|
+
{groupName}
|
|
101
|
+
</h2>
|
|
102
|
+
<ProjectGridInner cols={cols} projects={grouped[groupName]} />
|
|
103
|
+
</div>
|
|
104
|
+
))}
|
|
105
|
+
|
|
106
|
+
{ungrouped.length > 0 && (
|
|
107
|
+
<div style={{ marginBottom: '1.5rem' }}>
|
|
108
|
+
{groupOrder.length > 0 && (
|
|
109
|
+
<h2
|
|
110
|
+
style={{
|
|
111
|
+
fontSize: '1.25rem',
|
|
112
|
+
fontWeight: 600,
|
|
113
|
+
marginBottom: '0.75rem',
|
|
114
|
+
marginTop: 0,
|
|
115
|
+
color: 'var(--clearify-text)',
|
|
116
|
+
letterSpacing: '-0.01em',
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
Other Projects
|
|
120
|
+
</h2>
|
|
121
|
+
)}
|
|
122
|
+
<ProjectGridInner cols={cols} projects={ungrouped} />
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
125
|
+
|
|
126
|
+
<style>{gridResponsiveStyles}</style>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const gridResponsiveStyles = `
|
|
132
|
+
@media (max-width: 900px) {
|
|
133
|
+
.clearify-project-grid {
|
|
134
|
+
grid-template-columns: repeat(2, 1fr) !important;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
@media (max-width: 640px) {
|
|
138
|
+
.clearify-project-grid {
|
|
139
|
+
grid-template-columns: 1fr !important;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
`;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
const statusStyles = {
|
|
4
|
+
active: {
|
|
5
|
+
bg: 'rgba(34, 197, 94, 0.08)',
|
|
6
|
+
bgDark: 'rgba(34, 197, 94, 0.15)',
|
|
7
|
+
color: '#16a34a',
|
|
8
|
+
colorDark: '#4ade80',
|
|
9
|
+
label: 'Active',
|
|
10
|
+
},
|
|
11
|
+
beta: {
|
|
12
|
+
bg: 'rgba(99, 102, 241, 0.08)',
|
|
13
|
+
bgDark: 'rgba(99, 102, 241, 0.15)',
|
|
14
|
+
color: '#6366f1',
|
|
15
|
+
colorDark: '#818cf8',
|
|
16
|
+
label: 'Beta',
|
|
17
|
+
},
|
|
18
|
+
planned: {
|
|
19
|
+
bg: 'rgba(245, 158, 11, 0.08)',
|
|
20
|
+
bgDark: 'rgba(245, 158, 11, 0.15)',
|
|
21
|
+
color: '#d97706',
|
|
22
|
+
colorDark: '#fbbf24',
|
|
23
|
+
label: 'Planned',
|
|
24
|
+
},
|
|
25
|
+
deprecated: {
|
|
26
|
+
bg: 'rgba(239, 68, 68, 0.08)',
|
|
27
|
+
bgDark: 'rgba(239, 68, 68, 0.15)',
|
|
28
|
+
color: '#dc2626',
|
|
29
|
+
colorDark: '#f87171',
|
|
30
|
+
label: 'Deprecated',
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
interface StatusBadgeProps {
|
|
35
|
+
status: 'active' | 'beta' | 'planned' | 'deprecated';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function StatusBadge({ status }: StatusBadgeProps) {
|
|
39
|
+
const s = statusStyles[status];
|
|
40
|
+
const badgeClass = `clearify-status-badge-${status}`;
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<span
|
|
44
|
+
style={{
|
|
45
|
+
display: 'inline-flex',
|
|
46
|
+
alignItems: 'center',
|
|
47
|
+
padding: '0.125rem 0.5rem',
|
|
48
|
+
borderRadius: '9999px',
|
|
49
|
+
fontSize: '0.6875rem',
|
|
50
|
+
fontWeight: 500,
|
|
51
|
+
lineHeight: 1.5,
|
|
52
|
+
backgroundColor: s.bg,
|
|
53
|
+
color: s.color,
|
|
54
|
+
fontFamily: 'var(--font-sans)',
|
|
55
|
+
whiteSpace: 'nowrap',
|
|
56
|
+
letterSpacing: '0.01em',
|
|
57
|
+
}}
|
|
58
|
+
className={badgeClass}
|
|
59
|
+
>
|
|
60
|
+
{s.label}
|
|
61
|
+
|
|
62
|
+
<style>{`
|
|
63
|
+
.dark .${badgeClass} {
|
|
64
|
+
background-color: ${s.bgDark} !important;
|
|
65
|
+
color: ${s.colorDark} !important;
|
|
66
|
+
}
|
|
67
|
+
`}</style>
|
|
68
|
+
</span>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -12,3 +12,6 @@ export { Badge } from './Badge.js';
|
|
|
12
12
|
export { Tooltip } from './Tooltip.js';
|
|
13
13
|
export { Columns, Column } from './Columns.js';
|
|
14
14
|
export { Frame } from './Frame.js';
|
|
15
|
+
export { StatusBadge } from './StatusBadge.js';
|
|
16
|
+
export { ProjectCard } from './ProjectCard.js';
|
|
17
|
+
export { ProjectGrid } from './ProjectGrid.js';
|