mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2025-03-13 05:32:24 +00:00
Merge b2205eb443
into 4028b5a652
This commit is contained in:
commit
69f8a6cee8
20 changed files with 240 additions and 60 deletions
|
@ -3,10 +3,12 @@ import paths from "@/utils/paths";
|
|||
import { ArrowUUpLeft, Wrench } from "@phosphor-icons/react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useMatch } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function SettingsButton() {
|
||||
const isInSettings = !!useMatch("/settings/*");
|
||||
const { user } = useUser();
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (user && user?.role === "default") return null;
|
||||
|
||||
|
@ -18,7 +20,7 @@ export default function SettingsButton() {
|
|||
className="transition-all duration-300 p-2 rounded-full bg-theme-sidebar-footer-icon hover:bg-theme-sidebar-footer-icon-hover"
|
||||
aria-label="Home"
|
||||
data-tooltip-id="footer-item"
|
||||
data-tooltip-content="Back to workspaces"
|
||||
data-tooltip-content={t("settings.back-to-workspaces")}
|
||||
>
|
||||
<ArrowUUpLeft
|
||||
className="h-5 w-5"
|
||||
|
@ -37,7 +39,7 @@ export default function SettingsButton() {
|
|||
// className="transition-all duration-300 p-2 rounded-full bg-sidebar-button hover:bg-menu-item-selected-gradient hover:border-slate-100 hover:border-opacity-50 border-transparent border"
|
||||
aria-label="Settings"
|
||||
data-tooltip-id="footer-item"
|
||||
data-tooltip-content="Open settings"
|
||||
data-tooltip-content={t("settings.open")}
|
||||
>
|
||||
<Wrench
|
||||
className="h-5 w-5"
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import truncate from "truncate";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const THREAD_CALLOUT_DETAIL_WIDTH = 26;
|
||||
export default function ThreadItem({
|
||||
|
@ -27,6 +28,7 @@ export default function ThreadItem({
|
|||
const { slug, threadSlug = null } = useParams();
|
||||
const optionsContainer = useRef(null);
|
||||
const [showOptions, setShowOptions] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const linkTo = !thread.slug
|
||||
? paths.workspace.chat(slug)
|
||||
: paths.workspace.thread(slug, thread.slug);
|
||||
|
@ -126,7 +128,7 @@ export default function ThreadItem({
|
|||
type="button"
|
||||
className="border-none"
|
||||
onClick={() => setShowOptions(!showOptions)}
|
||||
aria-label="Thread options"
|
||||
aria-label={t("threads.options")}
|
||||
>
|
||||
<DotsThree
|
||||
className="text-slate-300 light:text-theme-text-secondary hover:text-white hover:light:text-theme-text-primary"
|
||||
|
@ -161,6 +163,7 @@ function OptionsMenu({
|
|||
currentThreadSlug,
|
||||
}) {
|
||||
const menuRef = useRef(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Ref menu options
|
||||
const outsideClick = (e) => {
|
||||
|
@ -196,7 +199,7 @@ function OptionsMenu({
|
|||
|
||||
const renameThread = async () => {
|
||||
const name = window
|
||||
.prompt("What would you like to rename this thread to?")
|
||||
.prompt(t("threads.rename_question"))
|
||||
?.trim();
|
||||
if (!name || name.length === 0) {
|
||||
close();
|
||||
|
@ -221,19 +224,14 @@ function OptionsMenu({
|
|||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
if (
|
||||
!window.confirm(
|
||||
"Are you sure you want to delete this thread? All of its chats will be deleted. You cannot undo this."
|
||||
)
|
||||
)
|
||||
return;
|
||||
if (!window.confirm(t("threads.delete.question"))) return;
|
||||
const success = await Workspace.threads.delete(workspace.slug, thread.slug);
|
||||
if (!success) {
|
||||
showToast("Thread could not be deleted!", "error", { clear: true });
|
||||
showToast(t("threads.delete.failure"), "error", { clear: true });
|
||||
return;
|
||||
}
|
||||
if (success) {
|
||||
showToast("Thread deleted successfully!", "success", { clear: true });
|
||||
showToast(t("threads.delete.success"), "success", { clear: true });
|
||||
onRemove(thread.id);
|
||||
// Redirect if deleting the active thread
|
||||
if (currentThreadSlug === thread.slug) {
|
||||
|
@ -254,7 +252,7 @@ function OptionsMenu({
|
|||
className="w-full rounded-md flex items-center p-2 gap-x-2 hover:bg-slate-500/20 text-slate-300 light:text-theme-text-primary"
|
||||
>
|
||||
<PencilSimple size={18} />
|
||||
<p className="text-sm">Rename</p>
|
||||
<p className="text-sm">{t("threads.rename")}</p>
|
||||
</button>
|
||||
<button
|
||||
onClick={handleDelete}
|
||||
|
@ -262,7 +260,7 @@ function OptionsMenu({
|
|||
className="w-full rounded-md flex items-center p-2 gap-x-2 hover:bg-red-500/20 text-slate-300 light:text-theme-text-primary hover:text-red-100"
|
||||
>
|
||||
<Trash size={18} />
|
||||
<p className="text-sm">Delete Thread</p>
|
||||
<p className="text-sm">{t("threads.delete.delete")}</p>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Plus, CircleNotch, Trash } from "@phosphor-icons/react";
|
|||
import { useEffect, useState } from "react";
|
||||
import ThreadItem from "./ThreadItem";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
export const THREAD_RENAME_EVENT = "renameThread";
|
||||
|
||||
export default function ThreadContainer({ workspace }) {
|
||||
|
@ -158,6 +159,8 @@ export default function ThreadContainer({ workspace }) {
|
|||
|
||||
function NewThreadButton({ workspace }) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onClick = async () => {
|
||||
setLoading(true);
|
||||
const { thread, error } = await Workspace.threads.new(workspace.slug);
|
||||
|
@ -195,11 +198,11 @@ function NewThreadButton({ workspace }) {
|
|||
|
||||
{loading ? (
|
||||
<p className="text-left text-white light:text-theme-text-primary text-sm">
|
||||
Starting Thread...
|
||||
{t("threads.start")}
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-left text-white light:text-theme-text-primary text-sm">
|
||||
New Thread
|
||||
{t("threads.new")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
@ -208,6 +211,7 @@ function NewThreadButton({ workspace }) {
|
|||
}
|
||||
|
||||
function DeleteAllThreadButton({ ctrlPressed, threads, onDelete }) {
|
||||
const { t } = useTranslation();
|
||||
if (!ctrlPressed || threads.filter((t) => t.deleted).length === 0)
|
||||
return null;
|
||||
return (
|
||||
|
@ -225,7 +229,7 @@ function DeleteAllThreadButton({ ctrlPressed, threads, onDelete }) {
|
|||
/>
|
||||
</div>
|
||||
<p className="text-white light:text-theme-text-secondary text-left text-sm group-hover:text-red-400">
|
||||
Delete Selected
|
||||
{t("threads.delete.selected")}
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
} from "@phosphor-icons/react";
|
||||
import ConfluenceLogo from "@/media/dataConnectors/confluence.png";
|
||||
import { toPercentString } from "@/utils/numbers";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function combineLikeSources(sources) {
|
||||
const combined = {};
|
||||
|
@ -36,9 +37,10 @@ function combineLikeSources(sources) {
|
|||
}
|
||||
|
||||
export default function Citations({ sources = [] }) {
|
||||
if (sources.length === 0) return null;
|
||||
const [open, setOpen] = useState(false);
|
||||
const [selectedSource, setSelectedSource] = useState(null);
|
||||
const { t } = useTranslation();
|
||||
if (sources.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col mt-4 justify-left">
|
||||
|
@ -48,7 +50,7 @@ export default function Citations({ sources = [] }) {
|
|||
open ? "pb-2" : ""
|
||||
} hover:text-white/75 hover:light:text-black/75 transition-all duration-300`}
|
||||
>
|
||||
{open ? "Hide Citations" : "Show Citations"}
|
||||
{open ? t("citations.hide") : t("citations.show")}
|
||||
<CaretRight
|
||||
className={`w-3.5 h-3.5 inline-block ml-1 transform transition-transform duration-300 ${
|
||||
open ? "rotate-90" : ""
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { Trash, DotsThreeVertical, TreeView } from "@phosphor-icons/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function ActionMenu({ chatId, forkThread, isEditing, role }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const menuRef = useRef(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const toggleMenu = () => setOpen(!open);
|
||||
|
||||
|
@ -40,8 +42,8 @@ function ActionMenu({ chatId, forkThread, isEditing, role }) {
|
|||
onClick={toggleMenu}
|
||||
className="border-none text-[var(--theme-sidebar-footer-icon-fill)] hover:text-[var(--theme-sidebar-footer-icon-fill)] transition-colors duration-200"
|
||||
data-tooltip-id="action-menu"
|
||||
data-tooltip-content="More actions"
|
||||
aria-label="More actions"
|
||||
data-tooltip-content={t("general.message.more-actions")}
|
||||
aria-label="general.message.more-actions"
|
||||
>
|
||||
<DotsThreeVertical size={24} weight="bold" />
|
||||
</button>
|
||||
|
@ -52,14 +54,14 @@ function ActionMenu({ chatId, forkThread, isEditing, role }) {
|
|||
className="border-none rounded-t-lg flex items-center text-white gap-x-2 hover:bg-theme-action-menu-item-hover py-1.5 px-2 transition-colors duration-200 w-full text-left"
|
||||
>
|
||||
<TreeView size={18} />
|
||||
<span className="text-sm">Fork</span>
|
||||
<span className="text-sm">{t("general.message.fork")}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={handleDelete}
|
||||
className="border-none flex rounded-b-lg items-center text-white gap-x-2 hover:bg-theme-action-menu-item-hover py-1.5 px-2 transition-colors duration-200 w-full text-left"
|
||||
>
|
||||
<Trash size={18} />
|
||||
<span className="text-sm">Delete</span>
|
||||
<span className="text-sm">{t("general.message.delete")}</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import { Trash } from "@phosphor-icons/react";
|
||||
import Workspace from "@/models/workspace";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const DELETE_EVENT = "delete-message";
|
||||
|
||||
|
@ -39,6 +40,7 @@ export function useWatchDeleteMessage({ chatId = null, role = "user" }) {
|
|||
}
|
||||
|
||||
export function DeleteMessage({ chatId, isEditing, role }) {
|
||||
const { t } = useTranslation();
|
||||
if (!chatId || isEditing || role === "user") return null;
|
||||
|
||||
function emitDeleteEvent() {
|
||||
|
@ -52,7 +54,7 @@ export function DeleteMessage({ chatId, isEditing, role }) {
|
|||
role="menuitem"
|
||||
>
|
||||
<Trash size={21} weight="fill" />
|
||||
<p>Delete</p>
|
||||
<p>{t("general.message.delete")}</p>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Pencil } from "@phosphor-icons/react";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const EDIT_EVENT = "toggle-message-edit";
|
||||
|
||||
|
@ -29,6 +30,7 @@ export function useEditMessage({ chatId, role }) {
|
|||
}
|
||||
|
||||
export function EditMessageAction({ chatId = null, role, isEditing }) {
|
||||
const { t } = useTranslation();
|
||||
function handleEditClick() {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(EDIT_EVENT, { detail: { chatId, role } })
|
||||
|
@ -45,11 +47,13 @@ export function EditMessageAction({ chatId = null, role, isEditing }) {
|
|||
<button
|
||||
onClick={handleEditClick}
|
||||
data-tooltip-id="edit-input-text"
|
||||
data-tooltip-content={`Edit ${
|
||||
role === "user" ? "Prompt" : "Response"
|
||||
} `}
|
||||
data-tooltip-content={t(
|
||||
`general.message.edit.${role === "user" ? "prompt" : "response"}`
|
||||
)}
|
||||
className="border-none text-zinc-300"
|
||||
aria-label={`Edit ${role === "user" ? "Prompt" : "Response"}`}
|
||||
aria-label={t(
|
||||
`general.message.edit.${role === "user" ? "prompt" : "response"}`
|
||||
)}
|
||||
>
|
||||
<Pencil
|
||||
color="var(--theme-sidebar-footer-icon-fill)"
|
||||
|
|
|
@ -2,12 +2,14 @@ import { useEffect, useState, useRef } from "react";
|
|||
import { SpeakerHigh, PauseCircle, CircleNotch } from "@phosphor-icons/react";
|
||||
import Workspace from "@/models/workspace";
|
||||
import showToast from "@/utils/toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function AsyncTTSMessage({ slug, chatId }) {
|
||||
const playerRef = useRef(null);
|
||||
const [speaking, setSpeaking] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [audioSrc, setAudioSrc] = useState(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
function speakMessage() {
|
||||
if (speaking) {
|
||||
|
@ -58,10 +60,14 @@ export default function AsyncTTSMessage({ slug, chatId }) {
|
|||
onClick={speakMessage}
|
||||
data-tooltip-id="message-to-speech"
|
||||
data-tooltip-content={
|
||||
speaking ? "Pause TTS speech of message" : "TTS Speak message"
|
||||
speaking
|
||||
? t("chat-history.tts.pause-tts")
|
||||
: t("chat-history.tts.start-tts")
|
||||
}
|
||||
className="border-none text-[var(--theme-sidebar-footer-icon-fill)]"
|
||||
aria-label={speaking ? "Pause speech" : "Speak message"}
|
||||
aria-label={
|
||||
speaking ? t("chat-history.tts.pause") : t("chat-history.tts.start")
|
||||
}
|
||||
>
|
||||
{speaking ? (
|
||||
<PauseCircle size={18} className="mb-1" />
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { SpeakerHigh, PauseCircle } from "@phosphor-icons/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function NativeTTSMessage({ message }) {
|
||||
const [speaking, setSpeaking] = useState(false);
|
||||
const [supported, setSupported] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
setSupported("speechSynthesis" in window);
|
||||
}, []);
|
||||
|
@ -38,10 +41,14 @@ export default function NativeTTSMessage({ message }) {
|
|||
onClick={speakMessage}
|
||||
data-tooltip-id="message-to-speech"
|
||||
data-tooltip-content={
|
||||
speaking ? "Pause TTS speech of message" : "TTS Speak message"
|
||||
speaking
|
||||
? t("chat-history.tts.pause-tts")
|
||||
: t("chat-history.tts.start-tts")
|
||||
}
|
||||
className="border-none text-[var(--theme-sidebar-footer-icon-fill)]"
|
||||
aria-label={speaking ? "Pause speech" : "Speak message"}
|
||||
aria-label={
|
||||
speaking ? t("chat-history.tts.pause") : t("chat-history.tts.start")
|
||||
}
|
||||
>
|
||||
{speaking ? (
|
||||
<PauseCircle size={18} className="mb-1" />
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { useEffect, useState, useRef } from "react";
|
||||
import { SpeakerHigh, PauseCircle, CircleNotch } from "@phosphor-icons/react";
|
||||
import PiperTTSClient from "@/utils/piperTTS";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function PiperTTS({ voiceId = null, message }) {
|
||||
const playerRef = useRef(null);
|
||||
const [speaking, setSpeaking] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [audioSrc, setAudioSrc] = useState(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
async function speakMessage(e) {
|
||||
e.preventDefault();
|
||||
|
@ -55,10 +57,14 @@ export default function PiperTTS({ voiceId = null, message }) {
|
|||
disabled={loading}
|
||||
data-tooltip-id="message-to-speech"
|
||||
data-tooltip-content={
|
||||
speaking ? "Pause TTS speech of message" : "TTS Speak message"
|
||||
speaking
|
||||
? t("chat-history.tts.pause-tts")
|
||||
: t("chat-history.tts.start-tts")
|
||||
}
|
||||
className="border-none text-[var(--theme-sidebar-footer-icon-fill)]"
|
||||
aria-label={speaking ? "Pause speech" : "Speak message"}
|
||||
aria-label={
|
||||
speaking ? t("chat-history.tts.pause") : t("chat-history.tts.start")
|
||||
}
|
||||
>
|
||||
{speaking ? (
|
||||
<PauseCircle size={18} className="mb-1" />
|
||||
|
|
|
@ -5,6 +5,7 @@ import Workspace from "@/models/workspace";
|
|||
import { EditMessageAction } from "./EditMessage";
|
||||
import RenderMetrics from "./RenderMetrics";
|
||||
import ActionMenu from "./ActionMenu";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const Actions = ({
|
||||
message,
|
||||
|
@ -19,6 +20,7 @@ const Actions = ({
|
|||
metrics = {},
|
||||
}) => {
|
||||
const [selectedFeedback, setSelectedFeedback] = useState(feedbackScore);
|
||||
const { t } = useTranslation();
|
||||
const handleFeedback = async (newFeedback) => {
|
||||
const updatedFeedback =
|
||||
selectedFeedback === newFeedback ? null : newFeedback;
|
||||
|
@ -48,7 +50,7 @@ const Actions = ({
|
|||
isSelected={selectedFeedback === true}
|
||||
handleFeedback={() => handleFeedback(true)}
|
||||
tooltipId="feedback-button"
|
||||
tooltipContent="Good response"
|
||||
tooltipContent={t("general.message.feedback.good-response")}
|
||||
IconComponent={ThumbsUp}
|
||||
/>
|
||||
)}
|
||||
|
@ -93,6 +95,7 @@ function FeedbackButton({
|
|||
|
||||
function CopyMessage({ message }) {
|
||||
const { copied, copyText } = useCopyText();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -100,9 +103,9 @@ function CopyMessage({ message }) {
|
|||
<button
|
||||
onClick={() => copyText(message)}
|
||||
data-tooltip-id="copy-assistant-text"
|
||||
data-tooltip-content="Copy"
|
||||
data-tooltip-content={t("general.message.feedback.copy")}
|
||||
className="text-zinc-300"
|
||||
aria-label="Copy"
|
||||
aria-label={t("general.message.feedback.copy")}
|
||||
>
|
||||
{copied ? (
|
||||
<Check
|
||||
|
@ -124,15 +127,16 @@ function CopyMessage({ message }) {
|
|||
}
|
||||
|
||||
function RegenerateMessage({ regenerateMessage, chatId }) {
|
||||
const { t } = useTranslation();
|
||||
if (!chatId) return null;
|
||||
return (
|
||||
<div className="mt-3 relative">
|
||||
<button
|
||||
onClick={() => regenerateMessage(chatId)}
|
||||
data-tooltip-id="regenerate-assistant-text"
|
||||
data-tooltip-content="Regenerate response"
|
||||
data-tooltip-content={t("general.message.feedback.regenerate")}
|
||||
className="border-none text-zinc-300"
|
||||
aria-label="Regenerate"
|
||||
aria-label={t("general.message.feedback.regenerate")}
|
||||
>
|
||||
<ArrowsClockwise
|
||||
color="var(--theme-sidebar-footer-icon-fill)"
|
||||
|
|
|
@ -13,6 +13,7 @@ import { useParams } from "react-router-dom";
|
|||
import paths from "@/utils/paths";
|
||||
import Appearance from "@/models/appearance";
|
||||
import useTextSize from "@/hooks/useTextSize";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
export default function ChatHistory({
|
||||
|
@ -33,6 +34,7 @@ export default function ChatHistory({
|
|||
const isStreaming = history[history.length - 1]?.animate;
|
||||
const { showScrollbar } = Appearance.getSettings();
|
||||
const { textSizeClass } = useTextSize();
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isUserScrolling && (isAtBottom || isStreaming)) {
|
||||
|
@ -179,22 +181,28 @@ export default function ChatHistory({
|
|||
<div className="flex flex-col h-full md:mt-0 pb-44 md:pb-40 w-full justify-end items-center">
|
||||
<div className="flex flex-col items-center md:items-start md:max-w-[600px] w-full px-4">
|
||||
<p className="text-white/60 text-lg font-base py-4">
|
||||
Welcome to your new workspace.
|
||||
{t("chat-history.welcome")}
|
||||
</p>
|
||||
{!user || user.role !== "default" ? (
|
||||
<p className="w-full items-center text-white/60 text-lg font-base flex flex-col md:flex-row gap-x-1">
|
||||
To get started either{" "}
|
||||
{t("chat-history.get-started-either")}{" "}
|
||||
<span
|
||||
className="underline font-medium cursor-pointer"
|
||||
onClick={showModal}
|
||||
>
|
||||
upload a document
|
||||
{t("chat-history.upload-doc")}
|
||||
</span>
|
||||
or <b className="font-medium italic">send a chat.</b>
|
||||
{t("chat-history.or")}{" "}
|
||||
<b className="font-medium italic">
|
||||
{t("chat-history.send-chat")}
|
||||
</b>
|
||||
</p>
|
||||
) : (
|
||||
<p className="w-full items-center text-white/60 text-lg font-base flex flex-col md:flex-row gap-x-1">
|
||||
To get started <b className="font-medium italic">send a chat.</b>
|
||||
{t("chat-history.get-started")}{" "}
|
||||
<b className="font-medium italic">
|
||||
{t("chat-history.send-chat")}
|
||||
</b>
|
||||
</p>
|
||||
)}
|
||||
<WorkspaceChatSuggestions
|
||||
|
|
|
@ -2,16 +2,19 @@ import { useEffect, useRef, useState } from "react";
|
|||
import { Tooltip } from "react-tooltip";
|
||||
import { At } from "@phosphor-icons/react";
|
||||
import { useIsAgentSessionActive } from "@/utils/chat/agent";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function AvailableAgentsButton({ showing, setShowAgents }) {
|
||||
const agentSessionActive = useIsAgentSessionActive();
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (agentSessionActive) return null;
|
||||
return (
|
||||
<div
|
||||
id="agent-list-btn"
|
||||
data-tooltip-id="tooltip-agent-list-btn"
|
||||
data-tooltip-content="View all available agents you can use for chatting."
|
||||
aria-label="View all available agents you can use for chatting."
|
||||
data-tooltip-content={t("agent.menu.available")}
|
||||
aria-label={t("agent.menu.available")}
|
||||
onClick={() => setShowAgents(!showing)}
|
||||
className={`flex justify-center items-center cursor-pointer ${
|
||||
showing ? "!opacity-100" : ""
|
||||
|
@ -47,6 +50,8 @@ export function AvailableAgents({
|
|||
}) {
|
||||
const formRef = useRef(null);
|
||||
const agentSessionActive = useIsAgentSessionActive();
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
function listenForOutsideClick() {
|
||||
if (!showing || !formRef.current) return false;
|
||||
|
@ -81,7 +86,7 @@ export function AvailableAgents({
|
|||
>
|
||||
<div className="w-full flex-col text-left flex pointer-events-none">
|
||||
<div className="text-theme-text-primary text-sm">
|
||||
<b>@agent</b> - the default agent for this workspace.
|
||||
<b>@agent</b>{t("agent.menu.default")}
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2 mt-2">
|
||||
<AbilityTag text="rag-search" />
|
||||
|
@ -101,7 +106,7 @@ export function AvailableAgents({
|
|||
>
|
||||
<div className="w-full flex-col text-center flex pointer-events-none">
|
||||
<div className="text-theme-text-secondary text-xs italic">
|
||||
custom agents are coming soon!
|
||||
{t("agent.menu.custom")}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import useUser from "@/hooks/useUser";
|
||||
import { PaperclipHorizontal } from "@phosphor-icons/react";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
/**
|
||||
* This is a simple proxy component that clicks on the DnD file uploader for the user.
|
||||
|
@ -8,6 +9,7 @@ import { Tooltip } from "react-tooltip";
|
|||
*/
|
||||
export default function AttachItem() {
|
||||
const { user } = useUser();
|
||||
const { t } = useTranslation();
|
||||
if (!!user && user.role === "default") return null;
|
||||
|
||||
return (
|
||||
|
@ -15,13 +17,12 @@ export default function AttachItem() {
|
|||
<button
|
||||
id="attach-item-btn"
|
||||
data-tooltip-id="attach-item-btn"
|
||||
data-tooltip-content="Attach a file to this chat"
|
||||
aria-label="Attach a file to this chat"
|
||||
data-tooltip-content={t("chat.prompt.attach")}
|
||||
aria-label={t("chat.prompt.attach")}
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e?.target?.blur();
|
||||
document?.getElementById("dnd-chat-file-uploader")?.click();
|
||||
return;
|
||||
}}
|
||||
className={`border-none relative flex justify-center items-center opacity-60 hover:opacity-100 light:opacity-100 light:hover:opacity-60 cursor-pointer`}
|
||||
>
|
||||
|
|
|
@ -4,13 +4,15 @@ import { Tooltip } from "react-tooltip";
|
|||
import ResetCommand from "./reset";
|
||||
import EndAgentSession from "./endAgentSession";
|
||||
import SlashPresets from "./SlashPresets";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function SlashCommandsButton({ showing, setShowSlashCommand }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div
|
||||
id="slash-cmd-btn"
|
||||
data-tooltip-id="tooltip-slash-cmd-btn"
|
||||
data-tooltip-content="View all available slash commands for chatting."
|
||||
data-tooltip-content={t("chat.prompt.commands")}
|
||||
onClick={() => setShowSlashCommand(!showing)}
|
||||
className={`flex justify-center items-center cursor-pointer ${
|
||||
showing ? "!opacity-100" : ""
|
||||
|
|
|
@ -2,6 +2,7 @@ import { useEffect, useCallback } from "react";
|
|||
import { Microphone } from "@phosphor-icons/react";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import _regeneratorRuntime from "regenerator-runtime";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import SpeechRecognition, {
|
||||
useSpeechRecognition,
|
||||
} from "react-speech-recognition";
|
||||
|
@ -20,6 +21,7 @@ export default function SpeechToText({ sendCommand }) {
|
|||
} = useSpeechRecognition({
|
||||
clearTranscriptOnListen: true,
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
|
||||
function startSTTSession() {
|
||||
if (!isMicrophoneAvailable) {
|
||||
|
@ -95,8 +97,8 @@ export default function SpeechToText({ sendCommand }) {
|
|||
<div
|
||||
id="text-size-btn"
|
||||
data-tooltip-id="tooltip-text-size-btn"
|
||||
data-tooltip-content="Speak your prompt"
|
||||
aria-label="Speak your prompt"
|
||||
data-tooltip-content={t("chat.prompt.speak")}
|
||||
aria-label={t("chat.prompt.speak")}
|
||||
onClick={listening ? endTTSSession : startSTTSession}
|
||||
className={`border-none relative flex justify-center items-center opacity-60 hover:opacity-100 light:opacity-100 light:hover:opacity-60 cursor-pointer ${
|
||||
!!listening ? "!opacity-100" : ""
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { useState, useRef, useEffect } from "react";
|
||||
import { TextT } from "@phosphor-icons/react";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function TextSizeButton() {
|
||||
const [showTextSizeMenu, setShowTextSizeMenu] = useState(false);
|
||||
const buttonRef = useRef(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -12,8 +14,8 @@ export default function TextSizeButton() {
|
|||
ref={buttonRef}
|
||||
id="text-size-btn"
|
||||
data-tooltip-id="tooltip-text-size-btn"
|
||||
data-tooltip-content="Change text size"
|
||||
aria-label="Change text size"
|
||||
data-tooltip-content={t("chat.prompt.text-size")}
|
||||
aria-label={t("chat.prompt.text-size")}
|
||||
onClick={() => setShowTextSizeMenu(!showTextSizeMenu)}
|
||||
className={`border-none relative flex justify-center items-center opacity-60 hover:opacity-100 light:opacity-100 light:hover:opacity-60 cursor-pointer ${
|
||||
showTextSizeMenu ? "!opacity-100" : ""
|
||||
|
|
|
@ -17,6 +17,7 @@ import AttachmentManager from "./Attachments";
|
|||
import AttachItem from "./AttachItem";
|
||||
import { PASTE_ATTACHMENT_EVENT } from "../DnDWrapper";
|
||||
import useTextSize from "@/hooks/useTextSize";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const PROMPT_INPUT_EVENT = "set_prompt_input";
|
||||
const MAX_EDIT_STACK_SIZE = 100;
|
||||
|
@ -38,6 +39,7 @@ export default function PromptInput({
|
|||
const undoStack = useRef([]);
|
||||
const redoStack = useRef([]);
|
||||
const { textSizeClass } = useTextSize();
|
||||
const { t } = useTranslation();
|
||||
|
||||
/**
|
||||
* To prevent too many re-renders we remotely listen for updates from the parent
|
||||
|
@ -272,7 +274,7 @@ export default function PromptInput({
|
|||
}}
|
||||
value={promptInput}
|
||||
className={`border-none cursor-text max-h-[50vh] md:max-h-[350px] md:min-h-[40px] mx-2 md:mx-0 pt-[12px] w-full leading-5 md:text-md text-white bg-transparent placeholder:text-white/60 light:placeholder:text-theme-text-primary resize-none active:outline-none focus:outline-none flex-grow ${textSizeClass}`}
|
||||
placeholder={"Send a message"}
|
||||
placeholder={t("chat.prompt.input.send-message")}
|
||||
/>
|
||||
{buttonDisabled ? (
|
||||
<StopGenerationButton />
|
||||
|
@ -283,15 +285,15 @@ export default function PromptInput({
|
|||
type="submit"
|
||||
className="border-none inline-flex justify-center rounded-2xl cursor-pointer opacity-60 hover:opacity-100 light:opacity-100 light:hover:opacity-60 ml-4"
|
||||
data-tooltip-id="send-prompt"
|
||||
data-tooltip-content="Send prompt message to workspace"
|
||||
aria-label="Send prompt message to workspace"
|
||||
data-tooltip-content={t("chat.prompt.input.send-prompt-to-workspace")}
|
||||
aria-label={t("chat.prompt.input.send-prompt-to-workspace")}
|
||||
>
|
||||
<PaperPlaneRight
|
||||
color="var(--theme-sidebar-footer-icon-fill)"
|
||||
className="w-[22px] h-[22px] pointer-events-none text-theme-text-primary"
|
||||
weight="fill"
|
||||
/>
|
||||
<span className="sr-only">Send message</span>
|
||||
<span className="sr-only">{t("chat.prompt.input.send-message")}</span>
|
||||
</button>
|
||||
<Tooltip
|
||||
id="send-prompt"
|
||||
|
|
|
@ -89,6 +89,8 @@ const TRANSLATIONS = {
|
|||
"experimental-features": "Experimentelle Funktionen",
|
||||
contact: "Support kontaktieren",
|
||||
"browser-extension": "Browser-Erweiterung",
|
||||
"back-to-workspaces": "zurück zum Arbeitsbereich",
|
||||
open: "Einstellungen öffnen",
|
||||
},
|
||||
login: {
|
||||
"multi-user": {
|
||||
|
@ -159,8 +161,20 @@ const TRANSLATIONS = {
|
|||
"Passen Sie die Nachrichten an, die Ihren Arbeitsbereich-Benutzern vorgeschlagen werden.",
|
||||
add: "Neue Nachricht hinzufügen",
|
||||
save: "Nachrichten speichern",
|
||||
delete: "Löschen",
|
||||
fork: "Aufteilen",
|
||||
"more-actions": "Weitere Aktionen",
|
||||
heading: "Erkläre mir",
|
||||
body: "die Vorteile von AnythingLLM",
|
||||
edit: {
|
||||
prompt: "Prompt bearbeiten",
|
||||
response: "Antwort bearbeiten",
|
||||
},
|
||||
feedback: {
|
||||
"good-response": "Gute Antwort",
|
||||
regenerate: "Neu erzeugen",
|
||||
copy: "Kopieren",
|
||||
},
|
||||
},
|
||||
pfp: {
|
||||
title: "Assistent-Profilbild",
|
||||
|
@ -220,6 +234,14 @@ const TRANSLATIONS = {
|
|||
title: "Prompt",
|
||||
description:
|
||||
"Der Prompt, der in diesem Arbeitsbereich verwendet wird. Definieren Sie den Kontext und die Anweisungen für die KI, um eine Antwort zu generieren. Sie sollten einen sorgfältig formulierten Prompt bereitstellen, damit die KI eine relevante und genaue Antwort generieren kann.",
|
||||
input: {
|
||||
"send-message": "Nachricht abschicken",
|
||||
"send-prompt-to-workspace": "Prompt an Workspace senden",
|
||||
},
|
||||
attach: "Datei an Chat anhängen",
|
||||
commands: "Verfügbare Slash-Befehle für Chat anzeigen",
|
||||
"text-size": "Schriftgröße ändern",
|
||||
speak: "Mündliche Eingabe",
|
||||
},
|
||||
refusal: {
|
||||
title: "Abfragemodus-Ablehnungsantwort",
|
||||
|
@ -320,6 +342,12 @@ const TRANSLATIONS = {
|
|||
"Die Websuche während Agentensitzungen funktioniert erst, wenn dies eingerichtet ist.",
|
||||
},
|
||||
},
|
||||
menu: {
|
||||
available:
|
||||
"Verfügbare Agenten, die zum Chatten verwendet werden können, anzeigen.",
|
||||
default: " - Standardagent für diesen Arbeitsbereich.",
|
||||
custom: "dedizierte Agenten gibt es bald!",
|
||||
},
|
||||
},
|
||||
recorded: {
|
||||
title: "Arbeitsbereich-Chats",
|
||||
|
@ -507,6 +535,39 @@ const TRANSLATIONS = {
|
|||
vector: "Vektordatenbank",
|
||||
anonymous: "Anonyme Telemetrie aktiviert",
|
||||
},
|
||||
"chat-history": {
|
||||
welcome: "Willkommen in Ihrem Arbeitsbereich.",
|
||||
"get-started-either": "Zum Anfang:",
|
||||
"upload-doc": "Dokument hochladen",
|
||||
or: "oder",
|
||||
"send-chat": "Chat abschicken.",
|
||||
"get-started": "Zum Anfang: ,",
|
||||
tts: {
|
||||
"start-tts": "TTS Nachricht vorlesen",
|
||||
"pause-tts": "TTS Nachricht pausieren",
|
||||
start: "Nachricht vorlesen",
|
||||
pause: "Nachricht pausieren",
|
||||
},
|
||||
},
|
||||
citations: {
|
||||
hide: "Zitate ausblenden",
|
||||
show: "Zitate anzeigen",
|
||||
},
|
||||
threads: {
|
||||
new: "Neuer Thread",
|
||||
start: "Thread startet ...",
|
||||
delete: {
|
||||
selected: "Ausgewählte Threads löschen",
|
||||
delete: "Thread löschen",
|
||||
question:
|
||||
"Möchten Sie diesen Thread wirklich löschen? Alle Chats in diesem Thread werden gelöscht. Diese Aktion ist irreversibel.",
|
||||
success: "Thread erfolgreich gelöscht!",
|
||||
failure: "Thread konnte nicht gelöscht werden!",
|
||||
},
|
||||
options: "Thread Optionen",
|
||||
rename: "Umbenennen",
|
||||
rename_question: "Wie möchten Sie den Thread umbenennen?",
|
||||
},
|
||||
};
|
||||
|
||||
export default TRANSLATIONS;
|
||||
|
|
|
@ -100,6 +100,8 @@ const TRANSLATIONS = {
|
|||
"experimental-features": "Experimental Features",
|
||||
contact: "Contact Support",
|
||||
"browser-extension": "Browser Extension",
|
||||
"back-to-workspaces": "Back to Workspaces",
|
||||
open: "Open settings",
|
||||
},
|
||||
|
||||
// Page Definitions
|
||||
|
@ -178,8 +180,20 @@ const TRANSLATIONS = {
|
|||
"Customize the messages that will be suggested to your workspace users.",
|
||||
add: "Add new message",
|
||||
save: "Save Messages",
|
||||
delete: "Delete",
|
||||
fork: "Fork",
|
||||
"more-actions": "More actions",
|
||||
heading: "Explain to me",
|
||||
body: "the benefits of AnythingLLM",
|
||||
edit: {
|
||||
prompt: "Edit Prompt",
|
||||
response: "Edit Response",
|
||||
},
|
||||
feedback: {
|
||||
"good-response": "Good response",
|
||||
regenerate: "Regenerate",
|
||||
copy: "Copy",
|
||||
},
|
||||
},
|
||||
pfp: {
|
||||
title: "Assistant Profile Image",
|
||||
|
@ -241,6 +255,14 @@ const TRANSLATIONS = {
|
|||
title: "Prompt",
|
||||
description:
|
||||
"The prompt that will be used on this workspace. Define the context and instructions for the AI to generate a response. You should to provide a carefully crafted prompt so the AI can generate a relevant and accurate response.",
|
||||
input: {
|
||||
"send-message": "Send a message",
|
||||
"send-prompt-to-workspace": "Send prompt message to workspace",
|
||||
},
|
||||
attach: "Attach a file to this chat",
|
||||
commands: "View all available slash commands for chatting.",
|
||||
"text-size": "Change text size",
|
||||
speak: "Speak your prompt",
|
||||
},
|
||||
refusal: {
|
||||
title: "Query mode refusal response",
|
||||
|
@ -345,6 +367,11 @@ const TRANSLATIONS = {
|
|||
"Web search during agent sessions will not work until this is set up.",
|
||||
},
|
||||
},
|
||||
menu: {
|
||||
available: "View all available agents you can use for chatting.",
|
||||
default: " - the default agent for this workspace.",
|
||||
custom: "custom agents are coming soon!",
|
||||
},
|
||||
},
|
||||
|
||||
// Workspace Chats
|
||||
|
@ -551,6 +578,39 @@ const TRANSLATIONS = {
|
|||
vector: "Vector Database",
|
||||
anonymous: "Anonymous Telemetry Enabled",
|
||||
},
|
||||
"chat-history": {
|
||||
welcome: "Welcome to your new workspace.",
|
||||
"get-started-either": "To get started either",
|
||||
"upload-doc": "upload a document",
|
||||
or: "or",
|
||||
"send-chat": "send a chat.",
|
||||
"get-started": "To get started",
|
||||
tts: {
|
||||
"start-tts": "TTS Speak message",
|
||||
"pause-tts": "Pause TTS speech of message",
|
||||
start: "Speak message",
|
||||
pause: "Pause speech",
|
||||
},
|
||||
},
|
||||
citations: {
|
||||
hide: "Hide Citations",
|
||||
show: "Show Citations",
|
||||
},
|
||||
threads: {
|
||||
new: "New Thread",
|
||||
start: "Starting Thread...",
|
||||
delete: {
|
||||
selected: "Delete Selected",
|
||||
delete: "Delete Thread",
|
||||
question:
|
||||
"Are you sure you want to delete this thread? All of its chats will be deleted. You cannot undo this.",
|
||||
success: "Thread deleted successfully!",
|
||||
failure: "Thread could not be deleted!",
|
||||
},
|
||||
options: "Thread options",
|
||||
rename: "Rename",
|
||||
rename_question: "What would you like to rename this thread to?",
|
||||
},
|
||||
};
|
||||
|
||||
export default TRANSLATIONS;
|
||||
|
|
Loading…
Add table
Reference in a new issue