Skip to content

Add confidence indicator, uncertainty + disagreement markers, feedback form, correction input - #3

Merged
loookashow merged 5 commits into
mainfrom
feat/control-and-transparency
May 8, 2026
Merged

Add confidence indicator, uncertainty + disagreement markers, feedback form, correction input#3
loookashow merged 5 commits into
mainfrom
feat/control-and-transparency

Conversation

@loookashow

Copy link
Copy Markdown
Contributor

Adds five new components for surfacing assistant confidence and user disagreement / corrections, plus the shared scaffolding (tokens, types, events, i18n keys) the components share.

Components

  • loquix-confidence-indicator — score 0–1 rendered as bar / dots / badge / numeric. Auto-derives low / medium / high from low-threshold / high-threshold; invalid or out-of-range thresholds reset to defaults so derivation still tracks the value. Exposes role="meter" with aria-valuetext on every variant.
  • loquix-uncertainty-marker — wraps an inline phrase as unsure, needs-verification, or speculative. Variants: underline (wavy), highlight (background), icon (trailing glyph). Tooltip opens on hover/focus, closes on mouseleave/blur/Escape. aria-describedby keeps the slotted phrase as the accessible name. Enter and Space activate. Emits loquix-uncertainty-click with { kind, reason? }.
  • loquix-disagreement-marker — inline pill or full banner attached to a disputed message. Banner can show "Mark resolved" via resolvable. Emits loquix-disagreement-resolve.
  • loquix-feedback-form — higher-order flow that composes two loquix-action-feedback buttons with a reasons + comment card. Parent state drives the children's active; the inner loquix-feedback is intercepted and never leaks. Emits loquix-feedback-submit with { sentiment, reason?, comment? }reason is a stable ID, never the localised chip label. require-comment-on-down enforces a non-empty comment for negative feedback. Reasons use radio semantics
    (role="radio" + aria-checked).
  • loquix-correction-input — strikethrough original + correction textarea + reason input + Submit / Cancel. Emits loquix-correction-submit with { correction, reason?, original? } and loquix-correction-cancel. Submit gated by non-empty correction (and non-empty reason when reason-required). All values rendered as text via Lit interpolation — no unsafeHTML.

Shared additions

  • Tokens for confidence levels and uncertainty kinds in variables.css + themes/dark.css.
  • Type unions (ConfidenceVariant/Level, UncertaintyKind/Variant, DisagreementVariant, stable FeedbackReason union).
  • New HTMLElementEventMap entries + detail interfaces.
  • I18n keys for all five components in i18n/types.ts + translations/en.ts.
  • React wrappers in @loquix/react.
  • Storybook stories in docs/src/stories/core/ and define-* imports in docs/.storybook/preview.ts.
  • Regenerated custom-elements.json.
  • Changeset (minor bump for @loquix/core; @loquix/react follows via linked config).

Reviews

  • Visual: live Safari + Chrome testing in Storybook caught two cross-browser issues — text-decoration shorthand silently wiping per-kind colour in WebKit, and text-decoration-skip-ink: auto in Chrome carving gaps in the wavy underline at descenders.

Test plan

  • pnpm --filter @loquix/core build — green
  • pnpm --filter @loquix/react build — green
  • pnpm --filter @loquix/core test — 956 tests pass, Chromium + WebKit
  • pnpm lint — clean (only pre-existing warnings)
  • pnpm format:check — clean
  • Visual review of all five components in Storybook (light + dark themes)

loookashow added 5 commits May 7, 2026 22:29
…primitives

Adds the first of five new components for surfacing how confident the
assistant is and where it is uncertain, plus the shared scaffolding the remaining four will reuse.

Foundation (additive):
- Tokens for confidence levels and uncertainty kinds in variables.css + dark.css
- Types: ConfidenceVariant/Level, UncertaintyKind/Variant, DisagreementVariant FeedbackReason (stable ID union — labels live in i18n)
- Event detail types for feedback submit, correction submit/cancel, disagreement resolve, uncertainty click
- i18n keys for all five upcoming components

