summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/NodeEditor.tsx32
-rw-r--r--src/components/ArrowButton.tsx4
-rw-r--r--src/components/Button.tsx2
-rw-r--r--src/components/Header.tsx4
-rw-r--r--src/components/TextInput.tsx4
-rw-r--r--src/components/index.ts35
-rw-r--r--src/components/nodes/Input.tsx5
-rw-r--r--src/components/nodes/Socket.tsx2
-rw-r--r--src/components/nodes/index.ts9
-rw-r--r--src/context.ts4
-rw-r--r--src/index.tsx5
-rw-r--r--src/node.ts1
-rw-r--r--src/nodes/Fourier.tsx2
-rw-r--r--src/nodes/Intersperse.tsx8
-rw-r--r--src/nodes/Linspace.tsx2
-rw-r--r--src/nodes/Plot.tsx4
-rw-r--r--src/nodes/Unzip.tsx10
-rw-r--r--src/nodes/Viewer.tsx2
-rw-r--r--src/nodes/index.ts4
-rw-r--r--src/pages/LogIn.tsx4
-rw-r--r--src/pages/ProjectsList.tsx22
-rw-r--r--src/pages/SignUp.tsx4
-rw-r--r--src/types.ts18
-rw-r--r--src/wasm.ts4
24 files changed, 123 insertions, 68 deletions
diff --git a/src/NodeEditor.tsx b/src/NodeEditor.tsx
index a9e7c83..781e64b 100644
--- a/src/NodeEditor.tsx
+++ b/src/NodeEditor.tsx
@@ -1,16 +1,25 @@
import { useContext, useEffect, useMemo, useCallback, useRef } from 'preact/hooks';
import { signal, computed, batch, useSignal, useComputed, Signal } from '@preact/signals';
import { Pb } from './context.ts';
-import { SocketHandlers } from './node.ts';
+import { NodeComponent, SocketHandlers } from './node.ts';
import { nodeRegistry } from './nodes';
-import type { SocketHandler, NodeInfo } from './node.tsx';
+import type { SocketHandler, NodeInfo } from './node.ts';
import { InputSocket } from './dataflow.ts';
import { Toolbar, ButtonMenu, MenuItem } from './components';
import './NodeEditor.css';
-export const nodeFactory = () => {
+interface NodeInstance {
+ id: number;
+ component: NodeComponent<any>;
+ x: Signal<number>;
+ y: Signal<number>;
+ inputs: Record<string, InputSocket<any>>;
+ outputs: Record<string, Signal<any>>;
+}
+
+const nodeFactory = () => {
let nextNodeId = 0;
- return (x: number, y: number, { component, func, inputs }: NodeInfo<any, any>) => {
+ return (x: number, y: number, { component, func, inputs }: NodeInfo<any, any>): NodeInstance => {
const mapEntries = (obj: {}, f: (x: [string, any]) => [string, any]) => (
Object.fromEntries(Object.entries(obj).map(f))
);
@@ -54,17 +63,22 @@ interface LinkData extends LinkProps {
to: { nodeId: number, socket: string };
}
-const NodeEditor = ({ user, project }) => {
- const pb = useContext(Pb);
+export interface NodeEditorProps {
+ user: string;
+ project: string;
+}
+
+const NodeEditor = ({ user, project }: NodeEditorProps) => {
+ const pb = useContext(Pb)!;
const offsetX = useSignal(0);
const offsetY = useSignal(0);
const scale = useSignal(1);
const instantiateNode = useMemo(nodeFactory, []);
- const svgRef = useRef(null);
+ const svgRef = useRef<SVGSVGElement | null>(null);
- const nodes = useSignal([]);
+ const nodes = useSignal<NodeInstance[]>([]);
const currentLink = useSignal<null | Omit<LinkData, 'to'>>(null);
const links = useSignal<LinkData[]>([]);
@@ -225,7 +239,7 @@ const NodeEditor = ({ user, project }) => {
scale.value *= 1 + delta;
}), []);
- const addNode = useCallback((node: NodeInfo) => {
+ const addNode = useCallback((node: NodeInfo<any, any>) => {
nodes.value = nodes.value.concat(instantiateNode(100, 100, node));
}, []);
diff --git a/src/components/ArrowButton.tsx b/src/components/ArrowButton.tsx
index ce0c898..a95964e 100644
--- a/src/components/ArrowButton.tsx
+++ b/src/components/ArrowButton.tsx
@@ -1,7 +1,9 @@
import Button, { ButtonProps } from './Button.tsx';
import './ArrowButton.css';
-const ArrowButton = ({ children, ...props }: ButtonProps) => {
+export type ArrowButtonProps = ButtonProps;
+
+const ArrowButton = ({ children, ...props }: ArrowButtonProps) => {
return (
<Button {...props} class={(props.class || '') + ' __ArrowButton'}>
{children}
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 60f42c7..8f094ce 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -4,7 +4,7 @@ import './Button.css';
export interface ButtonProps {
children: ComponentChildren;
kind?: 'primary' | 'outline' | 'ghost';
- props: Record<string, any>;
+ [prop: string]: any;
}
const Button = ({ children, kind = 'primary', ...props }: ButtonProps) => {
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index 95d5fef..a3b344d 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -5,13 +5,13 @@ import './Header.css';
export interface HeaderProps {
children: ComponentChildren;
class?: string;
- title: string;
+ title?: string;
}
const Header = ({ children, title, ...props }: HeaderProps) => {
return (
<header class={(props.class || '') + ' __Header'}>
- {title && <Button kind="ghost" class="title" href="/">{title}</Button>}
+ {!!title && <Button kind="ghost" class="title" href="/">{title}</Button>}
<nav>
{children}
</nav>
diff --git a/src/components/TextInput.tsx b/src/components/TextInput.tsx
index 026b062..c74828d 100644
--- a/src/components/TextInput.tsx
+++ b/src/components/TextInput.tsx
@@ -4,12 +4,12 @@ import './TextInput.css';
export interface TextInputProps {
signal?: Signal<string>;
- props: Record<string, any>;
+ [prop: string]: any;
}
const TextInput = ({ signal, ...props }: TextInputProps) => {
const onInputSignal = useCallback((event: InputEvent) => {
- signal.value = event.target.value;
+ if (signal) signal.value = (event.target as HTMLInputElement).value;
}, [signal]);
return (
diff --git a/src/components/index.ts b/src/components/index.ts
index c9c3625..badd32e 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -1,12 +1,25 @@
-export { default as ArrowButton } from './ArrowButton.tsx';
-export { default as Button } from './Button.tsx';
-export { default as ButtonMenu } from './ButtonMenu.tsx';
+export { default as ArrowButton } from './ArrowButton.tsx';
+export { default as Button } from './Button.tsx';
+export { default as ButtonMenu } from './ButtonMenu.tsx';
export { default as ContainedList } from './ContainedList.tsx';
-export { default as Content } from './Content.tsx';
-export { default as Form } from './Form.tsx';
-export { default as FormLabel } from './FormLabel.tsx';
-export { default as Header } from './Header.tsx';
-export { default as Menu } from './Menu.tsx';
-export { default as MenuItem } from './MenuItem.tsx';
-export { default as TextInput } from './TextInput.tsx';
-export { default as Toolbar } from './Toolbar.tsx';
+export { default as Content } from './Content.tsx';
+export { default as Form } from './Form.tsx';
+export { default as FormLabel } from './FormLabel.tsx';
+export { default as Header } from './Header.tsx';
+export { default as Menu } from './Menu.tsx';
+export { default as MenuItem } from './MenuItem.tsx';
+export { default as TextInput } from './TextInput.tsx';
+export { default as Toolbar } from './Toolbar.tsx';
+
+export type { ArrowButtonProps } from './ArrowButton.tsx';
+export type { ButtonMenuProps } from './ButtonMenu.tsx';
+export type { ButtonProps } from './Button.tsx';
+export type { ContainedListProps } from './ContainedList.tsx';
+export type { ContentProps } from './Content.tsx';
+export type { FormLabelProps } from './FormLabel.tsx';
+export type { FormProps } from './Form.tsx';
+export type { HeaderProps } from './Header.tsx';
+export type { MenuItemProps } from './MenuItem.tsx';
+export type { MenuProps } from './Menu.tsx';
+export type { TextInputProps } from './TextInput.tsx';
+export type { ToolbarProps } from './Toolbar.tsx';
diff --git a/src/components/nodes/Input.tsx b/src/components/nodes/Input.tsx
index c32b32d..4bd7751 100644
--- a/src/components/nodes/Input.tsx
+++ b/src/components/nodes/Input.tsx
@@ -1,3 +1,4 @@
+import type { ComponentChildren } from 'preact';
import { useCallback } from 'preact/hooks';
import { InputSocket } from '../../dataflow.ts';
import { InSocket } from './Socket.tsx';
@@ -37,7 +38,7 @@ export const InputArray = ({ name, label }: Omit<InputProps<any>, 'value'>) => {
);
};
-const InputNum = (parseFunc: (string) => number) => ({ name, label, value }: InputProps<number>) => {
+const InputNum = (parseFunc: (_: string) => number) => ({ name, label, value }: InputProps<number | any>) => {
const onInput = useCallback((event: InputEvent) => {
value.value = parseFunc((event.target as HTMLInputElement).value);
}, [value]);
@@ -77,7 +78,7 @@ export interface InputSelectProps extends InputProps<string> {
}
export const InputSelect = ({ name, label, value, options }: InputSelectProps) => {
- const onChange = useCallback((event: InputEvent) => {
+ const onChange = useCallback((event: Event) => {
value.value = (event.target as HTMLSelectElement).value;
}, [value]);
return (
diff --git a/src/components/nodes/Socket.tsx b/src/components/nodes/Socket.tsx
index a4f38e1..6a958c5 100644
--- a/src/components/nodes/Socket.tsx
+++ b/src/components/nodes/Socket.tsx
@@ -1,5 +1,5 @@
import { useContext } from 'preact/hooks';
-import { NodeId, SocketHandlers } from '../../node.ts';
+import { NodeId, SocketHandlers, SocketHandler } from '../../node.ts';
import './Socket.css';
interface SocketProps {
diff --git a/src/components/nodes/index.ts b/src/components/nodes/index.ts
index d29f39f..b5ca5fc 100644
--- a/src/components/nodes/index.ts
+++ b/src/components/nodes/index.ts
@@ -1,3 +1,6 @@
-export { default as NodeShell, NodeShellProps } from './NodeShell.tsx';
-export { OutputNumber, OutputVector, OutputProps } from './Output.tsx';
-export { InputAny, InputArray, InputInteger, InputNumber, InputVector, InputSelect, InputProps, InputSelectProps } from './Input.tsx';
+export { InputAny, InputArray, InputInteger, InputNumber, InputVector, InputSelect } from './Input.tsx';
+export { default as NodeShell } from './NodeShell.tsx';
+export { OutputNumber, OutputVector } from './Output.tsx';
+export type { InputProps, InputSelectProps } from './Input.tsx';
+export type { NodeShellProps } from './NodeShell.tsx';
+export type { OutputProps } from './Output.tsx';
diff --git a/src/context.ts b/src/context.ts
index 054d796..dd7bb51 100644
--- a/src/context.ts
+++ b/src/context.ts
@@ -1,4 +1,4 @@
import { createContext } from 'preact';
-import type { PocketBase } from 'pocketbase';
+import PocketBase from 'pocketbase';
-export const Pb = createContext<PocketBase>();
+export const Pb = createContext<PocketBase | null>(null);
diff --git a/src/index.tsx b/src/index.tsx
index ff833cc..6fdf07e 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,15 +1,14 @@
import { render } from 'preact';
-import { useEffect, useMemo } from 'preact/hooks';
+import { useMemo } from 'preact/hooks';
import { Router } from 'preact-router';
import PocketBase from 'pocketbase';
import { Pb } from './context.ts';
import { Home, SignUp, LogIn, ProjectsList } from './pages';
import NodeEditor from './NodeEditor.tsx';
-import { Header, Button } from './components';
import './index.css';
export const App = () => {
- const pb = useMemo(() => new PocketBase(`/`));
+ const pb = useMemo(() => new PocketBase(`/`), []);
return (
<Pb.Provider value={pb}>
<Router>
diff --git a/src/node.ts b/src/node.ts
index 1f4d5eb..76ed42b 100644
--- a/src/node.ts
+++ b/src/node.ts
@@ -1,4 +1,5 @@
import { createContext, FunctionComponent } from 'preact';
+import type { Signal } from '@preact/signals';
import { InputSocket } from './dataflow.ts';
export type SocketHandler = (nodeId: number, socket: string, event: MouseEvent) => void;
diff --git a/src/nodes/Fourier.tsx b/src/nodes/Fourier.tsx
index f33768f..e5a81f0 100644
--- a/src/nodes/Fourier.tsx
+++ b/src/nodes/Fourier.tsx
@@ -21,6 +21,6 @@ export const Fourier = ({ id, x, y, inputs }: NodeComponentProps<FourierInputs>)
export const FourierNode: NodeInfo<FourierInputs, FourierOutputs> = {
component: Fourier,
- func: ({ data }) => ({ data: data ? fft(data) : null }),
+ func: ({ data }) => ({ data: data ? fft(data) as Float32Array : null }),
inputs: { data: null },
};
diff --git a/src/nodes/Intersperse.tsx b/src/nodes/Intersperse.tsx
index 0c7e271..3b0e3a6 100644
--- a/src/nodes/Intersperse.tsx
+++ b/src/nodes/Intersperse.tsx
@@ -3,12 +3,12 @@ import type { NodeComponentProps, NodeInfo } from '../node.ts';
import { intersperse } from '../wasm.ts';
export interface IntersperseInputs {
- a: Float32Array,
- b: Float32Array,
+ a: Float32Array | null,
+ b: Float32Array | null,
}
export interface IntersperseOutputs {
- out: Float32Array,
+ out: Float32Array | null,
}
export const Intersperse = ({ id, x, y, inputs }: NodeComponentProps<IntersperseInputs>) => {
@@ -23,6 +23,6 @@ export const Intersperse = ({ id, x, y, inputs }: NodeComponentProps<Intersperse
export const IntersperseNode: NodeInfo<IntersperseInputs, IntersperseOutputs> = {
component: Intersperse,
- func: ({ a, b }) => ({ out: a && b ? intersperse(a, b) : null }),
+ func: ({ a, b }) => ({ out: a && b ? intersperse(a, b) as Float32Array : null }),
inputs: { a: null, b: null },
}; \ No newline at end of file
diff --git a/src/nodes/Linspace.tsx b/src/nodes/Linspace.tsx
index b8239a1..7ae6db6 100644
--- a/src/nodes/Linspace.tsx
+++ b/src/nodes/Linspace.tsx
@@ -25,6 +25,6 @@ export const Linspace = ({ id, x, y, inputs }: NodeComponentProps<LinspaceInputs
export const LinspaceNode: NodeInfo<LinspaceInputs, LinspaceOutputs> = {
component: Linspace,
- func: ({ start, stop, n }) => ({ data: linspace(start, stop, Math.floor(n)) }),
+ func: ({ start, stop, n }) => ({ data: linspace(start, stop, Math.floor(n)) as Float32Array }),
inputs: { start: 0, stop: 1, n: 10 },
}; \ No newline at end of file
diff --git a/src/nodes/Plot.tsx b/src/nodes/Plot.tsx
index d87fb72..0f303fa 100644
--- a/src/nodes/Plot.tsx
+++ b/src/nodes/Plot.tsx
@@ -32,11 +32,11 @@ export const Plot = ({ id, x, y, inputs }: NodeComponentProps<PlotInputs>) => {
}
}
- let xticks = [];
+ let xticks: [string, number][] = [];
for (let x = minX.value; x <= maxX.value; x += dx.value / xtickCount) {
xticks.push([x.toFixed(1), (x - minX.value) / dx.value * width]);
}
- let yticks = [];
+ let yticks: [string, number][] = [];
for (let y = minY.value; y <= maxY.value; y += dy.value / ytickCount) {
yticks.push([y.toFixed(1), height - (y - minY.value) / dy.value * height]);
}
diff --git a/src/nodes/Unzip.tsx b/src/nodes/Unzip.tsx
index 5bcb477..4afdf60 100644
--- a/src/nodes/Unzip.tsx
+++ b/src/nodes/Unzip.tsx
@@ -3,12 +3,12 @@ import type { NodeComponentProps, NodeInfo } from '../node.ts';
import { unzip } from '../wasm.ts';
export interface UnzipInputs {
- data: Float32Array,
+ data: Float32Array | null,
}
export interface UnzipOutputs {
- a: Float32Array,
- b: Float32Array,
+ a: Float32Array | null,
+ b: Float32Array | null,
}
export const Unzip = ({ id, x, y, inputs }: NodeComponentProps<UnzipInputs>) => {
@@ -24,8 +24,8 @@ export const Unzip = ({ id, x, y, inputs }: NodeComponentProps<UnzipInputs>) =>
export const UnzipNode: NodeInfo<UnzipInputs, UnzipOutputs> = {
component: Unzip,
func: ({ data }) => {
- const out = data ? unzip(data) : null;
- return { a: out?.slice(0, out.length/2), b: out?.slice(out.length/2) };
+ const out = data ? unzip(data) as Float32Array : null;
+ return { a: out?.slice(0, out.length/2) || null, b: out?.slice(out.length/2) || null };
},
inputs: { data: null },
}; \ No newline at end of file
diff --git a/src/nodes/Viewer.tsx b/src/nodes/Viewer.tsx
index d92da3c..c21fd8f 100644
--- a/src/nodes/Viewer.tsx
+++ b/src/nodes/Viewer.tsx
@@ -10,7 +10,7 @@ export interface ViewerOutputs {}
export const Viewer = ({ id, x, y, inputs }: NodeComponentProps<ViewerInputs>) => {
let data = inputs.value.value;
if (ArrayBuffer.isView(data)) {
- data = Array.from(data);
+ data = Array.from(data as Float32Array);
}
return (
<NodeShell name="Viewer" id={id} x={x} y={y}>
diff --git a/src/nodes/index.ts b/src/nodes/index.ts
index 09310bb..f7a153a 100644
--- a/src/nodes/index.ts
+++ b/src/nodes/index.ts
@@ -1,4 +1,4 @@
-import type { NodeInfo } from '../node.tsx';
+import type { NodeInfo } from '../node.ts';
import { CombineXYZNode } from './CombineXYZ.tsx';
import { SeparateXYZNode } from './SeparateXYZ.tsx';
import { ViewerNode } from './Viewer.tsx';
@@ -11,7 +11,7 @@ import { PlotNode } from './Plot.tsx';
// TODO: ComplexMath
-const nodeRegistry: Record<string, NodeInfo<any>> = {
+const nodeRegistry: Record<string, NodeInfo<any, any>> = {
'Combine XYZ': CombineXYZNode,
'Separate XYZ': SeparateXYZNode,
'Viewer': ViewerNode,
diff --git a/src/pages/LogIn.tsx b/src/pages/LogIn.tsx
index 296a499..5820cb6 100644
--- a/src/pages/LogIn.tsx
+++ b/src/pages/LogIn.tsx
@@ -5,7 +5,7 @@ import { Pb } from '../context.ts';
import { Header, Content, Form, TextInput, Button, ArrowButton, FormLabel } from '../components';
const LogIn = () => {
- const pb = useContext(Pb);
+ const pb = useContext(Pb)!;
const email = useSignal('');
const password = useSignal('');
@@ -14,7 +14,7 @@ const LogIn = () => {
event.preventDefault();
await pb.collection('users').authWithPassword(email.value, password.value);
if (pb.authStore.isValid) {
- route('/' + pb.authStore.model.username);
+ route('/' + pb.authStore.model!.username);
}
}, []);
diff --git a/src/pages/ProjectsList.tsx b/src/pages/ProjectsList.tsx
index a7e3bed..13dd5d6 100644
--- a/src/pages/ProjectsList.tsx
+++ b/src/pages/ProjectsList.tsx
@@ -1,26 +1,30 @@
import { useContext, useEffect, useCallback } from 'preact/hooks';
import { useSignal } from '@preact/signals';
import { route } from 'preact-router';
+import type { ListResult } from 'pocketbase';
+import type { Project } from '../types.ts';
import { Pb } from '../context.ts';
import { logOut } from '../util.ts';
import { Header, Content, ContainedList, Form, FormLabel, TextInput, Button } from '../components';
-const ProjectsList = ({ user }) => {
- const pb = useContext(Pb);
- const projects = useSignal(null);
+export interface ProjectsListProps {
+ user: string;
+}
+
+const ProjectsList = ({ user }: ProjectsListProps) => {
+ const pb = useContext(Pb)!;
+ const projects = useSignal<ListResult<Project> | null>(null);
const projectName = useSignal('');
- useEffect(() => {
- pb.collection('projects')
- .getList(1, 20, { sort: '-mtime' })
- .then(p => projects.value = p);
+ useEffect(async () => {
+ projects.value = await pb.collection('projects').getList(1, 20, { sort: '-mtime' });
}, []);
- const onCreateProject = useCallback(async (event: FormEvent) => {
+ const onCreateProject = useCallback(async (event: SubmitEvent) => {
event.preventDefault();
const project = await pb.collection('projects').create({
name: projectName.value,
- owner: pb.authStore.model.id,
+ owner: pb.authStore.model!.id,
});
route(`/${user}/${project.name}`);
}, [user]);
diff --git a/src/pages/SignUp.tsx b/src/pages/SignUp.tsx
index 22e1512..e1cee3c 100644
--- a/src/pages/SignUp.tsx
+++ b/src/pages/SignUp.tsx
@@ -5,7 +5,7 @@ import { Pb } from '../context.ts';
import { Header, Content, Form, FormLabel, TextInput, Button, ArrowButton } from '../components';
const SignUp = () => {
- const pb = useContext(Pb);
+ const pb = useContext(Pb)!;
const username = useSignal('');
const email = useSignal('');
@@ -22,7 +22,7 @@ const SignUp = () => {
passwordConfirm: confirm.value,
});
if (pb.authStore.isValid) {
- route('/' + pb.authStore.model.username);
+ route('/' + pb.authStore.model!.username);
}
}, []);
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 0000000..29d14a3
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,18 @@
+export interface Project {
+ id: string;
+ owner: string;
+ name: string;
+}
+
+export interface Node {
+ id: string;
+ x: number;
+ y: number;
+ name: string;
+}
+
+export interface Link {
+ id: string;
+ from: string;
+ to: string;
+}
diff --git a/src/wasm.ts b/src/wasm.ts
index cf7b552..4a8d3c8 100644
--- a/src/wasm.ts
+++ b/src/wasm.ts
@@ -1,5 +1,5 @@
import { instantiate } from '../build/out.js';
-import url from '../build/out.wasm';
+import module from '../build/out.wasm';
export const {
memory,
mathS, mathV, mathSS, mathSV, mathVS, mathVV,
@@ -7,4 +7,4 @@ export const {
intersperse,
unzip,
fft,
-} = await instantiate(await WebAssembly.compileStreaming(fetch(url)), {}); \ No newline at end of file
+} = await instantiate(await WebAssembly.compileStreaming(fetch(module)), { env: {} }); \ No newline at end of file