dsh-memo-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 CHANGED
@@ -79,56 +79,30 @@ window.__ModuleLoader__.load({
79
79
  title: "库文件名",
80
80
  tier: "common",
81
81
  control: "text",
82
- hint: "数据目录下的数据库文件名。改名=换库,旧笔记不会被读入。"
82
+ readonly: true,
83
+ resolveFrom: "dbFile",
84
+ hint: "数据目录下的库文件(备忘不建库,显示的是会落在哪)。只读,要改请编辑配置文件。"
83
85
  },
84
86
  {
85
87
  key: "html.dir",
86
88
  title: "HTML 产物目录名",
87
89
  tier: "common",
88
90
  control: "text",
89
- hint: "数据目录下存放 HELP 页面的子目录名。"
91
+ readonly: true,
92
+ resolveFrom: "htmlDir",
93
+ hint: "数据目录下存放 HELP 页面的子目录名。只读,要改请编辑配置文件。"
90
94
  },
91
95
  {
92
96
  key: "media.dir",
93
97
  title: "附件目录",
94
98
  tier: "common",
95
99
  control: "directory",
96
- hint: "附件的存放根目录。留空=工作目录下的 media。"
100
+ prefillResolved: "mediaDir",
101
+ hint: "附件的存放根目录。留空=数据目录下的 media(目录须已存在,技能不替你建)。"
97
102
  }
98
103
  ];
99
- /** 高级项:默认收起。改错了多半只是自己撞上麻烦,不确定就别动。 */
100
- const ADVANCED = [
101
- {
102
- key: "files.help",
103
- title: "产物文件名主体 · HELP",
104
- tier: "advanced",
105
- control: "text",
106
- hint: "HELP 文件名前缀,后面自动加时间戳。"
107
- },
108
- {
109
- key: "files.lookup",
110
- title: "产物文件名主体 · 速查表",
111
- tier: "advanced",
112
- control: "text",
113
- hint: "速查表文件名前缀。"
114
- },
115
- {
116
- key: "lark.cliPath",
117
- title: "飞书 CLI 路径",
118
- tier: "advanced",
119
- control: "text",
120
- hint: "飞书 CLI 的可执行文件路径。留空=自动探测。"
121
- },
122
- {
123
- key: "lark.qrDir",
124
- title: "飞书授权二维码落点",
125
- tier: "advanced",
126
- control: "directory",
127
- hint: "飞书授权二维码 PNG 的存放目录,缺就现建。留空=系统临时目录。"
128
- }
129
- ];
130
- /** 设置页的行表,顺序即页面顺序。 */
131
- const CONFIG_ITEMS = [...COMMON, ...ADVANCED];
104
+ /** 设置页的行表,顺序即页面顺序(#760 起高级组清空:删键 4 项出表,无高级项)。 */
105
+ const CONFIG_ITEMS = [...COMMON];
132
106
  /** 常用项行数(页面上直接画出来的那些)。 */
133
107
  const COMMON_ITEM_COUNT = COMMON.length;
134
108
  /** 高级组标题与副文案。 */
@@ -1297,7 +1271,7 @@ window.__ModuleLoader__.load({
1297
1271
  alive = false;
1298
1272
  };
1299
1273
  }, [props.getCall]);
1300
- if (state.kind === "loading") return react.createElement("div", { style: S.card }, react.createElement("div", { style: S.muted }, "备忘录加载中…"));
1274
+ if (state.kind === "loading") return react.createElement("div", { style: S.card }, react.createElement("div", { style: S.muted }, "备忘录加载中"));
1301
1275
  if (state.kind === "data") return react.createElement("div", { style: S.card }, react.createElement("div", { style: S.title }, SLOT_TITLE), react.createElement("div", { style: S.total }, `total ${state.total}`), react.createElement(VersionLine, null));
1302
1276
  return react.createElement("div", { style: S.card }, react.createElement("div", { style: state.kind === "absent" ? S.muted : S.error }, state.message), react.createElement(VersionLine, null));
1303
1277
  }
