feat(nvim): display selected file in tree in the editor
This commit is contained in:
@@ -14,6 +14,7 @@
|
|||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-router-dom": "^6.21.3",
|
||||||
"vite-tsconfig-paths": "^4.3.1",
|
"vite-tsconfig-paths": "^4.3.1",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
|
|||||||
31
pnpm-lock.yaml
generated
31
pnpm-lock.yaml
generated
@@ -14,6 +14,9 @@ dependencies:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: 18.2.0
|
specifier: 18.2.0
|
||||||
version: 18.2.0(react@18.2.0)
|
version: 18.2.0(react@18.2.0)
|
||||||
|
react-router-dom:
|
||||||
|
specifier: ^6.21.3
|
||||||
|
version: 6.21.3(react-dom@18.2.0)(react@18.2.0)
|
||||||
vite-tsconfig-paths:
|
vite-tsconfig-paths:
|
||||||
specifier: ^4.3.1
|
specifier: ^4.3.1
|
||||||
version: 4.3.1(typescript@5.3.3)(vite@5.0.12)
|
version: 4.3.1(typescript@5.3.3)(vite@5.0.12)
|
||||||
@@ -419,6 +422,11 @@ packages:
|
|||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/@remix-run/router@1.14.2:
|
||||||
|
resolution: {integrity: sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@rollup/rollup-android-arm-eabi@4.9.6:
|
/@rollup/rollup-android-arm-eabi@4.9.6:
|
||||||
resolution: {integrity: sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==}
|
resolution: {integrity: sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
@@ -2726,6 +2734,29 @@ packages:
|
|||||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/react-router-dom@6.21.3(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-kNzubk7n4YHSrErzjLK72j0B5i969GsuCGazRl3G6j1zqZBLjuSlYBdVdkDOgzGdPIffUOc9nmgiadTEVoq91g==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8'
|
||||||
|
react-dom: '>=16.8'
|
||||||
|
dependencies:
|
||||||
|
'@remix-run/router': 1.14.2
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
react-router: 6.21.3(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/react-router@6.21.3(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-a0H638ZXULv1OdkmiK6s6itNhoy33ywxmUFT/xtSoVyf9VnC7n7+VT4LjVzdIHSaF5TIh9ylUgxMXksHTgGrKg==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8'
|
||||||
|
dependencies:
|
||||||
|
'@remix-run/router': 1.14.2
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react@18.2.0:
|
/react@18.2.0:
|
||||||
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|||||||
49
src/App.tsx
49
src/App.tsx
@@ -1,3 +1,4 @@
|
|||||||
|
import { BrowserRouter } from "react-router-dom";
|
||||||
import { MusicPlayer } from "./components/MusicPlayer";
|
import { MusicPlayer } from "./components/MusicPlayer";
|
||||||
import { MusicVisualizer } from "./components/MusicVisualizer";
|
import { MusicVisualizer } from "./components/MusicVisualizer";
|
||||||
import { Nvim } from "./components/Nvim/Nvim";
|
import { Nvim } from "./components/Nvim/Nvim";
|
||||||
@@ -7,32 +8,34 @@ import { AppContextProvider } from "./context/AppContext";
|
|||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<AppContextProvider>
|
<AppContextProvider>
|
||||||
<main
|
<BrowserRouter>
|
||||||
className={
|
<main
|
||||||
"insets-0 fixed flex h-screen w-screen flex-col gap-3 bg-[url(/wallpaper.jpg)] bg-cover p-3 font-body leading-[26px]"
|
className={
|
||||||
}
|
"insets-0 fixed flex h-screen w-screen flex-col gap-3 bg-[url(/wallpaper.jpg)] bg-cover p-3 font-body leading-[26px]"
|
||||||
>
|
}
|
||||||
<nav className="border border-red-500">toolbar</nav>
|
>
|
||||||
|
<nav className="border border-red-500">toolbar</nav>
|
||||||
<Terminal className="flex-1">
|
|
||||||
<Nvim />
|
|
||||||
</Terminal>
|
|
||||||
|
|
||||||
<div className="flex gap-3">
|
|
||||||
<Terminal className="flex-1 select-none">
|
|
||||||
<MusicPlayer
|
|
||||||
title="Last Tango in Kyoto"
|
|
||||||
artist="Floating Bits"
|
|
||||||
album="Last Tango in Kyoto"
|
|
||||||
duration={93}
|
|
||||||
/>
|
|
||||||
</Terminal>
|
|
||||||
|
|
||||||
<Terminal className="flex-1">
|
<Terminal className="flex-1">
|
||||||
<MusicVisualizer />
|
<Nvim />
|
||||||
</Terminal>
|
</Terminal>
|
||||||
</div>
|
|
||||||
</main>
|
<div className="flex gap-3">
|
||||||
|
<Terminal className="flex-1 select-none">
|
||||||
|
<MusicPlayer
|
||||||
|
title="Last Tango in Kyoto"
|
||||||
|
artist="Floating Bits"
|
||||||
|
album="Last Tango in Kyoto"
|
||||||
|
duration={93}
|
||||||
|
/>
|
||||||
|
</Terminal>
|
||||||
|
|
||||||
|
<Terminal className="flex-1">
|
||||||
|
<MusicVisualizer />
|
||||||
|
</Terminal>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</BrowserRouter>
|
||||||
</AppContextProvider>
|
</AppContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,71 @@ import { useApp } from "~/context/AppContext";
|
|||||||
import { NvimStatusBar } from "./NvimStatusBar";
|
import { NvimStatusBar } from "./NvimStatusBar";
|
||||||
import { NvimTree } from "./NvimTree";
|
import { NvimTree } from "./NvimTree";
|
||||||
import { buildFileTree } from "~/utils/filesystem";
|
import { buildFileTree } from "~/utils/filesystem";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { NvimEditor } from "./NvimEditor";
|
||||||
|
|
||||||
|
const fetchData = async (
|
||||||
|
branch: string,
|
||||||
|
repo: string,
|
||||||
|
file: string,
|
||||||
|
): Promise<string | null> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get<string>(
|
||||||
|
`https://raw.githubusercontent.com/pihkaal/${repo}/${branch}/${file}`,
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const Nvim = () => {
|
export const Nvim = () => {
|
||||||
const manifest = useApp();
|
const manifest = useApp();
|
||||||
|
const [data, setData] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const view = params.get("view");
|
||||||
|
if (!view) {
|
||||||
|
navigate("?view=README.md");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = view.split("/");
|
||||||
|
if (path.length === 1) {
|
||||||
|
path.splice(0, 0, "pihkaal");
|
||||||
|
}
|
||||||
|
const repo = path[0]!;
|
||||||
|
const file = path[1]!;
|
||||||
|
|
||||||
|
void (async () => {
|
||||||
|
const data =
|
||||||
|
(await fetchData("main", repo, file)) ??
|
||||||
|
(await fetchData("dev", repo, file));
|
||||||
|
if (!data) {
|
||||||
|
navigate("?view=README.md");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setData(data);
|
||||||
|
})();
|
||||||
|
}, [location, navigate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="flex flex-col">
|
||||||
<div className="flex">
|
<div className="flex flex-1 flex-row">
|
||||||
<div className="w-fit">
|
<div className="w-fit">
|
||||||
<NvimTree files={buildFileTree(manifest)} />
|
<NvimTree files={buildFileTree(manifest)} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1"></div>
|
<div className="flex-1">
|
||||||
|
<NvimEditor content={data} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="h-fit bg-[#29293c]">
|
<div className="h-fit bg-[#29293c]">
|
||||||
<NvimStatusBar label="NORMAL" fileName="README.md" />
|
<NvimStatusBar label="NORMAL" fileName="README.md" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
16
src/components/Nvim/NvimEditor.tsx
Normal file
16
src/components/Nvim/NvimEditor.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { useTerminal } from "~/context/TerminalContext";
|
||||||
|
import { TerminalRenderer } from "~/utils/terminal/renderer";
|
||||||
|
|
||||||
|
export const NvimEditor = (props: { content: string | null }) => {
|
||||||
|
const { cols: width, rows: height } = useTerminal();
|
||||||
|
|
||||||
|
const canvas = new TerminalRenderer(width * 0.8, height - 2);
|
||||||
|
|
||||||
|
if (props.content) {
|
||||||
|
props.content.split("\n").forEach((line, y) => {
|
||||||
|
canvas.write(0, y, line);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas.render();
|
||||||
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useTerminal } from "~/context/TerminalContext";
|
import { useTerminal } from "~/context/TerminalContext";
|
||||||
import {
|
import {
|
||||||
DEFAULT_FILE_STYLE,
|
DEFAULT_FILE_STYLE,
|
||||||
@@ -23,6 +24,7 @@ const PATH_UNFOLDED: Cell = {
|
|||||||
export const NvimTree = (props: { files: Array<File> }) => {
|
export const NvimTree = (props: { files: Array<File> }) => {
|
||||||
const [selected, setSelected] = useState(0);
|
const [selected, setSelected] = useState(0);
|
||||||
const [files, setFiles] = useState(props.files);
|
const [files, setFiles] = useState(props.files);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { cols: width, rows: height } = useTerminal();
|
const { cols: width, rows: height } = useTerminal();
|
||||||
const canvas = new TerminalRenderer(width * 0.2, height - 2, {
|
const canvas = new TerminalRenderer(width * 0.2, height - 2, {
|
||||||
@@ -108,6 +110,9 @@ export const NvimTree = (props: { files: Array<File> }) => {
|
|||||||
if (current.type === "directory") {
|
if (current.type === "directory") {
|
||||||
current.folded = !current.folded;
|
current.folded = !current.folded;
|
||||||
setFiles([...files]);
|
setFiles([...files]);
|
||||||
|
} else {
|
||||||
|
//document.location.href = `?view=${current.path}`
|
||||||
|
navigate(`?view=${current.path}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export const FILE_STYLES: Record<string, Cell> = {
|
|||||||
|
|
||||||
export type File = {
|
export type File = {
|
||||||
name: string;
|
name: string;
|
||||||
|
path: string;
|
||||||
} & (
|
} & (
|
||||||
| {
|
| {
|
||||||
type: "file";
|
type: "file";
|
||||||
@@ -54,17 +55,20 @@ export const buildFileTree = (manifest: Manifest): Array<File> => {
|
|||||||
project.files.forEach(file => {
|
project.files.forEach(file => {
|
||||||
files.push({
|
files.push({
|
||||||
name: file,
|
name: file,
|
||||||
|
path: file,
|
||||||
type: "file",
|
type: "file",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
files.push({
|
files.push({
|
||||||
name: project.name,
|
name: project.name,
|
||||||
|
path: project.name,
|
||||||
type: "directory",
|
type: "directory",
|
||||||
folded: true,
|
folded: true,
|
||||||
children: sortFiles(
|
children: sortFiles(
|
||||||
project.files.map(file => ({
|
project.files.map(file => ({
|
||||||
name: file,
|
name: file,
|
||||||
|
path: `${project.name}/${file}`,
|
||||||
type: "file",
|
type: "file",
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user