dsh-multiview 0.1.10 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.11 — 2026-11-07
4
+
5
+ ### 新增
6
+
7
+ - **性能模式(可选,默认关闭)**:顶栏「视图」左边的仪表盘图标。开启后非聚焦格暂停实时渲染,只保留标题与「实时渲染已暂停」占位;点击该格立即恢复。
8
+ - 真机(win11-test,DSH 0.2.2-rc.1)四路同时长流式实测:**p95 140ms → 58ms**,帧率 24.4 → 44.1fps,p50 19.3 → 15.8ms(1 格实时 + 3 格暂停)。
9
+ - 根因记录:CPU profile 显示分屏插件自身只占 0.6% 自耗时,大头是被四人份"虚拟化 transcript 逐 chunk 测量/重渲染 + 渲染器非 JS 开销"占据;因此唯一有效杠杆就是减少同时在渲染的格子。
10
+
11
+ ## 0.1.10 — 2026-11-07
12
+
13
+ 首个通过 **npm Trusted Publishing(OIDC)** 发布的版本:仓库中没有任何 npm token 或 secret,打 tag 即由 GitHub Actions 用 OIDC 换取短期凭据发布,并附带 provenance 签名。
14
+
15
+ - workflow 不再使用 setup-node 的 registry-url:它会写入 _authToken=${NODE_AUTH_TOKEN},空值会让 npm 退回 token 认证而不是走 OIDC 交换。
16
+ - 发布命令为 npm publish --access public --provenance。
17
+
3
18
  ## 0.1.9 — 2026-11-07
4
19
 
5
20
  ### 基础设施
package/client.js CHANGED
@@ -11,7 +11,7 @@ const NS = 'multiview';
11
11
  const MV_PANEL_ID = 'multiview';
12
12
  const MV_PANE_SLOT = 'multiview.pane';
13
13
  const MV_STORAGE_KEY = 'dsh.multiview.v1';
14
- const MV_PLUGIN_VERSION = '0.1.10';
14
+ const MV_PLUGIN_VERSION = '0.1.11';
15
15
  const MV_ZH = {
16
16
  panel: '分屏',
17
17
  title: '分屏',
@@ -34,6 +34,11 @@ const MV_ZH = {
34
34
  waiting: '等待你处理',
35
35
  unread: '有新结果',
36
36
  views: '视图',
37
+ perfMode: '性能模式',
38
+ perfModeOn: '性能模式已开启:只有聚焦的那一格实时渲染',
39
+ perfModeOff: '性能模式已关闭:每一格都实时渲染',
40
+ parkedTitle: '实时渲染已暂停',
41
+ parkedHint: '性能模式下非聚焦格不渲染;点击这一格恢复',
37
42
  viewsEmpty: '还没有保存的视图。绑定两个以上会话后会自动记住。',
38
43
  viewsCurrent: '当前分屏',
39
44
  viewsDelete: '删除视图',
@@ -79,6 +84,11 @@ const MV_EN = {
79
84
  waiting: 'Waiting for you',
80
85
  unread: 'New result',
81
86
  views: 'Views',
87
+ perfMode: 'Performance mode',
88
+ perfModeOn: 'Performance mode on: only the focused pane renders live',
89
+ perfModeOff: 'Performance mode off: every pane renders live',
90
+ parkedTitle: 'Live rendering paused',
91
+ parkedHint: 'Performance mode parks unfocused panes; click this pane to resume',
82
92
  viewsEmpty: 'No saved views yet. A split with two or more sessions is remembered automatically.',
83
93
  viewsCurrent: 'Current split',
84
94
  viewsDelete: 'Delete view',
@@ -443,7 +453,8 @@ function mvInitialState() {
443
453
  seeded: false,
444
454
  reconciled: false,
445
455
  views: [],
446
- currentViewId: null
456
+ currentViewId: null,
457
+ perfMode: false
447
458
  };
448
459
  }
449
460
  let mvState = mvInitialState();
@@ -470,7 +481,8 @@ function mvScheduleSave() {
470
481
  preset: mvState.preset,
471
482
  seeded: mvState.seeded,
472
483
  views: mvState.views,
473
- currentViewId: mvState.currentViewId
484
+ currentViewId: mvState.currentViewId,
485
+ perfMode: mvState.perfMode === true
474
486
  };
475
487
  window.localStorage.setItem(MV_STORAGE_KEY, JSON.stringify(durable));
476
488
  } catch (error) {}
@@ -591,7 +603,8 @@ function mvHydrate() {
591
603
  seeded: raw.seeded === true,
592
604
  reconciled: false,
593
605
  views,
594
- currentViewId
606
+ currentViewId,
607
+ perfMode: raw.perfMode === true
595
608
  };
596
609
  }
597
610
  function mvReset() {
@@ -802,6 +815,12 @@ function mvDeleteView(viewId) {
802
815
  };
803
816
  });
804
817
  }
818
+ function mvTogglePerfMode() {
819
+ mvCommit((current)=>({
820
+ ...current,
821
+ perfMode: current.perfMode !== true
822
+ }));
823
+ }
805
824
 
806
825
  const MV_UPDATE_KEY = 'dsh.multiview.update.v1';
807
826
  const MV_CHECK_INTERVAL = 24 * 60 * 60 * 1000;
@@ -1158,6 +1177,11 @@ const MV_CSS = `
1158
1177
  .mv-icon:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
1159
1178
  .mv-chip{color:var(--dsw-alias-label-secondary)}
1160
1179
  .mv-chip[data-active="true"]{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}
1180
+ .mv-perf[data-active="true"]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-state-business-primary)}
1181
+ .mv-parked{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;width:100%;height:100%;padding:0;border:none;background:transparent;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer}
1182
+ .mv-parked:hover{background:var(--dsw-alias-interactive-bg-hover)}
1183
+ .mv-parked-title{font-size:13px;color:var(--dsw-alias-label-secondary)}
1184
+ .mv-parked-note{font-size:11px;color:var(--dsw-alias-label-tertiary)}
1161
1185
  @media (prefers-reduced-motion: reduce){*{transition:none !important;animation:none !important}}
1162
1186
  `;
1163
1187
  function MvGlyph(props) {
@@ -1202,6 +1226,7 @@ const MV_ICON = {
1202
1226
  external: 'M6 10L13 3M9 3h4v4',
1203
1227
  updateCheck: 'M8 2a6 6 0 1 0 0 12A6 6 0 1 0 8 2zM8 10.7V5.7M5.6 8.1 8 5.6l2.4 2.5',
1204
1228
  updateInstall: 'M3 9.5v3c0 .6.45 1 1 1h8c.55 0 1-.4 1-1v-3M8 2.6v6.2M5.4 6.4 8 9l2.6-2.6',
1229
+ perf: 'M2.6 12.2a5.4 5.4 0 1 1 10.8 0M8 12.2 10.5 7.4',
1205
1230
  views: 'M8 2.2 13.4 5 8 7.8 2.6 5 8 2.2M2.6 8.2 8 11l5.4-2.8M2.6 11.2 8 14l5.4-2.8'
1206
1231
  };
1207
1232
 
@@ -1667,6 +1692,9 @@ function PaneFrame(props) {
1667
1692
  onPick: (id)=>surface.onBind(leaf.id, id),
1668
1693
  views: surface.state.views,
1669
1694
  currentViewId: surface.state.currentViewId
1695
+ }) : surface.state.perfMode === true && !focused ? h(MvParkedPane, {
1696
+ title,
1697
+ onResume: ()=>surface.onFocus(leaf.id)
1670
1698
  }) : h(MvPaneHost, {
1671
1699
  sessionId,
1672
1700
  SessionProvider: surface.SessionProvider,
@@ -1680,6 +1708,19 @@ const MvPaneHost = React.memo(function MvPaneHost(props) {
1680
1708
  renderSlot: props.renderSlot
1681
1709
  });
1682
1710
  }, (previous, next)=>previous.sessionId === next.sessionId && previous.SessionProvider === next.SessionProvider && previous.renderSlot === next.renderSlot);
1711
+ function MvParkedPane(props) {
1712
+ const { title, onResume } = props;
1713
+ return h('button', {
1714
+ type: 'button',
1715
+ className: 'mv-parked',
1716
+ onClick: onResume,
1717
+ title: T('parkedHint')
1718
+ }, h('span', {
1719
+ className: 'mv-parked-title'
1720
+ }, title), h('span', {
1721
+ className: 'mv-parked-note'
1722
+ }, T('parkedTitle')));
1723
+ }
1683
1724
  function renderMvNode(node, surface) {
1684
1725
  if (node.kind === 'leaf') return h(PaneFrame, {
1685
1726
  key: node.id,
@@ -1806,6 +1847,20 @@ function MvUpdateDialog(props) {
1806
1847
  'aria-label': T('updateCheck')
1807
1848
  }, body, actions));
1808
1849
  }
