Initial commit

This commit is contained in:
ktkk 2025-09-24 10:04:30 +00:00
commit 82d714d8a3
9 changed files with 251 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
# Nix
result*
# Misc
.direnv

61
flake.lock generated Normal file
View file

@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1758427187,
"narHash": "sha256-pHpxZ/IyCwoTQPtFIAG2QaxuSm8jWzrzBGjwQZIttJc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "554be6495561ff07b6c724047bdd7e0716aa7b46",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

36
flake.nix Normal file
View file

@ -0,0 +1,36 @@
{
description = "Go Template";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = {
nixpkgs,
flake-utils,
...
}:
flake-utils.lib.eachDefaultSystem (
system: let
pkgs = nixpkgs.legacyPackages.${system};
nativeBuildInputs = with pkgs; [
go
gopls
];
buildInputs = with pkgs; [];
in {
devShells.default = pkgs.mkShell {inherit nativeBuildInputs buildInputs;};
packages.default = pkgs.buildGoModule rec {
name = "template";
src = ./.;
inherit buildInputs;
vendorHash = null;
};
}
);
}

3
go.mod Normal file
View file

@ -0,0 +1,3 @@
module git.katkak.dev/katkak/terminal
go 1.25.0

56
main.go Normal file
View file

@ -0,0 +1,56 @@
package main
import (
"io"
"fmt"
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.Handle("/", http.FileServer(http.Dir("./static")))
mux.HandleFunc("GET /command/{command}", commandHandler)
port := 8080
log.Printf("Running on port %d\n", port)
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), mux); err != nil {
log.Fatal(err)
}
}
func commandHandler(w http.ResponseWriter, r *http.Request) {
command := r.PathValue("command")
log.Printf("Handling command %s for %s\n", command, r.RemoteAddr)
c, ok := commands[command]
if !ok {
fmt.Fprintf(w, "Unknown command: %s", command)
return
}
c.Action(w, []string{})
}
type command interface {
Usage(w io.Writer)
Action(w io.Writer, args []string)
}
var commands = map[string]command{
"help": &helpCommand{},
}
type helpCommand struct {}
func (c *helpCommand) Usage(w io.Writer) {
fmt.Fprintf(w, "Usage: help <command>")
}
func (c *helpCommand) Action(w io.Writer, args []string) {
fmt.Fprint(w, "Invalid input\r\n")
c.Usage(w)
}

26
static/index.html Normal file
View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://esm.sh/@xterm/xterm/css/xterm.css" />
<link rel="stylesheet" href="./style.css" />
<title>Document</title>
<script type="importmap">
{
"imports": {
"@xterm/xterm": "https://esm.sh/@xterm/xterm",
"@xterm/addon-fit": "https://esm.sh/@xterm/addon-fit"
}
}
</script>
<script src="./index.js" type="module"></script>
</head>
<body>
<my-terminal></my-terminal>
</body>
</html>

59
static/index.js Normal file
View file

@ -0,0 +1,59 @@
import { Terminal } from "@xterm/xterm";
import { FitAddon } from "@xterm/addon-fit";
class MyTerminalElement extends HTMLElement {
#terminal;
#fitAddon;
#command = "";
static #prompt = "$>";
constructor() {
super();
this.#terminal = new Terminal({
cursorBlink: true,
});
this.#fitAddon = new FitAddon();
this.#terminal.loadAddon(this.#fitAddon);
}
connectedCallback() {
this.#terminal.open(this);
this.#fitAddon.fit();
this.#terminal.writeln("Hello from xterm.js");
this.#terminal.write(MyTerminalElement.#prompt);
this.#terminal.onData(async data => {
if (data === '\r') {
await this.handleCommand();
this.#terminal.write("\r\n");
this.#terminal.write(MyTerminalElement.#prompt);
this.#command = "";
} else {
this.#terminal.write(data);
this.#command += data;
}
});
}
async handleCommand() {
const [command, ...args] = this.#command.split(" ");
if (command.length === 0) {
return;
}
const response = await fetch(`/command/${command}`);
this.#terminal.write("\r\n");
this.#terminal.write(await response.text());
}
}
customElements.define("my-terminal", MyTerminalElement);

4
static/style.css Normal file
View file

@ -0,0 +1,4 @@
html {
background-color: #000000;
}