From c92cc465dde27446870536e7ef7bfd8da7047266 Mon Sep 17 00:00:00 2001 From: Simon Couch Date: Tue, 25 Aug 2026 08:24:46 -0700 Subject: [PATCH 1/9] replace provenance pills with dots --- pkg-r/R/provenance.R | 6 +- pkg-r/R/trajectory-review.R | 41 ++-- pkg-r/inst/www/commons-chat/commons-chat.css | 184 ++++++++++++------ pkg-r/inst/www/commons-chat/commons-chat.js | 6 +- .../www/commons-viewer/commons-viewer.css | 10 - pkg-r/tests/testthat/test-citation-browser.R | 74 +++++-- pkg-r/tests/testthat/test-trajectory-review.R | 45 ++--- pkg-r/vignettes/commons.Rmd | 20 +- pkg-r/vignettes/commons.css | 8 - 9 files changed, 235 insertions(+), 159 deletions(-) diff --git a/pkg-r/R/provenance.R b/pkg-r/R/provenance.R index 3257f33f..ecb6634c 100644 --- a/pkg-r/R/provenance.R +++ b/pkg-r/R/provenance.R @@ -6,13 +6,13 @@ provenance_display <- list( "This answer comes from a governed calculation defined by", "your data team." ), - pill_class = "trusted" + dot_class = "verified" ), B = list( label = "Cited", icon = NULL, body = "This answer includes supporting text verified against a trusted source.", - pill_class = "cited" + dot_class = NULL ), C = list( label = "Untrusted", @@ -21,7 +21,7 @@ provenance_display <- list( "This answer was not produced by a governed calculation and has", "no verified supporting citation. AI can be wrong." ), - pill_class = "caution" + dot_class = "untrusted" ) ) diff --git a/pkg-r/R/trajectory-review.R b/pkg-r/R/trajectory-review.R index 6b4b4dbb..c594fc70 100644 --- a/pkg-r/R/trajectory-review.R +++ b/pkg-r/R/trajectory-review.R @@ -273,45 +273,32 @@ review_audit_aside <- function(decisions) { ) } -commons_answer_pill <- function(tag) { +commons_answer_dot <- function(tag) { entry <- provenance_display[[tag]] - if (is.null(entry)) { + if (is.null(entry) || is.null(entry$dot_class)) { return(NULL) } - htmltools::tags$span( + htmltools::tags$sup( class = paste0( - "commons-answer-pill commons-answer-pill-", - entry$pill_class + "commons-answer-dot commons-answer-dot-", + entry$dot_class ), title = entry$body, `aria-label` = paste0(entry$label, ". ", entry$body), + role = "img", tabindex = "0", - commons_pill_icon(entry$icon, entry$label), - htmltools::tags$span(entry$label), - commons_pill_tooltip(entry$body) + htmltools::tags$span( + class = "commons-answer-dot-mark", + `aria-hidden` = "true" + ), + commons_dot_tooltip(entry$body) ) } -commons_pill_tooltip <- function(text) { +commons_dot_tooltip <- function(text) { htmltools::tags$span(class = "commons-tooltip", role = "tooltip", text) } -commons_pill_icon <- function(file, alt) { - if (is.null(file)) { - return(NULL) - } - src <- svg_data_uri(file) - if (is.null(src)) { - return(NULL) - } - - htmltools::tags$img( - src = src, - alt = alt, - class = "commons-answer-pill-icon" - ) -} - exchange_answer_chunks <- function(turns, decisions) { chunks <- list() resolver <- recorded_citation_resolver(decisions) @@ -745,7 +732,7 @@ viewer_server <- function( ) }) - # A fresh id prevents stale pill timers from targeting a new transcript. + # A fresh id prevents stale decoration timers from targeting a new transcript. output$transcript <- shiny::renderUI({ key <- selected() if (is.null(key)) { @@ -935,7 +922,7 @@ question_entry <- function(record, selected = NULL, flags = character()) { class = "commons-viewer-entry-meta", flag_marker(flagged), htmltools::tags$span(entry_date(record)), - commons_answer_pill(record$tag) + commons_answer_dot(record$tag) ) ) ) diff --git a/pkg-r/inst/www/commons-chat/commons-chat.css b/pkg-r/inst/www/commons-chat/commons-chat.css index 5b734354..eb020de0 100644 --- a/pkg-r/inst/www/commons-chat/commons-chat.css +++ b/pkg-r/inst/www/commons-chat/commons-chat.css @@ -35,42 +35,43 @@ shiny-chat-container { /* ---- Answer provenance ----------------------------------------------- */ -.commons-answer-pill { +.commons-answer-dot { + --commons-answer-dot-color: currentColor; align-items: center; - border-radius: 999px; + border-radius: 50%; + color: var(--commons-answer-dot-color); display: inline-flex; - font-size: 0.74rem; - font-weight: 400; - gap: 0.3rem; - line-height: 1.2; - max-width: 100%; - padding: 0.18rem 0.45rem; + height: 1rem; + justify-content: center; + line-height: 0; + margin-inline-start: 0.125em; position: relative; - vertical-align: text-bottom; + vertical-align: super; + width: 1rem; } -.commons-answer-pill-icon { - flex: 0 0 auto; - height: 0.82rem; - width: 0.82rem; +.commons-answer-dot:focus-visible { + outline: 0.125rem solid var(--bs-focus-ring-color, rgba(0, 123, 194, 0.5)); + outline-offset: 0; } -.commons-answer-pill-trusted { - background: #f2fbf5; - border: 1px solid #cfeedd; - color: #286144; +.commons-answer-dot-mark { + animation: commons-answer-dot-pulse 1.4s ease-out 1; + background: currentColor; + border: 1px solid rgba(0, 0, 0, 0.18); + border-radius: 50%; + box-sizing: border-box; + display: block; + height: 0.625rem; + width: 0.625rem; } -.commons-answer-pill-cited { - background: #eef7fa; - border: 1px solid #c9e2e9; - color: #285867; +.commons-answer-dot-verified { + --commons-answer-dot-color: #28c840; } -.commons-answer-pill-caution { - background: #fff8ec; - border: 1px solid #f2ddbb; - color: #6b4b1b; +.commons-answer-dot-untrusted { + --commons-answer-dot-color: #febc2e; } /* ---- Provenance tooltips ---------------------------------------------- */ @@ -99,15 +100,15 @@ shiny-chat-container { z-index: 1000; } -.commons-answer-pill:hover > .commons-tooltip, -.commons-answer-pill:focus > .commons-tooltip, -.commons-answer-pill:focus-within > .commons-tooltip { +.commons-answer-dot:hover > .commons-tooltip, +.commons-answer-dot:focus > .commons-tooltip, +.commons-answer-dot:focus-within > .commons-tooltip { display: block; } -.commons-answer-pill:hover::before, -.commons-answer-pill:focus::before, -.commons-answer-pill:focus-within::before { +.commons-answer-dot:hover::before, +.commons-answer-dot:focus::before, +.commons-answer-dot:focus-within::before { background: var(--bs-body-bg, #fff); border-bottom: 1px solid var(--bs-border-color, #dee2e6); border-right: 1px solid var(--bs-border-color, #dee2e6); @@ -128,7 +129,7 @@ shiny-chat-container { top: calc(100% + 0.45rem); } -.commons-answer-pill.commons-tooltip-below::before { +.commons-answer-dot.commons-tooltip-below::before { bottom: auto; top: calc(100% + 0.28rem); transform: translateX(-50%) rotate(225deg); @@ -143,47 +144,96 @@ shiny-chat-container [data-shinychat-aside-display="compact"] { --shiny-chat-aside-marker-font-family: var(--bs-font-monospace); } -/* Temporary bridge until Shinychat provides a public per-aside tone API. */ +/* Temporary bridge until Shinychat provides a public per-aside marker API. */ shiny-chat-container - .shiny-aside-pill:has(img[src$="/trusted-icon.svg"]) { - background: #f2fbf5; - box-shadow: inset 0 0 0 1px #cfeedd; - color: #286144; + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) { + align-items: center; + background: transparent; + border: 0; + border-radius: 50%; + box-shadow: none; + color: transparent; + display: inline-flex; + font-size: 0; + height: 1rem; + justify-content: center; + margin-inline-start: 0.125em; + max-inline-size: none; + min-height: 1rem; + min-inline-size: 1rem; + padding: 0; + vertical-align: super; + width: 1rem; } shiny-chat-container - .shiny-aside-pill:has(img[src$="/trusted-icon.svg"]):hover, -shiny-chat-container - .shiny-aside-pill:has(img[src$="/trusted-icon.svg"]):focus-visible, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + )::after { + animation: commons-answer-dot-pulse 1.4s ease-out 1; + background: var(--commons-answer-dot-color); + border: 1px solid rgba(0, 0, 0, 0.18); + border-radius: 50%; + box-sizing: border-box; + color: var(--commons-answer-dot-color); + content: ""; + height: 0.625rem; + width: 0.625rem; +} + shiny-chat-container - .shiny-aside-pill:has(img[src$="/trusted-icon.svg"])[aria-expanded="true"] { - background: #e4f6ea; - color: #204f38; + .shiny-aside-pill:has(img[src$="/trusted-icon.svg"]) { + --commons-answer-dot-color: #28c840; } shiny-chat-container .shiny-aside-pill:has(img[src$="/warning-icon.svg"]) { - background: #fff8ec; - box-shadow: inset 0 0 0 1px #f2ddbb; - color: #6b4b1b; + --commons-answer-dot-color: #febc2e; } shiny-chat-container - .shiny-aside-pill:has(img[src$="/warning-icon.svg"]):hover, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ):hover, shiny-chat-container - .shiny-aside-pill:has(img[src$="/warning-icon.svg"]):focus-visible, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ):focus-visible, shiny-chat-container - .shiny-aside-pill:has(img[src$="/warning-icon.svg"])[aria-expanded="true"] { - background: #fcefd8; - color: #583b14; + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + )[aria-expanded="true"] { + background: transparent; + color: transparent; } shiny-chat-container .shiny-aside-pill:has( img[src$="/trusted-icon.svg"], img[src$="/warning-icon.svg"] - ) { - font-weight: 400; + ):hover::after, +shiny-chat-container + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + )[aria-expanded="true"]::after { + filter: brightness(0.9); +} + +shiny-chat-container + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ):focus-visible { + outline: 0.125rem solid var(--bs-focus-ring-color, rgba(0, 123, 194, 0.5)); + outline-offset: 0; } shiny-chat-container @@ -191,8 +241,32 @@ shiny-chat-container img[src$="/trusted-icon.svg"], img[src$="/warning-icon.svg"] ) - img { - opacity: 1; + > * { + display: none; +} + +@keyframes commons-answer-dot-pulse { + 0%, + 100% { + box-shadow: 0 0 0 0 transparent; + transform: scale(1); + } + + 35% { + box-shadow: 0 0 0 0.3rem color-mix(in srgb, currentColor 18%, transparent); + transform: scale(1.18); + } +} + +@media (prefers-reduced-motion: reduce) { + .commons-answer-dot-mark, + shiny-chat-container + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + )::after { + animation: none; + } } /* ---- Measure display ------------------------------------------------- */ diff --git a/pkg-r/inst/www/commons-chat/commons-chat.js b/pkg-r/inst/www/commons-chat/commons-chat.js index a0f8a237..ac541561 100644 --- a/pkg-r/inst/www/commons-chat/commons-chat.js +++ b/pkg-r/inst/www/commons-chat/commons-chat.js @@ -5,8 +5,8 @@ return; } - if (window.commonsAnswerPillTooltipInitialized) return; - window.commonsAnswerPillTooltipInitialized = true; + if (window.commonsAnswerDotTooltipInitialized) return; + window.commonsAnswerDotTooltipInitialized = true; // Keep the viewport still when a tool card is expanded or collapsed; // otherwise shinychat's stick-to-bottom scrolling chases the height @@ -96,7 +96,7 @@ var onMarker = function(event) { if (!event.target || !event.target.closest) return; - var marker = event.target.closest(".commons-answer-pill"); + var marker = event.target.closest(".commons-answer-dot"); if (marker) placeTooltip(marker); }; diff --git a/pkg-r/inst/www/commons-viewer/commons-viewer.css b/pkg-r/inst/www/commons-viewer/commons-viewer.css index d445957f..5c4aa486 100644 --- a/pkg-r/inst/www/commons-viewer/commons-viewer.css +++ b/pkg-r/inst/www/commons-viewer/commons-viewer.css @@ -282,16 +282,6 @@ margin-top: 0.125rem; } -.commons-viewer-entry-meta .commons-answer-pill { - font-size: 0.68rem; - padding: 0.12rem 0.4rem; -} - -.commons-viewer-entry-meta .commons-answer-pill-icon { - height: 0.75rem; - width: 0.75rem; -} - .commons-viewer-empty { color: var(--bs-secondary-color, #6c757d); padding: 1rem 0; diff --git a/pkg-r/tests/testthat/test-citation-browser.R b/pkg-r/tests/testthat/test-citation-browser.R index b9026f30..dd4c0a0a 100644 --- a/pkg-r/tests/testthat/test-citation-browser.R +++ b/pkg-r/tests/testthat/test-citation-browser.R @@ -197,7 +197,7 @@ test_that("Shiny Chat renders native numbered streamed citations", { expect_no_match(answer_html, "commons-citation", fixed = TRUE) }) -test_that("Shiny Chat distinguishes verified, cited, and untrusted asides", { +test_that("Shiny Chat renders provenance dots and numbered citations", { skip_on_cran() skip_if_not_installed("shinytest2") skip_if_not_installed("chromote") @@ -228,21 +228,52 @@ test_that("Shiny Chat distinguishes verified, cited, and untrusted asides", { "['Verified answer', 'Untrusted']", ".map((label) => document.querySelector(", "`button[aria-label=\"${label}\"]`", - ").textContent).join('|');" + ").textContent.trim()).join('|');" ) ), "Verified answer|Untrusted" ) + expect_identical( + app$get_js( + paste0( + "(() => {", + "const nodes = ['Verified answer', 'Untrusted'].map((label) => ", + "document.querySelector(`button[aria-label=\"${label}\"]`));", + "return nodes.every((node) => {", + "const marker = getComputedStyle(node);", + "const dot = getComputedStyle(node, '::after');", + "return marker.width === '16px' && marker.height === '16px' && ", + "marker.verticalAlign === 'super' && dot.width === '10px' && ", + "dot.height === '10px' && dot.borderRadius === '50%' && ", + "dot.animationName === 'commons-answer-dot-pulse' && ", + "dot.animationIterationCount === '1';", + "});", + "})()" + ) + ), + TRUE + ) expect_identical( app$get_js( paste0( "['Verified answer', 'Untrusted']", - ".map((label) => getComputedStyle(document.querySelector(", + ".map((label) => getComputedStyle(", + "document.querySelector(`button[aria-label=\"${label}\"]`), ", + "'::after').backgroundColor).join('|');" + ) + ), + "rgb(40, 200, 64)|rgb(254, 188, 46)" + ) + expect_identical( + app$get_js( + paste0( + "['Verified answer', 'Untrusted']", + ".map((label) => document.querySelector(", "`button[aria-label=\"${label}\"]`", - ")).fontWeight).join('|');" + ").closest('p')?.innerText.trim()).join('|');" ) ), - "400|400" + "Governed result.|Fallback result." ) expect_identical( app$get_js( @@ -267,22 +298,6 @@ test_that("Shiny Chat distinguishes verified, cited, and untrusted asides", { ), TRUE ) - expect_identical( - app$get_js( - paste0( - "(() => {", - "const trusted = document.querySelector(", - "'button[aria-label=\"Verified answer\"]');", - "const warning = document.querySelector(", - "'button[aria-label=\"Untrusted\"]');", - "return trusted && warning && ", - "getComputedStyle(trusted).color !== getComputedStyle(warning).color;", - "})()" - ) - ), - TRUE - ) - app$get_js( 'document.querySelector(\'button[aria-label="Verified answer"]\').click();' ) @@ -300,6 +315,23 @@ test_that("Shiny Chat distinguishes verified, cited, and untrusted asides", { "This answer comes from a governed calculation defined by your data team.", fixed = TRUE ) + + app$get_chromote_session()$Emulation$setEmulatedMedia( + features = list(list( + name = "prefers-reduced-motion", + value = "reduce" + )) + ) + expect_identical( + app$get_js( + paste0( + "getComputedStyle(", + "document.querySelector('button[aria-label=\"Untrusted\"]'), ", + "'::after').animationName;" + ) + ), + "none" + ) }) test_that("Shiny Chat preserves Markdown blocks around citations", { diff --git a/pkg-r/tests/testthat/test-trajectory-review.R b/pkg-r/tests/testthat/test-trajectory-review.R index c9df75bc..99c591a7 100644 --- a/pkg-r/tests/testthat/test-trajectory-review.R +++ b/pkg-r/tests/testthat/test-trajectory-review.R @@ -87,30 +87,31 @@ test_that("hit_rate counts exchange tags across conversations", { expect_equal(rate$counts, c(A = 1, B = 1, C = 1, none = 1)) }) -test_that("answer pills describe trusted, cited, and uncited answers", { +test_that("answer dots describe verified and untrusted answers", { skip_if_not_installed("htmltools") - trusted <- htmltools::renderTags(commons_answer_pill("A"))$html - cited <- htmltools::renderTags(commons_answer_pill("B"))$html - uncited <- htmltools::renderTags(commons_answer_pill("C"))$html - - expect_match(trusted, "Verified answer") - expect_match(trusted, "governed calculation") - expect_match(trusted, "commons-tooltip") - expect_match(trusted, "commons-answer-pill-icon") - expect_match(trusted, "commons-answer-pill-trusted") - - expect_match(cited, "Cited") - expect_match(cited, "verified against a trusted source") - expect_match(cited, "commons-tooltip") - expect_match(cited, "commons-answer-pill-cited") - - expect_match(uncited, "Untrusted") - expect_match(uncited, "AI can be wrong") - expect_match(uncited, "not produced by a governed calculation") - expect_match(uncited, "commons-tooltip") - expect_match(uncited, "commons-answer-pill-icon") - expect_match(uncited, "commons-answer-pill-caution") + verified <- htmltools::renderTags(commons_answer_dot("A"))$html + untrusted <- htmltools::renderTags(commons_answer_dot("C"))$html + + expect_match(verified, " How many total animals were observed at Oak Bluff? -The agent then searches for a trusted calculation that can answer the question. If it finds one, it runs that code and then reports the result along with a "Verified answer" tag. +The agent then searches for a trusted calculation that can answer the question. If it finds one, it runs that code and then reports the result with a green verified dot. > At Oak Bluff, 59 individual animals were observed across 5 species, based on 28 hours of survey effort. Note this reflects observed individuals during surveys, not necessarily a full census of every animal present at the site. > -> `r commons:::commons_answer_pill("A")` +> `r commons:::commons_answer_dot("A")` Although the agent had to decide which trusted calculation to run, it did not have to decide _what code to write_, reducing degrees of freedom and allowing it to take advantage of pre-vetted code. @@ -69,7 +69,7 @@ file.copy(skill, ".claude/skills", recursive = TRUE) ## Trust flow -commons agents will use trusted calculations whenever possible. When the user asks a question, the agent first searches the semantic layer for a trusted calculation. If it finds one, it then calls that calculation and the resulting answer is tagged with a green verified answer pill. +commons agents will use trusted calculations whenever possible. When the user asks a question, the agent first searches the semantic layer for a trusted calculation. If it finds one, it then calls that calculation and the resulting answer is marked with a green verified dot. If a relevant trusted calculation is not found, the agent proceeds down the lower trust path. It searches through the context for additional information, then uses that information to write custom SQL or R code to answer the user's question. These answers either include a verified citation in a footnote or are tagged as `Untrusted`. @@ -92,7 +92,7 @@ If a relevant trusted calculation is not found, the agent proceeds down the lowe Run trusted
calculation
-`r commons:::commons_answer_pill("A")` +`r commons:::commons_answer_dot("A")`
@@ -109,7 +109,7 @@ If a relevant trusted calculation is not found, the agent proceeds down the lowe Cited answer1 or -`r commons:::commons_answer_pill("C")` +`r commons:::commons_answer_dot("C")`
@@ -123,11 +123,11 @@ The following table details the various ways each trust outcome can occur: | How the answer is produced | Outcome | |---|---| -| A trusted R [measure](#semantic-layer) | `r commons:::commons_answer_pill("A")` | -| A [data-dictionary metric](#definitions), possibly grouped or filtered with [definitions](#data-dictionaries) | `r commons:::commons_answer_pill("A")` | -| A [Snowflake semantic-view or Databricks metric-view metric](#warehouse-semantic-layers) | `r commons:::commons_answer_pill("A")` | -| Custom SQL, including SQL that uses [data-dictionary definitions](#definitions) | Cited or `r commons:::commons_answer_pill("C")` | -| Custom R | Cited or `r commons:::commons_answer_pill("C")` | +| A trusted R [measure](#semantic-layer) | `r commons:::commons_answer_dot("A")` | +| A [data-dictionary metric](#definitions), possibly grouped or filtered with [definitions](#data-dictionaries) | `r commons:::commons_answer_dot("A")` | +| A [Snowflake semantic-view or Databricks metric-view metric](#warehouse-semantic-layers) | `r commons:::commons_answer_dot("A")` | +| Custom SQL, including SQL that uses [data-dictionary definitions](#definitions) | Cited or `r commons:::commons_answer_dot("C")` | +| Custom R | Cited or `r commons:::commons_answer_dot("C")` | | No data tool used (e.g., because the agent already had sufficient information or the question could not be answered from accessible information) | No label | The agent itself does not tag the answer as verified, cited, or untrusted. This process is instead handled deterministically by the commons package based on the agent's behavior. diff --git a/pkg-r/vignettes/commons.css b/pkg-r/vignettes/commons.css index bab7eb98..c8e0a671 100644 --- a/pkg-r/vignettes/commons.css +++ b/pkg-r/vignettes/commons.css @@ -1,11 +1,3 @@ -.commons-answer-pill-icon { - background: transparent; - border: 0; - border-radius: 0; - margin: 0; - padding: 0; -} - .commons-flow { margin: 1.5rem 0; overflow-x: auto; From 125dd40250c0fd52754405a259c211512fd9d23c Mon Sep 17 00:00:00 2001 From: Simon Couch Date: Tue, 25 Aug 2026 08:37:01 -0700 Subject: [PATCH 2/9] refine provenance dots --- pkg-r/inst/www/commons-chat/commons-chat.css | 10 ++++------ pkg-r/tests/testthat/test-citation-browser.R | 5 +++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/pkg-r/inst/www/commons-chat/commons-chat.css b/pkg-r/inst/www/commons-chat/commons-chat.css index eb020de0..cfbed6e3 100644 --- a/pkg-r/inst/www/commons-chat/commons-chat.css +++ b/pkg-r/inst/www/commons-chat/commons-chat.css @@ -58,12 +58,11 @@ shiny-chat-container { .commons-answer-dot-mark { animation: commons-answer-dot-pulse 1.4s ease-out 1; background: currentColor; - border: 1px solid rgba(0, 0, 0, 0.18); border-radius: 50%; box-sizing: border-box; display: block; - height: 0.625rem; - width: 0.625rem; + height: 0.5rem; + width: 0.5rem; } .commons-answer-dot-verified { @@ -176,13 +175,12 @@ shiny-chat-container )::after { animation: commons-answer-dot-pulse 1.4s ease-out 1; background: var(--commons-answer-dot-color); - border: 1px solid rgba(0, 0, 0, 0.18); border-radius: 50%; box-sizing: border-box; color: var(--commons-answer-dot-color); content: ""; - height: 0.625rem; - width: 0.625rem; + height: 0.5rem; + width: 0.5rem; } shiny-chat-container diff --git a/pkg-r/tests/testthat/test-citation-browser.R b/pkg-r/tests/testthat/test-citation-browser.R index dd4c0a0a..32ade048 100644 --- a/pkg-r/tests/testthat/test-citation-browser.R +++ b/pkg-r/tests/testthat/test-citation-browser.R @@ -243,8 +243,9 @@ test_that("Shiny Chat renders provenance dots and numbered citations", { "const marker = getComputedStyle(node);", "const dot = getComputedStyle(node, '::after');", "return marker.width === '16px' && marker.height === '16px' && ", - "marker.verticalAlign === 'super' && dot.width === '10px' && ", - "dot.height === '10px' && dot.borderRadius === '50%' && ", + "marker.verticalAlign === 'super' && dot.width === '8px' && ", + "dot.height === '8px' && dot.borderRadius === '50%' && ", + "dot.borderStyle === 'none' && ", "dot.animationName === 'commons-answer-dot-pulse' && ", "dot.animationIterationCount === '1';", "});", From 64b60a0414772d0b07bd6173bf97e11eea0e5146 Mon Sep 17 00:00:00 2001 From: Simon Couch Date: Tue, 25 Aug 2026 08:44:23 -0700 Subject: [PATCH 3/9] smooth provenance dot pulse --- pkg-r/inst/www/commons-chat/commons-chat.css | 6 +++--- pkg-r/tests/testthat/test-citation-browser.R | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg-r/inst/www/commons-chat/commons-chat.css b/pkg-r/inst/www/commons-chat/commons-chat.css index cfbed6e3..24e35af2 100644 --- a/pkg-r/inst/www/commons-chat/commons-chat.css +++ b/pkg-r/inst/www/commons-chat/commons-chat.css @@ -56,7 +56,7 @@ shiny-chat-container { } .commons-answer-dot-mark { - animation: commons-answer-dot-pulse 1.4s ease-out 1; + animation: commons-answer-dot-pulse 1.6s ease-in-out 1; background: currentColor; border-radius: 50%; box-sizing: border-box; @@ -173,7 +173,7 @@ shiny-chat-container img[src$="/trusted-icon.svg"], img[src$="/warning-icon.svg"] )::after { - animation: commons-answer-dot-pulse 1.4s ease-out 1; + animation: commons-answer-dot-pulse 1.6s ease-in-out 1; background: var(--commons-answer-dot-color); border-radius: 50%; box-sizing: border-box; @@ -246,7 +246,7 @@ shiny-chat-container @keyframes commons-answer-dot-pulse { 0%, 100% { - box-shadow: 0 0 0 0 transparent; + box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 18%, transparent); transform: scale(1); } diff --git a/pkg-r/tests/testthat/test-citation-browser.R b/pkg-r/tests/testthat/test-citation-browser.R index 32ade048..47821903 100644 --- a/pkg-r/tests/testthat/test-citation-browser.R +++ b/pkg-r/tests/testthat/test-citation-browser.R @@ -247,6 +247,8 @@ test_that("Shiny Chat renders provenance dots and numbered citations", { "dot.height === '8px' && dot.borderRadius === '50%' && ", "dot.borderStyle === 'none' && ", "dot.animationName === 'commons-answer-dot-pulse' && ", + "dot.animationDuration === '1.6s' && ", + "dot.animationTimingFunction === 'ease-in-out' && ", "dot.animationIterationCount === '1';", "});", "})()" From 61f1c51dabb19f2a39bacb60cec23948f881b598 Mon Sep 17 00:00:00 2001 From: Simon Couch Date: Tue, 25 Aug 2026 11:50:47 -0700 Subject: [PATCH 4/9] center and enlarge provenance dots --- pkg-r/inst/www/commons-chat/commons-chat.css | 12 ++++++------ pkg-r/tests/testthat/test-citation-browser.R | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg-r/inst/www/commons-chat/commons-chat.css b/pkg-r/inst/www/commons-chat/commons-chat.css index 24e35af2..e6e6cab2 100644 --- a/pkg-r/inst/www/commons-chat/commons-chat.css +++ b/pkg-r/inst/www/commons-chat/commons-chat.css @@ -46,7 +46,7 @@ shiny-chat-container { line-height: 0; margin-inline-start: 0.125em; position: relative; - vertical-align: super; + vertical-align: middle; width: 1rem; } @@ -61,8 +61,8 @@ shiny-chat-container { border-radius: 50%; box-sizing: border-box; display: block; - height: 0.5rem; - width: 0.5rem; + height: 0.625rem; + width: 0.625rem; } .commons-answer-dot-verified { @@ -164,7 +164,7 @@ shiny-chat-container min-height: 1rem; min-inline-size: 1rem; padding: 0; - vertical-align: super; + vertical-align: middle; width: 1rem; } @@ -179,8 +179,8 @@ shiny-chat-container box-sizing: border-box; color: var(--commons-answer-dot-color); content: ""; - height: 0.5rem; - width: 0.5rem; + height: 0.625rem; + width: 0.625rem; } shiny-chat-container diff --git a/pkg-r/tests/testthat/test-citation-browser.R b/pkg-r/tests/testthat/test-citation-browser.R index 47821903..d266e1ab 100644 --- a/pkg-r/tests/testthat/test-citation-browser.R +++ b/pkg-r/tests/testthat/test-citation-browser.R @@ -243,8 +243,8 @@ test_that("Shiny Chat renders provenance dots and numbered citations", { "const marker = getComputedStyle(node);", "const dot = getComputedStyle(node, '::after');", "return marker.width === '16px' && marker.height === '16px' && ", - "marker.verticalAlign === 'super' && dot.width === '8px' && ", - "dot.height === '8px' && dot.borderRadius === '50%' && ", + "marker.verticalAlign === 'middle' && dot.width === '10px' && ", + "dot.height === '10px' && dot.borderRadius === '50%' && ", "dot.borderStyle === 'none' && ", "dot.animationName === 'commons-answer-dot-pulse' && ", "dot.animationDuration === '1.6s' && ", From 069b6504e8d5cf8453164ccc35fe1fefa0498dee Mon Sep 17 00:00:00 2001 From: Simon Couch Date: Tue, 25 Aug 2026 11:54:40 -0700 Subject: [PATCH 5/9] render citations as provenance dots --- pkg-r/inst/www/commons-chat/commons-chat.css | 95 +++++++++++++------- pkg-r/tests/testthat/test-citation-browser.R | 24 ++++- 2 files changed, 86 insertions(+), 33 deletions(-) diff --git a/pkg-r/inst/www/commons-chat/commons-chat.css b/pkg-r/inst/www/commons-chat/commons-chat.css index e6e6cab2..0a0a5baa 100644 --- a/pkg-r/inst/www/commons-chat/commons-chat.css +++ b/pkg-r/inst/www/commons-chat/commons-chat.css @@ -145,9 +145,12 @@ shiny-chat-container [data-shinychat-aside-display="compact"] { /* Temporary bridge until Shinychat provides a public per-aside marker API. */ shiny-chat-container - .shiny-aside-pill:has( - img[src$="/trusted-icon.svg"], - img[src$="/warning-icon.svg"] + :is( + .shiny-aside-pill--number, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) ) { align-items: center; background: transparent; @@ -169,9 +172,12 @@ shiny-chat-container } shiny-chat-container - .shiny-aside-pill:has( - img[src$="/trusted-icon.svg"], - img[src$="/warning-icon.svg"] + :is( + .shiny-aside-pill--number, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) )::after { animation: commons-answer-dot-pulse 1.6s ease-in-out 1; background: var(--commons-answer-dot-color); @@ -194,50 +200,76 @@ shiny-chat-container } shiny-chat-container - .shiny-aside-pill:has( - img[src$="/trusted-icon.svg"], - img[src$="/warning-icon.svg"] + .shiny-aside-pill--number { + --commons-answer-dot-color: #007bc2; +} + +shiny-chat-container + :is( + .shiny-aside-pill--number, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) ):hover, shiny-chat-container - .shiny-aside-pill:has( - img[src$="/trusted-icon.svg"], - img[src$="/warning-icon.svg"] + :is( + .shiny-aside-pill--number, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) ):focus-visible, shiny-chat-container - .shiny-aside-pill:has( - img[src$="/trusted-icon.svg"], - img[src$="/warning-icon.svg"] + :is( + .shiny-aside-pill--number, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) )[aria-expanded="true"] { background: transparent; color: transparent; } shiny-chat-container - .shiny-aside-pill:has( - img[src$="/trusted-icon.svg"], - img[src$="/warning-icon.svg"] + :is( + .shiny-aside-pill--number, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) ):hover::after, shiny-chat-container - .shiny-aside-pill:has( - img[src$="/trusted-icon.svg"], - img[src$="/warning-icon.svg"] + :is( + .shiny-aside-pill--number, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) )[aria-expanded="true"]::after { filter: brightness(0.9); } shiny-chat-container - .shiny-aside-pill:has( - img[src$="/trusted-icon.svg"], - img[src$="/warning-icon.svg"] + :is( + .shiny-aside-pill--number, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) ):focus-visible { outline: 0.125rem solid var(--bs-focus-ring-color, rgba(0, 123, 194, 0.5)); outline-offset: 0; } shiny-chat-container - .shiny-aside-pill:has( - img[src$="/trusted-icon.svg"], - img[src$="/warning-icon.svg"] + :is( + .shiny-aside-pill--number, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) ) > * { display: none; @@ -259,9 +291,12 @@ shiny-chat-container @media (prefers-reduced-motion: reduce) { .commons-answer-dot-mark, shiny-chat-container - .shiny-aside-pill:has( - img[src$="/trusted-icon.svg"], - img[src$="/warning-icon.svg"] + :is( + .shiny-aside-pill--number, + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) )::after { animation: none; } diff --git a/pkg-r/tests/testthat/test-citation-browser.R b/pkg-r/tests/testthat/test-citation-browser.R index d266e1ab..d4001254 100644 --- a/pkg-r/tests/testthat/test-citation-browser.R +++ b/pkg-r/tests/testthat/test-citation-browser.R @@ -7,7 +7,7 @@ test_that("normal package checks can disable browser tests explicitly", { ) }) -test_that("Shiny Chat renders native numbered streamed citations", { +test_that("Shiny Chat renders streamed citations as provenance dots", { skip_on_cran() skip_if_not_installed("shinytest2") skip_if_not_installed("chromote") @@ -59,6 +59,24 @@ test_that("Shiny Chat renders native numbered streamed citations", { ), "Aside 1: documentation|Aside 2: sales table" ) + expect_identical( + app$get_js( + paste0( + "Array.from(document.querySelectorAll('", + marker_selector, + "')).every((node) => {", + "const marker = getComputedStyle(node);", + "const dot = getComputedStyle(node, '::after');", + "return marker.width === '16px' && marker.height === '16px' && ", + "marker.verticalAlign === 'middle' && marker.fontSize === '0px' && ", + "dot.width === '10px' && dot.height === '10px' && ", + "dot.borderRadius === '50%' && ", + "dot.backgroundColor === 'rgb(0, 123, 194)';", + "});" + ) + ), + TRUE + ) expect_identical( app$get_js( paste0( @@ -88,8 +106,8 @@ test_that("Shiny Chat renders native numbered streamed citations", { ) ), paste( - "Canopy weighting follows the documentation.\n[1]", - "Revenue timing follows the table definition.\n[2]", + "Canopy weighting follows the documentation.", + "Revenue timing follows the table definition.", sep = "|" ) ) From b54b7be6cd86d3d7d8a8c3df93456cbfd632b22e Mon Sep 17 00:00:00 2001 From: Simon Couch Date: Tue, 25 Aug 2026 12:10:47 -0700 Subject: [PATCH 6/9] distinguish provenance marker shapes --- pkg-r/R/trajectory-review.R | 17 ++++-- pkg-r/inst/www/commons-chat/citation-mark.svg | 3 + pkg-r/inst/www/commons-chat/commons-chat.css | 58 +++++++++++++++++-- pkg-r/tests/testthat/test-citation-browser.R | 37 ++++++------ pkg-r/tests/testthat/test-trajectory-review.R | 6 +- 5 files changed, 93 insertions(+), 28 deletions(-) create mode 100644 pkg-r/inst/www/commons-chat/citation-mark.svg diff --git a/pkg-r/R/trajectory-review.R b/pkg-r/R/trajectory-review.R index c594fc70..043aaf78 100644 --- a/pkg-r/R/trajectory-review.R +++ b/pkg-r/R/trajectory-review.R @@ -287,14 +287,23 @@ commons_answer_dot <- function(tag) { `aria-label` = paste0(entry$label, ". ", entry$body), role = "img", tabindex = "0", - htmltools::tags$span( - class = "commons-answer-dot-mark", - `aria-hidden` = "true" - ), + commons_dot_icon(entry$icon), commons_dot_tooltip(entry$body) ) } +commons_dot_icon <- function(file) { + src <- svg_data_uri(file) + if (is.null(src)) { + return(NULL) + } + htmltools::tags$img( + src = src, + alt = "", + class = "commons-answer-dot-mark" + ) +} + commons_dot_tooltip <- function(text) { htmltools::tags$span(class = "commons-tooltip", role = "tooltip", text) } diff --git a/pkg-r/inst/www/commons-chat/citation-mark.svg b/pkg-r/inst/www/commons-chat/citation-mark.svg new file mode 100644 index 00000000..8344c761 --- /dev/null +++ b/pkg-r/inst/www/commons-chat/citation-mark.svg @@ -0,0 +1,3 @@ + + + diff --git a/pkg-r/inst/www/commons-chat/commons-chat.css b/pkg-r/inst/www/commons-chat/commons-chat.css index 0a0a5baa..26c35430 100644 --- a/pkg-r/inst/www/commons-chat/commons-chat.css +++ b/pkg-r/inst/www/commons-chat/commons-chat.css @@ -57,12 +57,9 @@ shiny-chat-container { .commons-answer-dot-mark { animation: commons-answer-dot-pulse 1.6s ease-in-out 1; - background: currentColor; - border-radius: 50%; - box-sizing: border-box; display: block; - height: 0.625rem; - width: 0.625rem; + height: 0.75rem; + width: 0.75rem; } .commons-answer-dot-verified { @@ -204,6 +201,16 @@ shiny-chat-container --commons-answer-dot-color: #007bc2; } +shiny-chat-container + button.shiny-aside-pill.shiny-aside-pill--number::after { + background-image: url("citation-mark.svg"); + background-position: center; + background-repeat: no-repeat; + background-size: 0.625rem; + height: 0.75rem; + width: 0.75rem; +} + shiny-chat-container :is( .shiny-aside-pill--number, @@ -275,6 +282,41 @@ shiny-chat-container display: none; } +shiny-chat-container + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + )::after { + display: none; +} + +shiny-chat-container + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) + > img { + animation: commons-answer-dot-pulse 1.6s ease-in-out 1; + display: block; + height: 0.75rem; + width: 0.75rem; +} + +shiny-chat-container + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ):hover + > img, +shiny-chat-container + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + )[aria-expanded="true"] + > img { + filter: brightness(0.9); +} + @keyframes commons-answer-dot-pulse { 0%, 100% { @@ -290,6 +332,12 @@ shiny-chat-container @media (prefers-reduced-motion: reduce) { .commons-answer-dot-mark, + shiny-chat-container + .shiny-aside-pill:has( + img[src$="/trusted-icon.svg"], + img[src$="/warning-icon.svg"] + ) + > img, shiny-chat-container :is( .shiny-aside-pill--number, diff --git a/pkg-r/tests/testthat/test-citation-browser.R b/pkg-r/tests/testthat/test-citation-browser.R index d4001254..9d84c410 100644 --- a/pkg-r/tests/testthat/test-citation-browser.R +++ b/pkg-r/tests/testthat/test-citation-browser.R @@ -7,7 +7,7 @@ test_that("normal package checks can disable browser tests explicitly", { ) }) -test_that("Shiny Chat renders streamed citations as provenance dots", { +test_that("Shiny Chat renders streamed citation markers", { skip_on_cran() skip_if_not_installed("shinytest2") skip_if_not_installed("chromote") @@ -69,9 +69,10 @@ test_that("Shiny Chat renders streamed citations as provenance dots", { "const dot = getComputedStyle(node, '::after');", "return marker.width === '16px' && marker.height === '16px' && ", "marker.verticalAlign === 'middle' && marker.fontSize === '0px' && ", - "dot.width === '10px' && dot.height === '10px' && ", + "dot.width === '12px' && dot.height === '12px' && ", "dot.borderRadius === '50%' && ", - "dot.backgroundColor === 'rgb(0, 123, 194)';", + "dot.backgroundColor === 'rgb(0, 123, 194)' && ", + "dot.backgroundImage !== 'none';", "});" ) ), @@ -215,7 +216,7 @@ test_that("Shiny Chat renders streamed citations as provenance dots", { expect_no_match(answer_html, "commons-citation", fixed = TRUE) }) -test_that("Shiny Chat renders provenance dots and numbered citations", { +test_that("Shiny Chat renders distinct provenance markers", { skip_on_cran() skip_if_not_installed("shinytest2") skip_if_not_installed("chromote") @@ -259,15 +260,15 @@ test_that("Shiny Chat renders provenance dots and numbered citations", { "document.querySelector(`button[aria-label=\"${label}\"]`));", "return nodes.every((node) => {", "const marker = getComputedStyle(node);", - "const dot = getComputedStyle(node, '::after');", + "const icon = node.querySelector('img');", + "const iconStyle = getComputedStyle(icon);", "return marker.width === '16px' && marker.height === '16px' && ", - "marker.verticalAlign === 'middle' && dot.width === '10px' && ", - "dot.height === '10px' && dot.borderRadius === '50%' && ", - "dot.borderStyle === 'none' && ", - "dot.animationName === 'commons-answer-dot-pulse' && ", - "dot.animationDuration === '1.6s' && ", - "dot.animationTimingFunction === 'ease-in-out' && ", - "dot.animationIterationCount === '1';", + "marker.verticalAlign === 'middle' && iconStyle.width === '12px' && ", + "iconStyle.height === '12px' && iconStyle.display === 'block' && ", + "iconStyle.animationName === 'commons-answer-dot-pulse' && ", + "iconStyle.animationDuration === '1.6s' && ", + "iconStyle.animationTimingFunction === 'ease-in-out' && ", + "iconStyle.animationIterationCount === '1';", "});", "})()" ) @@ -278,12 +279,12 @@ test_that("Shiny Chat renders provenance dots and numbered citations", { app$get_js( paste0( "['Verified answer', 'Untrusted']", - ".map((label) => getComputedStyle(", - "document.querySelector(`button[aria-label=\"${label}\"]`), ", - "'::after').backgroundColor).join('|');" + ".map((label) => document.querySelector(", + "`button[aria-label=\"${label}\"] img`", + ").getAttribute('src').split('/').pop()).join('|');" ) ), - "rgb(40, 200, 64)|rgb(254, 188, 46)" + "trusted-icon.svg|warning-icon.svg" ) expect_identical( app$get_js( @@ -347,8 +348,8 @@ test_that("Shiny Chat renders provenance dots and numbered citations", { app$get_js( paste0( "getComputedStyle(", - "document.querySelector('button[aria-label=\"Untrusted\"]'), ", - "'::after').animationName;" + "document.querySelector('button[aria-label=\"Untrusted\"] img')", + ").animationName;" ) ), "none" diff --git a/pkg-r/tests/testthat/test-trajectory-review.R b/pkg-r/tests/testthat/test-trajectory-review.R index 99c591a7..b8422e34 100644 --- a/pkg-r/tests/testthat/test-trajectory-review.R +++ b/pkg-r/tests/testthat/test-trajectory-review.R @@ -87,7 +87,7 @@ test_that("hit_rate counts exchange tags across conversations", { expect_equal(rate$counts, c(A = 1, B = 1, C = 1, none = 1)) }) -test_that("answer dots describe verified and untrusted answers", { +test_that("answer markers describe verified and untrusted answers", { skip_if_not_installed("htmltools") verified <- htmltools::renderTags(commons_answer_dot("A"))$html @@ -98,6 +98,8 @@ test_that("answer dots describe verified and untrusted answers", { expect_match(verified, "Verified answer") expect_match(verified, "governed calculation") expect_match(verified, "commons-tooltip") + expect_match(verified, " Date: Tue, 25 Aug 2026 12:27:31 -0700 Subject: [PATCH 7/9] refine citation and warning markers --- pkg-r/inst/figs/warning-icon.svg | 4 ++-- pkg-r/inst/www/commons-chat/citation-mark.svg | 4 ++-- pkg-r/inst/www/commons-chat/commons-chat.css | 15 ++++++++++++++- pkg-r/tests/testthat/test-citation-browser.R | 3 ++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/pkg-r/inst/figs/warning-icon.svg b/pkg-r/inst/figs/warning-icon.svg index fa175077..9f25e7fe 100644 --- a/pkg-r/inst/figs/warning-icon.svg +++ b/pkg-r/inst/figs/warning-icon.svg @@ -7,7 +7,7 @@ } .cls-2 { - fill: #965b25; + fill: #fff; } @@ -17,4 +17,4 @@ - \ No newline at end of file + diff --git a/pkg-r/inst/www/commons-chat/citation-mark.svg b/pkg-r/inst/www/commons-chat/citation-mark.svg index 8344c761..c997a8b9 100644 --- a/pkg-r/inst/www/commons-chat/citation-mark.svg +++ b/pkg-r/inst/www/commons-chat/citation-mark.svg @@ -1,3 +1,3 @@ - - + + diff --git a/pkg-r/inst/www/commons-chat/commons-chat.css b/pkg-r/inst/www/commons-chat/commons-chat.css index 26c35430..285fd822 100644 --- a/pkg-r/inst/www/commons-chat/commons-chat.css +++ b/pkg-r/inst/www/commons-chat/commons-chat.css @@ -203,10 +203,12 @@ shiny-chat-container shiny-chat-container button.shiny-aside-pill.shiny-aside-pill--number::after { + animation: commons-citation-mark-pulse 1.6s ease-in-out 1; background-image: url("citation-mark.svg"); background-position: center; background-repeat: no-repeat; - background-size: 0.625rem; + background-size: 0.75rem; + box-shadow: none; height: 0.75rem; width: 0.75rem; } @@ -330,6 +332,17 @@ shiny-chat-container } } +@keyframes commons-citation-mark-pulse { + 0%, + 100% { + transform: scale(1); + } + + 35% { + transform: scale(1.18); + } +} + @media (prefers-reduced-motion: reduce) { .commons-answer-dot-mark, shiny-chat-container diff --git a/pkg-r/tests/testthat/test-citation-browser.R b/pkg-r/tests/testthat/test-citation-browser.R index 9d84c410..cbd50120 100644 --- a/pkg-r/tests/testthat/test-citation-browser.R +++ b/pkg-r/tests/testthat/test-citation-browser.R @@ -72,7 +72,8 @@ test_that("Shiny Chat renders streamed citation markers", { "dot.width === '12px' && dot.height === '12px' && ", "dot.borderRadius === '50%' && ", "dot.backgroundColor === 'rgb(0, 123, 194)' && ", - "dot.backgroundImage !== 'none';", + "dot.backgroundImage !== 'none' && dot.boxShadow === 'none' && ", + "dot.animationName === 'commons-citation-mark-pulse';", "});" ) ), From 94acc7492b34ae38f646269a2346d23ec3be654a Mon Sep 17 00:00:00 2001 From: Simon Couch Date: Tue, 25 Aug 2026 12:34:56 -0700 Subject: [PATCH 8/9] enlarge rounded warning marker --- pkg-r/inst/figs/warning-icon.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg-r/inst/figs/warning-icon.svg b/pkg-r/inst/figs/warning-icon.svg index 9f25e7fe..64e9ca4f 100644 --- a/pkg-r/inst/figs/warning-icon.svg +++ b/pkg-r/inst/figs/warning-icon.svg @@ -12,7 +12,7 @@ - + From a7b415c0b5af602493d1fbe43bbb3c3f7387e1ec Mon Sep 17 00:00:00 2001 From: Simon Couch Date: Tue, 25 Aug 2026 13:13:37 -0700 Subject: [PATCH 9/9] soften provenance marker shapes --- pkg-r/inst/figs/warning-icon.svg | 2 +- pkg-r/inst/www/commons-chat/commons-chat.css | 1 + pkg-r/tests/testthat/test-citation-browser.R | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg-r/inst/figs/warning-icon.svg b/pkg-r/inst/figs/warning-icon.svg index 64e9ca4f..9f25e7fe 100644 --- a/pkg-r/inst/figs/warning-icon.svg +++ b/pkg-r/inst/figs/warning-icon.svg @@ -12,7 +12,7 @@ - + diff --git a/pkg-r/inst/www/commons-chat/commons-chat.css b/pkg-r/inst/www/commons-chat/commons-chat.css index 285fd822..c2874ca1 100644 --- a/pkg-r/inst/www/commons-chat/commons-chat.css +++ b/pkg-r/inst/www/commons-chat/commons-chat.css @@ -208,6 +208,7 @@ shiny-chat-container background-position: center; background-repeat: no-repeat; background-size: 0.75rem; + border-radius: 0.1875rem; box-shadow: none; height: 0.75rem; width: 0.75rem; diff --git a/pkg-r/tests/testthat/test-citation-browser.R b/pkg-r/tests/testthat/test-citation-browser.R index cbd50120..aaf774f6 100644 --- a/pkg-r/tests/testthat/test-citation-browser.R +++ b/pkg-r/tests/testthat/test-citation-browser.R @@ -70,7 +70,7 @@ test_that("Shiny Chat renders streamed citation markers", { "return marker.width === '16px' && marker.height === '16px' && ", "marker.verticalAlign === 'middle' && marker.fontSize === '0px' && ", "dot.width === '12px' && dot.height === '12px' && ", - "dot.borderRadius === '50%' && ", + "dot.borderRadius === '3px' && ", "dot.backgroundColor === 'rgb(0, 123, 194)' && ", "dot.backgroundImage !== 'none' && dot.boxShadow === 'none' && ", "dot.animationName === 'commons-citation-mark-pulse';",