docs: document static asset serving, AI extraction UI, and JS conventions
This commit is contained in:
parent
3746568dd7
commit
63765f17d7
1 changed files with 57 additions and 0 deletions
57
CLAUDE.md
57
CLAUDE.md
|
|
@ -331,6 +331,62 @@ let (items, navigator) = build_page_view(page, request, RoasterView::from,
|
|||
ROASTER_PAGE_PATH, ROASTER_FRAGMENT_PATH, search);
|
||||
```
|
||||
|
||||
### Static Assets
|
||||
|
||||
Static files live in `templates/` and are compiled into the binary via `include_str!()`/`include_bytes!()`. Each file needs an explicit route in `application/routes/mod.rs`:
|
||||
|
||||
```rust
|
||||
.route("/styles.css", get(styles))
|
||||
.route("/extract.js", get(extract_js))
|
||||
.route("/favicon.ico", get(favicon))
|
||||
|
||||
async fn extract_js() -> impl IntoResponse {
|
||||
(
|
||||
[("content-type", "application/javascript; charset=utf-8")],
|
||||
include_str!("../../../templates/extract.js"),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
There is no `tower-http` static file serving — all assets are embedded at compile time.
|
||||
|
||||
### AI Extraction Controls
|
||||
|
||||
Pages with AI-powered form filling (roasters, roasts, scan) share a common JavaScript library at `templates/extract.js` served at `/extract.js`. It provides three functions:
|
||||
|
||||
- `triggerPhotoExtract(formId, endpoint, onSuccess)` — opens camera/file picker, reads as data URL
|
||||
- `extractFromText(formId, endpoint, onSuccess)` — reads text from input field
|
||||
- `doExtract(formId, endpoint, body, onSuccess)` — POST to API, toggle waiting state, call callback on success
|
||||
|
||||
Each page provides only its own `onSuccess` callback (e.g., `fillRoasterForm`, `fillRoastForm`, `fillScanForms`).
|
||||
|
||||
#### Element ID Convention
|
||||
|
||||
The shared library locates DOM elements using the `formId` prefix:
|
||||
|
||||
| Element | ID pattern | Purpose |
|
||||
|---------|-----------|---------|
|
||||
| Controls wrapper | `{formId}-extract-controls` | Hidden during extraction |
|
||||
| Waiting message | `{formId}-extract-waiting` | Shown during extraction (spinner + text) |
|
||||
| Error paragraph | `{formId}-extract-error` | Shown on failure |
|
||||
| Text input | `{formId}-extract-text` | Text description input |
|
||||
|
||||
Example `formId` values: `roaster-form`, `roast-form`, `scan`.
|
||||
|
||||
#### Template Structure
|
||||
|
||||
```html
|
||||
<div id="{formId}-extract-controls" class="flex flex-wrap items-center gap-3">
|
||||
<!-- Photo button, text input, Go button -->
|
||||
</div>
|
||||
<div id="{formId}-extract-waiting" class="hidden flex items-center gap-3 text-sm text-amber-700">
|
||||
<!-- Spinner SVG + "Waiting for response…" -->
|
||||
</div>
|
||||
<p id="{formId}-extract-error" class="hidden mt-2 text-sm text-red-600"></p>
|
||||
```
|
||||
|
||||
Buttons wire up via onclick with the callback: `onclick="triggerPhotoExtract('roast-form', '/api/v1/extract-roast', fillRoastForm)"`.
|
||||
|
||||
### Error Handling
|
||||
|
||||
- Domain errors: `RepositoryError` in `domain/errors.rs`
|
||||
|
|
@ -461,6 +517,7 @@ newSentinel.className = "infinite-scroll-sentinel h-4 md:hidden";
|
|||
6. **Commit authorship**: Never add "Co-Authored-By" trailers to commit messages
|
||||
7. **Commit signing**: Never use `--no-gpg-sign` when committing — always allow the default GPG signing
|
||||
8. **Committing**: Do not commit unless explicitly asked to — provide a draft commit message instead
|
||||
9. **JavaScript style**: Use ES6+ syntax — `const`/`let`, arrow functions, template literals
|
||||
|
||||
## Communication Style
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue