@puredesktop/create-app 2.1.0 → 2.1.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.
@@ -0,0 +1,138 @@
1
+ # Theme CSS Variables
2
+
3
+ This file is generated from `packages/ui/src/theme/tokens.ts`.
4
+
5
+ PureDesktop injects these variables through `AppFrame` and the shell bridge
6
+ theme payload. Use them in app styling instead of hardcoded platform colors,
7
+ spacing, typography, radii, shadows, transitions, or app stacking values.
8
+
9
+ This static reference is generated by PureDesktop maintainers before the
10
+ create-app package is released.
11
+
12
+ | Variable | Domain |
13
+ | --- | --- |
14
+ | `--platform-colors-accent` | colors |
15
+ | `--platform-colors-accent-hover` | colors |
16
+ | `--platform-colors-accent-muted` | colors |
17
+ | `--platform-colors-app-viewport` | colors |
18
+ | `--platform-colors-bg` | colors |
19
+ | `--platform-colors-border` | colors |
20
+ | `--platform-colors-border-strong` | colors |
21
+ | `--platform-colors-chrome-tab-strip` | colors |
22
+ | `--platform-colors-chrome-titlebar` | colors |
23
+ | `--platform-colors-danger` | colors |
24
+ | `--platform-colors-divider` | colors |
25
+ | `--platform-colors-elevated` | colors |
26
+ | `--platform-colors-focus` | colors |
27
+ | `--platform-colors-info` | colors |
28
+ | `--platform-colors-selection` | colors |
29
+ | `--platform-colors-semantic-blue` | colors |
30
+ | `--platform-colors-semantic-blue-border` | colors |
31
+ | `--platform-colors-semantic-blue-muted` | colors |
32
+ | `--platform-colors-semantic-blue-text` | colors |
33
+ | `--platform-colors-semantic-green` | colors |
34
+ | `--platform-colors-semantic-green-border` | colors |
35
+ | `--platform-colors-semantic-green-muted` | colors |
36
+ | `--platform-colors-semantic-green-text` | colors |
37
+ | `--platform-colors-semantic-orange` | colors |
38
+ | `--platform-colors-semantic-orange-border` | colors |
39
+ | `--platform-colors-semantic-orange-muted` | colors |
40
+ | `--platform-colors-semantic-orange-text` | colors |
41
+ | `--platform-colors-semantic-red` | colors |
42
+ | `--platform-colors-semantic-red-border` | colors |
43
+ | `--platform-colors-semantic-red-muted` | colors |
44
+ | `--platform-colors-semantic-red-text` | colors |
45
+ | `--platform-colors-success` | colors |
46
+ | `--platform-colors-surface` | colors |
47
+ | `--platform-colors-surface-active` | colors |
48
+ | `--platform-colors-surface-hover` | colors |
49
+ | `--platform-colors-text` | colors |
50
+ | `--platform-colors-text-danger` | colors |
51
+ | `--platform-colors-text-disabled` | colors |
52
+ | `--platform-colors-text-inverse` | colors |
53
+ | `--platform-colors-text-secondary` | colors |
54
+ | `--platform-colors-warning` | colors |
55
+ | `--platform-radius-full` | radius |
56
+ | `--platform-radius-lg` | radius |
57
+ | `--platform-radius-md` | radius |
58
+ | `--platform-radius-sm` | radius |
59
+ | `--platform-shadow-lg` | shadow |
60
+ | `--platform-shadow-md` | shadow |
61
+ | `--platform-shadow-sm` | shadow |
62
+ | `--platform-spacing-lg` | spacing |
63
+ | `--platform-spacing-md` | spacing |
64
+ | `--platform-spacing-sm` | spacing |
65
+ | `--platform-spacing-xl` | spacing |
66
+ | `--platform-spacing-xs` | spacing |
67
+ | `--platform-spacing-xxl` | spacing |
68
+ | `--platform-transition-base` | transition |
69
+ | `--platform-transition-fast` | transition |
70
+ | `--platform-transition-slow` | transition |
71
+ | `--platform-typography-font-family` | typography |
72
+ | `--platform-typography-font-family-content` | typography |
73
+ | `--platform-typography-font-family-mono` | typography |
74
+ | `--platform-typography-font-size-base` | typography |
75
+ | `--platform-typography-font-size-lg` | typography |
76
+ | `--platform-typography-font-size-sm` | typography |
77
+ | `--platform-typography-font-size-xl` | typography |
78
+ | `--platform-typography-font-size-xs` | typography |
79
+ | `--platform-typography-font-weight-bold` | typography |
80
+ | `--platform-typography-font-weight-medium` | typography |
81
+ | `--platform-typography-font-weight-normal` | typography |
82
+ | `--platform-typography-line-height-base` | typography |
83
+ | `--platform-typography-line-height-tight` | typography |
84
+ | `--platform-z-index-zi-app-base` | zIndex |
85
+ | `--platform-z-index-zi-app-chrome` | zIndex |
86
+ | `--platform-z-index-zi-app-menus` | zIndex |
87
+ | `--platform-z-index-zi-app-modal` | zIndex |
88
+ | `--platform-z-index-zi-app-sidebar` | zIndex |
89
+ | `--platform-z-index-zi-app-toast` | zIndex |
90
+ | `--platform-z-index-zi-app-viewport-toolbar` | zIndex |
91
+
92
+ ## App-Owned Colors
93
+
94
+ Use platform variables for the base app shell, text, surfaces, borders,
95
+ spacing, typography, radius, shadows, transitions, and z-index.
96
+
97
+ When the app domain needs colors beyond the platform set, define app-prefixed
98
+ variables and keep them synchronized with the shell theme through
99
+ `data-platform-theme`.
100
+
101
+ Good use cases for app-owned colors:
102
+
103
+ - chart series;
104
+ - calendar categories;
105
+ - tags and labels;
106
+ - syntax or document annotations;
107
+ - domain-specific status colors not covered by platform semantic colors.
108
+
109
+ Pattern:
110
+
111
+ ```ts
112
+ const Root = styled.div`
113
+ --myapp-chart-blue: #3b73d9;
114
+ --myapp-chart-blue-muted: color-mix(
115
+ in srgb,
116
+ var(--myapp-chart-blue) 12%,
117
+ var(--platform-colors-surface)
118
+ );
119
+
120
+ [data-platform-theme='dark'] & {
121
+ --myapp-chart-blue: #8ab4ff;
122
+ --myapp-chart-blue-muted: color-mix(
123
+ in srgb,
124
+ var(--myapp-chart-blue) 18%,
125
+ var(--platform-colors-surface)
126
+ );
127
+ }
128
+ `
129
+ ```
130
+
131
+ Rules for app-owned colors:
132
+
133
+ - prefix app variables with the app slug or a short app namespace;
134
+ - provide dark-mode values inside `[data-platform-theme='dark'] &`;
135
+ - derive muted fills, hovers, selections, and outlines with `color-mix()` and
136
+ platform surface, text, border, or background variables;
137
+ - use `--platform-colors-accent` for primary app identity and actions;
138
+ - reserve app-owned colors for domain meaning.
@@ -0,0 +1,124 @@
1
+ # Tool Handlers
2
+
3
+ App agent tools have two halves:
4
+
5
+ - `plugin.json` declares the tool schema for the shell agent.
6
+ - app source registers a runtime handler after the iframe bridge is ready.
7
+
8
+ The names must match exactly.
9
+
10
+ ## Generated Structure
11
+
12
+ When the manifest includes `app.agents.tools`, the generator creates this
13
+ structure and wires `useAppAgentTools(ready)` into `src/App.tsx`:
14
+
15
+ ```text
16
+ src/
17
+ agents/
18
+ catalog.ts
19
+ handlers/
20
+ index.ts
21
+ hooks/
22
+ useAppAgentTools.ts
23
+ ```
24
+
25
+ ## Catalog
26
+
27
+ Generated from manifest tool names:
28
+
29
+ ```ts
30
+ export const APP_AGENT_TOOL_NAMES = ['listItems'] as const
31
+
32
+ export const APP_AGENT_LOG_LABEL = '{{APP_SLUG}}'
33
+ ```
34
+
35
+ Keep this list in the same order as `plugin.json` `app.agents.tools`.
36
+
37
+ ## Registration Hook
38
+
39
+ Generated when tools exist:
40
+
41
+ ```ts
42
+ import { usePlatformAgentTools } from '@puredesktop/puredesktop-ui-bridge/bridge/react/usePlatformAgentTools'
43
+ import { APP_AGENT_LOG_LABEL, APP_AGENT_TOOL_NAMES } from '../agents/catalog'
44
+ import { appAgentHandlers } from '../agents/handlers'
45
+
46
+ export function useAppAgentTools(ready: boolean): void {
47
+ usePlatformAgentTools({
48
+ ready,
49
+ tools: APP_AGENT_TOOL_NAMES,
50
+ logLabel: APP_AGENT_LOG_LABEL,
51
+ handlers: appAgentHandlers,
52
+ })
53
+ }
54
+ ```
55
+
56
+ Call this hook from `src/App.tsx` after `usePlatformBridge()`:
57
+
58
+ ```ts
59
+ useAppAgentTools(ready)
60
+ ```
61
+
62
+ ## Handler Shape
63
+
64
+ Generated handlers start as explicit not-implemented tool errors. Replace each
65
+ stub with app domain logic.
66
+
67
+ ```ts
68
+ import {
69
+ agentToolErrorContent,
70
+ formatAgentToolJson,
71
+ readAgentToolStringArg,
72
+ } from '@puredesktop/puredesktop-ui-bridge/bridge/agentToolHelpers'
73
+ import type { AgentToolHandler } from '@puredesktop/puredesktop-ui-bridge/bridge/react/usePlatformAgentTools'
74
+
75
+ const listItems: AgentToolHandler = async invoke => {
76
+ const status = readAgentToolStringArg(invoke.arguments, 'status')
77
+
78
+ return {
79
+ content: formatAgentToolJson({
80
+ items: [],
81
+ status: status || null,
82
+ }),
83
+ }
84
+ }
85
+
86
+ export const appAgentHandlers = {
87
+ listItems,
88
+ } satisfies Record<string, AgentToolHandler>
89
+ ```
90
+
91
+ Return compact JSON for structured data. Return `agentToolErrorContent(message)`
92
+ for validation failures that the agent can correct.
93
+
94
+ ## Read Tool Pattern
95
+
96
+ Read tools return current app state, ids, paths, and exact values needed before a
97
+ write. The agent needs stable ids and exact target names.
98
+
99
+ ## Write Tool Pattern
100
+
101
+ Write tools validate precise targets. A write handler returns a short result
102
+ with the changed id/path and enough data for the agent to explain what changed.
103
+
104
+ Manifest write tools that mutate files, records, external services, or broad app
105
+ state set `requiresApproval: true` unless the action is intentionally safe.
106
+
107
+ ## Validation
108
+
109
+ `npm run puredesktop:check` verifies declared tool names appear in source and in
110
+ the built output. For generated tool scaffolds, it also verifies:
111
+
112
+ - `src/agents/catalog.ts` exports `APP_AGENT_TOOL_NAMES`;
113
+ - every manifest tool name appears in the catalog;
114
+ - `src/agents/handlers/index.ts` exports `appAgentHandlers`;
115
+ - every manifest tool name appears in the handler map;
116
+ - `src/hooks/useAppAgentTools.ts` registers with `usePlatformAgentTools`;
117
+ - `src/App.tsx` calls `useAppAgentTools(ready)`.
118
+
119
+ Build before running it:
120
+
121
+ ```bash
122
+ npm run build
123
+ npm run puredesktop:check
124
+ ```
@@ -0,0 +1,59 @@
1
+ # UI And Components
2
+
3
+ The generated app starts intentionally small. Build the real workflow in
4
+ `src/components/AppShell.tsx` and move domain logic into `src/lib/` as it grows.
5
+
6
+ ## AppFrame
7
+
8
+ Every render path in `src/App.tsx` uses `AppFrame`. This gives the iframe the
9
+ PureDesktop reset, theme CSS variables, and shell-compatible surface styling.
10
+
11
+ ## Component Shape
12
+
13
+ Start with controlled, simple components:
14
+
15
+ ```text
16
+ src/
17
+ components/
18
+ AppShell.tsx
19
+ ItemList.tsx
20
+ ItemDetail.tsx
21
+ lib/
22
+ items.ts
23
+ items.test.ts
24
+ ```
25
+
26
+ Keep app state close to the workflow until a shared hook or reducer removes real
27
+ duplication.
28
+
29
+ ## Platform Components
30
+
31
+ Import shared UI from `@puredesktop/puredesktop-ui-bridge/components/...` before
32
+ creating custom primitives.
33
+
34
+ Useful starting points:
35
+
36
+ - `AppFrame`
37
+ - `EmptyState`
38
+ - `Heading`
39
+ - `Text`
40
+
41
+ ## Styling
42
+
43
+ Use styled-components and platform CSS variables:
44
+
45
+ ```ts
46
+ const Root = styled.div`
47
+ display: flex;
48
+ flex-direction: column;
49
+ gap: var(--platform-spacing-md);
50
+ color: var(--platform-colors-text);
51
+ background: var(--platform-colors-bg);
52
+ `
53
+ ```
54
+
55
+ Use app-local CSS variables only for app-specific meaning, for example
56
+ `--{{APP_SLUG}}-accent`.
57
+
58
+ See [Theme CSS variables](./theme-vars.md) for the generated list of available
59
+ platform variables by domain.
@@ -9,10 +9,10 @@
9
9
  "typecheck": "tsc -p tsconfig.json --noEmit",
