dsh-calorie 0.3.12 → 0.3.13
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/bridge.d.ts +20 -14
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +25 -77
- package/dist/bridge.js.map +1 -1
- package/dist/client.js +481 -143
- package/dist/client.js.map +1 -1
- package/package.json +3 -3
package/dist/client.js
CHANGED
|
@@ -158,21 +158,6 @@ window.__ModuleLoader__.load({
|
|
|
158
158
|
function noFollowKeys() {
|
|
159
159
|
return [];
|
|
160
160
|
}
|
|
161
|
-
/** 这一行有没有「写的值 ≠ 生效值」这回事:只要是可改行、标了落点来源、写的非空、
|
|
162
|
-
* 且技能算出来的生效值另有其值(去掉尾分隔符后仍不同),就报这两个事实。 */
|
|
163
|
-
function fallbackFactsOf(item, raw, source) {
|
|
164
|
-
if (item.readonly === true || item.prefillFrom === void 0) return null;
|
|
165
|
-
const written = raw.trim();
|
|
166
|
-
if (written === "") return null;
|
|
167
|
-
const effective = prefillValueOf(source, item.prefillFrom);
|
|
168
|
-
if (effective === void 0) return null;
|
|
169
|
-
const strip = (path) => path.replace(/[\\/]+$/, "");
|
|
170
|
-
if (strip(written) === strip(effective)) return null;
|
|
171
|
-
return {
|
|
172
|
-
written,
|
|
173
|
-
effective
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
161
|
/** 配置面报错 → 人话指引。
|
|
177
162
|
*
|
|
178
163
|
* 报文由技能侧给出(`base-link-core` 的 `ConfigError` 已带行号与文件名,技能 `cli/config.ts`
|
|
@@ -1072,6 +1057,24 @@ window.__ModuleLoader__.load({
|
|
|
1072
1057
|
*
|
|
1073
1058
|
* 手写 `React.createElement`(本包 client 束禁 JSX)。样式表 `S` 就在本件里——面板怎么画只有这一处。
|
|
1074
1059
|
*
|
|
1060
|
+
* **外观真源=`docs/agents/配置区域视觉模型-v3.1.html`(#920 阶段二起)**:本件的 `S` 逐项照它的
|
|
1061
|
+
* CSS(`:32-205`)、行模板照它的 `rowShell`/`masterShell`/`cardShell`(`:349-451`)、状态徽标照它的
|
|
1062
|
+
* `badgeHTML`(`:544-552`)那七档。**颜色/间距/圆角/字号/层级一律照取值,不许自己重新设计**;
|
|
1063
|
+
* 与本件不同的只有下面这四条(每条都在这里写明为什么,改之前先读 #920 的证据件):
|
|
1064
|
+
* ① **字号一律 em,没有 px**:跨包锁 `test/panel-type-739.test.mjs` 钉死「零 `font:` 简写/零字族/
|
|
1065
|
+
* 字号一律相对单位」——宿主没有界面字号 token,绝对 px 会在别的窗口尺寸上失真。v3.1 的 px 按它
|
|
1066
|
+
* 自己已锁死的那两对锚点换算(`13.5px↔1.08em`、`11.5px↔0.92em` ⇒ **1px=0.08em**),一个不丢。
|
|
1067
|
+
* ② **值列不写等宽字族**:v3.1 的值列要 `font-family: var(--ic-mono)`,而 `#739 ①` 明令共用件零
|
|
1068
|
+
* `fontFamily`/零 `font:` 简写 ⇒ 值继承宿主字族。这是与 v3.1 的**一处已知不同**(#920 证据件有账)。
|
|
1069
|
+
* ③ **换行判据交给 CSS,不按字符数估宽**:v3.1 靠 JS 量文本宽在两套模板间两选一(审查 P0-4/P0-5:
|
|
1070
|
+
* 每列 6.048px、真阈值 40–43 列而注释按 34 估 ⇒ 9 行被无谓拆两行、快面一次性长高 194px)。
|
|
1071
|
+
* 本件没有 DOM 可量,故照审查给的修法把同一个决定交给排版引擎:行是 `flex-wrap` 容器,标签列定宽、
|
|
1072
|
+
* 值列 `flex-basis:auto`(=内容的自然宽)——装不下时**整行让位**(值落第二行、按钮仍在第一行行尾,
|
|
1073
|
+
* 与 v3.1 的 `.is-wide` 同形);值再长就在自己那一行里 `overflow-wrap:anywhere` 折下去。
|
|
1074
|
+
* **零省略号、零截断**(`white-space` 不用 `nowrap`、`text-overflow` 一次都不出现)。
|
|
1075
|
+
* ④ **画不出来的三样**用等价形态顶上:分组箭头 `::before`/`[open]` 旋转/keyframes 动画内联样式
|
|
1076
|
+
* 给不出 ⇒ 画静态「›」与静态环(形状与 v3.1 同,只少了动)。
|
|
1077
|
+
*
|
|
1075
1078
|
* 三条定稿(v3)在本件落地:
|
|
1076
1079
|
* ① **只读行的目录按钮不画**(不画一枚点了也没反应的死按钮);
|
|
1077
1080
|
* ② 目录行的按钮字面**两档合一**,一律「浏览文件夹」(入口供不了时整枚不画);
|
|
@@ -1079,148 +1082,401 @@ window.__ModuleLoader__.load({
|
|
|
1079
1082
|
*
|
|
1080
1083
|
* 对外只经 `config-panel-api.ts` 那一道门;本件自己不出门。
|
|
1081
1084
|
*/
|
|
1082
|
-
/**
|
|
1085
|
+
/** 面板的样式表:取值逐项照 v3.1(颜色走 DSH 主题别名,写死值只做回退;字号一律相对单位 em)。
|
|
1086
|
+
*
|
|
1087
|
+
* 键名一个都不许改:六家附加块真消费 `okText`/`error`/`rows`/`label`/`muted`/`hint`/`info`/
|
|
1088
|
+
* `bar`/`btn` 这九格(`plugin-memo-ilife/src/client.ts`、`plugin-schedule-ilife/src/client.ts` 的
|
|
1089
|
+
* `PanelStyleSlots`),跨包锁另钉 `card`/`input`/`btnPrimary`/`btnPick`/`summary`/`title`/`version`。 */
|
|
1083
1090
|
const S = {
|
|
1091
|
+
/** `.ic-card`:`background:var(--ic-surface)`(=bg-layer-1)+`border:1px solid var(--ic-line-strong)`
|
|
1092
|
+
* +`border-radius:12px`+`padding:14px`+`box-shadow:inset 0 1px 0 rgba(255,255,255,.06)`。
|
|
1093
|
+
* 基准字号**不钉在卡片上**(#739 ⑤:跟着宿主走)。 */
|
|
1084
1094
|
card: {
|
|
1085
|
-
padding:
|
|
1086
|
-
borderRadius:
|
|
1087
|
-
border: "1px solid var(--dsw-alias-border, rgba(
|
|
1088
|
-
background: "var(--dsw-alias-bg-
|
|
1089
|
-
color: "var(--dsw-alias-label-primary,
|
|
1090
|
-
|
|
1095
|
+
padding: 14,
|
|
1096
|
+
borderRadius: 12,
|
|
1097
|
+
border: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))",
|
|
1098
|
+
background: "var(--dsw-alias-bg-layer-1, #232324)",
|
|
1099
|
+
color: "var(--dsw-alias-label-primary, #f9fafb)",
|
|
1100
|
+
boxShadow: "inset 0 1px 0 rgba(255,255,255,.06)"
|
|
1101
|
+
},
|
|
1102
|
+
/** `.ic-head{display:flex;align-items:center;gap:10px;min-height:22px}`(徽标靠 `margin-left:auto` 右贴)。 */
|
|
1103
|
+
head: {
|
|
1104
|
+
display: "flex",
|
|
1105
|
+
alignItems: "center",
|
|
1106
|
+
gap: 10,
|
|
1107
|
+
minHeight: 22
|
|
1091
1108
|
},
|
|
1109
|
+
/** `.ic-title{font-size:13.5px;font-weight:640}`——字号按 60 行那条锚点换算成 `1.08em`。 */
|
|
1092
1110
|
title: {
|
|
1093
1111
|
fontSize: "1.08em",
|
|
1094
|
-
fontWeight:
|
|
1095
|
-
|
|
1112
|
+
fontWeight: 640
|
|
1113
|
+
},
|
|
1114
|
+
/** `.ic-file{margin-top:4px}` 的那 4px 挂在两行的公共容器上:`info` 自己一格 margin 都不写
|
|
1115
|
+
* (#743 ①:头部两行间距不一家一个数,故这一项一律不写 margin)。 */
|
|
1116
|
+
fileWrap: { marginTop: 4 },
|
|
1117
|
+
/** `.ic-file b{color:var(--ic-faint);font-weight:400;margin-right:5px}`(「配置文件」「数据目录」那两个字)。 */
|
|
1118
|
+
fileLabel: {
|
|
1119
|
+
color: "var(--dsw-alias-label-tertiary, #adb2b8)",
|
|
1120
|
+
fontWeight: 400,
|
|
1121
|
+
marginRight: 5
|
|
1122
|
+
},
|
|
1123
|
+
/** `.ic-file{color:var(--ic-muted);font-size:11.5px;word-break:break-all}`(11.5px ⇒ `0.92em`)。 */
|
|
1124
|
+
info: {
|
|
1125
|
+
color: "var(--dsw-alias-label-secondary, #cfd3d6)",
|
|
1126
|
+
fontSize: "0.92em",
|
|
1127
|
+
wordBreak: "break-all"
|
|
1096
1128
|
},
|
|
1129
|
+
/** `.ic-muted` 那一档(次要字)。 */
|
|
1097
1130
|
muted: {
|
|
1098
|
-
color: "var(--dsw-alias-label-secondary, #
|
|
1131
|
+
color: "var(--dsw-alias-label-secondary, #cfd3d6)",
|
|
1099
1132
|
fontSize: "0.92em"
|
|
1100
1133
|
},
|
|
1134
|
+
/** 错误字:取 v3.1 的 `--ic-danger`;字号 `1em`(#739 钉死)。 */
|
|
1101
1135
|
error: {
|
|
1102
1136
|
marginTop: 8,
|
|
1103
|
-
color: "var(--dsw-alias-
|
|
1137
|
+
color: "var(--dsw-alias-state-error-primary, #e0685f)",
|
|
1104
1138
|
fontSize: "1em",
|
|
1105
1139
|
whiteSpace: "pre-wrap"
|
|
1106
1140
|
},
|
|
1141
|
+
/** 成功字:取 v3.1 的 `--ic-ok`;字号 `0.96em`(#739 钉死)。 */
|
|
1107
1142
|
okText: {
|
|
1108
1143
|
marginTop: 8,
|
|
1109
|
-
color: "var(--dsw-alias-state-success-primary, #
|
|
1144
|
+
color: "var(--dsw-alias-state-success-primary, #4ec9a0)",
|
|
1110
1145
|
fontSize: "0.96em"
|
|
1111
1146
|
},
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1147
|
+
/** 行组的容器(六家附加块也拿它当外框;v3.1 没有对应类,故只给一个中性块)。 */
|
|
1148
|
+
rows: { display: "block" },
|
|
1149
|
+
/** `.ic-row`:三列栅格 + `gap:4px 10px` + `padding:7px 0` + 一条 `.ic-line` 下边线。
|
|
1150
|
+
* 本件把它落成**换行弹性盒**(见文件头 ③):标签定宽在第一行,值装不下时整行让位,
|
|
1151
|
+
* 动作槽绝对定位贴在行尾(`.ic-row .ic-slot[data-slot^="a:"]` 那一格)。 */
|
|
1152
|
+
row: {
|
|
1153
|
+
position: "relative",
|
|
1154
|
+
display: "flex",
|
|
1155
|
+
flexWrap: "wrap",
|
|
1156
|
+
alignItems: "center",
|
|
1157
|
+
gap: "4px 10px",
|
|
1158
|
+
padding: "7px 0",
|
|
1159
|
+
borderBottom: "1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06))"
|
|
1116
1160
|
},
|
|
1117
|
-
|
|
1118
|
-
|
|
1161
|
+
/** `.ic-row:last-child{border-bottom:0}`——内联样式没有 `:last-child`,由画它的人明说。 */
|
|
1162
|
+
rowLast: {
|
|
1163
|
+
position: "relative",
|
|
1164
|
+
display: "flex",
|
|
1165
|
+
flexWrap: "wrap",
|
|
1166
|
+
alignItems: "center",
|
|
1167
|
+
gap: "4px 10px",
|
|
1168
|
+
padding: "7px 0"
|
|
1169
|
+
},
|
|
1170
|
+
/** `.ic-under{margin:9px 0 0 8px;border-left:1px solid var(--ic-line-strong);padding-left:13px}`:
|
|
1171
|
+
* **跟随行**相对它的可改行(母版行)缩进 8+1+13=**22px**,左边挂一条线。
|
|
1172
|
+
*
|
|
1173
|
+
* 取值逐字照抄;只有一处形态差别要说清:`.ic-under` 在 v3.1 里是**一整块**(同一个可改行的一批
|
|
1174
|
+
* 跟随者共用一个 div、共一条左边线、块首还有 9px 上边距),而共用件手上只有「谁是跟随行」这一个
|
|
1175
|
+
* 集合(由六家交的 `followKeysOf` 钩子逐键探出),**没有**「这一行归哪一块」的分块归属
|
|
1176
|
+
* (同一个键可以同时挂在两张跟随表上,钩子只回一个去重后的并集)。故这里按**行**落这条缩进:
|
|
1177
|
+
* 缩进量与左边线同值,块首那 9px 上边距不落(落下去会在每两行之间多出一道 9px 的缝)。 */
|
|
1178
|
+
followerRow: {
|
|
1179
|
+
position: "relative",
|
|
1180
|
+
display: "flex",
|
|
1181
|
+
flexWrap: "wrap",
|
|
1182
|
+
alignItems: "center",
|
|
1183
|
+
gap: "4px 10px",
|
|
1184
|
+
padding: "7px 0 7px 13px",
|
|
1185
|
+
marginLeft: 8,
|
|
1186
|
+
borderLeft: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))",
|
|
1187
|
+
borderBottom: "1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06))"
|
|
1188
|
+
},
|
|
1189
|
+
/** 跟随行的最后一行:同上,去掉下边线(照 `.ic-row:last-child{border-bottom:0}`)。 */
|
|
1190
|
+
followerRowLast: {
|
|
1191
|
+
position: "relative",
|
|
1192
|
+
display: "flex",
|
|
1193
|
+
flexWrap: "wrap",
|
|
1194
|
+
alignItems: "center",
|
|
1195
|
+
gap: "4px 10px",
|
|
1196
|
+
padding: "7px 0 7px 13px",
|
|
1197
|
+
marginLeft: 8,
|
|
1198
|
+
borderLeft: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))"
|
|
1199
|
+
},
|
|
1200
|
+
/** `.ic-row .ic-k{font-size:11.5px;color:var(--ic-muted)}`;`--ic-kw:118px` 是标签列的定宽。 */
|
|
1201
|
+
rowKey: {
|
|
1202
|
+
flex: "0 0 auto",
|
|
1203
|
+
minWidth: 118,
|
|
1119
1204
|
fontSize: "0.92em",
|
|
1120
|
-
|
|
1205
|
+
color: "var(--dsw-alias-label-secondary, #cfd3d6)"
|
|
1206
|
+
},
|
|
1207
|
+
/** `.ic-row .ic-v{font-size:11px;min-width:0}`(11px ⇒ `0.88em`)+ 文件头 ③ 的让位口径:
|
|
1208
|
+
* `flex:1 1 auto` ⇒ 装不下整行让位;`overflow-wrap:anywhere` ⇒ 折下去、零省略号;
|
|
1209
|
+
* `paddingRight` 给行尾那枚按钮(+列间距)让出宽度——就是 `.ic-row` 第三列那一格。 */
|
|
1210
|
+
rowValue: {
|
|
1211
|
+
flex: "1 1 auto",
|
|
1212
|
+
minWidth: 0,
|
|
1213
|
+
paddingRight: 56,
|
|
1214
|
+
fontSize: "0.88em",
|
|
1215
|
+
whiteSpace: "normal",
|
|
1216
|
+
overflowWrap: "anywhere",
|
|
1217
|
+
lineHeight: 1.5
|
|
1218
|
+
},
|
|
1219
|
+
/** 行尾动作槽(复制/跟随标记):绝对定位贴行尾,行一换行它仍留在第一行行尾(=`.is-wide` 那一形)。 */
|
|
1220
|
+
rowActs: {
|
|
1221
|
+
position: "absolute",
|
|
1222
|
+
right: 0,
|
|
1223
|
+
top: 7,
|
|
1224
|
+
display: "flex",
|
|
1225
|
+
gap: 4,
|
|
1226
|
+
alignItems: "center"
|
|
1227
|
+
},
|
|
1228
|
+
/** `.ic-follow{font-size:11px;color:var(--ic-attn);white-space:nowrap}`(11px ⇒ `0.88em`)。 */
|
|
1229
|
+
follow: {
|
|
1230
|
+
fontSize: "0.88em",
|
|
1231
|
+
color: "var(--dsw-alias-state-warn-primary, #e0a33e)",
|
|
1232
|
+
whiteSpace: "nowrap",
|
|
1233
|
+
display: "inline-flex",
|
|
1234
|
+
alignItems: "center",
|
|
1235
|
+
gap: 4
|
|
1236
|
+
},
|
|
1237
|
+
/** `.ic-follow::before{content:'↳';opacity:.8}`——内联样式给不出伪元素,画成真的那一个字符。 */
|
|
1238
|
+
followMark: { opacity: .8 },
|
|
1239
|
+
/** `.ic-master`:母版行那一块盒子(`--ic-sunken` 底 + `border-radius:8px` + `padding:11px 12px`
|
|
1240
|
+
* + `margin-top:11px` + 内高光)。 */
|
|
1241
|
+
master: {
|
|
1242
|
+
background: "var(--dsw-alias-bg-layer-3, #353638)",
|
|
1243
|
+
border: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))",
|
|
1244
|
+
borderRadius: 8,
|
|
1245
|
+
padding: "11px 12px",
|
|
1246
|
+
marginTop: 11,
|
|
1247
|
+
boxShadow: "inset 0 1px 0 rgba(255,255,255,.05)"
|
|
1248
|
+
},
|
|
1249
|
+
/** `.ic-master .ic-lab{font-weight:600;display:flex;align-items:center;gap:7px}`;
|
|
1250
|
+
* 字号**不写**(#739 ④:`label` 这一项该继承宿主字号)。 */
|
|
1251
|
+
label: {
|
|
1252
|
+
display: "flex",
|
|
1253
|
+
alignItems: "center",
|
|
1254
|
+
gap: 7,
|
|
1255
|
+
fontWeight: 600
|
|
1256
|
+
},
|
|
1257
|
+
/** `.ic-chip{font-size:10.5px;border:1px solid var(--ic-accent);border-radius:999px;padding:0 6px}`(10.5px ⇒ `0.84em`)。 */
|
|
1258
|
+
chip: {
|
|
1259
|
+
fontSize: "0.84em",
|
|
1260
|
+
border: "1px solid var(--dsw-alias-brand-primary, #f9fafb)",
|
|
1261
|
+
borderRadius: 999,
|
|
1262
|
+
padding: "0 6px",
|
|
1263
|
+
color: "var(--dsw-alias-brand-primary, #f9fafb)",
|
|
1264
|
+
whiteSpace: "nowrap"
|
|
1121
1265
|
},
|
|
1122
|
-
|
|
1123
|
-
label: { fontWeight: 600 },
|
|
1266
|
+
/** `.ic-master .ic-hint{color:var(--ic-muted);font-size:11.5px;margin-top:3px;line-height:1.5}`。 */
|
|
1124
1267
|
hint: {
|
|
1125
|
-
color: "var(--dsw-alias-label-secondary, #
|
|
1268
|
+
color: "var(--dsw-alias-label-secondary, #cfd3d6)",
|
|
1126
1269
|
fontSize: "0.92em",
|
|
1127
|
-
|
|
1270
|
+
marginTop: 3,
|
|
1271
|
+
lineHeight: 1.5
|
|
1128
1272
|
},
|
|
1273
|
+
/** `.ic-master input`:整行宽、`margin-top:7px`、`padding:5px 8px`、`--ic-bg` 底、`--ic-line-strong` 描边、
|
|
1274
|
+
* `border-radius:6px`。字号与字族**不写**(#739 ④/①:`input` 继承宿主)。 */
|
|
1129
1275
|
input: {
|
|
1276
|
+
display: "block",
|
|
1130
1277
|
width: "100%",
|
|
1278
|
+
marginTop: 7,
|
|
1279
|
+
padding: "5px 8px",
|
|
1131
1280
|
boxSizing: "border-box",
|
|
1132
|
-
|
|
1281
|
+
background: "var(--dsw-alias-bg-base, #151517)",
|
|
1282
|
+
border: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))",
|
|
1133
1283
|
borderRadius: 6,
|
|
1134
|
-
|
|
1135
|
-
background: "var(--dsw-alias-bg-base, transparent)",
|
|
1136
|
-
color: "var(--dsw-alias-label-primary, inherit)"
|
|
1284
|
+
color: "var(--dsw-alias-label-primary, #f9fafb)"
|
|
1137
1285
|
},
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1286
|
+
/** `.ic-master input.is-dirty`:`border:2px solid var(--ic-attn)` + `padding:4px 7px`
|
|
1287
|
+
* + `box-shadow:0 0 0 3px <琥珀 22%>`;2px 描边与 5→4 的内边距对消,宽高不变
|
|
1288
|
+
* (审查「站得住」第 7 条)。 */
|
|
1289
|
+
inputDirty: {
|
|
1290
|
+
display: "block",
|
|
1291
|
+
width: "100%",
|
|
1292
|
+
marginTop: 7,
|
|
1293
|
+
padding: "4px 7px",
|
|
1294
|
+
boxSizing: "border-box",
|
|
1295
|
+
background: "var(--dsw-alias-bg-base, #151517)",
|
|
1296
|
+
border: "2px solid var(--dsw-alias-state-warn-primary, #e0a33e)",
|
|
1297
|
+
borderRadius: 6,
|
|
1298
|
+
color: "var(--dsw-alias-label-primary, #f9fafb)",
|
|
1299
|
+
boxShadow: "0 0 0 3px color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e0a33e) 22%, transparent)"
|
|
1142
1300
|
},
|
|
1301
|
+
/** 布尔档那枚方框(v3.1 的母版行只有文本框,这一格按输入框的同一套外边距对齐)。 */
|
|
1302
|
+
checkbox: { marginTop: 7 },
|
|
1303
|
+
/** `.ic-acts{display:flex;gap:5px;margin-top:7px;flex-wrap:wrap;align-items:center}`(母版行的按钮行)。 */
|
|
1143
1304
|
acts: {
|
|
1144
1305
|
display: "flex",
|
|
1145
|
-
gap:
|
|
1146
|
-
|
|
1147
|
-
|
|
1306
|
+
gap: 5,
|
|
1307
|
+
marginTop: 7,
|
|
1308
|
+
flexWrap: "wrap",
|
|
1309
|
+
alignItems: "center"
|
|
1148
1310
|
},
|
|
1311
|
+
/** `.ic-btn.is-mini{font-size:11px;padding:2px 7px;color:var(--ic-muted)}`——`btnPick` 是锁钉死的那一格,
|
|
1312
|
+
* 字号**不写**(#739 ④:该继承宿主字号),形状照 v3.1。 */
|
|
1149
1313
|
btnPick: {
|
|
1150
|
-
|
|
1151
|
-
padding: "4px 10px",
|
|
1314
|
+
padding: "2px 7px",
|
|
1152
1315
|
borderRadius: 6,
|
|
1153
|
-
border: "1px solid var(--dsw-alias-border, rgba(
|
|
1154
|
-
background: "
|
|
1155
|
-
color: "var(--dsw-alias-label-
|
|
1156
|
-
|
|
1157
|
-
|
|
1316
|
+
border: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))",
|
|
1317
|
+
background: "transparent",
|
|
1318
|
+
color: "var(--dsw-alias-label-secondary, #cfd3d6)",
|
|
1319
|
+
cursor: "pointer",
|
|
1320
|
+
whiteSpace: "nowrap"
|
|
1158
1321
|
},
|
|
1322
|
+
/** `.ic-invalid{margin-top:7px;padding:7px 9px;border-radius:6px;font-size:11.5px;line-height:1.5;
|
|
1323
|
+
* background:color-mix(in srgb, var(--ic-attn) 16%, transparent);border:1px solid var(--ic-attn)}`。 */
|
|
1159
1324
|
invalid: {
|
|
1160
|
-
marginTop:
|
|
1161
|
-
padding: "
|
|
1325
|
+
marginTop: 7,
|
|
1326
|
+
padding: "7px 9px",
|
|
1162
1327
|
borderRadius: 6,
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1328
|
+
fontSize: "0.92em",
|
|
1329
|
+
lineHeight: 1.5,
|
|
1330
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e0a33e) 16%, transparent)",
|
|
1331
|
+
border: "1px solid var(--dsw-alias-state-warn-primary, #e0a33e)",
|
|
1332
|
+
color: "var(--dsw-alias-label-primary, #f9fafb)"
|
|
1333
|
+
},
|
|
1334
|
+
/** `.ic-group{margin-top:14px}`(高级组默认收起)。 */
|
|
1335
|
+
advanced: { marginTop: 14 },
|
|
1336
|
+
/** `.ic-group > .ic-ghead{display:flex;align-items:center;gap:8px;cursor:pointer;list-style:none;
|
|
1337
|
+
* font-weight:600;padding:2px 0}`——字号**不写**(#739 ④:`summary` 该继承宿主字号)。 */
|
|
1338
|
+
summary: {
|
|
1339
|
+
display: "flex",
|
|
1340
|
+
alignItems: "center",
|
|
1341
|
+
gap: 8,
|
|
1342
|
+
cursor: "pointer",
|
|
1343
|
+
listStyle: "none",
|
|
1344
|
+
fontWeight: 600,
|
|
1345
|
+
padding: "2px 0"
|
|
1346
|
+
},
|
|
1347
|
+
/** `.ic-ghead::before{content:'›';color:var(--ic-muted)}`——画成真的那一个字符(内联样式给不出伪元素)。 */
|
|
1348
|
+
groupMark: { color: "var(--dsw-alias-label-secondary, #cfd3d6)" },
|
|
1349
|
+
/** `.ic-ghead .ic-gnote{color:var(--ic-faint);font-weight:400;font-size:11.5px}`(11.5px ⇒ `0.92em`)。 */
|
|
1350
|
+
groupNote: {
|
|
1351
|
+
color: "var(--dsw-alias-label-tertiary, #adb2b8)",
|
|
1352
|
+
fontWeight: 400,
|
|
1166
1353
|
fontSize: "0.92em"
|
|
1167
1354
|
},
|
|
1355
|
+
/** `.ic-group > .ic-gbody{margin-top:9px;padding:11px 12px;background:var(--ic-sunken);
|
|
1356
|
+
* border-radius:8px;box-shadow:inset 0 1px 0 rgba(255,255,255,.04)}`。 */
|
|
1357
|
+
groupBody: {
|
|
1358
|
+
marginTop: 9,
|
|
1359
|
+
padding: "11px 12px",
|
|
1360
|
+
background: "var(--dsw-alias-bg-layer-3, #353638)",
|
|
1361
|
+
border: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))",
|
|
1362
|
+
borderRadius: 8,
|
|
1363
|
+
boxShadow: "inset 0 1px 0 rgba(255,255,255,.04)"
|
|
1364
|
+
},
|
|
1365
|
+
/** `.ic-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:14px;padding-top:12px;
|
|
1366
|
+
* border-top:1px solid var(--ic-line)}` + **贴下沿吸住**(v3.1 的 `.ic-attnbar.is-sticky` 是它唯一的
|
|
1367
|
+
* 吸底口径:`position:sticky;bottom:6px;z-index:3`;本票按票面「贴下沿」取 0,好让底栏与下沿齐平)。 */
|
|
1168
1368
|
bar: {
|
|
1169
1369
|
display: "flex",
|
|
1170
1370
|
gap: 8,
|
|
1171
|
-
|
|
1371
|
+
alignItems: "center",
|
|
1172
1372
|
flexWrap: "wrap",
|
|
1373
|
+
marginTop: 14,
|
|
1374
|
+
paddingTop: 12,
|
|
1375
|
+
borderTop: "1px solid var(--dsw-alias-border-l1, rgba(255,255,255,.06))",
|
|
1173
1376
|
position: "sticky",
|
|
1174
1377
|
bottom: 0,
|
|
1175
|
-
zIndex:
|
|
1176
|
-
background: "var(--dsw-alias-bg-layer-1, #
|
|
1177
|
-
padding: "8px 0",
|
|
1178
|
-
borderTop: "1px solid var(--dsw-alias-border, rgba(128,128,128,.25))"
|
|
1179
|
-
},
|
|
1180
|
-
saveMsg: {
|
|
1181
|
-
marginRight: "auto",
|
|
1182
|
-
alignSelf: "center",
|
|
1183
|
-
color: "var(--dsw-alias-state-warning-primary, #b26a00)",
|
|
1184
|
-
fontSize: "0.9em"
|
|
1378
|
+
zIndex: 3,
|
|
1379
|
+
background: "var(--dsw-alias-bg-layer-1, #232324)"
|
|
1185
1380
|
},
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1381
|
+
/** `.ic-bar .ic-note{margin-left:auto;color:var(--ic-faint);font-size:11.5px}`(就绪态那半句)。 */
|
|
1382
|
+
barNote: {
|
|
1383
|
+
marginLeft: "auto",
|
|
1384
|
+
color: "var(--dsw-alias-label-tertiary, #adb2b8)",
|
|
1385
|
+
fontSize: "0.92em"
|
|
1191
1386
|
},
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1387
|
+
/** 有改动时底栏那半句(黄字,粘在按钮组左侧)。 */
|
|
1388
|
+
saveMsg: {
|
|
1389
|
+
marginLeft: "auto",
|
|
1390
|
+
color: "var(--dsw-alias-state-warn-primary, #e0a33e)",
|
|
1391
|
+
fontSize: "0.92em"
|
|
1196
1392
|
},
|
|
1393
|
+
/** `.ic-btn{font-size:12px;padding:4px 10px;border-radius:6px;border:1px solid var(--ic-line-strong);
|
|
1394
|
+
* background:transparent;color:var(--ic-text)}`——字号**不写**(#739 ④)。 */
|
|
1197
1395
|
btn: {
|
|
1198
|
-
padding: "4px
|
|
1396
|
+
padding: "4px 10px",
|
|
1199
1397
|
borderRadius: 6,
|
|
1200
|
-
border: "1px solid var(--dsw-alias-border, rgba(
|
|
1201
|
-
background: "
|
|
1202
|
-
color: "var(--dsw-alias-label-primary,
|
|
1203
|
-
cursor: "pointer"
|
|
1398
|
+
border: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12))",
|
|
1399
|
+
background: "transparent",
|
|
1400
|
+
color: "var(--dsw-alias-label-primary, #f9fafb)",
|
|
1401
|
+
cursor: "pointer",
|
|
1402
|
+
whiteSpace: "nowrap"
|
|
1204
1403
|
},
|
|
1404
|
+
/** `.ic-btn.is-primary{background:var(--ic-accent);border-color:var(--ic-accent);color:var(--ic-accent-ink);
|
|
1405
|
+
* font-weight:500}`——`--ic-accent` 取 `--dsw-alias-brand-primary`,前景取 `label-primary-foreground`。 */
|
|
1205
1406
|
btnPrimary: {
|
|
1206
|
-
padding: "4px
|
|
1407
|
+
padding: "4px 10px",
|
|
1207
1408
|
borderRadius: 6,
|
|
1208
|
-
border: "1px solid var(--dsw-alias-brand-primary, #
|
|
1209
|
-
background: "var(--dsw-alias-brand-primary, #
|
|
1210
|
-
color: "#
|
|
1211
|
-
|
|
1212
|
-
},
|
|
1213
|
-
advanced: { marginTop: 12 },
|
|
1214
|
-
summary: {
|
|
1409
|
+
border: "1px solid var(--dsw-alias-brand-primary, #f9fafb)",
|
|
1410
|
+
background: "var(--dsw-alias-brand-primary, #f9fafb)",
|
|
1411
|
+
color: "var(--dsw-alias-label-primary-foreground, #0f1115)",
|
|
1412
|
+
fontWeight: 500,
|
|
1215
1413
|
cursor: "pointer",
|
|
1216
|
-
|
|
1414
|
+
whiteSpace: "nowrap"
|
|
1217
1415
|
},
|
|
1416
|
+
/** 附加块(版本行/状态行)那份字面:`--ic-faint` + `0.92em`(#739 ③ 钉死这一格)。 */
|
|
1218
1417
|
version: {
|
|
1219
1418
|
marginTop: 8,
|
|
1220
1419
|
paddingTop: 8,
|
|
1221
|
-
borderTop: "1px dashed var(--dsw-alias-border, rgba(
|
|
1222
|
-
color: "var(--dsw-alias-label-tertiary, #
|
|
1420
|
+
borderTop: "1px dashed var(--dsw-alias-border-l1, rgba(255,255,255,.06))",
|
|
1421
|
+
color: "var(--dsw-alias-label-tertiary, #adb2b8)",
|
|
1223
1422
|
fontSize: "0.92em"
|
|
1423
|
+
},
|
|
1424
|
+
/** `.ic-badge{font-size:11px;line-height:1;padding:4px 9px;border-radius:999px;white-space:nowrap;
|
|
1425
|
+
* display:inline-flex;align-items:center;gap:6px;border:1px solid transparent}`(11px ⇒ `0.88em`)。 */
|
|
1426
|
+
badge: {
|
|
1427
|
+
fontSize: "0.88em",
|
|
1428
|
+
lineHeight: 1,
|
|
1429
|
+
padding: "4px 9px",
|
|
1430
|
+
borderRadius: 999,
|
|
1431
|
+
whiteSpace: "nowrap",
|
|
1432
|
+
display: "inline-flex",
|
|
1433
|
+
alignItems: "center",
|
|
1434
|
+
gap: 6,
|
|
1435
|
+
border: "1px solid transparent"
|
|
1436
|
+
},
|
|
1437
|
+
/** `.ic-badge .ic-bd{width:6px;height:6px;border-radius:50%;background:currentColor}`。 */
|
|
1438
|
+
badgeDot: {
|
|
1439
|
+
width: 6,
|
|
1440
|
+
height: 6,
|
|
1441
|
+
borderRadius: "50%",
|
|
1442
|
+
background: "currentColor"
|
|
1443
|
+
},
|
|
1444
|
+
/** `.ic-spin{width:11px;height:11px;border-radius:50%;border:1.5px solid rgba(0,0,0,.25);
|
|
1445
|
+
* border-top-color:currentColor}`——旋转那半要 `@keyframes`(内联样式给不出),故只画环。 */
|
|
1446
|
+
badgeSpinner: {
|
|
1447
|
+
width: 11,
|
|
1448
|
+
height: 11,
|
|
1449
|
+
borderRadius: "50%",
|
|
1450
|
+
border: "1.5px solid rgba(0,0,0,.25)",
|
|
1451
|
+
borderTopColor: "currentColor",
|
|
1452
|
+
display: "inline-block"
|
|
1453
|
+
},
|
|
1454
|
+
/** `.ic-badge.is-dirty`:`--ic-attn` 实底 + `--ic-attn-ink` 深字(那 3:1 是设计专门解过的对比度)。 */
|
|
1455
|
+
badgeDirty: {
|
|
1456
|
+
color: "#241703",
|
|
1457
|
+
background: "var(--dsw-alias-state-warn-primary, #e0a33e)",
|
|
1458
|
+
borderColor: "var(--dsw-alias-state-warn-primary, #e0a33e)",
|
|
1459
|
+
fontWeight: 600
|
|
1460
|
+
},
|
|
1461
|
+
/** `.ic-badge.is-busy`:与 dirty 同色(写的时候还在那一档上)。 */
|
|
1462
|
+
badgeBusy: {
|
|
1463
|
+
color: "#241703",
|
|
1464
|
+
background: "var(--dsw-alias-state-warn-primary, #e0a33e)",
|
|
1465
|
+
borderColor: "var(--dsw-alias-state-warn-primary, #e0a33e)",
|
|
1466
|
+
fontWeight: 600
|
|
1467
|
+
},
|
|
1468
|
+
/** `.ic-badge.is-ok{color:var(--ic-ok);border-color:var(--ic-ok);background:color-mix(in srgb, var(--ic-ok) 14%, transparent)}`。 */
|
|
1469
|
+
badgeOk: {
|
|
1470
|
+
color: "var(--dsw-alias-state-success-primary, #4ec9a0)",
|
|
1471
|
+
borderColor: "var(--dsw-alias-state-success-primary, #4ec9a0)",
|
|
1472
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-success-primary, #4ec9a0) 14%, transparent)"
|
|
1473
|
+
},
|
|
1474
|
+
/** `.ic-badge.is-bad{color:var(--ic-danger-ink);background:var(--ic-danger);border-color:var(--ic-danger)}`。 */
|
|
1475
|
+
badgeBad: {
|
|
1476
|
+
color: "#fff5f4",
|
|
1477
|
+
background: "var(--dsw-alias-state-error-primary, #e0685f)",
|
|
1478
|
+
borderColor: "var(--dsw-alias-state-error-primary, #e0685f)",
|
|
1479
|
+
fontWeight: 600
|
|
1224
1480
|
}
|
|
1225
1481
|
};
|
|
1226
1482
|
/** 复制那一枚按钮的字面(定稿 v3 第三条:就地变,不弹提示)。 */
|
|
@@ -1229,29 +1485,85 @@ window.__ModuleLoader__.load({
|
|
|
1229
1485
|
if (state === "failed") return "复制失败";
|
|
1230
1486
|
return "复制";
|
|
1231
1487
|
}
|
|
1232
|
-
/**
|
|
1488
|
+
/** 状态 → 徽标那一档(v3.1 `paint()` 里的先后次序:读取失败/写入中/写入失败/值非法/有改动/已保存/就绪)。 */
|
|
1489
|
+
function panelBadgeOf(facts) {
|
|
1490
|
+
if (facts.readFailed) return "readFailed";
|
|
1491
|
+
if (facts.busy) return "busy";
|
|
1492
|
+
if (facts.writeFailed) return "writeFailed";
|
|
1493
|
+
if (facts.invalid) return "invalid";
|
|
1494
|
+
if (facts.dirtyCount > 0) return "dirty";
|
|
1495
|
+
if (facts.saved) return "saved";
|
|
1496
|
+
return "ready";
|
|
1497
|
+
}
|
|
1498
|
+
/** 徽标那一档画的字(v3.1 `badgeHTML` 的字面,只把「已保存 · 刚刚」的演示时刻换成档名)。 */
|
|
1499
|
+
function badgeTextOf(kind, dirtyCount) {
|
|
1500
|
+
if (kind === "dirty") return `${dirtyCount} 项未保存`;
|
|
1501
|
+
if (kind === "busy") return "正在写入";
|
|
1502
|
+
if (kind === "saved") return "已保存";
|
|
1503
|
+
if (kind === "invalid") return "配置里有值用不了";
|
|
1504
|
+
if (kind === "writeFailed") return "写入失败";
|
|
1505
|
+
return "";
|
|
1506
|
+
}
|
|
1507
|
+
/** 徽标那一档的底色(v3.1 的四条 `.ic-badge.is-*`)。 */
|
|
1508
|
+
function badgeStyleOf(kind) {
|
|
1509
|
+
if (kind === "dirty" || kind === "invalid") return {
|
|
1510
|
+
...S.badge,
|
|
1511
|
+
...S.badgeDirty
|
|
1512
|
+
};
|
|
1513
|
+
if (kind === "busy") return {
|
|
1514
|
+
...S.badge,
|
|
1515
|
+
...S.badgeBusy
|
|
1516
|
+
};
|
|
1517
|
+
if (kind === "saved") return {
|
|
1518
|
+
...S.badge,
|
|
1519
|
+
...S.badgeOk
|
|
1520
|
+
};
|
|
1521
|
+
if (kind === "writeFailed") return {
|
|
1522
|
+
...S.badge,
|
|
1523
|
+
...S.badgeBad
|
|
1524
|
+
};
|
|
1525
|
+
return S.badge;
|
|
1526
|
+
}
|
|
1527
|
+
/** 画卡片标题行右端那枚状态徽标(就绪态与读取失败那一支不画)。 */
|
|
1528
|
+
function PanelBadgeView(props) {
|
|
1529
|
+
const text = badgeTextOf(props.kind, props.dirtyCount);
|
|
1530
|
+
if (text === "") return null;
|
|
1531
|
+
return react.createElement("span", { style: badgeStyleOf(props.kind) }, props.kind === "busy" ? react.createElement("span", { style: S.badgeSpinner }) : react.createElement("span", { style: S.badgeDot }), text);
|
|
1532
|
+
}
|
|
1533
|
+
/** 一行怎么画(照 v3.1 的两套行模板):
|
|
1534
|
+
*
|
|
1535
|
+
* **只读行**(`.ic-row`)=标签—值同行、值装不下整行让位、行尾一枚「复制」——**不画输入框**
|
|
1536
|
+
* (值来自技能侧解析,页面上本来就不给改),也不画那行说明(v3.1 的 `rowShell` 里没有它)。
|
|
1537
|
+
* **可改行**(`.ic-master`)=标签 +「可改」小徽标 + 说明 + 整行输入框 + 按钮行。
|
|
1233
1538
|
*
|
|
1234
1539
|
* 目录档=文本框 + 一枚唤起目录选择的按钮;入口三态是 `none`(命名空间拿不到、或这条路已被拒)
|
|
1235
1540
|
* **就不画按钮**,文本框照旧——那是该缝自己的契约(供不了就收起入口,不是失败)。
|
|
1236
|
-
*
|
|
1541
|
+
* 只读行:目录入口**一枚都不画**(定稿 v3 ①)。 */
|
|
1237
1542
|
function Row(props) {
|
|
1238
1543
|
const { item } = props;
|
|
1239
1544
|
const readonly = item.readonly === true;
|
|
1240
1545
|
const disabled = props.disabled || readonly;
|
|
1241
|
-
const
|
|
1546
|
+
const copy = react.createElement("button", {
|
|
1547
|
+
style: S.btnPick,
|
|
1548
|
+
type: "button",
|
|
1549
|
+
disabled: props.onCopy === void 0,
|
|
1550
|
+
onClick: () => props.onCopy?.(item.key, props.value)
|
|
1551
|
+
}, copyLabelOf(props.copyState));
|
|
1552
|
+
const follow = props.follow === true ? react.createElement("span", { style: S.follow }, react.createElement("span", { style: S.followMark }, "↳"), "将跟随更新") : null;
|
|
1553
|
+
if (readonly) {
|
|
1554
|
+
/** 跟随行落 `.ic-under` 那一档缩进;其余按是不是本组最后一行挑有没有下边线。 */
|
|
1555
|
+
const rowStyle = props.nested === true ? props.last === true ? S.followerRowLast : S.followerRow : props.last === true ? S.rowLast : S.row;
|
|
1556
|
+
return react.createElement("div", { style: rowStyle }, react.createElement("span", { style: S.rowKey }, item.title), react.createElement("span", { style: S.rowValue }, props.value), react.createElement("span", { style: S.rowActs }, follow, copy));
|
|
1557
|
+
}
|
|
1558
|
+
const onChange = (e) => props.onChange(item.key, e.target.value);
|
|
1242
1559
|
const control = item.control === "switch" ? react.createElement("input", {
|
|
1243
1560
|
type: "checkbox",
|
|
1561
|
+
style: S.checkbox,
|
|
1244
1562
|
checked: props.value === "true",
|
|
1245
1563
|
disabled,
|
|
1246
|
-
onChange:
|
|
1564
|
+
onChange: (e) => props.onChange(item.key, e.target.checked ? "true" : "false")
|
|
1247
1565
|
}) : react.createElement("input", {
|
|
1248
|
-
style: props.dirty === true ?
|
|
1249
|
-
...S.input,
|
|
1250
|
-
borderColor: "var(--dsw-alias-state-warning-primary, #b26a00)"
|
|
1251
|
-
} : readonly && props.follow === true ? {
|
|
1252
|
-
...S.input,
|
|
1253
|
-
opacity: .55
|
|
1254
|
-
} : S.input,
|
|
1566
|
+
style: props.dirty === true ? S.inputDirty : S.input,
|
|
1255
1567
|
type: item.control === "number" ? "number" : "text",
|
|
1256
1568
|
value: props.value,
|
|
1257
1569
|
disabled,
|
|
@@ -1260,21 +1572,13 @@ window.__ModuleLoader__.load({
|
|
|
1260
1572
|
});
|
|
1261
1573
|
/** 目录入口:只读行不画(定稿 v3 ①),供不了(`none`/缺席)也不画。 */
|
|
1262
1574
|
const entry = props.browser ?? null;
|
|
1263
|
-
const browse = item.control === "directory" &&
|
|
1575
|
+
const browse = item.control === "directory" && entry !== null && entry.mode !== "none" ? react.createElement("button", {
|
|
1264
1576
|
style: S.btnPick,
|
|
1265
1577
|
type: "button",
|
|
1266
1578
|
disabled,
|
|
1267
1579
|
onClick: () => entry.onOpen(item.key)
|
|
1268
1580
|
}, "浏览文件夹") : null;
|
|
1269
|
-
|
|
1270
|
-
style: S.btnPick,
|
|
1271
|
-
type: "button",
|
|
1272
|
-
disabled: props.onCopy === void 0,
|
|
1273
|
-
onClick: () => props.onCopy?.(item.key, props.value)
|
|
1274
|
-
}, copyLabelOf(props.copyState));
|
|
1275
|
-
const acts = react.createElement("div", { style: S.acts }, browse, copy);
|
|
1276
|
-
const facts = props.fallback ?? null;
|
|
1277
|
-
return react.createElement("div", { style: S.row }, react.createElement("div", { style: S.label }, item.title, props.dirty === true ? react.createElement("span", { style: S.dirtyDot }, "●已改动") : null), react.createElement("div", { style: S.hint }, item.hint), readonly && props.follow === true ? react.createElement("div", { style: S.followNote }, "将跟随更新,保存后生效。") : null, facts === null ? null : react.createElement("div", { style: S.invalid }, "配置里写的是 ", react.createElement("b", null, facts.written), ";现在生效的是 ", react.createElement("b", null, facts.effective), "。"), react.createElement("div", { style: S.pickRow }, control, acts));
|
|
1581
|
+
return react.createElement("div", { style: S.master }, react.createElement("div", { style: S.label }, item.title, react.createElement("span", { style: S.chip }, "可改")), react.createElement("div", { style: S.hint }, item.hint), control, react.createElement("div", { style: S.acts }, browse, copy), props.alert == null ? null : react.createElement("div", { style: S.invalid }, props.alert.message));
|
|
1278
1582
|
}
|
|
1279
1583
|
/** 应用内浏览器对话框的文案(面板自带,逐条对应图上那几个位置)。 */
|
|
1280
1584
|
const BROWSER_LABELS = {
|
|
@@ -1301,14 +1605,27 @@ window.__ModuleLoader__.load({
|
|
|
1301
1605
|
const surface = state.kind === "ready" ? state.surface : null;
|
|
1302
1606
|
const headText = title === void 0 ? "配置" : `${title} · 配置`;
|
|
1303
1607
|
const headDataDir = surface === null ? "" : surface.resolved?.["dbDir"] ?? surface.dataDir;
|
|
1304
|
-
const
|
|
1608
|
+
const ready = state.kind === "ready";
|
|
1609
|
+
const dirtyCount = ready ? props.dirtyKeys.length : 0;
|
|
1610
|
+
const badge = panelBadgeOf({
|
|
1611
|
+
readFailed: state.kind === "failed",
|
|
1612
|
+
busy: props.busy,
|
|
1613
|
+
writeFailed: props.writeError !== null,
|
|
1614
|
+
invalid: props.invalid === true,
|
|
1615
|
+
dirtyCount,
|
|
1616
|
+
saved: props.notice !== null
|
|
1617
|
+
});
|
|
1618
|
+
const head = react.createElement("div", { style: S.head }, react.createElement("div", { style: S.title }, headText), react.createElement(PanelBadgeView, {
|
|
1619
|
+
kind: badge,
|
|
1620
|
+
dirtyCount
|
|
1621
|
+
}));
|
|
1622
|
+
const fileLines = surface === null ? null : react.createElement("div", { style: S.fileWrap }, react.createElement("div", { style: S.info }, react.createElement("b", { style: S.fileLabel }, "配置文件"), surface.path), react.createElement("div", { style: S.info }, react.createElement("b", { style: S.fileLabel }, "数据目录"), headDataDir), surface.created ? react.createElement("div", { style: S.muted }, "(配置文件刚按默认值生成)") : null);
|
|
1305
1623
|
const parts = {
|
|
1306
1624
|
styles: S,
|
|
1307
1625
|
reply: surface
|
|
1308
1626
|
};
|
|
1309
1627
|
const extra = props.extra === void 0 ? null : props.extra(parts);
|
|
1310
|
-
if (state.kind === "
|
|
1311
|
-
if (state.kind === "failed") return react.createElement("div", { style: S.card }, head, react.createElement("div", { style: S.error }, state.message), extra, react.createElement("div", { style: S.bar }, react.createElement("button", {
|
|
1628
|
+
if (state.kind === "failed") return react.createElement("div", { style: S.card }, head, fileLines, react.createElement("div", { style: S.error }, state.message), extra, react.createElement("div", { style: S.bar }, react.createElement("button", {
|
|
1312
1629
|
style: S.btn,
|
|
1313
1630
|
type: "button",
|
|
1314
1631
|
onClick: props.onRetry
|
|
@@ -1319,37 +1636,49 @@ window.__ModuleLoader__.load({
|
|
|
1319
1636
|
}, "重置为默认")));
|
|
1320
1637
|
const common = props.items.filter((item) => item.tier === "common");
|
|
1321
1638
|
const advanced = props.items.filter((item) => item.tier === "advanced");
|
|
1322
|
-
|
|
1639
|
+
/** 还没读到整面(`loading`)时**框架照画**:行表与控件形状是客户端常量,不必等任何回执。
|
|
1640
|
+
* 值先空着、控件与三枚底栏键一律不可用;回执到了由 `ready` 那一支把值填上(见 `config-panel.ts`)。 */
|
|
1641
|
+
const frozen = props.busy || !ready;
|
|
1642
|
+
const followers = props.followerKeys ?? [];
|
|
1643
|
+
const renderRow = (item, last) => react.createElement(Row, {
|
|
1323
1644
|
key: item.key,
|
|
1324
1645
|
item,
|
|
1325
1646
|
value: props.draft[item.key] ?? "",
|
|
1326
|
-
disabled:
|
|
1647
|
+
disabled: frozen,
|
|
1327
1648
|
onChange: props.onChange,
|
|
1328
1649
|
browser: props.rowEntry,
|
|
1329
|
-
dirty: props.dirtyKeys.includes(item.key),
|
|
1330
|
-
follow: props.followKeys.includes(item.key),
|
|
1331
|
-
onCopy: props.onCopy,
|
|
1650
|
+
dirty: ready && props.dirtyKeys.includes(item.key),
|
|
1651
|
+
follow: ready && props.followKeys.includes(item.key),
|
|
1652
|
+
onCopy: ready ? props.onCopy : void 0,
|
|
1332
1653
|
copyState: props.copy !== null && props.copy.key === item.key ? props.copy.ok ? "done" : "failed" : "idle",
|
|
1333
|
-
|
|
1654
|
+
alert: ready ? surface?.alerts?.[item.key] ?? null : null,
|
|
1655
|
+
last,
|
|
1656
|
+
nested: followers.includes(item.key)
|
|
1334
1657
|
});
|
|
1335
|
-
|
|
1336
|
-
const
|
|
1337
|
-
|
|
1658
|
+
/** 每组最后那一行才免掉下边线(`.ic-row:last-child`);母版行自己不带线,不必管。 */
|
|
1659
|
+
const lastIndexOf = (list) => {
|
|
1660
|
+
for (let i = list.length - 1; i >= 0; i -= 1) if (list[i].readonly === true) return i;
|
|
1661
|
+
return -1;
|
|
1662
|
+
};
|
|
1663
|
+
const commonLast = lastIndexOf(common);
|
|
1664
|
+
const advancedLast = lastIndexOf(advanced);
|
|
1665
|
+
const dirty = ready && dirtyCount > 0;
|
|
1666
|
+
return react.createElement("div", { style: S.card }, head, fileLines, ready ? null : react.createElement("div", { style: S.muted }, "配置读取中"), react.createElement("div", { style: S.rows }, common.map((item, index) => renderRow(item, index === commonLast))), advanced.length === 0 ? null : react.createElement("details", { style: S.advanced }, react.createElement("summary", { style: S.summary }, react.createElement("span", { style: S.groupMark }, "›"), ADVANCED_GROUP_TITLE, react.createElement("span", { style: S.groupNote }, ADVANCED_GROUP_NOTE)), react.createElement("div", { style: S.groupBody }, advanced.map((item, index) => renderRow(item, index === advancedLast)))), extra, react.createElement("div", { style: S.bar }, react.createElement("button", {
|
|
1338
1667
|
style: dirty ? S.btnPrimary : S.btn,
|
|
1339
1668
|
type: "button",
|
|
1340
|
-
disabled:
|
|
1669
|
+
disabled: frozen || !dirty,
|
|
1341
1670
|
onClick: props.onSave
|
|
1342
1671
|
}, props.busy ? "处理中" : dirty ? `保存(${dirtyCount} 项未保存)` : "保存"), react.createElement("button", {
|
|
1343
1672
|
style: S.btn,
|
|
1344
1673
|
type: "button",
|
|
1345
|
-
disabled:
|
|
1674
|
+
disabled: frozen,
|
|
1346
1675
|
onClick: props.onReset
|
|
1347
1676
|
}, "重置为默认"), react.createElement("button", {
|
|
1348
1677
|
style: S.btn,
|
|
1349
1678
|
type: "button",
|
|
1350
|
-
disabled:
|
|
1679
|
+
disabled: frozen,
|
|
1351
1680
|
onClick: props.onRetry
|
|
1352
|
-
}, "重新读取")), props.picking ? react.createElement("div", { style: S.muted }, "已唤起系统文件夹对话框:选中后自动填上,取消则不动。") : null, props.browseRow !== null ? react.createElement(DirectoryBrowserFromRow, {
|
|
1681
|
+
}, "重新读取"), dirty ? react.createElement("div", { style: S.saveMsg }, `浏览改动后请点保存(${dirtyCount} 项未保存),保存后跟随项自动更新`) : ready ? react.createElement("div", { style: S.barNote }, "没有未保存的改动") : null), props.picking ? react.createElement("div", { style: S.muted }, "已唤起系统文件夹对话框:选中后自动填上,取消则不动。") : null, props.browseRow !== null ? react.createElement(DirectoryBrowserFromRow, {
|
|
1353
1682
|
open: true,
|
|
1354
1683
|
row: props.browseRow,
|
|
1355
1684
|
labels: BROWSER_LABELS
|
|
@@ -1358,15 +1687,6 @@ window.__ModuleLoader__.load({
|
|
|
1358
1687
|
role: "alert"
|
|
1359
1688
|
}, props.writeError) : null, props.error !== null ? react.createElement("div", { style: S.error }, props.error) : null, props.notice !== null ? react.createElement("div", { style: S.okText }, props.notice) : null);
|
|
1360
1689
|
}
|
|
1361
|
-
/** 「写的值 ≠ 生效值」两个事实要的那几格:整面回执里取值本身、落点顶层那一个、`resolved` 组。 */
|
|
1362
|
-
function draftSourceOf(surface) {
|
|
1363
|
-
if (surface === null) return { values: {} };
|
|
1364
|
-
return {
|
|
1365
|
-
values: surface.values,
|
|
1366
|
-
dataDir: surface.dataDir,
|
|
1367
|
-
resolved: surface.resolved
|
|
1368
|
-
};
|
|
1369
|
-
}
|
|
1370
1690
|
//#endregion
|
|
1371
1691
|
//#region ../plugin-manager/dist/directory-browser-roots.js
|
|
1372
1692
|
/** 「本机根清单」的取数接线(客户端半):把一通电话变成图上那一行可跳转的根。
|
|
@@ -1504,6 +1824,18 @@ window.__ModuleLoader__.load({
|
|
|
1504
1824
|
return false;
|
|
1505
1825
|
}
|
|
1506
1826
|
}
|
|
1827
|
+
/** **静态**跟随行名单:给行表里每个键喂一遍六家交的钩子,收它返回的跟随者。
|
|
1828
|
+
*
|
|
1829
|
+
* 为什么能这么探:`followKeysOf` 是**纯函数**,六家的实现都只是「脏键里有没有这一格」那一下判断
|
|
1830
|
+
* (各家 `src/client.ts`),喂单键即得「谁跟着它」——**与当刻脏不脏无关**。有了这个集合,共用件就能
|
|
1831
|
+
* 照 v3.1 的 `.ic-under` 把跟随行相对它的可改行缩进(#920 补),**六家源码一个字节不用改**。
|
|
1832
|
+
* 拿不到钩子时回空表(一行都不缩进,照旧平铺)。 */
|
|
1833
|
+
function followerKeysOf(items, hook) {
|
|
1834
|
+
if (hook === void 0) return [];
|
|
1835
|
+
const out = /* @__PURE__ */ new Set();
|
|
1836
|
+
for (const item of items) for (const key of hook([item.key])) out.add(key);
|
|
1837
|
+
return [...out];
|
|
1838
|
+
}
|
|
1507
1839
|
/** 技能设置页:承载一家自己的全部可配置项(只配置,不干活)。 */
|
|
1508
1840
|
function ConfigPanel(props) {
|
|
1509
1841
|
const [state, setState] = react.useState({ kind: "loading" });
|
|
@@ -1662,6 +1994,10 @@ window.__ModuleLoader__.load({
|
|
|
1662
1994
|
const baseline = surface !== null ? toDraft(props.items, surface.values, surface) : null;
|
|
1663
1995
|
const dirtyKeys = baseline === null ? [] : dirtyKeysOf(props.items, draft, baseline);
|
|
1664
1996
|
const followKeys = (props.followKeysOf ?? noFollowKeys)(dirtyKeys);
|
|
1997
|
+
/** 技能侧报出「有格子真用不了」⇒ 标题行那枚「配置里有值用不了」徽标才亮(#915 第二步)。 */
|
|
1998
|
+
const hasAlert = surface !== null && surface.alerts !== void 0 && Object.keys(surface.alerts).length > 0;
|
|
1999
|
+
/** 静态那一份(与脏不脏无关):画跟随行的缩进用(v3.1 的 `.ic-under`)。 */
|
|
2000
|
+
const followerKeys = followerKeysOf(props.items, props.followKeysOf);
|
|
1665
2001
|
const mode = pickerModeOf(picker);
|
|
1666
2002
|
const rowEntry = mode === "none" ? null : {
|
|
1667
2003
|
mode,
|
|
@@ -1681,6 +2017,8 @@ window.__ModuleLoader__.load({
|
|
|
1681
2017
|
rowEntry,
|
|
1682
2018
|
dirtyKeys,
|
|
1683
2019
|
followKeys,
|
|
2020
|
+
followerKeys,
|
|
2021
|
+
invalid: hasAlert,
|
|
1684
2022
|
copy,
|
|
1685
2023
|
onCopy,
|
|
1686
2024
|
onChange,
|