@puredesktop/create-app 2.1.9 → 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.
@@ -0,0 +1,440 @@
1
+ # Using The Document Editor
2
+
3
+ Use the platform document editor when the app needs a rich writing surface:
4
+ notes, long-form documents, reports, books, review workflows, markdown-backed
5
+ content, or any product surface where formatting matters.
6
+
7
+ The editor is controlled by the app. The platform provides the TipTap editor,
8
+ toolbar, comments, slash commands, document features, and conversion helpers.
9
+ The bridge provides the shell persistence mechanisms. Your app owns the typed
10
+ editor surface or session code that decides what to load, when to save, which
11
+ bridge helper to call, and how persisted data maps back into product state.
12
+
13
+ The editor does not persist content by itself.
14
+
15
+ ## Imports
16
+
17
+ Generated apps import the editor from the UI bridge package:
18
+
19
+ ```tsx
20
+ import {
21
+ DocumentEditor,
22
+ type DocumentEditorHandle,
23
+ type EditorViewMode,
24
+ EditorViewModeToggle,
25
+ DocumentToolbarIcon,
26
+ ToolbarActionButton,
27
+ ToolbarActionDivider,
28
+ ToolbarActionStrip,
29
+ ToolbarInlineToolButton,
30
+ useEditorExtensions,
31
+ type SlashCommandItem,
32
+ } from '@puredesktop/puredesktop-ui-bridge/editor'
33
+ ```
34
+
35
+ ## Minimal Editor
36
+
37
+ ```tsx
38
+ import { useMemo, useRef, useState } from 'react'
39
+ import {
40
+ DocumentEditor,
41
+ type DocumentEditorHandle,
42
+ type SlashCommandItem,
43
+ useEditorExtensions,
44
+ } from '@puredesktop/puredesktop-ui-bridge/editor'
45
+
46
+ export function NotesEditor(): React.ReactElement {
47
+ const editorRef = useRef<DocumentEditorHandle>(null)
48
+ const [content, setContent] = useState('<h1>Untitled</h1><p></p>')
49
+ const extensions = useEditorExtensions({
50
+ placeholder: 'Start writing...',
51
+ features: {
52
+ comments: true,
53
+ autoReviewPrompts: true,
54
+ tables: true,
55
+ taskList: true,
56
+ math: true,
57
+ mermaid: true,
58
+ },
59
+ })
60
+
61
+ const slashCommands = useMemo<SlashCommandItem[]>(
62
+ () => [
63
+ {
64
+ id: 'insert-summary',
65
+ title: 'Summary block',
66
+ description: 'Add a heading and starter paragraph.',
67
+ section: 'App',
68
+ run: ({ editor, range }) => {
69
+ editor
70
+ .chain()
71
+ .focus()
72
+ .deleteRange(range)
73
+ .insertContent('<h2>Summary</h2><p></p>')
74
+ .run()
75
+ },
76
+ },
77
+ ],
78
+ [],
79
+ )
80
+
81
+ return (
82
+ <DocumentEditor
83
+ ref={editorRef}
84
+ value={content}
85
+ extensions={extensions}
86
+ inputFormat="html"
87
+ outputFormat="html"
88
+ enableComments
89
+ slashCommands={slashCommands}
90
+ onChange={setContent}
91
+ />
92
+ )
93
+ }
94
+ ```
95
+
96
+ `onChange` receives the serialized document in the selected output format. Save
97
+ that value through the app's existing document/page/session save path. Simple
98
+ apps can debounce and call their session update command from the editor surface.
99
+ Larger document apps can delegate to their document/session modules. Do not make
100
+ the editor own product persistence.
101
+
102
+ ## Capability Map
103
+
104
+ The editor package includes these capability groups:
105
+
106
+ | Capability | Exports | Use when |
107
+ | --- | --- | --- |
108
+ | Document surface | `DocumentEditor`, `DocumentEditorHandle`, `DocumentEditorProps` | Rendering and controlling a rich document editor. |
109
+ | Extension setup | `useEditorExtensions`, `buildExtensions`, `EditorFeatures` | Choosing document features for the product surface. |
110
+ | Content conversion | `resolveEditorContent`, `toHtml`, `toMd`, `EditorInputFormat` | Loading markdown or HTML and saving in the app's chosen format. |
111
+ | Toolbar chrome | `DocumentToolbarIcon`, `ToolbarActionStrip`, `ToolbarActionButton`, `ToolbarActionIconButton`, `ToolbarInlineToolButton`, `ToolbarActionDivider` | Adding app controls into the shared editor toolbar. |
112
+ | View mode | `EditorViewModeToggle`, `EditorViewMode` | Switching between full editor chrome and a quiet page view. |
113
+ | Slash commands | `SlashCommands`, `createDefaultSlashCommands`, `SlashCommandItem` | Adding app-specific insert/actions to the slash menu. |
114
+ | Comments and review marks | `CommentMark`, comment types/status helpers, `parseCommentReplies`, `serializeCommentReplies` | Anchored review comments and editorial annotations. |
115
+ | Auto review | `runAutoReview`, `buildAutoReviewPrompt`, `buildAutoReviewPromptBundle`, `collectAutoReviewParagraphs`, `collectScopedAutoReviewParagraphs`, `validateAutoReviewFindings` | App-provided review workflows that annotate document text. |
116
+ | Rich document nodes | `Figure`, `Footnote`, `IndexMarker`, `MermaidBlock`, `SmartTypography`, math insertion helpers | Product surfaces that need figures, notes, diagrams, smart typography, or editable math. |
117
+ | Assets | `CollectionImage`, `createCollectionImagePasteExtension`, `insertCollectionImage`, `insertCollectionImageFromBinaryResult` | Persisted pasted or selected images that need app-managed storage. |
118
+ | Diff view | `DocumentDiffView`, `buildAlignedLineDiff` | Showing text/document differences in app UI. |
119
+
120
+ ## Feature Flags
121
+
122
+ `useEditorExtensions` builds the shared extension set. Enable only the features
123
+ the product needs:
124
+
125
+ ```tsx
126
+ const extensions = useEditorExtensions({
127
+ placeholder: 'Write the report...',
128
+ features: {
129
+ tables: true,
130
+ taskList: true,
131
+ highlight: true,
132
+ math: true,
133
+ figures: true,
134
+ mermaid: true,
135
+ footnotes: true,
136
+ smartTypography: true,
137
+ comments: true,
138
+ autoReviewPrompts: true,
139
+ indexMarkers: false,
140
+ documentAssetIdentity: true,
141
+ },
142
+ })
143
+ ```
144
+
145
+ Comments require both `features.comments` and `enableComments` on
146
+ `DocumentEditor`.
147
+
148
+ ## Editor Handle
149
+
150
+ Use a ref for explicit editor actions:
151
+
152
+ ```tsx
153
+ const editorRef = useRef<DocumentEditorHandle>(null)
154
+
155
+ editorRef.current?.insertHtml('<p>Inserted from the app.</p>')
156
+ editorRef.current?.insertImage({
157
+ src: imageUrl,
158
+ alt: 'Sketch',
159
+ caption: 'Draft sketch',
160
+ })
161
+
162
+ const tiptapEditor = editorRef.current?.getEditor()
163
+ ```
164
+
165
+ The handle also supports opening, updating, applying, removing, and clearing
166
+ comment marks, plus `runAutoReview` when the app provides a review provider.
167
+
168
+ ## View Modes
169
+
170
+ Use `boxed` for a full editing surface with toolbar chrome. Use `page` for a
171
+ quiet reading or print-like surface.
172
+
173
+ ```tsx
174
+ const [viewMode, setViewMode] = useState<EditorViewMode>('boxed')
175
+
176
+ return (
177
+ <>
178
+ <EditorViewModeToggle value={viewMode} onChange={setViewMode} />
179
+ <DocumentEditor
180
+ value={content}
181
+ extensions={extensions}
182
+ viewMode={viewMode}
183
+ showToolbar={viewMode !== 'page'}
184
+ showSelectionComments={viewMode !== 'page'}
185
+ onChange={setContent}
186
+ />
187
+ </>
188
+ )
189
+ ```
190
+
191
+ ## Toolbar Slots
192
+
193
+ Keep product-specific controls outside the editor implementation by using the
194
+ toolbar slots:
195
+
196
+ ```tsx
197
+ <DocumentEditor
198
+ value={content}
199
+ extensions={extensions}
200
+ toolbarInlineTools={
201
+ <ToolbarActionStrip>
202
+ <ToolbarInlineToolButton
203
+ aria-label="Insert section"
204
+ title="Insert section"
205
+ onClick={() => editorRef.current?.insertHtml('<h2>New section</h2>')}
206
+ >
207
+ <DocumentToolbarIcon name="insert-menu" />
208
+ </ToolbarInlineToolButton>
209
+ </ToolbarActionStrip>
210
+ }
211
+ toolbarActions={
212
+ <ToolbarActionStrip>
213
+ <ToolbarActionDivider />
214
+ <ToolbarActionButton onClick={saveDocument}>
215
+ Save
216
+ </ToolbarActionButton>
217
+ </ToolbarActionStrip>
218
+ }
219
+ onChange={setContent}
220
+ />
221
+ ```
222
+
223
+ Use platform toolbar components for editor chrome, and keep app-specific logic
224
+ in app hooks or `src/lib`.
225
+
226
+ ## Assets
227
+
228
+ There are two image paths.
229
+
230
+ Use `editorRef.current?.insertImage(...)` only when the app already has a stable
231
+ image URL:
232
+
233
+ ```tsx
234
+ editorRef.current?.insertImage({
235
+ src: imageUrl,
236
+ alt: 'Screenshot',
237
+ caption: 'Imported screenshot',
238
+ })
239
+ ```
240
+
241
+ Use collection image handling when the user can paste, drop, or choose image
242
+ files and the app must keep those files with the document. In that flow:
243
+
244
+ 1. The editor detects pasted/dropped image bytes.
245
+ 2. The app writes those bytes through bridge filesystem helpers.
246
+ 3. The app inserts an image node into the editor.
247
+ 4. The document stores a relative asset path so it can be reopened later.
248
+
249
+ Wire it like this:
250
+
251
+ ```tsx
252
+ import { useMemo, useRef } from 'react'
253
+ import {
254
+ CollectionImage,
255
+ createCollectionImagePasteExtension,
256
+ DocumentEditor,
257
+ type DocumentEditorHandle,
258
+ useEditorExtensions,
259
+ } from '@puredesktop/puredesktop-ui-bridge/editor'
260
+ import { createCollectionImagePasteHandler } from '@puredesktop/puredesktop-ui-bridge/bridge/collectionImagePaste'
261
+ import { DEFAULT_COLLECTION_FIGURES_DIR } from '@puredesktop/puredesktop-ui-bridge/bridge/collectionAssets'
262
+ import {
263
+ readPlatformFileBinary,
264
+ writePlatformFileBinary,
265
+ } from '../bridge/platformBridge'
266
+
267
+ function base64ToBytes(base64: string): Uint8Array {
268
+ const binary = atob(base64)
269
+ return Uint8Array.from(binary, char => char.charCodeAt(0))
270
+ }
271
+
272
+ function bytesToBase64(bytes: Uint8Array): string {
273
+ let binary = ''
274
+ const chunk = 0x8000
275
+ for (let index = 0; index < bytes.length; index += chunk) {
276
+ binary += String.fromCharCode(...bytes.subarray(index, index + chunk))
277
+ }
278
+ return btoa(binary)
279
+ }
280
+
281
+ async function readImageBytes(absolutePath: string): Promise<Uint8Array> {
282
+ const result = await readPlatformFileBinary(absolutePath)
283
+ return base64ToBytes(result.base64)
284
+ }
285
+
286
+ async function writeImageBytes(
287
+ absolutePath: string,
288
+ bytes: Uint8Array,
289
+ ): Promise<void> {
290
+ await writePlatformFileBinary(absolutePath, bytesToBase64(bytes))
291
+ }
292
+
293
+ export function DocumentWithImages({
294
+ documentFolder,
295
+ content,
296
+ onChange,
297
+ }: {
298
+ documentFolder: string
299
+ content: string
300
+ onChange: (content: string) => void
301
+ }): React.ReactElement {
302
+ const editorRef = useRef<DocumentEditorHandle>(null)
303
+ const baseExtensions = useEditorExtensions({
304
+ placeholder: 'Start writing...',
305
+ features: {
306
+ figures: true,
307
+ },
308
+ })
309
+
310
+ const handlePastedImage = useMemo(
311
+ () =>
312
+ createCollectionImagePasteHandler({
313
+ collectionPath: documentFolder,
314
+ subdir: DEFAULT_COLLECTION_FIGURES_DIR,
315
+ getEditor: () => editorRef.current?.getEditor(),
316
+ writeBinary: writeImageBytes,
317
+ }),
318
+ [documentFolder],
319
+ )
320
+
321
+ const extensions = useMemo(() => {
322
+ const withoutStockImage = baseExtensions.filter(
323
+ extension => extension.name !== 'image',
324
+ )
325
+ return [
326
+ ...withoutStockImage,
327
+ CollectionImage,
328
+ createCollectionImagePasteExtension({
329
+ handleImage: handlePastedImage,
330
+ readBinary: readImageBytes,
331
+ }),
332
+ ]
333
+ }, [baseExtensions, handlePastedImage])
334
+
335
+ return (
336
+ <DocumentEditor
337
+ ref={editorRef}
338
+ value={content}
339
+ extensions={extensions}
340
+ onChange={onChange}
341
+ />
342
+ )
343
+ }
344
+ ```
345
+
346
+ `CollectionImage` replaces the stock image node because collection images need
347
+ two things at once: a data URL for immediate display in the editor and a
348
+ relative asset path for saved document HTML.
349
+
350
+ `createCollectionImagePasteExtension` is the TipTap paste/drop hook. It does not
351
+ save anything by itself. It reads image bytes and passes them to `handleImage`.
352
+
353
+ `createCollectionImagePasteHandler` is the bridge-aware handler. It builds a
354
+ relative path under the document folder, writes the bytes with `writeBinary`,
355
+ then inserts the image into the current editor.
356
+
357
+ Keep the byte/base64 adapter functions at the app bridge boundary if multiple
358
+ components need images. Component code should call domain-named helpers, not
359
+ repeat raw bridge conversion details.
360
+
361
+ Use `insertCollectionImageFromBinaryResult` when the app already loaded an
362
+ asset as `{ mimeType, base64 }` and needs to insert it into the editor, for
363
+ example from an asset picker.
364
+
365
+ ## Auto Review
366
+
367
+ Auto review means: collect paragraphs from the current document, send those
368
+ paragraphs to an app-provided review model/tool, validate the returned findings
369
+ against the original paragraph text, then apply accepted findings as editor
370
+ comments.
371
+
372
+ The editor owns paragraph collection, finding validation, and comment
373
+ application. The app owns the review provider because the app decides which
374
+ agent, model, network call, prompt, or bridge helper performs the review.
375
+
376
+ Use auto review for document products that need editorial annotations, for
377
+ example copy editing, fact checking, source review, logic review, claim review,
378
+ or voice review. Do not use it for normal spellcheck or for apps that only need
379
+ a plain text box.
380
+
381
+ The provider contract is:
382
+
383
+ ```tsx
384
+ import {
385
+ buildAutoReviewPrompt,
386
+ parseAutoReviewFindingResponse,
387
+ type AutoReviewFindingsProvider,
388
+ } from '@puredesktop/puredesktop-ui-bridge/editor'
389
+
390
+ const findingsProvider: AutoReviewFindingsProvider = async ({
391
+ paragraphs,
392
+ options,
393
+ }) => {
394
+ const prompt = buildAutoReviewPrompt('copyedit', paragraphs)
395
+
396
+ // Call the app's chosen review path here:
397
+ // - an app-scoped shell agent session;
398
+ // - a permitted network endpoint;
399
+ // - an app-owned bridge helper.
400
+ const modelResponse = await runDocumentReview(prompt, options)
401
+
402
+ return parseAutoReviewFindingResponse(modelResponse)
403
+ }
404
+ ```
405
+
406
+ `modelResponse` must be JSON that describes findings. Each finding points at an
407
+ exact span inside one collected paragraph:
408
+
409
+ ```json
410
+ {
411
+ "findings": [
412
+ {
413
+ "paragraph_id": "p-1",
414
+ "exact_text": "the exact words in the paragraph",
415
+ "annotation_type": "copyedit",
416
+ "suggested_note": "Short note shown to the user.",
417
+ "severity": "minor",
418
+ "confidence": 0.8
419
+ }
420
+ ]
421
+ }
422
+ ```
423
+
424
+ Then run the review from the editor handle:
425
+
426
+ ```tsx
427
+ const result = await editorRef.current?.runAutoReview({
428
+ process: 'copyedit',
429
+ enabledTypes: ['copyedit'],
430
+ findingsProvider,
431
+ maxFindings: 12,
432
+ minConfidence: 0.55,
433
+ })
434
+ ```
435
+
436
+ If `findingsProvider` is missing, `runAutoReview` returns an
437
+ `unavailableReason` and does not annotate the document. If a finding's
438
+ `exact_text` cannot be found in its paragraph, the editor skips it. The model
439
+ does not edit the document directly; it only returns findings that the editor
440
+ validates and applies as comments.
@@ -10,7 +10,7 @@ agent tools.
10
10
  "schemaVersion": 1,
