Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Binary file modified artifacts/e2e/element-semantics.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions openspec/changes/console-live-semantic-coexist/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
## Why

`unified-console-semantic-viewer`(CH-H1/H2)把範本 6 面板語意 viewer(①模型資訊 ②IFC語意 ③結構 ④對構 ⑤幾何/分類碼 ⑥空間)放進中央 `MockViewport`,但其 gate 為 `viewerTab==="model" && !_hasRemoteVideoFrame()` —— **取得真實 Kit 幀後即卸載**,讓出中央給 `<video>` live 3D。

在「有 GPU、Kit 真的出幀」的環境(本機 RTX 4060 Ti,實測 1920×1080 frame)下,這導致:真人開真實 session → live 3D 一出現 → ②③④⑥ 語意面板**整片消失**,無法「點構件看語意」。這既不符北極星範本(AI-BIM-Geo Viewer:語意面板**環繞**中央 3D 並存,非二選一),也讓 `element-semantics` browser E2E 在 GPU 環境恆紅(中央被 live 3D 取代、對構表 row 消失無從點擊)。

附帶釐清一條部署真相:`coordinator :8004 /ui/open` 是 **302 轉址**到 `viewer :5173`(docker `web-viewer-sample` 服務,`vite dev` 跑 **baked source**,無 bind-mount)。故 viewer 前端改動 MUST 重建 viewer image 才會反映在 `/ui/open` 入口;只 `npm run build:ui`(更新 `:8004/ui/` dist-ui console)不會更新 `:5173` 入口。此前因建錯目標導致「改了卻沒效」的假象。

## What Changes

- **`MockViewport.tsx`**:新增 `liveMode?: boolean` prop。`liveMode=true`(已出真 Kit 幀)時 banner 誠實標「語意側欄 · live 3D 已出幀」(**不再宣稱 no-GPU**),並套 `gv-mock--live` 改為左側語意側欄;`liveMode=false` 維持既有中央 deterministic·no-GPU 佔位。reserved padding 僅 non-live 套用。
- **`Window.tsx`**:MockViewport mount gate 移除 `!_hasRemoteVideoFrame()`(取得真幀後**不卸載**),改傳 `liveMode={this._hasRemoteVideoFrame()}`。`viewerTab==="model"` 與 session/expectedStageUrl 條件不變;問題分頁(`viewerTab!=="model"`)仍不掛載。
- **`viewer.css`**:新增 `.gv-mock--live`(左緣 400px 半透明語意側欄、單欄 grid、z-index 26 —— 高於左側 USD 樹 dock(25) 與治理 overlay(20),確保語意內容恆可見不被既有左側 dock 蓋住〔reviewer P1〕;模型置中不受遮蔽、與右側 340px 治理 overlay 水平不重疊)。完整 reserved-space(側欄與 live 3D 完全並排不疊放 video 左緣、且不蓋 stage-truth 尾端)版面列為後續 follow-up(reviewer P2,非本 change 範圍)。
- **部署**:重建 docker `viewer` image 使 `:5173`(`/ui/open` 入口)反映上述前端改動。
- E2E:`element-semantics`(點對構表 row → ②IFC語意/⑥空間/⑤roadmap,與 live 3D 並存)+ `issues-tab`(模型↔問題切換)+ `gov-viewer-layout`(harness 不空白)三支 live 驗綠。

## Capabilities

### New Capabilities

- None。

### Modified Capabilities

- `unified-governance-console`:新增可驗收 requirement(取得真 Kit 幀後語意面板 SHALL 與 live 3D 並存為側欄、不消失;banner 誠實表態;viewer 前端入口為 `:5173` docker viewer,改動須重建 image)。

## Impact

- Owner:`web-viewer-sample/src/Window.tsx`(先 gitnexus_impact;additive:移 gate 條件 + 傳 liveMode)、`console/viewer/MockViewport.tsx`(+liveMode/banner/pad)、`console/viewer/viewer.css`(+.gv-mock--live)。
- API/boundary:**無變更**(前端只打 :8004;②④⑥ 仍走既有 coordinator for-session / element-mapping proxy;不新增 prod 依賴;不直連 :49101/:49102)。
- 部署:重建 docker `viewer` image(`compose.runtime-manager.yml + compose.host-kit.yml`,project `ai-bim-web-plane-host-kit`);deploy.ps1 golden path 已含 viewer build,merge 後一鍵部署即反映。
- 驗證:viewer tsc + vitest(MockViewport 周邊單元)+ Playwright 全 15 支綠(含 element-semantics 由紅轉綠、issues-tab 去 flaky);真實 session live e2e + 截圖佐證。
- Non-goals:不改 GovernanceOverlay 內部 A1–A10 邏輯;不改 streaming/coordinator data shape;⑤幾何/材質 + 分類碼仍誠實 roadmap(pipeline 無來源,不捏造)。
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## ADDED Requirements

