cortico-world-screen-diary 0.1.0 → 0.6.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,67 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0
4
+
5
+ - 词袋正式参与状态计算:应用词(按覆盖后的类别)与标题词一起,按窗口出现频次与时间衰减投票出质心
6
+ - 新增 `useWords`(默认开)、`wordWindowMinutes`(15)、`wordHalfLifeMinutes`(5)配置;关闭时回退到原来的类别 EMA
7
+ - `diary_state` 与时间线面板显示当前窗口高频词;应用词坐标/权重跟随类型覆盖,标题词跟随内置词库
8
+ - 雷达轨迹改为词袋质心经 EMA 平滑后的结果
9
+ ## 0.5.4
10
+
11
+ - 简介与 README 互相指路:把「窥屏助手」作为配套扩展放在显眼位置,说明安装顺序与可选关系
12
+ - keywords 增加 screen-sense,方便市场搜索
13
+ ## 0.5.3
14
+
15
+ - flow 事件改用本地时区时间戳(此前 checkin/reengage 写成 UTC,日志里比实际早 9 小时)
16
+ - checkin 尚未投递时,若 2 分钟内马上 reengage,调用 `withdrawPending` 撤回,避免模型同时收到“可能离开”和“回来了”
17
+ ## 0.5.2
18
+
19
+ - 词汇表改用 CSS Grid:表头与数据列对齐,数字列右对齐
20
+ - 应用词行显示类型/衰减控件;标题词行明确标注「标题词 · 只读」,不再看起来像缺控件
21
+ ## 0.5.1
22
+
23
+ - 中文分词从 2-gram 改为 ICU `Intl.Segmenter`:不再产生 能体/体记/忆与 这类垃圾词
24
+ - 补充中文停用词与「智能/记忆/行为/解析」等词库坐标
25
+ - 词汇表默认隐藏权重 0.1 的未知词,可勾选「显示未知词」查看全部
26
+ ## 0.5.0
27
+
28
+ - 阶段 1:应用表升级为统一「词汇与衰减」表,应用名与窗口标题词同权
29
+ - 新增极简分词器:进程名去 .exe、英文/数字切词、中文 2-gram、停用词过滤
30
+ - 词库含坐标 [工作/娱乐, 轻/重] 与置信权重;只读频次诊断,不改变现有分类行为
31
+ - 只保存 token 与计数,不保存标题原文;通讯类在分词前过滤
32
+ ## 0.4.2
33
+
34
+ - 应用与衰减表增加表头:应用 / 次数 / 默认类型 / 类型覆盖 / 流枯竭窗口(分钟)/ 操作
35
+ - 「类别默认」输入改为「默认」,并在悬停说明:留空跟随类型默认窗口
36
+ - 新增「开发者选项」页:当前 flow、规则列表(reengage/checkin)、配置与计数诊断
37
+ ## 0.4.1
38
+
39
+ - 修复「应用与衰减」面板读取失败:补上 `apps` / `setApp` 的 invoke 分支
40
+ ## 0.4.0
41
+
42
+ - 新增「应用与衰减」面板:列出出现过的应用、默认分类、出现次数;逐行覆盖类型(词性)与流枯竭窗口
43
+ - 新工具 `diary_apps`(列出)与 `diary_set_app`(设置/清除覆盖),宠物也能改
44
+ - 覆盖写入 `apps.json`;不改窗口标题存储
45
+ - 覆盖分类会同时改变投影与过滤(例如把某个应用从过滤改为开发)
46
+ ## 0.3.0
47
+
48
+ - 新增 flow 层:只用窗口切换的时间梯度判断 busy / settling / depleted / away,不监听键鼠
49
+ - 按应用类型的衰退窗口:开发/文书 20 分、视频 30、游戏 12、浏览器 8(基准可配)
50
+ - 事件:`screen-diary.reengage`(默认开,piggyback)与可选的 `screen-diary.checkin`(默认关闭,debounce,冷却 60 分钟)
51
+ - 雷达去掉象限文字标签,只保留抽象轨迹
52
+ - `casual` 挂机/轻娱乐分类
53
+ ## 0.2.1
54
+
55
+ - 升级回填:首次启动 0.2.x 时,若还没有状态采样,把 0.1.x 的旧细节按同一投影/过滤重放为状态采样
56
+ - 隐私迁移:回填时删除 0.1.x 遗留的通讯类与模糊 AI 条目
57
+ - 英文简介改成状态空间描述
58
+ ## 0.2.0
59
+
60
+ - 二维状态空间:技术轴(重度/轻度)× 办公轴(工作/娱乐),应用先投影再 EMA 平滑
61
+ - 隐私过滤:通讯类应用直接过滤;AI 对话默认模糊为中性,可配置
62
+ - 细节遗忘:标题/速写/正文只保留 `detailHours`,之后只留平滑状态
63
+ - 工具改为 `diary_state`、`diary_recent`、`diary_today`(半小时分桶)、`diary_search`(显式回忆)、`diary_stats`
64
+ - 面板改为二维轨迹图 + 当前象限 + 短期细节
3
65
  ## 0.1.0
4
66
 
5
67
  - 第一个版本:消费窥屏助手的事件流,整理成本地活动日记
package/README.md CHANGED
@@ -4,55 +4,137 @@ 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
+ 本包不做任何采集:前台窗口、截图和视觉速写都来自 [窥屏助手](https://www.npmjs.com/package/cortico-world-screen-sense)(`cortico-world-screen-sense`)。它把 `screen-sense.focus` / `screen-sense.screenshot` 写进事件流,本包只读事件、做状态投影与记忆整理。
24
24
 
25
- - `screen-sense.focus`:`meta = { kind: 'focus', app, title, previousMs }`
26
- - `screen-sense.screenshot`:`meta = { kind: 'screenshot', app, title, photoId, caption }`
25
+ - 只用窥屏助手也能工作;本包是它的**可选消费方**,用来把事件变成可回顾的平滑记忆。
26
+ - 没装窥屏助手时,活动日记不会报错,只会提示等待事件源。
27
+ - 安装顺序:先窥屏助手,再活动日记。
27
28
 
28
- 只保存文本与结构化字段;**事件里没有原图**。生产方改名/删除事件类型属于 major,新增字段属于 minor。
29
+ GitHub:[cortico-world-screen-sense](https://github.com/Wwhois163wW/cortico-world-screen-sense) · [cortico-world-screen-diary](https://github.com/Wwhois163wW/cortico-world-screen-diary)
29
30
 
30
- 读取游标保存在本 World 数据目录,重启后从上次位置继续;第一次启动可按 `lookbackMinutes` 回填一段历史。记录按 `keepDays` 保留,超过自动淘汰。
31
+ ## 二维状态空间
32
+
33
+ - **纵轴 技术轴**:重度(开发/折腾)↔ 轻度(看视频/轻办公)
34
+ - **横轴 办公轴**:工作 ↔ 娱乐
35
+
36
+ 每个前台应用先投影成 `(tech, work)`,再按 `smoothing` 做 EMA 平滑,得到连续状态与轨迹。窗口标题只用来判断浏览器在哪个象限(github → 开发,Bilibili → 视频),不会长期保存。
37
+
38
+ | 类别 | 示例 | tech | work |
39
+ | --- | --- | --- | --- |
40
+ | 开发 | IDE、终端、git、docker、github、npm | +0.8 | +0.6 |
41
+ | 游戏 | Steam、Minecraft、LOL | +0.6 | -0.8 |
42
+ | 文书 | Word/Excel/PDF/Notion | -0.2 | +0.8 |
43
+ | 视频 | bilibili、YouTube、播放器 | -0.5 | -0.8 |
44
+ | 浏览器 | Chrome/Edge | 按标题推断,默认 (0,0) | |
45
+
46
+ ## 词袋状态(已参与计算)
47
+
48
+ 面板里的「词汇与衰减」把**应用名和窗口标题词放在同一张表**:
49
+
50
+ - 列:词、次数、坐标 `[工作/娱乐, 轻/重]`、权重、类型/衰减(仅应用词)
51
+ - 应用名去掉 `.exe` 后整体是一个 token;标题按英文/数字切词、中文 2-gram、停用词过滤
52
+ - 强特征词(如 `bilibili`、`deepseek`、`harness`)权重高;中性容器(如 `chrome`)权重低
53
+ - 应用词可覆盖类型与流枯竭窗口;标题词目前只读
54
+ - 只保存 token 与计数,不保存标题原文;通讯类在分词前过滤
55
+
56
+ 这一阶段只做诊断,**不改变现有分类与投影**。下一步会用“切换事件 + 时间衰减”做加权词袋质心,与现有 EMA 轨迹并行对比,再决定是否转正。
57
+
58
+ 词袋现在**正式参与状态计算**(`useWords: true`):
59
+
60
+ - 每个应用词带它覆盖后的类别坐标与权重;标题词用内置词库坐标与权重
61
+ - 窗口内每个词按出现次数与时间衰减(半衰期默认 5 分钟)投票,得到 `(工作, 技术)` 质心
62
+ - 质心再经 `smoothing` 的 EMA 平滑,写入轨迹与采样
63
+ - 关闭 `useWords` 时回退到原来的“应用类别投影 + EMA”
64
+ - `diary_state` 与面板显示当前窗口的高频词,作为语义指纹
65
+
66
+ ## 流枯竭:搭话时机,不是监控
67
+
68
+ 不使用键鼠钩子,也不做全局输入监听;只利用**前台窗口切换的时间梯度**:
69
+
70
+ - 切换密集 = `busy`,不打扰
71
+ - 静默超过该应用的**衰退窗口** = `depleted`(专注或离开,不强行区分)
72
+ - `depleted` 后出现新切换 = `screen-diary.reengage`(piggyback),Coo 可以说“回来啦”
73
+ - 进入 `depleted` 时若开启主动搭话,发 `screen-diary.checkin`(debounce,默认关闭,带冷却);之后仍无切换则内部确认 `away`,不再发事件
74
+
75
+ 衰退窗口按应用类型乘倍率:开发/文书 20 分钟、视频 30 分钟、游戏 12 分钟、浏览器 8 分钟(基准可配)。flow 状态只服务当次交互,不进入雷达、不做长期画像。
76
+
77
+ ## 隐私与遗忘
78
+
79
+ - **通讯类直接过滤**:微信、QQ、Discord、Telegram、Slack、Teams、Zoom、邮件客户端等不投影、不落盘、不进入状态。
80
+ - **AI 对话默认模糊为中性**:不记录具体窗口标题;可在配置里改为额外过滤或正常投影。
81
+ - **细节会淡忘**:窗口标题、速写、正文只保留 `detailHours`(默认 72 小时);之后只剩平滑状态与日摘要,保留 `keepDays`(默认 30 天)。
82
+ - 状态是“大概在干什么”,允许不精确;需要精确回忆时才用显式搜索。
83
+
84
+ ## 从 0.1 升级
85
+
86
+ 0.2.0 之前只保存详细条目,没有状态采样。升级到 0.2.x 后第一次启动会自动回填:
87
+
88
+ - 旧条目按同一套投影与平滑重放成状态采样(每 1 分钟最多一个点)
89
+ - 回填时顺带删除旧数据里的通讯类与模糊 AI 条目
90
+ - 如果之前已经产生过 0.2 的采样,则不会重复回填
31
91
 
32
92
  ## 工具
33
93
 
34
- | 工具 | 作用 |
94
+ | 工具 | 用途 |
35
95
  | --- | --- |
36
- | `diary_today` | 读某一天(默认今天)的日记,按小时列出前台切换与速写 |
37
- | `diary_search` | 按应用名、窗口标题、速写或正文搜索,新的在前 |
38
- | `diary_stats` | 总条数、覆盖天数、最常出现的应用 |
96
+ | `diary_state` | 当前平滑状态 + 最近 10 个采样;反应类场景默认用它 |
97
+ | `diary_recent` | 最近 N 个平滑采样(默认 10) |
98
+ | `diary_today` | 某天按半小时分桶的平滑状态,默认今天 |
99
+ | `diary_search` | **显式回忆**:在短期细节记录里搜应用/标题/速写,最多 5 条 |
100
+ | `diary_stats` | 覆盖天数、状态采样数、主要状态 |
101
+
102
+ ## 事件
103
+
104
+ | 事件 | 触发 | 默认 |
105
+ | --- | --- | --- |
106
+ | `screen-diary.reengage` | 长静默后重新出现窗口切换 | 开(piggyback) |
107
+ | `screen-diary.checkin` | 流枯竭且开启主动搭话 | 关(debounce,冷却 60 分钟) |
39
108
 
40
109
  ## 配置(`worlds.screen-diary.*`)
41
110
 
42
111
  | 键 | 默认 | 含义 |
43
112
  | --- | --- | --- |
44
- | `enabled` | `false` | 是否轮询事件流(World 总览里启用后生效) |
113
+ | `enabled` | `false` | 是否轮询事件流 |
45
114
  | `pollMs` | `5000` | 检查新事件的间隔 |
46
115
  | `lookbackMinutes` | `60` | 首次启动回填最近多少分钟 |
47
- | `keepDays` | `30` | 日记保留天数 |
116
+ | `keepDays` | `30` | 平滑状态与日摘要保留天数 |
117
+ | `detailHours` | `72` | 细节(标题/速写/正文)保留小时 |
118
+ | `blurAi` | `true` | AI 对话是否模糊为中性 |
119
+ | `ignoreApps` | `""` | 额外过滤的应用名片段 |
120
+ | `smoothing` | `0.25` | EMA 平滑强度,0.05 平滑、0.9 灵敏 |
121
+ | `sampleSec` | `30` | 两次状态采样最小间隔 |
122
+ | `useWords` | `true` | 是否用词袋质心计算状态(关闭回退类别 EMA) |
123
+ | `wordWindowMinutes` | `15` | 词袋窗口长度 |
124
+ | `wordHalfLifeMinutes` | `5` | 词权重衰减半衰期 |
48
125
  | `source` | `screen-sense` | 生产方 World id |
49
- | `maxEventsPerPoll` | `500` | 单次导入上限,超出留给下一拍 |
126
+ | `maxEventsPerPoll` | `500` | 单次导入上限 |
127
+ | `flowDecayMinutes` | `20` | 流枯竭基准窗口,按应用类型乘倍率 |
128
+ | `checkInEnabled` | `false` | 流枯竭时是否允许 Coo 主动搭话 |
129
+ | `awayAfterMinutes` | `10` | 搭话后仍无切换,多久确认离开 |
130
+ | `checkInCooldownMinutes` | `60` | 两次主动搭话的最小间隔 |
50
131
 
51
132
  ## 隐私
52
133
 
53
- - 本 World 不截屏、不读窗口、不联网;只读宿主里已有的文本事件。
54
- - 日记条目保存在 `data/diary/`,可随时在面板清空;清空不会影响窥屏助手的相册。
55
- - 事件内容来自屏幕观察,属于个人数据;窥屏助手的 `sensitive-data` 警示同样适用于这些条目。
134
+ - 本 World 不截屏、不联网;只读宿主里已有的文本事件,并立即投影为分类与两个数值。
135
+ - 通讯类应用完全跳过;AI 对话默认不保存标题。
136
+ - 日记数据在 `data/diary/`,可随时在面板清空;不影响窥屏助手的相册。
137
+ - 生产方的 `sensitive-data` 警示同样适用于这里的短期细节。
56
138
 
57
139
  ## 开发
58
140
 
@@ -70,6 +152,20 @@ pnpm describe:extension <本包目录>
70
152
  pnpm check:extension <本包目录>
71
153
  ```
72
154
 
155
+ ## 发布
156
+
157
+ 本包目前**手动发布**,不依赖 tag / 可信发布:
158
+
159
+ ```bash
160
+ pnpm install
161
+ pnpm run typecheck
162
+ pnpm run test
163
+ pnpm run build
164
+ npm publish --access public
165
+ ```
166
+
167
+ > 仓库没有自动发布 workflow;不要给本包打 `v*` tag,直接 `npm publish` 即可,它会先跑 `prepublishOnly` 检查。
168
+
73
169
  ## 许可
74
170
 
75
171
  AGPL-3.0-or-later。
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,18 +97,99 @@
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."
102
+ }
103
+ },
104
+ {
105
+ "name": "diary_apps",
106
+ "description": "List the applications seen most recently, their category and their per-app flow-decay override (if any).",
107
+ "parameters": {
108
+ "type": "object",
109
+ "properties": {
110
+ "limit": {
111
+ "type": "integer",
112
+ "minimum": 1,
113
+ "maximum": 50,
114
+ "description": "Most apps to return; default 15."
115
+ }
116
+ }
117
+ },
118
+ "tags": [
119
+ "read"
120
+ ],
121
+ "summary": {
122
+ "zh": "列出最近见过的应用、默认分类与逐应用覆盖项。",
123
+ "en": "Lists recently seen apps, their default category and per-app overrides."
124
+ }
125
+ },
126
+ {
127
+ "name": "diary_set_app",
128
+ "description": "Set or clear the per-app category (word class) and flow-decay minutes when the default classification feels wrong for this person.",
129
+ "parameters": {
130
+ "type": "object",
131
+ "properties": {
132
+ "app": {
133
+ "type": "string",
134
+ "description": "Application name exactly as listed by diary_apps."
135
+ },
136
+ "category": {
137
+ "type": "string",
138
+ "enum": [
139
+ "develop",
140
+ "game",
141
+ "casual",
142
+ "office",
143
+ "video",
144
+ "browser",
145
+ "ai",
146
+ "neutral",
147
+ "filtered"
148
+ ],
149
+ "description": "Override category; omit to leave unchanged."
150
+ },
151
+ "decayMinutes": {
152
+ "type": "integer",
153
+ "minimum": 1,
154
+ "maximum": 180,
155
+ "description": "Per-app flow-decay minutes; 0 clears the override."
156
+ }
157
+ },
158
+ "required": [
159
+ "app"
160
+ ]
161
+ },
162
+ "tags": [
163
+ "write"
164
+ ],
165
+ "summary": {
166
+ "zh": "设置某个应用的类型(词性)与流枯竭窗口;0 清除覆盖。",
167
+ "en": "Sets an app category or flow-decay override; 0 clears it."
168
+ }
169
+ }
170
+ ],
171
+ "events": [
172
+ {
173
+ "type": "screen-diary.reengage",
174
+ "description": {
175
+ "zh": "窗口切换打断了专注或离开:用户回来了,适合轻声搭话。",
176
+ "en": "A window switch interrupted focus or absence: the user is back, a good moment for a light greeting."
177
+ }
178
+ },
179
+ {
180
+ "type": "screen-diary.checkin",
181
+ "description": {
182
+ "zh": "流枯竭后的可选主动搭话(默认关闭,有冷却)。",
183
+ "en": "An optional proactive check-in when the flow depletes (off by default, rate limited)."
65
184
  }
66
185
  }
67
186
  ],
68
- "events": [],
69
187
  "prompts": [
70
188
  {
71
189
  "key": "worlds.screen-diary.envPrompt",
72
190
  "title": "活动日记",
73
191
  "role": "envPrompt",
74
- "content": "## 活动日记\n\n你有一条由另一个 World(窥屏助手)维护的事件流带来的日记:前台切换与视觉速写已经按本机时间整理好。\n\n规则:\n\n- 想知道使用者今天/某天在做什么,用 `diary_today`;想找过去某个应用、窗口或画面,用 `diary_search`;只问规模与常用应用用 `diary_stats`。\n- 日记里的内容来自屏幕观察:只把它当作了解使用者的线索,不要当作对方的原话。\n- 日记不保存原图;需要画面细节时,用窥屏助手提供的 `screen_photo_show`(原图会进入上下文)。\n- 如果日记是空的,说明窥屏助手未安装、未启用或还没产生事件;不要反复重试。\n"
192
+ "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- screen-diary.reengage 表示对方在安静一段时间后回来了,适合轻声打招呼,不要连续追问。\\n- screen-diary.checkin 表示流枯竭、可能在专注或离开;可以提醒休息或轻声确认,一次就够。\\n- 日记为空说明窥屏助手未安装、未启用或还没产生事件;不要反复重试。\n"
75
193
  }
76
194
  ]
77
195
  }
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 B(c){return c==="zh"||c==="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:r=>`\u5DF2\u5220\u9664 ${r} \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:r=>`\u8FD8\u6CA1\u6709\u6536\u5230\u6765\u81EA ${r} \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:r=>`\u5F53\u524D\uFF1A${r}`,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:r=>`Deleted ${r} records`,empty:"No entries yet. Install and enable screen-sense, switch apps a few times, then come back.",waitingProducer:r=>`No events from ${r} yet; check that screen-sense is installed and enabled.`,loading:"Loading\u2026",failed:"Could not load",stateTitle:"Current state",current:r=>`Now: ${r}`,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 G="http://www.w3.org/2000/svg",P=(c,e={})=>{let r=document.createElementNS(G,c);for(let[T,f]of Object.entries(e))r.setAttribute(T,String(f));return r},j={mount(c){let e=B(c.language),r=c.language==="zh"||c.language==="zh-Hant",T=t=>r?t.zh:t.en,f=document.createElement("div");f.style.cssText="display:flex;flex-direction:column;gap:12px;height:100%;box-sizing:border-box;padding:14px;overflow:auto";let d=(t,a=!1)=>{let w=document.createElement("button");return w.textContent=t,w.style.cssText=`font:inherit;font-size:13px;padding:4px 10px;border-radius:6px;cursor:pointer;border:1px solid rgba(128,128,128,.45);background:${a?"rgba(200,60,60,.12)":"transparent"};color:inherit`,w},k=t=>{let a=document.createElement("input");return a.placeholder=t,a.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",a},v=document.createElement("div");v.style.cssText="display:flex;gap:8px;align-items:center;flex-wrap:wrap";let y=document.createElement("span");y.style.cssText="opacity:.75;font-size:13px";let E=document.createElement("span");E.style.flex="1";let z=d(e.refresh),u=d(e.clear,!0);v.append(y,E,z,u);let $=document.createElement("div");$.style.cssText="font-size:14px;font-weight:600";let C=document.createElement("div");C.style.cssText="font-size:12px;opacity:.7";let o=P("svg",{viewBox:"0 0 420 300",width:"100%",height:"300",style:"max-width:520px;align-self:center"}),n=document.createElement("div");n.style.cssText="display:flex;flex-direction:column;gap:6px";let i=document.createElement("div");i.textContent=e.privacyNote,i.style.cssText="font-size:12px;opacity:.7;max-width:640px",n.append(o,i);let s=document.createElement("div");s.textContent=e.details,s.style.cssText="font-size:13px;font-weight:600;opacity:.85;margin-top:6px";let x=document.createElement("div");x.textContent=e.detailsNote,x.style.cssText="font-size:12px;opacity:.65";let l=document.createElement("div");l.style.cssText="display:flex;gap:8px;align-items:center;flex-wrap:wrap";let h=k(e.searchPlaceholder),D=d(e.search),b=d(e.refresh);l.append(h,D,b);let p=document.createElement("div");p.style.cssText="font-size:12px;opacity:.8;min-height:16px;white-space:pre-wrap";let g=document.createElement("div");g.style.cssText="display:flex;flex-direction:column;gap:6px";let I=t=>{o.replaceChildren();let a=420,w=300,M=a/2,S=w/2,L=40,R=N=>M+Math.max(-1,Math.min(1,N))*(M-L),U=N=>S-Math.max(-1,Math.min(1,N))*(S-L);o.append(P("line",{x1:L,y1:S,x2:a-L,y2:S,stroke:"currentColor","stroke-opacity":.35,"stroke-width":1}),P("line",{x1:M,y1:L,x2:M,y2:w-L,stroke:"currentColor","stroke-opacity":.35,"stroke-width":1}));let F=t.samples.slice(-120).reverse();if(F.length>=2){let N=P("polyline",{points:F.map(H=>`${R(H.work)},${U(H.tech)}`).join(" "),fill:"none",stroke:"#5fd4c8","stroke-width":2,"stroke-opacity":.85});o.append(N);for(let H of F.slice(0,12))o.append(P("circle",{cx:R(H.work),cy:U(H.tech),r:3,fill:"#5fd4c8","fill-opacity":.5}))}o.append(P("circle",{cx:R(t.current.work),cy:U(t.current.tech),r:7,fill:"#5fd4c8"}))},W=t=>{let a=document.createElement("div");a.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 w=document.createElement("span");w.textContent=t.clock,w.style.cssText="font-family:ui-monospace,monospace;opacity:.7;flex:none";let M=document.createElement("span");M.textContent=t.app??"",M.style.cssText="font-family:ui-monospace,monospace;opacity:.85;flex:none;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";let S=document.createElement("span");return S.textContent=t.caption??t.text,S.style.cssText="flex:1;line-height:1.45",a.append(w,M,S),a},m=async(t="")=>{g.replaceChildren(),p.textContent="";try{let a=t?await c.invoke("search",[t,20]):await c.invoke("list",[{limit:50}]);if(a.entries.length===0){p.textContent=t?e.noResults:e.detailsNote;return}for(let w of a.entries)g.append(W(w))}catch(a){p.textContent=`${e.failed}: ${a instanceof Error?a.message:String(a)}`}},A=async()=>{y.textContent=e.loading;try{let t=await c.invoke("state");y.textContent=`${t.days} \u5929 \xB7 ${t.samples.length} \u91C7\u6837`,$.textContent=e.current(T(t.current)),C.textContent=t.top&&t.top.length?(r?"\u9AD8\u9891\u8BCD\uFF1A":"Top tokens: ")+t.top.map(a=>a.token).join(" \xB7 "):"",t.samples.length===0&&(p.textContent=e.waitingProducer(t.source)),I(t),await m()}catch(t){y.textContent=`${e.failed}: ${t instanceof Error?t.message:String(t)}`}};return z.addEventListener("click",()=>{A()}),b.addEventListener("click",()=>{h.value="",m()}),D.addEventListener("click",()=>{m(h.value.trim())}),h.addEventListener("keydown",t=>{t.key==="Enter"&&m(h.value.trim())}),u.addEventListener("click",()=>{window.confirm(e.clearConfirm)&&(async()=>{try{let t=await c.invoke("clear",[]);p.textContent=e.cleared(t.removed),await A()}catch(t){p.textContent=`${e.failed}: ${t instanceof Error?t.message:String(t)}`}})()}),f.append(v,$,C,n,s,x,l,p,g),c.root.append(f),A(),{dispose(){}}}},O=[["develop","\u5F00\u53D1","Development"],["office","\u6587\u4E66","Documents"],["video","\u89C6\u9891","Video"],["game","\u6E38\u620F","Gaming"],["casual","\u6302\u673A/\u8F7B\u5A31\u4E50","Casual"],["browser","\u6D4F\u89C8\u5668","Browser"],["ai","AI","AI"],["neutral","\u4E2D\u6027","Neutral"],["filtered","\u8FC7\u6EE4","Filtered"]],K={mount(c){let e=c.language==="zh"||c.language==="zh-Hant",r=document.createElement("div");r.style.cssText="display:flex;flex-direction:column;gap:10px;height:100%;box-sizing:border-box;padding:14px;overflow:auto";let T=document.createElement("div");T.textContent=e?"\u8BCD\u6C47\u4E0E\u8870\u51CF":"Words and decay",T.style.cssText="font-size:14px;font-weight:600";let f=document.createElement("div");f.textContent=e?"\u5E94\u7528\u540D\u4E0E\u7A97\u53E3\u6807\u9898\u8BCD\u540C\u8868\uFF1B\u5750\u6807 = [\u5DE5\u4F5C/\u5A31\u4E50, \u8F7B/\u91CD]\uFF0C\u6743\u91CD\u8D8A\u9AD8\u8D8A\u80FD\u62C9\u52A8\u72B6\u6001\u3002\u6807\u9898\u53EA\u4EE5\u8BCD\u9891\u5F62\u5F0F\u4FDD\u7559\u3002\u901A\u8BAF\u7C7B\u9ED8\u8BA4\u8FC7\u6EE4\uFF1B\u5E94\u7528\u8BCD\u53EF\u8986\u76D6\u7C7B\u578B\u4E0E\u6D41\u67AF\u7AED\u7A97\u53E3\u3002":"App names and title words share one table; coordinates are [work/entertainment, light/heavy] and heavier words pull the state more. Titles are kept only as token counts. Communication apps are filtered by default; app tokens can override category and decay.",f.style.cssText="font-size:12px;opacity:.7;max-width:800px";let d=document.createElement("div");d.style.cssText="font-size:12px;opacity:.75;min-height:16px";let k=document.createElement("label");k.style.cssText="display:flex;gap:6px;align-items:center;font-size:12px;opacity:.75;cursor:pointer";let v=document.createElement("input");v.type="checkbox";let y=document.createElement("span");y.textContent=e?"\u663E\u793A\u672A\u77E5\u8BCD\uFF08\u6743\u91CD 0.1\uFF09":"Show unknown tokens (weight 0.1)",k.append(v,y),v.addEventListener("change",()=>{C()});let E=document.createElement("div");E.style.cssText="display:flex;flex-direction:column;gap:6px";let z="minmax(150px,1.6fr) 52px 56px 56px 52px minmax(150px,1fr) 96px",u=()=>{let o=document.createElement("div");o.style.cssText="display:grid;grid-template-columns:"+z+";gap:8px;align-items:center;font-size:11px;opacity:.55;padding:4px 0;border-bottom:1px solid rgba(128,128,128,.3)";let n=[e?"\u8BCD":"token",e?"\u6B21\u6570":"count",e?"\u5DE5\u4F5C":"work",e?"\u6280\u672F":"tech",e?"\u6743\u91CD":"weight",e?"\u7C7B\u578B / \u8870\u51CF":"type / decay",e?"\u64CD\u4F5C":"action"];for(let i=0;i<n.length;i+=1){let s=document.createElement("span");s.textContent=n[i],i>=1&&i<=4&&(s.style.textAlign="right"),i===6&&(s.style.textAlign="right"),o.append(s)}return o},$=o=>{let n=document.createElement("div");n.style.cssText="display:grid;grid-template-columns:"+z+";gap:8px;align-items:center;font-size:13px;padding:5px 0;border-bottom:1px solid rgba(128,128,128,.14)";let i=document.createElement("span");i.textContent=o.token,i.style.cssText="font-family:ui-monospace,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";let s=document.createElement("span");s.textContent=o.count+" \xD7",s.style.cssText="opacity:.65;font-size:12px;text-align:right";let x=document.createElement("span");x.textContent=o.coord[0].toFixed(2),x.style.cssText="opacity:.7;font-size:12px;text-align:right;font-family:ui-monospace,monospace";let l=document.createElement("span");l.textContent=o.coord[1].toFixed(2),l.style.cssText="opacity:.7;font-size:12px;text-align:right;font-family:ui-monospace,monospace";let h=document.createElement("span");if(h.textContent=o.weight.toFixed(1),h.style.cssText="opacity:.7;font-size:12px;text-align:right;font-family:ui-monospace,monospace",n.append(i,s,x,l,h),o.kind!=="app"||!o.app){let m=document.createElement("span");m.textContent=e?"\u6807\u9898\u8BCD \xB7 \u53EA\u8BFB":"title word \xB7 read-only",m.style.cssText="font-size:11px;opacity:.45";let A=document.createElement("span");return A.textContent="\u2014",A.style.cssText="opacity:.3;text-align:right",n.append(m,A),n}let D=document.createElement("div");D.style.cssText="display:flex;gap:4px;align-items:center;min-width:0";let b=document.createElement("select");b.style.cssText="font:inherit;font-size:12px;padding:3px 4px;border-radius:5px;background:transparent;color:inherit;border:1px solid rgba(128,128,128,.4);min-width:0;flex:1";let p=document.createElement("option");p.value="",p.textContent=e?"\u8DDF\u968F\u9ED8\u8BA4":"default",b.append(p);for(let[m,A,t]of O){let a=document.createElement("option");a.value=m,a.textContent=e?A:t,b.append(a)}let g=document.createElement("input");g.type="number",g.min="1",g.max="180",g.placeholder=e?"\u9ED8\u8BA4":"def",g.title=e?"\u7559\u7A7A\u8DDF\u968F\u8BE5\u7C7B\u578B\u7684\u9ED8\u8BA4\u7A97\u53E3":"Empty follows the category default",g.style.cssText="width:62px;font:inherit;font-size:12px;padding:3px 4px;border-radius:5px;background:transparent;color:inherit;border:1px solid rgba(128,128,128,.4)",D.append(b,g);let I=document.createElement("div");I.style.cssText="display:flex;justify-content:flex-end";let W=document.createElement("button");return W.textContent=e?"\u4FDD\u5B58":"Save",W.style.cssText="font:inherit;font-size:12px;padding:3px 9px;border-radius:5px;cursor:pointer;border:1px solid rgba(128,128,128,.45);background:transparent;color:inherit",W.addEventListener("click",()=>{(async()=>{try{let m=g.value.trim()?Number(g.value):0;await c.invoke("setApp",[o.app,b.value||null,m]),d.textContent=e?"\u5DF2\u4FDD\u5B58 "+o.app:"Saved "+o.app}catch(m){d.textContent=(e?"\u4FDD\u5B58\u5931\u8D25":"Save failed")+": "+(m instanceof Error?m.message:String(m))}})()}),I.append(W),n.append(D,I),n},C=async()=>{E.replaceChildren(),d.textContent=e?"\u8BFB\u53D6\u4E2D\u2026":"Loading\u2026";try{let n=(await c.invoke("words")).words,i=v.checked?n:n.filter(s=>s.kind==="app"||s.weight>.1);d.textContent=i.length?`${i.length} / ${n.length} ${e?"\u4E2A\u8BCD":"tokens"}`:e?"\u8FD8\u6CA1\u6709\u91C7\u96C6\u5230\u6709\u4FE1\u53F7\u8BCD\uFF1B\u53EF\u52FE\u9009\u201C\u663E\u793A\u672A\u77E5\u8BCD\u201D\u67E5\u770B\u5168\u90E8":"No signal tokens yet; enable \u201CShow unknown tokens\u201D to see everything",E.append(u());for(let s of i)E.append($(s))}catch(o){d.textContent=`${e?"\u8BFB\u53D6\u5931\u8D25":"Failed"}: ${o instanceof Error?o.message:String(o)}`}};return r.append(T,f,k,d,E),c.root.append(r),C(),{dispose(){}}}},V={mount(c){let e=c.language==="zh"||c.language==="zh-Hant",r=document.createElement("div");r.style.cssText="display:flex;flex-direction:column;gap:10px;height:100%;box-sizing:border-box;padding:14px;overflow:auto";let T=document.createElement("div");T.textContent=e?"\u5F00\u53D1\u8005\u9009\u9879":"Developer options",T.style.cssText="font-size:14px;font-weight:600";let f=document.createElement("div");f.textContent=e?"\u8FD9\u91CC\u663E\u793A\u5F53\u524D\u89C4\u5219\u4E0E\u8BCA\u65AD\uFF1B\u4E0B\u4E00\u6B65\u7684\u89C4\u5219\u5F00\u5173\u3001\u9608\u503C\u4E0E\u89E6\u53D1\u539F\u56E0\u4F1A\u653E\u5728\u8FD9\u91CC\u3002":"Current rules and diagnostics; rule toggles, thresholds and reasons will live here next.",f.style.cssText="font-size:12px;opacity:.7;max-width:760px";let d=document.createElement("div");d.style.cssText="display:flex;flex-direction:column;gap:10px;font-size:13px";let k=document.createElement("div");k.style.cssText="font-size:12px;opacity:.75;min-height:16px";let v=document.createElement("label");v.style.cssText="display:flex;gap:6px;align-items:center;font-size:12px;opacity:.75;cursor:pointer";let y=document.createElement("input");y.type="checkbox";let E=document.createElement("span");E.textContent=e?"\u663E\u793A\u672A\u77E5\u8BCD\uFF08\u6743\u91CD 0.1\uFF09":"Show unknown tokens (weight 0.1)",v.append(y,E),y.addEventListener("change",()=>{$()});let z=n=>{let i=document.createElement("div");i.style.cssText="display:flex;flex-direction:column;gap:5px;padding:10px;border:1px solid rgba(128,128,128,.18);border-radius:8px";let s=document.createElement("div");return s.textContent=n,s.style.cssText="font-size:12px;font-weight:600;opacity:.8",i.append(s),i},u=(n,i)=>{let s=document.createElement("div");s.style.cssText="display:flex;gap:12px;font-size:12px";let x=document.createElement("span");x.textContent=n,x.style.cssText="opacity:.6;min-width:170px";let l=document.createElement("span");return l.textContent=i,l.style.cssText="font-family:ui-monospace,monospace;word-break:break-all",s.append(x,l),s},$=async()=>{d.replaceChildren(),k.textContent=e?"\u8BFB\u53D6\u4E2D\u2026":"Loading\u2026";try{let n=await c.invoke("diagnostics");k.textContent="";let i=z(e?"\u5F53\u524D flow":"Current flow");i.append(u(e?"\u72B6\u6001":"state",n.flow.state),u(e?"\u6700\u540E\u5207\u6362":"last switch",n.flow.lastSwitchAt?new Date(n.flow.lastSwitchAt).toLocaleString():"\u2014"),u(e?"\u9759\u9ED8 / \u9608\u503C":"gap / decay",`${Math.round(n.flow.gapMs/1e3)}s / ${Math.round(n.flow.decayMs/1e3)}s`),u(e?"\u6700\u540E\u7C7B\u578B":"last category",n.flow.category));let s=z(e?"\u89C4\u5219":"Rules");for(let l of n.rules){let h=document.createElement("div");h.style.cssText="display:flex;gap:10px;align-items:center;font-size:12px";let D=document.createElement("span");D.textContent=l.enabled?e?"\u5F00":"on":e?"\u5173":"off",D.style.cssText=`padding:1px 6px;border-radius:999px;border:1px solid ${l.enabled?"rgba(95,212,200,.6)":"rgba(128,128,128,.35)"};opacity:${l.enabled?1:.55};font-size:11px`;let b=document.createElement("span");b.textContent=`${l.id} \xB7 ${l.action}`,b.style.cssText="font-family:ui-monospace,monospace";let p=document.createElement("span");p.textContent=l.trigger,p.style.cssText="opacity:.55;margin-left:auto",h.append(D,b,p),s.append(h)}let x=z(e?"\u914D\u7F6E\u4E0E\u8BA1\u6570":"Config and counts");x.append(u("source",n.config.source),u("smoothing",String(n.config.smoothing)),u(e?"\u7EC6\u8282\u5C0F\u65F6":"detailHours",String(n.config.detailHours)),u(e?"\u4FDD\u7559\u5929\u6570":"keepDays",String(n.config.keepDays)),u(e?"\u5E94\u7528 / \u91C7\u6837 / \u7EC6\u8282":"apps / samples / entries",`${n.counts.apps} / ${n.counts.samples} / ${n.counts.entries}`),u(e?"\u6E38\u6807":"cursor",String(n.counts.cursor))),d.append(i,s,x)}catch(n){k.textContent=`${e?"\u8BFB\u53D6\u5931\u8D25":"Failed"}: ${n instanceof Error?n.message:String(n)}`}},C=document.createElement("div");C.style.cssText="display:flex;gap:8px;align-items:center";let o=document.createElement("button");return o.textContent=e?"\u5237\u65B0":"Refresh",o.style.cssText="font:inherit;font-size:12px;padding:4px 10px;border-radius:6px;cursor:pointer;border:1px solid rgba(128,128,128,.45);background:transparent;color:inherit",o.addEventListener("click",()=>{$()}),C.append(o),r.append(T,f,C,k,d),c.root.append(r),$(),{dispose(){}}}},_={panels:{timeline:j,words:K,developer:V}},X=_;export{X as default};
2
2
  //# sourceMappingURL=webui.js.map