10
10
  "puredesktop:check": "node scripts/validate-puredesktop-app.mjs"
11
11
  },
12
- "dependencies": {
13
- "{{UI_PACKAGE_NAME}}": "{{UI_PACKAGE_VERSION}}",
14
- "react": "^19.1.0",
15
- "react-dom": "^19.1.0",
12
+ "dependencies": {
13
+ "@puredesktop/puredesktop-ui-bridge": "2.1.1",
14
+ "react": "^19.1.0",
15
+ "react-dom": "^19.1.0",
16
16
  "styled-components": "^6.1.18"
17
17
  },
18
18
  "devDependencies": {
@@ -14,7 +14,7 @@
14
14
  "navigationLabel": "{{APP_TITLE}}",
15
15
  "productName": "pure.{{APP_SLUG}}",
16
16
  "kind": "{{APP_SLUG}}",
17
- "description": "{{APP_TITLE}} - built with PureScience Desktop.",
17
+ "description": "{{APP_TITLE}} - built with PureDesktop.",
18
18
  "usePureDesktopAiPanel": true
19
19
  }
20
20
  }
@@ -199,6 +199,9 @@ async function validateAgentTools(manifest, distPath) {
199
199
 
200
200
  if (toolNames.size === 0) return
201
201
 
202
+ const manifestToolNames = [...toolNames]
203
+ await validateAgentToolScaffold(manifestToolNames)
204
+
202
205
  const sourceText = await readProjectText(join(ROOT, 'src'))
203
206
  const distText = await readProjectText(distPath)
204
207
  const registrationHints = ['usePlatformAgentTools', 'registerAgentTools', 'agents.tools.register']
@@ -206,7 +209,7 @@ async function validateAgentTools(manifest, distPath) {
206
209
  errors.push('Declared app agent tools must be registered in source with usePlatformAgentTools or agents.tools.register.')
207
210
  }
208
211
 
209
- for (const toolName of toolNames) {
212
+ for (const toolName of manifestToolNames) {
210
213
  if (!sourceText.includes(toolName)) {
211
214
  errors.push(`Declared app agent tool "${toolName}" was not found in source files.`)
212
215
  }
@@ -220,6 +223,68 @@ async function validateAgentTools(manifest, distPath) {
220
223
  }
221
224
  }
222
225
 
226
+ async function validateAgentToolScaffold(toolNames) {
227
+ const catalogPath = join(ROOT, 'src', 'agents', 'catalog.ts')
228
+ const handlersPath = join(ROOT, 'src', 'agents', 'handlers', 'index.ts')
229
+ const hookPath = join(ROOT, 'src', 'hooks', 'useAppAgentTools.ts')
230
+ const appPath = join(ROOT, 'src', 'App.tsx')
231
+
232
+ const catalogText = await readRequiredText(
233
+ catalogPath,
234
+ 'Declared app agent tools require src/agents/catalog.ts.',
235
+ )
236
+ const handlersText = await readRequiredText(
237
+ handlersPath,
238
+ 'Declared app agent tools require src/agents/handlers/index.ts.',
239
+ )
240
+ const hookText = await readRequiredText(
241
+ hookPath,
242
+ 'Declared app agent tools require src/hooks/useAppAgentTools.ts.',
243
+ )
244
+ const appText = await readRequiredText(
245
+ appPath,
246
+ 'Declared app agent tools require src/App.tsx.',
247
+ )
248
+
249
+ if (!catalogText || !handlersText || !hookText || !appText) return
250
+
251
+ if (!catalogText.includes('APP_AGENT_TOOL_NAMES')) {
252
+ errors.push('src/agents/catalog.ts must export APP_AGENT_TOOL_NAMES.')
253
+ }
254
+ if (!handlersText.includes('appAgentHandlers')) {
255
+ errors.push('src/agents/handlers/index.ts must export appAgentHandlers.')
256
+ }
257
+ if (!hookText.includes('usePlatformAgentTools')) {
258
+ errors.push('src/hooks/useAppAgentTools.ts must call usePlatformAgentTools.')
259
+ }
260
+ if (!hookText.includes('APP_AGENT_TOOL_NAMES')) {
261
+ errors.push('src/hooks/useAppAgentTools.ts must register APP_AGENT_TOOL_NAMES.')
262
+ }
263
+ if (!appText.includes('useAppAgentTools(ready)')) {
264
+ errors.push('src/App.tsx must call useAppAgentTools(ready).')
265
+ }
266
+
267
+ for (const toolName of toolNames) {
268
+ if (!catalogText.includes(toolName)) {
269
+ errors.push(`src/agents/catalog.ts is missing app agent tool "${toolName}".`)
270
+ }
271
+ if (!handlersText.includes(toolName)) {
272
+ errors.push(`src/agents/handlers/index.ts is missing app agent tool "${toolName}".`)
273
+ }
274
+ }
275
+ }
276
+
277
+ async function readRequiredText(path, message) {
278
+ if (!(await isFile(path))) {
279
+ errors.push(message)
280
+ return ''
281
+ }
282
+ return readFile(path, 'utf8').catch(error => {
283
+ errors.push(`${message} ${errorMessage(error)}`)
284
+ return ''
285
+ })
286
+ }
287
+
223
288
  async function readProjectText(root) {
224
289
  const chunks = []
225
290
  await collectText(root, chunks)
@@ -33,8 +33,8 @@ export function App(): React.ReactElement {
33
33
  bootError
34
34
  ? bootError.message
35
35
  : booting
36
- ? 'Loading'
37
- : 'Waiting for PureScience shell bridge'
36
+ ? 'Loading...'
37
+ : 'Waiting for PureDesktop shell bridge...'
38
38
  }
39
39
  />
40
40
  </AppFrame>
@@ -1,9 +1,58 @@
1
- import { bridge } from '@puredesktop/puredesktop-ui-bridge/bridge/client'
2
- import { PLATFORM_BRIDGE_METHODS } from '@puredesktop/puredesktop-ui-bridge/bridge/methods'
3
- import { APP_SLUG } from '../constants'
4
- import type { AppSettings } from '../types'
5
-
6
- export { bridge }
1
+ import { bridge } from '@puredesktop/puredesktop-ui-bridge/bridge/client'
2
+ import { getPlatformAppSettings } from '@puredesktop/puredesktop-ui-bridge/bridge/appSettings'
3
+ import { APP_SLUG } from '../constants'
4
+ import type { AppSettings } from '../types'
5
+
6
+ export {
7
+ getPlatformAppSettings,
8
+ updatePlatformAppSettings,
9
+ } from '@puredesktop/puredesktop-ui-bridge/bridge/appSettings'
10
+ export {
11
+ getPlatformPreferences,
12
+ patchPlatformPreferences,
13
+ } from '@puredesktop/puredesktop-ui-bridge/bridge/preferences'
14
+ export {
15
+ networkFetch,
16
+ type PlatformNetworkFetchRequest,
17
+ type PlatformNetworkFetchResponse,
18
+ } from '@puredesktop/puredesktop-ui-bridge/bridge/network'
19
+ export {
20
+ createPlatformFolder,
21
+ deletePlatformFile,
22
+ listPlatformFiles,
23
+ readPlatformFileBinary,
24
+ readPlatformFileBinaryDataUrl,
25
+ readPlatformFilePreview,
26
+ readPlatformFilePreviewUrl,
27
+ readPlatformTextFile,
28
+ renamePlatformFile,
29
+ writePlatformFileBinary,
30
+ writePlatformTextFile,
31
+ type PlatformFileCreateFolderResult,
32
+ type PlatformFileDeleteResult,
33
+ type PlatformFileEntry,
34
+ type PlatformFileListResult,
35
+ type PlatformFilePreviewUrlResult,
36
+ type PlatformFileReadBinaryResult,
37
+ type PlatformFileReadPreviewResult,
38
+ type PlatformFileRenameResult,
39
+ type PlatformFileWriteResult,
40
+ } from '@puredesktop/puredesktop-ui-bridge/bridge/fs'
41
+ export {
42
+ openPlatformFileDialog,
43
+ openPlatformFolderDialog,
44
+ openPlatformImageDialog,
45
+ savePlatformFolderDialog,
46
+ } from '@puredesktop/puredesktop-ui-bridge/bridge/dialog'
47
+ export {
48
+ readPlatformStorageJson,
49
+ writePlatformStorageJson,
50
+ type PlatformStorageJsonReadResult,
51
+ type PlatformStorageJsonRequest,
52
+ type PlatformStorageJsonWriteRequest,
53
+ type PlatformStorageJsonWriteResult,
54
+ } from '@puredesktop/puredesktop-ui-bridge/bridge/storage'
55
+ export { bridge }
7
56
 
8
57
  const STANDALONE_SETTINGS_KEY = 'puredesktop:{{APP_SLUG}}:settings'
9
58
 
@@ -20,12 +69,10 @@ function readStandaloneSettings(): AppSettings {
20
69
  }
21
70
  }
22
71
 
23
- export async function fetchAppSettings(): Promise<AppSettings> {
24
- if (isStandaloneDevMode()) {
25
- return readStandaloneSettings()
26
- }
27
-
28
- return bridge.call<AppSettings>(PLATFORM_BRIDGE_METHODS.SETTINGS_APP_GET, [
29
- APP_SLUG,
30
- ])
31
- }
72
+ export async function fetchAppSettings(): Promise<AppSettings> {
73
+ if (isStandaloneDevMode()) {
74
+ return readStandaloneSettings()
75
+ }
76
+
77
+ return getPlatformAppSettings(APP_SLUG) as Promise<AppSettings>
78
+ }