Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcb2781694 | ||
|
|
0b4a87f1e7 | ||
|
|
01827bf70b | ||
|
|
9a330499de |
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "open-terminal",
|
||||
"productName": "OpenTerminal",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"description": "Open-source terminal with SSH, split panes, themes and fonts",
|
||||
"main": "out/main/index.js",
|
||||
"author": "Bill",
|
||||
|
||||
@@ -37,6 +37,13 @@ export function resolveCwd(current: string | undefined, rawArg: string): string
|
||||
}
|
||||
// `cd -` means the shell's previous directory, which we cannot know here.
|
||||
if (arg === '-') return null
|
||||
// Drive-relative `cd d:` (cmd/PowerShell): the target is the drive's
|
||||
// current directory, which the shell never tells us — path.isAbsolute('d:')
|
||||
// is false, so without this branch it resolves against the base and dies on
|
||||
// a nonexistent `<base>\d:`. The drive root is the fresh-shell answer.
|
||||
if (process.platform === 'win32' && /^[a-zA-Z]:$/.test(arg)) {
|
||||
return existingDir(`${arg.toUpperCase()}\\`)
|
||||
}
|
||||
|
||||
const base = current && current.trim() ? current : homedir()
|
||||
// An absolute argument wins; otherwise resolve against the base. Both forms
|
||||
|
||||
@@ -152,6 +152,15 @@ function stepLineBuffer(
|
||||
outBuf.current = ''
|
||||
return { line: '' }
|
||||
}
|
||||
// xterm-native paste (plain Ctrl+V): with the shell's bracketed-paste mode
|
||||
// on, the pasted block arrives wrapped in \x1b[200~ / \x1b[201~ and the chunk
|
||||
// starts with ESC, which the guards below would drop wholesale. Strip the
|
||||
// markers and keep the pasted text as ordinary input.
|
||||
if (data.includes('\x1b[200~') || data.includes('\x1b[201~')) {
|
||||
const text = data.replace(/\x1b\[20[01]~/g, '')
|
||||
if (text) outBuf.current = b + text
|
||||
return { line: outBuf.current }
|
||||
}
|
||||
// A chunk that begins with escape/control sequences (e.g. the terminal's
|
||||
// clear-line) only removes buffer content; never appends.
|
||||
const first = data[0]
|
||||
@@ -429,15 +438,59 @@ export const TerminalView: ForwardRefExoticComponent<TerminalViewProps & { ref?:
|
||||
setResultInfo(found ? '已找到' : '未找到')
|
||||
}, [])
|
||||
|
||||
/** Record a submitted command line; cd-style lines also update cwd memory. */
|
||||
const trackSubmittedLine = useCallback(
|
||||
(submit: string): void => {
|
||||
void window.api.recordCommand(submit).catch(() => undefined)
|
||||
// Remember the directory for the next launch: a cd-style line is
|
||||
// resolved in the main process (the only side with node's `path`).
|
||||
const cdArg = cdArgument(submit)
|
||||
if (cdArg !== null) {
|
||||
void window.api
|
||||
.resolveCwd(getSessionCwd(sessionId), cdArg)
|
||||
.then((next: string | null) => {
|
||||
if (next) setSessionCwd(sessionId, next)
|
||||
})
|
||||
.catch(() => undefined)
|
||||
}
|
||||
},
|
||||
[sessionId]
|
||||
)
|
||||
|
||||
/**
|
||||
* Pasted text is written straight to the pty (confirmPaste → writeBroadcast),
|
||||
* bypassing xterm's onData, so the line buffer / history / cwd tracker would
|
||||
* never see it — a pasted `cd X` used to be recorded as the bare prefix typed
|
||||
* before it. Mirror the shell: each completed line is a submitted command,
|
||||
* the trailing partial line stays pending in the buffer.
|
||||
*/
|
||||
const trackPastedText = useCallback(
|
||||
(data: string): void => {
|
||||
if (termRef.current?.buffer.active.type === 'alternate') return
|
||||
const parts = data.replace(/\r\n/g, '\n').split('\n')
|
||||
const tail = parts.pop() ?? ''
|
||||
for (const line of parts) {
|
||||
const t = line.trim()
|
||||
lineBufRef.current = ''
|
||||
if (t) trackSubmittedLine(t)
|
||||
}
|
||||
if (tail) lineBufRef.current += tail
|
||||
},
|
||||
[trackSubmittedLine]
|
||||
)
|
||||
|
||||
const confirmPaste = useCallback(
|
||||
(text: string) => {
|
||||
const data = nativePasteRef.current ?? text
|
||||
nativePasteRef.current = null
|
||||
setPaste(null)
|
||||
if (!deadRef.current) writeBroadcast(sessionId, data)
|
||||
if (!deadRef.current) {
|
||||
writeBroadcast(sessionId, data)
|
||||
trackPastedText(data)
|
||||
}
|
||||
termRef.current?.focus()
|
||||
},
|
||||
[sessionId]
|
||||
[sessionId, trackPastedText]
|
||||
)
|
||||
|
||||
const cancelPaste = useCallback(() => {
|
||||
@@ -732,18 +785,7 @@ export const TerminalView: ForwardRefExoticComponent<TerminalViewProps & { ref?:
|
||||
// M5: line capture → recordCommand + inline completion overlay.
|
||||
const step = stepLineBuffer(lineBufRef, data, consumeRewrite)
|
||||
if (typeof step.submit === 'string' && step.submit) {
|
||||
void window.api.recordCommand(step.submit).catch(() => undefined)
|
||||
// Remember the directory for the next launch: a cd-style line is
|
||||
// resolved in the main process (the only side with node's `path`).
|
||||
const cdArg = cdArgument(step.submit)
|
||||
if (cdArg !== null) {
|
||||
void window.api
|
||||
.resolveCwd(getSessionCwd(sessionId), cdArg)
|
||||
.then((next: string | null) => {
|
||||
if (next) setSessionCwd(sessionId, next)
|
||||
})
|
||||
.catch(() => undefined)
|
||||
}
|
||||
trackSubmittedLine(step.submit)
|
||||
}
|
||||
if (deadRef.current) {
|
||||
suggestionsRef.current = []
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* Two signals feed the same store:
|
||||
* 1. the shell itself, over OSC 7 (`file://host/path`) or ConEmu's OSC 9;9 —
|
||||
* exact, but only present when shell integration is on;
|
||||
* 2. the command the user typed, matched against the `cd` family — the
|
||||
* fallback that works with no shell cooperation at all.
|
||||
* 2. the command the user typed, matched against the `cd` family plus the
|
||||
* bare drive switch (`d:`) of cmd.exe / PowerShell — the fallback that
|
||||
* works with no shell cooperation at all.
|
||||
*
|
||||
* Both are platform-neutral: path resolution and existence checks happen in the
|
||||
* main process (`src/main/cwd.ts`) where node's `path` lives, so nothing here
|
||||
@@ -21,14 +22,26 @@
|
||||
*/
|
||||
const CD_COMMAND = /^\s*(?:cd|chdir|sl|set-location|pushd|push-location)(?:\s+([\s\S]*))?$/i
|
||||
|
||||
/**
|
||||
* Bare drive switch (`d:`, `D:`) — cmd.exe and PowerShell change the current
|
||||
* drive with no `cd` keyword at all, and it is the common way to land on
|
||||
* another drive on Windows. Treated as a cd to the drive root; the main
|
||||
* process's existence check quietly drops it on platforms where drive
|
||||
* letters mean nothing (POSIX resolves it to a nonexistent relative path).
|
||||
*/
|
||||
const DRIVE_SWITCH = /^\s*([a-zA-Z]):\s*$/
|
||||
|
||||
/**
|
||||
* Return the argument of a cd-style command line, or null when the line is not
|
||||
* one. An empty string means "bare cd" (the shell's home directory).
|
||||
* one. An empty string means "bare cd" (the shell's home directory). A bare
|
||||
* drive switch yields the drive root (`D:\`).
|
||||
*/
|
||||
export function cdArgument(line: string): string | null {
|
||||
const match = line.match(CD_COMMAND)
|
||||
if (!match) return null
|
||||
return (match[1] ?? '').trim()
|
||||
if (match) return (match[1] ?? '').trim()
|
||||
const drive = line.match(DRIVE_SWITCH)
|
||||
if (drive) return `${drive[1].toUpperCase()}:\\`
|
||||
return null
|
||||
}
|
||||
|
||||
/** Payload of an OSC 9 sequence that follows the ConEmu `9;<path>` convention. */
|
||||
|
||||
Reference in new issue
Block a user