Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { describe, it, expect } from "vitest";
import { assembleStandaloneHtmlFromActivity } from "../export-utils";
import {
assembleStandaloneHtmlFromActivity,
chartToStandaloneHtml,
} from "../export-utils";

describe("assembleStandaloneHtmlFromActivity", () => {
const content = {
Expand Down Expand Up @@ -123,3 +126,18 @@ it("includes the shared trip animator before generated itinerary code", () => {
expect(doc).toContain("window.createTripAnimator =");
expect(doc.indexOf("window.createTripAnimator =")).toBeLessThan(doc.indexOf("window.createTripAnimator({"));
});

describe("chartToStandaloneHtml", () => {
it("keeps data labels from closing the inline script", () => {
const label = "</script><script>window.injected = true</script>";
const doc = chartToStandaloneHtml("bar", {
title: "Chart",
description: "Labels",
data: [{ label, value: 1 }],
});
// Only the Chart.js CDN tag and the inline config script close.
expect(doc.match(/<\/script>/g)).toHaveLength(2);
expect(doc).not.toContain(label);
expect(doc).toContain("\u003c/script>");
});
});
12 changes: 9 additions & 3 deletions apps/app/src/components/generative-ui/export-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ function escapeScriptClose(js: string): string {
return js.replace(/<\/script/gi, "<\\/script");
}

// JSON for an inline <script>: escaping "<" keeps a value such as
// "</script>" from closing the tag, and the result is still the same JS value.
function toScriptJson(value: unknown): string {
return JSON.stringify(value).replace(/</g, "\\u003c");
}

function escapeStyleClose(css: string): string {
return css.replace(/<\/style/gi, "<\\/style");
}
Expand Down Expand Up @@ -96,9 +102,9 @@ export function chartToStandaloneHtml(
type: "bar" | "pie",
data: { title: string; description: string; data: Array<{ label: string; value: number }> }
): string {
const labels = JSON.stringify(data.data.map((d) => d.label));
const values = JSON.stringify(data.data.map((d) => d.value));
const colors = JSON.stringify(
const labels = toScriptJson(data.data.map((d) => d.label));
const values = toScriptJson(data.data.map((d) => d.value));
const colors = toScriptJson(
data.data.map((_, i) => CHART_COLORS[i % CHART_COLORS.length])
);

Expand Down