@puredesktop/create-app 2.1.7 → 2.1.9
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 +89 -88
- package/package.json +1 -1
- package/template/README.md +3 -1
- package/template/docs/README.md +8 -3
- package/template/docs/howtos/adding-agent-tool.md +106 -0
- package/template/docs/howtos/persisting-app-state.md +242 -0
- package/template/docs/plugin-manifest.md +1 -1
- package/template/docs/theme-vars.md +138 -138
- package/template/docs/tool-handlers.md +3 -0
- package/template/docs/ui-and-components.md +75 -51
- package/template/package.json +1 -1
- package/template/plugin.json +1 -1
- package/template/scripts/validate-puredesktop-app.mjs +46 -8
- package/template/src/App.tsx +5 -4
- package/template/src/components/AppShell.tsx +11 -33
- package/template/src/components/StarterWorkspace.tsx +154 -0
- package/template/src/constants.ts +2 -1
- package/template/src/lib/starterWorkspace.ts +29 -0
- package/template/vite.config.js +13 -10
|
@@ -1,138 +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 | Light value | Dark value |
|
|
13
|
-
| --- | --- | --- | --- |
|
|
14
|
-
| `--platform-colors-accent` | colors | `#1b1b1e` | `#e5e5e9` |
|
|
15
|
-
| `--platform-colors-accent-hover` | colors | `#1b1b1e` | `#eaeaee` |
|
|
16
|
-
| `--platform-colors-accent-muted` | colors | `#eaeaee` | `#313135` |
|
|
17
|
-
| `--platform-colors-app-viewport` | colors | `#fbfbfc` | `#202024` |
|
|
18
|
-
| `--platform-colors-bg` | colors | `#fbfbfc` | `#202024` |
|
|
19
|
-
| `--platform-colors-border` | colors | `#e5e5e9` | `#313135` |
|
|
20
|
-
| `--platform-colors-border-strong` | colors | `#dadade` | `#43434a` |
|
|
21
|
-
| `--platform-colors-chrome-tab-strip` | colors | `#ececef` | `#1b1b1e` |
|
|
22
|
-
| `--platform-colors-chrome-titlebar` | colors | `#fbfbfc` | `#202024` |
|
|
23
|
-
| `--platform-colors-danger` | colors | `#b23a2e` | `#e0796e` |
|
|
24
|
-
| `--platform-colors-divider` | colors | `#e5e5e9` | `#313135` |
|
|
25
|
-
| `--platform-colors-elevated` | colors | `#ffffff` | `#26262a` |
|
|
26
|
-
| `--platform-colors-focus` | colors | `#1b1b1e` | `#e5e5e9` |
|
|
27
|
-
| `--platform-colors-info` | colors | `#3b6fb0` | `#6fa0e0` |
|
|
28
|
-
| `--platform-colors-selection` | colors | `#eaeaee` | `#313135` |
|
|
29
|
-
| `--platform-colors-semantic-blue` | colors | `#3b6fb0` | `#6fa0e0` |
|
|
30
|
-
| `--platform-colors-semantic-blue-border` | colors | `#b9cbe4` | `#365373` |
|
|
31
|
-
| `--platform-colors-semantic-blue-muted` | colors | `#e8f0fb` | `#1c2a3c` |
|
|
32
|
-
| `--platform-colors-semantic-blue-text` | colors | `#244f87` | `#a8c8f0` |
|
|
33
|
-
| `--platform-colors-semantic-green` | colors | `#1f8a55` | `#57b988` |
|
|
34
|
-
| `--platform-colors-semantic-green-border` | colors | `#1f8a55` | `#32684d` |
|
|
35
|
-
| `--platform-colors-semantic-green-muted` | colors | `#eef7f1` | `#18342a` |
|
|
36
|
-
| `--platform-colors-semantic-green-text` | colors | `#16683f` | `#8fe0b6` |
|
|
37
|
-
| `--platform-colors-semantic-orange` | colors | `#c98a2b` | `#e0b257` |
|
|
38
|
-
| `--platform-colors-semantic-orange-border` | colors | `#e3c98f` | `#6d5b2f` |
|
|
39
|
-
| `--platform-colors-semantic-orange-muted` | colors | `#f3e5c8` | `#3a3322` |
|
|
40
|
-
| `--platform-colors-semantic-orange-text` | colors | `#7a5114` | `#f0ce8a` |
|
|
41
|
-
| `--platform-colors-semantic-red` | colors | `#b23a2e` | `#e0796e` |
|
|
42
|
-
| `--platform-colors-semantic-red-border` | colors | `#dfb7b1` | `#753f38` |
|
|
43
|
-
| `--platform-colors-semantic-red-muted` | colors | `#f4dfdc` | `#3a211d` |
|
|
44
|
-
| `--platform-colors-semantic-red-text` | colors | `#8a2d24` | `#f0a89e` |
|
|
45
|
-
| `--platform-colors-success` | colors | `#1f8a55` | `#57b988` |
|
|
46
|
-
| `--platform-colors-surface` | colors | `#f8f8fa` | `#26262a` |
|
|
47
|
-
| `--platform-colors-surface-active` | colors | `#eaeaee` | `#161619` |
|
|
48
|
-
| `--platform-colors-surface-hover` | colors | `#eaeaee` | `#2c2c30` |
|
|
49
|
-
| `--platform-colors-text` | colors | `#1b1b1e` | `#ececef` |
|
|
50
|
-
| `--platform-colors-text-danger` | colors | `#b23a2e` | `#e0796e` |
|
|
51
|
-
| `--platform-colors-text-disabled` | colors | `#9a9aa0` | `#6e6e74` |
|
|
52
|
-
| `--platform-colors-text-inverse` | colors | `#ffffff` | `#1b1b1e` |
|
|
53
|
-
| `--platform-colors-text-secondary` | colors | `#3a3a3e` | `#c9c9cf` |
|
|
54
|
-
| `--platform-colors-warning` | colors | `#c98a2b` | `#e0b257` |
|
|
55
|
-
| `--platform-radius-full` | radius | `999px` | `999px` |
|
|
56
|
-
| `--platform-radius-lg` | radius | `0px` | `0px` |
|
|
57
|
-
| `--platform-radius-md` | radius | `0px` | `0px` |
|
|
58
|
-
| `--platform-radius-sm` | radius | `0px` | `0px` |
|
|
59
|
-
| `--platform-shadow-lg` | shadow | `0 18px 48px rgb(18 18 22 / 0.13)` | `0 18px 44px rgba(0, 0, 0, 0.38)` |
|
|
60
|
-
| `--platform-shadow-md` | shadow | `0 8px 24px rgb(18 18 22 / 0.08)` | `0 8px 24px rgba(0, 0, 0, 0.3)` |
|
|
61
|
-
| `--platform-shadow-sm` | shadow | `none` | `none` |
|
|
62
|
-
| `--platform-spacing-lg` | spacing | `16px` | `16px` |
|
|
63
|
-
| `--platform-spacing-md` | spacing | `12px` | `12px` |
|
|
64
|
-
| `--platform-spacing-sm` | spacing | `8px` | `8px` |
|
|
65
|
-
| `--platform-spacing-xl` | spacing | `24px` | `24px` |
|
|
66
|
-
| `--platform-spacing-xs` | spacing | `4px` | `4px` |
|
|
67
|
-
| `--platform-spacing-xxl` | spacing | `32px` | `32px` |
|
|
68
|
-
| `--platform-transition-base` | transition | `200ms ease` | `200ms ease` |
|
|
69
|
-
| `--platform-transition-fast` | transition | `120ms ease` | `120ms ease` |
|
|
70
|
-
| `--platform-transition-slow` | transition | `320ms ease` | `320ms ease` |
|
|
71
|
-
| `--platform-typography-font-family` | typography | `"Archivo", system-ui, -apple-system, "Segoe UI", sans-serif` | `"Archivo", system-ui, -apple-system, "Segoe UI", sans-serif` |
|
|
72
|
-
| `--platform-typography-font-family-content` | typography | `'Source Serif 4', Georgia, serif` | `'Source Serif 4', Georgia, serif` |
|
|
73
|
-
| `--platform-typography-font-family-mono` | typography | `'JetBrains Mono', ui-monospace, monospace` | `'JetBrains Mono', ui-monospace, monospace` |
|
|
74
|
-
| `--platform-typography-font-size-base` | typography | `14.5px` | `14.5px` |
|
|
75
|
-
| `--platform-typography-font-size-lg` | typography | `17px` | `17px` |
|
|
76
|
-
| `--platform-typography-font-size-sm` | typography | `13px` | `13px` |
|
|
77
|
-
| `--platform-typography-font-size-xl` | typography | `24px` | `24px` |
|
|
78
|
-
| `--platform-typography-font-size-xs` | typography | `11px` | `11px` |
|
|
79
|
-
| `--platform-typography-font-weight-bold` | typography | `600` | `600` |
|
|
80
|
-
| `--platform-typography-font-weight-medium` | typography | `500` | `500` |
|
|
81
|
-
| `--platform-typography-font-weight-normal` | typography | `400` | `400` |
|
|
82
|
-
| `--platform-typography-line-height-base` | typography | `1.6` | `1.6` |
|
|
83
|
-
| `--platform-typography-line-height-tight` | typography | `1.25` | `1.25` |
|
|
84
|
-
| `--platform-z-index-zi-app-base` | zIndex | `0` | `0` |
|
|
85
|
-
| `--platform-z-index-zi-app-chrome` | zIndex | `20` | `20` |
|
|
86
|
-
| `--platform-z-index-zi-app-menus` | zIndex | `100` | `100` |
|
|
87
|
-
| `--platform-z-index-zi-app-modal` | zIndex | `300` | `300` |
|
|
88
|
-
| `--platform-z-index-zi-app-sidebar` | zIndex | `10` | `10` |
|
|
89
|
-
| `--platform-z-index-zi-app-toast` | zIndex | `200` | `200` |
|
|
90
|
-
| `--platform-z-index-zi-app-viewport-toolbar` | zIndex | `25` | `25` |
|
|
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.
|
|
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 | Light value | Dark value |
|
|
13
|
+
| --- | --- | --- | --- |
|
|
14
|
+
| `--platform-colors-accent` | colors | `#1b1b1e` | `#e5e5e9` |
|
|
15
|
+
| `--platform-colors-accent-hover` | colors | `#1b1b1e` | `#eaeaee` |
|
|
16
|
+
| `--platform-colors-accent-muted` | colors | `#eaeaee` | `#313135` |
|
|
17
|
+
| `--platform-colors-app-viewport` | colors | `#fbfbfc` | `#202024` |
|
|
18
|
+
| `--platform-colors-bg` | colors | `#fbfbfc` | `#202024` |
|
|
19
|
+
| `--platform-colors-border` | colors | `#e5e5e9` | `#313135` |
|
|
20
|
+
| `--platform-colors-border-strong` | colors | `#dadade` | `#43434a` |
|
|
21
|
+
| `--platform-colors-chrome-tab-strip` | colors | `#ececef` | `#1b1b1e` |
|
|
22
|
+
| `--platform-colors-chrome-titlebar` | colors | `#fbfbfc` | `#202024` |
|
|
23
|
+
| `--platform-colors-danger` | colors | `#b23a2e` | `#e0796e` |
|
|
24
|
+
| `--platform-colors-divider` | colors | `#e5e5e9` | `#313135` |
|
|
25
|
+
| `--platform-colors-elevated` | colors | `#ffffff` | `#26262a` |
|
|
26
|
+
| `--platform-colors-focus` | colors | `#1b1b1e` | `#e5e5e9` |
|
|
27
|
+
| `--platform-colors-info` | colors | `#3b6fb0` | `#6fa0e0` |
|
|
28
|
+
| `--platform-colors-selection` | colors | `#eaeaee` | `#313135` |
|
|
29
|
+
| `--platform-colors-semantic-blue` | colors | `#3b6fb0` | `#6fa0e0` |
|
|
30
|
+
| `--platform-colors-semantic-blue-border` | colors | `#b9cbe4` | `#365373` |
|
|
31
|
+
| `--platform-colors-semantic-blue-muted` | colors | `#e8f0fb` | `#1c2a3c` |
|
|
32
|
+
| `--platform-colors-semantic-blue-text` | colors | `#244f87` | `#a8c8f0` |
|
|
33
|
+
| `--platform-colors-semantic-green` | colors | `#1f8a55` | `#57b988` |
|
|
34
|
+
| `--platform-colors-semantic-green-border` | colors | `#1f8a55` | `#32684d` |
|
|
35
|
+
| `--platform-colors-semantic-green-muted` | colors | `#eef7f1` | `#18342a` |
|
|
36
|
+
| `--platform-colors-semantic-green-text` | colors | `#16683f` | `#8fe0b6` |
|
|
37
|
+
| `--platform-colors-semantic-orange` | colors | `#c98a2b` | `#e0b257` |
|
|
38
|
+
| `--platform-colors-semantic-orange-border` | colors | `#e3c98f` | `#6d5b2f` |
|
|
39
|
+
| `--platform-colors-semantic-orange-muted` | colors | `#f3e5c8` | `#3a3322` |
|
|
40
|
+
| `--platform-colors-semantic-orange-text` | colors | `#7a5114` | `#f0ce8a` |
|
|
41
|
+
| `--platform-colors-semantic-red` | colors | `#b23a2e` | `#e0796e` |
|
|
42
|
+
| `--platform-colors-semantic-red-border` | colors | `#dfb7b1` | `#753f38` |
|
|
43
|
+
| `--platform-colors-semantic-red-muted` | colors | `#f4dfdc` | `#3a211d` |
|
|
44
|
+
| `--platform-colors-semantic-red-text` | colors | `#8a2d24` | `#f0a89e` |
|
|
45
|
+
| `--platform-colors-success` | colors | `#1f8a55` | `#57b988` |
|
|
46
|
+
| `--platform-colors-surface` | colors | `#f8f8fa` | `#26262a` |
|
|
47
|
+
| `--platform-colors-surface-active` | colors | `#eaeaee` | `#161619` |
|
|
48
|
+
| `--platform-colors-surface-hover` | colors | `#eaeaee` | `#2c2c30` |
|
|
49
|
+
| `--platform-colors-text` | colors | `#1b1b1e` | `#ececef` |
|
|
50
|
+
| `--platform-colors-text-danger` | colors | `#b23a2e` | `#e0796e` |
|
|
51
|
+
| `--platform-colors-text-disabled` | colors | `#9a9aa0` | `#6e6e74` |
|
|
52
|
+
| `--platform-colors-text-inverse` | colors | `#ffffff` | `#1b1b1e` |
|
|
53
|
+
| `--platform-colors-text-secondary` | colors | `#3a3a3e` | `#c9c9cf` |
|
|
54
|
+
| `--platform-colors-warning` | colors | `#c98a2b` | `#e0b257` |
|
|
55
|
+
| `--platform-radius-full` | radius | `999px` | `999px` |
|
|
56
|
+
| `--platform-radius-lg` | radius | `0px` | `0px` |
|
|
57
|
+
| `--platform-radius-md` | radius | `0px` | `0px` |
|
|
58
|
+
| `--platform-radius-sm` | radius | `0px` | `0px` |
|
|
59
|
+
| `--platform-shadow-lg` | shadow | `0 18px 48px rgb(18 18 22 / 0.13)` | `0 18px 44px rgba(0, 0, 0, 0.38)` |
|
|
60
|
+
| `--platform-shadow-md` | shadow | `0 8px 24px rgb(18 18 22 / 0.08)` | `0 8px 24px rgba(0, 0, 0, 0.3)` |
|
|
61
|
+
| `--platform-shadow-sm` | shadow | `none` | `none` |
|
|
62
|
+
| `--platform-spacing-lg` | spacing | `16px` | `16px` |
|
|
63
|
+
| `--platform-spacing-md` | spacing | `12px` | `12px` |
|
|
64
|
+
| `--platform-spacing-sm` | spacing | `8px` | `8px` |
|
|
65
|
+
| `--platform-spacing-xl` | spacing | `24px` | `24px` |
|
|
66
|
+
| `--platform-spacing-xs` | spacing | `4px` | `4px` |
|
|
67
|
+
| `--platform-spacing-xxl` | spacing | `32px` | `32px` |
|
|
68
|
+
| `--platform-transition-base` | transition | `200ms ease` | `200ms ease` |
|
|
69
|
+
| `--platform-transition-fast` | transition | `120ms ease` | `120ms ease` |
|
|
70
|
+
| `--platform-transition-slow` | transition | `320ms ease` | `320ms ease` |
|
|
71
|
+
| `--platform-typography-font-family` | typography | `"Archivo", system-ui, -apple-system, "Segoe UI", sans-serif` | `"Archivo", system-ui, -apple-system, "Segoe UI", sans-serif` |
|
|
72
|
+
| `--platform-typography-font-family-content` | typography | `'Source Serif 4', Georgia, serif` | `'Source Serif 4', Georgia, serif` |
|
|
73
|
+
| `--platform-typography-font-family-mono` | typography | `'JetBrains Mono', ui-monospace, monospace` | `'JetBrains Mono', ui-monospace, monospace` |
|
|
74
|
+
| `--platform-typography-font-size-base` | typography | `14.5px` | `14.5px` |
|
|
75
|
+
| `--platform-typography-font-size-lg` | typography | `17px` | `17px` |
|
|
76
|
+
| `--platform-typography-font-size-sm` | typography | `13px` | `13px` |
|
|
77
|
+
| `--platform-typography-font-size-xl` | typography | `24px` | `24px` |
|
|
78
|
+
| `--platform-typography-font-size-xs` | typography | `11px` | `11px` |
|
|
79
|
+
| `--platform-typography-font-weight-bold` | typography | `600` | `600` |
|
|
80
|
+
| `--platform-typography-font-weight-medium` | typography | `500` | `500` |
|
|
81
|
+
| `--platform-typography-font-weight-normal` | typography | `400` | `400` |
|
|
82
|
+
| `--platform-typography-line-height-base` | typography | `1.6` | `1.6` |
|
|
83
|
+
| `--platform-typography-line-height-tight` | typography | `1.25` | `1.25` |
|
|
84
|
+
| `--platform-z-index-zi-app-base` | zIndex | `0` | `0` |
|
|
85
|
+
| `--platform-z-index-zi-app-chrome` | zIndex | `20` | `20` |
|
|
86
|
+
| `--platform-z-index-zi-app-menus` | zIndex | `100` | `100` |
|
|
87
|
+
| `--platform-z-index-zi-app-modal` | zIndex | `300` | `300` |
|
|
88
|
+
| `--platform-z-index-zi-app-sidebar` | zIndex | `10` | `10` |
|
|
89
|
+
| `--platform-z-index-zi-app-toast` | zIndex | `200` | `200` |
|
|
90
|
+
| `--platform-z-index-zi-app-viewport-toolbar` | zIndex | `25` | `25` |
|
|
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.
|
|
@@ -7,6 +7,9 @@ App agent tools have two halves:
|
|
|
7
7
|
|
|
8
8
|
The names must match exactly.
|
|
9
9
|
|
|
10
|
+
For the exact edit checklist when adding a new tool, read
|
|
11
|
+
[Adding an app agent tool](./adding-agent-tool.md).
|
|
12
|
+
|
|
10
13
|
## Generated Structure
|
|
11
14
|
|
|
12
15
|
When the manifest includes `app.agents.tools`, the generator creates this
|
|
@@ -1,35 +1,56 @@
|
|
|
1
|
-
# UI And Components
|
|
2
|
-
|
|
3
|
-
The generated app starts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
# UI And Components
|
|
2
|
+
|
|
3
|
+
The generated app starts with a small starter workspace so the iframe has a
|
|
4
|
+
useful screen while the first builder task runs. Treat the starter as temporary
|
|
5
|
+
scaffold, not product UI.
|
|
6
|
+
|
|
7
|
+
`src/App.tsx` owns bridge readiness and `AppFrame`. `src/components/AppShell.tsx`
|
|
8
|
+
is the thin composition root after boot succeeds. Do not turn either file into
|
|
9
|
+
the product implementation.
|
|
10
|
+
|
|
11
|
+
## AppFrame
|
|
12
|
+
|
|
13
|
+
Every render path in `src/App.tsx` uses `AppFrame`. This gives the iframe the
|
|
14
|
+
PureDesktop reset, theme CSS variables, and shell-compatible surface styling.
|
|
15
|
+
|
|
16
|
+
## App Structure
|
|
17
|
+
|
|
18
|
+
Use the first real product task to replace the starter workspace with app-owned
|
|
19
|
+
surfaces. Keep responsibilities split:
|
|
20
|
+
|
|
21
|
+
- `src/App.tsx`: bridge readiness, boot loading, error states, and `AppFrame`.
|
|
22
|
+
- `src/components/AppShell.tsx`: thin composition root that wires boot data into
|
|
23
|
+
product surfaces.
|
|
24
|
+
- `src/components/*`: product screens, panels, lists, editors, dialogs, and
|
|
25
|
+
controls.
|
|
26
|
+
- `src/hooks/*`: reusable workflow state, effects, synchronization, and bridge
|
|
27
|
+
orchestration.
|
|
28
|
+
- `src/lib/*`: domain types, reducers, rules, transforms, fixtures, and tests.
|
|
29
|
+
|
|
30
|
+
Start with controlled, simple modules:
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
src/
|
|
34
|
+
components/
|
|
35
|
+
AppShell.tsx
|
|
36
|
+
StarterWorkspace.tsx # replace when the first product surface exists
|
|
37
|
+
ProjectBoard.tsx
|
|
38
|
+
ProjectDetail.tsx
|
|
39
|
+
hooks/
|
|
40
|
+
useProjects.ts
|
|
41
|
+
lib/
|
|
42
|
+
projects.ts
|
|
43
|
+
projects.test.ts
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Keep state local only while it is display-only. When state drives multiple
|
|
47
|
+
components, persistence, agent tools, bridge calls, or domain rules, move it into
|
|
48
|
+
an app hook or `src/lib` module before adding more screens.
|
|
49
|
+
|
|
50
|
+
## Platform Components
|
|
51
|
+
|
|
31
52
|
Import shared UI by exact exported package paths before creating custom
|
|
32
|
-
primitives.
|
|
53
|
+
primitives. Use existing app folders before creating new ones.
|
|
33
54
|
|
|
34
55
|
Useful starting points:
|
|
35
56
|
|
|
@@ -38,23 +59,26 @@ Useful starting points:
|
|
|
38
59
|
- `Heading` from `@puredesktop/puredesktop-ui-bridge/components/common/typography/Heading`
|
|
39
60
|
- `Text` from `@puredesktop/puredesktop-ui-bridge/components/common/typography/Text`
|
|
40
61
|
- `Button` from `@puredesktop/puredesktop-ui-bridge/components/common/buttons/Button`
|
|
41
|
-
|
|
42
|
-
## Styling
|
|
43
|
-
|
|
44
|
-
Use styled-components and platform CSS variables:
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
const Root = styled.div`
|
|
48
|
-
display: flex;
|
|
49
|
-
flex-direction: column;
|
|
50
|
-
gap: var(--platform-spacing-md);
|
|
51
|
-
color: var(--platform-colors-text);
|
|
52
|
-
background: var(--platform-colors-bg);
|
|
53
|
-
`
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Use app-local CSS variables only for app-specific meaning, for example
|
|
57
|
-
`--{{APP_SLUG}}-accent`.
|
|
58
|
-
|
|
59
|
-
See [Theme CSS variables](./theme-vars.md) for the generated list of available
|
|
60
|
-
platform variables by domain.
|
|
62
|
+
|
|
63
|
+
## Styling
|
|
64
|
+
|
|
65
|
+
Use styled-components and platform CSS variables:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const Root = styled.div`
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
gap: var(--platform-spacing-md);
|
|
72
|
+
color: var(--platform-colors-text);
|
|
73
|
+
background: var(--platform-colors-bg);
|
|
74
|
+
`
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Use app-local CSS variables only for app-specific meaning, for example
|
|
78
|
+
`--{{APP_SLUG}}-accent`.
|
|
79
|
+
|
|
80
|
+
See [Theme CSS variables](./theme-vars.md) for the generated list of available
|
|
81
|
+
platform variables by domain.
|
|
82
|
+
|
|
83
|
+
Avoid inline styles except for tiny dynamic values that cannot be represented as
|
|
84
|
+
props on a styled component.
|
package/template/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"puredesktop:check": "node scripts/validate-puredesktop-app.mjs"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@puredesktop/puredesktop-ui-bridge": "2.1.
|
|
13
|
+
"@puredesktop/puredesktop-ui-bridge": "2.1.8",
|
|
14
14
|
"react": "^19.1.0",
|
|
15
15
|
"react-dom": "^19.1.0",
|
|
16
16
|
"styled-components": "^6.1.18"
|
package/template/plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"id": "{{PLUGIN_ID}}",
|
|
4
4
|
"name": "{{APP_TITLE}}",
|
|
5
|
-
"permissions": ["network", "settings", "agents"],
|
|
5
|
+
"permissions": ["network", "settings", "filesystem", "agents"],
|
|
6
6
|
"entrypoint": {
|
|
7
7
|
"kind": "dev-url",
|
|
8
8
|
"url": "http://localhost:{{APP_PORT}}"
|
|
@@ -20,9 +20,10 @@ if (packageJson) {
|
|
|
20
20
|
validatePackageJson(packageJson)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
if (manifest) {
|
|
24
|
-
validateManifest(manifest)
|
|
25
|
-
|
|
23
|
+
if (manifest) {
|
|
24
|
+
validateManifest(manifest)
|
|
25
|
+
await validateBridgePermissionUsage(manifest)
|
|
26
|
+
}
|
|
26
27
|
|
|
27
28
|
if (await isDirectory(distPath)) {
|
|
28
29
|
info.push('dist folder is present for static registration.')
|
|
@@ -135,7 +136,7 @@ function validateManifest(manifest) {
|
|
|
135
136
|
if (errors.length === 0) info.push('plugin.json is valid.')
|
|
136
137
|
}
|
|
137
138
|
|
|
138
|
-
function validateEntrypoint(entrypoint, permissions) {
|
|
139
|
+
function validateEntrypoint(entrypoint, permissions) {
|
|
139
140
|
if (!isPlainObject(entrypoint)) {
|
|
140
141
|
errors.push('entrypoint is required.')
|
|
141
142
|
return
|
|
@@ -169,10 +170,47 @@ function validateEntrypoint(entrypoint, permissions) {
|
|
|
169
170
|
}
|
|
170
171
|
return
|
|
171
172
|
}
|
|
172
|
-
errors.push(`Unsupported entrypoint kind: ${String(entrypoint.kind ?? '') || '(missing)'}.`)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async function
|
|
173
|
+
errors.push(`Unsupported entrypoint kind: ${String(entrypoint.kind ?? '') || '(missing)'}.`)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function validateBridgePermissionUsage(manifest) {
|
|
177
|
+
const permissions = Array.isArray(manifest.permissions) ? manifest.permissions : []
|
|
178
|
+
const sourceText = await readProjectText(join(ROOT, 'src'))
|
|
179
|
+
const requirements = [
|
|
180
|
+
{
|
|
181
|
+
permission: 'filesystem',
|
|
182
|
+
hints: [
|
|
183
|
+
'/bridge/storage',
|
|
184
|
+
'/bridge/fs',
|
|
185
|
+
'/bridge/dialog',
|
|
186
|
+
'/bridge/react/usePlatformJsonStore',
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
permission: 'settings',
|
|
191
|
+
hints: [
|
|
192
|
+
'/bridge/appSettings',
|
|
193
|
+
'/bridge/preferences',
|
|
194
|
+
'/bridge/react/usePlatformAppSettings',
|
|
195
|
+
'/bridge/react/usePlatformPreferences',
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
permission: 'network',
|
|
200
|
+
hints: ['/bridge/network', '/bridge/vision'],
|
|
201
|
+
},
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
for (const requirement of requirements) {
|
|
205
|
+
if (permissions.includes(requirement.permission)) continue
|
|
206
|
+
if (!requirement.hints.some(hint => sourceText.includes(hint))) continue
|
|
207
|
+
errors.push(
|
|
208
|
+
`Source imports ${requirement.permission}-gated bridge helpers, so plugin.json must include the ${requirement.permission} permission.`,
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function validateAgentTools(manifest, distPath) {
|
|
176
214
|
const tools = manifest.app?.agents?.tools ?? []
|
|
177
215
|
if (!Array.isArray(tools) || tools.length === 0) {
|
|
178
216
|
info.push('No app agent tools declared.')
|
package/template/src/App.tsx
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { AppFrame } from '@puredesktop/puredesktop-ui-bridge/components/common/containers/AppFrame'
|
|
2
2
|
import { EmptyState } from '@puredesktop/puredesktop-ui-bridge/components/common/feedback/EmptyState'
|
|
3
3
|
import { usePlatformBridge } from '@puredesktop/puredesktop-ui-bridge/bridge/react/usePlatformBridge'
|
|
4
|
-
import { AppShell } from './components/AppShell'
|
|
5
|
-
import { isStandaloneDevMode } from './bridge/platformBridge'
|
|
6
|
-
import {
|
|
4
|
+
import { AppShell } from './components/AppShell'
|
|
5
|
+
import { isStandaloneDevMode } from './bridge/platformBridge'
|
|
6
|
+
import { APP_TITLE } from './constants'
|
|
7
|
+
import { useAppBoot } from './hooks/useAppBoot'
|
|
7
8
|
|
|
8
9
|
export function App(): React.ReactElement {
|
|
9
10
|
const { error: bridgeError, ready } = usePlatformBridge()
|
|
@@ -28,7 +29,7 @@ export function App(): React.ReactElement {
|
|
|
28
29
|
<AppFrame>
|
|
29
30
|
<EmptyState
|
|
30
31
|
tone={bootError ? 'error' : 'neutral'}
|
|
31
|
-
title={bootError ? 'Boot failed' :
|
|
32
|
+
title={bootError ? 'Boot failed' : APP_TITLE}
|
|
32
33
|
message={
|
|
33
34
|
bootError
|
|
34
35
|
? bootError.message
|
|
@@ -1,33 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
`
|
|
13
|
-
|
|
14
|
-
export interface AppShellProps {
|
|
15
|
-
settings: AppSettings
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function AppShell({ settings }: AppShellProps): React.ReactElement {
|
|
19
|
-
return (
|
|
20
|
-
<Root>
|
|
21
|
-
<Heading level={1}>{{APP_TITLE}}</Heading>
|
|
22
|
-
<Text>
|
|
23
|
-
Start building in <code>src/components/AppShell.tsx</code>. Bridge boot
|
|
24
|
-
is wired — AppFrame, settings load, and standalone browser dev all work.
|
|
25
|
-
</Text>
|
|
26
|
-
{Object.keys(settings).length > 0 ? (
|
|
27
|
-
<Text size="sm" tone="secondary">
|
|
28
|
-
Loaded {Object.keys(settings).length} setting key(s).
|
|
29
|
-
</Text>
|
|
30
|
-
) : null}
|
|
31
|
-
</Root>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
1
|
+
import type { AppSettings } from '../types'
|
|
2
|
+
import { APP_TITLE } from '../constants'
|
|
3
|
+
import { StarterWorkspace } from './StarterWorkspace'
|
|
4
|
+
|
|
5
|
+
export interface AppShellProps {
|
|
6
|
+
settings: AppSettings
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function AppShell({ settings }: AppShellProps): React.ReactElement {
|
|
10
|
+
return <StarterWorkspace appName={APP_TITLE} settings={settings} />
|
|
11
|
+
}
|