DEV: Add review menu state (#159)

This commit is contained in:
Keegan George 2023-08-24 17:49:24 -07:00 committed by GitHub
parent 65c6b5e16c
commit 7790313b1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 242 additions and 9 deletions

View File

@ -0,0 +1,48 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal";
import DModalCancel from "discourse/components/d-modal-cancel";
import I18n from "I18n";
import { htmlSafe } from "@ember/template";
const t = I18n.t.bind(I18n);
export default class ModalDiffModal extends Component {
<template>
<DModal
class="composer-ai-helper-modal"
@title={{t "discourse_ai.ai_helper.context_menu.changes"}}
@closeModal={{@closeModal}}
>
<:body>
{{#if @diff}}
{{htmlSafe @diff}}
{{else}}
<div class="composer-ai-helper-modal__old-value">
{{@oldValue}}
</div>
<div class="composer-ai-helper-modal__new-value">
{{@newValue}}
</div>
{{/if}}
</:body>
<:footer>
<DButton
class="btn-primary confirm"
@action={{this.triggerConfirmChanges}}
@label="discourse_ai.ai_helper.context_menu.confirm"
/>
<DModalCancel @close={{@closeModal}} />
</:footer>
</DModal>
</template>
@action
triggerConfirmChanges() {
this.args.closeModal();
this.args.confirm();
}
}

View File

@ -51,6 +51,34 @@
</li>
</ul>
{{else if (eq this.menuState this.CONTEXT_MENU_STATES.review)}}
<ul class="ai-helper-context-menu__review">
<li>
<DButton
@icon="exchange-alt"
@label="discourse_ai.ai_helper.context_menu.view_changes"
@action={{this.viewChanges}}
class="btn-flat view-changes"
/>
</li>
<li>
<DButton
@icon="undo"
@label="discourse_ai.ai_helper.context_menu.revert"
@action={{this.undoAIAction}}
class="btn-flat revert"
/>
</li>
<li>
<DButton
@icon="check"
@label="discourse_ai.ai_helper.context_menu.confirm"
@action={{this.confirmChanges}}
class="btn-flat confirm"
/>
</li>
</ul>
{{else if (eq this.menuState this.CONTEXT_MENU_STATES.resets)}}
<ul class="ai-helper-context-menu__resets">
<li>
@ -76,3 +104,13 @@
</div>
{{/if}}
</div>
{{#if this.showDiffModal}}
<Modal::DiffModal
@confirm={{this.confirmChanges}}
@diff={{this.diff}}
@oldValue={{this.selectedText}}
@newValue={{this.newSelectedText}}
@closeModal={{fn (mut this.showDiffModal) false}}
/>
{{/if}}

View File

@ -38,10 +38,14 @@ export default class AiHelperContextMenu extends Component {
@tracked caretCoords;
@tracked virtualElement;
@tracked selectedText = "";
@tracked newSelectedText;
@tracked loading = false;
@tracked oldEditorValue;
@tracked newEditorValue;
@tracked generatedTitleSuggestions = [];
@tracked lastUsedOption = null;
@tracked showDiffModal = false;
@tracked diff;
CONTEXT_MENU_STATES = {
triggers: "TRIGGERS",
@ -49,6 +53,7 @@ export default class AiHelperContextMenu extends Component {
resets: "RESETS",
loading: "LOADING",
suggesions: "SUGGESTIONS",
review: "REVIEW",
};
prompts = [];
promptTypes = {};
@ -104,8 +109,9 @@ export default class AiHelperContextMenu extends Component {
: "";
if (this.selectedText.length === 0) {
if (this.loading) {
// prevent accidentally closing context menu while results loading
if (this.loading || this.menuState === this.CONTEXT_MENU_STATES.review) {
// prevent accidentally closing context menu
// while results loading or in review state
return;
}
@ -163,12 +169,18 @@ export default class AiHelperContextMenu extends Component {
_updateSuggestedByAI(data) {
const composer = this.args.outletArgs.composer;
this.oldEditorValue = this._dEditorInput.value;
const newValue = this.oldEditorValue.replace(
this.newSelectedText = data.suggestions[0];
this.newEditorValue = this.oldEditorValue.replace(
this.selectedText,
data.suggestions[0]
this.newSelectedText
);
composer.set("reply", newValue);
this.menuState = this.CONTEXT_MENU_STATES.resets;
if (data.diff) {
this.diff = data.diff;
}
composer.set("reply", this.newEditorValue);
this.menuState = this.CONTEXT_MENU_STATES.review;
}
@afterRender
@ -240,6 +252,10 @@ export default class AiHelperContextMenu extends Component {
data: { mode: option, text: this.selectedText },
})
.then((data) => {
// resets the values if new suggestion is started:
this.diff = null;
this.newSelectedText = null;
if (this.prompts[option].name === "generate_titles") {
this.menuState = this.CONTEXT_MENU_STATES.suggestions;
this.generatedTitleSuggestions = data.suggestions;
@ -263,4 +279,14 @@ export default class AiHelperContextMenu extends Component {
this.closeContextMenu();
}
}
@action
viewChanges() {
this.showDiffModal = true;
}
@action
confirmChanges() {
this.menuState = this.CONTEXT_MENU_STATES.resets;
}
}

View File

@ -5,7 +5,8 @@
margin: 10px 0 10px 0;
}
.text-preview {
.text-preview,
.inline-diff {
ins {
background-color: var(--success-low);
text-decoration: underline;
@ -20,6 +21,17 @@
}
}
&__old-value {
background-color: var(--danger-low);
color: var(--danger);
margin-bottom: 1rem;
}
&__new-value {
background-color: var(--success-low);
color: var(--success);
}
.selection-hint {
font-size: var(--font-down-2);
margin-bottom: 20px;
@ -34,7 +46,7 @@
background: var(--secondary);
box-shadow: var(--shadow-dropdown);
padding: 0.25rem;
max-width: 15rem;
max-width: 25rem;
border: 1px solid var(--primary-low);
list-style: none;
z-index: 999;
@ -79,6 +91,12 @@
align-items: center;
flex-flow: row wrap;
}
&__review {
display: flex;
align-items: center;
flex-flow: row wrap;
}
}
.d-editor-input.loading {

View File

@ -22,6 +22,10 @@ en:
loading: "AI is generating"
cancel: "Cancel"
regen: "Try Again"
view_changes: "View Changes"
confirm: "Confirm"
revert: "Revert"
changes: "Changes"
reviewables:
model_used: "Model used:"
accuracy: "Accuracy:"

View File

@ -14,6 +14,7 @@ RSpec.describe "AI Composer helper", type: :system, js: true do
let(:composer) { PageObjects::Components::Composer.new }
let(:ai_helper_context_menu) { PageObjects::Components::AIHelperContextMenu.new }
let(:ai_helper_modal) { PageObjects::Modals::AiHelper.new }
let(:diff_modal) { PageObjects::Modals::DiffModal.new }
context "when using the translation mode" do
let(:mode) { OpenAiCompletionsInferenceStubs::TRANSLATE }
@ -140,6 +141,7 @@ RSpec.describe "AI Composer helper", type: :system, js: true do
composer.composer_input.value == OpenAiCompletionsInferenceStubs.translated_response.strip
end
ai_helper_context_menu.click_confirm_button
expect(ai_helper_context_menu).to be_showing_resets
end
@ -154,10 +156,26 @@ RSpec.describe "AI Composer helper", type: :system, js: true do
composer.composer_input.value == OpenAiCompletionsInferenceStubs.translated_response.strip
end
ai_helper_context_menu.click_confirm_button
ai_helper_context_menu.click_undo_button
expect(composer.composer_input.value).to eq(OpenAiCompletionsInferenceStubs.spanish_text)
end
it "reverts results when revert button is clicked" do
trigger_context_menu(OpenAiCompletionsInferenceStubs.spanish_text)
ai_helper_context_menu.click_ai_button
ai_helper_context_menu.select_helper_model(
OpenAiCompletionsInferenceStubs.text_mode_to_id(mode),
)
wait_for do
composer.composer_input.value == OpenAiCompletionsInferenceStubs.translated_response.strip
end
ai_helper_context_menu.click_revert_button
expect(composer.composer_input.value).to eq(OpenAiCompletionsInferenceStubs.spanish_text)
end
it "reverts results when Ctrl/Cmd + Z is pressed on the keyboard" do
trigger_context_menu(OpenAiCompletionsInferenceStubs.spanish_text)
ai_helper_context_menu.click_ai_button
@ -173,12 +191,57 @@ RSpec.describe "AI Composer helper", type: :system, js: true do
expect(composer.composer_input.value).to eq(OpenAiCompletionsInferenceStubs.spanish_text)
end
it "confirms the results when confirm button is pressed" do
trigger_context_menu(OpenAiCompletionsInferenceStubs.spanish_text)
ai_helper_context_menu.click_ai_button
ai_helper_context_menu.select_helper_model(
OpenAiCompletionsInferenceStubs.text_mode_to_id(mode),
)
wait_for do
composer.composer_input.value == OpenAiCompletionsInferenceStubs.translated_response.strip
end
ai_helper_context_menu.click_confirm_button
expect(composer.composer_input.value).to eq(
OpenAiCompletionsInferenceStubs.translated_response.strip,
)
end
it "hides the context menu when pressing Escape on the keyboard" do
trigger_context_menu(OpenAiCompletionsInferenceStubs.spanish_text)
ai_helper_context_menu.click_ai_button
ai_helper_context_menu.press_escape_key
expect(ai_helper_context_menu).to have_no_context_menu
end
it "shows the changes in a modal when view changes button is pressed" do
trigger_context_menu(OpenAiCompletionsInferenceStubs.spanish_text)
ai_helper_context_menu.click_ai_button
ai_helper_context_menu.select_helper_model(
OpenAiCompletionsInferenceStubs.text_mode_to_id(mode),
)
wait_for do
composer.composer_input.value == OpenAiCompletionsInferenceStubs.translated_response.strip
end
ai_helper_context_menu.click_view_changes_button
expect(diff_modal).to be_visible
expect(diff_modal.old_value).to eq(
OpenAiCompletionsInferenceStubs.spanish_text.gsub(/[[:space:]]+/, " ").strip,
)
expect(diff_modal.new_value).to eq(
OpenAiCompletionsInferenceStubs
.translated_response
.gsub(/[[:space:]]+/, " ")
.gsub(/[]/, "'")
.gsub(/[“”]/, '"')
.strip,
)
diff_modal.confirm_changes
expect(ai_helper_context_menu).to be_showing_resets
end
end
context "when using the proofreading mode" do

View File

@ -10,6 +10,7 @@ module PageObjects
SUGGESTIONS_STATE_SELECTOR = "#{CONTEXT_MENU_SELECTOR}__suggestions"
LOADING_STATE_SELECTOR = "#{CONTEXT_MENU_SELECTOR}__loading"
RESETS_STATE_SELECTOR = "#{CONTEXT_MENU_SELECTOR}__resets"
REVIEW_STATE_SELECTOR = "#{CONTEXT_MENU_SELECTOR}__review"
def click_ai_button
find("#{TRIGGER_STATE_SELECTOR} .btn").click
@ -27,6 +28,18 @@ module PageObjects
find("#{RESETS_STATE_SELECTOR} .undo").click
end
def click_revert_button
find("#{REVIEW_STATE_SELECTOR} .revert").click
end
def click_view_changes_button
find("#{REVIEW_STATE_SELECTOR} .view-changes").click
end
def click_confirm_button
find("#{REVIEW_STATE_SELECTOR} .confirm").click
end
def press_undo_keys
find(COMPOSER_EDITOR_SELECTOR).send_keys([:control, "z"])
end

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
module PageObjects
module Modals
class DiffModal < PageObjects::Modals::Base
def visible?
page.has_css?(".composer-ai-helper-modal", wait: 5)
end
def confirm_changes
find(".modal-footer button.confirm", wait: 5).click
end
def old_value
find(".composer-ai-helper-modal__old-value").text
end
def new_value
find(".composer-ai-helper-modal__new-value").text
end
end
end
end