release: ship v1.6.0 with paged suggestions and a cleaner phrase memory
Next-sentence suggestions now arrive one at a time up to twelve, shown three per page with Ctrl+Alt+Up/Down to move, Left/Right to page, Enter to accept and Esc to close; old default bindings migrate and the panel guide follows the live bindings. The overlay is redesigned, stays put while candidates stream and sits outside the input box when no caret is reported. The personal phrase memory stops learning from terminals, code editors and the coding-agent hub, ignores symbol-heavy lines and empty-field placeholders, and prunes existing entries that break those rules. Fixes suggestion keys starting dictation, installs stuck on a pre-1.5.0 speech engine without the focus endpoint, Ollama runner windows flashing while typing, the speech engine starting twice, and cold-model timeouts. Live captions can be dragged to a remembered position and show a waiting notice until the first line arrives. Bumps the product version to 1.6.0 (Android/iOS build 1060000).
This commit is contained in:
parent
856e375f3e
commit
2fe20fa7b5
71 changed files with 2469 additions and 366 deletions
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
import { EventEmitter, once } from 'events'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { createReadStream, createWriteStream, existsSync, statSync } from 'node:fs'
|
||||
import { createReadStream, createWriteStream, existsSync, readFileSync, statSync, writeFileSync } from 'node:fs'
|
||||
import { mkdir, rm, stat } from 'node:fs/promises'
|
||||
import { Readable, Writable } from 'node:stream'
|
||||
import { pipeline } from 'node:stream/promises'
|
||||
|
|
@ -23,6 +23,7 @@ import * as tar from 'tar'
|
|||
import { getLogger } from './LoggerService'
|
||||
import { D3ROError, ErrorCode } from '@d3ro/core/errors'
|
||||
import { RUNTIME_FEED_URL } from '../update-feed'
|
||||
import { compareVersions } from '../update-policy'
|
||||
|
||||
const logger = getLogger('RuntimeProvisioner')
|
||||
|
||||
|
|
@ -68,6 +69,17 @@ export interface RuntimeStatus {
|
|||
}
|
||||
|
||||
const RUNTIME_DIR_NAME = 'runtime'
|
||||
/** 설치된 런타임의 버전(runtime.json version)을 남기는 마커 */
|
||||
const RUNTIME_VERSION_FILE = '.runtime-version'
|
||||
|
||||
/**
|
||||
* 이 앱이 요구하는 런타임 최소 버전. 사이드카 API 가 바뀔 때만 올린다.
|
||||
* 1.5.0 — UIA 브리지(`/uia/focus`)가 처음 들어간 사이드카. ffmpeg 는 CLI 가 안정적이라 확인하지 않는다.
|
||||
*/
|
||||
const RUNTIME_MIN_VERSION: Record<RuntimeComponent, string | null> = {
|
||||
sidecar: '1.5.0',
|
||||
ffmpeg: null
|
||||
}
|
||||
const DOWNLOAD_TIMEOUT_MS = 120_000
|
||||
/** 부품 다운로드 재시도 횟수 — 전송 중 잘림/일시적 네트워크 오류 대비 */
|
||||
const PART_DOWNLOAD_ATTEMPTS = 3
|
||||
|
|
@ -106,6 +118,31 @@ class RuntimeProvisioner extends EventEmitter {
|
|||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 설치된 런타임이 이 앱이 요구하는 최소 버전 이상인지.
|
||||
*
|
||||
* 사이드카는 설치본에 넣지 않고 별도로 내려받는다(위 주석) — 그래서 앱이 업데이트돼
|
||||
* 새 엔드포인트를 요구해도(예: v1.5.0의 `/uia/focus`), 예전에 내려받은 사이드카가
|
||||
* 남아 있으면 새 앱은 그 구버전 엔진과 계속 통신한다. 마커가 없거나 최소 버전보다
|
||||
* 낮으면 다시 받는다. 앱 버전과 "같음" 으로 비교하지 않는다 — 그러면 엔진이 그대로인
|
||||
* 릴리스마다 모든 사용자가 100MB를 다시 받는다.
|
||||
*/
|
||||
private _isCurrentVersion(component: RuntimeComponent): boolean {
|
||||
const minimum = RUNTIME_MIN_VERSION[component]
|
||||
if (minimum === null) return true
|
||||
try {
|
||||
const installed = readFileSync(this._versionMarkerPath(component), 'utf8').trim()
|
||||
const order = compareVersions(installed, minimum)
|
||||
return order !== null && order >= 0
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private _versionMarkerPath(component: RuntimeComponent): string {
|
||||
return join(this.componentDir(component), RUNTIME_VERSION_FILE)
|
||||
}
|
||||
|
||||
getStatus(): RuntimeStatus[] {
|
||||
return RUNTIME_COMPONENTS.map((component) => {
|
||||
const binary = this.binaryPath(component)
|
||||
|
|
@ -129,7 +166,7 @@ class RuntimeProvisioner extends EventEmitter {
|
|||
* 동시 호출은 같은 작업을 공유한다.
|
||||
*/
|
||||
async ensure(component: RuntimeComponent): Promise<string> {
|
||||
if (this.isInstalled(component)) {
|
||||
if (this.isInstalled(component) && this._isCurrentVersion(component)) {
|
||||
return this.binaryPath(component)
|
||||
}
|
||||
|
||||
|
|
@ -190,6 +227,8 @@ class RuntimeProvisioner extends EventEmitter {
|
|||
)
|
||||
}
|
||||
|
||||
writeFileSync(this._versionMarkerPath(component), index.version, 'utf8')
|
||||
|
||||
this._emitProgress(component, 'done', 100, entry.totalSize, entry.totalSize, 0)
|
||||
logger.info(
|
||||
`런타임 설치 완료: ${component} (${(entry.totalSize / 1048576).toFixed(1)}MiB, ${Date.now() - started}ms)`,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue