@tachui/cli 0.8.1-alpha → 0.8.8
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/README.md +37 -412
- package/bin/tacho.js +59 -7
- package/dist/commands/analyze-imports.d.ts +18 -0
- package/dist/commands/analyze-imports.d.ts.map +1 -0
- package/dist/commands/analyze-imports.js +152 -0
- package/dist/commands/analyze-imports.js.map +1 -0
- package/dist/commands/analyze.js +1 -1
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/dev.js +3 -3
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/generate.d.ts +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +1 -22
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +10 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +170 -680
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate/remove-modifier-trigger.d.ts +3 -0
- package/dist/commands/migrate/remove-modifier-trigger.d.ts.map +1 -0
- package/dist/commands/migrate/remove-modifier-trigger.js +103 -0
- package/dist/commands/migrate/remove-modifier-trigger.js.map +1 -0
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +8 -6
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/modifier-docs.d.ts +24 -0
- package/dist/commands/modifier-docs.d.ts.map +1 -0
- package/dist/commands/modifier-docs.js +375 -0
- package/dist/commands/modifier-docs.js.map +1 -0
- package/dist/import-optimizer.d.ts +50 -0
- package/dist/import-optimizer.d.ts.map +1 -0
- package/dist/import-optimizer.js +227 -0
- package/dist/import-optimizer.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/migrations/remove-modifier-trigger.d.ts +7 -0
- package/dist/migrations/remove-modifier-trigger.d.ts.map +1 -0
- package/dist/migrations/remove-modifier-trigger.js +99 -0
- package/dist/migrations/remove-modifier-trigger.js.map +1 -0
- package/dist/scaffold/core-version-map.d.ts +2 -0
- package/dist/scaffold/core-version-map.d.ts.map +1 -0
- package/dist/scaffold/core-version-map.js +130 -0
- package/dist/scaffold/core-version-map.js.map +1 -0
- package/dist/scaffold/create-project.d.ts +15 -0
- package/dist/scaffold/create-project.d.ts.map +1 -0
- package/dist/scaffold/create-project.js +84 -0
- package/dist/scaffold/create-project.js.map +1 -0
- package/dist/scaffold/package-root.d.ts +2 -0
- package/dist/scaffold/package-root.d.ts.map +1 -0
- package/dist/scaffold/package-root.js +34 -0
- package/dist/scaffold/package-root.js.map +1 -0
- package/dist/scaffold/templates.d.ts +11 -0
- package/dist/scaffold/templates.d.ts.map +1 -0
- package/dist/scaffold/templates.js +26 -0
- package/dist/scaffold/templates.js.map +1 -0
- package/dist/scaffold/validators.d.ts +2 -0
- package/dist/scaffold/validators.d.ts.map +1 -0
- package/dist/scaffold/validators.js +32 -0
- package/dist/scaffold/validators.js.map +1 -0
- package/package.json +7 -3
- package/templates/advanced/README.md.template +23 -0
- package/templates/advanced/index.html.template +23 -0
- package/templates/advanced/package.json.template +20 -0
- package/templates/advanced/src/App.ts.template +54 -0
- package/templates/advanced/src/main.ts.template +4 -0
- package/templates/advanced/tsconfig.json.template +14 -0
- package/templates/advanced/vite.config.ts.template +11 -0
- package/templates/basic/README.md.template +17 -0
- package/templates/basic/index.html.template +22 -0
- package/templates/basic/package.json.template +20 -0
- package/templates/basic/src/App.ts.template +35 -0
- package/templates/basic/src/main.ts.template +4 -0
- package/templates/basic/tsconfig.json.template +14 -0
- package/templates/basic/vite.config.ts.template +11 -0
package/dist/commands/init.js
CHANGED
|
@@ -1,729 +1,219 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tacho CLI - Init Command
|
|
3
3
|
*
|
|
4
|
-
* Initialize new TachUI projects
|
|
4
|
+
* Initialize new TachUI projects from file-based templates.
|
|
5
5
|
*/
|
|
6
|
-
import { existsSync,
|
|
7
|
-
import {
|
|
6
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
7
|
+
import { basename, join } from 'node:path';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import { Command } from 'commander';
|
|
10
10
|
import ora from 'ora';
|
|
11
11
|
import prompts from 'prompts';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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;
|
|
12
|
+
import { createProject } from '../scaffold/create-project.js';
|
|
13
|
+
import { resolveCoreVersionFromMap } from '../scaffold/core-version-map.js';
|
|
14
|
+
import { resolvePackageRoot } from '../scaffold/package-root.js';
|
|
15
|
+
import { getTemplateDefinition, listTemplateDefinitions } from '../scaffold/templates.js';
|
|
16
|
+
import { validateProjectName } from '../scaffold/validators.js';
|
|
17
|
+
export function readCliVersion(packageRoot) {
|
|
18
|
+
const packageJsonPath = join(packageRoot, 'package.json');
|
|
19
|
+
if (!existsSync(packageJsonPath)) {
|
|
20
|
+
return null;
|
|
123
21
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
22
|
+
try {
|
|
23
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
24
|
+
return typeof packageJson.version === 'string' ? packageJson.version : null;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
127
28
|
}
|
|
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
29
|
}
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
}
|
|
30
|
+
export function isValidSemverLike(value) {
|
|
31
|
+
return typeof value === 'string' && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(value);
|
|
377
32
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
33
|
+
export async function resolveLatestPublishedCoreVersion() {
|
|
34
|
+
const registryLatestUrl = process.env.TACHUI_CORE_LATEST_URL || 'https://registry.npmjs.org/@tachui/core/latest';
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
const timeout = setTimeout(() => controller.abort(), 2500);
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(registryLatestUrl, {
|
|
39
|
+
signal: controller.signal,
|
|
40
|
+
});
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const body = (await response.json());
|
|
45
|
+
return isValidSemverLike(body.version) ? body.version : null;
|
|
387
46
|
}
|
|
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;
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
575
49
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
579
|
-
background-color: #f5f5f7;
|
|
50
|
+
finally {
|
|
51
|
+
clearTimeout(timeout);
|
|
580
52
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
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
|
-
};
|
|
53
|
+
}
|
|
54
|
+
export async function resolveDefaultTachuiVersion(cliVersion, resolveRegistryVersion = resolveLatestPublishedCoreVersion) {
|
|
55
|
+
const registryVersion = await resolveRegistryVersion();
|
|
56
|
+
if (registryVersion) {
|
|
57
|
+
return {
|
|
58
|
+
version: registryVersion,
|
|
59
|
+
source: 'registry',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const mappedVersion = resolveCoreVersionFromMap(cliVersion);
|
|
63
|
+
if (mappedVersion) {
|
|
64
|
+
return {
|
|
65
|
+
version: mappedVersion,
|
|
66
|
+
source: 'compatibility-map',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
throw new Error('Unable to determine a default @tachui/core version. Pass --tachui-version explicitly.');
|
|
70
|
+
}
|
|
71
|
+
function printTemplates() {
|
|
72
|
+
console.log(chalk.cyan('\nAvailable templates:\n'));
|
|
73
|
+
for (const template of listTemplateDefinitions()) {
|
|
74
|
+
console.log(`${chalk.green(template.id)} - ${template.description}`);
|
|
75
|
+
}
|
|
76
|
+
console.log('');
|
|
77
|
+
}
|
|
78
|
+
function resolveInstallCommand(packageManager) {
|
|
79
|
+
return packageManager === 'pnpm' ? 'pnpm install' : 'npm install';
|
|
80
|
+
}
|
|
81
|
+
function resolveDevCommand(packageManager) {
|
|
82
|
+
return packageManager === 'pnpm' ? 'pnpm dev' : 'npm run dev';
|
|
83
|
+
}
|
|
84
|
+
function validateTargetToProjectName(target, cwd) {
|
|
85
|
+
const projectName = target === '.' ? basename(cwd) : basename(target);
|
|
86
|
+
return validateProjectName(projectName);
|
|
87
|
+
}
|
|
643
88
|
export const initCommand = new Command('init')
|
|
644
89
|
.description('Initialize a new TachUI project')
|
|
645
|
-
.argument('[
|
|
646
|
-
.option('-t, --template <template>', 'Project template (basic,
|
|
647
|
-
.option('-y, --yes', 'Skip prompts and use
|
|
648
|
-
.
|
|
90
|
+
.argument('[target]', 'Project directory name (use "." for current directory)')
|
|
91
|
+
.option('-t, --template <template>', 'Project template (basic, advanced)', 'basic')
|
|
92
|
+
.option('-y, --yes', 'Skip prompts and use provided options')
|
|
93
|
+
.option('--tachui-version <version>', 'TachUI package version to scaffold')
|
|
94
|
+
.option('--package-manager <packageManager>', 'Package manager for next-step instructions (npm, pnpm)', 'npm')
|
|
95
|
+
.option('--list-templates', 'List available templates')
|
|
96
|
+
.action(async (target, options) => {
|
|
649
97
|
try {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
98
|
+
const packageRoot = resolvePackageRoot(import.meta.url);
|
|
99
|
+
const templatesRoot = join(packageRoot, 'templates');
|
|
100
|
+
const cliVersion = readCliVersion(packageRoot);
|
|
101
|
+
if (options?.listTemplates) {
|
|
102
|
+
printTemplates();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const resolvedDefaultVersion = options?.tachuiVersion
|
|
106
|
+
? null
|
|
107
|
+
: await resolveDefaultTachuiVersion(cliVersion);
|
|
108
|
+
if (resolvedDefaultVersion?.source === 'compatibility-map') {
|
|
109
|
+
console.log(chalk.yellow('Warning: could not reach npm registry; using CLI compatibility map for default TachUI version.'));
|
|
110
|
+
}
|
|
111
|
+
let finalTarget = target;
|
|
112
|
+
let finalTemplateId = (options?.template || 'basic').toLowerCase();
|
|
113
|
+
let finalTachuiVersion = options?.tachuiVersion || resolvedDefaultVersion?.version || '';
|
|
114
|
+
let finalPackageManager = options?.packageManager || 'npm';
|
|
653
115
|
if (!options?.yes) {
|
|
654
|
-
const
|
|
116
|
+
const responses = await prompts([
|
|
655
117
|
{
|
|
656
118
|
type: 'text',
|
|
657
|
-
name: '
|
|
658
|
-
message: 'Project
|
|
659
|
-
initial:
|
|
660
|
-
validate: (value) =>
|
|
119
|
+
name: 'target',
|
|
120
|
+
message: 'Project directory:',
|
|
121
|
+
initial: finalTarget || 'my-tachui-app',
|
|
122
|
+
validate: (value) => {
|
|
123
|
+
const result = validateTargetToProjectName(value, process.cwd());
|
|
124
|
+
return result ?? true;
|
|
125
|
+
},
|
|
661
126
|
},
|
|
662
127
|
{
|
|
663
128
|
type: 'select',
|
|
664
129
|
name: 'template',
|
|
665
130
|
message: 'Choose a template:',
|
|
666
|
-
choices:
|
|
131
|
+
choices: listTemplateDefinitions().map(template => ({
|
|
667
132
|
title: template.name,
|
|
668
133
|
description: template.description,
|
|
669
|
-
value:
|
|
134
|
+
value: template.id,
|
|
670
135
|
})),
|
|
671
|
-
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
type: 'text',
|
|
139
|
+
name: 'tachuiVersion',
|
|
140
|
+
message: 'TachUI version for generated dependencies:',
|
|
141
|
+
initial: finalTachuiVersion,
|
|
142
|
+
validate: (value) => isValidSemverLike(value.trim())
|
|
143
|
+
? true
|
|
144
|
+
: 'Version must be a valid semver string (for example 0.8.8-alpha)',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'select',
|
|
148
|
+
name: 'packageManager',
|
|
149
|
+
message: 'Package manager:',
|
|
150
|
+
choices: [
|
|
151
|
+
{ title: 'npm', value: 'npm' },
|
|
152
|
+
{ title: 'pnpm', value: 'pnpm' },
|
|
153
|
+
],
|
|
154
|
+
initial: finalPackageManager === 'pnpm' ? 1 : 0,
|
|
672
155
|
},
|
|
673
156
|
]);
|
|
674
|
-
if (!
|
|
157
|
+
if (!responses.target) {
|
|
675
158
|
console.log(chalk.yellow('Operation cancelled'));
|
|
676
159
|
return;
|
|
677
160
|
}
|
|
678
|
-
|
|
679
|
-
|
|
161
|
+
if (!responses.template || !responses.tachuiVersion || !responses.packageManager) {
|
|
162
|
+
console.log(chalk.yellow('Operation cancelled'));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
finalTarget = responses.target;
|
|
166
|
+
finalTemplateId = responses.template;
|
|
167
|
+
finalTachuiVersion = responses.tachuiVersion;
|
|
168
|
+
finalPackageManager = responses.packageManager;
|
|
680
169
|
}
|
|
681
|
-
if (!
|
|
682
|
-
console.error(chalk.red('Project
|
|
170
|
+
else if (!finalTarget) {
|
|
171
|
+
console.error(chalk.red('Project target is required when using --yes'));
|
|
683
172
|
process.exit(1);
|
|
684
173
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
console.error(chalk.red(`Template "${selectedTemplate}" not found`));
|
|
174
|
+
if (!finalTarget) {
|
|
175
|
+
console.error(chalk.red('Project target is required'));
|
|
688
176
|
process.exit(1);
|
|
689
177
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
console.error(chalk.red(`Directory "${finalProjectName}" already exists`));
|
|
178
|
+
finalTachuiVersion = finalTachuiVersion.trim();
|
|
179
|
+
if (!isValidSemverLike(finalTachuiVersion)) {
|
|
180
|
+
console.error(chalk.red(`Invalid --tachui-version "${finalTachuiVersion}". Expected a semver value like 0.8.8-alpha.`));
|
|
694
181
|
process.exit(1);
|
|
695
182
|
}
|
|
696
|
-
const
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
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);
|
|
183
|
+
const template = getTemplateDefinition(finalTemplateId);
|
|
184
|
+
if (!template) {
|
|
185
|
+
const available = listTemplateDefinitions()
|
|
186
|
+
.map(item => item.id)
|
|
187
|
+
.join(', ');
|
|
188
|
+
console.error(chalk.red(`Unknown template "${finalTemplateId}". Available templates: ${available}`));
|
|
189
|
+
process.exit(1);
|
|
710
190
|
}
|
|
711
|
-
|
|
712
|
-
|
|
191
|
+
const packageManager = finalPackageManager === 'pnpm' ? 'pnpm' : 'npm';
|
|
192
|
+
const spinner = ora('Creating TachUI project...').start();
|
|
193
|
+
const result = createProject({
|
|
194
|
+
cwd: process.cwd(),
|
|
195
|
+
target: finalTarget,
|
|
196
|
+
tachuiVersion: finalTachuiVersion,
|
|
197
|
+
template,
|
|
198
|
+
templatesRoot,
|
|
199
|
+
});
|
|
200
|
+
spinner.succeed('Project created successfully');
|
|
201
|
+
const installCommand = resolveInstallCommand(packageManager);
|
|
202
|
+
const devCommand = resolveDevCommand(packageManager);
|
|
203
|
+
const cdLine = finalTarget === '.' ? null : `cd ${finalTarget}`;
|
|
713
204
|
console.log(`
|
|
714
|
-
${chalk.green('
|
|
715
|
-
${chalk.green('
|
|
716
|
-
${chalk.green('
|
|
205
|
+
${chalk.green('Project:')} ${chalk.cyan(result.projectName)}
|
|
206
|
+
${chalk.green('Location:')} ${result.projectPath}
|
|
207
|
+
${chalk.green('Template:')} ${template.name}
|
|
208
|
+
${chalk.green('TachUI version:')} ${finalTachuiVersion}
|
|
209
|
+
${chalk.green('Files created:')} ${result.createdFiles}
|
|
717
210
|
|
|
718
|
-
${chalk.yellow('
|
|
719
|
-
${template.features.map(
|
|
211
|
+
${chalk.yellow('Included features:')}
|
|
212
|
+
${template.features.map(feature => ` - ${feature}`).join('\n')}
|
|
720
213
|
|
|
721
214
|
${chalk.yellow('Next steps:')}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
npm run dev
|
|
725
|
-
|
|
726
|
-
${chalk.green('Happy coding with TachUI! 🚀')}
|
|
215
|
+
${cdLine ? ` ${cdLine}\n` : ''} ${installCommand}
|
|
216
|
+
${devCommand}
|
|
727
217
|
`);
|
|
728
218
|
}
|
|
729
219
|
catch (error) {
|