# Hostkeeper V2 — HTMX Components & Partials > **Status**: Menggantikan React Component Tree > **Last Updated**: 2026-07-07 > **Framework**: Go HTML Template + HTMX + Alpine.js --- ## 1. Template Structure ``` views/ ├── layout.html ← Base layout (html, head, body, sidebar, header, scripts) ├── index.html ← Entry: {{template "layout" .}} + {{template "content" .}} │ └── partials/ ← HTMX-swappable partial fragments ├── nav.html ├── dashboard.html ├── host_list.html ├── host_card.html ├── host_modal.html ├── terminal.html ├── sftp.html ├── sftp_pane.html ├── snippets.html ├── snippet_grid.html ├── snippet_modal.html ├── keychain.html ├── key_grid.html ├── key_modal.html ├── settings.html ├── brief.html └── toast.html ``` --- ## 2. Komponen per Halaman ### 2.1 Navigasi (layout.html + nav.html) ```html Hostkeeper V2
{{template "nav" .}}

{{.Title}}

{{block "content" .}}{{end}}
``` ### 2.2 Dashboard (partials/dashboard.html) ```html
...
...
...
{{template "host_list" .Hosts}}
{{template "host_list_table" .Hosts}}
``` ### 2.3 Terminal (partials/terminal.html) ```html
``` ### 2.4 SFTP (partials/sftp.html) ```html
{{svg "hard-drive"}} Local Workstation
{{template "sftp_pane" .LocalFiles}}
... sama seperti local pane ...
Drop files here
``` ### 2.5 Snippets (partials/snippets.html) ```html

Collections

All Favorites Recent

Tags

{{range .AllTags}} {{.}} {{end}}
{{template "snippet_grid" .Snippets}}
``` ### 2.6 Keychain (partials/keychain.html) ```html
{{range .Keys}}
{{svg "key"}} {{.Name}}
{{.Strength}}
User {{.User}}
Passphrase
{{.Fingerprint}}
{{end}}
``` ### 2.7 Settings (partials/settings.html) ```html

Profile

Security & Transmission

{{range .Toggles}}
{{.Label}}

{{.Desc}}

{{end}}

Connected Devices

{{range .Devices}}
{{svg .Type}} {{.Name}} {{.Status}}
{{end}}

Danger Zone

Flush all app state — this cannot be undone.

``` ### 2.8 Brief Overlay (partials/brief.html) ```html

System Brief

Color Palette

Typography

Inter — Interface UI

JetBrains Mono — Code & Data

Glassmorphism

backdrop-blur-md + rgba(255,255,255,0.7)

``` --- ## 3. Alpine.js Components (State Management) ### 3.1 Terminal Tabs ```javascript // inline dalam terminal.html function terminalTabs() { return { tabs: [{ id: '1', title: 'localhost' }], activeTab: '1', addTab() { const id = String(Date.now()); this.tabs.push({ id, title: `host-${this.tabs.length + 1}` }); this.activeTab = id; this.$nextTick(() => initTerminal(id)); }, closeTab(id) { this.tabs = this.tabs.filter(t => t.id !== id); if (this.activeTab === id) this.activeTab = this.tabs[0]?.id; }, switchTab(id) { this.activeTab = id; this.$nextTick(() => focusTerminal(id)); } } } ``` ### 3.2 Toast Notifications ```javascript // inline dalam sftp.html, dashboard.html function toastManager() { return { message: '', visible: false, show(msg) { this.message = msg; this.visible = true; setTimeout(() => this.visible = false, 3000); } } } ``` --- ## 4. JavaScript Files (Vanilla) ### static/js/clipboard.js ```javascript function copyToClipboard(text, triggerId) { navigator.clipboard.writeText(text).then(() => { // trigger toast via htmx event document.body.dispatchEvent(new CustomEvent('showToast', { detail: { message: 'Copied!' } })); }); } ``` ### static/js/toggles.js ```javascript function toggleSwitch(el) { const checked = el.getAttribute('aria-checked') === 'true'; el.setAttribute('aria-checked', !checked); el.classList.toggle('active'); fetch(`/settings/toggle?key=${el.dataset.key}&val=${!checked}`, { method: 'POST' }); } ``` ### static/js/utils.js ```javascript function revealPassphrase(el) { const span = el.closest('.pass-row').querySelector('.pass-text'); const isHidden = span.dataset.hidden === 'true'; span.textContent = isHidden ? span.dataset.value : '••••••••'; span.dataset.hidden = isHidden ? 'false' : 'true'; } function strengthClass(len) { if (len > 12) return 'secure'; if (len > 7) return 'moderate'; return 'weak'; } ```