dsh-milestone 0.6.4 → 0.6.5
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 +9 -2
- package/lib/client.js +391 -954
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -796,72 +796,40 @@ window.__ModuleLoader__.load({
|
|
|
796
796
|
"settings.lang.zh": "中文",
|
|
797
797
|
/** Language option: force English copy. */
|
|
798
798
|
"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": "重新查看教程"
|
|
799
|
+
/** 0.6.5 tour: bubble 0 title (also the bubble's aria-label). */
|
|
800
|
+
"tour.welcome.title": "欢迎使用 dsh-milestone",
|
|
801
|
+
/** 0.6.5 tour: bubble 0 description — the REAL rail list anchor. */
|
|
802
|
+
"tour.welcome.desc": "这是你的会话导航条:每条提问一个圆点,点击跳转,悬停查看元信息。",
|
|
803
|
+
/** 0.6.5 tour: bubble 1 title — the expand arrow. */
|
|
804
|
+
"tour.step1.title": "展开工具栏",
|
|
805
|
+
/** 0.6.5 tour: bubble 1 description — what clicking REALLY does (auto-advance on the real click). */
|
|
806
|
+
"tour.step1.desc": "点这里展开全部功能键:搜索、列表、收藏、聚焦、更新检测与设置都在里面。",
|
|
807
|
+
/** 0.6.5 tour: bubble 2 title — the settings gear. */
|
|
808
|
+
"tour.step2.title": "打开设置",
|
|
809
|
+
/** 0.6.5 tour: bubble 2 description — what opening settings contains (auto-advance on the real open). */
|
|
810
|
+
"tour.step2.desc": "打开设置:钉住常用键、个性化外观、切换语言、搭配「聚焦」。",
|
|
811
|
+
/** 0.6.5 tour: bubble 3 title — the dots (real hover/click behavior). */
|
|
812
|
+
"tour.step3.title": "圆点导航",
|
|
813
|
+
/** 0.6.5 tour: bubble 3 description. */
|
|
814
|
+
"tour.step3.desc": "每个圆点 = 一条提问——点击跳转,悬停查看时间、模型与用量。",
|
|
815
|
+
/** 0.6.5 tour: bubble 4 title — done. */
|
|
816
|
+
"tour.step4.title": "开始使用",
|
|
817
|
+
/** 0.6.5 tour: bubble 4 description — finish line. */
|
|
818
|
+
"tour.step4.desc": "去试试吧;有问题欢迎到 GitHub 提 Issue。",
|
|
819
|
+
/** 0.6.5 tour: primary action on bubble 0 (begin) and bubble 4 (finish). */
|
|
820
|
+
"tour.start": "开始使用",
|
|
821
|
+
/** 0.6.5 tour: forward navigation (bubbles 1–3; the real-click auto-advance comes first). */
|
|
822
|
+
"tour.next": "下一步",
|
|
823
|
+
/** 0.6.5 tour: back navigation (bubbles 1–4). */
|
|
824
|
+
"tour.prev": "上一步",
|
|
825
|
+
/** 0.6.5 tour: corner skip — every bubble, persists the flag. */
|
|
826
|
+
"tour.skip": "跳过",
|
|
827
|
+
/** 0.6.5 tour: progress text `第 n / 5 步`. */
|
|
828
|
+
"tour.step.label": "第 {n} / 5 步",
|
|
829
|
+
/** 0.6.5 tour: aria-label on one progress dot. */
|
|
830
|
+
"tour.step.short": "第 {n} 步",
|
|
831
|
+
/** 0.6.5 settings footer action: replay the tour (data-onboarding-reopen kept). */
|
|
832
|
+
"tour.reopen": "重新查看教程"
|
|
865
833
|
};
|
|
866
834
|
const en = {
|
|
867
835
|
"jump.to": "Jump to message {n}",
|
|
@@ -967,39 +935,23 @@ window.__ModuleLoader__.load({
|
|
|
967
935
|
"settings.lang.system": "Follow system",
|
|
968
936
|
"settings.lang.zh": "Chinese",
|
|
969
937
|
"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"
|
|
938
|
+
"tour.welcome.title": "Welcome to dsh-milestone",
|
|
939
|
+
"tour.welcome.desc": "This is your session milestone rail: every prompt is a dot — click to jump, hover for metadata.",
|
|
940
|
+
"tour.step1.title": "Expand the toolbar",
|
|
941
|
+
"tour.step1.desc": "Click to reveal every function key: search, list, bookmarks, focus, update check, and settings.",
|
|
942
|
+
"tour.step2.title": "Open settings",
|
|
943
|
+
"tour.step2.desc": "Open settings: pin your favorite keys, personalize the look, switch language, and tune focus.",
|
|
944
|
+
"tour.step3.title": "Dot navigation",
|
|
945
|
+
"tour.step3.desc": "Each dot is one prompt — click to jump, hover for time, model, and usage.",
|
|
946
|
+
"tour.step4.title": "Get started",
|
|
947
|
+
"tour.step4.desc": "Go try it — questions and issues are welcome on GitHub.",
|
|
948
|
+
"tour.start": "Get started",
|
|
949
|
+
"tour.next": "Next",
|
|
950
|
+
"tour.prev": "Back",
|
|
951
|
+
"tour.skip": "Skip",
|
|
952
|
+
"tour.step.label": "Step {n} of 5",
|
|
953
|
+
"tour.step.short": "Step {n}",
|
|
954
|
+
"tour.reopen": "Replay the tour"
|
|
1003
955
|
};
|
|
1004
956
|
/**
|
|
1005
957
|
* Interpolate `{name}` placeholders with params, matching the harness t seat's
|
|
@@ -1427,22 +1379,6 @@ window.__ModuleLoader__.load({
|
|
|
1427
1379
|
]
|
|
1428
1380
|
});
|
|
1429
1381
|
}
|
|
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
1382
|
const MODAL_FG = "#e6e8ee";
|
|
1447
1383
|
const MODAL_TITLE = "#c7cede";
|
|
1448
1384
|
const MODAL_TEXT = "#b9c2d4";
|
|
@@ -1488,610 +1424,137 @@ window.__ModuleLoader__.load({
|
|
|
1488
1424
|
} catch {}
|
|
1489
1425
|
}
|
|
1490
1426
|
//#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
|
|
1427
|
+
//#region src/client/MilestoneTour.tsx
|
|
1503
1428
|
/**
|
|
1504
|
-
*
|
|
1429
|
+
* MilestoneTour: the 0.6.5 first-run coach-bubble tutorial.
|
|
1430
|
+
*
|
|
1431
|
+
* Unlike the deleted 0.6.4 MilestoneOnboarding modal (built-in toy demos on a
|
|
1432
|
+
* fake overlay), this tour never fakes anything. Each of the 5 bubbles anchors
|
|
1433
|
+
* a REAL rail element (`data-rail-list`, `data-toolbar-expand`,
|
|
1434
|
+
* `data-toolbar-settings`, the first `data-rail-dot`, the rail root), explains
|
|
1435
|
+
* what clicking that control ACTUALLY does, and auto-advances by OBSERVING the
|
|
1436
|
+
* live state the rail already owns:
|
|
1437
|
+
* - bubble 1 (expand arrow) waits for `toolbarExpanded` → true — the user
|
|
1438
|
+
* really clicked the arrow (「下一步」 remains a fallback);
|
|
1439
|
+
* - bubble 2 (settings gear) waits for `settingsOpen` → true — the settings
|
|
1440
|
+
* panel appearing IS the proof — and SUSPENDS while the modal is open
|
|
1441
|
+
* (state is kept; closing settings resumes the bubble at the current step);
|
|
1442
|
+
* - bubble 3 (dots) needs no interaction — hover/click are explained.
|
|
1505
1443
|
*
|
|
1506
1444
|
* Trigger/ownership lives in MilestoneRail (mount timer + settings reopen);
|
|
1507
|
-
* this component is
|
|
1508
|
-
*
|
|
1509
|
-
* close
|
|
1510
|
-
*
|
|
1445
|
+
* this component is presentation + state machine only. It persists the
|
|
1446
|
+
* `dsh-milestone.onboarded` flag itself via onboarding-store on IMPRESSION
|
|
1447
|
+
* (showing bubble 0 is enough) and on every close path (skip / Esc / finish).
|
|
1448
|
+
*
|
|
1449
|
+
* Positioning: `position: fixed`, opened on the target's FREE side (rail on
|
|
1450
|
+
* the right → bubble left of the target; rail on the left → bubble right of
|
|
1451
|
+
* it), measured with getBoundingClientRect and clamped to the viewport.
|
|
1452
|
+
* Re-measured on step changes, whenever the anchor may have (un)mounted (the
|
|
1453
|
+
* forced toolbar expansion), and on window resize. There is NO overlay and the
|
|
1454
|
+
* bubble never covers its own target — the user is supposed to CLICK the real
|
|
1455
|
+
* controls, so pointer-events live only on the bubble box itself.
|
|
1511
1456
|
*
|
|
1512
|
-
*
|
|
1513
|
-
*
|
|
1514
|
-
*
|
|
1515
|
-
* `--ms-accent*` CSS variables), and a small ONBOARDING_CSS block for the
|
|
1516
|
-
* hover / :focus-visible / reduced-motion rules inline styles cannot express.
|
|
1457
|
+
* Anchor guarantee: entering any step ≥ 2 calls `onSetToolbarExpanded(true)`
|
|
1458
|
+
* because `data-toolbar-settings` renders only while the toolbar is expanded
|
|
1459
|
+
* (or the gear pinned) — the 「下一步」 fallback path lands there un-expanded.
|
|
1517
1460
|
*
|
|
1518
|
-
*
|
|
1519
|
-
*
|
|
1520
|
-
*
|
|
1521
|
-
*
|
|
1522
|
-
* - The flag is written on IMPRESSION (the welcome page showing is enough);
|
|
1523
|
-
* skip / finish / Escape re-assert it. A mid-tour refresh/crash never
|
|
1524
|
-
* re-pops — replay lives in settings 重新查看教程.
|
|
1461
|
+
* Visual language reuses modal-tokens (MODAL_TIP_BG panel, one border tone,
|
|
1462
|
+
* the 12/8px radius scale, three text tiers) and the rail root's inherited
|
|
1463
|
+
* `--ms-accent*` CSS variables. The highlight ring is zero-asset: a
|
|
1464
|
+
* `data-tour-highlight` attribute + one injected CSS rule.
|
|
1525
1465
|
*/
|
|
1526
|
-
/**
|
|
1466
|
+
/** The tour bubble floats above every other rail layer (settings overlay 105). */
|
|
1467
|
+
const TOUR_Z = 106;
|
|
1468
|
+
/** Gap between the target element and the bubble (px). */
|
|
1469
|
+
const TOUR_GAP = 12;
|
|
1470
|
+
/** Bubble width (px) — the left-edge positioning math clamps against it. */
|
|
1471
|
+
const TOUR_WIDTH = 280;
|
|
1472
|
+
/** Minimum padding between the bubble and the viewport edges (px). */
|
|
1473
|
+
const VIEWPORT_PAD = 8;
|
|
1474
|
+
/** Fallback bubble height used when the layout has not settled (px). */
|
|
1475
|
+
const TOUR_HEIGHT_FALLBACK = 120;
|
|
1476
|
+
/** Progress-dot count — the 5 bubble steps (0 = welcome … 4 = done). */
|
|
1527
1477
|
const STEP_COUNT = [
|
|
1478
|
+
0,
|
|
1528
1479
|
1,
|
|
1529
1480
|
2,
|
|
1530
1481
|
3,
|
|
1531
1482
|
4
|
|
1532
1483
|
];
|
|
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
1484
|
/**
|
|
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.
|
|
1485
|
+
* Static tour styling that `:hover`/`:focus-visible`/reduced-motion need (the
|
|
1486
|
+
* `:focus-visible` ring and the base-state backgrounds inline styles cannot
|
|
1487
|
+
* duplicate). BASE-states first — the UA default button face must never flood
|
|
1488
|
+
* through on the dark bubble (that exact bug already shipped once).
|
|
1558
1489
|
*/
|
|
1559
|
-
const
|
|
1560
|
-
@keyframes ms-
|
|
1561
|
-
[data-
|
|
1562
|
-
|
|
1563
|
-
[data-
|
|
1564
|
-
[data-
|
|
1565
|
-
[data-
|
|
1566
|
-
|
|
1567
|
-
[data-
|
|
1490
|
+
const TOUR_CSS = `
|
|
1491
|
+
@keyframes ms-tour-fade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
|
|
1492
|
+
[data-tour-bubble] { animation: ms-tour-fade 160ms ease; }
|
|
1493
|
+
/* BASE-state backgrounds for every bubble control (transparent or themed). */
|
|
1494
|
+
[data-tour-skip] { background: transparent; }
|
|
1495
|
+
[data-tour-prev] { background: rgba(255, 255, 255, 0.06); }
|
|
1496
|
+
[data-tour-primary] { background: var(--ms-accent-bg); color: var(--ms-accent-soft); }
|
|
1497
|
+
/* Hover washes on top of the explicit base states. */
|
|
1498
|
+
[data-tour-skip]:hover { background: rgba(255, 255, 255, 0.08); }
|
|
1499
|
+
[data-tour-prev]:hover:not(:disabled) { background: rgba(255, 255, 255, 0.12); }
|
|
1500
|
+
[data-tour-primary]:hover { background: rgba(255, 255, 255, 0.14); }
|
|
1501
|
+
/* ONE accent focus ring for keyboard use. */
|
|
1502
|
+
[data-tour-skip]:focus-visible, [data-tour-prev]:focus-visible, [data-tour-primary]:focus-visible {
|
|
1568
1503
|
box-shadow: 0 0 0 2px var(--ms-accent-soft);
|
|
1569
1504
|
}
|
|
1570
|
-
|
|
1571
|
-
[data-
|
|
1505
|
+
/* The real-target highlight ring: accent halo + a subtle scale hint. */
|
|
1506
|
+
[data-tour-highlight] {
|
|
1507
|
+
box-shadow: 0 0 0 2px var(--ms-accent-soft), 0 0 0 4px var(--ms-accent);
|
|
1508
|
+
transform: scale(1.04);
|
|
1509
|
+
transition: box-shadow 140ms ease, transform 140ms ease;
|
|
1510
|
+
}
|
|
1572
1511
|
@media (prefers-reduced-motion: reduce) {
|
|
1573
|
-
[data-
|
|
1574
|
-
[data-
|
|
1575
|
-
[data-
|
|
1512
|
+
[data-tour-bubble] { animation: none; }
|
|
1513
|
+
[data-tour-highlight] { transform: none; transition: none; }
|
|
1514
|
+
[data-tour-skip], [data-tour-prev], [data-tour-primary] { transition: none; }
|
|
1576
1515
|
}
|
|
1577
1516
|
`;
|
|
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
|
-
});
|
|
1517
|
+
/** Rail root — every step's fallback anchor and bubble 4's target. */
|
|
1518
|
+
function railRoot() {
|
|
1519
|
+
return document.querySelector("[data-side]");
|
|
1989
1520
|
}
|
|
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
|
-
});
|
|
1521
|
+
const STEP_DEFS = [
|
|
1522
|
+
{
|
|
1523
|
+
titleKey: "tour.welcome.title",
|
|
1524
|
+
descKey: "tour.welcome.desc",
|
|
1525
|
+
anchor: () => document.querySelector("[data-rail-list]")
|
|
1526
|
+
},
|
|
1527
|
+
{
|
|
1528
|
+
titleKey: "tour.step1.title",
|
|
1529
|
+
descKey: "tour.step1.desc",
|
|
1530
|
+
anchor: () => document.querySelector("[data-toolbar-expand]")
|
|
1531
|
+
},
|
|
1532
|
+
{
|
|
1533
|
+
titleKey: "tour.step2.title",
|
|
1534
|
+
descKey: "tour.step2.desc",
|
|
1535
|
+
anchor: () => document.querySelector("[data-toolbar-settings]")
|
|
1536
|
+
},
|
|
1537
|
+
{
|
|
1538
|
+
titleKey: "tour.step3.title",
|
|
1539
|
+
descKey: "tour.step3.desc",
|
|
1540
|
+
anchor: () => document.querySelector("[data-rail-dot]") ?? railRoot()
|
|
1541
|
+
},
|
|
1542
|
+
{
|
|
1543
|
+
titleKey: "tour.step4.title",
|
|
1544
|
+
descKey: "tour.step4.desc",
|
|
1545
|
+
anchor: () => railRoot()
|
|
1546
|
+
}
|
|
1547
|
+
];
|
|
1548
|
+
/** Clamp `value` into [min, max], never letting min exceed max. */
|
|
1549
|
+
function clamp(value, min, max) {
|
|
1550
|
+
return Math.min(Math.max(value, min), Math.max(min, max));
|
|
2082
1551
|
}
|
|
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 }) {
|
|
1552
|
+
function MilestoneTour({ t, side, toolbarExpanded, settingsOpen, onSetToolbarExpanded, onClose }) {
|
|
2093
1553
|
const [step, setStep] = (0, react.useState)(0);
|
|
2094
|
-
const
|
|
1554
|
+
const [pos, setPos] = (0, react.useState)(null);
|
|
1555
|
+
const bubbleRef = (0, react.useRef)(null);
|
|
1556
|
+
const primaryRef = (0, react.useRef)(null);
|
|
1557
|
+
const stepDef = STEP_DEFS[step];
|
|
2095
1558
|
(0, react.useEffect)(() => {
|
|
2096
1559
|
writeOnboardedFlag();
|
|
2097
1560
|
}, []);
|
|
@@ -2101,274 +1564,227 @@ window.__ModuleLoader__.load({
|
|
|
2101
1564
|
onClose();
|
|
2102
1565
|
}, [onClose]);
|
|
2103
1566
|
(0, react.useEffect)(() => {
|
|
1567
|
+
if (settingsOpen) return;
|
|
2104
1568
|
const onKey = (e) => {
|
|
2105
1569
|
if (e.key !== "Escape") return;
|
|
2106
1570
|
finish();
|
|
2107
1571
|
};
|
|
2108
1572
|
window.addEventListener("keydown", onKey);
|
|
2109
1573
|
return () => window.removeEventListener("keydown", onKey);
|
|
2110
|
-
}, [finish]);
|
|
1574
|
+
}, [finish, settingsOpen]);
|
|
1575
|
+
(0, react.useEffect)(() => {
|
|
1576
|
+
if (step === 1 && toolbarExpanded) setStep(2);
|
|
1577
|
+
}, [step, toolbarExpanded]);
|
|
2111
1578
|
(0, react.useEffect)(() => {
|
|
2112
|
-
|
|
2113
|
-
}, [step]);
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
1579
|
+
if (step === 2 && settingsOpen) setStep(3);
|
|
1580
|
+
}, [step, settingsOpen]);
|
|
1581
|
+
(0, react.useEffect)(() => {
|
|
1582
|
+
if (step >= 2 && !toolbarExpanded) onSetToolbarExpanded(true);
|
|
1583
|
+
}, [
|
|
1584
|
+
step,
|
|
1585
|
+
toolbarExpanded,
|
|
1586
|
+
onSetToolbarExpanded
|
|
1587
|
+
]);
|
|
1588
|
+
/** Measure the current anchor and place the bubble on its free side. */
|
|
1589
|
+
const measure = (0, react.useCallback)(() => {
|
|
1590
|
+
const anchor = STEP_DEFS[step].anchor();
|
|
1591
|
+
const bubble = bubbleRef.current;
|
|
1592
|
+
if (anchor === null || bubble === null) {
|
|
1593
|
+
setPos(null);
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
const rect = anchor.getBoundingClientRect();
|
|
1597
|
+
const bw = bubble.offsetWidth || TOUR_WIDTH;
|
|
1598
|
+
const bh = bubble.offsetHeight || TOUR_HEIGHT_FALLBACK;
|
|
1599
|
+
const left = clamp(side === "left" ? rect.right + TOUR_GAP : rect.left - TOUR_GAP - bw, VIEWPORT_PAD, window.innerWidth - bw - VIEWPORT_PAD);
|
|
1600
|
+
const top = clamp(rect.top + rect.height / 2 - bh / 2, VIEWPORT_PAD, window.innerHeight - bh - VIEWPORT_PAD);
|
|
1601
|
+
setPos({
|
|
1602
|
+
left,
|
|
1603
|
+
top
|
|
1604
|
+
});
|
|
1605
|
+
}, [step, side]);
|
|
1606
|
+
(0, react.useLayoutEffect)(() => {
|
|
1607
|
+
measure();
|
|
1608
|
+
window.addEventListener("resize", measure);
|
|
1609
|
+
return () => window.removeEventListener("resize", measure);
|
|
1610
|
+
}, [
|
|
1611
|
+
measure,
|
|
1612
|
+
toolbarExpanded,
|
|
1613
|
+
settingsOpen
|
|
1614
|
+
]);
|
|
1615
|
+
(0, react.useEffect)(() => {
|
|
1616
|
+
document.querySelectorAll("[data-tour-highlight]").forEach((el) => el.removeAttribute("data-tour-highlight"));
|
|
1617
|
+
const anchor = STEP_DEFS[step].anchor();
|
|
1618
|
+
if (anchor !== null) anchor.setAttribute("data-tour-highlight", "");
|
|
1619
|
+
return () => {
|
|
1620
|
+
document.querySelectorAll("[data-tour-highlight]").forEach((el) => el.removeAttribute("data-tour-highlight"));
|
|
1621
|
+
};
|
|
1622
|
+
}, [
|
|
1623
|
+
step,
|
|
1624
|
+
toolbarExpanded,
|
|
1625
|
+
settingsOpen
|
|
1626
|
+
]);
|
|
1627
|
+
(0, react.useEffect)(() => {
|
|
1628
|
+
if (settingsOpen) return;
|
|
1629
|
+
primaryRef.current?.focus();
|
|
1630
|
+
}, [step, settingsOpen]);
|
|
1631
|
+
const goNext = () => setStep((s) => Math.min(STEP_COUNT.length - 1, s + 1));
|
|
1632
|
+
const goPrev = () => setStep((s) => Math.max(0, s - 1));
|
|
1633
|
+
if (settingsOpen) return null;
|
|
1634
|
+
const isLast = step === STEP_COUNT.length - 1;
|
|
1635
|
+
const primaryLabel = step === 0 || isLast ? t("tour.start") : t("tour.next");
|
|
1636
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1637
|
+
ref: bubbleRef,
|
|
1638
|
+
"data-tour-bubble": true,
|
|
1639
|
+
"data-tour-step": step,
|
|
1640
|
+
role: "dialog",
|
|
1641
|
+
"aria-label": t(stepDef.titleKey),
|
|
2118
1642
|
style: {
|
|
2119
1643
|
position: "fixed",
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
padding:
|
|
1644
|
+
left: pos?.left,
|
|
1645
|
+
top: pos?.top,
|
|
1646
|
+
width: TOUR_WIDTH,
|
|
1647
|
+
maxWidth: "calc(100vw - 24px)",
|
|
1648
|
+
boxSizing: "border-box",
|
|
1649
|
+
zIndex: TOUR_Z,
|
|
1650
|
+
padding: 14,
|
|
1651
|
+
background: MODAL_TIP_BG,
|
|
1652
|
+
color: MODAL_FG,
|
|
1653
|
+
borderRadius: 12,
|
|
1654
|
+
border: `1px solid ${MODAL_BORDER}`,
|
|
1655
|
+
boxShadow: "0 12px 36px rgba(0, 0, 0, 0.45)"
|
|
2127
1656
|
},
|
|
2128
|
-
children:
|
|
2129
|
-
|
|
2130
|
-
"
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
1657
|
+
children: [
|
|
1658
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: TOUR_CSS }),
|
|
1659
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1660
|
+
style: {
|
|
1661
|
+
display: "flex",
|
|
1662
|
+
alignItems: "flex-start",
|
|
1663
|
+
justifyContent: "space-between",
|
|
1664
|
+
gap: 8
|
|
1665
|
+
},
|
|
1666
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1667
|
+
"data-tour-title": true,
|
|
1668
|
+
style: {
|
|
1669
|
+
fontSize: 14,
|
|
1670
|
+
fontWeight: 700,
|
|
1671
|
+
color: MODAL_FG,
|
|
1672
|
+
lineHeight: 1.35
|
|
1673
|
+
},
|
|
1674
|
+
children: t(stepDef.titleKey)
|
|
1675
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1676
|
+
type: "button",
|
|
1677
|
+
"data-tour-skip": true,
|
|
1678
|
+
onClick: finish,
|
|
1679
|
+
style: {
|
|
1680
|
+
flexShrink: 0,
|
|
1681
|
+
background: "transparent",
|
|
1682
|
+
border: "none",
|
|
1683
|
+
padding: "2px 6px",
|
|
1684
|
+
borderRadius: 8,
|
|
1685
|
+
cursor: "pointer",
|
|
1686
|
+
color: MODAL_HINT,
|
|
1687
|
+
fontSize: 12,
|
|
1688
|
+
lineHeight: 1.4
|
|
1689
|
+
},
|
|
1690
|
+
children: t("tour.skip")
|
|
1691
|
+
})]
|
|
1692
|
+
}),
|
|
1693
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1694
|
+
"data-tour-desc": true,
|
|
1695
|
+
style: {
|
|
1696
|
+
marginTop: 6,
|
|
1697
|
+
fontSize: 12.5,
|
|
1698
|
+
lineHeight: 1.5,
|
|
1699
|
+
color: MODAL_TEXT
|
|
1700
|
+
},
|
|
1701
|
+
children: t(stepDef.descKey)
|
|
1702
|
+
}),
|
|
1703
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
1704
|
+
style: {
|
|
1705
|
+
display: "flex",
|
|
1706
|
+
alignItems: "center",
|
|
1707
|
+
justifyContent: "space-between",
|
|
1708
|
+
gap: 10,
|
|
1709
|
+
marginTop: 12
|
|
1710
|
+
},
|
|
2149
1711
|
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,
|
|
1712
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1713
|
+
type: "button",
|
|
1714
|
+
"data-tour-prev": true,
|
|
1715
|
+
disabled: step === 0,
|
|
1716
|
+
onClick: goPrev,
|
|
2161
1717
|
style: {
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
1718
|
+
flexShrink: 0,
|
|
1719
|
+
background: "rgba(255, 255, 255, 0.06)",
|
|
1720
|
+
border: `1px solid ${MODAL_BORDER}`,
|
|
1721
|
+
borderRadius: 8,
|
|
1722
|
+
padding: "5px 12px",
|
|
1723
|
+
cursor: step === 0 ? "default" : "pointer",
|
|
1724
|
+
color: step === 0 ? MODAL_HINT : MODAL_TEXT,
|
|
1725
|
+
fontSize: 12,
|
|
1726
|
+
lineHeight: 1.4,
|
|
1727
|
+
opacity: step === 0 ? .55 : 1
|
|
2166
1728
|
},
|
|
2167
|
-
children: t("
|
|
1729
|
+
children: t("tour.prev")
|
|
2168
1730
|
}),
|
|
2169
1731
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2170
|
-
"data-onboarding-welcome-actions": true,
|
|
2171
1732
|
style: {
|
|
2172
1733
|
display: "flex",
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
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
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2197
|
-
style: {
|
|
2198
|
-
display: "flex",
|
|
2199
|
-
alignItems: "flex-start",
|
|
2200
|
-
justifyContent: "space-between",
|
|
2201
|
-
gap: 10
|
|
1734
|
+
flexDirection: "column",
|
|
1735
|
+
alignItems: "center",
|
|
1736
|
+
gap: 5,
|
|
1737
|
+
minWidth: 74
|
|
2202
1738
|
},
|
|
2203
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2204
|
-
"data-
|
|
1739
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1740
|
+
"data-tour-progress": true,
|
|
2205
1741
|
style: {
|
|
2206
|
-
fontSize:
|
|
2207
|
-
|
|
2208
|
-
|
|
1742
|
+
fontSize: 11.5,
|
|
1743
|
+
color: MODAL_HINT,
|
|
1744
|
+
lineHeight: 1.2
|
|
2209
1745
|
},
|
|
2210
|
-
children:
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
1746
|
+
children: t("tour.step.label", { n: step + 1 })
|
|
1747
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1748
|
+
style: {
|
|
1749
|
+
display: "flex",
|
|
1750
|
+
gap: 4
|
|
1751
|
+
},
|
|
1752
|
+
children: STEP_COUNT.map((n) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1753
|
+
"data-tour-progress-dot": true,
|
|
1754
|
+
"data-active": step === n ? "true" : void 0,
|
|
1755
|
+
"aria-label": t("tour.step.short", { n: n + 1 }),
|
|
1756
|
+
"aria-current": step === n ? "step" : void 0,
|
|
1757
|
+
style: {
|
|
1758
|
+
width: 14,
|
|
1759
|
+
height: 4,
|
|
1760
|
+
borderRadius: 2,
|
|
1761
|
+
background: step === n ? "var(--ms-accent)" : "rgba(255, 255, 255, 0.18)"
|
|
1762
|
+
}
|
|
1763
|
+
}, n))
|
|
2222
1764
|
})]
|
|
2223
1765
|
}),
|
|
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,
|
|
1766
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1767
|
+
type: "button",
|
|
1768
|
+
ref: primaryRef,
|
|
1769
|
+
"data-tour-primary": true,
|
|
1770
|
+
onClick: isLast ? finish : goNext,
|
|
2280
1771
|
style: {
|
|
2281
|
-
|
|
2282
|
-
|
|
1772
|
+
flexShrink: 0,
|
|
1773
|
+
background: "var(--ms-accent-bg)",
|
|
1774
|
+
color: "var(--ms-accent-soft)",
|
|
1775
|
+
border: "1px solid var(--ms-accent)",
|
|
2283
1776
|
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
|
|
1777
|
+
padding: "5px 14px",
|
|
1778
|
+
cursor: "pointer",
|
|
1779
|
+
fontSize: 12.5,
|
|
1780
|
+
fontWeight: 600,
|
|
1781
|
+
lineHeight: 1.4
|
|
2310
1782
|
},
|
|
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
|
-
]
|
|
1783
|
+
children: primaryLabel
|
|
2368
1784
|
})
|
|
2369
1785
|
]
|
|
2370
|
-
})
|
|
2371
|
-
|
|
1786
|
+
})
|
|
1787
|
+
]
|
|
2372
1788
|
});
|
|
2373
1789
|
}
|
|
2374
1790
|
//#endregion
|
|
@@ -3317,6 +2733,13 @@ window.__ModuleLoader__.load({
|
|
|
3317
2733
|
return fresh;
|
|
3318
2734
|
}
|
|
3319
2735
|
//#endregion
|
|
2736
|
+
//#region src/client/version-meta.ts
|
|
2737
|
+
/**
|
|
2738
|
+
* Installed plugin version. Injected at build time as
|
|
2739
|
+
* `__DSH_MILESTONE_VERSION__`; falls back to `0.0.0-dev` when unbuilt.
|
|
2740
|
+
*/
|
|
2741
|
+
const PLUGIN_VERSION = "0.6.5";
|
|
2742
|
+
//#endregion
|
|
3320
2743
|
//#region src/client/MilestoneRail.tsx
|
|
3321
2744
|
/**
|
|
3322
2745
|
* MilestoneRail: the milestone.rail entry (session scope). Renders a fixed
|
|
@@ -3499,6 +2922,14 @@ window.__ModuleLoader__.load({
|
|
|
3499
2922
|
[data-toolbar-settings-close]:hover { background: rgba(255, 255, 255, 0.08); }
|
|
3500
2923
|
[data-toolbar-settings-reset], [data-onboarding-reopen] { background: rgba(255, 255, 255, 0.06); }
|
|
3501
2924
|
[data-toolbar-settings-reset]:hover, [data-onboarding-reopen]:hover { background: rgba(255, 255, 255, 0.1); }
|
|
2925
|
+
/* BASE state reset: every modal surface must sit transparent on the dark
|
|
2926
|
+
panel — without it the UA default button face (light gray) floods through
|
|
2927
|
+
and rows become unreadable light-on-light. Hover washes above take over
|
|
2928
|
+
on interaction. */
|
|
2929
|
+
[data-toolbar-pin-toggle], [data-personal-toggle], [data-focus-toggle-settings],
|
|
2930
|
+
[data-toolbar-settings-close], [data-focus-option] {
|
|
2931
|
+
background: transparent;
|
|
2932
|
+
}
|
|
3502
2933
|
/* ONE accent ring for keyboard focus on every modal control. */
|
|
3503
2934
|
[data-toolbar-pin-toggle]:focus-visible, [data-personal-toggle]:focus-visible,
|
|
3504
2935
|
[data-focus-toggle-settings]:focus-visible,
|
|
@@ -3579,8 +3010,8 @@ window.__ModuleLoader__.load({
|
|
|
3579
3010
|
/** B4 update-check: mount-time silent check delay (ms) — give the harness
|
|
3580
3011
|
* time to settle before hitting the registry. */
|
|
3581
3012
|
const UPDATE_CHECK_MOUNT_DELAY = 1500;
|
|
3582
|
-
/** 0.6.
|
|
3583
|
-
* before the
|
|
3013
|
+
/** 0.6.5 first-run coach tour: mount-time show delay (ms) — let the rail settle
|
|
3014
|
+
* before the first bubble pops. */
|
|
3584
3015
|
const ONBOARDING_MOUNT_DELAY = 800;
|
|
3585
3016
|
/** Initial state: no check has completed yet, nothing to show. */
|
|
3586
3017
|
const NO_UPDATE_CHECK = {
|
|
@@ -3809,7 +3240,8 @@ window.__ModuleLoader__.load({
|
|
|
3809
3240
|
const [expandHovered, setExpandHovered] = (0, react.useState)(false);
|
|
3810
3241
|
const [settingsHovered, setSettingsHovered] = (0, react.useState)(false);
|
|
3811
3242
|
const [settingsOpen, setSettingsOpen] = (0, react.useState)(false);
|
|
3812
|
-
const [
|
|
3243
|
+
const [tourOpen, setTourOpen] = (0, react.useState)(false);
|
|
3244
|
+
const [tourRun, setTourRun] = (0, react.useState)(0);
|
|
3813
3245
|
/** The feature whose near-row description tip is currently visible
|
|
3814
3246
|
* (`null` = none — tips only appear on hover/focus of their own row). */
|
|
3815
3247
|
const [descFeature, setDescFeature] = (0, react.useState)(null);
|
|
@@ -3891,7 +3323,7 @@ window.__ModuleLoader__.load({
|
|
|
3891
3323
|
}, []);
|
|
3892
3324
|
(0, react.useEffect)(() => {
|
|
3893
3325
|
const timer = window.setTimeout(() => {
|
|
3894
|
-
if (!readOnboardedFlag())
|
|
3326
|
+
if (!readOnboardedFlag()) setTourOpen(true);
|
|
3895
3327
|
}, ONBOARDING_MOUNT_DELAY);
|
|
3896
3328
|
return () => window.clearTimeout(timer);
|
|
3897
3329
|
}, []);
|
|
@@ -4058,12 +3490,14 @@ window.__ModuleLoader__.load({
|
|
|
4058
3490
|
setSettingsOpen(false);
|
|
4059
3491
|
settingsBtnRef.current?.focus();
|
|
4060
3492
|
};
|
|
4061
|
-
/** 0.6.
|
|
4062
|
-
* immediately (the flag may or may not be set; skipping/completing it
|
|
4063
|
-
* re-persists the flag anyway).
|
|
4064
|
-
|
|
3493
|
+
/** 0.6.5: settings → 重新查看教程 — close settings and replay the coach
|
|
3494
|
+
* tour immediately (the flag may or may not be set; skipping/completing it
|
|
3495
|
+
* re-persists the flag anyway). The `tourRun` bump remounts the tour so the
|
|
3496
|
+
* replay always restarts from bubble 0. */
|
|
3497
|
+
const reopenTour = () => {
|
|
4065
3498
|
setSettingsOpen(false);
|
|
4066
|
-
|
|
3499
|
+
setTourRun((n) => n + 1);
|
|
3500
|
+
setTourOpen(true);
|
|
4067
3501
|
};
|
|
4068
3502
|
/** B1: a feature renders while the toolbar is EXPANDED or while it is pinned. */
|
|
4069
3503
|
const featureVisible = (id) => toolbarExpanded || pinned.includes(id);
|
|
@@ -5345,7 +4779,7 @@ window.__ModuleLoader__.load({
|
|
|
5345
4779
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5346
4780
|
type: "button",
|
|
5347
4781
|
"data-onboarding-reopen": true,
|
|
5348
|
-
onClick:
|
|
4782
|
+
onClick: reopenTour,
|
|
5349
4783
|
style: {
|
|
5350
4784
|
padding: "7px 16px",
|
|
5351
4785
|
border: `1px solid rgba(255, 255, 255, 0.14)`,
|
|
@@ -5354,17 +4788,20 @@ window.__ModuleLoader__.load({
|
|
|
5354
4788
|
color: "#b9c2d4",
|
|
5355
4789
|
fontSize: 12.5
|
|
5356
4790
|
},
|
|
5357
|
-
children: t("
|
|
4791
|
+
children: t("tour.reopen")
|
|
5358
4792
|
})]
|
|
5359
4793
|
})
|
|
5360
4794
|
]
|
|
5361
4795
|
})
|
|
5362
4796
|
}),
|
|
5363
|
-
|
|
4797
|
+
tourOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MilestoneTour, {
|
|
5364
4798
|
t,
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
4799
|
+
side,
|
|
4800
|
+
toolbarExpanded,
|
|
4801
|
+
settingsOpen,
|
|
4802
|
+
onSetToolbarExpanded: setToolbarExpanded,
|
|
4803
|
+
onClose: () => setTourOpen(false)
|
|
4804
|
+
}, tourRun),
|
|
5368
4805
|
updateOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5369
4806
|
ref: updatePanelRef,
|
|
5370
4807
|
"data-update-panel": true,
|
|
@@ -5401,7 +4838,7 @@ window.__ModuleLoader__.load({
|
|
|
5401
4838
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5402
4839
|
style: { color: "#8b96ab" },
|
|
5403
4840
|
children: [t("update.current"), ": "]
|
|
5404
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "0.6.
|
|
4841
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "0.6.5" })] }),
|
|
5405
4842
|
updateCheck.phase === "ok" && updateCheck.latest !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5406
4843
|
"data-update-latest": true,
|
|
5407
4844
|
children: [
|