dsh-milestone 0.6.4 → 0.6.6
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/README.md +56 -119
- package/lib/client.js +499 -967
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -598,6 +598,9 @@ window.__ModuleLoader__.load({
|
|
|
598
598
|
"turn.label": "第 {n} 轮",
|
|
599
599
|
/** Hover position: `第 {hover.index + 1} / {hover.total} 条`. */
|
|
600
600
|
"pos.of": "第 {n} / {m} 条",
|
|
601
|
+
/** Hover position of a collapsed-turn summary dot: `第 {a}–{b} / {m} 条`
|
|
602
|
+
* (the range of messages the summary dot represents). */
|
|
603
|
+
"pos.range": "第 {a}–{b} / {m} 条",
|
|
601
604
|
/** Search input placeholder. */
|
|
602
605
|
"search.placeholder": "搜索消息内容",
|
|
603
606
|
/** aria-label on the search toggle button and the search input. */
|
|
@@ -658,6 +661,8 @@ window.__ModuleLoader__.load({
|
|
|
658
661
|
"list.close": "收起列表",
|
|
659
662
|
/** Header title of the all-prompts list panel. */
|
|
660
663
|
"list.label": "全部提问",
|
|
664
|
+
/** Bottom hint of the all-prompts list while it drains older pages. */
|
|
665
|
+
"list.loading": "正在加载更早消息…",
|
|
661
666
|
/** Header title + input placeholder of the cross-session search panel. */
|
|
662
667
|
"search.cross": "跨会话搜索",
|
|
663
668
|
/** aria-label + title on the cross-session search toggle when the panel is CLOSED. */
|
|
@@ -796,78 +801,48 @@ window.__ModuleLoader__.load({
|
|
|
796
801
|
"settings.lang.zh": "中文",
|
|
797
802
|
/** Language option: force English copy. */
|
|
798
803
|
"settings.lang.en": "English",
|
|
799
|
-
/** 0.6.
|
|
800
|
-
"
|
|
801
|
-
/** 0.6.
|
|
802
|
-
"
|
|
803
|
-
/** 0.6.
|
|
804
|
-
"
|
|
805
|
-
/** 0.6.
|
|
806
|
-
"
|
|
807
|
-
/** 0.6.
|
|
808
|
-
"
|
|
809
|
-
/** 0.6.
|
|
810
|
-
"
|
|
811
|
-
/** 0.6.
|
|
812
|
-
"
|
|
813
|
-
/** 0.6.
|
|
814
|
-
"
|
|
815
|
-
/** 0.6.
|
|
816
|
-
"
|
|
817
|
-
/** 0.6.
|
|
818
|
-
"
|
|
819
|
-
/** 0.6.
|
|
820
|
-
"
|
|
821
|
-
/** 0.6.
|
|
822
|
-
"
|
|
823
|
-
/** 0.6.
|
|
824
|
-
"
|
|
825
|
-
/** 0.6.
|
|
826
|
-
"
|
|
827
|
-
/** 0.6.
|
|
828
|
-
"
|
|
829
|
-
/** 0.6.
|
|
830
|
-
"
|
|
831
|
-
/** 0.6.
|
|
832
|
-
"
|
|
833
|
-
/** 0.6.4 onboarding: step 3 line 1. */
|
|
834
|
-
"onboarding.step3.desc1": "调整强调色、圆点大小、距边距离与左右位置,即调即存。",
|
|
835
|
-
/** 0.6.4 onboarding: step 3 line 2. */
|
|
836
|
-
"onboarding.step3.desc2": "切换界面语言、搭配「聚焦」效果,或把常用功能 pin 在折叠外。",
|
|
837
|
-
/** 0.6.4 onboarding: step 4 title — update detection & support. */
|
|
838
|
-
"onboarding.step4.title": "更新检测与支持",
|
|
839
|
-
/** 0.6.4 onboarding: step 4 line 1. */
|
|
840
|
-
"onboarding.step4.desc1": "挂载后自动检查 npm 新版本;有新版本时工具栏会亮起提示。",
|
|
841
|
-
/** 0.6.4 onboarding: step 4 line 2. */
|
|
842
|
-
"onboarding.step4.desc2": "喜欢这个插件?欢迎 Star、提交 Issue,或通过 npm 安装升级。",
|
|
843
|
-
/** 0.6.4 onboarding demo: fake message 1 (step 1 dots / step 2 corpus). */
|
|
844
|
-
"onboarding.demo.m1": "帮我优化这段代码",
|
|
845
|
-
/** 0.6.4 onboarding demo: fake message 2. */
|
|
846
|
-
"onboarding.demo.m2": "解释这个报错",
|
|
847
|
-
/** 0.6.4 onboarding demo: fake message 3. */
|
|
848
|
-
"onboarding.demo.m3": "再检查边界条件",
|
|
849
|
-
/** 0.6.4 onboarding demo: fake message 4. */
|
|
850
|
-
"onboarding.demo.m4": "写个单元测试",
|
|
851
|
-
/** 0.6.4 onboarding demo: fake message 5. */
|
|
852
|
-
"onboarding.demo.m5": "总结一下方案",
|
|
853
|
-
/** 0.6.4 onboarding demo (step 1): hint under the toy dot rail. */
|
|
854
|
-
"onboarding.demo.hoverHint": "试试悬停与点击圆点",
|
|
855
|
-
/** 0.6.4 onboarding demo (step 2): hint under the toy search/favorite. */
|
|
856
|
-
"onboarding.demo.searchHint": "试试输入「报错」,或点星标切换只看收藏",
|
|
857
|
-
/** 0.6.4 onboarding demo (step 3): the 展开箭头 → 齿轮 → 设置 path hint. */
|
|
858
|
-
"onboarding.demo.settingsPath": "展开箭头 → 齿轮 → 设置",
|
|
859
|
-
/** 0.6.4 onboarding demo (step 3): legend label for the focus mix. */
|
|
860
|
-
"onboarding.demo.focusMix": "聚焦搭配",
|
|
861
|
-
/** 0.6.4 onboarding demo (step 3): legend label for pinning features. */
|
|
862
|
-
"onboarding.demo.pin": "pin 固定",
|
|
863
|
-
/** 0.6.4 settings footer action: replay the tutorial. */
|
|
864
|
-
"onboarding.reopen": "重新查看教程"
|
|
804
|
+
/** 0.6.5 tour: bubble 0 title (also the bubble's aria-label). */
|
|
805
|
+
"tour.welcome.title": "欢迎使用 dsh-milestone",
|
|
806
|
+
/** 0.6.5 tour: bubble 0 description — the REAL rail list anchor. */
|
|
807
|
+
"tour.welcome.desc": "这是你的会话导航条:每条提问一个圆点,点击跳转,悬停查看元信息。",
|
|
808
|
+
/** 0.6.5 tour: bubble 1 title — the expand arrow. */
|
|
809
|
+
"tour.step1.title": "展开工具栏",
|
|
810
|
+
/** 0.6.5 tour: bubble 1 description — what clicking REALLY does (auto-advance on the real click). */
|
|
811
|
+
"tour.step1.desc": "点这里展开全部功能键:搜索、列表、收藏、聚焦、更新检测与设置都在里面。",
|
|
812
|
+
/** 0.6.5 tour: bubble 2 title — the settings gear. */
|
|
813
|
+
"tour.step2.title": "打开设置",
|
|
814
|
+
/** 0.6.5 tour: bubble 2 description — what opening settings contains (auto-advance on the real open). */
|
|
815
|
+
"tour.step2.desc": "打开设置:钉住常用键、个性化外观、切换语言、搭配「聚焦」。",
|
|
816
|
+
/** 0.6.5 tour: bubble 3 title — the dots (real hover/click behavior). */
|
|
817
|
+
"tour.step3.title": "圆点导航",
|
|
818
|
+
/** 0.6.5 tour: bubble 3 description. */
|
|
819
|
+
"tour.step3.desc": "每个圆点 = 一条提问——点击跳转,悬停查看时间、模型与用量。",
|
|
820
|
+
/** 0.6.5 tour: bubble 4 title — done. */
|
|
821
|
+
"tour.step4.title": "开始使用",
|
|
822
|
+
/** 0.6.5 tour: bubble 4 description — finish line. */
|
|
823
|
+
"tour.step4.desc": "去试试吧;有问题欢迎到 GitHub 提 Issue。",
|
|
824
|
+
/** 0.6.5 tour: primary action on bubble 0 (begin) and bubble 4 (finish). */
|
|
825
|
+
"tour.start": "开始使用",
|
|
826
|
+
/** 0.6.5 tour: forward navigation (bubbles 1–3; the real-click auto-advance comes first). */
|
|
827
|
+
"tour.next": "下一步",
|
|
828
|
+
/** 0.6.5 tour: back navigation (bubbles 1–4). */
|
|
829
|
+
"tour.prev": "上一步",
|
|
830
|
+
/** 0.6.5 tour: corner skip — every bubble, persists the flag. */
|
|
831
|
+
"tour.skip": "跳过",
|
|
832
|
+
/** 0.6.5 tour: progress text `第 n / 5 步`. */
|
|
833
|
+
"tour.step.label": "第 {n} / 5 步",
|
|
834
|
+
/** 0.6.5 tour: aria-label on one progress dot. */
|
|
835
|
+
"tour.step.short": "第 {n} 步",
|
|
836
|
+
/** 0.6.5 settings footer action: replay the tour (data-onboarding-reopen kept). */
|
|
837
|
+
"tour.reopen": "重新查看教程"
|
|
865
838
|
};
|
|
866
839
|
const en = {
|
|
867
840
|
"jump.to": "Jump to message {n}",
|
|
868
841
|
"window.hint": "Showing {n} messages · more below",
|
|
869
842
|
"turn.label": "Turn {n}",
|
|
870
843
|
"pos.of": "Message {n} of {m}",
|
|
844
|
+
/** Collapsed-summary dot position: the message RANGE the dot represents. */
|
|
845
|
+
"pos.range": "Messages {a}–{b} of {m}",
|
|
871
846
|
"search.placeholder": "Search message content",
|
|
872
847
|
"search.label": "Search messages",
|
|
873
848
|
"bookmark.filter": "Bookmarks only",
|
|
@@ -898,6 +873,8 @@ window.__ModuleLoader__.load({
|
|
|
898
873
|
"list.open": "Open list",
|
|
899
874
|
"list.close": "Close list",
|
|
900
875
|
"list.label": "All prompts",
|
|
876
|
+
/** Bottom hint of the all-prompts list while it drains older pages. */
|
|
877
|
+
"list.loading": "Loading earlier messages…",
|
|
901
878
|
"search.cross": "Cross-session search",
|
|
902
879
|
"search.cross.open": "Open cross-session search",
|
|
903
880
|
"search.cross.close": "Close cross-session search",
|
|
@@ -967,39 +944,23 @@ window.__ModuleLoader__.load({
|
|
|
967
944
|
"settings.lang.system": "Follow system",
|
|
968
945
|
"settings.lang.zh": "Chinese",
|
|
969
946
|
"settings.lang.en": "English",
|
|
970
|
-
"
|
|
971
|
-
"
|
|
972
|
-
"
|
|
973
|
-
"
|
|
974
|
-
"
|
|
975
|
-
"
|
|
976
|
-
"
|
|
977
|
-
"
|
|
978
|
-
"
|
|
979
|
-
"
|
|
980
|
-
"
|
|
981
|
-
"
|
|
982
|
-
"
|
|
983
|
-
"
|
|
984
|
-
"
|
|
985
|
-
"
|
|
986
|
-
"
|
|
987
|
-
"onboarding.step3.desc1": "Tune the accent color, dot size, edge distance, and left/right side — saved as you adjust.",
|
|
988
|
-
"onboarding.step3.desc2": "Switch the UI language, compose your focus effect, or pin features outside the collapse.",
|
|
989
|
-
"onboarding.step4.title": "Updates & support",
|
|
990
|
-
"onboarding.step4.desc1": "A silent check runs after mount; when a new version exists, the toolbar shows a hint.",
|
|
991
|
-
"onboarding.step4.desc2": "Enjoying the plugin? Star us, file an issue, or upgrade via npm.",
|
|
992
|
-
"onboarding.demo.m1": "Help me optimize this code",
|
|
993
|
-
"onboarding.demo.m2": "Explain this error",
|
|
994
|
-
"onboarding.demo.m3": "Check the edge cases again",
|
|
995
|
-
"onboarding.demo.m4": "Write a unit test",
|
|
996
|
-
"onboarding.demo.m5": "Summarize the plan",
|
|
997
|
-
"onboarding.demo.hoverHint": "Try hovering and clicking the dots",
|
|
998
|
-
"onboarding.demo.searchHint": "Try typing “error”, or tap the star for bookmarks-only",
|
|
999
|
-
"onboarding.demo.settingsPath": "Expand arrow → gear → Settings",
|
|
1000
|
-
"onboarding.demo.focusMix": "Focus mix",
|
|
1001
|
-
"onboarding.demo.pin": "Pin keys",
|
|
1002
|
-
"onboarding.reopen": "Replay the tour"
|
|
947
|
+
"tour.welcome.title": "Welcome to dsh-milestone",
|
|
948
|
+
"tour.welcome.desc": "This is your session milestone rail: every prompt is a dot — click to jump, hover for metadata.",
|
|
949
|
+
"tour.step1.title": "Expand the toolbar",
|
|
950
|
+
"tour.step1.desc": "Click to reveal every function key: search, list, bookmarks, focus, update check, and settings.",
|
|
951
|
+
"tour.step2.title": "Open settings",
|
|
952
|
+
"tour.step2.desc": "Open settings: pin your favorite keys, personalize the look, switch language, and tune focus.",
|
|
953
|
+
"tour.step3.title": "Dot navigation",
|
|
954
|
+
"tour.step3.desc": "Each dot is one prompt — click to jump, hover for time, model, and usage.",
|
|
955
|
+
"tour.step4.title": "Get started",
|
|
956
|
+
"tour.step4.desc": "Go try it — questions and issues are welcome on GitHub.",
|
|
957
|
+
"tour.start": "Get started",
|
|
958
|
+
"tour.next": "Next",
|
|
959
|
+
"tour.prev": "Back",
|
|
960
|
+
"tour.skip": "Skip",
|
|
961
|
+
"tour.step.label": "Step {n} of 5",
|
|
962
|
+
"tour.step.short": "Step {n}",
|
|
963
|
+
"tour.reopen": "Replay the tour"
|
|
1003
964
|
};
|
|
1004
965
|
/**
|
|
1005
966
|
* Interpolate `{name}` placeholders with params, matching the harness t seat's
|
|
@@ -1022,6 +983,31 @@ window.__ModuleLoader__.load({
|
|
|
1022
983
|
//#endregion
|
|
1023
984
|
//#region src/client/turn-group-logic.ts
|
|
1024
985
|
/**
|
|
986
|
+
* Renumber the raw harness turn numbers into a compact 1-based DISPLAY
|
|
987
|
+
* sequence over the marks that actually render. The harness numbers every
|
|
988
|
+
* engine turn (subagent/injected turns included), so the raw numbers show
|
|
989
|
+
* gaps (turns that produced no user mark) and repeats (several marks sharing
|
|
990
|
+
* one turn); labels fed through this map read as clean 1, 2, 3, … rounds.
|
|
991
|
+
*
|
|
992
|
+
* Grouping and collapse logic keep operating on the RAW turn (same-turn marks
|
|
993
|
+
* are contiguous in rail order, so first-appearance ranking preserves the
|
|
994
|
+
* partition) — only labels consume this map.
|
|
995
|
+
*
|
|
996
|
+
* @param marks - marks in rail order (only `turn` is consulted).
|
|
997
|
+
* @returns raw turn -> display round (1-based), in first-appearance order;
|
|
998
|
+
* turns that never appear (or marks without turn info) get no entry.
|
|
999
|
+
*/
|
|
1000
|
+
function buildDisplayTurns(marks) {
|
|
1001
|
+
const display = /* @__PURE__ */ new Map();
|
|
1002
|
+
let rank = 0;
|
|
1003
|
+
for (const mark of marks) {
|
|
1004
|
+
if (mark.turn === void 0) continue;
|
|
1005
|
+
if (display.has(mark.turn)) continue;
|
|
1006
|
+
display.set(mark.turn, ++rank);
|
|
1007
|
+
}
|
|
1008
|
+
return display;
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1025
1011
|
* Partition consecutive marks by turn. Marks with the same numeric turn that
|
|
1026
1012
|
* appear one after another share a group; each mark with `turn === undefined`
|
|
1027
1013
|
* becomes its own singleton group with `turn: null`.
|
|
@@ -1334,9 +1320,10 @@ window.__ModuleLoader__.load({
|
|
|
1334
1320
|
/**
|
|
1335
1321
|
* @param props - the panel anchor, the full marks array, and the rail's jump handler.
|
|
1336
1322
|
*/
|
|
1337
|
-
function MilestoneListPanel({ panelTop, panelRight, marks, onJump, onClose, t }) {
|
|
1323
|
+
function MilestoneListPanel({ panelTop, panelRight, marks, onJump, onClose, loading = false, t }) {
|
|
1338
1324
|
const panelRef = (0, react.useRef)(null);
|
|
1339
1325
|
useOutsideDismiss(panelRef, true, onClose, { exclude: (target) => outsideDismissMatches(target, "[data-list-toggle]") });
|
|
1326
|
+
const displayTurns = (0, react.useMemo)(() => buildDisplayTurns(marks), [marks]);
|
|
1340
1327
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1341
1328
|
ref: panelRef,
|
|
1342
1329
|
"data-milestone-list": true,
|
|
@@ -1376,7 +1363,7 @@ window.__ModuleLoader__.load({
|
|
|
1376
1363
|
children: marks.length
|
|
1377
1364
|
})]
|
|
1378
1365
|
}),
|
|
1379
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1366
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1380
1367
|
style: {
|
|
1381
1368
|
maxHeight: 300,
|
|
1382
1369
|
overflowY: "auto",
|
|
@@ -1384,7 +1371,7 @@ window.__ModuleLoader__.load({
|
|
|
1384
1371
|
flexDirection: "column",
|
|
1385
1372
|
gap: 2
|
|
1386
1373
|
},
|
|
1387
|
-
children: marks.map((mark, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1374
|
+
children: [marks.map((mark, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1388
1375
|
type: "button",
|
|
1389
1376
|
"data-list-item": true,
|
|
1390
1377
|
"data-jump-key": mark.key,
|
|
@@ -1411,7 +1398,7 @@ window.__ModuleLoader__.load({
|
|
|
1411
1398
|
children: [t("pos.of", {
|
|
1412
1399
|
n: i + 1,
|
|
1413
1400
|
m: marks.length
|
|
1414
|
-
}), mark.turn !== void 0 ? ` · ${t("turn.label", { n: mark.turn })}` : null]
|
|
1401
|
+
}), mark.turn !== void 0 ? ` · ${t("turn.label", { n: displayTurns.get(mark.turn) ?? mark.turn })}` : null]
|
|
1415
1402
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1416
1403
|
style: {
|
|
1417
1404
|
fontSize: 13,
|
|
@@ -1422,27 +1409,20 @@ window.__ModuleLoader__.load({
|
|
|
1422
1409
|
},
|
|
1423
1410
|
children: mark.preview || t("no.text")
|
|
1424
1411
|
})]
|
|
1425
|
-
}, mark.key))
|
|
1412
|
+
}, mark.key)), loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1413
|
+
"data-list-loading": true,
|
|
1414
|
+
style: {
|
|
1415
|
+
fontSize: 12,
|
|
1416
|
+
color: "#8b96ab",
|
|
1417
|
+
textAlign: "center",
|
|
1418
|
+
padding: "6px 8px"
|
|
1419
|
+
},
|
|
1420
|
+
children: t("list.loading")
|
|
1421
|
+
})]
|
|
1426
1422
|
})
|
|
1427
1423
|
]
|
|
1428
1424
|
});
|
|
1429
1425
|
}
|
|
1430
|
-
//#endregion
|
|
1431
|
-
//#region src/client/modal-tokens.ts
|
|
1432
|
-
/**
|
|
1433
|
-
* modal-tokens: the ONE source for the settings modal's dark-panel palette and
|
|
1434
|
-
* radius scale, shared with the 0.6.4 onboarding tutorial modal so the two
|
|
1435
|
-
* dialogs cannot drift (same dark panel on a dark host, same three text tiers,
|
|
1436
|
-
* same 12px panel / 8px control radius, same border tone).
|
|
1437
|
-
*
|
|
1438
|
-
* The static MODAL_CSS block (hover/focus-visible/reduced-motion rules) stays
|
|
1439
|
-
* in MilestoneRail.tsx because it also styles the settings modal's own chrome
|
|
1440
|
-
* (`[data-support-card]`, `[data-toolbar-*]`, ...); the onboarding modal
|
|
1441
|
-
* carries its own small ONBOARDING_CSS block for its own controls, built from
|
|
1442
|
-
* these same tokens. MilestoneRail.tsx imports these constants verbatim so a
|
|
1443
|
-
* future palette change edits exactly one file.
|
|
1444
|
-
*/
|
|
1445
|
-
const MODAL_BG = "rgba(20, 24, 32, 0.98)";
|
|
1446
1426
|
const MODAL_FG = "#e6e8ee";
|
|
1447
1427
|
const MODAL_TITLE = "#c7cede";
|
|
1448
1428
|
const MODAL_TEXT = "#b9c2d4";
|
|
@@ -1488,610 +1468,137 @@ window.__ModuleLoader__.load({
|
|
|
1488
1468
|
} catch {}
|
|
1489
1469
|
}
|
|
1490
1470
|
//#endregion
|
|
1491
|
-
//#region src/client/
|
|
1492
|
-
/**
|
|
1493
|
-
* Installed plugin version. Injected at build time as
|
|
1494
|
-
* `__DSH_MILESTONE_VERSION__`; falls back to `0.0.0-dev` when unbuilt.
|
|
1495
|
-
*/
|
|
1496
|
-
const PLUGIN_VERSION = "0.6.4";
|
|
1497
|
-
/** Main repository of dsh-milestone (as referenced by README.md). */
|
|
1498
|
-
const PLUGIN_REPO_URL = "https://github.com/SnowCrescenter-tech/dsh-milestone";
|
|
1499
|
-
/** npm package page (install channel promoted in the settings menu). */
|
|
1500
|
-
const PLUGIN_NPM_URL = "https://www.npmjs.com/package/dsh-milestone";
|
|
1501
|
-
//#endregion
|
|
1502
|
-
//#region src/client/MilestoneOnboarding.tsx
|
|
1471
|
+
//#region src/client/MilestoneTour.tsx
|
|
1503
1472
|
/**
|
|
1504
|
-
*
|
|
1473
|
+
* MilestoneTour: the 0.6.5 first-run coach-bubble tutorial.
|
|
1474
|
+
*
|
|
1475
|
+
* Unlike the deleted 0.6.4 MilestoneOnboarding modal (built-in toy demos on a
|
|
1476
|
+
* fake overlay), this tour never fakes anything. Each of the 5 bubbles anchors
|
|
1477
|
+
* a REAL rail element (`data-rail-list`, `data-toolbar-expand`,
|
|
1478
|
+
* `data-toolbar-settings`, the first `data-rail-dot`, the rail root), explains
|
|
1479
|
+
* what clicking that control ACTUALLY does, and auto-advances by OBSERVING the
|
|
1480
|
+
* live state the rail already owns:
|
|
1481
|
+
* - bubble 1 (expand arrow) waits for `toolbarExpanded` → true — the user
|
|
1482
|
+
* really clicked the arrow (「下一步」 remains a fallback);
|
|
1483
|
+
* - bubble 2 (settings gear) waits for `settingsOpen` → true — the settings
|
|
1484
|
+
* panel appearing IS the proof — and SUSPENDS while the modal is open
|
|
1485
|
+
* (state is kept; closing settings resumes the bubble at the current step);
|
|
1486
|
+
* - bubble 3 (dots) needs no interaction — hover/click are explained.
|
|
1505
1487
|
*
|
|
1506
1488
|
* Trigger/ownership lives in MilestoneRail (mount timer + settings reopen);
|
|
1507
|
-
* this component is
|
|
1508
|
-
*
|
|
1509
|
-
* close
|
|
1510
|
-
* `dsh-milestone.onboarded` flag itself via onboarding-store.
|
|
1489
|
+
* this component is presentation + state machine only. It persists the
|
|
1490
|
+
* `dsh-milestone.onboarded` flag itself via onboarding-store on IMPRESSION
|
|
1491
|
+
* (showing bubble 0 is enough) and on every close path (skip / Esc / finish).
|
|
1511
1492
|
*
|
|
1512
|
-
*
|
|
1513
|
-
*
|
|
1514
|
-
*
|
|
1515
|
-
*
|
|
1516
|
-
*
|
|
1493
|
+
* Positioning: `position: fixed`, opened on the target's FREE side (rail on
|
|
1494
|
+
* the right → bubble left of the target; rail on the left → bubble right of
|
|
1495
|
+
* it), measured with getBoundingClientRect and clamped to the viewport.
|
|
1496
|
+
* Re-measured on step changes, whenever the anchor may have (un)mounted (the
|
|
1497
|
+
* forced toolbar expansion), and on window resize. There is NO overlay and the
|
|
1498
|
+
* bubble never covers its own target — the user is supposed to CLICK the real
|
|
1499
|
+
* controls, so pointer-events live only on the bubble box itself.
|
|
1517
1500
|
*
|
|
1518
|
-
*
|
|
1519
|
-
*
|
|
1520
|
-
*
|
|
1521
|
-
*
|
|
1522
|
-
*
|
|
1523
|
-
*
|
|
1524
|
-
*
|
|
1501
|
+
* Anchor guarantee: entering any step ≥ 2 calls `onSetToolbarExpanded(true)`
|
|
1502
|
+
* because `data-toolbar-settings` renders only while the toolbar is expanded
|
|
1503
|
+
* (or the gear pinned) — the 「下一步」 fallback path lands there un-expanded.
|
|
1504
|
+
*
|
|
1505
|
+
* Visual language reuses modal-tokens (MODAL_TIP_BG panel, one border tone,
|
|
1506
|
+
* the 12/8px radius scale, three text tiers) and the rail root's inherited
|
|
1507
|
+
* `--ms-accent*` CSS variables. The highlight ring is zero-asset: a
|
|
1508
|
+
* `data-tour-highlight` attribute + one injected CSS rule.
|
|
1525
1509
|
*/
|
|
1526
|
-
/**
|
|
1510
|
+
/** The tour bubble floats above every other rail layer (settings overlay 105). */
|
|
1511
|
+
const TOUR_Z = 106;
|
|
1512
|
+
/** Gap between the target element and the bubble (px). */
|
|
1513
|
+
const TOUR_GAP = 12;
|
|
1514
|
+
/** Bubble width (px) — the left-edge positioning math clamps against it. */
|
|
1515
|
+
const TOUR_WIDTH = 280;
|
|
1516
|
+
/** Minimum padding between the bubble and the viewport edges (px). */
|
|
1517
|
+
const VIEWPORT_PAD = 8;
|
|
1518
|
+
/** Fallback bubble height used when the layout has not settled (px). */
|
|
1519
|
+
const TOUR_HEIGHT_FALLBACK = 120;
|
|
1520
|
+
/** Progress-dot count — the 5 bubble steps (0 = welcome … 4 = done). */
|
|
1527
1521
|
const STEP_COUNT = [
|
|
1522
|
+
0,
|
|
1528
1523
|
1,
|
|
1529
1524
|
2,
|
|
1530
1525
|
3,
|
|
1531
1526
|
4
|
|
1532
1527
|
];
|
|
1533
|
-
/** The onboarding modal floats above every other rail layer (settings 105). */
|
|
1534
|
-
const ONBOARDING_Z = 110;
|
|
1535
|
-
/** Fake messages the step-1/step-2 demos run on (zh AND en keys exist). */
|
|
1536
|
-
const DEMO_MESSAGE_KEYS = [
|
|
1537
|
-
"onboarding.demo.m1",
|
|
1538
|
-
"onboarding.demo.m2",
|
|
1539
|
-
"onboarding.demo.m3",
|
|
1540
|
-
"onboarding.demo.m4",
|
|
1541
|
-
"onboarding.demo.m5"
|
|
1542
|
-
];
|
|
1543
|
-
/** Fake per-turn durations for the step-1 hover tooltip (unit text is locale-neutral). */
|
|
1544
|
-
const DEMO_DURATIONS = [
|
|
1545
|
-
"6.2s",
|
|
1546
|
-
"13.4s",
|
|
1547
|
-
"5.1s",
|
|
1548
|
-
"22.0s",
|
|
1549
|
-
"9.8s"
|
|
1550
|
-
];
|
|
1551
1528
|
/**
|
|
1552
|
-
* Static
|
|
1553
|
-
*
|
|
1554
|
-
*
|
|
1555
|
-
*
|
|
1556
|
-
* come from the rail root's CSS variables (`--ms-accent*`), inherited into
|
|
1557
|
-
* this fixed layer because the overlay renders inside the rail root.
|
|
1529
|
+
* Static tour styling that `:hover`/`:focus-visible`/reduced-motion need (the
|
|
1530
|
+
* `:focus-visible` ring and the base-state backgrounds inline styles cannot
|
|
1531
|
+
* duplicate). BASE-states first — the UA default button face must never flood
|
|
1532
|
+
* through on the dark bubble (that exact bug already shipped once).
|
|
1558
1533
|
*/
|
|
1559
|
-
const
|
|
1560
|
-
@keyframes ms-
|
|
1561
|
-
[data-
|
|
1562
|
-
|
|
1563
|
-
[data-
|
|
1564
|
-
[data-
|
|
1565
|
-
[data-
|
|
1566
|
-
|
|
1567
|
-
[data-
|
|
1534
|
+
const TOUR_CSS = `
|
|
1535
|
+
@keyframes ms-tour-fade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
|
|
1536
|
+
[data-tour-bubble] { animation: ms-tour-fade 160ms ease; }
|
|
1537
|
+
/* BASE-state backgrounds for every bubble control (transparent or themed). */
|
|
1538
|
+
[data-tour-skip] { background: transparent; }
|
|
1539
|
+
[data-tour-prev] { background: rgba(255, 255, 255, 0.06); }
|
|
1540
|
+
[data-tour-primary] { background: var(--ms-accent-bg); color: var(--ms-accent-soft); }
|
|
1541
|
+
/* Hover washes on top of the explicit base states. */
|
|
1542
|
+
[data-tour-skip]:hover { background: rgba(255, 255, 255, 0.08); }
|
|
1543
|
+
[data-tour-prev]:hover:not(:disabled) { background: rgba(255, 255, 255, 0.12); }
|
|
1544
|
+
[data-tour-primary]:hover { background: rgba(255, 255, 255, 0.14); }
|
|
1545
|
+
/* ONE accent focus ring for keyboard use. */
|
|
1546
|
+
[data-tour-skip]:focus-visible, [data-tour-prev]:focus-visible, [data-tour-primary]:focus-visible {
|
|
1568
1547
|
box-shadow: 0 0 0 2px var(--ms-accent-soft);
|
|
1569
1548
|
}
|
|
1570
|
-
|
|
1571
|
-
[data-
|
|
1549
|
+
/* The real-target highlight ring: accent halo + a subtle scale hint. */
|
|
1550
|
+
[data-tour-highlight] {
|
|
1551
|
+
box-shadow: 0 0 0 2px var(--ms-accent-soft), 0 0 0 4px var(--ms-accent);
|
|
1552
|
+
transform: scale(1.04);
|
|
1553
|
+
transition: box-shadow 140ms ease, transform 140ms ease;
|
|
1554
|
+
}
|
|
1572
1555
|
@media (prefers-reduced-motion: reduce) {
|
|
1573
|
-
[data-
|
|
1574
|
-
[data-
|
|
1575
|
-
[data-
|
|
1556
|
+
[data-tour-bubble] { animation: none; }
|
|
1557
|
+
[data-tour-highlight] { transform: none; transition: none; }
|
|
1558
|
+
[data-tour-skip], [data-tour-prev], [data-tour-primary] { transition: none; }
|
|
1576
1559
|
}
|
|
1577
1560
|
`;
|
|
1578
|
-
/**
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
color: "var(--ms-accent-soft)",
|
|
1582
|
-
border: "1px solid var(--ms-accent)",
|
|
1583
|
-
borderRadius: 8,
|
|
1584
|
-
padding: "7px 18px",
|
|
1585
|
-
fontSize: 13,
|
|
1586
|
-
fontWeight: 600,
|
|
1587
|
-
cursor: "pointer"
|
|
1588
|
-
};
|
|
1589
|
-
/** Secondary dialog action — back / welcome skip. */
|
|
1590
|
-
const GHOST_BUTTON = {
|
|
1591
|
-
background: "rgba(255, 255, 255, 0.06)",
|
|
1592
|
-
color: MODAL_TEXT,
|
|
1593
|
-
border: `1px solid ${MODAL_BORDER}`,
|
|
1594
|
-
borderRadius: 8,
|
|
1595
|
-
padding: "7px 16px",
|
|
1596
|
-
fontSize: 12.5,
|
|
1597
|
-
cursor: "pointer"
|
|
1598
|
-
};
|
|
1599
|
-
/** Corner skip (step pages) — the quietest of the three button tiers. */
|
|
1600
|
-
const SKIP_BUTTON = {
|
|
1601
|
-
background: "transparent",
|
|
1602
|
-
color: MODAL_HINT,
|
|
1603
|
-
border: "none",
|
|
1604
|
-
padding: "4px 8px",
|
|
1605
|
-
borderRadius: 8,
|
|
1606
|
-
fontSize: 12,
|
|
1607
|
-
cursor: "pointer"
|
|
1608
|
-
};
|
|
1609
|
-
/** Resolve the five fake demo messages through the active locale. */
|
|
1610
|
-
function demoMessages(t) {
|
|
1611
|
-
return DEMO_MESSAGE_KEYS.map((key) => t(key));
|
|
1612
|
-
}
|
|
1613
|
-
/**
|
|
1614
|
-
* Step-1 demo: a toy dot rail with the REAL dot visual — accent gradient dots,
|
|
1615
|
-
* hover enlarges + shows a fake metadata tooltip (position + duration), click
|
|
1616
|
-
* marks the dot with the white ring. All local state; nothing touches real
|
|
1617
|
-
* session data.
|
|
1618
|
-
*/
|
|
1619
|
-
function DemoDotRail({ t, accent }) {
|
|
1620
|
-
const messages = demoMessages(t);
|
|
1621
|
-
const [hoverIndex, setHoverIndex] = (0, react.useState)(null);
|
|
1622
|
-
const [activeIndex, setActiveIndex] = (0, react.useState)(null);
|
|
1623
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1624
|
-
"data-demo-dots": true,
|
|
1625
|
-
children: [
|
|
1626
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1627
|
-
"data-demo-dot-row": true,
|
|
1628
|
-
style: {
|
|
1629
|
-
display: "flex",
|
|
1630
|
-
alignItems: "center",
|
|
1631
|
-
justifyContent: "center",
|
|
1632
|
-
gap: 22
|
|
1633
|
-
},
|
|
1634
|
-
children: messages.map((msg, i) => {
|
|
1635
|
-
const hovered = hoverIndex === i;
|
|
1636
|
-
const active = activeIndex === i;
|
|
1637
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1638
|
-
type: "button",
|
|
1639
|
-
"data-demo-dot": i,
|
|
1640
|
-
"data-active": active ? "true" : void 0,
|
|
1641
|
-
"aria-label": msg,
|
|
1642
|
-
"aria-pressed": active,
|
|
1643
|
-
onMouseEnter: () => setHoverIndex(i),
|
|
1644
|
-
onMouseLeave: () => setHoverIndex(null),
|
|
1645
|
-
onFocus: () => setHoverIndex(i),
|
|
1646
|
-
onBlur: () => setHoverIndex(null),
|
|
1647
|
-
onClick: () => setActiveIndex(i),
|
|
1648
|
-
style: {
|
|
1649
|
-
width: 36,
|
|
1650
|
-
height: 36,
|
|
1651
|
-
display: "flex",
|
|
1652
|
-
alignItems: "center",
|
|
1653
|
-
justifyContent: "center",
|
|
1654
|
-
background: "transparent",
|
|
1655
|
-
border: "none",
|
|
1656
|
-
padding: 0,
|
|
1657
|
-
cursor: "pointer",
|
|
1658
|
-
borderRadius: "50%"
|
|
1659
|
-
},
|
|
1660
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
|
|
1661
|
-
width: 13,
|
|
1662
|
-
height: 13,
|
|
1663
|
-
borderRadius: "50%",
|
|
1664
|
-
background: dotColor(i, messages.length, accent),
|
|
1665
|
-
boxShadow: active ? "0 0 0 3px rgba(255, 255, 255, 0.9)" : hovered ? "0 0 0 3px rgba(255, 255, 255, 0.35)" : "none",
|
|
1666
|
-
transform: hovered || active ? "scale(1.35)" : "scale(1)",
|
|
1667
|
-
transition: "transform 120ms ease, box-shadow 120ms ease"
|
|
1668
|
-
} })
|
|
1669
|
-
}, msg);
|
|
1670
|
-
})
|
|
1671
|
-
}),
|
|
1672
|
-
hoverIndex !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1673
|
-
"data-demo-tooltip": true,
|
|
1674
|
-
style: {
|
|
1675
|
-
marginTop: 8,
|
|
1676
|
-
textAlign: "center",
|
|
1677
|
-
fontSize: 12,
|
|
1678
|
-
color: "#b9c2d4",
|
|
1679
|
-
lineHeight: 1.5
|
|
1680
|
-
},
|
|
1681
|
-
children: [
|
|
1682
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("pos.of", {
|
|
1683
|
-
n: hoverIndex + 1,
|
|
1684
|
-
m: messages.length
|
|
1685
|
-
}) }),
|
|
1686
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: " · " }),
|
|
1687
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("duration.label", { name: DEMO_DURATIONS[hoverIndex] }) })
|
|
1688
|
-
]
|
|
1689
|
-
}),
|
|
1690
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1691
|
-
"data-demo-hint": true,
|
|
1692
|
-
style: {
|
|
1693
|
-
marginTop: 8,
|
|
1694
|
-
textAlign: "center",
|
|
1695
|
-
fontSize: 12,
|
|
1696
|
-
color: MODAL_HINT
|
|
1697
|
-
},
|
|
1698
|
-
children: t("onboarding.demo.hoverHint")
|
|
1699
|
-
})
|
|
1700
|
-
]
|
|
1701
|
-
});
|
|
1702
|
-
}
|
|
1703
|
-
/**
|
|
1704
|
-
* Step-2 demo: a toy search box that filters the demo messages (N / M count)
|
|
1705
|
-
* plus one bookmark star — active = solid star + "bookmarks only" row list.
|
|
1706
|
-
* Local state only; the real rail/search/bookmarks are untouched.
|
|
1707
|
-
*/
|
|
1708
|
-
function DemoSearch({ t, accent }) {
|
|
1709
|
-
const messages = demoMessages(t);
|
|
1710
|
-
const starIndex = 1;
|
|
1711
|
-
const [query, setQuery] = (0, react.useState)("");
|
|
1712
|
-
const [favOnly, setFavOnly] = (0, react.useState)(false);
|
|
1713
|
-
const lower = query.trim().toLowerCase();
|
|
1714
|
-
const visible = favOnly ? messages.filter((_, i) => i === starIndex) : messages;
|
|
1715
|
-
const matches = lower === "" ? visible.length : visible.filter((m) => m.toLowerCase().includes(lower)).length;
|
|
1716
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1717
|
-
"data-demo-search": true,
|
|
1718
|
-
children: [
|
|
1719
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1720
|
-
style: {
|
|
1721
|
-
display: "flex",
|
|
1722
|
-
alignItems: "center",
|
|
1723
|
-
gap: 8
|
|
1724
|
-
},
|
|
1725
|
-
children: [
|
|
1726
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1727
|
-
"data-demo-search-input": true,
|
|
1728
|
-
type: "text",
|
|
1729
|
-
value: query,
|
|
1730
|
-
onChange: (e) => setQuery(e.target.value),
|
|
1731
|
-
placeholder: t("search.placeholder"),
|
|
1732
|
-
"aria-label": t("search.label"),
|
|
1733
|
-
style: {
|
|
1734
|
-
flex: 1,
|
|
1735
|
-
minWidth: 0,
|
|
1736
|
-
padding: "6px 10px",
|
|
1737
|
-
borderRadius: 8,
|
|
1738
|
-
border: `1px solid ${MODAL_BORDER}`,
|
|
1739
|
-
background: "rgba(255, 255, 255, 0.05)",
|
|
1740
|
-
color: MODAL_FG,
|
|
1741
|
-
fontSize: 12.5,
|
|
1742
|
-
outline: "none"
|
|
1743
|
-
}
|
|
1744
|
-
}),
|
|
1745
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1746
|
-
"data-demo-search-count": true,
|
|
1747
|
-
style: {
|
|
1748
|
-
fontSize: 12,
|
|
1749
|
-
color: MODAL_TEXT,
|
|
1750
|
-
whiteSpace: "nowrap"
|
|
1751
|
-
},
|
|
1752
|
-
children: [
|
|
1753
|
-
matches,
|
|
1754
|
-
" / ",
|
|
1755
|
-
visible.length
|
|
1756
|
-
]
|
|
1757
|
-
}),
|
|
1758
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1759
|
-
type: "button",
|
|
1760
|
-
"data-demo-star": true,
|
|
1761
|
-
"aria-pressed": favOnly,
|
|
1762
|
-
"aria-label": t("bookmark.filter"),
|
|
1763
|
-
title: t("bookmark.filter"),
|
|
1764
|
-
onClick: () => setFavOnly((v) => !v),
|
|
1765
|
-
style: {
|
|
1766
|
-
width: 30,
|
|
1767
|
-
height: 30,
|
|
1768
|
-
flexShrink: 0,
|
|
1769
|
-
display: "flex",
|
|
1770
|
-
alignItems: "center",
|
|
1771
|
-
justifyContent: "center",
|
|
1772
|
-
background: favOnly ? "var(--ms-accent-bg)" : "transparent",
|
|
1773
|
-
border: `1px solid ${MODAL_BORDER}`,
|
|
1774
|
-
borderRadius: 8,
|
|
1775
|
-
padding: 0,
|
|
1776
|
-
cursor: "pointer",
|
|
1777
|
-
color: favOnly ? "var(--ms-accent-soft)" : MODAL_HINT
|
|
1778
|
-
},
|
|
1779
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1780
|
-
width: "14",
|
|
1781
|
-
height: "14",
|
|
1782
|
-
viewBox: "0 0 24 24",
|
|
1783
|
-
fill: favOnly ? "currentColor" : "none",
|
|
1784
|
-
stroke: "currentColor",
|
|
1785
|
-
strokeWidth: "2",
|
|
1786
|
-
strokeLinejoin: "round",
|
|
1787
|
-
"aria-hidden": "true",
|
|
1788
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m12 2 3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" })
|
|
1789
|
-
})
|
|
1790
|
-
})
|
|
1791
|
-
]
|
|
1792
|
-
}),
|
|
1793
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1794
|
-
"data-demo-search-rows": true,
|
|
1795
|
-
style: {
|
|
1796
|
-
display: "flex",
|
|
1797
|
-
flexDirection: "column",
|
|
1798
|
-
gap: 6,
|
|
1799
|
-
marginTop: 10
|
|
1800
|
-
},
|
|
1801
|
-
children: visible.map((msg, i) => {
|
|
1802
|
-
const realIndex = favOnly ? starIndex : i;
|
|
1803
|
-
const lit = lower === "" || msg.toLowerCase().includes(lower);
|
|
1804
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1805
|
-
"data-demo-search-row": true,
|
|
1806
|
-
"data-index": realIndex,
|
|
1807
|
-
"data-lit": lit ? "true" : "false",
|
|
1808
|
-
style: {
|
|
1809
|
-
display: "flex",
|
|
1810
|
-
alignItems: "center",
|
|
1811
|
-
gap: 8,
|
|
1812
|
-
fontSize: 12,
|
|
1813
|
-
color: lit ? MODAL_FG : MODAL_HINT,
|
|
1814
|
-
opacity: lit ? 1 : .45,
|
|
1815
|
-
transition: "opacity 120ms ease"
|
|
1816
|
-
},
|
|
1817
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1818
|
-
"aria-hidden": "true",
|
|
1819
|
-
style: {
|
|
1820
|
-
width: 8,
|
|
1821
|
-
height: 8,
|
|
1822
|
-
flexShrink: 0,
|
|
1823
|
-
borderRadius: "50%",
|
|
1824
|
-
background: dotColor(realIndex, messages.length, accent)
|
|
1825
|
-
}
|
|
1826
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: msg })]
|
|
1827
|
-
}, msg);
|
|
1828
|
-
})
|
|
1829
|
-
}),
|
|
1830
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1831
|
-
"data-demo-search-hint": true,
|
|
1832
|
-
style: {
|
|
1833
|
-
marginTop: 8,
|
|
1834
|
-
textAlign: "center",
|
|
1835
|
-
fontSize: 12,
|
|
1836
|
-
color: MODAL_HINT
|
|
1837
|
-
},
|
|
1838
|
-
children: t("onboarding.demo.searchHint")
|
|
1839
|
-
})
|
|
1840
|
-
]
|
|
1841
|
-
});
|
|
1842
|
-
}
|
|
1843
|
-
/**
|
|
1844
|
-
* Step-3 demo: a static legend of the personalization controls (accent / size /
|
|
1845
|
-
* distance / side / language / focus mix / pin), each with a tiny aria-hidden
|
|
1846
|
-
* visual, plus the 展开箭头 → 齿轮 → 设置 path hint.
|
|
1847
|
-
*/
|
|
1848
|
-
function DemoSettings({ t, accent }) {
|
|
1849
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1850
|
-
"data-demo-settings": true,
|
|
1851
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1852
|
-
style: {
|
|
1853
|
-
display: "flex",
|
|
1854
|
-
flexDirection: "column",
|
|
1855
|
-
gap: 6
|
|
1856
|
-
},
|
|
1857
|
-
children: [
|
|
1858
|
-
{
|
|
1859
|
-
label: t("settings.accent"),
|
|
1860
|
-
visual: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1861
|
-
"aria-hidden": "true",
|
|
1862
|
-
style: {
|
|
1863
|
-
width: 10,
|
|
1864
|
-
height: 10,
|
|
1865
|
-
borderRadius: "50%",
|
|
1866
|
-
background: accent
|
|
1867
|
-
}
|
|
1868
|
-
})
|
|
1869
|
-
},
|
|
1870
|
-
{
|
|
1871
|
-
label: t("settings.iconSize"),
|
|
1872
|
-
visual: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1873
|
-
"aria-hidden": "true",
|
|
1874
|
-
style: {
|
|
1875
|
-
display: "flex",
|
|
1876
|
-
alignItems: "flex-end",
|
|
1877
|
-
gap: 2
|
|
1878
|
-
},
|
|
1879
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
|
|
1880
|
-
width: 7,
|
|
1881
|
-
height: 7,
|
|
1882
|
-
borderRadius: "50%",
|
|
1883
|
-
background: MODAL_HINT
|
|
1884
|
-
} }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
|
|
1885
|
-
width: 12,
|
|
1886
|
-
height: 12,
|
|
1887
|
-
borderRadius: "50%",
|
|
1888
|
-
background: MODAL_TEXT
|
|
1889
|
-
} })]
|
|
1890
|
-
})
|
|
1891
|
-
},
|
|
1892
|
-
{
|
|
1893
|
-
label: t("settings.inset"),
|
|
1894
|
-
visual: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1895
|
-
"aria-hidden": "true",
|
|
1896
|
-
style: {
|
|
1897
|
-
width: 12,
|
|
1898
|
-
height: 12,
|
|
1899
|
-
border: `1px solid ${MODAL_BORDER}`,
|
|
1900
|
-
borderRadius: 2
|
|
1901
|
-
}
|
|
1902
|
-
})
|
|
1903
|
-
},
|
|
1904
|
-
{
|
|
1905
|
-
label: t("settings.side"),
|
|
1906
|
-
visual: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1907
|
-
"aria-hidden": "true",
|
|
1908
|
-
style: {
|
|
1909
|
-
fontSize: 14,
|
|
1910
|
-
color: MODAL_TEXT
|
|
1911
|
-
},
|
|
1912
|
-
children: "⇄"
|
|
1913
|
-
})
|
|
1914
|
-
},
|
|
1915
|
-
{
|
|
1916
|
-
label: t("settings.language"),
|
|
1917
|
-
visual: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1918
|
-
"aria-hidden": "true",
|
|
1919
|
-
style: {
|
|
1920
|
-
fontSize: 11,
|
|
1921
|
-
color: MODAL_TEXT
|
|
1922
|
-
},
|
|
1923
|
-
children: "中 / EN"
|
|
1924
|
-
})
|
|
1925
|
-
},
|
|
1926
|
-
{
|
|
1927
|
-
label: t("onboarding.demo.focusMix"),
|
|
1928
|
-
visual: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
1929
|
-
"aria-hidden": "true",
|
|
1930
|
-
width: "14",
|
|
1931
|
-
height: "10",
|
|
1932
|
-
viewBox: "0 0 24 24",
|
|
1933
|
-
fill: "none",
|
|
1934
|
-
stroke: "currentColor",
|
|
1935
|
-
strokeWidth: "2.5",
|
|
1936
|
-
strokeLinecap: "round",
|
|
1937
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
1938
|
-
cx: "12",
|
|
1939
|
-
cy: "12",
|
|
1940
|
-
r: "3"
|
|
1941
|
-
})]
|
|
1942
|
-
})
|
|
1943
|
-
},
|
|
1944
|
-
{
|
|
1945
|
-
label: t("onboarding.demo.pin"),
|
|
1946
|
-
visual: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
1947
|
-
"aria-hidden": "true",
|
|
1948
|
-
width: "12",
|
|
1949
|
-
height: "14",
|
|
1950
|
-
viewBox: "0 0 24 24",
|
|
1951
|
-
fill: "none",
|
|
1952
|
-
stroke: "currentColor",
|
|
1953
|
-
strokeWidth: "2",
|
|
1954
|
-
strokeLinecap: "round",
|
|
1955
|
-
strokeLinejoin: "round",
|
|
1956
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 21v-4" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M7 4h10l-1.2 6.5a4.6 4.6 0 0 1-7.6 0z" })]
|
|
1957
|
-
})
|
|
1958
|
-
}
|
|
1959
|
-
].map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1960
|
-
"data-demo-legend-row": true,
|
|
1961
|
-
style: {
|
|
1962
|
-
display: "flex",
|
|
1963
|
-
alignItems: "center",
|
|
1964
|
-
gap: 10,
|
|
1965
|
-
fontSize: 12.5,
|
|
1966
|
-
color: MODAL_TEXT
|
|
1967
|
-
},
|
|
1968
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1969
|
-
style: {
|
|
1970
|
-
width: 56,
|
|
1971
|
-
flexShrink: 0,
|
|
1972
|
-
display: "flex",
|
|
1973
|
-
justifyContent: "center"
|
|
1974
|
-
},
|
|
1975
|
-
children: row.visual
|
|
1976
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: row.label })]
|
|
1977
|
-
}, row.label))
|
|
1978
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1979
|
-
"data-demo-settings-path": true,
|
|
1980
|
-
style: {
|
|
1981
|
-
marginTop: 10,
|
|
1982
|
-
textAlign: "center",
|
|
1983
|
-
fontSize: 12,
|
|
1984
|
-
color: MODAL_HINT
|
|
1985
|
-
},
|
|
1986
|
-
children: t("onboarding.demo.settingsPath")
|
|
1987
|
-
})]
|
|
1988
|
-
});
|
|
1561
|
+
/** Rail root — every step's fallback anchor and bubble 4's target. */
|
|
1562
|
+
function railRoot() {
|
|
1563
|
+
return document.querySelector("[data-side]");
|
|
1989
1564
|
}
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
}
|
|
2006
|
-
|
|
2007
|
-
"
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
},
|
|
2021
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2022
|
-
"aria-hidden": "true",
|
|
2023
|
-
style: {
|
|
2024
|
-
width: 6,
|
|
2025
|
-
height: 6,
|
|
2026
|
-
borderRadius: "50%",
|
|
2027
|
-
background: "var(--ms-accent)"
|
|
2028
|
-
}
|
|
2029
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
2030
|
-
t("update.title"),
|
|
2031
|
-
" · v",
|
|
2032
|
-
PLUGIN_VERSION
|
|
2033
|
-
] })]
|
|
2034
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2035
|
-
"data-demo-links": true,
|
|
2036
|
-
style: {
|
|
2037
|
-
display: "grid",
|
|
2038
|
-
gridTemplateColumns: "repeat(4, minmax(0, 1fr))",
|
|
2039
|
-
gap: 8,
|
|
2040
|
-
marginTop: 12
|
|
2041
|
-
},
|
|
2042
|
-
children: [
|
|
2043
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2044
|
-
href: PLUGIN_REPO_URL,
|
|
2045
|
-
target: "_blank",
|
|
2046
|
-
rel: "noreferrer",
|
|
2047
|
-
"data-demo-link": true,
|
|
2048
|
-
"data-link": "repo",
|
|
2049
|
-
style: linkStyle,
|
|
2050
|
-
children: t("settings.repo")
|
|
2051
|
-
}),
|
|
2052
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2053
|
-
href: PLUGIN_REPO_URL,
|
|
2054
|
-
target: "_blank",
|
|
2055
|
-
rel: "noreferrer",
|
|
2056
|
-
"data-demo-link": true,
|
|
2057
|
-
"data-link": "star",
|
|
2058
|
-
style: linkStyle,
|
|
2059
|
-
children: t("settings.star")
|
|
2060
|
-
}),
|
|
2061
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2062
|
-
href: `${PLUGIN_REPO_URL}/issues`,
|
|
2063
|
-
target: "_blank",
|
|
2064
|
-
rel: "noreferrer",
|
|
2065
|
-
"data-demo-link": true,
|
|
2066
|
-
"data-link": "issues",
|
|
2067
|
-
style: linkStyle,
|
|
2068
|
-
children: t("settings.issues")
|
|
2069
|
-
}),
|
|
2070
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2071
|
-
href: PLUGIN_NPM_URL,
|
|
2072
|
-
target: "_blank",
|
|
2073
|
-
rel: "noreferrer",
|
|
2074
|
-
"data-demo-link": true,
|
|
2075
|
-
"data-link": "npm",
|
|
2076
|
-
style: linkStyle,
|
|
2077
|
-
children: t("settings.npm")
|
|
2078
|
-
})
|
|
2079
|
-
]
|
|
2080
|
-
})]
|
|
2081
|
-
});
|
|
1565
|
+
const STEP_DEFS = [
|
|
1566
|
+
{
|
|
1567
|
+
titleKey: "tour.welcome.title",
|
|
1568
|
+
descKey: "tour.welcome.desc",
|
|
1569
|
+
anchor: () => document.querySelector("[data-rail-list]")
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
titleKey: "tour.step1.title",
|
|
1573
|
+
descKey: "tour.step1.desc",
|
|
1574
|
+
anchor: () => document.querySelector("[data-toolbar-expand]")
|
|
1575
|
+
},
|
|
1576
|
+
{
|
|
1577
|
+
titleKey: "tour.step2.title",
|
|
1578
|
+
descKey: "tour.step2.desc",
|
|
1579
|
+
anchor: () => document.querySelector("[data-toolbar-settings]")
|
|
1580
|
+
},
|
|
1581
|
+
{
|
|
1582
|
+
titleKey: "tour.step3.title",
|
|
1583
|
+
descKey: "tour.step3.desc",
|
|
1584
|
+
anchor: () => document.querySelector("[data-rail-dot]") ?? railRoot()
|
|
1585
|
+
},
|
|
1586
|
+
{
|
|
1587
|
+
titleKey: "tour.step4.title",
|
|
1588
|
+
descKey: "tour.step4.desc",
|
|
1589
|
+
anchor: () => railRoot()
|
|
1590
|
+
}
|
|
1591
|
+
];
|
|
1592
|
+
/** Clamp `value` into [min, max], never letting min exceed max. */
|
|
1593
|
+
function clamp(value, min, max) {
|
|
1594
|
+
return Math.min(Math.max(value, min), Math.max(min, max));
|
|
2082
1595
|
}
|
|
2083
|
-
|
|
2084
|
-
* The tutorial dialog. `step` 0 = welcome; 1–4 = the step pages.
|
|
2085
|
-
*
|
|
2086
|
-
* Impressions persist: the moment the dialog mounts (welcome shown), the
|
|
2087
|
-
* onboarded flag is written — closing mid-tour via refresh/crash/navigation
|
|
2088
|
-
* never re-pops (ROADMAP 0.6.4: 印象即持久化). Every explicit close path
|
|
2089
|
-
* (skip anywhere, finish, Escape) re-asserts the same `'1'` and then lets the
|
|
2090
|
-
* parent unmount the modal.
|
|
2091
|
-
*/
|
|
2092
|
-
function MilestoneOnboarding({ t, accent, onClose }) {
|
|
1596
|
+
function MilestoneTour({ t, side, toolbarExpanded, settingsOpen, onSetToolbarExpanded, onClose }) {
|
|
2093
1597
|
const [step, setStep] = (0, react.useState)(0);
|
|
2094
|
-
const
|
|
1598
|
+
const [pos, setPos] = (0, react.useState)(null);
|
|
1599
|
+
const bubbleRef = (0, react.useRef)(null);
|
|
1600
|
+
const primaryRef = (0, react.useRef)(null);
|
|
1601
|
+
const stepDef = STEP_DEFS[step];
|
|
2095
1602
|
(0, react.useEffect)(() => {
|
|
2096
1603
|
writeOnboardedFlag();
|
|
2097
1604
|
}, []);
|
|
@@ -2101,274 +1608,227 @@ window.__ModuleLoader__.load({
|
|
|
2101
1608
|
onClose();
|
|
2102
1609
|
}, [onClose]);
|
|
2103
1610
|
(0, react.useEffect)(() => {
|
|
1611
|
+
if (settingsOpen) return;
|
|
2104
1612
|
const onKey = (e) => {
|
|
2105
1613
|
if (e.key !== "Escape") return;
|
|
2106
1614
|
finish();
|
|
2107
1615
|
};
|
|
2108
1616
|
window.addEventListener("keydown", onKey);
|
|
2109
1617
|
return () => window.removeEventListener("keydown", onKey);
|
|
2110
|
-
}, [finish]);
|
|
1618
|
+
}, [finish, settingsOpen]);
|
|
1619
|
+
(0, react.useEffect)(() => {
|
|
1620
|
+
if (step === 1 && toolbarExpanded) setStep(2);
|
|
1621
|
+
}, [step, toolbarExpanded]);
|
|
1622
|
+
(0, react.useEffect)(() => {
|
|
1623
|
+
if (step === 2 && settingsOpen) setStep(3);
|
|
1624
|
+
}, [step, settingsOpen]);
|
|
1625
|
+
(0, react.useEffect)(() => {
|
|
1626
|
+
if (step >= 2 && !toolbarExpanded) onSetToolbarExpanded(true);
|
|
1627
|
+
}, [
|
|
1628
|
+
step,
|
|
1629
|
+
toolbarExpanded,
|
|
1630
|
+
onSetToolbarExpanded
|
|
1631
|
+
]);
|
|
1632
|
+
/** Measure the current anchor and place the bubble on its free side. */
|
|
1633
|
+
const measure = (0, react.useCallback)(() => {
|
|
1634
|
+
const anchor = STEP_DEFS[step].anchor();
|
|
1635
|
+
const bubble = bubbleRef.current;
|
|
1636
|
+
if (anchor === null || bubble === null) {
|
|
1637
|
+
setPos(null);
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
const rect = anchor.getBoundingClientRect();
|
|
1641
|
+
const bw = bubble.offsetWidth || TOUR_WIDTH;
|
|
1642
|
+
const bh = bubble.offsetHeight || TOUR_HEIGHT_FALLBACK;
|
|
1643
|
+
const left = clamp(side === "left" ? rect.right + TOUR_GAP : rect.left - TOUR_GAP - bw, VIEWPORT_PAD, window.innerWidth - bw - VIEWPORT_PAD);
|
|
1644
|
+
const top = clamp(rect.top + rect.height / 2 - bh / 2, VIEWPORT_PAD, window.innerHeight - bh - VIEWPORT_PAD);
|
|
1645
|
+
setPos({
|
|
1646
|
+
left,
|
|
1647
|
+
top
|
|
1648
|
+
});
|
|
1649
|
+
}, [step, side]);
|
|
1650
|
+
(0, react.useLayoutEffect)(() => {
|
|
1651
|
+
measure();
|
|
1652
|
+
window.addEventListener("resize", measure);
|
|
1653
|
+
return () => window.removeEventListener("resize", measure);
|
|
1654
|
+
}, [
|
|
1655
|
+
measure,
|
|
1656
|
+
toolbarExpanded,
|
|
1657
|
+
settingsOpen
|
|
1658
|
+
]);
|
|
2111
1659
|
(0, react.useEffect)(() => {
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
1660
|
+
document.querySelectorAll("[data-tour-highlight]").forEach((el) => el.removeAttribute("data-tour-highlight"));
|
|
1661
|
+
const anchor = STEP_DEFS[step].anchor();
|
|
1662
|
+
if (anchor !== null) anchor.setAttribute("data-tour-highlight", "");
|
|
1663
|
+
return () => {
|
|
1664
|
+
document.querySelectorAll("[data-tour-highlight]").forEach((el) => el.removeAttribute("data-tour-highlight"));
|
|
1665
|
+
};
|
|
1666
|
+
}, [
|
|
1667
|
+
step,
|
|
1668
|
+
toolbarExpanded,
|
|
1669
|
+
settingsOpen
|
|
1670
|
+
]);
|
|
1671
|
+
(0, react.useEffect)(() => {
|
|
1672
|
+
if (settingsOpen) return;
|
|
1673
|
+
primaryRef.current?.focus();
|
|
1674
|
+
}, [step, settingsOpen]);
|
|
1675
|
+
const goNext = () => setStep((s) => Math.min(STEP_COUNT.length - 1, s + 1));
|
|
1676
|
+
const goPrev = () => setStep((s) => Math.max(0, s - 1));
|
|
1677
|
+
if (settingsOpen) return null;
|
|
1678
|
+
const isLast = step === STEP_COUNT.length - 1;
|
|
1679
|
+
const primaryLabel = step === 0 || isLast ? t("tour.start") : t("tour.next");
|
|
1680
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1681
|
+
ref: bubbleRef,
|
|
1682
|
+
"data-tour-bubble": true,
|
|
1683
|
+
"data-tour-step": step,
|
|
1684
|
+
role: "dialog",
|
|
1685
|
+
"aria-label": t(stepDef.titleKey),
|
|
2118
1686
|
style: {
|
|
2119
1687
|
position: "fixed",
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
padding:
|
|
1688
|
+
left: pos?.left,
|
|
1689
|
+
top: pos?.top,
|
|
1690
|
+
width: TOUR_WIDTH,
|
|
1691
|
+
maxWidth: "calc(100vw - 24px)",
|
|
1692
|
+
boxSizing: "border-box",
|
|
1693
|
+
zIndex: TOUR_Z,
|
|
1694
|
+
padding: 14,
|
|
1695
|
+
background: MODAL_TIP_BG,
|
|
1696
|
+
color: MODAL_FG,
|
|
1697
|
+
borderRadius: 12,
|
|
1698
|
+
border: `1px solid ${MODAL_BORDER}`,
|
|
1699
|
+
boxShadow: "0 12px 36px rgba(0, 0, 0, 0.45)"
|
|
2127
1700
|
},
|
|
2128
|
-
children:
|
|
2129
|
-
|
|
2130
|
-
"
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
1701
|
+
children: [
|
|
1702
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: TOUR_CSS }),
|
|
1703
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1704
|
+
style: {
|
|
1705
|
+
display: "flex",
|
|
1706
|
+
alignItems: "flex-start",
|
|
1707
|
+
justifyContent: "space-between",
|
|
1708
|
+
gap: 8
|
|
1709
|
+
},
|
|
1710
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1711
|
+
"data-tour-title": true,
|
|
1712
|
+
style: {
|
|
1713
|
+
fontSize: 14,
|
|
1714
|
+
fontWeight: 700,
|
|
1715
|
+
color: MODAL_FG,
|
|
1716
|
+
lineHeight: 1.35
|
|
1717
|
+
},
|
|
1718
|
+
children: t(stepDef.titleKey)
|
|
1719
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1720
|
+
type: "button",
|
|
1721
|
+
"data-tour-skip": true,
|
|
1722
|
+
onClick: finish,
|
|
1723
|
+
style: {
|
|
1724
|
+
flexShrink: 0,
|
|
1725
|
+
background: "transparent",
|
|
1726
|
+
border: "none",
|
|
1727
|
+
padding: "2px 6px",
|
|
1728
|
+
borderRadius: 8,
|
|
1729
|
+
cursor: "pointer",
|
|
1730
|
+
color: MODAL_HINT,
|
|
1731
|
+
fontSize: 12,
|
|
1732
|
+
lineHeight: 1.4
|
|
1733
|
+
},
|
|
1734
|
+
children: t("tour.skip")
|
|
1735
|
+
})]
|
|
1736
|
+
}),
|
|
1737
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1738
|
+
"data-tour-desc": true,
|
|
1739
|
+
style: {
|
|
1740
|
+
marginTop: 6,
|
|
1741
|
+
fontSize: 12.5,
|
|
1742
|
+
lineHeight: 1.5,
|
|
1743
|
+
color: MODAL_TEXT
|
|
1744
|
+
},
|
|
1745
|
+
children: t(stepDef.descKey)
|
|
1746
|
+
}),
|
|
1747
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
1748
|
+
style: {
|
|
1749
|
+
display: "flex",
|
|
1750
|
+
alignItems: "center",
|
|
1751
|
+
justifyContent: "space-between",
|
|
1752
|
+
gap: 10,
|
|
1753
|
+
marginTop: 12
|
|
1754
|
+
},
|
|
2149
1755
|
children: [
|
|
2150
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
2151
|
-
"
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
color: MODAL_FG
|
|
2156
|
-
},
|
|
2157
|
-
children: t("onboarding.welcome.title")
|
|
2158
|
-
}),
|
|
2159
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2160
|
-
"data-onboarding-welcome-subtitle": true,
|
|
1756
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1757
|
+
type: "button",
|
|
1758
|
+
"data-tour-prev": true,
|
|
1759
|
+
disabled: step === 0,
|
|
1760
|
+
onClick: goPrev,
|
|
2161
1761
|
style: {
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
1762
|
+
flexShrink: 0,
|
|
1763
|
+
background: "rgba(255, 255, 255, 0.06)",
|
|
1764
|
+
border: `1px solid ${MODAL_BORDER}`,
|
|
1765
|
+
borderRadius: 8,
|
|
1766
|
+
padding: "5px 12px",
|
|
1767
|
+
cursor: step === 0 ? "default" : "pointer",
|
|
1768
|
+
color: step === 0 ? MODAL_HINT : MODAL_TEXT,
|
|
1769
|
+
fontSize: 12,
|
|
1770
|
+
lineHeight: 1.4,
|
|
1771
|
+
opacity: step === 0 ? .55 : 1
|
|
2166
1772
|
},
|
|
2167
|
-
children: t("
|
|
1773
|
+
children: t("tour.prev")
|
|
2168
1774
|
}),
|
|
2169
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2170
|
-
"data-onboarding-welcome-actions": true,
|
|
2171
|
-
style: {
|
|
2172
|
-
display: "flex",
|
|
2173
|
-
justifyContent: "center",
|
|
2174
|
-
gap: 10,
|
|
2175
|
-
marginTop: 18
|
|
2176
|
-
},
|
|
2177
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2178
|
-
type: "button",
|
|
2179
|
-
"data-onboarding-start": true,
|
|
2180
|
-
onClick: () => setStep(1),
|
|
2181
|
-
style: PRIMARY_BUTTON,
|
|
2182
|
-
children: t("onboarding.start")
|
|
2183
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2184
|
-
type: "button",
|
|
2185
|
-
"data-onboarding-skip": true,
|
|
2186
|
-
onClick: finish,
|
|
2187
|
-
style: GHOST_BUTTON,
|
|
2188
|
-
children: t("onboarding.skip")
|
|
2189
|
-
})]
|
|
2190
|
-
})
|
|
2191
|
-
]
|
|
2192
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2193
|
-
"data-onboarding-step": true,
|
|
2194
|
-
"data-step": step,
|
|
2195
|
-
children: [
|
|
2196
1775
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2197
1776
|
style: {
|
|
2198
1777
|
display: "flex",
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
gap:
|
|
1778
|
+
flexDirection: "column",
|
|
1779
|
+
alignItems: "center",
|
|
1780
|
+
gap: 5,
|
|
1781
|
+
minWidth: 74
|
|
2202
1782
|
},
|
|
2203
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2204
|
-
"data-
|
|
1783
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1784
|
+
"data-tour-progress": true,
|
|
2205
1785
|
style: {
|
|
2206
|
-
fontSize:
|
|
2207
|
-
|
|
2208
|
-
|
|
1786
|
+
fontSize: 11.5,
|
|
1787
|
+
color: MODAL_HINT,
|
|
1788
|
+
lineHeight: 1.2
|
|
2209
1789
|
},
|
|
2210
|
-
children:
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
1790
|
+
children: t("tour.step.label", { n: step + 1 })
|
|
1791
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1792
|
+
style: {
|
|
1793
|
+
display: "flex",
|
|
1794
|
+
gap: 4
|
|
1795
|
+
},
|
|
1796
|
+
children: STEP_COUNT.map((n) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1797
|
+
"data-tour-progress-dot": true,
|
|
1798
|
+
"data-active": step === n ? "true" : void 0,
|
|
1799
|
+
"aria-label": t("tour.step.short", { n: n + 1 }),
|
|
1800
|
+
"aria-current": step === n ? "step" : void 0,
|
|
1801
|
+
style: {
|
|
1802
|
+
width: 14,
|
|
1803
|
+
height: 4,
|
|
1804
|
+
borderRadius: 2,
|
|
1805
|
+
background: step === n ? "var(--ms-accent)" : "rgba(255, 255, 255, 0.18)"
|
|
1806
|
+
}
|
|
1807
|
+
}, n))
|
|
2222
1808
|
})]
|
|
2223
1809
|
}),
|
|
2224
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
marginTop: 10
|
|
2230
|
-
},
|
|
2231
|
-
children: [
|
|
2232
|
-
step === 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2233
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2234
|
-
"data-onboarding-step-desc": true,
|
|
2235
|
-
"data-desc": "1",
|
|
2236
|
-
children: t("onboarding.step1.desc1")
|
|
2237
|
-
}),
|
|
2238
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2239
|
-
"data-onboarding-step-desc": true,
|
|
2240
|
-
"data-desc": "2",
|
|
2241
|
-
children: t("onboarding.step1.desc2")
|
|
2242
|
-
}),
|
|
2243
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2244
|
-
"data-onboarding-step-desc": true,
|
|
2245
|
-
"data-desc": "3",
|
|
2246
|
-
children: t("onboarding.step1.desc3")
|
|
2247
|
-
})
|
|
2248
|
-
] }),
|
|
2249
|
-
step === 2 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2250
|
-
"data-onboarding-step-desc": true,
|
|
2251
|
-
"data-desc": "1",
|
|
2252
|
-
children: t("onboarding.step2.desc1")
|
|
2253
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2254
|
-
"data-onboarding-step-desc": true,
|
|
2255
|
-
"data-desc": "2",
|
|
2256
|
-
children: t("onboarding.step2.desc2")
|
|
2257
|
-
})] }),
|
|
2258
|
-
step === 3 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2259
|
-
"data-onboarding-step-desc": true,
|
|
2260
|
-
"data-desc": "1",
|
|
2261
|
-
children: t("onboarding.step3.desc1")
|
|
2262
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2263
|
-
"data-onboarding-step-desc": true,
|
|
2264
|
-
"data-desc": "2",
|
|
2265
|
-
children: t("onboarding.step3.desc2")
|
|
2266
|
-
})] }),
|
|
2267
|
-
step === 4 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2268
|
-
"data-onboarding-step-desc": true,
|
|
2269
|
-
"data-desc": "1",
|
|
2270
|
-
children: t("onboarding.step4.desc1")
|
|
2271
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2272
|
-
"data-onboarding-step-desc": true,
|
|
2273
|
-
"data-desc": "2",
|
|
2274
|
-
children: t("onboarding.step4.desc2")
|
|
2275
|
-
})] })
|
|
2276
|
-
]
|
|
2277
|
-
}),
|
|
2278
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2279
|
-
"data-onboarding-demo": true,
|
|
1810
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1811
|
+
type: "button",
|
|
1812
|
+
ref: primaryRef,
|
|
1813
|
+
"data-tour-primary": true,
|
|
1814
|
+
onClick: isLast ? finish : goNext,
|
|
2280
1815
|
style: {
|
|
2281
|
-
|
|
2282
|
-
|
|
1816
|
+
flexShrink: 0,
|
|
1817
|
+
background: "var(--ms-accent-bg)",
|
|
1818
|
+
color: "var(--ms-accent-soft)",
|
|
1819
|
+
border: "1px solid var(--ms-accent)",
|
|
2283
1820
|
borderRadius: 8,
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
t,
|
|
2290
|
-
accent
|
|
2291
|
-
}),
|
|
2292
|
-
step === 2 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DemoSearch, {
|
|
2293
|
-
t,
|
|
2294
|
-
accent
|
|
2295
|
-
}),
|
|
2296
|
-
step === 3 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DemoSettings, {
|
|
2297
|
-
t,
|
|
2298
|
-
accent
|
|
2299
|
-
}),
|
|
2300
|
-
step === 4 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DemoSupport, { t })
|
|
2301
|
-
]
|
|
2302
|
-
}),
|
|
2303
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
2304
|
-
style: {
|
|
2305
|
-
display: "flex",
|
|
2306
|
-
alignItems: "center",
|
|
2307
|
-
justifyContent: "space-between",
|
|
2308
|
-
gap: 12,
|
|
2309
|
-
marginTop: 16
|
|
1821
|
+
padding: "5px 14px",
|
|
1822
|
+
cursor: "pointer",
|
|
1823
|
+
fontSize: 12.5,
|
|
1824
|
+
fontWeight: 600,
|
|
1825
|
+
lineHeight: 1.4
|
|
2310
1826
|
},
|
|
2311
|
-
children:
|
|
2312
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2313
|
-
type: "button",
|
|
2314
|
-
"data-onboarding-prev": true,
|
|
2315
|
-
onClick: goPrev,
|
|
2316
|
-
disabled: step === 1,
|
|
2317
|
-
style: GHOST_BUTTON,
|
|
2318
|
-
children: t("onboarding.prev")
|
|
2319
|
-
}),
|
|
2320
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2321
|
-
style: {
|
|
2322
|
-
display: "flex",
|
|
2323
|
-
flexDirection: "column",
|
|
2324
|
-
alignItems: "center",
|
|
2325
|
-
gap: 6
|
|
2326
|
-
},
|
|
2327
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2328
|
-
"data-onboarding-progress": true,
|
|
2329
|
-
style: {
|
|
2330
|
-
fontSize: 12,
|
|
2331
|
-
color: MODAL_HINT
|
|
2332
|
-
},
|
|
2333
|
-
children: t("onboarding.step.label", { n: step })
|
|
2334
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2335
|
-
style: {
|
|
2336
|
-
display: "flex",
|
|
2337
|
-
gap: 5
|
|
2338
|
-
},
|
|
2339
|
-
children: STEP_COUNT.map((n) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2340
|
-
"data-onboarding-progress-dot": true,
|
|
2341
|
-
"data-active": step === n ? "true" : void 0,
|
|
2342
|
-
"aria-label": t("onboarding.step.short", { n }),
|
|
2343
|
-
"aria-current": step === n ? "step" : void 0,
|
|
2344
|
-
style: {
|
|
2345
|
-
width: 16,
|
|
2346
|
-
height: 4,
|
|
2347
|
-
borderRadius: 2,
|
|
2348
|
-
background: step === n ? "var(--ms-accent)" : "rgba(255, 255, 255, 0.18)",
|
|
2349
|
-
transition: "background 120ms ease, width 120ms ease"
|
|
2350
|
-
}
|
|
2351
|
-
}, n))
|
|
2352
|
-
})]
|
|
2353
|
-
}),
|
|
2354
|
-
step < 4 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2355
|
-
type: "button",
|
|
2356
|
-
"data-onboarding-next": true,
|
|
2357
|
-
onClick: goNext,
|
|
2358
|
-
style: PRIMARY_BUTTON,
|
|
2359
|
-
children: t("onboarding.next")
|
|
2360
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2361
|
-
type: "button",
|
|
2362
|
-
"data-onboarding-finish": true,
|
|
2363
|
-
onClick: finish,
|
|
2364
|
-
style: PRIMARY_BUTTON,
|
|
2365
|
-
children: t("onboarding.finish")
|
|
2366
|
-
})
|
|
2367
|
-
]
|
|
1827
|
+
children: primaryLabel
|
|
2368
1828
|
})
|
|
2369
1829
|
]
|
|
2370
|
-
})
|
|
2371
|
-
|
|
1830
|
+
})
|
|
1831
|
+
]
|
|
2372
1832
|
});
|
|
2373
1833
|
}
|
|
2374
1834
|
//#endregion
|
|
@@ -2415,10 +1875,7 @@ window.__ModuleLoader__.load({
|
|
|
2415
1875
|
marginBottom: 4
|
|
2416
1876
|
},
|
|
2417
1877
|
children: [
|
|
2418
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children:
|
|
2419
|
-
n: hover.index + 1,
|
|
2420
|
-
m: hover.total
|
|
2421
|
-
}) }),
|
|
1878
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: hover.posLabel }),
|
|
2422
1879
|
hover.turnLabel !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: hover.turnLabel }),
|
|
2423
1880
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2424
1881
|
type: "button",
|
|
@@ -3317,6 +2774,13 @@ window.__ModuleLoader__.load({
|
|
|
3317
2774
|
return fresh;
|
|
3318
2775
|
}
|
|
3319
2776
|
//#endregion
|
|
2777
|
+
//#region src/client/version-meta.ts
|
|
2778
|
+
/**
|
|
2779
|
+
* Installed plugin version. Injected at build time as
|
|
2780
|
+
* `__DSH_MILESTONE_VERSION__`; falls back to `0.0.0-dev` when unbuilt.
|
|
2781
|
+
*/
|
|
2782
|
+
const PLUGIN_VERSION = "0.6.6";
|
|
2783
|
+
//#endregion
|
|
3320
2784
|
//#region src/client/MilestoneRail.tsx
|
|
3321
2785
|
/**
|
|
3322
2786
|
* MilestoneRail: the milestone.rail entry (session scope). Renders a fixed
|
|
@@ -3499,6 +2963,14 @@ window.__ModuleLoader__.load({
|
|
|
3499
2963
|
[data-toolbar-settings-close]:hover { background: rgba(255, 255, 255, 0.08); }
|
|
3500
2964
|
[data-toolbar-settings-reset], [data-onboarding-reopen] { background: rgba(255, 255, 255, 0.06); }
|
|
3501
2965
|
[data-toolbar-settings-reset]:hover, [data-onboarding-reopen]:hover { background: rgba(255, 255, 255, 0.1); }
|
|
2966
|
+
/* BASE state reset: every modal surface must sit transparent on the dark
|
|
2967
|
+
panel — without it the UA default button face (light gray) floods through
|
|
2968
|
+
and rows become unreadable light-on-light. Hover washes above take over
|
|
2969
|
+
on interaction. */
|
|
2970
|
+
[data-toolbar-pin-toggle], [data-personal-toggle], [data-focus-toggle-settings],
|
|
2971
|
+
[data-toolbar-settings-close], [data-focus-option] {
|
|
2972
|
+
background: transparent;
|
|
2973
|
+
}
|
|
3502
2974
|
/* ONE accent ring for keyboard focus on every modal control. */
|
|
3503
2975
|
[data-toolbar-pin-toggle]:focus-visible, [data-personal-toggle]:focus-visible,
|
|
3504
2976
|
[data-focus-toggle-settings]:focus-visible,
|
|
@@ -3579,8 +3051,8 @@ window.__ModuleLoader__.load({
|
|
|
3579
3051
|
/** B4 update-check: mount-time silent check delay (ms) — give the harness
|
|
3580
3052
|
* time to settle before hitting the registry. */
|
|
3581
3053
|
const UPDATE_CHECK_MOUNT_DELAY = 1500;
|
|
3582
|
-
/** 0.6.
|
|
3583
|
-
* before the
|
|
3054
|
+
/** 0.6.5 first-run coach tour: mount-time show delay (ms) — let the rail settle
|
|
3055
|
+
* before the first bubble pops. */
|
|
3584
3056
|
const ONBOARDING_MOUNT_DELAY = 800;
|
|
3585
3057
|
/** Initial state: no check has completed yet, nothing to show. */
|
|
3586
3058
|
const NO_UPDATE_CHECK = {
|
|
@@ -3687,6 +3159,8 @@ window.__ModuleLoader__.load({
|
|
|
3687
3159
|
const [bookmarksOnly, setBookmarksOnly] = (0, react.useState)(false);
|
|
3688
3160
|
const [focusActive, setFocusActive] = (0, react.useState)(false);
|
|
3689
3161
|
const [listOpen, setListOpen] = (0, react.useState)(false);
|
|
3162
|
+
const [drainPage, setDrainPage] = (0, react.useState)(false);
|
|
3163
|
+
const [drainFailed, setDrainFailed] = (0, react.useState)(false);
|
|
3690
3164
|
const [crossOpen, setCrossOpen] = (0, react.useState)(false);
|
|
3691
3165
|
const [copiedKey, setCopiedKey] = (0, react.useState)(null);
|
|
3692
3166
|
const [forkedKey, setForkedKey] = (0, react.useState)(null);
|
|
@@ -3739,6 +3213,7 @@ window.__ModuleLoader__.load({
|
|
|
3739
3213
|
}
|
|
3740
3214
|
return counts;
|
|
3741
3215
|
}, [displayMarks]);
|
|
3216
|
+
const displayTurns = (0, react.useMemo)(() => buildDisplayTurns(marks), [marks]);
|
|
3742
3217
|
const [prefs, setPrefs] = (0, react.useState)(() => loadPrefs());
|
|
3743
3218
|
const { pinned, accent, iconSize, inset, side } = prefs;
|
|
3744
3219
|
const scale = iconSize / DOT_HIT;
|
|
@@ -3809,7 +3284,8 @@ window.__ModuleLoader__.load({
|
|
|
3809
3284
|
const [expandHovered, setExpandHovered] = (0, react.useState)(false);
|
|
3810
3285
|
const [settingsHovered, setSettingsHovered] = (0, react.useState)(false);
|
|
3811
3286
|
const [settingsOpen, setSettingsOpen] = (0, react.useState)(false);
|
|
3812
|
-
const [
|
|
3287
|
+
const [tourOpen, setTourOpen] = (0, react.useState)(false);
|
|
3288
|
+
const [tourRun, setTourRun] = (0, react.useState)(0);
|
|
3813
3289
|
/** The feature whose near-row description tip is currently visible
|
|
3814
3290
|
* (`null` = none — tips only appear on hover/focus of their own row). */
|
|
3815
3291
|
const [descFeature, setDescFeature] = (0, react.useState)(null);
|
|
@@ -3891,7 +3367,7 @@ window.__ModuleLoader__.load({
|
|
|
3891
3367
|
}, []);
|
|
3892
3368
|
(0, react.useEffect)(() => {
|
|
3893
3369
|
const timer = window.setTimeout(() => {
|
|
3894
|
-
if (!readOnboardedFlag())
|
|
3370
|
+
if (!readOnboardedFlag()) setTourOpen(true);
|
|
3895
3371
|
}, ONBOARDING_MOUNT_DELAY);
|
|
3896
3372
|
return () => window.clearTimeout(timer);
|
|
3897
3373
|
}, []);
|
|
@@ -4009,6 +3485,22 @@ window.__ModuleLoader__.load({
|
|
|
4009
3485
|
window.addEventListener("keydown", onKey);
|
|
4010
3486
|
return () => window.removeEventListener("keydown", onKey);
|
|
4011
3487
|
}, [listOpen, crossOpen]);
|
|
3488
|
+
const listDraining = listOpen && (drainPage || hasMore && !drainFailed);
|
|
3489
|
+
(0, react.useEffect)(() => {
|
|
3490
|
+
if (!listOpen) return;
|
|
3491
|
+
setDrainFailed(false);
|
|
3492
|
+
}, [listOpen]);
|
|
3493
|
+
(0, react.useEffect)(() => {
|
|
3494
|
+
if (!listOpen || !hasMore || drainPage || drainFailed) return;
|
|
3495
|
+
setDrainPage(true);
|
|
3496
|
+
loadOlder().catch(() => setDrainFailed(true)).finally(() => setDrainPage(false));
|
|
3497
|
+
}, [
|
|
3498
|
+
listOpen,
|
|
3499
|
+
hasMore,
|
|
3500
|
+
drainPage,
|
|
3501
|
+
drainFailed,
|
|
3502
|
+
loadOlder
|
|
3503
|
+
]);
|
|
4012
3504
|
if (railBox === null || marks.length < MIN_MARKS) return null;
|
|
4013
3505
|
const updateQuery = (query) => {
|
|
4014
3506
|
setSearch({
|
|
@@ -4058,12 +3550,14 @@ window.__ModuleLoader__.load({
|
|
|
4058
3550
|
setSettingsOpen(false);
|
|
4059
3551
|
settingsBtnRef.current?.focus();
|
|
4060
3552
|
};
|
|
4061
|
-
/** 0.6.
|
|
4062
|
-
* immediately (the flag may or may not be set; skipping/completing it
|
|
4063
|
-
* re-persists the flag anyway).
|
|
4064
|
-
|
|
3553
|
+
/** 0.6.5: settings → 重新查看教程 — close settings and replay the coach
|
|
3554
|
+
* tour immediately (the flag may or may not be set; skipping/completing it
|
|
3555
|
+
* re-persists the flag anyway). The `tourRun` bump remounts the tour so the
|
|
3556
|
+
* replay always restarts from bubble 0. */
|
|
3557
|
+
const reopenTour = () => {
|
|
4065
3558
|
setSettingsOpen(false);
|
|
4066
|
-
|
|
3559
|
+
setTourRun((n) => n + 1);
|
|
3560
|
+
setTourOpen(true);
|
|
4067
3561
|
};
|
|
4068
3562
|
/** B1: a feature renders while the toolbar is EXPANDED or while it is pinned. */
|
|
4069
3563
|
const featureVisible = (id) => toolbarExpanded || pinned.includes(id);
|
|
@@ -4385,11 +3879,20 @@ window.__ModuleLoader__.load({
|
|
|
4385
3879
|
}
|
|
4386
3880
|
}
|
|
4387
3881
|
const meta = deriveTurnMeta(nodes, locations, mark.turn, trajectoryRequests);
|
|
3882
|
+
const summaryCount = collapsedSummaries.get(mark.key);
|
|
4388
3883
|
return {
|
|
4389
3884
|
mark,
|
|
4390
3885
|
index,
|
|
4391
3886
|
total: displayMarks.length,
|
|
4392
|
-
|
|
3887
|
+
posLabel: summaryCount !== void 0 ? t("pos.range", {
|
|
3888
|
+
a: index - summaryCount + 2,
|
|
3889
|
+
b: index + 1,
|
|
3890
|
+
m: displayMarks.length
|
|
3891
|
+
}) : t("pos.of", {
|
|
3892
|
+
n: index + 1,
|
|
3893
|
+
m: displayMarks.length
|
|
3894
|
+
}),
|
|
3895
|
+
turnLabel: mark.turn !== void 0 ? t("turn.label", { n: displayTurns.get(mark.turn) ?? mark.turn }) : null,
|
|
4393
3896
|
durationLabel,
|
|
4394
3897
|
reasonLabel,
|
|
4395
3898
|
ttftLabel,
|
|
@@ -5345,7 +4848,7 @@ window.__ModuleLoader__.load({
|
|
|
5345
4848
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5346
4849
|
type: "button",
|
|
5347
4850
|
"data-onboarding-reopen": true,
|
|
5348
|
-
onClick:
|
|
4851
|
+
onClick: reopenTour,
|
|
5349
4852
|
style: {
|
|
5350
4853
|
padding: "7px 16px",
|
|
5351
4854
|
border: `1px solid rgba(255, 255, 255, 0.14)`,
|
|
@@ -5354,17 +4857,20 @@ window.__ModuleLoader__.load({
|
|
|
5354
4857
|
color: "#b9c2d4",
|
|
5355
4858
|
fontSize: 12.5
|
|
5356
4859
|
},
|
|
5357
|
-
children: t("
|
|
4860
|
+
children: t("tour.reopen")
|
|
5358
4861
|
})]
|
|
5359
4862
|
})
|
|
5360
4863
|
]
|
|
5361
4864
|
})
|
|
5362
4865
|
}),
|
|
5363
|
-
|
|
4866
|
+
tourOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MilestoneTour, {
|
|
5364
4867
|
t,
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
4868
|
+
side,
|
|
4869
|
+
toolbarExpanded,
|
|
4870
|
+
settingsOpen,
|
|
4871
|
+
onSetToolbarExpanded: setToolbarExpanded,
|
|
4872
|
+
onClose: () => setTourOpen(false)
|
|
4873
|
+
}, tourRun),
|
|
5368
4874
|
updateOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5369
4875
|
ref: updatePanelRef,
|
|
5370
4876
|
"data-update-panel": true,
|
|
@@ -5401,7 +4907,7 @@ window.__ModuleLoader__.load({
|
|
|
5401
4907
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5402
4908
|
style: { color: "#8b96ab" },
|
|
5403
4909
|
children: [t("update.current"), ": "]
|
|
5404
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "0.6.
|
|
4910
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "0.6.6" })] }),
|
|
5405
4911
|
updateCheck.phase === "ok" && updateCheck.latest !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5406
4912
|
"data-update-latest": true,
|
|
5407
4913
|
children: [
|
|
@@ -5506,6 +5012,8 @@ window.__ModuleLoader__.load({
|
|
|
5506
5012
|
panelRight: panelRightFor(PANEL_WIDTH_STANDARD),
|
|
5507
5013
|
marks,
|
|
5508
5014
|
onJump: jump,
|
|
5015
|
+
onClose: () => setListOpen(false),
|
|
5016
|
+
loading: listDraining,
|
|
5509
5017
|
t
|
|
5510
5018
|
}),
|
|
5511
5019
|
crossOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MilestoneSessionSearch, {
|
|
@@ -5589,7 +5097,7 @@ window.__ModuleLoader__.load({
|
|
|
5589
5097
|
"aria-current": dotState === "active" ? "true" : void 0,
|
|
5590
5098
|
"data-current": dotState === "current" ? "true" : void 0,
|
|
5591
5099
|
"data-dimmed": dotState === "dimmed" ? "true" : void 0,
|
|
5592
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
5100
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5593
5101
|
style: {
|
|
5594
5102
|
position: "relative",
|
|
5595
5103
|
width: size,
|
|
@@ -5602,7 +5110,7 @@ window.__ModuleLoader__.load({
|
|
|
5602
5110
|
opacity: isHovered || dotState !== "dimmed" ? 1 : .22
|
|
5603
5111
|
},
|
|
5604
5112
|
"data-bookmarked": bookmarked ? "true" : void 0,
|
|
5605
|
-
children: ringStyle !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5113
|
+
children: [ringStyle !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5606
5114
|
"data-badge": badge,
|
|
5607
5115
|
style: {
|
|
5608
5116
|
position: "absolute",
|
|
@@ -5613,7 +5121,31 @@ window.__ModuleLoader__.load({
|
|
|
5613
5121
|
pointerEvents: "none",
|
|
5614
5122
|
animation: ringStyle.pulse ? "milestone-badge-pulse 2s ease-in-out infinite" : void 0
|
|
5615
5123
|
}
|
|
5616
|
-
})
|
|
5124
|
+
}), summaryCount !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5125
|
+
"data-collapsed-badge": true,
|
|
5126
|
+
style: {
|
|
5127
|
+
position: "absolute",
|
|
5128
|
+
top: -5,
|
|
5129
|
+
right: -7,
|
|
5130
|
+
minWidth: 15,
|
|
5131
|
+
height: 15,
|
|
5132
|
+
padding: "0 3px",
|
|
5133
|
+
display: "flex",
|
|
5134
|
+
alignItems: "center",
|
|
5135
|
+
justifyContent: "center",
|
|
5136
|
+
borderRadius: 8,
|
|
5137
|
+
background: "rgba(20, 24, 32, 0.96)",
|
|
5138
|
+
border: `1px solid ${accentSoft}`,
|
|
5139
|
+
boxSizing: "border-box",
|
|
5140
|
+
color: "#e6e8ee",
|
|
5141
|
+
fontSize: 9,
|
|
5142
|
+
lineHeight: 1,
|
|
5143
|
+
fontWeight: 600,
|
|
5144
|
+
whiteSpace: "nowrap",
|
|
5145
|
+
pointerEvents: "none"
|
|
5146
|
+
},
|
|
5147
|
+
children: ["×", summaryCount]
|
|
5148
|
+
})]
|
|
5617
5149
|
})
|
|
5618
5150
|
}) }, mark.key);
|
|
5619
5151
|
})
|