This commit is contained in:
geoffsee
2025-05-22 23:14:01 -04:00
commit 33679583af
242 changed files with 15090 additions and 0 deletions

13
src/layout/Content.tsx Normal file
View File

@@ -0,0 +1,13 @@
import { Flex } from "@chakra-ui/react";
import React from "react";
import { useIsMobile } from "../components/contexts/MobileContext";
function Content({ children }) {
const isMobile = useIsMobile();
return (
<Flex flexDirection="column" w="100%" h="100vh" p={!isMobile ? 4 : 1}>
{children}
</Flex>
);
}
export default Content;

48
src/layout/Hero.tsx Normal file
View File

@@ -0,0 +1,48 @@
import React from "react";
import { Box, Heading, Text } from "@chakra-ui/react";
import { usePageContext } from "../renderer/usePageContext";
import Routes from "../renderer/routes";
import { useIsMobile } from "../components/contexts/MobileContext";
export default function Hero() {
const pageContext = usePageContext();
const isMobile = useIsMobile();
return (
<Box p={2}>
<Box>
<Heading
textAlign={isMobile ? "left" : "right"}
minWidth="90px"
maxWidth={"220px"}
color="text.accent"
as="h3"
letterSpacing={"tight"}
size="lg"
>
{Routes[normalizePath(pageContext.urlPathname)]?.heroLabel}
</Heading>
</Box>
<Text
isTruncated
maxWidth="100%"
whiteSpace="nowrap"
letterSpacing={"tight"}
color="text.accent"
textAlign={isMobile ? "left" : "right"}
overflow="hidden"
>
{new Date().toLocaleDateString()}
</Text>
</Box>
);
}
const normalizePath = (path) => {
if (!path) return "/";
if (path.length > 1 && path.endsWith("/")) {
path = path.slice(0, -1);
}
return path.toLowerCase();
};

21
src/layout/Layout.css Normal file
View File

@@ -0,0 +1,21 @@
/* CSS for people who'd rather be coding */
* {
box-sizing: border-box; /* Because guessing sizes is for amateurs */
}
a {
text-decoration: none;
color: #fffff0; /* White, like the light at the end of a dark terminal */
}
a:hover {
color: #c0c0c0; /* Light gray, like the reflections of your code */
}
/* Media query, because even I have standards */
@media (max-width: 600px) {
body {
font-size: 14px; /* For ants */
}
}

52
src/layout/Layout.tsx Normal file
View File

@@ -0,0 +1,52 @@
import React, { useEffect, useState } from "react";
import { PageContextProvider } from "../renderer/usePageContext";
import { MobileProvider } from "../components/contexts/MobileContext";
import LayoutComponent from "./LayoutComponent";
import userOptionsStore from "../stores/UserOptionsStore";
import { observer } from "mobx-react-lite";
import { Chakra } from "../components/contexts/ChakraContext";
import { getTheme } from "./theme/color-themes";
export { Layout };
const Layout = observer(({ pageContext, children }) => {
const [activeTheme, setActiveTheme] = useState<string>("darknight");
useEffect(() => {
if (userOptionsStore.theme !== activeTheme) {
setActiveTheme(userOptionsStore.theme);
}
}, [userOptionsStore.theme]);
try {
if (pageContext?.headersOriginal) {
const headers = new Headers(pageContext.headersOriginal);
const cookies = headers.get("cookie");
const userPreferencesCookie = cookies
?.split("; ")
.find((row) => row.startsWith("user_preferences="))
?.split("=")[1];
try {
const { theme: receivedTheme } = JSON.parse(
atob(userPreferencesCookie ?? "{}"),
);
setActiveTheme(receivedTheme);
} catch (e) {}
}
} catch (e) {}
return (
<React.StrictMode>
<PageContextProvider pageContext={pageContext}>
<MobileProvider>
<Chakra theme={getTheme(activeTheme)}>
<LayoutComponent>{children}</LayoutComponent>
</Chakra>
</MobileProvider>
</PageContextProvider>
</React.StrictMode>
);
});

