create-sygnal-app 1.0.0 → 1.0.2

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.
Files changed (52) hide show
  1. package/index.js +15 -4
  2. package/package.json +5 -2
  3. package/template-astro/package.json +2 -2
  4. package/template-astro/public/favicon.svg +7 -0
  5. package/template-astro/public/style.css +274 -0
  6. package/template-astro/src/components/App.jsx +99 -0
  7. package/template-astro/src/components/TaskItem.jsx +23 -0
  8. package/template-astro/src/pages/index.astro +7 -19
  9. package/template-astro-ts/astro.config.mjs +6 -0
  10. package/template-astro-ts/package.json +18 -0
  11. package/template-astro-ts/public/favicon.svg +7 -0
  12. package/template-astro-ts/public/style.css +274 -0
  13. package/template-astro-ts/src/components/App.tsx +118 -0
  14. package/template-astro-ts/src/components/TaskItem.tsx +38 -0
  15. package/template-astro-ts/src/env.d.ts +1 -0
  16. package/template-astro-ts/src/pages/index.astro +19 -0
  17. package/template-astro-ts/tsconfig.json +7 -0
  18. package/template-vike/package.json +2 -2
  19. package/template-vike/pages/+Layout.jsx +4 -1
  20. package/template-vike/pages/about/+Page.jsx +1 -1
  21. package/template-vike/pages/index/+Page.jsx +19 -8
  22. package/template-vike/public/favicon.svg +7 -0
  23. package/template-vike/public/style.css +87 -27
  24. package/template-vike-ts/package.json +19 -0
  25. package/template-vike-ts/pages/+Head.tsx +3 -0
  26. package/template-vike-ts/pages/+Layout.tsx +25 -0
  27. package/template-vike-ts/pages/+config.ts +6 -0
  28. package/template-vike-ts/pages/about/+Page.tsx +22 -0
  29. package/template-vike-ts/pages/about/+config.ts +3 -0
  30. package/template-vike-ts/pages/index/+Page.tsx +50 -0
  31. package/template-vike-ts/pages/index/+config.ts +3 -0
  32. package/template-vike-ts/public/favicon.svg +7 -0
  33. package/template-vike-ts/public/style.css +131 -0
  34. package/template-vike-ts/tsconfig.json +14 -0
  35. package/template-vike-ts/vite.config.ts +10 -0
  36. package/template-vite/index.html +6 -2
  37. package/template-vite/package.json +2 -2
  38. package/template-vite/public/favicon.svg +7 -0
  39. package/template-vite/src/App.jsx +84 -13
  40. package/template-vite/src/components/TaskItem.jsx +23 -0
  41. package/template-vite/src/main.js +1 -6
  42. package/template-vite/src/style.css +247 -30
  43. package/template-vite-ts/index.html +16 -0
  44. package/template-vite-ts/package.json +18 -0
  45. package/template-vite-ts/public/favicon.svg +7 -0
  46. package/template-vite-ts/src/App.tsx +118 -0
  47. package/template-vite-ts/src/components/TaskItem.tsx +38 -0
  48. package/template-vite-ts/src/main.ts +5 -0
  49. package/template-vite-ts/src/style.css +274 -0
  50. package/template-vite-ts/tsconfig.json +14 -0
  51. package/template-vite-ts/vite.config.ts +6 -0
  52. package/template-astro/src/components/Counter.jsx +0 -25
package/index.js CHANGED
@@ -12,17 +12,14 @@ const TEMPLATES = {
12
12
  vite: {
13
13
  label: 'Vite (SPA)',
14
14
  hint: 'Single-page app with Vite + HMR',
15
- dir: 'template-vite',
16
15
  },
17
16
  vike: {
18
17
  label: 'Vike (SSR)',
19
18
  hint: 'File-based routing with SSR, layouts, and data fetching',
20
- dir: 'template-vike',
21
19
  },
22
20
  astro: {
23
21
  label: 'Astro',
24
22
  hint: 'Content-focused site with island hydration',
25
- dir: 'template-astro',
26
23
  },
27
24
  }
28
25
 
@@ -61,6 +58,19 @@ async function main() {
61
58
  process.exit(0)
62
59
  }
63
60
 
61
+ const language = await p.select({
62
+ message: 'Language:',
63
+ options: [
64
+ { value: 'js', label: 'JavaScript' },
65
+ { value: 'ts', label: 'TypeScript' },
66
+ ],
67
+ })
68
+
69
+ if (p.isCancel(language)) {
70
+ p.cancel('Cancelled.')
71
+ process.exit(0)
72
+ }
73
+
64
74
  const installDeps = await p.confirm({
65
75
  message: 'Install dependencies?',
66
76
  initialValue: true,
@@ -82,7 +92,8 @@ async function main() {
82
92
 
83
93
  // Copy template
84
94
  s.start('Scaffolding project...')
85
- const templateDir = join(__dirname, TEMPLATES[template].dir)
95
+ const templateName = language === 'ts' ? `template-${template}-ts` : `template-${template}`
96
+ const templateDir = join(__dirname, templateName)
86
97
  copyDir(templateDir, targetDir)
87
98
 
88
99
  // Update package.json name
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-sygnal-app",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Scaffold a new Sygnal project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,8 +9,11 @@
9
9
  "files": [
10
10
  "index.js",
11
11
  "template-vite",
12
+ "template-vite-ts",
12
13
  "template-vike",
13
- "template-astro"
14
+ "template-vike-ts",
15
+ "template-astro",
16
+ "template-astro-ts"
14
17
  ],
15
18
  "keywords": [
16
19
  "sygnal",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "my-sygnal-app",
3
- "version": "0.1.0",
4
3
  "private": true,
4
+ "version": "0.1.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "astro dev",
@@ -10,6 +10,6 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "astro": "^5.0.0",
13
- "sygnal": "^5.1.0"
13
+ "sygnal": "^5.1.1"
14
14
  }
15
15
  }
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 269 240" width="269" height="240">
2
+ <g fill="#1485EF">
3
+ <polygon points="95,104.5 44.5,82 142,24.5 248.5,25 175,69.5 154,67.5"/>
4
+ <polygon points="142,215.5 27.5,214 94,175.5 115,182.5 188,139.5 122.5,112 163,86.5 170,83.5 233,108.5 234.5,161"/>
5
+ <polygon points="102,168.5 44.5,144 45,102.5 145.5,144"/>
6
+ </g>
7
+ </svg>
@@ -0,0 +1,274 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ margin: 0;
6
+ padding: 0;
7
+ }
8
+
9
+ :root {
10
+ --color-bg: #0f1117;
11
+ --color-surface: #1a1d27;
12
+ --color-surface-2: #242837;
13
+ --color-border: #2e3346;
14
+ --color-text: #e4e6ef;
15
+ --color-text-muted: #8b8fa3;
16
+ --color-primary: #1485ef;
17
+ --color-primary-hover: #0d6dd6;
18
+ --color-success: #22c55e;
19
+ --color-danger: #ef4444;
20
+ --color-danger-hover: #dc2626;
21
+ --radius: 12px;
22
+ --radius-sm: 8px;
23
+ }
24
+
25
+ body {
26
+ min-height: 100vh;
27
+ background: var(--color-bg);
28
+ color: var(--color-text);
29
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
30
+ display: flex;
31
+ justify-content: center;
32
+ padding: 40px 20px;
33
+ -webkit-font-smoothing: antialiased;
34
+ }
35
+
36
+ /* ── Header ─────────────────────────────────────────────── */
37
+
38
+ .header {
39
+ text-align: center;
40
+ margin-bottom: 32px;
41
+ }
42
+
43
+ .logo-row {
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: center;
47
+ gap: 16px;
48
+ }
49
+
50
+ .logo {
51
+ width: 56px;
52
+ height: 50px;
53
+ }
54
+
55
+ h1 {
56
+ font-size: 2rem;
57
+ font-weight: 700;
58
+ letter-spacing: -0.02em;
59
+ text-align: left;
60
+ }
61
+
62
+ .tagline {
63
+ color: var(--color-text-muted);
64
+ font-size: 0.875rem;
65
+ text-align: left;
66
+ }
67
+
68
+ /* ── Card ───────────────────────────────────────────────── */
69
+
70
+ .card {
71
+ background: var(--color-surface);
72
+ border: 1px solid var(--color-border);
73
+ border-radius: var(--radius);
74
+ padding: 24px;
75
+ width: 100%;
76
+ max-width: 480px;
77
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
78
+ }
79
+
80
+ .card-header {
81
+ display: flex;
82
+ align-items: center;
83
+ justify-content: space-between;
84
+ margin-bottom: 20px;
85
+ }
86
+
87
+ .card-header h2 {
88
+ font-size: 1.125rem;
89
+ font-weight: 600;
90
+ }
91
+
92
+ .badge {
93
+ background: var(--color-surface-2);
94
+ color: var(--color-text-muted);
95
+ font-size: 0.75rem;
96
+ font-weight: 500;
97
+ padding: 4px 10px;
98
+ border-radius: 20px;
99
+ }
100
+
101
+ /* ── Add Form ───────────────────────────────────────────── */
102
+
103
+ .add-form {
104
+ display: flex;
105
+ gap: 8px;
106
+ margin-bottom: 20px;
107
+ }
108
+
109
+ .add-input {
110
+ flex: 1;
111
+ background: var(--color-surface-2);
112
+ border: 1px solid var(--color-border);
113
+ border-radius: var(--radius-sm);
114
+ padding: 10px 14px;
115
+ font-size: 0.875rem;
116
+ color: var(--color-text);
117
+ font-family: inherit;
118
+ outline: none;
119
+ transition: border-color 0.15s;
120
+ }
121
+
122
+ .add-input:focus {
123
+ border-color: var(--color-primary);
124
+ }
125
+
126
+ .add-input::placeholder {
127
+ color: var(--color-text-muted);
128
+ }
129
+
130
+ .add-btn {
131
+ background: var(--color-primary);
132
+ color: #fff;
133
+ border: none;
134
+ border-radius: var(--radius-sm);
135
+ padding: 10px 18px;
136
+ font-size: 0.875rem;
137
+ font-weight: 600;
138
+ font-family: inherit;
139
+ cursor: pointer;
140
+ transition: background 0.15s;
141
+ white-space: nowrap;
142
+ }
143
+
144
+ .add-btn:hover:not(:disabled) {
145
+ background: var(--color-primary-hover);
146
+ }
147
+
148
+ .add-btn:disabled {
149
+ opacity: 0.4;
150
+ cursor: not-allowed;
151
+ }
152
+
153
+ /* ── Task List ──────────────────────────────────────────── */
154
+
155
+ .tasks {
156
+ display: flex;
157
+ flex-direction: column;
158
+ gap: 2px;
159
+ }
160
+
161
+ .empty {
162
+ text-align: center;
163
+ padding: 32px 16px;
164
+ color: var(--color-text-muted);
165
+ font-size: 0.875rem;
166
+ }
167
+
168
+ /* ── Task Item ──────────────────────────────────────────── */
169
+
170
+ .task-item {
171
+ display: flex;
172
+ align-items: center;
173
+ gap: 10px;
174
+ padding: 10px 12px;
175
+ background: var(--color-surface-2);
176
+ border-radius: var(--radius-sm);
177
+ transition: background 0.1s;
178
+ }
179
+
180
+ .task-item:hover {
181
+ background: #2a2f40;
182
+ }
183
+
184
+ .task-item .toggle {
185
+ width: 28px;
186
+ height: 28px;
187
+ border-radius: 50%;
188
+ border: 2px solid var(--color-border);
189
+ background: transparent;
190
+ color: var(--color-text-muted);
191
+ font-size: 0.875rem;
192
+ cursor: pointer;
193
+ display: flex;
194
+ align-items: center;
195
+ justify-content: center;
196
+ flex-shrink: 0;
197
+ transition: border-color 0.15s, color 0.15s, background 0.15s;
198
+ padding: 0;
199
+ }
200
+
201
+ .task-item .toggle:hover {
202
+ border-color: var(--color-primary);
203
+ color: var(--color-primary);
204
+ }
205
+
206
+ .task-item.done .toggle {
207
+ background: var(--color-success);
208
+ border-color: var(--color-success);
209
+ color: #fff;
210
+ }
211
+
212
+ .task-text {
213
+ flex: 1;
214
+ font-size: 0.875rem;
215
+ transition: color 0.15s, text-decoration 0.15s;
216
+ }
217
+
218
+ .task-item.done .task-text {
219
+ color: var(--color-text-muted);
220
+ text-decoration: line-through;
221
+ }
222
+
223
+ .task-item .delete {
224
+ width: 28px;
225
+ height: 28px;
226
+ border-radius: 6px;
227
+ border: none;
228
+ background: transparent;
229
+ color: var(--color-text-muted);
230
+ font-size: 1.125rem;
231
+ cursor: pointer;
232
+ display: flex;
233
+ align-items: center;
234
+ justify-content: center;
235
+ flex-shrink: 0;
236
+ opacity: 0;
237
+ transition: opacity 0.15s, background 0.15s, color 0.15s;
238
+ padding: 0;
239
+ }
240
+
241
+ .task-item:hover .delete {
242
+ opacity: 1;
243
+ }
244
+
245
+ .task-item .delete:hover {
246
+ background: var(--color-danger);
247
+ color: #fff;
248
+ }
249
+
250
+ /* ── Footer ─────────────────────────────────────────────── */
251
+
252
+ .footer {
253
+ text-align: center;
254
+ margin-top: 24px;
255
+ color: var(--color-text-muted);
256
+ font-size: 0.8125rem;
257
+ }
258
+
259
+ .footer code {
260
+ background: var(--color-surface-2);
261
+ padding: 2px 6px;
262
+ border-radius: 4px;
263
+ font-size: 0.8125rem;
264
+ }
265
+
266
+ .footer a {
267
+ color: var(--color-primary);
268
+ text-decoration: none;
269
+ margin-left: 4px;
270
+ }
271
+
272
+ .footer a:hover {
273
+ text-decoration: underline;
274
+ }
@@ -0,0 +1,99 @@
1
+ import { xs, ABORT } from 'sygnal'
2
+ import TaskItem from './TaskItem.jsx'
3
+
4
+ function App({ state }) {
5
+ return (
6
+ <div className="app">
7
+ <header className="header">
8
+ <div className="logo-row">
9
+ <img src="/favicon.svg" alt="Sygnal" className="logo" />
10
+ <div>
11
+ <h1>Sygnal</h1>
12
+ <p className="tagline">Reactive components with pure functions</p>
13
+ </div>
14
+ </div>
15
+ </header>
16
+
17
+ <main>
18
+ <div className="card">
19
+ <div className="card-header">
20
+ <h2>Task Tracker</h2>
21
+ <span className="badge">{state.remaining} remaining</span>
22
+ </div>
23
+
24
+ <div className="add-form">
25
+ <input
26
+ type="text"
27
+ className="add-input"
28
+ placeholder="Add a new task..."
29
+ value={state.inputValue}
30
+ />
31
+ <button className="add-btn" attrs={{ disabled: !state.inputValue.trim() }}>Add</button>
32
+ </div>
33
+
34
+ {state.tasks.length === 0
35
+ ? <div className="empty"><p>No tasks yet. Add one above!</p></div>
36
+ : <collection of={TaskItem} from="tasks" className="tasks" />
37
+ }
38
+ </div>
39
+
40
+ <footer className="footer">
41
+ <p>
42
+ Edit <code>src/components/App.jsx</code> to get started &mdash;{' '}
43
+ <a href="https://sygnal.js.org" target="_blank" rel="noopener">Docs</a>
44
+ </p>
45
+ </footer>
46
+ </main>
47
+ </div>
48
+ )
49
+ }
50
+
51
+ App.initialState = {
52
+ inputValue: '',
53
+ nextId: 4,
54
+ tasks: [
55
+ { id: 1, text: 'Learn about Sygnal components', done: true },
56
+ { id: 2, text: 'Explore intent and model', done: false },
57
+ { id: 3, text: 'Build something awesome', done: false },
58
+ ],
59
+ }
60
+
61
+ App.calculated = {
62
+ remaining: (state) => state.tasks.filter(t => !t.done).length,
63
+ }
64
+
65
+ App.intent = ({ DOM, CHILD }) => ({
66
+ UPDATE_INPUT: DOM.input('.add-input').value(),
67
+ ADD_TASK: xs.merge(
68
+ DOM.click('.add-btn'),
69
+ DOM.keydown('.add-input').key().filter(key => key === 'Enter')
70
+ ),
71
+ TOGGLE_TASK: CHILD.select(TaskItem),
72
+ })
73
+
74
+ App.model = {
75
+ UPDATE_INPUT: (state, value) => ({
76
+ ...state,
77
+ inputValue: value,
78
+ }),
79
+
80
+ ADD_TASK: (state) => {
81
+ const text = state.inputValue.trim()
82
+ if (!text) return ABORT
83
+ return {
84
+ ...state,
85
+ inputValue: '',
86
+ nextId: state.nextId + 1,
87
+ tasks: [...state.tasks, { id: state.nextId, text, done: false }],
88
+ }
89
+ },
90
+
91
+ TOGGLE_TASK: (state, id) => ({
92
+ ...state,
93
+ tasks: state.tasks.map(task =>
94
+ task.id === id ? { ...task, done: !task.done } : task
95
+ ),
96
+ }),
97
+ }
98
+
99
+ export default App
@@ -0,0 +1,23 @@
1
+ function TaskItem({ state }) {
2
+ return (
3
+ <div className={`task-item ${state.done ? 'done' : ''}`}>
4
+ <button className="toggle">{state.done ? '\u2713' : '\u25CB'}</button>
5
+ <span className="task-text">{state.text}</span>
6
+ <button className="delete">x</button>
7
+ </div>
8
+ )
9
+ }
10
+
11
+ TaskItem.intent = ({ DOM }) => ({
12
+ TOGGLE: DOM.click('.toggle'),
13
+ DELETE: DOM.click('.delete'),
14
+ })
15
+
16
+ TaskItem.model = {
17
+ TOGGLE: {
18
+ PARENT: (state) => state.id,
19
+ },
20
+ DELETE: () => undefined,
21
+ }
22
+
23
+ export default TaskItem
@@ -1,31 +1,19 @@
1
1
  ---
2
- import Counter from '../components/Counter.jsx'
2
+ import App from '../components/App.jsx'
3
3
  ---
4
4
 
5
5
  <html lang="en">
6
6
  <head>
7
7
  <meta charset="UTF-8" />
8
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
8
9
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
9
10
  <title>Sygnal + Astro</title>
10
- <style>
11
- * { margin: 0; padding: 0; box-sizing: border-box; }
12
- body {
13
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
14
- display: flex;
15
- justify-content: center;
16
- align-items: center;
17
- min-height: 100vh;
18
- background: #f5f5f5;
19
- color: #333;
20
- }
21
- .page { text-align: center; }
22
- h1 { font-size: 2.5rem; margin-bottom: 2rem; color: #111; }
23
- </style>
11
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
12
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
13
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
14
+ <link rel="stylesheet" href="/style.css" />
24
15
  </head>
25
16
  <body>
26
- <div class="page">
27
- <h1>Sygnal + Astro</h1>
28
- <Counter client:load />
29
- </div>
17
+ <App client:load />
30
18
  </body>
31
19
  </html>
@@ -0,0 +1,6 @@
1
+ import { defineConfig } from 'astro/config'
2
+ import sygnal from 'sygnal/astro'
3
+
4
+ export default defineConfig({
5
+ integrations: [sygnal()],
6
+ })
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "my-sygnal-app",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "astro dev",
8
+ "build": "astro build",
9
+ "preview": "astro preview"
10
+ },
11
+ "dependencies": {
12
+ "astro": "^5.0.0",
13
+ "sygnal": "^5.1.1"
14
+ },
15
+ "devDependencies": {
16
+ "typescript": "^5.4.5"
17
+ }
18
+ }
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 269 240" width="269" height="240">
2
+ <g fill="#1485EF">
3
+ <polygon points="95,104.5 44.5,82 142,24.5 248.5,25 175,69.5 154,67.5"/>
4
+ <polygon points="142,215.5 27.5,214 94,175.5 115,182.5 188,139.5 122.5,112 163,86.5 170,83.5 233,108.5 234.5,161"/>
5
+ <polygon points="102,168.5 44.5,144 45,102.5 145.5,144"/>
6
+ </g>
7
+ </svg>