chore: bump version to 0.1.5 and improve accessibility in components
This commit is contained in:
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@evoscientist/webui",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@evoscientist/webui",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@langchain/core": "1.1.19",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@evoscientist/webui",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"description": "Web UI for EvoScientist — a self-evolving AI scientist built on DeepAgents/LangGraph.",
|
||||
"author": "Xi Zhang <zacharyzhang2022@gmail.com>",
|
||||
"type": "module",
|
||||
|
||||
@@ -62,7 +62,12 @@ export const MarkdownContent = React.memo<MarkdownContentProps>(
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
const match = /language-(\w+)/.exec(className || "");
|
||||
const code = String(children).replace(/\n$/, "");
|
||||
const raw = String(children);
|
||||
// Fenced blocks always end with \n; inline code does not. Use
|
||||
// this to distinguish unlanguaged fenced blocks from inline code
|
||||
// so detectFileLink is only called for true inline code.
|
||||
const isBlock = raw.endsWith("\n");
|
||||
const code = raw.replace(/\n$/, "");
|
||||
// react-markdown v9 dropped the `inline` prop. Block code carries
|
||||
// a `language-*` className from the fenced ```lang form; inline
|
||||
// code does not. Treat absence of a language match as inline.
|
||||
@@ -85,7 +90,7 @@ export const MarkdownContent = React.memo<MarkdownContentProps>(
|
||||
// Inline code that names a workspace or memory file becomes a
|
||||
// click-to-open link. ChatInterface (or any ancestor) listens
|
||||
// for `evosci:open-file` and routes to the right dialog.
|
||||
const link = detectFileLink(code);
|
||||
const link = !isBlock ? detectFileLink(code) : null;
|
||||
if (link) {
|
||||
return (
|
||||
<button
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { flushSync } from "react-dom";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
@@ -16,6 +16,30 @@ interface ToolApprovalInterruptProps {
|
||||
onSubmitted?: () => void;
|
||||
}
|
||||
|
||||
function argsToRecord(args: unknown): Record<string, unknown> {
|
||||
return args && typeof args === "object"
|
||||
? (args as Record<string, unknown>)
|
||||
: {};
|
||||
}
|
||||
|
||||
function cloneArgs(args: Record<string, unknown>): Record<string, unknown> {
|
||||
try {
|
||||
return JSON.parse(JSON.stringify(args)) as Record<string, unknown>;
|
||||
} catch {
|
||||
return { ...args };
|
||||
}
|
||||
}
|
||||
|
||||
function formatValue(value: unknown): string {
|
||||
if (typeof value === "string") return value;
|
||||
if (value === undefined) return "undefined";
|
||||
try {
|
||||
return JSON.stringify(value, null, 2) ?? String(value);
|
||||
} catch {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
|
||||
export function ToolApprovalInterrupt({
|
||||
actionRequest,
|
||||
reviewConfig,
|
||||
@@ -29,6 +53,10 @@ export function ToolApprovalInterrupt({
|
||||
const [showRejectionInput, setShowRejectionInput] = useState(false);
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
|
||||
const actionArgs = useMemo(
|
||||
() => argsToRecord(actionRequest.args),
|
||||
[actionRequest.args]
|
||||
);
|
||||
const allowedDecisions = reviewConfig?.allowedDecisions ??
|
||||
reviewConfig?.allowed_decisions ?? ["approve", "reject", "edit"];
|
||||
|
||||
@@ -94,7 +122,7 @@ export function ToolApprovalInterrupt({
|
||||
|
||||
const startEditing = () => {
|
||||
setIsEditing(true);
|
||||
setEditedArgs(JSON.parse(JSON.stringify(actionRequest.args)));
|
||||
setEditedArgs(cloneArgs(actionArgs));
|
||||
setShowRejectionInput(false);
|
||||
};
|
||||
|
||||
@@ -126,6 +154,7 @@ export function ToolApprovalInterrupt({
|
||||
<AlertCircle
|
||||
size={16}
|
||||
className="text-yellow-600 dark:text-yellow-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="text-xs font-semibold uppercase tracking-wider">
|
||||
Approval Required
|
||||
@@ -156,7 +185,7 @@ export function ToolApprovalInterrupt({
|
||||
Edit Arguments
|
||||
</span>
|
||||
<div className="mt-2 space-y-3">
|
||||
{Object.entries(actionRequest.args).map(([key, value]) => (
|
||||
{Object.entries(actionArgs).map(([key, value]) => (
|
||||
<div key={key}>
|
||||
<label className="mb-1 block text-xs font-medium text-foreground">
|
||||
{key}
|
||||
@@ -166,10 +195,10 @@ export function ToolApprovalInterrupt({
|
||||
editedArgs[key] !== undefined
|
||||
? typeof editedArgs[key] === "string"
|
||||
? (editedArgs[key] as string)
|
||||
: JSON.stringify(editedArgs[key], null, 2)
|
||||
: formatValue(editedArgs[key])
|
||||
: typeof value === "string"
|
||||
? value
|
||||
: JSON.stringify(value, null, 2)
|
||||
: formatValue(value)
|
||||
}
|
||||
onChange={(e) => updateEditedArg(key, e.target.value)}
|
||||
className="font-mono text-xs"
|
||||
@@ -188,7 +217,7 @@ export function ToolApprovalInterrupt({
|
||||
Arguments
|
||||
</span>
|
||||
<pre className="mt-2 overflow-x-auto whitespace-pre-wrap break-all rounded-sm border border-border bg-muted/40 p-2 font-mono text-xs text-foreground">
|
||||
{JSON.stringify(actionRequest.args, null, 2)}
|
||||
{formatValue(actionArgs)}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
@@ -231,7 +260,10 @@ export function ToolApprovalInterrupt({
|
||||
disabled={isLoading}
|
||||
className="bg-green-600 text-white hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-700"
|
||||
>
|
||||
<Check size={14} />
|
||||
<Check
|
||||
size={14}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{isLoading ? "Saving…" : "Save & Approve"}
|
||||
</Button>
|
||||
</>
|
||||
@@ -270,7 +302,10 @@ export function ToolApprovalInterrupt({
|
||||
disabled={isLoading}
|
||||
className="text-destructive hover:bg-destructive/10"
|
||||
>
|
||||
<X size={14} />
|
||||
<X
|
||||
size={14}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Reject
|
||||
</Button>
|
||||
)}
|
||||
@@ -282,7 +317,10 @@ export function ToolApprovalInterrupt({
|
||||
onClick={startEditing}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<Pencil size={14} />
|
||||
<Pencil
|
||||
size={14}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Edit
|
||||
</Button>
|
||||
)}
|
||||
@@ -297,7 +335,10 @@ export function ToolApprovalInterrupt({
|
||||
"dark:bg-green-600 dark:hover:bg-green-700"
|
||||
)}
|
||||
>
|
||||
<Check size={14} />
|
||||
<Check
|
||||
size={14}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{isLoading ? "Approving…" : "Approve"}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -264,7 +264,10 @@ export const WorkspaceFileDialog = React.memo<{
|
||||
<DialogTitle className="sr-only">{path}</DialogTitle>
|
||||
<div className="mb-4 flex items-center justify-between gap-3 border-b border-border pb-4">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<FileText className="h-5 w-5 shrink-0 text-[var(--color-text-tertiary)]" />
|
||||
<FileText
|
||||
className="h-5 w-5 shrink-0 text-[var(--color-text-tertiary)]"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="overflow-hidden text-ellipsis whitespace-nowrap text-base font-medium text-primary">
|
||||
{path}
|
||||
</span>
|
||||
@@ -284,6 +287,7 @@ export const WorkspaceFileDialog = React.memo<{
|
||||
<Eye
|
||||
size={16}
|
||||
className="mr-1"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Cancel
|
||||
</Button>
|
||||
@@ -297,11 +301,13 @@ export const WorkspaceFileDialog = React.memo<{
|
||||
<Loader2
|
||||
size={16}
|
||||
className="mr-1 animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : (
|
||||
<Save
|
||||
size={16}
|
||||
className="mr-1"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
{saving ? "Saving…" : "Save"}
|
||||
@@ -321,29 +327,29 @@ export const WorkspaceFileDialog = React.memo<{
|
||||
<Pencil
|
||||
size={16}
|
||||
className="mr-1"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Edit
|
||||
</Button>
|
||||
)}
|
||||
<a
|
||||
href={workspaceFileUrl(path, true)}
|
||||
download={name}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 px-2"
|
||||
asChild
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 px-2"
|
||||
asChild
|
||||
<a
|
||||
href={workspaceFileUrl(path, true)}
|
||||
download={name}
|
||||
>
|
||||
<span>
|
||||
<Download
|
||||
size={16}
|
||||
className="mr-1"
|
||||
/>
|
||||
Download
|
||||
</span>
|
||||
</Button>
|
||||
</a>
|
||||
<Download
|
||||
size={16}
|
||||
className="mr-1"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Download
|
||||
</a>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@@ -357,9 +363,13 @@ export const WorkspaceFileDialog = React.memo<{
|
||||
<Loader2
|
||||
size={16}
|
||||
className="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : (
|
||||
<Trash2 size={16} />
|
||||
<Trash2
|
||||
size={16}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
</>
|
||||
@@ -409,25 +419,30 @@ export const WorkspaceFileDialog = React.memo<{
|
||||
? "This file is too large to preview inline."
|
||||
: "This file type can't be previewed."}
|
||||
</p>
|
||||
<a
|
||||
href={workspaceFileUrl(path, true)}
|
||||
download={name}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
asChild
|
||||
>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
<a
|
||||
href={workspaceFileUrl(path, true)}
|
||||
download={name}
|
||||
>
|
||||
<Download
|
||||
size={16}
|
||||
className="mr-1"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Download file
|
||||
</Button>
|
||||
</a>
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
) : loading ? (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<Loader2 className="size-5 animate-spin text-muted-foreground" />
|
||||
<Loader2
|
||||
className="size-5 animate-spin text-muted-foreground"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="flex h-full items-center justify-center p-12">
|
||||
|
||||
Reference in New Issue
Block a user