1850
+ function MvPerfButton() {
1851
+ const state = useMvState();
1852
+ const on = state.perfMode === true;
1853
+ return h('button', {
1854
+ type: 'button',
1855
+ className: 'mv-icon mv-perf',
1856
+ 'data-active': on ? 'true' : 'false',
1857
+ title: on ? T('perfModeOn') : T('perfModeOff'),
1858
+ 'aria-label': T('perfMode'),
1859
+ onClick: ()=>mvTogglePerfMode()
1860
+ }, h(MvGlyph, {
1861
+ path: MV_ICON.perf
1862
+ }));
1863
+ }
1809
1864
  function MvViewsButton(props) {
1810
1865
  const { views, currentKey } = props;
1811
1866
  const [open, setOpen] = React.useState(false);
@@ -2059,7 +2114,7 @@ function MultiviewStage(props) {
2059
2114
  className: 'mv-chip',
2060
2115
  'data-active': 'true',
2061
2116
  onClick: mvRestore
2062
- }, T('restore')), h(MvViewsButton, {
2117
+ }, T('restore')), h(MvPerfButton, null), h(MvViewsButton, {
2063
2118
  views: state.views ?? [],
2064
2119
  currentKey: mvViewKey(state.layout)
2065
2120
  }), h(MvUpdateButton, null), h('span', {
@@ -64,6 +64,15 @@
64
64
 
65
65
  > 该 VM 的测试 profile 里没有可用的审批触发条件(无 `danger-full-access` 提权会话),所以审批链路只在 `desktop-kfscv8c-1` 的 0.2.1-rc.5 实例上验证过(S11)。
66
66
 
67
+
68
+ ## 2.2 卸载 / 重装演练(win11-test,2026-11-07)
69
+
70
+ | # | 步骤 | 结果 |
71
+ |---|---|---|
72
+ | U1 | 从 profile 删除依赖 + bundle 行 + `node_modules\dsh-multiview`,重启 | 启动图 0 条 multiview;侧栏无「分屏」;无 `.mv-*` 残留样式(`oursLeft: 0`) |
73
+ | U2 | 残留检查 | `localStorage['dsh.multiview.v1']` 仍在(约 1.4KB,文档已声明为唯一残留) |
74
+ | U3 | 重新装回 0.1.10 + 恢复 bundle 行,重启 | 启动图 1 条;「分屏」回来;打开面板 4 格正常、无错误提示 |
75
+
67
76
  ## 3. 性能(CDP rAF 采样,1530×851)
68
77
 
69
78
  | 场景 | p50 | p95 | 最长帧 | >50ms 长任务 | JS 堆 |
@@ -72,7 +81,10 @@
72
81
  | 4 格挂载、空闲 | 16.7ms | 16.8ms | 16.8ms | 0 | 75MB |
73
82
  | 4 格挂载、2 路同时流式 | 16.7ms | 16.7ms | 16.8ms | 0 | 85MB |
74
83
  | 4 格挂载、4 路**同时长流式**(60s 采样,前 ~40s 四路都在产出) | 16.7ms | **50ms** | **150ms** | **2** | 53MB |
84
+ | 4 格、4 路同时长流式(**win11-test 真机**,0.2.2-rc.1,60s,四路全程在产出) | 19.3ms | **140ms** | **370ms** | **55** | 69MB |
85
+ | 4 格静态内容、脚本化滚动 10s(win11-test 真机) | 15.8ms | 26ms | 85ms | — | — |
75
86
 
87
+ > **2026-11-07 真机复测结论**:交互(滚动/拖拽/聚焦)在 4 格下仍是 ~59fps、p95 26ms;卡顿只出现在「多格同时长流式」——那是 DSH Chat 自己对每个 chunk 重排 markdown 的成本 ×4,不是分屏框架的开销(`contain: strict` A/B 59.2 vs 58.5fps,属噪声,未采纳)。另:虚拟机窗口失焦时 Chromium 会把 rAF 压到 ~7fps,所以真机绝对帧率受环境影响,只有同一窗口内的对比才有意义。
76
88
  > 四路长文同时流式是本插件目前压到的最重场景:平均仍近 60fps,但 p95 升到 50ms、出现 2 个 >50ms 长任务与一次 150ms 长帧。原因是四份虚拟化 transcript 各自按 chunk 重排,属于渲染侧固有成本;未做节流/降级(保持"每格都是真实 Chat"的产品约束)。需要更稳时可后续加"非聚焦格降频"。|
77
89
 
78
90
  采样脚本:`.dev/sampler.js`(`requestAnimationFrame` 间隔分布 + `PerformanceObserver('longtask')` + `performance.memory`)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-multiview",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Multi-view for DeepSeek Harness: 2-4 live conversations side by side, each pane a real Chat, with resize, swap, saved splits and pane focus.",
5
5
  "type": "module",
6
6
  "license": "MIT",