chore(phase-0): struktur repo + hello TUI Bubble Tea v2 (T-0.1, T-0.2)
This commit is contained in:
1 parent
ca45f67b0c
commit
db75653e3d
8 files changed
+314
No files matched your search
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 oxpoe
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
BINARY := tia
|
||||||
|
|
||||||
|
.PHONY: build run test vet fmt clean
|
||||||
|
|
||||||
|
build:
|
||||||
|
go build -o $(BINARY) ./cmd/tia
|
||||||
|
|
||||||
|
run: build
|
||||||
|
./$(BINARY)
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
vet:
|
||||||
|
go vet ./...
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
gofmt -w .
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(BINARY)
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# tia — Terminal Is A My Way
|
||||||
|
|
||||||
|
> "Satu binary untuk semua kebutuhan Docker kamu." · Tagline: **Just Work**
|
||||||
|
|
||||||
|
TUI modern (Go + Charm v2) + CLI pendamping untuk manajemen Docker:
|
||||||
|
stack & container, konverter `docker run` → compose, multi-host, mouse-first,
|
||||||
|
audio feedback, animasi ASCII.
|
||||||
|
|
||||||
|
Status: **Phase 0 — fondasi** (lihat roadmap di `docs/PRD.md` §8).
|
||||||
|
|
||||||
|
## Build & jalankan
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make build # menghasilkan ./tia
|
||||||
|
./tia # buka TUI
|
||||||
|
./tia version # versi & build info
|
||||||
|
make test # unit test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Struktur repo
|
||||||
|
|
||||||
|
```
|
||||||
|
cmd/tia/ entry point — router TUI default vs CLI companion
|
||||||
|
internal/app/ root Bubble Tea model
|
||||||
|
internal/ (menyusul) docker, stack, config, ui, i18n, host, cli, audio
|
||||||
|
docs/PRD.md product requirements
|
||||||
|
docs/rea/ laporan analisis proyek referensi (playbook + modul)
|
||||||
|
reference/ repo referensi untuk REA — git-ignored, jangan di-commit
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prinsip
|
||||||
|
|
||||||
|
- **Satu binary** (`CGO_ENABLED=0`), tanpa dependency runtime selain Docker.
|
||||||
|
- **Core tanpa UI**: logika dibagi TUI & CLI → hasil identik (F-028).
|
||||||
|
- **Mouse & keyboard setara** sejak hari pertama (F-022).
|
||||||
|
- **i18n dari awal** — Indonesia default, English fallback (F-023).
|
||||||
|
|
||||||
|
## Lisensi
|
||||||
|
|
||||||
|
MIT — lihat `LICENSE`. Atribusi pihak ketiga: `THIRD_PARTY_NOTICES.md`,
|
||||||
|
register aset: `docs/ASSETS.md`.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Third-Party Notices
|
||||||
|
|
||||||
|
## Proyek referensi (analisis perilaku — tanpa copy kode verbatim)
|
||||||
|
|
||||||
|
Sesuai aturan REA (`docs/rea/README.md`): kami mempelajari **perilaku** proyek berikut lalu
|
||||||
|
mengimplementasikan ulang dalam Go. Tidak ada kode yang disalin. Tabel pemetaan flag di
|
||||||
|
`docs/rea/composerize.md` diadaptasi dari perilaku proyek aslinya (lisensi MIT).
|
||||||
|
|
||||||
|
| Proyek | URL | Commit dianalisis | Lisensi |
|
||||||
|
|---|---|---|---|
|
||||||
|
| composerize | https://github.com/composerize/composerize | `3e39eb7` | MIT |
|
||||||
|
| Dockge | https://github.com/louislam/dockge | `f809ae1` | MIT |
|
||||||
|
| lazydocker | https://github.com/jesseduffield/lazydocker | `7e7aadc` | MIT |
|
||||||
|
|
||||||
|
Repo referensi hanya ada di `reference/` (git-ignored).
|
||||||
|
|
||||||
|
## Library Go yang dipakai
|
||||||
|
|
||||||
|
| Library | Versi | Lisensi |
|
||||||
|
|---|---|---|
|
||||||
|
| bubbletea | v2.1.0 | MIT |
|
||||||
|
| lipgloss | v2.0.6 | MIT |
|
||||||
|
|
||||||
|
*(Daftar bertambah setiap dependency baru diadopsi; wajib selaras dengan kebijakan aset PRD Appendix C.)*
|
||||||
|
|
||||||
|
## Aset
|
||||||
|
|
||||||
|
Lihat `docs/ASSETS.md`.
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime/debug"
|
||||||
|
|
||||||
|
tea "charm.land/bubbletea/v2"
|
||||||
|
|
||||||
|
"github.com/oxpoe/tia/internal/app"
|
||||||
|
)
|
||||||
|
|
||||||
|
const usage = `tia — Terminal Is A My Way (Just Work)
|
||||||
|
|
||||||
|
Pemakaian:
|
||||||
|
tia Buka TUI interaktif
|
||||||
|
tia version Tampilkan versi & build info
|
||||||
|
|
||||||
|
Subcommand CLI pendamping (convert, ps, up, down, update, doctor)
|
||||||
|
menyusul di Phase 1–3 — lihat docs/PRD.md §F-028.
|
||||||
|
`
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
switch os.Args[1] {
|
||||||
|
case "version", "--version":
|
||||||
|
fmt.Println(versionString())
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "perintah tidak dikenal: %q\n\n%s", os.Args[1], usage)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := tea.NewProgram(app.New()).Run(); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "tia:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// versionString memakai build info VCS — tanpa ldflags.
|
||||||
|
func versionString() string {
|
||||||
|
info, ok := debug.ReadBuildInfo()
|
||||||
|
if !ok {
|
||||||
|
return "tia dev"
|
||||||
|
}
|
||||||
|
ver := info.Main.Version
|
||||||
|
if ver == "" || ver == "(devel)" {
|
||||||
|
ver = "dev"
|
||||||
|
}
|
||||||
|
rev, dirty := "", false
|
||||||
|
for _, s := range info.Settings {
|
||||||
|
switch s.Key {
|
||||||
|
case "vcs.revision":
|
||||||
|
if len(s.Value) >= 7 {
|
||||||
|
rev = s.Value[:7]
|
||||||
|
}
|
||||||
|
case "vcs.modified":
|
||||||
|
dirty = s.Value == "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out := "tia " + ver
|
||||||
|
if rev != "" {
|
||||||
|
out += " (" + rev
|
||||||
|
if dirty {
|
||||||
|
out += "-dirty"
|
||||||
|
}
|
||||||
|
out += ")"
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
module github.com/oxpoe/tia
|
||||||
|
|
||||||
|
go 1.27.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
charm.land/bubbletea/v2 v2.1.0
|
||||||
|
charm.land/lipgloss/v2 v2.0.6
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||||
|
github.com/charmbracelet/ultraviolet v0.0.0-20261008173134-6b8d4baf91b4 // indirect
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.9 // indirect
|
||||||
|
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.4.1 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.30 // indirect
|
||||||
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
github.com/xo/terminfo v1.0.0 // indirect
|
||||||
|
golang.org/x/sync v0.22.0 // indirect
|
||||||
|
golang.org/x/sys v0.47.0 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
charm.land/bubbletea/v2 v2.1.0 h1:A9fj9h5LFrvQhQHFNggp8K8rvY6CUfgn9dMbb6p2HIw=
|
||||||
|
charm.land/bubbletea/v2 v2.1.0/go.mod h1:xWHeyxIX9/wiFU2kgPW04ojOZyTzrWf+8lGjUCaftiU=
|
||||||
|
charm.land/lipgloss/v2 v2.0.6 h1:EaGKeuA8FvF+v2BT5VmZd2LoYLaMZJXA5n34th8nCIQ=
|
||||||
|
charm.land/lipgloss/v2 v2.0.6/go.mod h1:ipDDJNSGa1hlwDtSfW1s2/xR8Vdhbut4PXh2zEKZd0Q=
|
||||||
|
github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
|
||||||
|
github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
|
||||||
|
github.com/charmbracelet/ultraviolet v0.0.0-20261008173134-6b8d4baf91b4 h1:hISLzUbwcvJbGoNR6A3L/TpguRRnACBmZOee8LcHLJw=
|
||||||
|
github.com/charmbracelet/ultraviolet v0.0.0-20261008173134-6b8d4baf91b4/go.mod h1:aJDVk+WOVM1K04rdc/71PMPoUGfjxjhvXfP3Pjz1Zio=
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.9 h1:kIrqLBxtTVbLyyHO2Evoi93EjQo1mylWmpNEsfPxXAM=
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.9/go.mod h1:DG/hpOnaI2QJ/KMMqLL2h0KVhluuJLazf8973P+Vy5w=
|
||||||
|
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
|
||||||
|
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
|
||||||
|
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||||
|
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.4.1 h1:1EO+WB73+EH8EVbzlrG3KLAfEypQWVHIBqlTf+2hNss=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.4.1/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
github.com/mattn/go-runewidth v0.0.30 h1:+KUuiDA4fF0R1p5FeueHefjDm+GIM+kWfFnDjybOPgk=
|
||||||
|
github.com/mattn/go-runewidth v0.0.30/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8=
|
||||||
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/xo/terminfo v1.0.0 h1:2ZpYzqWzyyytjk3TP6aJVDhkMAkc99/1xKQdA3TDTBY=
|
||||||
|
github.com/xo/terminfo v1.0.0/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
|
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||||
|
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
|
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||||
|
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
tea "charm.land/bubbletea/v2"
|
||||||
|
"charm.land/lipgloss/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Model root sementara (T-0.2, Phase 0).
|
||||||
|
// Tujuannya membuktikan Bubble Tea v2 (View struct, mouse, resize) sebelum
|
||||||
|
// layout penuh Phase 1 menggantikannya.
|
||||||
|
type Model struct {
|
||||||
|
width, height int
|
||||||
|
clicks int
|
||||||
|
lastClick string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() Model {
|
||||||
|
return Model{lastClick: "belum ada klik"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Model) Init() tea.Cmd { return nil }
|
||||||
|
|
||||||
|
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case tea.WindowSizeMsg:
|
||||||
|
m.width, m.height = msg.Width, msg.Height
|
||||||
|
|
||||||
|
case tea.KeyPressMsg:
|
||||||
|
switch msg.String() {
|
||||||
|
case "q", "ctrl+c", "esc":
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
|
||||||
|
case tea.MouseClickMsg:
|
||||||
|
if msg.Button == tea.MouseLeft {
|
||||||
|
m.clicks++
|
||||||
|
m.lastClick = fmt.Sprintf("klik #%d di (%d, %d)", m.clicks, msg.X, msg.Y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Model) View() tea.View {
|
||||||
|
title := lipgloss.NewStyle().
|
||||||
|
Bold(true).
|
||||||
|
Foreground(lipgloss.Color("#cba6f7")).
|
||||||
|
Render("tia — Terminal Is A My Way")
|
||||||
|
tagline := lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#a6adc8")).
|
||||||
|
Render("Just Work · Phase 0 · skeleton TUI")
|
||||||
|
box := lipgloss.NewStyle().
|
||||||
|
Border(lipgloss.RoundedBorder()).
|
||||||
|
BorderForeground(lipgloss.Color("#6c7086")).
|
||||||
|
Padding(0, 2).
|
||||||
|
Render(m.lastClick + "\n\nklik mouse di mana saja · tekan q untuk keluar")
|
||||||
|
|
||||||
|
content := fmt.Sprintf("%s\n%s\n\n%s\n\nukuran jendela: %dx%d",
|
||||||
|
title, tagline, box, m.width, m.height)
|
||||||
|
|
||||||
|
v := tea.NewView(content)
|
||||||
|
v.AltScreen = true
|
||||||
|
v.MouseMode = tea.MouseModeCellMotion
|
||||||
|
return v
|
||||||
|
}
|
||||||
Reference in new issue
Block a user