mirror of
https://github.com/discourse/discourse-ai.git
synced 2025-03-09 11:48:47 +00:00
FEATURE: Add custom prompts to post helper options (#355)
* FEATURE: Add custom prompts to post helper options * 💄Make pretty * 💄Make pretty!
This commit is contained in:
parent
6de9b9c274
commit
74a7ac4a3d
@ -0,0 +1,46 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { Input } from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import not from "truth-helpers/helpers/not";
|
||||
|
||||
export default class AiHelperCustomPrompt extends Component {
|
||||
<template>
|
||||
<div class="ai-custom-prompt" {{didInsert this.setupCustomPrompt}}>
|
||||
<Input
|
||||
@value={{@value}}
|
||||
placeholder={{i18n
|
||||
"discourse_ai.ai_helper.context_menu.custom_prompt.placeholder"
|
||||
}}
|
||||
class="ai-custom-prompt__input"
|
||||
@enter={{this.sendInput}}
|
||||
/>
|
||||
|
||||
<DButton
|
||||
@class="ai-custom-prompt__submit btn-primary"
|
||||
@icon="discourse-sparkles"
|
||||
@action={{@submit}}
|
||||
@actionParam={{@promptArgs}}
|
||||
@disabled={{not @value.length}}
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@tracked _customPromptInput;
|
||||
|
||||
@action
|
||||
setupCustomPrompt() {
|
||||
this._customPromptInput = document.querySelector(
|
||||
".ai-custom-prompt__input"
|
||||
);
|
||||
this._customPromptInput.focus();
|
||||
}
|
||||
|
||||
@action
|
||||
sendInput() {
|
||||
return this.args.submit(this.args.promptArgs);
|
||||
}
|
||||
}
|
@ -17,27 +17,11 @@
|
||||
<ul class="ai-helper-context-menu__options">
|
||||
{{#each this.helperOptions as |option|}}
|
||||
{{#if (eq option.name "custom_prompt")}}
|
||||
<div
|
||||
class="ai-custom-prompt"
|
||||
{{did-insert this.setupCustomPrompt}}
|
||||
>
|
||||
<Input
|
||||
@value={{this.customPromptValue}}
|
||||
placeholder={{i18n
|
||||
"discourse_ai.ai_helper.context_menu.custom_prompt.placeholder"
|
||||
}}
|
||||
class="ai-custom-prompt__input"
|
||||
@enter={{action (fn this.updateSelected option)}}
|
||||
/>
|
||||
|
||||
<DButton
|
||||
@class="ai-custom-prompt__submit btn-primary"
|
||||
@icon="discourse-sparkles"
|
||||
@action={{this.updateSelected}}
|
||||
@actionParam={{option}}
|
||||
@disabled={{not this.customPromptValue.length}}
|
||||
/>
|
||||
</div>
|
||||
<AiHelperCustomPrompt
|
||||
@value={{this.customPromptValue}}
|
||||
@promptArgs={{option}}
|
||||
@submit={{this.updateSelected}}
|
||||
/>
|
||||
{{else}}
|
||||
<li data-name={{option.translated_name}} data-value={{option.id}}>
|
||||
<DButton
|
||||
|
@ -11,6 +11,7 @@ import { cook } from "discourse/lib/text";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import eq from "truth-helpers/helpers/eq";
|
||||
import not from "truth-helpers/helpers/not";
|
||||
import AiHelperCustomPrompt from "../../components/ai-helper-custom-prompt";
|
||||
import AiHelperLoading from "../../components/ai-helper-loading";
|
||||
import { showPostAIHelper } from "../../lib/show-ai-helper";
|
||||
|
||||
@ -19,12 +20,14 @@ export default class AIHelperOptionsMenu extends Component {
|
||||
return showPostAIHelper(outletArgs, helper);
|
||||
}
|
||||
@service messageBus;
|
||||
@service siteSettings;
|
||||
@service currentUser;
|
||||
@tracked helperOptions = [];
|
||||
@tracked menuState = this.MENU_STATES.triggers;
|
||||
@tracked loading = false;
|
||||
@tracked suggestion = "";
|
||||
@tracked showMainButtons = true;
|
||||
|
||||
@tracked customPromptValue = "";
|
||||
@tracked copyButtonIcon = "copy";
|
||||
@tracked copyButtonLabel = "discourse_ai.ai_helper.post_options_menu.copy";
|
||||
|
||||
@ -96,18 +99,23 @@ export default class AIHelperOptionsMenu extends Component {
|
||||
this._activeAIRequest = ajax("/discourse-ai/ai-helper/suggest", {
|
||||
method: "POST",
|
||||
data: {
|
||||
mode: option.value,
|
||||
mode: option.id,
|
||||
text: this.args.outletArgs.data.quoteState.buffer,
|
||||
custom_prompt: "",
|
||||
custom_prompt: this.customPromptValue,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (option.name !== "Explain") {
|
||||
this._activeAIRequest.catch(popupAjaxError).finally(() => {
|
||||
this.loading = false;
|
||||
this.menuState = this.MENU_STATES.result;
|
||||
});
|
||||
this._activeAIRequest
|
||||
.then(({ suggestions }) => {
|
||||
this.suggestion = suggestions[0];
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
this.menuState = this.MENU_STATES.result;
|
||||
});
|
||||
}
|
||||
|
||||
return this._activeAIRequest;
|
||||
@ -142,15 +150,32 @@ export default class AIHelperOptionsMenu extends Component {
|
||||
async loadPrompts() {
|
||||
let prompts = await ajax("/discourse-ai/ai-helper/prompts");
|
||||
|
||||
this.helperOptions = prompts
|
||||
.filter((item) => item.location.includes("post"))
|
||||
.map((p) => {
|
||||
return {
|
||||
name: p.translated_name,
|
||||
value: p.id,
|
||||
icon: p.icon,
|
||||
};
|
||||
});
|
||||
prompts = prompts.filter((item) => item.location.includes("post"));
|
||||
|
||||
// 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._showUserCustomPrompts()) {
|
||||
prompts = prompts.filter((p) => p.name !== "custom_prompt");
|
||||
}
|
||||
|
||||
this.helperOptions = prompts;
|
||||
}
|
||||
|
||||
_showUserCustomPrompts() {
|
||||
const allowedGroups =
|
||||
this.siteSettings?.ai_helper_custom_prompts_allowed_groups
|
||||
.split("|")
|
||||
.map((id) => parseInt(id, 10));
|
||||
|
||||
return this.currentUser?.groups.some((g) => allowedGroups.includes(g.id));
|
||||
}
|
||||
|
||||
<template>
|
||||
@ -170,15 +195,23 @@ export default class AIHelperOptionsMenu extends Component {
|
||||
{{else if (eq this.menuState this.MENU_STATES.options)}}
|
||||
<div class="ai-post-helper__options">
|
||||
{{#each this.helperOptions as |option|}}
|
||||
<DButton
|
||||
@class="btn-flat"
|
||||
@icon={{option.icon}}
|
||||
@translatedLabel={{option.name}}
|
||||
@action={{this.performAISuggestion}}
|
||||
@actionParam={{option}}
|
||||
data-name={{option.name}}
|
||||
data-value={{option.value}}
|
||||
/>
|
||||
{{#if (eq option.name "custom_prompt")}}
|
||||
<AiHelperCustomPrompt
|
||||
@value={{this.customPromptValue}}
|
||||
@promptArgs={{option}}
|
||||
@submit={{this.performAISuggestion}}
|
||||
/>
|
||||
{{else}}
|
||||
<DButton
|
||||
@class="btn-flat"
|
||||
@icon={{option.icon}}
|
||||
@translatedLabel={{option.translated_name}}
|
||||
@action={{this.performAISuggestion}}
|
||||
@actionParam={{option}}
|
||||
data-name={{option.name}}
|
||||
data-value={{option.value}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
|
@ -108,39 +108,19 @@
|
||||
align-items: center;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
}
|
||||
|
||||
&__custom-prompt {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding: 0.5rem;
|
||||
.ai-custom-prompt {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
&-header {
|
||||
margin-bottom: 0.5rem;
|
||||
&__input[type="text"] {
|
||||
border-color: var(--primary-400);
|
||||
margin-bottom: 0;
|
||||
|
||||
.btn {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-custom-prompt-input {
|
||||
min-height: 90px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-custom-prompt {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
&__input {
|
||||
background: var(--primary-low);
|
||||
border-color: var(--primary-low);
|
||||
margin-bottom: 0;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
&::placeholder {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -326,6 +306,15 @@
|
||||
align-items: flex-start;
|
||||
gap: 0.25rem;
|
||||
justify-content: flex-start;
|
||||
|
||||
> button:last-child {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.ai-custom-prompt {
|
||||
padding: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__suggestion {
|
||||
|
@ -85,6 +85,8 @@ module DiscourseAi
|
||||
</input>
|
||||
<output>
|
||||
</output>
|
||||
<result>
|
||||
</result>
|
||||
]
|
||||
|
||||
result.dup.tap { |dup_result| tags_to_remove.each { |tag| dup_result.gsub!(tag, "") } }
|
||||
@ -130,7 +132,7 @@ module DiscourseAi
|
||||
when "tone"
|
||||
%w[composer]
|
||||
when "custom_prompt"
|
||||
%w[composer]
|
||||
%w[composer post]
|
||||
when "rewrite"
|
||||
%w[composer]
|
||||
when "explain"
|
||||
|
Loading…
x
Reference in New Issue
Block a user