docs(AGENTS): update Nebula project guidelines and knowledge base
This commit is contained in:
292
AGENTS.md
292
AGENTS.md
@@ -1,227 +1,133 @@
|
||||
# AGENTS.md - Nebula Project Guidelines
|
||||
# NEBULA PROJECT KNOWLEDGE BASE
|
||||
|
||||
## Project Overview
|
||||
**Generated:** 2026-01-11 | **Commit:** fcbe848 | **Branch:** feat/pkg
|
||||
|
||||
Nebula is a Go/Templ wallet application with HTMX 4 for server-driven UI and WebAwesome (wa-*) web components.
|
||||
## OVERVIEW
|
||||
|
||||
**Stack**: Go 1.25+ | templ v0.3.977 | htmx 4.0.0-alpha5 | WebAwesome | SQLite WASM
|
||||
Go/Templ wallet UI framework using HTMX 4 for server-driven interactions and WebAwesome (`wa-*`) web components. Hybrid: root is a library (`package nebula`), `cmd/server/` is the reference app.
|
||||
|
||||
## Build & Run Commands
|
||||
|
||||
```bash
|
||||
# Generate Go code from .templ files (REQUIRED after any .templ changes)
|
||||
templ generate
|
||||
|
||||
# Build the project
|
||||
go build ./...
|
||||
|
||||
# Run development server (serves on :8080)
|
||||
go run main.go
|
||||
|
||||
# Watch mode for templ files
|
||||
templ generate --watch
|
||||
|
||||
# Full build + run + open browser
|
||||
make all
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
## STRUCTURE
|
||||
|
||||
```
|
||||
nebula/
|
||||
├── models/ # Data types (MVC models)
|
||||
├── views/ # Page templates (.templ)
|
||||
├── layouts/ # Base layouts (DashboardLayout, CenteredLayout)
|
||||
├── components/ # Reusable UI components
|
||||
├── handlers/ # HTTP route handlers
|
||||
├── _migrate/ # HTML prototypes to convert
|
||||
├── HTMX.md # htmx 4 patterns documentation
|
||||
└── SQLC.md # Database schema documentation
|
||||
├── cmd/server/ # App entry: main.go + routes.go (handlers HERE, not handlers/)
|
||||
├── views/ # Page templates (.templ) - XxxPage, XxxContent, XxxWithStepper
|
||||
├── components/ # Reusable UI (sidebar, stepper, navbar, charts)
|
||||
├── layouts/ # Base (HTML shell), App (dashboard), Centered (auth)
|
||||
├── models/ # Data structs + DefaultXxxData() factories
|
||||
├── htmx/ # HTMX 4 context/request/response helpers
|
||||
├── pkg/config/ # Config models for middleware injection
|
||||
├── _migrate/ # HTML prototypes pending conversion (technical debt)
|
||||
├── middleware.go # Injects HTMX + Config into request context
|
||||
├── mount.go # Mount() helper for handler registration
|
||||
├── options.go # WithXxx() functional options
|
||||
└── config.go # DefaultConfig() + Config struct
|
||||
```
|
||||
|
||||
## Code Style
|
||||
## WHERE TO LOOK
|
||||
|
||||
### Go Files
|
||||
| Task | Location | Notes |
|
||||
|------|----------|-------|
|
||||
| Add route | `cmd/server/routes.go` | Go 1.22+ patterns: `GET /path/{param}` |
|
||||
| Add page | `views/` + `models/` | Create XxxPage + model, register in routes.go |
|
||||
| HTMX patterns | `HTMX.md` | `<hx-partial>`, morphing, SSE, OOB updates |
|
||||
| Modify layout | `layouts/base.templ` | CDN scripts, htmx-config meta tag |
|
||||
| WebAwesome ref | [webawesome.com](https://webawesome.com) | All `wa-*` components |
|
||||
| Migrate HTML | `_migrate/` -> `views/` | See MIGRATION.md |
|
||||
|
||||
- Standard `gofmt` formatting
|
||||
- Package-level types in `models/` directory
|
||||
- Handlers in `handlers/routes.go` with pattern: `handleX(w http.ResponseWriter, r *http.Request)`
|
||||
- Use `r.Header.Get("HX-Request") == "true"` to detect HTMX requests
|
||||
- Return partials for HTMX, full pages for direct navigation
|
||||
## CONVENTIONS
|
||||
|
||||
### Templ Files (.templ)
|
||||
### Naming
|
||||
|
||||
- One main page template per file: `PageName(data ModelType) templ.Component`
|
||||
- Helper functions lowercase: `helperName()`
|
||||
- CSS-in-templ using `templ css` blocks or `<style>` tags
|
||||
- Component parameters use Go types from `models/` package
|
||||
| Type | Pattern | Example |
|
||||
|------|---------|---------|
|
||||
| Page template | `XxxPage(data)` | `DashboardPage(data, tab)` |
|
||||
| Partial (HTMX) | `XxxContent`, `XxxTab` | `DashboardContent(data, tab)` |
|
||||
| OOB update | `XxxWithStepper`, `XxxWithOOB` | `WelcomeStepWithStepper(step)` |
|
||||
| Model | `XxxData`, `XxxState` | `DashboardData`, `LoginState` |
|
||||
| Factory | `DefaultXxxData()` | `DefaultDashboardData()` |
|
||||
| Handler | `handleXxx` | `handleDashboard` |
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
| Type | Convention | Example |
|
||||
|------|------------|---------|
|
||||
| Page template | `XxxPage` | `SettingsPage`, `DashboardPage` |
|
||||
| Partial template | `XxxContent`, `XxxTab` | `DashboardContent`, `ProfileTab` |
|
||||
| OOB update | `XxxWithOOB`, `XxxWithStepper` | `LoginStepWithOOB` |
|
||||
| Model struct | PascalCase | `SettingsData`, `ProfileSettings` |
|
||||
| Handler func | `handleXxx` | `handleSettings`, `handleDashboard` |
|
||||
|
||||
## HTMX 4 Patterns
|
||||
|
||||
### Partial Updates
|
||||
|
||||
```html
|
||||
<div hx-get="/endpoint" hx-target="#target" hx-swap="innerHTML">
|
||||
```
|
||||
|
||||
### Out-of-Band Updates
|
||||
### HTMX 4 Request Pattern
|
||||
|
||||
```go
|
||||
templ ComponentWithOOB() {
|
||||
@MainContent()
|
||||
<div id="sidebar" hx-swap-oob="true">
|
||||
@UpdatedSidebar()
|
||||
func handleXxx(w http.ResponseWriter, r *http.Request) {
|
||||
data := models.DefaultXxxData()
|
||||
if r.Header.Get("HX-Request") == "true" {
|
||||
views.XxxContent(data).Render(r.Context(), w) // Partial
|
||||
return
|
||||
}
|
||||
views.XxxPage(data).Render(r.Context(), w) // Full page
|
||||
}
|
||||
```
|
||||
|
||||
### OOB Updates (Multi-Target)
|
||||
|
||||
```templ
|
||||
templ XxxStepWithStepper(step int) {
|
||||
@XxxStepContent(step)
|
||||
<div id="stepper-container" hx-swap-oob="innerHTML">
|
||||
@components.Stepper(step, steps)
|
||||
</div>
|
||||
}
|
||||
```
|
||||
|
||||
### HTMX Request Detection
|
||||
### Templ Syntax Quick Ref
|
||||
|
||||
```go
|
||||
if r.Header.Get("HX-Request") == "true" {
|
||||
views.PartialContent(data).Render(r.Context(), w)
|
||||
return
|
||||
}
|
||||
views.FullPage(data).Render(r.Context(), w)
|
||||
```templ
|
||||
if cond { } // Conditional
|
||||
for _, item := range items { } // Loop
|
||||
<input disabled?={ isDisabled } /> // Bool attr
|
||||
<div class={ "base " + extra }> // String interpolation
|
||||
@ChildComponent(props) // Composition
|
||||
@templ.Raw(html) // Raw HTML (avoid)
|
||||
```
|
||||
|
||||
## WebAwesome Components
|
||||
## ANTI-PATTERNS (THIS PROJECT)
|
||||
|
||||
Use `wa-*` web components for UI:
|
||||
| DO NOT | Why |
|
||||
|--------|-----|
|
||||
| Edit `*_templ.go` files | Generated by `templ generate` - changes purged |
|
||||
| Use separate `htmx-ext-*` packages | HTMX 4 bundles extensions at `/dist/ext/` |
|
||||
| Rely on implicit inheritance | Use `:inherited` modifier (`hx-target:inherited`) |
|
||||
| Use `TopBar` or `TopBarStyles` | DEPRECATED - use `wa-page` slots instead |
|
||||
| Manual DOM manipulation | Use `hx-*` attributes; server controls state |
|
||||
| Put handlers in `handlers/` | Docs outdated - use `cmd/server/routes.go` |
|
||||
|
||||
```html
|
||||
<wa-button variant="brand">Click me</wa-button>
|
||||
<wa-card>Content</wa-card>
|
||||
<wa-tab-group>
|
||||
<wa-tab panel="one">Tab 1</wa-tab>
|
||||
<wa-tab-panel name="one">Panel 1</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
<wa-icon name="user"></wa-icon>
|
||||
<wa-avatar initials="JD"></wa-avatar>
|
||||
```
|
||||
|
||||
## Route Registration
|
||||
|
||||
All routes in `handlers/routes.go`:
|
||||
|
||||
```go
|
||||
func RegisterRoutes(mux *http.ServeMux) {
|
||||
mux.HandleFunc("GET /path", handlePath)
|
||||
mux.HandleFunc("POST /path/action", handleAction)
|
||||
}
|
||||
```
|
||||
|
||||
Go 1.22+ routing patterns:
|
||||
- `GET /path` - method prefix
|
||||
- `GET /path/{param}` - path parameters via `r.PathValue("param")`
|
||||
- `GET /path/{param...}` - catch-all
|
||||
|
||||
## Model Pattern
|
||||
|
||||
Models in `models/` package with `DefaultXxxData()` factory functions:
|
||||
|
||||
```go
|
||||
// models/example.go
|
||||
package models
|
||||
|
||||
type ExampleData struct {
|
||||
Field1 string
|
||||
Field2 int
|
||||
Items []Item
|
||||
}
|
||||
|
||||
func DefaultExampleData() ExampleData {
|
||||
return ExampleData{
|
||||
Field1: "default",
|
||||
Items: []Item{...},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Migration Pattern (HTML to Templ)
|
||||
|
||||
When converting `_migrate/*.html` files:
|
||||
|
||||
1. Create model types in `models/xxx.go`
|
||||
2. Create view in `views/xxx.templ`
|
||||
3. Add route in `handlers/routes.go`
|
||||
4. Update sidebar active state in `components/sidebar.templ`
|
||||
|
||||
### Templ Syntax Reference
|
||||
|
||||
```go
|
||||
// Conditionals
|
||||
if condition {
|
||||
<div>shown</div>
|
||||
}
|
||||
|
||||
// Loops
|
||||
for _, item := range items {
|
||||
<div>{ item.Name }</div>
|
||||
}
|
||||
|
||||
// Boolean attributes
|
||||
<input disabled?={ isDisabled } />
|
||||
<wa-tab active?={ tab == "profile" }>
|
||||
|
||||
// String interpolation
|
||||
<div class={ "base " + extraClass }>
|
||||
<div data-id={ item.ID }>
|
||||
|
||||
// Raw HTML (use sparingly)
|
||||
@templ.Raw(htmlString)
|
||||
|
||||
// Component composition
|
||||
@ChildComponent(props)
|
||||
```
|
||||
|
||||
## Testing
|
||||
## COMMANDS
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
go test ./...
|
||||
# Generate templ (REQUIRED after .templ changes)
|
||||
templ generate
|
||||
|
||||
# Run with verbose
|
||||
go test -v ./...
|
||||
# Dev server on :8080
|
||||
go run ./cmd/server
|
||||
# OR
|
||||
make start
|
||||
|
||||
# Run specific package
|
||||
go test ./handlers/...
|
||||
# Full cycle: generate + start + open browser
|
||||
make all
|
||||
|
||||
# Build binary
|
||||
make build # outputs ./nebula
|
||||
|
||||
# Watch mode
|
||||
templ generate --watch
|
||||
```
|
||||
|
||||
## Common Tasks
|
||||
## STACK VERSIONS
|
||||
|
||||
### Add New Page
|
||||
- **Go**: 1.25.5 (cutting-edge)
|
||||
- **templ**: v0.3.977
|
||||
- **htmx**: 4.0.0-alpha5 (CDN)
|
||||
- **WebAwesome**: Kit 47c7425b971f443c (CDN)
|
||||
- **D3.js**: v7 (charts)
|
||||
|
||||
1. Create `models/newpage.go` with data types
|
||||
2. Create `views/newpage.templ` with page template
|
||||
3. Add handler in `handlers/routes.go`
|
||||
4. Add nav item in `components/sidebar.templ` if needed
|
||||
## NOTES
|
||||
|
||||
### Add New Component
|
||||
|
||||
1. Create in `components/newcomp.templ`
|
||||
2. Import in views: `import "nebula/components"`
|
||||
3. Use: `@components.NewComp(props)`
|
||||
|
||||
### Modify Layout
|
||||
|
||||
Layouts in `layouts/`:
|
||||
- `base.templ` - HTML document wrapper with htmx CDN
|
||||
- `app.templ` - Dashboard app shell with sidebar
|
||||
- `centered.templ` - Centered auth pages
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **templ**: Template engine - `go install github.com/a-h/templ/cmd/templ@latest`
|
||||
- **htmx 4**: Loaded from CDN in `layouts/base.templ`
|
||||
- **WebAwesome**: Loaded from CDN in `layouts/base.templ`
|
||||
- **No tests exist** - `go test ./...` returns "no test files"
|
||||
- **No CI/CD** - No `.github/workflows/`
|
||||
- **SQLite WASM** mentioned in docs but not implemented
|
||||
- **Documentation references `handlers/`** - doesn't exist; handlers are in `cmd/server/routes.go`
|
||||
- Config split: root `config.go` (app config) vs `pkg/config/` (HTMX feature flags)
|
||||
- Middleware chain: `nebula.Middleware(opts...)(mux)` injects context for templates
|
||||
|
||||
Reference in New Issue
Block a user