update
This commit is contained in:
parent
84ebac763f
commit
069a35e959
11 changed files with 81 additions and 161 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -22,3 +22,5 @@ dist-ssr
|
|||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
51
README.md
51
README.md
|
@ -1,50 +1,3 @@
|
|||
# React + TypeScript + Vite
|
||||
# cubeviz
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||
|
||||
- Configure the top-level `parserOptions` property like this:
|
||||
|
||||
```js
|
||||
export default tseslint.config({
|
||||
languageOptions: {
|
||||
// other options...
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
|
||||
- Optionally add `...tseslint.configs.stylisticTypeChecked`
|
||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import react from 'eslint-plugin-react'
|
||||
|
||||
export default tseslint.config({
|
||||
// Set the react version
|
||||
settings: { react: { version: '18.3' } },
|
||||
plugins: {
|
||||
// Add the react plugin
|
||||
react,
|
||||
},
|
||||
rules: {
|
||||
// other rules...
|
||||
// Enable its recommended rules
|
||||
...react.configs.recommended.rules,
|
||||
...react.configs['jsx-runtime'].rules,
|
||||
},
|
||||
})
|
||||
```
|
||||
Cubical type theory visualizer.
|
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -13,6 +13,7 @@
|
|||
"@react-three/drei": "^9.114.0",
|
||||
"@react-three/fiber": "^8.17.8",
|
||||
"@types/three": "^0.169.0",
|
||||
"jotai": "^2.10.0",
|
||||
"meshline": "^3.3.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
|
|
42
src/App.css
42
src/App.css
|
@ -1,42 +0,0 @@
|
|||
#root {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.react:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
a:nth-of-type(2) .logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
68
src/App.tsx
68
src/App.tsx
|
@ -1,63 +1,15 @@
|
|||
import { ReactNode, useCallback, useRef, useState } from "react";
|
||||
import { Canvas, extend, useFrame } from "@react-three/fiber";
|
||||
import { OrbitControls, OrthographicCamera } from "@react-three/drei";
|
||||
import { BoxGeometry, EdgesGeometry, LineBasicMaterial } from "three";
|
||||
import { InlineMath, BlockMath } from "react-katex";
|
||||
import { createPortal } from "react-dom";
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import { OrbitControls } from "@react-three/drei";
|
||||
|
||||
import "./App.css";
|
||||
import styles from "./styles.module.scss";
|
||||
import "katex/dist/katex.min.css";
|
||||
import Point from "./components/Point";
|
||||
import Path from "./components/Path";
|
||||
import { SettingsBox } from "./SettingsContext";
|
||||
|
||||
// https://threejs.org/manual/#en/align-html-elements-to-3d
|
||||
|
||||
function createBox({ dimensions, ...props }) {
|
||||
// This reference gives us direct access to the THREE.Mesh object
|
||||
const ref = useRef();
|
||||
// Hold state for hovered and clicked events
|
||||
const [hovered, hover] = useState(false);
|
||||
const [clicked, click] = useState(false);
|
||||
|
||||
const box = new BoxGeometry(1, 1, 1);
|
||||
const edges = new EdgesGeometry(box);
|
||||
|
||||
const label = (
|
||||
<div>
|
||||
helloge {JSON.stringify(clicked)}
|
||||
<InlineMath math="f(x)" />
|
||||
</div>
|
||||
);
|
||||
|
||||
// Return the view, these are regular Threejs elements expressed in JSX
|
||||
const view = (
|
||||
<mesh
|
||||
{...props}
|
||||
ref={ref}
|
||||
onClick={(event) => click(!clicked)}
|
||||
onPointerOver={(event) => hover(true)}
|
||||
onPointerOut={(event) => hover(false)}
|
||||
>
|
||||
{/* <boxGeometry args={[1, 1, 1]} /> */}
|
||||
{/* <edgesGeometry args={[box]} /> */}
|
||||
<lineSegments
|
||||
args={[edges, new LineBasicMaterial({ color: "darkgray" })]}
|
||||
/>
|
||||
<meshStandardMaterial color={hovered ? "hotpink" : "orange"} />
|
||||
</mesh>
|
||||
);
|
||||
|
||||
return [view, label];
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [labelContainerEl, setLabelContainerEl] =
|
||||
useState<HTMLDivElement | null>(null);
|
||||
const labelContainerRef = useCallback((el) => {
|
||||
if (el) setLabelContainerEl(el);
|
||||
}, []);
|
||||
|
||||
let coords: [number, number, number][] = [
|
||||
[0, 0, 0],
|
||||
[0, 0, 1],
|
||||
|
@ -84,7 +36,8 @@ function App() {
|
|||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.container}>
|
||||
<SettingsBox />
|
||||
<Canvas className={styles.canvas}>
|
||||
<OrbitControls />
|
||||
<ambientLight intensity={Math.PI / 2} />
|
||||
|
@ -105,7 +58,16 @@ function App() {
|
|||
<Path key={JSON.stringify([start, end])} start={start} end={end} />
|
||||
))}
|
||||
</Canvas>
|
||||
</>
|
||||
<div className={styles.footer}>
|
||||
<a
|
||||
href="https://git.sr.ht/~mzhang/cubeviz"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
Source
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
20
src/SettingsContext.tsx
Normal file
20
src/SettingsContext.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { atom, useAtom } from "jotai";
|
||||
import styles from "./styles.module.scss";
|
||||
|
||||
export const showEmptyAtom = atom(true);
|
||||
|
||||
export function SettingsBox() {
|
||||
const [showEmpty, setShowEmpty] = useAtom(showEmptyAtom);
|
||||
|
||||
return (
|
||||
<div className={styles.header}>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="showEmptyCheckbox"
|
||||
checked={showEmpty}
|
||||
onChange={() => setShowEmpty((v) => !v)}
|
||||
/>
|
||||
<label htmlFor="showEmptyCheckbox">Show empty?</label>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,20 +1,23 @@
|
|||
import { useCallback, useState } from "react";
|
||||
import { InlineMath } from "react-katex";
|
||||
import { type FormEvent, useCallback, useState } from "react";
|
||||
import { BlockMath } from "react-katex";
|
||||
|
||||
import styles from "./EditBox.module.scss";
|
||||
import { showEmptyAtom } from "../SettingsContext";
|
||||
import { useAtomValue } from "jotai";
|
||||
|
||||
export interface EditBoxProps {}
|
||||
// export interface EditBoxProps {}
|
||||
|
||||
export default function EditBox({}: EditBoxProps) {
|
||||
export default function EditBox() {
|
||||
const [value, setValue] = useState("");
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const showEmpty = useAtomValue(showEmptyAtom);
|
||||
|
||||
const handleDblClick = useCallback(() => {
|
||||
if (!isEditing) setIsEditing(true);
|
||||
}, [isEditing]);
|
||||
|
||||
const done = useCallback(
|
||||
(evt) => {
|
||||
(evt: FormEvent) => {
|
||||
evt.preventDefault();
|
||||
if (isEditing) setIsEditing(false);
|
||||
},
|
||||
|
@ -26,15 +29,18 @@ export default function EditBox({}: EditBoxProps) {
|
|||
{isEditing ? (
|
||||
<form onSubmit={done}>
|
||||
<input
|
||||
// biome-ignore lint/a11y/noAutofocus: <explanation>
|
||||
autoFocus={true}
|
||||
onBlur={done}
|
||||
value={value}
|
||||
onChange={(evt) => setValue(evt.target.value)}
|
||||
placeholder="Type latex code..."
|
||||
/>
|
||||
</form>
|
||||
) : value === "" ? (
|
||||
<span className={styles.empty}>(empty)</span>
|
||||
<span className={styles.empty}>{showEmpty ? <>(empty)</> : ""}</span>
|
||||
) : (
|
||||
<InlineMath math={value} />
|
||||
<BlockMath math={value} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { extend, useFrame } from "@react-three/fiber";
|
||||
import { extend } from "@react-three/fiber";
|
||||
import { Html, Line } from "@react-three/drei";
|
||||
import { MeshLineGeometry, MeshLineMaterial, raycast } from "meshline";
|
||||
import { MeshLineGeometry, MeshLineMaterial } from "meshline";
|
||||
import EditBox from "./EditBox";
|
||||
|
||||
extend({ MeshLineGeometry, MeshLineMaterial });
|
||||
|
|
|
@ -1,11 +1,28 @@
|
|||
.canvas {
|
||||
.container {
|
||||
display: grid;
|
||||
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.labels {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
.canvas {
|
||||
grid-area: 1 / 1;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
grid-area: 1 / 1;
|
||||
place-self: start center;
|
||||
z-index: 50;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
grid-area: 1 / 1;
|
||||
place-self: end end;
|
||||
z-index: 50;
|
||||
padding: 10px;
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
base: "/cubeviz",
|
||||
plugins: [react()],
|
||||
})
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue