copy: the product says "tags" now, and the schema keeps saying Label
Android / Build, or is the channel already serving this? (push) Successful in 3s
CI & Build / Build now, or wait for Android? (push) Successful in 3s
CI & Build / Python lint (push) Successful in 3s
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 7s
CI & Build / Python tests (push) Successful in 16s
CI & Build / integration (push) Successful in 23s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 3m7s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 5m17s
Desktop (Tauri) / Update manifest (push) Successful in 4s
Android / Kotlin + Rust (APK) (push) Successful in 8m4s
Android / Build, or is the channel already serving this? (push) Successful in 3s
CI & Build / Build now, or wait for Android? (push) Successful in 3s
CI & Build / Python lint (push) Successful in 3s
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 7s
CI & Build / Python tests (push) Successful in 16s
CI & Build / integration (push) Successful in 23s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 3m7s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 5m17s
Desktop (Tauri) / Update manifest (push) Successful in 4s
Android / Kotlin + Rust (APK) (push) Successful in 8m4s
Two words for one concept cost real comprehension: over a single exchange
the operator concluded that auto-tagging did not exist (it does, in
`derive.rs`) and that a tag-management view did not exist (it does,
`LabelsModal.vue`). The `#` is how most of these get made, so the `#` wins
the noun.
User-visible strings only, on all three surfaces plus the server's errors.
`Label`, `NoteLabel`, `via_tag`, `label_id`, the tables, `/api/labels` and
the FFI names are all untouched — renaming those touches migrations and the
wire format to buy nothing a reader can see.
Two of these were more than a find-and-replace:
* Android's `label_from_tag` said "from #tag", sitting beside a chip that
already renders as `#name`. Once every one of them IS a tag that hint is
circular. What it actually tells you is that the note's BODY owns this
one — which is why it alone has no remove cross — so it now says "from
the text".
* The web's empty state said "No labels yet — create one above" while
Android's already mentioned the `#` route. The web now says it too. That
is the exact fact the operator did not have.
The paired `aria-label`s went with their `title`s; a screen reader saying
"label" while the tooltip says "tag" is the same confusion with a smaller
audience.
Left alone deliberately: `json_error("invalid label")` and
`"label_ids must be a list"` in `notes/__init__.py` name the `?label=` query
parameter and the `label_ids` request field. Those are wire surface, not the
word a person reads.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3MMqUtzX1TJgA1oypvm1c
This commit is contained in:
@@ -441,18 +441,18 @@ async function signOut() {
|
||||
</RouterLink>
|
||||
|
||||
<div class="mt-3 flex items-center justify-between px-3 pb-1">
|
||||
<span class="text-xs font-semibold uppercase tracking-wide text-neutral-400">Labels</span>
|
||||
<span class="text-xs font-semibold uppercase tracking-wide text-neutral-400">Tags</span>
|
||||
<button
|
||||
type="button"
|
||||
class="text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-200"
|
||||
title="Edit labels"
|
||||
aria-label="Edit labels"
|
||||
title="Manage tags"
|
||||
aria-label="Manage tags"
|
||||
@click="managing = true"
|
||||
>
|
||||
<Icon name="pencil" />
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="!labels.items.length" class="px-3 py-1 text-xs text-neutral-400">No labels yet</p>
|
||||
<p v-if="!labels.items.length" class="px-3 py-1 text-xs text-neutral-400">No tags yet</p>
|
||||
<RouterLink
|
||||
v-for="lb in labels.items"
|
||||
:key="lb.id"
|
||||
|
||||
@@ -108,7 +108,7 @@ const chipOff = "border-neutral-300 text-neutral-600 hover:bg-neutral-100 dark:b
|
||||
class="mt-2 flex flex-col gap-3 rounded-xl border border-neutral-200 p-3 dark:border-neutral-800"
|
||||
>
|
||||
<div v-if="labels.items.length" class="flex flex-wrap items-center gap-1.5">
|
||||
<span class="w-16 shrink-0 text-xs text-neutral-400">Labels</span>
|
||||
<span class="w-16 shrink-0 text-xs text-neutral-400">Tags</span>
|
||||
<button
|
||||
v-for="lb in labels.items"
|
||||
:key="lb.id"
|
||||
|
||||
@@ -46,7 +46,7 @@ onBeforeUnmount(() => document.removeEventListener("mousedown", onDocMousedown))
|
||||
|
||||
<template>
|
||||
<div ref="root" class="relative">
|
||||
<button type="button" class="icon-btn" title="Labels" aria-label="Labels" @click="open = !open">
|
||||
<button type="button" class="icon-btn" title="Tags" aria-label="Tags" @click="open = !open">
|
||||
<Icon name="tag" />
|
||||
</button>
|
||||
<div
|
||||
@@ -56,7 +56,7 @@ onBeforeUnmount(() => document.removeEventListener("mousedown", onDocMousedown))
|
||||
<input
|
||||
v-model="filter"
|
||||
type="text"
|
||||
placeholder="Label note…"
|
||||
placeholder="Tag note…"
|
||||
class="mb-1 w-full rounded-md border border-neutral-300 bg-white px-2 py-1 text-sm outline-none focus-visible:ring-2 focus-visible:ring-brand dark:border-neutral-600 dark:bg-neutral-900"
|
||||
/>
|
||||
<ul class="max-h-48 overflow-y-auto">
|
||||
|
||||
@@ -67,7 +67,7 @@ async function doMerge(sourceId: string, targetId: string) {
|
||||
<template>
|
||||
<BaseModal panel-class="w-full max-w-sm shadow-xl" @close="emit('close')">
|
||||
<div class="flex items-center justify-between border-b border-neutral-100 px-4 py-3 dark:border-neutral-800">
|
||||
<h2 class="text-sm font-semibold">Manage labels</h2>
|
||||
<h2 class="text-sm font-semibold">Manage tags</h2>
|
||||
<button type="button" class="icon-btn" aria-label="Close" @click="emit('close')"><Icon name="close" /></button>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 p-4">
|
||||
@@ -76,7 +76,7 @@ async function doMerge(sourceId: string, targetId: string) {
|
||||
<input
|
||||
v-model="newName"
|
||||
type="text"
|
||||
placeholder="Create label"
|
||||
placeholder="Create tag"
|
||||
class="flex-1 rounded-md border border-neutral-300 bg-white px-2 py-1.5 text-sm outline-none focus-visible:ring-2 focus-visible:ring-brand dark:border-neutral-700 dark:bg-neutral-800"
|
||||
/>
|
||||
</form>
|
||||
@@ -87,7 +87,7 @@ async function doMerge(sourceId: string, targetId: string) {
|
||||
class="h-4 w-4 shrink-0 rounded-full border border-black/10 transition hover:scale-110 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand dark:border-white/15"
|
||||
:class="labelDot(lb)"
|
||||
:title="`Color: ${NOTE_COLOR_LABELS[resolveLabelColor(lb)]}`"
|
||||
aria-label="Change label color"
|
||||
aria-label="Change tag color"
|
||||
@click="openColor(lb.id)"
|
||||
/>
|
||||
<input
|
||||
@@ -104,8 +104,8 @@ async function doMerge(sourceId: string, targetId: string) {
|
||||
v-if="canMerge"
|
||||
type="button"
|
||||
class="icon-btn"
|
||||
title="Merge into another label"
|
||||
aria-label="Merge into another label"
|
||||
title="Merge into another tag"
|
||||
aria-label="Merge into another tag"
|
||||
@click="openMerge(lb.id)"
|
||||
>
|
||||
<Icon name="merge" />
|
||||
@@ -113,8 +113,8 @@ async function doMerge(sourceId: string, targetId: string) {
|
||||
<button
|
||||
type="button"
|
||||
class="icon-btn"
|
||||
title="Delete label"
|
||||
aria-label="Delete label"
|
||||
title="Delete tag"
|
||||
aria-label="Delete tag"
|
||||
@click="labels.remove(lb.id)"
|
||||
>
|
||||
<Icon name="trash" />
|
||||
@@ -160,7 +160,7 @@ async function doMerge(sourceId: string, targetId: string) {
|
||||
</li>
|
||||
</ul>
|
||||
<p v-if="!labels.items.length" class="py-2 text-center text-xs text-neutral-400">
|
||||
No labels yet — create one above.
|
||||
No tags yet — create one above, or write a #tag in a note and it becomes one.
|
||||
</p>
|
||||
</div>
|
||||
</BaseModal>
|
||||
|
||||
@@ -191,7 +191,7 @@ const emptyState = computed(() => {
|
||||
if (currentView.value === "trash") return { title: "Trash is empty", subtitle: "Notes you delete land here first." };
|
||||
if (currentView.value === "archived")
|
||||
return { title: "Nothing archived", subtitle: "Archived notes are tucked away here." };
|
||||
if (currentLabel.value) return { title: "No notes with this label", subtitle: "Tag a note to see it here." };
|
||||
if (currentLabel.value) return { title: "No notes with this tag", subtitle: "Tag a note to see it here." };
|
||||
// Says what "no account" actually means for the notes about to be written here.
|
||||
// A new user otherwise has no way to tell whether this thing is storing their
|
||||
// thoughts locally, silently waiting for a login, or quietly sending them off.
|
||||
|
||||
Reference in New Issue
Block a user