decantr 0.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/AGENTS.md +868 -0
- package/CHANGELOG.md +255 -0
- package/CLAUDE.md +178 -0
- package/LICENSE +21 -0
- package/README.md +229 -0
- package/cli/art.js +127 -0
- package/cli/commands/a11y.js +61 -0
- package/cli/commands/audit.js +225 -0
- package/cli/commands/build.js +38 -0
- package/cli/commands/dev.js +18 -0
- package/cli/commands/doctor.js +197 -0
- package/cli/commands/figma-sync.js +48 -0
- package/cli/commands/figma-tokens.js +55 -0
- package/cli/commands/generate.js +26 -0
- package/cli/commands/init.js +116 -0
- package/cli/commands/lint.js +209 -0
- package/cli/commands/mcp.js +530 -0
- package/cli/commands/migrate.js +175 -0
- package/cli/commands/test.js +38 -0
- package/cli/commands/validate.js +354 -0
- package/cli/index.js +113 -0
- package/package.json +95 -0
- package/reference/atoms.md +517 -0
- package/reference/behaviors.md +384 -0
- package/reference/build-tooling.md +275 -0
- package/reference/color-guidelines.md +965 -0
- package/reference/component-lifecycle.md +137 -0
- package/reference/compound-spacing.md +95 -0
- package/reference/decantation-process.md +499 -0
- package/reference/dev-server-routes.md +93 -0
- package/reference/form-system.md +253 -0
- package/reference/i18n.md +336 -0
- package/reference/icons.md +576 -0
- package/reference/llm-primer.md +953 -0
- package/reference/plugins.md +252 -0
- package/reference/registry-consumption.md +76 -0
- package/reference/router.md +217 -0
- package/reference/shells.md +116 -0
- package/reference/spatial-guidelines.md +541 -0
- package/reference/ssr.md +234 -0
- package/reference/state-data.md +215 -0
- package/reference/state-patterns.md +166 -0
- package/reference/state.md +194 -0
- package/reference/style-system.md +110 -0
- package/reference/tokens.md +460 -0
- package/src/app.js +19 -0
- package/src/chart/_animate.js +266 -0
- package/src/chart/_base.js +109 -0
- package/src/chart/_data.js +209 -0
- package/src/chart/_format.js +106 -0
- package/src/chart/_interact.js +364 -0
- package/src/chart/_palette.js +105 -0
- package/src/chart/_renderer.js +52 -0
- package/src/chart/_scene.js +262 -0
- package/src/chart/_shared.js +371 -0
- package/src/chart/index.js +637 -0
- package/src/chart/layouts/_layout-base.js +328 -0
- package/src/chart/layouts/cartesian.js +148 -0
- package/src/chart/layouts/hierarchy.js +562 -0
- package/src/chart/layouts/polar.js +101 -0
- package/src/chart/renderers/canvas.js +179 -0
- package/src/chart/renderers/svg.js +256 -0
- package/src/chart/renderers/webgpu.js +715 -0
- package/src/chart/types/_type-base.js +26 -0
- package/src/chart/types/area.js +134 -0
- package/src/chart/types/bar.js +173 -0
- package/src/chart/types/box-plot.js +125 -0
- package/src/chart/types/bubble.js +63 -0
- package/src/chart/types/candlestick.js +115 -0
- package/src/chart/types/chord.js +85 -0
- package/src/chart/types/combination.js +108 -0
- package/src/chart/types/funnel.js +68 -0
- package/src/chart/types/gauge.js +163 -0
- package/src/chart/types/heatmap.js +98 -0
- package/src/chart/types/histogram.js +71 -0
- package/src/chart/types/line.js +111 -0
- package/src/chart/types/org-chart.js +93 -0
- package/src/chart/types/pie.js +81 -0
- package/src/chart/types/radar.js +96 -0
- package/src/chart/types/radial.js +68 -0
- package/src/chart/types/range-area.js +55 -0
- package/src/chart/types/range-bar.js +61 -0
- package/src/chart/types/sankey.js +73 -0
- package/src/chart/types/scatter.js +66 -0
- package/src/chart/types/sparkline.js +81 -0
- package/src/chart/types/sunburst.js +69 -0
- package/src/chart/types/swimlane.js +88 -0
- package/src/chart/types/treemap.js +62 -0
- package/src/chart/types/waterfall.js +100 -0
- package/src/components/_base.js +1658 -0
- package/src/components/_behaviors.js +1140 -0
- package/src/components/_primitives.js +534 -0
- package/src/components/_qr-encoder.js +539 -0
- package/src/components/accordion.js +207 -0
- package/src/components/affix.js +62 -0
- package/src/components/alert-dialog.js +75 -0
- package/src/components/alert.js +47 -0
- package/src/components/aspect-ratio.js +24 -0
- package/src/components/avatar-group.js +55 -0
- package/src/components/avatar.js +38 -0
- package/src/components/back-top.js +75 -0
- package/src/components/badge.js +74 -0
- package/src/components/banner.js +68 -0
- package/src/components/breadcrumb.js +162 -0
- package/src/components/button.js +115 -0
- package/src/components/calendar.js +131 -0
- package/src/components/card.js +192 -0
- package/src/components/carousel.js +98 -0
- package/src/components/cascader.js +261 -0
- package/src/components/checkbox.js +80 -0
- package/src/components/chip.js +81 -0
- package/src/components/code-block.js +82 -0
- package/src/components/collapsible.js +50 -0
- package/src/components/color-palette.js +438 -0
- package/src/components/color-picker.js +314 -0
- package/src/components/combobox.js +181 -0
- package/src/components/command.js +174 -0
- package/src/components/comment.js +206 -0
- package/src/components/context-menu.js +76 -0
- package/src/components/data-table.js +724 -0
- package/src/components/date-picker.js +217 -0
- package/src/components/date-range-picker.js +244 -0
- package/src/components/datetime-picker.js +271 -0
- package/src/components/descriptions.js +68 -0
- package/src/components/drawer.js +179 -0
- package/src/components/dropdown.js +88 -0
- package/src/components/empty.js +41 -0
- package/src/components/float-button.js +90 -0
- package/src/components/form.js +106 -0
- package/src/components/hover-card.js +49 -0
- package/src/components/icon.js +87 -0
- package/src/components/image.js +97 -0
- package/src/components/index.js +117 -0
- package/src/components/input-group.js +75 -0
- package/src/components/input-number.js +155 -0
- package/src/components/input-otp.js +178 -0
- package/src/components/input.js +91 -0
- package/src/components/kbd.js +36 -0
- package/src/components/label.js +25 -0
- package/src/components/list.js +118 -0
- package/src/components/masked-input.js +236 -0
- package/src/components/mentions.js +165 -0
- package/src/components/menu.js +259 -0
- package/src/components/message.js +80 -0
- package/src/components/modal.js +147 -0
- package/src/components/navigation-menu.js +166 -0
- package/src/components/notification.js +84 -0
- package/src/components/pagination.js +104 -0
- package/src/components/placeholder.js +132 -0
- package/src/components/popconfirm.js +70 -0
- package/src/components/popover.js +58 -0
- package/src/components/progress.js +61 -0
- package/src/components/qrcode.js +251 -0
- package/src/components/radiogroup.js +120 -0
- package/src/components/range-slider.js +176 -0
- package/src/components/rate.js +186 -0
- package/src/components/resizable.js +83 -0
- package/src/components/result.js +57 -0
- package/src/components/scroll-area.js +43 -0
- package/src/components/segmented.js +97 -0
- package/src/components/select.js +165 -0
- package/src/components/separator.js +31 -0
- package/src/components/shell.js +407 -0
- package/src/components/skeleton.js +39 -0
- package/src/components/slider.js +141 -0
- package/src/components/sortable-list.js +176 -0
- package/src/components/space.js +42 -0
- package/src/components/spinner.js +112 -0
- package/src/components/splitter.js +147 -0
- package/src/components/statistic.js +136 -0
- package/src/components/steps.js +99 -0
- package/src/components/switch.js +95 -0
- package/src/components/table.js +44 -0
- package/src/components/tabs.js +216 -0
- package/src/components/tag.js +115 -0
- package/src/components/textarea.js +82 -0
- package/src/components/time-picker.js +153 -0
- package/src/components/time-range-picker.js +170 -0
- package/src/components/timeline.js +226 -0
- package/src/components/toast.js +71 -0
- package/src/components/toggle.js +213 -0
- package/src/components/tooltip.js +57 -0
- package/src/components/tour.js +159 -0
- package/src/components/transfer.js +163 -0
- package/src/components/tree-select.js +274 -0
- package/src/components/tree.js +141 -0
- package/src/components/typography.js +136 -0
- package/src/components/upload.js +118 -0
- package/src/components/visually-hidden.js +20 -0
- package/src/components/watermark.js +124 -0
- package/src/core/index.js +539 -0
- package/src/core/lifecycle.js +69 -0
- package/src/css/atoms.js +651 -0
- package/src/css/components.js +940 -0
- package/src/css/derive.js +1296 -0
- package/src/css/index.js +265 -0
- package/src/css/runtime.js +268 -0
- package/src/css/styles/addons/bioluminescent.js +93 -0
- package/src/css/styles/addons/clay.js +70 -0
- package/src/css/styles/addons/clean.js +57 -0
- package/src/css/styles/addons/command-center.js +143 -0
- package/src/css/styles/addons/dopamine.js +83 -0
- package/src/css/styles/addons/editorial.js +80 -0
- package/src/css/styles/addons/glassmorphism.js +99 -0
- package/src/css/styles/addons/liquid-glass.js +105 -0
- package/src/css/styles/addons/prismatic.js +100 -0
- package/src/css/styles/addons/retro.js +63 -0
- package/src/css/styles/auradecantism.js +96 -0
- package/src/css/theme-registry.js +444 -0
- package/src/data/entity.js +281 -0
- package/src/data/index.js +13 -0
- package/src/data/persist.js +225 -0
- package/src/data/query.js +839 -0
- package/src/data/realtime.js +299 -0
- package/src/data/url.js +177 -0
- package/src/data/worker.js +134 -0
- package/src/explorer/archetypes.js +243 -0
- package/src/explorer/atoms.js +228 -0
- package/src/explorer/charts.js +497 -0
- package/src/explorer/components.js +129 -0
- package/src/explorer/foundations.js +949 -0
- package/src/explorer/icons.js +178 -0
- package/src/explorer/patterns.js +247 -0
- package/src/explorer/recipes.js +194 -0
- package/src/explorer/shared/pattern-examples.js +1337 -0
- package/src/explorer/shared/showcase-renderer.js +958 -0
- package/src/explorer/shared/spec-table.js +41 -0
- package/src/explorer/shared/usage-links.js +87 -0
- package/src/explorer/shell-config.js +10 -0
- package/src/explorer/shells.js +551 -0
- package/src/explorer/styles.js +161 -0
- package/src/explorer/tokens.js +262 -0
- package/src/explorer/tools.js +525 -0
- package/src/form/index.js +804 -0
- package/src/i18n/index.js +251 -0
- package/src/icons/essential.js +479 -0
- package/src/icons/index.js +53 -0
- package/src/plugins/index.js +282 -0
- package/src/registry/archetypes/content-site.json +71 -0
- package/src/registry/archetypes/docs-explorer.json +23 -0
- package/src/registry/archetypes/ecommerce.json +104 -0
- package/src/registry/archetypes/financial-dashboard.json +77 -0
- package/src/registry/archetypes/index.json +41 -0
- package/src/registry/archetypes/portfolio.json +82 -0
- package/src/registry/archetypes/recipe-community.json +159 -0
- package/src/registry/archetypes/saas-dashboard.json +86 -0
- package/src/registry/architect/cross-cutting.json +45 -0
- package/src/registry/architect/domains/ecommerce.json +294 -0
- package/src/registry/architect/domains/financial-services.json +302 -0
- package/src/registry/architect/index.json +26 -0
- package/src/registry/architect/traits.json +379 -0
- package/src/registry/atoms.json +16 -0
- package/src/registry/chart-showcase.json +160 -0
- package/src/registry/chart.json +136 -0
- package/src/registry/components.json +8616 -0
- package/src/registry/core.json +216 -0
- package/src/registry/css.json +319 -0
- package/src/registry/data.json +135 -0
- package/src/registry/foundations.json +11 -0
- package/src/registry/icons.json +463 -0
- package/src/registry/index.json +101 -0
- package/src/registry/patterns/activity-feed.json +37 -0
- package/src/registry/patterns/article-content.json +27 -0
- package/src/registry/patterns/auth-form.json +37 -0
- package/src/registry/patterns/author-card.json +20 -0
- package/src/registry/patterns/card-grid.json +127 -0
- package/src/registry/patterns/category-nav.json +26 -0
- package/src/registry/patterns/chart-grid.json +36 -0
- package/src/registry/patterns/chat-interface.json +37 -0
- package/src/registry/patterns/checklist-card.json +55 -0
- package/src/registry/patterns/comparison-panel.json +27 -0
- package/src/registry/patterns/component-showcase.json +24 -0
- package/src/registry/patterns/contact-form.json +31 -0
- package/src/registry/patterns/cta-section.json +20 -0
- package/src/registry/patterns/data-table.json +37 -0
- package/src/registry/patterns/detail-header.json +83 -0
- package/src/registry/patterns/detail-panel.json +27 -0
- package/src/registry/patterns/explorer-shell.json +22 -0
- package/src/registry/patterns/filter-bar.json +33 -0
- package/src/registry/patterns/filter-sidebar.json +27 -0
- package/src/registry/patterns/form-sections.json +110 -0
- package/src/registry/patterns/goal-tracker.json +27 -0
- package/src/registry/patterns/hero.json +107 -0
- package/src/registry/patterns/index.json +47 -0
- package/src/registry/patterns/kpi-grid.json +36 -0
- package/src/registry/patterns/media-gallery.json +20 -0
- package/src/registry/patterns/order-history.json +20 -0
- package/src/registry/patterns/pagination.json +19 -0
- package/src/registry/patterns/photo-to-recipe.json +36 -0
- package/src/registry/patterns/pipeline-tracker.json +28 -0
- package/src/registry/patterns/post-list.json +27 -0
- package/src/registry/patterns/pricing-table.json +32 -0
- package/src/registry/patterns/scorecard.json +28 -0
- package/src/registry/patterns/search-bar.json +20 -0
- package/src/registry/patterns/specimen-grid.json +19 -0
- package/src/registry/patterns/stat-card.json +55 -0
- package/src/registry/patterns/stats-bar.json +55 -0
- package/src/registry/patterns/steps-card.json +55 -0
- package/src/registry/patterns/table-of-contents.json +19 -0
- package/src/registry/patterns/testimonials.json +21 -0
- package/src/registry/patterns/timeline.json +27 -0
- package/src/registry/patterns/token-inspector.json +21 -0
- package/src/registry/patterns/wizard.json +27 -0
- package/src/registry/recipe-auradecantism.json +69 -0
- package/src/registry/recipe-clean.json +65 -0
- package/src/registry/recipe-command-center.json +78 -0
- package/src/registry/router.json +73 -0
- package/src/registry/schema/README.md +197 -0
- package/src/registry/skeletons.json +259 -0
- package/src/registry/state.json +137 -0
- package/src/registry/tokens.json +40 -0
- package/src/router/hash.js +17 -0
- package/src/router/history.js +18 -0
- package/src/router/index.js +598 -0
- package/src/ssr/index.js +922 -0
- package/src/state/arrays.js +181 -0
- package/src/state/devtools.js +647 -0
- package/src/state/index.js +498 -0
- package/src/state/middleware.js +288 -0
- package/src/state/scheduler.js +206 -0
- package/src/state/store.js +300 -0
- package/src/tags/index.js +19 -0
- package/src/tannins/auth.js +396 -0
- package/src/test/dom.js +352 -0
- package/src/test/index.js +62 -0
- package/src/test/state.js +306 -0
- package/tools/a11y-audit.js +487 -0
- package/tools/analyzer.js +315 -0
- package/tools/audit.js +706 -0
- package/tools/builder.js +1422 -0
- package/tools/css-extract.js +188 -0
- package/tools/dev-server.js +316 -0
- package/tools/dts-gen.js +1260 -0
- package/tools/figma-components.js +329 -0
- package/tools/figma-patterns.js +516 -0
- package/tools/figma-plugin/code.js +453 -0
- package/tools/figma-plugin/manifest.json +14 -0
- package/tools/figma-plugin/ui.html +268 -0
- package/tools/figma-render.js +293 -0
- package/tools/figma-tokens.js +712 -0
- package/tools/figma-upload.js +318 -0
- package/tools/generate.js +738 -0
- package/tools/icons.js +133 -0
- package/tools/init-templates.js +265 -0
- package/tools/install-hooks.sh +5 -0
- package/tools/migrations/0.5.0.js +53 -0
- package/tools/migrations/0.6.0.js +95 -0
- package/tools/minify.js +170 -0
- package/tools/pre-commit +4 -0
- package/tools/registry.js +662 -0
- package/tools/reset-playground.js +61 -0
- package/tools/starter-templates/content-site/app.js +49 -0
- package/tools/starter-templates/content-site/essence.js +19 -0
- package/tools/starter-templates/content-site/pages.js +31 -0
- package/tools/starter-templates/ecommerce/app.js +50 -0
- package/tools/starter-templates/ecommerce/essence.js +19 -0
- package/tools/starter-templates/ecommerce/pages.js +31 -0
- package/tools/starter-templates/landing-page/app.js +38 -0
- package/tools/starter-templates/landing-page/essence.js +18 -0
- package/tools/starter-templates/landing-page/pages.js +21 -0
- package/tools/starter-templates/portfolio/app.js +45 -0
- package/tools/starter-templates/portfolio/essence.js +19 -0
- package/tools/starter-templates/portfolio/pages.js +33 -0
- package/tools/starter-templates/saas-dashboard/app.js +70 -0
- package/tools/starter-templates/saas-dashboard/essence.js +19 -0
- package/tools/starter-templates/saas-dashboard/pages.js +31 -0
- package/tools/verify-pack.js +203 -0
- package/types/chart.d.ts +77 -0
- package/types/components.d.ts +587 -0
- package/types/core.d.ts +89 -0
- package/types/css.d.ts +149 -0
- package/types/data.d.ts +238 -0
- package/types/form.d.ts +164 -0
- package/types/i18n.d.ts +51 -0
- package/types/icons.d.ts +27 -0
- package/types/index.d.ts +13 -0
- package/types/router.d.ts +116 -0
- package/types/ssr.d.ts +102 -0
- package/types/state.d.ts +83 -0
- package/types/tags.d.ts +62 -0
- package/types/tannins.d.ts +63 -0
- package/types/test.d.ts +48 -0
package/tools/icons.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generic SVG icon importer — reads a directory of SVG files, extracts inner
|
|
4
|
+
* content, and writes a JS module that can be loaded via registerIcons().
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node tools/icons.js <svg-dir> [output.js]
|
|
8
|
+
*
|
|
9
|
+
* Examples:
|
|
10
|
+
* node tools/icons.js ./my-icons → src/icons/custom.js
|
|
11
|
+
* node tools/icons.js ./brand-icons brand.js → src/icons/brand.js
|
|
12
|
+
* node tools/icons.js node_modules/some-icons/svgs → src/icons/custom.js
|
|
13
|
+
*
|
|
14
|
+
* The generated file exports an ICONS object. Import and register in your app:
|
|
15
|
+
* import { ICONS } from './icons/custom.js';
|
|
16
|
+
* import { registerIcons } from 'decantr/icons';
|
|
17
|
+
* registerIcons(ICONS);
|
|
18
|
+
*
|
|
19
|
+
* Also regenerates src/registry/icons.json with the current essential icon list.
|
|
20
|
+
*
|
|
21
|
+
* Run with no arguments to just regenerate the registry from essential.js:
|
|
22
|
+
* node tools/icons.js
|
|
23
|
+
*/
|
|
24
|
+
import { readFileSync, writeFileSync, readdirSync, mkdirSync, existsSync } from 'node:fs';
|
|
25
|
+
import { join, dirname, basename, resolve } from 'node:path';
|
|
26
|
+
import { fileURLToPath } from 'node:url';
|
|
27
|
+
|
|
28
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
const root = join(__dirname, '..');
|
|
30
|
+
const iconsOutDir = join(root, 'src', 'icons');
|
|
31
|
+
const registryDir = join(root, 'src', 'registry');
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Extract the inner content of an SVG, stripping the outer <svg> wrapper.
|
|
35
|
+
*/
|
|
36
|
+
function extractInner(svgContent) {
|
|
37
|
+
let s = svgContent.replace(/<!--[\s\S]*?-->/g, '');
|
|
38
|
+
s = s.replace(/<svg[^>]*>/, '').replace(/<\/svg>\s*$/, '');
|
|
39
|
+
s = s.replace(/\s+/g, ' ').trim();
|
|
40
|
+
return s;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Read a directory of SVG files and return { name: innerContent } map.
|
|
45
|
+
*/
|
|
46
|
+
function importSVGs(dir) {
|
|
47
|
+
const absDir = resolve(dir);
|
|
48
|
+
if (!existsSync(absDir)) {
|
|
49
|
+
console.error(`Directory not found: ${absDir}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const files = readdirSync(absDir).filter(f => f.endsWith('.svg')).sort();
|
|
53
|
+
if (files.length === 0) {
|
|
54
|
+
console.error(`No .svg files found in: ${absDir}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const icons = {};
|
|
58
|
+
for (const file of files) {
|
|
59
|
+
const name = basename(file, '.svg');
|
|
60
|
+
const svg = readFileSync(join(absDir, file), 'utf-8');
|
|
61
|
+
icons[name] = extractInner(svg);
|
|
62
|
+
}
|
|
63
|
+
return icons;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Write icon data as a JS module.
|
|
68
|
+
*/
|
|
69
|
+
function writeIconModule(icons, outPath) {
|
|
70
|
+
const entries = Object.entries(icons)
|
|
71
|
+
.map(([k, v]) => ` '${k}': '${v.replace(/'/g, "\\'")}'`)
|
|
72
|
+
.join(',\n');
|
|
73
|
+
writeFileSync(outPath,
|
|
74
|
+
`// Generated by tools/icons.js — do not edit\nexport const ICONS = {\n${entries}\n};\n`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Regenerate src/registry/icons.json from essential.js.
|
|
80
|
+
*/
|
|
81
|
+
function updateRegistry() {
|
|
82
|
+
const essentialPath = join(iconsOutDir, 'essential.js');
|
|
83
|
+
if (!existsSync(essentialPath)) return;
|
|
84
|
+
|
|
85
|
+
const source = readFileSync(essentialPath, 'utf-8');
|
|
86
|
+
const names = [];
|
|
87
|
+
const re = /'([a-z][a-z0-9-]*)'\s*:/g;
|
|
88
|
+
let m;
|
|
89
|
+
while ((m = re.exec(source)) !== null) names.push(m[1]);
|
|
90
|
+
|
|
91
|
+
mkdirSync(registryDir, { recursive: true });
|
|
92
|
+
writeFileSync(join(registryDir, 'icons.json'), JSON.stringify({
|
|
93
|
+
generated: new Date().toISOString().split('T')[0],
|
|
94
|
+
source: 'decantr original icons',
|
|
95
|
+
essential: names.sort(),
|
|
96
|
+
essentialCount: names.length,
|
|
97
|
+
api: {
|
|
98
|
+
'icon(name, opts?)': 'Render icon as <span> with CSS mask-image',
|
|
99
|
+
'registerIcon(name, svgInner)': 'Add a single custom icon',
|
|
100
|
+
'registerIcons(map)': 'Bulk-register icons from { name: svgInner }',
|
|
101
|
+
'hasIcon(name)': 'Check if icon is available',
|
|
102
|
+
'getIconNames()': 'List all available icon names'
|
|
103
|
+
},
|
|
104
|
+
importGuide: 'node tools/icons.js <svg-dir> [output.js] — import SVG directory as JS module'
|
|
105
|
+
}, null, 2) + '\n');
|
|
106
|
+
|
|
107
|
+
console.log(`Registry: src/registry/icons.json (${names.length} essential icons)`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --- Main ---
|
|
111
|
+
|
|
112
|
+
const args = process.argv.slice(2);
|
|
113
|
+
|
|
114
|
+
if (args.length >= 1) {
|
|
115
|
+
// Import mode: read SVG directory, write JS module
|
|
116
|
+
const svgDir = args[0];
|
|
117
|
+
const outName = args[1] || 'custom.js';
|
|
118
|
+
const outPath = outName.includes('/') ? resolve(outName) : join(iconsOutDir, outName);
|
|
119
|
+
|
|
120
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
121
|
+
const icons = importSVGs(svgDir);
|
|
122
|
+
writeIconModule(icons, outPath);
|
|
123
|
+
|
|
124
|
+
console.log(`Imported ${Object.keys(icons).length} icons from ${svgDir}`);
|
|
125
|
+
console.log(` → ${outPath}`);
|
|
126
|
+
console.log(`\nUsage in your app:`);
|
|
127
|
+
console.log(` import { ICONS } from './icons/${basename(outPath)}';`);
|
|
128
|
+
console.log(` import { registerIcons } from 'decantr/icons';`);
|
|
129
|
+
console.log(` registerIcons(ICONS);`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Always update registry
|
|
133
|
+
updateRegistry();
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template utilities for `decantr init`.
|
|
3
|
+
* Generates the minimal project skeleton files.
|
|
4
|
+
*/
|
|
5
|
+
import { readFile } from 'node:fs/promises';
|
|
6
|
+
import { join, dirname, resolve } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const frameworkRoot = resolve(__dirname, '..');
|
|
11
|
+
|
|
12
|
+
/** Minimal CSS custom properties for initial page render (before JS hydrates the full token set) */
|
|
13
|
+
export const THEME_CSS = {
|
|
14
|
+
'dark': ':root{--d-bg:#060918;--d-fg:#fafafa;--d-primary:#6500C6;--d-primary-hover:#7a1ad4;--d-muted:#8892a4;--d-border:rgba(255,255,255,0.08);--d-surface-1:rgba(12,15,40,0.55);--d-error:#D80F4A;--d-success:#00C388;--d-warning:#FDA303;--d-overlay:rgba(0,0,0,0.6);--d-radius:12px;--d-radius-lg:16px;--d-font:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif}',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function packageJson(name) {
|
|
18
|
+
return JSON.stringify({
|
|
19
|
+
name,
|
|
20
|
+
version: '0.1.0',
|
|
21
|
+
type: 'module',
|
|
22
|
+
scripts: {
|
|
23
|
+
dev: 'decantr dev',
|
|
24
|
+
build: 'decantr build',
|
|
25
|
+
test: 'decantr test'
|
|
26
|
+
},
|
|
27
|
+
dependencies: {
|
|
28
|
+
decantr: '^0.4.0'
|
|
29
|
+
}
|
|
30
|
+
}, null, 2);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function configJson(name) {
|
|
34
|
+
return JSON.stringify({
|
|
35
|
+
$schema: 'https://decantr.ai/schemas/config.v2.json',
|
|
36
|
+
name,
|
|
37
|
+
style: 'auradecantism',
|
|
38
|
+
mode: 'dark',
|
|
39
|
+
router: 'hash',
|
|
40
|
+
dev: { port: 4200 },
|
|
41
|
+
build: {
|
|
42
|
+
outDir: 'dist',
|
|
43
|
+
sourcemap: true,
|
|
44
|
+
treeShake: true,
|
|
45
|
+
codeSplit: true,
|
|
46
|
+
purgeCSS: true,
|
|
47
|
+
incremental: true,
|
|
48
|
+
analyze: true
|
|
49
|
+
}
|
|
50
|
+
}, null, 2);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function indexHtml(name) {
|
|
54
|
+
return `<!DOCTYPE html>
|
|
55
|
+
<html lang="en">
|
|
56
|
+
<head>
|
|
57
|
+
<meta charset="UTF-8">
|
|
58
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
59
|
+
<title>${name}</title>
|
|
60
|
+
<style>${THEME_CSS.dark}*{margin:0;box-sizing:border-box}body{font-family:var(--d-font);color:var(--d-fg);background:var(--d-bg);min-height:100vh}a{color:var(--d-primary);text-decoration:none}a:hover{color:var(--d-primary-hover)}</style>
|
|
61
|
+
</head>
|
|
62
|
+
<body>
|
|
63
|
+
<div id="app"></div>
|
|
64
|
+
<script type="module" src="/src/app.js"></script>
|
|
65
|
+
</body>
|
|
66
|
+
</html>`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function manifestJson(name) {
|
|
70
|
+
return JSON.stringify({
|
|
71
|
+
$schema: 'https://decantr.ai/schemas/manifest.v2.json',
|
|
72
|
+
version: '0.4.0',
|
|
73
|
+
name,
|
|
74
|
+
entrypoint: 'src/app.js',
|
|
75
|
+
shell: 'public/index.html',
|
|
76
|
+
mountTarget: '#app'
|
|
77
|
+
}, null, 2);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function claudeMd(name) {
|
|
81
|
+
return `# ${name}
|
|
82
|
+
|
|
83
|
+
Built with [decantr](https://decantr.ai) v0.4.0 — AI-first web framework.
|
|
84
|
+
|
|
85
|
+
## Quick Start
|
|
86
|
+
|
|
87
|
+
**Read this first:** \`node_modules/decantr/reference/llm-primer.md\` — single-file guide with all imports, top atoms with Tailwind equivalences, component signatures, Chart API, skeleton code, and pattern examples. This covers 80% of code generation needs.
|
|
88
|
+
|
|
89
|
+
## Project Structure
|
|
90
|
+
|
|
91
|
+
- \`src/app.js\` — Entry point, mounts the app to \`#app\`
|
|
92
|
+
- \`src/pages/\` — Route page components
|
|
93
|
+
- \`src/components/\` — Project-specific reusable components
|
|
94
|
+
- \`public/index.html\` — HTML shell with theme CSS variables
|
|
95
|
+
- \`decantr.config.json\` — Project configuration
|
|
96
|
+
- \`decantr.essence.json\` — Project DNA (generated during Decantation Process)
|
|
97
|
+
- \`AGENTS.md\` — Framework translation layer (read this for API equivalences)
|
|
98
|
+
|
|
99
|
+
## The Decantation Process
|
|
100
|
+
|
|
101
|
+
Follow this process for ALL new projects and major feature additions:
|
|
102
|
+
|
|
103
|
+
### Stage 1: POUR — User expresses intent in natural language
|
|
104
|
+
### Stage 2: SETTLE — Decompose into five layers:
|
|
105
|
+
- **Terroir**: Domain archetype → read \`node_modules/decantr/src/registry/archetypes/\`
|
|
106
|
+
- **Vintage**: Style + mode + recipe → read \`node_modules/decantr/src/registry/recipe-*.json\`
|
|
107
|
+
- **Character**: Brand personality traits (e.g. "tactical", "minimal", "playful")
|
|
108
|
+
- **Structure**: Page/view map with skeleton assignments
|
|
109
|
+
- **Tannins**: Functional systems (auth, search, payments, etc.)
|
|
110
|
+
### Stage 3: CLARIFY — Write \`decantr.essence.json\` and confirm with user
|
|
111
|
+
### Stage 4: DECANT — Resolve each page's Blend (spatial arrangement)
|
|
112
|
+
Read the archetype's \`default_blend\` for each page. Copy into the Essence's \`blend\` array, then customize.
|
|
113
|
+
|
|
114
|
+
**Blend format**: Each \`blend\` is an ordered array of rows:
|
|
115
|
+
- \`"pattern-id"\` — full-width pattern row
|
|
116
|
+
- \`{ "cols": ["a", "b"], "at": "lg" }\` — equal-width side-by-side, collapse below \`lg\`
|
|
117
|
+
- \`{ "cols": ["a", "b"], "span": { "a": 3 }, "at": "md" }\` — weighted columns (a=3fr, b=1fr)
|
|
118
|
+
|
|
119
|
+
Optional \`surface\` on each structure entry sets page container atoms (default: \`_flex _col _gap4 _p4 _overflow[auto] _flex1\`).
|
|
120
|
+
|
|
121
|
+
### Stage 5: SERVE — Generate code from Blend specs
|
|
122
|
+
For each page, read its \`blend\` array and apply:
|
|
123
|
+
1. Create page container with \`surface\` atoms
|
|
124
|
+
2. String rows → full-width pattern (use pattern's \`default_blend.atoms\`)
|
|
125
|
+
3. \`{ cols }\` rows → \`_grid _gc{N} _gap4\` wrapper, collapse below \`at\` breakpoint
|
|
126
|
+
4. \`{ cols, span }\` rows → \`_gc{total}\` grid, each pattern gets \`_span{weight}\`
|
|
127
|
+
5. Apply recipe wrappers per \`recipe_overrides\`
|
|
128
|
+
|
|
129
|
+
### Ongoing: AGE — Read Essence before every change. Guard against drift.
|
|
130
|
+
|
|
131
|
+
### Cork Rules (Anti-Drift)
|
|
132
|
+
Before writing ANY code, read \`decantr.essence.json\`. Verify:
|
|
133
|
+
1. Style matches the Vintage. Do not switch styles.
|
|
134
|
+
2. Page exists in the Structure. If new, add it to the Essence first.
|
|
135
|
+
3. Layout follows the page's Blend. Do not freestyle spatial arrangement.
|
|
136
|
+
4. Composition follows the active Recipe. Do not freestyle decoration.
|
|
137
|
+
5. Density and tone match the Character.
|
|
138
|
+
If a request conflicts with the Essence, flag the conflict and ask for confirmation.
|
|
139
|
+
|
|
140
|
+
## Essence Schema
|
|
141
|
+
|
|
142
|
+
You MUST create \`decantr.essence.json\` during CLARIFY. Do NOT proceed to DECANT without it.
|
|
143
|
+
|
|
144
|
+
**Simple (single domain):**
|
|
145
|
+
\`\`\`json
|
|
146
|
+
{
|
|
147
|
+
"terroir": "saas-dashboard",
|
|
148
|
+
"vintage": { "style": "command-center", "mode": "dark", "recipe": "command-center", "shape": "sharp" },
|
|
149
|
+
"character": ["tactical", "data-dense"],
|
|
150
|
+
"vessel": { "type": "spa", "routing": "hash" },
|
|
151
|
+
"structure": [
|
|
152
|
+
{ "id": "overview", "skeleton": "sidebar-main", "blend": ["kpi-grid", "data-table"] }
|
|
153
|
+
],
|
|
154
|
+
"tannins": ["auth", "realtime-data"],
|
|
155
|
+
"cork": { "enforce_style": true, "enforce_recipe": true }
|
|
156
|
+
}
|
|
157
|
+
\`\`\`
|
|
158
|
+
|
|
159
|
+
**Sectioned (multi-domain):**
|
|
160
|
+
\`\`\`json
|
|
161
|
+
{
|
|
162
|
+
"vessel": { "type": "spa", "routing": "hash" },
|
|
163
|
+
"character": ["professional", "technical"],
|
|
164
|
+
"sections": [
|
|
165
|
+
{
|
|
166
|
+
"id": "brand",
|
|
167
|
+
"path": "/",
|
|
168
|
+
"terroir": "portfolio",
|
|
169
|
+
"vintage": { "style": "glassmorphism", "mode": "dark" },
|
|
170
|
+
"structure": [{ "id": "home", "skeleton": "full-bleed", "blend": ["hero", "cta-section"] }],
|
|
171
|
+
"tannins": ["analytics"]
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
"shared_tannins": ["auth"],
|
|
175
|
+
"cork": { "enforce_style": true, "enforce_sections": true }
|
|
176
|
+
}
|
|
177
|
+
\`\`\`
|
|
178
|
+
|
|
179
|
+
## Framework Imports
|
|
180
|
+
|
|
181
|
+
\`\`\`js
|
|
182
|
+
import { tags } from 'decantr/tags';
|
|
183
|
+
import { h, text, cond, list, mount, onMount, onDestroy } from 'decantr/core';
|
|
184
|
+
import { createSignal, createEffect, createMemo, createStore, batch } from 'decantr/state';
|
|
185
|
+
import { createRouter, link, navigate, useRoute } from 'decantr/router';
|
|
186
|
+
import { css, setStyle, setMode } from 'decantr/css';
|
|
187
|
+
import { Button, Input, Card, Modal, Tabs, ... } from 'decantr/components';
|
|
188
|
+
\`\`\`
|
|
189
|
+
|
|
190
|
+
## Styles
|
|
191
|
+
|
|
192
|
+
Available: \`auradecantism\` (default), \`clean\`, \`retro\`, \`glassmorphism\`, \`command-center\`
|
|
193
|
+
Modes: \`light\`, \`dark\`, \`auto\`
|
|
194
|
+
Shapes: \`sharp\`, \`rounded\`, \`pill\`
|
|
195
|
+
|
|
196
|
+
## Registry
|
|
197
|
+
|
|
198
|
+
- \`node_modules/decantr/src/registry/index.json\` — Full API catalog
|
|
199
|
+
- \`node_modules/decantr/src/registry/components.json\` — Component props/types
|
|
200
|
+
- \`node_modules/decantr/src/registry/archetypes/\` — Domain archetypes
|
|
201
|
+
- \`node_modules/decantr/src/registry/patterns/\` — Experience patterns
|
|
202
|
+
- \`node_modules/decantr/src/registry/recipe-*.json\` — Visual language recipes
|
|
203
|
+
|
|
204
|
+
## Commands
|
|
205
|
+
|
|
206
|
+
- \`npx decantr dev\` — Dev server with hot reload
|
|
207
|
+
- \`npx decantr build\` — Production build to \`dist/\`
|
|
208
|
+
- \`npx decantr test\` — Run tests
|
|
209
|
+
- \`npx decantr validate\` — Validate \`decantr.essence.json\`
|
|
210
|
+
- \`npx decantr generate\` — Generate code from \`decantr.essence.json\`
|
|
211
|
+
`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function appJs() {
|
|
215
|
+
return `import { mount } from 'decantr/core';
|
|
216
|
+
import { tags } from 'decantr/tags';
|
|
217
|
+
import { css, define, setStyle, setMode } from 'decantr/css';
|
|
218
|
+
import { Image, CodeBlock } from 'decantr/components';
|
|
219
|
+
|
|
220
|
+
const { div, span, p, h1 } = tags;
|
|
221
|
+
|
|
222
|
+
// Style and mode are set here for initial render.
|
|
223
|
+
// After CLARIFY, these should match your essence vintage.
|
|
224
|
+
// For sectioned essences, add a router.beforeEach guard to switch per section.
|
|
225
|
+
setStyle('auradecantism');
|
|
226
|
+
setMode('dark');
|
|
227
|
+
|
|
228
|
+
define('_pink', 'color:#FE4474');
|
|
229
|
+
|
|
230
|
+
function App() {
|
|
231
|
+
return div({ class: 'd-mesh ' + css('_flex _col _aic _jcc _minhscreen _gap6 _p6') },
|
|
232
|
+
Image({ src: '/images/logo-portrait.svg', alt: 'decantr', width: '120px', height: '120px', fit: 'contain', class: css('_mb2') }),
|
|
233
|
+
h1({ class: css('_fw[900] _ls[-0.04em] _lh[1] _t48 _fgfg') },
|
|
234
|
+
span('decantr'),
|
|
235
|
+
span({ class: css('_pink') }, '.'),
|
|
236
|
+
span('a'),
|
|
237
|
+
span({ class: css('_pink') }, 'i')
|
|
238
|
+
),
|
|
239
|
+
p({ class: css('_t16 _fgmuted _tac') }, "You're all set up. Prompt your AI to start building."),
|
|
240
|
+
CodeBlock({ language: 'bash', copyable: false, class: css('_w[480px] _mw[100%]') },
|
|
241
|
+
'$ npx decantr dev\\n\\nThen tell your AI what to build:\\n\\n"Build me a SaaS dashboard with KPIs,\\n charts, and user management"'
|
|
242
|
+
)
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
mount(document.getElementById('app'), App);
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function essenceJson() {
|
|
251
|
+
return JSON.stringify({
|
|
252
|
+
"_comment": "Project DNA — generated by decantr init. Fill in during CLARIFY stage.",
|
|
253
|
+
"terroir": null,
|
|
254
|
+
"vintage": { "style": "auradecantism", "mode": "dark", "recipe": null, "shape": "rounded" },
|
|
255
|
+
"character": [],
|
|
256
|
+
"vessel": { "type": "spa", "routing": "hash" },
|
|
257
|
+
"structure": [],
|
|
258
|
+
"tannins": [],
|
|
259
|
+
"cork": {}
|
|
260
|
+
}, null, 2);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export async function agentsMd() {
|
|
264
|
+
return readFile(join(frameworkRoot, 'AGENTS.md'), 'utf-8');
|
|
265
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: 0.5.0
|
|
3
|
+
*
|
|
4
|
+
* Breaking changes in v0.5.0:
|
|
5
|
+
* - "organs" renamed to "tannins"
|
|
6
|
+
* - "anatomy" renamed to "structure"
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const version = '0.5.0';
|
|
10
|
+
|
|
11
|
+
export function migrate(essence) {
|
|
12
|
+
const result = { ...essence };
|
|
13
|
+
|
|
14
|
+
// Rename organs → tannins (top-level)
|
|
15
|
+
if ('organs' in result && !('tannins' in result)) {
|
|
16
|
+
result.tannins = result.organs;
|
|
17
|
+
delete result.organs;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Rename anatomy → structure (top-level)
|
|
21
|
+
if ('anatomy' in result && !('structure' in result)) {
|
|
22
|
+
result.structure = result.anatomy;
|
|
23
|
+
delete result.anatomy;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Handle sectioned essences
|
|
27
|
+
if (Array.isArray(result.sections)) {
|
|
28
|
+
result.sections = result.sections.map(section => {
|
|
29
|
+
const s = { ...section };
|
|
30
|
+
|
|
31
|
+
if ('organs' in s && !('tannins' in s)) {
|
|
32
|
+
s.tannins = s.organs;
|
|
33
|
+
delete s.organs;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if ('anatomy' in s && !('structure' in s)) {
|
|
37
|
+
s.structure = s.anatomy;
|
|
38
|
+
delete s.anatomy;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return s;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Rename shared_organs → shared_tannins
|
|
46
|
+
if ('shared_organs' in result && !('shared_tannins' in result)) {
|
|
47
|
+
result.shared_tannins = result.shared_organs;
|
|
48
|
+
delete result.shared_organs;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
result.version = '0.5.0';
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: 0.6.0
|
|
3
|
+
*
|
|
4
|
+
* Breaking changes in v0.6.0:
|
|
5
|
+
* - 13 domain-specific patterns consolidated into presets on generic patterns
|
|
6
|
+
* - Pattern references in blend arrays must use { pattern, preset, as } format
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const version = '0.6.0';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Map of old pattern names to their v2 preset equivalents.
|
|
13
|
+
* Keys are the old string references found in blend arrays.
|
|
14
|
+
* Values are the new { pattern, preset?, as? } objects.
|
|
15
|
+
*/
|
|
16
|
+
const PATTERN_RENAMES = {
|
|
17
|
+
'recipe-hero': { pattern: 'hero', preset: 'image-overlay', as: 'recipe-hero' },
|
|
18
|
+
'cookbook-hero': { pattern: 'hero', preset: 'image-overlay-compact', as: 'cookbook-hero' },
|
|
19
|
+
'product-grid': { pattern: 'card-grid', preset: 'product', as: 'product-grid' },
|
|
20
|
+
'recipe-card-grid': { pattern: 'card-grid', preset: 'content', as: 'recipe-card-grid' },
|
|
21
|
+
'cookbook-grid': { pattern: 'card-grid', preset: 'collection', as: 'cookbook-grid' },
|
|
22
|
+
'feature-grid': { pattern: 'card-grid', preset: 'icon', as: 'feature-grid' },
|
|
23
|
+
'profile-header': { pattern: 'detail-header', preset: 'profile', as: 'profile-header' },
|
|
24
|
+
'recipe-stats-bar': { pattern: 'stats-bar' },
|
|
25
|
+
'recipe-ingredients': { pattern: 'checklist-card' },
|
|
26
|
+
'recipe-instructions': { pattern: 'steps-card' },
|
|
27
|
+
'nutrition-card': { pattern: 'stat-card' },
|
|
28
|
+
'recipe-form-simple': { pattern: 'form-sections', preset: 'creation', as: 'recipe-form-simple' },
|
|
29
|
+
'recipe-form-chef': { pattern: 'form-sections', preset: 'structured', as: 'recipe-form-chef' },
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Convert a single blend item if it matches an old pattern name.
|
|
34
|
+
*/
|
|
35
|
+
function migrateBlendItem(item) {
|
|
36
|
+
if (typeof item === 'string' && item in PATTERN_RENAMES) {
|
|
37
|
+
return { ...PATTERN_RENAMES[item] };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Handle cols arrays — migrate column references inside
|
|
41
|
+
if (item && typeof item === 'object' && Array.isArray(item.cols)) {
|
|
42
|
+
const newCols = item.cols.map(col =>
|
|
43
|
+
(typeof col === 'string' && col in PATTERN_RENAMES) ? col : col
|
|
44
|
+
);
|
|
45
|
+
// Check if any col references need migrating (they stay as strings in cols)
|
|
46
|
+
// Cols are identifiers, not pattern references — no migration needed
|
|
47
|
+
return item;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return item;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Migrate all blend arrays in a structure array.
|
|
55
|
+
*/
|
|
56
|
+
function migrateStructure(structure) {
|
|
57
|
+
if (!Array.isArray(structure)) return structure;
|
|
58
|
+
|
|
59
|
+
return structure.map(page => {
|
|
60
|
+
if (!page.blend && !page.patterns) return page;
|
|
61
|
+
|
|
62
|
+
const blendKey = page.blend ? 'blend' : 'patterns';
|
|
63
|
+
const blend = page[blendKey];
|
|
64
|
+
|
|
65
|
+
if (!Array.isArray(blend)) return page;
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
...page,
|
|
69
|
+
[blendKey]: blend.map(migrateBlendItem),
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function migrate(essence) {
|
|
75
|
+
const result = { ...essence };
|
|
76
|
+
|
|
77
|
+
// Migrate top-level structure
|
|
78
|
+
if (Array.isArray(result.structure)) {
|
|
79
|
+
result.structure = migrateStructure(result.structure);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Migrate sectioned essences
|
|
83
|
+
if (Array.isArray(result.sections)) {
|
|
84
|
+
result.sections = result.sections.map(section => {
|
|
85
|
+
if (!Array.isArray(section.structure)) return section;
|
|
86
|
+
return {
|
|
87
|
+
...section,
|
|
88
|
+
structure: migrateStructure(section.structure),
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
result.version = '0.6.0';
|
|
94
|
+
return result;
|
|
95
|
+
}
|