diff --git a/package.json b/package.json
index 9b6394fa9..00ee4d146 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,9 @@
"private": true,
"dependencies": {
"@codemirror/lang-javascript": "^6.1.1",
+ "@fortawesome/fontawesome-svg-core": "^6.4.0",
+ "@fortawesome/free-solid-svg-icons": "^6.4.0",
+ "@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.13",
"@tauri-apps/api": "^1.3.0",
"@testing-library/jest-dom": "^5.14.1",
@@ -19,8 +22,10 @@
"http-server": "^14.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-hot-toast": "^2.4.1",
"react-json-view": "^1.21.3",
"react-modal-promise": "^1.0.2",
+ "react-router-dom": "^6.14.1",
"react-scripts": "5.0.1",
"sketch-helpers": "^0.0.3",
"swr": "^2.0.4",
diff --git a/public/kitt-8bit-winking.svg b/public/kitt-8bit-winking.svg
new file mode 100644
index 000000000..0501e2067
--- /dev/null
+++ b/public/kitt-8bit-winking.svg
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/kitt-arcade-winking.svg b/public/kitt-arcade-winking.svg
new file mode 100644
index 000000000..5e1ef86e3
--- /dev/null
+++ b/public/kitt-arcade-winking.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/App.test.tsx b/src/App.test.tsx
index cd373d517..9728dcd27 100644
--- a/src/App.test.tsx
+++ b/src/App.test.tsx
@@ -1,5 +1,6 @@
import { render, screen } from '@testing-library/react'
import { App } from './App'
+import { BrowserRouter } from 'react-router-dom';
let listener: ((rect: any) => void) | undefined = undefined
;(global as any).ResizeObserver = class ResizeObserver {
@@ -12,7 +13,9 @@ let listener: ((rect: any) => void) | undefined = undefined
}
test('renders learn react link', () => {
- render( )
+ render(
+
+ )
const linkElement = screen.getByText(/Variables/i)
expect(linkElement).toBeInTheDocument()
})
diff --git a/src/App.tsx b/src/App.tsx
index 500cd2d28..6f7f8f6ae 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -21,6 +21,7 @@ import ModalContainer from 'react-modal-promise'
import { EngineCommandManager } from './lang/std/engineConnection'
import { isOverlap } from './lib/utils'
import { SetToken } from './components/TokenInput'
+import { AppHeader } from './components/AppHeader'
export function App() {
const cam = useRef()
@@ -253,6 +254,7 @@ export function App() {
}, [code, isStreamReady])
return (
+
@@ -285,9 +287,6 @@ export function App() {
-
-
-
diff --git a/src/Auth.tsx b/src/Auth.tsx
index 362f62e26..e9a506cb8 100644
--- a/src/Auth.tsx
+++ b/src/Auth.tsx
@@ -4,6 +4,24 @@ import withBaseUrl from './lib/withBaseURL'
import { App } from './App'
import { SetToken } from './components/TokenInput'
import { useStore } from './useStore'
+import {
+ createBrowserRouter,
+ RouterProvider,
+} from "react-router-dom"
+import { ErrorPage } from './components/ErrorPage'
+import { Settings } from './routes/Settings'
+
+const router = createBrowserRouter([
+ {
+ path: "/",
+ element:
,
+ errorElement:
,
+ },
+ {
+ path: "/settings",
+ element:
,
+ }
+])
export const Auth = () => {
const { data: user } = useSWR(withBaseUrl('/user'), fetcher) as any
@@ -37,5 +55,5 @@ export const Auth = () => {
)
}
- return
+ return
}
diff --git a/src/colors.css b/src/colors.css
new file mode 100644
index 000000000..43f59280b
--- /dev/null
+++ b/src/colors.css
@@ -0,0 +1,263 @@
+:root {
+ /*
+ Generated using Catmosphere Theme Builder
+ by KittyCAD
+ https://catmosphere-theme-builder.vercel.app/?colors=%5B%7B%22from%22:%7B%22l%22:1,%22c%22:0.01,%22h%22:78%7D,%22to%22:%7B%22l%22:0.065,%22c%22:0.05,%22h%22:182.6%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.45,%22h%22:122.4%7D,%22to%22:%7B%22l%22:0.13,%22c%22:0.031,%22h%22:137.2%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.13,%22h%22:176%7D,%22to%22:%7B%22l%22:0.116,%22c%22:0.097,%22h%22:213.1%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.169,%22h%22:144.4%7D,%22to%22:%7B%22l%22:0.12,%22c%22:0.45,%22h%22:132.7%7D,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.087,%22h%22:261.6%7D,%22to%22:%7B%22l%22:0.22,%22c%22:0.084,%22h%22:275.5%7D,%22steps%22:12,%22uuid%22:%227tpx9pf1zd6%22%7D,%7B%22from%22:%7B%22l%22:0.954,%22c%22:0.108,%22h%22:280.6%7D,%22to%22:%7B%22l%22:0.166,%22c%22:0.188,%22h%22:263.8%7D,%22steps%22:12,%22uuid%22:%22vu652mebd3%22%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.115,%22h%22:0%7D,%22to%22:%7B%22l%22:0.096,%22c%22:0.261,%22h%22:302%7D,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.185,%22h%22:19.8%7D,%22to%22:%7B%22l%22:0.368,%22c%22:0.45,%22h%22:9.4%7D,%22steps%22:8,%22uuid%22:%22g05inkd34l%22%7D,%7B%22from%22:%7B%22l%22:0.912,%22c%22:0.139,%22h%22:87%7D,%22to%22:%7B%22l%22:0.502,%22c%22:0.45,%22h%22:97.7%7D,%22steps%22:8,%22uuid%22:%22l892hcw4ef%22%7D,%7B%22from%22:%7B%22l%22:0.89,%22c%22:0.16,%22h%22:143.4%7D,%22to%22:%7B%22l%22:0.466,%22c%22:0.208,%22h%22:147.7%7D,%22steps%22:8,%22uuid%22:%22hkd09y9ov4h%22%7D%5D
+ */
+ /* Chalkboard */
+ --chalkboard-10: oklch(99.70% 0.008766 102.8deg);
+ --chalkboard-20: oklch(91.34% 0.009353 109.0deg);
+ --chalkboard-30: oklch(82.99% 0.009940 115.2deg);
+ --chalkboard-40: oklch(74.63% 0.01053 121.4deg);
+ --chalkboard-50: oklch(66.27% 0.01111 127.6deg);
+ --chalkboard-60: oklch(57.92% 0.01170 133.9deg);
+ --chalkboard-70: oklch(49.56% 0.01229 140.1deg);
+ --chalkboard-80: oklch(41.21% 0.01288 146.3deg);
+ --chalkboard-90: oklch(32.85% 0.01346 152.5deg);
+ --chalkboard-100: oklch(24.49% 0.01405 158.7deg);
+ --chalkboard-110: oklch(16.14% 0.01464 164.9deg);
+ --chalkboard-120: oklch(7.783% 0.01522 171.1deg);
+
+ /* Energy */
+ --energy-10: oklch(93.31% 0.2270 122.3deg);
+ --energy-20: oklch(86.01% 0.2092 123.6deg);
+ --energy-30: oklch(78.71% 0.1914 125.0deg);
+ --energy-40: oklch(71.41% 0.1736 126.3deg);
+ --energy-50: oklch(64.10% 0.1557 127.7deg);
+ --energy-60: oklch(56.80% 0.1379 129.1deg);
+ --energy-70: oklch(49.50% 0.1201 130.4deg);
+ --energy-80: oklch(42.20% 0.1023 131.8deg);
+ --energy-90: oklch(34.90% 0.08446 133.1deg);
+ --energy-100: oklch(27.60% 0.06664 134.5deg);
+ --energy-110: oklch(20.30% 0.04882 135.8deg);
+ --energy-120: oklch(13.00% 0.03100 137.2deg);
+
+ /* Liquid */
+ --liquid-10: oklch(93.45% 0.1002 193.1deg);
+ --liquid-20: oklch(86.21% 0.09511 198.7deg);
+ --liquid-30: oklch(78.97% 0.09003 204.2deg);
+ --liquid-40: oklch(71.74% 0.08495 209.8deg);
+ --liquid-50: oklch(64.50% 0.07988 215.3deg);
+ --liquid-60: oklch(57.26% 0.07480 220.9deg);
+ --liquid-70: oklch(50.03% 0.06972 226.4deg);
+ --liquid-80: oklch(42.79% 0.06465 232.0deg);
+ --liquid-90: oklch(35.56% 0.05957 237.5deg);
+ --liquid-100: oklch(28.32% 0.05450 243.1deg);
+ --liquid-110: oklch(21.08% 0.04942 248.6deg);
+ --liquid-120: oklch(13.85% 0.04434 254.2deg);
+
+ /* Fern */
+ --fern-10: oklch(93.22% 0.1243 144.8deg);
+ --fern-20: oklch(86.59% 0.1193 144.6deg);
+ --fern-30: oklch(79.97% 0.1143 144.4deg);
+ --fern-40: oklch(73.34% 0.1093 144.2deg);
+ --fern-50: oklch(66.71% 0.1043 144.0deg);
+ --fern-60: oklch(60.09% 0.09927 143.8deg);
+ --fern-70: oklch(53.46% 0.09425 143.6deg);
+ --fern-80: oklch(46.83% 0.08924 143.3deg);
+ --fern-90: oklch(40.21% 0.08422 143.1deg);
+ --fern-100: oklch(33.58% 0.07920 142.9deg);
+ --fern-110: oklch(26.95% 0.07419 142.7deg);
+ --fern-120: oklch(20.33% 0.06917 142.5deg);
+
+ /* Cool */
+ --cool-10: oklch(97.71% 0.03321 196.6deg);
+ --cool-20: oklch(90.82% 0.03783 203.8deg);
+ --cool-30: oklch(83.94% 0.04245 211.0deg);
+ --cool-40: oklch(77.06% 0.04706 218.1deg);
+ --cool-50: oklch(70.18% 0.05168 225.3deg);
+ --cool-60: oklch(63.29% 0.05630 232.5deg);
+ --cool-70: oklch(56.41% 0.06091 239.6deg);
+ --cool-80: oklch(49.53% 0.06553 246.8deg);
+ --cool-90: oklch(42.65% 0.07015 254.0deg);
+ --cool-100: oklch(35.76% 0.07477 261.2deg);
+ --cool-110: oklch(28.88% 0.07938 268.3deg);
+ --cool-120: oklch(22.00% 0.08400 275.5deg);
+
+ /* River */
+ --river-10: oklch(93.35% 0.03169 273.4deg);
+ --river-20: oklch(86.91% 0.04221 273.1deg);
+ --river-30: oklch(80.46% 0.05274 272.7deg);
+ --river-40: oklch(74.01% 0.06326 272.4deg);
+ --river-50: oklch(67.57% 0.07378 272.0deg);
+ --river-60: oklch(61.12% 0.08430 271.7deg);
+ --river-70: oklch(54.67% 0.09483 271.4deg);
+ --river-80: oklch(48.22% 0.1053 271.0deg);
+ --river-90: oklch(41.78% 0.1159 270.7deg);
+ --river-100: oklch(35.33% 0.1264 270.4deg);
+ --river-110: oklch(28.88% 0.1369 270.0deg);
+ --river-120: oklch(22.44% 0.1474 269.7deg);
+
+ /* Berry */
+ --berry-10: oklch(93.77% 0.05212 329.0deg);
+ --berry-20: oklch(87.30% 0.05912 325.3deg);
+ --berry-30: oklch(80.82% 0.06612 321.6deg);
+ --berry-40: oklch(74.34% 0.07313 317.8deg);
+ --berry-50: oklch(67.86% 0.08013 314.1deg);
+ --berry-60: oklch(61.39% 0.08713 310.3deg);
+ --berry-70: oklch(54.91% 0.09413 306.6deg);
+ --berry-80: oklch(48.43% 0.1011 302.8deg);
+ --berry-90: oklch(41.95% 0.1081 299.1deg);
+ --berry-100: oklch(35.47% 0.1151 295.4deg);
+ --berry-110: oklch(29.00% 0.1221 291.6deg);
+ --berry-120: oklch(22.52% 0.1291 287.9deg);
+
+ /* Destroy */
+ --destroy-10: oklch(88.21% 0.06281 14.85deg);
+ --destroy-20: oklch(83.23% 0.08511 16.91deg);
+ --destroy-30: oklch(78.25% 0.1074 18.96deg);
+ --destroy-40: oklch(73.27% 0.1297 21.01deg);
+ --destroy-50: oklch(68.29% 0.1520 23.07deg);
+ --destroy-60: oklch(63.31% 0.1743 25.12deg);
+ --destroy-70: oklch(58.33% 0.1966 27.18deg);
+ --destroy-80: oklch(53.35% 0.2189 29.23deg);
+
+ /* Warn */
+ --warn-10: oklch(90.19% 0.1361 92.00deg);
+ --warn-20: oklch(84.60% 0.1388 84.84deg);
+ --warn-30: oklch(79.01% 0.1414 77.68deg);
+ --warn-40: oklch(73.42% 0.1440 70.52deg);
+ --warn-50: oklch(67.83% 0.1466 63.36deg);
+ --warn-60: oklch(62.24% 0.1492 56.20deg);
+ --warn-70: oklch(56.65% 0.1518 49.04deg);
+ --warn-80: oklch(51.06% 0.1544 41.88deg);
+
+ /* Succeed */
+ --succeed-10: oklch(89.00% 0.1600 143.4deg);
+ --succeed-20: oklch(83.23% 0.1608 143.3deg);
+ --succeed-30: oklch(77.46% 0.1616 143.1deg);
+ --succeed-40: oklch(71.69% 0.1623 143.0deg);
+ --succeed-50: oklch(65.92% 0.1631 142.9deg);
+ --succeed-60: oklch(60.16% 0.1639 142.8deg);
+ --succeed-70: oklch(54.39% 0.1647 142.6deg);
+ --succeed-80: oklch(48.62% 0.1654 142.5deg);
+
+ /* Base values for use with Tailwind. */
+ /* Chalkboard */
+ --_chalkboard-10: 99.70% 0.008766 102.8deg;
+ --_chalkboard-20: 91.34% 0.009353 109.0deg;
+ --_chalkboard-30: 82.99% 0.009940 115.2deg;
+ --_chalkboard-40: 74.63% 0.01053 121.4deg;
+ --_chalkboard-50: 66.27% 0.01111 127.6deg;
+ --_chalkboard-60: 57.92% 0.01170 133.9deg;
+ --_chalkboard-70: 49.56% 0.01229 140.1deg;
+ --_chalkboard-80: 41.21% 0.01288 146.3deg;
+ --_chalkboard-90: 32.85% 0.01346 152.5deg;
+ --_chalkboard-100: 24.49% 0.01405 158.7deg;
+ --_chalkboard-110: 16.14% 0.01464 164.9deg;
+ --_chalkboard-120: 7.783% 0.01522 171.1deg;
+
+ /* Energy */
+ --_energy-10: 93.31% 0.2270 122.3deg;
+ --_energy-20: 86.01% 0.2092 123.6deg;
+ --_energy-30: 78.71% 0.1914 125.0deg;
+ --_energy-40: 71.41% 0.1736 126.3deg;
+ --_energy-50: 64.10% 0.1557 127.7deg;
+ --_energy-60: 56.80% 0.1379 129.1deg;
+ --_energy-70: 49.50% 0.1201 130.4deg;
+ --_energy-80: 42.20% 0.1023 131.8deg;
+ --_energy-90: 34.90% 0.08446 133.1deg;
+ --_energy-100: 27.60% 0.06664 134.5deg;
+ --_energy-110: 20.30% 0.04882 135.8deg;
+ --_energy-120: 13.00% 0.03100 137.2deg;
+
+ /* Liquid */
+ --_liquid-10: 93.45% 0.1002 193.1deg;
+ --_liquid-20: 86.21% 0.09511 198.7deg;
+ --_liquid-30: 78.97% 0.09003 204.2deg;
+ --_liquid-40: 71.74% 0.08495 209.8deg;
+ --_liquid-50: 64.50% 0.07988 215.3deg;
+ --_liquid-60: 57.26% 0.07480 220.9deg;
+ --_liquid-70: 50.03% 0.06972 226.4deg;
+ --_liquid-80: 42.79% 0.06465 232.0deg;
+ --_liquid-90: 35.56% 0.05957 237.5deg;
+ --_liquid-100: 28.32% 0.05450 243.1deg;
+ --_liquid-110: 21.08% 0.04942 248.6deg;
+ --_liquid-120: 13.85% 0.04434 254.2deg;
+
+ /* Fern */
+ --_fern-10: 93.22% 0.1243 144.8deg;
+ --_fern-20: 86.59% 0.1193 144.6deg;
+ --_fern-30: 79.97% 0.1143 144.4deg;
+ --_fern-40: 73.34% 0.1093 144.2deg;
+ --_fern-50: 66.71% 0.1043 144.0deg;
+ --_fern-60: 60.09% 0.09927 143.8deg;
+ --_fern-70: 53.46% 0.09425 143.6deg;
+ --_fern-80: 46.83% 0.08924 143.3deg;
+ --_fern-90: 40.21% 0.08422 143.1deg;
+ --_fern-100: 33.58% 0.07920 142.9deg;
+ --_fern-110: 26.95% 0.07419 142.7deg;
+ --_fern-120: 20.33% 0.06917 142.5deg;
+
+ /* Cool */
+ --_cool-10: 97.71% 0.03321 196.6deg;
+ --_cool-20: 90.82% 0.03783 203.8deg;
+ --_cool-30: 83.94% 0.04245 211.0deg;
+ --_cool-40: 77.06% 0.04706 218.1deg;
+ --_cool-50: 70.18% 0.05168 225.3deg;
+ --_cool-60: 63.29% 0.05630 232.5deg;
+ --_cool-70: 56.41% 0.06091 239.6deg;
+ --_cool-80: 49.53% 0.06553 246.8deg;
+ --_cool-90: 42.65% 0.07015 254.0deg;
+ --_cool-100: 35.76% 0.07477 261.2deg;
+ --_cool-110: 28.88% 0.07938 268.3deg;
+ --_cool-120: 22.00% 0.08400 275.5deg;
+
+ /* River */
+ --_river-10: 93.35% 0.03169 273.4deg;
+ --_river-20: 86.91% 0.04221 273.1deg;
+ --_river-30: 80.46% 0.05274 272.7deg;
+ --_river-40: 74.01% 0.06326 272.4deg;
+ --_river-50: 67.57% 0.07378 272.0deg;
+ --_river-60: 61.12% 0.08430 271.7deg;
+ --_river-70: 54.67% 0.09483 271.4deg;
+ --_river-80: 48.22% 0.1053 271.0deg;
+ --_river-90: 41.78% 0.1159 270.7deg;
+ --_river-100: 35.33% 0.1264 270.4deg;
+ --_river-110: 28.88% 0.1369 270.0deg;
+ --_river-120: 22.44% 0.1474 269.7deg;
+
+ /* Berry */
+ --_berry-10: 93.77% 0.05212 329.0deg;
+ --_berry-20: 87.30% 0.05912 325.3deg;
+ --_berry-30: 80.82% 0.06612 321.6deg;
+ --_berry-40: 74.34% 0.07313 317.8deg;
+ --_berry-50: 67.86% 0.08013 314.1deg;
+ --_berry-60: 61.39% 0.08713 310.3deg;
+ --_berry-70: 54.91% 0.09413 306.6deg;
+ --_berry-80: 48.43% 0.1011 302.8deg;
+ --_berry-90: 41.95% 0.1081 299.1deg;
+ --_berry-100: 35.47% 0.1151 295.4deg;
+ --_berry-110: 29.00% 0.1221 291.6deg;
+ --_berry-120: 22.52% 0.1291 287.9deg;
+
+ /* Destroy */
+ --_destroy-10: 88.21% 0.06281 14.85deg;
+ --_destroy-20: 83.23% 0.08511 16.91deg;
+ --_destroy-30: 78.25% 0.1074 18.96deg;
+ --_destroy-40: 73.27% 0.1297 21.01deg;
+ --_destroy-50: 68.29% 0.1520 23.07deg;
+ --_destroy-60: 63.31% 0.1743 25.12deg;
+ --_destroy-70: 58.33% 0.1966 27.18deg;
+ --_destroy-80: 53.35% 0.2189 29.23deg;
+
+ /* Warn */
+ --_warn-10: 90.19% 0.1361 92.00deg;
+ --_warn-20: 84.60% 0.1388 84.84deg;
+ --_warn-30: 79.01% 0.1414 77.68deg;
+ --_warn-40: 73.42% 0.1440 70.52deg;
+ --_warn-50: 67.83% 0.1466 63.36deg;
+ --_warn-60: 62.24% 0.1492 56.20deg;
+ --_warn-70: 56.65% 0.1518 49.04deg;
+ --_warn-80: 51.06% 0.1544 41.88deg;
+
+ /* Succeed */
+ --_succeed-10: 89.00% 0.1600 143.4deg;
+ --_succeed-20: 83.23% 0.1608 143.3deg;
+ --_succeed-30: 77.46% 0.1616 143.1deg;
+ --_succeed-40: 71.69% 0.1623 143.0deg;
+ --_succeed-50: 65.92% 0.1631 142.9deg;
+ --_succeed-60: 60.16% 0.1639 142.8deg;
+ --_succeed-70: 54.39% 0.1647 142.6deg;
+ --_succeed-80: 48.62% 0.1654 142.5deg;
+}
\ No newline at end of file
diff --git a/src/components/ActionButton.tsx b/src/components/ActionButton.tsx
new file mode 100644
index 000000000..09aff5bf3
--- /dev/null
+++ b/src/components/ActionButton.tsx
@@ -0,0 +1,35 @@
+import { Link } from 'react-router-dom'
+import { ActionIcon, ActionIconProps } from './ActionIcon'
+
+interface ActionButtonProps extends React.PropsWithChildren {
+ icon?: ActionIconProps
+ className?: string
+ onClick?: () => void
+ to?: string
+ as?: 'button' | 'link'
+}
+
+export const ActionButton = ({
+ icon,
+ className,
+ onClick,
+ to = '/',
+ as = 'button',
+ children,
+}: ActionButtonProps) => {
+ const classNames = `group mono flex items-center gap-2 text-chalkboard-110 rounded-sm border border-chalkboard-40 hover:border-liquid-40 p-[3px] ${
+ icon ? 'pr-2' : 'px-2'
+ } ${className}`
+
+ return as === 'button' ? (
+
+ {icon && }
+ {children}
+
+ ) : (
+
+ {icon &&
}
+ {children}
+
+ )
+}
diff --git a/src/components/ActionIcon.tsx b/src/components/ActionIcon.tsx
new file mode 100644
index 000000000..b6fdfb69e
--- /dev/null
+++ b/src/components/ActionIcon.tsx
@@ -0,0 +1,48 @@
+import {
+ IconDefinition,
+ faCircleExclamation,
+} from '@fortawesome/free-solid-svg-icons'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+
+const iconSizes = {
+ sm: 12,
+ md: 14.4,
+ lg: 18,
+}
+
+export interface ActionIconProps extends React.PropsWithChildren {
+ icon?: IconDefinition
+ bgClassName?: string
+ iconClassName?: string
+ size?: keyof typeof iconSizes
+}
+
+export const ActionIcon = ({
+ icon,
+ bgClassName,
+ iconClassName,
+ size = 'md',
+ children,
+}: ActionIconProps) => {
+ return (
+
+ {children || (
+
+ )}
+
+ )
+}
diff --git a/src/components/AppHeader.tsx b/src/components/AppHeader.tsx
new file mode 100644
index 000000000..efd529529
--- /dev/null
+++ b/src/components/AppHeader.tsx
@@ -0,0 +1,37 @@
+import { faGear } from '@fortawesome/free-solid-svg-icons'
+import { Toolbar } from '../Toolbar'
+import { ActionButton } from './ActionButton'
+
+interface AppHeaderProps extends React.PropsWithChildren {
+ showToolbar?: boolean
+}
+
+export const AppHeader = ({ showToolbar = true, children }: AppHeaderProps) => {
+ return (
+
+
+
+ KittyCAD App
+
+ {/* Toolbar if the context deems it */}
+ {showToolbar && (
+
+
+
+ )}
+ {/* If there are children, show them, otherwise... */}
+ {children || (
+ // TODO: If signed out, show the token paste field
+
+ // If signed in, show the account avatar
+
+ Settings
+
+ )}
+
+ )
+}
diff --git a/src/components/ErrorPage.tsx b/src/components/ErrorPage.tsx
new file mode 100644
index 000000000..83564a87b
--- /dev/null
+++ b/src/components/ErrorPage.tsx
@@ -0,0 +1,8 @@
+export const ErrorPage = () => {
+ return (
+
+ )
+}
diff --git a/src/index.css b/src/index.css
index 2f1e8afda..557a8ff1e 100644
--- a/src/index.css
+++ b/src/index.css
@@ -3,6 +3,7 @@
@tailwind utilities;
@import '../node_modules/allotment/dist/style.css';
+@import './colors.css';
body {
margin: 0;
@@ -13,6 +14,11 @@ body {
-moz-osx-font-smoothing: grayscale;
}
+.mono {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
+
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
diff --git a/src/index.tsx b/src/index.tsx
index 128716319..b5a7d8c7e 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -2,11 +2,13 @@ import ReactDOM from 'react-dom/client'
import './index.css'
import { Auth } from './Auth'
import reportWebVitals from './reportWebVitals'
+import { Toaster } from 'react-hot-toast'
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
-root.render(
+root.render(<>
-)
+
+>)
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
diff --git a/src/routes/Settings.tsx b/src/routes/Settings.tsx
new file mode 100644
index 000000000..1bfd38e73
--- /dev/null
+++ b/src/routes/Settings.tsx
@@ -0,0 +1,143 @@
+import { faCheck, faFolder, faXmark } from '@fortawesome/free-solid-svg-icons'
+import { ActionButton } from '../components/ActionButton'
+import { AppHeader } from '../components/AppHeader'
+import { open } from '@tauri-apps/api/dialog'
+import { useStore } from '../useStore'
+import { useState } from 'react'
+import { toast } from 'react-hot-toast'
+
+export const Settings = () => {
+ const {
+ defaultDir: originalDefaultDir,
+ setDefaultDir: saveDefaultDir,
+ defaultProjectName: originalDefaultProjectName,
+ setDefaultProjectName: saveDefaultProjectName,
+ } = useStore((s) => ({
+ defaultDir: s.defaultDir,
+ setDefaultDir: s.setDefaultDir,
+ defaultProjectName: s.defaultProjectName,
+ setDefaultProjectName: s.setDefaultProjectName,
+ }))
+ const [defaultDir, setDefaultDir] = useState(originalDefaultDir)
+ const [defaultProjectName, setDefaultProjectName] = useState(
+ originalDefaultProjectName
+ )
+
+ async function handleDirectorySelection() {
+ const newDirectory = await open({
+ directory: true,
+ defaultPath: (defaultDir.base || '') + (defaultDir.dir || '/'),
+ title: 'Choose a new default directory',
+ })
+
+ if (newDirectory && newDirectory !== null && !Array.isArray(newDirectory)) {
+ setDefaultDir({ base: defaultDir.base, dir: newDirectory })
+ }
+ }
+
+ const handleSaveClick = () => {
+ saveDefaultDir(defaultDir)
+ saveDefaultProjectName(defaultProjectName)
+ toast.success('Settings saved!')
+ }
+
+ return (
+ <>
+
+
+ Close
+
+
+
+
User Settings
+ {(window as any).__TAURI__ && (
+
+
+
+ setDefaultDir({
+ base: originalDefaultDir.base,
+ dir: e.target.value,
+ })
+ }
+ />
+
+ Choose a folder
+
+
+
+ )}
+
+ setDefaultProjectName(e.target.value)}
+ />
+
+
+ Save Settings
+
+
+ >
+ )
+}
+
+interface SettingsSectionProps extends React.PropsWithChildren {
+ title: string
+ description?: string
+}
+
+function SettingsSection({
+ title,
+ description,
+ children,
+}: SettingsSectionProps) {
+ return (
+
+
+
{title}
+
{description}
+
+ {children}
+
+ )
+}
diff --git a/src/useStore.ts b/src/useStore.ts
index 1feb75abc..192f34ab7 100644
--- a/src/useStore.ts
+++ b/src/useStore.ts
@@ -155,6 +155,8 @@ export interface StoreState {
// tauri specific app settings
defaultDir: DefaultDir
setDefaultDir: (dir: DefaultDir) => void
+ defaultProjectName: string
+ setDefaultProjectName: (defaultProjectName: string) => void
showHomeMenu: boolean
setHomeShowMenu: (showMenu: boolean) => void
homeMenuItems: {
@@ -310,9 +312,11 @@ export const useStore = create
()(
// tauri specific app settings
defaultDir: {
- dir: '',
+ dir: '~/Documents/',
},
setDefaultDir: (dir) => set({ defaultDir: dir }),
+ defaultProjectName: 'new-project-$n',
+ setDefaultProjectName: (defaultProjectName) => set({ defaultProjectName }),
showHomeMenu: true,
setHomeShowMenu: (showHomeMenu) => set({ showHomeMenu }),
homeMenuItems: [],
@@ -324,7 +328,12 @@ export const useStore = create()(
name: 'store',
partialize: (state) =>
Object.fromEntries(
- Object.entries(state).filter(([key]) => ['code', 'defaultDir', 'token'].includes(key))
+ Object.entries(state).filter(([key]) => [
+ 'code',
+ 'defaultDir',
+ 'defaultProjectName',
+ 'token',
+ ].includes(key))
),
}
)
diff --git a/tailwind.config.js b/tailwind.config.js
index 1490793a2..814c69f32 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -1,10 +1,42 @@
+const themeColorRamps = [
+ { name: 'chalkboard', stops: 12 },
+ { name: 'energy', stops: 12 },
+ { name: 'liquid', stops: 12 },
+ { name: 'fern', stops: 12 },
+ { name: 'cool', stops: 12 },
+ { name: 'river', stops: 12 },
+ { name: 'berry', stops: 12 },
+ { name: 'destroy', stops: 8 },
+ { name: 'warn', stops: 8 },
+ { name: 'succeed', stops: 8 },
+]
+const toOKLCHVar = val => `oklch(var(${val}) / ) `
+
+const themeColors = Object.fromEntries(
+ themeColorRamps.map(({name, stops}) => [
+ name,
+ Object.fromEntries(
+ new Array(stops)
+ .fill(0)
+ .map((_, i) => [
+ (i + 1) * 10,
+ toOKLCHVar(`--_${name}-${(i + 1) * 10}`),
+ ])
+ ),
+ ])
+)
+
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
- extend: {},
+ extend: {
+ colors: {
+ ...themeColors,
+ },
+ },
},
plugins: [],
}
diff --git a/yarn.lock b/yarn.lock
index a6e500615..824ec44b2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1539,6 +1539,32 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
+"@fortawesome/fontawesome-common-types@6.4.0":
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz#88da2b70d6ca18aaa6ed3687832e11f39e80624b"
+ integrity sha512-HNii132xfomg5QVZw0HwXXpN22s7VBHQBv9CeOu9tfJnhsWQNd2lmTNi8CSrnw5B+5YOmzu1UoPAyxaXsJ6RgQ==
+
+"@fortawesome/fontawesome-svg-core@^6.4.0":
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.0.tgz#3727552eff9179506e9203d72feb5b1063c11a21"
+ integrity sha512-Bertv8xOiVELz5raB2FlXDPKt+m94MQ3JgDfsVbrqNpLU9+UE2E18GKjLKw+d3XbeYPqg1pzyQKGsrzbw+pPaw==
+ dependencies:
+ "@fortawesome/fontawesome-common-types" "6.4.0"
+
+"@fortawesome/free-solid-svg-icons@^6.4.0":
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.0.tgz#48c0e790847fa56299e2f26b82b39663b8ad7119"
+ integrity sha512-kutPeRGWm8V5dltFP1zGjQOEAzaLZj4StdQhWVZnfGFCvAPVvHh8qk5bRrU4KXnRRRNni5tKQI9PBAdI6MP8nQ==
+ dependencies:
+ "@fortawesome/fontawesome-common-types" "6.4.0"
+
+"@fortawesome/react-fontawesome@^0.2.0":
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz#d90dd8a9211830b4e3c08e94b63a0ba7291ddcf4"
+ integrity sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==
+ dependencies:
+ prop-types "^15.8.1"
+
"@headlessui/react@^1.7.13":
version "1.7.13"
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.13.tgz#fd150b394954e9f1d86ed2340cffd1217d6e7628"
@@ -1958,6 +1984,11 @@
schema-utils "^3.0.0"
source-map "^0.7.3"
+"@remix-run/router@1.7.1":
+ version "1.7.1"
+ resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.1.tgz#fea7ac35ae4014637c130011f59428f618730498"
+ integrity sha512-bgVQM4ZJ2u2CM8k1ey70o1ePFXsEzYVZoWghh6WjM8p59jQ7HxzbHW4SbnWFG7V9ig9chLawQxDTZ3xzOF8MkQ==
+
"@rollup/plugin-babel@^5.2.0":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
@@ -5568,6 +5599,11 @@ globby@^11.0.4, globby@^11.1.0:
merge2 "^1.4.1"
slash "^3.0.0"
+goober@^2.1.10:
+ version "2.1.13"
+ resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.13.tgz#e3c06d5578486212a76c9eba860cbc3232ff6d7c"
+ integrity sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==
+
gopd@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
@@ -8802,6 +8838,13 @@ react-error-overlay@^6.0.11:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"
integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
+react-hot-toast@^2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.4.1.tgz#df04295eda8a7b12c4f968e54a61c8d36f4c0994"
+ integrity sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==
+ dependencies:
+ goober "^2.1.10"
+
react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -8842,6 +8885,21 @@ react-refresh@^0.11.0:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
+react-router-dom@^6.14.1:
+ version "6.14.1"
+ resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.14.1.tgz#0ad7ba7abdf75baa61169d49f096f0494907a36f"
+ integrity sha512-ssF6M5UkQjHK70fgukCJyjlda0Dgono2QGwqGvuk7D+EDGHdacEN3Yke2LTMjkrpHuFwBfDFsEjGVXBDmL+bWw==
+ dependencies:
+ "@remix-run/router" "1.7.1"
+ react-router "6.14.1"
+
+react-router@6.14.1:
+ version "6.14.1"
+ resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.1.tgz#5e82bcdabf21add859dc04b1859f91066b3a5810"
+ integrity sha512-U4PfgvG55LdvbQjg5Y9QRWyVxIdO1LlpYT7x+tMAxd9/vmiPuJhIwdxZuIQLN/9e3O4KFDHYfR9gzGeYMasW8g==
+ dependencies:
+ "@remix-run/router" "1.7.1"
+
react-scripts@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-5.0.1.tgz#6285dbd65a8ba6e49ca8d651ce30645a6d980003"