discourse-ai/assets/javascripts/discourse/components/ai-composer-helper-menu.gjs

163 lines
4.4 KiB
Plaintext
Raw Normal View History

import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { getOwner } from "@ember/owner";
import { service } from "@ember/service";
import I18n from "discourse-i18n";
import DToast from "float-kit/components/d-toast";
import DToastInstance from "float-kit/lib/d-toast-instance";
import AiHelperOptionsList from "../components/ai-helper-options-list";
import ModalDiffModal from "../components/modal/diff-modal";
import ThumbnailSuggestion from "../components/modal/thumbnail-suggestions";
export default class AiComposerHelperMenu extends Component {
@service modal;
@service siteSettings;
@service currentUser;
@service site;
@tracked newSelectedText;
@tracked diff;
@tracked customPromptValue = "";
@tracked noContentError = false;
prompts = [];
promptTypes = {};
constructor() {
super(...arguments);
if (this.args.data.toolbarEvent.getText().length === 0) {
this.noContentError = true;
}
}
get helperOptions() {
let prompts = this.currentUser?.ai_helper_prompts;
prompts = prompts
.filter((p) => p.location.includes("composer"))
.filter((p) => p.name !== "generate_titles")
.map((p) => {
// AI helper by default returns interface locale on translations
// Since we want site default translations (and we are using: force_default_locale)
// we need to replace the translated_name with the site default locale name
const siteLocale = this.siteSettings.default_locale;
const availableLocales = JSON.parse(
this.siteSettings.available_locales
);
const locale = availableLocales.find((l) => l.value === siteLocale);
const translatedName = I18n.t(
"discourse_ai.ai_helper.context_menu.translate_prompt",
{
language: locale.name,
}
);
if (p.name === "translate") {
return { ...p, translated_name: translatedName };
}
return p;
});
// Find the custom_prompt object and move it to the beginning of the array
const customPromptIndex = prompts.findIndex(
(p) => p.name === "custom_prompt"
);
if (customPromptIndex !== -1) {
const customPrompt = prompts.splice(customPromptIndex, 1)[0];
prompts.unshift(customPrompt);
}
if (!this.currentUser?.can_use_custom_prompts) {
prompts = prompts.filter((p) => p.name !== "custom_prompt");
}
prompts.forEach((p) => {
this.prompts[p.id] = p;
});
this.promptTypes = prompts.reduce((memo, p) => {
memo[p.name] = p.prompt_type;
return memo;
}, {});
return prompts;
}
get toast() {
const owner = getOwner(this);
const options = {
close: () => this.args.close(),
duration: 3000,
data: {
theme: "error",
icon: "triangle-exclamation",
message: I18n.t("discourse_ai.ai_helper.no_content_error"),
},
};
const custom = class CustomToastInstance extends DToastInstance {
constructor() {
super(owner, options);
}
@action
close() {
this.options.close();
}
};
return new custom(owner, options);
}
@action
async suggestChanges(option) {
await this.args.close();
if (option.name === "illustrate_post") {
return this.modal.show(ThumbnailSuggestion, {
model: {
mode: option.id,
selectedText: this.args.data.selectedText,
thumbnails: this.thumbnailSuggestions,
},
});
}
return this.modal.show(ModalDiffModal, {
model: {
mode: option.id,
selectedText: this.args.data.selectedText,
revert: this.undoAiAction,
toolbarEvent: this.args.data.toolbarEvent,
customPromptValue: this.customPromptValue,
},
});
}
@action
closeMenu() {
this.customPromptValue = "";
this.args.close();
}
<template>
{{#if this.noContentError}}
<DToast @toast={{this.toast}} />
{{else}}
<div class="ai-composer-helper-menu">
{{#if this.site.mobileView}}
<div class="ai-composer-helper-menu__selected-text">
{{@data.selectedText}}
</div>
{{/if}}
<AiHelperOptionsList
@options={{this.helperOptions}}
@customPromptValue={{this.customPromptValue}}
@performAction={{this.suggestChanges}}
@shortcutVisible={{true}}
/>
</div>
{{/if}}
</template>
}