@herbertgao/pi-extensions 2026.9.19 → 2026.9.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +1 -8
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +76 -59
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +76 -59
  4. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/preview.webp +0 -0
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +93 -34
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +346 -117
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +6 -36
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/index.ts +2 -2
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -29
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +330 -360
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/index.ts +56 -108
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/session.ts +97 -15
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/subagent.ts +80 -45
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/flush-docked-bash.ts +36 -0
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer-layout.ts +229 -0
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +479 -0
  17. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/startup-header.ts +92 -61
  18. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +58 -38
  19. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +19 -3
  20. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +1080 -229
  21. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +232 -259
  22. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +38 -48
  23. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +8 -2
  24. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +2 -7
  25. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +277 -113
  26. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +15 -6
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +18 -72
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +61 -26
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/ansi-utils.ts +25 -57
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-component.ts +125 -0
  31. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-edit-render.ts +227 -0
  32. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +136 -0
  33. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +115 -0
  34. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-inline.ts +324 -0
  35. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-layout.ts +762 -0
  36. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-limits.ts +152 -0
  37. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-palette.ts +410 -0
  38. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-parse.ts +535 -0
  39. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +2 -2
  40. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +21 -3219
  41. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-text.ts +44 -0
  42. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +568 -0
  43. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +19 -18
  44. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +2 -2
  45. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +1 -0
  46. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +142 -192
  47. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/mcp-title.ts +39 -0
  48. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{message-display.ts → tool/message-display.ts} +24 -90
  49. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +426 -0
  50. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +90 -132
  51. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{show-more-hint.ts → tool/show-more-hint.ts} +10 -0
  52. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +8 -26
  53. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-color.ts +44 -0
  54. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +76 -0
  55. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/component-tree.ts +34 -0
  56. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/format.ts +28 -0
  57. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +51 -0
  58. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/patch-keys.ts +124 -0
  59. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/sgr-mouse.ts +70 -0
  60. package/node_modules/@herbertgao/pi-cc-extensions/package.json +7 -7
  61. package/package.json +3 -7
  62. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +0 -101
  63. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.svg +0 -101
  64. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/welcome.webp +0 -0
  65. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/render-utils.ts +0 -5
  66. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +0 -30
  67. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +0 -12
  68. package/node_modules/pi-footer/CHANGELOG.md +0 -145
  69. package/node_modules/pi-footer/LICENSE +0 -21
  70. package/node_modules/pi-footer/README.md +0 -354
  71. package/node_modules/pi-footer/package.json +0 -68
  72. package/node_modules/pi-footer/src/cache.ts +0 -106
  73. package/node_modules/pi-footer/src/colors.ts +0 -237
  74. package/node_modules/pi-footer/src/config.ts +0 -202
  75. package/node_modules/pi-footer/src/event-widgets.ts +0 -41
  76. package/node_modules/pi-footer/src/extension-statuses.ts +0 -102
  77. package/node_modules/pi-footer/src/git.ts +0 -135
  78. package/node_modules/pi-footer/src/index.ts +0 -268
  79. package/node_modules/pi-footer/src/metrics.ts +0 -141
  80. package/node_modules/pi-footer/src/presets.ts +0 -383
  81. package/node_modules/pi-footer/src/render.ts +0 -114
  82. package/node_modules/pi-footer/src/separators.ts +0 -52
  83. package/node_modules/pi-footer/src/types.ts +0 -122
  84. package/node_modules/pi-footer/src/ui/color-level-confirm.ts +0 -22
  85. package/node_modules/pi-footer/src/ui/color-options.ts +0 -195
  86. package/node_modules/pi-footer/src/ui/config-lifecycle.ts +0 -61
  87. package/node_modules/pi-footer/src/ui/edit-colors.ts +0 -13
  88. package/node_modules/pi-footer/src/ui/events.ts +0 -35
  89. package/node_modules/pi-footer/src/ui/extension-status-picker.ts +0 -60
  90. package/node_modules/pi-footer/src/ui/extension-statuses.ts +0 -73
  91. package/node_modules/pi-footer/src/ui/fields.ts +0 -252
  92. package/node_modules/pi-footer/src/ui/global-menu.ts +0 -126
  93. package/node_modules/pi-footer/src/ui/helpers.ts +0 -44
  94. package/node_modules/pi-footer/src/ui/layout.ts +0 -17
  95. package/node_modules/pi-footer/src/ui/line-list.ts +0 -19
  96. package/node_modules/pi-footer/src/ui/model.ts +0 -64
  97. package/node_modules/pi-footer/src/ui/navigation.ts +0 -29
  98. package/node_modules/pi-footer/src/ui/option-edit.ts +0 -93
  99. package/node_modules/pi-footer/src/ui/overlay-render.ts +0 -103
  100. package/node_modules/pi-footer/src/ui/screen-context.ts +0 -18
  101. package/node_modules/pi-footer/src/ui/screen-controller.ts +0 -27
  102. package/node_modules/pi-footer/src/ui/screen-render.ts +0 -32
  103. package/node_modules/pi-footer/src/ui/screen-state.ts +0 -20
  104. package/node_modules/pi-footer/src/ui/screen.ts +0 -243
  105. package/node_modules/pi-footer/src/ui/screens/add-widget.ts +0 -92
  106. package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +0 -52
  107. package/node_modules/pi-footer/src/ui/screens/controller.ts +0 -12
  108. package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +0 -68
  109. package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +0 -115
  110. package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +0 -61
  111. package/node_modules/pi-footer/src/ui/screens/global.ts +0 -75
  112. package/node_modules/pi-footer/src/ui/screens/line-list.ts +0 -108
  113. package/node_modules/pi-footer/src/ui/screens/main.ts +0 -99
  114. package/node_modules/pi-footer/src/ui/screens/terminal.ts +0 -121
  115. package/node_modules/pi-footer/src/ui/screens/widget-list.ts +0 -154
  116. package/node_modules/pi-footer/src/ui/terminal-menu.ts +0 -32
  117. package/node_modules/pi-footer/src/ui/theme.ts +0 -48
  118. package/node_modules/pi-footer/src/ui/title-bar.ts +0 -100
  119. package/node_modules/pi-footer/src/ui/widget-actions.ts +0 -76
  120. package/node_modules/pi-footer/src/ui.ts +0 -66
  121. package/node_modules/pi-footer/src/widgets/context.ts +0 -28
  122. package/node_modules/pi-footer/src/widgets/core/active-tools.ts +0 -17
  123. package/node_modules/pi-footer/src/widgets/core/context-window.ts +0 -24
  124. package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +0 -19
  125. package/node_modules/pi-footer/src/widgets/core/cwd.ts +0 -108
  126. package/node_modules/pi-footer/src/widgets/core/event.ts +0 -34
  127. package/node_modules/pi-footer/src/widgets/core/external-status.ts +0 -102
  128. package/node_modules/pi-footer/src/widgets/core/model-provider.ts +0 -20
  129. package/node_modules/pi-footer/src/widgets/core/model.ts +0 -35
  130. package/node_modules/pi-footer/src/widgets/core/provider.ts +0 -17
  131. package/node_modules/pi-footer/src/widgets/core/session-name.ts +0 -17
  132. package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +0 -17
  133. package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +0 -17
  134. package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +0 -17
  135. package/node_modules/pi-footer/src/widgets/git/branch.ts +0 -81
  136. package/node_modules/pi-footer/src/widgets/git/clean.ts +0 -19
  137. package/node_modules/pi-footer/src/widgets/git/deletions.ts +0 -17
  138. package/node_modules/pi-footer/src/widgets/git/diff.ts +0 -38
  139. package/node_modules/pi-footer/src/widgets/git/insertions.ts +0 -17
  140. package/node_modules/pi-footer/src/widgets/git/remote.ts +0 -17
  141. package/node_modules/pi-footer/src/widgets/git/root.ts +0 -17
  142. package/node_modules/pi-footer/src/widgets/git/sha.ts +0 -17
  143. package/node_modules/pi-footer/src/widgets/git/staged.ts +0 -17
  144. package/node_modules/pi-footer/src/widgets/git/status.ts +0 -19
  145. package/node_modules/pi-footer/src/widgets/git/unstaged.ts +0 -17
  146. package/node_modules/pi-footer/src/widgets/git/untracked.ts +0 -17
  147. package/node_modules/pi-footer/src/widgets/instance.ts +0 -118
  148. package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +0 -18
  149. package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +0 -32
  150. package/node_modules/pi-footer/src/widgets/layout/separator.ts +0 -57
  151. package/node_modules/pi-footer/src/widgets/layout/spacer.ts +0 -31
  152. package/node_modules/pi-footer/src/widgets/options.ts +0 -122
  153. package/node_modules/pi-footer/src/widgets/project/runtime.ts +0 -361
  154. package/node_modules/pi-footer/src/widgets/registry.ts +0 -223
  155. package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +0 -17
  156. package/node_modules/pi-footer/src/widgets/session/compactions.ts +0 -17
  157. package/node_modules/pi-footer/src/widgets/session/elapsed.ts +0 -18
  158. package/node_modules/pi-footer/src/widgets/session/last-activity.ts +0 -18
  159. package/node_modules/pi-footer/src/widgets/session/messages.ts +0 -19
  160. package/node_modules/pi-footer/src/widgets/session/session-id.ts +0 -17
  161. package/node_modules/pi-footer/src/widgets/session/session-start.ts +0 -18
  162. package/node_modules/pi-footer/src/widgets/session/tool-results.ts +0 -17
  163. package/node_modules/pi-footer/src/widgets/session/total-messages.ts +0 -19
  164. package/node_modules/pi-footer/src/widgets/session/total-time.ts +0 -18
  165. package/node_modules/pi-footer/src/widgets/session/user-messages.ts +0 -17
  166. package/node_modules/pi-footer/src/widgets/store.ts +0 -26
  167. package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +0 -55
  168. package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +0 -18
  169. package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +0 -18
  170. package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +0 -108
  171. package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +0 -26
  172. package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +0 -29
  173. package/node_modules/pi-footer/src/widgets/tokens/context.ts +0 -29
  174. package/node_modules/pi-footer/src/widgets/tokens/cost.ts +0 -51
  175. package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +0 -25
  176. package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +0 -18
  177. package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +0 -25
  178. package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +0 -18
  179. package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +0 -20
  180. package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +0 -25
  181. package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +0 -18
  182. package/node_modules/pi-footer/src/widgets/types.ts +0 -181
  183. package/node_modules/pi-footer/src/widgets/utils/colors.ts +0 -11
  184. package/node_modules/pi-footer/src/widgets/utils/context.ts +0 -112
  185. package/node_modules/pi-footer/src/widgets/utils/session.ts +0 -19
  186. package/node_modules/pi-footer/src/widgets/utils/token-format.ts +0 -78
@@ -7,9 +7,24 @@
7
7
  import { getSettingsListTheme } from "@earendil-works/pi-coding-agent";
8
8
  import { Input, SettingsList, matchesKey, truncateToWidth } from "@earendil-works/pi-tui";
9
9
  import type { CompactThinkingController } from "../feature/compact-thinking.ts";
10
- import { applyStartupHeader } from "../feature/shell/startup-header.ts";
10
+ import {
11
+ applyCustomFooter,
12
+ clearCustomFooter,
13
+ getFooterStatusSnapshot,
14
+ } from "../feature/shell/footer.ts";
15
+ import {
16
+ footerChipDescription,
17
+ footerLineOfKey,
18
+ formatFooterChipSummary,
19
+ orderedFooterKeys,
20
+ reorderFooterKey,
21
+ resolveFooterChipLayout,
22
+ shiftFooterKeyLine,
23
+ toggleFooterKeyHidden,
24
+ type FooterChipLayout,
25
+ } from "../feature/shell/footer-layout.ts";
26
+ import { applyStartupHeader, clearStartupHeader } from "../feature/shell/startup-header.ts";
11
27
  import type { ToolGroupingHooks } from "../renderer/tool/grouping.ts";
12
- import type { DiffIndicatorMode, DiffViewMode } from "../renderer/tool/diff/index.ts";
13
28
  import {
14
29
  config,
15
30
  DEFAULT_CONFIG,
@@ -23,14 +38,19 @@ import {
23
38
  EXPANDED_PREVIEW_MAX_LINES_VALUES,
24
39
  formatExcludeRenderers,
25
40
  getCompactThinkingConfig,
41
+ pickInputClip,
26
42
  pickPositiveInt,
27
43
  pickPositiveNumber,
28
- saveConfig,
29
44
  SCROLL_STEP_LINES_VALUES,
30
45
  THINKING_ANIMATION_INTERVAL_VALUES,
31
46
  THINKING_PREVIEW_LINES_VALUES,
47
+ INPUT_CLIP_VALUES,
32
48
  WRITE_DIFF_COLLAPSED_LINES_VALUES,
49
+ updateConfig,
33
50
  type CompactStyleMode,
51
+ type Config,
52
+ type DiffIndicatorMode,
53
+ type DiffViewMode,
34
54
  } from "./config.ts";
35
55
 
36
56
  /** renderer 注入的渲染副作用,面板自身不触碰渲染状态。 */
@@ -41,7 +61,7 @@ export type CcstylePanelHooks = {
41
61
 
42
62
  function modeSettingDescription(mode: CompactStyleMode): string {
43
63
  if (mode === "compact") {
44
- return "(Experimental) One summary line per assistant round; edit/write stay single-line until expanded.";
64
+ return "(Experimental) One summary line per assistant round; edit/write reuse the same Diff preview settings as on.";
45
65
  }
46
66
  if (mode === "off") {
47
67
  return "Pi native tool rendering. Diff options below still apply independently.";
@@ -55,6 +75,120 @@ function excludeRenderersDescription(names: readonly string[]): string {
55
75
  : `Native renderer for: ${names.join(", ")}. Agent is always native. Enter to toggle.`;
56
76
  }
57
77
 
78
+ function customFooterDescription(enabled: boolean): string {
79
+ if (!enabled) return "Pi native footer restored. Chip layout below still applies when turned on.";
80
+ return "Custom status bar with model, context, cache, cost, git, and plugin chips.";
81
+ }
82
+
83
+ function pluginChipsDescription(): string {
84
+ return `Arrange plugin chips: ${formatFooterChipSummary(config)}. Enter to edit.`;
85
+ }
86
+
87
+ function footerLayoutFromConfig(): FooterChipLayout {
88
+ return {
89
+ footerHiddenKeys: config.footerHiddenKeys,
90
+ footerLine1Keys: config.footerLine1Keys,
91
+ footerLine2Keys: config.footerLine2Keys,
92
+ footerLine3Keys: config.footerLine3Keys,
93
+ };
94
+ }
95
+
96
+ function persistFooterLayout(layout: FooterChipLayout): void {
97
+ updateConfig({
98
+ footerHiddenKeys: layout.footerHiddenKeys,
99
+ footerLine1Keys: layout.footerLine1Keys,
100
+ footerLine2Keys: layout.footerLine2Keys,
101
+ footerLine3Keys: layout.footerLine3Keys,
102
+ });
103
+ }
104
+
105
+ function footerChipItem(
106
+ key: string,
107
+ layout: FooterChipLayout,
108
+ snapshot: ReadonlyMap<string, string>,
109
+ ) {
110
+ const line = footerLineOfKey(layout, key) ?? 2;
111
+ const hidden = layout.footerHiddenKeys.includes(key);
112
+ const live = snapshot.get(key)?.trim() ?? "";
113
+ return {
114
+ id: key,
115
+ label: key,
116
+ description: footerChipDescription(key, live),
117
+ currentValue: hidden ? `line${line} · hidden` : `line${line}`,
118
+ };
119
+ }
120
+
121
+ function buildFooterChipsSubmenu(
122
+ onClose: () => void,
123
+ onLiveChange: () => void,
124
+ ): {
125
+ render: (width: number) => string[];
126
+ invalidate: () => void;
127
+ handleInput: (data: string) => void;
128
+ } {
129
+ const snapshot = () => getFooterStatusSnapshot();
130
+ const resolved = () => resolveFooterChipLayout(footerLayoutFromConfig(), [...snapshot().keys()]);
131
+ const items: ReturnType<typeof footerChipItem>[] = [];
132
+ const rebuildItems = (layout: FooterChipLayout, selectedKey?: string) => {
133
+ const next = orderedFooterKeys(layout).map((key) => footerChipItem(key, layout, snapshot()));
134
+ items.length = 0;
135
+ items.push(...next);
136
+ if (!selectedKey) return 0;
137
+ const index = items.findIndex((item) => item.id === selectedKey);
138
+ return index >= 0 ? index : 0;
139
+ };
140
+ rebuildItems(resolved());
141
+ const list = new SettingsList(
142
+ items,
143
+ Math.min(8, Math.max(4, items.length)),
144
+ getSettingsListTheme(),
145
+ () => {},
146
+ () => onClose(),
147
+ { enableSearch: false },
148
+ );
149
+ const listState = list as unknown as { selectedIndex: number; items: typeof items };
150
+
151
+ const mutate = (transform: (layout: FooterChipLayout, key: string) => FooterChipLayout) => {
152
+ const key = listState.items[listState.selectedIndex]?.id;
153
+ if (!key) return;
154
+ const next = transform(resolved(), key);
155
+ persistFooterLayout(next);
156
+ listState.selectedIndex = rebuildItems(
157
+ resolveFooterChipLayout(next, [...snapshot().keys()]),
158
+ key,
159
+ );
160
+ onLiveChange();
161
+ };
162
+
163
+ return {
164
+ render: (width: number) => list.render(width),
165
+ invalidate: () => list.invalidate(),
166
+ handleInput: (data: string) => {
167
+ if (matchesKey(data, "left")) {
168
+ mutate((layout, key) => shiftFooterKeyLine(layout, key, -1));
169
+ return;
170
+ }
171
+ if (matchesKey(data, "right")) {
172
+ mutate((layout, key) => shiftFooterKeyLine(layout, key, 1));
173
+ return;
174
+ }
175
+ if (data === "[") {
176
+ mutate((layout, key) => reorderFooterKey(layout, key, -1));
177
+ return;
178
+ }
179
+ if (data === "]") {
180
+ mutate((layout, key) => reorderFooterKey(layout, key, 1));
181
+ return;
182
+ }
183
+ if (data === " " || matchesKey(data, "space")) {
184
+ mutate((layout, key) => toggleFooterKeyHidden(layout, key));
185
+ return;
186
+ }
187
+ list.handleInput(data);
188
+ },
189
+ };
190
+ }
191
+
58
192
  function diffViewModeDescription(mode: DiffViewMode): string {
59
193
  if (mode === "split") return "Force side-by-side diff when width allows; otherwise unified.";
60
194
  if (mode === "unified") return "Always render a single unified diff column.";
@@ -67,6 +201,7 @@ function diffIndicatorDescription(mode: DiffIndicatorMode): string {
67
201
  return "Vertical bar indicators on changed lines (default).";
68
202
  }
69
203
 
204
+ /** 额外功能开关项:on/off 二值,描述随状态切换;切换后需重启生效。 */
70
205
  function featureToggleSetting(
71
206
  id: string,
72
207
  label: string,
@@ -119,8 +254,7 @@ function buildExcludeRenderersSubmenu(
119
254
  const excluded = new Set(config.excludeRenderers);
120
255
  if (value === "exclude") excluded.add(id);
121
256
  else excluded.delete(id);
122
- config.excludeRenderers = [...excluded].sort((a, b) => a.localeCompare(b));
123
- saveConfig();
257
+ updateConfig({ excludeRenderers: [...excluded].sort((a, b) => a.localeCompare(b)) });
124
258
  onLiveChange();
125
259
  },
126
260
  () => onClose(),
@@ -182,7 +316,7 @@ function buildNumberInputSubmenu(
182
316
 
183
317
  /** Section tabs for /ccstyle — matches Zentui-style "A / B / C" headers. */
184
318
  type CcstyleSection = {
185
- id: "style" | "diff" | "thinking" | "ui" | "feature";
319
+ id: "style" | "diff" | "thinking" | "ui" | "feature" | "footer";
186
320
  label: string;
187
321
  items: any[];
188
322
  };
@@ -238,19 +372,22 @@ export async function showCcstylePanel(
238
372
  currentValue: config.mode === "compact" ? "compact (Experimental)" : config.mode,
239
373
  values: ["on", "compact (Experimental)", "off"],
240
374
  };
241
- // Tracks whether the Exclude-tools submenu is open so Tab switches sections
375
+ // Tracks whether a nested submenu is open so Tab switches sections
242
376
  // only at the top level (mirrors Zentui settings: Tab = switch sections).
243
- let excludeSubmenuOpen = false;
377
+ let nestedSubmenuOpen = false;
378
+ let nestedHint = "";
244
379
  const excludeSetting = {
245
380
  id: "excludeRenderers",
246
381
  label: "Exclude tools",
247
382
  description: excludeRenderersDescription(config.excludeRenderers),
248
383
  currentValue: formatExcludeRenderers(config.excludeRenderers),
249
384
  submenu: (_current: string, closeSubmenu: (selected?: string) => void) => {
250
- excludeSubmenuOpen = true;
385
+ nestedSubmenuOpen = true;
386
+ nestedHint = " Enter/Space to toggle · Esc back to Style";
251
387
  return buildExcludeRenderersSubmenu(
252
388
  () => {
253
- excludeSubmenuOpen = false;
389
+ nestedSubmenuOpen = false;
390
+ nestedHint = "";
254
391
  excludeSetting.currentValue = formatExcludeRenderers(config.excludeRenderers);
255
392
  excludeSetting.description = excludeRenderersDescription(config.excludeRenderers);
256
393
  closeSubmenu();
@@ -263,6 +400,16 @@ export async function showCcstylePanel(
263
400
  );
264
401
  },
265
402
  };
403
+ const inputClipSetting = {
404
+ id: "inputClip",
405
+ label: "Input clip",
406
+ description:
407
+ "Max characters for path/command/name in single and grouped tool summaries; 0 fits available width. Enter to type a custom value.",
408
+ currentValue: String(config.inputClip),
409
+ values: [...INPUT_CLIP_VALUES],
410
+ submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
411
+ buildNumberInputSubmenu(theme, inputClipSetting, closeSubmenu),
412
+ };
266
413
  const diffViewSetting = {
267
414
  id: "diffViewMode",
268
415
  label: "Diff layout",
@@ -301,7 +448,7 @@ export async function showCcstylePanel(
301
448
  id: "writeDiffCollapsedLines",
302
449
  label: "Write collapsed lines",
303
450
  description:
304
- "Write-only collapsed body lines. 0 shows ↳ created + expand hint. Enter to type a custom value.",
451
+ "Write-only collapsed body lines. 0 shows ↳ created + expand hint (stats stay on the title). Enter to type a custom value.",
305
452
  currentValue: String(config.writeDiffCollapsedLines),
306
453
  values: [...WRITE_DIFF_COLLAPSED_LINES_VALUES],
307
454
  submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
@@ -316,20 +463,11 @@ export async function showCcstylePanel(
316
463
  currentValue: config.diffWordWrap ? "on" : "off",
317
464
  values: ["on", "off"],
318
465
  };
319
- const expandedMaxSetting = {
320
- id: "expandedPreviewMaxLines",
321
- label: "Expanded max lines",
322
- description:
323
- "Max Output/diff body lines when expanded. Default 40 keeps the TUI compact; raise for large dumps.",
324
- currentValue: String(config.expandedPreviewMaxLines),
325
- values: [...EXPANDED_PREVIEW_MAX_LINES_VALUES],
326
- submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
327
- buildNumberInputSubmenu(theme, expandedMaxSetting, closeSubmenu),
328
- };
329
466
  const expandedInputSetting = {
330
467
  id: "expandedInputMaxLines",
331
- label: "Expanded Input max lines",
332
- description: "Max Input body lines before the show-more footer appears.",
468
+ label: "Expanded input lines",
469
+ description:
470
+ "Max Input section lines in an expanded tool card. Overflow shows click to show more. Default 5.",
333
471
  currentValue: String(config.expandedInputMaxLines),
334
472
  values: [...EXPANDED_INPUT_MAX_LINES_VALUES],
335
473
  submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
@@ -337,13 +475,23 @@ export async function showCcstylePanel(
337
475
  };
338
476
  const expandedOutputSetting = {
339
477
  id: "expandedOutputMaxLines",
340
- label: "Expanded Output max lines",
341
- description: "Max Output body lines before the show-more footer appears.",
478
+ label: "Expanded output lines",
479
+ description:
480
+ "Max Output section lines in an expanded tool card. Overflow shows click to show more. Default 10.",
342
481
  currentValue: String(config.expandedOutputMaxLines),
343
482
  values: [...EXPANDED_OUTPUT_MAX_LINES_VALUES],
344
483
  submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
345
484
  buildNumberInputSubmenu(theme, expandedOutputSetting, closeSubmenu),
346
485
  };
486
+ const expandedMaxSetting = {
487
+ id: "expandedPreviewMaxLines",
488
+ label: "Expanded max lines",
489
+ description: "Max TaskList body lines when expanded. Expanded diffs show every line.",
490
+ currentValue: String(config.expandedPreviewMaxLines),
491
+ values: [...EXPANDED_PREVIEW_MAX_LINES_VALUES],
492
+ submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
493
+ buildNumberInputSubmenu(theme, expandedMaxSetting, closeSubmenu),
494
+ };
347
495
  const thinkingTitleSetting = {
348
496
  id: "useSummaryTitlesAsThinkingTitle",
349
497
  label: "Summary title",
@@ -396,48 +544,89 @@ export async function showCcstylePanel(
396
544
  submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
397
545
  buildNumberInputSubmenu(theme, scrollStepSetting, closeSubmenu),
398
546
  };
547
+
548
+ // 额外功能开关:注册于扩展加载期,切换后需重启(/reload)生效。
399
549
  const sessionReferenceToggle = featureToggleSetting(
400
550
  "enableSessionReference",
401
551
  "Session reference",
402
- "@ session mentions search and inject referenced context after restart.",
552
+ "@ session mentions search & inject referenced session context. Next restart applies.",
403
553
  "Session reference disabled.",
404
554
  config.enableSessionReference,
405
555
  );
406
556
  const subagentAutocompleteToggle = featureToggleSetting(
407
557
  "enableSubagentAutocomplete",
408
558
  "Subagent autocomplete",
409
- "@ subagent mentions suggest agents and delegation instructions after restart.",
559
+ "@subagent:[name] mentions suggest agents and inject delegation instructions. Next restart applies.",
410
560
  "Subagent autocomplete disabled.",
411
561
  config.enableSubagentAutocomplete,
412
562
  );
413
563
  const contextCommandToggle = featureToggleSetting(
414
564
  "enableContextCommand",
415
565
  "Context usage",
416
- "/context shows context-window distribution after restart.",
566
+ "/context shows context-window distribution with previews. Next restart applies.",
417
567
  "Context command disabled.",
418
568
  config.enableContextCommand,
419
569
  );
420
570
  const agentSummaryToggle = featureToggleSetting(
421
571
  "enableAgentSummary",
422
572
  "Agent summary",
423
- "Append per-round tool stats after each agent turn after restart.",
573
+ "Append per-round tool stats after each agent turn. Next restart applies.",
424
574
  "Agent summary disabled.",
425
575
  config.enableAgentSummary,
426
576
  );
427
577
  const workingMessageToggle = featureToggleSetting(
428
578
  "enableWorkingMessage",
429
579
  "Working message",
430
- "Extend Working... with token count and elapsed time after restart.",
580
+ "Extend Working... footer with token count and elapsed time. Next restart applies.",
431
581
  "Native Working... footer only.",
432
582
  config.enableWorkingMessage,
433
583
  );
434
584
  const aliasesToggle = featureToggleSetting(
435
585
  "enableAliases",
436
586
  "Aliases",
437
- "/clear and /exit aliases enabled after restart.",
587
+ "/clear and /exit aliases enabled. Next restart applies.",
438
588
  "Aliases disabled.",
439
589
  config.enableAliases,
440
590
  );
591
+ const footerNerdIconsSetting = {
592
+ id: "footerNerdIcons",
593
+ label: "Nerd Font icons",
594
+ description: config.footerNerdIcons
595
+ ? "Git and cache chips use Nerd Font glyphs. Turn off for plain text."
596
+ : "Git and cache chips use plain text. No Nerd Font required.",
597
+ currentValue: config.footerNerdIcons ? "on" : "off",
598
+ values: ["on", "off"],
599
+ };
600
+ const customFooterSetting = {
601
+ id: "enableCustomFooter",
602
+ label: "Status bar",
603
+ description: customFooterDescription(config.enableCustomFooter),
604
+ currentValue: config.enableCustomFooter ? "on" : "off",
605
+ values: ["on", "off"],
606
+ };
607
+ const pluginChipsSetting = {
608
+ id: "footerPluginChips",
609
+ label: "Plugin chips",
610
+ description: pluginChipsDescription(),
611
+ currentValue: formatFooterChipSummary(config),
612
+ submenu: (_current: string, closeSubmenu: (selected?: string) => void) => {
613
+ nestedSubmenuOpen = true;
614
+ nestedHint = " ←→ line · [ ] reorder · Space hide · Esc back to Footer";
615
+ return buildFooterChipsSubmenu(
616
+ () => {
617
+ nestedSubmenuOpen = false;
618
+ nestedHint = "";
619
+ pluginChipsSetting.currentValue = formatFooterChipSummary(config);
620
+ pluginChipsSetting.description = pluginChipsDescription();
621
+ closeSubmenu();
622
+ },
623
+ () => {
624
+ pluginChipsSetting.currentValue = formatFooterChipSummary(config);
625
+ pluginChipsSetting.description = pluginChipsDescription();
626
+ },
627
+ );
628
+ },
629
+ };
441
630
  const featureToggles: Record<string, { apply: (on: boolean) => void }> = {
442
631
  enableSessionReference: sessionReferenceToggle,
443
632
  enableSubagentAutocomplete: subagentAutocompleteToggle,
@@ -448,16 +637,41 @@ export async function showCcstylePanel(
448
637
  };
449
638
 
450
639
  const onSettingChange = (id: string, value: string) => {
640
+ if (id === "enableCustomFooter") {
641
+ const enabled = value === "on";
642
+ updateConfig({ enableCustomFooter: enabled });
643
+ customFooterSetting.currentValue = enabled ? "on" : "off";
644
+ customFooterSetting.description = customFooterDescription(enabled);
645
+ if (enabled) applyCustomFooter(ctx);
646
+ else clearCustomFooter(ctx);
647
+ ctx.ui.notify(`Updated ${id}: ${value}`, "info");
648
+ return;
649
+ }
650
+ if (id === "footerNerdIcons") {
651
+ const enabled = value === "on";
652
+ updateConfig({ footerNerdIcons: enabled });
653
+ footerNerdIconsSetting.currentValue = enabled ? "on" : "off";
654
+ footerNerdIconsSetting.description = enabled
655
+ ? "Git and cache chips use Nerd Font glyphs. Turn off for plain text."
656
+ : "Git and cache chips use plain text. No Nerd Font required.";
657
+ ctx.ui.notify(`Updated ${id}: ${value}`, "info");
658
+ return;
659
+ }
660
+ // 额外功能开关:字段名与配置布尔字段一一对应,切换后重启生效。
451
661
  const featureToggle = featureToggles[id];
452
662
  if (featureToggle) {
453
- // SAFETY: featureToggles contains only boolean keys from Config.
454
- (config as unknown as Record<string, boolean>)[id] = value === "on";
663
+ updateConfig({ [id]: value === "on" } as Partial<Config>);
455
664
  featureToggle.apply(value === "on");
456
- saveConfig();
457
665
  ctx.ui.notify(`Updated ${id}: ${value} (next restart)`, "info");
458
666
  return;
459
667
  }
460
668
  switch (id) {
669
+ case "inputClip":
670
+ updateConfig({
671
+ inputClip: pickInputClip(value),
672
+ });
673
+ inputClipSetting.currentValue = String(config.inputClip);
674
+ break;
461
675
  case "mode": {
462
676
  // 选项值带 Experimental 标记,选择后还原为真实 mode 值。
463
677
  const mode: CompactStyleMode =
@@ -471,109 +685,118 @@ export async function showCcstylePanel(
471
685
  excludeSetting.description = excludeRenderersDescription(config.excludeRenderers);
472
686
  return;
473
687
  case "diffViewMode":
474
- config.diffViewMode = value as DiffViewMode;
688
+ updateConfig({ diffViewMode: value as DiffViewMode });
475
689
  diffViewSetting.description = diffViewModeDescription(config.diffViewMode);
476
690
  break;
477
691
  case "diffIndicatorMode":
478
- config.diffIndicatorMode = value as DiffIndicatorMode;
692
+ updateConfig({ diffIndicatorMode: value as DiffIndicatorMode });
479
693
  diffIndicatorSetting.description = diffIndicatorDescription(config.diffIndicatorMode);
480
694
  break;
481
695
  case "diffSplitMinWidth":
482
- config.diffSplitMinWidth = pickPositiveInt(
483
- value,
484
- DEFAULT_CONFIG.diffSplitMinWidth,
485
- 40,
486
- 300,
487
- );
696
+ updateConfig({
697
+ diffSplitMinWidth: pickPositiveInt(value, DEFAULT_CONFIG.diffSplitMinWidth, 40, 300),
698
+ });
488
699
  diffSplitSetting.currentValue = String(config.diffSplitMinWidth);
489
700
  break;
490
701
  case "editDiffCollapsedLines":
491
- config.editDiffCollapsedLines = pickPositiveInt(
492
- value,
493
- DEFAULT_CONFIG.editDiffCollapsedLines,
494
- 1,
495
- 500,
496
- );
702
+ updateConfig({
703
+ editDiffCollapsedLines: pickPositiveInt(
704
+ value,
705
+ DEFAULT_CONFIG.editDiffCollapsedLines,
706
+ 1,
707
+ 500,
708
+ ),
709
+ });
497
710
  diffCollapsedSetting.currentValue = String(config.editDiffCollapsedLines);
498
711
  break;
499
712
  case "writeDiffCollapsedLines":
500
- config.writeDiffCollapsedLines = pickPositiveInt(
501
- value,
502
- DEFAULT_CONFIG.writeDiffCollapsedLines,
503
- 0,
504
- 500,
505
- );
713
+ updateConfig({
714
+ writeDiffCollapsedLines: pickPositiveInt(
715
+ value,
716
+ DEFAULT_CONFIG.writeDiffCollapsedLines,
717
+ 0,
718
+ 500,
719
+ ),
720
+ });
506
721
  writeDiffCollapsedSetting.currentValue = String(config.writeDiffCollapsedLines);
507
722
  break;
508
723
  case "diffWordWrap":
509
- config.diffWordWrap = value === "on";
724
+ updateConfig({ diffWordWrap: value === "on" });
510
725
  diffWordWrapSetting.description = config.diffWordWrap
511
726
  ? "Long diff lines wrap within the panel width."
512
727
  : "Long diff lines are truncated to the panel width.";
513
728
  break;
514
- case "expandedPreviewMaxLines":
515
- config.expandedPreviewMaxLines = pickPositiveInt(
516
- value,
517
- DEFAULT_CONFIG.expandedPreviewMaxLines,
518
- 10,
519
- 50_000,
520
- );
521
- expandedMaxSetting.currentValue = String(config.expandedPreviewMaxLines);
522
- break;
523
729
  case "expandedInputMaxLines":
524
- config.expandedInputMaxLines = pickPositiveInt(
525
- value,
526
- DEFAULT_CONFIG.expandedInputMaxLines,
527
- 1,
528
- 5_000,
529
- );
730
+ updateConfig({
731
+ expandedInputMaxLines: pickPositiveInt(
732
+ value,
733
+ DEFAULT_CONFIG.expandedInputMaxLines,
734
+ 1,
735
+ 5_000,
736
+ ),
737
+ });
530
738
  expandedInputSetting.currentValue = String(config.expandedInputMaxLines);
531
739
  break;
532
740
  case "expandedOutputMaxLines":
533
- config.expandedOutputMaxLines = pickPositiveInt(
534
- value,
535
- DEFAULT_CONFIG.expandedOutputMaxLines,
536
- 1,
537
- 5_000,
538
- );
741
+ updateConfig({
742
+ expandedOutputMaxLines: pickPositiveInt(
743
+ value,
744
+ DEFAULT_CONFIG.expandedOutputMaxLines,
745
+ 1,
746
+ 5_000,
747
+ ),
748
+ });
539
749
  expandedOutputSetting.currentValue = String(config.expandedOutputMaxLines);
540
750
  break;
751
+ case "expandedPreviewMaxLines":
752
+ updateConfig({
753
+ expandedPreviewMaxLines: pickPositiveInt(
754
+ value,
755
+ DEFAULT_CONFIG.expandedPreviewMaxLines,
756
+ 10,
757
+ 50_000,
758
+ ),
759
+ });
760
+ expandedMaxSetting.currentValue = String(config.expandedPreviewMaxLines);
761
+ break;
541
762
  case "useSummaryTitlesAsThinkingTitle":
542
- config.useSummaryTitlesAsThinkingTitle = value === "on";
763
+ updateConfig({ useSummaryTitlesAsThinkingTitle: value === "on" });
543
764
  break;
544
765
  case "previewLines":
545
- config.previewLines = pickPositiveInt(value, DEFAULT_CONFIG.previewLines, 0);
766
+ updateConfig({ previewLines: pickPositiveInt(value, DEFAULT_CONFIG.previewLines, 0) });
546
767
  thinkingPreviewSetting.currentValue = String(config.previewLines);
547
768
  break;
548
769
  case "animationIntervalMs":
549
- config.animationIntervalMs = pickPositiveNumber(
550
- value,
551
- DEFAULT_CONFIG.animationIntervalMs,
552
- );
770
+ updateConfig({
771
+ animationIntervalMs: pickPositiveNumber(value, DEFAULT_CONFIG.animationIntervalMs),
772
+ });
553
773
  thinkingAnimationSetting.currentValue = String(config.animationIntervalMs);
554
774
  break;
555
775
  case "dimThinkingText":
556
- config.dimThinkingText = value === "on";
776
+ updateConfig({ dimThinkingText: value === "on" });
557
777
  thinkingDimSetting.description = config.dimThinkingText
558
778
  ? "Thinking text uses the theme's dim color."
559
779
  : "Keep the default thinking text color.";
560
780
  break;
561
- case "showStartupHeader":
562
- config.showStartupHeader = value === "on";
781
+ case "showStartupHeader": {
782
+ const enabled = value === "on";
783
+ updateConfig({ showStartupHeader: enabled });
563
784
  startupHeaderSetting.description = config.showStartupHeader
564
785
  ? "Show the custom startup header (logo + tips) on new sessions."
565
786
  : "Use Pi's native startup header instead.";
566
- // 实时切换:on → 自定义 header;off → 官方默认 header。
567
- applyStartupHeader(ctx);
787
+ if (enabled) applyStartupHeader(ctx);
788
+ else clearStartupHeader(ctx);
568
789
  break;
790
+ }
569
791
  case "scrollStepLines":
570
- config.scrollStepLines = pickPositiveInt(value, DEFAULT_CONFIG.scrollStepLines, 1, 50);
792
+ updateConfig({
793
+ scrollStepLines: pickPositiveInt(value, DEFAULT_CONFIG.scrollStepLines, 1, 50),
794
+ });
571
795
  scrollStepSetting.currentValue = String(config.scrollStepLines);
572
796
  break;
573
797
  default:
574
798
  return;
575
799
  }
576
- saveConfig();
577
800
  compactThinking?.updateConfig(getCompactThinkingConfig());
578
801
  hooks.refreshCurrentTranscript(ctx);
579
802
  ctx.ui.notify(`Updated ${id}: ${value}`, "info");
@@ -585,6 +808,30 @@ export async function showCcstylePanel(
585
808
  label: "Style",
586
809
  items: [modeSetting, excludeSetting],
587
810
  },
811
+ {
812
+ id: "feature",
813
+ label: "Features",
814
+ items: [
815
+ sessionReferenceToggle.setting,
816
+ subagentAutocompleteToggle.setting,
817
+ contextCommandToggle.setting,
818
+ agentSummaryToggle.setting,
819
+ workingMessageToggle.setting,
820
+ aliasesToggle.setting,
821
+ ],
822
+ },
823
+ {
824
+ id: "ui",
825
+ label: "UI",
826
+ items: [
827
+ expandedInputSetting,
828
+ expandedOutputSetting,
829
+ expandedMaxSetting,
830
+ inputClipSetting,
831
+ startupHeaderSetting,
832
+ scrollStepSetting,
833
+ ],
834
+ },
588
835
  {
589
836
  id: "diff",
590
837
  label: "Diff",
@@ -592,6 +839,7 @@ export async function showCcstylePanel(
592
839
  diffViewSetting,
593
840
  diffIndicatorSetting,
594
841
  diffSplitSetting,
842
+ diffCollapsedSetting,
595
843
  writeDiffCollapsedSetting,
596
844
  diffWordWrapSetting,
597
845
  ],
@@ -607,28 +855,9 @@ export async function showCcstylePanel(
607
855
  ],
608
856
  },
609
857
  {
610
- id: "ui",
611
- label: "UI",
612
- items: [
613
- expandedMaxSetting,
614
- expandedInputSetting,
615
- expandedOutputSetting,
616
- diffCollapsedSetting,
617
- startupHeaderSetting,
618
- scrollStepSetting,
619
- ],
620
- },
621
- {
622
- id: "feature",
623
- label: "Feature",
624
- items: [
625
- sessionReferenceToggle.setting,
626
- subagentAutocompleteToggle.setting,
627
- contextCommandToggle.setting,
628
- agentSummaryToggle.setting,
629
- workingMessageToggle.setting,
630
- aliasesToggle.setting,
631
- ],
858
+ id: "footer",
859
+ label: "Footer",
860
+ items: [customFooterSetting, footerNerdIconsSetting, pluginChipsSetting],
632
861
  },
633
862
  ];
634
863
 
@@ -649,13 +878,12 @@ export async function showCcstylePanel(
649
878
  const activeList = () => lists[activeSection]!;
650
879
 
651
880
  const switchSection = (delta: number) => {
652
- if (excludeSubmenuOpen) return;
881
+ if (nestedSubmenuOpen) return;
653
882
  activeSection = (activeSection + delta + sections.length) % sections.length;
654
883
  };
655
884
 
656
885
  /** 数值项:当前选中项有 submenu + values 时,Space 仅循环预设,不打开子面板。 */
657
886
  const cyclePresetInList = (list: InstanceType<typeof SettingsList>): boolean => {
658
- // SAFETY: SettingsList owns these stable fields used by its built-in input handler.
659
887
  const internal = list as unknown as {
660
888
  submenuComponent: unknown;
661
889
  items: {
@@ -700,7 +928,8 @@ export async function showCcstylePanel(
700
928
  truncateToWidth(
701
929
  theme.fg(
702
930
  "dim",
703
- " Enter/Space to change · Enter on numbers types a custom value · Tab/Shift+Tab to switch sections · Esc to close",
931
+ nestedHint ||
932
+ " Enter/Space to change · Enter on numbers types a custom value · Tab/Shift+Tab to switch sections · Esc to close",
704
933
  ),
705
934
  safeWidth,
706
935
  ),
@@ -711,12 +940,12 @@ export async function showCcstylePanel(
711
940
  for (const list of lists) list.invalidate();
712
941
  },
713
942
  handleInput(data: string) {
714
- if (!excludeSubmenuOpen && isForwardTabKey(data)) {
943
+ if (!nestedSubmenuOpen && isForwardTabKey(data)) {
715
944
  switchSection(1);
716
945
  tui.requestRender();
717
946
  return;
718
947
  }
719
- if (!excludeSubmenuOpen && isBackTabKey(data)) {
948
+ if (!nestedSubmenuOpen && isBackTabKey(data)) {
720
949
  switchSection(-1);
721
950
  tui.requestRender();
722
951
  return;