恢复成自定义协议了

This commit is contained in:
2026-08-11 16:10:02 +08:00
parent 274dd4c044
commit f2c1374a7f
27 changed files with 820 additions and 1526 deletions
+440 -642
View File
File diff suppressed because it is too large Load Diff
+172 -9
View File
@@ -3,19 +3,182 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<title>Renderive Core2 全控件性能画廊</title>
<link rel="stylesheet" href="https://unpkg.com/amis@6.13.0/sdk/sdk.css">
<link rel="stylesheet" href="https://unpkg.com/amis@6.13.0/sdk/helper.css">
<link rel="stylesheet" href="https://unpkg.com/amis@6.13.0/sdk/iconfont.css">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="root" class="rv-app"></div>
<div id="menu-host" class="rv-menu-host" hidden>
<div id="menu-root" class="rv-menu-root"></div>
</div>
<script src="https://unpkg.com/amis@6.13.0/sdk/sdk.js"></script>
<header class="topbar">
<div class="brand">
<span class="brand-mark" aria-hidden="true">R2</span>
<div><p class="eyebrow">CORE2 · KERNEL · WEBSOCKET</p><h1>全控件 API 与帧策略性能画廊</h1></div>
</div>
<div class="topbar-actions">
<span id="connection-status" class="connection-status" data-state="connecting"><i></i><span>读取后端目录</span></span>
<button id="toggle-streams" class="quiet-button" type="button">暂停自动像素流</button>
</div>
</header>
<section class="hero">
<div>
<p class="eyebrow">THREE REAL KERNEL STRATEGIES</p>
<h2>三套对称页面,同一批控件,直接比较</h2>
<p id="mode-description">所有属性、动作、观察者和性能数据均由后端通过 WebSocket 返回。</p>
</div>
<dl class="metrics">
<div><dt id="page-count">3</dt><dd>帧策略页</dd></div>
<div><dt id="case-count"></dt><dd>每页控件</dd></div>
<div><dt id="canvas-count"></dt><dd>独立场景</dd></div>
<div><dt id="api-count"></dt><dd>手测入口</dd></div>
</dl>
</section>
<nav id="mode-tabs" class="mode-tabs" aria-label="Kernel 帧控制策略"></nav>
<nav id="category-filter" class="category-filter" aria-label="控件分类"></nav>
<main id="pages" class="pages" aria-live="polite">
<div class="loading-card"><span class="loader"></span><strong>等待后端返回控件与帧策略目录</strong></div>
</main>
<aside id="context-menu" class="context-menu" hidden aria-label="后端控件测试菜单">
<header class="menu-header">
<div><p id="menu-component" class="eyebrow"></p><h2 id="menu-title"></h2><p id="menu-description"></p></div>
<button id="menu-close" class="icon-button" type="button" aria-label="关闭">×</button>
</header>
<nav class="menu-tabs" aria-label="测试菜单页面">
<button type="button" data-tab="controls" class="active">控件属性</button>
<button type="button" data-tab="actions">专属 API</button>
<button type="button" data-tab="observer">Kernel 观察者</button>
<button type="button" data-tab="performance">性能</button>
</nav>
<div id="menu-body" class="menu-body"></div>
<footer class="menu-footer"><span id="menu-status">字段、验证与 API 映射来自后端 JSON</span><code>WS /renderive/gallery</code></footer>
</aside>
<div id="toast" class="toast" hidden></div>
<template id="page-template">
<section class="mode-page" hidden>
<header class="page-header"><div><p class="page-strategy"></p><h2 class="page-title"></h2></div><p class="page-description"></p></header>
<div class="gallery"></div>
</section>
</template>
<template id="card-template">
<article class="plot-card">
<header class="card-header">
<div><span class="card-category"></span><h3 class="card-title"></h3></div>
<div class="card-health">
<span class="motion-status" data-state="waiting"><i></i><span>等待动态帧</span></span>
<span class="card-socket" data-state="connecting"><i></i><span>连接中</span></span>
</div>
</header>
<div class="canvas-shell" tabindex="0">
<canvas aria-label="Core2 后端像素画布"></canvas>
<div class="canvas-hint">右键:本控件全部 API</div>
<div class="canvas-loading"><span class="loader"></span><small>创建 Kernel Scene</small></div>
</div>
<dl class="performance-strip">
<div><dt class="perf-fps">0.0</dt><dd>后端渲染 FPS</dd></div>
<div><dt class="perf-transport-fps">0.0</dt><dd>像素响应 FPS</dd></div>
<div><dt class="perf-present-fps">0.0</dt><dd>浏览器呈现 FPS</dd></div>
<div><dt class="perf-rtt">0.00</dt><dd>WS 往返 ms</dd></div>
<div><dt class="perf-overwritten">0</dt><dd>未呈现覆盖</dd></div>
<div><dt class="perf-render">0.00</dt><dd>Core 渲染 ms</dd></div>
<div><dt class="perf-encode">0.00</dt><dd>像素编码 ms</dd></div>
<div><dt class="perf-bandwidth">0.0</dt><dd>响应负载 MB/s</dd></div>
<div><dt class="perf-pending">0</dt><dd>Kernel 待处理</dd></div>
<div><dt class="perf-dropped">0</dt><dd>Kernel 丢弃</dd></div>
<div><dt class="perf-points">0→0</dt><dd>输入→绘制</dd></div>
<div><dt class="perf-limit">N/A</dt><dd>当前瓶颈</dd></div>
<div><dt class="perf-event">none</dt><dd>观察事件</dd></div>
<div><dt class="perf-timeouts">0</dt><dd>像素超时</dd></div>
<div><dt class="perf-pixel-age"></dt><dd>最近像素龄</dd></div>
</dl>
<div class="limit-flags" aria-label="低延迟限速来源">
<strong>限速来源</strong>
<span class="limit-frequency">Kernel 用户频率:<b>未受限</b></span>
<span class="limit-paint">Kernel PaintEvent<b>未受限</b></span>
<span class="limit-render">Kernel 后台渲染:<b>未受限</b></span>
<span class="limit-consumer">消费者反馈:<b>未受限</b></span>
</div>
<section class="kernel-observer-panel" aria-label="Kernel 低延迟全量统计">
<header>
<div><span>KERNEL <b data-observer-field="mode">low_latency</b> OBSERVER</span><strong data-observer-field="limit_state">frequency_limited</strong></div>
<div>事件 <b data-observer-field="last_event">none</b></div>
</header>
<div class="observer-counters">
<div><small>配置频率</small><b data-observer-field="configured_frequency_hz">0 Hz</b></div>
<div><small>观察次数</small><b data-observer-field="observation_count">0</b></div>
<div><small>最新序号</small><b data-observer-field="latest_sequence">0</b></div>
<div><small>发布</small><b data-observer-field="produced_frame_count">0</b></div>
<div><small>完成</small><b data-observer-field="consumed_frame_count">0</b></div>
<div><small>待处理</small><b data-observer-field="pending_frame_count">0</b></div>
<div><small>丢弃</small><b data-observer-field="dropped_frame_count">0</b></div>
<div><small>失败</small><b data-observer-field="failed_operation_count">0</b></div>
</div>
<div class="latency-summary">
<div><small>目标间隔</small><b data-observer-field="target_interval_ns">0 ns</b></div>
<div><small>PaintEvent</small><b data-observer-field="paint_duration_ns">0 ns</b></div>
<div><small>后台渲染</small><b data-observer-field="render_duration_ns">0 ns</b></div>
<div><small>内部瓶颈</small><b data-observer-field="bottleneck_duration_ns">0 ns</b></div>
<div><small>消费者总开关</small><b data-observer-field="consumer_feedback_master_enabled">关闭</b></div>
<div><small>Kernel 反馈有效</small><b data-observer-field="consumer_feedback_enabled">关闭</b></div>
<div><small>像素响应反馈</small><b data-observer-field="consumer_pixel_feedback_enabled">关闭</b></div>
<div><small>浏览器呈现反馈</small><b data-observer-field="consumer_presentation_feedback_enabled">关闭</b></div>
<div><small>手动消费者反馈</small><b data-observer-field="consumer_manual_feedback_enabled">关闭</b></div>
<div><small>手动消费者 FPS</small><b data-observer-field="consumer_manual_fps">0 FPS</b></div>
<div><small>生效反馈来源</small><b data-observer-field="consumer_feedback_source">none</b></div>
<div><small>像素响应周期</small><b data-observer-field="consumer_pixel_interval_ns">0 ns</b></div>
<div><small>浏览器呈现周期</small><b data-observer-field="consumer_presentation_interval_ns">0 ns</b></div>
<div><small>手动消费者周期</small><b data-observer-field="consumer_manual_interval_ns">0 ns</b></div>
<div><small>消费者原始采样</small><b data-observer-field="consumer_sample_interval_ns">0 ns</b></div>
<div><small>消费者平滑周期</small><b data-observer-field="consumer_smoothed_interval_ns">0 ns</b></div>
<div><small>消费者抖动</small><b data-observer-field="consumer_variation_ns">0 ns</b></div>
<div><small>消费者安全期限</small><b data-observer-field="consumer_safety_interval_ns">0 ns</b></div>
<div><small>消费者限速周期</small><b data-observer-field="consumer_interval_ns">0 ns</b></div>
<div><small>消费者限速 FPS</small><b data-observer-field="consumer_effective_fps">0 FPS</b></div>
<div><small>下次刷新</small><b data-observer-field="next_refresh_interval_ns">0 ns</b></div>
<div class="critical"><small>端到端延迟</small><b data-observer-field="end_to_end_ns">0 ns</b></div>
</div>
<div class="client-summary" aria-label="浏览器消费者反馈全量统计">
<div><small>RAF 最新周期</small><b data-client-field="display_interval_latest_ms">0 ms</b></div>
<div><small>RAF 中位周期</small><b data-client-field="display_interval_ms">0 ms</b></div>
<div><small>RAF P95 周期</small><b data-client-field="display_interval_p95_ms">0 ms</b></div>
<div><small>RAF P95-P50 抖动</small><b data-client-field="display_jitter_ms">0 ms</b></div>
<div><small>WS 往返</small><b data-client-field="frame_round_trip_ms">0 ms</b></div>
<div><small>像素响应 FPS</small><b data-client-field="transport_fps">0 FPS</b></div>
<div><small>浏览器呈现 FPS</small><b data-client-field="presentation_fps">0 FPS</b></div>
<div><small>WS 缓冲</small><b data-client-field="websocket_buffered_bytes">0 B</b></div>
<div><small>未呈现覆盖</small><b data-client-field="overwritten_pixel_frames">0</b></div>
<div><small>变化像素帧</small><b data-client-field="changed_pixel_frames">0</b></div>
<div><small>重复像素帧</small><b data-client-field="duplicate_pixel_frames">0</b></div>
<div><small>像素请求超时</small><b data-client-field="frame_request_timeout_count">0</b></div>
<div><small>最近像素龄</small><b data-client-field="last_pixel_receive_age_ms">0 ms</b></div>
<div><small>最近变化龄</small><b data-client-field="last_pixel_change_age_ms">0 ms</b></div>
</div>
<div class="latency-details" aria-label="Kernel 各阶段等待耗时">
<div><small>Painter Lease 等待</small><b data-observer-field="paint_lease_wait_ns">0 ns</b></div>
<div><small>Paint State 等待</small><b data-observer-field="paint_state_wait_ns">0 ns</b></div>
<div><small>Publish State 等待</small><b data-observer-field="publish_state_wait_ns">0 ns</b></div>
<div><small>Ready 等待</small><b data-observer-field="ready_wait_ns">0 ns</b></div>
<div><small>开始渲染时帧龄</small><b data-observer-field="frame_age_at_render_ns">0 ns</b></div>
<div><small>Render Lease 等待</small><b data-observer-field="render_lease_wait_ns">0 ns</b></div>
<div><small>Render State 等待</small><b data-observer-field="render_state_wait_ns">0 ns</b></div>
<div><small>Render Finish 等待</small><b data-observer-field="render_finish_state_wait_ns">0 ns</b></div>
<div><small>回放队列等待</small><b data-observer-field="queue_wait_ns">0 ns</b></div>
</div>
</section>
<div class="card-meta">
<p class="card-description"></p>
<dl><div><dt class="card-controls"></dt><dd>属性</dd></div><div><dt class="card-actions"></dt><dd>动作</dd></div><div><dt class="card-frames">0</dt><dd>像素帧</dd></div></dl>
</div>
<footer class="card-footer">
<code class="card-component"></code>
<div><button type="button" class="frame-button">刷新一帧</button><button type="button" class="open-menu">测试菜单</button></div>
</footer>
</article>
</template>
<script src="app.js"></script>
</body>
</html>
+81 -596
View File
@@ -1,599 +1,84 @@
:root {
color-scheme:dark;
--bg:#060a10;
--panel:#0b1119;
--panel2:#101823;
--panel3:#151f2c;
--line:#1e2b3a;
--line2:#2a3b4e;
--text:#dce9ff;
--muted:#8190a5;
--accent:#35e6b2;
--accent2:#6bc8ff;
--warn:#ffcf82;
--danger:#ff6b81;
}
* {
box-sizing:border-box;
}
html,body,#root {
min-height:100%;
margin:0;
color-scheme: dark;
font-family: Inter, "Segoe UI Variable", "Microsoft YaHei UI", sans-serif;
--bg: #07090d; --panel: #10141b; --panel2: #151b24; --line: #29313d;
--strong: #3b4655; --text: #f2f5f8; --muted: #929eac; --accent: #45ddbe;
--blue: #6aa9ff; --warning: #ffd166; --danger: #ff6885;
}
* { box-sizing: border-box; }
html { background: var(--bg); }
body {
overflow-x:hidden;
background:radial-gradient(circle at 18% 0,#102136 0,transparent 30rem),linear-gradient(180deg,#070c13,#05080d 58%,#060a10);
color:var(--text);
font:13px/1.5 Inter,"Segoe UI","Microsoft YaHei",sans-serif;
}
button,input,select,textarea {
font:inherit;
}
.rv-app,.cxd-Page,.cxd-Page-body,.cxd-Page-content {
min-height:100vh;
background:transparent!important;
color:var(--text)!important;
}
.cxd-Page-body,.cxd-Page-content {
padding:0!important;
}
.rv-shell {
width:min(1760px,100%);
margin:0 auto;
padding:18px 22px 44px;
}
.rv-topbar {
display:flex;
align-items:center;
justify-content:space-between;
gap:18px;
padding:8px 0 18px;
}
.rv-brand {
display:flex;
align-items:center;
gap:12px;
}
.rv-brand-mark {
display:grid;
width:42px;
height:42px;
place-items:center;
border:1px solid #35e6b255;
border-radius:10px;
color:var(--accent);
background:#35e6b20d;
box-shadow:inset 0 0 20px #35e6b20a;
font:800 13px/1 ui-monospace,SFMono-Regular,Consolas,monospace;
}
.rv-eyebrow {
margin:0 0 4px;
color:var(--accent);
font:700 10px/1.2 ui-monospace,SFMono-Regular,Consolas,monospace;
letter-spacing:.15em;
text-transform:uppercase;
}
.rv-brand h1,.rv-hero h2 {
margin:0;
color:#f2f7ff;
font-weight:720;
letter-spacing:-.02em;
}
.rv-brand h1 {
font-size:18px;
}
.rv-top-actions {
display:flex;
align-items:center;
justify-content:flex-end;
gap:10px;
}
.rv-connection,.rv-live-status {
display:inline-flex;
align-items:center;
gap:7px;
min-width:0;
color:var(--muted);
font:700 10px/1.25 ui-monospace,SFMono-Regular,Consolas,monospace;
}
.rv-connection i,.rv-live-status i {
width:7px;
height:7px;
flex:0 0 auto;
border-radius:50%;
background:#8793a3;
box-shadow:0 0 0 3px #8793a312;
}
.rv-connection-ready i,.rv-live-status-moving i,.rv-live-status-ready i {
background:var(--accent);
box-shadow:0 0 0 3px #35e6b21a,0 0 12px #35e6b280;
}
.rv-connection-error i,.rv-live-status-stalled i,.rv-live-status-error i {
background:var(--danger);
box-shadow:0 0 0 3px #ff6b811a,0 0 12px #ff6b8166;
}
.rv-live-status-duplicate i {
background:var(--warn);
}
.rv-hero {
display:grid;
grid-template-columns:minmax(0,1.45fr) minmax(380px,.75fr);
gap:20px;
padding:22px;
border:1px solid var(--line);
border-radius:12px;
background:linear-gradient(135deg,#0c1420ee,#091019ee);
box-shadow:0 16px 50px #00000036;
}
.rv-hero h2 {
margin-top:6px;
font-size:25px;
}
.rv-hero p {
margin:10px 0 0;
color:var(--muted);
}
.rv-hero-metrics .cxd-Grid-col {
padding:4px!important;
}
.rv-hero-metric,.rv-metric,.rv-limit-tile,.rv-observer-metric {
min-width:0;
height:100%;
padding:9px 10px;
border:1px solid var(--line);
border-radius:7px;
background:#090f17;
}
.rv-hero-metric b,.rv-metric b,.rv-observer-metric b {
display:block;
min-width:0;
overflow-wrap:anywhere;
color:var(--accent);
font:700 11px/1.35 ui-monospace,SFMono-Regular,Consolas,monospace;
}
.rv-hero-metric b {
font-size:18px;
}
.rv-hero-metric small,.rv-metric small,.rv-observer-metric small {
display:block;
margin-top:5px;
color:var(--muted);
font-size:9px;
line-height:1.35;
}
.rv-notice {
margin-top:12px;
}
.rv-filter-form {
margin:12px 0 5px;
}
.rv-filter-form .cxd-Form-row {
margin:0!important;
}
.rv-filter-form .cxd-ButtonGroup {
display:flex;
flex-wrap:wrap;
gap:5px;
}
.rv-mode-tabs {
margin-top:12px;
}
.rv-mode-tabs>.cxd-Tabs-links {
display:grid!important;
grid-template-columns:repeat(3,minmax(0,1fr));
gap:8px;
border:0!important;
background:transparent!important;
}
.rv-mode-tabs>.cxd-Tabs-links>li {
margin:0!important;
border:1px solid var(--line)!important;
border-radius:7px!important;
background:#0a111a!important;
}
.rv-mode-tabs>.cxd-Tabs-links>li>a {
padding:10px 12px!important;
color:var(--muted)!important;
font-weight:700!important;
}
.rv-mode-tabs>.cxd-Tabs-links>li.is-active {
border-color:#35e6b266!important;
background:#35e6b20d!important;
}
.rv-mode-tabs>.cxd-Tabs-links>li.is-active>a {
color:var(--accent)!important;
}
.rv-mode-tabs>.cxd-Tabs-content {
padding:10px 0 0!important;
border:0!important;
background:transparent!important;
}
.rv-page-header {
display:flex;
align-items:flex-end;
justify-content:space-between;
gap:14px;
margin:6px 0 12px;
padding:0 2px;
}
.rv-page-header h2 {
margin:2px 0 0;
color:#f2f7ff;
font-size:18px;
}
.rv-page-header p {
max-width:720px;
margin:0;
color:var(--muted);
text-align:right;
}
.rv-gallery>.cxd-Grid-col {
padding:6px!important;
}
.rv-card {
overflow:hidden;
min-width:0;
height:100%;
margin:0!important;
border:1px solid var(--line)!important;
border-radius:9px!important;
background:var(--panel)!important;
box-shadow:0 12px 32px #0000002b!important;
}
.rv-card>.cxd-Panel-body {
padding:0!important;
background:transparent!important;
}
.rv-card>.cxd-Panel-footer {
display:flex;
flex-wrap:wrap;
gap:8px;
padding:10px!important;
border-top:1px solid var(--line)!important;
background:#090f17!important;
}
.rv-card-header {
display:flex;
align-items:flex-start;
justify-content:space-between;
gap:12px;
padding:12px 13px 10px;
}
.rv-card-category {
display:block;
margin-bottom:3px;
color:var(--accent2);
font:700 9px/1.2 ui-monospace,SFMono-Regular,Consolas,monospace;
text-transform:uppercase;
}
.rv-card-title {
margin:0;
color:#eef5ff;
font-size:15px;
}
.rv-card-statuses {
display:flex;
flex-wrap:wrap;
justify-content:flex-end;
gap:8px;
}
.rv-canvas-shell {
position:relative;
width:100%;
height:clamp(300px,34vw,520px);
min-height:0;
overflow:hidden;
outline:none;
border-block:1px solid var(--line);
background:#04070b;
}
.rv-canvas-shell:focus-within {
box-shadow:inset 0 0 0 1px #35e6b25c;
}
.rv-canvas-shell canvas {
display:block;
width:100%;
height:100%;
background:#07111f;
image-rendering:auto;
}
.rv-canvas-hint {
position:absolute;
right:8px;
bottom:7px;
padding:3px 6px;
border:1px solid #ffffff14;
border-radius:4px;
color:#9cabc0;
background:#05090dc7;
pointer-events:none;
font:9px/1.25 ui-monospace,SFMono-Regular,Consolas,monospace;
}
.rv-canvas-loading {
position:absolute;
inset:0;
display:grid;
place-items:center;
color:var(--muted);
background:#05090de6;
pointer-events:none;
}
.rv-canvas-ready .rv-canvas-loading {
display:none;
}
.rv-metrics-grid,.rv-limit-grid,.rv-observer-grid,.rv-client-grid {
padding:6px;
border-bottom:1px solid var(--line);
background:#070c12;
}
.rv-metrics-grid .cxd-Grid-col,.rv-limit-grid .cxd-Grid-col,.rv-observer-grid .cxd-Grid-col,.rv-client-grid .cxd-Grid-col {
padding:3px!important;
}
.rv-limit-tile {
color:var(--muted);
font:700 9px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;
}
.rv-limit-tile b {
display:block;
margin-top:3px;
overflow-wrap:anywhere;
color:inherit;
}
.rv-limit-tile[data-active="true"] {
border-color:#ffb84d66;
color:var(--warn);
background:#ff9d170c;
}
.rv-observer-title {
display:flex;
align-items:center;
justify-content:space-between;
gap:12px;
padding:7px 10px;
border-bottom:1px solid var(--line);
color:#9ab1cc;
background:#07101a;
font:700 9px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;
}
.rv-observer-title b {
color:var(--accent);
}
.rv-card-meta {
display:grid;
grid-template-columns:repeat(4,minmax(0,1fr));
gap:1px;
border-top:1px solid var(--line);
background:var(--line);
}
.rv-card-meta div {
min-width:0;
padding:8px 10px;
background:#0a1018;
}
.rv-card-meta b,.rv-card-meta code {
display:block;
min-width:0;
overflow-wrap:anywhere;
color:#c8d7ea;
font-size:10px;
}
.rv-card-meta small {
display:block;
margin-top:3px;
color:var(--muted);
font-size:8px;
}
.rv-menu-host {
position:fixed;
z-index:10000;
inset:0;
padding:12px;
background:#00000078;
backdrop-filter:blur(3px);
}
.rv-menu-host[hidden] {
display:none;
}
.rv-menu-root {
width:min(760px,calc(100vw - 24px));
max-height:calc(100vh - 24px);
margin-left:auto;
overflow:auto;
border:1px solid var(--line2);
border-radius:10px;
background:#0a1018;
box-shadow:0 24px 80px #00000099;
}
.rv-menu-root .cxd-Page,.rv-menu-root .cxd-Page-body,.rv-menu-root .cxd-Page-content {
min-height:0!important;
}
.rv-menu-shell {
padding:12px;
}
.rv-menu-header {
display:flex;
align-items:flex-start;
justify-content:space-between;
gap:12px;
padding-bottom:10px;
border-bottom:1px solid var(--line);
}
.rv-menu-header h2 {
margin:2px 0 4px;
color:#f2f7ff;
font-size:18px;
}
.rv-menu-header p {
margin:0;
color:var(--muted);
}
.rv-menu-tabs>.cxd-Tabs-links {
margin-top:10px!important;
border-bottom-color:var(--line)!important;
}
.rv-menu-tabs>.cxd-Tabs-content {
padding:10px 0 0!important;
border:0!important;
background:transparent!important;
}
.rv-control-panel {
margin:0 0 8px!important;
border:1px solid var(--line)!important;
background:#0b121b!important;
}
.rv-control-panel>.cxd-Panel-heading {
border-bottom:1px solid var(--line)!important;
background:#0d1621!important;
color:var(--accent2)!important;
font-weight:700!important;
}
.rv-control-panel>.cxd-Panel-body {
padding:8px 10px!important;
background:transparent!important;
}
.rv-control-form {
margin:0!important;
padding:4px 0!important;
border-bottom:1px solid #182331;
}
.rv-control-form:last-child {
border-bottom:0;
}
.rv-control-form .cxd-Form-row {
margin:0!important;
}
.rv-action-form {
margin:0!important;
padding:10px 0!important;
border-bottom:1px solid #182331;
}
.rv-action-form:last-child {
border-bottom:0;
}
.rv-action-head {
display:flex;
align-items:flex-start;
justify-content:space-between;
gap:12px;
margin-bottom:8px;
}
.rv-action-head>div {
min-width:0;
}
.rv-action-head b {
display:block;
color:#edf7ff;
font-size:12px;
}
.rv-action-head code {
display:block;
margin-top:3px;
color:var(--accent);
font:9px/1.35 ui-monospace,monospace;
overflow-wrap:anywhere;
}
.rv-action-head small {
max-width:45%;
color:var(--muted);
font-size:9px;
line-height:1.4;
}
.rv-json .cxd-JSONField {
border-color:var(--line)!important;
background:#070c12!important;
color:#cddbf0!important;
}
.cxd-Panel,.cxd-Card,.cxd-Form,.cxd-Tabs-content,.cxd-Drawer-content,.cxd-Modal-content {
color:var(--text)!important;
}
.cxd-Button {
border-color:var(--line2)!important;
background:#111b27!important;
color:#c9d7ea!important;
box-shadow:none!important;
}
.cxd-Button:hover,.cxd-Button.is-active,.cxd-ButtonGroup .cxd-Button.is-active {
border-color:#35e6b266!important;
background:#35e6b212!important;
color:var(--accent)!important;
}
.cxd-Button--primary {
border-color:#35e6b25c!important;
background:#12342e!important;
color:#75f6d2!important;
}
.cxd-Form-label,.cxd-Form-itemLabel,.cxd-Form-itemLabel label {
color:#c9d7ea!important;
}
.cxd-Form-description,.cxd-Form-help,.cxd-Form-itemDesc {
color:var(--muted)!important;
}
.cxd-TextControl-input,.cxd-NumberControl-input,.cxd-Select,.cxd-SelectControl,.cxd-ColorPicker,.cxd-InputBox,.cxd-TextControl {
border-color:var(--line2)!important;
background:#070d14!important;
color:var(--text)!important;
}
.cxd-Select-menu,.cxd-PopOver,.cxd-PopOverAble-popover {
border-color:var(--line2)!important;
background:#0d1621!important;
color:var(--text)!important;
}
.cxd-Tabs-links>li>a {
color:var(--muted)!important;
}
.cxd-Tabs-links>li.is-active>a {
color:var(--accent)!important;
}
.cxd-Tabs-links>li.is-active>a:after {
background:var(--accent)!important;
}
.cxd-Alert {
border-color:var(--line2)!important;
background:#0c1420!important;
color:#cbd9eb!important;
}
.cxd-Alert--danger {
border-color:#ff6b8159!important;
background:#271018!important;
color:#ffb2bd!important;
}
.cxd-Switch.is-checked {
background:var(--accent)!important;
}
@media (max-width:1100px) {
.rv-hero {
grid-template-columns:1fr;
}
.rv-card-meta {
grid-template-columns:repeat(2,minmax(0,1fr));
}
}
@media (max-width:720px) {
.rv-shell {
padding:12px 8px 28px;
}
.rv-topbar,.rv-page-header {
align-items:flex-start;
flex-direction:column;
}
.rv-top-actions {
width:100%;
justify-content:space-between;
}
.rv-page-header p {
text-align:left;
}
.rv-mode-tabs>.cxd-Tabs-links {
grid-template-columns:1fr;
}
.rv-card-meta {
grid-template-columns:1fr;
}
.rv-menu-host {
padding:6px;
}
.rv-menu-root {
width:100%;
max-height:calc(100vh - 12px);
}
}
margin: 0; min-width: 320px; min-height: 100vh; color: var(--text);
background: linear-gradient(rgba(255,255,255,.018) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,.018) 1px, transparent 1px),
radial-gradient(circle at 16% -10%, rgba(69,221,190,.12), transparent 34rem), var(--bg);
background-size: 24px 24px, 24px 24px, auto, auto;
}
button, input, select { font: inherit; } button { color: inherit; }
h1, h2, h3, p { margin-top: 0; }
.eyebrow { margin: 0 0 5px; color: var(--accent); font: 700 10px/1.2 ui-monospace, monospace; letter-spacing: .15em; text-transform: uppercase; }
.topbar { position: sticky; z-index: 20; top: 0; display: flex; align-items: center; justify-content: space-between; gap: 22px; min-height: 76px; padding: 12px clamp(18px,4vw,56px); border-bottom: 1px solid #ffffff14; background: #07090dea; backdrop-filter: blur(20px); }
.brand, .topbar-actions { display: flex; align-items: center; gap: 14px; }
.brand-mark { display: grid; place-items: center; width: 46px; height: 46px; border-radius: 12px; color: #07110f; background: var(--accent); font: 800 16px/1 ui-monospace, monospace; }
h1 { margin: 0; font-size: clamp(17px,2vw,23px); }
.connection-status, .card-socket { display: inline-flex; align-items: center; gap: 7px; color: var(--muted); font: 600 11px/1 ui-monospace, monospace; }
.connection-status i, .card-socket i { width: 8px; height: 8px; border-radius: 50%; background: var(--warning); box-shadow: 0 0 12px currentColor; }
[data-state="ready"] i { background: var(--accent); } [data-state="error"] i { background: var(--danger); }
.card-health { display:flex; align-items:flex-end; flex-direction:column; gap:7px; }
.motion-status { display:inline-flex; align-items:center; gap:6px; color:var(--muted); font:600 9px/1 ui-monospace,monospace; }
.motion-status i { width:6px; height:6px; border-radius:50%; background:var(--warning); }
.motion-status[data-state="moving"] { color:var(--accent); }.motion-status[data-state="moving"] i { background:var(--accent); box-shadow:0 0 10px var(--accent); animation:motion-pulse .9s ease-in-out infinite alternate; }
.motion-status[data-state="duplicate"] { color:var(--warning); }.motion-status[data-state="duplicate"] i { background:var(--warning); }
.motion-status[data-state="stalled"] { color:var(--danger); }.motion-status[data-state="stalled"] i { background:var(--danger); }
@keyframes motion-pulse { to { opacity:.35; transform:scale(.72); } }
.quiet-button, .open-menu, .frame-button, .icon-button, .menu-tabs button, .category-filter button, .mode-tabs button, .action-button { border: 1px solid var(--line); border-radius: 8px; background: var(--panel2); cursor: pointer; transition: .16s border-color,.16s background,.16s transform; }
button:hover { border-color: var(--accent); } button:active { transform: translateY(1px); }
.quiet-button { padding: 9px 13px; font-size: 12px; }
.hero { display: grid; grid-template-columns: minmax(300px,1fr) minmax(430px,.9fr); align-items: end; gap: 40px; max-width: 1680px; margin: auto; padding: clamp(32px,5vw,64px) clamp(18px,4vw,56px) 28px; }
.hero h2 { margin-bottom: 10px; font-size: clamp(27px,4vw,47px); letter-spacing: -.045em; }
.hero > div > p:last-child { margin: 0; max-width: 750px; color: var(--muted); line-height: 1.7; }
.metrics { display: grid; grid-template-columns: repeat(4,1fr); gap: 1px; margin: 0; border: 1px solid var(--line); background: var(--line); }
.metrics div { padding: 17px; background: var(--panel); } .metrics dt { color: var(--text); font: 750 clamp(20px,3vw,33px)/1 ui-monospace,monospace; } .metrics dd { margin: 8px 0 0; color: var(--muted); font-size: 10px; }
.mode-tabs { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; max-width: 1680px; margin: auto; padding: 0 clamp(18px,4vw,56px) 18px; }
.mode-tabs button { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px; color: var(--muted); text-align: left; }
.mode-tabs button span { color: var(--text); font-weight: 750; } .mode-tabs button code { font-size: 10px; }
.mode-tabs button.active { border-color: var(--accent); background: #123029; box-shadow: inset 0 0 0 1px #45ddbe35; }
.category-filter { display: flex; gap: 7px; overflow-x: auto; max-width: 1680px; margin: auto; padding: 0 clamp(18px,4vw,56px) 20px; }
.category-filter button { flex: 0 0 auto; padding: 8px 11px; color: var(--muted); font-size: 11px; }
.category-filter button.active { color: #06110e; border-color: var(--accent); background: var(--accent); }
.pages, .mode-page { max-width: 1680px; margin: auto; }
.mode-page[hidden] { display: none; }
.page-header { display: flex; align-items: end; justify-content: space-between; gap: 24px; padding: 3px clamp(18px,4vw,56px) 18px; }
.page-header h2 { margin: 0; font-size: 23px; }.page-strategy { margin-bottom: 5px; color: var(--accent); font: 700 10px/1 ui-monospace,monospace; }.page-description { max-width: 650px; margin: 0; color: var(--muted); font-size: 12px; line-height: 1.6; text-align: right; }
.gallery { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 18px; padding: 0 clamp(18px,4vw,56px) 70px; }
.loading-card { display: grid; place-items: center; gap: 12px; min-height: 360px; margin: 0 clamp(18px,4vw,56px); border: 1px dashed var(--strong); color: var(--muted); }
.loader { width: 18px; height: 18px; border: 2px solid var(--strong); border-top-color: var(--accent); border-radius: 50%; animation: spin .8s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } }
.plot-card { min-width: 0; overflow: hidden; border: 1px solid var(--line); border-radius: 13px; background: linear-gradient(180deg,#ffffff08,transparent 30%),var(--panel); box-shadow: 0 20px 55px #0000002e; }
.plot-card[data-mode="manual"] { border-top-color: #ffd166aa; }.plot-card[data-mode="low_latency"] { border-top-color: #45ddbeaa; }.plot-card[data-mode="playback"] { border-top-color: #6aa9ffaa; }.plot-card[hidden] { display:none; }
.card-header, .card-footer { display:flex; align-items:center; justify-content:space-between; gap:15px; padding:13px 15px; }.card-header { border-bottom:1px solid var(--line); }.card-category { display:block; margin-bottom:4px; color:var(--blue); font:700 9px/1 ui-monospace,monospace; letter-spacing:.1em; }.card-title { margin:0; font-size:17px; }
.canvas-shell { position:relative; height:clamp(245px,27vw,350px); outline:none; background:#060a11; cursor:crosshair; }.canvas-shell:focus-visible { box-shadow:inset 0 0 0 2px var(--accent); }.canvas-shell canvas { display:block; width:100%; height:100%; }.canvas-hint { position:absolute; right:9px; bottom:8px; padding:5px 7px; border:1px solid #ffffff1e; border-radius:5px; color:#ffffffa8; background:#04070bc9; font:9px/1 ui-monospace,monospace; pointer-events:none; }.canvas-loading { position:absolute; inset:0; display:grid; place-content:center; justify-items:center; gap:9px; color:var(--muted); background:#080d15; }.plot-card[data-ready="true"] .canvas-loading { display:none; }
.performance-strip { display:grid; grid-template-columns:repeat(auto-fit,minmax(105px,1fr)); gap:1px; margin:0; border-block:1px solid var(--line); background:var(--line); }.performance-strip div { min-width:0; padding:9px 8px; background:#0c1118; }.performance-strip dt { min-width:0; overflow:visible; color:var(--accent); font:700 11px/1.35 ui-monospace,monospace; text-overflow:clip; white-space:normal; overflow-wrap:anywhere; word-break:break-word; }.performance-strip .perf-event,.performance-strip .perf-limit { font-size:10px; }.performance-strip .perf-limit-cell { grid-column:auto; }.performance-strip dd { margin:5px 0 0; color:var(--muted); font-size:8px; line-height:1.35; white-space:normal; overflow-wrap:anywhere; word-break:break-word; }
.limit-flags { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:7px; padding:7px 10px; border-bottom:1px solid var(--line); color:var(--muted); background:#090e15; font:9px/1.35 ui-monospace,monospace; }.limit-flags strong { grid-column:1/-1; color:var(--text); }.limit-flags span { min-width:0; padding:6px 7px; border:1px solid var(--line); border-radius:4px; line-height:1.45; white-space:normal; overflow-wrap:anywhere; word-break:break-word; }.limit-flags span[data-active="true"] { border-color:#ffb84d88; color:#ffcf82; background:#ff9d1712; }.limit-flags b { color:inherit; white-space:normal; overflow-wrap:anywhere; }.plot-card:not([data-mode="low_latency"]) .limit-flags { display:none; }
.kernel-observer-panel { border-bottom:1px solid var(--line); background:#080d14; font-family:ui-monospace,monospace; }
.kernel-observer-panel > header { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:9px 11px; border-bottom:1px solid var(--line); color:var(--muted); font-size:8px; flex-wrap:wrap; }
.kernel-observer-panel > header div:first-child { display:flex; align-items:center; gap:9px; min-width:0; flex-wrap:wrap; }.kernel-observer-panel > header span { color:var(--accent); letter-spacing:.08em; overflow-wrap:anywhere; }.kernel-observer-panel > header strong,.kernel-observer-panel > header b { color:var(--text); font-weight:700; overflow-wrap:anywhere; }
.observer-counters,.latency-summary,.client-summary,.latency-details { display:grid; gap:1px; background:var(--line); }
.observer-counters { grid-template-columns:repeat(auto-fit,minmax(90px,1fr)); }.latency-summary,.client-summary { grid-template-columns:repeat(auto-fit,minmax(105px,1fr)); border-top:1px solid var(--line); }.latency-details { grid-template-columns:repeat(auto-fit,minmax(145px,1fr)); border-top:1px solid var(--line); }
.observer-counters div,.latency-summary div,.client-summary div,.latency-details div { min-width:0; padding:8px 7px; background:#0b1119; }
.kernel-observer-panel small { display:block; min-width:0; overflow:visible; margin-bottom:5px; color:var(--muted); font-size:7px; line-height:1.35; text-overflow:clip; white-space:normal; overflow-wrap:anywhere; word-break:break-word; }
.kernel-observer-panel b { display:block; min-width:0; overflow:visible; color:#b7c7dc; font-size:9px; line-height:1.35; text-overflow:clip; white-space:normal; overflow-wrap:anywhere; word-break:break-word; }
.latency-summary b { color:var(--accent); font-size:10px; }.latency-summary .critical { background:#10201e; }.latency-summary .critical b { color:#72f3d9; }
.plot-card:not([data-mode="low_latency"]) .kernel-observer-panel { display:none; }
.card-meta { display:grid; grid-template-columns:1fr auto; gap:17px; padding:13px 15px; }.card-description { margin:0; color:var(--muted); font-size:11px; line-height:1.55; }.card-meta dl { display:flex; margin:0; }.card-meta dl div { min-width:52px; padding-left:11px; border-left:1px solid var(--line); }.card-meta dt { font:700 14px/1 ui-monospace,monospace; }.card-meta dd { margin:5px 0 0; color:var(--muted); font-size:9px; }
.card-footer { border-top:1px solid var(--line); background:#0000001e; }.card-footer code { color:var(--accent); font-size:10px; }.card-footer > div { display:flex; gap:7px; }.open-menu,.frame-button { padding:7px 9px; color:var(--muted); font-size:10px; }.frame-button { color:var(--text); }
.context-menu { position:fixed; z-index:100; width:min(570px,calc(100vw - 24px)); max-height:min(840px,calc(100vh - 24px)); overflow:hidden; border:1px solid var(--strong); border-radius:12px; background:#0f131afa; box-shadow:0 30px 90px #0000009e; backdrop-filter:blur(22px); }.context-menu[hidden] { display:none; }
.menu-header { display:flex; justify-content:space-between; gap:18px; padding:16px 18px 13px; border-bottom:1px solid var(--line); }.menu-header h2 { margin-bottom:6px; font-size:19px; }.menu-header p:last-child { margin:0; color:var(--muted); font-size:10px; line-height:1.5; }.icon-button { flex:0 0 auto; width:31px; height:31px; font-size:20px; }
.menu-tabs { display:grid; grid-template-columns:repeat(4,1fr); padding:8px; border-bottom:1px solid var(--line); }.menu-tabs button { padding:8px 4px; border-color:transparent; color:var(--muted); background:transparent; font-size:10px; }.menu-tabs button.active { color:var(--text); border-color:var(--strong); background:var(--panel2); }
.menu-body { max-height:calc(min(840px,100vh - 24px) - 190px); overflow:auto; padding:12px; overscroll-behavior:contain; }.control-group + .control-group { margin-top:15px; }.control-group h3 { position:sticky; z-index:1; top:-12px; margin:0 -2px 6px; padding:9px 4px 7px; color:var(--accent); background:#0f131af5; font:700 9px/1 ui-monospace,monospace; letter-spacing:.1em; }
.control-row { display:grid; grid-template-columns:minmax(145px,1fr) minmax(130px,.75fr); gap:13px; align-items:center; min-height:49px; padding:8px 9px; border-top:1px solid #ffffff0e; }.control-copy label,.action-copy strong { display:block; margin-bottom:4px; font-size:11px; }.control-copy code,.action-copy code { display:block; overflow:hidden; color:var(--muted); font-size:9px; text-overflow:ellipsis; white-space:nowrap; }.control-input { width:100%; min-width:0; padding:7px 8px; border:1px solid var(--strong); border-radius:6px; outline:none; color:var(--text); background:#090d13; font-size:10px; }.control-input:focus { border-color:var(--accent); }input[type="color"].control-input { height:34px; padding:3px; }input[type="checkbox"].control-input { justify-self:end; width:38px; height:20px; accent-color:var(--accent); }
.action-row { display:flex; align-items:center; justify-content:space-between; gap:13px; padding:10px 9px; border-top:1px solid #ffffff0e; }.action-copy { min-width:0; }.action-controls { display:flex; gap:7px; align-items:center; }.action-controls input { width:105px; }.action-button { padding:7px 10px; color:#07110f; border-color:var(--accent); background:var(--accent); font-size:10px; font-weight:700; }
.telemetry-grid { display:grid; grid-template-columns:minmax(165px,.7fr) 1fr; margin:0; border:1px solid var(--line); }.telemetry-grid dt,.telemetry-grid dd { margin:0; padding:8px 10px; border-bottom:1px solid var(--line); font:10px/1.4 ui-monospace,monospace; overflow-wrap:anywhere; }.telemetry-grid dt { color:var(--muted); background:#ffffff06; }.telemetry-grid dd { color:var(--accent); }
.menu-footer { display:flex; justify-content:space-between; gap:14px; padding:10px 14px; border-top:1px solid var(--line); color:var(--muted); font-size:9px; }.menu-footer code { color:var(--accent); }
.toast { position:fixed; z-index:140; left:50%; bottom:22px; max-width:min(560px,calc(100vw - 30px)); padding:11px 15px; border:1px solid var(--strong); border-radius:8px; background:var(--panel2); box-shadow:0 18px 50px #00000073; transform:translateX(-50%); font-size:11px; }.toast[data-error="true"] { border-color:var(--danger); }
@media (max-width:980px) { .hero { grid-template-columns:1fr; }.gallery { grid-template-columns:1fr; }.mode-tabs button { flex-direction:column; align-items:flex-start; }.page-header { align-items:flex-start; flex-direction:column; }.page-description { text-align:left; } }
@media (max-width:650px) { .topbar { align-items:flex-start; }.topbar-actions { align-items:flex-end; flex-direction:column; }.connection-status span { display:none; }.metrics { grid-template-columns:repeat(2,1fr); }.mode-tabs { grid-template-columns:1fr; }.mode-tabs button { flex-direction:row; }.card-meta { grid-template-columns:1fr; }.performance-strip { grid-template-columns:repeat(auto-fit,minmax(92px,1fr)); }.limit-flags { grid-template-columns:1fr; }.observer-counters { grid-template-columns:repeat(auto-fit,minmax(80px,1fr)); }.latency-summary,.client-summary,.latency-details { grid-template-columns:repeat(auto-fit,minmax(100px,1fr)); }.context-menu { inset:auto 6px 6px!important; width:auto; max-height:calc(100vh - 12px); } }