# Todo-Glance A server-backed CRUD todo widget for [Glance](https://github.com/glanceapp/glance). Stores tasks in SQLite instead of browser localStorage, so your todos persist across devices and sessions. Integrates as a Glance Extension widget with native theming via Glance CSS variables. ## Stack - **Go** — single `main.go`, no frameworks - **SQLite** via [modernc.org/sqlite](https://pkg.go.dev/modernc.org/sqlite) — pure Go, no CGO required - **Vanilla JS** — embedded in the Go binary, no build step ## Quick Start ### Docker Compose (recommended) ```bash docker compose up --build ``` - Glance dashboard: `http://localhost:8080` - Todo API: `http://localhost:8081` The included `glance.yml` has the widget pre-configured. Edit it to add your other Glance widgets. ### Standalone ```bash go build -o todo-glance . ./todo-glance ``` Then add the widget to your existing Glance config: ```yaml - type: extension url: http://todo-glance:8081/ allow-potentially-dangerous-html: true cache: 1s ``` Replace `todo-glance` with the hostname or IP where the server is reachable from Glance. ## Environment Variables | Variable | Default | Description | |---|---|---| | `TODO_PORT` | `8081` | Server listen port | | `TODO_DB_PATH` | `./todos.db` | SQLite database file path | | `TODO_EXTERNAL_URL` | *(from Host header)* | URL browsers use to reach the API. Required when the browser-facing URL differs from the internal one (e.g. behind a reverse proxy). | ## API | Method | Path | Description | |---|---|---| | `GET` | `/` | Extension widget HTML (returns `Widget-Title` and `Widget-Content-Type` headers) | | `GET` | `/api/todos` | List all todos | | `POST` | `/api/todos` | Create a todo — `{"text": "...", "priority": 0-3, "tags": [...]}` | | `PUT` | `/api/todos/{id}` | Update a todo — `{"text": "...", "done": true, "priority": 0-3, "tags": [...]}` | | `DELETE` | `/api/todos/{id}` | Delete a todo — returns 204 | Priority levels: `0` = none, `1` = low, `2` = medium, `3` = high. Todos are sorted by: incomplete first, then highest priority, then newest. ### Examples ```bash # Create with priority and tags curl -X POST -H 'Content-Type: application/json' \ -d '{"text":"Fix login bug","priority":3,"tags":["work","urgent"]}' \ http://localhost:8081/api/todos # Create simple curl -X POST -H 'Content-Type: application/json' \ -d '{"text":"Buy milk"}' http://localhost:8081/api/todos # List (sorted by priority) curl http://localhost:8081/api/todos # Mark done curl -X PUT -H 'Content-Type: application/json' \ -d '{"done":true}' http://localhost:8081/api/todos/1 # Change priority curl -X PUT -H 'Content-Type: application/json' \ -d '{"priority":2}' http://localhost:8081/api/todos/1 # Update tags curl -X PUT -H 'Content-Type: application/json' \ -d '{"tags":["work","reviewed"]}' http://localhost:8081/api/todos/1 # Delete curl -X DELETE http://localhost:8081/api/todos/1 ``` ## Architecture ``` Browser (Glance page) │ │ GET / Glance fetches widget HTML from todo-glance │ ─────────────► (internal Docker network) │ │ API calls JS in the widget calls /api/* endpoints │ ─────────────► (browser hits TODO_EXTERNAL_URL / localhost:8081) │ ▼ todo-glance ──► SQLite file (/data/todos.db in Docker) ``` The HTML is served to Glance over the internal network. Once rendered in the browser, the embedded JS makes API calls directly to the todo-glance server using the external URL. CORS headers are set on all `/api/` endpoints to allow this cross-origin access.