mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 06:53:14 +00:00
feat: add pierre file tree to full-screen git panel (#1487)
* feat: add pierre file tree to full-screen git panel Render the dashboard git panel with real changed-file data using pierre diffs, add a full-screen toggle, and show a pierre FileTree explorer on the right when expanded. Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com> * feat: always show git panel as collapsible resizable card - git panel renders on every thread with collapse to a floating expand button and drag-resize, persisted to localStorage (matches left sidebar) - panel content wrapped in a rounded card with Git/Desktop/Terminal tabs above it; Desktop/Terminal and Review/Commits show Coming Soon - send button morphs into a stop button during an active run * fix: follow app theme for pierre diff rendering diffOptions used themeType "system" so @pierre/diffs followed the OS color scheme instead of the app's .dark class, producing unreadable dark-on-light diffs when the two disagreed. Add useDiffOptions() which resolves themeType from the app theme, and use it at all diff render sites. * fix: align git panel card bottom gutter with prompt bar --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
This commit is contained in:
parent
259ec57186
commit
4cabdb2351
12 changed files with 492 additions and 101 deletions
17
ui/bun.lock
17
ui/bun.lock
|
|
@ -7,8 +7,10 @@
|
|||
"dependencies": {
|
||||
"@base-ui/react": "^1.4.1",
|
||||
"@fontsource-variable/inter": "^5.2.8",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"@pierre/diffs": "^1.2.1",
|
||||
"@pierre/trees": "1.0.0-beta.4",
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
"@tanstack/react-devtools": "^0.10.0",
|
||||
"@tanstack/react-query": "^5.100.10",
|
||||
|
|
@ -22,6 +24,7 @@
|
|||
"clsx": "^2.1.1",
|
||||
"diff": "^9.0.0",
|
||||
"lucide-react": "^1.16.0",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"nitro": "latest",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
|
|
@ -407,6 +410,10 @@
|
|||
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
|
||||
|
||||
"@monaco-editor/loader": ["@monaco-editor/loader@1.7.0", "", { "dependencies": { "state-local": "^1.0.6" } }, "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA=="],
|
||||
|
||||
"@monaco-editor/react": ["@monaco-editor/react@4.7.0", "", { "dependencies": { "@monaco-editor/loader": "^1.5.0" }, "peerDependencies": { "monaco-editor": ">= 0.25.0 < 1", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA=="],
|
||||
|
||||
"@mswjs/interceptors": ["@mswjs/interceptors@0.41.9", "", { "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", "@open-draft/until": "^2.0.0", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "strict-event-emitter": "^0.5.1" } }, "sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w=="],
|
||||
|
||||
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
|
||||
|
|
@ -447,6 +454,8 @@
|
|||
|
||||
"@pierre/theme": ["@pierre/theme@1.0.3", "", {}, "sha512-sWHv11TMoqKxKDgTIk5VbhQjdPhs8DCcBxbjh3mRlS3YOM/OcrWoGX6MM8eBGn9cUu3M46Py0JnxsG2nJaFTuA=="],
|
||||
|
||||
"@pierre/trees": ["@pierre/trees@1.0.0-beta.4", "", { "dependencies": { "preact": "11.0.0-beta.0", "preact-render-to-string": "6.6.5" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-OfT1yk9ne8Te5+GB5zUY8yqE6B8BqjBHQJleH4lu8ltwNpoocZl4vXt1AzlEExpxI/pp+AFX5QG+lR3JjtTEag=="],
|
||||
|
||||
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.1", "", { "os": "android", "cpu": "arm64" }, "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg=="],
|
||||
|
||||
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg=="],
|
||||
|
|
@ -1607,6 +1616,8 @@
|
|||
|
||||
"minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
||||
|
||||
"monaco-editor": ["monaco-editor@0.52.2", "", {}, "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"msw": ["msw@2.14.6", "", { "dependencies": { "@inquirer/confirm": "^6.0.11", "@mswjs/interceptors": "^0.41.3", "@open-draft/deferred-promise": "^3.0.0", "@types/statuses": "^2.0.6", "cookie": "^1.1.1", "graphql": "^16.13.2", "headers-polyfill": "^5.0.1", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", "rettime": "^0.11.11", "statuses": "^2.0.2", "strict-event-emitter": "^0.5.1", "tough-cookie": "^6.0.1", "type-fest": "^5.5.0", "until-async": "^3.0.2", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": ">= 4.8.x" }, "optionalPeers": ["typescript"], "bin": { "msw": "cli/index.js" } }, "sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg=="],
|
||||
|
|
@ -1725,6 +1736,10 @@
|
|||
|
||||
"powershell-utils": ["powershell-utils@0.1.0", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="],
|
||||
|
||||
"preact": ["preact@11.0.0-beta.0", "", {}, "sha512-IcODoASASYwJ9kxz7+MJeiJhvLriwSb4y4mHIyxdgaRZp6kPUud7xytrk/6GZw8U3y6EFJaRb5wi9SrEK+8+lg=="],
|
||||
|
||||
"preact-render-to-string": ["preact-render-to-string@6.6.5", "", { "peerDependencies": { "preact": ">=10 || >= 11.0.0-0" } }, "sha512-O6MHzYNIKYaiSX3bOw0gGZfEbOmlIDtDfWwN1JJdc/T3ihzRT6tGGSEWE088dWrEDGa1u7101q+6fzQnO9XCPA=="],
|
||||
|
||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||
|
||||
"prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="],
|
||||
|
|
@ -1905,6 +1920,8 @@
|
|||
|
||||
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
||||
|
||||
"state-local": ["state-local@1.0.7", "", {}, "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="],
|
||||
|
||||
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||
|
||||
"std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="],
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
"@monaco-editor/react": "^4.7.0",
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"@pierre/diffs": "^1.2.1",
|
||||
"@pierre/trees": "1.0.0-beta.4",
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
"@tanstack/react-devtools": "^0.10.0",
|
||||
"@tanstack/react-query": "^5.100.10",
|
||||
|
|
|
|||
|
|
@ -1,62 +1,306 @@
|
|||
import { useMemo, useState } from "react";
|
||||
import { MultiFileDiff } from "@pierre/diffs/react";
|
||||
import { CaretDownIcon, GitPullRequestIcon } from "@phosphor-icons/react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { MultiFileDiff } from "@pierre/diffs/react"
|
||||
import {
|
||||
FileTree,
|
||||
useFileTree,
|
||||
useFileTreeSelection,
|
||||
} from "@pierre/trees/react"
|
||||
import {
|
||||
ArrowsInIcon,
|
||||
ArrowsOutIcon,
|
||||
CaretDownIcon,
|
||||
GitPullRequestIcon,
|
||||
SidebarSimpleIcon,
|
||||
} from "@phosphor-icons/react"
|
||||
import type { GitStatus, GitStatusEntry } from "@pierre/trees"
|
||||
|
||||
import { diffOptions } from "@/components/agents/utils/diffUtils";
|
||||
import type { AgentThread } from "@/lib/agents/types";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { AgentThread } from "@/lib/agents/types"
|
||||
import type { ChangedFileSummaryItem } from "@/components/agents/ported"
|
||||
import { useDiffOptions } from "@/components/agents/utils/diffUtils"
|
||||
import { summarizeChangedFiles } from "@/components/agents/ported"
|
||||
import { Z } from "@/components/agents/z-index"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
interface AgentGitPanelProps {
|
||||
thread: AgentThread;
|
||||
thread: AgentThread
|
||||
}
|
||||
|
||||
interface PanelFile {
|
||||
filePath: string
|
||||
treePath: string
|
||||
additions: number
|
||||
deletions: number
|
||||
originalContent: string
|
||||
modifiedContent: string
|
||||
status: GitStatus
|
||||
}
|
||||
|
||||
function deriveStatus(file: ChangedFileSummaryItem): GitStatus {
|
||||
if (file.originalContent.length === 0 && file.modifiedContent.length > 0) {
|
||||
return "added"
|
||||
}
|
||||
if (file.modifiedContent.length === 0 && file.originalContent.length > 0) {
|
||||
return "deleted"
|
||||
}
|
||||
return "modified"
|
||||
}
|
||||
|
||||
function commonDirPrefix(paths: Array<string>): string {
|
||||
const first = paths[0]
|
||||
if (paths.length === 0 || first === undefined) return ""
|
||||
const base = first.split("/").slice(0, -1)
|
||||
let depth = base.length
|
||||
for (const path of paths) {
|
||||
const segments = path.split("/").slice(0, -1)
|
||||
let i = 0
|
||||
while (i < depth && i < segments.length && segments[i] === base[i]) i++
|
||||
depth = i
|
||||
}
|
||||
return depth === 0 ? "" : `${base.slice(0, depth).join("/")}/`
|
||||
}
|
||||
|
||||
const PANEL_STORAGE_WIDTH = "open-swe.gitpanel.width"
|
||||
const PANEL_STORAGE_COLLAPSED = "open-swe.gitpanel.collapsed"
|
||||
const PANEL_DEFAULT_WIDTH = 420
|
||||
const PANEL_MIN_WIDTH = 320
|
||||
const PANEL_MAX_WIDTH = 720
|
||||
|
||||
function readStoredPanelWidth(): number {
|
||||
if (typeof window === "undefined") return PANEL_DEFAULT_WIDTH
|
||||
const raw = window.localStorage.getItem(PANEL_STORAGE_WIDTH)
|
||||
const parsed = raw ? Number(raw) : NaN
|
||||
if (!Number.isFinite(parsed)) return PANEL_DEFAULT_WIDTH
|
||||
return Math.min(PANEL_MAX_WIDTH, Math.max(PANEL_MIN_WIDTH, parsed))
|
||||
}
|
||||
|
||||
function readStoredPanelCollapsed(): boolean {
|
||||
if (typeof window === "undefined") return false
|
||||
return window.localStorage.getItem(PANEL_STORAGE_COLLAPSED) === "1"
|
||||
}
|
||||
|
||||
function PanelResizeHandle({
|
||||
width,
|
||||
onResize,
|
||||
}: {
|
||||
width: number
|
||||
onResize: (next: number) => void
|
||||
}) {
|
||||
const startRef = useRef<{ x: number; width: number } | null>(null)
|
||||
const [dragging, setDragging] = useState(false)
|
||||
|
||||
const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
|
||||
e.preventDefault()
|
||||
startRef.current = { x: e.clientX, width }
|
||||
setDragging(true)
|
||||
e.currentTarget.setPointerCapture(e.pointerId)
|
||||
}
|
||||
|
||||
const onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (!startRef.current) return
|
||||
onResize(startRef.current.width - (e.clientX - startRef.current.x))
|
||||
}
|
||||
|
||||
const onPointerUp = (e: React.PointerEvent<HTMLDivElement>) => {
|
||||
startRef.current = null
|
||||
setDragging(false)
|
||||
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
|
||||
e.currentTarget.releasePointerCapture(e.pointerId)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!dragging) return
|
||||
const prev = document.body.style.cursor
|
||||
document.body.style.cursor = "col-resize"
|
||||
return () => {
|
||||
document.body.style.cursor = prev
|
||||
}
|
||||
}, [dragging])
|
||||
|
||||
return (
|
||||
<div
|
||||
role="separator"
|
||||
aria-orientation="vertical"
|
||||
onPointerDown={onPointerDown}
|
||||
onPointerMove={onPointerMove}
|
||||
onPointerUp={onPointerUp}
|
||||
onPointerCancel={onPointerUp}
|
||||
className={cn(
|
||||
"absolute top-0 left-0 z-20 h-full w-1 cursor-col-resize touch-none select-none",
|
||||
"after:absolute after:inset-y-0 after:left-0 after:w-px after:bg-transparent after:transition-colors",
|
||||
"hover:after:bg-[var(--ui-border)]",
|
||||
dragging && "after:bg-[var(--ui-border)]"
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function treeThemeStyle(): React.CSSProperties {
|
||||
return {
|
||||
"--trees-theme-sidebar-bg": "var(--ui-surface)",
|
||||
"--trees-theme-sidebar-fg": "var(--ui-text)",
|
||||
"--trees-theme-sidebar-border": "var(--ui-border)",
|
||||
"--trees-theme-sidebar-header-fg": "var(--ui-text-dim)",
|
||||
"--trees-theme-list-hover-bg": "var(--ui-panel-2)",
|
||||
"--trees-theme-list-active-selection-bg": "var(--ui-accent-bubble)",
|
||||
"--trees-theme-list-active-selection-fg": "var(--ui-text)",
|
||||
"--trees-theme-input-bg": "var(--ui-panel)",
|
||||
"--trees-theme-input-border": "var(--ui-border)",
|
||||
"--trees-theme-focus-ring": "var(--ui-accent)",
|
||||
"--trees-theme-scrollbar-thumb": "var(--ui-border)",
|
||||
"--trees-theme-git-added-fg": "var(--ui-success)",
|
||||
"--trees-theme-git-deleted-fg": "var(--ui-danger)",
|
||||
"--trees-theme-git-modified-fg": "var(--ui-accent)",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
|
||||
export function AgentGitPanel({ thread }: AgentGitPanelProps) {
|
||||
const [tab, setTab] = useState<"diff" | "review" | "commits">("diff");
|
||||
const pr = thread.pr;
|
||||
const [topTab, setTopTab] = useState<"git" | "desktop" | "terminal">("git")
|
||||
const [tab, setTab] = useState<"diff" | "review" | "commits">("diff")
|
||||
const [collapsed, setCollapsedState] = useState(() => readStoredPanelCollapsed())
|
||||
const [width, setWidthState] = useState(() => readStoredPanelWidth())
|
||||
const [fullScreen, setFullScreen] = useState(false)
|
||||
|
||||
const fileContents = useMemo(() => {
|
||||
const defaults: Record<string, { original: string; modified: string }> = {
|
||||
"app.js": {
|
||||
original: "export function init() {\n mount();\n}\n",
|
||||
modified: "export function init() {\n // test comment\n mount();\n}\n",
|
||||
},
|
||||
"contextPanel.js": {
|
||||
original: "export function renderPanel() {\n return panel;\n}\n",
|
||||
modified: "export function renderPanel() {\n // test comment\n return panel;\n}\n",
|
||||
},
|
||||
"settings.js": {
|
||||
original: "export const defaults = {};\n",
|
||||
modified: "export const defaults = {};\n// test comment\n",
|
||||
},
|
||||
};
|
||||
const setCollapsed = useCallback((next: boolean) => {
|
||||
setCollapsedState(next)
|
||||
window.localStorage.setItem(PANEL_STORAGE_COLLAPSED, next ? "1" : "0")
|
||||
}, [])
|
||||
|
||||
return (thread.changedFiles ?? []).map((file) => ({
|
||||
path: file.path,
|
||||
const setWidth = useCallback((next: number) => {
|
||||
const clamped = Math.min(PANEL_MAX_WIDTH, Math.max(PANEL_MIN_WIDTH, next))
|
||||
setWidthState(clamped)
|
||||
window.localStorage.setItem(PANEL_STORAGE_WIDTH, String(clamped))
|
||||
}, [])
|
||||
const [selectedTreePath, setSelectedTreePath] = useState<string | null>(null)
|
||||
const sectionRefs = useRef<Record<string, HTMLDivElement | null>>({})
|
||||
const pr = thread.pr
|
||||
|
||||
const chunks = useMemo(
|
||||
() => thread.messages.flatMap((message) => message.chunks),
|
||||
[thread.messages]
|
||||
)
|
||||
|
||||
const files = useMemo<Array<PanelFile>>(() => {
|
||||
const summary = summarizeChangedFiles(chunks)
|
||||
const prefix = commonDirPrefix(summary.map((file) => file.filePath))
|
||||
return summary.map((file) => ({
|
||||
filePath: file.filePath,
|
||||
treePath:
|
||||
prefix && file.filePath.startsWith(prefix)
|
||||
? file.filePath.slice(prefix.length)
|
||||
: file.filePath,
|
||||
additions: file.additions,
|
||||
original: defaults[file.path]?.original ?? "",
|
||||
modified: defaults[file.path]?.modified ?? file.patch ?? "",
|
||||
}));
|
||||
}, [thread.changedFiles]);
|
||||
deletions: file.deletions,
|
||||
originalContent: file.originalContent,
|
||||
modifiedContent: file.modifiedContent,
|
||||
status: deriveStatus(file),
|
||||
}))
|
||||
}, [chunks])
|
||||
|
||||
const totals = useMemo(
|
||||
() =>
|
||||
files.reduce(
|
||||
(acc, file) => ({
|
||||
additions: acc.additions + file.additions,
|
||||
deletions: acc.deletions + file.deletions,
|
||||
}),
|
||||
{ additions: 0, deletions: 0 }
|
||||
),
|
||||
[files]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedTreePath) return
|
||||
const target = files.find((file) => file.treePath === selectedTreePath)
|
||||
if (!target) return
|
||||
sectionRefs.current[target.filePath]?.scrollIntoView({
|
||||
block: "start",
|
||||
behavior: "smooth",
|
||||
})
|
||||
}, [selectedTreePath, files])
|
||||
|
||||
if (collapsed) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCollapsed(false)}
|
||||
aria-label="Expand git panel"
|
||||
title="Expand git panel"
|
||||
className="fixed top-3 right-3 z-30 flex size-7 items-center justify-center rounded-md border border-border bg-background text-muted-foreground shadow-sm hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
<SidebarSimpleIcon className="size-4" />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className="flex h-full w-[420px] shrink-0 flex-col border-l border-[var(--ui-border)] bg-[var(--ui-surface)]">
|
||||
<div className="flex h-11 shrink-0 items-center gap-1 border-b border-[var(--ui-border)] px-3">
|
||||
{(["Git", "Desktop", "Terminal"] as const).map((label, i) => (
|
||||
<aside
|
||||
className={cn(
|
||||
"relative flex shrink-0 flex-col bg-[var(--ui-bg)]",
|
||||
fullScreen ? "fixed inset-0 !w-full" : "h-full"
|
||||
)}
|
||||
style={fullScreen ? { zIndex: Z.MODAL } : { width }}
|
||||
>
|
||||
<div className="flex h-11 shrink-0 items-center gap-1 px-3">
|
||||
{(
|
||||
[
|
||||
["git", "Git"],
|
||||
["desktop", "Desktop"],
|
||||
["terminal", "Terminal"],
|
||||
] as const
|
||||
).map(([id, label]) => (
|
||||
<button
|
||||
key={label}
|
||||
key={id}
|
||||
type="button"
|
||||
onClick={() => setTopTab(id)}
|
||||
className={cn(
|
||||
"rounded-md px-2.5 py-1 text-xs transition-colors",
|
||||
i === 0
|
||||
topTab === id
|
||||
? "bg-[var(--ui-accent-bubble)] font-medium text-[var(--ui-text)]"
|
||||
: "text-[var(--ui-text-dim)] hover:bg-[var(--ui-panel-2)]",
|
||||
: "text-[var(--ui-text-dim)] hover:bg-[var(--ui-panel-2)]"
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setFullScreen(false)
|
||||
setCollapsed(true)
|
||||
}}
|
||||
aria-label="Collapse git panel"
|
||||
title="Collapse git panel"
|
||||
className="ml-auto rounded-md p-1.5 text-[var(--ui-text-dim)] transition-colors hover:bg-[var(--ui-panel-2)] hover:text-[var(--ui-text)]"
|
||||
>
|
||||
<SidebarSimpleIcon className="size-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setFullScreen((v) => !v)}
|
||||
aria-label={fullScreen ? "Exit full screen" : "Enter full screen"}
|
||||
className="rounded-md p-1.5 text-[var(--ui-text-dim)] transition-colors hover:bg-[var(--ui-panel-2)] hover:text-[var(--ui-text)]"
|
||||
>
|
||||
{fullScreen ? (
|
||||
<ArrowsInIcon className="size-4" />
|
||||
) : (
|
||||
<ArrowsOutIcon className="size-4" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"flex min-h-0 flex-1 flex-col overflow-hidden rounded-xl border border-[var(--ui-border)] bg-[var(--ui-surface)] shadow-sm",
|
||||
fullScreen ? "mx-3 mb-3" : "mr-4 mb-4 ml-1"
|
||||
)}
|
||||
>
|
||||
{topTab !== "git" ? (
|
||||
<div className="flex flex-1 items-center justify-center p-6 text-xs text-[var(--ui-text-dim)]">
|
||||
Coming Soon
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{pr && (
|
||||
<div className="border-b border-[var(--ui-border)] px-4 py-3">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
|
|
@ -74,12 +318,6 @@ export function AgentGitPanel({ thread }: AgentGitPanelProps) {
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="shrink-0 rounded-md border border-[var(--ui-border)] px-2.5 py-1 text-xs hover:bg-[var(--ui-panel-2)]"
|
||||
>
|
||||
Mark as ready
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -89,7 +327,7 @@ export function AgentGitPanel({ thread }: AgentGitPanelProps) {
|
|||
[
|
||||
["diff", "Diff"],
|
||||
["review", "Review"],
|
||||
["commits", `Commits (${fileContents.length})`],
|
||||
["commits", "Commits"],
|
||||
] as const
|
||||
).map(([id, label]) => (
|
||||
<button
|
||||
|
|
@ -100,58 +338,155 @@ export function AgentGitPanel({ thread }: AgentGitPanelProps) {
|
|||
"rounded-md px-2.5 py-1 text-xs transition-colors",
|
||||
tab === id
|
||||
? "bg-[var(--ui-accent-bubble)] font-medium text-[var(--ui-text)]"
|
||||
: "text-[var(--ui-text-dim)] hover:bg-[var(--ui-panel-2)]",
|
||||
: "text-[var(--ui-text-dim)] hover:bg-[var(--ui-panel-2)]"
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
{files.length > 0 && (
|
||||
<span className="ml-auto flex items-center gap-2 text-[11px] text-[var(--ui-text-dim)]">
|
||||
<span>
|
||||
{files.length} file{files.length === 1 ? "" : "s"}
|
||||
</span>
|
||||
<span className="text-[var(--ui-success)]">
|
||||
+{totals.additions}
|
||||
</span>
|
||||
<span className="text-[var(--ui-danger)]">-{totals.deletions}</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="min-h-0 flex-1 overflow-y-auto">
|
||||
{tab === "diff" && fileContents.length > 0 ? (
|
||||
<div className="space-y-2 p-2">
|
||||
{fileContents.map((file) => (
|
||||
<FileDiffSection key={file.path} file={file} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-6 text-center text-xs text-[var(--ui-text-dim)]">
|
||||
{tab === "commits" ? "Commit history will appear here." : "No diff available."}
|
||||
<div className="flex min-h-0 flex-1">
|
||||
<div className="min-h-0 flex-1 overflow-y-auto">
|
||||
{tab === "diff" && files.length > 0 ? (
|
||||
<div className="space-y-2 p-2">
|
||||
{files.map((file) => (
|
||||
<FileDiffSection
|
||||
key={file.filePath}
|
||||
file={file}
|
||||
sectionRef={(node) => {
|
||||
sectionRefs.current[file.filePath] = node
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-6 text-center text-xs text-[var(--ui-text-dim)]">
|
||||
{tab === "diff" ? "No diff available." : "Coming Soon"}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{fullScreen && files.length > 0 && (
|
||||
<div className="w-72 shrink-0 border-l border-[var(--ui-border)] bg-[var(--ui-surface)]">
|
||||
<FileTreeExplorer
|
||||
files={files}
|
||||
selectedTreePath={selectedTreePath}
|
||||
onSelect={setSelectedTreePath}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{!fullScreen && <PanelResizeHandle width={width} onResize={setWidth} />}
|
||||
</aside>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function FileDiffSection({
|
||||
file,
|
||||
sectionRef,
|
||||
}: {
|
||||
file: { path: string; additions: number; original: string; modified: string };
|
||||
file: PanelFile
|
||||
sectionRef: (node: HTMLDivElement | null) => void
|
||||
}) {
|
||||
const [open, setOpen] = useState(true);
|
||||
const [open, setOpen] = useState(true)
|
||||
const diffOptions = useDiffOptions()
|
||||
|
||||
return (
|
||||
<div className="mb-2 overflow-hidden rounded-lg border border-[var(--ui-border)]">
|
||||
<div
|
||||
ref={sectionRef}
|
||||
className="mb-2 scroll-mt-2 overflow-hidden rounded-lg border border-[var(--ui-border)]"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen((v) => !v)}
|
||||
className="flex w-full items-center gap-2 bg-[var(--ui-panel-2)] px-3 py-2 text-left text-xs"
|
||||
>
|
||||
<CaretDownIcon className={cn("size-3 transition-transform", !open && "-rotate-90")} />
|
||||
<span className="font-medium text-[var(--ui-text)]">{file.path}</span>
|
||||
<span className="ml-auto text-[var(--ui-success)]">+{file.additions}</span>
|
||||
<CaretDownIcon
|
||||
className={cn("size-3 transition-transform", !open && "-rotate-90")}
|
||||
/>
|
||||
<span className="truncate font-medium text-[var(--ui-text)]">
|
||||
{file.treePath}
|
||||
</span>
|
||||
<span className="ml-auto flex shrink-0 items-center gap-2">
|
||||
<span className="text-[var(--ui-success)]">+{file.additions}</span>
|
||||
<span className="text-[var(--ui-danger)]">-{file.deletions}</span>
|
||||
</span>
|
||||
</button>
|
||||
{open && (
|
||||
<div className="max-h-[320px] overflow-auto bg-[var(--ui-panel)] p-2 font-mono text-[11px] leading-5">
|
||||
<div className="max-h-[420px] overflow-auto bg-[var(--ui-panel)] p-2 font-mono text-[11px] leading-5">
|
||||
<MultiFileDiff
|
||||
oldFile={{ name: file.path, contents: file.original }}
|
||||
newFile={{ name: file.path, contents: file.modified }}
|
||||
oldFile={{ name: file.treePath, contents: file.originalContent }}
|
||||
newFile={{ name: file.treePath, contents: file.modifiedContent }}
|
||||
options={diffOptions}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function FileTreeExplorer({
|
||||
files,
|
||||
selectedTreePath,
|
||||
onSelect,
|
||||
}: {
|
||||
files: Array<PanelFile>
|
||||
selectedTreePath: string | null
|
||||
onSelect: (path: string) => void
|
||||
}) {
|
||||
const paths = useMemo(() => files.map((file) => file.treePath), [files])
|
||||
const gitStatus = useMemo<Array<GitStatusEntry>>(
|
||||
() => files.map((file) => ({ path: file.treePath, status: file.status })),
|
||||
[files]
|
||||
)
|
||||
|
||||
const { model } = useFileTree({
|
||||
paths,
|
||||
gitStatus,
|
||||
initialExpansion: "open",
|
||||
flattenEmptyDirectories: true,
|
||||
search: true,
|
||||
icons: "standard",
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
model.resetPaths(paths)
|
||||
}, [model, paths])
|
||||
|
||||
useEffect(() => {
|
||||
model.setGitStatus(gitStatus)
|
||||
}, [model, gitStatus])
|
||||
|
||||
const selection = useFileTreeSelection(model)
|
||||
useEffect(() => {
|
||||
const path = selection[0]
|
||||
if (path) onSelect(path)
|
||||
}, [selection, onSelect])
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedTreePath) {
|
||||
model.scrollToPath(selectedTreePath, { focus: false })
|
||||
}
|
||||
}, [model, selectedTreePath])
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<FileTree model={model} style={{ height: "100%", ...treeThemeStyle() }} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useQueryClient } from "@tanstack/react-query"
|
|||
import type { PendingPrompt } from "@/lib/agents/pendingPrompts"
|
||||
import type { AgentThread, ImageChunk, Message } from "@/lib/agents/types"
|
||||
import type { ModelSelection } from "@/lib/agents/useModelOptions"
|
||||
import { AgentGitPanel } from "@/components/agents/AgentGitPanel"
|
||||
import { AgentPromptBar } from "@/components/agents/AgentPromptBar"
|
||||
import { MessageView } from "@/components/agents/ported"
|
||||
import {
|
||||
|
|
@ -169,8 +170,8 @@ export function AgentThreadView({ thread }: AgentThreadViewProps) {
|
|||
hasActiveRun && thread.messages.length === 0 && pendingPrompts.length > 0
|
||||
|
||||
return (
|
||||
<div className="flex min-w-0 flex-1 flex-col">
|
||||
<div className="flex min-h-0 flex-1 flex-col">
|
||||
<div className="flex min-w-0 flex-1">
|
||||
<div className="flex min-w-0 flex-1 flex-col">
|
||||
{hasMessages ? (
|
||||
<div className="relative flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
<MessageView
|
||||
|
|
@ -220,6 +221,7 @@ export function AgentThreadView({ thread }: AgentThreadViewProps) {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<AgentGitPanel thread={thread} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -360,14 +360,14 @@ export const CloudPromptBar = memo(function CloudPromptBarComponent({
|
|||
<ImagePlus className="size-4" />
|
||||
</button>
|
||||
|
||||
{canCancel && (
|
||||
{canCancel && !canSubmit ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
disabled={cancelling}
|
||||
aria-label="Cancel run"
|
||||
title="Cancel run"
|
||||
className="flex size-7 shrink-0 items-center justify-center rounded-full border border-[var(--ui-border)] bg-[var(--ui-panel-2)] text-[color:var(--ui-text-muted)] transition-colors hover:text-[color:var(--ui-text)] disabled:cursor-default disabled:opacity-60"
|
||||
className="flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--ui-accent)] text-white transition-opacity hover:opacity-90 disabled:cursor-default disabled:opacity-60"
|
||||
>
|
||||
{cancelling ? (
|
||||
<LoaderCircle className="size-3.5 animate-spin" />
|
||||
|
|
@ -379,21 +379,21 @@ export const CloudPromptBar = memo(function CloudPromptBarComponent({
|
|||
/>
|
||||
)}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSubmit}
|
||||
disabled={!canSubmit}
|
||||
aria-label="Send message"
|
||||
className="flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--ui-accent)] text-white transition-opacity hover:opacity-90 disabled:cursor-default disabled:opacity-40"
|
||||
>
|
||||
{disabled ? (
|
||||
<LoaderCircle className="size-3.5 animate-spin" />
|
||||
) : (
|
||||
<ArrowUp className="size-3.5" strokeWidth={2.5} />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSubmit}
|
||||
disabled={!canSubmit}
|
||||
aria-label="Send message"
|
||||
className="flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--ui-accent)] text-white transition-opacity hover:opacity-90 disabled:cursor-default disabled:opacity-40"
|
||||
>
|
||||
{disabled ? (
|
||||
<LoaderCircle className="size-3.5 animate-spin" />
|
||||
) : (
|
||||
<ArrowUp className="size-3.5" strokeWidth={2.5} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { useRef, useEffect, useLayoutEffect, useCallback, memo, useMemo, useStat
|
|||
import { ChevronDown, ChevronUp } from "lucide-react";
|
||||
import { diffLines } from "diff";
|
||||
import { MultiFileDiff } from "@pierre/diffs/react";
|
||||
import { diffOptions } from "@/components/agents/utils/diffUtils";
|
||||
import { useDiffOptions } from "@/components/agents/utils/diffUtils";
|
||||
import { CodeBlock } from "./CodeBlock";
|
||||
import { Markdown } from "./Markdown";
|
||||
import { ToolExecution } from "./ToolExecution";
|
||||
|
|
@ -180,6 +180,7 @@ const TurnChangedFilesCard = memo(function TurnChangedFilesCard({
|
|||
projectPath?: string;
|
||||
}) {
|
||||
const [expandedByPath, setExpandedByPath] = useState<Record<string, boolean>>({});
|
||||
const diffOptions = useDiffOptions();
|
||||
|
||||
const toggleFile = useCallback((filePath: string) => {
|
||||
setExpandedByPath((prev) => ({ ...prev, [filePath]: !prev[filePath] }));
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import {
|
|||
X,
|
||||
Undo2,
|
||||
} from "lucide-react";
|
||||
import { diffOptions } from "@/components/agents/utils/diffUtils";
|
||||
import { useDiffOptions } from "@/components/agents/utils/diffUtils";
|
||||
|
||||
const STATUS_LABELS: Partial<Record<GitFileStatus, string>> = {
|
||||
"index-modified": "M",
|
||||
|
|
@ -961,6 +961,7 @@ const DiffCard = memo(function DiffCard({
|
|||
defaultExpanded: boolean;
|
||||
}) {
|
||||
const [expanded, setExpanded] = useState(defaultExpanded);
|
||||
const diffOptions = useDiffOptions();
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { memo, useState, useCallback, useRef, useLayoutEffect } from "react";
|
||||
import { memo, useState, useCallback, useMemo, useRef, useLayoutEffect } from "react";
|
||||
import { diffLines } from "diff";
|
||||
import { MultiFileDiff } from "@pierre/diffs/react";
|
||||
import type { ToolExecutionChunk, AcpToolKind } from "@/lib/agents/types";
|
||||
import { DiffView } from "./DiffView";
|
||||
import { diffOptions } from "@/components/agents/utils/diffUtils";
|
||||
import { useDiffOptions } from "@/components/agents/utils/diffUtils";
|
||||
|
||||
interface ToolExecutionProps {
|
||||
chunk: ToolExecutionChunk;
|
||||
|
|
@ -106,11 +106,6 @@ function formatToolDisplay(
|
|||
}
|
||||
}
|
||||
|
||||
const inlineDiffOptions = {
|
||||
...diffOptions,
|
||||
disableFileHeader: true,
|
||||
};
|
||||
|
||||
const InlineDiffCollapsible = memo(function InlineDiffCollapsible({
|
||||
filePath,
|
||||
fileName,
|
||||
|
|
@ -130,6 +125,11 @@ const InlineDiffCollapsible = memo(function InlineDiffCollapsible({
|
|||
}) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const toggle = useCallback(() => setExpanded((prev) => !prev), []);
|
||||
const diffOptions = useDiffOptions();
|
||||
const inlineDiffOptions = useMemo(
|
||||
() => ({ ...diffOptions, disableFileHeader: true }),
|
||||
[diffOptions]
|
||||
);
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
const [scrolledFromTop, setScrolledFromTop] = useState(false);
|
||||
const [scrolledFromBottom, setScrolledFromBottom] = useState(false);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,12 @@ export { HeaderBar } from "./HeaderBar";
|
|||
export { Logo } from "./Logo";
|
||||
export { Markdown } from "./Markdown";
|
||||
export { MarkdownTable } from "./MarkdownTable";
|
||||
export { MessageView, summarizeChangedFiles, type MessageViewScrollControl } from "./MessageView";
|
||||
export {
|
||||
MessageView,
|
||||
summarizeChangedFiles,
|
||||
type ChangedFileSummaryItem,
|
||||
type MessageViewScrollControl,
|
||||
} from "./MessageView";
|
||||
export { PanelResizeHandle } from "./PanelResizeHandle";
|
||||
export { ShellCommand } from "./ShellCommand";
|
||||
export { TodoList } from "./TodoList";
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import { useMemo } from "react";
|
||||
import { useResolvedTheme } from "@/lib/theme";
|
||||
|
||||
export const DIFF_UNSAFE_CSS = `
|
||||
[data-diffs-header],
|
||||
[data-diff],
|
||||
|
|
@ -62,3 +65,11 @@ export const diffOptions = {
|
|||
unsafeCSS: DIFF_UNSAFE_CSS,
|
||||
collapsedContextThreshold: 4,
|
||||
};
|
||||
|
||||
export function useDiffOptions() {
|
||||
const resolvedTheme = useResolvedTheme();
|
||||
return useMemo(
|
||||
() => ({ ...diffOptions, themeType: resolvedTheme }),
|
||||
[resolvedTheme]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,8 +125,8 @@ export interface FileRoutesByFullPath {
|
|||
'/review': typeof ReviewRoute
|
||||
'/usage': typeof UsageRoute
|
||||
'/agents/$threadId': typeof AgentsThreadIdRoute
|
||||
'/review/styles': typeof ReviewStylesRoute
|
||||
'/agents/instructions': typeof AgentsInstructionsRoute
|
||||
'/review/styles': typeof ReviewStylesRoute
|
||||
'/agents/': typeof AgentsIndexRoute
|
||||
'/agents/automations/$scheduleId': typeof AgentsAutomationsScheduleIdRoute
|
||||
'/agents/automations/new': typeof AgentsAutomationsNewRoute
|
||||
|
|
@ -143,8 +143,8 @@ export interface FileRoutesByTo {
|
|||
'/review': typeof ReviewRoute
|
||||
'/usage': typeof UsageRoute
|
||||
'/agents/$threadId': typeof AgentsThreadIdRoute
|
||||
'/review/styles': typeof ReviewStylesRoute
|
||||
'/agents/instructions': typeof AgentsInstructionsRoute
|
||||
'/review/styles': typeof ReviewStylesRoute
|
||||
'/agents': typeof AgentsIndexRoute
|
||||
'/agents/automations/$scheduleId': typeof AgentsAutomationsScheduleIdRoute
|
||||
'/agents/automations/new': typeof AgentsAutomationsNewRoute
|
||||
|
|
@ -163,8 +163,8 @@ export interface FileRoutesById {
|
|||
'/review': typeof ReviewRoute
|
||||
'/usage': typeof UsageRoute
|
||||
'/agents/$threadId': typeof AgentsThreadIdRoute
|
||||
'/review_/styles': typeof ReviewStylesRoute
|
||||
'/agents_/instructions': typeof AgentsInstructionsRoute
|
||||
'/review_/styles': typeof ReviewStylesRoute
|
||||
'/agents/': typeof AgentsIndexRoute
|
||||
'/agents/automations/$scheduleId': typeof AgentsAutomationsScheduleIdRoute
|
||||
'/agents/automations/new': typeof AgentsAutomationsNewRoute
|
||||
|
|
@ -184,8 +184,8 @@ export interface FileRouteTypes {
|
|||
| '/review'
|
||||
| '/usage'
|
||||
| '/agents/$threadId'
|
||||
| '/review/styles'
|
||||
| '/agents/instructions'
|
||||
| '/review/styles'
|
||||
| '/agents/'
|
||||
| '/agents/automations/$scheduleId'
|
||||
| '/agents/automations/new'
|
||||
|
|
@ -202,8 +202,8 @@ export interface FileRouteTypes {
|
|||
| '/review'
|
||||
| '/usage'
|
||||
| '/agents/$threadId'
|
||||
| '/review/styles'
|
||||
| '/agents/instructions'
|
||||
| '/review/styles'
|
||||
| '/agents'
|
||||
| '/agents/automations/$scheduleId'
|
||||
| '/agents/automations/new'
|
||||
|
|
@ -221,8 +221,8 @@ export interface FileRouteTypes {
|
|||
| '/review'
|
||||
| '/usage'
|
||||
| '/agents/$threadId'
|
||||
| '/review_/styles'
|
||||
| '/agents_/instructions'
|
||||
| '/review_/styles'
|
||||
| '/agents/'
|
||||
| '/agents/automations/$scheduleId'
|
||||
| '/agents/automations/new'
|
||||
|
|
@ -240,8 +240,8 @@ export interface RootRouteChildren {
|
|||
MySettingsRoute: typeof MySettingsRoute
|
||||
ReviewRoute: typeof ReviewRoute
|
||||
UsageRoute: typeof UsageRoute
|
||||
ReviewStylesRoute: typeof ReviewStylesRoute
|
||||
AgentsInstructionsRoute: typeof AgentsInstructionsRoute
|
||||
ReviewStylesRoute: typeof ReviewStylesRoute
|
||||
ReviewRepositoriesOwnerRoute: typeof ReviewRepositoriesOwnerRoute
|
||||
}
|
||||
|
||||
|
|
@ -398,8 +398,8 @@ const rootRouteChildren: RootRouteChildren = {
|
|||
MySettingsRoute: MySettingsRoute,
|
||||
ReviewRoute: ReviewRoute,
|
||||
UsageRoute: UsageRoute,
|
||||
ReviewStylesRoute: ReviewStylesRoute,
|
||||
AgentsInstructionsRoute: AgentsInstructionsRoute,
|
||||
ReviewStylesRoute: ReviewStylesRoute,
|
||||
ReviewRepositoriesOwnerRoute: ReviewRepositoriesOwnerRoute,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
|
|
|
|||
18
ui/yarn.lock
18
ui/yarn.lock
|
|
@ -1658,6 +1658,14 @@
|
|||
resolved "https://registry.yarnpkg.com/@pierre/theme/-/theme-1.0.3.tgz#eec66e55576f981000f4816263632d3ba1479229"
|
||||
integrity sha512-sWHv11TMoqKxKDgTIk5VbhQjdPhs8DCcBxbjh3mRlS3YOM/OcrWoGX6MM8eBGn9cUu3M46Py0JnxsG2nJaFTuA==
|
||||
|
||||
"@pierre/trees@1.0.0-beta.4":
|
||||
version "1.0.0-beta.4"
|
||||
resolved "https://registry.yarnpkg.com/@pierre/trees/-/trees-1.0.0-beta.4.tgz#d19881431e2396c19641b6bd36d1d4e5a267cac8"
|
||||
integrity sha512-OfT1yk9ne8Te5+GB5zUY8yqE6B8BqjBHQJleH4lu8ltwNpoocZl4vXt1AzlEExpxI/pp+AFX5QG+lR3JjtTEag==
|
||||
dependencies:
|
||||
preact "11.0.0-beta.0"
|
||||
preact-render-to-string "6.6.5"
|
||||
|
||||
"@rolldown/binding-android-arm64@1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz#ebe1264e43ba5bb224c58c85e0ac238f87e5ad14"
|
||||
|
|
@ -6710,6 +6718,16 @@ powershell-utils@^0.1.0:
|
|||
resolved "https://registry.yarnpkg.com/powershell-utils/-/powershell-utils-0.1.0.tgz#5a42c9a824fb4f2f251ccb41aaae73314f5d6ac2"
|
||||
integrity sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==
|
||||
|
||||
preact-render-to-string@6.6.5:
|
||||
version "6.6.5"
|
||||
resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-6.6.5.tgz#1be84cc8565e3bcfcf2a33cb9c61185e6498ab31"
|
||||
integrity sha512-O6MHzYNIKYaiSX3bOw0gGZfEbOmlIDtDfWwN1JJdc/T3ihzRT6tGGSEWE088dWrEDGa1u7101q+6fzQnO9XCPA==
|
||||
|
||||
preact@11.0.0-beta.0:
|
||||
version "11.0.0-beta.0"
|
||||
resolved "https://registry.yarnpkg.com/preact/-/preact-11.0.0-beta.0.tgz#c6e5c6e8657abf18b4a637eae88b5b377d36973f"
|
||||
integrity sha512-IcODoASASYwJ9kxz7+MJeiJhvLriwSb4y4mHIyxdgaRZp6kPUud7xytrk/6GZw8U3y6EFJaRb5wi9SrEK+8+lg==
|
||||
|
||||
prettier-plugin-tailwindcss@^0.7.2:
|
||||
version "0.7.4"
|
||||
resolved "https://registry.yarnpkg.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.4.tgz#eadd3f9d2e4bfba2994b22f80dda25804a608a9a"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue