make react component

This commit is contained in:
dylan 2024-03-31 12:13:27 -07:00
parent 55fca0a5c2
commit d606a8002f
8 changed files with 117 additions and 66 deletions

33
TODO.md
View File

@ -1,15 +1,20 @@
TODO # TODO
- [ ] User Auth ## MVP
- [x] Component Pack - [ ] Update GH Workflow to be by push
- [x] Typescript Pack - [ ] Add version prop to picobook.json
- [ ] Can this be used in a way where it doesn't need to be imported everywhere (lib?) - [ ] "Compile" carts in server and save them to db
- [ ] Consider making more parts into Packs - [ ] Load cart by URL in React
- [ ] tsconfig with extends
- [ ] server with wrapper around fastify ## Later
- [ ] scripts from package.json - [ ] Update pico console handle
- [ ] Streamline spinning up a server - [ ] Add readme prop to picobook.json (figure out best name) and display it in React
- [ ] Should public be top level? - [ ] Multiplayer support
- [ ] Clean up .env variable names - [ ] More console support (touch detected, gamepad count, etc.)
- [ ] use Temporal (polyfill)? - [ ] User accounts to manage published games, and user accounts for players to save progress
- [ ] Do I want to add testing infrastructure? - [ ] GPIO Support (maybe gpio prop in picobook.json can have values like "ignore" | "v1")
## GPIO ideas
- RGB background color
- Awards
- Extra buttons?

View File

@ -1,34 +1,16 @@
import { css } from "@emotion/css"; import { css } from "@emotion/css";
import { Center, Cover, Stack } from "@firebox/components"; import { Center, Cover, Stack } from "@firebox/components";
import { Pico8Console } from "./pico8-client/Pico8Console";
import testcarts from "./testcarts";
const App = (props: { name: string }) => { const App = (props: {}) => {
const {name} = props;
return ( return (
<Stack> <div className={css`
<div className={css`background-color: floralwhite;`}> min-height: 100vh;
<Cover gap pad> `}>
<Center> <Pico8Console carts={testcarts.carts} />
<Stack gap={-1}> </div>
<h1>Hello, {name}!</h1>
<p>Welcome to a website with a certain design philosophy. Tell me how it's working out! I want to see this text wrap a few times. Hopefully this sentence will help.</p>
</Stack>
</Center>
<Cover.Footer>A page by Dylan Pizzo</Cover.Footer>
</Cover>
</div>
<div className={css`background-color: aliceblue;`}>
<Cover gap pad>
<Center>
<Stack gap={-1}>
<h1>Hello, {name}!</h1>
<p>Welcome to a website with a certain design philosophy. Tell me how it's working out! I want to see this text wrap a few times. Hopefully this sentence will help.</p>
</Stack>
</Center>
<Cover.Footer>A page by Dylan Pizzo</Cover.Footer>
</Cover>
</div>
</Stack>
); );
}; };
export const app = <App name="World" />; export const app = <App />;

View File

@ -1,8 +1,8 @@
// import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
// import { app } from "./app.js"; import { app } from "./app.js";
// const domNode = document.getElementById("root")!; const domNode = document.getElementById("root")!;
// const root = createRoot(domNode); const root = createRoot(domNode);
// root.render(app); root.render(app);
export * from "./pico8-client/index"; // export * from "./pico8-client/index";

View File

@ -0,0 +1,44 @@
import { css } from "@emotion/css";
import { PicoCart, PicoPlayerHandle, makePicoConsole } from "./renderCart";
import { ForwardedRef, forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
type Pico8ConsoleImperatives = {
getPicoConsoleHandle(): PicoPlayerHandle | null;
}
export const Pico8Console = forwardRef((props: { carts: PicoCart[] }, forwardedRef: ForwardedRef<Pico8ConsoleImperatives>) => {
const {carts} = props;
const ref = useRef<HTMLDivElement>(null);
const [handle, setHandle] = useState<PicoPlayerHandle | null>(null);
const attachConsole = useCallback(async () => {
const picoConsole = await makePicoConsole({
carts,
});
if (ref.current) {
ref.current.appendChild(picoConsole.canvas);
}
setHandle(picoConsole);
}, [carts]);
useImperativeHandle(forwardedRef, () => ({
getPicoConsoleHandle() {
return handle;
}
}), [handle]);
useEffect(() => {
attachConsole();
}, [attachConsole]);
return (
<div ref={ref} className={css`
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
& > canvas {
width: 100%;
height: 100%;
}
`}></div>
);
});

View File

@ -1,9 +1,9 @@
// @ts-ignore // @ts-ignore
import "./build/veryRawRenderCart.js"; import "./build/veryRawRenderCart.js";
type PicoBool = 0 | 1; export type PicoBool = 0 | 1;
type RenderCart = (Module: {canvas: HTMLCanvasElement}, cartNames: string[], cartDatas: number[][], audioContext: AudioContext) => { export type RenderCart = (Module: {canvas: HTMLCanvasElement}, cartNames: string[], cartDatas: number[][], audioContext: AudioContext) => {
p8_touch_detected?: PicoBool; p8_touch_detected?: PicoBool;
p8_dropped_cart?: string; p8_dropped_cart?: string;
p8_dropped_cart_name?: string; p8_dropped_cart_name?: string;

View File

@ -1,8 +1,8 @@
import { assertNever } from "@firebox/tsutil"; import { assertNever } from "@firebox/tsutil";
import { pngToRom } from "./pngToRom"; import { pngToRom } from "./pngToRom";
import { renderCart as rawRenderCart } from "./rawRenderCart"; import { RenderCart, renderCart as rawRenderCart } from "./rawRenderCart";
type PicoCart = { export type PicoCart = {
name: string; name: string;
src: string; src: string;
} | { } | {
@ -20,7 +20,9 @@ type PlayerButtons = {
menu: boolean; menu: boolean;
} }
type PicoPlayerHandle = { export type PicoPlayerHandle = {
raw: ReturnType<RenderCart>;
rawModule: unknown;
// external things // external things
readonly canvas: HTMLCanvasElement; readonly canvas: HTMLCanvasElement;
@ -35,16 +37,16 @@ type PicoPlayerHandle = {
setGamepadCount: (count: number) => void; setGamepadCount: (count: number) => void;
readonly gpio: number[]; // read + write (should be 256-tuple) readonly gpio: number[]; // read + write (should be 256-tuple)
// state (all communicated out) // state
readonly state: { readonly state: {
readonly frameNumber: number; frameNumber: number;
readonly isPaused: boolean; isPaused: boolean;
readonly hasFocus: boolean; hasFocus: boolean;
readonly requestPointerLock: boolean; requestPointerLock: boolean;
readonly requirePageNavigateConfirmation: boolean; requirePageNavigateConfirmation: boolean;
readonly showDpad: boolean; showDpad: boolean;
readonly shutdownRequested: boolean; shutdownRequested: boolean;
readonly soundVolume: number; soundVolume: number;
}; };
// misc? // misc?
@ -109,6 +111,8 @@ export const makePicoConsole = async (props: {
]; ];
handle.pico8_gamepads = {count: 0}; handle.pico8_gamepads = {count: 0};
return { return {
raw: handle,
rawModule: Module,
canvas, canvas,
state: { state: {
frameNumber: handle.pico8_state.frame_number!, frameNumber: handle.pico8_state.frame_number!,

21
src/client/testcarts.ts Normal file

File diff suppressed because one or more lines are too long

View File

@ -3,11 +3,6 @@
<meta name="viewport" content="width=device-width, user-scalable=no"> <meta name="viewport" content="width=device-width, user-scalable=no">
<script type="text/javascript"> <script type="text/javascript">
console.log(Object.keys(window));
setTimeout(() => {
console.log(Object.keys(window));
}, 1000);
// Default shell for PICO-8 0.2.2 (includes @weeble's gamepad mod 1.0) // Default shell for PICO-8 0.2.2 (includes @weeble's gamepad mod 1.0)
// This file is available under a CC0 license https://creativecommons.org/share-your-work/public-domain/cc0/ // This file is available under a CC0 license https://creativecommons.org/share-your-work/public-domain/cc0/
// (note: "this file" does not include any cartridge or cartridge artwork injected into a derivative html file when using the PICO-8 html exporter) // (note: "this file" does not include any cartridge or cartridge artwork injected into a derivative html file when using the PICO-8 html exporter)