Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pkg-r/R/chat.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
#' @section Citations and provenance:
#' The server verifies each `<commons-citation>` the model writes against its
#' own context, measure definitions, and data documentation as the answer
#' streams, and rewrites verified citations inline as numbered,
#' server-authored `<shiny-aside>` elements. Citation details name the
#' trusted source. A provenance marker in a compact `<shiny-aside>` follows
#' streams, and rewrites verified citations inline as server-authored
#' `<shiny-aside>` elements. Citation details name the
#' trusted source. A provenance marker `<shiny-aside>` follows
#' the answer when it was produced by a governed calculation, or when a
#' fallback answer cites nothing verified.
#'
Expand Down
30 changes: 26 additions & 4 deletions pkg-r/R/citations.R
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,36 @@ render_citation_aside <- function(quote, explanation, corpus) {
}

citation_aside_html <- function(quote, explanation, label, kind) {
icon <- citation_icon_url(kind)
# The pill renders the uniform quote mark; the per-kind icon goes in
# the body title (commons-chat.css hides shinychat's popover title row,
# so the aside's icon attribute is only a styling hook for the pill).
icon <- commons_icon_url("citation-mark.svg")
kind_icon <- citation_icon_url(kind)
reason <- if (nzchar(explanation)) paste0(explanation, "\n\n") else ""
blockquote <- paste0("> ", gsub("\n", "\n> ", trimws(quote), fixed = TRUE))
# shinychat only renders the popover's title row for grouped asides, so
# the body carries its own title (icon + label) to keep the source named
# for singleton citations; commons-chat.css hides shinychat's row.
title <- sprintf(
paste0(
'<span class="commons-citation-title">',
'%s<span class="commons-citation-title-label">%s</span></span>\n\n'
),
if (is.null(kind_icon)) {
""
} else {
sprintf('<img src="%s" alt="">', escape_attr(kind_icon))
},
htmltools::htmlEscape(label)
)
sprintf(
paste0(
'<shiny-aside display="compact" label="%s"%s>',
"%s%s</shiny-aside>"
'<shiny-aside label="%s"%s>',
"%s%s%s</shiny-aside>"
),
escape_attr(label),
if (is.null(icon)) "" else sprintf(' icon="%s"', escape_attr(icon)),
title,
reason,
blockquote
)
Expand Down Expand Up @@ -227,7 +247,9 @@ non_citable_tool_output_text <- function(tools) {
paste(items, collapse = "\n")
}

# These SVGs need a fixed stroke because images cannot inherit currentColor.
# The per-kind icon appears in the aside body's title; the pill renders
# the uniform citation-mark.svg quote mark instead. These SVGs need a
# fixed stroke because images cannot inherit currentColor.
citation_icon_url <- function(kind) {
file <- switch(
kind,
Expand Down
151 changes: 118 additions & 33 deletions pkg-r/inst/www/commons-chat/commons-chat.css
Original file line number Diff line number Diff line change
Expand Up @@ -130,67 +130,152 @@ shiny-chat-container .shiny-chat-input .tiptap {
transform: translateX(-50%) rotate(225deg);
}

/* Shinychat owns compact marker structure and placement through this public API. */
shiny-chat-container [data-shinychat-aside-display="compact"] {
--shiny-chat-aside-marker-color: var(--bs-link-color);
--shiny-chat-aside-marker-hover-color: var(--bs-link-hover-color);
/* Citations are labeled identity asides: shinychat accumulates those
* sharing a paragraph into one pill with a "+N" overflow and a carousel
* popover. They render icon-only like the provenance markers, in a quieter
* mid navy — verified support, short of a governed calculation. */
shiny-chat-container
.shiny-aside-pill:has(img[src$="/citation-mark.svg"]) {
--shiny-chat-aside-marker-color: #2e4a6b;
--shiny-chat-aside-marker-bg: transparent;
--shiny-chat-aside-marker-hover-bg: transparent;
--shiny-chat-aside-marker-font-family: var(--bs-font-monospace);
--shiny-chat-aside-marker-hover-color: #223851;
--shiny-chat-aside-marker-hover-bg: #e7edf4;
}

/* Temporary bridge until Shinychat provides a public per-aside tone API. */
/* Temporary bridge until shinychat forwards a per-aside tone hook to the
* pill: the icon src is the only per-aside signal that survives to the
* rendered button, so it selects the tone — but styling goes only through
* shinychat's public --shiny-chat-aside-marker-* variables, so its own
* hover, focus, expanded, and compact-number rules keep working. */
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-chat-aside-marker-color: #286144;
--shiny-chat-aside-marker-bg: transparent;
--shiny-chat-aside-marker-hover-color: #204f38;
--shiny-chat-aside-marker-hover-bg: #e4f6ea;
}

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-chat-container
.shiny-aside-pill:has(img[src$="/trusted-icon.svg"])[aria-expanded="true"] {
background: #e4f6ea;
color: #204f38;
.shiny-aside-pill:has(img[src$="/warning-icon.svg"]) {
--shiny-chat-aside-marker-color: #6b4b1b;
--shiny-chat-aside-marker-bg: transparent;
--shiny-chat-aside-marker-hover-color: #583b14;
--shiny-chat-aside-marker-hover-bg: #fcefd8;
}

/* Provenance and citation markers render as bare icons: the label stays in
* the DOM (the button's aria-label announces it) but doesn't render, and a
* grouped "+N" overflow count still shows. */
shiny-chat-container
.shiny-aside-pill:has(img[src$="/warning-icon.svg"]) {
background: #fff8ec;
box-shadow: inset 0 0 0 1px #f2ddbb;
color: #6b4b1b;
.shiny-aside-pill:has(
:is(
img[src$="/trusted-icon.svg"],
img[src$="/warning-icon.svg"],
img[src$="/citation-mark.svg"]
)
) {
border-radius: 50%;
font-weight: 400;
gap: 0.15em;
padding: 0.2em;
}

/* A grouped marker carries a "+N more" count, which widens the pill: give
* it a stadium shape and roomier padding instead of the bare-icon circle. */
shiny-chat-container
.shiny-aside-pill:has(img[src$="/warning-icon.svg"]):hover,
shiny-chat-container
.shiny-aside-pill:has(img[src$="/warning-icon.svg"]):focus-visible,
.shiny-aside-pill:has(
:is(
img[src$="/trusted-icon.svg"],
img[src$="/warning-icon.svg"],
img[src$="/citation-mark.svg"]
)
):has(.shiny-aside-pill__overflow) {
border-radius: 999px;
gap: 0.3em;
padding: 0.2em 0.55em 0.2em 0.25em;
}

shiny-chat-container
.shiny-aside-pill:has(img[src$="/warning-icon.svg"])[aria-expanded="true"] {
background: #fcefd8;
color: #583b14;
.shiny-aside-pill:has(
:is(
img[src$="/trusted-icon.svg"],
img[src$="/warning-icon.svg"],
img[src$="/citation-mark.svg"]
)
)
.shiny-aside-pill__label {
display: none;
}

shiny-chat-container
.shiny-aside-pill:has(
img[src$="/trusted-icon.svg"],
img[src$="/warning-icon.svg"]
) {
font-weight: 400;
:is(
img[src$="/trusted-icon.svg"],
img[src$="/warning-icon.svg"],
img[src$="/citation-mark.svg"]
)
)
img {
height: 1.5em;
width: 1.5em;
}

shiny-chat-container
.shiny-aside-pill:has(
img[src$="/trusted-icon.svg"],
img[src$="/warning-icon.svg"]
:is(img[src$="/trusted-icon.svg"], img[src$="/warning-icon.svg"])
)
img {
animation: commons-provenance-marker-pulse 1.6s ease-in-out 1;
opacity: 1;
}

@keyframes commons-provenance-marker-pulse {
0% {
transform: scale(1);
}
35% {
transform: scale(1.4);
}
70% {
transform: scale(0.92);
}
100% {
transform: scale(1);
}
}

@media (prefers-reduced-motion: reduce) {
shiny-chat-container
.shiny-aside-pill:has(
:is(img[src$="/trusted-icon.svg"], img[src$="/warning-icon.svg"])
)
img {
animation: none;
}
}

/* The citation aside body carries its own title (icon + label) because
* shinychat only renders its popover title row for grouped asides; hide
* shinychat's row so grouped popovers don't name the source twice. The
* popover is portaled to <body>, so these rules must stay unscoped. */
.commons-citation-title {
align-items: center;
display: flex;
font-weight: 600;
gap: 0.3em;
margin-bottom: 0.25rem;
}

.commons-citation-title img {
height: 1.1em;
width: 1.1em;
}

.shiny-aside-popover:has(.commons-citation-title)
.shiny-aside-popover__label {
display: none;
}

/* ---- Measure display ------------------------------------------------- */

.commons-measure-display {
Expand Down
6 changes: 3 additions & 3 deletions pkg-r/inst/www/commons-chat/figs/citation-definition.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions pkg-r/inst/www/commons-chat/figs/citation-mark.svg

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only thought when using this was the proportion of whitespace inside of this icon is a good bit higher than the other two icons. Could we shrink the quotation marks slightly?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a slightly lighter blue, as well? It's giving Merriam-Webster🤪

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shrunk the glyph to match the others' proportions in c8d1ded.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lightened to #55729e in c8d1ded — also softened the warning yellow while I was at it.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 3 additions & 4 deletions pkg-r/inst/www/commons-chat/figs/citation-prose.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions pkg-r/inst/www/commons-chat/figs/citation-schema.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 5 additions & 19 deletions pkg-r/inst/www/commons-chat/figs/trusted-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 0 additions & 20 deletions pkg-r/inst/www/commons-chat/figs/untrusted-icon.svg

This file was deleted.

25 changes: 6 additions & 19 deletions pkg-r/inst/www/commons-chat/figs/warning-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading