@tachui/cli 0.7.0-alpha1
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 +363 -0
- package/bin/tacho.js +10 -0
- package/dist/commands/analyze.d.ts +8 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +337 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/dev.d.ts +8 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +171 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/generate.d.ts +8 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +716 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +734 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/migrate.d.ts +8 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +335 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/optimize.d.ts +8 -0
- package/dist/commands/optimize.d.ts.map +1 -0
- package/dist/commands/optimize.js +357 -0
- package/dist/commands/optimize.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tacho CLI - Init Command
|
|
3
|
+
*
|
|
4
|
+
* Initialize new TachUI projects with smart templates and Phase 6 features
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { join, resolve } from 'node:path';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
import ora from 'ora';
|
|
11
|
+
import prompts from 'prompts';
|
|
12
|
+
const templates = {
|
|
13
|
+
basic: {
|
|
14
|
+
name: 'Basic TachUI App',
|
|
15
|
+
description: 'Simple TachUI application with core components',
|
|
16
|
+
features: ['Text & Button components', 'Layout system', 'Modifiers'],
|
|
17
|
+
files: {
|
|
18
|
+
'package.json': JSON.stringify({
|
|
19
|
+
name: '{projectName}',
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
description: 'TachUI application',
|
|
22
|
+
type: 'module',
|
|
23
|
+
scripts: {
|
|
24
|
+
dev: 'vite',
|
|
25
|
+
build: 'vite build',
|
|
26
|
+
preview: 'vite preview',
|
|
27
|
+
typecheck: 'tsc --noEmit',
|
|
28
|
+
},
|
|
29
|
+
dependencies: {
|
|
30
|
+
'@tachui/core': '^0.1.0',
|
|
31
|
+
'@tachui/forms': '^0.1.0',
|
|
32
|
+
},
|
|
33
|
+
devDependencies: {
|
|
34
|
+
vite: '^5.0.0',
|
|
35
|
+
typescript: '^5.0.0',
|
|
36
|
+
'@types/node': '^20.0.0',
|
|
37
|
+
},
|
|
38
|
+
}, null, 2),
|
|
39
|
+
'vite.config.ts': `import { defineConfig } from 'vite'
|
|
40
|
+
|
|
41
|
+
export default defineConfig({
|
|
42
|
+
server: {
|
|
43
|
+
port: 3000,
|
|
44
|
+
open: true
|
|
45
|
+
},
|
|
46
|
+
build: {
|
|
47
|
+
target: 'es2020'
|
|
48
|
+
}
|
|
49
|
+
})`,
|
|
50
|
+
'tsconfig.json': JSON.stringify({
|
|
51
|
+
compilerOptions: {
|
|
52
|
+
target: 'ES2020',
|
|
53
|
+
module: 'ESNext',
|
|
54
|
+
moduleResolution: 'bundler',
|
|
55
|
+
allowSyntheticDefaultImports: true,
|
|
56
|
+
esModuleInterop: true,
|
|
57
|
+
jsx: 'preserve',
|
|
58
|
+
declaration: true,
|
|
59
|
+
strict: true,
|
|
60
|
+
skipLibCheck: true,
|
|
61
|
+
forceConsistentCasingInFileNames: true,
|
|
62
|
+
},
|
|
63
|
+
include: ['src/**/*'],
|
|
64
|
+
exclude: ['node_modules', 'dist'],
|
|
65
|
+
}, null, 2),
|
|
66
|
+
'src/main.ts': `import { mount } from '@tachui/core'
|
|
67
|
+
import { App } from './App'
|
|
68
|
+
|
|
69
|
+
// Mount the app
|
|
70
|
+
mount('#app', App())`,
|
|
71
|
+
'src/App.ts': `import { Layout, Text, Button } from '@tachui/core'
|
|
72
|
+
|
|
73
|
+
export function App() {
|
|
74
|
+
return Layout.VStack({
|
|
75
|
+
children: [
|
|
76
|
+
Text('Welcome to TachUI!')
|
|
77
|
+
.modifier
|
|
78
|
+
.fontSize(32)
|
|
79
|
+
.fontWeight('bold')
|
|
80
|
+
.foregroundColor('#007AFF')
|
|
81
|
+
.margin({ bottom: 16 })
|
|
82
|
+
.build(),
|
|
83
|
+
|
|
84
|
+
Text('SwiftUI-inspired web development')
|
|
85
|
+
.modifier
|
|
86
|
+
.fontSize(18)
|
|
87
|
+
.foregroundColor('#666')
|
|
88
|
+
.margin({ bottom: 24 })
|
|
89
|
+
.build(),
|
|
90
|
+
|
|
91
|
+
Button({
|
|
92
|
+
title: 'Get Started',
|
|
93
|
+
onTap: () => console.log('Hello TachUI!')
|
|
94
|
+
})
|
|
95
|
+
.modifier
|
|
96
|
+
.backgroundColor('#007AFF')
|
|
97
|
+
.foregroundColor('#ffffff')
|
|
98
|
+
.padding(16, 32)
|
|
99
|
+
.cornerRadius(8)
|
|
100
|
+
.build()
|
|
101
|
+
],
|
|
102
|
+
spacing: 0,
|
|
103
|
+
alignment: 'center'
|
|
104
|
+
})
|
|
105
|
+
.modifier
|
|
106
|
+
.frame(undefined, '100vh')
|
|
107
|
+
.justifyContent('center')
|
|
108
|
+
.alignItems('center')
|
|
109
|
+
.backgroundColor('#f5f5f7')
|
|
110
|
+
.build()
|
|
111
|
+
}`,
|
|
112
|
+
'index.html': `<!DOCTYPE html>
|
|
113
|
+
<html lang="en">
|
|
114
|
+
<head>
|
|
115
|
+
<meta charset="UTF-8">
|
|
116
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
117
|
+
<title>{projectName}</title>
|
|
118
|
+
<style>
|
|
119
|
+
* {
|
|
120
|
+
margin: 0;
|
|
121
|
+
padding: 0;
|
|
122
|
+
box-sizing: border-box;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
body {
|
|
126
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
127
|
+
}
|
|
128
|
+
</style>
|
|
129
|
+
</head>
|
|
130
|
+
<body>
|
|
131
|
+
<div id="app"></div>
|
|
132
|
+
<script type="module" src="/src/main.ts"></script>
|
|
133
|
+
</body>
|
|
134
|
+
</html>`,
|
|
135
|
+
'README.md': `# {projectName}
|
|
136
|
+
|
|
137
|
+
A TachUI application built with SwiftUI-inspired components and reactive architecture.
|
|
138
|
+
|
|
139
|
+
## Getting Started
|
|
140
|
+
|
|
141
|
+
\`\`\`bash
|
|
142
|
+
npm install
|
|
143
|
+
npm run dev
|
|
144
|
+
\`\`\`
|
|
145
|
+
|
|
146
|
+
## Available Scripts
|
|
147
|
+
|
|
148
|
+
- \`npm run dev\` - Start development server
|
|
149
|
+
- \`npm run build\` - Build for production
|
|
150
|
+
- \`npm run preview\` - Preview production build
|
|
151
|
+
- \`npm run typecheck\` - Type check without building
|
|
152
|
+
|
|
153
|
+
## Learn More
|
|
154
|
+
|
|
155
|
+
- [TachUI Documentation](https://github.com/whoughton/TachUI)
|
|
156
|
+
- [TachUI Examples](https://github.com/whoughton/TachUI/tree/main/examples)
|
|
157
|
+
`,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
phase6: {
|
|
161
|
+
name: 'Phase 6 Features App',
|
|
162
|
+
description: 'Complete app with state management, lifecycle, and navigation',
|
|
163
|
+
features: [
|
|
164
|
+
'@State & @ObservedObject',
|
|
165
|
+
'Lifecycle modifiers',
|
|
166
|
+
'NavigationView & TabView',
|
|
167
|
+
'Real-world patterns',
|
|
168
|
+
],
|
|
169
|
+
files: {
|
|
170
|
+
'package.json': JSON.stringify({
|
|
171
|
+
name: '{projectName}',
|
|
172
|
+
version: '1.0.0',
|
|
173
|
+
description: 'TachUI Phase 6 application with advanced features',
|
|
174
|
+
type: 'module',
|
|
175
|
+
scripts: {
|
|
176
|
+
dev: 'vite',
|
|
177
|
+
build: 'vite build',
|
|
178
|
+
preview: 'vite preview',
|
|
179
|
+
typecheck: 'tsc --noEmit',
|
|
180
|
+
},
|
|
181
|
+
dependencies: {
|
|
182
|
+
'@tachui/core': '^0.1.0',
|
|
183
|
+
'@tachui/forms': '^0.1.0',
|
|
184
|
+
},
|
|
185
|
+
devDependencies: {
|
|
186
|
+
vite: '^5.0.0',
|
|
187
|
+
typescript: '^5.0.0',
|
|
188
|
+
'@types/node': '^20.0.0',
|
|
189
|
+
},
|
|
190
|
+
}, null, 2),
|
|
191
|
+
'vite.config.ts': `import { defineConfig } from 'vite'
|
|
192
|
+
|
|
193
|
+
export default defineConfig({
|
|
194
|
+
server: {
|
|
195
|
+
port: 3000,
|
|
196
|
+
open: true
|
|
197
|
+
},
|
|
198
|
+
build: {
|
|
199
|
+
target: 'es2020'
|
|
200
|
+
}
|
|
201
|
+
})`,
|
|
202
|
+
'tsconfig.json': JSON.stringify({
|
|
203
|
+
compilerOptions: {
|
|
204
|
+
target: 'ES2020',
|
|
205
|
+
module: 'ESNext',
|
|
206
|
+
moduleResolution: 'bundler',
|
|
207
|
+
allowSyntheticDefaultImports: true,
|
|
208
|
+
esModuleInterop: true,
|
|
209
|
+
jsx: 'preserve',
|
|
210
|
+
declaration: true,
|
|
211
|
+
strict: true,
|
|
212
|
+
skipLibCheck: true,
|
|
213
|
+
forceConsistentCasingInFileNames: true,
|
|
214
|
+
},
|
|
215
|
+
include: ['src/**/*'],
|
|
216
|
+
exclude: ['node_modules', 'dist'],
|
|
217
|
+
}, null, 2),
|
|
218
|
+
'src/main.ts': `import { mount } from '@tachui/core'
|
|
219
|
+
import { App } from './App'
|
|
220
|
+
|
|
221
|
+
// Mount the app
|
|
222
|
+
mount('#app', App())`,
|
|
223
|
+
'src/App.ts': `import { TabView, createTabItem } from '@tachui/core/navigation'
|
|
224
|
+
import { State } from '@tachui/core/state'
|
|
225
|
+
import { HomeScreen } from './screens/HomeScreen'
|
|
226
|
+
import { TodoScreen } from './screens/TodoScreen'
|
|
227
|
+
import { SettingsScreen } from './screens/SettingsScreen'
|
|
228
|
+
|
|
229
|
+
export function App() {
|
|
230
|
+
const selectedTab = State('home')
|
|
231
|
+
|
|
232
|
+
const tabs = [
|
|
233
|
+
createTabItem(
|
|
234
|
+
'home',
|
|
235
|
+
'Home',
|
|
236
|
+
HomeScreen(),
|
|
237
|
+
{ icon: '🏠' }
|
|
238
|
+
),
|
|
239
|
+
createTabItem(
|
|
240
|
+
'todos',
|
|
241
|
+
'Todos',
|
|
242
|
+
TodoScreen(),
|
|
243
|
+
{ icon: '📝' }
|
|
244
|
+
),
|
|
245
|
+
createTabItem(
|
|
246
|
+
'settings',
|
|
247
|
+
'Settings',
|
|
248
|
+
SettingsScreen(),
|
|
249
|
+
{ icon: '⚙️' }
|
|
250
|
+
)
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
return TabView(tabs, {
|
|
254
|
+
selection: selectedTab.projectedValue,
|
|
255
|
+
tabPlacement: 'bottom',
|
|
256
|
+
accentColor: '#007AFF'
|
|
257
|
+
})
|
|
258
|
+
}`,
|
|
259
|
+
'src/screens/HomeScreen.ts': `import { Layout, Text, Button } from '@tachui/core'
|
|
260
|
+
import { State } from '@tachui/core/state'
|
|
261
|
+
|
|
262
|
+
export function HomeScreen() {
|
|
263
|
+
const welcomeMessage = State('Welcome to TachUI Phase 6!')
|
|
264
|
+
const clickCount = State(0)
|
|
265
|
+
|
|
266
|
+
return Layout.VStack({
|
|
267
|
+
children: [
|
|
268
|
+
Text(() => welcomeMessage.wrappedValue)
|
|
269
|
+
.modifier
|
|
270
|
+
.fontSize(28)
|
|
271
|
+
.fontWeight('bold')
|
|
272
|
+
.foregroundColor('#007AFF')
|
|
273
|
+
.textAlign('center')
|
|
274
|
+
.margin({ bottom: 24 })
|
|
275
|
+
.build(),
|
|
276
|
+
|
|
277
|
+
Text('This app demonstrates all Phase 6 features:')
|
|
278
|
+
.modifier
|
|
279
|
+
.fontSize(18)
|
|
280
|
+
.foregroundColor('#333')
|
|
281
|
+
.margin({ bottom: 16 })
|
|
282
|
+
.build(),
|
|
283
|
+
|
|
284
|
+
Layout.VStack({
|
|
285
|
+
children: [
|
|
286
|
+
Text('✅ @State reactive property wrapper'),
|
|
287
|
+
Text('✅ Lifecycle modifiers (onAppear, task)'),
|
|
288
|
+
Text('✅ TabView navigation system'),
|
|
289
|
+
Text('✅ Real-world component patterns')
|
|
290
|
+
].map(text =>
|
|
291
|
+
text.modifier
|
|
292
|
+
.fontSize(16)
|
|
293
|
+
.foregroundColor('#666')
|
|
294
|
+
.padding({ vertical: 4 })
|
|
295
|
+
.build()
|
|
296
|
+
),
|
|
297
|
+
spacing: 4,
|
|
298
|
+
alignment: 'leading'
|
|
299
|
+
}),
|
|
300
|
+
|
|
301
|
+
Button({
|
|
302
|
+
title: \`Clicked \${() => clickCount.wrappedValue} times\`,
|
|
303
|
+
onTap: () => clickCount.wrappedValue++
|
|
304
|
+
})
|
|
305
|
+
.modifier
|
|
306
|
+
.backgroundColor('#007AFF')
|
|
307
|
+
.foregroundColor('#ffffff')
|
|
308
|
+
.padding(16, 24)
|
|
309
|
+
.cornerRadius(8)
|
|
310
|
+
.margin({ top: 32 })
|
|
311
|
+
.build()
|
|
312
|
+
],
|
|
313
|
+
spacing: 0,
|
|
314
|
+
alignment: 'center'
|
|
315
|
+
})
|
|
316
|
+
.modifier
|
|
317
|
+
.padding(24)
|
|
318
|
+
.onAppear(() => {
|
|
319
|
+
console.log('Home screen appeared!')
|
|
320
|
+
})
|
|
321
|
+
.task(async () => {
|
|
322
|
+
// Simulate loading welcome message
|
|
323
|
+
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
324
|
+
welcomeMessage.wrappedValue = 'Welcome to TachUI Phase 6! 🚀'
|
|
325
|
+
})
|
|
326
|
+
.build()
|
|
327
|
+
}`,
|
|
328
|
+
'src/screens/TodoScreen.ts': `import { Layout, Text, Button } from '@tachui/core'
|
|
329
|
+
import { TextField } from '@tachui/forms'
|
|
330
|
+
import { State, ObservableObjectBase, ObservedObject } from '@tachui/core/state'
|
|
331
|
+
|
|
332
|
+
class TodoItem extends ObservableObjectBase {
|
|
333
|
+
constructor(
|
|
334
|
+
public id: string,
|
|
335
|
+
private _text: string,
|
|
336
|
+
private _completed: boolean = false
|
|
337
|
+
) {
|
|
338
|
+
super()
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
get text() { return this._text }
|
|
342
|
+
set text(value: string) {
|
|
343
|
+
this._text = value
|
|
344
|
+
this.notifyChange()
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
get completed() { return this._completed }
|
|
348
|
+
set completed(value: boolean) {
|
|
349
|
+
this._completed = value
|
|
350
|
+
this.notifyChange()
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
toggle() {
|
|
354
|
+
this.completed = !this.completed
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
class TodoStore extends ObservableObjectBase {
|
|
359
|
+
private _items: TodoItem[] = []
|
|
360
|
+
|
|
361
|
+
get items() { return this._items }
|
|
362
|
+
|
|
363
|
+
addItem(text: string) {
|
|
364
|
+
const item = new TodoItem(Date.now().toString(), text)
|
|
365
|
+
this._items.push(item)
|
|
366
|
+
this.notifyChange()
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
removeItem(id: string) {
|
|
370
|
+
this._items = this._items.filter(item => item.id !== id)
|
|
371
|
+
this.notifyChange()
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
get completedCount() {
|
|
375
|
+
return this._items.filter(item => item.completed).length
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export function TodoScreen() {
|
|
380
|
+
const todoStore = ObservedObject(new TodoStore())
|
|
381
|
+
const newTodoText = State('')
|
|
382
|
+
|
|
383
|
+
const addTodo = () => {
|
|
384
|
+
if (newTodoText.wrappedValue.trim()) {
|
|
385
|
+
todoStore.wrappedValue.addItem(newTodoText.wrappedValue)
|
|
386
|
+
newTodoText.wrappedValue = ''
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return Layout.VStack({
|
|
391
|
+
children: [
|
|
392
|
+
Text('My Todos')
|
|
393
|
+
.modifier
|
|
394
|
+
.fontSize(28)
|
|
395
|
+
.fontWeight('bold')
|
|
396
|
+
.margin({ bottom: 16 })
|
|
397
|
+
.build(),
|
|
398
|
+
|
|
399
|
+
Text(() => \`\${todoStore.wrappedValue.completedCount} of \${todoStore.wrappedValue.items.length} completed\`)
|
|
400
|
+
.modifier
|
|
401
|
+
.fontSize(16)
|
|
402
|
+
.foregroundColor('#666')
|
|
403
|
+
.margin({ bottom: 20 })
|
|
404
|
+
.build(),
|
|
405
|
+
|
|
406
|
+
Layout.HStack({
|
|
407
|
+
children: [
|
|
408
|
+
TextField({
|
|
409
|
+
placeholder: 'Enter new todo',
|
|
410
|
+
text: newTodoText.projectedValue
|
|
411
|
+
})
|
|
412
|
+
.modifier
|
|
413
|
+
.flexGrow(1)
|
|
414
|
+
.build(),
|
|
415
|
+
|
|
416
|
+
Button({
|
|
417
|
+
title: 'Add',
|
|
418
|
+
onTap: addTodo
|
|
419
|
+
})
|
|
420
|
+
.modifier
|
|
421
|
+
.backgroundColor('#007AFF')
|
|
422
|
+
.foregroundColor('#ffffff')
|
|
423
|
+
.padding(8, 16)
|
|
424
|
+
.cornerRadius(6)
|
|
425
|
+
.build()
|
|
426
|
+
],
|
|
427
|
+
spacing: 12
|
|
428
|
+
}),
|
|
429
|
+
|
|
430
|
+
Layout.VStack({
|
|
431
|
+
children: todoStore.wrappedValue.items.map(item => {
|
|
432
|
+
const observedItem = ObservedObject(item)
|
|
433
|
+
|
|
434
|
+
return Layout.HStack({
|
|
435
|
+
children: [
|
|
436
|
+
Button({
|
|
437
|
+
title: observedItem.wrappedValue.completed ? '✅' : '⬜',
|
|
438
|
+
onTap: () => observedItem.wrappedValue.toggle()
|
|
439
|
+
})
|
|
440
|
+
.modifier
|
|
441
|
+
.backgroundColor('transparent')
|
|
442
|
+
.border(0)
|
|
443
|
+
.padding(0)
|
|
444
|
+
.build(),
|
|
445
|
+
|
|
446
|
+
Text(() => observedItem.wrappedValue.text)
|
|
447
|
+
.modifier
|
|
448
|
+
.fontSize(16)
|
|
449
|
+
.foregroundColor(observedItem.wrappedValue.completed ? '#999' : '#333')
|
|
450
|
+
.textDecoration(observedItem.wrappedValue.completed ? 'line-through' : 'none')
|
|
451
|
+
.flexGrow(1)
|
|
452
|
+
.build(),
|
|
453
|
+
|
|
454
|
+
Button({
|
|
455
|
+
title: '🗑️',
|
|
456
|
+
onTap: () => todoStore.wrappedValue.removeItem(observedItem.wrappedValue.id)
|
|
457
|
+
})
|
|
458
|
+
.modifier
|
|
459
|
+
.backgroundColor('transparent')
|
|
460
|
+
.border(0)
|
|
461
|
+
.padding(0)
|
|
462
|
+
.build()
|
|
463
|
+
],
|
|
464
|
+
spacing: 12,
|
|
465
|
+
alignment: 'center'
|
|
466
|
+
})
|
|
467
|
+
.modifier
|
|
468
|
+
.backgroundColor('#f8f9fa')
|
|
469
|
+
.padding(12)
|
|
470
|
+
.cornerRadius(8)
|
|
471
|
+
.margin({ bottom: 8 })
|
|
472
|
+
.build()
|
|
473
|
+
}),
|
|
474
|
+
spacing: 0
|
|
475
|
+
})
|
|
476
|
+
.modifier
|
|
477
|
+
.margin({ top: 20 })
|
|
478
|
+
.build()
|
|
479
|
+
],
|
|
480
|
+
spacing: 0
|
|
481
|
+
})
|
|
482
|
+
.modifier
|
|
483
|
+
.padding(24)
|
|
484
|
+
.build()
|
|
485
|
+
}`,
|
|
486
|
+
'src/screens/SettingsScreen.ts': `import { Layout, Text } from '@tachui/core'
|
|
487
|
+
import { State } from '@tachui/core/state'
|
|
488
|
+
|
|
489
|
+
export function SettingsScreen() {
|
|
490
|
+
const version = State('1.0.0')
|
|
491
|
+
|
|
492
|
+
return Layout.VStack({
|
|
493
|
+
children: [
|
|
494
|
+
Text('Settings')
|
|
495
|
+
.modifier
|
|
496
|
+
.fontSize(28)
|
|
497
|
+
.fontWeight('bold')
|
|
498
|
+
.margin({ bottom: 32 })
|
|
499
|
+
.build(),
|
|
500
|
+
|
|
501
|
+
Layout.VStack({
|
|
502
|
+
children: [
|
|
503
|
+
Text('App Information')
|
|
504
|
+
.modifier
|
|
505
|
+
.fontSize(20)
|
|
506
|
+
.fontWeight('semibold')
|
|
507
|
+
.margin({ bottom: 16 })
|
|
508
|
+
.build(),
|
|
509
|
+
|
|
510
|
+
Layout.HStack({
|
|
511
|
+
children: [
|
|
512
|
+
Text('Version:')
|
|
513
|
+
.modifier
|
|
514
|
+
.fontSize(16)
|
|
515
|
+
.foregroundColor('#666')
|
|
516
|
+
.build(),
|
|
517
|
+
|
|
518
|
+
Text(() => version.wrappedValue)
|
|
519
|
+
.modifier
|
|
520
|
+
.fontSize(16)
|
|
521
|
+
.fontWeight('medium')
|
|
522
|
+
.build()
|
|
523
|
+
],
|
|
524
|
+
spacing: 8,
|
|
525
|
+
alignment: 'center'
|
|
526
|
+
}),
|
|
527
|
+
|
|
528
|
+
Layout.HStack({
|
|
529
|
+
children: [
|
|
530
|
+
Text('Framework:')
|
|
531
|
+
.modifier
|
|
532
|
+
.fontSize(16)
|
|
533
|
+
.foregroundColor('#666')
|
|
534
|
+
.build(),
|
|
535
|
+
|
|
536
|
+
Text('TachUI Phase 6')
|
|
537
|
+
.modifier
|
|
538
|
+
.fontSize(16)
|
|
539
|
+
.fontWeight('medium')
|
|
540
|
+
.build()
|
|
541
|
+
],
|
|
542
|
+
spacing: 8,
|
|
543
|
+
alignment: 'center'
|
|
544
|
+
}),
|
|
545
|
+
|
|
546
|
+
Text('Built with SwiftUI-inspired components and reactive state management')
|
|
547
|
+
.modifier
|
|
548
|
+
.fontSize(14)
|
|
549
|
+
.foregroundColor('#999')
|
|
550
|
+
.textAlign('center')
|
|
551
|
+
.margin({ top: 24 })
|
|
552
|
+
.build()
|
|
553
|
+
],
|
|
554
|
+
spacing: 12,
|
|
555
|
+
alignment: 'leading'
|
|
556
|
+
})
|
|
557
|
+
],
|
|
558
|
+
spacing: 0
|
|
559
|
+
})
|
|
560
|
+
.modifier
|
|
561
|
+
.padding(24)
|
|
562
|
+
.build()
|
|
563
|
+
}`,
|
|
564
|
+
'index.html': `<!DOCTYPE html>
|
|
565
|
+
<html lang="en">
|
|
566
|
+
<head>
|
|
567
|
+
<meta charset="UTF-8">
|
|
568
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
569
|
+
<title>{projectName}</title>
|
|
570
|
+
<style>
|
|
571
|
+
* {
|
|
572
|
+
margin: 0;
|
|
573
|
+
padding: 0;
|
|
574
|
+
box-sizing: border-box;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
body {
|
|
578
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
579
|
+
background-color: #f5f5f7;
|
|
580
|
+
}
|
|
581
|
+
</style>
|
|
582
|
+
</head>
|
|
583
|
+
<body>
|
|
584
|
+
<div id="app"></div>
|
|
585
|
+
<script type="module" src="/src/main.ts"></script>
|
|
586
|
+
</body>
|
|
587
|
+
</html>`,
|
|
588
|
+
'README.md': `# {projectName}
|
|
589
|
+
|
|
590
|
+
A complete TachUI application showcasing Phase 6 features:
|
|
591
|
+
|
|
592
|
+
- **@State**: Reactive local state management
|
|
593
|
+
- **@ObservedObject**: External object observation
|
|
594
|
+
- **Lifecycle Modifiers**: onAppear, task, refreshable
|
|
595
|
+
- **Navigation**: TabView with multiple screens
|
|
596
|
+
- **Real-world Patterns**: Todo app with persistent state
|
|
597
|
+
|
|
598
|
+
## Getting Started
|
|
599
|
+
|
|
600
|
+
\`\`\`bash
|
|
601
|
+
npm install
|
|
602
|
+
npm run dev
|
|
603
|
+
\`\`\`
|
|
604
|
+
|
|
605
|
+
## Features Demonstrated
|
|
606
|
+
|
|
607
|
+
### State Management
|
|
608
|
+
- Local reactive state with \`@State\`
|
|
609
|
+
- Observable objects with \`@ObservedObject\`
|
|
610
|
+
- Property wrapper patterns from SwiftUI
|
|
611
|
+
|
|
612
|
+
### Lifecycle Management
|
|
613
|
+
- \`onAppear\` for component initialization
|
|
614
|
+
- \`task\` for async operations with automatic cancellation
|
|
615
|
+
- Component lifecycle integration
|
|
616
|
+
|
|
617
|
+
### Navigation System
|
|
618
|
+
- \`TabView\` for tab-based navigation
|
|
619
|
+
- Multiple screens with state preservation
|
|
620
|
+
- SwiftUI-style navigation patterns
|
|
621
|
+
|
|
622
|
+
### Real-world Patterns
|
|
623
|
+
- Todo application with CRUD operations
|
|
624
|
+
- Observable data models
|
|
625
|
+
- Reactive UI updates
|
|
626
|
+
- Component composition
|
|
627
|
+
|
|
628
|
+
## Available Scripts
|
|
629
|
+
|
|
630
|
+
- \`npm run dev\` - Start development server
|
|
631
|
+
- \`npm run build\` - Build for production
|
|
632
|
+
- \`npm run preview\` - Preview production build
|
|
633
|
+
- \`npm run typecheck\` - Type check without building
|
|
634
|
+
|
|
635
|
+
## Learn More
|
|
636
|
+
|
|
637
|
+
- [TachUI Documentation](https://github.com/whoughton/TachUI)
|
|
638
|
+
- [Phase 6 Features Guide](https://github.com/whoughton/TachUI/blob/main/docs/api/phase-6-features.md)
|
|
639
|
+
`,
|
|
640
|
+
},
|
|
641
|
+
},
|
|
642
|
+
};
|
|
643
|
+
export const initCommand = new Command('init')
|
|
644
|
+
.description('Initialize a new TachUI project')
|
|
645
|
+
.argument('[project-name]', 'Project name')
|
|
646
|
+
.option('-t, --template <template>', 'Project template (basic, phase6)', 'basic')
|
|
647
|
+
.option('-y, --yes', 'Skip prompts and use defaults')
|
|
648
|
+
.action(async (projectName, options) => {
|
|
649
|
+
try {
|
|
650
|
+
let finalProjectName = projectName;
|
|
651
|
+
let selectedTemplate = options?.template || 'basic';
|
|
652
|
+
// Interactive prompts if not using --yes flag
|
|
653
|
+
if (!options?.yes) {
|
|
654
|
+
const response = await prompts([
|
|
655
|
+
{
|
|
656
|
+
type: 'text',
|
|
657
|
+
name: 'projectName',
|
|
658
|
+
message: 'Project name:',
|
|
659
|
+
initial: projectName || 'my-tachui-app',
|
|
660
|
+
validate: (value) => (value.length > 0 ? true : 'Project name is required'),
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
type: 'select',
|
|
664
|
+
name: 'template',
|
|
665
|
+
message: 'Choose a template:',
|
|
666
|
+
choices: Object.entries(templates).map(([key, template]) => ({
|
|
667
|
+
title: template.name,
|
|
668
|
+
description: template.description,
|
|
669
|
+
value: key,
|
|
670
|
+
})),
|
|
671
|
+
initial: selectedTemplate === 'phase6' ? 1 : 0,
|
|
672
|
+
},
|
|
673
|
+
]);
|
|
674
|
+
if (!response.projectName) {
|
|
675
|
+
console.log(chalk.yellow('Operation cancelled'));
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
finalProjectName = response.projectName;
|
|
679
|
+
selectedTemplate = response.template;
|
|
680
|
+
}
|
|
681
|
+
if (!finalProjectName) {
|
|
682
|
+
console.error(chalk.red('Project name is required'));
|
|
683
|
+
process.exit(1);
|
|
684
|
+
}
|
|
685
|
+
const template = templates[selectedTemplate];
|
|
686
|
+
if (!template) {
|
|
687
|
+
console.error(chalk.red(`Template "${selectedTemplate}" not found`));
|
|
688
|
+
process.exit(1);
|
|
689
|
+
}
|
|
690
|
+
const projectPath = resolve(finalProjectName);
|
|
691
|
+
// Check if directory already exists
|
|
692
|
+
if (existsSync(projectPath)) {
|
|
693
|
+
console.error(chalk.red(`Directory "${finalProjectName}" already exists`));
|
|
694
|
+
process.exit(1);
|
|
695
|
+
}
|
|
696
|
+
const spinner = ora('Creating TachUI project...').start();
|
|
697
|
+
// Create project directory
|
|
698
|
+
mkdirSync(projectPath, { recursive: true });
|
|
699
|
+
// Create all template files
|
|
700
|
+
for (const [filePath, content] of Object.entries(template.files)) {
|
|
701
|
+
const fullPath = join(projectPath, filePath);
|
|
702
|
+
const dir = fullPath.substring(0, fullPath.lastIndexOf('/'));
|
|
703
|
+
// Create directory if it doesn't exist
|
|
704
|
+
if (dir !== projectPath) {
|
|
705
|
+
mkdirSync(dir, { recursive: true });
|
|
706
|
+
}
|
|
707
|
+
// Replace template variables
|
|
708
|
+
const processedContent = content.replace(/{projectName}/g, finalProjectName);
|
|
709
|
+
writeFileSync(fullPath, processedContent);
|
|
710
|
+
}
|
|
711
|
+
spinner.succeed('TachUI project created successfully!');
|
|
712
|
+
// Success message with features
|
|
713
|
+
console.log(`
|
|
714
|
+
${chalk.green('✅ Project created:')} ${chalk.cyan(finalProjectName)}
|
|
715
|
+
${chalk.green('📁 Location:')} ${projectPath}
|
|
716
|
+
${chalk.green('🎨 Template:')} ${template.name}
|
|
717
|
+
|
|
718
|
+
${chalk.yellow('Features included:')}
|
|
719
|
+
${template.features.map((feature) => ` • ${feature}`).join('\n')}
|
|
720
|
+
|
|
721
|
+
${chalk.yellow('Next steps:')}
|
|
722
|
+
cd ${finalProjectName}
|
|
723
|
+
npm install
|
|
724
|
+
npm run dev
|
|
725
|
+
|
|
726
|
+
${chalk.green('Happy coding with TachUI! 🚀')}
|
|
727
|
+
`);
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
console.error(chalk.red('Error creating project:'), error.message);
|
|
731
|
+
process.exit(1);
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
//# sourceMappingURL=init.js.map
|