This is rendered HTML content with styling.
++ This page demonstrates all the different cell output types supported by + the system. HTML and SVG outputs are rendered using IframeOutput for + sandboxed rendering. +
+ +
+ {finalIframeUriPrefix}/react.html
+
+
- import("@/components/outputs/shared-with-iframe/MarkdownRenderer").then(
- (m) => ({
- default: m.MarkdownRenderer,
- })
- )
+ import("./MarkdownRenderer.js").then((m) => ({
+ default: m.MarkdownRenderer,
+ }))
);
const JsonOutput = React.lazy(() =>
- import("@/components/outputs/shared-with-iframe/JsonOutput").then((m) => ({
+ import("./JsonOutput.js").then((m) => ({
default: m.JsonOutput,
}))
);
const HtmlOutput = React.lazy(() =>
- import("@/components/outputs/shared-with-iframe/HtmlOutput").then((m) => ({
+ import("./HtmlOutput.js").then((m) => ({
default: m.HtmlOutput,
}))
);
const ImageOutput = React.lazy(() =>
- import("@/components/outputs/shared-with-iframe/ImageOutput").then((m) => ({
+ import("./ImageOutput.js").then((m) => ({
default: m.ImageOutput,
}))
);
const SvgOutput = React.lazy(() =>
- import("@/components/outputs/shared-with-iframe/SvgOutput").then((m) => ({
+ import("./SvgOutput.js").then((m) => ({
default: m.SvgOutput,
}))
);
const PlainTextOutput = React.lazy(() =>
- import("@/components/outputs/shared-with-iframe/PlainTextOutput").then(
- (m) => ({
- default: m.PlainTextOutput,
- })
- )
+ import("./PlainTextOutput.js").then((m) => ({
+ default: m.PlainTextOutput,
+ }))
);
const GeoJsonMapOutput = React.lazy(() =>
- import(
- "@/components/outputs/shared-with-iframe/geojson/GeoJsonMapOutput"
- ).then((m) => ({
+ import("./geojson/GeoJsonMapOutput.js").then((m) => ({
default: m.GeoJsonMapOutput,
}))
);
// Dynamic imports for AI outputs
const AiToolCallOutput = React.lazy(() =>
- import("@/components/outputs/shared-with-iframe/AiToolCallOutput").then(
- (m) => ({
- default: m.AiToolCallOutput,
- })
- )
+ import("./AiToolCallOutput.js").then((m) => ({
+ default: m.AiToolCallOutput,
+ }))
);
const AiToolResultOutput = React.lazy(() =>
- import("@/components/outputs/shared-with-iframe/AiToolResultOutput").then(
- (m) => ({
- default: m.AiToolResultOutput,
- })
- )
+ import("./AiToolResultOutput.js").then((m) => ({
+ default: m.AiToolResultOutput,
+ }))
);
export function RichOutputContent({
diff --git a/src/components/outputs/shared-with-iframe/SingleOutput.tsx b/packages/components/src/outputs/SingleOutput.tsx
similarity index 92%
rename from src/components/outputs/shared-with-iframe/SingleOutput.tsx
rename to packages/components/src/outputs/SingleOutput.tsx
index cf2347d98..1df254591 100644
--- a/src/components/outputs/shared-with-iframe/SingleOutput.tsx
+++ b/packages/components/src/outputs/SingleOutput.tsx
@@ -1,31 +1,25 @@
import React from "react";
-import {
- AnsiErrorOutput,
- AnsiStreamOutput,
-} from "@/components/outputs/shared-with-iframe/AnsiOutput";
+import { AnsiErrorOutput, AnsiStreamOutput } from "./AnsiOutput.js";
import {
AI_TOOL_CALL_MIME_TYPE,
AI_TOOL_RESULT_MIME_TYPE,
APPLICATION_MIME_TYPES,
IMAGE_MIME_TYPES,
JUPYTER_MIME_TYPES,
- MediaContainer,
- OutputData,
TEXT_MIME_TYPES,
isArtifactContainer,
isInlineContainer,
} from "@runtimed/schema";
-import PlainTextOutput from "./PlainTextOutput";
-import { RichOutputContent } from "./RichOutputContent";
+import type { MediaContainer, OutputData } from "@runtimed/schema";
+import PlainTextOutput from "./PlainTextOutput.js";
+import { RichOutputContent } from "./RichOutputContent.js";
// Dynamic imports for heavy components
const MarkdownRenderer = React.lazy(() =>
- import("@/components/outputs/shared-with-iframe/MarkdownRenderer").then(
- (m) => ({
- default: m.MarkdownRenderer,
- })
- )
+ import("./MarkdownRenderer.js").then((m) => ({
+ default: m.MarkdownRenderer,
+ }))
);
/**
@@ -175,6 +169,8 @@ export const SingleOutput: React.FC<{
);
}
+ // outputType is now always "multimedia_display" or "multimedia_result"
+
return (
diff --git a/src/components/outputs/shared-with-iframe/SuspenseSpinner.tsx b/packages/components/src/outputs/SuspenseSpinner.tsx
similarity index 83%
rename from src/components/outputs/shared-with-iframe/SuspenseSpinner.tsx
rename to packages/components/src/outputs/SuspenseSpinner.tsx
index 3c9953dfc..f63436511 100644
--- a/src/components/outputs/shared-with-iframe/SuspenseSpinner.tsx
+++ b/packages/components/src/outputs/SuspenseSpinner.tsx
@@ -1,5 +1,5 @@
import { Suspense } from "react";
-import { Spinner, type SpinnerSize } from "../../ui/Spinner";
+import { Spinner, type SpinnerSize } from "../ui/Spinner.js";
import React from "react";
import { useTimeout } from "react-use";
@@ -11,7 +11,6 @@ export function DelayedSpinner({
size?: SpinnerSize;
delay?: number;
}) {
- // eslint-disable-next-line react-compiler/react-compiler
"use no memo";
const [isReady] = useTimeout(delay);
diff --git a/src/components/outputs/shared-with-iframe/SvgOutput.tsx b/packages/components/src/outputs/SvgOutput.tsx
similarity index 86%
rename from src/components/outputs/shared-with-iframe/SvgOutput.tsx
rename to packages/components/src/outputs/SvgOutput.tsx
index a39466bdd..1f56f8d97 100644
--- a/src/components/outputs/shared-with-iframe/SvgOutput.tsx
+++ b/packages/components/src/outputs/SvgOutput.tsx
@@ -1,4 +1,4 @@
-import { throwIfNotInIframe } from "@/util/iframe";
+import { throwIfNotInIframe } from "../utils/iframe.js";
import React from "react";
interface SvgOutputProps {
diff --git a/packages/components/src/outputs/SyntaxHighlighter.tsx b/packages/components/src/outputs/SyntaxHighlighter.tsx
new file mode 100644
index 000000000..f4e83d5eb
--- /dev/null
+++ b/packages/components/src/outputs/SyntaxHighlighter.tsx
@@ -0,0 +1,82 @@
+import React from "react";
+import { Prism as PrismHighlighter } from "react-syntax-highlighter";
+import { oneLight } from "react-syntax-highlighter/dist/esm/styles/prism";
+import { Check, Copy } from "lucide-react";
+
+export interface SyntaxHighlighterProps {
+ /** Code content to highlight */
+ children: string;
+ /** Programming language for syntax highlighting */
+ language?: string;
+ /** Show copy button on hover (default: true) */
+ enableCopy?: boolean;
+ /** Custom styles to apply to the code block */
+ customStyle?: React.CSSProperties;
+ /** Additional class name for the container */
+ className?: string;
+ /** Show line numbers (default: false) */
+ showLineNumbers?: boolean;
+}
+
+const defaultStyle: React.CSSProperties = {
+ margin: 0,
+ padding: "0.75rem",
+ fontSize: "0.875rem",
+ overflow: "auto",
+ background: "#fafafa",
+ borderRadius: "0.375rem",
+};
+
+/**
+ * Syntax highlighter component using Prism.
+ * Supports copy-to-clipboard and configurable styling.
+ */
+export const SyntaxHighlighter: React.FC = ({
+ children,
+ language = "",
+ enableCopy = true,
+ customStyle,
+ className,
+ showLineNumbers = false,
+}) => {
+ const [copied, setCopied] = React.useState(false);
+
+ const handleCopy = async () => {
+ try {
+ await navigator.clipboard.writeText(children);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ } catch (err) {
+ console.error("Failed to copy code:", err);
+ }
+ };
+
+ return (
+
+
+ {children}
+
+ {enableCopy && (
+
+ )}
+
+ );
+};
+
+export default SyntaxHighlighter;
diff --git a/src/components/outputs/shared-with-iframe/VerifiedImage.tsx b/packages/components/src/outputs/VerifiedImage.tsx
similarity index 100%
rename from src/components/outputs/shared-with-iframe/VerifiedImage.tsx
rename to packages/components/src/outputs/VerifiedImage.tsx
diff --git a/src/components/outputs/shared-with-iframe/comms.ts b/packages/components/src/outputs/comms.ts
similarity index 94%
rename from src/components/outputs/shared-with-iframe/comms.ts
rename to packages/components/src/outputs/comms.ts
index 78da92a87..7b2e4747e 100644
--- a/src/components/outputs/shared-with-iframe/comms.ts
+++ b/packages/components/src/outputs/comms.ts
@@ -1,7 +1,7 @@
// NOTE: code here is shared between the iframe and the parent page.
// It's done to colocate types to ensure typesafety across the two bundles.
-import { OutputData } from "@runtimed/schema";
+import type { OutputData } from "@runtimed/schema";
import { useEffect, useRef, useState } from "react";
type UpdateOutputsEvent = {
@@ -156,13 +156,14 @@ export function useIframeCommsChild() {
sendHeight();
// Handle incoming content updates
- window.addEventListener("message", (event: MessageEvent) => {
+ const messageHandler = (event: MessageEvent) => {
const data = event.data;
if (data && data.type === "update-outputs") {
setOutputs(data.outputs || []);
setTimeout(sendHeight, 50);
}
- });
+ };
+ window.addEventListener("message", messageHandler);
// After the MutationObserver setup
const resizeObserver = new ResizeObserver(sendHeight);
@@ -176,7 +177,7 @@ export function useIframeCommsChild() {
// Fire once all current fonts are ready
(document as any).fonts.ready
.then(() => {
- if (document.fonts.size > 0) {
+ if ((document.fonts as any).size > 0) {
sendHeight();
}
})
@@ -184,6 +185,7 @@ export function useIframeCommsChild() {
}
return () => {
+ window.removeEventListener("message", messageHandler);
resizeObserver.disconnect();
document.removeEventListener("load", sendHeight, true);
};
diff --git a/src/components/outputs/shared-with-iframe/geojson/GeoJsonMapOutput.tsx b/packages/components/src/outputs/geojson/GeoJsonMapOutput.tsx
similarity index 95%
rename from src/components/outputs/shared-with-iframe/geojson/GeoJsonMapOutput.tsx
rename to packages/components/src/outputs/geojson/GeoJsonMapOutput.tsx
index 5128fb58a..de658f889 100644
--- a/src/components/outputs/shared-with-iframe/geojson/GeoJsonMapOutput.tsx
+++ b/packages/components/src/outputs/geojson/GeoJsonMapOutput.tsx
@@ -7,8 +7,8 @@ import {
} from "maplibre-react-components";
import { useId, useMemo } from "react";
import { useMeasure } from "react-use";
-import { mapFitFeatures2, normalizeData } from "./geojson-utils";
-import { MapFeature } from "./MapFeature";
+import { mapFitFeatures2, normalizeData } from "./geojson-utils.js";
+import { MapFeature } from "./MapFeature.js";
interface GeoJsonMapProps {
data: unknown;
diff --git a/src/components/outputs/shared-with-iframe/geojson/MapFeature.tsx b/packages/components/src/outputs/geojson/MapFeature.tsx
similarity index 100%
rename from src/components/outputs/shared-with-iframe/geojson/MapFeature.tsx
rename to packages/components/src/outputs/geojson/MapFeature.tsx
diff --git a/src/components/outputs/shared-with-iframe/geojson/geojson-utils.ts b/packages/components/src/outputs/geojson/geojson-utils.ts
similarity index 98%
rename from src/components/outputs/shared-with-iframe/geojson/geojson-utils.ts
rename to packages/components/src/outputs/geojson/geojson-utils.ts
index dc24d6899..00908c750 100644
--- a/src/components/outputs/shared-with-iframe/geojson/geojson-utils.ts
+++ b/packages/components/src/outputs/geojson/geojson-utils.ts
@@ -109,6 +109,7 @@ export function mapFitFeatures2(
return mapFitFeatures(data, size, options);
} catch (error) {
// Fallback to no zoom if there's an error
+ console.error("Error fitting features", error);
return noZoomResult;
}
}
diff --git a/packages/components/src/styles.css b/packages/components/src/styles.css
new file mode 100644
index 000000000..2df935927
--- /dev/null
+++ b/packages/components/src/styles.css
@@ -0,0 +1,318 @@
+@import "tailwindcss";
+@plugin "@tailwindcss/typography";
+
+@source ".";
+
+@custom-variant dark (&:is(.dark *));
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 222.2 84% 4.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 84% 4.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 84% 4.9%;
+ --primary: 222.2 47.4% 11.2%;
+ --primary-foreground: 210 40% 98%;
+ --secondary: 210 40% 96%;
+ --secondary-foreground: 222.2 84% 4.9%;
+ --muted: 210 40% 96%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+ --accent: 210 40% 96%;
+ --accent-foreground: 222.2 84% 4.9%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 214.3 31.8% 91.4%;
+ --input: 214.3 31.8% 91.4%;
+ --ring: 222.2 84% 4.9%;
+ --radius: 0.5rem;
+ }
+
+ .dark {
+ --background: 222.2 84% 4.9%;
+ --foreground: 210 40% 98%;
+ --card: 222.2 84% 4.9%;
+ --card-foreground: 210 40% 98%;
+ --popover: 222.2 84% 4.9%;
+ --popover-foreground: 210 40% 98%;
+ --primary: 210 40% 98%;
+ --primary-foreground: 222.2 47.4% 11.2%;
+ --secondary: 217.2 32.6% 17.5%;
+ --secondary-foreground: 210 40% 98%;
+ --muted: 217.2 32.6% 17.5%;
+ --muted-foreground: 215 20.2% 65.1%;
+ --accent: 217.2 32.6% 17.5%;
+ --accent-foreground: 210 40% 98%;
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+ --ring: 212.7 26.8% 83.9%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
+
+@theme inline {
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-popover: var(--popover);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+ --color-chart-1: var(--chart-1);
+ --color-chart-2: var(--chart-2);
+ --color-chart-3: var(--chart-3);
+ --color-chart-4: var(--chart-4);
+ --color-chart-5: var(--chart-5);
+ --color-sidebar: var(--sidebar);
+ --color-sidebar-foreground: var(--sidebar-foreground);
+ --color-sidebar-primary: var(--sidebar-primary);
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
+ --color-sidebar-accent: var(--sidebar-accent);
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+ --color-sidebar-border: var(--sidebar-border);
+ --color-sidebar-ring: var(--sidebar-ring);
+}
+
+/*
+Breakpoints taken from:
+https://tailwindcss.com/docs/theme#:~:text=%2D%2Dbreakpoint%2Dsm%3A%2040,%3A%2048rem%3B
+Tailwind breakpoints don't show up as CSS vars unless they're explicity defined.
+*/
+:root {
+ --breakpoint-sm: 40rem;
+ --breakpoint-md: 48rem;
+ --breakpoint-lg: 64rem;
+ --breakpoint-xl: 80rem;
+ --breakpoint-2xl: 96rem;
+ --sidebar: hsl(0 0% 98%);
+ --sidebar-foreground: hsl(240 5.3% 26.1%);
+ --sidebar-primary: hsl(240 5.9% 10%);
+ --sidebar-primary-foreground: hsl(0 0% 98%);
+ --sidebar-accent: hsl(240 4.8% 95.9%);
+ --sidebar-accent-foreground: hsl(240 5.9% 10%);
+ --sidebar-border: hsl(220 13% 91%);
+ --sidebar-ring: hsl(217.2 91.2% 59.8%);
+}
+
+:root {
+ --radius: 0.625rem;
+ --background: oklch(1 0 0);
+ --foreground: oklch(0.13 0.028 261.692);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.13 0.028 261.692);
+ --popover: oklch(1 0 0);
+ --popover-foreground: oklch(0.13 0.028 261.692);
+ --primary: oklch(0.21 0.034 264.665);
+ --primary-foreground: oklch(0.985 0.002 247.839);
+ --secondary: oklch(0.967 0.003 264.542);
+ --secondary-foreground: oklch(0.21 0.034 264.665);
+ --muted: oklch(0.967 0.003 264.542);
+ --muted-foreground: oklch(0.551 0.027 264.364);
+ --accent: oklch(0.967 0.003 264.542);
+ --accent-foreground: oklch(0.21 0.034 264.665);
+ --destructive: oklch(0.577 0.245 27.325);
+ --border: oklch(0.928 0.006 264.531);
+ --input: oklch(0.928 0.006 264.531);
+ --ring: oklch(0.707 0.022 261.325);
+ --chart-1: oklch(0.646 0.222 41.116);
+ --chart-2: oklch(0.6 0.118 184.704);
+ --chart-3: oklch(0.398 0.07 227.392);
+ --chart-4: oklch(0.828 0.189 84.429);
+ --chart-5: oklch(0.769 0.188 70.08);
+ --sidebar: oklch(0.985 0.002 247.839);
+ --sidebar-foreground: oklch(0.13 0.028 261.692);
+ --sidebar-primary: oklch(0.21 0.034 264.665);
+ --sidebar-primary-foreground: oklch(0.985 0.002 247.839);
+ --sidebar-accent: oklch(0.967 0.003 264.542);
+ --sidebar-accent-foreground: oklch(0.21 0.034 264.665);
+ --sidebar-border: oklch(0.928 0.006 264.531);
+ --sidebar-ring: oklch(0.707 0.022 261.325);
+}
+
+.dark {
+ --background: oklch(0.13 0.028 261.692);
+ --foreground: oklch(0.985 0.002 247.839);
+ --card: oklch(0.21 0.034 264.665);
+ --card-foreground: oklch(0.985 0.002 247.839);
+ --popover: oklch(0.21 0.034 264.665);
+ --popover-foreground: oklch(0.985 0.002 247.839);
+ --primary: oklch(0.928 0.006 264.531);
+ --primary-foreground: oklch(0.21 0.034 264.665);
+ --secondary: oklch(0.278 0.033 256.848);
+ --secondary-foreground: oklch(0.985 0.002 247.839);
+ --muted: oklch(0.278 0.033 256.848);
+ --muted-foreground: oklch(0.707 0.022 261.325);
+ --accent: oklch(0.278 0.033 256.848);
+ --accent-foreground: oklch(0.985 0.002 247.839);
+ --destructive: oklch(0.704 0.191 22.216);
+ --border: oklch(1 0 0 / 10%);
+ --input: oklch(1 0 0 / 15%);
+ --ring: oklch(0.551 0.027 264.364);
+ --chart-1: oklch(0.488 0.243 264.376);
+ --chart-2: oklch(0.696 0.17 162.48);
+ --chart-3: oklch(0.769 0.188 70.08);
+ --chart-4: oklch(0.627 0.265 303.9);
+ --chart-5: oklch(0.645 0.246 16.439);
+ --sidebar: oklch(0.21 0.034 264.665);
+ --sidebar-foreground: oklch(0.985 0.002 247.839);
+ --sidebar-primary: oklch(0.488 0.243 264.376);
+ --sidebar-primary-foreground: oklch(0.985 0.002 247.839);
+ --sidebar-accent: oklch(0.278 0.033 256.848);
+ --sidebar-accent-foreground: oklch(0.985 0.002 247.839);
+ --sidebar-border: oklch(1 0 0 / 10%);
+ --sidebar-ring: oklch(0.551 0.027 264.364);
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
+
+/* Component-specific styles */
+
+/* DataFrame styling for pandas HTML output */
+.dataframe-container table.dataframe {
+ border-collapse: collapse;
+ margin: 0;
+ font-size: 0.875rem;
+ width: 100%;
+ background-color: white;
+}
+
+.dataframe-container table.dataframe th,
+.dataframe-container table.dataframe td {
+ border: 1px solid #dee2e6;
+ padding: 8px 12px;
+ text-align: left;
+}
+
+.dataframe-container table.dataframe thead th {
+ background-color: #f8f9fa;
+ font-weight: 600;
+ border-bottom: 2px solid #dee2e6;
+}
+
+.dataframe-container table.dataframe tbody tr:nth-child(even) {
+ background-color: #f9f9f9;
+}
+
+.dataframe-container table.dataframe tbody tr:hover {
+ background-color: #f5f5f5;
+}
+
+.dataframe-container table.dataframe tbody tr th {
+ background-color: #f8f9fa;
+ font-weight: 500;
+ text-align: right;
+}
+
+/* Handle text alignment from pandas */
+.dataframe-container table.dataframe thead th[style*="text-align: right"] {
+ text-align: right;
+}
+
+.dataframe-container table.dataframe tbody td {
+ vertical-align: top;
+}
+
+/* Override any scoped styles that might interfere */
+.dataframe-container .dataframe tbody tr th:only-of-type {
+ vertical-align: middle;
+}
+
+.dataframe-container .dataframe tbody tr th {
+ vertical-align: top;
+}
+
+.dataframe-container .dataframe thead th {
+ text-align: right;
+}
+
+/* Make sure the table is responsive */
+.dataframe-container {
+ overflow-x: auto;
+ max-width: 100%;
+}
+
+/* Image responsiveness and zoom styles */
+.rich-output img {
+ max-width: 100% !important;
+ height: auto !important;
+ display: block;
+}
+
+/* Constrain large images on mobile */
+@media (max-width: 640px) {
+ .rich-output img {
+ max-height: 300px !important;
+ object-fit: contain;
+ }
+
+ .rich-output svg {
+ max-width: 100% !important;
+ max-height: 300px !important;
+ }
+}
+
+/* SVG responsiveness */
+.rich-output svg {
+ max-width: 100%;
+ height: auto;
+}
+
+/* Zoom modal styles */
+.zoom-modal {
+ backdrop-filter: blur(4px);
+}
+
+.zoom-modal img {
+ max-width: 95vw;
+ max-height: 95vh;
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
+}
+
+/* Hover effects for zoomable images */
+.zoomable-image {
+ transition: all 0.2s ease;
+}
+
+.zoomable-image:hover {
+ transform: scale(1.02);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+
+/* Style numeric columns to align right by default */
+.dataframe-container table.dataframe td:last-child,
+.dataframe-container table.dataframe td[style*="text-align: right"] {
+ text-align: right;
+}
diff --git a/packages/components/src/ui/Spinner.tsx b/packages/components/src/ui/Spinner.tsx
new file mode 100644
index 000000000..877023270
--- /dev/null
+++ b/packages/components/src/ui/Spinner.tsx
@@ -0,0 +1,23 @@
+import { cn } from "../utils/cn.js";
+import { LoaderIcon, type LucideProps } from "lucide-react";
+
+export type SpinnerSize = "sm" | "md" | "lg";
+
+type SpinnerProps = LucideProps & {
+ size?: SpinnerSize;
+};
+
+export const Spinner = ({ className, size = "sm", ...props }: SpinnerProps) => (
+
+);
diff --git a/packages/components/src/ui/button.tsx b/packages/components/src/ui/button.tsx
new file mode 100644
index 000000000..957f6dacf
--- /dev/null
+++ b/packages/components/src/ui/button.tsx
@@ -0,0 +1,74 @@
+import * as React from "react";
+import { Slot } from "@radix-ui/react-slot";
+import { cva, type VariantProps } from "class-variance-authority";
+
+import { cn } from "../utils/cn.js";
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+ {
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90 border-1 border-transparent",
+ success: "bg-green-500 text-white shadow-xs hover:bg-green-600",
+ destructive:
+ "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+ outline:
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
+ ghost:
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
+ destructiveGhost:
+ "text-destructive hover:bg-destructive/10 hover:text-destructive focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:hover:bg-destructive/20",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
+ xs: "h-6 px-2 has-[>svg]:px-1.5",
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
+ xl: "h-12 rounded-lg text-md px-6 has-[>svg]:px-5",
+ icon: "size-9",
+ },
+ // Default `focus-visible` means we only show the focus ring AFTER a user tabs to it
+ // But sometimes we want to show the focus right away
+ showFocusRing: {
+ true: "focus:border-ring focus:ring-ring/50 focus:ring-[3px]",
+ false: "",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+);
+
+function Button({
+ className,
+ variant,
+ size,
+ asChild = false,
+ showFocusRing = false,
+ ...props
+}: React.ComponentProps<"button"> &
+ VariantProps & {
+ asChild?: boolean;
+ showFocusRing?: boolean;
+ }) {
+ const Comp = asChild ? Slot : "button";
+
+ return (
+
+ );
+}
+
+export { Button, buttonVariants };
diff --git a/packages/components/src/ui/card.tsx b/packages/components/src/ui/card.tsx
new file mode 100644
index 000000000..11b2adf11
--- /dev/null
+++ b/packages/components/src/ui/card.tsx
@@ -0,0 +1,92 @@
+import * as React from "react";
+
+import { cn } from "../utils/cn.js";
+
+function Card({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardAction({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardContent({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+export {
+ Card,
+ CardAction,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+};
diff --git a/packages/components/src/utils/cn.ts b/packages/components/src/utils/cn.ts
new file mode 100644
index 000000000..365058ceb
--- /dev/null
+++ b/packages/components/src/utils/cn.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from "clsx";
+import { twMerge } from "tailwind-merge";
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/packages/components/src/utils/iframe.ts b/packages/components/src/utils/iframe.ts
new file mode 100644
index 000000000..d222f0ff9
--- /dev/null
+++ b/packages/components/src/utils/iframe.ts
@@ -0,0 +1,9 @@
+function throwIfNotInIframe() {
+ if (window.self === window.top) {
+ throw new Error(
+ "This content must be rendered inside an iframe for security reasons."
+ );
+ }
+}
+
+export { throwIfNotInIframe };
diff --git a/packages/components/src/utils/output-grouping.ts b/packages/components/src/utils/output-grouping.ts
new file mode 100644
index 000000000..738a31448
--- /dev/null
+++ b/packages/components/src/utils/output-grouping.ts
@@ -0,0 +1,95 @@
+import type { OutputData } from "@runtimed/schema";
+
+/**
+ * Groups consecutive terminal outputs of the same type (stdout/stderr) into single outputs
+ * and consecutive markdown outputs into single outputs.
+ * This improves readability by avoiding fragmented output.
+ * @param outputs - Array of output data objects sorted by position
+ * @returns Array with consecutive outputs of the same type merged into single outputs
+ */
+function isTerminalOutput(output: OutputData): boolean {
+ return output.outputType === "terminal" && typeof output.data === "string";
+}
+
+function isMarkdownOutput(output: OutputData): boolean {
+ return output.outputType === "markdown" && typeof output.data === "string";
+}
+
+export function groupConsecutiveStreamOutputs(
+ outputs: readonly OutputData[]
+): OutputData[] {
+ const result: OutputData[] = [];
+
+ for (let i = 0; i < outputs.length; i++) {
+ const currentOutput = outputs[i];
+
+ // Handle terminal outputs
+ if (isTerminalOutput(currentOutput)) {
+ // Start collecting consecutive terminal outputs of the same stream type
+ const currentStreamName = currentOutput.streamName;
+ const textParts = [currentOutput.data as string];
+
+ // Look ahead for more consecutive terminal outputs of the same type
+ let j = i + 1;
+ while (j < outputs.length) {
+ const nextOutput = outputs[j];
+
+ if (!isTerminalOutput(nextOutput)) {
+ break;
+ }
+
+ // Only group outputs of the same stream type (stdout/stderr)
+ if (nextOutput.streamName !== currentStreamName) {
+ break;
+ }
+
+ textParts.push(nextOutput.data as string);
+ j++;
+ }
+
+ // Create the merged output with concatenated text
+ const mergedOutput: OutputData = {
+ ...currentOutput,
+ data: textParts.join(""),
+ };
+
+ result.push(mergedOutput);
+ i = j - 1; // Skip all the outputs we just merged (will be incremented by for loop)
+ continue;
+ }
+
+ // Handle markdown outputs
+ if (isMarkdownOutput(currentOutput)) {
+ // Start collecting consecutive markdown outputs
+ const textParts = [currentOutput.data as string];
+
+ // Look ahead for more consecutive markdown outputs
+ let j = i + 1;
+ while (j < outputs.length) {
+ const nextOutput = outputs[j];
+
+ if (!isMarkdownOutput(nextOutput)) {
+ break;
+ }
+
+ textParts.push(nextOutput.data as string);
+ j++;
+ }
+
+ // Create the merged output with concatenated text
+ const mergedOutput: OutputData = {
+ ...currentOutput,
+ data: textParts.join(""),
+ };
+
+ result.push(mergedOutput);
+ i = j - 1; // Skip all the outputs we just merged (will be incremented by for loop)
+ continue;
+ }
+
+ // If it's not a terminal or markdown output, just add it as-is
+ result.push(currentOutput);
+ }
+
+ return result;
+}
diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json
new file mode 100644
index 000000000..0cc4554a1
--- /dev/null
+++ b/packages/components/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "lib": ["ES2020", "DOM"],
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "jsx": "react-jsx",
+ "strict": true,
+ "declaration": true,
+ "declarationMap": true,
+ "skipLibCheck": true
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules", "**/*.test.ts", "**/*.spec.ts", "../schema"]
+}
diff --git a/packages/components/tsdown.config.ts b/packages/components/tsdown.config.ts
new file mode 100644
index 000000000..9a274761a
--- /dev/null
+++ b/packages/components/tsdown.config.ts
@@ -0,0 +1,33 @@
+import { defineConfig } from "tsdown";
+
+export default defineConfig({
+ entry: ["src/index.ts"],
+ format: ["esm", "cjs"],
+ dts: true,
+ sourcemap: true,
+ clean: true,
+ external: [
+ "react",
+ "react-dom",
+ "@uiw/react-json-view",
+ "@radix-ui/react-slot",
+ "@runtimed/schema",
+ "ansi-to-react",
+ "class-variance-authority",
+ "clsx",
+ "geojson-map-fit-mercator",
+ "katex",
+ "lucide-react",
+ "maplibre-gl",
+ "maplibre-react-components",
+ "react-markdown",
+ "react-syntax-highlighter",
+ "react-use",
+ "rehype-katex",
+ "rehype-raw",
+ "remark-gfm",
+ "remark-math",
+ "tailwind-merge",
+ ],
+ platform: "browser",
+});
diff --git a/packages/notebook-preview/demo.html b/packages/notebook-preview/demo.html
new file mode 100644
index 000000000..5971d56af
--- /dev/null
+++ b/packages/notebook-preview/demo.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Output Types Demo
+
+
+
+
+
+
diff --git a/packages/notebook-preview/index.html b/packages/notebook-preview/index.html
new file mode 100644
index 000000000..6135342e7
--- /dev/null
+++ b/packages/notebook-preview/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Notebook Preview
+
+
+
+
+
+
diff --git a/packages/notebook-preview/package.json b/packages/notebook-preview/package.json
new file mode 100644
index 000000000..d59b0be3b
--- /dev/null
+++ b/packages/notebook-preview/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@runtimed/notebook-preview",
+ "version": "0.3.0-beta.1",
+ "type": "module",
+ "scripts": {
+ "dev": "vite build --watch --mode development",
+ "prebuild": "pnpm --filter @runtimed/components build",
+ "build": "vite build",
+ "preview": "vite --port 5175",
+ "type-check": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@runtimed/components": "workspace:*",
+ "react": "19.2.1",
+ "react-dom": "19.2.1"
+ },
+ "devDependencies": {
+ "@tailwindcss/vite": "^4.1.10",
+ "@types/react": "^19.1.8",
+ "@types/react-dom": "^19.1.6",
+ "@vitejs/plugin-react": "^4.5.2",
+ "tailwindcss": "^4.1.10",
+ "typescript": "^5.8.3",
+ "vite": "^6.3.5"
+ },
+ "engines": {
+ "node": ">=23.0.0",
+ "pnpm": ">=10.9.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/notebook-preview/public/health.html b/packages/notebook-preview/public/health.html
new file mode 100644
index 000000000..5a495500f
--- /dev/null
+++ b/packages/notebook-preview/public/health.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+ Health Check
+
+
+
+ Health Check
+
+ Put this page in an iframe and send a message to the iframe to check
+ health.
+
+ iframe.contentWindow?.postMessage({ healthy: true }, "*");
+ Messages received:
+ No message received
+
+
+
diff --git a/packages/notebook-preview/react.html b/packages/notebook-preview/react.html
new file mode 100644
index 000000000..a5df9d5f9
--- /dev/null
+++ b/packages/notebook-preview/react.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ React IFrame Content
+
+
+
+
+
+
diff --git a/packages/notebook-preview/src/App.tsx b/packages/notebook-preview/src/App.tsx
new file mode 100644
index 000000000..a4c45d722
--- /dev/null
+++ b/packages/notebook-preview/src/App.tsx
@@ -0,0 +1,39 @@
+import { useState, useEffect } from "react";
+import { NotebookRenderer, type JupyterNotebook } from "./NotebookRenderer";
+
+export function App() {
+ const [notebookData, setNotebookData] = useState(
+ null
+ );
+
+ useEffect(() => {
+ // Notify parent that iframe is ready
+ window.parent.postMessage({ type: "iframe-loaded" }, "*");
+
+ // Accept messages from any origin
+ const handleMessage = (event: MessageEvent) => {
+ if (event.data?.json !== undefined) {
+ const json =
+ typeof event.data.json === "string"
+ ? JSON.parse(event.data.json)
+ : event.data.json;
+ setNotebookData(json as JupyterNotebook);
+ // Scroll to top when new notebook data arrives
+ window.scrollTo({ top: 0, behavior: "instant" });
+ }
+ };
+
+ window.addEventListener("message", handleMessage);
+ return () => window.removeEventListener("message", handleMessage);
+ }, []);
+
+ if (!notebookData) {
+ return (
+
+ Waiting for notebook data...
+
+ );
+ }
+
+ return ;
+}
diff --git a/packages/notebook-preview/src/NotebookRenderer.tsx b/packages/notebook-preview/src/NotebookRenderer.tsx
new file mode 100644
index 000000000..32e1d250a
--- /dev/null
+++ b/packages/notebook-preview/src/NotebookRenderer.tsx
@@ -0,0 +1,266 @@
+import type { OutputData, OutputType } from "@runtimed/schema";
+import {
+ SingleOutput,
+ OutputsContainer,
+ SuspenseSpinner,
+ ExecutionCount,
+ SyntaxHighlighter,
+} from "@runtimed/components";
+
+// Jupyter notebook types
+export interface JupyterOutput {
+ output_type: "stream" | "execute_result" | "display_data" | "error";
+ name?: string; // stdout, stderr for stream
+ text?: string | string[];
+ data?: Record;
+ metadata?: Record;
+ execution_count?: number | null;
+ ename?: string;
+ evalue?: string;
+ traceback?: string[];
+}
+
+export interface JupyterCell {
+ id: string;
+ cell_type: "code" | "markdown" | "raw";
+ source: string | string[];
+ outputs?: JupyterOutput[];
+ execution_count?: number | null;
+}
+
+export interface JupyterNotebook {
+ cells: JupyterCell[];
+ metadata?: Record;
+ nbformat?: number;
+ nbformat_minor?: number;
+}
+
+// Helper to join source lines
+function joinSource(source: string | string[]): string {
+ return Array.isArray(source) ? source.join("") : source;
+}
+
+// Convert Jupyter output to OutputData format
+function convertJupyterOutput(
+ output: JupyterOutput,
+ cellId: string,
+ position: number
+): OutputData | null {
+ const baseOutput = {
+ id: `${cellId}-output-${position}`,
+ cellId,
+ position,
+ streamName: null,
+ executionCount: null,
+ displayId: null,
+ artifactId: null,
+ mimeType: null,
+ metadata: null,
+ representations: null,
+ data: null,
+ };
+
+ switch (output.output_type) {
+ case "stream": {
+ const text = Array.isArray(output.text)
+ ? output.text.join("")
+ : output.text || "";
+ return {
+ ...baseOutput,
+ outputType: "terminal" as OutputType,
+ streamName: (output.name as "stdout" | "stderr") || "stdout",
+ data: text,
+ } as OutputData;
+ }
+
+ case "execute_result":
+ case "display_data": {
+ const outputData = output.data || {};
+ const representations: Record =
+ {};
+
+ for (const [mimeType, content] of Object.entries(outputData)) {
+ const data = Array.isArray(content) ? content.join("") : content;
+ representations[mimeType] = { type: "inline", data };
+ }
+
+ // Get primary mime type
+ const mimeTypes = Object.keys(representations);
+ const primaryMimeType =
+ mimeTypes.find((m) => m.startsWith("text/html")) ||
+ mimeTypes.find((m) => m.startsWith("image/")) ||
+ mimeTypes.find((m) => m === "application/json") ||
+ mimeTypes.find((m) => m === "text/plain") ||
+ mimeTypes[0];
+
+ return {
+ ...baseOutput,
+ outputType:
+ output.output_type === "execute_result"
+ ? ("multimedia_result" as OutputType)
+ : ("multimedia_display" as OutputType),
+ executionCount: output.execution_count ?? null,
+ mimeType: primaryMimeType || null,
+ representations,
+ data: representations["text/plain"]?.data?.toString() || null,
+ } as OutputData;
+ }
+
+ case "error": {
+ return {
+ ...baseOutput,
+ outputType: "error" as OutputType,
+ data: JSON.stringify({
+ ename: output.ename || "Error",
+ evalue: output.evalue || "",
+ traceback: output.traceback || [],
+ }),
+ } as OutputData;
+ }
+
+ default:
+ return null;
+ }
+}
+
+// Extract language from notebook metadata
+function getNotebookLanguage(metadata?: Record): string {
+ // Try kernelspec name first (e.g., "python3", "ir", "julia")
+ const kernelspec = metadata?.kernelspec as
+ | { language?: string; name?: string }
+ | undefined;
+ if (kernelspec?.language) {
+ return kernelspec.language;
+ }
+ // Try language_info (more detailed)
+ const languageInfo = metadata?.language_info as { name?: string } | undefined;
+ if (languageInfo?.name) {
+ return languageInfo.name;
+ }
+ // Default to python
+ return "python";
+}
+
+// Code cell component
+function CodeCell({ cell, language }: { cell: JupyterCell; language: string }) {
+ const source = joinSource(cell.source);
+ const outputs = (cell.outputs || [])
+ .map((output, i) => convertJupyterOutput(output, cell.id, i))
+ .filter((o): o is OutputData => o !== null);
+
+ return (
+
+ {/* Execution count badge */}
+
+
+
+
+
+ {/* Input */}
+
+
+ {source}
+
+
+
+ {/* Outputs */}
+ {outputs.length > 0 && (
+
+
+
+ {outputs.map((output) => (
+
+ ))}
+
+
+
+ )}
+
+
+
+ );
+}
+
+// Markdown cell component
+function MarkdownCell({ cell }: { cell: JupyterCell }) {
+ const source = joinSource(cell.source);
+
+ // For markdown cells, render the output using SingleOutput with markdown type
+ const markdownOutput: OutputData = {
+ id: `${cell.id}-markdown`,
+ cellId: cell.id,
+ outputType: "markdown",
+ position: 0,
+ streamName: null,
+ executionCount: null,
+ displayId: null,
+ artifactId: null,
+ mimeType: null,
+ metadata: null,
+ representations: null,
+ data: source,
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+// Raw cell component
+function RawCell({ cell }: { cell: JupyterCell }) {
+ const source = joinSource(cell.source);
+
+ return (
+
+
+
+
+
+ {source}
+
+
+
+
+ );
+}
+
+// Notebook renderer
+export function NotebookRenderer({ notebook }: { notebook: JupyterNotebook }) {
+ const language = getNotebookLanguage(notebook.metadata);
+
+ return (
+
+ {notebook.cells.map((cell, index) => {
+ switch (cell.cell_type) {
+ case "code":
+ return (
+
+ );
+ case "markdown":
+ return ;
+ case "raw":
+ return ;
+ default:
+ return null;
+ }
+ })}
+
+ );
+}
diff --git a/packages/notebook-preview/src/demo-main.tsx b/packages/notebook-preview/src/demo-main.tsx
new file mode 100644
index 000000000..fa5bfb348
--- /dev/null
+++ b/packages/notebook-preview/src/demo-main.tsx
@@ -0,0 +1,12 @@
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import "./style.css";
+import "@runtimed/components/styles.css";
+
+import { OutputTypesDemoPage } from "@runtimed/components";
+
+createRoot(document.getElementById("root")!).render(
+
+
+
+);
diff --git a/packages/notebook-preview/src/main.tsx b/packages/notebook-preview/src/main.tsx
new file mode 100644
index 000000000..72f39d1eb
--- /dev/null
+++ b/packages/notebook-preview/src/main.tsx
@@ -0,0 +1,11 @@
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import { App } from "./App";
+import "./style.css";
+import "@runtimed/components/styles.css";
+
+createRoot(document.getElementById("root")!).render(
+
+
+
+);
diff --git a/packages/notebook-preview/src/react-main.tsx b/packages/notebook-preview/src/react-main.tsx
new file mode 100644
index 000000000..bb6a13abe
--- /dev/null
+++ b/packages/notebook-preview/src/react-main.tsx
@@ -0,0 +1,30 @@
+import { createRoot } from "react-dom/client";
+import { IframeReactApp, sendFromIframe } from "@runtimed/components";
+import "./style.css";
+import "@runtimed/components/styles.css";
+
+// Main React initialization for iframe outputs
+function initializeReactIframe() {
+ const container = document.getElementById("react-root");
+ if (!container) {
+ console.error("React root element not found");
+ return;
+ }
+
+ const root = createRoot(container);
+ root.render( );
+
+ // Send iframe loaded message
+ sendFromIframe({ type: "iframe-loaded" });
+}
+
+document.addEventListener("dblclick", () => {
+ sendFromIframe({ type: "iframe-double-click" });
+});
+
+// Initialize when DOM is ready
+if (document.readyState === "loading") {
+ document.addEventListener("DOMContentLoaded", initializeReactIframe);
+} else {
+ initializeReactIframe();
+}
diff --git a/packages/notebook-preview/src/style.css b/packages/notebook-preview/src/style.css
new file mode 100644
index 000000000..a9656f3cd
--- /dev/null
+++ b/packages/notebook-preview/src/style.css
@@ -0,0 +1,50 @@
+@import "tailwindcss";
+
+@source "../node_modules/@runtimed/components/src";
+
+:root {
+ --radius: 0.625rem;
+ --background: oklch(1 0 0);
+ --foreground: oklch(0.13 0.028 261.692);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.13 0.028 261.692);
+ --popover: oklch(1 0 0);
+ --popover-foreground: oklch(0.13 0.028 261.692);
+ --primary: oklch(0.21 0.034 264.665);
+ --primary-foreground: oklch(0.985 0.002 247.839);
+ --secondary: oklch(0.967 0.003 264.542);
+ --secondary-foreground: oklch(0.21 0.034 264.665);
+ --muted: oklch(0.967 0.003 264.542);
+ --muted-foreground: oklch(0.551 0.027 264.364);
+ --accent: oklch(0.967 0.003 264.542);
+ --accent-foreground: oklch(0.21 0.034 264.665);
+ --destructive: oklch(0.577 0.245 27.325);
+ --border: oklch(0.928 0.006 264.531);
+ --input: oklch(0.928 0.006 264.531);
+ --ring: oklch(0.707 0.022 261.325);
+}
+
+@theme inline {
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-popover: var(--popover);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+}
diff --git a/packages/notebook-preview/tsconfig.json b/packages/notebook-preview/tsconfig.json
new file mode 100644
index 000000000..a4c834a6c
--- /dev/null
+++ b/packages/notebook-preview/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"]
+}
diff --git a/packages/notebook-preview/vite.config.ts b/packages/notebook-preview/vite.config.ts
new file mode 100644
index 000000000..bb59aef6b
--- /dev/null
+++ b/packages/notebook-preview/vite.config.ts
@@ -0,0 +1,28 @@
+import { defineConfig } from "vite";
+import { resolve } from "path";
+import react from "@vitejs/plugin-react";
+import tailwindcss from "@tailwindcss/vite";
+
+export default defineConfig({
+ plugins: [tailwindcss(), react()],
+ base: "./",
+ // resolve: {
+ // alias: {
+ // "@runtimed/components/styles.css": resolve(
+ // __dirname,
+ // "../components/dist/styles.css"
+ // ),
+ // },
+ // },
+ build: {
+ outDir: "dist",
+ emptyOutDir: true,
+ rollupOptions: {
+ input: {
+ index: resolve(__dirname, "index.html"),
+ react: resolve(__dirname, "react.html"),
+ health: resolve(__dirname, "demo.html"),
+ },
+ },
+ },
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d310522b2..663c17b8e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -133,6 +133,9 @@ importers:
'@runtimed/ai-core':
specifier: workspace:*
version: link:packages/ai-core
+ '@runtimed/components':
+ specifier: workspace:*
+ version: link:packages/components
'@runtimed/pyodide-runtime':
specifier: workspace:*
version: link:packages/pyodide-runtime
@@ -397,7 +400,7 @@ importers:
version: 0.6.14(prettier@3.6.2)
rollup-plugin-visualizer:
specifier: ^6.0.3
- version: 6.0.3(rollup@4.45.0)
+ version: 6.0.3(rolldown@1.0.0-rc.1)(rollup@4.45.0)
tailwindcss:
specifier: ^4.1.10
version: 4.1.11
@@ -491,6 +494,143 @@ importers:
specifier: ^5.8.3
version: 5.8.3
+ packages/components:
+ dependencies:
+ '@radix-ui/react-slot':
+ specifier: ^1.2.3
+ version: 1.2.3(@types/react@19.1.8)(react@19.2.1)
+ '@runtimed/schema':
+ specifier: workspace:*
+ version: link:../schema
+ '@uiw/react-json-view':
+ specifier: 2.0.0-alpha.40
+ version: 2.0.0-alpha.40(@babel/runtime@7.27.6)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ ansi-to-react:
+ specifier: ^6.1.6
+ version: 6.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ class-variance-authority:
+ specifier: ^0.7.1
+ version: 0.7.1
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
+ geojson-map-fit-mercator:
+ specifier: ^1.1.0
+ version: 1.1.0
+ katex:
+ specifier: ^0.16.22
+ version: 0.16.22
+ lucide-react:
+ specifier: ^0.545.0
+ version: 0.545.0(react@19.2.1)
+ maplibre-gl:
+ specifier: ^5.7.1
+ version: 5.7.1
+ maplibre-react-components:
+ specifier: ^0.2.6
+ version: 0.2.6(maplibre-gl@5.7.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ react-error-boundary:
+ specifier: ^6.0.0
+ version: 6.0.0(react@19.2.1)
+ react-markdown:
+ specifier: ^10.1.0
+ version: 10.1.0(@types/react@19.1.8)(react@19.2.1)
+ react-syntax-highlighter:
+ specifier: ^15.6.1
+ version: 15.6.1(react@19.2.1)
+ react-use:
+ specifier: ^17.6.0
+ version: 17.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ rehype-katex:
+ specifier: ^7.0.1
+ version: 7.0.1
+ rehype-raw:
+ specifier: ^7.0.0
+ version: 7.0.0
+ remark-gfm:
+ specifier: ^4.0.1
+ version: 4.0.1
+ remark-math:
+ specifier: ^6.0.0
+ version: 6.0.0
+ tailwind-merge:
+ specifier: ^3.3.1
+ version: 3.3.1
+ devDependencies:
+ '@tailwindcss/cli':
+ specifier: ^4.1.10
+ version: 4.1.11
+ '@types/node':
+ specifier: ^24.0.10
+ version: 24.0.13
+ '@types/react':
+ specifier: ^19.1.8
+ version: 19.1.8
+ '@types/react-dom':
+ specifier: ^19.1.6
+ version: 19.1.6(@types/react@19.1.8)
+ '@types/react-syntax-highlighter':
+ specifier: ^15.5.13
+ version: 15.5.13
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.35.1
+ version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)
+ '@typescript-eslint/parser':
+ specifier: ^8.35.1
+ version: 8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)
+ eslint:
+ specifier: ^9.30.1
+ version: 9.31.0(jiti@2.4.2)
+ prettier:
+ specifier: ^3.6.0
+ version: 3.6.2
+ react:
+ specifier: 19.2.1
+ version: 19.2.1
+ react-dom:
+ specifier: 19.2.1
+ version: 19.2.1(react@19.2.1)
+ tsdown:
+ specifier: 0.20.0-beta.1
+ version: 0.20.0-beta.1(typescript@5.8.3)
+ typescript:
+ specifier: ^5.8.3
+ version: 5.8.3
+
+ packages/notebook-preview:
+ dependencies:
+ '@runtimed/components':
+ specifier: workspace:*
+ version: link:../components
+ react:
+ specifier: 19.2.1
+ version: 19.2.1
+ react-dom:
+ specifier: 19.2.1
+ version: 19.2.1(react@19.2.1)
+ devDependencies:
+ '@tailwindcss/vite':
+ specifier: ^4.1.10
+ version: 4.1.11(vite@6.3.5(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.21.0)(yaml@2.8.1))
+ '@types/react':
+ specifier: ^19.1.8
+ version: 19.1.8
+ '@types/react-dom':
+ specifier: ^19.1.6
+ version: 19.1.6(@types/react@19.1.8)
+ '@vitejs/plugin-react':
+ specifier: ^4.5.2
+ version: 4.6.0(vite@6.3.5(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.21.0)(yaml@2.8.1))
+ tailwindcss:
+ specifier: ^4.1.10
+ version: 4.1.11
+ typescript:
+ specifier: ^5.8.3
+ version: 5.8.3
+ vite:
+ specifier: ^6.3.5
+ version: 6.3.5(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.21.0)(yaml@2.8.1)
+
packages/pyodide-runtime:
dependencies:
'@runtimed/agent-core':
@@ -579,6 +719,10 @@ packages:
resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==}
engines: {node: '>=6.9.0'}
+ '@babel/generator@8.0.0-beta.4':
+ resolution: {integrity: sha512-5xRfRZk6wx1BRu2XnTE8cTh2mx1ixrZ3/vpn7p/RCJpgctL6pexVVHE3eqtwlYvHhPAuOYCAlnsAyXpBdmfh5Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+
'@babel/helper-annotate-as-pure@7.27.3':
resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==}
engines: {node: '>=6.9.0'}
@@ -633,10 +777,18 @@ packages:
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
+ '@babel/helper-string-parser@8.0.0-rc.1':
+ resolution: {integrity: sha512-vi/pfmbrOtQmqgfboaBhaCU50G7mcySVu69VU8z+lYoPPB6WzI9VgV7WQfL908M4oeSH5fDkmoupIqoE0SdApw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+
'@babel/helper-validator-identifier@7.27.1':
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
engines: {node: '>=6.9.0'}
+ '@babel/helper-validator-identifier@8.0.0-rc.1':
+ resolution: {integrity: sha512-I4YnARytXC2RzkLNVnf5qFNFMzp679qZpmtw/V3Jt2uGnWiIxyJtaukjG7R8pSx8nG2NamICpGfljQsogj+FbQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+
'@babel/helper-validator-option@7.27.1':
resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
engines: {node: '>=6.9.0'}
@@ -650,6 +802,11 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
+ '@babel/parser@8.0.0-beta.4':
+ resolution: {integrity: sha512-fBcUqUN3eenLyg25QFkOwY1lmV6L0RdG92g6gxyS2CVCY8kHdibkQz1+zV3bLzxcvNnfHoi3i9n5Dci+g93acg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
'@babel/plugin-proposal-private-methods@7.18.6':
resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==}
engines: {node: '>=6.9.0'}
@@ -685,6 +842,10 @@ packages:
resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==}
engines: {node: '>=6.9.0'}
+ '@babel/types@8.0.0-beta.4':
+ resolution: {integrity: sha512-xjk2xqYp25ePzAs0I08hN2lrbUDDQFfCjwq6MIEa8HwHa0WK8NfNtdvtXod8Ku2CbE1iui7qwWojGvjQiyrQeA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+
'@cloudflare/kv-asset-handler@0.4.0':
resolution: {integrity: sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==}
engines: {node: '>=18.0.0'}
@@ -957,9 +1118,18 @@ packages:
effect: 3.15.5
vitest: ^3.0.0
+ '@emnapi/core@1.8.1':
+ resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==}
+
'@emnapi/runtime@1.4.4':
resolution: {integrity: sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==}
+ '@emnapi/runtime@1.8.1':
+ resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==}
+
+ '@emnapi/wasi-threads@1.1.0':
+ resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==}
+
'@esbuild/aix-ppc64@0.25.4':
resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==}
engines: {node: '>=18'}
@@ -1796,6 +1966,9 @@ packages:
cpu: [x64]
os: [win32]
+ '@napi-rs/wasm-runtime@1.1.1':
+ resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==}
+
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -1833,6 +2006,12 @@ packages:
peerDependencies:
react: 19.2.1
+ '@oxc-project/types@0.107.0':
+ resolution: {integrity: sha512-QFDRbYfV2LVx8tyqtyiah3jQPUj1mK2+RYwxyFWyGoys6XJnwTdlzO6rdNNHOPorHAu5Uo34oWRKcvNpbJarmQ==}
+
+ '@oxc-project/types@0.110.0':
+ resolution: {integrity: sha512-6Ct21OIlrEnFEJk5LT4e63pk3btsI6/TusD/GStLi7wYlGJNOl1GI9qvXAnRAxQU9zqA2Oz+UwhfTOU2rPZVow==}
+
'@parcel/watcher-android-arm64@2.5.1':
resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
engines: {node: '>= 10.0.0'}
@@ -1941,6 +2120,9 @@ packages:
'@poppinss/exception@1.2.2':
resolution: {integrity: sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==}
+ '@quansync/fs@1.0.0':
+ resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==}
+
'@radix-ui/number@1.1.1':
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
@@ -2475,9 +2657,169 @@ packages:
react: 19.2.1
react-dom: 19.2.1
+ '@rolldown/binding-android-arm64@1.0.0-beta.59':
+ resolution: {integrity: sha512-6yLLgyswYwiCfls9+hoNFY9F8TQdwo15hpXDHzlAR0X/GojeKF+AuNcXjYNbOJ4zjl/5D6lliE8CbpB5t1OWIQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@rolldown/binding-android-arm64@1.0.0-rc.1':
+ resolution: {integrity: sha512-He6ZoCfv5D7dlRbrhNBkuMVIHd0GDnjJwbICE1OWpG7G3S2gmJ+eXkcNLJjzjNDpeI2aRy56ou39AJM9AD8YFA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@rolldown/binding-darwin-arm64@1.0.0-beta.59':
+ resolution: {integrity: sha512-hqGXRc162qCCIOAcHN2Cw4eXiVTwYsMFLOhAy1IG2CxY+dwc/l4Ga+dLPkLor3Ikqy5WDn+7kxHbbh6EmshEpQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rolldown/binding-darwin-arm64@1.0.0-rc.1':
+ resolution: {integrity: sha512-YzJdn08kSOXnj85ghHauH2iHpOJ6eSmstdRTLyaziDcUxe9SyQJgGyx/5jDIhDvtOcNvMm2Ju7m19+S/Rm1jFg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rolldown/binding-darwin-x64@1.0.0-beta.59':
+ resolution: {integrity: sha512-ezvvGuhteE15JmMhJW0wS7BaXmhwLy1YHeEwievYaPC1PgGD86wgBKfOpHr9tSKllAXbCe0BeeMvasscWLhKdA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rolldown/binding-darwin-x64@1.0.0-rc.1':
+ resolution: {integrity: sha512-cIvAbqM+ZVV6lBSKSBtlNqH5iCiW933t1q8j0H66B3sjbe8AxIRetVqfGgcHcJtMzBIkIALlL9fcDrElWLJQcQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rolldown/binding-freebsd-x64@1.0.0-beta.59':
+ resolution: {integrity: sha512-4fhKVJiEYVd5n6no/mrL3LZ9kByfCGwmONOrdtvx8DJGDQhehH/q3RfhG3V/4jGKhpXgbDjpIjkkFdybCTcgew==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rolldown/binding-freebsd-x64@1.0.0-rc.1':
+ resolution: {integrity: sha512-rVt+B1B/qmKwCl1XD02wKfgh3vQPXRXdB/TicV2w6g7RVAM1+cZcpigwhLarqiVCxDObFZ7UgXCxPC7tpDoRog==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.59':
+ resolution: {integrity: sha512-T3Y52sW6JAhvIqArBw+wtjNU1Ieaz4g0NBxyjSJoW971nZJBZygNlSYx78G4cwkCmo1dYTciTPDOnQygLV23pA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.1':
+ resolution: {integrity: sha512-69YKwJJBOFprQa1GktPgbuBOfnn+EGxu8sBJ1TjPER+zhSpYeaU4N07uqmyBiksOLGXsMegymuecLobfz03h8Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.59':
+ resolution: {integrity: sha512-NIW40jQDSQap2KDdmm9z3B/4OzWJ6trf8dwx3FD74kcQb3v34ThsBFTtzE5KjDuxnxgUlV+DkAu+XgSMKrgufw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.1':
+ resolution: {integrity: sha512-9JDhHUf3WcLfnViFWm+TyorqUtnSAHaCzlSNmMOq824prVuuzDOK91K0Hl8DUcEb9M5x2O+d2/jmBMsetRIn3g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm64-musl@1.0.0-beta.59':
+ resolution: {integrity: sha512-CCKEk+H+8c0WGe/8n1E20n85Tq4Pv+HNAbjP1KfUXW+01aCWSMjU56ChNrM2tvHnXicfm7QRNoZyfY8cWh7jLQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm64-musl@1.0.0-rc.1':
+ resolution: {integrity: sha512-UvApLEGholmxw/HIwmUnLq3CwdydbhaHHllvWiCTNbyGom7wTwOtz5OAQbAKZYyiEOeIXZNPkM7nA4Dtng7CLw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rolldown/binding-linux-x64-gnu@1.0.0-beta.59':
+ resolution: {integrity: sha512-VlfwJ/HCskPmQi8R0JuAFndySKVFX7yPhE658o27cjSDWWbXVtGkSbwaxstii7Q+3Rz87ZXN+HLnb1kd4R9Img==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@rolldown/binding-linux-x64-gnu@1.0.0-rc.1':
+ resolution: {integrity: sha512-uVctNgZHiGnJx5Fij7wHLhgw4uyZBVi6mykeWKOqE7bVy9Hcxn0fM/IuqdMwk6hXlaf9fFShDTFz2+YejP+x0A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@rolldown/binding-linux-x64-musl@1.0.0-beta.59':
+ resolution: {integrity: sha512-kuO92hTRyGy0Ts3Nsqll0rfO8eFsEJe9dGQGktkQnZ2hrJrDVN0y419dMgKy/gB2S2o7F2dpWhpfQOBehZPwVA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@rolldown/binding-linux-x64-musl@1.0.0-rc.1':
+ resolution: {integrity: sha512-T6Eg0xWwcxd/MzBcuv4Z37YVbUbJxy5cMNnbIt/Yr99wFwli30O4BPlY8hKeGyn6lWNtU0QioBS46lVzDN38bg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@rolldown/binding-openharmony-arm64@1.0.0-beta.59':
+ resolution: {integrity: sha512-PXAebvNL4sYfCqi8LdY4qyFRacrRoiPZLo3NoUmiTxm7MPtYYR8CNtBGNokqDmMuZIQIecRaD/jbmFAIDz7DxQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rolldown/binding-openharmony-arm64@1.0.0-rc.1':
+ resolution: {integrity: sha512-PuGZVS2xNJyLADeh2F04b+Cz4NwvpglbtWACgrDOa5YDTEHKwmiTDjoD5eZ9/ptXtcpeFrMqD2H4Zn33KAh1Eg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rolldown/binding-wasm32-wasi@1.0.0-beta.59':
+ resolution: {integrity: sha512-yJoklQg7XIZq8nAg0bbkEXcDK6sfpjxQGxpg2Nd6ERNtvg+eOaEBRgPww0BVTrYFQzje1pB5qPwC2VnJHT3koQ==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@rolldown/binding-wasm32-wasi@1.0.0-rc.1':
+ resolution: {integrity: sha512-2mOxY562ihHlz9lEXuaGEIDCZ1vI+zyFdtsoa3M62xsEunDXQE+DVPO4S4x5MPK9tKulG/aFcA/IH5eVN257Cw==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.59':
+ resolution: {integrity: sha512-ljZ4+McmCbIuZwEBaoGtiG8Rq2nJjaXEnLEIx+usWetXn1ECjXY0LAhkELxOV6ytv4ensEmoJJ8nXg47hRMjlw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.1':
+ resolution: {integrity: sha512-oQVOP5cfAWZwRD0Q3nGn/cA9FW3KhMMuQ0NIndALAe6obqjLhqYVYDiGGRGrxvnjJsVbpLwR14gIUYnpIcHR1g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rolldown/binding-win32-x64-msvc@1.0.0-beta.59':
+ resolution: {integrity: sha512-bMY4tTIwbdZljW+xe/ln1hvs0SRitahQSXfWtvgAtIzgSX9Ar7KqJzU7lRm33YTRFIHLULRi53yNjw9nJGd6uQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@rolldown/binding-win32-x64-msvc@1.0.0-rc.1':
+ resolution: {integrity: sha512-Ydsxxx++FNOuov3wCBPaYjZrEvKOOGq3k+BF4BPridhg2pENfitSRD2TEuQ8i33bp5VptuNdC9IzxRKU031z5A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
'@rolldown/pluginutils@1.0.0-beta.19':
resolution: {integrity: sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==}
+ '@rolldown/pluginutils@1.0.0-beta.59':
+ resolution: {integrity: sha512-aoh6LAJRyhtazs98ydgpNOYstxUlsOV1KJXcpf/0c0vFcUA8uyd/hwKRhqE/AAPNqAho9RliGsvitCoOzREoVA==}
+
+ '@rolldown/pluginutils@1.0.0-rc.1':
+ resolution: {integrity: sha512-UTBjtTxVOhodhzFVp/ayITaTETRHPUPYZPXQe0WU0wOgxghMojXxYjOiPOauKIYNWJAWS2fd7gJgGQK8GU8vDA==}
+
'@rollup/plugin-replace@6.0.2':
resolution: {integrity: sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==}
engines: {node: '>=14.0.0'}
@@ -2832,6 +3174,9 @@ packages:
'@turf/transform-rotate@7.2.0':
resolution: {integrity: sha512-EMCj0Zqy3cF9d3mGRqDlYnX2ZBXe3LgT+piDR0EuF5c5sjuKErcFcaBIsn/lg1gp4xCNZFinkZ3dsFfgGHf6fw==}
+ '@tybys/wasm-util@0.10.1':
+ resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
+
'@types/aria-query@5.0.4':
resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
@@ -2886,6 +3231,9 @@ packages:
'@types/js-cookie@3.0.6':
resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==}
+ '@types/jsesc@2.5.1':
+ resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==}
+
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
@@ -3015,6 +3363,13 @@ packages:
react: 19.2.1
react-dom: 19.2.1
+ '@uiw/react-json-view@2.0.0-alpha.40':
+ resolution: {integrity: sha512-j8YgmUrLAokX0k3TJC1+Rae3G2XS2hTYA9SsnQVWeQpn/PiqxwG8mI4A5TCASUTltPtpM/9Yp+mRm7L4Wjy8rw==}
+ peerDependencies:
+ '@babel/runtime': '>=7.10.0'
+ react: 19.2.1
+ react-dom: 19.2.1
+
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
@@ -3129,6 +3484,10 @@ packages:
resolution: {integrity: sha512-BRrU0Bo1X9dFGw6KgGz6hWrqQuOlVEDOzkb0QSLZY9sXHqA7pNj7yHPVJRz7y/rj4EOJ3d/D5uxH+ee9leYgsg==}
engines: {node: '>=10'}
+ ansis@4.2.0:
+ resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==}
+ engines: {node: '>=14'}
+
anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
@@ -3151,6 +3510,10 @@ packages:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
+ ast-kit@3.0.0-beta.1:
+ resolution: {integrity: sha512-trmleAnZ2PxN/loHWVhhx1qeOHSRXq4TDsBBxq3GqeJitfk3+jTQ+v/C1km/KYq9M7wKqCewMh+/NAvVH7m+bw==}
+ engines: {node: '>=20.19.0'}
+
ast-types@0.13.4:
resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
engines: {node: '>=4'}
@@ -3192,6 +3555,9 @@ packages:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
+ birpc@4.0.0:
+ resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==}
+
blake3-wasm@2.1.5:
resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==}
@@ -3490,6 +3856,15 @@ packages:
dom-accessibility-api@0.6.3:
resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+ dts-resolver@2.1.3:
+ resolution: {integrity: sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ oxc-resolver: '>=11.0.0'
+ peerDependenciesMeta:
+ oxc-resolver:
+ optional: true
+
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
@@ -3506,6 +3881,10 @@ packages:
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+ empathic@2.0.0:
+ resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==}
+ engines: {node: '>=14'}
+
enhanced-resolve@5.18.2:
resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==}
engines: {node: '>=10.13.0'}
@@ -3964,6 +4343,9 @@ packages:
resolution: {integrity: sha512-qfvdJ42t6CQE0N/iSCa8KsW8SQqYD67YB+TYbwPHlnALvX+s7ynh8otR1NEk5jXtUg73gpV/B82OSufDmwtX3w==}
engines: {node: '>=16.9.0'}
+ hookable@6.0.1:
+ resolution: {integrity: sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==}
+
html-encoding-sniffer@3.0.0:
resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
engines: {node: '>=12'}
@@ -4019,6 +4401,10 @@ packages:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
+ import-without-cache@0.2.5:
+ resolution: {integrity: sha512-B6Lc2s6yApwnD2/pMzFh/d5AVjdsDXjgkeJ766FmFuJELIGHNycKRj+l3A39yZPM4CchqNCB4RITEAYB1KUM6A==}
+ engines: {node: '>=20.19.0'}
+
imurmurhash@0.1.4:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
@@ -4619,6 +5005,9 @@ packages:
obliterator@2.0.5:
resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==}
+ obug@2.1.1:
+ resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==}
+
ohash@2.0.11:
resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
@@ -4906,6 +5295,9 @@ packages:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
+ quansync@1.0.0:
+ resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==}
+
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@@ -5097,6 +5489,35 @@ packages:
robust-predicates@2.0.4:
resolution: {integrity: sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==}
+ rolldown-plugin-dts@0.21.8:
+ resolution: {integrity: sha512-czOQoe6eZpRKCv9P+ijO/v4A2TwQjASAV7qezUxRZSua06Yb2REPIZv/mbfXiZDP1ZfI7Ez7re7qfK9F9u0Epw==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@ts-macro/tsc': ^0.3.6
+ '@typescript/native-preview': '>=7.0.0-dev.20250601.1'
+ rolldown: ^1.0.0-beta.57
+ typescript: ^5.0.0
+ vue-tsc: ~3.2.0
+ peerDependenciesMeta:
+ '@ts-macro/tsc':
+ optional: true
+ '@typescript/native-preview':
+ optional: true
+ typescript:
+ optional: true
+ vue-tsc:
+ optional: true
+
+ rolldown@1.0.0-beta.59:
+ resolution: {integrity: sha512-Slm000Gd8/AO9z4Kxl4r8mp/iakrbAuJ1L+7ddpkNxgQ+Vf37WPvY63l3oeyZcfuPD1DRrUYBsRPIXSOhvOsmw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
+ rolldown@1.0.0-rc.1:
+ resolution: {integrity: sha512-M3AeZjYE6UclblEf531Hch0WfVC/NOL43Cc+WdF3J50kk5/fvouHhDumSGTh0oRjbZ8C4faaVr5r6Nx1xMqDGg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
rollup-plugin-visualizer@6.0.3:
resolution: {integrity: sha512-ZU41GwrkDcCpVoffviuM9Clwjy5fcUxlz0oMoTXTYsK+tcIFzbdacnrr2n8TXcHxbGKKXtOdjxM2HUS4HjkwIw==}
engines: {node: '>=18'}
@@ -5166,6 +5587,11 @@ packages:
engines: {node: '>=10'}
hasBin: true
+ semver@7.7.3:
+ resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
+ engines: {node: '>=10'}
+ hasBin: true
+
set-cookie-parser@2.7.1:
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
@@ -5375,6 +5801,10 @@ packages:
tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
+ tinyexec@1.0.2:
+ resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==}
+ engines: {node: '>=18'}
+
tinyglobby@0.2.14:
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
engines: {node: '>=12.0.0'}
@@ -5415,6 +5845,10 @@ packages:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'}
+ tree-kill@1.2.2:
+ resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
+ hasBin: true
+
trim-lines@3.0.1:
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
@@ -5434,6 +5868,31 @@ packages:
resolution: {integrity: sha512-wzGQE1zzOVM7BVyJ/vd1Ato3YQOOMg5Yv591jDe1oWpAX5bOFtpNd4XzR/EtVNWlBATJhO905WCAwJQuhQuxeQ==}
engines: {node: '>=20'}
+ tsdown@0.20.0-beta.1:
+ resolution: {integrity: sha512-F/A0vGKG8/8XSKMxQtHCeRHpI2jlpKQLQ8sEDKh3XXWZjAXyz5pjHosJvsL5NEv+9ZtcQZEw0xEzPgfJSAWgdg==}
+ engines: {node: '>=20.19.0'}
+ hasBin: true
+ peerDependencies:
+ '@arethetypeswrong/core': ^0.18.1
+ '@vitejs/devtools': '*'
+ publint: ^0.3.0
+ typescript: ^5.0.0
+ unplugin-lightningcss: ^0.4.0
+ unplugin-unused: ^0.5.0
+ peerDependenciesMeta:
+ '@arethetypeswrong/core':
+ optional: true
+ '@vitejs/devtools':
+ optional: true
+ publint:
+ optional: true
+ typescript:
+ optional: true
+ unplugin-lightningcss:
+ optional: true
+ unplugin-unused:
+ optional: true
+
tslib@1.9.3:
resolution: {integrity: sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==}
@@ -5475,6 +5934,9 @@ packages:
resolution: {integrity: sha512-dPJyqPzx8preQhqq24bBG1YNkvigm87K8kVEHCD+ruZg24t6IFEFv00xMWfxcC4djmFtiTLdFuADn4+DOz6R7Q==}
hasBin: true
+ unconfig-core@7.4.2:
+ resolution: {integrity: sha512-VgPCvLWugINbXvMQDf8Jh0mlbvNjNC6eSUziHsBCMpxR05OPrNrvDnyatdMjRgcHaaNsCqz+wjNXxNw1kRLHUg==}
+
undici-types@7.8.0:
resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==}
@@ -5520,6 +5982,16 @@ packages:
unist-util-visit@5.0.0:
resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
+ unrun@0.2.26:
+ resolution: {integrity: sha512-A3DQLBcDyTui4Hlaoojkldg+8x+CIR+tcSHY0wzW+CgB4X/DNyH58jJpXp1B/EkE+yG6tU8iH1mWsLtwFU3IQg==}
+ engines: {node: '>=20.19.0'}
+ hasBin: true
+ peerDependencies:
+ synckit: ^0.11.11
+ peerDependenciesMeta:
+ synckit:
+ optional: true
+
update-browserslist-db@1.1.3:
resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
hasBin: true
@@ -5918,6 +6390,15 @@ snapshots:
'@jridgewell/trace-mapping': 0.3.29
jsesc: 3.1.0
+ '@babel/generator@8.0.0-beta.4':
+ dependencies:
+ '@babel/parser': 8.0.0-beta.4
+ '@babel/types': 8.0.0-beta.4
+ '@jridgewell/gen-mapping': 0.3.12
+ '@jridgewell/trace-mapping': 0.3.29
+ '@types/jsesc': 2.5.1
+ jsesc: 3.1.0
+
'@babel/helper-annotate-as-pure@7.27.3':
dependencies:
'@babel/types': 7.28.1
@@ -5992,8 +6473,12 @@ snapshots:
'@babel/helper-string-parser@7.27.1': {}
+ '@babel/helper-string-parser@8.0.0-rc.1': {}
+
'@babel/helper-validator-identifier@7.27.1': {}
+ '@babel/helper-validator-identifier@8.0.0-rc.1': {}
+
'@babel/helper-validator-option@7.27.1': {}
'@babel/helpers@7.27.6':
@@ -6005,6 +6490,10 @@ snapshots:
dependencies:
'@babel/types': 7.28.1
+ '@babel/parser@8.0.0-beta.4':
+ dependencies:
+ '@babel/types': 8.0.0-beta.4
+
'@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.28.0)':
dependencies:
'@babel/core': 7.28.0
@@ -6048,6 +6537,11 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
+ '@babel/types@8.0.0-beta.4':
+ dependencies:
+ '@babel/helper-string-parser': 8.0.0-rc.1
+ '@babel/helper-validator-identifier': 8.0.0-rc.1
+
'@cloudflare/kv-asset-handler@0.4.0':
dependencies:
mime: 3.0.0
@@ -6356,11 +6850,27 @@ snapshots:
effect: 3.15.5
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.0.13)(@vitest/ui@3.2.4)(happy-dom@15.11.7)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.21.0)(yaml@2.8.1)
+ '@emnapi/core@1.8.1':
+ dependencies:
+ '@emnapi/wasi-threads': 1.1.0
+ tslib: 2.8.1
+ optional: true
+
'@emnapi/runtime@1.4.4':
dependencies:
tslib: 2.8.1
optional: true
+ '@emnapi/runtime@1.8.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/wasi-threads@1.1.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
'@esbuild/aix-ppc64@0.25.4':
optional: true
@@ -7189,6 +7699,13 @@ snapshots:
'@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3':
optional: true
+ '@napi-rs/wasm-runtime@1.1.1':
+ dependencies:
+ '@emnapi/core': 1.8.1
+ '@emnapi/runtime': 1.8.1
+ '@tybys/wasm-util': 0.10.1
+ optional: true
+
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -7220,6 +7737,10 @@ snapshots:
dependencies:
react: 19.2.1
+ '@oxc-project/types@0.107.0': {}
+
+ '@oxc-project/types@0.110.0': {}
+
'@parcel/watcher-android-arm64@2.5.1':
optional: true
@@ -7344,6 +7865,10 @@ snapshots:
'@poppinss/exception@1.2.2': {}
+ '@quansync/fs@1.0.0':
+ dependencies:
+ quansync: 1.0.0
+
'@radix-ui/number@1.1.1': {}
'@radix-ui/primitive@1.1.2': {}
@@ -7896,8 +8421,94 @@ snapshots:
react: 19.2.1
react-dom: 19.2.1(react@19.2.1)
+ '@rolldown/binding-android-arm64@1.0.0-beta.59':
+ optional: true
+
+ '@rolldown/binding-android-arm64@1.0.0-rc.1':
+ optional: true
+
+ '@rolldown/binding-darwin-arm64@1.0.0-beta.59':
+ optional: true
+
+ '@rolldown/binding-darwin-arm64@1.0.0-rc.1':
+ optional: true
+
+ '@rolldown/binding-darwin-x64@1.0.0-beta.59':
+ optional: true
+
+ '@rolldown/binding-darwin-x64@1.0.0-rc.1':
+ optional: true
+
+ '@rolldown/binding-freebsd-x64@1.0.0-beta.59':
+ optional: true
+
+ '@rolldown/binding-freebsd-x64@1.0.0-rc.1':
+ optional: true
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.59':
+ optional: true
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.1':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.59':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.1':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-musl@1.0.0-beta.59':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-musl@1.0.0-rc.1':
+ optional: true
+
+ '@rolldown/binding-linux-x64-gnu@1.0.0-beta.59':
+ optional: true
+
+ '@rolldown/binding-linux-x64-gnu@1.0.0-rc.1':
+ optional: true
+
+ '@rolldown/binding-linux-x64-musl@1.0.0-beta.59':
+ optional: true
+
+ '@rolldown/binding-linux-x64-musl@1.0.0-rc.1':
+ optional: true
+
+ '@rolldown/binding-openharmony-arm64@1.0.0-beta.59':
+ optional: true
+
+ '@rolldown/binding-openharmony-arm64@1.0.0-rc.1':
+ optional: true
+
+ '@rolldown/binding-wasm32-wasi@1.0.0-beta.59':
+ dependencies:
+ '@napi-rs/wasm-runtime': 1.1.1
+ optional: true
+
+ '@rolldown/binding-wasm32-wasi@1.0.0-rc.1':
+ dependencies:
+ '@napi-rs/wasm-runtime': 1.1.1
+ optional: true
+
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.59':
+ optional: true
+
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.1':
+ optional: true
+
+ '@rolldown/binding-win32-x64-msvc@1.0.0-beta.59':
+ optional: true
+
+ '@rolldown/binding-win32-x64-msvc@1.0.0-rc.1':
+ optional: true
+
'@rolldown/pluginutils@1.0.0-beta.19': {}
+ '@rolldown/pluginutils@1.0.0-beta.59': {}
+
+ '@rolldown/pluginutils@1.0.0-rc.1': {}
+
'@rollup/plugin-replace@6.0.2(rollup@4.45.0)':
dependencies:
'@rollup/pluginutils': 5.2.0(rollup@4.45.0)
@@ -8271,6 +8882,11 @@ snapshots:
'@types/geojson': 7946.0.16
tslib: 2.8.1
+ '@tybys/wasm-util@0.10.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
'@types/aria-query@5.0.4': {}
'@types/babel__core@7.20.5':
@@ -8332,6 +8948,8 @@ snapshots:
'@types/js-cookie@3.0.6': {}
+ '@types/jsesc@2.5.1': {}
+
'@types/json-schema@7.0.15': {}
'@types/katex@0.16.7': {}
@@ -8502,6 +9120,12 @@ snapshots:
- '@codemirror/lint'
- '@codemirror/search'
+ '@uiw/react-json-view@2.0.0-alpha.40(@babel/runtime@7.27.6)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ react: 19.2.1
+ react-dom: 19.2.1(react@19.2.1)
+
'@ungap/structured-clone@1.3.0': {}
'@vitejs/plugin-react@4.6.0(vite@6.3.5(@types/node@24.0.13)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.21.0)(yaml@2.8.1))':
@@ -8626,6 +9250,8 @@ snapshots:
ansis@4.0.0-node10: {}
+ ansis@4.2.0: {}
+
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
@@ -8645,6 +9271,12 @@ snapshots:
assertion-error@2.0.1: {}
+ ast-kit@3.0.0-beta.1:
+ dependencies:
+ '@babel/parser': 8.0.0-beta.4
+ estree-walker: 3.0.3
+ pathe: 2.0.3
+
ast-types@0.13.4:
dependencies:
tslib: 2.8.1
@@ -8683,6 +9315,8 @@ snapshots:
binary-extensions@2.3.0: {}
+ birpc@4.0.0: {}
+
blake3-wasm@2.1.5: {}
blessed@0.1.81: {}
@@ -8954,6 +9588,8 @@ snapshots:
dom-accessibility-api@0.6.3: {}
+ dts-resolver@2.1.3: {}
+
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -8971,6 +9607,8 @@ snapshots:
emoji-regex@8.0.0: {}
+ empathic@2.0.0: {}
+
enhanced-resolve@5.18.2:
dependencies:
graceful-fs: 4.2.11
@@ -9549,6 +10187,8 @@ snapshots:
hono@4.9.1: {}
+ hookable@6.0.1: {}
+
html-encoding-sniffer@3.0.0:
dependencies:
whatwg-encoding: 2.0.0
@@ -9619,6 +10259,8 @@ snapshots:
parent-module: 1.0.1
resolve-from: 4.0.0
+ import-without-cache@0.2.5: {}
+
imurmurhash@0.1.4: {}
indent-string@4.0.0: {}
@@ -10400,6 +11042,8 @@ snapshots:
obliterator@2.0.5: {}
+ obug@2.1.1: {}
+
ohash@2.0.11: {}
ollama@0.5.18:
@@ -10688,6 +11332,8 @@ snapshots:
dependencies:
side-channel: 1.1.0
+ quansync@1.0.0: {}
+
queue-microtask@1.2.3: {}
quickselect@2.0.0: {}
@@ -10940,13 +11586,68 @@ snapshots:
robust-predicates@2.0.4: {}
- rollup-plugin-visualizer@6.0.3(rollup@4.45.0):
+ rolldown-plugin-dts@0.21.8(rolldown@1.0.0-beta.59)(typescript@5.8.3):
+ dependencies:
+ '@babel/generator': 8.0.0-beta.4
+ '@babel/parser': 8.0.0-beta.4
+ '@babel/types': 8.0.0-beta.4
+ ast-kit: 3.0.0-beta.1
+ birpc: 4.0.0
+ dts-resolver: 2.1.3
+ get-tsconfig: 4.13.0
+ obug: 2.1.1
+ rolldown: 1.0.0-beta.59
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - oxc-resolver
+
+ rolldown@1.0.0-beta.59:
+ dependencies:
+ '@oxc-project/types': 0.107.0
+ '@rolldown/pluginutils': 1.0.0-beta.59
+ optionalDependencies:
+ '@rolldown/binding-android-arm64': 1.0.0-beta.59
+ '@rolldown/binding-darwin-arm64': 1.0.0-beta.59
+ '@rolldown/binding-darwin-x64': 1.0.0-beta.59
+ '@rolldown/binding-freebsd-x64': 1.0.0-beta.59
+ '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.59
+ '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.59
+ '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.59
+ '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.59
+ '@rolldown/binding-linux-x64-musl': 1.0.0-beta.59
+ '@rolldown/binding-openharmony-arm64': 1.0.0-beta.59
+ '@rolldown/binding-wasm32-wasi': 1.0.0-beta.59
+ '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.59
+ '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.59
+
+ rolldown@1.0.0-rc.1:
+ dependencies:
+ '@oxc-project/types': 0.110.0
+ '@rolldown/pluginutils': 1.0.0-rc.1
+ optionalDependencies:
+ '@rolldown/binding-android-arm64': 1.0.0-rc.1
+ '@rolldown/binding-darwin-arm64': 1.0.0-rc.1
+ '@rolldown/binding-darwin-x64': 1.0.0-rc.1
+ '@rolldown/binding-freebsd-x64': 1.0.0-rc.1
+ '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.1
+ '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.1
+ '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.1
+ '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.1
+ '@rolldown/binding-linux-x64-musl': 1.0.0-rc.1
+ '@rolldown/binding-openharmony-arm64': 1.0.0-rc.1
+ '@rolldown/binding-wasm32-wasi': 1.0.0-rc.1
+ '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.1
+ '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.1
+
+ rollup-plugin-visualizer@6.0.3(rolldown@1.0.0-rc.1)(rollup@4.45.0):
dependencies:
open: 8.4.2
picomatch: 4.0.2
source-map: 0.7.4
yargs: 17.7.2
optionalDependencies:
+ rolldown: 1.0.0-rc.1
rollup: 4.45.0
rollup@4.45.0:
@@ -11013,6 +11714,8 @@ snapshots:
semver@7.7.2: {}
+ semver@7.7.3: {}
+
set-cookie-parser@2.7.1: {}
set-harmonic-interval@1.0.1: {}
@@ -11236,6 +11939,8 @@ snapshots:
tinyexec@0.3.2: {}
+ tinyexec@1.0.2: {}
+
tinyglobby@0.2.14:
dependencies:
fdir: 6.4.6(picomatch@4.0.2)
@@ -11266,6 +11971,8 @@ snapshots:
totalist@3.0.1: {}
+ tree-kill@1.2.2: {}
+
trim-lines@3.0.1: {}
trough@2.2.0: {}
@@ -11280,6 +11987,33 @@ snapshots:
dependencies:
type-fest: 5.1.0
+ tsdown@0.20.0-beta.1(typescript@5.8.3):
+ dependencies:
+ ansis: 4.2.0
+ cac: 6.7.14
+ defu: 6.1.4
+ empathic: 2.0.0
+ hookable: 6.0.1
+ import-without-cache: 0.2.5
+ obug: 2.1.1
+ picomatch: 4.0.3
+ rolldown: 1.0.0-beta.59
+ rolldown-plugin-dts: 0.21.8(rolldown@1.0.0-beta.59)(typescript@5.8.3)
+ semver: 7.7.3
+ tinyexec: 1.0.2
+ tinyglobby: 0.2.15
+ tree-kill: 1.2.2
+ unconfig-core: 7.4.2
+ unrun: 0.2.26
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - '@ts-macro/tsc'
+ - '@typescript/native-preview'
+ - oxc-resolver
+ - synckit
+ - vue-tsc
+
tslib@1.9.3: {}
tslib@2.8.1: {}
@@ -11314,6 +12048,11 @@ snapshots:
ulid@3.0.1: {}
+ unconfig-core@7.4.2:
+ dependencies:
+ '@quansync/fs': 1.0.0
+ quansync: 1.0.0
+
undici-types@7.8.0: {}
undici@7.11.0: {}
@@ -11383,6 +12122,10 @@ snapshots:
unist-util-is: 6.0.0
unist-util-visit-parents: 6.0.1
+ unrun@0.2.26:
+ dependencies:
+ rolldown: 1.0.0-rc.1
+
update-browserslist-db@1.1.3(browserslist@4.25.1):
dependencies:
browserslist: 4.25.1
diff --git a/src/components/notebook/cell/ExecutableCell.tsx b/src/components/notebook/cell/ExecutableCell.tsx
index 79b30dd6e..2c2abd6cb 100644
--- a/src/components/notebook/cell/ExecutableCell.tsx
+++ b/src/components/notebook/cell/ExecutableCell.tsx
@@ -43,7 +43,7 @@ import { SqlToolbar } from "./toolbars/SqlToolbar.js";
import { MaybeCellOutputs } from "@/components/outputs/MaybeCellOutputs.js";
import { useToolApprovals } from "@/hooks/useToolApprovals.js";
-import { AiToolApprovalOutput } from "../../outputs/shared-with-iframe/AiToolApprovalOutput.js";
+import { AiToolApprovalOutput } from "@runtimed/components";
import { cn } from "@/lib/utils.js";
import { generateQueueId } from "@/util/queue-id.js";
import { useTrpc } from "@/components/TrpcProvider.js";
diff --git a/src/components/notebook/cell/MarkdownCell.tsx b/src/components/notebook/cell/MarkdownCell.tsx
index 8a5e0dc00..8a41878cf 100644
--- a/src/components/notebook/cell/MarkdownCell.tsx
+++ b/src/components/notebook/cell/MarkdownCell.tsx
@@ -15,7 +15,7 @@ import React, {
useState,
} from "react";
-import { IframeOutput } from "@/components/outputs/IframeOutput.js";
+import { IframeOutput } from "@runtimed/components";
import { Button } from "@/components/ui/button.js";
import { useFeatureFlag } from "@/contexts/FeatureFlagContext.js";
import { useUserRegistry } from "@/hooks/useUserRegistry.js";
@@ -335,6 +335,7 @@ export const MarkdownCell: React.FC = ({
>
{/* Send markdown content to iframe */}
setIsEditing(true)}
onMarkdownRendered={() => setReadyToShowRendered(true)}
outputs={[
diff --git a/src/components/outputs/IframeOutput.tsx b/src/components/outputs/IframeOutput.tsx
index 30f9c3f21..5ec22afa0 100644
--- a/src/components/outputs/IframeOutput.tsx
+++ b/src/components/outputs/IframeOutput.tsx
@@ -1,80 +1,16 @@
-import { CellType, OutputData } from "@runtimed/schema";
-import { useState, useRef, useEffect } from "react";
-import { useDebounce } from "react-use";
-import { useIframeCommsParent } from "./shared-with-iframe/comms";
-
-export interface IframeOutputProps {
- outputs: OutputData[];
- style?: React.CSSProperties;
- className?: string;
- onHeightChange?: (height: number) => void;
- isReact?: boolean;
- defaultHeight?: string;
- onDoubleClick?: () => void;
- onMarkdownRendered?: () => void;
- cellType?: CellType;
-}
-
-export const IframeOutput: React.FC = ({
- outputs,
- className,
- style,
- isReact,
- onHeightChange,
- defaultHeight = "0px",
- onDoubleClick,
- onMarkdownRendered,
- cellType,
-}) => {
- const { iframeRef, iframeHeight } = useIframeCommsParent({
- defaultHeight,
- onHeightChange,
- outputs,
- onDoubleClick,
- onMarkdownRendered,
- });
-
- const [debouncedIframeHeight, setDebouncedIframeHeight] =
- useState(iframeHeight);
-
- // Iframe can get height updates pretty often, but we want to avoid layout jumping each time
- // TODO: ensure that it's a leading debounce!
- useDebounce(() => setDebouncedIframeHeight(iframeHeight), 50, [iframeHeight]);
-
- const isAiCell = cellType === "ai";
- const scrollContainerRef = useRef(null);
-
- // Auto-scroll to bottom when content changes for AI cells
- useEffect(() => {
- if (isAiCell && scrollContainerRef.current) {
- const container = scrollContainerRef.current;
- container.scrollTop = container.scrollHeight;
- }
- }, [isAiCell, outputs, debouncedIframeHeight]);
-
- const iframeElement = (
-
- );
-
- if (isAiCell) {
- return (
-
- {iframeElement}
-
- );
- }
-
- return iframeElement;
+import React from "react";
+import {
+ IframeOutput as BaseIframeOutput,
+ type IframeOutputProps,
+} from "@runtimed/components";
+
+/**
+ * Wrapper component that provides iframeUri from environment variable
+ */
+export const IframeOutput: React.FC> = (
+ props
+) => {
+ const iframeUri =
+ import.meta.env.VITE_IFRAME_OUTPUT_URI || "http://localhost:8000";
+ return ;
};
diff --git a/src/components/outputs/MaybeCellOutputs.tsx b/src/components/outputs/MaybeCellOutputs.tsx
index 2ae45cd45..821d69e51 100644
--- a/src/components/outputs/MaybeCellOutputs.tsx
+++ b/src/components/outputs/MaybeCellOutputs.tsx
@@ -4,9 +4,11 @@ import { CellType, OutputData, SAFE_MIME_TYPES } from "@runtimed/schema";
import { groupConsecutiveStreamOutputs } from "@/util/output-grouping";
import { useQuery } from "@livestore/react";
import { useMemo } from "react";
-import { SingleOutput } from "./shared-with-iframe/SingleOutput";
-import { OutputsContainer } from "./shared-with-iframe/OutputsContainer";
-import { SuspenseSpinner } from "./shared-with-iframe/SuspenseSpinner";
+import {
+ SingleOutput,
+ OutputsContainer,
+ SuspenseSpinner,
+} from "@runtimed/components";
import { MaybeFixCodeButton } from "./MaybeFixCodeButton";
import { IframeOutput } from "./IframeOutput";
diff --git a/src/components/outputs/MaybeFixCodeButton.tsx b/src/components/outputs/MaybeFixCodeButton.tsx
index 265074431..4965abe16 100644
--- a/src/components/outputs/MaybeFixCodeButton.tsx
+++ b/src/components/outputs/MaybeFixCodeButton.tsx
@@ -3,7 +3,7 @@ import { OutputData } from "@runtimed/schema";
import { Bug } from "lucide-react";
import { useCallback } from "react";
import { Button } from "../ui/button";
-import { OutputsContainer } from "./shared-with-iframe/OutputsContainer";
+import { OutputsContainer } from "@runtimed/components";
export function MaybeFixCodeButton({
isLoading,
diff --git a/src/components/outputs/index.ts b/src/components/outputs/index.ts
deleted file mode 100644
index f63a5516d..000000000
--- a/src/components/outputs/index.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-// Re-export AnsiOutput from the notebook folder for convenience
-import { AiToolCallData, AiToolResultData } from "@runtimed/schema";
-
-export { AiToolCallOutput } from "./shared-with-iframe/AiToolCallOutput.js";
-export { AiToolResultOutput } from "./shared-with-iframe/AiToolResultOutput.js";
-export { AiToolApprovalOutput } from "./shared-with-iframe/AiToolApprovalOutput.js";
-export { HtmlOutput } from "./shared-with-iframe/HtmlOutput.js";
-export { ImageOutput } from "./shared-with-iframe/ImageOutput.js";
-export { JsonOutput } from "./shared-with-iframe/JsonOutput.js";
-
-export { PlainTextOutput } from "./shared-with-iframe/PlainTextOutput.js";
-export { SingleOutput as RichOutput } from "./shared-with-iframe/SingleOutput.js";
-export { SvgOutput } from "./shared-with-iframe/SvgOutput.js";
-
-// Note: Heavy output components are now dynamically imported in RichOutput.tsx
-// to reduce bundle size. They are no longer exported from this index file.
-
-// Re-export types from schema for consistency
-export type {
- OutputData,
- AiToolCallData,
- AiToolResultData,
-} from "@runtimed/schema";
-
-// Legacy type aliases for backward compatibility
-export type ToolCallData = AiToolCallData;
-export type ToolResultData = AiToolResultData;
diff --git a/src/pages/demo/geojson/GeoJsonDemoPage.tsx b/src/pages/demo/geojson/GeoJsonDemoPage.tsx
index 5e382b846..b2a681da1 100644
--- a/src/pages/demo/geojson/GeoJsonDemoPage.tsx
+++ b/src/pages/demo/geojson/GeoJsonDemoPage.tsx
@@ -1,7 +1,9 @@
import {
mapFitFeatures2,
normalizeData,
-} from "@/components/outputs/shared-with-iframe/geojson/geojson-utils";
+ MapFeature,
+ GeoJsonMapOutput,
+} from "@runtimed/components";
import { bartTestData } from "./example-data/bart";
import {
bicycleRental,
@@ -11,9 +13,7 @@ import {
} from "./example-data/campus";
import { geometryCollection } from "./example-data/geomtry-collection";
import { wikipediaTestData } from "./example-data/wikipdedia";
-import { MapFeature } from "@/components/outputs/shared-with-iframe/geojson/MapFeature";
import { RMap } from "maplibre-react-components";
-import { GeoJsonMapOutput } from "@/components/outputs/shared-with-iframe/geojson/GeoJsonMapOutput";
export const GeoJsonDemoPage = () => {
return (
diff --git a/src/routes.tsx b/src/routes.tsx
index cecd2754f..3434a848d 100644
--- a/src/routes.tsx
+++ b/src/routes.tsx
@@ -55,6 +55,11 @@ const FeatureFlagsPage = React.lazy(() =>
default: mod.FeatureFlagsPage,
}))
);
+const OutputTypesDemoPage = React.lazy(() =>
+ import("@runtimed/components").then((mod) => ({
+ default: mod.OutputTypesDemoPage,
+ }))
+);
import { ErrorBoundary } from "react-error-boundary";
import { Confirmer, ConfirmProvider } from "./components/ui/confirm";
@@ -150,6 +155,16 @@ export const App: React.FC = () => {
}
/>
} />
+
+ }
+ />
diff --git a/src/types/react-syntax-highlighter.d.ts b/src/types/react-syntax-highlighter.d.ts
new file mode 100644
index 000000000..852126696
--- /dev/null
+++ b/src/types/react-syntax-highlighter.d.ts
@@ -0,0 +1 @@
+declare module "react-syntax-highlighter/dist/esm/styles/prism";
diff --git a/src/util/prefetch.ts b/src/util/prefetch.ts
index 36d292937..ecbb47c20 100644
--- a/src/util/prefetch.ts
+++ b/src/util/prefetch.ts
@@ -75,32 +75,24 @@ export function prefetchOutputChunks(): void {
// These imports will trigger chunk loading but won't execute the modules
// until they're actually needed via React.lazy()
- import(
- "../components/outputs/shared-with-iframe/MarkdownRenderer.js"
- ).catch(() => {
- // Silently ignore prefetch failures
- });
- import("../components/outputs/shared-with-iframe/JsonOutput.js").catch(
- () => {}
- );
- import("../components/outputs/shared-with-iframe/PlainTextOutput.js").catch(
- () => {}
- );
- import("../components/outputs/shared-with-iframe/HtmlOutput.js").catch(
- () => {}
- );
- import("../components/outputs/shared-with-iframe/ImageOutput.js").catch(
- () => {}
- );
- import("../components/outputs/shared-with-iframe/SvgOutput.js").catch(
- () => {}
- );
- import(
- "../components/outputs/shared-with-iframe/AiToolCallOutput.js"
- ).catch(() => {});
- import(
- "../components/outputs/shared-with-iframe/AiToolResultOutput.js"
- ).catch(() => {});
+ import("@runtimed/components")
+ .then((m) => m.MarkdownRenderer)
+ .catch(() => {
+ // Silently ignore prefetch failures
+ });
+ import("@runtimed/components").then((m) => m.JsonOutput).catch(() => {});
+ import("@runtimed/components")
+ .then((m) => m.PlainTextOutput)
+ .catch(() => {});
+ import("@runtimed/components").then((m) => m.HtmlOutput).catch(() => {});
+ import("@runtimed/components").then((m) => m.ImageOutput).catch(() => {});
+ import("@runtimed/components").then((m) => m.SvgOutput).catch(() => {});
+ import("@runtimed/components")
+ .then((m) => m.AiToolCallOutput)
+ .catch(() => {});
+ import("@runtimed/components")
+ .then((m) => m.AiToolResultOutput)
+ .catch(() => {});
});
}
@@ -137,12 +129,12 @@ export function prefetchOutputsConservative(): void {
prefetchWhenIdle(
() => {
// Only prefetch the most commonly used components
- import(
- "../components/outputs/shared-with-iframe/PlainTextOutput.js"
- ).catch(() => {});
- import(
- "../components/outputs/shared-with-iframe/MarkdownRenderer.js"
- ).catch(() => {});
+ import("@runtimed/components")
+ .then((m) => m.PlainTextOutput)
+ .catch(() => {});
+ import("@runtimed/components")
+ .then((m) => m.MarkdownRenderer)
+ .catch(() => {});
// Preload react-spring as it's used in the loading screen
import("@react-spring/web").catch(() => {});
},
diff --git a/test/components/outputs/AiToolResultOutput.test.tsx b/test/components/outputs/AiToolResultOutput.test.tsx
index 4ec80feac..4ee49dba6 100644
--- a/test/components/outputs/AiToolResultOutput.test.tsx
+++ b/test/components/outputs/AiToolResultOutput.test.tsx
@@ -2,7 +2,7 @@ import React from "react";
import { render, screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import type { AiToolResultData } from "@runtimed/schema";
-import { AiToolResultOutput } from "../../../src/components/outputs/shared-with-iframe/AiToolResultOutput";
+import { AiToolResultOutput } from "@runtimed/components";
describe("AiToolResultOutput", () => {
it("renders success result correctly", () => {