refactor: rework assets fetching
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -30,4 +30,4 @@ yarn-error.log*
|
|||||||
.notes
|
.notes
|
||||||
|
|
||||||
# Generated
|
# Generated
|
||||||
/src/manifest.ts
|
/src/assets.ts
|
||||||
|
|||||||
@@ -4,6 +4,24 @@ import { spawnSync } from "child_process";
|
|||||||
import { Octokit } from "@octokit/rest";
|
import { Octokit } from "@octokit/rest";
|
||||||
import { env } from "./env";
|
import { env } from "./env";
|
||||||
|
|
||||||
|
type Manifest = {
|
||||||
|
files: string[];
|
||||||
|
projects: string[];
|
||||||
|
links: {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
icon: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type Project = {
|
||||||
|
name: string;
|
||||||
|
content: string;
|
||||||
|
language: string | null;
|
||||||
|
url: string;
|
||||||
|
private: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export const manifest = (): Plugin => ({
|
export const manifest = (): Plugin => ({
|
||||||
name: "generate-pages-plugin",
|
name: "generate-pages-plugin",
|
||||||
buildStart: async () => {
|
buildStart: async () => {
|
||||||
@@ -15,12 +33,12 @@ export const manifest = (): Plugin => ({
|
|||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
const storedUpdatedAt = (
|
const storedUpdatedAt = (
|
||||||
await readFile("./node_modules/.cache/manifest")
|
await readFile("./node_modules/.cache/assets")
|
||||||
).toString();
|
).toString();
|
||||||
if (storedUpdatedAt === manifestRepo.updated_at) return;
|
if (storedUpdatedAt === manifestRepo.updated_at) return;
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
await writeFile("./node_modules/.cache/manifest", manifestRepo.updated_at);
|
await writeFile("./node_modules/.cache/assets", manifestRepo.updated_at);
|
||||||
|
|
||||||
const getRepoFileContent = async (repo: string, path: string) => {
|
const getRepoFileContent = async (repo: string, path: string) => {
|
||||||
const { data: file } = await octokit.repos.getContent({
|
const { data: file } = await octokit.repos.getContent({
|
||||||
@@ -36,18 +54,9 @@ export const manifest = (): Plugin => ({
|
|||||||
|
|
||||||
const manifest = JSON.parse(
|
const manifest = JSON.parse(
|
||||||
await getRepoFileContent(env.GITHUB_USERNAME, "manifest.json"),
|
await getRepoFileContent(env.GITHUB_USERNAME, "manifest.json"),
|
||||||
) as {
|
) as Manifest;
|
||||||
files: string[];
|
|
||||||
projects: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const projects: Array<{
|
const projects: Array<Project> = [];
|
||||||
name: string;
|
|
||||||
content: string;
|
|
||||||
language: string | null;
|
|
||||||
url: string;
|
|
||||||
private: boolean;
|
|
||||||
}> = [];
|
|
||||||
for (const project of manifest.projects) {
|
for (const project of manifest.projects) {
|
||||||
const { data: repo } = await octokit.repos.get({
|
const { data: repo } = await octokit.repos.get({
|
||||||
owner: env.GITHUB_USERNAME,
|
owner: env.GITHUB_USERNAME,
|
||||||
@@ -67,18 +76,16 @@ export const manifest = (): Plugin => ({
|
|||||||
const code = `
|
const code = `
|
||||||
const projects = ${JSON.stringify(projects, null, 2)} as const;
|
const projects = ${JSON.stringify(projects, null, 2)} as const;
|
||||||
|
|
||||||
export type Project = typeof projects[number];
|
const links = ${JSON.stringify(manifest.links, null, 2)} as const;
|
||||||
|
|
||||||
const projectsMap = Object.fromEntries(projects.map(project => [project.name, project])) as const;
|
export const assets = {
|
||||||
|
|
||||||
export const manifest = {
|
|
||||||
projects,
|
projects,
|
||||||
projectsMap
|
links
|
||||||
};
|
};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
await writeFile("./src/manifest.ts", code);
|
await writeFile("./src/assets.ts", code);
|
||||||
|
|
||||||
spawnSync("prettier", ["--write", "./src/manifest.ts"]);
|
spawnSync("prettier", ["--write", "./src/assets.ts"]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ const InnerCava = (props: InnerKittyProps<typeof Cava>) => {
|
|||||||
const analyser = audioContext.createAnalyser();
|
const analyser = audioContext.createAnalyser();
|
||||||
analyser.fftSize = 256;
|
analyser.fftSize = 256;
|
||||||
|
|
||||||
console.log("ok");
|
|
||||||
void audioElement.play().then(() => void audioContext.resume());
|
void audioElement.play().then(() => void audioContext.resume());
|
||||||
|
|
||||||
if (!sourceRef.current) {
|
if (!sourceRef.current) {
|
||||||
@@ -132,7 +131,7 @@ const InnerCava = (props: InnerKittyProps<typeof Cava>) => {
|
|||||||
return () => {
|
return () => {
|
||||||
if (requestRef.current) cancelAnimationFrame(requestRef.current);
|
if (requestRef.current) cancelAnimationFrame(requestRef.current);
|
||||||
};
|
};
|
||||||
}, [props.cols, props.audio]);
|
}, [props.cols, props.audio, calculateBarHeights]);
|
||||||
|
|
||||||
return barHeights.map((value, i) => (
|
return barHeights.map((value, i) => (
|
||||||
<FrequencyBar key={i} value={value} max={255 / 2} height={props.rows} />
|
<FrequencyBar key={i} value={value} max={255 / 2} height={props.rows} />
|
||||||
|
|||||||
@@ -1,41 +1,9 @@
|
|||||||
import axios from "axios";
|
import { useState } from "react";
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
export const NvimEditor = (props: { source: string | undefined }) => {
|
|
||||||
const [cache, setCache] = useState(new Map<string, string>());
|
|
||||||
const [data, setData] = useState<string>();
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
|
export const NvimEditor = (props: { content: string | undefined }) => {
|
||||||
const [selectedLine, setSelectedLine] = useState(0);
|
const [selectedLine, setSelectedLine] = useState(0);
|
||||||
|
|
||||||
useEffect(() => {
|
let rows = props.content?.split("\n") ?? [];
|
||||||
if (!props.source) return;
|
|
||||||
|
|
||||||
const cached = cache.get(props.source);
|
|
||||||
if (cached) {
|
|
||||||
console.log("cache hit");
|
|
||||||
setData(cached);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
axios
|
|
||||||
.get<string>(props.source)
|
|
||||||
.then(({ data }) => {
|
|
||||||
setData(data);
|
|
||||||
setLoading(false);
|
|
||||||
|
|
||||||
setCache((cache) => {
|
|
||||||
cache.set(props.source!, data);
|
|
||||||
return cache;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
}, [props.source]);
|
|
||||||
|
|
||||||
let rows = data?.split("\n") ?? [];
|
|
||||||
// trim end empty lines
|
// trim end empty lines
|
||||||
for (let i = rows.length - 1; i >= 0; i--) {
|
for (let i = rows.length - 1; i >= 0; i--) {
|
||||||
if (rows[i].trim().length === 0) {
|
if (rows[i].trim().length === 0) {
|
||||||
@@ -51,9 +19,7 @@ export const NvimEditor = (props: { source: string | undefined }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return loading ? (
|
return (
|
||||||
<p>Loading...</p>
|
|
||||||
) : (
|
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
{rows.map((row, i) => (
|
{rows.map((row, i) => (
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { ICONS, getIcon } from "~/utils/icons";
|
import { DEFAULT_ICON } from "~/utils/icons";
|
||||||
|
import { type Icon } from "~/utils/tree";
|
||||||
|
|
||||||
export const NvimStatusBar = (props: {
|
export const NvimStatusBar = (props: {
|
||||||
label: string;
|
label: string;
|
||||||
labelColor: string;
|
labelColor: string;
|
||||||
fileIcon?: string;
|
fileIcon?: Icon;
|
||||||
fileName: string;
|
fileName?: string;
|
||||||
}) => (
|
}) => (
|
||||||
<div className="select-none bg-[#29293c]">
|
<div className="select-none bg-[#29293c]">
|
||||||
<span className="text-[#272332]" style={{ background: props.labelColor }}>
|
<span className="text-[#272332]" style={{ background: props.labelColor }}>
|
||||||
@@ -15,14 +16,8 @@ export const NvimStatusBar = (props: {
|
|||||||
</span>
|
</span>
|
||||||
<span className="bg-[#474353] text-[#373040]">{"\ue0ba"}</span>
|
<span className="bg-[#474353] text-[#373040]">{"\ue0ba"}</span>
|
||||||
<span className="bg-[#373040]">{` ${
|
<span className="bg-[#373040]">{` ${
|
||||||
getIcon({
|
props.fileIcon?.char ?? DEFAULT_ICON.char
|
||||||
type: "file",
|
}${props.fileName ?? "Empty"} `}</span>
|
||||||
name: props.fileName,
|
|
||||||
icon: props.fileIcon,
|
|
||||||
fileName: props.fileName,
|
|
||||||
repo: "",
|
|
||||||
}).char
|
|
||||||
}${props.fileName} `}</span>
|
|
||||||
<span className="bg-[#373040] text-[#29293c]">{"\ue0ba"}</span>
|
<span className="bg-[#373040] text-[#29293c]">{"\ue0ba"}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
34
src/components/Nvim/NvimTree/NvimTreeChild.tsx
Normal file
34
src/components/Nvim/NvimTree/NvimTreeChild.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { DEFAULT_ICON } from "~/utils/icons";
|
||||||
|
import { type Child } from "~/utils/tree";
|
||||||
|
|
||||||
|
export const NvimTreeChild = (props: {
|
||||||
|
child: Child;
|
||||||
|
y: number;
|
||||||
|
selected: boolean;
|
||||||
|
inDirectory: boolean | "last";
|
||||||
|
onSelect: (y: number) => void;
|
||||||
|
onOpen: (file: Child) => void;
|
||||||
|
}) => {
|
||||||
|
const icon = props.child.icon ?? DEFAULT_ICON;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
style={{ background: props.selected ? "#504651" : "" }}
|
||||||
|
onMouseDown={() => props.onSelect(props.y)}
|
||||||
|
onDoubleClick={() => props.onOpen(props.child)}
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
{props.inDirectory && (
|
||||||
|
<span className="text-[#5b515b]">
|
||||||
|
{props.inDirectory === "last" ? "└ " : "│ "}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<span style={{ color: icon.color }}>{`${icon.char}`}</span>
|
||||||
|
{props.child.name === "README.md" ? (
|
||||||
|
<span className="font-bold text-[#d8c5a1]">README.md</span>
|
||||||
|
) : (
|
||||||
|
<span>{props.child.name}</span>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Directory } from "~/utils/types";
|
import { type Folder } from "~/utils/tree";
|
||||||
|
|
||||||
export const NvimTreeDirectory = (props: {
|
export const NvimTreeDirectory = (props: {
|
||||||
directory: Directory;
|
directory: Folder;
|
||||||
y: number;
|
y: number;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
onSelect: (y: number) => void;
|
onSelect: (y: number) => void;
|
||||||
onOpen: (directory: Directory) => void;
|
onOpen: (directory: Folder) => void;
|
||||||
}) => (
|
}) => (
|
||||||
<li
|
<li
|
||||||
className="text-[#a0b6ee]"
|
className="text-[#a0b6ee]"
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
import { getIcon } from "~/utils/icons";
|
|
||||||
import { File } from "~/utils/types";
|
|
||||||
|
|
||||||
export const NvimTreeFile = (props: {
|
|
||||||
file: File;
|
|
||||||
y: number;
|
|
||||||
selected: boolean;
|
|
||||||
inDirectory: boolean | "last";
|
|
||||||
onSelect: (y: number) => void;
|
|
||||||
onOpen: (file: File) => void;
|
|
||||||
}) => (
|
|
||||||
<li
|
|
||||||
style={{ background: props.selected ? "#504651" : "" }}
|
|
||||||
onMouseDown={() => props.onSelect(props.y)}
|
|
||||||
onDoubleClick={() => props.onOpen(props.file)}
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
{props.inDirectory && (
|
|
||||||
<span className="text-[#5b515b]">
|
|
||||||
{props.inDirectory === "last" ? "└ " : "│ "}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<span style={{ color: getIcon(props.file).color }}>{`${
|
|
||||||
getIcon(props.file).char
|
|
||||||
}`}</span>
|
|
||||||
{props.file.name === "README.md" ? (
|
|
||||||
<span className="font-bold text-[#d8c5a1]">README.md</span>
|
|
||||||
) : (
|
|
||||||
<span>{props.file.name}</span>
|
|
||||||
)}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
@@ -1,65 +1,44 @@
|
|||||||
import { useApp } from "~/hooks/useApp";
|
import { useApp } from "~/hooks/useApp";
|
||||||
import { CHAR_HEIGHT, CHAR_WIDTH } from "../../Kitty";
|
import { CHAR_HEIGHT, CHAR_WIDTH } from "../../Kitty";
|
||||||
import { type ReactNode, useEffect, useState } from "react";
|
import { type ReactNode, useEffect, useState } from "react";
|
||||||
import {
|
import { type InnerKittyProps } from "~/utils/types";
|
||||||
type File,
|
|
||||||
type InnerKittyProps,
|
|
||||||
type RootManifest,
|
|
||||||
type Directory,
|
|
||||||
} from "~/utils/types";
|
|
||||||
import { type Nvim } from "..";
|
import { type Nvim } from "..";
|
||||||
import { NvimTreeDirectory } from "./NvimTreeDirectory";
|
import { NvimTreeDirectory } from "./NvimTreeDirectory";
|
||||||
import { NvimTreeFile } from "./NvimTreeFile";
|
import { NvimTreeChild } from "./NvimTreeChild";
|
||||||
|
import { assets } from "~/assets";
|
||||||
|
import { getIcon } from "~/utils/icons";
|
||||||
|
import {
|
||||||
|
file,
|
||||||
|
folder,
|
||||||
|
project,
|
||||||
|
sortFiles,
|
||||||
|
link,
|
||||||
|
type Child,
|
||||||
|
} from "~/utils/tree";
|
||||||
|
|
||||||
const sortFiles = (files: Array<File | Directory>) =>
|
const buildTree = () =>
|
||||||
files
|
|
||||||
.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,
|
|
||||||
);
|
|
||||||
|
|
||||||
const manifestToTree = (manifest: RootManifest) =>
|
|
||||||
sortFiles([
|
sortFiles([
|
||||||
{
|
folder(
|
||||||
type: "directory",
|
"links",
|
||||||
name: "links",
|
assets.links.map((l) => link(l.name, l.url, l.icon)),
|
||||||
opened: false,
|
),
|
||||||
files: manifest.links.map((link) => ({
|
folder(
|
||||||
type: "link" as const,
|
"projects",
|
||||||
...link,
|
assets.projects.map((p) =>
|
||||||
})),
|
project(p.name, p.content, p.url, p.language, p.private),
|
||||||
},
|
),
|
||||||
{
|
),
|
||||||
type: "directory",
|
file("README.md", "hey", getIcon("README.md")),
|
||||||
name: "projects",
|
|
||||||
opened: false,
|
|
||||||
files: manifest.projects.map((project) => ({
|
|
||||||
type: "file" as const,
|
|
||||||
repo: project.name,
|
|
||||||
fileName: "README.md",
|
|
||||||
...project,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
...manifest.files.map((file) => ({
|
|
||||||
type: "file" as const,
|
|
||||||
name: file,
|
|
||||||
repo: "pihkaal",
|
|
||||||
fileName: file,
|
|
||||||
})),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const NvimTree = (
|
export const NvimTree = (
|
||||||
props: InnerKittyProps<typeof Nvim> & {
|
props: InnerKittyProps<typeof Nvim> & {
|
||||||
onOpen: (file: File) => void;
|
onOpen: (file: Child) => void;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const { rootManifest, activeKitty } = useApp();
|
const { activeKitty } = useApp();
|
||||||
|
|
||||||
const [files, setFiles] = useState(manifestToTree(rootManifest));
|
const [files, setFiles] = useState(buildTree());
|
||||||
const [selectedY, setSelectedY] = useState(0);
|
const [selectedY, setSelectedY] = useState(0);
|
||||||
|
|
||||||
const tree: Array<ReactNode> = [];
|
const tree: Array<ReactNode> = [];
|
||||||
@@ -67,7 +46,7 @@ export const NvimTree = (
|
|||||||
let selectedFile = files[0];
|
let selectedFile = files[0];
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (selectedY === y) selectedFile = file;
|
if (selectedY === y) selectedFile = file;
|
||||||
if (file.type === "directory") {
|
if (file.type === "folder") {
|
||||||
tree.push(
|
tree.push(
|
||||||
<NvimTreeDirectory
|
<NvimTreeDirectory
|
||||||
key={y}
|
key={y}
|
||||||
@@ -83,15 +62,15 @@ export const NvimTree = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (file.opened) {
|
if (file.opened) {
|
||||||
file.files.forEach((childFile, i) => {
|
file.children.forEach((child, i) => {
|
||||||
y++;
|
y++;
|
||||||
if (selectedY === y) selectedFile = childFile;
|
if (selectedY === y) selectedFile = child;
|
||||||
tree.push(
|
tree.push(
|
||||||
<NvimTreeFile
|
<NvimTreeChild
|
||||||
key={y}
|
key={y}
|
||||||
file={childFile}
|
child={child}
|
||||||
y={y}
|
y={y}
|
||||||
inDirectory={i === file.files.length - 1 ? "last" : true}
|
inDirectory={i === file.children.length - 1 ? "last" : true}
|
||||||
selected={selectedY === y}
|
selected={selectedY === y}
|
||||||
onSelect={setSelectedY}
|
onSelect={setSelectedY}
|
||||||
onOpen={props.onOpen}
|
onOpen={props.onOpen}
|
||||||
@@ -101,9 +80,9 @@ export const NvimTree = (
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tree.push(
|
tree.push(
|
||||||
<NvimTreeFile
|
<NvimTreeChild
|
||||||
key={y}
|
key={y}
|
||||||
file={file}
|
child={file}
|
||||||
y={y}
|
y={y}
|
||||||
inDirectory={false}
|
inDirectory={false}
|
||||||
selected={selectedY === y}
|
selected={selectedY === y}
|
||||||
@@ -129,7 +108,7 @@ export const NvimTree = (
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "Enter":
|
case "Enter":
|
||||||
if (selectedFile.type === "directory") {
|
if (selectedFile.type === "folder") {
|
||||||
selectedFile.opened = !selectedFile.opened;
|
selectedFile.opened = !selectedFile.opened;
|
||||||
setFiles([...files]);
|
setFiles([...files]);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,30 +5,27 @@ import { NvimInput } from "./NvimInput";
|
|||||||
import { NvimStatusBar } from "./NvimStatusBar";
|
import { NvimStatusBar } from "./NvimStatusBar";
|
||||||
import { NvimTree } from "./NvimTree";
|
import { NvimTree } from "./NvimTree";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { File, InnerKittyProps } from "~/utils/types";
|
import { type InnerKittyProps } from "~/utils/types";
|
||||||
|
import { type Child, type Icon } from "~/utils/tree";
|
||||||
|
|
||||||
export const Nvim = (_props: {}) => {
|
export const Nvim = (_props: unknown) => {
|
||||||
const kitty = useKitty();
|
const kitty = useKitty();
|
||||||
|
|
||||||
return kitty && <InnerNvimTree {...kitty} />;
|
return kitty && <InnerNvimTree {...kitty} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const InnerNvimTree = (props: InnerKittyProps<typeof Nvim>) => {
|
const InnerNvimTree = (props: InnerKittyProps<typeof Nvim>) => {
|
||||||
const [activeFile, setActiveFile] = useState<{
|
const [activeChild, setActiveChild] = useState<{
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
content: string;
|
||||||
icon?: string;
|
icon: Icon;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const handleOpenFile = (file: File) => {
|
const handleOpenChild = (child: Child) => {
|
||||||
if (file.type === "link") {
|
if (child.type === "link") {
|
||||||
window.open(file.url, "_blank")?.focus();
|
window.open(child.url, "_blank")?.focus();
|
||||||
} else {
|
} else {
|
||||||
setActiveFile({
|
setActiveChild(child);
|
||||||
name: file.repo === "pihkaal" ? file.fileName : file.repo,
|
|
||||||
icon: file.icon,
|
|
||||||
url: `https://raw.githubusercontent.com/pihkaal/${file.repo}/main/${file.fileName}`,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,7 +40,7 @@ const InnerNvimTree = (props: InnerKittyProps<typeof Nvim>) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ gridArea: "1 / 1 / 1 / 2" }}>
|
<div style={{ gridArea: "1 / 1 / 1 / 2" }}>
|
||||||
<NvimTree {...props} onOpen={handleOpenFile} />
|
<NvimTree {...props} onOpen={handleOpenChild} />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="overflow-y-auto break-all"
|
className="overflow-y-auto break-all"
|
||||||
@@ -53,14 +50,14 @@ const InnerNvimTree = (props: InnerKittyProps<typeof Nvim>) => {
|
|||||||
wordWrap: "break-word",
|
wordWrap: "break-word",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NvimEditor source={activeFile?.url} />
|
<NvimEditor content={activeChild?.content} />
|
||||||
</div>
|
</div>
|
||||||
<div style={{ gridArea: "2 / 1 / 2 / 3" }}>
|
<div style={{ gridArea: "2 / 1 / 2 / 3" }}>
|
||||||
<NvimStatusBar
|
<NvimStatusBar
|
||||||
label=" NORMAL"
|
label=" NORMAL"
|
||||||
labelColor="#7ea7ca"
|
labelColor="#7ea7ca"
|
||||||
fileIcon={activeFile?.icon}
|
fileIcon={activeChild?.icon}
|
||||||
fileName={activeFile?.name ?? `NvimTree_1`}
|
fileName={activeChild?.name}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ gridArea: "3 / 1 / 3 / 3" }}>
|
<div style={{ gridArea: "3 / 1 / 3 / 3" }}>
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { createContext } from "react";
|
import { createContext } from "react";
|
||||||
import { type RootManifest } from "~/utils/types";
|
|
||||||
|
|
||||||
export const AppContext = createContext<
|
export const AppContext = createContext<
|
||||||
| {
|
| {
|
||||||
rootManifest: RootManifest;
|
|
||||||
activeKitty: string;
|
activeKitty: string;
|
||||||
setActiveKitty: (value: string) => void;
|
setActiveKitty: (value: string) => void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,11 @@
|
|||||||
import axios from "axios";
|
import { type ReactNode, useState } from "react";
|
||||||
import { type ReactNode, useState, useEffect } from "react";
|
|
||||||
import { AppContext } from "~/context/AppContext";
|
import { AppContext } from "~/context/AppContext";
|
||||||
import { type RootManifest } from "~/utils/types";
|
|
||||||
|
|
||||||
export const AppProvider = (props: { children?: ReactNode }) => {
|
export const AppProvider = (props: { children?: ReactNode }) => {
|
||||||
const [activeKitty, setActiveKitty] = useState(":r0:");
|
const [activeKitty, setActiveKitty] = useState(":r0:");
|
||||||
const [rootManifest, setRootManifest] = useState<RootManifest>({
|
|
||||||
files: ["README.md", "pubkey.asc"],
|
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: "me",
|
|
||||||
icon: "ts",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "tlock",
|
|
||||||
icon: "rs",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
links: [
|
|
||||||
{
|
|
||||||
name: "github",
|
|
||||||
url: "https://github.com/pihkaal",
|
|
||||||
icon: "github",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "instagram",
|
|
||||||
url: "https://instagram.com/pihkaal",
|
|
||||||
icon: "instagram",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return;
|
|
||||||
void axios
|
|
||||||
.get<RootManifest>(
|
|
||||||
"https://raw.githubusercontent.com/pihkaal/pihkaal/main/manifest.json",
|
|
||||||
)
|
|
||||||
.then((x) => setRootManifest(x.data));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (!rootManifest) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppContext.Provider value={{ rootManifest, activeKitty, setActiveKitty }}>
|
<AppContext.Provider value={{ activeKitty, setActiveKitty }}>
|
||||||
{rootManifest && props.children}
|
{props.children}
|
||||||
</AppContext.Provider>
|
</AppContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,48 +1,48 @@
|
|||||||
import { File, Icon } from "./types";
|
import { type Child, type Icon } from "./tree";
|
||||||
|
|
||||||
export const getIcon = (file: File | string | undefined): Icon => {
|
export const getIcon = (file: Child | string | undefined): Icon => {
|
||||||
if (file === undefined) return ICONS["UNKNOWN"];
|
if (!file) return DEFAULT_ICON;
|
||||||
|
|
||||||
let iconName;
|
|
||||||
if (typeof file === "string") {
|
if (typeof file === "string") {
|
||||||
const parts = file.split(".");
|
const parts = file.split(".");
|
||||||
iconName = parts[parts.length - 1];
|
const iconName = parts[parts.length - 1];
|
||||||
} else {
|
|
||||||
iconName = file.icon;
|
return ICONS[EXT_TO_LANGUAGE[iconName]] ?? DEFAULT_ICON;
|
||||||
if (!iconName) {
|
|
||||||
const parts = file.name.split(".");
|
|
||||||
iconName = parts[parts.length - 1];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ICONS[iconName]) iconName = "UNKNOWN";
|
return file.icon ?? DEFAULT_ICON;
|
||||||
return ICONS[iconName];
|
};
|
||||||
|
|
||||||
|
export const EXT_TO_LANGUAGE: Record<string, string> = {
|
||||||
|
asc: "Key",
|
||||||
|
md: "Markdown",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ICONS: Record<string, Icon> = {
|
export const ICONS: Record<string, Icon> = {
|
||||||
md: {
|
Markdown: {
|
||||||
char: " ",
|
char: " ",
|
||||||
color: "#89bafa",
|
color: "#89bafa",
|
||||||
},
|
},
|
||||||
asc: {
|
Key: {
|
||||||
char: " ",
|
char: " ",
|
||||||
color: "#f9e2af",
|
color: "#f9e2af",
|
||||||
},
|
},
|
||||||
ts: {
|
TypeScript: {
|
||||||
char: " ",
|
char: " ",
|
||||||
color: "#4d86a2",
|
color: "#4d86a2",
|
||||||
},
|
},
|
||||||
rs: {
|
Rust: {
|
||||||
char: " ",
|
char: " ",
|
||||||
color: "#be8f78",
|
color: "#be8f78",
|
||||||
},
|
},
|
||||||
instagram: {
|
Instagram: {
|
||||||
char: " ",
|
char: " ",
|
||||||
color: "#e1306c",
|
color: "#e1306c",
|
||||||
},
|
},
|
||||||
github: {
|
Github: {
|
||||||
char: " ",
|
char: " ",
|
||||||
color: "#ffffff",
|
color: "#ffffff",
|
||||||
},
|
},
|
||||||
UNKNOWN: { char: " ", color: "#f599ae" },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_ICON = { char: " ", color: "#f599ae" };
|
||||||
|
|||||||
90
src/utils/tree.ts
Normal file
90
src/utils/tree.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import { DEFAULT_ICON, ICONS } from "./icons";
|
||||||
|
|
||||||
|
export type Icon = {
|
||||||
|
char: string;
|
||||||
|
color: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Project = {
|
||||||
|
type: "project";
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
private: boolean;
|
||||||
|
content: string;
|
||||||
|
icon: Icon;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type File = {
|
||||||
|
type: "file";
|
||||||
|
name: string;
|
||||||
|
content: string;
|
||||||
|
icon: Icon;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Link = {
|
||||||
|
type: "link";
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
icon: Icon;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Folder = {
|
||||||
|
name: string;
|
||||||
|
type: "folder";
|
||||||
|
children: Array<Child>;
|
||||||
|
opened: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Child = Link | Project | File;
|
||||||
|
|
||||||
|
export const sortFiles = (files: Array<Folder | Child>) =>
|
||||||
|
files
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
.sort((a, b) =>
|
||||||
|
a.type === "folder" && b.type !== "folder"
|
||||||
|
? -1
|
||||||
|
: a.type !== "folder" && b.type === "folder"
|
||||||
|
? 1
|
||||||
|
: 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
export const folder = (name: string, children: Array<Child>): Folder => ({
|
||||||
|
type: "folder",
|
||||||
|
name,
|
||||||
|
opened: false,
|
||||||
|
children,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const link = (name: string, url: string, icon: Icon): Link => ({
|
||||||
|
type: "link",
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
icon,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const file = (name: string, content: string, icon: Icon): File => ({
|
||||||
|
type: "file",
|
||||||
|
name,
|
||||||
|
content,
|
||||||
|
icon,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const project = (
|
||||||
|
name: string,
|
||||||
|
content: string,
|
||||||
|
url: string,
|
||||||
|
language: string,
|
||||||
|
priv: boolean,
|
||||||
|
): Project => ({
|
||||||
|
type: "project",
|
||||||
|
name,
|
||||||
|
content,
|
||||||
|
url,
|
||||||
|
icon: ICONS[language] ?? DEFAULT_ICON,
|
||||||
|
private: priv,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const icon = (char: string, color: string): Icon => ({
|
||||||
|
char,
|
||||||
|
color,
|
||||||
|
});
|
||||||
@@ -6,39 +6,3 @@ export type Prettify<T> = NonNullable<{ [K in keyof T]: T[K] }>;
|
|||||||
export type InnerKittyProps<T extends (...args: any[]) => any> = Prettify<
|
export type InnerKittyProps<T extends (...args: any[]) => any> = Prettify<
|
||||||
Parameters<T>[0] & KittyContextProps
|
Parameters<T>[0] & KittyContextProps
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type RootManifest = {
|
|
||||||
files: Array<string>;
|
|
||||||
projects: Array<{
|
|
||||||
name: string;
|
|
||||||
icon: string;
|
|
||||||
}>;
|
|
||||||
links: Array<{
|
|
||||||
name: string;
|
|
||||||
url: string;
|
|
||||||
icon: string;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Icon = {
|
|
||||||
char: string;
|
|
||||||
color: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type File = {
|
|
||||||
name: string;
|
|
||||||
} & (
|
|
||||||
| {
|
|
||||||
type: "link";
|
|
||||||
url: string;
|
|
||||||
icon: string;
|
|
||||||
}
|
|
||||||
| { type: "file"; repo: string; fileName: string; icon?: string }
|
|
||||||
);
|
|
||||||
|
|
||||||
export type Directory = {
|
|
||||||
name: string;
|
|
||||||
type: "directory";
|
|
||||||
files: Array<File>;
|
|
||||||
opened: boolean;
|
|
||||||
};
|
|
||||||
|
|||||||
Reference in New Issue
Block a user