create-jay 0.21.0 → 0.22.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/dist/index.js +174 -43
- package/package.json +1 -1
- package/templates/CLAUDE.md +13 -0
- package/templates/auth-callback.jay-html +56 -0
- package/templates/page.jay-html +162 -14
- package/templates/page.ts +1 -1
- package/templates/tsconfig.json +12 -1
package/dist/index.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
// lib/index.ts
|
|
4
4
|
import path2 from "node:path";
|
|
5
|
+
import fs2 from "node:fs";
|
|
5
6
|
import { execSync } from "node:child_process";
|
|
6
7
|
import chalk from "chalk";
|
|
8
|
+
import { input as input2 } from "@inquirer/prompts";
|
|
7
9
|
|
|
8
10
|
// lib/prompts.ts
|
|
9
11
|
import { input, checkbox } from "@inquirer/prompts";
|
|
@@ -97,30 +99,20 @@ var PLUGINS = [
|
|
|
97
99
|
group: "Wix",
|
|
98
100
|
checked: false,
|
|
99
101
|
isDep: true
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
name: "@jay-framework/aiditor",
|
|
103
|
-
label: "AIditor",
|
|
104
|
-
description: "Visual AI editor: point-and-shoot editing with Claude",
|
|
105
|
-
group: "AIditor",
|
|
106
|
-
checked: false,
|
|
107
|
-
isDep: true
|
|
108
102
|
}
|
|
109
103
|
];
|
|
110
|
-
var CORE_DEPS =
|
|
111
|
-
"@jay-framework/fullstack-component",
|
|
112
|
-
"@jay-framework/
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"@
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
|
|
122
|
-
"vite"
|
|
123
|
-
];
|
|
104
|
+
var CORE_DEPS = {
|
|
105
|
+
"@jay-framework/fullstack-component": "^0.21.0",
|
|
106
|
+
"@jay-framework/jay-stack-cli": "^0.21.0"
|
|
107
|
+
};
|
|
108
|
+
var CORE_DEV_DEPS = {
|
|
109
|
+
"@jay-framework/aiditor": "^0.21.0",
|
|
110
|
+
"@jay-framework/jay-cli": "^0.21.0",
|
|
111
|
+
"@types/node": "^22.10.0",
|
|
112
|
+
rimraf: "^6.0.0",
|
|
113
|
+
typescript: "~5.7.2",
|
|
114
|
+
vite: "^6.0.1"
|
|
115
|
+
};
|
|
124
116
|
|
|
125
117
|
// lib/prompts.ts
|
|
126
118
|
async function runPrompts() {
|
|
@@ -143,7 +135,7 @@ async function runPrompts() {
|
|
|
143
135
|
}
|
|
144
136
|
const choices = [];
|
|
145
137
|
for (const [group, plugins] of groups) {
|
|
146
|
-
choices.push({ name: `\u2500\u2500 ${group} \u2500\u2500`, value: "__separator__", disabled: "" });
|
|
138
|
+
choices.push({ name: `\u2500\u2500 ${group} \u2500\u2500`, value: "__separator__", disabled: " " });
|
|
147
139
|
for (const plugin of plugins) {
|
|
148
140
|
choices.push({
|
|
149
141
|
name: `${plugin.label} \u2014 ${plugin.description}`,
|
|
@@ -154,7 +146,8 @@ async function runPrompts() {
|
|
|
154
146
|
}
|
|
155
147
|
const selected = await checkbox({
|
|
156
148
|
message: "Select plugins to install:",
|
|
157
|
-
choices
|
|
149
|
+
choices,
|
|
150
|
+
pageSize: process.stdout.rows ? process.stdout.rows - 5 : 20
|
|
158
151
|
});
|
|
159
152
|
const selectedPlugins = PLUGINS.filter((p) => selected.includes(p.name));
|
|
160
153
|
return { name: name.trim(), selectedPlugins };
|
|
@@ -180,35 +173,45 @@ function writeFile(projectDir, relativePath, content) {
|
|
|
180
173
|
fs.writeFileSync(fullPath, content, "utf-8");
|
|
181
174
|
}
|
|
182
175
|
function generatePackageJson(name, selectedPlugins) {
|
|
183
|
-
const deps = {};
|
|
184
|
-
|
|
185
|
-
deps[dep] = "latest";
|
|
186
|
-
}
|
|
176
|
+
const deps = { ...CORE_DEPS };
|
|
177
|
+
const hasWixPlugins2 = selectedPlugins.some((p) => p.name.startsWith("@jay-framework/wix-"));
|
|
187
178
|
for (const plugin of selectedPlugins) {
|
|
188
179
|
if (plugin.isDep)
|
|
189
180
|
deps[plugin.name] = "latest";
|
|
190
181
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
devDeps[dep] = "latest";
|
|
182
|
+
if (hasWixPlugins2 && !deps["@jay-framework/wix-server-client"]) {
|
|
183
|
+
deps["@jay-framework/wix-server-client"] = "latest";
|
|
194
184
|
}
|
|
185
|
+
const devDeps = { ...CORE_DEV_DEPS };
|
|
195
186
|
for (const plugin of selectedPlugins) {
|
|
196
187
|
if (!plugin.isDep)
|
|
197
188
|
devDeps[plugin.name] = "latest";
|
|
198
189
|
}
|
|
190
|
+
const hasWixDeploy = selectedPlugins.some((p) => p.name === "@jay-framework/wix-deploy");
|
|
191
|
+
const hasAiditor = true;
|
|
199
192
|
const pkg = {
|
|
200
193
|
name,
|
|
201
194
|
version: "0.1.0",
|
|
202
195
|
type: "module",
|
|
203
196
|
private: true,
|
|
204
197
|
scripts: {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
validate: "jay-stack validate",
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
198
|
+
setup: "jay-stack-cli setup",
|
|
199
|
+
dev: "jay-stack-cli dev",
|
|
200
|
+
"agent-kit": "jay-stack-cli agent-kit",
|
|
201
|
+
validate: "jay-stack-cli validate",
|
|
202
|
+
clean: "rimraf dist && rimraf build && rimraf -g 'src/**/*.d.ts'",
|
|
203
|
+
definitions: "jay-cli definitions src",
|
|
204
|
+
build: "npm run agent-kit && npm run definitions",
|
|
205
|
+
"build:production": "npm run build && jay-stack-cli build",
|
|
206
|
+
"build:check-types": "tsc",
|
|
207
|
+
serve: "jay-stack-cli serve",
|
|
208
|
+
...hasWixDeploy && {
|
|
209
|
+
"wix:deploy": "jay-stack-cli run wix-deploy/deploy --exclude-plugins aiditor",
|
|
210
|
+
"wix:serve": "node serve.mjs"
|
|
211
|
+
},
|
|
212
|
+
...hasAiditor && {
|
|
213
|
+
"aiditor:publish": hasWixDeploy ? "npm run build:production && npm run wix:deploy" : "npm run build:production && echo 'add your deploy command here'"
|
|
214
|
+
}
|
|
212
215
|
},
|
|
213
216
|
dependencies: sortKeys(deps),
|
|
214
217
|
devDependencies: sortKeys(devDeps)
|
|
@@ -225,13 +228,18 @@ function sortKeys(obj) {
|
|
|
225
228
|
function scaffoldProject(projectDir, name, selectedPlugins) {
|
|
226
229
|
fs.mkdirSync(projectDir, { recursive: true });
|
|
227
230
|
writeFile(projectDir, "package.json", generatePackageJson(name, selectedPlugins));
|
|
228
|
-
writeFile(
|
|
231
|
+
writeFile(
|
|
232
|
+
projectDir,
|
|
233
|
+
"src/pages/page.jay-html",
|
|
234
|
+
readTemplate("page.jay-html").replace(/\{\{name\}\}/g, name)
|
|
235
|
+
);
|
|
229
236
|
writeFile(projectDir, "src/pages/page.ts", readTemplate("page.ts"));
|
|
230
237
|
writeFile(projectDir, "src/pages/page.jay-contract", readTemplate("page.jay-contract"));
|
|
231
238
|
writeFile(projectDir, "src/styles/theme.css", readTemplate("theme.css"));
|
|
232
239
|
writeFile(projectDir, ".jay", readTemplate("jay-config.yaml"));
|
|
233
240
|
writeFile(projectDir, "vite.config.ts", readTemplate("vite.config.ts"));
|
|
234
241
|
writeFile(projectDir, "tsconfig.json", readTemplate("tsconfig.json"));
|
|
242
|
+
writeFile(projectDir, "CLAUDE.md", readTemplate("CLAUDE.md"));
|
|
235
243
|
const jaySkillPath = path.join(templatesDir(), "jay-skill.md");
|
|
236
244
|
if (fs.existsSync(jaySkillPath)) {
|
|
237
245
|
writeFile(projectDir, "jay-skill.md", fs.readFileSync(jaySkillPath, "utf-8"));
|
|
@@ -242,6 +250,14 @@ function scaffoldProject(projectDir, name, selectedPlugins) {
|
|
|
242
250
|
if (hasDesignValidator) {
|
|
243
251
|
writeFile(projectDir, "DESIGN.md", readTemplate("DESIGN.md"));
|
|
244
252
|
}
|
|
253
|
+
const hasWixMembers = selectedPlugins.some((p) => p.name === "@jay-framework/wix-members");
|
|
254
|
+
if (hasWixMembers) {
|
|
255
|
+
writeFile(
|
|
256
|
+
projectDir,
|
|
257
|
+
"src/pages/auth/callback/page.jay-html",
|
|
258
|
+
readTemplate("auth-callback.jay-html")
|
|
259
|
+
);
|
|
260
|
+
}
|
|
245
261
|
}
|
|
246
262
|
|
|
247
263
|
// lib/index.ts
|
|
@@ -254,6 +270,112 @@ function detectPackageManager() {
|
|
|
254
270
|
function run(cmd, cwd) {
|
|
255
271
|
execSync(cmd, { cwd, stdio: "inherit" });
|
|
256
272
|
}
|
|
273
|
+
function hasWixPlugins(selectedPlugins) {
|
|
274
|
+
return selectedPlugins.some((p) => p.name.startsWith("@jay-framework/wix-"));
|
|
275
|
+
}
|
|
276
|
+
async function promptWixApiKey() {
|
|
277
|
+
console.log("");
|
|
278
|
+
console.log(chalk.bold(" Wix Setup"));
|
|
279
|
+
console.log("");
|
|
280
|
+
console.log(chalk.dim(" Wix plugins require API credentials. Create an API key at:"));
|
|
281
|
+
console.log(chalk.cyan(" https://manage.wix.com/account/api-keys"));
|
|
282
|
+
console.log("");
|
|
283
|
+
return input2({
|
|
284
|
+
message: "Wix API Key:",
|
|
285
|
+
validate: (v) => v.trim() ? true : "API key is required"
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
async function setupWix(projectDir, apiKey) {
|
|
289
|
+
let loggedIn = false;
|
|
290
|
+
try {
|
|
291
|
+
execSync("npx @wix/cli whoami", { cwd: projectDir, stdio: "pipe" });
|
|
292
|
+
loggedIn = true;
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
295
|
+
if (!loggedIn) {
|
|
296
|
+
console.log(chalk.dim(" You need to log in to Wix to connect your site."));
|
|
297
|
+
try {
|
|
298
|
+
run("npx @wix/cli login", projectDir);
|
|
299
|
+
} catch {
|
|
300
|
+
console.log(chalk.yellow(" \u26A0 Wix login failed \u2014 run `npx @wix/cli login` manually"));
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
console.log(chalk.dim(" Connecting to your Wix site..."));
|
|
305
|
+
try {
|
|
306
|
+
run("npm create @wix/new@latest init", projectDir);
|
|
307
|
+
} catch {
|
|
308
|
+
console.log(chalk.yellow(" \u26A0 Wix site init failed \u2014 you can run it manually later"));
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const wixConfigPath = path2.join(projectDir, "wix.config.json");
|
|
312
|
+
if (!fs2.existsSync(wixConfigPath)) {
|
|
313
|
+
console.log(chalk.yellow(" \u26A0 wix.config.json not created \u2014 skipping credential setup"));
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
let siteId = "";
|
|
317
|
+
let appId = "";
|
|
318
|
+
try {
|
|
319
|
+
const wixConfig = JSON.parse(fs2.readFileSync(wixConfigPath, "utf-8"));
|
|
320
|
+
siteId = wixConfig.siteId || "";
|
|
321
|
+
appId = wixConfig.appId || "";
|
|
322
|
+
} catch {
|
|
323
|
+
console.log(chalk.yellow(" \u26A0 Could not parse wix.config.json"));
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (!siteId || !appId) {
|
|
327
|
+
console.log(chalk.yellow(" \u26A0 Missing siteId or appId in wix.config.json"));
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const configDir = path2.join(projectDir, "config");
|
|
331
|
+
fs2.mkdirSync(configDir, { recursive: true });
|
|
332
|
+
fs2.writeFileSync(
|
|
333
|
+
path2.join(configDir, ".wix.yaml"),
|
|
334
|
+
`# Wix API Configuration
|
|
335
|
+
#
|
|
336
|
+
# This file contains credentials for connecting to your Wix site.
|
|
337
|
+
# Get these values from your Wix dashboard:
|
|
338
|
+
# - API Key: https://dev.wix.com/docs/rest/articles/getting-started/api-keys
|
|
339
|
+
# - Site ID: Found in your Wix dashboard URL or site settings
|
|
340
|
+
# - OAuth Client ID: Create an OAuth app in Wix Developers dashboard
|
|
341
|
+
#
|
|
342
|
+
# IMPORTANT: This file contains secrets. Add config/.wix.yaml to .gitignore.
|
|
343
|
+
|
|
344
|
+
# Server-side authentication (required)
|
|
345
|
+
apiKeyStrategy:
|
|
346
|
+
apiKey: "${apiKey.trim()}"
|
|
347
|
+
siteId: "${siteId}"
|
|
348
|
+
|
|
349
|
+
# Client-side authentication (required for interactive features)
|
|
350
|
+
oauthStrategy:
|
|
351
|
+
clientId: "${appId}"
|
|
352
|
+
`,
|
|
353
|
+
"utf-8"
|
|
354
|
+
);
|
|
355
|
+
const gitignorePath = path2.join(projectDir, ".gitignore");
|
|
356
|
+
const gitignoreEntries = ["config/.wix.yaml", "wix.config.json"];
|
|
357
|
+
if (fs2.existsSync(gitignorePath)) {
|
|
358
|
+
let content = fs2.readFileSync(gitignorePath, "utf-8");
|
|
359
|
+
for (const entry of gitignoreEntries) {
|
|
360
|
+
if (!content.includes(entry)) {
|
|
361
|
+
content += `
|
|
362
|
+
${entry}`;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
fs2.writeFileSync(gitignorePath, content.trimEnd() + "\n", "utf-8");
|
|
366
|
+
} else {
|
|
367
|
+
fs2.writeFileSync(
|
|
368
|
+
gitignorePath,
|
|
369
|
+
`node_modules
|
|
370
|
+
dist
|
|
371
|
+
build
|
|
372
|
+
${gitignoreEntries.join("\n")}
|
|
373
|
+
`,
|
|
374
|
+
"utf-8"
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
console.log(chalk.green(" \u2713 Wix credentials configured"));
|
|
378
|
+
}
|
|
257
379
|
async function main() {
|
|
258
380
|
const pm = detectPackageManager();
|
|
259
381
|
console.log("");
|
|
@@ -263,24 +385,33 @@ async function main() {
|
|
|
263
385
|
const projectDir = path2.resolve(process.cwd(), config.name);
|
|
264
386
|
console.log("");
|
|
265
387
|
console.log(`Creating project in ${chalk.cyan(`./${config.name}`)}...`);
|
|
388
|
+
const wixSelected = hasWixPlugins(config.selectedPlugins);
|
|
389
|
+
const wixApiKey = wixSelected ? await promptWixApiKey() : "";
|
|
266
390
|
scaffoldProject(projectDir, config.name, config.selectedPlugins);
|
|
267
391
|
console.log(chalk.green(" \u2713 Created project structure"));
|
|
268
392
|
console.log(chalk.dim(" Installing dependencies..."));
|
|
269
393
|
run(pm === "yarn" ? "yarn install" : "npm install", projectDir);
|
|
270
394
|
console.log(chalk.green(" \u2713 Installed dependencies"));
|
|
395
|
+
if (wixSelected) {
|
|
396
|
+
await setupWix(projectDir, wixApiKey);
|
|
397
|
+
}
|
|
271
398
|
console.log(chalk.dim(" Generating agent-kit..."));
|
|
272
399
|
try {
|
|
273
|
-
run("npx jay-stack agent-kit", projectDir);
|
|
400
|
+
run("npx jay-stack-cli agent-kit", projectDir);
|
|
274
401
|
console.log(chalk.green(" \u2713 Generated agent-kit"));
|
|
275
402
|
} catch {
|
|
276
|
-
console.log(
|
|
403
|
+
console.log(
|
|
404
|
+
chalk.yellow(
|
|
405
|
+
" \u26A0 Agent-kit generation skipped (can run later with: npm run agent-kit)"
|
|
406
|
+
)
|
|
407
|
+
);
|
|
277
408
|
}
|
|
278
409
|
console.log(chalk.dim(" Running plugin setup..."));
|
|
279
410
|
try {
|
|
280
|
-
run("npx jay-stack setup", projectDir);
|
|
411
|
+
run("npx jay-stack-cli setup", projectDir);
|
|
281
412
|
console.log(chalk.green(" \u2713 Plugin setup complete"));
|
|
282
413
|
} catch {
|
|
283
|
-
console.log(chalk.yellow(" \u26A0 Setup incomplete (can run later with:
|
|
414
|
+
console.log(chalk.yellow(" \u26A0 Setup incomplete (can run later with: npm run setup)"));
|
|
284
415
|
}
|
|
285
416
|
const runCmd = pm === "yarn" ? "yarn dev" : "npm run dev";
|
|
286
417
|
console.log("");
|
package/package.json
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This is a Jay Stack project.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
Run `yarn setup` then `yarn agent-kit` to generate the agent kit.
|
|
8
|
+
|
|
9
|
+
Read `agent-kit/` for contracts, plugin references, and role guides. See the `/jay` skill for full details on the agent kit structure, CLI commands, and project organization.
|
|
10
|
+
|
|
11
|
+
Before making changes, read `agent-kit/plugins-index.yaml` and the relevant role guide (`agent-kit/designer/`, `agent-kit/developer/`, etc.).
|
|
12
|
+
|
|
13
|
+
Run `yarn validate` after changes.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head>
|
|
3
|
+
<title>Signing in...</title>
|
|
4
|
+
<meta name="description" content="Authentication callback — please wait while we sign you in.">
|
|
5
|
+
<script type="application/jay-headless" plugin="@jay-framework/wix-members" contract="auth-callback"></script>
|
|
6
|
+
<script type="application/jay-data">
|
|
7
|
+
data:
|
|
8
|
+
</script>
|
|
9
|
+
<link rel="stylesheet" href="../../../styles/theme.css" />
|
|
10
|
+
<style>
|
|
11
|
+
.auth-callback {
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
min-height: 100vh;
|
|
16
|
+
padding: 2rem;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.auth-callback-content {
|
|
20
|
+
text-align: center;
|
|
21
|
+
max-width: 400px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.auth-callback-content h1 {
|
|
25
|
+
font-size: 1.5rem;
|
|
26
|
+
font-weight: 600;
|
|
27
|
+
margin-bottom: 0.75rem;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.auth-callback-content p {
|
|
31
|
+
color: var(--color-text-muted);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.auth-error {
|
|
35
|
+
color: #dc2626;
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
38
|
+
</head>
|
|
39
|
+
<body>
|
|
40
|
+
<jay:auth-callback>
|
|
41
|
+
<main class="auth-callback">
|
|
42
|
+
<div class="auth-callback-content">
|
|
43
|
+
<div if="isProcessing">
|
|
44
|
+
<h1>Signing you in...</h1>
|
|
45
|
+
<p>Please wait while we complete your login.</p>
|
|
46
|
+
</div>
|
|
47
|
+
<div if="hasError">
|
|
48
|
+
<h1>Something went wrong</h1>
|
|
49
|
+
<p class="auth-error">{errorMessage}</p>
|
|
50
|
+
<p><a href="/">Return to homepage</a></p>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</main>
|
|
54
|
+
</jay:auth-callback>
|
|
55
|
+
</body>
|
|
56
|
+
</html>
|
package/templates/page.jay-html
CHANGED
|
@@ -1,35 +1,183 @@
|
|
|
1
1
|
<html lang="en">
|
|
2
2
|
<head>
|
|
3
|
-
<title>{{name}}</title>
|
|
4
|
-
<meta name="description" content="
|
|
3
|
+
<title>Welcome to {{name}}</title>
|
|
4
|
+
<meta name="description" content="Your Jay Stack project is ready — edit pages with the AIditor, a coding agent, or by hand.">
|
|
5
5
|
<script type="application/jay-data" contract="./page.jay-contract"></script>
|
|
6
|
+
<script
|
|
7
|
+
type="application/jay-headless"
|
|
8
|
+
plugin="@jay-framework/ui-kit"
|
|
9
|
+
contract="letter-split"
|
|
10
|
+
></script>
|
|
6
11
|
<link rel="stylesheet" href="../styles/theme.css" />
|
|
7
12
|
<style>
|
|
13
|
+
/* ── Hero ── */
|
|
8
14
|
.hero {
|
|
9
15
|
display: flex;
|
|
10
16
|
flex-direction: column;
|
|
11
17
|
align-items: center;
|
|
12
18
|
justify-content: center;
|
|
13
|
-
min-height:
|
|
19
|
+
min-height: 50vh;
|
|
14
20
|
text-align: center;
|
|
15
|
-
padding: 2rem;
|
|
21
|
+
padding: 3rem 2rem 2rem;
|
|
16
22
|
}
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
|
|
24
|
+
.hero-title {
|
|
25
|
+
font-size: clamp(2.2rem, 5vw, 3.5rem);
|
|
26
|
+
font-weight: 800;
|
|
27
|
+
margin-bottom: 1.5rem;
|
|
28
|
+
line-height: 1.15;
|
|
29
|
+
white-space: pre;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Letter-split wave animation */
|
|
33
|
+
.letter {
|
|
34
|
+
display: inline-block;
|
|
35
|
+
animation: wave 2.2s ease-in-out infinite;
|
|
36
|
+
animation-delay: calc(var(--i) * 0.07s);
|
|
37
|
+
}
|
|
38
|
+
@keyframes wave {
|
|
39
|
+
0%, 100% { transform: translateY(0); }
|
|
40
|
+
30% { transform: translateY(-0.25em); }
|
|
41
|
+
50% { transform: translateY(0); }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.hero-subtitle {
|
|
45
|
+
font-size: 1.2rem;
|
|
46
|
+
color: var(--color-text-muted);
|
|
47
|
+
max-width: 520px;
|
|
48
|
+
margin-bottom: 2rem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* ── CTA button ── */
|
|
52
|
+
.cta-btn {
|
|
53
|
+
display: inline-flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
gap: 0.5rem;
|
|
56
|
+
padding: 0.85rem 2rem;
|
|
57
|
+
font-size: 1.1rem;
|
|
58
|
+
font-weight: 600;
|
|
59
|
+
color: #fff;
|
|
60
|
+
background: var(--color-primary);
|
|
61
|
+
border: none;
|
|
62
|
+
border-radius: 0.6rem;
|
|
63
|
+
text-decoration: none;
|
|
64
|
+
cursor: pointer;
|
|
65
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
66
|
+
}
|
|
67
|
+
.cta-btn:hover {
|
|
68
|
+
transform: translateY(-2px);
|
|
69
|
+
box-shadow: 0 6px 20px rgba(37, 99, 235, 0.35);
|
|
70
|
+
text-decoration: none;
|
|
71
|
+
}
|
|
72
|
+
.cta-btn:active {
|
|
73
|
+
transform: translateY(0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ── Steps section ── */
|
|
77
|
+
.steps {
|
|
78
|
+
display: flex;
|
|
79
|
+
flex-direction: column;
|
|
80
|
+
gap: 1.5rem;
|
|
81
|
+
max-width: 640px;
|
|
82
|
+
margin: 0 auto;
|
|
83
|
+
padding: 2rem 2rem 4rem;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.step {
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: flex-start;
|
|
89
|
+
gap: 1rem;
|
|
90
|
+
padding: 1.25rem 1.5rem;
|
|
91
|
+
background: var(--color-surface);
|
|
92
|
+
border: 1px solid var(--color-border);
|
|
93
|
+
border-radius: 0.75rem;
|
|
94
|
+
opacity: 0;
|
|
95
|
+
transform: translateY(12px);
|
|
96
|
+
animation: fadeUp 0.5s ease forwards;
|
|
97
|
+
}
|
|
98
|
+
.step:nth-child(1) { animation-delay: 0.2s; }
|
|
99
|
+
.step:nth-child(2) { animation-delay: 0.4s; }
|
|
100
|
+
.step:nth-child(3) { animation-delay: 0.6s; }
|
|
101
|
+
|
|
102
|
+
@keyframes fadeUp {
|
|
103
|
+
to {
|
|
104
|
+
opacity: 1;
|
|
105
|
+
transform: translateY(0);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.step-number {
|
|
110
|
+
flex-shrink: 0;
|
|
111
|
+
width: 2rem;
|
|
112
|
+
height: 2rem;
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
justify-content: center;
|
|
19
116
|
font-weight: 700;
|
|
20
|
-
|
|
117
|
+
font-size: 0.95rem;
|
|
118
|
+
color: var(--color-primary);
|
|
119
|
+
background: rgba(37, 99, 235, 0.1);
|
|
120
|
+
border-radius: 50%;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.step-body h2 {
|
|
124
|
+
font-size: 1rem;
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
margin-bottom: 0.2rem;
|
|
127
|
+
}
|
|
128
|
+
.step-body p {
|
|
129
|
+
font-size: 0.9rem;
|
|
130
|
+
color: var(--color-text-muted);
|
|
131
|
+
line-height: 1.5;
|
|
21
132
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
133
|
+
|
|
134
|
+
.step-body code {
|
|
135
|
+
font-size: 0.85rem;
|
|
136
|
+
background: var(--color-border);
|
|
137
|
+
padding: 0.1em 0.4em;
|
|
138
|
+
border-radius: 0.3rem;
|
|
26
139
|
}
|
|
27
140
|
</style>
|
|
28
141
|
</head>
|
|
29
142
|
<body>
|
|
30
|
-
<main
|
|
31
|
-
<
|
|
32
|
-
|
|
143
|
+
<main>
|
|
144
|
+
<section class="hero">
|
|
145
|
+
<jay:letter-split text="{title}">
|
|
146
|
+
<h1 class="hero-title" aria-label="Welcome to {{name}}">
|
|
147
|
+
<span forEach="letters" trackBy="index" class="letter" aria-hidden="true" style="--i: {index}">{text}</span>
|
|
148
|
+
</h1>
|
|
149
|
+
</jay:letter-split>
|
|
150
|
+
|
|
151
|
+
<p class="hero-subtitle">{description}</p>
|
|
152
|
+
|
|
153
|
+
<a class="cta-btn" href="/aiditor">Open the AIditor ➔</a>
|
|
154
|
+
</section>
|
|
155
|
+
|
|
156
|
+
<section class="steps">
|
|
157
|
+
<div class="step">
|
|
158
|
+
<span class="step-number">1</span>
|
|
159
|
+
<div class="step-body">
|
|
160
|
+
<h2>Use the AIditor</h2>
|
|
161
|
+
<p>Open the visual editor at <a href="/aiditor">/aiditor</a> to design and tweak pages with AI assistance.</p>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div class="step">
|
|
166
|
+
<span class="step-number">2</span>
|
|
167
|
+
<div class="step-body">
|
|
168
|
+
<h2>Use any coding agent</h2>
|
|
169
|
+
<p>Work with your favorite AI coding tool — Cursor, Claude Code, or any agent that can edit files.</p>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<div class="step">
|
|
174
|
+
<span class="step-number">3</span>
|
|
175
|
+
<div class="step-body">
|
|
176
|
+
<h2>Edit the source directly</h2>
|
|
177
|
+
<p>Pages live in <code>src/pages/</code> — open the jay-html files in any editor and start building.</p>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</section>
|
|
33
181
|
</main>
|
|
34
182
|
</body>
|
|
35
183
|
</html>
|
package/templates/page.ts
CHANGED
|
@@ -5,7 +5,7 @@ export const page = makeJayStackComponent<PageContract>().withSlowlyRender(async
|
|
|
5
5
|
return phaseOutput(
|
|
6
6
|
{
|
|
7
7
|
title: 'Welcome to Jay',
|
|
8
|
-
description: 'Your project is ready
|
|
8
|
+
description: 'Your project is ready — here are three ways to start building.',
|
|
9
9
|
},
|
|
10
10
|
{},
|
|
11
11
|
);
|
package/templates/tsconfig.json
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"extends": "@jay-framework/dev-environment/examples/tsconfig.json",
|
|
3
2
|
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"lib": ["ES2022", "DOM"],
|
|
6
|
+
"moduleResolution": "node",
|
|
7
|
+
"allowSyntheticDefaultImports": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"useDefineForClassFields": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"noEmit": true,
|
|
4
14
|
"outDir": "dist",
|
|
15
|
+
"types": [],
|
|
5
16
|
},
|
|
6
17
|
"include": ["src"],
|
|
7
18
|
}
|