loquix-confidence-indicator:
- Variants: bar / dots / badge / numeric
- Auto-derives level from value with low-threshold / high-threshold; invalid or inverted thresholds reset to defaults so derivation still tracks value
- A11y: role="meter" + aria-valuetext on every variant
- 19 tests (Chromium + WebKit), Storybook story, React wrapper
Two new components for surfacing model uncertainty and user disagreement within a conversation.

loquix-uncertainty-marker:
- Wraps an inline phrase as unsure / needs-verification / speculative
- Variants: underline (wavy), highlight (background), icon (trailing glyph)
- Tooltip opens on hover/focus, closes on mouseleave/blur/Escape
- A11y: role=button + aria-describedby (slotted text remains the accessible name); Enter and Space activate (Space preventDefault'd)
- Emits loquix-uncertainty-click with { kind, reason? }
- 14 tests, Storybook story, React wrapper

loquix-disagreement-marker:
- Inline pill or full banner attached to a disputed message
- Optional reason text + slot for extra banner content
- Banner can show a "Mark resolved" button via the resolvable attribute
- Emits loquix-disagreement-resolve when resolved
- A11y: role=status on both variants
- 13 tests, Storybook story, React wrapper
Two interactive forms that complete the confidence/uncertainty surface for assistant messages.

loquix-feedback-form:
- Higher-order flow over two loquix-action-feedback buttons + reasons
  + comment card
- Parent state drives child active props (?active=${value === ...});
  inner loquix-feedback is intercepted with stopPropagation so only the form's loquix-feedback-submit reaches consumers
- Reason chips use role=radio + aria-checked (not aria-pressed)
- Stable FeedbackReason ID union: 'accurate'/'well-written'/'helpful'/
  'inaccurate'/'off-topic'/'unsafe'/'other'. Localised labels live in i18n; the event payload always carries the ID
- Optional require-comment-on-down gates Send on negative sentiment
- 18 tests, Storybook story, React wrapper

loquix-correction-input:
- Original (strikethrough) + correction textarea + reason input + Cancel/Submit
- Submit disabled until correction is non-empty(and reason non-empty when reason-required)
- Emits loquix-correction-submit / loquix-correction-cancel
- All values rendered as text via Lit interpolation — no unsafeHTML
- 12 tests, Storybook story, React wrapper

Plus a changeset documenting all five new components.
- feedback-form: validate value via _validValue() helper so runtime-invalid sentiment strings collapse to null; reset draft state in willUpdate when value changes externally so controlled usage doesn't carry stale thanks / chip / comment after parent assigns value=null or flips sentiment
- uncertainty-marker: tooltip moved out of role=button subtree (now a sibling under .wrapper) so the slotted phrase remains the accessible name and the kind/reason is exposed purely via aria-describedby
- confidence-indicator: out-of-range thresholds (outside [0, 1]) fall back to defaults like other invalid inputs; dots variant no longer forces a lit dot at value=0

From Safari visual testing:
- uncertainty-marker: switch text-decoration to longhand (line/style/thickness/offset) so Safari keeps the per-kind text-decoration-color — WebKit resets it when the shorthand 'underline wavy' is used
- uncertainty-marker: stop overriding font-family on :host so the slotted phrase inherits font from surrounding prose (was looking heavier / differently shaped than the paragraph it lived in)

Plus regression tests for invalid sentiment, controlled value reset across submit and across sentiment flip, threshold range, dots at 0, and tooltip-not-inside-role=button.
- uncertainty-marker: force text-decoration-skip-ink: none. Chrome's default 'auto' carves gaps in the wavy underline where descenders (p, g, y) cross the baseline; Safari already kept it solid. Now both render a continuous wave.
- feedback-form story: override layout to 'padded' so the buttons row stays at the left edge in both idle and selected states. With the global 'centered' layout, expanding the host width (when the form panel opens) made the buttons appear to jump left — a story-only artifact, not a component bug.
@loookashow
loookashow merged commit e7db5db into main May 8, 2026
5 checks passed
@loookashow
loookashow deleted the feat/control-and-transparency branch May 8, 2026 08:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant