feat(tags): system-tag UI markers + full protection sweep (step 4 of #128)
UI: shield marker + tooltip on TagChip and TagCard; system tags hide rename/merge/delete affordances (chip kebab entirely — set-fandom never applies to their general kind; remove stays, un-tagging is normal use). Aliases stay available: mapping model outputs ONTO a system tag is useful. Directory cards carry is_system. Every destructive path that could take out a system row is now guarded, found by sweeping run 1891s off-by-three failures — each one was a surface that would have eaten the seeded tags: - prune-unused: predicate exempts is_system (they ship with zero applications and matched every unused condition) - reset-content: predicate exempts is_system AND keeps their applications — hygiene flags describe the file, not content tagging - admin tag DELETE: refused with system_tag error - normalize_existing_tags: scan excludes is_system — canonicalization would recase wip -> Wip behind TagService.rename's guard, breaking the name-keyed presentation lookup Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
@@ -13,9 +13,15 @@
|
||||
<div class="fc-tagcard__name">
|
||||
<template v-if="!editing">
|
||||
{{ card.name }}
|
||||
<v-icon
|
||||
v-if="card.is_system" size="14" icon="mdi-shield-outline"
|
||||
class="fc-tagcard__system"
|
||||
title="System tag — tagged items are excluded from training other concepts"
|
||||
/>
|
||||
<span v-if="card.kind === 'character' && card.fandom_name"
|
||||
class="fc-tagcard__fandom">— {{ card.fandom_name }}</span>
|
||||
<v-icon
|
||||
v-if="!card.is_system"
|
||||
class="fc-tagcard__edit" size="14" icon="mdi-pencil"
|
||||
@click.stop="startEdit"
|
||||
/>
|
||||
@@ -58,12 +64,18 @@
|
||||
prepend-icon="mdi-tag-multiple"
|
||||
@click="$emit('aliases', card)"
|
||||
/>
|
||||
<!-- System tags (#128): merge-away and delete are refused
|
||||
server-side — the hygiene machinery keys on the row — so
|
||||
don't offer them. Aliases stay (mapping model outputs ONTO
|
||||
a system tag is useful). -->
|
||||
<v-list-item
|
||||
v-if="!card.is_system"
|
||||
title="Merge with…"
|
||||
prepend-icon="mdi-call-merge"
|
||||
@click="$emit('merge-with', card)"
|
||||
/>
|
||||
<v-list-item
|
||||
v-if="!card.is_system"
|
||||
title="Delete tag"
|
||||
prepend-icon="mdi-delete"
|
||||
base-color="error"
|
||||
@@ -133,6 +145,7 @@ function submit() {
|
||||
.fc-tagcard__body { padding: 8px 12px; }
|
||||
.fc-tagcard__name { font-weight: 600; }
|
||||
.fc-tagcard__fandom { font-weight: 400; opacity: 0.7; }
|
||||
.fc-tagcard__system { opacity: 0.6; margin-left: 2px; }
|
||||
.fc-tagcard__meta {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
margin-top: 4px;
|
||||
|
||||
@@ -10,14 +10,23 @@
|
||||
@click:close="$emit('remove', tag.id)"
|
||||
>
|
||||
<v-icon start size="x-small">{{ iconFor(tag.kind) }}</v-icon>
|
||||
{{ tag.name }}<span
|
||||
{{ tag.name }}<v-icon
|
||||
v-if="tag.is_system" end size="x-small" class="fc-tag-chip__system"
|
||||
title="System tag — tagged items are excluded from training other concepts"
|
||||
>mdi-shield-outline</v-icon><span
|
||||
v-if="tag.fandom_id" class="fc-tag-chip__fandom"
|
||||
:title="tag.fandom_name ? `Fandom: ${tag.fandom_name}` : 'Has a fandom'"
|
||||
>→<template v-if="fandomLabel"> {{ fandomLabel }}</template></span>
|
||||
</v-chip>
|
||||
<!-- Modal-safe kebab is baked into KebabMenu (this chip lives in the
|
||||
teleported image modal — #711). -->
|
||||
<KebabMenu class="fc-tag-chip__kebab" :label="`More actions for ${tag.name}`">
|
||||
teleported image modal — #711). System tags hide it entirely: rename
|
||||
is refused server-side (the hygiene machinery keys on the row) and
|
||||
set-fandom never applies to their general kind. Remove (✕) stays —
|
||||
un-tagging an image is normal use. -->
|
||||
<KebabMenu
|
||||
v-if="!tag.is_system"
|
||||
class="fc-tag-chip__kebab" :label="`More actions for ${tag.name}`"
|
||||
>
|
||||
<v-list-item @click="$emit('rename', tag)">
|
||||
<v-list-item-title>Rename…</v-list-item-title>
|
||||
</v-list-item>
|
||||
@@ -64,6 +73,7 @@ function iconFor (k) { return KIND_ICONS[k] || 'mdi-tag' }
|
||||
outline: 2px solid rgb(var(--v-theme-accent));
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.fc-tag-chip__system { opacity: 0.6; margin-left: 2px; }
|
||||
.fc-tag-chip__kebab { opacity: 0.7; }
|
||||
.fc-tag-chip:hover .fc-tag-chip__kebab { opacity: 1; }
|
||||
.fc-tag-chip__fandom { opacity: 0.7; font-size: 0.85em; }
|
||||
|
||||
Reference in New Issue
Block a user