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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cloud/app/components/docs-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ function createSidebarConfig(): SidebarConfig {

allDocInfo.forEach((doc) => {
// Extract the slug pattern from the path
const keyPath = doc.path;
// Strip "docs/" prefix to match the lookup format
const prefix = "docs/";
const keyPath = doc.path.startsWith(prefix)
? doc.path.slice(prefix.length)
: doc.path;
slugToRoutePathMap.set(keyPath, doc.routePath);
});

Expand Down
182 changes: 91 additions & 91 deletions cloud/app/components/mdx/component-registry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { Button } from "@/app/components/ui/button";
import { ButtonLink } from "@/app/components/ui/button-link";
import MirascopeLogo from "@/app/components/blocks/branding/mirascope-logo";
import ProductLogo from "@/app/components/blocks/branding/mirascope-logo";
// import { ProviderCodeWrapper } from "./ProviderCodeWrapper";
import { ModelProviderCodeWrapper } from "@/app/components/mdx/elements/model-provider-code-wrapper";
// import { ResponsiveImage } from "@/src/components/mdx/providers/ResponsiveImage";
// import { devComponents } from "@/app/components/mdx/elements/DevComponents";
import { idSlugFromChildren } from "@/app/lib/mdx/heading-utils";
Expand Down Expand Up @@ -338,95 +338,95 @@ const mediaElements = {
},
};

// todo(sebastian): bring back code elements
// const codeElements = {
// // Inline code - this is only for inline elements, not code blocks
// code: (props: React.ComponentPropsWithoutRef<"code">) => {
// // Don't apply inline code styling to code blocks (which are children of pre tags)
// const isInPre = React.useRef<boolean>(false);
// React.useLayoutEffect(() => {
// // Type assertion for DOM properties access
// const element = props as unknown as {
// parentElement?: { tagName: string };
// };
// const parentIsPre =
// props.className?.includes("language-") ||
// props.className?.includes("shiki") ||
// element.parentElement?.tagName === "PRE";
// isInPre.current = !!parentIsPre;
// }, [props]);

// // Only apply inline code styling to actual inline code, not code blocks
// if (isInPre.current) {
// return <code {...props} />;
// }

// return (
// <code
// className="bg-muted text-muted-foreground rounded px-1 py-0.5 font-mono text-[0.9em]"
// {...props}
// />
// );
// },

// // Code blocks - use our custom CodeBlock component with provider substitution
// pre: (props: React.ComponentPropsWithoutRef<"pre">) => {
// // Get meta information from our data attribute or initialize to empty
// let meta = (props as any)["data-meta"] || "";

// // Initialize variables for code content and language
// let codeContent = "";
// let language = "txt";

// // Process children to find code content and language
// if (props.children) {
// const children = React.Children.toArray(props.children);

// // Loop through children to find code content (typically there's only one child)
// for (const child of children) {
// if (!React.isValidElement(child)) continue;

// // Check if this is a code element or has code-like properties
// const childProps = child.props as {
// className?: string;
// children?: React.ReactNode | string;
// };

// // Extract language from className
// if (childProps.className?.includes("language-")) {
// language =
// (childProps.className.match(/language-(\w+)/) || [])[1] || "txt";

// // Also check for meta in className (legacy approach)
// // This looks for patterns like {1-3} or {1,3} after the language
// if (!meta) {
// const metaMatch = childProps.className.match(/\{([^}]+)\}/);
// meta = metaMatch ? `{${metaMatch[1]}}` : "";
// }
// }

// // Get code content
// if (typeof childProps.children === "string") {
// codeContent = childProps.children;
// break;
// }
// }
// }

// // Handle mermaid diagrams
// if (language === "mermaid" && codeContent) {
// return <MermaidDiagram chart={codeContent.trim()} />;
// }

// return (
// <ProviderCodeWrapper
// code={codeContent.replace(/\n$/, "")}
// language={language}
// meta={meta}
// />
// );
// },
// };
const codeElements = {
// Inline code - this is only for inline elements, not code blocks
code: (props: React.ComponentPropsWithoutRef<"code">) => {
// Don't apply inline code styling to code blocks (which are children of pre tags)
const isInPre = React.useRef<boolean>(false);
React.useLayoutEffect(() => {
// Type assertion for DOM properties access
const element = props as unknown as {
parentElement?: { tagName: string };
};
const parentIsPre =
props.className?.includes("language-") ||
props.className?.includes("shiki") ||
element.parentElement?.tagName === "PRE";
isInPre.current = !!parentIsPre;
}, [props]);

// Only apply inline code styling to actual inline code, not code blocks
if (isInPre.current) {
return <code {...props} />;
}

return (
<code
className="bg-muted text-muted-foreground rounded px-1 py-0.5 font-mono text-[0.9em]"
{...props}
/>
);
},

// Code blocks - use our custom CodeBlock component with provider substitution
pre: (props: React.ComponentPropsWithoutRef<"pre">) => {
// Get meta information from our data attribute or initialize to empty
let meta =
(props as unknown as { "data-meta"?: string })["data-meta"] || "";

// Initialize variables for code content and language
let codeContent = "";
let language = "txt";

// Process children to find code content and language
if (props.children) {
const children = React.Children.toArray(props.children);

// Loop through children to find code content (typically there's only one child)
for (const child of children) {
if (!React.isValidElement(child)) continue;

// Check if this is a code element or has code-like properties
const childProps = child.props as {
className?: string;
children?: React.ReactNode | string;
};

// Extract language from className
if (childProps.className?.includes("language-")) {
language =
(childProps.className.match(/language-(\w+)/) || [])[1] || "txt";

// Also check for meta in className (legacy approach)
// This looks for patterns like {1-3} or {1,3} after the language
if (!meta) {
const metaMatch = childProps.className.match(/\{([^}]+)\}/);
meta = metaMatch ? `{${metaMatch[1]}}` : "";
}
}

// Get code content
if (typeof childProps.children === "string") {
codeContent = childProps.children;
break;
}
}
}

// Handle mermaid diagrams
if (language === "mermaid" && codeContent) {
return <MermaidDiagram chart={codeContent.trim()} />;
}

return (
<ModelProviderCodeWrapper
code={codeContent.replace(/\n$/, "")}
language={language}
meta={meta}
/>
);
},
};

// -----------------------------------------------------------------------------
// Complete Component Registry
Expand All @@ -439,5 +439,5 @@ export default {
...listElements,
...tableElements,
...mediaElements,
// ...codeElements,
...codeElements,
};
63 changes: 63 additions & 0 deletions cloud/app/components/mdx/elements/analytics-code-block.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useRef, useMemo } from "react";
import { CodeBlock } from "@/app/components/blocks/code-block/code-block";
// import analyticsManager from "@/src/lib/services/analytics";

interface AnalyticsCodeBlockProps {
code: string;
language?: string;
meta?: string;
className?: string;
showLineNumbers?: boolean;
}

