From 0a9ce46e02e7d25509a90a906052bcdb2c13cedf Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Fri, 26 Jan 2024 09:17:40 +0100 Subject: [PATCH] feat(rendering): component based --- src/components/MusicPlayer.tsx | 36 ++++++++++++++++--- src/components/Terminal.tsx | 7 ++-- src/components/terminal/TerminalCanvas.tsx | 23 ++++++++++++ src/components/terminal/TerminalText.tsx | 16 +++++++++ src/context/TerminalCanvasContext.ts | 18 ++++++++++ src/context/TerminalContext.tsx | 6 ++-- src/utils/terminal/elements/box.ts | 4 +-- .../terminal/{canvas.tsx => renderer.tsx} | 8 ++--- 8 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 src/components/terminal/TerminalCanvas.tsx create mode 100644 src/components/terminal/TerminalText.tsx create mode 100644 src/context/TerminalCanvasContext.ts rename src/utils/terminal/{canvas.tsx => renderer.tsx} (94%) diff --git a/src/components/MusicPlayer.tsx b/src/components/MusicPlayer.tsx index dfde91d..9b73633 100644 --- a/src/components/MusicPlayer.tsx +++ b/src/components/MusicPlayer.tsx @@ -1,6 +1,9 @@ import { useTerminal } from "~/context/TerminalContext"; -import { TerminalCanvas } from "~/utils/terminal/canvas"; +import { TerminalRenderer } from "~/utils/terminal/renderer"; import { TerminalBoxElement } from "~/utils/terminal/elements/box"; +import { TerminalCanvas } from "./terminal/TerminalCanvas"; +import { TerminalText } from "./terminal/TerminalText"; +import { useEffect, useState } from "react"; const theme = { black: "#45475a", @@ -21,19 +24,44 @@ const formatDurationMSS = (duration: number) => { return `${minutes}:${seconds.toString().padStart(2, "0")}`; }; - export const MusicPlayer = (props: { title: string; artist: string; album: string; duration: number; played: number; +}) => { + const { cols } = useTerminal(); + const [count, setCount] = useState(0); + + useEffect(() => { + const interval = setInterval(() => { + setCount(c => c + 1); + }, 1000); + + return () => clearInterval(interval); + }); + + return ( + + + Playback + + + ); +}; +export const MusicPlayer2 = (props: { + title: string; + artist: string; + album: string; + duration: number; + played: number; }) => { props; formatDurationMSS; const { cols } = useTerminal(); - const canvas = new TerminalCanvas(cols, 5); + const canvas = new TerminalRenderer(cols, 5); canvas.writeElement( new TerminalBoxElement(canvas.width, canvas.height), @@ -45,7 +73,7 @@ export const MusicPlayer = (props: { foreground: theme.magenta, }); - const inner = new TerminalCanvas(canvas.width - 2, canvas.height - 2); + const inner = new TerminalRenderer(canvas.width - 2, canvas.height - 2); // Title and Artist inner.write(2, 0, "Last Tango in Kyoto ยท Floating Bits", { foreground: theme.cyan, diff --git a/src/components/Terminal.tsx b/src/components/Terminal.tsx index 7abc9ba..2085016 100644 --- a/src/components/Terminal.tsx +++ b/src/components/Terminal.tsx @@ -8,10 +8,7 @@ export const Terminal = (props: { }) => { const terminalRef = useRef(null); - const [size, setSize] = useState<{ cols: number; rows: number }>({ - cols: -1, - rows: -1, - }); + const [size, setSize] = useState<{ cols: number; rows: number }>(); useEffect(() => { const precision = 300; @@ -58,7 +55,7 @@ export const Terminal = (props: { )} style={{ backdropFilter: "blur(2px)" }} > - {size.cols > -1 && props.children} + {size && props.children} ); diff --git a/src/components/terminal/TerminalCanvas.tsx b/src/components/terminal/TerminalCanvas.tsx new file mode 100644 index 0000000..a986f70 --- /dev/null +++ b/src/components/terminal/TerminalCanvas.tsx @@ -0,0 +1,23 @@ +import React, { type ReactNode, useEffect, useState } from "react"; +import { TerminalCanvasContextProvider } from "~/context/TerminalCanvasContext"; +import { TerminalRenderer } from "~/utils/terminal/renderer"; + +export const TerminalCanvas = (props: { + width: number; + height: number; + children?: Array | ReactNode; +}) => { + const [canvas] = useState(new TerminalRenderer(props.width, props.height)); + const [render, setRender] = useState>([]); + + useEffect(() => { + setRender(canvas.render()); + }, [canvas, props.children]); + + return ( + + {props.children} + {render} + + ); +}; diff --git a/src/components/terminal/TerminalText.tsx b/src/components/terminal/TerminalText.tsx new file mode 100644 index 0000000..6c9c82b --- /dev/null +++ b/src/components/terminal/TerminalText.tsx @@ -0,0 +1,16 @@ +import { useTerminalCanvas } from "~/context/TerminalCanvasContext"; + +export const TerminalText = (props: { + x: number; + y: number; + children: Array | string; +}) => { + const canvas = useTerminalCanvas(); + + const text = Array.isArray(props.children) + ? props.children.join("") + : props.children; + canvas.write(props.x, props.y, text); + + return null; +}; diff --git a/src/context/TerminalCanvasContext.ts b/src/context/TerminalCanvasContext.ts new file mode 100644 index 0000000..36c066e --- /dev/null +++ b/src/context/TerminalCanvasContext.ts @@ -0,0 +1,18 @@ +import { createContext, useContext } from "react"; +import { type TerminalRenderer } from "~/utils/terminal/renderer"; + +const TerminalCanvasContext = createContext( + undefined, +); + +export const TerminalCanvasContextProvider = TerminalCanvasContext.Provider; + +export const useTerminalCanvas = () => { + const context = useContext(TerminalCanvasContext); + if (!context) + throw new Error( + "useTerminalCanvas must be used inside a Terminal component", + ); + + return context; +}; diff --git a/src/context/TerminalContext.tsx b/src/context/TerminalContext.tsx index 66f4f68..abd4d24 100644 --- a/src/context/TerminalContext.tsx +++ b/src/context/TerminalContext.tsx @@ -1,8 +1,8 @@ import { createContext, useContext } from "react"; -const TerminalContext = createContext<{ cols: number; rows: number } | null>( - null, -); +const TerminalContext = createContext< + { cols: number; rows: number } | undefined +>(undefined); export const TerminalContextProvider = TerminalContext.Provider; diff --git a/src/utils/terminal/elements/box.ts b/src/utils/terminal/elements/box.ts index 1183c4d..4c3cd0d 100644 --- a/src/utils/terminal/elements/box.ts +++ b/src/utils/terminal/elements/box.ts @@ -1,5 +1,5 @@ import { type Cell, type CellStyle } from "../cell"; -import { TerminalCanvas } from "../canvas"; +import { TerminalRenderer } from "../renderer"; import { type TerminalElement } from "../element"; export class TerminalBoxElement implements TerminalElement { @@ -10,7 +10,7 @@ export class TerminalBoxElement implements TerminalElement { public readonly height: number, style: CellStyle = {}, ) { - const canvas = new TerminalCanvas(width, height, style); + const canvas = new TerminalRenderer(width, height, style); if (width == 1 && height > 1) { for (let y = 0; y < height - 1; y++) { diff --git a/src/utils/terminal/canvas.tsx b/src/utils/terminal/renderer.tsx similarity index 94% rename from src/utils/terminal/canvas.tsx rename to src/utils/terminal/renderer.tsx index 180e0a7..8940fe9 100644 --- a/src/utils/terminal/canvas.tsx +++ b/src/utils/terminal/renderer.tsx @@ -3,7 +3,7 @@ import { floorAll } from "../math"; import { type CellStyle, type Cell } from "./cell"; import { type TerminalElement } from "./element"; -export class TerminalCanvas implements TerminalElement { +export class TerminalRenderer implements TerminalElement { public readonly data: Array>; constructor( @@ -74,10 +74,10 @@ export class TerminalCanvas implements TerminalElement { y: number, width: number, height: number, - ): TerminalCanvas { + ): TerminalRenderer { [x, y, width, height] = floorAll(x, y, width, height); - const canvas = new TerminalCanvas(width, height); + const canvas = new TerminalRenderer(width, height); for (let cy = 0; cy < height; cy++) { for (let cx = 0; cx < width; cx++) { canvas.apply(cx, cy, this.data[y + cy][x + cx]); @@ -89,7 +89,7 @@ export class TerminalCanvas implements TerminalElement { render(): Array { const nodes: Array = []; - + console.log("here2"); for (let y = 0; y < this.height; y++) { for (let x = 0; x < this.width; x++) { const cell = this.data[y][x];