pi-cc-extensions 0.8.31 → 0.8.32

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 (52) hide show
  1. package/README.en.md +164 -174
  2. package/README.md +113 -115
  3. package/extensions/claude-code-style.ts +314 -180
  4. package/extensions/compact-thinking.ts +58 -0
  5. package/extensions/fixed-editor.ts +185 -0
  6. package/extensions/index.ts +10 -4
  7. package/node_modules/@tifan/pi-fixed-editor/README.md +36 -0
  8. package/node_modules/@tifan/pi-fixed-editor/package.json +46 -0
  9. package/node_modules/@tifan/pi-fixed-editor/src/cluster.ts +113 -0
  10. package/node_modules/@tifan/pi-fixed-editor/src/index.ts +259 -0
  11. package/node_modules/@tifan/pi-fixed-editor/src/terminal-split.ts +1337 -0
  12. package/package.json +9 -5
  13. package/extensions/ask-user-question/LICENSE +0 -21
  14. package/extensions/ask-user-question/README.md +0 -63
  15. package/extensions/ask-user-question/ask-user-question.ts +0 -266
  16. package/extensions/ask-user-question/config.ts +0 -79
  17. package/extensions/ask-user-question/events.ts +0 -55
  18. package/extensions/ask-user-question/index.ts +0 -17
  19. package/extensions/ask-user-question/reconcile.ts +0 -47
  20. package/extensions/ask-user-question/rpc-fallback.ts +0 -172
  21. package/extensions/ask-user-question/state/build-questionnaire.ts +0 -314
  22. package/extensions/ask-user-question/state/dialog-height.ts +0 -16
  23. package/extensions/ask-user-question/state/key-router.ts +0 -291
  24. package/extensions/ask-user-question/state/questionnaire-session.ts +0 -193
  25. package/extensions/ask-user-question/state/row-intent.ts +0 -143
  26. package/extensions/ask-user-question/state/selectors/contract.ts +0 -24
  27. package/extensions/ask-user-question/state/selectors/derivations.ts +0 -40
  28. package/extensions/ask-user-question/state/selectors/focus.ts +0 -17
  29. package/extensions/ask-user-question/state/selectors/projections.ts +0 -102
  30. package/extensions/ask-user-question/state/state-reducer.ts +0 -299
  31. package/extensions/ask-user-question/state/state.ts +0 -49
  32. package/extensions/ask-user-question/tool/format-answer.ts +0 -29
  33. package/extensions/ask-user-question/tool/response-envelope.ts +0 -50
  34. package/extensions/ask-user-question/tool/types.ts +0 -149
  35. package/extensions/ask-user-question/tool/validate-questionnaire.ts +0 -64
  36. package/extensions/ask-user-question/view/component-binding.ts +0 -45
  37. package/extensions/ask-user-question/view/components/inline-input.ts +0 -109
  38. package/extensions/ask-user-question/view/components/multi-select-view.ts +0 -198
  39. package/extensions/ask-user-question/view/components/option-list-view.ts +0 -77
  40. package/extensions/ask-user-question/view/components/preview/markdown-content-cache.ts +0 -76
  41. package/extensions/ask-user-question/view/components/preview/preview-block-renderer.ts +0 -114
  42. package/extensions/ask-user-question/view/components/preview/preview-box-renderer.ts +0 -86
  43. package/extensions/ask-user-question/view/components/preview/preview-layout-decider.ts +0 -219
  44. package/extensions/ask-user-question/view/components/preview/preview-pane.ts +0 -230
  45. package/extensions/ask-user-question/view/components/submit-picker.ts +0 -66
  46. package/extensions/ask-user-question/view/components/tab-bar.ts +0 -57
  47. package/extensions/ask-user-question/view/components/wrapping-select.ts +0 -307
  48. package/extensions/ask-user-question/view/dialog-builder.ts +0 -225
  49. package/extensions/ask-user-question/view/props-adapter.ts +0 -123
  50. package/extensions/ask-user-question/view/stateful-view.ts +0 -24
  51. package/extensions/ask-user-question/view/tab-components.ts +0 -16
  52. package/extensions/ask-user-question/view/tab-content-strategy.ts +0 -264
