web: shared note actions, reminder chips, page header, formatters and settings seam

From the audit (#5179, web half).

- NoteActions: share, pin, archive and trash (restore and delete forever
  when trashed), as both the card and the editor offer them. The editor's
  history toggle goes in its slot, and it closes on `acted`.
- ReminderActions: the Done / 1h / 1d chips on the card and in the editor,
  which are now the same chips.
- PageHeader: the back-to-board header that Settings, Sync and Linked
  devices each wrote out, now with a `back` icon from the shared set.
- notes/datetime: formatShortDateTime (was formatReminder and the editor's
  revLabel) and formatDateTime (the two `fmt` copies).
- notes/colors: labelDotClasses (the sidebar's and the tag manager's
  labelDot).
- Drawer links use exact-active-class instead of route.name ternaries.
- BoardView binds its three grids from one gridBinds object.
- Settings goes through repo.settings (rest, plus a local adapter that
  answers "needs a server") and shows load failures through AsyncState.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 20:02:18 -04:00
co-authored by Claude Opus 5.5
parent 05c82c2362
commit e75e3d37d0
17 changed files with 348 additions and 422 deletions
+4 -23
View File
@@ -7,7 +7,9 @@ import BaseButton from "../components/BaseButton.vue";
import BaseInput from "../components/BaseInput.vue";
import ClientDownloads from "../components/ClientDownloads.vue";
import Icon from "../components/Icon.vue";
import PageHeader from "../components/PageHeader.vue";
import { errorMessage } from "../api/errors";
import { formatDateTime } from "../notes/datetime";
// Per-user (not admin) management of linked native clients — the Tauri desktop and
// Android apps authenticate sync with a device bearer token issued here.
@@ -66,11 +68,6 @@ async function revoke(id: string, name: string) {
}
}
function fmt(iso: string | null): string {
if (!iso) return "never";
return new Date(iso).toLocaleString();
}
onMounted(() => {
void load();
});
@@ -78,23 +75,7 @@ onMounted(() => {
<template>
<div class="mx-auto min-h-full max-w-2xl px-4 py-8">
<header class="mb-8 flex items-center gap-3">
<RouterLink to="/" class="icon-btn" title="Back to board" aria-label="Back to board">
<svg
class="h-[18px] w-[18px]"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="m15 18-6-6 6-6" />
</svg>
</RouterLink>
<h1 class="text-xl font-bold tracking-tight">Linked devices</h1>
</header>
<PageHeader title="Linked devices" />
<p class="mb-6 max-w-xl text-sm text-neutral-500 dark:text-neutral-400">
Link the Inkwell desktop or mobile app to sync your notes. Create a device token here, then
@@ -170,7 +151,7 @@ onMounted(() => {
<div class="min-w-0">
<p class="truncate text-sm font-medium text-neutral-800 dark:text-neutral-100">{{ d.name }}</p>
<p class="text-xs text-neutral-400">
Linked {{ fmt(d.created_at) }} · last synced {{ fmt(d.last_used_at) }}
Linked {{ formatDateTime(d.created_at) }} · last synced {{ formatDateTime(d.last_used_at) }}
</p>
</div>
</div>