From d6bd11f65ef3a5cff4ceff5ae4c4b75dd45f82e2 Mon Sep 17 00:00:00 2001 From: Daniel Kluge Date: Tue, 14 Dec 2021 23:08:18 +0100 Subject: [PATCH] First REPL mock --- .eslintrc.json | 6 +++++- components/REPLInput.tsx | 35 +++++++++++++++++++++++++++++++++++ lib/commands.ts | 7 +++++++ pages/index.tsx | 3 ++- styles/REPLInput.module.css | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 components/REPLInput.tsx create mode 100644 lib/commands.ts create mode 100644 styles/REPLInput.module.css diff --git a/.eslintrc.json b/.eslintrc.json index bffb357..ef752f8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,7 @@ { - "extends": "next/core-web-vitals" + "extends": "next/core-web-vitals", + "rules": { + "semi": ["warn", "always"], + "quotes": ["warn", "double"] + } } diff --git a/components/REPLInput.tsx b/components/REPLInput.tsx new file mode 100644 index 0000000..2a753a8 --- /dev/null +++ b/components/REPLInput.tsx @@ -0,0 +1,35 @@ +import type { NextPage } from "next"; +import React from "react" +import { commandCompletion } from "../lib/commands" +import styles from "../styles/REPLInput.module.css" + +const REPLInput: NextPage = () => { + const typed = React.createRef(); + const completion = React.createRef(); + const [currentCmd, setCurrentCmd] = React.useState(""); + + const replinOnChange = (e: React.FormEvent) => { + const currentInput = (e.target as HTMLInputElement).value; + const suggest = commandCompletion(currentInput); + setCurrentCmd(suggest); + if (typed.current) typed.current.innerHTML = currentInput; + if (completion.current) completion.current.innerHTML = suggest.substring(currentInput.length); + } + + const tabComplete = (e: React.KeyboardEvent) => { + if (e.key === "Tab" && currentCmd !== "") { + e.preventDefault(); + (e.target as HTMLInputElement).value = currentCmd; + if(typed.current) typed.current.innerHTML = currentCmd; + if(completion.current) completion.current.innerHTML = ""; + } + return false; + } + + return
+ + +
+} + +export default REPLInput; \ No newline at end of file diff --git a/lib/commands.ts b/lib/commands.ts new file mode 100644 index 0000000..572cca7 --- /dev/null +++ b/lib/commands.ts @@ -0,0 +1,7 @@ +const commandList = ["about", "navigate", "external", "help", "ed", "nano"] + +export function commandCompletion(input: string): string { + if (input === "") return ""; + const candidates = commandList.map((cmd) => [cmd.substring(0, input.length), cmd]).filter((cmd) => cmd[0] === input); + return candidates.length > 0 ? candidates[0][1] : ""; +} \ No newline at end of file diff --git a/pages/index.tsx b/pages/index.tsx index 7c06fcf..73be612 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,8 +1,9 @@ import type { NextPage } from 'next' +import REPLInput from '../components/REPLInput' const Home: NextPage = () => { return ( - <>Hallu + ) } diff --git a/styles/REPLInput.module.css b/styles/REPLInput.module.css new file mode 100644 index 0000000..8da6aef --- /dev/null +++ b/styles/REPLInput.module.css @@ -0,0 +1,37 @@ +.wrapper > input, .wrapper > span { + font-size: 1.25rem; + font-family: "CascadiaCode"; + padding: .125rem .25rem; + width: calc(100% - 1rem); +} + +.wrapper { + position: relative; +} + +.in, .in:focus { + border: 0; + outline-width: 0; +} + +.completionWrapper { + position: absolute; + pointer-events: none; + left: 0; + top: 0; + width: min-content; +} + +.completionWrapper span { + padding: 0; + margin: 0; + display: inline-block; +} + +.typed { + opacity: 0; +} + +.completion { + color: #ccc +}