228 lines
5.1 KiB
Markdown
228 lines
5.1 KiB
Markdown
# AGENTS.md - Nebula Project Guidelines
|
|
|
|
## Project Overview
|
|
|
|
Nebula is a Go/Templ wallet application with HTMX 4 for server-driven UI and WebAwesome (wa-*) web components.
|
|
|
|
**Stack**: Go 1.25+ | templ v0.3.977 | htmx 4.0.0-alpha5 | WebAwesome | SQLite WASM
|
|
|
|
## 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
|
|
|
|
```
|
|
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
|
|
```
|
|
|
|
## Code Style
|
|
|
|
### Go Files
|
|
|
|
- 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
|
|
|
|
### Templ Files (.templ)
|
|
|
|
- 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
|
|
|
|
### 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
|
|
|
|
```go
|
|
templ ComponentWithOOB() {
|
|
@MainContent()
|
|
<div id="sidebar" hx-swap-oob="true">
|
|
@UpdatedSidebar()
|
|
</div>
|
|
}
|
|
```
|
|
|
|
### HTMX Request Detection
|
|
|
|
```go
|
|
if r.Header.Get("HX-Request") == "true" {
|
|
views.PartialContent(data).Render(r.Context(), w)
|
|
return
|
|
}
|
|
views.FullPage(data).Render(r.Context(), w)
|
|
```
|
|
|
|
## WebAwesome Components
|
|
|
|
Use `wa-*` web components for UI:
|
|
|
|
```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
|
|
|
|
```bash
|
|
# Run all tests
|
|
go test ./...
|
|
|
|
# Run with verbose
|
|
go test -v ./...
|
|
|
|
# Run specific package
|
|
go test ./handlers/...
|
|
```
|
|
|
|
## Common Tasks
|
|
|
|
### Add New Page
|
|
|
|
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
|
|
|
|
### 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`
|