@jay-framework/jay-stack-cli 0.15.4 → 0.15.6
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/agent-kit-template/{INSTRUCTIONS.md → designer/INSTRUCTIONS.md} +11 -8
- package/agent-kit-template/{contracts-and-plugins.md → designer/contracts-and-plugins.md} +1 -0
- package/agent-kit-template/{jay-html-syntax.md → designer/jay-html-components.md} +89 -158
- package/agent-kit-template/designer/jay-html-styling.md +97 -0
- package/agent-kit-template/designer/jay-html-syntax.md +44 -0
- package/agent-kit-template/designer/jay-html-template-syntax.md +203 -0
- package/agent-kit-template/developer/INSTRUCTIONS.md +34 -0
- package/agent-kit-template/developer/cli-commands.md +228 -0
- package/agent-kit-template/developer/component-data.md +109 -0
- package/agent-kit-template/developer/component-refs.md +117 -0
- package/agent-kit-template/developer/component-state.md +140 -0
- package/agent-kit-template/developer/configuration.md +76 -0
- package/agent-kit-template/developer/page-components.md +103 -0
- package/agent-kit-template/developer/page-contracts.md +114 -0
- package/agent-kit-template/developer/project-structure.md +242 -0
- package/agent-kit-template/developer/render-results.md +112 -0
- package/agent-kit-template/developer/routing.md +161 -0
- package/agent-kit-template/developer/seo-guide.md +93 -0
- package/agent-kit-template/plugin/INSTRUCTIONS.md +40 -0
- package/agent-kit-template/plugin/actions-guide.md +125 -0
- package/agent-kit-template/plugin/component-context.md +103 -0
- package/agent-kit-template/plugin/component-data.md +109 -0
- package/agent-kit-template/plugin/component-refs.md +117 -0
- package/agent-kit-template/plugin/component-state.md +140 -0
- package/agent-kit-template/plugin/component-structure.md +174 -0
- package/agent-kit-template/plugin/contracts-guide.md +193 -0
- package/agent-kit-template/plugin/plugin-structure.md +194 -0
- package/agent-kit-template/plugin/render-results.md +112 -0
- package/agent-kit-template/plugin/seo-guide.md +93 -0
- package/agent-kit-template/plugin/services-guide.md +116 -0
- package/agent-kit-template/plugin/validation.md +101 -0
- package/dist/index.js +805 -61
- package/package.json +10 -10
- /package/agent-kit-template/{cli-commands.md → designer/cli-commands.md} +0 -0
- /package/agent-kit-template/{project-structure.md → designer/project-structure.md} +0 -0
- /package/agent-kit-template/{routing.md → designer/routing.md} +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Component State Hooks
|
|
2
|
+
|
|
3
|
+
All hooks are used inside the interactive phase (the `withInteractive` constructor function). They provide reactive state management for client-side behavior.
|
|
4
|
+
|
|
5
|
+
## createSignal
|
|
6
|
+
|
|
7
|
+
Creates a reactive getter/setter pair:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { createSignal } from '@jay-framework/component';
|
|
11
|
+
|
|
12
|
+
const [count, setCount] = createSignal(0);
|
|
13
|
+
|
|
14
|
+
// Read
|
|
15
|
+
count(); // 0
|
|
16
|
+
|
|
17
|
+
// Write
|
|
18
|
+
setCount(5); // set to 5
|
|
19
|
+
setCount((n) => n + 1); // increment
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Can initialize from a getter (reactive dependency):
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
const [label, setLabel] = createSignal(() => 'Hello ' + props.name());
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## createPatchableSignal
|
|
29
|
+
|
|
30
|
+
Creates a signal with JSON Patch support for fine-grained updates to complex objects:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { createPatchableSignal } from '@jay-framework/component';
|
|
34
|
+
import { REPLACE } from '@jay-framework/json-patch';
|
|
35
|
+
|
|
36
|
+
const [data, setData, patchData] = createPatchableSignal({
|
|
37
|
+
label: 'Hello',
|
|
38
|
+
count: 0,
|
|
39
|
+
nested: { value: 42 },
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Patch a specific field
|
|
43
|
+
patchData({ op: REPLACE, path: ['label'], value: 'Updated' });
|
|
44
|
+
|
|
45
|
+
// Patch nested field
|
|
46
|
+
patchData({ op: REPLACE, path: ['nested', 'value'], value: 99 });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
See [component-data.md](component-data.md) for more on immutable data and patching.
|
|
50
|
+
|
|
51
|
+
## createMemo
|
|
52
|
+
|
|
53
|
+
Creates a memoized computed value that recalculates only when dependencies change:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { createMemo } from '@jay-framework/component';
|
|
57
|
+
|
|
58
|
+
const fullName = createMemo(() => `${firstName()} ${lastName()}`);
|
|
59
|
+
|
|
60
|
+
// Read
|
|
61
|
+
fullName(); // recomputes only when firstName() or lastName() change
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
With initial value:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const total = createMemo((prev) => prev + latestValue(), 0);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## createEffect
|
|
71
|
+
|
|
72
|
+
Registers a side effect that runs on mount and when dependencies change. Optional cleanup function:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { createEffect } from '@jay-framework/component';
|
|
76
|
+
|
|
77
|
+
createEffect(() => {
|
|
78
|
+
const handler = () => setWindowWidth(window.innerWidth);
|
|
79
|
+
window.addEventListener('resize', handler);
|
|
80
|
+
return () => window.removeEventListener('resize', handler); // cleanup
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Effects track reactive dependencies automatically:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
createEffect(() => {
|
|
88
|
+
document.title = `${count()} items`; // reruns when count() changes
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## createDerivedArray
|
|
93
|
+
|
|
94
|
+
Efficiently maps an array with smart caching. Only remaps items that actually changed:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { createDerivedArray } from '@jay-framework/component';
|
|
98
|
+
|
|
99
|
+
const displayItems = createDerivedArray(
|
|
100
|
+
() => products(),
|
|
101
|
+
(item, index, length) => ({
|
|
102
|
+
name: item().name,
|
|
103
|
+
displayPrice: formatPrice(item().price),
|
|
104
|
+
isLast: index() === length() - 1,
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// Read the mapped array
|
|
109
|
+
displayItems();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Key optimizations:
|
|
113
|
+
|
|
114
|
+
- Reuses mapped items when the source item hasn't changed
|
|
115
|
+
- Only tracks `index()` and `length()` if you actually call them
|
|
116
|
+
- Uses object identity (not deep equality) for cache hits
|
|
117
|
+
|
|
118
|
+
## createEvent
|
|
119
|
+
|
|
120
|
+
Creates an event emitter for component-to-parent communication:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { createEvent } from '@jay-framework/component';
|
|
124
|
+
|
|
125
|
+
const onChange = createEvent<{ value: number }>((emitter) => {
|
|
126
|
+
emitter.emit({ value: count() });
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## useReactive
|
|
131
|
+
|
|
132
|
+
Gets the current reactive context for advanced use cases:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { useReactive } from '@jay-framework/component';
|
|
136
|
+
|
|
137
|
+
const reactive = useReactive();
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Most components won't need this — prefer the higher-level hooks above.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
## .jay File
|
|
4
|
+
|
|
5
|
+
Optional YAML file at project root configuring the dev server:
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
devServer:
|
|
9
|
+
portRange:
|
|
10
|
+
- 3000
|
|
11
|
+
- 3100
|
|
12
|
+
pagesBase: ./src/pages
|
|
13
|
+
componentsBase: ./src/components
|
|
14
|
+
publicFolder: ./public
|
|
15
|
+
editorServer:
|
|
16
|
+
portRange:
|
|
17
|
+
- 3101
|
|
18
|
+
- 3200
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Key Fields
|
|
22
|
+
|
|
23
|
+
- `devServer.pagesBase` — Root directory for pages (default: `./src/pages`)
|
|
24
|
+
- `devServer.componentsBase` — Root directory for components
|
|
25
|
+
- `devServer.publicFolder` — Static assets directory
|
|
26
|
+
- `devServer.portRange` — Port range for the dev server
|
|
27
|
+
|
|
28
|
+
## Plugin Configuration (config/)
|
|
29
|
+
|
|
30
|
+
The `config/` folder holds plugin configuration files generated by `jay-stack setup`:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
config/
|
|
34
|
+
├── project.conf.yaml # Project name and metadata
|
|
35
|
+
└── wix-data.yaml # Plugin-specific config (auto-generated)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Plugins create their config files here during setup. The location is configured via `configBase` in the `.jay` file (defaults to `./config`).
|
|
39
|
+
|
|
40
|
+
## Project Init (src/init.ts)
|
|
41
|
+
|
|
42
|
+
The project can have an `init.ts` for project-level initialization:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { makeJayInit, registerService } from '@jay-framework/fullstack-component';
|
|
46
|
+
|
|
47
|
+
export const init = makeJayInit()
|
|
48
|
+
.withServer(async () => {
|
|
49
|
+
// Register project-level services
|
|
50
|
+
registerService(MY_SERVICE, createMyService());
|
|
51
|
+
|
|
52
|
+
// Return data for client
|
|
53
|
+
return { locale: 'en-US' };
|
|
54
|
+
})
|
|
55
|
+
.withClient((data) => {
|
|
56
|
+
// Client-side init
|
|
57
|
+
console.log('Locale:', data.locale);
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Init runs after all plugin inits, in dependency order.
|
|
62
|
+
|
|
63
|
+
## package.json Scripts
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"scripts": {
|
|
68
|
+
"dev": "jay-stack-cli dev",
|
|
69
|
+
"validate": "jay-stack-cli validate",
|
|
70
|
+
"definitions": "jay-cli definitions src",
|
|
71
|
+
"build": "npm run definitions",
|
|
72
|
+
"setup": "jay-stack-cli setup",
|
|
73
|
+
"agent-kit": "jay-stack-cli agent-kit"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Page Components
|
|
2
|
+
|
|
3
|
+
A page component (`page.ts`) uses `makeJayStackComponent` to provide page-level data across rendering phases.
|
|
4
|
+
|
|
5
|
+
## Basic Page Component
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { makeJayStackComponent, phaseOutput } from '@jay-framework/fullstack-component';
|
|
9
|
+
import type { HomePageContract } from './page.jay-contract.generated';
|
|
10
|
+
|
|
11
|
+
export const page = makeJayStackComponent<HomePageContract>()
|
|
12
|
+
.withSlowlyRender(async () => {
|
|
13
|
+
return phaseOutput({ heroTitle: 'Welcome', heroSubtitle: 'Build something great' }, {});
|
|
14
|
+
})
|
|
15
|
+
.withFastRender(async () => {
|
|
16
|
+
return phaseOutput({ featuredCount: 12 }, {});
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The export name must be `page` for page-level components.
|
|
21
|
+
|
|
22
|
+
## Page Component with Params
|
|
23
|
+
|
|
24
|
+
For dynamic routes, use `withLoadParams` and access params via props:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
export const page = makeJayStackComponent<ProductPageContract>()
|
|
28
|
+
.withServices(PRODUCTS_DB)
|
|
29
|
+
.withLoadParams(async function* (db) {
|
|
30
|
+
const products = await db.getAll();
|
|
31
|
+
yield products.map((p) => ({ slug: p.slug }));
|
|
32
|
+
})
|
|
33
|
+
.withSlowlyRender(async (props, db) => {
|
|
34
|
+
const product = await db.getBySlug(props.slug);
|
|
35
|
+
if (!product) return notFound('Product not found');
|
|
36
|
+
return phaseOutput(
|
|
37
|
+
{ title: product.name, description: product.desc },
|
|
38
|
+
{ productId: product.id },
|
|
39
|
+
);
|
|
40
|
+
})
|
|
41
|
+
.withFastRender(async (props, db) => {
|
|
42
|
+
const price = await db.getPrice(props.carryForward.productId);
|
|
43
|
+
return phaseOutput({ price, inStock: price > 0 }, {});
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Page Component with Interactive Phase
|
|
48
|
+
|
|
49
|
+
Add client-side interactivity:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
export const page = makeJayStackComponent<ProductPageContract>()
|
|
53
|
+
.withServices(PRODUCTS_DB)
|
|
54
|
+
.withSlowlyRender(async (props, db) => {
|
|
55
|
+
// ... slow render
|
|
56
|
+
})
|
|
57
|
+
.withFastRender(async (props, db) => {
|
|
58
|
+
// ... fast render
|
|
59
|
+
})
|
|
60
|
+
.withInteractive(function ProductPage(props, refs) {
|
|
61
|
+
const [quantity, setQuantity] = createSignal(1);
|
|
62
|
+
|
|
63
|
+
refs.addToCart.onClick(async () => {
|
|
64
|
+
await addToCartAction({
|
|
65
|
+
productId: props.carryForward.productId,
|
|
66
|
+
quantity: quantity(),
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
refs.quantityInput.exec$((input) => {
|
|
71
|
+
input.addEventListener('change', (e) => {
|
|
72
|
+
setQuantity(parseInt((e.target as HTMLInputElement).value));
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
render: () => ({
|
|
78
|
+
quantity: quantity(),
|
|
79
|
+
}),
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Combining with Headless Plugins
|
|
85
|
+
|
|
86
|
+
A page component handles page-level data. Plugin headless components handle their own data independently. Both render into the same page:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
src/pages/products/[slug]/
|
|
90
|
+
├── page.jay-html # Template: binds to both page + plugin data
|
|
91
|
+
├── page.jay-contract # Page-level contract (title, breadcrumbs, etc.)
|
|
92
|
+
├── page.ts # Page component
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The jay-html template uses unprefixed bindings for page data and key-prefixed bindings for plugin data.
|
|
96
|
+
|
|
97
|
+
## Builder API Reference
|
|
98
|
+
|
|
99
|
+
See the plugin [component-structure.md](../plugin/component-structure.md) for the full builder API: `.withProps()`, `.withServices()`, `.withContexts()`, phase rendering, and render results.
|
|
100
|
+
|
|
101
|
+
## State Hooks Reference
|
|
102
|
+
|
|
103
|
+
See [component-state.md](component-state.md) for `createSignal`, `createMemo`, `createEffect`, and other hooks used in the interactive phase.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Page Contracts
|
|
2
|
+
|
|
3
|
+
A page can have its own contract (`page.jay-contract`) defining page-level data, alongside headless plugin contracts.
|
|
4
|
+
|
|
5
|
+
## When to Use a Page Contract
|
|
6
|
+
|
|
7
|
+
- The page has its own data that isn't provided by a plugin
|
|
8
|
+
- The page component (`page.ts`) renders data into ViewState
|
|
9
|
+
- You want type-safe bindings between the page component and its jay-html template
|
|
10
|
+
|
|
11
|
+
If the page only uses plugin headless components and has no page-level data, a page contract is optional.
|
|
12
|
+
|
|
13
|
+
## Basic Page Contract
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
name: home-page
|
|
17
|
+
tags:
|
|
18
|
+
- tag: heroTitle
|
|
19
|
+
type: data
|
|
20
|
+
dataType: string
|
|
21
|
+
phase: slow
|
|
22
|
+
- tag: heroSubtitle
|
|
23
|
+
type: data
|
|
24
|
+
dataType: string
|
|
25
|
+
phase: slow
|
|
26
|
+
- tag: featuredCount
|
|
27
|
+
type: data
|
|
28
|
+
dataType: number
|
|
29
|
+
phase: fast
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Place as `page.jay-contract` next to `page.jay-html`:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
src/pages/
|
|
36
|
+
├── page.jay-html
|
|
37
|
+
├── page.jay-contract
|
|
38
|
+
└── page.ts
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Page Contract with Params
|
|
42
|
+
|
|
43
|
+
If the page has a dynamic route, declare `params` in the contract:
|
|
44
|
+
|
|
45
|
+
```yaml
|
|
46
|
+
name: product-page
|
|
47
|
+
description: Product detail page
|
|
48
|
+
params:
|
|
49
|
+
slug: string
|
|
50
|
+
tags:
|
|
51
|
+
- tag: title
|
|
52
|
+
type: data
|
|
53
|
+
dataType: string
|
|
54
|
+
phase: slow
|
|
55
|
+
- tag: price
|
|
56
|
+
type: data
|
|
57
|
+
dataType: number
|
|
58
|
+
phase: fast+interactive
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The route must have matching dynamic segments:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
src/pages/products/[slug]/
|
|
65
|
+
├── page.jay-html
|
|
66
|
+
├── page.jay-contract
|
|
67
|
+
└── page.ts
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Page Contract with Props
|
|
71
|
+
|
|
72
|
+
Pages rarely use props (they're top-level), but page contracts can declare them for reusable page patterns:
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
name: category-page
|
|
76
|
+
props:
|
|
77
|
+
- name: categoryId
|
|
78
|
+
type: string
|
|
79
|
+
required: true
|
|
80
|
+
tags:
|
|
81
|
+
- tag: categoryName
|
|
82
|
+
type: data
|
|
83
|
+
dataType: string
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Combining with Plugin Contracts
|
|
87
|
+
|
|
88
|
+
A page can have both a page contract and headless plugin contracts. The page contract covers page-owned data; plugins cover their own domains:
|
|
89
|
+
|
|
90
|
+
```html
|
|
91
|
+
<!-- page.jay-html -->
|
|
92
|
+
<html>
|
|
93
|
+
<head>
|
|
94
|
+
<script
|
|
95
|
+
type="application/jay-headless"
|
|
96
|
+
plugin="wix-stores"
|
|
97
|
+
contract="product-page"
|
|
98
|
+
key="product"
|
|
99
|
+
></script>
|
|
100
|
+
</head>
|
|
101
|
+
<body>
|
|
102
|
+
<!-- Page contract data -->
|
|
103
|
+
<h1>{heroTitle}</h1>
|
|
104
|
+
|
|
105
|
+
<!-- Plugin contract data (prefixed with key) -->
|
|
106
|
+
<div>{product.name}</div>
|
|
107
|
+
<div>{product.price}</div>
|
|
108
|
+
</body>
|
|
109
|
+
</html>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Contract Format Reference
|
|
113
|
+
|
|
114
|
+
See the plugin [contracts-guide.md](../plugin/contracts-guide.md) for the full contract format: tag types, phases, sub-contracts, async data, and validation rules.
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# Project Structure
|
|
2
|
+
|
|
3
|
+
## Directory Layout
|
|
4
|
+
|
|
5
|
+
A jay-stack project follows this structure:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
my-project/
|
|
9
|
+
├── .jay # Dev server configuration (optional)
|
|
10
|
+
├── package.json # Dependencies and scripts
|
|
11
|
+
├── tsconfig.json # TypeScript config
|
|
12
|
+
├── public/ # Static assets (images, fonts, etc.)
|
|
13
|
+
├── config/ # Plugin configuration (generated by jay-stack setup)
|
|
14
|
+
│ ├── project.conf.yaml # Project metadata (name, etc.)
|
|
15
|
+
│ └── <plugin-name>.yaml # Plugin-specific config files
|
|
16
|
+
├── src/
|
|
17
|
+
│ ├── pages/ # Pages (directory-based routing)
|
|
18
|
+
│ │ ├── page.jay-html # Homepage → /
|
|
19
|
+
│ │ ├── page.jay-contract # Homepage contract (optional)
|
|
20
|
+
│ │ ├── products/
|
|
21
|
+
│ │ │ ├── page.jay-html # Products list → /products
|
|
22
|
+
│ │ │ └── [slug]/
|
|
23
|
+
│ │ │ └── page.jay-html # Product detail → /products/:slug
|
|
24
|
+
│ │ └── cart/
|
|
25
|
+
│ │ └── page.jay-html # Cart → /cart
|
|
26
|
+
│ └── styles/
|
|
27
|
+
│ └── theme.css # Global theme stylesheet
|
|
28
|
+
└── agent-kit/ # Generated by jay-stack agent-kit
|
|
29
|
+
├── INSTRUCTIONS.md
|
|
30
|
+
├── materialized-contracts/ # Contracts and indexes
|
|
31
|
+
└── references/ # Plugin reference data (product catalogs, schemas)
|
|
32
|
+
└── <plugin-name>/ # Per-plugin discovery data
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## What You Create
|
|
36
|
+
|
|
37
|
+
As an agent building pages, you typically create:
|
|
38
|
+
|
|
39
|
+
1. **`src/pages/**/\*.jay-html`\*\* — Page templates (the main output)
|
|
40
|
+
2. **`src/pages/**/\*.jay-contract`\*\* — Page-level contracts (when the page has its own data)
|
|
41
|
+
3. **`src/styles/*.css`** — Theme stylesheet (one per project, reused across pages)
|
|
42
|
+
|
|
43
|
+
You do **not** need to create: `package.json`, `tsconfig.json`, `.jay`, `page.conf.yaml`, `src/init.ts`, server actions, or services — these are set up by the project scaffolding, the Figma plugin, or provided by plugins.
|
|
44
|
+
|
|
45
|
+
## Styling
|
|
46
|
+
|
|
47
|
+
### Global Theme (src/styles/)
|
|
48
|
+
|
|
49
|
+
Each project has a theme CSS file in `src/styles/` with CSS custom properties (design tokens):
|
|
50
|
+
|
|
51
|
+
```css
|
|
52
|
+
:root {
|
|
53
|
+
/* Colors */
|
|
54
|
+
--bg-primary: #faf8f5;
|
|
55
|
+
--bg-card: #ffffff;
|
|
56
|
+
--text-primary: #2d2a26;
|
|
57
|
+
--text-secondary: #6b665e;
|
|
58
|
+
--accent: #c45c3e;
|
|
59
|
+
--accent-hover: #d4704f;
|
|
60
|
+
--border: #e8e4dd;
|
|
61
|
+
|
|
62
|
+
/* Typography */
|
|
63
|
+
--font-serif: 'Cormorant Garamond', Georgia, serif;
|
|
64
|
+
--font-sans: 'DM Sans', -apple-system, sans-serif;
|
|
65
|
+
|
|
66
|
+
/* Spacing & Layout */
|
|
67
|
+
--radius-md: 10px;
|
|
68
|
+
--radius-lg: 14px;
|
|
69
|
+
--container-max: 1400px;
|
|
70
|
+
--page-padding: 48px;
|
|
71
|
+
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04);
|
|
72
|
+
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.06);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The theme provides reusable CSS classes for common UI patterns:
|
|
77
|
+
|
|
78
|
+
- **Layout**: `.container`, `.container-narrow`
|
|
79
|
+
- **Cards**: `.card`, `.card-elevated`
|
|
80
|
+
- **Buttons**: `.btn`, `.btn-primary`, `.btn-secondary`, `.btn-ghost`, `.btn-sm`, `.btn-lg`
|
|
81
|
+
- **Forms**: `.input`, `.select`, `.checkbox`
|
|
82
|
+
- **Badges**: `.badge`, `.badge-accent`, `.badge-success`, `.badge-error`
|
|
83
|
+
- **Typography**: `.page-title`, `.section-title`, `.label`
|
|
84
|
+
- **Product components**: `.product-card`, `.product-card-image`, `.product-card-content`, `.product-card-price`
|
|
85
|
+
|
|
86
|
+
### Linking the Theme from Pages
|
|
87
|
+
|
|
88
|
+
Use a relative path from the page to the theme file:
|
|
89
|
+
|
|
90
|
+
```html
|
|
91
|
+
<!-- From src/pages/page.jay-html (depth 1) -->
|
|
92
|
+
<link rel="stylesheet" href="../styles/theme.css" />
|
|
93
|
+
|
|
94
|
+
<!-- From src/pages/products/page.jay-html (depth 2) -->
|
|
95
|
+
<link rel="stylesheet" href="../../styles/theme.css" />
|
|
96
|
+
|
|
97
|
+
<!-- From src/pages/products/[slug]/page.jay-html (depth 3) -->
|
|
98
|
+
<link rel="stylesheet" href="../../../styles/theme.css" />
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Page-Specific Styles
|
|
102
|
+
|
|
103
|
+
Add page-specific styles in `<style>` tags within the jay-html `<head>`:
|
|
104
|
+
|
|
105
|
+
```html
|
|
106
|
+
<head>
|
|
107
|
+
<link rel="stylesheet" href="../../styles/theme.css" />
|
|
108
|
+
<style>
|
|
109
|
+
.hero {
|
|
110
|
+
padding: 80px 0;
|
|
111
|
+
text-align: center;
|
|
112
|
+
}
|
|
113
|
+
.featured-grid {
|
|
114
|
+
display: grid;
|
|
115
|
+
grid-template-columns: repeat(3, 1fr);
|
|
116
|
+
gap: 24px;
|
|
117
|
+
}
|
|
118
|
+
</style>
|
|
119
|
+
</head>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### External Fonts
|
|
123
|
+
|
|
124
|
+
Import fonts in the theme CSS or via `<link>` in jay-html:
|
|
125
|
+
|
|
126
|
+
```css
|
|
127
|
+
/* In theme.css */
|
|
128
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Responsive Design
|
|
132
|
+
|
|
133
|
+
Use media queries in the theme CSS or page-specific styles:
|
|
134
|
+
|
|
135
|
+
```css
|
|
136
|
+
@media (max-width: 1024px) {
|
|
137
|
+
.product-page-layout {
|
|
138
|
+
grid-template-columns: 1fr;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
@media (max-width: 768px) {
|
|
142
|
+
.products-grid {
|
|
143
|
+
grid-template-columns: 1fr;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Configuration Files
|
|
149
|
+
|
|
150
|
+
### config/ (Plugin Configuration)
|
|
151
|
+
|
|
152
|
+
The `config/` folder holds plugin configuration files, typically generated by `jay-stack setup`:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
config/
|
|
156
|
+
├── project.conf.yaml # Project name and metadata
|
|
157
|
+
└── wix-data.yaml # Plugin-specific config (auto-generated by setup)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Plugins create their config files here during setup. The location is configured via `configBase` in the `.jay` file (defaults to `./config`).
|
|
161
|
+
|
|
162
|
+
You generally don't need to create files here manually — plugins handle this via `jay-stack setup`. However, you may need to read these files to understand how a plugin is configured.
|
|
163
|
+
|
|
164
|
+
### .jay (Dev Server Config)
|
|
165
|
+
|
|
166
|
+
Optional YAML file at project root:
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
devServer:
|
|
170
|
+
portRange:
|
|
171
|
+
- 3000
|
|
172
|
+
- 3100
|
|
173
|
+
pagesBase: ./src/pages
|
|
174
|
+
componentsBase: ./src/components
|
|
175
|
+
publicFolder: ./public
|
|
176
|
+
editorServer:
|
|
177
|
+
portRange:
|
|
178
|
+
- 3101
|
|
179
|
+
- 3200
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### package.json (Scripts)
|
|
183
|
+
|
|
184
|
+
Standard scripts for jay-stack projects:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"scripts": {
|
|
189
|
+
"dev": "jay-stack-cli dev",
|
|
190
|
+
"validate": "jay-stack-cli validate",
|
|
191
|
+
"definitions": "jay-cli definitions src",
|
|
192
|
+
"build": "npm run definitions"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Common Page Patterns
|
|
198
|
+
|
|
199
|
+
### Product List / Search Page
|
|
200
|
+
|
|
201
|
+
Uses a product search contract with filters, sorting, and pagination:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
src/pages/products/
|
|
205
|
+
└── page.jay-html # Search input, filters sidebar, product grid, load more
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Product Detail Page (Dynamic Route)
|
|
209
|
+
|
|
210
|
+
Uses a product page contract with media gallery, options, and add-to-cart:
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
src/pages/products/[slug]/
|
|
214
|
+
└── page.jay-html # Gallery, options, quantity, actions
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Category Page
|
|
218
|
+
|
|
219
|
+
Uses a category contract with product grid:
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
src/pages/categories/[slug]/
|
|
223
|
+
└── page.jay-html # Category hero, product grid, load more
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Cart Page
|
|
227
|
+
|
|
228
|
+
Uses a cart contract with line items and checkout:
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
src/pages/cart/
|
|
232
|
+
└── page.jay-html # Line items, quantity controls, summary, coupon, checkout
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Homepage
|
|
236
|
+
|
|
237
|
+
Combines multiple headless components:
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
src/pages/
|
|
241
|
+
└── page.jay-html # Hero, featured products, categories, etc.
|
|
242
|
+
```
|