coopani-world-journey 0.2.2 → 0.2.3

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,7 +1,60 @@
1
1
  # 更新日志
2
2
 
3
+ ## 0.2.3 — 2026-10-31
4
+
5
+ 设置页重排 + 一套暖色主题。
6
+
7
+ ### 设置页:从一堵字墙变成三组
8
+
9
+ 原来 **11 个字段平铺在一组里**,描述每条两三行。打开设置页看到的就是一堵字墙 ——
10
+ 想改倍速,得先扫过生图密钥。
11
+
12
+ 拆成三组,边界不是按「技术上属于什么」,是按**用户来这儿想干什么**:
13
+
14
+ | 组 | 装什么 | 判据 |
15
+ |---|---|---|
16
+ | 出发与节奏 | 出发地、倍速、歇几天、最长多久 | 改完立刻能感觉到 |
17
+ | 她的日子 | 同行、记账、预算、日记目录 | 叙事口径和钱 |
18
+ | 可选接口 | 高德、生图 | 不填也完整旅行 |
19
+
20
+ 文案同步压到一行内,**只说「留空会怎样」不解释原理** —— 原理在 README 和源码注释里,
21
+ 设置页上没人读长段。
22
+
23
+ ### 顶部加状态徽章
24
+
25
+ 设置页第一件想知道的事是「她现在到底在哪儿」,而这在配置项里一个字都没有,得翻日志。
26
+ 现在顶部有徽章:`现在 在去天水的路上`、`去过 4 个地方`、`花销 7052 元`。
27
+
28
+ 只放最多三个 —— 再多就不是信息,是装饰。
29
+
30
+ ### 暖色主题(本地,不进包)
31
+
32
+ 宿主的配置声明里**没有任何样式字段**,设置页的配色由全局主题决定,扩展改不了。
33
+ 所以这件事只能在主题层做:
34
+
35
+ - `theme.json` 新增方案「大肥鱼环游世界 · 暖」,36 个 token 全覆盖(light + dark)
36
+ - 底色暖米(`#faf5ee`)、主色赤陶(`#c06a35`)、文字深棕(`#2c241c`),不用纯黑纯白
37
+ - 深色版是暖棕底(`#171310`)+ 暖橙强调(`#e39b62`),不是把浅色简单反相
38
+
39
+ 这个方案是本地部署文件,**不进 npm 包** —— 它是这台机器的外观选择,不是扩展的一部分。
40
+ 换回原来的:外观页里选回「DeepSeek 大肥鱼 · 深度求索」即可,八个原方案一个没动。
41
+
42
+ ### 测试
43
+
44
+ 88 项 + 装载自检 33/33。`/api/worlds` 实测返回
45
+ `badges: [{"label":"现在","value":"在去天水的路上"}]`。
46
+
3
47
  ## 0.2.2 — 2026-10-31
4
48
 
49
+ ### 改名:巡游世界 → 「大肥鱼环游世界」
50
+
51
+ displayName 和所有界面文案统一(设置页、日志、提示词标题、README)。
52
+ 包名 `coopani-world-journey` 不动 —— 改了就是另一个包了。
53
+
54
+ 同时补了一段**和「一起听歌」的联动说明**(两个扩展各写一份)——
55
+ 这条在最早的需求里就提过,一直没落地,直到 0.2.1 做了联动才写得出来。
56
+
57
+
5
58
  Mr.Q 两句话:「路线规划不合理」「她是兰州的鱼,应该更习惯兰州这边的气候」,
6
59
  以及第三句关键的话 ——「其实也合理,如果从银川回兰州的话」。
7
60
 
package/README.md CHANGED
@@ -42,15 +42,34 @@
42
42
 
43
43
  ## 设置项
44
44
 
45
+ 设置页按「用户来这儿想干什么」分三组,不是按技术分类 —— **最常改的在最上面,一辈子不动一次的沉到底**。
46
+
47
+ ### 出发与节奏
48
+
45
49
  | 项 | 默认 | 说明 |
46
50
  |---|---|---|
47
51
  | 从哪儿出发 | 空 | 填城市名(兰州 / 西安)。留空则用上次的位置 |
48
- | 时间倍速 | 1 | 1 = 真实速度。4 = 一天压缩成六小时。24 = 一天一小时 || 回家休息几天 | 3 | 0 = 当天就能再走 |
49
- | 一次最多在外几个月 | 6 | 到了强制回家 |
52
+ | 时间倍速 | 1 | 1 = 真实速度。4 = 一天压缩成六小时。24 = 一天一小时 |
53
+ | 回家后歇几天 | 3 | 0 = 当天就能再走 |
54
+ | 一次最多在外多久 | 6 | 到了强制回家 |
55
+
56
+ ### 她的日子
57
+
58
+ | 项 | 默认 | 说明 |
59
+ |---|---|---|
50
60
  | 让你跟着一块走 | 关 | 打开后她说「我们」,日记里会提到你。**只是叙事,她并不知道你此刻在做什么** |
61
+ | 记花销 | 关 | 打开才累计交通、房费、三餐 |
62
+ | 预算 | 0 | 0 = 不限。见底时她会自己降级 |
51
63
  | 日记保存位置 | 空 | 留空跟本机。填同步盘则多设备共用 |
52
- | 高德密钥 | 空 | 可选。留空照常旅行,天气标为未知 |
53
- | 生图接口 / 密钥 | 空 | 可选,未接 |
64
+
65
+ ### 可选接口
66
+
67
+ | 项 | 默认 | 说明 |
68
+ |---|---|---|
69
+ | 高德密钥 | 空 | 留空照常旅行,天气标为未知 |
70
+ | 生图接口 / 密钥 | 空 | 留空就把景色写成文字 |
71
+
72
+ 页面顶部还有两个徽章,不点任何东西就知道**她现在在哪儿**(「现在 在去天水的路上」)、去过几个地方、花了多少。
54
73
 
55
74
  ### 倍速到底影响什么
56
75
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "coopani-world-journey",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
- "description": "让她真的在世界里走动(需 Coopani 0.2.16+):从你家出发,自己挑去哪、坐什么去,路上耗掉真实的时间;到了按当地时间过日子——吃饭、看日出、住店、写日记。",
5
+ "description": "大肥鱼环游世界:她真的出门、真的按当地时间过日子。从你家出发,自己挑去哪、坐什么去,路上耗掉真实的时间;到了按当地时间吃饭、看日出、住店、写日记。装了「一起听歌」的话,你放的歌会跟着她走。需 Coopani 0.2.16 以上。",
6
6
  "author": {
7
7
  "name": "Mr.Q"
8
8
  },
package/src/config.ts CHANGED
@@ -71,98 +71,121 @@ export const JOURNEY_DEFAULTS = (): JourneyConfigSection => ({
71
71
  imageKey: '',
72
72
  });
73
73
 
74
+ /**
75
+ * 设置页的三组。
76
+ *
77
+ * ## 为什么拆成三组
78
+ *
79
+ * 原来十二个字段平铺在一组里,描述每条两三行 —— 打开设置页看到的就是一堵字墙,
80
+ * 想改倍速得先扫过生图密钥。拆开之后:**最常改的在最上面,一辈子不动一次的沉到底**。
81
+ *
82
+ * 分组的边界不是按「技术上属于什么」,是按**用户来这儿想干什么**:
83
+ * 1. 她要怎么走 —— 改完立刻能感觉到
84
+ * 2. 她的日子怎么过 —— 叙事和账
85
+ * 3. 要不要接外部服务 —— 不填也完整
86
+ *
87
+ * ## 文案的分寸
88
+ *
89
+ * 每条控制在一行内,**说明「留空会怎样」而不是解释原理**。
90
+ * 原理写在 README 和源码注释里,设置页上没人读长段。
91
+ */
74
92
  export function journeyConfigGroups(): ConfigGroup[] {
75
93
  return [
76
94
  {
77
- id: 'journey.main',
95
+ id: 'journey.pace',
78
96
  owner: `world:${JOURNEY_ID}`,
79
97
  schema: {
80
98
  type: 'object',
81
- title: '大肥鱼环游世界',
99
+ title: '出发与节奏',
100
+ description: '她怎么走、多久歇一次。这几项改完最明显。',
82
101
  properties: {
83
102
  'worlds.journey.homeCity': {
84
103
  type: 'string',
85
104
  title: '从哪儿出发',
86
- description:
87
- '填城市名就行(兰州、西安、成都)。留空则沿用她上次出发的地方。'
88
- + '填了内置表里没有的城市会退回本机所在城市,日志里会写一句。',
105
+ description: '城市名就行:兰州、西安、成都。留空就沿用她上次出发的地方。',
89
106
  },
90
107
  'worlds.journey.speed': {
91
108
  type: 'number',
92
109
  title: '时间倍速',
93
- description:
94
- '1 = 真实速度,火车开十一小时就真的是十一小时,她这期间不打扰你。'
95
- + '4 = 四倍速,一天压缩成六小时。24 = 一天一小时,适合想快点看她走完一圈的时候。',
110
+ description: '1 是真实速度,火车开十一小时就真的十一小时。24 是一天压成一小时。',
96
111
  enum: [1, 4, 24],
97
112
  'x-hot': true,
98
113
  },
99
114
  'worlds.journey.restDays': {
100
115
  type: 'integer',
101
- title: '回家后休息几天',
102
- description: '到家之后要隔几天才再出门。0 = 当天就能走。默认 3。',
116
+ title: '回家后歇几天',
117
+ description: '到家隔几天再出门。0 = 当天就能再走。',
103
118
  minimum: 0,
104
119
  maximum: 60,
105
120
  'x-suffix': '天',
106
121
  },
107
122
  'worlds.journey.maxTripMonths': {
108
123
  type: 'integer',
109
- title: '一次最多在外几个月',
110
- description:
111
- '连续旅行的上限,到了就强制回家待一阵。想让她一直在外面就把这个数调大。默认 6。',
124
+ title: '一次最多在外多久',
125
+ description: '到点就强制回家歇一阵。想让她一直在外面就调大。',
112
126
  minimum: 1,
113
127
  maximum: 24,
114
128
  'x-suffix': '个月',
115
129
  },
130
+ },
131
+ },
132
+ },
133
+ {
134
+ id: 'journey.life',
135
+ owner: `world:${JOURNEY_ID}`,
136
+ schema: {
137
+ type: 'object',
138
+ title: '她的日子',
139
+ description: '叙事口径和花销。关掉的开关,她一个字都不会提。',
140
+ properties: {
116
141
  'worlds.journey.withUser': {
117
142
  type: 'boolean',
118
143
  title: '让你跟着一块走',
119
- description:
120
- '打开后她说「我们」而不是「我」,日记里会提到你。'
121
- + '注意:这只是叙事上的同行,她不会因此知道你此刻在干什么。',
122
- },
123
- 'worlds.journey.memoryDir': {
124
- type: 'string',
125
- title: '日记保存位置',
126
- description:
127
- '留空=跟着这台机器。填同步盘或移动硬盘里的文件夹,公司和家里的她就是同一份日记。'
128
- + '⚠ 别指向网盘根目录,建一个专用子目录。网盘的「按需文件/节省空间」会造成读不到,'
129
- + '要把它设成「始终保留在此设备上」。',
130
- 'x-path': { kind: 'directory' },
144
+ description: '打开后她说「我们」、日记里提到你。只是叙事上的同行,她不会因此知道你在干什么。',
131
145
  },
132
146
  'worlds.journey.moneyEnabled': {
133
147
  type: 'boolean',
134
148
  title: '记花销',
135
- description:
136
- '打开后累计这一趟花了多少钱:交通费、房费、一天三顿。'
137
- + '关着就一分都不记,她也不会提钱。',
149
+ description: '打开才累计交通、房费、三餐。关着一分不记,也不会提钱。',
138
150
  },
139
151
  'worlds.journey.budget': {
140
152
  type: 'integer',
141
153
  title: '预算(可留空)',
142
- description:
143
- '0 = 不限。填了之后,钱见底时她会自己降级:住便宜的、改坐火车、少在外面吃。'
144
- + '不会卡死不走,只是会过得紧。',
154
+ description: '0 = 不限。见底时她会自己降级:住便宜的、改坐火车,而不是硬撑。',
145
155
  minimum: 0,
146
156
  'x-suffix': '元',
147
157
  },
158
+ 'worlds.journey.memoryDir': {
159
+ type: 'string',
160
+ title: '日记保存位置',
161
+ description: '留空=只跟着这台机器。填同步盘里的一个专用子目录,换台电脑还是同一份日记。',
162
+ 'x-path': { kind: 'directory' },
163
+ },
164
+ },
165
+ },
166
+ },
167
+ {
168
+ id: 'journey.services',
169
+ owner: `world:${JOURNEY_ID}`,
170
+ schema: {
171
+ type: 'object',
172
+ title: '可选接口',
173
+ description: '不填也能完整旅行——距离、时区、日出日落都是本地算的。',
174
+ properties: {
148
175
  'worlds.journey.amapKey': {
149
176
  type: 'string',
150
- title: '高德 Web 服务密钥(可选)',
151
- description:
152
- '只用来查天气和校验地点是否存在。留空也照常旅行——距离和时区都是本地算的,'
153
- + '只是天气会标为未知。配额有限,别填在一天要跑几十趟的地方。',
177
+ title: '高德密钥',
178
+ description: '只用来查天气。留空天气标为未知,其他照常。',
154
179
  },
155
180
  'worlds.journey.imageApi': {
156
181
  type: 'string',
157
- title: '生图接口(可选)',
158
- description:
159
- 'OpenAI 兼容的 images/generations 根地址,例如 https://api.openai.com/v1。'
160
- + '留空=她把当天的景色写成文字,不画。填了才会花钱,默认不填。',
182
+ title: '生图接口',
183
+ description: 'OpenAI 兼容的 images/generations 根地址。留空就把景色写成文字。',
161
184
  },
162
185
  'worlds.journey.imageKey': {
163
186
  type: 'string',
164
- title: '生图密钥(可选)',
165
- description: '只存在本机配置里,不写进日记目录,也不进日志。',
187
+ title: '生图密钥',
188
+ description: '只存本机配置,不进日记、不进日志。',
166
189
  },
167
190
  },
168
191
  },
package/src/world.ts CHANGED
@@ -1161,8 +1161,44 @@ export class JourneyWorld implements World {
1161
1161
  };
1162
1162
  }
1163
1163
 
1164
+ /**
1165
+ * 设置页头部的两个徽章。
1166
+ *
1167
+ * 用户打开设置页,第一个想知道的是「她现在到底在哪儿」——
1168
+ * 这件事在配置项里一个字都没有,得翻日志才有。放两个徽章在这儿,
1169
+ * 不用点任何东西就知道她此刻在做什么。
1170
+ *
1171
+ * 只放两个:徽章多了就不是信息,是装饰。
1172
+ */
1173
+ private settingBadges(): Array<{ label: string; value: string; tone?: 'on' | 'off' | 'plain' }> {
1174
+ const st = this.st;
1175
+ if (!st) return [];
1176
+ let where: string;
1177
+ if (st.phase === 'traveling' && st.leg) {
1178
+ const to = this.cityOrNull(st.leg.toId);
1179
+ where = to ? `在去${to.name}的路上` : '在路上';
1180
+ } else {
1181
+ const here = this.cityOrNull(st.hereId);
1182
+ if (!here) where = '不知道在哪';
1183
+ else if (st.phase === 'home') {
1184
+ where = this.stillResting(this.worldNow()) ? `在${here.name}歇着` : `在${here.name}待命`;
1185
+ } else where = `在${here.name}`;
1186
+ }
1187
+ const badges: Array<{ label: string; value: string; tone?: 'on' | 'off' | 'plain' }> = [
1188
+ { label: '现在', value: where, tone: 'plain' },
1189
+ ];
1190
+ if (st.everVisited.length) {
1191
+ badges.push({ label: '去过', value: `${st.everVisited.length} 个地方`, tone: 'plain' });
1192
+ }
1193
+ if (this.cfg.moneyEnabled && st.spent > 0) {
1194
+ badges.push({ label: '花销', value: `${st.spent} 元`, tone: 'plain' });
1195
+ }
1196
+ return badges;
1197
+ }
1198
+
1164
1199
  webUi(): WorldWebUiDecl {
1165
1200
  return {
1201
+ badges: this.settingBadges(),
1166
1202
  config: journeyConfigGroups(),
1167
1203
  promptDocs: [{
1168
1204
  key: `worlds.${JOURNEY_ID}.envPrompt`,