From 38f7e9c2c4828244c43e088b28a073d7a64885ae Mon Sep 17 00:00:00 2001 From: Keegan George Date: Fri, 30 May 2025 10:35:53 -0700 Subject: [PATCH] UX: AI composer helper refinements (#1387) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This update includes a variety of small refinements to the AI composer helper: - prevent height jump when going from loading text placeholder → proofreading text streaming - update padding on AI helper options list to be more suitable with typical Discourse menu design - for composer helper results that are not `showResultAsDiff` (i.e. translation): - update before/after diff design to be more subtle - results should be in normal font (as the text is cooked and not raw markdown) - fix: smooth streaming animation stuck showing dot icon even after smooth streaming is done --- .../discourse/components/modal/diff-modal.gjs | 76 +++++++++++-------- assets/stylesheets/common/streaming.scss | 2 +- .../modules/ai-helper/common/ai-helper.scss | 14 ++-- 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/assets/javascripts/discourse/components/modal/diff-modal.gjs b/assets/javascripts/discourse/components/modal/diff-modal.gjs index a7e46523..dc29b7ed 100644 --- a/assets/javascripts/discourse/components/modal/diff-modal.gjs +++ b/assets/javascripts/discourse/components/modal/diff-modal.gjs @@ -5,6 +5,7 @@ import didInsert from "@ember/render-modifiers/modifiers/did-insert"; import willDestroy from "@ember/render-modifiers/modifiers/will-destroy"; import { service } from "@ember/service"; import { htmlSafe } from "@ember/template"; +import { or } from "truth-helpers"; import CookText from "discourse/components/cook-text"; import DButton from "discourse/components/d-button"; import DModal from "discourse/components/d-modal"; @@ -41,6 +42,10 @@ export default class ModalDiffModal extends Component { } get diffResult() { + if (this.loading) { + return this.escapedSelectedText; + } + if (this.diffStreamer.diff?.length > 0) { return this.diffStreamer.diff; } @@ -50,10 +55,22 @@ export default class ModalDiffModal extends Component { return this.escapedSelectedText; } + get smoothStreamerResult() { + if (this.loading) { + return this.escapedSelectedText; + } + + return this.smoothStreamer.renderedText; + } + get isStreaming() { // diffStreamer stops Streaming when it is finished with a chunk, looking at isDone is safe // it starts off not done - return !this.diffStreamer.isDone || this.smoothStreamer.isStreaming; + if (this.args.model.showResultAsDiff) { + return !this.diffStreamer.isDone; + } + + return this.smoothStreamer.isStreaming; } get primaryBtnLabel() { @@ -154,42 +171,37 @@ export default class ModalDiffModal extends Component { {{willDestroy this.cleanup}} class="text-preview" > - {{#if this.loading}} -
- {{~@model.selectedText~}} -
- {{else}} -
- {{~#if @model.showResultAsDiff~}} - {{htmlSafe this.diffResult}} +
+ {{~#if @model.showResultAsDiff~}} + {{htmlSafe this.diffResult}} + {{else}} + {{#if (or this.loading this.smoothStreamer.isStreaming)}} + {{else}} - {{#if this.smoothStreamer.isStreaming}} +
+ {{~this.escapedSelectedText~}} +
+
- {{else}} -
- {{@model.selectedText}} -
-
- -
- {{/if}} +
{{/if}} -
- {{/if}} + {{/if}} +
diff --git a/assets/stylesheets/common/streaming.scss b/assets/stylesheets/common/streaming.scss index 8315eaa0..bbeb720f 100644 --- a/assets/stylesheets/common/streaming.scss +++ b/assets/stylesheets/common/streaming.scss @@ -111,7 +111,7 @@ mark.highlight { animation-name: mark-blink; } -.composer-ai-helper-modal__loading { +.composer-ai-helper-modal__loading.inline-diff { white-space: pre-wrap; } diff --git a/assets/stylesheets/modules/ai-helper/common/ai-helper.scss b/assets/stylesheets/modules/ai-helper/common/ai-helper.scss index d17d592b..20ef5909 100644 --- a/assets/stylesheets/modules/ai-helper/common/ai-helper.scss +++ b/assets/stylesheets/modules/ai-helper/common/ai-helper.scss @@ -1,11 +1,13 @@ @use "lib/viewport"; .composer-ai-helper-modal { - .text-preview, .inline-diff { font-family: var(--d-font-family--monospace); font-variant-ligatures: none; + } + .text-preview, + .inline-diff { ins { background-color: var(--success-low); text-decoration: none; @@ -55,13 +57,16 @@ } &__old-value { - background-color: var(--danger-low); + white-space: pre-wrap; + border-left: 2px solid var(--danger); + padding-left: 1rem; color: var(--danger); margin-bottom: 1rem; } &__new-value { - background-color: var(--success-low); + border-left: 2px solid var(--success); + padding-left: 1rem; color: var(--success); } @@ -77,7 +82,6 @@ } .ai-composer-helper-menu { - padding: 0.25rem; max-width: 25rem; list-style: none; @@ -701,7 +705,7 @@ width: 100%; border-radius: 0; margin: 0; - padding: 0.5em 1rem; + padding: 0.7rem 1rem; &:focus, &:hover {