View File

@@ -0,0 +1,35 @@
import React from "react";
import { Grid, GridItem } from "@chakra-ui/react";
import Navigation from "./Navigation";
import Routes from "../renderer/routes";
import Hero from "./Hero";
import Content from "./Content";
import { useIsMobile } from "../components/contexts/MobileContext";
export default function LayoutComponent({ children }) {
const isMobile = useIsMobile();
return (
<Grid
templateAreas={
isMobile
? `"nav"
"main"`
: `"nav main"`
}
gridTemplateRows={isMobile ? "auto 1fr" : "1fr"}
gridTemplateColumns={isMobile ? "1fr" : "auto 1fr"}
minHeight="100vh"
gap="1"
>
<GridItem area={"nav"} hidden={false}>
<Navigation routeRegistry={Routes}>
<Hero />
</Navigation>
</GridItem>
<GridItem area={"main"}>
<Content>{children}</Content>
</GridItem>
</Grid>
);
}

43
src/layout/NavItem.tsx Normal file
View File

@@ -0,0 +1,43 @@
import { Box } from "@chakra-ui/react";
import React from "react";
function NavItem({ path, children, color, onClick, as, cursor }) {
return (
<Box
as={as ?? "a"}
href={path}
mb={2}
cursor={cursor}
// ml={5}
mr={2}
color={color ?? "text.accent"}
letterSpacing="normal"
display="block"
position="relative"
textAlign="right"
onClick={onClick}
_after={{
content: '""',
position: "absolute",
width: "100%",
height: "2px",
bottom: "0",
left: "0",
bg: "accent.secondary",
transform: "scaleX(0)",
transformOrigin: "right",
transition: "transform 0.3s ease-in-out",
}}
_hover={{
color: "tertiary.tertiary",
_after: {
transform: "scaleX(1)",
},
}}
>
{children}
</Box>
);
}
export default NavItem;

132
src/layout/Navigation.tsx Normal file
View File

@@ -0,0 +1,132 @@
import React, { useEffect } from "react";
import { observer } from "mobx-react-lite";
import {
Box,
Collapse,
Grid,
GridItem,
useBreakpointValue,
} from "@chakra-ui/react";
import { MenuIcon } from "lucide-react";
import Sidebar from "./Sidebar";
import NavItem from "./NavItem";
import menuState from "../stores/AppMenuStore";
import { usePageContext } from "../renderer/usePageContext";
import { useIsMobile } from "../components/contexts/MobileContext";
import { getTheme } from "./theme/color-themes";
import userOptionsStore from "../stores/UserOptionsStore";
const Navigation = observer(({ children, routeRegistry }) => {
const isMobile = useIsMobile();
const pageContext = usePageContext();
const currentPath = pageContext.urlPathname || "/";
const getTopValue = () => {
if (!isMobile) return undefined;
if (currentPath === "/") return 12;
return 0;
};
const variant = useBreakpointValue(
{
base: "outline",
md: "solid",
},
{
fallback: "md",
},
);
useEffect(() => {
menuState.closeMenu();
}, [variant]);
return (
<Grid templateColumns="1fr" templateRows="auto 1fr">
<GridItem
p={4}
position="fixed"
top={0}
left={0}
zIndex={1100}
width={isMobile ? "20%" : "100%"}
hidden={!isMobile}
>
<Grid templateColumns="auto 1fr" alignItems="center">
<GridItem>
<MenuIcon
cursor="pointer"
w={6}
h={6}
stroke={getTheme(userOptionsStore.theme).colors.text.accent}
onClick={() => {
switch (menuState.isOpen) {
case true:
menuState.closeMenu();
break;
case false:
menuState.openMenu();
break;
}
}}
/>
</GridItem>
<GridItem>{children}</GridItem>
</Grid>
</GridItem>
<GridItem>
<Collapse
hidden={isMobile && !menuState.isOpen}
in={(isMobile && menuState.isOpen) || !isMobile}
animateOpacity
>
<Grid
as="nav"
templateColumns="1fr"
width="100%"
h={isMobile ? "100vh" : "100vh"}
top={getTopValue()}
position={"relative"}
bg={"transparent"}
zIndex={1000}
gap={4}
p={isMobile ? 4 : 0}
>
<GridItem>{!isMobile && children}</GridItem>
<GridItem>
<Sidebar>
{Object.keys(routeRegistry)
.filter((p) => !routeRegistry[p].hideNav)
.map((path) => (
<NavItem key={path} path={path}>
{routeRegistry[path].sidebarLabel}
</NavItem>
))}
</Sidebar>
</GridItem>
</Grid>
</Collapse>
</GridItem>
{isMobile && (
<Box
position="fixed"
top="0"
left="0"
right="0"
height={menuState.isOpen ? "100vh" : "auto"}
pointerEvents="none"
zIndex={900}
>
<Box
height="100%"
transition="all 0.3s"
opacity={menuState.isOpen ? 1 : 0}
/>
</Box>
)}
</Grid>
);
});
export default Navigation;

141
src/layout/Sidebar.tsx Normal file
View File

@@ -0,0 +1,141 @@
import React, { useState } from "react";
import { Box, Flex, VStack } from "@chakra-ui/react";
import NavItem from "./NavItem";
import ToolBar from "../components/toolbar/Toolbar";
import { useIsMobile } from "../components/contexts/MobileContext";
import FeedbackModal from "../components/feedback/FeedbackModal";
import { ThemeSelectionOptions } from "../components/ThemeSelection";
function LowerSidebarContainer({ children, isMobile, ...props }) {
const bottom = isMobile ? undefined : "6rem";
const position = isMobile ? "relative" : "absolute";
return (
<Box width="100%" m={0.99} position={position} bottom={bottom} {...props}>
{children}
</Box>
);
}
function Sidebar({ children: navLinks }) {
const isMobile = useIsMobile();
return (
<SidebarContainer isMobile={isMobile}>
<VStack
spacing={6}
alignItems={isMobile ? "flex-start" : "flex-end"}
letterSpacing="tighter"
width="100%"
height="100%"
>
{navLinks}
<Box
alignItems={isMobile ? "flex-start" : "flex-end"}
bg="background.primary"
zIndex={1000}
width="100%"
fontSize={"x-small"}
>
<LowerSidebarContainer isMobile={isMobile}>
<ToolBar isMobile={isMobile} />
<RegulatoryItems isMobile={isMobile} />
<ThemeSelectionOptions />
</LowerSidebarContainer>
</Box>
</VStack>
{!isMobile && <BreathingVerticalDivider />}
</SidebarContainer>
);
}
function RegulatoryItems({ isMobile }) {
const [isFeedbackModalOpen, setFeedbackModalOpen] = useState(false);
const openFeedbackModal = () => setFeedbackModalOpen(true);
const closeFeedbackModal = () => setFeedbackModalOpen(false);
return (
<>
<VStack alignItems={isMobile ? "flex-start" : "flex-end"} spacing={1}>
<NavItem
color="text.tertiary"
as={"span"}
path=""
cursor={"pointer"}
onClick={() => {
window.open("https://seemueller.ai");
}}
>
seemueller.ai
</NavItem>
<NavItem
color="text.tertiary"
as={"span"}
path=""
cursor={"pointer"}
onClick={openFeedbackModal}
>
Feedback
</NavItem>
<NavItem color="text.tertiary" path="/privacy-policy">
Privacy Policy
</NavItem>
<NavItem color="text.tertiary" path="/terms-of-service">
Terms of Service
</NavItem>
</VStack>
{/* Feedback Modal */}
<FeedbackModal
isOpen={isFeedbackModalOpen}
onClose={closeFeedbackModal}
/>
</>
);
}
function SidebarContainer({ children, isMobile }) {
return (
<Flex
mt={isMobile ? 28 : undefined}
position="relative"
height="100vh"
width="100%"
>
{children}
</Flex>
);
}
function BreathingVerticalDivider() {
return (
<Box
position="absolute"
h="150%"
right={0}
bottom={0}
width="2px"
background="text.secondary"
animation="breathing 3s ease-in-out infinite"
>
<style>
{`
@keyframes breathing {
0%, 100% {
opacity: 0.7;
transform: scaleY(1);
}
50% {
opacity: 1;
transform: scaleY(1.2);
}
}
`}
</style>
</Box>
);
}
export default Sidebar;

