begin next js version

This commit is contained in:
Timothy Pidashev
2024-03-16 22:51:31 -07:00
parent bceec10c3f
commit b87d34410a
101 changed files with 4408 additions and 8082 deletions
+36 -1
View File
@@ -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
+1 -1
View File
@@ -10,6 +10,6 @@ timmypidashev.localhost {
}
handle @backend_routes {
reverse_proxy landing:8000
reverse_proxy web:8000
}
}
-3
View File
@@ -1,3 +0,0 @@
.web
__pycache__/*
Dockerfile
+3
View File
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
+36 -4
View File
@@ -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
View File
@@ -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
View File
@@ -1 +0,0 @@
{"PING": "http://localhost:8000/ping", "EVENT": "ws://localhost:8000/_event", "UPLOAD": "http://localhost:8000/_upload"}
-1
View File
@@ -1 +0,0 @@
module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true};
-30
View File
@@ -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"
}
}
-173
View File
@@ -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>
)
}
-44
View File
@@ -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>
);
}
-18
View File
@@ -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>
)
}
-171
View File
@@ -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>
)
}
-6
View File
@@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
-9
View File
@@ -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

-11
View File
@@ -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
View File
@@ -1 +0,0 @@
{"version": "0.4.3", "project_hash": 76742530442461448999350655539571039381, "last_version_check_datetime": "2024-03-12 10:19:00.533460"}
-3
View File
@@ -1,3 +0,0 @@
@import url('./tailwind.css');
@import url('@/fonts/fonts.css');
@import url('@/css/scrollbar.css');
-3
View File
@@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
-7
View File
@@ -1,7 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./pages/**/*.{js,ts,jsx,tsx}", "./utils/**/*.{js,ts,jsx,tsx}"],
theme: null,
plugins: [
],
};
-36
View File
@@ -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
}
-8
View File
@@ -1,8 +0,0 @@
/** @jsxImportSource @emotion/react */
import { memo } from "react"
import { E, isTrue } from "/utils/state"
-112
View File
@@ -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>
)
}
-69
View File
@@ -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 };
}
-43
View File
@@ -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,
};
},
};
},
};
}
-728
View File
@@ -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
View File
@@ -1 +0,0 @@
export default {"styles": {"global": {":root": {}, "body": {"backgroundColor": "#282828"}}}}
-15
View File
@@ -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
View 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.
-9
View File
@@ -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.
-11
View File
@@ -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
View File
@@ -0,0 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default nextConfig;
+4213
View File
File diff suppressed because it is too large Load Diff
+20
View 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
View 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
View 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
View File
@@ -1 +0,0 @@
reflex==0.4.4
-6
View File
@@ -1,6 +0,0 @@
import reflex as rx
config = rx.Config(
app_name="web",
api_url="http://localhost:8000",
)
+20
View 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
View 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
View File
@@ -0,0 +1,10 @@
// Imports
// Metadata
// Exports
export default function Index() {
return (
<h1>Test</h1>
);
}
View File
View File
View File
+7
View File
@@ -0,0 +1,7 @@
const Footer = () => {
return (
<h1>footer</h1>
);
}
export default Footer;
+7
View File
@@ -0,0 +1,7 @@
const Navbar = () => {
return (
<h1>navbar</h1>
);
}
export default Navbar;
-2
View File
@@ -1,2 +0,0 @@
from .navbar import navbar
from .footer import footer
-20
View File
@@ -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"
)
-27
View File
@@ -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
View File
@@ -1 +0,0 @@
from .motion import *
-44
View File
@@ -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
-14
View File
@@ -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,
]
-20
View File
@@ -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"
)
-159
View File
@@ -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"
)
-19
View File
@@ -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%",
),
)
-18
View File
@@ -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"
)
-18
View File
@@ -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"
)
-20
View File
@@ -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"
)
-29
View File
@@ -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]
-2
View File
@@ -1,2 +0,0 @@
from .state import State
from .theme import ThemeState
-5
View File
@@ -1,5 +0,0 @@
import reflex as rx
class State(rx.State):
"""The app state."""
pass
-19
View File
@@ -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]
-91
View File
@@ -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
View File
@@ -1 +0,0 @@
from .webpage import webpage
-58
View File
@@ -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