Commit Graph
181 Commits
Author SHA1 Message Date
kristianwindandClaude Opus 5 094ea5cdd8 feat(upcoming): look up to 30 days ahead, and choose how far
Kommendes liste has always been the next seven days and nothing else. Seven days
is the right walk to take in the morning, and it is still what the view opens on
— but "when does this month get busy" is a different question, and the month grid
answers it with shape rather than in order. So the list now takes a horizon: 7,
14 or 30 days, chosen with a switcher beside the view buttons.

Only the list gets one. A week grid is a week and a month grid is a month; there
is no fortnight for them to show, so the switcher is hidden in those modes.

The choice lives in localStorage next to the mode, under its own key. It is the
same kind of choice as list-or-grid — how you want to look, not a fact about the
work — and it should survive a reload without following you to a phone where
thirty day headings is a very long scroll.

Once the strip is longer than a week the day headings carry the week number.
Thirty days repeat every weekday four times, and "mandag 6. okt" alone does not
say which mandag.

Server side the endpoint already took `?days=` and capped it at 31, so the window
needed nothing — but its row limit was a constant 1000 written for a week. A
month asked for with a week's ceiling would truncate the last days silently, and
a day cut off the end of the answer looks exactly like a day with nothing due, so
the limit now follows the window.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SA1TDmgQZw3AufJqkbwezx
2026-09-07 14:46:49 +02:00
kristianwindandClaude Opus 4.8 9d390d021f docs(site): show all six themes, and put the demo line in English
Adds a gallery of the same Today view in each of the six themes — Northern lights,
Charcoal, Dusk, Daylight, Paper, Contrast — so the range is shown rather than
described, on the landing page and (as the swatch panel) the docs home. The lone
Charcoal Today capture is folded into it.

And the quick-add demo that leads the docs — "betal moms i morgen kl 10 p1 #Firma
@regnskab" — is now English, "file the VAT return tomorrow at 10am p1 #Accounts
@tax", on every page it appears as the line to type: the home, the quick-add page,
the API and MCP examples, and the OpenAPI blurb. The Danish date words on the
quick-add page stay, because documenting the Danish parser is their job.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-04 09:46:50 +02:00
kristianwindandClaude Opus 4.8 1612f53966 docs(site): the Charcoal theme, offline, and the star that comes off
Refreshes the whole screenshot set to the app as it ships now — the three-line
note rows, the lightened calendar — and adds two Charcoal captures, the theme
modelled on Apple Notes, since it is the one worth showing. The landing page and
the docs home both gain it, a "holds through a short network drop" line, and the
correction that a starred or flagged mail now loses its mark once it is a task
rather than keeping it. Six themes now, not five.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-04 09:20:11 +02:00
Kristian Wind 4d5f1a56b1 Merge pull request #56 from kristianwind/gmail-unstar
CI / OpenAPI is valid (push) Successful in 2m55s
CI / End-to-end (push) Successful in 8m3s
CI / Docker image builds (push) Successful in 1m46s
Release / Build and publish image (push) Failing after 7s
CI / Go (push) Failing after 13m23s
feat(gmail): take the star off a mail once it has become a task
2026-09-04 09:06:31 +02:00
kristianwindandClaude Opus 4.8 84ca506961 feat(gmail): take the star off a mail once it has become a task
A starred Gmail message became a task and kept its star, so a starred inbox only
grew — the IMAP side has always unflagged what it imports, and Gmail could not,
because the scope was gmail.readonly. This gives Gmail the same one write: after
the task is made, the STARRED label comes off the message, in one batchModify for
the whole sweep. Nothing else is touched — not the body, not other labels, and a
label trigger's label stays where it is, since that label is the person's own.

The scope goes from gmail.readonly to gmail.modify, which is the narrowest scope
Google offers that can remove a label. An existing connection keeps working on its
old read-only grant — the sync still reads, the unstar is a best-effort 403 that is
logged, not fatal — and starts clearing stars once the account is reconnected and
the new grant is in place. It self-heals: a mail that could not be unstarred stays
starred, stays in the next query, and is tried again.