View File

@@ -0,0 +1,19 @@
import { useEffect, useState } from "react";
import { useMediaQuery } from "@chakra-ui/react";
// Only use this when it is necessary to style responsively outside a MobileProvider.
export function useIsMobile() {
const [isMobile, setIsMobile] = useState(false);
const [isFallbackMobile] = useMediaQuery("(max-width: 768px)");
useEffect(() => {
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
const mobile =
/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(
userAgent.toLowerCase(),
);
setIsMobile(mobile);
}, []);
return isMobile || isFallbackMobile;
}

View File

@@ -0,0 +1,239 @@
import { extendTheme } from "@chakra-ui/react";
const fonts = {
body: "monospace",
heading: "monospace",
};
const styles = {
global: {
body: {
fontFamily: fonts.body,
bg: "background.primary",
color: "text.primary",
margin: 0,
overflow: "hidden",
},
html: {
overflow: "hidden",
},
"::selection": {
backgroundColor: "accent.secondary",
color: "background.primary",
},
},
};
const components = {
Button: {
baseStyle: {
fontWeight: "bold",
borderRadius: "md", // Slightly rounded corners
},
variants: {
solid: {
bg: "accent.primary",
color: "background.primary",
_hover: {
bg: "accent.primary",
color: "background.primary",
},
},
outline: {
borderColor: "accent.primary",
color: "text.primary",
_hover: {
bg: "accent.primary",
color: "background.primary",
},
},
ghost: {
color: "text.primary",
_hover: {
bg: "background.secondary",
},
},
},
},
Link: {
baseStyle: {
color: "accent.secondary",
_hover: {
color: "accent.primary",
textDecoration: "none",
},
},
},
Heading: {
baseStyle: {
color: "text.primary",
letterSpacing: "tight",
},
sizes: {
"4xl": { fontSize: ["6xl", null, "7xl"], lineHeight: 1 },
"3xl": { fontSize: ["5xl", null, "6xl"], lineHeight: 1.2 },
"2xl": { fontSize: ["4xl", null, "5xl"] },
xl: { fontSize: ["3xl", null, "4xl"] },
lg: { fontSize: ["2xl", null, "3xl"] },
md: { fontSize: "xl" },
sm: { fontSize: "md" },
xs: { fontSize: "sm" },
},
},
Text: {
baseStyle: {
color: "text.primary",
},
variants: {
secondary: {
color: "text.secondary",
},
accent: {
color: "text.accent",
},
},
},
Input: {
variants: {
filled: {
field: {
bg: "background.secondary",
_hover: {
bg: "background.tertiary",
},
_focus: {
bg: "background.tertiary",
borderColor: "accent.primary",
},
},
},
},
},
MDXEditor: {
baseStyle: (props) => ({
border: "1px solid",
borderColor: "text.primary",
borderRadius: "lg",
height: "sm",
bg: "background.primary",
color: "text.primary",
boxShadow: "md",
// p: 4,
".mdxeditor-toolbar": {
border: "1px solid",
borderColor: "text.primary",
borderRadius: "xl",
bg: "background.primary",
m: 2,
p: 2,
// mb: 3,
// p: 3,
"& button": {
border: "none",
borderRadius: "md",
cursor: "pointer",
px: 3,
py: 1,
mr: 2,
transition: "all 0.3s ease",
_hover: {
bg: "text.secondary",
},
_active: {
bg: "background.primary",
color: "text.primary",
},
'&[data-state="on"]': {
bg: "transparent",
fill: "text.primary",
stroke: "text.primary",
boxShadow: "0 0 0 2px var(--text-primary)",
transform: "translateY(-1px)",
transition: "all 0.2s ease",
// border: '2px solid transparent', // No border needed for SVG
},
'&[data-state="off"]': {
bg: "transparent",
fill: "text.secondary",
stroke: "text.secondary",
opacity: 0.8,
transition: "all 0.2s ease",
},
},
},
"[aria-label='editable markdown']": {
color: "text.primary",
},
}),
},
CodeBlocks: {
baseStyle: (props) => ({
bg: "background.primary",
// color: 'text.primary',
}),
},
};
const Base_theme = extendTheme({
config: {
cssVarPrefix: "wgs",
initialColorMode: "dark",
useSystemColorMode: false,
},
fonts,
styles,
components,
letterSpacings: {
tighter: "-0.05em",
tight: "-0.025em",
normal: "0",
wide: "0.025em",
wider: "0.05em",
widest: "0.1em",
},
space: {
px: "1px",
0.5: "0.125rem",
1: "0.25rem",
1.5: "0.375rem",
2: "0.5rem",
2.5: "0.625rem",
3: "0.75rem",
3.5: "0.875rem",
4: "1rem",
5: "1.25rem",
6: "1.5rem",
7: "1.75rem",
8: "2rem",
9: "2.25rem",
10: "2.5rem",
12: "3rem",
14: "3.5rem",
16: "4rem",
18: "4.5rem",
20: "5rem",
22: "5.5rem",
24: "6rem",
28: "7rem",
32: "8rem",
34: "8.5rem",
36: "9rem",
38: "9.5rem",
40: "10rem",
44: "11rem",
48: "12rem",
52: "13rem",
56: "14rem",
60: "15rem",
64: "16rem",
72: "18rem",
80: "20rem",
96: "24rem",
},
});
export default Base_theme;

View File

@@ -0,0 +1,32 @@
export default {
brand: {
900: "#21252b",
800: "#343a40",
750: "#495057",
700: "#525c65",
600: "#90ee90",
500: "#ffa07a",
400: "#e0e0e0",
300: "#ff69b4",
200: "#da70d6",
100: "#ffffff",
},
background: {
primary: "#21252b",
secondary: "#343a40",
tertiary: "#495057",
},
text: {
primary: "#e0e0e0",
secondary: "#c0c0c0",
tertiary: "#a9a9a9",
accent: "#87cefa",
link: "#87cefa",
},
accent: {
primary: "#90ee90",
secondary: "#ffa07a",
danger: "#ff69b4",
confirm: "#90ee90",
},
};

View File

@@ -0,0 +1,32 @@
export default {
brand: {
900: "#1E1E2E",
800: "#302D41",
750: "#332E41",
700: "#575268",
600: "#6E6C7E",
500: "#988BA2",
400: "#C3BAC6",
300: "#D9E0EE",
200: "#F5E0DC",
100: "#FAE3B0",
},
background: {
primary: "#1E1E2E",
secondary: "#302D41",
tertiary: "#575268",
},
text: {
primary: "#D9E0EE",
secondary: "#C3BAC6",
tertiary: "#988BA2",
accent: "#F5E0DC",
link: "#96CDFB",
},
accent: {
primary: "#F5C2E7",
secondary: "#DDB6F2",
danger: "#F28FAD",
confirm: "#ABE9B3",
},
};

View File

@@ -0,0 +1,32 @@
export default {
brand: {
900: "#000000",
800: "#333333",
750: "#2B2B2B",
700: "#666666",
600: "#999999",
500: "#CCCCCC",
400: "#FFFFFF",
300: "#F0F0F0",
200: "#F8F9FA",
100: "#FFFFFF",
},
background: {
primary: "#000000",
secondary: "#222222",
tertiary: "#333333",
},
text: {
primary: "#F0F0F0",
secondary: "#CCCCCC",
tertiary: "#999999",
accent: "#FFFFFF",
link: "#0d9488",
},
accent: {
primary: "#FFFFFF",
secondary: "#c0c0c0",
danger: "#E53E3E",
confirm: "#00D26A",
},
};