@@ -1363,24 +1337,42 @@ window.__ModuleLoader__.load({
1363
1337
  function resetConfigSurface(call) {
1364
1338
  return configRpc(call, RPC_ENDPOINT_CONFIG_RESET, {});
1365
1339
  }
1366
- /** 把配置取值铺成「行键 → 输入框文本」(页面表单态;值缺项即空串,不返空留白)。
1367
- *
1368
- * `prefill` 是**落点回执**(配置面那几格解析出来的绝对路径):标了 `prefillFrom` 的行在取值空着时
1369
- * 直接显示那条绝对路径——用户不必自己拼路径(#743),显示的就是技能真会用的那个目录(逐字相同)。 */
1340
+ /** 只读行显示什么:标了 `resolveFrom` ⇒ 回执 `resolved` 组那一格;没标 ⇒ 配置文件里那个值本身。 */
1341
+ function readonlyTextOf(item, raw, prefill) {
1342
+ if (item.resolveFrom === void 0) return raw;
1343
+ const shown = prefill.resolved?.[item.resolveFrom];
1344
+ return typeof shown === "string" ? shown : "";
1345
+ }
1370
1346
  function toDraft(values, prefill = {}) {
1371
1347
  const draft = {};
1372
1348
  for (const item of CONFIG_ITEMS) {
1373
1349
  const v = readPath(values, item.key);
1374
1350
  const raw = v === void 0 || v === null ? "" : String(v);
1351
+ if (item.readonly === true) {
1352
+ draft[item.key] = readonlyTextOf(item, raw, prefill);
1353
+ continue;
1354
+ }
1355
+ if (raw === "" && item.prefillResolved !== void 0) {
1356
+ const shown = prefill.resolved?.[item.prefillResolved];
1357
+ if (typeof shown === "string" && shown !== "") {
1358
+ draft[item.key] = shown;
1359
+ continue;
1360
+ }
1361
+ }
1375
1362
  const fallback = item.prefillFrom === void 0 ? void 0 : prefill[item.prefillFrom];
1376
1363
  draft[item.key] = raw === "" && typeof fallback === "string" && fallback !== "" ? fallback : raw;
1377
1364
  }
1378
1365
  return draft;
1379
1366
  }
1380
- /** 表单态 → 配置取值(按控件种类还原类型;空串对文本项照收,语义由「按默认落点」承担)。 */
1367
+ /** 表单态 → 配置取值(按控件种类还原类型;空串对文本项照收,语义由「按默认落点」承担)。
1368
+ *
1369
+ * **只读行不收**(#760,照 #749 样板):它们显示的是技能算好的绝对路径,写回配置就是把「显示的路径」
1370
+ * 当成「配置值」——保存只提交真能改的那些行(本家=`db.dir`/`media.dir` 两行),其余键由技能侧做组内
1371
+ * 合并保留现值。 */
1381
1372
  function fromDraft(draft) {
1382
1373
  const values = {};
1383
1374
  for (const item of CONFIG_ITEMS) {
1375
+ if (item.readonly === true) continue;
1384
1376
  const raw = draft[item.key] ?? "";
1385
1377
  if (item.control === "number") {
1386
1378
  const n = Number(raw);
@@ -1439,33 +1431,75 @@ window.__ModuleLoader__.load({
1439
1431
  else if (outcome.kind === "unavailable") deps.onUnavailable(outcome.message);
1440
1432
  };
1441
1433
  }
1434
+ function copyPrompt(text, port) {
1435
+ const globalNavigator = globalThis.navigator;
1436
+ const channel = port !== void 0 ? port : globalNavigator?.clipboard ?? null;
1437
+ if (channel === null || typeof channel.writeText !== "function") return Promise.resolve(false);
1438
+ try {
1439
+ return Promise.resolve(channel.writeText(text)).then(() => true, () => false);
1440
+ } catch {
1441
+ return Promise.resolve(false);
1442
+ }
1443
+ }
1444
+ /** 「飞书 CLI」状态行(#760,定稿 #759;不是配置项):三档读数 + 复制安装指引按钮 + 官网链接。
1445
+ *
1446
+ * 判据由技能侧出(回执 `lark` 格),面板只显示:
1447
+ * · 没找到 CLI → 红「没找到飞书 CLI」+ 复制安装指引按钮 + 官网链接;
1448
+ * · 找到了但没登录/缺 task 权限 → 黄「找到了〈路径〉,但还没登录或没拿到 task 域授权」+ 同一个按钮 + 官网链接;
1449
+ * · 就绪 → 绿「已登录且 task 域可写:〈路径〉(〈版本〉)」+ 同一个按钮 + 官网链接。
1450
+ * 官网行三档逐字显示「飞书CLI官网为:https://www.feishu.cn/feishu-cli」,显示成文字+点一下新窗口跳转。
1451
+ * 旧技能(回执无 `lark` 格)⇒ 只显示一行弱提示,不报错、不探测。 */
1452
+ function LarkStatus(props) {
1453
+ const lark = props.lark;
1454
+ if (lark === void 0) return react.createElement("div", { style: S.rows }, react.createElement("div", { style: S.label }, "飞书 CLI"), react.createElement("div", { style: S.muted }, "状态未知(技能回执无此格,请升级技能包)。"));
1455
+ const status = lark.tier === "missing" ? "没找到飞书 CLI" : lark.tier === "partial" ? "找到了 " + (lark.cliPath ?? "飞书 CLI") + ",但还没登录或没拿到 task 域授权" : "已登录且 task 域可写:" + (lark.cliPath ?? "") + (lark.version !== null ? "(" + lark.version + ")" : "");
1456
+ const statusStyle = lark.tier === "full" ? S.okText : lark.tier === "partial" ? S.muted : S.error;
1457
+ return react.createElement("div", { style: S.rows }, react.createElement("div", { style: S.label }, "飞书 CLI"), react.createElement("div", { style: statusStyle }, status), react.createElement("div", { style: S.bar }, react.createElement("button", {
1458
+ style: S.btn,
1459
+ type: "button",
1460
+ onClick: () => props.onCopy(lark.prompt)
1461
+ }, "复制安装指引"), react.createElement("a", {
1462
+ href: lark.websiteUrl,
1463
+ target: "_blank",
1464
+ rel: "noreferrer"
1465
+ }, lark.websiteLine)));
1466
+ }
1442
1467
  /** 一行输入(四种控件对齐受限 YAML 子集:文本/数字/布尔/目录)。
1443
1468
  *
1444
1469
  * 目录档=文本框 + 一枚唤起系统文件夹选择器的按钮;`onBrowse` 缺席(命名空间拿不到、
1445
- * 或这条路已被拒)时不画按钮,文本框照旧——那是该缝自己的契约(供不了就收起入口,不是失败)。 */
1470
+ * 或这条路已被拒)时不画按钮,文本框照旧——那是该缝自己的契约(供不了就收起入口,不是失败)。
1471
+ *
1472
+ * **只读行(#760,照 #749 样板)**:`item.readonly` 为真时控件一律 `disabled`(值只经技能侧解析后显示、
1473
+ * 不给改),`onChange` 不接(不给「改得动」留假象);目录行的浏览按钮**保留但不可点击**
1474
+ * (定稿:入口不作废,只是不能改)。控件文案**不出现省略号**(#746 处置 9)——按钮就写
1475
+ * 「选择文件夹」/「浏览」两个字面。 */
1446
1476
  function Row(props) {
1447
1477
  const { item } = props;
1478
+ const readonly = item.readonly === true;
1479
+ const disabled = props.disabled || readonly;
1480
+ const onChange = readonly ? void 0 : (e) => props.onChange(item.key, e.target.value);
1448
1481
  const control = item.control === "switch" ? react.createElement("input", {
1449
1482
  type: "checkbox",
1450
1483
  checked: props.value === "true",
1451
- disabled: props.disabled,
1452
- onChange: (e) => props.onChange(item.key, e.target.checked ? "true" : "false")
1484
+ disabled,
1485
+ onChange: readonly ? void 0 : (e) => props.onChange(item.key, e.target.checked ? "true" : "false")
1453
1486
  }) : react.createElement("input", {
1454
1487
  style: S.input,
1455
1488
  type: item.control === "number" ? "number" : "text",
1456
1489
  value: props.value,
1457
- disabled: props.disabled,
1490
+ disabled,
1458
1491
  spellCheck: false,
1459
- onChange: (e) => props.onChange(item.key, e.target.value)
1492
+ onChange
1460
1493
  });
1461
- /** 三态入口:供不了(`none`/缺席)就不画,不摆一个点了没反应的死按钮。 */
1494
+ /** 三态入口:供不了(`none`/缺席)就不画,不摆一个点了没反应的死按钮。
1495
+ * 只读行照画(按钮保留),但 `disabled` ⇒ 点不动。 */
1462
1496
  const entry = props.browser ?? null;
1463
1497
  const browse = item.control === "directory" && entry !== null && entry.mode !== "none" ? react.createElement("button", {
1464
1498
  style: S.btnPick,
1465
1499
  type: "button",
1466
- disabled: props.disabled,
1500
+ disabled,
1467
1501
  onClick: () => entry.onOpen(item.key)
1468
- }, entry.mode === "native" ? "选择文件夹…" : "浏览…") : null;
1502
+ }, entry.mode === "native" ? "选择文件夹" : "浏览") : null;
1469
1503
  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));
1470
1504
  }
1471
1505
  /** 技能设置页:承载备忘录自己的全部可配置项(只配置,不干活)。
@@ -1571,7 +1605,17 @@ window.__ModuleLoader__.load({
1571
1605
  onOpen: onOpenRow
1572
1606
  };
1573
1607
  const surface = state.kind === "ready" ? state.surface : null;
1574
- const dirty = surface !== null && CONFIG_ITEMS.some((i) => (draft[i.key] ?? "") !== (toDraft(surface.values, surface)[i.key] ?? ""));
1608
+ /** 脏值只看**可改行**(#760,照 #749 样板):只读行显示的是技能算好的绝对路径,拿它当「改动」
1609
+ * 会让打开面板就变「未保存」。 */
1610
+ const editableItems = CONFIG_ITEMS.filter((i) => i.readonly !== true);
1611
+ const dirty = surface !== null && editableItems.some((i) => (draft[i.key] ?? "") !== (toDraft(surface.values, surface)[i.key] ?? ""));
1612
+ /** 复制安装指引(面板「飞书 CLI」状态行那枚按钮):成功落字,失败指到下方的全文手动复制。 */
1613
+ const onCopyPrompt = react.useCallback(async (prompt) => {
1614
+ setNotice(null);
1615
+ setError(null);
1616
+ if (await copyPrompt(prompt)) setNotice("已复制,去粘贴给 AI");
1617
+ else setError("复制失败,长按选择下方文本手动复制");
1618
+ }, []);
1575
1619
  /** 写完(保存/重置)之后重新读一份整面,读到了才敢提示「已完成」。 */
1576
1620
  const writeThenReload = react.useCallback(async (done) => {
1577
1621
  setBusy(true);
@@ -1610,7 +1654,7 @@ window.__ModuleLoader__.load({
1610
1654
  else setError(r.message);
1611
1655
  }, [props.getCall, writeThenReload]);
1612
1656
  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);
1613
- if (state.kind === "loading") return react.createElement("div", { style: S.card }, head, react.createElement("div", { style: S.muted }, "配置读取中…"), react.createElement(VersionLine, null));
1657
+ if (state.kind === "loading") return react.createElement("div", { style: S.card }, head, react.createElement("div", { style: S.muted }, "配置读取中"), react.createElement(VersionLine, null));
1614
1658
  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", {
1615
1659
  style: S.btn,
1616
1660
  type: "button",
@@ -1630,12 +1674,15 @@ window.__ModuleLoader__.load({
1630
1674
  onChange,
1631
1675
  browser: rowEntry
1632
1676
  });
1633
- return react.createElement("div", { style: S.card }, head, react.createElement("div", { style: S.rows }, common.map(renderRow)), 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)), react.createElement("div", { style: S.bar }, react.createElement("button", {
1677
+ return react.createElement("div", { style: S.card }, head, react.createElement("div", { style: S.rows }, common.map(renderRow)), advanced.length > 0 ? 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)) : null, react.createElement(LarkStatus, {
1678
+ lark: surface?.lark,
1679
+ onCopy: (prompt) => void onCopyPrompt(prompt)
1680
+ }), react.createElement("div", { style: S.bar }, react.createElement("button", {
1634
1681
  style: dirty ? S.btnPrimary : S.btn,
1635
1682
  type: "button",
1636
1683
  disabled: busy || !dirty,
1637
1684
  onClick: () => void onSave()
1638
- }, busy ? "处理中…" : "保存"), react.createElement("button", {
1685
+ }, busy ? "处理中" : "保存"), react.createElement("button", {
1639
1686
  style: S.btn,
1640
1687
  type: "button",
1641
1688
  disabled: busy,
@@ -1656,7 +1703,7 @@ window.__ModuleLoader__.load({
1656
1703
  go: "转到",
1657
1704
  showHidden: (n) => "显示隐藏目录(" + n + ")",
1658
1705
  empty: "这个目录里没有子目录。",
1659
- loading: "正在读取…",
1706
+ loading: "正在读取",
1660
1707
  newFolder: "新建文件夹",
1661
1708
  createConfirm: "创建",
1662
1709
  createCancel: "取消",
@@ -1721,8 +1768,10 @@ window.__ModuleLoader__.load({
1721
1768
  }
1722
1769
  }
1723
1770
  //#endregion
1771
+ exports.LarkStatus = LarkStatus;
1724
1772
  exports.Row = Row;
1725
1773
  exports.apply = apply;
1774
+ exports.copyPrompt = copyPrompt;
1726
1775
  exports.createBrowseHandler = createBrowseHandler;
1727
1776
  exports.directoryEntryMode = directoryEntryMode;
1728
1777
  exports.fetchConfigSurface = fetchConfigSurface;