@telemetryos/cli 1.9.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/commands/auth.js +8 -15
  3. package/dist/commands/init.js +131 -68
  4. package/dist/commands/publish.d.ts +22 -0
  5. package/dist/commands/publish.js +238 -0
  6. package/dist/index.js +2 -0
  7. package/dist/plugins/math-tools.d.ts +2 -0
  8. package/dist/plugins/math-tools.js +18 -0
  9. package/dist/services/api-client.d.ts +18 -0
  10. package/dist/services/api-client.js +70 -0
  11. package/dist/services/archiver.d.ts +4 -0
  12. package/dist/services/archiver.js +65 -0
  13. package/dist/services/build-poller.d.ts +10 -0
  14. package/dist/services/build-poller.js +63 -0
  15. package/dist/services/cli-config.d.ts +10 -0
  16. package/dist/services/cli-config.js +45 -0
  17. package/dist/services/generate-application.d.ts +2 -1
  18. package/dist/services/generate-application.js +31 -32
  19. package/dist/services/project-config.d.ts +24 -0
  20. package/dist/services/project-config.js +51 -0
  21. package/dist/services/run-server.js +29 -73
  22. package/dist/types/api.d.ts +44 -0
  23. package/dist/types/api.js +1 -0
  24. package/dist/types/applications.d.ts +44 -0
  25. package/dist/types/applications.js +1 -0
  26. package/dist/utils/ansi.d.ts +10 -0
  27. package/dist/utils/ansi.js +10 -0
  28. package/dist/utils/path-utils.d.ts +55 -0
  29. package/dist/utils/path-utils.js +99 -0
  30. package/package.json +4 -2
  31. package/templates/vite-react-typescript/CLAUDE.md +14 -6
  32. package/templates/vite-react-typescript/_claude/skills/tos-architecture/SKILL.md +4 -28
  33. package/templates/vite-react-typescript/_claude/skills/tos-multi-mode/SKILL.md +359 -0
  34. package/templates/vite-react-typescript/_claude/skills/tos-render-design/SKILL.md +304 -12
  35. package/templates/vite-react-typescript/_claude/skills/tos-render-kiosk-design/SKILL.md +384 -0
  36. package/templates/vite-react-typescript/_claude/skills/tos-render-signage-design/SKILL.md +515 -0
  37. package/templates/vite-react-typescript/_claude/skills/tos-render-ui-design/SKILL.md +325 -0
  38. package/templates/vite-react-typescript/_claude/skills/tos-requirements/SKILL.md +405 -125
  39. package/templates/vite-react-typescript/_claude/skills/tos-store-sync/SKILL.md +96 -5
  40. package/templates/vite-react-typescript/_claude/skills/tos-weather-api/SKILL.md +443 -269
  41. package/templates/vite-react-typescript/index.html +1 -1
@@ -0,0 +1,384 @@
1
+ ---
2
+ name: tos-render-kiosk-design
3
+ description: Design patterns for interactive TelemetryOS kiosks. Use AFTER reading tos-render-ui-design. Covers touch interaction, onClick handlers, idle timeout, touch targets, and navigation state.
4
+ ---
5
+
6
+ # Interactive Kiosk Design (Touch-Enabled)
7
+
8
+ For TelemetryOS render views where users **interact with the screen** via touch or click.
9
+
10
+ > **Prerequisites:** Read `tos-render-ui-design` first for foundation concepts (rem scaling, safe zones, text sizing, responsive layouts).
11
+
12
+ ---
13
+
14
+ ## What is an Interactive Kiosk?
15
+
16
+ **Use for:** Wayfinding, directories, check-in systems, search interfaces
17
+
18
+ Interactive kiosks allow user interaction with these characteristics:
19
+
20
+ - Users can touch/click elements on screen
21
+ - onClick handlers for buttons, navigation, forms
22
+ - Idle timeout returns to home screen after inactivity
23
+ - Touch feedback with `:active` states (not `:hover`)
24
+ - Manages interaction state (current screen, navigation history)
25
+
26
+ **Examples:**
27
+ - Wayfinding kiosks in shopping malls
28
+ - Hotel check-in systems
29
+ - Building directory search
30
+ - Product catalog browsers
31
+ - Information lookup stations
32
+
33
+ ---
34
+
35
+ ## Interaction Patterns
36
+
37
+ ### onClick Handlers
38
+
39
+ Add click handlers to interactive elements:
40
+
41
+ ```typescript
42
+ import { useState } from 'react'
43
+
44
+ function Render() {
45
+ const [screen, setScreen] = useState('home')
46
+
47
+ return (
48
+ <div className="render">
49
+ {screen === 'home' && (
50
+ <button
51
+ className="kiosk-button"
52
+ onClick={() => setScreen('search')}
53
+ >
54
+ Search Directory
55
+ </button>
56
+ )}
57
+ {screen === 'search' && (
58
+ <SearchScreen onBack={() => setScreen('home')} />
59
+ )}
60
+ </div>
61
+ )
62
+ }
63
+ ```
64
+
65
+ **Key points:**
66
+ - Use standard React `onClick` handlers
67
+ - Manage screen/navigation state with `useState` or device store
68
+ - Provide clear navigation (back buttons, home button)
69
+ - Keep navigation shallow (avoid deep nested screens)
70
+
71
+ ### Touch Feedback (:active states)
72
+
73
+ Use `:active` pseudo-class for touch feedback (NOT `:hover`):
74
+
75
+ ```css
76
+ .kiosk-button {
77
+ padding: 2rem 4rem;
78
+ font-size: 3rem;
79
+ background: blue;
80
+ color: white;
81
+ border: none;
82
+ border-radius: 1rem;
83
+ transition: transform 0.1s, background 0.1s;
84
+ }
85
+
86
+ /* Touch feedback - user sees visual response when tapping */
87
+ .kiosk-button:active {
88
+ transform: scale(0.95);
89
+ background: darkblue;
90
+ }
91
+ ```
92
+
93
+ **Why :active instead of :hover?**
94
+ - Touch devices don't have hover
95
+ - `:active` triggers on touch/click
96
+ - Provides immediate visual feedback
97
+ - Shows user their tap was registered
98
+
99
+ **Feedback strategies:**
100
+ - Scale down slightly (0.95-0.98)
101
+ - Darken background color
102
+ - Change border/shadow
103
+ - Keep transition fast (0.1s)
104
+
105
+ ### Touch Target Sizing
106
+
107
+ Make touch targets large enough to tap accurately:
108
+
109
+ ```css
110
+ /* Minimum sizes for touch targets */
111
+ .kiosk-button {
112
+ min-width: 15rem; /* Large enough to tap */
113
+ min-height: 8rem;
114
+ padding: 2rem 4rem;
115
+ font-size: 3rem; /* Large, readable text */
116
+ }
117
+
118
+ /* Gap between interactive elements */
119
+ .button-group {
120
+ display: flex;
121
+ gap: 1rem; /* Minimum 1rem spacing */
122
+ }
123
+ ```
124
+
125
+ **Touch target guidelines:**
126
+
127
+ | Element | Min Height | Min Padding | Font Size | Gap |
128
+ |---------|-----------|-------------|-----------|-----|
129
+ | Button | 8rem | 2rem | 3rem | 1rem |
130
+ | Nav item | 8rem | 2rem | 3rem | 1rem |
131
+ | Input field | 8rem | 2rem | 3rem | 1rem |
132
+
133
+ **Why large targets?**
134
+ - Users tap with fingers, not precise mouse cursor
135
+ - Prevents accidental taps on adjacent elements
136
+ - Reduces user frustration
137
+ - Improves accessibility
138
+
139
+ ---
140
+
141
+ ## State Management
142
+
143
+ ### Idle Timeout Pattern
144
+
145
+ Return to home screen after inactivity to prevent kiosks from staying on user's screen:
146
+
147
+ ```typescript
148
+ import { useState, useEffect } from 'react'
149
+
150
+ function Render() {
151
+ const [screen, setScreen] = useState('home')
152
+ const [lastInteraction, setLastInteraction] = useState(Date.now())
153
+
154
+ // Return to home after 30 seconds of inactivity
155
+ useEffect(() => {
156
+ const timeout = setTimeout(() => {
157
+ const elapsed = Date.now() - lastInteraction
158
+ if (elapsed > 30000 && screen !== 'home') {
159
+ setScreen('home')
160
+ }
161
+ }, 30000)
162
+
163
+ return () => clearTimeout(timeout)
164
+ }, [lastInteraction, screen])
165
+
166
+ const handleInteraction = (newScreen: string) => {
167
+ setScreen(newScreen)
168
+ setLastInteraction(Date.now())
169
+ }
170
+
171
+ return (
172
+ <div className="render">
173
+ {screen === 'home' && (
174
+ <button onClick={() => handleInteraction('search')}>
175
+ Search
176
+ </button>
177
+ )}
178
+ </div>
179
+ )
180
+ }
181
+ ```
182
+
183
+ **Key points:**
184
+ - Track last interaction timestamp
185
+ - Clear timeout on cleanup
186
+ - Configurable timeout duration (typically 15-60 seconds)
187
+ - Update timestamp on any interaction
188
+ - Only reset if not already on home screen
189
+
190
+ ### Device Store for Navigation
191
+
192
+ Use device store to persist state across composition changes:
193
+
194
+ ```typescript
195
+ // hooks/store.ts
196
+ import { createUseDeviceStoreState } from '@telemetryos/sdk/react'
197
+
198
+ export const useKioskScreenState = createUseDeviceStoreState<string>(
199
+ 'kiosk-screen',
200
+ 'home'
201
+ )
202
+
203
+ // Render.tsx
204
+ import { useKioskScreenState } from '../hooks/store'
205
+
206
+ export function Render() {
207
+ const [_isLoading, screen, setScreen] = useKioskScreenState()
208
+
209
+ return (
210
+ <div className="render">
211
+ {screen === 'home' && <HomeScreen />}
212
+ {screen === 'search' && <SearchScreen />}
213
+ </div>
214
+ )
215
+ }
216
+ ```
217
+
218
+ **Why device store for kiosk navigation?**
219
+ - Persists state on the device (survives composition changes)
220
+ - Doesn't sync to other devices (screen state is device-local)
221
+ - Maintains kiosk state during playlist rotation
222
+ - Each physical device has independent navigation
223
+
224
+ **Don't use instance store** - That syncs across all devices. For kiosk navigation, use device store so each kiosk has independent state.
225
+
226
+ ---
227
+
228
+ ## Complete Example
229
+
230
+ ### Interactive Kiosk with Navigation
231
+
232
+ ```typescript
233
+ // Render.tsx - Interactive kiosk with navigation
234
+ import { useState, useEffect } from 'react'
235
+ import { useUiScaleToSetRem } from '@telemetryos/sdk/react'
236
+ import { useUiScaleStoreState } from '../hooks/store'
237
+ import './Render.css'
238
+
239
+ export function Render() {
240
+ const [isLoading, uiScale] = useUiScaleStoreState()
241
+ const [screen, setScreen] = useState('home')
242
+ const [lastInteraction, setLastInteraction] = useState(Date.now())
243
+
244
+ useUiScaleToSetRem(uiScale)
245
+
246
+ // Idle timeout - return to home after 30 seconds
247
+ useEffect(() => {
248
+ const timeout = setTimeout(() => {
249
+ const elapsed = Date.now() - lastInteraction
250
+ if (elapsed > 30000 && screen !== 'home') {
251
+ setScreen('home')
252
+ }
253
+ }, 30000)
254
+
255
+ return () => clearTimeout(timeout)
256
+ }, [lastInteraction, screen])
257
+
258
+ const handleInteraction = (newScreen: string) => {
259
+ setScreen(newScreen)
260
+ setLastInteraction(Date.now())
261
+ }
262
+
263
+ if (isLoading) return null
264
+
265
+ return (
266
+ <div className="render">
267
+ {screen === 'home' && (
268
+ <div className="kiosk-home">
269
+ <h1 className="kiosk-home__title">Welcome</h1>
270
+ <button
271
+ className="kiosk-button"
272
+ onClick={() => handleInteraction('search')}
273
+ >
274
+ Search Directory
275
+ </button>
276
+ <button
277
+ className="kiosk-button"
278
+ onClick={() => handleInteraction('map')}
279
+ >
280
+ View Map
281
+ </button>
282
+ </div>
283
+ )}
284
+
285
+ {screen === 'search' && (
286
+ <SearchScreen onBack={() => handleInteraction('home')} />
287
+ )}
288
+
289
+ {screen === 'map' && (
290
+ <MapScreen onBack={() => handleInteraction('home')} />
291
+ )}
292
+ </div>
293
+ )
294
+ }
295
+ ```
296
+
297
+ ```css
298
+ /* Render.css - Interactive kiosk styles */
299
+ .kiosk-home {
300
+ display: flex;
301
+ flex-direction: column;
302
+ align-items: center;
303
+ justify-content: center;
304
+ gap: 3rem;
305
+ height: 100%;
306
+ }
307
+
308
+ .kiosk-home__title {
309
+ font-size: 6rem;
310
+ margin: 0;
311
+ }
312
+
313
+ /* Touch-friendly button with :active feedback */
314
+ .kiosk-button {
315
+ min-width: 20rem;
316
+ min-height: 8rem;
317
+ padding: 2rem 4rem;
318
+ font-size: 3rem;
319
+ background: #0066cc;
320
+ color: white;
321
+ border: none;
322
+ border-radius: 1rem;
323
+ cursor: pointer;
324
+ transition: transform 0.1s, background 0.1s;
325
+ }
326
+
327
+ /* Touch feedback - scales down when tapped */
328
+ .kiosk-button:active {
329
+ transform: scale(0.95);
330
+ background: #0052a3;
331
+ }
332
+ ```
333
+
334
+ ---
335
+
336
+ ## Touch Interaction Guidelines
337
+
338
+ ### Visual Feedback Requirements
339
+
340
+ Every interactive element should provide visual feedback:
341
+
342
+ ✅ Use `:active` state (not `:hover`)
343
+ ✅ Visual change must be immediate (< 100ms)
344
+ ✅ Change should be obvious (scale, color, shadow)
345
+ ✅ Feedback on tap down, not just tap up
346
+
347
+ ### Touch Target Checklist
348
+
349
+ - [ ] All buttons are at least 8rem height
350
+ - [ ] Padding is at least 2rem
351
+ - [ ] Font size is at least 3rem
352
+ - [ ] Gap between buttons is at least 1rem
353
+ - [ ] Touch targets don't overlap
354
+ - [ ] `:active` states provide visual feedback
355
+
356
+ ---
357
+
358
+ ## Common Mistakes
359
+
360
+ | Mistake | Problem | Fix |
361
+ |---------|---------|-----|
362
+ | Using `:hover` on interactive kiosks | No hover on touch devices | Use `:active` instead for touch feedback |
363
+ | Small touch targets on kiosks | Hard to tap accurately | Minimum 8rem height, 2rem padding, 3rem font |
364
+ | No idle timeout on kiosks | Kiosk stays on user's screen | Add useEffect timeout logic |
365
+ | No touch feedback on kiosks | User unsure if tap registered | Add `:active` state animations |
366
+ | Using instance store for navigation | State syncs to other devices | Use device store for local navigation |
367
+
368
+ ---
369
+
370
+ ## Testing Your Kiosk
371
+
372
+ ✅ **Test on touch devices** - Not just mouse, verify touch works
373
+ ✅ **Verify idle timeout** - Confirm it returns to home after inactivity
374
+ ✅ **Check touch targets** - Ensure all buttons are large enough
375
+ ✅ **Ensure :active feedback** - Visual response should be obvious
376
+ ✅ **Test navigation flow** - Complete user journeys should work
377
+ ✅ **Verify state persistence** - State should survive composition rotation
378
+
379
+ ---
380
+
381
+ ## See Also
382
+
383
+ - `tos-render-ui-design` - Foundation concepts for all render views (UI scaling, rem usage, responsive layouts)
384
+ - `tos-render-signage-design` - For display-only patterns without interaction