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">
|
<ul class="ai-helper-context-menu__options">
|
||||||
{{#each this.helperOptions as |option|}}
|
{{#each this.helperOptions as |option|}}
|
||||||
{{#if (eq option.name "custom_prompt")}}
|
{{#if (eq option.name "custom_prompt")}}
|
||||||
<div
|
<AiHelperCustomPrompt
|
||||||
class="ai-custom-prompt"
|
|
||||||
{{did-insert this.setupCustomPrompt}}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
@value={{this.customPromptValue}}
|
@value={{this.customPromptValue}}
|
||||||
placeholder={{i18n
|
@promptArgs={{option}}
|
||||||
"discourse_ai.ai_helper.context_menu.custom_prompt.placeholder"
|
@submit={{this.updateSelected}}
|
||||||
}}
|
|
||||||
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>
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<li data-name={{option.translated_name}} data-value={{option.id}}>
|
<li data-name={{option.translated_name}} data-value={{option.id}}>
|
||||||
<DButton
|
<DButton
|
||||||
|
@ -11,6 +11,7 @@ import { cook } from "discourse/lib/text";
|
|||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
import eq from "truth-helpers/helpers/eq";
|
import eq from "truth-helpers/helpers/eq";
|
||||||
import not from "truth-helpers/helpers/not";
|
import not from "truth-helpers/helpers/not";
|
||||||
|
import AiHelperCustomPrompt from "../../components/ai-helper-custom-prompt";
|
||||||
import AiHelperLoading from "../../components/ai-helper-loading";
|
import AiHelperLoading from "../../components/ai-helper-loading";
|
||||||
import { showPostAIHelper } from "../../lib/show-ai-helper";
|
import { showPostAIHelper } from "../../lib/show-ai-helper";
|
||||||
|
|
||||||
@ -19,12 +20,14 @@ export default class AIHelperOptionsMenu extends Component {
|
|||||||
return showPostAIHelper(outletArgs, helper);
|
return showPostAIHelper(outletArgs, helper);
|
||||||
}
|
}
|
||||||
@service messageBus;
|
@service messageBus;
|
||||||
|
@service siteSettings;
|
||||||
|
@service currentUser;
|
||||||
@tracked helperOptions = [];
|
@tracked helperOptions = [];
|
||||||
@tracked menuState = this.MENU_STATES.triggers;
|
@tracked menuState = this.MENU_STATES.triggers;
|
||||||
@tracked loading = false;
|
@tracked loading = false;
|
||||||
@tracked suggestion = "";
|
@tracked suggestion = "";
|
||||||
@tracked showMainButtons = true;
|
@tracked showMainButtons = true;
|
||||||
|
@tracked customPromptValue = "";
|
||||||
@tracked copyButtonIcon = "copy";
|
@tracked copyButtonIcon = "copy";
|
||||||
@tracked copyButtonLabel = "discourse_ai.ai_helper.post_options_menu.copy";
|
@tracked copyButtonLabel = "discourse_ai.ai_helper.post_options_menu.copy";
|
||||||
|
|
||||||
@ -96,15 +99,20 @@ export default class AIHelperOptionsMenu extends Component {
|
|||||||
this._activeAIRequest = ajax("/discourse-ai/ai-helper/suggest", {
|
this._activeAIRequest = ajax("/discourse-ai/ai-helper/suggest", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data: {
|
data: {
|
||||||
mode: option.value,
|
mode: option.id,
|
||||||
text: this.args.outletArgs.data.quoteState.buffer,
|
text: this.args.outletArgs.data.quoteState.buffer,
|
||||||
custom_prompt: "",
|
custom_prompt: this.customPromptValue,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option.name !== "Explain") {
|
if (option.name !== "Explain") {
|
||||||
this._activeAIRequest.catch(popupAjaxError).finally(() => {
|
this._activeAIRequest
|
||||||
|
.then(({ suggestions }) => {
|
||||||
|
this.suggestion = suggestions[0];
|
||||||
|
})
|
||||||
|
.catch(popupAjaxError)
|
||||||
|
.finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.menuState = this.MENU_STATES.result;
|
this.menuState = this.MENU_STATES.result;
|
||||||
});
|
});
|
||||||
@ -142,15 +150,32 @@ export default class AIHelperOptionsMenu extends Component {
|
|||||||
async loadPrompts() {
|
async loadPrompts() {
|
||||||
let prompts = await ajax("/discourse-ai/ai-helper/prompts");
|
let prompts = await ajax("/discourse-ai/ai-helper/prompts");
|
||||||
|
|
||||||
this.helperOptions = prompts
|
prompts = prompts.filter((item) => item.location.includes("post"));
|
||||||
.filter((item) => item.location.includes("post"))
|
|
||||||
.map((p) => {
|
// Find the custom_prompt object and move it to the beginning of the array
|
||||||
return {
|
const customPromptIndex = prompts.findIndex(
|
||||||
name: p.translated_name,
|
(p) => p.name === "custom_prompt"
|
||||||
value: p.id,
|
);
|
||||||
icon: p.icon,
|
|
||||||
};
|
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>
|
<template>
|
||||||
@ -170,15 +195,23 @@ export default class AIHelperOptionsMenu extends Component {
|
|||||||
{{else if (eq this.menuState this.MENU_STATES.options)}}
|
{{else if (eq this.menuState this.MENU_STATES.options)}}
|
||||||
<div class="ai-post-helper__options">
|
<div class="ai-post-helper__options">
|
||||||
{{#each this.helperOptions as |option|}}
|
{{#each this.helperOptions as |option|}}
|
||||||
|
{{#if (eq option.name "custom_prompt")}}
|
||||||
|
<AiHelperCustomPrompt
|
||||||
|
@value={{this.customPromptValue}}
|
||||||
|
@promptArgs={{option}}
|
||||||
|
@submit={{this.performAISuggestion}}
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
<DButton
|
<DButton
|
||||||
@class="btn-flat"
|
@class="btn-flat"
|
||||||
@icon={{option.icon}}
|
@icon={{option.icon}}
|
||||||
@translatedLabel={{option.name}}
|
@translatedLabel={{option.translated_name}}
|
||||||
@action={{this.performAISuggestion}}
|
@action={{this.performAISuggestion}}
|
||||||
@actionParam={{option}}
|
@actionParam={{option}}
|
||||||
data-name={{option.name}}
|
data-name={{option.name}}
|
||||||
data-value={{option.value}}
|
data-value={{option.value}}
|
||||||
/>
|
/>
|
||||||
|
{{/if}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -108,41 +108,21 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__custom-prompt {
|
.ai-custom-prompt {
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
padding: 0.5rem;
|
|
||||||
|
|
||||||
&-header {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ai-custom-prompt-input {
|
|
||||||
min-height: 90px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ai-custom-prompt {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
|
|
||||||
&__input {
|
&__input[type="text"] {
|
||||||
background: var(--primary-low);
|
border-color: var(--primary-400);
|
||||||
border-color: var(--primary-low);
|
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: var(--primary-medium);
|
color: var(--primary-medium);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.d-editor-input.loading {
|
.d-editor-input.loading {
|
||||||
@ -326,6 +306,15 @@
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
> button:last-child {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-custom-prompt {
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__suggestion {
|
&__suggestion {
|
||||||
|
@ -85,6 +85,8 @@ module DiscourseAi
|
|||||||
</input>
|
</input>
|
||||||
<output>
|
<output>
|
||||||
</output>
|
</output>
|
||||||
|
<result>
|
||||||
|
</result>
|
||||||
]
|
]
|
||||||
|
|
||||||
result.dup.tap { |dup_result| tags_to_remove.each { |tag| dup_result.gsub!(tag, "") } }
|
result.dup.tap { |dup_result| tags_to_remove.each { |tag| dup_result.gsub!(tag, "") } }
|
||||||
@ -130,7 +132,7 @@ module DiscourseAi
|
|||||||
when "tone"
|
when "tone"
|
||||||
%w[composer]
|
%w[composer]
|
||||||
when "custom_prompt"
|
when "custom_prompt"
|
||||||
%w[composer]
|
%w[composer post]
|
||||||
when "rewrite"
|
when "rewrite"
|
||||||
%w[composer]
|
%w[composer]
|
||||||
when "explain"
|
when "explain"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user