@telemetryos/cli 1.7.5 → 1.8.1
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/CHANGELOG.md +22 -0
- package/dist/commands/init.js +11 -0
- package/dist/services/generate-application.d.ts +1 -0
- package/dist/services/generate-application.js +127 -4
- package/dist/utils/validate-project-name.d.ts +19 -0
- package/dist/utils/validate-project-name.js +44 -0
- package/package.json +2 -2
- package/templates/vite-react-typescript/CLAUDE.md +68 -1244
- package/templates/vite-react-typescript/_claude/settings.local.json +17 -0
- package/templates/vite-react-typescript/_claude/skills/tos-architecture/SKILL.md +313 -0
- package/templates/vite-react-typescript/_claude/skills/tos-debugging/SKILL.md +299 -0
- package/templates/vite-react-typescript/_claude/skills/tos-media-api/SKILL.md +335 -0
- package/templates/vite-react-typescript/_claude/skills/tos-proxy-fetch/SKILL.md +319 -0
- package/templates/vite-react-typescript/_claude/skills/tos-render-design/SKILL.md +332 -0
- package/templates/vite-react-typescript/_claude/skills/tos-requirements/SKILL.md +252 -0
- package/templates/vite-react-typescript/_claude/skills/tos-settings-ui/SKILL.md +636 -0
- package/templates/vite-react-typescript/_claude/skills/tos-store-sync/SKILL.md +359 -0
- package/templates/vite-react-typescript/_claude/skills/tos-weather-api/SKILL.md +384 -0
- package/templates/vite-react-typescript/src/views/Render.css +1 -1
- package/templates/vite-react-typescript/src/views/Render.tsx +1 -1
- package/templates/vite-react-typescript/src/views/Settings.tsx +2 -2
- package/templates/vite-react-typescript/AGENTS.md +0 -7
- /package/templates/vite-react-typescript/{gitignore → _gitignore} +0 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tos-render-design
|
|
3
|
+
description: Design patterns for TelemetryOS Render views. Use when building or reviewing Render view layouts, handling responsive scaling, or ensuring digital signage best practices.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Render View Design
|
|
7
|
+
|
|
8
|
+
TelemetryOS Render views display on digital signage—TVs, video walls, and displays viewed from a distance with no user interaction. This fundamentally shapes how to design them.
|
|
9
|
+
|
|
10
|
+
> **Note:** The init project already provides base styles in `index.css` (viewport scaling, box-sizing) and `Render.css` (`.render` class with padding, overflow, flexbox). Build on these—don't override them.
|
|
11
|
+
|
|
12
|
+
## Digital Signage Constraints
|
|
13
|
+
|
|
14
|
+
### No User Interaction
|
|
15
|
+
|
|
16
|
+
Unless building for kiosk/touchscreen scenarios, assume **no mouse, keyboard, or touch input**:
|
|
17
|
+
|
|
18
|
+
```css
|
|
19
|
+
/* WRONG - No one will hover */
|
|
20
|
+
.button:hover {
|
|
21
|
+
background: blue;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* WRONG - No one will focus */
|
|
25
|
+
.input:focus {
|
|
26
|
+
outline: 2px solid blue;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Avoid `:hover`, `:focus`, `:active`, and similar interaction pseudo-classes.
|
|
31
|
+
|
|
32
|
+
### No Scrolling
|
|
33
|
+
|
|
34
|
+
Content **must fit the viewport**. There's no user to scroll:
|
|
35
|
+
|
|
36
|
+
```css
|
|
37
|
+
/* WRONG - Creates scrollbar no one can use */
|
|
38
|
+
.container {
|
|
39
|
+
overflow-y: scroll;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* WRONG - Content disappears off-screen */
|
|
43
|
+
.content {
|
|
44
|
+
height: 150vh;
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```css
|
|
49
|
+
/* CORRECT - Content contained */
|
|
50
|
+
.container {
|
|
51
|
+
height: 100vh;
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If content might overflow, truncate it or conditionally hide elements—never show a scrollbar.
|
|
57
|
+
|
|
58
|
+
## UI Scale Hooks
|
|
59
|
+
|
|
60
|
+
Displays range from tablets to 8K video walls. Standard CSS pixels create inconsistent sizing. The SDK provides hooks that redefine `rem` as viewport-relative:
|
|
61
|
+
|
|
62
|
+
### useUiScaleToSetRem(uiScale)
|
|
63
|
+
|
|
64
|
+
Sets the document's root font-size based on viewport. **Call once in your Render view:**
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { useUiScaleToSetRem } from '@telemetryos/sdk/react'
|
|
68
|
+
import { useUiScaleStoreState } from '../hooks/store'
|
|
69
|
+
|
|
70
|
+
export function Render() {
|
|
71
|
+
const [_isLoading, uiScale] = useUiScaleStoreState()
|
|
72
|
+
useUiScaleToSetRem(uiScale)
|
|
73
|
+
|
|
74
|
+
return <div className="content">...</div>
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**How it works:**
|
|
79
|
+
- At scale 1: `1rem` = 1% of viewport's longest dimension
|
|
80
|
+
- At scale 2: `1rem` = 2% of viewport's longest dimension
|
|
81
|
+
- A 2rem font occupies identical screen percentage on Full HD and 4K
|
|
82
|
+
|
|
83
|
+
### useUiAspectRatio()
|
|
84
|
+
|
|
85
|
+
Returns current aspect ratio, updating on resize:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { useUiAspectRatio } from '@telemetryos/sdk/react'
|
|
89
|
+
|
|
90
|
+
export function Render() {
|
|
91
|
+
const aspectRatio = useUiAspectRatio()
|
|
92
|
+
|
|
93
|
+
// > 1 = landscape, < 1 = portrait, = 1 = square
|
|
94
|
+
const isPortrait = aspectRatio < 1
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className={isPortrait ? 'portrait-layout' : 'landscape-layout'}>
|
|
98
|
+
...
|
|
99
|
+
</div>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Best Practices
|
|
105
|
+
|
|
106
|
+
### Use rem for Everything
|
|
107
|
+
|
|
108
|
+
All sizing should use `rem` to scale with the UI scale setting:
|
|
109
|
+
|
|
110
|
+
```css
|
|
111
|
+
/* CORRECT - Scales with viewport */
|
|
112
|
+
.title {
|
|
113
|
+
font-size: 4rem;
|
|
114
|
+
margin-bottom: 1rem;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.card {
|
|
118
|
+
padding: 2rem;
|
|
119
|
+
border-radius: 0.5rem;
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```css
|
|
124
|
+
/* WRONG - Fixed pixels don't scale */
|
|
125
|
+
.title {
|
|
126
|
+
font-size: 48px;
|
|
127
|
+
margin-bottom: 12px;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Title Safe Zone
|
|
132
|
+
|
|
133
|
+
The init project's `.render` class already applies ~3rem padding from screen edges (SMPTE ST 2046-1 standard for avoiding bezel cutoff). Keep this padding when building your layout.
|
|
134
|
+
|
|
135
|
+
### Minimum Text Size
|
|
136
|
+
|
|
137
|
+
Text should be no smaller than ~2rem for comfortable viewing at typical distances (approximately 4% of screen height):
|
|
138
|
+
|
|
139
|
+
```css
|
|
140
|
+
.body-text {
|
|
141
|
+
font-size: 2rem; /* Minimum readable size */
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.headline {
|
|
145
|
+
font-size: 4rem;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.small-label {
|
|
149
|
+
font-size: 1.5rem; /* Use sparingly */
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Constrain Layouts
|
|
154
|
+
|
|
155
|
+
The init project's `index.css` and `.render` class already set up the base layout with `overflow: hidden` and flexbox. When adding child elements, use `min-height: 0` or `min-width: 0` on flex children to allow them to shrink:
|
|
156
|
+
|
|
157
|
+
```css
|
|
158
|
+
.my-content {
|
|
159
|
+
flex: 1;
|
|
160
|
+
min-height: 0; /* Allows flex children to shrink below content size */
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Text Truncation
|
|
165
|
+
|
|
166
|
+
When text might overflow, truncate gracefully:
|
|
167
|
+
|
|
168
|
+
```css
|
|
169
|
+
.title {
|
|
170
|
+
white-space: nowrap;
|
|
171
|
+
overflow: hidden;
|
|
172
|
+
text-overflow: ellipsis;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* Multi-line truncation */
|
|
176
|
+
.description {
|
|
177
|
+
display: -webkit-box;
|
|
178
|
+
-webkit-line-clamp: 3;
|
|
179
|
+
-webkit-box-orient: vertical;
|
|
180
|
+
overflow: hidden;
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Adaptive Content
|
|
185
|
+
|
|
186
|
+
Use `useUiAspectRatio()` to adapt layouts for portrait vs landscape:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
function Dashboard() {
|
|
190
|
+
const aspectRatio = useUiAspectRatio()
|
|
191
|
+
const isPortrait = aspectRatio < 1
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<div className={`dashboard ${isPortrait ? 'dashboard--portrait' : ''}`}>
|
|
195
|
+
<PrimaryContent />
|
|
196
|
+
{/* Hide sidebar in portrait mode */}
|
|
197
|
+
{!isPortrait && <Sidebar />}
|
|
198
|
+
</div>
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Complete Example
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// Render.tsx - Building on the init project's .render class
|
|
207
|
+
import { useUiScaleToSetRem, useUiAspectRatio } from '@telemetryos/sdk/react'
|
|
208
|
+
import { useUiScaleStoreState } from '../hooks/store'
|
|
209
|
+
import './Render.css'
|
|
210
|
+
|
|
211
|
+
export function Render() {
|
|
212
|
+
const [isLoading, uiScale] = useUiScaleStoreState()
|
|
213
|
+
const aspectRatio = useUiAspectRatio()
|
|
214
|
+
|
|
215
|
+
useUiScaleToSetRem(uiScale)
|
|
216
|
+
|
|
217
|
+
if (isLoading) return null
|
|
218
|
+
|
|
219
|
+
const isPortrait = aspectRatio < 1
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<div className="render">
|
|
223
|
+
<header className="render__header">
|
|
224
|
+
<h1 className="render__title">Dashboard</h1>
|
|
225
|
+
</header>
|
|
226
|
+
|
|
227
|
+
<main className={`render__content ${isPortrait ? 'render__content--portrait' : ''}`}>
|
|
228
|
+
<div className="render__primary">
|
|
229
|
+
<MainDisplay />
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
{!isPortrait && (
|
|
233
|
+
<aside className="render__sidebar">
|
|
234
|
+
<SecondaryInfo />
|
|
235
|
+
</aside>
|
|
236
|
+
)}
|
|
237
|
+
</main>
|
|
238
|
+
</div>
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
```css
|
|
244
|
+
/* Render.css - Add to existing styles, don't override .render base */
|
|
245
|
+
.render__header {
|
|
246
|
+
flex-shrink: 0;
|
|
247
|
+
margin-bottom: 2rem;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.render__title {
|
|
251
|
+
font-size: 4rem;
|
|
252
|
+
margin: 0;
|
|
253
|
+
white-space: nowrap;
|
|
254
|
+
overflow: hidden;
|
|
255
|
+
text-overflow: ellipsis;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.render__content {
|
|
259
|
+
flex: 1;
|
|
260
|
+
min-height: 0;
|
|
261
|
+
display: flex;
|
|
262
|
+
gap: 2rem;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.render__content--portrait {
|
|
266
|
+
flex-direction: column;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.render__primary {
|
|
270
|
+
flex: 1;
|
|
271
|
+
min-width: 0;
|
|
272
|
+
min-height: 0;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.render__sidebar {
|
|
276
|
+
width: 25rem;
|
|
277
|
+
flex-shrink: 0;
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Store Hook for UI Scale
|
|
282
|
+
|
|
283
|
+
Create a store hook to let admins adjust the UI scale:
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// hooks/store.ts
|
|
287
|
+
import { createUseInstanceStoreState } from '@telemetryos/sdk/react'
|
|
288
|
+
|
|
289
|
+
export const useUiScaleStoreState = createUseInstanceStoreState<number>('ui-scale', 1)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
// Settings.tsx - Add slider control
|
|
294
|
+
import { SettingsSliderFrame, SettingsField, SettingsLabel } from '@telemetryos/sdk/react'
|
|
295
|
+
import { useUiScaleStoreState } from '../hooks/store'
|
|
296
|
+
|
|
297
|
+
export function Settings() {
|
|
298
|
+
// Pass 0 debounce for instant slider updates
|
|
299
|
+
const [isLoading, uiScale, setUiScale] = useUiScaleStoreState(0)
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<SettingsField>
|
|
303
|
+
<SettingsLabel>UI Scale</SettingsLabel>
|
|
304
|
+
<SettingsSliderFrame>
|
|
305
|
+
<input
|
|
306
|
+
type="range"
|
|
307
|
+
min={1}
|
|
308
|
+
max={3}
|
|
309
|
+
step={0.01}
|
|
310
|
+
disabled={isLoading}
|
|
311
|
+
value={uiScale}
|
|
312
|
+
onChange={(e) => setUiScale(parseFloat(e.target.value))}
|
|
313
|
+
/>
|
|
314
|
+
<span>{uiScale}x</span>
|
|
315
|
+
</SettingsSliderFrame>
|
|
316
|
+
</SettingsField>
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Common Mistakes
|
|
322
|
+
|
|
323
|
+
| Mistake | Problem | Fix |
|
|
324
|
+
|---------|---------|-----|
|
|
325
|
+
| Using `px` units | Won't scale across resolutions | Use `rem` everywhere |
|
|
326
|
+
| Adding `:hover` styles | No mouse on digital signage | Remove interaction states |
|
|
327
|
+
| Using `overflow: scroll` | No user to scroll | Use `overflow: hidden`, truncate content |
|
|
328
|
+
| Fixed heights in `px` | Breaks on different aspect ratios | Use `vh`, `%`, or flex |
|
|
329
|
+
| Forgetting `useUiScaleToSetRem()` | `rem` units won't scale properly | Call it once in Render view with the uiScale from `useUiScaleStoreState()` |
|
|
330
|
+
| Text below 2rem | Unreadable from viewing distance | Minimum 2rem for body text |
|
|
331
|
+
| Removing `.render` padding | Content cut off by bezels | Keep the ~3rem padding from init project |
|
|
332
|
+
| Overriding `index.css` base styles | Breaks viewport scaling | Add new styles, don't modify base setup |
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tos-requirements
|
|
3
|
+
description: Gather requirements for a new TelemetryOS app. Use at the START of any new app project to understand the developer's vision, render layout design, configuration fields, data sources, and implementation plan before writing code.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TelemetryOS App Requirements Gathering
|
|
7
|
+
|
|
8
|
+
Use this skill at the START of any new TelemetryOS application project. Gather complete requirements before writing any code to ensure a successful "one-shot" implementation.
|
|
9
|
+
|
|
10
|
+
## Requirements Interview
|
|
11
|
+
|
|
12
|
+
**IMPORTANT: This is a conversation, not a survey.** Ask questions one phase at a time. Wait for answers before proceeding. Use earlier answers to skip irrelevant questions.
|
|
13
|
+
|
|
14
|
+
### Phase 1: Start with Vision
|
|
15
|
+
|
|
16
|
+
Ask ONE question to start:
|
|
17
|
+
|
|
18
|
+
> "What app do you want to build? Give me a quick description."
|
|
19
|
+
|
|
20
|
+
That's it. Wait for their answer. Their response will tell you:
|
|
21
|
+
- What data sources they need (weather? media? external API?)
|
|
22
|
+
- What the layout probably looks like
|
|
23
|
+
- What settings make sense
|
|
24
|
+
|
|
25
|
+
### Phase 2: Clarify Based on Their Answer
|
|
26
|
+
|
|
27
|
+
Based on what they described, ask only the relevant follow-ups:
|
|
28
|
+
|
|
29
|
+
**If they mentioned specific data** (weather, stocks, social media, etc.):
|
|
30
|
+
- "Do you have an API in mind, or should I suggest one?"
|
|
31
|
+
- "How often should it refresh?"
|
|
32
|
+
|
|
33
|
+
**If they mentioned media/images/video**:
|
|
34
|
+
- "Will these come from the TelemetryOS media library, or external URLs?"
|
|
35
|
+
|
|
36
|
+
**If the layout isn't clear**:
|
|
37
|
+
- "Quick layout check: fullscreen content, or split/grid layout?"
|
|
38
|
+
|
|
39
|
+
**If they gave a detailed description**: Skip to Phase 3 - you probably have enough.
|
|
40
|
+
|
|
41
|
+
### Phase 3: Fill Gaps
|
|
42
|
+
|
|
43
|
+
Only ask about things that aren't obvious from their description:
|
|
44
|
+
|
|
45
|
+
- Settings they'd want to configure (if not clear)
|
|
46
|
+
- Any specific constraints (refresh rates, data limits)
|
|
47
|
+
- Edge cases that matter for their use case
|
|
48
|
+
|
|
49
|
+
**Don't ask about:**
|
|
50
|
+
- Categories that don't apply to their app
|
|
51
|
+
- Settings with obvious defaults
|
|
52
|
+
- Technical details you can infer
|
|
53
|
+
|
|
54
|
+
### Reference: Layout Types
|
|
55
|
+
|
|
56
|
+
If you need to clarify layout:
|
|
57
|
+
- **Single panel** - fullscreen content
|
|
58
|
+
- **Split layout** - sidebar + main area
|
|
59
|
+
- **Grid** - multiple items in rows/columns
|
|
60
|
+
- **Fullscreen media** - image/video player
|
|
61
|
+
|
|
62
|
+
### Reference: Store Keys
|
|
63
|
+
|
|
64
|
+
Settings allow admins to configure the app. Use these categories to identify what settings make sense for their app (don't ask about every category).
|
|
65
|
+
|
|
66
|
+
#### Categories (consult as needed)
|
|
67
|
+
|
|
68
|
+
**Display Settings** - Visual appearance and layout
|
|
69
|
+
- Colors, fonts, background styles
|
|
70
|
+
- Layout options (columns, alignment, spacing)
|
|
71
|
+
- Show/hide toggles for UI elements
|
|
72
|
+
- Animation preferences
|
|
73
|
+
|
|
74
|
+
> Digital signage typically uses dark backgrounds (better contrast on TVs, reduces glare). Don't ask about light/dark "mode" unless the developer brings it up.
|
|
75
|
+
|
|
76
|
+
**Data Configuration** - How the app fetches and processes data
|
|
77
|
+
- API keys and credentials
|
|
78
|
+
- Endpoint URLs
|
|
79
|
+
- Refresh intervals
|
|
80
|
+
- Data limits (max items, page size)
|
|
81
|
+
|
|
82
|
+
**Content Selection** - What content to display
|
|
83
|
+
- Media folder IDs or tags
|
|
84
|
+
- Playlist/item selection
|
|
85
|
+
- Content filtering rules
|
|
86
|
+
- Sort order preferences
|
|
87
|
+
|
|
88
|
+
**Localization** - Regional and format preferences
|
|
89
|
+
- Timezone
|
|
90
|
+
- Units (imperial/metric, currency)
|
|
91
|
+
- Date/time formats
|
|
92
|
+
- Language/locale
|
|
93
|
+
|
|
94
|
+
#### Store Scope Rules
|
|
95
|
+
|
|
96
|
+
**Default to `instance`** - most settings are instance-scoped. Only use other scopes when the setting clearly fits the patterns below.
|
|
97
|
+
|
|
98
|
+
**Use `application` scope for:**
|
|
99
|
+
- API keys and credentials (shared cost, single billing)
|
|
100
|
+
- Account-wide branding (company logo URL, brand colors)
|
|
101
|
+
- License keys or subscription identifiers
|
|
102
|
+
- Shared service endpoints configured once per account
|
|
103
|
+
|
|
104
|
+
**Use `instance` scope for everything else:**
|
|
105
|
+
- Content selection (what to display)
|
|
106
|
+
- Layout options (how to display it)
|
|
107
|
+
- Refresh intervals and timing
|
|
108
|
+
- Localization (timezone, units, language)
|
|
109
|
+
- Visual preferences (colors, fonts, backgrounds)
|
|
110
|
+
- Filters, sort order, display limits
|
|
111
|
+
|
|
112
|
+
**Quick Reference:**
|
|
113
|
+
| Scope | Synced? | Shared Across | Common Use |
|
|
114
|
+
|-------|---------|---------------|------------|
|
|
115
|
+
| `instance` | Yes | Same instance on all devices | Per-widget config |
|
|
116
|
+
| `application` | Yes | All instances in account | API keys, credentials |
|
|
117
|
+
|
|
118
|
+
#### Capture Each Store Key
|
|
119
|
+
|
|
120
|
+
For each setting identified, record:
|
|
121
|
+
|
|
122
|
+
| Key | Category | Scope | Type | Default | Constraints | Required? |
|
|
123
|
+
|-----|----------|-------|------|---------|-------------|-----------|
|
|
124
|
+
| city | Localization | instance | string | '' | min 2 chars | Yes |
|
|
125
|
+
| apiKey | Data Config | application | string | '' | - | Yes |
|
|
126
|
+
| units | Localization | instance | 'imperial' \| 'metric' | 'imperial' | enum | Yes |
|
|
127
|
+
| refreshInterval | Data Config | instance | number | 30 | 10-300 | No |
|
|
128
|
+
|
|
129
|
+
#### Questions to Ask (spread across conversation)
|
|
130
|
+
|
|
131
|
+
After understanding the core app, circle back to fill in settings details:
|
|
132
|
+
- **What should admins be able to configure?** (prompt with relevant categories)
|
|
133
|
+
- **What are sensible defaults?** (app should work with minimal config)
|
|
134
|
+
- **Are there validation rules or constraints?** (min/max, patterns, enums)
|
|
135
|
+
- **Which settings are required vs optional?**
|
|
136
|
+
|
|
137
|
+
Ask these as follow-ups in later turns, not all upfront. Scope is typically inferred from the rules above—only ask about scope if a setting doesn't clearly fit.
|
|
138
|
+
|
|
139
|
+
### Reference: Data Sources
|
|
140
|
+
|
|
141
|
+
Consult this when you need to understand their data needs (don't ask about all of these):
|
|
142
|
+
|
|
143
|
+
**TelemetryOS Platform APIs:**
|
|
144
|
+
- `media()` - User-uploaded images/videos from media library
|
|
145
|
+
- `weather()` - Weather data (current, hourly, daily forecasts)
|
|
146
|
+
- `applications()` - Embedding other TOS apps
|
|
147
|
+
|
|
148
|
+
**External APIs:**
|
|
149
|
+
- Use `proxy().fetch()` for external APIs (handles CORS)
|
|
150
|
+
- Note: authentication method (API key, OAuth, none)
|
|
151
|
+
- Note: rate limits if known
|
|
152
|
+
|
|
153
|
+
**Refresh patterns:**
|
|
154
|
+
- Timer-based (every N seconds/minutes)
|
|
155
|
+
- Event-based (on user action)
|
|
156
|
+
- Most apps use 30-60 second refresh for live data
|
|
157
|
+
|
|
158
|
+
### Implementation Checklist
|
|
159
|
+
|
|
160
|
+
After gathering requirements, generate:
|
|
161
|
+
|
|
162
|
+
#### Store Hooks (hooks/store.ts)
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// Instance-scoped keys (most common - per-widget config)
|
|
166
|
+
export const use[Key]State = createUseInstanceStoreState<Type>('key', default)
|
|
167
|
+
// Usage: const [loading, value, setValue] = use[Key]State()
|
|
168
|
+
|
|
169
|
+
// Application-scoped keys (shared across all instances)
|
|
170
|
+
export const use[Key]State = createUseApplicationStoreState<Type>('key', default)
|
|
171
|
+
// Usage: const [loading, value, setValue] = use[Key]State()
|
|
172
|
+
|
|
173
|
+
// Device-scoped keys (stays on device, Render only)
|
|
174
|
+
export const use[Key]State = createUseDeviceStoreState<Type>('key', default)
|
|
175
|
+
// Usage: const [loading, value, setValue] = use[Key]State()
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### Settings UI Components
|
|
179
|
+
|
|
180
|
+
List each Settings control needed:
|
|
181
|
+
- [ ] Text input for X
|
|
182
|
+
- [ ] Dropdown for Y
|
|
183
|
+
- [ ] Slider for Z
|
|
184
|
+
- [ ] Toggle for W
|
|
185
|
+
|
|
186
|
+
#### Render View Structure
|
|
187
|
+
|
|
188
|
+
Describe the component hierarchy:
|
|
189
|
+
```
|
|
190
|
+
Render
|
|
191
|
+
├── Header (title, logo)
|
|
192
|
+
├── MainContent
|
|
193
|
+
│ └── DataDisplay
|
|
194
|
+
└── Footer (timestamp, refresh indicator)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### SDK APIs Required
|
|
198
|
+
|
|
199
|
+
- [ ] createUseInstanceStoreState - Settings ↔ Render sync
|
|
200
|
+
- [ ] proxy().fetch() - External API calls
|
|
201
|
+
- [ ] weather() - Weather data
|
|
202
|
+
- [ ] media() - Media library
|
|
203
|
+
|
|
204
|
+
## Output Format
|
|
205
|
+
|
|
206
|
+
After gathering requirements, provide a structured summary:
|
|
207
|
+
|
|
208
|
+
```markdown
|
|
209
|
+
# [App Name] Requirements
|
|
210
|
+
|
|
211
|
+
## Vision
|
|
212
|
+
[One sentence description]
|
|
213
|
+
|
|
214
|
+
## Render View
|
|
215
|
+
- Layout: [single/split/grid/fullscreen]
|
|
216
|
+
- Content: [description]
|
|
217
|
+
- Refresh: [interval or trigger]
|
|
218
|
+
|
|
219
|
+
## Store Keys
|
|
220
|
+
| Key | Category | Scope | Type | Default | UI Component |
|
|
221
|
+
|-----|----------|-------|------|---------|--------------|
|
|
222
|
+
| ... | ... | instance/application/device | ... | ... | ... |
|
|
223
|
+
|
|
224
|
+
## Data Sources
|
|
225
|
+
- Internal: [list]
|
|
226
|
+
- External: [list with endpoints]
|
|
227
|
+
|
|
228
|
+
## Implementation Plan
|
|
229
|
+
1. Create store hooks
|
|
230
|
+
2. Build Settings UI
|
|
231
|
+
3. Build Render view
|
|
232
|
+
4. Add data fetching
|
|
233
|
+
5. Test and polish
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Tips for Success
|
|
237
|
+
|
|
238
|
+
1. **Don't skip requirements** - Incomplete requirements lead to rework
|
|
239
|
+
2. **Validate early** - Confirm understanding before coding
|
|
240
|
+
3. **Start simple** - MVP first, then add features
|
|
241
|
+
4. **Use SDK hooks** - `createUseInstanceStoreState` for all store keys
|
|
242
|
+
5. **Follow patterns** - Match existing Settings UI components exactly
|
|
243
|
+
|
|
244
|
+
## Next Steps
|
|
245
|
+
|
|
246
|
+
After gathering requirements, use these skills to implement:
|
|
247
|
+
|
|
248
|
+
- **`tos-store-sync`** - Create store hooks from the Store Keys table
|
|
249
|
+
- **`tos-settings-ui`** - Build the Settings UI components
|
|
250
|
+
- **`tos-proxy-fetch`** - Implement external API calls
|
|
251
|
+
- **`tos-weather-api`** - Integrate weather data (if needed)
|
|
252
|
+
- **`tos-media-api`** - Access media library (if needed)
|