diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..d3211da
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,10 @@
+VITE_GRAPHQL_ENDPOINT=https://admin.e-multicontent.com/graphql
+VITE_ARTIFACT_ROUTE_HYDRATION=true
+VITE_BACKEND_PROFILE=full
+VITE_SITE_URL=https://e-multicontent.com
+STOREFRONT_ARTIFACT_SITE_KEY=e-multi
+STOREFRONT_ARTIFACT_MODE=off
+CMS_TAILWIND_SOURCE_API_URL=https://admin.e-multicontent.com/wp-json/funkycommerce-tailwind/v1
+CMS_TAILWIND_BOOTSTRAP=true
+CMS_TAILWIND_REQUIRED=true
+STOREFRONT_ARTIFACT_SIGNING_SECRET=e200a0a19a618dd149af13f7f6a83124a0b57e50251506692a1e07d908f04a54
\ No newline at end of file
diff --git a/apps/storefront/.env.example b/apps/storefront/.env.example
deleted file mode 100644
index 4702126..0000000
--- a/apps/storefront/.env.example
+++ /dev/null
@@ -1,34 +0,0 @@
-# Single source of truth for the backend connection. Every other backend-derived
-# value (auth mutations, abandoned-cart tracking endpoint, live shop currency, payment
-# gateway availability) is derived from this one URL's origin — see lib/env.ts. Point
-# it at your WPGraphQL endpoint, e.g.:
-VITE_GRAPHQL_ENDPOINT=
-#
-# Optional direct URL to the static Tailwind class manifest generated by the
-# Optional static Tailwind manifest for deployments that publish one independently.
-# Superfunky production leaves this empty and compiles only the reviewed local
-# contract, performing no CMS Tailwind inventory request. A configured manifest
-# fails before Vite when unavailable, incomplete, or invalid.
-VITE_CMS_TAILWIND_MANIFEST_URL=
-#
-# Preferred incremental mode: a signed, read-only source endpoint. Requires
-# STOREFRONT_ARTIFACT_SIGNING_SECRET and VITE_SITE_URL. The build downloads the
-# previous deployed index, fetches only changed source bodies, and publishes the
-# next index with the successful deployment.
-CMS_TAILWIND_SOURCE_API_URL=
-# Set only for the first deployment when no previous index exists. A successful
-# deployment publishes the index; remove this flag afterward.
-CMS_TAILWIND_BOOTSTRAP=false
-#
-# Development and production builds default to the public
-# https://dev.superfunky.pro/graphql reference backend when this is empty. Set an
-# explicit endpoint for every customer or production deployment.
-#
-# Web Push subscribe, unsubscribe, and VAPID public-key routes are derived from this
-# backend origin. The Stripe publishable key is likewise supplied by backend store
-# settings. Private VAPID and Stripe keys must never be exposed to the frontend.
-
-# Edge/CDN geolocation endpoint (see lib/geolocation.ts) — typically injected by the
-# hosting platform (e.g. Netlify's /geolocation) rather than the WP backend itself, so
-# it stays a separate env var.
-VITE_GEOLOCATION_ENDPOINT=
diff --git a/apps/storefront/src/components/CmsPageContent.tsx b/apps/storefront/src/components/CmsPageContent.tsx
index 1aba92c..07d46ea 100644
--- a/apps/storefront/src/components/CmsPageContent.tsx
+++ b/apps/storefront/src/components/CmsPageContent.tsx
@@ -245,47 +245,47 @@ export function CmsPageContent({
? `${window.location.origin}${pathname === "/" ? "" : pathname.replace(/\/+$/, "")}`
: page.seo.canonical || page.seo.opengraphUrl || `${window.location.origin}${pathname}`}
languageCode={contentLanguageCode}
- keywords={page.seo.keywords || undefined}
- siteName={page.seo.siteName || undefined}
- appendSiteName={false}
- robots={
- robots
- || (
- homePath
- ? "index, follow"
- : page.seo.robotsSource === "explicit"
- ? page.seo.robots
- : fallbackRobots || page.seo.robots
- )
+ keywords={page.seo.keywords || undefined}
+ siteName={page.seo.siteName || undefined}
+ appendSiteName={false}
+ robots={
+ robots
+ || (
+ homePath
+ ? "index, follow"
+ : page.seo.robotsSource === "explicit"
+ ? page.seo.robots
+ : fallbackRobots || page.seo.robots
+ )
+ }
+ opengraphType={page.seo.opengraphType === "article" ? "article" : "website"}
+ opengraphTitle={page.seo.opengraphTitle || undefined}
+ opengraphDescription={page.seo.opengraphDescription || undefined}
+ image={page.featuredImage
+ ? {
+ url: page.featuredImage.sourceUrl,
+ alt: page.featuredImage.altText || page.title,
+ width: page.featuredImage.width,
+ height: page.featuredImage.height,
}
- opengraphType={page.seo.opengraphType === "article" ? "article" : "website"}
- opengraphTitle={page.seo.opengraphTitle || undefined}
- opengraphDescription={page.seo.opengraphDescription || undefined}
- image={page.featuredImage
- ? {
- url: page.featuredImage.sourceUrl,
- alt: page.featuredImage.altText || page.title,
- width: page.featuredImage.width,
- height: page.featuredImage.height,
- }
- : page.seo.opengraphImage
- ? { url: page.seo.opengraphImage, alt: page.title }
- : undefined}
- opengraphPublishedTime={page.seo.opengraphPublishedTime || undefined}
- opengraphModifiedTime={page.seo.opengraphModifiedTime || undefined}
- opengraphAuthor={page.seo.opengraphAuthor || undefined}
- opengraphPublisher={page.seo.opengraphPublisher || undefined}
- twitterTitle={page.seo.twitterTitle || undefined}
- twitterDescription={page.seo.twitterDescription || undefined}
- schema={{
- pageType: page.seo.pageType || undefined,
- articleType: page.seo.articleType || undefined,
- }}
- breadcrumbs={page.seo.breadcrumbs}
- translations={page.translations.map((translation) => ({
- languageCode: translation.languageCode,
- url: translation.uri,
- }))}
+ : page.seo.opengraphImage
+ ? { url: page.seo.opengraphImage, alt: page.title }
+ : undefined}
+ opengraphPublishedTime={page.seo.opengraphPublishedTime || undefined}
+ opengraphModifiedTime={page.seo.opengraphModifiedTime || undefined}
+ opengraphAuthor={page.seo.opengraphAuthor || undefined}
+ opengraphPublisher={page.seo.opengraphPublisher || undefined}
+ twitterTitle={page.seo.twitterTitle || undefined}
+ twitterDescription={page.seo.twitterDescription || undefined}
+ schema={{
+ pageType: page.seo.pageType || undefined,
+ articleType: page.seo.articleType || undefined,
+ }}
+ breadcrumbs={page.seo.breadcrumbs}
+ translations={page.translations.map((translation) => ({
+ languageCode: translation.languageCode,
+ url: translation.uri,
+ }))}
/>
{breadcrumbs.length > 1 ? : null}
@@ -372,7 +372,8 @@ function NestedWordPressShortcodes({ html, slots }: { html: string; slots: Neste
return (
<>
-
+
+
{slots.map(({ marker, content }, index) => (
-
-
-
Loading {marker.name}
-
+
+
+
+
Loading {marker.name}
- ),
+
+ ),
target,
marker.slotId,
);
diff --git a/apps/storefront/src/components/ElevenLabsExistingMarkupAssets.tsx b/apps/storefront/src/components/ElevenLabsExistingMarkupAssets.tsx
new file mode 100644
index 0000000..063caa8
--- /dev/null
+++ b/apps/storefront/src/components/ElevenLabsExistingMarkupAssets.tsx
@@ -0,0 +1,219 @@
+
+import { useEffect } from "react";
+import { graphqlRequest } from "@funky/sdk";
+import "./ElevenLabsHeadlessReader.css";
+
+type AssetsResponse = {
+ elwrHeadlessAssets: {
+ css: string | null;
+ scriptUrl: string | null;
+ styleUrl: string | null;
+ } | null;
+};
+
+type ReaderResponse = {
+ post: {
+ elwrReaderHtml: string | null;
+ } | null;
+};
+
+const ASSETS_QUERY = `
+ query ElevenLabsAssets {
+ elwrHeadlessAssets {
+ css
+ scriptUrl
+ styleUrl
+ }
+ }
+`;
+
+const READER_QUERY = `
+ query ElevenLabsReaderColor($id: ID!) {
+ post(id: $id, idType: DATABASE_ID) {
+ elwrReaderHtml
+ }
+ }
+`;
+
+type Props = {
+ postId?: number;
+};
+
+function extractReaderColor(html: string): string | null {
+ const doc = new DOMParser().parseFromString(html, "text/html");
+
+ const reader = doc.querySelector(".elwr-reader");
+ if (!reader) return null;
+
+ const color = reader.style
+ .getPropertyValue("--elwr-color")
+ .trim();
+
+ // Accept only hex colors from the backend.
+ return /^#[0-9a-fA-F]{3,8}$/.test(color) ? color : null;
+}
+
+function applyReaderColor(color: string) {
+ document.querySelectorAll(".elwr-reader").forEach(
+ (reader) => {
+ reader.style.setProperty("--elwr-color", color);
+ }
+ );
+}
+
+export function ElevenLabsExistingMarkupAssets({
+ postId,
+}: Props) {
+ useEffect(() => {
+ let cancelled = false;
+ let observer: MutationObserver | null = null;
+
+ async function loadAssets() {
+ try {
+ const { data, errors } =
+ await graphqlRequest(ASSETS_QUERY);
+
+ if (errors?.length) {
+ throw new Error(
+ errors.map((error) => error.message).join("; ")
+ );
+ }
+
+ if (cancelled || !data?.elwrHeadlessAssets) {
+ return;
+ }
+
+ const { css, scriptUrl, styleUrl } =
+ data.elwrHeadlessAssets;
+
+ // 1. Load the plugin's original CSS.
+ if (styleUrl) {
+ let link = document.getElementById(
+ "elevenlabs-reader-stylesheet"
+ ) as HTMLLinkElement | null;
+
+ if (!link) {
+ link = document.createElement("link");
+ link.id = "elevenlabs-reader-stylesheet";
+ link.rel = "stylesheet";
+ document.head.appendChild(link);
+ }
+
+ if (link.href !== styleUrl) {
+ link.href = styleUrl;
+ }
+ }
+
+ // 2. Inject WordPress dynamic CSS.
+ if (css) {
+ let style = document.getElementById(
+ "elevenlabs-dynamic-css"
+ ) as HTMLStyleElement | null;
+
+ if (!style) {
+ style = document.createElement("style");
+ style.id = "elevenlabs-dynamic-css";
+ document.head.appendChild(style);
+ }
+
+ style.textContent = css;
+ }
+
+ // 3. Restore the WordPress admin color if
+ // the frontend sanitizer removed it.
+ if (Number.isInteger(postId) && (postId ?? 0) > 0) {
+ try {
+ const readerResult =
+ await graphqlRequest(
+ READER_QUERY,
+ { id: String(postId) }
+ );
+
+ if (readerResult.errors?.length) {
+ throw new Error(
+ readerResult.errors
+ .map((error) => error.message)
+ .join("; ")
+ );
+ }
+
+ if (cancelled) return;
+
+ const html =
+ readerResult.data?.post?.elwrReaderHtml;
+
+ const color = html
+ ? extractReaderColor(html)
+ : null;
+
+ if (color) {
+ applyReaderColor(color);
+
+ // Also handle reader markup mounted later.
+ observer = new MutationObserver(() => {
+ applyReaderColor(color);
+ });
+
+ observer.observe(document.body, {
+ childList: true,
+ subtree: true,
+ });
+
+ console.info(
+ "[ELWR] WordPress admin color:",
+ color
+ );
+ } else {
+ console.warn(
+ "[ELWR] No admin color found in reader HTML"
+ );
+ }
+ } catch (error) {
+ console.warn(
+ "[ELWR] Could not restore reader color:",
+ error
+ );
+ }
+ }
+
+ if (cancelled) return;
+
+ // 4. Load the plugin's JavaScript.
+ if (scriptUrl) {
+ let script = document.getElementById(
+ "elevenlabs-reader-script"
+ ) as HTMLScriptElement | null;
+
+ if (!script) {
+ script = document.createElement("script");
+ script.id = "elevenlabs-reader-script";
+ script.async = true;
+ script.src = scriptUrl;
+ document.body.appendChild(script);
+ }
+ }
+
+ console.info("[ELWR] Assets loaded", {
+ css: Boolean(css),
+ styleUrl,
+ scriptUrl,
+ postId,
+ });
+ } catch (error) {
+ console.error(
+ "[ELWR] Failed to load assets:",
+ error
+ );
+ }
+ }
+
+ loadAssets();
+
+ return () => {
+ cancelled = true;
+ observer?.disconnect();
+ };
+ }, [postId]);
+
+ return null;
+}
diff --git a/apps/storefront/src/components/ElevenLabsHeadlessReader.css b/apps/storefront/src/components/ElevenLabsHeadlessReader.css
new file mode 100644
index 0000000..e1c769d
--- /dev/null
+++ b/apps/storefront/src/components/ElevenLabsHeadlessReader.css
@@ -0,0 +1,16 @@
+.elwr-headless-root {
+ width: 100%;
+ max-width: 100%;
+}
+
+.elwr-headless-root .elwr-reader {
+ width: 100%;
+ max-width: 100%;
+ box-sizing: border-box;
+}
+
+.elwr-headless-root .elwr-reader *,
+.elwr-headless-root .elwr-reader *::before,
+.elwr-headless-root .elwr-reader *::after {
+ box-sizing: border-box;
+}
\ No newline at end of file
diff --git a/apps/storefront/src/components/ElevenLabsHeadlessReader.tsx b/apps/storefront/src/components/ElevenLabsHeadlessReader.tsx
new file mode 100644
index 0000000..e0c6909
--- /dev/null
+++ b/apps/storefront/src/components/ElevenLabsHeadlessReader.tsx
@@ -0,0 +1,108 @@
+
+import "./ElevenLabsHeadlessReader.css";
+import { fetchElevenLabsReader } from "../lib/elevenlabs";
+import { useEffect, useState } from "react";
+
+type Props = {
+ postId: number;
+};
+
+export function ElevenLabsHeadlessReader({ postId }: Props) {
+ const [html, setHtml] = useState("");
+ const [css, setCss] = useState("");
+ const [scriptUrl, setScriptUrl] = useState("");
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ let active = true;
+
+ setHtml("");
+ setError(null);
+
+ fetchElevenLabsReader(postId)
+ .then((result) => {
+ if (!active) return;
+
+ setHtml(result.post?.elwrReaderHtml ?? "");
+ setCss(result.elwrHeadlessAssets?.css ?? "");
+ setScriptUrl(result.elwrHeadlessAssets?.scriptUrl ?? "");
+ console.log('asdasd', result.post?.elwrReaderHtml ?? "")
+ console.log('asdasd', result.elwrHeadlessAssets?.css ?? "")
+ console.log(result.elwrHeadlessAssets?.scriptUrl ?? "")
+ })
+ .catch((reason: unknown) => {
+ if (!active) return;
+
+ setError(
+ reason instanceof Error
+ ? reason.message
+ : "ElevenLabs reader could not be loaded"
+ );
+ });
+
+ return () => {
+ active = false;
+ };
+ }, [postId]);
+
+ // Load WordPress plugin CSS
+ useEffect(() => {
+ if (!css) return;
+
+ const id = "elevenlabs-dynamic-css";
+
+ let style = document.getElementById(id) as HTMLStyleElement | null;
+
+ if (!style) {
+ style = document.createElement("style");
+ style.id = id;
+ document.head.appendChild(style);
+ }
+
+ style.textContent = css;
+ }, [css]);
+
+ // Load WordPress plugin JavaScript
+ useEffect(() => {
+ if (!scriptUrl) return;
+
+ const id = "elevenlabs-reader-script";
+
+ let script = document.getElementById(id) as HTMLScriptElement | null;
+
+ if (script?.src === scriptUrl) return;
+
+ if (script) {
+ script.remove();
+ }
+
+ script = document.createElement("script");
+ script.id = id;
+ script.src = scriptUrl;
+ script.async = true;
+
+ script.onerror = () => {
+ setError("ElevenLabs player JavaScript failed to load.");
+ };
+
+ document.body.appendChild(script);
+
+ return () => {
+ // Keep the shared script loaded; its click handler uses event delegation.
+ };
+ }, [scriptUrl]);
+
+ if (error) {
+ return {error}
;
+ }
+
+ if (!html) return null;
+
+ return (
+
+ );
+}
diff --git a/apps/storefront/src/components/wordpressShortcodes.tsx b/apps/storefront/src/components/wordpressShortcodes.tsx
index 7305429..61fcc26 100644
--- a/apps/storefront/src/components/wordpressShortcodes.tsx
+++ b/apps/storefront/src/components/wordpressShortcodes.tsx
@@ -58,6 +58,7 @@ import {
communityMemberMatchesRoles,
normalizeCommunityMemberTypes,
} from "../lib/communityMemberTypes";
+import { ElevenLabsHeadlessReader } from "./ElevenLabsHeadlessReader";
export type WordPressShortcodeAttributes = Record;
export type WordPressShortcodeRenderer = (attributes: WordPressShortcodeAttributes) => ReactNode;
@@ -284,47 +285,46 @@ function SliderShortcode({ attributes }: ShortcodeProps) {
renderItem={(slide, index, meta) => {
const SlideHeading = index === 0 ? firstSlideHeadingLevel : slideHeadingLevel;
return (
-
- {slide.image ? : null}
-
-
- {slide.label ?
{slide.label} : null}
-
{slide.title}
- {slide.subtitle ?
{slide.subtitle}
: null}
- {primaryCtaConfig || secondaryCtaConfig ? (
-
- {primaryCtaConfig ? (
-
- {primaryCtaConfig.label}
-
- ) : null}
- {secondaryCtaConfig ? (
-
- {secondaryCtaConfig.label}
-
- ) : null}
-
- ) : null}
-
-
+ {slide.image ? : null}
+
+
+ {slide.label ?
{slide.label} : null}
+
{slide.title}
+ {slide.subtitle ?
{slide.subtitle}
: null}
+ {primaryCtaConfig || secondaryCtaConfig ? (
+
+ {primaryCtaConfig ? (
+
+ {primaryCtaConfig.label}
+
+ ) : null}
+ {secondaryCtaConfig ? (
+
+ {secondaryCtaConfig.label}
+
+ ) : null}
+
+ ) : null}
+
+
);
}}
/>
@@ -1419,19 +1419,19 @@ function resolveOrderSummary(
{ label: t("order_success.row.subtotal"), value: formatStoreApiMoney(totals.subtotal, totals) },
...(isPositiveStoreApiAmount(totals.total_discount)
? [{
- label: liveOrder.coupons.length
- ? t("order_success.row.discount_with_codes", { codes: liveOrder.coupons.map(({ code }) => code).join(", ") })
- : t("order_success.row.discount"),
- value: `-${formatStoreApiMoney(totals.total_discount, totals)}`,
- }]
+ label: liveOrder.coupons.length
+ ? t("order_success.row.discount_with_codes", { codes: liveOrder.coupons.map(({ code }) => code).join(", ") })
+ : t("order_success.row.discount"),
+ value: `-${formatStoreApiMoney(totals.total_discount, totals)}`,
+ }]
: []),
...(totals.total_shipping !== null
? [{
- label: t("order_success.row.shipping"),
- value: isPositiveStoreApiAmount(totals.total_shipping)
- ? formatStoreApiMoney(totals.total_shipping, totals)
- : t("order_success.row.free"),
- }]
+ label: t("order_success.row.shipping"),
+ value: isPositiveStoreApiAmount(totals.total_shipping)
+ ? formatStoreApiMoney(totals.total_shipping, totals)
+ : t("order_success.row.free"),
+ }]
: []),
...(isPositiveStoreApiAmount(totals.total_tax)
? [{ label: t("order_success.row.tax"), value: formatStoreApiMoney(totals.total_tax, totals) }]
@@ -1554,6 +1554,7 @@ function UnsubscribeFormShortcode({ attributes }: ShortcodeProps) {
}
export const WORDPRESS_SHORTCODE_RENDERERS: Record = {
+ elevenlabs_reader: (attributes) => ,
hero: (attributes) => ,
"video-hero": (attributes) => ,
"spotify-radio": (attributes) => ,
diff --git a/apps/storefront/src/lib/elevenlabs.ts b/apps/storefront/src/lib/elevenlabs.ts
new file mode 100644
index 0000000..dec112d
--- /dev/null
+++ b/apps/storefront/src/lib/elevenlabs.ts
@@ -0,0 +1,46 @@
+
+import { graphqlRequest } from "@funky/sdk";
+
+export type ElevenLabsPayload = {
+ post: {
+ elwrReaderHtml: string | null;
+ } | null;
+ elwrHeadlessAssets: {
+ css: string | null;
+ scriptUrl: string | null;
+ } | null;
+};
+
+const QUERY = `
+ query ElevenLabsReader($id: ID!) {
+ post(id: $id, idType: DATABASE_ID) {
+ elwrReaderHtml
+ }
+ elwrHeadlessAssets {
+ css
+ scriptUrl
+ }
+ }
+`;
+
+export async function fetchElevenLabsReader(
+ postId: number
+): Promise {
+ const { data, errors } =
+ await graphqlRequest(
+ QUERY,
+ { id: String(postId) }
+ );
+
+ if (errors?.length) {
+ throw new Error(
+ errors.map(({ message }) => message).join("; ")
+ );
+ }
+
+ if (!data) {
+ throw new Error("ElevenLabs GraphQL returned no data");
+ }
+
+ return data;
+}
diff --git a/apps/storefront/src/lib/pages.ts b/apps/storefront/src/lib/pages.ts
index 138d4c4..f81452c 100644
--- a/apps/storefront/src/lib/pages.ts
+++ b/apps/storefront/src/lib/pages.ts
@@ -500,25 +500,25 @@ async function getPage(id: string | number, idType: "URI" | "DATABASE_ID"): Prom
page.translations?.flatMap((translation) =>
translation?.uri
? [{
- databaseId: translation.databaseId,
- uri: translation.uri,
- languageCode: translation.language?.code?.toLowerCase() || resolvePathLanguageCode(
- translation.uri,
- STOREFRONT_EXPECTED_LOCALES,
- STOREFRONT_DEFAULT_LANGUAGE,
- STOREFRONT_BACKEND_PROFILE === "blog",
- ),
- }]
+ databaseId: translation.databaseId,
+ uri: translation.uri,
+ languageCode: translation.language?.code?.toLowerCase() || resolvePathLanguageCode(
+ translation.uri,
+ STOREFRONT_EXPECTED_LOCALES,
+ STOREFRONT_DEFAULT_LANGUAGE,
+ STOREFRONT_BACKEND_PROFILE === "blog",
+ ),
+ }]
: [],
) || [],
author: author
? {
- id: author.id,
- name: author.name?.trim() || "Unknown author",
- description: author.description,
- uri: author.uri,
- avatarUrl: author.avatar?.url || null,
- }
+ id: author.id,
+ name: author.name?.trim() || "Unknown author",
+ description: author.description,
+ uri: author.uri,
+ avatarUrl: author.avatar?.url || null,
+ }
: null,
featuredImage: normalizeFeaturedImage(page.featuredImage, seo?.schema),
scripts: page.enqueuedScripts?.nodes.map(mapScript) || [],
diff --git a/apps/storefront/src/lib/shortcodeRegistry.mjs b/apps/storefront/src/lib/shortcodeRegistry.mjs
index 3f0da5c..5692521 100644
--- a/apps/storefront/src/lib/shortcodeRegistry.mjs
+++ b/apps/storefront/src/lib/shortcodeRegistry.mjs
@@ -1,4 +1,5 @@
export const CONTENT_SHORTCODE_NAMES = Object.freeze([
+ "elevenlabs_reader",
"hero",
"video-hero",
"spotify-radio",
diff --git a/apps/storefront/src/pages/PostMockupPage.tsx b/apps/storefront/src/pages/PostMockupPage.tsx
index 21c1d2c..8c68f4c 100644
--- a/apps/storefront/src/pages/PostMockupPage.tsx
+++ b/apps/storefront/src/pages/PostMockupPage.tsx
@@ -1,3 +1,4 @@
+
import {
useEffect,
useLayoutEffect,
@@ -13,6 +14,8 @@ import { Breadcrumbs, type BreadcrumbItem } from "../components/Breadcrumbs";
import { ContentLoadingState } from "../components/ContentLoadingState";
import { GuestStarRating } from "../components/GuestStarRating";
import { renderCmsContent } from "../components/CmsPageContent";
+import { ElevenLabsHeadlessReader } from "../components/ElevenLabsHeadlessReader";
+import { ElevenLabsExistingMarkupAssets } from "../components/ElevenLabsExistingMarkupAssets";
import { WORDPRESS_SHORTCODE_RENDERERS } from "../components/wordpressShortcodes";
import { APPLICATION_SHORTCODE_RENDERERS } from "../components/applicationShortcodeRenderers";
import { useIncrementalData } from "@funky/sdk/react";
@@ -136,11 +139,11 @@ function PostMockupPageInner({
opengraphDescription={post.seo.opengraphDescription || undefined}
image={post.featuredImage
? {
- url: post.featuredImage.sourceUrl,
- alt: post.featuredImage.altText || post.title,
- width: post.featuredImage.width,
- height: post.featuredImage.height,
- }
+ url: post.featuredImage.sourceUrl,
+ alt: post.featuredImage.altText || post.title,
+ width: post.featuredImage.width,
+ height: post.featuredImage.height,
+ }
: post.seo.opengraphImage
? { url: post.seo.opengraphImage, alt: post.title }
: undefined}
@@ -277,7 +280,12 @@ function PostMockupPageInner({
@@ -412,11 +420,10 @@ function TableOfContents({ entries }: { entries: TocEntry[] }) {
{entry.text}
@@ -441,24 +448,21 @@ function TocDotRail({ entries, side }: { entries: TocEntry[]; side: "left" | "ri
return (
-
+
{entries.map((entry) => {
const isActive = entry.id === activeId;
return (
{entry.text}
@@ -470,29 +474,44 @@ function TocDotRail({ entries, side }: { entries: TocEntry[]; side: "left" | "ri
);
}
+
function PostContentBody({
contentRef,
html,
+ postId,
className,
}: {
contentRef: RefObject;
html: string;
+ postId: number;
className?: string;
}) {
return (
+
+
{renderCmsContent(
html,
- { ...WORDPRESS_SHORTCODE_RENDERERS, ...APPLICATION_SHORTCODE_RENDERERS },
+ {
+ ...WORDPRESS_SHORTCODE_RENDERERS,
+ elevenlabs_reader: (attrs) => (
+
+ ),
+ ...APPLICATION_SHORTCODE_RENDERERS,
+ }
)}
);
}
+
function AuthorBio({ author, layout = "fullwidth" }: { author: CmsPost["author"]; layout?: AuthorLayout }) {
const initials = author.name
.split(" ")
diff --git a/apps/storefront/vite.config.ts.timestamp-1793711596638-fe440ddd99829.mjs b/apps/storefront/vite.config.ts.timestamp-1793711596638-fe440ddd99829.mjs
new file mode 100644
index 0000000..4814836
--- /dev/null
+++ b/apps/storefront/vite.config.ts.timestamp-1793711596638-fe440ddd99829.mjs
@@ -0,0 +1,29 @@
+// vite.config.ts
+import { defineConfig } from "file:///D:/superfunky-storefront/node_modules/.pnpm/vite@5.4.21_@types+node@26.1.2/node_modules/vite/dist/node/index.js";
+import react from "file:///D:/superfunky-storefront/node_modules/.pnpm/@vitejs+plugin-react@4.7.0_vite@5.4.21_@types+node@26.1.2_/node_modules/@vitejs/plugin-react/dist/index.js";
+import { fileURLToPath } from "node:url";
+var __vite_injected_original_import_meta_url = "file:///D:/superfunky-storefront/apps/storefront/vite.config.ts";
+var frontendRoot = fileURLToPath(new URL("../..", __vite_injected_original_import_meta_url));
+var vite_config_default = defineConfig({
+ define: {
+ "import.meta.env.STOREFRONT_EXPECTED_LOCALES": JSON.stringify(
+ process.env.STOREFRONT_EXPECTED_LOCALES || ""
+ )
+ },
+ plugins: [react()],
+ build: {
+ manifest: true,
+ sourcemap: true
+ },
+ server: {
+ host: "127.0.0.1",
+ port: 4173,
+ fs: {
+ allow: [frontendRoot]
+ }
+ }
+});
+export {
+ vite_config_default as default
+};
+//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJEOlxcXFxzdXBlcmZ1bmt5LXN0b3JlZnJvbnRcXFxcYXBwc1xcXFxzdG9yZWZyb250XCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCJEOlxcXFxzdXBlcmZ1bmt5LXN0b3JlZnJvbnRcXFxcYXBwc1xcXFxzdG9yZWZyb250XFxcXHZpdGUuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9EOi9zdXBlcmZ1bmt5LXN0b3JlZnJvbnQvYXBwcy9zdG9yZWZyb250L3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSBcInZpdGVcIjtcclxuaW1wb3J0IHJlYWN0IGZyb20gXCJAdml0ZWpzL3BsdWdpbi1yZWFjdFwiO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSBcIm5vZGU6dXJsXCI7XHJcblxyXG5jb25zdCBmcm9udGVuZFJvb3QgPSBmaWxlVVJMVG9QYXRoKG5ldyBVUkwoXCIuLi8uLlwiLCBpbXBvcnQubWV0YS51cmwpKTtcclxuXHJcbi8vIGBAZnVua3kvdWlgIGlzIGEgcmVhbCBwbnBtIHdvcmtzcGFjZSBwYWNrYWdlIG5vdyAoc2VlIC4uLy4uL3BucG0td29ya3NwYWNlLnlhbWwpLFxyXG4vLyBzeW1saW5rZWQgaW50byBub2RlX21vZHVsZXMgd2l0aCBpdHMgcGVlckRlcGVuZGVuY2llcyAocmVhY3QsIHJlYWN0LXJvdXRlci1kb20sXHJcbi8vIGx1Y2lkZS1yZWFjdCwgcmVhY3QtaGVsbWV0LWFzeW5jKSByZXNvbHZlZCBhZ2FpbnN0IHRoaXMgYXBwJ3MgY29waWVzIFx1MjAxNCBubyBtYW51YWxcclxuLy8gYWxpYXNpbmcgbmVlZGVkIGZvciBpdCB0byBzaGFyZSBhIHNpbmdsZSBpbnN0YW5jZSBvZiB0aG9zZSBsaWJyYXJpZXMuXHJcbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XHJcbiAgZGVmaW5lOiB7XHJcbiAgICBcImltcG9ydC5tZXRhLmVudi5TVE9SRUZST05UX0VYUEVDVEVEX0xPQ0FMRVNcIjogSlNPTi5zdHJpbmdpZnkoXHJcbiAgICAgIHByb2Nlc3MuZW52LlNUT1JFRlJPTlRfRVhQRUNURURfTE9DQUxFUyB8fCBcIlwiLFxyXG4gICAgKSxcclxuICB9LFxyXG4gIHBsdWdpbnM6IFtyZWFjdCgpXSxcclxuICBidWlsZDoge1xyXG4gICAgbWFuaWZlc3Q6IHRydWUsXHJcbiAgICBzb3VyY2VtYXA6IHRydWUsXHJcbiAgfSxcclxuICBzZXJ2ZXI6IHtcclxuICAgIGhvc3Q6IFwiMTI3LjAuMC4xXCIsXHJcbiAgICBwb3J0OiA0MTczLFxyXG4gICAgZnM6IHtcclxuICAgICAgYWxsb3c6IFtmcm9udGVuZFJvb3RdLFxyXG4gICAgfSxcclxuICB9LFxyXG59KTtcclxuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUFrVCxTQUFTLG9CQUFvQjtBQUMvVSxPQUFPLFdBQVc7QUFDbEIsU0FBUyxxQkFBcUI7QUFGaUssSUFBTSwyQ0FBMkM7QUFJaFAsSUFBTSxlQUFlLGNBQWMsSUFBSSxJQUFJLFNBQVMsd0NBQWUsQ0FBQztBQU1wRSxJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixRQUFRO0FBQUEsSUFDTiwrQ0FBK0MsS0FBSztBQUFBLE1BQ2xELFFBQVEsSUFBSSwrQkFBK0I7QUFBQSxJQUM3QztBQUFBLEVBQ0Y7QUFBQSxFQUNBLFNBQVMsQ0FBQyxNQUFNLENBQUM7QUFBQSxFQUNqQixPQUFPO0FBQUEsSUFDTCxVQUFVO0FBQUEsSUFDVixXQUFXO0FBQUEsRUFDYjtBQUFBLEVBQ0EsUUFBUTtBQUFBLElBQ04sTUFBTTtBQUFBLElBQ04sTUFBTTtBQUFBLElBQ04sSUFBSTtBQUFBLE1BQ0YsT0FBTyxDQUFDLFlBQVk7QUFBQSxJQUN0QjtBQUFBLEVBQ0Y7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=