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.
Files changed (3) hide show
  1. package/README.md +9 -2
  2. package/lib/client.js +391 -954
  3. 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.4 onboarding: welcome title (also the tutorial dialog's aria-label). */
800
- "onboarding.welcome.title": "欢迎使用 dsh-milestone",
801
- /** 0.6.4 onboarding: one-line positioning statement on the welcome page. */
802
- "onboarding.welcome.subtitle": "一条附着在会话侧边的里程碑导航:悬停圆点看元信息,点击跳到任意提问。",
803
- /** 0.6.4 onboarding: primary action on the welcome page → step 1. */
804
- "onboarding.start": "开始引导",
805
- /** 0.6.4 onboarding: skip action — persists the flag and closes, on EVERY page. */
806
- "onboarding.skip": "跳过",
807
- /** 0.6.4 onboarding: step-page progress text `第 n / 4 步`. */
808
- "onboarding.step.label": "第 {n} / 4 步",
809
- /** 0.6.4 onboarding: aria-label on one progress dot. */
810
- "onboarding.step.short": "第 {n} 步",
811
- /** 0.6.4 onboarding: step-page back navigation. */
812
- "onboarding.prev": "上一步",
813
- /** 0.6.4 onboarding: step-page forward navigation (steps 1–3). */
814
- "onboarding.next": "下一步",
815
- /** 0.6.4 onboarding: step-4 primary action — persists the flag and closes. */
816
- "onboarding.finish": "开始使用",
817
- /** 0.6.4 onboarding: step 1 title — the dot timeline. */
818
- "onboarding.step1.title": "圆点时间线",
819
- /** 0.6.4 onboarding: step 1 line 1. */
820
- "onboarding.step1.desc1": "每条提问在侧边对应一个圆点,自上而下按时间排列。",
821
- /** 0.6.4 onboarding: step 1 line 2. */
822
- "onboarding.step1.desc2": "悬停圆点,查看该轮元信息(用时、首字、模型与 Token)。",
823
- /** 0.6.4 onboarding: step 1 line 3. */
824
- "onboarding.step1.desc3": "点击任意圆点,会话立即跳到那条提问。",
825
- /** 0.6.4 onboarding: step 2 title — in-rail search & bookmarks. */
826
- "onboarding.step2.title": "搜索与收藏",
827
- /** 0.6.4 onboarding: step 2 line 1. */
828
- "onboarding.step2.desc1": "输入关键词过滤圆点,只点亮匹配项并显示 N / M 计数;Enter 逐个跳转,Esc 关闭。",
829
- /** 0.6.4 onboarding: step 2 line 2. */
830
- "onboarding.step2.desc2": "星标收藏重要消息,再用「只看收藏」一键过滤。",
831
- /** 0.6.4 onboarding: step 3 title — personalization & settings. */
832
- "onboarding.step3.title": "个性化与设置",
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
- "onboarding.welcome.title": "Welcome to dsh-milestone",
971
- "onboarding.welcome.subtitle": "A milestone rail beside your conversation: hover a dot for metadata, click any dot to jump.",
972
- "onboarding.start": "Start the tour",
973
- "onboarding.skip": "Skip",
974
- "onboarding.step.label": "Step {n} of 4",
975
- "onboarding.step.short": "Step {n}",
976
- "onboarding.prev": "Back",
977
- "onboarding.next": "Next",
978
- "onboarding.finish": "Get started",
979
- "onboarding.step1.title": "The dot timeline",
980
- "onboarding.step1.desc1": "Every prompt is a dot on the side rail, listed top to bottom in time order.",
981
- "onboarding.step1.desc2": "Hover a dot to see that turn’s metadata (duration, first token, model, tokens).",
982
- "onboarding.step1.desc3": "Click any dot to jump the conversation straight to that prompt.",
983
- "onboarding.step2.title": "Search & bookmarks",
984
- "onboarding.step2.desc1": "Type to filter the dots — matches light up with an N / M counter; Enter jumps, Esc closes.",
985
- "onboarding.step2.desc2": "Star a message to bookmark it, then use “Bookmarks only” to filter.",
986
- "onboarding.step3.title": "Personalize & settings",
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/version-meta.ts
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
- * MilestoneOnboarding: the 0.6.4 first-run tutorial modal.
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 a fully SELF-CONTAINED dialog: it carries the welcome
1508
- * page, the 4 step pages with built-in toy demos, step progress, and the
1509
- * close paths (skip / finish / Escape), and it persists the
1510
- * `dsh-milestone.onboarded` flag itself via onboarding-store.
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
- * Visual language is copied from the settings modal (MilestoneRail's
1513
- * MODAL_CSS): dark `rgba(20, 24, 32, 0.98)` panel, three text tiers, 12px
1514
- * panel / 8px control radii, one accent (inherited through the rail root's
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
- * Deliberate non-features:
1519
- * - NO outside-pointerdown dismissal (a misclick must never swallow the
1520
- * tutorial — the requirement explicitly keeps it off).
1521
- * - Escape ALWAYS means "skip": it persists the flag and closes.
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
- /** Step-page count — the progress indicator shape. */
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 onboarding-modal styling that needs `:hover`/`:focus-visible`
1553
- * (inline styles cannot express them): one accent focus ring for every
1554
- * control, hover washes, the demo-link micro-lift, the panel fade-in, and the
1555
- * reduced-motion kill — mirrors the settings MODAL_CSS pattern. Accent values
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 ONBOARDING_CSS = `
1560
- @keyframes ms-onboarding-fade { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: none; } }
1561
- [data-onboarding-panel] { animation: ms-onboarding-fade 160ms ease; }
1562
- [data-onboarding-skip]:hover { background: rgba(255, 255, 255, 0.08); }
1563
- [data-onboarding-prev]:hover, [data-onboarding-next]:hover, [data-onboarding-start]:hover, [data-onboarding-finish]:hover { background: rgba(255, 255, 255, 0.1); }
1564
- [data-onboarding-skip]:focus-visible, [data-onboarding-prev]:focus-visible,
1565
- [data-onboarding-next]:focus-visible, [data-onboarding-start]:focus-visible,
1566
- [data-onboarding-finish]:focus-visible, [data-demo-star]:focus-visible,
1567
- [data-demo-dot]:focus-visible, [data-demo-search-input]:focus-visible {
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
- [data-demo-link] { transition: transform 120ms ease, border-color 120ms ease, background 120ms ease; }
1571
- [data-demo-link]:hover { transform: translateY(-2px); border-color: var(--ms-accent); background: rgba(255, 255, 255, 0.09); }
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-onboarding-panel] { animation: none; }
1574
- [data-onboarding-skip], [data-onboarding-prev], [data-onboarding-next],
1575
- [data-onboarding-start], [data-onboarding-finish], [data-demo-link] { transition: none; }
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
- /** Primary (accent) dialog action — start / next / finish. */
1579
- const PRIMARY_BUTTON = {
1580
- background: "var(--ms-accent-bg)",
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
- /** Step-4 demo: update-check pill + the four support links (repo/star/issues/npm). */
1991
- function DemoSupport({ t }) {
1992
- const linkStyle = {
1993
- display: "flex",
1994
- alignItems: "center",
1995
- justifyContent: "center",
1996
- padding: "7px 6px",
1997
- borderRadius: 8,
1998
- background: "rgba(255, 255, 255, 0.05)",
1999
- border: `1px solid ${MODAL_BORDER}`,
2000
- color: MODAL_TITLE,
2001
- fontSize: 11.5,
2002
- textDecoration: "none",
2003
- textAlign: "center",
2004
- lineHeight: 1.35
2005
- };
2006
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2007
- "data-demo-support": true,
2008
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2009
- "data-demo-update-pill": true,
2010
- style: {
2011
- display: "inline-flex",
2012
- alignItems: "center",
2013
- gap: 6,
2014
- padding: "4px 10px",
2015
- borderRadius: 999,
2016
- background: "rgba(255, 255, 255, 0.06)",
2017
- border: `1px solid ${MODAL_BORDER}`,
2018
- color: MODAL_TEXT,
2019
- fontSize: 12
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 panelRef = (0, react.useRef)(null);
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
- panelRef.current?.querySelector("[data-onboarding-start], [data-onboarding-next], [data-onboarding-finish]")?.focus();
2113
- }, [step]);
2114
- const goNext = () => setStep((s) => Math.min(4, s + 1));
2115
- const goPrev = () => setStep((s) => Math.max(1, s - 1));
2116
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2117
- "data-onboarding-overlay": true,
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
- inset: 0,
2121
- background: "rgba(8, 10, 15, 0.55)",
2122
- zIndex: ONBOARDING_Z,
2123
- display: "flex",
2124
- alignItems: "center",
2125
- justifyContent: "center",
2126
- padding: 16
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: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2129
- ref: panelRef,
2130
- "data-onboarding-panel": true,
2131
- role: "dialog",
2132
- "aria-modal": "true",
2133
- "aria-label": t("onboarding.welcome.title"),
2134
- onClick: (e) => e.stopPropagation(),
2135
- style: {
2136
- width: "min(560px, 92vw)",
2137
- maxHeight: "80vh",
2138
- overflowY: "auto",
2139
- padding: 20,
2140
- background: MODAL_BG,
2141
- color: MODAL_FG,
2142
- borderRadius: 12,
2143
- boxShadow: "0 24px 64px rgba(0, 0, 0, 0.55)",
2144
- scrollbarWidth: "thin",
2145
- scrollbarColor: "rgba(255, 255, 255, 0.2) transparent"
2146
- },
2147
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: ONBOARDING_CSS }), step === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2148
- "data-onboarding-welcome": true,
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)("div", {
2151
- "data-onboarding-welcome-title": true,
2152
- style: {
2153
- fontSize: 17,
2154
- fontWeight: 700,
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
- marginTop: 8,
2163
- fontSize: 13,
2164
- lineHeight: 1.65,
2165
- color: MODAL_TEXT
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("onboarding.welcome.subtitle")
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
- 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
- /* @__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.jsxs)("div", {
2204
- "data-onboarding-step-title": true,
1739
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1740
+ "data-tour-progress": true,
2205
1741
  style: {
2206
- fontSize: 15,
2207
- fontWeight: 600,
2208
- color: MODAL_FG
1742
+ fontSize: 11.5,
1743
+ color: MODAL_HINT,
1744
+ lineHeight: 1.2
2209
1745
  },
2210
- children: [
2211
- step === 1 && t("onboarding.step1.title"),
2212
- step === 2 && t("onboarding.step2.title"),
2213
- step === 3 && t("onboarding.step3.title"),
2214
- step === 4 && t("onboarding.step4.title")
2215
- ]
2216
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2217
- type: "button",
2218
- "data-onboarding-skip": true,
2219
- onClick: finish,
2220
- style: SKIP_BUTTON,
2221
- children: t("onboarding.skip")
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.jsxs)("div", {
2225
- style: {
2226
- display: "flex",
2227
- flexDirection: "column",
2228
- gap: 6,
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
- marginTop: 14,
2282
- padding: 12,
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
- background: MODAL_TIP_BG,
2285
- border: `1px solid ${MODAL_BORDER}`
2286
- },
2287
- children: [
2288
- step === 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DemoDotRail, {
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.4 first-run tutorial: mount-time show delay (ms) — let the rail settle
3583
- * before the onboarding modal pops. */
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 [onboardingOpen, setOnboardingOpen] = (0, react.useState)(false);
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()) setOnboardingOpen(true);
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.4: settings → 重新查看教程 — close settings and replay the tutorial
4062
- * immediately (the flag may or may not be set; skipping/completing it
4063
- * re-persists the flag anyway). */
4064
- const reopenOnboarding = () => {
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
- setOnboardingOpen(true);
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: reopenOnboarding,
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("onboarding.reopen")
4791
+ children: t("tour.reopen")
5358
4792
  })]
5359
4793
  })
5360
4794
  ]
5361
4795
  })
5362
4796
  }),
5363
- onboardingOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MilestoneOnboarding, {
4797
+ tourOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MilestoneTour, {
5364
4798
  t,
5365
- accent,
5366
- onClose: () => setOnboardingOpen(false)
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.4" })] }),
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: [