mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-11-11 08:42:45 +00:00
* fix(#3398): automated AI fix * fix: address Codex review on PR #3400 * fix: address Codex review on PR #3400 * fix: address Codex review on PR #3400 * fix: address Codex review on PR #3400 * fix: address Codex review on PR #3400 * fix: address Codex review on PR #3400 * fix: address Codex review on PR #3400 * fix: address Codex review on PR #3400 * fix: address Codex review on PR #3400 * fix: address Codex review on PR #3400 * fix(terminal): protect popup and sanitized multiline pastes * fix(terminal): preserve input state across paste confirmation * fix(terminal): retain serial character-mode paste echo * test(terminal): cover paced input write options * fix(terminal): retain telnet echo for paced pastes * test(terminal): scope serial buffer assertion to serial sessions * fix(terminal): align runtime write options with bridge contract * fix(terminal): preserve bookkeeping for all paced pastes * fix(terminal): record confirmed paste lines in order * fix: record paced paste commands after transport writes * fix: omit paste receipts for filtered input * fix: cancel superseded paste batches across input paths * fix: classify each acknowledged paste line before recording * fix: stop paced paste when editing buffered serial input * fix: preserve recording pause boundaries and serial edit cancellation * fix: treat paced terminal pastes as user input * fix: preserve paste drafts until write and snippet broadcast origin * fix: retain filtered paste drafts and invalidate urgent input ownership * fix: broadcast paced user paste only on guarded line receipts --------- Co-authored-by: netcatty-bot <308658023+netcatty-bot@users.noreply.github.com> Co-authored-by: bincxz <16399091+binaricat@users.noreply.github.com>
82 lines
2.4 KiB
TypeScript
82 lines
2.4 KiB
TypeScript
/**
|
|
* App-level store for the "paste multiple lines" confirmation dialog (#3398).
|
|
*
|
|
* Terminal paste handlers call `requestMultilinePasteConfirm` from anywhere in
|
|
* the renderer and await the user's decision; `MultilinePasteConfirmHost`
|
|
* (mounted once in App) renders the dialog and resolves the pending request.
|
|
*/
|
|
|
|
export type MultilinePasteConfirmAction = "send" | "line-by-line" | "cancel";
|
|
|
|
export interface MultilinePasteConfirmRequest {
|
|
id: string;
|
|
/** Clipboard text the dialog previews (editable by the user). */
|
|
text: string;
|
|
lineCount: number;
|
|
charCount: number;
|
|
/** Restore the originating terminal's focus after the modal releases it. */
|
|
onClose?: () => void;
|
|
}
|
|
|
|
export interface MultilinePasteConfirmResponse {
|
|
action: MultilinePasteConfirmAction;
|
|
/** Text to send: the preview content, possibly edited by the user. */
|
|
text: string;
|
|
}
|
|
|
|
interface PendingRequest {
|
|
request: MultilinePasteConfirmRequest;
|
|
resolve: (response: MultilinePasteConfirmResponse) => void;
|
|
}
|
|
|
|
let pending: PendingRequest | null = null;
|
|
let nextRequestId = 0;
|
|
const listeners = new Set<() => void>();
|
|
|
|
const emit = (): void => {
|
|
for (const listener of listeners) listener();
|
|
};
|
|
|
|
export function requestMultilinePasteConfirm(
|
|
request: Omit<MultilinePasteConfirmRequest, "id">,
|
|
): Promise<MultilinePasteConfirmResponse> {
|
|
// Only one confirmation dialog can be pending; a newer paste supersedes the
|
|
// older one (the stale promise resolves as "cancel" so its paste is dropped).
|
|
if (pending) {
|
|
const stale = pending;
|
|
pending = null;
|
|
stale.resolve({ action: "cancel", text: stale.request.text });
|
|
}
|
|
nextRequestId += 1;
|
|
const requestWithId: MultilinePasteConfirmRequest = {
|
|
...request,
|
|
id: `multiline-paste-${nextRequestId}`,
|
|
};
|
|
return new Promise((resolve) => {
|
|
pending = { request: requestWithId, resolve };
|
|
emit();
|
|
});
|
|
}
|
|
|
|
export function respondMultilinePasteConfirm(
|
|
action: MultilinePasteConfirmAction,
|
|
text?: string,
|
|
): void {
|
|
const current = pending;
|
|
if (!current) return;
|
|
pending = null;
|
|
current.resolve({ action, text: text ?? current.request.text });
|
|
emit();
|
|
}
|
|
|
|
/** Snapshot for `useSyncExternalStore`; stable reference while pending. */
|
|
export const getPendingMultilinePasteConfirm =
|
|
(): MultilinePasteConfirmRequest | null => pending?.request ?? null;
|
|
|
|
export function subscribeMultilinePasteConfirm(listener: () => void): () => void {
|
|
listeners.add(listener);
|
|
return () => {
|
|
listeners.delete(listener);
|
|
};
|
|
}
|