Compare commits

..
Author SHA1 Message Date
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
Kristian Wind acec77a20c Merge pull request #37 from kristianwind/sidebar-things-nav
Release / Build and publish image (push) Failing after 6s
feat(sidebar): icons for fixed views, bold areas, one straight edge
2026-09-03 10:17:56 +02:00
35 changed files with 59 additions and 8 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 KiB

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 KiB

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 KiB

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 KiB

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 KiB

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 KiB

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 40 KiB

+12 -3
View File
@@ -1525,9 +1525,18 @@ test('en gruppe er en side med sine projekter, en beskrivelse og filer', async (
// Into the group, then onto its page — which is what the heading is a link to
// now. The chevron beside it still folds; they are two intentions and the old
// single target could only serve the smaller one.
await sidebar
.getByRole('link', { name: 'Drejebænk' })
.dragTo(sidebar.getByRole('link', { name: 'Værksted' }));
//
// The drop is retried until the project actually nests under the group: a native
// HTML5 drag occasionally does not register the first time under CI load, and a
// single dragTo then leaves the group empty and the page assertion failing for a
// reason that has nothing to do with what the test is about.
const nested = sidebar.locator('.folder > a').filter({ hasText: 'Drejebænk' });
await expect(async () => {
await sidebar
.getByRole('link', { name: 'Drejebænk' })
.dragTo(sidebar.getByRole('link', { name: 'Værksted' }));
await expect(nested).toHaveCount(1, { timeout: 1000 });
}).toPass({ timeout: 10000 });
await sidebar.getByRole('link', { name: 'Værksted' }).click();
await expect(page.getByRole('heading', { name: 'Værksted', level: 1 })).toBeVisible();
+39 -4
View File
@@ -487,6 +487,7 @@
it, which is the second time that drop has earned its test. -->
<a
href={item.href}
data-view={item.key}
class:active={current === item.href}
class:receiving={item.key === 'today' && overToday}
onclick={onnavigate}
@@ -616,6 +617,7 @@
{#snippet projectRow(project, sortable)}
<a
href="/projekt/{project.id}"
data-view={project.is_inbox ? 'inbox' : undefined}
class:sortable
class:active={current === `/projekt/${project.id}`}
class:dragging={draggingId === project.id}
@@ -724,7 +726,7 @@
and with every project filed there would be no loose row left to aim at. -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="group-head"
class="group-head sticky-head"
class:over={overGroup === ''}
ondragover={(e) => {
if (!carries(e, PROJECT)) return;
@@ -1201,8 +1203,26 @@
flex: none;
}
a.active .mark {
color: var(--accent);
/* The fixed views carry a colour of their own, the way Things does — the star
amber, the Inbox blue — and keep it whether or not they are the active row.
So no active-state override here; the colour is the icon's identity. */
[data-view='inbox'] .mark {
color: var(--color-blue);
}
[data-view='today'] .mark {
color: var(--color-amber);
}
[data-view='upcoming'] .mark {
color: var(--color-tomato);
}
[data-view='delegated'] .mark {
color: var(--color-violet);
}
[data-view='notes'] .mark {
color: var(--color-teal);
}
[data-view='calendar'] .mark {
color: var(--color-indigo);
}
.group-head {
@@ -1212,6 +1232,17 @@
padding: 0 var(--s2) var(--s2);
}
/* The Projekter heading holds the only way to add a project or a group, so it
must not scroll away with a long list. Pinned to the top of the scroller, on
the sidebar's own ground so the rows pass under it. */
.sticky-head {
position: sticky;
top: 0;
z-index: 3;
padding-top: var(--s2);
background: var(--surface-sunken);
}
h2 {
font-size: var(--text-xs);
font-weight: 560;
@@ -1533,8 +1564,12 @@
color: var(--ink);
}
/* An accent-tinted selection, not a half-step of surface: on the sunken dark
sidebar --surface-raised was all but invisible, so the active row read as no
different from the rest. --accent-sunken carries the accent hue and stands out
in both themes. */
a.active {
background: var(--surface-raised);
background: var(--accent-sunken);
color: var(--ink);
font-weight: 600;
}
+8 -1
View File
@@ -1081,6 +1081,11 @@
min-width: 0;
border-right: 1px solid var(--line);
padding-right: var(--s3);
/* White in light, a nuance lighter than the ground in dark — the note list
is a surface you read down, so it lifts off the page like the editor does.
--surface-raised is #fff in the light themes and one step up in the dark. */
background: var(--surface-raised);
padding-left: var(--s3);
}
.head {
@@ -1546,7 +1551,9 @@
flex-direction: column;
min-height: 0;
min-width: 0;
background: var(--surface);
/* A nuance lighter than the ground in dark, still white in light — matches
the note list beside it so the two read as one writing surface. */
background: var(--surface-raised);
border: 1px solid var(--line);
border-radius: var(--radius);
/* Luft nok til at teksten ikke rører kanten.