@jmfederico/pi-web 1.202608.0 → 1.202608.2

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.
Files changed (258) hide show
  1. package/README.md +4 -4
  2. package/dist/cli.js +287 -113
  3. package/dist/cli.js.map +1 -1
  4. package/dist/client/assets/CodeViewer-BMWwxG7q.js +4 -0
  5. package/dist/client/assets/{TerminalPanel-fYrKc3jh.js → TerminalPanel-CacQDIYn.js} +6 -6
  6. package/dist/client/assets/index-DUW2xnoV.js +4279 -0
  7. package/dist/client/assets/vendor-editor-core-CXO8gGab.js +12 -0
  8. package/dist/client/assets/vendor-editor-languages-CpW4sJsX.js +46 -0
  9. package/dist/client/index.html +3 -3
  10. package/dist/config.js +110 -73
  11. package/dist/config.js.map +1 -1
  12. package/dist/docker/piWebDockerCommandPlan.js +4 -3
  13. package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
  14. package/dist/environment.js +4 -0
  15. package/dist/environment.js.map +1 -0
  16. package/dist/nativeServices/installedServiceDefinitions.js +602 -0
  17. package/dist/nativeServices/installedServiceDefinitions.js.map +1 -0
  18. package/dist/nativeServices/serviceAction.js +174 -0
  19. package/dist/nativeServices/serviceAction.js.map +1 -0
  20. package/dist/nativeServices/serviceDoctor.js +374 -71
  21. package/dist/nativeServices/serviceDoctor.js.map +1 -1
  22. package/dist/nativeServices/servicePlan.js +1 -1
  23. package/dist/nativeServices/servicePlan.js.map +1 -1
  24. package/dist/{pi-web-plugins → pi-packages}/relays/markdownDocument.js +1 -1
  25. package/dist/pi-packages/relays/package.json +11 -0
  26. package/dist/{pi-web-plugins → pi-packages}/relays/pi-web-plugin.js +4 -4
  27. package/dist/pi-packages/relays/prompts/relay-worktree.md +157 -0
  28. package/dist/pi-packages/relays/prompts/relay.md +152 -0
  29. package/dist/{pi-web-plugins → pi-packages}/relays/relayDiscovery.js +1 -1
  30. package/dist/{pi-web-plugins → pi-packages}/relays/relaysPanelElement.js +1 -1
  31. package/dist/pi-packages/relays/skills/relay/SKILL.md +123 -0
  32. package/dist/pi-web-plugins/git/browser/git-contract.js +108 -0
  33. package/dist/pi-web-plugins/git/browser/git-panel.js +609 -0
  34. package/dist/pi-web-plugins/git/browser/gitFileList.js +43 -0
  35. package/dist/pi-web-plugins/git/browser/gitFileShared.js +16 -0
  36. package/dist/pi-web-plugins/git/browser/gitFileTree.js +81 -0
  37. package/dist/pi-web-plugins/git/browser/gitFileViewPreference.js +35 -0
  38. package/dist/pi-web-plugins/git/browser/gitRoute.js +42 -0
  39. package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +10 -0
  40. package/dist/pi-web-plugins/git/browser/unifiedDiff.js +301 -0
  41. package/dist/{server/git/gitService.js → pi-web-plugins/git/git-backend.js} +209 -60
  42. package/dist/pi-web-plugins/git/package.json +16 -0
  43. package/dist/pi-web-plugins/git/server-plugin.js +176 -0
  44. package/dist/pi-web-plugins/info/infoInternals.js +16 -2
  45. package/dist/pi-web-plugins/info/package.json +1 -1
  46. package/dist/pi-web-plugins/info/pi-web-plugin.js +2 -2
  47. package/dist/pi-web-plugins/updates/package.json +1 -1
  48. package/dist/pi-web-plugins/updates/pi-web-plugin.js +1 -1
  49. package/dist/pi-web-plugins/workspace-tasks/package.json +1 -1
  50. package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +3 -3
  51. package/dist/piWebVersionReport.js +63 -10
  52. package/dist/piWebVersionReport.js.map +1 -1
  53. package/dist/plugin-api.d.ts +33 -16
  54. package/dist/pluginRecoveryCli.js +172 -0
  55. package/dist/pluginRecoveryCli.js.map +1 -0
  56. package/dist/server/activity/workspaceActivityService.js +27 -19
  57. package/dist/server/activity/workspaceActivityService.js.map +1 -1
  58. package/dist/server/app.js +41 -15
  59. package/dist/server/app.js.map +1 -1
  60. package/dist/server/configRoutes.js +3 -21
  61. package/dist/server/configRoutes.js.map +1 -1
  62. package/dist/server/knownAutoInstallPiPackages.js +30 -0
  63. package/dist/server/knownAutoInstallPiPackages.js.map +1 -0
  64. package/dist/server/machines/machineClient.js +23 -4
  65. package/dist/server/machines/machineClient.js.map +1 -1
  66. package/dist/server/machines/machinePluginProxyRoutes.js +74 -17
  67. package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
  68. package/dist/server/machines/machineProxyRoutes.js +165 -8
  69. package/dist/server/machines/machineProxyRoutes.js.map +1 -1
  70. package/dist/server/machines/machineService.js +23 -0
  71. package/dist/server/machines/machineService.js.map +1 -1
  72. package/dist/server/piPackageIdentity.js +25 -0
  73. package/dist/server/piPackageIdentity.js.map +1 -0
  74. package/dist/server/piPackageService.js +69 -8
  75. package/dist/server/piPackageService.js.map +1 -1
  76. package/dist/server/piWebPluginCatalog.js +584 -0
  77. package/dist/server/piWebPluginCatalog.js.map +1 -0
  78. package/dist/server/piWebPluginLifecycle.js +162 -0
  79. package/dist/server/piWebPluginLifecycle.js.map +1 -0
  80. package/dist/server/piWebPluginService.js +159 -261
  81. package/dist/server/piWebPluginService.js.map +1 -1
  82. package/dist/server/piWebStatus.js +41 -9
  83. package/dist/server/piWebStatus.js.map +1 -1
  84. package/dist/server/plugins/pluginBackendProxyRoutes.js +69 -0
  85. package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -0
  86. package/dist/server/plugins/serverPluginExec.js +184 -0
  87. package/dist/server/plugins/serverPluginExec.js.map +1 -0
  88. package/dist/server/plugins/serverPluginRuntime.js +480 -0
  89. package/dist/server/plugins/serverPluginRuntime.js.map +1 -0
  90. package/dist/server/projectTrustRoutes.js +80 -0
  91. package/dist/server/projectTrustRoutes.js.map +1 -0
  92. package/dist/server/projects/projectService.js +3 -1
  93. package/dist/server/projects/projectService.js.map +1 -1
  94. package/dist/server/realtime/sessionEventHub.js +26 -11
  95. package/dist/server/realtime/sessionEventHub.js.map +1 -1
  96. package/dist/server/requestCancellation.js +32 -0
  97. package/dist/server/requestCancellation.js.map +1 -0
  98. package/dist/server/sessiond/agentProcessEnvironment.js +29 -30
  99. package/dist/server/sessiond/agentProcessEnvironment.js.map +1 -1
  100. package/dist/server/sessiond/autoInstallPiPackages.js +63 -0
  101. package/dist/server/sessiond/autoInstallPiPackages.js.map +1 -0
  102. package/dist/server/sessiond/pluginBackendRoutes.js +64 -0
  103. package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -0
  104. package/dist/server/sessiond/sessionDaemonShutdown.js +24 -0
  105. package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -0
  106. package/dist/server/sessiond/sessionProxyRoutes.js +2 -2
  107. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
  108. package/dist/server/sessiond/sessionServiceDependencies.js +2 -1
  109. package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -1
  110. package/dist/server/sessiond/sessiondStateOwnership.js +235 -0
  111. package/dist/server/sessiond/sessiondStateOwnership.js.map +1 -0
  112. package/dist/server/sessiond/workspaceCatalogRoutes.js +31 -0
  113. package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -0
  114. package/dist/server/sessiond/workspaceRemovalRoutes.js +41 -0
  115. package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -0
  116. package/dist/server/sessiond.js +284 -98
  117. package/dist/server/sessiond.js.map +1 -1
  118. package/dist/server/sessions/authService.js +15 -54
  119. package/dist/server/sessions/authService.js.map +1 -1
  120. package/dist/server/sessions/dockerEnvironmentFacts.js +254 -0
  121. package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -0
  122. package/dist/server/sessions/modelCatalogRefresher.js +4 -3
  123. package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
  124. package/dist/server/sessions/piSessionManagerGateway.js +18 -67
  125. package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
  126. package/dist/server/sessions/piSessionService.js +495 -142
  127. package/dist/server/sessions/piSessionService.js.map +1 -1
  128. package/dist/server/sessions/sessionCommandService.js +24 -1
  129. package/dist/server/sessions/sessionCommandService.js.map +1 -1
  130. package/dist/server/sessions/sessionEnvironmentFacts.js +48 -0
  131. package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -0
  132. package/dist/server/sessions/sessionFileFormat.js +27 -0
  133. package/dist/server/sessions/sessionFileFormat.js.map +1 -0
  134. package/dist/server/sessions/sessionFileHeader.js +1 -14
  135. package/dist/server/sessions/sessionFileHeader.js.map +1 -1
  136. package/dist/server/sessions/sessionModelScope.js +160 -0
  137. package/dist/server/sessions/sessionModelScope.js.map +1 -0
  138. package/dist/server/sessions/sessionNameGenerator.js +6 -6
  139. package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
  140. package/dist/server/sessions/sessionRoutes.js +55 -0
  141. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  142. package/dist/server/sessions/sessionSummaryScanner.js +96 -224
  143. package/dist/server/sessions/sessionSummaryScanner.js.map +1 -1
  144. package/dist/server/sessions/spawnSubsessionTool.js +5 -9
  145. package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
  146. package/dist/server/sessions/spawnTargetResolver.js +1 -1
  147. package/dist/server/status/machineStatusRoutes.js +8 -0
  148. package/dist/server/status/machineStatusRoutes.js.map +1 -0
  149. package/dist/server/status/machineStatusService.js +181 -0
  150. package/dist/server/status/machineStatusService.js.map +1 -0
  151. package/dist/server/status/workspaceAttribution.js +87 -0
  152. package/dist/server/status/workspaceAttribution.js.map +1 -0
  153. package/dist/server/storage/piPackageDismissalStore.js +78 -0
  154. package/dist/server/storage/piPackageDismissalStore.js.map +1 -0
  155. package/dist/server/terminalProxyRoutes.js +2 -1
  156. package/dist/server/terminalProxyRoutes.js.map +1 -1
  157. package/dist/server/workspaceExplorerRoutes.js +20 -13
  158. package/dist/server/workspaceExplorerRoutes.js.map +1 -1
  159. package/dist/server/workspaces/fileContentService.js +18 -9
  160. package/dist/server/workspaces/fileContentService.js.map +1 -1
  161. package/dist/server/workspaces/filePreviewResponseHeaders.js +18 -0
  162. package/dist/server/workspaces/filePreviewResponseHeaders.js.map +1 -0
  163. package/dist/server/workspaces/filePreviewResponsePolicy.js +55 -0
  164. package/dist/server/workspaces/filePreviewResponsePolicy.js.map +1 -0
  165. package/dist/server/workspaces/filePreviewService.js +81 -0
  166. package/dist/server/workspaces/filePreviewService.js.map +1 -0
  167. package/dist/server/workspaces/projectWorkspaceCwds.js +0 -5
  168. package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -1
  169. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +402 -0
  170. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -0
  171. package/dist/server/workspaces/workspaceCatalog.js +47 -0
  172. package/dist/server/workspaces/workspaceCatalog.js.map +1 -0
  173. package/dist/server/workspaces/workspaceContext.js +1 -3
  174. package/dist/server/workspaces/workspaceContext.js.map +1 -1
  175. package/dist/server/workspaces/workspaceDeletionRoutes.js +35 -125
  176. package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
  177. package/dist/server/workspaces/workspaceProviderRegistry.js +651 -0
  178. package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -0
  179. package/dist/server/workspaces/workspaceRemovalService.js +256 -0
  180. package/dist/server/workspaces/workspaceRemovalService.js.map +1 -0
  181. package/dist/server/workspaces/workspaceRouteErrors.js +7 -0
  182. package/dist/server/workspaces/workspaceRouteErrors.js.map +1 -0
  183. package/dist/server/workspaces/worktreePreRemoveHook.js +39 -0
  184. package/dist/server/workspaces/worktreePreRemoveHook.js.map +1 -0
  185. package/dist/server-plugin-api.d.ts +140 -0
  186. package/dist/server-plugin-api.js +2 -0
  187. package/dist/server-plugin-api.js.map +1 -0
  188. package/dist/serverPluginRecovery.js +138 -0
  189. package/dist/serverPluginRecovery.js.map +1 -0
  190. package/dist/sessiond/activeAgentProfile.js +3 -22
  191. package/dist/sessiond/activeAgentProfile.js.map +1 -1
  192. package/dist/sessiond/config.js +11 -0
  193. package/dist/sessiond/config.js.map +1 -1
  194. package/dist/sessiond/sessionDaemonClient.js +11 -9
  195. package/dist/sessiond/sessionDaemonClient.js.map +1 -1
  196. package/dist/shared/activeAgentProfile.js +2 -41
  197. package/dist/shared/activeAgentProfile.js.map +1 -1
  198. package/dist/shared/activity.js +0 -3
  199. package/dist/shared/activity.js.map +1 -1
  200. package/dist/shared/apiTypes.js +7 -5
  201. package/dist/shared/apiTypes.js.map +1 -1
  202. package/dist/shared/capabilities.js +8 -6
  203. package/dist/shared/capabilities.js.map +1 -1
  204. package/dist/shared/federatedRoutes.js +36 -5
  205. package/dist/shared/federatedRoutes.js.map +1 -1
  206. package/dist/shared/machinePluginIds.js +4 -2
  207. package/dist/shared/machinePluginIds.js.map +1 -1
  208. package/dist/shared/machineStatus.js +94 -0
  209. package/dist/shared/machineStatus.js.map +1 -0
  210. package/dist/shared/piWebStatusParsing.js +33 -0
  211. package/dist/shared/piWebStatusParsing.js.map +1 -1
  212. package/dist/shared/pluginApiTypes.d.ts +150 -0
  213. package/dist/shared/pluginApiTypes.js +3 -0
  214. package/dist/shared/pluginApiTypes.js.map +1 -0
  215. package/dist/shared/pluginBackendProtocol.js +110 -0
  216. package/dist/shared/pluginBackendProtocol.js.map +1 -0
  217. package/dist/shared/pluginIds.js +5 -0
  218. package/dist/shared/pluginIds.js.map +1 -1
  219. package/dist/shared/pluginRecoveryCommands.js +14 -0
  220. package/dist/shared/pluginRecoveryCommands.js.map +1 -0
  221. package/dist/shared/workspaceFiles.js +41 -2
  222. package/dist/shared/workspaceFiles.js.map +1 -1
  223. package/dist/shared/workspaceRemovalProtocol.js +24 -0
  224. package/dist/shared/workspaceRemovalProtocol.js.map +1 -0
  225. package/docs/config.md +105 -52
  226. package/docs/plugins.md +403 -146
  227. package/examples/workspace-provider-plugin/README.md +52 -0
  228. package/examples/workspace-provider-plugin/package.json +25 -0
  229. package/examples/workspace-provider-plugin/src/browser/index.ts +69 -0
  230. package/examples/workspace-provider-plugin/src/server.ts +64 -0
  231. package/examples/workspace-provider-plugin/tsconfig.json +21 -0
  232. package/package.json +30 -11
  233. package/server-plugin-api.d.ts +1 -0
  234. package/dist/client/assets/CodeViewer-D91Sp61M.js +0 -4
  235. package/dist/client/assets/UnifiedDiffViewer-BafLWGtF.js +0 -39
  236. package/dist/client/assets/index-CA8q9_o7.js +0 -4025
  237. package/dist/client/assets/vendor-editor-core-vUi74DnD.js +0 -12
  238. package/dist/client/assets/vendor-editor-languages-DoaXUodB.js +0 -46
  239. package/dist/pi-web-plugins/relays/package.json +0 -9
  240. package/dist/plugin-api/unstable.d.ts +0 -22
  241. package/dist/server/activity/workspaceActivityRoutes.js +0 -4
  242. package/dist/server/activity/workspaceActivityRoutes.js.map +0 -1
  243. package/dist/server/git/gitService.js.map +0 -1
  244. package/dist/server/gitRoutes.js +0 -23
  245. package/dist/server/gitRoutes.js.map +0 -1
  246. package/dist/server/sessions/parentSessionLocator.js +0 -75
  247. package/dist/server/sessions/parentSessionLocator.js.map +0 -1
  248. package/dist/server/workspaces/gitWorktreeDiscovery.js +0 -43
  249. package/dist/server/workspaces/gitWorktreeDiscovery.js.map +0 -1
  250. package/dist/server/workspaces/imagePreviewService.js +0 -40
  251. package/dist/server/workspaces/imagePreviewService.js.map +0 -1
  252. package/dist/server/workspaces/workspaceService.js +0 -52
  253. package/dist/server/workspaces/workspaceService.js.map +0 -1
  254. package/dist/shared/apiTypes.d.ts +0 -1217
  255. package/dist/shared/thinkingLevels.d.ts +0 -27
  256. package/plugin-api/unstable.d.ts +0 -1
  257. /package/dist/{pi-web-plugins → pi-packages}/relays/vendor/README.md +0 -0
  258. /package/dist/{pi-web-plugins → pi-packages}/relays/vendor/marked.esm.js +0 -0
