mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-06-04 20:20:37 +02:00
Improved linking + scrolling (#3744)
* nits * quick nit * update various components * quick nit * update * chat nits * minor linear check fix
This commit is contained in:
parent
b12c51f56c
commit
d6863ec775
4
.github/workflows/pr-linear-check.yml
vendored
4
.github/workflows/pr-linear-check.yml
vendored
@ -9,9 +9,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check PR body for Linear link or override
|
- name: Check PR body for Linear link or override
|
||||||
|
env:
|
||||||
|
PR_BODY: ${{ github.event.pull_request.body }}
|
||||||
run: |
|
run: |
|
||||||
PR_BODY="${{ github.event.pull_request.body }}"
|
|
||||||
|
|
||||||
# Looking for "https://linear.app" in the body
|
# Looking for "https://linear.app" in the body
|
||||||
if echo "$PR_BODY" | grep -qE "https://linear\.app"; then
|
if echo "$PR_BODY" | grep -qE "https://linear\.app"; then
|
||||||
echo "Found a Linear link. Check passed."
|
echo "Found a Linear link. Check passed."
|
||||||
|
@ -890,47 +890,20 @@ export function AssistantEditor({
|
|||||||
{imageGenerationTool && (
|
{imageGenerationTool && (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center content-start mb-2">
|
<div className="flex items-center content-start mb-2">
|
||||||
<TooltipProvider>
|
<BooleanFormField
|
||||||
<Tooltip>
|
name={`enabled_tools_map.${imageGenerationTool.id}`}
|
||||||
<TooltipTrigger>
|
label={imageGenerationTool.display_name}
|
||||||
<CheckboxField
|
subtext="Generate and manipulate images using AI-powered tools"
|
||||||
size="sm"
|
disabled={
|
||||||
id={`enabled_tools_map.${imageGenerationTool.id}`}
|
!currentLLMSupportsImageOutput ||
|
||||||
name={`enabled_tools_map.${imageGenerationTool.id}`}
|
!isImageGenerationAvailable
|
||||||
onCheckedChange={() => {
|
}
|
||||||
if (isImageGenerationAvailable) {
|
disabledTooltip={
|
||||||
toggleToolInValues(
|
!currentLLMSupportsImageOutput
|
||||||
imageGenerationTool.id
|
? "To use Image Generation, select GPT-4 or another image compatible model as the default model for this Assistant."
|
||||||
);
|
: "Image Generation requires an OpenAI or Azure Dall-E configuration."
|
||||||
}
|
}
|
||||||
}}
|
/>
|
||||||
className={
|
|
||||||
!isImageGenerationAvailable
|
|
||||||
? "opacity-50 cursor-not-allowed"
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</TooltipTrigger>
|
|
||||||
{!isImageGenerationAvailable && (
|
|
||||||
<TooltipContent side="top" align="center">
|
|
||||||
<p className="bg-background-900 max-w-[200px] mb-1 text-sm rounded-lg p-1.5 text-white">
|
|
||||||
{!currentLLMSupportsImageOutput
|
|
||||||
? "To use Image Generation, select GPT-4 or another image compatible model as the default model for this Assistant."
|
|
||||||
: "Image Generation requires an OpenAI or Azure Dalle configuration."}
|
|
||||||
</p>
|
|
||||||
</TooltipContent>
|
|
||||||
)}
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
<div className="flex flex-col ml-2">
|
|
||||||
<span className="text-sm">
|
|
||||||
{imageGenerationTool.display_name}
|
|
||||||
</span>
|
|
||||||
<span className="text-xs text-subtle">
|
|
||||||
Generate and manipulate images using AI-powered
|
|
||||||
tools
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -964,23 +937,12 @@ export function AssistantEditor({
|
|||||||
|
|
||||||
{customTools.length > 0 &&
|
{customTools.length > 0 &&
|
||||||
customTools.map((tool) => (
|
customTools.map((tool) => (
|
||||||
<React.Fragment key={tool.id}>
|
<BooleanFormField
|
||||||
<div className="flex items-center content-start mb-2">
|
key={tool.id}
|
||||||
<Checkbox
|
name={`enabled_tools_map.${tool.id}`}
|
||||||
size="sm"
|
label={tool.display_name}
|
||||||
id={`enabled_tools_map.${tool.id}`}
|
subtext={tool.description}
|
||||||
checked={values.enabled_tools_map[tool.id]}
|
/>
|
||||||
onCheckedChange={() => {
|
|
||||||
toggleToolInValues(tool.id);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="ml-2">
|
|
||||||
<span className="text-sm">
|
|
||||||
{tool.display_name}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1333,7 +1295,6 @@ export function AssistantEditor({
|
|||||||
<BooleanFormField
|
<BooleanFormField
|
||||||
small
|
small
|
||||||
removeIndent
|
removeIndent
|
||||||
alignTop
|
|
||||||
name="llm_relevance_filter"
|
name="llm_relevance_filter"
|
||||||
label="AI Relevance Filter"
|
label="AI Relevance Filter"
|
||||||
subtext="If enabled, the LLM will filter out documents that are not useful for answering the user query prior to generating a response. This typically improves the quality of the response but incurs slightly higher cost."
|
subtext="If enabled, the LLM will filter out documents that are not useful for answering the user query prior to generating a response. This typically improves the quality of the response but incurs slightly higher cost."
|
||||||
@ -1342,7 +1303,6 @@ export function AssistantEditor({
|
|||||||
<BooleanFormField
|
<BooleanFormField
|
||||||
small
|
small
|
||||||
removeIndent
|
removeIndent
|
||||||
alignTop
|
|
||||||
name="include_citations"
|
name="include_citations"
|
||||||
label="Citations"
|
label="Citations"
|
||||||
subtext="Response will include citations ([1], [2], etc.) for documents referenced by the LLM. In general, we recommend to leave this enabled in order to increase trust in the LLM answer."
|
subtext="Response will include citations ([1], [2], etc.) for documents referenced by the LLM. In general, we recommend to leave this enabled in order to increase trust in the LLM answer."
|
||||||
@ -1355,7 +1315,6 @@ export function AssistantEditor({
|
|||||||
<BooleanFormField
|
<BooleanFormField
|
||||||
small
|
small
|
||||||
removeIndent
|
removeIndent
|
||||||
alignTop
|
|
||||||
name="datetime_aware"
|
name="datetime_aware"
|
||||||
label="Date and Time Aware"
|
label="Date and Time Aware"
|
||||||
subtext='Toggle this option to let the assistant know the current date and time (formatted like: "Thursday Jan 1, 1970 00:01"). To inject it in a specific place in the prompt, use the pattern [[CURRENT_DATETIME]]'
|
subtext='Toggle this option to let the assistant know the current date and time (formatted like: "Thursday Jan 1, 1970 00:01"). To inject it in a specific place in the prompt, use the pattern [[CURRENT_DATETIME]]'
|
||||||
|
@ -50,7 +50,7 @@ export const rerankingModels: RerankingModel[] = [
|
|||||||
cloud: true,
|
cloud: true,
|
||||||
displayName: "LiteLLM",
|
displayName: "LiteLLM",
|
||||||
description: "Host your own reranker or router with LiteLLM proxy",
|
description: "Host your own reranker or router with LiteLLM proxy",
|
||||||
link: "https://docs.litellm.ai/docs/proxy",
|
link: "https://docs.litellm.ai/docs/simple_proxy",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rerank_provider_type: null,
|
rerank_provider_type: null,
|
||||||
@ -82,7 +82,7 @@ export const rerankingModels: RerankingModel[] = [
|
|||||||
modelName: "rerank-english-v3.0",
|
modelName: "rerank-english-v3.0",
|
||||||
displayName: "Cohere English",
|
displayName: "Cohere English",
|
||||||
description: "High-performance English-focused reranking model.",
|
description: "High-performance English-focused reranking model.",
|
||||||
link: "https://docs.cohere.com/docs/rerank",
|
link: "https://docs.cohere.com/v2/reference/rerank",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
cloud: true,
|
cloud: true,
|
||||||
@ -90,7 +90,7 @@ export const rerankingModels: RerankingModel[] = [
|
|||||||
modelName: "rerank-multilingual-v3.0",
|
modelName: "rerank-multilingual-v3.0",
|
||||||
displayName: "Cohere Multilingual",
|
displayName: "Cohere Multilingual",
|
||||||
description: "Powerful multilingual reranking model.",
|
description: "Powerful multilingual reranking model.",
|
||||||
link: "https://docs.cohere.com/docs/rerank",
|
link: "https://docs.cohere.com/v2/reference/rerank",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Modal } from "@/components/Modal";
|
|
||||||
import AssistantCard from "./AssistantCard";
|
import AssistantCard from "./AssistantCard";
|
||||||
import { useAssistants } from "@/components/context/AssistantsContext";
|
import { useAssistants } from "@/components/context/AssistantsContext";
|
||||||
import { useUser } from "@/components/user/UserProvider";
|
import { useUser } from "@/components/user/UserProvider";
|
||||||
import { FilterIcon } from "lucide-react";
|
import { FilterIcon } from "lucide-react";
|
||||||
import { checkUserOwnsAssistant } from "@/lib/assistants/checkOwnership";
|
import { checkUserOwnsAssistant } from "@/lib/assistants/checkOwnership";
|
||||||
|
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
||||||
|
|
||||||
export const AssistantBadgeSelector = ({
|
export const AssistantBadgeSelector = ({
|
||||||
text,
|
text,
|
||||||
@ -21,11 +20,12 @@ export const AssistantBadgeSelector = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`${
|
className={`
|
||||||
selected
|
select-none ${
|
||||||
? "bg-neutral-900 text-white"
|
selected
|
||||||
: "bg-transparent text-neutral-900"
|
? "bg-neutral-900 text-white"
|
||||||
} w-12 h-5 text-center px-1 py-0.5 rounded-lg cursor-pointer text-[12px] font-normal leading-[10px] border border-black justify-center items-center gap-1 inline-flex`}
|
: "bg-transparent text-neutral-900"
|
||||||
|
} w-12 h-5 text-center px-1 py-0.5 rounded-lg cursor-pointer text-[12px] font-normal leading-[10px] border border-black justify-center items-center gap-1 inline-flex`}
|
||||||
onClick={toggleFilter}
|
onClick={toggleFilter}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
@ -60,11 +60,15 @@ const useAssistantFilter = () => {
|
|||||||
return { assistantFilters, toggleAssistantFilter, setAssistantFilters };
|
return { assistantFilters, toggleAssistantFilter, setAssistantFilters };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function AssistantModal({
|
interface AssistantModalProps {
|
||||||
hideModal,
|
|
||||||
}: {
|
|
||||||
hideModal: () => void;
|
hideModal: () => void;
|
||||||
}) {
|
modalHeight?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AssistantModal({
|
||||||
|
hideModal,
|
||||||
|
modalHeight,
|
||||||
|
}: AssistantModalProps) {
|
||||||
const { assistants, pinnedAssistants } = useAssistants();
|
const { assistants, pinnedAssistants } = useAssistants();
|
||||||
const { assistantFilters, toggleAssistantFilter } = useAssistantFilter();
|
const { assistantFilters, toggleAssistantFilter } = useAssistantFilter();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -86,11 +90,11 @@ export default function AssistantModal({
|
|||||||
!assistantFilters[AssistantFilter.Private] || !assistant.is_public;
|
!assistantFilters[AssistantFilter.Private] || !assistant.is_public;
|
||||||
const pinnedFilter =
|
const pinnedFilter =
|
||||||
!assistantFilters[AssistantFilter.Pinned] ||
|
!assistantFilters[AssistantFilter.Pinned] ||
|
||||||
(user?.preferences?.pinned_assistants?.includes(assistant.id) ?? false);
|
(pinnedAssistants.map((a) => a.id).includes(assistant.id) ?? false);
|
||||||
|
|
||||||
const mineFilter =
|
const mineFilter =
|
||||||
!assistantFilters[AssistantFilter.Mine] ||
|
!assistantFilters[AssistantFilter.Mine] ||
|
||||||
assistants.map((a: Persona) => checkUserOwnsAssistant(user, a));
|
checkUserOwnsAssistant(user, assistant);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(nameMatches || labelMatches) &&
|
(nameMatches || labelMatches) &&
|
||||||
@ -111,142 +115,145 @@ export default function AssistantModal({
|
|||||||
(assistant) => !assistant.builtin_persona && !assistant.is_default_persona
|
(assistant) => !assistant.builtin_persona && !assistant.is_default_persona
|
||||||
);
|
);
|
||||||
|
|
||||||
const maxHeight = 900;
|
|
||||||
const calculatedHeight = Math.min(
|
|
||||||
Math.ceil(assistants.length / 2) * 170 + 200,
|
|
||||||
window.innerHeight * 0.8
|
|
||||||
);
|
|
||||||
|
|
||||||
const height = Math.min(calculatedHeight, maxHeight);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Dialog open={true} onOpenChange={(open) => !open && hideModal()}>
|
||||||
heightOverride={`${height}px`}
|
<DialogContent
|
||||||
onOutsideClick={hideModal}
|
className="p-0 overflow-hidden max-h-[80vh] max-w-none w-[95%] bg-background rounded-sm shadow-2xl transform transition-all duration-300 ease-in-out relative w-11/12 max-w-4xl pt-10 pb-10 px-10 overflow-hidden flex flex-col max-w-4xl"
|
||||||
removeBottomPadding
|
style={{
|
||||||
className={`max-w-4xl max-h-[90vh] ${height} w-[95%] overflow-hidden`}
|
position: "fixed",
|
||||||
>
|
top: "10vh",
|
||||||
<div className="flex flex-col h-full">
|
left: "50%",
|
||||||
<div className="flex bg-background flex-col sticky top-0 z-10">
|
transform: "translateX(-50%)",
|
||||||
<div className="flex px-2 justify-between items-center gap-x-2 mb-0">
|
margin: 0,
|
||||||
<div className="h-12 w-full rounded-lg flex-col justify-center items-start gap-2.5 inline-flex">
|
}}
|
||||||
<div className="h-12 rounded-md w-full shadow-[0px_0px_2px_0px_rgba(0,0,0,0.25)] border border-[#dcdad4] flex items-center px-3">
|
>
|
||||||
{!isSearchFocused && (
|
<div className="flex overflow-hidden flex-col h-full">
|
||||||
<svg
|
<div className="flex flex-col sticky top-0 z-10">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<div className="flex px-2 justify-between items-center gap-x-2 mb-0">
|
||||||
className="h-5 w-5 text-gray-400"
|
<div className="h-12 w-full rounded-lg flex-col justify-center items-start gap-2.5 inline-flex">
|
||||||
fill="none"
|
<div className="h-12 rounded-md w-full shadow-[0px_0px_2px_0px_rgba(0,0,0,0.25)] border border-[#dcdad4] flex items-center px-3">
|
||||||
viewBox="0 0 24 24"
|
{!isSearchFocused && (
|
||||||
stroke="currentColor"
|
<svg
|
||||||
>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<path
|
className="h-5 w-5 text-gray-400"
|
||||||
strokeLinecap="round"
|
fill="none"
|
||||||
strokeLinejoin="round"
|
viewBox="0 0 24 24"
|
||||||
strokeWidth={2}
|
stroke="currentColor"
|
||||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
>
|
||||||
/>
|
<path
|
||||||
</svg>
|
strokeLinecap="round"
|
||||||
)}
|
strokeLinejoin="round"
|
||||||
<input
|
strokeWidth={2}
|
||||||
value={searchQuery}
|
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
/>
|
||||||
onFocus={() => setIsSearchFocused(true)}
|
</svg>
|
||||||
onBlur={() => setIsSearchFocused(false)}
|
)}
|
||||||
type="text"
|
<input
|
||||||
className="w-full h-full bg-transparent outline-none text-black"
|
value={searchQuery}
|
||||||
/>
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
</div>
|
onFocus={() => setIsSearchFocused(true)}
|
||||||
</div>
|
onBlur={() => setIsSearchFocused(false)}
|
||||||
<button
|
type="text"
|
||||||
onClick={() => router.push("/assistants/new")}
|
className="w-full h-full bg-transparent outline-none text-black"
|
||||||
className="h-10 cursor-pointer px-6 py-3 bg-black rounded-md border border-black justify-center items-center gap-2.5 inline-flex"
|
|
||||||
>
|
|
||||||
<div className="text-[#fffcf4] text-lg font-normal leading-normal">
|
|
||||||
Create
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="px-2 flex py-4 items-center gap-x-2 flex-wrap">
|
|
||||||
<FilterIcon size={16} />
|
|
||||||
<AssistantBadgeSelector
|
|
||||||
text="Pinned"
|
|
||||||
selected={assistantFilters[AssistantFilter.Pinned]}
|
|
||||||
toggleFilter={() => toggleAssistantFilter(AssistantFilter.Pinned)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<AssistantBadgeSelector
|
|
||||||
text="Mine"
|
|
||||||
selected={assistantFilters[AssistantFilter.Mine]}
|
|
||||||
toggleFilter={() => toggleAssistantFilter(AssistantFilter.Mine)}
|
|
||||||
/>
|
|
||||||
<AssistantBadgeSelector
|
|
||||||
text="Private"
|
|
||||||
selected={assistantFilters[AssistantFilter.Private]}
|
|
||||||
toggleFilter={() =>
|
|
||||||
toggleAssistantFilter(AssistantFilter.Private)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<AssistantBadgeSelector
|
|
||||||
text="Public"
|
|
||||||
selected={assistantFilters[AssistantFilter.Public]}
|
|
||||||
toggleFilter={() => toggleAssistantFilter(AssistantFilter.Public)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="w-full border-t border-neutral-200" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex-grow overflow-y-auto">
|
|
||||||
<h2 className="text-2xl font-semibold text-gray-800 mb-2 px-4 py-2">
|
|
||||||
Featured Assistants
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="w-full px-2 pb-2 grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-6">
|
|
||||||
{featuredAssistants.length > 0 ? (
|
|
||||||
featuredAssistants.map((assistant, index) => (
|
|
||||||
<div key={index}>
|
|
||||||
<AssistantCard
|
|
||||||
pinned={pinnedAssistants
|
|
||||||
.map((a) => a.id)
|
|
||||||
.includes(assistant.id)}
|
|
||||||
persona={assistant}
|
|
||||||
closeModal={hideModal}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<div className="col-span-2 text-center text-gray-500">
|
|
||||||
No featured assistants match filters
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
<button
|
||||||
|
onClick={() => router.push("/assistants/new")}
|
||||||
|
className="h-10 cursor-pointer px-6 py-3 bg-black rounded-md border border-black justify-center items-center gap-2.5 inline-flex"
|
||||||
|
>
|
||||||
|
<div className="text-[#fffcf4] text-lg font-normal leading-normal">
|
||||||
|
Create
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="px-2 flex py-4 items-center gap-x-2 flex-wrap">
|
||||||
|
<FilterIcon size={16} />
|
||||||
|
<AssistantBadgeSelector
|
||||||
|
text="Pinned"
|
||||||
|
selected={assistantFilters[AssistantFilter.Pinned]}
|
||||||
|
toggleFilter={() =>
|
||||||
|
toggleAssistantFilter(AssistantFilter.Pinned)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<AssistantBadgeSelector
|
||||||
|
text="Mine"
|
||||||
|
selected={assistantFilters[AssistantFilter.Mine]}
|
||||||
|
toggleFilter={() => toggleAssistantFilter(AssistantFilter.Mine)}
|
||||||
|
/>
|
||||||
|
<AssistantBadgeSelector
|
||||||
|
text="Private"
|
||||||
|
selected={assistantFilters[AssistantFilter.Private]}
|
||||||
|
toggleFilter={() =>
|
||||||
|
toggleAssistantFilter(AssistantFilter.Private)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<AssistantBadgeSelector
|
||||||
|
text="Public"
|
||||||
|
selected={assistantFilters[AssistantFilter.Public]}
|
||||||
|
toggleFilter={() =>
|
||||||
|
toggleAssistantFilter(AssistantFilter.Public)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="w-full border-t border-neutral-200" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{allAssistants && allAssistants.length > 0 && (
|
<div className="flex-grow overflow-y-auto">
|
||||||
<>
|
<h2 className="text-2xl font-semibold text-gray-800 mb-2 px-4 py-2">
|
||||||
<h2 className="text-2xl font-semibold text-gray-800 mt-4 mb-2 px-4 py-2">
|
Featured Assistants
|
||||||
All Assistants
|
</h2>
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="w-full mt-2 px-2 pb-2 grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-6">
|
<div className="w-full px-2 pb-2 grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-6">
|
||||||
{allAssistants
|
{featuredAssistants.length > 0 ? (
|
||||||
.sort((a, b) => b.id - a.id)
|
featuredAssistants.map((assistant, index) => (
|
||||||
.map((assistant, index) => (
|
<div key={index}>
|
||||||
<div key={index}>
|
<AssistantCard
|
||||||
<AssistantCard
|
pinned={pinnedAssistants
|
||||||
pinned={
|
.map((a) => a.id)
|
||||||
user?.preferences?.pinned_assistants?.includes(
|
.includes(assistant.id)}
|
||||||
assistant.id
|
persona={assistant}
|
||||||
) ?? false
|
closeModal={hideModal}
|
||||||
}
|
/>
|
||||||
persona={assistant}
|
</div>
|
||||||
closeModal={hideModal}
|
))
|
||||||
/>
|
) : (
|
||||||
</div>
|
<div className="col-span-2 text-center text-gray-500">
|
||||||
))}
|
No featured assistants match filters
|
||||||
</div>
|
</div>
|
||||||
</>
|
)}
|
||||||
)}
|
</div>
|
||||||
|
|
||||||
|
{allAssistants && allAssistants.length > 0 && (
|
||||||
|
<>
|
||||||
|
<h2 className="text-2xl font-semibold text-gray-800 mt-4 mb-2 px-4 py-2">
|
||||||
|
All Assistants
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="w-full mt-2 px-2 pb-2 grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-6">
|
||||||
|
{allAssistants
|
||||||
|
.sort((a, b) => b.id - a.id)
|
||||||
|
.map((assistant, index) => (
|
||||||
|
<div key={index}>
|
||||||
|
<AssistantCard
|
||||||
|
pinned={
|
||||||
|
user?.preferences?.pinned_assistants?.includes(
|
||||||
|
assistant.id
|
||||||
|
) ?? false
|
||||||
|
}
|
||||||
|
persona={assistant}
|
||||||
|
closeModal={hideModal}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</DialogContent>
|
||||||
</Modal>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
export default AssistantModal;
|
||||||
|
@ -694,6 +694,7 @@ export function ChatInputBar({
|
|||||||
flexPriority="stiff"
|
flexPriority="stiff"
|
||||||
name="Filters"
|
name="Filters"
|
||||||
Icon={FiFilter}
|
Icon={FiFilter}
|
||||||
|
toggle
|
||||||
tooltipContent="Filter your search"
|
tooltipContent="Filter your search"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ export const MemoizedAnchor = memo(
|
|||||||
const index = parseInt(match[1], 10) - 1;
|
const index = parseInt(match[1], 10) - 1;
|
||||||
const associatedDoc = docs?.[index];
|
const associatedDoc = docs?.[index];
|
||||||
if (!associatedDoc) {
|
if (!associatedDoc) {
|
||||||
return <>{children}</>;
|
return <a href={children as string}>{children}</a>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let icon: React.ReactNode = null;
|
let icon: React.ReactNode = null;
|
||||||
@ -77,9 +77,24 @@ export const MemoizedLink = memo((props: any) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleMouseDown = () => {
|
||||||
|
let url = rest.href || rest.children?.toString();
|
||||||
|
if (url && !url.startsWith("http://") && !url.startsWith("https://")) {
|
||||||
|
// Try to construct a valid URL
|
||||||
|
const httpsUrl = `https://${url}`;
|
||||||
|
try {
|
||||||
|
new URL(httpsUrl);
|
||||||
|
url = httpsUrl;
|
||||||
|
} catch {
|
||||||
|
// If not a valid URL, don't modify original url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.open(url, "_blank");
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
onMouseDown={() => rest.href && window.open(rest.href, "_blank")}
|
onMouseDown={handleMouseDown}
|
||||||
className="cursor-pointer text-link hover:text-link-hover"
|
className="cursor-pointer text-link hover:text-link-hover"
|
||||||
>
|
>
|
||||||
{rest.children}
|
{rest.children}
|
||||||
|
@ -375,7 +375,11 @@ export const AIMessage = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
style={{ position: "absolute", left: "-9999px" }}
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
left: "-9999px",
|
||||||
|
display: "none",
|
||||||
|
}}
|
||||||
dangerouslySetInnerHTML={{ __html: htmlContent }}
|
dangerouslySetInnerHTML={{ __html: htmlContent }}
|
||||||
/>
|
/>
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
|
@ -6,8 +6,17 @@ import React, {
|
|||||||
useContext,
|
useContext,
|
||||||
useState,
|
useState,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
useLayoutEffect,
|
||||||
|
useRef,
|
||||||
} from "react";
|
} from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipTrigger,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
|
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { ChatSession } from "../interfaces";
|
import { ChatSession } from "../interfaces";
|
||||||
import { NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA } from "@/lib/constants";
|
import { NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA } from "@/lib/constants";
|
||||||
@ -44,6 +53,7 @@ import {
|
|||||||
import { useSortable } from "@dnd-kit/sortable";
|
import { useSortable } from "@dnd-kit/sortable";
|
||||||
import { CSS } from "@dnd-kit/utilities";
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
import { CircleX } from "lucide-react";
|
import { CircleX } from "lucide-react";
|
||||||
|
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
|
||||||
|
|
||||||
interface HistorySidebarProps {
|
interface HistorySidebarProps {
|
||||||
page: pageType;
|
page: pageType;
|
||||||
@ -90,6 +100,24 @@ const SortableAssistant: React.FC<SortableAssistantProps> = ({
|
|||||||
...(isDragging ? { zIndex: 1000, position: "relative" as const } : {}),
|
...(isDragging ? { zIndex: 1000, position: "relative" as const } : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const nameRef = useRef<HTMLParagraphElement>(null);
|
||||||
|
const hiddenNameRef = useRef<HTMLSpanElement>(null);
|
||||||
|
const [isNameTruncated, setIsNameTruncated] = useState(false);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const checkTruncation = () => {
|
||||||
|
if (nameRef.current && hiddenNameRef.current) {
|
||||||
|
const visibleWidth = nameRef.current.offsetWidth;
|
||||||
|
const fullTextWidth = hiddenNameRef.current.offsetWidth;
|
||||||
|
setIsNameTruncated(fullTextWidth > visibleWidth);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
checkTruncation();
|
||||||
|
window.addEventListener("resize", checkTruncation);
|
||||||
|
return () => window.removeEventListener("resize", checkTruncation);
|
||||||
|
}, [assistant.name]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={setNodeRef}
|
ref={setNodeRef}
|
||||||
@ -115,10 +143,28 @@ const SortableAssistant: React.FC<SortableAssistantProps> = ({
|
|||||||
: ""
|
: ""
|
||||||
} relative flex items-center gap-x-2 py-1 px-2 rounded-md`}
|
} relative flex items-center gap-x-2 py-1 px-2 rounded-md`}
|
||||||
>
|
>
|
||||||
<AssistantIcon assistant={assistant} size={16} className="flex-none" />
|
<AssistantIcon assistant={assistant} size={20} className="flex-none" />
|
||||||
<p className="text-base text-left w-fit line-clamp-1 text-ellipsis text-black">
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<p
|
||||||
|
ref={nameRef}
|
||||||
|
className="text-base text-left w-fit line-clamp-1 text-ellipsis text-black"
|
||||||
|
>
|
||||||
|
{assistant.name}
|
||||||
|
</p>
|
||||||
|
</TooltipTrigger>
|
||||||
|
{isNameTruncated && (
|
||||||
|
<TooltipContent>{assistant.name}</TooltipContent>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
<span
|
||||||
|
ref={hiddenNameRef}
|
||||||
|
className="absolute left-[-9999px] whitespace-nowrap"
|
||||||
|
>
|
||||||
{assistant.name}
|
{assistant.name}
|
||||||
</p>
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -295,7 +341,7 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="h-full relative overflow-y-auto">
|
<div className="h-full relative overflow-x-hidden overflow-y-auto">
|
||||||
<div className="flex px-4 font-normal text-sm gap-x-2 leading-normal text-[#6c6c6c]/80 items-center font-normal leading-normal">
|
<div className="flex px-4 font-normal text-sm gap-x-2 leading-normal text-[#6c6c6c]/80 items-center font-normal leading-normal">
|
||||||
Assistants
|
Assistants
|
||||||
</div>
|
</div>
|
||||||
@ -303,6 +349,7 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
|
|||||||
sensors={sensors}
|
sensors={sensors}
|
||||||
collisionDetection={closestCenter}
|
collisionDetection={closestCenter}
|
||||||
onDragEnd={handleDragEnd}
|
onDragEnd={handleDragEnd}
|
||||||
|
modifiers={[restrictToVerticalAxis]}
|
||||||
>
|
>
|
||||||
<SortableContext
|
<SortableContext
|
||||||
items={pinnedAssistants.map((a) =>
|
items={pinnedAssistants.map((a) =>
|
||||||
|
@ -13,7 +13,7 @@ import { FiPlus, FiTrash2, FiCheck, FiX } from "react-icons/fi";
|
|||||||
import { NEXT_PUBLIC_DELETE_ALL_CHATS_ENABLED } from "@/lib/constants";
|
import { NEXT_PUBLIC_DELETE_ALL_CHATS_ENABLED } from "@/lib/constants";
|
||||||
import { FolderDropdown } from "../folders/FolderDropdown";
|
import { FolderDropdown } from "../folders/FolderDropdown";
|
||||||
import { ChatSessionDisplay } from "./ChatSessionDisplay";
|
import { ChatSessionDisplay } from "./ChatSessionDisplay";
|
||||||
import { useState, useCallback, useRef, useContext } from "react";
|
import { useState, useCallback, useRef, useContext, useEffect } from "react";
|
||||||
import { Caret } from "@/components/icons/icons";
|
import { Caret } from "@/components/icons/icons";
|
||||||
import { groupSessionsByDateRange } from "../lib";
|
import { groupSessionsByDateRange } from "../lib";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@ -36,6 +36,7 @@ import { useSortable } from "@dnd-kit/sortable";
|
|||||||
import { CSS } from "@dnd-kit/utilities";
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
import { useChatContext } from "@/components/context/ChatContext";
|
import { useChatContext } from "@/components/context/ChatContext";
|
||||||
import { SettingsContext } from "@/components/settings/SettingsProvider";
|
import { SettingsContext } from "@/components/settings/SettingsProvider";
|
||||||
|
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
|
||||||
|
|
||||||
interface SortableFolderProps {
|
interface SortableFolderProps {
|
||||||
folder: Folder;
|
folder: Folder;
|
||||||
@ -53,34 +54,41 @@ interface SortableFolderProps {
|
|||||||
const SortableFolder: React.FC<SortableFolderProps> = (props) => {
|
const SortableFolder: React.FC<SortableFolderProps> = (props) => {
|
||||||
const settings = useContext(SettingsContext);
|
const settings = useContext(SettingsContext);
|
||||||
const mobile = settings?.isMobile;
|
const mobile = settings?.isMobile;
|
||||||
const { attributes, listeners, setNodeRef, transform, transition } =
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
useSortable({
|
const {
|
||||||
id: props.folder.folder_id?.toString() ?? "",
|
attributes,
|
||||||
data: {
|
listeners,
|
||||||
activationConstraint: {
|
setNodeRef,
|
||||||
distance: 8,
|
transform,
|
||||||
},
|
transition,
|
||||||
},
|
isDragging: isDraggingDndKit,
|
||||||
disabled: mobile,
|
} = useSortable({
|
||||||
});
|
id: props.folder.folder_id?.toString() ?? "",
|
||||||
|
disabled: mobile,
|
||||||
|
});
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const style = {
|
|
||||||
|
const style: React.CSSProperties = {
|
||||||
transform: CSS.Transform.toString(transform),
|
transform: CSS.Transform.toString(transform),
|
||||||
transition,
|
transition,
|
||||||
|
zIndex: isDragging ? 1000 : "auto",
|
||||||
|
position: isDragging ? "relative" : "static",
|
||||||
|
opacity: isDragging ? 0.6 : 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsDragging(isDraggingDndKit);
|
||||||
|
}, [isDraggingDndKit]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={setNodeRef}
|
ref={setNodeRef}
|
||||||
className="pr-3 ml-4 overflow-visible flex items-start"
|
className="pr-3 ml-4 overflow-visible flex items-start"
|
||||||
style={style}
|
style={style}
|
||||||
|
{...attributes}
|
||||||
|
{...listeners}
|
||||||
>
|
>
|
||||||
<FolderDropdown
|
<FolderDropdown ref={ref} {...props} />
|
||||||
ref={ref}
|
|
||||||
{...props}
|
|
||||||
{...(mobile ? {} : attributes)}
|
|
||||||
{...(mobile ? {} : listeners)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -359,6 +367,7 @@ export function PagesTab({
|
|||||||
|
|
||||||
{folders && folders.length > 0 && (
|
{folders && folders.length > 0 && (
|
||||||
<DndContext
|
<DndContext
|
||||||
|
modifiers={[restrictToVerticalAxis]}
|
||||||
sensors={sensors}
|
sensors={sensors}
|
||||||
collisionDetection={closestCenter}
|
collisionDetection={closestCenter}
|
||||||
onDragEnd={handleDragEnd}
|
onDragEnd={handleDragEnd}
|
||||||
|
@ -287,11 +287,53 @@
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scrollbar {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styling for textarea scrollbar */
|
||||||
|
textarea::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea::-webkit-scrollbar-track {
|
||||||
|
background: var(--scrollbar-track);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--scrollbar-thumb);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--scrollbar-thumb-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styling for textarea resize handle */
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Firefox */
|
||||||
|
textarea {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
|
||||||
|
}
|
||||||
|
|
||||||
.inputscroll::-webkit-scrollbar-track {
|
.inputscroll::-webkit-scrollbar-track {
|
||||||
background: #e5e7eb;
|
background: #e5e7eb;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 0px;
|
||||||
|
/* Vertical scrollbar width */
|
||||||
|
height: 8px;
|
||||||
|
/* Horizontal scrollbar height */
|
||||||
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
/* background: theme("colors.scrollbar.track"); */
|
/* background: theme("colors.scrollbar.track"); */
|
||||||
|
@ -63,7 +63,7 @@ export function Modal({
|
|||||||
<div
|
<div
|
||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
className={cn(
|
className={cn(
|
||||||
`fixed inset-0 bg-black bg-opacity-25 backdrop-blur-sm h-full
|
`fixed inset-0 bg-white bg-opacity-25 backdrop-blur-sm h-full
|
||||||
flex items-center justify-center z-[9999] transition-opacity duration-300 ease-in-out`
|
flex items-center justify-center z-[9999] transition-opacity duration-300 ease-in-out`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@ -76,6 +76,7 @@ export function Modal({
|
|||||||
}}
|
}}
|
||||||
className={`
|
className={`
|
||||||
bg-background
|
bg-background
|
||||||
|
bg-blue-400
|
||||||
text-emphasis
|
text-emphasis
|
||||||
rounded
|
rounded
|
||||||
shadow-2xl
|
shadow-2xl
|
||||||
|
@ -25,11 +25,13 @@ import {
|
|||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
import { FaMarkdown } from "react-icons/fa";
|
import { FaMarkdown } from "react-icons/fa";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState, useCallback } from "react";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
import { EditIcon } from "@/components/icons/icons";
|
import { EditIcon } from "@/components/icons/icons";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { CheckboxField } from "@/components/ui/checkbox";
|
||||||
|
import { CheckedState } from "@radix-ui/react-checkbox";
|
||||||
|
|
||||||
export function SectionHeader({
|
export function SectionHeader({
|
||||||
children,
|
children,
|
||||||
@ -51,7 +53,7 @@ export function Label({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`block font-medium base ${className} ${
|
className={`block font-medium base ${className} ${
|
||||||
small ? "text-sm" : "text-base"
|
small ? "text-xs" : "text-sm"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@ -75,7 +77,7 @@ export function LabelWithTooltip({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function SubLabel({ children }: { children: string | JSX.Element }) {
|
export function SubLabel({ children }: { children: string | JSX.Element }) {
|
||||||
return <div className="text-sm text-subtle mb-2">{children}</div>;
|
return <div className="text-xs text-subtle">{children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ManualErrorMessage({ children }: { children: string }) {
|
export function ManualErrorMessage({ children }: { children: string }) {
|
||||||
@ -439,53 +441,62 @@ interface BooleanFormFieldProps {
|
|||||||
name: string;
|
name: string;
|
||||||
label: string;
|
label: string;
|
||||||
subtext?: string | JSX.Element;
|
subtext?: string | JSX.Element;
|
||||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
||||||
removeIndent?: boolean;
|
removeIndent?: boolean;
|
||||||
small?: boolean;
|
small?: boolean;
|
||||||
alignTop?: boolean;
|
|
||||||
noLabel?: boolean;
|
noLabel?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
checked?: boolean;
|
|
||||||
optional?: boolean;
|
optional?: boolean;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
|
disabledTooltip?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BooleanFormField = ({
|
export const BooleanFormField = ({
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
subtext,
|
subtext,
|
||||||
onChange,
|
|
||||||
removeIndent,
|
removeIndent,
|
||||||
noLabel,
|
noLabel,
|
||||||
optional,
|
optional,
|
||||||
small,
|
small,
|
||||||
disabled,
|
disabled,
|
||||||
alignTop,
|
|
||||||
checked,
|
|
||||||
tooltip,
|
tooltip,
|
||||||
|
disabledTooltip,
|
||||||
}: BooleanFormFieldProps) => {
|
}: BooleanFormFieldProps) => {
|
||||||
const [field, meta, helpers] = useField<boolean>(name);
|
const { setFieldValue } = useFormikContext<any>();
|
||||||
const { setValue } = helpers;
|
|
||||||
|
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChange = useCallback(
|
||||||
setValue(e.target.checked);
|
(checked: CheckedState) => {
|
||||||
if (onChange) {
|
if (!disabled) {
|
||||||
onChange(e);
|
setFieldValue(name, checked);
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
[disabled, name, setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<label className="flex text-sm">
|
<label className="flex items-center text-sm cursor-pointer">
|
||||||
<Field
|
<TooltipProvider>
|
||||||
type="checkbox"
|
<Tooltip>
|
||||||
{...field}
|
<TooltipTrigger>
|
||||||
checked={checked !== undefined ? checked : field.value}
|
<CheckboxField
|
||||||
disabled={disabled}
|
name={name}
|
||||||
onChange={handleChange}
|
size="sm"
|
||||||
className={`${removeIndent ? "mr-2" : "mx-3"}
|
className={`
|
||||||
px-5 w-3.5 h-3.5 ${alignTop ? "mt-1" : "my-auto"}`}
|
${disabled ? "opacity-50" : ""}
|
||||||
/>
|
${removeIndent ? "mr-2" : "mx-3"}`}
|
||||||
|
onCheckedChange={handleChange}
|
||||||
|
/>
|
||||||
|
</TooltipTrigger>
|
||||||
|
{disabled && disabledTooltip && (
|
||||||
|
<TooltipContent side="top" align="center">
|
||||||
|
<p className="bg-background-900 max-w-[200px] mb-1 text-sm rounded-lg p-1.5 text-white">
|
||||||
|
{disabledTooltip}
|
||||||
|
</p>
|
||||||
|
</TooltipContent>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
{!noLabel && (
|
{!noLabel && (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-x-2">
|
<div className="flex items-center gap-x-2">
|
||||||
|
@ -139,7 +139,7 @@ export function AssistantIcon({
|
|||||||
alt={assistant.name}
|
alt={assistant.name}
|
||||||
src={buildImgUrl(assistant.uploaded_image_id)}
|
src={buildImgUrl(assistant.uploaded_image_id)}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
className={`h-[${dimension}px] w-[${dimension}px] object-cover object-center rounded-sm transition-opacity duration-300 ${wrapperClass}`}
|
className={`h-[${dimension}px] w-[${dimension}px] rounded-full object-cover object-center transition-opacity duration-300 ${wrapperClass}`}
|
||||||
style={style}
|
style={style}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
|
|||||||
<DialogPrimitive.Overlay
|
<DialogPrimitive.Overlay
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user