Attributes UX: Key (type) labels, filter, Overlay without field target.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

CE-WP-0010: rename form vocabulary in user-facing strings to attributes,
show type next to keys, filter attributes at ≥3 characters, and allow
card→highlight guide lines when no attribute is focused.
This commit is contained in:
tegwick 2026-07-30 19:41:41 +02:00
parent c07e369bdb
commit 32c5a2ede4
5 changed files with 137 additions and 53 deletions

View file

@ -11,10 +11,10 @@
* `useSyncExternalStore` subscription via `useRectRegistryVersion` picks
* up that single tick and React re-renders once per frame.
*
* Active-only: only the currently active triple is drawn. If any leg's
* rect is missing (e.g. the viewer hasn't reported a highlight rect for
* the active annotation yet), that leg is omitted but the other one
* still renders.
* Active-only: draws legs for the active evidence card. Attributecard
* when a Capture attribute is focused; cardhighlight whenever the
* highlight rect is available (Annotate or Capture). Missing legs are
* omitted independently.
*
* MVP-sufficient. Future polish: easing the curve direction by source
* type, animating the transition between active states, dimming
@ -33,14 +33,6 @@ function rectCenter(rect: DOMRect): { x: number; y: number } {
return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
}
function rectBottomCenter(rect: DOMRect): { x: number; y: number } {
return { x: rect.left + rect.width / 2, y: rect.bottom };
}
function rectTopCenter(rect: DOMRect): { x: number; y: number } {
return { x: rect.left + rect.width / 2, y: rect.top };
}
/**
* Build a quadratic bezier from `a` to `b` whose control point bulges
* horizontally between them. The horizontal-bulge style is right for a
@ -72,18 +64,24 @@ export function Overlay({
const version = useRectRegistryVersion();
const paths = useMemo<readonly string[]>(() => {
if (!state.activeTarget || !state.activeEvidenceItemId) return [];
const fieldRect = registry.getRect("field", state.activeTarget.targetId);
// CE-WP-0010: card → highlight is enough for Annotate mode (no attribute
// target). Field → card still draws when Capture has an active target.
if (!state.activeEvidenceItemId) return [];
const fieldRect = state.activeTarget
? registry.getRect("field", state.activeTarget.targetId)
: null;
const cardRect = registry.getRect("evidence-card", state.activeEvidenceItemId);
const highlightRect = state.activeAnnotationId
? registry.getRect("highlight", state.activeAnnotationId)
: null;
const out: string[] = [];
if (fieldRect && cardRect) {
out.push(bezierPath(rectBottomCenter(fieldRect), rectTopCenter(cardRect)));
// Horizontal layout (Evidence | Attributes): centre-to-centre reads better
// than the old bottom-strip geometry.
out.push(bezierPath(rectCenter(fieldRect), rectCenter(cardRect)));
}
if (cardRect && highlightRect) {
out.push(bezierPath(rectTopCenter(cardRect), rectCenter(highlightRect)));
out.push(bezierPath(rectCenter(cardRect), rectCenter(highlightRect)));
}
void version; // memo invalidator
return out;