@@ -0,0 +1,52 @@
1
+ # PI WEB dual-entry workspace-provider example
2
+
3
+ This standalone package is a copyable starting point for a trusted PI WEB plugin with both browser API v2 and server API v1 entries. It claims only projects that opt in with a marker file, publishes non-secret metadata to the browser, and demonstrates a browser request routed to the server provider that owns the workspace.
4
+
5
+ The example imports only the supported package declarations:
6
+
7
+ - `@jmfederico/pi-web/plugin-api`
8
+ - `@jmfederico/pi-web/server-plugin-api`
9
+
10
+ It requires PI WEB `1.202608.1` or newer, the first release that provides those entrypoints and browser API v2.
11
+
12
+ ## Build and install
13
+
14
+ Copy `examples/workspace-provider-plugin/` out of the PI WEB repository or installed `@jmfederico/pi-web` package, then run:
15
+
16
+ ```bash
17
+ npm install
18
+ npm run build
19
+ mkdir -p ~/.pi-web/plugins
20
+ ln -s "$PWD" ~/.pi-web/plugins/example-workspaces
21
+ ```
22
+
23
+ If `PI_WEB_DATA_DIR` is set, link into `$PI_WEB_DATA_DIR/plugins` instead. The link name must be a valid plugin id and must not collide with another package.
24
+
25
+ Opt one registered PI WEB project into this example provider:
26
+
27
+ ```bash
28
+ mkdir -p /absolute/path/to/project/.pi-web
29
+ touch /absolute/path/to/project/.pi-web/example-workspace-provider
30
+ ```
31
+
32
+ Then restart the target session daemon and reload the browser:
33
+
34
+ ```bash
35
+ systemctl --user restart pi-web-sessiond
36
+ ```
37
+
38
+ Restarting `pi-web-sessiond.service` may interrupt active sessions and runtime ownership. A browser or web/API restart alone does not activate a server entry.
39
+
40
+ Open the opted-in project. The **Example Provider** panel displays the public marker metadata and its button calls the owning server provider's `summary` operation. Remove the marker and restart sessiond (or trigger a later workspace resolution) to stop claiming that project.
41
+
42
+ ## Boundary demonstrated by the package
43
+
44
+ `package.json` declares:
45
+
46
+ - browser module `dist/browser/index.js` with `browserRoot: "dist/browser"`;
47
+ - server module `dist/server.js`, outside the browser root;
48
+ - `machineSpecific: true`, required for a dual browser/server entry.
49
+
50
+ Only files under `dist/browser/` can be served through this plugin's browser asset route. Source, package metadata, the server module, and dependencies remain outside that route. Keep secrets out of `publicMetadata`: it is visible to every browser script and API consumer.
51
+
52
+ The server provider intentionally does not advertise workspace removal. See the canonical plugin guide for removal-plan shell and completion semantics, package limits, federation behavior, and the complete API contract: <https://pi-web.dev/plugins>.
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "pi-web-example-workspace-provider",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "tsc -p tsconfig.json"
8
+ },
9
+ "devDependencies": {
10
+ "@jmfederico/pi-web": "^1.202608.1",
11
+ "@types/node": "^24.13.3",
12
+ "typescript": "^6.0.3"
13
+ },
14
+ "piWeb": {
15
+ "plugins": [
16
+ {
17
+ "id": "example-workspaces",
18
+ "browserRoot": "dist/browser",
19
+ "module": "dist/browser/index.js",
20
+ "serverModule": "dist/server.js",
21
+ "machineSpecific": true
22
+ }
23
+ ]
24
+ }
25
+ }
@@ -0,0 +1,69 @@
1
+ import type { JsonObject, PiWebPlugin, WorkspacePanelContext } from "@jmfederico/pi-web/plugin-api";
2
+
3
+ const summaries = new Map<string, string>();
4
+
5
+ const plugin: PiWebPlugin = {
6
+ apiVersion: 2,
7
+ name: "Example Workspace Provider",
8
+ activate: ({ pluginId, runtimePluginId, html }) => ({
9
+ contributions: {
10
+ actions: [
11
+ {
12
+ id: "workspace.open",
13
+ title: "Open Example Workspace Provider",
14
+ enabled: ({ state }) => state.selectedWorkspace?.provider?.pluginId === pluginId,
15
+ run: ({ selectWorkspaceTool }) => {
16
+ selectWorkspaceTool(`${runtimePluginId}:workspace.example-provider`);
17
+ },
18
+ },
19
+ ],
20
+ workspacePanels: [
21
+ {
22
+ id: "workspace.example-provider",
23
+ title: "Example Provider",
24
+ visible: ({ workspace }) => workspace.provider?.pluginId === pluginId,
25
+ render: (context) => {
26
+ const marker = stringMetadata(context.workspace.provider?.metadata, "marker") ?? "unknown";
27
+ const summary = summaries.get(workspaceKey(context)) ?? "Request a summary from the owning server plugin.";
28
+ return html`
29
+ <section class="toolbar"><strong>Example workspace provider</strong></section>
30
+ <section class="viewer">
31
+ <p>This workspace is owned by <code>${pluginId}</code>.</p>
32
+ <p class="muted">Claim marker: <code>${marker}</code></p>
33
+ <button
34
+ ?disabled=${context.backend === undefined}
35
+ @click=${() => { void refreshSummary(context); }}
36
+ >Request backend summary</button>
37
+ <p aria-live="polite">${summary}</p>
38
+ </section>
39
+ `;
40
+ },
41
+ },
42
+ ],
43
+ },
44
+ }),
45
+ };
46
+
47
+ export default plugin;
48
+
49
+ async function refreshSummary(context: WorkspacePanelContext): Promise<void> {
50
+ const key = workspaceKey(context);
51
+ try {
52
+ if (context.backend === undefined) throw new Error("The paired workspace backend is unavailable");
53
+ const result = await context.backend.request("summary", null);
54
+ if (typeof result !== "string") throw new Error("The workspace backend returned an invalid summary");
55
+ summaries.set(key, result);
56
+ } catch (error) {
57
+ summaries.set(key, error instanceof Error ? error.message : String(error));
58
+ }
59
+ context.host.requestRender();
60
+ }
61
+
62
+ function workspaceKey(context: WorkspacePanelContext): string {
63
+ return `${context.machine.id}:${context.workspace.id}`;
64
+ }
65
+
66
+ function stringMetadata(metadata: JsonObject | undefined, key: string): string | undefined {
67
+ const value = metadata?.[key];
68
+ return typeof value === "string" ? value : undefined;
69
+ }
@@ -0,0 +1,64 @@
1
+ import { access } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import type {
4
+ PiWebServerPlugin,
5
+ ProjectInput,
6
+ ProviderClaim,
7
+ ProviderRequestContext,
8
+ ProviderWorkspace,
9
+ WorkspaceProvider,
10
+ } from "@jmfederico/pi-web/server-plugin-api";
11
+
12
+ const markerPath = ".pi-web/example-workspace-provider";
13
+
14
+ const plugin: PiWebServerPlugin = {
15
+ apiVersion: 1,
16
+ name: "Example Workspace Provider",
17
+ activate({ pluginId, logger }) {
18
+ logger.info("Activating example workspace provider", { pluginId });
19
+ return { workspaceProvider: createWorkspaceProvider(pluginId) };
20
+ },
21
+ };
22
+
23
+ export default plugin;
24
+
25
+ function createWorkspaceProvider(pluginId: string): WorkspaceProvider {
26
+ return {
27
+ async probe(project: ProjectInput, signal: AbortSignal): Promise<ProviderClaim> {
28
+ signal.throwIfAborted();
29
+ try {
30
+ await access(join(project.path, markerPath));
31
+ signal.throwIfAborted();
32
+ return "claim";
33
+ } catch (error) {
34
+ if (isMissingFile(error)) return "pass";
35
+ throw error;
36
+ }
37
+ },
38
+ async list(project: ProjectInput, signal: AbortSignal): Promise<ProviderWorkspace[]> {
39
+ signal.throwIfAborted();
40
+ return [{
41
+ key: "main",
42
+ path: project.path,
43
+ label: project.name,
44
+ isMain: true,
45
+ // This object is visible to every browser script and API consumer.
46
+ publicMetadata: { kind: "example", marker: markerPath },
47
+ }];
48
+ },
49
+ request(context: ProviderRequestContext) {
50
+ context.signal.throwIfAborted();
51
+ if (context.operation !== "summary") {
52
+ throw new Error(`Unsupported example workspace operation: ${context.operation}`);
53
+ }
54
+ return Promise.resolve(`${pluginId} owns ${context.workspace.label} at ${context.workspace.path}`);
55
+ },
56
+ };
57
+ }
58
+
59
+ function isMissingFile(error: unknown): boolean {
60
+ return typeof error === "object"
61
+ && error !== null
62
+ && "code" in error
63
+ && Reflect.get(error, "code") === "ENOENT";
64
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2022", "DOM"],
7
+ "types": ["node"],
8
+ "rootDir": "src",
9
+ "outDir": "dist",
10
+ "strict": true,
11
+ "exactOptionalPropertyTypes": true,
12
+ "noUncheckedIndexedAccess": true,
13
+ "noImplicitReturns": true,
14
+ "noFallthroughCasesInSwitch": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "verbatimModuleSyntax": true,
17
+ "skipLibCheck": false,
18
+ "noEmitOnError": true
19
+ },
20
+ "include": ["src/**/*.ts"]
21
+ }
package/package.json CHANGED
@@ -1,10 +1,28 @@
1
1
  {
2
2
  "name": "@jmfederico/pi-web",
3
- "version": "1.202608.0",
3
+ "version": "1.202608.2",
4
4
  "description": "Web UI for persistent Pi Coding Agent sessions in real workspaces.",
5
5
  "license": "MIT",
6
6
  "author": "Federico Jaramillo Martinez",
7
7
  "type": "module",
8
+ "exports": {
9
+ "./plugin-api": {
10
+ "types": "./dist/plugin-api.d.ts"
11
+ },
12
+ "./server-plugin-api": {
13
+ "types": "./dist/server-plugin-api.d.ts"
14
+ }
15
+ },
16
+ "typesVersions": {
17
+ "*": {
18
+ "plugin-api": [
19
+ "dist/plugin-api.d.ts"
20
+ ],
21
+ "server-plugin-api": [
22
+ "dist/server-plugin-api.d.ts"
23
+ ]
24
+ }
25
+ },
8
26
  "bin": {
9
27
  "pi-web": "dist/cli.js",
10
28
  "pi-web-server": "dist/server/index.js",
@@ -17,15 +35,16 @@
17
35
  "README.md",
18
36
  "LICENSE",
19
37
  "extensions",
38
+ "examples/workspace-provider-plugin",
20
39
  "docs/plugins.md",
21
40
  "docs/config.md",
22
41
  "docs/assets",
23
42
  "plugin-api.d.ts",
24
- "plugin-api/unstable.d.ts"
43
+ "server-plugin-api.d.ts"
25
44
  ],
26
45
  "scripts": {
27
46
  "dev": "bash -c 'trap \"kill 0\" EXIT; npm run dev:sessiond & npm run dev:web & npm run dev:client & wait'",
28
- "dev:sessiond": "tsx watch src/server/sessiond.ts",
47
+ "dev:sessiond": "npm run build:plugins && tsx watch src/server/sessiond.ts",
29
48
  "dev:web": "bash -c 'set -e; npm run build:plugins; trap \"kill 0\" EXIT; npm run dev:plugins & tsx watch src/server/index.ts & wait'",
30
49
  "dev:server": "npm run dev:web",
31
50
  "dev:client": "vite --host 0.0.0.0",
@@ -37,12 +56,12 @@
37
56
  "typecheck": "tsc --noEmit",
38
57
  "typecheck:cached": "tsc --noEmit --incremental --tsBuildInfoFile node_modules/.cache/pi-web/typecheck.tsbuildinfo",
39
58
  "knip": "knip",
40
- "lint": "eslint \"src/**/*.ts\" \"extensions/**/*.ts\" \"pi-web-plugins/**/*.ts\" vite.config.ts vitest.config.ts",
59
+ "lint": "eslint \"src/**/*.ts\" \"extensions/**/*.ts\" \"pi-web-plugins/**/*.ts\" \"pi-packages/**/*.ts\" vite.config.ts vitest.config.ts",
41
60
  "test": "vitest run --config vitest.config.ts",
42
61
  "verify": "npm run typecheck && npm run lint && npm run knip && npm test",
43
62
  "verify:staged": "node scripts/verify-staged.mjs",
44
63
  "start": "tsx src/server/index.ts",
45
- "start:sessiond": "tsx src/server/sessiond.ts",
64
+ "start:sessiond": "npm run build:plugins && tsx src/server/sessiond.ts",
46
65
  "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
47
66
  "prepack": "npm run build",
48
67
  "pack:dry": "npm pack --dry-run",
@@ -84,9 +103,9 @@
84
103
  },
85
104
  "devDependencies": {
86
105
  "@changesets/cli": "^2.31.0",
87
- "@earendil-works/pi-agent-core": "^0.83.0",
88
- "@earendil-works/pi-ai": "^0.83.0",
89
- "@earendil-works/pi-coding-agent": "^0.83.0",
106
+ "@earendil-works/pi-agent-core": "^0.84.1",
107
+ "@earendil-works/pi-ai": "^0.84.1",
108
+ "@earendil-works/pi-coding-agent": "^0.84.1",
90
109
  "@eslint/js": "^10.0.1",
91
110
  "@types/node": "^24.13.3",
92
111
  "@types/ws": "^8.18.1",
@@ -116,9 +135,9 @@
116
135
  "homepage": "https://pi-web.dev/",
117
136
  "packageManager": "npm@11.11.0",
118
137
  "peerDependencies": {
119
- "@earendil-works/pi-agent-core": ">=0.83.0",
120
- "@earendil-works/pi-ai": ">=0.83.0",
121
- "@earendil-works/pi-coding-agent": ">=0.83.0"
138
+ "@earendil-works/pi-agent-core": ">=0.84.0",
139
+ "@earendil-works/pi-ai": ">=0.84.0",
140
+ "@earendil-works/pi-coding-agent": ">=0.84.0"
122
141
  },
123
142
  "keywords": [
124
143
  "pi-package",
@@ -0,0 +1 @@
1
+ export * from "./dist/server-plugin-api.js";
@@ -1,4 +0,0 @@
1
- import{$ as e,J as t,P as n,S as r,Y as i,l as a,q as o,y as s}from"./vendor-editor-core-vUi74DnD.js";import{c,i as l,l as u,n as d,r as f,s as p,t as m,u as h}from"./vendor-editor-languages-DoaXUodB.js";import{_ as g,a as _,f as v,g as y,m as b,p as x,u as S}from"./index-CA8q9_o7.js";import{t as C}from"./vendor-editor-legacy-CYBnW6ZU.js";var w=class extends b{constructor(...e){super(...e),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 y`<div class="host"></div>`}recreateEditor(){this.editorHost&&(this.view?.destroy(),this.view=new o({parent:this.editorHost,state:e.create({doc:this.content,extensions:[i(),t.of(a),n(r,{fallback:!0}),e.readOnly.of(!0),o.editable.of(!1),o.lineWrapping,T,...D(this.language),...O(this.language)]})}))}static{this.styles=g`
2
- :host { display: block; min-height: 0; height: 100%; }
3
- .host { height: 100%; min-height: 0; overflow: auto; }
4
- `}};_([v()],w.prototype,`content`,void 0),_([v()],w.prototype,`language`,void 0),_([S(`.host`)],w.prototype,`editorHost`,void 0),w=_([x(`code-viewer`)],w);var T=o.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`}}),E=o.theme({".cm-content":{textAlign:`start`},".cm-line":{unicodeBidi:`plaintext`}});function D(e){return e===`markdown`?[o.contentAttributes.of({dir:`auto`}),E]:[]}function O(e){if(e===void 0)return[];switch(e){case`typescript`:return[u({typescript:!0})];case`javascript`:return[u()];case`json`:return[l()];case`markdown`:return[p()];case`css`:return[h()];case`html`:return[c()];case`python`:return[f()];case`rust`:return[d()];case`go`:return[m()];case`diff`:return[s.define(C)];default:return[]}}export{w as CodeViewer};
@@ -1,39 +0,0 @@
1
- import{_ as e,a as t,f as n,g as r,m as i,p as a}from"./index-CA8q9_o7.js";var o=class{diff(e,t,n={}){let r;typeof n==`function`?(r=n,n={}):`callback`in n&&(r=n.callback);let i=this.castInput(e,n),a=this.castInput(t,n),o=this.removeEmpty(this.tokenize(i,n)),s=this.removeEmpty(this.tokenize(a,n));return this.diffWithOptionsObj(o,s,n,r)}diffWithOptionsObj(e,t,n,r){let i=e=>{if(e=this.postProcess(e,n),r){setTimeout(function(){r(e)},0);return}else return e},a=t.length,o=e.length,s=1,c=a+o;n.maxEditLength!=null&&(c=Math.min(c,n.maxEditLength));let l=n.timeout??1/0,u=Date.now()+l,d=[{oldPos:-1,lastComponent:void 0}],f=this.extractCommon(d[0],t,e,0,n);if(d[0].oldPos+1>=o&&f+1>=a)return i(this.buildValues(d[0].lastComponent,t,e));let p=-1/0,m=1/0,h=()=>{for(let r=Math.max(p,-s);r<=Math.min(m,s);r+=2){let s,c=d[r-1],l=d[r+1];c&&(d[r-1]=void 0);let u=!1;if(l){let e=l.oldPos-r;u=l&&0<=e&&e<a}let h=c&&c.oldPos+1<o;if(!u&&!h){d[r]=void 0;continue}if(s=!h||u&&c.oldPos<l.oldPos?this.addToPath(l,!0,!1,0,n):this.addToPath(c,!1,!0,1,n),f=this.extractCommon(s,t,e,r,n),s.oldPos+1>=o&&f+1>=a)return i(this.buildValues(s.lastComponent,t,e))||!0;d[r]=s,s.oldPos+1>=o&&(m=Math.min(m,r-1)),f+1>=a&&(p=Math.max(p,r+1))}s++};if(r)(function e(){setTimeout(function(){if(s>c||Date.now()>u)return r(void 0);h()||e()},0)})();else for(;s<=c&&Date.now()<=u;){let e=h();if(e)return e}}addToPath(e,t,n,r,i){let a=e.lastComponent;return a&&!i.oneChangePerToken&&a.added===t&&a.removed===n?{oldPos:e.oldPos+r,lastComponent:{count:a.count+1,added:t,removed:n,previousComponent:a.previousComponent}}:{oldPos:e.oldPos+r,lastComponent:{count:1,added:t,removed:n,previousComponent:a}}}extractCommon(e,t,n,r,i){let a=t.length,o=n.length,s=e.oldPos,c=s-r,l=0;for(;c+1<a&&s+1<o&&this.equals(n[s+1],t[c+1],i);)c++,s++,l++,i.oneChangePerToken&&(e.lastComponent={count:1,previousComponent:e.lastComponent,added:!1,removed:!1});return l&&!i.oneChangePerToken&&(e.lastComponent={count:l,previousComponent:e.lastComponent,added:!1,removed:!1}),e.oldPos=s,c}equals(e,t,n){return n.comparator?n.comparator(e,t):e===t||!!n.ignoreCase&&e.toLowerCase()===t.toLowerCase()}removeEmpty(e){let t=[];for(let n=0;n<e.length;n++)e[n]&&t.push(e[n]);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,n){let r=[],i;for(;e;)r.push(e),i=e.previousComponent,delete e.previousComponent,e=i;r.reverse();let a=r.length,o=0,s=0,c=0;for(;o<a;o++){let e=r[o];if(e.removed)e.value=this.join(n.slice(c,c+e.count)),c+=e.count;else{if(!e.added&&this.useLongestToken){let r=t.slice(s,s+e.count);r=r.map(function(e,t){let r=n[c+t];return r.length>e.length?r:e}),e.value=this.join(r)}else e.value=this.join(t.slice(s,s+e.count));s+=e.count,e.added||(c+=e.count)}}return r}},s=new class extends o{};function c(e,t,n){return s.diff(e,t,n)}function l(e,t){let n;for(n=0;n<e.length&&n<t.length;n++)if(e[n]!=t[n])return e.slice(0,n);return e.slice(0,n)}function u(e,t){let n;if(!e||!t||e[e.length-1]!=t[t.length-1])return``;for(n=0;n<e.length&&n<t.length;n++)if(e[e.length-(n+1)]!=t[t.length-(n+1)])return e.slice(-n);return e.slice(-n)}function d(e,t,n){if(e.slice(0,t.length)!=t)throw Error(`string ${JSON.stringify(e)} doesn't start with prefix ${JSON.stringify(t)}; this is a bug`);return n+e.slice(t.length)}function f(e,t,n){if(!t)return e+n;if(e.slice(-t.length)!=t)throw Error(`string ${JSON.stringify(e)} doesn't end with suffix ${JSON.stringify(t)}; this is a bug`);return e.slice(0,-t.length)+n}function p(e,t){return d(e,t,``)}function m(e,t){return f(e,t,``)}function h(e,t){return t.slice(0,g(e,t))}function g(e,t){let n=0;e.length>t.length&&(n=e.length-t.length);let r=t.length;e.length<t.length&&(r=e.length);let i=Array(r),a=0;i[0]=0;for(let e=1;e<r;e++){for(t[e]==t[a]?i[e]=i[a]:i[e]=a;a>0&&t[e]!=t[a];)a=i[a];t[e]==t[a]&&a++}a=0;for(let r=n;r<e.length;r++){for(;a>0&&e[r]!=t[a];)a=i[a];e[r]==t[a]&&a++}return a}function _(e,t){let n=[];for(let r of Array.from(t.segment(e))){let e=r.segment;n.length&&/\s/.test(n[n.length-1])&&/\s/.test(e)?n[n.length-1]+=e:n.push(e)}return n}function v(e,t){if(t)return b(e,t)[1];let n;for(n=e.length-1;n>=0&&e[n].match(/\s/);n--);return e.substring(n+1)}function y(e,t){if(t)return b(e,t)[0];let n=e.match(/^\s*/);return n?n[0]:``}function b(e,t){if(!t)return[y(e),v(e)];if(t.resolvedOptions().granularity!=`word`)throw Error(`The segmenter passed must have a granularity of "word"`);let n=_(e,t),r=n[0],i=n[n.length-1];return[/\s/.test(r)?r:``,/\s/.test(i)?i:``]}var x=`a-zA-Z0-9_\\u{AD}\\u{C0}-\\u{D6}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}`,S=RegExp(`[${x}]+|\\s+|[^${x}]`,`ug`);new class extends o{equals(e,t,n){return n.ignoreCase&&(e=e.toLowerCase(),t=t.toLowerCase()),e.trim()===t.trim()}tokenize(e,t={}){let n;if(t.intlSegmenter){let r=t.intlSegmenter;if(r.resolvedOptions().granularity!=`word`)throw Error(`The segmenter passed must have a granularity of "word"`);n=_(e,r)}else n=e.match(S)||[];let r=[],i=null;return n.forEach(e=>{/\s/.test(e)?i==null?r.push(e):r.push(r.pop()+e):i!=null&&/\s/.test(i)?r[r.length-1]==i?r.push(r.pop()+e):r.push(i+e):r.push(e),i=e}),r}join(e){return e.map((e,t)=>t==0?e:e.replace(/^\s+/,``)).join(``)}postProcess(e,t){if(!e||t.oneChangePerToken)return e;let n=null,r=null,i=null;return e.forEach(e=>{e.added?r=e:e.removed?i=e:((r||i)&&C(n,i,r,e,t.intlSegmenter),n=e,r=null,i=null)}),(r||i)&&C(n,i,r,null,t.intlSegmenter),e}};function C(e,t,n,r,i){if(t&&n){let[a,o]=b(t.value,i),[s,c]=b(n.value,i);if(e){let r=l(a,s);e.value=f(e.value,s,r),t.value=p(t.value,r),n.value=p(n.value,r)}if(r){let e=u(o,c);r.value=d(r.value,c,e),t.value=m(t.value,e),n.value=m(n.value,e)}}else if(n){if(e){let e=y(n.value,i);n.value=n.value.substring(e.length)}if(r){let e=y(r.value,i);r.value=r.value.substring(e.length)}}else if(e&&r){let n=y(r.value,i),[a,o]=b(t.value,i),s=l(n,a);t.value=p(t.value,s);let c=u(p(n,s),o);t.value=m(t.value,c),r.value=d(r.value,n,c),e.value=f(e.value,n,n.slice(0,n.length-c.length))}else if(r){let e=y(r.value,i),n=h(v(t.value,i),e);t.value=m(t.value,n)}else if(e){let n=h(v(e.value,i),y(t.value,i));t.value=p(t.value,n)}}new class extends o{tokenize(e){let t=RegExp(`(\\r?\\n)|[${x}]+|[^\\S\\n\\r]+|[^${x}]`,`ug`);return e.match(t)||[]}},new class extends o{constructor(){super(...arguments),this.tokenize=w}equals(e,t,n){return n.ignoreWhitespace?((!n.newlineIsToken||!e.includes(`
2
- `))&&(e=e.trim()),(!n.newlineIsToken||!t.includes(`
3
- `))&&(t=t.trim())):n.ignoreNewlineAtEof&&!n.newlineIsToken&&(e.endsWith(`
4
- `)&&(e=e.slice(0,-1)),t.endsWith(`
5
- `)&&(t=t.slice(0,-1))),super.equals(e,t,n)}};function w(e,t){t.stripTrailingCr&&(e=e.replace(/\r\n/g,`
6
- `));let n=[],r=e.split(/(\n|\r\n)/);r[r.length-1]||r.pop();for(let e=0;e<r.length;e++){let i=r[e];e%2&&!t.newlineIsToken?n[n.length-1]+=i:n.push(i)}return n}function T(e){return e==`.`||e==`!`||e==`?`}new class extends o{tokenize(e){let t=[],n=0;for(let r=0;r<e.length;r++){if(r==e.length-1){t.push(e.slice(n));break}if(T(e[r])&&e[r+1].match(/\s/)){for(t.push(e.slice(n,r+1)),r=n=r+1;e[r+1]?.match(/\s/);)r++;t.push(e.slice(n,r+1)),n=r+1}}return t}},new class extends o{tokenize(e){return e.split(/([{}:;,]|\s+)/)}},new class extends o{constructor(){super(...arguments),this.tokenize=w}get useLongestToken(){return!0}castInput(e,t){let{undefinedReplacement:n,stringifyReplacer:r=(e,t)=>t===void 0?n:t}=t;return typeof e==`string`?e:JSON.stringify(E(e,null,null,r),null,` `)}equals(e,t,n){return super.equals(e.replace(/,([\r\n])/g,`$1`),t.replace(/,([\r\n])/g,`$1`),n)}};function E(e,t,n,r,i){t||=[],n||=[],r&&(e=r(i===void 0?``:i,e));let a;for(a=0;a<t.length;a+=1)if(t[a]===e)return n[a];let o;if(Object.prototype.toString.call(e)===`[object Array]`){for(t.push(e),o=Array(e.length),n.push(o),a=0;a<e.length;a+=1)o[a]=E(e[a],t,n,r,String(a));return t.pop(),n.pop(),o}if(e&&e.toJSON&&(e=e.toJSON()),typeof e==`object`&&e){t.push(e),o={},n.push(o);let i=[],s;for(s in e)Object.prototype.hasOwnProperty.call(e,s)&&i.push(s);for(i.sort(),a=0;a<i.length;a+=1)s=i[a],o[s]=E(e[s],t,n,r,s);t.pop(),n.pop()}else o=e;return o}new class extends o{tokenize(e){return e.slice()}join(e){return e}removeEmpty(e){return e}};var D=/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/,O=5e3,k=20,A=.2,j=.25;function M(e){let t=N(e);return I(t),t}function N(e){let t=P(e),n=[],r,i;for(let e of t){let t=D.exec(e);if(t!==null){r=Number(t[1]),i=Number(t[2]),n.push(F(`hunk`,``,e));continue}if(r!==void 0&&i!==void 0){if(e.startsWith(`+`)){n.push(F(`add`,`+`,e.slice(1),{newLineNumber:i})),i++;continue}if(e.startsWith(`-`)){n.push(F(`remove`,`-`,e.slice(1),{oldLineNumber:r})),r++;continue}if(e.startsWith(` `)){n.push(F(`context`,` `,e.slice(1),{oldLineNumber:r,newLineNumber:i})),r++,i++;continue}if(e.startsWith(`\\`)){n.push(F(`marker`,``,e));continue}}r=void 0,i=void 0,n.push(F(`meta`,``,e))}return n}function P(e){if(e===``)return[];let t=e.replace(/\r\n/g,`
7
- `).replace(/\r/g,`
8
- `).split(`
9
- `);return t.at(-1)===``&&t.pop(),t}function F(e,t,n,r={}){return{kind:e,prefix:t,text:n,spans:n===``?[]:[{text:n,changed:!1}],...r}}function I(e){let t=0;for(;t<e.length;){if(e[t]?.kind!==`remove`){t++;continue}let n=t;for(;e[t]?.kind===`remove`;)t++;let r=t;for(;e[t]?.kind===`add`;)t++;r!==t&&L(e.slice(n,r),e.slice(r,t))}}function L(e,t){if(!(e.length+t.length>k))for(let n of R(e,t)){let e=U(n.removed.text,n.added.text);e!==void 0&&(n.removed.spans=e.removed,n.added.spans=e.added)}}function R(e,t){if(e.length===t.length)return e.map((e,n)=>({removed:e,added:t[n]})).filter(z);if(e.length===1)return B(e[0],t);if(t.length===1)return V(e,t[0]);let n=[],r=Math.min(e.length,t.length);for(let i=0;i<r;i++){let r=e[i],a=t[i];r===void 0||a===void 0||W(r.text,a.text)>=j&&n.push({removed:r,added:a})}return n}function z(e){return e.added!==void 0}function B(e,t){if(e===void 0)return[];let n=H(e.text,t);return n===void 0?[]:[{removed:e,added:n}]}function V(e,t){if(t===void 0)return[];let n=H(t.text,e);return n===void 0?[]:[{removed:n,added:t}]}function H(e,t){let n,r=j;for(let i of t){let t=W(e,i.text);t<=r||(n=i,r=t)}return n}function U(e,t){if(e===t||e.length>O||t.length>O)return;let n=c(e,t),r=G(n,e,t);if(Math.max(e.length,t.length)>=20&&r<A)return;let i=[],a=[];for(let e of n)e.value!==``&&(e.added?a.push({text:e.value,changed:!0}):e.removed?i.push({text:e.value,changed:!0}):(i.push({text:e.value,changed:!1}),a.push({text:e.value,changed:!1})));if(!(!i.some(e=>e.changed)&&!a.some(e=>e.changed)))return{removed:K(i),added:K(a)}}function W(e,t){return e===t?1:e.length>O||t.length>O?0:G(c(e,t),e,t)}function G(e,t,n){let r=Math.max(t.length,n.length);return r===0?1:e.reduce((e,t)=>t.added||t.removed?e:e+t.value.length,0)/r}function K(e){let t=[];for(let n of e){let e=t[t.length-1];e?.changed===n.changed?e.text+=n.text:t.push({...n})}return t}var q=class extends i{constructor(...e){super(...e),this.diff=``}render(){let e=M(this.diff);return e.length===0?r`<p class="empty">No diff.</p>`:r`
10
- <div class="scroller">
11
- <div class="diff-grid" role="table" aria-label="Unified diff">
12
- ${e.map(e=>this.renderLine(e))}
13
- </div>
14
- </div>
15
- `}renderLine(e){let t=e.kind;return r`
16
- <div class="line" role="row">
17
- <span class=${`cell line-number old ${t}`} role="cell">${Y(e.oldLineNumber)}</span>
18
- <span class=${`cell line-number new ${t}`} role="cell">${Y(e.newLineNumber)}</span>
19
- <span class=${`cell prefix ${t}`} role="cell">${e.prefix}</span>
20
- <span class=${`cell content ${t}`} role="cell">${J(e.spans)}</span>
21
- </div>
22
- `}static{this.styles=e`
23
- :host { display: block; min-height: 0; height: 100%; color: var(--pi-text); background: var(--pi-bg); }
24
- .empty { box-sizing: border-box; margin: 0; padding: 10px; color: var(--pi-muted); }
25
- .scroller { height: 100%; min-height: 0; overflow: auto; background: var(--pi-bg); }
26
- .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; }
27
- .line { display: contents; }
28
- .cell { min-height: 1.45em; white-space: pre; }
29
- .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; }
30
- .prefix { padding: 0 4px; color: var(--pi-dim); text-align: center; user-select: none; }
31
- .content { padding: 0 12px 0 4px; }
32
- .meta { color: var(--pi-dim); }
33
- .hunk { background: color-mix(in srgb, var(--pi-accent) 9%, transparent); color: var(--pi-accent); }
34
- .add { background: color-mix(in srgb, var(--pi-success) 12%, transparent); }
35
- .remove { background: color-mix(in srgb, var(--pi-danger) 12%, transparent); }
36
- .marker { color: var(--pi-dim); }
37
- .content.add .inline-change { border-radius: 2px; background: color-mix(in srgb, var(--pi-success) 36%, transparent); color: var(--pi-text); }
38
- .content.remove .inline-change { border-radius: 2px; background: color-mix(in srgb, var(--pi-danger) 36%, transparent); color: var(--pi-text); }
39
- `}};t([n()],q.prototype,`diff`,void 0),q=t([a(`unified-diff-viewer`)],q);function J(e){return e.map(e=>r`<span class=${e.changed?`inline-change`:``}>${e.text}</span>`)}function Y(e){return e===void 0?``:String(e)}export{q as UnifiedDiffViewer};