Compare commits
3 Commits
8f1e782ed0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
f932050632
|
|||
|
975c61cce3
|
|||
|
3df6aa402a
|
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,2 +1,7 @@
|
|||||||
main
|
main
|
||||||
yule
|
yule
|
||||||
|
.ssh/
|
||||||
|
|
||||||
|
# nix
|
||||||
|
.direnv/
|
||||||
|
result
|
||||||
|
|||||||
@@ -4,4 +4,6 @@ services:
|
|||||||
container_name: yule
|
container_name: yule
|
||||||
ports:
|
ports:
|
||||||
- "${SSH_PORT:-2222}:2222"
|
- "${SSH_PORT:-2222}:2222"
|
||||||
|
volumes:
|
||||||
|
- ${ROOT_DIR}:/app/.ssh
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
27
flake.lock
generated
Normal file
27
flake.lock
generated
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1766473571,
|
||||||
|
"narHash": "sha256-5G1NDO2PulBx1RoaA6U1YoUDX0qZslpPxv+n5GX6Qto=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "76701a179d3a98b07653e2b0409847499b2a07d3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-25.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
38
flake.nix
Normal file
38
flake.nix
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
description = "yule dev shell";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs }:
|
||||||
|
let
|
||||||
|
allSystems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
"x86_64-darwin"
|
||||||
|
"aarch64-darwin"
|
||||||
|
];
|
||||||
|
|
||||||
|
forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f {
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
});
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells = forAllSystems ({ pkgs }: {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
go
|
||||||
|
gopls
|
||||||
|
gotools
|
||||||
|
go-tools
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
echo "<yule dev shell>"
|
||||||
|
echo "Go version: $(go version)"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
2
go.mod
2
go.mod
@@ -8,6 +8,7 @@ require (
|
|||||||
github.com/charmbracelet/lipgloss v1.1.0
|
github.com/charmbracelet/lipgloss v1.1.0
|
||||||
github.com/charmbracelet/ssh v0.0.0-20250826160808-ebfa259c7309
|
github.com/charmbracelet/ssh v0.0.0-20250826160808-ebfa259c7309
|
||||||
github.com/charmbracelet/wish v1.4.7
|
github.com/charmbracelet/wish v1.4.7
|
||||||
|
github.com/muesli/termenv v0.16.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -35,7 +36,6 @@ require (
|
|||||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
github.com/muesli/termenv v0.16.0 // indirect
|
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
golang.org/x/crypto v0.46.0 // indirect
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
|
|||||||
67
main.go
67
main.go
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/charmbracelet/wish/activeterm"
|
"github.com/charmbracelet/wish/activeterm"
|
||||||
"github.com/charmbracelet/wish/bubbletea"
|
"github.com/charmbracelet/wish/bubbletea"
|
||||||
"github.com/charmbracelet/wish/logging"
|
"github.com/charmbracelet/wish/logging"
|
||||||
|
"github.com/muesli/termenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -33,6 +34,7 @@ func init() {
|
|||||||
func main() {
|
func main() {
|
||||||
s, err := wish.NewServer(
|
s, err := wish.NewServer(
|
||||||
wish.WithAddress(net.JoinHostPort(host, port)),
|
wish.WithAddress(net.JoinHostPort(host, port)),
|
||||||
|
wish.WithHostKeyPath(".ssh/id_ed25519"),
|
||||||
wish.WithMiddleware(
|
wish.WithMiddleware(
|
||||||
bubbletea.Middleware(teaHandler),
|
bubbletea.Middleware(teaHandler),
|
||||||
activeterm.Middleware(),
|
activeterm.Middleware(),
|
||||||
@@ -66,7 +68,9 @@ func main() {
|
|||||||
|
|
||||||
func teaHandler(s ssh.Session) (tea.Model, []tea.ProgramOption) {
|
func teaHandler(s ssh.Session) (tea.Model, []tea.ProgramOption) {
|
||||||
pty, _, _ := s.Pty()
|
pty, _, _ := s.Pty()
|
||||||
m := newModel(pty.Window.Width, pty.Window.Height)
|
renderer := bubbletea.MakeRenderer(s)
|
||||||
|
renderer.SetColorProfile(termenv.ANSI256)
|
||||||
|
m := newModel(pty.Window.Width, pty.Window.Height, renderer)
|
||||||
return m, []tea.ProgramOption{tea.WithAltScreen()}
|
return m, []tea.ProgramOption{tea.WithAltScreen()}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,9 +81,10 @@ type model struct {
|
|||||||
height int
|
height int
|
||||||
viewport viewport.Model
|
viewport viewport.Model
|
||||||
buffer []int
|
buffer []int
|
||||||
|
renderer *lipgloss.Renderer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newModel(width, height int) model {
|
func newModel(width, height int, renderer *lipgloss.Renderer) model {
|
||||||
vp := viewport.New(width, height)
|
vp := viewport.New(width, height)
|
||||||
size := width * height
|
size := width * height
|
||||||
return model{
|
return model{
|
||||||
@@ -87,6 +92,7 @@ func newModel(width, height int) model {
|
|||||||
height: height,
|
height: height,
|
||||||
viewport: vp,
|
viewport: vp,
|
||||||
buffer: make([]int, size+width+1),
|
buffer: make([]int, size+width+1),
|
||||||
|
renderer: renderer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,58 +165,40 @@ func (m model) View() string {
|
|||||||
return m.renderFire()
|
return m.renderFire()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ANSI 256-color palette for fire effect (from dark to bright)
|
var fireColors = []string{"0", "52", "88", "124", "160", "196", "202", "208", "214", "220", "226", "227", "228", "229", "230"}
|
||||||
var fireStyles = []lipgloss.Style{
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("0")), // black
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("52")), // dark red
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("88")), // dark red 2
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("124")), // red
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("160")), // red 2
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("196")), // bright red
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("202")), // orange-red
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("208")), // orange
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("214")), // orange-yellow
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("220")), // yellow
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("226")), // bright yellow
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("227")), // light yellow
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("228")), // pale yellow
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("229")), // near white
|
|
||||||
lipgloss.NewStyle().Foreground(lipgloss.Color("230")), // white-yellow
|
|
||||||
}
|
|
||||||
|
|
||||||
var fireChars = []rune{' ', '.', ':', '^', '*', 'x', 's', 'S', '#', '$'}
|
var fireChars = []rune{' ', '.', ':', '^', '*', 'x', 's', 'S', '#', '$'}
|
||||||
|
|
||||||
func getFireStyle(v int) lipgloss.Style {
|
func getFireColorIndex(v int) int {
|
||||||
if v <= 0 {
|
if v <= 0 {
|
||||||
return fireStyles[0]
|
return 0
|
||||||
} else if v <= 2 {
|
} else if v <= 2 {
|
||||||
return fireStyles[1]
|
return 1
|
||||||
} else if v <= 4 {
|
} else if v <= 4 {
|
||||||
return fireStyles[2]
|
return 2
|
||||||
} else if v <= 6 {
|
} else if v <= 6 {
|
||||||
return fireStyles[3]
|
return 3
|
||||||
} else if v <= 8 {
|
} else if v <= 8 {
|
||||||
return fireStyles[4]
|
return 4
|
||||||
} else if v <= 10 {
|
} else if v <= 10 {
|
||||||
return fireStyles[5]
|
return 5
|
||||||
} else if v <= 13 {
|
} else if v <= 13 {
|
||||||
return fireStyles[6]
|
return 6
|
||||||
} else if v <= 16 {
|
} else if v <= 16 {
|
||||||
return fireStyles[7]
|
return 7
|
||||||
} else if v <= 20 {
|
} else if v <= 20 {
|
||||||
return fireStyles[8]
|
return 8
|
||||||
} else if v <= 25 {
|
} else if v <= 25 {
|
||||||
return fireStyles[9]
|
return 9
|
||||||
} else if v <= 30 {
|
} else if v <= 30 {
|
||||||
return fireStyles[10]
|
return 10
|
||||||
} else if v <= 40 {
|
} else if v <= 40 {
|
||||||
return fireStyles[11]
|
return 11
|
||||||
} else if v <= 50 {
|
} else if v <= 50 {
|
||||||
return fireStyles[12]
|
return 12
|
||||||
} else if v <= 60 {
|
} else if v <= 60 {
|
||||||
return fireStyles[13]
|
return 13
|
||||||
}
|
}
|
||||||
return fireStyles[14]
|
return 14
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m model) renderFire() string {
|
func (m model) renderFire() string {
|
||||||
@@ -232,7 +220,8 @@ func (m model) renderFire() string {
|
|||||||
v = m.buffer[i]
|
v = m.buffer[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
style := getFireStyle(v)
|
colorIdx := getFireColorIndex(v)
|
||||||
|
style := m.renderer.NewStyle().Foreground(lipgloss.Color(fireColors[colorIdx]))
|
||||||
|
|
||||||
chIdx := v
|
chIdx := v
|
||||||
if chIdx > 9 {
|
if chIdx > 9 {
|
||||||
@@ -248,7 +237,7 @@ func (m model) renderFire() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
footer := "Press 'q' to exit"
|
footer := "Press 'q' to exit"
|
||||||
footerStyle := lipgloss.NewStyle().
|
footerStyle := m.renderer.NewStyle().
|
||||||
Foreground(lipgloss.Color("240")).
|
Foreground(lipgloss.Color("240")).
|
||||||
Width(width).
|
Width(width).
|
||||||
Align(lipgloss.Center)
|
Align(lipgloss.Center)
|
||||||
|
|||||||
Reference in New Issue
Block a user