@simten/embed 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CircuitEmbed.d.ts.map +1 -1
- package/dist/CircuitEmbed.js +11 -1
- package/dist/CircuitEmbed.js.map +1 -1
- package/dist/circuit-embed.js +19 -19
- package/dist/styles.css +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CircuitEmbed.d.ts","sourceRoot":"","sources":["../src/CircuitEmbed.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAA4C,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,OAAO,CAAC;AACvG,OAAO,EAAiB,KAAK,mBAAmB,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAChG,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"CircuitEmbed.d.ts","sourceRoot":"","sources":["../src/CircuitEmbed.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAA4C,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,OAAO,CAAC;AACvG,OAAO,EAAiB,KAAK,mBAAmB,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAChG,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAiB/D,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY;IACtE,mDAAmD;IACnD,OAAO,EAAE,CAAC,CAAC;IACX;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;IAC5B,YAAY;IACZ,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,gCAAgC;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sCAAsC;IACtC,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,QAAQ,KAAK,IAAI,CAAC;IAC1F,kCAAkC;IAClC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IACjD;;;;;;;;;OASG;IACH,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC7D;AAED,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAmLrD;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAuB,CAAC,CAAC,SAAS,YAAY,EACrE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,YAAY,CAAC,kBAAkB,CAAC,CAAA;CAAE,KACrE,YAAY,CAAC"}
|
package/dist/CircuitEmbed.js
CHANGED
|
@@ -14,6 +14,7 @@ import { forwardRef, useState } from "react";
|
|
|
14
14
|
import { CircuitViewer } from "./CircuitViewer";
|
|
15
15
|
import { circuitToSource } from "@simten/core/circuit";
|
|
16
16
|
import { encodeSourceForUrl } from "@simten/ui/share";
|
|
17
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@simten/ui/primitives/tooltip";
|
|
17
18
|
import { useShareCircuit } from "./share-context";
|
|
18
19
|
/**
|
|
19
20
|
* Where Fork links open. simten.dev for everything except local dev of the
|
|
@@ -44,6 +45,15 @@ function inferAspectFromLayout(layout) {
|
|
|
44
45
|
return 1.5;
|
|
45
46
|
return w / h;
|
|
46
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* The Fork action, rendered in two placements (floating corner when there's no
|
|
50
|
+
* info bar, inline in the info bar otherwise). One component so the onFork /
|
|
51
|
+
* error / tooltip logic lives in a single place. Uses the shared shadcn tooltip
|
|
52
|
+
* (same one ClockControls uses) instead of the native `title` attribute.
|
|
53
|
+
*/
|
|
54
|
+
function ForkButton({ onFork, forkError, forkPending, className, tooltipSide = "top", }) {
|
|
55
|
+
return (_jsx(TooltipProvider, { delayDuration: 300, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: onFork, className: `cursor-pointer ${className}`, children: forkError ? "Fork failed" : forkPending ? "Forking…" : "Fork →" }) }), _jsx(TooltipContent, { side: tooltipSide, children: forkError ?? "Open and modify this circuit in the Simten editor" })] }) }));
|
|
56
|
+
}
|
|
47
57
|
const CircuitEmbedImpl = forwardRef(function CircuitEmbed({ circuit, height, aspectRatio, showControls = true, layout, theme, title, subtitle, description, href, focus, showPortLabels, onPortClick, glowUnconnected, autoRunSpeed = 500, initialInputs, forkSource, onPortValuesChange, }, ref) {
|
|
48
58
|
const hasInfoBar = title || description;
|
|
49
59
|
const [forkError, setForkError] = useState(null);
|
|
@@ -94,7 +104,7 @@ const CircuitEmbedImpl = forwardRef(function CircuitEmbed({ circuit, height, asp
|
|
|
94
104
|
? responsiveStyle
|
|
95
105
|
: { height }
|
|
96
106
|
: { height: '100%' };
|
|
97
|
-
return (_jsxs("div", { style: outerStyle, className: `relative flex flex-col ${hasInfoBar ? 'rounded-xl border border-[var(--embed-border)] overflow-hidden bg-[var(--embed-bg-secondary)]' : ''}`, children: [!hasInfoBar && !href && (_jsx(
|
|
107
|
+
return (_jsxs("div", { style: outerStyle, className: `relative flex flex-col ${hasInfoBar ? 'rounded-xl border border-[var(--embed-border)] overflow-hidden bg-[var(--embed-bg-secondary)]' : ''}`, children: [!hasInfoBar && !href && (_jsx(ForkButton, { onFork: onFork, forkError: forkError, forkPending: forkPending, tooltipSide: "bottom", className: "absolute top-2 right-2 z-10 hidden md:flex items-center px-2.5 py-1 rounded border border-[var(--embed-border)] bg-[var(--embed-bg-secondary)] text-[11px] text-[var(--embed-text-primary)] hover:opacity-80 transition-colors shadow-sm" })), _jsx("div", { style: canvasStyle, className: "min-h-0", children: _jsx(CircuitViewer, { ref: ref, circuit: circuit, height: "100%", showControls: showControls, autoHarness: true, initialInputs: initialInputs, layout: layout, theme: theme, focus: focus, showPortLabels: showPortLabels, onPortClick: onPortClick, glowUnconnected: glowUnconnected, autoRunSpeed: autoRunSpeed, onPortValuesChange: onPortValuesChange }) }), hasInfoBar && (_jsxs("div", { className: "border-t border-[var(--embed-border)] px-4 py-3 flex items-end justify-between gap-4", children: [_jsxs("div", { children: [_jsx("div", { className: "text-base font-semibold text-[var(--embed-text-primary)]", children: title }), subtitle && _jsx("div", { className: "text-xs text-[var(--embed-text-muted)] font-mono mt-0.5", children: subtitle }), description && _jsx("div", { className: "text-sm text-[var(--embed-text-secondary)] mt-1.5 leading-relaxed", children: description })] }), href ? (_jsx("a", { href: href, className: "shrink-0 px-3 py-1.5 rounded border border-[var(--embed-border)] text-xs text-[var(--embed-text-primary)] hover:opacity-80 transition-colors", children: "Open \u2192" })) : (_jsx(ForkButton, { onFork: onFork, forkError: forkError, forkPending: forkPending, tooltipSide: "top", className: "hidden md:flex items-center shrink-0 px-3 py-1.5 rounded border border-[var(--embed-border)] text-xs text-[var(--embed-text-primary)] hover:opacity-80 transition-colors" }))] }))] }));
|
|
98
108
|
});
|
|
99
109
|
/**
|
|
100
110
|
* CircuitEmbed component with generic inference over the circuit type.
|
package/dist/CircuitEmbed.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CircuitEmbed.js","sourceRoot":"","sources":["../src/CircuitEmbed.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * CircuitEmbed — embeddable circuit viewer with optional info bar.\n *\n * Thin wrapper around CircuitViewer that adds:\n * - Auto-harness (wraps bare circuits with Switch/Led nodes)\n * - Info bar (title, subtitle, description, link)\n *\n * For React users: pass a BuiltCircuit object.\n * For web component users: <circuit-embed code=\"...\"> goes through the\n * web component bridge which sandboxes compilation via an iframe.\n */\n\nimport { forwardRef, useState, type CSSProperties, type ForwardedRef, type ReactElement } from \"react\";\nimport { CircuitViewer, type CircuitViewerHandle, type HarnessedLayout } from \"./CircuitViewer\";\nimport { circuitToSource, type BuiltCircuit } from \"@simten/core/circuit\";\nimport type { FlatPortValueMap } from \"@simten/core/simulator\";\nimport { encodeSourceForUrl } from \"@simten/ui/share\";\nimport { useShareCircuit } from \"./share-context\";\n\n/**\n * Where Fork links open. simten.dev for everything except local dev of the\n * embed itself (where the embed is mounted at localhost). Detected at runtime\n * because the embed runs on third-party origins we don't control at build time.\n */\nfunction simtenHost(): string {\n if (typeof window === \"undefined\") return \"https://simten.dev\";\n return window.location.hostname === \"localhost\"\n ? window.location.origin\n : \"https://simten.dev\";\n}\n\nexport interface CircuitEmbedProps<C extends BuiltCircuit = BuiltCircuit> {\n /** The circuit to display (result of circuit()) */\n circuit: C;\n /**\n * Container height. Optional — when omitted, the embed sizes itself\n * width-responsively using `aspectRatio` (or one inferred from `layout`)\n * with sensible mobile-friendly min/max clamps.\n */\n height?: number | string;\n /**\n * Width-to-height ratio of the embed. Used only when `height` is not set.\n * If omitted and `layout` is passed, the ratio is computed from the\n * layout's bounding box. Otherwise defaults to 1.5 (3:2).\n */\n aspectRatio?: number;\n /** Show clock controls for sequential circuits */\n showControls?: boolean;\n /**\n * Pre-computed node positions. Keys are constrained to the circuit's\n * input names, output names, and node labels at compile time.\n * Pass to bypass the runtime layout engine.\n */\n layout?: HarnessedLayout<C>;\n /** Theme */\n theme?: \"light\" | \"dark\";\n /** Title shown in bottom bar */\n title?: string;\n /** Subtitle shown next to title */\n subtitle?: string;\n /** Description shown below title */\n description?: string;\n /**\n * Custom URL for the card's right-side link. When omitted (the common case),\n * a Fork button is rendered that opens the circuit in the simten.dev editor\n * via `/circuit/<lz-encoded-source>`. Pass `href` only to override.\n */\n href?: string;\n /**\n * Optional raw TypeScript source for the Fork button. When provided, the\n * Fork button encodes this verbatim instead of running the BuiltCircuit\n * through the IR-to-source serializer (which drops comments and helpers).\n * The web-component path passes the user's original `code` here.\n */\n forkSource?: string;\n /** Focus on specific node(s) */\n focus?: string | string[];\n /** Show port labels on nodes */\n showPortLabels?: boolean;\n /** Callback when a port is clicked */\n onPortClick?: (nodeLabel: string, portName: string, portType: \"input\" | \"output\") => void;\n /** Highlight unconnected ports */\n glowUnconnected?: boolean;\n /** Auto-run speed (ms between ticks) */\n autoRunSpeed?: number;\n /** Initial values for input ports (set on harness Switch/Input nodes) */\n initialInputs?: Record<string, number | boolean>;\n /**\n * Called when the embed's internal simulator settles on a new set of\n * port values — once on first settle, then on every subsequent settled\n * change (e.g. when the user toggles a switch on the canvas). Forwarded\n * verbatim to CircuitViewer; see its docs for full firing semantics.\n *\n * Use this to drive sibling UI (truth-table highlights, external value\n * readouts) without giving up the embed's chrome. The callback is\n * ref-stabilized inside the embed, so inline functions are safe.\n */\n onPortValuesChange?: (portValues: FlatPortValueMap) => void;\n}\n\nexport type CircuitEmbedHandle = CircuitViewerHandle;\n\n// Approximate node footprint used when inferring aspect ratio from a layout.\n// Layout coords are top-left of each node, so we add ~one node's width/height\n// to the bounding box so the rightmost / bottommost nodes aren't clipped.\nconst NODE_W = 160;\nconst NODE_H = 90;\n\nfunction inferAspectFromLayout(layout: Record<string, { x: number; y: number }> | undefined): number {\n if (!layout) return 1.5; // default 3:2 — sane for auto-laid-out circuits\n const positions = Object.values(layout);\n if (positions.length === 0) return 1.5;\n const w = Math.max(...positions.map(p => p.x)) + NODE_W;\n const h = Math.max(...positions.map(p => p.y)) + NODE_H;\n if (w <= 0 || h <= 0) return 1.5;\n return w / h;\n}\n\nconst CircuitEmbedImpl = forwardRef<CircuitEmbedHandle, CircuitEmbedProps>(\n function CircuitEmbed({\n circuit,\n height,\n aspectRatio,\n showControls = true,\n layout,\n theme,\n title,\n subtitle,\n description,\n href,\n focus,\n showPortLabels,\n onPortClick,\n glowUnconnected,\n autoRunSpeed = 500,\n initialInputs,\n forkSource,\n onPortValuesChange,\n }, ref) {\n const hasInfoBar = title || description;\n const [forkError, setForkError] = useState<string | null>(null);\n const [forkPending, setForkPending] = useState(false);\n const shareCircuit = useShareCircuit();\n\n const onFork = async () => {\n if (forkPending) return;\n try {\n const source = forkSource ?? circuitToSource(circuit);\n // Use the KV shortener when available (simten.dev). Outside simten.dev\n // — embeds on third-party pages — fall back to the inline-encoded URL.\n if (shareCircuit) {\n setForkPending(true);\n const { hash } = await shareCircuit(source);\n window.open(`${simtenHost()}/circuit/s/${hash}`, \"_blank\", \"noopener\");\n } else {\n const encoded = encodeSourceForUrl(source);\n window.open(`${simtenHost()}/circuit/${encoded}`, \"_blank\", \"noopener\");\n }\n } catch (err) {\n setForkError(err instanceof Error ? err.message : \"Couldn't fork this circuit\");\n setTimeout(() => setForkError(null), 3000);\n } finally {\n setForkPending(false);\n }\n };\n\n // Sizing strategy: if `height` is set, use it (backwards compat).\n // Otherwise size width-responsively via aspect-ratio with mobile clamps.\n const useResponsive = height === undefined;\n const aspect = aspectRatio ?? inferAspectFromLayout(layout as Record<string, { x: number; y: number }> | undefined);\n const responsiveStyle: CSSProperties = {\n width: '100%',\n aspectRatio: String(aspect),\n minHeight: 240,\n maxHeight: '70vh',\n };\n\n const outerStyle: CSSProperties | undefined = hasInfoBar\n ? undefined\n : useResponsive\n ? responsiveStyle\n : { height };\n const canvasStyle: CSSProperties = hasInfoBar\n ? useResponsive\n ? responsiveStyle\n : { height }\n : { height: '100%' };\n\n return (\n <div style={outerStyle} className={`relative flex flex-col ${hasInfoBar ? 'rounded-xl border border-[var(--embed-border)] overflow-hidden bg-[var(--embed-bg-secondary)]' : ''}`}>\n {!hasInfoBar && !href && (\n <button\n type=\"button\"\n onClick={onFork}\n title={forkError ?? \"Open and modify this circuit in the Simten editor\"}\n className=\"absolute top-2 right-2 z-10 hidden md:flex items-center px-2.5 py-1 rounded border border-[var(--embed-border)] bg-[var(--embed-bg-secondary)] text-[11px] text-[var(--embed-text-primary)] hover:opacity-80 transition-colors shadow-sm\"\n >\n {forkError ? \"Fork failed\" : \"Fork →\"}\n </button>\n )}\n <div style={canvasStyle} className=\"min-h-0\">\n <CircuitViewer\n ref={ref}\n circuit={circuit}\n height=\"100%\"\n showControls={showControls}\n autoHarness\n initialInputs={initialInputs}\n layout={layout}\n theme={theme}\n focus={focus}\n showPortLabels={showPortLabels}\n onPortClick={onPortClick}\n glowUnconnected={glowUnconnected}\n autoRunSpeed={autoRunSpeed}\n onPortValuesChange={onPortValuesChange}\n />\n </div>\n {hasInfoBar && (\n <div className=\"border-t border-[var(--embed-border)] px-4 py-3 flex items-end justify-between gap-4\">\n <div>\n <div className=\"text-base font-semibold text-[var(--embed-text-primary)]\">{title}</div>\n {subtitle && <div className=\"text-xs text-[var(--embed-text-muted)] font-mono mt-0.5\">{subtitle}</div>}\n {description && <div className=\"text-sm text-[var(--embed-text-secondary)] mt-1.5 leading-relaxed\">{description}</div>}\n </div>\n {href ? (\n <a href={href} className=\"shrink-0 px-3 py-1.5 rounded border border-[var(--embed-border)] text-xs text-[var(--embed-text-primary)] hover:opacity-80 transition-colors\">\n Open →\n </a>\n ) : (\n <button\n type=\"button\"\n onClick={onFork}\n title={forkError ?? \"Open and modify this circuit in the Simten editor\"}\n className=\"hidden md:flex items-center shrink-0 px-3 py-1.5 rounded border border-[var(--embed-border)] text-xs text-[var(--embed-text-primary)] hover:opacity-80 transition-colors\"\n >\n {forkError ? `Can't fork: ${forkError.slice(0, 40)}` : \"Fork →\"}\n </button>\n )}\n </div>\n )}\n </div>\n );\n }\n);\n\n/**\n * CircuitEmbed component with generic inference over the circuit type.\n * Cast preserves the generic so `layout` keys are constrained at compile time.\n */\nexport const CircuitEmbed = CircuitEmbedImpl as <C extends BuiltCircuit>(\n props: CircuitEmbedProps<C> & { ref?: ForwardedRef<CircuitEmbedHandle> },\n) => ReactElement;\n"]}
|
|
1
|
+
{"version":3,"file":"CircuitEmbed.js","sourceRoot":"","sources":["../src/CircuitEmbed.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * CircuitEmbed — embeddable circuit viewer with optional info bar.\n *\n * Thin wrapper around CircuitViewer that adds:\n * - Auto-harness (wraps bare circuits with Switch/Led nodes)\n * - Info bar (title, subtitle, description, link)\n *\n * For React users: pass a BuiltCircuit object.\n * For web component users: <circuit-embed code=\"...\"> goes through the\n * web component bridge which sandboxes compilation via an iframe.\n */\n\nimport { forwardRef, useState, type CSSProperties, type ForwardedRef, type ReactElement } from \"react\";\nimport { CircuitViewer, type CircuitViewerHandle, type HarnessedLayout } from \"./CircuitViewer\";\nimport { circuitToSource, type BuiltCircuit } from \"@simten/core/circuit\";\nimport type { FlatPortValueMap } from \"@simten/core/simulator\";\nimport { encodeSourceForUrl } from \"@simten/ui/share\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"@simten/ui/primitives/tooltip\";\nimport { useShareCircuit } from \"./share-context\";\n\n/**\n * Where Fork links open. simten.dev for everything except local dev of the\n * embed itself (where the embed is mounted at localhost). Detected at runtime\n * because the embed runs on third-party origins we don't control at build time.\n */\nfunction simtenHost(): string {\n if (typeof window === \"undefined\") return \"https://simten.dev\";\n return window.location.hostname === \"localhost\"\n ? window.location.origin\n : \"https://simten.dev\";\n}\n\nexport interface CircuitEmbedProps<C extends BuiltCircuit = BuiltCircuit> {\n /** The circuit to display (result of circuit()) */\n circuit: C;\n /**\n * Container height. Optional — when omitted, the embed sizes itself\n * width-responsively using `aspectRatio` (or one inferred from `layout`)\n * with sensible mobile-friendly min/max clamps.\n */\n height?: number | string;\n /**\n * Width-to-height ratio of the embed. Used only when `height` is not set.\n * If omitted and `layout` is passed, the ratio is computed from the\n * layout's bounding box. Otherwise defaults to 1.5 (3:2).\n */\n aspectRatio?: number;\n /** Show clock controls for sequential circuits */\n showControls?: boolean;\n /**\n * Pre-computed node positions. Keys are constrained to the circuit's\n * input names, output names, and node labels at compile time.\n * Pass to bypass the runtime layout engine.\n */\n layout?: HarnessedLayout<C>;\n /** Theme */\n theme?: \"light\" | \"dark\";\n /** Title shown in bottom bar */\n title?: string;\n /** Subtitle shown next to title */\n subtitle?: string;\n /** Description shown below title */\n description?: string;\n /**\n * Custom URL for the card's right-side link. When omitted (the common case),\n * a Fork button is rendered that opens the circuit in the simten.dev editor\n * via `/circuit/<lz-encoded-source>`. Pass `href` only to override.\n */\n href?: string;\n /**\n * Optional raw TypeScript source for the Fork button. When provided, the\n * Fork button encodes this verbatim instead of running the BuiltCircuit\n * through the IR-to-source serializer (which drops comments and helpers).\n * The web-component path passes the user's original `code` here.\n */\n forkSource?: string;\n /** Focus on specific node(s) */\n focus?: string | string[];\n /** Show port labels on nodes */\n showPortLabels?: boolean;\n /** Callback when a port is clicked */\n onPortClick?: (nodeLabel: string, portName: string, portType: \"input\" | \"output\") => void;\n /** Highlight unconnected ports */\n glowUnconnected?: boolean;\n /** Auto-run speed (ms between ticks) */\n autoRunSpeed?: number;\n /** Initial values for input ports (set on harness Switch/Input nodes) */\n initialInputs?: Record<string, number | boolean>;\n /**\n * Called when the embed's internal simulator settles on a new set of\n * port values — once on first settle, then on every subsequent settled\n * change (e.g. when the user toggles a switch on the canvas). Forwarded\n * verbatim to CircuitViewer; see its docs for full firing semantics.\n *\n * Use this to drive sibling UI (truth-table highlights, external value\n * readouts) without giving up the embed's chrome. The callback is\n * ref-stabilized inside the embed, so inline functions are safe.\n */\n onPortValuesChange?: (portValues: FlatPortValueMap) => void;\n}\n\nexport type CircuitEmbedHandle = CircuitViewerHandle;\n\n// Approximate node footprint used when inferring aspect ratio from a layout.\n// Layout coords are top-left of each node, so we add ~one node's width/height\n// to the bounding box so the rightmost / bottommost nodes aren't clipped.\nconst NODE_W = 160;\nconst NODE_H = 90;\n\nfunction inferAspectFromLayout(layout: Record<string, { x: number; y: number }> | undefined): number {\n if (!layout) return 1.5; // default 3:2 — sane for auto-laid-out circuits\n const positions = Object.values(layout);\n if (positions.length === 0) return 1.5;\n const w = Math.max(...positions.map(p => p.x)) + NODE_W;\n const h = Math.max(...positions.map(p => p.y)) + NODE_H;\n if (w <= 0 || h <= 0) return 1.5;\n return w / h;\n}\n\n/**\n * The Fork action, rendered in two placements (floating corner when there's no\n * info bar, inline in the info bar otherwise). One component so the onFork /\n * error / tooltip logic lives in a single place. Uses the shared shadcn tooltip\n * (same one ClockControls uses) instead of the native `title` attribute.\n */\nfunction ForkButton({\n onFork,\n forkError,\n forkPending,\n className,\n tooltipSide = \"top\",\n}: {\n onFork: () => void;\n forkError: string | null;\n forkPending: boolean;\n className: string;\n tooltipSide?: \"top\" | \"bottom\";\n}) {\n return (\n <TooltipProvider delayDuration={300}>\n <Tooltip>\n <TooltipTrigger asChild>\n <button type=\"button\" onClick={onFork} className={`cursor-pointer ${className}`}>\n {forkError ? \"Fork failed\" : forkPending ? \"Forking…\" : \"Fork →\"}\n </button>\n </TooltipTrigger>\n <TooltipContent side={tooltipSide}>\n {forkError ?? \"Open and modify this circuit in the Simten editor\"}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n );\n}\n\nconst CircuitEmbedImpl = forwardRef<CircuitEmbedHandle, CircuitEmbedProps>(\n function CircuitEmbed({\n circuit,\n height,\n aspectRatio,\n showControls = true,\n layout,\n theme,\n title,\n subtitle,\n description,\n href,\n focus,\n showPortLabels,\n onPortClick,\n glowUnconnected,\n autoRunSpeed = 500,\n initialInputs,\n forkSource,\n onPortValuesChange,\n }, ref) {\n const hasInfoBar = title || description;\n const [forkError, setForkError] = useState<string | null>(null);\n const [forkPending, setForkPending] = useState(false);\n const shareCircuit = useShareCircuit();\n\n const onFork = async () => {\n if (forkPending) return;\n try {\n const source = forkSource ?? circuitToSource(circuit);\n // Use the KV shortener when available (simten.dev). Outside simten.dev\n // — embeds on third-party pages — fall back to the inline-encoded URL.\n if (shareCircuit) {\n setForkPending(true);\n const { hash } = await shareCircuit(source);\n window.open(`${simtenHost()}/circuit/s/${hash}`, \"_blank\", \"noopener\");\n } else {\n const encoded = encodeSourceForUrl(source);\n window.open(`${simtenHost()}/circuit/${encoded}`, \"_blank\", \"noopener\");\n }\n } catch (err) {\n setForkError(err instanceof Error ? err.message : \"Couldn't fork this circuit\");\n setTimeout(() => setForkError(null), 3000);\n } finally {\n setForkPending(false);\n }\n };\n\n // Sizing strategy: if `height` is set, use it (backwards compat).\n // Otherwise size width-responsively via aspect-ratio with mobile clamps.\n const useResponsive = height === undefined;\n const aspect = aspectRatio ?? inferAspectFromLayout(layout as Record<string, { x: number; y: number }> | undefined);\n const responsiveStyle: CSSProperties = {\n width: '100%',\n aspectRatio: String(aspect),\n minHeight: 240,\n maxHeight: '70vh',\n };\n\n const outerStyle: CSSProperties | undefined = hasInfoBar\n ? undefined\n : useResponsive\n ? responsiveStyle\n : { height };\n const canvasStyle: CSSProperties = hasInfoBar\n ? useResponsive\n ? responsiveStyle\n : { height }\n : { height: '100%' };\n\n return (\n <div style={outerStyle} className={`relative flex flex-col ${hasInfoBar ? 'rounded-xl border border-[var(--embed-border)] overflow-hidden bg-[var(--embed-bg-secondary)]' : ''}`}>\n {!hasInfoBar && !href && (\n <ForkButton\n onFork={onFork}\n forkError={forkError}\n forkPending={forkPending}\n tooltipSide=\"bottom\"\n className=\"absolute top-2 right-2 z-10 hidden md:flex items-center px-2.5 py-1 rounded border border-[var(--embed-border)] bg-[var(--embed-bg-secondary)] text-[11px] text-[var(--embed-text-primary)] hover:opacity-80 transition-colors shadow-sm\"\n />\n )}\n <div style={canvasStyle} className=\"min-h-0\">\n <CircuitViewer\n ref={ref}\n circuit={circuit}\n height=\"100%\"\n showControls={showControls}\n autoHarness\n initialInputs={initialInputs}\n layout={layout}\n theme={theme}\n focus={focus}\n showPortLabels={showPortLabels}\n onPortClick={onPortClick}\n glowUnconnected={glowUnconnected}\n autoRunSpeed={autoRunSpeed}\n onPortValuesChange={onPortValuesChange}\n />\n </div>\n {hasInfoBar && (\n <div className=\"border-t border-[var(--embed-border)] px-4 py-3 flex items-end justify-between gap-4\">\n <div>\n <div className=\"text-base font-semibold text-[var(--embed-text-primary)]\">{title}</div>\n {subtitle && <div className=\"text-xs text-[var(--embed-text-muted)] font-mono mt-0.5\">{subtitle}</div>}\n {description && <div className=\"text-sm text-[var(--embed-text-secondary)] mt-1.5 leading-relaxed\">{description}</div>}\n </div>\n {href ? (\n <a href={href} className=\"shrink-0 px-3 py-1.5 rounded border border-[var(--embed-border)] text-xs text-[var(--embed-text-primary)] hover:opacity-80 transition-colors\">\n Open →\n </a>\n ) : (\n <ForkButton\n onFork={onFork}\n forkError={forkError}\n forkPending={forkPending}\n tooltipSide=\"top\"\n className=\"hidden md:flex items-center shrink-0 px-3 py-1.5 rounded border border-[var(--embed-border)] text-xs text-[var(--embed-text-primary)] hover:opacity-80 transition-colors\"\n />\n )}\n </div>\n )}\n </div>\n );\n }\n);\n\n/**\n * CircuitEmbed component with generic inference over the circuit type.\n * Cast preserves the generic so `layout` keys are constrained at compile time.\n */\nexport const CircuitEmbed = CircuitEmbedImpl as <C extends BuiltCircuit>(\n props: CircuitEmbedProps<C> & { ref?: ForwardedRef<CircuitEmbedHandle> },\n) => ReactElement;\n"]}
|