mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-01 01:51:44 +00:00
fix: replace all buttons for design system buttons
This commit is contained in:
parent
529eb29b6a
commit
e116957a63
15 changed files with 117 additions and 193 deletions
|
|
@ -160,7 +160,10 @@ function resolveIcon(name: string): ComponentType<{ className?: string }> {
|
|||
return ICON_MAP[name] ?? Puzzle;
|
||||
}
|
||||
|
||||
function buildNavItems(builtIn: NavItem[], manifests: PluginManifest[]): NavItem[] {
|
||||
function buildNavItems(
|
||||
builtIn: NavItem[],
|
||||
manifests: PluginManifest[],
|
||||
): NavItem[] {
|
||||
const items = [...builtIn];
|
||||
|
||||
for (const manifest of manifests) {
|
||||
|
|
@ -425,18 +428,22 @@ export default function App() {
|
|||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-14 shrink-0 items-center justify-between gap-2 px-5",
|
||||
"flex h-14 shrink-0 items-center justify-between gap-2",
|
||||
"border-b border-current/20",
|
||||
)}
|
||||
>
|
||||
<Typography
|
||||
className="font-bold text-[1.125rem] leading-[0.95] tracking-[0.0525rem] text-midground"
|
||||
style={{ mixBlendMode: "plus-lighter" }}
|
||||
>
|
||||
Hermes
|
||||
<br />
|
||||
Agent
|
||||
</Typography>
|
||||
<div className="flex items-center gap-2">
|
||||
<PluginSlot name="header-left" />
|
||||
|
||||
<Typography
|
||||
className="font-bold text-[1.125rem] leading-[0.95] tracking-[0.0525rem] text-midground"
|
||||
style={{ mixBlendMode: "plus-lighter" }}
|
||||
>
|
||||
Hermes
|
||||
<br />
|
||||
Agent
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -452,8 +459,6 @@ export default function App() {
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<PluginSlot name="header-left" />
|
||||
|
||||
<nav
|
||||
className="min-h-0 w-full flex-1 overflow-y-auto overflow-x-hidden border-t border-current/10 py-2"
|
||||
aria-label={t.app.navigation}
|
||||
|
|
@ -545,7 +550,8 @@ export default function App() {
|
|||
<div
|
||||
className={cn(
|
||||
"w-full min-w-0",
|
||||
(isDocsRoute || isChatRoute) && "min-h-0 flex flex-1 flex-col",
|
||||
(isDocsRoute || isChatRoute) &&
|
||||
"min-h-0 flex flex-1 flex-col",
|
||||
)}
|
||||
>
|
||||
<Routes>
|
||||
|
|
@ -579,8 +585,9 @@ export default function App() {
|
|||
or idle-disconnect after N minutes hidden; neither is
|
||||
shipped today.
|
||||
*/}
|
||||
{embeddedChat && !chatOverriddenByPlugin && (
|
||||
pluginsLoading ? (
|
||||
{embeddedChat &&
|
||||
!chatOverriddenByPlugin &&
|
||||
(pluginsLoading ? (
|
||||
// Direct /chat deep-link: plugin manifests haven't resolved
|
||||
// yet, so we can't tell if a plugin is going to claim this
|
||||
// route. Show a lightweight placeholder instead of a
|
||||
|
|
@ -593,7 +600,10 @@ export default function App() {
|
|||
aria-live="polite"
|
||||
>
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<Loader2 className="h-4 w-4 animate-spin" aria-hidden />
|
||||
<Loader2
|
||||
className="h-4 w-4 animate-spin"
|
||||
aria-hidden
|
||||
/>
|
||||
<span>Loading chat…</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -609,8 +619,7 @@ export default function App() {
|
|||
>
|
||||
<ChatPage isActive={isChatRoute} />
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
<PluginSlot name="post-main" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@
|
|||
* terminal pane keeps working unimpaired.
|
||||
*/
|
||||
|
||||
import { Button } from "@nous-research/ui";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
|
||||
import { ModelPickerDialog } from "@/components/ModelPickerDialog";
|
||||
|
|
@ -337,12 +337,11 @@ export function ChatSidebar({ channel, className }: ChatSidebarProps) {
|
|||
|
||||
{error && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="mt-1 h-6 px-1.5 text-xs"
|
||||
outlined
|
||||
className="mt-1"
|
||||
onClick={reconnect}
|
||||
prefix={<RefreshCw />}
|
||||
>
|
||||
<RefreshCw className="mr-1 h-3 w-3" />
|
||||
reconnect
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Button } from "@nous-research/ui";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import type { GatewayClient } from "@/lib/gatewayClient";
|
||||
import { Check, Loader2, Search, X } from "lucide-react";
|
||||
|
|
@ -222,10 +222,10 @@ export function ModelPickerDialog({ gw, sessionId, onClose, onSubmit }: Props) {
|
|||
</label>
|
||||
|
||||
<div className="flex items-center gap-2 ml-auto">
|
||||
<Button variant="ghost" size="sm" onClick={onClose}>
|
||||
<Button outlined onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="sm" onClick={confirm} disabled={!canConfirm}>
|
||||
<Button onClick={confirm} disabled={!canConfirm}>
|
||||
Switch
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import { ExternalLink, Copy, X, Check, Loader2 } from "lucide-react";
|
||||
import { H2 } from "@nous-research/ui";
|
||||
import { Button, H2 } from "@nous-research/ui";
|
||||
import { api, type OAuthProvider, type OAuthStartResponse } from "@/lib/api";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useI18n } from "@/i18n";
|
||||
|
||||
|
|
@ -254,7 +253,6 @@ export function OAuthLoginModal({
|
|||
<Button
|
||||
onClick={handleSubmitPkceCode}
|
||||
disabled={!pkceCode.trim()}
|
||||
size="sm"
|
||||
>
|
||||
{t.oauth.submitCode}
|
||||
</Button>
|
||||
|
|
@ -289,8 +287,7 @@ export function OAuthLoginModal({
|
|||
}
|
||||
</code>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
outlined
|
||||
onClick={() =>
|
||||
handleCopyUserCode(
|
||||
(
|
||||
|
|
@ -301,12 +298,12 @@ export function OAuthLoginModal({
|
|||
).user_code,
|
||||
)
|
||||
}
|
||||
className="text-xs"
|
||||
className="!p-2 aspect-square"
|
||||
>
|
||||
{codeCopied ? (
|
||||
<Check className="h-3 w-3" />
|
||||
<Check className="h-3.5 w-3.5" />
|
||||
) : (
|
||||
<Copy className="h-3 w-3" />
|
||||
<Copy className="h-3.5 w-3.5" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -348,11 +345,10 @@ export function OAuthLoginModal({
|
|||
{errorMsg || t.oauth.loginFailed}
|
||||
</div>
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button variant="outline" size="sm" onClick={handleClose}>
|
||||
<Button outlined onClick={handleClose}>
|
||||
{t.common.close}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (start?.session_id) {
|
||||
api.cancelOAuthSession(start.session_id).catch(() => {});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useEffect, useState, useCallback, useRef } from "react";
|
||||
import { ShieldCheck, ShieldOff, Copy, ExternalLink, RefreshCw, LogOut, Terminal, LogIn } from "lucide-react";
|
||||
import { api, type OAuthProvider } from "@/lib/api";
|
||||
import { Button } from "@nous-research/ui";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { OAuthLoginModal } from "@/components/OAuthLoginModal";
|
||||
import { useI18n } from "@/i18n";
|
||||
|
|
@ -94,13 +94,11 @@ export function OAuthProvidersCard({ onError, onSuccess }: Props) {
|
|||
<CardTitle className="text-base">{t.oauth.providerLogins}</CardTitle>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
outlined
|
||||
onClick={refresh}
|
||||
disabled={loading}
|
||||
className="text-xs"
|
||||
prefix={<RefreshCw className={loading ? "animate-spin" : undefined} />}
|
||||
>
|
||||
<RefreshCw className={`h-3 w-3 mr-1 ${loading ? "animate-spin" : ""}`} />
|
||||
{t.common.refresh}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -194,53 +192,42 @@ export function OAuthProvidersCard({ onError, onSuccess }: Props) {
|
|||
className="inline-flex"
|
||||
title={`Open ${p.name} docs`}
|
||||
>
|
||||
<Button variant="ghost" size="sm" className="h-7 w-7 p-0">
|
||||
<Button outlined className="!p-1.5 aspect-square">
|
||||
<ExternalLink className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</a>
|
||||
)}
|
||||
{!p.status.logged_in && p.flow !== "external" && (
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
onClick={() => setLoginFor(p)}
|
||||
className="text-xs h-7"
|
||||
prefix={<LogIn />}
|
||||
>
|
||||
<LogIn className="h-3 w-3 mr-1" />
|
||||
{t.oauth.login}
|
||||
</Button>
|
||||
)}
|
||||
{!p.status.logged_in && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
outlined
|
||||
onClick={() => handleCopy(p)}
|
||||
className="text-xs h-7"
|
||||
title={t.oauth.copyCliCommand}
|
||||
prefix={copiedId === p.id ? undefined : <Copy />}
|
||||
>
|
||||
{copiedId === p.id ? (
|
||||
<>{t.oauth.copied}</>
|
||||
) : (
|
||||
<>
|
||||
<Copy className="h-3 w-3 mr-1" />
|
||||
{t.oauth.cli}
|
||||
</>
|
||||
)}
|
||||
{copiedId === p.id ? t.oauth.copied : t.oauth.cli}
|
||||
</Button>
|
||||
)}
|
||||
{p.status.logged_in && p.flow !== "external" && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
outlined
|
||||
onClick={() => handleDisconnect(p)}
|
||||
disabled={isBusy}
|
||||
className="text-xs h-7"
|
||||
prefix={
|
||||
isBusy ? (
|
||||
<RefreshCw className="animate-spin" />
|
||||
) : (
|
||||
<LogOut />
|
||||
)
|
||||
}
|
||||
>
|
||||
{isBusy ? (
|
||||
<RefreshCw className="h-3 w-3 mr-1 animate-spin" />
|
||||
) : (
|
||||
<LogOut className="h-3 w-3 mr-1" />
|
||||
)}
|
||||
{t.oauth.disconnect}
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap font-mondwest text-xs tracking-[0.1em] uppercase transition-colors cursor-pointer"
|
||||
+ " disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-foreground/90 text-background hover:bg-foreground",
|
||||
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
outline: "border border-border bg-transparent hover:bg-foreground/10 hover:text-foreground",
|
||||
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-foreground/10 hover:text-foreground",
|
||||
link: "text-foreground underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-8 px-3 text-[0.65rem]",
|
||||
lg: "h-10 px-8",
|
||||
icon: "h-9 w-9",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export function Button({
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
...props
|
||||
}: React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof buttonVariants>) {
|
||||
return <button className={cn(buttonVariants({ variant, size }), className)} {...props} />;
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { Button } from "@nous-research/ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
export function ConfirmDialog({
|
||||
cancelLabel = "Cancel",
|
||||
|
|
@ -101,8 +101,7 @@ export function ConfirmDialog({
|
|||
<div className="flex items-center justify-end gap-2 p-3">
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
outlined
|
||||
onClick={onCancel}
|
||||
disabled={loading}
|
||||
>
|
||||
|
|
@ -111,10 +110,9 @@ export function ConfirmDialog({
|
|||
<Button
|
||||
data-confirm
|
||||
type="button"
|
||||
variant={destructive ? "destructive" : "default"}
|
||||
size="sm"
|
||||
onClick={onConfirm}
|
||||
disabled={loading}
|
||||
className={destructive ? "!bg-destructive !text-destructive-foreground" : undefined}
|
||||
>
|
||||
{loading ? "…" : confirmLabel}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ import {
|
|||
import { api } from "@/lib/api";
|
||||
import type { AnalyticsResponse, AnalyticsDailyEntry, AnalyticsModelEntry, AnalyticsSkillEntry } from "@/lib/api";
|
||||
import { timeAgo } from "@/lib/utils";
|
||||
import { Button } from "@nous-research/ui";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { usePageHeader } from "@/contexts/usePageHeader";
|
||||
import { useI18n } from "@/i18n";
|
||||
import { PluginSlot } from "@/plugins";
|
||||
|
|
@ -317,9 +317,7 @@ export default function AnalyticsPage() {
|
|||
<Button
|
||||
key={p.label}
|
||||
type="button"
|
||||
variant={days === p.days ? "default" : "outline"}
|
||||
size="sm"
|
||||
className="h-7 min-w-0 text-xs"
|
||||
outlined={days !== p.days}
|
||||
onClick={() => setDays(p.days)}
|
||||
>
|
||||
{p.label}
|
||||
|
|
@ -328,13 +326,11 @@ export default function AnalyticsPage() {
|
|||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
outlined
|
||||
onClick={load}
|
||||
disabled={loading}
|
||||
className="h-7 text-xs"
|
||||
prefix={<RefreshCw />}
|
||||
>
|
||||
<RefreshCw className="mr-1 h-3 w-3" />
|
||||
{t.common.refresh}
|
||||
</Button>
|
||||
</div>,
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ import { getNestedValue, setNestedValue } from "@/lib/nested";
|
|||
import { useToast } from "@/hooks/useToast";
|
||||
import { Toast } from "@/components/Toast";
|
||||
import { AutoField } from "@/components/AutoField";
|
||||
import { Button } from "@nous-research/ui";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useI18n } from "@/i18n";
|
||||
|
|
@ -345,10 +345,10 @@ export default function ConfigPage() {
|
|||
</code>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Button variant="ghost" size="sm" onClick={handleExport} title={t.config.exportConfig} aria-label={t.config.exportConfig}>
|
||||
<Button outlined onClick={handleExport} title={t.config.exportConfig} aria-label={t.config.exportConfig} className="!p-2 aspect-square">
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" onClick={() => fileInputRef.current?.click()} title={t.config.importConfig} aria-label={t.config.importConfig}>
|
||||
<Button outlined onClick={() => fileInputRef.current?.click()} title={t.config.importConfig} aria-label={t.config.importConfig} className="!p-2 aspect-square">
|
||||
<Upload className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<input ref={fileInputRef} type="file" accept=".json" className="hidden" onChange={handleImport} />
|
||||
|
|
@ -358,7 +358,7 @@ export default function ConfigPage() {
|
|||
: prettyCategoryName(activeCategory);
|
||||
const resetTitle = t.config.resetScopeTooltip.replace("{scope}", resetScopeLabel);
|
||||
return (
|
||||
<Button variant="ghost" size="sm" onClick={handleReset} title={resetTitle} aria-label={resetTitle}>
|
||||
<Button outlined onClick={handleReset} title={resetTitle} aria-label={resetTitle} className="!p-2 aspect-square">
|
||||
<RotateCcw className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
);
|
||||
|
|
@ -367,32 +367,19 @@ export default function ConfigPage() {
|
|||
<div className="w-px h-5 bg-border mx-1" />
|
||||
|
||||
<Button
|
||||
variant={yamlMode ? "default" : "outline"}
|
||||
size="sm"
|
||||
outlined={!yamlMode}
|
||||
onClick={() => setYamlMode(!yamlMode)}
|
||||
className="gap-1.5"
|
||||
prefix={yamlMode ? <FormInput /> : <Code />}
|
||||
>
|
||||
{yamlMode ? (
|
||||
<>
|
||||
<FormInput className="h-3.5 w-3.5" />
|
||||
{t.common.form}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Code className="h-3.5 w-3.5" />
|
||||
YAML
|
||||
</>
|
||||
)}
|
||||
{yamlMode ? t.common.form : "YAML"}
|
||||
</Button>
|
||||
|
||||
{yamlMode ? (
|
||||
<Button size="sm" onClick={handleYamlSave} disabled={yamlSaving} className="gap-1.5">
|
||||
<Save className="h-3.5 w-3.5" />
|
||||
<Button onClick={handleYamlSave} disabled={yamlSaving} prefix={<Save />}>
|
||||
{yamlSaving ? t.common.saving : t.common.save}
|
||||
</Button>
|
||||
) : (
|
||||
<Button size="sm" onClick={handleSave} disabled={saving} className="gap-1.5">
|
||||
<Save className="h-3.5 w-3.5" />
|
||||
<Button onClick={handleSave} disabled={saving} prefix={<Save />}>
|
||||
{saving ? t.common.saving : t.common.save}
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Clock, Pause, Play, Plus, Trash2, Zap } from "lucide-react";
|
||||
import { H2 } from "@nous-research/ui";
|
||||
import { Button, H2 } from "@nous-research/ui";
|
||||
import { api } from "@/lib/api";
|
||||
import type { CronJob } from "@/lib/api";
|
||||
import { DeleteConfirmDialog } from "@/components/DeleteConfirmDialog";
|
||||
|
|
@ -9,7 +9,6 @@ import { useConfirmDelete } from "@/hooks/useConfirmDelete";
|
|||
import { Toast } from "@/components/Toast";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectOption } from "@/components/ui/select";
|
||||
|
|
@ -166,7 +165,6 @@ export default function CronPage() {
|
|||
loading={jobDelete.isDeleting}
|
||||
/>
|
||||
|
||||
{/* Create new job form */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-base">
|
||||
|
|
@ -237,9 +235,9 @@ export default function CronPage() {
|
|||
<Button
|
||||
onClick={handleCreate}
|
||||
disabled={creating}
|
||||
prefix={<Plus />}
|
||||
className="w-full"
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
{creating ? t.common.creating : t.common.create}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -248,7 +246,6 @@ export default function CronPage() {
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Jobs list */}
|
||||
<div className="flex flex-col gap-3">
|
||||
<H2
|
||||
variant="sm"
|
||||
|
|
@ -269,7 +266,6 @@ export default function CronPage() {
|
|||
{jobs.map((job) => (
|
||||
<Card key={job.id}>
|
||||
<CardContent className="flex items-center gap-4 py-4">
|
||||
{/* Info */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className="font-medium text-sm truncate">
|
||||
|
|
@ -306,16 +302,15 @@ export default function CronPage() {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
outlined
|
||||
title={job.state === "paused" ? t.cron.resume : t.cron.pause}
|
||||
aria-label={
|
||||
job.state === "paused" ? t.cron.resume : t.cron.pause
|
||||
}
|
||||
onClick={() => handlePauseResume(job)}
|
||||
className="!p-2 aspect-square"
|
||||
>
|
||||
{job.state === "paused" ? (
|
||||
<Play className="h-4 w-4 text-success" />
|
||||
|
|
@ -325,21 +320,21 @@ export default function CronPage() {
|
|||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
outlined
|
||||
title={t.cron.triggerNow}
|
||||
aria-label={t.cron.triggerNow}
|
||||
onClick={() => handleTrigger(job)}
|
||||
className="!p-2 aspect-square"
|
||||
>
|
||||
<Zap className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
outlined
|
||||
title={t.common.delete}
|
||||
aria-label={t.common.delete}
|
||||
onClick={() => jobDelete.requestDelete(job.id)}
|
||||
className="!p-2 aspect-square"
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-destructive" />
|
||||
</Button>
|
||||
|
|
@ -348,6 +343,7 @@ export default function CronPage() {
|
|||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<PluginSlot name="cron:bottom" />
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,12 +2,19 @@ import { useLayoutEffect } from "react";
|
|||
import { ExternalLink } from "lucide-react";
|
||||
import { useI18n } from "@/i18n";
|
||||
import { usePageHeader } from "@/contexts/usePageHeader";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { PluginSlot } from "@/plugins";
|
||||
|
||||
export const HERMES_DOCS_URL = "https://hermes-agent.nousresearch.com/docs/";
|
||||
|
||||
const DS_BUTTON_OUTLINED_LINK_CN = cn(
|
||||
"group relative inline-grid grid-cols-[auto_1fr_auto] items-center",
|
||||
"px-[.9em_.75em] py-[1.25em] gap-2",
|
||||
"leading-0 font-bold tracking-[0.2em] uppercase",
|
||||
"text-midground bg-transparent shadow-midground",
|
||||
"shadow-[inset_-1px_-1px_0_0_#00000080,inset_1px_1px_0_0_#ffffff80]",
|
||||
);
|
||||
|
||||
export default function DocsPage() {
|
||||
const { t } = useI18n();
|
||||
const { setEnd } = usePageHeader();
|
||||
|
|
@ -18,12 +25,9 @@ export default function DocsPage() {
|
|||
href={HERMES_DOCS_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={cn(
|
||||
buttonVariants({ variant: "outline", size: "sm" }),
|
||||
"h-7 text-xs",
|
||||
)}
|
||||
className={DS_BUTTON_OUTLINED_LINK_CN}
|
||||
>
|
||||
<ExternalLink className="mr-1.5 h-3 w-3" />
|
||||
<ExternalLink className="size-3.5" />
|
||||
{t.app.openDocumentation}
|
||||
</a>,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import { Toast } from "@/components/Toast";
|
|||
import { useConfirmDelete } from "@/hooks/useConfirmDelete";
|
||||
import { useToast } from "@/hooks/useToast";
|
||||
import { OAuthProvidersCard } from "@/components/OAuthProvidersCard";
|
||||
import { Button } from "@nous-research/ui";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useI18n } from "@/i18n";
|
||||
|
|
@ -134,9 +134,8 @@ function EnvVarRow({
|
|||
{t.env.getKey} <ExternalLink className="h-2.5 w-2.5" />
|
||||
</a>
|
||||
)}
|
||||
<Button size="sm" variant="outline" className="h-6 text-[0.6rem] px-2"
|
||||
<Button outlined prefix={<Pencil />}
|
||||
onClick={() => setEdits((prev) => ({ ...prev, [varKey]: "" }))}>
|
||||
<Pencil className="h-2.5 w-2.5" />
|
||||
{t.common.set}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -159,9 +158,8 @@ function EnvVarRow({
|
|||
{t.env.getKey} <ExternalLink className="h-2.5 w-2.5" />
|
||||
</a>
|
||||
)}
|
||||
<Button size="sm" variant="outline" className="h-7 text-[0.6rem]"
|
||||
<Button outlined prefix={<Pencil />}
|
||||
onClick={() => setEdits((prev) => ({ ...prev, [varKey]: "" }))}>
|
||||
<Pencil className="h-3 w-3" />
|
||||
{t.common.set}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -206,26 +204,25 @@ function EnvVarRow({
|
|||
</div>
|
||||
|
||||
{info.is_set && (
|
||||
<Button size="sm" variant="ghost" onClick={() => onReveal(varKey)}
|
||||
<Button outlined onClick={() => onReveal(varKey)}
|
||||
title={isRevealed ? t.env.hideValue : t.env.showValue}
|
||||
aria-label={isRevealed ? `Hide ${varKey}` : `Reveal ${varKey}`}>
|
||||
aria-label={isRevealed ? `Hide ${varKey}` : `Reveal ${varKey}`}
|
||||
className="!p-2 aspect-square">
|
||||
{isRevealed
|
||||
? <EyeOff className="h-4 w-4" />
|
||||
: <Eye className="h-4 w-4" />}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button size="sm" variant="outline"
|
||||
<Button outlined prefix={<Pencil />}
|
||||
onClick={() => setEdits((prev) => ({ ...prev, [varKey]: "" }))}>
|
||||
<Pencil className="h-3 w-3" />
|
||||
{info.is_set ? t.common.replace : t.common.set}
|
||||
</Button>
|
||||
|
||||
{info.is_set && (
|
||||
<Button size="sm" variant="ghost"
|
||||
className="text-destructive hover:text-destructive hover:bg-destructive/10"
|
||||
<Button outlined prefix={<Trash2 />}
|
||||
className="text-destructive hover:!text-destructive"
|
||||
onClick={() => onClear(varKey)} disabled={saving === varKey || clearDialogOpen}>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
{saving === varKey ? "..." : t.common.clear}
|
||||
</Button>
|
||||
)}
|
||||
|
|
@ -238,13 +235,12 @@ function EnvVarRow({
|
|||
onChange={(e) => setEdits((prev) => ({ ...prev, [varKey]: e.target.value }))}
|
||||
placeholder={info.is_set ? t.env.replaceCurrentValue.replace("{preview}", info.redacted_value ?? "---") : t.env.enterValue}
|
||||
className="flex-1 font-mono-ui text-xs" />
|
||||
<Button size="sm" onClick={() => onSave(varKey)}
|
||||
<Button onClick={() => onSave(varKey)} prefix={<Save />}
|
||||
disabled={saving === varKey || !edits[varKey]}>
|
||||
<Save className="h-3 w-3" />
|
||||
{saving === varKey ? "..." : t.common.save}
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onClick={() => onCancelEdit(varKey)}>
|
||||
<X className="h-3 w-3" /> {t.common.cancel}
|
||||
<Button outlined prefix={<X />} onClick={() => onCancelEdit(varKey)}>
|
||||
{t.common.cancel}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -537,7 +533,7 @@ export default function EnvPage() {
|
|||
{t.env.changesNote}
|
||||
</p>
|
||||
</div>
|
||||
<Button variant="ghost" size="sm" onClick={() => setShowAdvanced(!showAdvanced)}>
|
||||
<Button outlined onClick={() => setShowAdvanced(!showAdvanced)}>
|
||||
{showAdvanced ? t.env.hideAdvanced : t.env.showAdvanced}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useEffect, useLayoutEffect, useState, useCallback, useRef } from "react";
|
||||
import { FileText, RefreshCw } from "lucide-react";
|
||||
import { api } from "@/lib/api";
|
||||
import { Button } from "@nous-research/ui";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Label } from "@/components/ui/label";
|
||||
|
|
@ -101,13 +101,11 @@ export default function LogsPage() {
|
|||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
outlined
|
||||
onClick={fetchLogs}
|
||||
disabled={loading}
|
||||
className="h-7 text-xs"
|
||||
prefix={<RefreshCw />}
|
||||
>
|
||||
<RefreshCw className="mr-1 h-3 w-3" />
|
||||
{t.common.refresh}
|
||||
</Button>
|
||||
</div>,
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ import { timeAgo } from "@/lib/utils";
|
|||
import { Markdown } from "@/components/Markdown";
|
||||
import { PlatformsCard } from "@/components/PlatformsCard";
|
||||
import { Toast } from "@/components/Toast";
|
||||
import { Button } from "@nous-research/ui";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { DeleteConfirmDialog } from "@/components/DeleteConfirmDialog";
|
||||
import { useConfirmDelete } from "@/hooks/useConfirmDelete";
|
||||
|
|
@ -356,9 +356,8 @@ function SessionRow({
|
|||
</Badge>
|
||||
{resumeInChatEnabled && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-7 w-7 text-muted-foreground hover:text-success"
|
||||
outlined
|
||||
className="!p-1.5 aspect-square text-muted-foreground hover:text-success"
|
||||
aria-label={t.sessions.resumeInChat}
|
||||
title={t.sessions.resumeInChat}
|
||||
onClick={(e) => {
|
||||
|
|
@ -370,9 +369,8 @@ function SessionRow({
|
|||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-7 w-7 text-muted-foreground hover:text-destructive"
|
||||
outlined
|
||||
className="!p-1.5 aspect-square text-muted-foreground hover:text-destructive"
|
||||
aria-label={t.sessions.deleteSession}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
|
@ -808,9 +806,8 @@ export default function SessionsPage() {
|
|||
</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-7 w-7 p-0"
|
||||
outlined
|
||||
className="!p-1.5 aspect-square"
|
||||
disabled={page === 0}
|
||||
onClick={() => setPage((p) => p - 1)}
|
||||
aria-label={t.sessions.previousPage}
|
||||
|
|
@ -822,9 +819,8 @@ export default function SessionsPage() {
|
|||
{Math.ceil(total / PAGE_SIZE)}
|
||||
</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-7 w-7 p-0"
|
||||
outlined
|
||||
className="!p-1.5 aspect-square"
|
||||
disabled={(page + 1) * PAGE_SIZE >= total}
|
||||
onClick={() => setPage((p) => p + 1)}
|
||||
aria-label={t.sessions.nextPage}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ import React, {
|
|||
} from "react";
|
||||
import { api, fetchJSON } from "@/lib/api";
|
||||
import { cn, timeAgo, isoTimeAgo } from "@/lib/utils";
|
||||
import { Button } from "@nous-research/ui";
|
||||
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectOption } from "@/components/ui/select";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue