the prompt and command input works but the history does not
This commit is contained in:
@ -1,28 +1,47 @@
|
|||||||
import type { JSX } from "react"
|
|
||||||
import { bold } from "../color"
|
import { bold } from "../color"
|
||||||
import { get_working_dir_name_full, set_working_dir, SetDirStatus } from "../fs/fn"
|
import { get_working_dir_name_full, set_working_dir, SetDirStatus } from "../fs/fn"
|
||||||
|
|
||||||
type args = string[]
|
import create from "../../elements/create"
|
||||||
type command = JSX.Element | boolean
|
|
||||||
|
|
||||||
function parse_ls(entries: JSX.Element[]) {
|
type Term = HTMLElement
|
||||||
return <div className="horizontal-display">
|
type Args = string[]
|
||||||
|
|
||||||
</div>
|
function strout(term: Term, s: string) {
|
||||||
|
const p = create("p")
|
||||||
|
p.innerText = s
|
||||||
|
term.appendChild(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
function cd(args: args): command {
|
function clear(term: Term, args: Args): boolean {
|
||||||
|
Array.from(term.children).forEach(node => {
|
||||||
|
if (node.tagName === "DIV") {
|
||||||
|
if (node.className === "shell-prompt") {
|
||||||
|
const input = node.getElementsByClassName("shell-ps1")[0] as HTMLInputElement
|
||||||
|
if (input.disabled || input.value === "clear") {
|
||||||
|
node.remove()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node.remove()
|
||||||
|
}
|
||||||
|
} else if (node.tagName === "P") {
|
||||||
|
node.remove()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function cd(term: Term, args: Args): boolean {
|
||||||
const new_dir_status = set_working_dir(args[1])
|
const new_dir_status = set_working_dir(args[1])
|
||||||
|
|
||||||
if (new_dir_status === SetDirStatus.NotADirectory) {
|
if (new_dir_status === SetDirStatus.NotADirectory) {
|
||||||
return <p>{"cd: \""}{bold(args[1])}{"\" is not a directory"}</p>
|
// return <p>{"cd: \""}{bold(args[1])}{"\" is not a directory"}</p>
|
||||||
} else if (new_dir_status === SetDirStatus.NotFound) {
|
} else if (new_dir_status === SetDirStatus.NotFound) {
|
||||||
return <p>{"cd: The directory \""}{bold(args[1])}{"\" does not exist"}</p>
|
// return <p>{"cd: The directory \""}{bold(args[1])}{"\" does not exist"}</p>
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
function ls(args: args): command {
|
function ls(term: Term, args: Args): boolean {
|
||||||
// if (args[1] === undefined) {
|
// if (args[1] === undefined) {
|
||||||
// for (const dir_name in working_dir) {
|
// for (const dir_name in working_dir) {
|
||||||
|
|
||||||
@ -32,18 +51,21 @@ function ls(args: args): command {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
function pwd(args: args): command {
|
function pwd(term: Term, args: Args): boolean {
|
||||||
return <p>{`${get_working_dir_name_full()}`}</p>
|
strout(term, get_working_dir_name_full())
|
||||||
}
|
|
||||||
|
|
||||||
function cat(args: args): command {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
interface commands_list {
|
function cat(term: Term, args: Args): boolean {
|
||||||
[index: string]: (args: args) => command
|
return true
|
||||||
}
|
}
|
||||||
const commands: commands_list = {
|
|
||||||
|
type Command = (term: Term, args: Args) => boolean
|
||||||
|
interface CommandsList {
|
||||||
|
[index: string]: Command,
|
||||||
|
}
|
||||||
|
const commands: CommandsList = {
|
||||||
|
["clear"]: clear,
|
||||||
["cd"]: cd,
|
["cd"]: cd,
|
||||||
["ls"]: ls,
|
["ls"]: ls,
|
||||||
["pwd"]: pwd,
|
["pwd"]: pwd,
|
||||||
@ -51,3 +73,4 @@ const commands: commands_list = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default commands
|
export default commands
|
||||||
|
export { type Command }
|
14
src/components/client/shell/command/parse.ts
Normal file
14
src/components/client/shell/command/parse.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
function trim(stdin: string): string {
|
||||||
|
const trimmed_str: string[] = []
|
||||||
|
stdin.split(" ").forEach(s => { if (s !== "") { trimmed_str.push(s) } })
|
||||||
|
return trimmed_str.join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
function to_args(trimmed_str: string): string[] {
|
||||||
|
return trimmed_str.split(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
trim,
|
||||||
|
to_args
|
||||||
|
}
|
@ -1,37 +1,37 @@
|
|||||||
import type { JSX } from "react";
|
|
||||||
import commands from "./list";
|
|
||||||
import { bold } from "../color";
|
import { bold } from "../color";
|
||||||
|
import { to_args, trim } from "./parse";
|
||||||
|
|
||||||
function trim(stdin: string): string {
|
import commands, { type Command } from "./list";
|
||||||
const trimmed_str: string[] = []
|
import create from "../../elements/create";
|
||||||
stdin.split(" ").forEach(s => { if (s !== "") { trimmed_str.push(s) } })
|
import history from "../history";
|
||||||
return trimmed_str.join(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
function to_args(trimmed_str: string): string[] {
|
type Term = HTMLElement
|
||||||
return trimmed_str.split(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
function valid_command(args: string[]): JSX.Element | undefined {
|
function valid_command(term: Term, args: string[]) {
|
||||||
for (const command_in_list in commands) {
|
for (const command_in_list in commands) {
|
||||||
const command = args[0]
|
const command = args[0]
|
||||||
if (command === command_in_list) {
|
if (command === command_in_list) {
|
||||||
return commands[command_in_list](args)
|
return (commands[command_in_list] as Command)(term, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
function unknown_command(cmd_name: string) {
|
function unknown_command(cmd_name: string) {
|
||||||
return <p>{"shell: Unknown command: "}{bold(cmd_name)}</p>
|
const unknown_element = create("p")
|
||||||
|
unknown_element.innerText = "shell: Unknown command: "
|
||||||
|
unknown_element.appendChild(bold(cmd_name))
|
||||||
|
return unknown_element
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function run(stdin: string) {
|
export default function run(term: Term, stdin: string) {
|
||||||
const args = to_args(trim(stdin))
|
const args = to_args(trim(stdin))
|
||||||
const command = valid_command(args)
|
const valid = valid_command(term, args)
|
||||||
|
const command = args[0] as string
|
||||||
|
|
||||||
if (args[0] !== "" && !command) {
|
if (command !== "" && !valid) {
|
||||||
return unknown_command(args[0])
|
return unknown_command(command)
|
||||||
}
|
}
|
||||||
return command ? command : <></>
|
history.add(args.join(" "))
|
||||||
|
return false
|
||||||
}
|
}
|
@ -1,35 +0,0 @@
|
|||||||
import Display from "./prompt"
|
|
||||||
import run from "./command/run"
|
|
||||||
|
|
||||||
const enum Key {
|
|
||||||
Enter = "Enter",
|
|
||||||
ArrowUp = "ArrowUp",
|
|
||||||
ArrowDown = "ArrowDown",
|
|
||||||
Tab = "Tab"
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyboard_events(terminal_window: HTMLElement, new_elements_f: newElement) {
|
|
||||||
const terminal_event = (keyboard_event: KeyboardEvent) => {
|
|
||||||
if (keyboard_event.key === Key.Enter) {
|
|
||||||
keyboard_event.preventDefault()
|
|
||||||
const current_prompt = get_current_prompt()
|
|
||||||
if (current_prompt) {
|
|
||||||
const prompt = new_prompt()
|
|
||||||
const output = run(current_prompt.value)
|
|
||||||
new_elements_f([output, prompt])
|
|
||||||
terminal_window.removeEventListener("keydown", terminal_event)
|
|
||||||
}
|
|
||||||
} else if (keyboard_event.key === Key.ArrowUp) {
|
|
||||||
keyboard_event.preventDefault()
|
|
||||||
} else if (keyboard_event.key === Key.ArrowDown) {
|
|
||||||
keyboard_event.preventDefault()
|
|
||||||
} else if (keyboard_event.key === Key.Tab) {
|
|
||||||
keyboard_event.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
terminal_window.addEventListener("keydown", terminal_event)
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
keyboard_events,
|
|
||||||
}
|
|
@ -2,7 +2,7 @@ import { Entry, EntryType, fs, type FsEntrySignature } from "./fs"
|
|||||||
|
|
||||||
let working_dir = ["/", "home", "user"]
|
let working_dir = ["/", "home", "user"]
|
||||||
|
|
||||||
function get_working_dir_name(): string {
|
function get_working_dir_name() {
|
||||||
return working_dir[working_dir.length-1]
|
return working_dir[working_dir.length-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ function iter_fs_to_goal(w_dir_clone: string[]): FsIterEntry {
|
|||||||
|
|
||||||
for (const w_dir of w_dir_clone) {
|
for (const w_dir of w_dir_clone) {
|
||||||
if (w_dir === "/") { continue }
|
if (w_dir === "/") { continue }
|
||||||
if (next_iter.inner) {
|
if (next_iter && next_iter.inner) {
|
||||||
const found = next_iter.inner.find(entry => entry.name === w_dir)
|
const found = next_iter.inner.find(entry => entry.name === w_dir)
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
import { get_working_dir_name } from "./fs/fn"
|
|
||||||
import { cyan, green } from "./color"
|
|
||||||
|
|
||||||
const userAgent = navigator.userAgent
|
|
||||||
const browser_name_fallible = userAgent.match(/Firefox.\d+[\d.\d]+|Chrome.\d+[\d.\d]+/gm)?.map(f => f.split("/")[0])
|
|
||||||
|
|
||||||
let browser_name = "unknown"
|
|
||||||
if (browser_name_fallible) {
|
|
||||||
browser_name = browser_name_fallible[0] === "Firefox" ? "gecko" : "chromium"
|
|
||||||
}
|
|
||||||
|
|
||||||
function working_dir() {
|
|
||||||
const name = get_working_dir_name()
|
|
||||||
return name === "user" ? "~" : name
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Display() {
|
|
||||||
const user = cyan("user")
|
|
||||||
const dir = green(working_dir())
|
|
||||||
return <p>{user}@{browser_name} {dir}{"> "}</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
export { userAgent }
|
|
@ -1,42 +1,91 @@
|
|||||||
// import { red } from "../shell/color"
|
import { history_list } from "./shell/history"
|
||||||
// import { display_prompt, keyboard_events } from "../shell/events"
|
|
||||||
|
|
||||||
import prompt from "./elements/prompt"
|
import prompt from "./elements/prompt"
|
||||||
// import run from "./shell/command/run"
|
import run from "./shell/command/run"
|
||||||
|
|
||||||
const terminal_window = document.querySelector("main")
|
let history_index = 0
|
||||||
|
|
||||||
|
const term_win_unsafe = document.querySelector("main")
|
||||||
const enum Key {
|
const enum Key {
|
||||||
Enter = "Enter",
|
Enter = "Enter",
|
||||||
ArrowUp = "ArrowUp",
|
ArrowRight = "ArrowRight",
|
||||||
ArrowDown = "ArrowDown",
|
ArrowUp = "ArrowUp",
|
||||||
Tab = "Tab"
|
ArrowDown = "ArrowDown",
|
||||||
}
|
Tab = "Tab"
|
||||||
|
|
||||||
function spawnps1(terminal_window_safe: HTMLElement) {
|
|
||||||
const ps1prompt = prompt()
|
|
||||||
terminal_window_safe.appendChild(ps1prompt.body)
|
|
||||||
input_processor(ps1prompt.input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type InputClosure = (key_event: KeyboardEvent) => void
|
type InputClosure = (key_event: KeyboardEvent) => void
|
||||||
function key_enter(ps1input: HTMLInputElement, key_event: KeyboardEvent, input_closure: InputClosure) {
|
interface EnterArgs {
|
||||||
key_event.preventDefault()
|
readonly term_win_safe: HTMLElement,
|
||||||
// run(ps1input.value)
|
readonly ps1input: HTMLInputElement,
|
||||||
ps1input.removeEventListener("keydown", input_closure)
|
readonly closure: InputClosure
|
||||||
|
}
|
||||||
|
function key_enter(input: EnterArgs) {
|
||||||
|
const unknown_command_msg = run(input.term_win_safe, input.ps1input.value)
|
||||||
|
|
||||||
|
if (unknown_command_msg) {
|
||||||
|
input.term_win_safe.appendChild(unknown_command_msg)
|
||||||
|
}
|
||||||
|
input.ps1input.removeEventListener("keydown", input.closure)
|
||||||
|
spawnps1(input.term_win_safe)
|
||||||
}
|
}
|
||||||
|
|
||||||
function input_processor(ps1input: HTMLInputElement) {
|
function key_up_arrow(ps1input: HTMLInputElement) {
|
||||||
const input_closure = (key_event: KeyboardEvent) => {
|
const history_item = history_list[history_index]
|
||||||
if (key_event.key === Key.Enter) {
|
if (history_item) {
|
||||||
key_enter(ps1input, key_event, input_closure)
|
ps1input.value = history_item
|
||||||
|
history_index+=1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function key_down_arrow(ps1input: HTMLInputElement) {
|
||||||
|
const history_item = history_list[history_index]
|
||||||
|
if (history_item) {
|
||||||
|
history_index-=1
|
||||||
|
if (history_index === -1) {
|
||||||
|
history_index = 0
|
||||||
|
} else {
|
||||||
|
ps1input.value = history_item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ps1input.addEventListener("keydown", input_closure)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (terminal_window) {
|
function spawnps1(term_win_safe: HTMLElement) {
|
||||||
spawnps1(terminal_window)
|
const ps1prompt = prompt()
|
||||||
|
term_win_safe.appendChild(ps1prompt.body)
|
||||||
|
bind_processor(term_win_safe, ps1prompt.input)
|
||||||
|
history_index = 0
|
||||||
|
ps1prompt.input.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function bind_processor(term_win_safe: HTMLElement, ps1prompt_input: HTMLInputElement) {
|
||||||
|
const input_closure = (key_event: KeyboardEvent) => {
|
||||||
|
if (key_event.key === Key.Enter) {
|
||||||
|
key_event.preventDefault()
|
||||||
|
key_enter({
|
||||||
|
term_win_safe: term_win_safe,
|
||||||
|
ps1input: ps1prompt_input,
|
||||||
|
closure: input_closure
|
||||||
|
})
|
||||||
|
} else if (key_event.key === Key.Tab) {
|
||||||
|
key_event.preventDefault()
|
||||||
|
|
||||||
|
} else if (key_event.key === Key.ArrowRight) {
|
||||||
|
key_event.preventDefault()
|
||||||
|
|
||||||
|
} else if (key_event.key === Key.ArrowUp) {
|
||||||
|
key_event.preventDefault()
|
||||||
|
key_up_arrow(ps1prompt_input)
|
||||||
|
} else if (key_event.key === Key.ArrowDown) {
|
||||||
|
key_event.preventDefault()
|
||||||
|
key_down_arrow(ps1prompt_input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ps1prompt_input.addEventListener("keydown", input_closure)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (term_win_unsafe) {
|
||||||
|
spawnps1(term_win_unsafe)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user