Why: The first S4 pass makes the project the navigation context, keeps deep views in one overlay, and leaves domain panels empty until a plugin declares them. Evidence: npx tsc -b and npm test (61 passed) in modules/frontend. Impact: Top bar, project sidebar, inbox all/todo filter, one-way scroll sync, drag-and-drop on the whole chat column, and local molv/system themes. Refs: feature/s4-shell-overhaul Co-authored-by: Cursor <cursoragent@cursor.com>
31 lines
1.0 KiB
TypeScript
31 lines
1.0 KiB
TypeScript
// 中栏 → 右栏单向滚动同步,以及互定位后的 1.2s 闪烁。
|
||
// 比例按可滚动范围计算;任一侧没有溢出时不同步,避免把静止栏拽动。
|
||
|
||
export interface ScrollBox {
|
||
scrollTop: number
|
||
scrollHeight: number
|
||
clientHeight: number
|
||
}
|
||
|
||
export function scrollRatio(box: ScrollBox): number | null {
|
||
const range = box.scrollHeight - box.clientHeight
|
||
if (range <= 0) return null
|
||
return box.scrollTop / range
|
||
}
|
||
|
||
export function scrollTopForRatio(box: Pick<ScrollBox, 'scrollHeight' | 'clientHeight'>, ratio: number): number | null {
|
||
const range = box.scrollHeight - box.clientHeight
|
||
if (range <= 0) return null
|
||
const clamped = Math.min(1, Math.max(0, ratio))
|
||
return clamped * range
|
||
}
|
||
|
||
/** 给已定位的节点加上 mk-flash,1.2s 后摘掉。 */
|
||
export function flashElement(el: Element | null) {
|
||
if (!el || typeof window === 'undefined') return
|
||
el.classList.remove('mk-flash')
|
||
void (el as HTMLElement).offsetWidth
|
||
el.classList.add('mk-flash')
|
||
window.setTimeout(() => el.classList.remove('mk-flash'), 1200)
|
||
}
|