Skip to content

Commit ca2ad10

Browse files
committed
fix: allow users to put image paths anywhere in the message
1 parent 5fdd00c commit ca2ad10

File tree

3 files changed

+80
-18
lines changed

3 files changed

+80
-18
lines changed

npm-app/src/__tests__/image-upload.test.ts

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { describe, test, expect, beforeEach, afterEach } from 'bun:test'
2-
import { writeFileSync, unlinkSync, mkdirSync, rmSync } from 'fs'
1+
import { writeFileSync, mkdirSync, rmSync } from 'fs'
32
import path from 'path'
3+
4+
import { describe, test, expect, beforeEach, afterEach } from 'bun:test'
5+
46
import {
57
processImageFile,
68
isImageFile,
@@ -163,14 +165,47 @@ describe('Image Upload Functionality', () => {
163165
expect(paths).toEqual([])
164166
})
165167

168+
test('should auto-detect bare relative paths with separators', () => {
169+
const input = 'Check assets/multi-agents.png and images/logo.jpg'
170+
const paths = extractImagePaths(input)
171+
expect(paths).toEqual(['assets/multi-agents.png', 'images/logo.jpg'])
172+
})
173+
174+
test('should auto-detect Windows-style bare relative paths', () => {
175+
const input = 'See assets\\windows\\image.png'
176+
const paths = extractImagePaths(input)
177+
expect(paths).toEqual(['assets\\windows\\image.png'])
178+
})
179+
180+
test('should NOT auto-detect URLs', () => {
181+
const input =
182+
'Visit https://example.com/image.png and http://site.com/photo.jpg'
183+
const paths = extractImagePaths(input)
184+
expect(paths).toEqual([])
185+
})
186+
187+
test('should handle expanded trailing punctuation', () => {
188+
const input =
189+
'Files: assets/logo.png), ./images/banner.jpg], and ~/photos/pic.png>'
190+
const paths = extractImagePaths(input)
191+
expect(paths.sort()).toEqual(
192+
['./images/banner.jpg', '~/photos/pic.png', 'assets/logo.png'].sort(),
193+
)
194+
})
195+
166196
test('should handle weird characters and spaces in quoted paths', () => {
167-
const input = 'Files: "./ConstellationFS Demo · 1.21am · 09-11.jpeg" and \'../images/café ñoño (2024).png\''
197+
const input =
198+
'Files: "./ConstellationFS Demo · 1.21am · 09-11.jpeg" and \'../images/café ñoño (2024).png\''
168199
const paths = extractImagePaths(input)
169-
expect(paths).toEqual(['./ConstellationFS Demo · 1.21am · 09-11.jpeg', '../images/café ñoño (2024).png'])
200+
expect(paths).toEqual([
201+
'./ConstellationFS Demo · 1.21am · 09-11.jpeg',
202+
'../images/café ñoño (2024).png',
203+
])
170204
})
171205

172206
test('should require quotes for paths with spaces to avoid false positives', () => {
173-
const input = '/Users/brandonchen/Downloads/ConstellationFS Demo · 1.21am · 09-11.jpeg'
207+
const input =
208+
'/Users/brandonchen/Downloads/ConstellationFS Demo · 1.21am · 09-11.jpeg'
174209
const paths = extractImagePaths(input)
175210
// Unquoted paths with spaces are not auto-detected to avoid false positives
176211
expect(paths).toEqual([])

npm-app/src/client.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,15 +1164,38 @@ export class Client {
11641164
)
11651165

11661166
// Replace each @agentName with @agentName (agentId)
1167-
cleanPrompt = cleanPrompt.replace(agentRegex, (match) => {
1168-
const agentName = match.substring(1).trim() // Remove @ and trim
1169-
const agentId = resolveNameToId(agentName, localAgentInfo)
1167+
// But skip @mentions that look like file paths (contain / or \)
1168+
cleanPrompt = cleanPrompt.replace(
1169+
agentRegex,
1170+
(match, agentName, offset, string) => {
1171+
// Check if this @ mention is part of a file path
1172+
const beforeMatch = string.substring(Math.max(0, offset - 10), offset)
1173+
const afterMatch = string.substring(
1174+
offset + match.length,
1175+
offset + match.length + 20,
1176+
)
11701177

1171-
if (agentId) {
1172-
return `@${agentName} (agent type: ${agentId})`
1173-
}
1174-
return match // Return original if no agent ID found
1175-
})
1178+
// Skip if this looks like a file path (contains / or \ nearby)
1179+
if (
1180+
beforeMatch.includes('/') ||
1181+
beforeMatch.includes('\\') ||
1182+
afterMatch.includes('/') ||
1183+
afterMatch.includes('\\') ||
1184+
match.includes('/') ||
1185+
match.includes('\\')
1186+
) {
1187+
return match // Don't modify file paths
1188+
}
1189+
1190+
const trimmedAgentName = agentName.trim()
1191+
const agentId = resolveNameToId(trimmedAgentName, localAgentInfo)
1192+
1193+
if (agentId) {
1194+
return `@${trimmedAgentName} (agent type: ${agentId})`
1195+
}
1196+
return match // Return original if no agent ID found
1197+
},
1198+
)
11761199

11771200
return cleanPrompt
11781201
}

npm-app/src/utils/image-handler.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { readFileSync, statSync } from 'fs'
22
import { homedir } from 'os'
33
import path from 'path'
4+
45
import { Jimp } from 'jimp'
56

67
import { logger } from './logger'
@@ -175,7 +176,6 @@ export async function processImageFile(
175176
const originalWidth = image.bitmap.width
176177
const originalHeight = image.bitmap.height
177178

178-
let bestBuffer = processedBuffer
179179
let bestBase64Size = base64Size
180180
let compressionAttempts = []
181181

@@ -241,7 +241,6 @@ export async function processImageFile(
241241

242242
// Keep track of the best attempt so far
243243
if (testBase64Size < bestBase64Size) {
244-
bestBuffer = testBuffer
245244
bestBase64Size = testBase64Size
246245
}
247246
} catch (attemptError) {
@@ -373,12 +372,17 @@ export function extractImagePaths(input: string): string[] {
373372
const pathRegexes = [
374373
// Absolute paths: /path/to/file, ~/path, C:\path (Windows)
375374
new RegExp(
376-
`(?:^|\\s)((?:[~/]|[A-Za-z]:\\\\)[^\\s"']*\\.(?:${imageExts}))(?=\\s|$|[.,!?;])`,
375+
`(?:^|\\s)((?:[~/]|[A-Za-z]:\\\\)[^\\s"']*\\.(?:${imageExts}))(?=\\s|$|[.,!?;)\\]}>])`,
377376
'gi',
378377
),
379378
// Relative paths with separators: ./path/file, ../path/file
380379
new RegExp(
381-
`(?:^|\\s)(\\.\\.?[\\/\\\\][^\\s"']*\\.(?:${imageExts}))(?=\\s|$|[.,!?;])`,
380+
`(?:^|\\s)(\\.\\.?[\\/\\\\][^\\s"']*\\.(?:${imageExts}))(?=\\s|$|[.,!?;)\\]}>])`,
381+
'gi',
382+
),
383+
// Bare relative paths with separators (like assets/image.png)
384+
new RegExp(
385+
`(?:^|\\s)((?![^\\s]*:\\/\\/)[^\\s"':]*[\\/\\\\][^\\s"']*\\.(?:${imageExts}))(?=\\s|$|[.,!?;)\\]}>])`,
382386
'gi',
383387
),
384388
// Quoted paths (single or double quotes)
@@ -388,7 +392,7 @@ export function extractImagePaths(input: string): string[] {
388392
// Extract paths using all regex patterns
389393
for (const regex of pathRegexes) {
390394
while ((match = regex.exec(cleanInput)) !== null) {
391-
const path = match[1].replace(/[.,!?;]+$/, '') // Remove trailing punctuation
395+
const path = match[1].replace(/[.,!?;)\]}>">]+$/, '') // Remove trailing punctuation
392396
if (isImageFile(path) && !paths.includes(path)) {
393397
paths.push(path)
394398
}

0 commit comments

Comments
 (0)