Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6fbbd96
integrate sigmond demo in homepage and AI page
niravcodes Jul 21, 2026
970ccea
cleanup
niravcodes Jul 21, 2026
aa34630
Merge branch 'main' into nko/aidemo
niravcodes Jul 21, 2026
2490a78
make more general
niravcodes Jul 22, 2026
b94b3d8
Merge remote-tracking branch 'refs/remotes/origin/nko/aidemo' into nk…
niravcodes Jul 22, 2026
4f01789
make loading behavior better
niravcodes Jul 22, 2026
03ea3bb
fix flicker on load
niravcodes Jul 22, 2026
6256425
remove flashing image
niravcodes Jul 24, 2026
d24a23b
Merge branch 'main' into nko/aidemo
niravcodes Jul 27, 2026
6ff54b2
make widget follow color-theme
niravcodes Aug 3, 2026
dbbd876
Merge branch 'main' into nko/aidemo
niravcodes Aug 3, 2026
e603a2e
Merge branch 'main' into nko/aidemo
niravcodes Aug 5, 2026
ad82395
Merge remote-tracking branch 'origin/main' into nko/aidemo
hey-august Aug 7, 2026
da19a82
Merge branch 'main' into nko/aidemo
niravcodes Aug 10, 2026
feb4348
Merge branch 'main' into nko/aidemo
niravcodes Aug 10, 2026
563a8d2
Merge branch 'main' into nko/aidemo
niravcodes Aug 10, 2026
b47b253
add llm ignore
niravcodes Aug 10, 2026
015046d
add pageslug var
niravcodes Aug 11, 2026
8772941
Merge branch 'main' into nko/aidemo
niravcodes Aug 13, 2026
49d4419
final debug
niravcodes Aug 14, 2026
4d4606a
edit styliing
niravcodes Aug 14, 2026
698cdd4
Merge branch 'main' into nko/aidemo
niravcodes Aug 14, 2026
9c811f6
Merge branch 'main' into nko/aidemo
hey-august Aug 19, 2026
c1e67bd
Merge branch 'main' into nko/aidemo
niravcodes Aug 24, 2026
2088c1b
address reviews
niravcodes Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/rules/mdx-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Every component available on this site: Fern's full default library (description
Custom components (registered in `docs.yml` → `fern/components/`):

- `VoiceWidget` — interactive click-to-call demo. Human-only: wrap usages in `<llms-ignore>`.
- `SigmondWidget` — renders nothing itself; it wires every `[data-sigmond-launcher]` element on the page to the live Sigmond video call. Pair it with the clickable markup — `<Markdown src="/snippets/common/talk-with-sigmond.mdx" />` or the homepage band — or the card renders and does nothing. Human-only: wrap the launcher markup *and* the `<SigmondWidget />` element in `<llms-ignore>`.
- `Skeleton` / `SkeletonText` — loading placeholders; registered, no MDX uses yet.

Fern's Markdown export flattens an untitled callout into ordinary prose. Give a `Warning` a concise title when losing the warning context could change a reader's safe interpretation—for example, legal or compliance disclaimers, credential handling, network restrictions, or service availability. Do not add titles mechanically to every callout; the title must identify the specific hazard or constraint.
Binary file added fern/assets/images/sigmond-thumbnail.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions fern/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
// component's public surface (components + types) without enumerating them.
export * from "./voice-widget/index";
export * from "./skeleton/index";
export * from "./sigmond-card/index";
154 changes: 154 additions & 0 deletions fern/components/sigmond-card/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { useEffect } from "react";

const WIDGET_UMD_URL =
"https://cdn.signalwire.com/npm/@signalwire/address-widget@dev/dist/address-widget.umd.js";

const DEFAULT_TOKEN =
"eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwidHlwIjoiU0FUIiwiY2giOiJwdWMuc2lnbmFsd2lyZS5jb20ifQ..IKPXZDR3ZYlpnUuw.yU8NBgPFQN1LH0Ws-Rs2ca-vAbUEP7Gvr0upOf-hDaTUfp-Aa5TALLMkJdUli8UX0ina34hng0G2XPLh7NZ2VZ5cphdd8AUCDKWkJ5HCI8538aNxPWcUWG25B-eELha7yEIrHZlO5BIxuyGVPskf0BIPGgiFSlctotyGB-VyYwSp_Uf6kdcwTCiEgra9LcyUMpNZyz5xH3p93xDU9bvzunFj50qIbfmQ2S0tDgEdduQWlTIfqUjRT6ePuwLGDBZYCCz9JtCQM0S2Fs7ZwvdSOzLFLqThkWnH3mSnkgKtkSTTXpnKUplPBJQaNRn0P4rJiwkk61BGyCJQ8Y8XYNeUHa-m1pRERQNweohQNBA0Ph3ie3vZLFJFjOeWuuztK8JSnfu4m9fxlkiA6icaHYuUm7KTSQ6n7oV5a2dkbLs7s51GRi7di0mm6Xw3jr2Zw1EGS8NgIrAU2HTv7stMAjpvDneYDfPaysCrmhCwu_TJgpG7PARiA4glKv9IuPeF5xpnymM5p0V2KL4HSEz-MJwziOwPpQRED_Brd2DiE0cz.CEYQUqZhcs5wKCtJQqUAMQ";

const DEFAULT_DESTINATION = "/public/sigmond";
const DEFAULT_LABEL = "Talk with Sigmond";
const DEFAULT_POSTER = "https://mcdn.signalwire.com/images/sigmond_still.png";

function pageSlug(): string {
if (typeof window === "undefined") return "";
const segments = (window.location.pathname || "/").split("/").filter(Boolean);
if (segments.length === 0) return "home";
return segments
.map((s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, ""))
.filter(Boolean)
.join("__");
}

type WidgetInstance = { open: () => Promise<void>; close: () => Promise<void>; theme?: string };
type WidgetGlobal = {
mount: (target: Element | string, options: Record<string, unknown>) => WidgetInstance;
unmount?: (widget: WidgetInstance) => Promise<void>;
};

declare global {
interface Window {
SignalWireAddressWidget?: WidgetGlobal;
}
}

let loaderPromise: Promise<WidgetGlobal> | null = null;
function loadWidgetGlobal(): Promise<WidgetGlobal> {
if (typeof window === "undefined") return Promise.reject(new Error("no window"));
if (window.SignalWireAddressWidget) return Promise.resolve(window.SignalWireAddressWidget);
if (!loaderPromise) {
loaderPromise = new Promise<WidgetGlobal>((resolve, reject) => {
const s = document.createElement("script");
s.src = WIDGET_UMD_URL;
s.async = true;
s.onload = () =>
window.SignalWireAddressWidget
? resolve(window.SignalWireAddressWidget)
: reject(new Error("address-widget UMD loaded but global missing"));
s.onerror = () => {
loaderPromise = null;
reject(new Error("failed to load address-widget UMD"));
};
document.head.appendChild(s);
});
}
return loaderPromise;
}

export interface SigmondWidgetProps {
token?: string;
}

export function SigmondWidget({ token = DEFAULT_TOKEN }: SigmondWidgetProps) {
useEffect(() => {
const teardown: Array<() => void> = [];

if (!document.head.querySelector("link[data-signalwire-address-fonts]")) {
const marker = document.createElement("link");
marker.setAttribute("data-signalwire-address-fonts", "host");
document.head.appendChild(marker);
}

void loadWidgetGlobal().catch(() => {});

const launchers = Array.from(
document.querySelectorAll<HTMLElement>("[data-sigmond-launcher]"),
).filter((el) => !el.dataset.sigmondWired);

if (launchers.length === 0) {
console.warn(
"SigmondWidget: no [data-sigmond-launcher] element found on this page — the card will render but do nothing.",
);
}

for (const el of launchers) {
el.dataset.sigmondWired = "true";
let widget: WidgetInstance | null = null;
let busy = false;

const open = async () => {
if (busy) return;
busy = true;
el.classList.add("sigmond-card--opening");
try {
const global = await loadWidgetGlobal();
const theme = document.documentElement.classList.contains("dark") ? "dark" : "light";
if (!widget) {
const host = document.createElement("div");
host.className = "sigmond-widget-host";
document.body.appendChild(host);
widget = global.mount(host, {
token,
destination: el.dataset.destination || DEFAULT_DESTINATION,
label: el.dataset.label || DEFAULT_LABEL,
poster: el.dataset.poster || DEFAULT_POSTER,
theme,
layout: "stacked",
video: true,
audio: true,
autoGainControl: false,
inputVolume: 125,
userVariables: { page_slug: pageSlug() },
});
const mounted = widget;
teardown.push(() => {
const unmount = window.SignalWireAddressWidget?.unmount;
const done = unmount
? Promise.resolve(unmount(mounted)).catch((e) =>
console.error("SigmondWidget: unmount failed:", e),
)
: Promise.resolve();
void done.then(() => host.remove());
});
}
widget.theme = theme;
void widget.open();
} catch (e) {
console.error("SigmondWidget:", e);
} finally {
busy = false;
el.classList.remove("sigmond-card--opening");
}
};

const onClick = () => void open();
const onKey = (e: KeyboardEvent) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
void open();
}
};
el.addEventListener("click", onClick);
el.addEventListener("keydown", onKey);
teardown.push(() => {
el.removeEventListener("click", onClick);
el.removeEventListener("keydown", onKey);
delete el.dataset.sigmondWired;
});
}

return () => teardown.forEach((fn) => fn());
}, [token]);

return null;
}
Loading
Loading