xertica-ui 2.8.0 → 2.9.7
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/CHANGELOG.md +108 -0
- package/README.md +1 -1
- package/bin/cli.ts +327 -87
- package/bin/language-config.ts +58 -18
- package/components/blocks/auth/AuthPageShell.tsx +63 -18
- package/components/blocks/auth/SocialLoginButtons.tsx +3 -3
- package/components/blocks/index.ts +1 -0
- package/components/blocks/marketing/CTASection.tsx +73 -0
- package/components/blocks/marketing/ComplianceBadgeRow.tsx +49 -0
- package/components/blocks/marketing/EvidenceTimeline.tsx +91 -0
- package/components/blocks/marketing/FeatureRow.tsx +78 -0
- package/components/blocks/marketing/Hero.tsx +125 -0
- package/components/blocks/marketing/HowItWorksSteps.tsx +90 -0
- package/components/blocks/marketing/LogoCloud.tsx +47 -0
- package/components/blocks/marketing/MarketingFooter.tsx +109 -0
- package/components/blocks/marketing/MarketingNavbar.tsx +192 -0
- package/components/blocks/marketing/PillBadge.tsx +96 -0
- package/components/blocks/marketing/SectionHeading.tsx +82 -0
- package/components/blocks/marketing/ServiceCardGrid.tsx +238 -0
- package/components/blocks/marketing/StatBand.tsx +99 -0
- package/components/blocks/marketing/TopAccentCards.tsx +91 -0
- package/components/blocks/marketing/index.ts +41 -0
- package/components/brand/index.ts +1 -0
- package/components/brand/isotype/Isotype.tsx +64 -0
- package/components/brand/isotype/IsotypeDiagonal.tsx +51 -0
- package/components/brand/isotype/IsotypeFrames.tsx +82 -0
- package/components/brand/isotype/IsotypeMini.tsx +65 -0
- package/components/brand/isotype/IsotypeTwist.tsx +68 -0
- package/components/brand/isotype/frame-variants.ts +20 -0
- package/components/brand/isotype/index.ts +9 -0
- package/components/brand/isotype/isotype-diagonal.mdx +52 -0
- package/components/brand/isotype/isotype-diagonal.stories.tsx +44 -0
- package/components/brand/isotype/isotype-diagonal.test.tsx +180 -0
- package/components/brand/isotype/isotype-frames.mdx +50 -0
- package/components/brand/isotype/isotype-frames.stories.tsx +63 -0
- package/components/brand/isotype/isotype-frames.test.tsx +92 -0
- package/components/brand/isotype/isotype-mini.mdx +61 -0
- package/components/brand/isotype/isotype-mini.stories.tsx +75 -0
- package/components/brand/isotype/isotype-mini.test.tsx +45 -0
- package/components/brand/isotype/isotype-twist.test.tsx +71 -0
- package/components/brand/isotype/isotype.mdx +61 -0
- package/components/brand/isotype/isotype.stories.tsx +74 -0
- package/components/brand/isotype/isotype.test.tsx +41 -0
- package/components/brand/isotype/mini-patterns.ts +50 -0
- package/components/brand/isotype/patterns.ts +83 -0
- package/components/brand/isotype/twist-variants.ts +20 -0
- package/components/pages/forgot-password-page/ForgotPasswordPage.tsx +3 -6
- package/components/pages/index.ts +1 -0
- package/components/pages/landing-page/LandingPage.tsx +360 -0
- package/components/pages/landing-page/index.ts +1 -0
- package/components/pages/login-page/LoginPage.tsx +3 -6
- package/components/pages/reset-password-page/ResetPasswordPage.tsx +1 -4
- package/components/pages/verify-email-page/VerifyEmailPage.tsx +2 -5
- package/components/ui/button/button.test.tsx +1 -1
- package/components/ui/button/button.tsx +7 -6
- package/components/ui/card/card.test.tsx +12 -0
- package/components/ui/card/card.tsx +5 -3
- package/components/ui/input-otp/input-otp.test.tsx +14 -1
- package/contexts/BrandColorsContext.tsx +9 -5
- package/contexts/theme-data.ts +32 -5
- package/dist/AssistantChart-BU48lxL2.js +3383 -0
- package/dist/AssistantChart-CoN6ga6Z.js +3383 -0
- package/dist/AssistantChart-DIRYJ7WY.js +3383 -0
- package/dist/AssistantChart-DOnY1ZlD.cjs +3551 -0
- package/dist/AssistantChart-DjM3qnp5.cjs +3551 -0
- package/dist/AssistantChart-hxPyE1S9.cjs +3551 -0
- package/dist/AudioPlayer-CpihsbaD.js +1278 -0
- package/dist/AudioPlayer-CvGajzMC.cjs +1282 -0
- package/dist/AudioPlayer-CvHpFqDG.cjs +1282 -0
- package/dist/AudioPlayer-D4S7GQou.js +1278 -0
- package/dist/BrandColorsContext-BNKLUVcU.js +829 -0
- package/dist/BrandColorsContext-DNZGHItQ.cjs +836 -0
- package/dist/BrandColorsContext-Ug_TZ8yn.cjs +846 -0
- package/dist/BrandColorsContext-z0O2LlZz.js +839 -0
- package/dist/CodeBlock-BkaZf-R8.cjs +223 -0
- package/dist/CodeBlock-CKu8LgbP.cjs +223 -0
- package/dist/CodeBlock-DiLS5LLI.js +221 -0
- package/dist/CodeBlock-zdylBGdZ.js +221 -0
- package/dist/CustomTooltipContent-BAR5qCQg.js +33 -0
- package/dist/CustomTooltipContent-CBLHBxX8.cjs +54 -0
- package/dist/CustomTooltipContent-CjWm3jjV.js +33 -0
- package/dist/CustomTooltipContent-DTV9MN9E.cjs +54 -0
- package/dist/EvidenceTimeline-DrRk4yqb.cjs +560 -0
- package/dist/EvidenceTimeline-l4MaV4C7.js +546 -0
- package/dist/FeatureCard-CIbBHqeY.cjs +497 -0
- package/dist/FeatureCard-DtoKPSfW.cjs +497 -0
- package/dist/FeatureCard-P4iEpBmB.js +485 -0
- package/dist/FeatureCard-e3f8bnF0.js +485 -0
- package/dist/ImageWithFallback-C1ww9Hg3.cjs +122 -0
- package/dist/ImageWithFallback-CBrkAEo3.cjs +122 -0
- package/dist/ImageWithFallback-CGk5zPH-.js +98 -0
- package/dist/ImageWithFallback-CfnoFFSP.js +98 -0
- package/dist/Isotype--fRZWwS6.js +85 -0
- package/dist/Isotype-0dpLqetB.js +85 -0
- package/dist/Isotype-B8XLEXSB.cjs +92 -0
- package/dist/Isotype-ChKwN7e-.cjs +91 -0
- package/dist/IsotypeDiagonal-CuPYPP4f.js +84 -0
- package/dist/IsotypeDiagonal-eNcOXRAc.cjs +91 -0
- package/dist/IsotypeDiagonal-oiwpXZhE.js +104 -0
- package/dist/IsotypeDiagonal-xMFoFXkb.cjs +109 -0
- package/dist/IsotypeFrames-BcWtyAWp.cjs +83 -0
- package/dist/IsotypeFrames-BnvSJKiH.cjs +73 -0
- package/dist/IsotypeFrames-iDRTRuIu.js +79 -0
- package/dist/IsotypeFrames-qoXOvOJt.js +69 -0
- package/dist/IsotypeMini-CQtWVo3h.js +126 -0
- package/dist/IsotypeMini-Cu_sW5yN.cjs +134 -0
- package/dist/IsotypeTwist-BTdNL4iV.js +106 -0
- package/dist/IsotypeTwist-DvsUgZs_.cjs +115 -0
- package/dist/LanguageContext-B1aOXO-K.js +1987 -0
- package/dist/LanguageContext-BP0k7AzI.js +1993 -0
- package/dist/LanguageContext-CChcIHIG.cjs +1998 -0
- package/dist/LanguageContext-ubC7TSs_.cjs +1992 -0
- package/dist/LanguageSelector-BLFPXGo8.cjs +48 -0
- package/dist/LanguageSelector-CYRzWkRZ.cjs +48 -0
- package/dist/LanguageSelector-CyHjZgC4.cjs +48 -0
- package/dist/LanguageSelector-DE8lIySO.js +46 -0
- package/dist/LanguageSelector-DUr1hjBW.js +46 -0
- package/dist/LanguageSelector-DjbWIKWC.js +46 -0
- package/dist/LogoCloud-C_uMKVNg.cjs +513 -0
- package/dist/LogoCloud-DJsMPNPh.js +500 -0
- package/dist/MarketingFooter-B_ycEdTJ.cjs +727 -0
- package/dist/MarketingFooter-Bhx-2Vlr.cjs +727 -0
- package/dist/MarketingFooter-Br1MGK9e.js +699 -0
- package/dist/MarketingFooter-BwE1vmeX.cjs +700 -0
- package/dist/MarketingFooter-CsZ1_xL9.js +709 -0
- package/dist/MarketingFooter-DCwf78Q4.cjs +716 -0
- package/dist/MarketingFooter-KH2r-OOL.js +709 -0
- package/dist/MarketingFooter-nB7G7gQ5.js +682 -0
- package/dist/SectionHeading-DEeOKcAd.cjs +857 -0
- package/dist/SectionHeading-DMNObcwm.js +838 -0
- package/dist/ThemeToggle--CGJjQJv.cjs +219 -0
- package/dist/ThemeToggle-Bn_7Pmfe.js +215 -0
- package/dist/ThemeToggle-CoL8Ady7.js +215 -0
- package/dist/ThemeToggle-D2Wzm5bn.cjs +219 -0
- package/dist/VerifyEmailPage-90lK6pP4.js +3258 -0
- package/dist/VerifyEmailPage-BJX1Rter.cjs +3320 -0
- package/dist/VerifyEmailPage-BSWvOgry.js +3310 -0
- package/dist/VerifyEmailPage-CDqIkhx-.js +3224 -0
- package/dist/VerifyEmailPage-Crxje3ZB.js +3323 -0
- package/dist/VerifyEmailPage-D2ShbuEN.cjs +3333 -0
- package/dist/VerifyEmailPage-DEBZXQ7c.cjs +3310 -0
- package/dist/VerifyEmailPage-DGaX-jZv.cjs +3268 -0
- package/dist/VerifyEmailPage-ZtOPkgOA.cjs +3234 -0
- package/dist/VerifyEmailPage-s927GSiR.js +3300 -0
- package/dist/XerticaOrbe-BHex09xs.js +1925 -0
- package/dist/XerticaOrbe-BKW3LJEz.js +1925 -0
- package/dist/XerticaOrbe-Br2i99U_.cjs +1927 -0
- package/dist/XerticaOrbe-kjFmuyx-.cjs +1927 -0
- package/dist/XerticaProvider-Bi5lKC9N.js +44 -0
- package/dist/XerticaProvider-BrPMVhiv.js +44 -0
- package/dist/XerticaProvider-CnX5kt5W.cjs +46 -0
- package/dist/XerticaProvider-DEKNJTlN.js +44 -0
- package/dist/XerticaProvider-X_wavDYA.cjs +46 -0
- package/dist/XerticaProvider-b3n_oL8w.cjs +46 -0
- package/dist/alert-dialog--qdlmB5i.cjs +872 -0
- package/dist/alert-dialog-C48TOdYU.cjs +872 -0
- package/dist/alert-dialog-CEPpnpXt.js +819 -0
- package/dist/alert-dialog-NjflitDa.js +819 -0
- package/dist/assistant.cjs.js +2 -2
- package/dist/assistant.es.js +2 -2
- package/dist/avatar-CtEKgg7s.cjs +77 -0
- package/dist/avatar-D03MNQxs.js +54 -0
- package/dist/avatar-Df7KgSvs.js +54 -0
- package/dist/avatar-Gy0_Xu9i.cjs +77 -0
- package/dist/blocks.cjs.js +33 -18
- package/dist/blocks.es.js +2 -2
- package/dist/brand.cjs.js +110 -4
- package/dist/brand.es.js +98 -4
- package/dist/breadcrumb-BGO5PZPG.js +90 -0
- package/dist/breadcrumb-BvGAY4ne.cjs +98 -0
- package/dist/breadcrumb-CU-punra.js +90 -0
- package/dist/breadcrumb-DjV9wfX1.cjs +98 -0
- package/dist/button-B05czY_8.js +62 -0
- package/dist/button-B3SE1liJ.js +62 -0
- package/dist/button-BMDlWZR8.cjs +85 -0
- package/dist/button-DVhloDtr.cjs +85 -0
- package/dist/cli.js +280 -62
- package/dist/components/blocks/auth/AuthPageShell.d.ts +21 -5
- package/dist/components/blocks/index.d.ts +1 -0
- package/dist/components/blocks/marketing/CTASection.d.ts +28 -0
- package/dist/components/blocks/marketing/ComplianceBadgeRow.d.ts +23 -0
- package/dist/components/blocks/marketing/EvidenceTimeline.d.ts +26 -0
- package/dist/components/blocks/marketing/FeatureRow.d.ts +25 -0
- package/dist/components/blocks/marketing/Hero.d.ts +35 -0
- package/dist/components/blocks/marketing/HowItWorksSteps.d.ts +29 -0
- package/dist/components/blocks/marketing/LogoCloud.d.ts +22 -0
- package/dist/components/blocks/marketing/MarketingFooter.d.ts +42 -0
- package/dist/components/blocks/marketing/MarketingNavbar.d.ts +47 -0
- package/dist/components/blocks/marketing/PillBadge.d.ts +21 -0
- package/dist/components/blocks/marketing/SectionHeading.d.ts +23 -0
- package/dist/components/blocks/marketing/ServiceCardGrid.d.ts +65 -0
- package/dist/components/blocks/marketing/StatBand.d.ts +25 -0
- package/dist/components/blocks/marketing/TopAccentCards.d.ts +29 -0
- package/dist/components/blocks/marketing/index.d.ts +28 -0
- package/dist/components/brand/index.d.ts +1 -0
- package/dist/components/brand/isotype/Isotype.d.ts +27 -0
- package/dist/components/brand/isotype/IsotypeDiagonal.d.ts +19 -0
- package/dist/components/brand/isotype/IsotypeFrames.d.ts +21 -0
- package/dist/components/brand/isotype/IsotypeMini.d.ts +26 -0
- package/dist/components/brand/isotype/IsotypeTwist.d.ts +22 -0
- package/dist/components/brand/isotype/frame-variants.d.ts +11 -0
- package/dist/components/brand/isotype/index.d.ts +9 -0
- package/dist/components/brand/isotype/mini-patterns.d.ts +25 -0
- package/dist/components/brand/isotype/patterns.d.ts +42 -0
- package/dist/components/brand/isotype/twist-variants.d.ts +11 -0
- package/dist/components/pages/forgot-password-page/ForgotPasswordPage.d.ts +2 -2
- package/dist/components/pages/index.d.ts +1 -0
- package/dist/components/pages/landing-page/LandingPage.d.ts +18 -0
- package/dist/components/pages/landing-page/index.d.ts +1 -0
- package/dist/components/pages/login-page/LoginPage.d.ts +2 -2
- package/dist/components/pages/verify-email-page/VerifyEmailPage.d.ts +1 -1
- package/dist/components/ui/card/card.d.ts +3 -1
- package/dist/contexts/theme-data.d.ts +6 -0
- package/dist/dropdown-menu-ChQa9zW3.cjs +225 -0
- package/dist/dropdown-menu-DRvnsQAt.cjs +225 -0
- package/dist/dropdown-menu-DUk81i5Y.js +190 -0
- package/dist/dropdown-menu-NlUHhVmK.js +190 -0
- package/dist/hooks.cjs.js +2 -2
- package/dist/hooks.es.js +2 -2
- package/dist/index.cjs.js +57 -41
- package/dist/index.es.js +25 -25
- package/dist/input-Csp_Metb.cjs +91 -0
- package/dist/input-DVRwGVLx.js +65 -0
- package/dist/input-Dqyj7Hdw.cjs +91 -0
- package/dist/input-dOgrFQ-3.js +65 -0
- package/dist/layout.cjs.js +1 -1
- package/dist/layout.es.js +1 -1
- package/dist/media.cjs.js +1 -1
- package/dist/media.es.js +1 -1
- package/dist/pages.cjs.js +2 -1
- package/dist/pages.es.js +1 -1
- package/dist/rich-text-editor-7K7UjLJN.cjs +2968 -0
- package/dist/rich-text-editor-B3jLP2bm.cjs +2968 -0
- package/dist/rich-text-editor-BntMVQsB.js +2894 -0
- package/dist/rich-text-editor-Diz_onyB.js +2894 -0
- package/dist/rich-text-editor-RWvK4hd3.cjs +2968 -0
- package/dist/rich-text-editor-gauEkzPM.js +2894 -0
- package/dist/select-B6PygS38.cjs +161 -0
- package/dist/select-BD763J5Y.cjs +161 -0
- package/dist/select-QJDKZITN.js +130 -0
- package/dist/select-q7aDfuPn.js +130 -0
- package/dist/sidebar-Bkk1YAsD.cjs +1154 -0
- package/dist/sidebar-CuzYeOSz.cjs +1154 -0
- package/dist/sidebar-CwlW3LXE.js +1151 -0
- package/dist/sidebar-DNkf-Ug6.js +1151 -0
- package/dist/sidebar-DaS3fplw.cjs +1154 -0
- package/dist/sidebar-FDCY42oP.js +1151 -0
- package/dist/skeleton-BtBwJ39W.js +79 -0
- package/dist/skeleton-CJyg0eJ1.cjs +88 -0
- package/dist/skeleton-D8WaauK0.cjs +88 -0
- package/dist/skeleton-DBYtjaZW.cjs +88 -0
- package/dist/skeleton-DY_lSFWf.js +79 -0
- package/dist/skeleton-Df65Ox10.js +79 -0
- package/dist/slider-BKV_vvsh.cjs +78 -0
- package/dist/slider-B_lUjFNa.cjs +78 -0
- package/dist/slider-CtBpIaU4.js +56 -0
- package/dist/slider-KgWmSJbx.js +56 -0
- package/dist/tooltip-BPttuPxU.js +48 -0
- package/dist/tooltip-CIKRzQqE.cjs +72 -0
- package/dist/tooltip-CoDByjE_.cjs +72 -0
- package/dist/tooltip-_MAwsiOL.js +48 -0
- package/dist/ui.cjs.js +13 -13
- package/dist/ui.es.js +13 -13
- package/dist/xertica-assistant-B1xAl2Vn.js +2173 -0
- package/dist/xertica-assistant-BBUMaUXw.js +2173 -0
- package/dist/xertica-assistant-CWuBwfzt.cjs +2180 -0
- package/dist/xertica-assistant-D2hlwiEP.js +2173 -0
- package/dist/xertica-assistant-DN2J_sQQ.cjs +2180 -0
- package/dist/xertica-assistant-DdYS6WXT.cjs +2180 -0
- package/dist/xertica-ui.css +1 -1
- package/llms-compact.txt +1 -1
- package/llms.txt +1 -1
- package/package.json +4 -2
- package/styles/xertica/base.css +8 -0
- package/styles/xertica/tokens.css +1 -0
- package/templates/package.json +1 -1
- package/templates/src/app/components/AuthGuard.tsx +6 -0
- package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +1 -4
- package/templates/src/features/auth/ui/LoginContent.tsx +1 -4
- package/templates/src/features/auth/ui/ResetPasswordContent.tsx +1 -4
- package/templates/src/features/auth/ui/VerifyEmailContent.tsx +1 -4
- package/templates/src/locales/en/pages/landing.json +175 -0
- package/templates/src/locales/es/pages/landing.json +175 -0
- package/templates/src/locales/pt-BR/pages/landing.json +175 -0
- package/templates/src/pages/LandingPage.tsx +349 -0
- package/templates/src/styles/xertica/tokens.css +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,114 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## [2.9.7] — 2026-07-14
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **Suíte de testes com exceção não tratada bloqueando `npm publish`** — o pacote de terceiros `input-otp` agenda três `setTimeout` internos (0ms/10ms/50ms) em um `useEffect` sem função de limpeza no unmount. Ao rodar a suíte completa, o cleanup automático do Testing Library desmontava o `InputOTP` entre um teste e outro sem cancelar esses timers, que disparavam mais tarde — já fora do ambiente jsdom daquele arquivo — lançando `ReferenceError: window is not defined` como exceção não tratada. Isso fazia `vitest run` sair com erro mesmo com todos os 358 testes passando, derrubando a etapa `test:unit` do `prepublishOnly` e, com isso, qualquer tentativa de `npm publish`. Corrigido em `components/ui/input-otp/input-otp.test.tsx` usando `vi.useFakeTimers()`/`vi.useRealTimers()` ao redor de cada teste, impedindo que os timers da lib vazem para fora do ciclo de vida do teste.
|
|
19
|
+
- **`npm publish` alertava sobre correção automática do campo `bin`** — `"bin": "./dist/cli.js"` (string, com prefixo `./`) era normalizado pelo próprio npm no momento do publish, gerando o aviso `npm auto-corrected some errors in your package.json`. Corrigido escrevendo `bin` já na forma canônica esperada (`{ "xertica-ui": "dist/cli.js" }`), sem mudança de comportamento — apenas elimina o aviso.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## [2.9.6] — 2026-07-14
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **CLI `init` pergunta o tipo de projeto antes de tudo** — novo primeiro prompt `O que você deseja criar?` com duas opções: `Full system` (o fluxo completo existente, inalterado: auth, sidebar, páginas, i18n) e `Web page` (um scaffold mínimo com apenas a landing page de marketing). Escolhendo `Web page`, o fluxo se reduz a 3 prompts — idiomas (multiselect), tema (select) e instalação de dependências (confirm) — e o projeto já sai pronto, sem nenhuma outra configuração.
|
|
28
|
+
- **Novo scaffold mínimo para "Web page"** (`initPageProject` em `bin/cli.ts`) — gera um `App.tsx` que renderiza `LandingPage` (via novo `generateLandingAppTsx` em `bin/language-config.ts`) diretamente dentro de `XerticaProvider`, sem `Router`, `QueryClientProvider` ou `AuthProvider`. Não cria `AuthContext.tsx`, `AuthGuard.tsx`, `AppLayout.tsx`, `src/shared/` nem `src/features/` — apenas `src/app/App.tsx`, `src/pages/LandingPage.tsx`, i18n/locales e tokens de tema. Também omite `CLAUDE.md`/`guidelines/` (documentam a arquitetura FSD de auth, inexistente neste scaffold) e `.env.example` (chaves usadas só pelo assistente/mapa).
|
|
29
|
+
- **`src/pages/LandingPage.tsx` do scaffold "Web page" ganha conteúdo de demonstração real** — em vez de um re-export de `xertica-ui/pages`, o template (`templates/src/pages/LandingPage.tsx`) agora traz a composição completa da landing page (Hero, cards, feature rows, "how it works", compliance/stats, CTA, footer) montada localmente a partir de `xertica-ui/blocks`/`xertica-ui/brand`, igual ao padrão já usado pelos demais templates (`HomeContent.tsx` etc.) — o usuário recebe algo editável como base, não uma caixa-preta importada da lib.
|
|
30
|
+
- **`.xertica.json` ganha o campo `projectType: 'page' | 'system'`** — projetos escaffoldados antes desta versão não têm o campo e são tratados como `'system'` em todos os pontos que o leem, preservando compatibilidade.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- **CLI `update` adaptado para os dois tipos de projeto** — em projetos `page`, o menu principal omite a opção `Assistant` (sem conceito de assistente nesse scaffold); as opções `Languages` e `Dark Mode` regeneram `App.tsx` via `generateLandingAppTsx` em vez de `generateAppTsx`; e `Project files` mostra uma lista reduzida de 2 itens (`App shell + landing page`, `Root config files`) em vez dos 5 itens do fluxo completo, evitando tentar atualizar `AppLayout.tsx`/`AuthGuard.tsx`/`src/shared/`/`src/features/`, que não existem nesse tipo de projeto.
|
|
35
|
+
- **`generateAppTsx` refatorado** — a lógica de montagem da prop `availableLanguages` foi extraída para um helper interno compartilhado (`buildAvailableLanguagesProp`), reaproveitado agora também pelo novo `generateLandingAppTsx`.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## [2.9.5] — 2026-07-14
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- **Novo isotipo `IsotypeTwist`** (`components/brand/isotype`) — quinta marca da série "isotipos": duas formas angulares sobrepostas, seguindo o mesmo mecanismo do `IsotypeFrames` (ambas 100% opacas, interseção resolvida via `mix-blend-mode: multiply` na forma da frente, com `isolate` na raiz), com duas variantes de cor registradas em `ISOTYPE_TWIST_VARIANTS`: `'magenta-blue'` (rosa + azul) e `'blue-red'` (azul + vermelho). Substitui o `IsotypeDiagonal` no painel esquerdo do `AuthPageShell` (`leftPanel="isotype"`, usado na página de login), agora renderizado com a variante `'magenta-blue'`.
|
|
44
|
+
- **`SectionHeading` ganha uma barra de destaque opcional** — nova prop `accentColor` (mesma convenção `BlockColor`/`colorTokens` local dos demais blocos) renderiza uma pequena barra colorida sob o título, aproximando o padrão da referência de mockup. É opcional: sem `accentColor`, o título continua exatamente como antes.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- **`SectionHeading` simplificado** — removidos o numeral fantasma (`index`) e o rótulo eyebrow, mantendo apenas o título (com barra de destaque e descrição opcionais), conforme a referência de mockup. As props `index`/`eyebrow` saem da API do componente; as chaves de tradução `ecosystem.eyebrow`, `useCases.eyebrow` e `methodology.eyebrow` (agora não usadas) foram removidas dos 6 arquivos `landing.json`. Aplicado às 3 seções que já usam `SectionHeading` na `LandingPage` (Ecossistema, Casos de uso, Metodologia).
|
|
49
|
+
- **`ServiceCardGrid`/`ServiceCard` com `icon` opcional** — o chip de ícone só renderiza quando `icon` é informado, permitindo cards sem ícone (título, descrição e checklist/bullets/CTA), conforme a referência de mockup. Aplicado à seção "Casos de uso" da `LandingPage`, que passa a renderizar os 3 cards sem ícone.
|
|
50
|
+
|
|
51
|
+
- **`AuthPageShell` (`leftPanel="isotype"`) com fundos invertidos** — o painel do isotipo na página de login passa a usar `bg-muted` (mais escuro) e o painel do formulário à direita passa a usar `bg-card` (Marfim, `#FFFEF8` no tema xertica, mais claro), invertendo a relação de claridade anterior entre os dois lados.
|
|
52
|
+
- **`ForgotPasswordPage`, `ResetPasswordPage` e `VerifyEmailPage` trocam a foto de fundo pelo painel de isotipo** — as 3 telas usavam `AuthPageShell` com `imageSrc`/foto de herói e um overlay de gradiente que, no tema xertica (token de gradiente plano em `#1E1E1E`), ficava com o painel esquerdo escuro. Passam a usar `leftPanel="isotype"`, a mesma solução (agora clara) já aplicada à `LoginPage`, unificando a aparência das 4 telas de autenticação. Espelhado em `templates/src/features/auth/ui/{ForgotPassword,ResetPassword,VerifyEmail}Content.tsx`.
|
|
53
|
+
- **`SectionHeading` perde a linha divisória superior** — removida a borda `border-t`/`pt-6` que antecedia o título (Ecossistema, Casos de uso, Metodologia na `LandingPage`); o cabeçalho agora fica sem regra visual antes do título.
|
|
54
|
+
- **`CTASection` volta a ter fundo escuro** — troca o `bg-card` plano por `bg-sidebar`/`border-sidebar-border` (mesma superfície escura já usada na seção de metodologia desta página) e o texto passa a `text-sidebar-foreground`/`text-sidebar-foreground/70`, sem gradiente e sem cor hardcoded. Os botões continuam com as variantes naturais (`default`/`outline`), que já contrastam corretamente sobre `bg-sidebar`.
|
|
55
|
+
- **Seção de compliance/stats da `LandingPage` reorganizada** — `ComplianceBadgeRow` (badges) e `StatBand` (números) agora ficam contidos em um único card (`border`, `bg-card`, `rounded-2xl`) com um divisor entre os dois grupos, em vez de flutuarem soltos direto sobre o fundo da página. `ComplianceBadgeRow` passa de um `flex flex-wrap` com gaps largos para um `grid grid-cols-2 sm:grid-cols-4`, alinhando os badges em colunas; o chip de ícone usa `bg-background` para manter contraste dentro do novo card.
|
|
56
|
+
- **`IsotypeFrames` restrito a duas variantes de cor** — `IsotypeFrameVariantId` passa de 6 para apenas `'blue-magenta'` e `'blue-yellow'` (azul+rosa e azul+amarelo); `'green-red'`, `'green-blue'`, `'magenta-yellow'` e `'magenta-purple'` saem do registro. Único uso afetado fora do próprio componente: a seção "Onboarding" da `LandingPage`, que usava `magenta-yellow` e passa a usar `blue-yellow`. No par `blue-yellow`, o azul é o quadro de trás (posicionado mais para cima) e o amarelo o quadro da frente (posicionado mais para baixo).
|
|
57
|
+
|
|
58
|
+
### Removed
|
|
59
|
+
|
|
60
|
+
- **Removidas as seções de depoimento e de "pilares" da `LandingPage`** — o card de citação com `StatBand` (componentes/temas/downloads) e o grid de 4 colunas com ícones (tokens de tema, componentes acessíveis, blocos prontos, CLI de scaffolding) saem da página. As chaves de tradução `testimonial`, `pillars` e `stats.components`/`stats.themes`/`stats.downloads` (agora não usadas) foram removidas dos 6 arquivos `landing.json`.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## [2.9.4] — 2026-07-13
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- **Novo bloco `HowItWorksSteps`** (`xertica-ui/blocks` → `marketing`) — variante horizontal do `EvidenceTimeline`: mesma badge numerada/colorida (`colorTokens` local, sem linha conectora), em grid responsivo (`grid-cols-1 sm:grid-cols-2 lg:grid-cols-4`) em vez de lista vertical com fio. Substitui o `EvidenceTimeline` na seção "Metodologia" da `LandingPage` (mesmos 4 itens/cópias); o `EvidenceTimeline` continua exportado e funcional para quem preferir o layout vertical.
|
|
69
|
+
- **Novo bloco `TopAccentCards`** (`xertica-ui/blocks` → `marketing`) — par de cards com destaque de cor na borda superior (`border-t-4`), título, corpo e lista opcional de bullets simples, sem ícone/CTA. Segue a mesma convenção local de `BlockColor`/`colorTokens` dos demais blocos de marketing. Nova seção "framing" (propósito + problema) inserida na `LandingPage` logo após o `Hero`.
|
|
70
|
+
|
|
71
|
+
### Changed
|
|
72
|
+
|
|
73
|
+
- **`CTASection` perde o fundo em gradiente** — passa a usar um fundo plano `bg-card` com borda (`border border-border`), título em `text-foreground` e descrição em `text-muted-foreground`. Os botões `cta`/`secondaryCta` deixam de ter cor forçada (branco sobre gradiente) e usam as variantes naturais `default`/`outline`, que já contrastam corretamente sobre `bg-card`. Nenhum outro consumidor deste bloco existe fora da `LandingPage`.
|
|
74
|
+
- **`PillBadge` vira um pill de verdade** — `rounded-[var(--radius)]` (6px) dá lugar a `rounded-full`, alinhando o componente ao próprio nome. Único consumidor é o eyebrow do `Hero`.
|
|
75
|
+
- **Recolorida a seção "Metodologia" da `LandingPage`** — os 4 passos passam de azul/celeste/amarelo/coral para coral/celeste/azul/laranja (`chart-4`/`chart-2`/`chart-1`/`warning`), aproximando a sequência de cores da referência de mockup (vermelho/verde-azulado/roxo/laranja); como o tema `xertica` não tem roxo, `chart-1` (azul) substitui esse passo — é um desvio conhecido da referência.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## [2.9.3] — 2026-07-13
|
|
80
|
+
|
|
81
|
+
### Changed
|
|
82
|
+
|
|
83
|
+
- **Padronização das variantes de botão na landing page** — o botão de "iniciar sessão" (`MarketingNavbar`, desktop e mobile) e o `secondaryCta` do `Hero` passam a usar a variante `secondary` (preto). O `primaryCta` do `Hero` ("Começar agora") e o `cta` do `CTASection` continuam `default`/primary, reservado para as ações principais de conversão. A variante `outline` continua reservada para os cards (`ServiceCardGrid`). Mudança feita direto nos blocos, sem novas props — nenhum outro consumidor desses componentes existe fora da `LandingPage`.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## [2.9.2] — 2026-07-13
|
|
88
|
+
|
|
89
|
+
### Changed
|
|
90
|
+
|
|
91
|
+
- **Tema `xertica` deixa de ter gradientes** — `gradientStart`/`gradientEnd` (claro e escuro) passam a apontar todos para o mesmo Negro Xertica (`#1E1E1E`), então `--gradient-diagonal` resolve para um preenchimento sólido nesse tema. Afeta todo consumidor do token (banner do `CTASection` na `LandingPage`, `AuthPageShell`, etc.) sem exigir mudança em nenhum componente — os demais temas mantêm seus gradientes normalmente.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## [2.9.1] — 2026-07-13
|
|
96
|
+
|
|
97
|
+
### Changed
|
|
98
|
+
|
|
99
|
+
- **`StatBand` ganha alinhamento numérico e rótulo em estilo legenda** — o valor numérico agora usa `tabular-nums` (mesma técnica já usada no numeral fantasma do `SectionHeading`), e o rótulo passa de texto simples (`text-sm`) para o padrão de legenda já usado em `ServiceCardGrid` (`text-xs font-semibold uppercase tracking-wide`). Ritmo de grade mais generoso (`gap-y-8`→`gap-y-10`, `pl-8`→`pl-10` nos divisores).
|
|
100
|
+
- **Espaçamento título→texto corrido padronizado em `SectionHeading`, `FeatureRow`, `CTASection` e `EvidenceTimeline`** — na referência de Swiss Design, títulos e o texto corrido que os segue precisam de um espaço deliberado entre si. O gap `mt-3`/`mt-4` (12–16px) usado nos blocos de marketing da `LandingPage` passa para `mt-6` (24px) nos três primeiros; `EvidenceTimeline`, por ser um relacionamento secundário mais denso dentro de uma lista, recebe um ajuste proporcional menor (`mt-1`→`mt-2`).
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## [2.9.0] — 2026-07-13
|
|
105
|
+
|
|
106
|
+
### Added
|
|
107
|
+
|
|
108
|
+
- **`xertica-ui/blocks` ganha um novo namespace `marketing`** — `Hero`, `FeatureRow`, `CTASection`, `ComplianceBadgeRow`, `EvidenceTimeline`, `LogoCloud`, `MarketingFooter`, `MarketingNavbar`, `PillBadge`, `ServiceCardGrid` e `StatBand`, compondo a nova `LandingPage` (`xertica-ui/pages`, também registrada em `/landing` no template do CLI). Todos os visuais são placeholders orientados a tokens (isotipos da marca, SVGs inline), sem dependência de imagens externas.
|
|
109
|
+
- **Nova marca `IsotypeDiagonal`** — quarta variação da família `isotype` (`xertica-ui/brand`), com corte diagonal fixo formando pontas triangulares nos encontros azul/verde e magenta/vermelho e na hipotenusa do amarelo, as três sobre a mesma diagonal do quadrado. `AuthPageShell` ganha a prop `leftPanel="isotype"` (painel de marca sobre `bg-sidebar`, alternativa ao par imagem+gradiente) e `gradientClassName` (override do gradiente por página); `LoginPage` e o `LoginContent` do template do CLI passam a usar `leftPanel="isotype"`, substituindo a foto de estoque (Unsplash) que usavam antes.
|
|
110
|
+
- **`Button` variant `outline` ganha um token de contorno dedicado** — `--outline-border` / utilitário `border-outline-stroke` — com o mesmo mecanismo opcional (`BrandColors.outlineBorderColor`) já usado por `inputBorderColor`. Os 9 temas pré-existentes ficam pixel-idênticos (fallback para `--border`); o tema `xertica` passa a usar o mesmo Negro Xertica do contorno dos inputs.
|
|
111
|
+
|
|
112
|
+
### Changed
|
|
113
|
+
|
|
114
|
+
- **`Isotype` (grid plano) e `IsotypeMini` saem de rotação** nas páginas do próprio design system (Hero da `LandingPage`, cabeçalho da `HomeContent`) — continuam exportados e totalmente funcionais, apenas comentados no ponto de uso para fácil reativação futura. Só `IsotypeDiagonal` e `IsotypeFrames` seguem em uso ativo.
|
|
115
|
+
|
|
116
|
+
### Fixed
|
|
117
|
+
|
|
118
|
+
- **`Card` `variant="white"` podia renderizar texto ilegível no dark mode** — `text-card-foreground` (tema-reativo) era aplicado incondicionalmente, então um card forçado a branco herdava a cor de texto quase-branca do tema no dark mode. Agora `variant="white"` sempre pareia com `text-neutral-900` fixo, independente do tema/modo.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
14
122
|
## [2.8.0] — 2026-07-09
|
|
15
123
|
|
|
16
124
|
### Added
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **Enterprise-grade React design system** built on Tailwind CSS v4, Radix UI, and Lucide Icons — with a robust AI-first documentation layer for precise LLM-driven composition and autonomous agent interaction.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/xertica-ui)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
|
|
8
8
|
---
|
package/bin/cli.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
syncLocaleFiles,
|
|
18
18
|
generateI18nFile,
|
|
19
19
|
generateAppTsx,
|
|
20
|
+
generateLandingAppTsx,
|
|
20
21
|
} from './language-config';
|
|
21
22
|
|
|
22
23
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -31,6 +32,8 @@ interface XerticaConfig {
|
|
|
31
32
|
hasAssistant: boolean;
|
|
32
33
|
disableDarkMode?: boolean;
|
|
33
34
|
themeId?: string;
|
|
35
|
+
/** Missing on projects scaffolded before this field existed — treat as 'system'. */
|
|
36
|
+
projectType?: 'page' | 'system';
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
async function readXerticaConfig(targetDir: string): Promise<XerticaConfig | null> {
|
|
@@ -286,6 +289,165 @@ export function TemplatePage() {
|
|
|
286
289
|
`;
|
|
287
290
|
}
|
|
288
291
|
|
|
292
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
293
|
+
// package.json copy helper — shared by the 'system' and 'page' init flows.
|
|
294
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
295
|
+
|
|
296
|
+
async function copyPackageJson(templatesDir: string, targetDir: string): Promise<void> {
|
|
297
|
+
const pkgTemplatePath = path.join(templatesDir, 'package.json');
|
|
298
|
+
if (await fs.pathExists(pkgTemplatePath)) {
|
|
299
|
+
const pkgContent = await fs.readJson(pkgTemplatePath);
|
|
300
|
+
const projectName = path.basename(targetDir) || 'my-xertica-app';
|
|
301
|
+
pkgContent.name = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
302
|
+
await fs.writeJson(path.join(targetDir, 'package.json'), pkgContent, { spaces: 2 });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
307
|
+
// Minimal "web page" scaffold — just the marketing LandingPage, no auth shell.
|
|
308
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
309
|
+
|
|
310
|
+
async function initPageProject(targetDir: string, templatesDir: string): Promise<void> {
|
|
311
|
+
const response = await prompts([
|
|
312
|
+
{
|
|
313
|
+
type: 'multiselect',
|
|
314
|
+
name: 'languages',
|
|
315
|
+
message: 'Which languages should the page support?',
|
|
316
|
+
instructions: false,
|
|
317
|
+
hint: 'Select at least 1. Single-language apps auto-hide the LanguageSelector.',
|
|
318
|
+
min: 1,
|
|
319
|
+
choices: SUPPORTED_LANGUAGES.map(l => ({
|
|
320
|
+
title: l.label,
|
|
321
|
+
value: l.code,
|
|
322
|
+
selected: true,
|
|
323
|
+
})),
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
type: 'select',
|
|
327
|
+
name: 'theme',
|
|
328
|
+
message: 'Select the default color theme for your project:',
|
|
329
|
+
choices: colorThemes.map(t => ({
|
|
330
|
+
title: t.name,
|
|
331
|
+
description: t.description,
|
|
332
|
+
value: t.id,
|
|
333
|
+
})),
|
|
334
|
+
initial: 0,
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
type: 'confirm',
|
|
338
|
+
name: 'install',
|
|
339
|
+
message: 'Install dependencies automatically?',
|
|
340
|
+
initial: true,
|
|
341
|
+
},
|
|
342
|
+
]);
|
|
343
|
+
|
|
344
|
+
if (!response.languages || !response.theme) return;
|
|
345
|
+
|
|
346
|
+
const spinner = ora('Initializing project...').start();
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
await fs.ensureDir(targetDir);
|
|
350
|
+
|
|
351
|
+
const selectedLanguages: string[] =
|
|
352
|
+
Array.isArray(response.languages) && response.languages.length > 0
|
|
353
|
+
? response.languages
|
|
354
|
+
: DEFAULT_SELECTION;
|
|
355
|
+
|
|
356
|
+
// 1. Copy root config files (no CLAUDE.md/guidelines — those document the
|
|
357
|
+
// FSD auth-app architecture, which this scaffold doesn't have; no
|
|
358
|
+
// .env.example — its keys are only used by the assistant/maps pages).
|
|
359
|
+
const rootFilesToCopy = [
|
|
360
|
+
'index.html',
|
|
361
|
+
'vite.config.ts',
|
|
362
|
+
'tsconfig.json',
|
|
363
|
+
'tsconfig.node.json',
|
|
364
|
+
'postcss.config.js',
|
|
365
|
+
'vite-env.d.ts',
|
|
366
|
+
'eslint.config.js',
|
|
367
|
+
'.gitignore',
|
|
368
|
+
'.prettierrc',
|
|
369
|
+
];
|
|
370
|
+
|
|
371
|
+
for (const file of rootFilesToCopy) {
|
|
372
|
+
const srcPath = path.join(templatesDir, file);
|
|
373
|
+
if (await fs.pathExists(srcPath)) {
|
|
374
|
+
await fs.copy(srcPath, path.join(targetDir, file));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// 2. Copy package.json
|
|
379
|
+
await copyPackageJson(templatesDir, targetDir);
|
|
380
|
+
|
|
381
|
+
// 3. Copy src/main.tsx (unchanged regardless of App.tsx variant)
|
|
382
|
+
await fs.copy(
|
|
383
|
+
path.join(templatesDir, 'src', 'main.tsx'),
|
|
384
|
+
path.join(targetDir, 'src', 'main.tsx')
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
// 4. Generate src/app/App.tsx rendering LandingPage directly
|
|
388
|
+
await fs.ensureDir(path.join(targetDir, 'src', 'app'));
|
|
389
|
+
await fs.writeFile(
|
|
390
|
+
path.join(targetDir, 'src', 'app', 'App.tsx'),
|
|
391
|
+
generateLandingAppTsx(selectedLanguages)
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
// 5. Generate i18n.ts + copy selected locales
|
|
395
|
+
await fs.writeFile(
|
|
396
|
+
path.join(targetDir, 'src', 'i18n.ts'),
|
|
397
|
+
generateI18nFile(selectedLanguages)
|
|
398
|
+
);
|
|
399
|
+
await syncLocaleFiles(templatesDir, targetDir, selectedLanguages, { pruneOthers: true });
|
|
400
|
+
await writeLanguagesConfig(targetDir, selectedLanguages);
|
|
401
|
+
|
|
402
|
+
// 6. Persist project config
|
|
403
|
+
await writeXerticaConfig(targetDir, {
|
|
404
|
+
hasAssistant: false,
|
|
405
|
+
disableDarkMode: false,
|
|
406
|
+
themeId: response.theme,
|
|
407
|
+
projectType: 'page',
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// 7. Copy the LandingPage re-export shim
|
|
411
|
+
await fs.ensureDir(path.join(targetDir, 'src', 'pages'));
|
|
412
|
+
await fs.copy(
|
|
413
|
+
path.join(templatesDir, 'src', 'pages', 'LandingPage.tsx'),
|
|
414
|
+
path.join(targetDir, 'src', 'pages', 'LandingPage.tsx')
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
// 8. Generate theme tokens
|
|
418
|
+
const selectedTheme = colorThemes.find(t => t.id === response.theme) || colorThemes[0];
|
|
419
|
+
const tokensDir = path.join(targetDir, 'src', 'styles', 'xertica');
|
|
420
|
+
await fs.ensureDir(tokensDir);
|
|
421
|
+
await fs.copy(
|
|
422
|
+
path.join(templatesDir, 'src', 'styles', 'index.css'),
|
|
423
|
+
path.join(targetDir, 'src', 'styles', 'index.css')
|
|
424
|
+
);
|
|
425
|
+
await fs.writeFile(path.join(tokensDir, 'tokens.css'), generateTokensCss(selectedTheme));
|
|
426
|
+
|
|
427
|
+
spinner.succeed('Project initialized successfully!');
|
|
428
|
+
|
|
429
|
+
if (response.install) {
|
|
430
|
+
const installSpinner = ora('Installing dependencies...').start();
|
|
431
|
+
await execa('npm', ['install'], { cwd: targetDir });
|
|
432
|
+
installSpinner.succeed('Dependencies installed!');
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
console.log(chalk.green('\n✅ Done! Your Xertica UI landing page is ready.'));
|
|
436
|
+
console.log(chalk.cyan(`\n cd ${path.relative(process.cwd(), targetDir) || '.'}`));
|
|
437
|
+
if (!response.install) {
|
|
438
|
+
console.log(chalk.cyan(' npm install'));
|
|
439
|
+
}
|
|
440
|
+
console.log(chalk.cyan(' npm run dev'));
|
|
441
|
+
console.log();
|
|
442
|
+
console.log(chalk.gray(' Edit the demo content in src/pages/LandingPage.tsx'));
|
|
443
|
+
console.log(chalk.gray(' Customize the theme in src/styles/xertica/tokens.css'));
|
|
444
|
+
console.log(chalk.gray(' To change the theme or languages later: npx xertica-ui update'));
|
|
445
|
+
} catch (error) {
|
|
446
|
+
spinner.fail('Failed to initialize project');
|
|
447
|
+
console.error(error);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
289
451
|
const program = new Command();
|
|
290
452
|
|
|
291
453
|
program
|
|
@@ -303,6 +465,32 @@ program
|
|
|
303
465
|
|
|
304
466
|
console.log(chalk.blue(`🚀 Welcome to Xertica UI CLI! ${chalk.dim(`v${pkgJson.version}`)}`));
|
|
305
467
|
|
|
468
|
+
const { projectType } = await prompts({
|
|
469
|
+
type: 'select',
|
|
470
|
+
name: 'projectType',
|
|
471
|
+
message: 'What do you want to create?',
|
|
472
|
+
choices: [
|
|
473
|
+
{
|
|
474
|
+
title: 'Full system',
|
|
475
|
+
description: 'Auth, sidebar, pages, i18n — the current full scaffold',
|
|
476
|
+
value: 'system',
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
title: 'Web page',
|
|
480
|
+
description: 'Just the marketing landing page — pick a theme and languages, done',
|
|
481
|
+
value: 'page',
|
|
482
|
+
},
|
|
483
|
+
],
|
|
484
|
+
initial: 0,
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
if (!projectType) return;
|
|
488
|
+
|
|
489
|
+
if (projectType === 'page') {
|
|
490
|
+
await initPageProject(targetDir, templatesDir);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
|
|
306
494
|
const response = await prompts([
|
|
307
495
|
{
|
|
308
496
|
type: 'multiselect',
|
|
@@ -408,13 +596,7 @@ program
|
|
|
408
596
|
}
|
|
409
597
|
|
|
410
598
|
// 2. Copy package.json
|
|
411
|
-
|
|
412
|
-
if (await fs.pathExists(pkgTemplatePath)) {
|
|
413
|
-
const pkgContent = await fs.readJson(pkgTemplatePath);
|
|
414
|
-
const projectName = path.basename(targetDir) || 'my-xertica-app';
|
|
415
|
-
pkgContent.name = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
416
|
-
await fs.writeJson(path.join(targetDir, 'package.json'), pkgContent, { spaces: 2 });
|
|
417
|
-
}
|
|
599
|
+
await copyPackageJson(templatesDir, targetDir);
|
|
418
600
|
|
|
419
601
|
// 3. Copy src/main.tsx
|
|
420
602
|
await fs.copy(
|
|
@@ -458,7 +640,12 @@ program
|
|
|
458
640
|
await writeLanguagesConfig(targetDir, selectedLanguages);
|
|
459
641
|
|
|
460
642
|
// 6.5 Persist project feature flags (.xertica.json)
|
|
461
|
-
await writeXerticaConfig(targetDir, {
|
|
643
|
+
await writeXerticaConfig(targetDir, {
|
|
644
|
+
hasAssistant,
|
|
645
|
+
disableDarkMode,
|
|
646
|
+
themeId: response.theme,
|
|
647
|
+
projectType: 'system',
|
|
648
|
+
});
|
|
462
649
|
|
|
463
650
|
// 6.4 Copy context
|
|
464
651
|
await fs.ensureDir(path.join(targetDir, 'src', 'app', 'context'));
|
|
@@ -610,6 +797,8 @@ program
|
|
|
610
797
|
console.log(chalk.blue(`🔧 Xertica UI CLI ${chalk.dim(`v${pkgJson.version}`)}`));
|
|
611
798
|
|
|
612
799
|
const currentConfig = await readXerticaConfig(targetDir);
|
|
800
|
+
const projectType = currentConfig?.projectType ?? 'system';
|
|
801
|
+
const isPageProject = projectType === 'page';
|
|
613
802
|
|
|
614
803
|
const { updateType } = await prompts({
|
|
615
804
|
type: 'select',
|
|
@@ -626,13 +815,17 @@ program
|
|
|
626
815
|
description: 'Add or remove supported languages (pt-BR, en, es, …)',
|
|
627
816
|
value: 'languages',
|
|
628
817
|
},
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
818
|
+
...(isPageProject
|
|
819
|
+
? []
|
|
820
|
+
: [
|
|
821
|
+
{
|
|
822
|
+
title: 'Assistant',
|
|
823
|
+
description: currentConfig?.hasAssistant
|
|
824
|
+
? 'Remove the AI Assistant from your project'
|
|
825
|
+
: 'Add the AI Assistant to your project',
|
|
826
|
+
value: 'assistant',
|
|
827
|
+
},
|
|
828
|
+
]),
|
|
636
829
|
{
|
|
637
830
|
title: 'Dark Mode',
|
|
638
831
|
description: currentConfig?.disableDarkMode
|
|
@@ -642,7 +835,9 @@ program
|
|
|
642
835
|
},
|
|
643
836
|
{
|
|
644
837
|
title: 'Project files',
|
|
645
|
-
description:
|
|
838
|
+
description: isPageProject
|
|
839
|
+
? 'Update the app shell and landing page to a specific version'
|
|
840
|
+
: 'Update app shell, shared, features and pages to a specific version',
|
|
646
841
|
value: 'project',
|
|
647
842
|
},
|
|
648
843
|
],
|
|
@@ -798,7 +993,9 @@ program
|
|
|
798
993
|
// 3) Regenerate App.tsx so the `availableLanguages` prop matches
|
|
799
994
|
await fs.writeFile(
|
|
800
995
|
path.join(targetDir, 'src', 'app', 'App.tsx'),
|
|
801
|
-
|
|
996
|
+
isPageProject
|
|
997
|
+
? generateLandingAppTsx(newCodes, currentConfig?.disableDarkMode ?? false)
|
|
998
|
+
: generateAppTsx(newCodes, currentConfig?.disableDarkMode ?? false)
|
|
802
999
|
);
|
|
803
1000
|
|
|
804
1001
|
// 4) Persist the new selection
|
|
@@ -1014,7 +1211,9 @@ program
|
|
|
1014
1211
|
|
|
1015
1212
|
await fs.writeFile(
|
|
1016
1213
|
path.join(targetDir, 'src', 'app', 'App.tsx'),
|
|
1017
|
-
|
|
1214
|
+
isPageProject
|
|
1215
|
+
? generateLandingAppTsx(selectedCodes, newDisableDarkMode)
|
|
1216
|
+
: generateAppTsx(selectedCodes, newDisableDarkMode)
|
|
1018
1217
|
);
|
|
1019
1218
|
|
|
1020
1219
|
spinner.succeed(
|
|
@@ -1063,38 +1262,53 @@ program
|
|
|
1063
1262
|
type: 'multiselect',
|
|
1064
1263
|
name: 'filesToUpdate',
|
|
1065
1264
|
message: 'Select which parts of the project to update:',
|
|
1066
|
-
choices:
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1265
|
+
choices: isPageProject
|
|
1266
|
+
? [
|
|
1267
|
+
{
|
|
1268
|
+
title: 'App shell + landing page',
|
|
1269
|
+
description: 'App.tsx and the LandingPage re-export',
|
|
1270
|
+
value: 'app',
|
|
1271
|
+
selected: true,
|
|
1272
|
+
},
|
|
1273
|
+
{
|
|
1274
|
+
title: 'Root config files',
|
|
1275
|
+
description: 'vite.config.ts, tsconfig.json, etc.',
|
|
1276
|
+
value: 'config',
|
|
1277
|
+
selected: false,
|
|
1278
|
+
},
|
|
1279
|
+
]
|
|
1280
|
+
: [
|
|
1281
|
+
{
|
|
1282
|
+
title: 'App shell (src/app/)',
|
|
1283
|
+
description: 'App.tsx, AppLayout.tsx',
|
|
1284
|
+
value: 'app',
|
|
1285
|
+
selected: true,
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
title: 'Shared utilities (src/shared/)',
|
|
1289
|
+
description: 'auth.ts, navigation.ts, types',
|
|
1290
|
+
value: 'shared',
|
|
1291
|
+
selected: true,
|
|
1292
|
+
},
|
|
1293
|
+
{
|
|
1294
|
+
title: 'Features (src/features/)',
|
|
1295
|
+
description: 'auth, home, template UI components',
|
|
1296
|
+
value: 'features',
|
|
1297
|
+
selected: true,
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
title: 'Pages (src/pages/)',
|
|
1301
|
+
description: 'Thin page wrapper components',
|
|
1302
|
+
value: 'pages',
|
|
1303
|
+
selected: true,
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
title: 'Root config files',
|
|
1307
|
+
description: 'vite.config.ts, tsconfig.json, etc.',
|
|
1308
|
+
value: 'config',
|
|
1309
|
+
selected: false,
|
|
1310
|
+
},
|
|
1311
|
+
],
|
|
1098
1312
|
});
|
|
1099
1313
|
|
|
1100
1314
|
if (!filesToUpdate || filesToUpdate.length === 0) return;
|
|
@@ -1124,13 +1338,6 @@ program
|
|
|
1124
1338
|
const updatedTemplatesDir = path.join(targetDir, 'node_modules', 'xertica-ui', 'templates');
|
|
1125
1339
|
|
|
1126
1340
|
if (filesToUpdate.includes('app')) {
|
|
1127
|
-
// AppLayout is always safe to overwrite (no per-project config baked in)
|
|
1128
|
-
await fs.copy(
|
|
1129
|
-
path.join(updatedTemplatesDir, 'src', 'app', 'components', 'AppLayout.tsx'),
|
|
1130
|
-
path.join(targetDir, 'src', 'app', 'components', 'AppLayout.tsx'),
|
|
1131
|
-
{ overwrite: true }
|
|
1132
|
-
);
|
|
1133
|
-
|
|
1134
1341
|
// For App.tsx and i18n.ts we must preserve the user's language selection.
|
|
1135
1342
|
// Read it from .languages.json (or fall back to inspecting locales/ for
|
|
1136
1343
|
// projects scaffolded before the file existed).
|
|
@@ -1152,38 +1359,71 @@ program
|
|
|
1152
1359
|
|
|
1153
1360
|
const projectConfig = await readXerticaConfig(targetDir);
|
|
1154
1361
|
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1362
|
+
if (isPageProject) {
|
|
1363
|
+
// Regenerate App.tsx and i18n.ts, re-copy the LandingPage shim —
|
|
1364
|
+
// no AppLayout/AuthGuard exist in a page-only project.
|
|
1365
|
+
await fs.writeFile(
|
|
1366
|
+
path.join(targetDir, 'src', 'app', 'App.tsx'),
|
|
1367
|
+
generateLandingAppTsx(selectedCodes, projectConfig?.disableDarkMode ?? false)
|
|
1368
|
+
);
|
|
1369
|
+
await fs.writeFile(
|
|
1370
|
+
path.join(targetDir, 'src', 'i18n.ts'),
|
|
1371
|
+
generateI18nFile(selectedCodes)
|
|
1372
|
+
);
|
|
1373
|
+
await syncLocaleFiles(updatedTemplatesDir, targetDir, selectedCodes, {
|
|
1374
|
+
pruneOthers: true,
|
|
1375
|
+
});
|
|
1376
|
+
await fs.copy(
|
|
1377
|
+
path.join(updatedTemplatesDir, 'src', 'pages', 'LandingPage.tsx'),
|
|
1378
|
+
path.join(targetDir, 'src', 'pages', 'LandingPage.tsx'),
|
|
1379
|
+
{ overwrite: true }
|
|
1380
|
+
);
|
|
1381
|
+
} else {
|
|
1382
|
+
// AppLayout is always safe to overwrite (no per-project config baked in)
|
|
1383
|
+
await fs.copy(
|
|
1384
|
+
path.join(updatedTemplatesDir, 'src', 'app', 'components', 'AppLayout.tsx'),
|
|
1385
|
+
path.join(targetDir, 'src', 'app', 'components', 'AppLayout.tsx'),
|
|
1386
|
+
{ overwrite: true }
|
|
1387
|
+
);
|
|
1161
1388
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1389
|
+
// Regenerate App.tsx and i18n.ts honoring the persisted language set
|
|
1390
|
+
await fs.writeFile(
|
|
1391
|
+
path.join(targetDir, 'src', 'app', 'App.tsx'),
|
|
1392
|
+
generateAppTsx(selectedCodes, projectConfig?.disableDarkMode ?? false)
|
|
1393
|
+
);
|
|
1394
|
+
await fs.writeFile(
|
|
1395
|
+
path.join(targetDir, 'src', 'i18n.ts'),
|
|
1396
|
+
generateI18nFile(selectedCodes)
|
|
1397
|
+
);
|
|
1167
1398
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
hasAssistant
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1399
|
+
// Refresh locale JSON files for the selected languages (keys grow over
|
|
1400
|
+
// library updates) — but prune any orphans from prior selections.
|
|
1401
|
+
await syncLocaleFiles(updatedTemplatesDir, targetDir, selectedCodes, {
|
|
1402
|
+
pruneOthers: true,
|
|
1403
|
+
});
|
|
1404
|
+
|
|
1405
|
+
// Regenerate AuthGuard preserving the current page set and assistant flag
|
|
1406
|
+
const pagesDir = path.join(targetDir, 'src', 'pages');
|
|
1407
|
+
const existingPages = (await fs.pathExists(pagesDir))
|
|
1408
|
+
? await fs.readdir(pagesDir)
|
|
1409
|
+
: [];
|
|
1410
|
+
const hasLoginP = existingPages.includes('LoginPage.tsx');
|
|
1411
|
+
const hasHomeP = existingPages.includes('HomePage.tsx');
|
|
1412
|
+
const hasTemplateP = existingPages.includes('TemplatePage.tsx');
|
|
1413
|
+
const hasAssistantP =
|
|
1414
|
+
projectConfig?.hasAssistant ?? existingPages.includes('AssistantPage.tsx');
|
|
1415
|
+
const firstProtectedP = hasHomeP ? '/home' : hasTemplateP ? '/template' : '/login';
|
|
1416
|
+
await fs.writeFile(
|
|
1417
|
+
path.join(targetDir, 'src', 'app', 'components', 'AuthGuard.tsx'),
|
|
1418
|
+
generateAuthGuard({
|
|
1419
|
+
hasLogin: hasLoginP,
|
|
1420
|
+
hasHome: hasHomeP,
|
|
1421
|
+
hasTemplate: hasTemplateP,
|
|
1422
|
+
hasAssistant: hasAssistantP,
|
|
1423
|
+
firstProtectedPath: firstProtectedP,
|
|
1424
|
+
})
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1187
1427
|
}
|
|
1188
1428
|
|
|
1189
1429
|
if (filesToUpdate.includes('shared')) {
|