concept-atlas-dense-explain 0.6.1 → 0.7.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/bin/cli.mjs +15 -5
- package/package.json +1 -1
- package/skill/SKILL.md +3 -3
- package/template/scripts/build.mjs +61 -27
- package/template/src/components/MDXComponents.jsx +2 -1
- package/template/src/model/skins.js +18 -1
- package/template/src/styles/concept-explain.css +10 -3904
- package/template/src/styles/core.css +3948 -0
- package/template/src/styles/motion.css +189 -0
- package/template/src/styles/packs/elastic.css +517 -0
- package/template/src/styles/packs/manuscript.css +426 -0
- package/template/src/styles/packs/shadcn.css +517 -0
- package/template/src/styles/skins.css +561 -0
- package/template/src/styles/tokens.css +80 -37
- package/template/src/views/RelationGraph.jsx +31 -14
- package/template/vite.config.js +63 -6
package/bin/cli.mjs
CHANGED
|
@@ -14,7 +14,7 @@ let buildCounter = 0;
|
|
|
14
14
|
|
|
15
15
|
function usage() {
|
|
16
16
|
console.log('Usage:');
|
|
17
|
-
console.log(' npx concept-atlas-dense-explain <input.mdx>... [--mode atlas|scroll] [--skin <id>] [--default-mode dark|light|system] [--style <id>] [-o output.html|dir] [--force] [--concurrency N] [--link-assets] [--json] [--no-validate]');
|
|
17
|
+
console.log(' npx concept-atlas-dense-explain <input.mdx>... [--mode atlas|scroll] [--skin <id>] [--default-mode dark|light|system] [--style <id>] [-o output.html|dir] [--force] [--concurrency N] [--link-assets] [--inline-mermaid] [--mermaid-cdn <url>] [--json] [--no-validate]');
|
|
18
18
|
console.log(' npx concept-atlas-dense-explain render <input.mdx>... [-o output.html|dir]');
|
|
19
19
|
console.log(' npx concept-atlas-dense-explain validate <input.mdx> [--mode atlas|scroll] [--strict] [--json]');
|
|
20
20
|
console.log(' npx concept-atlas-dense-explain create <output.mdx> [--mode atlas|scroll] [--force]');
|
|
@@ -22,6 +22,7 @@ function usage() {
|
|
|
22
22
|
console.log('');
|
|
23
23
|
console.log(' Multiple inputs build in parallel (default 2 at a time, cap 4); -o is then a directory.');
|
|
24
24
|
console.log(' --link-assets keeps figures as relative links instead of inlining them as base64.');
|
|
25
|
+
console.log(' Mermaid diagrams load from a CDN at runtime by default (fast builds, needs network); --inline-mermaid bakes Mermaid into the HTML for a fully offline single file; --mermaid-cdn overrides the CDN URL.');
|
|
25
26
|
console.log(` --skin bakes a default palette (${SKINS.map(skin => skin.id).join(', ')}); --default-mode bakes a default dark/light mode; --style bakes a default component style (${COMPONENT_STYLES.map(style => style.id).join(', ')}). Readers can still switch in the UI.`);
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -43,7 +44,7 @@ function fail(message) {
|
|
|
43
44
|
process.exit(1);
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
const VALUE_FLAGS = new Set(['--mode', '-o', '--output', '--concurrency', '--skin', '--default-mode', '--style']);
|
|
47
|
+
const VALUE_FLAGS = new Set(['--mode', '-o', '--output', '--concurrency', '--skin', '--default-mode', '--style', '--mermaid-cdn']);
|
|
47
48
|
|
|
48
49
|
/** Splits argv into flags, flag values and positional arguments. */
|
|
49
50
|
function parseFlags(argv) {
|
|
@@ -203,6 +204,8 @@ const strict = parsed.flags.has('--strict');
|
|
|
203
204
|
const skipValidate = parsed.flags.has('--no-validate');
|
|
204
205
|
const force = parsed.flags.has('--force');
|
|
205
206
|
const linkAssets = parsed.flags.has('--link-assets');
|
|
207
|
+
const inlineMermaid = parsed.flags.has('--inline-mermaid');
|
|
208
|
+
const mermaidCdn = parsed.values.get('--mermaid-cdn') || null;
|
|
206
209
|
const modeFlag = parsed.values.get('--mode') || null;
|
|
207
210
|
|
|
208
211
|
// Compile-time appearance defaults. Invalid values fail fast with the valid
|
|
@@ -332,6 +335,10 @@ async function buildOne(job) {
|
|
|
332
335
|
if (skinFlag) define.__ATLAS_DEFAULT_SKIN__ = JSON.stringify(skinFlag);
|
|
333
336
|
if (defaultModeFlag) define.__ATLAS_DEFAULT_MODE__ = JSON.stringify(defaultModeFlag);
|
|
334
337
|
if (styleFlag) define.__ATLAS_DEFAULT_STYLE__ = JSON.stringify(styleFlag);
|
|
338
|
+
if (features.mermaid) {
|
|
339
|
+
define.__ATLAS_MERMAID_MODE__ = JSON.stringify(inlineMermaid ? 'inline' : 'cdn');
|
|
340
|
+
if (mermaidCdn) define.__ATLAS_MERMAID_CDN_URL__ = JSON.stringify(mermaidCdn);
|
|
341
|
+
}
|
|
335
342
|
try {
|
|
336
343
|
await build({
|
|
337
344
|
root: templateRoot,
|
|
@@ -357,9 +364,10 @@ async function buildOne(job) {
|
|
|
357
364
|
|
|
358
365
|
/** Saves the mermaid/KaTeX payload when a document never renders them. */
|
|
359
366
|
function describeFeatures(features) {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
367
|
+
const tags = [];
|
|
368
|
+
if (features.mermaid) tags.push(inlineMermaid ? 'mermaid inline' : 'mermaid cdn');
|
|
369
|
+
if (features.math) tags.push('KaTeX');
|
|
370
|
+
return tags.length ? ` [${tags.join(', ')}]` : '';
|
|
363
371
|
}
|
|
364
372
|
|
|
365
373
|
function summarizeFeatures(jobs, results) {
|
|
@@ -368,7 +376,9 @@ function summarizeFeatures(jobs, results) {
|
|
|
368
376
|
if (!pages.length) return '';
|
|
369
377
|
const droppedMath = pages.filter(job => !job.features.math).length;
|
|
370
378
|
const droppedMermaid = pages.filter(job => !job.features.mermaid).length;
|
|
379
|
+
const cdnMermaid = pages.filter(job => job.features.mermaid && !inlineMermaid).length;
|
|
371
380
|
const parts = [];
|
|
381
|
+
if (cdnMermaid) parts.push(`Mermaid via CDN on ${cdnMermaid}/${pages.length}`);
|
|
372
382
|
if (droppedMath) parts.push(`KaTeX dropped on ${droppedMath}/${pages.length}`);
|
|
373
383
|
if (droppedMermaid) parts.push(`Mermaid dropped on ${droppedMermaid}/${pages.length}`);
|
|
374
384
|
return parts.join(', ');
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -26,9 +26,9 @@ If the user only wants the prompt/methodology and not files, still choose a shel
|
|
|
26
26
|
npx concept-atlas-dense-explain validate <file>.mdx --json
|
|
27
27
|
```
|
|
28
28
|
Every diagnostic is `CODE line:column message`. Fix all `error`s and re-run; warnings are quality signals you should also address when cheap.
|
|
29
|
-
6. Compile: `npx concept-atlas-dense-explain <file>.mdx --mode atlas|scroll [-o out.html] [--skin <id>] [--default-mode dark|light|system] [--style <id>]`. Output is a standalone HTML beside the MDX unless `-o` is given. Validation errors abort the build; use `--no-validate` only to force a knowingly broken build.
|
|
30
|
-
7. **Appearance (optional)**: pages ship with a reader-facing appearance menu — palette (`aurora` cool
|
|
31
|
-
8. For several documents, pass them all in one call: `npx concept-atlas-dense-explain a.mdx b.mdx c.mdx -o dist --force [--concurrency 3]`. `-o` is then a directory. The batch validates everything first and builds in parallel. Builds only bundle the heavy renderers the content uses: a page with no `<Math
|
|
29
|
+
6. Compile: `npx concept-atlas-dense-explain <file>.mdx --mode atlas|scroll [-o out.html] [--skin <id>] [--default-mode dark|light|system] [--style <id>] [--inline-mermaid] [--mermaid-cdn <url>]`. Output is a standalone HTML beside the MDX unless `-o` is given. Validation errors abort the build; use `--no-validate` only to force a knowingly broken build. Mermaid loads from a CDN at runtime by default (fast build, small HTML, needs network); pass `--inline-mermaid` when the user needs a fully offline single file.
|
|
30
|
+
7. **Appearance (optional)**: pages ship with a reader-facing appearance menu — palette (`aurora` cool indigo, `ember` warm gold, `verdant` forest green, `sakura` pink-plum, `noir` achromatic ink), a dark/light toggle, and a component style pack (`manuscript` editorial marginalia, `classic` boxed cards, `shadcn` hairline-bordered minimal UI, `elastic` bordered observability panels). The shipped default is aurora × manuscript × light; choices persist in localStorage across both carriers. You can bake different compile-time defaults: `--skin ember --default-mode dark --style classic` (or env `CONCEPT_ATLAS_SKIN` / `CONCEPT_ATLAS_DEFAULT_MODE` / `CONCEPT_ATLAS_STYLE` on the repo build). `--default-mode` only honors `dark`/`light`; `system` is accepted by the CLI but resolves to the carrier default (`light`). Bake a default only when the user asks for one. Content MDX never sets appearance — it is carrier/tooling territory, not content.
|
|
31
|
+
8. For several documents, pass them all in one call: `npx concept-atlas-dense-explain a.mdx b.mdx c.mdx -o dist --force [--concurrency 3]`. `-o` is then a directory. The batch validates everything first and builds in parallel. Builds only bundle the heavy renderers the content uses: a page with no `<Math>` skips KaTeX (its ~1.4MB inlined fonts), and Mermaid is served from a CDN by default rather than bundled. Do not add dummy `<Math>`/`<Mermaid>` nodes to "enable" them — write the components only when the content needs them. Add `--link-assets` when the page carries many screenshots and size matters.
|
|
32
32
|
9. Report the shell, output path, validation result (errors/warnings), and limitations. Do not claim interactions you did not verify.
|
|
33
33
|
|
|
34
34
|
## Carriers
|
|
@@ -3,12 +3,21 @@ import path from 'path';
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { normalizeSkin, normalizeStyle } from '../src/model/skins.js';
|
|
6
|
-
import { extractPageTitle } from '../src/model/validate-content.js';
|
|
6
|
+
import { extractPageTitle, detectFeatures } from '../src/model/validate-content.js';
|
|
7
7
|
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = path.dirname(__filename);
|
|
10
10
|
const rootDir = path.resolve(__dirname, '..');
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Mermaid is served from a CDN at runtime by default so its ~2100-module
|
|
14
|
+
* transform stays out of the build. Set CONCEPT_ATLAS_INLINE_MERMAID=1 to bake
|
|
15
|
+
* it back into the HTML for a fully offline single file; CONCEPT_ATLAS_MERMAID_CDN
|
|
16
|
+
* overrides the CDN URL.
|
|
17
|
+
*/
|
|
18
|
+
const INLINE_MERMAID = ['1', 'true', 'yes'].includes((process.env.CONCEPT_ATLAS_INLINE_MERMAID || '').toLowerCase());
|
|
19
|
+
const MERMAID_CDN_URL = process.env.CONCEPT_ATLAS_MERMAID_CDN || '';
|
|
20
|
+
|
|
12
21
|
/**
|
|
13
22
|
* Optional compile-time appearance defaults, read from the environment and
|
|
14
23
|
* forwarded as `define`s. The carrier HTML plugin replaces the placeholders
|
|
@@ -26,28 +35,65 @@ function appearanceDefines() {
|
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
/**
|
|
29
|
-
* The
|
|
30
|
-
* the repository
|
|
31
|
-
*
|
|
32
|
-
*
|
|
38
|
+
* The demo document mounted by each carrier. Unlike the npm template (which
|
|
39
|
+
* aliases the user's MDX), the repository hardcodes its demos in main.jsx /
|
|
40
|
+
* scroll-main.jsx, so the build reads them to derive the tab title and the set
|
|
41
|
+
* of optional renderers actually needed.
|
|
33
42
|
*/
|
|
34
|
-
function
|
|
43
|
+
function demoSourceFor(entry) {
|
|
35
44
|
const demos = entry === 'scroll.html'
|
|
36
45
|
? ['content/scroll-reading-demo.mdx']
|
|
37
46
|
: ['content/components-demo.mdx', 'content/compile-runtime.mdx'];
|
|
38
47
|
const demo = demos.map(name => path.resolve(rootDir, name)).find(file => fs.existsSync(file));
|
|
39
|
-
return demo ?
|
|
48
|
+
return demo ? { demo, source: fs.readFileSync(demo, 'utf8') } : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Builds one standalone carrier HTML. `__ATLAS_FEATURES__` mirrors the CLI's
|
|
53
|
+
* per-document stubbing so a demo that never uses <Math>/<Mermaid> skips the
|
|
54
|
+
* KaTeX fonts and Mermaid module graph instead of bundling them unconditionally.
|
|
55
|
+
*/
|
|
56
|
+
async function buildCarrier(entry, baseDefine) {
|
|
57
|
+
const define = { ...baseDefine };
|
|
58
|
+
const demo = demoSourceFor(entry);
|
|
59
|
+
if (demo) {
|
|
60
|
+
const title = extractPageTitle(demo.source);
|
|
61
|
+
if (title) {
|
|
62
|
+
define.__ATLAS_PAGE_TITLE__ = JSON.stringify(title);
|
|
63
|
+
console.log(`🔖 ${entry} 标签页标题:${title}`);
|
|
64
|
+
}
|
|
65
|
+
const features = detectFeatures(demo.source);
|
|
66
|
+
define.__ATLAS_FEATURES__ = JSON.stringify(features);
|
|
67
|
+
if (features.mermaid) {
|
|
68
|
+
define.__ATLAS_MERMAID_MODE__ = JSON.stringify(INLINE_MERMAID ? 'inline' : 'cdn');
|
|
69
|
+
if (MERMAID_CDN_URL) define.__ATLAS_MERMAID_CDN_URL__ = JSON.stringify(MERMAID_CDN_URL);
|
|
70
|
+
if (!INLINE_MERMAID) console.log(`🌐 ${entry} Mermaid 运行时从 CDN 加载(--inline-mermaid 可内联)`);
|
|
71
|
+
}
|
|
72
|
+
const dropped = [features.math ? null : 'KaTeX', features.mermaid ? null : 'Mermaid'].filter(Boolean);
|
|
73
|
+
if (dropped.length) console.log(`⚡ ${entry} 省略未使用的渲染器:${dropped.join('、')}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await build({
|
|
77
|
+
root: rootDir,
|
|
78
|
+
define,
|
|
79
|
+
build: {
|
|
80
|
+
outDir: 'dist',
|
|
81
|
+
// dist is cleared once up front; parallel carriers must not wipe each
|
|
82
|
+
// other's output mid-build.
|
|
83
|
+
emptyOutDir: false,
|
|
84
|
+
rollupOptions: { input: path.resolve(rootDir, entry) },
|
|
85
|
+
}
|
|
86
|
+
});
|
|
40
87
|
}
|
|
41
88
|
|
|
42
89
|
async function runBuild() {
|
|
43
90
|
console.log('🚀 开始构建 Concept Atlas 知识讲解页面...');
|
|
44
91
|
|
|
45
92
|
try {
|
|
46
|
-
//
|
|
93
|
+
// Clear dist once, then let every carrier write into it concurrently.
|
|
47
94
|
const distDir = path.resolve(rootDir, 'dist');
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
95
|
+
fs.rmSync(distDir, { recursive: true, force: true });
|
|
96
|
+
fs.mkdirSync(distDir, { recursive: true });
|
|
51
97
|
|
|
52
98
|
const mode = process.env.CONCEPT_ATLAS_MODE;
|
|
53
99
|
const carriers = mode === 'atlas' ? ['index.html'] : mode === 'scroll' ? ['scroll.html'] : ['index.html', 'scroll.html'];
|
|
@@ -56,22 +102,10 @@ async function runBuild() {
|
|
|
56
102
|
console.log(`🎨 默认外观:skin=${define.__ATLAS_DEFAULT_SKIN__ || '(carrier 默认)'} mode=${define.__ATLAS_DEFAULT_MODE__ || '(carrier 默认)'}`);
|
|
57
103
|
}
|
|
58
104
|
|
|
59
|
-
// vite-plugin-singlefile supports one HTML input per build
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const entryDefine = title ? { ...define, __ATLAS_PAGE_TITLE__: JSON.stringify(title) } : define;
|
|
64
|
-
if (title) console.log(`🔖 ${entry} 标签页标题:${title}`);
|
|
65
|
-
await build({
|
|
66
|
-
root: rootDir,
|
|
67
|
-
define: entryDefine,
|
|
68
|
-
build: {
|
|
69
|
-
outDir: 'dist',
|
|
70
|
-
emptyOutDir: index === 0,
|
|
71
|
-
rollupOptions: { input: path.resolve(rootDir, entry) },
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
105
|
+
// vite-plugin-singlefile supports one HTML input per build and emits no
|
|
106
|
+
// shared assets, so the carriers are independent and build in parallel
|
|
107
|
+
// instead of one full pass after another.
|
|
108
|
+
await Promise.all(carriers.map(entry => buildCarrier(entry, define)));
|
|
75
109
|
|
|
76
110
|
console.log(`✅ 构建成功!产物已生成到 ${carriers.map(entry => `dist/${entry}`).join(' 和 ')}。`);
|
|
77
111
|
} catch (err) {
|
|
@@ -70,7 +70,7 @@ function configureMermaid() {
|
|
|
70
70
|
lineColor: read('--mermaid-line', MERMAID_FALLBACKS.lineColor),
|
|
71
71
|
secondaryColor: nodeBg,
|
|
72
72
|
tertiaryColor: read('--mermaid-canvas', MERMAID_FALLBACKS.tertiaryColor),
|
|
73
|
-
fontFamily: 'Plus Jakarta Sans, sans-serif
|
|
73
|
+
fontFamily: read('--font-sans', "'Plus Jakarta Sans', sans-serif"),
|
|
74
74
|
},
|
|
75
75
|
});
|
|
76
76
|
}
|
|
@@ -942,6 +942,7 @@ export function Chart({ title = '图表', type = 'bar', data = [], series = [],
|
|
|
942
942
|
<g key={seriesIndex}>
|
|
943
943
|
<polyline
|
|
944
944
|
className="chart-line"
|
|
945
|
+
pathLength="1"
|
|
945
946
|
style={chartColor(seriesIndex)}
|
|
946
947
|
points={entry.values.map((value, idx) => `${xFor(idx)},${yFor(value)}`).join(' ')}
|
|
947
948
|
/>
|
|
@@ -13,6 +13,21 @@ export const SKINS = [
|
|
|
13
13
|
id: 'ember',
|
|
14
14
|
label: 'Ember · 炉火',
|
|
15
15
|
swatch: { dark: '#1c1812', light: '#f6f1e7', accent: '#d99a4e' }
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'verdant',
|
|
19
|
+
label: 'Verdant · 苔原',
|
|
20
|
+
swatch: { dark: '#0f1813', light: '#f3f7f2', accent: '#10b981' }
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'sakura',
|
|
24
|
+
label: 'Sakura · 樱雾',
|
|
25
|
+
swatch: { dark: '#1e131d', light: '#fbf3f7', accent: '#ec4899' }
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'noir',
|
|
29
|
+
label: 'Noir · 墨白',
|
|
30
|
+
swatch: { dark: '#141416', light: '#f5f5f4', accent: '#e63946' }
|
|
16
31
|
}
|
|
17
32
|
];
|
|
18
33
|
|
|
@@ -32,7 +47,9 @@ export function normalizeSkin(value) {
|
|
|
32
47
|
*/
|
|
33
48
|
export const COMPONENT_STYLES = [
|
|
34
49
|
{ id: 'manuscript', label: 'Manuscript · 评注手稿' },
|
|
35
|
-
{ id: 'classic', label: 'Classic · 经典卡片' }
|
|
50
|
+
{ id: 'classic', label: 'Classic · 经典卡片' },
|
|
51
|
+
{ id: 'shadcn', label: 'shadcn · 极简界面' },
|
|
52
|
+
{ id: 'elastic', label: 'Elastic · 观测面板' }
|
|
36
53
|
];
|
|
37
54
|
|
|
38
55
|
export const DEFAULT_STYLE = 'manuscript';
|