Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
a8d6650
initial setup, wip
lebalz Sep 3, 2025
dc005ab
setup flow nodes
lebalz Sep 8, 2025
51a512c
Merge branch 'main' into feature/live-nand-game
lebalz Sep 8, 2025
f8ff840
UPDATE INPUT STYLES
lebalz Sep 8, 2025
5848331
update dynamic docs
lebalz Sep 8, 2025
de197bf
setup initial circuit mode
lebalz Sep 8, 2025
da6210b
support streamed updates
lebalz Sep 8, 2025
09af1c0
implement reconnections
lebalz Sep 8, 2025
dd8e83c
hide attribution
lebalz Sep 8, 2025
a390ea8
Merge branch 'main' into feature/live-nand-game
lebalz Sep 9, 2025
f984bb4
introduce and, or and switch
lebalz Sep 10, 2025
c853cde
fix stream
lebalz Sep 10, 2025
a0ea3ee
fix setup
lebalz Sep 11, 2025
74dd444
Merge branch 'main' into feature/live-nand-game
lebalz Sep 13, 2025
fbceba2
fix lag
lebalz Sep 13, 2025
aca2972
add basic nodes
lebalz Sep 13, 2025
93f1462
add led
lebalz Sep 13, 2025
d2d7f78
update flow node
lebalz Sep 13, 2025
67b5d60
add xor
lebalz Sep 13, 2025
d097722
ensure propper internal updates
lebalz Sep 14, 2025
a191de6
add not
lebalz Sep 14, 2025
03898f8
fix type
lebalz Sep 14, 2025
f7936c5
add decimal display
lebalz Sep 14, 2025
80a03d5
Merge branch 'main' into feature/live-nand-game
lebalz Oct 14, 2025
190d1fa
update yarn.lock
lebalz Oct 14, 2025
1a6f0a7
Merge branch 'main' into feature/live-nand-game
lebalz Oct 14, 2025
50b1b4c
Merge branch 'main' into feature/live-nand-game
lebalz Oct 14, 2025
8f7cda4
Merge branch 'main' into feature/live-nand-game
lebalz Oct 14, 2025
30365a5
Merge branch 'main' into feature/live-nand-game
lebalz Oct 14, 2025
fcf3487
Merge branch 'main' into feature/live-nand-game
lebalz Dec 1, 2025
5b0d7a5
add yarn.lock
lebalz Dec 1, 2025
4b55dfd
some basic stuff
lebalz Dec 1, 2025
1ac7da4
Merge branch 'main' into feature/live-nand-game
lebalz Dec 18, 2025
54300c6
add type mapping to plugin
lebalz Dec 18, 2025
25c14c2
Merge branch 'main' into feature/live-nand-game
lebalz Dec 18, 2025
57adf0f
update to new plugin architecture
lebalz Dec 18, 2025
71fa191
move to hfr packages
lebalz Dec 18, 2025
1076f86
format
lebalz Dec 18, 2025
2333a33
warn
lebalz Dec 18, 2025
6e05de9
debug
lebalz Dec 18, 2025
42b33ee
Merge branch 'main' into feature/live-nand-game
lebalz Dec 18, 2025
99a4949
propper imports
lebalz Dec 19, 2025
1674190
Merge branch 'main' into feature/live-nand-game
lebalz Dec 28, 2025
0736412
fix dynamic documents in offline api
lebalz Dec 28, 2025
da1b789
remove logs, fix type error
lebalz Dec 28, 2025
27431bc
update gates
lebalz Dec 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/hfr/_category_.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
label: '@hfr/'
link:
type: generated-index
title: '@hfr/ Packages'
16 changes: 16 additions & 0 deletions packages/hfr/circuit/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
page_id: 980c75ef-0868-422e-9578-5ce228a48a0a
---

import DynamicDocumentRoots from '@tdev-components/documents/DynamicDocumentRoots';
import BrowserWindow from '@tdev-components/BrowserWindow';

# Schaltkreise

```mdx
<DynamicDocumentRoots id="e8844ea2-cdc7-4e9c-ba72-eef74511ff5f" name="Schaltkreise" roomType='circuit'/>
```

<BrowserWindow>
<DynamicDocumentRoots id="e7abf364-54c5-4b0d-a078-dfb22c8bab5f" name="Schaltkreise" roomType='circuit'/>
</BrowserWindow>
192 changes: 192 additions & 0 deletions packages/hfr/circuit/components/Circuit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import clsx from 'clsx';

import { observer } from 'mobx-react-lite';
import styles from './styles.module.scss';
import React from 'react';
import PermissionsPanel from '@tdev-components/PermissionsPanel';
import { Background, ReactFlow, MiniMap, Controls, Panel } from '@xyflow/react';
import type {
OnConnect,
Edge,
OnEdgesChange,
OnNodesChange,
Node,
OnReconnect,
FinalConnectionState,
HandleType,
OnNodesDelete
} from '@xyflow/react';

import '@xyflow/react/dist/style.css';
import { mdiCarBattery, mdiElectricSwitch, mdiLedOn } from '@mdi/js';
import Button from '@tdev-components/shared/Button';
import { useStore } from '@tdev-hooks/useStore';
import { nodeTypes } from './Nodes';
import { NodeType } from '@hfr/circuit';
import { DynamicRoomProps } from '@tdev-stores/ComponentStore';

type OnReconnectEnd = (
event: MouseEvent | TouchEvent,
edge: Edge,
handleType: HandleType,
connectionState: FinalConnectionState
) => void;

type OnReconnectStart = (event: React.MouseEvent, edge: Edge, handleType: HandleType) => void;

const Circuit = observer((props: DynamicRoomProps<'circuit'>): React.ReactNode => {
const { dynamicRoot } = props;
const documentStore = useStore('documentStore');
const edgeReconnectSuccessful = React.useRef(true);
const onChange = React.useCallback<OnNodesChange<Node>>(
(change) => {
dynamicRoot.room!.onNodesChange(change);
},
[dynamicRoot.room]
);
const onChangeEdge = React.useCallback<OnEdgesChange<Edge>>(
(change) => {
dynamicRoot.room!.onEdgeChange(change);
},
[dynamicRoot.room]
);
const onConnect = React.useCallback<OnConnect>(
(connection) => {
dynamicRoot.room!.onConnect(connection);
},
[dynamicRoot.room]
);
const onNodesDelete = React.useCallback<OnNodesDelete>(
(deleted) => {
edgeReconnectSuccessful.current = true;
dynamicRoot.room!.onDelete(deleted);
},
[dynamicRoot.room]
);
const onReconnectStart = React.useCallback<OnReconnectStart>(() => {
edgeReconnectSuccessful.current = false;
}, []);

const onReconnect = React.useCallback<OnReconnect>(
(oldEdge, newConnection) => {
edgeReconnectSuccessful.current = true;
dynamicRoot.room!.reconnectEdge(oldEdge.id, newConnection);
},
[dynamicRoot.room]
);

const onReconnectEnd = React.useCallback<OnReconnectEnd>((_, edge) => {
if (!edgeReconnectSuccessful.current) {
const doc = documentStore.find(edge.id);
if (doc) {
documentStore.apiDelete(doc);
}
}

edgeReconnectSuccessful.current = true;
}, []);
if (!dynamicRoot || !dynamicRoot.room) {
return null;
}

return (
<div className={clsx(styles.wrapper)}>
<div className={clsx(styles.rooms)}>
<h1 className={clsx(styles.name)}>
{dynamicRoot.name} <PermissionsPanel documentRootId={dynamicRoot.rootDocumentId} />
</h1>
<div style={{ width: '100%', height: '80vh' }}>
<ReactFlow
nodeTypes={nodeTypes}
nodes={dynamicRoot.room!.nodes}
edges={dynamicRoot.room!.edges}
onNodesDelete={onNodesDelete}
onNodesChange={onChange}
onEdgesChange={onChangeEdge}
onConnect={onConnect}
onReconnect={onReconnect}
onReconnectStart={onReconnectStart}
onReconnectEnd={onReconnectEnd}
fitView
minZoom={0.1}
maxZoom={4}
snapToGrid={true}
snapGrid={[10, 10]}
proOptions={{ hideAttribution: true }}
>
<MiniMap />
<Controls />
<Panel position="top-right" className={clsx(styles.panel)}>
<Button
icon={mdiElectricSwitch}
size={1}
color="blue"
onClick={() => {
dynamicRoot.room!.addFlowNode(NodeType.SwitchNode, { power: 0 });
}}
/>
<Button
text="OR"
size={1}
color="blue"
onClick={() => {
dynamicRoot.room!.addFlowNode(NodeType.OrNode, {});
}}
/>
<Button
text="XOR"
size={1}
color="blue"
onClick={() => {
dynamicRoot.room!.addFlowNode(NodeType.XorNode, {});
}}
/>
<Button
text="AND"
size={1}
color="blue"
onClick={() => {
dynamicRoot.room!.addFlowNode(NodeType.AndNode, {});
}}
/>
<Button
text="123"
size={1}
color="blue"
onClick={() => {
dynamicRoot.room!.addFlowNode(NodeType.DecimalDisplayNode, { pins: 4 });
}}
/>
<Button
text="NOT"
size={1}
color="blue"
onClick={() => {
dynamicRoot.room!.addFlowNode(NodeType.NotNode, {});
}}
/>
<Button
icon={mdiLedOn}
size={1}
color="blue"
onClick={() => {
dynamicRoot.room!.addFlowNode(NodeType.LedNode, {});
}}
/>
<Button
icon={mdiCarBattery}
size={1}
color="blue"
onClick={() => {
dynamicRoot.room!.addFlowNode(NodeType.BatteryNode, { pins: 5 });
}}
/>
</Panel>
<Background />
</ReactFlow>
</div>
</div>
</div>
);
});
export default Circuit;
26 changes: 26 additions & 0 deletions packages/hfr/circuit/components/Nodes/And/assets/Gate-AND.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions packages/hfr/circuit/components/Nodes/And/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import clsx from 'clsx';
import styles from './styles.module.scss';
import shared from '../styles.module.scss';
import { observer } from 'mobx-react-lite';
import { useStore } from '@tdev-hooks/useStore';
import { Handle, Node, NodeProps, Position } from '@xyflow/react';
import FlowNode from '@hfr/circuit/models/FlowNode';
import { NodeType } from '@hfr/circuit';
import NodeWrapper from '../NodeWrapper';
import AndGate from './assets/Gate-AND.svg';

export type AndNode = Node<{}, 'AndNode'>;

const AndNode = observer((props: NodeProps<AndNode>) => {
const documentStore = useStore('documentStore');
const doc = documentStore.find(props.id) as FlowNode<NodeType.AndNode> | undefined;
if (!doc) {
return null;
}
return (
<NodeWrapper node={doc} className={clsx(styles.and, shared.gate)}>
<AndGate className={shared.image} />
AND
<Handle
type="target"
position={Position.Left}
style={{ top: '10px' }}
className={clsx(doc.inputEdgeA?.isPowerOn && shared.on, shared.handle)}
id="a"
/>
<Handle
type="target"
position={Position.Left}
style={{ top: '50px' }}
className={clsx(doc.inputEdgeB?.isPowerOn && shared.on, shared.handle)}
id="b"
/>
<Handle
type="source"
position={Position.Right}
style={{
right: '0px'
}}
className={clsx(doc.deriver.output && shared.on, shared.handle)}
/>
</NodeWrapper>
);
});

export default AndNode;
4 changes: 4 additions & 0 deletions packages/hfr/circuit/components/Nodes/And/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.and {
width: 80px;
height: 60px;
}
68 changes: 68 additions & 0 deletions packages/hfr/circuit/components/Nodes/Battery/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import clsx from 'clsx';
import styles from './styles.module.scss';
import shared from '../styles.module.scss';
import { observer } from 'mobx-react-lite';
import { useStore } from '@tdev-hooks/useStore';
import { Handle, Node, NodeProps, Position, useUpdateNodeInternals } from '@xyflow/react';
import { mdiCarBattery, mdiMinusCircle, mdiPlusCircle } from '@mdi/js';
import FlowNode from '@hfr/circuit/models/FlowNode';
import { NodeType } from '@hfr/circuit';
import Icon from '@mdi/react';
import Button from '@tdev-components/shared/Button';
import NodeWrapper from '../NodeWrapper';

export type BatteryNode = Node<{}, 'BatteryNode'>;

const BatteryNode = observer((props: NodeProps<BatteryNode>) => {
const documentStore = useStore('documentStore');
const updateNodeInternals = useUpdateNodeInternals();
const doc = documentStore.find(props.id) as FlowNode<NodeType.BatteryNode> | undefined;
if (!doc) {
return null;
}
const pins = doc?.deriver.pins ?? 3;
return (
<NodeWrapper node={doc} className={styles.battery}>
<Icon path={mdiCarBattery} color="var(--ifm-color-primary)" size={1} />
<Handle
type="source"
position={Position.Top}
className={clsx(shared.on, shared.handle)}
style={{ top: -2, left: '17px' }}
/>
{Array.from({ length: pins }).map((_, i) => (
<Handle
key={i}
id={i === 0 ? 'a' : i === 1 ? 'b' : `p${i}`}
type="target"
position={i === 0 ? Position.Bottom : Position.Top}
className={clsx(shared.handle)}
style={{ top: `${25 - (i === 0 ? 5 : 0)}px`, left: `${5 + i * 50}px` }}
/>
))}
<div className={clsx(styles.ground)} style={{ ['--data-width' as any]: `${(pins - 1) * 50}px` }}>
<Button
icon={mdiPlusCircle}
size={0.3}
className={clsx(styles.pin, styles.pinAdd)}
onClick={() => {
doc?.deriver.addPin();
updateNodeInternals(props.id);
}}
/>
<Button
icon={mdiMinusCircle}
size={0.3}
className={clsx(styles.pin, styles.pinRemove)}
onClick={() => {
doc?.deriver.removePin();
updateNodeInternals(props.id);
}}
/>
</div>
</NodeWrapper>
);
});

export default BatteryNode;
20 changes: 20 additions & 0 deletions packages/hfr/circuit/components/Nodes/Battery/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.battery {
.ground {
position: absolute;
border-top: 1px solid var(--ifm-color-danger);
width: var(--data-width);
bottom: 5px;
margin-top: -1px;
margin-left: 6px;
.pin {
position: absolute;
bottom: -22px;
&.pinAdd {
right: -30px;
}
&.pinRemove {
right: 15px;
}
}
}
}
Loading