@herbertgao/pi-extensions 2026.9.18 → 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 (226) hide show
  1. package/README.md +20 -28
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +76 -59
  4. package/node_modules/@herbertgao/pi-cc-extensions/README.md +76 -59
  5. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/preview.webp +0 -0
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +93 -34
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +346 -117
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +6 -36
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/index.ts +2 -2
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -29
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +330 -360
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/index.ts +56 -108
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/session.ts +97 -15
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/subagent.ts +80 -45
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/flush-docked-bash.ts +36 -0
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer-layout.ts +229 -0
  17. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +479 -0
  18. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/startup-header.ts +92 -61
  19. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +75 -40
  20. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +19 -3
  21. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +1080 -229
  22. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +232 -259
  23. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +42 -41
  24. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +8 -2
  25. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +2 -7
  26. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +277 -113
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +15 -6
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +18 -72
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +61 -26
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/ansi-utils.ts +25 -57
  31. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-component.ts +125 -0
  32. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-edit-render.ts +227 -0
  33. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +136 -0
  34. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +115 -0
  35. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-inline.ts +324 -0
  36. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-layout.ts +762 -0
  37. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-limits.ts +152 -0
  38. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-palette.ts +410 -0
  39. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-parse.ts +535 -0
  40. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +2 -2
  41. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +21 -3219
  42. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-text.ts +44 -0
  43. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +568 -0
  44. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +47 -10
  45. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +2 -2
  46. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +1 -0
  47. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +145 -190
  48. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/mcp-title.ts +39 -0
  49. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{message-display.ts → tool/message-display.ts} +24 -90
  50. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +426 -0
  51. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +90 -132
  52. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{show-more-hint.ts → tool/show-more-hint.ts} +10 -0
  53. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +8 -26
  54. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-color.ts +44 -0
  55. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +76 -0
  56. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/component-tree.ts +34 -0
  57. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/format.ts +28 -0
  58. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +51 -0
  59. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/patch-keys.ts +124 -0
  60. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/sgr-mouse.ts +70 -0
  61. package/node_modules/@herbertgao/pi-cc-extensions/package.json +8 -8
  62. package/node_modules/pi-antigravity/README.md +29 -6
  63. package/node_modules/pi-antigravity/package.json +12 -7
  64. package/node_modules/pi-antigravity/src/auth/accounts.ts +327 -0
  65. package/node_modules/pi-antigravity/src/auth/index.ts +9 -0
  66. package/node_modules/pi-antigravity/src/client/client.ts +1 -1
  67. package/node_modules/pi-antigravity/src/image/image.ts +4 -4
  68. package/node_modules/pi-antigravity/src/index.ts +203 -13
  69. package/node_modules/pi-antigravity/src/search/index.ts +1 -0
  70. package/node_modules/pi-antigravity/src/search/search.ts +325 -0
  71. package/node_modules/pi-antigravity/src/stream/stream.ts +191 -14
  72. package/node_modules/pi-antigravity/src/types/enums.ts +45 -36
  73. package/node_modules/pi-antigravity/src/types/types.ts +5 -12
  74. package/node_modules/pi-antigravity/src/utils/http.ts +7 -2
  75. package/node_modules/pi-antigravity/src/utils/util.ts +31 -3
  76. package/node_modules/pi-typesafe/README.md +2 -0
  77. package/node_modules/pi-typesafe/package.json +2 -1
  78. package/node_modules/pi-web-access/CHANGELOG.md +72 -0
  79. package/node_modules/pi-web-access/README.md +34 -13
  80. package/node_modules/pi-web-access/chrome-cookies.ts +79 -25
  81. package/node_modules/pi-web-access/curator-page.ts +5 -1
  82. package/node_modules/pi-web-access/curator-server.ts +2 -0
  83. package/node_modules/pi-web-access/dist/index.js +1337 -1009
  84. package/node_modules/pi-web-access/exa.ts +20 -52
  85. package/node_modules/pi-web-access/extract.ts +41 -4
  86. package/node_modules/pi-web-access/gemini-search.ts +12 -4
  87. package/node_modules/pi-web-access/index.ts +29 -37
  88. package/node_modules/pi-web-access/openai-search.ts +35 -7
  89. package/node_modules/pi-web-access/package.json +6 -5
  90. package/node_modules/pi-web-access/page-query.ts +2 -2
  91. package/node_modules/pi-web-access/query-rewrite.ts +2 -3
  92. package/node_modules/pi-web-access/ssrf-protection.ts +27 -10
  93. package/node_modules/pi-web-access/storage.ts +48 -77
  94. package/node_modules/pi-web-access/summary-model-scope.ts +6 -77
  95. package/node_modules/pi-web-access/summary-review.ts +3 -4
  96. package/node_modules/pi-web-access/tavily.ts +67 -20
  97. package/node_modules/pi-web-access/tool-activation.ts +53 -33
  98. package/node_modules/pi-web-access/utils.ts +1 -1
  99. package/node_modules/pi-web-access/you.ts +164 -0
  100. package/node_modules/pi-web-access/zai.ts +161 -0
  101. package/package.json +7 -11
  102. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +0 -101
  103. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.svg +0 -101
  104. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/welcome.webp +0 -0
  105. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/render-utils.ts +0 -5
  106. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +0 -30
  107. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +0 -12
  108. package/node_modules/pi-footer/CHANGELOG.md +0 -145
  109. package/node_modules/pi-footer/LICENSE +0 -21
  110. package/node_modules/pi-footer/README.md +0 -354
  111. package/node_modules/pi-footer/package.json +0 -68
  112. package/node_modules/pi-footer/src/cache.ts +0 -106
  113. package/node_modules/pi-footer/src/colors.ts +0 -237
  114. package/node_modules/pi-footer/src/config.ts +0 -202
  115. package/node_modules/pi-footer/src/event-widgets.ts +0 -41
  116. package/node_modules/pi-footer/src/extension-statuses.ts +0 -102
  117. package/node_modules/pi-footer/src/git.ts +0 -135
  118. package/node_modules/pi-footer/src/index.ts +0 -268
  119. package/node_modules/pi-footer/src/metrics.ts +0 -141
  120. package/node_modules/pi-footer/src/presets.ts +0 -383
  121. package/node_modules/pi-footer/src/render.ts +0 -114
  122. package/node_modules/pi-footer/src/separators.ts +0 -52
  123. package/node_modules/pi-footer/src/types.ts +0 -122
  124. package/node_modules/pi-footer/src/ui/color-level-confirm.ts +0 -22
  125. package/node_modules/pi-footer/src/ui/color-options.ts +0 -195
  126. package/node_modules/pi-footer/src/ui/config-lifecycle.ts +0 -61
  127. package/node_modules/pi-footer/src/ui/edit-colors.ts +0 -13
  128. package/node_modules/pi-footer/src/ui/events.ts +0 -35
  129. package/node_modules/pi-footer/src/ui/extension-status-picker.ts +0 -60
  130. package/node_modules/pi-footer/src/ui/extension-statuses.ts +0 -73
  131. package/node_modules/pi-footer/src/ui/fields.ts +0 -252
  132. package/node_modules/pi-footer/src/ui/global-menu.ts +0 -126
  133. package/node_modules/pi-footer/src/ui/helpers.ts +0 -44
  134. package/node_modules/pi-footer/src/ui/layout.ts +0 -17
  135. package/node_modules/pi-footer/src/ui/line-list.ts +0 -19
  136. package/node_modules/pi-footer/src/ui/model.ts +0 -64
  137. package/node_modules/pi-footer/src/ui/navigation.ts +0 -29
  138. package/node_modules/pi-footer/src/ui/option-edit.ts +0 -93
  139. package/node_modules/pi-footer/src/ui/overlay-render.ts +0 -103
  140. package/node_modules/pi-footer/src/ui/screen-context.ts +0 -18
  141. package/node_modules/pi-footer/src/ui/screen-controller.ts +0 -27
  142. package/node_modules/pi-footer/src/ui/screen-render.ts +0 -32
  143. package/node_modules/pi-footer/src/ui/screen-state.ts +0 -20
  144. package/node_modules/pi-footer/src/ui/screen.ts +0 -243
  145. package/node_modules/pi-footer/src/ui/screens/add-widget.ts +0 -92
  146. package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +0 -52
  147. package/node_modules/pi-footer/src/ui/screens/controller.ts +0 -12
  148. package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +0 -68
  149. package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +0 -115
  150. package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +0 -61
  151. package/node_modules/pi-footer/src/ui/screens/global.ts +0 -75
  152. package/node_modules/pi-footer/src/ui/screens/line-list.ts +0 -108
  153. package/node_modules/pi-footer/src/ui/screens/main.ts +0 -99
  154. package/node_modules/pi-footer/src/ui/screens/terminal.ts +0 -121
  155. package/node_modules/pi-footer/src/ui/screens/widget-list.ts +0 -154
  156. package/node_modules/pi-footer/src/ui/terminal-menu.ts +0 -32
  157. package/node_modules/pi-footer/src/ui/theme.ts +0 -48
  158. package/node_modules/pi-footer/src/ui/title-bar.ts +0 -100
  159. package/node_modules/pi-footer/src/ui/widget-actions.ts +0 -76
  160. package/node_modules/pi-footer/src/ui.ts +0 -66
  161. package/node_modules/pi-footer/src/widgets/context.ts +0 -28
  162. package/node_modules/pi-footer/src/widgets/core/active-tools.ts +0 -17
  163. package/node_modules/pi-footer/src/widgets/core/context-window.ts +0 -24
  164. package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +0 -19
  165. package/node_modules/pi-footer/src/widgets/core/cwd.ts +0 -108
  166. package/node_modules/pi-footer/src/widgets/core/event.ts +0 -34
  167. package/node_modules/pi-footer/src/widgets/core/external-status.ts +0 -102
  168. package/node_modules/pi-footer/src/widgets/core/model-provider.ts +0 -20
  169. package/node_modules/pi-footer/src/widgets/core/model.ts +0 -35
  170. package/node_modules/pi-footer/src/widgets/core/provider.ts +0 -17
  171. package/node_modules/pi-footer/src/widgets/core/session-name.ts +0 -17
  172. package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +0 -17
  173. package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +0 -17
  174. package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +0 -17
  175. package/node_modules/pi-footer/src/widgets/git/branch.ts +0 -81
  176. package/node_modules/pi-footer/src/widgets/git/clean.ts +0 -19
  177. package/node_modules/pi-footer/src/widgets/git/deletions.ts +0 -17
  178. package/node_modules/pi-footer/src/widgets/git/diff.ts +0 -38
  179. package/node_modules/pi-footer/src/widgets/git/insertions.ts +0 -17
  180. package/node_modules/pi-footer/src/widgets/git/remote.ts +0 -17
  181. package/node_modules/pi-footer/src/widgets/git/root.ts +0 -17
  182. package/node_modules/pi-footer/src/widgets/git/sha.ts +0 -17
  183. package/node_modules/pi-footer/src/widgets/git/staged.ts +0 -17
  184. package/node_modules/pi-footer/src/widgets/git/status.ts +0 -19
  185. package/node_modules/pi-footer/src/widgets/git/unstaged.ts +0 -17
  186. package/node_modules/pi-footer/src/widgets/git/untracked.ts +0 -17
  187. package/node_modules/pi-footer/src/widgets/instance.ts +0 -118
  188. package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +0 -18
  189. package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +0 -32
  190. package/node_modules/pi-footer/src/widgets/layout/separator.ts +0 -57
  191. package/node_modules/pi-footer/src/widgets/layout/spacer.ts +0 -31
  192. package/node_modules/pi-footer/src/widgets/options.ts +0 -122
  193. package/node_modules/pi-footer/src/widgets/project/runtime.ts +0 -361
  194. package/node_modules/pi-footer/src/widgets/registry.ts +0 -223
  195. package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +0 -17
  196. package/node_modules/pi-footer/src/widgets/session/compactions.ts +0 -17
  197. package/node_modules/pi-footer/src/widgets/session/elapsed.ts +0 -18
  198. package/node_modules/pi-footer/src/widgets/session/last-activity.ts +0 -18
  199. package/node_modules/pi-footer/src/widgets/session/messages.ts +0 -19
  200. package/node_modules/pi-footer/src/widgets/session/session-id.ts +0 -17
  201. package/node_modules/pi-footer/src/widgets/session/session-start.ts +0 -18
  202. package/node_modules/pi-footer/src/widgets/session/tool-results.ts +0 -17
  203. package/node_modules/pi-footer/src/widgets/session/total-messages.ts +0 -19
  204. package/node_modules/pi-footer/src/widgets/session/total-time.ts +0 -18
  205. package/node_modules/pi-footer/src/widgets/session/user-messages.ts +0 -17
  206. package/node_modules/pi-footer/src/widgets/store.ts +0 -26
  207. package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +0 -55
  208. package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +0 -18
  209. package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +0 -18
  210. package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +0 -108
  211. package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +0 -26
  212. package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +0 -29
  213. package/node_modules/pi-footer/src/widgets/tokens/context.ts +0 -29
  214. package/node_modules/pi-footer/src/widgets/tokens/cost.ts +0 -51
  215. package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +0 -25
  216. package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +0 -18
  217. package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +0 -25
  218. package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +0 -18
  219. package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +0 -20
  220. package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +0 -25
  221. package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +0 -18
  222. package/node_modules/pi-footer/src/widgets/types.ts +0 -181
  223. package/node_modules/pi-footer/src/widgets/utils/colors.ts +0 -11
  224. package/node_modules/pi-footer/src/widgets/utils/context.ts +0 -112
  225. package/node_modules/pi-footer/src/widgets/utils/session.ts +0 -19
  226. package/node_modules/pi-footer/src/widgets/utils/token-format.ts +0 -78
@@ -0,0 +1,36 @@
1
+ /**
2
+ * fullscreen 下 ! / !! 在 agent 仍 streaming 时挂到 dock(pendingMessages)。
3
+ * 官方只在下次普通 submit 才 flushPendingBashComponents,命令结束后卡钉在输入框上方。
4
+ * 在 handleBashCommand 收尾补一次 flush。
5
+ */
6
+ import { InteractiveMode } from "@earendil-works/pi-coding-agent";
7
+ import { FLUSH_DOCKED_BASH_PATCH, patchRegistry } from "../../utils/patch-keys.ts";
8
+
9
+ type Patch = {
10
+ active: boolean;
11
+ prototype: any;
12
+ original: (...args: any[]) => unknown;
13
+ installed: (...args: any[]) => unknown;
14
+ };
15
+
16
+ export function installFlushDockedBash(): void {
17
+ const prototype = InteractiveMode.prototype as any;
18
+ const previous = patchRegistry.get<Patch>(FLUSH_DOCKED_BASH_PATCH);
19
+ if (previous) previous.active = false;
20
+ const original =
21
+ previous && prototype.handleBashCommand === previous.installed
22
+ ? previous.original
23
+ : prototype.handleBashCommand;
24
+ const patch: Patch = {
25
+ active: true,
26
+ prototype,
27
+ original,
28
+ installed: async function (this: any, ...args: any[]) {
29
+ const result = await original.apply(this, args);
30
+ if (patch.active) this.flushPendingBashComponents();
31
+ return result;
32
+ },
33
+ };
34
+ prototype.handleBashCommand = patch.installed;
35
+ patchRegistry.install(FLUSH_DOCKED_BASH_PATCH, patch);
36
+ }
@@ -0,0 +1,229 @@
1
+ /**
2
+ * 自定义 footer 插件芯片布局:行、顺序、隐藏。
3
+ * 纯函数,供 config 规范化、footer 渲染、配置面板共用。
4
+ */
5
+
6
+ export const PI_USAGE_KEY = "pi-usage";
7
+ export const PI_USAGE_SOURCE = "@narumitw/pi-usage";
8
+ const LEGACY_PI_USAGE_KEYS = new Set(["localUsage", "piUsage"]);
9
+ export const SKIP_FOOTER_STATUS_KEY = "model";
10
+
11
+ export type FooterLineId = 1 | 2 | 3;
12
+
13
+ export type FooterChipLayout = {
14
+ footerHiddenKeys: string[];
15
+ footerLine1Keys: string[];
16
+ footerLine2Keys: string[];
17
+ footerLine3Keys: string[];
18
+ };
19
+
20
+ export const DEFAULT_FOOTER_CHIP_LAYOUT: FooterChipLayout = {
21
+ footerHiddenKeys: [],
22
+ footerLine1Keys: [PI_USAGE_KEY],
23
+ footerLine2Keys: [],
24
+ footerLine3Keys: [],
25
+ };
26
+
27
+ export function isSkippedFooterStatusKey(key: string): boolean {
28
+ return key === SKIP_FOOTER_STATUS_KEY;
29
+ }
30
+
31
+ /** 未单独配置时的默认行:usage 类和本包用量在 line1,其余 line2。不会默认进 line3。 */
32
+ export function defaultFooterLine(key: string): FooterLineId {
33
+ if (key === PI_USAGE_KEY) return 1;
34
+ if (/usage|quota|balance/i.test(key)) return 1;
35
+ return 2;
36
+ }
37
+
38
+ export function normalizeFooterKeyList(value: unknown): string[] {
39
+ if (!Array.isArray(value)) return [];
40
+ const out: string[] = [];
41
+ const seen = new Set<string>();
42
+ for (const item of value) {
43
+ if (typeof item !== "string") continue;
44
+ let key = item.trim();
45
+ if (LEGACY_PI_USAGE_KEYS.has(key)) key = PI_USAGE_KEY;
46
+ if (!key || isSkippedFooterStatusKey(key) || seen.has(key)) continue;
47
+ seen.add(key);
48
+ out.push(key);
49
+ }
50
+ return out;
51
+ }
52
+
53
+ function takeUnique(keys: string[], seen: Set<string>): string[] {
54
+ const out: string[] = [];
55
+ for (const key of keys) {
56
+ if (seen.has(key)) continue;
57
+ seen.add(key);
58
+ out.push(key);
59
+ }
60
+ return out;
61
+ }
62
+
63
+ /** 跨行去重(line1 → line2 → line3);补上 pi-usage 槽位。 */
64
+ export function normalizeFooterChipLayout(source: Record<string, unknown>): FooterChipLayout {
65
+ const hasHidden = Array.isArray(source.footerHiddenKeys);
66
+ const hasAnyLine = [source.footerLine1Keys, source.footerLine2Keys, source.footerLine3Keys].some(
67
+ Array.isArray,
68
+ );
69
+ const seen = new Set<string>();
70
+ let footerLine1Keys = takeUnique(normalizeFooterKeyList(source.footerLine1Keys), seen);
71
+ const footerLine2Keys = takeUnique(normalizeFooterKeyList(source.footerLine2Keys), seen);
72
+ const footerLine3Keys = takeUnique(normalizeFooterKeyList(source.footerLine3Keys), seen);
73
+ const footerHiddenKeys = normalizeFooterKeyList(source.footerHiddenKeys);
74
+
75
+ if (!seen.has(PI_USAGE_KEY)) {
76
+ footerLine1Keys = [PI_USAGE_KEY, ...footerLine1Keys];
77
+ }
78
+ if (!hasHidden && !hasAnyLine) {
79
+ return { ...DEFAULT_FOOTER_CHIP_LAYOUT };
80
+ }
81
+ return { footerHiddenKeys, footerLine1Keys, footerLine2Keys, footerLine3Keys };
82
+ }
83
+
84
+ /** 面板描述用:pi-usage 数据来自 @narumitw/pi-usage,其它芯片用来源 setStatus key。 */
85
+ export function footerChipSource(key: string): string {
86
+ return key === PI_USAGE_KEY ? PI_USAGE_SOURCE : key;
87
+ }
88
+
89
+ export function footerChipDescription(key: string, liveText: string): string {
90
+ const source = footerChipSource(key);
91
+ const live = liveText.trim();
92
+ if (live) return `${live} · from ${source}`;
93
+ if (key === PI_USAGE_KEY) return `From ${source}. No text right now.`;
94
+ return `inactive · from ${source}`;
95
+ }
96
+
97
+ export function footerLineOfKey(layout: FooterChipLayout, key: string): FooterLineId | undefined {
98
+ if (layout.footerLine1Keys.includes(key)) return 1;
99
+ if (layout.footerLine2Keys.includes(key)) return 2;
100
+ if (layout.footerLine3Keys.includes(key)) return 3;
101
+ return undefined;
102
+ }
103
+
104
+ function keysOnLine(layout: FooterChipLayout, line: FooterLineId): string[] {
105
+ if (line === 1) return layout.footerLine1Keys;
106
+ if (line === 2) return layout.footerLine2Keys;
107
+ return layout.footerLine3Keys;
108
+ }
109
+
110
+ function withLineKeys(
111
+ layout: FooterChipLayout,
112
+ line: FooterLineId,
113
+ keys: string[],
114
+ ): FooterChipLayout {
115
+ if (line === 1) return { ...layout, footerLine1Keys: keys };
116
+ if (line === 2) return { ...layout, footerLine2Keys: keys };
117
+ return { ...layout, footerLine3Keys: keys };
118
+ }
119
+
120
+ /**
121
+ * 配置里还没有的 live key 接到默认行末尾(按 key 字母序,避免每次渲染乱序)。
122
+ * pi-usage 由 normalize 保证已在某一行。
123
+ */
124
+ export function resolveFooterChipLayout(
125
+ layout: FooterChipLayout,
126
+ liveKeys: readonly string[],
127
+ ): FooterChipLayout {
128
+ const known = new Set([
129
+ ...layout.footerLine1Keys,
130
+ ...layout.footerLine2Keys,
131
+ ...layout.footerLine3Keys,
132
+ ]);
133
+ const extras = [
134
+ ...new Set(liveKeys.filter((key) => key && !isSkippedFooterStatusKey(key) && !known.has(key))),
135
+ ].sort((a, b) => a.localeCompare(b));
136
+ const next: FooterChipLayout = {
137
+ footerHiddenKeys: [...layout.footerHiddenKeys],
138
+ footerLine1Keys: [...layout.footerLine1Keys],
139
+ footerLine2Keys: [...layout.footerLine2Keys],
140
+ footerLine3Keys: [...layout.footerLine3Keys],
141
+ };
142
+ for (const key of extras) {
143
+ if (defaultFooterLine(key) === 1) next.footerLine1Keys.push(key);
144
+ else next.footerLine2Keys.push(key);
145
+ }
146
+ return next;
147
+ }
148
+
149
+ export function moveFooterKeyToLine(
150
+ layout: FooterChipLayout,
151
+ key: string,
152
+ line: FooterLineId,
153
+ ): FooterChipLayout {
154
+ if (isSkippedFooterStatusKey(key)) return layout;
155
+ const current = footerLineOfKey(layout, key);
156
+ if (current === line) return layout;
157
+ const stripped: FooterChipLayout = {
158
+ footerHiddenKeys: [...layout.footerHiddenKeys],
159
+ footerLine1Keys: layout.footerLine1Keys.filter((item) => item !== key),
160
+ footerLine2Keys: layout.footerLine2Keys.filter((item) => item !== key),
161
+ footerLine3Keys: layout.footerLine3Keys.filter((item) => item !== key),
162
+ };
163
+ return withLineKeys(stripped, line, [...keysOnLine(stripped, line), key]);
164
+ }
165
+
166
+ export function shiftFooterKeyLine(
167
+ layout: FooterChipLayout,
168
+ key: string,
169
+ delta: -1 | 1,
170
+ ): FooterChipLayout {
171
+ const current = footerLineOfKey(layout, key);
172
+ if (!current) return layout;
173
+ const next = (current + delta) as number;
174
+ if (next < 1 || next > 3) return layout;
175
+ return moveFooterKeyToLine(layout, key, next as FooterLineId);
176
+ }
177
+
178
+ export function reorderFooterKey(
179
+ layout: FooterChipLayout,
180
+ key: string,
181
+ delta: -1 | 1,
182
+ ): FooterChipLayout {
183
+ const line = footerLineOfKey(layout, key);
184
+ if (!line) return layout;
185
+ const keys = [...keysOnLine(layout, line)];
186
+ const index = keys.indexOf(key);
187
+ const nextIndex = index + delta;
188
+ if (index < 0 || nextIndex < 0 || nextIndex >= keys.length) return layout;
189
+ const swap = keys[nextIndex]!;
190
+ keys[nextIndex] = key;
191
+ keys[index] = swap;
192
+ return withLineKeys(layout, line, keys);
193
+ }
194
+
195
+ export function toggleFooterKeyHidden(layout: FooterChipLayout, key: string): FooterChipLayout {
196
+ if (isSkippedFooterStatusKey(key)) return layout;
197
+ const hidden = new Set(layout.footerHiddenKeys);
198
+ if (hidden.has(key)) hidden.delete(key);
199
+ else hidden.add(key);
200
+ return { ...layout, footerHiddenKeys: [...hidden] };
201
+ }
202
+
203
+ /** 按行顺序收集可见、且当前有文案的插件芯片文本。 */
204
+ export function visibleFooterPluginTexts(
205
+ lineKeys: readonly string[],
206
+ hiddenKeys: readonly string[],
207
+ texts: ReadonlyMap<string, string>,
208
+ ): string[] {
209
+ const hidden = new Set(hiddenKeys);
210
+ const out: string[] = [];
211
+ for (const key of lineKeys) {
212
+ if (hidden.has(key)) continue;
213
+ const text = texts
214
+ .get(key)
215
+ ?.replace(/[\r\n\t]+/g, " ")
216
+ .trim();
217
+ if (text) out.push(text);
218
+ }
219
+ return out;
220
+ }
221
+
222
+ export function formatFooterChipSummary(layout: FooterChipLayout): string {
223
+ const hidden = layout.footerHiddenKeys.length;
224
+ return `hidden=${hidden} · L1=${layout.footerLine1Keys.length} · L2=${layout.footerLine2Keys.length} · L3=${layout.footerLine3Keys.length}`;
225
+ }
226
+
227
+ export function orderedFooterKeys(layout: FooterChipLayout): string[] {
228
+ return [...layout.footerLine1Keys, ...layout.footerLine2Keys, ...layout.footerLine3Keys];
229
+ }