@chainingintention/pi-web-cn 1.202607.5 → 1.202607.6
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/README.md +3 -0
- package/dist/cli.js +283 -315
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/CodeViewer-DxWBEGHy.js +4 -0
- package/dist/client/assets/TerminalPanel-CLBbwrSg.js +184 -0
- package/dist/client/assets/UnifiedDiffViewer-CvVtrOUV.js +39 -0
- package/dist/client/assets/index-CEswQiQV.js +3149 -0
- package/dist/client/assets/vendor-editor-core-vUi74DnD.js +12 -0
- package/dist/client/assets/vendor-editor-languages-DoaXUodB.js +46 -0
- package/dist/client/assets/vendor-editor-legacy-CYBnW6ZU.js +1 -0
- package/dist/client/assets/vendor-terminal-BrP-ENHg.css +1 -0
- package/dist/client/assets/vendor-terminal-D8k4UKM2.js +35 -0
- package/dist/client/index.html +6 -6
- package/dist/client/manifest.webmanifest +4 -4
- package/dist/config.js +221 -9
- package/dist/config.js.map +1 -1
- package/dist/nativeServices/serviceDoctor.js +480 -0
- package/dist/nativeServices/serviceDoctor.js.map +1 -0
- package/dist/nativeServices/serviceInstall.js +35 -0
- package/dist/nativeServices/serviceInstall.js.map +1 -0
- package/dist/nativeServices/servicePlan.js +357 -0
- package/dist/nativeServices/servicePlan.js.map +1 -0
- package/dist/nativeServices/serviceProbe.js +445 -0
- package/dist/nativeServices/serviceProbe.js.map +1 -0
- package/dist/nativeServices/serviceRendering.js +122 -0
- package/dist/nativeServices/serviceRendering.js.map +1 -0
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +11 -0
- package/dist/plugin-api.d.ts +2 -0
- package/dist/server/activeAgentProfileProvider.js +25 -0
- package/dist/server/activeAgentProfileProvider.js.map +1 -0
- package/dist/server/app.js +63 -16
- package/dist/server/app.js.map +1 -1
- package/dist/server/browserMessageProjection.js +56 -0
- package/dist/server/browserMessageProjection.js.map +1 -0
- package/dist/server/configRoutes.js +152 -10
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/machines/machineClient.js +19 -9
- package/dist/server/machines/machineClient.js.map +1 -1
- package/dist/server/machines/machinePluginProxyRoutes.js +7 -4
- package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +16 -4
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineRoutes.js +1 -1
- package/dist/server/machines/machineRoutes.js.map +1 -1
- package/dist/server/machines/machineService.js +2 -2
- package/dist/server/machines/machineService.js.map +1 -1
- package/dist/server/managementEmbed.js +28 -11
- package/dist/server/managementEmbed.js.map +1 -1
- package/dist/server/piPackageRoutes.js +7 -3
- package/dist/server/piPackageRoutes.js.map +1 -1
- package/dist/server/piPackageService.js +34 -2
- package/dist/server/piPackageService.js.map +1 -1
- package/dist/server/piWebPluginService.js +48 -12
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/piWebReleaseLookupCache.js +37 -0
- package/dist/server/piWebReleaseLookupCache.js.map +1 -0
- package/dist/server/piWebStatus.js +48 -49
- package/dist/server/piWebStatus.js.map +1 -1
- package/dist/server/piWebStatusCache.js +19 -6
- package/dist/server/piWebStatusCache.js.map +1 -1
- package/dist/server/realtime/sessionEventHub.js +2 -1
- package/dist/server/realtime/sessionEventHub.js.map +1 -1
- package/dist/server/realtime/sessionEventScope.js +0 -3
- package/dist/server/realtime/sessionEventScope.js.map +1 -1
- package/dist/server/sessiond/sessionDaemonStartup.js +49 -0
- package/dist/server/sessiond/sessionDaemonStartup.js.map +1 -0
- package/dist/server/sessiond.js +102 -70
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/authProviderOptions.js +23 -22
- package/dist/server/sessions/authProviderOptions.js.map +1 -1
- package/dist/server/sessions/authRoutes.js +14 -12
- package/dist/server/sessions/authRoutes.js.map +1 -1
- package/dist/server/sessions/authService.js +28 -22
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/oauthLoginFlowService.js +17 -19
- package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
- package/dist/server/sessions/piSessionManagerGateway.js +22 -21
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +237 -111
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/projectAuthService.js +135 -0
- package/dist/server/sessions/projectAuthService.js.map +1 -0
- package/dist/server/sessions/sessionArchiveMigration.js +590 -0
- package/dist/server/sessions/sessionArchiveMigration.js.map +1 -0
- package/dist/server/sessions/sessionArchiveStore.js +7 -4
- package/dist/server/sessions/sessionArchiveStore.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +15 -4
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionService.js +2 -0
- package/dist/server/sessions/sessionService.js.map +1 -0
- package/dist/server/sessions/spawnSubsessionTool.js +71 -31
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/terminals/terminalService.js +2 -2
- package/dist/server/terminals/terminalService.js.map +1 -1
- package/dist/sessiond/activeAgentProfile.js +31 -0
- package/dist/sessiond/activeAgentProfile.js.map +1 -0
- package/dist/sessiond/sessionDaemonClient.js +38 -0
- package/dist/sessiond/sessionDaemonClient.js.map +1 -1
- package/dist/shared/activeAgentProfile.js +76 -0
- package/dist/shared/activeAgentProfile.js.map +1 -0
- package/dist/shared/apiTypes.d.ts +26 -0
- package/dist/shared/apiTypes.js +2 -0
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +5 -0
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +1 -0
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/piWebConfigParsing.js +0 -5
- package/dist/shared/piWebConfigParsing.js.map +1 -1
- package/dist/shared/piWebStatusParsing.js +6 -0
- package/dist/shared/piWebStatusParsing.js.map +1 -1
- package/dist/shared/thinkingLevels.d.ts +1 -1
- package/dist/shared/thinkingLevels.js +1 -1
- package/dist/shared/thinkingLevels.js.map +1 -1
- package/docs/config.md +63 -9
- package/docs/plugins.md +18 -5
- package/package.json +35 -31
- package/dist/client/assets/CodeViewer-CfZodNYb.js +0 -4
- package/dist/client/assets/TerminalPanel-eua763mA.js +0 -126
- package/dist/client/assets/UnifiedDiffViewer-C9-QeGMj.js +0 -34
- package/dist/client/assets/index-BiFWXoRZ.js +0 -3084
- package/dist/client/assets/vendor-editor-core-C7aWiQy0.js +0 -12
- package/dist/client/assets/vendor-editor-languages-EW34FfVy.js +0 -26
- package/dist/client/assets/vendor-editor-legacy-B4QLsWF8.js +0 -1
- package/dist/client/assets/vendor-terminal-DDGTF8rc.css +0 -1
- package/dist/client/assets/vendor-terminal-DjQ08hXu.js +0 -16
package/docs/config.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# PI WEB configuration reference
|
|
2
2
|
|
|
3
|
-
PI WEB configuration covers the machine-local and project-local settings you usually need: the web/API bind address, trusted development-host settings, UI preferences, plugin enablement, file-explorer path access, manual upload defaults, upload limits, and session-daemon tools.
|
|
3
|
+
PI WEB configuration covers the machine-local and project-local settings you usually need: the web/API bind address, trusted development-host settings, UI preferences, plugin enablement, file-explorer path access, manual upload defaults, upload limits, Pi-compatible agent profiles and companion CLIs, and session-daemon tools.
|
|
4
4
|
|
|
5
5
|
This file is the markdown reference for agents and package consumers. The website page is <https://pi-web.dev/config>.
|
|
6
6
|
|
|
@@ -11,12 +11,25 @@ PI WEB uses two config files:
|
|
|
11
11
|
- **Global PI WEB config:** `$PI_WEB_CONFIG`, or `$XDG_CONFIG_HOME/pi-web/config.json`, or `~/.config/pi-web/config.json`.
|
|
12
12
|
- **Project-local PI WEB config:** `<project>/.pi-web/config.json` for commit-able project settings.
|
|
13
13
|
|
|
14
|
+
Normal-mode provider credentials and custom models are stored separately for each registered project under the managed data directory (`$PI_WEB_DATA_DIR`, or `~/.pi-web`):
|
|
15
|
+
|
|
16
|
+
- Each project has its own managed `auth.json` and `models.json`.
|
|
17
|
+
- All worktrees belonging to the same project share those two files.
|
|
18
|
+
- On first use, PI WEB copies the existing global Pi files from the effective Pi agent directory (by default `~/.pi/agent/auth.json` and `~/.pi/agent/models.json`) into the project store. Later changes are independent; the global files are not a fallback.
|
|
19
|
+
- Normal-mode auth requests and new session/list requests must resolve to a registered project. Existing active sessions keep the project registry they were opened with, even if the project is later closed. Management-embed mode keeps its separate managed credential store.
|
|
20
|
+
|
|
14
21
|
Each PI WEB machine has its own config. When using Fleet/machine federation, Settings uses the selected machine for config that affects work running there: session daemon tools, PI WEB plugin enablement, external path access, and upload defaults. Gateway/browser-only settings stay local to the gateway: keyboard shortcuts, remote machine registry/tokens, and gateway host/port/allowed-hosts. Remote servers that do not advertise selected-machine settings support report those settings as unavailable instead of silently falling back to the gateway.
|
|
15
22
|
|
|
16
23
|
Pi package settings are separate from PI WEB config. They live in Pi's package-manager settings on the target machine and are managed by Pi (`pi install`, `pi remove`, `pi update`) or **Settings → Pi packages**. In a federated setup, **Settings → Pi packages** targets the currently selected machine. The PI WEB `plugins` config key only enables or disables discovered PI WEB browser plugins on the machine whose config you are editing; it does not install, remove, or update Pi packages.
|
|
17
24
|
|
|
18
25
|
If you installed services with a custom config path, rerun `pi-web install --config /path/to/config.json` after changing that path or after upgrading from a version that only applied the custom path to the web service. This regenerates service files so the web/API and session daemon use the same `PI_WEB_CONFIG`.
|
|
19
26
|
|
|
27
|
+
## Reverse-proxy deployment paths
|
|
28
|
+
|
|
29
|
+
The deployment path is not a PI WEB config-file key or environment setting. The published client is portable: one build works at `/` and at canonical trailing-slash prefixes such as `/ai/` or `/test/ai/`.
|
|
30
|
+
|
|
31
|
+
For a nested deployment, redirect the slashless prefix to the trailing-slash URL, strip the prefix before forwarding to PI WEB, and proxy authenticated HTTP and WebSocket traffic through the same location. Relative browser and PWA URLs then stay within that prefix. See the [reverse proxy installation guide](https://pi-web.dev/install#reverse-proxy-prefix) for a complete Nginx example.
|
|
32
|
+
|
|
20
33
|
## Precedence and reloads
|
|
21
34
|
|
|
22
35
|
Machine-global runtime values are resolved as:
|
|
@@ -27,13 +40,13 @@ defaults → global config file → environment overrides
|
|
|
27
40
|
|
|
28
41
|
Supported project-local settings are then applied for that project's workspaces. For upload defaults, `<project>/.pi-web/config.json` overrides the global value.
|
|
29
42
|
|
|
30
|
-
Environment overrides include `PI_WEB_HOST`, `PI_WEB_PORT` / `PORT`, `PI_WEB_ALLOWED_HOSTS`, `PI_WEB_MAX_UPLOAD_BYTES`, `PI_WEB_SPAWN_SESSIONS`, and `PI_WEB_SUBSESSIONS`.
|
|
43
|
+
Environment overrides include `PI_WEB_HOST`, `PI_WEB_PORT` / `PORT`, `PI_WEB_ALLOWED_HOSTS`, `PI_WEB_MAX_UPLOAD_BYTES`, `PI_WEB_AGENT_COMMAND`, `PI_WEB_AGENT_DIR`, `PI_WEB_AGENT_SESSION_DIR`, `PI_CODING_AGENT_DIR` / `PI_CODING_AGENT_SESSION_DIR` for Pi compatibility, `PI_WEB_SPAWN_SESSIONS`, and `PI_WEB_SUBSESSIONS`.
|
|
31
44
|
|
|
32
45
|
Process restarts depend on the key:
|
|
33
46
|
|
|
34
47
|
- `host` / `port`: restart the gateway web/API service or process.
|
|
35
48
|
- `maxUploadBytes`: restart both the web/API process and the session daemon on that machine.
|
|
36
|
-
- `spawnSessions` / `subsessions`: restart the session daemon on that machine.
|
|
49
|
+
- `agent.command` / `agent.dir` / `spawnSessions` / `subsessions`: restart the session daemon on that machine.
|
|
37
50
|
- `pathAccess`: applies on the next request; existing file views may need a browser refresh.
|
|
38
51
|
- `uploads.defaultFolder`: applies to newly opened Files upload dialogs and new direct drag/drop batches after config/workspace refresh.
|
|
39
52
|
- `plugins`: reload the browser tab after changing PI WEB plugin enablement.
|
|
@@ -53,6 +66,10 @@ Process restarts depend on the key:
|
|
|
53
66
|
"defaultFolder": ".pi-web/uploads"
|
|
54
67
|
},
|
|
55
68
|
"maxUploadBytes": 67108864,
|
|
69
|
+
"agent": {
|
|
70
|
+
"command": "pi",
|
|
71
|
+
"dir": "~/agent-profiles/research"
|
|
72
|
+
},
|
|
56
73
|
"spawnSessions": true,
|
|
57
74
|
"subsessions": false,
|
|
58
75
|
"plugins": {
|
|
@@ -91,7 +108,7 @@ Plugins may own separate project files, such as `.pi-web/tasks.json` for the bui
|
|
|
91
108
|
|
|
92
109
|
## Configuration matrix
|
|
93
110
|
|
|
94
|
-
Rows with JSON key `—` are runtime-only environment variables, not config-file keys. `Global` means machine-global. In Settings, selected-machine-safe global keys (`pathAccess`, `uploads`, `maxUploadBytes`, `spawnSessions`, `subsessions`, and `plugins`) are edited for the selected machine; gateway host/port/allowed-hosts, keyboard shortcuts, and machine registry/tokens stay local.
|
|
111
|
+
Rows with JSON key `—` are runtime-only environment variables, not config-file keys. `Global` means machine-global. In Settings, selected-machine-safe global keys (`pathAccess`, `uploads`, `maxUploadBytes`, `agent`, `spawnSessions`, `subsessions`, and `plugins`) are edited for the selected machine; gateway host/port/allowed-hosts, keyboard shortcuts, and machine registry/tokens stay local.
|
|
95
112
|
|
|
96
113
|
| Config | JSON key | Env var | Scope | Project-local behavior | Applies / restart |
|
|
97
114
|
| --- | --- | --- | --- | --- | --- |
|
|
@@ -102,6 +119,8 @@ Rows with JSON key `—` are runtime-only environment variables, not config-file
|
|
|
102
119
|
| External filesystem roots | `pathAccess.allowedPaths` | — | Global + project | **Merges**: global roots first, then project roots; duplicates removed | Next file request; refresh existing views if needed |
|
|
103
120
|
| Manual file upload default folder | `uploads.defaultFolder` | — | Global + project | **Overrides**: project value wins for workspaces in that project; otherwise global/default applies | New Upload dialogs and direct drag/drop batches after config/workspace refresh |
|
|
104
121
|
| Upload/body limit | `maxUploadBytes` | `PI_WEB_MAX_UPLOAD_BYTES` | Global | Not supported locally | Restart web/API and session daemon on that machine |
|
|
122
|
+
| Companion CLI command | `agent.command` | `PI_WEB_AGENT_COMMAND` | Global/session daemon | Not supported locally | Restart session daemon on that machine; affects doctor/status/update checks |
|
|
123
|
+
| Agent profile state directory | `agent.dir` | `PI_WEB_AGENT_DIR` (`PI_CODING_AGENT_DIR` for Pi compatibility) | Global/session daemon | Not supported locally | Restart session daemon on that machine; affects auth, models, settings, sessions, Pi packages, and package-backed plugins |
|
|
105
124
|
| Agent can spawn sessions | `spawnSessions` | `PI_WEB_SPAWN_SESSIONS` | Global/session daemon | Not supported locally | Restart session daemon on that machine |
|
|
106
125
|
| Tracked subsessions (beta) | `subsessions` | `PI_WEB_SUBSESSIONS` | Global/session daemon | Not supported locally; also requires `spawnSessions` | Restart session daemon on that machine |
|
|
107
126
|
| Plugin enablement/settings | `plugins.<id>.enabled`, `plugins.<id>.settings` | — | Global | Not core local config; plugins may read their own project files | Reload browser tab |
|
|
@@ -109,19 +128,25 @@ Rows with JSON key `—` are runtime-only environment variables, not config-file
|
|
|
109
128
|
| Project config version | `version` | — | Project | Project-local only; must be `1` when present | Next project-config read |
|
|
110
129
|
| **Runtime-only environment variables** | | | | | |
|
|
111
130
|
| Global config file path | — | `PI_WEB_CONFIG` (`XDG_CONFIG_HOME` affects the default path) | Process/env | Selects the global config file; not a project config | Restart services/processes after changing env |
|
|
112
|
-
| Managed data directory | — | `PI_WEB_DATA_DIR` | Process/env | Not supported locally | Restart
|
|
131
|
+
| Managed data directory | — | `PI_WEB_DATA_DIR` | Process/env | Not supported locally | Restart web/API and session daemon |
|
|
113
132
|
| Session daemon socket | — | `PI_WEB_SESSIOND_SOCKET` | Web/API + session daemon env | Not supported locally | Restart daemon and web/API; both must match |
|
|
114
133
|
| Session daemon TCP port | — | `PI_WEB_SESSIOND_PORT` | Session daemon env | Not supported locally | Restart session daemon; set `PI_WEB_SESSIOND_URL` for web/API too |
|
|
115
134
|
| Session daemon TCP host | — | `PI_WEB_SESSIOND_HOST` | Session daemon env | Not supported locally | Restart session daemon |
|
|
116
135
|
| Web-to-daemon URL | — | `PI_WEB_SESSIOND_URL` | Web/API env | Not supported locally | Restart web/API |
|
|
117
136
|
| Projects storage file | — | `PI_WEB_PROJECTS_FILE` | Web/API + session daemon env | Not supported locally | Restart services; advanced state override |
|
|
118
137
|
| Remote machines storage file | — | `PI_WEB_MACHINES_FILE` | Web/API env | Not supported locally | Restart web/API; advanced state override |
|
|
119
|
-
|
|
|
120
|
-
|
|
|
138
|
+
| Agent profile session storage directory | — | `PI_WEB_AGENT_SESSION_DIR` (`PI_CODING_AGENT_SESSION_DIR` for Pi compatibility) | Session daemon env | Not supported locally | Restart session daemon; env-only session storage override |
|
|
139
|
+
| Agent profile state directory | — | `PI_WEB_AGENT_DIR` (`PI_CODING_AGENT_DIR` for Pi compatibility) | Web/API + session daemon env | Not supported locally | Restart services |
|
|
121
140
|
| Skip update checks | — | `PI_WEB_SKIP_VERSION_CHECK`, `PI_WEB_OFFLINE`, `PI_SKIP_VERSION_CHECK`, `PI_OFFLINE` | Web/API env | Not supported locally | Restart web/API after env changes |
|
|
122
141
|
|
|
123
142
|
## Key details
|
|
124
143
|
|
|
144
|
+
### Managed data directory
|
|
145
|
+
|
|
146
|
+
`PI_WEB_DATA_DIR` sets the root for PI WEB-managed runtime state and defaults to `~/.pi-web`. Unless a more specific path override is configured, PI WEB stores its project and machine registries, locally discovered plugins, default session-daemon socket, and session archives beneath this root.
|
|
147
|
+
|
|
148
|
+
This setting does not change the PI WEB config file selected by `PI_WEB_CONFIG` or Pi-owned state such as the active session files selected by `PI_CODING_AGENT_SESSION_DIR`.
|
|
149
|
+
|
|
125
150
|
### External path access
|
|
126
151
|
|
|
127
152
|
`pathAccess.allowedPaths` grants PI WEB's file explorer and absolute `@` path completions access to specific filesystem roots outside the current workspace.
|
|
@@ -165,13 +190,42 @@ For machine federation, Settings saves the global upload default on the selected
|
|
|
165
190
|
|
|
166
191
|
The per-request size limit is still controlled by `maxUploadBytes` / `PI_WEB_MAX_UPLOAD_BYTES` on the machine serving the upload.
|
|
167
192
|
|
|
193
|
+
### Pi-compatible agent profile and companion CLI
|
|
194
|
+
|
|
195
|
+
`agent.command` selects the Pi-compatible companion CLI used by `pi-web doctor` and, when it can be generated safely, package-managed update commands. It defaults to `pi`. This setting does **not** replace the embedded runtime: every session continues to use PI WEB's bundled Pi SDK.
|
|
196
|
+
|
|
197
|
+
`agent.dir` selects the Pi-compatible state profile used for auth providers, models, settings, sessions, Pi packages, and Pi-package-backed PI WEB plugin discovery. It defaults to `~/.pi/agent` only for a canonical Pi companion command. The directory must use the data layout supported by the bundled Pi SDK; PI WEB does not load or convert incompatible fork formats, migrate profile data, or repartition PI WEB-managed archives when the profile changes.
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"agent": {
|
|
202
|
+
"command": "pi-lab",
|
|
203
|
+
"dir": "/opt/pi-profiles/lab"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
An alternate command always requires an explicit state directory. The command must be a safe bare executable name such as `pi-lab` or a host-absolute executable path such as `/opt/pi/bin/pi`; relative paths, shell expressions, and launcher strings are rejected. The state directory must be host-absolute or start with `~`. In a federated save, the gateway transports Unix and Windows absolute paths without reinterpreting them, and the target machine validates and returns the persisted profile.
|
|
209
|
+
|
|
210
|
+
Environment variables take precedence over the config file. `PI_WEB_AGENT_COMMAND` selects the companion CLI, `PI_WEB_AGENT_DIR` sets the profile state directory, and `PI_WEB_AGENT_SESSION_DIR` overrides session storage separately from `agent.dir`. The legacy `PI_CODING_AGENT_DIR` and `PI_CODING_AGENT_SESSION_DIR` names apply only to a canonical Pi companion command; PI WEB never derives ambient environment-variable names from an arbitrary command. Use the explicit `PI_WEB_AGENT_*` names for alternate commands. `PI_WEB_AGENT_DIR` is an unconditional override, while a legacy `PI_CODING_AGENT_DIR` override stops applying when Settings selects an alternate command so the command and directory can transition together.
|
|
211
|
+
|
|
212
|
+
The session daemon resolves the persisted desired values plus its environment once at startup. That secret-free active profile stays fixed for the daemon lifetime. **Settings → Session daemon** saves command and directory together as desired configuration and shows whether the profile is active, needs a restart, or cannot be compared. Until the daemon restarts, sessions, Pi package operations, package-backed plugin discovery, status/install detection, and update planning continue to use the daemon-owned active profile; a web/API restart recovers that same active profile instead of applying the newly saved values.
|
|
213
|
+
|
|
214
|
+
If the session daemon cannot report a valid active profile, profile-dependent package and plugin operations report unavailable instead of falling back to independently resolved config. A package-managed update command is shown only when PI WEB can preserve the active profile with a recognized, safe Pi companion CLI; otherwise the command is omitted. Remote profile editing likewise requires advertised support, and the gateway rejects a remote save if the target does not return the requested profile. Restart the session daemon on the selected machine to establish the next active profile.
|
|
215
|
+
|
|
168
216
|
### Session daemon tools
|
|
169
217
|
|
|
170
218
|
`spawnSessions` controls whether agents receive the `spawn_session` tool. It defaults to `true`; set it to `false` if you do not want an agent to start independent PI WEB sessions.
|
|
171
219
|
|
|
172
|
-
`subsessions` is beta and controls whether agents receive the tracked-subsession tools: `spawn_subsession`, `list_subsessions`, `check_subsession`, and `
|
|
220
|
+
`subsessions` is beta and controls whether agents receive the tracked-subsession tools: `spawn_subsession`, `list_subsessions`, `check_subsession`, `read_subsession`, and `yield_to_subsessions`. It defaults to `false` and also requires `spawnSessions` to be enabled.
|
|
221
|
+
|
|
222
|
+
Tracked subsessions are join-oriented. Calling `spawn_subsession` returns immediately, so the parent can continue independent work while the child runs. Work whose result the parent does not need to join belongs in the fire-and-forget `spawn_session` tool instead.
|
|
223
|
+
|
|
224
|
+
At a join point, after finishing its independent work, the parent calls `yield_to_subsessions` alone as the final action in its tool batch. Pi ends a tool batch early only when every result in that batch is terminating. If any tracked child is still working, the action ends the current agent run so the parent becomes idle. If none are working, it does not end the run and clearly reports that there is nothing to wait for.
|
|
225
|
+
|
|
226
|
+
A completion notice wakes an idle parent or queues behind in-flight work. Each notice lists any other tracked children still working, so the parent can continue work or call `yield_to_subsessions` again at the next join point. Further notices arrive automatically; do not poll.
|
|
173
227
|
|
|
174
|
-
|
|
228
|
+
`list_subsessions`, `check_subsession`, and `read_subsession` never yield or change control flow. They are for deliberate inspection or recovery, not completion polling. While a child works, agent-facing `check_subsession` and `read_subsession` withhold partial output and direct the parent to continue independent work or yield at the join point. Output becomes available when the child stops. In notices and inspection results, PI WEB guidance precedes a labeled marker and the child output or transcript always comes last.
|
|
175
229
|
|
|
176
230
|
In **Settings → Session daemon**, these keys are saved on the selected machine. Restart the session daemon on that machine after changing them.
|
|
177
231
|
|
package/docs/plugins.md
CHANGED
|
@@ -202,9 +202,11 @@ Built-in plugins can be managed from **Settings → PI WEB plugins** or with the
|
|
|
202
202
|
### Updates
|
|
203
203
|
|
|
204
204
|
**Plugin id:** `updates`
|
|
205
|
-
**What it does:** adds a conditional **Updates** workspace tab with PI WEB update, restart, and installed-service guidance.
|
|
205
|
+
**What it does:** adds a conditional **Updates** workspace tab with PI WEB update, restart, and installed-service guidance, plus a **Check for PI WEB Updates** action for the selected machine.
|
|
206
206
|
|
|
207
|
-
|
|
207
|
+
While a browser tab is connected, PI WEB refreshes the selected machine's status every 15 minutes. npm release lookups are cached on that machine for six hours, so the automatic refresh normally contacts npm at most once in that window. Run **Check for PI WEB Updates** from the action palette to bypass both caches and check immediately. Operator settings that skip remote version checks, such as `PI_WEB_OFFLINE`, are still respected.
|
|
208
|
+
|
|
209
|
+
Updates is enabled by default. It declares `machineSpecific: true` so the gateway Updates tab and action only appear for the local machine; while a remote machine is selected, that remote machine's Updates plugin is used if available. To hide it, disable `updates` in **Settings → PI WEB plugins** or set:
|
|
208
210
|
|
|
209
211
|
```json
|
|
210
212
|
{
|
|
@@ -323,7 +325,7 @@ Rules:
|
|
|
323
325
|
|
|
324
326
|
### Manifest and assets
|
|
325
327
|
|
|
326
|
-
The manifest contains each discovered plugin module:
|
|
328
|
+
The manifest contains each discovered plugin module. Current PI WEB releases emit `module` as a leading application-root reference:
|
|
327
329
|
|
|
328
330
|
```json
|
|
329
331
|
{
|
|
@@ -339,15 +341,25 @@ The manifest contains each discovered plugin module:
|
|
|
339
341
|
}
|
|
340
342
|
```
|
|
341
343
|
|
|
344
|
+
The browser maps leading application-root references into the current application base, so the same manifest works at the origin root or under a reverse-proxy path prefix. Keeping this output format also lets gateways from existing PI WEB releases consume plugins from an upgraded remote machine. For compatibility, federated gateways additionally accept explicit manifest-relative references such as `./my-plugin/pi-web-plugin.js` and legacy plugin-root-relative references such as `nested/pi-web-plugin.js`; all accepted forms are rewritten to deployment-portable, gateway-relative references.
|
|
345
|
+
|
|
342
346
|
`source` describes where the plugin came from (`bundled`, `local`, or the Pi package source). `scope` is `bundled`, `local`, `user`, or `project`. `machineSpecific` controls whether the gateway copy is valid for remote machines or only each selected machine's own copy can appear.
|
|
343
347
|
|
|
344
|
-
|
|
348
|
+
At an origin-root deployment, a plugin's static assets are available under:
|
|
345
349
|
|
|
346
350
|
```text
|
|
347
351
|
/pi-web-plugins/<plugin-id>/<path-inside-plugin-root>
|
|
348
352
|
```
|
|
349
353
|
|
|
350
|
-
|
|
354
|
+
Prefer module-relative asset URLs so they also work for remote machine plugins. For example, a built plugin module can reference an SVG shipped beside it:
|
|
355
|
+
|
|
356
|
+
```js
|
|
357
|
+
const iconUrl = new URL("./assets/icon.svg", import.meta.url);
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
The final installed plugin package must contain `assets/icon.svg` at that path relative to the final built module. PI WEB serves files that already exist in the package; it does not copy a source `public/` directory or apply Vite-style public-directory semantics. Configure the plugin build and package contents to emit or copy the asset into its final module-relative location.
|
|
361
|
+
|
|
362
|
+
PI WEB prevents asset path traversal outside the plugin root. JavaScript, JSON, CSS, HTML, and SVG files get appropriate content types; unknown file types are served as octet-stream.
|
|
351
363
|
|
|
352
364
|
## Plugin module shape
|
|
353
365
|
|
|
@@ -466,6 +478,7 @@ interface PluginRuntimeContext {
|
|
|
466
478
|
openTerminal: (options?: { terminalId?: string }) => void;
|
|
467
479
|
refreshFiles: () => void | Promise<void>;
|
|
468
480
|
refreshGit: () => void | Promise<void>;
|
|
481
|
+
checkForPiWebUpdates?: () => void | Promise<void>;
|
|
469
482
|
startSession: () => void | Promise<void>;
|
|
470
483
|
archiveSession: () => void | Promise<void>;
|
|
471
484
|
stopActiveWork: () => void | Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chainingintention/pi-web-cn",
|
|
3
|
-
"version": "1.202607.
|
|
3
|
+
"version": "1.202607.6",
|
|
4
4
|
"description": "Remote web UI and browser control plane for persistent Pi Coding Agent sessions.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Federico Jaramillo Martinez",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"dist",
|
|
15
|
+
"!dist/**/*.testSupport.*",
|
|
15
16
|
"install.sh",
|
|
16
17
|
"README.md",
|
|
17
18
|
"LICENSE",
|
|
@@ -29,18 +30,20 @@
|
|
|
29
30
|
"dev:server": "npm run dev:web",
|
|
30
31
|
"dev:client": "vite --host 0.0.0.0",
|
|
31
32
|
"dev:plugins": "node scripts/build-plugins.mjs --watch",
|
|
32
|
-
"build": "tsc -p tsconfig.build.json && npm run build:plugin-api && npm run build:plugins && vite build",
|
|
33
|
+
"build": "npm run clean && tsc -p tsconfig.build.json && npm run build:plugin-api && npm run build:plugins && vite build",
|
|
33
34
|
"build:plugin-api": "tsc -p tsconfig.plugin-api.json",
|
|
34
35
|
"build:plugins": "tsc -p tsconfig.plugins.json && node scripts/build-plugins.mjs",
|
|
35
36
|
"capture:screenshots": "node scripts/capture-screenshots.mjs",
|
|
36
37
|
"typecheck": "tsc --noEmit",
|
|
38
|
+
"typecheck:cached": "tsc --noEmit --incremental --tsBuildInfoFile node_modules/.cache/pi-web/typecheck.tsbuildinfo",
|
|
37
39
|
"knip": "knip",
|
|
38
40
|
"lint": "eslint \"src/**/*.ts\" \"extensions/**/*.ts\" \"pi-web-plugins/**/*.ts\" vite.config.ts vitest.config.ts",
|
|
39
41
|
"test": "vitest run --config vitest.config.ts",
|
|
40
42
|
"verify": "npm run typecheck && npm run lint && npm run knip && npm test",
|
|
43
|
+
"verify:staged": "node scripts/verify-staged.mjs",
|
|
41
44
|
"start": "tsx src/server/index.ts",
|
|
42
45
|
"start:sessiond": "tsx src/server/sessiond.ts",
|
|
43
|
-
"clean": "
|
|
46
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
44
47
|
"prepack": "npm run build",
|
|
45
48
|
"pack:dry": "npm pack --dry-run",
|
|
46
49
|
"prepublishOnly": "npm run verify",
|
|
@@ -51,7 +54,7 @@
|
|
|
51
54
|
"changelog:status": "changeset status"
|
|
52
55
|
},
|
|
53
56
|
"dependencies": {
|
|
54
|
-
"@codemirror/commands": "^6.10.
|
|
57
|
+
"@codemirror/commands": "^6.10.4",
|
|
55
58
|
"@codemirror/lang-css": "^6.3.1",
|
|
56
59
|
"@codemirror/lang-go": "^6.0.1",
|
|
57
60
|
"@codemirror/lang-html": "^6.4.11",
|
|
@@ -60,38 +63,39 @@
|
|
|
60
63
|
"@codemirror/lang-markdown": "^6.5.0",
|
|
61
64
|
"@codemirror/lang-python": "^6.2.1",
|
|
62
65
|
"@codemirror/lang-rust": "^6.0.2",
|
|
63
|
-
"@codemirror/language": "^6.12.
|
|
64
|
-
"@codemirror/legacy-modes": "^6.5.
|
|
65
|
-
"@codemirror/state": "^6.
|
|
66
|
-
"@codemirror/view": "^6.
|
|
67
|
-
"@fastify/
|
|
68
|
-
"@fastify/
|
|
66
|
+
"@codemirror/language": "^6.12.4",
|
|
67
|
+
"@codemirror/legacy-modes": "^6.5.3",
|
|
68
|
+
"@codemirror/state": "^6.7.1",
|
|
69
|
+
"@codemirror/view": "^6.43.6",
|
|
70
|
+
"@fastify/compress": "^9.0.0",
|
|
71
|
+
"@fastify/static": "^9.3.0",
|
|
72
|
+
"@fastify/websocket": "^11.3.0",
|
|
69
73
|
"@xterm/addon-fit": "^0.11.0",
|
|
70
74
|
"@xterm/xterm": "^6.0.0",
|
|
71
|
-
"diff": "^
|
|
72
|
-
"fastify": "^5.
|
|
73
|
-
"lit": "^3.3.
|
|
74
|
-
"marked": "^18.0.
|
|
75
|
+
"diff": "^9.0.0",
|
|
76
|
+
"fastify": "^5.10.0",
|
|
77
|
+
"lit": "^3.3.3",
|
|
78
|
+
"marked": "^18.0.6",
|
|
75
79
|
"node-pty": "^1.1.0",
|
|
76
|
-
"typebox": "1.
|
|
77
|
-
"ws": "^8.
|
|
80
|
+
"typebox": "1.3.6",
|
|
81
|
+
"ws": "^8.21.0"
|
|
78
82
|
},
|
|
79
83
|
"devDependencies": {
|
|
80
84
|
"@changesets/cli": "^2.31.0",
|
|
81
|
-
"@earendil-works/pi-agent-core": "^0.80.
|
|
82
|
-
"@earendil-works/pi-ai": "^0.80.
|
|
83
|
-
"@earendil-works/pi-coding-agent": "^0.80.
|
|
85
|
+
"@earendil-works/pi-agent-core": "^0.80.10",
|
|
86
|
+
"@earendil-works/pi-ai": "^0.80.10",
|
|
87
|
+
"@earendil-works/pi-coding-agent": "^0.80.10",
|
|
84
88
|
"@eslint/js": "^10.0.1",
|
|
85
|
-
"@types/node": "^24.
|
|
89
|
+
"@types/node": "^24.13.3",
|
|
86
90
|
"@types/ws": "^8.18.1",
|
|
87
|
-
"eslint": "^10.
|
|
88
|
-
"globals": "^17.
|
|
89
|
-
"knip": "^6.
|
|
90
|
-
"tsx": "^4.
|
|
91
|
-
"typescript": "^
|
|
92
|
-
"typescript-eslint": "^8.
|
|
93
|
-
"vite": "^
|
|
94
|
-
"vitest": "^4.1.
|
|
91
|
+
"eslint": "^10.6.0",
|
|
92
|
+
"globals": "^17.7.0",
|
|
93
|
+
"knip": "^6.25.0",
|
|
94
|
+
"tsx": "^4.23.0",
|
|
95
|
+
"typescript": "^6.0.3",
|
|
96
|
+
"typescript-eslint": "^8.63.0",
|
|
97
|
+
"vite": "^8.1.4",
|
|
98
|
+
"vitest": "^4.1.10"
|
|
95
99
|
},
|
|
96
100
|
"publishConfig": {
|
|
97
101
|
"access": "public"
|
|
@@ -109,9 +113,9 @@
|
|
|
109
113
|
"homepage": "https://github.com/nayuto-wakusei/pi-web-ChineseTranslation#readme",
|
|
110
114
|
"packageManager": "npm@11.11.0",
|
|
111
115
|
"peerDependencies": {
|
|
112
|
-
"@earendil-works/pi-agent-core": ">=0.80.
|
|
113
|
-
"@earendil-works/pi-ai": ">=0.80.
|
|
114
|
-
"@earendil-works/pi-coding-agent": ">=0.80.
|
|
116
|
+
"@earendil-works/pi-agent-core": ">=0.80.10 <1",
|
|
117
|
+
"@earendil-works/pi-ai": ">=0.80.10 <1",
|
|
118
|
+
"@earendil-works/pi-coding-agent": ">=0.80.10 <1"
|
|
115
119
|
},
|
|
116
120
|
"keywords": [
|
|
117
121
|
"pi-package",
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{v as r,J as d,$ as h,A as m,Y as g,U as f,a0 as v,V as b}from"./vendor-editor-core-C7aWiQy0.js";import{g as y,r as w,p as x,h as C,c as j,m as k,j as E,a as l}from"./vendor-editor-languages-EW34FfVy.js";import{d as H}from"./vendor-editor-legacy-B4QLsWF8.js";import{i as _,b as O,a as S,n as p,f as P,t as T}from"./index-BiFWXoRZ.js";var A=Object.defineProperty,L=Object.getOwnPropertyDescriptor,i=(e,s,n,a)=>{for(var t=a>1?void 0:a?L(s,n):s,c=e.length-1,u;c>=0;c--)(u=e[c])&&(t=(a?u(s,n,t):u(t))||t);return a&&t&&A(s,n,t),t};let o=class extends _{constructor(){super(...arguments),this.content=""}firstUpdated(){this.recreateEditor()}updated(e){(e.has("content")||e.has("language"))&&this.recreateEditor()}disconnectedCallback(){this.view?.destroy(),this.view=void 0,super.disconnectedCallback()}render(){return O`<div class="host"></div>`}recreateEditor(){this.editorHost&&(this.view?.destroy(),this.view=new r({parent:this.editorHost,state:d.create({doc:this.content,extensions:[h(),m.of(g),f(b,{fallback:!0}),d.readOnly.of(!0),r.editable.of(!1),r.lineWrapping,V,...F(this.language),...M(this.language)]})}))}};o.styles=S`
|
|
2
|
-
:host { display: block; min-height: 0; height: 100%; }
|
|
3
|
-
.host { height: 100%; min-height: 0; overflow: auto; }
|
|
4
|
-
`;i([p()],o.prototype,"content",2);i([p()],o.prototype,"language",2);i([P(".host")],o.prototype,"editorHost",2);o=i([T("code-viewer")],o);const V=r.theme({"&":{height:"100%",color:"var(--pi-text)",backgroundColor:"var(--pi-bg)",fontSize:"12px"},".cm-scroller":{fontFamily:"ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",lineHeight:"1.45"},".cm-gutters":{backgroundColor:"var(--pi-bg)",color:"var(--pi-dim)",borderRight:"1px solid var(--pi-border-muted)"},".cm-activeLineGutter":{backgroundColor:"transparent"},".cm-activeLine":{backgroundColor:"transparent"},".cm-content":{caretColor:"transparent"},"&.cm-focused":{outline:"none"}}),D=r.theme({".cm-content":{textAlign:"start"},".cm-line":{unicodeBidi:"plaintext"}});function F(e){return e==="markdown"?[r.contentAttributes.of({dir:"auto"}),D]:[]}function M(e){if(e===void 0)return[];switch(e){case"typescript":return[l({typescript:!0})];case"javascript":return[l()];case"json":return[E()];case"markdown":return[k()];case"css":return[j()];case"html":return[C()];case"python":return[x()];case"rust":return[w()];case"go":return[y()];case"diff":return[v.define(H)];default:return[]}}export{o as CodeViewer};
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import{a as E,n as m,i as K,b as l,t as R,c as h,s as D,d as w,e as _,f as F,r as c}from"./index-BiFWXoRZ.js";import{D as T,o as S}from"./vendor-terminal-DjQ08hXu.js";import"./vendor-editor-core-C7aWiQy0.js";import"./vendor-editor-languages-EW34FfVy.js";const $="pi-web.terminal.softKeys",N="(pointer: coarse), (max-width: 760px)";function z(e,t){return e??t}function V(e){if(e==="true")return!0;if(e==="false")return!1}function P(){return typeof window<"u"&&"matchMedia"in window?window.matchMedia(N):void 0}function M(e){return e?.matches===!0}function j(e=P()){return z(O(),M(e))}function B(){return O()!==void 0}function O(e=A()){if(e!==void 0)try{return V(e.getItem($))}catch{return}}function H(e,t=A()){if(t!==void 0)try{t.setItem($,String(e))}catch{}}function A(){if(!(typeof window>"u"))try{return window.localStorage}catch{return}}const U=[{id:"escape",label:"Esc",ariaLabel:"Escape",title:"发送 Escape"},{id:"tab",label:"Tab",ariaLabel:"Tab",title:"发送 Tab"},{id:"ctrl-c",label:"Ctrl+C",ariaLabel:"Control C",title:"中断前台进程"},{id:"ctrl-d",label:"Ctrl+D",ariaLabel:"Control D",title:"发送 EOF / 关闭输入"},{id:"ctrl-z",label:"Ctrl+Z",ariaLabel:"Control Z",title:"挂起前台进程"},{id:"ctrl-l",label:"Ctrl+L",ariaLabel:"Control L",title:"清屏 / 重绘终端"},{id:"ctrl-r",label:"Ctrl+R",ariaLabel:"Control R",title:"反向搜索历史"},{id:"ctrl-u",label:"Ctrl+U",ariaLabel:"Control U",title:"删除到行首"},{id:"ctrl-w",label:"Ctrl+W",ariaLabel:"Control W",title:"删除上一个单词"},{id:"arrow-left",label:"←",ariaLabel:"左箭头",title:"向左移动"},{id:"arrow-up",label:"↑",ariaLabel:"上箭头",title:"向上移动 / 上一条命令"},{id:"arrow-down",label:"↓",ariaLabel:"下箭头",title:"向下移动 / 下一条命令"},{id:"arrow-right",label:"→",ariaLabel:"右箭头",title:"向右移动"},{id:"home",label:"Home",ariaLabel:"Home",title:"移动到开头"},{id:"end",label:"End",ariaLabel:"End",title:"移动到末尾"},{id:"page-up",label:"PgUp",ariaLabel:"向上翻页",title:"向上翻页"},{id:"page-down",label:"PgDn",ariaLabel:"向下翻页",title:"向下翻页"},{id:"delete",label:"Del",ariaLabel:"Delete",title:"向前删除"},{id:"backspace",label:"⌫",ariaLabel:"Backspace",title:"退格"},{id:"meta-backward-word",label:"M-B",ariaLabel:"Meta B",title:"向后移动一个单词"},{id:"meta-forward-word",label:"M-F",ariaLabel:"Meta F",title:"向前移动一个单词"}],d="\x1B",q="";function Y(e,t){switch(e){case"escape":return d;case"tab":return" ";case"ctrl-c":return p("c");case"ctrl-d":return p("d");case"ctrl-z":return p("z");case"ctrl-l":return p("l");case"ctrl-r":return p("r");case"ctrl-u":return p("u");case"ctrl-w":return p("w");case"arrow-left":return b("D",t);case"arrow-up":return b("A",t);case"arrow-down":return b("B",t);case"arrow-right":return b("C",t);case"home":return x("H",t);case"end":return x("F",t);case"page-up":return`${d}[5~`;case"page-down":return`${d}[6~`;case"delete":return`${d}[3~`;case"backspace":return q;case"meta-backward-word":return`${d}b`;case"meta-forward-word":return`${d}f`}}function p(e){return String.fromCharCode(e.toUpperCase().charCodeAt(0)-64)}function b(e,t){return t?.applicationCursorKeysMode===!0?`${d}O${e}`:`${d}[${e}`}function x(e,t){return t?.applicationCursorKeysMode===!0?`${d}O${e}`:`${d}[${e}`}var W=Object.defineProperty,X=Object.getOwnPropertyDescriptor,y=(e,t,i,n)=>{for(var r=n>1?void 0:n?X(t,i):t,a=e.length-1,u;a>=0;a--)(u=e[a])&&(r=(n?u(t,i,r):u(r))||r);return n&&r&&W(t,i,r),r};const J=8,Z=500;let f=class extends K{constructor(){super(...arguments),this.refocusOnClick=!0,this.onInput=()=>{},this.lastPointerFinishedAt=0}sendSoftKey(e,t){this.onInput(Y(e.id,this.modes),t)}onSoftKeyPointerDown(e,t){e.pointerType==="mouse"&&e.button!==0||(e.preventDefault(),this.pointerStart={pointerId:e.pointerId,key:t,clientX:e.clientX,clientY:e.clientY})}onSoftKeyPointerMove(e){const t=this.pointerStart;t?.pointerId===e.pointerId&&I(t,e)&&this.finishSoftKeyPointer()}onSoftKeyPointerUp(e,t){const i=this.pointerStart;i?.pointerId===e.pointerId&&(e.preventDefault(),this.finishSoftKeyPointer(),!(i.key.id!==t.id||I(i,e))&&this.sendSoftKey(t,{refocus:e.pointerType==="mouse"}))}onSoftKeyPointerCancel(e){this.pointerStart?.pointerId===e.pointerId&&this.finishSoftKeyPointer()}finishSoftKeyPointer(){this.pointerStart=void 0,this.lastPointerFinishedAt=Date.now()}onSoftKeyClick(e,t){if(Date.now()-this.lastPointerFinishedAt<Z){e.preventDefault();return}this.sendSoftKey(t,{refocus:this.refocusOnClick})}render(){return l`
|
|
2
|
-
<div class="terminal-soft-keys" role="toolbar" aria-label="终端快捷键">
|
|
3
|
-
${U.map(e=>l`
|
|
4
|
-
<button
|
|
5
|
-
type="button"
|
|
6
|
-
class="soft-key"
|
|
7
|
-
title=${e.title}
|
|
8
|
-
aria-label=${e.ariaLabel}
|
|
9
|
-
@pointerdown=${t=>{this.onSoftKeyPointerDown(t,e)}}
|
|
10
|
-
@pointermove=${t=>{this.onSoftKeyPointerMove(t)}}
|
|
11
|
-
@pointerup=${t=>{this.onSoftKeyPointerUp(t,e)}}
|
|
12
|
-
@pointercancel=${t=>{this.onSoftKeyPointerCancel(t)}}
|
|
13
|
-
@click=${t=>{this.onSoftKeyClick(t,e)}}
|
|
14
|
-
>${e.label}</button>
|
|
15
|
-
`)}
|
|
16
|
-
</div>
|
|
17
|
-
`}};f.styles=E`
|
|
18
|
-
:host { flex: 0 0 auto; display: block; }
|
|
19
|
-
.terminal-soft-keys { display: flex; gap: 6px; align-items: center; padding: 6px; border-bottom: 1px solid var(--pi-border-muted); background: var(--pi-bg); overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; touch-action: pan-x; }
|
|
20
|
-
.terminal-soft-keys::-webkit-scrollbar { display: none; }
|
|
21
|
-
button { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; max-width: none; min-height: 34px; border: 1px solid var(--pi-border); border-radius: 7px; background: var(--pi-surface); color: var(--pi-text); padding: 6px 9px; font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; cursor: pointer; touch-action: pan-x; -webkit-touch-callout: none; user-select: none; }
|
|
22
|
-
button:disabled { opacity: .5; cursor: not-allowed; }
|
|
23
|
-
`;y([m({attribute:!1})],f.prototype,"modes",2);y([m({type:Boolean})],f.prototype,"refocusOnClick",2);y([m({attribute:!1})],f.prototype,"onInput",2);f=y([R("terminal-soft-keys")],f);function I(e,t){return Math.hypot(t.clientX-e.clientX,t.clientY-e.clientY)>J}var G=Object.defineProperty,Q=Object.getOwnPropertyDescriptor,s=(e,t,i,n)=>{for(var r=n>1?void 0:n?Q(t,i):t,a=e.length-1,u;a>=0;a--)(u=e[a])&&(r=(n?u(t,i,r):u(r))||r);return n&&r&&G(t,i,r),r};const ee={cursorBlink:!0,convertEol:!0,fontFamily:"ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",fontSize:13},te={cols:100,rows:30},ie=1e3;let o=class extends K{constructor(){super(...arguments),this.machineId="local",this.autoStart=!1,this.onSelectTerminal=()=>{},this.terminals=[],this.commandRuns=[],this.loading=!1,this.visible=!1,this.cancellingRunIds=[],this.continuingTerminalIds=[],this.defaultSoftKeysEnvironment=!1,this.softKeysEnabled=j(),this.suppressTerminalInput=!1,this.softKeysDefaultEnvironmentMedia=P(),this.softKeysPreferenceStored=B(),this.onSoftKeysDefaultEnvironmentChange=()=>{this.syncDefaultSoftKeysEnvironment()}}connectedCallback(){super.connectedCallback(),this.syncDefaultSoftKeysEnvironment(),this.softKeysDefaultEnvironmentMedia?.addEventListener("change",this.onSoftKeysDefaultEnvironmentChange),this.themeObserver=new MutationObserver(()=>{this.applyTerminalTheme()}),this.themeObserver.observe(document.documentElement,{attributes:!0,attributeFilter:["class","style","data-theme"]})}firstUpdated(){this.intersectionObserver=new IntersectionObserver(e=>{this.visible=e[0]?.isIntersecting===!0}),this.intersectionObserver.observe(this)}disconnectedCallback(){this.intersectionObserver?.disconnect(),this.intersectionObserver=void 0,this.themeObserver?.disconnect(),this.themeObserver=void 0,this.softKeysDefaultEnvironmentMedia?.removeEventListener("change",this.onSoftKeysDefaultEnvironmentChange),this.updateCommandRunPolling(!1),this.disposeTerminalView(),super.disconnectedCallback()}syncDefaultSoftKeysEnvironment(){const e=M(this.softKeysDefaultEnvironmentMedia),t=this.softKeysEnabled;this.defaultSoftKeysEnvironment=e,this.softKeysPreferenceStored||(this.softKeysEnabled=e),this.softKeysEnabled!==t&&this.scheduleFitAndNotify()}scheduleFitAndNotify(){this.updateComplete.then(()=>{this.fitAndNotify()})}willUpdate(e){const t=this.workspace===void 0?void 0:JSON.stringify([this.machineId,this.workspace.path]);if(t!==this.observedWorkspaceScope){this.observedWorkspaceScope=t,this.loadedCwd=void 0,this.autoStartConsumedCwd=void 0,this.terminals=[],this.commandRuns=[],this.selectedId=void 0,this.cancellingRunIds=[],this.continuingTerminalIds=[],this.updateCommandRunPolling(!1),this.disposeTerminalView();return}if(e.has("selectedTerminalId")){if(e.get("selectedTerminalId")!==void 0&&this.selectedTerminalId===void 0){this.loadedCwd=void 0,this.selectTerminalIdInView(void 0);return}this.applyRequestedTerminalSelection()}}updated(e){this.visible?this.hasPendingCommandRuns()&&this.updateCommandRunPolling(!0):this.updateCommandRunPolling(!1),this.loadVisibleWorkspaceTerminals(),e.has("selectedTerminalId")&&this.shouldReloadForRequestedTerminal()&&this.loadTerminals(),this.ensureTerminalView()}loadVisibleWorkspaceTerminals(){const e=this.workspace?.path;!this.visible||e===void 0||e===this.loadedCwd||(this.loadedCwd=e,this.loadTerminals())}async loadTerminals(){this.loading=!0,this.error=void 0;try{const e=this.workspace;if(e===void 0)return;const t=this.consumeAutoStart(),[i,n]=await Promise.all([h.terminals(e.projectId,e.id,this.machineId),h.listCommandRuns({projectId:e.projectId,workspaceId:e.id},this.machineId)]);this.terminals=i,this.commandRuns=n,this.selectPreferredLoadedTerminal({replaceUrl:!0}),this.updateCommandRunPolling(this.hasPendingCommandRuns(n)),i.length===0&&t&&await this.startTerminal()}catch(e){this.error=e instanceof Error?e.message:String(e)}finally{this.loading=!1}}applyRequestedTerminalSelection(){this.selectedTerminalId!==void 0&&!this.terminals.some(e=>e.id===this.selectedTerminalId)||this.selectPreferredLoadedTerminal({replaceUrl:!0})}consumeAutoStart(){const e=this.workspace?.path;return!this.autoStart||e===void 0||this.autoStartConsumedCwd===e?!1:(this.autoStartConsumedCwd=e,!0)}shouldReloadForRequestedTerminal(){const e=this.workspace?.path;return this.visible&&e!==void 0&&e===this.loadedCwd&&this.selectedTerminalId!==void 0&&!this.loading&&!this.terminals.some(t=>t.id===this.selectedTerminalId)}selectPreferredLoadedTerminal(e){let t=D(this.terminals,{targetTerminalId:this.selectedTerminalId});t===void 0&&this.selectedTerminalId!==void 0&&(t=w(this.terminals)),this.selectTerminalIdInView(t?.id),(t?.id!==this.selectedTerminalId||t===void 0&&this.selectedTerminalId!==void 0)&&this.onSelectTerminal(t?.id,{replace:e?.replaceUrl===!0})}selectTerminalIdInView(e){this.selectedId!==e&&(this.selectedId=e,this.disposeTerminalView())}async startTerminal(){if(this.workspace!==void 0){this.error=void 0;try{const e=this.measureTerminalSize()??te,t=await h.startTerminal(this.workspace.projectId,this.workspace.id,e,this.machineId);this.terminals=[...this.terminals,t],this.selectTerminal(t.id)}catch(e){this.error=e instanceof Error?e.message:String(e)}}}async closeTerminal(e,t){t.stopPropagation();try{if(this.workspace===void 0)return;await h.closeTerminal(this.workspace.projectId,this.workspace.id,e,this.machineId);const i=this.terminals.filter(n=>n.id!==e);if(this.terminals=i,this.selectedId===e||this.selectedTerminalId===e){const n=w(i)?.id;this.selectTerminalIdInView(n),this.onSelectTerminal(n,{replace:!0})}}catch(i){this.error=i instanceof Error?i.message:String(i)}}selectTerminal(e){this.selectedId!==e&&this.selectTerminalIdInView(e),this.onSelectTerminal(e)}selectedTerminalInfo(){return this.terminals.find(e=>e.id===this.selectedId)}selectedCommandRun(){const e=this.selectedTerminalInfo()?.commandRunId;if(e!==void 0)return this.commandRuns.find(t=>t.id===e)}async loadCommandRuns(){const e=this.workspace;if(e!==void 0)try{const t=await h.listCommandRuns({projectId:e.projectId,workspaceId:e.id},this.machineId);this.commandRuns=t,this.cancellingRunIds=this.cancellingRunIds.filter(i=>t.some(n=>n.id===i&&v(n))),this.updateCommandRunPolling(this.hasPendingCommandRuns(t))}catch(t){this.error=t instanceof Error?t.message:String(t)}}updateCommandRunPolling(e){if(e&&this.commandRunPollTimer===void 0){this.commandRunPollTimer=window.setInterval(()=>{this.loadCommandRuns()},ie);return}!e&&this.commandRunPollTimer!==void 0&&(window.clearInterval(this.commandRunPollTimer),this.commandRunPollTimer=void 0)}hasPendingCommandRuns(e=this.commandRuns){return e.some(v)}async cancelCommandRun(e){if(!(!v(e)||this.cancellingRunIds.includes(e.id))){this.error=void 0,this.cancellingRunIds=[...this.cancellingRunIds,e.id];try{await h.cancelCommandRun(e.id,this.machineId),await this.loadCommandRuns()}catch(t){this.error=t instanceof Error?t.message:String(t)}finally{this.cancellingRunIds=this.cancellingRunIds.filter(t=>t!==e.id)}}}async continueTerminal(e){if(!(this.workspace===void 0||this.continuingTerminalIds.includes(e))){this.error=void 0,this.continuingTerminalIds=[...this.continuingTerminalIds,e];try{const t=await h.continueTerminal(this.workspace.projectId,this.workspace.id,e,this.machineId);this.terminals=this.terminals.map(i=>i.id===e?t:i),this.socket===void 0&&this.disposeTerminalView(),this.fitAndNotify(),this.terminal?.focus()}catch(t){this.error=t instanceof Error?t.message:String(t)}finally{this.continuingTerminalIds=this.continuingTerminalIds.filter(t=>t!==e)}}}ensureTerminalView(){const e=this.workspace,t=this.terminalHostElement();if(!this.visible||this.terminal!==void 0||this.selectedId===void 0||t===void 0||e===void 0)return;const i=new T(C(this)),n=new S;i.loadAddon(n),i.open(t),this.terminal=i,this.fitAddon=n,this.resizeObserver=new ResizeObserver(()=>{this.fitAndNotify()}),this.resizeObserver.observe(t),i.onData(a=>{this.suppressTerminalInput||this.sendTerminalInput(a)});const r=this.fitTerminal();this.connectSocket(e.projectId,e.id,this.selectedId,i,r),requestAnimationFrame(()=>{this.fitAndNotify()}),i.focus()}connectSocket(e,t,i,n,r){const a=_(e,t,i,r,this.machineId);a.binaryType="arraybuffer",this.socket=a,a.addEventListener("open",()=>{this.fitAndNotify()}),a.addEventListener("message",u=>{this.handleSocketMessage(u.data,i,n)}),a.addEventListener("close",()=>{this.socket===a&&(this.socket=void 0)})}async handleSocketMessage(e,t,i){try{const n=re(await se(e));n.type==="output"&&this.writeTerminalOutput(i,n.data,n.replay===!0),n.type==="exit"&&(i.writeln(`\r
|
|
24
|
-
[进程已退出${n.exitCode===void 0?"":`,退出码 ${String(n.exitCode)}`}]`),this.terminals=this.terminals.map(r=>r.id===t?{...r,exited:!0,...n.exitCode===void 0?{}:{exitCode:n.exitCode}}:r),this.loadCommandRuns()),n.type==="error"&&i.writeln(`\r
|
|
25
|
-
[终端错误:${n.message}]`)}catch(n){i.writeln(`\r
|
|
26
|
-
[终端错误:${n instanceof Error?n.message:String(n)}]`)}}writeTerminalOutput(e,t,i){if(!i){e.write(t);return}this.suppressTerminalInput=!0,e.write(t,()=>{this.suppressTerminalInput=!1})}fitAndNotify(){const e=this.fitTerminal();e!==void 0&&this.send({type:"resize",...e})}fitTerminal(){if(this.fitAddon===void 0||this.terminal===void 0)return;const e=this.fitAddon.proposeDimensions(),t=k(e);if(t!==void 0)return this.fitAddon.fit(),t}measureTerminalSize(){const e=this.fitTerminal();if(e!==void 0)return e;const t=this.terminalHostElement();if(this.terminal!==void 0||t===void 0)return;const i=new T(C(this)),n=new S;i.loadAddon(n),i.open(t);const r=k(n.proposeDimensions());return i.dispose(),r}terminalHostElement(){const e=this.terminalHost;return e instanceof HTMLDivElement?e:void 0}applyTerminalTheme(){this.terminal!==void 0&&(this.terminal.options.theme=L(this))}sendTerminalInput(e){const t=oe(e);t!==""&&this.send({type:"input",data:t})}sendSoftKeyInput(e,t){this.sendTerminalInput(e),t.refocus&&this.focusTerminal()}focusTerminal(){const e=this.terminal;e!==void 0&&(e.focus(),requestAnimationFrame(()=>{e.focus()}))}send(e){this.socket?.readyState===WebSocket.OPEN&&this.socket.send(JSON.stringify(e))}disposeTerminalView(){this.resizeObserver?.disconnect(),this.resizeObserver=void 0,this.socket?.close(),this.socket=void 0,this.terminal?.dispose(),this.terminal=void 0,this.fitAddon=void 0}renderCommandRunNotice(){const e=this.selectedCommandRun();if(e===void 0)return null;const t=this.selectedTerminalInfo();if(v(e)){const i=this.cancellingRunIds.includes(e.id);return l`
|
|
27
|
-
<section class="command-run-notice running">
|
|
28
|
-
<div>
|
|
29
|
-
<strong>${e.title}</strong>
|
|
30
|
-
<p>命令正在运行。按 <kbd>Ctrl</kbd>+<kbd>C</kbd> 或使用按钮取消。</p>
|
|
31
|
-
<code>${e.command}</code>
|
|
32
|
-
</div>
|
|
33
|
-
<button class="danger" ?disabled=${i} @click=${()=>{this.cancelCommandRun(e)}}>${i?"已发送取消…":"取消命令"}</button>
|
|
34
|
-
</section>
|
|
35
|
-
`}if(t?.exited===!0){const i=this.continuingTerminalIds.includes(t.id);return l`
|
|
36
|
-
<section class=${`command-run-notice ${e.status}`}>
|
|
37
|
-
<div>
|
|
38
|
-
<strong>${ne(e)}</strong>
|
|
39
|
-
<p>输出已保留。可继续进入 shell 查看或运行后续命令。</p>
|
|
40
|
-
<code>${e.command}</code>
|
|
41
|
-
</div>
|
|
42
|
-
<button ?disabled=${i} @click=${()=>{this.continueTerminal(t.id)}}>${i?"正在启动 shell…":"在 shell 中继续"}</button>
|
|
43
|
-
</section>
|
|
44
|
-
`}return null}selectedTerminalAcceptsInput(){const e=this.selectedTerminalInfo();return e!==void 0&&!e.exited}shouldShowSoftKeys(){return this.selectedTerminalAcceptsInput()&&this.softKeysEnabled}shouldShowSoftKeysToggle(){return this.selectedTerminalAcceptsInput()}toggleSoftKeys(){this.softKeysEnabled=!this.softKeysEnabled,this.softKeysPreferenceStored=!0,H(this.softKeysEnabled),this.scheduleFitAndNotify()}renderSoftKeysToggle(){return this.shouldShowSoftKeysToggle()?l`
|
|
45
|
-
<button
|
|
46
|
-
type="button"
|
|
47
|
-
class=${this.softKeysEnabled?"soft-keys-toggle selected":"soft-keys-toggle"}
|
|
48
|
-
title=${this.softKeysEnabled?"隐藏终端快捷键":"显示终端快捷键"}
|
|
49
|
-
aria-label=${this.softKeysEnabled?"隐藏终端快捷键":"显示终端快捷键"}
|
|
50
|
-
aria-pressed=${String(this.softKeysEnabled)}
|
|
51
|
-
@click=${()=>{this.toggleSoftKeys()}}
|
|
52
|
-
>
|
|
53
|
-
<svg class="keyboard-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
|
54
|
-
<rect x="3" y="5" width="18" height="14" rx="2"></rect>
|
|
55
|
-
<path d="M7 9h.01M10 9h.01M13 9h.01M16 9h.01M7 12h.01M10 12h.01M13 12h.01M16 12h.01M8 16h8"></path>
|
|
56
|
-
</svg>
|
|
57
|
-
<span>按键</span>
|
|
58
|
-
</button>
|
|
59
|
-
`:null}renderSoftKeys(){return l`
|
|
60
|
-
<terminal-soft-keys
|
|
61
|
-
.modes=${this.terminal?.modes}
|
|
62
|
-
.refocusOnClick=${!this.defaultSoftKeysEnvironment}
|
|
63
|
-
.onInput=${(e,t)=>{this.sendSoftKeyInput(e,t)}}
|
|
64
|
-
></terminal-soft-keys>
|
|
65
|
-
`}render(){return l`
|
|
66
|
-
<section class="terminal-shell">
|
|
67
|
-
<div class="terminal-tabs">
|
|
68
|
-
${this.renderSoftKeysToggle()}
|
|
69
|
-
${this.terminals.map(e=>l`
|
|
70
|
-
<button class=${this.selectedId===e.id?"selected":""} @click=${()=>{this.selectTerminal(e.id)}}>
|
|
71
|
-
<span>${e.name}${e.exited?" · 已退出":""}</span>
|
|
72
|
-
<small @click=${t=>{this.closeTerminal(e.id,t)}}>×</small>
|
|
73
|
-
</button>
|
|
74
|
-
`)}
|
|
75
|
-
<button class="new" ?disabled=${this.workspace===void 0} @click=${()=>{this.startTerminal()}}>+ 终端</button>
|
|
76
|
-
</div>
|
|
77
|
-
${this.error===void 0?null:l`<p class="error">${this.error}</p>`}
|
|
78
|
-
${this.renderCommandRunNotice()}
|
|
79
|
-
${this.shouldShowSoftKeys()?this.renderSoftKeys():null}
|
|
80
|
-
${this.loading?l`<p class="muted">正在加载终端…</p>`:null}
|
|
81
|
-
<div class="terminal-host"></div>
|
|
82
|
-
</section>
|
|
83
|
-
`}};o.styles=E`
|
|
84
|
-
:host { flex: 1 1 auto; min-height: 0; display: flex; }
|
|
85
|
-
.terminal-shell { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; background: var(--pi-terminal-bg); }
|
|
86
|
-
.terminal-tabs { flex: 0 0 auto; display: flex; gap: 6px; align-items: center; padding: 6px; border-bottom: 1px solid var(--pi-border-muted); background: var(--pi-bg); overflow: auto; }
|
|
87
|
-
button { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 180px; border: 1px solid var(--pi-border); border-radius: 7px; background: var(--pi-surface); color: var(--pi-text); padding: 5px 7px; cursor: pointer; }
|
|
88
|
-
button.selected { border-color: var(--pi-accent); background: var(--pi-selection-bg); }
|
|
89
|
-
button.new { flex: 0 0 auto; color: var(--pi-muted); }
|
|
90
|
-
.soft-keys-toggle { flex: 0 0 auto; }
|
|
91
|
-
.soft-keys-toggle .keyboard-icon { flex: 0 0 auto; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
|
|
92
|
-
button span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
93
|
-
button small { color: var(--pi-muted); font-size: 14px; line-height: 1; }
|
|
94
|
-
button small:hover { color: var(--pi-danger); }
|
|
95
|
-
button.danger { color: var(--pi-danger); }
|
|
96
|
-
button:disabled { opacity: .5; cursor: not-allowed; }
|
|
97
|
-
.command-run-notice { flex: 0 0 auto; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px; align-items: center; padding: 8px 10px; border-bottom: 1px solid var(--pi-border-muted); background: var(--pi-surface); color: var(--pi-text); }
|
|
98
|
-
.command-run-notice.running { border-color: var(--pi-warning-border); }
|
|
99
|
-
.command-run-notice.succeeded { border-color: var(--pi-success-border); }
|
|
100
|
-
.command-run-notice.failed { border-color: var(--pi-danger); }
|
|
101
|
-
.command-run-notice p { margin: 3px 0; color: var(--pi-muted); }
|
|
102
|
-
.command-run-notice code { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--pi-text-secondary); font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
103
|
-
.command-run-notice kbd { border: 1px solid var(--pi-border); border-radius: 4px; background: var(--pi-bg); padding: 0 4px; font: 11px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
104
|
-
.command-run-notice button { justify-self: end; max-width: none; }
|
|
105
|
-
.terminal-host { flex: 1 1 auto; min-height: 0; padding: 6px; box-sizing: border-box; overflow: hidden; }
|
|
106
|
-
.terminal-host .xterm { height: 100%; cursor: text; position: relative; user-select: none; }
|
|
107
|
-
.terminal-host .xterm.focus, .terminal-host .xterm:focus { outline: none; }
|
|
108
|
-
.terminal-host .xterm-helpers { position: absolute; top: 0; z-index: 5; }
|
|
109
|
-
/* Hide the helper textarea without using !important on the positional properties (left/top/width/height/z-index). xterm sets those inline during IME/dead-key composition (e.g. "~" on a Swedish layout) so the composition is positioned at the cursor and committed correctly; forcing them here would pin the textarea off-screen with zero size and break composition. */
|
|
110
|
-
.terminal-host .xterm-helper-textarea { position: absolute; left: -9999em; top: 0; width: 0; height: 0; padding: 0 !important; border: 0 !important; margin: 0 !important; opacity: 0 !important; z-index: -5; white-space: nowrap !important; overflow: hidden !important; resize: none !important; outline: 0 !important; appearance: none !important; }
|
|
111
|
-
/* The composition view shows pending dead-key/IME input. Without these rules it renders as a static block in the top-left corner instead of overlaying the cursor. */
|
|
112
|
-
.terminal-host .composition-view { position: absolute; display: none; white-space: nowrap; z-index: 1; background: var(--pi-terminal-bg, #000); color: var(--pi-terminal-text, #fff); }
|
|
113
|
-
.terminal-host .composition-view.active { display: block; }
|
|
114
|
-
.terminal-host .xterm-viewport { position: absolute; inset: 0; overflow-y: scroll; cursor: default; background-color: var(--pi-terminal-bg); }
|
|
115
|
-
.terminal-host .xterm-screen { position: relative; }
|
|
116
|
-
.terminal-host .xterm-screen canvas { position: absolute; left: 0; top: 0; }
|
|
117
|
-
.terminal-host .xterm-char-measure-element { display: inline-block; visibility: hidden; position: absolute; top: 0; left: -9999em; line-height: normal; }
|
|
118
|
-
.terminal-host .xterm-accessibility:not(.debug), .terminal-host .xterm-message { position: absolute; inset: 0; z-index: 10; color: transparent; pointer-events: none; }
|
|
119
|
-
.terminal-host .xterm-accessibility-tree:not(.debug) *::selection { color: transparent; }
|
|
120
|
-
.terminal-host .xterm-accessibility-tree { font-family: monospace; user-select: text; white-space: pre; }
|
|
121
|
-
.terminal-host .xterm-accessibility-tree > div { transform-origin: left; width: fit-content; }
|
|
122
|
-
.terminal-host .live-region { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
|
|
123
|
-
.error { flex: 0 0 auto; margin: 0; padding: 8px; color: var(--pi-danger); border-bottom: 1px solid var(--pi-border); background: var(--pi-surface); }
|
|
124
|
-
.muted { margin: 10px; color: var(--pi-muted); }
|
|
125
|
-
.xterm { height: 100%; }
|
|
126
|
-
`;s([m({attribute:!1})],o.prototype,"workspace",2);s([m()],o.prototype,"machineId",2);s([m({attribute:!1})],o.prototype,"selectedTerminalId",2);s([m({type:Boolean})],o.prototype,"autoStart",2);s([m({attribute:!1})],o.prototype,"onSelectTerminal",2);s([F(".terminal-host")],o.prototype,"terminalHost",2);s([c()],o.prototype,"terminals",2);s([c()],o.prototype,"commandRuns",2);s([c()],o.prototype,"selectedId",2);s([c()],o.prototype,"loading",2);s([c()],o.prototype,"error",2);s([c()],o.prototype,"visible",2);s([c()],o.prototype,"cancellingRunIds",2);s([c()],o.prototype,"continuingTerminalIds",2);s([c()],o.prototype,"defaultSoftKeysEnvironment",2);s([c()],o.prototype,"softKeysEnabled",2);o=s([R("terminal-panel")],o);function v(e){return e.status==="queued"||e.status==="running"}function ne(e){return e.status==="succeeded"?`命令已成功${e.exitCode===void 0?"":`,退出码 ${String(e.exitCode)}`}`:`命令失败${e.exitCode===void 0?"":`,退出码 ${String(e.exitCode)}`}`}function re(e){const t=JSON.parse(e);if(!le(t))return{type:"error",message:"无效的终端消息"};const i=t;return i.type==="output"&&typeof i.data=="string"?{type:"output",data:i.data,...typeof i.replay=="boolean"?{replay:i.replay}:{}}:i.type==="exit"?{type:"exit",...typeof i.exitCode=="number"?{exitCode:i.exitCode}:{}}:i.type==="error"&&typeof i.message=="string"?{type:"error",message:i.message}:{type:"error",message:"无效的终端消息"}}function oe(e){return e.replaceAll("\x1B[I","").replaceAll("\x1B[O","")}async function se(e){return typeof e=="string"?e:e instanceof ArrayBuffer?new TextDecoder().decode(e):e instanceof Blob?await e.text():String(e)}function C(e){return{...ee,theme:L(e)}}function L(e){return{background:g(e,"--pi-terminal-bg","#05070a"),foreground:g(e,"--pi-terminal-text","#e6edf3"),cursor:g(e,"--pi-accent","#58a6ff"),selectionBackground:g(e,"--pi-terminal-selection","#264f78")}}function g(e,t,i){const n=getComputedStyle(e).getPropertyValue(t).trim();return n===""?i:n}function k(e){if(!(e===void 0||!ae(e.cols,e.rows)))return{cols:Math.floor(e.cols),rows:Math.floor(e.rows)}}function ae(e,t){return Number.isFinite(e)&&Number.isFinite(t)&&e>0&&t>0}function le(e){return typeof e=="object"&&e!==null}export{o as TerminalPanel,oe as filterTerminalInput};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import{i as A,b as P,a as V,n as z,t as F}from"./index-BiFWXoRZ.js";import"./vendor-editor-core-C7aWiQy0.js";import"./vendor-editor-languages-EW34FfVy.js";class U{diff(e,t,i={}){let o;typeof i=="function"?(o=i,i={}):"callback"in i&&(o=i.callback);const r=this.castInput(e,i),s=this.castInput(t,i),u=this.removeEmpty(this.tokenize(r,i)),l=this.removeEmpty(this.tokenize(s,i));return this.diffWithOptionsObj(u,l,i,o)}diffWithOptionsObj(e,t,i,o){var r;const s=c=>{if(c=this.postProcess(c,i),o){setTimeout(function(){o(c)},0);return}else return c},u=t.length,l=e.length;let d=1,a=u+l;i.maxEditLength!=null&&(a=Math.min(a,i.maxEditLength));const g=(r=i.timeout)!==null&&r!==void 0?r:1/0,v=Date.now()+g,f=[{oldPos:-1,lastComponent:void 0}];let h=this.extractCommon(f[0],t,e,0,i);if(f[0].oldPos+1>=l&&h+1>=u)return s(this.buildValues(f[0].lastComponent,t,e));let w=-1/0,y=1/0;const $=()=>{for(let c=Math.max(w,-d);c<=Math.min(y,d);c+=2){let p;const x=f[c-1],b=f[c+1];x&&(f[c-1]=void 0);let D=!1;if(b){const S=b.oldPos-c;D=b&&0<=S&&S<u}const k=x&&x.oldPos+1<l;if(!D&&!k){f[c]=void 0;continue}if(!k||D&&x.oldPos<b.oldPos?p=this.addToPath(b,!0,!1,0,i):p=this.addToPath(x,!1,!0,1,i),h=this.extractCommon(p,t,e,c,i),p.oldPos+1>=l&&h+1>=u)return s(this.buildValues(p.lastComponent,t,e))||!0;f[c]=p,p.oldPos+1>=l&&(y=Math.min(y,c-1)),h+1>=u&&(w=Math.max(w,c+1))}d++};if(o)(function c(){setTimeout(function(){if(d>a||Date.now()>v)return o(void 0);$()||c()},0)})();else for(;d<=a&&Date.now()<=v;){const c=$();if(c)return c}}addToPath(e,t,i,o,r){const s=e.lastComponent;return s&&!r.oneChangePerToken&&s.added===t&&s.removed===i?{oldPos:e.oldPos+o,lastComponent:{count:s.count+1,added:t,removed:i,previousComponent:s.previousComponent}}:{oldPos:e.oldPos+o,lastComponent:{count:1,added:t,removed:i,previousComponent:s}}}extractCommon(e,t,i,o,r){const s=t.length,u=i.length;let l=e.oldPos,d=l-o,a=0;for(;d+1<s&&l+1<u&&this.equals(i[l+1],t[d+1],r);)d++,l++,a++,r.oneChangePerToken&&(e.lastComponent={count:1,previousComponent:e.lastComponent,added:!1,removed:!1});return a&&!r.oneChangePerToken&&(e.lastComponent={count:a,previousComponent:e.lastComponent,added:!1,removed:!1}),e.oldPos=l,d}equals(e,t,i){return i.comparator?i.comparator(e,t):e===t||!!i.ignoreCase&&e.toLowerCase()===t.toLowerCase()}removeEmpty(e){const t=[];for(let i=0;i<e.length;i++)e[i]&&t.push(e[i]);return t}castInput(e,t){return e}tokenize(e,t){return Array.from(e)}join(e){return e.join("")}postProcess(e,t){return e}get useLongestToken(){return!1}buildValues(e,t,i){const o=[];let r;for(;e;)o.push(e),r=e.previousComponent,delete e.previousComponent,e=r;o.reverse();const s=o.length;let u=0,l=0,d=0;for(;u<s;u++){const a=o[u];if(a.removed)a.value=this.join(i.slice(d,d+a.count)),d+=a.count;else{if(!a.added&&this.useLongestToken){let g=t.slice(l,l+a.count);g=g.map(function(v,f){const h=i[d+f];return h.length>v.length?h:v}),a.value=this.join(g)}else a.value=this.join(t.slice(l,l+a.count));l+=a.count,a.added||(d+=a.count)}}return o}}class R extends U{}const q=new R;function j(n,e,t){return q.diff(n,e,t)}const B=/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/,C=5e3,H=20,G=.2,E=.25;function J(n){const e=K(n);return X(e),e}function K(n){const e=Q(n),t=[];let i,o;for(const r of e){const s=B.exec(r);if(s!==null){i=Number(s[1]),o=Number(s[2]),t.push(m("hunk","",r));continue}if(i!==void 0&&o!==void 0){if(r.startsWith("+")){t.push(m("add","+",r.slice(1),{newLineNumber:o})),o++;continue}if(r.startsWith("-")){t.push(m("remove","-",r.slice(1),{oldLineNumber:i})),i++;continue}if(r.startsWith(" ")){t.push(m("context"," ",r.slice(1),{oldLineNumber:i,newLineNumber:o})),i++,o++;continue}if(r.startsWith("\\")){t.push(m("marker","",r));continue}}i=void 0,o=void 0,t.push(m("meta","",r))}return t}function Q(n){if(n==="")return[];const e=n.replace(/\r\n/g,`
|
|
2
|
-
`).replace(/\r/g,`
|
|
3
|
-
`).split(`
|
|
4
|
-
`);return e.at(-1)===""&&e.pop(),e}function m(n,e,t,i={}){return{kind:n,prefix:e,text:t,spans:t===""?[]:[{text:t,changed:!1}],...i}}function X(n){let e=0;for(;e<n.length;){if(n[e]?.kind!=="remove"){e++;continue}const i=e;for(;n[e]?.kind==="remove";)e++;const o=e;for(;n[e]?.kind==="add";)e++;if(o===e)continue;const r=n.slice(i,o),s=n.slice(o,e);Y(r,s)}}function Y(n,e){if(!(n.length+e.length>H))for(const t of Z(n,e)){const i=te(t.removed.text,t.added.text);i!==void 0&&(t.removed.spans=i.removed,t.added.spans=i.added)}}function Z(n,e){if(n.length===e.length)return n.map((o,r)=>({removed:o,added:e[r]})).filter(T);if(n.length===1)return ee(n[0],e);if(e.length===1)return ne(n,e[0]);const t=[],i=Math.min(n.length,e.length);for(let o=0;o<i;o++){const r=n[o],s=e[o];r===void 0||s===void 0||O(r.text,s.text)>=E&&t.push({removed:r,added:s})}return t}function T(n){return n.added!==void 0}function ee(n,e){if(n===void 0)return[];const t=N(n.text,e);return t===void 0?[]:[{removed:n,added:t}]}function ne(n,e){if(e===void 0)return[];const t=N(e.text,n);return t===void 0?[]:[{removed:t,added:e}]}function N(n,e){let t,i=E;for(const o of e){const r=O(n,o.text);r<=i||(t=o,i=r)}return t}function te(n,e){if(n===e||n.length>C||e.length>C)return;const t=j(n,e),i=_(t,n,e);if(Math.max(n.length,e.length)>=20&&i<G)return;const o=[],r=[];for(const s of t)s.value!==""&&(s.added?r.push({text:s.value,changed:!0}):s.removed?o.push({text:s.value,changed:!0}):(o.push({text:s.value,changed:!1}),r.push({text:s.value,changed:!1})));if(!(!o.some(s=>s.changed)&&!r.some(s=>s.changed)))return{removed:M(o),added:M(r)}}function O(n,e){return n===e?1:n.length>C||e.length>C?0:_(j(n,e),n,e)}function _(n,e,t){const i=Math.max(e.length,t.length);return i===0?1:n.reduce((r,s)=>s.added||s.removed?r:r+s.value.length,0)/i}function M(n){const e=[];for(const t of n){const i=e[e.length-1];i?.changed===t.changed?i.text+=t.text:e.push({...t})}return e}var ie=Object.defineProperty,oe=Object.getOwnPropertyDescriptor,W=(n,e,t,i)=>{for(var o=i>1?void 0:i?oe(e,t):e,r=n.length-1,s;r>=0;r--)(s=n[r])&&(o=(i?s(e,t,o):s(o))||o);return i&&o&&ie(e,t,o),o};let L=class extends A{constructor(){super(...arguments),this.diff=""}render(){const n=J(this.diff);return n.length===0?P`<p class="empty">No diff.</p>`:P`
|
|
5
|
-
<div class="scroller">
|
|
6
|
-
<div class="diff-grid" role="table" aria-label="Unified diff">
|
|
7
|
-
${n.map(e=>this.renderLine(e))}
|
|
8
|
-
</div>
|
|
9
|
-
</div>
|
|
10
|
-
`}renderLine(n){const e=n.kind;return P`
|
|
11
|
-
<div class="line" role="row">
|
|
12
|
-
<span class=${`cell line-number old ${e}`} role="cell">${I(n.oldLineNumber)}</span>
|
|
13
|
-
<span class=${`cell line-number new ${e}`} role="cell">${I(n.newLineNumber)}</span>
|
|
14
|
-
<span class=${`cell prefix ${e}`} role="cell">${n.prefix}</span>
|
|
15
|
-
<span class=${`cell content ${e}`} role="cell">${re(n.spans)}</span>
|
|
16
|
-
</div>
|
|
17
|
-
`}};L.styles=V`
|
|
18
|
-
:host { display: block; min-height: 0; height: 100%; color: var(--pi-text); background: var(--pi-bg); }
|
|
19
|
-
.empty { box-sizing: border-box; margin: 0; padding: 10px; color: var(--pi-muted); }
|
|
20
|
-
.scroller { height: 100%; min-height: 0; overflow: auto; background: var(--pi-bg); }
|
|
21
|
-
.diff-grid { display: grid; grid-template-columns: max-content max-content 2ch max-content; width: max-content; min-width: 100%; padding: 6px 0; font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; line-height: 1.45; }
|
|
22
|
-
.line { display: contents; }
|
|
23
|
-
.cell { min-height: 1.45em; white-space: pre; }
|
|
24
|
-
.line-number { min-width: 4ch; padding: 0 8px; border-right: 1px solid var(--pi-border-muted); color: var(--pi-dim); text-align: right; user-select: none; }
|
|
25
|
-
.prefix { padding: 0 4px; color: var(--pi-dim); text-align: center; user-select: none; }
|
|
26
|
-
.content { padding: 0 12px 0 4px; }
|
|
27
|
-
.meta { color: var(--pi-dim); }
|
|
28
|
-
.hunk { background: color-mix(in srgb, var(--pi-accent) 9%, transparent); color: var(--pi-accent); }
|
|
29
|
-
.add { background: color-mix(in srgb, var(--pi-success) 12%, transparent); }
|
|
30
|
-
.remove { background: color-mix(in srgb, var(--pi-danger) 12%, transparent); }
|
|
31
|
-
.marker { color: var(--pi-dim); }
|
|
32
|
-
.content.add .inline-change { border-radius: 2px; background: color-mix(in srgb, var(--pi-success) 36%, transparent); color: var(--pi-text); }
|
|
33
|
-
.content.remove .inline-change { border-radius: 2px; background: color-mix(in srgb, var(--pi-danger) 36%, transparent); color: var(--pi-text); }
|
|
34
|
-
`;W([z()],L.prototype,"diff",2);L=W([F("unified-diff-viewer")],L);function re(n){return n.map(e=>P`<span class=${e.changed?"inline-change":""}>${e.text}</span>`)}function I(n){return n===void 0?"":String(n)}export{L as UnifiedDiffViewer};
|