Skip to content

Commit 2ec02d9

Browse files
committed
Handle clipboard image tokens
1 parent c55413f commit 2ec02d9

File tree

3 files changed

+73
-5
lines changed

3 files changed

+73
-5
lines changed

packages/cli/src/ui/components/InputPrompt.test.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,57 @@ describe('InputPrompt', () => {
555555
unmount();
556556
});
557557

558+
it('should render clipboard images with a friendly label', async () => {
559+
const clipboardToken = '@.gemini-clipboard/image-5.png';
560+
props.buffer.text = clipboardToken;
561+
props.buffer.lines = [clipboardToken];
562+
props.buffer.viewportVisualLines = [clipboardToken];
563+
props.buffer.allVisualLines = [clipboardToken];
564+
props.buffer.visualToLogicalMap = [[0, 0]];
565+
props.buffer.visualCursor = [0, clipboardToken.length];
566+
567+
const { stdout, unmount } = renderWithProviders(
568+
<InputPrompt {...props} />,
569+
{
570+
uiActions,
571+
},
572+
);
573+
574+
await waitFor(() => {
575+
const frame = clean(stdout.lastFrame());
576+
expect(frame).toContain('[image #5]');
577+
expect(frame).not.toContain('.gemini-clipboard');
578+
});
579+
580+
unmount();
581+
});
582+
583+
it('should render clipboard images with backslash paths', async () => {
584+
const clipboardToken = '@.gemini-clipboard\\image-7.png';
585+
props.buffer.text = clipboardToken;
586+
props.buffer.lines = [clipboardToken];
587+
props.buffer.viewportVisualLines = [clipboardToken];
588+
props.buffer.allVisualLines = [clipboardToken];
589+
props.buffer.visualToLogicalMap = [[0, 0]];
590+
props.buffer.visualCursor = [0, clipboardToken.length];
591+
592+
const { stdout, unmount } = renderWithProviders(
593+
<InputPrompt {...props} />,
594+
{
595+
uiActions,
596+
},
597+
);
598+
599+
await waitFor(() => {
600+
const frame = clean(stdout.lastFrame());
601+
expect(frame).toContain('[image #7]');
602+
expect(frame).not.toContain('.gemini-clipboard');
603+
expect(frame).not.toContain('\\image-7.png');
604+
});
605+
606+
unmount();
607+
});
608+
558609
it('should handle errors during clipboard operations', async () => {
559610
const consoleErrorSpy = vi
560611
.spyOn(console, 'error')

packages/cli/src/ui/components/InputPrompt.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,15 @@ export const calculatePromptWidths = (mainContentWidth: number) => {
101101
} as const;
102102
};
103103

104+
const CLIPBOARD_IMAGE_TOKEN_REGEX =
105+
/^@\.gemini-clipboard[\\/](?:clipboard|image)-(\d+)\.(?:png|jpe?g|gif|bmp|webp|tiff)$/i;
106+
107+
const getClipboardImageLabel = (tokenText: string): string | null => {
108+
const match = CLIPBOARD_IMAGE_TOKEN_REGEX.exec(tokenText);
109+
if (!match) return null;
110+
return `[image #${match[1]}]`;
111+
};
112+
104113
export const InputPrompt: React.FC<InputPromptProps> = ({
105114
buffer,
106115
onSubmit,
@@ -1074,17 +1083,25 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
10741083
let charCount = 0;
10751084
segments.forEach((seg, segIdx) => {
10761085
const segLen = cpLen(seg.text);
1077-
let display = seg.text;
1086+
const clipboardLabel = getClipboardImageLabel(seg.text);
1087+
let display = clipboardLabel ?? seg.text;
10781088

10791089
if (isOnCursorLine) {
10801090
const relativeVisualColForHighlight =
10811091
cursorVisualColAbsolute;
10821092
const segStart = charCount;
10831093
const segEnd = segStart + segLen;
1084-
if (
1094+
1095+
const cursorInSegment =
10851096
relativeVisualColForHighlight >= segStart &&
1086-
relativeVisualColForHighlight < segEnd
1087-
) {
1097+
relativeVisualColForHighlight < segEnd;
1098+
1099+
if (clipboardLabel) {
1100+
display =
1101+
cursorInSegment && showCursor
1102+
? chalk.inverse(clipboardLabel)
1103+
: clipboardLabel;
1104+
} else if (cursorInSegment) {
10881105
const charToHighlight = cpSlice(
10891106
seg.text,
10901107
relativeVisualColForHighlight - segStart,

packages/cli/src/ui/utils/highlight.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export type HighlightToken = {
1111
type: 'default' | 'command' | 'file';
1212
};
1313

14-
const HIGHLIGHT_REGEX = /(^\/[a-zA-Z0-9_-]+|@(?:\\ |[a-zA-Z0-9_./-])+)/g;
14+
const HIGHLIGHT_REGEX = /(^\/[a-zA-Z0-9_-]+|@(?:\\ |[a-zA-Z0-9_.\\\\/:-])+)/g;
1515

1616
export function parseInputForHighlighting(
1717
text: string,

0 commit comments

Comments
 (0)