DEV: Strip out old modal based AI helper (#209)
This commit is contained in:
parent
0733ff7e67
commit
abe96d5533
|
@ -1,69 +0,0 @@
|
|||
<DModalBody @title="discourse_ai.ai_helper.title">
|
||||
<span>{{i18n "discourse_ai.ai_helper.description"}}</span>
|
||||
|
||||
<ComboBox
|
||||
@value={{this.selected}}
|
||||
@content={{this.helperOptions}}
|
||||
@onChange={{action this.updateSelected}}
|
||||
@valueProperty="value"
|
||||
@class="ai-helper-mode"
|
||||
/>
|
||||
|
||||
<div class="text-preview">
|
||||
<Textarea
|
||||
@value={{this.composedMessage}}
|
||||
disabled="true"
|
||||
class="preview-area"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="selection-hint">{{i18n
|
||||
"discourse_ai.ai_helper.selection_hint"
|
||||
}}</div>
|
||||
|
||||
<div class="text-preview">
|
||||
<ConditionalLoadingSpinner @condition={{this.loading}} />
|
||||
|
||||
{{#unless this.loading}}
|
||||
{{#if this.selectingTopicTitle}}
|
||||
<div class="radios">
|
||||
{{#each this.generatedTitlesSuggestions as |title index|}}
|
||||
<label class="radio-label" for="title-suggestion-{{index}}">
|
||||
<RadioButton
|
||||
@id="title-suggestion-{{index}}"
|
||||
@name="title-suggestion"
|
||||
@value={{title}}
|
||||
@selection={{this.selectedTitle}}
|
||||
/>
|
||||
<b>{{title}}</b>
|
||||
</label>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{else if this.proofreadingText}}
|
||||
{{html-safe this.proofreadDiff}}
|
||||
{{else if this.translatingText}}
|
||||
<Textarea
|
||||
@value={{this.translatedSuggestion}}
|
||||
disabled="true"
|
||||
class="preview-area"
|
||||
/>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
</DModalBody>
|
||||
|
||||
<div class="modal-footer">
|
||||
{{#if this.canSave}}
|
||||
<DButton
|
||||
@class="btn-primary create"
|
||||
@action={{this.applySuggestion}}
|
||||
@label="save"
|
||||
/>
|
||||
<DModalCancel @close={{route-action "closeModal"}} />
|
||||
{{else}}
|
||||
<div class="ai-helper-waiting-selection">{{i18n
|
||||
"discourse_ai.modals.select_option"
|
||||
}}</div>
|
||||
{{/if}}
|
||||
</div>
|
|
@ -1,152 +0,0 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action, computed } from "@ember/object";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
const LIST = "list";
|
||||
const TEXT = "text";
|
||||
const DIFF = "diff";
|
||||
|
||||
export default class AiHelper extends Component {
|
||||
@tracked selected = null;
|
||||
@tracked loading = false;
|
||||
|
||||
@tracked generatedTitlesSuggestions = [];
|
||||
|
||||
@tracked proofReadSuggestion = null;
|
||||
@tracked translatedSuggestion = null;
|
||||
@tracked selectedTitle = null;
|
||||
|
||||
@tracked proofreadDiff = null;
|
||||
|
||||
@tracked helperOptions = [];
|
||||
prompts = [];
|
||||
promptTypes = {};
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadPrompts();
|
||||
}
|
||||
|
||||
async loadPrompts() {
|
||||
let prompts = await ajax("/discourse-ai/ai-helper/prompts");
|
||||
|
||||
prompts.map((p) => {
|
||||
this.prompts[p.id] = p;
|
||||
});
|
||||
|
||||
this.promptTypes = prompts.reduce((memo, p) => {
|
||||
memo[p.name] = p.prompt_type;
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
this.helperOptions = prompts.map((p) => {
|
||||
return {
|
||||
name: p.translated_name,
|
||||
value: p.id,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
get composedMessage() {
|
||||
const editor = this.args.editor;
|
||||
|
||||
return editor.getSelected().value || editor.value;
|
||||
}
|
||||
|
||||
@computed("selected", "selectedTitle", "translatingText", "proofreadingText")
|
||||
get canSave() {
|
||||
return (
|
||||
(this.selected &&
|
||||
this.prompts[this.selected].prompt_type === LIST &&
|
||||
this.selectedTitle) ||
|
||||
this.translatingText ||
|
||||
this.proofreadingText
|
||||
);
|
||||
}
|
||||
|
||||
@computed("selected", "translatedSuggestion")
|
||||
get translatingText() {
|
||||
return (
|
||||
this.selected &&
|
||||
this.prompts[this.selected].prompt_type === TEXT &&
|
||||
this.translatedSuggestion
|
||||
);
|
||||
}
|
||||
|
||||
@computed("selected", "proofReadSuggestion")
|
||||
get proofreadingText() {
|
||||
return (
|
||||
this.selected &&
|
||||
this.prompts[this.selected].prompt_type === DIFF &&
|
||||
this.proofReadSuggestion
|
||||
);
|
||||
}
|
||||
|
||||
@computed("selected", "generatedTitlesSuggestions")
|
||||
get selectingTopicTitle() {
|
||||
return (
|
||||
this.selected &&
|
||||
this.prompts[this.selected].prompt_type === LIST &&
|
||||
this.generatedTitlesSuggestions.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
_updateSuggestedByAI(data) {
|
||||
switch (data.type) {
|
||||
case LIST:
|
||||
this.generatedTitlesSuggestions = data.suggestions;
|
||||
break;
|
||||
case TEXT:
|
||||
this.translatedSuggestion = data.suggestions[0];
|
||||
break;
|
||||
case DIFF:
|
||||
this.proofReadSuggestion = data.suggestions[0];
|
||||
this.proofreadDiff = data.diff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
async updateSelected(value) {
|
||||
this.loading = true;
|
||||
this.selected = value;
|
||||
|
||||
if (value === LIST) {
|
||||
this.selectedTitle = null;
|
||||
}
|
||||
|
||||
if (this.hasSuggestion) {
|
||||
this.loading = false;
|
||||
} else {
|
||||
return ajax("/discourse-ai/ai-helper/suggest", {
|
||||
method: "POST",
|
||||
data: { mode: this.selected, text: this.composedMessage },
|
||||
})
|
||||
.then((data) => {
|
||||
this._updateSuggestedByAI(data);
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => (this.loading = false));
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
applySuggestion() {
|
||||
if (this.selectingTopicTitle) {
|
||||
const composer = this.args.editor.outletArgs?.composer;
|
||||
|
||||
if (composer) {
|
||||
composer.set("title", this.selectedTitle);
|
||||
}
|
||||
} else {
|
||||
const newText = this.proofreadingText
|
||||
? this.proofReadSuggestion
|
||||
: this.translatedSuggestion;
|
||||
this.args.editor.replaceText(this.composedMessage, newText);
|
||||
}
|
||||
|
||||
this.args.closeModal();
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ export default class ModalDiffModal extends Component {
|
|||
</:footer>
|
||||
</DModal>
|
||||
</template>
|
||||
|
||||
|
||||
@action
|
||||
triggerConfirmChanges() {
|
||||
this.args.closeModal();
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<AiHelper @editor={{this.editor}} @closeModal={{route-action "closeModal"}} />
|
|
@ -1,72 +0,0 @@
|
|||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
function initializeComposerAIHelper(api) {
|
||||
api.modifyClass("component:composer-editor", {
|
||||
pluginId: "discourse-ai",
|
||||
|
||||
actions: {
|
||||
extraButtons(toolbar) {
|
||||
this._super(toolbar);
|
||||
|
||||
const removeAiHelperFromPM =
|
||||
this.composerModel.privateMessage &&
|
||||
!this.siteSettings.ai_helper_allowed_in_pm;
|
||||
|
||||
if (removeAiHelperFromPM) {
|
||||
const extrasGroup = toolbar.groups.find((g) => g.group === "extras");
|
||||
const newButtons = extrasGroup.buttons.filter(
|
||||
(b) => b.id !== "ai-helper"
|
||||
);
|
||||
|
||||
extrasGroup.buttons = newButtons;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
api.modifyClass("component:d-editor", {
|
||||
pluginId: "discourse-ai",
|
||||
|
||||
actions: {
|
||||
openAIHelper() {
|
||||
if (this.value) {
|
||||
showModal("composer-ai-helper").setProperties({ editor: this });
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
api.onToolbarCreate((toolbar) => {
|
||||
toolbar.addButton({
|
||||
id: "ai-helper",
|
||||
title: "discourse_ai.ai_helper.title",
|
||||
group: "extras",
|
||||
icon: "discourse-sparkles",
|
||||
className: "composer-ai-helper",
|
||||
sendAction: () => toolbar.context.send("openAIHelper"),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "discourse-ai-composer-helper",
|
||||
|
||||
initialize(container) {
|
||||
const settings = container.lookup("service:site-settings");
|
||||
const user = container.lookup("service:current-user");
|
||||
const helperEnabled =
|
||||
settings.discourse_ai_enabled && settings.composer_ai_helper_enabled;
|
||||
|
||||
const allowedGroups = settings.ai_helper_allowed_groups
|
||||
.split("|")
|
||||
.map((id) => parseInt(id, 10));
|
||||
const canUseAssistant = user?.groups.some((g) =>
|
||||
allowedGroups.includes(g.id)
|
||||
);
|
||||
|
||||
if (helperEnabled && canUseAssistant) {
|
||||
withPluginApi("1.6.0", initializeComposerAIHelper);
|
||||
}
|
||||
},
|
||||
};
|
|
@ -1,10 +1,4 @@
|
|||
.composer-ai-helper-modal {
|
||||
.combobox,
|
||||
.text-preview,
|
||||
.ai-helper-waiting-selection {
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
.text-preview,
|
||||
.inline-diff {
|
||||
ins {
|
||||
|
@ -31,11 +25,6 @@
|
|||
background-color: var(--success-low);
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.selection-hint {
|
||||
font-size: var(--font-down-2);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.topic-above-suggested-outlet.related-topics {
|
||||
|
|
|
@ -13,7 +13,6 @@ 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 }
|
||||
let(:ai_suggestion_dropdown) { PageObjects::Components::AISuggestionDropdown.new }
|
||||
fab!(:category) { Fabricate(:category) }
|
||||
|
@ -24,79 +23,6 @@ RSpec.describe "AI Composer helper", type: :system, js: true do
|
|||
fab!(:feedback) { Fabricate(:tag) }
|
||||
fab!(:review) { Fabricate(:tag) }
|
||||
|
||||
context "when using the translation mode" do
|
||||
let(:mode) { OpenAiCompletionsInferenceStubs::TRANSLATE }
|
||||
|
||||
before { OpenAiCompletionsInferenceStubs.stub_prompt(mode) }
|
||||
|
||||
it "replaces the composed message with AI generated content" do
|
||||
visit("/latest")
|
||||
page.find("#create-topic").click
|
||||
|
||||
composer.fill_content(OpenAiCompletionsInferenceStubs.spanish_text)
|
||||
page.find(".composer-ai-helper").click
|
||||
|
||||
expect(ai_helper_modal).to be_visible
|
||||
|
||||
ai_helper_modal.select_helper_model(OpenAiCompletionsInferenceStubs.text_mode_to_id(mode))
|
||||
ai_helper_modal.save_changes
|
||||
|
||||
expect(composer.composer_input.value).to eq(
|
||||
OpenAiCompletionsInferenceStubs.translated_response.strip,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when using the proofreading mode" do
|
||||
let(:mode) { OpenAiCompletionsInferenceStubs::PROOFREAD }
|
||||
|
||||
before { OpenAiCompletionsInferenceStubs.stub_prompt(mode) }
|
||||
|
||||
it "replaces the composed message with AI generated content" do
|
||||
visit("/latest")
|
||||
page.find("#create-topic").click
|
||||
|
||||
composer.fill_content(OpenAiCompletionsInferenceStubs.translated_response)
|
||||
page.find(".composer-ai-helper").click
|
||||
|
||||
expect(ai_helper_modal).to be_visible
|
||||
|
||||
ai_helper_modal.select_helper_model(OpenAiCompletionsInferenceStubs.text_mode_to_id(mode))
|
||||
|
||||
wait_for { ai_helper_modal.has_diff? == true }
|
||||
|
||||
ai_helper_modal.save_changes
|
||||
|
||||
expect(composer.composer_input.value).to eq(
|
||||
OpenAiCompletionsInferenceStubs.proofread_response.strip,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when selecting an AI generated title" do
|
||||
let(:mode) { OpenAiCompletionsInferenceStubs::GENERATE_TITLES }
|
||||
|
||||
before { OpenAiCompletionsInferenceStubs.stub_prompt(mode) }
|
||||
|
||||
it "replaces the topic title" do
|
||||
visit("/latest")
|
||||
page.find("#create-topic").click
|
||||
|
||||
composer.fill_content(OpenAiCompletionsInferenceStubs.translated_response)
|
||||
page.find(".composer-ai-helper").click
|
||||
|
||||
expect(ai_helper_modal).to be_visible
|
||||
|
||||
ai_helper_modal.select_helper_model(OpenAiCompletionsInferenceStubs.text_mode_to_id(mode))
|
||||
ai_helper_modal.select_title_suggestion(2)
|
||||
ai_helper_modal.save_changes
|
||||
|
||||
expected_title = "The Quiet Piece that Moves Literature: A Gaucho's Story"
|
||||
|
||||
expect(find("#reply-title").value).to eq(expected_title)
|
||||
end
|
||||
end
|
||||
|
||||
def trigger_context_menu(content)
|
||||
visit("/latest")
|
||||
page.find("#create-topic").click
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Modals
|
||||
class AiHelper < PageObjects::Modals::Base
|
||||
def visible?
|
||||
page.has_css?(".composer-ai-helper-modal", wait: 5)
|
||||
end
|
||||
|
||||
def select_helper_model(mode)
|
||||
find(".ai-helper-mode").click
|
||||
find(".select-kit-row[data-value=\"#{mode}\"]").click
|
||||
end
|
||||
|
||||
def save_changes
|
||||
find(".modal-footer button.create", wait: 5).click
|
||||
end
|
||||
|
||||
def select_title_suggestion(option_number)
|
||||
find("input#title-suggestion-#{option_number}").click
|
||||
end
|
||||
|
||||
def has_diff?
|
||||
has_css?(".text-preview .inline-diff")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue