notes
All repositories: gitoria
9.6 KB
# notes.worldapi.orgA notes app in Hybriel (hl:web). Log in with ident; the **sidebar** lists your notes by subject, last edited first;a click opens the note on the right, edited with the Markdown editor `<md-editor>`; **New note** and **Delete note**.The notes are also kept **in the browser (IndexedDB)**: they open and can be edited offline, and sync with the server(and other devices) when the network is back. Concept: `CONCEPT.md`.## How it works- **Subject** = the first non-empty line of the note without leading `#` (at most 80 characters); an empty note is"Untitled note". No separate title field.- **Order** = `edited` (ms), last first. Only a *changed* text moves a note to the top.- **Pages**: `/` (the list; on a phone the list alone) and `/note/:id` (the note; on a phone it replaces the list,"All notes" leads back; from 50rem wide both show side by side). Signed out: only "Log in with ident".- **Offline + sync** (ticket notes#2): `notes-offline.js` fills the notes area from the browser's own copy (IndexedDB`notes-<user>`), every change is written there first and sent to `/api/notes` about 0.6 s later (and every 10 s apull looks for other devices' changes; also on "online", on returning to the tab; other tabs follow throughBroadcastChannel). `sw.js` (service worker, network first) keeps the page and assets, so a reload works with nonetwork; the page carries no note data. A note made offline gets a local id (`l…`) and its server id at the firstpush (the address follows). **Conflict** (changed here and elsewhere): the later edit wins, the other version is keptas a copy note ("kept as a copy …" line at its end). A delete of a note changed elsewhere meanwhile is refused (thechanged note stays). Deleted notes stay on the server as tombstones (`gone`) so other devices learn of it. Log outfirst sends what is unsent, then this browser deletes its copy of the notes.- **Private**: every function in `notes.hl` takes the owner's user id and only reaches that user's notes; anotheruser's note id answers "does not exist"; saving/deleting it is refused. A note is at most 200 000 characters(refused, never cut).- **Login**: ident's button flow (`/login/callback` → `?ident_code=` → server-side exchange) plus the identityselector, as in gitoria; the session cookie is `notessid`, idle time 14 days. The session carries the users `@id`only; the ident identity id never leaves the server.- **Short ids (ident#23, antcolony mission 039)**: `users.identity` holds what ident's exchange answers — since ident#23 the identity'spublic 5-character short id (`a68sz`), before that the per-app id (32 hex); `users.hl isIdentId` accepts both (the old`isHex` check refused short ids). The switch: one-off `tools/migrate-short-ids.hl` (old → short id, idempotent, never`finish`), gate `node tests/short-id-switch.mjs` (ports 8720/8721, no browser), runbook`antcolony-docs/docs/short-id-switch.md` (Byrodin: `/CONTAINERS/projects/antcolony/docs/short-id-switch.md` once synced).## Files| file | what ||---|---|| `project.hl` | routes, login callback, audience, server || `users.hl` | `users` table (`@id`, `!identity`), the ident exchange || `notes.hl` | `notes` table (`@id`, index `owner`; `{ owner, text, created, edited, gone, local }`), sync push / delete / conflict rules || `components/main.hl` | the shell: header, login/logout, faces `notesLogin` / `notesLogOut` || `components/notes.hl` | the page (`/`, `/note/:id`): only `<note-book id="notesapp">` || `notes-offline.js` | the notes area: sidebar, editor, New / Delete, IndexedDB, sync (served at `/notes-offline.js`) || `sw.js` | service worker: keeps the page + assets for offline use || `icons/` | app icons: `icon.svg` (source), `icon-192/512.png`, `apple-touch-icon.png`, `favicon.svg`, `favicon.ico` || `jsoncheck.hl` | JSON syntax pre-check (copy from tickets; hybriel#6) || `styles.hl` | all CSS (mobile first; accent = yellow); `shared/tokens.hl` = copy of worldapi-tokens || `shared/md-editor.js` | vendored copy of worldapi-components `md-editor.js` (served at `/md-editor.js`) || `login.js` | ident selector ↔ shell bridge || `tests/browser.mjs` | the gate (own server, own ident copy, real Chrome); `deploy.sh` runs it |Data: `storage/mpackdb/{users,notes}.db` (never sent by deploy.sh). Only ever open COPIES of live data.## PWA (installable app, antcolony mission 046)- `project.hl` `appIcons` / `appTouchIcon` / `appFavicon` / `appThemeColor` (token `darker`, the header) / `appBackgroundColor`(`dark`) → hl:web serves `/__hl/manifest.webmanifest` (name `notes`, start_url/scope `/`, standalone) and links it,the apple-touch-icon and theme-color from every head. Each icon has its own `file` route in `project.hl`.- **Offline is notes' own**, not hl:web's: no `offline = [...]` in `project.hl`, because hl:web would then register`/__hl/sw.js` for scope `/` and replace `/sw.js` (or be replaced by it). `/sw.js` is registered by `notes-offline.js`once signed in; it is the only worker (the gate checks that, and that `/__hl/sw.js` is 404). Moving to hl:web'soffline = ticket notes#4.- **Icons** (`icons/`): `icon.svg` = source (512; a note page with a folded corner and a pencil, yellow onrgb(25,30,35); everything inside the maskable safe circle r=204, so one image serves `any` and `maskable`);`favicon.svg` = the same drawing cropped tight, thicker strokes. Render:```rsvg-convert -w 192 -h 192 icons/icon.svg -o icons/icon-192.pngrsvg-convert -w 512 -h 512 icons/icon.svg -o icons/icon-512.pngrsvg-convert -w 180 -h 180 icons/icon.svg -o icons/apple-touch-icon.pngfor s in 16 32 48; do rsvg-convert -w $s -h $s icons/favicon.svg -o /tmp/fav-$s.png; donemagick /tmp/fav-16.png /tmp/fav-32.png /tmp/fav-48.png icons/favicon.ico```## The sync API (`project.hl`; JSON, the session cookie is the login; 401 signed out, 400 for bad JSON/fields)- `GET /api/me` → `{ user }` · `GET /api/notes` → `{ notes: [{ id, edited, gone }] }` · `GET /api/notes?id=` → `{ id, text, edited, created }`- `POST /api/notes` `{ op:'save', id, local, text, base, at }` → `{ id, edited, text, copy? }` ·`{ op:'delete', id, base }` → `{ ok }` | `{ kept, id, edited, text }`. `base` = the `edited` the device last saw, `at` = when it edited.## Run and test```NOTES_PORT=8710 NOTES_WATCH=0 ./bin/hybriel project.hl # dev; needs IDENT_API_KEY/SECRET for a loginnode tests/browser.mjs # the gate: ports 8701 (ident), 8702 (app), Chrome 8703–8709# (other ports: NOTES_GATE_PORT / _IDENT_PORT / _CHROME=8722-8726)# includes the PWA checks; screenshots in .scratch/gate-*.png./deploy.sh [--dry-run] # gate → backup → rsync to Byrodin → restart → URL 200 (architect)```Environment: `NOTES_PORT` (dev 8710, container 45007), `NOTES_STORAGE`, `NOTES_SESSIONS`, `NOTES_WATCH=0`,`NOTES_PUBLIC_URL` (default https://notes.worldapi.org), `IDENT_URL`, `IDENT_EXCHANGE_URL`, `IDENT_API_KEY`,`IDENT_API_SECRET` (`.env` beside `project.hl`, never read by workers).## First deploy (architect)Container `notes.worldapi.org` on `127.0.0.1:45007` replaces the demo-notes at the same address: back the demo up,stop it, point the vhost to 45007 (WebSocket Upgrade headers needed), register the app in ident with the origin`https://notes.worldapi.org`, put the key and secret into `.env`.## Hybriel notes- `bin/hybriel` + `plugins/` (core crypto data fetch fs http http1 mpackdb proc time web) are vendored = hybriel **masterff51cf46** (2026-10-01, antcolony mission 048: #113 mpackdb update() duplicate ids, #115/#116 lambda members on faces, #118 `for`over a literal list, client-built SVG namespace, hl:markdown #111 — notes uses none of these directly; before: e6720b1f,antcolony mission 037: #103/#104 client fixes, #105 `headers`, #106 `let` per loop pass, #94 files in emit;earlier #83 hashed `/__hl/…?v=` URLs + immutable cache, #82 socket reconnect, #95–#97 offline/PWA from project.hl — themanifest (`appIcons`) is used since antcolony mission 046, `offline` is NOT: notes keeps its own sw.js / notes-offline.js). Built from a read-only `git archive master` (`zig build-Doptimize=ReleaseFast -Dtarget=x86_64-linux-gnu.2.39` in `native/`), sha256 `e70631f0…eff3471b`. No local patch:re-vendor = copy binary + plugins, run the gate. Previous copy (e6720b1f): `.scratch/pre-048/`.- The notes area is plain JS on purpose: IndexedDB, a service worker and offline edits cannot go through hl:web'sfaces (they need the socket; it never reopens, hybriel#82). The framework's link click and `popstate` would navigatethrough the socket, so the area claims its own link clicks and uses `history.replaceState` (Back leaves the app).- Gate needs a secure origin for the service worker: Chrome runs with `--unsafely-treat-insecure-origin-as-secure`.- Server restart closed open tabs' sockets for good (hybriel#82) — fixed in the vendored master since 035 (reconnect); notchecked by the notes gate. The notes area still uses its own JS (candidate for hl:web offline, #95–#97).## History and worker briefs- `LOG.md` — append-only history, one dated line per step (moved here from the antcolony LOG on 2026-10-01).- `missions/NNN-*.md` — worker briefs for this app; `reports/NNN-*.md` — their reports (same name). Numbered perproject since 2026-10-01 (antcolony#40); older text, code comments and commits use the old antcolony numbers →map: `/media/STORAGE/projects/antcolony-docs/docs/mission-map.md` (Byrodin: `/CONTAINERS/projects/antcolony/docs/mission-map.md`).
Branches
- mainmain branch
Latest commits
- 124613b6antcolony#40: mission references point to the moved missionsmre
- 1ca2f34dantcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
- 2bebebdanotes: Hybriel master ff51cf46 (re-vendor round)mre
- 3eff126dnotes#3: installable app (manifest + own icon/favicon; notes' own sw.js kept)mre
- 9883c540deploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
- eee693b8deploy.sh: never send .git or .gitignore to Byrodinmre
- c8904061State of 2026-09-27, before the move to gitoriamre