mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-04 11:22:10 +00:00
fix(ui): resolve eslint errors in dashboard components (#1538)
Clears ~83 pre-existing eslint errors in the UI: auto-fixable rules (array-type, import/order, sort-imports, type-only imports, redundant assertions/conditions) plus manual fixes for unnecessary conditions and banned @ts-nocheck directives. The 8 ported components excluded from tsconfig are now also ignored by eslint so type-aware linting no longer fails to parse them. Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
This commit is contained in:
parent
3297e799de
commit
0108d764d9
29 changed files with 91 additions and 83 deletions
|
|
@ -11,6 +11,17 @@ export default [
|
||||||
"dist/**",
|
"dist/**",
|
||||||
"src/routeTree.gen.ts",
|
"src/routeTree.gen.ts",
|
||||||
"src/components/ui/**",
|
"src/components/ui/**",
|
||||||
|
// Ported from a desktop host app and excluded from tsconfig (not yet
|
||||||
|
// integrated), so type-aware linting cannot resolve them. Keep this list
|
||||||
|
// in sync with the `exclude` entries in tsconfig.json.
|
||||||
|
"src/components/agents/ported/ChatView.tsx",
|
||||||
|
"src/components/agents/ported/PromptBar.tsx",
|
||||||
|
"src/components/agents/ported/BranchSelector.tsx",
|
||||||
|
"src/components/agents/ported/ContextIndicator.tsx",
|
||||||
|
"src/components/agents/ported/SourceControlPanel.tsx",
|
||||||
|
"src/components/agents/ported/SourceControlTile.tsx",
|
||||||
|
"src/components/agents/ported/Footer.tsx",
|
||||||
|
"src/components/agents/ported/ThreadPicker.tsx",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
...tanstackConfig,
|
...tanstackConfig,
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ export function AgentInstructionsPanel() {
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (detail.data) setDraft(detail.data.instructions ?? "");
|
if (detail.data) setDraft(detail.data.instructions);
|
||||||
}, [detail.data?.instructions, detail.data?.full_name]);
|
}, [detail.data?.instructions, detail.data?.full_name]);
|
||||||
|
|
||||||
const create = useMutation({
|
const create = useMutation({
|
||||||
|
|
@ -104,7 +104,7 @@ export function AgentInstructionsPanel() {
|
||||||
const canAdd = normalizedAddRepo !== null && !configured.has(normalizedAddRepo);
|
const canAdd = normalizedAddRepo !== null && !configured.has(normalizedAddRepo);
|
||||||
const active =
|
const active =
|
||||||
detail.data ?? instructions.data?.find((s) => s.full_name === selected) ?? null;
|
detail.data ?? instructions.data?.find((s) => s.full_name === selected) ?? null;
|
||||||
const dirty = active != null && draft !== (active.instructions ?? "");
|
const dirty = active != null && draft !== active.instructions;
|
||||||
|
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
if (!normalizedAddRepo || !canAdd) return;
|
if (!normalizedAddRepo || !canAdd) return;
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,11 @@ import {
|
||||||
IoSunnyOutline,
|
IoSunnyOutline,
|
||||||
} from "react-icons/io5";
|
} from "react-icons/io5";
|
||||||
|
|
||||||
|
import type { SessionUser } from "@/lib/api";
|
||||||
|
import type { Theme } from "@/lib/theme";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
import { api, type SessionUser } from "@/lib/api";
|
import { api } from "@/lib/api";
|
||||||
import { useTheme, type Theme } from "@/lib/theme";
|
import { useTheme } from "@/lib/theme";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const THEME_OPTIONS: Array<{ value: Theme; label: string; icon: typeof IoSunnyOutline }> = [
|
const THEME_OPTIONS: Array<{ value: Theme; label: string; icon: typeof IoSunnyOutline }> = [
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ import type { GitStatus, GitStatusEntry } from "@pierre/trees"
|
||||||
|
|
||||||
import type { AgentThread, Message } from "@/lib/agents/types"
|
import type { AgentThread, Message } from "@/lib/agents/types"
|
||||||
import type { ThreadPrDiffFile } from "@/lib/agents/api"
|
import type { ThreadPrDiffFile } from "@/lib/agents/api"
|
||||||
|
import type { ChangedFileSummaryItem } from "@/components/agents/messages"
|
||||||
import { useAgentThreadPrDiff } from "@/lib/agents/queries"
|
import { useAgentThreadPrDiff } from "@/lib/agents/queries"
|
||||||
import { buttonVariants } from "@/components/ui/button"
|
import { buttonVariants } from "@/components/ui/button"
|
||||||
import type { ChangedFileSummaryItem } from "@/components/agents/messages"
|
|
||||||
import { useDiffOptions } from "@/components/agents/utils/diffUtils"
|
import { useDiffOptions } from "@/components/agents/utils/diffUtils"
|
||||||
import { summarizeChangedFiles } from "@/components/agents/ported"
|
import { summarizeChangedFiles } from "@/components/agents/ported"
|
||||||
import { Z } from "@/components/agents/z-index"
|
import { Z } from "@/components/agents/z-index"
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
import { ReplyCard } from "@/components/agents/ported/ReplyCard";
|
|
||||||
import { ShellCommand } from "@/components/agents/ported/ShellCommand";
|
|
||||||
import { ToolExecution } from "@/components/agents/ported/ToolExecution";
|
|
||||||
import type { Message } from "@/lib/agents/types";
|
|
||||||
|
|
||||||
import { ChunkRenderer } from "./ChunkRenderer";
|
import { ChunkRenderer } from "./ChunkRenderer";
|
||||||
import { ReasoningBlock } from "./ReasoningBlock";
|
import { ReasoningBlock } from "./ReasoningBlock";
|
||||||
import { buildRenderItems, summarizeExploration, type RenderItem } from "./renderItems";
|
import { buildRenderItems, summarizeExploration } from "./renderItems";
|
||||||
import { SubagentGroup } from "@/components/agents/subagents";
|
|
||||||
import { summarizeChangedFiles } from "./summarizeChangedFiles";
|
import { summarizeChangedFiles } from "./summarizeChangedFiles";
|
||||||
import { TurnChangedFilesCard } from "./TurnChangedFilesCard";
|
import { TurnChangedFilesCard } from "./TurnChangedFilesCard";
|
||||||
import { WorkSummary } from "./WorkSummary";
|
import { WorkSummary } from "./WorkSummary";
|
||||||
|
import type { RenderItem } from "./renderItems";
|
||||||
|
import type { Message } from "@/lib/agents/types";
|
||||||
import type { ApprovalCallbacks, ChangedFileSummaryItem } from "./types";
|
import type { ApprovalCallbacks, ChangedFileSummaryItem } from "./types";
|
||||||
|
import { SubagentGroup } from "@/components/agents/subagents";
|
||||||
|
import { ToolExecution } from "@/components/agents/ported/ToolExecution";
|
||||||
|
import { ShellCommand } from "@/components/agents/ported/ShellCommand";
|
||||||
|
import { ReplyCard } from "@/components/agents/ported/ReplyCard";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render-item types kept visible (not collapsed) when a turn finishes — the
|
* Render-item types kept visible (not collapsed) when a turn finishes — the
|
||||||
|
|
@ -24,9 +24,9 @@ const REPLY_ITEM_TYPES = new Set<RenderItem["type"]>(["text-chunk", "reply-item"
|
||||||
* Split a finished turn's items into collapsible work and the trailing reply,
|
* Split a finished turn's items into collapsible work and the trailing reply,
|
||||||
* where the reply is the maximal suffix made up solely of reply/text items.
|
* where the reply is the maximal suffix made up solely of reply/text items.
|
||||||
*/
|
*/
|
||||||
function splitWorkAndReply(items: RenderItem[]): {
|
function splitWorkAndReply(items: Array<RenderItem>): {
|
||||||
workItems: RenderItem[];
|
workItems: Array<RenderItem>;
|
||||||
replyItems: RenderItem[];
|
replyItems: Array<RenderItem>;
|
||||||
} {
|
} {
|
||||||
let splitIndex = items.length;
|
let splitIndex = items.length;
|
||||||
while (splitIndex > 0) {
|
while (splitIndex > 0) {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
|
import type { Chunk } from "@/lib/agents/types";
|
||||||
|
import type { ApprovalCallbacks } from "./types";
|
||||||
import { CodeBlock } from "@/components/agents/ported/CodeBlock";
|
import { CodeBlock } from "@/components/agents/ported/CodeBlock";
|
||||||
import { Markdown } from "@/components/agents/ported/Markdown";
|
import { Markdown } from "@/components/agents/ported/Markdown";
|
||||||
import { ToolExecution } from "@/components/agents/ported/ToolExecution";
|
import { ToolExecution } from "@/components/agents/ported/ToolExecution";
|
||||||
import type { Chunk } from "@/lib/agents/types";
|
|
||||||
|
|
||||||
import type { ApprovalCallbacks } from "./types";
|
|
||||||
|
|
||||||
export function ChunkRenderer({
|
export function ChunkRenderer({
|
||||||
chunk,
|
chunk,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
// @ts-nocheck — ported from open-swe-app (Electron); strict checks applied when wiring cloud APIs.
|
|
||||||
import { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
import { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||||
import { ChevronDown } from "lucide-react";
|
import { ChevronDown } from "lucide-react";
|
||||||
|
|
||||||
import { useLiveMarkdownMessageId } from "@/lib/agents/provider/useLiveMarkdownMessageId";
|
|
||||||
|
|
||||||
import { AgentMessage } from "./AgentMessage";
|
import { AgentMessage } from "./AgentMessage";
|
||||||
import { ThinkingSpinner } from "./ThinkingSpinner";
|
import { ThinkingSpinner } from "./ThinkingSpinner";
|
||||||
import { UserMessage } from "./UserMessage";
|
import { UserMessage } from "./UserMessage";
|
||||||
import type { MessagesProps } from "./types";
|
import type { MessagesProps } from "./types";
|
||||||
|
import { useLiveMarkdownMessageId } from "@/lib/agents/provider/useLiveMarkdownMessageId";
|
||||||
|
|
||||||
const BOTTOM_LOCK_THRESHOLD_PX = 24;
|
const BOTTOM_LOCK_THRESHOLD_PX = 24;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import { formatElapsed } from "@/lib/utils";
|
import { formatElapsed } from "@/lib/utils";
|
||||||
|
|
||||||
const BUSY_TEXTS: { present: string; past: string }[] = [
|
const BUSY_TEXTS: Array<{ present: string; past: string }> = [
|
||||||
{ present: "vibing...", past: "Vibed" },
|
{ present: "vibing...", past: "Vibed" },
|
||||||
{ present: "noodling...", past: "Noodled" },
|
{ present: "noodling...", past: "Noodled" },
|
||||||
{ present: "pondering...", past: "Pondered" },
|
{ present: "pondering...", past: "Pondered" },
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,8 @@ import { memo, useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { ChevronDown, ChevronUp } from "lucide-react";
|
import { ChevronDown, ChevronUp } from "lucide-react";
|
||||||
import { MultiFileDiff } from "@pierre/diffs/react";
|
import { MultiFileDiff } from "@pierre/diffs/react";
|
||||||
|
|
||||||
import { useDiffOptions, warmDiffHighlighter } from "@/components/agents/utils/diffUtils";
|
|
||||||
|
|
||||||
import type { ChangedFileSummaryItem } from "./types";
|
import type { ChangedFileSummaryItem } from "./types";
|
||||||
|
import { useDiffOptions, warmDiffHighlighter } from "@/components/agents/utils/diffUtils";
|
||||||
|
|
||||||
function stripProjectPathForDisplay(path: string, projectPath?: string): string {
|
function stripProjectPathForDisplay(path: string, projectPath?: string): string {
|
||||||
if (!projectPath) return path;
|
if (!projectPath) return path;
|
||||||
|
|
@ -84,7 +83,7 @@ export const TurnChangedFilesCard = memo(function TurnChangedFilesCard({
|
||||||
totals,
|
totals,
|
||||||
projectPath,
|
projectPath,
|
||||||
}: {
|
}: {
|
||||||
files: ChangedFileSummaryItem[];
|
files: Array<ChangedFileSummaryItem>;
|
||||||
totals: { additions: number; deletions: number };
|
totals: { additions: number; deletions: number };
|
||||||
projectPath?: string;
|
projectPath?: string;
|
||||||
}) {
|
}) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import type { Message } from "@/lib/agents/types";
|
||||||
export function UserMessage({ message }: { message: Message }) {
|
export function UserMessage({ message }: { message: Message }) {
|
||||||
const text = message.chunks
|
const text = message.chunks
|
||||||
.filter((c) => c.kind === "text")
|
.filter((c) => c.kind === "text")
|
||||||
.map((c) => (c as { kind: "text"; text: string }).text)
|
.map((c) => c.text)
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
const images = message.chunks.filter((c) => c.kind === "image");
|
const images = message.chunks.filter((c) => c.kind === "image");
|
||||||
|
|
@ -39,14 +39,12 @@ export function UserMessage({ message }: { message: Message }) {
|
||||||
{images.length > 0 && (
|
{images.length > 0 && (
|
||||||
<div className="flex gap-2 mb-2 flex-wrap justify-end">
|
<div className="flex gap-2 mb-2 flex-wrap justify-end">
|
||||||
{images.map((img, i) => (
|
{images.map((img, i) => (
|
||||||
img.kind === "image" && (
|
<img
|
||||||
<img
|
key={i}
|
||||||
key={i}
|
src={`data:${img.mimeType};base64,${img.base64}`}
|
||||||
src={`data:${img.mimeType};base64,${img.base64}`}
|
alt={img.fileName || "image"}
|
||||||
alt={img.fileName || "image"}
|
className="max-w-48 max-h-48 rounded border border-gray-600"
|
||||||
className="max-w-48 max-h-48 rounded border border-gray-600"
|
/>
|
||||||
/>
|
|
||||||
)
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useState, type ReactNode } from "react";
|
import { useState } from "react";
|
||||||
import { ChevronRight } from "lucide-react";
|
import { ChevronRight } from "lucide-react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import { formatElapsed } from "@/lib/utils";
|
import { formatElapsed } from "@/lib/utils";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ import type { Chunk, ToolExecutionChunk } from "@/lib/agents/types";
|
||||||
export type RenderItem =
|
export type RenderItem =
|
||||||
| { type: "text-chunk"; key: string; chunk: Chunk }
|
| { type: "text-chunk"; key: string; chunk: Chunk }
|
||||||
| { type: "reasoning-item"; key: string; chunk: Chunk }
|
| { type: "reasoning-item"; key: string; chunk: Chunk }
|
||||||
| { type: "explored-group"; key: string; id: string; chunks: ToolExecutionChunk[] }
|
| { type: "explored-group"; key: string; id: string; chunks: Array<ToolExecutionChunk> }
|
||||||
/**
|
/**
|
||||||
* One or more `task` (subagent) tool calls collapsed into a single group so
|
* One or more `task` (subagent) tool calls collapsed into a single group so
|
||||||
* they can be rendered side by side as a card grid (see subagents/SubagentGroup).
|
* they can be rendered side by side as a card grid (see subagents/SubagentGroup).
|
||||||
*/
|
*/
|
||||||
| { type: "subagent-group"; key: string; id: string; chunks: ToolExecutionChunk[] }
|
| { type: "subagent-group"; key: string; id: string; chunks: Array<ToolExecutionChunk> }
|
||||||
| { type: "edit-item"; key: string; chunk: ToolExecutionChunk }
|
| { type: "edit-item"; key: string; chunk: ToolExecutionChunk }
|
||||||
| { type: "shell-item"; key: string; chunk: ToolExecutionChunk }
|
| { type: "shell-item"; key: string; chunk: ToolExecutionChunk }
|
||||||
| { type: "reply-item"; key: string; chunk: ToolExecutionChunk }
|
| { type: "reply-item"; key: string; chunk: ToolExecutionChunk }
|
||||||
|
|
@ -68,11 +68,11 @@ function isSubagentTool(chunk: ToolExecutionChunk): boolean {
|
||||||
return chunk.toolKind === "task";
|
return chunk.toolKind === "task";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildRenderItems(chunks: Chunk[], messageId?: string): RenderItem[] {
|
export function buildRenderItems(chunks: Array<Chunk>, messageId?: string): Array<RenderItem> {
|
||||||
const items: RenderItem[] = [];
|
const items: Array<RenderItem> = [];
|
||||||
let exploredBuffer: ToolExecutionChunk[] = [];
|
let exploredBuffer: Array<ToolExecutionChunk> = [];
|
||||||
let exploredStartIndex = -1;
|
let exploredStartIndex = -1;
|
||||||
let subagentBuffer: ToolExecutionChunk[] = [];
|
let subagentBuffer: Array<ToolExecutionChunk> = [];
|
||||||
let subagentStartIndex = -1;
|
let subagentStartIndex = -1;
|
||||||
|
|
||||||
const flushExplored = () => {
|
const flushExplored = () => {
|
||||||
|
|
@ -165,7 +165,7 @@ export function buildRenderItems(chunks: Chunk[], messageId?: string): RenderIte
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function summarizeExploration(chunks: ToolExecutionChunk[]): string {
|
export function summarizeExploration(chunks: Array<ToolExecutionChunk>): string {
|
||||||
const count = chunks.length;
|
const count = chunks.length;
|
||||||
return `Explored ${count} file${count === 1 ? "" : "s"}`;
|
return `Explored ${count} file${count === 1 ? "" : "s"}`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { countLineChanges } from "@/components/agents/utils/diffStats";
|
import type { Chunk } from "@/lib/agents/types";
|
||||||
import type { Chunk, DiffData } from "@/lib/agents/types";
|
|
||||||
|
|
||||||
import type { ChangedFileSummaryItem } from "./types";
|
import type { ChangedFileSummaryItem } from "./types";
|
||||||
|
import { countLineChanges } from "@/components/agents/utils/diffStats";
|
||||||
|
|
||||||
export function summarizeChangedFiles(chunks: Chunk[]): ChangedFileSummaryItem[] {
|
export function summarizeChangedFiles(chunks: Array<Chunk>): Array<ChangedFileSummaryItem> {
|
||||||
const byFile = new Map<string, { filePath: string; originalContent: string | null; modifiedContent: string }>();
|
const byFile = new Map<string, { filePath: string; originalContent: string | null; modifiedContent: string }>();
|
||||||
|
|
||||||
for (const chunk of chunks) {
|
for (const chunk of chunks) {
|
||||||
|
|
@ -12,7 +12,7 @@ export function summarizeChangedFiles(chunks: Chunk[]): ChangedFileSummaryItem[]
|
||||||
const diffEntries = chunk.diffs?.length ? chunk.diffs : (chunk.diffData ? [chunk.diffData] : []);
|
const diffEntries = chunk.diffs?.length ? chunk.diffs : (chunk.diffData ? [chunk.diffData] : []);
|
||||||
if (diffEntries.length === 0) continue;
|
if (diffEntries.length === 0) continue;
|
||||||
|
|
||||||
for (const diffData of diffEntries as DiffData[]) {
|
for (const diffData of diffEntries) {
|
||||||
const existing = byFile.get(diffData.filePath);
|
const existing = byFile.get(diffData.filePath);
|
||||||
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export type MessagesScrollControl = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface MessagesProps extends ApprovalCallbacks {
|
export interface MessagesProps extends ApprovalCallbacks {
|
||||||
messages: Message[];
|
messages: Array<Message>;
|
||||||
isStreaming: boolean;
|
isStreaming: boolean;
|
||||||
/** Live run signal from `useStream().isLoading` — drives Streamdown token animation. */
|
/** Live run signal from `useStream().isLoading` — drives Streamdown token animation. */
|
||||||
streamIsLoading?: boolean;
|
streamIsLoading?: boolean;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { getSingletonHighlighter, type ThemedToken } from "shiki";
|
import { getSingletonHighlighter } from "shiki";
|
||||||
|
import type { ThemedToken } from "shiki";
|
||||||
import { useResolvedTheme } from "@/lib/theme";
|
import { useResolvedTheme } from "@/lib/theme";
|
||||||
|
|
||||||
interface CodeBlockProps {
|
interface CodeBlockProps {
|
||||||
|
|
@ -9,7 +10,7 @@ interface CodeBlockProps {
|
||||||
|
|
||||||
const SHIKI_THEME = { light: "github-light", dark: "github-dark" } as const;
|
const SHIKI_THEME = { light: "github-light", dark: "github-dark" } as const;
|
||||||
|
|
||||||
const TOKEN_CACHE = new Map<string, ThemedToken[][]>();
|
const TOKEN_CACHE = new Map<string, Array<Array<ThemedToken>>>();
|
||||||
|
|
||||||
function normalizeLanguage(language?: string): string {
|
function normalizeLanguage(language?: string): string {
|
||||||
const raw = (language || "").toLowerCase().trim();
|
const raw = (language || "").toLowerCase().trim();
|
||||||
|
|
@ -45,7 +46,7 @@ function languageLabel(language: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CodeBlock({ text, language }: CodeBlockProps) {
|
export function CodeBlock({ text, language }: CodeBlockProps) {
|
||||||
const [tokens, setTokens] = useState<ThemedToken[][] | null>(null);
|
const [tokens, setTokens] = useState<Array<Array<ThemedToken>> | null>(null);
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
const resolvedTheme = useResolvedTheme();
|
const resolvedTheme = useResolvedTheme();
|
||||||
const shikiTheme = SHIKI_THEME[resolvedTheme];
|
const shikiTheme = SHIKI_THEME[resolvedTheme];
|
||||||
|
|
@ -67,13 +68,11 @@ export function CodeBlock({ text, language }: CodeBlockProps) {
|
||||||
|
|
||||||
getSingletonHighlighter({
|
getSingletonHighlighter({
|
||||||
themes: [shikiTheme],
|
themes: [shikiTheme],
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
langs: [normalizedLanguage as any],
|
langs: [normalizedLanguage as any],
|
||||||
})
|
})
|
||||||
.then((highlighter) => {
|
.then((highlighter) => {
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
const result = highlighter.codeToTokens(text, {
|
const result = highlighter.codeToTokens(text, {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
lang: normalizedLanguage as any,
|
lang: normalizedLanguage as any,
|
||||||
theme: shikiTheme,
|
theme: shikiTheme,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { LoaderCircle } from 'lucide-react';
|
import { LoaderCircle } from 'lucide-react';
|
||||||
import { useState, useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
export function CompactingIndicator() {
|
export function CompactingIndicator() {
|
||||||
const [dots, setDots] = useState(1);
|
const [dots, setDots] = useState(1);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { MultiFileDiff } from "@pierre/diffs/react";
|
import { MultiFileDiff } from "@pierre/diffs/react";
|
||||||
|
import type { DiffData } from "@/lib/agents/types";
|
||||||
import { diffOptions } from "@/components/agents/utils/diffUtils";
|
import { diffOptions } from "@/components/agents/utils/diffUtils";
|
||||||
import { countLineChanges } from "@/components/agents/utils/diffStats";
|
import { countLineChanges } from "@/components/agents/utils/diffStats";
|
||||||
import type { DiffData } from "@/lib/agents/types";
|
|
||||||
|
|
||||||
interface DiffViewProps {
|
interface DiffViewProps {
|
||||||
diffData: DiffData;
|
diffData: DiffData;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { memo, type ReactNode } from "react";
|
import { memo } from "react";
|
||||||
import { Streamdown } from "streamdown";
|
import { Streamdown } from "streamdown";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
import "streamdown/styles.css";
|
import "streamdown/styles.css";
|
||||||
|
|
||||||
interface MarkdownProps {
|
interface MarkdownProps {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
// @ts-nocheck — ported from open-swe-app (Electron).
|
import { Children, isValidElement } from 'react';
|
||||||
import { Children, isValidElement, type ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
type ElementWithChildren = { children?: ReactNode };
|
||||||
|
|
||||||
interface MarkdownTableProps {
|
interface MarkdownTableProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
|
@ -9,17 +11,19 @@ function countCells(node: ReactNode): number {
|
||||||
return Children.toArray(node).filter((child) => isValidElement(child) && (child.type === 'th' || child.type === 'td')).length;
|
return Children.toArray(node).filter((child) => isValidElement(child) && (child.type === 'th' || child.type === 'td')).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectColumnCounts(node: ReactNode, counts: number[]) {
|
function collectColumnCounts(node: ReactNode, counts: Array<number>) {
|
||||||
Children.forEach(node, (child) => {
|
Children.forEach(node, (child) => {
|
||||||
if (!isValidElement(child)) return;
|
if (!isValidElement(child)) return;
|
||||||
|
|
||||||
|
const childProps = child.props as ElementWithChildren;
|
||||||
|
|
||||||
if (child.type === 'tr') {
|
if (child.type === 'tr') {
|
||||||
const count = countCells(child.props.children);
|
const count = countCells(childProps.children);
|
||||||
if (count > 0) counts.push(count);
|
if (count > 0) counts.push(count);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
collectColumnCounts(child.props.children, counts);
|
collectColumnCounts(childProps.children, counts);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +33,7 @@ function getTableMinWidth(columnCount: number): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MarkdownTable({ children }: MarkdownTableProps) {
|
export function MarkdownTable({ children }: MarkdownTableProps) {
|
||||||
const columnCounts: number[] = [];
|
const columnCounts: Array<number> = [];
|
||||||
collectColumnCounts(children, columnCounts);
|
collectColumnCounts(children, columnCounts);
|
||||||
const columnCount = Math.max(0, ...columnCounts);
|
const columnCount = Math.max(0, ...columnCounts);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ function isSlackTextObject(value: unknown): value is SlackTextObject {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSlackBlockArray(value: unknown): value is SlackBlock[] {
|
function isSlackBlockArray(value: unknown): value is Array<SlackBlock> {
|
||||||
return (
|
return (
|
||||||
Array.isArray(value) &&
|
Array.isArray(value) &&
|
||||||
value.every((block) => !!block && typeof block === "object")
|
value.every((block) => !!block && typeof block === "object")
|
||||||
|
|
@ -96,7 +96,7 @@ function renderSlackBody(text: string): Array<ReactNode> {
|
||||||
function blocksFromOptions(
|
function blocksFromOptions(
|
||||||
message: string,
|
message: string,
|
||||||
options: unknown
|
options: unknown
|
||||||
): SlackBlock[] | null {
|
): Array<SlackBlock> | null {
|
||||||
if (!Array.isArray(options)) return null
|
if (!Array.isArray(options)) return null
|
||||||
const cleanOptions = options.filter(
|
const cleanOptions = options.filter(
|
||||||
(option): option is string =>
|
(option): option is string =>
|
||||||
|
|
@ -115,7 +115,7 @@ function blocksFromOptions(
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSlackBlocks(blocks: SlackBlock[]): ReactNode {
|
function renderSlackBlocks(blocks: Array<SlackBlock>): ReactNode {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{blocks.map((block, index) => {
|
{blocks.map((block, index) => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { memo, useState, useRef, useCallback, useLayoutEffect } from "react";
|
import { memo, useCallback, useLayoutEffect, useRef, useState } from "react";
|
||||||
import type { ToolExecutionChunk } from "@/lib/agents/types";
|
import type { ToolExecutionChunk } from "@/lib/agents/types";
|
||||||
|
|
||||||
interface ShellCommandProps {
|
interface ShellCommandProps {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useRef, useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
import type { TodoItem } from "@/lib/agents/types";
|
import type { TodoItem } from "@/lib/agents/types";
|
||||||
|
|
||||||
interface TodoListProps {
|
interface TodoListProps {
|
||||||
todos: TodoItem[];
|
todos: Array<TodoItem>;
|
||||||
className?: string;
|
className?: string;
|
||||||
runActive?: boolean;
|
runActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { memo, useState, useCallback, useMemo, useRef, useLayoutEffect } from "react";
|
import { memo, useCallback, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||||
import { MultiFileDiff } from "@pierre/diffs/react";
|
import { MultiFileDiff } from "@pierre/diffs/react";
|
||||||
import type { ToolExecutionChunk, AcpToolKind } from "@/lib/agents/types";
|
|
||||||
import { DiffView } from "./DiffView";
|
import { DiffView } from "./DiffView";
|
||||||
|
import type { AcpToolKind, ToolExecutionChunk } from "@/lib/agents/types";
|
||||||
import { useDiffOptions } from "@/components/agents/utils/diffUtils";
|
import { useDiffOptions } from "@/components/agents/utils/diffUtils";
|
||||||
import { countLineChanges } from "@/components/agents/utils/diffStats";
|
import { countLineChanges } from "@/components/agents/utils/diffStats";
|
||||||
|
|
||||||
|
|
@ -204,7 +204,7 @@ export const ToolExecution = memo(function ToolExecution({
|
||||||
const editedFileName = editedFilePath ? getFileName(editedFilePath) : "";
|
const editedFileName = editedFilePath ? getFileName(editedFilePath) : "";
|
||||||
const diffStats = diffData ? countLineChanges(diffData.originalContent, diffData.newContent, diffData.filePath) : null;
|
const diffStats = diffData ? countLineChanges(diffData.originalContent, diffData.newContent, diffData.filePath) : null;
|
||||||
|
|
||||||
if (isCompletedEditOp && diffStats && diffData) {
|
if (isCompletedEditOp && diffStats) {
|
||||||
return (
|
return (
|
||||||
<InlineDiffCollapsible
|
<InlineDiffCollapsible
|
||||||
filePath={editedFilePath || diffData.filePath}
|
filePath={editedFilePath || diffData.filePath}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import { Bot, Loader2 } from "lucide-react";
|
import { Bot, Loader2 } from "lucide-react";
|
||||||
|
|
||||||
import { useIsInAgentThreadStream } from "@/lib/agents/provider/useIsInAgentThreadStream";
|
|
||||||
import type { ToolExecutionChunk } from "@/lib/agents/types";
|
|
||||||
|
|
||||||
import { SubagentActivity } from "./SubagentActivity";
|
import { SubagentActivity } from "./SubagentActivity";
|
||||||
|
import type { ToolExecutionChunk } from "@/lib/agents/types";
|
||||||
|
import { useIsInAgentThreadStream } from "@/lib/agents/provider/useIsInAgentThreadStream";
|
||||||
|
|
||||||
/** Coerce an unknown tool-argument value to a trimmed string, or `""`. */
|
/** Coerce an unknown tool-argument value to a trimmed string, or `""`. */
|
||||||
function asString(value: unknown): string {
|
function asString(value: unknown): string {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import type { ToolExecutionChunk } from "@/lib/agents/types";
|
|
||||||
|
|
||||||
import { SubagentCard } from "./SubagentCard";
|
import { SubagentCard } from "./SubagentCard";
|
||||||
|
import type { ToolExecutionChunk } from "@/lib/agents/types";
|
||||||
|
|
||||||
/** Maximum number of subagent cards rendered per row. */
|
/** Maximum number of subagent cards rendered per row. */
|
||||||
const MAX_SUBAGENT_COLUMNS = 4;
|
const MAX_SUBAGENT_COLUMNS = 4;
|
||||||
|
|
@ -11,7 +10,7 @@ const MAX_SUBAGENT_COLUMNS = 4;
|
||||||
* {@link MAX_SUBAGENT_COLUMNS}, so 1–4 subagents fill the row evenly and 5+
|
* {@link MAX_SUBAGENT_COLUMNS}, so 1–4 subagents fill the row evenly and 5+
|
||||||
* wrap onto additional rows.
|
* wrap onto additional rows.
|
||||||
*/
|
*/
|
||||||
export function SubagentGroup({ chunks }: { chunks: ToolExecutionChunk[] }) {
|
export function SubagentGroup({ chunks }: { chunks: Array<ToolExecutionChunk> }) {
|
||||||
const columns = Math.min(Math.max(chunks.length, 1), MAX_SUBAGENT_COLUMNS);
|
const columns = Math.min(Math.max(chunks.length, 1), MAX_SUBAGENT_COLUMNS);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ const LIVE_MARKDOWN_CLEAR_MS = 2000;
|
||||||
* keeps markdown streaming until the run has been idle for a beat.
|
* keeps markdown streaming until the run has been idle for a beat.
|
||||||
*/
|
*/
|
||||||
export function useLiveMarkdownMessageId(
|
export function useLiveMarkdownMessageId(
|
||||||
visibleMessages: Message[],
|
visibleMessages: Array<Message>,
|
||||||
streamIsLoading: boolean | undefined,
|
streamIsLoading: boolean | undefined,
|
||||||
isStreaming: boolean,
|
isStreaming: boolean,
|
||||||
): string | null {
|
): string | null {
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ function isFormField(target: EventTarget | null): boolean {
|
||||||
* (Cmd on macOS, Ctrl elsewhere). Accepts one combo or several aliases.
|
* (Cmd on macOS, Ctrl elsewhere). Accepts one combo or several aliases.
|
||||||
*/
|
*/
|
||||||
export function useHotkey(
|
export function useHotkey(
|
||||||
combo: string | string[],
|
combo: string | Array<string>,
|
||||||
handler: (event: KeyboardEvent) => void,
|
handler: (event: KeyboardEvent) => void,
|
||||||
options: HotkeyOptions = {}
|
options: HotkeyOptions = {}
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
|
|
||||||
import type { AgentSource, AgentStatus } from "@/lib/agents/types"
|
import type { AgentSource, AgentStatus } from "@/lib/agents/types"
|
||||||
import {
|
import type { ThreadsPageFilters } from "@/components/agents/AgentsThreadsPage";
|
||||||
AgentsThreadsPage,
|
import { AgentsThreadsPage } from "@/components/agents/AgentsThreadsPage"
|
||||||
type ThreadsPageFilters,
|
|
||||||
} from "@/components/agents/AgentsThreadsPage"
|
|
||||||
|
|
||||||
const SOURCES: ReadonlyArray<AgentSource> = [
|
const SOURCES: ReadonlyArray<AgentSource> = [
|
||||||
"dashboard",
|
"dashboard",
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,7 @@ function CloudAgentsPage() {
|
||||||
label="Default Repository"
|
label="Default Repository"
|
||||||
description="Used when no repository is specified"
|
description="Used when no repository is specified"
|
||||||
control={
|
control={
|
||||||
repos.data?.repositories?.length ? (
|
repos.data?.repositories.length ? (
|
||||||
<div className="w-56">
|
<div className="w-56">
|
||||||
<RepoSelector
|
<RepoSelector
|
||||||
repos={repos.data.repositories}
|
repos={repos.data.repositories}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue