commit ca45f67b0c0a72312a2ab223d9bf15974d08729d Author: oxpoe <324556608+oxpoe@users.noreply.github.com> Date: Thu Oct 15 06:47:55 2026 +0700 docs: PRD + playbook REA + laporan modul 1 (composerize) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b1cec7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Build artifacts +/tia +/dist/ +*.exe + +# Reference repos for REA (analisis saja — jangan pernah di-commit) +/reference/ + +# OS / editor +.DS_Store +*.swp +.idea/ +.vscode/ + +# Local test sandbox +/tmp/ diff --git a/docs/ASSETS.md b/docs/ASSETS.md new file mode 100644 index 0000000..ec303bf --- /dev/null +++ b/docs/ASSETS.md @@ -0,0 +1,16 @@ +# Register Aset (docs/ASSETS.md) + +Kebijakan: semua aset wajib open source (CC0/MIT/Apache/BSD/OFL). Dilarang NC/ND/lisensi tidak +jelas. Setiap aset dicatat di sini + `THIRD_PARTY_NOTICES.md`; aset suara yang di-embed wajib +tersedia atribusinya dari menu **Tentang** di aplikasi. + +| Aset | Sumber | Lisensi | Tanggal | Status | File target | +|---|---|---|---|---|---| +| Palet Catppuccin (Mocha/Latte) | https://github.com/catppuccin/catppuccin | MIT | — | planned (Phase 1) | `internal/ui/theme/catppuccin.go` | +| Efek suara UI (click, modal, notif, error) | Kenney Interface Sounds — https://kenney.nl/assets/interface-sounds | CC0 1.0 | — | pending (T-0.6) | `internal/audio/assets/*.wav` | +| Logo ASCII `tia` | buatan sendiri | ikut lisensi tia | 2026-10 | planned (Phase 1) | `internal/ui/animation/logo.go` | +| Ikon/glyph | Unicode standar (●, ○, ▶, ⟳, ✔, ⚠, ✕) | bebas | 2026-10 | dipakai | di kode UI | + +## Aturan aset suara +- Format final: WAV (44.1 kHz, 16-bit, mono), durasi < 1 detik, total < 2 MB, di-embed `go:embed`. +- Konversi dari sumber (OGG/MP3) dilakukan sekali saat mengambil aset (T-0.6), hasil disimpan di repo. diff --git a/docs/PRD.md b/docs/PRD.md new file mode 100644 index 0000000..5c859d7 --- /dev/null +++ b/docs/PRD.md @@ -0,0 +1,1532 @@ +# 📋 PRD: tia — "Terminal Is A" My Way + +> **Product Requirements Document** +> **Version:** 1.0.0-draft +> **Author:** oxpoe +> **Created:** 2026-10-15 +> **Status:** Draft — Menunggu review & approval + +--- + +## 1. Executive Summary + +**tia** (Terminal Is A — my way) adalah aplikasi terminal yang dibangun dengan Go dan ekosistem Charm (charmbracelet), berfungsi sebagai all-in-one Docker management tool langsung dari terminal. tia menggabungkan kemampuan **management & convert dari Dockge** dengan **UI/UX elegance dari lazydocker**, diperkaya dengan audio feedback, animasi ASCII, dashboard beranda yang keren, serta utilitas pelengkap. + +### Vision Statement & Tagline +``` +"Satu binary untuk semua kebutuhan Docker kamu — Terminal Is A My Way" +Tagline: "Just Work" +``` + +### Kenapa tia? +- **Dockge** bagus tapi web-based — butuh browser, tidak cocok untuk server headless atau workflow terminal-native. +- **lazydocker** bagus tapi pakai library lama (gocui), tidak ada fitur konversi `docker run` → `compose`, dan manajemen stack terbatas. +- **tia** mengambil yang terbaik dari keduanya: **modern UI (Charm Bubble Tea v2)** + **manajemen stack lengkap** + **konverter cerdas** + **multi-host** + **audio & animasi ASCII** — semuanya dalam satu binary ringkas tanpa dependensi runtime rumit. + +### Prinsip Produk: Satu Binary Multifungsi (TUI Utama + CLI Pendamping) +tia hadir sebagai **satu binary mandiri (single static binary)** yang fleksibel dengan filosofi **"Just Work"**: +1. **Pengalaman Utama = TUI Interaktif Penuh:** + Menjalankan `tia` tanpa argumen langsung meluncurkan TUI modern lengkap dengan dashboard keren (logo, jam digital ASCII), animasi dinamis, mouse support penuh, efek suara interaktif, dan notifikasi toast status. +2. **Mode CLI Pendamping (Scriptable & Headless):** + Untuk kebutuhan otomatisasi, CI/CD, atau perintah kilat tanpa membuka antarmuka TUI, binary yang sama dapat dipanggil dengan subcommand CLI (misal: `tia convert ...`, `tia ps`, `tia up `, `tia doctor`). Seluruh logika inti di-share dari `internal/core`, sehingga output dan perilakunya 100% konsisten. +3. **Instalasi Instan:** + Dapat diinstal dengan **1 baris perintah (`curl -fsSL ... | sh`)** atau cukup unduh binary Go precompiled secara manual dan langsung jalankan (`./tia`). +4. **Mouse & Keyboard Setara:** + Seluruh navigasi dan operasi di TUI ramah mouse (klik, drag, scroll, hover) maupun keyboard shortcut vim-like. +5. **Aset Bebas & Terbuka:** + Aset suara (CC0), palet warna (Catppuccin), dan animasi murni open-source serta tertanam (embedded) langsung di dalam binary. + +--- + +## 2. Target Users + +| User | Use Case | +|---|---| +| **Homelab enthusiast** | Manage Docker stacks di server rumah via SSH | +| **DevOps engineer** | Monitor & manage containers di production/staging | +| **Developer** | Quick convert docker run commands, manage dev containers | +| **Sysadmin** | Multi-host Docker management tanpa web UI overhead | + +--- + +## 3. Technology Stack + +| Component | Technology | Version | Alasan | +|---|---|---|---| +| **Language** | Go | 1.24+ | Performance, single binary, Docker SDK native | +| **TUI Framework** | Bubble Tea v2 | `charm.land/bubbletea/v2` | Elm Architecture, Cursed Renderer, modern | +| **UI Components** | Bubbles v2 | `charm.land/bubbles/v2` | List, Table, Viewport, TextInput, dll | +| **Styling** | Lip Gloss v2 | `charm.land/lipgloss/v2` | CSS-like styling, canvas layering | +| **Forms** | Huh v2 | `charm.land/huh/v2` | Interactive forms, setup wizard | +| **Markdown** | Glamour v2 | `charm.land/glamour/v2` | Help docs rendering | +| **Logging** | Charm Log v2 | `charm.land/log/v2` | Structured logging to file | +| **Docker** | Docker SDK for Go | `github.com/docker/docker/client` | Real-time events, stats, management | +| **Compose** | Docker Compose CLI | System binary | Compose operations (up/down/pull) | +| **YAML** | go-yaml v3 | `gopkg.in/yaml.v3` | Compose file parsing/writing | +| **Config** | go-yaml v3 | `gopkg.in/yaml.v3` | Baca/tulis config, diatur lewat layar Pengaturan di TUI (tanpa framework CLI) | +| **Audio Engine** | Oto v3 | `github.com/ebitengine/oto/v3` | Pure-Go audio player (`CGO_ENABLED=0`), PulseAudio/PipeWire/ALSA fallback | +| **Audio Assets** | Kenney Interface Sounds | CC0 1.0 Universal | Aset audio open-source tanpa royalti, WAV 44.1kHz mono embedded (<2MB) | +| **Animation Engine**| Harmonica v2 | `charm.land/harmonica/v2` | Fisika pegas (spring physics) & timer ticker 30 FPS untuk animasi TUI | +| **CLI Dispatcher** | Native Go Router | Go standard library | Router ringan untuk mode CLI pendamping (`tia convert`, `tia ps`, dll) | + +### Docker Interaction Strategy: Hybrid + +``` +┌─────────────────────────────────────────────────────────────┐ +│ tia Docker Layer │ +├──────────────────────────┬──────────────────────────────────┤ +│ Docker SDK for Go │ docker compose CLI │ +│ (Real-time & Direct) │ (Compose Operations) │ +├──────────────────────────┼──────────────────────────────────┤ +│ • Container list/inspect │ • docker compose up -d │ +│ • Image list/inspect │ • docker compose down │ +│ • Volume list/inspect │ • docker compose pull │ +│ • Network list/inspect │ • docker compose restart │ +│ • Container stats stream │ • docker compose logs -f │ +│ • Container logs stream │ • docker compose ps │ +│ • Docker events stream │ • docker compose ls │ +│ • Container start/stop │ • docker compose build │ +│ • Container exec │ • docker compose config │ +│ • Image pull/remove │ │ +│ • System prune │ │ +│ • Container update │ │ +│ (resource limits) │ │ +└──────────────────────────┴──────────────────────────────────┘ +``` + +**Alasan hybrid:** +- Docker SDK lebih performant untuk real-time operations (events, stats, logs) +- Docker Compose CLI lebih reliable untuk compose operations — karena compose logic (variable interpolation, .env loading, service ordering, network creation) terlalu complex untuk di-reimplment via raw API +- Ini approach yang sama dengan Dockge dan lazydocker + +--- + +## 4. Feature Requirements + +### 4.1 Core Features (MVP — Phase 1 & 2) + +#### F-001: Multi-Panel TUI Layout +- **Priority:** P0 (Must Have) +- **Description:** Layout 2 panel (sidebar + content) dengan responsive sizing, mirip lazydocker +- **Acceptance Criteria:** + - Sidebar (⅓ width) menampilkan: Stacks, Containers, Images, Volumes, Networks + - Content panel (⅔ width) menampilkan detail item yang dipilih + - Tab navigation di content panel (Logs, Stats, Config, Env, Top) + - Responsive saat terminal resize + - 3 screen modes: normal (33/67), half (50/50), fullscreen (100% content) + +#### F-002: Container Management View +- **Priority:** P0 +- **Description:** List dan inspect semua Docker containers +- **Acceptance Criteria:** + - List containers dengan status indicator (🟢 running, 🔴 stopped, 🟡 paused) + - Show: name, image, status, ports, uptime + - Real-time status update via Docker events + - Filter/search containers + +#### F-003: Image, Volume, Network Views +- **Priority:** P0 +- **Description:** Browse dan inspect Docker resources +- **Acceptance Criteria:** + - Images: repository, tag, size, created date, in-use status + - Volumes: name, driver, mountpoint, size + - Networks: name, driver, scope, subnet, attached containers + +#### F-004: Real-time Log Viewer +- **Priority:** P0 +- **Description:** Stream container/stack logs real-time +- **Acceptance Criteria:** + - Real-time log streaming dengan ANSI color support + - Scroll through history (viewport) + - Toggle timestamps on/off + - Search/filter dalam logs (`/`) + - Configurable tail lines (default 300) + +#### F-005: Resource Stats & Graphs +- **Priority:** P0 +- **Description:** Live CPU, Memory, Network, I/O stats dengan ASCII graphs +- **Acceptance Criteria:** + - CPU usage percentage + sparkline graph + - Memory usage (used/limit) + bar graph + - Network I/O (↑ upload, ↓ download rate) + - Disk I/O (read/write rate) + - PIDs count + - Update interval: 1 second + - ASCII sparkline characters: `▁▂▃▄▅▆▇█` + +#### F-006: Vim-like Navigation & Keybindings +- **Priority:** P0 +- **Description:** Keyboard-driven navigation +- **Keybinding Map:** + +| Key | Scope | Action | +|---|---|---| +| `q` / `Ctrl+C` | Global | Quit | +| `?` | Global | Toggle help overlay | +| `Tab` / `Shift+Tab` | Global | Switch focus sidebar ↔ content | +| `j` / `↓` | List | Move down | +| `k` / `↑` | List | Move up | +| `Enter` | List | Select / Expand | +| `h` / `←` | Tabs | Previous tab | +| `l` / `→` | Tabs | Next tab | +| `[` / `]` | Tabs | Previous / Next tab | +| `/` | List | Search / Filter | +| `Esc` | Modal | Close modal / Cancel | +| `1-5` | Global | Jump to section | +| `+` / `_` | Global | Cycle screen mode | + +#### F-022: Full Mouse Support (Mouse-Friendly) +- **Priority:** P0 (Must Have — berlaku sejak Phase 1, bukan fitur tambahan) +- **Description:** tia harus nyaman dipakai hanya dengan mouse, selevel dengan keyboard. Mouse bukan pelengkap, tapi cara interaksi utama yang setara. +- **Prinsip desain:** + 1. **Parity:** semua aksi yang bisa dilakukan lewat keyboard juga bisa lewat mouse, dan sebaliknya + 2. **Discoverable:** elemen klikable terlihat jelas (tombol berlabel, hover highlight, ikon) + 3. **Non-destruktif:** aksi destruktif lewat mouse tetap lewat dialog konfirmasi +- **Interaksi mouse yang didukung:** + +| Interaksi | Lokasi | Aksi | +|---|---|---| +| Klik kiri | Item di sidebar | Pilih item + tampilkan detail | +| Klik kiri | Header section sidebar (Stacks/Containers/…) | Expand / collapse section | +| Klik kiri | Tab (Logs/Stats/Config/…) | Pindah tab | +| Klik kiri | Panel (sidebar/content) | Pindah fokus ke panel tsb | +| Double-click | Item container/stack | Aksi default (buka detail/logs fullscreen) | +| Klik kanan | Item di list | Context menu (start/stop/restart/exec/remove/…) | +| Scroll wheel | List / log / YAML / inspector | Scroll konten (sesuai `mouse_scroll_lines`) | +| Scroll wheel | Tab bar | Ganti tab (opsional) | +| Klik + drag | Scrollbar | Scroll cepat | +| Klik + drag | Border antara sidebar & content | Resize lebar sidebar | +| Hover | Item / tombol / tab | Highlight + tooltip singkat (shortcut key) | +| Klik kiri | Tombol toolbar/status bar | Jalankan aksi (`▶ Start`, `■ Stop`, `↻ Restart`, `⌨ Exec`, `⟳ Update`, `🗑 Delete`, `⇄ Convert`) | +| Klik kiri | Hint di status bar (`[?] help`, `[q] quit`) | Jalankan aksi hint tersebut | +| Klik kiri | Checkbox di list | Multi-select untuk bulk action | +| Klik kiri | Tombol/field di modal & form | Fokus field, tekan tombol, pilih opsi | + +- **Acceptance Criteria:** + - Seluruh alur utama dapat diselesaikan **tanpa menyentuh keyboard** (kecuali mengetik teks, mis. paste command ke converter) + - **Toolbar aksi klikable** muncul di atas content panel sesuai konteks item terpilih (container/stack/image/volume/network) + - Hover highlight pada list item, tab, dan tombol + - Klik kanan membuka context menu yang sama dengan keybinding `x` + - Sidebar bisa di-resize dengan drag (disimpan di config) + - Scroll wheel bekerja di semua area scrollable + - Mode **text select** (toggle `m`) untuk menonaktifkan mouse capture sementara agar bisa select & copy teks native terminal; tersedia juga tombol **Copy** + OSC52 clipboard di log/YAML viewer + - Mouse dapat dimatikan penuh via config (`mouse_enabled: false`) dan app tetap fungsional via keyboard + - Berfungsi di dalam tmux dan SSH session +- **Catatan implementasi (high level, tanpa kode):** Bubble Tea mendukung event klik, scroll, dan motion; hit-testing area klik dikelola oleh layout manager (setiap komponen mendaftarkan zona klikable-nya), sehingga perlu direncanakan dari Phase 1 agar tidak refactor besar di belakang. + +#### F-023: Multi-Bahasa / i18n (Indonesia & English) +- **Priority:** P0 (berlaku sejak Phase 1) +- **Description:** Seluruh teks UI tersedia dalam **Bahasa Indonesia (default)** dan **English**. Bahasa dapat dipilih di setup wizard dan diubah kapan saja lewat config atau menu pengaturan, tanpa restart. +- **Acceptance Criteria:** + - Default bahasa: Indonesia (`language: id`); fallback otomatis ke English jika kunci terjemahan belum ada + - Katalog bahasa disimpan sebagai file YAML (`id.yaml`, `en.yaml`) dan di-embed ke dalam binary (tetap single binary) + - **Tidak ada string UI yang di-hardcode**: label, tombol, tab, dialog, pesan error, dan hint status bar semuanya lewat katalog + - Bahasa dipilih di langkah pertama wizard, dan bisa diganti lewat config (`ui.language`) atau menu pengaturan dengan efek langsung + - Bahasa dapat diganti kapan saja dari **layar Pengaturan** di dalam tia (lihat 5.1.5); tidak ada flag atau env var yang diperlukan + - Istilah teknis yang sudah umum **tidak diterjemahkan**: `Env`, `Image`, `Volume`, `Stack`, `Shell`, `CPU`, `RAM`, `PID`, `Log` + - Tidak diterjemahkan juga: output log container, nama flag docker, isi file YAML + - Lebar kolom/tombol tidak boleh rusak karena teks Indonesia lebih panjang (layout dihitung dari panjang teks, bukan lebar tetap) + - Struktur katalog mudah ditambah bahasa baru di masa depan (cukup tambah file `.yaml`) + - Pesan error dari Docker/CLI tetap ditampilkan asli, dengan penjelasan singkat berbahasa terpilih di atasnya + +**Contoh pemetaan istilah:** + +| Kunci | Indonesia | English | +|---|---|---| +| `section.stacks` | Stack | Stacks | +| `section.containers` | Kontainer | Containers | +| `section.networks` | Jaringan | Networks | +| `tab.logs` | Log | Logs | +| `tab.stats` | Statistik | Stats | +| `tab.config` | Konfigurasi | Config | +| `tab.processes` | Proses | Top | +| `action.start` | Mulai | Start | +| `action.stop` | Hentikan | Stop | +| `action.restart` | Mulai ulang | Restart | +| `action.pause` | Jeda | Pause | +| `action.remove` | Hapus | Remove | +| `action.update` | Perbarui | Update | +| `state.running` | berjalan | running | +| `state.stopped` | berhenti | stopped | +| `state.healthy` | sehat | healthy | + +#### F-024: Audio Feedback & Sound Effects +- **Priority:** P0 (berlaku sejak Phase 1) +- **Description:** Setiap interaksi pengguna (klik mouse, navigasi keyboard, buka modal, dismiss, eksekusi perintah, notifikasi status) memberikan umpan balik suara yang menyenangkan dan memuaskan (*tactile feel*). +- **Interaksi & Kejadian Bersuara:** + - `click.wav`: Saat mengklik tombol toolbar, baris list, tab, atau checkbox. + - `navigate.wav`: Saat memindahkan kursor navigasi (`j`/`k` atau panah) di daftar. + - `modal_open.wav`: Saat modal dialog terbuka (konverter, prune, pengaturan). + - `modal_close.wav`: Saat modal ditutup atau dibatalkan. + - `notify_success.wav`: Saat operasi berhasil (stack running, konversi sukses, prune tuntas). + - `notify_warning.wav`: Saat muncul peringatan atau konfirmasi aksi destruktif (misal: hapus kontainer). + - `notify_error.wav`: Saat perintah gagal, koneksi daemon putus, atau syntax error. + - `exec.wav`: Saat memicu shell exec atau trigger rebuild. +- **Acceptance Criteria:** + - Menggunakan library audio pure-Go `github.com/ebitengine/oto/v3` (`CGO_ENABLED=0`), tanpa kompilasi CGO dan tanpa merusak sifat single binary mandiri. + - Seluruh aset audio bersumber dari lisensi terbuka **CC0 (Kenney Interface Sounds / UI Audio)**, dikonversi ke WAV (mono 44.1kHz, 16-bit), total ukuran < 2 MB, di-embed lewat `go:embed`. + - Inisialisasi audio asynchronous (tidak memperlambat waktu startup aplikasi < 500ms). + - Dilengkapi kontrol penuh di Layar Pengaturan: + - Toggle On/Off efek suara (`ui.sound_enabled: true/false`). + - Pengatur volume (0% - 100%). + - Mute otomatis jika mendeteksi sesi SSH (dengan opsi override aktif manual). + - Degradasi anggun (*graceful fallback*): jika mesin tidak memiliki audio driver/server aktif (misal container atau headless tanpa sound card), aplikasi tetap berjalan 100% normal tanpa panik/crash. + - Seluruh sinyal audio **selalu memiliki padanan visual** (toast, status badge, warna Catppuccin). + +#### F-025: Top-Center Status Toast Notifications +- **Priority:** P0 (berlaku sejak Phase 1) +- **Description:** Setiap notifikasi status aplikasi muncul sebagai pop-up (toast) animasi elegan yang meluncur dari **tengah atas layar** (*top-center*), lengkap dengan status badge, ikon, dan deskripsi kejadian. +- **Kategori Status Notifikasi:** + 1. `[ SUKSES ]` (Hijau/Green): Aksi berhasil (contoh: *Kontainer 'api' berhasil dimulai*, *Konversi docker run tersimpan ke ~/.tia/stacks/web*). + 2. `[ PERINGATAN ]` (Kuning/Yellow): Butuh perhatian (contoh: *Penyimpanan Docker tersisa < 10%*, *Port 8080 bertabrakan*). + 3. `[ GAGAL ]` (Merah/Red): Error/kegagalan (contoh: *Gagal menarik image nginx:latest - timeout*, *Docker daemon tidak merespons*). + 4. `[ INFO ]` (Biru/Blue): Pemberitahuan informasional (contoh: *Host remote 'prod-srv' terhubung*, *Mode tema diubah ke Latte*). +- **Acceptance Criteria:** + - Posisi selalu berada di tengah atas viewport terminal (`x: center`, `y: 0` atau `y: 1`). + - Animasi transisi slide-in lembut saat muncul dan slide-out saat hilang. + - Dilengkapi auto-dismiss timer (default: 4 detik), durasi dapat dihentikan saat mouse di-hover di atasnya. + - Tombol tutup cepat (`✕`) yang bisa diklik mouse atau ditekan `Esc` untuk menutup seketika. + - Sistem antrean (*toast queue*): jika ada beberapa event masuk serentak, notifikasi diantrekan secara berurutan atau ditumpuk rapi tanpa menutupi layar kerja secara agresif. + - Mendukung klik langsung pada toast untuk menuju ke resource terkait (misal: klik toast error container -> langsung fokus ke container tersebut dan buka tab Log). + +#### F-026: Animasi ASCII & Efek Visual Dinamis +- **Priority:** P0 (berlaku sejak Phase 1) +- **Description:** Menghadirkan animasi ASCII yang hidup, estetik, dan halus (*charming*) untuk memperkuat identitas TUI modern tanpa membebani CPU. +- **Elemen Animasi:** + 1. **Splash Screen / Animated Logo:** Saat aplikasi start pertama kali atau membuka Beranda, logo ASCII `tia` muncul dengan efek ketikan/glitch/shimmer halus. + 2. **Real-time Live Digital Clock:** Jam digital format ASCII dinamis yang berdetik setiap detik di pojok dashboard beranda. + 3. **Pulsing Status Indicators:** Indikator titik status kontainer berkedip/bernapas lembut (pulsing `●`) untuk kontainer yang sedang dalam masa transisi (*starting*, *restarting*). + 4. **Smooth Progress Bars & Spinners:** Visual progress bar pengunduhan image dan spinner elegan dari Charm saat memproses operasi latar belakang. + 5. **Animated Sparklines:** Grafik telemetri CPU & Memory yang mengalir halus secara real-time. +- **Acceptance Criteria:** + - Framerate dibatasi maksimal 30 FPS untuk menjaga efisiensi daya dan CPU (< 3% idle CPU usage). + - Ditenagai oleh `charm.land/harmonica/v2` untuk fisika transisi dan `tea.Tick` untuk timer event. + - Mode *Reduced Motion / Non-Animated*: opsi di layar Pengaturan untuk mematikan animasi bagi pengguna yang menginginkan tampilan statis atau berada di koneksi SSH dengan bandwidth sangat rendah. + +#### F-027: Dashboard Beranda Keren (Home Dashboard View) +- **Priority:** P0 (berlaku sejak Phase 1) +- **Description:** Layar sambutan / beranda komprehensif yang menampilkan status menyeluruh ekosistem Docker lokal/remote, jam real-time, logo ASCII, serta pintasan aksi cepat. +- **Komponen Dashboard Beranda:** + - **Header Hero:** Seni ASCII Logo `tia` bertuliskan *Terminal Is A My Way* + Tagline *Just Work* dengan warna aksen Catppuccin. + - **Live Digital Clock Widget:** Jam digital ASCII besar (`HH:MM:SS`) beserta penanggalan lengkap dan zona waktu lokal. + - **Docker Engine Overview Card:** Versi Docker daemon, API version, status socket, total memory/CPU host, uptime. + - **Quick Resource Counters:** Kotak metrik ringkas: Total Stack (Aktif/Mati), Kontainer (Berjalan/Berhenti), Total Image, Volume, & Jaringan. + - **Quick Actions Launcher:** Tombol pintas klikable untuk aksi favorit: `[ ⇄ Konversi Baru ]`, `[ 🧹 Bersihkan Sistem ]`, `[ ➕ Buat Stack ]`, `[ ⚙ Pengaturan ]`, `[ 🌐 Ganti Host ]`. + - **Recent Activity Feed:** 5 peristiwa Docker terkini (kontainer baru start, image di-pull, error terakhir). +- **Acceptance Criteria:** + - Tampil saat aplikasi baru pertama kali dibuka, atau dapat diakses kapan saja dengan menekan `0` atau mengklik ikon Beranda di header. + - Seluruh kartu statistik dan tombol aksi dapat diklik dengan mouse. + - Tata letak responsif menyesuaikan ukuran jendela terminal. + +#### F-028: Satu Binary Multifungsi & CLI Companion Mode +- **Priority:** P0 (arsitektur fondasi) +- **Description:** `tia` adalah satu file binary mandiri (single binary) yang multifungsi. Secara default `tia` membuka TUI interaktif penuh, namun dapat dipanggil langsung dari command line (CLI mode) untuk eksekusi skrip, CI/CD, atau konversi cepat tanpa membuka TUI. +- **Subcommand CLI Pendamping:** + - `tia`: Meluncurkan antarmuka TUI interaktif penuh. + - `tia convert [flags/command]`: Mengonversi perintah `docker run` menjadi compose YAML dan mencetaknya ke stdout atau menyimpannya langsung ke direktori stack (contoh: `tia convert "docker run -d -p 80:80 nginx"`). + - `tia ps`: Menampilkan ringkasan status semua stack & kontainer dalam format tabel rapi atau JSON (`--json`). + - `tia up `: Menjalankan stack di background tanpa membuka TUI. + - `tia down `: Menghentikan & menurunkan stack. + - `tia update `: Menarik image terbaru dan merefresh kontainer stack via CLI. + - `tia doctor`: Memeriksa kesehatan sistem (koneksi Docker daemon, ketersediaan sound server/driver, versi Go, direktori stack). + - `tia version` / `tia --version`: Menampilkan versi, commit git, dan build info. +- **Acceptance Criteria:** + - Berbagi basis kode logika inti yang sama di `internal/core`, menjamin kesamaan hasil antara eksekusi di TUI dan CLI. + - Kode keluar (*exit codes*) standar (0 = sukses, >0 = error) untuk integrasi skrip bash. + - Format output CLI mendukung `--json` untuk parsing oleh `jq` atau tool otomatisasi lainnya. + - Tetap terkompilasi sebagai satu binary statis tanpa ketergantungan library eksternal. + +#### F-029: 1-Line Installer & Standalone Binary Distribution +- **Priority:** P0 (distribusi rilis) +- **Description:** Pengguna dapat memasang `tia` dalam hitungan detik menggunakan perintah instalasi satu baris (*one-line command*) otomatis, atau dengan mengunduh binary Go mandiri tanpa proses kompilasi manual. +- **Metode Instalasi:** + 1. **One-Line Command (Rekomendasi):** + ```bash + curl -fsSL https://raw.githubusercontent.com/oxpoe/tia/main/install.sh | sh + ``` + - Skrip otomatis mendeteksi OS (Linux, macOS) dan arsitektur CPU (`amd64`, `arm64`). + - Mengunduh rilis tarball terbaru dari GitHub Releases. + - Memverifikasi checksum SHA256 secara otomatis demi keamanan sebelum mengekstrak. + - Memasang binary ke direktori pengguna (`~/.local/bin/tia` tanpa memerlukan akses `sudo`). + - Memberikan instruksi jika `~/.local/bin` belum ada di `$PATH`. + 2. **Manual Download Precompiled Binary:** + - Pengguna dapat langsung menuju halaman GitHub Releases, mengunduh file binary terkompresi (misal: `tia_linux_amd64.tar.gz`), mengekstraknya, dan langsung menjalankannya (`./tia`). + 3. **Go Install (untuk developer Go):** + ```bash + go install github.com/oxpoe/tia/cmd/tia@latest + ``` +- **Acceptance Criteria:** + - Skrip instalasi aman (`set -euo pipefail`), transparan, dan tidak meminta privilege root/sudo secara membabi-buta. + - Rilis otomatis di-bundle via GoReleaser dengan SHA256 checksums file. + +--- + +### 4.2 Stack Management & Converter (Phase 3) + +#### F-007: Stacks Directory Management +- **Priority:** P0 +- **Description:** Manage Docker Compose stacks dengan filesystem-based approach +- **Stacks Strategy:** + 1. Saat setup pertama, user menentukan stacks directory (default: `~/.tia/stacks/`) + 2. tia scan system untuk existing compose files (`docker compose ls --all`) + 3. Existing stacks bisa di-copy/move ke stacks directory + 4. Semua managed stacks ada dalam 1 folder yang ditentukan saat setup +- **Acceptance Criteria:** + - Setup wizard saat pertama run + - Scan dan discover existing compose stacks + - Import/migrate stacks ke managed directory + - Stack status: Active (🟢), Exited (🔴), Inactive (⚪) + - Stack CRUD: create, start, stop, restart, down, delete, update + +#### F-008: Docker Run → Docker Compose Converter +- **Priority:** P0 +- **Description:** Convert `docker run` commands menjadi `docker-compose.yml` +- **Supported docker run flags:** + +| Flag | Compose Mapping | +|---|---| +| `--name` | `container_name` | +| `-p` / `--publish` | `ports` | +| `-v` / `--volume` | `volumes` | +| `--mount` | `volumes` (long syntax) | +| `-e` / `--env` | `environment` | +| `--env-file` | `env_file` | +| `--restart` | `restart` | +| `--network` | `networks` | +| `--memory` / `-m` | `deploy.resources.limits.memory` | +| `--cpus` | `deploy.resources.limits.cpus` | +| `-d` / `--detach` | (implied) | +| `--hostname` | `hostname` | +| `--entrypoint` | `entrypoint` | +| (trailing args) | `command` | +| `--user` / `-u` | `user` | +| `--workdir` / `-w` | `working_dir` | +| `--label` / `-l` | `labels` | +| `--cap-add` | `cap_add` | +| `--cap-drop` | `cap_drop` | +| `--privileged` | `privileged` | +| `--pid` | `pid` | +| `--dns` | `dns` | +| `--log-driver` | `logging.driver` | +| `--log-opt` | `logging.options` | +| `--tmpfs` | `tmpfs` | +| `--device` | `devices` | +| `--health-cmd` | `healthcheck.test` | +| `--health-interval` | `healthcheck.interval` | +| `--health-timeout` | `healthcheck.timeout` | +| `--health-retries` | `healthcheck.retries` | +| `--shm-size` | `shm_size` | +| `--init` | `init` | +| `--read-only` | `read_only` | +| `--stop-signal` | `stop_signal` | +| `--stop-grace-period` | `stop_grace_period` | + +- **Acceptance Criteria:** + - Modal UI: paste docker run → preview YAML → edit → save as stack + - Support multi-line commands (backslash continuation) + - Handle quoted strings dan special characters + - Generate valid compose YAML + - Option: append to existing stack or create new + +#### F-009: Compose YAML Editor +- **Priority:** P1 (Should Have) +- **Description:** View dan edit compose.yaml dari dalam tia +- **Acceptance Criteria:** + - YAML viewer dengan syntax highlighting (via glamour) + - **Editor YAML bawaan di dalam tia** (area teks multi-baris, syntax highlighting, nomor baris, validasi YAML live), bisa dioperasikan dengan keyboard dan mouse + - Tombol `[ Simpan ]`, `[ Batal ]`, dan `[ Validasi ]` yang bisa diklik + - Editor eksternal bersifat **opsional** (vim/nano/helix lewat pengaturan): TUI di-suspend sementara lalu kembali otomatis + - Editor `.env` dengan cara yang sama (bawaan di tia) + - Compose validation (`docker compose config`) + +#### F-010: Stack Update +- **Priority:** P0 +- **Description:** Update stack (pull latest images + recreate containers) +- **Acceptance Criteria:** + - Pull latest images untuk semua services in stack + - Recreate containers dengan images baru + - Show progress (pull progress, recreating status) + - Option: update single service atau seluruh stack + +--- + +### 4.3 Container Actions (Phase 4) + +#### F-011: Container Exec (Shell) +- **Priority:** P0 +- **Description:** Exec shell into running container +- **Acceptance Criteria:** + - Suspend TUI → attach ke container shell → resume TUI saat exit + - Auto-detect shell (`/bin/bash`, `/bin/sh`, `/bin/ash`) + - Menggunakan `tea.ExecProcess()` untuk clean suspension + - Keybinding: `e` pada container yang dipilih + +#### F-012: Container Lifecycle Actions +- **Priority:** P0 +- **Description:** Full container lifecycle management +- **Acceptance Criteria:** + - Start, Stop, Restart, Pause, Unpause, Remove, Rename + - Confirmation dialog untuk destructive actions (remove) + - Bulk actions: select multiple → apply action + - Context menu (`x`) untuk semua available actions + +#### F-013: Bulk Operations +- **Priority:** P1 +- **Description:** Batch operations pada multiple resources +- **Acceptance Criteria:** + - Select multiple containers/images/volumes + - Apply: stop all, remove all, restart all + - Confirmation with summary sebelum execute + +--- + +### 4.4 Advanced Features (Phase 5) + +#### F-014: System Prune +- **Priority:** P1 +- **Description:** Cleanup unused Docker resources +- **Acceptance Criteria:** + - Interactive prune dialog + - Preview: reclaimable space per category + - Categories: stopped containers, dangling images, unused images, unused volumes, unused networks, build cache + - Selective prune (pilih kategori) + - Summary setelah prune (space reclaimed) + +#### F-015: Container Health Check Monitor +- **Priority:** P1 +- **Description:** Monitor container health checks +- **Acceptance Criteria:** + - Health status indicators: 🟢 healthy, 🟡 starting, 🔴 unhealthy, ⚪ none + - Health check command, interval, timeout, retries + - Health check log history + - Alert/highlight unhealthy containers + +#### F-016: Resource Limits Editor +- **Priority:** P2 (Nice to Have) +- **Description:** Edit container resource limits on-the-fly +- **Acceptance Criteria:** + - Form (via huh) untuk set/modify: memory limit, CPU limit, pids limit + - Uses Docker SDK `ContainerUpdate()` + - Show current vs new values + +#### F-017: Dockerfile Builder/Editor +- **Priority:** P2 +- **Description:** Basic Dockerfile template editor +- **Acceptance Criteria:** + - Dockerfile templates (FROM, RUN, COPY, EXPOSE, CMD, etc.) + - Editor Dockerfile bawaan di dalam tia (opsional: buka di editor eksternal lalu kembali ke TUI) + - Build integration: `docker build` dari dalam tia + - Build progress streaming + +#### F-018: Docker Registry Browser +- **Priority:** P2 +- **Description:** Browse Docker Hub dan search images +- **Acceptance Criteria:** + - Search images on Docker Hub (via Registry HTTP API v2) + - View available tags + - Pull image langsung dari browser + - Show image description, stars, pulls + +#### F-019: Export/Import Stacks +- **Priority:** P1 +- **Description:** Export dan import compose stacks +- **Acceptance Criteria:** + - Export: pack `compose.yaml` + `.env` + configs → `.tar.gz` + - Import: extract archive → create stack in stacks directory + - Portable format + +#### F-020: Volume Backup & Restore +- **Priority:** P1 +- **Description:** Backup dan restore named Docker volumes +- **Acceptance Criteria:** + - Backup: volume → `.tar.gz` (via temporary alpine container) + - Restore: `.tar.gz` → volume + - Backup strategy: + ``` + docker run --rm -v :/data -v :/backup alpine \ + tar czf /backup/-.tar.gz -C /data . + ``` + - List available backups + - Selective restore + +--- + +### 4.5 Multi-Host Support (Phase 6) + +#### F-021: Multi-Host Docker Management +- **Priority:** P1 +- **Description:** Manage Docker pada multiple remote servers via SSH +- **Acceptance Criteria:** + - Add/remove/edit remote hosts + - SSH tunnel ke remote Docker socket + - Host selector di sidebar (switch antar hosts) + - Semua fitur bekerja di remote host (containers, stacks, images, etc.) + - Host config: name, address, SSH key path, port, user + - Connection status indicator per host + - Config persistence di `~/.config/tia/hosts.yaml` + +--- + +## 5. UI/UX Design + +### 5.1.0 Layar Beranda (Home Dashboard View) — Jam Digital ASCII, Metrik & Pintasan +Layar pembuka interaktif yang menampilkan identitas keren `tia` ("Terminal Is A My Way" - Tagline: "Just Work"), jam digital ASCII dinamis, status ringkas ekosistem Docker, dan pintasan aksi cepat: + +``` +┌──────────────────────────────────────────────────────────────────────────────┐ +│ ▓ tia · terminal is a Tagline: Just Work ● lokal ▾ │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ _ _ │ +│ | |_(_) __ _ ╔╦╗╦╔═╗ Terminal Is A My Way ┌─── JAM LOKAL ────┐ │ +│ | __| |/ _` | ║ ║╠═╣ v0.1.0 (Catppuccin Mocha) │ ██ █▀█ █ █ █▀█ │ │ +│ |_| |_|\__,_| ╩ ╩╩ ╩ Engine: Docker 27.2.0 (API 1.47│ ██ █▄█ █▀█ █▄█ │ │ +│ │ WIB (UTC+7) │ │ +│ ┌─ STATUS EKOSISTEM DOCKER ─────────────────────────┐ │ 15 Okt 2026 │ │ +│ │ 📦 Stacks : 3 Total (● 2 Aktif, ○ 1 Berhenti) │ └──────────────────┘ │ +│ │ 🐳 Kontainer : 8 Total (● 5 Berjalan, ○ 3 Mati) │ │ +│ │ 🖼️ Image : 14 Buah (1.42 GB digunakan) │ ┌─ RESOURCE HOST ───┐ │ +│ │ 💾 Volume : 6 Buah (340 MB) │ │ CPU : [███░░] 28% │ │ +│ │ 🌐 Jaringan : 4 Jaringan (bridge, host, app-net) │ │ RAM : 4.2G/16G │ │ +│ └───────────────────────────────────────────────────┘ └───────────────────┘ │ +│ │ +│ ┌─ PINTASAN AKSI CEPAT (Klik mouse atau tekan shortcut) ───────────────────┐ │ +│ │ [ ⇄ Konversi (c) ] [ ➕ Stack Baru (n) ] [ 🧹 Prune (P) ] [ ⚙ Set (,) ]│ │ +│ └──────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─ AKTIVITAS TERKINI (Log Event Docker) ───────────────────────────────────┐ │ +│ │ 05:42:10 [kontainer] 'api' health status: healthy │ │ +│ │ 05:40:02 [stack] 'web-app' diperbarui (pull image sukses) │ │ +│ │ 05:38:15 [image] 'postgres:16-alpine' di-pull selesai │ │ +│ └──────────────────────────────────────────────────────────────────────────┘ │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ [Enter] Masuk Workspace [0] Beranda [?] Bantuan [q] Keluar │ +└──────────────────────────────────────────────────────────────────────────────┘ +``` + +### 5.1 Workspace Utama (Resource Management & Inspection) + +> Bahasa UI default: **Indonesia** (opsi English, lihat F-023). Istilah teknis (`Env`, `Image`, `Volume`, `Stack`, `Shell`) tidak diterjemahkan. Pesan log container ditampilkan apa adanya. + +``` +┌──────────────────────────────────────────────────────────────────────────────┐ +│ ▓ tia · terminal is a ● lokal ▾ v0.1.0 │ +├──────────────────────┬───────────────────────────────────────────────────────┤ +│ ▾ Stack (3) │ [Log] Statistik Konfigurasi Env Proses │ +│ ● web-app │ [■ Berhenti] [↻ Mulai Ulang] [⌨ Shell] [⏸ Jeda] │ +│ ● database │ ───────────────────────────────────────────────────── │ +│ ○ redis │ 05:17:08 INFO Starting server... │ +│ ▾ Kontainer (5) │ 05:17:09 INFO Listening on :8080 │ +│ ● nginx │ 05:17:10 INFO DB connected │ +│ ▶ ● api │ 05:17:11 WARN Cache miss rate 40% │ +│ ● postgres │ 05:17:12 INFO GET /health 200 2ms │ +│ ● worker │ 05:17:13 ERRO timeout: redis:6379 │ +│ ○ redis │ 05:17:14 INFO retrying (1/3)... │ +│ ▸ Image (12) │ 05:17:15 INFO redis reconnected │ +│ ▸ Volume (8) │ │ +│ ▸ Jaringan (4) │ │ +├──────────────────────┴───────────────────────────────────────────────────────┤ +│ [?]bantuan [c]konversi [n]baru [P]bersihkan [m]pilih-teks [/]cari [q]keluar │ +└──────────────────────────────────────────────────────────────────────────────┘ + ▶ = terpilih ● berjalan ○ berhenti ▾/▸ = buka/tutup bagian + Mouse: klik item/tab/tombol · klik kanan = menu · scroll · seret batas = ubah lebar + Semua elemen [..] di toolbar & status bar bisa diklik. +``` + +### 5.1.1 Tab Statistik + +``` +┌────────────────────────────────────────────────────────────┐ +│ Log [Statistik] Konfigurasi Env Proses │ +│ │ +│ CPU ████████░░░░░░░░░░░░ 45.2% │ +│ RAM ██████░░░░░░░░░░░░░░ 312M / 1.0G │ +│ │ +│ Riwayat CPU (60 dtk) │ +│ ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁▂▃▄▅▆▇▆▅▄▃▂▁▁▂▃ │ +│ │ +│ NET ↑ 1.2 MB/s ↓ 340 KB/s │ +│ Disk Baca 2.1 MB/s Tulis 890 KB/s │ +│ PIDs 24 │ +└────────────────────────────────────────────────────────────┘ +``` + +### 5.1.2 Detail Stack + +``` +┌────────────────────────────────────────────────────────────┐ +│ [Layanan] Konfigurasi Env Log │ +│ [▶ Jalankan] [■ Henti] [↻ Ulang] [⟳ Perbarui] [✎ Edit] │ +│ ────────────────────────────────────────────────────────── │ +│ LAYANAN IMAGE STATUS PORT │ +│ web nginx:1.27 ● berjalan 8080→80 │ +│ app myapp:latest ● berjalan 3000→3000 │ +│ db postgres:16 ● sehat 5432 │ +│ cache redis:7 ○ berhenti - │ +│ │ +│ Pembaruan terakhir: 2 hari lalu Tersedia update: 2 │ +└────────────────────────────────────────────────────────────┘ +``` + +### 5.1.3 Modal Konversi `docker run` → compose + +``` +┌────────────────────────────────────────────────────────────┐ +│ ⇄ Konversi docker run → compose ✕ │ +├────────────────────────────────────────────────────────────┤ +│ Tempel perintah docker run: │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ docker run -d --name web -p 8080:80 \ │ │ +│ │ -v ./html:/usr/share/nginx/html:ro \ │ │ +│ │ -e TZ=Asia/Jakarta --restart unless-stopped \ │ │ +│ │ nginx:latest │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ │ +│ Pratinjau (compose.yaml): │ +│ │ services: │ │ +│ │ web: │ │ +│ │ image: nginx:latest │ │ +│ │ container_name: web │ │ +│ │ ports: ["8080:80"] │ │ +│ │ volumes: ["./html:/usr/share/nginx/html:ro"] │ │ +│ │ environment: [TZ=Asia/Jakarta] │ │ +│ │ restart: unless-stopped │ │ +│ │ +│ Simpan: (•) Stack baru [ web ] ( ) Tambah │ +│ │ +│ [ Salin YAML ] [ Simpan & Jalankan ] [ Batal ] │ +└────────────────────────────────────────────────────────────┘ +``` + +### 5.1.4 Menu Klik Kanan & Dialog Konfirmasi + +``` +┌────────────────────────┐ ┌────────────────────────────────────────┐ +│ ▶ Mulai │ │ Hapus kontainer? │ +│ ■ Hentikan │ │ │ +│ ↻ Mulai ulang │ │ Kontainer "api" akan dihapus. │ +│ ⏸ Jeda │ │ Volume tetap disimpan. │ +│ ⌨ Buka shell │ │ │ +│ ≣ Lihat log │ │ [ Hapus ] [ Batal ] │ +│ ⟳ Perbarui image │ └────────────────────────────────────────┘ +│ ✎ Ganti nama │ +├────────────────────────┤ +│ ✕ Hapus │ +└────────────────────────┘ +``` + +### 5.1.5 Layar Pengaturan (dibuka dengan `,` atau menu ⚙ di header) + +Semua pengaturan diubah **dari dalam tia**, tanpa flag CLI, env var, atau edit file manual. Perubahan bahasa, tema, suara, dan animasi langsung berlaku seketika. + +``` +┌────────────────────────────────────────────────────────────┐ +│ ⚙ Pengaturan ✕ │ +├────────────────────────────────────────────────────────────┤ +│ Bahasa [ Indonesia ▾ ] │ +│ Mode tema [ Otomatis ▾ ] │ +│ Warna aksen [ mauve ▾ ] │ +│ Direktori stack [ ~/.tia/stacks ] │ +│ │ +│ [x] Efek suara interaktif (klik, modal, alert, notif) │ +│ Volume audio [ ████████░░ 80% ] │ +│ [x] Mute suara otomatis saat koneksi SSH │ +│ [x] Animasi ASCII dinamis (logo shimmer, clock, 30 FPS) │ +│ [x] Tampilkan timestamp log │ +│ [x] Aktifkan navigasi mouse │ +│ [x] Efek hover highlight mouse │ +│ │ +│ Perubahan berlaku langsung tanpa restart. │ +│ │ +│ [ Simpan ] [ Batal ] │ +└────────────────────────────────────────────────────────────┘ +``` + +### 5.1.6 Notifikasi Toast Melayang (Top-Center Status Popup) + +Notifikasi status meluncur halus dari **tengah atas layar** (*top-center*), lengkap dengan status badge berwarna Catppuccin, ikon, deskripsi peristiwa, dan tombol tutup cepat `✕`: + +``` + ┌────────────────────────────────────────────────────────┐ + │ ✔ [ SUKSES ] Konversi Berhasil Disimpan ✕│ + │ Stack 'web-app' tersimpan di ~/.tia/stacks/web-app/ │ + └────────────────────────────────────────────────────────┘ + + ┌────────────────────────────────────────────────────────┐ + │ ⚠ [ PERINGATAN ] Port 8080 Bentrok ✕│ + │ Kontainer 'nginx' gagal bind port: sudah dipakai 'api' │ + └────────────────────────────────────────────────────────┘ + + ┌────────────────────────────────────────────────────────┐ + │ ✖ [ GAGAL ] Docker Daemon Putus ✕│ + │ Gagal menghubungi /var/run/docker.sock - periksa servis│ + └────────────────────────────────────────────────────────┘ + + ┌────────────────────────────────────────────────────────┐ + │ ℹ [ INFO ] Host Remote Terhubung ✕│ + │ Berhasil membuka SSH tunnel ke node 'prod-server-01' │ + └────────────────────────────────────────────────────────┘ +``` + +> Setup wizard pertama kali ada di bagian 7.3. + +### 5.2 Detail Tabs per Resource + +| Sidebar Section | Available Tabs | Content | +|---|---|---| +| **Stacks** | Logs, Services, Config, Env | Stack logs, service list, compose.yaml, .env | +| **Containers** | Logs, Stats, Config, Env, Top | Container logs, resource stats, inspect JSON, env vars, processes | +| **Images** | Config, Layers | Image inspect, Dockerfile layers/history | +| **Volumes** | Config | Volume inspect, mountpoint, driver | +| **Networks** | Config | Network inspect, IPAM, attached containers | + +### 5.3 Modal System + +Modals digunakan untuk: +- **Confirm Dialog:** Destructive actions (delete, prune, remove) +- **Input Dialog:** Search, filter, rename +- **Form Dialog:** Stack creation wizard, resource limits editor, host config (via huh) +- **Converter Dialog:** docker run → compose converter +- **Help Overlay:** Full keybinding reference + +**Mouse pada modal:** semua modal punya tombol klikable (`[ OK ]`, `[ Cancel ]`, `[ Save ]`), tombol close `✕` di pojok, klik di luar modal = tutup (kecuali form yang belum disimpan → minta konfirmasi), field form bisa difokuskan dengan klik, dan list/select bisa dipilih dengan klik. + +### 5.4 Theme System: Catppuccin + +**Keputusan:** tia memakai **Catppuccin** sebagai satu-satunya tema bawaan (v1). Tema lain (Dracula, Tokyo Night, dll.) tidak masuk v1. + +#### Mode tema + +| Mode | Perilaku | +|---|---| +| `auto` (default) | Deteksi background terminal saat start → **Mocha** (gelap) atau **Latte** (terang) | +| `dark` | Paksa **Catppuccin Mocha** | +| `light` | Paksa **Catppuccin Latte** | +| `terminal` | Pakai **palet ANSI terminal pengguna** (16 warna) agar tia otomatis serasi dengan tema terminal yang sudah dipakai | + +- Mode bisa diganti saat runtime lewat menu pengaturan atau keybinding (tanpa restart) +- Mode diganti lewat **layar Pengaturan** di dalam tia, atau tombol cepat `T` untuk berganti mode (Otomatis → Gelap → Terang → Terminal) +- Warna **aksen** dapat dipilih (default `mauve`), mengikuti 14 warna aksen Catppuccin + +#### Arsitektur warna: token semantik +Komponen UI **tidak boleh memakai kode warna langsung**. Semuanya lewat token semantik yang dipetakan ke palet aktif, supaya ganti mode tidak mengubah komponen apa pun: + +| Token | Dipakai untuk | Mocha (gelap) | Latte (terang) | +|---|---|---|---| +| `base` | Background utama | Base | Base | +| `surface` | Panel, modal, toolbar | Surface0 | Surface0 | +| `overlay` | Border, separator | Overlay0 | Overlay0 | +| `text` | Teks utama | Text | Text | +| `subtext` | Teks sekunder, hint | Subtext0 | Subtext0 | +| `accent` | Fokus, tab aktif, item terpilih | Mauve (default) | Mauve (default) | +| `success` | ● berjalan, sehat | Green | Green | +| `warning` | ⏸ jeda, WARN, memulai | Yellow | Yellow | +| `danger` | ○ berhenti, ERRO, unhealthy, tombol Hapus | Red | Red | +| `info` | INFO, link, port | Blue | Blue | +| `hover` | Highlight saat mouse hover | Surface1 | Surface1 | +| `selection` | Item terpilih | Surface2 | Surface2 | + +#### Mode `terminal` +- Token dipetakan ke warna ANSI terminal (mis. `success` → ANSI hijau, `danger` → ANSI merah, `accent` → ANSI magenta) +- Background tidak diubah (memakai background terminal), hanya warna foreground dan aksen +- Cocok untuk pengguna yang sudah memasang Catppuccin (atau tema lain) di terminal mereka + +#### Acceptance Criteria +- Semua komponen (sidebar, tab, toolbar, log, grafik, modal, menu, wizard) terbaca jelas di Mocha, Latte, dan mode terminal +- Kontras teks memenuhi keterbacaan (tidak ada teks pudar di Latte) +- Level log berwarna: `INFO`=info, `WARN`=warning, `ERRO`=danger +- Grafik CPU/RAM berubah warna sesuai beban: hijau (<60%), kuning (60–85%), merah (>85%) +- Fallback otomatis ke 256/16 warna jika terminal tidak mendukung truecolor +- Jika deteksi background gagal, `auto` jatuh ke Mocha (gelap) +- Ganti mode saat runtime langsung berlaku tanpa restart +- Mode `terminal` tetap terbaca di terminal dengan palet apa pun + +> **Catatan:** Catppuccin juga punya flavor Frappé dan Macchiato (gelap menengah). Tidak masuk v1, bisa ditambahkan nanti hanya dengan menambah palet karena arsitektur token sudah siap. + +--- + +## 6. Architecture + +### 6.1 High-Level Architecture + +```mermaid +graph TD + subgraph "tia Binary" + A[main.go] --> C[Bubble Tea Program] + + subgraph "UI Layer" + C --> D[Root Model] + D --> D1[Sidebar Panel] + D --> D2[Content Panel] + D --> D3[Status Bar] + D --> D4[Modal Stack] + end + + subgraph "Service Layer" + E[Docker Service] --> E1[Container Ops] + E --> E2[Image Ops] + E --> E3[Volume Ops] + E --> E4[Network Ops] + E --> E5[Stats Collector] + E --> E6[Events Listener] + + F[Stack Service] --> F1[Stack Manager] + F --> F2[Stack Scanner] + F --> F3[Converter] + + G[Config Service] --> G1[Setup Wizard] + G --> G2[Config Manager] + G --> G3[Theme Manager] + end + + subgraph "Docker Layer" + H["Docker SDK Client"] --> H1["Container API"] + H --> H2["Image API"] + H --> H3["System API"] + + I["docker compose CLI"] --> I1["Compose Ops"] + end + + D --> E + D --> F + D --> G + E --> H + F --> I + end + + subgraph "External" + J[Docker Daemon] + K[Docker Hub Registry] + L["Remote Host (SSH)"] + end + + H --> J + I --> J + E2 --> K + L --> J +``` + +### 6.2 Project Structure + +``` +tia/ +├── cmd/ +│ └── tia/ +│ └── main.go # Entry point (Router TUI default vs CLI companion) +├── internal/ +│ ├── app/ +│ │ ├── app.go # Root bubbletea Model +│ │ ├── keymap.go # Global keybindings +│ │ └── messages.go # Custom tea.Msg types +│ ├── cli/ # Mode CLI pendamping (single binary) +│ │ ├── root.go # CLI argument parser & dispatcher +│ │ ├── convert.go # 'tia convert' command +│ │ ├── ps.go # 'tia ps' list stacks & containers +│ │ ├── stack.go # 'tia up / down / update' commands +│ │ └── doctor.go # 'tia doctor' diagnostic check +│ ├── audio/ # Audio feedback engine (Pure Go, Oto v3) +│ │ ├── player.go # Audio context & sound player +│ │ ├── sounds.go # Sound event definitions & mapping +│ │ └── assets/ # Embedded CC0 WAV sound files (<2MB) +│ │ ├── click.wav +│ │ ├── modal_open.wav +│ │ ├── modal_close.wav +│ │ ├── success.wav +│ │ ├── warning.wav +│ │ └── error.wav +│ ├── ui/ +│ │ ├── animation/ # ASCII animation & spring physics +│ │ │ ├── ticker.go # 30 FPS render loop controller +│ │ │ ├── clock.go # Real-time ASCII digital clock generator +│ │ │ └── logo.go # Shimmer / typing ASCII logo banner +│ │ ├── layout/ +│ │ │ ├── layout.go # Multi-panel layout manager +│ │ │ ├── sidebar.go # Left sidebar panel +│ │ │ └── content.go # Right content panel +│ │ ├── views/ +│ │ │ └── home.go # Home Dashboard (logo, jam ASCII, metrik, aktivitas) +│ │ ├── components/ +│ │ │ ├── toast.go # Top-Center floating notification popup +│ │ │ ├── statusbar.go # Bottom status/help bar +│ │ │ ├── tabbar.go # Tab navigation +│ │ │ ├── logview.go # Real-time log viewer +│ │ │ ├── statsview.go # CPU/Memory stats with graphs +│ │ │ ├── inspector.go # JSON inspector +│ │ │ ├── processview.go # Container processes (top) +│ │ │ ├── envview.go # Environment variables +│ │ │ ├── yamlview.go # YAML editor/viewer +│ │ │ ├── converter.go # docker run → compose UI +│ │ │ ├── healthview.go # Health check monitor +│ │ │ └── registry.go # Registry browser +│ │ ├── modal/ +│ │ │ ├── modal.go # Modal dialog base +│ │ │ ├── confirm.go # Confirmation dialog +│ │ │ ├── input.go # Text input dialog +│ │ │ ├── form.go # Multi-field form (huh) +│ │ │ ├── prune.go # Prune dialog +│ │ │ └── resources.go # Resource limits editor +│ │ └── theme/ +│ │ ├── theme.go # Theme manager & token semantik (bg, fg, accent, success, ...) +│ │ ├── catppuccin.go # Palet Catppuccin: Mocha (gelap) & Latte (terang) +│ │ ├── detect.go # Deteksi background terminal (gelap/terang) +│ │ └── ansi.go # Mode "terminal": pakai palet ANSI terminal pengguna +│ ├── docker/ +│ │ ├── client.go # Docker SDK client wrapper +│ │ ├── container.go # Container CRUD + lifecycle +│ │ ├── image.go # Image operations +│ │ ├── volume.go # Volume operations +│ │ ├── network.go # Network operations +│ │ ├── compose.go # docker compose CLI wrapper +│ │ ├── stats.go # Stats streaming +│ │ ├── events.go # Events listener +│ │ ├── exec.go # Container exec +│ │ ├── prune.go # System/resource prune +│ │ ├── backup.go # Volume backup/restore +│ │ └── registry.go # Docker Hub API client +│ ├── stack/ +│ │ ├── manager.go # Stack CRUD +│ │ ├── scanner.go # Filesystem scanner +│ │ ├── converter.go # docker run → compose parser +│ │ ├── export.go # Stack export/import +│ │ └── types.go # Stack data types +│ ├── i18n/ +│ │ ├── i18n.go # Loader & fungsi terjemahan T("key") +│ │ └── locales/ +│ │ ├── id.yaml # Katalog Bahasa Indonesia (default) +│ │ └── en.yaml # Katalog English (fallback) +│ ├── config/ +│ │ ├── config.go # Config load/save +│ │ ├── setup.go # First-run setup wizard +│ │ ├── hosts.go # Multi-host config +│ │ └── types.go # Config structs +│ └── host/ +│ ├── manager.go # Multi-host manager +│ ├── ssh.go # SSH tunnel +│ └── types.go # Host types +├── install.sh # One-line installer script +├── docs/ +│ ├── PRD.md # This document +│ ├── ASSETS.md # Open-source assets register +│ └── rea/ # Reverse Engineering & Analysis reports +├── go.mod +├── go.sum +├── Makefile +├── README.md +├── LICENSE +└── .goreleaser.yml # Release automation +``` + +### 6.3 Key Design Patterns + +#### Elm Architecture (The Elm Architecture / TEA) +```go +// Setiap komponen implement tea.Model +type Model interface { + Init() tea.Cmd + Update(tea.Msg) (tea.Model, tea.Cmd) + View() string +} +``` + +#### Message-Driven Communication +```go +// Contoh message types +type ContainerListMsg []docker.Container +type ContainerStatsMsg docker.Stats +type LogLineMsg string +type DockerEventMsg docker.Event +type StackStatusMsg stack.Status +type ErrorMsg error +``` + +#### Composable Components +``` +Root Model +├── Sidebar Model (bubbles/list) +│ ├── Stacks List +│ ├── Containers List +│ ├── Images List +│ ├── Volumes List +│ └── Networks List +├── Content Model +│ ├── TabBar Model +│ ├── LogView Model (bubbles/viewport) +│ ├── StatsView Model +│ ├── Inspector Model (bubbles/viewport) +│ └── ProcessView Model (bubbles/table) +├── StatusBar Model (bubbles/help) +└── Modal Stack + ├── Confirm Model + ├── Form Model (huh) + └── Converter Model +``` + +--- + +## 7. Configuration + +### 7.1 Config File Location +``` +~/.config/tia/config.yaml +``` + +### 7.2 Config Schema +```yaml +# tia configuration +version: 1 + +# Stacks directory — all managed compose stacks live here +stacks_dir: ~/.tia/stacks + +# UI preferences +ui: + language: id # id (Indonesia) | en (English); default: id, fallback: en + theme: catppuccin # satu-satunya tema bawaan (v1) + theme_mode: auto # auto | dark | light | terminal + # auto = deteksi background terminal -> Mocha (gelap) / Latte (terang) + # dark = Catppuccin Mocha + # light = Catppuccin Latte + # terminal = pakai palet ANSI terminal pengguna (ikut tema terminal) + theme_accent: mauve # warna aksen: rosewater|flamingo|pink|mauve|red|maroon|peach|yellow|green|teal|sky|sapphire|blue|lavender + sidebar_width: 0.33 # sidebar width ratio (0.2 - 0.5) + show_timestamps: true # show timestamps in logs + log_tail_lines: 300 # default number of log lines to tail + stats_interval: 1s # stats refresh interval + mouse_enabled: true # enable mouse support (click, scroll, drag, hover) + mouse_hover: true # hover highlight pada item/tombol/tab + mouse_scroll_lines: 3 # jumlah baris per scroll wheel tick + double_click_ms: 400 # interval double-click (ms) + show_action_buttons: true # tampilkan tombol aksi klikable di toolbar + + # Audio & Sound Effects + sound_enabled: true # aktifkan efek suara (klik, popup notif, alert, modal) + sound_volume: 70 # volume suara (0 - 100) + sound_mute_on_ssh: true # otomatis senyapkan (mute) suara bila berjalan di sesi SSH + + # Animasi & Visual Dinamis + animation_enabled: true # aktifkan animasi (splash shimmer, pulse indicator, ticker) + animation_fps: 30 # frame per second maksimum untuk animasi (default: 30 FPS) + +# Editor +editor: + mode: builtin # builtin (editor di dalam tia) | external (suspend TUI, buka editor luar) + external_command: "" # kosong = pakai $EDITOR, fallback vim/nano (dipakai hanya jika mode: external) + +# Docker +docker: + socket: unix:///var/run/docker.sock + compose_command: docker compose # or docker-compose for legacy + +# Hosts (multi-host) +hosts: + - name: local + type: local + default: true + # - name: production + # type: ssh + # address: user@192.168.1.100 + # port: 22 + # key: ~/.ssh/id_rsa + +# Keybinding overrides (optional) +# keybindings: +# quit: q +# help: "?" +``` + +### 7.3 First-Run Setup Wizard +Langkah wizard: **Bahasa → Direktori stack → Tema → Impor stack yang ditemukan.** Bahasa dipilih di langkah pertama (default: Indonesia) dan langsung diterapkan ke langkah berikutnya. + +``` +┌────────────────────────────────────────────────────────────┐ +│ Selamat datang di tia! Mari kita atur dulu. │ +│ │ +│ Bahasa / Language: (•) Indonesia ( ) English │ +│ │ +│ Direktori stack: │ +│ [ ~/.tia/stacks ] │ +│ │ +│ Tema: (•) Otomatis ( ) Gelap ( ) Terang ( ) Terminal │ +│ │ +│ Ditemukan 4 stack compose yang ada: │ +│ [x] /opt/stacks/nginx │ +│ [x] /opt/stacks/postgres │ +│ [x] /home/oxpoe/projects/api │ +│ [ ] /tmp/test-stack │ +│ │ +│ Mode impor: (•) Salin ( ) Pindah ( ) Symlink │ +│ │ +│ [ Kembali ] [ Selesai ] │ +└────────────────────────────────────────────────────────────┘ +``` + +--- + +## 8. Implementation Roadmap + +### Phase 0: Riset, Spike & Fondasi Proses (Week 0-1) +> **Goal:** Mengunci keputusan berisiko sebelum membangun UI + +| Deliverable | Description | +|---|---| +| **Spike audio** | Buktikan suara bisa diputar dari binary `CGO_ENABLED=0` (Oto v3) di Linux (PulseAudio/PipeWire/ALSA) & macOS; ukur latensi; uji fallback saat tanpa audio device | +| **Spike animasi** | Prototipe animasi ASCII 30 FPS + toast slide-in; ukur CPU idle | +| REA skill | Buat skill REA + template laporan; jalankan REA modul 1 (composerize) sebagai uji coba alur | +| Register aset | `docs/ASSETS.md`, `THIRD_PARTY_NOTICES`; pilih dan unduh aset suara CC0 (Kenney), konversi ke WAV | +| Keputusan lisensi | Tetapkan lisensi tia (rekomendasi MIT) | +| Repo & CI | Struktur repo, lint, test, goreleaser (dry run) | + +**Exit Criteria:** Spike audio & animasi lolos target, REA terbukti jalan, aset & lisensi tercatat + +--- + +### Phase 1: Foundation & MVP (Week 1-3) +> **Goal:** Working TUI yang bisa browse Docker resources, dengan "rasa" tia (dashboard, animasi, suara, notifikasi) dari awal + +| Deliverable | Description | +|---|---| +| Project scaffolding | go mod, project structure, Makefile | +| `internal/core` | Lapisan logika bersama (Docker, stack, config) yang **tidak bergantung UI**, dipakai TUI dan CLI | +| Root model | Bubble Tea app with 2-panel layout | +| **Home Dashboard** | Layar pembuka: logo ASCII beranimasi, jam digital ASCII + tanggal, ringkasan Docker & host, aktivitas terbaru, aksi cepat (F-027) | +| Sidebar | Sections: Stacks, Containers, Images, Volumes, Networks | +| Content panel | Tab system with placeholder views | +| Docker client | SDK connection, list containers/images/volumes/networks | +| Navigation | Vim-like keybindings, focus management | +| Mouse support | Klik/scroll/hover/klik-kanan, hit-testing zones di layout, toolbar aksi klikable, drag resize sidebar | +| **Notifikasi (toast)** | Popup atas-tengah dengan status (sukses/peringatan/gagal/info), antrean, auto-dismiss, klik untuk tutup (F-025) | +| **Sound engine** | Efek suara untuk klik, navigasi, modal, notifikasi, sukses, peringatan, error; toggle & volume di Pengaturan (F-024) | +| **Animation engine** | Splash logo, shimmer logo, spinner/progress, toast slide-in, transisi status container (F-026) | +| i18n (id/en) | Katalog bahasa Indonesia & Inggris, semua string UI lewat katalog (tanpa hardcode), pilih bahasa di wizard & config | +| Config | Basic config load/save | +| Theme | Catppuccin (Mocha/Latte), mode Otomatis/Gelap/Terang/Terminal, deteksi background terminal, semua warna via token semantik | +| Status bar | Context-aware help text (hint klikable) | +| **Rilis awal** | goreleaser + GitHub Releases + `install.sh` + checksum (F-029), `tia --version` / `tia doctor` | + +**Exit Criteria:** Jalankan `tia` → splash → Home Dashboard (jam, ringkasan) → masuk workspace, lihat semua resource Docker, navigasi dengan keyboard **dan mouse**; setiap aksi memberi suara + toast; instal dengan satu baris perintah berhasil di mesin bersih + +--- + +### Phase 2: Monitoring & Logs (Week 3) +> **Goal:** Real-time monitoring capabilities + +| Deliverable | Description | +|---|---| +| Log viewer | Real-time log streaming, ANSI colors, search, scroll | +| Stats view | CPU/Memory/Network/IO graphs, 1s refresh | +| Process view | Container processes (docker top) | +| Inspector | JSON inspect for any resource | +| Env view | Environment variables display | +| Events listener | Auto-update resource lists on Docker events | + +**Exit Criteria:** Select container → see live logs, stats updating, processes + +--- + +### Phase 3: Stack Management & Converter (Week 4-5) +> **Goal:** Full stack lifecycle + docker run converter + +| Deliverable | Description | +|---|---| +| Stack scanner | Scan & discover existing compose stacks | +| Stack manager | Create, start, stop, restart, down, delete, update | +| Converter | docker run → compose.yaml with 25+ flags | +| Converter UI | Modal: paste → preview → edit → save | +| YAML viewer | Syntax highlighted compose.yaml view | +| Setup wizard | First-run setup with huh forms | +| Compose CLI wrapper | docker compose up/down/pull/restart/ps | +| REA modul 1, 2, 7 | Analisis composerize, Dockge (stack), dan format output Compose, hasil di `docs/rea/` + test vectors | +| CLI mode (pendamping) | `tia convert`, `tia ps`, `tia stacks`, `tia up/down/restart/update`, `--json`, kode keluar jelas (F-028) | + +**Exit Criteria:** Convert docker run command → save as stack → manage it, **baik dari TUI maupun dari CLI**, dengan hasil identik + +--- + +### Phase 4: Container Actions & Exec (Week 5-6) +> **Goal:** Full container lifecycle + exec shell + +| Deliverable | Description | +|---|---| +| Container exec | Shell into container (tea.ExecProcess) | +| Lifecycle actions | Start, stop, restart, pause, unpause, remove, rename | +| Bulk actions | Multi-select + batch operations | +| Context menu | Action menu (`x`) for all available actions | +| Confirm dialogs | Confirmation for destructive actions | + +**Exit Criteria:** Exec into container, bulk stop/remove, all lifecycle actions work + +--- + +### Phase 5: Advanced Features (Week 7-8) +> **Goal:** Power-user features + +| Deliverable | Description | +|---|---| +| System prune | Interactive cleanup with space preview | +| Health check monitor | Health status tracking & history | +| Resource limits editor | Modify CPU/memory limits on running containers | +| Dockerfile builder | Basic editor + build integration | +| Registry browser | Search Docker Hub, view tags, pull | +| Stack export/import | Pack/unpack stacks as .tar.gz | +| Volume backup | Backup/restore named volumes | + +**Exit Criteria:** All advanced features functional + +--- + +### Phase 6: Multi-Host (Week 9-10) +> **Goal:** Manage Docker on remote servers + +| Deliverable | Description | +|---|---| +| Host manager | Add/remove/edit remote hosts | +| SSH tunnel | Forward remote Docker socket | +| Host selector | Switch between hosts in sidebar | +| Remote operations | All features work on remote hosts | + +**Exit Criteria:** Add remote host → manage its containers/stacks from tia + +--- + +## 9. Non-Functional Requirements + +### Performance +- Startup time < 500ms +- UI response < 16ms (60fps) +- Memory usage < 50MB idle, < 100MB active monitoring +- Efficient rendering via Cursed Renderer (cell-diffing) +- Animasi dibatasi **30 FPS**, CPU idle < 3% saat dashboard terbuka +- Inisialisasi audio **tidak memblok startup** (dilakukan di background); suara pertama < 100 ms setelah siap +- Ukuran binary: target < 25 MB (termasuk aset suara < 2 MB) + +### Compatibility +- Linux (primary) +- macOS (secondary) +- Windows (WSL2 — best effort) +- Docker Engine 20.10+ +- Docker Compose v2 (plugin mode) + +### Input & Mouse Support +- **Mouse-first friendly:** semua fitur utama dapat dioperasikan penuh dengan mouse, keyboard tetap first-class (tidak ada fitur mouse-only atau keyboard-only) +- Mouse capture aktif by default (`mouse_enabled: true`), bisa dimatikan via config +- Tampilan harus mengindikasikan elemen yang bisa diklik (hover highlight, tombol berlabel, kursor/affordance jelas) +- Kompatibel dengan terminal modern (kitty, alacritty, wezterm, gnome-terminal, iTerm2), tmux, dan SSH session +- Fallback graceful bila terminal tidak mendukung mouse + +### Distribution +- **Satu binary statis** (`CGO_ENABLED=0`), tanpa runtime dependency selain Docker (audio memakai library pure-Go; server audio hanya dibutuhkan jika suara diaktifkan) +- Instal satu baris (skrip) **atau** unduh binary manual dari GitHub Releases, detail di **F-029** +- Release otomatis via goreleaser: `.tar.gz`/`.zip` + `checksums.txt`; `.deb`, `.rpm`, Homebrew, AUR menyusul +- Binary name: `tia` + +### Audio & Animasi +- Suara dan animasi **opsional**: bisa dimatikan penuh dari Pengaturan, dan aplikasi tetap 100% fungsional tanpa keduanya +- Setiap suara dan animasi punya padanan visual/teks agar informasi tidak hanya lewat bunyi +- Mode "reduced motion" untuk pengguna yang sensitif terhadap gerakan + +### Security +- No credentials stored in plaintext (SSH keys referenced by path) +- Docker socket access follows system permissions +- Confirmation for all destructive operations + +--- + +## 10. Success Metrics + +| Metric | Target | +|---|---| +| Docker run → compose conversion accuracy | > 95% untuk common flags | +| TUI startup time | < 500ms | +| Real-time stats latency | < 2 seconds | +| Container exec suspend/resume | Clean, no artifacts | +| Stack operations (up/down/restart) | Same reliability as native CLI | +| Multi-host connection | < 5 seconds SSH tunnel setup | +| Mouse coverage | 100% aksi utama bisa dilakukan hanya dengan mouse (tanpa keyboard) | +| Parity keyboard & mouse | Setiap fitur bisa lewat keyboard DAN mouse | +| Latensi suara klik | < 100 ms dari aksi sampai terdengar | +| Notifikasi (toast) muncul | < 100 ms setelah event, tampil di atas-tengah dengan status lengkap | +| Animasi | Stabil 30 FPS, CPU idle < 3% saat dashboard terbuka | +| Instalasi satu baris | < 30 detik dari perintah sampai `tia` bisa dijalankan | +| Paritas CLI vs TUI | Hasil konversi & operasi stack lewat CLI identik dengan lewat TUI | +| Coverage aset | 100% aset tercatat di register aset dengan lisensi valid | + +--- + +## 11. Risks & Mitigations + +| Risk | Impact | Mitigation | +|---|---|---| +| Charm v2 breaking changes (baru release) | High | Pin exact versions, thorough testing | +| Docker API version incompatibility | Medium | Use API version negotiation | +| Terminal compatibility (colors, unicode) | Medium | Lipgloss adaptive colors, fallback ASCII | +| Complex compose files break converter | Medium | Focus on common flags, graceful fallback | +| SSH tunnel instability | Low | Reconnection logic, timeout handling | +| Mouse event tidak konsisten antar terminal/tmux/SSH | Medium | Test di terminal populer (kitty, alacritty, wezterm, gnome-terminal, tmux, SSH), keyboard selalu jadi fallback penuh, toggle mouse via config | +| Mouse capture mengganggu copy-paste teks native | Medium | Sediakan mode "text select" (toggle `m`) + tombol Copy di log/YAML viewer + dukungan OSC52 clipboard | +| Suara tidak terdengar saat tia dijalankan lewat SSH (audio diputar di server, bukan di laptop pengguna) | High | Deteksi sesi SSH → suara default mati + info; semua suara selalu punya padanan visual; opsi terminal bell (BEL) sebagai fallback; rekomendasikan pola "tia lokal + multi-host" | +| Tidak ada audio device/server (PulseAudio/PipeWire/ALSA) di mesin | Medium | Deteksi saat start, degradasi diam-diam ke mode tanpa suara + notifikasi sekali; `tia doctor` menampilkan status audio | +| Dependensi audio merusak single static binary (cgo) | High | Pilih library audio pure-Go (Oto v3 dengan `CGO_ENABLED=0`); **spike teknis di Phase 0** untuk membuktikan sebelum dikunci | +| Animasi menghabiskan CPU / flicker di terminal lambat | Medium | Batas 30 FPS, animasi dimatikan otomatis saat idle/terminal lambat, mode "reduced motion", toggle di Pengaturan | +| Spam suara/notifikasi dari event Docker beruntun | Medium | Debounce, rate limit, dan pengelompokan notifikasi sejenis | +| Pola `curl \| sh` dianggap tidak aman | Medium | HTTPS saja, verifikasi checksum SHA256, skrip ringkas bisa dibaca, instal ke `~/.local/bin` tanpa sudo, sediakan jalur instal manual | +| Pelanggaran lisensi aset / kode referensi | High | Kebijakan aset open source (Appendix C), register aset, aturan REA "tulis ulang, jangan salin", verifikasi lisensi di awal tiap modul | +| Pertentangan prinsip TUI-first dengan mode CLI | Low | CLI hanya pendamping non-interaktif; seluruh logika di `internal/core` dipakai bersama sehingga tidak ada duplikasi | + +--- + +## 12. REA Skill: Reverse Engineering & Analysis + +> [!NOTE] +> Singkatan "REA" saya artikan sebagai **Reverse Engineering & Analysis**. Bagian ini perlu dikonfirmasi (lihat Open Questions). + +### 12.1 Tujuan +REA adalah **skill kerja** (panduan terstruktur untuk agent/developer) yang dipakai selama pengembangan untuk **menganalisis logika backend proyek referensi** (Dockge, lazydocker, composerize) lalu **mem-porting perilakunya ke Go** di dalam tia, dengan hasil yang bisa diverifikasi. + +Yang dianalisis dan di-clone adalah **logika backend** (alur, algoritma, aturan, format data, perintah Docker). **Bukan** UI, branding, aset, atau frontend mereka. + +### 12.2 Aturan Legal & Etika +1. **Cek lisensi dulu** langsung dari repo sumber sebelum menyentuh kodenya (target saat ini umumnya MIT, tapi wajib diverifikasi) +2. **Tulis ulang, jangan salin.** Pelajari perilaku dan algoritma, lalu implementasikan sendiri dalam Go. Tidak ada copy-paste kode verbatim +3. Jika ada bagian yang terpaksa diadaptasi dekat dengan aslinya, **cantumkan atribusi** dan lisensi aslinya di `THIRD_PARTY_NOTICES` +4. Jangan pernah menganalisis/meniru proyek **closed-source** atau berlisensi tidak jelas +5. Jangan mengambil nama, logo, atau aset visual proyek referensi + +### 12.3 Bentuk Skill +- Disimpan di repo sebagai skill (`SKILL.md` + template laporan), lokasi final mengikuti konvensi skill Antigravity +- Dipanggil per modul: "REA: analisis `` dari ``" +- Output selalu berupa dokumen analisis + test vectors + spesifikasi port, sebelum satu baris kode Go ditulis + +### 12.4 Alur Kerja + +```mermaid +flowchart LR + A[1. Scope] --> B[2. Map] + B --> C[3. Trace] + C --> D[4. Spec] + D --> E[5. Port] + E --> F[6. Verify] + F -->|beda perilaku| C +``` + +| Langkah | Aktivitas | Output | +|---|---|---| +| 1. **Scope** | Tentukan modul & batasnya, cek lisensi | Catatan scope + lisensi | +| 2. **Map** | Petakan file/fungsi/struktur data yang relevan | Peta modul | +| 3. **Trace** | Telusuri alur eksekusi dari input ke output, termasuk edge case & error path | Diagram alur + daftar edge case | +| 4. **Spec** | Tulis spesifikasi netral: input, output, aturan, tabel pemetaan, perintah Docker yang dipanggil | `docs/rea/.md` + test vectors | +| 5. **Port** | Implementasi ulang dalam Go (terpisah dari UI, di `internal/core`) | Kode Go + unit test | +| 6. **Verify** | Uji paritas: input sama harus menghasilkan output setara dengan referensi | Laporan paritas (lolos/gagal per kasus) | + +### 12.5 Template Laporan Analisis (`docs/rea/.md`) +- Ringkasan & tujuan modul +- Sumber (repo, commit/versi, lisensi) +- Alur eksekusi (diagram) +- Struktur data & format I/O +- Aturan & edge case +- Perintah Docker/Compose yang dipanggil beserta format outputnya +- Test vectors (input → output yang diharapkan) +- Perbedaan yang disengaja dari referensi (dan alasannya) + +### 12.6 Target Analisis + +| # | Modul referensi | Yang dianalisis | Hasil di tia | Fase | +|---|---|---|---|---| +| 1 | **composerize** | Parser flag `docker run`, tokenisasi (quote, backslash, multi-line), pemetaan flag → field compose, normalisasi | `internal/stack/converter.go` + korpus test | 3 | +| 2 | **Dockge** (stack) | Scan folder stack, kandidat nama file compose (`compose.yaml`, `compose.yml`, `docker-compose.yaml`, `docker-compose.yml`), normalisasi nama project, rekonsiliasi status (`docker compose ls --all --format json` vs isi disk), deteksi unmanaged, pola perintah up/down/pull/restart | `internal/stack/scanner.go`, `manager.go`, `internal/docker/compose.go` | 3 | +| 3 | **Dockge** (agent) | Arsitektur primary ↔ agent untuk multi-host (ide desain; tia memakai SSH tunnel) | `internal/host/*` | 6 | +| 4 | **lazydocker** (commands) | Pemanggilan Docker SDK: list/inspect, stats streaming, buffer riwayat CPU/RAM, deteksi shell, log streaming | `internal/docker/*` | 2, 4 | +| 5 | **lazydocker** (tasks) | Manajemen stream di background: cancel stream saat pilihan pindah, cegah goroutine bocor | Pola stream/cancel pada TUI | 2 | +| 6 | **lazydocker** (custom commands) | Template perintah kustom dengan placeholder | Fitur custom commands (opsional) | 5 | +| 7 | **Docker/Compose CLI** | Format JSON `compose ls`, `compose ps`, `compose config`, kode keluar | Parser output compose | 3 | + +### 12.7 Verifikasi Paritas +- **Golden tests:** pasangan input/output dari analisis disimpan sebagai test vectors +- **Differential testing (converter):** jalankan korpus perintah `docker run` yang sama pada composerize dan converter tia, lalu bandingkan YAML hasilnya setelah dinormalisasi (urutan key, kutip) +- **Target paritas:** ≥ 95% pada flag umum (selaras dengan success metric converter); setiap perbedaan harus tercatat sebagai "perbedaan yang disengaja" +- **Integration test** dengan Docker sungguhan untuk scanner dan status stack + +### 12.8 Definition of Done per Modul +- [ ] Lisensi sumber sudah diverifikasi dan dicatat +- [ ] Laporan analisis `docs/rea/.md` selesai +- [ ] Test vectors tersedia +- [ ] Implementasi Go terpisah dari UI (bisa dipakai TUI **dan** CLI) +- [ ] Uji paritas lolos sesuai target +- [ ] `THIRD_PARTY_NOTICES` diperbarui bila perlu + +--- + +## 13. Future Considerations (Post v1.0) + +- [ ] Docker Swarm support +- [ ] Kubernetes basic support (pods, deployments) +- [ ] Plugin system (custom commands via Go plugins or Lua) +- [ ] Remote access via SSH TUI (using Charm's `wish`) +- [ ] Notification integrations (Discord, Telegram, Slack) +- [ ] Container metrics history & persistence +- [ ] Compose file templates marketplace +- [ ] AI-assisted compose generation +- [ ] Container security scanning integration (Trivy, Grype) + +--- + +## Appendix A: Reference Projects + +| Project | URL | What We Take | +|---|---|---| +| Dockge | https://github.com/louislam/dockge | Stack management, converter, file-system approach | +| lazydocker | https://github.com/jesseduffield/lazydocker | UI layout, keybindings, multi-panel design | +| Charm (bubbletea) | https://github.com/charmbracelet/bubbletea | TUI framework | +| Charm (bubbles) | https://github.com/charmbracelet/bubbles | UI components | +| Charm (lipgloss) | https://github.com/charmbracelet/lipgloss | Styling | +| Charm (huh) | https://github.com/charmbracelet/huh | Forms | + +## Appendix B: Charm v2 Import Paths + +```go +import ( + tea "charm.land/bubbletea/v2" + "charm.land/bubbles/v2/list" + "charm.land/bubbles/v2/table" + "charm.land/bubbles/v2/viewport" + "charm.land/bubbles/v2/textinput" + "charm.land/bubbles/v2/help" + "charm.land/bubbles/v2/key" + "charm.land/bubbles/v2/spinner" + "charm.land/bubbles/v2/progress" + "charm.land/lipgloss/v2" + "charm.land/huh/v2" + "charm.land/glamour/v2" + "charm.land/log/v2" +) +``` + +## Appendix C: Aset Open Source & Lisensi + +### Kebijakan +- Semua aset (suara, palet, font, ikon, library) **wajib open source / bebas dipakai**: CC0, MIT, Apache-2.0, BSD, OFL +- **Dilarang:** lisensi NC (non-commercial), ND (no-derivatives), atau lisensi yang tidak jelas +- Setiap aset dicatat di **register aset** (`docs/ASSETS.md` + `THIRD_PARTY_NOTICES`): nama, penulis, URL sumber, lisensi, tanggal diambil, dan file mana yang memakainya +- Aset suara di-embed ke binary, jadi atribusi harus ikut tersedia dari dalam aplikasi (menu **Tentang**) +- Lisensi tia sendiri perlu diputuskan (rekomendasi: **MIT**); lihat Open Questions + +### Sumber yang direkomendasikan + +| Kebutuhan | Sumber | Lisensi | Catatan | +|---|---|---|---| +| Efek suara UI (klik, notif, alert) | **Kenney: Interface Sounds / UI Audio** (kenney.nl) | CC0 | Pilihan utama, tanpa kewajiban atribusi | +| Efek suara tambahan | OpenGameArt (filter **CC0** saja) | CC0 | Cek lisensi per file | +| Efek suara tambahan | Freesound (filter **CC0** saja) | CC0 | Hindari CC-BY-NC | +| Palet warna | **Catppuccin** | MIT | Sudah diputuskan | +| Logo ASCII & animasi | **Buatan sendiri** | Ikut lisensi tia | Menghindari masalah lisensi font | +| Font ASCII (jika dipakai) | FIGlet / font lain | **Beda per font** | Wajib cek lisensi tiap font sebelum dipakai | +| Glyph/ikon | Unicode standar (●, ○, ▶, ⟳, ✔, ⚠) | Bebas | **Tidak mewajibkan Nerd Font**; sediakan fallback ASCII | +| Library TUI | Charm (bubbletea, bubbles, lipgloss, huh, glamour, log, harmonica) | MIT | | +| Library Docker | Docker SDK for Go | Apache-2.0 | | +| Library audio | Oto (ebitengine) | Apache-2.0 | | + +### Format aset suara +- Sumber asli biasanya OGG. Untuk menghindari dependensi decoder tambahan, **konversi ke WAV** (44.1 kHz, 16-bit, mono) saat build aset +- Efek suara berdurasi pendek (< 1 detik), target total aset suara **< 2 MB** di dalam binary +- File suara disimpan di `internal/audio/assets/` dan di-embed lewat `go:embed` + +> [!NOTE] +> Lisensi proyek referensi (Dockge, lazydocker, composerize) umumnya MIT, tetapi **wajib diverifikasi ulang langsung dari repo masing-masing** sebelum ada logika yang diadaptasi. Lihat bagian REA Skill. diff --git a/docs/rea/README.md b/docs/rea/README.md new file mode 100644 index 0000000..376989f --- /dev/null +++ b/docs/rea/README.md @@ -0,0 +1,66 @@ +# REA — Reverse Engineering & Analysis (Playbook) + +> Skill kerja untuk menganalisis **logika backend** proyek referensi, lalu mem-porting perilakunya +> ke Go di dalam `tia`. Definisi lengkap: `docs/PRD.md` §12. + +## Aturan (wajib, tidak bisa dinegosiasi) + +1. **Cek lisensi dulu** — langsung dari repo sumber, sebelum membaca kode serius. Hanya proyek + open source (MIT/Apache/BSD dsb). Jangan pernah menganalisis proyek closed-source. +2. **Tulis ulang, jangan salin.** Pelajari perilaku → implementasi sendiri dalam Go. + Tidak ada copy-paste verbatim. +3. Jika terpaksa dekat dengan aslinya (mis. tabel pemetaan flag): catat di laporan sebagai + "diadaptasi", sertakan atribusi di `THIRD_PARTY_NOTICES.md`. +4. Jangan ambil nama, logo, aset visual, atau branding proyek referensi. +5. Repo referensi di-clone ke `reference/` (git-ignored). **Tidak pernah** ikut ter-commit. + +## Alur kerja + +``` +1 Scope → 2 Map → 3 Trace → 4 Spec → 5 Port → 6 Verify + ↑ │ + └── beda perilaku ┘ +``` + +| Langkah | Aktivitas | Output | +|---|---|---| +| 1. **Scope** | Modul & batasnya, verifikasi lisensi | Catatan scope + lisensi di laporan | +| 2. **Map** | Petakan file/fungsi/struktur data relevan | Peta modul | +| 3. **Trace** | Telusuri alur input → output, edge case & error path | Diagram alur + daftar edge case | +| 4. **Spec** | Spesifikasi netral: input, output, aturan, tabel pemetaan, perintah Docker | `docs/rea/.md` + test vectors | +| 5. **Port** | Implementasi ulang dalam Go (di `internal/core`, tanpa UI) | Kode Go + unit test | +| 6. **Verify** | Uji paritas: input sama → output setara (golden/differential test) | Laporan paritas (lolos/gagal per kasus) | + +## Definition of Done per modul + +- [ ] Lisensi sumber diverifikasi & dicatat (repo, commit, lisensi) +- [ ] Laporan `docs/rea/.md` selesai (pakai `docs/rea/TEMPLATE.md`) +- [ ] Test vectors tersedia +- [ ] Implementasi Go terpisah dari UI (dipakai TUI **dan** CLI) +- [ ] Uji paritas lolos sesuai target (≥ 95% flag umum untuk converter) +- [ ] `THIRD_PARTY_NOTICES.md` diperbarui bila perlu + +## Indeks modul (PRD §12.6) + +| # | Modul | Repo | Fase | Status | +|---|---|---|---|---| +| 1 | Parser `docker run` → compose | `composerize/composerize` | 3 | ✅ [composerize.md](composerize.md) | +| 2 | Scan stack & rekonsiliasi status | `louislam/dockge` | 3 | ⬜ belum | +| 3 | Arsitektur primary ↔ agent (ide multi-host) | `louislam/dockge` | 6 | ⬜ belum | +| 4 | Pola pemanggilan Docker SDK | `jesseduffield/lazydocker` | 2, 4 | ⬜ belum | +| 5 | Manajemen stream/task background | `jesseduffield/lazydocker` | 2 | ⬜ belum | +| 6 | Custom commands (template placeholder) | `jesseduffield/lazydocker` | 5 | ⬜ belum | +| 7 | Format output `docker compose ls/ps/config` | `docker/compose` (CLI lokal) | 3 | ⬜ belum | + +Repos referensi (commit yang dianalisis): + +| Repo | Commit | Lisensi | +|---|---|---| +| composerize | `3e39eb7` | MIT | +| dockge | `f809ae1` | MIT | +| lazydocker | `7e7aadc` | MIT | + +## Cara memanggil modul baru + +Prompt singkat: `REA: analisis modul dari ` — agent mengikuti alur 1–6 di atas, +selalu mulai dari verifikasi lisensi dan membaca `docs/rea/TEMPLATE.md`. diff --git a/docs/rea/TEMPLATE.md b/docs/rea/TEMPLATE.md new file mode 100644 index 0000000..6696e8e --- /dev/null +++ b/docs/rea/TEMPLATE.md @@ -0,0 +1,40 @@ +# REA — + +> Status: DRAFT | SPEC FINAL | PORTED | VERIFIED +> Fase: · Target kode: `` + +## 1. Ringkasan & Tujuan + + +## 2. Sumber +| Item | Nilai | +|---|---| +| Repo | `` | +| Commit | `` | +| Lisensi | `` (diverifikasi dari file LICENSE, tanggal) | +| Tanggal analisis | `` | + +## 3. Alur Eksekusi + + +## 4. Struktur Data & Format I/O + + +## 5. Aturan & Edge Case + + +## 6. Perintah Docker/Compose yang Dipanggil +| Perintah | Kapan | Format output yang diharapkan | +|---|---|---| + +## 7. Test Vectors + + +## 8. Perbedaan yang Disengaja dari Referensi + + +## 9. Rencana Porting Go + + +## 10. Verifikasi Paritas + diff --git a/docs/rea/composerize.md b/docs/rea/composerize.md new file mode 100644 index 0000000..e0db43a --- /dev/null +++ b/docs/rea/composerize.md @@ -0,0 +1,578 @@ +# REA — Modul 1: Parser `docker run` → Compose (composerize) + +> Status: SPEC FINAL · Fase 3 · Target kode: `internal/stack/converter.go` +> Analisis: 2026-10-15 · Metode: baca sumber + fixture test (bukan black-box) + +## 1. Ringkasan & Tujuan + +`composerize` mengubah perintah `docker run` menjadi file compose YAML. Di tia, logika ini menjadi +**konverter inti** yang dipakai dua-duanya: modal konverter TUI (F-008) dan `tia convert` CLI (F-028). + +Yang diporting: **pipeline parsing, tabel pemetaan flag, dan aturan normalisasi**. +Yang tidak diport: fitur merge dengan file compose existing via stdin (ditangani layer stack tia +dengan cara berbeda), website, dan UI mereka. + +## 2. Sumber + +| Item | Nilai | +|---|---| +| Repo | https://github.com/composerize/composerize | +| Commit | `3e39eb7` (clone `--depth 1`, 2026-10-15) | +| Lisensi | **MIT** — Copyright (c) 2024 Composerize (diverifikasi dari file `LICENSE`) | +| File kunci | `packages/composerize/src/{index,logic,mappings}.js`, `cli.js`, `__tests__/*.js` | +| Dependency eksternal | `yargs-parser` (tokenizer), `composeverter` (yamlParse/yamlStringify/migrasi format), `deepmerge` | + +Catatan: `composeverter` adalah paket npm terpisah (tidak ada di repo). Migrasi format +(`version: '3'` → common spec) terjadi di sana. Untuk tia kita langsung memproduksi output +common-spec, jadi bagian itu tidak perlu diport. + +## 3. Alur Eksekusi + +``` +input (string perintah) + │ + ├─ 1. split multi-command: /^(?:\s*\$)?\s*(?:docker|podman)\s+/gm + │ → setiap segmen harus match ^(run|create|container run|service create), + │ kalau bukan → komentar "# ignored : docker/podman " + │ + ├─ 2. normalisasi teks (urutan PENTING): + │ a. hapus komentar baris: /\n\s*#[^\n]+/g + │ b. lindungi $(...) : ganti spasi dalam $() dengan '¤' + │ c. hapus line continuation: /\\\n/g (backslash + newline) + │ d. collapse whitespace: /(\s)+/g → ' ' + │ e. trim + │ f. perbaiki '-p80' → '-p 80': /\s-p(\d)/g + │ g. '\ \' (spasi-backslash-spasi) → ' ' [utf8: '\' literal] + │ h. hapus ';' di akhir: /\s*;$/g + │ + ├─ 3. buang prefix perintah: + │ /^(?:\s*\$\s+)?(?:docker|podman)\s+(run|create|container\s+run|service\s+create)/ + │ + ├─ 4. tokenisasi via yargs-parser: + │ configuration: halt-at-non-option=true, camel-case-expansion=false, + │ boolean-negation=false + │ boolean flags: i interactive t tty d detach rm init help privileged + │ P publish-all no-healthcheck oom-kill-disable + │ read-only sig-proxy + │ → halt-at-non-option: semua token setelah IMAGE menjadi argumen command + │ → boolean-negation=false: '--no-healthcheck' tetap key 'no-healthcheck' + │ + ├─ 5. bersihkan nilai: stripQuotes() + kembalikan '¤' → spasi (dalam $()) + │ + ├─ 6. loop setiap key → MAPPINGS[key] → ComposeEntry → deepmerge ke service + │ key tak dikenal → baris komentar "# --key=value" + │ + ├─ 7. image = token positional pertama (stripQuotes) + │ token positional sisanya → command: gabung dengan spasi + │ + ├─ 8. post-processing: + │ a. network_mode == host → hapus ports + │ b. volume source tanpa '/', '\', '$', '<', '>' → dianggap named volume + │ → top-level volumes + {external: {name}} + komentar + │ c. networks: nilai map kosong → null; semua null → jadikan array nama + │ → top-level networks {external: true, name} + komentar + │ + ├─ 9. bungkus: {version:'3', services:{: service}} — digabung (deepmerge) + │ dengan existing compose bila ada + │ + └─ 10. stringify YAML (indent default library 4, CLI 2) + + prepend komentar (ignored options, external network/volume) + + migrasi format: latest → common spec (hapus version, tambah + "name: "), v2x → "2.4", v3x → "3" +``` + +## 4. Struktur Data & Format I/O + +**Input:** string (satu atau banyak perintah `docker/podman run/create/...`). +**Output:** string YAML, atau `SyntaxError` bila tidak ada satu pun perintah valid: + +``` +must have at least a valid docker/podman run/create/service create/container run command +``` + +**Tipe nilai flag** (`mappings.js`): + +| Type | Arti | +|---|---| +| `Array` | list of string | +| `Value` | string apa adanya | +| `IntValue` / `FloatValue` | `parseInt` / `parseFloat` | +| `Switch` | boolean (`value === 'true'` \|\| `true`) | +| `Envs` | `["K=V", "K"]` — split di `=` pertama, sisanya digabung lagi | +| `Networks` | `host/bridge/none/container:*` → `network_mode`; selain itu → `networks.{nama}` | +| `Map` | nilai tunggal `k=v,k2=v2` → object (nilai numerik → int) | +| `MapArray` | seperti Map tapi tiap kemunculan flag jadi satu object (dipakai `--mount`) | +| `Ulimits` | `type=soft[:hard]` → `ulimits.: int \| {soft,hard}` | +| `Gpus` | `all` \| angka → `deploy.resources.reservations.devices[{driver:nvidia,count,capabilities:[gpu]}]` | +| `DeviceBlockIOConfigWeight` | `path:weight` → `[{path, weight:int}]` | +| `DeviceBlockIOConfigRate` | `path:rate` → `[{path, rate:int\|string}]` | + +**Nama service:** dari image → buang registry (`split('/')` terakhir) → buang tag +(`split(':')[0]`). Image kosong → `!!!invalid!!!`. + +## 5. Aturan & Edge Case + +| # | Kasus | Perilaku referensi | Catatan untuk tia | +|---|---|---|---| +| 1 | Komentar di input | Hanya dihapus jika didahului newline (`/\n\s*#/`) — komentar di baris pertama TIDAK terhapus | Tiru regex persis; uji kasus baris pertama | +| 2 | `$(pwd)`, `$(id -u)` | Spasi di dalam `$()` dilindungi `¤` agar tokenizer tidak memecah | Harus ditiru — krusial, banyak dipakai di compose nyata | +| 3 | Line continuation `\` + newline | Dihapus lalu whitespace di-collapse | Sama | +| 4 | `-p80:80` | Diperbaiki jadi `-p 80:80` (hanya untuk `-p` + digit!) | Sama. `-eFOO=bar` **tidak** diperbaiki — perlu uji differential | +| 5 | `;` di akhir | Dihapus | Sama | +| 6 | `-d` / `--detached` | Diabaikan **diam-diam** (mapping ke path `''`) | Sama | +| 7 | `--detach` (panjang) | Muncul sebagai komentar `# --detach` (inkonsisten dgn `-d`) | tia: ignore senyap keduanya (lihat §8) | +| 8 | `--rm` | Diabaikan diam-diam | Sama | +| 9 | Flag tak dikenal | Komentar `# ignored options for ''` + daftar flag di atas YAML | Sama (tanpa gagal) | +| 10 | `--network host` | `network_mode: host` + ports dihapus | Sama | +| 11 | `--network appnet` | `networks: [appnet]` + top-level `networks.appnet.external: true, name` + komentar peringatan | Sama (parity dgn docker run: network harus sudah ada) | +| 12 | Named volume `-v vol:/data` | `volumes.vol.external: {name: vol}` + komentar | **Berbeda di tia** (lihat §8) | +| 13 | `--ip x` tanpa `--network` | Network fallback = `"default"` → `networks.default.ipv4_address` | Tiru; hasilnya agak aneh tapi konsisten | +| 14 | `--tmpfs` berulang | Digabung salah jadi `"/a,/b"` (String(array)) | tia: list (lihat §8) | +| 15 | `--mount ... ,readonly` | Key tanpa nilai menghasilkan value `undefined` → hilang dari YAML | tia: `readonly: true` (lihat §8) | +| 16 | Command setelah image | Token positional digabung dengan spasi (kutip hilang) | Tiru; tandai untuk uji differential | +| 17 | Kutip pada nilai | `stripQuotes` hanya membuang kutip terluar yang sepasang | Sama | +| 18 | Nilai `-` tunggal / angka | `Map`/`MapArray` mengubah string numerik jadi int | Sama | +| 19 | `--volumes-from` | Dipetakan ke `volume_from` | **BUG referensi** — compose spec = `volumes_from`. tia pakai yang benar | +| 20 | Tanpa perintah valid | `SyntaxError` dengan pesan spesifik | Pesan i18n di layer UI; inti error setara | + +## 6. Perintah Docker/Compose yang Dipanggil + +Tidak ada — murni transformasi string→YAML. (Verifikasi output: `docker compose config` opsional +untuk validasi di luar converter.) + +## 7. Tabel Pemetaan Flag (docker run → compose) + +Diambil dari `mappings.js` + short-flag alias. `[A]`=Array, `[V]`=Value, `[I]`=Int, `[F]`=Float, +`[S]`=Switch, `[E]`=Envs, `[N]`=Networks, `[M]`=Map, `[MA]`=MapArray, `[U]`=Ulimits, `[G]`=Gpus. + +| docker run | compose | tipe | +|---|---|---| +| `--add-host` | `extra_hosts` | A | +| `--blkio-weight` | `blkio_config.weight` | I | +| `--blkio-weight-device` | `blkio_config.weight_device` | DeviceBlockIO | +| `--cap-add` / `--cap-drop` | `cap_add` / `cap_drop` | A | +| `--cgroup-parent` | `cgroup_parent` | V | +| `--cgroupns` | `cgroup` | V | +| `--cpu-period` `--cpu-quota` `--cpu-rt-period` `--cpu-rt-runtime` | `cpu_period` `cpu_quota` `cpu_rt_period` `cpu_rt_runtime` | V (string!) | +| `-c` `--cpu-shares` | `cpu_shares` | I | +| `--cpus` | `deploy.resources.limits.cpus` | F | +| `--device` | `devices` | A | +| `--device-cgroup-rule` | `device_cgroup_rules` | A | +| `--device-read-bps` `--device-read-iops` `--device-write-bps` `--device-write-iops` | `blkio_config.device_*` | DeviceBlockIO | +| `--dns` `--dns-opt` `--dns-search` | `dns` `dns_opt` `dns_search` | A | +| `--domainname` | `domainname` | V | +| `--entrypoint` | `entrypoint` | A (satu elemen) | +| `--env-file` | `env_file` | A | +| `-e` `--env` | `environment` | E | +| `--expose` | `expose` | A | +| `--gpus` | `deploy.resources.reservations.devices` | G | +| `--group-add` | `group_add` | A | +| `--health-cmd` `--health-interval` `--health-retries` `--health-start-period` `--health-timeout` | `healthcheck.{test,interval,retries,start_period,timeout}` | V | +| `--no-healthcheck` | `healthcheck.disable` | S | +| `-h` `--hostname` | `hostname` | V | +| `--init` | `init` | S | +| `-i` `--interactive` | `stdin_open` | S | +| `--ip` `--ip6` | `networks..ipv4_address` / `.ipv6_address` | V | +| `--ipc` | `ipc` | V | +| `--isolation` | `isolation` | V | +| `-l` `--label` | `labels` | A | +| `--link` `--link-local-ip` | `links` / `networks..link_local_ips` | A | +| `--log-driver` | `logging.driver` | V | +| `--log-opt` | `logging.options` | M | +| `--mac-address` | `mac_address` | V | +| `-m` `--memory` | `deploy.resources.limits.memory` | V | +| `--memory-reservation` | `deploy.resources.reservations.memory` | V | +| `--memory-swap` / `--memory-swappiness` | `memswap_limit` / `mem_swappiness` | V | +| `--mount` | `volumes` (long syntax object) | MA | +| `--name` | `container_name` | V | +| `--net` `--network` | `networks` / `network_mode` | N | +| `--network-alias` | `networks..aliases` | A | +| `--oom-kill-disable` | `oom_kill_disable` | S | +| `--oom-score-adj` | `oom_score_adj` | V | +| `--pid` | `pid` | V | +| `--pids-limit` | `deploy.resources.limits.pids` | I | +| `--platform` | `platform` | V | +| `--privileged` | `privileged` | S | +| `-p` `--publish` | `ports` | A | +| `--pull` | `pull_policy` | V | +| `--read-only` | `read_only` | S | +| `--restart` | `restart` | V | +| `--runtime` | `runtime` | V | +| `--security-opt` | `security_opt` | A | +| `--shm-size` | `shm_size` | V | +| `--stop-signal` | `stop_signal` | V | +| `--stop-timeout` | `stop_grace_period` | V | +| `--storage-opt` | `storage_opt` | M | +| `--sysctl` | `sysctls` | A | +| `--tmpfs` | `tmpfs` | V | +| `-t` `--tty` | `tty` | S | +| `--ulimit` | `ulimits` | U | +| `-u` `--user` | `user` | V | +| `--userns` | `userns_mode` | V | +| `--uts` | `uts` | V | +| `-v` `--volume` | `volumes` | A | +| `--volumes-from` | `volumes_from` *(ref: `volume_from` — bug)* | A | +| `-w` `--workdir` | `working_dir` | V | +| `--rm` `-d` `--detached` | *(diabaikan)* | — | + +**Tidak ditangani referensi** (tia boleh menambah nanti, catat sebagai ekstensi): +`--health-start-interval`, `--annotation`, `--cidfile`, `--label-file`, `--attach`, +`-P/--publish-all`, `--sig-proxy`, `--stop-grace-period` (bukan flag docker run; yang benar `--stop-timeout`). + +## 8. Test Vectors + +Format target tia: **common spec** (tanpa `version:`, tanpa `name:` — nama project diurus layer +stack), indent 2, urutan key = urutan flag lalu `image` terakhir (mengikuti referensi). + +### V1 — basic +``` +docker run -p 80:80 foobar/baz:latest +``` +```yaml +services: + baz: + ports: + - 80:80 + image: foobar/baz:latest +``` + +### V2 — flags umum +``` +docker run -d --name web -p 8080:80 -v ./html:/usr/share/nginx/html:ro -e TZ=Asia/Jakarta --restart unless-stopped nginx:latest +``` +```yaml +services: + web: + container_name: web + ports: + - 8080:80 + volumes: + - ./html:/usr/share/nginx/html:ro + environment: + - TZ=Asia/Jakarta + restart: unless-stopped + image: nginx:latest +``` +*(nama service = `nginx` karena diambil dari image; `--name` hanya mengisi `container_name`)* + +### V3 — multi-line + `$(...)` +``` +docker run \ + -d \ + --name tor-relay \ + -e TZ=Europe/London \ + -e PUID=$(id -u) \ + -v $(pwd)/tor/data:/data:Z \ + -p 9001:9001 \ + --restart always \ + ilshidur/tor-relay +``` +```yaml +services: + tor-relay: + container_name: tor-relay + environment: + - TZ=Europe/London + - PUID=$(id -u) + volumes: + - $(pwd)/tor/data:/data:Z + ports: + - 9001:9001 + restart: always + image: ilshidur/tor-relay +``` + +### V4 — kutip pada nilai +``` +docker run --name="foobar" -e GREETING="hello world" nginx +``` +```yaml +services: + nginx: + container_name: foobar + environment: + - GREETING=hello world + image: nginx +``` + +### V5 — `-p80` nempel +``` +docker run -p8080:80 nginx +``` +```yaml +services: + nginx: + ports: + - 8080:80 + image: nginx +``` + +### V6 — flag tak dikenal +``` +docker run -d -z machin --unknown-long=truc nginx +``` +```yaml +# ignored options for 'nginx' +# -z=machin +# --unknown-long=truc +services: + nginx: + image: nginx +``` + +### V7 — network bernama (external) +``` +docker run --network appnet nginx +``` +```yaml +# named network 'appnet' is marked as "external" (used by service 'nginx'), so either remove "external" from network definition or it needs to be created using: docker network create -d bridge appnet +services: + nginx: + networks: + - appnet + image: nginx +networks: + appnet: + external: true + name: appnet +``` + +### V8 — network mode host (ports dibuang) +``` +docker run --network host -p 8080:80 nginx +``` +```yaml +services: + nginx: + network_mode: host + image: nginx +``` + +### V9 — named volume (perilaku tia; §8 perbedaan #3) +``` +docker run -v vol:/data nginx +``` +```yaml +services: + nginx: + volumes: + - vol:/data + image: nginx +volumes: + vol: {} +``` + +### V10 — `--mount` long syntax +``` +docker run --mount type=bind,source=/host,target=/container,readonly nginx +``` +```yaml +services: + nginx: + volumes: + - type: bind + source: /host + target: /container + readonly: true + image: nginx +``` + +### V11 — healthcheck +``` +docker run --health-cmd "curl -f http://localhost/" --health-interval 30s --health-retries 3 --health-timeout 5s --health-start-period 10s nginx +``` +```yaml +services: + nginx: + healthcheck: + test: curl -f http://localhost/ + interval: 30s + retries: 3 + timeout: 5s + start_period: 10s + image: nginx +``` + +### V12 — log opt (berulang → satu object) +``` +docker run --log-opt max-size=1g --log-opt max-file=3 --restart always nginx +``` +```yaml +services: + nginx: + logging: + options: + max-size: 1g + max-file: 3 + restart: always + image: nginx +``` + +### V13 — limits +``` +docker run --memory 512m --cpus 1.5 --pids-limit 200 nginx +``` +```yaml +services: + nginx: + deploy: + resources: + limits: + memory: 512m + cpus: 1.5 + pids: 200 + image: nginx +``` + +### V14 — gpus +``` +docker run --gpus all nvidia/cuda:12.0-base +``` +```yaml +services: + cuda: + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: + - gpu + image: nvidia/cuda:12.0-base +``` + +### V15 — command setelah image +``` +docker run nginx:alpine nginx -g daemon off; +``` +```yaml +services: + nginx: + image: nginx:alpine + command: nginx -g daemon off +``` +*(`;` akhir dihapus oleh normalisasi)* + +### V16 — multi perintah + perintah diabaikan +``` +docker run -p 80:80 foo/bar:latest +docker stop +docker run -p 80:80 foo/buzz:latest +``` +```yaml +# ignored : docker/podman stop +services: + bar: + ports: + - 80:80 + image: foo/bar:latest + buzz: + ports: + - 80:80 + image: foo/buzz:latest +``` + +### V17 — error +``` +foo bar +``` +→ error (setara): `must have at least a valid docker/podman run/create/service create/container run command` + +### V18 — env-file + env +``` +docker run --env-file .env -e DEBUG=1 app/api:1.2 +``` +```yaml +services: + api: + env_file: + - .env + environment: + - DEBUG=1 + image: app/api:1.2 +``` + +### V19 — ulimit +``` +docker run --ulimit nofile=1024:2048 --ulimit nproc=65535 nginx +``` +```yaml +services: + nginx: + ulimits: + nofile: + soft: 1024 + hard: 2048 + nproc: 65535 + image: nginx +``` + +### V20 — ip + network +``` +docker run --network appnet --ip 10.0.0.5 nginx +``` +```yaml +# named network 'appnet' is marked as "external" ... +services: + nginx: + networks: + appnet: + ipv4_address: 10.0.0.5 + image: nginx +networks: + appnet: + external: true + name: appnet +``` + +> **Perlu verifikasi differential** (langsung lawan composerize asli, butuh Node): +> kutip di dalam `command`, `-eFOO=bar` (short flag nempel), `--` separator, +> `--privileged false`, path Windows (`C:\`), dan format kutip YAML-nya. +> Daftar ini masuk korpus uji paritas Fase 3. + +## 9. Perbedaan yang Disengaja dari Referensi + +| # | Perbedaan | Alasan | +|---|---|---| +| 1 | Output = **common spec** murni: tanpa `version:`, tanpa `name: ` | Tujuannya tia: file stack valid di Compose v2+; nama project ditentukan folder stack, bukan placeholder | +| 2 | Indent default **2** (referensi library 4, CLI 2) | Konvensi compose + samakan dengan perilaku CLI referensi | +| 3 | **Named volume tidak** di-mark `external` (`volumes: {vol: {}}`) | `docker run -v vol:/data` auto-create volume; `external` mengubah perilaku (harus sudah ada). Network tetap `external: true` karena `docker run --network X` memang menuntut X sudah ada | +| 4 | `--volumes-from` → `volumes_from` | `volume_from` adalah bug referensi (bukan field compose spec) | +| 5 | `--tmpfs` berulang → list `["/a", "/b"]` | Referensi menggabung jadi `"/a,/b"` (bug) | +| 6 | `--mount ...,readonly` → `readonly: true` | Referensi menjatuhkan key tanpa nilai | +| 7 | `-d` dan `--detach` sama-sama diabaikan senyap | Referensi inkonsisten (`-d` senyap, `--detach` komentar) | +| 8 | Tidak ada merge stdin; "tambah ke stack" ditangani layer stack (baca-tulis YAML compose) | Arsitektur tia: converter tetap murni string→string agar bisa dipakai TUI & CLI dengan hasil identik | +| 9 | Pesan error dibungkus i18n (F-023) | Prinsip tia; isi/arti error tetap sama | + +## 10. Rencana Porting Go + +File: `internal/stack/converter.go` (+ `converter_test.go`, `testdata/`). + +``` +Tokenizer (tulis sendiri, ~80 baris): + 1. preprocess: komentar, $() → placeholder, line-continuation, collapse space + 2. split token dgn dukungan kutip tunggal/ganda + 3. klasifikasi: --opt=val | --opt val | -x val | -xval | -it (cluster) | positional + 4. halt-at-non-option: setelah positional pertama → semua ke args command +Parser: map[string][]string (key → nilai; berulang jadi slice, urutan dipertahankan) +Mapper: tabel MAPPINGS di Go (key → type + path) +Builder: bangun pohon kompose secara TERURUT (yaml.Node / ordered map) → yaml.v3 +``` + +Keputusan desain: +- **Urutan key deterministik** — map Go tidak berurutan; pakai `yaml.Node` (atau tipe ordered + map kecil) agar output stabil dan bisa dibandingkan di golden test. +- Tabel MAPPINGS ditulis **dari perilaku** (bukan copy kode JS); atribusi di `THIRD_PARTY_NOTICES.md`. +- Test vectors V1–V20 di atas dikonsumsi sebagai golden test. Differential test vs composerize + asli dijalankan di CI (`node`, npm `composerize`), YAML dinormalisasi (sort key) lalu dibandingkan; + target paritas ≥ 95% untuk flag umum — selisih yang tersisa wajib ada di tabel §9. + +## 11. Verifikasi Paritas + +| Item | Status | +|---|---| +| Test vectors tertulis | ✅ (V1–V20) | +| Golden test di Go | ⬜ Fase 3 | +| Differential vs composerize (Node) | ⬜ Fase 3 — perlu `node`/`npm` di CI | +| Laporan paritas | ⬜ Fase 3 |