@hyperspaceng/neural-web-ui 0.68.2 → 0.69.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.69.0] - 2026-04-22
6
+
7
+ ### Breaking Changes
8
+
9
+ - Migrated the web UI's TypeBox-based tool definitions and runtime dependency from `@sinclair/typebox` 0.34.x to `typebox` 1.x. Install and import from `typebox` instead of `@sinclair/typebox` when embedding or extending `@mariozechner/pi-web-ui` with shared TypeBox schemas ([#3112](https://github.com/badlogic/pi-mono/issues/3112))
10
+
11
+ ### Fixed
12
+
13
+ - Render SVG artifact previews through a blob-backed image instead of injecting untrusted SVG markup into the page DOM ([#3552](https://github.com/badlogic/pi-mono/issues/3552))
14
+
5
15
  ## [0.68.1] - 2026-04-22
6
16
 
7
17
  ## [0.68.0] - 2026-04-20
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-ui-example",
3
- "version": "0.68.2",
3
+ "version": "0.69.1",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperspaceng/neural-web-ui",
3
- "version": "0.68.2",
3
+ "version": "0.69.1",
4
4
  "description": "Reusable web UI components for AI chat interfaces powered by @mariozechner/pi-ai",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,8 +18,9 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@lmstudio/sdk": "^1.5.0",
21
- "@hyperspaceng/neural-ai": "^0.68.2",
22
- "@hyperspaceng/neural-tui": "^0.68.2",
21
+ "@hyperspaceng/neural-ai": "^0.69.1",
22
+ "@hyperspaceng/neural-tui": "^0.69.1",
23
+ "typebox": "^1.1.24",
23
24
  "docx-preview": "^0.3.7",
24
25
  "jszip": "^3.10.1",
25
26
  "lucide": "^0.544.0",
@@ -13,11 +13,17 @@ export class SvgArtifact extends ArtifactElement {
13
13
  @property() override filename = "";
14
14
 
15
15
  private _content = "";
16
+ @state() private previewUrl = "";
17
+
16
18
  override get content(): string {
17
19
  return this._content;
18
20
  }
19
21
  override set content(value: string) {
22
+ if (this._content === value) {
23
+ return;
24
+ }
20
25
  this._content = value;
26
+ this.updatePreviewUrl();
21
27
  this.requestUpdate();
22
28
  }
23
29
 
@@ -31,6 +37,21 @@ export class SvgArtifact extends ArtifactElement {
31
37
  this.viewMode = mode;
32
38
  }
33
39
 
40
+ private revokePreviewUrl() {
41
+ if (this.previewUrl) {
42
+ URL.revokeObjectURL(this.previewUrl);
43
+ this.previewUrl = "";
44
+ }
45
+ }
46
+
47
+ private updatePreviewUrl() {
48
+ this.revokePreviewUrl();
49
+ if (!this._content) {
50
+ return;
51
+ }
52
+ this.previewUrl = URL.createObjectURL(new Blob([this._content], { type: "image/svg+xml" }));
53
+ }
54
+
34
55
  public getHeaderButtons() {
35
56
  const toggle = new PreviewCodeToggle();
36
57
  toggle.mode = this.viewMode;
@@ -52,14 +73,34 @@ export class SvgArtifact extends ArtifactElement {
52
73
  `;
53
74
  }
54
75
 
76
+ override connectedCallback() {
77
+ super.connectedCallback();
78
+ if (this._content && !this.previewUrl) {
79
+ this.updatePreviewUrl();
80
+ }
81
+ }
82
+
83
+ override disconnectedCallback() {
84
+ super.disconnectedCallback();
85
+ this.revokePreviewUrl();
86
+ }
87
+
55
88
  override render() {
56
89
  return html`
57
90
  <div class="h-full flex flex-col">
58
91
  <div class="flex-1 overflow-auto">
59
92
  ${
60
93
  this.viewMode === "preview"
61
- ? html`<div class="h-full flex items-center justify-center">
62
- ${unsafeHTML(this.content.replace(/<svg(\s|>)/i, (_m, p1) => `<svg class="w-full h-full"${p1}`))}
94
+ ? html`<div class="h-full flex items-center justify-center p-4">
95
+ ${
96
+ this.previewUrl
97
+ ? html`<img
98
+ class="max-w-full max-h-full w-full h-full object-contain"
99
+ src="${this.previewUrl}"
100
+ alt="${this.filename}"
101
+ />`
102
+ : ""
103
+ }
63
104
  </div>`
64
105
  : html`<pre class="m-0 p-4 text-xs"><code class="hljs language-xml">${unsafeHTML(
65
106
  hljs.highlight(this.content, { language: "xml", ignoreIllegals: true }).value,
@@ -3,11 +3,11 @@ import "@mariozechner/mini-lit/dist/MarkdownBlock.js";
3
3
  import { Button } from "@mariozechner/mini-lit/dist/Button.js";
4
4
  import type { Agent, AgentMessage, AgentTool } from "@mariozechner/pi-agent-core";
5
5
  import { StringEnum, type ToolCall } from "@mariozechner/pi-ai";
6
- import { type Static, Type } from "@sinclair/typebox";
7
6
  import { html, LitElement, type TemplateResult } from "lit";
8
7
  import { customElement, property, state } from "lit/decorators.js";
9
8
  import { createRef, type Ref, ref } from "lit/directives/ref.js";
10
9
  import { X } from "lucide";
10
+ import { type Static, Type } from "typebox";
11
11
  import type { ArtifactMessage } from "../../components/Messages.js";
12
12
  import { ArtifactsRuntimeProvider } from "../../components/sandbox/ArtifactsRuntimeProvider.js";
13
13
  import { AttachmentsRuntimeProvider } from "../../components/sandbox/AttachmentsRuntimeProvider.js";
@@ -1,9 +1,9 @@
1
1
  import type { AgentTool } from "@mariozechner/pi-agent-core";
2
2
  import type { ToolResultMessage } from "@mariozechner/pi-ai";
3
- import { type Static, Type } from "@sinclair/typebox";
4
3
  import { html } from "lit";
5
4
  import { createRef, ref } from "lit/directives/ref.js";
6
5
  import { FileText } from "lucide";
6
+ import { type Static, Type } from "typebox";
7
7
  import { EXTRACT_DOCUMENT_DESCRIPTION } from "../prompts/prompts.js";
8
8
  import { loadAttachment } from "../utils/attachment-utils.js";
9
9
  import { isCorsError } from "../utils/proxy-utils.js";
@@ -1,10 +1,10 @@
1
1
  import { i18n } from "@mariozechner/mini-lit";
2
2
  import type { AgentTool } from "@mariozechner/pi-agent-core";
3
3
  import type { ToolResultMessage } from "@mariozechner/pi-ai";
4
- import { type Static, Type } from "@sinclair/typebox";
5
4
  import { html } from "lit";
6
5
  import { createRef, ref } from "lit/directives/ref.js";
7
6
  import { Code } from "lucide";
7
+ import { type Static, Type } from "typebox";
8
8
  import { type SandboxFile, SandboxIframe, type SandboxResult } from "../components/SandboxedIframe.js";
9
9
  import type { SandboxRuntimeProvider } from "../components/sandbox/SandboxRuntimeProvider.js";
10
10
  import { JAVASCRIPT_REPL_TOOL_DESCRIPTION } from "../prompts/prompts.js";