@puredesktop/create-app 1.0.0-beta.2 → 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.
- package/bin/create-puredesktop-app.mjs +308 -77
- package/package.json +2 -2
- package/template/README.md +39 -296
- package/template/docs/README.md +31 -0
- package/template/docs/agent.md +58 -0
- package/template/docs/app-lifecycle.md +60 -0
- package/template/docs/bridge/README.md +79 -0
- package/template/docs/bridge/dialog.md +56 -0
- package/template/docs/bridge/fs.md +95 -0
- package/template/docs/bridge/network.md +38 -0
- package/template/docs/bridge/settings.md +63 -0
- package/template/docs/bridge/storage.md +48 -0
- package/template/docs/plugin-manifest.md +108 -0
- package/template/docs/theme-vars.md +138 -0
- package/template/docs/tool-handlers.md +124 -0
- package/template/docs/ui-and-components.md +59 -0
- package/template/package.json +4 -4
- package/template/plugin.json +1 -1
- package/template/scripts/validate-puredesktop-app.mjs +66 -1
- package/template/src/App.tsx +2 -2
- package/template/src/bridge/platformBridge.ts +62 -15
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Filesystem
|
|
2
|
+
|
|
3
|
+
Permission: `filesystem`
|
|
4
|
+
|
|
5
|
+
Filesystem helpers read, write, list, rename, delete, and preview files through
|
|
6
|
+
the shell.
|
|
7
|
+
|
|
8
|
+
Import from the generated app bridge:
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import {
|
|
12
|
+
createPlatformFolder,
|
|
13
|
+
deletePlatformFile,
|
|
14
|
+
listPlatformFiles,
|
|
15
|
+
readPlatformTextFile,
|
|
16
|
+
renamePlatformFile,
|
|
17
|
+
writePlatformTextFile,
|
|
18
|
+
} from '../../src/bridge/platformBridge'
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## listPlatformFiles
|
|
22
|
+
|
|
23
|
+
Lists a folder.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
const listing = await listPlatformFiles('/absolute/folder')
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
|
|
31
|
+
- `rootPath`: listed folder path.
|
|
32
|
+
- `parentPath`: parent folder path or `null`.
|
|
33
|
+
- `entries`: file and folder entries with `path`, `name`, `kind`, size,
|
|
34
|
+
modified time, MIME type, extension, and directory flag.
|
|
35
|
+
|
|
36
|
+
## readPlatformTextFile
|
|
37
|
+
|
|
38
|
+
Reads a text file.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const content = await readPlatformTextFile('/absolute/file.md')
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Returns the file contents as a string.
|
|
45
|
+
|
|
46
|
+
## writePlatformTextFile
|
|
47
|
+
|
|
48
|
+
Writes a text file.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
await writePlatformTextFile('/absolute/file.md', '# Notes\n')
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Returns `{ ok: true }`.
|
|
55
|
+
|
|
56
|
+
## createPlatformFolder
|
|
57
|
+
|
|
58
|
+
Creates a folder under a parent directory.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
const folder = await createPlatformFolder('/absolute/parent', 'Project')
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Returns `{ path }` for the created folder.
|
|
65
|
+
|
|
66
|
+
## renamePlatformFile
|
|
67
|
+
|
|
68
|
+
Renames a file or folder within its current parent directory.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const renamed = await renamePlatformFile('/absolute/file.md', 'renamed.md')
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Returns `{ path }` for the renamed path.
|
|
75
|
+
|
|
76
|
+
## deletePlatformFile
|
|
77
|
+
|
|
78
|
+
Deletes a file or folder.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
await deletePlatformFile('/absolute/file.md')
|
|
82
|
+
await deletePlatformFile('/absolute/folder', true)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Pass `true` for recursive folder deletion. Returns `{ ok: true }`.
|
|
86
|
+
|
|
87
|
+
## Preview And Binary Helpers
|
|
88
|
+
|
|
89
|
+
The same domain also exports:
|
|
90
|
+
|
|
91
|
+
- `readPlatformFilePreview(path, maxBytes?)`
|
|
92
|
+
- `readPlatformFilePreviewUrl(path)`
|
|
93
|
+
- `readPlatformFileBinary(path, maxBytes?)`
|
|
94
|
+
- `readPlatformFileBinaryDataUrl(path, maxBytes?)`
|
|
95
|
+
- `writePlatformFileBinary(path, base64)`
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Network
|
|
2
|
+
|
|
3
|
+
Permission: `network`
|
|
4
|
+
|
|
5
|
+
Use `networkFetch` for HTTP(S) requests through the shell.
|
|
6
|
+
|
|
7
|
+
Import from the generated app bridge:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { networkFetch } from '../../src/bridge/platformBridge'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
const response = await networkFetch({
|
|
17
|
+
url: 'https://example.com/api/items',
|
|
18
|
+
method: 'GET',
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
throw new Error(`Request failed with ${response.status}`)
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Request fields:
|
|
27
|
+
|
|
28
|
+
- `url`: HTTP or HTTPS URL.
|
|
29
|
+
- `method`: optional, defaults to `GET`.
|
|
30
|
+
- `headers`: optional string map.
|
|
31
|
+
- `body`: optional string.
|
|
32
|
+
|
|
33
|
+
Response fields:
|
|
34
|
+
|
|
35
|
+
- `status`: HTTP status code.
|
|
36
|
+
- `ok`: `true` for 2xx responses.
|
|
37
|
+
- `headers`: response headers as a string map.
|
|
38
|
+
- `body`: response body as text.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Settings
|
|
2
|
+
|
|
3
|
+
Permission: `settings`
|
|
4
|
+
|
|
5
|
+
Settings helpers read and update user preferences through the shell. App
|
|
6
|
+
settings are stored inside user preferences under the current app's slug.
|
|
7
|
+
|
|
8
|
+
Import from the generated app bridge:
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import {
|
|
12
|
+
getPlatformAppSettings,
|
|
13
|
+
getPlatformPreferences,
|
|
14
|
+
patchPlatformPreferences,
|
|
15
|
+
updatePlatformAppSettings,
|
|
16
|
+
} from '../../src/bridge/platformBridge'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## getPlatformAppSettings
|
|
20
|
+
|
|
21
|
+
Reads settings for the current app slug.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const settings = await getPlatformAppSettings(APP_SLUG)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The shell allows plugin frames to access only their own app settings.
|
|
28
|
+
|
|
29
|
+
## updatePlatformAppSettings
|
|
30
|
+
|
|
31
|
+
Patches settings for the current app slug.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const next = await updatePlatformAppSettings({
|
|
35
|
+
appSlug: APP_SLUG,
|
|
36
|
+
patch: { workspacePath: '/absolute/folder' },
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Returns the merged app settings object.
|
|
41
|
+
|
|
42
|
+
## getPlatformPreferences
|
|
43
|
+
|
|
44
|
+
Reads shell user preferences.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const prefs = await getPlatformPreferences()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Common fields include the working directory, theme, app settings, user profile,
|
|
51
|
+
and agent defaults.
|
|
52
|
+
|
|
53
|
+
## patchPlatformPreferences
|
|
54
|
+
|
|
55
|
+
Patches shell user preferences.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const next = await patchPlatformPreferences({
|
|
59
|
+
theme: 'dark',
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Returns the merged user preferences object.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Storage
|
|
2
|
+
|
|
3
|
+
Permission: `filesystem`
|
|
4
|
+
|
|
5
|
+
Storage helpers read and write JSON files in the shell-managed PureDesktop data
|
|
6
|
+
folder. These helpers use the same bridge path as the exposed
|
|
7
|
+
`storage.readJson` and `storage.writeJson` methods.
|
|
8
|
+
|
|
9
|
+
Import from the generated app bridge:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import {
|
|
13
|
+
readPlatformStorageJson,
|
|
14
|
+
writePlatformStorageJson,
|
|
15
|
+
} from '../../src/bridge/platformBridge'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## readPlatformStorageJson
|
|
19
|
+
|
|
20
|
+
Reads an app JSON store file.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const result = await readPlatformStorageJson({
|
|
24
|
+
appSlug: APP_SLUG,
|
|
25
|
+
fileName: 'items.json',
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
|
|
31
|
+
- `path`: resolved storage path.
|
|
32
|
+
- `value`: parsed JSON value, or `null` when the file is absent.
|
|
33
|
+
|
|
34
|
+
## writePlatformStorageJson
|
|
35
|
+
|
|
36
|
+
Writes an app JSON store file.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
await writePlatformStorageJson({
|
|
40
|
+
appSlug: APP_SLUG,
|
|
41
|
+
fileName: 'items.json',
|
|
42
|
+
value: { items: [] },
|
|
43
|
+
})
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Returns `{ path, ok: true }`.
|
|
47
|
+
|
|
48
|
+
Storage file names must be file names ending with `.json`.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Plugin Manifest
|
|
2
|
+
|
|
3
|
+
`plugin.json` is the shell contract for discovery, permissions, routing, and app
|
|
4
|
+
agent tools.
|
|
5
|
+
|
|
6
|
+
## Required Shape
|
|
7
|
+
|
|
8
|
+
```json
|
|
9
|
+
{
|
|
10
|
+
"schemaVersion": 1,
|
|
11
|
+
"id": "{{PLUGIN_ID}}",
|
|
12
|
+
"name": "{{APP_TITLE}}",
|
|
13
|
+
"permissions": ["network", "settings", "agents"],
|
|
14
|
+
"entrypoint": {
|
|
15
|
+
"kind": "dev-url",
|
|
16
|
+
"url": "http://localhost:{{APP_PORT}}"
|
|
17
|
+
},
|
|
18
|
+
"app": {
|
|
19
|
+
"id": "plugin.{{APP_SLUG}}",
|
|
20
|
+
"slug": "{{APP_SLUG}}",
|
|
21
|
+
"name": "{{APP_TITLE}}",
|
|
22
|
+
"navigationLabel": "{{APP_TITLE}}",
|
|
23
|
+
"productName": "pure.{{APP_SLUG}}",
|
|
24
|
+
"kind": "{{APP_SLUG}}",
|
|
25
|
+
"description": "{{APP_TITLE}} - built with PureDesktop.",
|
|
26
|
+
"usePureDesktopAiPanel": true
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`app.slug` is the API scope for settings, storage, app agent sessions, and tab
|
|
32
|
+
routing. `src/constants.ts` `APP_SLUG` must match `plugin.json` `app.slug`.
|
|
33
|
+
|
|
34
|
+
## Entrypoint
|
|
35
|
+
|
|
36
|
+
Development apps use:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"entrypoint": {
|
|
41
|
+
"kind": "dev-url",
|
|
42
|
+
"url": "http://localhost:{{APP_PORT}}"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Built apps use a static bundle registered from `dist/`. The validator checks the
|
|
48
|
+
build output before registration.
|
|
49
|
+
|
|
50
|
+
## Permissions
|
|
51
|
+
|
|
52
|
+
Declare every shell capability used by bridge helpers:
|
|
53
|
+
|
|
54
|
+
| Permission | Enables |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `settings` | `settings.*`, app settings, user preferences |
|
|
57
|
+
| `filesystem` | `fs.*`, `dialog.*`, `storage.*`, file open routing |
|
|
58
|
+
| `network` | `network.fetch`, hosted entrypoints, OAuth helpers |
|
|
59
|
+
| `render` | document render and print helpers |
|
|
60
|
+
| `assets` | asset library helpers |
|
|
61
|
+
| `agents` | app agent sessions and runtime tool handlers |
|
|
62
|
+
|
|
63
|
+
## App Agent Tools
|
|
64
|
+
|
|
65
|
+
Declare app-owned tools under `app.agents.tools`:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"app": {
|
|
70
|
+
"agents": {
|
|
71
|
+
"tools": [
|
|
72
|
+
{
|
|
73
|
+
"name": "listItems",
|
|
74
|
+
"description": "List the current items with ids, titles, status, and next action.",
|
|
75
|
+
"inputSchema": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"properties": {
|
|
78
|
+
"status": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"description": "Optional status filter."
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"additionalProperties": false
|
|
84
|
+
},
|
|
85
|
+
"requiresApproval": false
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The manifest declaration exposes the tool to the shell agent. Runtime handler
|
|
94
|
+
registration in the iframe executes the tool.
|
|
95
|
+
|
|
96
|
+
## Validation
|
|
97
|
+
|
|
98
|
+
Run:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm run typecheck
|
|
102
|
+
npm run build
|
|
103
|
+
npm run puredesktop:check
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The validator checks manifest identity, required scripts and dependencies,
|
|
107
|
+
`agents.md`, `app.usePureDesktopAiPanel`, permissions, built output, and declared
|
|
108
|
+
agent tool presence in source and dist.
|
|
@@ -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.
|
package/template/package.json
CHANGED
|
@@ -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
|
-
"
|
|
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": {
|
package/template/plugin.json
CHANGED
|
@@ -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
|
|
17
|
+
"description": "{{APP_TITLE}} - built with PureDesktop.",
|
|
18
18
|
"usePureDesktopAiPanel": true
|
|
19
19
|
}
|
|
20
20
|
}
|