cortico-world-screen-diary 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0
4
+
5
+ - 二维状态空间:技术轴(重度/轻度)× 办公轴(工作/娱乐),应用先投影再 EMA 平滑
6
+ - 隐私过滤:通讯类应用直接过滤;AI 对话默认模糊为中性,可配置
7
+ - 细节遗忘:标题/速写/正文只保留 `detailHours`,之后只留平滑状态
8
+ - 工具改为 `diary_state`、`diary_recent`、`diary_today`(半小时分桶)、`diary_search`(显式回忆)、`diary_stats`
9
+ - 面板改为二维轨迹图 + 当前象限 + 短期细节
3
10
  ## 0.1.0
4
11
 
5
12
  - 第一个版本:消费窥屏助手的事件流,整理成本地活动日记
package/README.md CHANGED
@@ -4,55 +4,74 @@ Owner: `src/index.ts`
4
4
 
5
5
  [![CI](https://github.com/Wwhois163wW/cortico-world-screen-diary/actions/workflows/ci.yml/badge.svg)](https://github.com/Wwhois163wW/cortico-world-screen-diary/actions/workflows/ci.yml)
6
6
 
7
- **活动日记**是一个**消费方 World**:它不截屏、不读窗口,只读另一个 World([窥屏助手](https://www.npmjs.com/package/cortico-world-screen-sense))写进事件流的前台切换与视觉速写,整理成本地的时间线、搜索与统计。
7
+ **活动日记**是一个**消费方 World**:它不截屏、不读窗口,只读另一个 World([窥屏助手](https://www.npmjs.com/package/cortico-world-screen-sense))写进事件流的前台切换,把工作状态投影到二维空间并做平滑;窗口标题只短期保留,之后只剩“大概在干什么”。
8
8
 
9
- 这是“World 依赖事件流”的 PoC:生产方(窥屏助手)不知道本扩展存在;本扩展只依赖事件契约,不依赖它的代码。
9
+ 这是“World 依赖事件流”的 PoC:生产方不知道本扩展存在;本扩展只依赖事件契约,不依赖它的代码。
10
10
 
11
11
  ## 依赖
12
12
 
13
- 先安装并启用 **`cortico-world-screen-sense`(窥屏助手)**,然后在扩展页安装本包:
13
+ 先安装并启用 **`cortico-world-screen-sense`(窥屏助手)**,再安装本包:
14
14
 
15
15
  ```text
16
16
  cortico-world-screen-diary
17
17
  ```
18
18
 
19
- 装完重启 Core,在「World 总览」里启用「活动日记」。如果窥屏助手没启用或还没产生事件,面板会提示等待,不会报错。
19
+ 装完重启 Core,在「World 总览」里启用「活动日记」。生产方没启用或还没事件时,面板会提示等待。
20
20
 
21
- ## 它读什么
21
+ ## 二维状态空间
22
22
 
23
- 通过宿主提供的 `host.store` 增量读取(`source = screen-sense`):
23
+ - **纵轴 技术轴**:重度(开发/折腾)↔ 轻度(看视频/轻办公)
24
+ - **横轴 办公轴**:工作 ↔ 娱乐
24
25
 
25
- - `screen-sense.focus`:`meta = { kind: 'focus', app, title, previousMs }`
26
- - `screen-sense.screenshot`:`meta = { kind: 'screenshot', app, title, photoId, caption }`
26
+ 每个前台应用先投影成 `(tech, work)`,再按 `smoothing` 做 EMA 平滑,得到连续状态与轨迹。窗口标题只用来判断浏览器在哪个象限(github → 开发,Bilibili → 视频),不会长期保存。
27
27
 
28
- 只保存文本与结构化字段;**事件里没有原图**。生产方改名/删除事件类型属于 major,新增字段属于 minor。
28
+ | 类别 | 示例 | tech | work |
29
+ | --- | --- | --- | --- |
30
+ | 开发 | IDE、终端、git、docker、github、npm | +0.8 | +0.6 |
31
+ | 游戏 | Steam、Minecraft、LOL | +0.6 | -0.8 |
32
+ | 文书 | Word/Excel/PDF/Notion | -0.2 | +0.8 |
33
+ | 视频 | bilibili、YouTube、播放器 | -0.5 | -0.8 |
34
+ | 浏览器 | Chrome/Edge | 按标题推断,默认 (0,0) | |
29
35
 
30
- 读取游标保存在本 World 数据目录,重启后从上次位置继续;第一次启动可按 `lookbackMinutes` 回填一段历史。记录按 `keepDays` 保留,超过自动淘汰。
36
+ ## 隐私与遗忘
37
+
38
+ - **通讯类直接过滤**:微信、QQ、Discord、Telegram、Slack、Teams、Zoom、邮件客户端等不投影、不落盘、不进入状态。
39
+ - **AI 对话默认模糊为中性**:不记录具体窗口标题;可在配置里改为额外过滤或正常投影。
40
+ - **细节会淡忘**:窗口标题、速写、正文只保留 `detailHours`(默认 72 小时);之后只剩平滑状态与日摘要,保留 `keepDays`(默认 30 天)。
41
+ - 状态是“大概在干什么”,允许不精确;需要精确回忆时才用显式搜索。
31
42
 
32
43
  ## 工具
33
44
 
34
- | 工具 | 作用 |
45
+ | 工具 | 用途 |
35
46
  | --- | --- |
36
- | `diary_today` | 读某一天(默认今天)的日记,按小时列出前台切换与速写 |
37
- | `diary_search` | 按应用名、窗口标题、速写或正文搜索,新的在前 |
38
- | `diary_stats` | 总条数、覆盖天数、最常出现的应用 |
47
+ | `diary_state` | 当前平滑状态 + 最近 10 个采样;反应类场景默认用它 |
48
+ | `diary_recent` | 最近 N 个平滑采样(默认 10) |
49
+ | `diary_today` | 某天按半小时分桶的平滑状态,默认今天 |
50
+ | `diary_search` | **显式回忆**:在短期细节记录里搜应用/标题/速写,最多 5 条 |
51
+ | `diary_stats` | 覆盖天数、状态采样数、主要状态 |
39
52
 
40
53
  ## 配置(`worlds.screen-diary.*`)
41
54
 
42
55
  | 键 | 默认 | 含义 |
43
56
  | --- | --- | --- |
44
- | `enabled` | `false` | 是否轮询事件流(World 总览里启用后生效) |
57
+ | `enabled` | `false` | 是否轮询事件流 |
45
58
  | `pollMs` | `5000` | 检查新事件的间隔 |
46
59
  | `lookbackMinutes` | `60` | 首次启动回填最近多少分钟 |
47
- | `keepDays` | `30` | 日记保留天数 |
60
+ | `keepDays` | `30` | 平滑状态与日摘要保留天数 |
61
+ | `detailHours` | `72` | 细节(标题/速写/正文)保留小时 |
62
+ | `blurAi` | `true` | AI 对话是否模糊为中性 |
63
+ | `ignoreApps` | `""` | 额外过滤的应用名片段 |
64
+ | `smoothing` | `0.25` | EMA 平滑强度,0.05 平滑、0.9 灵敏 |
65
+ | `sampleSec` | `30` | 两次状态采样最小间隔 |
48
66
  | `source` | `screen-sense` | 生产方 World id |
49
- | `maxEventsPerPoll` | `500` | 单次导入上限,超出留给下一拍 |
67
+ | `maxEventsPerPoll` | `500` | 单次导入上限 |
50
68
 
51
69
  ## 隐私
52
70
 
53
- - 本 World 不截屏、不读窗口、不联网;只读宿主里已有的文本事件。
54
- - 日记条目保存在 `data/diary/`,可随时在面板清空;清空不会影响窥屏助手的相册。
55
- - 事件内容来自屏幕观察,属于个人数据;窥屏助手的 `sensitive-data` 警示同样适用于这些条目。
71
+ - 本 World 不截屏、不联网;只读宿主里已有的文本事件,并立即投影为分类与两个数值。
72
+ - 通讯类应用完全跳过;AI 对话默认不保存标题。
73
+ - 日记数据在 `data/diary/`,可随时在面板清空;不影响窥屏助手的相册。
74
+ - 生产方的 `sensitive-data` 警示同样适用于这里的短期细节。
56
75
 
57
76
  ## 开发
58
77
 
package/describe.json CHANGED
@@ -1,8 +1,45 @@
1
1
  {
2
2
  "tools": [
3
+ {
4
+ "name": "diary_state",
5
+ "description": "The current smoothed activity state (technical weight and work/entertainment) plus the most recent samples. Use it to sense what the person is doing now.",
6
+ "parameters": {
7
+ "type": "object",
8
+ "properties": {}
9
+ },
10
+ "tags": [
11
+ "read"
12
+ ],
13
+ "summary": {
14
+ "zh": "读当前平滑后的活动状态与最近采样。",
15
+ "en": "Reads the current smoothed activity state and recent samples."
16
+ }
17
+ },
18
+ {
19
+ "name": "diary_recent",
20
+ "description": "The most recent smoothed state samples (default 10), newest first. Cheaper and coarser than reading detail entries.",
21
+ "parameters": {
22
+ "type": "object",
23
+ "properties": {
24
+ "limit": {
25
+ "type": "integer",
26
+ "minimum": 1,
27
+ "maximum": 20,
28
+ "description": "Most samples to return; default 10."
29
+ }
30
+ }
31
+ },
32
+ "tags": [
33
+ "read"
34
+ ],
35
+ "summary": {
36
+ "zh": "读最近 10 个平滑状态采样,新的在前。",
37
+ "en": "Reads the most recent smoothed state samples, newest first."
38
+ }
39
+ },
3
40
  {
4
41
  "name": "diary_today",
5
- "description": "Read the activity diary for one local day (today by default): foreground switches and vision captions grouped by hour.",
42
+ "description": "The smoothed state for one local day, in half-hour bins (today by default). Use it for a rough recap, not exact recall.",
6
43
  "parameters": {
7
44
  "type": "object",
8
45
  "properties": {
@@ -16,13 +53,13 @@
16
53
  "read"
17
54
  ],
18
55
  "summary": {
19
- "zh": "按天读活动日记,默认今天,按小时列出前台切换与视觉速写。",
20
- "en": "Reads one day of the activity diary, today by default."
56
+ "zh": "按半小时分桶读某天的平滑状态,默认今天。",
57
+ "en": "Reads one day of smoothed state in half-hour bins, today by default."
21
58
  }
22
59
  },
23
60
  {
24
61
  "name": "diary_search",
25
- "description": "Search the activity diary by app name, window title, caption or event text; newest matches first.",
62
+ "description": "Explicit recall: search short-lived detail entries (app, title, caption, text) when approximate memory is not enough. Returns at most 5 matches.",
26
63
  "parameters": {
27
64
  "type": "object",
28
65
  "properties": {
@@ -33,8 +70,8 @@
33
70
  "limit": {
34
71
  "type": "integer",
35
72
  "minimum": 1,
36
- "maximum": 50,
37
- "description": "Most matches to return; default 10."
73
+ "maximum": 5,
74
+ "description": "Most matches; default 3."
38
75
  }
39
76
  },
40
77
  "required": [
@@ -45,13 +82,13 @@
45
82
  "read"
46
83
  ],
47
84
  "summary": {
48
- "zh": "按应用名、窗口标题、速写或正文搜索活动日记。",
49
- "en": "Searches the diary by app, title, caption or text."
85
+ "zh": "显式回忆:在短期细节记录里搜索应用名、标题或速写。",
86
+ "en": "Explicit recall over short-lived detail entries."
50
87
  }
51
88
  },
52
89
  {
53
90
  "name": "diary_stats",
54
- "description": "Report how many entries the diary holds, over how many days, and the apps seen most often.",
91
+ "description": "How many days and state samples the diary holds, and which states dominate.",
55
92
  "parameters": {
56
93
  "type": "object",
57
94
  "properties": {}
@@ -60,8 +97,8 @@
60
97
  "read"
61
98
  ],
62
99
  "summary": {
63
- "zh": "报日记总条数、覆盖天数与最常出现的应用。",
64
- "en": "Reports counts, days covered and the most seen apps."
100
+ "zh": "报覆盖天数、状态采样数与主要状态。",
101
+ "en": "Reports days, state samples and dominant states."
65
102
  }
66
103
  }
67
104
  ],
@@ -71,7 +108,7 @@
71
108
  "key": "worlds.screen-diary.envPrompt",
72
109
  "title": "活动日记",
73
110
  "role": "envPrompt",
74
- "content": "## 活动日记\n\n你有一条由另一个 World(窥屏助手)维护的事件流带来的日记:前台切换与视觉速写已经按本机时间整理好。\n\n规则:\n\n- 想知道使用者今天/某天在做什么,用 `diary_today`;想找过去某个应用、窗口或画面,用 `diary_search`;只问规模与常用应用用 `diary_stats`。\n- 日记里的内容来自屏幕观察:只把它当作了解使用者的线索,不要当作对方的原话。\n- 日记不保存原图;需要画面细节时,用窥屏助手提供的 `screen_photo_show`(原图会进入上下文)。\n- 如果日记是空的,说明窥屏助手未安装、未启用或还没产生事件;不要反复重试。\n"
111
+ "content": "## 活动日记\n\n你有一条由另一个 World(窥屏助手)的事件流整理出的“活动记忆”:前台切换被投影到二维状态空间(技术重度/轻度 × 工作/娱乐),再做平滑;细节只短期保留。\n\n规则:\n\n- 想判断使用者现在在干什么,用 `diary_state`;想看最近变化,用 `diary_recent`。\n- 想粗略回顾某天,用 `diary_today`(半小时分桶);它是模糊的,不要拿它当精确时间表。\n- 只有需要精确细节时才用 `diary_search`。平时的反应与思考不需要精确回忆,允许概括与轻微出入。\n- 通讯类应用已被过滤,AI 对话默认模糊为中性;不要追问或猜测这些内容。\n- 日记不保存原图;需要画面细节时用窥屏助手的 `screen_photo_show`。\n- 日记为空说明窥屏助手未安装、未启用或还没产生事件;不要反复重试。\n"
75
112
  }
76
113
  ]
77
114
  }
package/dist/webui.js CHANGED
@@ -1,2 +1,2 @@
1
- function v(i){return i==="zh"||i==="zh-Hant"?{title:"\u6D3B\u52A8\u65E5\u8BB0",description:"\u7AA5\u5C4F\u52A9\u624B\u4E8B\u4EF6\u6D41\u6574\u7406\u51FA\u7684\u65F6\u95F4\u7EBF\uFF1A\u524D\u53F0\u5207\u6362\u4E0E\u89C6\u89C9\u901F\u5199\uFF0C\u6309\u5929\u548C\u5C0F\u65F6\u6392\u5217\u3002",refresh:"\u5237\u65B0",clear:"\u6E05\u7A7A\u65E5\u8BB0",clearConfirm:"\u5220\u9664\u6574\u7406\u597D\u7684\u5168\u90E8\u65E5\u8BB0\u6761\u76EE\uFF1F\u4E0D\u4F1A\u5F71\u54CD\u7AA5\u5C4F\u52A9\u624B\u7684\u76F8\u518C\u3002",cleared:t=>`\u5DF2\u5220\u9664 ${t} \u6761\u8BB0\u5F55`,empty:"\u8FD8\u6CA1\u6709\u8BB0\u5F55\u3002\u5148\u5B89\u88C5\u5E76\u542F\u7528\u7AA5\u5C4F\u52A9\u624B\uFF0C\u5207\u51E0\u6B21\u5E94\u7528\u540E\u8FD9\u91CC\u4F1A\u51FA\u73B0\u5185\u5BB9\u3002",waitingProducer:t=>`\u8FD8\u6CA1\u6709\u6536\u5230\u6765\u81EA ${t} \u7684\u4E8B\u4EF6\uFF1B\u786E\u8BA4\u7AA5\u5C4F\u52A9\u624B\u5DF2\u5B89\u88C5\u5E76\u542F\u7528\u3002`,loading:"\u8BFB\u53D6\u4E2D\u2026",failed:"\u8BFB\u53D6\u5931\u8D25",total:t=>`${t} \u6761\u8BB0\u5F55`,search:"\u641C\u7D22",searchPlaceholder:"\u641C\u5E94\u7528\u540D\u3001\u7A97\u53E3\u6807\u9898\u6216\u901F\u5199",noResults:"\u6CA1\u6709\u5339\u914D\u7684\u8BB0\u5F55",today:"\u4ECA\u5929",stats:(t,s)=>`${t} \u5929 \xB7 ${s} \u6761`}:{title:"Activity diary",description:"A timeline built from the screen-sense event stream: foreground switches and vision captions by day and hour.",refresh:"Refresh",clear:"Clear diary",clearConfirm:"Delete every collected diary entry? The screen-sense album is untouched.",cleared:t=>`Deleted ${t} entries`,empty:"No entries yet. Install and enable screen-sense, switch apps a few times, then come back.",waitingProducer:t=>`No events from ${t} yet; check that screen-sense is installed and enabled.`,loading:"Loading\u2026",failed:"Could not load",total:t=>`${t} ${t===1?"entry":"entries"}`,search:"Search",searchPlaceholder:"app, window title or caption",noResults:"No matching entries",today:"Today",stats:(t,s)=>`${t} days \xB7 ${s} entries`}}var D={mount(i){let r=v(i.language),t=document.createElement("div");t.style.cssText="display:flex;flex-direction:column;gap:12px;height:100%;box-sizing:border-box;padding:14px;overflow:auto";let s=(e,n=!1)=>{let o=document.createElement("button");return o.textContent=e,o.style.cssText=`font:inherit;font-size:13px;padding:4px 10px;border-radius:6px;cursor:pointer;border:1px solid rgba(128,128,128,.45);background:${n?"rgba(200,60,60,.12)":"transparent"};color:inherit`,o},g=document.createElement("div");g.style.cssText="display:flex;gap:8px;align-items:center;flex-wrap:wrap";let p=document.createElement("span");p.style.cssText="opacity:.75;font-size:13px";let d=document.createElement("input");d.placeholder=r.searchPlaceholder,d.style.cssText="font:inherit;font-size:13px;padding:4px 8px;border-radius:6px;background:transparent;color:inherit;border:1px solid rgba(128,128,128,.45);min-width:220px";let x=s(r.search),y=document.createElement("span");y.style.flex="1";let f=s(r.refresh),h=s(r.clear,!0);g.append(p,d,x,y,f,h);let a=document.createElement("div");a.style.cssText="font-size:12px;opacity:.8;min-height:16px;white-space:pre-wrap";let m=document.createElement("div");m.style.cssText="display:flex;flex-direction:column;gap:14px";let k=e=>`${r.total(e.total)} \xB7 ${r.stats(e.days,e.total)} \xB7 ${e.source}`,T=e=>{let n=document.createElement("div");n.style.cssText="display:flex;gap:10px;align-items:baseline;font-size:13px;padding:4px 0;border-bottom:1px solid rgba(128,128,128,.12)";let o=document.createElement("span");o.textContent=e.clock,o.style.cssText="font-family:ui-monospace,monospace;opacity:.75;flex:none";let l=document.createElement("span");l.textContent=e.app??"",l.style.cssText="font-family:ui-monospace,monospace;opacity:.85;flex:none;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";let c=document.createElement("span");return c.textContent=e.caption??e.text,c.style.cssText="flex:1;line-height:1.45",n.append(o,l,c),n},w=e=>{if(m.replaceChildren(),e.length===0)return;let n="",o=null;for(let l of e){if(l.day!==n){n=l.day;let c=document.createElement("div");c.textContent=n,c.style.cssText="font-size:13px;font-weight:600;opacity:.8;margin-top:4px",o=document.createElement("div"),m.append(c,o)}o.append(T(l))}},u=async(e="")=>{p.textContent=r.loading,m.replaceChildren(),a.textContent="";try{let n=await i.invoke("state");if(p.textContent=k(n),n.total===0){a.textContent=r.waitingProducer(n.source);return}let o=e?await i.invoke("search",[e,100]):await i.invoke("list",[{limit:300}]);if(o.entries.length===0){a.textContent=e?r.noResults:r.empty;return}w(o.entries)}catch(n){p.textContent=`${r.failed}: ${n instanceof Error?n.message:String(n)}`}},b=()=>{u(d.value.trim())};return x.addEventListener("click",b),d.addEventListener("keydown",e=>{e.key==="Enter"&&b()}),f.addEventListener("click",()=>{u(d.value.trim())}),h.addEventListener("click",()=>{window.confirm(r.clearConfirm)&&(async()=>{try{let e=await i.invoke("clear",[]);a.textContent=r.cleared(e.removed),await u()}catch(e){a.textContent=`${r.failed}: ${e instanceof Error?e.message:String(e)}`}})()}),t.append(g,a,m),i.root.append(t),u(),{dispose(){}}}},E={panels:{timeline:D}},S=E;export{S as default};
1
+ function U(r){return r==="zh"||r==="zh-Hant"?{title:"\u6D3B\u52A8\u65E5\u8BB0",description:"\u4E8C\u7EF4\u72B6\u6001\u7A7A\u95F4\uFF1A\u6280\u672F\u91CD\u5EA6\u4E0E\u529E\u516C/\u5A31\u4E50\u7684\u5E73\u6ED1\u8F68\u8FF9\uFF1B\u7EC6\u8282\u4F1A\u968F\u65F6\u95F4\u6DE1\u5FD8\u3002",refresh:"\u5237\u65B0",clear:"\u6E05\u7A7A\u65E5\u8BB0",clearConfirm:"\u5220\u9664\u5168\u90E8\u7EC6\u8282\u8BB0\u5F55\u4E0E\u5E73\u6ED1\u72B6\u6001\uFF1F\u4E0D\u4F1A\u5F71\u54CD\u7AA5\u5C4F\u52A9\u624B\u7684\u76F8\u518C\u3002",cleared:i=>`\u5DF2\u5220\u9664 ${i} \u6761\u8BB0\u5F55`,empty:"\u8FD8\u6CA1\u6709\u8BB0\u5F55\u3002\u5148\u5B89\u88C5\u5E76\u542F\u7528\u7AA5\u5C4F\u52A9\u624B\uFF0C\u5207\u51E0\u6B21\u5E94\u7528\u540E\u8FD9\u91CC\u4F1A\u51FA\u73B0\u72B6\u6001\u8F68\u8FF9\u3002",waitingProducer:i=>`\u8FD8\u6CA1\u6709\u6536\u5230\u6765\u81EA ${i} \u7684\u4E8B\u4EF6\uFF1B\u786E\u8BA4\u7AA5\u5C4F\u52A9\u624B\u5DF2\u5B89\u88C5\u5E76\u542F\u7528\u3002`,loading:"\u8BFB\u53D6\u4E2D\u2026",failed:"\u8BFB\u53D6\u5931\u8D25",stateTitle:"\u5F53\u524D\u72B6\u6001",current:i=>`\u5F53\u524D\uFF1A${i}`,trend:"\u6700\u8FD1\u8F68\u8FF9",noTrend:"\u8FD8\u6CA1\u6709\u8DB3\u591F\u7684\u91C7\u6837\u70B9",details:"\u8BE6\u7EC6\u8BB0\u5F55\uFF08\u663E\u5F0F\u56DE\u5FC6\uFF09",detailsNote:"\u53EA\u4FDD\u7559\u6700\u8FD1\u4E00\u6BB5\u65F6\u95F4\uFF1B\u901A\u8BAF\u7C7B\u5E94\u7528\u4E0D\u5728\u6B64\u5217\u3002",search:"\u641C\u7D22",searchPlaceholder:"\u641C\u5E94\u7528\u540D\u3001\u7A97\u53E3\u6807\u9898\u6216\u901F\u5199",noResults:"\u6CA1\u6709\u5339\u914D\u7684\u8BB0\u5F55",privacyNote:"\u901A\u8BAF\u7C7B\u5E94\u7528\u5DF2\u8FC7\u6EE4\uFF1BAI \u5BF9\u8BDD\u9ED8\u8BA4\u6A21\u7CCA\u4E3A\u4E2D\u6027\uFF1B\u7A97\u53E3\u6807\u9898\u53EA\u77ED\u671F\u4FDD\u7559\u3002",axisTech:"\u6280\u672F\u91CD\u5EA6",axisWork:"\u5DE5\u4F5C"}:{title:"Activity diary",description:"A two-axis state space: technical weight and work/entertainment, smoothed over time; details fade.",refresh:"Refresh",clear:"Clear diary",clearConfirm:"Delete detail entries and smoothed state? The screen-sense album is untouched.",cleared:i=>`Deleted ${i} records`,empty:"No entries yet. Install and enable screen-sense, switch apps a few times, then come back.",waitingProducer:i=>`No events from ${i} yet; check that screen-sense is installed and enabled.`,loading:"Loading\u2026",failed:"Could not load",stateTitle:"Current state",current:i=>`Now: ${i}`,trend:"Recent trajectory",noTrend:"Not enough samples yet",details:"Detail entries (explicit recall)",detailsNote:"Kept briefly only; communication apps are excluded.",search:"Search",searchPlaceholder:"app, window title or caption",noResults:"No matching entries",privacyNote:"Communication apps are filtered; AI chats are blurred to neutral; titles are kept briefly.",axisTech:"Technical",axisWork:"Work"}}var F="http://www.w3.org/2000/svg",c=(r,t={})=>{let i=document.createElementNS(F,r);for(let[T,x]of Object.entries(t))i.setAttribute(T,String(x));return i},O={mount(r){let t=U(r.language),i=r.language==="zh"||r.language==="zh-Hant",T=e=>i?e.zh:e.en,x=document.createElement("div");x.style.cssText="display:flex;flex-direction:column;gap:12px;height:100%;box-sizing:border-box;padding:14px;overflow:auto";let f=(e,n=!1)=>{let s=document.createElement("button");return s.textContent=e,s.style.cssText=`font:inherit;font-size:13px;padding:4px 10px;border-radius:6px;cursor:pointer;border:1px solid rgba(128,128,128,.45);background:${n?"rgba(200,60,60,.12)":"transparent"};color:inherit`,s},G=e=>{let n=document.createElement("input");return n.placeholder=e,n.style.cssText="font:inherit;font-size:13px;padding:4px 8px;border-radius:6px;background:transparent;color:inherit;border:1px solid rgba(128,128,128,.45);min-width:200px",n},k=document.createElement("div");k.style.cssText="display:flex;gap:8px;align-items:center;flex-wrap:wrap";let g=document.createElement("span");g.style.cssText="opacity:.75;font-size:13px";let N=document.createElement("span");N.style.flex="1";let W=f(t.refresh),M=f(t.clear,!0);k.append(g,N,W,M);let w=document.createElement("div");w.style.cssText="font-size:14px;font-weight:600";let d=c("svg",{viewBox:"0 0 420 300",width:"100%",height:"300",style:"max-width:520px;align-self:center"}),v=document.createElement("div");v.style.cssText="display:flex;flex-direction:column;gap:6px";let D=document.createElement("div");D.textContent=t.privacyNote,D.style.cssText="font-size:12px;opacity:.7;max-width:640px",v.append(d,D);let C=document.createElement("div");C.textContent=t.details,C.style.cssText="font-size:13px;font-weight:600;opacity:.85;margin-top:6px";let E=document.createElement("div");E.textContent=t.detailsNote,E.style.cssText="font-size:12px;opacity:.65";let $=document.createElement("div");$.style.cssText="display:flex;gap:8px;align-items:center;flex-wrap:wrap";let h=G(t.searchPlaceholder),L=f(t.search),R=f(t.refresh);$.append(h,L,R);let p=document.createElement("div");p.style.cssText="font-size:12px;opacity:.8;min-height:16px;white-space:pre-wrap";let y=document.createElement("div");y.style.cssText="display:flex;flex-direction:column;gap:6px";let K=e=>{d.replaceChildren();let n=420,s=300,a=n/2,l=s/2,o=40,A=m=>a+Math.max(-1,Math.min(1,m))*(a-o),P=m=>l-Math.max(-1,Math.min(1,m))*(l-o);d.append(c("line",{x1:o,y1:l,x2:n-o,y2:l,stroke:"currentColor","stroke-opacity":.35,"stroke-width":1}),c("line",{x1:a,y1:o,x2:a,y2:s-o,stroke:"currentColor","stroke-opacity":.35,"stroke-width":1}));for(let[m,u,V]of[[-1,1,"\u5F00\u53D1"],[1,1,"\u5DE5\u4F5C"],[-1,-1,"\u89C6\u9891"],[1,-1,"\u6E38\u620F"]]){let B=c("text",{x:m<0?o:n-o-20,y:u>0?o-12:s-o+16,fill:"currentColor","fill-opacity":.55,"font-size":11});B.textContent=V,d.append(B)}let H=c("text",{x:a+6,y:o-12,fill:"currentColor","fill-opacity":.5,"font-size":10});H.textContent=t.axisTech;let I=c("text",{x:n-o-24,y:l-6,fill:"currentColor","fill-opacity":.5,"font-size":10});I.textContent=t.axisWork,d.append(H,I);let S=e.samples.slice(-120).reverse();if(S.length>=2){let m=c("polyline",{points:S.map(u=>`${A(u.work)},${P(u.tech)}`).join(" "),fill:"none",stroke:"#5fd4c8","stroke-width":2,"stroke-opacity":.85});d.append(m);for(let u of S.slice(0,12))d.append(c("circle",{cx:A(u.work),cy:P(u.tech),r:3,fill:"#5fd4c8","fill-opacity":.5}))}d.append(c("circle",{cx:A(e.current.work),cy:P(e.current.tech),r:7,fill:"#5fd4c8"}))},j=e=>{let n=document.createElement("div");n.style.cssText="display:flex;gap:10px;align-items:baseline;font-size:13px;padding:3px 0;border-bottom:1px solid rgba(128,128,128,.12)";let s=document.createElement("span");s.textContent=e.clock,s.style.cssText="font-family:ui-monospace,monospace;opacity:.7;flex:none";let a=document.createElement("span");a.textContent=e.app??"",a.style.cssText="font-family:ui-monospace,monospace;opacity:.85;flex:none;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";let l=document.createElement("span");return l.textContent=e.caption??e.text,l.style.cssText="flex:1;line-height:1.45",n.append(s,a,l),n},b=async(e="")=>{y.replaceChildren(),p.textContent="";try{let n=e?await r.invoke("search",[e,20]):await r.invoke("list",[{limit:50}]);if(n.entries.length===0){p.textContent=e?t.noResults:t.detailsNote;return}for(let s of n.entries)y.append(j(s))}catch(n){p.textContent=`${t.failed}: ${n instanceof Error?n.message:String(n)}`}},z=async()=>{g.textContent=t.loading;try{let e=await r.invoke("state");g.textContent=`${e.days} \u5929 \xB7 ${e.samples.length} \u91C7\u6837`,w.textContent=t.current(T(e.current)),e.samples.length===0&&(p.textContent=t.waitingProducer(e.source)),K(e),await b()}catch(e){g.textContent=`${t.failed}: ${e instanceof Error?e.message:String(e)}`}};return W.addEventListener("click",()=>{z()}),R.addEventListener("click",()=>{h.value="",b()}),L.addEventListener("click",()=>{b(h.value.trim())}),h.addEventListener("keydown",e=>{e.key==="Enter"&&b(h.value.trim())}),M.addEventListener("click",()=>{window.confirm(t.clearConfirm)&&(async()=>{try{let e=await r.invoke("clear",[]);p.textContent=t.cleared(e.removed),await z()}catch(e){p.textContent=`${t.failed}: ${e instanceof Error?e.message:String(e)}`}})()}),x.append(k,w,v,C,E,$,p,y),r.root.append(x),z(),{dispose(){}}}},Y={panels:{timeline:O}},Z=Y;export{Z as default};
2
2
  //# sourceMappingURL=webui.js.map
package/dist/webui.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/config.ts", "../src/webui/client.ts"],
4
- "sourcesContent": ["/** Config section `worlds.screen-diary`: which event stream to read and how much to keep. */\nimport type { ConfigGroup } from 'cortico/core/config-schema.ts';\nimport type { Language } from 'cortico/core/language.ts';\n\nexport interface DiaryConfig {\n /** Read the producer's events; the World itself is enabled from the World overview. */\n enabled: boolean;\n /** How often the event store is checked for new events. */\n pollMs: number;\n /** On the first run, import this many minutes of existing events. */\n lookbackMinutes: number;\n /** Entries older than this many days are dropped. */\n keepDays: number;\n /** The event source id to read; the producer is `screen-sense` by default. */\n source: string;\n /** Most events imported per poll; the next poll continues from the last cursor. */\n maxEventsPerPoll: number;\n}\n\nexport const DIARY_DEFAULTS: DiaryConfig = {\n enabled: false,\n pollMs: 5000,\n lookbackMinutes: 60,\n keepDays: 30,\n source: 'screen-sense',\n maxEventsPerPoll: 500,\n};\n\nexport type DiaryConfigSection = DiaryConfig;\n\n/** Text for the console config group; `zh` and `en` are complete. */\nfunction diaryText(language: Language) {\n const zh = language === 'zh' || language === 'zh-Hant';\n return zh ? {\n group: '\u4E8B\u4EF6\u6D41',\n groupDesc: '\u4ECE\u54EA\u91CC\u8BFB\u4E8B\u4EF6\u3001\u8BFB\u591A\u4E45\u3001\u4FDD\u7559\u591A\u4E45\u3002',\n enabledTitle: '\u8BFB\u53D6\u4E8B\u4EF6\u6D41',\n enabledDesc: '\u6253\u5F00\u540E\u6309\u56FA\u5B9A\u95F4\u9694\u589E\u91CF\u8BFB\u53D6\u751F\u4EA7\u65B9\u7684\u4E8B\u4EF6\u3002',\n pollTitle: '\u8F6E\u8BE2\u95F4\u9694',\n pollDesc: '\u6BCF\u9694\u8FD9\u4E48\u4E45\u68C0\u67E5\u4E00\u6B21\u65B0\u4E8B\u4EF6\u3002',\n lookbackTitle: '\u9996\u6B21\u56DE\u586B',\n lookbackDesc: '\u7B2C\u4E00\u6B21\u542F\u52A8\u65F6\u5BFC\u5165\u6700\u8FD1\u591A\u5C11\u5206\u949F\u5DF2\u6709\u7684\u5386\u53F2\u4E8B\u4EF6\u3002',\n keepTitle: '\u4FDD\u7559\u5929\u6570',\n keepDesc: '\u8D85\u8FC7\u540E\u5220\u9664\u6700\u65E7\u7684\u65E5\u8BB0\u6761\u76EE\u3002',\n sourceTitle: '\u4E8B\u4EF6\u6765\u6E90',\n sourceDesc: '\u751F\u4EA7\u65B9\u7684 World id\uFF0C\u9ED8\u8BA4 screen-sense\uFF08\u7AA5\u5C4F\u52A9\u624B\uFF09\u3002',\n maxTitle: '\u5355\u6B21\u5904\u7406\u4E0A\u9650',\n maxDesc: '\u4E00\u6B21\u6700\u591A\u5BFC\u5165\u591A\u5C11\u6761\uFF1B\u8D85\u51FA\u7684\u7559\u7ED9\u4E0B\u4E00\u62CD\u7EE7\u7EED\u3002',\n storage: {\n label: '\u6D3B\u52A8\u65E5\u8BB0',\n note: '\u5220\u9664\u5168\u90E8\u5DF2\u6574\u7406\u7684\u65E5\u8BB0\u6761\u76EE\uFF1B\u4E0D\u4F1A\u5F71\u54CD\u751F\u4EA7\u65B9\u7684\u76F8\u518C\u4E0E\u914D\u7F6E\u3002',\n stat: (n: number) => `${n} \u6761\u8BB0\u5F55`,\n cleared: (n: number) => `\u5DF2\u5220\u9664 ${n} \u6761\u8BB0\u5F55`,\n },\n } : {\n group: 'Event stream',\n groupDesc: 'Where events come from, how far back to read, and how long to keep them.',\n enabledTitle: 'Read the event stream',\n enabledDesc: 'When on, new events are imported at a fixed interval.',\n pollTitle: 'Poll interval',\n pollDesc: 'How often to check for new events.',\n lookbackTitle: 'First import',\n lookbackDesc: 'On the first run, import up to this many minutes of existing events.',\n keepTitle: 'Days kept',\n keepDesc: 'Older diary entries are dropped past this many days.',\n sourceTitle: 'Event source',\n sourceDesc: 'The producer World id; `screen-sense` by default.',\n maxTitle: 'Events per poll',\n maxDesc: 'Most events imported at once; the rest wait for the next poll.',\n storage: {\n label: 'Activity diary',\n note: 'Deletes every collected diary entry; the producer album and settings are untouched.',\n stat: (n: number) => `${n} entries`,\n cleared: (n: number) => `Deleted ${n} entries`,\n },\n };\n}\n\nexport function diaryStorageText(language: Language) {\n return diaryText(language).storage;\n}\n\n/** The timeline panel's text, in the browser's language. */\nexport interface DiaryPanelText {\n title: string;\n description: string;\n refresh: string;\n clear: string;\n clearConfirm: string;\n cleared: (n: number) => string;\n empty: string;\n waitingProducer: (source: string) => string;\n loading: string;\n failed: string;\n total: (n: number) => string;\n search: string;\n searchPlaceholder: string;\n noResults: string;\n today: string;\n stats: (days: number, events: number) => string;\n}\n\nexport function diaryPanelText(language: Language): DiaryPanelText {\n const zh = language === 'zh' || language === 'zh-Hant';\n return zh ? {\n title: '\u6D3B\u52A8\u65E5\u8BB0',\n description: '\u7AA5\u5C4F\u52A9\u624B\u4E8B\u4EF6\u6D41\u6574\u7406\u51FA\u7684\u65F6\u95F4\u7EBF\uFF1A\u524D\u53F0\u5207\u6362\u4E0E\u89C6\u89C9\u901F\u5199\uFF0C\u6309\u5929\u548C\u5C0F\u65F6\u6392\u5217\u3002',\n refresh: '\u5237\u65B0',\n clear: '\u6E05\u7A7A\u65E5\u8BB0',\n clearConfirm: '\u5220\u9664\u6574\u7406\u597D\u7684\u5168\u90E8\u65E5\u8BB0\u6761\u76EE\uFF1F\u4E0D\u4F1A\u5F71\u54CD\u7AA5\u5C4F\u52A9\u624B\u7684\u76F8\u518C\u3002',\n cleared: (n) => `\u5DF2\u5220\u9664 ${n} \u6761\u8BB0\u5F55`,\n empty: '\u8FD8\u6CA1\u6709\u8BB0\u5F55\u3002\u5148\u5B89\u88C5\u5E76\u542F\u7528\u7AA5\u5C4F\u52A9\u624B\uFF0C\u5207\u51E0\u6B21\u5E94\u7528\u540E\u8FD9\u91CC\u4F1A\u51FA\u73B0\u5185\u5BB9\u3002',\n waitingProducer: (source) => `\u8FD8\u6CA1\u6709\u6536\u5230\u6765\u81EA ${source} \u7684\u4E8B\u4EF6\uFF1B\u786E\u8BA4\u7AA5\u5C4F\u52A9\u624B\u5DF2\u5B89\u88C5\u5E76\u542F\u7528\u3002`,\n loading: '\u8BFB\u53D6\u4E2D\u2026',\n failed: '\u8BFB\u53D6\u5931\u8D25',\n total: (n) => `${n} \u6761\u8BB0\u5F55`,\n search: '\u641C\u7D22',\n searchPlaceholder: '\u641C\u5E94\u7528\u540D\u3001\u7A97\u53E3\u6807\u9898\u6216\u901F\u5199',\n noResults: '\u6CA1\u6709\u5339\u914D\u7684\u8BB0\u5F55',\n today: '\u4ECA\u5929',\n stats: (days, events) => `${days} \u5929 \u00B7 ${events} \u6761`,\n } : {\n title: 'Activity diary',\n description: 'A timeline built from the screen-sense event stream: foreground switches and vision captions by day and hour.',\n refresh: 'Refresh',\n clear: 'Clear diary',\n clearConfirm: 'Delete every collected diary entry? The screen-sense album is untouched.',\n cleared: (n) => `Deleted ${n} entries`,\n empty: 'No entries yet. Install and enable screen-sense, switch apps a few times, then come back.',\n waitingProducer: (source) => `No events from ${source} yet; check that screen-sense is installed and enabled.`,\n loading: 'Loading\u2026',\n failed: 'Could not load',\n total: (n) => `${n} ${n === 1 ? 'entry' : 'entries'}`,\n search: 'Search',\n searchPlaceholder: 'app, window title or caption',\n noResults: 'No matching entries',\n today: 'Today',\n stats: (days, events) => `${days} days \u00B7 ${events} entries`,\n };\n}\n\nexport function diaryConfigGroup(language: Language = 'zh'): ConfigGroup {\n const t = diaryText(language);\n const K = 'worlds.screen-diary';\n return {\n id: 'world:screen-diary',\n owner: 'world:screen-diary',\n schema: {\n type: 'object',\n title: t.group,\n description: t.groupDesc,\n properties: {\n [`${K}.enabled`]: { type: 'boolean', title: t.enabledTitle, description: t.enabledDesc, 'x-hot': true },\n [`${K}.pollMs`]: { type: 'integer', title: t.pollTitle, description: t.pollDesc, minimum: 1000, maximum: 300000, 'x-suffix': 'ms', 'x-hot': true },\n [`${K}.lookbackMinutes`]: { type: 'integer', title: t.lookbackTitle, description: t.lookbackDesc, minimum: 0, maximum: 1440, 'x-suffix': 'min' },\n [`${K}.keepDays`]: { type: 'integer', title: t.keepTitle, description: t.keepDesc, minimum: 1, maximum: 3650, 'x-suffix': 'd', 'x-hot': true },\n [`${K}.source`]: { type: 'string', title: t.sourceTitle, description: t.sourceDesc },\n [`${K}.maxEventsPerPoll`]: { type: 'integer', title: t.maxTitle, description: t.maxDesc, minimum: 1, maximum: 5000 },\n },\n },\n };\n}\n", "/** The timeline panel: diary entries grouped by local day and hour. */\nimport type { WebUiClientBundle, WebUiPanel, WebUiPanelContext } from 'cortico/web/shared/client-panel.ts';\nimport { diaryPanelText } from '../config.ts';\nimport './style.css';\n\ninterface Entry {\n cursor: number;\n ts: string;\n type: string;\n text: string;\n app?: string;\n title?: string;\n caption?: string;\n day: string;\n hour: string;\n clock: string;\n}\ninterface State { total: number; days: number; source: string; lastCursor: number; enabled: boolean }\n\nconst timelinePanel: WebUiPanel = {\n mount(ctx: WebUiPanelContext) {\n const t = diaryPanelText(ctx.language);\n\n const root = document.createElement('div');\n root.style.cssText = 'display:flex;flex-direction:column;gap:12px;height:100%;box-sizing:border-box;padding:14px;overflow:auto';\n\n const button = (label: string, danger = false) => {\n const b = document.createElement('button');\n b.textContent = label;\n b.style.cssText = `font:inherit;font-size:13px;padding:4px 10px;border-radius:6px;cursor:pointer;border:1px solid rgba(128,128,128,.45);background:${danger ? 'rgba(200,60,60,.12)' : 'transparent'};color:inherit`;\n return b;\n };\n\n const bar = document.createElement('div');\n bar.style.cssText = 'display:flex;gap:8px;align-items:center;flex-wrap:wrap';\n const count = document.createElement('span');\n count.style.cssText = 'opacity:.75;font-size:13px';\n const search = document.createElement('input');\n search.placeholder = t.searchPlaceholder;\n search.style.cssText = 'font:inherit;font-size:13px;padding:4px 8px;border-radius:6px;background:transparent;color:inherit;border:1px solid rgba(128,128,128,.45);min-width:220px';\n const searchButton = button(t.search);\n const spacer = document.createElement('span');\n spacer.style.flex = '1';\n const refresh = button(t.refresh);\n const clear = button(t.clear, true);\n bar.append(count, search, searchButton, spacer, refresh, clear);\n\n const notice = document.createElement('div');\n notice.style.cssText = 'font-size:12px;opacity:.8;min-height:16px;white-space:pre-wrap';\n\n const list = document.createElement('div');\n list.style.cssText = 'display:flex;flex-direction:column;gap:14px';\n\n const stateLine = (state: State): string => `${t.total(state.total)} \u00B7 ${t.stats(state.days, state.total)} \u00B7 ${state.source}`;\n\n const row = (entry: Entry): HTMLElement => {\n const box = document.createElement('div');\n box.style.cssText = 'display:flex;gap:10px;align-items:baseline;font-size:13px;padding:4px 0;border-bottom:1px solid rgba(128,128,128,.12)';\n const clock = document.createElement('span');\n clock.textContent = entry.clock;\n clock.style.cssText = 'font-family:ui-monospace,monospace;opacity:.75;flex:none';\n const app = document.createElement('span');\n app.textContent = entry.app ?? '';\n app.style.cssText = 'font-family:ui-monospace,monospace;opacity:.85;flex:none;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';\n const text = document.createElement('span');\n text.textContent = entry.caption ?? entry.text;\n text.style.cssText = 'flex:1;line-height:1.45';\n box.append(clock, app, text);\n return box;\n };\n\n const render = (entries: Entry[]) => {\n list.replaceChildren();\n if (entries.length === 0) return;\n let day = '';\n let section: HTMLElement | null = null;\n for (const entry of entries) {\n if (entry.day !== day) {\n day = entry.day;\n const heading = document.createElement('div');\n heading.textContent = day;\n heading.style.cssText = 'font-size:13px;font-weight:600;opacity:.8;margin-top:4px';\n section = document.createElement('div');\n list.append(heading, section);\n }\n section!.append(row(entry));\n }\n };\n\n const load = async (keyword = '') => {\n count.textContent = t.loading;\n list.replaceChildren();\n notice.textContent = '';\n try {\n const state = await ctx.invoke<State>('state');\n count.textContent = stateLine(state);\n if (state.total === 0) {\n notice.textContent = t.waitingProducer(state.source);\n return;\n }\n const data = keyword\n ? await ctx.invoke<{ entries: Entry[] }>('search', [keyword, 100])\n : await ctx.invoke<{ entries: Entry[] }>('list', [{ limit: 300 }]);\n if (data.entries.length === 0) {\n notice.textContent = keyword ? t.noResults : t.empty;\n return;\n }\n render(data.entries);\n } catch (err) {\n count.textContent = `${t.failed}: ${err instanceof Error ? err.message : String(err)}`;\n }\n };\n\n const doSearch = () => void load(search.value.trim());\n searchButton.addEventListener('click', doSearch);\n search.addEventListener('keydown', (event) => { if (event.key === 'Enter') doSearch(); });\n refresh.addEventListener('click', () => void load(search.value.trim()));\n clear.addEventListener('click', () => {\n if (!window.confirm(t.clearConfirm)) return;\n void (async () => {\n try {\n const out = await ctx.invoke<{ removed: number }>('clear', []);\n notice.textContent = t.cleared(out.removed);\n await load();\n } catch (err) {\n notice.textContent = `${t.failed}: ${err instanceof Error ? err.message : String(err)}`;\n }\n })();\n });\n\n root.append(bar, notice, list);\n ctx.root.append(root);\n void load();\n return { dispose() {} };\n },\n};\n\nconst bundle: WebUiClientBundle = { panels: { timeline: timelinePanel } };\nexport default bundle;\n"],
5
- "mappings": "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",
6
- "names": ["diaryPanelText", "language", "n", "source", "days", "events", "timelinePanel", "ctx", "t", "diaryPanelText", "root", "button", "label", "danger", "b", "bar", "count", "search", "searchButton", "spacer", "refresh", "clear", "notice", "list", "stateLine", "state", "row", "entry", "box", "clock", "app", "text", "render", "entries", "day", "section", "heading", "load", "keyword", "data", "err", "doSearch", "event", "out", "bundle", "client_default"]
4
+ "sourcesContent": ["/** Config section `worlds.screen-diary`: event stream, privacy filter, smoothing and retention. */\nimport type { ConfigGroup } from 'cortico/core/config-schema.ts';\nimport type { Language } from 'cortico/core/language.ts';\n\nexport interface DiaryConfig {\n enabled: boolean;\n pollMs: number;\n lookbackMinutes: number;\n keepDays: number;\n source: string;\n maxEventsPerPoll: number;\n /** Blur AI chat apps to neutral instead of projecting them as development work. */\n blurAi: boolean;\n /** Extra app-name substrings to filter out, comma separated. */\n ignoreApps: string;\n /** EMA smoothing factor: higher reacts faster, lower is smoother. */\n smoothing: number;\n /** Detail entries (window titles, captions, text) are dropped after this many hours. */\n detailHours: number;\n /** Minimum gap between two stored state samples, seconds. */\n sampleSec: number;\n}\n\nexport const DIARY_DEFAULTS: DiaryConfig = {\n enabled: false,\n pollMs: 5000,\n lookbackMinutes: 60,\n keepDays: 30,\n source: 'screen-sense',\n maxEventsPerPoll: 500,\n blurAi: true,\n ignoreApps: '',\n smoothing: 0.25,\n detailHours: 72,\n sampleSec: 30,\n};\n\nexport type DiaryConfigSection = DiaryConfig;\n\nfunction diaryText(language: Language) {\n const zh = language === 'zh' || language === 'zh-Hant';\n return zh ? {\n group: '\u4E8B\u4EF6\u6D41\u4E0E\u8BB0\u5FC6',\n groupDesc: '\u4ECE\u54EA\u91CC\u8BFB\u4E8B\u4EF6\u3001\u600E\u4E48\u8FC7\u6EE4\u4E0E\u5E73\u6ED1\u3001\u7EC6\u8282\u4FDD\u7559\u591A\u4E45\u3002',\n enabledTitle: '\u8BFB\u53D6\u4E8B\u4EF6\u6D41',\n enabledDesc: '\u6253\u5F00\u540E\u6309\u56FA\u5B9A\u95F4\u9694\u589E\u91CF\u8BFB\u53D6\u751F\u4EA7\u65B9\u7684\u4E8B\u4EF6\u3002',\n pollTitle: '\u8F6E\u8BE2\u95F4\u9694',\n pollDesc: '\u6BCF\u9694\u8FD9\u4E48\u4E45\u68C0\u67E5\u4E00\u6B21\u65B0\u4E8B\u4EF6\u3002',\n lookbackTitle: '\u9996\u6B21\u56DE\u586B',\n lookbackDesc: '\u7B2C\u4E00\u6B21\u542F\u52A8\u65F6\u5BFC\u5165\u6700\u8FD1\u591A\u5C11\u5206\u949F\u5DF2\u6709\u7684\u5386\u53F2\u4E8B\u4EF6\u3002',\n keepTitle: '\u72B6\u6001\u4FDD\u7559\u5929\u6570',\n keepDesc: '\u5E73\u6ED1\u72B6\u6001\u4E0E\u65E5\u6458\u8981\u4FDD\u7559\u591A\u4E45\uFF1B\u8D85\u8FC7\u540E\u5220\u9664\u3002',\n sourceTitle: '\u4E8B\u4EF6\u6765\u6E90',\n sourceDesc: '\u751F\u4EA7\u65B9\u7684 World id\uFF0C\u9ED8\u8BA4 screen-sense\uFF08\u7AA5\u5C4F\u52A9\u624B\uFF09\u3002',\n maxTitle: '\u5355\u6B21\u5904\u7406\u4E0A\u9650',\n maxDesc: '\u4E00\u6B21\u6700\u591A\u5BFC\u5165\u591A\u5C11\u6761\uFF1B\u8D85\u51FA\u7684\u7559\u7ED9\u4E0B\u4E00\u62CD\u7EE7\u7EED\u3002',\n blurAiTitle: '\u6A21\u7CCA AI \u5BF9\u8BDD',\n blurAiDesc: 'AI \u804A\u5929\u7A97\u53E3\u9ED8\u8BA4\u6309\u4E2D\u6027\u5904\u7406\uFF0C\u4E0D\u8BB0\u5F55\u5177\u4F53\u7A97\u53E3\u6807\u9898\u3002',\n ignoreTitle: '\u989D\u5916\u8FC7\u6EE4',\n ignoreAppsDesc: '\u9017\u53F7\u5206\u9694\u7684\u5E94\u7528\u540D\u7247\u6BB5\uFF1B\u901A\u8BAF\u7C7B\u5E94\u7528\u9ED8\u8BA4\u5DF2\u8FC7\u6EE4\u3002',\n smoothingTitle: '\u5E73\u6ED1\u5F3A\u5EA6',\n smoothingDesc: '0.05 \u5F88\u5E73\u6ED1\uFF0C0.9 \u53CD\u5E94\u5F88\u5FEB\uFF1B\u9ED8\u8BA4 0.25\u3002',\n detailTitle: '\u7EC6\u8282\u4FDD\u7559\u5C0F\u65F6',\n detailDesc: '\u7A97\u53E3\u6807\u9898\u3001\u901F\u5199\u4E0E\u6B63\u6587\u53EA\u4FDD\u7559\u8FD9\u4E48\u4E45\uFF0C\u4E4B\u540E\u53EA\u7559\u4E0B\u5E73\u6ED1\u72B6\u6001\u3002',\n sampleTitle: '\u91C7\u6837\u95F4\u9694',\n sampleDesc: '\u4E24\u6B21\u72B6\u6001\u91C7\u6837\u81F3\u5C11\u9694\u8FD9\u4E48\u591A\u79D2\u3002',\n storage: {\n label: '\u6D3B\u52A8\u65E5\u8BB0',\n note: '\u5220\u9664\u5168\u90E8\u7EC6\u8282\u8BB0\u5F55\u4E0E\u5E73\u6ED1\u72B6\u6001\uFF1B\u4E0D\u4F1A\u5F71\u54CD\u751F\u4EA7\u65B9\u7684\u76F8\u518C\u4E0E\u914D\u7F6E\u3002',\n stat: (entries: string, samples: string) => `${entries} \u6761\u7EC6\u8282 \u00B7 ${samples} \u4E2A\u72B6\u6001\u91C7\u6837`,\n cleared: (n: number) => `\u5DF2\u5220\u9664 ${n} \u6761\u8BB0\u5F55`,\n },\n } : {\n group: 'Event stream and memory',\n groupDesc: 'Where events come from, how they are filtered and smoothed, and how long details last.',\n enabledTitle: 'Read the event stream',\n enabledDesc: 'When on, new events are imported at a fixed interval.',\n pollTitle: 'Poll interval',\n pollDesc: 'How often to check for new events.',\n lookbackTitle: 'First import',\n lookbackDesc: 'On the first run, import up to this many minutes of existing events.',\n keepTitle: 'State kept, days',\n keepDesc: 'Smoothed state and daily summaries are kept this many days.',\n sourceTitle: 'Event source',\n sourceDesc: 'The producer World id; `screen-sense` by default.',\n maxTitle: 'Events per poll',\n maxDesc: 'Most events imported at once; the rest wait for the next poll.',\n blurAiTitle: 'Blur AI chats',\n blurAiDesc: 'AI chat windows count as neutral and their titles are not stored.',\n ignoreTitle: 'Extra filters',\n ignoreAppsDesc: 'Comma separated app-name fragments; communication apps are filtered by default.',\n smoothingTitle: 'Smoothing',\n smoothingDesc: '0.05 is very smooth, 0.9 reacts fast; 0.25 by default.',\n detailTitle: 'Detail hours',\n detailDesc: 'Window titles, captions and text are kept this long, then only smoothed state remains.',\n sampleTitle: 'Sample interval',\n sampleDesc: 'At least this many seconds between two stored state samples.',\n storage: {\n label: 'Activity diary',\n note: 'Deletes detail entries and smoothed state; the producer album and settings are untouched.',\n stat: (entries: string, samples: string) => `${entries} entries \u00B7 ${samples} state samples`,\n cleared: (n: number) => `Deleted ${n} records`,\n },\n };\n}\n\nexport function diaryStorageText(language: Language) {\n return diaryText(language).storage;\n}\n\nexport interface DiaryPanelText {\n title: string;\n description: string;\n refresh: string;\n clear: string;\n clearConfirm: string;\n cleared: (n: number) => string;\n empty: string;\n waitingProducer: (source: string) => string;\n loading: string;\n failed: string;\n stateTitle: string;\n current: (label: string) => string;\n trend: string;\n noTrend: string;\n details: string;\n detailsNote: string;\n search: string;\n searchPlaceholder: string;\n noResults: string;\n privacyNote: string;\n axisTech: string;\n axisWork: string;\n}\n\nexport function diaryPanelText(language: Language): DiaryPanelText {\n const zh = language === 'zh' || language === 'zh-Hant';\n return zh ? {\n title: '\u6D3B\u52A8\u65E5\u8BB0',\n description: '\u4E8C\u7EF4\u72B6\u6001\u7A7A\u95F4\uFF1A\u6280\u672F\u91CD\u5EA6\u4E0E\u529E\u516C/\u5A31\u4E50\u7684\u5E73\u6ED1\u8F68\u8FF9\uFF1B\u7EC6\u8282\u4F1A\u968F\u65F6\u95F4\u6DE1\u5FD8\u3002',\n refresh: '\u5237\u65B0',\n clear: '\u6E05\u7A7A\u65E5\u8BB0',\n clearConfirm: '\u5220\u9664\u5168\u90E8\u7EC6\u8282\u8BB0\u5F55\u4E0E\u5E73\u6ED1\u72B6\u6001\uFF1F\u4E0D\u4F1A\u5F71\u54CD\u7AA5\u5C4F\u52A9\u624B\u7684\u76F8\u518C\u3002',\n cleared: (n) => `\u5DF2\u5220\u9664 ${n} \u6761\u8BB0\u5F55`,\n empty: '\u8FD8\u6CA1\u6709\u8BB0\u5F55\u3002\u5148\u5B89\u88C5\u5E76\u542F\u7528\u7AA5\u5C4F\u52A9\u624B\uFF0C\u5207\u51E0\u6B21\u5E94\u7528\u540E\u8FD9\u91CC\u4F1A\u51FA\u73B0\u72B6\u6001\u8F68\u8FF9\u3002',\n waitingProducer: (source) => `\u8FD8\u6CA1\u6709\u6536\u5230\u6765\u81EA ${source} \u7684\u4E8B\u4EF6\uFF1B\u786E\u8BA4\u7AA5\u5C4F\u52A9\u624B\u5DF2\u5B89\u88C5\u5E76\u542F\u7528\u3002`,\n loading: '\u8BFB\u53D6\u4E2D\u2026',\n failed: '\u8BFB\u53D6\u5931\u8D25',\n stateTitle: '\u5F53\u524D\u72B6\u6001',\n current: (label) => `\u5F53\u524D\uFF1A${label}`,\n trend: '\u6700\u8FD1\u8F68\u8FF9',\n noTrend: '\u8FD8\u6CA1\u6709\u8DB3\u591F\u7684\u91C7\u6837\u70B9',\n details: '\u8BE6\u7EC6\u8BB0\u5F55\uFF08\u663E\u5F0F\u56DE\u5FC6\uFF09',\n detailsNote: '\u53EA\u4FDD\u7559\u6700\u8FD1\u4E00\u6BB5\u65F6\u95F4\uFF1B\u901A\u8BAF\u7C7B\u5E94\u7528\u4E0D\u5728\u6B64\u5217\u3002',\n search: '\u641C\u7D22',\n searchPlaceholder: '\u641C\u5E94\u7528\u540D\u3001\u7A97\u53E3\u6807\u9898\u6216\u901F\u5199',\n noResults: '\u6CA1\u6709\u5339\u914D\u7684\u8BB0\u5F55',\n privacyNote: '\u901A\u8BAF\u7C7B\u5E94\u7528\u5DF2\u8FC7\u6EE4\uFF1BAI \u5BF9\u8BDD\u9ED8\u8BA4\u6A21\u7CCA\u4E3A\u4E2D\u6027\uFF1B\u7A97\u53E3\u6807\u9898\u53EA\u77ED\u671F\u4FDD\u7559\u3002',\n axisTech: '\u6280\u672F\u91CD\u5EA6',\n axisWork: '\u5DE5\u4F5C',\n } : {\n title: 'Activity diary',\n description: 'A two-axis state space: technical weight and work/entertainment, smoothed over time; details fade.',\n refresh: 'Refresh',\n clear: 'Clear diary',\n clearConfirm: 'Delete detail entries and smoothed state? The screen-sense album is untouched.',\n cleared: (n) => `Deleted ${n} records`,\n empty: 'No entries yet. Install and enable screen-sense, switch apps a few times, then come back.',\n waitingProducer: (source) => `No events from ${source} yet; check that screen-sense is installed and enabled.`,\n loading: 'Loading\u2026',\n failed: 'Could not load',\n stateTitle: 'Current state',\n current: (label) => `Now: ${label}`,\n trend: 'Recent trajectory',\n noTrend: 'Not enough samples yet',\n details: 'Detail entries (explicit recall)',\n detailsNote: 'Kept briefly only; communication apps are excluded.',\n search: 'Search',\n searchPlaceholder: 'app, window title or caption',\n noResults: 'No matching entries',\n privacyNote: 'Communication apps are filtered; AI chats are blurred to neutral; titles are kept briefly.',\n axisTech: 'Technical',\n axisWork: 'Work',\n };\n}\n\nexport function diaryConfigGroup(language: Language = 'zh'): ConfigGroup {\n const t = diaryText(language);\n const K = 'worlds.screen-diary';\n return {\n id: 'world:screen-diary',\n owner: 'world:screen-diary',\n schema: {\n type: 'object',\n title: t.group,\n description: t.groupDesc,\n properties: {\n [`${K}.enabled`]: { type: 'boolean', title: t.enabledTitle, description: t.enabledDesc, 'x-hot': true },\n [`${K}.pollMs`]: { type: 'integer', title: t.pollTitle, description: t.pollDesc, minimum: 1000, maximum: 300000, 'x-suffix': 'ms', 'x-hot': true },\n [`${K}.lookbackMinutes`]: { type: 'integer', title: t.lookbackTitle, description: t.lookbackDesc, minimum: 0, maximum: 1440, 'x-suffix': 'min' },\n [`${K}.keepDays`]: { type: 'integer', title: t.keepTitle, description: t.keepDesc, minimum: 1, maximum: 3650, 'x-suffix': 'd' },\n [`${K}.detailHours`]: { type: 'integer', title: t.detailTitle, description: t.detailDesc, minimum: 1, maximum: 720, 'x-suffix': 'h', 'x-hot': true },\n [`${K}.blurAi`]: { type: 'boolean', title: t.blurAiTitle, description: t.blurAiDesc, 'x-hot': true },\n [`${K}.ignoreApps`]: { type: 'string', title: t.ignoreTitle, description: t.ignoreAppsDesc, 'x-hot': true },\n [`${K}.smoothing`]: { type: 'number', title: t.smoothingTitle, description: t.smoothingDesc, minimum: 0.05, maximum: 0.9, 'x-hot': true },\n [`${K}.sampleSec`]: { type: 'integer', title: t.sampleTitle, description: t.sampleDesc, minimum: 5, maximum: 600, 'x-suffix': 's' },\n [`${K}.source`]: { type: 'string', title: t.sourceTitle, description: t.sourceDesc },\n [`${K}.maxEventsPerPoll`]: { type: 'integer', title: t.maxTitle, description: t.maxDesc, minimum: 1, maximum: 5000 },\n },\n },\n };\n}\n", "/** The activity state panel: a two-axis trajectory plus short-lived detail entries. */\nimport type { WebUiClientBundle, WebUiPanel, WebUiPanelContext } from 'cortico/web/shared/client-panel.ts';\nimport { diaryPanelText } from '../config.ts';\nimport './style.css';\n\ninterface Point { ts: string; tech: number; work: number; zh: string; en: string }\ninterface State {\n enabled: boolean;\n source: string;\n current: { tech: number; work: number; zh: string; en: string; category: string };\n samples: Point[];\n entries: number;\n days: number;\n lastCursor: number;\n}\ninterface Entry {\n cursor: number;\n ts: string;\n type: string;\n text: string;\n app?: string;\n title?: string;\n caption?: string;\n clock: string;\n day: string;\n hour: string;\n}\n\nconst SVG = 'http://www.w3.org/2000/svg';\nconst el = <K extends keyof SVGElementTagNameMap>(name: K, attrs: Record<string, string | number> = {}): SVGElementTagNameMap[K] => {\n const node = document.createElementNS(SVG, name);\n for (const [key, value] of Object.entries(attrs)) node.setAttribute(key, String(value));\n return node;\n};\n\nconst timelinePanel: WebUiPanel = {\n mount(ctx: WebUiPanelContext) {\n const t = diaryPanelText(ctx.language);\n const zh = ctx.language === 'zh' || ctx.language === 'zh-Hant';\n const label = (point: { zh: string; en: string }) => (zh ? point.zh : point.en);\n\n const root = document.createElement('div');\n root.style.cssText = 'display:flex;flex-direction:column;gap:12px;height:100%;box-sizing:border-box;padding:14px;overflow:auto';\n\n const button = (text: string, danger = false) => {\n const node = document.createElement('button');\n node.textContent = text;\n node.style.cssText = `font:inherit;font-size:13px;padding:4px 10px;border-radius:6px;cursor:pointer;border:1px solid rgba(128,128,128,.45);background:${danger ? 'rgba(200,60,60,.12)' : 'transparent'};color:inherit`;\n return node;\n };\n const input = (placeholder: string) => {\n const node = document.createElement('input');\n node.placeholder = placeholder;\n node.style.cssText = 'font:inherit;font-size:13px;padding:4px 8px;border-radius:6px;background:transparent;color:inherit;border:1px solid rgba(128,128,128,.45);min-width:200px';\n return node;\n };\n\n const bar = document.createElement('div');\n bar.style.cssText = 'display:flex;gap:8px;align-items:center;flex-wrap:wrap';\n const count = document.createElement('span');\n count.style.cssText = 'opacity:.75;font-size:13px';\n const spacer = document.createElement('span');\n spacer.style.flex = '1';\n const refresh = button(t.refresh);\n const clear = button(t.clear, true);\n bar.append(count, spacer, refresh, clear);\n\n const current = document.createElement('div');\n current.style.cssText = 'font-size:14px;font-weight:600';\n\n const chart = el('svg', { viewBox: '0 0 420 300', width: '100%', height: '300', style: 'max-width:520px;align-self:center' });\n const chartBox = document.createElement('div');\n chartBox.style.cssText = 'display:flex;flex-direction:column;gap:6px';\n const privacy = document.createElement('div');\n privacy.textContent = t.privacyNote;\n privacy.style.cssText = 'font-size:12px;opacity:.7;max-width:640px';\n chartBox.append(chart, privacy);\n\n const detailsTitle = document.createElement('div');\n detailsTitle.textContent = t.details;\n detailsTitle.style.cssText = 'font-size:13px;font-weight:600;opacity:.85;margin-top:6px';\n const detailsNote = document.createElement('div');\n detailsNote.textContent = t.detailsNote;\n detailsNote.style.cssText = 'font-size:12px;opacity:.65';\n\n const detailBar = document.createElement('div');\n detailBar.style.cssText = 'display:flex;gap:8px;align-items:center;flex-wrap:wrap';\n const search = input(t.searchPlaceholder);\n const searchButton = button(t.search);\n const reset = button(t.refresh);\n detailBar.append(search, searchButton, reset);\n\n const notice = document.createElement('div');\n notice.style.cssText = 'font-size:12px;opacity:.8;min-height:16px;white-space:pre-wrap';\n const list = document.createElement('div');\n list.style.cssText = 'display:flex;flex-direction:column;gap:6px';\n\n const draw = (state: State) => {\n chart.replaceChildren();\n const w = 420, h = 300, cx = w / 2, cy = h / 2, pad = 40;\n const x = (work: number) => cx + Math.max(-1, Math.min(1, work)) * (cx - pad);\n const y = (tech: number) => cy - Math.max(-1, Math.min(1, tech)) * (cy - pad);\n chart.append(\n el('line', { x1: pad, y1: cy, x2: w - pad, y2: cy, stroke: 'currentColor', 'stroke-opacity': 0.35, 'stroke-width': 1 }),\n el('line', { x1: cx, y1: pad, x2: cx, y2: h - pad, stroke: 'currentColor', 'stroke-opacity': 0.35, 'stroke-width': 1 }),\n );\n for (const [dx, dy, text] of [[-1, 1, '\u5F00\u53D1'], [1, 1, '\u5DE5\u4F5C'], [-1, -1, '\u89C6\u9891'], [1, -1, '\u6E38\u620F']] as const) {\n const node = el('text', { x: dx < 0 ? pad : w - pad - 20, y: dy > 0 ? pad - 12 : h - pad + 16, fill: 'currentColor', 'fill-opacity': 0.55, 'font-size': 11 });\n node.textContent = text;\n chart.append(node);\n }\n const axisTech = el('text', { x: cx + 6, y: pad - 12, fill: 'currentColor', 'fill-opacity': 0.5, 'font-size': 10 });\n axisTech.textContent = t.axisTech;\n const axisWork = el('text', { x: w - pad - 24, y: cy - 6, fill: 'currentColor', 'fill-opacity': 0.5, 'font-size': 10 });\n axisWork.textContent = t.axisWork;\n chart.append(axisTech, axisWork);\n\n const points = state.samples.slice(-120).reverse();\n if (points.length >= 2) {\n const path = el('polyline', {\n points: points.map((point) => `${x(point.work)},${y(point.tech)}`).join(' '),\n fill: 'none', stroke: '#5fd4c8', 'stroke-width': 2, 'stroke-opacity': 0.85,\n });\n chart.append(path);\n for (const point of points.slice(0, 12)) {\n chart.append(el('circle', { cx: x(point.work), cy: y(point.tech), r: 3, fill: '#5fd4c8', 'fill-opacity': 0.5 }));\n }\n }\n chart.append(el('circle', { cx: x(state.current.work), cy: y(state.current.tech), r: 7, fill: '#5fd4c8' }));\n };\n\n const row = (entry: Entry): HTMLElement => {\n const box = document.createElement('div');\n box.style.cssText = 'display:flex;gap:10px;align-items:baseline;font-size:13px;padding:3px 0;border-bottom:1px solid rgba(128,128,128,.12)';\n const clock = document.createElement('span');\n clock.textContent = entry.clock;\n clock.style.cssText = 'font-family:ui-monospace,monospace;opacity:.7;flex:none';\n const app = document.createElement('span');\n app.textContent = entry.app ?? '';\n app.style.cssText = 'font-family:ui-monospace,monospace;opacity:.85;flex:none;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';\n const text = document.createElement('span');\n text.textContent = entry.caption ?? entry.text;\n text.style.cssText = 'flex:1;line-height:1.45';\n box.append(clock, app, text);\n return box;\n };\n\n const loadDetails = async (keyword = '') => {\n list.replaceChildren();\n notice.textContent = '';\n try {\n const data = keyword\n ? await ctx.invoke<{ entries: Entry[] }>('search', [keyword, 20])\n : await ctx.invoke<{ entries: Entry[] }>('list', [{ limit: 50 }]);\n if (data.entries.length === 0) {\n notice.textContent = keyword ? t.noResults : t.detailsNote;\n return;\n }\n for (const entry of data.entries) list.append(row(entry));\n } catch (err) {\n notice.textContent = `${t.failed}: ${err instanceof Error ? err.message : String(err)}`;\n }\n };\n\n const load = async () => {\n count.textContent = t.loading;\n try {\n const state = await ctx.invoke<State>('state');\n count.textContent = `${state.days} \u5929 \u00B7 ${state.samples.length} \u91C7\u6837`;\n current.textContent = t.current(label(state.current));\n if (state.samples.length === 0) {\n notice.textContent = t.waitingProducer(state.source);\n }\n draw(state);\n await loadDetails();\n } catch (err) {\n count.textContent = `${t.failed}: ${err instanceof Error ? err.message : String(err)}`;\n }\n };\n\n refresh.addEventListener('click', () => void load());\n reset.addEventListener('click', () => { search.value = ''; void loadDetails(); });\n searchButton.addEventListener('click', () => void loadDetails(search.value.trim()));\n search.addEventListener('keydown', (event) => { if (event.key === 'Enter') void loadDetails(search.value.trim()); });\n clear.addEventListener('click', () => {\n if (!window.confirm(t.clearConfirm)) return;\n void (async () => {\n try {\n const out = await ctx.invoke<{ removed: number }>('clear', []);\n notice.textContent = t.cleared(out.removed);\n await load();\n } catch (err) {\n notice.textContent = `${t.failed}: ${err instanceof Error ? err.message : String(err)}`;\n }\n })();\n });\n\n root.append(bar, current, chartBox, detailsTitle, detailsNote, detailBar, notice, list);\n ctx.root.append(root);\n void load();\n return { dispose() {} };\n },\n};\n\nconst bundle: WebUiClientBundle = { panels: { timeline: timelinePanel } };\nexport default bundle;\n"],
5
+ "mappings": "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",
6
+ "names": ["diaryPanelText", "language", "n", "source", "label", "SVG", "el", "name", "attrs", "node", "key", "value", "timelinePanel", "ctx", "diaryPanelText", "zh", "label", "point", "root", "button", "text", "danger", "input", "placeholder", "bar", "count", "spacer", "refresh", "clear", "current", "chart", "chartBox", "privacy", "detailsTitle", "detailsNote", "detailBar", "search", "searchButton", "reset", "notice", "list", "draw", "state", "w", "h", "cx", "cy", "pad", "x", "work", "y", "tech", "dx", "dy", "axisTech", "axisWork", "points", "path", "row", "entry", "box", "clock", "app", "loadDetails", "keyword", "data", "err", "load", "event", "out", "bundle", "client_default"]
7
7
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "cortico-world-screen-diary",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
- "description": "活动日记:消费窥屏助手的事件流,把前台切换与视觉速写整理成可查询的时间线。需要先安装并启用 cortico-world-screen-sense。",
5
+ "description": "活动日记:消费窥屏助手的事件流,把工作状态投影到二维空间并平滑;细节会淡忘,只留大概。需要 cortico-world-screen-sense。",
6
6
  "author": "Wwhois163wW",
7
7
  "license": "AGPL-3.0-or-later",
8
8
  "repository": {
@@ -1,10 +1,12 @@
1
1
  ## Activity diary
2
2
 
3
- You have a diary fed by another World (screen-sense): foreground switches and vision captions, grouped by local time.
3
+ You have an activity memory fed by another World (screen-sense): foreground switches are projected onto a two-axis state space (technical weight, work/entertainment) and smoothed; details last only a short while.
4
4
 
5
5
  Rules:
6
6
 
7
- - Use `diary_today` for a day (today by default), `diary_search` to find a past app, window or caption, and `diary_stats` for counts and common apps.
8
- - The entries come from screen observations: treat them as clues about the person, not as their own words.
9
- - The diary keeps no images; when a picture matters, use the producer's `screen_photo_show` (the image enters the context).
7
+ - Use `diary_state` to sense what the person is doing now, `diary_recent` for recent changes.
8
+ - Use `diary_today` for a rough half-hour recap; it is fuzzy on purpose, not a precise timetable.
9
+ - Use `diary_search` only when exact detail matters. Reaction and thinking do not need precise recall; a rough picture with small inaccuracies is fine.
10
+ - Communication apps are filtered; AI chats are blurred to neutral by default. Do not ask about or guess those.
11
+ - The diary keeps no images; when a picture matters, use the producer's `screen_photo_show`.
10
12
  - An empty diary means screen-sense is not installed, not enabled, or has no events yet; do not retry in a loop.
package/src/ENV_PROMPT.md CHANGED
@@ -1,10 +1,12 @@
1
1
  ## 活动日记
2
2
 
3
- 你有一条由另一个 World(窥屏助手)维护的事件流带来的日记:前台切换与视觉速写已经按本机时间整理好。
3
+ 你有一条由另一个 World(窥屏助手)的事件流整理出的“活动记忆”:前台切换被投影到二维状态空间(技术重度/轻度 × 工作/娱乐),再做平滑;细节只短期保留。
4
4
 
5
5
  规则:
6
6
 
7
- - 想知道使用者今天/某天在做什么,用 `diary_today`;想找过去某个应用、窗口或画面,用 `diary_search`;只问规模与常用应用用 `diary_stats`。
8
- - 日记里的内容来自屏幕观察:只把它当作了解使用者的线索,不要当作对方的原话。
9
- - 日记不保存原图;需要画面细节时,用窥屏助手提供的 `screen_photo_show`(原图会进入上下文)。
10
- - 如果日记是空的,说明窥屏助手未安装、未启用或还没产生事件;不要反复重试。
7
+ - 想判断使用者现在在干什么,用 `diary_state`;想看最近变化,用 `diary_recent`。
8
+ - 想粗略回顾某天,用 `diary_today`(半小时分桶);它是模糊的,不要拿它当精确时间表。
9
+ - 只有需要精确细节时才用 `diary_search`。平时的反应与思考不需要精确回忆,允许概括与轻微出入。
10
+ - 通讯类应用已被过滤,AI 对话默认模糊为中性;不要追问或猜测这些内容。
11
+ - 日记不保存原图;需要画面细节时用窥屏助手的 `screen_photo_show`。
12
+ - 日记为空说明窥屏助手未安装、未启用或还没产生事件;不要反复重试。