Add confidence indicator, uncertainty + disagreement markers, feedback form, correction input - #3
Merged
Merged
Conversation
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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-deriveslow/medium/highfromlow-threshold/high-threshold; invalid or out-of-range thresholds reset to defaults so derivation still tracks the value. Exposesrole="meter"witharia-valuetexton every variant.loquix-uncertainty-marker— wraps an inline phrase asunsure,needs-verification, orspeculative. Variants:underline(wavy),highlight(background),icon(trailing glyph). Tooltip opens on hover/focus, closes on mouseleave/blur/Escape.aria-describedbykeeps the slotted phrase as the accessible name. Enter and Space activate. Emitsloquix-uncertainty-clickwith{ kind, reason? }.loquix-disagreement-marker— inline pill or full banner attached to a disputed message. Banner can show "Mark resolved" viaresolvable. Emitsloquix-disagreement-resolve.loquix-feedback-form— higher-order flow that composes twoloquix-action-feedbackbuttons with a reasons + comment card. Parent state drives the children'sactive; the innerloquix-feedbackis intercepted and never leaks. Emitsloquix-feedback-submitwith{ sentiment, reason?, comment? }—reasonis a stable ID, never the localised chip label.require-comment-on-downenforces 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. Emitsloquix-correction-submitwith{ correction, reason?, original? }andloquix-correction-cancel. Submit gated by non-empty correction (and non-empty reason whenreason-required). All values rendered as text via Lit interpolation — nounsafeHTML.Shared additions
variables.css+themes/dark.css.ConfidenceVariant/Level,UncertaintyKind/Variant,DisagreementVariant, stableFeedbackReasonunion).HTMLElementEventMapentries + detail interfaces.i18n/types.ts+translations/en.ts.@loquix/react.docs/src/stories/core/anddefine-*imports indocs/.storybook/preview.ts.custom-elements.json.@loquix/core;@loquix/reactfollows vialinkedconfig).Reviews
text-decorationshorthand silently wiping per-kind colour in WebKit, andtext-decoration-skip-ink: autoin Chrome carving gaps in the wavy underline at descenders.Test plan
pnpm --filter @loquix/core build— greenpnpm --filter @loquix/react build— greenpnpm --filter @loquix/core test— 956 tests pass, Chromium + WebKitpnpm lint— clean (only pre-existing warnings)pnpm format:check— clean