From e9fe8c95d22192c188ce263302a2e33fb0f7e753 Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Fri, 26 Jan 2024 23:02:46 +0100 Subject: [PATCH] feat(nvim): simple one-level file tree --- src/components/Nvim/Nvim.tsx | 8 +- src/components/Nvim/NvimStatusBar.tsx | 27 +++++- src/components/Nvim/NvimTree.tsx | 127 +++++++++++++++++++++++++- 3 files changed, 151 insertions(+), 11 deletions(-) diff --git a/src/components/Nvim/Nvim.tsx b/src/components/Nvim/Nvim.tsx index dcbc2a0..115d95e 100644 --- a/src/components/Nvim/Nvim.tsx +++ b/src/components/Nvim/Nvim.tsx @@ -5,14 +5,14 @@ export const Nvim = () => { return (
-
+
-
+
-
- +
+
); diff --git a/src/components/Nvim/NvimStatusBar.tsx b/src/components/Nvim/NvimStatusBar.tsx index 138d698..f6ca985 100644 --- a/src/components/Nvim/NvimStatusBar.tsx +++ b/src/components/Nvim/NvimStatusBar.tsx @@ -1,12 +1,31 @@ import { useTerminal } from "~/context/TerminalContext"; import { TerminalRenderer } from "~/utils/terminal/renderer"; +import { theme } from "~/utils/terminal/theme"; -export const NvimStatusBar = () => { +export const NvimStatusBar = (props: { label: string; fileName: string }) => { const { cols: width } = useTerminal(); - const canvas = new TerminalRenderer(width, 2); + const canvas = new TerminalRenderer(width, 1); - canvas.write(0, 0, "status line 1"); - canvas.write(0, 1, "status line 2"); + canvas.write(0, 0, ` ${props.label} `, { + background: theme.blue, + foreground: "#000", + }); + canvas.write(props.label.length + 2, 0, "\ue0ba", { + background: theme.blue, + foreground: "#474353", + }); + canvas.write(props.label.length + 3, 0, "\ue0ba", { + background: "#474353", + foreground: "#373040", + }); + canvas.write(props.label.length + 4, 0, ` ${props.fileName} `, { + background: "#373040", + foreground: theme.white, + }); + canvas.write(props.label.length + 6 + props.fileName.length, 0, "\ue0ba", { + background: "#373040", + foreground: "#29293c", + }); return

{canvas.render()}

; }; diff --git a/src/components/Nvim/NvimTree.tsx b/src/components/Nvim/NvimTree.tsx index 76681a7..def578f 100644 --- a/src/components/Nvim/NvimTree.tsx +++ b/src/components/Nvim/NvimTree.tsx @@ -1,11 +1,132 @@ +import { useState, useEffect } from "react"; import { useTerminal } from "~/context/TerminalContext"; +import { type Cell } from "~/utils/terminal/cell"; import { TerminalRenderer } from "~/utils/terminal/renderer"; +import { theme } from "~/utils/terminal/theme"; + +const PATH_FOLDED: Cell = { + char: "", + foreground: theme.grey, +}; + +const PATH_UNFOLDED: Cell = { + char: "", + foreground: theme.blue, +}; + +const FILE_STYLES = { + directory: { + char: "\ue6ad", // \ue6ad ||| \ueaf6 + foreground: theme.blue, + }, + md: { + char: "\ue73e", + foreground: theme.blue, + }, + asc: { + char: "\uf43d", + foreground: theme.yellow, + }, +} as const satisfies Record; + +type FileType = keyof typeof FILE_STYLES; + +type File = { + name: string; +} & ( + | { + type: Exclude; + } + | { + type: "directory"; + children: Array; + folded: boolean; + } +); + +const FILES_SRC: Array = [ + { name: "projects", type: "directory", children: [], folded: true }, + { name: "README.md", type: "md" }, + { name: "LICENSE.md", type: "md" }, + { name: "prout", type: "directory", children: [], folded: true }, + { name: "hello", type: "directory", children: [], folded: true }, + { name: "pubkey.asc", type: "asc" }, +]; export const NvimTree = () => { - const { cols: width, rows: height } = useTerminal(); - const canvas = new TerminalRenderer(width * 0.15, height - 2); + const [selected, setSelected] = useState(0); + const [files, setFiles] = useState( + FILES_SRC.sort((a, b) => a.name.localeCompare(b.name)).sort((a, b) => + a.type === "directory" && b.type !== "directory" + ? -1 + : a.type !== "directory" && b.type === "directory" + ? 1 + : 0, + ), + ); - canvas.write(0, 0, "ijirjginrgi"); + const { cols: width, rows: height } = useTerminal(); + const canvas = new TerminalRenderer(width * 0.2, height - 2, { + background: "#0000001a", + }); + + const tree = new TerminalRenderer(canvas.width - 3, height - 1); + + useEffect(() => { + const onScroll = (event: KeyboardEvent) => { + switch (event.key) { + case "ArrowUp": + setSelected(x => Math.max(0, x - 1)); + break; + + case "ArrowDown": + setSelected(x => Math.min(files.length - 1, x + 1)); + break; + + case "Enter": + const newFiles = [...files]; + + const file = newFiles[selected]; + if (file?.type === "directory") { + file.folded = !file.folded; + } + + setFiles(newFiles); + break; + } + }; + + window.addEventListener("keydown", onScroll); + + return () => { + window.removeEventListener("keydown", onScroll); + }; + }); + + tree.write(0, selected, " ".repeat(tree.width), { background: "#504651" }); + + files.forEach((file, y) => { + tree.apply(2, y, FILE_STYLES[file.type]); + + if (file.type === "directory") { + tree.apply(0, y, file.folded ? PATH_FOLDED : PATH_UNFOLDED); + + tree.write(4, y, file.name, { + foreground: FILE_STYLES.directory.foreground, + }); + } else { + if (file.name === "README.md") { + tree.write(4, y, file.name, { + foreground: theme.yellow, + fontWeight: 800, + }); + } else { + tree.write(4, y, file.name); + } + } + }); + + canvas.writeElement(tree, 2, 1); return

{canvas.render()}

; };