mirror of
https://github.com/Eugeny/tabby.git
synced 2026-08-24 10:14:31 -05:00
fix: handle OSC sequences split across buffer chunks (#11144)
The OSCProcessor assumed OSC sequences (like OSC 52 clipboard) arrive in a single buffer chunk. However, when sequences exceed 1024 bytes, they get split across multiple chunks, causing the prefix and suffix to be in different calls. This fix adds buffering to accumulate incomplete OSC sequences until the terminator (BEL or ST) arrives, allowing proper handling of arbitrarily long OSC 52 clipboard data and other long OSC sequences. Fixes #6001 Co-authored-by: ToastyTheBot <toastythebot@anthropic.com>
This commit is contained in:
co-authored by
ToastyTheBot
parent
f05a07aeca
commit
4885888a2a
@@ -9,33 +9,62 @@ export class OSCProcessor extends SessionMiddleware {
|
||||
get cwdReported$ (): Observable<string> { return this.cwdReported }
|
||||
|
||||
private cwdReported = new Subject<string>()
|
||||
private buffer: Buffer | null = null
|
||||
|
||||
feedFromSession (data: Buffer): void {
|
||||
// Prepend any buffered data from previous chunks
|
||||
if (this.buffer) {
|
||||
data = Buffer.concat([this.buffer, data])
|
||||
this.buffer = null
|
||||
}
|
||||
|
||||
let startIndex = 0
|
||||
while (data.includes(OSCPrefix, startIndex)) {
|
||||
const si = startIndex
|
||||
if (!OSCSuffixes.some(s => data.includes(s, si))) {
|
||||
const processedData: Buffer[] = []
|
||||
|
||||
while (startIndex < data.length) {
|
||||
const prefixIndex = data.indexOf(OSCPrefix, startIndex)
|
||||
|
||||
if (prefixIndex === -1) {
|
||||
// No more OSC sequences, pass remaining data
|
||||
if (startIndex < data.length) {
|
||||
processedData.push(data.subarray(startIndex))
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
const params = data.subarray(data.indexOf(OSCPrefix, startIndex) + OSCPrefix.length)
|
||||
// Pass data before this OSC sequence
|
||||
if (prefixIndex > startIndex) {
|
||||
processedData.push(data.subarray(startIndex, prefixIndex))
|
||||
}
|
||||
|
||||
const [closesSuffix, closestSuffixIndex] = OSCSuffixes
|
||||
.map((suffix): [Buffer, number] => [suffix, params.indexOf(suffix)])
|
||||
.filter(([_, index]) => index !== -1)
|
||||
.sort(([_, a], [__, b]) => a - b)[0]
|
||||
// Look for suffix after the prefix
|
||||
const suffixSearchStart = prefixIndex + OSCPrefix.length
|
||||
let foundSuffix: [Buffer, number] | null = null
|
||||
|
||||
const oscString = params.subarray(0, closestSuffixIndex).toString()
|
||||
for (const suffix of OSCSuffixes) {
|
||||
const suffixIndex = data.indexOf(suffix, suffixSearchStart)
|
||||
if (suffixIndex !== -1) {
|
||||
if (!foundSuffix || suffixIndex < foundSuffix[1]) {
|
||||
foundSuffix = [suffix, suffixIndex]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
startIndex = data.indexOf(closesSuffix, startIndex) + closesSuffix.length
|
||||
if (!foundSuffix) {
|
||||
// No suffix found - buffer the rest and wait for next chunk
|
||||
this.buffer = data.subarray(prefixIndex)
|
||||
break
|
||||
}
|
||||
|
||||
// Extract OSC string (between prefix and suffix)
|
||||
const oscString = data.subarray(suffixSearchStart, foundSuffix[1]).toString()
|
||||
const [oscCodeString, ...oscParams] = oscString.split(';')
|
||||
const oscCode = parseInt(oscCodeString)
|
||||
|
||||
if (oscCode === 1337) {
|
||||
const paramString = oscParams.join(';')
|
||||
if (paramString.startsWith('CurrentDir=')) {
|
||||
let reportedCWD = paramString.split('=')[1]
|
||||
let reportedCWD = paramString.split('=', 2)[1]
|
||||
if (reportedCWD.startsWith('~')) {
|
||||
reportedCWD = os.homedir() + reportedCWD.substring(1)
|
||||
}
|
||||
@@ -43,11 +72,16 @@ export class OSCProcessor extends SessionMiddleware {
|
||||
} else {
|
||||
console.debug('Unsupported OSC 1337 parameter:', paramString)
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
// Move past this OSC sequence
|
||||
startIndex = foundSuffix[1] + foundSuffix[0].length
|
||||
}
|
||||
|
||||
// Pass through all processed data
|
||||
if (processedData.length > 0) {
|
||||
super.feedFromSession(Buffer.concat(processedData))
|
||||
}
|
||||
super.feedFromSession(data)
|
||||
}
|
||||
|
||||
close (): void {
|
||||
|
||||
Reference in New Issue
Block a user