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