@palate/cli 0.0.0-stage → 0.0.1
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/LICENSE +20 -0
- package/README.md +151 -2
- package/bin/palate.mjs +894 -0
- package/kb/anatomy/data-table.md +24 -0
- package/kb/anatomy/empty-state.md +16 -0
- package/kb/anatomy/filter-toolbar.md +19 -0
- package/kb/anatomy/meter.md +16 -0
- package/kb/anatomy/product-window.md +18 -0
- package/kb/anatomy/settings-page.md +18 -0
- package/kb/anatomy/status-indicator.md +17 -0
- package/kb/anatomy/toast-undo.md +16 -0
- package/kb/bench/briefs.json +618 -0
- package/kb/craft/SOURCES.md +48 -0
- package/kb/craft/creativity.md +55 -0
- package/kb/craft/details.md +75 -0
- package/kb/craft/landing-demos.md +46 -0
- package/kb/craft/mistakes.md +73 -0
- package/kb/craft/motion.md +53 -0
- package/kb/craft/principles.md +68 -0
- package/kb/index/discovery.json +1 -0
- package/kb/inspiration/index.json +79 -0
- package/kb/moves/animated-comparison-table.md +58 -0
- package/kb/moves/before-after-slider.md +58 -0
- package/kb/moves/bento-live-tiles.md +57 -0
- package/kb/moves/calm-motion-system.md +63 -0
- package/kb/moves/canvas-grain.md +58 -0
- package/kb/moves/changelog-as-magazine.md +56 -0
- package/kb/moves/code-drawn-diagram.md +56 -0
- package/kb/moves/colophon.md +54 -0
- package/kb/moves/command-palette-site-nav.md +58 -0
- package/kb/moves/confetti-free-celebration.md +58 -0
- package/kb/moves/console-easter-egg.md +57 -0
- package/kb/moves/copy-confirmation.md +57 -0
- package/kb/moves/cursor-reactive-light.md +57 -0
- package/kb/moves/dark-band-contrast-section.md +55 -0
- package/kb/moves/designed-dark-mode.md +58 -0
- package/kb/moves/duotone-photography.md +55 -0
- package/kb/moves/editorial-drop-cap.md +58 -0
- package/kb/moves/editorial-grid-pull-quotes.md +55 -0
- package/kb/moves/engraving-colour-lens.md +64 -0
- package/kb/moves/explorable-control.md +57 -0
- package/kb/moves/feature-films-on-painted-stages.md +58 -0
- package/kb/moves/first-win-onboarding.md +59 -0
- package/kb/moves/footer-live-status.md +56 -0
- package/kb/moves/footer-rewards-the-end.md +58 -0
- package/kb/moves/generative-brand-pattern.md +55 -0
- package/kb/moves/gentle-error-nudge.md +59 -0
- package/kb/moves/halftone-dot-imagery.md +55 -0
- package/kb/moves/hand-drawn-annotations.md +55 -0
- package/kb/moves/hero-paints-itself-in.md +65 -0
- package/kb/moves/honest-progress-narration.md +58 -0
- package/kb/moves/honest-testimonial-wall.md +57 -0
- package/kb/moves/inline-sparklines-in-copy.md +57 -0
- package/kb/moves/inspect-our-taste.md +58 -0
- package/kb/moves/interactive-pricing-calculator.md +59 -0
- package/kb/moves/isometric-illustration-system.md +56 -0
- package/kb/moves/kinetic-variable-type.md +57 -0
- package/kb/moves/konami-secret.md +59 -0
- package/kb/moves/layered-paper-depth.md +56 -0
- package/kb/moves/lightweight-3d-object-hero.md +59 -0
- package/kb/moves/line-engraving-from-image.md +63 -0
- package/kb/moves/link-preview-peek.md +57 -0
- package/kb/moves/live-data-ticker.md +61 -0
- package/kb/moves/made-with-proof.md +51 -0
- package/kb/moves/magnetic-buttons.md +55 -0
- package/kb/moves/marquee-done-right.md +58 -0
- package/kb/moves/moves.yaml +450 -0
- package/kb/moves/museum-painting-backdrop.md +59 -0
- package/kb/moves/one-colour-brand-world.md +56 -0
- package/kb/moves/playful-404.md +58 -0
- package/kb/moves/pointer-highlight-word.md +56 -0
- package/kb/moves/product-in-context.md +56 -0
- package/kb/moves/real-world-data.md +58 -0
- package/kb/moves/referral-reward-reveal.md +59 -0
- package/kb/moves/riso-print-texture.md +57 -0
- package/kb/moves/screenshot-detail-crops.md +56 -0
- package/kb/moves/scroll-progress-chapters.md +57 -0
- package/kb/moves/scroll-scrubbed-product-film.md +62 -0
- package/kb/moves/self-playing-product-demo.md +61 -0
- package/kb/moves/shape-continuity-morph.md +58 -0
- package/kb/moves/sliding-nav-indicator.md +58 -0
- package/kb/moves/sticky-scroll-story.md +56 -0
- package/kb/moves/story-led-long-scroll.md +56 -0
- package/kb/moves/tactile-button.md +63 -0
- package/kb/moves/technical-drawing-lines.md +57 -0
- package/kb/moves/time-of-day-theming.md +56 -0
- package/kb/moves/tiny-game-easter-egg.md +60 -0
- package/kb/moves/type-decode-reveal.md +57 -0
- package/kb/moves/typographic-numeral-hero.md +58 -0
- package/kb/moves/view-transitions-between-pages.md +57 -0
- package/kb/moves/waitlist-position-live.md +60 -0
- package/kb/patterns/app-team-members.md +27 -0
- package/kb/patterns/chat-agent-approvals.md +31 -0
- package/kb/patterns/commerce-product-page.md +34 -0
- package/kb/patterns/dashboard-entity-table.md +37 -0
- package/kb/patterns/dashboard-home-today.md +24 -0
- package/kb/patterns/docs-api-reference.md +31 -0
- package/kb/patterns/marketing-product-led-landing.md +30 -0
- package/kb/patterns/mobile-daily-checklist.md +26 -0
- package/kb/references/index.json +1 -0
- package/kb/references/thumbs/004c5e9072.jpg +0 -0
- package/kb/references/thumbs/008915eb21.jpg +0 -0
- package/kb/references/thumbs/0098cf66c2.jpg +0 -0
- package/kb/references/thumbs/012e61b31f.jpg +0 -0
- package/kb/references/thumbs/014d4e46fc.jpg +0 -0
- package/kb/references/thumbs/01e54f394a.jpg +0 -0
- package/kb/references/thumbs/01e99a579c.jpg +0 -0
- package/kb/references/thumbs/01fd014575.jpg +0 -0
- package/kb/references/thumbs/022778ca72.jpg +0 -0
- package/kb/references/thumbs/02717bf1e1.jpg +0 -0
- package/kb/references/thumbs/02a5cfdbe9.jpg +0 -0
- package/kb/references/thumbs/02c9823f8d.jpg +0 -0
- package/kb/references/thumbs/02f3974613.jpg +0 -0
- package/kb/references/thumbs/032427f1ce.jpg +0 -0
- package/kb/references/thumbs/03ba677d07.jpg +0 -0
- package/kb/references/thumbs/04f2476cfe.jpg +0 -0
- package/kb/references/thumbs/0563e7c52e.jpg +0 -0
- package/kb/references/thumbs/05fa6b8eef.jpg +0 -0
- package/kb/references/thumbs/065bce1517.jpg +0 -0
- package/kb/references/thumbs/06c017f113.jpg +0 -0
- package/kb/references/thumbs/0770e2d8d3.jpg +0 -0
- package/kb/references/thumbs/078a26c4cb.jpg +0 -0
- package/kb/references/thumbs/08679b6e84.jpg +0 -0
- package/kb/references/thumbs/088c45a34c.jpg +0 -0
- package/kb/references/thumbs/089e9fd4e4.jpg +0 -0
- package/kb/references/thumbs/08f773231e.jpg +0 -0
- package/kb/references/thumbs/093ca3e2e3.jpg +0 -0
- package/kb/references/thumbs/0963320f74.jpg +0 -0
- package/kb/references/thumbs/09fc1ded85.jpg +0 -0
- package/kb/references/thumbs/0a368f3963.jpg +0 -0
- package/kb/references/thumbs/0a9e0e5201.jpg +0 -0
- package/kb/references/thumbs/0aace67ee7.jpg +0 -0
- package/kb/references/thumbs/0b61ecf793.jpg +0 -0
- package/kb/references/thumbs/0bde18ae09.jpg +0 -0
- package/kb/references/thumbs/0bf7ee8143.jpg +0 -0
- package/kb/references/thumbs/0ca20a13e0.jpg +0 -0
- package/kb/references/thumbs/0cdc0198c4.jpg +0 -0
- package/kb/references/thumbs/0cf9790b28.jpg +0 -0
- package/kb/references/thumbs/0d0202904b.jpg +0 -0
- package/kb/references/thumbs/0d053aba18.jpg +0 -0
- package/kb/references/thumbs/0d0c2c13d2.jpg +0 -0
- package/kb/references/thumbs/0d30be99b0.jpg +0 -0
- package/kb/references/thumbs/0d503da1b7.jpg +0 -0
- package/kb/references/thumbs/0ddad7b740.jpg +0 -0
- package/kb/references/thumbs/0e0a148101.jpg +0 -0
- package/kb/references/thumbs/0e401b6b75.jpg +0 -0
- package/kb/references/thumbs/0e8f32ba0f.jpg +0 -0
- package/kb/references/thumbs/0ef1966a7d.jpg +0 -0
- package/kb/references/thumbs/0efd416d11.jpg +0 -0
- package/kb/references/thumbs/0f3af263ae.jpg +0 -0
- package/kb/references/thumbs/0f9644c3a5.jpg +0 -0
- package/kb/references/thumbs/1020552737.jpg +0 -0
- package/kb/references/thumbs/1037626587.jpg +0 -0
- package/kb/references/thumbs/10512b7e94.jpg +0 -0
- package/kb/references/thumbs/106dc4a7b5.jpg +0 -0
- package/kb/references/thumbs/108943f6f7.jpg +0 -0
- package/kb/references/thumbs/10a5551d51.jpg +0 -0
- package/kb/references/thumbs/10dd01076c.jpg +0 -0
- package/kb/references/thumbs/11b06e39da.jpg +0 -0
- package/kb/references/thumbs/11baa1ec83.jpg +0 -0
- package/kb/references/thumbs/11bbe12167.jpg +0 -0
- package/kb/references/thumbs/11ca37feb8.jpg +0 -0
- package/kb/references/thumbs/11f14d3f12.jpg +0 -0
- package/kb/references/thumbs/124530ceb5.jpg +0 -0
- package/kb/references/thumbs/1276402336.jpg +0 -0
- package/kb/references/thumbs/1280e5c9d6.jpg +0 -0
- package/kb/references/thumbs/12cac0d172.jpg +0 -0
- package/kb/references/thumbs/138be6623a.jpg +0 -0
- package/kb/references/thumbs/13a0c544d1.jpg +0 -0
- package/kb/references/thumbs/13a32cb94c.jpg +0 -0
- package/kb/references/thumbs/144035d641.jpg +0 -0
- package/kb/references/thumbs/1457e41924.jpg +0 -0
- package/kb/references/thumbs/14972e3a86.jpg +0 -0
- package/kb/references/thumbs/15a47f2e4c.jpg +0 -0
- package/kb/references/thumbs/15aa15d22e.jpg +0 -0
- package/kb/references/thumbs/160dcd24b7.jpg +0 -0
- package/kb/references/thumbs/1610978529.jpg +0 -0
- package/kb/references/thumbs/16db30ba37.jpg +0 -0
- package/kb/references/thumbs/16f19ae4f3.jpg +0 -0
- package/kb/references/thumbs/16f758b03f.jpg +0 -0
- package/kb/references/thumbs/1748e5b751.jpg +0 -0
- package/kb/references/thumbs/175d0b0e69.jpg +0 -0
- package/kb/references/thumbs/178f571a36.jpg +0 -0
- package/kb/references/thumbs/17c43dc025.jpg +0 -0
- package/kb/references/thumbs/17c7f05b50.jpg +0 -0
- package/kb/references/thumbs/17f97e61dd.jpg +0 -0
- package/kb/references/thumbs/1804e17365.jpg +0 -0
- package/kb/references/thumbs/195c162ed1.jpg +0 -0
- package/kb/references/thumbs/19e3699c29.jpg +0 -0
- package/kb/references/thumbs/1a48e0a4d5.jpg +0 -0
- package/kb/references/thumbs/1a6cda265e.jpg +0 -0
- package/kb/references/thumbs/1a7e012091.jpg +0 -0
- package/kb/references/thumbs/1a9173a172.jpg +0 -0
- package/kb/references/thumbs/1b34a6b4d2.jpg +0 -0
- package/kb/references/thumbs/1b5284f3b2.jpg +0 -0
- package/kb/references/thumbs/1b93fbd6d3.jpg +0 -0
- package/kb/references/thumbs/1c6728fef6.jpg +0 -0
- package/kb/references/thumbs/1c6f101618.jpg +0 -0
- package/kb/references/thumbs/1cf7011a94.jpg +0 -0
- package/kb/references/thumbs/1d1769e539.jpg +0 -0
- package/kb/references/thumbs/1d1fd23199.jpg +0 -0
- package/kb/references/thumbs/1d7b6602ac.jpg +0 -0
- package/kb/references/thumbs/1dbbc61b9e.jpg +0 -0
- package/kb/references/thumbs/1dbdd6d21d.jpg +0 -0
- package/kb/references/thumbs/1dd873744b.jpg +0 -0
- package/kb/references/thumbs/1df4dd7cf9.jpg +0 -0
- package/kb/references/thumbs/1ece8c35b8.jpg +0 -0
- package/kb/references/thumbs/1f0e5b9aa4.jpg +0 -0
- package/kb/references/thumbs/1f1419651b.jpg +0 -0
- package/kb/references/thumbs/1f2a6da92d.jpg +0 -0
- package/kb/references/thumbs/1f651f33c0.jpg +0 -0
- package/kb/references/thumbs/1f8b697d18.jpg +0 -0
- package/kb/references/thumbs/1fee3824cd.jpg +0 -0
- package/kb/references/thumbs/1ff7757853.jpg +0 -0
- package/kb/references/thumbs/2043182f01.jpg +0 -0
- package/kb/references/thumbs/2059dabcd5.jpg +0 -0
- package/kb/references/thumbs/209d9dbb95.jpg +0 -0
- package/kb/references/thumbs/20d8923554.jpg +0 -0
- package/kb/references/thumbs/210dd3b671.jpg +0 -0
- package/kb/references/thumbs/2120719116.jpg +0 -0
- package/kb/references/thumbs/21a7384429.jpg +0 -0
- package/kb/references/thumbs/225f187391.jpg +0 -0
- package/kb/references/thumbs/230caf7dd2.jpg +0 -0
- package/kb/references/thumbs/235afad355.jpg +0 -0
- package/kb/references/thumbs/2378066687.jpg +0 -0
- package/kb/references/thumbs/241faf68a1.jpg +0 -0
- package/kb/references/thumbs/24d9f1a9b9.jpg +0 -0
- package/kb/references/thumbs/2505c8b9a0.jpg +0 -0
- package/kb/references/thumbs/25593638ca.jpg +0 -0
- package/kb/references/thumbs/25dd454f18.jpg +0 -0
- package/kb/references/thumbs/2602e123e2.jpg +0 -0
- package/kb/references/thumbs/26095c0b11.jpg +0 -0
- package/kb/references/thumbs/26260d6238.jpg +0 -0
- package/kb/references/thumbs/26bba21836.jpg +0 -0
- package/kb/references/thumbs/26bde3520b.jpg +0 -0
- package/kb/references/thumbs/272e22067c.jpg +0 -0
- package/kb/references/thumbs/27421b3226.jpg +0 -0
- package/kb/references/thumbs/2769ebca41.jpg +0 -0
- package/kb/references/thumbs/27e2f6e8b0.jpg +0 -0
- package/kb/references/thumbs/28432ba373.jpg +0 -0
- package/kb/references/thumbs/2846626f19.jpg +0 -0
- package/kb/references/thumbs/28750e821e.jpg +0 -0
- package/kb/references/thumbs/289481230f.jpg +0 -0
- package/kb/references/thumbs/2897967408.jpg +0 -0
- package/kb/references/thumbs/28c0a5fa17.jpg +0 -0
- package/kb/references/thumbs/28c181fba5.jpg +0 -0
- package/kb/references/thumbs/29806f539f.jpg +0 -0
- package/kb/references/thumbs/2997f56ee7.jpg +0 -0
- package/kb/references/thumbs/29b34a10fb.jpg +0 -0
- package/kb/references/thumbs/29bd78cdb4.jpg +0 -0
- package/kb/references/thumbs/29c8c6c22a.jpg +0 -0
- package/kb/references/thumbs/2ac23f7fae.jpg +0 -0
- package/kb/references/thumbs/2b76e13f79.jpg +0 -0
- package/kb/references/thumbs/2b84226479.jpg +0 -0
- package/kb/references/thumbs/2bf2dbf737.jpg +0 -0
- package/kb/references/thumbs/2bf89abbf6.jpg +0 -0
- package/kb/references/thumbs/2c562cd534.jpg +0 -0
- package/kb/references/thumbs/2cfe9d48c4.jpg +0 -0
- package/kb/references/thumbs/2d49f19b27.jpg +0 -0
- package/kb/references/thumbs/2dc8670da7.jpg +0 -0
- package/kb/references/thumbs/2de947c469.jpg +0 -0
- package/kb/references/thumbs/2e075ca79f.jpg +0 -0
- package/kb/references/thumbs/2eb0820e6b.jpg +0 -0
- package/kb/references/thumbs/2ec56a9dc0.jpg +0 -0
- package/kb/references/thumbs/30bb4ad607.jpg +0 -0
- package/kb/references/thumbs/30d19b8444.jpg +0 -0
- package/kb/references/thumbs/3173c71d73.jpg +0 -0
- package/kb/references/thumbs/31e59e4518.jpg +0 -0
- package/kb/references/thumbs/322b8960e8.jpg +0 -0
- package/kb/references/thumbs/325c9be6f4.jpg +0 -0
- package/kb/references/thumbs/3273338982.jpg +0 -0
- package/kb/references/thumbs/32f8387f38.jpg +0 -0
- package/kb/references/thumbs/332f6b74c3.jpg +0 -0
- package/kb/references/thumbs/334b146b6a.jpg +0 -0
- package/kb/references/thumbs/339abd60e4.jpg +0 -0
- package/kb/references/thumbs/33e88ec295.jpg +0 -0
- package/kb/references/thumbs/340931b5d4.jpg +0 -0
- package/kb/references/thumbs/34165a93bd.jpg +0 -0
- package/kb/references/thumbs/34d0575dd7.jpg +0 -0
- package/kb/references/thumbs/3549e75f0a.jpg +0 -0
- package/kb/references/thumbs/35827f1561.jpg +0 -0
- package/kb/references/thumbs/37c50280ec.jpg +0 -0
- package/kb/references/thumbs/37e66ecf11.jpg +0 -0
- package/kb/references/thumbs/3841a04fdc.jpg +0 -0
- package/kb/references/thumbs/38490c088f.jpg +0 -0
- package/kb/references/thumbs/3850319e9f.jpg +0 -0
- package/kb/references/thumbs/3856a64166.jpg +0 -0
- package/kb/references/thumbs/3881f211c2.jpg +0 -0
- package/kb/references/thumbs/38c7dde2f3.jpg +0 -0
- package/kb/references/thumbs/38ce9e486c.jpg +0 -0
- package/kb/references/thumbs/390ea8e01b.jpg +0 -0
- package/kb/references/thumbs/391b7fc27c.jpg +0 -0
- package/kb/references/thumbs/395f7214dc.jpg +0 -0
- package/kb/references/thumbs/39e81a5db2.jpg +0 -0
- package/kb/references/thumbs/3a67c8d655.jpg +0 -0
- package/kb/references/thumbs/3a89c25e3b.jpg +0 -0
- package/kb/references/thumbs/3aa0837542.jpg +0 -0
- package/kb/references/thumbs/3aae18aa8a.jpg +0 -0
- package/kb/references/thumbs/3bdbe767db.jpg +0 -0
- package/kb/references/thumbs/3c0616fc7f.jpg +0 -0
- package/kb/references/thumbs/3c18e9db1d.jpg +0 -0
- package/kb/references/thumbs/3c5a14076c.jpg +0 -0
- package/kb/references/thumbs/3ca9ec15c6.jpg +0 -0
- package/kb/references/thumbs/3d7756fd35.jpg +0 -0
- package/kb/references/thumbs/3dd45bd33d.jpg +0 -0
- package/kb/references/thumbs/3de72d5414.jpg +0 -0
- package/kb/references/thumbs/3dede597af.jpg +0 -0
- package/kb/references/thumbs/3ea948619e.jpg +0 -0
- package/kb/references/thumbs/402c12054c.jpg +0 -0
- package/kb/references/thumbs/403ab4af90.jpg +0 -0
- package/kb/references/thumbs/40773a8c63.jpg +0 -0
- package/kb/references/thumbs/407d9583db.jpg +0 -0
- package/kb/references/thumbs/415dc1d84c.jpg +0 -0
- package/kb/references/thumbs/420b676a4f.jpg +0 -0
- package/kb/references/thumbs/42a1ee85eb.jpg +0 -0
- package/kb/references/thumbs/43059221aa.jpg +0 -0
- package/kb/references/thumbs/432fa0d22c.jpg +0 -0
- package/kb/references/thumbs/43826f20e3.jpg +0 -0
- package/kb/references/thumbs/43ac55c98a.jpg +0 -0
- package/kb/references/thumbs/43d098f3c3.jpg +0 -0
- package/kb/references/thumbs/4449d80c2c.jpg +0 -0
- package/kb/references/thumbs/44625727dc.jpg +0 -0
- package/kb/references/thumbs/4482bb87a1.jpg +0 -0
- package/kb/references/thumbs/44b078acdc.jpg +0 -0
- package/kb/references/thumbs/44d0305850.jpg +0 -0
- package/kb/references/thumbs/456bb6b59e.jpg +0 -0
- package/kb/references/thumbs/45df73ce48.jpg +0 -0
- package/kb/references/thumbs/45edb45d87.jpg +0 -0
- package/kb/references/thumbs/4671d57cd5.jpg +0 -0
- package/kb/references/thumbs/46cc108518.jpg +0 -0
- package/kb/references/thumbs/470022e614.jpg +0 -0
- package/kb/references/thumbs/4719a669ce.jpg +0 -0
- package/kb/references/thumbs/47404f5ad4.jpg +0 -0
- package/kb/references/thumbs/479d3eb977.jpg +0 -0
- package/kb/references/thumbs/479ed92ceb.jpg +0 -0
- package/kb/references/thumbs/47baed50c7.jpg +0 -0
- package/kb/references/thumbs/47fdcfd7b0.jpg +0 -0
- package/kb/references/thumbs/48fd5e94c5.jpg +0 -0
- package/kb/references/thumbs/493807917b.jpg +0 -0
- package/kb/references/thumbs/49a1c67293.jpg +0 -0
- package/kb/references/thumbs/49e2d7fafe.jpg +0 -0
- package/kb/references/thumbs/49f540fcb6.jpg +0 -0
- package/kb/references/thumbs/4a67ec4028.jpg +0 -0
- package/kb/references/thumbs/4a93275b04.jpg +0 -0
- package/kb/references/thumbs/4aae6f52da.jpg +0 -0
- package/kb/references/thumbs/4b1bae66bd.jpg +0 -0
- package/kb/references/thumbs/4b75da15a9.jpg +0 -0
- package/kb/references/thumbs/4b77789b88.jpg +0 -0
- package/kb/references/thumbs/4bc2f3f3a4.jpg +0 -0
- package/kb/references/thumbs/4c48f27b88.jpg +0 -0
- package/kb/references/thumbs/4c52bd8bfb.jpg +0 -0
- package/kb/references/thumbs/4c9b4acc60.jpg +0 -0
- package/kb/references/thumbs/4cbf0752d4.jpg +0 -0
- package/kb/references/thumbs/4cd2672517.jpg +0 -0
- package/kb/references/thumbs/4dd22a8181.jpg +0 -0
- package/kb/references/thumbs/4ee3447314.jpg +0 -0
- package/kb/references/thumbs/4ef29c8144.jpg +0 -0
- package/kb/references/thumbs/4f82fadfd6.jpg +0 -0
- package/kb/references/thumbs/4fc9b643d8.jpg +0 -0
- package/kb/references/thumbs/50549524b0.jpg +0 -0
- package/kb/references/thumbs/509a615883.jpg +0 -0
- package/kb/references/thumbs/50c03ef855.jpg +0 -0
- package/kb/references/thumbs/50cf9acad6.jpg +0 -0
- package/kb/references/thumbs/51131b025c.jpg +0 -0
- package/kb/references/thumbs/51da1124f0.jpg +0 -0
- package/kb/references/thumbs/524d5e7288.jpg +0 -0
- package/kb/references/thumbs/5251247c81.jpg +0 -0
- package/kb/references/thumbs/526b91b7ec.jpg +0 -0
- package/kb/references/thumbs/52f02f6c2e.jpg +0 -0
- package/kb/references/thumbs/530ab530dc.jpg +0 -0
- package/kb/references/thumbs/5335ee4404.jpg +0 -0
- package/kb/references/thumbs/5385b90939.jpg +0 -0
- package/kb/references/thumbs/53c54654cc.jpg +0 -0
- package/kb/references/thumbs/5420794c6c.jpg +0 -0
- package/kb/references/thumbs/542a9dce8b.jpg +0 -0
- package/kb/references/thumbs/5571530a5f.jpg +0 -0
- package/kb/references/thumbs/56cc68d804.jpg +0 -0
- package/kb/references/thumbs/577b7f7c44.jpg +0 -0
- package/kb/references/thumbs/57adf8f1bb.jpg +0 -0
- package/kb/references/thumbs/57c5f5e3a6.jpg +0 -0
- package/kb/references/thumbs/57ffe4e839.jpg +0 -0
- package/kb/references/thumbs/5817dfc7d6.jpg +0 -0
- package/kb/references/thumbs/581a182a7a.jpg +0 -0
- package/kb/references/thumbs/58807679c8.jpg +0 -0
- package/kb/references/thumbs/589a1d9111.jpg +0 -0
- package/kb/references/thumbs/59167391ab.jpg +0 -0
- package/kb/references/thumbs/5a70420166.jpg +0 -0
- package/kb/references/thumbs/5b10df1b8d.jpg +0 -0
- package/kb/references/thumbs/5b7a712fdb.jpg +0 -0
- package/kb/references/thumbs/5bc46b92ad.jpg +0 -0
- package/kb/references/thumbs/5c0546a120.jpg +0 -0
- package/kb/references/thumbs/5c2b6125a8.jpg +0 -0
- package/kb/references/thumbs/5c536b9e42.jpg +0 -0
- package/kb/references/thumbs/5c8e441d30.jpg +0 -0
- package/kb/references/thumbs/5cb6f1baa5.jpg +0 -0
- package/kb/references/thumbs/5ce6c1f21e.jpg +0 -0
- package/kb/references/thumbs/5cef052c0a.jpg +0 -0
- package/kb/references/thumbs/5d08438209.jpg +0 -0
- package/kb/references/thumbs/5d0f9af9b0.jpg +0 -0
- package/kb/references/thumbs/5d300ed332.jpg +0 -0
- package/kb/references/thumbs/5d708a4794.jpg +0 -0
- package/kb/references/thumbs/5de8826a98.jpg +0 -0
- package/kb/references/thumbs/5df7bff22c.jpg +0 -0
- package/kb/references/thumbs/5e087d0f00.jpg +0 -0
- package/kb/references/thumbs/5e44e70ca0.jpg +0 -0
- package/kb/references/thumbs/5eff5d80ce.jpg +0 -0
- package/kb/references/thumbs/5f015c96b1.jpg +0 -0
- package/kb/references/thumbs/5f3289b7c4.jpg +0 -0
- package/kb/references/thumbs/5f830c40eb.jpg +0 -0
- package/kb/references/thumbs/60c4c3527b.jpg +0 -0
- package/kb/references/thumbs/60e9e594dd.jpg +0 -0
- package/kb/references/thumbs/615421d7be.jpg +0 -0
- package/kb/references/thumbs/6176e4004f.jpg +0 -0
- package/kb/references/thumbs/61f0fe5e06.jpg +0 -0
- package/kb/references/thumbs/6213785db4.jpg +0 -0
- package/kb/references/thumbs/62a670ae09.jpg +0 -0
- package/kb/references/thumbs/63cc12981b.jpg +0 -0
- package/kb/references/thumbs/641f15cc71.jpg +0 -0
- package/kb/references/thumbs/642ab2081d.jpg +0 -0
- package/kb/references/thumbs/644a59bf6e.jpg +0 -0
- package/kb/references/thumbs/644da8a8e3.jpg +0 -0
- package/kb/references/thumbs/6458986897.jpg +0 -0
- package/kb/references/thumbs/64cfbd7b93.jpg +0 -0
- package/kb/references/thumbs/661a57686a.jpg +0 -0
- package/kb/references/thumbs/66355a813a.jpg +0 -0
- package/kb/references/thumbs/664396dd44.jpg +0 -0
- package/kb/references/thumbs/66d832d65f.jpg +0 -0
- package/kb/references/thumbs/66de9e9833.jpg +0 -0
- package/kb/references/thumbs/6750964fd9.jpg +0 -0
- package/kb/references/thumbs/67c7164832.jpg +0 -0
- package/kb/references/thumbs/681600b6cd.jpg +0 -0
- package/kb/references/thumbs/682e75afe4.jpg +0 -0
- package/kb/references/thumbs/69274bc952.jpg +0 -0
- package/kb/references/thumbs/696ec08c52.jpg +0 -0
- package/kb/references/thumbs/69760111cc.jpg +0 -0
- package/kb/references/thumbs/697e32664f.jpg +0 -0
- package/kb/references/thumbs/69df92544b.jpg +0 -0
- package/kb/references/thumbs/69e48c5229.jpg +0 -0
- package/kb/references/thumbs/6a5fb75de1.jpg +0 -0
- package/kb/references/thumbs/6a7389e472.jpg +0 -0
- package/kb/references/thumbs/6a9b041e02.jpg +0 -0
- package/kb/references/thumbs/6ab2fa93ee.jpg +0 -0
- package/kb/references/thumbs/6ae3400319.jpg +0 -0
- package/kb/references/thumbs/6b21344bf3.jpg +0 -0
- package/kb/references/thumbs/6bd1b67a8a.jpg +0 -0
- package/kb/references/thumbs/6c99156c4f.jpg +0 -0
- package/kb/references/thumbs/6cc9b65384.jpg +0 -0
- package/kb/references/thumbs/6d15830a73.jpg +0 -0
- package/kb/references/thumbs/6d4c33d180.jpg +0 -0
- package/kb/references/thumbs/6e36815799.jpg +0 -0
- package/kb/references/thumbs/6eddb9cbfc.jpg +0 -0
- package/kb/references/thumbs/6f97968a7c.jpg +0 -0
- package/kb/references/thumbs/6fa0716a7d.jpg +0 -0
- package/kb/references/thumbs/6facd57184.jpg +0 -0
- package/kb/references/thumbs/6fd57b785a.jpg +0 -0
- package/kb/references/thumbs/700b3cb185.jpg +0 -0
- package/kb/references/thumbs/71197443fa.jpg +0 -0
- package/kb/references/thumbs/72082bc9ee.jpg +0 -0
- package/kb/references/thumbs/72453db479.jpg +0 -0
- package/kb/references/thumbs/72cd64d500.jpg +0 -0
- package/kb/references/thumbs/72d1e75283.jpg +0 -0
- package/kb/references/thumbs/72e13f7f5b.jpg +0 -0
- package/kb/references/thumbs/7459f8e644.jpg +0 -0
- package/kb/references/thumbs/7470d55c79.jpg +0 -0
- package/kb/references/thumbs/74c468f02a.jpg +0 -0
- package/kb/references/thumbs/74fed6eb77.jpg +0 -0
- package/kb/references/thumbs/756e05080d.jpg +0 -0
- package/kb/references/thumbs/75df7e5f49.jpg +0 -0
- package/kb/references/thumbs/76226da6ef.jpg +0 -0
- package/kb/references/thumbs/76a34aa056.jpg +0 -0
- package/kb/references/thumbs/76c7076589.jpg +0 -0
- package/kb/references/thumbs/76cccc396c.jpg +0 -0
- package/kb/references/thumbs/774654789f.jpg +0 -0
- package/kb/references/thumbs/7746cc494d.jpg +0 -0
- package/kb/references/thumbs/7766e36aa3.jpg +0 -0
- package/kb/references/thumbs/78049331ef.jpg +0 -0
- package/kb/references/thumbs/78808a0ca5.jpg +0 -0
- package/kb/references/thumbs/7890b0a310.jpg +0 -0
- package/kb/references/thumbs/78911975e6.jpg +0 -0
- package/kb/references/thumbs/78d289d5a0.jpg +0 -0
- package/kb/references/thumbs/794126267e.jpg +0 -0
- package/kb/references/thumbs/7976eb9025.jpg +0 -0
- package/kb/references/thumbs/798519ec7c.jpg +0 -0
- package/kb/references/thumbs/7a6cc5eecc.jpg +0 -0
- package/kb/references/thumbs/7ac8a7cd4a.jpg +0 -0
- package/kb/references/thumbs/7b1341d61d.jpg +0 -0
- package/kb/references/thumbs/7b9c4031e3.jpg +0 -0
- package/kb/references/thumbs/7ba5ad3779.jpg +0 -0
- package/kb/references/thumbs/7be6eb2bd0.jpg +0 -0
- package/kb/references/thumbs/7c30864f5d.jpg +0 -0
- package/kb/references/thumbs/7c379b5e17.jpg +0 -0
- package/kb/references/thumbs/7c4050eb02.jpg +0 -0
- package/kb/references/thumbs/7ca47b3221.jpg +0 -0
- package/kb/references/thumbs/7d43516bd7.jpg +0 -0
- package/kb/references/thumbs/7da9dbb98a.jpg +0 -0
- package/kb/references/thumbs/7ddda8f57c.jpg +0 -0
- package/kb/references/thumbs/7ece6d215c.jpg +0 -0
- package/kb/references/thumbs/7eebf029de.jpg +0 -0
- package/kb/references/thumbs/7f45483c15.jpg +0 -0
- package/kb/references/thumbs/7fde06eaa5.jpg +0 -0
- package/kb/references/thumbs/80407b97e9.jpg +0 -0
- package/kb/references/thumbs/80f0a6cf4b.jpg +0 -0
- package/kb/references/thumbs/8118b451b8.jpg +0 -0
- package/kb/references/thumbs/816baed4a8.jpg +0 -0
- package/kb/references/thumbs/817d2d15d9.jpg +0 -0
- package/kb/references/thumbs/8260cb430d.jpg +0 -0
- package/kb/references/thumbs/827b562899.jpg +0 -0
- package/kb/references/thumbs/8293cf1e92.jpg +0 -0
- package/kb/references/thumbs/8294653169.jpg +0 -0
- package/kb/references/thumbs/83061f871f.jpg +0 -0
- package/kb/references/thumbs/833df778ca.jpg +0 -0
- package/kb/references/thumbs/83d3acaafc.jpg +0 -0
- package/kb/references/thumbs/844c6efeff.jpg +0 -0
- package/kb/references/thumbs/84563d482a.jpg +0 -0
- package/kb/references/thumbs/848082dd94.jpg +0 -0
- package/kb/references/thumbs/8494b1da94.jpg +0 -0
- package/kb/references/thumbs/84b71014be.jpg +0 -0
- package/kb/references/thumbs/84be4a5f3c.jpg +0 -0
- package/kb/references/thumbs/84fd0fd25a.jpg +0 -0
- package/kb/references/thumbs/853e488e22.jpg +0 -0
- package/kb/references/thumbs/858c91585d.jpg +0 -0
- package/kb/references/thumbs/85903e8fe9.jpg +0 -0
- package/kb/references/thumbs/86597d43fd.jpg +0 -0
- package/kb/references/thumbs/866eb99500.jpg +0 -0
- package/kb/references/thumbs/86ac050340.jpg +0 -0
- package/kb/references/thumbs/86afec7b64.jpg +0 -0
- package/kb/references/thumbs/87238127f5.jpg +0 -0
- package/kb/references/thumbs/87321df792.jpg +0 -0
- package/kb/references/thumbs/87460f70cd.jpg +0 -0
- package/kb/references/thumbs/8770dfeaec.jpg +0 -0
- package/kb/references/thumbs/8798451a8c.jpg +0 -0
- package/kb/references/thumbs/87da1f34a3.jpg +0 -0
- package/kb/references/thumbs/882cdf8c05.jpg +0 -0
- package/kb/references/thumbs/8839f4106f.jpg +0 -0
- package/kb/references/thumbs/8844455212.jpg +0 -0
- package/kb/references/thumbs/88abba0497.jpg +0 -0
- package/kb/references/thumbs/88b372435d.jpg +0 -0
- package/kb/references/thumbs/88bd4f35fa.jpg +0 -0
- package/kb/references/thumbs/88c71a075e.jpg +0 -0
- package/kb/references/thumbs/892e5f048e.jpg +0 -0
- package/kb/references/thumbs/893cc8e920.jpg +0 -0
- package/kb/references/thumbs/896c198814.jpg +0 -0
- package/kb/references/thumbs/8a8e22f756.jpg +0 -0
- package/kb/references/thumbs/8a9d8a35cb.jpg +0 -0
- package/kb/references/thumbs/8ab1d67fec.jpg +0 -0
- package/kb/references/thumbs/8ab201fe12.jpg +0 -0
- package/kb/references/thumbs/8ac372c102.jpg +0 -0
- package/kb/references/thumbs/8b15736d62.jpg +0 -0
- package/kb/references/thumbs/8b1d8d6a05.jpg +0 -0
- package/kb/references/thumbs/8bb6050e20.jpg +0 -0
- package/kb/references/thumbs/8c6c9d1577.jpg +0 -0
- package/kb/references/thumbs/8d337851e0.jpg +0 -0
- package/kb/references/thumbs/8d44b33cc5.jpg +0 -0
- package/kb/references/thumbs/8d4536ff3b.jpg +0 -0
- package/kb/references/thumbs/8d9b99454a.jpg +0 -0
- package/kb/references/thumbs/8dda2303ff.jpg +0 -0
- package/kb/references/thumbs/8dec722d62.jpg +0 -0
- package/kb/references/thumbs/8e0ea4ca74.jpg +0 -0
- package/kb/references/thumbs/8ebba84d69.jpg +0 -0
- package/kb/references/thumbs/8ef09140c0.jpg +0 -0
- package/kb/references/thumbs/8f2928a1b0.jpg +0 -0
- package/kb/references/thumbs/8f88f9ddba.jpg +0 -0
- package/kb/references/thumbs/8fa4451009.jpg +0 -0
- package/kb/references/thumbs/90109b0e02.jpg +0 -0
- package/kb/references/thumbs/90e854e5c9.jpg +0 -0
- package/kb/references/thumbs/914e79f893.jpg +0 -0
- package/kb/references/thumbs/9161369197.jpg +0 -0
- package/kb/references/thumbs/9165c87d6f.jpg +0 -0
- package/kb/references/thumbs/9193f6558a.jpg +0 -0
- package/kb/references/thumbs/91ac7df877.jpg +0 -0
- package/kb/references/thumbs/9216bd4931.jpg +0 -0
- package/kb/references/thumbs/922ae8a26a.jpg +0 -0
- package/kb/references/thumbs/92a70e6867.jpg +0 -0
- package/kb/references/thumbs/92c77ece22.jpg +0 -0
- package/kb/references/thumbs/92e5914fbf.jpg +0 -0
- package/kb/references/thumbs/92ee36419a.jpg +0 -0
- package/kb/references/thumbs/94153a60b0.jpg +0 -0
- package/kb/references/thumbs/9449666640.jpg +0 -0
- package/kb/references/thumbs/944be6a646.jpg +0 -0
- package/kb/references/thumbs/945831476e.jpg +0 -0
- package/kb/references/thumbs/94c1af82f4.jpg +0 -0
- package/kb/references/thumbs/9599d6d589.jpg +0 -0
- package/kb/references/thumbs/960710f4cf.jpg +0 -0
- package/kb/references/thumbs/9608f12d64.jpg +0 -0
- package/kb/references/thumbs/962dabfaa0.jpg +0 -0
- package/kb/references/thumbs/965a3a95c6.jpg +0 -0
- package/kb/references/thumbs/9672ed5ae4.jpg +0 -0
- package/kb/references/thumbs/96891ae9c1.jpg +0 -0
- package/kb/references/thumbs/96e1907c99.jpg +0 -0
- package/kb/references/thumbs/96f25b6578.jpg +0 -0
- package/kb/references/thumbs/97211df9ae.jpg +0 -0
- package/kb/references/thumbs/9769d763a5.jpg +0 -0
- package/kb/references/thumbs/9792bd6d05.jpg +0 -0
- package/kb/references/thumbs/97d2837e3e.jpg +0 -0
- package/kb/references/thumbs/97d90d1d85.jpg +0 -0
- package/kb/references/thumbs/97fcd5774a.jpg +0 -0
- package/kb/references/thumbs/980f68564e.jpg +0 -0
- package/kb/references/thumbs/98511fb393.jpg +0 -0
- package/kb/references/thumbs/98f8fa694d.jpg +0 -0
- package/kb/references/thumbs/9a42c6bb97.jpg +0 -0
- package/kb/references/thumbs/9ab3600a06.jpg +0 -0
- package/kb/references/thumbs/9ab48d9130.jpg +0 -0
- package/kb/references/thumbs/9ad519197d.jpg +0 -0
- package/kb/references/thumbs/9b1382c657.jpg +0 -0
- package/kb/references/thumbs/9b6cc1e077.jpg +0 -0
- package/kb/references/thumbs/9beede8480.jpg +0 -0
- package/kb/references/thumbs/9bf8976952.jpg +0 -0
- package/kb/references/thumbs/9cace7b75a.jpg +0 -0
- package/kb/references/thumbs/9cdcc5745e.jpg +0 -0
- package/kb/references/thumbs/9d0b269074.jpg +0 -0
- package/kb/references/thumbs/9d41788b48.jpg +0 -0
- package/kb/references/thumbs/9d475bd6af.jpg +0 -0
- package/kb/references/thumbs/9e6bc543d0.jpg +0 -0
- package/kb/references/thumbs/9ed0e0a206.jpg +0 -0
- package/kb/references/thumbs/9ef538de13.jpg +0 -0
- package/kb/references/thumbs/9efc373ab4.jpg +0 -0
- package/kb/references/thumbs/9f4a939fa5.jpg +0 -0
- package/kb/references/thumbs/9f8d12d7b9.jpg +0 -0
- package/kb/references/thumbs/9fd5e3efa7.jpg +0 -0
- package/kb/references/thumbs/a0a9b9c572.jpg +0 -0
- package/kb/references/thumbs/a29ca9b185.jpg +0 -0
- package/kb/references/thumbs/a2a42884af.jpg +0 -0
- package/kb/references/thumbs/a2f5064e34.jpg +0 -0
- package/kb/references/thumbs/a3112ce672.jpg +0 -0
- package/kb/references/thumbs/a3437736d1.jpg +0 -0
- package/kb/references/thumbs/a372667e76.jpg +0 -0
- package/kb/references/thumbs/a39bae94e9.jpg +0 -0
- package/kb/references/thumbs/a3fc75d94c.jpg +0 -0
- package/kb/references/thumbs/a440487631.jpg +0 -0
- package/kb/references/thumbs/a4993d5fb5.jpg +0 -0
- package/kb/references/thumbs/a500476a86.jpg +0 -0
- package/kb/references/thumbs/a55506bf46.jpg +0 -0
- package/kb/references/thumbs/a59a5f9843.jpg +0 -0
- package/kb/references/thumbs/a5bce0eede.jpg +0 -0
- package/kb/references/thumbs/a5d921e1f0.jpg +0 -0
- package/kb/references/thumbs/a5e19d4637.jpg +0 -0
- package/kb/references/thumbs/a5f92b97d5.jpg +0 -0
- package/kb/references/thumbs/a6562c210a.jpg +0 -0
- package/kb/references/thumbs/a6f61a8d09.jpg +0 -0
- package/kb/references/thumbs/a719fdf663.jpg +0 -0
- package/kb/references/thumbs/a724e5442f.jpg +0 -0
- package/kb/references/thumbs/a852b4319f.jpg +0 -0
- package/kb/references/thumbs/a896c08448.jpg +0 -0
- package/kb/references/thumbs/a8d190a4b8.jpg +0 -0
- package/kb/references/thumbs/a8dcdd57d7.jpg +0 -0
- package/kb/references/thumbs/a951347e18.jpg +0 -0
- package/kb/references/thumbs/a9a1a0bf7d.jpg +0 -0
- package/kb/references/thumbs/a9b72986a8.jpg +0 -0
- package/kb/references/thumbs/aa11555ba8.jpg +0 -0
- package/kb/references/thumbs/aa4aab9093.jpg +0 -0
- package/kb/references/thumbs/aa89b96658.jpg +0 -0
- package/kb/references/thumbs/aaeb3739b6.jpg +0 -0
- package/kb/references/thumbs/ab25a608ec.jpg +0 -0
- package/kb/references/thumbs/ab34b62268.jpg +0 -0
- package/kb/references/thumbs/ab3b0217bb.jpg +0 -0
- package/kb/references/thumbs/ac70c04afb.jpg +0 -0
- package/kb/references/thumbs/ace0879117.jpg +0 -0
- package/kb/references/thumbs/ad0515ae55.jpg +0 -0
- package/kb/references/thumbs/ad66b25b47.jpg +0 -0
- package/kb/references/thumbs/ad68229d2f.jpg +0 -0
- package/kb/references/thumbs/add6480693.jpg +0 -0
- package/kb/references/thumbs/ae5b8c3b8c.jpg +0 -0
- package/kb/references/thumbs/ae81247b47.jpg +0 -0
- package/kb/references/thumbs/af8aa54379.jpg +0 -0
- package/kb/references/thumbs/afa80dedd4.jpg +0 -0
- package/kb/references/thumbs/afe04d730f.jpg +0 -0
- package/kb/references/thumbs/b09a7dc315.jpg +0 -0
- package/kb/references/thumbs/b0c02629f6.jpg +0 -0
- package/kb/references/thumbs/b1cb4f91de.jpg +0 -0
- package/kb/references/thumbs/b1d4c13c92.jpg +0 -0
- package/kb/references/thumbs/b20eef86db.jpg +0 -0
- package/kb/references/thumbs/b3136ccdde.jpg +0 -0
- package/kb/references/thumbs/b3f5e8d7dd.jpg +0 -0
- package/kb/references/thumbs/b44adb81c9.jpg +0 -0
- package/kb/references/thumbs/b457a5894b.jpg +0 -0
- package/kb/references/thumbs/b461281e0f.jpg +0 -0
- package/kb/references/thumbs/b4942644f0.jpg +0 -0
- package/kb/references/thumbs/b49f8eccad.jpg +0 -0
- package/kb/references/thumbs/b56c69be23.jpg +0 -0
- package/kb/references/thumbs/b59786b33c.jpg +0 -0
- package/kb/references/thumbs/b5d368ce5a.jpg +0 -0
- package/kb/references/thumbs/b5eb442406.jpg +0 -0
- package/kb/references/thumbs/b7fdabf4ab.jpg +0 -0
- package/kb/references/thumbs/b88ee8d235.jpg +0 -0
- package/kb/references/thumbs/b8a4e5be8a.jpg +0 -0
- package/kb/references/thumbs/b8c85b661c.jpg +0 -0
- package/kb/references/thumbs/ba1b8e7435.jpg +0 -0
- package/kb/references/thumbs/ba9a593eed.jpg +0 -0
- package/kb/references/thumbs/baa7afc201.jpg +0 -0
- package/kb/references/thumbs/baf74398f7.jpg +0 -0
- package/kb/references/thumbs/bb3e947c10.jpg +0 -0
- package/kb/references/thumbs/bb4c1bb7c1.jpg +0 -0
- package/kb/references/thumbs/bbaa25cc8a.jpg +0 -0
- package/kb/references/thumbs/bc7e7f88cc.jpg +0 -0
- package/kb/references/thumbs/bc9d5b58f5.jpg +0 -0
- package/kb/references/thumbs/bcc99a51f7.jpg +0 -0
- package/kb/references/thumbs/bce13dd1c1.jpg +0 -0
- package/kb/references/thumbs/bd079a6130.jpg +0 -0
- package/kb/references/thumbs/bd2d70dda7.jpg +0 -0
- package/kb/references/thumbs/bd5895c5f2.jpg +0 -0
- package/kb/references/thumbs/bde8e31d62.jpg +0 -0
- package/kb/references/thumbs/be084e78f3.jpg +0 -0
- package/kb/references/thumbs/be94f170c8.jpg +0 -0
- package/kb/references/thumbs/bed33654f7.jpg +0 -0
- package/kb/references/thumbs/beeb9a0c18.jpg +0 -0
- package/kb/references/thumbs/bef42094dc.jpg +0 -0
- package/kb/references/thumbs/bf625496fa.jpg +0 -0
- package/kb/references/thumbs/bf771f1398.jpg +0 -0
- package/kb/references/thumbs/c0ddefa3d9.jpg +0 -0
- package/kb/references/thumbs/c0f01e536e.jpg +0 -0
- package/kb/references/thumbs/c1d592b678.jpg +0 -0
- package/kb/references/thumbs/c1e83e1b3e.jpg +0 -0
- package/kb/references/thumbs/c2168f8096.jpg +0 -0
- package/kb/references/thumbs/c249cd6b17.jpg +0 -0
- package/kb/references/thumbs/c2e3d4feed.jpg +0 -0
- package/kb/references/thumbs/c3cec01df1.jpg +0 -0
- package/kb/references/thumbs/c47bedb42d.jpg +0 -0
- package/kb/references/thumbs/c4b03b4680.jpg +0 -0
- package/kb/references/thumbs/c4e6d18ec3.jpg +0 -0
- package/kb/references/thumbs/c5477e2877.jpg +0 -0
- package/kb/references/thumbs/c566794674.jpg +0 -0
- package/kb/references/thumbs/c5b945afbb.jpg +0 -0
- package/kb/references/thumbs/c5e4659497.jpg +0 -0
- package/kb/references/thumbs/c622786702.jpg +0 -0
- package/kb/references/thumbs/c6250649ca.jpg +0 -0
- package/kb/references/thumbs/c6261a9526.jpg +0 -0
- package/kb/references/thumbs/c62e609076.jpg +0 -0
- package/kb/references/thumbs/c6602d3e85.jpg +0 -0
- package/kb/references/thumbs/c6c1a16e40.jpg +0 -0
- package/kb/references/thumbs/c77f932157.jpg +0 -0
- package/kb/references/thumbs/c79788f01e.jpg +0 -0
- package/kb/references/thumbs/c7a511ad1e.jpg +0 -0
- package/kb/references/thumbs/c7acfbc3c8.jpg +0 -0
- package/kb/references/thumbs/c7bb6d1bb8.jpg +0 -0
- package/kb/references/thumbs/c829eadcb9.jpg +0 -0
- package/kb/references/thumbs/c891fd9d85.jpg +0 -0
- package/kb/references/thumbs/c89f812ee3.jpg +0 -0
- package/kb/references/thumbs/c8a85b9f61.jpg +0 -0
- package/kb/references/thumbs/c8fdf85c9a.jpg +0 -0
- package/kb/references/thumbs/c961ac1031.jpg +0 -0
- package/kb/references/thumbs/c9fd3f88d8.jpg +0 -0
- package/kb/references/thumbs/ca0d6399c3.jpg +0 -0
- package/kb/references/thumbs/ca316c83e4.jpg +0 -0
- package/kb/references/thumbs/ca3b93aa0d.jpg +0 -0
- package/kb/references/thumbs/cb0596a85b.jpg +0 -0
- package/kb/references/thumbs/cb95a5c3ef.jpg +0 -0
- package/kb/references/thumbs/cbe238b656.jpg +0 -0
- package/kb/references/thumbs/cbf530e608.jpg +0 -0
- package/kb/references/thumbs/cc173494ec.jpg +0 -0
- package/kb/references/thumbs/ccb664bddf.jpg +0 -0
- package/kb/references/thumbs/ccf69d36cd.jpg +0 -0
- package/kb/references/thumbs/ccf759db42.jpg +0 -0
- package/kb/references/thumbs/cd2a285807.jpg +0 -0
- package/kb/references/thumbs/cd4a24a8ca.jpg +0 -0
- package/kb/references/thumbs/cdb3fe8938.jpg +0 -0
- package/kb/references/thumbs/cdce1b9bc6.jpg +0 -0
- package/kb/references/thumbs/cde3aa761a.jpg +0 -0
- package/kb/references/thumbs/ce9d5cd9c2.jpg +0 -0
- package/kb/references/thumbs/ceb01f8a34.jpg +0 -0
- package/kb/references/thumbs/cee15c98d0.jpg +0 -0
- package/kb/references/thumbs/cf6944ddae.jpg +0 -0
- package/kb/references/thumbs/d15dd21cd8.jpg +0 -0
- package/kb/references/thumbs/d1633bf2ed.jpg +0 -0
- package/kb/references/thumbs/d16a838000.jpg +0 -0
- package/kb/references/thumbs/d1cf87e9f0.jpg +0 -0
- package/kb/references/thumbs/d32511d913.jpg +0 -0
- package/kb/references/thumbs/d327e9dedd.jpg +0 -0
- package/kb/references/thumbs/d35bf6c85e.jpg +0 -0
- package/kb/references/thumbs/d37afc9d61.jpg +0 -0
- package/kb/references/thumbs/d3b66655c7.jpg +0 -0
- package/kb/references/thumbs/d40b4172b6.jpg +0 -0
- package/kb/references/thumbs/d4c40d1824.jpg +0 -0
- package/kb/references/thumbs/d4cb55a4b6.jpg +0 -0
- package/kb/references/thumbs/d4f0c30f7e.jpg +0 -0
- package/kb/references/thumbs/d51cce3ae5.jpg +0 -0
- package/kb/references/thumbs/d52948f9ba.jpg +0 -0
- package/kb/references/thumbs/d62bd3cba4.jpg +0 -0
- package/kb/references/thumbs/d668a21c92.jpg +0 -0
- package/kb/references/thumbs/d685072c52.jpg +0 -0
- package/kb/references/thumbs/d6d7b96093.jpg +0 -0
- package/kb/references/thumbs/d7cd874b21.jpg +0 -0
- package/kb/references/thumbs/d7e111736a.jpg +0 -0
- package/kb/references/thumbs/d88325f832.jpg +0 -0
- package/kb/references/thumbs/d9272ded76.jpg +0 -0
- package/kb/references/thumbs/d962e7c654.jpg +0 -0
- package/kb/references/thumbs/d97f000ee2.jpg +0 -0
- package/kb/references/thumbs/d9c1ae2543.jpg +0 -0
- package/kb/references/thumbs/d9ccb3cac8.jpg +0 -0
- package/kb/references/thumbs/da99019680.jpg +0 -0
- package/kb/references/thumbs/da9b8bc615.jpg +0 -0
- package/kb/references/thumbs/daafbee2c1.jpg +0 -0
- package/kb/references/thumbs/db0f11f143.jpg +0 -0
- package/kb/references/thumbs/db5dd367f5.jpg +0 -0
- package/kb/references/thumbs/db69677866.jpg +0 -0
- package/kb/references/thumbs/db87c1956e.jpg +0 -0
- package/kb/references/thumbs/dbb86d8bda.jpg +0 -0
- package/kb/references/thumbs/dbd3a8ff2a.jpg +0 -0
- package/kb/references/thumbs/dbdf4b2794.jpg +0 -0
- package/kb/references/thumbs/dd4c19b4b1.jpg +0 -0
- package/kb/references/thumbs/dd98d9f6fa.jpg +0 -0
- package/kb/references/thumbs/ddb00e3b6a.jpg +0 -0
- package/kb/references/thumbs/dde3541a5c.jpg +0 -0
- package/kb/references/thumbs/de2a4da502.jpg +0 -0
- package/kb/references/thumbs/de4c387a66.jpg +0 -0
- package/kb/references/thumbs/de5618fa82.jpg +0 -0
- package/kb/references/thumbs/de6bec2538.jpg +0 -0
- package/kb/references/thumbs/dea13cbb1f.jpg +0 -0
- package/kb/references/thumbs/deff9202aa.jpg +0 -0
- package/kb/references/thumbs/df12835f8d.jpg +0 -0
- package/kb/references/thumbs/df57acbb3d.jpg +0 -0
- package/kb/references/thumbs/df8265009e.jpg +0 -0
- package/kb/references/thumbs/dfc99a1e8e.jpg +0 -0
- package/kb/references/thumbs/dfc9fde460.jpg +0 -0
- package/kb/references/thumbs/e038348eee.jpg +0 -0
- package/kb/references/thumbs/e04c2ae1d5.jpg +0 -0
- package/kb/references/thumbs/e070345c43.jpg +0 -0
- package/kb/references/thumbs/e0a0c84d47.jpg +0 -0
- package/kb/references/thumbs/e0af1e6b9e.jpg +0 -0
- package/kb/references/thumbs/e1025942c4.jpg +0 -0
- package/kb/references/thumbs/e11b6f5b82.jpg +0 -0
- package/kb/references/thumbs/e1553de3fc.jpg +0 -0
- package/kb/references/thumbs/e162ebe7cd.jpg +0 -0
- package/kb/references/thumbs/e1f1e531d8.jpg +0 -0
- package/kb/references/thumbs/e20842e5c9.jpg +0 -0
- package/kb/references/thumbs/e24e7d949b.jpg +0 -0
- package/kb/references/thumbs/e2a3e5d10a.jpg +0 -0
- package/kb/references/thumbs/e2de02615a.jpg +0 -0
- package/kb/references/thumbs/e305dad7dd.jpg +0 -0
- package/kb/references/thumbs/e52e3ef97f.jpg +0 -0
- package/kb/references/thumbs/e570e92d06.jpg +0 -0
- package/kb/references/thumbs/e583932fb9.jpg +0 -0
- package/kb/references/thumbs/e5da3c038f.jpg +0 -0
- package/kb/references/thumbs/e657634af3.jpg +0 -0
- package/kb/references/thumbs/e67946e3e1.jpg +0 -0
- package/kb/references/thumbs/e69e0f3639.jpg +0 -0
- package/kb/references/thumbs/e6ebe66031.jpg +0 -0
- package/kb/references/thumbs/e7db9563c9.jpg +0 -0
- package/kb/references/thumbs/e84a4c3b7b.jpg +0 -0
- package/kb/references/thumbs/e84cf52c27.jpg +0 -0
- package/kb/references/thumbs/e8707cf6e8.jpg +0 -0
- package/kb/references/thumbs/e888890b40.jpg +0 -0
- package/kb/references/thumbs/e8a502b418.jpg +0 -0
- package/kb/references/thumbs/e8ba68371d.jpg +0 -0
- package/kb/references/thumbs/e9098f54cc.jpg +0 -0
- package/kb/references/thumbs/e9144deae9.jpg +0 -0
- package/kb/references/thumbs/e93e98d757.jpg +0 -0
- package/kb/references/thumbs/e9762dd435.jpg +0 -0
- package/kb/references/thumbs/e9db4544c8.jpg +0 -0
- package/kb/references/thumbs/e9ddffeacd.jpg +0 -0
- package/kb/references/thumbs/e9e2cf1d7e.jpg +0 -0
- package/kb/references/thumbs/ea1e23e623.jpg +0 -0
- package/kb/references/thumbs/ea41200f5e.jpg +0 -0
- package/kb/references/thumbs/eaf4916e1a.jpg +0 -0
- package/kb/references/thumbs/eb2c505fc8.jpg +0 -0
- package/kb/references/thumbs/eb47943693.jpg +0 -0
- package/kb/references/thumbs/eb4ffd6625.jpg +0 -0
- package/kb/references/thumbs/eb82ac0a4d.jpg +0 -0
- package/kb/references/thumbs/ebeb345ade.jpg +0 -0
- package/kb/references/thumbs/ec4e2aba0a.jpg +0 -0
- package/kb/references/thumbs/ed683bc9f7.jpg +0 -0
- package/kb/references/thumbs/ed9e236a84.jpg +0 -0
- package/kb/references/thumbs/ee45e1caea.jpg +0 -0
- package/kb/references/thumbs/ee50bbf2e7.jpg +0 -0
- package/kb/references/thumbs/eed6ad3b7b.jpg +0 -0
- package/kb/references/thumbs/eee247fd70.jpg +0 -0
- package/kb/references/thumbs/ef87b6cb99.jpg +0 -0
- package/kb/references/thumbs/ef87dccf04.jpg +0 -0
- package/kb/references/thumbs/ef941bba1e.jpg +0 -0
- package/kb/references/thumbs/effc14fb2c.jpg +0 -0
- package/kb/references/thumbs/f04aa5a191.jpg +0 -0
- package/kb/references/thumbs/f13f12485f.jpg +0 -0
- package/kb/references/thumbs/f16a5c0fd4.jpg +0 -0
- package/kb/references/thumbs/f1ba7a7a8f.jpg +0 -0
- package/kb/references/thumbs/f1bc4c9c19.jpg +0 -0
- package/kb/references/thumbs/f1ee827878.jpg +0 -0
- package/kb/references/thumbs/f21736aeff.jpg +0 -0
- package/kb/references/thumbs/f22bc41213.jpg +0 -0
- package/kb/references/thumbs/f266aaffcc.jpg +0 -0
- package/kb/references/thumbs/f2c0ed0d65.jpg +0 -0
- package/kb/references/thumbs/f32ac710fd.jpg +0 -0
- package/kb/references/thumbs/f32b697ff6.jpg +0 -0
- package/kb/references/thumbs/f39c883904.jpg +0 -0
- package/kb/references/thumbs/f3b9d55e6e.jpg +0 -0
- package/kb/references/thumbs/f3c1763e14.jpg +0 -0
- package/kb/references/thumbs/f40853fd43.jpg +0 -0
- package/kb/references/thumbs/f4fd07dc4d.jpg +0 -0
- package/kb/references/thumbs/f50b756ee5.jpg +0 -0
- package/kb/references/thumbs/f51272e9b3.jpg +0 -0
- package/kb/references/thumbs/f538895eee.jpg +0 -0
- package/kb/references/thumbs/f5d28081b5.jpg +0 -0
- package/kb/references/thumbs/f6046c9501.jpg +0 -0
- package/kb/references/thumbs/f62e4af316.jpg +0 -0
- package/kb/references/thumbs/f6f6bffeb8.jpg +0 -0
- package/kb/references/thumbs/f736e00af6.jpg +0 -0
- package/kb/references/thumbs/f7932da47d.jpg +0 -0
- package/kb/references/thumbs/f879e118ca.jpg +0 -0
- package/kb/references/thumbs/f88257215d.jpg +0 -0
- package/kb/references/thumbs/f88c45a6d7.jpg +0 -0
- package/kb/references/thumbs/f89a7b646e.jpg +0 -0
- package/kb/references/thumbs/f8a1e7a274.jpg +0 -0
- package/kb/references/thumbs/f9183cf2b6.jpg +0 -0
- package/kb/references/thumbs/f995de5ce9.jpg +0 -0
- package/kb/references/thumbs/f9ada2c3e1.jpg +0 -0
- package/kb/references/thumbs/f9d4616f89.jpg +0 -0
- package/kb/references/thumbs/f9f4307bb0.jpg +0 -0
- package/kb/references/thumbs/fa070122c7.jpg +0 -0
- package/kb/references/thumbs/fa29cc1369.jpg +0 -0
- package/kb/references/thumbs/fa791a4dab.jpg +0 -0
- package/kb/references/thumbs/fa8060509e.jpg +0 -0
- package/kb/references/thumbs/fafeed4003.jpg +0 -0
- package/kb/references/thumbs/fb1c024f38.jpg +0 -0
- package/kb/references/thumbs/fb77aa28b3.jpg +0 -0
- package/kb/references/thumbs/fb9f7f1966.jpg +0 -0
- package/kb/references/thumbs/fba8a8c2da.jpg +0 -0
- package/kb/references/thumbs/fce2754c36.jpg +0 -0
- package/kb/references/thumbs/fce2fb83df.jpg +0 -0
- package/kb/references/thumbs/fd4c51bb47.jpg +0 -0
- package/kb/references/thumbs/fd8c269817.jpg +0 -0
- package/kb/references/thumbs/fdb7b250ca.jpg +0 -0
- package/kb/references/thumbs/fe18088de8.jpg +0 -0
- package/kb/references/thumbs/fe4f742ba7.jpg +0 -0
- package/kb/references/thumbs/fe64742ce7.jpg +0 -0
- package/kb/references/thumbs/feb5d62161.jpg +0 -0
- package/kb/references/thumbs/fed21b3e98.jpg +0 -0
- package/kb/references/thumbs/fed2205951.jpg +0 -0
- package/kb/references/thumbs/ffdacf1715.jpg +0 -0
- package/kb/sections/_buttons.md +93 -0
- package/kb/sections/_finish.md +325 -0
- package/kb/sections/about-founder-note.md +85 -0
- package/kb/sections/about-team-roster.md +86 -0
- package/kb/sections/alternating-rows-live-tiles.md +110 -0
- package/kb/sections/annotated-product-shot.md +105 -0
- package/kb/sections/asymmetric-bento-dominant.md +114 -0
- package/kb/sections/before-after-features.md +104 -0
- package/kb/sections/before-after-repair.md +107 -0
- package/kb/sections/bento-lead-tile.md +112 -0
- package/kb/sections/book-a-slot-honest.md +116 -0
- package/kb/sections/capability-prompt-ledger.md +68 -0
- package/kb/sections/case-study-band.md +113 -0
- package/kb/sections/centred-stack-hero.md +107 -0
- package/kb/sections/closing-demo-final-state.md +81 -0
- package/kb/sections/closing-full-bleed-line-and-form.md +86 -0
- package/kb/sections/closing-result-previewed.md +88 -0
- package/kb/sections/closing-three-real-steps.md +87 -0
- package/kb/sections/command-reference-table.md +70 -0
- package/kb/sections/comparison-old-way-bars.md +88 -0
- package/kb/sections/comparison-same-task-two-ways.md +88 -0
- package/kb/sections/day-of-the-place.md +102 -0
- package/kb/sections/docs-reading-rail.md +88 -0
- package/kb/sections/editorial-columns-hero.md +107 -0
- package/kb/sections/editorial-essay-pull-quote.md +112 -0
- package/kb/sections/events-calendar-list.md +107 -0
- package/kb/sections/explorable-control-demo.md +102 -0
- package/kb/sections/faq-open-answers-editorial.md +85 -0
- package/kb/sections/faq-ruled-accordion-aside.md +87 -0
- package/kb/sections/footer-closing-scene.md +84 -0
- package/kb/sections/footer-editorial-sitemap.md +85 -0
- package/kb/sections/footer-minimal-credits.md +80 -0
- package/kb/sections/full-bleed-image-band.md +103 -0
- package/kb/sections/full-bleed-image-hero.md +105 -0
- package/kb/sections/gallery-first-hero.md +109 -0
- package/kb/sections/horizontal-scroll-strip.md +101 -0
- package/kb/sections/how-i-work-own-voice.md +111 -0
- package/kb/sections/how-it-works-in-place.md +109 -0
- package/kb/sections/impact-numbers-sourced.md +109 -0
- package/kb/sections/install-steps-per-agent.md +77 -0
- package/kb/sections/masonry-real-work.md +109 -0
- package/kb/sections/numbered-editorial-columns.md +105 -0
- package/kb/sections/object-first-hero.md +106 -0
- package/kb/sections/object-timeline.md +105 -0
- package/kb/sections/one-huge-number.md +100 -0
- package/kb/sections/one-person-story.md +103 -0
- package/kb/sections/order-ahead-ticket.md +119 -0
- package/kb/sections/press-strip-quoted.md +106 -0
- package/kb/sections/pricing-comparison-table-sticky.md +94 -0
- package/kb/sections/pricing-free-pro-upgrade-moment.md +85 -0
- package/kb/sections/pricing-plan-beside-product.md +88 -0
- package/kb/sections/pricing-plans-ledger.md +94 -0
- package/kb/sections/pricing-usage-calculator.md +93 -0
- package/kb/sections/printed-menu-card.md +110 -0
- package/kb/sections/process-happening.md +102 -0
- package/kb/sections/project-index-ruled.md +106 -0
- package/kb/sections/proof-logo-cloud-with-reasons.md +82 -0
- package/kb/sections/proof-measured-numbers.md +88 -0
- package/kb/sections/proof-output-wall.md +84 -0
- package/kb/sections/proof-single-honest-quote.md +81 -0
- package/kb/sections/resources-searchable-list.md +110 -0
- package/kb/sections/rooms-and-rates.md +107 -0
- package/kb/sections/ruled-facts-table.md +108 -0
- package/kb/sections/ruled-list-pinned-visual.md +107 -0
- package/kb/sections/scroll-scrubbed-product-demo.md +103 -0
- package/kb/sections/self-playing-run-demo.md +104 -0
- package/kb/sections/services-price-ledger.md +106 -0
- package/kb/sections/signup-form-with-next-steps.md +86 -0
- package/kb/sections/signup-waitlist-place-in-line.md +90 -0
- package/kb/sections/spec-sheet-features.md +108 -0
- package/kb/sections/split-hero-live-object.md +108 -0
- package/kb/sections/sticky-feature-narrative.md +104 -0
- package/kb/sections/todays-board-hero.md +112 -0
- package/kb/sections/touchable-tool-hero.md +111 -0
- package/kb/sections/typographic-statement-hero.md +99 -0
- package/kb/sections/visit-hours-map.md +110 -0
- package/kb/sections/ways-to-help-honest.md +115 -0
- package/kb/sources/design-reference.md +991 -0
- package/kb/sources/product-and-docs.md +37 -0
- package/kb/systems/1win/DESIGN.md +463 -0
- package/kb/systems/LICENSE-awesome-design-md +21 -0
- package/kb/systems/README.md +7 -0
- package/kb/systems/airbnb/DESIGN.md +545 -0
- package/kb/systems/airtable/DESIGN.md +554 -0
- package/kb/systems/aljazeera/DESIGN.md +594 -0
- package/kb/systems/apple/DESIGN.md +562 -0
- package/kb/systems/binance/DESIGN.md +634 -0
- package/kb/systems/bmw/DESIGN.md +544 -0
- package/kb/systems/bmw-m/DESIGN.md +503 -0
- package/kb/systems/bugatti/DESIGN.md +454 -0
- package/kb/systems/cal/DESIGN.md +542 -0
- package/kb/systems/categories.json +87 -0
- package/kb/systems/claude/DESIGN.md +589 -0
- package/kb/systems/clay/DESIGN.md +541 -0
- package/kb/systems/clickhouse/DESIGN.md +544 -0
- package/kb/systems/cohere/DESIGN.md +451 -0
- package/kb/systems/coinbase/DESIGN.md +570 -0
- package/kb/systems/composio/DESIGN.md +506 -0
- package/kb/systems/cursor/DESIGN.md +537 -0
- package/kb/systems/dell-1996/DESIGN.md +632 -0
- package/kb/systems/dji/DESIGN.md +882 -0
- package/kb/systems/elevenlabs/DESIGN.md +504 -0
- package/kb/systems/expo/DESIGN.md +526 -0
- package/kb/systems/ferrari/DESIGN.md +531 -0
- package/kb/systems/figma/DESIGN.md +578 -0
- package/kb/systems/framer/DESIGN.md +544 -0
- package/kb/systems/hashicorp/DESIGN.md +575 -0
- package/kb/systems/hp/DESIGN.md +670 -0
- package/kb/systems/ibm/DESIGN.md +550 -0
- package/kb/systems/intercom/DESIGN.md +546 -0
- package/kb/systems/jobright/DESIGN.md +525 -0
- package/kb/systems/kraken/DESIGN.md +125 -0
- package/kb/systems/lamborghini/DESIGN.md +288 -0
- package/kb/systems/linear.app/DESIGN.md +548 -0
- package/kb/systems/lovable/DESIGN.md +298 -0
- package/kb/systems/mastercard/DESIGN.md +365 -0
- package/kb/systems/mercury/DESIGN.md +568 -0
- package/kb/systems/meta/DESIGN.md +683 -0
- package/kb/systems/minimax/DESIGN.md +746 -0
- package/kb/systems/mintlify/DESIGN.md +852 -0
- package/kb/systems/miro/DESIGN.md +825 -0
- package/kb/systems/mistral.ai/DESIGN.md +773 -0
- package/kb/systems/mongodb/DESIGN.md +767 -0
- package/kb/systems/mydealz/DESIGN.md +551 -0
- package/kb/systems/nerdwallet/DESIGN.md +722 -0
- package/kb/systems/nike/DESIGN.md +575 -0
- package/kb/systems/nintendo-2001/DESIGN.md +649 -0
- package/kb/systems/notion/DESIGN.md +821 -0
- package/kb/systems/nvidia/DESIGN.md +640 -0
- package/kb/systems/ollama/DESIGN.md +539 -0
- package/kb/systems/opencode.ai/DESIGN.md +521 -0
- package/kb/systems/oracle/DESIGN.md +778 -0
- package/kb/systems/oura/DESIGN.md +616 -0
- package/kb/systems/pinterest/DESIGN.md +597 -0
- package/kb/systems/playstation/DESIGN.md +661 -0
- package/kb/systems/posthog/DESIGN.md +690 -0
- package/kb/systems/raycast/DESIGN.md +669 -0
- package/kb/systems/renault/DESIGN.md +589 -0
- package/kb/systems/replicate/DESIGN.md +616 -0
- package/kb/systems/resend/DESIGN.md +585 -0
- package/kb/systems/revolut/DESIGN.md +636 -0
- package/kb/systems/runwayml/DESIGN.md +244 -0
- package/kb/systems/sanity/DESIGN.md +357 -0
- package/kb/systems/sap/DESIGN.md +639 -0
- package/kb/systems/sentry/DESIGN.md +551 -0
- package/kb/systems/shopify/DESIGN.md +516 -0
- package/kb/systems/slack/DESIGN.md +482 -0
- package/kb/systems/spacex/DESIGN.md +363 -0
- package/kb/systems/spotify/DESIGN.md +246 -0
- package/kb/systems/starbucks/DESIGN.md +580 -0
- package/kb/systems/stripe/DESIGN.md +487 -0
- package/kb/systems/supabase/DESIGN.md +462 -0
- package/kb/systems/superhuman/DESIGN.md +448 -0
- package/kb/systems/tesla/DESIGN.md +286 -0
- package/kb/systems/theverge/DESIGN.md +339 -0
- package/kb/systems/together.ai/DESIGN.md +633 -0
- package/kb/systems/uber/DESIGN.md +636 -0
- package/kb/systems/vercel/DESIGN.md +736 -0
- package/kb/systems/vodafone/DESIGN.md +538 -0
- package/kb/systems/voltagent/DESIGN.md +521 -0
- package/kb/systems/warp/DESIGN.md +526 -0
- package/kb/systems/webflow/DESIGN.md +588 -0
- package/kb/systems/wired/DESIGN.md +497 -0
- package/kb/systems/wise/DESIGN.md +544 -0
- package/kb/systems/x.ai/DESIGN.md +465 -0
- package/kb/systems/zapier/DESIGN.md +537 -0
- package/kb/taste/garvit-by-job.md +46 -0
- package/kb/taste/garvit-portfolio.md +94 -0
- package/kb/universal/color-roles.md +26 -0
- package/kb/universal/copy.md +16 -0
- package/kb/universal/details.md +31 -0
- package/kb/universal/first-screen.md +30 -0
- package/kb/universal/fit-the-codebase.md +29 -0
- package/kb/universal/layout-width.md +28 -0
- package/kb/universal/motion-feedback.md +23 -0
- package/kb/universal/signature.md +29 -0
- package/kb/universal/states.md +17 -0
- package/kb/universal/type-app.md +33 -0
- package/kb/universal/type-marketing.md +28 -0
- package/package.json +36 -4
- package/scripts/pack-assets.mjs +48 -0
- package/skill/palate/SKILL.md +261 -0
- package/src/auth-recheck.mjs +4 -0
- package/src/auth.mjs +416 -0
- package/src/capture.mjs +182 -0
- package/src/catalog/classify.mjs +96 -0
- package/src/catalog/curated.mjs +128 -0
- package/src/catalog/index-build.mjs +67 -0
- package/src/catalog/literal.mjs +146 -0
- package/src/catalog/store.mjs +40 -0
- package/src/catalog/vibrant.mjs +126 -0
- package/src/check.mjs +93 -0
- package/src/concept.mjs +157 -0
- package/src/context.mjs +226 -0
- package/src/copy.mjs +188 -0
- package/src/devserver.mjs +135 -0
- package/src/fontgenre.mjs +44 -0
- package/src/guard-agents.mjs +213 -0
- package/src/guard.mjs +377 -0
- package/src/kb.mjs +91 -0
- package/src/look.mjs +312 -0
- package/src/loop/ambition.mjs +620 -0
- package/src/loop/animate.mjs +70 -0
- package/src/loop/artdirection.mjs +264 -0
- package/src/loop/bench.mjs +184 -0
- package/src/loop/benchpick.mjs +155 -0
- package/src/loop/blueprint.mjs +620 -0
- package/src/loop/board.html +180 -0
- package/src/loop/build.mjs +393 -0
- package/src/loop/buttons.mjs +413 -0
- package/src/loop/common.mjs +206 -0
- package/src/loop/cost.mjs +63 -0
- package/src/loop/craft.mjs +152 -0
- package/src/loop/craftchecks.mjs +148 -0
- package/src/loop/critique.mjs +393 -0
- package/src/loop/details.mjs +225 -0
- package/src/loop/direction.html +168 -0
- package/src/loop/direction.mjs +256 -0
- package/src/loop/directions.json +8 -0
- package/src/loop/explore.mjs +219 -0
- package/src/loop/fail.mjs +110 -0
- package/src/loop/fix.mjs +194 -0
- package/src/loop/floor.mjs +677 -0
- package/src/loop/forget.mjs +52 -0
- package/src/loop/gate.mjs +1053 -0
- package/src/loop/grid.html +123 -0
- package/src/loop/growth.mjs +118 -0
- package/src/loop/icons.mjs +109 -0
- package/src/loop/imagery.mjs +1280 -0
- package/src/loop/judge-api.mjs +245 -0
- package/src/loop/judge.mjs +167 -0
- package/src/loop/layouts.mjs +492 -0
- package/src/loop/library.mjs +82 -0
- package/src/loop/lookplan.mjs +182 -0
- package/src/loop/lookvec.mjs +645 -0
- package/src/loop/match.mjs +923 -0
- package/src/loop/matched.mjs +26 -0
- package/src/loop/myrefs.mjs +151 -0
- package/src/loop/pick.mjs +213 -0
- package/src/loop/picker.html +118 -0
- package/src/loop/plan.mjs +289 -0
- package/src/loop/polish.mjs +39 -0
- package/src/loop/research-learn.mjs +41 -0
- package/src/loop/research.mjs +1130 -0
- package/src/loop/rulereview.mjs +146 -0
- package/src/loop/rules-board.html +199 -0
- package/src/loop/rules.mjs +939 -0
- package/src/loop/similar.mjs +276 -0
- package/src/loop/states-source.mjs +258 -0
- package/src/loop/states.mjs +1547 -0
- package/src/loop/stats.mjs +78 -0
- package/src/loop/taste.mjs +81 -0
- package/src/loop/timeline.mjs +149 -0
- package/src/loop/variants.mjs +228 -0
- package/src/loop/vary.mjs +249 -0
- package/src/mcp/server.mjs +1246 -0
- package/src/motion.mjs +207 -0
- package/src/moves.mjs +242 -0
- package/src/novelty.mjs +116 -0
- package/src/page-ambition.js +181 -0
- package/src/page-craft.js +724 -0
- package/src/page-details.js +415 -0
- package/src/page-devices.js +365 -0
- package/src/page-fingerprint.js +281 -0
- package/src/page-gate.js +1307 -0
- package/src/page-look.js +698 -0
- package/src/page-metrics.js +78 -0
- package/src/page-principles.js +1167 -0
- package/src/page-rules.js +308 -0
- package/src/page-sections.js +211 -0
- package/src/page-states.js +703 -0
- package/src/paths.mjs +13 -0
- package/src/png.mjs +107 -0
- package/src/point/auto.mjs +224 -0
- package/src/point/locate.mjs +114 -0
- package/src/point/overlay.js +1516 -0
- package/src/point/server.mjs +1122 -0
- package/src/point/trail.mjs +219 -0
- package/src/pool.mjs +80 -0
- package/src/principles.mjs +279 -0
- package/src/refs.mjs +299 -0
- package/src/robots.mjs +145 -0
- package/src/sections.mjs +210 -0
- package/src/shoot.mjs +235 -0
- package/src/systems/apply.mjs +355 -0
- package/src/systems/load.mjs +251 -0
- package/src/systems/pick.mjs +213 -0
- package/templates/vite-react-tw/index.html +12 -0
- package/templates/vite-react-tw/package.json +25 -0
- package/templates/vite-react-tw/src/App.tsx +3 -0
- package/templates/vite-react-tw/src/index.css +1 -0
- package/templates/vite-react-tw/src/main.tsx +10 -0
- package/templates/vite-react-tw/tsconfig.app.json +26 -0
- package/templates/vite-react-tw/tsconfig.json +7 -0
- package/templates/vite-react-tw/tsconfig.node.json +23 -0
- package/templates/vite-react-tw/vite.config.ts +8 -0
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
// `palate blueprint "<job>" --route /`: the design decided before any code. A planner (the agent, or a fresh
|
|
2
|
+
// subagent) expands the one-line job into a spec the way a designer would: who it's for and what they feel, the
|
|
3
|
+
// category's truths to keep and its clichés to avoid, a complete system (colours with named roles including a
|
|
4
|
+
// material tone, type roles, layered shadows, the page's own button language), an imagery and material decision for every
|
|
5
|
+
// section, and one section archetype per section from palate's own library (kb/sections), no two neighbours
|
|
6
|
+
// alike. `palate blueprint --file bp.json` checks it against hard structural rules and, when it passes, saves it
|
|
7
|
+
// with tokens palate computes (tokens.css, tokens.tailwind.css). The build then follows it section by section.
|
|
8
|
+
//
|
|
9
|
+
// Why: free generation regresses to the median page (one template for every section, one flat colour, no
|
|
10
|
+
// imagery). Every tool with a real quality floor commits a system and a section plan before code (research:
|
|
11
|
+
// reports/AI design quality floor.md). The blueprint is that commitment, and palate checks it, so the builder
|
|
12
|
+
// can't drift back to the median.
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { readJson, sessionDir } from "./common.mjs";
|
|
16
|
+
import { kindWords, tasteFor } from "./craft.mjs";
|
|
17
|
+
import { chosenDirection } from "./direction.mjs";
|
|
18
|
+
import { plan } from "./plan.mjs";
|
|
19
|
+
import { contrast, loadResearch } from "./research.mjs";
|
|
20
|
+
import { kindTags, projectOf, rulesFor } from "./rules.mjs";
|
|
21
|
+
import { projectId, readLayouts } from "./layouts.mjs";
|
|
22
|
+
import { mark } from "./timeline.mjs";
|
|
23
|
+
import { KB_ROOT } from "../paths.mjs";
|
|
24
|
+
import { CTAS, FRAMES, GROUNDS, HEADERS, MIN_DIFFERENT, describePlanned, lookProblems, plannedLook, recentLooks, recordPlanned } from "./lookplan.mjs";
|
|
25
|
+
import { BUTTON_AXES, BUTTON_LANGUAGES, buttonCss, buttonProblems, buttonsFromCta, ctaOf, describeButtons, rankLanguages, resolveButtons } from "./buttons.mjs";
|
|
26
|
+
|
|
27
|
+
const Sections = await import("../sections.mjs").catch(() => null);
|
|
28
|
+
const Imagery = await import("./imagery.mjs").catch(() => null);
|
|
29
|
+
|
|
30
|
+
const slash = (p) => String(p).replace(/\\/g, "/");
|
|
31
|
+
const KB_SECTIONS = path.join(process.env.PALATE_KB ?? KB_ROOT, "sections");
|
|
32
|
+
const hexRgb = (h) => (h && /^#[0-9a-f]{6}$/i.test(h) ? [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16)) : null);
|
|
33
|
+
const rgbHex = (rgb) => "#" + rgb.map((v) => Math.max(0, Math.min(255, Math.round(v))).toString(16).padStart(2, "0")).join("");
|
|
34
|
+
const mix = (a, b, t) => {
|
|
35
|
+
const [x, y] = [hexRgb(a), hexRgb(b)];
|
|
36
|
+
return x && y ? rgbHex(x.map((v, i) => v + (y[i] - v) * t)) : a;
|
|
37
|
+
};
|
|
38
|
+
const rgbTriplet = (h) => (hexRgb(h) ?? [0, 0, 0]).join(" ");
|
|
39
|
+
const lightness = (h) => {
|
|
40
|
+
const x = hexRgb(h);
|
|
41
|
+
return x ? (0.2126 * x[0] + 0.7152 * x[1] + 0.0722 * x[2]) / 255 : 0;
|
|
42
|
+
};
|
|
43
|
+
// The colour shadows are tinted with: the ink on a light page; on a dark page (light ink) the darkest colour, since
|
|
44
|
+
// a shadow tinted with cream text glows instead of falling.
|
|
45
|
+
export const shadowTint = (c) => {
|
|
46
|
+
const ink = c.ink ?? "#111111";
|
|
47
|
+
if (lightness(ink) < 0.5) return ink;
|
|
48
|
+
return [c.dark, c.bg, c.surface].filter((x) => hexRgb(x)).map((x) => mix(x, "#000000", 0.55)).sort((a, b) => lightness(a) - lightness(b))[0] ?? "#000000";
|
|
49
|
+
};
|
|
50
|
+
const dist = (a, b) => {
|
|
51
|
+
const [x, y] = [hexRgb(a), hexRgb(b)];
|
|
52
|
+
return x && y ? Math.hypot(x[0] - y[0], x[1] - y[1], x[2] - y[2]) : 0;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// The colour roles every blueprint names. material: a tone from the product's own world (clay, paper, steel,
|
|
56
|
+
// leaf), used for a band, a texture or an image treatment, so the page is never one flat colour.
|
|
57
|
+
export const COLOUR_ROLES = ["bg", "surface", "ink", "muted", "line", "accent", "on-accent", "material"];
|
|
58
|
+
export const IMAGERY_KINDS = ["photo", "illustration", "texture", "product-ui", "type", "none"];
|
|
59
|
+
export const SURFACES = ["bg", "surface", "material", "dark", "image", "accent"];
|
|
60
|
+
// Kinds of section a marketing page is planned from (kb/sections kinds).
|
|
61
|
+
export const PAGE_KINDS = ["hero", "demo", "features", "story", "gallery", "stats", "process", "pricing", "proof", "closing", "faq", "footer", "comparison", "about", "signup", "docs"];
|
|
62
|
+
// A job that asks a new page for its own look (a campaign, a microsite, a rebrand) gets the new-page brief.
|
|
63
|
+
const NEW_LOOK = /\b(new|different|fresh|distinct|separate) (look|style|brand(ing)?|identity|visual (language|identity))\b|\b(its|their|a) own (look|style|brand(ing)?|identity)\b|\ba (look|style) of its own\b|\brebrand|\bcampaign\b|\bmicrosite\b|\bstand-?alone\b/i;
|
|
64
|
+
const GENERIC_DISPLAY = /^(inter|roboto|arial|helvetica( neue)?|system-ui|ui-sans-serif|open sans|lato|poppins|montserrat|geist)$/i;
|
|
65
|
+
|
|
66
|
+
const imageryKind = (v) => String(v ?? "").split(":")[0].trim().toLowerCase();
|
|
67
|
+
|
|
68
|
+
// The archetype menu for the planner: for each kind, the best matches for this product's tags.
|
|
69
|
+
function menu(tags) {
|
|
70
|
+
if (!Sections?.matchSections) return ["(palate's section library isn't installed: name a layout in plain words for each section, with a shape from: centred-stack, split, full-bleed-media, object-first, editorial-columns, bento, cards-row, list-ruled, stacked-rows, table, marquee, single-statement, timeline, masonry, form-led)"];
|
|
71
|
+
const out = [];
|
|
72
|
+
for (const kind of PAGE_KINDS) {
|
|
73
|
+
const found = Sections.matchSections({ kind, tags, limit: 6 });
|
|
74
|
+
if (!found.length) continue;
|
|
75
|
+
out.push(`- **${kind}**: ${found.map((s) => `\`${s.id}\` (${s.shape}; ${s.layout}${s.imagery && s.imagery !== "none" ? `; imagery ${s.imagery}` : ""})`).join(" · ")}`);
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// An existing page as it is now: its first screen, the whole page and one crop per section (the planner decides
|
|
81
|
+
// what to keep, upgrade, remove or add; the gate later checks the result isn't worse than this).
|
|
82
|
+
async function captureCurrent(dir, route, session, { url } = {}) {
|
|
83
|
+
const { cropSections, findSections, openPage, withBrowser } = await import("../look.mjs");
|
|
84
|
+
const at = path.join(session, "current");
|
|
85
|
+
fs.rmSync(at, { recursive: true, force: true });
|
|
86
|
+
fs.mkdirSync(at, { recursive: true });
|
|
87
|
+
return withBrowser(dir, { url }, async ({ base, browser }) => {
|
|
88
|
+
const { page, ctx } = await openPage(browser, base, route, { width: 1440, reducedMotion: true });
|
|
89
|
+
try {
|
|
90
|
+
const fold = path.join(at, "fold.png");
|
|
91
|
+
const full = path.join(at, "full.png");
|
|
92
|
+
await page.screenshot({ path: fold });
|
|
93
|
+
await page.screenshot({ path: full, fullPage: true });
|
|
94
|
+
const crops = await cropSections(page, await findSections(page), path.join(at, "sections"), "w1440");
|
|
95
|
+
// How much the page says: a scaffold (one empty <main>) is new, not an existing page.
|
|
96
|
+
const text = await page.evaluate(() => (document.body?.innerText ?? "").replace(/\s+/g, " ").trim().length).catch(() => null);
|
|
97
|
+
// Every string the page shows (text, alt, labels, placeholders), so the gate can say what a change lost.
|
|
98
|
+
const strings = path.join(at, "strings.json");
|
|
99
|
+
fs.writeFileSync(strings, JSON.stringify(await page.evaluate(() => {
|
|
100
|
+
const out = new Set();
|
|
101
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
102
|
+
for (let n = walker.nextNode(); n; n = walker.nextNode()) {
|
|
103
|
+
const el = n.parentElement;
|
|
104
|
+
if (!el || el.closest("script,style,noscript,template,[aria-hidden=true]")) continue;
|
|
105
|
+
const cs = getComputedStyle(el);
|
|
106
|
+
if (cs.display === "none" || cs.visibility === "hidden") continue;
|
|
107
|
+
const t = n.textContent.replace(/\s+/g, " ").trim();
|
|
108
|
+
if (t.length >= 2) out.add(t);
|
|
109
|
+
}
|
|
110
|
+
for (const el of document.querySelectorAll("[alt],[aria-label],[placeholder],[title]")) for (const a of ["alt", "aria-label", "placeholder", "title"]) { const v = (el.getAttribute(a) ?? "").trim(); if (v.length >= 2) out.add(v); }
|
|
111
|
+
return [...out];
|
|
112
|
+
}).catch(() => []), null, 1));
|
|
113
|
+
return { fold, full, text, strings, sections: crops.map((c, i) => ({ n: i + 1, name: c.heading || c.name, file: c.file, height: c.box.height })) };
|
|
114
|
+
} finally {
|
|
115
|
+
await ctx.close();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// The planner's brief: what to decide and how it's checked. An existing route gets the same brief in its
|
|
121
|
+
// existing-page form: the brand is kept, and each current section is kept, upgraded, removed, or a new one added.
|
|
122
|
+
export async function blueprintBrief(dir, job, opts = {}) {
|
|
123
|
+
return (await blueprintPlan(dir, job, opts)).text;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// A route file is an existing page only when it really has content. A fresh scaffold (the bench template's
|
|
127
|
+
// `<main></main>`, a starter's demo page) or a page that renders at most one section and almost no text is new.
|
|
128
|
+
export const MIN_EXISTING_TEXT = 80;
|
|
129
|
+
export function hasContent(found, current) {
|
|
130
|
+
if (!found) return false;
|
|
131
|
+
if (current && !current.error) return (current.sections?.length ?? 0) > 1 || (current.text ?? Infinity) >= MIN_EXISTING_TEXT;
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// The brief and the decision behind it: { text, existing } (palate build words its own lines from it).
|
|
136
|
+
export async function blueprintPlan(dir, job, { route, name, direction, url } = {}) {
|
|
137
|
+
if (!job?.trim()) throw new Error('Usage: palate blueprint "<the job, in the user\'s words>" --route /x');
|
|
138
|
+
const d = plan(dir, job, { route, name, mode: "build", log: false });
|
|
139
|
+
const r = d.route ?? "/";
|
|
140
|
+
name ??= r === "/" ? "home" : r.replace(/^\//, "").replace(/[^a-z0-9]+/gi, "-");
|
|
141
|
+
const session = sessionDir(dir, name);
|
|
142
|
+
const { isScaffold, routeFile } = await import("../look.mjs");
|
|
143
|
+
const found = routeFile(dir, r);
|
|
144
|
+
// A scaffold is new without rendering it; otherwise the page as it is now decides (a capture that failed on a
|
|
145
|
+
// real file keeps it existing).
|
|
146
|
+
const current = found && !isScaffold(found.file) ? await captureCurrent(dir, r, session, { url }).catch((e) => ({ error: e.message })) : null;
|
|
147
|
+
const exists = Boolean(current) && hasContent(found, current);
|
|
148
|
+
if (exists) return { text: existingBrief(dir, job, { d, r, name, session, current }), existing: true };
|
|
149
|
+
// A new page on a site that already has a look (its home page is real): the site's brand, components and buttons
|
|
150
|
+
// carry over, unless the job asks for a new look. A docs page for palate's own site was planned with a new
|
|
151
|
+
// system and press quotes.
|
|
152
|
+
const home = r !== "/" ? routeFile(dir, "/") : null;
|
|
153
|
+
if (home && !isScaffold(home.file) && !NEW_LOOK.test(job) && !direction) return { text: existingBrief(dir, job, { d, r, name, session, current: null, site: slash(path.relative(dir, home.file)) }), existing: true };
|
|
154
|
+
const chosen = chosenDirection(dir, { name: direction, route: r });
|
|
155
|
+
// The research for this page: the direction's, else the newest research in the project.
|
|
156
|
+
const research = chosen?.research ? loadResearch(dir, chosen.research) : loadResearch(dir, "latest");
|
|
157
|
+
const u = research?.understanding ?? null;
|
|
158
|
+
const tags = [...new Set([...(u?.tags ?? []), ...(u?.mood ?? []), ...kindTags({ screen: d.screen, kind: d.kind, route: r, words: job })])];
|
|
159
|
+
const taste = tasteFor(kindWords(d.screen, d.kind, name), { sections: 1, lean: true, project: projectOf(dir) });
|
|
160
|
+
const rules = rulesFor(kindTags({ screen: d.screen, kind: d.kind, route: r, words: job }), { route: r, project: projectOf(dir) });
|
|
161
|
+
const out = path.join(session, "blueprint.json");
|
|
162
|
+
const starter = chosen?.idea?.starter ?? null;
|
|
163
|
+
const recent = recentLooks({ excludePid: projectId(dir) });
|
|
164
|
+
const brief = [
|
|
165
|
+
`# Blueprint: ${job}`,
|
|
166
|
+
"",
|
|
167
|
+
"You are the art director and planner. Decide the whole design before anyone writes code, then write it as JSON. Don't build anything. Every later step follows this: the builder builds it section by section, and an independent judge checks each section against it and against strong real sites.",
|
|
168
|
+
"",
|
|
169
|
+
"## Read first",
|
|
170
|
+
`- The project's README and data (${slash(dir)}): every fact, record and number on the page comes from there.`,
|
|
171
|
+
research ? `- The research: ${slash(path.join(research.dir, "research.md"))} and its contact sheet (${slash(path.join(research.dir, "contact.png"))}). Look at the screenshots, not only the notes.` : "- No research for this page. Use what you know of the category's best sites, but copy nothing.",
|
|
172
|
+
chosen ? `- The chosen direction: **${chosen.idea.name}**: ${chosen.idea.summary}${chosen.idea.metaphor ? ` Metaphor: ${chosen.idea.metaphor}` : ""}${chosen.idea.signature ? ` Signature: ${chosen.idea.signature.move}: ${chosen.idea.signature.moment}` : ""}${chosen.note ? ` The user's note: "${chosen.note}"` : ""}` : "- No direction was chosen: decide one yourself (a metaphor from the product's own world, one signature moment).",
|
|
173
|
+
starter ? `- The direction's starter system (refine it; keep its spirit): ${JSON.stringify(starter)}` : null,
|
|
174
|
+
"",
|
|
175
|
+
"## 1. Understand it like a designer (write it down in `spec`)",
|
|
176
|
+
"- Who uses it, where, on what device, and the one moment they care about (for a booking tool: the message that their thing is ready, not the booking form).",
|
|
177
|
+
"- Mood: 3–5 words you can defend from the audience and the product, not from the job sentence alone. A craft product is usually warm and tactile; a developer tool precise; a children's charity bright. Don't default to \"minimal\".",
|
|
178
|
+
"- **Category truths (keep)**: the materials, objects, rituals and words that make this category itself (pottery: clay, glaze, the kiln, hands, the shelf of finished pieces). Competitors using a truth doesn't make it a cliché.",
|
|
179
|
+
"- **Clichés (avoid)**: specific executions everyone repeats (the stock photo of hands on a wheel, terracotta-on-beige SaaS template, a centred hero over a framed dashboard, three equal feature cards). Avoid the execution, keep the truth.",
|
|
180
|
+
"",
|
|
181
|
+
"## 2. The system (`system`)",
|
|
182
|
+
`- colours, by role: ${COLOUR_ROLES.map((c) => `\`${c}\``).join(", ")}, optionally \`dark\` (a deep band colour) and \`surface-2\`. \`material\` is a tone from the product's world used for at least one band, texture or image treatment. AA contrast: ink and muted on bg and surface ≥ 4.5, accent on bg ≥ 3, on-accent on accent ≥ 4.5.`,
|
|
183
|
+
"- type: `display`, `body` (Google Fonts / OFL / system only; a display face with character, not Inter/Roboto/Geist), `source`, `scale` (6–8 px sizes), `numbers` (\"tabular body\": numbers, times and prices in the body face with tabular figures), `mono` only if the page shows real code.",
|
|
184
|
+
"- radius (3–4 values, nested), spacing (unit and scale), motion (one sentence: the character and the one orchestrated moment).",
|
|
185
|
+
"- Shadows are computed by palate from your colours (layered, ink-tinted): don't specify them. The buttons are yours to decide (2c); palate writes their CSS.",
|
|
186
|
+
"",
|
|
187
|
+
"## 2b. The look (`look`): this product's own, not palate's house style",
|
|
188
|
+
"Decide the page's visual language from the research (what the category's best sites do, the product's materials and truths), and make it unlike palate's recent pages below. palate refuses a look that repeats one of them on too many of these: the ground, texture, the headline's type genre, the hero composition, the header, the call to action's style, how the main object is framed.",
|
|
189
|
+
`- \`ground\`: ${GROUNDS.join(" | ")} (white is fine; cream paper is not a default). \`groundWhy\`: which truth or reference it comes from.`,
|
|
190
|
+
"- `texture`: true only if the product's material calls for it (paper for a bindery, speckle for clay); most pages have none.",
|
|
191
|
+
`- \`header\`: ${HEADERS.join(" | ")} (not always a logo left, links and a pill on the right); \`frame\`: ${FRAMES.join(" | ")} (how the main object or image is presented). (\`cta\` is derived from the buttons below.)`,
|
|
192
|
+
"- The headline's type genre comes from system.type.display and the hero composition from the hero's archetype: choose them to fit this product, not palate's habit.",
|
|
193
|
+
"- `influences`: at least two research references (by their R-ids) and the one decision each one informs (`{ \"ref\": \"R2\", \"takes\": \"a full-bleed photograph as the hero, the way the best bakeries lead with the bread\" }`): ideas, never their expression.",
|
|
194
|
+
...(recent.length ? ["", `palate's recent pages (yours must differ from each on at least ${MIN_DIFFERENT} of those axes, and its buttons from each page's):`, ...recent.map((x) => `- ${x.project}: ${describePlanned(x.look)}`)] : []),
|
|
195
|
+
"",
|
|
196
|
+
"## 2c. The buttons (`look.buttons`): this product's own button language",
|
|
197
|
+
"A button carries a lot of a brand's voice. palate's recent pages all had the same one (a filled box about 48px tall, a semi-bold sentence-case label, a small radius, a ring and a soft shadow, beside an underlined link) and only the colour changed: don't repeat it. Look at the buttons in the research screenshots, then choose from what the product is (its counter, its tools, its paper, its keyboard), not from habit. palate refuses a language that repeats a recent page's on most of these axes:",
|
|
198
|
+
...Object.entries(BUTTON_AXES).map(([k, v]) => `- \`${k}\`: ${v.join(" | ")}`),
|
|
199
|
+
"Start from one of palate's languages (ranked for this product) and change an axis or two if the research says so, or set `\"language\": \"custom\"` and all six axes:",
|
|
200
|
+
...rankLanguages([...tags, ...String(job).toLowerCase().split(/[^a-z]+/)]).map((n) => `- \`${n}\`: ${BUTTON_LANGUAGES[n].words}. Fits ${BUTTON_LANGUAGES[n].fits}.`),
|
|
201
|
+
`Write it as \`"buttons": { "language": "stamped-tag", "size": "large", "why": "R3's order slips: the action is a ticket" }\`. palate generates \`.btn-primary\` and \`.btn-secondary\` from it (states, focus, contrast and a 44px tap area included). More on each language: ${slash(path.join(KB_SECTIONS, "_buttons.md"))}.`,
|
|
202
|
+
"",
|
|
203
|
+
"## 3. Imagery and material (`imagery`)",
|
|
204
|
+
"- `treatment`: one treatment for every photo on the page (duotone in ink and material, tone-matched, or none).",
|
|
205
|
+
"- Every section gets an imagery decision (below). Sources, in order: the user's own photos; CC0 museum objects (The Met, Cleveland), or Commons photos with `--sources commons`, found with ONE batch for the whole page: `palate imagery \"<query 1> | <query 2> | …\"` (6 to 12 searches side by side, about a minute; a second batch only for a slot still empty; never one search per call: 60 single searches cost a benchmark page 37 minutes); code-drawn texture (`texture:grain|paper|speckle|halftone|linen`); the product's own UI built in code (`product-ui`); illustration drawn in code; type alone (`type`) for at most two sections. Never stock photos you can't license, never copied images.",
|
|
206
|
+
"",
|
|
207
|
+
"## 4. The sections (`sections`, in order)",
|
|
208
|
+
"Each: `{ \"id\": \"hero\", \"kind\": \"hero\", \"archetype\": \"<id from the menu>\", \"job\": \"what the visitor must understand or do here\", \"content\": \"the real content it carries, from the README\", \"surface\": \"bg|surface|material|dark|image|accent\", \"imagery\": \"photo:<query>|texture:<name>|product-ui|illustration:<what>|type|none\", \"signature\": true|false }`.",
|
|
209
|
+
"Hard rules palate checks:",
|
|
210
|
+
"- An archetype from the menu below for every section (it brings the structure, the finish and the checks the judge will use).",
|
|
211
|
+
"- No two neighbouring sections share a shape; no shape more than twice on the page; at least 3 different shapes.",
|
|
212
|
+
"- Surfaces vary: at least two surface tones, no more than two neighbours on the same surface, and at least one band of material, dark or image on a marketing page.",
|
|
213
|
+
"- At least one section with a photo, illustration or texture; at most two sections that are type or none only.",
|
|
214
|
+
"- Exactly one section carries the signature moment.",
|
|
215
|
+
"- Only sections the job needs, in an order that tells the story; every one of them gets the same care as the hero.",
|
|
216
|
+
"",
|
|
217
|
+
"### The section menu (palate's archetypes, ranked for this product)",
|
|
218
|
+
...menu(tags),
|
|
219
|
+
"",
|
|
220
|
+
"## 5. A second option for the hero (`alternatives`)",
|
|
221
|
+
"Name one other hero archetype that would also fit (`\"alternatives\": { \"hero\": \"<id>\" }`). On a full build, both are built and judged side by side.",
|
|
222
|
+
"",
|
|
223
|
+
...(rules.length ? ["## The user's rules this must respect", ...rules.map((x) => `- ${x.id} ${x.rule}`), ""] : []),
|
|
224
|
+
taste ? `## Whose taste\n${taste.trim()}\n` : "",
|
|
225
|
+
"## Write it",
|
|
226
|
+
`JSON to ${slash(out)}: { "product", "spec": { "audience", "moment", "mood": [..], "truths": [..], "cliches": [..] }, "system": { "colours": {..}, "type": {..}, "radius": [..], "spacing": {..}, "motion": "" }, "look": { "ground", "groundWhy", "texture", "header", "frame", "buttons": { "language", "why" }, "influences": [..] }, "imagery": { "treatment": "" }, "sections": [..], "alternatives": { "hero": "" } }`,
|
|
227
|
+
`Then: \`palate blueprint --file ${slash(out)}\` (CLI) or palate_blueprint with file. It says what fails; fix and run it again until it passes.`,
|
|
228
|
+
]
|
|
229
|
+
.filter((l) => l != null)
|
|
230
|
+
.join("\n");
|
|
231
|
+
const file = path.join(session, "blueprint-brief.md");
|
|
232
|
+
fs.mkdirSync(session, { recursive: true });
|
|
233
|
+
fs.writeFileSync(file, brief);
|
|
234
|
+
fs.writeFileSync(path.join(session, "blueprint-meta.json"), JSON.stringify({ name, job, route: r, screen: d.screen, kind: d.kind, direction: chosen ? chosen.name : null, created: new Date().toISOString() }, null, 1));
|
|
235
|
+
// The build's timeline starts here (timeline.mjs needs a loop.json; palate build fills it in later).
|
|
236
|
+
if (!fs.existsSync(path.join(session, "loop.json"))) fs.writeFileSync(path.join(session, "loop.json"), JSON.stringify({ name, job, mode: "build", stage: "blueprint", route: r, created: new Date().toISOString() }, null, 2));
|
|
237
|
+
fs.rmSync(path.join(session, "events.jsonl"), { force: true });
|
|
238
|
+
mark(dir, name, "blueprint_brief");
|
|
239
|
+
const text = [
|
|
240
|
+
`# Blueprint ${r}: decide the design before code`,
|
|
241
|
+
"",
|
|
242
|
+
found ? `${slash(path.relative(dir, found.file))} is only a scaffold so far (nothing on it to keep): this is a new page.` : null,
|
|
243
|
+
`Brief: ${slash(file)}`,
|
|
244
|
+
`Plan it yourself or with a fresh subagent: "Read ${slash(file)} and follow it exactly." Then palate blueprint --file ${slash(out)}.`,
|
|
245
|
+
]
|
|
246
|
+
.filter((l) => l != null)
|
|
247
|
+
.join("\n");
|
|
248
|
+
return { text, existing: false };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// The existing-page form of the brief.
|
|
252
|
+
// site: a new page on a site that already has a look (the home page's file): every section is added, in the site's brand.
|
|
253
|
+
function existingBrief(dir, job, { d, r, name, session, current, site = null }) {
|
|
254
|
+
const research = loadResearch(dir, "latest");
|
|
255
|
+
const u = research?.understanding ?? null;
|
|
256
|
+
const tags = [...new Set([...(u?.tags ?? []), ...(u?.mood ?? []), ...kindTags({ screen: d.screen, kind: d.kind, route: r, words: job })])];
|
|
257
|
+
const taste = tasteFor(kindWords(d.screen, d.kind, name), { sections: 1, lean: true, project: projectOf(dir) });
|
|
258
|
+
const rules = rulesFor(kindTags({ screen: d.screen, kind: d.kind, route: r, words: job }), { route: r, project: projectOf(dir) });
|
|
259
|
+
const out = path.join(session, "blueprint.json");
|
|
260
|
+
const brief = [
|
|
261
|
+
site ? `# Blueprint (a new page on an existing site): ${job}` : `# Blueprint (an existing page): ${job}`,
|
|
262
|
+
"",
|
|
263
|
+
site ? `You are the art director. This is a new page on a site that already has a look: its home page (${site}), header, footer, tokens, type and buttons. The new page belongs to that site: keep all of it and plan only the new page's sections, every one with "action": "add" and an archetype. Reuse the site's own header, footer and components; don't introduce a new system, button language or look. Write it as JSON; don't build anything. The builder then builds each section, and an independent judge checks the result against strong real sites.` : "You are the art director. This page exists. Decide, before anyone writes code, what changes: which sections stay as they are, which are rebuilt to a stronger archetype, which go, and what's added. Keep the project's brand. Write it as JSON; don't build anything. The builder then rebuilds only what you mark, and an independent judge checks the result against strong real sites and against the page as it is now (it may not get worse).",
|
|
264
|
+
"",
|
|
265
|
+
...(site ? [] : ["## The page as it is now (open every image)"]),
|
|
266
|
+
...(site ? [] : current?.sections?.length
|
|
267
|
+
? [`- First screen: ${slash(current.fold)} · whole page: ${slash(current.full)}`, ...current.sections.map((s) => `- Section ${s.n}: ${s.name} (${s.height}px): ${slash(s.file)}`)]
|
|
268
|
+
: [`- palate couldn't render it${current?.error ? ` (${current.error})` : ""}: run the dev server or pass --url, then palate blueprint again.`]),
|
|
269
|
+
"",
|
|
270
|
+
"## Read first",
|
|
271
|
+
site ? `- The project's README and data, its home page (${site}), its layout, global styles and shared components (${slash(dir)}): the tokens, faces, header, footer and buttons this page reuses.` : `- The project's README, data and its current code for this route (${slash(dir)}).`,
|
|
272
|
+
research ? `- Research for this project: ${slash(path.join(research.dir, "research.md"))}.` : "- No research for this project; use what you know of the category's best sites, copying nothing.",
|
|
273
|
+
"",
|
|
274
|
+
"## 1. `spec`",
|
|
275
|
+
site ? "- audience, the moment they care about, mood (3–5 words, defended from the site as it is), **category truths (keep)** and **clichés (avoid)** for this kind of page." : "- audience, the moment they care about, mood (3–5 words, defended), **category truths (keep)** and **clichés (avoid)**, as for a new page; also `problems`: what's below the bar on the current page, section by section, in plain words.",
|
|
276
|
+
"",
|
|
277
|
+
"## 2. `system`: keep the brand",
|
|
278
|
+
"- `\"keep\": true` and the existing tokens by role, as hex where you can read them (`colours`: bg, surface, ink, muted, line, accent, on-accent, and material if the page has one), plus `type` (the faces already in use). palate computes the finish tokens (layered shadows) from these; the project keeps its own buttons. Change the brand only if the job asks for a rebrand (then `\"keep\": false` and a full system, as for a new page).",
|
|
279
|
+
"",
|
|
280
|
+
"## 3. `sections`, in the page's new order",
|
|
281
|
+
"Each: `{ \"id\", \"kind\", \"action\": \"keep|upgrade|remove|add\", \"current\": <section number now, or null for add>, \"archetype\": \"<id>\" (required for upgrade and add), \"job\", \"content\", \"surface\", \"imagery\", \"why\": \"what's below the bar now, or why it stays\" }`.",
|
|
282
|
+
"- Upgrade a section only for a reason you can name (the judge will compare before and after). Keep what already works exactly as it is.",
|
|
283
|
+
"- Add only what the request asks for: one section per thing it names. Content it asks for (answers to buyers' questions, fees, a trial note) goes inside that section unless the request names another section. Never add an ending, a footer or sections it doesn't name: name them to the user as suggestions instead.",
|
|
284
|
+
"- The rebuilt page still follows the variety rules: no two neighbours with the same shape, surfaces that vary, real material somewhere.",
|
|
285
|
+
"",
|
|
286
|
+
"### The section menu (palate's archetypes, ranked for this product)",
|
|
287
|
+
...menu(tags),
|
|
288
|
+
"",
|
|
289
|
+
...(rules.length ? ["## The user's rules this must respect", ...rules.map((x) => `- ${x.id} ${x.rule}`), ""] : []),
|
|
290
|
+
taste ? `## Whose taste\n${taste.trim()}\n` : "",
|
|
291
|
+
"## Write it",
|
|
292
|
+
...(site ? ["## 4. `copy`: new", "Every string is new and comes from the project's README, data and code (commands, versions, prices): `\"copy\": \"edit\"`."] : ["## 4. `copy`: locked by default", "palate captured every string the page shows now. `\"copy\": \"keep\"` (the default): every one stays word for word, and the gate fails a change that loses one. `\"edit\"` only when the job asks for new copy; new labels are listed either way, for the hand-off."]),
|
|
293
|
+
"",
|
|
294
|
+
`JSON to ${slash(out)}: { "mode": "existing", "product", "spec": {..}, "system": { "keep": true, "colours": {..}, "type": {..} }, "copy": "${site ? "edit" : "keep"}", "imagery": { "treatment": "" }, "sections": [..] }`,
|
|
295
|
+
`Then: \`palate blueprint --file ${slash(out)}\` until it passes, then palate build again with the same job.`,
|
|
296
|
+
]
|
|
297
|
+
.filter((l) => l != null)
|
|
298
|
+
.join("\n");
|
|
299
|
+
const file = path.join(session, "blueprint-brief.md");
|
|
300
|
+
fs.mkdirSync(session, { recursive: true });
|
|
301
|
+
fs.writeFileSync(file, brief);
|
|
302
|
+
fs.writeFileSync(path.join(session, "blueprint-meta.json"), JSON.stringify({ name, job, route: r, screen: d.screen, kind: d.kind, existing: true, site: site ?? undefined, before: current?.full ? { fold: current.fold, full: current.full, strings: current.strings ?? null } : null, created: new Date().toISOString() }, null, 1));
|
|
303
|
+
if (!fs.existsSync(path.join(session, "loop.json"))) fs.writeFileSync(path.join(session, "loop.json"), JSON.stringify({ name, job, mode: "build", stage: "blueprint", route: r, created: new Date().toISOString() }, null, 2));
|
|
304
|
+
fs.rmSync(path.join(session, "events.jsonl"), { force: true });
|
|
305
|
+
mark(dir, name, "blueprint_brief");
|
|
306
|
+
return [site ? `# Blueprint ${r} (a new page on the site, in its own look): decide the sections before code` : `# Blueprint ${r} (existing page): decide what changes before code`, "", `Brief: ${slash(file)}`, `Plan it yourself or with a fresh subagent: "Read ${slash(file)} and follow it exactly." Then palate blueprint --file ${slash(out)}.`].join("\n");
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// The hard checks on a blueprint. Returns { ok, problems, warnings }.
|
|
310
|
+
// others: layout records of pages in the user's other projects (readLayouts), to catch a repeated look.
|
|
311
|
+
// recent: palate's recent looks in other projects (lookplan.recentLooks), for the house-look check.
|
|
312
|
+
export function validateBlueprint(bp, { screen = "landing", others = [], recent = [], research = false, tags = [] } = {}) {
|
|
313
|
+
const problems = [];
|
|
314
|
+
const warnings = [];
|
|
315
|
+
if (!bp || typeof bp !== "object") return { ok: false, problems: ["not a JSON object"], warnings };
|
|
316
|
+
const spec = bp.spec ?? {};
|
|
317
|
+
if ((spec.mood ?? []).length < 2) problems.push("spec.mood: 3–5 words defended from the audience and product");
|
|
318
|
+
if ((spec.truths ?? []).length < 2) problems.push("spec.truths: at least two category truths to keep");
|
|
319
|
+
if ((spec.cliches ?? []).length < 2) problems.push("spec.cliches: at least two specific executions to avoid");
|
|
320
|
+
if (!spec.moment) warnings.push("spec.moment: the one moment the audience cares about");
|
|
321
|
+
// An existing page keeps its brand: its colours and faces are the project's, so they aren't held to the new-page
|
|
322
|
+
// system rules (only checked where given), and only the sections it rebuilds need an archetype.
|
|
323
|
+
const existing = bp.mode === "existing";
|
|
324
|
+
const keep = existing && bp.system?.keep !== false;
|
|
325
|
+
// System
|
|
326
|
+
const sys = bp.system ?? {};
|
|
327
|
+
const c = sys.colours ?? sys.colors ?? {};
|
|
328
|
+
if (!keep) for (const k of COLOUR_ROLES) if (!hexRgb(c[k])) problems.push(`system.colours.${k} must be a hex colour (#rrggbb)`);
|
|
329
|
+
const need = (fg, bg, min) => {
|
|
330
|
+
const r = c[fg] && c[bg] ? contrast(c[fg], c[bg]) : null;
|
|
331
|
+
if (r != null && r < min) problems.push(`contrast: ${fg} ${c[fg]} on ${bg} ${c[bg]} is ${r}:1, needs ${min}:1`);
|
|
332
|
+
};
|
|
333
|
+
need("ink", "bg", 4.5);
|
|
334
|
+
need("muted", "bg", 4.5);
|
|
335
|
+
need("ink", "surface", 4.5);
|
|
336
|
+
need("muted", "surface", 4.5);
|
|
337
|
+
need("accent", "bg", 3);
|
|
338
|
+
need("on-accent", "accent", 4.5);
|
|
339
|
+
if (c.dark) need("bg", "dark", 4.5);
|
|
340
|
+
if (hexRgb(c.bg) && hexRgb(c.surface) && dist(c.bg, c.surface) < 6) warnings.push("bg and surface are almost the same colour: the page will read as one flat tone");
|
|
341
|
+
if (hexRgb(c.material) && hexRgb(c.bg) && dist(c.material, c.bg) < 18) problems.push("material is too close to bg: it has to read as its own band or texture");
|
|
342
|
+
const t = sys.type ?? {};
|
|
343
|
+
if (!keep) {
|
|
344
|
+
if (!t.display || !t.body) problems.push("system.type needs display and body");
|
|
345
|
+
if (t.display && GENERIC_DISPLAY.test(String(t.display).trim())) problems.push(`system.type.display ${t.display} is a default face: choose one with character`);
|
|
346
|
+
if (!/^(google-fonts|ofl|system)$/i.test(t.source ?? "")) problems.push('system.type.source must be "google-fonts", "ofl" or "system"');
|
|
347
|
+
if (!Array.isArray(t.scale) || t.scale.length < 5) problems.push("system.type.scale: 6–8 sizes in px");
|
|
348
|
+
if (t.mono && !/code/i.test(String(t.monoFor ?? t.mono_for ?? ""))) warnings.push("system.type.mono: only for real code (say monoFor: \"code\"); numbers use the body face with tabular figures");
|
|
349
|
+
if (!Array.isArray(sys.radius) || sys.radius.length < 2) problems.push("system.radius: 3–4 values");
|
|
350
|
+
if (!sys.spacing) problems.push("system.spacing: unit and scale");
|
|
351
|
+
if (!sys.motion) warnings.push("system.motion: one sentence");
|
|
352
|
+
}
|
|
353
|
+
// Another project's look: the same display face and nearly the same accent as a page made elsewhere.
|
|
354
|
+
for (const o of keep ? [] : others) {
|
|
355
|
+
const fonts = (o.style?.fonts ?? []).map((f) => String(f).toLowerCase());
|
|
356
|
+
const sameFace = t.display && fonts.includes(String(t.display).toLowerCase());
|
|
357
|
+
const nearAccent = hexRgb(o.style?.accent) && hexRgb(c.accent) && dist(o.style.accent, c.accent) < 40;
|
|
358
|
+
if (sameFace && nearAccent) {
|
|
359
|
+
problems.push(`the display face ${t.display} and accent ${c.accent} repeat ${o.project} ${o.route ?? ""} (${o.style.accent}): this product needs its own`);
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// Sections
|
|
364
|
+
const s = Array.isArray(bp.sections) ? bp.sections : [];
|
|
365
|
+
const marketing = !/^(app|dashboard|settings|table|team)$/.test(screen);
|
|
366
|
+
if (s.length < (marketing && !existing ? 4 : 1)) problems.push(`sections: ${s.length}; a ${screen} page needs at least ${marketing ? 4 : 1}`);
|
|
367
|
+
if (existing) {
|
|
368
|
+
s.forEach((x, i) => {
|
|
369
|
+
if (!["keep", "upgrade", "remove", "add"].includes(x.action)) problems.push(`sections[${i}] (${x.id ?? "?"}): action must be keep, upgrade, remove or add`);
|
|
370
|
+
});
|
|
371
|
+
if (!s.some((x) => x.action === "upgrade" || x.action === "add" || x.action === "remove")) problems.push("nothing changes: mark at least one section upgrade, add or remove (or this is a direct fix, not a build)");
|
|
372
|
+
}
|
|
373
|
+
// On an existing page, sections that stay as they are have no archetype; removed ones aren't on the new page.
|
|
374
|
+
const changing = (x) => !existing || x.action === "upgrade" || x.action === "add";
|
|
375
|
+
const lib = Sections?.getSection ? (id) => Sections.getSection(id) : null;
|
|
376
|
+
const shapes = [];
|
|
377
|
+
s.forEach((x, i) => {
|
|
378
|
+
const at = `sections[${i}] (${x.id ?? x.kind ?? "?"})`;
|
|
379
|
+
if (!x.kind) problems.push(`${at}: kind missing`);
|
|
380
|
+
if (existing && x.action === "remove") return shapes.push(null);
|
|
381
|
+
if (!x.job && changing(x)) problems.push(`${at}: job missing (what the visitor must understand or do here)`);
|
|
382
|
+
if (!x.content) warnings.push(`${at}: content missing (the real content it carries)`);
|
|
383
|
+
let shape = x.shape ?? null;
|
|
384
|
+
if (!changing(x)) {
|
|
385
|
+
shapes.push(x.archetype && lib?.(x.archetype) ? lib(x.archetype).shape : null);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (lib) {
|
|
389
|
+
const a = x.archetype ? lib(x.archetype) : null;
|
|
390
|
+
if (!a) problems.push(`${at}: archetype ${x.archetype ? `"${x.archetype}" isn't in the library` : "missing"}: pick one from the menu`);
|
|
391
|
+
else {
|
|
392
|
+
shape = a.shape;
|
|
393
|
+
if (x.kind && a.kind !== x.kind && !(x.kind === "footer" && a.kind === "footer")) warnings.push(`${at}: archetype ${a.id} is a ${a.kind} archetype, section kind is ${x.kind}`);
|
|
394
|
+
}
|
|
395
|
+
} else if (!shape) problems.push(`${at}: shape missing`);
|
|
396
|
+
shapes.push(shape);
|
|
397
|
+
if (!SURFACES.includes(x.surface)) problems.push(`${at}: surface must be one of ${SURFACES.join(", ")}`);
|
|
398
|
+
if (!IMAGERY_KINDS.includes(imageryKind(x.imagery))) problems.push(`${at}: imagery must start with one of ${IMAGERY_KINDS.join(", ")} (e.g. "photo:stoneware bowl", "texture:speckle")`);
|
|
399
|
+
});
|
|
400
|
+
// Variety
|
|
401
|
+
const real = s.map((x, i) => ({ x, shape: shapes[i] })).filter((y) => y.x.kind !== "footer" && !(existing && y.x.action === "remove"));
|
|
402
|
+
for (let i = 1; i < real.length; i++) if (real[i].shape && real[i].shape === real[i - 1].shape) problems.push(`sections ${real[i - 1].x.id} and ${real[i].x.id} are neighbours with the same shape (${real[i].shape}): change one`);
|
|
403
|
+
const counts = {};
|
|
404
|
+
for (const y of real) if (y.shape) counts[y.shape] = (counts[y.shape] ?? 0) + 1;
|
|
405
|
+
for (const [k, n] of Object.entries(counts)) if (n > 2) problems.push(`shape ${k} is used ${n} times; at most twice`);
|
|
406
|
+
if (marketing && !existing && Object.keys(counts).length < Math.min(3, real.length)) problems.push(`only ${Object.keys(counts).length} different shapes; at least 3`);
|
|
407
|
+
const surf = s.filter((x) => !(existing && x.action === "remove")).map((x) => x.surface);
|
|
408
|
+
// On an existing page the plan can't see every current section's ground and material: those become warnings.
|
|
409
|
+
// On an existing page the plan can't see the kept sections' grounds and material: those checks don't run there.
|
|
410
|
+
const soft = (msg) => (existing ? null : problems.push(msg));
|
|
411
|
+
if (marketing && new Set(surf.filter(Boolean)).size < 2) soft("every section is on the same surface: at least two tones");
|
|
412
|
+
for (let i = 2; i < surf.length; i++) if (surf[i] && surf[i] === surf[i - 1] && surf[i] === surf[i - 2]) problems.push(`three neighbours on the ${surf[i]} surface (${s[i - 2].id}, ${s[i - 1].id}, ${s[i].id})`);
|
|
413
|
+
if (marketing && !surf.some((v) => ["material", "dark", "image"].includes(v))) soft("no band of material, dark or image: the page will be one flat colour");
|
|
414
|
+
const kinds = s.filter((x) => !(existing && x.action === "remove")).map((x) => imageryKind(x.imagery));
|
|
415
|
+
if (marketing && !kinds.some((k) => ["photo", "illustration", "texture"].includes(k))) soft("no section has a photo, illustration or texture: the page has no material");
|
|
416
|
+
if (kinds.filter((k) => k === "type" || k === "none").length > 2) problems.push("more than two sections are type or none only");
|
|
417
|
+
const sig = s.filter((x) => x.signature);
|
|
418
|
+
if (marketing && !existing && sig.length !== 1) problems.push(`exactly one section carries the signature moment (now ${sig.length})`);
|
|
419
|
+
if (bp.alternatives?.hero && lib && !lib(bp.alternatives.hero)) warnings.push(`alternatives.hero "${bp.alternatives.hero}" isn't in the library`);
|
|
420
|
+
// The look: this product's own, not palate's house style (new pages and rebrands; a kept brand is the brand's).
|
|
421
|
+
if (!keep) {
|
|
422
|
+
const lk = bp.look ?? {};
|
|
423
|
+
if (!GROUNDS.includes(lk.ground)) problems.push(`look.ground: one of ${GROUNDS.join(", ")}, decided from the research`);
|
|
424
|
+
if (typeof lk.texture !== "boolean") problems.push("look.texture: true or false (true only when the product's material calls for it)");
|
|
425
|
+
if (!HEADERS.includes(lk.header)) problems.push(`look.header: one of ${HEADERS.join(", ")}`);
|
|
426
|
+
// The buttons: a language from the library (or custom), required; the coarse cta follows from it.
|
|
427
|
+
const bt = buttonProblems(lk.buttons);
|
|
428
|
+
problems.push(...bt.problems);
|
|
429
|
+
warnings.push(...bt.warnings);
|
|
430
|
+
if (lk.cta != null && !CTAS.includes(lk.cta)) problems.push(`look.cta: one of ${CTAS.join(", ")} (or leave it out: palate derives it from look.buttons)`);
|
|
431
|
+
else if (lk.cta && lk.buttons && !bt.problems.length && ctaOf(lk.buttons) !== lk.cta) warnings.push(`look.cta says ${lk.cta} but the buttons are ${ctaOf(lk.buttons)}: palate goes by the buttons`);
|
|
432
|
+
if (!FRAMES.includes(lk.frame)) problems.push(`look.frame: one of ${FRAMES.join(", ")}`);
|
|
433
|
+
const infl = (lk.influences ?? []).filter((x) => x?.ref && x?.takes);
|
|
434
|
+
if (research && infl.length < 2) problems.push("look.influences: at least two research references (R-ids) and the decision each informs");
|
|
435
|
+
if (!research && infl.length < 1) warnings.push("look.influences: name the sites or traditions this look learns from");
|
|
436
|
+
const hexOk = /^#[0-9a-f]{6}$/i.test(c.bg ?? "");
|
|
437
|
+
if (hexOk && lk.ground && !["photo", "colour"].includes(lk.ground)) {
|
|
438
|
+
const g = plannedLook({ system: { colours: { bg: c.bg } } }).ground;
|
|
439
|
+
if (g && g !== lk.ground && !(lk.ground.startsWith("light") && g.startsWith("light")) && !(lk.ground === "white" && g.startsWith("light"))) warnings.push(`look.ground says ${lk.ground} but colours.bg ${c.bg} reads as ${g}`);
|
|
440
|
+
}
|
|
441
|
+
for (const p of lookProblems(plannedLook(bp, { getSection: Sections?.getSection }), recent, { tags })) problems.push(p.line);
|
|
442
|
+
}
|
|
443
|
+
return { ok: !problems.length, problems, warnings };
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Finishing values palate derives from the colours, so every build starts from the same craft: layered shadows
|
|
447
|
+
// tinted with the ink, and the primary button's gradient, inner highlight and ring.
|
|
448
|
+
export function finishTokens(c) {
|
|
449
|
+
const ink = shadowTint(c);
|
|
450
|
+
const accent = c.accent ?? "#333333";
|
|
451
|
+
return {
|
|
452
|
+
"accent-hi": mix(accent, "#ffffff", 0.12),
|
|
453
|
+
"accent-lo": mix(accent, "#000000", 0.18),
|
|
454
|
+
"accent-ring": mix(accent, "#000000", 0.32),
|
|
455
|
+
"shadow-raised": `0 1px 1px rgb(${rgbTriplet(ink)} / 0.04), 0 2px 4px -1px rgb(${rgbTriplet(ink)} / 0.06), 0 12px 24px -8px rgb(${rgbTriplet(ink)} / 0.12)`,
|
|
456
|
+
"shadow-float": `0 1px 2px rgb(${rgbTriplet(ink)} / 0.05), 0 8px 16px -4px rgb(${rgbTriplet(ink)} / 0.08), 0 32px 64px -16px rgb(${rgbTriplet(ink)} / 0.18)`,
|
|
457
|
+
"button-primary": `linear-gradient(180deg, ${mix(accent, "#ffffff", 0.12)}, ${accent})`,
|
|
458
|
+
"button-inset": `inset 0 1px 0 rgb(255 255 255 / 0.22), 0 0 0 1px ${mix(accent, "#000000", 0.32)}, 0 1px 2px rgb(${rgbTriplet(ink)} / 0.18)`,
|
|
459
|
+
"hairline": `rgb(${rgbTriplet(c.ink ?? "#111111")} / 0.10)`, // lines follow the text colour, light on a dark page
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const fam = (f, fallback) => (f ? `"${f}", ${fallback}` : fallback);
|
|
464
|
+
// Display faces that are serifs (their fallback is a serif; everything else falls back to sans).
|
|
465
|
+
const SERIF_FACE = /serif|fraunces|gloock|playfair|garamond|caslon|baskerville|lora|merriweather|cormorant|newsreader|spectral|crimson|bodoni|didot|literata|source serif|young serif|dm serif|instrument serif|eb garamond|libre|domine|zilla|roboto slab|bitter|vollkorn|alegreya(?! sans)|petrona|gelasio|fraunces|recoleta|tiempos/i;
|
|
466
|
+
// tokens.css (plain custom properties) and tokens.tailwind.css (Tailwind v4 @theme: the same names become
|
|
467
|
+
// utilities like bg-surface, text-ink, font-display, shadow-raised).
|
|
468
|
+
// opts.flatButtons: kept for callers (no button is a gradient now). opts.rules: the user's rule texts, which win
|
|
469
|
+
// over the button language (no pills, no uppercase…).
|
|
470
|
+
export function blueprintTokens(bp, { flatButtons = false, rules = [] } = {}) {
|
|
471
|
+
const sys = bp.system ?? {};
|
|
472
|
+
const c = sys.colours ?? sys.colors ?? {};
|
|
473
|
+
const t = sys.type ?? {};
|
|
474
|
+
const f = finishTokens(c);
|
|
475
|
+
const radius = (sys.radius ?? []).map((r, i) => [`--radius-${["sm", "md", "lg", "xl"][i] ?? i}`, String(r).replace(/^(\d+)$/, "$1px")]);
|
|
476
|
+
const scale = (t.scale ?? []).map((px, i) => [`--text-step-${i + 1}`, `${px}px`]);
|
|
477
|
+
const vars = [
|
|
478
|
+
...Object.entries(c).map(([k, v]) => [`--color-${k}`, v]),
|
|
479
|
+
["--font-display", fam(t.display, SERIF_FACE.test(String(t.display ?? "")) ? "ui-serif, Georgia, serif" : "ui-sans-serif, system-ui, sans-serif")],
|
|
480
|
+
["--font-body", fam(t.body, "ui-sans-serif, system-ui, sans-serif")],
|
|
481
|
+
...(t.mono ? [["--font-mono", fam(t.mono, "ui-monospace, monospace")]] : []),
|
|
482
|
+
...scale,
|
|
483
|
+
...radius,
|
|
484
|
+
["--spacing-unit", `${sys.spacing?.unit ?? 8}px`],
|
|
485
|
+
["--shadow-raised", f["shadow-raised"]],
|
|
486
|
+
["--shadow-float", f["shadow-float"]],
|
|
487
|
+
["--color-accent-hi", f["accent-hi"]],
|
|
488
|
+
["--color-accent-lo", f["accent-lo"]],
|
|
489
|
+
["--color-accent-ring", f["accent-ring"]],
|
|
490
|
+
["--color-hairline", f.hairline],
|
|
491
|
+
];
|
|
492
|
+
const body = vars.map(([k, v]) => ` ${k}: ${v};`).join("\n");
|
|
493
|
+
// The names kb/sections/_finish.md and every archetype use (--ink, --paper, --brand, --s-7, --r-lg,
|
|
494
|
+
// --shadow-2, --hairline, --section …), pointed at this blueprint's values.
|
|
495
|
+
const sc = sys.spacing?.scale?.length ? sys.spacing.scale : [4, 8, 12, 16, 24, 32, 48, 64, 96, 128];
|
|
496
|
+
const ts = t.scale ?? [];
|
|
497
|
+
const tAt = (i) => (ts[i] != null ? `${ts[i]}px` : null);
|
|
498
|
+
const r = sys.radius ?? [];
|
|
499
|
+
const aliases = [
|
|
500
|
+
["--ink", "var(--color-ink)"],
|
|
501
|
+
["--ink-2", "var(--color-muted)"],
|
|
502
|
+
// Tertiary text is the muted colour: the blueprint check holds it to AA (a mix toward bg fell under 4.5:1).
|
|
503
|
+
["--ink-3", "var(--color-muted)"],
|
|
504
|
+
["--paper", "var(--color-bg)"],
|
|
505
|
+
["--paper-2", c["surface-2"] ? "var(--color-surface-2)" : "var(--color-surface)"],
|
|
506
|
+
["--surface", "var(--color-surface)"],
|
|
507
|
+
["--material", "var(--color-material)"],
|
|
508
|
+
["--brand", "var(--color-accent)"],
|
|
509
|
+
["--on-brand", "var(--color-on-accent)"],
|
|
510
|
+
["--focus", "var(--color-accent)"],
|
|
511
|
+
...sc.slice(0, 10).map((v, i) => [`--s-${i + 1}`, `${v}px`]),
|
|
512
|
+
["--t-caption", tAt(0)],
|
|
513
|
+
["--t-small", tAt(1)],
|
|
514
|
+
["--t-body", tAt(2)],
|
|
515
|
+
["--t-lead", tAt(3)],
|
|
516
|
+
["--t-h3", tAt(4)],
|
|
517
|
+
["--t-h2", tAt(ts.length - 2) ?? tAt(5)],
|
|
518
|
+
["--t-display", tAt(ts.length - 1)],
|
|
519
|
+
["--r-sm", r[0] != null ? String(r[0]).replace(/^(\d+)$/, "$1px") : null],
|
|
520
|
+
["--r-md", r[1] != null ? String(r[1]).replace(/^(\d+)$/, "$1px") : null],
|
|
521
|
+
["--r-lg", r[2] != null ? String(r[2]).replace(/^(\d+)$/, "$1px") : r[1] != null ? String(r[1]).replace(/^(\d+)$/, "$1px") : null],
|
|
522
|
+
["--r-pill", "999px"],
|
|
523
|
+
["--sh-tint", shadowTint(c)],
|
|
524
|
+
["--shadow-1", "0 0 0 1px color-mix(in oklab, var(--sh-tint) 7%, transparent), 0 1px 2px color-mix(in oklab, var(--sh-tint) 8%, transparent), 0 2px 6px -2px color-mix(in oklab, var(--sh-tint) 8%, transparent)"],
|
|
525
|
+
["--shadow-2", "0 0 0 1px color-mix(in oklab, var(--sh-tint) 7%, transparent), 0 2px 4px -2px color-mix(in oklab, var(--sh-tint) 12%, transparent), 0 16px 32px -12px color-mix(in oklab, var(--sh-tint) 18%, transparent)"],
|
|
526
|
+
["--shadow-3", "0 0 0 1px color-mix(in oklab, var(--sh-tint) 8%, transparent), 0 4px 8px -4px color-mix(in oklab, var(--sh-tint) 14%, transparent), 0 32px 64px -16px color-mix(in oklab, var(--sh-tint) 26%, transparent)"],
|
|
527
|
+
["--hairline", "color-mix(in oklab, var(--color-ink) 12%, transparent)"],
|
|
528
|
+
["--hairline-strong", "color-mix(in oklab, var(--color-ink) 22%, transparent)"],
|
|
529
|
+
["--section", "clamp(72px, 7vw + 24px, 128px)"],
|
|
530
|
+
["--section-tight", "clamp(48px, 4vw + 16px, 80px)"],
|
|
531
|
+
["--container", "1240px"],
|
|
532
|
+
["--gutter", "24px"],
|
|
533
|
+
["--page-x", "clamp(16px, 4vw, 48px)"],
|
|
534
|
+
].filter(([, v]) => v != null);
|
|
535
|
+
const aliasBlock = `/* The names kb/sections/_finish.md and the section archetypes use. */\n:root {\n${aliases.map(([k, v]) => ` ${k}: ${v};`).join("\n")}\n}`;
|
|
536
|
+
// The buttons: the plan's own language (buttons.mjs), never one anatomy for every product. An older plan with
|
|
537
|
+
// only a cta gets the language it implies; an existing page that keeps its brand keeps its own buttons.
|
|
538
|
+
const lang = pageButtons(bp);
|
|
539
|
+
const helpers = [
|
|
540
|
+
lang ? buttonCss(lang, c, { rules }).css : "/* Buttons: the project's own (the plan keeps the brand). */",
|
|
541
|
+
"/* Numbers, times and prices: the body face with tabular figures, never monospace. */",
|
|
542
|
+
".num { font-variant-numeric: tabular-nums; font-feature-settings: \"tnum\" 1; }",
|
|
543
|
+
].join("\n");
|
|
544
|
+
return {
|
|
545
|
+
css: `/* palate blueprint tokens: generated from blueprint.json; edit the blueprint, not this file. */\n:root {\n${body}\n}\n\n${aliasBlock}\n\n${helpers}\n`,
|
|
546
|
+
tailwind: `/* palate blueprint tokens for Tailwind v4: @import this after "tailwindcss". Generated; edit the blueprint. */\n@theme {\n${body}\n}\n\n${aliasBlock}\n\n${helpers}\n`,
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// The page's button language: look.buttons, else what an older plan's cta implies (on top of plain-solid), else
|
|
551
|
+
// plain-solid for a new page; null for an existing page that keeps its brand.
|
|
552
|
+
export function pageButtons(bp) {
|
|
553
|
+
const lk = bp?.look ?? {};
|
|
554
|
+
if (lk.buttons) return resolveButtons(lk.buttons);
|
|
555
|
+
if (bp?.mode === "existing" && bp.system?.keep !== false) return null;
|
|
556
|
+
const fromCta = CTAS.includes(lk.cta) ? buttonsFromCta(lk.cta) : null;
|
|
557
|
+
return { ...resolveButtons("plain-solid"), ...(fromCta ?? {}), language: fromCta ? "custom" : "plain-solid", derived: Boolean(fromCta) };
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// The saved blueprint for a route (or name), or null.
|
|
561
|
+
export function loadBlueprint(dir, { name, route } = {}) {
|
|
562
|
+
const tries = name ? [name] : route ? [route === "/" ? "home" : String(route).replace(/^\//, "").replace(/[^a-z0-9]+/gi, "-")] : ["home"];
|
|
563
|
+
for (const n of tries) {
|
|
564
|
+
const f = path.join(dir, ".palate", "variants", n, "blueprint.json");
|
|
565
|
+
const bp = readJson(f, null);
|
|
566
|
+
if (bp?.passed) return { ...bp, file: f, session: path.dirname(f) };
|
|
567
|
+
}
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Checks a blueprint file and, when it passes, saves it with its tokens.
|
|
572
|
+
export function saveBlueprint(dir, file, { name } = {}) {
|
|
573
|
+
const src = path.resolve(file);
|
|
574
|
+
let bp;
|
|
575
|
+
try {
|
|
576
|
+
bp = JSON.parse(fs.readFileSync(src, "utf8"));
|
|
577
|
+
} catch (e) {
|
|
578
|
+
throw new Error(`Can't read ${slash(src)}: ${e.message}`);
|
|
579
|
+
}
|
|
580
|
+
const session = name ? sessionDir(dir, name) : path.dirname(src);
|
|
581
|
+
const meta = readJson(path.join(session, "blueprint-meta.json"), {});
|
|
582
|
+
const pid = projectId(dir);
|
|
583
|
+
const others = readLayouts().filter((h) => h.pid !== pid && h.style);
|
|
584
|
+
const recent = recentLooks({ excludePid: pid });
|
|
585
|
+
const res = loadResearch(dir, "latest");
|
|
586
|
+
const v = validateBlueprint(bp, { screen: meta.screen ?? "landing", others, recent, research: Boolean(res), tags: [...(res?.understanding?.tags ?? []), ...(res?.understanding?.mood ?? [])] });
|
|
587
|
+
const head = `# Blueprint ${meta.route ?? ""}: ${v.ok ? "passes" : "FAILS"}`;
|
|
588
|
+
if (!v.ok) return [head, "", "Fix these, then run it again:", ...v.problems.map((p) => `- ✗ ${p}`), ...v.warnings.map((w) => `- ! ${w}`)].join("\n");
|
|
589
|
+
// Tokens when the plan names colours by role (an existing page that keeps its brand may name only some).
|
|
590
|
+
const cc = bp.system?.colours ?? bp.system?.colors ?? {};
|
|
591
|
+
const withTokens = Boolean(hexRgb(cc.ink) && hexRgb(cc.accent));
|
|
592
|
+
if (withTokens) {
|
|
593
|
+
// The user's rules win over palate's own finish (e.g. a kept "no gradient buttons" rule).
|
|
594
|
+
const userRules = rulesFor(["all", "landing", "marketing"], { route: meta.route ?? "/", project: projectOf(dir) });
|
|
595
|
+
const flatButtons = userRules.some((r) => r.check?.metric === "gradientButtons" || /gradient/i.test(r.rule ?? "") && /button|cta/i.test(r.rule ?? ""));
|
|
596
|
+
const tok = blueprintTokens(bp, { flatButtons, rules: userRules.map((r) => r.rule ?? "") });
|
|
597
|
+
fs.writeFileSync(path.join(session, "tokens.css"), tok.css);
|
|
598
|
+
fs.writeFileSync(path.join(session, "tokens.tailwind.css"), tok.tailwind);
|
|
599
|
+
}
|
|
600
|
+
const saved = { ...bp, passed: true, route: meta.route ?? bp.route ?? "/", job: meta.job ?? bp.job, before: meta.before ?? null, at: new Date().toISOString() };
|
|
601
|
+
fs.writeFileSync(path.join(session, "blueprint.json"), JSON.stringify(saved, null, 1));
|
|
602
|
+
// palate remembers the look it planned, so the next product's plan has to differ from it.
|
|
603
|
+
if (!(bp.mode === "existing" && bp.system?.keep !== false)) recordPlanned(dir, saved.route, plannedLook(bp, { getSection: Sections?.getSection }), { pid, bench: fs.existsSync(path.join(dir, ".palate", "bench.json")) });
|
|
604
|
+
mark(dir, path.basename(session), "blueprint_saved");
|
|
605
|
+
const lines = [
|
|
606
|
+
head,
|
|
607
|
+
"",
|
|
608
|
+
`- Mood: ${(bp.spec?.mood ?? []).join(", ")} · keep: ${(bp.spec?.truths ?? []).join("; ")} · avoid: ${(bp.spec?.cliches ?? []).join("; ")}`,
|
|
609
|
+
bp.mode === "existing" && bp.system?.keep !== false ? "- System: the project's own brand, kept." : `- System: ${bp.system?.type?.display} / ${bp.system?.type?.body}; accent ${cc.accent}, material ${cc.material}.`,
|
|
610
|
+
bp.look?.buttons ? `- Buttons: ${describeButtons(pageButtons(bp))} (.btn-primary and .btn-secondary in the tokens).` : null,
|
|
611
|
+
`- Sections: ${bp.sections.map((s) => `${s.id} (${bp.mode === "existing" ? `${s.action}${s.archetype ? ` → ${s.archetype}` : ""}` : `${s.archetype ?? s.shape}, ${s.surface}, ${s.imagery}${s.signature ? ", signature" : ""}`})`).join(" → ")}`,
|
|
612
|
+
...v.warnings.map((w) => `- ! ${w}`),
|
|
613
|
+
"",
|
|
614
|
+
withTokens ? `Tokens: ${slash(path.join(session, "tokens.css"))} (plain CSS) and ${slash(path.join(session, "tokens.tailwind.css"))} (Tailwind v4 @theme).` : "Tokens: the project's own (no colours by role in the plan, so palate wrote none).",
|
|
615
|
+
...(Imagery?.imageryPlanLines ? ["", "## Imagery to fetch before building", ...[].concat(Imagery.imageryPlanLines(bp.sections.filter((s) => bp.mode !== "existing" || s.action === "upgrade" || s.action === "add").map((s) => ({ section: s.id, imagery: s.imagery }))))] : []),
|
|
616
|
+
"",
|
|
617
|
+
`Next: palate build "${meta.job ?? bp.job ?? "<the job>"}" --route ${meta.route ?? "/"}: it builds this blueprint section by section.`,
|
|
618
|
+
];
|
|
619
|
+
return lines.filter((l) => l != null).join("\n");
|
|
620
|
+
}
|