@puredesktop/create-app 2.1.8 → 2.1.10
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 +60 -328
- package/package.json +3 -2
- package/template/README.md +3 -3
- package/template/docs/README.md +9 -8
- package/template/docs/howtos/persisting-app-state.md +242 -242
- package/template/docs/howtos/using-document-editor.md +440 -0
- package/template/docs/plugin-manifest.md +1 -1
- package/template/docs/ui-and-components.md +84 -79
- package/template/gitignore.tpl +6 -1
- package/template/package.json +1 -1
- package/template/plugin.json +1 -1
- package/template/scripts/validate-puredesktop-app.mjs +46 -46
- package/template/src/App.tsx +8 -8
- package/template/src/components/AppShell.tsx +11 -11
- package/template/src/components/StarterWorkspace.tsx +154 -154
- package/template/src/constants.ts +2 -2
- package/template/src/lib/starterWorkspace.ts +29 -29
- package/template/vite.config.js +13 -13
|
@@ -4,12 +4,9 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Published as @puredesktop/create-app -> npm create @puredesktop/app@latest
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import { dirname, join, resolve } from 'node:path'
|
|
9
|
-
import { fileURLToPath } from 'node:url'
|
|
7
|
+
import { scaffoldAppFromFiles } from '../lib/scaffold.mjs'
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
const TEMPLATE_DIR = join(__dirname, '..', 'template')
|
|
9
|
+
class UsageError extends Error {}
|
|
13
10
|
|
|
14
11
|
function usage() {
|
|
15
12
|
console.log(`Usage: npm create @puredesktop/app@latest [dir] [options]
|
|
@@ -18,10 +15,13 @@ Options:
|
|
|
18
15
|
--name <title> Display name (default: derived from dir)
|
|
19
16
|
--slug <slug> App slug for settings/bridge (default: dir name)
|
|
20
17
|
--port <number> Dev server port in plugin.json (default: 5300)
|
|
21
|
-
--
|
|
22
|
-
|
|
23
|
-
--
|
|
24
|
-
|
|
18
|
+
--target-path <path>
|
|
19
|
+
Scaffold into this path instead of [dir]
|
|
20
|
+
--allow-existing Allow target path to already exist
|
|
21
|
+
--manifest-json-file <path>
|
|
22
|
+
Read structured plugin.json manifest draft from a file
|
|
23
|
+
--agents-md-file <path>
|
|
24
|
+
Read structured agents.md content from a file
|
|
25
25
|
|
|
26
26
|
Examples:
|
|
27
27
|
npm create @puredesktop/app@latest my-tool
|
|
@@ -32,11 +32,13 @@ Examples:
|
|
|
32
32
|
function parseArgs(argv) {
|
|
33
33
|
const positional = []
|
|
34
34
|
const options = {
|
|
35
|
+
agentsMdFile: '',
|
|
36
|
+
allowExisting: false,
|
|
37
|
+
manifestJsonFile: '',
|
|
35
38
|
name: '',
|
|
36
|
-
slug: '',
|
|
37
39
|
port: 5300,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
slug: '',
|
|
41
|
+
targetPath: '',
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -57,333 +59,43 @@ function parseArgs(argv) {
|
|
|
57
59
|
options.port = Number(argv[++i])
|
|
58
60
|
continue
|
|
59
61
|
}
|
|
60
|
-
if (arg === '--
|
|
61
|
-
options.
|
|
62
|
+
if (arg === '--target-path') {
|
|
63
|
+
options.targetPath = argv[++i] ?? ''
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
if (arg === '--allow-existing') {
|
|
67
|
+
options.allowExisting = true
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
if (arg === '--manifest-json-file') {
|
|
71
|
+
options.manifestJsonFile = argv[++i] ?? ''
|
|
62
72
|
continue
|
|
63
73
|
}
|
|
64
|
-
if (arg === '--agents-md-
|
|
65
|
-
options.
|
|
74
|
+
if (arg === '--agents-md-file') {
|
|
75
|
+
options.agentsMdFile = argv[++i] ?? ''
|
|
66
76
|
continue
|
|
67
77
|
}
|
|
68
78
|
if (arg.startsWith('-')) {
|
|
69
|
-
|
|
70
|
-
usage()
|
|
71
|
-
process.exit(1)
|
|
79
|
+
throw new UsageError(`Unknown option: ${arg}`)
|
|
72
80
|
}
|
|
73
81
|
positional.push(arg)
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
process.exit(1)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return { targetDir: positional[0] ?? 'puredesktop-app', options }
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function slugify(value) {
|
|
85
|
-
return value
|
|
86
|
-
.trim()
|
|
87
|
-
.toLowerCase()
|
|
88
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
89
|
-
.replace(/^-+|-+$/g, '')
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function titleCaseFromSlug(slug) {
|
|
93
|
-
return slug
|
|
94
|
-
.split('-')
|
|
95
|
-
.filter(Boolean)
|
|
96
|
-
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
97
|
-
.join(' ')
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function decodeBase64Utf8(value, label) {
|
|
101
|
-
if (!isBase64(value)) {
|
|
102
|
-
console.error(`${label} must be valid base64`)
|
|
103
|
-
process.exit(1)
|
|
104
|
-
}
|
|
105
|
-
try {
|
|
106
|
-
return Buffer.from(value, 'base64').toString('utf8')
|
|
107
|
-
} catch (error) {
|
|
108
|
-
console.error(`${label} must be valid base64: ${errorMessage(error)}`)
|
|
109
|
-
process.exit(1)
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function readStructuredManifest(value, port) {
|
|
114
|
-
const text = decodeBase64Utf8(value, '--manifest-json-base64')
|
|
115
|
-
let manifest
|
|
116
|
-
try {
|
|
117
|
-
manifest = JSON.parse(text)
|
|
118
|
-
} catch (error) {
|
|
119
|
-
console.error(`--manifest-json-base64 must decode to JSON: ${errorMessage(error)}`)
|
|
120
|
-
process.exit(1)
|
|
121
|
-
}
|
|
122
|
-
return normalizeStructuredManifest(manifest, port)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function normalizeStructuredManifest(manifest, port) {
|
|
126
|
-
if (!isPlainObject(manifest)) {
|
|
127
|
-
console.error('--manifest-json-base64 must decode to a manifest object')
|
|
128
|
-
process.exit(1)
|
|
129
|
-
}
|
|
130
|
-
if (manifest.schemaVersion !== 1) {
|
|
131
|
-
console.error('manifest.schemaVersion must be 1')
|
|
132
|
-
process.exit(1)
|
|
133
|
-
}
|
|
134
|
-
const id = requiredString(manifest.id, 'manifest.id')
|
|
135
|
-
const name = requiredString(manifest.name, 'manifest.name')
|
|
136
|
-
if (!isPlainObject(manifest.app)) {
|
|
137
|
-
console.error('manifest.app is required')
|
|
138
|
-
process.exit(1)
|
|
139
|
-
}
|
|
140
|
-
const appSlug = requiredString(manifest.app.slug, 'manifest.app.slug')
|
|
141
|
-
const appName = requiredString(manifest.app.name, 'manifest.app.name')
|
|
142
|
-
if (slugify(appSlug) !== appSlug) {
|
|
143
|
-
console.error('manifest.app.slug must use lowercase letters, numbers, and hyphens')
|
|
144
|
-
process.exit(1)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return stripUndefinedProperties({
|
|
148
|
-
...manifest,
|
|
149
|
-
id,
|
|
150
|
-
name,
|
|
151
|
-
entrypoint: {
|
|
152
|
-
kind: 'dev-url',
|
|
153
|
-
url: `http://localhost:${port}`,
|
|
154
|
-
},
|
|
155
|
-
app: stripUndefinedProperties({
|
|
156
|
-
...manifest.app,
|
|
157
|
-
slug: appSlug,
|
|
158
|
-
name: appName,
|
|
159
|
-
entrypoint: undefined,
|
|
160
|
-
}),
|
|
161
|
-
})
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function readStructuredAgentsMd(value) {
|
|
165
|
-
const text = decodeBase64Utf8(value, '--agents-md-base64')
|
|
166
|
-
if (!text.trim()) {
|
|
167
|
-
console.error('--agents-md-base64 must decode to non-empty agents.md content')
|
|
168
|
-
process.exit(1)
|
|
84
|
+
if (options.targetPath && positional.length > 0) {
|
|
85
|
+
throw new UsageError('Pass either [dir] or --target-path, not both')
|
|
169
86
|
}
|
|
170
|
-
return text.endsWith('\n') ? text : `${text}\n`
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function requiredString(value, label) {
|
|
174
|
-
const text = typeof value === 'string' ? value.trim() : ''
|
|
175
|
-
if (!text) {
|
|
176
|
-
console.error(`${label} is required`)
|
|
177
|
-
process.exit(1)
|
|
178
|
-
}
|
|
179
|
-
return text
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function isPlainObject(value) {
|
|
183
|
-
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
184
|
-
}
|
|
185
87
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (!/^[A-Za-z0-9+/]+={0,2}$/u.test(normalized)) return false
|
|
190
|
-
if (normalized.length % 4 !== 0) return false
|
|
191
|
-
return Buffer.from(normalized, 'base64').toString('base64') === normalized
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function stripUndefinedProperties(value) {
|
|
195
|
-
return Object.fromEntries(
|
|
196
|
-
Object.entries(value).filter(([, entry]) => entry !== undefined),
|
|
197
|
-
)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function errorMessage(error) {
|
|
201
|
-
return error instanceof Error ? error.message : String(error)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function readGeneratedManifest(outDir) {
|
|
205
|
-
try {
|
|
206
|
-
return JSON.parse(readFileSync(join(outDir, 'plugin.json'), 'utf8'))
|
|
207
|
-
} catch (error) {
|
|
208
|
-
console.error(`Generated plugin.json could not be read: ${errorMessage(error)}`)
|
|
209
|
-
process.exit(1)
|
|
88
|
+
return {
|
|
89
|
+
options,
|
|
90
|
+
targetDir: options.targetPath || positional[0] || 'puredesktop-app',
|
|
210
91
|
}
|
|
211
92
|
}
|
|
212
93
|
|
|
213
|
-
function
|
|
214
|
-
|
|
215
|
-
if (!Array.isArray(tools)) return []
|
|
216
|
-
return tools
|
|
217
|
-
.map(tool => (typeof tool?.name === 'string' ? tool.name.trim() : ''))
|
|
218
|
-
.filter(Boolean)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function jsonString(value) {
|
|
222
|
-
return JSON.stringify(value)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function writeAgentToolScaffold(outDir, appSlug, toolNames) {
|
|
226
|
-
if (toolNames.length === 0) return
|
|
227
|
-
|
|
228
|
-
const agentsDir = join(outDir, 'src', 'agents')
|
|
229
|
-
const handlersDir = join(agentsDir, 'handlers')
|
|
230
|
-
const hooksDir = join(outDir, 'src', 'hooks')
|
|
231
|
-
mkdirSync(handlersDir, { recursive: true })
|
|
232
|
-
mkdirSync(hooksDir, { recursive: true })
|
|
233
|
-
|
|
234
|
-
writeFileSync(
|
|
235
|
-
join(agentsDir, 'catalog.ts'),
|
|
236
|
-
`export const APP_AGENT_TOOL_NAMES = ${JSON.stringify(toolNames, null, 2)} as const
|
|
237
|
-
|
|
238
|
-
export const APP_AGENT_LOG_LABEL = ${jsonString(appSlug)}
|
|
239
|
-
`,
|
|
240
|
-
)
|
|
241
|
-
|
|
242
|
-
const handlerEntries = toolNames
|
|
243
|
-
.map(
|
|
244
|
-
name =>
|
|
245
|
-
` ${jsonString(name)}: async () => notImplemented(${jsonString(name)}),`,
|
|
246
|
-
)
|
|
247
|
-
.join('\n')
|
|
248
|
-
|
|
249
|
-
writeFileSync(
|
|
250
|
-
join(handlersDir, 'index.ts'),
|
|
251
|
-
`import { agentToolErrorContent } from '@puredesktop/puredesktop-ui-bridge/bridge/agentToolHelpers'
|
|
252
|
-
import type { AgentToolHandler } from '@puredesktop/puredesktop-ui-bridge/bridge/react/usePlatformAgentTools'
|
|
253
|
-
|
|
254
|
-
function notImplemented(toolName: string) {
|
|
255
|
-
return agentToolErrorContent(
|
|
256
|
-
\`Tool "\${toolName}" is declared in plugin.json but its runtime handler has not been implemented yet.\`,
|
|
257
|
-
)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
export const appAgentHandlers = {
|
|
261
|
-
${handlerEntries}
|
|
262
|
-
} satisfies Record<string, AgentToolHandler>
|
|
263
|
-
`,
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
writeFileSync(
|
|
267
|
-
join(hooksDir, 'useAppAgentTools.ts'),
|
|
268
|
-
`import { usePlatformAgentTools } from '@puredesktop/puredesktop-ui-bridge/bridge/react/usePlatformAgentTools'
|
|
269
|
-
import { APP_AGENT_LOG_LABEL, APP_AGENT_TOOL_NAMES } from '../agents/catalog'
|
|
270
|
-
import { appAgentHandlers } from '../agents/handlers'
|
|
271
|
-
|
|
272
|
-
export function useAppAgentTools(ready: boolean): void {
|
|
273
|
-
usePlatformAgentTools({
|
|
274
|
-
ready,
|
|
275
|
-
tools: APP_AGENT_TOOL_NAMES,
|
|
276
|
-
logLabel: APP_AGENT_LOG_LABEL,
|
|
277
|
-
handlers: appAgentHandlers,
|
|
278
|
-
})
|
|
279
|
-
}
|
|
280
|
-
`,
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
wireAgentToolHook(outDir)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function wireAgentToolHook(outDir) {
|
|
287
|
-
const appPath = join(outDir, 'src', 'App.tsx')
|
|
288
|
-
const source = readFileSync(appPath, 'utf8')
|
|
289
|
-
const importPattern = /import \{ useAppBoot \} from '\.\/hooks\/useAppBoot'\r?\n/
|
|
290
|
-
const callPattern = /( const \{ error: bridgeError, ready \} = usePlatformBridge\(\)\r?\n)/
|
|
291
|
-
|
|
292
|
-
if (!importPattern.test(source) || !callPattern.test(source)) {
|
|
293
|
-
console.error('Generated src/App.tsx does not match the agent tool scaffold insertion points.')
|
|
294
|
-
process.exit(1)
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const withImport = source.replace(
|
|
298
|
-
importPattern,
|
|
299
|
-
match => `${match}import { useAppAgentTools } from './hooks/useAppAgentTools'\n`,
|
|
300
|
-
)
|
|
301
|
-
const withHook = withImport.replace(
|
|
302
|
-
callPattern,
|
|
303
|
-
(_match, line) => `${line} useAppAgentTools(ready)\n`,
|
|
304
|
-
)
|
|
305
|
-
writeFileSync(appPath, withHook)
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
function copyTemplate(srcDir, destDir, vars) {
|
|
309
|
-
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
|
|
310
|
-
const srcPath = join(srcDir, entry.name)
|
|
311
|
-
const destName = entry.name.replace(/\.tpl$/u, '')
|
|
312
|
-
const destPath = join(destDir, destName)
|
|
313
|
-
if (entry.isDirectory()) {
|
|
314
|
-
mkdirSync(destPath, { recursive: true })
|
|
315
|
-
copyTemplate(srcPath, destPath, vars)
|
|
316
|
-
continue
|
|
317
|
-
}
|
|
318
|
-
const raw = readFileSync(srcPath, 'utf8')
|
|
319
|
-
const rendered = raw.replace(/\{\{(\w+)\}\}/g, (_match, key) => vars[key] ?? '')
|
|
320
|
-
writeFileSync(destPath, rendered)
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const { targetDir, options } = parseArgs(process.argv.slice(2))
|
|
325
|
-
const structuredManifest = options.manifestJsonBase64
|
|
326
|
-
? readStructuredManifest(options.manifestJsonBase64, options.port)
|
|
327
|
-
: null
|
|
328
|
-
const structuredAgentsMd = options.agentsMdBase64
|
|
329
|
-
? readStructuredAgentsMd(options.agentsMdBase64)
|
|
330
|
-
: null
|
|
331
|
-
const dirName = targetDir
|
|
332
|
-
const structuredSlug = structuredManifest?.app.slug ?? ''
|
|
333
|
-
const structuredTitle = structuredManifest?.app.name ?? ''
|
|
334
|
-
if (structuredManifest && options.slug && options.slug !== structuredSlug) {
|
|
335
|
-
console.error('--slug must match manifest.app.slug when --manifest-json-base64 is used')
|
|
336
|
-
process.exit(1)
|
|
337
|
-
}
|
|
338
|
-
if (structuredManifest && options.name && options.name !== structuredTitle) {
|
|
339
|
-
console.error('--name must match manifest.app.name when --manifest-json-base64 is used')
|
|
340
|
-
process.exit(1)
|
|
341
|
-
}
|
|
342
|
-
const slug = structuredSlug || options.slug || slugify(dirName) || 'my-app'
|
|
343
|
-
const title = structuredTitle || options.name || titleCaseFromSlug(slug) || 'My App'
|
|
344
|
-
const pluginId = structuredManifest?.id ?? slug
|
|
345
|
-
const outDir = resolve(process.cwd(), dirName)
|
|
346
|
-
|
|
347
|
-
if (existsSync(outDir)) {
|
|
348
|
-
console.error(`Target already exists: ${outDir}`)
|
|
349
|
-
process.exit(1)
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
mkdirSync(outDir, { recursive: true })
|
|
353
|
-
|
|
354
|
-
const vars = {
|
|
355
|
-
APP_TITLE: title,
|
|
356
|
-
APP_SLUG: slug,
|
|
357
|
-
APP_NAME: slug,
|
|
358
|
-
PLUGIN_ID: pluginId,
|
|
359
|
-
APP_PORT: String(options.port),
|
|
360
|
-
PACKAGE_NAME: `@puredesktop/${slug}`,
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
copyTemplate(TEMPLATE_DIR, outDir, vars)
|
|
364
|
-
|
|
365
|
-
if (structuredManifest) {
|
|
366
|
-
writeFileSync(
|
|
367
|
-
join(outDir, 'plugin.json'),
|
|
368
|
-
`${JSON.stringify(structuredManifest, null, 2)}\n`,
|
|
369
|
-
)
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
if (structuredAgentsMd) {
|
|
373
|
-
writeFileSync(join(outDir, 'agents.md'), structuredAgentsMd)
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const generatedManifest = readGeneratedManifest(outDir)
|
|
377
|
-
writeAgentToolScaffold(
|
|
378
|
-
outDir,
|
|
379
|
-
generatedManifest?.app?.slug ?? slug,
|
|
380
|
-
readAgentToolNames(generatedManifest),
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
console.log(`
|
|
94
|
+
function printSuccess({ outDir, port, targetDir, title }) {
|
|
95
|
+
console.log(`
|
|
384
96
|
Created ${title} at ${outDir}
|
|
385
97
|
|
|
386
|
-
cd ${
|
|
98
|
+
cd ${targetDir}
|
|
387
99
|
npm install
|
|
388
100
|
npm run dev
|
|
389
101
|
|
|
@@ -393,9 +105,29 @@ Before shipping:
|
|
|
393
105
|
npm run puredesktop:check
|
|
394
106
|
|
|
395
107
|
Register in PureDesktop (File -> Register App...) with:
|
|
396
|
-
entrypoint: http://localhost:${
|
|
397
|
-
permissions: network, settings, filesystem, agents
|
|
108
|
+
entrypoint: http://localhost:${port}
|
|
109
|
+
permissions: network, settings, filesystem, agents
|
|
398
110
|
|
|
399
|
-
Build product surfaces in src/components, workflow hooks in src/hooks, and domain logic in src/lib.
|
|
400
|
-
Keep src/App.tsx and src/components/AppShell.tsx thin.
|
|
401
|
-
`)
|
|
111
|
+
Build product surfaces in src/components, workflow hooks in src/hooks, and domain logic in src/lib.
|
|
112
|
+
Keep src/App.tsx and src/components/AppShell.tsx thin.
|
|
113
|
+
`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const { options, targetDir } = parseArgs(process.argv.slice(2))
|
|
118
|
+
const result = scaffoldAppFromFiles({
|
|
119
|
+
agentsMdFile: options.agentsMdFile,
|
|
120
|
+
allowExisting: options.allowExisting,
|
|
121
|
+
manifestJsonFile: options.manifestJsonFile,
|
|
122
|
+
name: options.name,
|
|
123
|
+
port: options.port,
|
|
124
|
+
slug: options.slug,
|
|
125
|
+
targetDir,
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
printSuccess(result)
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
131
|
+
if (error instanceof UsageError) usage()
|
|
132
|
+
process.exit(1)
|
|
133
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@puredesktop/create-app",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.10",
|
|
4
4
|
"description": "Scaffold a PureDesktop plugin app (Vite + bridge SDK)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"bin",
|
|
12
|
+
"lib",
|
|
12
13
|
"template"
|
|
13
14
|
],
|
|
14
15
|
"publishConfig": {
|
|
15
|
-
"access": "
|
|
16
|
+
"access": "public",
|
|
16
17
|
"registry": "https://registry.npmjs.org"
|
|
17
18
|
},
|
|
18
19
|
"engines": {
|
package/template/README.md
CHANGED
|
@@ -36,9 +36,9 @@ and declared app agent tools.
|
|
|
36
36
|
- `src/App.tsx`: bridge and boot gates wrapped in `AppFrame`.
|
|
37
37
|
- `src/bridge/platformBridge.ts`: local app bridge exports.
|
|
38
38
|
- `src/hooks/useAppBoot.ts`: boot data loading after bridge readiness.
|
|
39
|
-
- `src/components/AppShell.tsx`: thin app-owned composition root.
|
|
40
|
-
- `src/components/StarterWorkspace.tsx`: temporary starter screen to replace
|
|
41
|
-
when the first product surface exists.
|
|
39
|
+
- `src/components/AppShell.tsx`: thin app-owned composition root.
|
|
40
|
+
- `src/components/StarterWorkspace.tsx`: temporary starter screen to replace
|
|
41
|
+
when the first product surface exists.
|
|
42
42
|
- `docs/`: builder-facing app, bridge, agent, and tool-handler docs.
|
|
43
43
|
|
|
44
44
|
## Builder Docs
|
package/template/docs/README.md
CHANGED
|
@@ -13,10 +13,11 @@ Read these in order when building or repairing an app:
|
|
|
13
13
|
2. [Plugin manifest](./plugin-manifest.md)
|
|
14
14
|
3. [Bridge helpers](./bridge/README.md)
|
|
15
15
|
4. [App agent](./agent.md)
|
|
16
|
-
5. [Tool handlers](./tool-handlers.md)
|
|
16
|
+
5. [Tool handlers](./tool-handlers.md)
|
|
17
17
|
6. [Adding an app agent tool](./howtos/adding-agent-tool.md)
|
|
18
|
-
7. [UI and components](./ui-and-components.md)
|
|
19
|
-
8. [
|
|
18
|
+
7. [UI and components](./ui-and-components.md)
|
|
19
|
+
8. [Using the document editor](./howtos/using-document-editor.md)
|
|
20
|
+
9. [Theme CSS variables](./theme-vars.md)
|
|
20
21
|
|
|
21
22
|
Key files in this scaffold:
|
|
22
23
|
|
|
@@ -26,11 +27,11 @@ Key files in this scaffold:
|
|
|
26
27
|
- `src/App.tsx`: bridge readiness, boot loading, and `AppFrame` wrapper.
|
|
27
28
|
- `src/bridge/platformBridge.ts`: local app bridge surface.
|
|
28
29
|
- `src/hooks/useAppBoot.ts`: app boot data loading after the bridge is ready.
|
|
29
|
-
- `src/components/AppShell.tsx`: thin app-owned composition root.
|
|
30
|
-
- `src/components/StarterWorkspace.tsx`: temporary starter screen to replace
|
|
31
|
-
when the first product surface exists.
|
|
32
|
-
- `src/lib/starterWorkspace.ts`: starter data/helper example showing where
|
|
33
|
-
reusable non-rendering logic belongs.
|
|
30
|
+
- `src/components/AppShell.tsx`: thin app-owned composition root.
|
|
31
|
+
- `src/components/StarterWorkspace.tsx`: temporary starter screen to replace
|
|
32
|
+
when the first product surface exists.
|
|
33
|
+
- `src/lib/starterWorkspace.ts`: starter data/helper example showing where
|
|
34
|
+
reusable non-rendering logic belongs.
|
|
34
35
|
- `scripts/validate-puredesktop-app.mjs`: registration and tool scaffold check.
|
|
35
36
|
|
|
36
37
|
Use `npm run puredesktop:check` after `npm run build` before registration.
|