Files
BoosAPI/desktop/libs/stream-monaco/test/mockWrapper.ts
T
boos e0a5f57793 feat(desktop): embed BoosAPI Desktop client as a subproject
Vendor the rebranded BoosAPI Desktop client (Tauri + Vue) into metapi as
a monorepo subdirectory under desktop/. Source only — node_modules,
dist, Cargo target and the upstream .git history are excluded.

desktop/ keeps its own independent Cargo workspace (desktop/src-tauri
with crates/*); it is not part of the backend boosapi crate, so a
`cargo check` at the metapi root still compiles only the backend. Build
the desktop app from desktop/src-tauri per desktop/README.md.

Note: app icons are still placeholder assets pending the final BoosAPI
logo. The updater signing keys live outside the repo and are not
included.
2026-06-22 11:32:57 +08:00

101 lines
2.7 KiB
TypeScript

export function createMockWrapper(opts: { updateThrottleMs?: number } = {}) {
const appendBuffer: string[] = []
let appendBufferScheduled = false
let appendImmediate: ReturnType<typeof setImmediate> | null = null
let pendingUpdate: { code: string } | null = null
let lastKnownCode = ''
let throttleMs = opts.updateThrottleMs ?? 50
let lastFlushTime = 0
let throttleTimer: ReturnType<typeof setTimeout> | null = null
const model = {
value: '',
getValue() { return this.value },
setValue(v: string) { this.value = v },
getLineCount() { return this.value.split('\n').length },
getLanguageId() { return 'javascript' },
}
function clearBufferedAppends() {
appendBuffer.length = 0
appendBufferScheduled = false
if (appendImmediate != null) {
clearImmediate(appendImmediate)
appendImmediate = null
}
}
function scheduleAppendFlush() {
appendBufferScheduled = true
appendImmediate = setImmediate(() => {
appendImmediate = null
const text = appendBuffer.join('')
appendBuffer.length = 0
appendBufferScheduled = false
model.value += text
})
}
function flushPendingUpdate() {
lastFlushTime = Date.now()
if (!pendingUpdate)
return
const { code } = pendingUpdate
pendingUpdate = null
let prev: string
if (appendBuffer.length > 0) {
clearBufferedAppends()
prev = model.getValue()
lastKnownCode = prev
}
else {
prev = lastKnownCode || model.getValue()
if (!lastKnownCode)
lastKnownCode = prev
}
if (prev === code)
return
if (code.startsWith(prev)) {
const suffix = code.slice(prev.length)
if (suffix) {
appendBuffer.push(suffix)
// schedule append flush on next tick
if (!appendBufferScheduled) {
scheduleAppendFlush()
}
}
lastKnownCode = code
return
}
model.setValue(code)
lastKnownCode = code
}
function updateCode(code: string) {
pendingUpdate = { code }
// emulate raf via setImmediate
setImmediate(() => {
if (!throttleMs) { flushPendingUpdate(); return }
const now = Date.now()
const since = now - lastFlushTime
if (since >= throttleMs) { flushPendingUpdate(); return }
if (throttleTimer != null)
return
const wait = throttleMs - since
throttleTimer = setTimeout(() => { throttleTimer = null; setImmediate(flushPendingUpdate) }, wait)
})
}
function setThrottleMs(ms: number) {
throttleMs = ms
}
function getThrottleMs() {
return throttleMs
}
return { updateCode, model, setThrottleMs, getThrottleMs }
}
export type MockWrapper = ReturnType<typeof createMockWrapper>