Skip to content

Commit 1bd3cfe

Browse files
committed
fix(release): correct ripgrep target for Linux ARM64 bundling
- Change aarch64-unknown-linux-musl to aarch64-unknown-linux-gnu (musl variant not available in ripgrep 15.1.0 release assets) - Update AgentUI and InkRenderer components
1 parent d58e7bf commit 1bd3cfe

3 files changed

Lines changed: 88 additions & 4 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ jobs:
390390
fi
391391
if [ -f "autohand-linux-arm64" ]; then
392392
chmod +x autohand-linux-arm64
393-
bundle_unix "autohand-linux-arm64" "aarch64-unknown-linux-musl"
393+
bundle_unix "autohand-linux-arm64" "aarch64-unknown-linux-gnu"
394394
fi
395395
396396
# Create bundled zip for Windows

src/ui/ink/AgentUI.tsx

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { useTranslation } from '../i18n/index.js';
1414
import { getPlanModeManager } from '../../commands/plan.js';
1515
import { TextBuffer } from '../textBuffer.js';
1616
import { handleTextBufferKey, type KeyHandlerResult } from '../textBufferKeyHandler.js';
17-
import { getPromptBlockWidth, isShiftEnterResidualSequence } from '../inputPrompt.js';
17+
import { getPromptBlockWidth, isShiftEnterResidualSequence, processImagesInText } from '../inputPrompt.js';
1818
import { renderTerminalMarkdown } from '../../core/immediateCommandRouter.js';
1919

