|
1 | 1 | import type { Session } from "@coder-studio/core"; |
2 | 2 | import { useAtomValue, useSetAtom } from "jotai"; |
3 | 3 | import { ArrowRight, FlipHorizontal, FlipVertical, X } from "lucide-react"; |
4 | | -import { type DragEvent, type FC, useState } from "react"; |
| 4 | +import { type DragEvent, type FC, type PointerEvent, useRef, useState } from "react"; |
5 | 5 | import { dispatchCommandAtom } from "../../../../atoms/connection"; |
6 | 6 | import { sessionsAtom } from "../../../../atoms/sessions"; |
7 | 7 | import { Button, IconButton, StatusDot, Tag, ThemedIcon, Tooltip } from "../../../../components/ui"; |
@@ -46,7 +46,9 @@ export const DraftLauncher: FC<DraftLauncherProps> = ({ |
46 | 46 | const t = useTranslation(); |
47 | 47 | const dispatch = useAtomValue(dispatchCommandAtom); |
48 | 48 | const setSessions = useSetAtom(sessionsAtom); |
| 49 | + const [activePanel, setActivePanel] = useState<"agent" | "file">("agent"); |
49 | 50 | const [isFileDropTarget, setIsFileDropTarget] = useState(false); |
| 51 | + const swipeStartXRef = useRef<number | null>(null); |
50 | 52 | const { states, launch } = useProviderLauncher( |
51 | 53 | dispatch, |
52 | 54 | workspaceId, |
@@ -189,6 +191,34 @@ export const DraftLauncher: FC<DraftLauncherProps> = ({ |
189 | 191 | onOpenFile?.(paneId, path); |
190 | 192 | }; |
191 | 193 |
|
| 194 | + const handleCarouselPointerDown = (event: PointerEvent<HTMLDivElement>) => { |
| 195 | + if (event.pointerType === "mouse") { |
| 196 | + return; |
| 197 | + } |
| 198 | + |
| 199 | + swipeStartXRef.current = event.clientX; |
| 200 | + }; |
| 201 | + |
| 202 | + const handleCarouselPointerUp = (event: PointerEvent<HTMLDivElement>) => { |
| 203 | + const startX = swipeStartXRef.current; |
| 204 | + swipeStartXRef.current = null; |
| 205 | + |
| 206 | + if (startX === null || event.pointerType === "mouse") { |
| 207 | + return; |
| 208 | + } |
| 209 | + |
| 210 | + const deltaX = event.clientX - startX; |
| 211 | + if (Math.abs(deltaX) < 48) { |
| 212 | + return; |
| 213 | + } |
| 214 | + |
| 215 | + setActivePanel(deltaX < 0 ? "file" : "agent"); |
| 216 | + }; |
| 217 | + |
| 218 | + const handleCarouselPointerCancel = () => { |
| 219 | + swipeStartXRef.current = null; |
| 220 | + }; |
| 221 | + |
192 | 222 | return ( |
193 | 223 | <div |
194 | 224 | className={`session-card agent-pane${dragState?.isDragging ? " draft-launcher--dragging" : ""}${dragState?.isActiveDropTarget || isFileDropTarget ? " draft-launcher--drop-target" : ""}`} |
@@ -254,7 +284,12 @@ export const DraftLauncher: FC<DraftLauncherProps> = ({ |
254 | 284 | <div className="agent-draft-launcher"> |
255 | 285 | <div className="agent-draft-content"> |
256 | 286 | <div className="agent-draft-component"> |
257 | | - <div className="agent-draft-component-row"> |
| 287 | + <div |
| 288 | + className={`agent-draft-component-row${activePanel === "file" ? " agent-draft-component-row--file" : ""}`} |
| 289 | + onPointerCancel={handleCarouselPointerCancel} |
| 290 | + onPointerDown={handleCarouselPointerDown} |
| 291 | + onPointerUp={handleCarouselPointerUp} |
| 292 | + > |
258 | 293 | {/* Agent panel */} |
259 | 294 | <div className="agent-draft-panel"> |
260 | 295 | <div className="agent-draft-panel-header"> |
@@ -384,6 +419,22 @@ export const DraftLauncher: FC<DraftLauncherProps> = ({ |
384 | 419 | </div> |
385 | 420 | </div> |
386 | 421 |
|
| 422 | + <div className="agent-draft-carousel-dots" role="group" aria-label="Draft panels"> |
| 423 | + {[ |
| 424 | + { id: "agent" as const, label: "Agent" }, |
| 425 | + { id: "file" as const, label: "File Editor" }, |
| 426 | + ].map((panel) => ( |
| 427 | + <button |
| 428 | + key={panel.id} |
| 429 | + aria-label={panel.label} |
| 430 | + aria-pressed={activePanel === panel.id} |
| 431 | + className={`agent-draft-carousel-dot${activePanel === panel.id ? " agent-draft-carousel-dot--active" : ""}`} |
| 432 | + onClick={() => setActivePanel(panel.id)} |
| 433 | + type="button" |
| 434 | + /> |
| 435 | + ))} |
| 436 | + </div> |
| 437 | + |
387 | 438 | <div className="agent-draft-footer">点击启动 Agent 或直接拖拽文件到右侧区域打开</div> |
388 | 439 | </div> |
389 | 440 | </div> |
|
0 commit comments