The consent screen now asks for more than it used to; the settings copy, the docs
and the scope table all say modify rather than read-only in the same change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-04 08:57:07 +02:00
kristianwindandClaude Opus 4.8 6db61b0cc7 test(e2e): block the service worker except where the test is about it
The caching worker controls every page once it registers, and Playwright's
page.route() does not see the fetches a worker makes — so the failed-load test,
which forces a 500 to check the error path, got the real 200 through the worker
and read as a broken feature. Block the worker by default and turn it back on only
for the offline test, which is the one that needs a real one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-04 08:12:15 +02:00
kristianwindandClaude Opus 4.8 57689ddfbb feat(offline): hold through a short network drop with a caching worker
The service worker did Web Push and nothing else, with a note that a cache
serving yesterday's data was worse than an honest "cannot connect". This adds a
cache that does not do that: network-first for everything that can go stale, so a
reader online always gets the live answer and the cache is reached only when the
network cannot answer. The task and note lists stay live; they just no longer go
blank the moment the signal does — a reload through a tunnel, a lift or a
base-station handover comes back to the app instead of the browser's offline page.

Writes are untouched and still go straight to the network; api.js already retries
them across the same short drop. The shell cache is versioned so a deploy lands in
a fresh cache and the old one is dropped on activate; the data cache is cleared on
sign-out so the next person on the browser does not inherit it.

