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 { get_working_dir_name_full, set_working_dir, SetDirStatus } from "../fs/fn"
|
||||
|
||||
type args = string[]
|
||||
type command = JSX.Element | boolean
|
||||
import create from "../../elements/create"
|
||||
|
||||
function parse_ls(entries: JSX.Element[]) {
|
||||
return <div className="horizontal-display">
|
||||
type Term = HTMLElement
|
||||
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])
|
||||
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
function ls(args: args): command {
|
||||
function ls(term: Term, args: Args): boolean {
|
||||
// if (args[1] === undefined) {
|
||||
// for (const dir_name in working_dir) {
|
||||
|
||||
@ -32,22 +51,26 @@ function ls(args: args): command {
|
||||
return true
|
||||
}
|
||||
|
||||
function pwd(args: args): command {
|
||||
return <p>{`${get_working_dir_name_full()}`}</p>
|
||||
}
|
||||
|
||||
function cat(args: args): command {
|
||||
function pwd(term: Term, args: Args): boolean {
|
||||
strout(term, get_working_dir_name_full())
|
||||
return true
|
||||
}
|
||||
|
||||
interface commands_list {
|
||||
[index: string]: (args: args) => command
|
||||
function cat(term: Term, args: Args): boolean {
|
||||
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,
|
||||
["ls"]: ls,
|
||||
["pwd"]: pwd,
|
||||
["cat"]: cat,
|
||||
}
|
||||
|
||||
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 { to_args, trim } from "./parse";
|
||||
|
||||
function trim(stdin: string): string {
|
||||
const trimmed_str: string[] = []
|
||||
stdin.split(" ").forEach(s => { if (s !== "") { trimmed_str.push(s) } })
|
||||
return trimmed_str.join(" ")
|
||||
}
|
||||
import commands, { type Command } from "./list";
|
||||
import create from "../../elements/create";
|
||||
import history from "../history";
|
||||
|
||||
function to_args(trimmed_str: string): string[] {
|
||||
return trimmed_str.split(" ")
|
||||
}
|
||||
type Term = HTMLElement
|
||||
|
||||
function valid_command(args: string[]): JSX.Element | undefined {
|
||||
function valid_command(term: Term, args: string[]) {
|
||||
for (const command_in_list in commands) {
|
||||
const command = args[0]
|
||||
if (command === command_in_list) {
|
||||
return commands[command_in_list](args)
|
||||
return (commands[command_in_list] as Command)(term, args)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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 command = valid_command(args)
|
||||
const valid = valid_command(term, args)
|
||||
const command = args[0] as string
|
||||
|
||||
if (args[0] !== "" && !command) {
|
||||
return unknown_command(args[0])
|
||||
if (command !== "" && !valid) {
|
||||
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"]
|
||||
|
||||
function get_working_dir_name(): string {
|
||||
function get_working_dir_name() {
|
||||
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) {
|
||||
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)
|
||||
|
||||
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 { display_prompt, keyboard_events } from "../shell/events"
|
||||
import { history_list } from "./shell/history"
|
||||
|
||||
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 {
|
||||
Enter = "Enter",
|
||||
ArrowUp = "ArrowUp",
|
||||
ArrowDown = "ArrowDown",
|
||||
Tab = "Tab"
|
||||
}
|
||||
|
||||
function spawnps1(terminal_window_safe: HTMLElement) {
|
||||
const ps1prompt = prompt()
|
||||
terminal_window_safe.appendChild(ps1prompt.body)
|
||||
input_processor(ps1prompt.input)
|
||||
Enter = "Enter",
|
||||
ArrowRight = "ArrowRight",
|
||||
ArrowUp = "ArrowUp",
|
||||
ArrowDown = "ArrowDown",
|
||||
Tab = "Tab"
|
||||
}
|
||||
|
||||
type InputClosure = (key_event: KeyboardEvent) => void
|
||||
function key_enter(ps1input: HTMLInputElement, key_event: KeyboardEvent, input_closure: InputClosure) {
|
||||
key_event.preventDefault()
|
||||
// run(ps1input.value)
|
||||
ps1input.removeEventListener("keydown", input_closure)
|
||||
interface EnterArgs {
|
||||
readonly term_win_safe: HTMLElement,
|
||||
readonly ps1input: HTMLInputElement,
|
||||
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) {
|
||||
const input_closure = (key_event: KeyboardEvent) => {
|
||||
if (key_event.key === Key.Enter) {
|
||||
key_enter(ps1input, key_event, input_closure)
|
||||
function key_up_arrow(ps1input: HTMLInputElement) {
|
||||
const history_item = history_list[history_index]
|
||||
if (history_item) {
|
||||
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) {
|
||||
spawnps1(terminal_window)
|
||||
function spawnps1(term_win_safe: HTMLElement) {
|
||||
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 {
|
||||
|
||||
}
|
Reference in New Issue
Block a user