View File

@@ -0,0 +1,40 @@
export default {
brand: {
colors: {
900: "#2C2E43",
800: "#3D4162",
750: "#4F5285",
700: "#6076AC",
600: "#7693D6",
500: "#8DAFF0",
400: "#A3C7FF",
300: "#B9E0FF",
200: "#CDF4FE",
100: "#E1FEFF",
},
},
background: {
primary: "linear-gradient(360deg, #15171C 100%, #353A47 100%)",
secondary: "#1B1F26",
tertiary: "#1E1E2E",
},
text: {
primary: "#f8f8f8",
secondary: "#3D4162",
tertiary: "#e5ebff",
accent: "#e6e6e6",
link: "aquamarine",
},
accent: {
primary: "#127c91",
secondary: "#39b4bf",
danger: "#E74C3C",
confirm: "#27AE60",
},
};

View File

@@ -0,0 +1,35 @@
export default {
brand: {
900: "#15171C",
800: "#1B1F26",
750: "#222731",
700: "#353A47",
600: "#535966",
500: "#747C88",
400: "#A0A4AC",
300: "#C6CBDC",
200: "#E6E9F0",
100: "#F3F4F8",
},
background: {
primary: "#15171C",
secondary: "#1B1F26",
tertiary: "#353A47",
},
text: {
primary: "#ffffff",
secondary: "#A0A4AC",
tertiary: "#747C88",
accent: "#E6E9F0",
link: "#96CDFB",
},
accent: {
primary: "#0095ff",
secondary: "#00acff",
danger: "#EA4D4D",
confirm: "#10CE8D",
},
};

View File

@@ -0,0 +1,50 @@
import { extendTheme } from "@chakra-ui/react";
import BaseTheme from "../base_theme";
import DarknightColors from "./Darknight";
import CapuchinColors from "./Capuchin";
import VsCodeColors from "./VsCode";
import OneDark from "./OneDark";
export function getColorThemes() {
return [
{ name: "darknight", colors: DarknightColors },
{ name: "onedark", colors: OneDark },
{ name: "capuchin", colors: CapuchinColors },
{ name: "vscode", colors: VsCodeColors },
];
}
const darknight = extendTheme({
...BaseTheme,
colors: DarknightColors,
});
const capuchin = extendTheme({
...BaseTheme,
colors: CapuchinColors,
});
const vsCode = extendTheme({
...BaseTheme,
colors: VsCodeColors,
});
const onedark = extendTheme({
...BaseTheme,
colors: OneDark,
});
export function getTheme(theme: string) {
switch (theme) {
case "onedark":
return onedark;
case "darknight":
return darknight;
case "capuchin":
return capuchin;
case "vscode":
return vsCode;
default:
return darknight;
}
}

33
src/layout/useMaxWidth.ts Normal file
View File

@@ -0,0 +1,33 @@
import { useState, useEffect } from "react";
import { useIsMobile } from "../components/contexts/MobileContext";
export const useMaxWidth = () => {
const isMobile = useIsMobile();
const [maxWidth, setMaxWidth] = useState("600px");
const calculateMaxWidth = () => {
if (isMobile) {
setMaxWidth("800px");
} else if (window.innerWidth < 1024) {
setMaxWidth("500px");
} else {
setMaxWidth("800px");
}
};
useEffect(() => {
calculateMaxWidth();
const handleResize = () => {
calculateMaxWidth();
};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, [isMobile]);
return maxWidth;
};

View File

@@ -0,0 +1,26 @@
import { useEffect, useState } from "react";
const usePageLoaded = (callback: () => void) => {
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
const handlePageLoad = () => {
setIsLoaded(true);
callback();
};
if (document.readyState === "complete") {
// Page is already fully loaded
handlePageLoad();
} else {
// Wait for the page to load
window.addEventListener("load", handlePageLoad);
}
return () => window.removeEventListener("load", handlePageLoad);
}, [callback]);
return isLoaded;
};
export default usePageLoaded;