fix(text-insert): restore every clipboard format after paste and never wipe non-text clipboards
This commit is contained in:
parent
190b6db284
commit
0adedf5e7b
5 changed files with 704 additions and 113 deletions
|
|
@ -0,0 +1,75 @@
|
|||
// src/main/services/text-insert/nut-keyboard-port.ts
|
||||
// 키 입력 시뮬레이션 포트와 nut-js 어댑터.
|
||||
// nut-js 는 ESM + 네이티브 모듈이므로 첫 사용 시 lazy dynamic import 한다.
|
||||
|
||||
import { getLogger } from '../LoggerService'
|
||||
import { D3ROError, ErrorCode } from '@d3ro/core/errors'
|
||||
|
||||
const logger = getLogger('TextInsertService')
|
||||
|
||||
/** 텍스트 삽입에 필요한 키 입력만 노출하는 포트. */
|
||||
export interface KeyboardPort {
|
||||
/** 플랫폼 붙여넣기 단축키 (macOS ⌘+V, 그 외 Ctrl+V) */
|
||||
paste(): Promise<void>
|
||||
/** 글자 단위 타이핑 (클립보드 비파괴) */
|
||||
type(text: string): Promise<void>
|
||||
}
|
||||
|
||||
type NutModule = typeof import('@nut-tree-fork/nut-js')
|
||||
|
||||
interface NutKeyboard {
|
||||
pressKey: NutModule['keyboard']['pressKey']
|
||||
releaseKey: NutModule['keyboard']['releaseKey']
|
||||
type: NutModule['keyboard']['type']
|
||||
Key: NutModule['Key']
|
||||
}
|
||||
|
||||
export class NutKeyboardPort implements KeyboardPort {
|
||||
private _nutKeyboard: NutKeyboard | null = null
|
||||
private _nutLoadPromise: Promise<void> | null = null
|
||||
|
||||
constructor(private readonly _platform: NodeJS.Platform = process.platform) {}
|
||||
|
||||
async paste(): Promise<void> {
|
||||
const nut = await this._ensureNut()
|
||||
const pasteModKey = this._platform === 'darwin' ? nut.Key.LeftSuper : nut.Key.LeftControl
|
||||
await nut.pressKey(pasteModKey, nut.Key.V)
|
||||
await nut.releaseKey(nut.Key.V, pasteModKey)
|
||||
}
|
||||
|
||||
async type(text: string): Promise<void> {
|
||||
const nut = await this._ensureNut()
|
||||
await nut.type(text)
|
||||
}
|
||||
|
||||
private async _ensureNut(): Promise<NutKeyboard> {
|
||||
if (this._nutKeyboard) return this._nutKeyboard
|
||||
|
||||
if (!this._nutLoadPromise) {
|
||||
this._nutLoadPromise = (async () => {
|
||||
try {
|
||||
const nut = await import('@nut-tree-fork/nut-js')
|
||||
this._nutKeyboard = {
|
||||
pressKey: nut.keyboard.pressKey.bind(nut.keyboard),
|
||||
releaseKey: nut.keyboard.releaseKey.bind(nut.keyboard),
|
||||
type: nut.keyboard.type.bind(nut.keyboard),
|
||||
Key: nut.Key
|
||||
}
|
||||
logger.info('nut-js loaded successfully')
|
||||
} catch (error) {
|
||||
logger.error(`Failed to load nut-js: ${error instanceof Error ? error.message : String(error)}`)
|
||||
throw new D3ROError(
|
||||
ErrorCode.TextInsertKeySimulationFailed,
|
||||
'nut-js 로드 실패. 키보드 시뮬레이션을 사용할 수 없습니다.'
|
||||
)
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
await this._nutLoadPromise
|
||||
if (!this._nutKeyboard) {
|
||||
throw new D3ROError(ErrorCode.TextInsertKeySimulationFailed, 'nut-js not available')
|
||||
}
|
||||
return this._nutKeyboard
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue