create-craft 0.0.37
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/LICENSE.md +21 -0
- package/README.md +0 -0
- package/bin/cli.ts +718 -0
- package/package.json +50 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Stacks.js
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
Binary file
|
package/bin/cli.ts
ADDED
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* create-craft - Scaffold a new Craft desktop app project
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { CLI } from '@stacksjs/clapp'
|
|
8
|
+
import { spawn } from 'node:child_process'
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
10
|
+
import { join, resolve } from 'node:path'
|
|
11
|
+
import process from 'node:process'
|
|
12
|
+
|
|
13
|
+
// Read the current version from the package's own package.json (which stays in sync via bumpx --recursive)
|
|
14
|
+
const craftVersion = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8')).version
|
|
15
|
+
|
|
16
|
+
const cli = new CLI('create-craft')
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Validate a project name against npm's package-name rules so the generated
|
|
20
|
+
* package.json is always parseable by npm/bun and free of shell metacharacters
|
|
21
|
+
* that could leak into commands like `bun install <name>`.
|
|
22
|
+
*
|
|
23
|
+
* Rules (subset of validate-npm-package-name):
|
|
24
|
+
* - 1..214 characters
|
|
25
|
+
* - lowercase letters, digits, and `-`, `_`, `.`
|
|
26
|
+
* - must start with letter or digit (no leading dot/underscore)
|
|
27
|
+
* - no whitespace, slashes, or path separators
|
|
28
|
+
*/
|
|
29
|
+
function validateProjectName(name: string): void {
|
|
30
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
31
|
+
throw new Error('Project name must be a non-empty string')
|
|
32
|
+
}
|
|
33
|
+
if (name.length > 214) {
|
|
34
|
+
throw new Error('Project name must be 214 characters or fewer')
|
|
35
|
+
}
|
|
36
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/.test(name)) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Invalid project name "${name}". `
|
|
39
|
+
+ 'Use lowercase letters, digits, dot, underscore, and dash only; must start with a letter or digit.',
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Default command - create a new project
|
|
45
|
+
cli
|
|
46
|
+
.command('[project-name]', 'Create a new Craft desktop app')
|
|
47
|
+
.option('--template <template>', 'Template to use', { default: 'minimal' })
|
|
48
|
+
.option('--skip-install', 'Skip installing dependencies', { default: false })
|
|
49
|
+
.example('create-craft my-app')
|
|
50
|
+
.example('create-craft my-app --template full-featured')
|
|
51
|
+
.example('bun create craft my-app')
|
|
52
|
+
.action(async (projectName?: string, options?: any) => {
|
|
53
|
+
if (!projectName) {
|
|
54
|
+
console.error('Error: Project name is required')
|
|
55
|
+
console.log('\nUsage: create-craft <project-name>')
|
|
56
|
+
console.log('Example: create-craft my-app')
|
|
57
|
+
process.exit(1)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
validateProjectName(projectName)
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
console.error(`Error: ${(e as Error).message}`)
|
|
65
|
+
process.exit(2)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const template = options?.template || 'minimal'
|
|
69
|
+
const skipInstall = options?.skipInstall || false
|
|
70
|
+
|
|
71
|
+
console.log(`\n✨ Creating a new Craft app: ${projectName}`)
|
|
72
|
+
console.log(`📦 Template: ${template}\n`)
|
|
73
|
+
|
|
74
|
+
const projectPath = resolve(process.cwd(), projectName)
|
|
75
|
+
|
|
76
|
+
// Check if directory already exists
|
|
77
|
+
if (existsSync(projectPath)) {
|
|
78
|
+
console.error(`Error: Directory "${projectName}" already exists`)
|
|
79
|
+
process.exit(1)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Create project directory
|
|
83
|
+
mkdirSync(projectPath, { recursive: true })
|
|
84
|
+
|
|
85
|
+
// Generate project based on template
|
|
86
|
+
if (template === 'minimal') {
|
|
87
|
+
createMinimalTemplate(projectPath, projectName)
|
|
88
|
+
}
|
|
89
|
+
else if (template === 'full-featured') {
|
|
90
|
+
createFullFeaturedTemplate(projectPath, projectName)
|
|
91
|
+
}
|
|
92
|
+
else if (template === 'todo-app') {
|
|
93
|
+
createTodoAppTemplate(projectPath, projectName)
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
console.error(`Error: Unknown template "${template}"`)
|
|
97
|
+
console.log('\nAvailable templates: minimal, full-featured, todo-app')
|
|
98
|
+
process.exit(1)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Install dependencies unless skipped
|
|
102
|
+
if (!skipInstall) {
|
|
103
|
+
console.log('\n📦 Installing dependencies...\n')
|
|
104
|
+
await installDependencies(projectPath)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Success message
|
|
108
|
+
console.log('\n✅ Project created successfully!\n')
|
|
109
|
+
console.log('Next steps:')
|
|
110
|
+
console.log(` cd ${projectName}`)
|
|
111
|
+
if (skipInstall) {
|
|
112
|
+
console.log(' bun install')
|
|
113
|
+
}
|
|
114
|
+
console.log(' bun run dev\n')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
// List available templates
|
|
118
|
+
cli
|
|
119
|
+
.command('list', 'List available templates')
|
|
120
|
+
.action(() => {
|
|
121
|
+
console.log('\n📋 Available templates:\n')
|
|
122
|
+
console.log(' minimal - Simplest possible Craft app')
|
|
123
|
+
console.log(' full-featured - Modern styled app with examples')
|
|
124
|
+
console.log(' todo-app - Interactive todo list application\n')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
cli.version(craftVersion)
|
|
128
|
+
cli.help()
|
|
129
|
+
cli.parse()
|
|
130
|
+
|
|
131
|
+
// Template generators
|
|
132
|
+
|
|
133
|
+
function createMinimalTemplate(projectPath: string, projectName: string): void {
|
|
134
|
+
console.log('📝 Generating minimal template...')
|
|
135
|
+
|
|
136
|
+
// Create package.json
|
|
137
|
+
const packageJson = {
|
|
138
|
+
name: projectName,
|
|
139
|
+
version: '0.0.1',
|
|
140
|
+
type: 'module',
|
|
141
|
+
private: true,
|
|
142
|
+
scripts: {
|
|
143
|
+
doctor: 'craft --version',
|
|
144
|
+
dev: 'bun run src/index.ts',
|
|
145
|
+
build: 'bun build src/index.ts --outdir dist --target bun',
|
|
146
|
+
},
|
|
147
|
+
dependencies: {
|
|
148
|
+
'craft-native': `^${craftVersion}`,
|
|
149
|
+
|
|
150
|
+
},
|
|
151
|
+
devDependencies: {
|
|
152
|
+
'@types/bun': 'latest',
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
writeFileSync(
|
|
157
|
+
join(projectPath, 'package.json'),
|
|
158
|
+
JSON.stringify(packageJson, null, 2),
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
// Create src directory
|
|
162
|
+
mkdirSync(join(projectPath, 'src'))
|
|
163
|
+
|
|
164
|
+
// Create src/index.ts
|
|
165
|
+
const indexTs = `import { show } from 'craft-native'
|
|
166
|
+
|
|
167
|
+
const html = \`
|
|
168
|
+
<!DOCTYPE html>
|
|
169
|
+
<html>
|
|
170
|
+
<head>
|
|
171
|
+
<meta charset="UTF-8">
|
|
172
|
+
<style>
|
|
173
|
+
body {
|
|
174
|
+
margin: 0;
|
|
175
|
+
height: 100vh;
|
|
176
|
+
display: flex;
|
|
177
|
+
justify-content: center;
|
|
178
|
+
align-items: center;
|
|
179
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
180
|
+
color: white;
|
|
181
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
182
|
+
}
|
|
183
|
+
h1 {
|
|
184
|
+
font-size: 3rem;
|
|
185
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
|
186
|
+
}
|
|
187
|
+
</style>
|
|
188
|
+
</head>
|
|
189
|
+
<body>
|
|
190
|
+
<h1>⚡ Hello from ${projectName}!</h1>
|
|
191
|
+
</body>
|
|
192
|
+
</html>
|
|
193
|
+
\`
|
|
194
|
+
|
|
195
|
+
await show(html, {
|
|
196
|
+
title: '${projectName}',
|
|
197
|
+
width: 600,
|
|
198
|
+
height: 400,
|
|
199
|
+
})
|
|
200
|
+
`
|
|
201
|
+
|
|
202
|
+
writeFileSync(join(projectPath, 'src/index.ts'), indexTs)
|
|
203
|
+
|
|
204
|
+
// Create README.md
|
|
205
|
+
const readme = `# ${projectName}
|
|
206
|
+
|
|
207
|
+
A minimal Craft desktop application.
|
|
208
|
+
|
|
209
|
+
## Getting Started
|
|
210
|
+
|
|
211
|
+
\`\`\`bash
|
|
212
|
+
bun install
|
|
213
|
+
bun run doctor
|
|
214
|
+
bun run dev
|
|
215
|
+
\`\`\`
|
|
216
|
+
|
|
217
|
+
\`bun run doctor\` verifies that the native \`craft\` binary is available on your PATH. Install it with \`pantry install craft\` if the command is missing.
|
|
218
|
+
|
|
219
|
+
## Build
|
|
220
|
+
|
|
221
|
+
\`\`\`bash
|
|
222
|
+
bun run build
|
|
223
|
+
\`\`\`
|
|
224
|
+
|
|
225
|
+
## Learn More
|
|
226
|
+
|
|
227
|
+
- [Craft Documentation](https://github.com/home-lang/craft)
|
|
228
|
+
- [TypeScript SDK](https://github.com/home-lang/craft/tree/main/packages/typescript)
|
|
229
|
+
`
|
|
230
|
+
|
|
231
|
+
writeFileSync(join(projectPath, 'README.md'), readme)
|
|
232
|
+
|
|
233
|
+
// Create .gitignore
|
|
234
|
+
const gitignore = `node_modules
|
|
235
|
+
dist
|
|
236
|
+
zig-out
|
|
237
|
+
zig-cache
|
|
238
|
+
.DS_Store
|
|
239
|
+
`
|
|
240
|
+
|
|
241
|
+
writeFileSync(join(projectPath, '.gitignore'), gitignore)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function createFullFeaturedTemplate(projectPath: string, projectName: string): void {
|
|
245
|
+
console.log('📝 Generating full-featured template...')
|
|
246
|
+
|
|
247
|
+
// Create package.json
|
|
248
|
+
const packageJson = {
|
|
249
|
+
name: projectName,
|
|
250
|
+
version: '0.0.1',
|
|
251
|
+
type: 'module',
|
|
252
|
+
private: true,
|
|
253
|
+
scripts: {
|
|
254
|
+
doctor: 'craft --version',
|
|
255
|
+
dev: 'bun run src/index.ts',
|
|
256
|
+
build: 'bun build src/index.ts --outdir dist --target bun',
|
|
257
|
+
},
|
|
258
|
+
dependencies: {
|
|
259
|
+
'craft-native': `^${craftVersion}`,
|
|
260
|
+
|
|
261
|
+
},
|
|
262
|
+
devDependencies: {
|
|
263
|
+
'@types/bun': 'latest',
|
|
264
|
+
},
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
writeFileSync(
|
|
268
|
+
join(projectPath, 'package.json'),
|
|
269
|
+
JSON.stringify(packageJson, null, 2),
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
// Create src directory
|
|
273
|
+
mkdirSync(join(projectPath, 'src'))
|
|
274
|
+
|
|
275
|
+
// Create src/index.ts
|
|
276
|
+
const indexTs = `import { show } from 'craft-native'
|
|
277
|
+
|
|
278
|
+
const html = \`
|
|
279
|
+
<!DOCTYPE html>
|
|
280
|
+
<html>
|
|
281
|
+
<head>
|
|
282
|
+
<meta charset="UTF-8">
|
|
283
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
284
|
+
<style>
|
|
285
|
+
* {
|
|
286
|
+
margin: 0;
|
|
287
|
+
padding: 0;
|
|
288
|
+
box-sizing: border-box;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
body {
|
|
292
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
293
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
294
|
+
min-height: 100vh;
|
|
295
|
+
display: flex;
|
|
296
|
+
justify-content: center;
|
|
297
|
+
align-items: center;
|
|
298
|
+
padding: 20px;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.container {
|
|
302
|
+
background: white;
|
|
303
|
+
border-radius: 16px;
|
|
304
|
+
padding: 40px;
|
|
305
|
+
max-width: 600px;
|
|
306
|
+
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
h1 {
|
|
310
|
+
color: #667eea;
|
|
311
|
+
font-size: 2.5rem;
|
|
312
|
+
margin-bottom: 20px;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
p {
|
|
316
|
+
color: #666;
|
|
317
|
+
line-height: 1.6;
|
|
318
|
+
margin-bottom: 30px;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.features {
|
|
322
|
+
display: grid;
|
|
323
|
+
gap: 15px;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.feature {
|
|
327
|
+
padding: 15px;
|
|
328
|
+
background: #f8f9fa;
|
|
329
|
+
border-radius: 8px;
|
|
330
|
+
border-left: 4px solid #667eea;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.feature h3 {
|
|
334
|
+
color: #333;
|
|
335
|
+
margin-bottom: 5px;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.feature p {
|
|
339
|
+
margin: 0;
|
|
340
|
+
font-size: 0.9rem;
|
|
341
|
+
}
|
|
342
|
+
</style>
|
|
343
|
+
</head>
|
|
344
|
+
<body>
|
|
345
|
+
<div class="container">
|
|
346
|
+
<h1>⚡ ${projectName}</h1>
|
|
347
|
+
<p>Welcome to your new Craft desktop application!</p>
|
|
348
|
+
|
|
349
|
+
<div class="features">
|
|
350
|
+
<div class="feature">
|
|
351
|
+
<h3>🚀 Fast</h3>
|
|
352
|
+
<p>Built with Zig for maximum performance</p>
|
|
353
|
+
</div>
|
|
354
|
+
<div class="feature">
|
|
355
|
+
<h3>💡 Simple</h3>
|
|
356
|
+
<p>TypeScript-first API, no Zig required</p>
|
|
357
|
+
</div>
|
|
358
|
+
<div class="feature">
|
|
359
|
+
<h3>🎨 Modern</h3>
|
|
360
|
+
<p>Use any web framework you love</p>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
</body>
|
|
365
|
+
</html>
|
|
366
|
+
\`
|
|
367
|
+
|
|
368
|
+
await show(html, {
|
|
369
|
+
title: '${projectName}',
|
|
370
|
+
width: 800,
|
|
371
|
+
height: 600,
|
|
372
|
+
})
|
|
373
|
+
`
|
|
374
|
+
|
|
375
|
+
writeFileSync(join(projectPath, 'src/index.ts'), indexTs)
|
|
376
|
+
|
|
377
|
+
// Create README.md
|
|
378
|
+
const readme = `# ${projectName}
|
|
379
|
+
|
|
380
|
+
A full-featured Craft desktop application.
|
|
381
|
+
|
|
382
|
+
## Getting Started
|
|
383
|
+
|
|
384
|
+
\`\`\`bash
|
|
385
|
+
bun install
|
|
386
|
+
bun run doctor
|
|
387
|
+
bun run dev
|
|
388
|
+
\`\`\`
|
|
389
|
+
|
|
390
|
+
\`bun run doctor\` verifies that the native \`craft\` binary is available on your PATH. Install it with \`pantry install craft\` if the command is missing.
|
|
391
|
+
|
|
392
|
+
## Build
|
|
393
|
+
|
|
394
|
+
\`\`\`bash
|
|
395
|
+
bun run build
|
|
396
|
+
\`\`\`
|
|
397
|
+
|
|
398
|
+
## Features
|
|
399
|
+
|
|
400
|
+
- ⚡ Fast startup and runtime performance
|
|
401
|
+
- 💡 TypeScript-first development
|
|
402
|
+
- 🎨 Modern HTML/CSS/JavaScript support
|
|
403
|
+
- 🔥 Hot reload in development mode
|
|
404
|
+
|
|
405
|
+
## Learn More
|
|
406
|
+
|
|
407
|
+
- [Craft Documentation](https://github.com/home-lang/craft)
|
|
408
|
+
- [TypeScript SDK](https://github.com/home-lang/craft/tree/main/packages/typescript)
|
|
409
|
+
`
|
|
410
|
+
|
|
411
|
+
writeFileSync(join(projectPath, 'README.md'), readme)
|
|
412
|
+
|
|
413
|
+
// Create .gitignore
|
|
414
|
+
const gitignore = `node_modules
|
|
415
|
+
dist
|
|
416
|
+
zig-out
|
|
417
|
+
zig-cache
|
|
418
|
+
.DS_Store
|
|
419
|
+
`
|
|
420
|
+
|
|
421
|
+
writeFileSync(join(projectPath, '.gitignore'), gitignore)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function createTodoAppTemplate(projectPath: string, projectName: string): void {
|
|
425
|
+
console.log('📝 Generating todo app template...')
|
|
426
|
+
|
|
427
|
+
// Create package.json
|
|
428
|
+
const packageJson = {
|
|
429
|
+
name: projectName,
|
|
430
|
+
version: '0.0.1',
|
|
431
|
+
type: 'module',
|
|
432
|
+
private: true,
|
|
433
|
+
scripts: {
|
|
434
|
+
doctor: 'craft --version',
|
|
435
|
+
dev: 'bun run src/index.ts',
|
|
436
|
+
build: 'bun build src/index.ts --outdir dist --target bun',
|
|
437
|
+
},
|
|
438
|
+
dependencies: {
|
|
439
|
+
'craft-native': `^${craftVersion}`,
|
|
440
|
+
|
|
441
|
+
},
|
|
442
|
+
devDependencies: {
|
|
443
|
+
'@types/bun': 'latest',
|
|
444
|
+
},
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
writeFileSync(
|
|
448
|
+
join(projectPath, 'package.json'),
|
|
449
|
+
JSON.stringify(packageJson, null, 2),
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
// Create src directory
|
|
453
|
+
mkdirSync(join(projectPath, 'src'))
|
|
454
|
+
|
|
455
|
+
// Create src/index.ts with full todo app
|
|
456
|
+
const indexTs = `import { show } from 'craft-native'
|
|
457
|
+
|
|
458
|
+
const html = \`
|
|
459
|
+
<!DOCTYPE html>
|
|
460
|
+
<html>
|
|
461
|
+
<head>
|
|
462
|
+
<meta charset="UTF-8">
|
|
463
|
+
<style>
|
|
464
|
+
* {
|
|
465
|
+
margin: 0;
|
|
466
|
+
padding: 0;
|
|
467
|
+
box-sizing: border-box;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
body {
|
|
471
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
472
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
473
|
+
min-height: 100vh;
|
|
474
|
+
display: flex;
|
|
475
|
+
justify-content: center;
|
|
476
|
+
align-items: center;
|
|
477
|
+
padding: 20px;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.container {
|
|
481
|
+
background: white;
|
|
482
|
+
border-radius: 16px;
|
|
483
|
+
padding: 30px;
|
|
484
|
+
width: 100%;
|
|
485
|
+
max-width: 500px;
|
|
486
|
+
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
h1 {
|
|
490
|
+
color: #667eea;
|
|
491
|
+
margin-bottom: 20px;
|
|
492
|
+
font-size: 2rem;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.input-group {
|
|
496
|
+
display: flex;
|
|
497
|
+
gap: 10px;
|
|
498
|
+
margin-bottom: 20px;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
input[type="text"] {
|
|
502
|
+
flex: 1;
|
|
503
|
+
padding: 12px;
|
|
504
|
+
border: 2px solid #e0e0e0;
|
|
505
|
+
border-radius: 8px;
|
|
506
|
+
font-size: 1rem;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
button {
|
|
510
|
+
padding: 12px 24px;
|
|
511
|
+
background: #667eea;
|
|
512
|
+
color: white;
|
|
513
|
+
border: none;
|
|
514
|
+
border-radius: 8px;
|
|
515
|
+
cursor: pointer;
|
|
516
|
+
font-size: 1rem;
|
|
517
|
+
font-weight: 600;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
button:hover {
|
|
521
|
+
background: #5568d3;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
.todo-list {
|
|
525
|
+
list-style: none;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
.todo-item {
|
|
529
|
+
display: flex;
|
|
530
|
+
align-items: center;
|
|
531
|
+
padding: 15px;
|
|
532
|
+
background: #f8f9fa;
|
|
533
|
+
border-radius: 8px;
|
|
534
|
+
margin-bottom: 10px;
|
|
535
|
+
gap: 10px;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.todo-item.completed {
|
|
539
|
+
opacity: 0.6;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.todo-item.completed span {
|
|
543
|
+
text-decoration: line-through;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
input[type="checkbox"] {
|
|
547
|
+
width: 20px;
|
|
548
|
+
height: 20px;
|
|
549
|
+
cursor: pointer;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.todo-text {
|
|
553
|
+
flex: 1;
|
|
554
|
+
color: #333;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.delete-btn {
|
|
558
|
+
padding: 6px 12px;
|
|
559
|
+
background: #ff6b6b;
|
|
560
|
+
font-size: 0.9rem;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.delete-btn:hover {
|
|
564
|
+
background: #ee5a52;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.empty-state {
|
|
568
|
+
text-align: center;
|
|
569
|
+
padding: 40px;
|
|
570
|
+
color: #999;
|
|
571
|
+
}
|
|
572
|
+
</style>
|
|
573
|
+
</head>
|
|
574
|
+
<body>
|
|
575
|
+
<div class="container">
|
|
576
|
+
<h1>📝 Todo List</h1>
|
|
577
|
+
|
|
578
|
+
<div class="input-group">
|
|
579
|
+
<input type="text" id="todoInput" placeholder="What needs to be done?" />
|
|
580
|
+
<button onclick="addTodo()">Add</button>
|
|
581
|
+
</div>
|
|
582
|
+
|
|
583
|
+
<ul id="todoList" class="todo-list"></ul>
|
|
584
|
+
<div id="emptyState" class="empty-state">No todos yet. Add one above!</div>
|
|
585
|
+
</div>
|
|
586
|
+
|
|
587
|
+
<script>
|
|
588
|
+
let todos = []
|
|
589
|
+
|
|
590
|
+
function render() {
|
|
591
|
+
const list = document.getElementById('todoList')
|
|
592
|
+
const emptyState = document.getElementById('emptyState')
|
|
593
|
+
|
|
594
|
+
if (todos.length === 0) {
|
|
595
|
+
list.innerHTML = ''
|
|
596
|
+
emptyState.style.display = 'block'
|
|
597
|
+
return
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
emptyState.style.display = 'none'
|
|
601
|
+
list.innerHTML = todos.map((todo, index) => \\\`
|
|
602
|
+
<li class="todo-item \\\${todo.completed ? 'completed' : ''}">
|
|
603
|
+
<input type="checkbox" \\\${todo.completed ? 'checked' : ''} onchange="toggleTodo(\\\${index})" />
|
|
604
|
+
<span class="todo-text">\\\${todo.text}</span>
|
|
605
|
+
<button class="delete-btn" onclick="deleteTodo(\\\${index})">Delete</button>
|
|
606
|
+
</li>
|
|
607
|
+
\\\`).join('')
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function addTodo() {
|
|
611
|
+
const input = document.getElementById('todoInput')
|
|
612
|
+
const text = input.value.trim()
|
|
613
|
+
|
|
614
|
+
if (text) {
|
|
615
|
+
todos.push({ text, completed: false })
|
|
616
|
+
input.value = ''
|
|
617
|
+
render()
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function toggleTodo(index) {
|
|
622
|
+
todos[index].completed = !todos[index].completed
|
|
623
|
+
render()
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function deleteTodo(index) {
|
|
627
|
+
todos.splice(index, 1)
|
|
628
|
+
render()
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
document.getElementById('todoInput').addEventListener('keypress', (e) => {
|
|
632
|
+
if (e.key === 'Enter') {
|
|
633
|
+
addTodo()
|
|
634
|
+
}
|
|
635
|
+
})
|
|
636
|
+
|
|
637
|
+
render()
|
|
638
|
+
</script>
|
|
639
|
+
</body>
|
|
640
|
+
</html>
|
|
641
|
+
\`
|
|
642
|
+
|
|
643
|
+
await show(html, {
|
|
644
|
+
title: '${projectName}',
|
|
645
|
+
width: 600,
|
|
646
|
+
height: 700,
|
|
647
|
+
})
|
|
648
|
+
`
|
|
649
|
+
|
|
650
|
+
writeFileSync(join(projectPath, 'src/index.ts'), indexTs)
|
|
651
|
+
|
|
652
|
+
// Create README.md
|
|
653
|
+
const readme = `# ${projectName}
|
|
654
|
+
|
|
655
|
+
An interactive todo list application built with Craft.
|
|
656
|
+
|
|
657
|
+
## Getting Started
|
|
658
|
+
|
|
659
|
+
\`\`\`bash
|
|
660
|
+
bun install
|
|
661
|
+
bun run doctor
|
|
662
|
+
bun run dev
|
|
663
|
+
\`\`\`
|
|
664
|
+
|
|
665
|
+
\`bun run doctor\` verifies that the native \`craft\` binary is available on your PATH. Install it with \`pantry install craft\` if the command is missing.
|
|
666
|
+
|
|
667
|
+
## Build
|
|
668
|
+
|
|
669
|
+
\`\`\`bash
|
|
670
|
+
bun run build
|
|
671
|
+
\`\`\`
|
|
672
|
+
|
|
673
|
+
## Features
|
|
674
|
+
|
|
675
|
+
- ✅ Add, complete, and delete todos
|
|
676
|
+
- 💾 Clean, modern UI
|
|
677
|
+
- ⚡ Fast and lightweight
|
|
678
|
+
|
|
679
|
+
## Learn More
|
|
680
|
+
|
|
681
|
+
- [Craft Documentation](https://github.com/home-lang/craft)
|
|
682
|
+
- [TypeScript SDK](https://github.com/home-lang/craft/tree/main/packages/typescript)
|
|
683
|
+
`
|
|
684
|
+
|
|
685
|
+
writeFileSync(join(projectPath, 'README.md'), readme)
|
|
686
|
+
|
|
687
|
+
// Create .gitignore
|
|
688
|
+
const gitignore = `node_modules
|
|
689
|
+
dist
|
|
690
|
+
zig-out
|
|
691
|
+
zig-cache
|
|
692
|
+
.DS_Store
|
|
693
|
+
`
|
|
694
|
+
|
|
695
|
+
writeFileSync(join(projectPath, '.gitignore'), gitignore)
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function installDependencies(projectPath: string): Promise<void> {
|
|
699
|
+
return new Promise((resolve, reject) => {
|
|
700
|
+
const proc = spawn('bun', ['install'], {
|
|
701
|
+
cwd: projectPath,
|
|
702
|
+
stdio: 'inherit',
|
|
703
|
+
})
|
|
704
|
+
|
|
705
|
+
proc.on('exit', (code) => {
|
|
706
|
+
if (code === 0 || code === null) {
|
|
707
|
+
resolve()
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
reject(new Error(`Installation failed with code ${code}`))
|
|
711
|
+
}
|
|
712
|
+
})
|
|
713
|
+
|
|
714
|
+
proc.on('error', (error) => {
|
|
715
|
+
reject(error)
|
|
716
|
+
})
|
|
717
|
+
})
|
|
718
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-craft",
|
|
3
|
+
"version": "0.0.37",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Scaffold a new Craft desktop app project",
|
|
7
|
+
"author": "Chris Breuer <chris@stacksjs.org>",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"homepage": "https://github.com/home-lang/craft#readme",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/home-lang/craft.git",
|
|
13
|
+
"directory": "packages/create-craft"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/home-lang/craft/issues"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"create-craft",
|
|
20
|
+
"scaffold",
|
|
21
|
+
"desktop",
|
|
22
|
+
"app",
|
|
23
|
+
"framework",
|
|
24
|
+
"craft",
|
|
25
|
+
"starter",
|
|
26
|
+
"template",
|
|
27
|
+
"boilerplate"
|
|
28
|
+
],
|
|
29
|
+
"bin": {
|
|
30
|
+
"create-craft": "./bin/cli.ts"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"bin",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"dev": "bun --watch bin/cli.ts",
|
|
38
|
+
"typecheck": "tsc --noEmit"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@stacksjs/clapp": "^0.2.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/bun": "latest"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0",
|
|
48
|
+
"bun": ">=1.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|