pith-ui 0.1.1 → 0.2.0-app.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/AI.md +33 -8
- package/API.md +424 -268
- package/APP-API.md +2019 -0
- package/APP.md +137 -0
- package/README.md +37 -4
- package/dist/app/files.d.ts +68 -0
- package/dist/app/files.d.ts.map +1 -0
- package/dist/app/files.js +177 -0
- package/dist/app/files.js.map +1 -0
- package/dist/app/index.d.ts +57 -0
- package/dist/app/index.d.ts.map +1 -0
- package/dist/app/index.js +300 -0
- package/dist/app/index.js.map +1 -0
- package/dist/app/internal.d.ts +31 -0
- package/dist/app/internal.d.ts.map +1 -0
- package/dist/app/internal.js +2 -0
- package/dist/app/internal.js.map +1 -0
- package/dist/app/resources.d.ts +89 -0
- package/dist/app/resources.d.ts.map +1 -0
- package/dist/app/resources.js +190 -0
- package/dist/app/resources.js.map +1 -0
- package/dist/app/session.d.ts +14 -0
- package/dist/app/session.d.ts.map +1 -0
- package/dist/app/session.js +12 -0
- package/dist/app/session.js.map +1 -0
- package/dist/app/settings.d.ts +109 -0
- package/dist/app/settings.d.ts.map +1 -0
- package/dist/app/settings.js +279 -0
- package/dist/app/settings.js.map +1 -0
- package/dist/app/workspace.d.ts +228 -0
- package/dist/app/workspace.d.ts.map +1 -0
- package/dist/app/workspace.js +402 -0
- package/dist/app/workspace.js.map +1 -0
- package/dist/composer.d.ts.map +1 -1
- package/dist/composer.js +37 -27
- package/dist/composer.js.map +1 -1
- package/dist/content/conversation-reader.d.ts.map +1 -1
- package/dist/content/conversation-reader.js +8 -1
- package/dist/content/conversation-reader.js.map +1 -1
- package/dist/content/conversation-view.d.ts.map +1 -1
- package/dist/content/conversation-view.js +34 -8
- package/dist/content/conversation-view.js.map +1 -1
- package/dist/content/image-viewer.d.ts.map +1 -1
- package/dist/content/image-viewer.js +11 -3
- package/dist/content/image-viewer.js.map +1 -1
- package/dist/content/paged-preview.d.ts +1 -1
- package/dist/content/paged-preview.d.ts.map +1 -1
- package/dist/content/paged-preview.js +4 -2
- package/dist/content/paged-preview.js.map +1 -1
- package/dist/content/views.d.ts +3 -0
- package/dist/content/views.d.ts.map +1 -1
- package/dist/content/views.js +110 -41
- package/dist/content/views.js.map +1 -1
- package/dist/content/welcome.d.ts.map +1 -1
- package/dist/content/welcome.js +5 -2
- package/dist/content/welcome.js.map +1 -1
- package/dist/dom.js +1 -1
- package/dist/dom.js.map +1 -1
- package/dist/files/panels.d.ts +2 -0
- package/dist/files/panels.d.ts.map +1 -1
- package/dist/files/panels.js +202 -128
- package/dist/files/panels.js.map +1 -1
- package/dist/files/systems.d.ts.map +1 -1
- package/dist/files/systems.js +37 -64
- package/dist/files/systems.js.map +1 -1
- package/dist/forms/actions.d.ts +24 -2
- package/dist/forms/actions.d.ts.map +1 -1
- package/dist/forms/actions.js +54 -25
- package/dist/forms/actions.js.map +1 -1
- package/dist/forms/custom-connection.d.ts.map +1 -1
- package/dist/forms/custom-connection.js +3 -2
- package/dist/forms/custom-connection.js.map +1 -1
- package/dist/forms/history.d.ts +1 -1
- package/dist/forms/history.d.ts.map +1 -1
- package/dist/forms/history.js +78 -15
- package/dist/forms/history.js.map +1 -1
- package/dist/forms/identity.d.ts.map +1 -1
- package/dist/forms/identity.js +9 -25
- package/dist/forms/identity.js.map +1 -1
- package/dist/forms/model-editor.js +3 -3
- package/dist/forms/model-editor.js.map +1 -1
- package/dist/forms/panels.d.ts +9 -2
- package/dist/forms/panels.d.ts.map +1 -1
- package/dist/forms/panels.js +79 -88
- package/dist/forms/panels.js.map +1 -1
- package/dist/forms/provider-login.d.ts.map +1 -1
- package/dist/forms/provider-login.js +20 -36
- package/dist/forms/provider-login.js.map +1 -1
- package/dist/forms/resource-editor.js +5 -4
- package/dist/forms/resource-editor.js.map +1 -1
- package/dist/foundations.d.ts +1 -1
- package/dist/foundations.d.ts.map +1 -1
- package/dist/foundations.js +22 -38
- package/dist/foundations.js.map +1 -1
- package/dist/navigation/context-menu.d.ts +7 -1
- package/dist/navigation/context-menu.d.ts.map +1 -1
- package/dist/navigation/context-menu.js +60 -57
- package/dist/navigation/context-menu.js.map +1 -1
- package/dist/navigation/navigation.d.ts +13 -0
- package/dist/navigation/navigation.d.ts.map +1 -1
- package/dist/navigation/navigation.js +96 -130
- package/dist/navigation/navigation.js.map +1 -1
- package/dist/operation.d.ts +12 -0
- package/dist/operation.d.ts.map +1 -0
- package/dist/operation.js +49 -0
- package/dist/operation.js.map +1 -0
- package/dist/primitives.d.ts +8 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +114 -143
- package/dist/primitives.js.map +1 -1
- package/dist/reconcile.d.ts +11 -0
- package/dist/reconcile.d.ts.map +1 -0
- package/dist/reconcile.js +36 -0
- package/dist/reconcile.js.map +1 -0
- package/dist/select.d.ts +14 -0
- package/dist/select.d.ts.map +1 -0
- package/dist/select.js +304 -0
- package/dist/select.js.map +1 -0
- package/dist/styles.css +439 -370
- package/dist/surface.d.ts +31 -0
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +193 -3
- package/dist/surface.js.map +1 -1
- package/dist/tasks/approval.d.ts.map +1 -1
- package/dist/tasks/approval.js +83 -45
- package/dist/tasks/approval.js.map +1 -1
- package/dist/tasks/permission-element.d.ts +3 -0
- package/dist/tasks/permission-element.d.ts.map +1 -0
- package/dist/tasks/permission-element.js +10 -0
- package/dist/tasks/permission-element.js.map +1 -0
- package/dist/tasks/permission.d.ts.map +1 -1
- package/dist/tasks/permission.js +37 -18
- package/dist/tasks/permission.js.map +1 -1
- package/dist/tasks/runtime.d.ts.map +1 -1
- package/dist/tasks/runtime.js +32 -15
- package/dist/tasks/runtime.js.map +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +93 -40
- package/dist/workspace.js.map +1 -1
- package/package.json +42 -8
package/APP.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Pith UI application consumption contract
|
|
2
|
+
|
|
3
|
+
This is the constrained application contract for Pith Desk and applications choosing the same boundary. It is shipped with the package whose API it describes. Read the installed [`dist/app/index.d.ts`](./dist/app/index.d.ts), the exact dependency and integrity in the consumer lockfile, and this document together. A repository `main` document or a newer showroom is not a substitute for the installed version's declarations.
|
|
4
|
+
|
|
5
|
+
The allowed UI imports are `pith-ui/app` and `pith-ui/styles.css`. The general `pith-ui` entry remains available for independent component compositions; Pith Desk must not use it, private package paths, copied components or internal CSS to bypass this contract. A missing application capability needs a tested semantic port in the library before the application consumes it.
|
|
6
|
+
|
|
7
|
+
<!-- PUI_APP_VERSION_START -->
|
|
8
|
+
This contract accompanies `pith-ui@0.2.0-app.1`. Exact API: [APP-API.md](./APP-API.md); installed declarations: `dist/app/index.d.ts`.
|
|
9
|
+
|
|
10
|
+
The full reference application source is versioned with this artifact: [TypeScript controller](https://github.com/minifish-org/pith-ui/blob/v0.2.0-app.1/examples/application/main.ts) and [HTML entry](https://github.com/minifish-org/pith-ui/blob/v0.2.0-app.1/examples/application/index.html). These sources are not bundled into the npm runtime package.
|
|
11
|
+
<!-- PUI_APP_VERSION_END -->
|
|
12
|
+
|
|
13
|
+
## One mount, four scenes
|
|
14
|
+
|
|
15
|
+
The only DOM argument is the checked application mount. `createApplication` builds and owns the interface beneath it. Obtain the mount once, validate it with `HTMLElement`, and pass it to the library. Do not query, retain, move, decorate or listen on the generated UI nodes.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import {createApplication, type ConversationMessage} from 'pith-ui/app';
|
|
19
|
+
import 'pith-ui/styles.css';
|
|
20
|
+
|
|
21
|
+
const mount = document.getElementById('app');
|
|
22
|
+
if (!(mount instanceof HTMLElement)) throw new Error('Missing application mount');
|
|
23
|
+
const messages: ConversationMessage[] = [
|
|
24
|
+
{id: 'intro', role: 'assistant', text: 'This example uses in-memory data.'},
|
|
25
|
+
];
|
|
26
|
+
const app = createApplication(mount, {
|
|
27
|
+
appearance: 'system',
|
|
28
|
+
brand: {name: 'Example', qualifier: 'Desk'},
|
|
29
|
+
workspace: {
|
|
30
|
+
onSubmit(text) {
|
|
31
|
+
messages.push({id: String(messages.length), role: 'user', text});
|
|
32
|
+
app.workspace.resetDraft('');
|
|
33
|
+
app.workspace.updateConversation({id: 'chat', messages, working: false});
|
|
34
|
+
return {ok: true};
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
app.workspace.updateComposer({action: 'send'});
|
|
39
|
+
app.workspace.updateConversation({id: 'chat', messages, working: false});
|
|
40
|
+
// The embedding owner calls app.destroy() when this application is released.
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The full, compiled reference controller and HTML entry linked in the version block exercise every scene with explicit simulated data. Their sources are versioned in the repository, outside the npm runtime package. The showroom item is [`application-reference`](https://minifish-org.github.io/pith-ui/?item=application-reference); it embeds that same application and links its full page. Use the showroom deployment corresponding to the tested package version. These are demonstrations, not evidence of publication or human acceptance.
|
|
44
|
+
|
|
45
|
+
`Application` exposes the four plain scene objects, `read()`, `setAppearance(appearance)`, `downloadFile({blob, name})` and `destroy()`. `read()` reports `{hasModal, appearance}` for business read gating. `onUIStateChange` observes that presentation state; it does not acknowledge a task or persist a read marker.
|
|
46
|
+
|
|
47
|
+
The application entry also exports the pure `formatCost` and `thinkingLabel` helpers. They format values without creating UI or granting access to the general component API.
|
|
48
|
+
|
|
49
|
+
| Scene | Complete feature ownership | Main methods |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `workspace` | Shell, sidebar, breadcrumb, workspace/conversation navigation, search and anchored menus; welcome; messages, tools/groups, commands, Markdown and code; reading anchors, literal find and follow-latest; composer, attachments, drop feedback, queue; permissions, full-access confirmation, approval/diff, runtime and failure; message/queue editing, confirmation, workspace selection and rename; branch history, cost presentation and completion notices | `updateShell`, `updateNavigation`, `updateWelcome`, `updateConversation`, `updateComposer`, `updatePending`, `updateAttachments`, `updatePermission`, `updateApproval`, `updateRuntime`, `updateTiming`, `updateFailure`, `updateCosts`, `updateNotices`; named dialog methods below |
|
|
52
|
+
| `settings` | Appearance controls, provider settings and credentials, connection test presentation, update status/actions; model/provider catalog and effort; independent connections with model definitions/capabilities/prices; MCP connection editor/list and OAuth/provider sign-in | `openSettings`, `openModels`, `openConnections`, `openCustomConnection`, `openConnectionLoading`, `openProviderLogin` |
|
|
53
|
+
| `resources` | Workspace instructions, skills, templates, diagnostics, refresh/create/use/edit/file actions; source editor, read-only state, save/delete and loading presentation | `open`, `openEditor`, `openEditorLoading` |
|
|
54
|
+
| `files` | Conversation artifact panel and actions; text, Markdown, diff and image preview; UTF-8 pagination, code-copy feedback, image zoom and focus restoration | `updateArtifacts`, `closeArtifacts`, `areArtifactsOpen`, `openPreview` |
|
|
55
|
+
|
|
56
|
+
Costs and branch history belong to `workspace`; update status belongs to `settings`. They are not file-center responsibilities merely because their generic implementation shares a source file with file components.
|
|
57
|
+
|
|
58
|
+
## Finite configuration and host capabilities
|
|
59
|
+
|
|
60
|
+
`ApplicationOptions` accepts `appearance`, `brand`, `host`, `workspace`, `onUIStateChange` and `onError`. Appearance is `light`, `dark` or `system`, also changed through `app.setAppearance`. No font, size, gap, density, class, arbitrary theme CSS, markup, Node slot or render callback is part of the application interface.
|
|
61
|
+
|
|
62
|
+
Branding is data: `{name, qualifier?, mark?: {src, darkSrc?, alt}}`. The application supplies approved static asset URLs and alternative text; optional `darkSrc` supplies the same brand's dark-appearance asset. The library selects the asset for light, dark or system appearance and places it in its owned brand treatment. Branding does not permit arbitrary SVG/HTML, a logo Node, a renderer or a second layout. With no brand option the name defaults to `Pith` and no logo is invented.
|
|
63
|
+
|
|
64
|
+
The two optional `host` callbacks are `copyText(text)` and `openExternal(url)`. They receive values, never controls or events. They may return `void`, `ActionResult`, or a promise of either. Omitted adapters use the browser clipboard and browser external-link mechanism. Native applications supply their existing capability adapters. The library presents pending/results; the adapter performs the actual capability and reports cancellation or failure according to application policy.
|
|
65
|
+
|
|
66
|
+
`app.downloadFile({blob, name})` owns the browser download affordance. The controller still acquires and validates the bytes, resolves ownership/path policy, and chooses native download behavior where applicable. It does not cause the library to fetch a file or authenticate a request.
|
|
67
|
+
|
|
68
|
+
## Scene ports and sessions
|
|
69
|
+
|
|
70
|
+
These are named methods, not a UI description language. Pass the concrete exported types rather than `Record<string, unknown>`, `any`, a structural cast to a low-level block, or an invented option. All scenes have `destroy()`; normally the root application owner calls `app.destroy()` once.
|
|
71
|
+
|
|
72
|
+
`workspace` also provides:
|
|
73
|
+
|
|
74
|
+
- Draft and input intent: `readDraft`, `resetDraft`, `focusComposer`, `pickImages`, `insertReferences`, `receiveNativeDrop` and `setDropPending`. Input/reset owns resizing; there is no application resize operation.
|
|
75
|
+
- Reading and navigation intent: `search`, `closeNavigationMenu`, `setSidebarOpen`, `followLatest`, `openFind`, `closeFind` and `pruneConversations`.
|
|
76
|
+
- Availability and system projection: `updateMessageAvailability`, `updateAvailability`, `updateImageGuidance`, `readCosts` and `resetCosts`.
|
|
77
|
+
- Owned openings: `openMessageEditor`, `openQueueEditor`, `openConfirmation`, `openWorkspacePicker`, `openRename`, `openFullAccess` and `openHistory`.
|
|
78
|
+
|
|
79
|
+
Navigation state supplies workspace/conversation identity, selection, status and allowed actions. Disclosure state and menu anchoring stay in the library; do not drive an `expanded` field or retain the menu's trigger. `setSidebarOpen` expresses an application intent, not a node-access escape hatch.
|
|
80
|
+
|
|
81
|
+
Every opening returns a plain session with `update(state)`, `fail(message)`, `isOpen()`, `close()` and `destroy()`. Only declared additions exist:
|
|
82
|
+
|
|
83
|
+
| Session | Additional semantic methods |
|
|
84
|
+
|---|---|
|
|
85
|
+
| Message/queue editor | `read(): string`, `reset(value)`, `focus()` |
|
|
86
|
+
| Workspace picker | `read(): {path}`, `reset(path?)`, `focus()` |
|
|
87
|
+
| Conversation rename | `read(): RenameConversationDraft`, `reset(state)`, `focus()` |
|
|
88
|
+
| Settings | `read`, `setProviders`, `resetProvider`, `setCredential`, `updateUpdates`, `setUpdatePending`, `setUpdateError`, `clearSecrets` |
|
|
89
|
+
| MCP connections | `read`, `edit(value?)`, `setConnections`, `clearBearerToken`, `clearSecrets` |
|
|
90
|
+
| Model picker | `read`, `reset(selection)`, `focus()` |
|
|
91
|
+
| Independent connection | `read`, `clearSecrets` |
|
|
92
|
+
| Resource inventory/editor | `read` with the declared state/draft result |
|
|
93
|
+
| Loading, confirmation, permission, login, history and file preview | The common session methods only |
|
|
94
|
+
|
|
95
|
+
`settings.openConnectionLoading({editing, error?})` and `resources.openEditorLoading({kind, path?, error?})` represent existing asynchronous load phases. Their subsequent `update` accepts `LoadingState` (`{error?}`); it is not a general status/rendering slot. The controller opens the corresponding editor after successful target validation. Independent-connection, resource-editor and branch-history openings share the SDK feature surface: replacing one invalidates its previous session. File preview has its own surface and can coexist with that feature surface.
|
|
96
|
+
|
|
97
|
+
`SettingsSession.setCredential` accepts credential presentation and optional typed `providerActions`, not an action Node. For update-controller reads, use `updateUpdates` for authoritative status, `setUpdatePending` for external in-flight state, and `setUpdateError(message, {allowCheckRetry: true})` for a failed read that may be checked manually. These operations do not start polling, download updates or authorize installation.
|
|
98
|
+
|
|
99
|
+
Resource action targets contain `{workspaceId, kind, name, path}`. File targets contain `{owner: {kind: 'artifact', id}, name, path}`. `FilePreviewInput` adds finite `capabilities` (`open`, `reveal`, `copyMode`) and optional content; preview `update` accepts content/feedback, not a different target. Use a fresh opening when target identity changes. `updateArtifacts` accepts its pure state and optional semantic callbacks; no file-action Node is supplied.
|
|
100
|
+
|
|
101
|
+
## UI state and business state
|
|
102
|
+
|
|
103
|
+
| Library owns | Application controller owns |
|
|
104
|
+
|---|---|
|
|
105
|
+
| Structure, fixed shared presentation, native controls and accessibility wiring | Request protocols, URLs, endpoint contracts, schema conversion and response parsing |
|
|
106
|
+
| Focus, menus, local disclosures, navigation expansion, image zoom and reading anchors | Workspace/conversation/task identity, target validation and authorization |
|
|
107
|
+
| Live editor controls, selection/composition, local submit pending and error placement | Versioned drafts, persistence, request generations, transactions and cancellation |
|
|
108
|
+
| Sanitized Markdown, code/diff presentation, pagination and copy feedback | Resource/file acquisition, write/delete policy, native open/reveal/copy/download |
|
|
109
|
+
| Credential field presentation and cleanup of owned secrets on close | Credential storage, login transaction/PKCE, account state and token lifetime |
|
|
110
|
+
| Runtime/cost/update/notification display | Runtime requests, cost calculation, update polling, completion/read acknowledgments |
|
|
111
|
+
|
|
112
|
+
Callbacks return semantic targets and values. `File[]` attachment inputs and `Blob` image/download bytes are capability data, not Node slots. `onLoadImage({conversationId, messageId, index})` loads validated bytes in the controller; the workspace scene owns the resulting presentation object URLs and releases them when its image scope changes or is destroyed. An `imageSrc` URL supplied for preview or attachments remains host-owned and must remain valid while used, then be revoked by that owner.
|
|
113
|
+
|
|
114
|
+
`resolveLocalLink` only resolves a display link to a host-owned file target; `onLocalFile` must validate it again before acquisition or a native action. A rendered action is not permission. Preserve existing per-message Copy/Edit/Branch eligibility and exact target rules; do not infer authorization from a role or grant a whole-turn copy action.
|
|
115
|
+
|
|
116
|
+
## Drafts, writes and lifecycle
|
|
117
|
+
|
|
118
|
+
- Create an application once. Update existing scene projections with stable keys. Do not recreate the shell or replace messages on each token merely to refresh presentation.
|
|
119
|
+
- Initial values are applied at opening or by a declared explicit reset/edit operation. Ordinary `update` feedback must preserve input, IME composition, selection, focus, scroll and owned disclosures. Keep versioned business drafts in the controller.
|
|
120
|
+
- Submit callbacks whose declaration returns `ActionResult` return `{ok: true}` or `{ok: false, error}` (or their promise). A failed save retains the draft; local pending combines with external disabled state. Void notifications, such as queue/approval intents, still require the controller to project authoritative availability and guard business operations.
|
|
121
|
+
- Validate submitted identity, version and permission immediately before a write. A successful write followed by a failed refresh remains a successful write; present the refresh failure separately and do not retry the write automatically.
|
|
122
|
+
- An opening's session addresses that opening only. After close, replacement or destruction, its old updates, failures, close calls and asynchronous results cannot mutate a later opening. Keep controller request generations/target checks as well: UI invalidation does not abort a request, undo a completed write, or make its data relevant to a new target.
|
|
123
|
+
- Closing settings, connection and login presentations clears their owned secrets. Do not place real credentials in source examples, URLs, logs, screenshots or showroom state. The controller retains the distinction between an empty field, keeping an existing credential, and an explicit removal.
|
|
124
|
+
- Call `destroy` for the released owner. It releases UI listeners, observers, frames, temporary feedback, object URLs and owned child views; it does not start business cleanup requests or cancel a completed operation. Repeating close/destroy is harmless.
|
|
125
|
+
|
|
126
|
+
The application must not add event delegation, selectors, dataset routing, form/field access, HTML templates or CSS to fill a scene gap. It must not inspect `element`, `part`, `field`, `control`, `body`, `button`, `style` or similar internal handles. Business objects may naturally contain fields such as `id`, `path`, `content` or `name`; those are not UI nodes.
|
|
127
|
+
|
|
128
|
+
## Migration and validation
|
|
129
|
+
|
|
130
|
+
1. Freeze the accepted behavior and visual baseline; inventory every existing feature and its controller/scene owner.
|
|
131
|
+
2. Add a permanent regression for each concrete contract gap before changing the library. Reuse the existing component implementation internally and extend only the finite semantic capability that is missing.
|
|
132
|
+
3. Build, type-check and test the library, its compiled reference application and the matching showroom. Validate the actual package exports, declaration graph, CSS, examples and production consumption.
|
|
133
|
+
4. Publish and verify the approved registry version or prerelease. Install that exact registry version in Pith Desk and commit its lockfile. Do not use a local tarball, `file:`, `link:`, workspace alias, source-path mapping, symlink or vendored library as the application's dependency.
|
|
134
|
+
5. Replace view construction with scene projections and callbacks while preserving controllers and business assertions. Remove the replaced templates/styles so there is one visual implementation. Enforce the approved import/mount/semantic boundary with the consumer's source gate.
|
|
135
|
+
6. Run behavior, keyboard/focus, late-result, draft, credential, streaming, copy, image, file and native-host checks; compare both themes, supported window sizes and resource budgets. Record observations separately from publication, deployment and human acceptance.
|
|
136
|
+
|
|
137
|
+
The reference application's in-memory simulator is an example controller, not a business implementation to move into the library. Its Node syntax/strict compilation tests and Chromium/WebKit interaction tests verify the example; they do not establish Pith Desk parity, a resource-budget result, native acceptance, an npm publication or a deployed showroom.
|
package/README.md
CHANGED
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Native TypeScript UI building blocks for AI-assisted application development. Choose a complete block, provide typed content and callbacks, and reuse its reviewed layout and interaction rules.
|
|
4
4
|
|
|
5
|
+
Pith Desk's approved consumption boundary is the constrained **`pith-ui/app`** entry: one checked mount and four semantic scenes, with no generated-node, template or styling access. Read the bundled [application consumption contract](./APP.md) first. The general **`pith-ui`** entry remains available for independent component compositions; it is not a fallback around the application's boundary.
|
|
6
|
+
|
|
5
7
|
The source is TypeScript and CSS. The package contains compiled ESM, declarations and a separate stylesheet. It uses browser DOM APIs, native controls and system fonts; no UI framework or animation runtime is required.
|
|
6
8
|
|
|
9
|
+
Presentation is fixed rather than assembled from style choices: system UI and monospace code fonts, small/medium/large text at 12/14/16 CSS px, regular/strong weights at 400/600, a shared 1.5 line height, and 4/8/12/16/24 CSS px spacing. Blocks choose the appropriate role automatically. Applications supply their own branding.
|
|
10
|
+
|
|
7
11
|
## Try the components
|
|
8
12
|
|
|
9
13
|
The [interactive showroom](https://minifish-org.github.io/pith-ui/) groups **design foundations → base components → product blocks → composed pages**. It exposes real controls, theme and state comparisons, CSS-pixel measurements, spacing rulers and motion playback. Showroom data and actions are simulated and labelled accordingly. Automated checks do not substitute for human acceptance.
|
|
10
14
|
|
|
15
|
+
The [`application-reference`](https://minifish-org.github.io/pith-ui/?item=application-reference) page embeds the [complete TypeScript reference application](./examples/application/main.ts): chat and simulated streaming, queues, approval, model/settings/login/MCP, resources, files and branch history. Use the deployment matching the package under review; source examples and a public site do not establish that a version has been published or manually accepted.
|
|
16
|
+
|
|
11
17
|
Run it locally with Node 22.18 or newer:
|
|
12
18
|
|
|
13
19
|
```sh
|
|
@@ -17,10 +23,37 @@ npm run dev
|
|
|
17
23
|
|
|
18
24
|
## Use the package
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
Choose an approved, verified registry version that exports `./app`, install it with `--save-exact`, and commit the consumer lockfile. The declarations and documents in that installed artifact define the contract. A consumer migration must not depend on a local tarball, `file:`/`link:`, source alias or symlink. See [installation and migration](./APP.md#migration-and-validation).
|
|
27
|
+
|
|
28
|
+
The application entry owns its DOM and presentation:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import {createApplication, type ConversationMessage} from 'pith-ui/app';
|
|
32
|
+
import 'pith-ui/styles.css';
|
|
33
|
+
|
|
34
|
+
const mount = document.getElementById('app');
|
|
35
|
+
if (!(mount instanceof HTMLElement)) throw new Error('Missing application mount');
|
|
36
|
+
const messages: ConversationMessage[] = [
|
|
37
|
+
{id: 'intro', role: 'assistant', text: 'This example uses in-memory data.'},
|
|
38
|
+
];
|
|
39
|
+
const app = createApplication(mount, {
|
|
40
|
+
brand: {name: 'Example', qualifier: 'Desk'},
|
|
41
|
+
workspace: {onSubmit(text) {
|
|
42
|
+
messages.push({id: String(messages.length), role: 'user', text});
|
|
43
|
+
app.workspace.resetDraft('');
|
|
44
|
+
app.workspace.updateConversation({id: 'chat', messages, working: false});
|
|
45
|
+
return {ok: true};
|
|
46
|
+
}},
|
|
47
|
+
});
|
|
48
|
+
app.workspace.updateComposer({action: 'send'});
|
|
49
|
+
app.workspace.updateConversation({id: 'chat', messages, working: false});
|
|
50
|
+
// The embedding owner calls app.destroy() when released.
|
|
22
51
|
```
|
|
23
52
|
|
|
53
|
+
The four scenes are `workspace`, `settings`, `resources` and `files`. Controllers retain requests, identities, drafts, policy, credentials, transactions and native capabilities. The [application contract](./APP.md) maps every feature and names each session's methods; [`dist/app/index.d.ts`](./dist/app/index.d.ts) in the installed package is authoritative.
|
|
54
|
+
|
|
55
|
+
For a general component consumer outside that application boundary, the root entry remains available:
|
|
56
|
+
|
|
24
57
|
```ts
|
|
25
58
|
import { createTextEditDialog, setAppearance } from 'pith-ui';
|
|
26
59
|
import 'pith-ui/styles.css';
|
|
@@ -52,7 +85,7 @@ editor.open({ value: 'Original message' });
|
|
|
52
85
|
| Sanitized Markdown, code/diff/file/image presentation | File acquisition, native actions, object-URL ownership |
|
|
53
86
|
| Interactive examples and component regression tests | End-to-end product policy and release validation |
|
|
54
87
|
|
|
55
|
-
The first consumer is Pith Desk. Its existing controllers
|
|
88
|
+
The first consumer is Pith Desk. Its existing controllers retain their request, draft, approval and recovery semantics. Its UI integration must use the application scenes instead of reconstructing shared views through lower-level factories.
|
|
56
89
|
|
|
57
90
|
## Find a block
|
|
58
91
|
|
|
@@ -61,7 +94,7 @@ The first consumer is Pith Desk. Its existing controllers keep their request, dr
|
|
|
61
94
|
- **Product blocks:** navigation, conversation reader, composer/queue, models, tasks/approval, settings/connections, OAuth/MCP, resources, files, branch history and system information.
|
|
62
95
|
- **Pages:** working compositions of those blocks, rather than separate copies of their implementations.
|
|
63
96
|
|
|
64
|
-
Read the bundled [
|
|
97
|
+
Read the bundled [application contract](./APP.md), [general component API reference](./API.md), [complete catalog](https://github.com/minifish-org/pith-ui/blob/main/docs/catalog.md), [integration guide](https://github.com/minifish-org/pith-ui/blob/main/docs/guide.md), [AI usage instructions](./AI.md), [validation guide](https://github.com/minifish-org/pith-ui/blob/main/docs/validation.md) and [release procedure](https://github.com/minifish-org/pith-ui/blob/main/docs/releasing.md). Match repository documentation and the showroom to the installed version when reviewing a release.
|
|
65
98
|
|
|
66
99
|
## Develop and validate
|
|
67
100
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ActionResult } from '../core.js';
|
|
2
|
+
import type { AppEnvironment } from './internal.js';
|
|
3
|
+
export type AppFileAction = 'open' | 'reveal' | 'copy' | 'copy-path';
|
|
4
|
+
export type FileActionResult = void | ActionResult | Promise<void | ActionResult>;
|
|
5
|
+
export interface AppFileOwner {
|
|
6
|
+
readonly kind: 'artifact';
|
|
7
|
+
readonly id: string;
|
|
8
|
+
}
|
|
9
|
+
export interface AppFileTarget {
|
|
10
|
+
readonly owner: AppFileOwner;
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly path: string;
|
|
13
|
+
}
|
|
14
|
+
export interface FileCapabilities {
|
|
15
|
+
readonly open?: boolean;
|
|
16
|
+
readonly reveal?: boolean;
|
|
17
|
+
readonly copyMode?: 'file' | 'path' | 'none';
|
|
18
|
+
}
|
|
19
|
+
export interface AppFilePreviewState {
|
|
20
|
+
readonly kind: 'text' | 'markdown' | 'diff' | 'image';
|
|
21
|
+
readonly text?: string;
|
|
22
|
+
readonly imageSrc?: string;
|
|
23
|
+
readonly imageAlt?: string;
|
|
24
|
+
readonly truncated?: boolean;
|
|
25
|
+
readonly error?: string;
|
|
26
|
+
readonly disabled?: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface FilePreviewInput extends AppFileTarget {
|
|
29
|
+
readonly capabilities: FileCapabilities;
|
|
30
|
+
readonly content?: AppFilePreviewState;
|
|
31
|
+
}
|
|
32
|
+
export interface FilePreviewCallbacks {
|
|
33
|
+
onFileAction?: (action: AppFileAction, target: AppFileTarget) => FileActionResult;
|
|
34
|
+
onCopyCode?: (text: string) => FileActionResult;
|
|
35
|
+
onClose?: () => void;
|
|
36
|
+
}
|
|
37
|
+
export interface FilePreviewSession {
|
|
38
|
+
update(state: Readonly<AppFilePreviewState>): void;
|
|
39
|
+
fail(message: string): void;
|
|
40
|
+
close(): void;
|
|
41
|
+
isOpen(): boolean;
|
|
42
|
+
destroy(): void;
|
|
43
|
+
}
|
|
44
|
+
export interface AppArtifactsState {
|
|
45
|
+
readonly ownerId: string;
|
|
46
|
+
readonly files: readonly {
|
|
47
|
+
readonly name: string;
|
|
48
|
+
readonly path: string;
|
|
49
|
+
}[];
|
|
50
|
+
readonly loading: boolean;
|
|
51
|
+
readonly error: string;
|
|
52
|
+
readonly copyMode: 'file' | 'path';
|
|
53
|
+
readonly disabled?: boolean;
|
|
54
|
+
}
|
|
55
|
+
export interface ArtifactsCallbacks {
|
|
56
|
+
onPreview?: (target: AppFileTarget) => FileActionResult;
|
|
57
|
+
onFileAction?: (action: AppFileAction, target: AppFileTarget) => FileActionResult;
|
|
58
|
+
}
|
|
59
|
+
export interface FilesScene {
|
|
60
|
+
updateArtifacts(state: Readonly<AppArtifactsState>, callbacks?: ArtifactsCallbacks): void;
|
|
61
|
+
closeArtifacts(): void;
|
|
62
|
+
areArtifactsOpen(): boolean;
|
|
63
|
+
openPreview(input: Readonly<FilePreviewInput>, callbacks?: FilePreviewCallbacks): FilePreviewSession;
|
|
64
|
+
destroy(): void;
|
|
65
|
+
}
|
|
66
|
+
/** File data comes from the host; presentation, actions and zoom stay owned here. */
|
|
67
|
+
export declare function createFilesScene(env: AppEnvironment): FilesScene;
|
|
68
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/app/files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { controlDisabled, setControlPending } from '../core.js';
|
|
2
|
+
import { createArtifactPanel, createFileActions, createFilePreview } from '../files/panels.js';
|
|
3
|
+
import { createCopyFeedback } from '../files/systems.js';
|
|
4
|
+
import { createOperation } from '../operation.js';
|
|
5
|
+
/** File data comes from the host; presentation, actions and zoom stay owned here. */
|
|
6
|
+
export function createFilesScene(env) {
|
|
7
|
+
let disposed = false;
|
|
8
|
+
let preview;
|
|
9
|
+
let artifactState = { ownerId: '', files: [], loading: false, error: '', copyMode: 'path' };
|
|
10
|
+
let artifactCallbacks = {};
|
|
11
|
+
const panel = createArtifactPanel({ container: env.part('artifacts'), control: env.part('artifacts-control'), toggle: env.part('artifacts-toggle'), count: env.part('artifacts-count'), headingId: env.idPrefix + 'artifacts-title', dismissExclusions: [env.part('preview-dialog'), env.part('image-dialog')] });
|
|
12
|
+
const operations = new Map();
|
|
13
|
+
const abort = new AbortController();
|
|
14
|
+
let artifactCopy = makeArtifactCopy();
|
|
15
|
+
function makeArtifactCopy() {
|
|
16
|
+
const owner = artifactState.ownerId;
|
|
17
|
+
return createCopyFeedback({ status: env.part('copy-status'), isDisabled: () => disposed || !!artifactState.disabled || artifactState.ownerId !== owner,
|
|
18
|
+
onError(error) { if (!disposed && artifactState.ownerId === owner) {
|
|
19
|
+
artifactState = { ...artifactState, error: error instanceof Error ? error.message : String(error) };
|
|
20
|
+
panel.update(artifactState);
|
|
21
|
+
} } });
|
|
22
|
+
}
|
|
23
|
+
async function completed(result) {
|
|
24
|
+
const value = await result;
|
|
25
|
+
if (value && !value.ok)
|
|
26
|
+
throw new Error(value.error);
|
|
27
|
+
}
|
|
28
|
+
const targetFor = (path) => {
|
|
29
|
+
const file = artifactState.files.find(file => file.path === path);
|
|
30
|
+
return file ? Object.freeze({ owner: Object.freeze({ kind: 'artifact', id: artifactState.ownerId }), name: file.name, path: file.path }) : undefined;
|
|
31
|
+
};
|
|
32
|
+
panel.element.addEventListener('click', event => {
|
|
33
|
+
const button = event.target.closest('button');
|
|
34
|
+
if (!button || button.disabled || disposed || artifactState.disabled)
|
|
35
|
+
return;
|
|
36
|
+
const path = button.dataset.previewPath || button.dataset.filePath;
|
|
37
|
+
if (!path)
|
|
38
|
+
return;
|
|
39
|
+
event.stopPropagation();
|
|
40
|
+
const target = targetFor(path);
|
|
41
|
+
if (!target)
|
|
42
|
+
return;
|
|
43
|
+
if (button.dataset.previewPath) {
|
|
44
|
+
if (artifactCallbacks.onPreview)
|
|
45
|
+
runArtifact(button, () => artifactCallbacks.onPreview(target));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const action = button.dataset.fileAction;
|
|
49
|
+
if (!artifactCallbacks.onFileAction || !['open', 'reveal', 'copy', 'copy-path'].includes(action))
|
|
50
|
+
return;
|
|
51
|
+
if (action === 'copy' || action === 'copy-path')
|
|
52
|
+
void artifactCopy.copy(button, () => completed(artifactCallbacks.onFileAction(action, target)), action === 'copy' ? 'File copied.' : 'Path copied.');
|
|
53
|
+
else
|
|
54
|
+
runArtifact(button, () => artifactCallbacks.onFileAction(action, target));
|
|
55
|
+
}, { signal: abort.signal });
|
|
56
|
+
function runArtifact(button, callback) {
|
|
57
|
+
let operation = operations.get(button);
|
|
58
|
+
if (!operation) {
|
|
59
|
+
const owner = artifactState.ownerId;
|
|
60
|
+
operation = createOperation({ unavailable: () => disposed || artifactState.ownerId !== owner || !!artifactState.disabled,
|
|
61
|
+
onPending(pending) { setControlPending(button, pending); button.disabled = controlDisabled(button, !!artifactState.disabled); },
|
|
62
|
+
onError(message) { if (!disposed && artifactState.ownerId === owner) {
|
|
63
|
+
artifactState = { ...artifactState, error: message };
|
|
64
|
+
panel.update(artifactState);
|
|
65
|
+
} } });
|
|
66
|
+
operations.set(button, operation);
|
|
67
|
+
}
|
|
68
|
+
operation.run(() => completed(callback()));
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
updateArtifacts(next, callbacks) {
|
|
72
|
+
if (disposed)
|
|
73
|
+
return;
|
|
74
|
+
if (next.ownerId !== artifactState.ownerId) {
|
|
75
|
+
artifactCopy.destroy();
|
|
76
|
+
for (const operation of operations.values())
|
|
77
|
+
operation.destroy();
|
|
78
|
+
operations.clear();
|
|
79
|
+
panel.setOpen(false);
|
|
80
|
+
artifactState = next;
|
|
81
|
+
artifactCopy = makeArtifactCopy();
|
|
82
|
+
}
|
|
83
|
+
else
|
|
84
|
+
artifactState = next;
|
|
85
|
+
if (callbacks)
|
|
86
|
+
artifactCallbacks = callbacks;
|
|
87
|
+
panel.update(next);
|
|
88
|
+
for (const button of panel.element.querySelectorAll('[data-file-action], [data-preview-path]'))
|
|
89
|
+
button.disabled = controlDisabled(button, !!next.disabled);
|
|
90
|
+
for (const [button, operation] of operations)
|
|
91
|
+
if (!panel.element.contains(button)) {
|
|
92
|
+
operation.destroy();
|
|
93
|
+
operations.delete(button);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
closeArtifacts() { if (!disposed)
|
|
97
|
+
panel.setOpen(false); },
|
|
98
|
+
areArtifactsOpen: () => !disposed && panel.open,
|
|
99
|
+
openPreview(input, callbacks = {}) {
|
|
100
|
+
preview?.destroy();
|
|
101
|
+
let released = false;
|
|
102
|
+
let content = input.content || { kind: 'text' };
|
|
103
|
+
let surface;
|
|
104
|
+
const target = Object.freeze({ owner: Object.freeze({ ...input.owner }), name: input.name, path: input.path });
|
|
105
|
+
const current = () => !disposed && !released && !!surface?.isOpen();
|
|
106
|
+
const fail = (message) => { if (current()) {
|
|
107
|
+
content = { ...content, error: message };
|
|
108
|
+
block.update(content);
|
|
109
|
+
} };
|
|
110
|
+
const feedback = createCopyFeedback({ status: env.part('copy-status'), isDisabled: () => !current() || !!content.disabled, onError: error => fail(error instanceof Error ? error.message : String(error)) });
|
|
111
|
+
const operation = createOperation({ unavailable: () => !current() || !!content.disabled, onPending: pending => actions.setDisabled(pending || !!content.disabled), onError: fail });
|
|
112
|
+
const actions = createFileActions({ kind: 'artifact', ownerId: input.owner.id, path: input.path, copyMode: input.capabilities.copyMode ?? 'none', onAction: action => {
|
|
113
|
+
if (!current() || content.disabled || !callbacks.onFileAction || action === 'preview')
|
|
114
|
+
return;
|
|
115
|
+
const button = actions.element.querySelector(`[data-file-action="${action}"]`);
|
|
116
|
+
if (action === 'copy' || action === 'copy-path') {
|
|
117
|
+
if (button)
|
|
118
|
+
void feedback.copy(button, () => completed(callbacks.onFileAction(action, target)), action === 'copy' ? 'File copied.' : 'Path copied.');
|
|
119
|
+
}
|
|
120
|
+
else
|
|
121
|
+
operation.run(() => completed(callbacks.onFileAction(action, target)));
|
|
122
|
+
} });
|
|
123
|
+
if (input.capabilities.open === false)
|
|
124
|
+
actions.element.querySelector('[data-file-action="open"]')?.remove();
|
|
125
|
+
if (input.capabilities.reveal === false)
|
|
126
|
+
actions.element.querySelector('[data-file-action="reveal"]')?.remove();
|
|
127
|
+
const block = createFilePreview({ titleId: env.idPrefix + 'preview-title', dialogId: env.idPrefix + 'preview-dialog', bodyId: env.idPrefix + 'file-preview-body', onClose: () => surface?.close(), onCopyCode: (text, button) => {
|
|
128
|
+
if (!current())
|
|
129
|
+
return;
|
|
130
|
+
void feedback.copy(button, () => completed(callbacks.onCopyCode ? callbacks.onCopyCode(text) : env.copyText(text)), 'Code copied.');
|
|
131
|
+
} });
|
|
132
|
+
const listeners = new AbortController();
|
|
133
|
+
const zoom = (event) => {
|
|
134
|
+
const image = event.target;
|
|
135
|
+
if (!current() || !(image instanceof HTMLImageElement) || !image.matches('.pui-file-preview-image'))
|
|
136
|
+
return;
|
|
137
|
+
if (event instanceof KeyboardEvent && (event.isComposing || !['Enter', ' '].includes(event.key)))
|
|
138
|
+
return;
|
|
139
|
+
event.preventDefault();
|
|
140
|
+
event.stopPropagation();
|
|
141
|
+
env.openImage(image);
|
|
142
|
+
};
|
|
143
|
+
block.element.addEventListener('click', zoom, { signal: listeners.signal });
|
|
144
|
+
block.element.addEventListener('keydown', zoom, { signal: listeners.signal });
|
|
145
|
+
block.open({ name: input.name, path: input.path, actions: actions.element });
|
|
146
|
+
const release = () => { if (released)
|
|
147
|
+
return; released = true; listeners.abort(); operation.destroy(); feedback.destroy(); actions.destroy(); block.destroy(); };
|
|
148
|
+
const session = {
|
|
149
|
+
update(state) { if (current()) {
|
|
150
|
+
content = state;
|
|
151
|
+
block.update(state);
|
|
152
|
+
actions.setDisabled(!!state.disabled || operation.pending);
|
|
153
|
+
} },
|
|
154
|
+
fail,
|
|
155
|
+
close() { if (current())
|
|
156
|
+
surface.close(); },
|
|
157
|
+
isOpen: current,
|
|
158
|
+
destroy() { if (released)
|
|
159
|
+
return; if (surface?.isOpen())
|
|
160
|
+
surface.destroy(); release(); },
|
|
161
|
+
};
|
|
162
|
+
if (!disposed) {
|
|
163
|
+
surface = env.openDialog('preview', { element: block.element, destroy: release }, () => { release(); callbacks.onClose?.(); });
|
|
164
|
+
preview = session;
|
|
165
|
+
if (input.content)
|
|
166
|
+
session.update(input.content);
|
|
167
|
+
}
|
|
168
|
+
else
|
|
169
|
+
release();
|
|
170
|
+
return session;
|
|
171
|
+
},
|
|
172
|
+
destroy() { if (disposed)
|
|
173
|
+
return; disposed = true; preview?.destroy(); abort.abort(); artifactCopy.destroy(); for (const operation of operations.values())
|
|
174
|
+
operation.destroy(); operations.clear(); panel.destroy(); },
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/app/files.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,eAAe,EAAE,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAC,mBAAmB,EAAE,iBAAiB,EAAE,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAC7F,OAAO,EAAC,kBAAkB,EAAC,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAC,eAAe,EAAC,MAAM,iBAAiB,CAAC;AAqDhD,qFAAqF;AACrF,MAAM,UAAU,gBAAgB,CAAC,GAAmB;IAClD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,OAAuC,CAAC;IAC5C,IAAI,aAAa,GAAgC,EAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAC,CAAC;IACvH,IAAI,iBAAiB,GAAuB,EAAE,CAAC;IAC/C,MAAM,KAAK,GAAG,mBAAmB,CAAC,EAAC,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,QAAQ,GAAG,iBAAiB,EAAE,iBAAiB,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAC,CAAC,CAAC;IAChT,MAAM,UAAU,GAAG,IAAI,GAAG,EAAyD,CAAC;IACpF,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IACpC,IAAI,YAAY,GAAG,gBAAgB,EAAE,CAAC;IACtC,SAAS,gBAAgB;QACvB,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC;QACpC,OAAO,kBAAkB,CAAC,EAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,QAAQ,IAAI,CAAC,CAAC,aAAa,CAAC,QAAQ,IAAI,aAAa,CAAC,OAAO,KAAK,KAAK;YACnJ,OAAO,CAAC,KAAK,IAAG,IAAI,CAAC,QAAQ,IAAI,aAAa,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;gBAAA,aAAa,GAAG,EAAC,GAAG,aAAa,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAC,CAAC;gBAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YAAA,CAAC,CAAA,CAAC,EAAC,CAAC,CAAC;IAC3M,CAAC;IACD,KAAK,UAAU,SAAS,CAAC,MAAwB;QAC/C,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC;QAC3B,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,SAAS,GAAG,CAAC,IAAY,EAA6B,EAAE;QAC5D,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QAClE,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,EAAC,IAAI,EAAE,UAAmB,EAAE,EAAE,EAAE,aAAa,CAAC,OAAO,EAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5J,CAAC,CAAC;IACF,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE;QAC9C,MAAM,MAAM,GAAI,KAAK,CAAC,MAAkB,CAAC,OAAO,CAAoB,QAAQ,CAAC,CAAC;QAC9E,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,IAAI,QAAQ,IAAI,aAAa,CAAC,QAAQ;YAAE,OAAO;QAC7E,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;QACnE,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YAC/B,IAAI,iBAAiB,CAAC,SAAS;gBAAE,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,SAAU,CAAC,MAAM,CAAC,CAAC,CAAC;YACjG,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,UAA2B,CAAC;QAC1D,IAAI,CAAC,iBAAiB,CAAC,YAAY,IAAI,CAAC,CAAC,MAAM,EAAC,QAAQ,EAAC,MAAM,EAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO;QACtG,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,WAAW;YAAE,KAAK,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,iBAAiB,CAAC,YAAa,CAAC,MAAM,EAAC,MAAM,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;;YACjM,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,YAAa,CAAC,MAAM,EAAC,MAAM,CAAC,CAAC,CAAC;IACjF,CAAC,EAAE,EAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAC,CAAC,CAAC;IAC3B,SAAS,WAAW,CAAC,MAAyB,EAAE,QAAgC;QAC9E,IAAI,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC;YACpC,SAAS,GAAG,eAAe,CAAC,EAAC,WAAW,EAAE,GAAG,EAAE,CAAC,QAAQ,IAAI,aAAa,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,aAAa,CAAC,QAAQ;gBACrH,SAAS,CAAC,OAAO,IAAG,iBAAiB,CAAC,MAAM,EAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,GAAG,eAAe,CAAC,MAAM,EAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAA,CAAC;gBAC3H,OAAO,CAAC,OAAO,IAAG,IAAI,CAAC,QAAQ,IAAI,aAAa,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;oBAAA,aAAa,GAAG,EAAC,GAAG,aAAa,EAAC,KAAK,EAAC,OAAO,EAAC,CAAC;oBAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;gBAAA,CAAC,CAAA,CAAC,EAAC,CAAC,CAAC;YAC1J,UAAU,CAAC,GAAG,CAAC,MAAM,EAAC,SAAS,CAAC,CAAC;QACnC,CAAC;QACD,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO;QACL,eAAe,CAAC,IAAI,EAAE,SAAS;YAC7B,IAAI,QAAQ;gBAAE,OAAO;YACrB,IAAI,IAAI,CAAC,OAAO,KAAK,aAAa,CAAC,OAAO,EAAE,CAAC;gBAC3C,YAAY,CAAC,OAAO,EAAE,CAAC;gBAAC,KAAK,MAAM,SAAS,IAAI,UAAU,CAAC,MAAM,EAAE;oBAAE,SAAS,CAAC,OAAO,EAAE,CAAC;gBAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBAC7G,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBACrB,aAAa,GAAG,IAAI,CAAC;gBAAC,YAAY,GAAG,gBAAgB,EAAE,CAAC;YAC1D,CAAC;;gBAAM,aAAa,GAAG,IAAI,CAAC;YAC5B,IAAI,SAAS;gBAAE,iBAAiB,GAAG,SAAS,CAAC;YAC7C,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACnB,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAoB,yCAAyC,CAAC;gBAAE,MAAM,CAAC,QAAQ,GAAG,eAAe,CAAC,MAAM,EAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7K,KAAK,MAAM,CAAC,MAAM,EAAC,SAAS,CAAC,IAAI,UAAU;gBAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBAAA,SAAS,CAAC,OAAO,EAAE,CAAC;oBAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBAAA,CAAC;QACrI,CAAC;QACD,cAAc,KAAI,IAAI,CAAC,QAAQ;YAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA,CAAC;QACvD,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI;QAC/C,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,EAAE;YAC/B,OAAO,EAAE,OAAO,EAAE,CAAC;YACnB,IAAI,QAAQ,GAAG,KAAK,CAAC;YACrB,IAAI,OAAO,GAAkC,KAAK,CAAC,OAAO,IAAI,EAAC,IAAI,EAAE,MAAM,EAAC,CAAC;YAC7E,IAAI,OAAiC,CAAC;YACtC,MAAM,MAAM,GAAkB,MAAM,CAAC,MAAM,CAAC,EAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,EAAC,GAAG,KAAK,CAAC,KAAK,EAAC,CAAC,EAAC,IAAI,EAAC,KAAK,CAAC,IAAI,EAAC,IAAI,EAAC,KAAK,CAAC,IAAI,EAAC,CAAC,CAAC;YACtH,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;YACpE,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,EAAE,GAAE,IAAI,OAAO,EAAE,EAAE,CAAC;gBAAA,OAAO,GAAG,EAAC,GAAG,OAAO,EAAC,KAAK,EAAC,OAAO,EAAC,CAAC;gBAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAAA,CAAC,CAAA,CAAC,CAAC;YAClH,MAAM,QAAQ,GAAG,kBAAkB,CAAC,EAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAC,CAAC,CAAC;YAC3M,MAAM,SAAS,GAAG,eAAe,CAAC,EAAC,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC,CAAC;YAClL,MAAM,OAAO,GAAG,iBAAiB,CAAC,EAAC,IAAI,EAAE,UAAU,EAAC,OAAO,EAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAC,IAAI,EAAC,KAAK,CAAC,IAAI,EAAC,QAAQ,EAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,IAAI,MAAM,EAAC,QAAQ,EAAE,MAAM,CAAC,EAAE;oBAC3J,IAAI,CAAC,OAAO,EAAE,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,SAAS,CAAC,YAAY,IAAI,MAAM,KAAK,SAAS;wBAAE,OAAO;oBAC9F,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAoB,sBAAsB,MAAM,IAAI,CAAC,CAAC;oBAClG,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;wBAChD,IAAI,MAAM;4BAAE,KAAK,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,YAAa,CAAC,MAAM,EAAC,MAAM,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;oBACvJ,CAAC;;wBAAM,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,YAAa,CAAC,MAAM,EAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBAChF,CAAC,EAAC,CAAC,CAAC;YACJ,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,KAAK;gBAAE,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,2BAA2B,CAAC,EAAE,MAAM,EAAE,CAAC;YAC5G,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,KAAK,KAAK;gBAAE,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,6BAA6B,CAAC,EAAE,MAAM,EAAE,CAAC;YAChH,MAAM,KAAK,GAAG,iBAAiB,CAAC,EAAC,OAAO,EAAC,GAAG,CAAC,QAAQ,GAAC,eAAe,EAAC,QAAQ,EAAC,GAAG,CAAC,QAAQ,GAAC,gBAAgB,EAAC,MAAM,EAAC,GAAG,CAAC,QAAQ,GAAC,mBAAmB,EAAC,OAAO,EAAC,GAAE,EAAE,CAAA,OAAO,EAAE,KAAK,EAAE,EAAC,UAAU,EAAC,CAAC,IAAI,EAAC,MAAM,EAAE,EAAE;oBAC3M,IAAI,CAAC,OAAO,EAAE;wBAAE,OAAO;oBACvB,KAAK,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;gBACtI,CAAC,EAAC,CAAC,CAAC;YACJ,MAAM,SAAS,GAAG,IAAI,eAAe,EAAE,CAAC;YACxC,MAAM,IAAI,GAAG,CAAC,KAAY,EAAE,EAAE;gBAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;gBAC3B,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,YAAY,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,yBAAyB,CAAC;oBAAE,OAAO;gBAC5G,IAAI,KAAK,YAAY,aAAa,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO,EAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBAAE,OAAO;gBACxG,KAAK,CAAC,cAAc,EAAE,CAAC;gBAAC,KAAK,CAAC,eAAe,EAAE,CAAC;gBAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACxE,CAAC,CAAC;YACF,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAC,IAAI,EAAC,EAAC,MAAM,EAAC,SAAS,CAAC,MAAM,EAAC,CAAC,CAAC;YAAC,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAC,IAAI,EAAC,EAAC,MAAM,EAAC,SAAS,CAAC,MAAM,EAAC,CAAC,CAAC;YACjJ,KAAK,CAAC,IAAI,CAAC,EAAC,IAAI,EAAC,KAAK,CAAC,IAAI,EAAC,IAAI,EAAC,KAAK,CAAC,IAAI,EAAC,OAAO,EAAC,OAAO,CAAC,OAAO,EAAC,CAAC,CAAC;YACtE,MAAM,OAAO,GAAG,GAAG,EAAE,GAAE,IAAI,QAAQ;gBAAE,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA,CAAC,CAAC;YAC/J,MAAM,OAAO,GAAuB;gBAClC,MAAM,CAAC,KAAK,IAAG,IAAI,OAAO,EAAE,EAAE,CAAC;oBAAA,OAAO,GAAG,KAAK,CAAC;oBAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,SAAS,CAAC,OAAO,CAAC,CAAC;gBAAA,CAAC,CAAA,CAAC;gBAClI,IAAI;gBACJ,KAAK,KAAI,IAAI,OAAO,EAAE;oBAAE,OAAQ,CAAC,KAAK,EAAE,CAAC,CAAA,CAAC;gBAC1C,MAAM,EAAE,OAAO;gBACf,OAAO,KAAI,IAAI,QAAQ;oBAAE,OAAO,CAAC,IAAI,OAAO,EAAE,MAAM,EAAE;oBAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA,CAAC;aACvF,CAAC;YACF,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,OAAO,GAAG,GAAG,CAAC,UAAU,CAAC,SAAS,EAAC,EAAC,OAAO,EAAC,KAAK,CAAC,OAAO,EAAC,OAAO,EAAC,OAAO,EAAC,EAAC,GAAG,EAAE,GAAE,OAAO,EAAE,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA,CAAC,CAAC,CAAC;gBACtH,OAAO,GAAG,OAAO,CAAC;gBAAC,IAAI,KAAK,CAAC,OAAO;oBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACtE,CAAC;;gBAAM,OAAO,EAAE,CAAC;YACjB,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,OAAO,KAAI,IAAI,QAAQ;YAAE,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,MAAM,SAAS,IAAI,UAAU,CAAC,MAAM,EAAE;YAAE,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA,CAAC;KACrN,CAAC;AACJ,CAAC","sourcesContent":["import type {ActionResult} from '../core.js';\nimport {controlDisabled, setControlPending} from '../core.js';\nimport {createArtifactPanel, createFileActions, createFilePreview} from '../files/panels.js';\nimport {createCopyFeedback} from '../files/systems.js';\nimport {createOperation} from '../operation.js';\nimport type {AppEnvironment, OwnedSurface} from './internal.js';\n\nexport type AppFileAction = 'open' | 'reveal' | 'copy' | 'copy-path';\nexport type FileActionResult = void | ActionResult | Promise<void | ActionResult>;\nexport interface AppFileOwner {readonly kind: 'artifact'; readonly id: string}\nexport interface AppFileTarget {readonly owner: AppFileOwner; readonly name: string; readonly path: string}\nexport interface FileCapabilities {readonly open?: boolean; readonly reveal?: boolean; readonly copyMode?: 'file' | 'path' | 'none'}\nexport interface AppFilePreviewState {\n readonly kind: 'text' | 'markdown' | 'diff' | 'image';\n readonly text?: string;\n readonly imageSrc?: string;\n readonly imageAlt?: string;\n readonly truncated?: boolean;\n readonly error?: string;\n readonly disabled?: boolean;\n}\nexport interface FilePreviewInput extends AppFileTarget {\n readonly capabilities: FileCapabilities;\n readonly content?: AppFilePreviewState;\n}\nexport interface FilePreviewCallbacks {\n onFileAction?: (action: AppFileAction, target: AppFileTarget) => FileActionResult;\n onCopyCode?: (text: string) => FileActionResult;\n onClose?: () => void;\n}\nexport interface FilePreviewSession {\n update(state: Readonly<AppFilePreviewState>): void;\n fail(message: string): void;\n close(): void;\n isOpen(): boolean;\n destroy(): void;\n}\nexport interface AppArtifactsState {\n readonly ownerId: string;\n readonly files: readonly {readonly name: string; readonly path: string}[];\n readonly loading: boolean;\n readonly error: string;\n readonly copyMode: 'file' | 'path';\n readonly disabled?: boolean;\n}\nexport interface ArtifactsCallbacks {\n onPreview?: (target: AppFileTarget) => FileActionResult;\n onFileAction?: (action: AppFileAction, target: AppFileTarget) => FileActionResult;\n}\nexport interface FilesScene {\n updateArtifacts(state: Readonly<AppArtifactsState>, callbacks?: ArtifactsCallbacks): void;\n closeArtifacts(): void;\n areArtifactsOpen(): boolean;\n openPreview(input: Readonly<FilePreviewInput>, callbacks?: FilePreviewCallbacks): FilePreviewSession;\n destroy(): void;\n}\n\n/** File data comes from the host; presentation, actions and zoom stay owned here. */\nexport function createFilesScene(env: AppEnvironment): FilesScene {\n let disposed = false;\n let preview: FilePreviewSession | undefined;\n let artifactState: Readonly<AppArtifactsState> = {ownerId: '', files: [], loading: false, error: '', copyMode: 'path'};\n let artifactCallbacks: ArtifactsCallbacks = {};\n const panel = createArtifactPanel({container: env.part('artifacts'), control: env.part('artifacts-control'), toggle: env.part('artifacts-toggle'), count: env.part('artifacts-count'), headingId: env.idPrefix + 'artifacts-title', dismissExclusions: [env.part('preview-dialog'), env.part('image-dialog')]});\n const operations = new Map<HTMLButtonElement, ReturnType<typeof createOperation>>();\n const abort = new AbortController();\n let artifactCopy = makeArtifactCopy();\n function makeArtifactCopy() {\n const owner = artifactState.ownerId;\n return createCopyFeedback({status: env.part('copy-status'), isDisabled: () => disposed || !!artifactState.disabled || artifactState.ownerId !== owner,\n onError(error) {if (!disposed && artifactState.ownerId === owner) {artifactState = {...artifactState, error: error instanceof Error ? error.message : String(error)}; panel.update(artifactState);}}});\n }\n async function completed(result: FileActionResult): Promise<void> {\n const value = await result;\n if (value && !value.ok) throw new Error(value.error);\n }\n const targetFor = (path: string): AppFileTarget | undefined => {\n const file = artifactState.files.find(file => file.path === path);\n return file ? Object.freeze({owner: Object.freeze({kind: 'artifact' as const, id: artifactState.ownerId}), name: file.name, path: file.path}) : undefined;\n };\n panel.element.addEventListener('click', event => {\n const button = (event.target as Element).closest<HTMLButtonElement>('button');\n if (!button || button.disabled || disposed || artifactState.disabled) return;\n const path = button.dataset.previewPath || button.dataset.filePath;\n if (!path) return;\n event.stopPropagation();\n const target = targetFor(path);\n if (!target) return;\n if (button.dataset.previewPath) {\n if (artifactCallbacks.onPreview) runArtifact(button, () => artifactCallbacks.onPreview!(target));\n return;\n }\n const action = button.dataset.fileAction as AppFileAction;\n if (!artifactCallbacks.onFileAction || !['open','reveal','copy','copy-path'].includes(action)) return;\n if (action === 'copy' || action === 'copy-path') void artifactCopy.copy(button, () => completed(artifactCallbacks.onFileAction!(action,target)), action === 'copy' ? 'File copied.' : 'Path copied.');\n else runArtifact(button, () => artifactCallbacks.onFileAction!(action,target));\n }, {signal: abort.signal});\n function runArtifact(button: HTMLButtonElement, callback: () => FileActionResult): void {\n let operation = operations.get(button);\n if (!operation) {\n const owner = artifactState.ownerId;\n operation = createOperation({unavailable: () => disposed || artifactState.ownerId !== owner || !!artifactState.disabled,\n onPending(pending) {setControlPending(button,pending); button.disabled = controlDisabled(button,!!artifactState.disabled);},\n onError(message) {if (!disposed && artifactState.ownerId === owner) {artifactState = {...artifactState,error:message}; panel.update(artifactState);}}});\n operations.set(button,operation);\n }\n operation.run(() => completed(callback()));\n }\n return {\n updateArtifacts(next, callbacks) {\n if (disposed) return;\n if (next.ownerId !== artifactState.ownerId) {\n artifactCopy.destroy(); for (const operation of operations.values()) operation.destroy(); operations.clear();\n panel.setOpen(false);\n artifactState = next; artifactCopy = makeArtifactCopy();\n } else artifactState = next;\n if (callbacks) artifactCallbacks = callbacks;\n panel.update(next);\n for (const button of panel.element.querySelectorAll<HTMLButtonElement>('[data-file-action], [data-preview-path]')) button.disabled = controlDisabled(button,!!next.disabled);\n for (const [button,operation] of operations) if (!panel.element.contains(button)) {operation.destroy(); operations.delete(button);}\n },\n closeArtifacts() {if (!disposed) panel.setOpen(false);},\n areArtifactsOpen: () => !disposed && panel.open,\n openPreview(input, callbacks = {}) {\n preview?.destroy();\n let released = false;\n let content: Readonly<AppFilePreviewState> = input.content || {kind: 'text'};\n let surface: OwnedSurface | undefined;\n const target: AppFileTarget = Object.freeze({owner: Object.freeze({...input.owner}),name:input.name,path:input.path});\n const current = () => !disposed && !released && !!surface?.isOpen();\n const fail = (message: string) => {if (current()) {content = {...content,error:message}; block.update(content);}};\n const feedback = createCopyFeedback({status: env.part('copy-status'), isDisabled: () => !current() || !!content.disabled, onError: error => fail(error instanceof Error ? error.message : String(error))});\n const operation = createOperation({unavailable: () => !current() || !!content.disabled, onPending: pending => actions.setDisabled(pending || !!content.disabled), onError: fail});\n const actions = createFileActions({kind: 'artifact',ownerId:input.owner.id,path:input.path,copyMode:input.capabilities.copyMode ?? 'none',onAction: action => {\n if (!current() || content.disabled || !callbacks.onFileAction || action === 'preview') return;\n const button = actions.element.querySelector<HTMLButtonElement>(`[data-file-action=\"${action}\"]`);\n if (action === 'copy' || action === 'copy-path') {\n if (button) void feedback.copy(button, () => completed(callbacks.onFileAction!(action,target)), action === 'copy' ? 'File copied.' : 'Path copied.');\n } else operation.run(() => completed(callbacks.onFileAction!(action,target)));\n }});\n if (input.capabilities.open === false) actions.element.querySelector('[data-file-action=\"open\"]')?.remove();\n if (input.capabilities.reveal === false) actions.element.querySelector('[data-file-action=\"reveal\"]')?.remove();\n const block = createFilePreview({titleId:env.idPrefix+'preview-title',dialogId:env.idPrefix+'preview-dialog',bodyId:env.idPrefix+'file-preview-body',onClose:()=>surface?.close(),onCopyCode:(text,button) => {\n if (!current()) return;\n void feedback.copy(button, () => completed(callbacks.onCopyCode ? callbacks.onCopyCode(text) : env.copyText(text)), 'Code copied.');\n }});\n const listeners = new AbortController();\n const zoom = (event: Event) => {\n const image = event.target;\n if (!current() || !(image instanceof HTMLImageElement) || !image.matches('.pui-file-preview-image')) return;\n if (event instanceof KeyboardEvent && (event.isComposing || !['Enter',' '].includes(event.key))) return;\n event.preventDefault(); event.stopPropagation(); env.openImage(image);\n };\n block.element.addEventListener('click',zoom,{signal:listeners.signal}); block.element.addEventListener('keydown',zoom,{signal:listeners.signal});\n block.open({name:input.name,path:input.path,actions:actions.element});\n const release = () => {if (released) return; released = true; listeners.abort(); operation.destroy(); feedback.destroy(); actions.destroy(); block.destroy();};\n const session: FilePreviewSession = {\n update(state) {if (current()) {content = state; block.update(state); actions.setDisabled(!!state.disabled || operation.pending);}},\n fail,\n close() {if (current()) surface!.close();},\n isOpen: current,\n destroy() {if (released) return; if (surface?.isOpen()) surface.destroy(); release();},\n };\n if (!disposed) {\n surface = env.openDialog('preview',{element:block.element,destroy:release},() => {release(); callbacks.onClose?.();});\n preview = session; if (input.content) session.update(input.content);\n } else release();\n return session;\n },\n destroy() {if (disposed) return; disposed = true; preview?.destroy(); abort.abort(); artifactCopy.destroy(); for (const operation of operations.values()) operation.destroy(); operations.clear(); panel.destroy();},\n };\n}\n"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type Appearance } from '../theme.js';
|
|
2
|
+
import type { ActionResult } from '../core.js';
|
|
3
|
+
import { type WorkspaceCallbacks, type WorkspaceScene } from './workspace.js';
|
|
4
|
+
import { type SettingsScene } from './settings.js';
|
|
5
|
+
import { type ResourcesScene } from './resources.js';
|
|
6
|
+
import { type FilesScene } from './files.js';
|
|
7
|
+
import type { HostResult } from './internal.js';
|
|
8
|
+
export type { LoadingState, LoadingSession } from './session.js';
|
|
9
|
+
export type { ActionResult } from '../core.js';
|
|
10
|
+
export type { Appearance } from '../theme.js';
|
|
11
|
+
export type { WorkspaceCallbacks, WorkspaceScene, WorkspaceAction, WorkspaceAvailability, NavigationStatus, NavigationState, ApplicationWorkspace, ApplicationConversation, ConversationState, ImageTarget, ConversationNavigationAction, SceneSession, MessageEditorState, MessageEditorSession, ConfirmationState, ComposerViewState, WorkspaceShellState, WelcomeState, PendingMessagesState, AttachmentsState, PermissionControlState, FullAccessState, TaskFailureState, ApprovalPanelState, RuntimePanelState, CostLedgerState, CompletionNoticeDisplay, ConversationMessage, ConversationAction, ConversationActionAvailability, WorkspacePickerState, RenameConversationState, RenameConversationDraft, BranchHistoryState } from './workspace.js';
|
|
12
|
+
export type { SettingsScene, SettingsSession, MCPSession, ModelSession, CustomConnectionSession, ProviderLoginSession, SettingsCredential, SettingsCallbacks, MCPCallbacks, ModelCallbacks, CustomConnectionCallbacks, ProviderLoginCallbacks, SettingsPanelState, SettingsDraft, SettingsProviderOption, MCPConnectionsState, MCPConnectionDraft, MCPEditorValue, MCPConnectionItem, ModelPickerState, ModelSelection, CustomConnectionState, CustomConnectionDraft, ProviderLoginState, UpdatePanelState, ProviderAction, ProviderActionsState, ProviderActionTarget, MCPConnectionAction, MCPConnectionTarget, UpdateAction } from './settings.js';
|
|
13
|
+
export type { ResourcesState, ResourcesCallbacks, ResourcesSession, ResourcesScene, AppResourceEntry, AppResourceTarget, AppResourceKind, AppResourceEditorState, ResourceEditorCallbacks, ResourceEditorSession, ResourceActionResult, ResourceDraft } from './resources.js';
|
|
14
|
+
export type { FilesScene, AppArtifactsState, ArtifactsCallbacks, AppFileOwner, AppFileTarget, AppFileAction, FileCapabilities, FileActionResult, AppFilePreviewState, FilePreviewInput, FilePreviewCallbacks, FilePreviewSession } from './files.js';
|
|
15
|
+
export { formatCost } from '../files/systems.js';
|
|
16
|
+
export { thinkingLabel } from '../forms/model-editor.js';
|
|
17
|
+
export interface ApplicationBrand {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly qualifier?: string;
|
|
20
|
+
readonly mark?: {
|
|
21
|
+
readonly src: string;
|
|
22
|
+
readonly darkSrc?: string;
|
|
23
|
+
readonly alt: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface ApplicationHost {
|
|
27
|
+
copyText?(text: string): HostResult;
|
|
28
|
+
openExternal?(url: string): HostResult;
|
|
29
|
+
}
|
|
30
|
+
export interface ApplicationState {
|
|
31
|
+
readonly hasModal: boolean;
|
|
32
|
+
readonly appearance: Appearance;
|
|
33
|
+
}
|
|
34
|
+
export interface ApplicationOptions {
|
|
35
|
+
readonly brand?: ApplicationBrand;
|
|
36
|
+
readonly appearance?: Appearance;
|
|
37
|
+
readonly host?: ApplicationHost;
|
|
38
|
+
readonly workspace?: WorkspaceCallbacks;
|
|
39
|
+
readonly onUIStateChange?: (state: ApplicationState) => void;
|
|
40
|
+
readonly onError?: (message: string) => void;
|
|
41
|
+
}
|
|
42
|
+
export interface Application {
|
|
43
|
+
readonly workspace: WorkspaceScene;
|
|
44
|
+
readonly settings: SettingsScene;
|
|
45
|
+
readonly resources: ResourcesScene;
|
|
46
|
+
readonly files: FilesScene;
|
|
47
|
+
read(): ApplicationState;
|
|
48
|
+
setAppearance(appearance: Appearance): void;
|
|
49
|
+
downloadFile(file: {
|
|
50
|
+
readonly blob: Blob;
|
|
51
|
+
readonly name: string;
|
|
52
|
+
}): Promise<ActionResult>;
|
|
53
|
+
destroy(): void;
|
|
54
|
+
}
|
|
55
|
+
/** The only DOM argument is the application mount. Scenes return semantic ports. */
|
|
56
|
+
export declare function createApplication(mount: HTMLElement, options?: ApplicationOptions): Application;
|
|
57
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/app/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgB,KAAK,UAAU,EAAC,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAuB,KAAK,kBAAkB,EAAE,KAAK,cAAc,EAAC,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAsB,KAAK,aAAa,EAAC,MAAM,eAAe,CAAC;AACtE,OAAO,EAAuB,KAAK,cAAc,EAAC,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAAmB,KAAK,UAAU,EAAC,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAiB,UAAU,EAA6B,MAAM,eAAe,CAAC;AAC1F,YAAY,EAAC,YAAY,EAAE,cAAc,EAAC,MAAM,cAAc,CAAC;AAE/D,YAAY,EAAC,YAAY,EAAC,MAAM,YAAY,CAAC;AAC7C,YAAY,EAAC,UAAU,EAAC,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAC,kBAAkB,EAAE,cAAc,EAAE,eAAe,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,WAAW,EAAE,4BAA4B,EAAE,YAAY,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,eAAe,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,kBAAkB,EAAC,MAAM,gBAAgB,CAAC;AACztB,YAAY,EAAC,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,aAAa,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,cAAc,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,YAAY,EAAC,MAAM,eAAe,CAAC;AACpnB,YAAY,EAAC,cAAc,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,cAAc,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAC5Q,YAAY,EAAC,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,YAAY,CAAC;AACnP,OAAO,EAAC,UAAU,EAAC,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAC,aAAa,EAAC,MAAM,0BAA0B,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAC,CAAC;CACzF;AACD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IACpC,YAAY,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC;CACxC;AACD,MAAM,WAAW,gBAAgB;IAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;CAAC;AAC/F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC7D,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AACD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,IAAI,IAAI,gBAAgB,CAAC;IACzB,aAAa,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5C,YAAY,CAAC,IAAI,EAAE;QAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACxF,OAAO,IAAI,IAAI,CAAC;CACjB;AAQD,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,GAAE,kBAAuB,GAAG,WAAW,CAuJnG"}
|