release: ship v1.5.0 with on-device writing suggestions
Adds next-sentence suggestions while typing, weekly input insights and a personal phrase memory to the desktop app, and fixes custom instructions so they process the text instead of inserting the instruction's own wording. Local model requests are now bounded and individually cancellable. Bumps the product version to 1.5.0 (Android/iOS build 1050000), refreshes the landing and web download links, and records the new INPUT feature rows and the open verification gaps in the infrastructure map.
This commit is contained in:
parent
99f06c253c
commit
5c11ee2fde
104 changed files with 14410 additions and 174 deletions
|
|
@ -677,9 +677,13 @@ export type KeyBindingActionId =
|
|||
| 'caption'
|
||||
| 'history-popup'
|
||||
| 'command-popup'
|
||||
| 'suggestion-accept'
|
||||
| 'suggestion-next'
|
||||
| 'suggestion-prev'
|
||||
| 'suggestion-dismiss'
|
||||
|
||||
/** 액션 그룹 (설정 화면 섹션) */
|
||||
export type KeyBindingActionGroup = 'voice' | 'window'
|
||||
export type KeyBindingActionGroup = 'voice' | 'window' | 'input'
|
||||
|
||||
export interface KeyBindingActionSpec {
|
||||
id: KeyBindingActionId
|
||||
|
|
@ -771,6 +775,44 @@ export const KEYBINDING_ACTIONS: readonly KeyBindingActionSpec[] = Object.freeze
|
|||
holdMode: false,
|
||||
doublePress: false,
|
||||
defaultBindings: [kb(0x43 /* C */, { ctrl: true, shift: true })]
|
||||
},
|
||||
{
|
||||
id: 'suggestion-accept',
|
||||
group: 'input',
|
||||
labelKey: 'keybinding.action.suggestionAccept',
|
||||
descriptionKey: 'keybinding.action.suggestionAccept.desc',
|
||||
holdMode: false,
|
||||
doublePress: false,
|
||||
// 사용자 요청으로 Ctrl+Alt+화살표 계열로 통일했다:
|
||||
// 오른쪽 수락 / 아래 다음 후보 / 위 이전 후보 / 왼쪽 닫기.
|
||||
defaultBindings: [kb(VK.ArrowRight, { ctrl: true, alt: true })]
|
||||
},
|
||||
{
|
||||
id: 'suggestion-next',
|
||||
group: 'input',
|
||||
labelKey: 'keybinding.action.suggestionNext',
|
||||
descriptionKey: 'keybinding.action.suggestionNext.desc',
|
||||
holdMode: false,
|
||||
doublePress: false,
|
||||
defaultBindings: [kb(VK.ArrowDown, { ctrl: true, alt: true })]
|
||||
},
|
||||
{
|
||||
id: 'suggestion-prev',
|
||||
group: 'input',
|
||||
labelKey: 'keybinding.action.suggestionPrev',
|
||||
descriptionKey: 'keybinding.action.suggestionPrev.desc',
|
||||
holdMode: false,
|
||||
doublePress: false,
|
||||
defaultBindings: [kb(VK.ArrowUp, { ctrl: true, alt: true })]
|
||||
},
|
||||
{
|
||||
id: 'suggestion-dismiss',
|
||||
group: 'input',
|
||||
labelKey: 'keybinding.action.suggestionDismiss',
|
||||
descriptionKey: 'keybinding.action.suggestionDismiss.desc',
|
||||
holdMode: false,
|
||||
doublePress: false,
|
||||
defaultBindings: [kb(VK.ArrowLeft, { ctrl: true, alt: true })]
|
||||
}
|
||||
])
|
||||
|
||||
|
|
@ -1035,6 +1077,67 @@ export function detectBindingConflicts(
|
|||
return conflicts
|
||||
}
|
||||
|
||||
/** 저장된 전체 단축키 설정에서 사용 불가·중복 바인딩을 수집한 결과. */
|
||||
export interface KeyBindingAuditIssue {
|
||||
kind: 'invalid' | 'conflict'
|
||||
actionId: KeyBindingActionId
|
||||
bindingIndex: number
|
||||
binding: KeyBinding
|
||||
reasonKey: string | null
|
||||
conflictActionIds: KeyBindingActionId[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 전체 바인딩 맵을 한 번에 검수한다.
|
||||
*
|
||||
* hold/double-press 예외는 detectBindingConflicts()의 기존 계약을 그대로 따른다.
|
||||
*/
|
||||
export function auditKeyBindingMap(map: Readonly<KeyBindingMap>): KeyBindingAuditIssue[] {
|
||||
const issues: KeyBindingAuditIssue[] = []
|
||||
const reportedPairs = new Set<string>()
|
||||
|
||||
for (const action of KEYBINDING_ACTIONS) {
|
||||
const bindings = map[action.id] ?? []
|
||||
for (const [bindingIndex, binding] of bindings.entries()) {
|
||||
const validation = validateBinding(binding)
|
||||
if (!validation.valid) {
|
||||
issues.push({
|
||||
kind: 'invalid',
|
||||
actionId: action.id,
|
||||
bindingIndex,
|
||||
binding: { ...binding },
|
||||
reasonKey: validation.reasonKey,
|
||||
conflictActionIds: []
|
||||
})
|
||||
}
|
||||
|
||||
const conflictActionIds = detectBindingConflicts(action.id, binding, map)
|
||||
.map((conflict) => conflict.actionId)
|
||||
.filter((otherActionId) => action.id.localeCompare(otherActionId) < 0)
|
||||
.filter((otherActionId) => {
|
||||
const pairKey = `${bindingKey(binding)}:${action.id}:${otherActionId}`
|
||||
if (reportedPairs.has(pairKey)) return false
|
||||
reportedPairs.add(pairKey)
|
||||
return true
|
||||
})
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
|
||||
if (conflictActionIds.length > 0) {
|
||||
issues.push({
|
||||
kind: 'conflict',
|
||||
actionId: action.id,
|
||||
bindingIndex,
|
||||
binding: { ...binding },
|
||||
reasonKey: null,
|
||||
conflictActionIds
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 검색 (드롭다운 필터)
|
||||
// ============================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue