Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
50 changes: 50 additions & 0 deletions frontend/.github/workflows/react-doctor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# React Doctor — finds security, performance, correctness, accessibility,
# bundle-size, and architecture issues in React codebases.
#
# Docs: https://www.react.doctor/ci
# Source: https://github.com/millionco/react-doctor

name: React Doctor

on:
# Scans the PR's changed files and posts a sticky summary comment listing only the new issues introduced relative to the merge base of the target branch.
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
# Scans `main` on every push to track the health-score trend and catch regressions that slipped past PR review.
push:
branches: ["main"]

permissions:
contents: read
pull-requests: write
issues: write
statuses: write

# Cancels any in-flight scan for the same PR (or branch, on push) the moment a new commit arrives, so reviewers only ever see the latest run.
concurrency:
group: react-doctor-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
react-doctor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- uses: millionco/react-doctor@v2
# Advisory by default: React Doctor reports findings on every PR — a
# sticky summary comment, inline review comments, and a commit status
# with the health score — but never fails the check, so it won't red-X
# a teammate's PR on day one. When your team trusts the signal, graduate
# the gate: uncomment the block below and set blocking to "error" (fail
# on new error-severity findings) or "warning" (fail on any finding).
# Full reference: https://www.react.doctor/ci
# with:
# blocking: error # Gate level: "none" (advisory, the default) | "warning" | "error"
# scope: full # On PRs, scan the whole project instead of just changed files
# comment: false # Disable the sticky PR summary comment
# review-comments: false # Disable inline review comments on changed lines
# commit-status: false # Disable the commit status (score + counts, links to the run)
# version: "0.4.0" # Pin to a specific react-doctor version instead of "latest"
# directory: apps/web # Scan a sub-directory (default: ".")
# project: "web,admin" # In a monorepo, scan specific workspace project(s)
6 changes: 4 additions & 2 deletions frontend/app/chat/_components/assistant-message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ import type {
FunctionCall,
TokenUsage as TokenUsageType,
} from "../_types/types";
import { FunctionCallsContainer } from "./function-calls";
import { FunctionCallsContainer } from "./function-calls/container";
import { Message } from "./message";
import MessageActions from "./message-actions";
import { TokenUsage } from "./token-usage";

const EMPTY_FUNCTION_CALLS: FunctionCall[] = [];

interface AssistantMessageProps {
content: string;
functionCalls?: FunctionCall[];
Expand All @@ -43,7 +45,7 @@ interface AssistantMessageProps {

export function AssistantMessage({
content,
functionCalls = [],
functionCalls = EMPTY_FUNCTION_CALLS,
messageIndex,
expandedFunctionCalls,
onToggle,
Expand Down
30 changes: 17 additions & 13 deletions frontend/app/knowledge/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -230,19 +230,23 @@ function SearchPage() {
]),
);

const mostRecent = [...candidates].sort((a, b) => {
const aMs =
taskTimestampMsById.get(a.task_id) ??
parseTimestampMs(a.updated_at) ??
parseTimestampMs(a.created_at) ??
0;
const bMs =
taskTimestampMsById.get(b.task_id) ??
parseTimestampMs(b.updated_at) ??
parseTimestampMs(b.created_at) ??
0;
return bMs - aMs;
})[0];
const mostRecent = candidates.reduce(
(best, cur) => {
const curMs =
taskTimestampMsById.get(cur.task_id) ??
parseTimestampMs(cur.updated_at) ??
parseTimestampMs(cur.created_at) ??
0;
if (!best) return cur;
const bestMs =
taskTimestampMsById.get(best.task_id) ??
parseTimestampMs(best.updated_at) ??
parseTimestampMs(best.created_at) ??
0;
return curMs > bestMs ? cur : best;
},
undefined as (typeof candidates)[0] | undefined,
);

return mostRecent?.task_id || null;
},
Expand Down
4 changes: 3 additions & 1 deletion frontend/components/cloud-picker/unified-cloud-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import {
UnifiedCloudPickerProps,
} from "./types";

const EMPTY_FILES: CloudFile[] = [];

export const UnifiedCloudPicker = ({
provider,
onFileSelected,
selectedFiles = [],
selectedFiles = EMPTY_FILES,
isAuthenticated,
isIngesting,
accessToken,
Expand Down
5 changes: 2 additions & 3 deletions frontend/components/dev-role-toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ function parseApiError(
if (typeof detail === "string") return detail;
if (Array.isArray(detail)) {
const msg = detail
.map((d) =>
typeof d === "object" && d && "msg" in d ? String(d.msg) : "",
.flatMap((d) =>
typeof d === "object" && d && "msg" in d ? [String(d.msg)] : [],
)
.filter(Boolean)
.join("; ");
if (msg) return msg;
}
Expand Down
2 changes: 0 additions & 2 deletions frontend/components/knowledge-dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -852,8 +852,6 @@ export function KnowledgeDropdown() {
type="file"
// @ts-ignore - webkitdirectory is not in TypeScript types but is widely supported
webkitdirectory=""
// @ts-ignore
directory=""
multiple
onChange={handleFolderSelect}
className="hidden"
Expand Down
13 changes: 3 additions & 10 deletions frontend/components/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -519,23 +519,16 @@ export function Navigation({
}
asChild
>
<div
<button
type="button"
className="opacity-0 group-hover:opacity-100 data-[state=open]:opacity-100 data-[state=open]:text-foreground transition-opacity p-1 hover:bg-accent rounded text-muted-foreground hover:text-foreground ml-2 flex-shrink-0 cursor-pointer"
title="More options"
role="button"
tabIndex={0}
onClick={(e) => {
e.stopPropagation();
}}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
e.stopPropagation();
}
}}
>
<EllipsisVertical className="h-4 w-4" />
</div>
</button>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
</DropdownMenuTrigger>
<DropdownMenuContent
side="bottom"
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/ui/command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
<div className="flex items-center border-b px-3" data-cmdk-input-wrapper="">
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<CommandPrimitive.Input
ref={ref}
Expand Down
2 changes: 1 addition & 1 deletion frontend/lib/task-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export function sortTaskFileEntries(
entries: Array<[string, TaskFileEntry]>,
direction: TaskFileNameSort = "asc",
): Array<[string, TaskFileEntry]> {
const sorted = [...entries].sort(([pathA, infoA], [pathB, infoB]) =>
const sorted = entries.toSorted(([pathA, infoA], [pathB, infoB]) =>
getTaskFileName(pathA, infoA).localeCompare(
getTaskFileName(pathB, infoB),
undefined,
Expand Down
Loading