@@ -0,0 +1,259 @@
1
+ import {
2
+ copyToClipboard,
3
+ type ExtensionAPI,
4
+ type ExtensionContext,
5
+ } from "@earendil-works/pi-coding-agent"
6
+ import type { Component } from "@earendil-works/pi-tui"
7
+ import { visibleWidth } from "@earendil-works/pi-tui"
8
+ import { renderFixedEditorCluster } from "./cluster.js"
9
+ import {
10
+ emergencyTerminalModeReset,
11
+ TerminalSplitCompositor,
12
+ type TerminalLike,
13
+ } from "./terminal-split.js"
14
+
15
+ const WIDGET_KEY = "pi-fixed-editor-probe"
16
+ const WARNING_MESSAGE = "pi-fixed-editor: unsupported Pi TUI layout"
17
+
18
+ interface ContainerLike extends Component {
19
+ children: Component[]
20
+ }
21
+
22
+ interface TuiLike {
23
+ children: Component[]
24
+ terminal?: TerminalLike
25
+ focusedComponent?: Component | null
26
+ requestRender: (force?: boolean) => void
27
+ getShowHardwareCursor?: () => boolean
28
+ }
29
+
30
+ interface ContainerMatch {
31
+ container: ContainerLike
32
+ index: number
33
+ }
34
+
35
+ let compositor: TerminalSplitCompositor | null = null
36
+ let isInstalled = false
37
+ let didWarnUnsupported = false
38
+ let fixedStatusContainer: Component | null = null
39
+ let fixedWidgetContainerAbove: Component | null = null
40
+ let fixedEditorContainer: Component | null = null
41
+ let fixedWidgetContainerBelow: Component | null = null
42
+ let fixedFooterContainer: Component | null = null
43
+
44
+ class ProbeComponent implements Component {
45
+ private readonly install: () => void
46
+ private hasQueuedInstall = false
47
+
48
+ constructor(onInstall: () => void) {
49
+ this.install = onInstall
50
+ }
51
+
52
+ render(): string[] {
53
+ if (!this.hasQueuedInstall) {
54
+ this.hasQueuedInstall = true
55
+ queueMicrotask(this.install)
56
+ }
57
+ return []
58
+ }
59
+
60
+ invalidate(): void {
61
+ this.hasQueuedInstall = false
62
+ }
63
+ }
64
+
65
+ function isContainerLike(value: unknown): value is ContainerLike {
66
+ return (
67
+ typeof value === "object" &&
68
+ value !== null &&
69
+ Array.isArray(Reflect.get(value, "children")) &&
70
+ typeof Reflect.get(value, "render") === "function"
71
+ )
72
+ }
73
+
74
+ function isRenderable(value: unknown): value is Component {
75
+ return (
76
+ typeof value === "object" &&
77
+ value !== null &&
78
+ typeof Reflect.get(value, "render") === "function" &&
79
+ typeof Reflect.get(value, "invalidate") === "function"
80
+ )
81
+ }
82
+
83
+ function getTuiChildren(tui: TuiLike): Component[] {
84
+ return Array.isArray(tui.children) ? tui.children : []
85
+ }
86
+
87
+ function findContainerWithChild(
88
+ tui: TuiLike,
89
+ child: Component,
90
+ ): ContainerMatch | null {
91
+ const children = getTuiChildren(tui)
92
+ const index = children.findIndex(
93
+ (candidate) =>
94
+ isContainerLike(candidate) && candidate.children.includes(child),
95
+ )
96
+ if (index === -1) return null
97
+
98
+ const container = children[index]
99
+ return isContainerLike(container) ? { container, index } : null
100
+ }
101
+
102
+ function findEditorContainer(tui: TuiLike): ContainerMatch | null {
103
+ const focusedComponent = Reflect.get(tui, "focusedComponent")
104
+ if (isRenderable(focusedComponent)) {
105
+ const match = findContainerWithChild(tui, focusedComponent)
106
+ if (match) return match
107
+ }
108
+
109
+ const children = getTuiChildren(tui)
110
+ const index = children.findIndex((candidate) => {
111
+ if (!isContainerLike(candidate)) return false
112
+ return candidate.children.some((child) => {
113
+ return (
114
+ typeof Reflect.get(child, "getText") === "function" &&
115
+ typeof Reflect.get(child, "setText") === "function" &&
116
+ typeof Reflect.get(child, "handleInput") === "function"
117
+ )
118
+ })
119
+ })
120
+
121
+ if (index === -1) return null
122
+ const container = children[index]
123
+ return isContainerLike(container) ? { container, index } : null
124
+ }
125
+
126
+ function renderHidden(
127
+ activeCompositor: TerminalSplitCompositor,
128
+ component: Component | null,
129
+ width: number,
130
+ ): string[] {
131
+ if (!component) return []
132
+ return activeCompositor
133
+ .renderHidden(component, width)
134
+ .filter((line) => visibleWidth(line) > 0)
135
+ }
136
+
137
+ function resetContainers(): void {
138
+ fixedStatusContainer = null
139
+ fixedWidgetContainerAbove = null
140
+ fixedEditorContainer = null
141
+ fixedWidgetContainerBelow = null
142
+ fixedFooterContainer = null
143
+ }
144
+
145
+ function teardown(options?: { resetExtendedKeyboardModes?: boolean }): void {
146
+ const hadCompositor = compositor !== null
147
+ compositor?.dispose(options)
148
+ if (!hadCompositor && options?.resetExtendedKeyboardModes) {
149
+ try {
150
+ process.stdout.write(emergencyTerminalModeReset())
151
+ } catch {
152
+ // Exit cleanup cannot surface useful terminal write failures.
153
+ }
154
+ }
155
+ compositor = null
156
+ isInstalled = false
157
+ resetContainers()
158
+ }
159
+
160
+ function warnUnsupported(ctx: ExtensionContext): void {
161
+ if (didWarnUnsupported || ctx.mode !== "tui") return
162
+ didWarnUnsupported = true
163
+ ctx.ui.notify(WARNING_MESSAGE, "warning")
164
+ }
165
+
166
+ function install(ctx: ExtensionContext, tui: TuiLike): void {
167
+ if (isInstalled || compositor) return
168
+ if (ctx.mode !== "tui") return
169
+
170
+ const terminal = tui.terminal
171
+ if (!terminal || typeof terminal.write !== "function") {
172
+ warnUnsupported(ctx)
173
+ return
174
+ }
175
+
176
+ const editorMatch = findEditorContainer(tui)
177
+ if (!editorMatch) {
178
+ warnUnsupported(ctx)
179
+ return
180
+ }
181
+
182
+ const children = getTuiChildren(tui)
183
+ fixedStatusContainer = children[editorMatch.index - 2] ?? null
184
+ fixedWidgetContainerAbove = children[editorMatch.index - 1] ?? null
185
+ fixedEditorContainer = editorMatch.container
186
+ fixedWidgetContainerBelow = children[editorMatch.index + 1] ?? null
187
+ fixedFooterContainer = children[editorMatch.index + 2] ?? null
188
+
189
+ let nextCompositor: TerminalSplitCompositor
190
+ nextCompositor = new TerminalSplitCompositor({
191
+ tui,
192
+ terminal,
193
+ onCopySelection: (text) => {
194
+ void copyToClipboard(text)
195
+ },
196
+ getShowHardwareCursor: () =>
197
+ typeof tui.getShowHardwareCursor === "function" &&
198
+ tui.getShowHardwareCursor(),
199
+ renderCluster: (width, terminalRows) =>
200
+ renderFixedEditorCluster({
201
+ width,
202
+ terminalRows,
203
+ statusLines: renderHidden(nextCompositor, fixedStatusContainer, width),
204
+ aboveWidgetLines: renderHidden(
205
+ nextCompositor,
206
+ fixedWidgetContainerAbove,
207
+ width,
208
+ ),
209
+ editorLines: renderHidden(nextCompositor, fixedEditorContainer, width),
210
+ belowWidgetLines: renderHidden(
211
+ nextCompositor,
212
+ fixedWidgetContainerBelow,
213
+ width,
214
+ ),
215
+ footerLines: renderHidden(nextCompositor, fixedFooterContainer, width),
216
+ }),
217
+ })
218
+
219
+ compositor = nextCompositor
220
+ if (fixedStatusContainer) nextCompositor.hideRenderable(fixedStatusContainer)
221
+ if (fixedWidgetContainerAbove)
222
+ nextCompositor.hideRenderable(fixedWidgetContainerAbove)
223
+ if (fixedEditorContainer) nextCompositor.hideRenderable(fixedEditorContainer)
224
+ if (fixedWidgetContainerBelow)
225
+ nextCompositor.hideRenderable(fixedWidgetContainerBelow)
226
+ if (fixedFooterContainer) nextCompositor.hideRenderable(fixedFooterContainer)
227
+
228
+ try {
229
+ nextCompositor.install()
230
+ isInstalled = true
231
+ tui.requestRender(true)
232
+ } catch {
233
+ teardown({ resetExtendedKeyboardModes: true })
234
+ warnUnsupported(ctx)
235
+ }
236
+ }
237
+
238
+ export default function fixedEditor(pi: ExtensionAPI) {
239
+ pi.on("session_start", (_event, ctx) => {
240
+ didWarnUnsupported = false
241
+ if (ctx.mode !== "tui") return
242
+
243
+ ctx.ui.setWidget(
244
+ WIDGET_KEY,
245
+ (tui) => {
246
+ const tuiLike = tui as unknown as TuiLike
247
+ return new ProbeComponent(() => install(ctx, tuiLike))
248
+ },
249
+ { placement: "aboveEditor" },
250
+ )
251
+ })
252
+
253
+ pi.on("session_shutdown", (_event, ctx) => {
254
+ if (ctx.mode === "tui") {
255
+ ctx.ui.setWidget(WIDGET_KEY, undefined)
256
+ }
257
+ teardown({ resetExtendedKeyboardModes: true })
258
+ })
259
+ }