@pellux/goodvibes-tui 2.0.8 → 2.0.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-tui",
3
- "version": "2.0.8",
3
+ "version": "2.0.9",
4
4
  "description": "Terminal-native GoodVibes client for coding, operations, knowledge and channel workflows — a pure client of the GoodVibes daemon.",
5
5
  "type": "module",
6
6
  "main": "src/main.ts",
@@ -95,8 +95,8 @@
95
95
  "@anthropic-ai/bedrock-sdk": "^0.29.1",
96
96
  "@anthropic-ai/sdk": "^0.92.0",
97
97
  "@ast-grep/napi": "^0.42.0",
98
- "@pellux/goodvibes-sdk": "2.0.8",
99
- "@pellux/goodvibes-terminal-shell": "2.0.8",
98
+ "@pellux/goodvibes-sdk": "2.0.9",
99
+ "@pellux/goodvibes-terminal-shell": "2.0.9",
100
100
  "bash-language-server": "^5.6.0",
101
101
  "fuse.js": "^7.1.0",
102
102
  "@pellux/goodvibes-daemon": "1.28.7",
@@ -58,6 +58,45 @@ export async function hydrateOnboardingWizardFromRuntimeForHandler(handler: Inpu
58
58
  }
59
59
  }
60
60
 
61
+ /**
62
+ * handlePasteForHandler - Shared paste path for Ctrl+V and middle-click.
63
+ *
64
+ * Tries the clipboard's image first, then its text. The clipboard itself comes
65
+ * from `handler.clipboardSource`, so tests can hand in a clipboard without
66
+ * touching the real one.
67
+ *
68
+ * The prompt is edited on the HANDLER here, not on the shortcut route state
69
+ * that dispatched the key. feedInputTokens snapshots the prompt before
70
+ * dispatching and would write that snapshot back over this edit, erasing the
71
+ * paste — it does not, because it only restores a field the action left
72
+ * untouched (see the promptBefore guard in handler-feed.ts). That guard is the
73
+ * only reason Ctrl+V works, so it is pinned by test.
74
+ */
75
+ export function handlePasteForHandler(handler: InputHandler): ReturnType<typeof handleClipboardPaste> {
76
+ const result = handleClipboardPaste({
77
+ prompt: handler.prompt,
78
+ cursorPos: handler.cursorPos,
79
+ pasteRegistry: handler.pasteRegistry,
80
+ nextPasteId: handler.nextPasteId,
81
+ imageRegistry: handler.imageRegistry,
82
+ nextImageId: handler.nextImageId,
83
+ saveUndoState: () => handler.saveUndoState(),
84
+ ensureInputCursorVisible: () => handler.ensureInputCursorVisible(),
85
+ requestRender: handler.requestRender,
86
+ }, handler.uiServices.environment.shellPaths.workingDirectory, handler.clipboardSource);
87
+
88
+ handler.prompt = result.prompt;
89
+ handler.cursorPos = result.cursorPos;
90
+ handler.nextImageId = result.nextImageId;
91
+ handler.nextPasteId = result.nextPasteId;
92
+
93
+ if (!result.pasted) {
94
+ handler.conversationManager?.log('[Paste: clipboard does not contain supported text or image data]', { fg: '240' });
95
+ handler.requestRender();
96
+ }
97
+ return result;
98
+ }
99
+
61
100
  export function registerPasteForHandler(handler: InputHandler, content: string): string {
62
101
  const result = registerPaste({
63
102
  pasteRegistry: handler.pasteRegistry,
@@ -19,6 +19,7 @@ import type { UiRuntimeServices } from '../runtime/ui-services.ts';
19
19
  import type { CommandContext } from './command-registry.ts';
20
20
  import type { ConversationManager } from '../core/conversation';
21
21
  import type { ModelPickerModal, ModelPickerTarget } from './model-picker.ts';
22
+ import type { ClipboardPasteSource } from './handler-content-actions.ts';
22
23
  import type { SelectionManager } from '@pellux/goodvibes-terminal-shell';
23
24
  import type { InfiniteBuffer } from '@pellux/goodvibes-terminal-shell';
24
25
  import type { AutocompleteEngine } from './autocomplete.ts';
@@ -81,6 +82,11 @@ export interface InputHandlerLike {
81
82
  nextPasteId: number;
82
83
  imageRegistry: Map<string, { data: string; mediaType: string }>;
83
84
  nextImageId: number;
85
+ /**
86
+ * The clipboard this composer pastes from. Swappable so tests can supply a
87
+ * clipboard instead of reaching for the machine's real one.
88
+ */
89
+ clipboardSource: ClipboardPasteSource;
84
90
 
85
91
  // ── Timing ───────────────────────────────────────────────────────────────
86
92
  lastCopyTime: number;
@@ -124,6 +130,7 @@ export interface InputHandlerLike {
124
130
  // ── Methods: modal lifecycle ──────────────────────────────────────────────
125
131
  modalOpened(name: string): void;
126
132
  saveUndoState(): void;
133
+ ensureInputCursorVisible(contentWidth?: number): void;
127
134
 
128
135
  // ── Methods: block actions (dispatched in executeBlockAction) ────────────
129
136
  handleBlockCopy(): void;
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node
2
2
  import { dirname } from 'node:path';
3
3
  import { InputTokenizer } from '@pellux/goodvibes-sdk/platform/core';
4
4
  import { createOAuthLocalListener } from '@pellux/goodvibes-sdk/platform/config';
5
- import { clearModalStackForHandler, cleanupMarkerRegistryForHandler, executeBlockActionForHandler, expandPromptForHandler, findMarkerAtPosForHandler, getImageAttachmentsForHandler, handleBlockCopyForHandler, handleBlockSaveForHandler, handleBlockToggleForHandler, handleBookmarkForHandler, handleCopyForHandler, handleCtrlCForHandler, handleDiffApplyForHandler, handleEscapeForHandler, hydrateOnboardingWizardFromRuntimeForHandler, modalOpenedForHandler, openOnboardingWizardForHandler, registerPasteForHandler } from './handler-interactions.ts';
5
+ import { clearModalStackForHandler, cleanupMarkerRegistryForHandler, executeBlockActionForHandler, expandPromptForHandler, findMarkerAtPosForHandler, getImageAttachmentsForHandler, handleBlockCopyForHandler, handleBlockSaveForHandler, handleBlockToggleForHandler, handleBookmarkForHandler, handleCopyForHandler, handleCtrlCForHandler, handleDiffApplyForHandler, handleEscapeForHandler, handlePasteForHandler, hydrateOnboardingWizardFromRuntimeForHandler, modalOpenedForHandler, openOnboardingWizardForHandler, registerPasteForHandler } from './handler-interactions.ts';
6
6
  import { getViewportBottomLine } from '../renderer/conversation-layout.ts';
7
7
  import { clearOnboardingModelPickerCancelStateForHandler, clearOnboardingPendingModelPickerTargetForHandler, completeOpenAiSubscriptionFromListenerForHandler, getOnboardingConfigValueForHandler, getOnboardingRuntimePostureForHandler, handleModelPickerCommitForHandler, handleOnboardingActionForHandler, handleOpenAiSubscriptionFinishForHandler, handleOpenAiSubscriptionStartForHandler, openModelPickerWithTargetForHandler, openProviderModelPickerWithTargetForHandler, refreshOnboardingHydrationForHandler, restartOnboardingExternalServicesIfNeededForHandler, restoreOnboardingModelPickerCancelStateForHandler, saveWizardProgressForHandler, syncRuntimeFromOnboardingRequestForHandler, verifyOnboardingRuntimePostureForHandler, } from './handler-onboarding.ts';
8
8
  import type { OnboardingRuntimePosture } from './handler-types.ts';
@@ -53,12 +53,14 @@ import {
53
53
  handleBlockToggle,
54
54
  handleBookmark,
55
55
  handleClipboardPaste,
56
+ type ClipboardPasteSource,
56
57
  handleCopy,
57
58
  handleCtrlC,
58
59
  handleDiffApply,
59
60
  mediaTypeFromExt,
60
61
  registerPaste,
61
62
  } from './handler-content-actions.ts';
63
+ import { pasteFromClipboard, pasteImageFromClipboard } from '../utils/clipboard.ts';
62
64
  import {
63
65
  handleIndicatorFocusToken,
64
66
  handleMouseToken,
@@ -574,28 +576,15 @@ export class InputHandler implements InputHandlerLike {
574
576
  * Tries image clipboard first, falls back to text paste.
575
577
  */
576
578
  public handlePaste(): ReturnType<typeof handleClipboardPaste> {
577
- const result = handleClipboardPaste({
578
- prompt: this.prompt,
579
- cursorPos: this.cursorPos,
580
- pasteRegistry: this.pasteRegistry,
581
- nextPasteId: this.nextPasteId,
582
- imageRegistry: this.imageRegistry,
583
- nextImageId: this.nextImageId,
584
- saveUndoState: () => this.saveUndoState(),
585
- ensureInputCursorVisible: () => this.ensureInputCursorVisible(),
586
- requestRender: this.requestRender,
587
- }, this.uiServices.environment.shellPaths.workingDirectory);
588
- this.prompt = result.prompt;
589
- this.cursorPos = result.cursorPos;
590
- this.nextImageId = result.nextImageId;
591
- this.nextPasteId = result.nextPasteId;
592
- if (!result.pasted) {
593
- this.conversationManager?.log('[Paste: clipboard does not contain supported text or image data]', { fg: '240' });
594
- this.requestRender();
595
- }
596
- return result;
579
+ return handlePasteForHandler(this);
597
580
  }
598
581
 
582
+ /**
583
+ * The clipboard this composer pastes from. Swappable so tests can supply a
584
+ * clipboard instead of reaching for the machine's real one.
585
+ */
586
+ public clipboardSource: ClipboardPasteSource = { pasteImageFromClipboard, pasteFromClipboard };
587
+
599
588
  /** Content width for wrapping — set by main.ts via setContentWidth(). */
600
589
  public contentWidth = 76;
601
590
  public panelMouseLayout: PanelMouseLayout | null = null;
package/src/version.ts CHANGED
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  // The prebuild script updates the fallback value before compilation.
7
7
  // Uses import.meta.dir (Bun) to locate package.json relative to this file,
8
8
  // which is correct regardless of the process working directory.
9
- let _version = '2.0.8';
9
+ let _version = '2.0.9';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
12
12
  // Only trust a version read from OUR OWN package.json. A Bun single-file