add rebrand landing page (#211)
This commit is contained in:
@@ -1,148 +0,0 @@
|
||||
const codeTheme: {
|
||||
[key: string]: React.CSSProperties;
|
||||
} = {
|
||||
hljs: {
|
||||
display: "block",
|
||||
overflowX: "auto",
|
||||
padding: "0.5em",
|
||||
background: "#070808",
|
||||
color: "#d6deeb",
|
||||
},
|
||||
"hljs-keyword": {
|
||||
color: "#c792ea",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
"hljs-built_in": {
|
||||
color: "#addb67",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
"hljs-type": {
|
||||
color: "#82aaff",
|
||||
},
|
||||
"hljs-literal": {
|
||||
color: "#ff5874",
|
||||
},
|
||||
"hljs-number": {
|
||||
color: "#F78C6C",
|
||||
},
|
||||
"hljs-regexp": {
|
||||
color: "#5ca7e4",
|
||||
},
|
||||
"hljs-string": {
|
||||
color: "#ecc48d",
|
||||
},
|
||||
"hljs-subst": {
|
||||
color: "#d3423e",
|
||||
},
|
||||
"hljs-symbol": {
|
||||
color: "#82aaff",
|
||||
},
|
||||
"hljs-class": {
|
||||
color: "#ffcb8b",
|
||||
},
|
||||
"hljs-function": {
|
||||
color: "#82AAFF",
|
||||
},
|
||||
"hljs-title": {
|
||||
color: "#DCDCAA",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
"hljs-params": {
|
||||
color: "#7fdbca",
|
||||
},
|
||||
"hljs-comment": {
|
||||
color: "#637777",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
"hljs-doctag": {
|
||||
color: "#7fdbca",
|
||||
},
|
||||
"hljs-meta": {
|
||||
color: "#82aaff",
|
||||
},
|
||||
"hljs-meta-keyword": {
|
||||
color: "#82aaff",
|
||||
},
|
||||
"hljs-meta-string": {
|
||||
color: "#ecc48d",
|
||||
},
|
||||
"hljs-section": {
|
||||
color: "#82b1ff",
|
||||
},
|
||||
"hljs-tag": {
|
||||
color: "#7fdbca",
|
||||
},
|
||||
"hljs-name": {
|
||||
color: "#7fdbca",
|
||||
},
|
||||
"hljs-builtin-name": {
|
||||
color: "#7fdbca",
|
||||
},
|
||||
"hljs-attr": {
|
||||
color: "#7fdbca",
|
||||
},
|
||||
"hljs-attribute": {
|
||||
color: "#80cbc4",
|
||||
},
|
||||
"hljs-variable": {
|
||||
color: "#addb67",
|
||||
},
|
||||
"hljs-bullet": {
|
||||
color: "#d9f5dd",
|
||||
},
|
||||
"hljs-code": {
|
||||
color: "#80CBC4",
|
||||
},
|
||||
"hljs-emphasis": {
|
||||
color: "#c792ea",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
"hljs-strong": {
|
||||
color: "#addb67",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
"hljs-formula": {
|
||||
color: "#c792ea",
|
||||
},
|
||||
"hljs-link": {
|
||||
color: "#ff869a",
|
||||
},
|
||||
"hljs-quote": {
|
||||
color: "#697098",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
"hljs-selector-tag": {
|
||||
color: "#ff6363",
|
||||
},
|
||||
"hljs-selector-id": {
|
||||
color: "#fad430",
|
||||
},
|
||||
"hljs-selector-class": {
|
||||
color: "#addb67",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
"hljs-selector-attr": {
|
||||
color: "#c792ea",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
"hljs-selector-pseudo": {
|
||||
color: "#c792ea",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
"hljs-template-tag": {
|
||||
color: "#c792ea",
|
||||
},
|
||||
"hljs-template-variable": {
|
||||
color: "#addb67",
|
||||
},
|
||||
"hljs-addition": {
|
||||
color: "#addb67ff",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
"hljs-deletion": {
|
||||
color: "#EF535090",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
};
|
||||
|
||||
export default codeTheme;
|
@@ -16,7 +16,6 @@
|
||||
"@types/node": "^22.15.2",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@usesend/eslint-config": "workspace:*",
|
||||
"@usesend/tailwind-config": "workspace:*",
|
||||
"@usesend/typescript-config": "workspace:*",
|
||||
@@ -48,16 +47,17 @@
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"framer-motion": "^12.9.2",
|
||||
"hast-util-to-jsx-runtime": "^2.3.6",
|
||||
"input-otp": "^1.4.2",
|
||||
"lucide-react": "^0.503.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"pnpm": "^10.9.0",
|
||||
"react-hook-form": "^7.56.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"recharts": "^2.15.3",
|
||||
"shiki": "^3.3.0",
|
||||
"sonner": "^2.0.3",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zod": "^3.24.3"
|
||||
}
|
||||
}
|
||||
}
|
36
packages/ui/src/code-block.tsx
Normal file
36
packages/ui/src/code-block.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { BundledLanguage, codeToHast } from "shiki";
|
||||
import { toJsxRuntime } from "hast-util-to-jsx-runtime";
|
||||
import { Fragment } from "react";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
import { cn } from "../lib/utils";
|
||||
|
||||
interface Props {
|
||||
children: string;
|
||||
lang: BundledLanguage;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export async function CodeBlock(props: Props) {
|
||||
const out = await codeToHast(props.children, {
|
||||
lang: props.lang,
|
||||
themes: {
|
||||
dark: "catppuccin-mocha",
|
||||
light: "catppuccin-latte",
|
||||
},
|
||||
});
|
||||
|
||||
return toJsxRuntime(out, {
|
||||
Fragment,
|
||||
jsx,
|
||||
jsxs,
|
||||
components: {
|
||||
// your custom `pre` element
|
||||
pre: (nodeProps) => (
|
||||
<pre
|
||||
{...nodeProps}
|
||||
className={cn(nodeProps.className, props.className)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
}) as React.JSX.Element;
|
||||
}
|
@@ -1,101 +0,0 @@
|
||||
import { Light as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import js from "react-syntax-highlighter/dist/esm/languages/hljs/javascript";
|
||||
import ruby from "react-syntax-highlighter/dist/esm/languages/hljs/ruby";
|
||||
import php from "react-syntax-highlighter/dist/esm/languages/hljs/php";
|
||||
import python from "react-syntax-highlighter/dist/esm/languages/hljs/python";
|
||||
// import { nightOwl } from "react-syntax-highlighter/dist/esm/styles/hljs";
|
||||
import codeTheme from "../code-theme";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs";
|
||||
import { Button } from "./button";
|
||||
import { ClipboardCopy, Check } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { cn } from "../lib/utils";
|
||||
|
||||
export type Language = "js" | "ruby" | "php" | "python" | "curl";
|
||||
|
||||
export type CodeBlock = {
|
||||
language: Language;
|
||||
title?: string;
|
||||
code: string;
|
||||
};
|
||||
|
||||
type CodeProps = {
|
||||
codeBlocks: CodeBlock[];
|
||||
codeClassName?: string;
|
||||
};
|
||||
|
||||
SyntaxHighlighter.registerLanguage("js", js);
|
||||
SyntaxHighlighter.registerLanguage("ruby", ruby);
|
||||
SyntaxHighlighter.registerLanguage("php", php);
|
||||
SyntaxHighlighter.registerLanguage("python", python);
|
||||
|
||||
export const Code: React.FC<CodeProps> = ({ codeBlocks, codeClassName }) => {
|
||||
const [selectedTab, setSelectedTab] = useState(
|
||||
codeBlocks[0]?.language ?? "js"
|
||||
);
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
|
||||
const copyToClipboard = async (code: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(code);
|
||||
setIsCopied(true);
|
||||
setTimeout(() => setIsCopied(false), 2000); // Reset the icon back to clipboard after 2 seconds
|
||||
} catch (err) {
|
||||
alert("Failed to copy code");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="rounded-xl bg-background border shadow-[#1e293b] shadow-lg">
|
||||
<Tabs
|
||||
defaultValue={codeBlocks[0]?.language}
|
||||
onValueChange={(val) => setSelectedTab(val as Language)}
|
||||
>
|
||||
<div className="flex justify-between items-center border-b py-1 px-2">
|
||||
<TabsList className="w-full rounded-none justify-start bg-transparent h-12">
|
||||
<div className="">
|
||||
{codeBlocks.map((block) => (
|
||||
<TabsTrigger
|
||||
key={block.language}
|
||||
value={block.language}
|
||||
className="data-[state=active]:bg-accent py-0.5 px-4 "
|
||||
>
|
||||
{block.title || block.language}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</div>
|
||||
</TabsList>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="icon"
|
||||
onClick={() =>
|
||||
copyToClipboard(
|
||||
codeBlocks.find((block) => block.language === selectedTab)
|
||||
?.code || ""
|
||||
)
|
||||
}
|
||||
>
|
||||
{isCopied ? (
|
||||
<Check className="h-4 w-4" />
|
||||
) : (
|
||||
<ClipboardCopy className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
{codeBlocks.map((block) => (
|
||||
<TabsContent
|
||||
key={block.language}
|
||||
value={block.language}
|
||||
className="mt-0"
|
||||
>
|
||||
<div className={cn("overflow-auto rounded-b-xl", codeClassName)}>
|
||||
<SyntaxHighlighter language={block.language} style={codeTheme}>
|
||||
{block.code}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</TabsContent>
|
||||
))}
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
@@ -14,7 +14,7 @@ export const H1 = React.forwardRef<HTMLHeadingElement, TypographyProps>(
|
||||
ref={ref}
|
||||
className={cn(
|
||||
// font-mono, larger and a bit bolder
|
||||
" font-mono text-xl font-medium",
|
||||
" font-mono text-xl font-medium text-primary",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
@@ -14,7 +14,7 @@
|
||||
--popover: 220 2% 96%;
|
||||
--popover-foreground: 234 16% 35%;
|
||||
|
||||
--primary: 200 65% 14%;
|
||||
--primary: 167 34% 20%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
|
||||
--secondary: 210 40% 96.1%;
|
||||
@@ -63,6 +63,7 @@
|
||||
--purple: 266 85% 58%;
|
||||
--yellow: 35 77% 49%;
|
||||
--gray: 220 9% 46%;
|
||||
--primary-light: 183 74% 35%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
@@ -75,7 +76,7 @@
|
||||
--popover: 240 21% 15%;
|
||||
--popover-foreground: 226 64% 88%;
|
||||
|
||||
--primary: 220 23% 95%;
|
||||
--primary: 167 64% 94%;
|
||||
--primary-foreground: 240 23% 9%;
|
||||
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
@@ -122,6 +123,7 @@
|
||||
--purple: 267 84% 81%;
|
||||
--yellow: 41 86% 83%;
|
||||
--gray: 218 11% 65%;
|
||||
--primary-light: 170 57% 73%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +139,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.shiki,
|
||||
.shiki span {
|
||||
color: var(--shiki-dark) !important;
|
||||
background-color: var(--shiki-dark-bg) !important;
|
||||
/* Optional, if you also want font styles */
|
||||
font-style: var(--shiki-dark-font-style) !important;
|
||||
font-weight: var(--shiki-dark-font-weight) !important;
|
||||
text-decoration: var(--shiki-dark-text-decoration) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* .app,
|
||||
::before,
|
||||
::after {
|
||||
|
Reference in New Issue
Block a user