luckytri 1.0.2 → 1.0.4
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/bin/luckytri.js +5 -1
- package/docs/en/CHANGELOG.md +20 -0
- package/docs/en/guide.md +8 -0
- package/docs/en/her-life.md +2 -0
- package/docs/en/webui.md +4 -0
- package/docs/zh/CHANGELOG.md +20 -0
- package/docs/zh/guide.md +9 -1
- package/docs/zh/her-life.md +4 -2
- package/docs/zh/webui.md +4 -0
- package/package.json +111 -110
- package/public/app/assets/ChatsPage-CshdtTzV.js +3 -0
- package/public/app/assets/{Empty-DP6WMGHi.js → Empty-C7yzOBzy.js} +1 -1
- package/public/app/assets/{Field-CtbdqaiB.js → Field-BjFv4Rfn.js} +1 -1
- package/public/app/assets/HeartPage-eqo6ru16.js +1 -0
- package/public/app/assets/LifePage-CN8GE5NK.js +1 -0
- package/public/app/assets/MemoryPage-CCpMN4Wg.js +1 -0
- package/public/app/assets/NaturePage-C0JqdBTR.js +3 -0
- package/public/app/assets/{NaturePage-CTXHCE2D.css → NaturePage-CHkpb5NK.css} +1 -1
- package/public/app/assets/PeoplePage-BTU0H1pM.css +1 -0
- package/public/app/assets/PeoplePage-BXGBbO4u.js +1 -0
- package/public/app/assets/{PluginsPage-DksEZbUU.js → PluginsPage-Dksp5AV5.js} +1 -1
- package/public/app/assets/{SystemPage-BkRu0Rvx.css → SystemPage-BenIyTvk.css} +1 -1
- package/public/app/assets/SystemPage-Cd7aa8eE.js +1 -0
- package/public/app/assets/TimePage-Bs_zImb4.js +3 -0
- package/public/app/assets/{index-DY4e15Kh.css → index-BHZcHUMh.css} +1 -1
- package/public/app/assets/index-CyRtYxP3.js +4 -0
- package/public/app/assets/{knowledge-C6_HvoyB.js → knowledge-BJOIyuJo.js} +1 -1
- package/public/app/assets/{media-SMfTT-yc.js → media-Bwtx8blz.js} +1 -1
- package/public/app/assets/mind-DtTVrW6Z.js +1 -0
- package/public/app/index.html +4 -4
- package/public/guide.en.html +32 -3
- package/public/guide.html +19 -4
- package/public/tutorial.en.md +8 -0
- package/public/tutorial.md +9 -1
- package/scripts/launch.js +11 -4
- package/scripts/service.js +57 -7
- package/scripts/start-server.js +4 -1
- package/scripts/update-instance.js +201 -0
- package/server/app.js +3 -0
- package/server/core/context-builder.js +2 -2
- package/server/core/context-compactor.js +2 -2
- package/server/core/orchestrator.js +1 -0
- package/server/core/persona-manager.js +30 -5
- package/server/core/reply-delivery.js +10 -0
- package/server/core/response-validator.js +20 -0
- package/server/i18n/errors.js +38 -2
- package/server/index.js +45 -2
- package/server/mind/api.js +26 -0
- package/server/mind/bonds.js +9 -2
- package/server/mind/faces.js +257 -22
- package/server/mind/index.js +2 -0
- package/server/mind/initiative.js +6 -1
- package/server/mind/life/context.js +23 -9
- package/server/mind/life/growth.js +5 -3
- package/server/mind/nature.js +100 -4
- package/server/mind/relationships.js +279 -0
- package/server/mind/schema.js +5 -0
- package/server/mind/time/tasks.js +2 -1
- package/server/mind/view.js +5 -0
- package/server/runtime/installation.js +116 -0
- package/server/runtime/update-execution.js +127 -0
- package/server/runtime/update-manager.js +140 -0
- package/server/startup-lock.js +12 -12
- package/server/storage/archive.js +10 -2
- package/server/storage/backup-lock.js +23 -5
- package/server/storage/data-transfer.js +2 -1
- package/server/studio/display-names.js +1 -0
- package/server/studio/settings.js +1 -0
- package/server/studio/updates.js +144 -0
- package/public/app/assets/ChatsPage-BhlJHseG.js +0 -3
- package/public/app/assets/HeartPage-32BeTPn2.js +0 -1
- package/public/app/assets/LifePage-CxSm0XKI.js +0 -1
- package/public/app/assets/MemoryPage-BeKBcPCY.js +0 -1
- package/public/app/assets/NaturePage-NEPYl2HS.js +0 -3
- package/public/app/assets/PeoplePage-BmUxkrOU.css +0 -1
- package/public/app/assets/PeoplePage-DxtpekwM.js +0 -1
- package/public/app/assets/SystemPage-BvzJlHuY.js +0 -1
- package/public/app/assets/TimePage-7-awH5XL.js +0 -3
- package/public/app/assets/formatters-ZmW_xFHw.js +0 -1
- package/public/app/assets/index-4rItIkD-.js +0 -4
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
atomicJson,
|
|
6
|
+
UPDATE_ID,
|
|
7
|
+
updatePath,
|
|
8
|
+
updateRoot,
|
|
9
|
+
} from "./installation.js";
|
|
10
|
+
export const UPDATE_FAILED =
|
|
11
|
+
"更新未完成,原程序和数据已保留,请查看运行日志后重试。";
|
|
12
|
+
export const UPDATE_RECOVERY_FAILED =
|
|
13
|
+
"自动恢复未完成,更新前的校验备份已保留,请查看运行日志并手动恢复。";
|
|
14
|
+
const ACTIVE = new Set([
|
|
15
|
+
"queued",
|
|
16
|
+
"downloading",
|
|
17
|
+
"checking",
|
|
18
|
+
"stopping",
|
|
19
|
+
"backup",
|
|
20
|
+
"restarting",
|
|
21
|
+
"verifying",
|
|
22
|
+
"restoring",
|
|
23
|
+
]);
|
|
24
|
+
export const processAlive = (pid) => {
|
|
25
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
26
|
+
try {
|
|
27
|
+
process.kill(pid, 0);
|
|
28
|
+
return true;
|
|
29
|
+
} catch (e) {
|
|
30
|
+
return e.code !== "ESRCH";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
export function readUpdateJob(home, id) {
|
|
34
|
+
updatePath(home, id);
|
|
35
|
+
const job = JSON.parse(
|
|
36
|
+
readFileSync(join(updateRoot(home), "job.json"), "utf8"),
|
|
37
|
+
);
|
|
38
|
+
if (job.id !== id || !UPDATE_ID.test(job.id)) throw Error("更新任务无效");
|
|
39
|
+
return job;
|
|
40
|
+
}
|
|
41
|
+
export const writeUpdateJob = (home, job) => {
|
|
42
|
+
updatePath(home, job.id);
|
|
43
|
+
atomicJson(join(updateRoot(home), "job.json"), job);
|
|
44
|
+
return job;
|
|
45
|
+
};
|
|
46
|
+
export class InstanceUpdater {
|
|
47
|
+
constructor({
|
|
48
|
+
paths,
|
|
49
|
+
checker,
|
|
50
|
+
launch,
|
|
51
|
+
occupied = () => false,
|
|
52
|
+
now = Date.now,
|
|
53
|
+
}) {
|
|
54
|
+
this.paths = paths;
|
|
55
|
+
this.checker = checker;
|
|
56
|
+
this.launch = launch;
|
|
57
|
+
this.occupied = occupied;
|
|
58
|
+
this.now = now;
|
|
59
|
+
this.starting = false;
|
|
60
|
+
}
|
|
61
|
+
status() {
|
|
62
|
+
let job;
|
|
63
|
+
try {
|
|
64
|
+
job = JSON.parse(
|
|
65
|
+
readFileSync(join(updateRoot(this.paths.home), "job.json"), "utf8"),
|
|
66
|
+
);
|
|
67
|
+
if (!UPDATE_ID.test(job.id)) return null;
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
if (
|
|
72
|
+
ACTIVE.has(job.status) &&
|
|
73
|
+
job.workerPid &&
|
|
74
|
+
!processAlive(job.workerPid)
|
|
75
|
+
) {
|
|
76
|
+
job = writeUpdateJob(this.paths.home, {
|
|
77
|
+
...job,
|
|
78
|
+
status: "error",
|
|
79
|
+
error: ["queued", "downloading", "checking"].includes(job.status)
|
|
80
|
+
? UPDATE_FAILED
|
|
81
|
+
: UPDATE_RECOVERY_FAILED,
|
|
82
|
+
finished: this.now(),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
// Public progress is deliberately free of filesystem paths and process data.
|
|
86
|
+
return {
|
|
87
|
+
id: job.id,
|
|
88
|
+
version: job.version,
|
|
89
|
+
previous: job.previous,
|
|
90
|
+
status: job.status,
|
|
91
|
+
started: job.started,
|
|
92
|
+
finished: job.finished,
|
|
93
|
+
error: job.error || "",
|
|
94
|
+
restored: !!job.restored,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
get busy() {
|
|
98
|
+
return this.starting || ACTIVE.has(this.status()?.status);
|
|
99
|
+
}
|
|
100
|
+
async install(version) {
|
|
101
|
+
if (this.busy) return this.status();
|
|
102
|
+
if (this.occupied()) throw Error("数据处理或备份正在进行,请稍后再试");
|
|
103
|
+
if (!this.launch || this.paths.database === ":memory:")
|
|
104
|
+
throw Error("当前实例不支持自动更新");
|
|
105
|
+
this.starting = true;
|
|
106
|
+
try {
|
|
107
|
+
const info = await this.checker.check();
|
|
108
|
+
if (info.status !== "available" || version !== info.latest)
|
|
109
|
+
throw Error("请重新检查并选择可用的新版本");
|
|
110
|
+
updatePath(this.paths.home, randomUUID());
|
|
111
|
+
mkdirSync(updateRoot(this.paths.home), { recursive: true, mode: 0o700 });
|
|
112
|
+
const job = {
|
|
113
|
+
id: randomUUID(),
|
|
114
|
+
version,
|
|
115
|
+
previous: info.current,
|
|
116
|
+
status: "queued",
|
|
117
|
+
started: this.now(),
|
|
118
|
+
parent: process.pid,
|
|
119
|
+
};
|
|
120
|
+
writeUpdateJob(this.paths.home, job);
|
|
121
|
+
try {
|
|
122
|
+
const pid = await this.launch(job.id);
|
|
123
|
+
writeUpdateJob(this.paths.home, {
|
|
124
|
+
...readUpdateJob(this.paths.home, job.id),
|
|
125
|
+
workerPid: pid,
|
|
126
|
+
});
|
|
127
|
+
} catch {
|
|
128
|
+
writeUpdateJob(this.paths.home, {
|
|
129
|
+
...job,
|
|
130
|
+
status: "error",
|
|
131
|
+
error: UPDATE_FAILED,
|
|
132
|
+
});
|
|
133
|
+
throw Error(UPDATE_FAILED);
|
|
134
|
+
}
|
|
135
|
+
return this.status();
|
|
136
|
+
} finally {
|
|
137
|
+
this.starting = false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
package/server/startup-lock.js
CHANGED
|
@@ -2,7 +2,8 @@ import {
|
|
|
2
2
|
mkdirSync,
|
|
3
3
|
readFileSync,
|
|
4
4
|
rmdirSync,
|
|
5
|
-
|
|
5
|
+
unlinkSync,
|
|
6
|
+
existsSync,
|
|
6
7
|
statSync,
|
|
7
8
|
writeFileSync,
|
|
8
9
|
} from "node:fs";
|
|
@@ -11,6 +12,14 @@ import { runtimePaths } from "./paths.js";
|
|
|
11
12
|
|
|
12
13
|
const file = () => join(runtimePaths().data, "startup.json");
|
|
13
14
|
const lock = () => join(runtimePaths().data, "startup.lock");
|
|
15
|
+
function alive(pid) {
|
|
16
|
+
try {
|
|
17
|
+
process.kill(pid, 0);
|
|
18
|
+
return true;
|
|
19
|
+
} catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
14
23
|
|
|
15
24
|
function read() {
|
|
16
25
|
try {
|
|
@@ -22,15 +31,6 @@ function read() {
|
|
|
22
31
|
}
|
|
23
32
|
}
|
|
24
33
|
|
|
25
|
-
function alive(pid) {
|
|
26
|
-
try {
|
|
27
|
-
process.kill(pid, 0);
|
|
28
|
-
return true;
|
|
29
|
-
} catch {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
34
|
function write(role) {
|
|
35
35
|
mkdirSync(runtimePaths().data, { recursive: true, mode: 0o700 });
|
|
36
36
|
writeFileSync(
|
|
@@ -61,7 +61,7 @@ function claim(role) {
|
|
|
61
61
|
if (busy()) return false;
|
|
62
62
|
// Reclaim a dead owner's claim; the normal concurrent-launch path above
|
|
63
63
|
// never removes a directory just acquired by another process.
|
|
64
|
-
|
|
64
|
+
if (existsSync(file())) unlinkSync(file());
|
|
65
65
|
try {
|
|
66
66
|
rmdirSync(lock());
|
|
67
67
|
} catch (error) {
|
|
@@ -108,7 +108,7 @@ export function starting() {
|
|
|
108
108
|
export function clearStarting() {
|
|
109
109
|
const info = read();
|
|
110
110
|
if (info?.pid === process.pid) {
|
|
111
|
-
|
|
111
|
+
if (existsSync(file())) unlinkSync(file());
|
|
112
112
|
try {
|
|
113
113
|
rmdirSync(lock());
|
|
114
114
|
} catch (error) {
|
|
@@ -142,10 +142,18 @@ export function writeArchiveManifest(
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
export function verifyArchive(file) {
|
|
145
|
-
const inspected = inspectDatabase(file);
|
|
146
145
|
const sidecar = `${file}.json`;
|
|
147
|
-
if (!existsSync(sidecar))
|
|
146
|
+
if (!existsSync(sidecar))
|
|
147
|
+
return { ...inspectDatabase(file), manifest: false }; // Readable legacy backups remain usable.
|
|
148
148
|
const manifest = JSON.parse(readFileSync(sidecar, "utf8"));
|
|
149
|
+
// Reject changed bytes before SQLite attempts to parse a corrupted schema.
|
|
150
|
+
if (
|
|
151
|
+
manifest.format !== 1 ||
|
|
152
|
+
manifest.app !== "luckytri" ||
|
|
153
|
+
manifest.sha256 !== fileHash(file)
|
|
154
|
+
)
|
|
155
|
+
throw Error("备份校验清单不匹配");
|
|
156
|
+
const inspected = inspectDatabase(file);
|
|
149
157
|
if (
|
|
150
158
|
manifest.format !== 1 ||
|
|
151
159
|
manifest.app !== "luckytri" ||
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
mkdirSync,
|
|
5
5
|
openSync,
|
|
6
6
|
readFileSync,
|
|
7
|
-
|
|
7
|
+
unlinkSync,
|
|
8
8
|
statSync,
|
|
9
9
|
writeFileSync,
|
|
10
10
|
} from "node:fs";
|
|
@@ -12,7 +12,6 @@ import { join, resolve } from "node:path";
|
|
|
12
12
|
import { randomUUID } from "node:crypto";
|
|
13
13
|
|
|
14
14
|
const BUSY = "备份目录正在被使用,请稍后再试";
|
|
15
|
-
|
|
16
15
|
function alive(pid) {
|
|
17
16
|
if (!Number.isInteger(pid) || pid < 1) return false;
|
|
18
17
|
try {
|
|
@@ -28,8 +27,27 @@ export function withBackupLock(directory, run) {
|
|
|
28
27
|
mkdirSync(root, { recursive: true });
|
|
29
28
|
const file = join(root, ".backup-operation.lock");
|
|
30
29
|
const token = randomUUID();
|
|
30
|
+
const reclaim = () => {
|
|
31
|
+
if (!existsSync(file)) return;
|
|
32
|
+
const stat = statSync(file);
|
|
33
|
+
let owner;
|
|
34
|
+
try {
|
|
35
|
+
owner = JSON.parse(readFileSync(file, "utf8"));
|
|
36
|
+
} catch {
|
|
37
|
+
/* A fresh incomplete claim belongs to its writer. */
|
|
38
|
+
}
|
|
39
|
+
if (
|
|
40
|
+
(owner && !alive(owner.pid)) ||
|
|
41
|
+
(!owner && Date.now() - stat.mtimeMs > 6 * 3600000)
|
|
42
|
+
) {
|
|
43
|
+
const current = statSync(file);
|
|
44
|
+
if (current.ino === stat.ino && current.mtimeMs === stat.mtimeMs)
|
|
45
|
+
unlinkSync(file);
|
|
46
|
+
} else throw Error(BUSY);
|
|
47
|
+
};
|
|
31
48
|
let acquired = false;
|
|
32
49
|
for (let attempt = 0; attempt < 2 && !acquired; attempt++) {
|
|
50
|
+
reclaim();
|
|
33
51
|
try {
|
|
34
52
|
const fd = openSync(file, "wx");
|
|
35
53
|
let written = false;
|
|
@@ -38,7 +56,7 @@ export function withBackupLock(directory, run) {
|
|
|
38
56
|
written = true;
|
|
39
57
|
} finally {
|
|
40
58
|
closeSync(fd);
|
|
41
|
-
if (!written)
|
|
59
|
+
if (!written && existsSync(file)) unlinkSync(file);
|
|
42
60
|
}
|
|
43
61
|
acquired = true;
|
|
44
62
|
} catch (error) {
|
|
@@ -56,7 +74,7 @@ export function withBackupLock(directory, run) {
|
|
|
56
74
|
) {
|
|
57
75
|
const current = statSync(file);
|
|
58
76
|
if (current.ino === stat.ino && current.mtimeMs === stat.mtimeMs)
|
|
59
|
-
|
|
77
|
+
unlinkSync(file);
|
|
60
78
|
} else throw Error(BUSY);
|
|
61
79
|
}
|
|
62
80
|
}
|
|
@@ -67,7 +85,7 @@ export function withBackupLock(directory, run) {
|
|
|
67
85
|
if (existsSync(file)) {
|
|
68
86
|
try {
|
|
69
87
|
const owner = JSON.parse(readFileSync(file, "utf8"));
|
|
70
|
-
if (owner.token === token)
|
|
88
|
+
if (owner.token === token) unlinkSync(file);
|
|
71
89
|
} catch {
|
|
72
90
|
// Leave a lock changed by another process alone.
|
|
73
91
|
}
|
|
@@ -77,6 +77,7 @@ export function createDataTransfer({
|
|
|
77
77
|
run = work,
|
|
78
78
|
now = Date.now,
|
|
79
79
|
maxUploadBytes,
|
|
80
|
+
occupied = () => false,
|
|
80
81
|
} = {}) {
|
|
81
82
|
const root = resolve(directory);
|
|
82
83
|
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
@@ -105,7 +106,7 @@ export function createDataTransfer({
|
|
|
105
106
|
return rest;
|
|
106
107
|
};
|
|
107
108
|
const check = () => {
|
|
108
|
-
if (active || backup?.running || cleanup?.running)
|
|
109
|
+
if (active || backup?.running || cleanup?.running || occupied())
|
|
109
110
|
throw Error("数据处理或备份正在进行,请稍后再试");
|
|
110
111
|
if (source === ":memory:") throw Error("当前实例不支持文件导入导出");
|
|
111
112
|
};
|
|
@@ -32,6 +32,7 @@ export function mountStudio(app, store, runtime, chat) {
|
|
|
32
32
|
if (!runtime.cleanup)
|
|
33
33
|
return res.status(501).json({ error: "备份整理不可用" });
|
|
34
34
|
try {
|
|
35
|
+
if (runtime.updater?.busy) throw Error("版本更新正在进行,请稍后再试");
|
|
35
36
|
res.json(await runtime.cleanup.manual(req.body.items));
|
|
36
37
|
} catch (error) {
|
|
37
38
|
res.status(409).json({ error: error.message });
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { SOURCE_CHECKOUT } from "../paths.js";
|
|
2
|
+
import { USER_AGENT, VERSION } from "../version.js";
|
|
3
|
+
|
|
4
|
+
const REGISTRY = "https://registry.npmjs.org/luckytri/latest";
|
|
5
|
+
const SEMVER =
|
|
6
|
+
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
7
|
+
function parts(value) {
|
|
8
|
+
const m =
|
|
9
|
+
typeof value === "string" && value.length <= 100 && value.match(SEMVER);
|
|
10
|
+
if (!m || m.slice(1, 4).some((n) => !Number.isSafeInteger(Number(n))))
|
|
11
|
+
return null;
|
|
12
|
+
if (m[4]?.split(".").some((part) => /^0\d+$/.test(part))) return null;
|
|
13
|
+
return { numbers: m.slice(1, 4).map(Number), pre: m[4]?.split(".") || [] };
|
|
14
|
+
}
|
|
15
|
+
export function compareVersions(a, b) {
|
|
16
|
+
const left = parts(a),
|
|
17
|
+
right = parts(b);
|
|
18
|
+
if (!left || !right) throw Error("版本信息无效");
|
|
19
|
+
for (let i = 0; i < 3; i++)
|
|
20
|
+
if (left.numbers[i] !== right.numbers[i])
|
|
21
|
+
return Math.sign(left.numbers[i] - right.numbers[i]);
|
|
22
|
+
if (!left.pre.length || !right.pre.length)
|
|
23
|
+
return Math.sign(Number(!left.pre.length) - Number(!right.pre.length));
|
|
24
|
+
for (let i = 0; i < Math.max(left.pre.length, right.pre.length); i++) {
|
|
25
|
+
const x = left.pre[i],
|
|
26
|
+
y = right.pre[i];
|
|
27
|
+
if (x === y) continue;
|
|
28
|
+
if (x === undefined || y === undefined) return x === undefined ? -1 : 1;
|
|
29
|
+
const xn = /^\d+$/.test(x),
|
|
30
|
+
yn = /^\d+$/.test(y);
|
|
31
|
+
if (xn && yn) return BigInt(x) < BigInt(y) ? -1 : 1;
|
|
32
|
+
if (xn !== yn) return xn ? -1 : 1;
|
|
33
|
+
return x < y ? -1 : 1;
|
|
34
|
+
}
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// A manual, bounded metadata read. It never installs code, runs commands or
|
|
39
|
+
// sends instance data, model keys or npm login credentials to the registry.
|
|
40
|
+
export class UpdateChecker {
|
|
41
|
+
constructor({
|
|
42
|
+
fetch: fetcher = globalThis.fetch,
|
|
43
|
+
now = Date.now,
|
|
44
|
+
current = VERSION,
|
|
45
|
+
source = SOURCE_CHECKOUT,
|
|
46
|
+
timeout = 10000,
|
|
47
|
+
} = {}) {
|
|
48
|
+
this.fetch = fetcher;
|
|
49
|
+
this.now = now;
|
|
50
|
+
this.current = current;
|
|
51
|
+
this.source = source;
|
|
52
|
+
this.timeout = timeout;
|
|
53
|
+
this.latest = null;
|
|
54
|
+
this.checkedAt = null;
|
|
55
|
+
this.lastAttempt = null;
|
|
56
|
+
this.error = "";
|
|
57
|
+
this.pending = null;
|
|
58
|
+
}
|
|
59
|
+
status() {
|
|
60
|
+
return {
|
|
61
|
+
current: this.current,
|
|
62
|
+
latest: this.latest,
|
|
63
|
+
checkedAt: this.checkedAt,
|
|
64
|
+
error: this.error,
|
|
65
|
+
status: this.error
|
|
66
|
+
? "unavailable"
|
|
67
|
+
: !this.latest
|
|
68
|
+
? "unchecked"
|
|
69
|
+
: compareVersions(this.latest, this.current) > 0
|
|
70
|
+
? "available"
|
|
71
|
+
: compareVersions(this.latest, this.current) === 0
|
|
72
|
+
? "current"
|
|
73
|
+
: "ahead",
|
|
74
|
+
installation: this.source ? "source" : "npm",
|
|
75
|
+
npmUrl: "https://www.npmjs.com/package/luckytri",
|
|
76
|
+
releaseUrl: "https://github.com/TodayYueC/LuckyTri/releases",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
check() {
|
|
80
|
+
if (this.pending) return this.pending;
|
|
81
|
+
const now = this.now();
|
|
82
|
+
const elapsed =
|
|
83
|
+
this.lastAttempt == null ? Infinity : now - this.lastAttempt;
|
|
84
|
+
if (elapsed >= 0 && elapsed < (this.error ? 15000 : 300000))
|
|
85
|
+
return Promise.resolve({ ...this.status(), cached: true });
|
|
86
|
+
this.lastAttempt = now;
|
|
87
|
+
this.pending = this.read().finally(() => {
|
|
88
|
+
this.pending = null;
|
|
89
|
+
});
|
|
90
|
+
return this.pending;
|
|
91
|
+
}
|
|
92
|
+
async read() {
|
|
93
|
+
let response;
|
|
94
|
+
try {
|
|
95
|
+
response = await this.fetch(REGISTRY, {
|
|
96
|
+
headers: { Accept: "application/json", "User-Agent": USER_AGENT },
|
|
97
|
+
redirect: "error",
|
|
98
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
99
|
+
});
|
|
100
|
+
if (!response.ok) throw Error(`HTTP ${response.status}`);
|
|
101
|
+
if (Number(response.headers.get("content-length")) > 65536)
|
|
102
|
+
throw Error("版本信息过大");
|
|
103
|
+
const chunks = [];
|
|
104
|
+
let bytes = 0;
|
|
105
|
+
for await (const chunk of response.body) {
|
|
106
|
+
bytes += chunk.length;
|
|
107
|
+
if (bytes > 65536) throw Error("版本信息过大");
|
|
108
|
+
chunks.push(Buffer.from(chunk));
|
|
109
|
+
}
|
|
110
|
+
const data = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
111
|
+
if (data.name !== "luckytri" || !parts(data.version))
|
|
112
|
+
throw Error("版本信息无效");
|
|
113
|
+
this.latest = data.version;
|
|
114
|
+
this.checkedAt = this.now();
|
|
115
|
+
this.error = "";
|
|
116
|
+
} catch {
|
|
117
|
+
try {
|
|
118
|
+
await response?.body?.cancel();
|
|
119
|
+
} catch {
|
|
120
|
+
/* Already consumed or cancelled. */
|
|
121
|
+
}
|
|
122
|
+
this.error = "暂时无法连接 npm 或读取版本信息,请稍后重试。";
|
|
123
|
+
}
|
|
124
|
+
return { ...this.status(), cached: false };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function mountUpdates(app, checker, updater) {
|
|
129
|
+
const view = (info) => ({ ...info, operation: updater?.status() || null });
|
|
130
|
+
app.get("/api/system/update", (_req, res) =>
|
|
131
|
+
res.json(view(checker.status())),
|
|
132
|
+
);
|
|
133
|
+
app.post("/api/system/update/check", async (_req, res) =>
|
|
134
|
+
res.json(view(await checker.check())),
|
|
135
|
+
);
|
|
136
|
+
app.post("/api/system/update/install", async (req, res) => {
|
|
137
|
+
try {
|
|
138
|
+
if (!updater) throw Error("当前实例不支持自动更新");
|
|
139
|
+
res.json(await updater.install(req.body.version));
|
|
140
|
+
} catch (error) {
|
|
141
|
+
res.status(400).json({ error: error.message });
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{$ as e,Ct as t,E as n,Et as r,H as i,I as a,J as o,M as s,P as c,Tt as l,U as u,V as d,W as f,X as p,Y as m,Z as h,a as g,ct as _,dt as v,et as y,f as b,gt as x,ht as S,it as C,j as w,lt as T,m as E,n as D,nt as O,o as k,q as A,rt as j,s as M,t as N,ut as P,v as F,vt as I,wt as L,xt as R,yt as z,z as B}from"./Empty-DP6WMGHi.js";import{t as V}from"./formatters-ZmW_xFHw.js";import{F as ee,c as H,f as te,h as U,j as W,m as G,n as K,o as ne,p as q,u as J}from"./index-4rItIkD-.js";import{t as re}from"./media-SMfTT-yc.js";import{t as Y}from"./Field-CtbdqaiB.js";async function ie(e,t){let n=new URLSearchParams({session:e});return t!==void 0&&n.set(`after`,String(t)),E(`/core/events?`+n.toString())}async function ae(e,t={}){let n=new URLSearchParams({session:e});return t.limit&&n.set(`limit`,String(t.limit)),t.compact&&n.set(`compact`,`1`),E(`/core/traces?`+n.toString())}async function X(e){return E(`/core/traces/`+e)}async function oe(e){return E(`/simulate`,`POST`,e)}async function se(e,t){return E(`/decisions/`+e+`/feedback`,`POST`,{tag:t})}async function Z(e){return E(`/core/replay`,`POST`,e)}async function ce(e){return E(`/core/sessions`,`POST`,e)}async function le(e,t){return E(`/core/sessions/`+encodeURIComponent(e),`PATCH`,{enabled:t})}async function ue(e,t){return E(`/core/sessions/`+encodeURIComponent(e)+`/archive`,`PATCH`,{archived:t})}async function de(e){return E(`/core/sessions/`+encodeURIComponent(e),`DELETE`,{})}async function fe(e,t){return E(`/core/sessions/`+encodeURIComponent(e),`PUT`,t)}async function pe(e){return E(`/core/sessions/`+encodeURIComponent(e)+`/summaries`)}async function me(e){return E(`/core/sessions/`+encodeURIComponent(e)+`/context`,`DELETE`,{})}var he={class:`session-list`},ge={class:`list-head`},Q=[`placeholder`,`aria-label`],_e={class:`rows scroll-pane`},ve=[`data-pick`,`aria-current`,`onClick`],ye={class:`who`},be=[`aria-label`],xe=[`title`],Se=[`checked`,`aria-label`,`onChange`],Ce={key:0,class:`muted empty-line`},we={key:1,class:`archive-list`},Te={class:`row`},Ee=[`data-restore-session`,`onClick`],De=[`data-delete-session`,`onClick`],Oe=D(O({__name:`SessionList`,props:{sessions:{},selected:{},unread:{}},emits:[`select`,`add`,`toggle`,`restore`,`remove`],setup(n,{emit:i}){let s=n,c=i,u=R(``),m=A(()=>s.sessions.filter(e=>!e.archived&&`${e.name} ${e.id}`.toLowerCase().includes(u.value.toLowerCase()))),g=A(()=>s.sessions.filter(e=>e.archived));return(i,s)=>(P(),h(`div`,he,[o(`div`,ge,[o(`h2`,null,[e(r(t(a)(`会话`))+` `,1),o(`small`,null,r(m.value.length),1)]),o(`button`,{class:`small primary`,onClick:s[0]||=e=>c(`add`)},r(t(a)(`+ 添加会话`)),1)]),z(o(`input`,{"onUpdate:modelValue":s[1]||=e=>u.value=e,type:`search`,placeholder:t(a)(`搜索名称或号码`),"aria-label":t(a)(`搜索会话`)},null,8,Q),[[d,u.value]]),o(`div`,_e,[(P(!0),h(f,null,v(m.value,e=>(P(),h(`div`,{key:e.id,class:L([`session-row`,{selected:e.id===n.selected,paused:!e.enabled}])},[o(`button`,{class:`row-main`,"data-pick":e.id,"aria-current":e.id===n.selected?`true`:void 0,onClick:t=>c(`select`,e.id)},[o(`span`,{class:`badge`,style:l({"--hue":t(te)(e.id)})},r(e.kind===`private`?t(a)(`私`):t(a)(`群`)),5),o(`span`,ye,[o(`b`,null,r(t(U)(e)),1),o(`small`,null,r(e.enabled?t(a)(`参与中`):t(a)(`已暂停`)),1)]),n.unread[e.id]?(P(),h(`span`,{key:0,class:`unread`,"aria-label":t(a)(`{v} 条没细看`,{v:n.unread[e.id]})},r(n.unread[e.id]),9,be)):p(``,!0)],8,ve),o(`label`,{class:`mini-switch`,title:e.enabled?t(a)(`暂停参与`):t(a)(`开启参与`)},[o(`input`,{type:`checkbox`,checked:e.enabled,"aria-label":t(a)(`参与:{v}`,{v:t(U)(e)}),onChange:t=>c(`toggle`,e)},null,40,Se)],8,xe)],2))),128)),m.value.length?p(``,!0):(P(),h(`p`,Ce,r(t(a)(`暂无匹配的会话。`)),1)),g.value.length?(P(),h(`details`,we,[o(`summary`,null,r(t(a)(`已归档 · {length}`,{length:g.value.length})),1),(P(!0),h(f,null,v(g.value,e=>(P(),h(`article`,{key:e.id},[o(`b`,null,r(t(U)(e)),1),o(`div`,Te,[o(`button`,{class:`small`,"data-restore-session":e.id,onClick:t=>c(`restore`,e.id)},r(t(a)(`恢复`)),9,Ee),o(`button`,{class:`small danger`,"data-delete-session":e.id,onClick:t=>c(`remove`,e.id)},r(t(a)(`永久删除`)),9,De)])]))),128))])):p(``,!0)])]))}}),[[`__scopeId`,`data-v-9b8dcbac`]]),ke={class:`muted`},Ae={key:0,class:`notice`,role:`status`},je=[`pattern`,`inputmode`],Me=[`disabled`],Ne=O({__name:`AddSession`,props:{open:{type:Boolean},channel:{},ready:{type:Boolean}},emits:[`close`,`add`],setup(e,{emit:n}){let i=e,s=R(`group`),c=n,l=A(()=>i.channel===`qqbot`),d=A(()=>l.value?`[0-9A-Za-z_\\-]{8,64}`:`\\d{4,20}`);function f(e){let t=e.target;c(`add`,{...Object.fromEntries(new FormData(t)),channel:i.channel})}return(n,i)=>(P(),m(ne,{open:e.open,title:t(a)(`添加群聊 / 私聊`),eyebrow:t(a)(`NEW PLACE`),width:`440px`,onClose:i[1]||=e=>c(`close`)},{default:I(()=>[o(`form`,{id:`addSession`,class:`stack`,onSubmit:u(f,[`prevent`])},[o(`p`,ke,r(t(a)(`添加之后再决定要不要让 TA 参与;开不开口仍由 TA 自己决定。`)),1),l.value&&!e.ready?(P(),h(`p`,Ae,r(t(a)(`请先在「连接 QQ」里填写机器人的 AppID`)),1)):p(``,!0),y(Y,{label:t(a)(`类型`)},{default:I(()=>[y(H,{modelValue:s.value,"onUpdate:modelValue":i[0]||=e=>s.value=e,name:`kind`,"aria-label":t(a)(`类型`),options:[{value:`group`,label:t(a)(`群聊`)},{value:`private`,label:t(a)(`私聊`)}]},null,8,[`modelValue`,`aria-label`,`options`])]),_:1},8,[`label`]),y(Y,{label:l.value?t(a)(`群 openid / 用户 openid`):t(a)(`群号 / QQ 号`),hint:l.value?t(a)(`8–64 位字母、数字、下划线或短横线。官方平台的会话通常会在有人 @ 她或私聊她时自动出现,这里只用于提前登记。`):t(a)(`4–20 位数字`)},{default:I(()=>[o(`input`,{name:`id`,required:``,pattern:d.value,inputmode:l.value?`text`:`numeric`,autocomplete:`off`},null,8,je)]),_:1},8,[`label`,`hint`]),y(Y,{label:t(a)(`显示名称`)},{default:I(()=>[...i[2]||=[o(`input`,{name:`name`,required:``,maxlength:`100`,autocomplete:`off`},null,-1)]]),_:1},8,[`label`]),o(`button`,{class:`primary`,disabled:l.value&&!e.ready},r(t(a)(`添加会话`)),9,Me)],32)]),_:1},8,[`open`,`title`,`eyebrow`]))}}),Pe={class:`transcript`},Fe={class:`t-head`},Ie=[`aria-label`,`title`],Le={class:`t-title`},Re={id:`liveStatus`,class:`faint`},ze={class:`t-tools`},Be=[`disabled`],Ve={id:`liveMessages`,class:`chat-messages scroll-pane`},He={key:0,class:`day-label`},Ue=[`data-status`],We={key:0},Ge={class:`body`},Ke={class:`message-meta`},qe={key:0,class:`chip`,"data-tone":`warn`},Je={class:`text`},Ye=[`aria-expanded`,`onClick`],Xe={key:0,class:`why`},Ze={key:0,class:`muted`},Qe={key:0},$e={key:1},et={key:2,class:`fb`},tt={class:`composer-note`},nt={class:`chip`,"data-tone":`warn`},rt={class:`faint`},it={class:`composer-row`},at={class:`uid`},ot={class:`sr-only`},st=[`title`],ct=[`placeholder`,`onKeydown`],lt={class:`check mention`},ut=[`disabled`],dt={key:1,class:`composer off`},ft={class:`faint`},pt=D(O({__name:`Transcript`,props:{session:{},sessionId:{},events:{},traces:{},decisions:{},status:{},demo:{type:Boolean},compact:{type:Boolean},sending:{type:Boolean}},emits:[`simulate`,`feedback`,`openList`,`openReplay`],setup(n,{expose:g,emit:_}){let b=n,x=_,S=c({glanced:w(`TA 扫了一眼`),silent:w(`TA 看了,没出声`),deferred:w(`TA 睡着了,醒来再看`)}),T=R(`10001`),E=R(``),D=R(!0),O=R(),k=R({}),j=()=>V(s(),{month:`long`,day:`numeric`,weekday:`short`}),F=A(()=>{let e=b.events.map(e=>({type:`message`,key:`m`+e.seq,time:e.time,event:e})),t=b.events[0]?.time??2**53-1;for(let n of b.traces)S[n.status]&&n.time>=t&&e.push({type:`divider`,key:`t`+n.id,time:n.time,status:n.status,reasons:[n.reason].filter(Boolean),count:1});e.sort((e,t)=>e.time-t.time);let n=[],r=``;for(let t of e){let e=j().format(t.time);e!==r&&(r=e,n.push({type:`day`,key:`d`+t.time,time:t.time,label:e}));let i=n[n.length-1];if(t.type===`divider`&&i?.type===`divider`&&i.status===t.status){i.count+=1,t.reasons[0]&&!i.reasons.includes(t.reasons[0])&&i.reasons.push(t.reasons[0]);continue}n.push(t)}return n});function I(e){let t=String(e.payload.text||``);return b.decisions.filter(n=>n.session_id===e.session_id&&n.reply&&n.reply.includes(t)&&Math.abs(n.time-e.time)<6e5).sort((t,n)=>Math.abs(t.time-e.time)-Math.abs(n.time-e.time))[0]}function ee(e){return b.traces.filter(t=>t.status===`sent`&&t.time<=e.time+1e3).sort((e,t)=>t.time-e.time)[0]}async function U(e){if(k.value[e.seq]){let{[e.seq]:t,...n}=k.value;k.value=n;return}k.value={...k.value,[e.seq]:{loading:!0,detail:null}};let t=ee(e),n=null;try{n=t?await X(t.id):null}catch{n=null}k.value[e.seq]&&(k.value={...k.value,[e.seq]:{loading:!1,detail:n}})}async function W(){await C(),O.value?.focus()}function G(){E.value.trim()&&!b.sending&&(x(`simulate`,{userId:T.value,text:E.value,mentioned:D.value}),E.value=``)}return g({focusComposer:W}),(s,c)=>(P(),h(`section`,Pe,[o(`header`,Fe,[n.compact?(P(),h(`button`,{key:0,class:`icon-button`,"aria-label":t(a)(`全部会话`),title:t(a)(`全部会话`),onClick:c[0]||=e=>x(`openList`)},` ☰ `,8,Ie)):p(``,!0),n.session?(P(),h(`span`,{key:1,class:`badge`,style:l({"--hue":t(te)(n.session.id)})},r(n.session.kind===`private`?t(a)(`私`):t(a)(`群`)),5)):p(``,!0),o(`div`,Le,[o(`h2`,null,r(n.session?.name||t(a)(`选择一段对话`)),1),o(`span`,Re,r(n.status),1)]),o(`div`,ze,[o(`button`,{class:`small sim-button`,disabled:!n.sessionId,onClick:W},r(t(a)(`模拟消息`)),9,Be),o(`button`,{class:`small`,onClick:c[1]||=e=>x(`openReplay`)},r(t(a)(`回到那一刻`)),1)])]),o(`div`,Ve,[(P(!0),h(f,null,v(F.value,n=>(P(),h(f,{key:n.key},[n.type===`day`?(P(),h(`div`,He,[o(`span`,null,r(n.label),1)])):n.type===`divider`?(P(),h(`div`,{key:1,class:`divider`,"data-status":n.status},[o(`span`,null,r(t(S)[n.status])+r(n.count>1?` × ${n.count}`:``),1),n.reasons.length?(P(),h(`small`,We,r(n.reasons.slice(0,2).join(`;`)),1)):p(``,!0)],8,Ue)):(P(),h(`article`,{key:2,class:L([`message`,{bot:n.event.role===`assistant`,simulated:n.event.payload.simulated}])},[n.event.role===`assistant`?p(``,!0):(P(),h(`span`,{key:0,class:`avatar`,style:l({"--hue":t(te)(String(n.event.payload.userId||n.event.payload.name))}),"aria-hidden":`true`},r(t(q)(n.event.payload.name)),5)),o(`div`,Ge,[o(`div`,Ke,[o(`b`,null,r(n.event.payload.name),1),o(`time`,null,r(t(J)(n.event.time)),1),n.event.payload.simulated?(P(),h(`span`,qe,r(t(a)(`模拟`)),1)):p(``,!0)]),o(`p`,Je,r(n.event.payload.text),1),n.event.role===`assistant`?(P(),h(f,{key:0},[o(`button`,{class:`text-button why-toggle`,"aria-expanded":!!k.value[n.event.seq],onClick:e=>U(n.event)},r(k.value[n.event.seq]?t(a)(`收起`):t(a)(`为什么这么说`)),9,Ye),k.value[n.event.seq]?(P(),h(`div`,Xe,[k.value[n.event.seq].loading?(P(),h(`p`,Ze,r(t(a)(`正在翻当时的记录…`)),1)):(P(),h(f,{key:1},[k.value[n.event.seq].detail?.data?.decision?.appraisal?(P(),h(`p`,Qe,[o(`b`,null,r(t(a)(`TA 当时的理解:`)),1),e(r(k.value[n.event.seq].detail.data.decision.appraisal),1)])):p(``,!0),o(`p`,null,[o(`b`,null,r(t(a)(`理由:`)),1),e(r(k.value[n.event.seq].detail?.data?.reason||k.value[n.event.seq].detail?.data?.decision?.reason||I(n.event)?.reason||t(a)(`没有留下理由。`)),1)]),k.value[n.event.seq].detail?.data?.snapshot?.knowledge?.length?(P(),h(`p`,$e,[o(`b`,null,r(t(a)(`引用的资料:`)),1),e(r(k.value[n.event.seq].detail.data.snapshot.knowledge.map(e=>e.title).join(`、`)),1)])):p(``,!0),I(n.event)?(P(),h(`label`,et,[e(r(t(a)(`这句怎么样?`))+` `,1),y(H,{"model-value":I(n.event).feedback||``,"aria-label":t(a)(`这句怎么样?`),options:[{value:``,label:t(a)(`选择评价`)},...Object.entries(t(M).health.feedbackLabels||{}).map(([e,t])=>({value:e,label:String(t)}))],"onUpdate:modelValue":e=>x(`feedback`,I(n.event).id,e)},null,8,[`model-value`,`aria-label`,`options`,`onUpdate:modelValue`])])):p(``,!0)],64))])):p(``,!0)],64)):p(``,!0)])],2))],64))),128)),n.events.length?p(``,!0):(P(),m(N,{key:0,title:n.sessionId?t(a)(`等待新的消息`):t(a)(`选择一段对话`),text:t(a)(`群聊与私聊消息会在这里实时出现;TA 扫一眼、没出声也会留下一条分隔线。`)},null,8,[`title`,`text`]))]),n.demo?(P(),h(`form`,{key:0,id:`simulate`,class:`composer`,onSubmit:u(G,[`prevent`])},[o(`div`,tt,[o(`span`,nt,r(t(a)(`模拟消息`)),1),o(`span`,rt,r(t(a)(`作为一条模拟消息走完整流程,写进模拟会话;不会发到 QQ,也不进入真实记忆。`)),1)]),o(`div`,it,[o(`label`,at,[o(`span`,ot,r(t(a)(`体验用户 ID`)),1),z(o(`input`,{"onUpdate:modelValue":c[2]||=e=>T.value=e,name:`userId`,pattern:`\\d{4,20}`,required:``,title:t(a)(`体验用户 ID`)},null,8,st),[[d,T.value]])]),z(o(`textarea`,{ref_key:`composer`,ref:O,"onUpdate:modelValue":c[3]||=e=>E.value=e,name:`text`,rows:`1`,placeholder:t(a)(`以这位群友的身份说一句…`),required:``,onKeydown:i(u(G,[`exact`,`prevent`]),[`enter`])},null,40,ct),[[d,E.value]]),o(`label`,lt,[z(o(`input`,{"onUpdate:modelValue":c[4]||=e=>D.value=e,type:`checkbox`},null,512),[[B,D.value]]),e(r(t(a)(`视为 @`)),1)]),o(`button`,{class:`primary`,disabled:n.sending||!n.sessionId},r(n.sending?t(a)(`TA 在想…`):t(a)(`发送模拟消息`)),9,ut)])],32)):(P(),h(`div`,dt,[o(`span`,ft,r(t(a)(`真实模式下不能模拟消息。想看看 TA 会怎么回,可以「和 TA 聊聊」——那是试聊,什么都不写入。`)),1),o(`button`,{class:`small`,onClick:c[5]||=e=>t(M).chatOpen=!0},r(t(a)(`和 TA 聊聊`)),1)]))]))}}),[[`__scopeId`,`data-v-849aeb0f`]]),mt={class:`panel`},ht={key:0,class:`panel-body`},gt={key:1,class:`panel-body scroll-pane`},_t={class:`here`},vt={class:`facts`},yt={class:`eyebrow`},bt={key:1,class:`muted`},xt={key:2,class:`faint`},St={class:`eyebrow`},Ct={class:`faint`},wt={class:`row actions`},Tt=[`data-toggle`],Et=[`data-archive`],Dt=[`open`],Ot=[`data-session`],kt={class:`faint`},At=[`value`],jt={class:`faint`},Mt={class:`check`},Nt={class:`advanced-policy`},Pt={class:`stack tight`},Ft={class:`check`},It={class:`check`},Lt={class:`check`},Rt={class:`save-bar`},zt={class:`primary`,type:`submit`},Bt={class:`faint`},Vt={class:`fold summary-list`},Ht={key:0,class:`muted`},Ut={class:`faint`},Wt={key:0},Gt={key:2,class:`panel-body feedback-pane`},Kt={class:`faint`},qt={id:`feedbackList`,class:`scroll-pane`},Jt={class:`faint`},Yt={class:`reply-excerpt`},Xt={key:0,class:`muted`},Zt={class:`pagination`},Qt=[`disabled`],$t=[`disabled`],$=6,en=D(O({__name:`SessionPanel`,props:j({session:{},group:{},summaries:{},decisions:{}},{settingsOpen:{type:Boolean,default:!1},settingsOpenModifiers:{},tab:{default:`here`},tabModifiers:{}}),emits:j([`toggle`,`archive`,`clear`,`save`,`feedback`],[`update:settingsOpen`,`update:tab`]),setup(n,{emit:i}){let s=n,l=S(n,`settingsOpen`),m=S(n,`tab`),g=i,_=c([w(`近期细摘要`),w(`中期摘要`),w(`较早摘要`),w(`远期摘要`)]),b=R(0),C=A(()=>s.decisions.filter(e=>e.session_id===s.session?.id&&e.reply)),T=A(()=>Math.max(1,Math.ceil(C.value.length/$))),E=R(!1),D=R({});x(()=>s.session?.policy,e=>{D.value={...e||{}}},{immediate:!0,deep:!0}),x(()=>s.session?.id,()=>{b.value=0,E.value=!1});function O(e){let t=e?.face;return t?[t.role,t.tone,t.content].filter(Boolean).join(`
|
|
2
|
-
|
|
3
|
-
`):``}function k(e){let t=e.target,n=Object.fromEntries(new FormData(t));for(let e of[`aggregateMs`,`maxWaitMs`,`contextMessages`,`maxReply`])n[e]=Number(n[e]);for(let e of[`memory`,`selectiveVision`,`deepCheck`,`compaction`])n[e]=t.elements.namedItem(e).checked;g(`save`,n)}return(i,s)=>(P(),h(`section`,mt,[y(K,{modelValue:m.value,"onUpdate:modelValue":s[0]||=e=>m.value=e,label:t(a)(`这个会话`),items:[{key:`here`,label:t(a)(`这个会话`)},{key:`feedback`,label:t(a)(`反馈`)}]},null,8,[`modelValue`,`label`,`items`]),n.session?m.value===`here`?(P(),h(`div`,gt,[o(`div`,_t,[o(`div`,vt,[o(`div`,null,[o(`span`,yt,r(t(a)(`TA 在这里的样子`)),1),O(n.group)?(P(),h(f,{key:0},[o(`p`,{class:L([`face-body`,E.value?`open`:`clamp`])},r(O(n.group)),3),O(n.group).length>96?(P(),h(`button`,{key:0,type:`button`,class:`text-button`,onClick:s[1]||=e=>E.value=!E.value},r(E.value?t(a)(`收起`):t(a)(`展开完整内容`)),1)):p(``,!0)],64)):(P(),h(`p`,bt,r(t(a)(`还没有形成在这里的样子。`)),1)),n.group?.face?.aspiration?(P(),h(`small`,xt,r(t(a)(`想成为:{aspiration}`,{aspiration:n.group.face.aspiration})),1)):p(``,!0)]),o(`div`,null,[o(`span`,St,r(n.session.kind===`private`?t(a)(`这段私聊给 TA 的感觉`):t(a)(`这个群给 TA 的感觉`)),1),o(`p`,null,r(n.group?.bond?.feel||t(a)(`刚来这里不久`)),1)])]),o(`p`,Ct,r(t(a)(`在这里开不开口由 TA 自己决定:没有参与概率和冷却。TA 对这里每个人的感觉在「人际」里看。`)),1),o(`div`,wt,[o(`button`,{id:`toggleSession`,"data-toggle":n.session.id,class:L({primary:!n.session.enabled}),onClick:s[2]||=e=>g(`toggle`,n.session)},r(n.session.enabled?t(a)(`暂停参与`):t(a)(`开启参与`)),11,Tt),o(`button`,{"data-archive":n.session.id,onClick:s[3]||=e=>g(`archive`,n.session.id)},r(t(a)(`归档`)),9,Et),o(`button`,{id:`clearLiveContext`,onClick:s[4]||=e=>g(`clear`)},r(t(a)(`清空上下文`)),1)]),o(`details`,{class:`fold session-settings`,open:l.value,onToggle:s[19]||=e=>l.value=e.target.open},[o(`summary`,null,r(t(a)(`会话设置`)),1),(P(),h(`form`,{key:n.session.id,"data-session":n.session.id,class:`stack tight`,onSubmit:u(k,[`prevent`]),onInput:s[17]||=e=>t(M).dirty=!0,onChange:s[18]||=e=>t(M).dirty=!0},[o(`p`,kt,r(t(a)(`所有会话和独处都用模型库里的默认模型;其余已启用的模型按列表顺序做备用。她在这个聊天窗里用什么样子,由她自己从经历里决定。`)),1),o(`label`,null,[e(r(t(a)(`会话名称`))+` `,1),o(`input`,{name:`name`,maxlength:`100`,autocomplete:`off`,value:n.session.name},null,8,At),o(`small`,jt,r(t(a)(`官方机器人平台不提供群名称,可以在这里自己起一个`)),1)]),o(`label`,Mt,[z(o(`input`,{name:`selectiveVision`,type:`checkbox`,"onUpdate:modelValue":s[5]||=e=>D.value.selectiveVision=e,onInput:s[6]||=u(()=>{},[`stop`])},null,544),[[B,D.value.selectiveVision]]),e(r(t(a)(`节能看图:只在被 @ 或明确要求时,看这一条和附近的图`)),1)]),o(`details`,Nt,[o(`summary`,null,r(t(a)(`高级策略`)),1),o(`div`,Pt,[o(`label`,null,[e(r(t(a)(`聚合窗口 ms`)),1),z(o(`input`,{name:`aggregateMs`,type:`number`,"onUpdate:modelValue":s[7]||=e=>D.value.aggregateMs=e},null,512),[[d,D.value.aggregateMs,void 0,{number:!0}]])]),o(`label`,null,[e(r(t(a)(`最长等待 ms`)),1),z(o(`input`,{name:`maxWaitMs`,type:`number`,"onUpdate:modelValue":s[8]||=e=>D.value.maxWaitMs=e},null,512),[[d,D.value.maxWaitMs,void 0,{number:!0}]])]),o(`label`,null,[e(r(t(a)(`近期原文条数(10–500)`)),1),z(o(`input`,{name:`contextMessages`,type:`number`,min:`10`,max:`500`,"onUpdate:modelValue":s[9]||=e=>D.value.contextMessages=e},null,512),[[d,D.value.contextMessages,void 0,{number:!0}]])]),o(`label`,null,[e(r(t(a)(`一轮总字数`)),1),z(o(`input`,{name:`maxReply`,type:`number`,"onUpdate:modelValue":s[10]||=e=>D.value.maxReply=e},null,512),[[d,D.value.maxReply,void 0,{number:!0}]])]),o(`label`,Ft,[z(o(`input`,{name:`compaction`,type:`checkbox`,"onUpdate:modelValue":s[11]||=e=>D.value.compaction=e,onInput:s[12]||=u(()=>{},[`stop`])},null,544),[[B,D.value.compaction]]),e(r(t(a)(`上下文压缩:更早的聊天定期整理成分层摘要,越近越详细`)),1)]),o(`label`,It,[z(o(`input`,{name:`memory`,type:`checkbox`,"onUpdate:modelValue":s[13]||=e=>D.value.memory=e,onInput:s[14]||=u(()=>{},[`stop`])},null,544),[[B,D.value.memory]]),e(r(t(a)(`长期记忆`)),1)]),o(`label`,Lt,[z(o(`input`,{name:`deepCheck`,type:`checkbox`,"onUpdate:modelValue":s[15]||=e=>D.value.deepCheck=e,onInput:s[16]||=u(()=>{},[`stop`])},null,544),[[B,D.value.deepCheck]]),e(r(t(a)(`倾诉、纠正和危机时额外复审一次回复`)),1)])])]),o(`div`,Rt,[o(`button`,zt,r(t(a)(`保存会话设置`)),1),o(`small`,Bt,r(t(M).dirty?t(a)(`有未保存的修改`):t(a)(`保存后下一轮生效`)),1)])],40,Ot))],40,Dt),o(`details`,Vt,[o(`summary`,null,r(t(a)(`语境摘要 · {length} 段`,{length:n.summaries.length})),1),n.summaries.length?p(``,!0):(P(),h(`p`,Ht,r(t(a)(`还没有摘要。聊天超过近期原文条数后,更早的内容会自动整理到这里。`)),1)),(P(!0),h(f,null,v(n.summaries,e=>(P(),h(`article`,{key:e.id,class:`summary`},[o(`header`,null,[o(`b`,null,r(t(_)[e.level]||t(a)(`摘要`)),1),o(`small`,Ut,r(t(a)(`{period} · {messages} 条消息`,{period:e.period,messages:e.messages})),1)]),o(`p`,null,r(e.summary),1),e.keyPoints?.length?(P(),h(`ul`,Wt,[(P(!0),h(f,null,v(e.keyPoints,(e,n)=>(P(),h(`li`,{key:n},r(e.open?t(a)(`未完:`):``)+r(e.text),1))),128))])):p(``,!0)]))),128))])])])):(P(),h(`div`,Gt,[o(`p`,Kt,r(t(a)(`针对具体回复评价口吻;这是 TA 在这个会话里听到的话,不会改写天性。`)),1),o(`div`,qt,[(P(!0),h(f,null,v(C.value.slice(b.value*$,b.value*$+$),n=>(P(),h(`article`,{key:n.id,class:`feedback-item`},[o(`time`,Jt,r(t(J)(n.time)),1),o(`p`,Yt,r(n.reply),1),o(`label`,null,[e(r(t(a)(`这句回复怎么样?`))+` `,1),y(H,{"data-feedback":n.id,"model-value":n.feedback||``,"aria-label":t(a)(`这句回复怎么样?`),options:[{value:``,label:t(a)(`选择评价`)},...Object.entries(t(M).health.feedbackLabels||{}).map(([e,t])=>({value:e,label:String(t)}))],"onUpdate:modelValue":e=>g(`feedback`,n.id,e)},null,8,[`data-feedback`,`model-value`,`aria-label`,`options`,`onUpdate:modelValue`])])]))),128)),C.value.length?p(``,!0):(P(),h(`p`,Xt,r(t(a)(`还没有可以评价的回复。`)),1))]),o(`div`,Zt,[o(`button`,{class:`small`,disabled:b.value===0,onClick:s[20]||=e=>b.value--},`←`,8,Qt),o(`span`,null,r(b.value+1)+` / `+r(T.value),1),o(`button`,{class:`small`,disabled:b.value+1>=T.value,onClick:s[21]||=e=>b.value++},` → `,8,$t)])])):(P(),h(`div`,ht,[y(N,{title:t(a)(`还没有选中会话`),text:t(a)(`从左边选一个群聊或私聊。`)},null,8,[`title`,`text`])]))]))}}),[[`__scopeId`,`data-v-96fbad3d`]]),tn={class:`replay`},nn={class:`col source`},rn={class:`col-head`},an={id:`events`,class:`scroll-pane`},on={key:0},sn={key:1,class:`muted`},cn=[`disabled`],ln={class:`faint`},un={class:`col`},dn={class:`col-head`},fn={id:`traces`,class:`scroll-pane`},pn=[`data-trace`,`onClick`],mn=[`data-tone`],hn={key:0,class:`muted`},gn={class:`col`},_n={class:`col-head`},vn={class:`scroll-pane inspect`},yn={key:0,class:`metrics`},bn={id:`traceTokens`},xn={key:1,class:`notice`},Sn={id:`traceDetail`},Cn=D(O({__name:`ReplaySheet`,props:{open:{type:Boolean},sessionId:{},events:{}},emits:[`close`,`load`],setup(n,{emit:i}){let s=n,l=i,g=c({sent:w(`开口了`),silent:w(`没出声`),glanced:w(`扫了一眼`),deferred:w(`睡着了`),running:w(`处理中`),error:w(`失败`),complete:w(`完成`),stale:w(`旧稿作废`),cancelled:w(`已取消`),interrupted:w(`已中断`)}),_=c({live:w(`真实聊天`),demo:w(`模拟预览`),replay:w(`测试回放`),memory:w(`记忆整理`),summary:w(`语境压缩`)}),y=R([]),b=R(null),S=R(``),C=R(``),T=R(!1),E=R(``);async function D(){s.sessionId&&(y.value=await ae(s.sessionId))}function O(){let e=s.events.map(e=>Number(e.seq));e.length&&(S.value||=String(Math.min(...e)),C.value||=String(Math.max(...e)))}async function k(){if(!T.value){T.value=!0;try{F(a(`回放正在运行`));let e=await Z({session:s.sessionId,from:Number(S.value),to:Number(C.value)});E.value=JSON.stringify(e,null,2),F(a(`回放结束`)),await D()}catch(e){F(e.message,!0)}finally{T.value=!1}}}async function A(e){b.value=await X(e),E.value=JSON.stringify(b.value,null,2)}function j(e){return e?a(`输入 {v}(缓存读 {v2} / 写 {v3})· 输出 {v4}`,{v:G(e.input),v2:G(e.cachedRead),v3:G(e.cacheWrite),v4:G(e.output)}):`—`}return x(()=>[s.open,s.sessionId],()=>{s.open&&(S.value=``,C.value=``,b.value=null,O(),D())},{immediate:!0}),x(()=>s.events.length,O),(i,s)=>(P(),m(ne,{open:n.open,title:t(a)(`回到那一刻`),eyebrow:`REPLAY`,width:`1180px`,onClose:s[3]||=e=>l(`close`)},{default:I(()=>[o(`div`,tn,[o(`section`,nn,[o(`div`,rn,[o(`h3`,null,r(t(a)(`历史消息`)),1),o(`button`,{id:`loadMessages`,class:`small`,onClick:s[0]||=e=>{l(`load`),D()}},r(t(a)(`↻ 加载`)),1)]),o(`div`,an,[n.events.length?(P(),h(`table`,on,[o(`thead`,null,[o(`tr`,null,[o(`th`,null,r(t(a)(`序号`)),1),o(`th`,null,r(t(a)(`发送者`)),1),o(`th`,null,r(t(a)(`消息`)),1)])]),o(`tbody`,null,[(P(!0),h(f,null,v([...n.events].reverse(),e=>(P(),h(`tr`,{key:e.seq},[o(`td`,null,r(e.seq),1),o(`td`,null,r(e.payload.name),1),o(`td`,null,r(e.payload.text),1)]))),128))])])):(P(),h(`p`,sn,r(t(a)(`当前会话暂无历史消息。`)),1))]),o(`form`,{class:`range`,onSubmit:u(k,[`prevent`])},[o(`label`,null,[e(r(t(a)(`起始序号`)),1),z(o(`input`,{"onUpdate:modelValue":s[1]||=e=>S.value=e,name:`from`,type:`number`,required:``},null,512),[[d,S.value]])]),o(`label`,null,[e(r(t(a)(`截止序号`)),1),z(o(`input`,{"onUpdate:modelValue":s[2]||=e=>C.value=e,name:`to`,type:`number`,required:``},null,512),[[d,C.value]])]),o(`button`,{id:`replay`,class:`primary`,disabled:T.value||!n.events.length},r(T.value?t(a)(`正在回放…`):t(a)(`回放这段对话`)),9,cn),o(`p`,ln,r(t(a)(`隔离回放:只测试回复,不发送 QQ,不修改真实记忆和 TA 的心智。`)),1)],32)]),o(`section`,un,[o(`div`,dn,[o(`h3`,null,r(t(a)(`处理记录`)),1)]),o(`div`,fn,[(P(!0),h(f,null,v(y.value,e=>(P(),h(`button`,{key:e.id,"data-trace":e.id,class:L([`trace-row`,{selected:b.value?.id===e.id}]),onClick:t=>A(e.id)},[o(`span`,null,[o(`time`,null,r(t(J)(e.time)),1),o(`small`,{class:`chip`,"data-tone":e.status===`error`?`danger`:`quiet`},r(t(g)[e.status]||e.status),9,mn)]),o(`p`,null,r(e.reason||e.error||t(a)(`查看处理详情`)),1)],10,pn))),128)),y.value.length?p(``,!0):(P(),h(`p`,hn,r(t(a)(`有新的消息处理记录后会显示在这里。`)),1))])]),o(`section`,gn,[o(`div`,_n,[o(`h3`,null,r(t(a)(`运行详情`)),1)]),o(`div`,vn,[b.value?(P(),h(`div`,yn,[o(`span`,null,[e(r(t(a)(`模式`)),1),o(`b`,null,r(t(_)[b.value.mode]||b.value.mode),1)]),o(`span`,null,[e(r(t(a)(`耗时`)),1),o(`b`,null,r(b.value.data?.elapsed??`—`)+` ms`,1)]),o(`span`,null,[e(r(t(a)(`模型调用`)),1),o(`b`,null,r(t(a)(`{v} 次`,{v:b.value.data?.calls?.length??0})),1)]),o(`span`,bn,[s[4]||=e(`Token`,-1),o(`b`,null,r(j(b.value.data?.tokens)),1)])])):p(``,!0),b.value?(P(),h(`p`,xn,r(b.value.reason||b.value.data?.decision?.reason||t(a)(`展开下方查看上下文、模型输入与原始输出。`)),1)):p(``,!0),o(`pre`,Sn,r(E.value||t(a)(`选择一条处理记录,查看最终上下文、Prompt、原始输出、Token 与耗时。`)),1)])])])]),_:1},8,[`open`,`title`]))}}),[[`__scopeId`,`data-v-839b3181`]]),wn={key:0,class:`card col list-col`},Tn={class:`card col talk-col`},En={class:`card col panel-col`},Dn={class:`sheet-list`},On=D(O({__name:`ChatsPage`,setup(e){let r=re(`(max-width: 1100px)`),i=A(()=>M.core?.sessions||[]),c=A(()=>i.value.filter(e=>!e.archived)),l=sessionStorage.activeSession,u=R(c.value.some(e=>e.id===l)?l:c.value[0]?.id||``),d=A(()=>c.value.find(e=>e.id===u.value)||null),f=R([]),v=R([]),S=R([]),w=R([]),E=R({}),D=R(``),O=R(!1),j=R(!1),N=R(!1),z=R(M.sub===`settings`),B=R(M.sub===`replay`),V=R(`here`),H=A(()=>M.health?.decisions||[]),te=A(()=>w.value.find(e=>e.session===u.value)||null),U=!1,G=!1,K=!1,q=``,J=0,Y=null,X=0;async function Z(e=!1){if(!u.value)return;if(U){G=!0,K||=e;return}let t=u.value,n=e||q!==t;U=!0;try{let[e,r]=await Promise.all([ie(t,n?void 0:J),ae(t,{limit:30,compact:!0})]);if(u.value!==t)return;let i=document.getElementById(`liveMessages`),o=q!==t||!i||i.scrollHeight-i.scrollTop-i.clientHeight<80;n?(f.value=e.reverse(),J=0):e.length&&(f.value=[...f.value,...e]);for(let t of e)J=Math.max(J,Number(t.seq)||0);!n&&e.length===100&&(G=!0),v.value=r,q=t,await C();let c=document.getElementById(`liveMessages`);c&&o&&(c.scrollTop=c.scrollHeight),D.value=a(`实时同步 · `)+new Date().toLocaleTimeString(s())}catch(e){D.value=a(`同步失败:`)+e.message}finally{if(U=!1,u.value!==t)G=!1,Z();else if(G){let e=K;G=!1,K=!1,Z(e)}}}async function he(){try{let[e,t]=await Promise.all([W.bonds(),W.overview()]);w.value=e.groups,E.value=Object.fromEntries(t.attention.map(e=>[e.session,e.unread]))}catch{}}x(u,e=>{if(Y?.abort(),e&&(sessionStorage.activeSession=e),f.value=[],v.value=[],S.value=[],q=``,J=0,G=!1,K=!1,N.value=!1,Z(),!e)return;pe(e).then(t=>{u.value===e&&(S.value=[...t].reverse())}).catch(()=>{});let t=new AbortController;Y=t;let n=``;ee(e=>{let t=Number(e?.messages);Number.isSafeInteger(t)&&t<J&&(q=``,J=0,f.value=[]);let r=JSON.stringify(e),i=n!==``&&r!==n;n=r,i&&Z()},{session:e,signal:t.signal,reconnectMs:1e3})},{immediate:!0}),x(c,e=>{e.some(e=>e.id===u.value)||(u.value=e[0]?.id||``)}),x(V,async e=>{if(e===`feedback`)try{M.health=await b()}catch{}}),x(B,e=>k(e?`replay`:z.value?`settings`:``)),x(()=>M.sub,e=>{e===`replay`&&(B.value=!0),e===`settings`&&(z.value=!0)}),x(()=>M.pulse,he),_(()=>{he(),X=window.setInterval(()=>Z(!0),15e3)}),T(()=>{clearInterval(X),Y?.abort()});async function ge(e){try{let t=await ce(e);await g(),u.value=t?.sessionId||`${e.kind}:${e.id}`,z.value=!0,V.value=`here`,j.value=!1,F(a(`会话已添加`))}catch(e){F(e.message,!0)}}async function Q(e){await le(e.id,!e.enabled),await g()}async function _e(e){await ue(e,!0),await g()}async function ve(e){await ue(e,!1),await g()}async function ye(e){await n(a(`永久删除这个会话?它的消息和设置都会消失。`),{title:a(`永久删除`),confirmText:a(`删除`),danger:!0})&&(await de(e),await g())}async function be(e){if(d.value)try{await fe(d.value.id,{...e,name:String(e.name||``).trim()||d.value.name}),M.dirty=!1,F(a(`会话配置已保存`)),await g()}catch(e){F(e.message,!0)}}async function xe(){u.value&&await n(a(`确定清空这个会话的消息上下文吗?长期记忆不会删除。`),{title:a(`清空上下文`),confirmText:a(`清空`),danger:!0})&&(await me(u.value),f.value=[],v.value=[],q=``,J=0,await Z(!0))}async function Se(e){if(!O.value&&u.value){O.value=!0;try{let t=await oe({sessionId:u.value,...e});F(t.reason||a(`已发送`)),M.health=await b(),await Z()}catch(e){F(e.message,!0)}finally{O.value=!1}}}async function Ce(e,t){try{await se(e,t),F(a(`反馈已保存`)),M.health=await b()}catch(e){F(e.message,!0)}}return(e,n)=>(P(),h(`div`,{class:L([`page chats`,{compact:t(r)}])},[t(r)?p(``,!0):(P(),h(`aside`,wn,[y(Oe,{sessions:i.value,selected:u.value,unread:E.value,onSelect:n[0]||=e=>u.value=e,onAdd:n[1]||=e=>j.value=!0,onToggle:Q,onRestore:ve,onRemove:ye},null,8,[`sessions`,`selected`,`unread`])])),o(`div`,Tn,[y(pt,{"session-id":u.value,session:d.value,events:f.value,traces:v.value,decisions:H.value,status:D.value||t(a)(`请选择会话`),demo:!!t(M).health.settings.demo,compact:t(r),sending:O.value,onSimulate:Se,onFeedback:Ce,onOpenList:n[2]||=e=>N.value=!0,onOpenReplay:n[3]||=e=>B.value=!0},null,8,[`session-id`,`session`,`events`,`traces`,`decisions`,`status`,`demo`,`compact`,`sending`])]),o(`aside`,En,[y(en,{"settings-open":z.value,"onUpdate:settingsOpen":n[4]||=e=>z.value=e,tab:V.value,"onUpdate:tab":n[5]||=e=>V.value=e,session:d.value,group:te.value,summaries:S.value,decisions:H.value,onToggle:Q,onArchive:_e,onClear:xe,onSave:be,onFeedback:Ce},null,8,[`settings-open`,`tab`,`session`,`group`,`summaries`,`decisions`])]),t(r)?(P(),m(ne,{key:1,open:N.value,title:t(a)(`全部会话`),eyebrow:`CHATS`,width:`400px`,onClose:n[8]||=e=>N.value=!1},{default:I(()=>[o(`div`,Dn,[y(Oe,{sessions:i.value,selected:u.value,unread:E.value,onSelect:n[6]||=e=>u.value=e,onAdd:n[7]||=e=>j.value=!0,onToggle:Q,onRestore:ve,onRemove:ye},null,8,[`sessions`,`selected`,`unread`])])]),_:1},8,[`open`,`title`])):p(``,!0),y(Ne,{open:j.value,channel:t(M).health?.connection?.channel===`qqbot`?`qqbot`:`onebot`,ready:!!t(M).health?.connection?.qqbot?.appId,onClose:n[9]||=e=>j.value=!1,onAdd:ge},null,8,[`open`,`channel`,`ready`]),y(Cn,{open:B.value,"session-id":u.value,events:f.value,onClose:n[10]||=e=>B.value=!1,onLoad:n[11]||=e=>Z(!0)},null,8,[`open`,`session-id`,`events`])],2))}}),[[`__scopeId`,`data-v-c328f9fe`]]);export{On as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{$ as e,Ct as t,D as n,E as r,Et as i,I as a,J as o,Q as s,St as c,Tt as l,U as u,V as d,W as f,X as p,Y as m,Z as h,_ as g,ct as _,dt as v,et as y,gt as b,n as x,nt as S,o as ee,ot as C,q as w,s as T,t as E,ut as D,v as O,vt as te,wt as k,xt as A,yt as j}from"./Empty-DP6WMGHi.js";import{A as ne,D as M,E as N,I as re,L as P,M as F,N as I,P as L,a as ie,j as R,n as ae,o as oe,r as z,u as B,w as V}from"./index-4rItIkD-.js";import{t as se}from"./media-SMfTT-yc.js";var H={class:`ribbon`},U={class:`ribbon-head`},W={class:`ribbon-title`},G={class:`eyebrow`},K={key:0,class:`latest-mood`},q=[`viewBox`,`aria-label`],J=[`x2`,`y1`,`y2`],Y=[`d`],ce=[`d`],le=[`d`],ue=[`cx`,`cy`,`r`],de={key:1,class:`muted empty-line`},fe=[`aria-label`],X=150,Z=58,pe=x(S({__name:`MoodRibbon`,props:{moods:{},baseline:{},timeZone:{}},setup(e){let n=e,r=X/2,s=A(),c=A(1e3),l=null;_(()=>{l=new ResizeObserver(([e])=>{c.value=Math.max(240,Math.round(e.contentRect.width))}),s.value&&l.observe(s.value)}),C(()=>l?.disconnect());let u=w(()=>{let e=[...n.moods].sort((e,t)=>e.created-t.created),t=e.length>1?(c.value-60)/(e.length-1):0;return e.map((e,n)=>({...e,x:30+n*t,y:r-Math.max(-1,Math.min(1,e.valence))*Z}))}),d=w(()=>{let e=u.value;if(e.length<2)return``;let t=`M${e[0].x.toFixed(1)},${e[0].y.toFixed(1)}`;for(let n=0;n<e.length-1;n++){let r=e[n-1]||e[n],i=e[n],a=e[n+1],o=e[n+2]||a;t+=`C${(i.x+(a.x-r.x)/6).toFixed(1)},${(i.y+(a.y-r.y)/6).toFixed(1)} ${(a.x-(o.x-i.x)/6).toFixed(1)},${(a.y-(o.y-i.y)/6).toFixed(1)} ${a.x.toFixed(1)},${a.y.toFixed(1)}`}return t}),m=w(()=>{let e=u.value;return d.value?`${d.value}L${e[e.length-1].x},${X}L${e[0].x},${X}Z`:``}),g=w(()=>r-(n.baseline??.15)*Z),y=w(()=>[...n.moods].slice(0,6)),b=w(()=>y.value[0]);return(n,r)=>(D(),h(`section`,H,[o(`div`,U,[o(`div`,W,[o(`span`,G,i(t(a)(`MOOD / 此刻的天气`)),1),o(`h2`,null,i(t(a)(`心情有自己的潮汐。`)),1),o(`p`,null,i(t(a)(`最近 {length} 次被经历牵动;有起伏,也会慢慢平静。`,{length:e.moods.length})),1)]),b.value?(D(),h(`div`,K,[o(`span`,null,i(t(a)(`刚刚留下的心情`)),1),o(`b`,null,i(b.value.feeling),1),o(`small`,null,i(b.value.cause||t(a)(`暂时说不清缘由`)),1)])):p(``,!0)]),o(`div`,{ref_key:`box`,ref:s,class:`ribbon-box`},[d.value?(D(),h(`svg`,{key:0,class:`ribbon-chart`,viewBox:`0 0 ${c.value} ${X}`,role:`img`,"aria-label":t(a)(`最近的心情曲线`)},[o(`defs`,null,[o(`linearGradient`,{id:`ribbon-stroke`,x1:`0`,y1:`0`,x2:`0`,y2:X,gradientUnits:`userSpaceOnUse`},[...r[0]||=[o(`stop`,{offset:`0%`,style:{"stop-color":`var(--cheek)`}},null,-1),o(`stop`,{offset:`50%`,style:{"stop-color":`var(--accent)`}},null,-1),o(`stop`,{offset:`100%`,style:{"stop-color":`var(--orb-c)`}},null,-1)]]),r[1]||=o(`linearGradient`,{id:`ribbon-fill`,x1:`0`,y1:`0`,x2:`0`,y2:`1`},[o(`stop`,{offset:`0%`,style:{"stop-color":`var(--accent)`,"stop-opacity":`0.22`}}),o(`stop`,{offset:`100%`,style:{"stop-color":`var(--accent)`,"stop-opacity":`0`}})],-1)]),o(`line`,{class:`base`,x1:`0`,x2:c.value,y1:g.value,y2:g.value},null,8,J),o(`path`,{class:`area`,d:m.value},null,8,Y),o(`path`,{class:`line-glow`,d:d.value},null,8,ce),o(`path`,{class:`line`,d:d.value},null,8,le),(D(!0),h(f,null,v(u.value,n=>(D(),h(`g`,{key:n.id,class:k([`pt`,n.valence>=0?`warm`:`cool`])},[o(`circle`,{cx:n.x,cy:n.y,r:4+n.intensity*6},[o(`title`,null,i(t(B)(n.created,e.timeZone))+` `+i(n.feeling)+`:`+i(n.cause||t(a)(`说不清为什么`)),1)],8,ue)],2))),128))],8,q)):(D(),h(`p`,de,i(t(a)(`心情还没有被什么事牵动过。每一次经历都可能推动 TA 的心情,随后又会慢慢回到平常。`)),1))],512),y.value.length?(D(),h(`ul`,{key:0,class:`recent`,"aria-label":t(a)(`最近的心情`)},[(D(!0),h(f,null,v(y.value,n=>(D(),h(`li`,{key:n.id,class:k(n.valence>=0?`warm`:`cool`)},[o(`b`,null,i(n.feeling),1),o(`span`,null,i(n.cause||t(a)(`说不清为什么`)),1),o(`small`,null,i(t(B)(n.created,e.timeZone))+` · `+i(t(N)[n.origin]||n.origin),1)],2))),128))],8,fe)):p(``,!0)]))}}),[[`__scopeId`,`data-v-fef06576`]]),me=[`aria-label`],he={class:`self-scene`},ge={class:`scene-copy`},_e={class:`eyebrow`},ve={class:`scene-stats`},ye={class:`self-lens`,"aria-hidden":`true`},be={class:`orb-seat`},xe={class:`currents-head`},Se={class:`current-grid`},Ce={class:`current-head`},we={class:`current-number`},Te={class:`flecks`},Ee=[`data-thread`,`onClick`],De={class:`fleck-copy`},Oe=[`aria-expanded`,`onClick`],ke={"aria-hidden":`true`},Q=6,Ae=x(S({__name:`StarMap`,props:{threads:{},kinds:{},activity:{}},emits:[`open`],setup(n,{emit:r}){let c=n,u=r,d=A({}),m=w(()=>[...new Set(c.threads.map(e=>e.kind))].map(e=>({kind:e,label:c.kinds[e]||e,color:V[e]||`var(--accent)`,items:c.threads.filter(t=>t.kind===e).sort((e,t)=>(t.salience??0)-(e.salience??0))}))),g=w(()=>c.threads.filter(e=>!e.faded&&(e.salience??0)>=.5).length);function _(e){return e.faded?a(`暂时放远`):e.core?a(`慢慢笃定`):a(`还在生长`)}function b(e){return d.value[e.kind]?e.items:e.items.slice(0,Q)}function x(e){d.value={...d.value,[e]:!d.value[e]}}return(r,c)=>(D(),h(`section`,{class:`starmap`,"aria-label":t(a)(`心灵星图:TA 关于自己的线索`)},[o(`div`,he,[o(`div`,ge,[o(`span`,_e,i(t(a)(`SELF / 正在形成的自己`)),1),o(`h2`,null,[e(i(t(a)(`不是一张固定的画像,`)),1),c[0]||=o(`br`,null,null,-1),o(`em`,null,i(t(a)(`而是一直在生长。`)),1)]),o(`p`,null,i(t(a)(`一件经历可能留下一点痕迹。点开一枚光片,可以看到它从哪里来、后来又怎么改变。`)),1),o(`div`,ve,[o(`span`,null,[o(`b`,null,i(n.threads.length),1),e(` `+i(t(a)(`枚线索`)),1)]),o(`span`,null,[o(`b`,null,i(g.value),1),e(` `+i(t(a)(`枚此刻贴近`)),1)])])]),o(`div`,ye,[c[1]||=s(`<span class="lens-halo halo-one" data-v-f3560fca></span><span class="lens-halo halo-two" data-v-f3560fca></span><span class="lens-droplet droplet-one" data-v-f3560fca></span><span class="lens-droplet droplet-two" data-v-f3560fca></span><span class="lens-droplet droplet-three" data-v-f3560fca></span>`,5),o(`div`,be,[y(L,{mood:t(P),activity:n.activity||`idle`,size:106},null,8,[`mood`,`activity`])])])]),o(`div`,xe,[o(`div`,null,[c[2]||=o(`span`,{class:`eyebrow`},`INNER CURRENTS`,-1),o(`h3`,null,i(t(a)(`她留下的光片`)),1)]),o(`p`,null,i(t(a)(`越亮的线索,越贴近此刻。淡去的仍然保留在这里。`)),1)]),o(`div`,Se,[(D(!0),h(f,null,v(m.value,(n,r)=>(D(),h(`section`,{key:n.kind,class:`current`,style:l({"--tone":n.color,"--order":r})},[o(`header`,Ce,[c[3]||=o(`span`,{class:`current-icon`,"aria-hidden":`true`},[o(`i`)],-1),o(`div`,null,[o(`h4`,null,i(n.label),1),o(`small`,null,i(t(a)(`{length} 枚正在留存`,{length:n.items.length})),1)]),o(`span`,we,i(String(r+1).padStart(2,`0`)),1)]),o(`div`,Te,[(D(!0),h(f,null,v(b(n),e=>(D(),h(`button`,{key:e.thread,type:`button`,class:k([`fleck star`,{faded:e.faded,core:e.core}]),"data-thread":e.thread,onClick:t=>u(`open`,e)},[c[4]||=o(`span`,{class:`fleck-light`,"aria-hidden":`true`},null,-1),o(`span`,De,[o(`small`,null,i(_(e)),1),o(`b`,null,i(e.content),1)]),c[5]||=o(`span`,{class:`fleck-arrow`,"aria-hidden":`true`},`↗`,-1)],10,Ee))),128)),n.items.length>Q?(D(),h(`button`,{key:0,type:`button`,class:`fleck-more`,"aria-expanded":!!d.value[n.kind],onClick:e=>x(n.kind)},[e(i(d.value[n.kind]?t(a)(`收起线索`):t(a)(`再看 {v} 枚光片`,{v:n.items.length-Q}))+` `,1),o(`span`,ke,i(d.value[n.kind]?`↑`:`↓`),1)],8,Oe)):p(``,!0)])],4))),128))])],8,me))}}),[[`__scopeId`,`data-v-f3560fca`]]),je={class:`notes-wall`},Me={class:`wall-head`},Ne={class:`eyebrow`},Pe={class:`muted`},Fe=[`placeholder`,`aria-label`],Ie={key:0,class:`wall`},Le=[`data-kind`],Re={key:0,class:`stamp`},ze={key:1,class:`outreach`},Be=[`onClick`],Ve=[`onClick`],He=[`onClick`],Ue=x(S({__name:`NotesWall`,props:{thoughts:{},kinds:{},query:{}},emits:[`changed`,`search`],setup(n,{emit:s}){let c=n,l=s,g=A(c.query);async function _(e,t){try{await R.thought(e.id,t),l(`changed`)}catch(e){O(e.message,!0)}}async function y(e){if(await r(a(`删除这张便签?有后续修正的便签只能隐藏。`),{title:a(`删除便签`),confirmText:a(`删除`),danger:!0}))try{await R.removeThought(e.id),l(`changed`)}catch(e){O(e.message,!0)}}return(r,s)=>(D(),h(`section`,je,[o(`div`,Me,[o(`div`,null,[o(`span`,Ne,i(t(a)(`THOUGHTS / 留给自己的话`)),1),o(`h2`,null,i(t(a)(`有些念头,先放在心上。`)),1),o(`p`,Pe,i(t(a)(`有些是聊天之后的理解,有些是还没想好要告诉谁的念头。可以分享,也可以先留给自己。`)),1)]),o(`form`,{class:`wall-search`,role:`search`,onSubmit:s[2]||=u(e=>l(`search`,g.value),[`prevent`])},[j(o(`input`,{id:`noteSearch`,"onUpdate:modelValue":s[0]||=e=>g.value=e,placeholder:t(a)(`搜索某件事、某个人`),"aria-label":t(a)(`搜索便签`),onChange:s[1]||=e=>l(`search`,g.value)},null,40,Fe),[[d,g.value]])],32)]),n.thoughts.length?(D(),h(`div`,Ie,[(D(!0),h(f,null,v(n.thoughts,r=>(D(),h(`article`,{key:r.id,class:k([`note-card`,{muted:r.hidden,resolved:r.status===`resolved`}]),"data-kind":r.kind},[o(`header`,null,[o(`b`,null,[e(i(n.kinds[r.kind]||r.kind),1),r.parent_id?(D(),h(f,{key:0},[e(i(t(a)(`· 修正了以前的想法`)),1)],64)):p(``,!0)]),o(`time`,null,i(t(I)(r.created)),1)]),o(`p`,null,i(r.content),1),r.status===`resolved`?(D(),h(`p`,Re,i(r.resolution?t(a)(`放下了:{resolution}`,{resolution:r.resolution}):t(a)(`放下了`)),1)):p(``,!0),r.outreach?(D(),h(`p`,ze,[e(i(t(a)(`想主动说:{outreach} · {v}`,{outreach:r.outreach,v:t(M)[r.outreach_status]||r.outreach_status}))+` `,1),r.outreach_reason?(D(),h(f,{key:0},[s[3]||=o(`br`,null,null,-1),e(i(t(a)(`因为:{outreach_reason}`,{outreach_reason:r.outreach_reason})),1)],64)):p(``,!0),r.outreach_wait_reason?(D(),h(f,{key:1},[s[4]||=o(`br`,null,null,-1),e(i(t(a)(`{outreach_wait_reason};稍后重新决定。`,{outreach_wait_reason:r.outreach_wait_reason})),1)],64)):p(``,!0)])):p(``,!0),o(`footer`,null,[o(`button`,{class:`small`,onClick:e=>_(r,{status:r.status===`open`?`resolved`:`open`})},i(r.status===`open`?t(a)(`放下这件事`):t(a)(`重新关注`)),9,Be),o(`button`,{class:`small`,onClick:e=>_(r,{hidden:!r.hidden})},i(r.hidden?t(a)(`恢复`):t(a)(`不再参与 TA 的思考`)),9,Ve),o(`button`,{class:`text-button`,onClick:e=>y(r)},i(t(a)(`删除`)),9,He)])],10,Le))),128))])):(D(),m(E,{key:1,title:t(a)(`这面墙还空着`),text:t(a)(`独处时有了新的理解、放不下的事或久别想起的人,TA 会写一张便签贴在这里。`)},null,8,[`title`,`text`]))]))}}),[[`__scopeId`,`data-v-a56a9d02`]]),We={class:`page heart`},Ge={class:`heart-bar`},Ke={key:0,class:`muted`},qe={key:1,class:`thread-groups`},Je=[`data-thread-kind`],Ye=[`data-thread`,`onClick`],Xe=[`data-tone`],Ze={class:`thread-text`},Qe=[`aria-expanded`,`onClick`],$e={"aria-hidden":`true`},et={key:3,class:`grid-2 set-aside`},tt={class:`card fold`},nt={class:`eyebrow`},rt={key:0,class:`list`},it={class:`chip`,"data-tone":`quiet`},at={class:`grow`},ot={class:`faint`},st={key:1,class:`muted`},ct={class:`card fold`},lt={class:`eyebrow`},ut={key:0,class:`list`},dt={class:`chip`,"data-tone":`danger`},ft={class:`grow`},pt={key:0,class:`faint`},mt={class:`faint`},ht={key:1,class:`muted`},gt={key:0,class:`thread-sheet stack`},_t={class:`thread-content`},vt={class:`row`},yt={class:`chip`},bt={class:`chip`,"data-tone":`quiet`},xt={class:`stack tight`},St={class:`kv`},Ct={class:`sheet-title`},wt={class:`versions`},Tt={class:`faint`},$=4,Et=x(S({__name:`HeartPage`,setup(r){let s=se(`(max-width: 760px)`),u=c(g(`/mind/self`)||null),d=c(g(`/mind/mood`)||null),_=c(g(`/mind/thoughts?q=`)||[]),x=A(``),S=A(T.sub===`notes`?`notes`:T.sub===`list`||s.value?`list`:`stars`),C=A(null),j=A([]),M=w(()=>(u.value?.threads||[]).filter(e=>e.status!==`closed`)),P=A({}),L=w(()=>(u.value?.threads||[]).filter(e=>e.status===`closed`)),B=w(()=>{let e=u.value?.kinds||{};return Object.entries(e).map(([e,t])=>({kind:e,label:t,threads:M.value.filter(t=>t.kind===e)})).filter(e=>e.threads.length)});function H(e){return e.core?a(`TA 最核心的部分`):e.faded?a(`在远方:很久没被触及`):e.fading?a(`慢慢淡出`):ne[e.status]||e.status}function U(e){return P.value[e.kind]?e.threads:e.threads.slice(0,$)}function W(e){P.value={...P.value,[e]:!P.value[e]}}let G=0;async function K(){let e=++G,[t,n,r]=await Promise.all([R.self(),R.mood(),R.notes(x.value)]);e===G&&(u.value=t,d.value=n,_.value=r,C.value&&=t.threads.find(e=>e.thread===C.value.thread)||null)}async function q(e){let t=++G;x.value=e;let n=await R.notes(e);t===G&&(_.value=n)}async function J(e){C.value=e,j.value=[],j.value=await R.thread(e.thread)}async function Y(e){let t=await n(a(`撤销「{content}」?撤销后 TA 不会再这样认为,之后的独处也不会把它写回来。可以写下原因(可不填):`,{content:e.content}),{title:a(`撤销这条线索`),confirmText:a(`撤销`),danger:!0,placeholder:a(`原因`)});if(t!==null)try{await R.revoke(`self`,e.thread,t),O(a(`已撤销`)),C.value=null,await K()}catch(e){O(e.message,!0)}}return b(S,e=>{T.page===`heart`&&ee(e===`stars`?``:e)}),b(()=>[T.page,T.sub],([e,t])=>{e===`heart`&&(t===`notes`||t===`list`?S.value=t:!t&&S.value===`notes`&&(S.value=`stars`))}),ie(`heart`,K),(n,r)=>(D(),h(`div`,We,[d.value?(D(),m(pe,{key:0,moods:d.value.moods,baseline:d.value.affect.baseline,"time-zone":d.value.clock.timeZone},null,8,[`moods`,`baseline`,`time-zone`])):p(``,!0),o(`div`,Ge,[y(ae,{modelValue:S.value,"onUpdate:modelValue":r[0]||=e=>S.value=e,label:t(a)(`内心的分区`),items:[{key:`stars`,label:t(a)(`心灵星图`),count:M.value.length},{key:`list`,label:t(a)(`线索清单`)},{key:`notes`,label:t(a)(`放在心上`),count:_.value.length}]},null,8,[`modelValue`,`label`,`items`]),S.value===`notes`?p(``,!0):(D(),h(`p`,Ke,i(t(a)(`她从真实经历里慢慢认识自己。点开一条线索,可以看到它如何变化。`)),1))]),u.value?(D(),h(f,{key:1},[S.value===`stars`?(D(),h(f,{key:0},[M.value.length?(D(),m(Ae,{key:0,threads:M.value,kinds:u.value.kinds,activity:t(re).data?.activity?.kind,onOpen:J},null,8,[`threads`,`kinds`,`activity`])):(D(),m(E,{key:1,title:t(a)(`TA 还没有长出关于自己的东西`),text:t(a)(`聊得多了、独处过、写过日记,TA 会慢慢发现自己喜欢什么、怎么看事情、想做什么。天性只是种子。`)},null,8,[`title`,`text`]))],64)):S.value===`list`?(D(),h(`section`,qe,[(D(!0),h(f,null,v(B.value,n=>(D(),h(`div`,{key:n.kind,class:`card thread-group`,"data-thread-kind":n.kind},[o(`h2`,null,[o(`i`,{style:l({background:t(V)[n.kind]})},null,4),e(i(n.label)+` · `+i(n.threads.length),1)]),(D(!0),h(f,null,v(U(n),n=>(D(),h(`button`,{key:n.thread,class:k([`thread`,{faded:n.faded,core:n.core}]),"data-thread":n.thread,onClick:e=>J(n)},[o(`span`,{class:`chip`,"data-tone":n.faded||n.fading?`quiet`:void 0},i(H(n)),9,Xe),o(`span`,Ze,i(n.content),1),o(`small`,null,[e(i(t(a)(`强度 {v}`,{v:t(F)(n.strength)})),1),n.salience===null?p(``,!0):(D(),h(f,{key:0},[e(i(t(a)(`· 此刻的分量 {v}`,{v:t(F)(n.salience)})),1)],64))])],10,Ye))),128)),n.threads.length>$?(D(),h(`button`,{key:0,type:`button`,class:`thread-more`,"aria-expanded":!!P.value[n.kind],onClick:e=>W(n.kind)},[e(i(P.value[n.kind]?t(a)(`收起线索`):t(a)(`再看 {v} 条线索`,{v:n.threads.length-$}))+` `,1),o(`span`,$e,i(P.value[n.kind]?`↑`:`↓`),1)],8,Qe)):p(``,!0)],8,Je))),128)),B.value.length?p(``,!0):(D(),m(E,{key:0,title:t(a)(`TA 还没有长出关于自己的东西`),text:t(a)(`天性只是种子,其余的都从经历里来。`)},null,8,[`title`,`text`]))])):(D(),m(Ue,{key:2,thoughts:_.value,kinds:d.value?.kinds?.thoughts||{},query:x.value,onChanged:K,onSearch:q},null,8,[`thoughts`,`kinds`,`query`])),S.value===`notes`?p(``,!0):(D(),h(`div`,et,[o(`details`,tt,[o(`summary`,null,[o(`span`,nt,i(t(a)(`放下`)),1),o(`b`,null,i(t(a)(`TA 自己放下的 · {length}`,{length:L.value.length})),1)]),L.value.length?(D(),h(`ul`,rt,[(D(!0),h(f,null,v(L.value,e=>(D(),h(`li`,{key:e.thread,class:`list-row`},[o(`span`,it,i(t(a)(`放下`)),1),o(`span`,at,i(e.content),1),o(`time`,ot,i(t(I)(e.created)),1)]))),128))])):(D(),h(`p`,st,i(t(a)(`没有。`)),1))]),o(`details`,ct,[o(`summary`,null,[o(`span`,lt,i(t(a)(`撤销`)),1),o(`b`,null,i(t(a)(`你撤销过的 · {length}`,{length:u.value.revoked.length})),1)]),u.value.revoked.length?(D(),h(`ul`,ut,[(D(!0),h(f,null,v(u.value.revoked,n=>(D(),h(`li`,{key:n.id,class:`list-row`},[o(`span`,dt,i(t(a)(`撤销`)),1),o(`span`,ft,[e(i(n.content),1),n.reason?(D(),h(`small`,pt,` · `+i(n.reason),1)):p(``,!0)]),o(`time`,mt,i(t(I)(n.created)),1)]))),128))])):(D(),h(`p`,ht,i(t(a)(`没有。`)),1))])]))],64)):p(``,!0),y(oe,{open:!!C.value,title:C.value?u.value?.kinds?.[C.value.kind]||C.value.kind:``,eyebrow:t(a)(`一颗星`),onClose:r[2]||=e=>C.value=null},{default:te(()=>[C.value?(D(),h(`div`,gt,[o(`p`,_t,i(C.value.content),1),o(`div`,vt,[o(`span`,yt,i(H(C.value)),1),o(`span`,bt,i(t(N)[C.value.origin]||C.value.origin),1)]),o(`div`,xt,[y(z,{label:t(a)(`强度`),value:C.value.strength},null,8,[`label`,`value`]),C.value.salience===null?p(``,!0):(D(),m(z,{key:0,label:t(a)(`此刻的分量`),value:C.value.salience},null,8,[`label`,`value`]))]),o(`dl`,St,[o(`dt`,null,i(t(a)(`经历的天数`)),1),o(`dd`,null,i(t(a)(`{length} 天`,{length:C.value.days.length})),1),o(`dt`,null,i(t(a)(`来源`)),1),o(`dd`,null,i(t(a)(`{length} 处`,{length:C.value.sources.length})),1),o(`dt`,null,i(t(a)(`最近一次被触及`)),1),o(`dd`,null,i(t(I)(C.value.created)),1)]),o(`section`,null,[o(`h3`,Ct,i(t(a)(`怎么变成现在这样的 · {versions} 个版本`,{versions:C.value.versions})),1),o(`ol`,wt,[(D(!0),h(f,null,v(j.value,e=>(D(),h(`li`,{key:e.id},[o(`time`,null,i(t(I)(e.created)),1),o(`b`,null,i(t(F)(e.strength)),1),o(`span`,null,i(e.content),1)]))),128))])]),o(`button`,{class:`danger`,"data-revoke-thread":``,onClick:r[1]||=e=>Y(C.value)},i(t(a)(`撤销这条线索`)),1),o(`p`,Tt,i(t(a)(`撤销后 TA 不会再这样认为;这只是拿走一段理解,不会改写发生过的事。`)),1)])):p(``,!0)]),_:1},8,[`open`,`title`,`eyebrow`])]))}}),[[`__scopeId`,`data-v-f49fd383`]]);export{Et as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{$ as e,Ct as t,D as n,Et as r,I as i,J as a,L as o,M as s,St as c,Tt as l,W as u,X as d,Y as f,Z as p,_ as m,dt as h,et as g,gt as _,i as v,n as y,nt as b,o as x,q as S,s as C,t as w,ut as T,v as E,vt as D,wt as O,xt as k}from"./Empty-DP6WMGHi.js";import{I as A,N as j,O as M,_ as N,a as P,d as F,j as I,k as L,m as R,n as z,t as B,v as V,y as H}from"./index-4rItIkD-.js";var U={key:0,class:`book`},ee=[`aria-label`],te=[`data-day`,`aria-current`,`onClick`],W={class:`eyebrow`},G=[`aria-label`],K={class:`ink`},q={key:0,class:`row`},J=[`onClick`],Y={key:1},X=[`disabled`,`aria-label`],Z=[`disabled`],Q=[`disabled`,`aria-label`],ne={key:2,class:`day-diff`},re={key:0},ie={key:1},ae={key:2},oe={key:3},se={key:4},ce={key:1,class:`muted`},le=y(b({__name:`DiaryBook`,props:{diaries:{}},setup(n){let s=n,c=k(0),l=k(null),m=k(!1),y=0,b=S(()=>s.diaries[c.value]||null);_(()=>s.diaries,e=>{c.value>=e.length&&(c.value=0)}),_(()=>b.value?.id,()=>{y++,l.value=null,m.value=!1});async function x(){if(!b.value)return;let e=b.value.day,t=++y;if(l.value?.day===b.value.day){l.value=null;return}m.value=!0;try{let n=await I.day(e);t===y&&b.value?.day===e&&(l.value=n)}catch(e){t===y&&E(e.message,!0)}finally{t===y&&(m.value=!1)}}return(s,_)=>n.diaries.length?(T(),p(`div`,U,[a(`nav`,{class:`days`,"aria-label":t(i)(`日记的日子`)},[(T(!0),p(u,null,h(n.diaries,(e,n)=>(T(),p(`button`,{key:e.id,class:O([`diary-day`,{active:n===c.value}]),"data-day":e.day,"aria-current":n===c.value?`true`:void 0,onClick:e=>c.value=n},[a(`b`,null,r(t(F)(e.day)),1),a(`small`,null,r(t(N)(e.day))+r(e.mood?` · ${e.mood}`:``),1)],10,te))),128))],8,ee),g(o,{name:`page-turn`,mode:`out-in`},{default:D(()=>[b.value?(T(),p(`article`,{key:b.value.id,class:`diary-page`},[a(`header`,null,[a(`div`,null,[a(`span`,W,r(t(i)(`日记`)),1),a(`h2`,null,[e(r(t(F)(b.value.day))+` `,1),a(`small`,null,r(t(N)(b.value.day)),1)])]),b.value.mood?(T(),p(`span`,{key:0,class:`stamp`,"aria-label":t(i)(`那天的心情`)},r(b.value.mood),9,G)):d(``,!0)]),a(`p`,K,r(b.value.content),1),b.value.actions?.length?(T(),p(`div`,q,[(T(!0),p(u,null,h(b.value.actions,e=>(T(),p(`button`,{key:e.ref,class:`text-button`,onClick:n=>t(v)(`time`,e.work?`works/`+e.work:`tasks`)},r(e.title)+` → `,9,J))),128))])):d(``,!0),b.value.compare?(T(),p(`blockquote`,Y,r(t(i)(`和昨天的自己比:{compare}`,{compare:b.value.compare})),1)):d(``,!0),a(`footer`,null,[a(`button`,{disabled:c.value>=n.diaries.length-1,"aria-label":t(i)(`前一天`),onClick:_[0]||=e=>c.value+=1},r(t(i)(`‹ 前一天`)),9,X),a(`button`,{class:`primary`,disabled:m.value,onClick:x},r(l.value?.day===b.value.day?t(i)(`合上那天的 TA`):t(i)(`那天的 TA`)),9,Z),a(`button`,{disabled:c.value===0,"aria-label":t(i)(`后一天`),onClick:_[1]||=e=>--c.value},r(t(i)(`后一天 ›`)),9,Q)]),l.value?.day===b.value.day?(T(),p(`section`,ne,[l.value.change?(T(),p(u,{key:0},[a(`h3`,null,r(t(i)(`那天的 TA,和前一天比`)),1),l.value.change.appeared.length?(T(),p(`p`,re,[a(`b`,null,r(t(i)(`多了:`)),1),e(r(l.value.change.appeared.map(e=>e.content).join(`;`)),1)])):d(``,!0),l.value.change.changed.length?(T(),p(`p`,ie,[a(`b`,null,r(t(i)(`变了:`)),1),e(r(l.value.change.changed.map(e=>`${e.before.content} → ${e.content}`).join(`;`)),1)])):d(``,!0),l.value.change.faded.length?(T(),p(`p`,ae,[a(`b`,null,r(t(i)(`放下了:`)),1),e(r(l.value.change.faded.map(e=>e.content).join(`;`)),1)])):d(``,!0),l.value.change.livingFor?(T(),p(`p`,oe,[a(`b`,null,r(t(i)(`正在过的:`)),1),e(r(l.value.change.livingFor.from||t(i)(`还没有`))+` → `+r(l.value.change.livingFor.to||t(i)(`还没有`)),1)])):l.value.snapshot?.livingFor?.content?(T(),p(`p`,se,[a(`b`,null,r(t(i)(`正在过的:`)),1),e(r(l.value.snapshot.livingFor.content),1)])):d(``,!0),a(`p`,null,[a(`b`,null,r(t(i)(`心情:`)),1),e(r(l.value.change.mood.before||`—`)+` → `+r(l.value.change.mood.after||`—`),1)])],64)):(T(),p(`p`,ce,r(t(i)(`这是 TA 留下快照的第一天,还没有可以对照的昨天。`)),1))])):d(``,!0)])):d(``,!0)]),_:1})])):(T(),f(w,{key:1,title:t(i)(`还没有日记`),text:t(i)(`每天睡前(或凌晨),如果这一天真的有过经历,TA 会写下来,并和昨天的自己对照。`)},null,8,[`title`,`text`]))}}),[[`__scopeId`,`data-v-c7291772`]]),ue={class:`ahead`},de={class:`ahead-head`},fe={class:`eyebrow`},pe={class:`muted`},me={key:0,class:`calendar-wrap`},he={class:`calendar card`},ge=[`aria-label`],_e=[`aria-label`],ve={class:`grid`,role:`grid`},ye={key:0,class:`blank`},be=[`aria-label`,`onClick`],xe={key:0,class:`dots`},Se=[`data-state`],Ce={class:`picked card`},we={key:0,class:`list`},Te=[`data-tone`],Ee={class:`grow`},De={class:`faint`},Oe=[`onClick`],ke={key:1,class:`muted`},Ae={key:1},je={key:0,class:`list`},Me=[`data-ahead`],Ne={class:`when`},Pe=[`data-tone`],Fe={class:`grow`},Ie={class:`faint`},Le=[`onClick`],Re=y(b({__name:`AheadCalendar`,props:{items:{},timeZone:{},today:{}},emits:[`changed`],setup(o,{emit:c}){let l=o,m=c,_=k(`calendar`),[v,y]=l.today.split(`-`).map(Number),b=k(v),x=k(y),C=k(l.today),D=S(()=>[1,2,3,4,5,6,7].map(e=>new Date(Date.UTC(2024,0,e)).toLocaleDateString(s(),{weekday:`narrow`,timeZone:`UTC`}))),A=e=>new Intl.DateTimeFormat(`en-CA`,{timeZone:l.timeZone,year:`numeric`,month:`2-digit`,day:`2-digit`}).format(e),M=S(()=>{let e=new Map;for(let t of l.items){let n=A(t.occurrence);e.set(n,[...e.get(n)||[],t])}return e}),N=S(()=>{let e=(new Date(Date.UTC(b.value,x.value-1,1)).getUTCDay()+6)%7,t=new Date(Date.UTC(b.value,x.value,0)).getUTCDate(),n=Array(e).fill(null);for(let e=1;e<=t;e++){let t=`${b.value}-${String(x.value).padStart(2,`0`)}-${String(e).padStart(2,`0`)}`;n.push({day:t,n:e,items:M.value.get(t)||[]})}return n}),P=S(()=>M.value.get(C.value)||[]),F=S(()=>{let e=l.items.filter(e=>e.state===`pending`).sort((e,t)=>e.occurrence-t.occurrence),t=l.items.filter(e=>e.state!==`pending`).sort((e,t)=>t.occurrence-e.occurrence);return[...e,...t]});function L(e){let t=x.value+e;b.value+=Math.floor((t-1)/12),x.value=(t-1+120)%12+1}async function R(e){let t=await n(i(`撤销「{content}」?TA 不再惦记这件事,之后整理记忆时也不会把它写回来。可以写下原因(可不填):`,{content:e.content}),{title:i(`撤销这个约定`),confirmText:i(`撤销`),danger:!0,placeholder:i(`原因`)});if(t!==null)try{await I.revoke(`anticipation`,e.id,t),E(i(`已撤销`)),m(`changed`)}catch(e){E(e.message,!0)}}function B(e){if([`missed`,`lapsed`,`revoked`].includes(e.state))return`danger`;if(e.state!==`pending`)return`quiet`}return(n,s)=>(T(),p(`section`,ue,[a(`div`,de,[a(`div`,null,[a(`span`,fe,r(t(i)(`AHEAD · 向前看`)),1),a(`h2`,null,r(t(i)(`让未来有值得期待的事。`)),1),a(`p`,pe,r(t(i)(`约定、想做的事与每年都会回来的日子,都安静地留在这里。`)),1)]),g(z,{modelValue:_.value,"onUpdate:modelValue":s[0]||=e=>_.value=e,label:t(i)(`约定的视图`),items:[{key:`calendar`,label:t(i)(`日历`)},{key:`list`,label:t(i)(`清单`),count:o.items.length}]},null,8,[`modelValue`,`label`,`items`])]),_.value===`calendar`?(T(),p(`div`,me,[a(`div`,he,[a(`header`,null,[a(`button`,{class:`icon-button ghost`,"aria-label":t(i)(`上个月`),onClick:s[1]||=e=>L(-1)},` ‹ `,8,ge),a(`b`,null,r(t(i)(`{year} 年 {month} 月`,{year:b.value,month:x.value})),1),a(`button`,{class:`icon-button ghost`,"aria-label":t(i)(`下个月`),onClick:s[2]||=e=>L(1)},` › `,8,_e)]),a(`div`,ve,[(T(!0),p(u,null,h(D.value,(e,t)=>(T(),p(`span`,{key:t,class:`wd`},r(e),1))),128)),(T(!0),p(u,null,h(N.value,(n,a)=>(T(),p(u,{key:a},[n?(T(),p(`button`,{key:1,class:O([`cell`,{today:n.day===o.today,picked:n.day===C.value,has:n.items.length}]),"aria-label":t(i)(`{n} 日{v}`,{n:n.n,v:n.items.length?t(i)(`,{length} 件事`,{length:n.items.length}):``}),onClick:e=>C.value=n.day},[e(r(n.n)+` `,1),n.items.length?(T(),p(`i`,xe,[(T(!0),p(u,null,h(n.items.slice(0,3),e=>(T(),p(`em`,{key:e.id,"data-state":e.state},null,8,Se))),128))])):d(``,!0)],10,be)):(T(),p(`span`,ye))],64))),128))])]),a(`div`,Ce,[a(`h3`,null,r(t(i)(`{month} 月 {date} 日`,{month:Number(C.value.slice(5,7)),date:Number(C.value.slice(8,10))})),1),P.value.length?(T(),p(`ul`,we,[(T(!0),p(u,null,h(P.value,e=>(T(),p(`li`,{key:e.id,class:O([`ahead-item`,{closed:e.state!==`pending`}])},[a(`span`,{class:`chip`,"data-tone":B(e)},r(t(H)[e.state]||e.state),9,Te),a(`div`,Ee,[a(`p`,null,r(e.name?`${e.name}:`:``)+r(e.content),1),a(`small`,De,r(t(V)[e.kind]||e.kind)+` · `+r(e.when)+r(e.recurrence===`yearly`?t(i)(` · 每年`):``),1)]),e.state===`revoked`?d(``,!0):(T(),p(`button`,{key:0,class:`text-button`,onClick:t=>R(e)},r(t(i)(`撤销`)),9,Oe))],2))),128))])):(T(),p(`p`,ke,r(t(i)(`这一天没有 TA 在等的事。`)),1))])])):(T(),p(`div`,Ae,[F.value.length?(T(),p(`ul`,je,[(T(!0),p(u,null,h(F.value,e=>(T(),p(`li`,{key:e.id,class:O([`ahead-item card flat`,{closed:e.state!==`pending`}]),"data-ahead":e.id},[a(`time`,Ne,r(t(j)(e.occurrence,o.timeZone).slice(0,5)),1),a(`span`,{class:`chip`,"data-tone":B(e)},r(t(H)[e.state]||e.state),9,Pe),a(`div`,Fe,[a(`p`,null,r(e.name?`${e.name}:`:``)+r(e.content),1),a(`small`,Ie,r(t(V)[e.kind]||e.kind)+` · `+r(e.when)+r(e.recurrence===`yearly`?t(i)(` · 每年`):``)+r(e.private?t(i)(` · 私下知道的`):``)+r(e.closed_note?` · ${e.closed_note}`:``),1)]),e.state===`revoked`?d(``,!0):(T(),p(`button`,{key:0,class:`text-button`,onClick:t=>R(e)},r(t(i)(`撤销`)),9,Le))],10,Me))),128))])):(T(),f(w,{key:1,title:t(i)(`还没有 TA 在等的事`),text:t(i)(`有人说起之后的安排、TA 答应了别人什么,整理记忆时会记下来。`)},null,8,[`title`,`text`]))]))]))}}),[[`__scopeId`,`data-v-0abd3715`]]),ze={class:`page life`},Be={class:`life-intro`},Ve={class:`eyebrow`},He={class:`life-bar`},Ue={key:0,class:`chip`,"data-tone":`quiet`},We={key:1,class:`card reviews`},Ge={class:`card-head`},Ke={class:`eyebrow`},qe={class:`faint`},Je={class:`review-text`},Ye={key:0},Xe={key:2,class:`story`},Ze={key:0,class:`chapter preface`},Qe={class:`eyebrow`},$e={key:1,class:`versions`},et={class:`eyebrow`},tt=[`onClick`],$={key:1,class:`versions`},nt={key:4,class:`shelf`},rt={class:`muted shelf-note`},it={key:0,class:`books`},at={class:`faint`},ot={class:`card creations`},st={class:`faint`},ct={style:{"white-space":`pre-wrap`}},lt={class:`card runs`},ut={class:`eyebrow`},dt={key:0,class:`list run-list`},ft={class:`faint`},pt=[`data-tone`],mt={class:`grow`},ht={class:`faint`},gt={key:1,class:`muted`},_t=y(b({__name:`LifePage`,setup(n){let o=[`diary`,`reviews`,`story`,`ahead`,`shelf`],s=c(m(`/mind/life?q=`)||null),y=k(o.includes(C.sub)?C.sub:`diary`),b=k({}),E=k(null),O=S(()=>A.data?.clock?.timeZone||`Asia/Shanghai`),N=S(()=>A.data?.clock?.local?.slice(0,10)||new Date().toISOString().slice(0,10)),F=0;async function V(){let e=++F,t=await I.life();e===F&&(s.value=t)}async function H(e){if(b.value[e]){let{[e]:t,...n}=b.value;b.value=n;return}b.value={...b.value,[e]:await I.chapter(e)}}async function U(){E.value=E.value?null:await I.story()}return _(y,e=>{C.page===`life`&&x(e===`diary`?``:e)}),_(()=>[C.page,C.sub],([e,t])=>{e===`life`&&(y.value=o.includes(t)?t:`diary`)}),P(`life`,V),(n,o)=>(T(),p(`div`,ze,[a(`header`,Be,[a(`div`,null,[a(`span`,Ve,r(t(i)(`LIFE · 一生`)),1),a(`h1`,null,[e(r(t(i)(`日子经过,`)),1),a(`em`,null,r(t(i)(`留下自己的形状。`)),1)]),a(`p`,null,r(t(i)(`写下今天,回望从前,也给明天留一点期待。`)),1)]),o[4]||=a(`div`,{class:`life-mark`,"aria-hidden":`true`},[a(`span`)],-1)]),a(`div`,He,[a(`button`,{class:`text-button`,onClick:o[0]||=e=>t(v)(`time`)},r(t(i)(`时间、任务与作品 →`)),1),g(z,{modelValue:y.value,"onUpdate:modelValue":o[1]||=e=>y.value=e,label:t(i)(`一生的分区`),items:[{key:`diary`,label:t(i)(`日记本`),count:s.value?.diaries.length??``},{key:`reviews`,label:t(i)(`回顾`),count:s.value?.reviews.length??``},{key:`story`,label:t(i)(`我的来路`),count:s.value?s.value.chapters.length:``},{key:`ahead`,label:t(i)(`约定与期待`),count:s.value?.anticipations.filter(e=>e.state===`pending`).length??``},{key:`shelf`,label:t(i)(`书架`),count:s.value?.readings.length??``}]},null,8,[`modelValue`,`label`,`items`]),s.value?(T(),p(`span`,Ue,r(t(i)(`来到这里的第 {dayOfLife} 天`,{dayOfLife:s.value.dayOfLife})),1)):d(``,!0)]),s.value?(T(),p(u,{key:0},[y.value===`diary`?(T(),f(le,{key:0,diaries:s.value.diaries},null,8,[`diaries`])):y.value===`reviews`?(T(),p(`section`,We,[a(`div`,Ge,[a(`div`,null,[a(`span`,Ke,r(t(i)(`回顾`)),1),a(`h2`,null,r(t(i)(`TA 的回顾`)),1),a(`p`,null,r(t(i)(`每隔一段时间,TA 在夜里重新看看这段日子:留下了什么、自己怎样在变。`)),1)])]),s.value.reviews.length?(T(),f(B,{key:0,items:s.value.reviews,label:t(i)(`回顾`)},{default:D(({item:e})=>[a(`time`,qe,r(t(j)(e.created,O.value)),1),a(`p`,Je,r(e.content),1),e.compare?(T(),p(`blockquote`,Ye,r(t(i)(`和上次回顾比:{compare}`,{compare:e.compare})),1)):d(``,!0)]),_:1},8,[`items`,`label`])):(T(),f(w,{key:1,title:t(i)(`还没有回顾过`),text:t(i)(`日记攒到两篇以后,TA 会在睡着的时候第一次回顾。`)},null,8,[`title`,`text`]))])):y.value===`story`?(T(),p(`section`,Xe,[s.value.story?(T(),p(`article`,Ze,[a(`span`,Qe,r(t(i)(`前言`)),1),a(`h2`,null,r(t(i)(`我的来路 · 第 {dayOfLife} 天`,{dayOfLife:s.value.dayOfLife})),1),a(`p`,null,r(s.value.story.content),1),s.value.storyVersions>1?(T(),p(`button`,{key:0,class:`text-button`,onClick:U},r(E.value?t(i)(`收起旧版本`):t(i)(`重写过 {v} 次`,{v:s.value.storyVersions-1})),1)):d(``,!0),E.value?(T(),p(`ol`,$e,[(T(!0),p(u,null,h(E.value.slice(1),e=>(T(),p(`li`,{key:e.id},[a(`time`,null,r(t(j)(e.created,O.value)),1),a(`span`,null,r(e.content),1)]))),128))])):d(``,!0)])):d(``,!0),(T(!0),p(u,null,h(s.value.chapters,e=>(T(),p(`article`,{key:e.id,class:`chapter`},[a(`span`,et,r(t(i)(`第 {chapter} 章`,{chapter:e.chapter})),1),a(`h2`,null,r(e.title),1),a(`p`,null,r(e.content),1),e.versions>1?(T(),p(`button`,{key:0,class:`text-button`,onClick:t=>H(e.chapter)},r(b.value[e.chapter]?t(i)(`收起旧版本`):t(i)(`重写过 {v} 次`,{v:e.versions-1})),9,tt)):d(``,!0),b.value[e.chapter]?(T(),p(`ol`,$,[(T(!0),p(u,null,h(b.value[e.chapter].slice(1),e=>(T(),p(`li`,{key:e.id},[a(`time`,null,r(t(j)(e.created,O.value)),1),a(`span`,null,r(e.title)+`:`+r(e.content),1)]))),128))])):d(``,!0)]))),128)),!s.value.story&&!s.value.chapters.length?(T(),f(w,{key:1,title:t(i)(`自传还没开始写`),text:t(i)(`有了两篇日记之后,TA 会在夜里第一次回顾,写下「我的来路」和第一章。旧的版本都会留着。`)},null,8,[`title`,`text`])):d(``,!0)])):y.value===`ahead`?(T(),f(Re,{key:3,items:s.value.anticipations,"time-zone":O.value,today:N.value,onChanged:V},null,8,[`items`,`time-zone`,`today`])):(T(),p(`section`,nt,[a(`p`,rt,[e(r(t(i)(`独处时,TA 会从共享资料里挑自己感兴趣的读,一段一段地读下去,读后的想法可以改变 TA。还有 {v} 段没读。`,{v:t(R)(s.value.unread)}))+` `,1),a(`button`,{class:`text-button`,onClick:o[2]||=e=>t(v)(`memory`,`shelf`)},r(t(i)(`去资料书架放点书`)),1)]),s.value.readings.length?(T(),p(`div`,it,[(T(!0),p(u,null,h(s.value.readings,e=>(T(),p(`article`,{key:e.id,class:`book-card`},[a(`div`,{class:`spine`,style:l({"--p":(e.ordinal+1)/Math.max(1,e.total)})},null,4),a(`div`,null,[a(`h3`,null,`《`+r(e.title)+`》`,1),a(`small`,at,r(t(i)(`第 {v} / {total} 段 · {v2}`,{v:e.ordinal+1,total:e.total,v2:t(j)(e.created,O.value)})),1),a(`p`,null,r(e.note||t(i)(`读完没说什么`)),1)])]))),128))])):(T(),f(w,{key:1,title:t(i)(`还没读过什么`),text:t(i)(`把文章放进「记忆 → 资料书架」的共享集合,TA 独处时会去读。`)},null,8,[`title`,`text`]))])),a(`details`,ot,[a(`summary`,null,r(t(i)(`独处时留下的东西 · {v}`,{v:s.value.creations?.length||0})),1),a(`button`,{class:`text-button`,onClick:o[3]||=e=>t(v)(`time`,`works`)},r(t(i)(`阅读草稿、篇章和旧版本 →`)),1),(T(!0),p(u,null,h(s.value.creations||[],e=>(T(),p(`article`,{key:e.id,class:`creation`},[a(`h3`,null,r(e.title),1),a(`small`,st,r(t(j)(e.created,O.value))+` · `+r(e.discretion===`open`?t(i)(`自己的作品`):t(i)(`留在私下`)),1),a(`p`,ct,r(e.content),1)]))),128)),s.value.creations?.length?d(``,!0):(T(),f(w,{key:0,title:t(i)(`还没有独处作品`),text:t(i)(`她会按自己留下的计划,阅读、写东西或整理想法。`)},null,8,[`title`,`text`]))]),a(`details`,lt,[a(`summary`,null,[a(`span`,ut,r(t(i)(`独处的时候`)),1),a(`b`,null,r(t(i)(`独处、日记与夜里的记录 · {length}`,{length:s.value.runs.length})),1)]),s.value.runs.length?(T(),p(`ul`,dt,[(T(!0),p(u,null,h(s.value.runs,e=>(T(),p(`li`,{key:e.id,class:`run-row`},[a(`time`,ft,r(t(j)(e.started,O.value)),1),a(`span`,{class:`chip`,"data-tone":e.status===`error`?`danger`:e.status===`written`?`ok`:`quiet`},r(t(M)[e.kind]||e.kind)+` · `+r(t(L)[e.status]||e.status),9,pt),a(`span`,mt,r(e.reason),1),a(`small`,ht,r(e.model||t(i)(`未调用模型`))+` · `+r(t(R)(e.tokens))+` Token`,1)]))),128))])):(T(),p(`p`,gt,r(t(i)(`安静本身不会留下记录;TA 真正独处、写日记或在夜里整理时才会记下。`)),1))])],64)):d(``,!0)]))}}),[[`__scopeId`,`data-v-e4940116`]]);export{_t as default};
|