Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
e897968
feat(app): 面间直投通道——会话选中改走指名投递
ProjectNyaser Oct 2, 2026
dd9d0cd
fix(app): review 处置——收尾重置排在注册路由之前,通道循环分代
ProjectNyaser Oct 2, 2026
baccf85
refactor(shared): 意图描述符表成单一事实源,hub 按参与面筛投递
ProjectNyaser Oct 2, 2026
cce0687
refactor(ui): 跨面意图落地面抽成通用机器,selection 改坐它
ProjectNyaser Oct 2, 2026
29eebaf
feat(app): panel-view 搬到直投通道,删掉旧三件名
ProjectNyaser Oct 3, 2026
ec0f07a
feat(app): settings-view 搬到直投通道,删掉旧三件名
ProjectNyaser Oct 3, 2026
12cd0c9
feat(app): 四个 command 搬到直投通道,删掉各 integration 的 drain 机器
ProjectNyaser Oct 3, 2026
7475f5c
refactor(ui): 撤下旧的意图协议:公开面只剩通用三件
ProjectNyaser Oct 3, 2026
ed80866
fix(ui): 通道加两道闸,掉掉“一秒上百次 poll”那一类循环
ProjectNyaser Oct 3, 2026
d03de89
fix(shared): since 不认数字字符串——poll 每次全量回放、永不挂起
ProjectNyaser Oct 3, 2026
af1e23d
fix(ci): audit 放宽无补丁可钉的那条上游告警(http-cache-semantics)
ProjectNyaser Oct 3, 2026
a3a82ce
chore(bundle): 关掉上游 base 的两行遥测(otel / session-telemetry-otel)
ProjectNyaser Oct 3, 2026
4686c88
chore: 把遥测整棵子树从依赖树里拿掉(不再需要 audit 例外)
ProjectNyaser Oct 3, 2026
02c1cfe
fix(pack): 本地替身包随工位物化(否则 file: overrides 在工位里 ENOENT)
ProjectNyaser Oct 3, 2026
d6ec688
refactor(ui): 面间只走同页广播,拆掉共享存储与直投通道
ProjectNyaser Oct 3, 2026
7bd1ca5
fix(app): 接住 CodeRabbit 三条,扫掉拆通道留下的死代码
ProjectNyaser Oct 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions integrations/tsconfig.paths.json
Original file line number Diff line number Diff line change
Expand Up @@ -1634,8 +1634,7 @@
"../node_modules/.pnpm/node_modules/@deepseek-ai/dsh-session-stats"
],
"@deepseek-ai/dsh-session-telemetry": [
"../vendor/deepseek-harness/packages/session/session-telemetry/src/index.ts",
"../node_modules/.pnpm/node_modules/@deepseek-ai/dsh-session-telemetry"
"../vendor/deepseek-harness/packages/session/session-telemetry/src/index.ts"
],
"@deepseek-ai/dsh-session-telemetry-otel": [
"../vendor/deepseek-harness/packages/session/session-telemetry-otel/src/index.ts",
Expand Down
46 changes: 18 additions & 28 deletions integrations/ui-layout/files/src/client/AppFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,40 +145,30 @@ export function AppFrame({
const surface = ROLE_SURFACES[role ?? ''] ?? 'standalone'

// 跨面主面板:本面(workspace)接收 FP 侧栏选中的而行(FP 整面只有侧栏、没有中列,
// 面板页只能由本面打开)。读快照一次 + 订阅变化;FP 不在场时本面不动。
// 面板页只能由本面打开)。走面间直投通道的通用落地面:登记一件回调,帧到了就落地;
// 首挂的快照帧会把当前值送过来,所以不再自己读一次快照。FP 不在场时本面不动。
useEffect(() => {
if (surface !== 'workspace') return
const bridge = (window as {
__DSHANA__?: {
readPanelView?: () => Promise<{ panelId: string | null }>
onPanelViewChanged?: (listener: () => void) => () => void
registerIntentLanding?: (kind: string, handler: (payload: unknown) => void) => () => void
}
}).__DSHANA__
const read = bridge?.readPanelView
const onChanged = bridge?.onPanelViewChanged
if (read === undefined || onChanged === undefined) return
let active = true
const register = bridge?.registerIntentLanding
if (register === undefined) return
let applied: string | null | undefined
const apply = (): void => {
void read().then((next) => {
if (!active) return
const panelId = next?.panelId ?? null
if (panelId === applied) return
applied = panelId
try {
actions.selectPanel(panelId as MainPanelId | null)
} catch (error: unknown) {
// 面板条目还没挂上(本面刚起)或已被摘掉:保持当前选中。
console.warn('[dshana/ui-layout] 跨面面板没能落地。', error)
}
}, () => { /* 读失败保持当前 */ })
}
const off = onChanged(apply)
apply()
return () => {
active = false
off()
}
const off = register('panel-view', (payload) => {
const panelId = (payload as { panelId?: string | null } | null)?.panelId ?? null
if (panelId === applied) return
applied = panelId
try {
actions.selectPanel(panelId as MainPanelId | null)
} catch (error: unknown) {
// 面板条目还没挂上(本面刚起)或已被摘掉:保持当前选中。
console.warn('[dshana/ui-layout] 跨面面板没能落地。', error)
}
})
return () => { off() }
}, [surface, actions])

// Track the frame's own box (not the window): rAF-throttled ResizeObserver.
Expand Down Expand Up @@ -370,7 +360,7 @@ export function AppFrame({
各自就是一幅 app 视图,自己拥有这一层。门必须按“排掉局部面”写而不是枚举整幅面:
同一套插件集在每个面上各挂一遍,注册到 `shell.overlay` 的条目否则会在每个文档里
各渲染一份(用户插件尤其明显)。FP 里发起的那些面由整幅面经 surface-bridge 的
overlay 意图通道(readIntent / writeIntent / onIntentChanged)落地。 */}
overlay 意图通道(publishIntent / registerIntentLanding)落地。 */}
{surface !== 'navigation' && (
<div className={css.overlayLayer} data-shell-overlay>
{overlays}
Expand Down
19 changes: 14 additions & 5 deletions integrations/ui-session/files/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -760,19 +760,23 @@ function installCrossSurfaceSelection(ctx: Context): void {
let localAt = 0
// 只读会话流面:钉住的那一段是否已经开过。开过之后本面不再被任何外部选中改动。
let pinnedApplied = false
// 最近一次收到的共用选中(随通知带来):有它就不必再回读记录。
let notified: { id: string | null; at: number } | null = null
let seen = mainSessionId(list)
// 本面所属文档的视图所有者导航面;ui-workspace 到场前缺席(见文件头那一段)。
let navigate: UiWorkspace | undefined
let warnedAbsentNavigator = false
// 面上线时列表已就绪 ⇒ 恢复早已落地,往后的选中变化都算用户动作。
let settled = snap0.phase === 'ready'

/** 共用的当前选中(带它的写入时刻;没有就报 null 与 0)。 */
const sharedSelection = (): Promise<{ id: string | null; at: number }> =>
read().then((next) => ({
/** 共用的当前选中(带它的写入时刻;没有就报 null 与 0)。收到过通知就直接用它。 */
const sharedSelection = (): Promise<{ id: string | null; at: number }> => {
if (notified !== null) return Promise.resolve(notified)
return read().then((next) => ({
id: next?.sessionId ?? null,
at: typeof next?.at === 'number' ? next.at : 0,
}))
}

/** 本次该显示哪一段:钉住的 sid 优先,否则共用的当前选中。 */
const desired = async (): Promise<{ id: string | null; at: number }> => {
Expand Down Expand Up @@ -832,7 +836,11 @@ function installCrossSurfaceSelection(ctx: Context): void {

ctx.effect(() => {
// 只读会话流面不听共用选中的广播:它的目标是钉住的那一段,且只认一次(见 applyRemote)。
const off = readOnly ? () => {} : onChanged(applyRemote)
const off = readOnly ? () => {} : onChanged((sessionId, at) => {
// 值随通知来,直接采用,不再回读:省一次取数,也不会读到一个还没写完的值。
notified = { id: sessionId ?? null, at }
applyRemote()
})
const offList = list.subscribe(() => {
const snap = list.getSnapshot()
const current = mainSessionId(list)
Expand All @@ -848,9 +856,10 @@ function installCrossSurfaceSelection(ctx: Context): void {
if (!settled) {
// 恢复落地的第一跳:只记录,随后与共享状态对一次(谁更新谁说了算)。
settled = true
} else if (!readOnly && current !== null) {
} else if (!readOnly && current !== null && current !== notified?.id) {
// 只读面不宣告本地变化:它只是在看,不该把另一个面的选中拉过来。
// 本地无选中也不宣告:空值在对面上表示「没有意见」,没有要传达的动作。
// 与刚收到的通知同值也不宣告:那是落地回声,写回去就是把一次点击放大成两次写。
localAt = Date.now()
void Promise.resolve(write(current)).catch(() => { /* 写失败不回滚本地 */ })
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,19 @@ export type SettingsView = { open: boolean; section: string | null }

type HanaSettingsBridge = {
role?: string
readSettingsView?: () => Promise<SettingsView>
writeSettingsView?: (next: SettingsView) => Promise<void>
onSettingsViewChanged?: (listener: () => void) => () => void
/** 通用面(settings-view 已上直投通道):读当前值 / 指名投递 / 登记落地。 */
readIntentState?: (kind: string) => Promise<{ value: unknown; at: number } | null>
publishIntent?: (kind: string, payload: unknown) => Promise<unknown>
registerIntentLanding?: (kind: string, handler: (payload: unknown) => void) => () => void
}

/** 帧/快照带来的原始载荷归一成本组件认的视图形状。 */
function normalizeView(raw: unknown): SettingsView {
const value = (raw ?? {}) as { open?: unknown; section?: unknown }
return {
open: value.open === true,
section: typeof value.section === 'string' && value.section ? value.section : null,
}
}

function hanaBridge(): HanaSettingsBridge | undefined {
Expand Down Expand Up @@ -132,9 +142,14 @@ export function SettingsRoot(props: SettingsRootComponentProps) {
} = props
const bridge = hanaBridge()
const role = bridge?.role ?? 'navigation'
const readView = bridge?.readSettingsView
const writeView = bridge?.writeSettingsView
const onViewChanged = bridge?.onSettingsViewChanged
const readState = bridge?.readIntentState
const writeView = bridge?.publishIntent
const registerLanding = bridge?.registerIntentLanding
/** 读当前设置视图(通道缓存/首读懒种子;没有当前值就是关且无分区)。 */
const readView = useCallback(async (): Promise<SettingsView> => {
const hit = readState === undefined ? null : await readState('settings-view')
return normalizeView(hit?.value)
}, [readState])
const { open, activeId } = useStore(state => state)
const shortcut = useShortcuts(rows => rows.find(row => row.id === 'settings.open'))
const { open: openState, close: closeState, select: selectState, openSection: openSectionState } = actions
Expand All @@ -157,7 +172,7 @@ export function SettingsRoot(props: SettingsRootComponentProps) {
setViewFailure({ kind: 'write', revision })
return
}
void writeView(next).then(() => {
void writeView('settings-view', next).then(() => {
if (pendingWrite.current?.revision === revision) pendingWrite.current = null
}, (error: unknown) => {
if (revision !== viewRevision.current) return
Expand All @@ -178,31 +193,36 @@ export function SettingsRoot(props: SettingsRootComponentProps) {

// 跟随共享状态:只有 workspace / standalone 订阅并应用——
// FP(navigation)是发射端(点设置写出去),主卡是接收端(读进来以模态面板打开)。
// 落地走直投通道的落地面:帧到了直接落(不再绕一圈读),首挂的快照帧就是当前值。
useEffect(() => {
if (role === 'navigation' || role === 'settings' || readView === undefined || onViewChanged === undefined) {
if (role === 'navigation' || role === 'settings' || registerLanding === undefined || readView === undefined) {
refreshSettingsView.current = undefined
return
}
let active = true
let generation = 0
const apply = (next: SettingsView): void => {
if (!active) return
if (next.open) {
if (next.section === null) openState()
else openSectionState(next.section)
} else {
closeState()
}
}
const refresh = () => {
const request = ++generation
const revision = viewRevision.current
void readView().then((next) => {
if (!active || request !== generation || revision !== viewRevision.current) return
if (next.open) {
if (next.section === null) openState()
else openSectionState(next.section)
} else {
closeState()
}
apply(next)
}, (error: unknown) => {
if (!active || request !== generation) return
console.error('DSH settings view could not be read.', error)
if (revision === viewRevision.current) setViewFailure({ kind: 'read', revision })
})
}
const off = onViewChanged(refresh)
const off = registerLanding('settings-view', (payload) => { apply(normalizeView(payload)) })
refreshSettingsView.current = refresh
refresh()
return () => {
Expand All @@ -211,7 +231,7 @@ export function SettingsRoot(props: SettingsRootComponentProps) {
if (refreshSettingsView.current === refresh) refreshSettingsView.current = undefined
off()
}
}, [readView, onViewChanged, role])
}, [readView, registerLanding, role])

// The ledger tick keeps the nav rows fresh: registrants re-register with
// freshly localized text on locale change, and the trigger/header/close
Expand Down
4 changes: 2 additions & 2 deletions integrations/ui-settings-general/integration.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
" role==='settings' → SettingsPanel embedded(占满整列;panel/embeddedPanel 新 CSS)",
" role==='workspace' → 只渲染 {panel}{syncFailure}{onboarding},**不画触发器**(这就是它不漂顶的原因)",
" 其余(navigation=FP)→ triggerRow(齿轮)+ panel + syncFailure + onboarding",
"共享状态:publish() 把 {open, section} 经 bridge.writeSettingsView 写出去(revision 守卫防晚到覆盖);",
"只有 workspace/standalone 订阅 bridge.readSettingsView + onSettingsViewChanged 跟随(FP 是发射端)。",
"共享状态:publish() 把 {open, section} 经通用面 bridge.publishIntent('settings-view', …) 指名投递过去(revision 守卫防晚到覆盖);",
"只有 workspace/standalone 用 bridge.registerIntentLanding('settings-view', …) 落地跟随(FP 是发射端)。",
"所以 FP 点齿轮 → 写共享状态 → 主卡读到就开面板;两边都开着时是主卡出面、FP 只留入口。",
"配套改动(同一批):integrations/ui-layout 恢复 workspace 的 settingsShell(落点);",
"packages/ui/src/app-shell.ts 发布 VIEW_STATE_API(read/write/onChanged,落在 hana.storage.global,",
Expand Down
39 changes: 11 additions & 28 deletions integrations/ui-shortcuts/files/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,49 +82,32 @@ export function apply(ctx: Context): void {
return () => { disposeCommand(); disposeSlot() }
})

// 落地端:接住 FP 发来的一次性「打开参考框」,用自己的 store 开;落地即清,重载不重放。
// 落地端:帧到了就开(直投通道的落地面);一次性动件,不在通道上重放。
ctx.effect(() => {
if (!landsIntent) return () => { /* 发射端与其它面不落地 */ }
const bridge = forwardBridge()
const readIntent = bridge?.readIntent
const onIntentChanged = bridge?.onIntentChanged
if (readIntent === undefined || onIntentChanged === undefined) return () => { /* 桥不在就不参与 */ }
let appliedAt = -1
const drain = (): void => {
void readIntent('shortcuts-panel').then((intent) => {
if (!intent || !intent.pending) return
const at = typeof intent.at === 'number' ? intent.at : 0
if (at <= appliedAt) return
appliedAt = at
instance.actions.open()
// 清空必须带回刚消费的 at(见 clearIntent 注释:否则会被自己的清空再唤醒)。
const clearIntent = bridge?.clearIntent
if (clearIntent !== undefined) void clearIntent('shortcuts-panel', at).catch(() => { /* 清不掉下次读再判一次 at */ })
}, () => { /* 读失败等下一次变化 */ })
}
const off = onIntentChanged('shortcuts-panel', drain)
// 面比发射端晚开时,把存着的那一条接住(已经是空的说明早已落地)。
drain()
return () => { if (typeof off === 'function') off() }
const registerLanding = bridge?.registerIntentLanding
if (registerLanding === undefined) return () => { /* 桥不在就不参与 */ }
const off = registerLanding('shortcuts-panel', () => { instance.actions.open() })
return () => { off() }
}, 'ui-shortcuts: 跨面意图的落地')
}

// ── 跨面转发用到的宿主桥面(壳页挂在 window.__DSHANA__;缺失即整条不参与)───────────
interface ForwardBridge {
readonly role?: string
writeIntent?(kind: string, value: unknown): Promise<unknown>
readIntent?(kind: string): Promise<{ value: unknown; at: number; pending: boolean }>
onIntentChanged?(kind: string, listener: () => void): () => void
clearIntent?(kind: string, at: number): Promise<unknown>
/** 已上直投通道:指名投递 / 登记落地(旧三件名随迁移删除)。 */
publishIntent?(kind: string, value: unknown): Promise<{ delivered: number }>
registerIntentLanding?(kind: string, handler: (payload: unknown) => void): () => void
}

function forwardBridge(): ForwardBridge | undefined {
const bridge = (globalThis as { __DSHANA__?: ForwardBridge }).__DSHANA__
return bridge !== null && typeof bridge === 'object' ? bridge : undefined
}

/** 发射一条意图(发射端用;桥不在或写失败就当没发出去)。 */
/** 发一条意图(发射端用;桥不在或发失败就当没发出去)。 */
function emitIntent(kind: string, value: unknown): void {
const written = forwardBridge()?.writeIntent?.(kind, value)
if (written !== undefined) void written.catch(() => { /* 写不进则本次不发 */ })
const written = forwardBridge()?.publishIntent?.(kind, value)
if (written !== undefined) void written.catch(() => { /* 发不出去则本次不发 */ })
}
2 changes: 1 addition & 1 deletion integrations/ui-shortcuts/integration.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"批次④:快捷键参考框的跨面转发。",
"为什么在 ui-shortcuts:这个包把参考框注册在 shell.overlay,而打开它的 `shortcuts.open` 命令在任何面都会",
"触发(含 FP)。FP 是局部面,app 级 overlay 那一层不归它(ui-layout 的门),于是框在 FP 里没有落点:",
"命令把「打开」写成意图,整幅面(workspace / standalone)读进来用自己的 store 开框,落地即 clearIntent。",
"命令把「打开」写成意图(面间直投通道的 publishIntent),整幅面(workspace / standalone)读进来用自己的 store 开框(registerIntentLanding),一次性动作、通道不回放。",
"设置区那一行的打开路径在主卡,不受影响,但同一处收口(openReference)以免两条路分叉。",
"同批核对结论:ui-plugin-manager 的 refresh-toast 与 ui-schedule 的 delete-toast 不需要转发——",
"它们的面只在整幅面被发起(插件管理页与任务页都注册在 `main`,FP 只挂面板图标;会话行那两个座位只投影",
Expand Down
5 changes: 3 additions & 2 deletions integrations/ui-sidebar/files/src/client/SidebarRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function PanelRow({ id, label, wide, usePanelInfo, selectPanel, renderSlot }: Pa

/** 跨面桥面里本插件用到的那一格(只有面板选中;缺失即不参与)。 */
type PanelSelectionBridge = {
writePanelView?: (panelId: string | null) => Promise<void>
publishIntent?: (kind: string, payload: unknown) => Promise<unknown>
}

/**
Expand All @@ -102,7 +102,8 @@ function usePanelPublisher(usePanelInfo: PropsRuntime<'sidebar'>['usePanelInfo']
if (last.current === activePanelId) return
last.current = activePanelId
const bridge = (window as { __DSHANA__?: PanelSelectionBridge }).__DSHANA__
void bridge?.writePanelView?.(activePanelId ?? null)
// 已上直投通道:通用面指名投递给主卡(“其余面”),回执不用。
void bridge?.publishIntent?.('panel-view', { panelId: activePanelId ?? null })
}, [activePanelId, enabled])
}

Expand Down
Loading