feature (plg_widget_console): improve console ux

This commit is contained in:
MickaelK
2026-04-13 13:37:56 +10:00
parent 4ab1211674
commit 713e08d91f
2 changed files with 58 additions and 12 deletions
@@ -64,6 +64,7 @@ register({
register({
id: "mimetype",
description: "Determine file type",
complete: complete(() => true),
run(shell, args) {
if (!args[0]) {
shell.term.writeln("mimetype: missing operand");
@@ -156,13 +157,13 @@ register({
register({
id: "cd",
description: "Change working directory",
complete: complete(({ type }) => type === "directory"),
run(shell, args) {
if (!args[0]) {
shell.cwd = shell.home || "/";
return;
}
const path = join(window.location.origin + shell.cwd, args[0])
.replace(new RegExp("\/?$"), "/");
const path = join(window.location.origin + shell.cwd, args[0]).replace(new RegExp("\/?$"), "/");
const sub = stat("api/files/cat?path=" + encodeURIComponent(path)).subscribe({
next({ type }) {
if (type !== "directory") shell.term.writeln("cd: not a directory");
@@ -178,6 +179,7 @@ register({
register({
id: "ls",
description: "List directory contents",
complete: complete(({ type }) => type === "directory"),
run(shell, args) {
const path = args[0] ?
join(window.location.origin + shell.cwd, args[0]).replace(new RegExp("\/?$"), "/")
@@ -208,6 +210,7 @@ register({
register({
id: "stat",
description: "Display file info",
complete: complete(() => true),
run(shell, args) {
if (!args[0]) {
shell.term.writeln("stat: missing operand");
@@ -231,6 +234,7 @@ register({
register({
id: "cat",
description: "Display file contents",
complete: complete(() => true),
run(shell, args) {
if (!args[0]) {
shell.term.writeln("cat: missing operand");
@@ -254,3 +258,36 @@ register({
return () => sub.unsubscribe();
},
});
function complete(filter) {
return function (shell, partial) {
const slashIdx = partial.lastIndexOf("/") + 1;
const [dir, prefix] = [
partial.slice(0, slashIdx),
partial.slice(slashIdx),
];
ls(join(window.location.origin + shell.cwd, dir)).pipe(
rxjs.debounceTime(100),
rxjs.first(),
rxjs.catchError(() => rxjs.EMPTY),
rxjs.map(({ files }) => files
.filter((f) => filter(f) && f.name.startsWith(prefix))
.map((f) => f.name + (f.type === "directory" ? "/" : ""))),
).subscribe((matches) => {
switch (matches.length) {
case 0:
return;
case 1:
const parts = shell.line.split(/\s+/);
parts[parts.length - 1] = dir + matches[0];
shell.replaceLine(parts.join(" "));
return;
default:
shell.term.write("\r\n");
shell.term.writeln(matches.join(" "));
shell.redrawLine();
return;
}
});
};
}
@@ -127,14 +127,14 @@ class ComponentShell extends HTMLElement {
if (ev.altKey && ev.key === "b") {
let i = this.cursor - 1;
while (i > 0 && this.line[i - 1] === " ") i--;
while (i > 0 && this.line[i - 1] !== " ") i--;
while (i > 0 && " /".indexOf(this.line[i - 1]) !== -1) i--;
while (i > 0 && " /".indexOf(this.line[i - 1]) === -1) i--;
this.moveCursor(i - this.cursor);
return;
} else if (ev.altKey && ev.key === "f") {
let i = this.cursor;
while (i < this.line.length && this.line[i] === " ") i++;
while (i < this.line.length && this.line[i] !== " ") i++;
while (i < this.line.length && " /".indexOf(this.line[i]) !== -1) i++;
while (i < this.line.length && " /".indexOf(this.line[i]) === -1) i++;
this.moveCursor(i - this.cursor);
return;
} else if (ev.ctrlKey && ev.key === "a") {
@@ -143,6 +143,9 @@ class ComponentShell extends HTMLElement {
} else if (ev.ctrlKey && ev.key === "e") {
this.moveCursor(this.line.length - this.cursor);
return;
} else if (ev.ctrlKey && ev.key === "d") {
this.deleteAtCursor(0);
return;
} else if (ev.ctrlKey && ev.key === "k") {
this.line = this.line.slice(0, this.cursor);
this.redrawLine();
@@ -193,13 +196,19 @@ class ComponentShell extends HTMLElement {
return;
} else if (ev.key === "Tab") {
if (ev.preventDefault) ev.preventDefault();
const matches = allCommands().map((c) => c.id).filter((id) => id.startsWith(this.line));
if (matches.length === 1) this.replaceLine(matches[0]);
else if (matches.length > 1) {
this.term.write("\r\n");
this.term.writeln(matches.join(" "));
this.redrawLine();
const parts = this.line.split(/\s+/);
if (parts.length <= 1) {
const matches = allCommands().map((c) => c.id).filter((id) => id.startsWith(this.line));
if (matches.length === 1) this.replaceLine(matches[0] + " ");
else if (matches.length > 1) {
this.term.write("\r\n");
this.term.writeln(matches.join(" "));
this.redrawLine();
}
return;
}
const cmd = getCommand(parts[0]);
if (cmd && cmd.complete) cmd.complete(this, parts.slice(1).join(" "));
return;
}