Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c193a48a7 | ||
|
|
9467b78560 | ||
|
|
be7332db17 | ||
|
|
bbee1d642d |
No files matched your search
@@ -37,6 +37,9 @@ nsis:
|
|||||||
oneClick: false
|
oneClick: false
|
||||||
allowToChangeInstallationDirectory: true
|
allowToChangeInstallationDirectory: true
|
||||||
perMachine: false
|
perMachine: false
|
||||||
|
# Installed shortcuts read "OTerminal"; productName stays OpenTerminal so the
|
||||||
|
# userData dir and window title are untouched.
|
||||||
|
shortcutName: OTerminal
|
||||||
createDesktopShortcut: true
|
createDesktopShortcut: true
|
||||||
createStartMenuShortcut: true
|
createStartMenuShortcut: true
|
||||||
artifactName: ${productName}-${version}-setup.${ext}
|
artifactName: ${productName}-${version}-setup.${ext}
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "open-terminal",
|
"name": "open-terminal",
|
||||||
"productName": "OpenTerminal",
|
"productName": "OpenTerminal",
|
||||||
"version": "1.0.5",
|
"version": "1.0.6",
|
||||||
"description": "Open-source terminal with SSH, split panes, themes and fonts",
|
"description": "Open-source terminal with SSH, split panes, themes and fonts",
|
||||||
"main": "out/main/index.js",
|
"main": "out/main/index.js",
|
||||||
"author": "Bill",
|
"author": "Bill",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
|
|||||||
import { Button, Progress, Select, Switch, Tag } from 'antd'
|
import { Button, Progress, Select, Switch, Tag } from 'antd'
|
||||||
import type { AppInfo, ReleaseNote, UpdateState, UpdateStatus } from '@shared/ipc'
|
import type { AppInfo, ReleaseNote, UpdateState, UpdateStatus } from '@shared/ipc'
|
||||||
import { useSettingsStore } from './store'
|
import { useSettingsStore } from './store'
|
||||||
|
import appIconUrl from '../../../../build/icon.png'
|
||||||
|
|
||||||
const STATUS_TEXT: Record<UpdateStatus, string> = {
|
const STATUS_TEXT: Record<UpdateStatus, string> = {
|
||||||
idle: '点击检查更新',
|
idle: '点击检查更新',
|
||||||
@@ -34,7 +35,7 @@ export function AboutTab(): React.JSX.Element {
|
|||||||
return (
|
return (
|
||||||
<div className="about-tab">
|
<div className="about-tab">
|
||||||
<div className="about-header">
|
<div className="about-header">
|
||||||
<div className="about-logo">OT</div>
|
<img className="about-logo" src={appIconUrl} alt="OpenTerminal" draggable={false} />
|
||||||
<div>
|
<div>
|
||||||
<div className="about-name">OpenTerminal</div>
|
<div className="about-name">OpenTerminal</div>
|
||||||
<div className="about-version">
|
<div className="about-version">
|
||||||
|
|||||||
@@ -380,13 +380,8 @@
|
|||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
background: var(--chrome-hover);
|
object-fit: cover;
|
||||||
color: #3fd06a;
|
display: block;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.about-name {
|
.about-name {
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import '@xterm/xterm/css/xterm.css'
|
|||||||
import { ClearOutlined, CopyOutlined, FolderOpenOutlined, PauseOutlined, SearchOutlined, SelectOutlined, SnippetsOutlined, SoundOutlined } from '@ant-design/icons'
|
import { ClearOutlined, CopyOutlined, FolderOpenOutlined, PauseOutlined, SearchOutlined, SelectOutlined, SnippetsOutlined, SoundOutlined } from '@ant-design/icons'
|
||||||
import { getThemeById } from '@shared/theme'
|
import { getThemeById } from '@shared/theme'
|
||||||
import type { CommandItem } from '@shared/commands'
|
import type { CommandItem } from '@shared/commands'
|
||||||
import type { PtyDataEvent, PtyExitEvent } from '@shared/ipc'
|
|
||||||
import type { TerminalSettings } from '@shared/settings'
|
import type { TerminalSettings } from '@shared/settings'
|
||||||
import { useSettingsStore } from '@renderer/settings/store'
|
import { useSettingsStore } from '@renderer/settings/store'
|
||||||
import { writeBroadcast } from '@renderer/workspace/broadcastStore'
|
import { writeBroadcast } from '@renderer/workspace/broadcastStore'
|
||||||
import { compileRules, HighlightStream, type CompiledRule } from './highlightEngine'
|
import { compileRules, HighlightStream, type CompiledRule } from './highlightEngine'
|
||||||
|
import { subscribePtyData, subscribePtyExit } from './ptyDispatcher'
|
||||||
import { cdArgument, conemuCwd } from './cwdTracker'
|
import { cdArgument, conemuCwd } from './cwdTracker'
|
||||||
import { getSessionCwd, setSessionCwd } from '@renderer/workspace/sessionCwdStore'
|
import { getSessionCwd, setSessionCwd } from '@renderer/workspace/sessionCwdStore'
|
||||||
import './terminal.css'
|
import './terminal.css'
|
||||||
@@ -792,18 +792,19 @@ export const TerminalView: ForwardRefExoticComponent<TerminalViewProps & { ref?:
|
|||||||
let replayDone = false
|
let replayDone = false
|
||||||
const pending: string[] = []
|
const pending: string[] = []
|
||||||
const unsubscribes: (() => void)[] = [
|
const unsubscribes: (() => void)[] = [
|
||||||
window.api.onPtyData((e: PtyDataEvent) => {
|
// Routed via the shared dispatcher (one IPC listener for all panes)
|
||||||
if (e.id !== sessionId || deadRef.current) return
|
// instead of a per-pane global listener.
|
||||||
|
subscribePtyData(sessionId, (data: string) => {
|
||||||
|
if (deadRef.current) return
|
||||||
if (!replayDone) {
|
if (!replayDone) {
|
||||||
pending.push(e.data)
|
pending.push(data)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writeHighlighted(e.data)
|
writeHighlighted(data)
|
||||||
}),
|
}),
|
||||||
window.api.onPtyExit((e: PtyExitEvent) => {
|
subscribePtyExit(sessionId, (code: number) => {
|
||||||
if (e.id !== sessionId) return
|
|
||||||
deadRef.current = true
|
deadRef.current = true
|
||||||
setExitCode(e.exitCode)
|
setExitCode(code)
|
||||||
setDead(true)
|
setDead(true)
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import type { PtyDataEvent, PtyExitEvent } from '@shared/ipc'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single shared PTY stream listener. Every pane used to register its own
|
||||||
|
* window.api.onPtyData/onPtyExit listener, so each output chunk woke N
|
||||||
|
* listeners (N = pane count) and N-1 of them discarded it after an id
|
||||||
|
* compare. With many terminals under heavy output that per-chunk fan-out is
|
||||||
|
* pure overhead. Here one IPC listener dispatches through a Map keyed by
|
||||||
|
* sessionId: one lookup per chunk, and only the owning pane runs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
type DataHandler = (data: string) => void
|
||||||
|
type ExitHandler = (exitCode: number) => void
|
||||||
|
|
||||||
|
const dataHandlers = new Map<string, DataHandler>()
|
||||||
|
const exitHandlers = new Map<string, ExitHandler>()
|
||||||
|
let bound = false
|
||||||
|
|
||||||
|
function ensureBound(): void {
|
||||||
|
if (bound) return
|
||||||
|
bound = true
|
||||||
|
window.api.onPtyData((e: PtyDataEvent) => {
|
||||||
|
dataHandlers.get(e.id)?.(e.data)
|
||||||
|
})
|
||||||
|
window.api.onPtyExit((e: PtyExitEvent) => {
|
||||||
|
exitHandlers.get(e.id)?.(e.exitCode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Subscribe to one session's output. Returns the unsubscribe function. */
|
||||||
|
export function subscribePtyData(id: string, cb: DataHandler): () => void {
|
||||||
|
ensureBound()
|
||||||
|
dataHandlers.set(id, cb)
|
||||||
|
return () => {
|
||||||
|
// A rebind (template apply) may have replaced the handler already; only
|
||||||
|
// delete when this subscription is still the registered one.
|
||||||
|
if (dataHandlers.get(id) === cb) dataHandlers.delete(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Subscribe to one session's exit. Returns the unsubscribe function. */
|
||||||
|
export function subscribePtyExit(id: string, cb: ExitHandler): () => void {
|
||||||
|
ensureBound()
|
||||||
|
exitHandlers.set(id, cb)
|
||||||
|
return () => {
|
||||||
|
if (exitHandlers.get(id) === cb) exitHandlers.delete(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user