mirror of
https://github.com/discourse/discourse-ai.git
synced 2025-07-08 23:32:45 +00:00
Previously we attempted to add a diff streaming animation to the AI composer helper: https://github.com/discourse/discourse-ai/pull/1332, but it resulted in issues despite attempted fixes (https://github.com/discourse/discourse-ai/pull/1338) so we temporarily suppressed the diff animation (https://github.com/discourse/discourse-ai/pull/1341). This update makes a second attempt at implementing the diff streaming animation. Instead of creating a custom diff algorithm, we make use of a third-party library [`jsDiff`](https://github.com/kpdecker/jsdiff) (which we added to core here: https://github.com/discourse/discourse/pull/32833). While streaming, the diff animation often struggles with markdown links and images, so we make use of `markdown-it` parser to detect those cases and prevent breaking the animation. --------- Co-authored-by: Sam Saffron <sam.saffron@gmail.com>
131 lines
2.1 KiB
SCSS
131 lines
2.1 KiB
SCSS
@keyframes flashing {
|
|
0%,
|
|
100% {
|
|
opacity: 0;
|
|
}
|
|
|
|
50% {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@mixin progress-dot {
|
|
content: "\25CF";
|
|
font-family:
|
|
"Söhne Circle",
|
|
system-ui,
|
|
-apple-system,
|
|
"Segoe UI",
|
|
Roboto,
|
|
Ubuntu,
|
|
Cantarell,
|
|
"Noto Sans",
|
|
sans-serif;
|
|
line-height: normal;
|
|
margin-left: 0.25rem;
|
|
vertical-align: baseline;
|
|
animation: flashing 1.5s 3s infinite;
|
|
display: inline-block;
|
|
font-size: 1rem;
|
|
color: var(--tertiary-medium);
|
|
}
|
|
|
|
.streamable-content.streaming .cooked p:last-child::after {
|
|
@include progress-dot;
|
|
}
|
|
|
|
article.streaming .cooked {
|
|
.progress-dot::after {
|
|
@include progress-dot;
|
|
}
|
|
|
|
> .progress-dot:only-child::after {
|
|
// if the progress dot is the only content
|
|
// we are likely waiting longer for a response
|
|
// so it can start animating instantly
|
|
animation: flashing 1.5s infinite;
|
|
}
|
|
}
|
|
|
|
@keyframes ai-indicator-wave {
|
|
0%,
|
|
60%,
|
|
100% {
|
|
transform: initial;
|
|
}
|
|
|
|
30% {
|
|
transform: translateY(-0.2em);
|
|
}
|
|
}
|
|
|
|
.ai-indicator-wave {
|
|
flex: 0 0 auto;
|
|
display: inline-flex;
|
|
|
|
&__dot {
|
|
display: inline-block;
|
|
|
|
@media (prefers-reduced-motion: no-preference) {
|
|
animation: ai-indicator-wave 1.8s linear infinite;
|
|
}
|
|
|
|
&:nth-child(2) {
|
|
animation-delay: -1.6s;
|
|
}
|
|
|
|
&:nth-child(3) {
|
|
animation-delay: -1.4s;
|
|
}
|
|
}
|
|
}
|
|
|
|
@keyframes mark-blink {
|
|
0%,
|
|
100% {
|
|
border-color: transparent;
|
|
}
|
|
|
|
50% {
|
|
border-color: var(--highlight-high);
|
|
}
|
|
}
|
|
|
|
@keyframes fade-in-highlight {
|
|
from {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
mark.highlight {
|
|
background-color: var(--highlight-high);
|
|
animation: fade-in-highlight 0.5s ease-in-out forwards;
|
|
}
|
|
|
|
.composer-ai-helper-modal__suggestion.thinking mark.highlight {
|
|
animation: mark-blink 1s step-start 0s infinite;
|
|
animation-name: mark-blink;
|
|
}
|
|
|
|
.composer-ai-helper-modal__loading {
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.composer-ai-helper-modal__suggestion.inline-diff {
|
|
white-space: pre-wrap;
|
|
|
|
del:last-child {
|
|
text-decoration: none;
|
|
background-color: transparent;
|
|
color: var(--primary-low-mid);
|
|
}
|
|
|
|
.diff-inner {
|
|
display: inline;
|
|
}
|
|
}
|