mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
refactor(mobile): extract the terminal message-bridge slice into a module
The script and the document end in the same slice, so the slice splits in two at the point where the IIFE closes: the script half becomes a module, the document half stays text. The byte pin proves the join is unchanged. The second catch keeps its binding: it names the error and reports it. Counts: qualified 1, scope declarations 0, rebindings 1, braced bodies 0, unbound catches 1, number properties 0. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
This commit is contained in:
@@ -98,3 +98,18 @@ export declare function isClickMouseTrackingMode(mode: string): boolean
|
||||
|
||||
/** `write-queue`: the trailing bytes a DECSET scan must carry into the next chunk. */
|
||||
export declare function extractMouseModeScanTail(input: string): string
|
||||
|
||||
/** `host-message-router`: routes one decoded host message. */
|
||||
export declare function handleMsg(msg: unknown): void
|
||||
|
||||
/** `terminal-init-and-write`: reports an engine failure to the host. */
|
||||
export declare function reportEngineError(summary: string, cause: unknown, fatal: unknown): void
|
||||
|
||||
/** `terminal-init-and-write`: re-fits the row count to the current viewport. */
|
||||
export declare function adjustRowsForViewport(): void
|
||||
|
||||
/** `smooth-scroll-and-cell-geometry`: clamps the pan offsets to the scaled surface. */
|
||||
export declare function clampPan(): void
|
||||
|
||||
/** `smooth-scroll-and-cell-geometry`: writes the pan and scale onto the surface transform. */
|
||||
export declare function updateTransform(): void
|
||||
|
||||
@@ -124,6 +124,8 @@ export type TerminalDocumentScope = {
|
||||
initialOscLinkRowOffset: number
|
||||
/** `runtime-state`: the escape byte every report is prefixed with. */
|
||||
ESC: string
|
||||
/** `terminal-init-and-write`: whether the terminal has ever reached ready. */
|
||||
everReady: boolean
|
||||
/** `runtime-state`: the C1 form of the control sequence introducer. */
|
||||
C1_CSI: string
|
||||
/** `runtime-state`: the tail of the last chunk, in case a DECSET straddles two writes. */
|
||||
@@ -210,6 +212,7 @@ export function createTerminalDocumentScope(): TerminalDocumentScope {
|
||||
initialOscLinks: [],
|
||||
initialOscLinkRowOffset: 0,
|
||||
ESC: String.fromCharCode(27),
|
||||
everReady: false,
|
||||
C1_CSI: String.fromCharCode(155),
|
||||
mouseModeScanTail: '',
|
||||
trackedMouseTrackingMode: 'none',
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { emitTerminalDocumentModule } from '../../../scripts/build-terminal-document-script.mjs'
|
||||
import { TERMINAL_HTML_MESSAGE_BRIDGE } from '../terminal-webview-html/message-bridge-and-document-close'
|
||||
import { compareTerminalDocumentScripts } from './terminal-document-equivalence.test-support'
|
||||
|
||||
const modulePath = fileURLToPath(new URL('./message-bridge.ts', import.meta.url))
|
||||
|
||||
describe('the message-bridge module', () => {
|
||||
it('emits the script the document carries, modulo the five normalisations', async () => {
|
||||
const emitted = await emitTerminalDocumentModule(modulePath)
|
||||
expect(compareTerminalDocumentScripts(TERMINAL_HTML_MESSAGE_BRIDGE, emitted, 'scope')).toEqual({
|
||||
equivalent: true,
|
||||
normalisations: {
|
||||
// Only the ready flag, read to decide whether a failed init is fatal.
|
||||
qualifiedReferences: 1,
|
||||
scopeFieldDeclarations: 0,
|
||||
rebindings: 1,
|
||||
bracedBodies: 0,
|
||||
// The parse guard. The second catch names its error and reports it, so it keeps its binding.
|
||||
unboundCatches: 1,
|
||||
numberProperties: 0
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
adjustRowsForViewport,
|
||||
applyFitScale,
|
||||
clampPan,
|
||||
handleMsg,
|
||||
notify,
|
||||
reportEngineError,
|
||||
repositionOverlay,
|
||||
updateTransform
|
||||
} from './document-externals'
|
||||
import { scope } from './document-scope'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
Terminal?: unknown
|
||||
}
|
||||
}
|
||||
|
||||
/** The decoded host message; only its type is read here, by the error reporter. */
|
||||
type TerminalHostMessage = { type?: unknown } | undefined
|
||||
|
||||
export function handleIncomingMessage(e: Event & { data?: TerminalHostMessage | string }) {
|
||||
let msg: TerminalHostMessage
|
||||
try {
|
||||
msg = typeof e.data === 'string' ? JSON.parse(e.data) : e.data
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
try {
|
||||
handleMsg(msg)
|
||||
} catch (ex) {
|
||||
reportEngineError(
|
||||
msg && msg.type === 'init' ? 'terminal init failed' : 'terminal message failed',
|
||||
ex,
|
||||
msg && msg.type === 'init' && !scope.everReady
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('message', handleIncomingMessage)
|
||||
|
||||
document.addEventListener('message', handleIncomingMessage)
|
||||
|
||||
window.addEventListener('resize', function () {
|
||||
// Why: viewport changed (keyboard open/close, orientation, RN container
|
||||
// size update). Re-fit so the scale matches the new vpWidth — without
|
||||
// this, opening the keyboard leaves the terminal at the old scale even
|
||||
// though there's now less vertical room and the fit ratio may differ.
|
||||
applyFitScale('window-resize')
|
||||
adjustRowsForViewport()
|
||||
repositionOverlay()
|
||||
clampPan()
|
||||
updateTransform()
|
||||
})
|
||||
|
||||
if (window.Terminal) {
|
||||
notify({ type: 'web-ready' })
|
||||
} else {
|
||||
reportEngineError('terminal engine missing', 'xterm failed to load', true)
|
||||
}
|
||||
@@ -11,7 +11,10 @@ import { TERMINAL_HTML_MOUSE_REPORT_AND_SCROLL_ROUTING } from './terminal-webvie
|
||||
import { TERMINAL_HTML_SMOOTH_SCROLL_AND_CELL_GEOMETRY } from './terminal-webview-html/smooth-scroll-and-cell-geometry'
|
||||
import { TERMINAL_HTML_SELECTION_OVERLAY } from './terminal-webview-html/selection-overlay'
|
||||
import { TERMINAL_HTML_SURFACE_TOUCH_GESTURES } from './terminal-webview-html/surface-touch-gestures'
|
||||
import { TERMINAL_HTML_MESSAGE_BRIDGE_AND_DOCUMENT_CLOSE } from './terminal-webview-html/message-bridge-and-document-close'
|
||||
import {
|
||||
TERMINAL_HTML_DOCUMENT_CLOSE,
|
||||
TERMINAL_HTML_MESSAGE_BRIDGE
|
||||
} from './terminal-webview-html/message-bridge-and-document-close'
|
||||
|
||||
export { MOBILE_TERMINAL_CARET_OPTIONS } from './terminal-webview-html/theme'
|
||||
|
||||
@@ -32,7 +35,8 @@ export const XTERM_HTML = [
|
||||
TERMINAL_HTML_SMOOTH_SCROLL_AND_CELL_GEOMETRY,
|
||||
TERMINAL_HTML_SELECTION_OVERLAY,
|
||||
TERMINAL_HTML_SURFACE_TOUCH_GESTURES,
|
||||
TERMINAL_HTML_MESSAGE_BRIDGE_AND_DOCUMENT_CLOSE
|
||||
TERMINAL_HTML_MESSAGE_BRIDGE,
|
||||
TERMINAL_HTML_DOCUMENT_CLOSE
|
||||
].join('')
|
||||
|
||||
export const XTERM_WEBVIEW_SOURCE = { html: XTERM_HTML }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const TERMINAL_HTML_MESSAGE_BRIDGE_AND_DOCUMENT_CLOSE = ` function handleIncomingMessage(e) {
|
||||
// The script ends here and the document ends below: the boundary is where the IIFE closes.
|
||||
export const TERMINAL_HTML_MESSAGE_BRIDGE = ` function handleIncomingMessage(e) {
|
||||
var msg;
|
||||
try {
|
||||
msg = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
|
||||
@@ -37,7 +38,9 @@ export const TERMINAL_HTML_MESSAGE_BRIDGE_AND_DOCUMENT_CLOSE = ` function handl
|
||||
} else {
|
||||
reportEngineError('terminal engine missing', 'xterm failed to load', true);
|
||||
}
|
||||
})();
|
||||
`
|
||||
|
||||
export const TERMINAL_HTML_DOCUMENT_CLOSE = `})();
|
||||
</script>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
Reference in New Issue
Block a user