dsh-calorie 0.3.40 → 0.3.43
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 +530 -223
- package/dist/client.js.map +1 -1
- package/package.json +2 -2
package/dist/client.js
CHANGED
|
@@ -1208,141 +1208,358 @@ window.__ModuleLoader__.load({
|
|
|
1208
1208
|
}, react.createElement("span", { style: S$2.product }, props.product), react.createElement("span", { style: S$2.purpose }, props.purpose));
|
|
1209
1209
|
}
|
|
1210
1210
|
//#endregion
|
|
1211
|
-
//#region ../plugin-manager/src/seal-
|
|
1212
|
-
/**
|
|
1213
|
-
|
|
1214
|
-
* 来源:原型定稿 2026-10-05(记录见 `docs/plugins/plugin-manager/印章原型-定稿.md` 第九节)。
|
|
1215
|
-
* 点印后弹出的那块:**外框逐档照抄对应那枚印的材质**(底色+边线都照抄,不许简化成"一圈金属色"),
|
|
1216
|
-
* **中间是纯纸**,内容固定三段——进展/状态/计划。
|
|
1217
|
-
*
|
|
1218
|
-
* 三条形状(逐值照原型锁死):
|
|
1219
|
-
* 外框:厚 1.125em(16px 下=18px,这是下限;只给 10px 整张卡会读成"白底加一圈边"),
|
|
1220
|
-
* 材质随 `role`×`tier` 九档之一,挂 `roughFrame` 糙边滤镜。
|
|
1221
|
-
* 纸:`bg-layer-2` 底+`border-l1` 描边,压在材质上(外框那层在纸下面,只露一圈)。
|
|
1222
|
-
* 字:标题居中 1em/字距 .16em;档位字 .66em/字距 .42em;三段标签 .66em/字距 .34em;
|
|
1223
|
-
* 正文 .84em/行高 1.9;段间 1px 细线+.94em 上距。
|
|
1224
|
-
*
|
|
1225
|
-
* 颜色分治(照 `title-seal.ts` 的先例):
|
|
1226
|
-
* 印那一份(外框材质、档位字色)**固定色**——纸变印不变,印不管纸面派生;
|
|
1227
|
-
* 纸那一份(纸底、纸边、标题、正文、段间线)走 DSH 主题别名。
|
|
1228
|
-
*
|
|
1229
|
-
* 糙边滤镜两条分开:印是小块,`scale 2.2` 就够;外框有几百像素宽,同一条会被稀释到看不出,
|
|
1230
|
-
* 故外框单用 `scale 5` 的 `roughFrame`。滤镜只挂外框那层,纸不挂(否则纸的直边也会被抖)。
|
|
1231
|
-
*
|
|
1232
|
-
* 本件是纯函数:吃 props 回元素树,不留状态、不取数。手写 `React.createElement`(本包 client 束禁 JSX)。
|
|
1233
|
-
*/
|
|
1234
|
-
/** 糙边滤镜 id(文档作用域,故每页只许挂一次 `SealFilterDefs`)。 */
|
|
1235
|
-
const ROUGH_EDGE = "dshLifeSealRoughEdge";
|
|
1236
|
-
const ROUGH_FRAME = "dshLifeSealRoughFrame";
|
|
1237
|
-
/** 档位话:标题下那一行不再印档位字,直接说档位所处的阶段(渲染期用,顶层可放行)。 */
|
|
1238
|
-
const TIER_TEXT = {
|
|
1239
|
-
gold: "精雕细琢中",
|
|
1240
|
-
silver: "全打通中",
|
|
1241
|
-
copper: "基础建设中"
|
|
1242
|
-
};
|
|
1243
|
-
/** 纸面四色是印自带的(羊皮纸底+ availability 墨色),不跟宿主主题跑——
|
|
1244
|
-
* 深色下宿主变量会把纸染黑,故这里写死定稿值;字族仍继承(定稿零字族)。 */
|
|
1245
|
-
const PAPER_BG = "#fdfaf2";
|
|
1246
|
-
const LABEL_SUB = "#4b4335";
|
|
1247
|
-
/** 九档材质(3 角色 × 3 档)。键就是原型里的 `role`/`tier`。 */
|
|
1248
|
-
const MATERIALS = {
|
|
1211
|
+
//#region ../plugin-manager/src/seal-palette.ts
|
|
1212
|
+
/** 九档调色板(3 角色 × 3 档)。键就是契约里的 `role`/`tier`。 */
|
|
1213
|
+
const SEAL_PALETTE = {
|
|
1249
1214
|
skill: {
|
|
1250
1215
|
copper: {
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
border: "3px solid #a5652f",
|
|
1254
|
-
boxShadow: "inset 0 0 0 1px #f8f1e2aa, inset 0 2px 0 #ffffff26, inset 0 -3px 6px #0000004d"
|
|
1255
|
-
},
|
|
1216
|
+
background: "linear-gradient(180deg,#8a4a28,#6e3418)",
|
|
1217
|
+
border: "3px solid #a5652f",
|
|
1256
1218
|
accent: "#9a5f2a",
|
|
1257
1219
|
ruler: "#c08145b3"
|
|
1258
1220
|
},
|
|
1259
1221
|
silver: {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
border: "3px double #eef3f6",
|
|
1263
|
-
boxShadow: "inset 0 0 0 1px #fff, inset 0 2px 0 #ffffff2e, inset 0 -3px 6px #0000004d"
|
|
1264
|
-
},
|
|
1222
|
+
background: "linear-gradient(180deg,#b73124,#8e1f14)",
|
|
1223
|
+
border: "3px double #eef3f6",
|
|
1265
1224
|
accent: "#6f767c",
|
|
1266
1225
|
ruler: "#9aa0a6b3"
|
|
1267
1226
|
},
|
|
1268
1227
|
gold: {
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
border: "3px double #f5d97a",
|
|
1272
|
-
boxShadow: "inset 0 0 0 1px #fff8, inset 0 2px 0 #ffffff33, inset 0 -3px 6px #0000004d"
|
|
1273
|
-
},
|
|
1228
|
+
background: "linear-gradient(180deg,#d34a35,#a32216)",
|
|
1229
|
+
border: "3px double #f5d97a",
|
|
1274
1230
|
accent: "#9c7a16",
|
|
1275
1231
|
ruler: "#d8b338b3"
|
|
1276
1232
|
}
|
|
1277
1233
|
},
|
|
1278
1234
|
help: {
|
|
1279
1235
|
copper: {
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
border: "3px solid #a5652f",
|
|
1283
|
-
boxShadow: "inset 0 2px 0 #ffffff26, inset 0 -3px 6px #0000004d"
|
|
1284
|
-
},
|
|
1236
|
+
background: "linear-gradient(180deg,#b06a3a,#7e3f1d)",
|
|
1237
|
+
border: "3px solid #a5652f",
|
|
1285
1238
|
accent: "#9a5f2a",
|
|
1286
1239
|
ruler: "#c08145b3"
|
|
1287
1240
|
},
|
|
1288
1241
|
silver: {
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
border: "3px double #e8eef2",
|
|
1292
|
-
boxShadow: "inset 0 2px 0 #ffffff2e, inset 0 -3px 6px #0000004d"
|
|
1293
|
-
},
|
|
1242
|
+
background: "linear-gradient(180deg,#8f979e,#5f666d)",
|
|
1243
|
+
border: "3px double #e8eef2",
|
|
1294
1244
|
accent: "#6f767c",
|
|
1295
1245
|
ruler: "#9aa0a6b3"
|
|
1296
1246
|
},
|
|
1297
1247
|
gold: {
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
border: "3px double #f5d97a",
|
|
1301
|
-
boxShadow: "inset 0 2px 0 #ffffff33, inset 0 -3px 6px #0000004d"
|
|
1302
|
-
},
|
|
1248
|
+
background: "linear-gradient(180deg,#c63d2a,#a32216)",
|
|
1249
|
+
border: "3px double #f5d97a",
|
|
1303
1250
|
accent: "#9c7a16",
|
|
1304
1251
|
ruler: "#d8b338b3"
|
|
1305
1252
|
}
|
|
1306
1253
|
},
|
|
1307
1254
|
plugin: {
|
|
1308
1255
|
copper: {
|
|
1309
|
-
|
|
1310
|
-
background: "linear-gradient(180deg,#b4773c,#7e3f1d 60%,#66300f)",
|
|
1311
|
-
boxShadow: "inset 0 1px 0 #d99a5e88, inset 0 -2px 5px #0006, 0 0 0 1px #5e2c12, inset 0 2px 0 #ffffff26"
|
|
1312
|
-
},
|
|
1256
|
+
background: "linear-gradient(180deg,#b4773c,#7e3f1d 60%,#66300f)",
|
|
1313
1257
|
accent: "#9a5f2a",
|
|
1314
|
-
ruler: "#c08145b3"
|
|
1315
|
-
rivet: {
|
|
1316
|
-
background: "radial-gradient(circle at 34% 28%,#f0c088,#a5652f 55%,#6a3216)",
|
|
1317
|
-
boxShadow: "inset 0 -1px 1px #00000055,0 1px 2px #00000088"
|
|
1318
|
-
}
|
|
1258
|
+
ruler: "#c08145b3"
|
|
1319
1259
|
},
|
|
1320
1260
|
silver: {
|
|
1321
|
-
|
|
1322
|
-
background: "linear-gradient(180deg,#d8dee3,#a7aeb4 55%,#818990)",
|
|
1323
|
-
boxShadow: "inset 0 1px 0 #ffffffdd, inset 0 -2px 5px #0005, 0 0 0 1px #6f767c, inset 0 2px 0 #ffffff33"
|
|
1324
|
-
},
|
|
1261
|
+
background: "linear-gradient(180deg,#d8dee3,#a7aeb4 55%,#818990)",
|
|
1325
1262
|
accent: "#6f767c",
|
|
1326
|
-
ruler: "#9aa0a6b3"
|
|
1327
|
-
rivet: {
|
|
1328
|
-
background: "radial-gradient(circle at 34% 28%,#ffffff,#c3cad0 50%,#7d848b)",
|
|
1329
|
-
boxShadow: "inset 0 -1px 1px #00000044,0 1px 2px #00000077"
|
|
1330
|
-
}
|
|
1263
|
+
ruler: "#9aa0a6b3"
|
|
1331
1264
|
},
|
|
1332
1265
|
gold: {
|
|
1333
|
-
|
|
1334
|
-
background: "linear-gradient(180deg,#f2dc86,#c9a227 55%,#9c7d16)",
|
|
1335
|
-
boxShadow: "inset 0 1px 0 #fff8d8dd, inset 0 -2px 5px #0005, 0 0 0 1px #8a6a15"
|
|
1336
|
-
},
|
|
1266
|
+
background: "linear-gradient(180deg,#f2dc86,#c9a227 55%,#9c7d16)",
|
|
1337
1267
|
accent: "#9c7a16",
|
|
1338
|
-
ruler: "#d8b338b3"
|
|
1339
|
-
rivet: {
|
|
1340
|
-
background: "radial-gradient(circle at 34% 28%,#fffbe0,#e3c565 50%,#a5841d)",
|
|
1341
|
-
boxShadow: "inset 0 -1px 1px #00000044,0 1px 2px #00000077,0 0 6px #c9a22788"
|
|
1342
|
-
}
|
|
1268
|
+
ruler: "#d8b338b3"
|
|
1343
1269
|
}
|
|
1344
1270
|
}
|
|
1345
1271
|
};
|
|
1272
|
+
/** 插件档那两颗铆钉(**两处共用的小件**:印面两颗、卷轴外框四角,必须同形,故住这一层)。 */
|
|
1273
|
+
const SEAL_RIVET = {
|
|
1274
|
+
copper: {
|
|
1275
|
+
background: "radial-gradient(circle at 34% 28%,#f0c088,#a5652f 55%,#6a3216)",
|
|
1276
|
+
boxShadow: "inset 0 -1px 1px #00000055,0 1px 2px #00000088"
|
|
1277
|
+
},
|
|
1278
|
+
silver: {
|
|
1279
|
+
background: "radial-gradient(circle at 34% 28%,#ffffff,#c3cad0 50%,#7d848b)",
|
|
1280
|
+
boxShadow: "inset 0 -1px 1px #00000044,0 1px 2px #00000077"
|
|
1281
|
+
},
|
|
1282
|
+
gold: {
|
|
1283
|
+
background: "radial-gradient(circle at 34% 28%,#fffbe0,#e3c565 50%,#a5841d)",
|
|
1284
|
+
boxShadow: "inset 0 -1px 1px #00000044,0 1px 2px #00000077,0 0 6px #c9a22788"
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
/** 档位金属边(铜/银/金):绦带(关闭钮)只借这一条,主体恒朱砂。 */
|
|
1288
|
+
const TIER_EDGE = {
|
|
1289
|
+
gold: "#f5d97a",
|
|
1290
|
+
silver: "#d8dee3",
|
|
1291
|
+
copper: "#a5652f"
|
|
1292
|
+
};
|
|
1293
|
+
/** 档位语三档金属(鎏金/冷银/暖铜):主色+渐变(`backgroundClip:text` 描出金属光泽)。 */
|
|
1294
|
+
const TIER_METAL = {
|
|
1295
|
+
gold: {
|
|
1296
|
+
flat: "#b8912a",
|
|
1297
|
+
gradient: "linear-gradient(180deg,#f6e27a 0%,#d9b53c 45%,#a5811b 100%)"
|
|
1298
|
+
},
|
|
1299
|
+
silver: {
|
|
1300
|
+
flat: "#8b959d",
|
|
1301
|
+
gradient: "linear-gradient(180deg,#f7fafc 0%,#c3ccd3 45%,#8b959d 100%)"
|
|
1302
|
+
},
|
|
1303
|
+
copper: {
|
|
1304
|
+
flat: "#a8663a",
|
|
1305
|
+
gradient: "linear-gradient(180deg,#f0bd8e 0%,#c07b45 45%,#8a4a24 100%)"
|
|
1306
|
+
}
|
|
1307
|
+
};
|
|
1308
|
+
//#endregion
|
|
1309
|
+
//#region ../plugin-manager/src/seal-paper-schemes.ts
|
|
1310
|
+
/** 卷轴纸面字排 + 关闭钮样子 · **定稿件**(2026-10-06 定档,原型选型期结束)。
|
|
1311
|
+
*
|
|
1312
|
+
* 定稿形状(逐值锁死,改动须同步 `test/seal-scroll.test.mjs` 与 `test/seal-bundle-1158.test.mjs`):
|
|
1313
|
+
* 纸面:三段紧凑行内(进展/状态/计划),行首一枚朱砂小方印(进/状/计),标签与正文同行基线对齐;
|
|
1314
|
+
* 末行吃回 0.5em 行距(不然底比顶空一截);饰线上距加宽到 `0.5em 0 0.75em`;纸底与头部沿用原样式。
|
|
1315
|
+
* 档位语:该档金属渐变压进字里(金=鎏金/银=冷银/铜=暖铜),不再一律金。
|
|
1316
|
+
* 关闭钮:右上角斜披的朱砂菱形绦带(正立「收卷」字独立成层),两条飘尾沿菱形两条下边各向底尖收,
|
|
1317
|
+
* 各自朝外 26°(左尾朝左下、右尾朝右下);尾是两层——外层过糙边出毛边,内层纯色芯不受滤镜。
|
|
1318
|
+
*
|
|
1319
|
+
* 为什么单独一件:`seal-scroll.ts` 已接近告警线(外框/纸/浮层/滤镜都住那里),纸面字排与关闭钮住这里。
|
|
1320
|
+
* 本件是纯函数:吃 ctx 回五件,不留状态、不取数。原型期的二十余档方案已按「定稿后只留胜出那一档」删除。
|
|
1321
|
+
*/
|
|
1322
|
+
/** 材质底层:**糙边滤镜只挂这一层**——文字住在它外面,所以字是清晰的(照插件章「金属带+印文」的老做法)。 */
|
|
1323
|
+
function bgLayer(style, children) {
|
|
1324
|
+
return react.createElement("span", {
|
|
1325
|
+
"aria-hidden": true,
|
|
1326
|
+
style: {
|
|
1327
|
+
position: "absolute",
|
|
1328
|
+
inset: 0,
|
|
1329
|
+
...style
|
|
1330
|
+
}
|
|
1331
|
+
}, children ?? null);
|
|
1332
|
+
}
|
|
1333
|
+
/** 清晰字面:压在材质层之上,不参与任何滤镜。 */
|
|
1334
|
+
function crisp(text, style) {
|
|
1335
|
+
return react.createElement("span", { style: {
|
|
1336
|
+
position: "relative",
|
|
1337
|
+
zIndex: 1,
|
|
1338
|
+
...style
|
|
1339
|
+
} }, text);
|
|
1340
|
+
}
|
|
1341
|
+
/** 档位级两个色(金属边、档位语金属渐变)从九档总表引,本件不再各写一份。 */
|
|
1342
|
+
const edgeOf = (tierKey) => TIER_EDGE[tierKey] ?? TIER_EDGE.gold;
|
|
1343
|
+
const metalOf = (tierKey) => TIER_METAL[tierKey] ?? TIER_METAL.gold;
|
|
1344
|
+
const KAI = "\"Kaiti SC\",\"KaiTi\",\"STKaiti\",serif";
|
|
1345
|
+
/** 关闭钮(定稿:绦带·飘尾)。右上角一枚斜披 45° 的朱砂菱形,两条飘尾分挂两条下边,尾先画、带后画压住尾根。 */
|
|
1346
|
+
function closeLook(tierKey) {
|
|
1347
|
+
const crimson = "linear-gradient(135deg,#c63d2a 8%,#a32216 50%,#7e1a10)";
|
|
1348
|
+
/** 档位金属边(铜/银/金):绦带**只借这一条**——主体恒朱砂,边随档走,跟章「红底+金属边」同构。 */
|
|
1349
|
+
const edge = edgeOf(tierKey);
|
|
1350
|
+
const ribbon = bgLayer({
|
|
1351
|
+
borderRadius: "0.375em",
|
|
1352
|
+
background: crimson,
|
|
1353
|
+
boxShadow: "0 0 0 1px #5c110a59, 0 2px 8px #3d241066, inset 0 0 0 2px " + edge,
|
|
1354
|
+
transform: "rotate(45deg)",
|
|
1355
|
+
filter: "url(#" + SEAL_ROUGH_METAL_ID + ")"
|
|
1356
|
+
}, null);
|
|
1357
|
+
/** 尾色=绦带上亮一档的朱砂(悬在纸上也不发灰、不发黑):两条尾同色。 */
|
|
1358
|
+
const TAIL_FILL = "linear-gradient(180deg,#e8573c 0%,#c33a24 55%,#a52612 100%)";
|
|
1359
|
+
/** 飘尾:屏幕坐标(不随菱形旋转)——左尾往左下、右尾往右下(CSS 正角=尾梢往左)。
|
|
1360
|
+
* 两层:外层过糙边滤镜出毛边,内层纯色芯不受滤镜——尾悬在纸上还是悬在暗底上都是同一色。 */
|
|
1361
|
+
const loose = (key, left, deg, len, top) => react.createElement("span", {
|
|
1362
|
+
key,
|
|
1363
|
+
"aria-hidden": true,
|
|
1364
|
+
style: {
|
|
1365
|
+
position: "absolute",
|
|
1366
|
+
left,
|
|
1367
|
+
top,
|
|
1368
|
+
width: "0.4375em",
|
|
1369
|
+
height: len,
|
|
1370
|
+
transformOrigin: "top center",
|
|
1371
|
+
transform: "rotate(" + deg + "deg)"
|
|
1372
|
+
}
|
|
1373
|
+
}, bgLayer({
|
|
1374
|
+
borderRadius: "0 0 0.1875em 0.1875em",
|
|
1375
|
+
background: TAIL_FILL,
|
|
1376
|
+
boxShadow: "0 0 0 1.5px " + edge + "cc, 0 1px 3px #00000055",
|
|
1377
|
+
filter: "url(#" + SEAL_ROUGH_METAL_ID + ")"
|
|
1378
|
+
}), react.createElement("span", {
|
|
1379
|
+
"aria-hidden": true,
|
|
1380
|
+
style: {
|
|
1381
|
+
position: "absolute",
|
|
1382
|
+
inset: "0.5px",
|
|
1383
|
+
borderRadius: "0 0 0.15625em 0.15625em",
|
|
1384
|
+
background: TAIL_FILL
|
|
1385
|
+
}
|
|
1386
|
+
}));
|
|
1387
|
+
return {
|
|
1388
|
+
label: crisp("收卷", {
|
|
1389
|
+
fontFamily: KAI,
|
|
1390
|
+
fontSize: "0.8125em",
|
|
1391
|
+
letterSpacing: "0.22em",
|
|
1392
|
+
textIndent: "0.22em",
|
|
1393
|
+
color: "#f8f1e2",
|
|
1394
|
+
textShadow: "0 1px 2px #000000aa"
|
|
1395
|
+
}),
|
|
1396
|
+
decor: react.createElement(react.Fragment, null, loose("tL", "calc(50% - 0.8125em)", 26, "2em", "2.8125em"), loose("tR", "calc(50% + 0.4375em)", -26, "2em", "2.8125em"), ribbon),
|
|
1397
|
+
style: {
|
|
1398
|
+
boxSizing: "border-box",
|
|
1399
|
+
padding: 0,
|
|
1400
|
+
position: "absolute",
|
|
1401
|
+
right: "-1.5em",
|
|
1402
|
+
top: "-1.5em",
|
|
1403
|
+
width: "3.25em",
|
|
1404
|
+
height: "3.25em",
|
|
1405
|
+
display: "flex",
|
|
1406
|
+
alignItems: "center",
|
|
1407
|
+
justifyContent: "center",
|
|
1408
|
+
cursor: "pointer",
|
|
1409
|
+
border: "none",
|
|
1410
|
+
background: "transparent"
|
|
1411
|
+
}
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
/** 画一条饰线(方向 90/270 决定渐变朝向;定稿的段上距是 0.5em 0 0.75em)。 */
|
|
1415
|
+
function ornament(st, ruler) {
|
|
1416
|
+
const line = (key, dir) => react.createElement("span", {
|
|
1417
|
+
key,
|
|
1418
|
+
style: {
|
|
1419
|
+
...st.ornLine,
|
|
1420
|
+
background: "linear-gradient(" + dir + "deg,#0000," + ruler + ")"
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
return react.createElement("div", { style: {
|
|
1424
|
+
...st.orn,
|
|
1425
|
+
margin: "0.5em 0 0.75em"
|
|
1426
|
+
} }, line("l", "90"), react.createElement("i", { style: {
|
|
1427
|
+
...st.ornDot,
|
|
1428
|
+
background: ruler
|
|
1429
|
+
} }), line("r", "270"));
|
|
1430
|
+
}
|
|
1431
|
+
/** 行首那枚朱砂小印(进/状/计):**两层**——材质层过印那条糙边滤镜(印泥压出来的毛边与斑驳),
|
|
1432
|
+
* 字独立成层保持清晰;方块各带一点歪斜(真印章不会次次压得方正)。
|
|
1433
|
+
* 外框仍是 0.7em 字/1.6 行高/左右 0.375em 垫——换的是质感,版面逐值不动。 */
|
|
1434
|
+
function inkMark(name) {
|
|
1435
|
+
const tilt = name === "进展" ? -2.4 : name === "状态" ? 1.6 : -1.2;
|
|
1436
|
+
return react.createElement("span", { style: {
|
|
1437
|
+
position: "relative",
|
|
1438
|
+
flex: "0 0 auto",
|
|
1439
|
+
fontSize: "0.7em",
|
|
1440
|
+
lineHeight: 1.6,
|
|
1441
|
+
padding: "0 0.375em",
|
|
1442
|
+
transform: "rotate(" + tilt + "deg)"
|
|
1443
|
+
} }, react.createElement("span", {
|
|
1444
|
+
"aria-hidden": true,
|
|
1445
|
+
style: {
|
|
1446
|
+
position: "absolute",
|
|
1447
|
+
inset: 0,
|
|
1448
|
+
borderRadius: "0.1875em",
|
|
1449
|
+
background: "#d92b1c",
|
|
1450
|
+
filter: "url(#" + SEAL_ROUGH_INK_ID + ")"
|
|
1451
|
+
}
|
|
1452
|
+
}, react.createElement("span", {
|
|
1453
|
+
"aria-hidden": true,
|
|
1454
|
+
style: {
|
|
1455
|
+
position: "absolute",
|
|
1456
|
+
inset: 0,
|
|
1457
|
+
borderRadius: "inherit",
|
|
1458
|
+
background: "radial-gradient(circle at 72% 76%,#a81a0e33 0%,#0000 58%), radial-gradient(circle at 20% 62%,#f0715a3d 0%,#0000 52%), radial-gradient(circle at 52% 12%,#ee6a5233 0%,#0000 46%)"
|
|
1459
|
+
}
|
|
1460
|
+
})), react.createElement("span", { style: {
|
|
1461
|
+
position: "relative",
|
|
1462
|
+
color: "#fdf7ea",
|
|
1463
|
+
opacity: .95
|
|
1464
|
+
} }, name.slice(0, 1)));
|
|
1465
|
+
}
|
|
1466
|
+
/** 三段:一行紧凑(行首朱砂小方印+标签+正文同一基线)。 */
|
|
1467
|
+
function inlineRows(st, ctx) {
|
|
1468
|
+
const one = (key, name, text, last = false) => react.createElement("div", {
|
|
1469
|
+
key,
|
|
1470
|
+
style: {
|
|
1471
|
+
margin: last ? "0 0 -0.5em" : "0 0 0.5em",
|
|
1472
|
+
display: "flex",
|
|
1473
|
+
alignItems: "baseline",
|
|
1474
|
+
gap: "0.5em"
|
|
1475
|
+
}
|
|
1476
|
+
}, inkMark(name), react.createElement("span", { style: {
|
|
1477
|
+
...st.label,
|
|
1478
|
+
flex: "0 0 auto",
|
|
1479
|
+
display: "inline",
|
|
1480
|
+
marginBottom: 0,
|
|
1481
|
+
fontSize: "0.8em",
|
|
1482
|
+
color: ctx.accent
|
|
1483
|
+
} }, name + ":"), react.createElement("span", { style: {
|
|
1484
|
+
...st.text,
|
|
1485
|
+
flex: "1 1 auto",
|
|
1486
|
+
display: "inline",
|
|
1487
|
+
margin: 0,
|
|
1488
|
+
fontSize: "0.9em"
|
|
1489
|
+
} }, text));
|
|
1490
|
+
return react.createElement(react.Fragment, null, one("progress", "进展", ctx.progress), one("status", "状态", ctx.status), one("plan", "计划", ctx.plan, true));
|
|
1491
|
+
}
|
|
1492
|
+
/** 档位语(一行):该档金属渐变压进字里。 */
|
|
1493
|
+
function tierNode(ctx) {
|
|
1494
|
+
const metal = metalOf(ctx.tierKey);
|
|
1495
|
+
return react.createElement("div", { style: {
|
|
1496
|
+
...ctx.styles.tier,
|
|
1497
|
+
fontFamily: KAI,
|
|
1498
|
+
fontSize: "1.0625em",
|
|
1499
|
+
letterSpacing: "0.34em",
|
|
1500
|
+
textIndent: "0.34em",
|
|
1501
|
+
color: "transparent",
|
|
1502
|
+
backgroundImage: metal.gradient,
|
|
1503
|
+
WebkitBackgroundClip: "text",
|
|
1504
|
+
backgroundClip: "text"
|
|
1505
|
+
} }, ctx.tierText);
|
|
1506
|
+
}
|
|
1507
|
+
/** 组装一件:纸底/头部/档位语/饰线/三段(定稿不覆盖纸底与头部,故两者缺席=用 `seal-scroll.ts` 的原样式)。 */
|
|
1508
|
+
function paperParts(ctx) {
|
|
1509
|
+
return {
|
|
1510
|
+
paper: void 0,
|
|
1511
|
+
head: void 0,
|
|
1512
|
+
tier: tierNode(ctx),
|
|
1513
|
+
orn: ornament(ctx.styles, ctx.ruler),
|
|
1514
|
+
body: inlineRows(ctx.styles, ctx)
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
//#endregion
|
|
1518
|
+
//#region ../plugin-manager/src/seal-scroll.ts
|
|
1519
|
+
/** 印章卷轴 · 纯组件(`title-seal.ts` 的配套件,不认任何一家)。
|
|
1520
|
+
*
|
|
1521
|
+
* 来源:原型定稿 2026-10-05(记录见 `docs/plugins/plugin-manager/印章原型-定稿.md` 第九节)。
|
|
1522
|
+
* 点印后弹出的那块:**外框逐档照抄对应那枚印的材质**(底色+边线都照抄,不许简化成"一圈金属色"),
|
|
1523
|
+
* **中间是纯纸**,内容固定三段——进展/状态/计划。
|
|
1524
|
+
*
|
|
1525
|
+
* 三条形状(逐值照原型锁死):
|
|
1526
|
+
* 外框:厚 1.125em(16px 下=18px,这是下限;只给 10px 整张卡会读成"白底加一圈边"),
|
|
1527
|
+
* 材质随 `role`×`tier` 九档之一,挂 `roughFrame` 糙边滤镜。
|
|
1528
|
+
* 纸:`bg-layer-2` 底+`border-l1` 描边,压在材质上(外框那层在纸下面,只露一圈)。
|
|
1529
|
+
* 字:标题居中 1em/字距 .16em;档位字 .66em/字距 .42em;三段标签 .66em/字距 .34em;
|
|
1530
|
+
* 正文 .84em/行高 1.9;段间 1px 细线+.94em 上距。
|
|
1531
|
+
*
|
|
1532
|
+
* 颜色分治(照 `title-seal.ts` 的先例):
|
|
1533
|
+
* 印那一份(外框材质、档位字色)**固定色**——纸变印不变,印不管纸面派生;
|
|
1534
|
+
* 纸那一份(纸底、纸边、标题、正文、段间线)走 DSH 主题别名。
|
|
1535
|
+
*
|
|
1536
|
+
* 糙边滤镜两条分开:印是小块,`scale 2.2` 就够;外框有几百像素宽,同一条会被稀释到看不出,
|
|
1537
|
+
* 故外框单用 `scale 5` 的 `roughFrame`。滤镜只挂外框那层,纸不挂(否则纸的直边也会被抖)。
|
|
1538
|
+
*
|
|
1539
|
+
* 本件是纯函数:吃 props 回元素树,不留状态、不取数。手写 `React.createElement`(本包 client 束禁 JSX)。
|
|
1540
|
+
*/
|
|
1541
|
+
/** 糙边滤镜 id(文档作用域,故每页只许挂一次 `SealFilterDefs`)。 */
|
|
1542
|
+
const ROUGH_EDGE = "dshLifeSealRoughEdge";
|
|
1543
|
+
const ROUGH_FRAME = "dshLifeSealRoughFrame";
|
|
1544
|
+
/** 中等件(轴杆/纸筒/绦带这类几十像素宽的件)那条糙边:2.2 太细、5 会抖过头。 */
|
|
1545
|
+
const ROUGH_METAL = "dshLifeSealRoughMetal";
|
|
1546
|
+
/** 印泥那条(行首那几枚小印):同 2.2 的抖动,末尾再加一点高斯模糊——印泥压在纸上边缘会洇开,
|
|
1547
|
+
* 光有抖动边只会读成「毛边贴纸」。字住在滤镜之外,所以糊的只有泥、不是字。 */
|
|
1548
|
+
const ROUGH_INK = "dshLifeSealInkEdge";
|
|
1549
|
+
/** 中等件那条糙边 id(关闭钮引用;定义只此一处)。 */
|
|
1550
|
+
const SEAL_ROUGH_METAL_ID = ROUGH_METAL;
|
|
1551
|
+
/** 印泥那条糙边 id(纸面行首小印引用;定义只此一处)。 */
|
|
1552
|
+
const SEAL_ROUGH_INK_ID = ROUGH_INK;
|
|
1553
|
+
/** 档位话:标题下那一行不再印档位字,直接说档位所处的阶段(渲染期用,顶层可放行)。 */
|
|
1554
|
+
const TIER_TEXT = {
|
|
1555
|
+
gold: "精雕细琢中",
|
|
1556
|
+
silver: "全打通中",
|
|
1557
|
+
copper: "基础建设中"
|
|
1558
|
+
};
|
|
1559
|
+
/** 纸面四色是印自带的(羊皮纸底+ availability 墨色),不跟宿主主题跑——
|
|
1560
|
+
* 深色下宿主变量会把纸染黑,故这里写死定稿值;字族仍继承(定稿零字族)。 */
|
|
1561
|
+
const PAPER_BG = "#fdfaf2";
|
|
1562
|
+
const PAPER_EDGE = "#e6d9bd";
|
|
1346
1563
|
const S$1 = {
|
|
1347
1564
|
/** 外框(`.frame`):绝对铺满,挂糙边滤镜;纸压在它上面,只露一圈。 */
|
|
1348
1565
|
frame: {
|
|
@@ -1377,7 +1594,7 @@ window.__ModuleLoader__.load({
|
|
|
1377
1594
|
},
|
|
1378
1595
|
inner: {
|
|
1379
1596
|
position: "relative",
|
|
1380
|
-
padding: "1.75em 2em
|
|
1597
|
+
padding: "1.75em 2em"
|
|
1381
1598
|
},
|
|
1382
1599
|
head: {
|
|
1383
1600
|
textAlign: "center",
|
|
@@ -1427,7 +1644,7 @@ window.__ModuleLoader__.load({
|
|
|
1427
1644
|
fontSize: "0.84em",
|
|
1428
1645
|
lineHeight: 1.9,
|
|
1429
1646
|
letterSpacing: "0.02em",
|
|
1430
|
-
color:
|
|
1647
|
+
color: "#4b4335"
|
|
1431
1648
|
},
|
|
1432
1649
|
/** 卷轴浮层:卡片内绝对定位(卡片 `S.card` 是 relative 定位祖先),上沿悬在三枚章之下、左右各留半 em——
|
|
1433
1650
|
* 不是 modal:无遮罩、不锁滚动,点章切换、点「关闭」收起。 */
|
|
@@ -1441,11 +1658,12 @@ window.__ModuleLoader__.load({
|
|
|
1441
1658
|
justifyContent: "center",
|
|
1442
1659
|
pointerEvents: "auto"
|
|
1443
1660
|
},
|
|
1444
|
-
/**
|
|
1661
|
+
/** 浮层内框:**宽随内容**(`max-content`,正好包住最长那一行;不是固定 25em 那块大纸)。
|
|
1662
|
+
* `maxWidth:100%` 是窄卡的兜底(收满可用宽、永不捅破卡片),此时长行换行、高度自己长。
|
|
1445
1663
|
* 挂卷轴展开动画(从上而下舒卷,240ms;纯声明式,无 hook,纯函数可直测)。 */
|
|
1446
1664
|
popInner: {
|
|
1447
1665
|
position: "relative",
|
|
1448
|
-
width: "
|
|
1666
|
+
width: "max-content",
|
|
1449
1667
|
maxWidth: "100%",
|
|
1450
1668
|
transformOrigin: "50% 0",
|
|
1451
1669
|
animation: "dshLifeSealUnroll 240ms ease-out"
|
|
@@ -1464,16 +1682,47 @@ window.__ModuleLoader__.load({
|
|
|
1464
1682
|
cursor: "pointer"
|
|
1465
1683
|
}
|
|
1466
1684
|
};
|
|
1685
|
+
/** 外框专有的内阴影(随档;底色与边线不在这张表里,从调色板来)。
|
|
1686
|
+
* 外框比印面大得多,同一圈金色在小章上够、在几百像素的框上会被稀释,故这一层是外框自己的排法。 */
|
|
1687
|
+
const FRAME_INSET = {
|
|
1688
|
+
skill: {
|
|
1689
|
+
copper: "inset 0 0 0 1px #f8f1e2aa, inset 0 2px 0 #ffffff26, inset 0 -3px 6px #0000004d",
|
|
1690
|
+
silver: "inset 0 0 0 1px #fff, inset 0 2px 0 #ffffff2e, inset 0 -3px 6px #0000004d",
|
|
1691
|
+
gold: "inset 0 0 0 1px #fff8, inset 0 2px 0 #ffffff33, inset 0 -3px 6px #0000004d"
|
|
1692
|
+
},
|
|
1693
|
+
help: {
|
|
1694
|
+
copper: "inset 0 2px 0 #ffffff26, inset 0 -3px 6px #0000004d",
|
|
1695
|
+
silver: "inset 0 2px 0 #ffffff2e, inset 0 -3px 6px #0000004d",
|
|
1696
|
+
gold: "inset 0 2px 0 #ffffff33, inset 0 -3px 6px #0000004d"
|
|
1697
|
+
},
|
|
1698
|
+
plugin: {
|
|
1699
|
+
copper: "inset 0 1px 0 #d99a5e88, inset 0 -2px 5px #0006, 0 0 0 1px #5e2c12, inset 0 2px 0 #ffffff26",
|
|
1700
|
+
silver: "inset 0 1px 0 #ffffffdd, inset 0 -2px 5px #0005, 0 0 0 1px #6f767c, inset 0 2px 0 #ffffff33",
|
|
1701
|
+
gold: "inset 0 1px 0 #fff8d8dd, inset 0 -2px 5px #0005, 0 0 0 1px #8a6a15"
|
|
1702
|
+
}
|
|
1703
|
+
};
|
|
1704
|
+
/** 取一档材质:底色与边线从调色板来(与印共用一份取值),外框专有的内阴影与铆钉在这里合成。 */
|
|
1467
1705
|
function materialOf(role, tier) {
|
|
1468
|
-
const
|
|
1469
|
-
if (
|
|
1470
|
-
return
|
|
1706
|
+
const palette = SEAL_PALETTE[role]?.[tier];
|
|
1707
|
+
if (palette === void 0) throw new Error("未知的印档:" + role + "/" + tier);
|
|
1708
|
+
return {
|
|
1709
|
+
frame: {
|
|
1710
|
+
background: palette.background,
|
|
1711
|
+
...palette.border === void 0 ? {} : { border: palette.border },
|
|
1712
|
+
boxShadow: FRAME_INSET[role][tier]
|
|
1713
|
+
},
|
|
1714
|
+
accent: palette.accent,
|
|
1715
|
+
ruler: palette.ruler,
|
|
1716
|
+
rivet: role === "plugin" ? SEAL_RIVET[tier] : void 0
|
|
1717
|
+
};
|
|
1471
1718
|
}
|
|
1472
1719
|
/** 糙边滤镜定义:两条分开(印用 edge,卷轴外框用 frame),每页只挂一次。
|
|
1473
1720
|
*
|
|
1474
|
-
* 挂在文档任意位置都行(`width/height=0` 不占位),同页重复挂会撞 id。
|
|
1721
|
+
* 挂在文档任意位置都行(`width/height=0` 不占位),同页重复挂会撞 id。
|
|
1722
|
+
* 印章交互(悬停上浮高亮/按压回缩/焦点环/级别 tooltip)也住这里:scoped 类名,纯 CSS、无 hook。
|
|
1723
|
+
* 悬停高亮须把糙边 url 原样带上(filter 单属性,另写会把糙边冲掉)。 */
|
|
1475
1724
|
function SealFilterDefs() {
|
|
1476
|
-
return react.createElement("svg", {
|
|
1725
|
+
return react.createElement(react.Fragment, null, react.createElement("style", null, ".dshLifeSealBtn{position:relative;transition:transform 160ms ease-out,filter 160ms ease-out}.dshLifeSealBtn:hover{transform:translateY(-2px);filter:url(#dshLifeSealRoughEdge) brightness(1.12)}.dshLifeSealBtn:active{transform:translateY(0) scale(.96)}.dshLifeSealBtn:focus-visible{outline:2px solid #f5d97a;outline-offset:2px}.dshLifeSealBtn::after{content:attr(data-tip);position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%);background:#241f19ee;color:#f8f1e2;font-size:.72em;letter-spacing:.1em;padding:.4em .9em;border-radius:.5em;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity 150ms ease;z-index:30}.dshLifeSealBtn:hover::after,.dshLifeSealBtn:focus-visible::after{opacity:1}"), react.createElement("svg", {
|
|
1477
1726
|
width: 0,
|
|
1478
1727
|
height: 0,
|
|
1479
1728
|
"aria-hidden": true,
|
|
@@ -1497,6 +1746,21 @@ window.__ModuleLoader__.load({
|
|
|
1497
1746
|
y: "-6%",
|
|
1498
1747
|
width: "112%",
|
|
1499
1748
|
height: "112%"
|
|
1749
|
+
}, react.createElement("feTurbulence", {
|
|
1750
|
+
baseFrequency: .6,
|
|
1751
|
+
numOctaves: 3,
|
|
1752
|
+
result: "n"
|
|
1753
|
+
}), react.createElement("feDisplacementMap", {
|
|
1754
|
+
in: "SourceGraphic",
|
|
1755
|
+
in2: "n",
|
|
1756
|
+
scale: 6.5
|
|
1757
|
+
})), react.createElement("filter", {
|
|
1758
|
+
id: ROUGH_METAL,
|
|
1759
|
+
key: ROUGH_METAL,
|
|
1760
|
+
x: "-8%",
|
|
1761
|
+
y: "-14%",
|
|
1762
|
+
width: "116%",
|
|
1763
|
+
height: "128%"
|
|
1500
1764
|
}, react.createElement("feTurbulence", {
|
|
1501
1765
|
baseFrequency: .9,
|
|
1502
1766
|
numOctaves: 2,
|
|
@@ -1504,8 +1768,43 @@ window.__ModuleLoader__.load({
|
|
|
1504
1768
|
}), react.createElement("feDisplacementMap", {
|
|
1505
1769
|
in: "SourceGraphic",
|
|
1506
1770
|
in2: "n",
|
|
1507
|
-
scale:
|
|
1508
|
-
}))
|
|
1771
|
+
scale: 3.2
|
|
1772
|
+
})), react.createElement("filter", {
|
|
1773
|
+
id: ROUGH_INK,
|
|
1774
|
+
key: ROUGH_INK,
|
|
1775
|
+
x: "-25%",
|
|
1776
|
+
y: "-25%",
|
|
1777
|
+
width: "150%",
|
|
1778
|
+
height: "150%"
|
|
1779
|
+
}, react.createElement("feTurbulence", {
|
|
1780
|
+
type: "fractalNoise",
|
|
1781
|
+
baseFrequency: .9,
|
|
1782
|
+
numOctaves: 2,
|
|
1783
|
+
result: "n"
|
|
1784
|
+
}), react.createElement("feDisplacementMap", {
|
|
1785
|
+
in: "SourceGraphic",
|
|
1786
|
+
in2: "n",
|
|
1787
|
+
scale: 2.2,
|
|
1788
|
+
result: "d"
|
|
1789
|
+
}), react.createElement("feGaussianBlur", {
|
|
1790
|
+
in: "d",
|
|
1791
|
+
stdDeviation: .3,
|
|
1792
|
+
result: "b"
|
|
1793
|
+
}), react.createElement("feTurbulence", {
|
|
1794
|
+
type: "fractalNoise",
|
|
1795
|
+
baseFrequency: .75,
|
|
1796
|
+
numOctaves: 3,
|
|
1797
|
+
result: "g"
|
|
1798
|
+
}), react.createElement("feColorMatrix", {
|
|
1799
|
+
in: "g",
|
|
1800
|
+
type: "matrix",
|
|
1801
|
+
values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .7 .45 0 0 .35",
|
|
1802
|
+
result: "mask"
|
|
1803
|
+
}), react.createElement("feComposite", {
|
|
1804
|
+
in: "b",
|
|
1805
|
+
in2: "mask",
|
|
1806
|
+
operator: "in"
|
|
1807
|
+
}))));
|
|
1509
1808
|
}
|
|
1510
1809
|
/** 一卷:外框材质 + 纯纸 + 进展/状态/计划三段。 */
|
|
1511
1810
|
function SealScroll(props) {
|
|
@@ -1548,38 +1847,38 @@ window.__ModuleLoader__.load({
|
|
|
1548
1847
|
}
|
|
1549
1848
|
})
|
|
1550
1849
|
] : [];
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1850
|
+
/** 纸面字排:定稿件直出五件。 */
|
|
1851
|
+
const parts = paperParts({
|
|
1852
|
+
tierKey: props.tier,
|
|
1853
|
+
tierText: TIER_TEXT[props.tier],
|
|
1854
|
+
progress: props.progress,
|
|
1855
|
+
status: props.status,
|
|
1856
|
+
plan: props.plan,
|
|
1857
|
+
accent: material.accent,
|
|
1858
|
+
ruler: material.ruler,
|
|
1859
|
+
paperEdge: PAPER_EDGE,
|
|
1860
|
+
styles: {
|
|
1861
|
+
head: S$1.head,
|
|
1862
|
+
paper: S$1.paper,
|
|
1863
|
+
tier: S$1.tier,
|
|
1864
|
+
orn: S$1.orn,
|
|
1865
|
+
ornLine: S$1.ornLine,
|
|
1866
|
+
ornDot: S$1.ornDot,
|
|
1867
|
+
block: S$1.block,
|
|
1868
|
+
blockDivided: S$1.blockDivided,
|
|
1869
|
+
blockLast: S$1.blockLast,
|
|
1870
|
+
label: S$1.label,
|
|
1871
|
+
text: S$1.text
|
|
1556
1872
|
}
|
|
1557
1873
|
});
|
|
1558
|
-
const block = (key, name, text, divided, last = false) => react.createElement("div", {
|
|
1559
|
-
key,
|
|
1560
|
-
style: last ? divided ? {
|
|
1561
|
-
...S$1.blockLast,
|
|
1562
|
-
paddingTop: "0.9375em",
|
|
1563
|
-
borderTop: "1px solid #e6d9bd"
|
|
1564
|
-
} : S$1.blockLast : divided ? S$1.blockDivided : S$1.block
|
|
1565
|
-
}, react.createElement("div", { style: {
|
|
1566
|
-
...S$1.label,
|
|
1567
|
-
color: material.accent
|
|
1568
|
-
} }, name), react.createElement("p", { style: S$1.text }, text));
|
|
1569
1874
|
return react.createElement("div", { style: S$1.scroll }, react.createElement("div", { style: {
|
|
1570
1875
|
...S$1.frame,
|
|
1571
1876
|
...material.frame
|
|
1572
|
-
} }, ...frameChildren), react.createElement("div", { style: S$1.paper }, react.createElement("div", { style: S$1.inner }, react.createElement("div", { style: S$1.head }, react.createElement(SealStamp, {
|
|
1877
|
+
} }, ...frameChildren), react.createElement("div", { style: parts.paper === void 0 ? S$1.paper : parts.paper }, react.createElement("div", { style: S$1.inner }, react.createElement("div", { style: parts.head === void 0 ? S$1.head : parts.head }, react.createElement(SealStamp, {
|
|
1573
1878
|
role: props.role,
|
|
1574
1879
|
tier: props.tier,
|
|
1575
1880
|
label: props.title
|
|
1576
|
-
}),
|
|
1577
|
-
...S$1.tier,
|
|
1578
|
-
color: material.accent
|
|
1579
|
-
} }, TIER_TEXT[props.tier])), react.createElement("div", { style: S$1.orn }, line("l", "90"), react.createElement("i", { style: {
|
|
1580
|
-
...S$1.ornDot,
|
|
1581
|
-
background: material.accent
|
|
1582
|
-
} }), line("r", "270")), block("progress", "进展", props.progress, false), block("status", "状态", props.status, true), block("plan", "计划", props.plan, true, true))));
|
|
1881
|
+
}), parts.tier), parts.orn, parts.body)));
|
|
1583
1882
|
}
|
|
1584
1883
|
/** 卷轴浮层:popover + 点击别处关闭 + 卷轴展开动画 + 关闭。`open` 为假时不渲染(纯函数,不收自己的状态)。 */
|
|
1585
1884
|
function SealScrollDialog(props) {
|
|
@@ -1602,16 +1901,27 @@ window.__ModuleLoader__.load({
|
|
|
1602
1901
|
"aria-hidden": true,
|
|
1603
1902
|
onClick: props.onClose
|
|
1604
1903
|
}), react.createElement("div", {
|
|
1605
|
-
style: S$1.dialog
|
|
1904
|
+
style: props.anchor === void 0 ? S$1.dialog : {
|
|
1905
|
+
...S$1.dialog,
|
|
1906
|
+
...props.anchor
|
|
1907
|
+
},
|
|
1606
1908
|
role: "dialog",
|
|
1607
1909
|
"aria-modal": false,
|
|
1608
1910
|
"aria-label": props.title
|
|
1609
|
-
}, react.createElement("style", null, "@keyframes dshLifeSealUnroll{from{opacity:0;transform:translateY(-10px) scaleY(.7)}to{opacity:1;transform:none}}"), react.createElement("div", { style: S$1.popInner },
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1911
|
+
}, react.createElement("style", null, "@keyframes dshLifeSealUnroll{from{opacity:0;transform:translateY(-10px) scaleY(.7)}to{opacity:1;transform:none}}"), react.createElement("div", { style: S$1.popInner }, (() => {
|
|
1912
|
+
const look = closeLook(props.tier);
|
|
1913
|
+
return react.createElement("button", {
|
|
1914
|
+
type: "button",
|
|
1915
|
+
className: "dshLifeSealBtn",
|
|
1916
|
+
style: {
|
|
1917
|
+
zIndex: 20,
|
|
1918
|
+
...look.style
|
|
1919
|
+
},
|
|
1920
|
+
"data-ilife-press": "seal-close",
|
|
1921
|
+
"data-ilife-close": "tie",
|
|
1922
|
+
onClick: props.onClose
|
|
1923
|
+
}, look.decor ?? null, look.label);
|
|
1924
|
+
})(), react.createElement(SealScroll, scroll))));
|
|
1615
1925
|
}
|
|
1616
1926
|
//#endregion
|
|
1617
1927
|
//#region ../plugin-manager/src/seal-stamp.ts
|
|
@@ -1653,88 +1963,64 @@ window.__ModuleLoader__.load({
|
|
|
1653
1963
|
color: "#f8f1e2"
|
|
1654
1964
|
}
|
|
1655
1965
|
};
|
|
1656
|
-
/**
|
|
1657
|
-
const
|
|
1966
|
+
/** 印面专有的内圈高光与外发光(随档;底色与边线不在这张表里,从调色板来)。 */
|
|
1967
|
+
const FACE_EXTRA = {
|
|
1658
1968
|
skill: {
|
|
1659
1969
|
copper: {
|
|
1660
|
-
background: "linear-gradient(180deg,#8a4a28,#6e3418)",
|
|
1661
1970
|
color: "#f8f1e2",
|
|
1662
|
-
border: "3px solid #a5652f",
|
|
1663
1971
|
boxShadow: "inset 0 0 0 1px #f8f1e2aa"
|
|
1664
1972
|
},
|
|
1665
1973
|
silver: {
|
|
1666
|
-
background: "linear-gradient(180deg,#b73124,#8e1f14)",
|
|
1667
1974
|
color: "#f8f1e2",
|
|
1668
|
-
border: "3px double #eef3f6",
|
|
1669
1975
|
boxShadow: "inset 0 0 0 1px #fff"
|
|
1670
1976
|
},
|
|
1671
1977
|
gold: {
|
|
1672
|
-
background: "linear-gradient(180deg,#d34a35,#a32216)",
|
|
1673
1978
|
color: "#fff8e0",
|
|
1674
|
-
border: "3px double #f5d97a",
|
|
1675
1979
|
boxShadow: "inset 0 0 0 1px #fff8,0 4px 16px #c9a22755"
|
|
1676
1980
|
}
|
|
1677
1981
|
},
|
|
1678
1982
|
help: {
|
|
1679
|
-
copper: {
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
},
|
|
1683
|
-
silver: {
|
|
1684
|
-
background: "linear-gradient(180deg,#8f979e,#5f666d)",
|
|
1685
|
-
border: "3px double #e8eef2"
|
|
1686
|
-
},
|
|
1687
|
-
gold: {
|
|
1688
|
-
background: "linear-gradient(180deg,#c63d2a,#a32216)",
|
|
1689
|
-
border: "3px double #f5d97a"
|
|
1690
|
-
}
|
|
1983
|
+
copper: {},
|
|
1984
|
+
silver: {},
|
|
1985
|
+
gold: {}
|
|
1691
1986
|
},
|
|
1692
1987
|
plugin: {
|
|
1693
|
-
copper: {
|
|
1694
|
-
silver: {
|
|
1695
|
-
gold: {
|
|
1988
|
+
copper: {},
|
|
1989
|
+
silver: {},
|
|
1990
|
+
gold: {}
|
|
1696
1991
|
}
|
|
1697
1992
|
};
|
|
1698
1993
|
/** 插件整面金属带的描边(随档)。 */
|
|
1699
1994
|
const BAND_EDGE = {
|
|
1700
|
-
copper:
|
|
1701
|
-
silver:
|
|
1702
|
-
gold:
|
|
1703
|
-
};
|
|
1704
|
-
/** 插件左右铆钉(随档,两颗)。 */
|
|
1705
|
-
const RIVET = {
|
|
1706
|
-
copper: {
|
|
1707
|
-
background: "radial-gradient(circle at 34% 28%,#f0c088,#a5652f 55%,#6a3216)",
|
|
1708
|
-
boxShadow: "inset 0 -1px 1px #00000055,0 1px 2px #00000088"
|
|
1709
|
-
},
|
|
1710
|
-
silver: {
|
|
1711
|
-
background: "radial-gradient(circle at 34% 28%,#ffffff,#c3cad0 50%,#7d848b)",
|
|
1712
|
-
boxShadow: "inset 0 -1px 1px #00000044,0 1px 2px #00000077"
|
|
1713
|
-
},
|
|
1714
|
-
gold: {
|
|
1715
|
-
background: "radial-gradient(circle at 34% 28%,#fffbe0,#e3c565 50%,#a5841d)",
|
|
1716
|
-
boxShadow: "inset 0 -1px 1px #00000044,0 1px 2px #00000077,0 0 6px #c9a22788"
|
|
1717
|
-
}
|
|
1995
|
+
copper: "inset 0 1px 0 #d99a5e88,inset 0 -2px 5px #0006,0 0 0 1px #5e2c12",
|
|
1996
|
+
silver: "inset 0 1px 0 #ffffffdd,inset 0 -2px 5px #0005,0 0 0 1px #6f767c",
|
|
1997
|
+
gold: "inset 0 1px 0 #fff8d8dd,inset 0 -2px 5px #0005,0 0 0 1px #8a6a15,0 3px 14px #c9a22766"
|
|
1718
1998
|
};
|
|
1719
1999
|
/** 一枚印章(button,点章弹卷)。未知组合直接抛错,不静默退化。 */
|
|
1720
2000
|
function SealStamp(props) {
|
|
1721
|
-
const
|
|
1722
|
-
if (
|
|
2001
|
+
const palette = SEAL_PALETTE[props.role]?.[props.tier];
|
|
2002
|
+
if (palette === void 0) throw new Error("未知印章组合:" + String(props.role) + "/" + String(props.tier));
|
|
2003
|
+
const style = {
|
|
2004
|
+
display: "inline-flex",
|
|
2005
|
+
alignItems: "center",
|
|
2006
|
+
cursor: "pointer",
|
|
2007
|
+
fontWeight: 800,
|
|
2008
|
+
lineHeight: 1.2,
|
|
2009
|
+
whiteSpace: "nowrap",
|
|
2010
|
+
filter: "url(#dshLifeSealRoughEdge)",
|
|
2011
|
+
...ROLE_BASE[props.role],
|
|
2012
|
+
background: palette.background,
|
|
2013
|
+
...palette.border === void 0 ? {} : { border: palette.border },
|
|
2014
|
+
...FACE_EXTRA[props.role][props.tier]
|
|
2015
|
+
};
|
|
2016
|
+
const tierName = props.tier === "copper" ? "铜" : props.tier === "silver" ? "银" : "金";
|
|
1723
2017
|
const buttonProps = {
|
|
1724
2018
|
type: "button",
|
|
1725
|
-
style
|
|
1726
|
-
|
|
1727
|
-
alignItems: "center",
|
|
1728
|
-
cursor: "pointer",
|
|
1729
|
-
fontWeight: 800,
|
|
1730
|
-
lineHeight: 1.2,
|
|
1731
|
-
whiteSpace: "nowrap",
|
|
1732
|
-
filter: "url(#dshLifeSealRoughEdge)",
|
|
1733
|
-
...ROLE_BASE[props.role],
|
|
1734
|
-
...face
|
|
1735
|
-
},
|
|
2019
|
+
style,
|
|
2020
|
+
className: "dshLifeSealBtn",
|
|
1736
2021
|
"aria-label": props.label,
|
|
1737
2022
|
"aria-haspopup": "dialog",
|
|
2023
|
+
"data-tip": tierName + "章 · " + TIER_TEXT[props.tier],
|
|
1738
2024
|
onClick: props.onSelect
|
|
1739
2025
|
};
|
|
1740
2026
|
if (props.role !== "plugin") return react.createElement("button", buttonProps, props.label);
|
|
@@ -1746,7 +2032,8 @@ window.__ModuleLoader__.load({
|
|
|
1746
2032
|
right: "-1px",
|
|
1747
2033
|
zIndex: 1,
|
|
1748
2034
|
borderRadius: "4px",
|
|
1749
|
-
|
|
2035
|
+
boxShadow: BAND_EDGE[props.tier],
|
|
2036
|
+
background: palette.background
|
|
1750
2037
|
};
|
|
1751
2038
|
const rivet = {
|
|
1752
2039
|
position: "absolute",
|
|
@@ -1756,13 +2043,10 @@ window.__ModuleLoader__.load({
|
|
|
1756
2043
|
borderRadius: "50%",
|
|
1757
2044
|
top: "50%",
|
|
1758
2045
|
transform: "translateY(-50%)",
|
|
1759
|
-
...
|
|
2046
|
+
...SEAL_RIVET[props.tier]
|
|
1760
2047
|
};
|
|
1761
2048
|
return react.createElement("button", buttonProps, react.createElement("span", {
|
|
1762
|
-
style:
|
|
1763
|
-
...band,
|
|
1764
|
-
background: face.background
|
|
1765
|
-
},
|
|
2049
|
+
style: band,
|
|
1766
2050
|
"data-seal-band": "1"
|
|
1767
2051
|
}), react.createElement("span", {
|
|
1768
2052
|
style: {
|
|
@@ -2449,37 +2733,56 @@ window.__ModuleLoader__.load({
|
|
|
2449
2733
|
const product = title ?? "";
|
|
2450
2734
|
const seals = props.seals ?? [];
|
|
2451
2735
|
const seed = props.sealSeed ?? 0;
|
|
2452
|
-
/**
|
|
2736
|
+
/** 三行定序:**由面板按角色定**(HELP 上/技能中/插件下;HELP 永远 z 最上)。
|
|
2737
|
+
* 调用方给的数组顺序无关紧要,缺哪一枚就只画剩下的(位置顺次上移)——
|
|
2738
|
+
* 少给一枚不该把整块面板炸掉(旧写法按位置取 `seals[2]`,两枚时直接 TypeError)。 */
|
|
2453
2739
|
const order = [
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
];
|
|
2458
|
-
/**
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2740
|
+
"help",
|
|
2741
|
+
"skill",
|
|
2742
|
+
"plugin"
|
|
2743
|
+
].map((role) => seals.findIndex((s) => s.role === role)).filter((index) => index >= 0);
|
|
2744
|
+
/** 三行槽位几何 · **唯一出处**:章的落位与卷轴浮层的锚点都从这里取,免得两处各写一套数字。
|
|
2745
|
+
* 行高(上/中/下;纵向净空各留约 10px,糙边抖动也碰不到)/轻微倾角(±0.5°~±2.5°,家下标×槽位哈希,
|
|
2746
|
+
* 纯函数每次同值)/右间距(三枚全部居右收敛,各家各行 0.2em~1.1em 错开,永不出面板右边界)。
|
|
2747
|
+
* `dialog` 是卷轴浮层那一份:贴该行下方 1.9em(章高约 1.5em + 0.4em 缝),右缘与那枚章对齐,
|
|
2748
|
+
* 再往左让 1.25em 给绦带(关闭钮骑在浮层右上方,不让它越出卡片)。 */
|
|
2749
|
+
const slotGeom = (slot, seed) => {
|
|
2750
|
+
const top = [
|
|
2751
|
+
"0.3em",
|
|
2752
|
+
"3.4em",
|
|
2753
|
+
"6.2em"
|
|
2754
|
+
][slot] ?? "0.3em";
|
|
2466
2755
|
const q = (seed * 5 + slot * 11 + 3) % 10;
|
|
2467
|
-
|
|
2756
|
+
const tilt = String((q % 2 === 0 ? -1 : 1) * (.5 + Math.floor(q / 2) * .5));
|
|
2757
|
+
const padRight = String(.2 + (seed * 7 + slot * 3 + 1) % 6 * .18) + "em";
|
|
2758
|
+
return {
|
|
2759
|
+
top,
|
|
2760
|
+
tilt,
|
|
2761
|
+
padRight,
|
|
2762
|
+
dialog: {
|
|
2763
|
+
top: "calc(" + top + " + 1.9em)",
|
|
2764
|
+
right: "calc(" + padRight + " + 1.25em)",
|
|
2765
|
+
left: "auto",
|
|
2766
|
+
justifyContent: "flex-end",
|
|
2767
|
+
maxWidth: "calc(100% - " + padRight + " - 1.75em)"
|
|
2768
|
+
}
|
|
2769
|
+
};
|
|
2468
2770
|
};
|
|
2469
|
-
/** 右间距(三枚全部居右收敛,各家各行 0.2em~1.1em 错开,永不出面板右边界)。 */
|
|
2470
|
-
const padRightOfSlot = (slot) => String(.2 + (seed * 7 + slot * 3 + 1) % 6 * .18) + "em";
|
|
2471
2771
|
const zOfRole = (role) => role === "help" ? 4 : role === "skill" ? 3 : 2;
|
|
2472
2772
|
const overlay = seals.length === 0 ? null : react.createElement("div", { style: S.sealOverlay }, react.createElement(SealFilterDefs, {}), ...order.map((dataIndex, slot) => react.createElement("span", {
|
|
2473
2773
|
key: seals[dataIndex].role + ":" + seals[dataIndex].tier,
|
|
2474
|
-
style: {
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2774
|
+
style: (() => {
|
|
2775
|
+
const g = slotGeom(slot, seed);
|
|
2776
|
+
return {
|
|
2777
|
+
position: "absolute",
|
|
2778
|
+
top: g.top,
|
|
2779
|
+
right: g.padRight,
|
|
2780
|
+
zIndex: zOfRole(seals[dataIndex].role),
|
|
2781
|
+
pointerEvents: "auto",
|
|
2782
|
+
display: "inline-flex",
|
|
2783
|
+
transform: "rotate(" + g.tilt + "deg)"
|
|
2784
|
+
};
|
|
2785
|
+
})()
|
|
2483
2786
|
}, react.createElement("span", { style: {
|
|
2484
2787
|
fontSize: "0.8em",
|
|
2485
2788
|
display: "inline-flex"
|
|
@@ -2490,6 +2793,9 @@ window.__ModuleLoader__.load({
|
|
|
2490
2793
|
onSelect: props.onSealSelect === void 0 ? void 0 : props.onSealSelect.bind(null, dataIndex)
|
|
2491
2794
|
})))));
|
|
2492
2795
|
const openSeal = props.selectedSeal === null || props.selectedSeal === void 0 ? null : seals[props.selectedSeal] ?? null;
|
|
2796
|
+
/** 卷轴浮层跟着**被点的那枚章**:锚点与章的落位同出一处(`slotGeom`),两处不会各自漂。 */
|
|
2797
|
+
const openSlot = props.selectedSeal === null || props.selectedSeal === void 0 ? -1 : order.indexOf(props.selectedSeal);
|
|
2798
|
+
const anchor = openSlot < 0 ? void 0 : slotGeom(openSlot, seed).dialog;
|
|
2493
2799
|
const dialog = openSeal === null ? null : react.createElement(SealScrollDialog, {
|
|
2494
2800
|
open: true,
|
|
2495
2801
|
onClose: props.onSealClose ?? (() => void 0),
|
|
@@ -2498,7 +2804,8 @@ window.__ModuleLoader__.load({
|
|
|
2498
2804
|
title: openSeal.label,
|
|
2499
2805
|
progress: openSeal.progress,
|
|
2500
2806
|
status: openSeal.status,
|
|
2501
|
-
plan: openSeal.plan
|
|
2807
|
+
plan: openSeal.plan,
|
|
2808
|
+
anchor
|
|
2502
2809
|
});
|
|
2503
2810
|
const headDataDir = surface === null ? "" : surface.resolved?.["dbDir"] ?? surface.dataDir;
|
|
2504
2811
|
const ready = state.kind === "ready";
|