diff --git a/frontend/src/api/rulebooks.ts b/frontend/src/api/rulebooks.ts index 9665f42..1a8c175 100644 --- a/frontend/src/api/rulebooks.ts +++ b/frontend/src/api/rulebooks.ts @@ -1,3 +1,5 @@ +import type { RecordUsage } from "@/types/usage"; + import { apiGet, apiPost, apiPatch, apiDelete } from "@/api/client"; /** How a rule reaches a session (milestone 307). */ @@ -96,6 +98,13 @@ export interface RuleHeader { * A date (YYYY-MM-DD), or the literal "never". */ last_verified?: string; + /** + * Surfaced-vs-opened counts from `rule_usage_events` (milestone 333). + * Zero-filled by the list route, so a rule predating the table reads as + * "never surfaced" rather than as a missing field — which for a while is + * every rule on every install. + */ + usage?: RecordUsage; } export interface ApplicableRules { diff --git a/frontend/src/api/snippets.ts b/frontend/src/api/snippets.ts index 25bd2d8..0931e14 100644 --- a/frontend/src/api/snippets.ts +++ b/frontend/src/api/snippets.ts @@ -1,3 +1,5 @@ +import type { RecordUsage } from "@/types/usage"; + import { apiGet, apiPost, apiPatch, apiDelete } from "@/api/client"; /** One canonical location of a reusable thing. A snippet that unifies several @@ -50,15 +52,11 @@ export interface Snippet { owner?: string | null; } -/** How often a record was put in front of an agent versus actually opened. - * A high `surfaced_count` with `pull_count: 0` is dead weight — it occupies a - * slot in every future auto-inject menu while never being used. */ -export interface SnippetUsage { - surfaced_count: number; - pull_count: number; - last_surfaced_at: string | null; - last_pulled_at: string | null; -} +/** Kept as a name because every consumer here says "snippet usage" — but it IS + * the shared shape, since rules answer the same question off their own table + * (milestone 333). The reasoning lives on `RecordUsage`; duplicating the four + * fields here is how the two drift. */ +export type SnippetUsage = RecordUsage; /** Result of the last drift check — does the recorded location and code still * match source? The check runs agent-side (Scribe has no checkout); this is the diff --git a/frontend/src/assets/components.css b/frontend/src/assets/components.css index 0d76513..c886ef0 100644 --- a/frontend/src/assets/components.css +++ b/frontend/src/assets/components.css @@ -351,3 +351,29 @@ .required { color: var(--fs-error); } .field-hint { margin: 0.3rem 0 0; font-size: 0.8rem; color: var(--fs-text-tertiary); } + +/* --- usage badge ---------------------------------------------------------- + "surfaced N×, opened M×" on a list row, for any record kind the retrieval + surfaces can choose: snippets and notes from note_usage_events, rules from + rule_usage_events. Promoted here from SnippetListView's scoped block when + the rule list needed the same chip (milestone 333 step 5) — a second scoped + copy is how the ninth duplicated CSS family starts (#3207). + + Geometry and colour only. A view keeps its own spacing as a scoped + remainder, the way it does for every other recipe in this file. */ +.usage-tag { + font-size: 0.7rem; + padding: 0.1rem 0.4rem; + border-radius: 4px; + white-space: nowrap; + font-variant-numeric: tabular-nums; + background: color-mix(in srgb, var(--fs-text-tertiary) 15%, transparent); + color: var(--fs-text-tertiary-fg); +} + +/* Dead weight is a nudge, not an error — it warns in the warning colour rather + than the danger one, because the record isn't broken, just unearned. */ +.usage-tag.usage-dead { + background: color-mix(in srgb, var(--fs-warning) 18%, transparent); + color: var(--fs-warning-fg); +} diff --git a/frontend/src/components/UsageBadge.vue b/frontend/src/components/UsageBadge.vue new file mode 100644 index 0000000..47fabbb --- /dev/null +++ b/frontend/src/components/UsageBadge.vue @@ -0,0 +1,62 @@ + + + + + diff --git a/frontend/src/components/rules/RuleListPane.vue b/frontend/src/components/rules/RuleListPane.vue index 6c2a662..32d08f7 100644 --- a/frontend/src/components/rules/RuleListPane.vue +++ b/frontend/src/components/rules/RuleListPane.vue @@ -1,5 +1,16 @@