dsh-milestone 0.6.2 → 0.6.4
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 +21 -4
- package/lib/client.js +1750 -170
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -682,6 +682,8 @@ window.__ModuleLoader__.load({
|
|
|
682
682
|
"settings.title": "设置",
|
|
683
683
|
/** Per-feature toggle label inside the settings menu: keep visible while collapsed. */
|
|
684
684
|
"settings.pin": "在折叠外显示",
|
|
685
|
+
/** Settings: feature-section hint explaining what the pin switch does. */
|
|
686
|
+
"settings.pin.hint": "开启后,工具栏折叠时该功能仍显示在箭头旁",
|
|
685
687
|
/** Settings action that clears every pinned feature. */
|
|
686
688
|
"settings.reset": "恢复默认",
|
|
687
689
|
/** Settings footer heading above the project links. */
|
|
@@ -702,6 +704,16 @@ window.__ModuleLoader__.load({
|
|
|
702
704
|
"settings.section.features": "功能与快捷区",
|
|
703
705
|
/** Settings modal: section heading for the personalization controls. */
|
|
704
706
|
"settings.section.personal": "个性化",
|
|
707
|
+
/** Settings modal: section heading for the focus-mode controls (0.6.3). */
|
|
708
|
+
"settings.section.focus": "聚焦",
|
|
709
|
+
/** Settings: personalization-section hint shown inside the expanded block. */
|
|
710
|
+
"settings.personal.hint": "圆点、强调色与位置,即调即存",
|
|
711
|
+
/** Settings: aria-label on the personalization block toggle while COLLAPSED. */
|
|
712
|
+
"settings.personal.expand": "展开个性化设置",
|
|
713
|
+
/** Settings: aria-label on the personalization block toggle while EXPANDED. */
|
|
714
|
+
"settings.personal.collapse": "收起个性化设置",
|
|
715
|
+
/** Settings: live value summary on the collapsed personalization header. */
|
|
716
|
+
"settings.personal.summary": "{accent} · 图标 {icon}px · {side}",
|
|
705
717
|
/** Settings: hover description — in-rail search. */
|
|
706
718
|
"settings.desc.search": "按完整消息内容过滤并跳转到对应消息",
|
|
707
719
|
/** Settings: hover description — all-prompts list. */
|
|
@@ -730,6 +742,30 @@ window.__ModuleLoader__.load({
|
|
|
730
742
|
"settings.side.left": "左侧",
|
|
731
743
|
/** Settings personalization: side radio — hug the right edge. */
|
|
732
744
|
"settings.side.right": "右侧",
|
|
745
|
+
/** Settings: focus block — hint shown inside the expanded block. */
|
|
746
|
+
"settings.focus.hint": "这些选项自由组合成你的「聚焦搭配」;总开关仍是工具栏的眼睛按钮",
|
|
747
|
+
/** Settings: aria-label on the focus block toggle while COLLAPSED. */
|
|
748
|
+
"settings.focus.expand": "展开聚焦设置",
|
|
749
|
+
/** Settings: aria-label on the focus block toggle while EXPANDED. */
|
|
750
|
+
"settings.focus.collapse": "收起聚焦设置",
|
|
751
|
+
/** Settings: focus option — dim the think reasoning disclosures. */
|
|
752
|
+
"settings.focus.dimThink": "淡化 think 推理区",
|
|
753
|
+
/** Settings: focus option — dim the tool-call cards. */
|
|
754
|
+
"settings.focus.dimTools": "淡化工具调用卡片",
|
|
755
|
+
/** Settings: focus option — compress think disclosures to a hover strip. */
|
|
756
|
+
"settings.focus.collapseThink": "折叠 think",
|
|
757
|
+
/** Settings: focus option — dim strength slider label. */
|
|
758
|
+
"settings.focus.opacity": "淡化强度",
|
|
759
|
+
/** Settings: live value summary on the collapsed focus header. */
|
|
760
|
+
"settings.focus.summary": "{opts} · 强度 {opacity}%",
|
|
761
|
+
/** Settings: focus summary — short label for the think-dim option. */
|
|
762
|
+
"settings.focus.summary.think": "think 淡化",
|
|
763
|
+
/** Settings: focus summary — short label for the tool-dim option. */
|
|
764
|
+
"settings.focus.summary.tools": "工具淡化",
|
|
765
|
+
/** Settings: focus summary — short label for the think-collapse option. */
|
|
766
|
+
"settings.focus.summary.collapse": "折叠 think",
|
|
767
|
+
/** Settings: focus summary — placeholder when every option is off. */
|
|
768
|
+
"settings.focus.summary.none": "未启用",
|
|
733
769
|
/** B4 update-check: toolbar button label + title/aria-label. */
|
|
734
770
|
"update.check": "检查更新",
|
|
735
771
|
/** B4 update-check: popover title. */
|
|
@@ -759,7 +795,73 @@ window.__ModuleLoader__.load({
|
|
|
759
795
|
/** Language option: force Chinese copy. */
|
|
760
796
|
"settings.lang.zh": "中文",
|
|
761
797
|
/** Language option: force English copy. */
|
|
762
|
-
"settings.lang.en": "English"
|
|
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": "重新查看教程"
|
|
763
865
|
};
|
|
764
866
|
const en = {
|
|
765
867
|
"jump.to": "Jump to message {n}",
|
|
@@ -808,6 +910,7 @@ window.__ModuleLoader__.load({
|
|
|
808
910
|
"toolbar.settings.close": "Close settings",
|
|
809
911
|
"settings.title": "Settings",
|
|
810
912
|
"settings.pin": "Show outside collapse",
|
|
913
|
+
"settings.pin.hint": "When on, the feature stays beside the arrow while the toolbar is folded",
|
|
811
914
|
"settings.reset": "Restore defaults",
|
|
812
915
|
"settings.support": "Support us",
|
|
813
916
|
"settings.repo": "GitHub repo",
|
|
@@ -818,6 +921,11 @@ window.__ModuleLoader__.load({
|
|
|
818
921
|
"settings.close": "Close",
|
|
819
922
|
"settings.section.features": "Features & Shortcuts",
|
|
820
923
|
"settings.section.personal": "Personalization",
|
|
924
|
+
"settings.section.focus": "Focus",
|
|
925
|
+
"settings.personal.hint": "Dot size, accent color, and position — saved as you adjust",
|
|
926
|
+
"settings.personal.expand": "Expand personalization",
|
|
927
|
+
"settings.personal.collapse": "Collapse personalization",
|
|
928
|
+
"settings.personal.summary": "{accent} · Icon {icon}px · {side}",
|
|
821
929
|
"settings.desc.search": "Filter by full message text and jump to the match",
|
|
822
930
|
"settings.desc.list": "Overview of every prompt in this session",
|
|
823
931
|
"settings.desc.sessionSearch": "Search across all sessions",
|
|
@@ -832,6 +940,18 @@ window.__ModuleLoader__.load({
|
|
|
832
940
|
"settings.side": "Position",
|
|
833
941
|
"settings.side.left": "Left",
|
|
834
942
|
"settings.side.right": "Right",
|
|
943
|
+
"settings.focus.hint": "Combine these options into your own focus recipe; the eye button on the toolbar stays the master switch",
|
|
944
|
+
"settings.focus.expand": "Expand focus settings",
|
|
945
|
+
"settings.focus.collapse": "Collapse focus settings",
|
|
946
|
+
"settings.focus.dimThink": "Dim think reasoning",
|
|
947
|
+
"settings.focus.dimTools": "Dim tool call cards",
|
|
948
|
+
"settings.focus.collapseThink": "Collapse think",
|
|
949
|
+
"settings.focus.opacity": "Dim strength",
|
|
950
|
+
"settings.focus.summary": "{opts} · Strength {opacity}%",
|
|
951
|
+
"settings.focus.summary.think": "Think dim",
|
|
952
|
+
"settings.focus.summary.tools": "Tools dim",
|
|
953
|
+
"settings.focus.summary.collapse": "Think collapse",
|
|
954
|
+
"settings.focus.summary.none": "Off",
|
|
835
955
|
"update.check": "Check updates",
|
|
836
956
|
"update.title": "Update check",
|
|
837
957
|
"update.current": "Current version",
|
|
@@ -846,7 +966,40 @@ window.__ModuleLoader__.load({
|
|
|
846
966
|
"settings.language": "Language",
|
|
847
967
|
"settings.lang.system": "Follow system",
|
|
848
968
|
"settings.lang.zh": "Chinese",
|
|
849
|
-
"settings.lang.en": "English"
|
|
969
|
+
"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"
|
|
850
1003
|
};
|
|
851
1004
|
/**
|
|
852
1005
|
* Interpolate `{name}` placeholders with params, matching the harness t seat's
|
|
@@ -1275,6 +1428,950 @@ window.__ModuleLoader__.load({
|
|
|
1275
1428
|
});
|
|
1276
1429
|
}
|
|
1277
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
|
+
const MODAL_FG = "#e6e8ee";
|
|
1447
|
+
const MODAL_TITLE = "#c7cede";
|
|
1448
|
+
const MODAL_TEXT = "#b9c2d4";
|
|
1449
|
+
const MODAL_HINT = "#8b96ab";
|
|
1450
|
+
const MODAL_BORDER = "rgba(255, 255, 255, 0.14)";
|
|
1451
|
+
const MODAL_TIP_BG = "#222834";
|
|
1452
|
+
//#endregion
|
|
1453
|
+
//#region src/client/onboarding-store.ts
|
|
1454
|
+
/**
|
|
1455
|
+
* onboarding-store: the persistence layer for the 0.6.4 first-run tutorial —
|
|
1456
|
+
* a single "has the user seen it" flag with NO payload.
|
|
1457
|
+
*
|
|
1458
|
+
* Storage contract: one localStorage key (`dsh-milestone.onboarded`) whose
|
|
1459
|
+
* VALUE is exactly the string `'1'` when the tutorial was completed or
|
|
1460
|
+
* skipped. Anything else (absent key, `'0'`, `'true'`, garbage) means "never
|
|
1461
|
+
* shown" and re-triggers the tutorial.
|
|
1462
|
+
*
|
|
1463
|
+
* Both accessors swallow storage failures silently (SSR, sandboxed iframe,
|
|
1464
|
+
* quota): the tutorial is a best-effort enhancement — a broken storage must
|
|
1465
|
+
* never crash the rail, and a user in that environment simply sees the
|
|
1466
|
+
* tutorial once per page load instead of once ever.
|
|
1467
|
+
*/
|
|
1468
|
+
/** The single localStorage key holding the onboarding "seen" flag. */
|
|
1469
|
+
const ONBOARDED_KEY = "dsh-milestone.onboarded";
|
|
1470
|
+
/**
|
|
1471
|
+
* True when the user already completed or skipped the tutorial (the stored
|
|
1472
|
+
* value is exactly `'1'`). Storage unavailability degrades to `false`.
|
|
1473
|
+
*/
|
|
1474
|
+
function readOnboardedFlag() {
|
|
1475
|
+
try {
|
|
1476
|
+
return localStorage.getItem(ONBOARDED_KEY) === "1";
|
|
1477
|
+
} catch {
|
|
1478
|
+
return false;
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* Persist the tutorial as seen/completed by writing the literal string `'1'`.
|
|
1483
|
+
* Storage failures are swallowed — the flag simply will not survive a reload.
|
|
1484
|
+
*/
|
|
1485
|
+
function writeOnboardedFlag() {
|
|
1486
|
+
try {
|
|
1487
|
+
localStorage.setItem(ONBOARDED_KEY, "1");
|
|
1488
|
+
} catch {}
|
|
1489
|
+
}
|
|
1490
|
+
//#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
|
|
1503
|
+
/**
|
|
1504
|
+
* MilestoneOnboarding: the 0.6.4 first-run tutorial modal.
|
|
1505
|
+
*
|
|
1506
|
+
* 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.
|
|
1511
|
+
*
|
|
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.
|
|
1517
|
+
*
|
|
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 重新查看教程.
|
|
1525
|
+
*/
|
|
1526
|
+
/** Step-page count — the progress indicator shape. */
|
|
1527
|
+
const STEP_COUNT = [
|
|
1528
|
+
1,
|
|
1529
|
+
2,
|
|
1530
|
+
3,
|
|
1531
|
+
4
|
|
1532
|
+
];
|
|
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
|
+
/**
|
|
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.
|
|
1558
|
+
*/
|
|
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 {
|
|
1568
|
+
box-shadow: 0 0 0 2px var(--ms-accent-soft);
|
|
1569
|
+
}
|
|
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); }
|
|
1572
|
+
@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; }
|
|
1576
|
+
}
|
|
1577
|
+
`;
|
|
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
|
+
});
|
|
1989
|
+
}
|
|
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
|
+
});
|
|
2082
|
+
}
|
|
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 }) {
|
|
2093
|
+
const [step, setStep] = (0, react.useState)(0);
|
|
2094
|
+
const panelRef = (0, react.useRef)(null);
|
|
2095
|
+
(0, react.useEffect)(() => {
|
|
2096
|
+
writeOnboardedFlag();
|
|
2097
|
+
}, []);
|
|
2098
|
+
/** Skip / finish / Escape converge here: persist, then close. */
|
|
2099
|
+
const finish = (0, react.useCallback)(() => {
|
|
2100
|
+
writeOnboardedFlag();
|
|
2101
|
+
onClose();
|
|
2102
|
+
}, [onClose]);
|
|
2103
|
+
(0, react.useEffect)(() => {
|
|
2104
|
+
const onKey = (e) => {
|
|
2105
|
+
if (e.key !== "Escape") return;
|
|
2106
|
+
finish();
|
|
2107
|
+
};
|
|
2108
|
+
window.addEventListener("keydown", onKey);
|
|
2109
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
2110
|
+
}, [finish]);
|
|
2111
|
+
(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,
|
|
2118
|
+
style: {
|
|
2119
|
+
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
|
|
2127
|
+
},
|
|
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,
|
|
2149
|
+
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,
|
|
2161
|
+
style: {
|
|
2162
|
+
marginTop: 8,
|
|
2163
|
+
fontSize: 13,
|
|
2164
|
+
lineHeight: 1.65,
|
|
2165
|
+
color: MODAL_TEXT
|
|
2166
|
+
},
|
|
2167
|
+
children: t("onboarding.welcome.subtitle")
|
|
2168
|
+
}),
|
|
2169
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2170
|
+
"data-onboarding-welcome-actions": true,
|
|
2171
|
+
style: {
|
|
2172
|
+
display: "flex",
|
|
2173
|
+
justifyContent: "center",
|
|
2174
|
+
gap: 10,
|
|
2175
|
+
marginTop: 18
|
|
2176
|
+
},
|
|
2177
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2178
|
+
type: "button",
|
|
2179
|
+
"data-onboarding-start": true,
|
|
2180
|
+
onClick: () => setStep(1),
|
|
2181
|
+
style: PRIMARY_BUTTON,
|
|
2182
|
+
children: t("onboarding.start")
|
|
2183
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2184
|
+
type: "button",
|
|
2185
|
+
"data-onboarding-skip": true,
|
|
2186
|
+
onClick: finish,
|
|
2187
|
+
style: GHOST_BUTTON,
|
|
2188
|
+
children: t("onboarding.skip")
|
|
2189
|
+
})]
|
|
2190
|
+
})
|
|
2191
|
+
]
|
|
2192
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2193
|
+
"data-onboarding-step": true,
|
|
2194
|
+
"data-step": step,
|
|
2195
|
+
children: [
|
|
2196
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2197
|
+
style: {
|
|
2198
|
+
display: "flex",
|
|
2199
|
+
alignItems: "flex-start",
|
|
2200
|
+
justifyContent: "space-between",
|
|
2201
|
+
gap: 10
|
|
2202
|
+
},
|
|
2203
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2204
|
+
"data-onboarding-step-title": true,
|
|
2205
|
+
style: {
|
|
2206
|
+
fontSize: 15,
|
|
2207
|
+
fontWeight: 600,
|
|
2208
|
+
color: MODAL_FG
|
|
2209
|
+
},
|
|
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")
|
|
2222
|
+
})]
|
|
2223
|
+
}),
|
|
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,
|
|
2280
|
+
style: {
|
|
2281
|
+
marginTop: 14,
|
|
2282
|
+
padding: 12,
|
|
2283
|
+
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
|
|
2310
|
+
},
|
|
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
|
+
]
|
|
2368
|
+
})
|
|
2369
|
+
]
|
|
2370
|
+
})]
|
|
2371
|
+
})
|
|
2372
|
+
});
|
|
2373
|
+
}
|
|
2374
|
+
//#endregion
|
|
1278
2375
|
//#region src/client/MilestoneRailTooltip.tsx
|
|
1279
2376
|
/**
|
|
1280
2377
|
* @param props - the hovered mark + bookmark wiring (see {@link MilestoneRailTooltipProps}).
|
|
@@ -1739,11 +2836,15 @@ window.__ModuleLoader__.load({
|
|
|
1739
2836
|
* JSON object:
|
|
1740
2837
|
*
|
|
1741
2838
|
* { "pinned": string[], "accent": "#rrggbb", "iconSize": number,
|
|
1742
|
-
* "inset": number, "side": "left" | "right", "locale": "system"|"zh"|"en"
|
|
2839
|
+
* "inset": number, "side": "left" | "right", "locale": "system"|"zh"|"en",
|
|
2840
|
+
* "focus": { "dimThink": boolean, "dimTools": boolean,
|
|
2841
|
+
* "collapseThink": boolean, "opacity": number } }
|
|
1743
2842
|
*
|
|
1744
2843
|
* Backward compatibility: the pre-personalization blob `{ "pinned": string[] }`
|
|
1745
2844
|
* (and an entirely absent value) parses to the DEFAULT prefs with the new
|
|
1746
|
-
* fields at their defaults — old users keep their pins untouched.
|
|
2845
|
+
* fields at their defaults — old users keep their pins untouched. The same
|
|
2846
|
+
* rule covers the `focus` object: a blob stored before 0.6.3 (no `focus`
|
|
2847
|
+
* field) gains the default focus mix.
|
|
1747
2848
|
*
|
|
1748
2849
|
* All reads are sanitized per field:
|
|
1749
2850
|
* - `pinned`: whitelisted ids only (`TOOLBAR_PIN_IDS`), duplicates dropped,
|
|
@@ -1752,7 +2853,10 @@ window.__ModuleLoader__.load({
|
|
|
1752
2853
|
* back to the default blue;
|
|
1753
2854
|
* - `iconSize` / `inset`: finite numbers snapped to the slider step
|
|
1754
2855
|
* (even values) and clamped to the slider range;
|
|
1755
|
-
* - `side`: exactly `'left'` or `'right'
|
|
2856
|
+
* - `side`: exactly `'left'` or `'right'`;
|
|
2857
|
+
* - `focus`: three booleans (`dimThink` / `dimTools` / `collapseThink`)
|
|
2858
|
+
* defaulting to `true` / `false` / `false`, plus the dim `opacity`
|
|
2859
|
+
* snapped to the 0.1 step and clamped to [0.2, 0.8].
|
|
1756
2860
|
*
|
|
1757
2861
|
* The whitelist lives HERE (not in MilestoneRail) so the pure functions stay
|
|
1758
2862
|
* dependency-free and unit-testable; MilestoneRail's feature registry keys
|
|
@@ -1776,14 +2880,27 @@ window.__ModuleLoader__.load({
|
|
|
1776
2880
|
"updateCheck",
|
|
1777
2881
|
"settings"
|
|
1778
2882
|
];
|
|
2883
|
+
/** The default accent (the classic milestone blue). */
|
|
2884
|
+
const DEFAULT_ACCENT = "#4d7cfd";
|
|
2885
|
+
/** Slider domain for the focus dim strength (0.2 = 20% .. 0.8 = 80%). */
|
|
2886
|
+
const FOCUS_OPACITY_MIN = .2;
|
|
2887
|
+
const FOCUS_OPACITY_MAX = .8;
|
|
2888
|
+
/** The canonical default focus mix (dim think at 40%; tools/collapse off). */
|
|
2889
|
+
const DEFAULT_FOCUS_PREFS = {
|
|
2890
|
+
dimThink: true,
|
|
2891
|
+
dimTools: false,
|
|
2892
|
+
collapseThink: false,
|
|
2893
|
+
opacity: .4
|
|
2894
|
+
};
|
|
1779
2895
|
/** The canonical default prefs ("恢复默认" target; also the read fallback). */
|
|
1780
2896
|
const DEFAULT_PREFS = {
|
|
1781
2897
|
pinned: [],
|
|
1782
|
-
accent:
|
|
2898
|
+
accent: DEFAULT_ACCENT,
|
|
1783
2899
|
iconSize: 28,
|
|
1784
2900
|
inset: 14,
|
|
1785
2901
|
side: "right",
|
|
1786
|
-
locale: "system"
|
|
2902
|
+
locale: "system",
|
|
2903
|
+
focus: { ...DEFAULT_FOCUS_PREFS }
|
|
1787
2904
|
};
|
|
1788
2905
|
/** Type guard for registry ids — unknown strings never survive a parse. */
|
|
1789
2906
|
function isToolbarPinId(id) {
|
|
@@ -1811,11 +2928,36 @@ window.__ModuleLoader__.load({
|
|
|
1811
2928
|
const snapped = Math.round(Math.min(max, Math.max(min, value)) / step) * step;
|
|
1812
2929
|
return Math.min(max, Math.max(min, snapped));
|
|
1813
2930
|
}
|
|
2931
|
+
/** Boolean sanitizer for the focus mix flags: only `true`/`false` survive. */
|
|
2932
|
+
function sanitizeBoolean(value, fallback) {
|
|
2933
|
+
return typeof value === "boolean" ? value : fallback;
|
|
2934
|
+
}
|
|
2935
|
+
/**
|
|
2936
|
+
* Focus-mix sanitizer: `dimTools`/`collapseThink` default off, `dimThink`
|
|
2937
|
+
* defaults ON (the classic pre-0.6.3 behavior), and `opacity` snaps to the
|
|
2938
|
+
* 0.1 step inside [0.2, 0.8]. Snapping works in tenths (step × 10 = integer)
|
|
2939
|
+
* instead of a raw division so hand-edited floats like `0.30000000000000004`
|
|
2940
|
+
* always converge on exactly `0.3`.
|
|
2941
|
+
*/
|
|
2942
|
+
function sanitizeFocus(raw) {
|
|
2943
|
+
if (typeof raw !== "object" || raw === null) return { ...DEFAULT_FOCUS_PREFS };
|
|
2944
|
+
const { dimThink, dimTools, collapseThink, opacity } = raw;
|
|
2945
|
+
const clamped = typeof opacity === "number" && Number.isFinite(opacity) ? Math.min(FOCUS_OPACITY_MAX, Math.max(FOCUS_OPACITY_MIN, opacity)) : DEFAULT_FOCUS_PREFS.opacity;
|
|
2946
|
+
const tenths = Math.round(clamped * 10);
|
|
2947
|
+
const snapped = Math.min(FOCUS_OPACITY_MAX, Math.max(FOCUS_OPACITY_MIN, tenths / 10));
|
|
2948
|
+
return {
|
|
2949
|
+
dimThink: sanitizeBoolean(dimThink, DEFAULT_FOCUS_PREFS.dimThink),
|
|
2950
|
+
dimTools: sanitizeBoolean(dimTools, DEFAULT_FOCUS_PREFS.dimTools),
|
|
2951
|
+
collapseThink: sanitizeBoolean(collapseThink, DEFAULT_FOCUS_PREFS.collapseThink),
|
|
2952
|
+
opacity: snapped
|
|
2953
|
+
};
|
|
2954
|
+
}
|
|
1814
2955
|
/**
|
|
1815
2956
|
* Parse + sanitize the raw persisted blob: `null` (nothing stored), invalid
|
|
1816
2957
|
* JSON, or a non-object shape all degrade to the DEFAULT prefs. Each field is
|
|
1817
2958
|
* sanitized independently, so a half-corrupt blob keeps its valid parts
|
|
1818
|
-
* (e.g. an old `{pinned}`-only blob gains the default accent/size/inset/side
|
|
2959
|
+
* (e.g. an old `{pinned}`-only blob gains the default accent/size/inset/side
|
|
2960
|
+
* AND the default focus mix).
|
|
1819
2961
|
*/
|
|
1820
2962
|
function parsePrefs(raw) {
|
|
1821
2963
|
if (raw === null) return { ...DEFAULT_PREFS };
|
|
@@ -1826,14 +2968,15 @@ window.__ModuleLoader__.load({
|
|
|
1826
2968
|
return { ...DEFAULT_PREFS };
|
|
1827
2969
|
}
|
|
1828
2970
|
if (typeof parsed !== "object" || parsed === null) return { ...DEFAULT_PREFS };
|
|
1829
|
-
const { pinned, accent, iconSize, inset, side, locale } = parsed;
|
|
2971
|
+
const { pinned, accent, iconSize, inset, side, locale, focus } = parsed;
|
|
1830
2972
|
return {
|
|
1831
2973
|
pinned: sanitizePinned(pinned),
|
|
1832
2974
|
accent: typeof accent === "string" && isHexColor(accent) ? accent.toLowerCase() : DEFAULT_PREFS.accent,
|
|
1833
2975
|
iconSize: clampStep(iconSize, 20, 36, 2, DEFAULT_PREFS.iconSize),
|
|
1834
2976
|
inset: clampStep(inset, 0, 40, 2, DEFAULT_PREFS.inset),
|
|
1835
2977
|
side: side === "left" || side === "right" ? side : DEFAULT_PREFS.side,
|
|
1836
|
-
locale: locale === "zh" || locale === "en" || locale === "system" ? locale : DEFAULT_PREFS.locale
|
|
2978
|
+
locale: locale === "zh" || locale === "en" || locale === "system" ? locale : DEFAULT_PREFS.locale,
|
|
2979
|
+
focus: sanitizeFocus(focus)
|
|
1837
2980
|
};
|
|
1838
2981
|
}
|
|
1839
2982
|
/**
|
|
@@ -2174,13 +3317,6 @@ window.__ModuleLoader__.load({
|
|
|
2174
3317
|
return fresh;
|
|
2175
3318
|
}
|
|
2176
3319
|
//#endregion
|
|
2177
|
-
//#region src/client/version-meta.ts
|
|
2178
|
-
/**
|
|
2179
|
-
* Installed plugin version. Injected at build time as
|
|
2180
|
-
* `__DSH_MILESTONE_VERSION__`; falls back to `0.0.0-dev` when unbuilt.
|
|
2181
|
-
*/
|
|
2182
|
-
const PLUGIN_VERSION = "0.6.2";
|
|
2183
|
-
//#endregion
|
|
2184
3320
|
//#region src/client/MilestoneRail.tsx
|
|
2185
3321
|
/**
|
|
2186
3322
|
* MilestoneRail: the milestone.rail entry (session scope). Renders a fixed
|
|
@@ -2224,6 +3360,13 @@ window.__ModuleLoader__.load({
|
|
|
2224
3360
|
* hover descriptions, the personalization controls, and the support-us card
|
|
2225
3361
|
* grid); everything the modal changes persists under `dsh-milestone.toolbar`.
|
|
2226
3362
|
*/
|
|
3363
|
+
/**
|
|
3364
|
+
* Settings modal shared palette + geometry — ONE source in modal-tokens.ts,
|
|
3365
|
+
* also consumed by the 0.6.4 onboarding tutorial modal so the two dialogs
|
|
3366
|
+
* cannot drift. Dark panel on a dark host, three text tiers (primary /
|
|
3367
|
+
* section title / hint + muted), one border tone, a 12px panel / 8px control
|
|
3368
|
+
* radius scale and a 4-unit spacing scale (4 / 8 / 12 / 16 / 20).
|
|
3369
|
+
*/
|
|
2227
3370
|
/** Minimum user messages before the rail adds value. */
|
|
2228
3371
|
const MIN_MARKS = 2;
|
|
2229
3372
|
const PREVIEW_LENGTH = 80;
|
|
@@ -2266,39 +3409,161 @@ window.__ModuleLoader__.load({
|
|
|
2266
3409
|
/** Tooltip anchor width: its maxWidth cap, so a tooltip never overlaps the rail. */
|
|
2267
3410
|
const TOOLTIP_ANCHOR_WIDTH = 300;
|
|
2268
3411
|
/**
|
|
2269
|
-
* P3 focus mode:
|
|
2270
|
-
*
|
|
2271
|
-
*
|
|
2272
|
-
*
|
|
2273
|
-
*
|
|
2274
|
-
*
|
|
2275
|
-
*
|
|
3412
|
+
* P3 focus mode (0.6.3: user-tuned "聚焦搭配"): when the eye toggle is armed,
|
|
3413
|
+
* an inline <style> (zero-asset, same pattern as the original FOCUS_CSS)
|
|
3414
|
+
* dims/collapses the harness content classes the USER opted into, at the
|
|
3415
|
+
* strength the user picked. Which content to dim and whether to additionally
|
|
3416
|
+
* collapse think is a persisted `prefs.focus` mix; the master on/off switch
|
|
3417
|
+
* stays the toolbar eye button.
|
|
3418
|
+
*
|
|
3419
|
+
* STABLE SELECTORS — researched against the rc.2 official build products:
|
|
3420
|
+
*
|
|
3421
|
+
* - think: `dsh-client-ui-conversation` renders every assistant reasoning
|
|
3422
|
+
* disclosure as a root `div[data-variant="think"][data-state="running|ok"]`
|
|
3423
|
+
* (ReasoningRow). The reasoning body lives INSIDE that root — the
|
|
3424
|
+
* DisclosureRow's children — so the root is a single clampable container:
|
|
3425
|
+
* CSS `max-height` + `overflow: hidden` collapses exactly the body while
|
|
3426
|
+
* the header row ("Think · summary") stays visible. → collapseThink is
|
|
3427
|
+
* feasible with pure CSS (no JS, no harness-internal interaction).
|
|
3428
|
+
*
|
|
3429
|
+
* - tool calls: `dsh-client-ui-tool` wraps EVERY atomic call (all ToolRow
|
|
3430
|
+
* presentation variants AND the bash-sample row) in
|
|
3431
|
+
* `div[data-chat-call-id]` (with `data-chat-anchor-key="call:<callId>"`);
|
|
3432
|
+
* no other card type in the harness uses that attribute (verified across
|
|
3433
|
+
* the whole @deepseek-ai install). → `[data-chat-call-id]` is the stable,
|
|
3434
|
+
* variant-independent tool-call-card selector.
|
|
3435
|
+
*
|
|
3436
|
+
* Hover/open restore mirrors the classic rule: `:hover` restores, and the
|
|
3437
|
+
* DisclosureRow inside the collapsed target sets `[data-open]` when the user
|
|
3438
|
+
* opens it (the DESCENDANT form `target [data-open]` is required — same as
|
|
3439
|
+
* pre-0.6.3). The collapse strip releases on hover AND on `[data-open]` so
|
|
3440
|
+
* an opened think disclosure never stays crushed.
|
|
3441
|
+
*/
|
|
3442
|
+
const FOCUS_SELECTOR_THINK = "[data-variant=\"think\"]";
|
|
3443
|
+
const FOCUS_SELECTOR_TOOL = "[data-chat-call-id]";
|
|
3444
|
+
/** Restored height when hovering/opening a collapsed think disclosure. */
|
|
3445
|
+
const FOCUS_THINK_MAX_HEIGHT = "78vh";
|
|
3446
|
+
/**
|
|
3447
|
+
* Compose the focus-mode stylesheet from the persisted focus mix. Pure —
|
|
3448
|
+
* exported so unit tests can pin the exact rule text. `''` when no option is
|
|
3449
|
+
* armed (the master switch simply injects nothing).
|
|
2276
3450
|
*/
|
|
2277
|
-
|
|
2278
|
-
|
|
3451
|
+
function buildFocusCss(focus) {
|
|
3452
|
+
const { dimThink, dimTools, collapseThink } = focus;
|
|
3453
|
+
const strength = focus.opacity.toFixed(1);
|
|
3454
|
+
const rules = [];
|
|
3455
|
+
if (dimThink || collapseThink) {
|
|
3456
|
+
const decls = [];
|
|
3457
|
+
if (dimThink) decls.push(`opacity: ${strength}`);
|
|
3458
|
+
if (collapseThink) decls.push(`max-height: 36px`, "overflow: hidden");
|
|
3459
|
+
rules.push(`${FOCUS_SELECTOR_THINK} { ${decls.join("; ")}; transition: opacity 0.2s${collapseThink ? ", max-height 0.2s" : ""}; }`);
|
|
3460
|
+
rules.push(`${FOCUS_SELECTOR_THINK}:hover, ${FOCUS_SELECTOR_THINK} [data-open] { opacity: 1;${collapseThink ? ` max-height: ${FOCUS_THINK_MAX_HEIGHT};` : ""} }`);
|
|
3461
|
+
}
|
|
3462
|
+
if (dimTools) {
|
|
3463
|
+
rules.push(`${FOCUS_SELECTOR_TOOL} { opacity: ${strength}; transition: opacity 0.2s; }`);
|
|
3464
|
+
rules.push(`${FOCUS_SELECTOR_TOOL}:hover, ${FOCUS_SELECTOR_TOOL} [data-open] { opacity: 1; }`);
|
|
3465
|
+
}
|
|
3466
|
+
return rules.join("\n");
|
|
3467
|
+
}
|
|
3468
|
+
/** Near-row description tip: how far its right edge sits from the row's right
|
|
3469
|
+
* edge — clears the 32px switch + its 10px padding. */
|
|
3470
|
+
const MODAL_TIP_RIGHT = 54;
|
|
2279
3471
|
/**
|
|
2280
|
-
* Static settings-modal styling that needs `:hover` (which
|
|
2281
|
-
* cannot express): the support-us card grid (micro-lift +
|
|
2282
|
-
*
|
|
2283
|
-
*
|
|
3472
|
+
* Static settings-modal styling that needs `:hover`/`:focus-visible` (which
|
|
3473
|
+
* inline styles cannot express): the support-us card grid (micro-lift +
|
|
3474
|
+
* accent highlight), the pin-row / personal-header hover washes, one accent
|
|
3475
|
+
* focus ring for every modal control, the near-row tip's little arrow, the
|
|
3476
|
+
* personalization body's reveal, and a themed thin scrollbar. Accent values
|
|
3477
|
+
* come from the rail root's CSS variables (`--ms-accent*`), so one block
|
|
3478
|
+
* serves every accent. Inline styles keep these elements background-free
|
|
3479
|
+
* (except where noted) so the `:hover` washes below actually win. The
|
|
2284
3480
|
* search-toggle recolor rule makes the EXTERNAL RailSearchUi chrome follow
|
|
2285
3481
|
* the accent too (that file is owned by an earlier phase and cannot change);
|
|
2286
|
-
* `!important` is required because
|
|
3482
|
+
* `!important` is required because that toggle's own inline styles win
|
|
2287
3483
|
* otherwise.
|
|
2288
3484
|
*/
|
|
2289
3485
|
const MODAL_CSS = `
|
|
2290
3486
|
[data-support-card] {
|
|
2291
3487
|
display: flex; align-items: center; justify-content: center; gap: 8px;
|
|
2292
3488
|
padding: 10px 12px; border-radius: 8px;
|
|
2293
|
-
background: rgba(255, 255, 255, 0.05); border: 1px solid
|
|
3489
|
+
background: rgba(255, 255, 255, 0.05); border: 1px solid ${MODAL_BORDER};
|
|
2294
3490
|
color: #c7cede; text-decoration: none; font-size: 12.5px; line-height: 1.4;
|
|
2295
3491
|
transition: transform 120ms ease, border-color 120ms ease, background 120ms ease;
|
|
2296
3492
|
}
|
|
2297
3493
|
[data-support-card]:hover { transform: translateY(-2px); border-color: var(--ms-accent); background: rgba(255, 255, 255, 0.09); }
|
|
3494
|
+
/* Row and header washes (the inline styles deliberately leave backgrounds
|
|
3495
|
+
unset so these rules win over the default padding-box background). */
|
|
2298
3496
|
[data-toolbar-pin-toggle]:hover, [data-toolbar-pin-toggle]:focus-visible { background: rgba(255, 255, 255, 0.06); }
|
|
3497
|
+
[data-personal-toggle]:hover, [data-focus-toggle-settings]:hover { background: rgba(255, 255, 255, 0.05); }
|
|
3498
|
+
[data-focus-option]:hover { background: rgba(255, 255, 255, 0.04); }
|
|
3499
|
+
[data-toolbar-settings-close]:hover { background: rgba(255, 255, 255, 0.08); }
|
|
3500
|
+
[data-toolbar-settings-reset], [data-onboarding-reopen] { background: rgba(255, 255, 255, 0.06); }
|
|
3501
|
+
[data-toolbar-settings-reset]:hover, [data-onboarding-reopen]:hover { background: rgba(255, 255, 255, 0.1); }
|
|
3502
|
+
/* ONE accent ring for keyboard focus on every modal control. */
|
|
3503
|
+
[data-toolbar-pin-toggle]:focus-visible, [data-personal-toggle]:focus-visible,
|
|
3504
|
+
[data-focus-toggle-settings]:focus-visible,
|
|
3505
|
+
[data-toolbar-settings-close]:focus-visible, [data-toolbar-settings-reset]:focus-visible,
|
|
3506
|
+
[data-onboarding-reopen]:focus-visible {
|
|
3507
|
+
box-shadow: 0 0 0 2px var(--ms-accent-soft);
|
|
3508
|
+
}
|
|
3509
|
+
/* Near-row description tip: a rotated square peeks out of the LEFT edge so
|
|
3510
|
+
the apex points back at the row's label. */
|
|
3511
|
+
[data-settings-tip]::before {
|
|
3512
|
+
content: ''; position: absolute; left: -3px; top: 50%;
|
|
3513
|
+
width: 7px; height: 7px; transform: translateY(-50%) rotate(45deg);
|
|
3514
|
+
background: ${MODAL_TIP_BG};
|
|
3515
|
+
border-left: 1px solid ${MODAL_BORDER};
|
|
3516
|
+
border-bottom: 1px solid ${MODAL_BORDER};
|
|
3517
|
+
}
|
|
3518
|
+
/* Tip + chevron motion lives here (not inline) so reduced-motion can kill it. */
|
|
3519
|
+
[data-settings-tip] { transition: opacity 140ms ease, transform 140ms ease, visibility 140ms; }
|
|
3520
|
+
[data-personal-toggle] svg, [data-focus-toggle-settings] svg { transition: transform 150ms ease; }
|
|
3521
|
+
/* One authored reveal: the personalization/focus bodies fade in on expand. */
|
|
3522
|
+
@keyframes ms-settings-fade { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: none; } }
|
|
3523
|
+
[data-settings-personal-body], [data-settings-focus-body] { animation: ms-settings-fade 140ms ease; }
|
|
3524
|
+
/* Thin themed scrollbar for the scrollable modal panel. */
|
|
3525
|
+
[data-toolbar-settings-panel]::-webkit-scrollbar { width: 10px; }
|
|
3526
|
+
[data-toolbar-settings-panel]::-webkit-scrollbar-thumb {
|
|
3527
|
+
background: rgba(255, 255, 255, 0.16); border-radius: 6px; border: 3px solid transparent; background-clip: padding-box;
|
|
3528
|
+
}
|
|
3529
|
+
[data-toolbar-settings-panel]::-webkit-scrollbar-track { background: transparent; }
|
|
3530
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3531
|
+
[data-settings-tip], [data-personal-toggle] svg, [data-focus-toggle-settings] svg, [data-support-card] { transition: none; }
|
|
3532
|
+
[data-settings-personal-body], [data-settings-focus-body] { animation: none; }
|
|
3533
|
+
}
|
|
2299
3534
|
[data-search-toggle] { color: #8b96ab !important; }
|
|
2300
3535
|
[data-search-toggle][aria-pressed="true"] { background: var(--ms-accent-bg) !important; color: var(--ms-accent-soft) !important; }
|
|
2301
3536
|
`;
|
|
3537
|
+
/**
|
|
3538
|
+
* Shared collapsible-section chrome (personalization + focus blocks): the
|
|
3539
|
+
* header button (chevron + title + live summary) and the summary text span.
|
|
3540
|
+
* One source so the two settings blocks cannot drift.
|
|
3541
|
+
*/
|
|
3542
|
+
const SECTION_TOGGLE_STYLE = {
|
|
3543
|
+
display: "flex",
|
|
3544
|
+
alignItems: "center",
|
|
3545
|
+
gap: 8,
|
|
3546
|
+
width: "100%",
|
|
3547
|
+
padding: "8px 10px",
|
|
3548
|
+
border: "none",
|
|
3549
|
+
borderRadius: 8,
|
|
3550
|
+
cursor: "pointer",
|
|
3551
|
+
color: MODAL_TITLE,
|
|
3552
|
+
fontSize: 13,
|
|
3553
|
+
fontWeight: 600,
|
|
3554
|
+
textAlign: "left"
|
|
3555
|
+
};
|
|
3556
|
+
const SECTION_SUMMARY_STYLE = {
|
|
3557
|
+
flex: 1,
|
|
3558
|
+
minWidth: 0,
|
|
3559
|
+
textAlign: "right",
|
|
3560
|
+
fontWeight: 400,
|
|
3561
|
+
fontSize: 12,
|
|
3562
|
+
color: MODAL_HINT,
|
|
3563
|
+
overflow: "hidden",
|
|
3564
|
+
textOverflow: "ellipsis",
|
|
3565
|
+
whiteSpace: "nowrap"
|
|
3566
|
+
};
|
|
2302
3567
|
/**
|
|
2303
3568
|
* P3 deep links (`#msg=<anchor-key>`): initial delay before the first
|
|
2304
3569
|
* deep-link attempt — the harness scrolls the conversation to the bottom on
|
|
@@ -2314,6 +3579,9 @@ window.__ModuleLoader__.load({
|
|
|
2314
3579
|
/** B4 update-check: mount-time silent check delay (ms) — give the harness
|
|
2315
3580
|
* time to settle before hitting the registry. */
|
|
2316
3581
|
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. */
|
|
3584
|
+
const ONBOARDING_MOUNT_DELAY = 800;
|
|
2317
3585
|
/** Initial state: no check has completed yet, nothing to show. */
|
|
2318
3586
|
const NO_UPDATE_CHECK = {
|
|
2319
3587
|
phase: "idle",
|
|
@@ -2501,6 +3769,28 @@ window.__ModuleLoader__.load({
|
|
|
2501
3769
|
return next;
|
|
2502
3770
|
});
|
|
2503
3771
|
};
|
|
3772
|
+
/** 0.6.3: patch ONE focus-mix flag/strength (nested field, same write-through). */
|
|
3773
|
+
const updateFocus = (patch) => {
|
|
3774
|
+
updatePrefs({ focus: {
|
|
3775
|
+
...prefs.focus,
|
|
3776
|
+
...patch
|
|
3777
|
+
} });
|
|
3778
|
+
};
|
|
3779
|
+
/**
|
|
3780
|
+
* 0.6.3: the focus block's live summary — the armed options joined into a
|
|
3781
|
+
* "聚焦搭配" line, plus the strength percentage. e.g. `think 淡化 · 强度 40%`.
|
|
3782
|
+
*/
|
|
3783
|
+
const focusSummary = (() => {
|
|
3784
|
+
const parts = [
|
|
3785
|
+
prefs.focus.dimThink ? t("settings.focus.summary.think") : null,
|
|
3786
|
+
prefs.focus.dimTools ? t("settings.focus.summary.tools") : null,
|
|
3787
|
+
prefs.focus.collapseThink ? t("settings.focus.summary.collapse") : null
|
|
3788
|
+
].filter((part) => part !== null);
|
|
3789
|
+
return t("settings.focus.summary", {
|
|
3790
|
+
opts: parts.length > 0 ? parts.join(" · ") : t("settings.focus.summary.none"),
|
|
3791
|
+
opacity: Math.round(prefs.focus.opacity * 100)
|
|
3792
|
+
});
|
|
3793
|
+
})();
|
|
2504
3794
|
/** B1: flip one feature's pin — state and the persisted blob update together. */
|
|
2505
3795
|
const onTogglePin = (id) => {
|
|
2506
3796
|
setPrefs((prev) => {
|
|
@@ -2519,9 +3809,16 @@ window.__ModuleLoader__.load({
|
|
|
2519
3809
|
const [expandHovered, setExpandHovered] = (0, react.useState)(false);
|
|
2520
3810
|
const [settingsHovered, setSettingsHovered] = (0, react.useState)(false);
|
|
2521
3811
|
const [settingsOpen, setSettingsOpen] = (0, react.useState)(false);
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
3812
|
+
const [onboardingOpen, setOnboardingOpen] = (0, react.useState)(false);
|
|
3813
|
+
/** The feature whose near-row description tip is currently visible
|
|
3814
|
+
* (`null` = none — tips only appear on hover/focus of their own row). */
|
|
3815
|
+
const [descFeature, setDescFeature] = (0, react.useState)(null);
|
|
3816
|
+
/** B-design: the personalization block collapsess by default so only a
|
|
3817
|
+
* value summary leads the section; expanding reveals the controls. */
|
|
3818
|
+
const [personalOpen, setPersonalOpen] = (0, react.useState)(false);
|
|
3819
|
+
/** B-design (0.6.3): the focus block mirrors the personalization block —
|
|
3820
|
+
* collapsed by default, the header leads with a live option summary. */
|
|
3821
|
+
const [focusOpen, setFocusOpen] = (0, react.useState)(false);
|
|
2525
3822
|
const settingsRef = (0, react.useRef)(null);
|
|
2526
3823
|
const settingsBtnRef = (0, react.useRef)(null);
|
|
2527
3824
|
const [updateOpen, setUpdateOpen] = (0, react.useState)(false);
|
|
@@ -2592,6 +3889,12 @@ window.__ModuleLoader__.load({
|
|
|
2592
3889
|
const timer = window.setTimeout(runUpdateCheck, UPDATE_CHECK_MOUNT_DELAY);
|
|
2593
3890
|
return () => window.clearTimeout(timer);
|
|
2594
3891
|
}, []);
|
|
3892
|
+
(0, react.useEffect)(() => {
|
|
3893
|
+
const timer = window.setTimeout(() => {
|
|
3894
|
+
if (!readOnboardedFlag()) setOnboardingOpen(true);
|
|
3895
|
+
}, ONBOARDING_MOUNT_DELAY);
|
|
3896
|
+
return () => window.clearTimeout(timer);
|
|
3897
|
+
}, []);
|
|
2595
3898
|
useOutsideDismiss(updatePanelRef, updateOpen, () => {
|
|
2596
3899
|
setUpdateOpen(false);
|
|
2597
3900
|
updateBtnRef.current?.focus();
|
|
@@ -2755,6 +4058,13 @@ window.__ModuleLoader__.load({
|
|
|
2755
4058
|
setSettingsOpen(false);
|
|
2756
4059
|
settingsBtnRef.current?.focus();
|
|
2757
4060
|
};
|
|
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 = () => {
|
|
4065
|
+
setSettingsOpen(false);
|
|
4066
|
+
setOnboardingOpen(true);
|
|
4067
|
+
};
|
|
2758
4068
|
/** B1: a feature renders while the toolbar is EXPANDED or while it is pinned. */
|
|
2759
4069
|
const featureVisible = (id) => toolbarExpanded || pinned.includes(id);
|
|
2760
4070
|
/** Base chrome-button style: accent-defined active tint, scaled hit area. */
|
|
@@ -3130,27 +4440,25 @@ window.__ModuleLoader__.load({
|
|
|
3130
4440
|
forkAt(mark.seq).then(() => setForkedKey(mark.key));
|
|
3131
4441
|
};
|
|
3132
4442
|
const showLoadOlder = hasMore && marks.length >= MIN_MARKS;
|
|
3133
|
-
const railStyle = {
|
|
3134
|
-
position: "fixed",
|
|
3135
|
-
top: railBox.top,
|
|
3136
|
-
...side === "left" ? { left: railBox.left } : { right: railBox.right },
|
|
3137
|
-
height: railBox.height,
|
|
3138
|
-
width: hit,
|
|
3139
|
-
pointerEvents: "auto",
|
|
3140
|
-
zIndex: 100,
|
|
3141
|
-
display: "flex",
|
|
3142
|
-
flexDirection: "column",
|
|
3143
|
-
gap: 6,
|
|
3144
|
-
paddingTop: 6,
|
|
3145
|
-
"--ms-accent": accent,
|
|
3146
|
-
"--ms-accent-soft": accentSoft,
|
|
3147
|
-
"--ms-accent-bg": accentBg,
|
|
3148
|
-
"--ms-icon": `${iconSize}px`,
|
|
3149
|
-
"--ms-inset": `${inset}px`
|
|
3150
|
-
};
|
|
3151
|
-
const descKey = `settings.desc.${descFeature}`;
|
|
3152
4443
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3153
|
-
style:
|
|
4444
|
+
style: {
|
|
4445
|
+
position: "fixed",
|
|
4446
|
+
top: railBox.top,
|
|
4447
|
+
...side === "left" ? { left: railBox.left } : { right: railBox.right },
|
|
4448
|
+
height: railBox.height,
|
|
4449
|
+
width: hit,
|
|
4450
|
+
pointerEvents: "auto",
|
|
4451
|
+
zIndex: 100,
|
|
4452
|
+
display: "flex",
|
|
4453
|
+
flexDirection: "column",
|
|
4454
|
+
gap: 6,
|
|
4455
|
+
paddingTop: 6,
|
|
4456
|
+
"--ms-accent": accent,
|
|
4457
|
+
"--ms-accent-soft": accentSoft,
|
|
4458
|
+
"--ms-accent-bg": accentBg,
|
|
4459
|
+
"--ms-icon": `${iconSize}px`,
|
|
4460
|
+
"--ms-inset": `${inset}px`
|
|
4461
|
+
},
|
|
3154
4462
|
"aria-label": t("rail.label"),
|
|
3155
4463
|
"data-focus-active": focusActive ? "true" : void 0,
|
|
3156
4464
|
"data-accent": accent,
|
|
@@ -3159,7 +4467,7 @@ window.__ModuleLoader__.load({
|
|
|
3159
4467
|
"data-inset": String(inset),
|
|
3160
4468
|
children: [
|
|
3161
4469
|
pulseCss !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: pulseCss }),
|
|
3162
|
-
focusActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children:
|
|
4470
|
+
focusActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: buildFocusCss(prefs.focus) }),
|
|
3163
4471
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: MODAL_CSS }),
|
|
3164
4472
|
showLoadOlder && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3165
4473
|
type: "button",
|
|
@@ -3239,11 +4547,13 @@ window.__ModuleLoader__.load({
|
|
|
3239
4547
|
width: "min(600px, 92vw)",
|
|
3240
4548
|
maxHeight: "78vh",
|
|
3241
4549
|
overflowY: "auto",
|
|
3242
|
-
padding:
|
|
4550
|
+
padding: 20,
|
|
3243
4551
|
background: "rgba(20, 24, 32, 0.98)",
|
|
3244
4552
|
color: "#e6e8ee",
|
|
3245
4553
|
borderRadius: 12,
|
|
3246
|
-
boxShadow: "0
|
|
4554
|
+
boxShadow: "0 24px 64px rgba(0, 0, 0, 0.55)",
|
|
4555
|
+
scrollbarWidth: "thin",
|
|
4556
|
+
scrollbarColor: "rgba(255, 255, 255, 0.2) transparent"
|
|
3247
4557
|
},
|
|
3248
4558
|
children: [
|
|
3249
4559
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3252,7 +4562,7 @@ window.__ModuleLoader__.load({
|
|
|
3252
4562
|
alignItems: "center",
|
|
3253
4563
|
justifyContent: "space-between",
|
|
3254
4564
|
gap: 8,
|
|
3255
|
-
marginBottom:
|
|
4565
|
+
marginBottom: 18
|
|
3256
4566
|
},
|
|
3257
4567
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3258
4568
|
"data-toolbar-settings-title": true,
|
|
@@ -3269,44 +4579,57 @@ window.__ModuleLoader__.load({
|
|
|
3269
4579
|
title: t("settings.close"),
|
|
3270
4580
|
onClick: closeSettings,
|
|
3271
4581
|
style: {
|
|
3272
|
-
width:
|
|
3273
|
-
height:
|
|
4582
|
+
width: 28,
|
|
4583
|
+
height: 28,
|
|
3274
4584
|
flexShrink: 0,
|
|
3275
4585
|
display: "flex",
|
|
3276
4586
|
alignItems: "center",
|
|
3277
4587
|
justifyContent: "center",
|
|
3278
|
-
background: "transparent",
|
|
3279
4588
|
border: "none",
|
|
3280
4589
|
padding: 0,
|
|
3281
4590
|
cursor: "pointer",
|
|
3282
4591
|
color: "#8b96ab",
|
|
3283
|
-
borderRadius:
|
|
3284
|
-
fontSize: 14,
|
|
4592
|
+
borderRadius: 8,
|
|
3285
4593
|
lineHeight: 1
|
|
3286
4594
|
},
|
|
3287
|
-
children: "
|
|
4595
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
4596
|
+
width: "14",
|
|
4597
|
+
height: "14",
|
|
4598
|
+
viewBox: "0 0 24 24",
|
|
4599
|
+
fill: "none",
|
|
4600
|
+
stroke: "currentColor",
|
|
4601
|
+
strokeWidth: "2",
|
|
4602
|
+
strokeLinecap: "round",
|
|
4603
|
+
"aria-hidden": "true",
|
|
4604
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M18 6 6 18" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m6 6 12 12" })]
|
|
4605
|
+
})
|
|
3288
4606
|
})]
|
|
3289
4607
|
}),
|
|
3290
4608
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3291
4609
|
"data-settings-section": true,
|
|
3292
|
-
style: { marginBottom:
|
|
3293
|
-
children: [
|
|
3294
|
-
"
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
4610
|
+
style: { marginBottom: 20 },
|
|
4611
|
+
children: [
|
|
4612
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4613
|
+
"data-settings-section-title": true,
|
|
4614
|
+
style: {
|
|
4615
|
+
fontSize: 13,
|
|
4616
|
+
fontWeight: 600,
|
|
4617
|
+
color: "#c7cede",
|
|
4618
|
+
marginBottom: 4
|
|
4619
|
+
},
|
|
4620
|
+
children: t("settings.section.features")
|
|
4621
|
+
}),
|
|
4622
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4623
|
+
"data-settings-pin-hint": true,
|
|
4624
|
+
style: {
|
|
4625
|
+
fontSize: 12,
|
|
4626
|
+
color: "#8b96ab",
|
|
4627
|
+
lineHeight: 1.5,
|
|
4628
|
+
marginBottom: 10
|
|
4629
|
+
},
|
|
4630
|
+
children: t("settings.pin.hint")
|
|
4631
|
+
}),
|
|
4632
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3310
4633
|
style: {
|
|
3311
4634
|
display: "flex",
|
|
3312
4635
|
flexDirection: "column",
|
|
@@ -3314,92 +4637,163 @@ window.__ModuleLoader__.load({
|
|
|
3314
4637
|
},
|
|
3315
4638
|
children: toolbarFeatures.map((feature) => {
|
|
3316
4639
|
const checked = pinned.includes(feature.id);
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
"data-toolbar-pin-
|
|
3321
|
-
"data-
|
|
3322
|
-
"
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
borderRadius: 8,
|
|
3337
|
-
cursor: "pointer",
|
|
3338
|
-
color: "#e6e8ee",
|
|
3339
|
-
fontSize: 13,
|
|
3340
|
-
textAlign: "left"
|
|
3341
|
-
},
|
|
3342
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(feature.labelKey) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3343
|
-
"aria-hidden": "true",
|
|
4640
|
+
const active = descFeature === feature.id;
|
|
4641
|
+
const tipId = `ms-settings-tip-${feature.id}`;
|
|
4642
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4643
|
+
"data-toolbar-pin-row": true,
|
|
4644
|
+
"data-row-id": feature.id,
|
|
4645
|
+
style: { position: "relative" },
|
|
4646
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4647
|
+
type: "button",
|
|
4648
|
+
role: "switch",
|
|
4649
|
+
"data-toolbar-pin-toggle": true,
|
|
4650
|
+
"data-pin-id": feature.id,
|
|
4651
|
+
"aria-checked": checked,
|
|
4652
|
+
"aria-label": t(feature.labelKey),
|
|
4653
|
+
"aria-describedby": tipId,
|
|
4654
|
+
onMouseEnter: () => setDescFeature(feature.id),
|
|
4655
|
+
onMouseLeave: () => setDescFeature((prev) => prev === feature.id ? null : prev),
|
|
4656
|
+
onFocus: () => setDescFeature(feature.id),
|
|
4657
|
+
onBlur: () => setDescFeature((prev) => prev === feature.id ? null : prev),
|
|
4658
|
+
onClick: () => onTogglePin(feature.id),
|
|
3344
4659
|
style: {
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
4660
|
+
display: "flex",
|
|
4661
|
+
alignItems: "center",
|
|
4662
|
+
justifyContent: "space-between",
|
|
4663
|
+
gap: 10,
|
|
4664
|
+
width: "100%",
|
|
4665
|
+
padding: "8px 10px",
|
|
4666
|
+
border: "none",
|
|
4667
|
+
borderRadius: 8,
|
|
4668
|
+
cursor: "pointer",
|
|
4669
|
+
color: "#e6e8ee",
|
|
4670
|
+
fontSize: 13,
|
|
4671
|
+
textAlign: "left"
|
|
3352
4672
|
},
|
|
3353
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4673
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4674
|
+
style: {
|
|
4675
|
+
overflow: "hidden",
|
|
4676
|
+
textOverflow: "ellipsis",
|
|
4677
|
+
whiteSpace: "nowrap"
|
|
4678
|
+
},
|
|
4679
|
+
children: t(feature.labelKey)
|
|
4680
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4681
|
+
"aria-hidden": "true",
|
|
4682
|
+
style: {
|
|
4683
|
+
position: "relative",
|
|
4684
|
+
width: 32,
|
|
4685
|
+
height: 18,
|
|
4686
|
+
flexShrink: 0,
|
|
4687
|
+
borderRadius: 9,
|
|
4688
|
+
background: checked ? accentStrong : "rgba(255, 255, 255, 0.16)",
|
|
4689
|
+
transition: "background 120ms ease"
|
|
4690
|
+
},
|
|
4691
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
|
|
4692
|
+
position: "absolute",
|
|
4693
|
+
top: 2,
|
|
4694
|
+
left: checked ? 16 : 2,
|
|
4695
|
+
width: 14,
|
|
4696
|
+
height: 14,
|
|
4697
|
+
borderRadius: "50%",
|
|
4698
|
+
background: "#ffffff",
|
|
4699
|
+
transition: "left 120ms ease"
|
|
4700
|
+
} })
|
|
4701
|
+
})]
|
|
4702
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4703
|
+
id: tipId,
|
|
4704
|
+
role: "tooltip",
|
|
4705
|
+
"data-settings-tip": true,
|
|
4706
|
+
"data-tip-for": feature.id,
|
|
4707
|
+
"data-tip-visible": active ? "true" : void 0,
|
|
4708
|
+
style: {
|
|
3354
4709
|
position: "absolute",
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
4710
|
+
right: MODAL_TIP_RIGHT,
|
|
4711
|
+
top: "50%",
|
|
4712
|
+
maxWidth: "55%",
|
|
4713
|
+
transform: `translateY(-50%) translateX(${active ? 0 : 4}px)`,
|
|
4714
|
+
padding: "5px 10px",
|
|
4715
|
+
borderRadius: 8,
|
|
4716
|
+
background: "#222834",
|
|
4717
|
+
border: `1px solid rgba(255, 255, 255, 0.14)`,
|
|
4718
|
+
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.35)",
|
|
4719
|
+
color: "#b9c2d4",
|
|
4720
|
+
fontSize: 12,
|
|
4721
|
+
lineHeight: 1.45,
|
|
4722
|
+
opacity: active ? 1 : 0,
|
|
4723
|
+
visibility: active ? "visible" : "hidden",
|
|
4724
|
+
pointerEvents: "none",
|
|
4725
|
+
zIndex: 4
|
|
4726
|
+
},
|
|
4727
|
+
children: t(`settings.desc.${feature.id}`)
|
|
3363
4728
|
})]
|
|
3364
4729
|
}, feature.id);
|
|
3365
4730
|
})
|
|
3366
|
-
})
|
|
3367
|
-
|
|
3368
|
-
"aria-live": "polite",
|
|
3369
|
-
style: {
|
|
3370
|
-
display: "flex",
|
|
3371
|
-
alignItems: "center",
|
|
3372
|
-
padding: "10px 12px",
|
|
3373
|
-
borderRadius: 8,
|
|
3374
|
-
background: "rgba(255, 255, 255, 0.05)",
|
|
3375
|
-
color: "#b9c2d4",
|
|
3376
|
-
fontSize: 12.5,
|
|
3377
|
-
lineHeight: 1.5
|
|
3378
|
-
},
|
|
3379
|
-
children: t(descKey)
|
|
3380
|
-
})]
|
|
3381
|
-
})]
|
|
4731
|
+
})
|
|
4732
|
+
]
|
|
3382
4733
|
}),
|
|
3383
4734
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3384
4735
|
"data-settings-section": true,
|
|
3385
4736
|
"data-settings-personal": true,
|
|
3386
|
-
style: { marginBottom:
|
|
3387
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3388
|
-
"
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
children:
|
|
3396
|
-
|
|
4737
|
+
style: { marginBottom: 20 },
|
|
4738
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4739
|
+
type: "button",
|
|
4740
|
+
"data-personal-toggle": true,
|
|
4741
|
+
"aria-expanded": personalOpen,
|
|
4742
|
+
"aria-label": personalOpen ? t("settings.personal.collapse") : t("settings.personal.expand"),
|
|
4743
|
+
title: personalOpen ? t("settings.personal.collapse") : t("settings.personal.expand"),
|
|
4744
|
+
onClick: () => setPersonalOpen((v) => !v),
|
|
4745
|
+
style: SECTION_TOGGLE_STYLE,
|
|
4746
|
+
children: [
|
|
4747
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
4748
|
+
width: "14",
|
|
4749
|
+
height: "14",
|
|
4750
|
+
viewBox: "0 0 24 24",
|
|
4751
|
+
fill: "none",
|
|
4752
|
+
stroke: "currentColor",
|
|
4753
|
+
strokeWidth: "2.5",
|
|
4754
|
+
strokeLinecap: "round",
|
|
4755
|
+
strokeLinejoin: "round",
|
|
4756
|
+
"aria-hidden": "true",
|
|
4757
|
+
style: {
|
|
4758
|
+
flexShrink: 0,
|
|
4759
|
+
transform: personalOpen ? "rotate(90deg)" : "none"
|
|
4760
|
+
},
|
|
4761
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m9 18 6-6-6-6" })
|
|
4762
|
+
}),
|
|
4763
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4764
|
+
"data-settings-section-title": true,
|
|
4765
|
+
style: { flexShrink: 0 },
|
|
4766
|
+
children: t("settings.section.personal")
|
|
4767
|
+
}),
|
|
4768
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4769
|
+
"data-settings-personal-summary": true,
|
|
4770
|
+
style: SECTION_SUMMARY_STYLE,
|
|
4771
|
+
children: t("settings.personal.summary", {
|
|
4772
|
+
accent,
|
|
4773
|
+
icon: iconSize,
|
|
4774
|
+
side: side === "left" ? t("settings.side.left") : t("settings.side.right")
|
|
4775
|
+
})
|
|
4776
|
+
})
|
|
4777
|
+
]
|
|
4778
|
+
}), personalOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4779
|
+
"data-settings-personal-body": true,
|
|
3397
4780
|
style: {
|
|
4781
|
+
padding: "10px 4px 8px",
|
|
3398
4782
|
display: "flex",
|
|
3399
4783
|
flexDirection: "column",
|
|
3400
|
-
gap:
|
|
4784
|
+
gap: 12
|
|
3401
4785
|
},
|
|
3402
4786
|
children: [
|
|
4787
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4788
|
+
"data-settings-personal-hint": true,
|
|
4789
|
+
style: {
|
|
4790
|
+
fontSize: 12,
|
|
4791
|
+
color: "#8b96ab",
|
|
4792
|
+
lineHeight: 1.5,
|
|
4793
|
+
padding: "0 6px"
|
|
4794
|
+
},
|
|
4795
|
+
children: t("settings.personal.hint")
|
|
4796
|
+
}),
|
|
3403
4797
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3404
4798
|
style: {
|
|
3405
4799
|
display: "flex",
|
|
@@ -3612,10 +5006,171 @@ window.__ModuleLoader__.load({
|
|
|
3612
5006
|
]
|
|
3613
5007
|
})]
|
|
3614
5008
|
}),
|
|
5009
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5010
|
+
"data-settings-section": true,
|
|
5011
|
+
"data-focus-settings": true,
|
|
5012
|
+
style: { marginBottom: 20 },
|
|
5013
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5014
|
+
type: "button",
|
|
5015
|
+
"data-focus-toggle-settings": true,
|
|
5016
|
+
"aria-expanded": focusOpen,
|
|
5017
|
+
"aria-label": focusOpen ? t("settings.focus.collapse") : t("settings.focus.expand"),
|
|
5018
|
+
title: focusOpen ? t("settings.focus.collapse") : t("settings.focus.expand"),
|
|
5019
|
+
onClick: () => setFocusOpen((v) => !v),
|
|
5020
|
+
style: SECTION_TOGGLE_STYLE,
|
|
5021
|
+
children: [
|
|
5022
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
5023
|
+
width: "14",
|
|
5024
|
+
height: "14",
|
|
5025
|
+
viewBox: "0 0 24 24",
|
|
5026
|
+
fill: "none",
|
|
5027
|
+
stroke: "currentColor",
|
|
5028
|
+
strokeWidth: "2.5",
|
|
5029
|
+
strokeLinecap: "round",
|
|
5030
|
+
strokeLinejoin: "round",
|
|
5031
|
+
"aria-hidden": "true",
|
|
5032
|
+
style: {
|
|
5033
|
+
flexShrink: 0,
|
|
5034
|
+
transform: focusOpen ? "rotate(90deg)" : "none"
|
|
5035
|
+
},
|
|
5036
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m9 18 6-6-6-6" })
|
|
5037
|
+
}),
|
|
5038
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5039
|
+
"data-settings-section-title": true,
|
|
5040
|
+
style: { flexShrink: 0 },
|
|
5041
|
+
children: t("settings.section.focus")
|
|
5042
|
+
}),
|
|
5043
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5044
|
+
"data-focus-summary": true,
|
|
5045
|
+
style: SECTION_SUMMARY_STYLE,
|
|
5046
|
+
children: focusSummary
|
|
5047
|
+
})
|
|
5048
|
+
]
|
|
5049
|
+
}), focusOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5050
|
+
"data-settings-focus-body": true,
|
|
5051
|
+
style: {
|
|
5052
|
+
padding: "10px 4px 8px",
|
|
5053
|
+
display: "flex",
|
|
5054
|
+
flexDirection: "column",
|
|
5055
|
+
gap: 12
|
|
5056
|
+
},
|
|
5057
|
+
children: [
|
|
5058
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5059
|
+
"data-settings-focus-hint": true,
|
|
5060
|
+
style: {
|
|
5061
|
+
fontSize: 12,
|
|
5062
|
+
color: "#8b96ab",
|
|
5063
|
+
lineHeight: 1.5,
|
|
5064
|
+
padding: "0 6px"
|
|
5065
|
+
},
|
|
5066
|
+
children: t("settings.focus.hint")
|
|
5067
|
+
}),
|
|
5068
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5069
|
+
"data-focus-option": true,
|
|
5070
|
+
style: {
|
|
5071
|
+
display: "flex",
|
|
5072
|
+
alignItems: "center",
|
|
5073
|
+
gap: 8,
|
|
5074
|
+
padding: "6px 8px",
|
|
5075
|
+
borderRadius: 8,
|
|
5076
|
+
fontSize: 13,
|
|
5077
|
+
color: "#e6e8ee",
|
|
5078
|
+
cursor: "pointer"
|
|
5079
|
+
},
|
|
5080
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5081
|
+
type: "checkbox",
|
|
5082
|
+
"data-focus-dim-think": true,
|
|
5083
|
+
checked: prefs.focus.dimThink,
|
|
5084
|
+
onChange: (e) => updateFocus({ dimThink: e.target.checked })
|
|
5085
|
+
}), t("settings.focus.dimThink")]
|
|
5086
|
+
}),
|
|
5087
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5088
|
+
"data-focus-option": true,
|
|
5089
|
+
style: {
|
|
5090
|
+
display: "flex",
|
|
5091
|
+
alignItems: "center",
|
|
5092
|
+
gap: 8,
|
|
5093
|
+
padding: "6px 8px",
|
|
5094
|
+
borderRadius: 8,
|
|
5095
|
+
fontSize: 13,
|
|
5096
|
+
color: "#e6e8ee",
|
|
5097
|
+
cursor: "pointer"
|
|
5098
|
+
},
|
|
5099
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5100
|
+
type: "checkbox",
|
|
5101
|
+
"data-focus-dim-tools": true,
|
|
5102
|
+
checked: prefs.focus.dimTools,
|
|
5103
|
+
onChange: (e) => updateFocus({ dimTools: e.target.checked })
|
|
5104
|
+
}), t("settings.focus.dimTools")]
|
|
5105
|
+
}),
|
|
5106
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5107
|
+
"data-focus-option": true,
|
|
5108
|
+
style: {
|
|
5109
|
+
display: "flex",
|
|
5110
|
+
alignItems: "center",
|
|
5111
|
+
gap: 8,
|
|
5112
|
+
padding: "6px 8px",
|
|
5113
|
+
borderRadius: 8,
|
|
5114
|
+
fontSize: 13,
|
|
5115
|
+
color: "#e6e8ee",
|
|
5116
|
+
cursor: "pointer"
|
|
5117
|
+
},
|
|
5118
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5119
|
+
type: "checkbox",
|
|
5120
|
+
"data-focus-collapse-think": true,
|
|
5121
|
+
checked: prefs.focus.collapseThink,
|
|
5122
|
+
onChange: (e) => updateFocus({ collapseThink: e.target.checked })
|
|
5123
|
+
}), t("settings.focus.collapseThink")]
|
|
5124
|
+
}),
|
|
5125
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5126
|
+
style: {
|
|
5127
|
+
display: "flex",
|
|
5128
|
+
alignItems: "center",
|
|
5129
|
+
gap: 10,
|
|
5130
|
+
flexWrap: "wrap"
|
|
5131
|
+
},
|
|
5132
|
+
children: [
|
|
5133
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5134
|
+
style: {
|
|
5135
|
+
fontSize: 12.5,
|
|
5136
|
+
color: "#8b96ab",
|
|
5137
|
+
width: 90,
|
|
5138
|
+
flexShrink: 0
|
|
5139
|
+
},
|
|
5140
|
+
children: t("settings.focus.opacity")
|
|
5141
|
+
}),
|
|
5142
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5143
|
+
type: "range",
|
|
5144
|
+
"data-focus-opacity": true,
|
|
5145
|
+
min: .2,
|
|
5146
|
+
max: .8,
|
|
5147
|
+
step: .1,
|
|
5148
|
+
value: prefs.focus.opacity,
|
|
5149
|
+
onChange: (e) => updateFocus({ opacity: Number(e.target.value) }),
|
|
5150
|
+
style: {
|
|
5151
|
+
flex: 1,
|
|
5152
|
+
minWidth: 140,
|
|
5153
|
+
maxWidth: 260
|
|
5154
|
+
}
|
|
5155
|
+
}),
|
|
5156
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5157
|
+
"data-focus-opacity-value": true,
|
|
5158
|
+
style: {
|
|
5159
|
+
fontSize: 12.5,
|
|
5160
|
+
color: "#b9c2d4",
|
|
5161
|
+
width: 44
|
|
5162
|
+
},
|
|
5163
|
+
children: [Math.round(prefs.focus.opacity * 100), "%"]
|
|
5164
|
+
})
|
|
5165
|
+
]
|
|
5166
|
+
})
|
|
5167
|
+
]
|
|
5168
|
+
})]
|
|
5169
|
+
}),
|
|
3615
5170
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3616
5171
|
"data-settings-section": true,
|
|
3617
5172
|
"data-settings-lang": true,
|
|
3618
|
-
style: { marginBottom:
|
|
5173
|
+
style: { marginBottom: 20 },
|
|
3619
5174
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3620
5175
|
"data-settings-section-title": true,
|
|
3621
5176
|
style: {
|
|
@@ -3765,26 +5320,51 @@ window.__ModuleLoader__.load({
|
|
|
3765
5320
|
]
|
|
3766
5321
|
})]
|
|
3767
5322
|
}),
|
|
3768
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3769
|
-
|
|
3770
|
-
"data-toolbar-settings-reset": true,
|
|
3771
|
-
onClick: onResetAll,
|
|
5323
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5324
|
+
"data-toolbar-settings-actions": true,
|
|
3772
5325
|
style: {
|
|
3773
|
-
display: "
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
borderRadius: 8,
|
|
3779
|
-
cursor: "pointer",
|
|
3780
|
-
color: "#b9c2d4",
|
|
3781
|
-
fontSize: 12.5
|
|
5326
|
+
display: "flex",
|
|
5327
|
+
alignItems: "center",
|
|
5328
|
+
justifyContent: "center",
|
|
5329
|
+
gap: 10,
|
|
5330
|
+
margin: "16px auto 2px"
|
|
3782
5331
|
},
|
|
3783
|
-
children:
|
|
5332
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5333
|
+
type: "button",
|
|
5334
|
+
"data-toolbar-settings-reset": true,
|
|
5335
|
+
onClick: onResetAll,
|
|
5336
|
+
style: {
|
|
5337
|
+
padding: "7px 16px",
|
|
5338
|
+
border: `1px solid rgba(255, 255, 255, 0.14)`,
|
|
5339
|
+
borderRadius: 8,
|
|
5340
|
+
cursor: "pointer",
|
|
5341
|
+
color: "#b9c2d4",
|
|
5342
|
+
fontSize: 12.5
|
|
5343
|
+
},
|
|
5344
|
+
children: t("settings.reset")
|
|
5345
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5346
|
+
type: "button",
|
|
5347
|
+
"data-onboarding-reopen": true,
|
|
5348
|
+
onClick: reopenOnboarding,
|
|
5349
|
+
style: {
|
|
5350
|
+
padding: "7px 16px",
|
|
5351
|
+
border: `1px solid rgba(255, 255, 255, 0.14)`,
|
|
5352
|
+
borderRadius: 8,
|
|
5353
|
+
cursor: "pointer",
|
|
5354
|
+
color: "#b9c2d4",
|
|
5355
|
+
fontSize: 12.5
|
|
5356
|
+
},
|
|
5357
|
+
children: t("onboarding.reopen")
|
|
5358
|
+
})]
|
|
3784
5359
|
})
|
|
3785
5360
|
]
|
|
3786
5361
|
})
|
|
3787
5362
|
}),
|
|
5363
|
+
onboardingOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MilestoneOnboarding, {
|
|
5364
|
+
t,
|
|
5365
|
+
accent,
|
|
5366
|
+
onClose: () => setOnboardingOpen(false)
|
|
5367
|
+
}),
|
|
3788
5368
|
updateOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3789
5369
|
ref: updatePanelRef,
|
|
3790
5370
|
"data-update-panel": true,
|
|
@@ -3821,7 +5401,7 @@ window.__ModuleLoader__.load({
|
|
|
3821
5401
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3822
5402
|
style: { color: "#8b96ab" },
|
|
3823
5403
|
children: [t("update.current"), ": "]
|
|
3824
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "0.6.
|
|
5404
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "0.6.4" })] }),
|
|
3825
5405
|
updateCheck.phase === "ok" && updateCheck.latest !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3826
5406
|
"data-update-latest": true,
|
|
3827
5407
|
children: [
|