From 70d7398d5ad45c6e243b738b54b730a66eb0b450 Mon Sep 17 00:00:00 2001 From: kristianwind Date: Wed, 9 Sep 2026 08:52:54 +0200 Subject: [PATCH] docs: skriv det ned, der er blevet bygget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API-dokumentationen har fulgt med hele vejen — der er en prøve, der fejler, hvis en rute ikke står i openapi.yaml, og den fangede mig fire gange i går. Brugervejledningen har ikke fulgt med, og det er den, folk læser. Nyt: en side om beskeder — hvad der udløser en, hvorfor gentagne rettelser bliver slået sammen, push pr. apparat og mærket på ikonet. En om at åbne links i en anden browser, som mest af alt handler om, hvad en webside ikke kan: hverken se hvilke browsere der findes eller vælge mellem dem. Og en om tællingen af installationer, som ikke stod nogen steder overhovedet — en melding, der er slået til som udgangspunkt, har en pligt til at være skrevet ned, også når ingen har spurgt. Rettet: AI-siden beskrev to funktioner, hvor der nu er syv, og listen over hvad der sendes hvorhen var derfor forkert som en påstand om noget privat. Noter-siden manglede deling med en e-mailadresse og hvad `@Sofie` gør. Mail-siden manglede den anden vej ind i indbakken. Konfigurationen sagde stadig, at versionstjekket kun kunne se efter. Og tagbeskeden når nu frem. Den ER annonceringen — skrevet på produktets eget sprog til dem, der bruger det — og udgivelsen på GitHub blev fyldt med en autogenereret liste over commit-overskrifter i stedet, som er skrevet til dem, der læser koden. Nu står teksten øverst og listen under den. Linjen med linket til udgivelsen selv tages af: en side, der linker til sig selv, hjælper ingen. Dokumentationen bygges nu også på en PR. `mkdocs build --strict` fejler på et link til en side, der ikke findes, og at bygge først efter merge er at få det at vide på main. Co-Authored-By: Claude Opus 5 --- .github/workflows/docs.yml | 10 +++++ .github/workflows/release.yml | 26 ++++++++++++ README.md | 27 +++++++++--- docs/ai.md | 77 +++++++++++++++++++++++++++++++---- docs/beacon.md | 75 ++++++++++++++++++++++++++++++++++ docs/configuration.md | 8 +++- docs/index.md | 15 ++++++- docs/links.md | 52 +++++++++++++++++++++++ docs/mail.md | 33 +++++++++++++++ docs/notes.md | 31 ++++++++++++++ docs/notifications.md | 51 +++++++++++++++++++++++ mkdocs.yml | 4 ++ 12 files changed, 391 insertions(+), 18 deletions(-) create mode 100644 docs/beacon.md create mode 100644 docs/links.md create mode 100644 docs/notifications.md diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f8f4da5..02e12cc 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -3,6 +3,11 @@ name: Documentation # The docs are built on every push so a broken build is caught immediately, but # only published once GitHub Pages has been enabled for the repository. Until the # owner decides to publish, this job builds and stops. +# +# And on pull requests, where the build is the only check there is: `mkdocs build +# --strict` fails on a link to a page that does not exist, and building only after +# the merge means finding that out on `main`. Nothing is published from a pull +# request — the `deploy` job below runs on a manual dispatch and nothing else. on: push: branches: [main] @@ -10,6 +15,11 @@ on: - 'docs/**' - 'mkdocs.yml' - '.github/workflows/docs.yml' + pull_request: + paths: + - 'docs/**' + - 'mkdocs.yml' + - '.github/workflows/docs.yml' workflow_dispatch: permissions: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ce3765e..950776f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + # Taggen selv, ikke kun den commit den peger på: udgivelsesteksten + # nedenfor står i tagbeskeden, og en overfladisk checkout henter + # commit'en uden den. + fetch-depth: 0 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 @@ -60,7 +65,28 @@ jobs: # # Created after the image, so a release never announces a version that # failed to build. + # Tagbeskeden ER annonceringen. Den skrives, når der tagges, på produktets + # eget sprog og til dem, der bruger det — og indtil nu nåede den aldrig + # frem: udgivelsen på GitHub blev fyldt med en autogenereret liste over + # commit-overskrifter, som er skrevet til dem, der læser koden. + # + # Den sidste linje er et link til udgivelsen selv, og den tages af: en + # side, der linker til sig selv, hjælper ingen. + - id: notes + run: | + { + echo 'body<> "$GITHUB_OUTPUT" + - uses: softprops/action-gh-release@v2 with: + body: ${{ steps.notes.outputs.body }} + # Stadig sat: den autogenererede liste lægges *under* teksten ovenfor, + # så både den, der vil vide hvad der er nyt, og den, der vil se hvilke + # ændringer det var, kan læse det samme sted. generate_release_notes: true make_latest: true diff --git a/README.md b/README.md index 02099d3..3bb4cdc 100644 --- a/README.md +++ b/README.md @@ -37,17 +37,32 @@ and an MCP endpoint so Claude can work your task list directly. - **Projects, sections, sub-tasks, labels, saved filters.** List, board and calendar views, and foldable groups over the projects in the sidebar. - **Notes, beside the work.** Rich text that is Markdown underneath — headings, - lists, quotes, fenced code. A note is shared by filing it in a project; - `#project` and `[[another note]]` are links, and a task shows the notes that - mention it. A zip of Markdown goes in — Obsidian, Bear, or Apple Notes through + lists, quotes, fenced code. A note is shared by filing it in a project, by + handing it to one person — including somebody who has no account yet, who gets an + invitation that becomes the share when they sign up — or by writing `@Sofie` in + the text, which shares it with her and tells her. `#project` and + `[[another note]]` are links, and a task shows the notes that mention it. A zip of Markdown goes in — Obsidian, Bear, or Apple Notes through the exporter in [`tools/`](tools/) — and comes back out the same shape. - **Drag where it means something.** Reorder a list or a board, file a project under a group, drop a task on another project, or drop it on another day. - **Sharing.** Owner, editor and viewer roles. There is no open registration: - everybody arrives through an invite link, either to a project or — from - **Settings → Brugere** — to the instance itself. Nobody's password is ever chosen - by somebody else. + everybody arrives through an invite link — to a project, to a single note, or + from **Settings → Brugere** to the instance itself. Nobody's password is ever + chosen by somebody else. - **Live sync.** A change made by somebody else appears without a refresh. +- **Notifications that are worth reading.** A bell for what other people did to what + you share — folded, so one person typing for five minutes is one line — pushed to + your devices and counted on the app's icon in the Dock. +- **AI that suggests and never writes.** Optional, off by default, your own key and + your own model if you want. It tidies the inbox, pulls the tasks out of a meeting + note, writes a morning plan, turns *"SV: SV: Vedr. levering uge 12"* into the + action the mail asks for, and answers questions about your own notes with links to + what the answer rests on. Every one of them arrives as a line you accept, edit or + refuse — a model that edits your tasks directly has to be right every time to be + worth having. +- **Anything can put a task in the inbox.** A mail to your own forwarding address, + or a POST of one line of text from a shortcut on your phone, a script or any + service that can call a URL. - **Recurring tasks** as RRULE (RFC 5545), reminders, comments and attachments. - **Standards, not homegrown formats.** iCalendar and CalDAV, ICS feeds, OAuth2 with PKCE, OpenAPI 3.1, MCP, Web Push, Argon2id. diff --git a/docs/ai.md b/docs/ai.md index f295072..4b6584b 100644 --- a/docs/ai.md +++ b/docs/ai.md @@ -43,23 +43,82 @@ The key is stored on your account and is **never sent back** to the settings pag a page that repopulates a password field is one that will eventually leak it into a screenshot. +## The shape of all of it + +**The model proposes; you decide.** Nothing is written until you say yes, and the +proposal is a line in the same syntax you type yourself — `Ring til Anders i morgen +p1 #Firma` — sitting in a field you can edit before you accept it. + +That is not caution for its own sake. A model that edits your tasks directly has to +be right *every* time to be worth having. One that suggests only has to be right +often enough to save you some typing, and when it is wrong you have already seen it. + +It is also why the suggestion is a line rather than a set of fields: there is one +reading of "i morgen" in this program, and a suggestion goes through the same +[quick add](quick-add.md) parser as anything you type — including after you have +edited it by hand. + +The model is given the names of the projects that exist and may name no others. A +`#Projekt` it invented would create one by accident the first time you accepted it, +and a guess must not be able to move a task somewhere you will not look for it. + ## What it can do -**Split a task into sub-tasks.** On any task: *Split with AI*. It suggests two to -seven concrete actions and creates them as sub-tasks. Written in your language. +**Tidy up the inbox.** In the Inbox, the menu → *Ryd op i indbakken*. For each loose +line it suggests how you would have written it if you had finished it — project, +date, priority — with one sentence on what it read out of the task. Accept, edit, or +skip, one line at a time. + +**Find the tasks in a note.** Open a note → *Find opgaver i noten*. Only what +somebody committed to or was asked to do; a heading is not a task and a topic that +was discussed is not a task. Each suggestion quotes the words it came from, so a +wrong one can be seen through rather than merely being wrong. The note itself is +never edited — the task carries `[[the note's title]]` in its description instead. + +**Today's plan, in the morning.** **Settings → Beskeder → Dagens plan**, off by +default; pick the hour yourself. One notification about what is due today and what +has been missed, and what to start with. The counts are done in the database and +handed to the model as facts it may not recompute — a plan that says four tasks +where there are three is worse than no plan. With no model configured the counts +stand on their own, which is what you needed to know at seven in the morning anyway. +Nothing due and nothing overdue sends nothing. + +**Mail that becomes a task you can act on.** Where a +[mailbox](mailboxes.md) used to produce *"Anders Jensen: SV: SV: Vedr. levering uge +12"*, the model writes the action the mail asks for — and leaves the mail alone if +it asks for nothing. The subject stays in the description, so a mail it misread can +still be read. If the model is unavailable the task is named sender and subject as +before: a model that is down must not be able to stop the post. + +**Ask your own notes and tasks.** ⌘K, type the question, ⌘⏎. *"Hvad lovede jeg +Anders i august?"* — search finds what contains the words, this answers the +question, and the answer links to the notes and tasks it rests on. If nothing +matches, it says so; a guess would be useless here, because you are asking precisely +because you cannot remember. + +**Split a task into sub-tasks.** On any task: *Split with AI*. Two to seven concrete +actions, created as sub-tasks. **A weekly summary.** A short note over what is outstanding: what looks urgent and -what appears to be slipping. At most six lines — it is a prompt to think, not a -report. +what appears to be slipping. At most six lines — a prompt to think, not a report. ## What is sent -Only what the feature needs: for a split, the task's title and notes; for a -summary, the titles, dates and priorities of your open tasks. Never comments, -attachments, or anything from a project the feature was not invoked on. +Only what the feature needs, and only when you invoke it: -Nothing is sent to anybody until you configure a provider. With a local model, -nothing leaves your machine at all. +| Feature | What leaves the instance | +| --- | --- | +| Tidy the inbox | The titles of the loose tasks in your inbox, the first words of their descriptions, and your project names | +| Tasks in a note | That one note's text, and your project names | +| Today's plan | The titles and dates of what is due or overdue today | +| Mail to task | Sender, subject and the snippet of the mails in that sweep | +| Ask | Your question, and the notes and tasks the search matched | +| Split a task | That task's title and notes | +| Weekly summary | The titles, dates and priorities of your open tasks | + +Never comments, never attachments, never a project the feature was not invoked on, +and never anything belonging to somebody else. Nothing is sent to anybody until you +configure a provider, and with a local model nothing leaves your machine at all. ## Turning it off diff --git a/docs/beacon.md b/docs/beacon.md new file mode 100644 index 0000000..e2fa5ed --- /dev/null +++ b/docs/beacon.md @@ -0,0 +1,75 @@ +# The install count + +verdande reports that it exists. Two values, once a day: + +```json +{ + "instance_id": "01a0806d-3601-7000-ad76-057f1f684881", + "version": "v0.44.1" +} +``` + +That is the whole message. No IP is stored by the collector, no hostname, no +domain, no account, no counts of anything you have made, nothing about what you +use it for. The payload is small enough to be printed in full on the settings page, +and it is printed there — a promise about telemetry is worth exactly as much as +your ability to check it. + +## On by default, off in one click + +The alternative that was considered first was compulsory, and the argument against +it is practical rather than principled: it cannot be enforced. The domain can be +blocked, the binary can be patched, and a fork removes it in its first commit. So +mandatory buys nothing over on-by-default while costing the trust the rest of this +program works for. + +Which is why the first administrator to sign in is **told, unprompted**, with the +real id, the real version and the real address in front of them, and two buttons. +On by default is only defensible if it is said out loud without being asked; a +settings page where it is stated fully is not the same thing, because that page is +opened by people who already suspect there is something to read. + +**Settings → Data** afterwards: one switch. An explicit "off" survives upgrades. + +!!! note "This disagrees with the update check, on purpose" + + `VERDANDE_UPDATE_CHECK` is off unless you ask for it, and its reasoning is that + a self-hosted app reaching out unbidden has broken the deal its operator made by + self-hosting. That is the same situation as this one, and the two defaults do + not agree. The case for treating them differently: the update check reveals an + instance to a third party (GitHub) and returns something you can act on, while + this returns nothing and goes to the project. Whether that is a distinction or + an excuse is a judgement call, and it was made deliberately. + +## What the collector cannot see, and what it can + +A request has a source address whether or not anybody writes it down. The collector +stores none and logs none, and the code is arranged so the address never reaches a +function that could — the table has nowhere to put one. But the request passes +through a reverse proxy and a tunnel on the way, and those keep their own logs. +"We send no IP" would be a half-truth; what is true is that nothing here records +one. + +The ping is not authenticated either, so anybody can post a made-up id and inflate +the count. That is an accepted trade: a token would have to ship inside every +install anyway. + +## Running your own count + +Somebody running a fleet points **Settings → Data → Modtagerens adresse** at their +own instance, and ticks **Denne instans er tælleren** there. Until that is ticked, +`POST /api/v1/beacon` answers **404** rather than accepting quietly — an instance +nobody made a collector must not become a bucket anyone can post into. + +The collector's page then shows totals, active in 7 and 30 days, and a breakdown by +version. Publishing that number at `/api/v1/beacon/count` is a second, separate +opt-in, and it has a floor under it (25 by default): a number that *falls* cannot be +published as a fact — a bad release, a DNS change that quietly stops the pings, or +lost rows all look like "fewer people use this now" — and a small number mostly says +how little it takes to move it. + +## When it cannot reach + +The reason is written down and shown on the settings page. A beacon that cannot +reach its collector otherwise fails in complete silence: it tries again tomorrow, +the page says "last sent" with a date that quietly gets older, and nobody notices. diff --git a/docs/configuration.md b/docs/configuration.md index 814870b..9d6cfbb 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -127,11 +127,17 @@ next time each value is written, so turning this on costs nobody a reconnection. | Variable | Default | What it does | |---|---|---| -| `VERDANDE_UPDATE_CHECK` | `false` | Ask GitHub whether a newer release exists. | +| `VERDANDE_UPDATE_CHECK` | `false` | Ask GitHub whether a newer release exists, and tell the administrators when one does. | | `VERDANDE_PANEL_URL` | — | The Yggdrasil panel this instance runs under. | | `VERDANDE_PANEL_TOKEN` | — | An API token from that panel, belonging to somebody with control of this server. | | `VERDANDE_PANEL_SERVER_ID` | — | Which server to restart — the id in the panel's URL. | +With the check on, administrators are told **once per version** — in the bell, in +push and on the app's badge — rather than having to open the settings page and +look. The notification leads to the page where the restart button is. With the +check off nothing is fetched and nothing is said; see +[Notifications](notifications.md). + All three together let **Settings → Notifications** restart this instance, which is also how it picks up a new version: a container cannot replace its own image, so the panel recreates it and pulls `:latest` on the way. The request asks diff --git a/docs/index.md b/docs/index.md index e203e23..5b24b10 100644 --- a/docs/index.md +++ b/docs/index.md @@ -27,8 +27,9 @@ forward mail to, and an MCP endpoint so Claude can work your task list directly. The calendar draws each task to its length — drag the foot to resize, the body to move it. - **Notes, beside the work.** Rich text to write in, Markdown on disk. Share a - single note with a person, or file it in a project; `#project` and - `[[another note]]` are links, and a task shows what has been written about it. + single note with a person — by name, by email address, or by writing `@Sofie` in + the text — or file it in a project; `#project` and `[[another note]]` are links, + and a task shows what has been written about it. - **Delegate, and snooze.** Hand a task to somebody and it is marked with an arrow to them, gathered on a *Waiting on others* page. Snooze one to park it, greyed, at the foot of the list until a moment you pick — without touching when @@ -38,8 +39,18 @@ forward mail to, and an MCP endpoint so Claude can work your task list directly. - **Sharing** with owner, editor and viewer roles. Anyone outside your instance joins through an invite link. - **Live sync** — a change made by somebody else appears without a refresh. +- **[Told when it matters](notifications.md).** A bell for what other people did to + what you share, pushed to your devices and counted on the app's icon in the Dock. +- **[AI that suggests and never writes](ai.md).** Optional, your own key. It tidies + the inbox, pulls the tasks out of a meeting note, writes a morning plan, turns + *"SV: SV: Vedr. levering uge 12"* into what the mail actually asks for, and + answers questions about your own notes with links to what the answer rests on — + every one of them as a line you accept, edit or refuse. - **Repeating tasks** as RRULE, comments and attachments, and reminders that go out as Web Push to an iPhone or a Mac. +- **[Anything can put a task in your inbox](mail.md)** — a mail to your own + address, or a POST from a shortcut on your phone, a script, or any service that + can call a URL. - **[Your mail as an inbox](mailboxes.md).** Star a mail in Gmail, or flag one over IMAP in iCloud or Fastmail, and it becomes a task — and the star or flag comes off once it has, so a starred inbox empties as the tasks are made instead diff --git a/docs/links.md b/docs/links.md new file mode 100644 index 0000000..64c2c11 --- /dev/null +++ b/docs/links.md @@ -0,0 +1,52 @@ +# Opening links + +A link in a task or a note opens in whichever browser your system opens links in. +**Settings → Links** can try to change that — and the honest description of what it +does is *try*. + +## What a web page cannot do + +Two things, and they set the limits of this feature: + +- **It cannot see which browsers are installed.** The list is deliberately closed: + it would be a fingerprint, distinctive enough to follow somebody from site to + site. +- **It cannot ask the system to use a particular one.** A link goes to the default + browser, and there is no API that says otherwise. + +The only handle that exists is the URL schemes browsers register for themselves — +`firefox://open-url?url=…`, `googlechrome://…`, `microsoft-edge:…` — and there is no +way to ask whether one exists. You can only try it. + +## So the setting is an attempt + +Pick Safari, Chrome, Firefox or Edge, or write your own scheme. When you click a +link, that scheme is tried first; if nothing has happened within about half a +second, the link opens the ordinary way. + +The fallback is the whole reason this is safe to turn on. Without it, a setting +that works on your phone would leave links doing nothing on your desktop. + +In practice: **it works on a phone**, where browsers register these schemes, and +**rarely on a Mac or a PC**, where Chrome and Firefox do not. Edge on Windows is +the usual exception. There is a **Prøv det** button for exactly this reason — you +cannot ask whether a scheme exists, so it is better to find out on example.dk than +on a link you meant to follow. + +## Writing your own + +Three placeholders, because browsers disagree about the shape they want the address +in: + +| | | +| --- | --- | +| `{url}` | The address as it is — `x-safari-https://example.dk` | +| `{encoded}` | The address as a parameter value — `firefox://open-url?url=https%3A%2F%2F…` | +| `{stripped}` | The address without the `https://` — `googlechrome://example.dk` | + +## It belongs to the machine, not the account + +The choice is kept in the browser you are sitting at, not on your account — the +same rule the sidebar's width follows. `firefox://` works on the phone and does +nothing on the Mac, so a choice that followed the account would be wrong in one of +the two places every time. diff --git a/docs/mail.md b/docs/mail.md index 2249e05..46724c0 100644 --- a/docs/mail.md +++ b/docs/mail.md @@ -67,3 +67,36 @@ immediately. !!! tip "Keep the address to yourself" Treat it like a secret. If you publish it somewhere, rotate it. + +## Pushing from another program + +The address above needs a mail server. When what you want is a shortcut on your +phone, a script, or any service that can call a URL, there is a second way in: +**Settings → Integrationer → Skub fra andre programmer** gives you an address to +POST a line of text to. + +```bash +curl -X POST https://todo.example.dk/inbound/hook/ \ + -H "Content-Type: text/plain" \ + -d "Ring til Anders i morgen p1 #Firma" +``` + +The body is read three ways, because those are the three you meet: plain text from +a shortcut that just sends what was selected, JSON with a `text` field from a +service, or a form field from a webhook form. The first line becomes the task and +the rest sits underneath it, and the line goes through the [quick add](quick-add.md) +parser — so "i morgen", "p1" and "#Firma" mean here what they mean in the box at the +top of the app. + +**The address is a key.** It has its own token rather than sharing the mail one: +the two are the same kind of secret but they leak separately, and a URL that has +been sitting in a shortcut on a lost phone has to be replaceable without changing +the address other people have in their address books. A token here can do one +thing — put a task in your inbox — which is why it is not an +[API token](api.md), which can do everything. Change it under the same setting; the +old one stops working at once. + +**POST only.** A GET would be easier to call, and that is exactly the problem: a +browser prefetch, a link check in a chat, and a revisited history entry would all +create tasks nobody asked for. + diff --git a/docs/notes.md b/docs/notes.md index bd87083..a58a60b 100644 --- a/docs/notes.md +++ b/docs/notes.md @@ -79,6 +79,37 @@ There is no folder tree. Projects, groups and labels already exist, are already shared and already have roles — a second hierarchy to file things in is one too many, and a note in a project inherits every one of those answers. +### Sharing one note with one person + +Filing a note in a project is the right tool when the note belongs with a body of +work, and the wrong one when it is just *let Sofie see this*. **Del med personer** +at the foot of the note does the second: this note, this person, as reader or +editor. Only the note's owner can share it. + +Type an **email address** rather than picking a name, and one of two things +happens. An address that already has an account is shared with straight away. One +that does not gets an invitation — a link that creates the account and the share +together, so the note is theirs the moment they sign up. Invitations wait in the +panel under the note, where they can be withdrawn; that is the only way back, since +only the token's hash is stored and a link sent to the wrong address cannot be +looked up. + +### Naming somebody in a note + +Writing `@Sofie` in a note shares it with her and tells her. Naming somebody in a +note *is* wanting them to read it, and doing it as two acts — write the name, then +open the panel and find the person — meant the second was forgotten, and the note +sat there addressing somebody who could not see it. The editor suggests names after +`@`, the same way `#` suggests projects. + +- **Only the owner's mentions share.** An editor may write the name; giving + somebody else's note away is the owner's alone. +- **As a reader**, which is the least that satisfies "look at this". A role you + have raised in the panel is never lowered by a mention. +- **Only a name that has just appeared** counts. A note is saved on every typing + pause, so without that every save would be another share and another notification + about the same name. + ## Finding The list groups itself: favourites first, then **Today**, **Yesterday**, **This diff --git a/docs/notifications.md b/docs/notifications.md new file mode 100644 index 0000000..450e0e1 --- /dev/null +++ b/docs/notifications.md @@ -0,0 +1,51 @@ +# Notifications + +What other people did that you need to know about, and what the day has in it. +Verdande tells you in three places at once, and they are the same thing said three +ways: the **bell** in the header, a **push** to your devices, and the **badge** on +the app's icon. + +## What causes one + +| | | +| --- | --- | +| Somebody edited a note shared with you | Repeat edits fold into one line while it is unread | +| Somebody shared a note with you | Once, when it happens | +| Somebody named you in a note with `@` | Instead of "edited a note" — one act, one message | +| Somebody gave you a task | Only on the change, and only to the person who got it | +| Somebody commented | | +| [Today's plan](ai.md), if you turned it on | In the morning, at the hour you chose | +| A new version is out | Administrators only, once per version | + +Not your own actions: the server does not tell anybody what they just did +themselves. And not everything that happens — a bell that rings at every change in +a shared project is a bell people stop looking at. + +**Repeat edits are folded.** A note is saved on every typing pause, so one person +writing for five minutes would otherwise be twenty identical lines. While the +message is unread, a further edit moves it back to the top and says who touched it +last. Once you have read it, the next edit is a new thing to be told. + +## Push, and the badge + +**Settings → Beskeder** turns on Web Push for the device you are sitting at, per +device: the phone in your pocket and the laptop you have open are different +answers, and a permission granted on one is not the other's. + +The **badge** — the number on the icon in the Dock on a Mac, on the taskbar on a +PC — needs the app to be installed (added to the Dock, or installed as an app in +Chrome or Edge). Half the point of being told is being told while you are doing +something else, and the icon is the only place the app can say anything then. It +counts unread messages and goes down by itself as you read them. Firefox does not +support it; nothing else changes there. + +The sentence you see is written in your browser from *what happened* and *who did +it*, so an English interface gets an English bell. The server's own wording is +Danish and is used only for push, which has no dictionary to look a phrase up in. + +## Telling you about a new version + +If the [update check](configuration.md) is on, administrators get one notification +per version — never one a day about the same one — and it leads to +**Settings → Beskeder**, where the button that restarts the instance to update it +sits. With the check off, nothing is fetched and nothing is said. diff --git a/mkdocs.yml b/mkdocs.yml index 32ab966..0b7c466 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -51,6 +51,8 @@ nav: - Signing in: signing-in.md - Quick add: quick-add.md - Notes: notes.md + - Notifications: notifications.md + - Opening links: links.md - Filters: filters.md - Importing from Todoist: import.md - Connecting things: @@ -60,6 +62,8 @@ nav: - Mailboxes: mailboxes.md - Claude (MCP): mcp.md - AI features: ai.md + - Privacy: + - The install count: beacon.md # openapi.yaml is deliberately not in the nav: MkDocs copies it to the site as a # file, and api.md links to it. A nav entry expects a page to render. - Reference: