From 9531ab700648f7524e687e74b9f54e8c9078566b Mon Sep 17 00:00:00 2001 From: Daniel Kluge Date: Tue, 21 Jun 2022 18:44:20 +0200 Subject: [PATCH] Add save function --- components/ProjectModal.tsx | 2 +- components/REPL/REPLHistory.tsx | 2 +- components/REPL/REPLInput.tsx | 18 ++++- components/REPL/index.tsx | 4 +- .../commands/ContextProvider.tsx | 11 +-- lib/commands/definitions.ts | 71 +++++++++++++++++-- lib/commands/index.ts | 9 +-- lib/commands/types.tsx | 8 +++ pages/_app.tsx | 2 +- pages/index.tsx | 2 +- 10 files changed, 99 insertions(+), 30 deletions(-) rename components/contexts/CommandInterface.tsx => lib/commands/ContextProvider.tsx (67%) diff --git a/components/ProjectModal.tsx b/components/ProjectModal.tsx index 981696f..86ab7bc 100644 --- a/components/ProjectModal.tsx +++ b/components/ProjectModal.tsx @@ -3,7 +3,7 @@ import { useEffect, useRef, useState, isValidElement } from "react"; import { useRouter } from "next/router"; import styles from "../styles/ProjectModal.module.css"; import type { Project, Diary } from "../lib/content/types"; -import { useCommands } from "./contexts/CommandInterface"; +import { useCommands } from "../lib/commands/ContextProvider"; import { generateContent, projectEmpty } from "../lib/content/generate"; import { useModalFunctions } from "./contexts/ModalFunctions"; import Spinner from "./Spinner"; diff --git a/components/REPL/REPLHistory.tsx b/components/REPL/REPLHistory.tsx index 27cddf8..d554a0f 100644 --- a/components/REPL/REPLHistory.tsx +++ b/components/REPL/REPLHistory.tsx @@ -87,7 +87,7 @@ const REPLHistory: NextPage = ({history, inputRef}) => { }; return
- { history.map((value, idx) =>
+ { history.map((value, idx) =>
{parseLine(value)}
) } diff --git a/components/REPL/REPLInput.tsx b/components/REPL/REPLInput.tsx index 033a32e..8258200 100644 --- a/components/REPL/REPLInput.tsx +++ b/components/REPL/REPLInput.tsx @@ -1,8 +1,8 @@ import type { NextPage } from "next"; -import { MutableRefObject, useState, createRef } from "react"; +import { MutableRefObject, useState, createRef, useEffect } from "react"; import { CommandInterface } from "../../lib/commands"; import styles from "../../styles/REPL/REPLInput.module.css"; -import { useCommands } from "../contexts/CommandInterface"; +import { useCommands } from "../../lib/commands/ContextProvider"; import { useModalFunctions } from "../contexts/ModalFunctions"; interface REPLInputParams { @@ -19,9 +19,11 @@ const REPLInput: NextPage = ({historyCallback, historyClear, in const [inCmdHistory, setInCmdHistory] = useState(-1); const [cmdHistory, setCmdHistory] = useState([]); const [usrInputTmp, setUsrInputTmp] = useState(""); - const {cmdContext: cmdIf} = useCommands(); + const {cmdContext: cmdIf, updateCallbacks} = useCommands(); const { modalFunctions } = useModalFunctions(); + updateCallbacks({ getCmdHistory: () => cmdHistory }); + const setInput = (inputRef: HTMLInputElement, input: string) => { const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set; if (!nativeSetter) return; @@ -145,6 +147,16 @@ const REPLInput: NextPage = ({historyCallback, historyClear, in } }; + useEffect(() => { + if (!window || !cmdIf) return; + const color = window.localStorage.getItem("color"); + if(color) cmdIf.executeCommand(`color ${color}`); + const history = window.localStorage.getItem("history"); + try { + if (history) setCmdHistory(JSON.parse(history)); + } catch {} + }, [cmdIf]); + return
diff --git a/components/REPL/index.tsx b/components/REPL/index.tsx index b43f33c..613240b 100644 --- a/components/REPL/index.tsx +++ b/components/REPL/index.tsx @@ -2,7 +2,8 @@ import { MutableRefObject, useEffect, useRef, useState } from "react"; import REPLInput from "./REPLInput"; import REPLHistory from "./REPLHistory"; import styles from "../../styles/REPL/REPLComplete.module.css"; -import type { NextPage, GetStaticProps } from "next"; +import type { NextPage } from "next"; +import { useCommands } from "../../lib/commands/ContextProvider"; interface IREPLProps { inputRef: MutableRefObject; @@ -12,6 +13,7 @@ interface IREPLProps { const REPL: NextPage = ({ inputRef, buildTime }) => { const [history, manipulateHistory] = useState([`cer0 0S - Build ${buildTime}`]); const containerRef = useRef(null); + const onCommandExecuted = (result: string[]) => manipulateHistory(result.reverse().concat(history).slice(0, 1000)); const onClearHistory = () => manipulateHistory([]); diff --git a/components/contexts/CommandInterface.tsx b/lib/commands/ContextProvider.tsx similarity index 67% rename from components/contexts/CommandInterface.tsx rename to lib/commands/ContextProvider.tsx index 0a1ee63..842c383 100644 --- a/components/contexts/CommandInterface.tsx +++ b/lib/commands/ContextProvider.tsx @@ -1,13 +1,8 @@ import { createContext, useContext } from "react"; import type { PropsWithChildren } from "react"; -import { CommandInterface } from "../../lib/commands"; -import type { Diary, Project, ContentList } from "../../lib/content/types"; - -interface CommandInterfaceCallbacks { - setModalVisible?: (visible: boolean) => void; - setModalContent?: (content: Project | Diary, selectedPage?: number) => void; - setModalHTML?: (html: any) => void; -} +import { CommandInterface } from "."; +import type { ContentList } from "../content/types"; +import type { CommandInterfaceCallbacks } from "./types"; const commandInterface = new CommandInterface(); const CommandContext = createContext(commandInterface); diff --git a/lib/commands/definitions.ts b/lib/commands/definitions.ts index f1dbc69..6bda24a 100644 --- a/lib/commands/definitions.ts +++ b/lib/commands/definitions.ts @@ -210,6 +210,19 @@ const clear: Command = { execute: () => [] }; +const getColors = () => { + const replColor = window.document.documentElement.style.getPropertyValue("--repl-color") || window.getComputedStyle(document.documentElement).getPropertyValue("--repl-color") || "rgb(24, 138, 24)"; + const linkColor = window.document.documentElement.style.getPropertyValue("--repl-color-link") || window.getComputedStyle(document.documentElement).getPropertyValue("--repl-color-link") || "rgb(31, 179, 31)"; + const hintColor = window.document.documentElement.style.getPropertyValue("--repl-color-hint") || window.getComputedStyle(document.documentElement).getPropertyValue("--repl-color-hint") || "rgba(24, 138, 24, 0.3)"; + return [replColor, linkColor, hintColor]; +}; + +const setColors = (color: Color) => { + window?.document.documentElement.style.setProperty("--repl-color", color.string()); + window?.document.documentElement.style.setProperty("--repl-color-link", color.lighten(0.3).rgb().string()); + window?.document.documentElement.style.setProperty("--repl-color-hint", color.fade(0.7).string()); +}; + const color: Command = { name: "color", desc: "Changes the color of the site.", @@ -219,7 +232,15 @@ const color: Command = { }, execute: (_flags, args, _raw, cmdIf) => { if (!window || !window.document) return []; - if (args.length !== 1) return printSyntax(color); + if (args.length === 0) { + const colors = getColors(); + return [ + "Current colors:", + `Text:\t\t${colors[0]}`, + `Links:\t\t${colors[1]}`, + `Completion:\t${colors[2]}` + ]; + } if (args[0] === "reset") { window.document.documentElement.style.removeProperty("--repl-color"); window.document.documentElement.style.removeProperty("--repl-color-link"); @@ -228,15 +249,12 @@ const color: Command = { } else { let color: Color; try { - color = Color(args.join().trim()); + color = Color(args.join(" ").trim()); } catch { return ["Invalid color!"]; } - window?.document.documentElement.style.setProperty("--repl-color", color.string()); - window?.document.documentElement.style.setProperty("--repl-color-link", color.lighten(0.3).rgb().string()); - window?.document.documentElement.style.setProperty("--repl-color-hint", color.fade(0.7).string()); + setColors(color); - console.log(color.hex().toLowerCase()); switch(true) { case color.hex().toLowerCase() === "#1f1e33": { // eslint-disable-next-line quotes @@ -251,4 +269,43 @@ const color: Command = { } }; -export const commandList = [about, help, man, project, exitCmd, clear, color].sort((a, b) => a.name.localeCompare(b.name)); \ No newline at end of file +const save: Command = { + name: "save", + desc: "Saves the current color and command history to local storage.", + subcommands: { + clear: { name: "clear", desc: "Clear the saved data." }, + confirm: { name: "confirm", desc: "You explicitly confirm, you allow saving to the local storage." } + }, + execute: (_flags, args, _raw, cmdIf) => { + const defaultRet = [ + "You can save the current color and command history to local storage.", + "To do so, use %{save confirm}.", + "By using this command above you agree on saving the non-functional data to local storage.", + "The data will never leave your computer!" + ]; + + if (args.length === 0) { + return defaultRet; + } else if (args[0] === "clear") { + window.localStorage.clear(); + return ["Colors and history removed from storage."]; + } else if (args[0] === "confirm") { + const result = []; + + const currentColors = getColors(); + const color = new Color(currentColors[0]); + if(color.contrast(new Color("#000")) === 1 || color.alpha() < 0.1) result.push("Skipping saving the color because it's too dark."); + else window.localStorage.setItem("color", currentColors[0]); + + const history = cmdIf.callbacks?.getCmdHistory ? cmdIf.callbacks.getCmdHistory() : []; + window.localStorage.setItem("history", JSON.stringify(history)); + + result.push("Colors and history saved to storage."); + return result; + } else { + return printSyntax(save); + } + }, +}; + +export const commandList = [about, help, man, project, exitCmd, clear, color, save].sort((a, b) => a.name.localeCompare(b.name)); \ No newline at end of file diff --git a/lib/commands/index.ts b/lib/commands/index.ts index a7ae1ee..9cdda2e 100644 --- a/lib/commands/index.ts +++ b/lib/commands/index.ts @@ -1,11 +1,6 @@ -import type { ContentList, Diary, Project } from "../content/types"; +import type { ContentList } from "../content/types"; import { printSyntax, commandList } from "./definitions"; - -interface CommandInterfaceCallbacks { - setModalVisible?: (visible: boolean) => void; - setModalContent?: (content: Project|Diary, selectedPage?: number) => void; - setModalHTML?: (html: string) => void; -} +import { CommandInterfaceCallbacks } from "./types"; export class CommandInterface { callbacks?: CommandInterfaceCallbacks; diff --git a/lib/commands/types.tsx b/lib/commands/types.tsx index c4b6bbd..fc87e91 100644 --- a/lib/commands/types.tsx +++ b/lib/commands/types.tsx @@ -1,4 +1,5 @@ import type { CommandInterface } from "."; +import type { Diary, Project } from "../content/types"; export interface Flag { short: string; @@ -19,3 +20,10 @@ export interface Command { subcommands?: Record; execute: (flags: string[], args: string[], raw: string, cmdIf: CommandInterface) => string[]; } + +export interface CommandInterfaceCallbacks { + setModalVisible?: (visible: boolean) => void; + setModalContent?: (content: Project | Diary, selectedPage?: number) => void; + setModalHTML?: (html: any) => void; + getCmdHistory?: () => string[]; +} \ No newline at end of file diff --git a/pages/_app.tsx b/pages/_app.tsx index 00fa758..0f040c1 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -2,7 +2,7 @@ import type { AppProps } from "next/app"; import Head from "next/head"; import "../styles/globals.css"; import "../styles/customAsciidoc.scss"; -import { CommandsProvider } from "../components/contexts/CommandInterface"; +import { CommandsProvider } from "../lib/commands/ContextProvider"; import { ModalFunctionProvider } from "../components/contexts/ModalFunctions"; function MyApp({ Component, pageProps }: AppProps) { diff --git a/pages/index.tsx b/pages/index.tsx index 3ad326a..ff20dc9 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -2,7 +2,7 @@ import type { NextPage, GetStaticProps } from "next"; import Head from "next/head"; import { GithubLogo, InstagramLogo, DiscordLogo, GameController } from "phosphor-react"; import { useEffect, useRef,useCallback } from "react"; -import { useCommands } from "../components/contexts/CommandInterface"; +import { useCommands } from "../lib/commands/ContextProvider"; import { useModalFunctions } from "../components/contexts/ModalFunctions"; import ProjectModal from "../components/ProjectModal"; import REPL from "../components/REPL";