cascivo 0.7.2 → 0.8.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/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as detectPackageManager, c as isPackageManager, l as loadConfig, n as DEFAULT_CONFIG$1, o as installCommand, r as THEMES, s as installHint, t as CASCIVO_HOST, u as __exportAll } from "./config-D7ddWN_9.mjs";
2
+ import { a as detectPackageManager, c as isPackageManager, l as loadConfig, n as DEFAULT_CONFIG$1, o as installCommand, r as THEMES, s as installHint, t as CASCIVO_HOST, u as __exportAll } from "./config-C8D_CmKI.mjs";
3
3
  import { n as resolveOutputPath, r as writeFileSafe, t as readFileSafe } from "./fs-m7ZvuBBm.mjs";
4
4
  import { r as fetchTextRetry, t as fetchJson } from "./http-CJZ5W0Fa.mjs";
5
5
  import { a as writeLock, c as findComponent, i as updateLockEntry, n as readLock, o as fetchRegistry, r as sha256, s as fileName, t as createLock } from "./lock-CW8UuEPJ.mjs";
@@ -514,11 +514,11 @@ function resolvePackageManagerFlag(args) {
514
514
  * and GETTING-STARTED.md tells adopters to pin exactly. Regenerate with `pnpm regen`.
515
515
  */
516
516
  const CASCIVO_VERSIONS = {
517
- "@cascivo/react": "0.16.1",
518
- "@cascivo/themes": "0.4.11",
519
- "@cascivo/charts": "0.16.1",
520
- "@cascivo/icons": "0.3.8",
521
- "@cascivo/eslint-config": "0.2.2"
517
+ "@cascivo/react": "0.17.1",
518
+ "@cascivo/themes": "0.4.12",
519
+ "@cascivo/charts": "0.17.1",
520
+ "@cascivo/icons": "0.3.9",
521
+ "@cascivo/eslint-config": "0.2.3"
522
522
  };
523
523
  /** `@cascivo/core`'s declared `@preact/signals-react` peer range. */
524
524
  const SIGNALS_PEER = ">=3.0.0";
@@ -614,7 +614,9 @@ function packageJson(opts) {
614
614
  build: "tsc && vite build",
615
615
  preview: "vite preview",
616
616
  typecheck: "tsc --noEmit",
617
- lint: "eslint ."
617
+ lint: "eslint .",
618
+ format: "prettier --write .",
619
+ "format:check": "prettier --check ."
618
620
  },
619
621
  dependencies: {
620
622
  "@cascivo/react": V["@cascivo/react"],
@@ -631,7 +633,9 @@ function packageJson(opts) {
631
633
  "@vitejs/plugin-react": "^5.0.0",
632
634
  eslint: "^9.0.0",
633
635
  "eslint-plugin-react-hooks": "^7.0.0",
636
+ prettier: "^3.0.0",
634
637
  typescript: "^5.7.0",
638
+ "typescript-eslint": "^8.0.0",
635
639
  vite: "^7.0.0"
636
640
  }
637
641
  };
@@ -679,7 +683,7 @@ function indexHtml(opts) {
679
683
  <head>
680
684
  <meta charset="UTF-8" />
681
685
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
682
- <title>${opts.name}</title>
686
+ <title>${brandName(opts.name)}</title>
683
687
  <style>
684
688
  @layer vendor, cascivo.reset, cascivo.base, cascivo.tokens, cascivo.component,
685
689
  cascivo.platform, cascivo.theme, cascivo.blocks, cascivo.example, cascivo.override;
@@ -699,10 +703,12 @@ function indexHtml(opts) {
699
703
  padding: 0;
700
704
  }
701
705
  }
702
- html,
703
- body,
704
- #root {
705
- height: 100%;
706
+ @layer cascivo.base {
707
+ html,
708
+ body,
709
+ #root {
710
+ height: 100%;
711
+ }
706
712
  }