The old hand-registered /sw.js becomes SvelteKit's src/service-worker.js, which it
registers itself, and the push handlers move across unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 22:46:35 +02:00
kristianwindandClaude Opus 4.8 55b8e438ad fix(rows): open on the first tap on iPhone, and round the hover highlight
On a touch screen a :hover on the tap target makes iOS spend the first tap
applying the hover and only open on the second — the double-tap Kristian hit
on tasks (and notes). The row hover is gated behind @media (hover: hover)
now, so a touch has no hover to apply and the first tap opens. The note
row's action buttons, which the hover used to reveal, are shown outright on
touch instead. And the task-row hover highlight is a rounded box (border
radius on the row), the same corner as a note in the list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 22:15:23 +02:00
kristianwindandClaude Opus 4.8 8a5dfef41a fix(theme): lighter day separators in Charcoal
In Charcoal the note-list day separators used --ground, the darkest colour
in the theme, so they read as dark bars on the lighter list. They take the
menu's own resting grey (#2d3133) now — the colour "Projekter" sits on —
via a --note-group-bg fallback token, so every other theme keeps --ground.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 20:46:51 +02:00
kristianwindandClaude Opus 4.8 c2d4ce0179 fix(notes): open a note on the first tap, not the second
On a phone the list/editor switch keyed on the loaded note, which arrives a
moment after the tap — so the first tap looked like nothing happened and
people tapped again. It keys on the selected id now, set the instant a row
is tapped, so the pane switches at once; the back button is there
immediately and the body fills in when it arrives, with a quiet pane rather
than the "pick a note" prompt in the half-second between.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 20:27:28 +02:00
kristianwindandClaude Opus 4.8 8a2c46f5ea fix(notes): show the note on a phone — one pane at a time
On a phone the note list and the editor stacked in one column, so a tapped
note opened below a full-height list, out of sight — "kan vælge en note men
kan ikke se indholdet". Now it is master-detail: the list fills the screen
until a note is opened, the editor takes over once one is, and an iOS-style
"‹ Noter" at the top of the editor goes back. The cards drop their border
and corners on one column, where there is nothing beside them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 19:07:02 +02:00
kristianwindandClaude Opus 4.8 905ceebc06 fix(theme): match Charcoal to Apple Notes' sampled colours
Kristian sent the exact values; the first Charcoal was a neutral grey guess
and missed them. Now: three panel greys, lightest sidebar (#2d3133) to
darkest editor (#1e1e1e) with the list (#242b2e) between — the reverse of
the usual sunken-is-darkest order, so the editor takes its own --editor-bg
rather than sharing the list's surface. The accent is amber: the selected
menu item is solid #ffce35 with dark text, a selected note is gold #a78c26
with dark text, and the section headings drop to a quiet grey (#8a8e90).

Each per-element colour is a fallback token only Charcoal sets, so every
other theme reads through to the shared token unchanged. Verified the
computed values land on Kristian's samples exactly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 17:42:55 +02:00
Kristian Wind 87c557941a Merge pull request #49 from kristianwind/theme-charcoal-polish
CI / OpenAPI is valid (push) Successful in 9s
CI / End-to-end (push) Successful in 8m47s
CI / Docker image builds (push) Successful in 1m47s
Release / Build and publish image (push) Failing after 5s
CI / Go (push) Failing after 14m25s
Charcoal theme, elegant selection, rounded note list, confirm sign-out
2026-09-03 16:37:44 +02:00
kristianwindandClaude Opus 4.8 1dc9751a33 test(e2e): sign out with the two-step confirm
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 16:29:18 +02:00
kristianwindandClaude Opus 4.8 b81f8f67cc feat(ui): Charcoal theme, elegant selection, rounded note list, confirm sign-out
- A new Charcoal theme — neutral dark greys on the menu, note list and
  editor instead of the default's faint green cast, with matching inks;
  the green accent stays. It joins the theme picker on its own (the picker
  is data-driven from THEMES).
- The text selection is a translucent wash of the accent, not the old
  near-invisible tint and not a solid slab — visible on white and on the
  dark surfaces, keeping the text's own colour.
- The note list is a rounded card now, the same corner and border as the
  editor beside it, with a few pixels more air around the menu and the list.
- Signing out asks first: a second click within a few seconds confirms, so
  a button the pointer passes over does not drop you at the login screen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 16:27:55 +02:00
kristianwindandClaude Opus 4.8 f62ffc8d92 feat(2fa): show a scannable QR code when enabling two-factor auth
The setup screen showed the secret and the otpauth URI as text and asked
you to scan — with nothing to scan. It renders the URI as a QR now, an SVG
(one dark path on a white card, so it reads in either theme) generated from
the barcode encoder already in the module graph via pquerna/otp. The secret
stays below it for a device that cannot scan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 16:27:11 +02:00
kristianwindandClaude Opus 4.8 b2fcc0b104 feat(offline): ride out short connection drops instead of losing the save
A save made while the connection blips for a few seconds is retried rather
than reverted: the request layer keeps trying a network failure for a
fifteen-second window, waking the instant the OS reports a link again, and
only falls back to the existing error path if the drop outlasts it. A quiet
"reconnecting" pill appears if the wait passes about a second, and leaves
the moment it heals — so a two-second blip is felt as nothing at all.

This is deliberately not offline mode: the app is a live view of a shared
list, so there is no cache to work in with no server and no write log to
reconcile. It is only the blip, ridden out. A server error (4xx/5xx) is the
server answering and is not retried; an aborted request (a superseded
search) is left to propagate.

Kristian: an offline function for when the net drops for short periods,
seconds. Shared items stay server-authoritative — nothing is edited against
a stale copy for longer than the blip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 15:26:46 +02:00
Kristian Wind 8dc601d09a Merge pull request #46 from kristianwind/app-icon-refresh
CI / OpenAPI is valid (push) Successful in 9s
CI / End-to-end (push) Successful in 9m19s
CI / Docker image builds (push) Successful in 1m56s
Release / Build and publish image (push) Failing after 6s
CI / Go (push) Failing after 14m31s
Centre and strengthen the app icon
2026-09-03 15:01:15 +02:00
kristianwindandClaude Opus 4.8 0363d5f422 feat(icon): centre and strengthen the app icon
The Wunjo rune sat left of centre and thin. It is heavier now (a 6.5
stroke), bigger, and nudged right of the geometric centre so the heavy
stave and the light flag balance about the middle of the tile — so it
reads clearly as a home-screen icon.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 14:53:21 +02:00
kristianwindandClaude Opus 4.8 05b7dac713 feat(notes): three-line rows like Apple Notes, with a filed line and dividers
Each note is three lines now: the title, then the date and the excerpt,
then where the note is filed — a folder like the menu's groups and the
project's name, the owner's when it is someone else's, or simply "Noter"
when it belongs to no project. The date leads the second line, left
aligned, instead of being pushed to the right of the title. A faint
hairline separates one note from the next, and a month heading is
capitalised ("August 2026", not "august 2026").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 14:52:56 +02:00
Kristian Wind cfd382e3df Merge pull request #44 from kristianwind/settings-link-style
CI / OpenAPI is valid (push) Successful in 11s
CI / End-to-end (push) Successful in 10m3s
CI / Docker image builds (push) Successful in 2m8s
Release / Build and publish image (push) Failing after 6s
CI / Go (push) Failing after 14m58s
Style the unstyled "open completed tasks" link in settings
2026-09-03 14:35:20 +02:00
kristianwindandClaude Opus 4.8 01001d0980 fix(settings): the "open completed tasks" link was unstyled
It carried class="ghost", which nothing in the data page styles — so it
fell back to the browser's blue underline while every link beside it is
the accent-coloured .link. Now it matches them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 14:27:20 +02:00
kristianwindandClaude Opus 4.8 4ac7180cda feat(notes): drop the disclosure arrow from the note-list headers too
Same as the sidebar: the box folds on a click, the star and people icon
are the marks that mean something, and a date group keeps an empty spacer
so every label still starts at the same place.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 14:25:55 +02:00
kristianwindandClaude Opus 4.8 e1683ef09d feat(notes): match the note-list headers to the new heading design
The note list still wore the old faint-uppercase group header beside the
sidebar's bold plain-case one, so the two columns read as two apps. The
group headers are bold, plainly cased and in the reading ink now, at
menu size, and their separator boxes carry the same rounded corner as an
active menu item — the hard rule underneath is gone, the box is the
separation. The disclosure mark stays: unlike a sidebar section, a
time-group has no icon, so it is the only thing that says "this folds".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 14:22:19 +02:00
kristianwindandClaude Opus 4.8 ec147cd1ba feat(sidebar): drop the fold arrows from headers and groups
The chevron beside every section and group was saying "clickable" a
second time next to an icon and a bold name that already said it. Gone
now: a system section folds from anywhere on its heading, and a group
folds from its folder icon while the name still opens the group's page.
No arrow on the rail.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 13:34:33 +02:00
kristianwindandClaude Opus 4.8 6cf63113df feat(calendar): lighter grid, per-calendar pastels, today marked at top
The calendar was a grey grid where every event was the same sunken block
edged in a colour you had to already know. Now the day itself is the
brightest surface and each event is a soft, dusty wash of its own
calendar's colour — so a screen of them reads as colour laid on white,
and two calendars are told apart at a glance. Tasks stay outlined cards,
the thing you can pick up, against the solid washes you cannot.

Today is marked where the day is named: its number sits in a filled disc
at the top of the column, the same one the month grid gives it, over a
faint wash down the whole column.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 13:09:07 +02:00
kristianwindandClaude Opus 4.8 1c223a2c26 fix(sidebar): scrollbar flush to the divider, and match the group test
The scrollbar thumb centred a 4px bar in a 10px track behind a
transparent border, so it floated three pixels shy of the right edge.
The thumb is the track now — a thin 6px rounded bar sitting flush
against the divider line, where a scrollbar belongs.

The group heading is a heading now, not a project row: the smoke test
measured a group against the projects filed under it and read the
section icon as a dot. It measures a group against "Projekter" — the
tier it shares — and reads the icon both headings carry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 12:54:05 +02:00
kristianwindandClaude Opus 4.8 a9c90ee6f1 feat(ui): unify section and group headers with icons
System sections (Projects, Filters, Labels) and user groups (Work,
Home) now share one design: a bold label at menu size, a leading
folder/funnel/tag/people icon, and an always-visible fold chevron.
Groups drop their colour dot for the same folder icon, so the sidebar
reads as one consistent hierarchy instead of two visual languages.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 12:34:07 +02:00
kristianwindandClaude Opus 4.8 378980de0c fix(sidebar): the scrollbar sits at the edge, thin and dim
It sat a pad-width inside the right edge because the scroller lives inside the
sidebar's padding, so it read as floating in the middle. Pull the scroller out
to the border and pad the content back — the rows do not move — and make the
bar thin and dim so it is present without drawing the eye.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 12:14:39 +02:00
kristianwindandClaude Opus 4.8 52df528994 test(e2e): retry the project→group drop until it nests
A native HTML5 drag occasionally does not register under CI load, leaving
the group empty and the group-page assertion failing for a reason unrelated
to the test. Retry the drop until the project actually sits under the group.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 12:00:02 +02:00
kristianwindandClaude Opus 4.8 2f00246d6f feat(ui): colour the fixed-view icons, lift the notes surfaces, fix the create header
Following the Things pass, four bits of polish:

- The fixed views carry their own colour — amber star for Today, blue tray
  for the Inbox, red Upcoming, violet Waiting on others, teal Notes, indigo
  Calendar — and keep it whether or not they are the active row.
- The active row was all but invisible on the dark sidebar (a half-step of
  surface); it is now an accent-tinted selection that reads in both themes.
- The Projekter heading — the only place to add a project or a group — no
  longer scrolls away with a long list; it is pinned to the top of the
  scroller.
- The note list and the editor lift off the page: white in light, a nuance
  lighter than the ground in dark, so the two read as one writing surface.

Screenshots regenerated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 11:47:17 +02:00
kristianwindandClaude Opus 4.8 59413a8b64 test(e2e): a grouped project now shares the left margin, not indented
The sidebar redesign puts every project at the same left edge; flip the
assertion from "indented more than a fixed view" to "in line with it".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 10:09:32 +02:00
kristianwindandClaude Opus 4.8 cf69cac410 feat(sidebar): icons for the fixed views, bold areas, one straight edge
Toward Things' calm: the fixed views carry icons instead of dots — a star
for Today, a calendar for Upcoming, a person for Waiting on others, a tray
for the Inbox. Areas (the user's groups) are set in bold, plainly cased, so
the eye lands on them the way it lands on an area in Things; the system
sections (Projekter, Filtre, Etiketter) stay quiet. And projects sit at the
same left margin whether or not they are in an area — one straight edge down
the sidebar rather than a staircase, with the bold heading doing the
grouping the indent used to.

Screenshots regenerated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 10:02:53 +02:00
kristianwindandClaude Opus 4.8 8dfab618f7 feat(settings): menu and body-text font size, and a bolder menu by default
The sidebar read as muted 13px; the menu items now use their own size at
medium weight so they carry at a glance (Todoist-ish). Both sizes are
choices under Settings → Appearance:

- Menu font size drives a dedicated --menu-size the sidebar uses.
- Body text size re-scales the shared type tokens (the list and the notes),
  the same mechanism a look uses.

The two are independent on purpose — one is a glance down a list of names,
the other a paragraph you read. Kept in localStorage like theme and look,
and applied in app.html before first paint so a chosen size does not flash
in and resize. Four steps each (Small/Default/Large/Largest); Default
carries no attribute so the base tokens stand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 15:30:30 +02:00
kristianwindandClaude Opus 4.8 247277e139 feat(users): always capitalise a display name's first letter
Andreas signed up as "andreas" and was shown that way everywhere. A name
is now capitalised on the way in — the first letter of each word raised,
the rest left as typed — in CreateUser (signup and invite-accept) and
UpdateProfile, so display, mail and export all agree.

Existing accounts are fixed by an idempotent startup pass
(NormalizeUserNames), the same shape as SealNotes: the rule is Unicode-aware
and per word, which SQLite's UPPER cannot spell, so it lives in Go rather
than a migration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-30 09:25:50 +02:00
kristianwindandClaude Opus 4.8 4103e1b0ca fix(tasks): open the task by clicking its text, links carved out
The first cut put an open-button overlay over the row, which intercepted
every click on the title text — nine smoke tests (and real users) open a
task by clicking its text, and the overlay stole those clicks. Make the
body a role=button instead, with each link stopping its own click from
bubbling: plain text opens the task, a link opens the page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 16:33:30 +02:00
kristianwindandClaude Opus 4.8 9d4a13c2c2 feat(tasks): make a URL in a task's title or description clickable
A task is plain text — the title is an input, the description a textarea —
so a pasted URL just sat there as words. It is now caught at display time
and shown as a link in the list and on the board, without the stored text
ever being anything but plain text.

The link cannot live inside the row's open-button (an <a> in a <button> is
invalid), so the body is a container with the open action as an overlay
behind it: plain text and the meta row still open the task, links are
raised above and take their own click. Conservative about what counts —
an explicit http(s):// or a bare www. host, not every dotted word.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 16:17:56 +02:00
kristianwindandClaude Opus 4.8 272c4918b0 feat(settings): a quiet Buy me a coffee link
A donate link belongs in Settings and nowhere in the way — so it sits last,
understated, below the device list. A plain anchor with an inline SVG cup:
no third-party script, no webfont, no tracking, so it works offline and
cannot phone home. Wording says the app is MIT with nothing held back and
that nothing changes if you don't pay.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 14:49:24 +02:00
kristianwindandClaude Opus 4.8 7066e2e06e docs(site): new features, refreshed screenshots and text
The landing page and the docs now show and describe what has landed since
the last site update:

- the delegated-task marker (an arrow to whoever has it) — visible now in
  the board, list and Waiting-on-others shots
- snooze — one seeded task is parked so the board shows a card greyed at
  the foot of its column
- Web Push reminders to iPhone and Mac, dated tasks in a subscribed
  calendar, task durations on the calendar, and per-person note sharing

Screenshots regenerated against the current UI (PNG for docs/README, WebP
for the landing page, from the one capture). "Lately" brought current.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 13:00:05 +02:00
Kristian Wind 2b76a172e8 Merge pull request #30 from kristianwind/add-delegated-marker
Release / Build and publish image (push) Failing after 10s
feat(tasks): mark a delegated task with a hand-off arrow
2026-08-27 12:43:13 +02:00
kristianwindandClaude Opus 4.8 15495255ef test(e2e): the delegated chip's title is now "Uddelegeret til"
The assignee chip reads as a delegation now, so its title changed from
"Ansvarlig: {name}" to "Uddelegeret til {name}". Point the role-edit
smoke test at the new title.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 12:36:48 +02:00
kristianwindandClaude Opus 4.8 7dcd475036 feat(tasks): mark a delegated task with a hand-off arrow
A task assigned to somebody who is not you is out of your hands and into
theirs, but the row only showed their name — the same weight as any
other meta. The assignee is now a bordered chip led by an outgoing arrow
in the accent colour, so a list reads at a glance as which tasks are
yours to do and which you are waiting on. Covers the board too: its
cards render a TaskRow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 12:27:52 +02:00
kristianwindandClaude Opus 4.8 fc4877c3bd fix(mobile): stop the task drawer's priority/due row overflowing
On a phone the detail drawer's field grid stayed two columns, so each
field got half the viewport. Prioritet collapsed to "Ing" and the due
row — a date input plus fixed-width time and duration fields — ran off
the right edge. Below 560px the grid now stacks to one column per field
and the due row wraps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 12:24:29 +02:00
kristianwindandClaude Opus 5 9e3f20b050 feat(tasks): snooze — park a task at the bottom until a time
Snooze puts a task out of the way without finishing it or filing it: it sinks to
the bottom of its list and greys, and comes back on its own when its time passes.
No job wakes it — "snoozed" is just snoozed_until being still in the future, read
against the clock each time the list is ordered.

- `snoozed_until` on tasks (migration 0031); the list order sinks a still-snoozed
  task below everything, and it rejoins its place the moment the time passes.
- POST /tasks/{id}/snooze {until} — an RFC3339 time to snooze to, or empty to wake.
  Nothing else about the task moves; a snooze is about when you want to see it, not
  when it is due.
- The task drawer gains quick picks (this evening / tomorrow / next week) and a
  custom time, and a Wake now once it is snoozed. The row dims and carries a
  "snoozed until X" mark, and the list keeps snoozed tasks at the foot — the client
  sorts the same way the server does, so a drag-sorted list does not jump on reload.

Tests: snooze sinks a task below one made after it and waking returns it (Go); and
from the drawer the row greys, shows the mark, and un-greys on wake (e2e). Full
suites green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 10:59:34 +02:00
kristianwindandClaude Opus 5 a70ca93102 feat(notes): an Archive button in the open note's footer
Archiving a single note meant finding its row in the list and its hover-only
button. With a note open to read or write, the one place you would reach for it —
its own footer, beside Gem and Slet — had no way to put it away.

There is one now: Arkivér between Save and Delete, which reads "bring it back" once
the note is already archived. Owner only, like delete, and it runs the same archive
the row button does — the note leaves the list without leaving the account.

e2e: archiving from the footer takes the note off the list, the archive holds it
with a bring-back button, and bringing it back returns it. The existing bulk-archive
test is scoped to the selection bar, now that "Arkivér" also lives in the footer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 15:37:17 +02:00
kristianwindandClaude Opus 5 8022502015 feat(notes): [[note]] is an inline link, not bracketed text
Choosing a note from the [[ completion wrote [[Title]] as plain text. It made a
real link behind the scenes — it showed in the panel under the note — but in the
note itself it stayed brackets, and "it doesn't become a link" is exactly how that
reads. A wiki link is a link where it is written.

Now a [[note]] renders inline as the title, in the accent colour, and a click on it
opens the note it points at. It is atomic — contenteditable="false" — so it moves
and deletes as one thing and a click is a click, not a caret landing mid-word. And
it appears the moment it is chosen: the completion drops the finished link in,
brackets and all swallowed, rather than text waiting to become a link when the note
is next opened.

The seam holds both ways: markdownToHtml lifts [[title]] into the link the same way
it already lifts an image, and htmlToMarkdown reads the link back to [[title]] by
the title it carries — so what is on disk is still the same Markdown, and a note
opened, saved and reopened is unchanged.

e2e: choosing a note writes an inline link, it survives the round-trip through
Markdown and a reload, and clicking it opens the note it names. Full suite green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 13:41:04 +02:00
kristianwindandClaude Opus 5 de95df44c1 fix(notes): a [[note]] link is one you can follow
Picking a note from the [[ completion wrote [[Title]] and the server made a real
link from it — but the link only ever showed in the panel under the note as plain
text. It said the note pointed somewhere and gave no way to go there, which reads
as "it didn't become a link at all".

The links in that panel are buttons now. A [[note]] resolves by its title to the
note — the one already on the list, or looked up if not — and opens it in place; a
#project goes to its page; a task link to the task. Titles with punctuation work,
brackets and parentheses included, because the match is on the whole title, not a
parse of it.

e2e: after linking a note, clicking its chip opens the note it names.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 13:11:46 +02:00
kristianwindandClaude Opus 5 6116ed2e21 fix(notes): [[ completes a note link, the way # completes a project
Typing [[ did nothing. The toolbar hint offered "[[ another note" and the parser
turned a fully-typed [[Title]] into a link on save, but the completion that makes
it usable — the one # has for projects — was never there, so you had to know and
type a note's title exactly, brackets and all.

Now [[ opens the same picker # does, over the notes the page has loaded: the
current note is left out (a note linking to itself is a loop nobody meant), and
one without a title yet cannot be pointed at. Choosing a note closes its own
brackets — the opener is already in the text — and leaves a space, so the next
word is a word and not more of the tag. On save the link is real and shows in the
note's links, not merely as text.

The picker's two kinds are told apart where a project wears its colour: a note
suggestion wears the brackets that open it.

e2e: [[ offers a note by title, choosing it writes [[Title]] into the note, and
the link lands in the note's links. Full Chromium suite green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 12:22:38 +02:00
kristianwindandClaude Opus 5 fcedab0b19 docs(site): add the new features and light screenshots of them
The landing page did not mention two things the app grew — sharing a note with a
person, and task durations — and its screenshots were all in the dark theme.

- Copy: a cell for sharing a note or a page (to a person, or by filing it in a
  project, with the Shared with me group), and one for how long a task takes (drawn
  to length on the calendar, dragged by its foot, kept when moved).

- Two new screenshots in the light theme, at the top of the gallery: the calendar
  drawing timed tasks to their real length, and a note somebody shared gathered
  under Shared with me. English throughout, like the rest of the site — the capture
  account is English, so the interface is.

The generator grew the demo data these need: a handful of timed tasks with lengths
across the week, and a note the colleague shares back to the owner. Every other
screenshot was recaptured from the current build in the same pass, so the whole set
reflects what ships today (the tidied note footer, and the rest).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 10:30:00 +02:00
kristianwindandClaude Opus 5 4f25905d81 feat(calendar): a task's length is drawn, resizable, and editable
A timed task was drawn at a fixed half hour whatever its `duration_min` said, so a
length the data model already carried was invisible and uneditable. Three parts:

- The calendar draws a task at its real length — a quarter-hour floor so a
  zero-minute task is still grabbable, the day's end as the ceiling — instead of a
  flat thirty minutes. A ninety-minute task now looks like ninety minutes.

- A grip along the foot of a timed task drags its length. Pointer events, not the
  HTML5 drag the body uses, and stopPropagation between them — otherwise making a
  task longer would pick it up and move it instead. It snaps to the quarter hour,
  follows the handle live, and writes once on release through a new store `resize`.
  Moving a task (the existing day/time drag) leaves the length alone.

- The task drawer gains a length field beside the time — minutes, shown only with
  a time to hang it on, since a duration without a start is a length of nothing and
  the calendar has nowhere to draw it. So a length can be set off the calendar too.

e2e: dragging the foot of a task makes it longer both on screen and in what the
server stores, and the time does not move. Full Chromium suite green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 10:14:39 +02:00