mirror of
https://github.com/timmypidashev/web.git
synced 2026-04-14 11:03:50 +00:00
begin next js version
This commit is contained in:
37
.gitignore
vendored
37
.gitignore
vendored
@@ -1 +1,36 @@
|
||||
__pycache__/
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.yarn/install-state.gz
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
@@ -10,6 +10,6 @@ timmypidashev.localhost {
|
||||
}
|
||||
|
||||
handle @backend_routes {
|
||||
reverse_proxy landing:8000
|
||||
reverse_proxy web:8000
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.web
|
||||
__pycache__/*
|
||||
Dockerfile
|
||||
3
src/web/.eslintrc.json
Normal file
3
src/web/.eslintrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
40
src/web/.gitignore
vendored
40
src/web/.gitignore
vendored
@@ -1,4 +1,36 @@
|
||||
*.db
|
||||
*.py[cod]
|
||||
.web
|
||||
__pycache__/
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.yarn/install-state.gz
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
39
src/web/.web/.gitignore
vendored
39
src/web/.web/.gitignore
vendored
@@ -1,39 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
/_static
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# DS_Store
|
||||
.DS_Store
|
||||
Binary file not shown.
@@ -1,21 +0,0 @@
|
||||
import { useColorMode as chakraUseColorMode } from "@chakra-ui/react"
|
||||
import { useTheme } from "next-themes"
|
||||
import { useEffect } from "react"
|
||||
import { ColorModeContext } from "/utils/context.js"
|
||||
|
||||
export default function ChakraColorModeProvider({ children }) {
|
||||
const {colorMode, toggleColorMode} = chakraUseColorMode()
|
||||
const {theme, setTheme} = useTheme()
|
||||
|
||||
useEffect(() => {
|
||||
if (colorMode != theme) {
|
||||
toggleColorMode()
|
||||
}
|
||||
}, [theme])
|
||||
|
||||
return (
|
||||
<ColorModeContext.Provider value={[ colorMode, toggleColorMode ]}>
|
||||
{children}
|
||||
</ColorModeContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { useTheme } from "next-themes"
|
||||
import { useEffect, useState } from "react"
|
||||
import { ColorModeContext, defaultColorMode } from "/utils/context.js"
|
||||
|
||||
|
||||
export default function RadixThemesColorModeProvider({ children }) {
|
||||
const {theme, setTheme} = useTheme()
|
||||
const [colorMode, setColorMode] = useState(defaultColorMode)
|
||||
|
||||
useEffect(() => {
|
||||
setColorMode(theme)
|
||||
}, [theme])
|
||||
|
||||
const toggleColorMode = () => {
|
||||
setTheme(theme === "light" ? "dark" : "light")
|
||||
}
|
||||
return (
|
||||
<ColorModeContext.Provider value={[ colorMode, toggleColorMode ]}>
|
||||
{children}
|
||||
</ColorModeContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{"PING": "http://localhost:8000/ping", "EVENT": "ws://localhost:8000/_event", "UPLOAD": "http://localhost:8000/_upload"}
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true};
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "reflex",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"export": "next build",
|
||||
"export-sitemap": "next build && next-sitemap",
|
||||
"prod": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "11.11.1",
|
||||
"@radix-ui/themes": "^2.0.0",
|
||||
"axios": "1.6.0",
|
||||
"framer-motion": "^11.0.12",
|
||||
"json5": "2.2.3",
|
||||
"lucide-react": "0.314.0",
|
||||
"next": "14.0.1",
|
||||
"next-sitemap": "4.1.8",
|
||||
"next-themes": "0.2.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"silly": "^0.2.0",
|
||||
"socket.io-client": "4.6.1",
|
||||
"universal-cookie": "4.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "10.4.14",
|
||||
"postcss": "8.4.31",
|
||||
"tailwindcss": "3.3.2"
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
/** @jsxImportSource @emotion/react */
|
||||
|
||||
|
||||
import { Fragment, useContext } from "react"
|
||||
import { EventLoopContext, StateContexts } from "/utils/context"
|
||||
import { Event, getBackendURL, isTrue } from "/utils/state"
|
||||
import { WifiOffIcon as LucideWifiOffIcon } from "lucide-react"
|
||||
import { keyframes } from "@emotion/react"
|
||||
import { Box as RadixThemesBox, Dialog as RadixThemesDialog, Flex as RadixThemesFlex, Heading as RadixThemesHeading, Link as RadixThemesLink, Text as RadixThemesText } from "@radix-ui/themes"
|
||||
import env from "/env.json"
|
||||
import { motion } from "framer-motion"
|
||||
import NextLink from "next/link"
|
||||
import NextHead from "next/head"
|
||||
|
||||
|
||||
|
||||
const pulse = keyframes`
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
export function Fragment_14636cc997c0546c0967a25d8e600f96 () {
|
||||
const [addEvents, connectErrors] = useContext(EventLoopContext);
|
||||
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{isTrue(connectErrors.length >= 2) ? (
|
||||
<Fragment>
|
||||
<RadixThemesDialog.Root css={{"zIndex": 9999}} open={connectErrors.length >= 2}>
|
||||
<RadixThemesDialog.Content>
|
||||
<RadixThemesDialog.Title>
|
||||
{`Connection Error`}
|
||||
</RadixThemesDialog.Title>
|
||||
<RadixThemesText as={`p`} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#ebdbb2"}}>
|
||||
{`Cannot connect to server: `}
|
||||
{(connectErrors.length > 0) ? connectErrors[connectErrors.length - 1].message : ''}
|
||||
{`. Check if server is reachable at `}
|
||||
{getBackendURL(env.EVENT).href}
|
||||
</RadixThemesText>
|
||||
</RadixThemesDialog.Content>
|
||||
</RadixThemesDialog.Root>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment/>
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
import { useIsPresent } from "framer-motion";
|
||||
|
||||
|
||||
export function Fragment_e9a05c105aa9215aeba52aeec8fe2e76 () {
|
||||
const state = useContext(StateContexts.state)
|
||||
const [addEvents, connectErrors] = useContext(EventLoopContext);
|
||||
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{isTrue(((!state.is_hydrated) || (connectErrors.length > 0))) ? (
|
||||
<Fragment>
|
||||
<LucideWifiOffIcon css={{"color": "crimson", "zIndex": 9999, "position": "fixed", "bottom": "30px", "right": "30px", "animation": `${pulse} 1s infinite`}} size={32}>
|
||||
{`wifi_off`}
|
||||
</LucideWifiOffIcon>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment/>
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Component() {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Fragment>
|
||||
<div css={{"position": "fixed", "width": "100vw", "height": "0"}}>
|
||||
<Fragment_e9a05c105aa9215aeba52aeec8fe2e76/>
|
||||
</div>
|
||||
<Fragment_14636cc997c0546c0967a25d8e600f96/>
|
||||
</Fragment>
|
||||
<RadixThemesBox>
|
||||
<motion.div animate={{"opacity": 1, "y": 0, "transition": {"duration": 0.5, "ease": "easeInOut"}}} initial={{"opacity": 0, "y": -50}}>
|
||||
<RadixThemesBox>
|
||||
<RadixThemesBox>
|
||||
<RadixThemesFlex css={{"display": "flex", "alignItems": "center", "justifyContent": "center"}} gap={`7`}>
|
||||
<RadixThemesFlex>
|
||||
<RadixThemesLink asChild={true} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#000000", "textDecoration": "none", "&:hover": {"color": "#b8bb26"}}}>
|
||||
<NextLink href={`/about`} passHref={true}>
|
||||
<RadixThemesText as={`p`} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#ebdbb2"}}>
|
||||
{`About`}
|
||||
</RadixThemesText>
|
||||
</NextLink>
|
||||
</RadixThemesLink>
|
||||
</RadixThemesFlex>
|
||||
<RadixThemesFlex>
|
||||
<RadixThemesLink asChild={true} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#000000", "textDecoration": "none", "&:hover": {"color": "#b8bb26"}}}>
|
||||
<NextLink href={`/projects`} passHref={true}>
|
||||
<RadixThemesText as={`p`} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#ebdbb2"}}>
|
||||
{`Projects`}
|
||||
</RadixThemesText>
|
||||
</NextLink>
|
||||
</RadixThemesLink>
|
||||
</RadixThemesFlex>
|
||||
<RadixThemesFlex>
|
||||
<RadixThemesLink asChild={true} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#000000", "textDecoration": "none", "&:hover": {"color": "#b8bb26"}}}>
|
||||
<NextLink href={`/resume`} passHref={true}>
|
||||
<RadixThemesText as={`p`} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#ebdbb2"}}>
|
||||
{`Resume`}
|
||||
</RadixThemesText>
|
||||
</NextLink>
|
||||
</RadixThemesLink>
|
||||
</RadixThemesFlex>
|
||||
<RadixThemesFlex>
|
||||
<RadixThemesLink asChild={true} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#000000", "textDecoration": "none", "&:hover": {"color": "#b8bb26"}}}>
|
||||
<NextLink href={`/blog`} passHref={true}>
|
||||
<RadixThemesText as={`p`} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#ebdbb2"}}>
|
||||
{`Blog`}
|
||||
</RadixThemesText>
|
||||
</NextLink>
|
||||
</RadixThemesLink>
|
||||
</RadixThemesFlex>
|
||||
<RadixThemesFlex>
|
||||
<RadixThemesLink asChild={true} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#000000", "textDecoration": "none", "&:hover": {"color": "#b8bb26"}}}>
|
||||
<NextLink href={`shop`} passHref={true}>
|
||||
<RadixThemesText as={`p`} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#ebdbb2"}}>
|
||||
{`Shop`}
|
||||
</RadixThemesText>
|
||||
</NextLink>
|
||||
</RadixThemesLink>
|
||||
</RadixThemesFlex>
|
||||
</RadixThemesFlex>
|
||||
</RadixThemesBox>
|
||||
<RadixThemesBox>
|
||||
<RadixThemesFlex css={{"height": "100vh", "width": "100%", "display": "flex", "alignItems": "center", "justifyContent": "center"}}>
|
||||
<RadixThemesFlex align={`start`} direction={`column`} gap={`2`}>
|
||||
<motion.div animate={{"opacity": 1, "y": 0, "transition": {"duration": 0.5, "ease": "easeInOut"}}} initial={{"opacity": 0, "y": -50}} whileHover={{"scale": 1.1}}>
|
||||
<RadixThemesHeading css={{"fontFamily": "ComicCode", "fontSize": 32, "color": "#ebdbb2"}} size={`9`}>
|
||||
{`Whoops, this page doesn't exist...`}
|
||||
</RadixThemesHeading>
|
||||
</motion.div>
|
||||
</RadixThemesFlex>
|
||||
</RadixThemesFlex>
|
||||
</RadixThemesBox>
|
||||
<RadixThemesBox css={{"borderTop": "2px solid #ebdbb2;"}}>
|
||||
<RadixThemesFlex css={{"height": "15vh", "display": "flex", "alignItems": "center", "justifyContent": "center"}}>
|
||||
<RadixThemesFlex align={`center`} direction={`column`} gap={`7`}>
|
||||
<RadixThemesHeading css={{"fontFamily": "ComicCode", "fontSize": 32, "color": "#ebdbb2"}} size={`9`}>
|
||||
{`Footer`}
|
||||
</RadixThemesHeading>
|
||||
</RadixThemesFlex>
|
||||
</RadixThemesFlex>
|
||||
</RadixThemesBox>
|
||||
</RadixThemesBox>
|
||||
</motion.div>
|
||||
</RadixThemesBox>
|
||||
<NextHead>
|
||||
<title>
|
||||
{`Page Not Found`}
|
||||
</title>
|
||||
<meta content={`A Reflex app.`} name={`description`}/>
|
||||
<meta content={`favicon.ico`} property={`og:image`}/>
|
||||
</NextHead>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/** @jsxImportSource @emotion/react */
|
||||
|
||||
import '/styles/styles.css'
|
||||
|
||||
import RadixThemesColorModeProvider from "/components/reflex/radix_themes_color_mode_provider.js"
|
||||
import { Theme as RadixThemesTheme } from "@radix-ui/themes"
|
||||
import "@radix-ui/themes/styles.css"
|
||||
import theme from "/utils/theme.js"
|
||||
import { Fragment } from "react"
|
||||
|
||||
|
||||
import { EventLoopProvider, StateProvider, defaultColorMode } from "/utils/context.js";
|
||||
import { ThemeProvider } from 'next-themes'
|
||||
|
||||
|
||||
|
||||
function AppWrap({children}) {
|
||||
|
||||
|
||||
return (
|
||||
<RadixThemesColorModeProvider>
|
||||
<RadixThemesTheme accentColor={`blue`} css={{...theme.styles.global[':root'], ...theme.styles.global.body}}>
|
||||
<Fragment>
|
||||
{children}
|
||||
</Fragment>
|
||||
</RadixThemesTheme>
|
||||
</RadixThemesColorModeProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default function MyApp({ Component, pageProps }) {
|
||||
return (
|
||||
<ThemeProvider defaultTheme={ defaultColorMode } storageKey="chakra-ui-color-mode" attribute="class">
|
||||
<AppWrap>
|
||||
<StateProvider>
|
||||
<EventLoopProvider>
|
||||
<Component {...pageProps} />
|
||||
</EventLoopProvider>
|
||||
</StateProvider>
|
||||
</AppWrap>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/** @jsxImportSource @emotion/react */
|
||||
|
||||
|
||||
import { Head, Html, Main, NextScript } from "next/document"
|
||||
|
||||
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html>
|
||||
<Head/>
|
||||
<body>
|
||||
<Main/>
|
||||
<NextScript/>
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/** @jsxImportSource @emotion/react */
|
||||
|
||||
|
||||
import { Fragment, useContext } from "react"
|
||||
import { EventLoopContext, StateContexts } from "/utils/context"
|
||||
import { Event, getBackendURL, isTrue } from "/utils/state"
|
||||
import { WifiOffIcon as LucideWifiOffIcon } from "lucide-react"
|
||||
import { keyframes } from "@emotion/react"
|
||||
import { Box as RadixThemesBox, Dialog as RadixThemesDialog, Flex as RadixThemesFlex, Heading as RadixThemesHeading, Link as RadixThemesLink, Text as RadixThemesText } from "@radix-ui/themes"
|
||||
import env from "/env.json"
|
||||
import { motion } from "framer-motion"
|
||||
import NextLink from "next/link"
|
||||
import NextHead from "next/head"
|
||||
|
||||
|
||||
|
||||
const pulse = keyframes`
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
export function Fragment_14636cc997c0546c0967a25d8e600f96 () {
|
||||
const [addEvents, connectErrors] = useContext(EventLoopContext);
|
||||
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{isTrue(connectErrors.length >= 2) ? (
|
||||
<Fragment>
|
||||
<RadixThemesDialog.Root css={{"zIndex": 9999}} open={connectErrors.length >= 2}>
|
||||
<RadixThemesDialog.Content>
|
||||
<RadixThemesDialog.Title>
|
||||
{`Connection Error`}
|
||||
</RadixThemesDialog.Title>
|
||||
<RadixThemesText as={`p`} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#ebdbb2"}}>
|
||||
{`Cannot connect to server: `}
|
||||
{(connectErrors.length > 0) ? connectErrors[connectErrors.length - 1].message : ''}
|
||||
{`. Check if server is reachable at `}
|
||||
{getBackendURL(env.EVENT).href}
|
||||
</RadixThemesText>
|
||||
</RadixThemesDialog.Content>
|
||||
</RadixThemesDialog.Root>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment/>
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
import { useIsPresent } from "framer-motion";
|
||||
|
||||
|
||||
export function Fragment_e9a05c105aa9215aeba52aeec8fe2e76 () {
|
||||
const state = useContext(StateContexts.state)
|
||||
const [addEvents, connectErrors] = useContext(EventLoopContext);
|
||||
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{isTrue(((!state.is_hydrated) || (connectErrors.length > 0))) ? (
|
||||
<Fragment>
|
||||
<LucideWifiOffIcon css={{"color": "crimson", "zIndex": 9999, "position": "fixed", "bottom": "30px", "right": "30px", "animation": `${pulse} 1s infinite`}} size={32}>
|
||||
{`wifi_off`}
|
||||
</LucideWifiOffIcon>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment/>
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Component() {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Fragment>
|
||||
<div css={{"position": "fixed", "width": "100vw", "height": "0"}}>
|
||||
<Fragment_e9a05c105aa9215aeba52aeec8fe2e76/>
|
||||
</div>
|
||||
<Fragment_14636cc997c0546c0967a25d8e600f96/>
|
||||
</Fragment>
|
||||
<RadixThemesBox>
|
||||
<motion.div animate={{"opacity": 1, "y": 0, "transition": {"duration": 0.5, "ease": "easeInOut"}}} initial={{"opacity": 0, "y": -50}}>
|
||||
<RadixThemesBox>
|
||||
<RadixThemesBox>
|
||||
<RadixThemesFlex css={{"display": "flex", "alignItems": "center", "justifyContent": "center"}} gap={`7`}>
|
||||
<RadixThemesFlex>
|
||||
<RadixThemesLink asChild={true} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#000000", "textDecoration": "none", "&:hover": {"color": "#b8bb26"}}}>
|
||||
<NextLink href={`/about`} passHref={true}>
|
||||
<RadixThemesText as={`p`} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#ebdbb2"}}>
|
||||
{`About`}
|
||||
</RadixThemesText>
|
||||
</NextLink>
|
||||
</RadixThemesLink>
|
||||
</RadixThemesFlex>
|
||||
<RadixThemesFlex>
|
||||
<RadixThemesLink asChild={true} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#000000", "textDecoration": "none", "&:hover": {"color": "#b8bb26"}}}>
|
||||
<NextLink href={`/projects`} passHref={true}>
|
||||
<RadixThemesText as={`p`} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#ebdbb2"}}>
|
||||
{`Projects`}
|
||||
</RadixThemesText>
|
||||
</NextLink>
|
||||
</RadixThemesLink>
|
||||
</RadixThemesFlex>
|
||||
<RadixThemesFlex>
|
||||
<RadixThemesLink asChild={true} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#000000", "textDecoration": "none", "&:hover": {"color": "#b8bb26"}}}>
|
||||
<NextLink href={`/resume`} passHref={true}>
|
||||
<RadixThemesText as={`p`} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#ebdbb2"}}>
|
||||
{`Resume`}
|
||||
</RadixThemesText>
|
||||
</NextLink>
|
||||
</RadixThemesLink>
|
||||
</RadixThemesFlex>
|
||||
<RadixThemesFlex>
|
||||
<RadixThemesLink asChild={true} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#000000", "textDecoration": "none", "&:hover": {"color": "#b8bb26"}}}>
|
||||
<NextLink href={`/blog`} passHref={true}>
|
||||
<RadixThemesText as={`p`} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#ebdbb2"}}>
|
||||
{`Blog`}
|
||||
</RadixThemesText>
|
||||
</NextLink>
|
||||
</RadixThemesLink>
|
||||
</RadixThemesFlex>
|
||||
<RadixThemesFlex>
|
||||
<RadixThemesLink asChild={true} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#000000", "textDecoration": "none", "&:hover": {"color": "#b8bb26"}}}>
|
||||
<NextLink href={`shop`} passHref={true}>
|
||||
<RadixThemesText as={`p`} css={{"fontFamily": "ComicCode", "fontSize": 24, "color": "#ebdbb2"}}>
|
||||
{`Shop`}
|
||||
</RadixThemesText>
|
||||
</NextLink>
|
||||
</RadixThemesLink>
|
||||
</RadixThemesFlex>
|
||||
</RadixThemesFlex>
|
||||
</RadixThemesBox>
|
||||
<RadixThemesBox>
|
||||
<RadixThemesFlex css={{"height": "100vh", "display": "flex", "alignItems": "center", "justifyContent": "center"}}>
|
||||
<RadixThemesFlex align={`center`} direction={`column`} gap={`7`}>
|
||||
<RadixThemesHeading css={{"fontFamily": "ComicCode", "fontSize": 32, "color": "#ebdbb2"}} size={`9`}>
|
||||
{`Index`}
|
||||
</RadixThemesHeading>
|
||||
</RadixThemesFlex>
|
||||
</RadixThemesFlex>
|
||||
</RadixThemesBox>
|
||||
<RadixThemesBox css={{"borderTop": "2px solid #ebdbb2;"}}>
|
||||
<RadixThemesFlex css={{"height": "15vh", "display": "flex", "alignItems": "center", "justifyContent": "center"}}>
|
||||
<RadixThemesFlex align={`center`} direction={`column`} gap={`7`}>
|
||||
<RadixThemesHeading css={{"fontFamily": "ComicCode", "fontSize": 32, "color": "#ebdbb2"}} size={`9`}>
|
||||
{`Footer`}
|
||||
</RadixThemesHeading>
|
||||
</RadixThemesFlex>
|
||||
</RadixThemesFlex>
|
||||
</RadixThemesBox>
|
||||
</RadixThemesBox>
|
||||
</motion.div>
|
||||
</RadixThemesBox>
|
||||
<NextHead>
|
||||
<title>
|
||||
{`Timothy Pidashev`}
|
||||
</title>
|
||||
<meta content={`A Reflex app.`} name={`description`}/>
|
||||
<meta content={`favicon.ico`} property={`og:image`}/>
|
||||
</NextHead>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/* Hide scrollbar for all elements */
|
||||
::-webkit-scrollbar {
|
||||
display: none; /* Safari and Chrome */
|
||||
}
|
||||
|
||||
/* Hide scrollbar for Firefox */
|
||||
html {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 407 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,11 +0,0 @@
|
||||
@font-face {
|
||||
font-family: ComicCode;
|
||||
src: url("ComicCode-Regular.otf") format("opentype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ComicCodeBold;
|
||||
font-weight: bold;
|
||||
src: url("ComicCode-Bold.otf") format("opentype");
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
['@radix-ui/themes@^2.0.0', 'framer-motion', 'lucide-react@0.314.0'],{"app_name": "web", "loglevel": "info", "frontend_port": 3000, "frontend_path": "", "backend_port": 8000, "api_url": "http://localhost:8000", "deploy_url": "http://localhost:3000", "backend_host": "0.0.0.0", "db_url": "sqlite:///reflex.db", "redis_url": null, "telemetry_enabled": true, "bun_path": "/home/timmy/.local/share/reflex/bun/bin/bun", "cors_allowed_origins": ["*"], "tailwind": {"content": ["./pages/**/*.{js,ts,jsx,tsx}", "./utils/**/*.{js,ts,jsx,tsx}"]}, "timeout": 120, "next_compression": true, "event_namespace": null, "frontend_packages": [], "cp_backend_url": "https://rxcp-prod-control-plane.fly.dev", "cp_web_url": "https://control-plane.reflex.run", "gunicorn_worker_class": "uvicorn.workers.UvicornH11Worker"}
|
||||
@@ -1 +0,0 @@
|
||||
{"version": "0.4.3", "project_hash": 76742530442461448999350655539571039381, "last_version_check_datetime": "2024-03-12 10:19:00.533460"}
|
||||
@@ -1,3 +0,0 @@
|
||||
@import url('./tailwind.css');
|
||||
@import url('@/fonts/fonts.css');
|
||||
@import url('@/css/scrollbar.css');
|
||||
@@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./pages/**/*.{js,ts,jsx,tsx}", "./utils/**/*.{js,ts,jsx,tsx}"],
|
||||
theme: null,
|
||||
plugins: [
|
||||
],
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
/**
|
||||
* React hook for use in /404 page to enable client-side routing.
|
||||
*
|
||||
* Uses the next/router to redirect to the provided URL when loading
|
||||
* the 404 page (for example as a fallback in static hosting situations).
|
||||
*
|
||||
* @returns {boolean} routeNotFound - true if the current route is an actual 404
|
||||
*/
|
||||
export const useClientSideRouting = () => {
|
||||
const [routeNotFound, setRouteNotFound] = useState(false)
|
||||
const didRedirect = useRef(false)
|
||||
const router = useRouter()
|
||||
useEffect(() => {
|
||||
if (
|
||||
router.isReady &&
|
||||
!didRedirect.current // have not tried redirecting yet
|
||||
) {
|
||||
didRedirect.current = true // never redirect twice to avoid "Hard Navigate" error
|
||||
// attempt to redirect to the route in the browser address bar once
|
||||
router.replace({
|
||||
pathname: window.location.pathname,
|
||||
query: window.location.search.slice(1),
|
||||
})
|
||||
.catch((e) => {
|
||||
setRouteNotFound(true) // navigation failed, so this is a real 404
|
||||
})
|
||||
}
|
||||
}, [router.isReady]);
|
||||
|
||||
// Return the reactive bool, to avoid flashing 404 page until we know for sure
|
||||
// the route is not found.
|
||||
return routeNotFound
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/** @jsxImportSource @emotion/react */
|
||||
|
||||
|
||||
import { memo } from "react"
|
||||
import { E, isTrue } from "/utils/state"
|
||||
|
||||
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import { createContext, useContext, useMemo, useReducer, useState } from "react"
|
||||
import { applyDelta, Event, hydrateClientStorage, useEventLoop, refs } from "/utils/state.js"
|
||||
|
||||
export const initialState = {"state": {"is_hydrated": false, "router": {"session": {"client_token": "", "client_ip": "", "session_id": ""}, "headers": {"host": "", "origin": "", "upgrade": "", "connection": "", "pragma": "", "cache_control": "", "user_agent": "", "sec_websocket_version": "", "sec_websocket_key": "", "sec_websocket_extensions": "", "accept_encoding": "", "accept_language": ""}, "page": {"host": "", "path": "", "raw_path": "", "full_path": "", "full_raw_path": "", "params": {}}}}, "state.state": {}, "state.state.theme_state": {"current_theme": 0, "theme": {"background_color": "#282828"}, "themes": {"0": {"background_color": "#282828"}, "1": {"background_color": "#000000"}}}, "state.update_vars_internal_state": {}, "state.on_load_internal_state": {}}
|
||||
|
||||
export const defaultColorMode = "light"
|
||||
export const ColorModeContext = createContext(null);
|
||||
export const UploadFilesContext = createContext(null);
|
||||
export const DispatchContext = createContext(null);
|
||||
export const StateContexts = {
|
||||
state: createContext(null),
|
||||
state__state: createContext(null),
|
||||
state__state__theme_state: createContext(null),
|
||||
state__update_vars_internal_state: createContext(null),
|
||||
state__on_load_internal_state: createContext(null),
|
||||
}
|
||||
export const EventLoopContext = createContext(null);
|
||||
export const clientStorage = {"cookies": {}, "local_storage": {}}
|
||||
|
||||
export const state_name = "state"
|
||||
|
||||
// Theses events are triggered on initial load and each page navigation.
|
||||
export const onLoadInternalEvent = () => {
|
||||
const internal_events = [];
|
||||
|
||||
// Get tracked cookie and local storage vars to send to the backend.
|
||||
const client_storage_vars = hydrateClientStorage(clientStorage);
|
||||
// But only send the vars if any are actually set in the browser.
|
||||
if (client_storage_vars && Object.keys(client_storage_vars).length !== 0) {
|
||||
internal_events.push(
|
||||
Event(
|
||||
'state.update_vars_internal_state.update_vars_internal',
|
||||
{vars: client_storage_vars},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// `on_load_internal` triggers the correct on_load event(s) for the current page.
|
||||
// If the page does not define any on_load event, this will just set `is_hydrated = true`.
|
||||
internal_events.push(Event('state.on_load_internal_state.on_load_internal'));
|
||||
|
||||
return internal_events;
|
||||
}
|
||||
|
||||
// The following events are sent when the websocket connects or reconnects.
|
||||
export const initialEvents = () => [
|
||||
Event('state.hydrate'),
|
||||
...onLoadInternalEvent()
|
||||
]
|
||||
|
||||
export const isDevMode = true
|
||||
|
||||
export function UploadFilesProvider({ children }) {
|
||||
const [filesById, setFilesById] = useState({})
|
||||
refs["__clear_selected_files"] = (id) => setFilesById(filesById => {
|
||||
const newFilesById = {...filesById}
|
||||
delete newFilesById[id]
|
||||
return newFilesById
|
||||
})
|
||||
return (
|
||||
<UploadFilesContext.Provider value={[filesById, setFilesById]}>
|
||||
{children}
|
||||
</UploadFilesContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function EventLoopProvider({ children }) {
|
||||
const dispatch = useContext(DispatchContext)
|
||||
const [addEvents, connectErrors] = useEventLoop(
|
||||
dispatch,
|
||||
initialEvents,
|
||||
clientStorage,
|
||||
)
|
||||
return (
|
||||
<EventLoopContext.Provider value={[addEvents, connectErrors]}>
|
||||
{children}
|
||||
</EventLoopContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function StateProvider({ children }) {
|
||||
const [state, dispatch_state] = useReducer(applyDelta, initialState["state"])
|
||||
const [state__state, dispatch_state__state] = useReducer(applyDelta, initialState["state.state"])
|
||||
const [state__state__theme_state, dispatch_state__state__theme_state] = useReducer(applyDelta, initialState["state.state.theme_state"])
|
||||
const [state__update_vars_internal_state, dispatch_state__update_vars_internal_state] = useReducer(applyDelta, initialState["state.update_vars_internal_state"])
|
||||
const [state__on_load_internal_state, dispatch_state__on_load_internal_state] = useReducer(applyDelta, initialState["state.on_load_internal_state"])
|
||||
const dispatchers = useMemo(() => {
|
||||
return {
|
||||
"state": dispatch_state,
|
||||
"state.state": dispatch_state__state,
|
||||
"state.state.theme_state": dispatch_state__state__theme_state,
|
||||
"state.update_vars_internal_state": dispatch_state__update_vars_internal_state,
|
||||
"state.on_load_internal_state": dispatch_state__on_load_internal_state,
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<StateContexts.state.Provider value={ state }>
|
||||
<StateContexts.state__state.Provider value={ state__state }>
|
||||
<StateContexts.state__state__theme_state.Provider value={ state__state__theme_state }>
|
||||
<StateContexts.state__update_vars_internal_state.Provider value={ state__update_vars_internal_state }>
|
||||
<StateContexts.state__on_load_internal_state.Provider value={ state__on_load_internal_state }>
|
||||
<DispatchContext.Provider value={dispatchers}>
|
||||
{children}
|
||||
</DispatchContext.Provider>
|
||||
</StateContexts.state__on_load_internal_state.Provider>
|
||||
</StateContexts.state__update_vars_internal_state.Provider>
|
||||
</StateContexts.state__state__theme_state.Provider>
|
||||
</StateContexts.state__state.Provider>
|
||||
</StateContexts.state.Provider>
|
||||
)
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import { GridCellKind } from "@glideapps/glide-data-grid";
|
||||
|
||||
export function getDEColumn(columns, col) {
|
||||
let c = columns[col];
|
||||
c.pos = col;
|
||||
return c;
|
||||
}
|
||||
|
||||
export function getDERow(data, row) {
|
||||
return data[row];
|
||||
}
|
||||
|
||||
export function locateCell(row, column) {
|
||||
if (Array.isArray(row)) {
|
||||
return row[column.pos];
|
||||
} else {
|
||||
return row[column.id];
|
||||
}
|
||||
}
|
||||
|
||||
export function formatCell(value, column) {
|
||||
const editable = column.editable ?? true;
|
||||
switch (column.type) {
|
||||
case "int":
|
||||
case "float":
|
||||
return {
|
||||
kind: GridCellKind.Number,
|
||||
data: value,
|
||||
displayData: value + "",
|
||||
readonly: !editable,
|
||||
allowOverlay: editable,
|
||||
};
|
||||
case "datetime":
|
||||
// value = moment format?
|
||||
case "str":
|
||||
return {
|
||||
kind: GridCellKind.Text,
|
||||
data: value,
|
||||
displayData: value,
|
||||
readonly: !editable,
|
||||
allowOverlay: editable,
|
||||
};
|
||||
case "bool":
|
||||
return {
|
||||
kind: GridCellKind.Boolean,
|
||||
data: value,
|
||||
readonly: !editable,
|
||||
};
|
||||
default:
|
||||
console.log(
|
||||
"Warning: column.type is undefined for column.title=" + column.title
|
||||
);
|
||||
return {
|
||||
kind: GridCellKind.Text,
|
||||
data: value,
|
||||
displayData: column.type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function formatDataEditorCells(col, row, columns, data) {
|
||||
if (row < data.length && col < columns.length) {
|
||||
const column = getDEColumn(columns, col);
|
||||
const rowData = getDERow(data, row);
|
||||
const cellData = locateCell(rowData, column);
|
||||
return formatCell(cellData, column);
|
||||
}
|
||||
return { kind: GridCellKind.Loading };
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Simulate the python range() builtin function.
|
||||
* inspired by https://dev.to/guyariely/using-python-range-in-javascript-337p
|
||||
*
|
||||
* If needed outside of an iterator context, use `Array.from(range(10))` or
|
||||
* spread syntax `[...range(10)]` to get an array.
|
||||
*
|
||||
* @param {number} start: the start or end of the range.
|
||||
* @param {number} stop: the end of the range.
|
||||
* @param {number} step: the step of the range.
|
||||
* @returns {object} an object with a Symbol.iterator method over the range
|
||||
*/
|
||||
export default function range(start, stop, step) {
|
||||
return {
|
||||
[Symbol.iterator]() {
|
||||
if (stop === undefined) {
|
||||
stop = start;
|
||||
start = 0;
|
||||
}
|
||||
if (step === undefined) {
|
||||
step = 1;
|
||||
}
|
||||
|
||||
let i = start - step;
|
||||
|
||||
return {
|
||||
next() {
|
||||
i += step;
|
||||
if ((step > 0 && i < stop) || (step < 0 && i > stop)) {
|
||||
return {
|
||||
value: i,
|
||||
done: false,
|
||||
};
|
||||
}
|
||||
return {
|
||||
value: undefined,
|
||||
done: true,
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,728 +0,0 @@
|
||||
// State management for Reflex web apps.
|
||||
import axios from "axios";
|
||||
import io from "socket.io-client";
|
||||
import JSON5 from "json5";
|
||||
import env from "/env.json";
|
||||
import Cookies from "universal-cookie";
|
||||
import { useEffect, useReducer, useRef, useState } from "react";
|
||||
import Router, { useRouter } from "next/router";
|
||||
import {
|
||||
initialEvents,
|
||||
initialState,
|
||||
onLoadInternalEvent,
|
||||
state_name,
|
||||
} from "utils/context.js";
|
||||
|
||||
// Endpoint URLs.
|
||||
const EVENTURL = env.EVENT;
|
||||
const UPLOADURL = env.UPLOAD;
|
||||
|
||||
// These hostnames indicate that the backend and frontend are reachable via the same domain.
|
||||
const SAME_DOMAIN_HOSTNAMES = ["localhost", "0.0.0.0", "::", "0:0:0:0:0:0:0:0"];
|
||||
|
||||
// Global variable to hold the token.
|
||||
let token;
|
||||
|
||||
// Key for the token in the session storage.
|
||||
const TOKEN_KEY = "token";
|
||||
|
||||
// create cookie instance
|
||||
const cookies = new Cookies();
|
||||
|
||||
// Dictionary holding component references.
|
||||
export const refs = {};
|
||||
|
||||
// Flag ensures that only one event is processing on the backend concurrently.
|
||||
let event_processing = false;
|
||||
// Array holding pending events to be processed.
|
||||
const event_queue = [];
|
||||
|
||||
// Pending upload promises, by id
|
||||
const upload_controllers = {};
|
||||
|
||||
/**
|
||||
* Generate a UUID (Used for session tokens).
|
||||
* Taken from: https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid
|
||||
* @returns A UUID.
|
||||
*/
|
||||
export const generateUUID = () => {
|
||||
let d = new Date().getTime(),
|
||||
d2 = (performance && performance.now && performance.now() * 1000) || 0;
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
||||
let r = Math.random() * 16;
|
||||
if (d > 0) {
|
||||
r = (d + r) % 16 | 0;
|
||||
d = Math.floor(d / 16);
|
||||
} else {
|
||||
r = (d2 + r) % 16 | 0;
|
||||
d2 = Math.floor(d2 / 16);
|
||||
}
|
||||
return (c == "x" ? r : (r & 0x7) | 0x8).toString(16);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the token for the current session.
|
||||
* @returns The token.
|
||||
*/
|
||||
export const getToken = () => {
|
||||
if (token) {
|
||||
return token;
|
||||
}
|
||||
if (typeof window !== "undefined") {
|
||||
if (!window.sessionStorage.getItem(TOKEN_KEY)) {
|
||||
window.sessionStorage.setItem(TOKEN_KEY, generateUUID());
|
||||
}
|
||||
token = window.sessionStorage.getItem(TOKEN_KEY);
|
||||
}
|
||||
return token;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the URL for the backend server
|
||||
* @param url_str The URL string to parse.
|
||||
* @returns The given URL modified to point to the actual backend server.
|
||||
*/
|
||||
export const getBackendURL = (url_str) => {
|
||||
// Get backend URL object from the endpoint.
|
||||
const endpoint = new URL(url_str);
|
||||
if (
|
||||
typeof window !== "undefined" &&
|
||||
SAME_DOMAIN_HOSTNAMES.includes(endpoint.hostname)
|
||||
) {
|
||||
// Use the frontend domain to access the backend
|
||||
const frontend_hostname = window.location.hostname;
|
||||
endpoint.hostname = frontend_hostname;
|
||||
if (window.location.protocol === "https:") {
|
||||
if (endpoint.protocol === "ws:") {
|
||||
endpoint.protocol = "wss:";
|
||||
} else if (endpoint.protocol === "http:") {
|
||||
endpoint.protocol = "https:";
|
||||
}
|
||||
endpoint.port = ""; // Assume websocket is on https port via load balancer.
|
||||
}
|
||||
}
|
||||
return endpoint;
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply a delta to the state.
|
||||
* @param state The state to apply the delta to.
|
||||
* @param delta The delta to apply.
|
||||
*/
|
||||
export const applyDelta = (state, delta) => {
|
||||
return { ...state, ...delta };
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle frontend event or send the event to the backend via Websocket.
|
||||
* @param event The event to send.
|
||||
* @param socket The socket object to send the event on.
|
||||
*
|
||||
* @returns True if the event was sent, false if it was handled locally.
|
||||
*/
|
||||
export const applyEvent = async (event, socket) => {
|
||||
// Handle special events
|
||||
if (event.name == "_redirect") {
|
||||
if (event.payload.external) window.open(event.payload.path, "_blank");
|
||||
else Router.push(event.payload.path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.name == "_console") {
|
||||
console.log(event.payload.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.name == "_remove_cookie") {
|
||||
cookies.remove(event.payload.key, { ...event.payload.options });
|
||||
queueEvents(initialEvents(), socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.name == "_clear_local_storage") {
|
||||
localStorage.clear();
|
||||
queueEvents(initialEvents(), socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.name == "_remove_local_storage") {
|
||||
localStorage.removeItem(event.payload.key);
|
||||
queueEvents(initialEvents(), socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.name == "_set_clipboard") {
|
||||
const content = event.payload.content;
|
||||
navigator.clipboard.writeText(content);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.name == "_download") {
|
||||
const a = document.createElement("a");
|
||||
a.hidden = true;
|
||||
a.href = event.payload.url;
|
||||
a.download = event.payload.filename;
|
||||
a.click();
|
||||
a.remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.name == "_alert") {
|
||||
alert(event.payload.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.name == "_set_focus") {
|
||||
const ref =
|
||||
event.payload.ref in refs ? refs[event.payload.ref] : event.payload.ref;
|
||||
ref.current.focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.name == "_set_value") {
|
||||
const ref =
|
||||
event.payload.ref in refs ? refs[event.payload.ref] : event.payload.ref;
|
||||
if (ref.current) {
|
||||
ref.current.value = event.payload.value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.name == "_call_script") {
|
||||
try {
|
||||
const eval_result = eval(event.payload.javascript_code);
|
||||
if (event.payload.callback) {
|
||||
if (!!eval_result && typeof eval_result.then === "function") {
|
||||
eval(event.payload.callback)(await eval_result);
|
||||
} else {
|
||||
eval(event.payload.callback)(eval_result);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("_call_script", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update token and router data (if missing).
|
||||
event.token = getToken();
|
||||
if (
|
||||
event.router_data === undefined ||
|
||||
Object.keys(event.router_data).length === 0
|
||||
) {
|
||||
event.router_data = (({ pathname, query, asPath }) => ({
|
||||
pathname,
|
||||
query,
|
||||
asPath,
|
||||
}))(Router);
|
||||
}
|
||||
|
||||
// Send the event to the server.
|
||||
if (socket) {
|
||||
socket.emit(
|
||||
"event",
|
||||
JSON.stringify(event, (k, v) => (v === undefined ? null : v))
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Send an event to the server via REST.
|
||||
* @param event The current event.
|
||||
* @param socket The socket object to send the response event(s) on.
|
||||
*
|
||||
* @returns Whether the event was sent.
|
||||
*/
|
||||
export const applyRestEvent = async (event, socket) => {
|
||||
let eventSent = false;
|
||||
if (event.handler == "uploadFiles") {
|
||||
// Start upload, but do not wait for it, which would block other events.
|
||||
uploadFiles(
|
||||
event.name,
|
||||
event.payload.files,
|
||||
event.payload.upload_id,
|
||||
event.payload.on_upload_progress,
|
||||
socket
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return eventSent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Queue events to be processed and trigger processing of queue.
|
||||
* @param events Array of events to queue.
|
||||
* @param socket The socket object to send the event on.
|
||||
*/
|
||||
export const queueEvents = async (events, socket) => {
|
||||
event_queue.push(...events);
|
||||
await processEvent(socket.current);
|
||||
};
|
||||
|
||||
/**
|
||||
* Process an event off the event queue.
|
||||
* @param socket The socket object to send the event on.
|
||||
*/
|
||||
export const processEvent = async (socket) => {
|
||||
// Only proceed if the socket is up, otherwise we throw the event into the void
|
||||
if (!socket) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only proceed if we're not already processing an event.
|
||||
if (event_queue.length === 0 || event_processing) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set processing to true to block other events from being processed.
|
||||
event_processing = true;
|
||||
|
||||
// Apply the next event in the queue.
|
||||
const event = event_queue.shift();
|
||||
|
||||
let eventSent = false;
|
||||
// Process events with handlers via REST and all others via websockets.
|
||||
if (event.handler) {
|
||||
eventSent = await applyRestEvent(event, socket);
|
||||
} else {
|
||||
eventSent = await applyEvent(event, socket);
|
||||
}
|
||||
// If no event was sent, set processing to false.
|
||||
if (!eventSent) {
|
||||
event_processing = false;
|
||||
// recursively call processEvent to drain the queue, since there is
|
||||
// no state update to trigger the useEffect event loop.
|
||||
await processEvent(socket);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect to a websocket and set the handlers.
|
||||
* @param socket The socket object to connect.
|
||||
* @param dispatch The function to queue state update
|
||||
* @param transports The transports to use.
|
||||
* @param setConnectErrors The function to update connection error value.
|
||||
* @param client_storage The client storage object from context.js
|
||||
*/
|
||||
export const connect = async (
|
||||
socket,
|
||||
dispatch,
|
||||
transports,
|
||||
setConnectErrors,
|
||||
client_storage = {}
|
||||
) => {
|
||||
// Get backend URL object from the endpoint.
|
||||
const endpoint = getBackendURL(EVENTURL);
|
||||
|
||||
// Create the socket.
|
||||
socket.current = io(endpoint.href, {
|
||||
path: endpoint["pathname"],
|
||||
transports: transports,
|
||||
autoUnref: false,
|
||||
});
|
||||
|
||||
function checkVisibility() {
|
||||
if (document.visibilityState === "visible") {
|
||||
if (!socket.current.connected) {
|
||||
console.log("Socket is disconnected, attempting to reconnect ");
|
||||
socket.current.connect();
|
||||
} else {
|
||||
console.log("Socket is reconnected ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Once the socket is open, hydrate the page.
|
||||
socket.current.on("connect", () => {
|
||||
setConnectErrors([]);
|
||||
});
|
||||
|
||||
socket.current.on("connect_error", (error) => {
|
||||
setConnectErrors((connectErrors) => [connectErrors.slice(-9), error]);
|
||||
});
|
||||
// On each received message, queue the updates and events.
|
||||
socket.current.on("event", (message) => {
|
||||
const update = JSON5.parse(message);
|
||||
for (const substate in update.delta) {
|
||||
dispatch[substate](update.delta[substate]);
|
||||
}
|
||||
applyClientStorageDelta(client_storage, update.delta);
|
||||
event_processing = !update.final;
|
||||
if (update.events) {
|
||||
queueEvents(update.events, socket);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("visibilitychange", checkVisibility);
|
||||
};
|
||||
|
||||
/**
|
||||
* Upload files to the server.
|
||||
*
|
||||
* @param state The state to apply the delta to.
|
||||
* @param handler The handler to use.
|
||||
* @param upload_id The upload id to use.
|
||||
* @param on_upload_progress The function to call on upload progress.
|
||||
* @param socket the websocket connection
|
||||
*
|
||||
* @returns The response from posting to the UPLOADURL endpoint.
|
||||
*/
|
||||
export const uploadFiles = async (
|
||||
handler,
|
||||
files,
|
||||
upload_id,
|
||||
on_upload_progress,
|
||||
socket
|
||||
) => {
|
||||
// return if there's no file to upload
|
||||
if (files === undefined || files.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (upload_controllers[upload_id]) {
|
||||
console.log("Upload already in progress for ", upload_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
let resp_idx = 0;
|
||||
const eventHandler = (progressEvent) => {
|
||||
// handle any delta / event streamed from the upload event handler
|
||||
const chunks = progressEvent.event.target.responseText.trim().split("\n");
|
||||
chunks.slice(resp_idx).map((chunk) => {
|
||||
try {
|
||||
socket._callbacks.$event.map((f) => {
|
||||
f(chunk);
|
||||
});
|
||||
resp_idx += 1;
|
||||
} catch (e) {
|
||||
if (progressEvent.progress === 1) {
|
||||
// Chunk may be incomplete, so only report errors when full response is available.
|
||||
console.log("Error parsing chunk", chunk, e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const controller = new AbortController();
|
||||
const config = {
|
||||
headers: {
|
||||
"Reflex-Client-Token": getToken(),
|
||||
"Reflex-Event-Handler": handler,
|
||||
},
|
||||
signal: controller.signal,
|
||||
onDownloadProgress: eventHandler,
|
||||
};
|
||||
if (on_upload_progress) {
|
||||
config["onUploadProgress"] = on_upload_progress;
|
||||
}
|
||||
const formdata = new FormData();
|
||||
|
||||
// Add the token and handler to the file name.
|
||||
files.forEach((file) => {
|
||||
formdata.append("files", file, file.path || file.name);
|
||||
});
|
||||
|
||||
// Send the file to the server.
|
||||
upload_controllers[upload_id] = controller;
|
||||
|
||||
try {
|
||||
return await axios.post(getBackendURL(UPLOADURL), formdata, config);
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
console.log(error.response.data);
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
console.log(error.request);
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log(error.message);
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
delete upload_controllers[upload_id];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an event object.
|
||||
* @param name The name of the event.
|
||||
* @param payload The payload of the event.
|
||||
* @param handler The client handler to process event.
|
||||
* @returns The event object.
|
||||
*/
|
||||
export const Event = (name, payload = {}, handler = null) => {
|
||||
return { name, payload, handler };
|
||||
};
|
||||
|
||||
/**
|
||||
* Package client-side storage values as payload to send to the
|
||||
* backend with the hydrate event
|
||||
* @param client_storage The client storage object from context.js
|
||||
* @returns payload dict of client storage values
|
||||
*/
|
||||
export const hydrateClientStorage = (client_storage) => {
|
||||
const client_storage_values = {};
|
||||
if (client_storage.cookies) {
|
||||
for (const state_key in client_storage.cookies) {
|
||||
const cookie_options = client_storage.cookies[state_key];
|
||||
const cookie_name = cookie_options.name || state_key;
|
||||
const cookie_value = cookies.get(cookie_name);
|
||||
if (cookie_value !== undefined) {
|
||||
client_storage_values[state_key] = cookies.get(cookie_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (client_storage.local_storage && typeof window !== "undefined") {
|
||||
for (const state_key in client_storage.local_storage) {
|
||||
const options = client_storage.local_storage[state_key];
|
||||
const local_storage_value = localStorage.getItem(
|
||||
options.name || state_key
|
||||
);
|
||||
if (local_storage_value !== null) {
|
||||
client_storage_values[state_key] = local_storage_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (client_storage.cookies || client_storage.local_storage) {
|
||||
return client_storage_values;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Update client storage values based on backend state delta.
|
||||
* @param client_storage The client storage object from context.js
|
||||
* @param delta The state update from the backend
|
||||
*/
|
||||
const applyClientStorageDelta = (client_storage, delta) => {
|
||||
// find the main state and check for is_hydrated
|
||||
const unqualified_states = Object.keys(delta).filter(
|
||||
(key) => key.split(".").length === 1
|
||||
);
|
||||
if (unqualified_states.length === 1) {
|
||||
const main_state = delta[unqualified_states[0]];
|
||||
if (main_state.is_hydrated !== undefined && !main_state.is_hydrated) {
|
||||
// skip if the state is not hydrated yet, since all client storage
|
||||
// values are sent in the hydrate event
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Save known client storage values to cookies and localStorage.
|
||||
for (const substate in delta) {
|
||||
for (const key in delta[substate]) {
|
||||
const state_key = `${substate}.${key}`;
|
||||
if (client_storage.cookies && state_key in client_storage.cookies) {
|
||||
const cookie_options = { ...client_storage.cookies[state_key] };
|
||||
const cookie_name = cookie_options.name || state_key;
|
||||
delete cookie_options.name; // name is not a valid cookie option
|
||||
cookies.set(cookie_name, delta[substate][key], cookie_options);
|
||||
} else if (
|
||||
client_storage.local_storage &&
|
||||
state_key in client_storage.local_storage &&
|
||||
typeof window !== "undefined"
|
||||
) {
|
||||
const options = client_storage.local_storage[state_key];
|
||||
localStorage.setItem(options.name || state_key, delta[substate][key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Establish websocket event loop for a NextJS page.
|
||||
* @param dispatch The reducer dispatch function to update state.
|
||||
* @param initial_events The initial app events.
|
||||
* @param client_storage The client storage object from context.js
|
||||
*
|
||||
* @returns [addEvents, connectErrors] -
|
||||
* addEvents is used to queue an event, and
|
||||
* connectErrors is an array of reactive js error from the websocket connection (or null if connected).
|
||||
*/
|
||||
export const useEventLoop = (
|
||||
dispatch,
|
||||
initial_events = () => [],
|
||||
client_storage = {}
|
||||
) => {
|
||||
const socket = useRef(null);
|
||||
const router = useRouter();
|
||||
const [connectErrors, setConnectErrors] = useState([]);
|
||||
|
||||
// Function to add new events to the event queue.
|
||||
const addEvents = (events, _e, event_actions) => {
|
||||
if (event_actions?.preventDefault && _e?.preventDefault) {
|
||||
_e.preventDefault();
|
||||
}
|
||||
if (event_actions?.stopPropagation && _e?.stopPropagation) {
|
||||
_e.stopPropagation();
|
||||
}
|
||||
queueEvents(events, socket);
|
||||
};
|
||||
|
||||
const sentHydrate = useRef(false); // Avoid double-hydrate due to React strict-mode
|
||||
useEffect(() => {
|
||||
if (router.isReady && !sentHydrate.current) {
|
||||
const events = initial_events();
|
||||
addEvents(
|
||||
events.map((e) => ({
|
||||
...e,
|
||||
router_data: (({ pathname, query, asPath }) => ({
|
||||
pathname,
|
||||
query,
|
||||
asPath,
|
||||
}))(router),
|
||||
}))
|
||||
);
|
||||
sentHydrate.current = true;
|
||||
}
|
||||
}, [router.isReady]);
|
||||
|
||||
// Main event loop.
|
||||
useEffect(() => {
|
||||
// Skip if the router is not ready.
|
||||
if (!router.isReady) {
|
||||
return;
|
||||
}
|
||||
// only use websockets if state is present
|
||||
if (Object.keys(initialState).length > 1) {
|
||||
// Initialize the websocket connection.
|
||||
if (!socket.current) {
|
||||
connect(
|
||||
socket,
|
||||
dispatch,
|
||||
["websocket", "polling"],
|
||||
setConnectErrors,
|
||||
client_storage
|
||||
);
|
||||
}
|
||||
(async () => {
|
||||
// Process all outstanding events.
|
||||
while (event_queue.length > 0 && !event_processing) {
|
||||
await processEvent(socket.current);
|
||||
}
|
||||
})();
|
||||
}
|
||||
});
|
||||
|
||||
// localStorage event handling
|
||||
useEffect(() => {
|
||||
const storage_to_state_map = {};
|
||||
|
||||
if (client_storage.local_storage && typeof window !== "undefined") {
|
||||
for (const state_key in client_storage.local_storage) {
|
||||
const options = client_storage.local_storage[state_key];
|
||||
if (options.sync) {
|
||||
const local_storage_value_key = options.name || state_key;
|
||||
storage_to_state_map[local_storage_value_key] = state_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// e is StorageEvent
|
||||
const handleStorage = (e) => {
|
||||
if (storage_to_state_map[e.key]) {
|
||||
const vars = {};
|
||||
vars[storage_to_state_map[e.key]] = e.newValue;
|
||||
const event = Event(
|
||||
`${state_name}.update_vars_internal_state.update_vars_internal`,
|
||||
{ vars: vars }
|
||||
);
|
||||
addEvents([event], e);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("storage", handleStorage);
|
||||
return () => window.removeEventListener("storage", handleStorage);
|
||||
});
|
||||
|
||||
// Route after the initial page hydration.
|
||||
useEffect(() => {
|
||||
const change_complete = () => addEvents(onLoadInternalEvent());
|
||||
router.events.on("routeChangeComplete", change_complete);
|
||||
return () => {
|
||||
router.events.off("routeChangeComplete", change_complete);
|
||||
};
|
||||
}, [router]);
|
||||
|
||||
return [addEvents, connectErrors];
|
||||
};
|
||||
|
||||
/***
|
||||
* Check if a value is truthy in python.
|
||||
* @param val The value to check.
|
||||
* @returns True if the value is truthy, false otherwise.
|
||||
*/
|
||||
export const isTrue = (val) => {
|
||||
return Array.isArray(val) ? val.length > 0 : !!val;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value from a ref.
|
||||
* @param ref The ref to get the value from.
|
||||
* @returns The value.
|
||||
*/
|
||||
export const getRefValue = (ref) => {
|
||||
if (!ref || !ref.current) {
|
||||
return;
|
||||
}
|
||||
if (ref.current.type == "checkbox") {
|
||||
return ref.current.checked; // chakra
|
||||
} else if (
|
||||
ref.current.className?.includes("rt-CheckboxButton") ||
|
||||
ref.current.className?.includes("rt-SwitchButton")
|
||||
) {
|
||||
return ref.current.ariaChecked == "true"; // radix
|
||||
} else if (ref.current.className?.includes("rt-SliderRoot")) {
|
||||
// find the actual slider
|
||||
return ref.current.querySelector(".rt-SliderThumb")?.ariaValueNow;
|
||||
} else {
|
||||
//querySelector(":checked") is needed to get value from radio_group
|
||||
return (
|
||||
ref.current.value ||
|
||||
(ref.current.querySelector &&
|
||||
ref.current.querySelector(":checked") &&
|
||||
ref.current.querySelector(":checked")?.value)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the values from a ref array.
|
||||
* @param refs The refs to get the values from.
|
||||
* @returns The values array.
|
||||
*/
|
||||
export const getRefValues = (refs) => {
|
||||
if (!refs) {
|
||||
return;
|
||||
}
|
||||
// getAttribute is used by RangeSlider because it doesn't assign value
|
||||
return refs.map((ref) =>
|
||||
ref.current
|
||||
? ref.current.value || ref.current.getAttribute("aria-valuenow")
|
||||
: null
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Spread two arrays or two objects.
|
||||
* @param first The first array or object.
|
||||
* @param second The second array or object.
|
||||
* @returns The final merged array or object.
|
||||
*/
|
||||
export const spreadArraysOrObjects = (first, second) => {
|
||||
if (Array.isArray(first) && Array.isArray(second)) {
|
||||
return [...first, ...second];
|
||||
} else if (typeof first === "object" && typeof second === "object") {
|
||||
return { ...first, ...second };
|
||||
} else {
|
||||
throw new Error("Both parameters must be either arrays or objects.");
|
||||
}
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
/** @jsxImportSource @emotion/react */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export default {"styles": {"global": {":root": {}, "body": {"backgroundColor": "#282828"}}}}
|
||||
@@ -1,15 +0,0 @@
|
||||
# This Dockerfile is used to deploy a simple single-container Reflex app instance.
|
||||
FROM python:3.11
|
||||
|
||||
# Copy local context to `/app` inside container (see .dockerignore)
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
|
||||
# Install app requirements and reflex in the container
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
# Deploy templates and prepare app
|
||||
RUN reflex init
|
||||
|
||||
# Always apply migrations before starting the backend.
|
||||
CMD reflex run --env dev
|
||||
36
src/web/README.md
Normal file
36
src/web/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
@@ -1,9 +0,0 @@
|
||||
/* Hide scrollbar for all elements */
|
||||
::-webkit-scrollbar {
|
||||
display: none; /* Safari and Chrome */
|
||||
}
|
||||
|
||||
/* Hide scrollbar for Firefox */
|
||||
html {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 407 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,11 +0,0 @@
|
||||
@font-face {
|
||||
font-family: ComicCode;
|
||||
src: url("ComicCode-Regular.otf") format("opentype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ComicCodeBold;
|
||||
font-weight: bold;
|
||||
src: url("ComicCode-Bold.otf") format("opentype");
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["public/*"]
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
4
src/web/next.config.mjs
Normal file
4
src/web/next.config.mjs
Normal file
@@ -0,0 +1,4 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {};
|
||||
|
||||
export default nextConfig;
|
||||
4213
src/web/package-lock.json
generated
Normal file
4213
src/web/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
src/web/package.json
Normal file
20
src/web/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"next": "14.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.1.3"
|
||||
}
|
||||
}
|
||||
1
src/web/public/next.svg
Normal file
1
src/web/public/next.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
src/web/public/vercel.svg
Normal file
1
src/web/public/vercel.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
|
||||
|
After Width: | Height: | Size: 629 B |
@@ -1 +0,0 @@
|
||||
reflex==0.4.4
|
||||
@@ -1,6 +0,0 @@
|
||||
import reflex as rx
|
||||
|
||||
config = rx.Config(
|
||||
app_name="web",
|
||||
api_url="http://localhost:8000",
|
||||
)
|
||||
20
src/web/src/app/layout.js
Normal file
20
src/web/src/app/layout.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// Imports
|
||||
import Navbar from "@/components/navbar";
|
||||
import Footer from "@/components/footer";
|
||||
|
||||
// Metadata
|
||||
export const metadata = {
|
||||
title: "Timothy Pidashev",
|
||||
description: "Engineering the Future!"
|
||||
};
|
||||
|
||||
// Exports
|
||||
export default function Layout({children}) {
|
||||
return (
|
||||
<>
|
||||
<Navbar/>
|
||||
<main>{children}</main>
|
||||
<Footer/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
11
src/web/src/app/not-found.jsx
Normal file
11
src/web/src/app/not-found.jsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const Not_found = () => {
|
||||
return (
|
||||
<div>
|
||||
<p>404: The requested page is not found!</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Not_found
|
||||
10
src/web/src/app/page.jsx
Normal file
10
src/web/src/app/page.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
// Imports
|
||||
|
||||
// Metadata
|
||||
|
||||
// Exports
|
||||
export default function Index() {
|
||||
return (
|
||||
<h1>Test</h1>
|
||||
);
|
||||
}
|
||||
0
src/web/src/app/projects/keep
Normal file
0
src/web/src/app/projects/keep
Normal file
0
src/web/src/app/resume/keep
Normal file
0
src/web/src/app/resume/keep
Normal file
0
src/web/src/app/shop/keep
Normal file
0
src/web/src/app/shop/keep
Normal file
7
src/web/src/components/footer.jsx
Normal file
7
src/web/src/components/footer.jsx
Normal file
@@ -0,0 +1,7 @@
|
||||
const Footer = () => {
|
||||
return (
|
||||
<h1>footer</h1>
|
||||
);
|
||||
}
|
||||
|
||||
export default Footer;
|
||||
7
src/web/src/components/navbar.jsx
Normal file
7
src/web/src/components/navbar.jsx
Normal file
@@ -0,0 +1,7 @@
|
||||
const Navbar = () => {
|
||||
return (
|
||||
<h1>navbar</h1>
|
||||
);
|
||||
}
|
||||
|
||||
export default Navbar;
|
||||
@@ -1,2 +0,0 @@
|
||||
from .navbar import navbar
|
||||
from .footer import footer
|
||||
@@ -1,20 +0,0 @@
|
||||
import reflex as rx
|
||||
from web.style import *
|
||||
|
||||
def footer():
|
||||
return rx.box(
|
||||
footer_content(),
|
||||
border_top=f"2px solid {color['white']};"
|
||||
)
|
||||
|
||||
|
||||
|
||||
def footer_content():
|
||||
return rx.center(
|
||||
rx.vstack(
|
||||
rx.heading("Footer", size="9"),
|
||||
align="center",
|
||||
spacing="7"
|
||||
),
|
||||
height="15vh"
|
||||
)
|
||||
@@ -1,27 +0,0 @@
|
||||
import reflex as rx
|
||||
from web.state import *
|
||||
from web.style import *
|
||||
|
||||
|
||||
def navbar():
|
||||
return rx.box(
|
||||
rx.center(
|
||||
rx.flex(
|
||||
rx.link("Home", href="/")
|
||||
),
|
||||
rx.flex(
|
||||
rx.link("Projects", href="/projects"),
|
||||
),
|
||||
rx.flex(
|
||||
rx.link("Resume", href="/resume")
|
||||
),
|
||||
rx.flex(
|
||||
rx.link("Blog", href="/blog")
|
||||
),
|
||||
rx.flex(
|
||||
rx.link("Shop", href="/shop")
|
||||
),
|
||||
spacing="7",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
from .motion import *
|
||||
@@ -1,44 +0,0 @@
|
||||
"""Reflex custom component Motion."""
|
||||
|
||||
import reflex as rx
|
||||
from typing import Any, Dict, List, Optional, Set, Union
|
||||
|
||||
class Motion(rx.Component):
|
||||
"""Motion component."""
|
||||
|
||||
# The React library to wrap.
|
||||
library = "framer-motion"
|
||||
|
||||
# The React component tag.
|
||||
tag = "motion.div"
|
||||
|
||||
# The initial state of the component.
|
||||
initial: rx.Var[Dict[str, Union[float, str]]]
|
||||
|
||||
# The animate state of the component.
|
||||
animate: rx.Var[Dict[str, Union[float, str]]]
|
||||
|
||||
# The transition
|
||||
transition: rx.Var[Dict[str, Union[float, str]]]
|
||||
|
||||
# What the component does when it's hovered.
|
||||
while_hover: rx.Var[Dict[str, Union[float, str]]]
|
||||
|
||||
# What the component does when it's tapped.
|
||||
while_tap: rx.Var[Dict[str, Union[float, str]]]
|
||||
|
||||
# What the component does when it's in view.
|
||||
while_in_view: rx.Var[Dict[str, Union[float, str]]]
|
||||
|
||||
# What the component does when its focused.
|
||||
while_focus: rx.Var[Dict[str, Union[float, str]]]
|
||||
|
||||
# What the component does when it's out of view.
|
||||
viewport: rx.Var[Union[str, List[str]]]
|
||||
|
||||
def _get_custom_code(self) -> str:
|
||||
return """
|
||||
import { useIsPresent } from "framer-motion";
|
||||
"""
|
||||
|
||||
motion = Motion.create
|
||||
@@ -1,14 +0,0 @@
|
||||
from web.route import Route
|
||||
|
||||
from .index import index
|
||||
from .projects import projects
|
||||
from .resume import resume
|
||||
from .blog import blog
|
||||
from .shop import shop
|
||||
from .page404 import page404
|
||||
|
||||
routes = [
|
||||
*[r for r in locals().values() if isinstance(r, Route)],
|
||||
#*blog_routes,
|
||||
#*doc_routes,
|
||||
]
|
||||
@@ -1,20 +0,0 @@
|
||||
import reflex as rx
|
||||
from web.templates import webpage
|
||||
|
||||
@webpage(path="/blog", title="Blog")
|
||||
def blog():
|
||||
return rx.box(
|
||||
blog_content()
|
||||
)
|
||||
|
||||
def blog_content():
|
||||
return rx.center(
|
||||
rx.vstack(
|
||||
rx.heading("Blog", size="9"),
|
||||
align="center",
|
||||
spacing="7",
|
||||
),
|
||||
height="100vh"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
import reflex as rx
|
||||
from web.components import navbar
|
||||
from web.templates import webpage
|
||||
from web.motion import motion
|
||||
|
||||
@webpage(path="/", title="Timothy Pidashev")
|
||||
def index() -> rx.Component:
|
||||
return rx.box(
|
||||
hero_section_1(),
|
||||
hero_section_2(),
|
||||
hero_section_3()
|
||||
)
|
||||
|
||||
def hero_section_1():
|
||||
return rx.center(
|
||||
rx.vstack( # Using stack instead of vstack for scrollability
|
||||
motion( # Wrap the text with motion to apply animation
|
||||
rx.heading(
|
||||
"Hello, Im",
|
||||
style={
|
||||
"text_align": "center",
|
||||
"line_height": "1.2em",
|
||||
"white-space": "nowrap",
|
||||
"overflow": "hidden",
|
||||
"@media (max-width: 768px)": {"font_size": "3em"},
|
||||
"@media (min-width: 769px)": {"font_size": "5em"},
|
||||
"text_shadow": "3px 2px 2px rgba(199, 130, 59, 0.15);"
|
||||
}
|
||||
),
|
||||
initial={"opacity": 0, "y": 50}, # Initial styles (hidden and moved down)
|
||||
animate={"opacity": 1, "y": 0}, # Animation styles (fade in and move up)
|
||||
transition={"type": "tween", "duration": 1, "delay": 0.5}, # Animation transition (smooth transition)
|
||||
),
|
||||
motion(
|
||||
rx.heading(
|
||||
"Timothy",
|
||||
style={
|
||||
"text_align": "center",
|
||||
"line_height": "1.2em",
|
||||
"white-space": "nowrap",
|
||||
"overflow": "hidden",
|
||||
"@media (max-width: 768px)": {"font_size": "3em"},
|
||||
"@media (min-width: 769px)": {"font_size": "5em"},
|
||||
"text_shadow": "3px 2px 2px rgba(199, 130, 59, 0.15);"
|
||||
}
|
||||
),
|
||||
initial={"opacity": 0, "y": 50},
|
||||
animate={"opacity": 1, "y": 0},
|
||||
transition={"type": "tween", "duration": 1, "delay": 0.5}
|
||||
),
|
||||
motion(
|
||||
rx.heading(
|
||||
"Pidashev",
|
||||
style={
|
||||
"font_size": "6vw",
|
||||
"text_align": "center",
|
||||
"line_height": "1.2em",
|
||||
"white-space": "nowrap",
|
||||
"overflow": "hidden",
|
||||
"@media (max-width: 768px)": {"font_size": "3em"},
|
||||
"@media (min-width: 769px)": {"font_size": "5em"},
|
||||
"text_shadow": "3px 2px 2px rgba(199, 130, 59, 0.15);"
|
||||
}
|
||||
),
|
||||
initial={"opacity": 0, "y": 50},
|
||||
animate={"opacity": 1, "y": 0},
|
||||
transition={"type": "tween", "duration": 1, "delay": 0.5}
|
||||
),
|
||||
align="center",
|
||||
overflow="auto", # Enable scrolling
|
||||
),
|
||||
height="100vh"
|
||||
)
|
||||
|
||||
def hero_section_2():
|
||||
return rx.center(
|
||||
rx.vstack(
|
||||
motion(
|
||||
rx.heading(
|
||||
"A 19 year old",
|
||||
style={
|
||||
"text_align": "center",
|
||||
"line_height": "1.2em",
|
||||
"white-space": "nowrap",
|
||||
"overflow": "hidden",
|
||||
"@media (max-width: 768px)": {"font_size": "2.5em"},
|
||||
"@media (min-width: 769px)": {"font_size": "5em"},
|
||||
"text_shadow": "3px 2px 2px rgba(199, 130, 59, 0.15);"
|
||||
}
|
||||
),
|
||||
initial={"opacity": 0, "y": 50},
|
||||
animate={"opacity": 1, "y": 0},
|
||||
transition={"type": "tween", "duration": 1, "delay": 0.5}
|
||||
),
|
||||
motion(
|
||||
rx.heading(
|
||||
"on an epic journey",
|
||||
style={
|
||||
"text_align": "center",
|
||||
"line_height": "1.2em",
|
||||
"white-space": "nowrap",
|
||||
"overflow": "hidden",
|
||||
"@media (max-width: 768px)": {"font_size": "2.5em"},
|
||||
"@media (min-width: 769px)": {"font_size": "5em"},
|
||||
"text_shadow": "3px 2px 2px rgba(199, 130, 59, 0.15);"
|
||||
}
|
||||
),
|
||||
initial={"opacity": 0, "y": 50},
|
||||
animate={"opacity": 1, "y": 0},
|
||||
transition={"type": "tween", "duration": 1, "delay": 0.5}
|
||||
),
|
||||
align="center",
|
||||
overflow="auto"
|
||||
),
|
||||
height="100vh"
|
||||
)
|
||||
|
||||
def hero_section_3():
|
||||
return rx.center(
|
||||
rx.vstack(
|
||||
motion(
|
||||
rx.heading(
|
||||
"to become a",
|
||||
style={
|
||||
"text_align": "center",
|
||||
"line_height": "1.2em",
|
||||
"white-space": "nowrap",
|
||||
"overflow": "hidden",
|
||||
"@media (max-width: 768px)": {"font_size": "2.5em"},
|
||||
"@media (min-width: 769px)": {"font_size": "5em"},
|
||||
"text_shadow": "3px 2px 2px rgba(199, 130, 59, 0.15);"
|
||||
}
|
||||
),
|
||||
initial={"opacity": 0, "y": 50},
|
||||
animate={"opacity": 1, "y": 0},
|
||||
transition={"type": "tween", "duration": 1, "delay": 0.5}
|
||||
),
|
||||
motion(
|
||||
rx.heading(
|
||||
"software engineer!",
|
||||
style={
|
||||
"text_align": "center",
|
||||
"line_height": "1.2em",
|
||||
"white-space": "nowrap",
|
||||
"overflow": "hidden",
|
||||
"@media (max-width: 768px)": {"font_size": "2.5em"},
|
||||
"@media (min-width: 769px)": {"font_size": "5em"},
|
||||
"text_shadow": "3px 2px 2px rgba(199, 130, 59, 0.15);"
|
||||
}
|
||||
),
|
||||
initial={"opacity": 0, "y": 50},
|
||||
animate={"opacity": 1, "y": 0},
|
||||
transition={"type": "tween", "duration": 1, "delay": 0.5}
|
||||
),
|
||||
align="center",
|
||||
overflow="auto",
|
||||
),
|
||||
height="100vh"
|
||||
)
|
||||
@@ -1,19 +0,0 @@
|
||||
import reflex as rx
|
||||
from web.templates import webpage
|
||||
|
||||
# TODO: Add a go back here link
|
||||
|
||||
@webpage(path="/404", title="Page Not Found")
|
||||
def page404():
|
||||
return rx.box(
|
||||
rx.center(
|
||||
rx.vstack(
|
||||
rx.heading(
|
||||
"Whoops, this page doesn't exist...", size="9"
|
||||
),
|
||||
align="center"
|
||||
),
|
||||
height="100vh",
|
||||
width="100%",
|
||||
),
|
||||
)
|
||||
@@ -1,18 +0,0 @@
|
||||
import reflex as rx
|
||||
from web.templates import webpage
|
||||
|
||||
@webpage(path="/projects", title="Projects")
|
||||
def projects():
|
||||
return rx.box(
|
||||
projects_content()
|
||||
)
|
||||
|
||||
def projects_content():
|
||||
return rx.center(
|
||||
rx.vstack(
|
||||
rx.heading("Projects", size="9"),
|
||||
align="center",
|
||||
spacing="7",
|
||||
),
|
||||
height="100vh"
|
||||
)
|
||||
@@ -1,18 +0,0 @@
|
||||
import reflex as rx
|
||||
from web.templates import webpage
|
||||
|
||||
@webpage(path="/resume", title="Resume")
|
||||
def resume():
|
||||
return rx.box(
|
||||
resume_content()
|
||||
)
|
||||
|
||||
def resume_content():
|
||||
return rx.center(
|
||||
rx.vstack(
|
||||
rx.heading("Resume", size="9"),
|
||||
align="center",
|
||||
spacing="7",
|
||||
),
|
||||
height="100vh"
|
||||
)
|
||||
@@ -1,20 +0,0 @@
|
||||
import reflex as rx
|
||||
from web.templates import webpage
|
||||
|
||||
@webpage(path="/shop", title="Shop")
|
||||
def shop():
|
||||
return rx.box(
|
||||
shop_content()
|
||||
)
|
||||
|
||||
def shop_content():
|
||||
return rx.center(
|
||||
rx.vstack(
|
||||
rx.heading("Shop", size="9"),
|
||||
align="center",
|
||||
spacing="7",
|
||||
),
|
||||
height="100vh"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
"""Manage routing for the application."""
|
||||
|
||||
import inspect
|
||||
import reflex as rx
|
||||
from reflex.base import Base
|
||||
from typing import Callable
|
||||
|
||||
class Route(Base):
|
||||
"""A Page Route."""
|
||||
|
||||
# The path of the route.
|
||||
path: str
|
||||
|
||||
# The page title.
|
||||
title: str | None = None
|
||||
|
||||
# The component to render for the route.
|
||||
component: Callable[[], rx.Component]
|
||||
|
||||
def get_path(component_function: Callable):
|
||||
"""Get the path for a page based on the file location.
|
||||
|
||||
Args:
|
||||
component_function: The component function for the page.
|
||||
"""
|
||||
module = inspect.getmodule(component_function)
|
||||
|
||||
# Create a path based on the module name.
|
||||
return module.__name__.replace(".", "/").replace("_", "-").split("web/pages")[1]
|
||||
@@ -1,2 +0,0 @@
|
||||
from .state import State
|
||||
from .theme import ThemeState
|
||||
@@ -1,5 +0,0 @@
|
||||
import reflex as rx
|
||||
|
||||
class State(rx.State):
|
||||
"""The app state."""
|
||||
pass
|
||||
@@ -1,19 +0,0 @@
|
||||
import reflex as rx
|
||||
from .state import State
|
||||
from web.style import *
|
||||
from typing import Dict, Any, List
|
||||
|
||||
|
||||
class ThemeState(State):
|
||||
"""App Theme State"""
|
||||
|
||||
current_theme: int = 0
|
||||
|
||||
themes = {
|
||||
0: {"background_color": "#282828"},
|
||||
1: {"background_color": "#000000"},
|
||||
}
|
||||
|
||||
@rx.var
|
||||
def theme(self) -> dict:
|
||||
return self.themes[self.current_theme]
|
||||
@@ -1,91 +0,0 @@
|
||||
import reflex as rx
|
||||
|
||||
color = {
|
||||
"white": "#ebdbb2",
|
||||
"black": "#000000",
|
||||
"red": {
|
||||
100: "#fb4934",
|
||||
200: "#cc241d",
|
||||
},
|
||||
"green": {
|
||||
100: "#b8bb26",
|
||||
200: "#98971a",
|
||||
},
|
||||
"yellow": {
|
||||
100: "#fabd2f",
|
||||
200: "#d79921",
|
||||
},
|
||||
"blue": {
|
||||
100: "#83a598",
|
||||
200: "#458588",
|
||||
},
|
||||
"purple": {
|
||||
100: "#d3869b",
|
||||
200: "#b16286",
|
||||
},
|
||||
"aqua": {
|
||||
100: "#8ec07c",
|
||||
200: "#689d6a",
|
||||
}
|
||||
}
|
||||
|
||||
base_style = {
|
||||
# Background
|
||||
# TODO: Implement dynamic background switching once reflex allows for Dict state management
|
||||
"background_color": "#282828",
|
||||
|
||||
# Text
|
||||
rx.text: {
|
||||
"font_family": "ComicCode",
|
||||
"font_size": 24,
|
||||
"color": color["white"]
|
||||
},
|
||||
|
||||
# Heading
|
||||
rx.heading: {
|
||||
"font_family": "ComicCode",
|
||||
"font_size": 32,
|
||||
"color": color["white"]
|
||||
},
|
||||
|
||||
# Link
|
||||
rx.link: {
|
||||
"font_family": "ComicCode",
|
||||
"font_size": 24,
|
||||
"color": color["white"],
|
||||
"text_decoration": "none",
|
||||
"_hover": {
|
||||
"color": color["green"][100]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
# Dark Theme
|
||||
#dark_theme = dict()
|
||||
#dark_theme["background_color"] = "#282828"
|
||||
|
||||
# Soft Contrast Dark Theme
|
||||
# TODO
|
||||
|
||||
# Medium Contrast Dark Theme
|
||||
# TODO
|
||||
|
||||
# Hard Contrast Dark Theme
|
||||
# TODO
|
||||
|
||||
# Amoled Contrast Dark Theme
|
||||
#amoled_dark_theme = dict()
|
||||
#amoled_dark_theme = "#000000"
|
||||
|
||||
# Light Theme
|
||||
# TODO
|
||||
|
||||
# Soft Contrast Light Theme
|
||||
# TODO
|
||||
|
||||
# Medium Contrast Light Theme
|
||||
# TODO
|
||||
|
||||
# Hard Contrast Light Theme
|
||||
# TODO
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
from .webpage import webpage
|
||||
@@ -1,58 +0,0 @@
|
||||
from typing import Callable
|
||||
import reflex as rx
|
||||
from web.route import Route
|
||||
|
||||
def webpage(path: str, title: str = "Timothy Pidashev", props=None) -> Callable:
|
||||
"""This template wraps the webpage with the navbar and footer.
|
||||
|
||||
Args:
|
||||
path: The path of the page.
|
||||
title: The title of the page.
|
||||
props: Props to apply to the template.
|
||||
|
||||
Returns:
|
||||
A wrapper function that returns the full webpage.
|
||||
"""
|
||||
props = props or {}
|
||||
|
||||
def webpage(contents: Callable[[], Route]) -> Route:
|
||||
"""Wrapper to create a templated route.
|
||||
|
||||
Args:
|
||||
contents: The function to create the page route.
|
||||
|
||||
Returns:
|
||||
The templated route.
|
||||
"""
|
||||
|
||||
def wrapper(*children, **props) -> rx.Component:
|
||||
"""The template component.
|
||||
|
||||
Args:
|
||||
children: The children components.
|
||||
props: The props to apply to the component.
|
||||
|
||||
Returns:
|
||||
The component with the template applied.
|
||||
"""
|
||||
# Import here to avoid circular imports.
|
||||
from web.components.navbar import navbar
|
||||
from web.components.footer import footer
|
||||
|
||||
# Declare the entire page content
|
||||
return rx.box(
|
||||
rx.box(
|
||||
navbar(),
|
||||
contents(*children, **props),
|
||||
footer(),
|
||||
**props
|
||||
)
|
||||
)
|
||||
|
||||
return Route(
|
||||
path=path,
|
||||
title=title,
|
||||
component=wrapper,
|
||||
)
|
||||
|
||||
return webpage
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user