diff --git a/apps/web/src/components/CompanionDiscoveryHub.test.mjs b/apps/web/src/components/CompanionDiscoveryHub.test.mjs index d8445881..101c30ec 100644 --- a/apps/web/src/components/CompanionDiscoveryHub.test.mjs +++ b/apps/web/src/components/CompanionDiscoveryHub.test.mjs @@ -31,7 +31,7 @@ describe("quiet discovery UI", () => { }); test("Paw mode explains benefits and exposes its inactive state without personalization controls", async () => { const html = await render(false, "zh-CN", true); - expect(html).toContain("开启后,EdgeEver 会像猫爪轻推纸片一样,帮你归拢零散碎片、将新想法沉淀到已有笔记、挑出过往的相关灵感。只在有价值时轻量提醒,改动完全由你决定。"); + expect(html).toContain("开启后,EdgeEver 会像猫爪轻推纸片一样,帮你归拢零散碎片、将新想法沉淀到已有笔记、挑出过往的相关灵感。"); expect(html).toContain("用得越久,猫爪越懂你。"); expect(html).toContain("猫爪模式"); expect(html).toContain("猫爪状态"); expect(html).toContain("未运行"); @@ -53,7 +53,7 @@ describe("quiet discovery UI", () => { }); test("English settings have matching concise benefits and no raw translation keys", async () => { const html = await render(false, "en-US", true); - expect(html).toContain("When enabled, EdgeEver quietly helps tuck scattered fragments together, append new ideas to existing notes, and surface relevant past insights like a gentle cat paw. It nudges you only when valuable, and all changes remain entirely up to you."); + expect(html).toContain("When enabled, EdgeEver quietly helps tuck scattered fragments together, append new ideas to existing notes, and surface relevant past insights like a gentle cat paw."); expect(html).toContain("The longer you use it, the better Paw mode understands you."); expect(html).toContain("Paw status"); expect(html).toContain("Not running"); expect(html).not.toContain("Personal preferences"); expect(html).not.toContain("one minute"); diff --git a/apps/web/src/components/settings/CompanionDiscoverySettingsCard.tsx b/apps/web/src/components/settings/CompanionDiscoverySettingsCard.tsx index 0e7b49ac..4785ae83 100644 --- a/apps/web/src/components/settings/CompanionDiscoverySettingsCard.tsx +++ b/apps/web/src/components/settings/CompanionDiscoverySettingsCard.tsx @@ -1,8 +1,8 @@ import { useState } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useTranslation } from "react-i18next"; +import { Sparkles, ShieldCheck, ChevronRight, Clock, Activity, MessageSquareText } from "lucide-react"; import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; import { Switch } from "@/components/ui/switch"; import { api } from "@/lib/api"; import { discoveryFeedKey, discoverySettingsKey, useCompanionDiscoverySettings } from "@/hooks/useCompanionDiscovery"; @@ -17,67 +17,207 @@ export function CompanionDiscoverySettingsCard({ scope, onOpenCompanion }: { const [error, setError] = useState(false); const settings = query.data; const enabled = settings?.enabled === true; - const feed = useQuery({ queryKey: discoveryFeedKey(scope), queryFn: async () => (await api.listCompanionDiscoveries()).items, - enabled, staleTime: 60_000, retry: false }); + const feed = useQuery({ + queryKey: discoveryFeedKey(scope), + queryFn: async () => (await api.listCompanionDiscoveries()).items, + enabled, + staleTime: 60_000, + retry: false, + }); const latest = feed.data?.[0]; const formatTime = (value: string) => new Intl.DateTimeFormat(i18n.resolvedLanguage ?? "en-US", { - dateStyle: "medium", timeStyle: "short", + dateStyle: "medium", + timeStyle: "short", }).format(new Date(value)); + const save = async (enabled: boolean) => { if (!settings || busy) return; - setBusy(true); setError(false); + setBusy(true); + setError(false); try { const result = await api.saveCompanionDiscoverySettings({ enabled, version: settings.version }); client.setQueryData(discoverySettingsKey(scope), result.settings); client.setQueryData(discoveryFeedKey(scope), []); - } catch { setError(true); await query.refetch(); } - finally { setBusy(false); } + } catch { + setError(true); + await query.refetch(); + } finally { + setBusy(false); + } }; - return - - {t("companion.discovery.settingsTitle")} - {t("companion.discovery.description")} -

{t("companion.discovery.tagline")}

-
- -
- - void save(enabled)} /> -
- {error || query.isError ?

{t("companion.discovery.settingsFailed")}

: null} -
-
-

{t("companion.discovery.transparency.title")}

- - - {t(`companion.discovery.transparency.state.${enabled ? settings?.lastStatus ?? "quiet" : "disabled"}`)} - -
-
-
{t("companion.discovery.transparency.lastCheck")}
-
{settings?.lastCheckAt ? formatTime(settings.lastCheckAt) : t("companion.discovery.transparency.neverChecked")}
-
{t("companion.discovery.transparency.nextCheck")}
-
{t(enabled ? "companion.discovery.transparency.nextCheckEnabled" : "companion.discovery.transparency.nextCheckDisabled")}
-
-

{t("companion.discovery.transparency.scope")}

-
- {enabled ?
-

{t("companion.discovery.transparency.latest")}

- {feed.isPending ?

{t("common.loading")}

: null} - {feed.isError ?

{t("companion.discovery.loadFailed")}

: null} - {!feed.isPending && !feed.isError && !latest ?

{t("companion.discovery.transparency.noDiscovery")}

: null} - {latest ?
-
-

{t(`companion.discovery.kind.${latest.kind}`)}

- + + return ( + + +
+
+
+ + {t("companion.discovery.settingsTitle")} +
+ + {t("companion.discovery.description")} +
-

{latest.title}

-

{latest.body}

-
: null} -
: null} - -
-
; +
+ + void save(enabled)} + /> +
+ + +
+ +

{t("companion.discovery.tagline")}

+
+ + + + {error || query.isError ? ( +

+ {t("companion.discovery.settingsFailed")} +

+ ) : null} + +
+
+

+ + {t("companion.discovery.transparency.title")} +

+ + + {t(`companion.discovery.transparency.state.${enabled ? settings?.lastStatus ?? "quiet" : "disabled"}`)} + +
+ +
+
+
+ + {t("companion.discovery.transparency.lastCheck")} +
+
+ {settings?.lastCheckAt ? formatTime(settings.lastCheckAt) : t("companion.discovery.transparency.neverChecked")} +
+
+
+
+ + {t("companion.discovery.transparency.nextCheck")} +
+
+ {t(enabled ? "companion.discovery.transparency.nextCheckEnabled" : "companion.discovery.transparency.nextCheckDisabled")} +
+
+
+ +
+ +

+ {t("companion.discovery.transparency.scope")} +

+
+
+ + {enabled ? ( +
+

+ {t("companion.discovery.transparency.latest")} +

+ {feed.isPending ?

{t("common.loading")}

: null} + {feed.isError ?

{t("companion.discovery.loadFailed")}

: null} + {!feed.isPending && !feed.isError && !latest ? ( +
+
+ +
+

+ {t("companion.discovery.transparency.noDiscovery")} +

+
+ ) : null} + {latest ? ( +
+
+ + {t(`companion.discovery.kind.${latest.kind}`)} + + +
+

{latest.title}

+

+ {latest.body} +

+
+ ) : null} +
+ ) : null} + +
+ +
+
+ + ); } diff --git a/docs/personality-memory-agent.md b/docs/personality-memory-agent.md index 731a0925..3a23d9f3 100644 --- a/docs/personality-memory-agent.md +++ b/docs/personality-memory-agent.md @@ -22,7 +22,7 @@ The main flow is: normal note-taking → the Agent identifies an opportunity at The settings page starts with a short benefit description, then makes the background behavior visible without adding configuration burden. A read-only status panel shows whether Paw mode is idle, checking, ready, or failed; the last check time; the conditions for another check; the data scope and confirmation boundary; and the latest generated discovery when one exists: -> When enabled, EdgeEver quietly helps tuck scattered fragments together, append new ideas to existing notes, and surface relevant past insights like a gentle cat paw. It nudges you only when valuable, and all changes remain entirely up to you. +> When enabled, EdgeEver quietly helps tuck scattered fragments together, append new ideas to existing notes, and surface relevant past insights like a gentle cat paw. > > The longer you use it, the better Paw mode understands you. diff --git a/docs/personality-memory-agent.zh-CN.md b/docs/personality-memory-agent.zh-CN.md index 4595cab7..b9deac64 100644 --- a/docs/personality-memory-agent.zh-CN.md +++ b/docs/personality-memory-agent.zh-CN.md @@ -22,7 +22,7 @@ Agent 是围绕用户知识工作的长期助手,不是一个熟悉笔记的 设置页以简短用途介绍开头,再以只读状态呈现后台行为,不增加配置负担。状态面板展示猫爪处于安静观察、检查中、发现就绪或检查失败,最近检查时间、下次检查条件、数据范围与确认边界;存在生成结果时还展示最近一条发现: -> 开启后,EdgeEver 会像猫爪轻推纸片一样,帮你归拢零散碎片、将新想法沉淀到已有笔记、挑出过往的相关灵感。只在有价值时轻量提醒,改动完全由你决定。 +> 开启后,EdgeEver 会像猫爪轻推纸片一样,帮你归拢零散碎片、将新想法沉淀到已有笔记、挑出过往的相关灵感。 > > 用得越久,猫爪越懂你。 diff --git a/packages/shared/src/i18n/en-US.ts b/packages/shared/src/i18n/en-US.ts index 2ea430a5..7de713cb 100644 --- a/packages/shared/src/i18n/en-US.ts +++ b/packages/shared/src/i18n/en-US.ts @@ -6,11 +6,12 @@ export const enUS = { discovery: { settingsTitle: "Paw mode", title: "Discoveries from EdgeEver", - description: "When enabled, EdgeEver quietly helps tuck scattered fragments together, append new ideas to existing notes, and surface relevant past insights like a gentle cat paw. It nudges you only when valuable, and all changes remain entirely up to you.", + description: "When enabled, EdgeEver quietly helps tuck scattered fragments together, append new ideas to existing notes, and surface relevant past insights like a gentle cat paw.", tagline: "The longer you use it, the better Paw mode understands you.", enable: "Enable Paw mode", settingsFailed: "Settings were not saved. Check your connection or refresh and retry.", openCompanion: "Conversation and personal memory", + openCompanionDesc: "Manage memories and context preferences Paw mode learns from conversations.", panelDescription: "No need to process every item. Only approved operations change notes; discoveries disappear after 7 days.", unread: "New discoveries", empty: "Nothing worth interrupting you for yet. Keep capturing your ideas.", diff --git a/packages/shared/src/i18n/zh-CN.ts b/packages/shared/src/i18n/zh-CN.ts index 06abbcad..21a44168 100644 --- a/packages/shared/src/i18n/zh-CN.ts +++ b/packages/shared/src/i18n/zh-CN.ts @@ -6,11 +6,12 @@ export const zhCN = { discovery: { settingsTitle: "猫爪模式", title: "来自 EdgeEver 的发现", - description: "开启后,EdgeEver 会像猫爪轻推纸片一样,帮你归拢零散碎片、将新想法沉淀到已有笔记、挑出过往的相关灵感。只在有价值时轻量提醒,改动完全由你决定。", + description: "开启后,EdgeEver 会像猫爪轻推纸片一样,帮你归拢零散碎片、将新想法沉淀到已有笔记、挑出过往的相关灵感。", tagline: "用得越久,猫爪越懂你。", enable: "启用猫爪模式", settingsFailed: "设置未保存。请检查连接或刷新后重试。", openCompanion: "对话与个人记忆", + openCompanionDesc: "管理猫爪在日常对话中沉淀的记忆与上下文偏好。", panelDescription: "不需要逐条处理。只有你确认的操作才会改变笔记;发现 7 天后自动隐藏。", unread: "有新发现", empty: "暂时没有值得打扰你的发现。继续记录就好。",