export function AnalyticsCodeBlock({
code,
language,
meta,
className,
showLineNumbers,
}: AnalyticsCodeBlockProps) {
const codeRef = useRef<HTMLDivElement>(null);

// Create a stable identifier for this code block based on its content
// This ensures the ID remains consistent across rerenders
const codeHash = useMemo(() => {
// Simple hash function for the code content
let hash = 0;
for (let i = 0; i < code.length; i++) {
const char = code.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash; // Convert to 32bit integer
}
return Math.abs(hash).toString(16).substring(0, 8);
}, [code]);

const onCopy = () => {
// const pagePath = window.location.pathname;
// Use path, language and hash of code to create a stable identifier
// const itemId = `${pagePath}#${language || "code"}-${codeHash}`;
// analyticsManager.trackCopyEvent({
// contentType: "code_snippet",
// itemId,
// product,
// language: language || "text",
// });
};

return (
<div
ref={codeRef}
data-code-hash={codeHash}
className="analytics-code-block"
>
<CodeBlock
code={code}
language={language}
meta={meta}
className={className}
showLineNumbers={showLineNumbers}
onCopy={onCopy}
/>
</div>
);
}
9 changes: 5 additions & 4 deletions cloud/app/components/mdx/elements/code-block-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useState } from "react";
import LoadingContent from "@/app/components/blocks/loading-content";
import { useProvider } from "@/app/components/mdx/elements/model-provider-provider";
// import { AnalyticsCodeBlock } from "./AnalyticsCodeBlock";
import { AnalyticsCodeBlock } from "./analytics-code-block";
interface ProviderCodeBlockProps {
examplePath: string; // Path relative to public/examples
language?: string;
Expand All @@ -14,7 +14,7 @@ interface ProviderCodeBlockProps {
*/
export default function ProviderCodeBlock({
examplePath,
// language = "python",
language = "python",
className = "",
}: ProviderCodeBlockProps) {
// Get the currently selected provider
Expand Down Expand Up @@ -81,8 +81,9 @@ export default function ProviderCodeBlock({
Example for {provider} not available yet.
</div>
)}
{/* {currentProviderCode && <AnalyticsCodeBlock code={currentProviderCode} language={language} />} */}
{currentProviderCode && "AnalyticsCodeBlock"}
{currentProviderCode && (
<AnalyticsCodeBlock code={currentProviderCode} language={language} />
)}
</>
);
}
7 changes: 2 additions & 5 deletions cloud/app/components/mdx/elements/install-snippet.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useProvider } from "@/app/components/mdx/elements/model-provider-provider";
import type { Provider } from "@/app/components/mdx/elements/model-provider-provider";
// import { AnalyticsCodeBlock } from "@/app/components/blocks/mdx/AnalyticsCodeBlock";
import { AnalyticsCodeBlock } from "@/app/components/mdx/elements/analytics-code-block";
import { cn } from "@/app/lib/utils";
import { TabbedSection, Tab } from "./tabbed-section";

Expand Down Expand Up @@ -44,8 +44,6 @@ export function InstallSnippet({ className = "" }: InstallSnippetProps) {
const { provider } = useProvider();

// Generate install commands for each OS
// @ts-expect-error until todo(sebastian): add back code elements
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const generateCommand = (os: OS) => {
const setEnvCmd = os === "MacOS / Linux" ? "export" : "set";
const apiKeyVar = providerApiKeys[provider];
Expand All @@ -63,8 +61,7 @@ export function InstallSnippet({ className = "" }: InstallSnippetProps) {
<TabbedSection className={cn(className)}>
{operatingSystems.map((os) => (
<Tab key={os} value={os}>
{/* <AnalyticsCodeBlock code={generateCommand(os)} language="bash" /> */}
AnalyticsCodeBlock
<AnalyticsCodeBlock code={generateCommand(os)} language="bash" />
</Tab>
))}
</TabbedSection>
Expand Down
38 changes: 38 additions & 0 deletions cloud/app/components/mdx/elements/model-provider-code-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// No need to import React with JSX transform
import {
useProvider,
replaceProviderVariables,
} from "@/app/components/mdx/elements/model-provider-provider";
import { AnalyticsCodeBlock } from "@/app/components/mdx/elements/analytics-code-block";

/**
* A wrapper component for code blocks that handles provider-specific substitutions.
* This allows us to use standard markdown code blocks with provider-specific variables.
*/
export function ModelProviderCodeWrapper({
code,
language,
meta,
className,
}: {
code: string;
language: string;
meta?: string;
className?: string;
}) {
const { provider } = useProvider();

// Only process python or bash code
if (code && (language === "python" || language === "bash")) {
code = replaceProviderVariables(code, provider);
}

return (
<AnalyticsCodeBlock
className={className}
code={code}
language={language}
meta={meta}
/>
);
}
1 change: 1 addition & 0 deletions cloud/app/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const buttonVariants = cva(
"[&_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 aria-invalid:border-destructive",
"font-handwriting",
].join(" "),
{
variants: {
Expand Down
Loading