11
11
  "id": "{{PLUGIN_ID}}",
12
12
  "name": "{{APP_TITLE}}",
13
- "permissions": ["network", "settings", "filesystem", "agents"],
13
+ "permissions": ["network", "settings", "filesystem", "agents"],
14
14
  "entrypoint": {
15
15
  "kind": "dev-url",
16
16
  "url": "http://localhost:{{APP_PORT}}"
@@ -1,84 +1,89 @@
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
-
52
- Import shared UI by exact exported package paths before creating custom
53
- primitives. Use existing app folders before creating new ones.
54
-
55
- Useful starting points:
56
-
57
- - `AppFrame` from `@puredesktop/puredesktop-ui-bridge/components/common/containers/AppFrame`
58
- - `EmptyState` from `@puredesktop/puredesktop-ui-bridge/components/common/feedback/EmptyState`
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
+
52
+ Import shared UI by exact exported package paths before creating custom
53
+ primitives. Use existing app folders before creating new ones.
54
+
55
+ Useful starting points:
56
+
57
+ - `AppFrame` from `@puredesktop/puredesktop-ui-bridge/components/common/containers/AppFrame`
58
+ - `EmptyState` from `@puredesktop/puredesktop-ui-bridge/components/common/feedback/EmptyState`
59
59
  - `Heading` from `@puredesktop/puredesktop-ui-bridge/components/common/typography/Heading`
60
60
  - `Text` from `@puredesktop/puredesktop-ui-bridge/components/common/typography/Text`
61
61
  - `Button` from `@puredesktop/puredesktop-ui-bridge/components/common/buttons/Button`
62
+ - `DocumentEditor` from `@puredesktop/puredesktop-ui-bridge/editor`
62
63
 
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`.
64
+ For rich writing surfaces, review workflows, markdown/HTML documents, and
65
+ document-specific toolbar controls, see
66
+ [Using the document editor](./howtos/using-document-editor.md).
79
67
 
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.
68
+ ## Styling
69
+
70
+ Use styled-components and platform CSS variables:
71
+
72
+ ```ts
73
+ const Root = styled.div`
74
+ display: flex;
75
+ flex-direction: column;
76
+ gap: var(--platform-spacing-md);
77
+ color: var(--platform-colors-text);
78
+ background: var(--platform-colors-bg);
79
+ `
80
+ ```
81
+
82
+ Use app-local CSS variables only for app-specific meaning, for example
83
+ `--{{APP_SLUG}}-accent`.
84
+
85
+ See [Theme CSS variables](./theme-vars.md) for the generated list of available
86
+ platform variables by domain.
87
+
88
+ Avoid inline styles except for tiny dynamic values that cannot be represented as
89
+ props on a styled component.
@@ -1,5 +1,10 @@
1
- .gitignore
2
1
  node_modules
2
+ .project-data
3
+ .swc
3
4
  dist
5
+ build
4
6
  .env
5
7
  .npmrc
8
+ package-lock.json
9
+ pnpm-lock.yaml
10
+ yarn.lock
@@ -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.8",
13
+ "@puredesktop/puredesktop-ui-bridge": "2.1.9",
14
14
  "react": "^19.1.0",
15
15
  "react-dom": "^19.1.0",
16
16
  "styled-components": "^6.1.18"
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "id": "{{PLUGIN_ID}}",
4
4
  "name": "{{APP_TITLE}}",
5
- "permissions": ["network", "settings", "filesystem", "agents"],
5
+ "permissions": ["network", "settings", "filesystem", "agents"],
6
6
  "entrypoint": {
7
7
  "kind": "dev-url",
8
8
  "url": "http://localhost:{{APP_PORT}}"