Merge pull request #72 from kristianwind/dokumenter-det-der-blev-bygget
docs: skriv det ned, der er blevet bygget
This commit is contained in:
@@ -3,6 +3,11 @@ name: Documentation
|
|||||||
# The docs are built on every push so a broken build is caught immediately, but
|
# 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
|
# only published once GitHub Pages has been enabled for the repository. Until the
|
||||||
# owner decides to publish, this job builds and stops.
|
# 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:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
@@ -10,6 +15,11 @@ on:
|
|||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
- 'mkdocs.yml'
|
- 'mkdocs.yml'
|
||||||
- '.github/workflows/docs.yml'
|
- '.github/workflows/docs.yml'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'docs/**'
|
||||||
|
- 'mkdocs.yml'
|
||||||
|
- '.github/workflows/docs.yml'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- 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-qemu-action@v3
|
||||||
- uses: docker/setup-buildx-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
|
# Created after the image, so a release never announces a version that
|
||||||
# failed to build.
|
# 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<<VERDANDE_EOF'
|
||||||
|
git tag -l --format='%(contents)' "${GITHUB_REF_NAME}" \
|
||||||
|
| sed '\#^https://github.com/.*/releases/tag/#d' \
|
||||||
|
| sed -e :a -e '/^\n*$/{$d;N;ba' -e '}'
|
||||||
|
echo VERDANDE_EOF
|
||||||
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- uses: softprops/action-gh-release@v2
|
- uses: softprops/action-gh-release@v2
|
||||||
with:
|
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
|
generate_release_notes: true
|
||||||
make_latest: true
|
make_latest: true
|
||||||
|
|||||||
@@ -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
|
- **Projects, sections, sub-tasks, labels, saved filters.** List, board and calendar
|
||||||
views, and foldable groups over the projects in the sidebar.
|
views, and foldable groups over the projects in the sidebar.
|
||||||
- **Notes, beside the work.** Rich text that is Markdown underneath — headings,
|
- **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;
|
lists, quotes, fenced code. A note is shared by filing it in a project, by
|
||||||
`#project` and `[[another note]]` are links, and a task shows the notes that
|
handing it to one person — including somebody who has no account yet, who gets an
|
||||||
mention it. A zip of Markdown goes in — Obsidian, Bear, or Apple Notes through
|
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.
|
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
|
- **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.
|
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:
|
- **Sharing.** Owner, editor and viewer roles. There is no open registration:
|
||||||
everybody arrives through an invite link, either to a project or — from
|
everybody arrives through an invite link — to a project, to a single note, or
|
||||||
**Settings → Brugere** — to the instance itself. Nobody's password is ever chosen
|
from **Settings → Brugere** to the instance itself. Nobody's password is ever
|
||||||
by somebody else.
|
chosen by somebody else.
|
||||||
- **Live sync.** A change made by somebody else appears without a refresh.
|
- **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.
|
- **Recurring tasks** as RRULE (RFC 5545), reminders, comments and attachments.
|
||||||
- **Standards, not homegrown formats.** iCalendar and CalDAV, ICS feeds, OAuth2 with
|
- **Standards, not homegrown formats.** iCalendar and CalDAV, ICS feeds, OAuth2 with
|
||||||
PKCE, OpenAPI 3.1, MCP, Web Push, Argon2id.
|
PKCE, OpenAPI 3.1, MCP, Web Push, Argon2id.
|
||||||
|
|||||||
+68
-9
@@ -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
|
a page that repopulates a password field is one that will eventually leak it into a
|
||||||
screenshot.
|
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
|
## What it can do
|
||||||
|
|
||||||
**Split a task into sub-tasks.** On any task: *Split with AI*. It suggests two to
|
**Tidy up the inbox.** In the Inbox, the menu → *Ryd op i indbakken*. For each loose
|
||||||
seven concrete actions and creates them as sub-tasks. Written in your language.
|
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
|
**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
|
what appears to be slipping. At most six lines — a prompt to think, not a report.
|
||||||
report.
|
|
||||||
|
|
||||||
## What is sent
|
## What is sent
|
||||||
|
|
||||||
Only what the feature needs: for a split, the task's title and notes; for a
|
Only what the feature needs, and only when you invoke it:
|
||||||
summary, the titles, dates and priorities of your open tasks. Never comments,
|
|
||||||
attachments, or anything from a project the feature was not invoked on.
|
|
||||||
|
|
||||||
Nothing is sent to anybody until you configure a provider. With a local model,
|
| Feature | What leaves the instance |
|
||||||
nothing leaves your machine at all.
|
| --- | --- |
|
||||||
|
| 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
|
## Turning it off
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -127,11 +127,17 @@ next time each value is written, so turning this on costs nobody a reconnection.
|
|||||||
|
|
||||||
| Variable | Default | What it does |
|
| 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_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_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. |
|
| `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,
|
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
|
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
|
image, so the panel recreates it and pulls `:latest` on the way. The request asks
|
||||||
|
|||||||
+13
-2
@@ -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
|
The calendar draws each task to its length — drag the foot to resize, the body
|
||||||
to move it.
|
to move it.
|
||||||
- **Notes, beside the work.** Rich text to write in, Markdown on disk. Share a
|
- **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
|
single note with a person — by name, by email address, or by writing `@Sofie` in
|
||||||
`[[another note]]` are links, and a task shows what has been written about it.
|
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
|
- **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,
|
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
|
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
|
- **Sharing** with owner, editor and viewer roles. Anyone outside your instance
|
||||||
joins through an invite link.
|
joins through an invite link.
|
||||||
- **Live sync** — a change made by somebody else appears without a refresh.
|
- **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
|
- **Repeating tasks** as RRULE, comments and attachments, and reminders that go
|
||||||
out as Web Push to an iPhone or a Mac.
|
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
|
- **[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
|
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
|
comes off once it has, so a starred inbox empties as the tasks are made instead
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -67,3 +67,36 @@ immediately.
|
|||||||
|
|
||||||
!!! tip "Keep the address to yourself"
|
!!! tip "Keep the address to yourself"
|
||||||
Treat it like a secret. If you publish it somewhere, rotate it.
|
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/<token> \
|
||||||
|
-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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
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.
|
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
|
## Finding
|
||||||
|
|
||||||
The list groups itself: favourites first, then **Today**, **Yesterday**, **This
|
The list groups itself: favourites first, then **Today**, **Yesterday**, **This
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -51,6 +51,8 @@ nav:
|
|||||||
- Signing in: signing-in.md
|
- Signing in: signing-in.md
|
||||||
- Quick add: quick-add.md
|
- Quick add: quick-add.md
|
||||||
- Notes: notes.md
|
- Notes: notes.md
|
||||||
|
- Notifications: notifications.md
|
||||||
|
- Opening links: links.md
|
||||||
- Filters: filters.md
|
- Filters: filters.md
|
||||||
- Importing from Todoist: import.md
|
- Importing from Todoist: import.md
|
||||||
- Connecting things:
|
- Connecting things:
|
||||||
@@ -60,6 +62,8 @@ nav:
|
|||||||
- Mailboxes: mailboxes.md
|
- Mailboxes: mailboxes.md
|
||||||
- Claude (MCP): mcp.md
|
- Claude (MCP): mcp.md
|
||||||
- AI features: ai.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
|
# 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.
|
# file, and api.md links to it. A nav entry expects a page to render.
|
||||||
- Reference:
|
- Reference:
|
||||||
|
|||||||
Reference in New Issue
Block a user