Skip to content

Commit

Permalink
Upload download functionality (#2)
Browse files Browse the repository at this point in the history
* feat: add dtekv-upload and dtekv-download tools to navigation

* feat: dialog instead of dropdown

* feat: remove old code

* chore: package version

* feat: remove unused imports

* feat: add soft load

---------

Co-authored-by: Alvinn8 <42838560+Alvinn8@users.noreply.github.com>
  • Loading branch information
PumpedSardines and Alvinn8 authored Nov 24, 2024
1 parent 7218e17 commit d3c865c
Show file tree
Hide file tree
Showing 18 changed files with 618 additions and 121 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "dtekv-emulator-web",
"private": true,
"version": "1.2.2",
"version": "1.3.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
1 change: 1 addition & 0 deletions src/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { HexDisplays, LedStrip, Switches, UartCallback, CpuLoadCallback } from "
// Need to add that as there for some reason?
export const store = createStore() as INTERNAL_PrdStore;

export const dialogElementAtom = atom<React.ReactNode | null>(null);
export const hasLoadedAtom = atom(false);
export const buttonPressedAtom = atom(false);
export const vgaBufferAtom = atom(new Uint8Array(320 * 240 * 3));
Expand Down
32 changes: 31 additions & 1 deletion src/cpu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,28 @@ function cpuLoop() {
requestAnimationFrame(cpuLoop);
}

export function softReset() {
if (currentLoadedBinary) {
currentLoadedBinary = new Uint8Array(currentLoadedBinary);
cpu.load(new Uint8Array(currentLoadedBinary));
cpu.reset();
const loadCallbacks = store.get(cpuLoadCallbacksAtom);
loadCallbacks.forEach((cb) => cb());
store.set(vgaBufferAtom, cpu.get_vga_frame_buffer());
store.set(hasLoadedAtom, true);
}
}

export function softLoadBinary(binary: Uint8Array) {
currentLoadedBinary = new Uint8Array(binary);
cpu.load(new Uint8Array(binary));
cpu.reset();
const loadCallbacks = store.get(cpuLoadCallbacksAtom);
loadCallbacks.forEach((cb) => cb());
store.set(vgaBufferAtom, cpu.get_vga_frame_buffer());
store.set(hasLoadedAtom, true);
}

export function loadBinary(binary: Uint8Array) {
currentLoadedBinary = new Uint8Array(binary);
cpu.set_to_new();
Expand All @@ -121,7 +143,7 @@ export function loadBinary(binary: Uint8Array) {
store.set(hasLoadedAtom, true);
}

export function reset() {
export function reload() {
if (currentLoadedBinary) {
loadBinary(currentLoadedBinary);
}
Expand All @@ -130,3 +152,11 @@ export function reset() {
export function startCpuLoop() {
requestAnimationFrame(cpuLoop);
}

export function loadDataAt(addr: number, data: Uint8Array) {
cpu.load_data_at(addr, data);
}

export function readDataAt(addr: number, length: number): Uint8Array {
return cpu.read_data_at(addr, length);
}
70 changes: 5 additions & 65 deletions src/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,82 +27,22 @@
--color-button-hover: #777;

--color-text: #fff;
--color-text-disabled: #777;
--color-background: #444;
--color-background-hover: #555;
--color-background-alt: #333;
--color-border: #222;
}

@media (prefers-color-scheme: light) {
:root {
--color-hex-display-on: #f00;
--color-hex-display-off: #522;
--color-hex-display-background: #c8c8c8;
--color-hex-display-gap-background: #b8b8b8;

--color-error-background: #a40;
--color-error-border: #a00;

--color-io-switch-background: #b8b8b8;
--color-io-switch-inner-background: #999;
--color-io-switch-knob: #555;

--color-io-leds-background: #b8b8b8;
--color-io-leds-on: #f00;
--color-io-leds-off: #522;

--color-io-button-background: #b8b8b8;
--color-io-button-clickable-area-background: #555;
--color-io-button-clickable-area-pressed: #444;
--color-io-button-clickable-area-hover: #666;

--color-button-hover: #777;
--color-text: #222;
--color-background: #f8f8f8;
--color-background-hover: #e8e8e8;
--color-background-alt: #c8c8c8;
--color-border: #b8b8b8;
}
body,
html {
margin: 0;
padding: 0;
}

[data-color="dark"] {
--color-hex-display-on: #f00;
--color-hex-display-off: #522;
--color-hex-display-background: #333;
--color-hex-display-gap-background: #444;

--color-error-background: #a40;
--color-error-border: #a00;

--color-io-switch-background: #777;
--color-io-switch-inner-background: #555;
--color-io-switch-knob: #333;

--color-io-leds-background: #777;
--color-io-leds-on: #f00;
--color-io-leds-off: #522;

--color-io-button-background: #777;
--color-io-button-clickable-area-background: #333;
--color-io-button-clickable-area-pressed: #555;
--color-io-button-clickable-area-hover: #383838;

--color-button-hover: #777;

--color-text: #fff;
--color-background: #444;
--color-background-hover: #555;
--color-background-alt: #333;
--color-border: #222;
}
body,html {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
background-color: var(--color-background);
color: var(--color-text);
font-family: sans-serif;
transition: background-color 0.2s, color 0.2s;
}
13 changes: 13 additions & 0 deletions src/hooks/useDialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useSetAtom } from 'jotai';
import { dialogElementAtom } from '../atoms';

function useDialog() {
const setDialogElement = useSetAtom(dialogElementAtom)

return {
open: (node: React.ReactNode) => setDialogElement(node),
close: () => setDialogElement(null)
}
}

export default useDialog;
32 changes: 32 additions & 0 deletions src/hooks/useOnClickOutside.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useEffect } from "react";

/**
* A hook that provides a callback called when the user clicks outside the
* provided elements.
*
* @param elements A list of refs to elements that are concidered "inside".
* @param onClickOutside A callback when the user clicks "outside".
*/
function useOnClickOutside(
elements: React.RefObject<HTMLElement>[],
onClickOutside: () => void,
) {
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
const target = event.target as Node;
if (
elements.every(
(element) => element.current && !element.current.contains(target)
)
) {
onClickOutside();
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [elements, onClickOutside]);
}

export default useOnClickOutside;
2 changes: 2 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { startCpuLoop } from "./cpu";
import useWindowDimension from "./hooks/useWindowDimensions";
import { MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT } from "./consts";
import TooSmall from "./pages/TooSmall";
import Dialog from "./partials/Dialog";

const App = () => {
const dimensions = useWindowDimension();
Expand All @@ -18,6 +19,7 @@ const App = () => {

return (
<jotai.Provider store={store}>
<Dialog />
{(() => {
if (isTooSmall) {
return <TooSmall />;
Expand Down
6 changes: 4 additions & 2 deletions src/pages/Emulator/views/Nav/Nav.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
background-color: var(--color-background);
border-right: 1px solid var(--color-border);
position: relative;

user-select: none;
}

.wrapper {
Expand All @@ -31,7 +33,7 @@
display: flex;
}

.example a {
.example button {
all: unset;
display: flex;
align-items: center;
Expand All @@ -47,7 +49,7 @@
cursor: pointer;
}

.example a:hover {
.example button:hover {
background-color: var(--color-background-hover);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
.navButton {
all: unset;
cursor: pointer;
padding: 0 12px;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--color-background);
border-right: 1px solid var(--color-border);
position: relative;

user-select: none;
}

.navButton:hover {
background-color: var(--color-background-hover);
}

.wrapper {
position: relative;
height: 100%;
}

.dropDown {
display: none;
position: absolute;
flex-direction: column;
transform: translateY(100%);
bottom: -1px;
left: -1px;
width: calc(100% + 1px);
z-index: 10;
}

.dropDown.open {
display: flex;
}

.dropDown button {
all: unset;
display: flex;
align-items: center;
box-sizing: border-box;
padding-left: 12px;
justify-content: flex-start;
background-color: var(--color-background);
border: 1px solid var(--color-border);
border-top: unset;
height: 30px;
width: 100%;
user-select: none;
cursor: pointer;
}

.dropDown button:hover {
background-color: var(--color-background-hover);
}


.dropDown button[disabled] {
cursor: not-allowed;
color: var(--color-text-disabled);
background-color: var(--color-background);
}
57 changes: 57 additions & 0 deletions src/pages/Emulator/views/Nav/helpers/NavDropDownButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useRef, useState } from "react";

import styles from "./NavDropDownButton.module.css";
import useOnClickOutside from "../../../../../../hooks/useOnClickOutside";
import cx from "../../../../../../utils/cx";

type NavDropDownButtonProps = {
title: string;
buttons: {
title: string;
disabled?: boolean;
onClick: () => void;
}[];
};

function NavDropDownButton(props: NavDropDownButtonProps) {
const [open, setOpen] = useState(false);
const dropDownRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);

useOnClickOutside([dropDownRef, buttonRef], () => setOpen(false));

return (
<div className={styles.wrapper}>
<button
ref={buttonRef}
onClick={() => {
setOpen(!open);
}}
className={styles.navButton}
>
{props.title}
</button>
<div
ref={dropDownRef}
className={cx(styles.dropDown, open && styles.open)}
>
{props.buttons.map(({ title, disabled, onClick }) => {
return (
<button
disabled={disabled}
key={title}
onClick={() => {
setOpen(false);
onClick();
}}
>
{title}
</button>
);
})}
</div>
</div>
);
}

export default React.memo(NavDropDownButton);
Loading

0 comments on commit d3c865c

Please sign in to comment.