### Requirement: 取得真實 Kit 幀後語意面板 SHALL 與 live 3D 並存(不消失),且 banner 誠實表態

primary 治理 viewer 的「模型」分頁,於取得真實 WebRTC/Kit 視訊幀(`_hasRemoteVideoFrame()` 為真)後,語意檢視面板(①模型資訊 ②IFC語意 ③結構 ④對構表 ⑥空間)SHALL 持續存在並與中央 `<video>` live 3D **並存**(呈現為左側語意側欄,對齊 AI-BIM-Geo Viewer 範本:面板環繞中央 3D),SHALL NOT 因出幀而整片卸載/消失。側欄 SHALL NOT 覆蓋中央 live 3D,亦 SHALL NOT 與右側 A1–A10 治理 overlay 水平重疊。

出幀後語意側欄的 banner SHALL 誠實標示「live 3D 已出幀」狀態,SHALL NOT 仍宣稱「no-GPU / deterministic」(誠實鐵律:不得在 GPU 實際出畫面時謊稱無 GPU)。未出幀時 SHALL 維持中央 deterministic·no-GPU 資訊濃密佔位(非空白、非壞掉)。

②④⑥ 之資料 SHALL 仍經 coordinator `:8004` 的 for-session / element-mapping proxy 取得(不直連 :49101/:49102);⑤幾何/材質與分類碼無 pipeline 來源時 SHALL 誠實標 roadmap/N/A,SHALL NOT 捏造。

#### Scenario: 真實 session 出 live 3D 後,點對構表構件仍可見 ②IFC語意 + ⑥空間

- **WHEN** 真人開啟有真實 Kit 幀的 session(GPU 出畫面)並停在「模型」分頁
- **THEN** 中央 SHALL 顯 live 3D `<video>`,左側 SHALL 同時呈現語意側欄(①③ + ④對構表 row),語意面板 SHALL NOT 因出幀而消失
- **AND** 側欄 banner SHALL 顯「live 3D 已出幀」(誠實),SHALL NOT 顯「no-GPU」
- **AND** 點④對構表第一列構件 SHALL 於 ②IFC語意 顯該構件真實 Type/Property(經 for-session proxy),⑥空間顯容納鏈,⑤幾何/分類碼誠實標 roadmap
- **AND** SHALL 具 browser E2E 證據(點構件 live 驗)+ 截圖(左側語意側欄 + 中央 live 3D + 右側治理 overlay 並存)

### Requirement: viewer 前端入口 SHALL 為 :5173 docker viewer,其前端改動 MUST 重建 viewer image 始生效

`coordinator :8004 /ui/open` SHALL 以 302 轉址至 `viewer :5173`(docker `web-viewer-sample` 服務,`vite dev` 跑 baked source、無 bind-mount)。因此 viewer 前端(Window/MockViewport/console 等)之改動 MUST 重建 docker `viewer` image 後始於 `/ui/open` 入口生效;僅重建 `:8004/ui/` dist-ui console(`npm run build:ui`)SHALL NOT 視為已部署 viewer 入口改動。`scripts/deploy.ps1` golden path SHALL 涵蓋 viewer image build,使 merge 後一鍵部署即反映。

#### Scenario: viewer 前端改動經重建 image 後在 /ui/open 入口生效

- **WHEN** 修改 viewer 前端碼並欲於 `/ui/open` 入口驗證
- **THEN** SHALL 重建 docker `viewer` image 並 `up -d viewer`,SHALL NOT 以「只 build dist-ui」當作已部署
- **AND** 驗證 SHALL 針對 `/ui/open` 實際轉址之 `:5173` 入口(或等價最新碼 dev server),SHALL NOT 誤針對陳舊 baked 容器而得「改了沒效」之假象
20 changes: 20 additions & 0 deletions openspec/changes/console-live-semantic-coexist/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## 1. 前端:語意面板與 live 3D 並存

- [x] 1.1 `MockViewport.tsx` 新增 `liveMode` prop;`liveMode=true` 套 `gv-mock--live` + 誠實 banner(「語意側欄 · live 3D 已出幀」),non-live 維持中央 deterministic·no-GPU 佔位;reserved padding 僅 non-live。
- [x] 1.2 `Window.tsx` MockViewport gate 移除 `!_hasRemoteVideoFrame()`、改傳 `liveMode={this._hasRemoteVideoFrame()}`(先 gitnexus_impact:MockViewport upstream LOW,僅 Window.render d=1)。
- [x] 1.3 `viewer.css` 新增 `.gv-mock--live`(左 400px 側欄 / 單欄 grid / z-index 18,不覆蓋中央 video、不與右側治理 overlay 重疊)。

## 2. 部署(viewer 入口為 :5173 docker viewer)

- [x] 2.1 重建 docker `viewer` image(baked source;`/ui/open` 302 轉址至 :5173),`up -d viewer` 套用。

## 3. 驗證

- [x] 3.1 `npx tsc --noEmit` 綠;viewer vitest 綠。
- [x] 3.2 Playwright 全 15 支綠:`element-semantics`(點 row → ②⑥⑤ 與 live 3D 並存)由紅轉綠、`issues-tab` 去 flaky、`gov-viewer-layout`(harness)回歸;截圖佐證左側語意側欄 + 中央 live 3D + 右側治理 overlay 並存。
- [x] 3.3 node 全鏈 smoke 另證 ②④⑥ 資料路徑(for-session 200 / 真實 psets+spatial+roadmap)。

## 4. 收尾

- [ ] 4.1 `npx openspec validate console-live-semantic-coexist --strict` 綠。
- [ ] 4.2 PR(繁中)→ CI + pr-review-agent 綠 → merge → archive/sync。
12 changes: 7 additions & 5 deletions web-viewer-sample/src/Window.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2203,15 +2203,17 @@ export default class App extends React.Component<AppProps, AppState> {
</>
}

{/* CH-H1:中央視區「不空白」—— 無真實 WebRTC 幀(harness 或尚未出幀)時,以資訊濃密 mock
viewport 取代空白(明標 deterministic·no-GPU,避免被當壞掉),把範本①模型資訊+④對構表+選取 echo
放進中央;取得真實 Kit 幀(_hasRemoteVideoFrame)後不渲染,讓 <video> live 3D 顯示。additive:
不改 AppStream / GovernanceOverlay / stage-truth / spectator 既有機制。 */}
{/* CH-H1/H3:模型分頁語意檢視(①模型資訊 ②IFC語意 ③結構 ④對構 ⑥空間)。
無真實 WebRTC 幀(harness 或尚未出幀)→ 中央資訊濃密 mock viewport(deterministic·no-GPU,非壞掉);
CH-H3:取得真實 Kit 幀(_hasRemoteVideoFrame)後**不再卸載**,改以 liveMode 切左側語意側欄,與中央
<video> live 3D 並存(對齊 AI-BIM-Geo Viewer 範本:①③ 左欄 + ②④⑥ 隨點構件),GPU 出畫面時語意
面板不消失。additive:不改 AppStream / GovernanceOverlay / stage-truth / spectator 既有機制;
問題分頁仍 viewerTab!=="model" 不掛載(不擾全幅治理)。 */}
{this.state.viewerTab === "model"
&& !this._hasRemoteVideoFrame()
&& (harnessEnabled() || (Boolean(this.state.reviewSessionId) && Boolean(this.state.expectedStageUrl)))
&& (
<MockViewport
liveMode={this._hasRemoteVideoFrame()}
Comment on lines 2212 to +2216
harness={harnessEnabled()}
stageUrl={this.state.expectedStageUrl}
loadedStageUrl={this.state.loadedStageUrl}
Expand Down
29 changes: 22 additions & 7 deletions web-viewer-sample/src/console/viewer/MockViewport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ export interface MockViewportProps {
reservedRight?: number;
reservedLeft?: number;
sessionId?: string | null;
// CH-H3:取得真實 Kit 幀(_hasRemoteVideoFrame)後仍掛載——改為左側語意側欄,與中央 live 3D <video> 並存
// (對齊範本:①③ 左欄 + ②④⑥ 隨點構件),而非整片消失。誠實鐵律:liveMode 下 banner 標「live 3D 已出幀」,不再宣稱 no-GPU。
liveMode?: boolean;
}

const DASH = "—";

export function MockViewport(props: MockViewportProps) {
const { harness, stageUrl, loadedStageUrl, webrtcStatus, selectedGuid, selectedPrim, bindings = [], reservedRight = 0, reservedLeft = 0 } = props;
const { harness, stageUrl, loadedStageUrl, webrtcStatus, selectedGuid, selectedPrim, bindings = [], reservedRight = 0, reservedLeft = 0, liveMode = false } = props;
const layers = bindings.filter((b) => b.ready_status === "ready");
// ④對構表資料源:經 coordinator :8004 element-mapping for-session proxy(CORS-safe + 守邊界,
// 瀏覽器不直連 :49101 artifact server)。harness 無真實 coordinator session → null(誠實空狀態)。
Expand All @@ -51,15 +54,27 @@ export function MockViewport(props: MockViewportProps) {
const spatialSrc = !harness && props.sessionId
? `${coordinatorClient.base}/api/governance/spatial-tree/for-session/${encodeURIComponent(props.sessionId)}`
: null;
// liveMode(已出真 Kit 幀):固定左側欄,不套 reserved padding(不覆蓋中央 live 3D)。非 liveMode:維持中央佔位(既有行為)。
const pad =
reservedRight || reservedLeft ? { paddingRight: reservedRight || undefined, paddingLeft: reservedLeft || undefined } : undefined;
!liveMode && (reservedRight || reservedLeft)
? { paddingRight: reservedRight || undefined, paddingLeft: reservedLeft || undefined }
: undefined;
Comment on lines 58 to +61

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the left dock space in live semantic mode

In real live sessions, _completeStageLoad() requests Kit children and getChildrenResponse populates usdPrims, which renders usd-stage-left-dock at left: 0, width 300, z-index 25. Because live mode now suppresses reservedLeft, the new .gv-mock--live sidebar also starts at left: 0 with z-index 18, so the USDStage dock is drawn over the first 300px of the 400px semantic sidebar, hiding or blocking most of the model info/mapping/IFC semantic UI exactly when a live frame is available. Keep the live sidebar offset from the existing left dock, or avoid rendering the overlapping dock in this mode.

Useful? React with 👍 / 👎.

return (
<div className="gv-mock" data-testid="mock-viewport" style={pad}>
<div className={`gv-mock${liveMode ? " gv-mock--live" : ""}`} data-testid="mock-viewport" style={pad}>
<div className="gv-mock__banner" data-testid="mock-viewport-banner">
<span className="gv-dot" /> Mock Viewport · <strong>deterministic · no-GPU</strong>
<span className="gv-mock__hint">
{harness ? "harness 決定性模式(不連真 Kit)" : "尚未取得真實 WebRTC 視訊幀"};此為刻意佔位非錯誤,取得真 Kit 幀後自動切換為 live 3D。
</span>
{liveMode ? (
<>
<span className="gv-dot" /> 語意側欄 · <strong>live 3D 已出幀</strong>
<span className="gv-mock__hint">中央為 live 3D 視訊(真 Kit 幀);此側欄同步 ①模型資訊 / ②IFC語意 / ③結構 / ④對構 / ⑥空間,點構件即查語意。</span>
</>
) : (
<>
<span className="gv-dot" /> Mock Viewport · <strong>deterministic · no-GPU</strong>
<span className="gv-mock__hint">
{harness ? "harness 決定性模式(不連真 Kit)" : "尚未取得真實 WebRTC 視訊幀"};此為刻意佔位非錯誤,取得真 Kit 幀後自動切換為 live 3D。
</span>
</>
)}
</div>

{/* section nav 已上移至 viewer 層分頁列(Window.tsx),「問題」分頁隱 MockViewport 後仍可切回。 */}
Expand Down
11 changes: 11 additions & 0 deletions web-viewer-sample/src/console/viewer/viewer.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@
padding: 14px;
box-sizing: border-box;
}
/* CH-H3:live 3D 已出幀時,MockViewport 不再整片覆蓋中央,改為左緣語意側欄(半透明),與中央 <video> live 3D
並存(對齊 AI-BIM-Geo Viewer 範本:左欄①③ + 隨點構件②④⑥;模型置中不受遮蔽)。
z-index 26:高於左側 USD 樹 dock(25) 與治理 overlay(20),確保語意內容恆在最上層可見(reviewer P1:避免被左側
USD dock 蓋住而隱藏語意);右側治理 overlay 在 right:0,與本左緣側欄水平不重疊。 */
.gv-mock.gv-mock--live {
right: auto; width: 400px; max-width: 46vw;
background: rgba(11, 13, 16, 0.92);
border-right: 1px solid #2f363f;
z-index: 26;
}
.gv-mock.gv-mock--live .gv-mock__grid { grid-template-columns: 1fr; }
.gv-mock__banner {
display: flex;
align-items: center;
Expand Down
Loading