707
713
  </style>
708
714
  </head>
@@ -731,7 +737,7 @@ if (root) {
731
737
  function viteEnv() {
732
738
  return `/// <reference types="vite/client" />\n`;
733
739
  }
734
- function appTsx(opts, sections) {
740
+ function appTsx(sections) {
735
741
  const sectionImports = sections.map((s) => `import { ${s.component} } from './sections/${s.component}'`).join("\n");
736
742
  const unionType = sections.map((s) => `'${s.key}'`).join(" | ");
737
743
  const navItems = sections.map((s) => ` {
@@ -744,19 +750,10 @@ function appTsx(opts, sections) {
744
750
  },`).join("\n");
745
751
  const renderedSections = sections.map((s) => ` {section.value === '${s.key}' && <${s.component} />}`).join("\n");
746
752
  return `'use client'
747
- import {
748
- AppShell,
749
- ShellHeader,
750
- SideNav,
751
- signal,
752
- useSignals,
753
- type SideNavItem,
754
- } from '@cascivo/react'
753
+ import { signal, useSignals, type SideNavItem } from '@cascivo/react'
754
+ import { Shell } from './Shell'
755
755
  ${sectionImports}
756
756
 
757
- import '@cascivo/themes/${opts.theme}.css'
758
- import '@cascivo/react/styles.css'
759
-
760
757
  type Section = ${unionType}
761
758
 
762
759
  const section = signal<Section>('${sections[0].key}')
@@ -768,36 +765,80 @@ export default function App() {
768
765
  ${navItems}
769
766
  ]
770
767
 
768
+ return (
769
+ <Shell navItems={navItems}>
770
+ ${renderedSections}
771
+ </Shell>
772
+ )
773
+ }
774
+ `;
775
+ }
776
+ /**
777
+ * The app shell, as its own component with a `children` slot.
778
+ *
779
+ * Split out of `App.tsx` because the shell composition — AppShell + ShellHeader + SideNav —
780
+ * is the valuable part of the scaffold, and it used to be welded to the signal-driven
781
+ * section switcher. A 2026-08-14 adopter prompted for "a dashboard with Vite and React
782
+ * Router", then deleted `App.tsx` and all of `src/sections/` — the majority of what `create`
783
+ * generated — and re-derived this wiring by hand.
784
+ *
785
+ * Now adding a router means deleting `App.tsx` + `src/sections/` and rendering `<Shell>` from
786
+ * the root route's layout, with `navItems` carrying `href` instead of `onClick`. Nothing in
787
+ * here needs to change.
788
+ */
789
+ function shellTsx(opts) {
790
+ return `'use client'
791
+ import { AppShell, ShellHeader, SideNav, type SideNavItem } from '@cascivo/react'
792
+ import type { ReactNode } from 'react'
793
+
794
+ import '@cascivo/themes/${opts.theme}.css'
795
+ // No '@cascivo/react/styles.css' here. On a bundler build each component imports its
796
+ // own CSS, so you ship exactly what you use — this app emits well under 100 kB of entry
797
+ // CSS instead of the ~273 kB aggregate sheet. Import the aggregate ONLY if you drop the
798
+ // bundler (CDN / single-file setup). See https://cascivo.com/docs/getting-started.md
799
+ // The theme import above is always required — themes are never automatic.
800
+
801
+ export interface ShellProps {
802
+ /** Side-nav entries. Use \`href\` for a routed app, \`onClick\` for local state. */
803
+ navItems: SideNavItem[]
804
+ children: ReactNode
805
+ }
806
+
807
+ /**
808
+ * App shell: header + side nav + a content slot.
809
+ *
810
+ * Adding a router? Keep this file. Delete \`App.tsx\` and \`src/sections/\`, render
811
+ * \`<Shell navItems={…}>\` from your root route's layout with your \`<Outlet />\` as children,
812
+ * and give each nav item an \`href\` instead of an \`onClick\`.
813
+ *
814
+ * For those hrefs to become real router links, call \`setLinkComponent\` ONCE at startup in
815
+ * \`main.tsx\` — see the "Adding a router" section of README.md for the exact snippet, or
816
+ * https://cascivo.com/docs/using-with-a-router.md for the full recipe.
817
+ */
818
+ export function Shell({ navItems, children }: ShellProps) {
771
819
  return (
772
820
  <AppShell
773
821
  header={<ShellHeader brand={{ name: '${brandName(opts.name).replace(/'/g, "\\'")}' }} />}
774
822
  nav={<SideNav items={navItems} />}
775
823
  >
776
- ${renderedSections}
824
+ {children}
777
825
  </AppShell>
778
826
  )
779
827
  }
780
828
  `;
781
829
  }
782
830
  function sectionTsx(section) {
783
- return `import { Card, CardContent, CardHeader, CardTitle, Heading, Text } from '@cascivo/react'
831
+ return `import { Card, CardContent, CardHeader, CardTitle, Flex, Heading, Text } from '@cascivo/react'
784
832
 
785
833
  export function ${section.component}() {
786
834
  return (
787
- <div
788
- style={{
789
- display: 'grid',
790
- gap: 'var(--cascivo-space-6)',
791
- padding: 'var(--cascivo-space-6)',
792
- maxWidth: '64rem',
793
- }}
794
- >
795
- <div style={{ display: 'grid', gap: 'var(--cascivo-space-2)' }}>
835
+ <Flex gap={6}>
836
+ <Flex gap={2}>
796
837
  <Heading level={1}>${section.label}</Heading>
797
838
  <Text muted>
798
839
  Edit <code>src/sections/${section.component}.tsx</code> to build out this page.
799
840
  </Text>
800
- </div>
841
+ </Flex>
801
842
 
802
843
  <Card>
803
844
  <CardHeader>
@@ -810,7 +851,7 @@ export function ${section.component}() {
810
851
  </Text>
811
852
  </CardContent>
812
853
  </Card>
813
- </div>
854
+ </Flex>
814
855
  )
815
856
  }
816
857
  `;
@@ -826,22 +867,45 @@ export function ${section.component}() {
826
867
  */
827
868
  function eslintConfig() {
828
869
  return `import js from '@eslint/js'
870
+ import tseslint from 'typescript-eslint'
829
871
  import reactHooks from 'eslint-plugin-react-hooks'
830
872
  import cascivo from '@cascivo/eslint-config'
831
873
 
832
874
  export default [
875
+ { ignores: ['dist/**'] },
833
876
  js.configs.recommended,
877
+ // Registers the TypeScript parser and the .ts/.tsx \`files\` patterns. WITHOUT THIS ESLINT
878
+ // LINTS NOTHING: its default \`files\` is **/*.{js,cjs,mjs}, so every file in this app is
879
+ // skipped with "File ignored because no matching configuration was supplied" and the
880
+ // \`lint\` script exits 0 having checked zero files.
881
+ ...tseslint.configs.recommended,
834
882
  // NOTE the \`.flat\` — the plugin exports both \`configs['recommended-latest']\` (the legacy
835
883
  // eslintrc shape, which applies NOTHING here and reports no error) and this one.
836
884
  reactHooks.configs.flat['recommended-latest'],
837
885
  // Spread LAST — flat config is last-wins. This turns off \`react-hooks/immutability\`,
838
- // which reports cascivo's signal writes (\`signal.value = next\`) as errors.
839
- // See https://cascivo.com/docs/using-with-strict-eslint.md
886
+ // which reports cascivo's signal writes (\`signal.value = next\`) as errors. That rule
887
+ // fires on the very first \`signal.value = x\` you write, so this is not optional wiring:
888
+ // see https://cascivo.com/docs/using-with-strict-eslint.md
840
889
  ...cascivo,
841
- { ignores: ['dist/**'] },
842
890
  ]
843
891
  `;
844
892
  }
893
+ /** Prettier config matching the style the scaffold's own generated source is written in. */
894
+ function prettierrc() {
895
+ return JSON.stringify({
896
+ semi: false,
897
+ singleQuote: true,
898
+ printWidth: 100
899
+ }, null, 2) + "\n";
900
+ }
901
+ function prettierIgnore() {
902
+ return `dist
903
+ node_modules
904
+ # Vendored cascivo source is formatted upstream — reformatting it makes every
905
+ # \`cascivo add\` update a merge conflict.
906
+ src/components/ui/
907
+ `;
908
+ }
845
909
  function gitignore() {
846
910
  return `node_modules
847
911
  dist
@@ -865,10 +929,34 @@ ${runScriptCommand(pm, "dev")}
865
929
 
866
930
  ## Structure
867
931
 
868
- - \`src/App.tsx\` — app shell, navigation, and section routing
932
+ - \`src/Shell.tsx\` — the app shell (header + side nav + content slot). Router-agnostic.
933
+ - \`src/App.tsx\` — nav items and which section is showing
869
934
  - \`src/sections/\` — one component per nav item
870
935
 
871
936
  Add more components with \`npx cascivo add <component>\`.
937
+
938
+ ## Adding a router
939
+
940
+ This app switches sections with a signal, not a router. To add one (React Router,
941
+ TanStack Router, …):
942
+
943
+ 1. **Keep \`src/Shell.tsx\`.** Delete \`src/App.tsx\` and \`src/sections/\`.
944
+ 2. Render \`<Shell navItems={…}>\` from your root route's layout, with your \`<Outlet />\`
945
+ as its children.
946
+ 3. Give each nav item an \`href\` instead of \`onClick\`.
947
+ 4. Register your router's Link **once** at startup, in \`src/main.tsx\`:
948
+
949
+ \`\`\`tsx
950
+ import { setLinkComponent } from '@cascivo/react'
951
+ import type { LinkComponentProps } from '@cascivo/react'
952
+ import { Link } from 'react-router'
953
+
954
+ setLinkComponent(({ href, ...rest }: LinkComponentProps) => <Link to={href ?? '#'} {...rest} />)
955
+ \`\`\`
956
+
957
+ That one call makes \`SideNav\`, \`ShellHeader\` and \`Breadcrumb\` render real router links.
958
+ Links you write in page content use \`<Link asChild>\` instead — two kinds of link, two
959
+ mechanisms. Full recipe: https://cascivo.com/docs/using-with-a-router.md
872
960
  `;
873
961
  }
874
962
  function agentsMd(opts) {
@@ -916,6 +1004,28 @@ This app's declared layer order (in \`index.html\`):
916
1004
  }
917
1005
  \`\`\`
918
1006
 
1007
+ ## Routing
1008
+
1009
+ If you add a router, keep \`src/Shell.tsx\` and delete \`src/App.tsx\` + \`src/sections/\`.
1010
+
1011
+ cascivo links come in **two kinds**, wired two different ways. Do not intercept
1012
+ \`onClick\`, and do not hand-wrap nav items:
1013
+
1014
+ 1. **Config-driven navs** (\`SideNav\`, \`ShellHeader\`, \`Breadcrumb\`, \`Switcher\`) render
1015
+ through a module singleton. Register your router's Link once, in \`src/main.tsx\`:
1016
+ \`setLinkComponent(({ href, ...rest }: LinkComponentProps) => <Link to={href ?? '#'} {...rest} />)\`
1017
+ 2. **Links in page content** use \`asChild\`:
1018
+ \`<Link asChild><RouterLink to="/x">x</RouterLink></Link>\`
1019
+
1020
+ Full recipe: https://cascivo.com/docs/using-with-a-router.md
1021
+
1022
+ ## Types
1023
+
1024
+ The vocabulary types are on a subpath: \`import type { Tone } from '@cascivo/react/types'\`
1025
+ (also \`Progress\`, \`SpaceStep\`). \`Status.status\` and \`Badge.variant\` use them, so a
1026
+ \`Record<MyState, Tone>\` is the supported way to map domain states onto tones. **Never**
1027
+ add \`@cascivo/core\` to this app's dependencies — it is transitive here.
1028
+
919
1029
  More: cascivo's machine-readable guide is at https://cascivo.com/llms.txt.
920
1030
  `;
921
1031
  }
@@ -943,6 +1053,14 @@ function buildScaffold(opts) {
943
1053
  path: "eslint.config.js",
944
1054
  contents: eslintConfig()
945
1055
  },
1056
+ {
1057
+ path: ".prettierrc",
1058
+ contents: prettierrc()
1059
+ },
1060
+ {
1061
+ path: ".prettierignore",
1062
+ contents: prettierIgnore()
1063
+ },
946
1064
  {
947
1065
  path: ".gitignore",
948
1066
  contents: gitignore()
@@ -965,7 +1083,11 @@ function buildScaffold(opts) {
965
1083
  },
966
1084
  {
967
1085
  path: "src/App.tsx",
968
- contents: appTsx(opts, sections)
1086
+ contents: appTsx(sections)
1087
+ },
1088
+ {
1089
+ path: "src/Shell.tsx",
1090
+ contents: shellTsx(opts)
969
1091
  },
970
1092
  ...sections.map((s) => ({
971
1093
  path: `src/sections/${s.component}.tsx`,
@@ -995,7 +1117,10 @@ async function create(args, cwd = process.cwd()) {
995
1117
  process.exitCode = 1;
996
1118
  return;
997
1119
  }
998
- const pm = detectPackageManager(cwd, pmFlag.pm ? { override: pmFlag.pm } : {});
1120
+ const pm = detectPackageManager(cwd, {
1121
+ preferLockfileOverUserAgent: true,
1122
+ ...pmFlag.pm ? { override: pmFlag.pm } : {}
1123
+ });
999
1124
  const rl = !yes && stdin.isTTY ? createInterface({
1000
1125
  input: stdin,
1001
1126
  output: stdout
@@ -1028,7 +1153,7 @@ async function create(args, cwd = process.cwd()) {
1028
1153
  const templateSpec = flagValue(args, "template");
1029
1154
  if (templateSpec) {
1030
1155
  const { add } = await Promise.resolve().then(() => add_exports);
1031
- const { loadConfig } = await import("./config-D7ddWN_9.mjs").then((n) => n.i);
1156
+ const { loadConfig } = await import("./config-C8D_CmKI.mjs").then((n) => n.i);
1032
1157
  console.log(`\nInstalling template "${templateSpec}"…`);
1033
1158
  await add([templateSpec], await loadConfig(), {
1034
1159
  cwd: targetDir,
@@ -1039,6 +1164,13 @@ async function create(args, cwd = process.cwd()) {
1039
1164
  console.log(` cd ${name}`);
1040
1165
  console.log(` ${installAllCommand(pm)}`);
1041
1166
  console.log(` ${runScriptCommand(pm, "dev")}`);
1167
+ console.log("\nGood to know:");
1168
+ console.log(" No cascivo.config.ts is written — this app uses the prebuilt @cascivo/react");
1169
+ console.log(" packages and never copies source. `cascivo add <component>` writes the");
1170
+ console.log(" config itself the first time you vendor a component.");
1171
+ console.log("\n Adding a router? Keep src/Shell.tsx, delete src/App.tsx + src/sections/,");
1172
+ console.log(" and register your Link once with setLinkComponent — see");
1173
+ console.log(" https://cascivo.com/docs/using-with-a-router.md");
1042
1174
  } finally {
1043
1175
  rl?.close();
1044
1176
  }