θΏ™ζ˜―indexlocζδΎ›ηš„ζœεŠ‘οΌŒδΈθ¦θΎ“ε…₯任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ import { useParams } from "react-router-dom";
import truncate from "truncate";

const THREAD_CALLOUT_DETAIL_WIDTH = 26;
export default function ThreadItem({ workspace, thread, onRemove, hasNext }) {
export default function ThreadItem({
idx,
activeIdx,
isActive,
workspace,
thread,
onRemove,
hasNext,
}) {
const optionsContainer = useRef(null);
const { slug, threadSlug = null } = useParams();
const { slug } = useParams();
const [showOptions, setShowOptions] = useState(false);
const [name, setName] = useState(thread.name);

const isActive = threadSlug === thread.slug;
const linkTo = !thread.slug
? paths.workspace.chat(slug)
: paths.workspace.thread(slug, thread.slug);
Expand All @@ -23,33 +30,48 @@ export default function ThreadItem({ workspace, thread, onRemove, hasNext }) {
{/* Curved line Element and leader if required */}
<div
style={{ width: THREAD_CALLOUT_DETAIL_WIDTH / 2 }}
className="border-l border-b border-slate-300 h-[50%] absolute top-0 left-2 rounded-bl-lg"
className={`${
isActive
? "border-l-2 border-b-2 border-white"
: "border-l border-b border-slate-300"
} h-[50%] absolute top-0 left-2 rounded-bl-lg`}
></div>
{/* Downstroke border for next item */}
{hasNext && (
<div
style={{ width: THREAD_CALLOUT_DETAIL_WIDTH / 2 }}
className="border-l border-slate-300 h-[100%] absolute top-0 left-2"
className={`${
idx <= activeIdx && !isActive
? "border-l-2 border-white"
: "border-l border-slate-300"
} h-[100%] absolute top-0 left-2`}
></div>
)}

{/* Curved line inline placeholder for spacing */}
{/* Curved line inline placeholder for spacing - not visible */}
<div
style={{ width: THREAD_CALLOUT_DETAIL_WIDTH }}
className="w-[26px] h-full"
/>
<div className="flex w-full items-center justify-between pr-2 group relative">
<a href={isActive ? "#" : linkTo} className="w-full">
<p
className={`text-left text-sm ${
isActive
? "font-semibold text-slate-300"
: "text-slate-400 italic"
}`}
>
{truncate(name, 25)}
</p>
</a>
{!!thread.slug && (
{thread.deleted ? (
<a className="w-full">
<p className={`text-left text-sm text-slate-400/50 italic`}>
deleted thread
</p>
</a>
) : (
<a href={isActive ? "#" : linkTo} className="w-full">
<p
className={`text-left text-sm ${
isActive ? "font-bold text-white" : "text-slate-400"
}`}
>
{truncate(name, 25)}
</p>
</a>
)}
{!!thread.slug && !thread.deleted && (
<div ref={optionsContainer}>
<div className="flex items-center w-fit group-hover:visible md:invisible gap-x-1">
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import showToast from "@/utils/toast";
import { Plus, CircleNotch } from "@phosphor-icons/react";
import { useEffect, useState } from "react";
import ThreadItem from "./ThreadItem";
import { useParams } from "react-router-dom";

export default function ThreadContainer({ workspace }) {
const { threadSlug = null } = useParams();
const [threads, setThreads] = useState([]);
const [loading, setLoading] = useState(true);

Expand All @@ -20,7 +22,12 @@ export default function ThreadContainer({ workspace }) {
}, [workspace.slug]);

function removeThread(threadId) {
setThreads((prev) => prev.filter((thread) => thread.id !== threadId));
setThreads((prev) =>
prev.map((_t) => {
if (_t.id !== threadId) return _t;
return { ..._t, deleted: true };
})
);
}

if (loading) {
Expand All @@ -33,15 +40,26 @@ export default function ThreadContainer({ workspace }) {
);
}

const activeThreadIdx = !!threads.find(
(thread) => thread?.slug === threadSlug
)
? threads.findIndex((thread) => thread?.slug === threadSlug) + 1
: 0;
return (
<div className="flex flex-col">
<ThreadItem
idx={0}
activeIdx={activeThreadIdx}
isActive={activeThreadIdx === 0}
thread={{ slug: null, name: "default" }}
hasNext={threads.length > 0}
/>
{threads.map((thread, i) => (
<ThreadItem
key={thread.slug}
idx={i + 1}
activeIdx={activeThreadIdx}
isActive={activeThreadIdx === i + 1}
workspace={workspace}
onRemove={removeThread}
thread={thread}
Expand All @@ -54,7 +72,7 @@ export default function ThreadContainer({ workspace }) {
}

function NewThreadButton({ workspace }) {
const [loading, setLoading] = useState();
const [loading, setLoading] = useState(false);
const onClick = async () => {
setLoading(true);
const { thread, error } = await Workspace.threads.new(workspace.slug);
Expand All @@ -74,15 +92,22 @@ function NewThreadButton({ workspace }) {
className="w-full relative flex h-[40px] items-center border-none hover:bg-slate-600/20 rounded-lg"
>
<div className="flex w-full gap-x-2 items-center pl-4">
<div className="bg-zinc-600 p-2 rounded-lg h-[24px] w-[24px] flex items-center justify-center">
{loading ? (
<CircleNotch
weight="bold"
size={14}
className="shrink-0 animate-spin text-slate-100"
/>
) : (
<Plus weight="bold" size={14} className="shrink-0 text-slate-100" />
)}
</div>

{loading ? (
<CircleNotch className="animate-spin text-slate-300" />
) : (
<Plus className="text-slate-300" />
)}
{loading ? (
<p className="text-left text-slate-300 text-sm">starting thread...</p>
<p className="text-left text-slate-100 text-sm">Starting Thread...</p>
) : (
<p className="text-left text-slate-300 text-sm">new thread</p>
<p className="text-left text-slate-100 text-sm">New Thread</p>
)}
</div>
</button>
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ export default function ActiveWorkspaces() {
href={isActive ? null : paths.workspace.chat(workspace.slug)}
className={`
transition-all duration-[200ms]
flex flex-grow w-[75%] gap-x-2 py-[6px] px-[12px] rounded-lg text-slate-200 justify-start items-center border
hover:bg-workspace-item-selected-gradient hover:border-slate-100 hover:border-opacity-50
flex flex-grow w-[75%] gap-x-2 py-[6px] px-[12px] rounded-lg text-slate-200 justify-start items-center
hover:bg-workspace-item-selected-gradient
${
isActive
? "bg-workspace-item-selected-gradient border-slate-100 border-opacity-50"
: "bg-workspace-item-gradient bg-opacity-60 border-transparent"
? "border-2 bg-workspace-item-selected-gradient border-white"
: "border bg-workspace-item-gradient bg-opacity-60 border-transparent hover:border-slate-100 hover:border-opacity-50"
}`}
>
<div className="flex flex-row justify-between w-full">
Expand Down