diff --git a/frontend/src/components/CommunityHub/PublishEntityModal/SystemPrompts/index.jsx b/frontend/src/components/CommunityHub/PublishEntityModal/SystemPrompts/index.jsx
new file mode 100644
index 00000000000..a3187922f4c
--- /dev/null
+++ b/frontend/src/components/CommunityHub/PublishEntityModal/SystemPrompts/index.jsx
@@ -0,0 +1,242 @@
+import { useState, useRef } from "react";
+import { useTranslation } from "react-i18next";
+import CommunityHub from "@/models/communityHub";
+import showToast from "@/utils/toast";
+import paths from "@/utils/paths";
+import { X } from "@phosphor-icons/react/dist/ssr";
+
+export default function SystemPrompts({ entity }) {
+ const { t } = useTranslation();
+ const formRef = useRef(null);
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [tags, setTags] = useState([]);
+ const [tagInput, setTagInput] = useState("");
+ const [visibility, setVisibility] = useState("public");
+ const [isSuccess, setIsSuccess] = useState(false);
+ const [itemId, setItemId] = useState(null);
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setIsSubmitting(true);
+ try {
+ const form = new FormData(formRef.current);
+ const data = {
+ name: form.get("name"),
+ description: form.get("description"),
+ prompt: form.get("prompt"),
+ tags: tags,
+ visibility: visibility,
+ };
+
+ const { success, error, itemId } =
+ await CommunityHub.createSystemPrompt(data);
+ if (!success) throw new Error(error);
+ setItemId(itemId);
+ setIsSuccess(true);
+ } catch (error) {
+ console.error("Failed to publish prompt:", error);
+ showToast(`Failed to publish prompt: ${error.message}`, "error", {
+ clear: true,
+ });
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ const handleKeyDown = (e) => {
+ if (e.key === "Enter" || e.key === ",") {
+ e.preventDefault();
+ const value = tagInput.trim();
+ if (value.length > 20) return;
+ if (value && !tags.includes(value)) {
+ setTags((prevTags) => [...prevTags, value].slice(0, 5)); // Limit to 5 tags
+ setTagInput("");
+ }
+ }
+ };
+
+ const removeTag = (tagToRemove) => {
+ setTags(tags.filter((tag) => tag !== tagToRemove));
+ };
+
+ if (isSuccess) {
+ return (
+
+ );
+ }
+
+ return (
+ <>
+
+
+ {t(`chat.prompt.publish.modal_title`)}
+
+
+
+ >
+ );
+}
diff --git a/frontend/src/components/CommunityHub/PublishEntityModal/index.jsx b/frontend/src/components/CommunityHub/PublishEntityModal/index.jsx
new file mode 100644
index 00000000000..2d606e65f58
--- /dev/null
+++ b/frontend/src/components/CommunityHub/PublishEntityModal/index.jsx
@@ -0,0 +1,43 @@
+import { X } from "@phosphor-icons/react";
+import { useCommunityHubAuth } from "@/hooks/useCommunityHubAuth";
+import UnauthenticatedHubModal from "@/components/CommunityHub/UnauthenticatedHubModal";
+import SystemPrompts from "./SystemPrompts";
+import ModalWrapper from "@/components/ModalWrapper";
+
+export default function PublishEntityModal({
+ show,
+ onClose,
+ entityType,
+ entity,
+}) {
+ const { isAuthenticated, loading } = useCommunityHubAuth();
+ if (!show || loading) return null;
+ if (!isAuthenticated)
+ return ;
+
+ const renderEntityForm = () => {
+ switch (entityType) {
+ case "system-prompt":
+ return ;
+ default:
+ return null;
+ }
+ };
+
+ return (
+
+
+
+
+
+ {renderEntityForm()}
+
+
+ );
+}
diff --git a/frontend/src/components/CommunityHub/UnauthenticatedHubModal/index.jsx b/frontend/src/components/CommunityHub/UnauthenticatedHubModal/index.jsx
new file mode 100644
index 00000000000..91665cd2d02
--- /dev/null
+++ b/frontend/src/components/CommunityHub/UnauthenticatedHubModal/index.jsx
@@ -0,0 +1,40 @@
+import { X } from "@phosphor-icons/react";
+import { useTranslation } from "react-i18next";
+import paths from "@/utils/paths";
+import { Link } from "react-router-dom";
+import ModalWrapper from "@/components/ModalWrapper";
+
+export default function UnauthenticatedHubModal({ show, onClose }) {
+ const { t } = useTranslation();
+ if (!show) return null;
+
+ return (
+
+
+
+
+
+
+ {t("chat.prompt.publish.unauthenticated.title")}
+
+
+ {t("chat.prompt.publish.unauthenticated.description")}
+
+
+ {t("chat.prompt.publish.unauthenticated.button")}
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/hooks/useCommunityHubAuth.js b/frontend/src/hooks/useCommunityHubAuth.js
new file mode 100644
index 00000000000..848659029e3
--- /dev/null
+++ b/frontend/src/hooks/useCommunityHubAuth.js
@@ -0,0 +1,30 @@
+import { useState, useEffect } from "react";
+import CommunityHub from "@/models/communityHub";
+
+/**
+ * Hook to check if the user is authenticated with the community hub by checking
+ * the user defined connection key in the settings.
+ * @returns {{isAuthenticated: boolean, loading: boolean}} An object containing the authentication status and loading state.
+ */
+export function useCommunityHubAuth() {
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ async function checkCommunityHubAuth() {
+ setLoading(true);
+ try {
+ const { connectionKey } = await CommunityHub.getSettings();
+ setIsAuthenticated(!!connectionKey);
+ } catch (error) {
+ console.error("Error checking hub auth:", error);
+ setIsAuthenticated(false);
+ } finally {
+ setLoading(false);
+ }
+ }
+ checkCommunityHubAuth();
+ }, []);
+
+ return { isAuthenticated, loading };
+}
diff --git a/frontend/src/index.css b/frontend/src/index.css
index a971f91eaf3..8c4ff7e1aac 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -17,6 +17,7 @@
--theme-bg-chat-input: #27282a;
--theme-text-primary: #ffffff;
--theme-text-secondary: rgba(255, 255, 255, 0.6);
+ --theme-placeholder: #57585a;
--theme-sidebar-item-default: rgba(255, 255, 255, 0.1);
--theme-sidebar-item-selected: rgba(255, 255, 255, 0.3);
--theme-sidebar-item-hover: #3f3f42;
@@ -116,6 +117,7 @@
--theme-bg-chat-input: #eaeaea;
--theme-text-primary: #0e0f0f;
--theme-text-secondary: #7a7d7e;
+ --theme-placeholder: #9ca3af;
--theme-sidebar-item-default: #ffffff;
--theme-sidebar-item-selected: #ffffff;
--theme-sidebar-item-hover: #c8efff;
diff --git a/frontend/src/locales/ar/common.js b/frontend/src/locales/ar/common.js
index 71ed7092a3a..917448158d9 100644
--- a/frontend/src/locales/ar/common.js
+++ b/frontend/src/locales/ar/common.js
@@ -240,6 +240,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/da/common.js b/frontend/src/locales/da/common.js
index 42a0c09a8a4..550df85e27c 100644
--- a/frontend/src/locales/da/common.js
+++ b/frontend/src/locales/da/common.js
@@ -241,6 +241,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/de/common.js b/frontend/src/locales/de/common.js
index 77b1a0aa1ac..1aef04f87ad 100644
--- a/frontend/src/locales/de/common.js
+++ b/frontend/src/locales/de/common.js
@@ -233,6 +233,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/en/common.js b/frontend/src/locales/en/common.js
index b6693a79196..a79cb5109fd 100644
--- a/frontend/src/locales/en/common.js
+++ b/frontend/src/locales/en/common.js
@@ -335,11 +335,45 @@ const TRANSLATIONS = {
noHistory: "No system prompt history available",
restore: "Restore",
delete: "Delete",
+ publish: "Publish to Community Hub",
deleteConfirm: "Are you sure you want to delete this history item?",
clearAllConfirm:
"Are you sure you want to clear all history? This action cannot be undone.",
expand: "Expand",
},
+ publish: {
+ public_description: "Public system prompts are visible to everyone.",
+ private_description: "Private system prompts are only visible to you.",
+ success_title: "Success!",
+ success_description:
+ "Your System Prompt has been published to the Community Hub!",
+ success_thank_you: "Thank you for sharing to the Community!",
+ view_on_hub: "View on Community Hub",
+ modal_title: "Publish System Prompt",
+ name_label: "Name",
+ name_description: "This is the display name of your system prompt.",
+ name_placeholder: "My System Prompt",
+ description_label: "Description",
+ description_description:
+ "This is the description of your system prompt. Use this to describe the purpose of your system prompt.",
+ tags_label: "Tags",
+ tags_description:
+ "Tags are used to label your system prompt for easier searching. You can add multiple tags. Max 5 tags. Max 20 characters per tag.",
+ tags_placeholder: "Type and press Enter to add tags",
+ visibility_label: "Visibility",
+ prompt_label: "Prompt",
+ prompt_description:
+ "This is the actual slash command that will be used to guide the LLM.",
+ prompt_placeholder: "Enter your system prompt here...",
+ publish_button: "Publish to Community Hub",
+ publishing: "Publishing...",
+ unauthenticated: {
+ title: "Authentication Required",
+ description:
+ "You need to authenticate with the AnythingLLM Community Hub before publishing prompts.",
+ button: "Connect to Community Hub",
+ },
+ },
},
refusal: {
title: "Query mode refusal response",
diff --git a/frontend/src/locales/es/common.js b/frontend/src/locales/es/common.js
index bb72a433d13..d808681444b 100644
--- a/frontend/src/locales/es/common.js
+++ b/frontend/src/locales/es/common.js
@@ -235,6 +235,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/fa/common.js b/frontend/src/locales/fa/common.js
index 08599498c3e..4582841f9a3 100644
--- a/frontend/src/locales/fa/common.js
+++ b/frontend/src/locales/fa/common.js
@@ -231,6 +231,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/fr/common.js b/frontend/src/locales/fr/common.js
index cf43f2e9627..e3f3aeabcbb 100644
--- a/frontend/src/locales/fr/common.js
+++ b/frontend/src/locales/fr/common.js
@@ -236,6 +236,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/he/common.js b/frontend/src/locales/he/common.js
index bcb2ab5f969..a0e98218a69 100644
--- a/frontend/src/locales/he/common.js
+++ b/frontend/src/locales/he/common.js
@@ -229,6 +229,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/it/common.js b/frontend/src/locales/it/common.js
index bac64617ec1..7d46fbe9052 100644
--- a/frontend/src/locales/it/common.js
+++ b/frontend/src/locales/it/common.js
@@ -234,6 +234,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/ja/common.js b/frontend/src/locales/ja/common.js
index e6766c3ffc9..9bc9d17c087 100644
--- a/frontend/src/locales/ja/common.js
+++ b/frontend/src/locales/ja/common.js
@@ -240,6 +240,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/ko/common.js b/frontend/src/locales/ko/common.js
index 30ef7fa528d..f4732f61858 100644
--- a/frontend/src/locales/ko/common.js
+++ b/frontend/src/locales/ko/common.js
@@ -229,6 +229,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/lv/common.js b/frontend/src/locales/lv/common.js
index aaab2ffaeea..6736a356d13 100644
--- a/frontend/src/locales/lv/common.js
+++ b/frontend/src/locales/lv/common.js
@@ -325,6 +325,35 @@ const TRANSLATIONS = {
clearAllConfirm:
"Vai tiešām vēlaties nodzēst visu vēsturi? Šo darbību nevar atsaukt.",
expand: "Paplašināt",
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/nl/common.js b/frontend/src/locales/nl/common.js
index fec9e96d7dd..447c7623698 100644
--- a/frontend/src/locales/nl/common.js
+++ b/frontend/src/locales/nl/common.js
@@ -233,6 +233,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/pt_BR/common.js b/frontend/src/locales/pt_BR/common.js
index 343b9d27a28..97734d4a1c6 100644
--- a/frontend/src/locales/pt_BR/common.js
+++ b/frontend/src/locales/pt_BR/common.js
@@ -322,6 +322,35 @@ const TRANSLATIONS = {
clearAllConfirm:
"Tem certeza que deseja limpar todo o histórico? Esta ação é irreversível.",
expand: "Expandir",
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/ru/common.js b/frontend/src/locales/ru/common.js
index fa80dbd13fd..2711d07ed8a 100644
--- a/frontend/src/locales/ru/common.js
+++ b/frontend/src/locales/ru/common.js
@@ -242,6 +242,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/tr/common.js b/frontend/src/locales/tr/common.js
index cf94d5308ee..0b41670a5fe 100644
--- a/frontend/src/locales/tr/common.js
+++ b/frontend/src/locales/tr/common.js
@@ -233,6 +233,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/vn/common.js b/frontend/src/locales/vn/common.js
index a92e3f23fd1..7b61a057717 100644
--- a/frontend/src/locales/vn/common.js
+++ b/frontend/src/locales/vn/common.js
@@ -232,6 +232,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/zh/common.js b/frontend/src/locales/zh/common.js
index dd20b892fce..e971c192ff2 100644
--- a/frontend/src/locales/zh/common.js
+++ b/frontend/src/locales/zh/common.js
@@ -312,6 +312,35 @@ const TRANSLATIONS = {
deleteConfirm: "您确定要删除此历史记录吗?",
clearAllConfirm: "您确定要清除所有历史记录吗?此操作无法撤消。",
expand: "展开",
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/locales/zh_TW/common.js b/frontend/src/locales/zh_TW/common.js
index 2abd8567e67..0e691d205bd 100644
--- a/frontend/src/locales/zh_TW/common.js
+++ b/frontend/src/locales/zh_TW/common.js
@@ -230,6 +230,35 @@ const TRANSLATIONS = {
deleteConfirm: null,
clearAllConfirm: null,
expand: null,
+ publish: null,
+ },
+ publish: {
+ public_description: null,
+ private_description: null,
+ success_title: null,
+ success_description: null,
+ success_thank_you: null,
+ view_on_hub: null,
+ modal_title: null,
+ name_label: null,
+ name_description: null,
+ name_placeholder: null,
+ description_label: null,
+ description_description: null,
+ tags_label: null,
+ tags_description: null,
+ tags_placeholder: null,
+ visibility_label: null,
+ prompt_label: null,
+ prompt_description: null,
+ prompt_placeholder: null,
+ publish_button: null,
+ publishing: null,
+ unauthenticated: {
+ title: null,
+ description: null,
+ button: null,
+ },
},
},
refusal: {
diff --git a/frontend/src/models/communityHub.js b/frontend/src/models/communityHub.js
index 517adbc80db..d3e5f0fb871 100644
--- a/frontend/src/models/communityHub.js
+++ b/frontend/src/models/communityHub.js
@@ -153,6 +153,34 @@ const CommunityHub = {
};
});
},
+
+ /**
+ * Create a new system prompt in the community hub
+ * @param {Object} data - The system prompt data
+ * @param {string} data.name - The name of the prompt
+ * @param {string} data.description - The description of the prompt
+ * @param {string} data.prompt - The actual system prompt text
+ * @param {string[]} data.tags - Array of tags
+ * @param {string} data.visibility - Either 'public' or 'private'
+ * @returns {Promise<{success: boolean, error: string | null}>}
+ */
+ createSystemPrompt: async (data) => {
+ return await fetch(`${API_BASE}/community-hub/system-prompt/create`, {
+ method: "POST",
+ headers: baseHeaders(),
+ body: JSON.stringify(data),
+ })
+ .then(async (res) => {
+ const response = await res.json();
+ if (!res.ok)
+ throw new Error(response.error || "Failed to create system prompt");
+ return { success: true, error: null, itemId: response.item?.id };
+ })
+ .catch((e) => ({
+ success: false,
+ error: e.message,
+ }));
+ },
};
export default CommunityHub;
diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatPromptSettings/ChatPromptHistory/PromptHistoryItem/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatPromptSettings/ChatPromptHistory/PromptHistoryItem/index.jsx
index 956bf40336b..278574a798f 100644
--- a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatPromptSettings/ChatPromptHistory/PromptHistoryItem/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatPromptSettings/ChatPromptHistory/PromptHistoryItem/index.jsx
@@ -14,6 +14,7 @@ export default function PromptHistoryItem({
user,
onRestore,
setHistory,
+ onPublishClick,
}) {
const { t } = useTranslation();
const [showMenu, setShowMenu] = useState(false);
@@ -82,11 +83,21 @@ export default function PromptHistoryItem({
{showMenu && (
+
{prompt !== DEFAULT_PROMPT && (
-
handleRestore(DEFAULT_PROMPT)}
- className="text-theme-text-primary hover:text-white light:hover:text-black text-sm font-medium"
- >
- Clear
-
+ <>
+
handleRestore(DEFAULT_PROMPT)}
+ className="text-theme-text-primary hover:text-white light:hover:text-black text-xs font-medium"
+ >
+ Clear
+
+
{
+ setCurrentPrompt(prompt);
+ openPublishModal();
+ }}
+ />
+ >
)}
+
>
);
}
+
+function PublishPromptCTA({ hidden = false, onClick }) {
+ if (hidden) return null;
+ return (
+
+ Publish to Community Hub
+
+ );
+}
diff --git a/frontend/src/utils/paths.js b/frontend/src/utils/paths.js
index 844a0fbc9f2..1639e536cd3 100644
--- a/frontend/src/utils/paths.js
+++ b/frontend/src/utils/paths.js
@@ -184,6 +184,9 @@ export default {
viewMoreOfType: function (type) {
return `${this.website()}/list/${type}`;
},
+ viewItem: function (type, id) {
+ return `${this.website()}/i/${type}/${id}`;
+ },
trending: () => {
return `/settings/community-hub/trending`;
},
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index 47d7b4c2ed1..3f07e74b059 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -61,6 +61,7 @@ export default {
text: {
primary: 'var(--theme-text-primary)',
secondary: 'var(--theme-text-secondary)',
+ placeholder: 'var(--theme-placeholder)',
},
sidebar: {
item: {
diff --git a/server/endpoints/communityHub.js b/server/endpoints/communityHub.js
index b8f0981ab52..241434a69ab 100644
--- a/server/endpoints/communityHub.js
+++ b/server/endpoints/communityHub.js
@@ -181,6 +181,39 @@ function communityHubEndpoints(app) {
}
}
);
+
+ app.post(
+ "/community-hub/:communityHubItemType/create",
+ [validatedRequest, flexUserRoleValid([ROLES.admin])],
+ async (request, response) => {
+ try {
+ const { communityHubItemType } = request.params;
+ const { connectionKey } = await SystemSettings.hubSettings();
+ if (!connectionKey)
+ throw new Error("Community Hub connection key not found");
+
+ const data = reqBody(request);
+ const { success, error, itemId } = await CommunityHub.createStaticItem(
+ communityHubItemType,
+ data,
+ connectionKey
+ );
+ if (!success) throw new Error(error);
+
+ await EventLogs.logEvent(
+ "community_hub_publish",
+ { itemType: communityHubItemType },
+ response.locals?.user?.id
+ );
+ response
+ .status(200)
+ .json({ success: true, error: null, item: { id: itemId } });
+ } catch (error) {
+ console.error(error);
+ response.status(500).json({ success: false, error: error.message });
+ }
+ }
+ );
}
module.exports = { communityHubEndpoints };
diff --git a/server/models/communityHub.js b/server/models/communityHub.js
index e68cdffaebd..89aa59b6081 100644
--- a/server/models/communityHub.js
+++ b/server/models/communityHub.js
@@ -9,6 +9,7 @@ const CommunityHub = {
process.env.NODE_ENV === "development"
? "http://127.0.0.1:5001/anythingllm-hub/us-central1/external/v1"
: "https://hub.external.anythingllm.com/v1",
+ supportedStaticItemTypes: ["system-prompt"],
/**
* Validate an import ID and return the entity type and ID.
@@ -172,6 +173,41 @@ const CommunityHub = {
return { createdByMe: {}, teamItems: [] };
});
},
+
+ /**
+ * Create a new item in the community hub - Only supports STATIC items for now.
+ * @param {string} itemType - The type of item to create
+ * @param {object} data - The item data
+ * @param {string} connectionKey - The hub connection key
+ * @returns {Promise<{success: boolean, error: string | null}>}
+ */
+ createStaticItem: async function (itemType, data, connectionKey) {
+ if (!connectionKey)
+ return { success: false, error: "Connection key is required" };
+ if (!this.supportedStaticItemTypes.includes(itemType))
+ return { success: false, error: "Unsupported item type" };
+
+ // If the item has specical considerations or preprocessing, we can delegate that below before sending the request.
+ // eg: Agent flow files and such.
+
+ return await fetch(`${this.apiBase}/${itemType}/create`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${connectionKey}`,
+ },
+ body: JSON.stringify(data),
+ })
+ .then((response) => response.json())
+ .then((result) => {
+ if (!!result.error) throw new Error(result.error || "Unknown error");
+ return { success: true, error: null, itemId: result.item.id };
+ })
+ .catch((error) => {
+ console.error(`Error creating ${itemType}:`, error);
+ return { success: false, error: error.message };
+ });
+ },
};
module.exports = { CommunityHub };