@camox/cli 0.10.1 → 0.12.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camox/cli",
3
- "version": "0.10.1",
3
+ "version": "0.12.1",
4
4
  "bin": {
5
5
  "camox": "./dist/index.mjs"
6
6
  },
@@ -26,7 +26,12 @@
26
26
  "@typescript/native-preview": "7.0.0-dev.20260412.1",
27
27
  "oxlint": "^0.15.0",
28
28
  "tsdown": "^0.21.8",
29
- "@camox/api-contract": "0.10.1"
29
+ "@camox/api-contract": "0.12.1"
30
+ },
31
+ "nx": {
32
+ "tags": [
33
+ "type:pkg"
34
+ ]
30
35
  },
31
36
  "scripts": {
32
37
  "build": "tsdown",
@@ -26,6 +26,7 @@
26
26
  "nitro": "3.0.260311-beta",
27
27
  "react": "^19.2.5",
28
28
  "react-dom": "^19.2.5",
29
+ "shadcn": "^4.3.1",
29
30
  "tailwind-merge": "^3.5.0",
30
31
  "tailwindcss": "^4.0.6"
31
32
  },
@@ -0,0 +1,63 @@
1
+ import { Type, createBlock } from "camox/createBlock";
2
+
3
+ import {
4
+ Accordion,
5
+ AccordionContent,
6
+ AccordionItem,
7
+ AccordionTrigger,
8
+ } from "@/components/ui/accordion";
9
+
10
+ const faq = createBlock({
11
+ id: "faq",
12
+ title: "FAQ",
13
+ description:
14
+ "Use this block to answer common questions about the product, pricing, or company. Place it near the bottom of a page to address objections before a conversion section.",
15
+ content: {
16
+ items: Type.RepeatableItem({
17
+ content: {
18
+ question: Type.String({
19
+ default: "What is your refund policy?",
20
+ title: "Question",
21
+ }),
22
+ answer: Type.String({
23
+ default:
24
+ "We offer a 30-day money-back guarantee. If you're not satisfied, contact support and we'll process your refund right away.",
25
+ title: "Answer",
26
+ }),
27
+ },
28
+ minItems: 3,
29
+ maxItems: Infinity,
30
+ title: "Questions",
31
+ toMarkdown: (c) => [`Q: ${c.question}`, `A: ${c.answer}`],
32
+ }),
33
+ },
34
+ component: FaqComponent,
35
+ toMarkdown: (c) => [c.items],
36
+ });
37
+
38
+ function FaqComponent() {
39
+ return (
40
+ <section className="py-24">
41
+ <div className="mx-auto max-w-2xl px-4">
42
+ <Accordion>
43
+ <faq.Repeater name="items">
44
+ {(item, index) => (
45
+ <AccordionItem value={index}>
46
+ <AccordionTrigger className="items-center text-lg">
47
+ <item.Field name="question">{(props) => <span {...props} />}</item.Field>
48
+ </AccordionTrigger>
49
+ <AccordionContent>
50
+ <item.Field name="answer">
51
+ {(props) => <p {...props} className="text-muted-foreground text-base" />}
52
+ </item.Field>
53
+ </AccordionContent>
54
+ </AccordionItem>
55
+ )}
56
+ </faq.Repeater>
57
+ </Accordion>
58
+ </div>
59
+ </section>
60
+ );
61
+ }
62
+
63
+ export { faq as block };
@@ -6,25 +6,23 @@ const footer = createBlock({
6
6
  title: "Footer",
7
7
  layoutOnly: true,
8
8
  description: "A footer at the bottom of a page with a site name and navigation links.",
9
- toMarkdown: ["{{title}}", "{{links}}"],
10
9
  content: {
11
10
  title: Type.String({ default: "{{projectName}}" }),
12
- links: Type.RepeatableItem(
13
- {
11
+ links: Type.RepeatableItem({
12
+ content: {
14
13
  link: Type.Link({
15
14
  default: { text: "Link", href: "#", newTab: false },
16
15
  title: "Link",
17
16
  }),
18
17
  },
19
- {
20
- minItems: 1,
21
- maxItems: 12,
22
- title: "Links",
23
- toMarkdown: ["{{link}}"],
24
- },
25
- ),
18
+ minItems: 1,
19
+ maxItems: 12,
20
+ title: "Links",
21
+ toMarkdown: (c) => [c.link],
22
+ }),
26
23
  },
27
24
  component: FooterComponent,
25
+ toMarkdown: (c) => [c.title, c.links],
28
26
  });
29
27
 
30
28
  function FooterComponent() {
@@ -8,7 +8,6 @@ const hero = createBlock({
8
8
  title: "Hero",
9
9
  description:
10
10
  "Use this block as the main landing section at the top of a page. It should capture attention immediately with a clear value proposition.",
11
- toMarkdown: ["# {{title}}", "{{description}}", "{{cta}}"],
12
11
  content: {
13
12
  title: Type.String({
14
13
  default: "Welcome to {{projectName}}",
@@ -25,6 +24,7 @@ const hero = createBlock({
25
24
  }),
26
25
  },
27
26
  component: HeroComponent,
27
+ toMarkdown: (c) => [`# ${c.title}`, c.description, c.cta],
28
28
  });
29
29
 
30
30
  function HeroComponent() {
@@ -9,7 +9,6 @@ const navbar = createBlock({
9
9
  layoutOnly: true,
10
10
  description:
11
11
  "A navigation bar at the top of a page with a brand name, navigation links, and a call-to-action link.",
12
- toMarkdown: ["{{title}}", "{{links}}", "{{cta}}"],
13
12
  content: {
14
13
  title: Type.Link({
15
14
  title: "Site name",
@@ -19,26 +18,25 @@ const navbar = createBlock({
19
18
  newTab: false,
20
19
  },
21
20
  }),
22
- links: Type.RepeatableItem(
23
- {
21
+ links: Type.RepeatableItem({
22
+ content: {
24
23
  link: Type.Link({
25
24
  default: { text: "Link", href: "#", newTab: false },
26
25
  title: "Link",
27
26
  }),
28
27
  },
29
- {
30
- minItems: 1,
31
- maxItems: 6,
32
- title: "Links",
33
- toMarkdown: ["{{link}}"],
34
- },
35
- ),
28
+ minItems: 1,
29
+ maxItems: 6,
30
+ title: "Links",
31
+ toMarkdown: (c) => [c.link],
32
+ }),
36
33
  cta: Type.Link({
37
34
  default: { text: "Get Started", href: "#", newTab: false },
38
35
  title: "CTA",
39
36
  }),
40
37
  },
41
38
  component: NavbarComponent,
39
+ toMarkdown: (c) => [c.title, c.links, c.cta],
42
40
  });
43
41
 
44
42
  function NavbarComponent() {
@@ -5,7 +5,6 @@ const statistics = createBlock({
5
5
  title: "Statistics",
6
6
  description:
7
7
  "Showcase key metrics, achievements, or performance indicators. Ideal for displaying platform statistics or company milestones.",
8
- toMarkdown: ["## {{subtitle}}", "{{description}}", "{{statistics}}"],
9
8
  content: {
10
9
  title: Type.String({
11
10
  default: "By the numbers",
@@ -21,8 +20,8 @@ const statistics = createBlock({
21
20
  "Our platform empowers teams to build and ship faster. Here are some numbers we're proud of.",
22
21
  title: "Description",
23
22
  }),
24
- statistics: Type.RepeatableItem(
25
- {
23
+ statistics: Type.RepeatableItem({
24
+ content: {
26
25
  number: Type.String({
27
26
  default: "100+",
28
27
  maxLength: 7,
@@ -33,15 +32,14 @@ const statistics = createBlock({
33
32
  title: "Label",
34
33
  }),
35
34
  },
36
- {
37
- minItems: 3,
38
- maxItems: 8,
39
- title: "Statistics",
40
- toMarkdown: ["**{{number}}** — {{label}}"],
41
- },
42
- ),
35
+ minItems: 3,
36
+ maxItems: 8,
37
+ title: "Statistics",
38
+ toMarkdown: (c) => [`**${c.number}** — ${c.label}`],
39
+ }),
43
40
  },
44
41
  component: StatisticsComponent,
42
+ toMarkdown: (c) => [`## ${c.subtitle}`, c.description, c.statistics],
45
43
  });
46
44
 
47
45
  function StatisticsComponent() {
@@ -5,7 +5,6 @@ const testimonial = createBlock({
5
5
  title: "Testimonial",
6
6
  description:
7
7
  "Display a customer testimonial or user review. Ideal for building trust and social proof.",
8
- toMarkdown: ["> {{quote}}", "— {{author}}, {{title}}, {{company}}"],
9
8
  content: {
10
9
  quote: Type.String({
11
10
  default:
@@ -17,6 +16,7 @@ const testimonial = createBlock({
17
16
  company: Type.String({ default: "TechCorp", title: "Company" }),
18
17
  },
19
18
  component: TestimonialComponent,
19
+ toMarkdown: (c) => [`> ${c.quote}`, `— ${c.author}, ${c.title}, ${c.company}`],
20
20
  });
21
21
 
22
22
  function TestimonialComponent() {
@@ -1,5 +1,6 @@
1
1
  @import "tailwindcss";
2
2
  @import "tw-animate-css";
3
+ @import "shadcn-ui/tailwind.css";
3
4
 
4
5
  @custom-variant dark (&:is(.dark *));
5
6