feat: complete Rust-to-Go rewrite with Bubbletea v2

Rewrites oxker from Rust/ratatui to Go/Bubbletea, migrated to the
Bubbletea v2 API (charm.land/bubbletea/v2). Removes all original Rust
source files and legacy Go modules (internal/ui, internal/input, bubbles).

Key changes:
- View() returns tea.View with declarative AltScreen and MouseMode
- KeyMsg → KeyPressMsg, MouseMsg → MouseClickMsg/WheelMsg/MotionMsg/ReleaseMsg
- execWriteKey rewritten for v2 key fields (Code/Mod/Text)
- Mouse toggle via View field instead of imperative commands
- Filter/search text input uses msg.Text for v2 space key compat

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Niko Syring
2026-03-12 03:41:14 +01:00
parent e020eb157c
commit d41761d6e9
224 changed files with 5140 additions and 26515 deletions
+112
View File
@@ -0,0 +1,112 @@
package docker
import (
"context"
"encoding/json"
"io"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
)
type Client struct {
cli *client.Client
ctx context.Context
cancel context.CancelFunc
}
func New(host ...string) (*Client, error) {
opts := []client.Opt{client.FromEnv, client.WithAPIVersionNegotiation()}
if len(host) > 0 && host[0] != "" {
opts = append(opts, client.WithHost(host[0]))
}
cli, err := client.NewClientWithOpts(opts...)
if err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.Background())
return &Client{cli: cli, ctx: ctx, cancel: cancel}, nil
}
// Cancel cancels all in-flight Docker API calls.
func (c *Client) Cancel() {
c.cancel()
}
func (c *Client) Ping() error {
_, err := c.cli.Ping(c.ctx)
return err
}
func (c *Client) ListContainers(all bool) ([]types.Container, error) {
opts := container.ListOptions{All: all}
return c.cli.ContainerList(c.ctx, opts)
}
func (c *Client) InspectContainer(id string) (types.ContainerJSON, error) {
return c.cli.ContainerInspect(c.ctx, id)
}
func (c *Client) StartContainer(id string) error {
return c.cli.ContainerStart(c.ctx, id, container.StartOptions{})
}
func (c *Client) StopContainer(id string) error {
return c.cli.ContainerStop(c.ctx, id, container.StopOptions{})
}
func (c *Client) RestartContainer(id string) error {
return c.cli.ContainerRestart(c.ctx, id, container.StopOptions{})
}
func (c *Client) DeleteContainer(id string, force bool) error {
return c.cli.ContainerRemove(c.ctx, id, container.RemoveOptions{Force: force})
}
func (c *Client) PauseContainer(id string) error {
return c.cli.ContainerPause(c.ctx, id)
}
func (c *Client) UnpauseContainer(id string) error {
return c.cli.ContainerUnpause(c.ctx, id)
}
func (c *Client) Logs(id string, options container.LogsOptions) (io.ReadCloser, error) {
return c.cli.ContainerLogs(c.ctx, id, options)
}
// ContainerStats gets a single stats snapshot for a container.
// Uses stream=false (NOT one-shot) so Docker fills in precpu_stats for accurate CPU delta.
func (c *Client) ContainerStats(id string) (*container.StatsResponse, error) {
resp, err := c.cli.ContainerStats(c.ctx, id, false)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var stats container.StatsResponse
if err := json.NewDecoder(resp.Body).Decode(&stats); err != nil {
return nil, err
}
return &stats, nil
}
func (c *Client) ContainerExec(id string, cmd []string, tty bool) (types.HijackedResponse, error) {
execConfig := container.ExecOptions{
Cmd: cmd,
Tty: tty,
AttachStdin: true,
AttachStdout: true,
AttachStderr: true,
}
exec, err := c.cli.ContainerExecCreate(c.ctx, id, execConfig)
if err != nil {
return types.HijackedResponse{}, err
}
return c.cli.ContainerExecAttach(c.ctx, exec.ID, container.ExecStartOptions{})
}
func (c *Client) Close() error {
return c.cli.Close()
}