- {tx(
- "settings.automations.legacyWarning",
- "This older automation is missing its target chat. Recreate it from the chat or channel where it should run.",
- )}
-
- ) : null}
-
{job.state.last_error ? (
{job.state.last_error}
@@ -4227,19 +4217,14 @@ function AutomationDeleteDialog({
);
}
-function automationNeedsRecreation(job: SessionAutomationJob): boolean {
- return !job.protected && !job.origin && job.payload.kind === "agent_turn";
-}
-
function automationNeedsAttention(job: SessionAutomationJob): boolean {
- return automationNeedsRecreation(job) || job.state.last_status === "error";
+ return job.state.last_status === "error";
}
function automationStatusKey(
job: SessionAutomationJob,
-): "active" | "running" | "paused" | "failed" | "system" | "needs_setup" | "completed" | "idle" {
+): "active" | "running" | "paused" | "failed" | "system" | "completed" | "idle" {
if (job.protected) return "system";
- if (automationNeedsRecreation(job)) return "needs_setup";
if (job.state.pending) return "running";
if (!job.enabled) return "paused";
if (job.state.last_status === "error") return "failed";
@@ -4412,9 +4397,6 @@ function automationStatus(
): { label: string; tone: "neutral" | "success" | "warning" } {
const status = automationStatusKey(job);
if (status === "system") return { label: tx("settings.automations.status.system", "System"), tone: "neutral" };
- if (status === "needs_setup") {
- return { label: tx("settings.automations.status.needsSetup", "Needs setup"), tone: "warning" };
- }
if (status === "running") {
return { label: tx("settings.automations.status.running", "Running now"), tone: "warning" };
}
@@ -4437,9 +4419,6 @@ function automationOriginLabel(
): string {
if (job.protected) return tx("settings.automations.origin.system", "System");
const origin = job.origin;
- if (!origin && job.payload.kind === "agent_turn") {
- return tx("settings.automations.origin.legacy", "Recreate in target chat");
- }
if (!origin) return tx("settings.automations.origin.unknown", "No linked chat");
if (origin.channel !== "websocket") return automationChannelLabel(origin.channel, tx);
return origin.title || origin.preview || origin.session_key || automationChannelLabel(origin.channel, tx);
@@ -4574,7 +4553,7 @@ function formatAutomationNextTitle(
function automationStatusDotClass(job: SessionAutomationJob): string {
const status = automationStatusKey(job);
if (status === "active" || status === "running") return "bg-emerald-500";
- if (status === "failed" || status === "needs_setup") return "bg-amber-500";
+ if (status === "failed") return "bg-amber-500";
if (status === "system") return "bg-blue-500";
return "bg-muted-foreground/45";
}
diff --git a/webui/src/i18n/locales/en/common.json b/webui/src/i18n/locales/en/common.json
index aaa35a21..372173b6 100644
--- a/webui/src/i18n/locales/en/common.json
+++ b/webui/src/i18n/locales/en/common.json
@@ -525,18 +525,15 @@
"system": "System",
"pending": "Pending",
"running": "Running now",
- "needsSetup": "Needs setup",
"paused": "Paused",
"failed": "Failed",
"completed": "Completed",
"noSchedule": "No schedule",
"active": "Active"
},
- "legacyWarning": "This older automation is missing its target chat. Recreate it from the chat or channel where it should run.",
"origin": {
"system": "System",
- "unknown": "No linked chat",
- "legacy": "Recreate in target chat"
+ "unknown": "No linked chat"
},
"channels": {
"api": "API",
diff --git a/webui/src/i18n/locales/es/common.json b/webui/src/i18n/locales/es/common.json
index aba33564..cc27c365 100644
--- a/webui/src/i18n/locales/es/common.json
+++ b/webui/src/i18n/locales/es/common.json
@@ -525,18 +525,15 @@
"system": "Sistema",
"pending": "Pendiente",
"running": "Ejecutándose ahora",
- "needsSetup": "Requiere configuración",
"paused": "Pausada",
"failed": "Fallida",
"completed": "Completada",
"noSchedule": "Sin programación",
"active": "Activa"
},
- "legacyWarning": "Esta automatización antigua no tiene chat de destino. Vuelve a crearla desde el chat o canal donde debe ejecutarse.",
"origin": {
"system": "Sistema",
- "unknown": "Sin chat vinculado",
- "legacy": "Recréala en el chat de destino"
+ "unknown": "Sin chat vinculado"
},
"channels": {
"api": "API",
diff --git a/webui/src/i18n/locales/fr/common.json b/webui/src/i18n/locales/fr/common.json
index 5ce9b6dc..9e2c4268 100644
--- a/webui/src/i18n/locales/fr/common.json
+++ b/webui/src/i18n/locales/fr/common.json
@@ -525,18 +525,15 @@
"system": "Système",
"pending": "En attente",
"running": "En cours d’exécution",
- "needsSetup": "Configuration requise",
"paused": "En pause",
"failed": "Échouée",
"completed": "Terminée",
"noSchedule": "Aucun planning",
"active": "En cours"
},
- "legacyWarning": "Cette ancienne automatisation n’a pas de discussion cible. Recréez-la depuis la discussion ou le canal où elle doit s’exécuter.",
"origin": {
"system": "Système",
- "unknown": "Aucune discussion liée",
- "legacy": "Recréez-la dans la discussion cible"
+ "unknown": "Aucune discussion liée"
},
"channels": {
"api": "API",
diff --git a/webui/src/i18n/locales/id/common.json b/webui/src/i18n/locales/id/common.json
index adab95db..6885d3a1 100644
--- a/webui/src/i18n/locales/id/common.json
+++ b/webui/src/i18n/locales/id/common.json
@@ -525,18 +525,15 @@
"system": "Sistem",
"pending": "Menunggu",
"running": "Sedang berjalan",
- "needsSetup": "Perlu disiapkan",
"paused": "Dijeda",
"failed": "Gagal",
"completed": "Selesai",
"noSchedule": "Tanpa jadwal",
"active": "Aktif"
},
- "legacyWarning": "Otomasi lama ini tidak memiliki chat tujuan. Buat ulang dari chat atau channel tempat otomasi akan berjalan.",
"origin": {
"system": "Sistem",
- "unknown": "Tidak ada chat tertaut",
- "legacy": "Buat ulang di chat tujuan"
+ "unknown": "Tidak ada chat tertaut"
},
"channels": {
"api": "API",
diff --git a/webui/src/i18n/locales/ja/common.json b/webui/src/i18n/locales/ja/common.json
index 632b4f42..e5be1c4d 100644
--- a/webui/src/i18n/locales/ja/common.json
+++ b/webui/src/i18n/locales/ja/common.json
@@ -525,18 +525,15 @@
"system": "システム",
"pending": "待機中",
"running": "実行中",
- "needsSetup": "再設定が必要",
"paused": "一時停止",
"failed": "失敗",
"completed": "完了",
"noSchedule": "スケジュールなし",
"active": "実行中"
},
- "legacyWarning": "この古い自動タスクには対象チャットがありません。実行先のチャットまたは外部 channel から作り直してください。",
"origin": {
"system": "システム",
- "unknown": "関連チャットなし",
- "legacy": "対象チャットで作り直してください"
+ "unknown": "関連チャットなし"
},
"channels": {
"api": "API",
diff --git a/webui/src/i18n/locales/ko/common.json b/webui/src/i18n/locales/ko/common.json
index 15e1611b..e8741c83 100644
--- a/webui/src/i18n/locales/ko/common.json
+++ b/webui/src/i18n/locales/ko/common.json
@@ -525,18 +525,15 @@
"system": "시스템",
"pending": "대기 중",
"running": "실행 중",
- "needsSetup": "설정 필요",
"paused": "일시 중지",
"failed": "실패",
"completed": "완료",
"noSchedule": "일정 없음",
"active": "활성"
},
- "legacyWarning": "이전 자동화에 대상 채팅이 없습니다. 실행되어야 하는 채팅 또는 외부 channel에서 다시 만드세요.",
"origin": {
"system": "시스템",
- "unknown": "연결된 채팅 없음",
- "legacy": "대상 채팅에서 다시 만드세요"
+ "unknown": "연결된 채팅 없음"
},
"channels": {
"api": "API",
diff --git a/webui/src/i18n/locales/vi/common.json b/webui/src/i18n/locales/vi/common.json
index 2e549e1f..4132f093 100644
--- a/webui/src/i18n/locales/vi/common.json
+++ b/webui/src/i18n/locales/vi/common.json
@@ -525,18 +525,15 @@
"system": "Hệ thống",
"pending": "Đang chờ",
"running": "Đang chạy",
- "needsSetup": "Cần thiết lập",
"paused": "Đã tạm dừng",
"failed": "Thất bại",
"completed": "Hoàn tất",
"noSchedule": "Không có lịch",
"active": "Đang chạy"
},
- "legacyWarning": "Tự động hóa cũ này thiếu cuộc trò chuyện đích. Hãy tạo lại từ cuộc trò chuyện hoặc channel nơi nó sẽ chạy.",
"origin": {
"system": "Hệ thống",
- "unknown": "Chưa liên kết cuộc trò chuyện",
- "legacy": "Tạo lại trong cuộc trò chuyện đích"
+ "unknown": "Chưa liên kết cuộc trò chuyện"
},
"channels": {
"api": "API",
diff --git a/webui/src/i18n/locales/zh-CN/common.json b/webui/src/i18n/locales/zh-CN/common.json
index f5e62e90..df54b609 100644
--- a/webui/src/i18n/locales/zh-CN/common.json
+++ b/webui/src/i18n/locales/zh-CN/common.json
@@ -525,18 +525,15 @@
"system": "系统",
"pending": "等待中",
"running": "正在运行",
- "needsSetup": "需重新设置",
"paused": "已暂停",
"failed": "失败",
"completed": "已完成",
"noSchedule": "无计划",
"active": "运行中"
},
- "legacyWarning": "这个旧版自动任务缺少目标会话。请从它应该运行的聊天或外部 channel 中重新创建。",
"origin": {
"system": "系统",
- "unknown": "未关联会话",
- "legacy": "请在目标会话中重新创建"
+ "unknown": "未关联会话"
},
"channels": {
"api": "API",
diff --git a/webui/src/i18n/locales/zh-TW/common.json b/webui/src/i18n/locales/zh-TW/common.json
index c787de5a..2c6f438f 100644
--- a/webui/src/i18n/locales/zh-TW/common.json
+++ b/webui/src/i18n/locales/zh-TW/common.json
@@ -525,18 +525,15 @@
"system": "系統",
"pending": "等待中",
"running": "正在執行",
- "needsSetup": "需重新設定",
"paused": "已暫停",
"failed": "失敗",
"completed": "已完成",
"noSchedule": "無排程",
"active": "執行中"
},
- "legacyWarning": "這個舊版自動任務缺少目標會話。請從它應該執行的聊天或外部 channel 中重新建立。",
"origin": {
"system": "系統",
- "unknown": "未關聯會話",
- "legacy": "請在目標會話中重新建立"
+ "unknown": "未關聯會話"
},
"channels": {
"api": "API",
diff --git a/webui/src/lib/types.ts b/webui/src/lib/types.ts
index 436aa8d6..88de7e38 100644
--- a/webui/src/lib/types.ts
+++ b/webui/src/lib/types.ts
@@ -114,9 +114,6 @@ export interface SessionAutomationJob {
payload: {
message: string;
kind?: "agent_turn" | "system_event" | string;
- session_key?: string | null;
- origin_channel?: string | null;
- origin_chat_id?: string | null;
};
state: {
next_run_at_ms?: number | null;
diff --git a/webui/src/tests/app-layout.test.tsx b/webui/src/tests/app-layout.test.tsx
index ff3e95d1..46657c28 100644
--- a/webui/src/tests/app-layout.test.tsx
+++ b/webui/src/tests/app-layout.test.tsx
@@ -372,7 +372,6 @@ describe("App layout", () => {
payload: {
message: "Check the repo status",
kind: "agent_turn",
- session_key: "websocket:chat-a",
},
state: {
next_run_at_ms: Date.UTC(2026, 3, 17, 10, 0, 0),
@@ -388,26 +387,6 @@ describe("App layout", () => {
preview: "Check release blockers",
},
},
- {
- id: "legacy-quiz",
- name: "english-quiz",
- enabled: true,
- protected: false,
- delete_after_run: false,
- schedule: { kind: "cron", expr: "30 9-23 * * *", tz: "Asia/Shanghai" },
- payload: {
- message: "Practice English",
- kind: "agent_turn",
- session_key: "unified:default",
- },
- state: {
- next_run_at_ms: Date.UTC(2026, 3, 17, 11, 0, 0),
- last_status: "ok",
- pending: false,
- run_history: [],
- },
- origin: null,
- },
{
id: "external-quiz",
name: "WeChat quiz",
@@ -418,7 +397,6 @@ describe("App layout", () => {
payload: {
message: "Send a quiz",
kind: "agent_turn",
- origin_channel: "weixin",
},
state: {
next_run_at_ms: Date.UTC(2026, 3, 17, 11, 30, 0),
@@ -464,9 +442,6 @@ describe("App layout", () => {
expect(screen.getAllByText("Daily repo check").length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText("Check the repo status").length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText("Release prep").length).toBeGreaterThanOrEqual(1);
- expect(screen.getByText("english-quiz")).toBeInTheDocument();
- expect(screen.getByText("Recreate in target chat")).toBeInTheDocument();
- expect(screen.queryByText("unified:default")).not.toBeInTheDocument();
expect(screen.getByText("WeChat quiz")).toBeInTheDocument();
expect(screen.getByText("WeChat")).toBeInTheDocument();
expect(screen.queryByText("weixin:wx-chat")).not.toBeInTheDocument();
@@ -490,7 +465,6 @@ describe("App layout", () => {
payload: {
message: "Old one-shot message",
kind: "agent_turn",
- session_key: "websocket:chat-a",
},
state: {
next_run_at_ms: null,
@@ -589,7 +563,6 @@ describe("App layout", () => {
payload: {
message: longMessage,
kind: "agent_turn",
- session_key: "websocket:chat-a",
},
state: {
next_run_at_ms: Date.UTC(2026, 3, 18, 10, 0, 0),
@@ -650,7 +623,6 @@ describe("App layout", () => {
payload: {
message: "检查仓库状态",
kind: "agent_turn",
- session_key: "websocket:chat-a",
},
state: {
next_run_at_ms: Date.UTC(2026, 3, 17, 10, 0, 0),
From 8f1fe7337c67dfbf8481dce2bd77df1067ce8266 Mon Sep 17 00:00:00 2001
From: chengyongru <2755839590@qq.com>
Date: Tue, 16 Jun 2026 00:42:25 +0800
Subject: [PATCH 25/38] fix(webui): tighten automation toolbar layout
---
.../src/components/settings/SettingsView.tsx | 50 ++++++++++---------
webui/src/i18n/locales/en/common.json | 2 +-
webui/src/i18n/locales/es/common.json | 2 +-
webui/src/i18n/locales/fr/common.json | 2 +-
webui/src/i18n/locales/id/common.json | 2 +-
webui/src/i18n/locales/ja/common.json | 2 +-
webui/src/i18n/locales/ko/common.json | 2 +-
webui/src/i18n/locales/vi/common.json | 2 +-
webui/src/i18n/locales/zh-CN/common.json | 2 +-
webui/src/i18n/locales/zh-TW/common.json | 2 +-
10 files changed, 35 insertions(+), 33 deletions(-)
diff --git a/webui/src/components/settings/SettingsView.tsx b/webui/src/components/settings/SettingsView.tsx
index 71c5b58b..b95b8d66 100644
--- a/webui/src/components/settings/SettingsView.tsx
+++ b/webui/src/components/settings/SettingsView.tsx
@@ -3477,42 +3477,44 @@ function AutomationsSettings({
return (
-
-
-
- {summaryOptions.map((option) => (
-
onFilterChange(option.value)}
- className={cn(
- "inline-flex h-8 items-center gap-2 rounded-[12px] px-3 text-[12px] font-medium text-muted-foreground transition-colors",
- filter === option.value && "bg-background text-foreground shadow-sm",
- )}
- >
- {option.label}
-
- {option.count}
-
-
- ))}
+
+
+
+
+ {summaryOptions.map((option) => (
+ onFilterChange(option.value)}
+ className={cn(
+ "inline-flex h-8 shrink-0 items-center gap-2 whitespace-nowrap rounded-[12px] px-3 text-[12px] font-medium text-muted-foreground transition-colors",
+ filter === option.value && "bg-background text-foreground shadow-sm",
+ )}
+ >
+ {option.label}
+
+ {option.count}
+
+
+ ))}
+
-
-
+
+
onQueryChange(event.target.value)}
- placeholder={tx("settings.automations.search", "Search automation, message, session, or cron expression")}
- className="h-9 rounded-[13px] border-border/45 bg-background/85 pl-9 text-[13px] shadow-sm"
+ placeholder={tx("settings.automations.search", "Search automations")}
+ className="h-9 w-full rounded-[13px] border-border/45 bg-background/85 pl-9 text-[13px] shadow-sm"
/>
{sortLabel[sort]}
diff --git a/webui/src/i18n/locales/en/common.json b/webui/src/i18n/locales/en/common.json
index 372173b6..47d2bb25 100644
--- a/webui/src/i18n/locales/en/common.json
+++ b/webui/src/i18n/locales/en/common.json
@@ -493,7 +493,7 @@
"updated": "Updated",
"name": "Name"
},
- "search": "Search automation, message, session, or cron expression",
+ "search": "Search automations",
"queue": "Queue",
"loading": "Loading automations...",
"noMatches": "No automations match this view.",
diff --git a/webui/src/i18n/locales/es/common.json b/webui/src/i18n/locales/es/common.json
index cc27c365..94c6bf05 100644
--- a/webui/src/i18n/locales/es/common.json
+++ b/webui/src/i18n/locales/es/common.json
@@ -493,7 +493,7 @@
"updated": "Actualizada",
"name": "Nombre"
},
- "search": "Buscar tarea, mensaje, sesión o expresión cron",
+ "search": "Buscar automatizaciones",
"queue": "Cola",
"loading": "Cargando automatizaciones...",
"noMatches": "No hay automatizaciones que coincidan con esta vista.",
diff --git a/webui/src/i18n/locales/fr/common.json b/webui/src/i18n/locales/fr/common.json
index 9e2c4268..7519eef3 100644
--- a/webui/src/i18n/locales/fr/common.json
+++ b/webui/src/i18n/locales/fr/common.json
@@ -493,7 +493,7 @@
"updated": "Mise à jour",
"name": "Nom"
},
- "search": "Rechercher une tâche, un message, une session ou une expression cron",
+ "search": "Rechercher des automatisations",
"queue": "File",
"loading": "Chargement des automatisations...",
"noMatches": "Aucune automatisation ne correspond à cette vue.",
diff --git a/webui/src/i18n/locales/id/common.json b/webui/src/i18n/locales/id/common.json
index 6885d3a1..43cc0d8f 100644
--- a/webui/src/i18n/locales/id/common.json
+++ b/webui/src/i18n/locales/id/common.json
@@ -493,7 +493,7 @@
"updated": "Diperbarui",
"name": "Nama"
},
- "search": "Cari tugas, pesan, sesi, atau ekspresi cron",
+ "search": "Cari otomatisasi",
"queue": "Antrean",
"loading": "Memuat otomasi...",
"noMatches": "Tidak ada otomasi yang cocok dengan tampilan ini.",
diff --git a/webui/src/i18n/locales/ja/common.json b/webui/src/i18n/locales/ja/common.json
index e5be1c4d..d530c2a6 100644
--- a/webui/src/i18n/locales/ja/common.json
+++ b/webui/src/i18n/locales/ja/common.json
@@ -493,7 +493,7 @@
"updated": "更新日時",
"name": "名前"
},
- "search": "タスク、メッセージ、セッション、cron 式を検索",
+ "search": "自動化を検索",
"queue": "キュー",
"loading": "自動タスクを読み込み中...",
"noMatches": "この表示に一致する自動タスクはありません。",
diff --git a/webui/src/i18n/locales/ko/common.json b/webui/src/i18n/locales/ko/common.json
index e8741c83..8e3ac4ee 100644
--- a/webui/src/i18n/locales/ko/common.json
+++ b/webui/src/i18n/locales/ko/common.json
@@ -493,7 +493,7 @@
"updated": "업데이트",
"name": "이름"
},
- "search": "작업, 메시지, 세션 또는 cron 식 검색",
+ "search": "자동화 검색",
"queue": "대기열",
"loading": "자동화를 불러오는 중...",
"noMatches": "이 보기와 일치하는 자동화가 없습니다.",
diff --git a/webui/src/i18n/locales/vi/common.json b/webui/src/i18n/locales/vi/common.json
index 4132f093..888234d3 100644
--- a/webui/src/i18n/locales/vi/common.json
+++ b/webui/src/i18n/locales/vi/common.json
@@ -493,7 +493,7 @@
"updated": "Đã cập nhật",
"name": "Tên"
},
- "search": "Tìm tác vụ, tin nhắn, phiên hoặc biểu thức cron",
+ "search": "Tìm tự động hóa",
"queue": "Hàng đợi",
"loading": "Đang tải tự động hóa...",
"noMatches": "Không có tự động hóa phù hợp với chế độ xem này.",
diff --git a/webui/src/i18n/locales/zh-CN/common.json b/webui/src/i18n/locales/zh-CN/common.json
index df54b609..79828a17 100644
--- a/webui/src/i18n/locales/zh-CN/common.json
+++ b/webui/src/i18n/locales/zh-CN/common.json
@@ -493,7 +493,7 @@
"updated": "更新时间",
"name": "名称"
},
- "search": "搜索任务、消息、会话或 cron 表达式",
+ "search": "搜索自动任务",
"queue": "任务队列",
"loading": "正在加载自动任务...",
"noMatches": "当前视图没有匹配的自动任务。",
diff --git a/webui/src/i18n/locales/zh-TW/common.json b/webui/src/i18n/locales/zh-TW/common.json
index 2c6f438f..a114bf44 100644
--- a/webui/src/i18n/locales/zh-TW/common.json
+++ b/webui/src/i18n/locales/zh-TW/common.json
@@ -493,7 +493,7 @@
"updated": "更新時間",
"name": "名稱"
},
- "search": "搜尋任務、訊息、會話或 cron 表達式",
+ "search": "搜尋自動任務",
"queue": "任務佇列",
"loading": "正在載入自動任務...",
"noMatches": "目前檢視沒有符合的自動任務。",
From f56a73b2d3cf28128da726eec2d0a615aab6054a Mon Sep 17 00:00:00 2001
From: chengyongru <2755839590@qq.com>
Date: Tue, 16 Jun 2026 00:59:42 +0800
Subject: [PATCH 26/38] fix(webui): align automation toolbar controls
---
.../src/components/settings/SettingsView.tsx | 193 ++++++++++++++----
webui/src/i18n/locales/en/common.json | 2 +-
webui/src/i18n/locales/es/common.json | 2 +-
webui/src/i18n/locales/fr/common.json | 2 +-
webui/src/i18n/locales/id/common.json | 2 +-
webui/src/i18n/locales/ja/common.json | 2 +-
webui/src/i18n/locales/ko/common.json | 2 +-
webui/src/i18n/locales/vi/common.json | 2 +-
webui/src/i18n/locales/zh-CN/common.json | 2 +-
webui/src/i18n/locales/zh-TW/common.json | 2 +-
webui/src/tests/app-layout.test.tsx | 13 +-
11 files changed, 175 insertions(+), 49 deletions(-)
diff --git a/webui/src/components/settings/SettingsView.tsx b/webui/src/components/settings/SettingsView.tsx
index b95b8d66..4db5e2b8 100644
--- a/webui/src/components/settings/SettingsView.tsx
+++ b/webui/src/components/settings/SettingsView.tsx
@@ -3438,10 +3438,10 @@ function AutomationsSettings({
const locale = i18n.resolvedLanguage || i18n.language;
const [selectedJobId, setSelectedJobId] = useState(null);
const filtered = useMemo(() => {
- const normalizedQuery = query.trim().toLowerCase();
+ const searchTokens = parseAutomationSearchQuery(query);
return sortAutomationJobs(jobs, sort)
.filter((job) => automationMatchesFilter(job, filter))
- .filter((job) => !normalizedQuery || automationSearchText(job).includes(normalizedQuery));
+ .filter((job) => !searchTokens.length || automationMatchesSearch(job, searchTokens));
}, [filter, jobs, query, sort]);
const activeCount = jobs.filter((job) => {
const key = automationStatusKey(job);
@@ -3478,16 +3478,16 @@ function AutomationsSettings({
return (
-
+
-
+
{summaryOptions.map((option) => (
onFilterChange(option.value)}
className={cn(
- "inline-flex h-8 shrink-0 items-center gap-2 whitespace-nowrap rounded-[12px] px-3 text-[12px] font-medium text-muted-foreground transition-colors",
+ "inline-flex h-8 min-w-0 shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-[12px] px-3 text-[12px] font-medium text-muted-foreground transition-colors",
filter === option.value && "bg-background text-foreground shadow-sm",
)}
>
@@ -3506,7 +3506,7 @@ function AutomationsSettings({
onQueryChange(event.target.value)}
- placeholder={tx("settings.automations.search", "Search automations")}
+ placeholder={tx("settings.automations.search", "name:quiz chat:WeChat cron:09-23")}
className="h-9 w-full rounded-[13px] border-border/45 bg-background/85 pl-9 text-[13px] shadow-sm"
/>
@@ -4364,24 +4364,156 @@ function automationScheduleChanged(
return draft.atLocal !== formatLocalDateTimeInput(job.schedule.at_ms ?? NaN);
}
-function automationSearchText(job: SessionAutomationJob): string {
- const originText = job.origin
- ? job.origin.channel === "websocket"
- ? [job.origin.session_key, job.origin.title, job.origin.preview]
- : [job.origin.channel]
- : [];
+type AutomationSearchField = "id" | "name" | "message" | "chat" | "cron" | "schedule" | "status";
+
+interface AutomationSearchToken {
+ field: AutomationSearchField | null;
+ value: string;
+}
+
+const AUTOMATION_SEARCH_FIELDS = new Set
([
+ "id",
+ "name",
+ "message",
+ "chat",
+ "cron",
+ "schedule",
+ "status",
+]);
+
+const AUTOMATION_CHANNEL_LABELS: Record = {
+ api: "API",
+ cli: "CLI",
+ dingtalk: "DingTalk",
+ discord: "Discord",
+ email: "Email",
+ feishu: "Feishu",
+ matrix: "Matrix",
+ msteams: "Microsoft Teams",
+ qq: "QQ",
+ slack: "Slack",
+ telegram: "Telegram",
+ wechat: "WeChat",
+ wecom: "WeCom",
+ weixin: "WeChat",
+ whatsapp: "WhatsApp",
+};
+
+function parseAutomationSearchQuery(query: string): AutomationSearchToken[] {
+ return (query.match(/[^\s:]+:"[^"]+"|"[^"]+"|\S+/g) ?? [])
+ .map((rawPart): AutomationSearchToken | null => {
+ const part = trimAutomationSearchValue(rawPart);
+ if (!part) return null;
+ const fieldMatch = part.match(/^([A-Za-z]+):(.*)$/);
+ if (!fieldMatch) return { field: null, value: part.toLowerCase() };
+ const field = fieldMatch[1].toLowerCase() as AutomationSearchField;
+ const value = trimAutomationSearchValue(fieldMatch[2]).toLowerCase();
+ if (!value) return null;
+ return AUTOMATION_SEARCH_FIELDS.has(field)
+ ? { field, value }
+ : { field: null, value: part.toLowerCase() };
+ })
+ .filter((token): token is AutomationSearchToken => Boolean(token));
+}
+
+function trimAutomationSearchValue(value: string): string {
+ return value.trim().replace(/^"|"$/g, "").trim();
+}
+
+function automationMatchesSearch(job: SessionAutomationJob, tokens: AutomationSearchToken[]): boolean {
+ return tokens.every((token) => automationSearchText(job, token.field).includes(token.value));
+}
+
+function automationSearchText(job: SessionAutomationJob, field: AutomationSearchField | null = null): string {
+ return automationSearchParts(job, field)
+ .filter(Boolean)
+ .join(" ")
+ .toLowerCase();
+}
+
+function automationSearchParts(
+ job: SessionAutomationJob,
+ field: AutomationSearchField | null,
+): Array {
+ const originParts = automationOriginSearchParts(job);
+ const scheduleParts = automationScheduleSearchParts(job);
+ if (field === "id") return [job.id];
+ if (field === "name") return [job.name, job.id];
+ if (field === "message") return [job.payload.message];
+ if (field === "chat") return originParts;
+ if (field === "cron" || field === "schedule") return scheduleParts;
+ if (field === "status") return [automationStatusKey(job), job.enabled ? "enabled" : "disabled"];
return [
job.id,
job.name,
job.payload.message,
- job.schedule.kind,
- job.schedule.expr,
- job.schedule.tz,
- ...originText,
- ]
- .filter(Boolean)
- .join(" ")
- .toLowerCase();
+ ...scheduleParts,
+ automationStatusKey(job),
+ ...originParts,
+ ];
+}
+
+function automationOriginSearchParts(job: SessionAutomationJob): Array {
+ const origin = job.origin;
+ if (!origin) return [];
+ const channel = origin.channel.trim().toLowerCase();
+ return [
+ origin.session_key,
+ origin.title,
+ origin.preview,
+ origin.channel,
+ AUTOMATION_CHANNEL_LABELS[channel],
+ ];
+}
+
+function automationScheduleSearchParts(job: SessionAutomationJob): Array {
+ const schedule = job.schedule;
+ const parts: Array = [
+ schedule.kind,
+ schedule.expr,
+ schedule.tz,
+ schedule.every_ms,
+ schedule.at_ms,
+ ];
+ if (schedule.kind === "cron" && schedule.expr) {
+ parts.push(...automationCronSearchParts(schedule.expr));
+ }
+ return parts;
+}
+
+function automationCronSearchParts(expr: string): string[] {
+ const parts = expr.trim().split(/\s+/);
+ if (parts.length !== 5) return [];
+ const [minute, hour, dayOfMonth, month, dayOfWeek] = parts;
+ const everyDay = dayOfMonth === "*" && month === "*" && dayOfWeek === "*";
+ const numericMinute = cronNumericToken(minute, 59);
+ const numericHour = cronNumericToken(hour, 23);
+ if (numericMinute === null) return [];
+ const paddedMinute = String(numericMinute).padStart(2, "0");
+
+ if (numericHour !== null) {
+ const time = `${String(numericHour).padStart(2, "0")}:${paddedMinute}`;
+ return [time, `:${paddedMinute}`];
+ }
+
+ if (everyDay && hour === "*") {
+ return [`:${paddedMinute}`, `hourly at :${paddedMinute}`];
+ }
+
+ const range = /^(\d{1,2})-(\d{1,2})$/.exec(hour);
+ if (!everyDay || !range) return [];
+ const start = Number(range[1]);
+ const end = Number(range[2]);
+ if (start > 23 || end > 23) return [];
+ const paddedRange = `${String(start).padStart(2, "0")}-${String(end).padStart(2, "0")}`;
+ const rawRange = `${start}-${end}`;
+ return [
+ paddedRange,
+ rawRange,
+ `:${paddedMinute}`,
+ `${paddedRange} at :${paddedMinute}`,
+ `hourly ${paddedRange} at :${paddedMinute}`,
+ ];
}
function automationMatchesFilter(job: SessionAutomationJob, filter: AutomationFilter): boolean {
@@ -4431,25 +4563,8 @@ function automationChannelLabel(
tx: (key: string, fallback: string, values?: Record) => string,
): string {
const key = channel.trim().toLowerCase();
- const labels: Record = {
- api: "API",
- cli: "CLI",
- dingtalk: "DingTalk",
- discord: "Discord",
- email: "Email",
- feishu: "Feishu",
- matrix: "Matrix",
- msteams: "Microsoft Teams",
- qq: "QQ",
- slack: "Slack",
- telegram: "Telegram",
- wechat: "WeChat",
- wecom: "WeCom",
- weixin: "WeChat",
- whatsapp: "WhatsApp",
- };
- return labels[key]
- ? tx(`settings.automations.channels.${key}`, labels[key])
+ return AUTOMATION_CHANNEL_LABELS[key]
+ ? tx(`settings.automations.channels.${key}`, AUTOMATION_CHANNEL_LABELS[key])
: channel;
}
diff --git a/webui/src/i18n/locales/en/common.json b/webui/src/i18n/locales/en/common.json
index 47d2bb25..125f2e9d 100644
--- a/webui/src/i18n/locales/en/common.json
+++ b/webui/src/i18n/locales/en/common.json
@@ -493,7 +493,7 @@
"updated": "Updated",
"name": "Name"
},
- "search": "Search automations",
+ "search": "name:quiz chat:WeChat cron:09-23",
"queue": "Queue",
"loading": "Loading automations...",
"noMatches": "No automations match this view.",
diff --git a/webui/src/i18n/locales/es/common.json b/webui/src/i18n/locales/es/common.json
index 94c6bf05..ce69a2d4 100644
--- a/webui/src/i18n/locales/es/common.json
+++ b/webui/src/i18n/locales/es/common.json
@@ -493,7 +493,7 @@
"updated": "Actualizada",
"name": "Nombre"
},
- "search": "Buscar automatizaciones",
+ "search": "name:quiz chat:WeChat cron:09-23",
"queue": "Cola",
"loading": "Cargando automatizaciones...",
"noMatches": "No hay automatizaciones que coincidan con esta vista.",
diff --git a/webui/src/i18n/locales/fr/common.json b/webui/src/i18n/locales/fr/common.json
index 7519eef3..0f02183e 100644
--- a/webui/src/i18n/locales/fr/common.json
+++ b/webui/src/i18n/locales/fr/common.json
@@ -493,7 +493,7 @@
"updated": "Mise à jour",
"name": "Nom"
},
- "search": "Rechercher des automatisations",
+ "search": "name:quiz chat:WeChat cron:09-23",
"queue": "File",
"loading": "Chargement des automatisations...",
"noMatches": "Aucune automatisation ne correspond à cette vue.",
diff --git a/webui/src/i18n/locales/id/common.json b/webui/src/i18n/locales/id/common.json
index 43cc0d8f..358b325f 100644
--- a/webui/src/i18n/locales/id/common.json
+++ b/webui/src/i18n/locales/id/common.json
@@ -493,7 +493,7 @@
"updated": "Diperbarui",
"name": "Nama"
},
- "search": "Cari otomatisasi",
+ "search": "name:quiz chat:WeChat cron:09-23",
"queue": "Antrean",
"loading": "Memuat otomasi...",
"noMatches": "Tidak ada otomasi yang cocok dengan tampilan ini.",
diff --git a/webui/src/i18n/locales/ja/common.json b/webui/src/i18n/locales/ja/common.json
index d530c2a6..60b4e00c 100644
--- a/webui/src/i18n/locales/ja/common.json
+++ b/webui/src/i18n/locales/ja/common.json
@@ -493,7 +493,7 @@
"updated": "更新日時",
"name": "名前"
},
- "search": "自動化を検索",
+ "search": "name:quiz chat:WeChat cron:09-23",
"queue": "キュー",
"loading": "自動タスクを読み込み中...",
"noMatches": "この表示に一致する自動タスクはありません。",
diff --git a/webui/src/i18n/locales/ko/common.json b/webui/src/i18n/locales/ko/common.json
index 8e3ac4ee..e2fbf801 100644
--- a/webui/src/i18n/locales/ko/common.json
+++ b/webui/src/i18n/locales/ko/common.json
@@ -493,7 +493,7 @@
"updated": "업데이트",
"name": "이름"
},
- "search": "자동화 검색",
+ "search": "name:quiz chat:WeChat cron:09-23",
"queue": "대기열",
"loading": "자동화를 불러오는 중...",
"noMatches": "이 보기와 일치하는 자동화가 없습니다.",
diff --git a/webui/src/i18n/locales/vi/common.json b/webui/src/i18n/locales/vi/common.json
index 888234d3..e2daf0bb 100644
--- a/webui/src/i18n/locales/vi/common.json
+++ b/webui/src/i18n/locales/vi/common.json
@@ -493,7 +493,7 @@
"updated": "Đã cập nhật",
"name": "Tên"
},
- "search": "Tìm tự động hóa",
+ "search": "name:quiz chat:WeChat cron:09-23",
"queue": "Hàng đợi",
"loading": "Đang tải tự động hóa...",
"noMatches": "Không có tự động hóa phù hợp với chế độ xem này.",
diff --git a/webui/src/i18n/locales/zh-CN/common.json b/webui/src/i18n/locales/zh-CN/common.json
index 79828a17..e683c360 100644
--- a/webui/src/i18n/locales/zh-CN/common.json
+++ b/webui/src/i18n/locales/zh-CN/common.json
@@ -493,7 +493,7 @@
"updated": "更新时间",
"name": "名称"
},
- "search": "搜索自动任务",
+ "search": "name:quiz chat:WeChat cron:09-23",
"queue": "任务队列",
"loading": "正在加载自动任务...",
"noMatches": "当前视图没有匹配的自动任务。",
diff --git a/webui/src/i18n/locales/zh-TW/common.json b/webui/src/i18n/locales/zh-TW/common.json
index a114bf44..925561b4 100644
--- a/webui/src/i18n/locales/zh-TW/common.json
+++ b/webui/src/i18n/locales/zh-TW/common.json
@@ -493,7 +493,7 @@
"updated": "更新時間",
"name": "名稱"
},
- "search": "搜尋自動任務",
+ "search": "name:quiz chat:WeChat cron:09-23",
"queue": "任務佇列",
"loading": "正在載入自動任務...",
"noMatches": "目前檢視沒有符合的自動任務。",
diff --git a/webui/src/tests/app-layout.test.tsx b/webui/src/tests/app-layout.test.tsx
index 46657c28..229baa1f 100644
--- a/webui/src/tests/app-layout.test.tsx
+++ b/webui/src/tests/app-layout.test.tsx
@@ -393,7 +393,7 @@ describe("App layout", () => {
enabled: true,
protected: false,
delete_after_run: false,
- schedule: { kind: "every", every_ms: 3_600_000 },
+ schedule: { kind: "cron", expr: "30 9-23 * * *", tz: "Asia/Shanghai" },
payload: {
message: "Send a quiz",
kind: "agent_turn",
@@ -452,6 +452,17 @@ describe("App layout", () => {
"page",
);
expect(document.title).toBe("Automations · nanobot");
+
+ const searchInput = within(automationsMain as HTMLElement).getByPlaceholderText(
+ "name:quiz chat:WeChat cron:09-23",
+ );
+ fireEvent.change(searchInput, { target: { value: "chat:WeChat" } });
+ await waitFor(() => expect(screen.queryByText("Daily repo check")).not.toBeInTheDocument());
+ expect(screen.getAllByText("WeChat quiz").length).toBeGreaterThanOrEqual(1);
+
+ fireEvent.change(searchInput, { target: { value: "cron:09-23" } });
+ await waitFor(() => expect(screen.queryByText("Daily repo check")).not.toBeInTheDocument());
+ expect(screen.getAllByText("WeChat quiz").length).toBeGreaterThanOrEqual(1);
});
it("edits a past one-time automation without resubmitting its old schedule", async () => {
From 23e12b84ff7589237738a48e8224cb4c6869ba80 Mon Sep 17 00:00:00 2001
From: chengyongru
Date: Tue, 16 Jun 2026 09:48:53 +0800
Subject: [PATCH 27/38] fix(webui): clarify automation search placeholder
---
webui/src/components/settings/SettingsView.tsx | 5 ++++-
webui/src/i18n/locales/en/common.json | 4 ++--
webui/src/i18n/locales/es/common.json | 4 ++--
webui/src/i18n/locales/fr/common.json | 4 ++--
webui/src/i18n/locales/id/common.json | 4 ++--
webui/src/i18n/locales/ja/common.json | 4 ++--
webui/src/i18n/locales/ko/common.json | 4 ++--
webui/src/i18n/locales/vi/common.json | 4 ++--
webui/src/i18n/locales/zh-CN/common.json | 4 ++--
webui/src/i18n/locales/zh-TW/common.json | 4 ++--
webui/src/tests/app-layout.test.tsx | 6 +++---
11 files changed, 25 insertions(+), 22 deletions(-)
diff --git a/webui/src/components/settings/SettingsView.tsx b/webui/src/components/settings/SettingsView.tsx
index 4db5e2b8..41135579 100644
--- a/webui/src/components/settings/SettingsView.tsx
+++ b/webui/src/components/settings/SettingsView.tsx
@@ -3506,7 +3506,10 @@ function AutomationsSettings({
onQueryChange(event.target.value)}
- placeholder={tx("settings.automations.search", "name:quiz chat:WeChat cron:09-23")}
+ placeholder={tx(
+ "settings.automations.search",
+ "Search task, message, linked chat, or schedule",
+ )}
className="h-9 w-full rounded-[13px] border-border/45 bg-background/85 pl-9 text-[13px] shadow-sm"
/>
diff --git a/webui/src/i18n/locales/en/common.json b/webui/src/i18n/locales/en/common.json
index 125f2e9d..f70110a0 100644
--- a/webui/src/i18n/locales/en/common.json
+++ b/webui/src/i18n/locales/en/common.json
@@ -493,12 +493,12 @@
"updated": "Updated",
"name": "Name"
},
- "search": "name:quiz chat:WeChat cron:09-23",
+ "search": "Search task, message, linked chat, or schedule",
"queue": "Queue",
"loading": "Loading automations...",
"noMatches": "No automations match this view.",
"empty": "No automations yet.",
- "emptyHint": "Create one from the chat or channel where it should run so nanobot keeps the right context.",
+ "emptyHint": "Create one from where it should run so nanobot keeps the right context.",
"oneShot": "One-time",
"systemTask": "System-managed automation",
"labels": {
diff --git a/webui/src/i18n/locales/es/common.json b/webui/src/i18n/locales/es/common.json
index ce69a2d4..c0b93027 100644
--- a/webui/src/i18n/locales/es/common.json
+++ b/webui/src/i18n/locales/es/common.json
@@ -493,12 +493,12 @@
"updated": "Actualizada",
"name": "Nombre"
},
- "search": "name:quiz chat:WeChat cron:09-23",
+ "search": "Buscar tarea, mensaje, chat vinculado u horario",
"queue": "Cola",
"loading": "Cargando automatizaciones...",
"noMatches": "No hay automatizaciones que coincidan con esta vista.",
"empty": "Aún no hay automatizaciones.",
- "emptyHint": "Crea una desde el chat o canal donde debe ejecutarse para que nanobot conserve el contexto correcto.",
+ "emptyHint": "Créala desde donde debe ejecutarse para que nanobot conserve el contexto correcto.",
"oneShot": "Una vez",
"systemTask": "Automatización administrada por el sistema",
"labels": {
diff --git a/webui/src/i18n/locales/fr/common.json b/webui/src/i18n/locales/fr/common.json
index 0f02183e..e8922499 100644
--- a/webui/src/i18n/locales/fr/common.json
+++ b/webui/src/i18n/locales/fr/common.json
@@ -493,12 +493,12 @@
"updated": "Mise à jour",
"name": "Nom"
},
- "search": "name:quiz chat:WeChat cron:09-23",
+ "search": "Rechercher tâche, message, discussion liée ou planning",
"queue": "File",
"loading": "Chargement des automatisations...",
"noMatches": "Aucune automatisation ne correspond à cette vue.",
"empty": "Aucune automatisation pour le moment.",
- "emptyHint": "Créez-en une depuis la discussion ou le canal où elle doit s’exécuter afin que nanobot conserve le bon contexte.",
+ "emptyHint": "Créez-la depuis son point d'exécution pour que nanobot conserve le bon contexte.",
"oneShot": "Ponctuelle",
"systemTask": "Automatisation gérée par le système",
"labels": {
diff --git a/webui/src/i18n/locales/id/common.json b/webui/src/i18n/locales/id/common.json
index 358b325f..aebc5171 100644
--- a/webui/src/i18n/locales/id/common.json
+++ b/webui/src/i18n/locales/id/common.json
@@ -493,12 +493,12 @@
"updated": "Diperbarui",
"name": "Nama"
},
- "search": "name:quiz chat:WeChat cron:09-23",
+ "search": "Cari tugas, pesan, chat terkait, atau jadwal",
"queue": "Antrean",
"loading": "Memuat otomasi...",
"noMatches": "Tidak ada otomasi yang cocok dengan tampilan ini.",
"empty": "Belum ada otomasi.",
- "emptyHint": "Buat dari chat atau channel tempat otomasi akan berjalan agar nanobot menyimpan konteks yang benar.",
+ "emptyHint": "Buat dari tempat tugas ini berjalan agar nanobot menyimpan konteks yang tepat.",
"oneShot": "Satu kali",
"systemTask": "Automasi yang dikelola sistem",
"labels": {
diff --git a/webui/src/i18n/locales/ja/common.json b/webui/src/i18n/locales/ja/common.json
index 60b4e00c..cd0628e8 100644
--- a/webui/src/i18n/locales/ja/common.json
+++ b/webui/src/i18n/locales/ja/common.json
@@ -493,12 +493,12 @@
"updated": "更新日時",
"name": "名前"
},
- "search": "name:quiz chat:WeChat cron:09-23",
+ "search": "タスク、メッセージ、関連チャット、予定を検索",
"queue": "キュー",
"loading": "自動タスクを読み込み中...",
"noMatches": "この表示に一致する自動タスクはありません。",
"empty": "自動タスクはまだありません。",
- "emptyHint": "実行先のチャットまたは外部 channel から作成すると、nanobot が正しいコンテキストを保持できます。",
+ "emptyHint": "実行元から作成すると、nanobot が正しいコンテキストを保持できます。",
"oneShot": "一回限り",
"systemTask": "システム管理の自動タスク",
"labels": {
diff --git a/webui/src/i18n/locales/ko/common.json b/webui/src/i18n/locales/ko/common.json
index e2fbf801..d91bc188 100644
--- a/webui/src/i18n/locales/ko/common.json
+++ b/webui/src/i18n/locales/ko/common.json
@@ -493,12 +493,12 @@
"updated": "업데이트",
"name": "이름"
},
- "search": "name:quiz chat:WeChat cron:09-23",
+ "search": "작업, 메시지, 연결된 채팅 또는 일정 검색",
"queue": "대기열",
"loading": "자동화를 불러오는 중...",
"noMatches": "이 보기와 일치하는 자동화가 없습니다.",
"empty": "아직 자동화가 없습니다.",
- "emptyHint": "실행되어야 하는 채팅 또는 외부 channel에서 만들면 nanobot이 올바른 컨텍스트를 유지합니다.",
+ "emptyHint": "실행될 위치에서 만들면 nanobot이 올바른 컨텍스트를 유지합니다.",
"oneShot": "일회성",
"systemTask": "시스템 관리 자동화",
"labels": {
diff --git a/webui/src/i18n/locales/vi/common.json b/webui/src/i18n/locales/vi/common.json
index e2daf0bb..8edee166 100644
--- a/webui/src/i18n/locales/vi/common.json
+++ b/webui/src/i18n/locales/vi/common.json
@@ -493,12 +493,12 @@
"updated": "Đã cập nhật",
"name": "Tên"
},
- "search": "name:quiz chat:WeChat cron:09-23",
+ "search": "Tìm tác vụ, tin nhắn, cuộc trò chuyện liên kết hoặc lịch",
"queue": "Hàng đợi",
"loading": "Đang tải tự động hóa...",
"noMatches": "Không có tự động hóa phù hợp với chế độ xem này.",
"empty": "Chưa có tự động hóa.",
- "emptyHint": "Tạo từ cuộc trò chuyện hoặc channel nơi tự động hóa sẽ chạy để nanobot giữ đúng ngữ cảnh.",
+ "emptyHint": "Tạo từ nơi tác vụ sẽ chạy để nanobot giữ đúng ngữ cảnh.",
"oneShot": "Một lần",
"systemTask": "Tự động hóa do hệ thống quản lý",
"labels": {
diff --git a/webui/src/i18n/locales/zh-CN/common.json b/webui/src/i18n/locales/zh-CN/common.json
index e683c360..cce6f4ac 100644
--- a/webui/src/i18n/locales/zh-CN/common.json
+++ b/webui/src/i18n/locales/zh-CN/common.json
@@ -493,12 +493,12 @@
"updated": "更新时间",
"name": "名称"
},
- "search": "name:quiz chat:WeChat cron:09-23",
+ "search": "搜索任务、消息、关联会话或计划",
"queue": "任务队列",
"loading": "正在加载自动任务...",
"noMatches": "当前视图没有匹配的自动任务。",
"empty": "暂无自动任务。",
- "emptyHint": "请从它应该运行的聊天或外部 channel 中创建,这样 nanobot 才能保留正确上下文。",
+ "emptyHint": "请从它应该运行的来源处创建,这样 nanobot 才能保留正确上下文。",
"oneShot": "一次性",
"systemTask": "系统管理的自动任务",
"labels": {
diff --git a/webui/src/i18n/locales/zh-TW/common.json b/webui/src/i18n/locales/zh-TW/common.json
index 925561b4..9aaf6f86 100644
--- a/webui/src/i18n/locales/zh-TW/common.json
+++ b/webui/src/i18n/locales/zh-TW/common.json
@@ -493,12 +493,12 @@
"updated": "更新時間",
"name": "名稱"
},
- "search": "name:quiz chat:WeChat cron:09-23",
+ "search": "搜尋任務、訊息、關聯對話或排程",
"queue": "任務佇列",
"loading": "正在載入自動任務...",
"noMatches": "目前檢視沒有符合的自動任務。",
"empty": "尚無自動任務。",
- "emptyHint": "請從它應該執行的聊天或外部 channel 中建立,這樣 nanobot 才能保留正確上下文。",
+ "emptyHint": "請從它應該執行的來源處建立,這樣 nanobot 才能保留正確上下文。",
"oneShot": "一次性",
"systemTask": "系統管理的自動任務",
"labels": {
diff --git a/webui/src/tests/app-layout.test.tsx b/webui/src/tests/app-layout.test.tsx
index 229baa1f..c4c93c92 100644
--- a/webui/src/tests/app-layout.test.tsx
+++ b/webui/src/tests/app-layout.test.tsx
@@ -454,13 +454,13 @@ describe("App layout", () => {
expect(document.title).toBe("Automations · nanobot");
const searchInput = within(automationsMain as HTMLElement).getByPlaceholderText(
- "name:quiz chat:WeChat cron:09-23",
+ "Search task, message, linked chat, or schedule",
);
- fireEvent.change(searchInput, { target: { value: "chat:WeChat" } });
+ fireEvent.change(searchInput, { target: { value: "WeChat" } });
await waitFor(() => expect(screen.queryByText("Daily repo check")).not.toBeInTheDocument());
expect(screen.getAllByText("WeChat quiz").length).toBeGreaterThanOrEqual(1);
- fireEvent.change(searchInput, { target: { value: "cron:09-23" } });
+ fireEvent.change(searchInput, { target: { value: "09-23" } });
await waitFor(() => expect(screen.queryByText("Daily repo check")).not.toBeInTheDocument());
expect(screen.getAllByText("WeChat quiz").length).toBeGreaterThanOrEqual(1);
});
From 04387bf9e3aec585b79b0a0ca2a70b6c85aa8a6f Mon Sep 17 00:00:00 2001
From: chengyongru
Date: Tue, 16 Jun 2026 10:08:50 +0800
Subject: [PATCH 28/38] fix(webui): keep automation workspace within viewport
---
.../src/components/settings/SettingsView.tsx | 51 ++++++++++++-------
1 file changed, 32 insertions(+), 19 deletions(-)
diff --git a/webui/src/components/settings/SettingsView.tsx b/webui/src/components/settings/SettingsView.tsx
index 41135579..cd53aa80 100644
--- a/webui/src/components/settings/SettingsView.tsx
+++ b/webui/src/components/settings/SettingsView.tsx
@@ -1712,15 +1712,22 @@ export function SettingsView({
onSave={handleAutomationEdit}
/>
-
+
-
+
{!showSidebar ? (
) : settings ? (
-
+
{error ? (
{error}
@@ -3476,8 +3489,8 @@ function AutomationsSettings({
}, [filtered, selectedJobId]);
return (
-
-
+
+
@@ -3550,9 +3563,9 @@ function AutomationsSettings({
{tx("settings.automations.loading", "Loading automations...")}
) : filtered.length && selectedJob ? (
-
-
-
+
+
+
{tx("settings.automations.queue", "Queue")}
@@ -3561,7 +3574,7 @@ function AutomationsSettings({
@@ -3596,7 +3609,7 @@ function AutomationsSettings({
{tx(
"settings.automations.emptyHint",
- "Create one from the chat or channel where it should run so nanobot keeps the right context.",
+ "Create one from where it should run so nanobot keeps the right context.",
)}
) : null}
@@ -3715,9 +3728,9 @@ function AutomationDetailPanel({
}, [job.id]);
return (
-
-
-
+
+
+
@@ -3742,8 +3755,8 @@ function AutomationDetailPanel({
-
-
+
+
{tx("settings.automations.fields.message", "Message")}
@@ -3798,8 +3811,8 @@ function AutomationDetailPanel({
) : null}
-
-
+
+
-
+
{created ? (
From 201d442a8533f70a62424f90e2028fcadffe19fc Mon Sep 17 00:00:00 2001
From: chengyongru
Date: Tue, 16 Jun 2026 11:07:48 +0800
Subject: [PATCH 29/38] fix(webui): soften automation manager styling
---
.../src/components/settings/SettingsView.tsx | 77 ++++++++++++-------
1 file changed, 51 insertions(+), 26 deletions(-)
diff --git a/webui/src/components/settings/SettingsView.tsx b/webui/src/components/settings/SettingsView.tsx
index cd53aa80..2bf98b5a 100644
--- a/webui/src/components/settings/SettingsView.tsx
+++ b/webui/src/components/settings/SettingsView.tsx
@@ -3490,22 +3490,23 @@ function AutomationsSettings({
return (
-
+
-
+
{summaryOptions.map((option) => (
onFilterChange(option.value)}
className={cn(
- "inline-flex h-8 min-w-0 shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-[12px] px-3 text-[12px] font-medium text-muted-foreground transition-colors",
- filter === option.value && "bg-background text-foreground shadow-sm",
+ "inline-flex h-8 min-w-0 shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-[11px] px-3 text-[12px] font-medium text-muted-foreground transition-colors",
+ filter === option.value &&
+ "bg-white text-orange-950 shadow-[0_8px_20px_rgba(120,72,25,0.1)] dark:bg-background/80 dark:text-orange-100",
)}
>
{option.label}
-
+
{option.count}
@@ -3523,14 +3524,14 @@ function AutomationsSettings({
"settings.automations.search",
"Search task, message, linked chat, or schedule",
)}
- className="h-9 w-full rounded-[13px] border-border/45 bg-background/85 pl-9 text-[13px] shadow-sm"
+ className="h-9 w-full rounded-[13px] border-orange-200/35 bg-background/82 pl-9 text-[13px] shadow-[0_8px_22px_rgba(120,72,25,0.055)] focus-visible:ring-orange-300/55 dark:border-orange-300/10 dark:bg-background/40"
/>
{sortLabel[sort]}
@@ -3563,18 +3564,18 @@ function AutomationsSettings({
{tx("settings.automations.loading", "Loading automations...")}
) : filtered.length && selectedJob ? (
-
-
-
+
+
+
{tx("settings.automations.queue", "Queue")}
-
+
{filtered.length}
@@ -3646,8 +3647,8 @@ function AutomationListItem({
className={cn(
"group grid w-full grid-cols-[minmax(0,1fr)_auto] gap-3 rounded-[18px] px-3 py-3.5 text-left transition-colors",
selected
- ? "bg-background text-foreground shadow-sm ring-1 ring-border/45"
- : "text-muted-foreground hover:bg-background/55 hover:text-foreground",
+ ? "bg-white/82 text-foreground shadow-[0_10px_28px_rgba(120,72,25,0.08)] ring-1 ring-orange-200/45 dark:bg-background/45 dark:ring-orange-300/12"
+ : "text-muted-foreground hover:bg-white/48 hover:text-foreground dark:hover:bg-background/24",
)}
>
@@ -3672,7 +3673,7 @@ function AutomationListItem({
-
+
{status.label}
{job.delete_after_run ? (
@@ -3728,17 +3729,17 @@ function AutomationDetailPanel({
}, [job.id]);
return (
-
-
+
+
{job.name || job.id}
-
{status.label}
+
{status.label}
{job.delete_after_run ? (
-
{tx("settings.automations.oneShot", "One-time")}
+
{tx("settings.automations.oneShot", "One-time")}
) : null}
@@ -3757,7 +3758,7 @@ function AutomationDetailPanel({