@camox/cli 0.16.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camox/cli",
3
- "version": "0.16.1",
3
+ "version": "0.17.0",
4
4
  "bin": {
5
5
  "camox": "./dist/index.mjs"
6
6
  },
@@ -26,7 +26,7 @@
26
26
  "@types/node": "^24.12.2",
27
27
  "@typescript/native-preview": "7.0.0-dev.20260412.1",
28
28
  "vite-plus": "latest",
29
- "@camox/api-contract": "0.16.1"
29
+ "@camox/api-contract": "0.17.0"
30
30
  },
31
31
  "nx": {
32
32
  "tags": [
@@ -37,7 +37,7 @@ const faq = createBlock({
37
37
 
38
38
  function FaqComponent() {
39
39
  return (
40
- <section className="py-24">
40
+ <section className="py-12 sm:py-16 md:py-20">
41
41
  <div className="mx-auto max-w-2xl px-4">
42
42
  <Accordion>
43
43
  <faq.Repeater name="items">
@@ -27,9 +27,9 @@ const footer = createBlock({
27
27
 
28
28
  function FooterComponent() {
29
29
  return (
30
- <footer className="dark bg-background py-4">
30
+ <footer className="dark bg-background border-border border-t py-4">
31
31
  <div className="container mx-auto px-4">
32
- <div className="flex flex-wrap items-center justify-between gap-x-6 gap-y-2">
32
+ <div className="flex flex-col gap-4 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between sm:gap-x-6 sm:gap-y-2">
33
33
  <div className="flex items-center gap-2">
34
34
  <footer.Field name="title">
35
35
  {(props) => <div {...props} className="text-foreground text-sm font-bold" />}
@@ -37,7 +37,7 @@ function FooterComponent() {
37
37
  <div className="text-muted-foreground text-sm">&copy; {new Date().getFullYear()}</div>
38
38
  </div>
39
39
 
40
- <div className="ml-auto flex flex-wrap items-center justify-end gap-4">
40
+ <div className="flex flex-col items-start gap-4 sm:ml-auto sm:flex-row sm:flex-wrap sm:items-center sm:justify-end">
41
41
  <footer.Repeater name="links">
42
42
  {(linkItem) => (
43
43
  <linkItem.Link name="link">
@@ -2,6 +2,7 @@ import { Link } from "@tanstack/react-router";
2
2
  import { Type, createBlock } from "camox/createBlock";
3
3
 
4
4
  import { Button } from "@/components/ui/button";
5
+ import { cn } from "@/lib/utils";
5
6
 
6
7
  const hero = createBlock({
7
8
  id: "hero",
@@ -14,12 +15,12 @@ const hero = createBlock({
14
15
  title: "Title",
15
16
  }),
16
17
  description: Type.String({
17
- default: "Build something amazing with Camox. Press ⌘+Enter to start editing content.",
18
+ default: "Press ⌘+Enter to access Camox Studio and edit content.",
18
19
  maxLength: 280,
19
20
  title: "Description",
20
21
  }),
21
22
  cta: Type.Link({
22
- default: { text: "Get Started", href: "/", newTab: false },
23
+ default: { text: "Get started", href: "/", newTab: false },
23
24
  title: "CTA",
24
25
  }),
25
26
  illustration: Type.Image({
@@ -31,6 +32,11 @@ const hero = createBlock({
31
32
  default: true,
32
33
  title: "With illustration",
33
34
  }),
35
+ theme: Type.Enum({
36
+ default: "dark",
37
+ options: { light: "Light", dark: "Dark" },
38
+ title: "Theme",
39
+ }),
34
40
  },
35
41
  component: HeroComponent,
36
42
  toMarkdown: (c, s) => [`# ${c.title}`, c.description, s.withIllustration(c.illustration), c.cta],
@@ -38,57 +44,45 @@ const hero = createBlock({
38
44
 
39
45
  function HeroComponent() {
40
46
  const withIllustration = hero.useSetting("withIllustration");
47
+ const theme = hero.useSetting("theme");
41
48
 
42
- if (withIllustration) {
43
- return (
44
- <section className="py-32">
45
- <div className="container mx-auto px-4">
46
- <div className="grid items-center gap-12 lg:grid-cols-[1fr_auto]">
47
- <div className="text-left">
48
- <hero.Field name="title">
49
- {(props) => (
50
- <h1
51
- {...props}
52
- className="text-foreground mb-6 text-5xl font-bold tracking-tight sm:text-6xl"
53
- />
54
- )}
55
- </hero.Field>
56
- <hero.Field name="description">
57
- {(props) => <p {...props} className="text-muted-foreground mb-10 text-xl" />}
58
- </hero.Field>
59
- <hero.Link name="cta">
60
- {(props) => <Button size="lg" nativeButton={false} render={<Link {...props} />} />}
61
- </hero.Link>
62
- </div>
63
- <hero.Image name="illustration">
49
+ return (
50
+ <section
51
+ className={cn(
52
+ theme === "dark" ? "dark" : "light",
53
+ "bg-background py-16 sm:py-24 md:py-28",
54
+ !withIllustration && "flex flex-col items-center justify-center",
55
+ )}
56
+ >
57
+ <div className="container mx-auto">
58
+ <div
59
+ className={cn(
60
+ withIllustration
61
+ ? "grid items-center gap-12 lg:grid-cols-[1fr_auto]"
62
+ : "mx-auto max-w-3xl text-center",
63
+ )}
64
+ >
65
+ <div className={cn(withIllustration && "text-left")}>
66
+ <hero.Field name="title">
64
67
  {(props) => (
65
- <img {...props} className="h-auto w-full max-w-sm rounded-lg lg:max-w-md" />
68
+ <h1
69
+ {...props}
70
+ className="text-foreground mb-6 text-4xl font-bold tracking-tight sm:text-6xl"
71
+ />
66
72
  )}
67
- </hero.Image>
73
+ </hero.Field>
74
+ <hero.Field name="description">
75
+ {(props) => <p {...props} className="text-muted-foreground mb-10 text-xl" />}
76
+ </hero.Field>
77
+ <hero.Link name="cta">
78
+ {(props) => <Button size="lg" nativeButton={false} render={<Link {...props} />} />}
79
+ </hero.Link>
68
80
  </div>
69
- </div>
70
- </section>
71
- );
72
- }
73
-
74
- return (
75
- <section className="flex flex-col items-center justify-center py-32">
76
- <div className="container mx-auto px-4">
77
- <div className="mx-auto max-w-3xl text-center">
78
- <hero.Field name="title">
79
- {(props) => (
80
- <h1
81
- {...props}
82
- className="text-foreground mb-6 text-5xl font-bold tracking-tight sm:text-6xl"
83
- />
84
- )}
85
- </hero.Field>
86
- <hero.Field name="description">
87
- {(props) => <p {...props} className="text-muted-foreground mb-10 text-xl" />}
88
- </hero.Field>
89
- <hero.Link name="cta">
90
- {(props) => <Button size="lg" nativeButton={false} render={<Link {...props} />} />}
91
- </hero.Link>
81
+ {withIllustration && (
82
+ <hero.Image name="illustration">
83
+ {(props) => <img {...props} className="h-auto w-full max-w-md rounded-lg" />}
84
+ </hero.Image>
85
+ )}
92
86
  </div>
93
87
  </div>
94
88
  </section>
@@ -1,5 +1,6 @@
1
1
  import { Link } from "@tanstack/react-router";
2
2
  import { Type, createBlock } from "camox/createBlock";
3
+ import type { ReactElement } from "react";
3
4
 
4
5
  import { Button } from "@/components/ui/button";
5
6
 
@@ -31,44 +32,78 @@ const navbar = createBlock({
31
32
  toMarkdown: (c) => [c.link],
32
33
  }),
33
34
  cta: Type.Link({
34
- default: { text: "Get Started", href: "#", newTab: false },
35
- title: "CTA",
35
+ default: { text: "Get started", href: "#", newTab: false },
36
+ title: "Call to action",
37
+ }),
38
+ },
39
+ settings: {
40
+ sticky: Type.Boolean({
41
+ default: true,
42
+ title: "Sticky",
36
43
  }),
37
44
  },
38
45
  component: NavbarComponent,
39
46
  toMarkdown: (c) => [c.title, c.links, c.cta],
40
47
  });
41
48
 
42
- function NavbarComponent() {
43
- return (
44
- <nav className="dark bg-background border-border border-b">
45
- <div className="container mx-auto px-4">
46
- <div className="flex h-16 items-center justify-between">
47
- <navbar.Link name="title">
48
- {(props) => <Link {...props} className="text-foreground text-lg font-bold" />}
49
- </navbar.Link>
49
+ function NavbarComponent(): ReactElement {
50
+ const sticky = navbar.useSetting("sticky");
51
+
52
+ const innerContent = (
53
+ <div className="container mx-auto px-4">
54
+ <div className="flex h-16 items-center justify-between">
55
+ <navbar.Link name="title">
56
+ {(props) => <Link {...props} className="text-foreground text-lg font-bold" />}
57
+ </navbar.Link>
50
58
 
51
- <div className="flex items-center gap-6">
52
- <navbar.Repeater name="links">
53
- {(linkItem) => (
54
- <linkItem.Link name="link">
55
- {(props) => (
56
- <Link
57
- {...props}
58
- className="text-muted-foreground hover:text-foreground text-sm transition-colors"
59
- />
60
- )}
61
- </linkItem.Link>
62
- )}
63
- </navbar.Repeater>
59
+ <div className="flex items-center gap-6">
60
+ <navbar.Repeater name="links">
61
+ {(linkItem) => (
62
+ <linkItem.Link name="link">
63
+ {(props) => (
64
+ <Link
65
+ {...props}
66
+ className="text-muted-foreground hover:text-foreground text-sm transition-colors"
67
+ />
68
+ )}
69
+ </linkItem.Link>
70
+ )}
71
+ </navbar.Repeater>
64
72
 
65
- <navbar.Link name="cta">
66
- {(props) => <Button size="sm" nativeButton={false} render={<Link {...props} />} />}
67
- </navbar.Link>
68
- </div>
73
+ <navbar.Link name="cta">
74
+ {(props) => (
75
+ <Button
76
+ size="sm"
77
+ variant="outline"
78
+ className="text-foreground"
79
+ nativeButton={false}
80
+ render={<Link {...props} />}
81
+ />
82
+ )}
83
+ </navbar.Link>
69
84
  </div>
70
85
  </div>
71
- </nav>
86
+ </div>
87
+ );
88
+
89
+ if (!sticky) {
90
+ return <nav className="dark bg-background border-border border-b">{innerContent}</nav>;
91
+ }
92
+
93
+ return (
94
+ <>
95
+ <div aria-hidden className="dark bg-background h-16 border-b border-transparent" />
96
+ <navbar.Detached>
97
+ {(props) => (
98
+ <nav
99
+ {...props}
100
+ className="dark bg-background border-border fixed top-0 right-0 left-0 z-50 border-b"
101
+ >
102
+ {innerContent}
103
+ </nav>
104
+ )}
105
+ </navbar.Detached>
106
+ </>
72
107
  );
73
108
  }
74
109
 
@@ -44,7 +44,7 @@ const statistics = createBlock({
44
44
 
45
45
  function StatisticsComponent() {
46
46
  return (
47
- <section className="dark bg-background py-24">
47
+ <section className="dark bg-background py-12 sm:py-16 md:py-20">
48
48
  <div className="container mx-auto">
49
49
  <div className="mb-16">
50
50
  <statistics.Field name="title">
@@ -57,7 +57,7 @@ function StatisticsComponent() {
57
57
  </statistics.Field>
58
58
  <statistics.Field name="subtitle">
59
59
  {(props) => (
60
- <h2 {...props} className="text-foreground mb-6 text-4xl font-bold sm:text-5xl" />
60
+ <h2 {...props} className="text-foreground mb-6 text-3xl font-bold sm:text-5xl" />
61
61
  )}
62
62
  </statistics.Field>
63
63
  <statistics.Field name="description">
@@ -70,11 +70,14 @@ function StatisticsComponent() {
70
70
  <statistics.Repeater name="statistics">
71
71
  {(stat) => (
72
72
  <div className="flex gap-3">
73
- <div className="w-0.5 bg-linear-to-b from-teal-400 to-emerald-500" />
73
+ <div className="from-chart-1 to-chart-2 w-0.5 bg-linear-to-b" />
74
74
  <div className="flex flex-col">
75
75
  <stat.Field name="number">
76
76
  {(props) => (
77
- <div {...props} className="text-foreground mb-2 text-4xl font-bold" />
77
+ <div
78
+ {...props}
79
+ className="text-foreground mb-2 text-3xl font-bold sm:text-4xl"
80
+ />
78
81
  )}
79
82
  </stat.Field>
80
83
  <stat.Field name="label">
@@ -11,9 +11,9 @@ const testimonial = createBlock({
11
11
  "This platform has transformed how we build and manage our website. The developer experience is exceptional.",
12
12
  title: "Quote",
13
13
  }),
14
- author: Type.String({ default: "Sarah Chen", title: "Author" }),
14
+ author: Type.String({ default: "Kai Doe", title: "Author" }),
15
15
  title: Type.String({ default: "Senior Developer", title: "Title" }),
16
- company: Type.String({ default: "TechCorp", title: "Company" }),
16
+ company: Type.String({ default: "E Corp", title: "Company" }),
17
17
  },
18
18
  component: TestimonialComponent,
19
19
  toMarkdown: (c) => [`> ${c.quote}`, `— ${c.author}, ${c.title}, ${c.company}`],
@@ -21,14 +21,14 @@ const testimonial = createBlock({
21
21
 
22
22
  function TestimonialComponent() {
23
23
  return (
24
- <section className="bg-background py-24">
24
+ <section className="bg-background py-12 sm:py-16 md:py-24">
25
25
  <div className="container mx-auto px-4">
26
26
  <div className="mx-auto max-w-4xl text-center">
27
27
  <testimonial.Field name="quote">
28
28
  {(props) => (
29
29
  <blockquote
30
30
  {...props}
31
- className="text-foreground mb-8 text-2xl leading-relaxed font-medium sm:text-3xl"
31
+ className="text-foreground mb-8 text-xl leading-relaxed font-medium sm:text-3xl"
32
32
  >
33
33
  "{props.children}"
34
34
  </blockquote>
@@ -5,6 +5,7 @@ import { block as footerBlock } from "../blocks/footer";
5
5
  import { block as heroBlock } from "../blocks/hero";
6
6
  import { block as navbarBlock } from "../blocks/navbar";
7
7
  import { block as statisticsBlock } from "../blocks/statistics";
8
+ import { block as testimonialBlock } from "../blocks/testimonial";
8
9
 
9
10
  const defaultLayout = createLayout({
10
11
  id: "default",
@@ -13,7 +14,7 @@ const defaultLayout = createLayout({
13
14
  blocks: {
14
15
  before: [navbarBlock],
15
16
  after: [footerBlock],
16
- initial: [heroBlock, statisticsBlock, faqBlock],
17
+ initial: [heroBlock, testimonialBlock, statisticsBlock, faqBlock],
17
18
  },
18
19
  component: DefaultLayout,
19
20
  buildMetaTitle: ({ pageMetaTitle, projectName }) => `${pageMetaTitle} | ${projectName}`,
@@ -8,82 +8,79 @@
8
8
 
9
9
  body {
10
10
  @apply m-0;
11
- font-family:
12
- -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell",
13
- "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
14
11
  -webkit-font-smoothing: antialiased;
15
12
  -moz-osx-font-smoothing: grayscale;
16
13
  }
17
14
 
18
15
  :root {
19
- --background: oklch(1 0 0);
20
- --foreground: oklch(0.141 0.005 285.823);
16
+ --background: oklch(0.99 0.01 260);
17
+ --foreground: oklch(0 0 0);
21
18
  --card: oklch(1 0 0);
22
- --card-foreground: oklch(0.141 0.005 285.823);
19
+ --card-foreground: oklch(0 0 0);
23
20
  --popover: oklch(1 0 0);
24
- --popover-foreground: oklch(0.141 0.005 285.823);
25
- --primary: oklch(0.62 0.19 260);
26
- --primary-foreground: oklch(0.984 0.019 200.873);
27
- --secondary: oklch(0.967 0.001 286.375);
28
- --secondary-foreground: oklch(0.21 0.006 285.885);
29
- --muted: oklch(0.967 0.001 286.375);
30
- --muted-foreground: oklch(0.552 0.016 285.938);
31
- --accent: oklch(0.967 0.001 286.375);
32
- --accent-foreground: oklch(0.21 0.006 285.885);
21
+ --popover-foreground: oklch(0 0 0);
22
+ --primary: oklch(0.623 0.214 259.815);
23
+ --primary-foreground: oklch(0.97 0.014 254.604);
24
+ --secondary: oklch(0.96 0.02 260);
25
+ --secondary-foreground: oklch(0.22 0.05 260);
26
+ --muted: oklch(0.96 0.02 260);
27
+ --muted-foreground: oklch(0.55 0.05 260);
28
+ --accent: oklch(0.96 0.02 260);
29
+ --accent-foreground: oklch(0.22 0.05 260);
33
30
  --destructive: oklch(0.577 0.245 27.325);
34
31
  --destructive-foreground: oklch(0.577 0.245 27.325);
35
- --border: oklch(0.92 0.004 286.32);
36
- --input: oklch(0.92 0.004 286.32);
37
- --ring: oklch(0.705 0.015 286.067);
38
- --chart-1: oklch(0.871 0.006 286.286);
39
- --chart-2: oklch(0.552 0.016 285.938);
40
- --chart-3: oklch(0.442 0.017 285.786);
41
- --chart-4: oklch(0.37 0.013 285.805);
42
- --chart-5: oklch(0.274 0.006 286.033);
32
+ --border: oklch(0.92 0.02 260);
33
+ --input: oklch(0.92 0.02 260);
34
+ --ring: oklch(0.7 0.05 260);
35
+ --chart-1: oklch(0.871 0.02 260);
36
+ --chart-2: oklch(0.62 0.19 260);
37
+ --chart-3: oklch(0.5 0.12 260);
38
+ --chart-4: oklch(0.4 0.08 260);
39
+ --chart-5: oklch(0.3 0.05 260);
43
40
  --radius: 0.625rem;
44
- --sidebar: oklch(0.985 0 0);
45
- --sidebar-foreground: oklch(0.141 0.005 285.823);
46
- --sidebar-primary: oklch(0.609 0.126 221.723);
41
+ --sidebar: oklch(0.99 0.01 260);
42
+ --sidebar-foreground: oklch(0 0 0);
43
+ --sidebar-primary: oklch(0.62 0.19 260);
47
44
  --sidebar-primary-foreground: oklch(0.984 0.019 200.873);
48
- --sidebar-accent: oklch(0.967 0.001 286.375);
49
- --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
50
- --sidebar-border: oklch(0.92 0.004 286.32);
51
- --sidebar-ring: oklch(0.705 0.015 286.067);
45
+ --sidebar-accent: oklch(0.96 0.02 260);
46
+ --sidebar-accent-foreground: oklch(0.22 0.05 260);
47
+ --sidebar-border: oklch(0.92 0.02 260);
48
+ --sidebar-ring: oklch(0.7 0.05 260);
52
49
  }
53
50
 
54
51
  .dark {
55
- --background: oklch(0.141 0.005 285.823);
52
+ --background: oklch(0.17 0.05 260);
56
53
  --foreground: oklch(0.985 0 0);
57
- --card: oklch(0.21 0.006 285.885);
54
+ --card: oklch(0.22 0.05 260);
58
55
  --card-foreground: oklch(0.985 0 0);
59
- --popover: oklch(0.21 0.006 285.885);
56
+ --popover: oklch(0.22 0.05 260);
60
57
  --popover-foreground: oklch(0.985 0 0);
61
- --primary: oklch(0.62 0.19 260);
62
- --primary-foreground: oklch(0.984 0.019 200.873);
63
- --secondary: oklch(0.274 0.006 286.033);
58
+ --primary: oklch(0.623 0.214 259.815);
59
+ --primary-foreground: oklch(0.97 0.014 254.604);
60
+ --secondary: oklch(0.3 0.05 260);
64
61
  --secondary-foreground: oklch(0.985 0 0);
65
- --muted: oklch(0.274 0.006 286.033);
66
- --muted-foreground: oklch(0.705 0.015 286.067);
67
- --accent: oklch(0.274 0.006 286.033);
62
+ --muted: oklch(0.3 0.05 260);
63
+ --muted-foreground: oklch(0.72 0.03 260);
64
+ --accent: oklch(0.3 0.05 260);
68
65
  --accent-foreground: oklch(0.985 0 0);
69
66
  --destructive: oklch(0.704 0.191 22.216);
70
67
  --destructive-foreground: oklch(0.637 0.237 25.331);
71
68
  --border: oklch(1 0 0 / 10%);
72
69
  --input: oklch(1 0 0 / 15%);
73
- --ring: oklch(0.552 0.016 285.938);
74
- --chart-1: oklch(0.871 0.006 286.286);
75
- --chart-2: oklch(0.552 0.016 285.938);
76
- --chart-3: oklch(0.442 0.017 285.786);
77
- --chart-4: oklch(0.37 0.013 285.805);
78
- --chart-5: oklch(0.274 0.006 286.033);
79
- --sidebar: oklch(0.21 0.006 285.885);
70
+ --ring: oklch(0.55 0.05 260);
71
+ --chart-1: oklch(0.871 0.02 260);
72
+ --chart-2: oklch(0.62 0.19 260);
73
+ --chart-3: oklch(0.5 0.12 260);
74
+ --chart-4: oklch(0.4 0.08 260);
75
+ --chart-5: oklch(0.3 0.05 260);
76
+ --sidebar: oklch(0.22 0.05 260);
80
77
  --sidebar-foreground: oklch(0.985 0 0);
81
- --sidebar-primary: oklch(0.715 0.143 215.221);
82
- --sidebar-primary-foreground: oklch(0.302 0.056 229.695);
83
- --sidebar-accent: oklch(0.274 0.006 286.033);
78
+ --sidebar-primary: oklch(0.62 0.19 260);
79
+ --sidebar-primary-foreground: oklch(0.984 0.019 200.873);
80
+ --sidebar-accent: oklch(0.3 0.05 260);
84
81
  --sidebar-accent-foreground: oklch(0.985 0 0);
85
82
  --sidebar-border: oklch(1 0 0 / 10%);
86
- --sidebar-ring: oklch(0.552 0.016 285.938);
83
+ --sidebar-ring: oklch(0.55 0.05 260);
87
84
  }
88
85
 
89
86
  @theme inline {
@@ -142,11 +139,24 @@ body {
142
139
  }
143
140
  html {
144
141
  @apply font-sans;
142
+ font-size: 14px;
143
+ @media (width >= theme(--breakpoint-sm)) {
144
+ font-size: 15px;
145
+ }
146
+ @media (width >= theme(--breakpoint-md)) {
147
+ font-size: 16px;
148
+ }
145
149
  }
146
150
  }
147
151
 
148
152
  @utility container {
149
153
  margin-inline: auto;
150
- padding-inline: 1rem;
154
+ padding-inline: 0.5rem;
151
155
  max-width: 80rem;
156
+ @media (width >= theme(--breakpoint-sm)) {
157
+ padding-inline: 1rem;
158
+ }
159
+ @media (width >= theme(--breakpoint-md)) {
160
+ padding-inline: 2rem;
161
+ }
152
162
  }