2020
export interface AgentUIState {
@@ -44,6 +44,8 @@ export interface AgentUIProps {
4444
onToggleLiveCommandExpanded?: () => void;
4545
onInputChange?: (input: string) => void;
4646
enableQueueInput?: boolean;
47+
/** Called when a dragged/dropped image is detected in the input */
48+
onImageDetected?: (data: Buffer, mimeType: string, filename?: string) => number;
4749
}
4850

4951
interface TextBufferKeyInfo {
@@ -55,6 +57,8 @@ interface TextBufferKeyInfo {
5557
}
5658

5759
const INK_TEXTBUFFER_VIEWPORT_HEIGHT = 10;
60+
/** Debounce delay for image detection after input changes (ms) */
61+
const INK_IMAGE_SCAN_DELAY_MS = 150;
5862

5963
function getInkTextBufferViewportWidth(columns: number | undefined): number {
6064
return Math.max(1, getPromptBlockWidth(columns) - 4);
@@ -133,14 +137,31 @@ export function getComposerHelpLine(
133137
return `${contextDisplay}${contextDisplay ? ' · ' : ''}${commandHint}`;
134138
}
135139

140+
/**
141+
* Check if text potentially contains an image path (quick heuristic).
142+
* Mirrors the logic from inputPrompt.ts.
143+
*/
144+
function hasPotentialImagePath(text: string): boolean {
145+
const imageExtPattern = /\.(png|jpg|jpeg|gif|webp)$/i;
146+
// Check for quoted paths, escaped paths, or simple paths
147+
if (imageExtPattern.test(text)) {
148+
return true;
149+
}
150+
if (/["'].*\.(png|jpg|jpeg|gif|webp)["']/i.test(text)) {
151+
return true;
152+
}
153+
return false;
154+
}
155+
136156
export function AgentUI({
137157
state,
138158
onInstruction,
139159
onEscape,
140160
onCtrlC,
141161
onToggleLiveCommandExpanded,
142162
onInputChange,
143-
enableQueueInput = true
163+
enableQueueInput = true,
164+
onImageDetected,
144165
}: AgentUIProps) {
145166
const { exit } = useApp();
146167
const { colors } = useTheme();
@@ -158,6 +179,11 @@ export function AgentUI({
158179
)
159180
);
160181

182+
// Track the last processed input to avoid re-processing the same text
183+
const lastProcessedInputRef = useRef<string>('');
184+
// Debounce timer for image scanning
185+
const imageScanTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
186+
161187
const syncInputFromBuffer = useCallback(() => {
162188
const buffer = textBufferRef.current;
163189
setInput(buffer.getText());
@@ -219,6 +245,57 @@ export function AgentUI({
219245
}
220246
}, [ctrlCCount]);
221247

248+
// Debounced image detection: when input changes and contains potential image paths,
249+
// process them through processImagesInText and update the input with [Image #N] placeholders.
250+
useEffect(() => {
251+
if (!onImageDetected) {
252+
return;
253+
}
254+
255+
// Clear any pending scan
256+
if (imageScanTimerRef.current) {
257+
clearTimeout(imageScanTimerRef.current);
258+
imageScanTimerRef.current = null;
259+
}
260+
261+
// Skip if already processed (e.g., after a replacement)
262+
if (input === lastProcessedInputRef.current) {
263+
return;
264+
}
265+
266+
// Quick heuristic check before scheduling the scan
267+
if (!hasPotentialImagePath(input)) {
268+
lastProcessedInputRef.current = input;
269+
return;
270+
}
271+
272+
// Debounce: wait for typing to settle before scanning
273+
imageScanTimerRef.current = setTimeout(() => {
274+
imageScanTimerRef.current = null;
275+
276+
const processed = processImagesInText(input, onImageDetected, {
277+
announce: false,
278+
});
279+
280+
if (processed !== input) {
281+
// Image was detected and replaced with [Image #N]
282+
lastProcessedInputRef.current = processed;
283+
const buffer = textBufferRef.current;
284+
buffer.setText(processed);
285+
syncInputFromBuffer();
286+
} else {
287+
lastProcessedInputRef.current = input;
288+
}
289+
}, INK_IMAGE_SCAN_DELAY_MS);
290+
291+
return () => {
292+
if (imageScanTimerRef.current) {
293+
clearTimeout(imageScanTimerRef.current);
294+
imageScanTimerRef.current = null;
295+
}
296+
};
297+
}, [input, onImageDetected, syncInputFromBuffer]);
298+
222299
useInput((char, key) => {
223300
syncBufferViewport();
224301

src/ui/ink/InkRenderer.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export interface InkRendererOptions {
2323
onEscape: () => void;
2424
onCtrlC: () => void;
2525
enableQueueInput?: boolean;
26+
/** Called when a dragged/dropped image is detected in the input */
27+
onImageDetected?: (data: Buffer, mimeType: string, filename?: string) => number;
2628
}
2729

2830
/**
@@ -41,6 +43,7 @@ interface AgentUIWrapperProps {
4143
onToggleLiveCommandExpanded: () => void;
4244
onInputChange: (input: string) => void;
4345
enableQueueInput?: boolean;
46+
onImageDetected?: (data: Buffer, mimeType: string, filename?: string) => number;
4447
}
4548

4649
/**
@@ -56,7 +59,8 @@ const AgentUIWrapper = forwardRef<AgentUIWrapperHandle, AgentUIWrapperProps>(
5659
onCtrlC,
5760
onToggleLiveCommandExpanded,
5861
onInputChange,
59-
enableQueueInput
62+
enableQueueInput,
63+
onImageDetected,
6064
} = props;
6165

6266
const [state, setState] = useState<AgentUIState>(initialState);
@@ -88,6 +92,7 @@ const AgentUIWrapper = forwardRef<AgentUIWrapperHandle, AgentUIWrapperProps>(
8892
onToggleLiveCommandExpanded={onToggleLiveCommandExpanded}
8993
onInputChange={handleInputChange}
9094
enableQueueInput={enableQueueInput}
95+
onImageDetected={onImageDetected}
9196
/>
9297
);
9398
}
@@ -146,6 +151,7 @@ export class InkRenderer {
146151
onToggleLiveCommandExpanded={() => this.toggleActiveLiveCommandExpanded()}
147152
onInputChange={this.handleInputChange}
148153
enableQueueInput={this.options.enableQueueInput}
154+
onImageDetected={this.options.onImageDetected}
149155
/>
150156
</I18nProvider>
151157
</ThemeProvider>,
@@ -505,6 +511,7 @@ export class InkRenderer {
505511
onToggleLiveCommandExpanded={() => this.toggleActiveLiveCommandExpanded()}
506512
onInputChange={this.handleInputChange}
507513
enableQueueInput={this.options.enableQueueInput}
514+
onImageDetected={this.options.onImageDetected}
508515
/>
509516
</I18nProvider>
510517
</ThemeProvider>,

0 commit comments

Comments
 (0)