dsh-schedule-ilife 0.3.8 → 0.3.9
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/dist/client.js +224 -38
- package/dist/client.js.map +1 -1
- package/dist/contract.d.ts +16 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/settings.d.ts +20 -3
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +22 -23
- package/dist/settings.js.map +1 -1
- package/package.json +2 -2
package/dist/client.js
CHANGED
|
@@ -33,7 +33,7 @@ window.__ModuleLoader__.load({
|
|
|
33
33
|
const PLUGIN = "dsh-schedule-ilife";
|
|
34
34
|
//#endregion
|
|
35
35
|
//#region src/settings.ts
|
|
36
|
-
/**
|
|
36
|
+
/** 常用项:#764 起是「可改 1 + 只读 2」(高级组已空,见下)。 */
|
|
37
37
|
const COMMON = [
|
|
38
38
|
{
|
|
39
39
|
key: "db.dir",
|
|
@@ -48,30 +48,23 @@ window.__ModuleLoader__.load({
|
|
|
48
48
|
title: "库文件名",
|
|
49
49
|
tier: "common",
|
|
50
50
|
control: "text",
|
|
51
|
-
|
|
51
|
+
readonly: true,
|
|
52
|
+
resolveFrom: "dbFile",
|
|
53
|
+
hint: "数据目录下的库文件。只读,要改请编辑配置文件。"
|
|
52
54
|
},
|
|
53
55
|
{
|
|
54
56
|
key: "html.dir",
|
|
55
|
-
title: "
|
|
57
|
+
title: "HELP 产物目录",
|
|
56
58
|
tier: "common",
|
|
57
59
|
control: "text",
|
|
58
|
-
|
|
60
|
+
readonly: true,
|
|
61
|
+
resolveFrom: "htmlDir",
|
|
62
|
+
hint: "HELP 页面的存放目录。只读,要改请编辑配置文件。"
|
|
59
63
|
}
|
|
60
64
|
];
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
title: "产物文件名主体 · HELP",
|
|
65
|
-
tier: "advanced",
|
|
66
|
-
control: "text",
|
|
67
|
-
hint: "HELP 文件名前缀,后面自动加时间戳。"
|
|
68
|
-
}, {
|
|
69
|
-
key: "lark.cliPath",
|
|
70
|
-
title: "飞书 CLI 路径",
|
|
71
|
-
tier: "advanced",
|
|
72
|
-
control: "text",
|
|
73
|
-
hint: "飞书 CLI 的完整路径。留空=自动探测。"
|
|
74
|
-
}];
|
|
65
|
+
/** 高级项:#764 起已空(`files.help`/`lark.cliPath` 出配置表)——组还在,行没有。
|
|
66
|
+
* 空组不渲染(见 client.ts),行表长度即 3。 */
|
|
67
|
+
const ADVANCED = [];
|
|
75
68
|
/** 设置页的行表,顺序即页面顺序。 */
|
|
76
69
|
const CONFIG_ITEMS = [...COMMON, ...ADVANCED];
|
|
77
70
|
/** 常用项行数(页面上直接画出来的那些)。 */
|
|
@@ -116,6 +109,11 @@ window.__ModuleLoader__.load({
|
|
|
116
109
|
const RPC_ENDPOINT_CONFIG_GET = "config.get";
|
|
117
110
|
const RPC_ENDPOINT_CONFIG_SAVE = "config.save";
|
|
118
111
|
const RPC_ENDPOINT_CONFIG_RESET = "config.reset";
|
|
112
|
+
/** #706 配置体检端点:只读一次,回一份报告(判据由技能侧出,本包只透传,不重写一个字)。
|
|
113
|
+
*
|
|
114
|
+
* 端点名与技能侧那条只读命令同名(`<技能>.config.check`);报告形状的唯一真相在技能侧
|
|
115
|
+
* `packages/skill-<技能>/src/health.ts`,面板侧镜像在 `packages/plugin-manager/src/health-contract.ts`。 */
|
|
116
|
+
const RPC_ENDPOINT_CONFIG_CHECK = "config.check";
|
|
119
117
|
/** 信封守卫:供 client 拆包前校验(port 拥有信封定义,守卫住 port)。 */
|
|
120
118
|
function isRpcResult(raw) {
|
|
121
119
|
if (typeof raw !== "object" || raw === null) return false;
|
|
@@ -1185,24 +1183,121 @@ window.__ModuleLoader__.load({
|
|
|
1185
1183
|
function resetConfigSurface(call) {
|
|
1186
1184
|
return configRpc(call, RPC_ENDPOINT_CONFIG_RESET, {});
|
|
1187
1185
|
}
|
|
1188
|
-
/**
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1186
|
+
/** 读一次配置体检(只读;判据由技能侧出,本包只透传,不重写一个字)。 */
|
|
1187
|
+
function fetchHealthSurface(call) {
|
|
1188
|
+
return configRpc(call, RPC_ENDPOINT_CONFIG_CHECK, {});
|
|
1189
|
+
}
|
|
1190
|
+
/** 从体检报告里挑出 `lark.cli` 那一项;形状不对回 null(不抛)。 */
|
|
1191
|
+
function larkItemOf(report) {
|
|
1192
|
+
if (typeof report !== "object" || report === null) return null;
|
|
1193
|
+
const items = report.items;
|
|
1194
|
+
if (!Array.isArray(items)) return null;
|
|
1195
|
+
for (const it of items) {
|
|
1196
|
+
if (typeof it !== "object" || it === null) continue;
|
|
1197
|
+
const rec = it;
|
|
1198
|
+
if (rec["id"] !== "lark.cli") continue;
|
|
1199
|
+
if (typeof rec["id"] !== "string" || typeof rec["status"] !== "string" || typeof rec["message"] !== "string") return null;
|
|
1200
|
+
return {
|
|
1201
|
+
id: rec["id"],
|
|
1202
|
+
status: rec["status"],
|
|
1203
|
+
message: rec["message"],
|
|
1204
|
+
action: typeof rec["action"] === "string" ? rec["action"] : ""
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
return null;
|
|
1208
|
+
}
|
|
1209
|
+
/** 飞书 CLI 官网(面板状态行与安装 prompt 里的同一地址,唯一定义地是这里)。 */
|
|
1210
|
+
const LARK_OFFICIAL_URL = "https://www.feishu.cn/feishu-cli";
|
|
1211
|
+
/** 状态行上逐字显示的那一行(三档都在、显示成文字、可点击跳转,不改写、不加话)。 */
|
|
1212
|
+
const LARK_OFFICIAL_LINE = "飞书CLI官网为:https://www.feishu.cn/feishu-cli";
|
|
1213
|
+
/** 「复制 prompt」按钮复制的内容(定稿住 #759 最新一条评论 v5 的逐字全文,两家的实施票都引它,
|
|
1214
|
+
* 不在本票里另写一份;官网指南 shape 变化时改 #759 那一条,这里跟着换)。 */
|
|
1215
|
+
const LARK_PROMPT_V5 = `在这台机器上安装并配置飞书 CLI(lark-cli)。你来推进,做完为止。
|
|
1216
|
+
|
|
1217
|
+
必须满足的终态(每条都要真跑,把命令原文和输出贴回来):
|
|
1218
|
+
1) lark-cli --version >= 1.0.82(更低的版本没有我们要用的 --no-wait)
|
|
1219
|
+
2) where lark-cli(Windows)或 which lark-cli(macOS/Linux)能命中;Windows 上还要确认 %APPDATA%\\npm\\lark-cli.cmd 这个文件存在
|
|
1220
|
+
3) lark-cli auth status 的输出里有 identities.user.openId
|
|
1221
|
+
4) lark-cli auth check --scope task 退出码 0(连退出码一起贴)—— 备忘录要用任务域
|
|
1222
|
+
5) lark-cli calendar +agenda 退出码 0(连退出码一起贴)—— 作息管家要读你的主日历
|
|
1223
|
+
6) 技能侧也能用:跑一次作息管家的「飞书探测」(=一条不落地的同步探测),不报「缺 CLI/未登录/日历不可达」
|
|
1224
|
+
|
|
1225
|
+
怎么做:
|
|
1226
|
+
7) Node.js/npm 不可用就先装。
|
|
1227
|
+
8) 安装:npm install -g @larksuite/cli。只装这个官方包(bin 名 lark-cli);不要装 npm 上的 lark-cli。
|
|
1228
|
+
9) 官方指南里的第二件事也照做:npx -y skills add https://open.feishu.cn --skill -y
|
|
1229
|
+
10) 配置与登录照这两条走(都是 CLI 自己在 --help 里写给 AI agent 的写法,别改写成别的):
|
|
1230
|
+
先初始化:lark-cli config init --new
|
|
1231
|
+
它会阻塞等你完成浏览器授权,所以起在后台、从它的输出里把验证链接捞出来
|
|
1232
|
+
(CLI 原文:Run it in the background and retrieve the verification URL from its output);
|
|
1233
|
+
链接给我,我批准后你继续。
|
|
1234
|
+
如果它报 Inside an Agent context 之类被拒,照它的提示改用 lark-cli config bind。
|
|
1235
|
+
再授权:lark-cli auth login --no-wait --json --domain calendar,task
|
|
1236
|
+
它会立刻返回一个验证链接(或二维码);把链接给我,我批准后你再用
|
|
1237
|
+
lark-cli auth login --device-code <拿到的 device code> 收尾。
|
|
1238
|
+
官方指南:https://open.feishu.cn/document/no_class/mcp-archive/feishu-cli-installation-guide.md
|
|
1239
|
+
子命令以 lark-cli auth --help / config --help 和官方文档为准,不要凭猜;做完要能过第 3、4、5 条。
|
|
1240
|
+
11) npm 全局目录如果不在系统 PATH 上,把它加进系统 PATH(不是只在当前终端 export)。
|
|
1241
|
+
|
|
1242
|
+
怎么推进:
|
|
1243
|
+
12) 除第 13 条那三类,其余你自己一口气做完,不要逐步问我确认:安装、改 PATH、重试、修报错都自己做完。
|
|
1244
|
+
13) 只有这三类必须我介入;遇到就用 /wizard 生成脚本带我走,走完你接着自动往下做:
|
|
1245
|
+
① 浏览器里的初始化授权 / 登录(把验证链接或二维码给我);
|
|
1246
|
+
② 系统权限提示(管理员/sudo);
|
|
1247
|
+
③ Node.js 需要我先定怎么装。
|
|
1248
|
+
14) 任何一步失败:自己按报错修;修不动再一次性告诉我「卡在哪、报错原文、需要我做什么」。
|
|
1249
|
+
|
|
1250
|
+
参考官网:https://www.feishu.cn/feishu-cli`;
|
|
1251
|
+
/** 复制一段文本:先剪贴板,不成再走兜底(永不抛,回是否成功)。 */
|
|
1252
|
+
async function copyText(text, deps = {}) {
|
|
1253
|
+
if (deps.clipboard !== void 0) try {
|
|
1254
|
+
await deps.clipboard.writeText(text);
|
|
1255
|
+
return true;
|
|
1256
|
+
} catch {}
|
|
1257
|
+
if (deps.execCopy !== void 0) try {
|
|
1258
|
+
return deps.execCopy(text);
|
|
1259
|
+
} catch {
|
|
1260
|
+
return false;
|
|
1261
|
+
}
|
|
1262
|
+
return false;
|
|
1263
|
+
}
|
|
1264
|
+
/** 当刻环境的剪贴板(经 `globalThis` 懒取:顶层不碰 DOM,loader 沙箱与 node 单测都不炸;拿不到回 null)。 */
|
|
1265
|
+
function clipboardOf() {
|
|
1266
|
+
try {
|
|
1267
|
+
return globalThis.navigator?.clipboard ?? null;
|
|
1268
|
+
} catch {
|
|
1269
|
+
return null;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
/** 只读行显示什么:标了 `resolveFrom` ⇒ 回执 `resolved` 组那一格;没标 ⇒ 配置文件里那个值本身
|
|
1273
|
+
* (数字类只读项走这一档:显示的是生效数字,见 #749 补注二的甲档)。 */
|
|
1274
|
+
function readonlyTextOf(item, raw, prefill) {
|
|
1275
|
+
if (item.resolveFrom === void 0) return raw;
|
|
1276
|
+
const shown = prefill.resolved?.[item.resolveFrom];
|
|
1277
|
+
return typeof shown === "string" ? shown : "";
|
|
1278
|
+
}
|
|
1192
1279
|
function toDraft(values, prefill = {}) {
|
|
1193
1280
|
const draft = {};
|
|
1194
1281
|
for (const item of CONFIG_ITEMS) {
|
|
1195
1282
|
const v = readPath(values, item.key);
|
|
1196
1283
|
const raw = v === void 0 || v === null ? "" : String(v);
|
|
1284
|
+
if (item.readonly === true) {
|
|
1285
|
+
draft[item.key] = readonlyTextOf(item, raw, prefill);
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
1197
1288
|
const fallback = item.prefillFrom === void 0 ? void 0 : prefill[item.prefillFrom];
|
|
1198
1289
|
draft[item.key] = raw === "" && typeof fallback === "string" && fallback !== "" ? fallback : raw;
|
|
1199
1290
|
}
|
|
1200
1291
|
return draft;
|
|
1201
1292
|
}
|
|
1202
|
-
/** 表单态 →
|
|
1293
|
+
/** 表单态 → 配置取值(按控件种类还原类型;空串对文本项照收,语义由「按默认落点」承担)。
|
|
1294
|
+
*
|
|
1295
|
+
* **只读行不收**(#764,照 #749 样板):它们显示的是技能算好的绝对路径,写回配置就是把「显示的路径」
|
|
1296
|
+
* 当成「配置值」——保存只提交真能改的那些行(本家=`db.dir` 一行),其余键由技能侧做组内合并保留现值。 */
|
|
1203
1297
|
function fromDraft(draft) {
|
|
1204
1298
|
const values = {};
|
|
1205
1299
|
for (const item of CONFIG_ITEMS) {
|
|
1300
|
+
if (item.readonly === true) continue;
|
|
1206
1301
|
const raw = draft[item.key] ?? "";
|
|
1207
1302
|
const value = item.control === "number" ? Number(raw) : item.control === "switch" ? raw === "true" : raw;
|
|
1208
1303
|
writePath(values, item.key, item.control === "number" && !Number.isFinite(value) ? 0 : value);
|
|
@@ -1261,31 +1356,38 @@ window.__ModuleLoader__.load({
|
|
|
1261
1356
|
/** 一行输入(四种控件对齐受限 YAML 子集:文本/数字/布尔/目录)。
|
|
1262
1357
|
*
|
|
1263
1358
|
* 目录档=文本框 + 一枚唤起系统文件夹选择器的按钮;`onBrowse` 缺席(命名空间拿不到、
|
|
1264
|
-
* 或这条路已被拒)时不画按钮,文本框照旧——那是该缝自己的契约(供不了就收起入口,不是失败)。
|
|
1359
|
+
* 或这条路已被拒)时不画按钮,文本框照旧——那是该缝自己的契约(供不了就收起入口,不是失败)。
|
|
1360
|
+
*
|
|
1361
|
+
* **只读行(#764,照 #749 样板)**:`item.readonly` 为真时控件一律 `disabled`(值只经技能侧解析后显示、
|
|
1362
|
+
* 不给改),目录行的浏览按钮**保留但不可点击**(#747 定稿:入口不作废,只是不能改)。控件文案**不出现省略号**
|
|
1363
|
+
* (#746 处置 9)——按钮就写「选择文件夹」/「浏览」两个字面。 */
|
|
1265
1364
|
function Row(props) {
|
|
1266
1365
|
const { item } = props;
|
|
1267
|
-
const
|
|
1366
|
+
const readonly = item.readonly === true;
|
|
1367
|
+
const disabled = props.disabled || readonly;
|
|
1368
|
+
const onChange = readonly ? void 0 : (e) => props.onChange(item.key, e.target.value);
|
|
1268
1369
|
const control = item.control === "switch" ? react.createElement("input", {
|
|
1269
1370
|
type: "checkbox",
|
|
1270
1371
|
checked: props.value === "true",
|
|
1271
|
-
disabled
|
|
1272
|
-
onChange: (e) => props.onChange(item.key, e.target.checked ? "true" : "false")
|
|
1372
|
+
disabled,
|
|
1373
|
+
onChange: readonly ? void 0 : (e) => props.onChange(item.key, e.target.checked ? "true" : "false")
|
|
1273
1374
|
}) : react.createElement("input", {
|
|
1274
1375
|
style: S.input,
|
|
1275
1376
|
type: item.control === "number" ? "number" : "text",
|
|
1276
1377
|
value: props.value,
|
|
1277
|
-
disabled
|
|
1378
|
+
disabled,
|
|
1278
1379
|
spellCheck: false,
|
|
1279
1380
|
onChange
|
|
1280
1381
|
});
|
|
1281
|
-
/** 三态入口:供不了(`none`/缺席)就不画,不摆一个点了没反应的死按钮。
|
|
1382
|
+
/** 三态入口:供不了(`none`/缺席)就不画,不摆一个点了没反应的死按钮。
|
|
1383
|
+
* 只读行照画(按钮保留),但 `disabled` ⇒ 点不动。 */
|
|
1282
1384
|
const entry = props.browser ?? null;
|
|
1283
1385
|
const browse = item.control === "directory" && entry !== null && entry.mode !== "none" ? react.createElement("button", {
|
|
1284
1386
|
style: S.btnPick,
|
|
1285
1387
|
type: "button",
|
|
1286
|
-
disabled
|
|
1388
|
+
disabled,
|
|
1287
1389
|
onClick: () => entry.onOpen(item.key)
|
|
1288
|
-
}, entry.mode === "native" ? "
|
|
1390
|
+
}, entry.mode === "native" ? "选择文件夹" : "浏览") : null;
|
|
1289
1391
|
return react.createElement("div", { style: S.row }, react.createElement("div", { style: S.label }, item.title), react.createElement("div", { style: S.hint }, item.hint), browse === null ? control : react.createElement("div", { style: S.pickRow }, control, browse));
|
|
1290
1392
|
}
|
|
1291
1393
|
/** 技能设置页:承载作息管家自己的全部可配置项(只配置,不干活)。 */
|
|
@@ -1297,6 +1399,10 @@ window.__ModuleLoader__.load({
|
|
|
1297
1399
|
const [busy, setBusy] = react.useState(false);
|
|
1298
1400
|
const [notice, setNotice] = react.useState(null);
|
|
1299
1401
|
const [error, setError] = react.useState(null);
|
|
1402
|
+
/** 飞书 CLI 状态行(三档读数由技能侧体检出,面板只显示;`null` 以外的三态见下)。 */
|
|
1403
|
+
const [lark, setLark] = react.useState({ kind: "loading" });
|
|
1404
|
+
/** 复制 prompt 那枚按钮的回执(idle=没点过)。 */
|
|
1405
|
+
const [copied, setCopied] = react.useState("idle");
|
|
1300
1406
|
const apply = react.useCallback((surface) => {
|
|
1301
1407
|
setState({
|
|
1302
1408
|
kind: "ready",
|
|
@@ -1304,6 +1410,25 @@ window.__ModuleLoader__.load({
|
|
|
1304
1410
|
});
|
|
1305
1411
|
setDraft(toDraft(surface.values, surface));
|
|
1306
1412
|
}, []);
|
|
1413
|
+
const loadHealth = react.useCallback(async () => {
|
|
1414
|
+
setLark({ kind: "loading" });
|
|
1415
|
+
const r = await fetchHealthSurface(props.getCall());
|
|
1416
|
+
if (!r.ok) {
|
|
1417
|
+
setLark({
|
|
1418
|
+
kind: "failed",
|
|
1419
|
+
message: r.message
|
|
1420
|
+
});
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
const item = larkItemOf(r.surface);
|
|
1424
|
+
setLark(item === null ? {
|
|
1425
|
+
kind: "failed",
|
|
1426
|
+
message: "体检回执里没有飞书 CLI 那一项"
|
|
1427
|
+
} : {
|
|
1428
|
+
kind: "ready",
|
|
1429
|
+
item
|
|
1430
|
+
});
|
|
1431
|
+
}, [props.getCall]);
|
|
1307
1432
|
const load = react.useCallback(async () => {
|
|
1308
1433
|
setNotice(null);
|
|
1309
1434
|
setError(null);
|
|
@@ -1313,7 +1438,12 @@ window.__ModuleLoader__.load({
|
|
|
1313
1438
|
kind: "failed",
|
|
1314
1439
|
message: r.message
|
|
1315
1440
|
});
|
|
1316
|
-
|
|
1441
|
+
await loadHealth();
|
|
1442
|
+
}, [
|
|
1443
|
+
apply,
|
|
1444
|
+
loadHealth,
|
|
1445
|
+
props.getCall
|
|
1446
|
+
]);
|
|
1317
1447
|
react.useEffect(() => {
|
|
1318
1448
|
let alive = true;
|
|
1319
1449
|
(async () => {
|
|
@@ -1324,6 +1454,22 @@ window.__ModuleLoader__.load({
|
|
|
1324
1454
|
kind: "failed",
|
|
1325
1455
|
message: r.message
|
|
1326
1456
|
});
|
|
1457
|
+
const h = await fetchHealthSurface(props.getCall());
|
|
1458
|
+
if (!alive) return;
|
|
1459
|
+
if (!h.ok) setLark({
|
|
1460
|
+
kind: "failed",
|
|
1461
|
+
message: h.message
|
|
1462
|
+
});
|
|
1463
|
+
else {
|
|
1464
|
+
const item = larkItemOf(h.surface);
|
|
1465
|
+
setLark(item === null ? {
|
|
1466
|
+
kind: "failed",
|
|
1467
|
+
message: "体检回执里没有飞书 CLI 那一项"
|
|
1468
|
+
} : {
|
|
1469
|
+
kind: "ready",
|
|
1470
|
+
item
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1327
1473
|
})();
|
|
1328
1474
|
return () => {
|
|
1329
1475
|
alive = false;
|
|
@@ -1388,7 +1534,10 @@ window.__ModuleLoader__.load({
|
|
|
1388
1534
|
onOpen: onOpenRow
|
|
1389
1535
|
};
|
|
1390
1536
|
const surface = state.kind === "ready" ? state.surface : null;
|
|
1391
|
-
|
|
1537
|
+
/** 脏值只看**可改行**(#764,照 #749 样板):只读行显示的是技能算好的绝对路径,
|
|
1538
|
+
* 拿它当「改动」会让打开面板就变「未保存」。 */
|
|
1539
|
+
const editableItems = CONFIG_ITEMS.filter((i) => i.readonly !== true);
|
|
1540
|
+
const dirty = surface !== null && editableItems.some((i) => (draft[i.key] ?? "") !== (toDraft(surface.values, surface)[i.key] ?? ""));
|
|
1392
1541
|
/** 写完之后重新读一份:写回执只有 {path, values},头部那两行要的是读整面。 */
|
|
1393
1542
|
const writeThenReload = react.useCallback(async (done) => {
|
|
1394
1543
|
setBusy(true);
|
|
@@ -1426,8 +1575,8 @@ window.__ModuleLoader__.load({
|
|
|
1426
1575
|
if (r.ok) await writeThenReload("已重置为默认(原配置已另存一份 .bak)");
|
|
1427
1576
|
else setError(r.message);
|
|
1428
1577
|
}, [props.getCall, writeThenReload]);
|
|
1429
|
-
const head = react.createElement("div", null, react.createElement("div", { style: S.title }, `${SLOT_TITLE} · 配置`), surface !== null ? react.createElement("div", null, react.createElement("div", { style: S.info }, `配置文件 ${surface.path}`), react.createElement("div", { style: S.info }, `数据目录 ${surface.dataDir}`), surface.created ? react.createElement("div", { style: S.muted }, "(配置文件刚按默认值生成)") : null) : null);
|
|
1430
|
-
if (state.kind === "loading") return react.createElement("div", { style: S.card }, head, react.createElement("div", { style: S.muted }, "
|
|
1578
|
+
const head = react.createElement("div", null, react.createElement("div", { style: S.title }, `${SLOT_TITLE} · 配置`), surface !== null ? react.createElement("div", null, react.createElement("div", { style: S.info }, `配置文件 ${surface.path}`), react.createElement("div", { style: S.info }, `数据目录 ${surface.resolved?.dbDir ?? surface.dataDir}`), surface.created ? react.createElement("div", { style: S.muted }, "(配置文件刚按默认值生成)") : null) : null);
|
|
1579
|
+
if (state.kind === "loading") return react.createElement("div", { style: S.card }, head, react.createElement("div", { style: S.muted }, "配置读取中"));
|
|
1431
1580
|
if (state.kind === "failed") return react.createElement("div", { style: S.card }, react.createElement("div", { style: S.title }, `${SLOT_TITLE} · 配置`), react.createElement("div", { style: S.error }, state.message), react.createElement("div", { style: S.bar }, react.createElement("button", {
|
|
1432
1581
|
style: S.btn,
|
|
1433
1582
|
type: "button",
|
|
@@ -1447,12 +1596,42 @@ window.__ModuleLoader__.load({
|
|
|
1447
1596
|
onChange,
|
|
1448
1597
|
browser: rowEntry
|
|
1449
1598
|
});
|
|
1450
|
-
|
|
1599
|
+
/** 「复制 prompt」:剪贴板不在就报失败,不抛(按钮三档都在,见 #761 定稿)。 */
|
|
1600
|
+
const onCopyPrompt = react.useCallback(async () => {
|
|
1601
|
+
const ok = await copyText(LARK_PROMPT_V5, { clipboard: clipboardOf() ?? void 0 });
|
|
1602
|
+
setCopied(ok ? "done" : "failed");
|
|
1603
|
+
}, []);
|
|
1604
|
+
/** 「飞书 CLI」状态行(#764,定稿 #761):不是配置项——三档读数由技能侧体检出,面板只显示;
|
|
1605
|
+
* 三档都有「复制 prompt」按钮与官网那一行(逐字、可点击)。 */
|
|
1606
|
+
const renderLark = () => {
|
|
1607
|
+
if (lark.kind === "loading") return react.createElement("div", { style: S.rows }, react.createElement("div", { style: S.label }, "飞书 CLI"), react.createElement("div", { style: S.muted }, "飞书 CLI 检测中"));
|
|
1608
|
+
if (lark.kind === "failed") return react.createElement("div", { style: S.rows }, react.createElement("div", { style: S.label }, "飞书 CLI"), react.createElement("div", { style: S.error }, lark.message), react.createElement("div", { style: S.bar }, react.createElement("button", {
|
|
1609
|
+
style: S.btn,
|
|
1610
|
+
type: "button",
|
|
1611
|
+
onClick: () => void loadHealth()
|
|
1612
|
+
}, "重新检测")));
|
|
1613
|
+
const item = lark.item;
|
|
1614
|
+
const tone = item.status === "green" ? S.okText : item.status === "red" ? S.error : S.muted;
|
|
1615
|
+
return react.createElement("div", { style: S.rows }, react.createElement("div", { style: S.label }, "飞书 CLI"), react.createElement("div", { style: tone }, item.message), item.action === "" ? null : react.createElement("div", { style: S.hint }, item.action), react.createElement("div", { style: S.bar }, react.createElement("button", {
|
|
1616
|
+
style: S.btn,
|
|
1617
|
+
type: "button",
|
|
1618
|
+
onClick: () => void onCopyPrompt()
|
|
1619
|
+
}, "复制 prompt"), react.createElement("button", {
|
|
1620
|
+
style: S.btn,
|
|
1621
|
+
type: "button",
|
|
1622
|
+
onClick: () => void loadHealth()
|
|
1623
|
+
}, "重新检测")), copied === "done" ? react.createElement("div", { style: S.okText }, "已复制安装指引") : copied === "failed" ? react.createElement("div", { style: S.error }, "复制失败:剪贴板不可用,请手动复制对话框里的安装指引") : null, react.createElement("div", { style: S.info }, react.createElement("a", {
|
|
1624
|
+
href: LARK_OFFICIAL_URL,
|
|
1625
|
+
target: "_blank",
|
|
1626
|
+
rel: "noreferrer"
|
|
1627
|
+
}, LARK_OFFICIAL_LINE)));
|
|
1628
|
+
};
|
|
1629
|
+
return react.createElement("div", { style: S.card }, head, react.createElement("div", { style: S.rows }, common.map(renderRow)), advanced.length === 0 ? null : react.createElement("details", { style: S.advanced }, react.createElement("summary", { style: S.summary }, ADVANCED_GROUP_TITLE), react.createElement("div", { style: S.muted }, ADVANCED_GROUP_NOTE), advanced.map(renderRow)), renderLark(), react.createElement("div", { style: S.bar }, react.createElement("button", {
|
|
1451
1630
|
style: dirty ? S.btnPrimary : S.btn,
|
|
1452
1631
|
type: "button",
|
|
1453
1632
|
disabled: busy || !dirty,
|
|
1454
1633
|
onClick: () => void onSave()
|
|
1455
|
-
}, busy ? "
|
|
1634
|
+
}, busy ? "处理中" : "保存"), react.createElement("button", {
|
|
1456
1635
|
style: S.btn,
|
|
1457
1636
|
type: "button",
|
|
1458
1637
|
disabled: busy,
|
|
@@ -1473,7 +1652,7 @@ window.__ModuleLoader__.load({
|
|
|
1473
1652
|
go: "转到",
|
|
1474
1653
|
showHidden: (n) => "显示隐藏目录(" + n + ")",
|
|
1475
1654
|
empty: "这个目录里没有子目录。",
|
|
1476
|
-
loading: "
|
|
1655
|
+
loading: "正在读取",
|
|
1477
1656
|
newFolder: "新建文件夹",
|
|
1478
1657
|
createConfirm: "创建",
|
|
1479
1658
|
createCancel: "取消",
|
|
@@ -1506,16 +1685,23 @@ window.__ModuleLoader__.load({
|
|
|
1506
1685
|
})));
|
|
1507
1686
|
}
|
|
1508
1687
|
//#endregion
|
|
1688
|
+
exports.LARK_OFFICIAL_LINE = LARK_OFFICIAL_LINE;
|
|
1689
|
+
exports.LARK_OFFICIAL_URL = LARK_OFFICIAL_URL;
|
|
1690
|
+
exports.LARK_PROMPT_V5 = LARK_PROMPT_V5;
|
|
1509
1691
|
exports.READ_TIMEOUT_MS = READ_TIMEOUT_MS;
|
|
1510
1692
|
exports.Row = Row;
|
|
1511
1693
|
exports.apply = apply;
|
|
1694
|
+
exports.clipboardOf = clipboardOf;
|
|
1695
|
+
exports.copyText = copyText;
|
|
1512
1696
|
exports.createBrowseHandler = createBrowseHandler;
|
|
1513
1697
|
exports.directoryEntryMode = directoryEntryMode;
|
|
1514
1698
|
exports.fetchConfigSurface = fetchConfigSurface;
|
|
1699
|
+
exports.fetchHealthSurface = fetchHealthSurface;
|
|
1515
1700
|
exports.fromDraft = fromDraft;
|
|
1516
1701
|
exports.humanizeConfigFailure = humanizeConfigFailure;
|
|
1517
1702
|
exports.inject = inject;
|
|
1518
1703
|
exports.isDirectoryPicker = isDirectoryPicker;
|
|
1704
|
+
exports.larkItemOf = larkItemOf;
|
|
1519
1705
|
exports.openRowBrowser = openRowBrowser;
|
|
1520
1706
|
exports.pickDirectory = pickDirectory;
|
|
1521
1707
|
exports.pickerModeOf = pickerModeOf;
|