@michengai/dsh-codex-ui 0.2.113 → 1.0.0

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/CHANGELOG.md CHANGED
@@ -4,7 +4,31 @@
4
4
 
5
5
  This changelog records recent releases of DSH Codex UI and its one-click installer. Earlier changes remain available in the [Git history](https://github.com/MichengAI/dsh-codex-ui/commits/main).
6
6
 
7
- ## Unreleased
7
+ ## 1.0.0 - 2026-09-08
8
+
9
+ - Release 1.0.0, bringing project and conversation navigation, plugin entry points, and standalone settings together in a cohesive Codex-style experience.
10
+ - Fix flashing when returning to the app or pressing Escape; let menus handle Escape first and preserve the current settings form while filtering navigation.
11
+ - Upgrade notes: the standalone settings view requires the bundle patch that disables ui-settings-general. If an existing settings shell is detected, its entry is retained. Replacing client files alone is not a complete upgrade; reload DSH after upgrading. Existing business configuration and conversations require no migration.
12
+ - Fix global settings search, onboarding and keyboard focus isolation, Escape handling for hidden dialogs, and configuration-file error feedback; retain the host settings shell when it is already active.
13
+ - Replace the settings dialog with a Codex-style standalone view, category search, grouped preferences, and focus restoration when returning to the app.
14
+ - Fix duplicate settings-shell registration, settings entry alignment and transitions, search focus, repeated headings, and content spacing.
15
+ - Give the plugin marketplace and side cards distinct store and sidebar-layout icons instead of the generic box.
16
+ - Preserve host configuration, onboarding, connection recovery, and installed plugin functionality; community plugin internal styling remains outside this change.
17
+
18
+ ## suite-installer-v0.1.31 - 2026-09-08
19
+
20
+ Resolved and pinned the following member versions from the official npm registry:
21
+
22
+ - `@michengai/dsh-archive-manager@0.1.32`
23
+ - `@michengai/dsh-codex-ui@0.2.113`
24
+ - `@michengai/dsh-skills-manager@0.1.43`
25
+ - `@michengai/dsh-agency-agents@0.1.34`
26
+ - `@michengai/dsh-im-connect@0.1.38`
27
+ - `@michengai/dsh-automation@0.1.34`
28
+ - `@michengai/dsh-btw@0.1.4`
29
+ - `@michengai/dsh-simplify@0.1.2`
30
+
31
+ The installer uses these exact versions for reproducible installation.
8
32
 
9
33
  ## 0.2.113 - 2026-09-08
10
34
 
@@ -4,7 +4,31 @@
4
4
 
5
5
  本日志记录 DSH Codex UI 及其一键安装器的最近发布;更早的变更可查看 [Git 提交历史](https://github.com/MichengAI/dsh-codex-ui/commits/main)。
6
6
 
7
- ## 未发布
7
+ ## 1.0.0 - 2026-09-08
8
+
9
+ - 发布 1.0.0:将项目与会话导航、插件入口和独立设置视图整合为完整的 Codex 风格体验。
10
+ - 修复返回应用和 Escape 退出时的页面闪烁;菜单先处理 Escape,搜索筛选保留正在填写的正文。
11
+ - 升级说明:独立设置页依赖 bundle patch 停用 ui-settings-general;检测到既有设置壳时保留宿主入口。单独替换客户端文件不等同于完整升级,升级后应重载 DSH。既有业务配置和会话无需迁移。
12
+ - 修复全局搜索设置入口、引导与键盘焦点隔离、隐藏弹窗对 Escape 的影响及配置文件失败反馈;旧设置壳并存时保留宿主入口。
13
+ - 设置改为 Codex 风格的独立页面,提供分类搜索、分组偏好、返回应用及焦点恢复。
14
+ - 修复新旧设置壳重复注册、设置入口对齐与交互动画、搜索聚焦、重复标题及内容留白。
15
+ - 为插件市场和侧边卡片分别使用商店与侧栏布局图标,避免与默认盒子图标混淆。
16
+ - 保留宿主配置、引导和连接恢复接口,以及已安装插件原有功能;社区插件内部样式不在本次统一范围。
17
+
18
+ ## suite-installer-v0.1.31 - 2026-09-08
19
+
20
+ 从官方 npm 解析并锁定以下成员版本:
21
+
22
+ - `@michengai/dsh-archive-manager@0.1.32`
23
+ - `@michengai/dsh-codex-ui@0.2.113`
24
+ - `@michengai/dsh-skills-manager@0.1.43`
25
+ - `@michengai/dsh-agency-agents@0.1.34`
26
+ - `@michengai/dsh-im-connect@0.1.38`
27
+ - `@michengai/dsh-automation@0.1.34`
28
+ - `@michengai/dsh-btw@0.1.4`
29
+ - `@michengai/dsh-simplify@0.1.2`
30
+
31
+ 安装器使用这些精确版本执行安装,确保可复现。
8
32
 
9
33
  ## 0.2.113 - 2026-09-08
10
34
 
package/cordis.patch.yml CHANGED
@@ -1,6 +1,8 @@
1
- # 由本插件接管 sidebar 的子插槽声明,原设置面板仍通过 sidebar.settings 挂载。
1
+ # 由本插件统一声明侧栏和设置子插槽,避免与原壳重复声明。
2
2
  - id: ui-sidebar
3
3
  disabled: true
4
+ - id: ui-settings-general
5
+ disabled: true
4
6
  - insert:
5
7
  - id: codex-ui
6
8
  name: '@michengai/dsh-codex-ui'
package/lib/client.js CHANGED
@@ -30,16 +30,22 @@ window.__ModuleLoader__.load({
30
30
  }
31
31
  let cancelPendingNavigation;
32
32
  const SETTINGS_NAVIGATION_TIMEOUT_MS = 4e3;
33
+ const SETTINGS_TRIGGER_SELECTOR = "[data-dcu-settings-trigger],[aria-haspopup=\"dialog\"]";
34
+ /** 根入口与分区跳转使用同一触发器合约,兼容保留的宿主设置壳。 */
35
+ function openSettingsRoot(root) {
36
+ root?.querySelector(SETTINGS_TRIGGER_SELECTOR)?.click();
37
+ }
33
38
  function openSettingsSection(root, label, onMissing, onSelected) {
34
39
  const labels = typeof label === "string" ? [label] : label;
35
- const trigger = root?.querySelector("[aria-haspopup=\"dialog\"]");
40
+ const trigger = root?.querySelector(SETTINGS_TRIGGER_SELECTOR);
36
41
  if (trigger === null || trigger === void 0) {
37
42
  onMissing?.();
38
43
  return;
39
44
  }
40
45
  const opening = cancelPendingNavigation !== void 0;
41
46
  cancelPendingNavigation?.();
42
- if (!opening && document.querySelector("[role=\"dialog\"]") === null) trigger.click();
47
+ const pageSelector = trigger.hasAttribute("data-dcu-settings-trigger") ? "[data-dcu-settings-page]" : "[role=\"dialog\"]";
48
+ if (!opening && document.querySelector(pageSelector) === null) trigger.click();
43
49
  let frame;
44
50
  let finished = false;
45
51
  const observer = new MutationObserver(() => {
@@ -54,7 +60,7 @@ window.__ModuleLoader__.load({
54
60
  if (cancelPendingNavigation === cleanup) cancelPendingNavigation = void 0;
55
61
  };
56
62
  const select = () => {
57
- const target = pickSettingsSectionButton([...document.querySelectorAll("[role=\"dialog\"] nav button")], labels);
63
+ const target = pickSettingsSectionButton([...document.querySelectorAll(`${pageSelector} nav button`)], labels);
58
64
  if (target === void 0) return false;
59
65
  cleanup();
60
66
  target.click();
@@ -483,6 +489,148 @@ window.__ModuleLoader__.load({
483
489
  * This source code is licensed under the ISC license.
484
490
  * See the LICENSE file in the root directory of this source tree.
485
491
  */
492
+ const ArrowLeft = createLucideIcon("arrow-left", [["path", {
493
+ d: "m12 19-7-7 7-7",
494
+ key: "1l729n"
495
+ }], ["path", {
496
+ d: "M19 12H5",
497
+ key: "x3x0zl"
498
+ }]]);
499
+ /**
500
+ * @license lucide-react v1.39.0 - ISC
501
+ *
502
+ * This source code is licensed under the ISC license.
503
+ * See the LICENSE file in the root directory of this source tree.
504
+ */
505
+ const Box = createLucideIcon("box", [
506
+ ["path", {
507
+ d: "M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z",
508
+ key: "hh9hay"
509
+ }],
510
+ ["path", {
511
+ d: "m3.3 7 8.7 5 8.7-5",
512
+ key: "g66t2b"
513
+ }],
514
+ ["path", {
515
+ d: "M12 22V12",
516
+ key: "d0xqtd"
517
+ }]
518
+ ]);
519
+ /**
520
+ * @license lucide-react v1.39.0 - ISC
521
+ *
522
+ * This source code is licensed under the ISC license.
523
+ * See the LICENSE file in the root directory of this source tree.
524
+ */
525
+ const CircleQuestionMark = createLucideIcon("circle-question-mark", [
526
+ ["circle", {
527
+ cx: "12",
528
+ cy: "12",
529
+ r: "10",
530
+ key: "1mglay"
531
+ }],
532
+ ["path", {
533
+ d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",
534
+ key: "1u773s"
535
+ }],
536
+ ["path", {
537
+ d: "M12 17h.01",
538
+ key: "p32p05"
539
+ }]
540
+ ]);
541
+ /**
542
+ * @license lucide-react v1.39.0 - ISC
543
+ *
544
+ * This source code is licensed under the ISC license.
545
+ * See the LICENSE file in the root directory of this source tree.
546
+ */
547
+ const Clock = createLucideIcon("clock", [["circle", {
548
+ cx: "12",
549
+ cy: "12",
550
+ r: "10",
551
+ key: "1mglay"
552
+ }], ["path", {
553
+ d: "M12 6v6l4 2",
554
+ key: "mmk7yg"
555
+ }]]);
556
+ /**
557
+ * @license lucide-react v1.39.0 - ISC
558
+ *
559
+ * This source code is licensed under the ISC license.
560
+ * See the LICENSE file in the root directory of this source tree.
561
+ */
562
+ const Cpu = createLucideIcon("cpu", [
563
+ ["path", {
564
+ d: "M12 20v2",
565
+ key: "1lh1kg"
566
+ }],
567
+ ["path", {
568
+ d: "M12 2v2",
569
+ key: "tus03m"
570
+ }],
571
+ ["path", {
572
+ d: "M17 20v2",
573
+ key: "1rnc9c"
574
+ }],
575
+ ["path", {
576
+ d: "M17 2v2",
577
+ key: "11trls"
578
+ }],
579
+ ["path", {
580
+ d: "M2 12h2",
581
+ key: "1t8f8n"
582
+ }],
583
+ ["path", {
584
+ d: "M2 17h2",
585
+ key: "7oei6x"
586
+ }],
587
+ ["path", {
588
+ d: "M2 7h2",
589
+ key: "asdhe0"
590
+ }],
591
+ ["path", {
592
+ d: "M20 12h2",
593
+ key: "1q8mjw"
594
+ }],
595
+ ["path", {
596
+ d: "M20 17h2",
597
+ key: "1fpfkl"
598
+ }],
599
+ ["path", {
600
+ d: "M20 7h2",
601
+ key: "1o8tra"
602
+ }],
603
+ ["path", {
604
+ d: "M7 20v2",
605
+ key: "4gnj0m"
606
+ }],
607
+ ["path", {
608
+ d: "M7 2v2",
609
+ key: "1i4yhu"
610
+ }],
611
+ ["rect", {
612
+ x: "4",
613
+ y: "4",
614
+ width: "16",
615
+ height: "16",
616
+ rx: "2",
617
+ key: "1vbyd7"
618
+ }],
619
+ ["rect", {
620
+ x: "8",
621
+ y: "8",
622
+ width: "8",
623
+ height: "8",
624
+ rx: "1",
625
+ key: "z9xiuo"
626
+ }]
627
+ ]);
628
+ /**
629
+ * @license lucide-react v1.39.0 - ISC
630
+ *
631
+ * This source code is licensed under the ISC license.
632
+ * See the LICENSE file in the root directory of this source tree.
633
+ */
486
634
  const EyeOff = createLucideIcon("eye-off", [
487
635
  ["path", {
488
636
  d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49",
@@ -522,6 +670,19 @@ window.__ModuleLoader__.load({
522
670
  * This source code is licensed under the ISC license.
523
671
  * See the LICENSE file in the root directory of this source tree.
524
672
  */
673
+ const Link = createLucideIcon("link", [["path", {
674
+ d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71",
675
+ key: "1cjeqo"
676
+ }], ["path", {
677
+ d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71",
678
+ key: "19qd67"
679
+ }]]);
680
+ /**
681
+ * @license lucide-react v1.39.0 - ISC
682
+ *
683
+ * This source code is licensed under the ISC license.
684
+ * See the LICENSE file in the root directory of this source tree.
685
+ */
525
686
  const MessageCircle = createLucideIcon("message-circle", [["path", {
526
687
  d: "M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719",
527
688
  key: "1sd12s"
@@ -532,6 +693,33 @@ window.__ModuleLoader__.load({
532
693
  * This source code is licensed under the ISC license.
533
694
  * See the LICENSE file in the root directory of this source tree.
534
695
  */
696
+ const MessageSquare = createLucideIcon("message-square", [["path", {
697
+ d: "M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",
698
+ key: "18887p"
699
+ }]]);
700
+ /**
701
+ * @license lucide-react v1.39.0 - ISC
702
+ *
703
+ * This source code is licensed under the ISC license.
704
+ * See the LICENSE file in the root directory of this source tree.
705
+ */
706
+ const PanelRight = createLucideIcon("panel-right", [["rect", {
707
+ width: "18",
708
+ height: "18",
709
+ x: "3",
710
+ y: "3",
711
+ rx: "2",
712
+ key: "afitv7"
713
+ }], ["path", {
714
+ d: "M15 3v18",
715
+ key: "14nvp0"
716
+ }]]);
717
+ /**
718
+ * @license lucide-react v1.39.0 - ISC
719
+ *
720
+ * This source code is licensed under the ISC license.
721
+ * See the LICENSE file in the root directory of this source tree.
722
+ */
535
723
  const Pin = createLucideIcon("pin", [["path", {
536
724
  d: "M12 17v5",
537
725
  key: "bb1du9"
@@ -545,6 +733,106 @@ window.__ModuleLoader__.load({
545
733
  * This source code is licensed under the ISC license.
546
734
  * See the LICENSE file in the root directory of this source tree.
547
735
  */
736
+ const Search = createLucideIcon("search", [["path", {
737
+ d: "m21 21-4.34-4.34",
738
+ key: "14j7rj"
739
+ }], ["circle", {
740
+ cx: "11",
741
+ cy: "11",
742
+ r: "8",
743
+ key: "4ej97u"
744
+ }]]);
745
+ /**
746
+ * @license lucide-react v1.39.0 - ISC
747
+ *
748
+ * This source code is licensed under the ISC license.
749
+ * See the LICENSE file in the root directory of this source tree.
750
+ */
751
+ const Settings = createLucideIcon("settings", [["path", {
752
+ d: "M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",
753
+ key: "1i5ecw"
754
+ }], ["circle", {
755
+ cx: "12",
756
+ cy: "12",
757
+ r: "3",
758
+ key: "1v7zrd"
759
+ }]]);
760
+ /**
761
+ * @license lucide-react v1.39.0 - ISC
762
+ *
763
+ * This source code is licensed under the ISC license.
764
+ * See the LICENSE file in the root directory of this source tree.
765
+ */
766
+ const SlidersHorizontal = createLucideIcon("sliders-horizontal", [
767
+ ["path", {
768
+ d: "M10 5H3",
769
+ key: "1qgfaw"
770
+ }],
771
+ ["path", {
772
+ d: "M12 19H3",
773
+ key: "yhmn1j"
774
+ }],
775
+ ["path", {
776
+ d: "M14 3v4",
777
+ key: "1sua03"
778
+ }],
779
+ ["path", {
780
+ d: "M16 17v4",
781
+ key: "1q0r14"
782
+ }],
783
+ ["path", {
784
+ d: "M21 12h-9",
785
+ key: "1o4lsq"
786
+ }],
787
+ ["path", {
788
+ d: "M21 19h-5",
789
+ key: "1rlt1p"
790
+ }],
791
+ ["path", {
792
+ d: "M21 5h-7",
793
+ key: "1oszz2"
794
+ }],
795
+ ["path", {
796
+ d: "M8 10v4",
797
+ key: "tgpxqk"
798
+ }],
799
+ ["path", {
800
+ d: "M8 12H3",
801
+ key: "a7s4jb"
802
+ }]
803
+ ]);
804
+ /**
805
+ * @license lucide-react v1.39.0 - ISC
806
+ *
807
+ * This source code is licensed under the ISC license.
808
+ * See the LICENSE file in the root directory of this source tree.
809
+ */
810
+ const Sparkles = createLucideIcon("sparkles", [
811
+ ["path", {
812
+ d: "M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z",
813
+ key: "1s2grr"
814
+ }],
815
+ ["path", {
816
+ d: "M20 2v4",
817
+ key: "1rf3ol"
818
+ }],
819
+ ["path", {
820
+ d: "M22 4h-4",
821
+ key: "gwowj6"
822
+ }],
823
+ ["circle", {
824
+ cx: "4",
825
+ cy: "20",
826
+ r: "2",
827
+ key: "6kqj1y"
828
+ }]
829
+ ]);
830
+ /**
831
+ * @license lucide-react v1.39.0 - ISC
832
+ *
833
+ * This source code is licensed under the ISC license.
834
+ * See the LICENSE file in the root directory of this source tree.
835
+ */
548
836
  const SquarePen = createLucideIcon("square-pen", [["path", {
549
837
  d: "M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7",
550
838
  key: "1m0v6g"
@@ -552,6 +840,41 @@ window.__ModuleLoader__.load({
552
840
  d: "M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z",
553
841
  key: "ohrbg2"
554
842
  }]]);
843
+ /**
844
+ * @license lucide-react v1.39.0 - ISC
845
+ *
846
+ * This source code is licensed under the ISC license.
847
+ * See the LICENSE file in the root directory of this source tree.
848
+ */
849
+ const Store = createLucideIcon("store", [
850
+ ["path", {
851
+ d: "M15 21v-5a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v5",
852
+ key: "slp6dd"
853
+ }],
854
+ ["path", {
855
+ d: "M17.774 10.31a1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.451 0 1.12 1.12 0 0 0-1.548 0 2.5 2.5 0 0 1-3.452 0 1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.77-3.248l2.889-4.184A2 2 0 0 1 7 2h10a2 2 0 0 1 1.653.873l2.895 4.192a2.5 2.5 0 0 1-3.774 3.244",
856
+ key: "o0xfot"
857
+ }],
858
+ ["path", {
859
+ d: "M4 10.95V19a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8.05",
860
+ key: "wn3emo"
861
+ }]
862
+ ]);
863
+ /**
864
+ * @license lucide-react v1.39.0 - ISC
865
+ *
866
+ * This source code is licensed under the ISC license.
867
+ * See the LICENSE file in the root directory of this source tree.
868
+ */
869
+ const User = createLucideIcon("user", [["path", {
870
+ d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",
871
+ key: "975kel"
872
+ }], ["circle", {
873
+ cx: "12",
874
+ cy: "7",
875
+ r: "4",
876
+ key: "17ys0d"
877
+ }]]);
555
878
  //#endregion
556
879
  //#region src/client/session-tree.tsx
557
880
  function PinIcon() {
@@ -5438,7 +5761,6 @@ body[data-we-sidebar-glass] .dcu-expanded-shell,body[data-we-sidebar-glass] .dcu
5438
5761
  .dcu-footer-actions [data-testid="billing-rail-button"] svg{flex:none;width:24px;height:24px;stroke-width:1.5px}
5439
5762
  .dcu-settings-seat [data-slot="settings.trigger"]{color:var(--dcu-sidebar-navigation)}
5440
5763
  .dcu-settings-seat [data-slot="settings.trigger"]>svg{color:var(--dcu-sidebar-icon)}
5441
- .dcu-root:not(.dcu-compact) .dcu-settings-seat [data-slot="settings.trigger"]>span{margin-left:4px}
5442
5764
  @media (prefers-reduced-motion:reduce){.dcu-footer-actions :is([data-testid="billing-trigger"],[data-testid="billing-rail-button"]){transition:none}}
5443
5765
  /* 宿主列负责缩放,宽态内容保持展开宽度,避免中文竖排与工作区逐帧重排。 */
5444
5766
  .dcu-expanded-shell,.dcu-root:not(.dcu-compact)>.dcu-foot{width:var(--dcu-sidebar-expanded-width,240px);flex-shrink:0}
@@ -5457,7 +5779,6 @@ body[data-we-sidebar-glass] .dcu-expanded-shell,body[data-we-sidebar-glass] .dcu
5457
5779
  @media (prefers-reduced-motion:reduce){[data-dcu-codex-sidebar-initialized],[data-dcu-codex-sidebar-initialized] [data-side="sidebar"],#root [data-dcu-codex-sidebar-initialized]:not([data-dragging]){transition:none}.dcu-root.dcu-collapsing .dcu-compact-shell{animation:none}}
5458
5780
  .dcu-native-workspaces{flex-direction:column}.dcu-native-workspaces>[data-mcp-connector-top-mount=true]{flex:none}.dcu-root .mcpConnectorLauncher,.dcu-root [data-mcp-connector-top-mount=true]{display:none!important}
5459
5781
  .dcu-dependency-notice{margin:0 10px 8px;border:1px solid var(--dcu-sidebar-border);border-radius:8px;padding:8px;color:var(--dcu-sidebar-secondary);font-size:12px;line-height:18px}
5460
- [role="dialog"][aria-labelledby]{width:min(1000px,calc(100vw - 48px));max-width:calc(100vw - 48px);height:min(800px,calc(100vh - 48px))}
5461
5782
  .dcu-search-scrim{position:fixed;z-index:10020;inset:0;display:flex;justify-content:center;align-items:flex-start;padding:72px 20px;background:color-mix(in srgb,#000 48%,transparent);animation:dcu-search-scrim-in 140ms ease-out}.dcu-search-dialog{width:min(560px,100%);max-height:min(640px,calc(100vh - 120px));overflow:auto;border:1px solid var(--dcu-sidebar-border);border-radius:16px;padding:10px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv4);animation:dcu-search-dialog-in 180ms cubic-bezier(.16,1,.3,1)}@keyframes dcu-search-scrim-in{from{opacity:0}to{opacity:1}}@keyframes dcu-search-dialog-in{from{opacity:0;transform:translateY(-6px) scale(.985)}to{opacity:1}}.dcu-search-input{margin-bottom:8px}.dcu-search-section{padding:6px 0}.dcu-search-title{padding:0 8px 4px;color:var(--dcu-sidebar-tertiary);font-size:12px;font-weight:600}.dcu-search-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:12px;width:100%;min-height:34px;border:0;border-radius:8px;padding:6px 8px;background:transparent;color:var(--dcu-sidebar-primary);font:inherit;text-align:left;cursor:pointer}.dcu-search-row:hover,.dcu-search-row[data-active=true]{background:var(--dcu-sidebar-hover)}.dcu-search-main{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dcu-search-detail{max-width:160px;overflow:hidden;color:var(--dcu-sidebar-tertiary);font-size:12px;text-overflow:ellipsis;white-space:nowrap}.dcu-search-empty{padding:18px 8px;color:var(--dcu-sidebar-tertiary);font-size:13px}@media (prefers-reduced-motion:reduce){.dcu-expanded-shell,.dcu-root.dcu-compact .dcu-compact-shell,.dcu-foot,.dcu-search-scrim,.dcu-search-dialog,.dcu-extension-panel,.dcu-extension-leading svg{animation:none;transition:none}}
5462
5783
  [data-conversation-scroll]{--dsh-composer-card-max-width:calc(var(--dsh-chat-content-width) + 32px);--dsh-composer-side-clearance:24px}[data-conversation-scroll] [data-composer-card]{min-height:96px;padding-top:10px;border-radius:16px;box-shadow:var(--dsw-shadow-lv3);transition:border-color 180ms ease,box-shadow 180ms ease}[data-conversation-scroll] [data-composer-card]:focus-within{border-color:var(--dsw-alias-button-info-fill);box-shadow:0 0 0 2px var(--dsw-static-deepseek-50),var(--dsw-shadow-lv3)}[data-conversation-scroll] [data-input-mirror]{min-height:44px}@media (prefers-reduced-motion:reduce){[data-conversation-scroll] [data-composer-card]{transition:none}}
5463
5784
  html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:has([data-dcu-official-turn-navigator]) .dcu-turn-navigator{display:none}
@@ -5545,7 +5866,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
5545
5866
  t
5546
5867
  ]);
5547
5868
  const openSettings = (0, react.useCallback)(() => {
5548
- settingsSeat.current?.querySelector("[aria-haspopup=\"dialog\"]")?.click();
5869
+ openSettingsRoot(settingsSeat.current);
5549
5870
  }, [settingsSeat]);
5550
5871
  const entries = (0, react.useMemo)(() => {
5551
5872
  const archived = new Set(workspaces.archivedSessionIds);
@@ -6856,6 +7177,23 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
6856
7177
  /** 本插件独占的界面文案命名空间。 */
6857
7178
  const NS = "michengai.codexUi";
6858
7179
  const zh = {
7180
+ "settings.title": "设置",
7181
+ "settings.back": "返回应用",
7182
+ "settings.openDocument": "打开配置文件",
7183
+ "settings.openDocumentError": "无法打开配置文件",
7184
+ "settings.search": "搜索设置…",
7185
+ "settings.personal": "个人",
7186
+ "settings.integrations": "集成",
7187
+ "settings.records": "记录与应用",
7188
+ "settings.general": "常规",
7189
+ "settings.permissions": "权限",
7190
+ "settings.editor": "编辑器",
7191
+ "settings.noResults": "没有匹配的设置分类",
7192
+ "settings.filterHint": "仅筛选导航,选择分类后切换当前页面。",
7193
+ "settings.disconnected": "连接异常",
7194
+ "settings.reconnect": "立即重连",
7195
+ "settings.connecting": "连接中",
7196
+ "settings.recovered": "连接成功",
6859
7197
  "input.historyHint": "↑↓ 翻历史",
6860
7198
  "sidebar.label": "DSH 导航",
6861
7199
  "sidebar.expand": "展开侧边栏",
@@ -7068,6 +7406,23 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
7068
7406
  "meta.locale": "zh-CN"
7069
7407
  };
7070
7408
  const en = {
7409
+ "settings.title": "Settings",
7410
+ "settings.back": "Back to app",
7411
+ "settings.openDocument": "Open configuration file",
7412
+ "settings.openDocumentError": "Could not open configuration file",
7413
+ "settings.search": "Search settings…",
7414
+ "settings.personal": "Personal",
7415
+ "settings.integrations": "Integrations",
7416
+ "settings.records": "Records and app",
7417
+ "settings.general": "General",
7418
+ "settings.permissions": "Permissions",
7419
+ "settings.editor": "Editor",
7420
+ "settings.noResults": "No matching settings categories",
7421
+ "settings.filterHint": "Navigation is filtered. Select a category to switch pages.",
7422
+ "settings.disconnected": "Disconnected",
7423
+ "settings.reconnect": "Reconnect now",
7424
+ "settings.connecting": "Connecting",
7425
+ "settings.recovered": "Connected",
7071
7426
  "input.historyHint": "↑↓ History",
7072
7427
  "sidebar.label": "DSH navigation",
7073
7428
  "sidebar.expand": "Expand sidebar",
@@ -7414,6 +7769,600 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
7414
7769
  };
7415
7770
  }
7416
7771
  //#endregion
7772
+ //#region src/client/settings-page-model.ts
7773
+ function settingsGroup(id) {
7774
+ if (/archive|about/.test(id)) return "records";
7775
+ if (/^(general|appearance|theme|language|shortcuts|voice|account)$/.test(id)) return "personal";
7776
+ return "integrations";
7777
+ }
7778
+ function filterSettingsRows(rows, query) {
7779
+ const needle = query.trim().toLocaleLowerCase();
7780
+ return needle === "" ? rows : rows.filter((row) => `${row.label} ${row.id}`.toLocaleLowerCase().includes(needle));
7781
+ }
7782
+ function generalItemGroup(id) {
7783
+ if (/permission|approval|sandbox/.test(id)) return "permissions";
7784
+ if (/composer|font|conversation|chat|enter/.test(id)) return "editor";
7785
+ return "general";
7786
+ }
7787
+ //#endregion
7788
+ //#region src/client/settings-page-styles.ts
7789
+ /** Codex 设置页的局部 tokens 与布局,不覆盖宿主其他弹窗。 */
7790
+ const settingsPageStyles = `
7791
+ .dcu-settings-page{--sp-bg:#fff;--sp-nav:#f5f5f5;--sp-card:#fafafa;--sp-border:#e5e5e5;--sp-text:#303030;--sp-muted:#737373;--sp-hover:#e9e9e9;--sp-active:#e4e4e4;position:fixed;inset:0;z-index:1000;display:grid;grid-template-columns:240px minmax(0,1fr);background:var(--sp-bg);color:var(--sp-text);font:14px/1.5 var(--dcu-font,system-ui);text-align:left;isolation:isolate}
7792
+ body[data-ds-dark-theme] .dcu-settings-page{--sp-bg:#181818;--sp-nav:#202322;--sp-card:#232323;--sp-border:#333;--sp-text:#dedede;--sp-muted:#a1a1a1;--sp-hover:#292c2b;--sp-active:#303332;color-scheme:dark}
7793
+ .dcu-settings-page *{box-sizing:border-box}
7794
+ .dcu-settings-page button,.dcu-settings-page input{font:inherit}
7795
+ .dcu-settings-page button{cursor:pointer}
7796
+ .dcu-settings-page :focus-visible{outline:2px solid #459cff;outline-offset:3px}
7797
+ .dcu-settings-nav{display:flex;flex-direction:column;min-height:0;padding:18px 8px;background:var(--sp-nav);border-right:1px solid var(--sp-border);overflow-y:auto}
7798
+ .dcu-settings-back{display:flex;align-items:center;gap:8px;width:100%;padding:8px;border:0;border-radius:7px;background:transparent;color:var(--sp-text);text-align:left}
7799
+ .dcu-settings-back:hover,.dcu-settings-link:hover{background:var(--sp-hover)}
7800
+ .dcu-settings-search{display:flex;align-items:center;gap:8px;padding:5px 9px;margin:12px 0 18px;background:var(--sp-hover);border:1px solid transparent;border-radius:8px;color:var(--sp-muted)}
7801
+ .dcu-settings-search:focus-within{border-color:#459cff}
7802
+ .dcu-settings-search input{width:100%;min-width:0;border:0;background:transparent;color:var(--sp-text);outline:none;font-size:13px}
7803
+ .dcu-settings-search input[type=search]{appearance:none;padding:0;border-radius:0;box-shadow:none;line-height:20px}
7804
+ .dcu-settings-search input:focus,.dcu-settings-search input:focus-visible{outline:none;box-shadow:none}
7805
+ .dcu-settings-search input::placeholder{color:var(--sp-muted)}
7806
+ .dcu-settings-group{margin:0 0 22px}
7807
+ .dcu-settings-group-label{margin:0 8px 6px;color:var(--sp-muted);font-size:12px;font-weight:500}
7808
+ .dcu-settings-link{display:flex;align-items:center;gap:9px;min-height:32px;width:100%;padding:5px 9px;border:0;border-radius:8px;background:transparent;color:var(--sp-text);text-align:left;font-size:14px!important}
7809
+ .dcu-settings-link[aria-current=page]{background:var(--sp-active)}
7810
+ .dcu-settings-link svg{flex:none;opacity:.9}
7811
+ .dcu-settings-link span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
7812
+ .dcu-settings-empty{padding:8px;color:var(--sp-muted);font-size:13px}
7813
+ .dcu-settings-main{min-width:0;min-height:0;overflow:auto;overscroll-behavior:contain;background:var(--sp-bg);scrollbar-width:thin;scrollbar-color:var(--sp-border) transparent}
7814
+ .dcu-settings-inner{width:min(100%,864px);margin:0 auto;padding:32px 48px}
7815
+ .dcu-settings-heading{display:flex;align-items:center;justify-content:space-between;gap:24px;margin-bottom:24px}
7816
+ .dcu-settings-heading h1{margin:0;font-size:24px;line-height:32px;font-weight:600;letter-spacing:-.4px}
7817
+ .dcu-settings-heading[data-own-title=false]{justify-content:flex-end;margin-bottom:16px}
7818
+ .dcu-settings-heading:not(:has(h1,button,a,[role=button])){display:none}
7819
+ .dcu-settings-actions{display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end;color:var(--sp-muted)}
7820
+ .dcu-settings-actions button{font-size:12px}
7821
+ .dcu-settings-general-group{margin-bottom:44px}
7822
+ .dcu-settings-general-group h2{margin:0 0 16px;font-size:14px;font-weight:600;color:var(--sp-text)}
7823
+ .dcu-settings-card{border:1px solid var(--sp-border);border-radius:16px;background:var(--sp-card);padding:0 16px;overflow:hidden}
7824
+ .dcu-settings-row+.dcu-settings-row{border-top:1px solid var(--sp-border)}
7825
+ .dcu-settings-row>[data-slot]{display:contents}
7826
+ .dcu-settings-row>[data-slot]>*{padding:12px 0!important;border-bottom:0!important;margin:0!important;min-width:0}
7827
+ .dcu-settings-row button{font-size:13px;border-radius:8px}
7828
+ .dcu-settings-row input{max-width:100%}
7829
+ .dcu-settings-trigger{appearance:none;box-sizing:border-box;display:flex;align-items:center;border:0;border-radius:8px;background:transparent;color:var(--dcu-sidebar-navigation,inherit);height:36px;min-height:36px;padding:0 4px;width:100%;font:400 14px/20px var(--dcu-font,system-ui);text-align:left;cursor:pointer;transition:background-color 160ms ease,color 160ms ease,transform 120ms ease}
7830
+ .dcu-settings-trigger-content{display:grid;grid-template-columns:20px minmax(0,1fr);column-gap:8px;align-items:center;width:100%;min-width:0}
7831
+ .dcu-settings-trigger-content svg{display:block;width:16px;height:16px;color:var(--dcu-sidebar-icon,currentColor);transition:transform 220ms cubic-bezier(.16,1,.3,1)}
7832
+ .dcu-settings-trigger:hover{background:var(--dcu-sidebar-hover,rgba(127,127,127,.12));color:var(--dcu-sidebar-primary,inherit)}
7833
+ .dcu-settings-trigger:hover svg{transform:rotate(18deg)}
7834
+ .dcu-settings-trigger:active{transform:scale(.98)}
7835
+ .dcu-settings-trigger:focus-visible{outline:2px solid #459cff;outline-offset:2px;background:var(--dcu-sidebar-hover,rgba(127,127,127,.12))}
7836
+ .dcu-settings-trigger[data-wide=false]{justify-content:center;width:36px;padding:0!important}
7837
+ .dcu-settings-trigger[data-wide=false] .dcu-settings-trigger-content{grid-template-columns:16px;justify-content:center}
7838
+ .dcu-settings-page{animation:dcu-settings-enter 180ms ease-out}
7839
+ .dcu-settings-nav{animation:dcu-settings-nav-enter 220ms cubic-bezier(.16,1,.3,1)}
7840
+ .dcu-settings-inner{animation:dcu-settings-content-enter 240ms cubic-bezier(.16,1,.3,1)}
7841
+ .dcu-settings-back,.dcu-settings-link{transition:background-color 160ms ease,color 160ms ease}
7842
+ @keyframes dcu-settings-enter{from{opacity:0}to{opacity:1}}
7843
+ @keyframes dcu-settings-nav-enter{from{opacity:0;transform:translateX(-6px)}to{opacity:1;transform:none}}
7844
+ @keyframes dcu-settings-content-enter{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
7845
+ @media(prefers-reduced-motion:reduce){.dcu-settings-trigger,.dcu-settings-trigger-content svg,.dcu-settings-back,.dcu-settings-link{transition:none}.dcu-settings-trigger:hover svg,.dcu-settings-trigger:active{transform:none}.dcu-settings-page,.dcu-settings-nav,.dcu-settings-inner{animation:none}}
7846
+ .dcu-settings-page [data-slot="settings.section"]{min-width:0}
7847
+ .dcu-settings-inner:has(.dcu-connector-frame){height:100%;display:flex;flex-direction:column}
7848
+ .dcu-settings-inner:has(.dcu-connector-frame)>.dcu-settings-heading{flex:none}
7849
+ .dcu-settings-page .dcu-connectors:has(>.dcu-connector-frame){display:flex;flex-direction:column;flex:1 1 0;min-height:0}
7850
+ .dcu-settings-page .dcu-connector-frame{flex:1 1 0;min-height:0;height:100%}
7851
+
7852
+ @media(max-width:900px){.dcu-settings-page{grid-template-columns:200px minmax(0,1fr)}.dcu-settings-inner{padding:24px 28px}.dcu-settings-heading{align-items:flex-start;flex-direction:column;gap:12px}}
7853
+ @media(max-width:600px){.dcu-settings-page{grid-template-columns:1fr;grid-template-rows:auto minmax(0,1fr)}.dcu-settings-nav{padding:8px 12px;max-height:220px;border-right:0;border-bottom:1px solid var(--sp-border)}.dcu-settings-search{margin:6px 0}.dcu-settings-groups{display:flex;gap:8px;overflow-x:auto;flex:none}.dcu-settings-group{display:flex;gap:4px;margin:0;flex:none}.dcu-settings-group-label{display:none}.dcu-settings-link{width:auto;min-height:40px;flex:none}.dcu-settings-inner{padding:24px 16px}.dcu-settings-heading{margin-bottom:28px}.dcu-settings-heading h1{font-size:22px}.dcu-settings-card{padding:0 12px}.dcu-settings-row>[data-slot]>*{flex-wrap:wrap;gap:12px!important}.dcu-settings-general-group{margin-bottom:32px}}
7854
+ `;
7855
+ //#endregion
7856
+ //#region src/client/settings-focus.ts
7857
+ /** 设置页与宿主浮层共享的可交互判断,忽略隐藏及已被隔离的对话框。 */
7858
+ function settingsElementAvailable(element) {
7859
+ for (let node = element; node !== null; node = node.parentElement) {
7860
+ const style = getComputedStyle(node);
7861
+ if (node.inert || node.hidden || node.getAttribute("aria-hidden") === "true" || style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse") return false;
7862
+ }
7863
+ return true;
7864
+ }
7865
+ function settingsOverlays(selector = "[role=\"dialog\"],[role=\"alertdialog\"],[role=\"menu\"],[role=\"listbox\"]") {
7866
+ return [...document.querySelectorAll(selector)].filter(settingsElementAvailable);
7867
+ }
7868
+ //#endregion
7869
+ //#region src/client/CodexSettingsPage.tsx
7870
+ const groupLabels = {
7871
+ personal: "settings.personal",
7872
+ integrations: "settings.integrations",
7873
+ records: "settings.records",
7874
+ permissions: "settings.permissions",
7875
+ general: "settings.general",
7876
+ editor: "settings.editor"
7877
+ };
7878
+ function sectionIcon(id) {
7879
+ if (id === "market" || id === "plugin-marketplace") return Store;
7880
+ if (id === "better-sidebar" || id === "sidebar-cards") return PanelRight;
7881
+ if (/model/.test(id)) return Cpu;
7882
+ if (/archive/.test(id)) return Archive;
7883
+ if (/about/.test(id)) return CircleQuestionMark;
7884
+ if (/expert|agency/.test(id)) return User;
7885
+ if (/skill/.test(id)) return Sparkles;
7886
+ if (/connector|mcp/.test(id)) return Link;
7887
+ if (/schedule|automation/.test(id)) return Clock;
7888
+ if (/im|assistant/.test(id)) return MessageSquare;
7889
+ if (id === "general") return Settings;
7890
+ if (/plugin/.test(id)) return SlidersHorizontal;
7891
+ return Box;
7892
+ }
7893
+ /** 独立设置视图复用原始 section/close 合约,退出时保留底层会话与输入状态。 */
7894
+ function CodexSettingsPage({ wide, sections, onboarding, connectionState, reconnect, useSessions, renderSlot, t }) {
7895
+ const rows = (0, react.useSyncExternalStore)(sections.subscribe, sections.getSnapshot);
7896
+ const steps = (0, react.useSyncExternalStore)(onboarding.subscribe, onboarding.getSnapshot);
7897
+ const connection = (0, react.useSyncExternalStore)(connectionState.subscribe, connectionState.getSnapshot);
7898
+ const onboardingActive = useSessions((state) => state.phase === "ready" && (state.current === void 0 || state.byId[state.current]?.blank === true));
7899
+ const [completed, setCompleted] = (0, react.useState)(() => /* @__PURE__ */ new Set());
7900
+ const [open, setOpen] = (0, react.useState)(false);
7901
+ const [activeId, setActiveId] = (0, react.useState)("general");
7902
+ const [query, setQuery] = (0, react.useState)("");
7903
+ const [recovered, setRecovered] = (0, react.useState)(false);
7904
+ const previousConnection = (0, react.useRef)(connection);
7905
+ const trigger = (0, react.useRef)(null);
7906
+ const page = (0, react.useRef)(null);
7907
+ const back = (0, react.useRef)(null);
7908
+ const onboardingRoot = (0, react.useRef)(null);
7909
+ const main = (0, react.useRef)(null);
7910
+ const wasOpen = (0, react.useRef)(false);
7911
+ const exitAnimation = (0, react.useRef)(null);
7912
+ const active = rows.find((row) => row.id === activeId) ?? rows[0];
7913
+ const step = onboardingActive ? steps.find((item) => !completed.has(item.id)) : void 0;
7914
+ const close = (0, react.useCallback)(() => {
7915
+ if (exitAnimation.current !== null) return;
7916
+ const finish = () => {
7917
+ exitAnimation.current = null;
7918
+ setOpen(false);
7919
+ setQuery("");
7920
+ };
7921
+ const element = page.current;
7922
+ if (element?.animate === void 0 || window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
7923
+ finish();
7924
+ return;
7925
+ }
7926
+ const animation = element.animate([{ opacity: getComputedStyle(element).opacity }, { opacity: 0 }], {
7927
+ duration: 140,
7928
+ easing: "ease-out",
7929
+ fill: "forwards"
7930
+ });
7931
+ exitAnimation.current = animation;
7932
+ animation.onfinish = finish;
7933
+ }, []);
7934
+ const openSection = (0, react.useCallback)((id) => {
7935
+ exitAnimation.current?.cancel();
7936
+ exitAnimation.current = null;
7937
+ setActiveId(id);
7938
+ setOpen(true);
7939
+ }, []);
7940
+ (0, react.useEffect)(() => () => {
7941
+ exitAnimation.current?.cancel();
7942
+ }, []);
7943
+ (0, react.useEffect)(() => {
7944
+ if (!onboardingActive) setCompleted(/* @__PURE__ */ new Set());
7945
+ }, [onboardingActive]);
7946
+ (0, react.useEffect)(() => {
7947
+ const previous = previousConnection.current;
7948
+ previousConnection.current = connection;
7949
+ setRecovered(connection === "connected" && (previous === "disconnected" || previous === "connecting"));
7950
+ if (connection !== "connected") return;
7951
+ const timer = window.setTimeout(() => {
7952
+ setRecovered(false);
7953
+ }, 3e3);
7954
+ return () => {
7955
+ window.clearTimeout(timer);
7956
+ };
7957
+ }, [connection]);
7958
+ (0, react.useEffect)(() => {
7959
+ if (wasOpen.current && !open) trigger.current?.focus();
7960
+ wasOpen.current = open;
7961
+ if (!open || page.current === null) return;
7962
+ if (settingsElementAvailable(page.current) && settingsOverlays().length === 0) back.current?.focus();
7963
+ const hidden = /* @__PURE__ */ new Map();
7964
+ const guideModals = step === void 0 ? [] : settingsOverlays("[role=\"dialog\"][aria-modal=\"true\"],[role=\"alertdialog\"][aria-modal=\"true\"]");
7965
+ const isolate = (element) => {
7966
+ if (element === onboardingRoot.current || guideModals.includes(element)) return;
7967
+ if (guideModals.some((modal) => element.contains(modal))) {
7968
+ for (const child of element.children) if (child instanceof HTMLElement && !/^(STYLE|SCRIPT|LINK)$/.test(child.tagName)) isolate(child);
7969
+ return;
7970
+ }
7971
+ hidden.set(element, element.inert);
7972
+ element.inert = true;
7973
+ };
7974
+ let branch = page.current;
7975
+ while (branch.parentElement !== null) {
7976
+ for (const sibling of branch.parentElement.children) {
7977
+ if (!(sibling instanceof HTMLElement) || sibling === branch || /^(STYLE|SCRIPT|LINK)$/.test(sibling.tagName)) continue;
7978
+ isolate(sibling);
7979
+ }
7980
+ branch = branch.parentElement;
7981
+ if (branch === document.body) break;
7982
+ }
7983
+ const onKeyDown = (event) => {
7984
+ if (event.key === "Escape" && !event.defaultPrevented && settingsOverlays().length === 0) {
7985
+ event.preventDefault();
7986
+ close();
7987
+ }
7988
+ };
7989
+ const keepFocus = (event) => {
7990
+ const target = event.target;
7991
+ if (!(target instanceof Node) || page.current === null || !settingsElementAvailable(page.current)) return;
7992
+ if (page.current.contains(target) || onboardingRoot.current?.contains(target) || settingsOverlays().some((overlay) => overlay.contains(target))) return;
7993
+ back.current?.focus();
7994
+ };
7995
+ const wrapFocus = (event) => {
7996
+ if (event.key !== "Tab" || event.defaultPrevented || page.current === null || !settingsElementAvailable(page.current) || settingsOverlays().length > 0) return;
7997
+ const items = [page.current, onboardingRoot.current].filter((root) => root !== null).flatMap((root) => [...root.querySelectorAll("button,input,select,textarea,a[href],[tabindex]")]).filter((element) => element.tabIndex >= 0 && !element.matches(":disabled") && settingsElementAvailable(element));
7998
+ const next = event.shiftKey ? items.at(-1) : items[0];
7999
+ if (document.activeElement === (event.shiftKey ? items[0] : items.at(-1))) {
8000
+ event.preventDefault();
8001
+ next?.focus();
8002
+ }
8003
+ };
8004
+ document.addEventListener("keydown", onKeyDown, true);
8005
+ document.addEventListener("keydown", wrapFocus);
8006
+ document.addEventListener("focusin", keepFocus);
8007
+ return () => {
8008
+ document.removeEventListener("keydown", onKeyDown, true);
8009
+ document.removeEventListener("keydown", wrapFocus);
8010
+ document.removeEventListener("focusin", keepFocus);
8011
+ for (const [element, inert] of hidden) element.inert = inert;
8012
+ };
8013
+ }, [
8014
+ open,
8015
+ close,
8016
+ step?.id
8017
+ ]);
8018
+ (0, react.useEffect)(() => {
8019
+ if (main.current !== null) main.current.scrollTop = 0;
8020
+ }, [active?.id]);
8021
+ const visible = filterSettingsRows(rows, query);
8022
+ const connectionIndicator = connection === "disconnected" ? "disconnected" : connection === "connecting" ? "connecting" : recovered ? "recovered" : void 0;
8023
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
8024
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: settingsPageStyles }),
8025
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8026
+ ref: trigger,
8027
+ type: "button",
8028
+ className: "dcu-settings-trigger",
8029
+ "data-dcu-settings-trigger": true,
8030
+ "data-wide": wide,
8031
+ "aria-expanded": open,
8032
+ "aria-label": t("settings.title"),
8033
+ onClick: () => {
8034
+ setOpen(true);
8035
+ },
8036
+ children: renderSlot("settings.trigger", { wide })
8037
+ }),
8038
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.ConnectionIndicator, {
8039
+ state: wide ? connectionIndicator : void 0,
8040
+ disconnectedLabel: t("settings.disconnected"),
8041
+ reconnectLabel: t("settings.reconnect"),
8042
+ connectingLabel: t("settings.connecting"),
8043
+ recoveredLabel: t("settings.recovered"),
8044
+ reconnectActionLabel: t("settings.reconnect"),
8045
+ restartActionLabel: t("settings.reconnect"),
8046
+ onReconnect: reconnect
8047
+ }),
8048
+ open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8049
+ ref: page,
8050
+ className: "dcu-settings-page",
8051
+ "data-dcu-settings-page": true,
8052
+ role: "region",
8053
+ "aria-label": t("settings.title"),
8054
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("nav", {
8055
+ className: "dcu-settings-nav",
8056
+ "aria-label": t("settings.title"),
8057
+ children: [
8058
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
8059
+ ref: back,
8060
+ type: "button",
8061
+ className: "dcu-settings-back",
8062
+ onClick: close,
8063
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArrowLeft, { size: 16 }), renderSlot("settings.close", {}) ?? t("settings.back")]
8064
+ }),
8065
+ renderSlot("settings.header", {}),
8066
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
8067
+ className: "dcu-settings-search",
8068
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Search, {
8069
+ size: 15,
8070
+ "aria-hidden": "true"
8071
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8072
+ type: "search",
8073
+ value: query,
8074
+ "aria-label": t("settings.search"),
8075
+ placeholder: t("settings.search"),
8076
+ onChange: (event) => {
8077
+ setQuery(event.target.value);
8078
+ }
8079
+ })]
8080
+ }),
8081
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8082
+ className: "dcu-settings-groups",
8083
+ children: [
8084
+ "personal",
8085
+ "integrations",
8086
+ "records"
8087
+ ].map((group) => {
8088
+ const entries = visible.filter((row) => settingsGroup(row.id) === group);
8089
+ return entries.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
8090
+ className: "dcu-settings-group",
8091
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
8092
+ className: "dcu-settings-group-label",
8093
+ children: t(groupLabels[group])
8094
+ }), entries.map((row) => {
8095
+ const Icon = sectionIcon(row.id);
8096
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
8097
+ type: "button",
8098
+ className: "dcu-settings-link",
8099
+ "aria-current": row.id === active?.id ? "page" : void 0,
8100
+ onClick: () => {
8101
+ setActiveId(row.id);
8102
+ },
8103
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
8104
+ size: 16,
8105
+ strokeWidth: 1.6,
8106
+ "aria-hidden": "true"
8107
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: row.id === "general" ? t("settings.general") : row.label })]
8108
+ }, row.id);
8109
+ })]
8110
+ }, group);
8111
+ })
8112
+ }),
8113
+ visible.length > 0 && !visible.some((row) => row.id === active?.id) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8114
+ className: "dcu-settings-empty",
8115
+ role: "status",
8116
+ children: t("settings.filterHint")
8117
+ }),
8118
+ visible.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8119
+ className: "dcu-settings-empty",
8120
+ role: "status",
8121
+ children: t("settings.noResults")
8122
+ })
8123
+ ]
8124
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8125
+ ref: main,
8126
+ className: "dcu-settings-main",
8127
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8128
+ className: "dcu-settings-inner",
8129
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
8130
+ className: "dcu-settings-heading",
8131
+ "data-own-title": active?.id === "general",
8132
+ children: [active?.id === "general" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", { children: t("settings.general") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8133
+ className: "dcu-settings-actions",
8134
+ children: renderSlot("settings.action", {})
8135
+ })]
8136
+ }), active !== void 0 && renderSlot("settings.section", { close }, { only: active.id })]
8137
+ })
8138
+ })]
8139
+ }),
8140
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8141
+ ref: onboardingRoot,
8142
+ style: { display: "contents" },
8143
+ children: step !== void 0 && renderSlot("settings.onboarding", {
8144
+ stepId: step.id,
8145
+ complete: () => {
8146
+ setCompleted((previous) => /* @__PURE__ */ new Set([...previous, step.id]));
8147
+ },
8148
+ openSection
8149
+ }, { only: step.id })
8150
+ })
8151
+ ] });
8152
+ }
8153
+ /** 每条偏好仍由原插件渲染和保存,只为公共条目添加可扩展的分组容器。 */
8154
+ function CodexGeneralSettings({ items, renderSlot, t }) {
8155
+ const rows = (0, react.useSyncExternalStore)(items.subscribe, items.getSnapshot);
8156
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8157
+ className: "dcu-settings-general",
8158
+ children: [
8159
+ "permissions",
8160
+ "general",
8161
+ "editor"
8162
+ ].map((group) => {
8163
+ const entries = rows.filter((row) => generalItemGroup(row.id) === group);
8164
+ return entries.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
8165
+ className: "dcu-settings-general-group",
8166
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t(groupLabels[group]) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8167
+ className: "dcu-settings-card",
8168
+ children: entries.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8169
+ className: "dcu-settings-row",
8170
+ children: renderSlot("settings.general.item", {}, { only: row.id })
8171
+ }, row.id))
8172
+ })]
8173
+ }, group);
8174
+ })
8175
+ });
8176
+ }
8177
+ //#endregion
8178
+ //#region src/client/SettingsDocumentAction.tsx
8179
+ /** 通过宿主公开接口打开配置文件,沿用共享元数据和路径无关的操作。 */
8180
+ function SettingsDocumentAction({ describe, openDocument, t }) {
8181
+ const snapshot = (0, react.useSyncExternalStore)((listener) => describe.subscribe(listener), () => describe.getSnapshot());
8182
+ const busy = (0, react.useRef)(false);
8183
+ const [opening, setOpening] = (0, react.useState)(false);
8184
+ const [error, setError] = (0, react.useState)(false);
8185
+ (0, react.useEffect)(() => {
8186
+ let active = true;
8187
+ setError(false);
8188
+ describe.ensure().catch(() => {
8189
+ if (active) setError(true);
8190
+ });
8191
+ return () => {
8192
+ active = false;
8193
+ };
8194
+ }, [describe]);
8195
+ if (!snapshot.view?.hasDocument) return error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8196
+ role: "alert",
8197
+ children: t("settings.openDocumentError")
8198
+ }) : null;
8199
+ const open = async () => {
8200
+ if (busy.current) return;
8201
+ busy.current = true;
8202
+ setOpening(true);
8203
+ setError(false);
8204
+ try {
8205
+ setError(!(await openDocument()).ok);
8206
+ } catch {
8207
+ setError(true);
8208
+ } finally {
8209
+ busy.current = false;
8210
+ setOpening(false);
8211
+ }
8212
+ };
8213
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8214
+ role: "alert",
8215
+ children: t("settings.openDocumentError")
8216
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8217
+ type: "button",
8218
+ disabled: opening,
8219
+ onClick: () => {
8220
+ open();
8221
+ },
8222
+ children: t("settings.openDocument")
8223
+ })] });
8224
+ }
8225
+ //#endregion
8226
+ //#region src/client/settings-page-registration.ts
8227
+ /** 用公开插槽替换设置壳,保留宿主的设置写入、连接恢复和首次使用引导。 */
8228
+ function registerSettingsPage(ctx) {
8229
+ const children = [
8230
+ "settings.trigger",
8231
+ "settings.header",
8232
+ "settings.action",
8233
+ "settings.close",
8234
+ "settings.section",
8235
+ "settings.onboarding",
8236
+ "settings.general.item"
8237
+ ];
8238
+ const occupied = () => ctx.slots.entriesOfSlot("sidebar.settings").length > 0 || children.some((name) => ctx.slots.snapshot(name).length > 0);
8239
+ const warn = () => console.warn("[michengai-codex-ui] 已存在设置外壳,保留宿主设置;独立设置页需要 bundle patch 停用 ui-settings-general。");
8240
+ if (occupied()) {
8241
+ warn();
8242
+ return;
8243
+ }
8244
+ let owned = false;
8245
+ const t = ctx.locale.bind(NS);
8246
+ const connection = ctx.get("connection");
8247
+ const source = (name) => {
8248
+ let revision = "";
8249
+ let cached = [];
8250
+ return {
8251
+ getSnapshot: () => {
8252
+ const next = `${ctx.slots.getVersion(name)}:${ctx.locale.getSnapshot().revision}`;
8253
+ if (revision !== next) {
8254
+ revision = next;
8255
+ cached = ctx.slots.entriesOfSlot(name).map((entry) => ({
8256
+ id: entry.options.id ?? "",
8257
+ order: entry.options.order ?? 0,
8258
+ label: (typeof entry.options.label === "function" ? entry.options.label() : entry.options.label) ?? ""
8259
+ })).sort((a, b) => a.order - b.order);
8260
+ }
8261
+ return cached;
8262
+ },
8263
+ subscribe: (listener) => {
8264
+ const offSlots = ctx.slots.subscribe(name, listener);
8265
+ const offLocale = ctx.locale.subscribe(listener);
8266
+ return () => {
8267
+ offSlots();
8268
+ offLocale();
8269
+ };
8270
+ }
8271
+ };
8272
+ };
8273
+ const sections = source("settings.section");
8274
+ const onboarding = source("settings.onboarding");
8275
+ const items = source("settings.general.item");
8276
+ ctx.slots.inject("settings.trigger", () => !owned ? () => {} : ctx.slots.register({
8277
+ name: "settings.trigger",
8278
+ locale: NS
8279
+ }, ({ wide }) => (0, react.createElement)("span", { className: "dcu-settings-trigger-content" }, (0, react.createElement)(Settings, {
8280
+ size: 16,
8281
+ strokeWidth: 1.6
8282
+ }), wide ? (0, react.createElement)("span", null, t("settings.title")) : null)));
8283
+ ctx.slots.inject("settings.close", () => !owned ? () => {} : ctx.slots.register({
8284
+ name: "settings.close",
8285
+ locale: NS
8286
+ }, () => t("settings.back")));
8287
+ ctx.inject(["settingsScope", "remote.settings"], (settingsCtx) => {
8288
+ const remote = settingsCtx.get("remote");
8289
+ if (!remote.$host.isLoopback) return;
8290
+ const describe = settingsCtx.settingsScope.describe();
8291
+ settingsCtx.slots.inject("settings.action", () => !owned ? () => {} : settingsCtx.slots.register({
8292
+ name: "settings.action",
8293
+ id: "open-document",
8294
+ locale: NS,
8295
+ inject: () => ({
8296
+ describe,
8297
+ openDocument: () => remote.settings.openSettingsDocument()
8298
+ })
8299
+ }, SettingsDocumentAction));
8300
+ });
8301
+ ctx.slots.inject("sidebar.settings", () => {
8302
+ if (occupied()) {
8303
+ warn();
8304
+ return () => {};
8305
+ }
8306
+ owned = true;
8307
+ const remove = ctx.slots.register({
8308
+ name: "sidebar.settings",
8309
+ priority: -1,
8310
+ locale: NS,
8311
+ children: {
8312
+ "settings.trigger": {
8313
+ kind: "single",
8314
+ scope: "root"
8315
+ },
8316
+ "settings.header": {
8317
+ kind: "single",
8318
+ scope: "root"
8319
+ },
8320
+ "settings.action": {
8321
+ kind: "list",
8322
+ scope: "root"
8323
+ },
8324
+ "settings.close": {
8325
+ kind: "single",
8326
+ scope: "root"
8327
+ },
8328
+ "settings.section": {
8329
+ kind: "list",
8330
+ scope: "root"
8331
+ },
8332
+ "settings.onboarding": {
8333
+ kind: "list",
8334
+ scope: "root"
8335
+ }
8336
+ },
8337
+ inject: () => ({
8338
+ sections,
8339
+ onboarding,
8340
+ connectionState: connection.state,
8341
+ reconnect: () => {
8342
+ connection.reconnect();
8343
+ }
8344
+ })
8345
+ }, CodexSettingsPage);
8346
+ return () => {
8347
+ owned = false;
8348
+ remove();
8349
+ };
8350
+ });
8351
+ ctx.slots.inject("settings.section", () => !owned ? () => {} : ctx.slots.register({
8352
+ name: "settings.section",
8353
+ id: "general",
8354
+ priority: -1,
8355
+ order: 0,
8356
+ locale: NS,
8357
+ label: () => t("settings.general"),
8358
+ children: { "settings.general.item": {
8359
+ kind: "list",
8360
+ scope: "root"
8361
+ } },
8362
+ inject: () => ({ items })
8363
+ }, CodexGeneralSettings));
8364
+ }
8365
+ //#endregion
7417
8366
  //#region src/client/permission-labels.ts
7418
8367
  const PERMISSION_LABELS = [
7419
8368
  {
@@ -8183,6 +9132,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
8183
9132
  }), "michengai-codex-ui: dictionaries");
8184
9133
  const t = ctx.locale.bind(NS);
8185
9134
  registerInputHistory(ctx);
9135
+ registerSettingsPage(ctx);
8186
9136
  const connection = ctx.get("connection");
8187
9137
  const openPath = (path) => openPathInHost(connection, path);
8188
9138
  ctx.effect(() => observeSlimSidebar(), "michengai-codex-ui: slim sidebar");
package/lib/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { apply as apply$1 } from "@deepseek-ai/dsh-client-ui-settings-general";
1
2
  import { spawn } from "node:child_process";
2
3
  import { existsSync } from "node:fs";
3
4
  import { lstat, mkdir, open, readFile, realpath, rename, rm, unlink, writeFile } from "node:fs/promises";
@@ -1674,6 +1675,7 @@ function sessionMoveAuthenticationError(status, error) {
1674
1675
  }
1675
1676
  /** 提供不泄露地址、命令和凭证的连接器目录。 */
1676
1677
  function apply(ctx) {
1678
+ apply$1(ctx);
1677
1679
  const host = hostServices(ctx);
1678
1680
  ctx.effect(() => {
1679
1681
  const foregroundExplorer = new ForegroundExplorer();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-codex-ui",
3
- "version": "0.2.113",
3
+ "version": "1.0.0",
4
4
  "description": "以 Codex 风格重构 DSH Web 侧栏的独立客户端插件",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {
@@ -54,7 +54,8 @@
54
54
  "@deepseek-ai/dsh-client-ui-conversation",
55
55
  "@deepseek-ai/dsh-client-ui-input-trigger",
56
56
  "@deepseek-ai/dsh-client-ui-workspace",
57
- "@deepseek-ai/dsh-client-ui-settings-general"
57
+ "@deepseek-ai/dsh-client-ui-settings",
58
+ "@deepseek-ai/dsh-client-connection"
58
59
  ],
59
60
  "platform": "web"
60
61
  }
@@ -63,7 +64,7 @@
63
64
  "build": "tsc --noEmit && tsdown",
64
65
  "typecheck": "tsc --noEmit",
65
66
  "test": "pnpm run typecheck && vitest run && tsdown && node tests/run-assertions.mjs && pnpm run test:layout",
66
- "test:layout": "node scripts/verify-workspace-drop-layout.mjs --run && node scripts/verify-sidebar-transition.mjs"
67
+ "test:layout": "node scripts/verify-workspace-drop-layout.mjs --run && node scripts/verify-sidebar-transition.mjs && node scripts/verify-settings-exit.mjs"
67
68
  },
68
69
  "peerDependencies": {
69
70
  "@deepseek-ai/cordis": ">=4.0.2 <5.0.0",