@ioai/robot_viewer 0.0.0-stage → 1.1.2

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.
Files changed (159) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +217 -2
  3. package/bin/cli.js +83 -0
  4. package/dist/assets/css/PlotPanel-3e7d7555.css +1 -0
  5. package/dist/assets/js/ColladaLoader-2a0c4638.js +1 -0
  6. package/dist/assets/js/GLTFLoader-7a7f40d7.js +1 -0
  7. package/dist/assets/js/MTLLoader-b6ce88b7.js +1 -0
  8. package/dist/assets/js/OBJLoader-7667aa01.js +1 -0
  9. package/dist/assets/js/PlotPanel-09f986c7.js +1 -0
  10. package/dist/assets/js/STLLoader-de0915d4.js +1 -0
  11. package/dist/assets/js/URDFLoader-a34b6917.js +1 -0
  12. package/dist/assets/js/USDZLoader-64197f74.js +7 -0
  13. package/dist/assets/js/__vite-browser-external-afb87ea8.js +1 -0
  14. package/dist/assets/js/codemirror-c3cec7b3.js +1 -0
  15. package/dist/assets/js/codemirror-themes-e6995a6a.js +1 -0
  16. package/dist/assets/js/d3-2e308866.js +1 -0
  17. package/dist/assets/js/index-56e94ce5.js +6 -0
  18. package/dist/assets/js/mujoco_wasm-345a2576.js +1 -0
  19. package/dist/assets/js/three-9c891de0.js +6 -0
  20. package/dist/camera-publish-worker.js +1 -0
  21. package/dist/favicon.svg +2 -0
  22. package/dist/heartbeat-worker.js +1 -0
  23. package/dist/index.html +4373 -0
  24. package/dist/models/mocap/blender_human.urdf +2319 -0
  25. package/dist/models/mocap/blender_human_skeleton_v4.urdf +1432 -0
  26. package/dist/models/mocap/blender_human_skeleton_v5.urdf +2717 -0
  27. package/dist/models/mocap/meshes/v4/L_base_link.STL +0 -0
  28. package/dist/models/mocap/meshes/v4/L_figer1_1.STL +0 -0
  29. package/dist/models/mocap/meshes/v4/L_figer1_2.STL +0 -0
  30. package/dist/models/mocap/meshes/v4/L_figer1_3.STL +0 -0
  31. package/dist/models/mocap/meshes/v4/L_figer1_4.STL +0 -0
  32. package/dist/models/mocap/meshes/v4/L_figer1_5.STL +0 -0
  33. package/dist/models/mocap/meshes/v4/L_figer1base.STL +0 -0
  34. package/dist/models/mocap/meshes/v4/L_figer2_1.STL +0 -0
  35. package/dist/models/mocap/meshes/v4/L_figer2_2.STL +0 -0
  36. package/dist/models/mocap/meshes/v4/L_figer2_3.STL +0 -0
  37. package/dist/models/mocap/meshes/v4/L_figer2_4.STL +0 -0
  38. package/dist/models/mocap/meshes/v4/L_figer2_5.STL +0 -0
  39. package/dist/models/mocap/meshes/v4/L_figer3_1.STL +0 -0
  40. package/dist/models/mocap/meshes/v4/L_figer3_2.STL +0 -0
  41. package/dist/models/mocap/meshes/v4/L_figer3_3.STL +0 -0
  42. package/dist/models/mocap/meshes/v4/L_figer3_4.STL +0 -0
  43. package/dist/models/mocap/meshes/v4/L_figer3_5.STL +0 -0
  44. package/dist/models/mocap/meshes/v4/L_figer4_1.STL +0 -0
  45. package/dist/models/mocap/meshes/v4/L_figer4_2.STL +0 -0
  46. package/dist/models/mocap/meshes/v4/L_figer4_3.STL +0 -0
  47. package/dist/models/mocap/meshes/v4/L_figer4_4.STL +0 -0
  48. package/dist/models/mocap/meshes/v4/L_figer4_5.STL +0 -0
  49. package/dist/models/mocap/meshes/v4/L_figer5_1.STL +0 -0
  50. package/dist/models/mocap/meshes/v4/L_figer5_2.STL +0 -0
  51. package/dist/models/mocap/meshes/v4/L_figer5_3.STL +0 -0
  52. package/dist/models/mocap/meshes/v4/L_figer5_4.STL +0 -0
  53. package/dist/models/mocap/meshes/v4/L_figer5_5.STL +0 -0
  54. package/dist/models/mocap/meshes/v4/L_handle.STL +0 -0
  55. package/dist/models/mocap/meshes/v4/L_handleseat.STL +0 -0
  56. package/dist/models/mocap/meshes/v4/L_mark.STL +0 -0
  57. package/dist/models/mocap/meshes/v4/L_markseat.STL +0 -0
  58. package/dist/models/mocap/meshes/v4/Lgamepadbase.STL +0 -0
  59. package/dist/models/mocap/meshes/v4/Link1.STL +0 -0
  60. package/dist/models/mocap/meshes/v4/Link2.STL +0 -0
  61. package/dist/models/mocap/meshes/v4/R_base_link.STL +0 -0
  62. package/dist/models/mocap/meshes/v4/R_figer1_1.STL +0 -0
  63. package/dist/models/mocap/meshes/v4/R_figer1_2.STL +0 -0
  64. package/dist/models/mocap/meshes/v4/R_figer1_3.STL +0 -0
  65. package/dist/models/mocap/meshes/v4/R_figer1_4.STL +0 -0
  66. package/dist/models/mocap/meshes/v4/R_figer1_5.STL +0 -0
  67. package/dist/models/mocap/meshes/v4/R_figer1base.STL +0 -0
  68. package/dist/models/mocap/meshes/v4/R_figer2_1.STL +0 -0
  69. package/dist/models/mocap/meshes/v4/R_figer2_2.STL +0 -0
  70. package/dist/models/mocap/meshes/v4/R_figer2_3.STL +0 -0
  71. package/dist/models/mocap/meshes/v4/R_figer2_4.STL +0 -0
  72. package/dist/models/mocap/meshes/v4/R_figer2_5.STL +0 -0
  73. package/dist/models/mocap/meshes/v4/R_figer3_1.STL +0 -0
  74. package/dist/models/mocap/meshes/v4/R_figer3_2.STL +0 -0
  75. package/dist/models/mocap/meshes/v4/R_figer3_3.STL +0 -0
  76. package/dist/models/mocap/meshes/v4/R_figer3_4.STL +0 -0
  77. package/dist/models/mocap/meshes/v4/R_figer3_5.STL +0 -0
  78. package/dist/models/mocap/meshes/v4/R_figer4_1.STL +0 -0
  79. package/dist/models/mocap/meshes/v4/R_figer4_2.STL +0 -0
  80. package/dist/models/mocap/meshes/v4/R_figer4_3.STL +0 -0
  81. package/dist/models/mocap/meshes/v4/R_figer4_4.STL +0 -0
  82. package/dist/models/mocap/meshes/v4/R_figer4_5.STL +0 -0
  83. package/dist/models/mocap/meshes/v4/R_figer5_1.STL +0 -0
  84. package/dist/models/mocap/meshes/v4/R_figer5_2.STL +0 -0
  85. package/dist/models/mocap/meshes/v4/R_figer5_3.STL +0 -0
  86. package/dist/models/mocap/meshes/v4/R_figer5_4.STL +0 -0
  87. package/dist/models/mocap/meshes/v4/R_figer5_5.STL +0 -0
  88. package/dist/models/mocap/meshes/v4/R_handle.STL +0 -0
  89. package/dist/models/mocap/meshes/v4/R_handleseat.STL +0 -0
  90. package/dist/models/mocap/meshes/v4/R_mark.STL +0 -0
  91. package/dist/models/mocap/meshes/v4/R_markseat.STL +0 -0
  92. package/dist/models/mocap/meshes/v4/Rgamepadbase.STL +0 -0
  93. package/dist/models/mocap/meshes/v4/base_link.STL +0 -0
  94. package/dist/models/mocap/meshes/v4/r_figer1_1.STL +0 -0
  95. package/dist/models/mocap/meshes/v5/L_handleseat.STL +0 -0
  96. package/dist/models/mocap/meshes/v5/R_handleseat.STL +0 -0
  97. package/dist/models/mocap/meshes/v5/link_LeftSkeletonBase.STL +0 -0
  98. package/dist/models/mocap/meshes/v5/link_LeftSkeletonIndex1.STL +0 -0
  99. package/dist/models/mocap/meshes/v5/link_LeftSkeletonIndex2.STL +0 -0
  100. package/dist/models/mocap/meshes/v5/link_LeftSkeletonIndex3.STL +0 -0
  101. package/dist/models/mocap/meshes/v5/link_LeftSkeletonIndex4.STL +0 -0
  102. package/dist/models/mocap/meshes/v5/link_LeftSkeletonMiddle1.STL +0 -0
  103. package/dist/models/mocap/meshes/v5/link_LeftSkeletonMiddle2.STL +0 -0
  104. package/dist/models/mocap/meshes/v5/link_LeftSkeletonMiddle3.STL +0 -0
  105. package/dist/models/mocap/meshes/v5/link_LeftSkeletonMiddle4.STL +0 -0
  106. package/dist/models/mocap/meshes/v5/link_LeftSkeletonPinky1.STL +0 -0
  107. package/dist/models/mocap/meshes/v5/link_LeftSkeletonPinky2.STL +0 -0
  108. package/dist/models/mocap/meshes/v5/link_LeftSkeletonPinky3.STL +0 -0
  109. package/dist/models/mocap/meshes/v5/link_LeftSkeletonPinky4.STL +0 -0
  110. package/dist/models/mocap/meshes/v5/link_LeftSkeletonRing1.STL +0 -0
  111. package/dist/models/mocap/meshes/v5/link_LeftSkeletonRing2.STL +0 -0
  112. package/dist/models/mocap/meshes/v5/link_LeftSkeletonRing3.STL +0 -0
  113. package/dist/models/mocap/meshes/v5/link_LeftSkeletonRing4.STL +0 -0
  114. package/dist/models/mocap/meshes/v5/link_LeftSkeletonThumb1.STL +0 -0
  115. package/dist/models/mocap/meshes/v5/link_LeftSkeletonThumb2.STL +0 -0
  116. package/dist/models/mocap/meshes/v5/link_LeftSkeletonThumb3.STL +0 -0
  117. package/dist/models/mocap/meshes/v5/link_LeftSkeletonThumb4.STL +0 -0
  118. package/dist/models/mocap/meshes/v5/link_LeftSkeletonThumbBase.STL +0 -0
  119. package/dist/models/mocap/meshes/v5/link_RightSkeletonBase.STL +0 -0
  120. package/dist/models/mocap/meshes/v5/link_RightSkeletonIndex1.STL +0 -0
  121. package/dist/models/mocap/meshes/v5/link_RightSkeletonIndex2.STL +0 -0
  122. package/dist/models/mocap/meshes/v5/link_RightSkeletonIndex3.STL +0 -0
  123. package/dist/models/mocap/meshes/v5/link_RightSkeletonIndex4.STL +0 -0
  124. package/dist/models/mocap/meshes/v5/link_RightSkeletonMiddle1.STL +0 -0
  125. package/dist/models/mocap/meshes/v5/link_RightSkeletonMiddle2.STL +0 -0
  126. package/dist/models/mocap/meshes/v5/link_RightSkeletonMiddle3.STL +0 -0
  127. package/dist/models/mocap/meshes/v5/link_RightSkeletonMiddle4.STL +0 -0
  128. package/dist/models/mocap/meshes/v5/link_RightSkeletonPinky1.STL +0 -0
  129. package/dist/models/mocap/meshes/v5/link_RightSkeletonPinky2.STL +0 -0
  130. package/dist/models/mocap/meshes/v5/link_RightSkeletonPinky3.STL +0 -0
  131. package/dist/models/mocap/meshes/v5/link_RightSkeletonPinky4.STL +0 -0
  132. package/dist/models/mocap/meshes/v5/link_RightSkeletonRing1.STL +0 -0
  133. package/dist/models/mocap/meshes/v5/link_RightSkeletonRing2.STL +0 -0
  134. package/dist/models/mocap/meshes/v5/link_RightSkeletonRing3.STL +0 -0
  135. package/dist/models/mocap/meshes/v5/link_RightSkeletonRing4.STL +0 -0
  136. package/dist/models/mocap/meshes/v5/link_RightSkeletonThumb1.STL +0 -0
  137. package/dist/models/mocap/meshes/v5/link_RightSkeletonThumb2.STL +0 -0
  138. package/dist/models/mocap/meshes/v5/link_RightSkeletonThumb3.STL +0 -0
  139. package/dist/models/mocap/meshes/v5/link_RightSkeletonThumb4.STL +0 -0
  140. package/dist/models/mocap/meshes/v5/link_RightSkeletonThumbBase.STL +0 -0
  141. package/dist/scripts/start-rosbridge.sh +77 -0
  142. package/dist/usd-iframe.html +41 -0
  143. package/dist/usd-viewer/bindings/.gitattributes +2 -0
  144. package/dist/usd-viewer/bindings/emHdBindings.data +19331 -0
  145. package/dist/usd-viewer/bindings/emHdBindings.js +12281 -0
  146. package/dist/usd-viewer/bindings/emHdBindings.wasm +0 -0
  147. package/dist/usd-viewer/bindings/emHdBindings.worker.js +124 -0
  148. package/dist/usd-viewer/environments/neutral.hdr +0 -0
  149. package/dist/usd-viewer/error-handler.js +57 -0
  150. package/dist/usd-viewer/hydra/HydraPrimitives.js +740 -0
  151. package/dist/usd-viewer/hydra/TextureRegistry.js +130 -0
  152. package/dist/usd-viewer/hydra/ThreeJsRenderDelegate.js +65 -0
  153. package/dist/usd-viewer/index.d.ts +1 -0
  154. package/dist/usd-viewer/main.js +1018 -0
  155. package/dist/usd-viewer/usd.html +36 -0
  156. package/dist/usd-viewer/usdUtils.js +67 -0
  157. package/package.json +70 -4
  158. package/server/package.json +14 -0
  159. package/server/static-server.js +106 -0
@@ -0,0 +1,4373 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="color-scheme" content="dark light">
7
+ <title>IO Robot Viewer</title>
8
+ <link rel="icon" type="image/svg+xml" href="./favicon.svg">
9
+ <link rel="alternate icon" href="./favicon.svg">
10
+ <!-- Blocking script: Set theme before rendering any content -->
11
+ <script>
12
+ (function() {
13
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
14
+ const theme = localStorage.getItem('theme') || (prefersDark ? 'dark' : 'light');
15
+ const bgColor = theme === 'dark' ? '#000000' : '#ffffff';
16
+
17
+ // Write styles directly to document
18
+ document.write('<style>html,body{margin:0;padding:0;background-color:' + bgColor + ';transition:none !important;}</style>');
19
+
20
+ // Set theme attribute for CSS
21
+ if (document.documentElement) {
22
+ document.documentElement.setAttribute('data-theme', theme);
23
+ }
24
+ })();
25
+ </script>
26
+ <style>
27
+ :root {
28
+ /* Apple Glass Design System */
29
+ --bg-primary: #000000;
30
+ --glass-bg: rgba(10, 10, 12, 0.7);
31
+ --glass-border: rgba(255, 255, 255, 0.08);
32
+ --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
33
+ --text-primary: #e8e8e8;
34
+ --text-secondary: #b8b8bd;
35
+ --text-tertiary: #7a7a7f;
36
+ --accent: #1e8e3e;
37
+ --accent-hover: #2aa14c;
38
+ --accent-secondary: rgba(30, 142, 62, 0.15);
39
+ --blur-amount: 24px;
40
+ --saturation: 180%;
41
+ --spring: cubic-bezier(0.4, 0, 0.2, 1);
42
+ }
43
+
44
+ [data-theme="light"] {
45
+ --bg-primary: #f5f5f7;
46
+ --glass-bg: rgba(245, 245, 245, 0.85);
47
+ --glass-border: rgba(0, 0, 0, 0.15);
48
+ --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
49
+ --text-primary: #1d1d1f;
50
+ --text-secondary: #424245;
51
+ --text-tertiary: #86868b;
52
+ --accent-secondary: rgba(30, 142, 62, 0.12);
53
+ --blur-amount: 24px;
54
+ --saturation: 180%;
55
+ }
56
+
57
+ * {
58
+ margin: 0;
59
+ padding: 0;
60
+ box-sizing: border-box;
61
+ }
62
+
63
+ body {
64
+ font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', sans-serif;
65
+ overflow: hidden;
66
+ background: var(--bg-primary);
67
+ color: var(--text-primary);
68
+ -webkit-font-smoothing: antialiased;
69
+ transition: background 0.3s var(--spring);
70
+ }
71
+
72
+ #container {
73
+ position: relative;
74
+ width: 100vw;
75
+ height: 100vh;
76
+ }
77
+
78
+ /* Floating card common styles */
79
+ .floating-card {
80
+ position: fixed;
81
+ width: 320px;
82
+ max-height: calc(100vh - 160px);
83
+ background: var(--glass-bg);
84
+ backdrop-filter: blur(6px) saturate(120%);
85
+ -webkit-backdrop-filter: blur(6px) saturate(120%);
86
+ border: 0.5px solid var(--glass-border);
87
+ border-radius: 16px;
88
+ box-shadow: var(--glass-shadow);
89
+ display: flex;
90
+ flex-direction: column;
91
+ overflow: hidden;
92
+ transition: all 0.4s var(--spring);
93
+ z-index: 50;
94
+ animation: slideIn 0.4s var(--spring);
95
+ }
96
+
97
+ .floating-card.minimized {
98
+ width: auto;
99
+ max-height: auto;
100
+ }
101
+
102
+ .floating-card.minimized .card-content {
103
+ max-height: 0;
104
+ padding: 0;
105
+ opacity: 0;
106
+ }
107
+
108
+ .floating-card::before {
109
+ content: '';
110
+ position: absolute;
111
+ top: 0;
112
+ left: 0;
113
+ right: 0;
114
+ height: 1px;
115
+ background: linear-gradient(90deg,
116
+ transparent 0%,
117
+ rgba(255, 255, 255, 0.2) 50%,
118
+ transparent 100%);
119
+ pointer-events: none;
120
+ z-index: 1;
121
+ }
122
+
123
+ .card-header {
124
+ padding: 14px 16px;
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: space-between;
128
+ border-bottom: 0.5px solid var(--glass-border);
129
+ flex-shrink: 0;
130
+ }
131
+
132
+ .floating-card.minimized .card-header {
133
+ border-bottom: none;
134
+ }
135
+
136
+ .card-title {
137
+ font-size: 13px;
138
+ font-weight: 600;
139
+ color: var(--text-primary);
140
+ letter-spacing: -0.2px;
141
+ }
142
+
143
+ .card-actions {
144
+ display: flex;
145
+ gap: 6px;
146
+ }
147
+
148
+ .card-action-btn {
149
+ width: 22px;
150
+ height: 22px;
151
+ border-radius: 6px;
152
+ border: none;
153
+ background: rgba(255, 255, 255, 0.06);
154
+ color: var(--text-secondary);
155
+ cursor: pointer;
156
+ display: flex;
157
+ align-items: center;
158
+ justify-content: center;
159
+ font-size: 12px;
160
+ transition: all 0.3s var(--spring);
161
+ }
162
+
163
+ .card-action-btn:hover {
164
+ background: rgba(255, 255, 255, 0.12);
165
+ color: var(--text-primary);
166
+ transform: scale(1.15);
167
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
168
+ }
169
+
170
+ .card-action-btn:active {
171
+ transform: scale(0.95);
172
+ }
173
+
174
+ .card-content {
175
+ flex: 1;
176
+ overflow-y: auto;
177
+ padding: 12px;
178
+ min-height: 0;
179
+ transition: all 0.4s var(--spring);
180
+ }
181
+
182
+ .card-content::-webkit-scrollbar {
183
+ width: 6px;
184
+ }
185
+
186
+ .card-content::-webkit-scrollbar-track {
187
+ background: transparent;
188
+ }
189
+
190
+ .card-content::-webkit-scrollbar-thumb {
191
+ background: rgba(255, 255, 255, 0.15);
192
+ border-radius: 6px;
193
+ }
194
+
195
+ .card-content::-webkit-scrollbar-thumb:hover {
196
+ background: rgba(255, 255, 255, 0.25);
197
+ }
198
+
199
+ /* File list floating panel */
200
+ #floating-files-panel {
201
+ top: 80px;
202
+ left: 20px;
203
+ width: 360px;
204
+ height: 340px;
205
+ transition: all 0.4s var(--spring);
206
+ display: flex;
207
+ flex-direction: column;
208
+ }
209
+
210
+ #floating-files-panel .floating-panel-content {
211
+ flex: 1;
212
+ min-height: 0;
213
+ overflow-y: auto;
214
+ }
215
+
216
+ #floating-files-panel.maximized {
217
+ height: calc(100vh - 100px) !important;
218
+ }
219
+
220
+ /* Joint control floating panel */
221
+ #floating-joints-panel {
222
+ top: calc(80px + 340px + 20px);
223
+ left: 20px;
224
+ width: 360px;
225
+ height: calc(100vh - 80px - 340px - 40px);
226
+ transition: all 0.4s var(--spring);
227
+ }
228
+
229
+ #floating-joints-panel.maximized {
230
+ top: 80px !important;
231
+ height: calc(100vh - 100px) !important;
232
+ }
233
+
234
+ #floating-joints-panel .floating-panel-content {
235
+ display: flex;
236
+ flex-direction: column;
237
+ }
238
+
239
+ .floating-panel-content {
240
+ flex: 1;
241
+ overflow-y: auto;
242
+ padding: 12px;
243
+ min-height: 0;
244
+ padding-right: 8px;
245
+ margin-right: 4px;
246
+ }
247
+
248
+ .floating-panel-content::-webkit-scrollbar {
249
+ width: 10px;
250
+ }
251
+
252
+ .floating-panel-content::-webkit-scrollbar-track {
253
+ background: transparent;
254
+ }
255
+
256
+ .floating-panel-content::-webkit-scrollbar-thumb {
257
+ background: rgba(128, 128, 128, 0.3);
258
+ border-radius: 6px;
259
+ border: 2px solid transparent;
260
+ background-clip: padding-box;
261
+ }
262
+
263
+ .floating-panel-content::-webkit-scrollbar-thumb:hover {
264
+ background: rgba(128, 128, 128, 0.5);
265
+ background-clip: padding-box;
266
+ }
267
+
268
+ @keyframes slideIn {
269
+ from {
270
+ opacity: 0;
271
+ transform: translateY(-10px);
272
+ }
273
+ to {
274
+ opacity: 1;
275
+ transform: translateY(0);
276
+ }
277
+ }
278
+
279
+ /* Common floating panel styles */
280
+ .floating-panel {
281
+ position: fixed;
282
+ background: var(--glass-bg);
283
+ backdrop-filter: blur(20px) saturate(150%);
284
+ -webkit-backdrop-filter: blur(20px) saturate(150%);
285
+ border: 0.5px solid var(--glass-border);
286
+ border-radius: 16px;
287
+ box-shadow: var(--glass-shadow);
288
+ z-index: 50;
289
+ display: flex;
290
+ flex-direction: column;
291
+ overflow: hidden;
292
+ min-width: 280px;
293
+ min-height: 100px;
294
+ max-width: calc(100vw - 40px);
295
+ transition: box-shadow 0.3s var(--spring), border-color 0.3s var(--spring);
296
+ animation: slideIn 0.4s var(--spring);
297
+ }
298
+
299
+ .floating-panel:hover {
300
+ box-shadow: 0 12px 48px rgba(0, 0, 0, 0.20);
301
+ border-color: rgba(255, 255, 255, 0.25);
302
+ }
303
+
304
+ .floating-panel::before {
305
+ content: '';
306
+ position: absolute;
307
+ top: 0;
308
+ left: 0;
309
+ right: 0;
310
+ height: 1px;
311
+ background: linear-gradient(90deg,
312
+ transparent 0%,
313
+ rgba(255, 255, 255, 0.2) 50%,
314
+ transparent 100%);
315
+ pointer-events: none;
316
+ z-index: 1;
317
+ }
318
+
319
+ [data-theme="light"] .floating-panel {
320
+ background: rgba(255, 255, 255, 0.9);
321
+ border-color: rgba(0, 0, 0, 0.1);
322
+ }
323
+
324
+ [data-theme="light"] .floating-panel:hover {
325
+ box-shadow: 0 12px 48px rgba(0, 0, 0, 0.12);
326
+ border-color: rgba(0, 0, 0, 0.15);
327
+ }
328
+
329
+ [data-theme="light"] .floating-panel::before {
330
+ background: linear-gradient(90deg,
331
+ transparent 0%,
332
+ rgba(0, 0, 0, 0.08) 50%,
333
+ transparent 100%);
334
+ }
335
+
336
+ #canvas-container {
337
+ position: fixed;
338
+ top: 0;
339
+ left: 0;
340
+ right: 0;
341
+ bottom: 0;
342
+ z-index: 1;
343
+ background: var(--bg-primary);
344
+ }
345
+
346
+ #canvas {
347
+ width: 100%;
348
+ height: 100%;
349
+ display: block;
350
+ }
351
+
352
+ /* Top control bar */
353
+ #top-control-bar {
354
+ position: absolute;
355
+ top: 20px;
356
+ left: 50%;
357
+ transform: translateX(-50%);
358
+ display: flex;
359
+ align-items: center;
360
+ gap: 6px;
361
+ padding: 5px 9px;
362
+ background: var(--glass-bg);
363
+ backdrop-filter: blur(20px) saturate(150%);
364
+ -webkit-backdrop-filter: blur(20px) saturate(150%);
365
+ border: 0.5px solid var(--glass-border);
366
+ border-radius: 21px;
367
+ box-shadow: var(--glass-shadow);
368
+ z-index: 100;
369
+ max-width: calc(100vw - 40px);
370
+ transition: all 0.4s var(--spring);
371
+ }
372
+
373
+ .control-bar-section {
374
+ display: flex;
375
+ align-items: center;
376
+ gap: 6px;
377
+ }
378
+
379
+ .control-bar-section:first-child {
380
+ margin-left: 0;
381
+ }
382
+
383
+ .control-bar-divider {
384
+ width: 1px;
385
+ height: 28px;
386
+ background: var(--glass-border);
387
+ margin: 0 8px;
388
+ }
389
+
390
+ .control-bar-select {
391
+ padding: 6px 28px 6px 10px;
392
+ height: 32px;
393
+ background: transparent;
394
+ border: none;
395
+ border-radius: 12px;
396
+ color: var(--text-primary);
397
+ font-size: 11px;
398
+ cursor: pointer;
399
+ appearance: none;
400
+ background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L5 5L9 1' stroke='%23ffffff' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
401
+ background-repeat: no-repeat;
402
+ background-position: right 8px center;
403
+ transition: background-color 0.2s, border-color 0.2s;
404
+ }
405
+
406
+ .control-bar-select:hover {
407
+ background-color: rgba(255, 255, 255, 0.1);
408
+ }
409
+
410
+ .control-bar-select:focus {
411
+ outline: none;
412
+ background-color: rgba(255, 255, 255, 0.12);
413
+ }
414
+
415
+ .tool-button {
416
+ display: flex;
417
+ align-items: center;
418
+ justify-content: center;
419
+ gap: 5px;
420
+ padding: 5px 12px;
421
+ height: auto;
422
+ background: transparent;
423
+ border: 1px solid rgba(255, 255, 255, 0.1);
424
+ border-radius: 12px;
425
+ cursor: pointer;
426
+ transition: all 0.3s var(--spring);
427
+ color: var(--text-secondary);
428
+ font-size: 11px;
429
+ font-weight: 500;
430
+ white-space: nowrap;
431
+ }
432
+
433
+ .tool-button-icon {
434
+ font-size: 13px;
435
+ }
436
+
437
+ .tool-button:hover {
438
+ background: rgba(255, 255, 255, 0.1);
439
+ color: var(--text-primary);
440
+ transform: scale(1.05);
441
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
442
+ }
443
+
444
+ .tool-button:active {
445
+ transform: scale(0.98);
446
+ }
447
+
448
+ [data-theme="light"] .tool-button {
449
+ border: 1px solid rgba(0, 0, 0, 0.1);
450
+ }
451
+
452
+ [data-theme="light"] .tool-button:hover {
453
+ background: rgba(0, 0, 0, 0.05);
454
+ }
455
+
456
+ .tool-button.active {
457
+ background: var(--accent);
458
+ color: white;
459
+ box-shadow: 0 2px 8px rgba(30, 142, 62, 0.3);
460
+ }
461
+
462
+ .tool-button.active:hover {
463
+ box-shadow: 0 4px 16px rgba(30, 142, 62, 0.4);
464
+ transform: scale(1.05);
465
+ }
466
+
467
+ .tool-button.active:active {
468
+ transform: scale(0.98);
469
+ }
470
+
471
+ /* Drop zone */
472
+ #drop-zone {
473
+ position: fixed;
474
+ top: 0;
475
+ left: 0;
476
+ right: 0;
477
+ bottom: 0;
478
+ border: 2px dashed transparent;
479
+ border-radius: 16px;
480
+ background: rgba(0, 0, 0, 0);
481
+ display: none;
482
+ align-items: center;
483
+ justify-content: center;
484
+ transition: all 0.3s ease;
485
+ z-index: 100;
486
+ pointer-events: none;
487
+ }
488
+
489
+ #drop-zone.drag-over {
490
+ display: flex;
491
+ border-color: var(--accent);
492
+ background: var(--accent-secondary);
493
+ backdrop-filter: blur(var(--blur-amount)) saturate(var(--saturation));
494
+ -webkit-backdrop-filter: blur(var(--blur-amount)) saturate(var(--saturation));
495
+ box-shadow: var(--shadow-lg);
496
+ pointer-events: all;
497
+ }
498
+
499
+ #drop-text {
500
+ text-align: center;
501
+ color: var(--text-secondary);
502
+ font-size: 15px;
503
+ font-weight: 500;
504
+ }
505
+
506
+ #drop-text strong {
507
+ color: var(--accent);
508
+ font-size: 17px;
509
+ display: block;
510
+ margin-bottom: 8px;
511
+ }
512
+
513
+ .github-progress-dialog {
514
+ position: fixed;
515
+ top: 50%;
516
+ left: 50%;
517
+ transform: translate(-50%, -50%) scale(0.9);
518
+ background: var(--glass-bg);
519
+ backdrop-filter: blur(20px) saturate(150%);
520
+ -webkit-backdrop-filter: blur(20px) saturate(150%);
521
+ border: 0.5px solid var(--glass-border);
522
+ border-radius: 16px;
523
+ box-shadow: var(--glass-shadow);
524
+ padding: 24px;
525
+ min-width: 360px;
526
+ z-index: 1000;
527
+ opacity: 0;
528
+ transition: all 0.3s var(--spring);
529
+ }
530
+
531
+ .github-progress-dialog.show {
532
+ opacity: 1;
533
+ transform: translate(-50%, -50%) scale(1);
534
+ }
535
+
536
+ .progress-content {
537
+ display: flex;
538
+ flex-direction: column;
539
+ gap: 16px;
540
+ }
541
+
542
+ .progress-title {
543
+ font-size: 17px;
544
+ font-weight: 600;
545
+ color: var(--text-primary);
546
+ text-align: center;
547
+ }
548
+
549
+ .progress-message {
550
+ font-size: 13px;
551
+ color: var(--text-secondary);
552
+ text-align: center;
553
+ min-height: 20px;
554
+ }
555
+
556
+ .progress-bar-container {
557
+ width: 100%;
558
+ height: 6px;
559
+ background: var(--glass-border);
560
+ border-radius: 3px;
561
+ overflow: hidden;
562
+ }
563
+
564
+ .progress-bar {
565
+ height: 100%;
566
+ background: var(--accent);
567
+ border-radius: 3px;
568
+ transition: width 0.3s ease;
569
+ width: 0%;
570
+ }
571
+
572
+ .progress-text {
573
+ font-size: 13px;
574
+ color: var(--text-tertiary);
575
+ text-align: center;
576
+ font-variant-numeric: tabular-nums;
577
+ }
578
+
579
+ .github-help-dialog {
580
+ position: fixed;
581
+ top: 0;
582
+ left: 0;
583
+ right: 0;
584
+ bottom: 0;
585
+ background: rgba(0, 0, 0, 0.6);
586
+ backdrop-filter: blur(5px);
587
+ -webkit-backdrop-filter: blur(5px);
588
+ display: flex;
589
+ align-items: center;
590
+ justify-content: center;
591
+ z-index: 2000;
592
+ opacity: 0;
593
+ transition: opacity 0.3s var(--spring);
594
+ }
595
+
596
+ .github-help-dialog.show {
597
+ opacity: 1;
598
+ }
599
+
600
+ .help-dialog-content {
601
+ background: var(--glass-bg);
602
+ backdrop-filter: blur(20px) saturate(150%);
603
+ -webkit-backdrop-filter: blur(20px) saturate(150%);
604
+ border: 0.5px solid var(--glass-border);
605
+ border-radius: 16px;
606
+ box-shadow: var(--glass-shadow);
607
+ max-width: 520px;
608
+ width: 90%;
609
+ max-height: 80vh;
610
+ overflow-y: auto;
611
+ transform: scale(0.9);
612
+ transition: transform 0.3s var(--spring);
613
+ }
614
+
615
+ .github-help-dialog.show .help-dialog-content {
616
+ transform: scale(1);
617
+ }
618
+
619
+ .help-dialog-header {
620
+ display: flex;
621
+ justify-content: space-between;
622
+ align-items: center;
623
+ padding: 20px 24px;
624
+ border-bottom: 0.5px solid var(--glass-border);
625
+ }
626
+
627
+ .help-dialog-header h3 {
628
+ font-size: 17px;
629
+ font-weight: 600;
630
+ color: var(--text-primary);
631
+ margin: 0;
632
+ }
633
+
634
+ .help-dialog-close {
635
+ background: none;
636
+ border: none;
637
+ color: var(--text-tertiary);
638
+ font-size: 20px;
639
+ cursor: pointer;
640
+ padding: 4px 8px;
641
+ border-radius: 6px;
642
+ transition: all 0.2s;
643
+ }
644
+
645
+ .help-dialog-close:hover {
646
+ background: var(--glass-border);
647
+ color: var(--text-primary);
648
+ }
649
+
650
+ .help-dialog-body {
651
+ padding: 20px 24px;
652
+ }
653
+
654
+ .help-dialog-body > p {
655
+ color: var(--text-secondary);
656
+ margin: 0 0 20px 0;
657
+ line-height: 1.5;
658
+ }
659
+
660
+ .help-section {
661
+ margin-bottom: 24px;
662
+ }
663
+
664
+ .help-section h4 {
665
+ font-size: 15px;
666
+ font-weight: 600;
667
+ color: var(--text-primary);
668
+ margin: 0 0 12px 0;
669
+ }
670
+
671
+ .help-section p {
672
+ color: var(--text-secondary);
673
+ margin: 0 0 12px 0;
674
+ font-size: 14px;
675
+ line-height: 1.5;
676
+ }
677
+
678
+ .help-section ul {
679
+ margin: 0;
680
+ padding-left: 20px;
681
+ color: var(--text-secondary);
682
+ }
683
+
684
+ .help-section li {
685
+ margin: 8px 0;
686
+ line-height: 1.5;
687
+ }
688
+
689
+ .example-urls {
690
+ display: flex;
691
+ flex-direction: column;
692
+ gap: 8px;
693
+ }
694
+
695
+ .example-urls code {
696
+ background: var(--glass-border);
697
+ padding: 8px 12px;
698
+ border-radius: 6px;
699
+ font-size: 12px;
700
+ color: var(--accent);
701
+ display: block;
702
+ overflow-x: auto;
703
+ white-space: nowrap;
704
+ }
705
+
706
+ .github-path-input {
707
+ width: 100%;
708
+ padding: 10px 12px;
709
+ background: var(--glass-border);
710
+ border: 1px solid var(--glass-border);
711
+ border-radius: 8px;
712
+ color: var(--text-primary);
713
+ font-size: 14px;
714
+ margin-bottom: 12px;
715
+ font-family: 'SF Mono', Monaco, monospace;
716
+ transition: all 0.2s;
717
+ }
718
+
719
+ .github-path-input:focus {
720
+ outline: none;
721
+ border-color: var(--accent);
722
+ background: rgba(255, 255, 255, 0.05);
723
+ }
724
+
725
+ .github-path-input::placeholder {
726
+ color: var(--text-tertiary);
727
+ }
728
+
729
+ .github-load-btn {
730
+ width: 100%;
731
+ padding: 10px 16px;
732
+ background: var(--accent);
733
+ border: none;
734
+ border-radius: 8px;
735
+ color: white;
736
+ font-size: 14px;
737
+ font-weight: 600;
738
+ cursor: pointer;
739
+ transition: all 0.2s;
740
+ }
741
+
742
+ .github-load-btn:hover {
743
+ background: var(--accent-hover);
744
+ transform: translateY(-1px);
745
+ }
746
+
747
+ .github-load-btn:active {
748
+ transform: translateY(0);
749
+ }
750
+
751
+ .help-tips {
752
+ background: rgba(30, 142, 62, 0.1);
753
+ border-left: 3px solid var(--accent);
754
+ padding: 12px 16px;
755
+ border-radius: 8px;
756
+ font-size: 13px;
757
+ color: var(--text-secondary);
758
+ margin-top: 16px;
759
+ }
760
+
761
+ .help-tips strong {
762
+ color: var(--accent);
763
+ }
764
+
765
+ .panel-title {
766
+ font-size: 11px;
767
+ font-weight: 700;
768
+ margin-bottom: 0;
769
+ margin-top: 0;
770
+ color: var(--text-secondary);
771
+ letter-spacing: 0.5px;
772
+ text-transform: uppercase;
773
+ }
774
+
775
+ .control-grid {
776
+ display: grid;
777
+ grid-template-columns: 1fr 1fr;
778
+ gap: 6px;
779
+ margin-bottom: 12px;
780
+ }
781
+
782
+ .control-button {
783
+ padding: 5px 12px;
784
+ height: auto;
785
+ background: transparent;
786
+ border: 1px solid rgba(255, 255, 255, 0.1);
787
+ border-radius: 12px;
788
+ color: var(--text-secondary);
789
+ font-size: 11px;
790
+ font-weight: 500;
791
+ cursor: pointer;
792
+ transition: all 0.2s var(--spring);
793
+ white-space: nowrap;
794
+ }
795
+
796
+ .control-button:hover {
797
+ background: rgba(255, 255, 255, 0.1);
798
+ color: var(--text-primary);
799
+ transform: scale(1.05);
800
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
801
+ }
802
+
803
+ .control-button:active {
804
+ transform: scale(0.98);
805
+ }
806
+
807
+ [data-theme="light"] .control-button {
808
+ border: 1px solid rgba(0, 0, 0, 0.1);
809
+ }
810
+
811
+ [data-theme="light"] .control-button:hover {
812
+ background: rgba(0, 0, 0, 0.05);
813
+ }
814
+
815
+ .control-button.active {
816
+ background: var(--accent);
817
+ color: white;
818
+ box-shadow: 0 2px 8px rgba(30, 142, 62, 0.3);
819
+ }
820
+
821
+ .control-button.active:hover {
822
+ box-shadow: 0 4px 16px rgba(30, 142, 62, 0.4);
823
+ transform: scale(1.05);
824
+ }
825
+
826
+ .control-button.active:active {
827
+ transform: scale(0.98);
828
+ }
829
+
830
+ #unit-toggle {
831
+ display: flex;
832
+ background: rgba(255, 255, 255, 0.08);
833
+ border-radius: 10px;
834
+ padding: 3px;
835
+ }
836
+
837
+ #unit-toggle button {
838
+ padding: 5px 12px;
839
+ background: transparent;
840
+ border: 1px solid rgba(255, 255, 255, 0.1);
841
+ border-radius: 8px;
842
+ color: var(--text-secondary);
843
+ font-size: 11px;
844
+ font-weight: 500;
845
+ cursor: pointer;
846
+ transition: all 0.2s var(--spring);
847
+ }
848
+
849
+ #unit-toggle button.active {
850
+ background: var(--accent);
851
+ color: white;
852
+ box-shadow: 0 1px 4px rgba(30, 142, 62, 0.3);
853
+ }
854
+
855
+ #unit-toggle button:hover:not(.active) {
856
+ color: var(--text-primary);
857
+ background: rgba(255, 255, 255, 0.08);
858
+ }
859
+
860
+ [data-theme="light"] #unit-toggle {
861
+ background: rgba(0, 0, 0, 0.06);
862
+ }
863
+
864
+ [data-theme="light"] #unit-toggle button {
865
+ border-color: rgba(0, 0, 0, 0.08);
866
+ }
867
+
868
+ [data-theme="light"] #unit-toggle button:hover:not(.active) {
869
+ background: rgba(0, 0, 0, 0.06);
870
+ }
871
+
872
+ #floating-help-panel {
873
+ top: 80px;
874
+ left: 50%;
875
+ margin-left: -250px;
876
+ width: 500px;
877
+ max-width: calc(100vw - 40px);
878
+ max-height: calc(100vh - 100px);
879
+ }
880
+
881
+ #floating-help-panel .floating-panel-content {
882
+ padding: 16px 20px;
883
+ }
884
+
885
+ #floating-help-panel.maximized {
886
+ width: 800px !important;
887
+ max-height: calc(100vh - 120px) !important;
888
+ }
889
+
890
+ .help-section {
891
+ margin-bottom: 24px;
892
+ }
893
+
894
+ .help-section:last-child {
895
+ margin-bottom: 0;
896
+ }
897
+
898
+ .help-section-title {
899
+ font-size: 16px;
900
+ font-weight: 600;
901
+ color: var(--text-primary);
902
+ margin-bottom: 12px;
903
+ display: flex;
904
+ align-items: center;
905
+ gap: 8px;
906
+ }
907
+
908
+ .help-section-title::before {
909
+ content: '';
910
+ width: 4px;
911
+ height: 16px;
912
+ background: linear-gradient(180deg, var(--accent) 0%, rgba(30, 142, 62, 0.5) 100%);
913
+ border-radius: 3px;
914
+ }
915
+
916
+ .help-section-content {
917
+ font-size: 13px;
918
+ line-height: 1.8;
919
+ color: var(--text-secondary);
920
+ }
921
+
922
+ /* Mocap Control Panel */
923
+ #mocap-control-panel {
924
+ top: 80px;
925
+ right: 780px;
926
+ width: 320px;
927
+ }
928
+
929
+ /* Joint Info Panel — PyBullet-style joint/link table */
930
+ #joint-info-panel {
931
+ display: none; flex-direction: column;
932
+ position: fixed;
933
+ top: 80px; right: 20px; width: 580px; max-height: calc(100vh - 160px);
934
+ background: var(--glass-bg);
935
+ backdrop-filter: blur(12px) saturate(120%);
936
+ -webkit-backdrop-filter: blur(12px) saturate(120%);
937
+ border: 0.5px solid var(--glass-border);
938
+ border-radius: 12px;
939
+ box-shadow: var(--glass-shadow);
940
+ z-index: 50;
941
+ overflow: hidden;
942
+ }
943
+ .joint-info-topbar {
944
+ display: flex; align-items: center; justify-content: space-between;
945
+ padding: 10px 16px;
946
+ border-bottom: 0.5px solid var(--glass-border);
947
+ }
948
+ .joint-info-copy-btn {
949
+ background: var(--glass-bg);
950
+ border: 0.5px solid var(--glass-border);
951
+ border-radius: 4px;
952
+ color: var(--text-secondary);
953
+ font-size: 11px; cursor: pointer;
954
+ padding: 2px 8px;
955
+ }
956
+ .joint-info-copy-btn:hover { color: var(--text-primary); }
957
+ .joint-info-title { font-size: 13px; font-weight: 600; color: var(--text-primary); }
958
+ .joint-info-topbar .panel-close-btn {
959
+ position: static !important;
960
+ transform: none !important;
961
+ }
962
+ .joint-info-topbar .panel-close-btn:hover {
963
+ transform: none !important;
964
+ }
965
+ .joint-info-topbar .panel-close-btn:active {
966
+ transform: none !important;
967
+ }
968
+ .joint-info-table-wrap {
969
+ overflow-y: auto;
970
+ max-height: calc(100vh - 260px);
971
+ }
972
+ .joint-info-table {
973
+ width: 100%;
974
+ border-collapse: collapse;
975
+ font-size: 11px;
976
+ }
977
+ .joint-info-table th {
978
+ position: sticky; top: 0;
979
+ background: var(--glass-bg);
980
+ color: var(--text-tertiary);
981
+ font-weight: 600;
982
+ text-align: left;
983
+ padding: 6px 12px;
984
+ border-bottom: 0.5px solid var(--glass-border);
985
+ }
986
+ .joint-info-table td {
987
+ padding: 4px 12px;
988
+ color: var(--text-secondary);
989
+ border-bottom: 0.5px solid rgba(255,255,255,0.04);
990
+ }
991
+ .joint-info-table code { font-size: 10px; }
992
+ .joint-movable td { color: var(--accent); }
993
+
994
+ .mocap-model-item {
995
+ padding: 12px;
996
+ background: rgba(255, 255, 255, 0.03);
997
+ border: 1px solid var(--glass-border);
998
+ border-radius: 8px;
999
+ margin-bottom: 12px;
1000
+ }
1001
+
1002
+ .mocap-model-header {
1003
+ display: flex;
1004
+ align-items: center;
1005
+ gap: 8px;
1006
+ margin-bottom: 8px;
1007
+ }
1008
+
1009
+
1010
+ .mocap-model-header span {
1011
+ flex: 1;
1012
+ font-size: 13px;
1013
+ font-weight: 500;
1014
+ color: var(--text-primary);
1015
+ }
1016
+
1017
+ .mocap-load-btn {
1018
+ width: 100%;
1019
+ padding: 6px 12px;
1020
+ background: var(--accent);
1021
+ border: none;
1022
+ border-radius: 6px;
1023
+ color: #fff;
1024
+ font-size: 12px;
1025
+ font-weight: 500;
1026
+ cursor: pointer;
1027
+ transition: all 0.2s;
1028
+ }
1029
+
1030
+ .mocap-load-btn:hover {
1031
+ background: var(--accent-hover);
1032
+ transform: translateY(-1px);
1033
+ }
1034
+
1035
+ .mocap-load-btn:disabled {
1036
+ opacity: 0.5;
1037
+ cursor: not-allowed;
1038
+ transform: none;
1039
+ }
1040
+
1041
+ [data-theme="light"] .mocap-model-item {
1042
+ background: rgba(0, 0, 0, 0.03);
1043
+ border-color: rgba(0, 0, 0, 0.1);
1044
+ }
1045
+
1046
+ /* ROS Connection Panel */
1047
+ #floating-ros-panel {
1048
+ top: 80px;
1049
+ right: 20px;
1050
+ width: 360px;
1051
+ max-height: calc(100vh - 100px);
1052
+ }
1053
+
1054
+ .ros-form-group {
1055
+ margin-bottom: 16px;
1056
+ }
1057
+
1058
+ .ros-hint {
1059
+ font-size: 11px;
1060
+ color: var(--text-tertiary);
1061
+ background: rgba(255, 255, 255, 0.03);
1062
+ border: 0.5px solid var(--glass-border);
1063
+ border-radius: 6px;
1064
+ padding: 6px 10px;
1065
+ margin-bottom: 12px;
1066
+ line-height: 1.5;
1067
+ }
1068
+
1069
+ .ros-hint code {
1070
+ background: rgba(255, 255, 255, 0.06);
1071
+ padding: 1px 5px;
1072
+ border-radius: 3px;
1073
+ font-size: 10px;
1074
+ font-family: 'SF Mono', Menlo, monospace;
1075
+ }
1076
+
1077
+ .ros-download-btn {
1078
+ display: inline-block;
1079
+ margin-top: 4px;
1080
+ font-size: 11px;
1081
+ color: var(--accent);
1082
+ cursor: pointer;
1083
+ text-decoration: none;
1084
+ }
1085
+
1086
+ .ros-download-btn:hover {
1087
+ color: var(--accent-hover);
1088
+ }
1089
+
1090
+ .ros-label {
1091
+ display: block;
1092
+ font-size: 12px;
1093
+ font-weight: 500;
1094
+ color: var(--text-secondary);
1095
+ margin-bottom: 6px;
1096
+ display: flex;
1097
+ justify-content: space-between;
1098
+ align-items: center;
1099
+ }
1100
+
1101
+ .ros-rate-badge {
1102
+ font-size: 11px;
1103
+ font-weight: 600;
1104
+ color: var(--accent);
1105
+ background: var(--accent-secondary);
1106
+ padding: 2px 8px;
1107
+ border-radius: 6px;
1108
+ }
1109
+
1110
+ .ros-input, .ros-select {
1111
+ width: 100%;
1112
+ padding: 8px 12px;
1113
+ background: rgba(255, 255, 255, 0.05);
1114
+ border: 1px solid var(--glass-border);
1115
+ border-radius: 8px;
1116
+ color: var(--text-primary);
1117
+ font-size: 13px;
1118
+ transition: all 0.2s var(--spring);
1119
+ }
1120
+
1121
+ .ros-input:focus, .ros-select:focus {
1122
+ outline: none;
1123
+ background: rgba(255, 255, 255, 0.08);
1124
+ border-color: var(--accent);
1125
+ }
1126
+
1127
+ .ros-btn {
1128
+ padding: 8px 16px;
1129
+ border: none;
1130
+ border-radius: 8px;
1131
+ font-size: 13px;
1132
+ font-weight: 500;
1133
+ cursor: pointer;
1134
+ transition: all 0.2s var(--spring);
1135
+ }
1136
+
1137
+ .ros-btn-primary {
1138
+ background: var(--accent);
1139
+ color: white;
1140
+ }
1141
+
1142
+ .ros-btn-primary:hover:not(:disabled) {
1143
+ background: var(--accent-hover);
1144
+ transform: translateY(-1px);
1145
+ }
1146
+
1147
+ .ros-btn-secondary {
1148
+ background: rgba(255, 255, 255, 0.1);
1149
+ color: var(--text-primary);
1150
+ }
1151
+
1152
+ .ros-btn-secondary:hover:not(:disabled) {
1153
+ background: rgba(255, 255, 255, 0.15);
1154
+ }
1155
+
1156
+ .ros-btn:disabled {
1157
+ opacity: 0.4;
1158
+ cursor: not-allowed;
1159
+ }
1160
+
1161
+ .ros-status {
1162
+ font-size: 12px;
1163
+ color: var(--text-tertiary);
1164
+ margin-left: auto;
1165
+ }
1166
+
1167
+ .ros-divider {
1168
+ height: 1px;
1169
+ background: var(--glass-border);
1170
+ margin: 16px 0;
1171
+ }
1172
+
1173
+ .ros-checkbox {
1174
+ display: flex;
1175
+ align-items: center;
1176
+ gap: 8px;
1177
+ font-size: 13px;
1178
+ color: var(--text-primary);
1179
+ cursor: pointer;
1180
+ }
1181
+
1182
+ .ros-checkbox input[type="checkbox"] {
1183
+ width: 16px;
1184
+ height: 16px;
1185
+ cursor: pointer;
1186
+ accent-color: #6fb885;
1187
+ }
1188
+
1189
+ .ros-topic-list {
1190
+ display: flex;
1191
+ flex-direction: column;
1192
+ gap: 8px;
1193
+ }
1194
+
1195
+ .ros-topic-row {
1196
+ display: flex;
1197
+ align-items: center;
1198
+ gap: 8px;
1199
+ }
1200
+
1201
+ .ros-topic-row .ros-topic-checkbox {
1202
+ width: 16px;
1203
+ height: 16px;
1204
+ cursor: pointer;
1205
+ accent-color: #6fb885;
1206
+ flex-shrink: 0;
1207
+ }
1208
+
1209
+ .ros-topic-row .ros-topic-input {
1210
+ flex: 1;
1211
+ min-width: 0;
1212
+ }
1213
+
1214
+ .ros-topic-row .ros-topic-rate {
1215
+ flex-shrink: 0;
1216
+ white-space: nowrap;
1217
+ }
1218
+
1219
+ .ros-slider {
1220
+ width: 100%;
1221
+ height: 4px;
1222
+ border-radius: 2px;
1223
+ background: rgba(255, 255, 255, 0.1);
1224
+ outline: none;
1225
+ -webkit-appearance: none;
1226
+ }
1227
+
1228
+ .ros-slider::-webkit-slider-thumb {
1229
+ -webkit-appearance: none;
1230
+ width: 16px;
1231
+ height: 16px;
1232
+ border-radius: 50%;
1233
+ background: var(--accent);
1234
+ cursor: pointer;
1235
+ transition: all 0.2s var(--spring);
1236
+ }
1237
+
1238
+ .ros-slider::-webkit-slider-thumb:hover {
1239
+ transform: scale(1.2);
1240
+ }
1241
+
1242
+ .ros-stats {
1243
+ display: flex;
1244
+ justify-content: space-between;
1245
+ font-size: 11px;
1246
+ color: var(--text-tertiary);
1247
+ padding-top: 8px;
1248
+ border-top: 1px solid var(--glass-border);
1249
+ }
1250
+
1251
+ [data-theme="light"] .ros-input,
1252
+ [data-theme="light"] .ros-select {
1253
+ background: rgba(0, 0, 0, 0.03);
1254
+ border-color: rgba(0, 0, 0, 0.1);
1255
+ }
1256
+
1257
+ [data-theme="light"] .ros-input:focus,
1258
+ [data-theme="light"] .ros-select:focus {
1259
+ background: rgba(0, 0, 0, 0.05);
1260
+ }
1261
+
1262
+ /* 深色模式下拉选项:避免白底灰字看不清(浅色模式保持浏览器原生默认) */
1263
+ [data-theme="dark"] .ros-select option {
1264
+ background-color: #2a2a2a;
1265
+ color: var(--text-primary);
1266
+ padding: 8px 12px;
1267
+ }
1268
+
1269
+ [data-theme="dark"] .ros-select option:checked {
1270
+ background: linear-gradient(135deg, var(--accent) 0%, rgba(30, 142, 62, 0.8) 100%);
1271
+ color: white;
1272
+ }
1273
+
1274
+ [data-theme="light"] .ros-hint {
1275
+ background: rgba(0, 0, 0, 0.03);
1276
+ border-color: rgba(0, 0, 0, 0.1);
1277
+ }
1278
+
1279
+ [data-theme="light"] .ros-hint code {
1280
+ background: rgba(0, 0, 0, 0.05);
1281
+ }
1282
+
1283
+ [data-theme="light"] .ros-btn-secondary {
1284
+ background: rgba(0, 0, 0, 0.08);
1285
+ }
1286
+
1287
+ [data-theme="light"] .ros-btn-secondary:hover:not(:disabled) {
1288
+ background: rgba(0, 0, 0, 0.12);
1289
+ }
1290
+
1291
+ /* Plot Panel — uPlot real-time monitor */
1292
+ #plot-container {
1293
+ font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', sans-serif;
1294
+ flex-direction: row;
1295
+ }
1296
+ #plot-sidebar {
1297
+ width: 374px; min-width: 374px;
1298
+ background: var(--glass-bg);
1299
+ border-right: 0.5px solid var(--glass-border);
1300
+ padding: 16px; overflow: hidden;
1301
+ display: flex; flex-direction: column; gap: 16px;
1302
+ position: relative;
1303
+ }
1304
+ #plot-chart-area {
1305
+ flex: 1; min-width: 0; padding: 16px;
1306
+ position: relative;
1307
+ }
1308
+ .plot-section { display: flex; flex-direction: column; gap: 6px; }
1309
+ .plot-check-list {
1310
+ display: flex; flex-direction: column; gap: 1px;
1311
+ }
1312
+ .plot-check-label {
1313
+ display: flex; align-items: center; gap: 6px;
1314
+ font-size: 11px; color: var(--text-secondary);
1315
+ padding: 3px 6px; border-radius: 4px; cursor: pointer;
1316
+ }
1317
+ .plot-check-label:hover { background: rgba(255,255,255,0.04); }
1318
+ .plot-check-label input[type="checkbox"] {
1319
+ width: 13px; height: 13px; accent-color: var(--accent); flex-shrink: 0;
1320
+ }
1321
+ [data-theme="light"] #plot-sidebar {
1322
+ background: rgba(245,245,245,0.95);
1323
+ border-right-color: rgba(0,0,0,0.1);
1324
+ }
1325
+ [data-theme="light"] .plot-check-label:hover { background: rgba(0,0,0,0.04); }
1326
+ .plot-topic-header {
1327
+ display: flex; align-items: center; gap: 4px;
1328
+ font-size: 11px; font-weight: 600; color: var(--text-primary);
1329
+ padding: 4px 6px; margin-top: 4px; border-radius: 4px;
1330
+ cursor: pointer; user-select: none;
1331
+ }
1332
+ .plot-topic-header:hover { background: rgba(255,255,255,0.04); }
1333
+ .plot-topic-arrow { font-size: 9px; width: 12px; flex-shrink: 0; }
1334
+ .plot-topic-name { flex:1; overflow:hidden; text-overflow:ellipsis; font-family:monospace; font-size:10px; }
1335
+ .plot-topic-badge {
1336
+ font-size: 9px; background: var(--glass-bg); border-radius: 6px;
1337
+ padding: 0 5px; color: var(--text-tertiary); font-weight: 400;
1338
+ }
1339
+ .plot-topic-joints { padding-left: 8px; }
1340
+ [data-theme="light"] .plot-topic-header:hover { background: rgba(0,0,0,0.04); }
1341
+ [data-theme="light"] .plot-topic-badge { background: rgba(0,0,0,0.05); }
1342
+ /* Topic 全选框:自定义三态(未选=空框 / 全选=对勾 / 部分选中=中间实心方块) */
1343
+ .plot-topic-checkbox {
1344
+ -webkit-appearance: none; appearance: none;
1345
+ width: 13px; height: 13px; flex-shrink: 0; box-sizing: border-box;
1346
+ border: 1px solid var(--text-tertiary); border-radius: 3px;
1347
+ background: transparent; cursor: pointer; position: relative;
1348
+ transition: background .15s, border-color .15s;
1349
+ }
1350
+ .plot-topic-checkbox:hover { border-color: var(--accent); }
1351
+ .plot-topic-checkbox:checked {
1352
+ background: var(--accent); border-color: var(--accent);
1353
+ }
1354
+ .plot-topic-checkbox:checked::after {
1355
+ content: ''; position: absolute; left: 3.5px; top: 1px;
1356
+ width: 3px; height: 6px; box-sizing: border-box;
1357
+ border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg);
1358
+ }
1359
+ .plot-topic-checkbox:indeterminate { border-color: var(--accent); }
1360
+ .plot-topic-checkbox:indeterminate::after {
1361
+ content: ''; position: absolute; left: 50%; top: 50%;
1362
+ width: 6px; height: 6px; transform: translate(-50%, -50%);
1363
+ background: var(--accent); border-radius: 1px;
1364
+ }
1365
+ /* End Plot Panel */
1366
+
1367
+ /* Axes Control Panel Styles */
1368
+ .axes-form-group {
1369
+ margin-bottom: 12px;
1370
+ }
1371
+
1372
+ .axes-search {
1373
+ width: 100%;
1374
+ padding: 8px 12px;
1375
+ background: rgba(255, 255, 255, 0.05);
1376
+ border: 1px solid var(--glass-border);
1377
+ border-radius: 8px;
1378
+ color: var(--text-primary);
1379
+ font-size: 13px;
1380
+ outline: none;
1381
+ transition: all 0.2s var(--spring);
1382
+ }
1383
+
1384
+ .axes-search:focus {
1385
+ background: rgba(255, 255, 255, 0.08);
1386
+ border-color: var(--accent);
1387
+ }
1388
+
1389
+ .axes-search::placeholder {
1390
+ color: var(--text-tertiary);
1391
+ }
1392
+
1393
+ .axes-btn {
1394
+ padding: 6px 12px;
1395
+ border: none;
1396
+ border-radius: 8px;
1397
+ font-size: 12px;
1398
+ font-weight: 500;
1399
+ cursor: pointer;
1400
+ transition: all 0.2s var(--spring);
1401
+ flex: 1;
1402
+ }
1403
+
1404
+ .axes-btn-secondary {
1405
+ background: rgba(255, 255, 255, 0.1);
1406
+ color: var(--text-primary);
1407
+ }
1408
+
1409
+ .axes-btn-secondary:hover {
1410
+ background: rgba(255, 255, 255, 0.15);
1411
+ transform: translateY(-1px);
1412
+ }
1413
+
1414
+ .axes-panel-content {
1415
+ display: flex;
1416
+ flex-direction: column;
1417
+ gap: 12px;
1418
+ overflow: hidden;
1419
+ min-height: 0;
1420
+ height: 100%;
1421
+ }
1422
+
1423
+ .axes-panel-controls {
1424
+ flex: 0 0 auto;
1425
+ min-height: 0;
1426
+ }
1427
+
1428
+ .axes-panel-controls .axes-form-group:last-child {
1429
+ margin-bottom: 0;
1430
+ }
1431
+
1432
+ .axes-list-container {
1433
+ flex: 1 1 0;
1434
+ height: 0;
1435
+ min-height: 0;
1436
+ max-height: none;
1437
+ overflow-y: auto;
1438
+ overflow-x: hidden;
1439
+ scrollbar-gutter: stable;
1440
+ scrollbar-width: thin;
1441
+ scrollbar-color: rgba(128, 128, 128, 0.45) transparent;
1442
+ border: 1px solid var(--glass-border);
1443
+ border-radius: 8px;
1444
+ background: rgba(0, 0, 0, 0.2);
1445
+ }
1446
+
1447
+ .axes-panel-bottom-spacer {
1448
+ flex: 0 0 12px;
1449
+ }
1450
+
1451
+ .axes-list-container::-webkit-scrollbar {
1452
+ width: 10px;
1453
+ }
1454
+
1455
+ .axes-list-container::-webkit-scrollbar-track {
1456
+ background: transparent;
1457
+ }
1458
+
1459
+ .axes-list-container::-webkit-scrollbar-thumb {
1460
+ background: rgba(128, 128, 128, 0.45);
1461
+ border-radius: 6px;
1462
+ border: 2px solid transparent;
1463
+ background-clip: padding-box;
1464
+ }
1465
+
1466
+ .axes-list-container::-webkit-scrollbar-thumb:hover {
1467
+ background: rgba(128, 128, 128, 0.65);
1468
+ background-clip: padding-box;
1469
+ }
1470
+
1471
+ .axes-list {
1472
+ padding: 8px 8px 20px;
1473
+ box-sizing: border-box;
1474
+ }
1475
+
1476
+ .axes-checkbox {
1477
+ display: flex;
1478
+ align-items: flex-start;
1479
+ gap: 8px;
1480
+ padding: 6px 8px;
1481
+ font-size: 13px;
1482
+ color: var(--text-primary);
1483
+ cursor: pointer;
1484
+ border-radius: 6px;
1485
+ transition: background 0.2s;
1486
+ }
1487
+
1488
+ .axes-checkbox:hover {
1489
+ background: rgba(255, 255, 255, 0.05);
1490
+ }
1491
+
1492
+ .axes-checkbox input[type="checkbox"] {
1493
+ width: 16px;
1494
+ height: 16px;
1495
+ margin-top: 2px;
1496
+ flex-shrink: 0;
1497
+ cursor: pointer;
1498
+ accent-color: var(--accent);
1499
+ }
1500
+
1501
+ .axes-checkbox span {
1502
+ flex: 1;
1503
+ min-width: 0;
1504
+ white-space: normal;
1505
+ overflow: visible;
1506
+ text-overflow: clip;
1507
+ overflow-wrap: anywhere;
1508
+ word-break: break-word;
1509
+ line-height: 1.4;
1510
+ }
1511
+
1512
+ .axes-divider {
1513
+ height: 1px;
1514
+ background: var(--glass-border);
1515
+ margin: 16px 0;
1516
+ }
1517
+
1518
+ .axes-label {
1519
+ display: flex;
1520
+ justify-content: space-between;
1521
+ align-items: center;
1522
+ font-size: 13px;
1523
+ color: var(--text-primary);
1524
+ margin-bottom: 8px;
1525
+ }
1526
+
1527
+ .axes-scale-value {
1528
+ font-size: 12px;
1529
+ color: var(--accent);
1530
+ font-weight: 600;
1531
+ }
1532
+
1533
+ .axes-slider {
1534
+ width: 100%;
1535
+ height: 4px;
1536
+ border-radius: 2px;
1537
+ background: rgba(255, 255, 255, 0.1);
1538
+ outline: none;
1539
+ -webkit-appearance: none;
1540
+ }
1541
+
1542
+ .axes-slider::-webkit-slider-thumb {
1543
+ -webkit-appearance: none;
1544
+ width: 16px;
1545
+ height: 16px;
1546
+ border-radius: 50%;
1547
+ background: var(--accent);
1548
+ cursor: pointer;
1549
+ transition: all 0.2s var(--spring);
1550
+ }
1551
+
1552
+ .axes-slider::-webkit-slider-thumb:hover {
1553
+ transform: scale(1.2);
1554
+ }
1555
+
1556
+ .axes-slider::-moz-range-thumb {
1557
+ width: 16px;
1558
+ height: 16px;
1559
+ border-radius: 50%;
1560
+ background: var(--accent);
1561
+ cursor: pointer;
1562
+ border: none;
1563
+ transition: all 0.2s var(--spring);
1564
+ }
1565
+
1566
+ .axes-slider::-moz-range-thumb:hover {
1567
+ transform: scale(1.2);
1568
+ }
1569
+
1570
+ .no-results {
1571
+ text-align: center;
1572
+ padding: 20px;
1573
+ color: var(--text-tertiary);
1574
+ font-size: 13px;
1575
+ }
1576
+
1577
+ .axes-rpy-section {
1578
+ border-top: 1px solid var(--glass-border);
1579
+ padding-top: 12px;
1580
+ margin-top: 12px;
1581
+ }
1582
+
1583
+ .axes-rpy-header {
1584
+ display: flex;
1585
+ justify-content: space-between;
1586
+ align-items: center;
1587
+ margin-bottom: 8px;
1588
+ }
1589
+
1590
+ .axes-rpy-title {
1591
+ font-size: 12px;
1592
+ font-weight: 600;
1593
+ color: var(--text-secondary);
1594
+ }
1595
+
1596
+ .axes-rpy-joint-name {
1597
+ font-size: 11px;
1598
+ color: var(--accent);
1599
+ font-family: monospace;
1600
+ }
1601
+
1602
+ .axes-rpy-row {
1603
+ display: flex;
1604
+ align-items: center;
1605
+ gap: 6px;
1606
+ margin-bottom: 4px;
1607
+ }
1608
+
1609
+ .axes-rpy-label {
1610
+ font-size: 11px;
1611
+ color: var(--text-tertiary);
1612
+ width: 50px;
1613
+ flex-shrink: 0;
1614
+ }
1615
+
1616
+ .axes-rpy-btn {
1617
+ width: 40px;
1618
+ height: 22px;
1619
+ border: 1px solid var(--glass-border);
1620
+ border-radius: 4px;
1621
+ background: var(--glass-bg);
1622
+ color: var(--text-primary);
1623
+ font-size: 10px;
1624
+ cursor: pointer;
1625
+ transition: all 0.15s ease;
1626
+ }
1627
+
1628
+ .axes-rpy-btn:hover {
1629
+ background: var(--accent);
1630
+ border-color: var(--accent);
1631
+ }
1632
+
1633
+ .axes-rpy-value {
1634
+ font-size: 13px;
1635
+ font-weight: 600;
1636
+ color: var(--text-primary);
1637
+ width: 32px;
1638
+ text-align: center;
1639
+ font-family: monospace;
1640
+ }
1641
+
1642
+ .axes-btn-primary {
1643
+ background: var(--accent);
1644
+ color: #fff;
1645
+ border: none;
1646
+ }
1647
+
1648
+ .axes-btn-primary:hover {
1649
+ background: var(--accent-hover);
1650
+ }
1651
+
1652
+ .axis-label {
1653
+ color: #fff;
1654
+ background: rgba(0, 0, 0, 0.75);
1655
+ padding: 3px 8px;
1656
+ border-radius: 4px;
1657
+ font-size: 12px;
1658
+ white-space: nowrap;
1659
+ pointer-events: none;
1660
+ font-weight: 500;
1661
+ border: 1px solid rgba(255, 255, 255, 0.3);
1662
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
1663
+ }
1664
+
1665
+ /* Light theme overrides for axes panel */
1666
+ [data-theme="light"] .axes-search {
1667
+ background: rgba(0, 0, 0, 0.03);
1668
+ border-color: rgba(0, 0, 0, 0.1);
1669
+ }
1670
+
1671
+ [data-theme="light"] .axes-search:focus {
1672
+ background: rgba(0, 0, 0, 0.05);
1673
+ }
1674
+
1675
+ [data-theme="light"] .axes-btn-secondary {
1676
+ background: rgba(0, 0, 0, 0.08);
1677
+ }
1678
+
1679
+ [data-theme="light"] .axes-btn-secondary:hover {
1680
+ background: rgba(0, 0, 0, 0.12);
1681
+ }
1682
+
1683
+ [data-theme="light"] .axes-list-container {
1684
+ background: rgba(0, 0, 0, 0.02);
1685
+ border-color: rgba(0, 0, 0, 0.1);
1686
+ }
1687
+
1688
+ [data-theme="light"] .axes-checkbox:hover {
1689
+ background: rgba(0, 0, 0, 0.05);
1690
+ }
1691
+
1692
+ [data-theme="light"] .axis-label {
1693
+ background: rgba(255, 255, 255, 0.95);
1694
+ color: #000;
1695
+ border-color: rgba(0, 0, 0, 0.2);
1696
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
1697
+ }
1698
+
1699
+ .help-section-content p {
1700
+ margin-bottom: 8px;
1701
+ }
1702
+
1703
+ .help-section-content strong {
1704
+ color: var(--text-primary);
1705
+ font-weight: 600;
1706
+ }
1707
+
1708
+ .help-action-buttons {
1709
+ display: flex;
1710
+ gap: 8px;
1711
+ margin-top: 12px;
1712
+ flex-wrap: wrap;
1713
+ }
1714
+
1715
+ .help-action-btn {
1716
+ flex: 1;
1717
+ min-width: 140px;
1718
+ display: inline-flex;
1719
+ align-items: center;
1720
+ justify-content: center;
1721
+ padding: 8px 12px;
1722
+ background: rgba(255, 255, 255, 0.05);
1723
+ border: 1px solid rgba(255, 255, 255, 0.1);
1724
+ border-radius: 12px;
1725
+ color: var(--text-primary);
1726
+ text-decoration: none;
1727
+ font-size: 13px;
1728
+ font-weight: 500;
1729
+ transition: all 0.2s;
1730
+ }
1731
+
1732
+ .help-action-btn:hover {
1733
+ background: rgba(255, 255, 255, 0.1);
1734
+ border-color: var(--accent);
1735
+ transform: translateY(-1px);
1736
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
1737
+ }
1738
+
1739
+ .help-action-btn:active {
1740
+ transform: translateY(0);
1741
+ }
1742
+
1743
+ .help-action-btn.disabled {
1744
+ opacity: 0.5;
1745
+ cursor: not-allowed;
1746
+ pointer-events: none;
1747
+ }
1748
+
1749
+ .help-action-btn.disabled:hover {
1750
+ background: rgba(255, 255, 255, 0.05);
1751
+ border-color: rgba(255, 255, 255, 0.1);
1752
+ transform: none;
1753
+ box-shadow: none;
1754
+ }
1755
+
1756
+ [data-theme="light"] .help-action-btn {
1757
+ background: rgba(0, 0, 0, 0.03);
1758
+ border: 1px solid rgba(0, 0, 0, 0.1);
1759
+ color: var(--text-primary);
1760
+ }
1761
+
1762
+ [data-theme="light"] .help-action-btn:hover {
1763
+ background: rgba(0, 0, 0, 0.06);
1764
+ border-color: var(--accent);
1765
+ }
1766
+
1767
+ [data-theme="light"] .help-action-btn.disabled {
1768
+ background: rgba(0, 0, 0, 0.02);
1769
+ border-color: rgba(0, 0, 0, 0.06);
1770
+ opacity: 0.4;
1771
+ }
1772
+
1773
+ [data-theme="light"] .help-action-btn.disabled:hover {
1774
+ background: rgba(0, 0, 0, 0.02);
1775
+ border-color: rgba(0, 0, 0, 0.06);
1776
+ }
1777
+
1778
+ .help-controls-grid {
1779
+ display: grid;
1780
+ grid-template-columns: auto 1fr;
1781
+ gap: 8px 16px;
1782
+ margin-top: 8px;
1783
+ }
1784
+
1785
+ .help-control-key {
1786
+ font-weight: 600;
1787
+ color: var(--accent);
1788
+ }
1789
+
1790
+ .help-control-desc {
1791
+ color: var(--text-secondary);
1792
+ }
1793
+
1794
+ .help-contact {
1795
+ display: flex;
1796
+ flex-direction: column;
1797
+ gap: 8px;
1798
+ margin-top: 8px;
1799
+ }
1800
+
1801
+ .help-contact-item {
1802
+ display: flex;
1803
+ align-items: center;
1804
+ gap: 8px;
1805
+ padding: 8px 12px;
1806
+ background: rgba(255, 255, 255, 0.05);
1807
+ border-radius: 12px;
1808
+ font-size: 12px;
1809
+ transition: all 0.2s;
1810
+ }
1811
+
1812
+ .help-contact-item:hover {
1813
+ background: rgba(255, 255, 255, 0.08);
1814
+ box-shadow: 0 0 16px rgba(30, 142, 62, 0.1);
1815
+ }
1816
+
1817
+ .help-contact-item a {
1818
+ color: var(--accent);
1819
+ text-decoration: none;
1820
+ }
1821
+
1822
+ .help-contact-item a:hover {
1823
+ text-decoration: underline;
1824
+ }
1825
+
1826
+ /* Hover info panel */
1827
+ #hover-info {
1828
+ position: fixed;
1829
+ bottom: 70px;
1830
+ left: 50%;
1831
+ transform: translateX(-50%);
1832
+ padding: 12px 16px;
1833
+ background: var(--glass-bg);
1834
+ backdrop-filter: blur(20px) saturate(150%);
1835
+ -webkit-backdrop-filter: blur(20px) saturate(150%);
1836
+ border: 0.5px solid var(--glass-border);
1837
+ border-radius: 16px;
1838
+ box-shadow: var(--glass-shadow);
1839
+ font-size: 12px;
1840
+ color: var(--text-secondary);
1841
+ min-width: 200px;
1842
+ opacity: 0;
1843
+ visibility: hidden;
1844
+ transition: all 0.3s var(--spring);
1845
+ transform: translateX(-50%) translateY(10px);
1846
+ pointer-events: none;
1847
+ z-index: 60;
1848
+ }
1849
+
1850
+ #hover-info::before {
1851
+ content: '';
1852
+ position: absolute;
1853
+ top: 0;
1854
+ left: 0;
1855
+ right: 0;
1856
+ height: 1px;
1857
+ background: linear-gradient(90deg,
1858
+ transparent 0%,
1859
+ rgba(255, 255, 255, 0.2) 50%,
1860
+ transparent 100%);
1861
+ border-radius: 16px 16px 0 0;
1862
+ }
1863
+
1864
+ #hover-info.visible {
1865
+ opacity: 1;
1866
+ visibility: visible;
1867
+ transform: translateX(-50%) translateY(0);
1868
+ }
1869
+
1870
+ #hover-link-name {
1871
+ font-weight: 600;
1872
+ font-size: 13px;
1873
+ color: var(--accent);
1874
+ margin-bottom: 6px;
1875
+ letter-spacing: -0.2px;
1876
+ }
1877
+
1878
+ #hover-joint-name {
1879
+ font-size: 11px;
1880
+ color: var(--text-primary);
1881
+ margin-bottom: 4px;
1882
+ }
1883
+
1884
+ #hover-link-mass {
1885
+ font-size: 11px;
1886
+ color: var(--text-tertiary);
1887
+ margin-bottom: 4px;
1888
+ }
1889
+
1890
+ #hover-merged-links {
1891
+ font-size: 10px;
1892
+ color: var(--text-tertiary);
1893
+ margin-top: 6px;
1894
+ padding-top: 6px;
1895
+ border-top: 0.5px solid var(--glass-border);
1896
+ }
1897
+
1898
+ #hover-link-pose {
1899
+ font-size: 11px;
1900
+ color: var(--text-tertiary);
1901
+ white-space: pre-wrap;
1902
+ }
1903
+
1904
+ /* File tree styles */
1905
+ .file-tree {
1906
+ font-size: 13px;
1907
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
1908
+ line-height: 1.4;
1909
+ }
1910
+
1911
+ .tree-item {
1912
+ padding: 2px 6px 2px 4px;
1913
+ cursor: pointer;
1914
+ margin: 0;
1915
+ font-size: 13px;
1916
+ line-height: 18px;
1917
+ border-radius: 6px;
1918
+ position: relative;
1919
+ transition: background 0.15s ease;
1920
+ color: var(--text-primary);
1921
+ }
1922
+
1923
+ .tree-item-header {
1924
+ display: flex;
1925
+ align-items: center;
1926
+ justify-content: space-between;
1927
+ height: 22px;
1928
+ border-radius: 6px;
1929
+ transition: background 0.15s ease;
1930
+ }
1931
+
1932
+ .tree-item-header:hover {
1933
+ background: rgba(255, 255, 255, 0.06);
1934
+ }
1935
+
1936
+ [data-theme="light"] .tree-item-header:hover {
1937
+ background: rgba(0, 0, 0, 0.04);
1938
+ }
1939
+
1940
+ .tree-item.selected {
1941
+ background: rgba(30, 142, 62, 0.25) !important;
1942
+ }
1943
+
1944
+ [data-theme="light"] .tree-item.selected {
1945
+ background: rgba(30, 142, 62, 0.18) !important;
1946
+ }
1947
+
1948
+ .tree-item.selected .name {
1949
+ font-weight: 500;
1950
+ }
1951
+
1952
+ .tree-item-left {
1953
+ display: flex;
1954
+ align-items: center;
1955
+ flex: 1;
1956
+ min-width: 0;
1957
+ gap: 6px;
1958
+ }
1959
+
1960
+ .tree-item.folder .tree-arrow {
1961
+ width: 12px;
1962
+ height: 12px;
1963
+ display: flex;
1964
+ align-items: center;
1965
+ justify-content: center;
1966
+ color: var(--text-secondary);
1967
+ flex-shrink: 0;
1968
+ transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
1969
+ font-size: 10px;
1970
+ opacity: 0.6;
1971
+ }
1972
+
1973
+ .tree-item.folder.collapsed .tree-arrow {
1974
+ transform: rotate(0deg);
1975
+ }
1976
+
1977
+ .tree-item.folder:not(.collapsed) .tree-arrow {
1978
+ transform: rotate(90deg);
1979
+ }
1980
+
1981
+ .tree-item.folder .tree-arrow::before {
1982
+ content: '▸';
1983
+ font-weight: bold;
1984
+ }
1985
+
1986
+ .tree-item .icon {
1987
+ width: 16px;
1988
+ height: 16px;
1989
+ display: flex;
1990
+ align-items: center;
1991
+ justify-content: center;
1992
+ flex-shrink: 0;
1993
+ font-size: 14px;
1994
+ opacity: 0.85;
1995
+ }
1996
+
1997
+ .tree-item.folder .icon {
1998
+ color: #5FA8FE;
1999
+ }
2000
+
2001
+ [data-theme="light"] .tree-item.folder .icon {
2002
+ color: #1e8e3e;
2003
+ }
2004
+
2005
+ .tree-item.folder .icon::before {
2006
+ content: '📁';
2007
+ font-size: 16px;
2008
+ filter: grayscale(0.3) brightness(1.1);
2009
+ }
2010
+
2011
+ .tree-item:not(.folder) .icon::before {
2012
+ content: '📄';
2013
+ font-size: 15px;
2014
+ }
2015
+
2016
+ .tree-item[data-type="model"] .icon::before {
2017
+ content: '📄';
2018
+ filter: hue-rotate(270deg) brightness(1.2);
2019
+ }
2020
+
2021
+ .tree-item[data-type="link"] .icon::before {
2022
+ content: '🔗';
2023
+ }
2024
+
2025
+ .tree-item[data-type="mesh"] .icon::before {
2026
+ content: '📄';
2027
+ filter: hue-rotate(200deg) brightness(1.1);
2028
+ }
2029
+
2030
+ .tree-item .name {
2031
+ user-select: none;
2032
+ white-space: nowrap;
2033
+ overflow: hidden;
2034
+ text-overflow: ellipsis;
2035
+ flex: 1;
2036
+ font-size: 13px;
2037
+ font-weight: 400;
2038
+ letter-spacing: -0.01em;
2039
+ }
2040
+
2041
+ .tree-item .type-badge {
2042
+ padding: 0 4px;
2043
+ font-size: 10px;
2044
+ font-weight: 500;
2045
+ color: var(--text-tertiary);
2046
+ text-transform: uppercase;
2047
+ letter-spacing: 0.02em;
2048
+ margin-left: 8px;
2049
+ flex-shrink: 0;
2050
+ opacity: 0.5;
2051
+ font-variant-numeric: tabular-nums;
2052
+ }
2053
+
2054
+ [data-theme="light"] .tree-item .type-badge {
2055
+ color: #86868b;
2056
+ }
2057
+
2058
+ .tree-children {
2059
+ margin-left: 0;
2060
+ padding-left: 20px;
2061
+ display: none;
2062
+ }
2063
+
2064
+ .tree-item.folder:not(.collapsed) > .tree-children {
2065
+ display: block;
2066
+ }
2067
+
2068
+ #model-list::-webkit-scrollbar {
2069
+ width: 10px;
2070
+ }
2071
+
2072
+ #model-list::-webkit-scrollbar-track {
2073
+ background: transparent;
2074
+ }
2075
+
2076
+ #model-list::-webkit-scrollbar-thumb {
2077
+ background: rgba(128, 128, 128, 0.3);
2078
+ border-radius: 6px;
2079
+ border: 2px solid transparent;
2080
+ background-clip: padding-box;
2081
+ }
2082
+
2083
+ #model-list::-webkit-scrollbar-thumb:hover {
2084
+ background: rgba(128, 128, 128, 0.5);
2085
+ background-clip: padding-box;
2086
+ }
2087
+
2088
+ /* Model tree floating panel */
2089
+ #floating-model-tree {
2090
+ top: 80px;
2091
+ left: calc(100vw - 360px - 20px);
2092
+ width: 360px;
2093
+ height: 340px;
2094
+ transition: all 0.4s var(--spring);
2095
+ }
2096
+
2097
+ #floating-model-tree.maximized {
2098
+ top: 80px !important;
2099
+ left: 20px !important;
2100
+ width: calc(100vw - 40px) !important;
2101
+ height: calc(100vh - 100px) !important;
2102
+ }
2103
+
2104
+ .floating-panel-header {
2105
+ padding: 16px;
2106
+ border-bottom: 0.5px solid var(--glass-border);
2107
+ display: flex;
2108
+ justify-content: space-between;
2109
+ align-items: center;
2110
+ font-weight: 600;
2111
+ font-size: 13px;
2112
+ color: var(--text-primary);
2113
+ letter-spacing: -0.2px;
2114
+ user-select: none;
2115
+ cursor: move;
2116
+ flex-shrink: 0;
2117
+ position: relative;
2118
+ z-index: 10;
2119
+ }
2120
+
2121
+ .floating-panel-header > span {
2122
+ flex: 1;
2123
+ }
2124
+
2125
+ .floating-panel-header button {
2126
+ width: 22px;
2127
+ height: 22px;
2128
+ border-radius: 6px;
2129
+ border: none;
2130
+ background: rgba(255, 255, 255, 0.06);
2131
+ color: var(--text-secondary);
2132
+ cursor: pointer;
2133
+ display: flex;
2134
+ align-items: center;
2135
+ justify-content: center;
2136
+ font-size: 12px;
2137
+ transition: all 0.2s;
2138
+ }
2139
+
2140
+ .floating-panel-header button:hover {
2141
+ background: rgba(255, 255, 255, 0.12);
2142
+ color: var(--text-primary);
2143
+ transform: scale(1.05);
2144
+ }
2145
+
2146
+ .floating-panel-header button:active {
2147
+ transform: scale(0.95);
2148
+ }
2149
+
2150
+ [data-theme="light"] .floating-panel-header {
2151
+ background: rgba(0, 0, 0, 0.03);
2152
+ border-bottom-color: rgba(0, 0, 0, 0.08);
2153
+ }
2154
+
2155
+ [data-theme="light"] .floating-panel-header button {
2156
+ background: rgba(0, 0, 0, 0.12);
2157
+ color: var(--text-primary);
2158
+ border: 1px solid rgba(0, 0, 0, 0.1);
2159
+ }
2160
+
2161
+ [data-theme="light"] .floating-panel-header button:hover {
2162
+ background: rgba(0, 0, 0, 0.2);
2163
+ border-color: rgba(0, 0, 0, 0.15);
2164
+ }
2165
+
2166
+ .panel-close-btn {
2167
+ position: absolute !important;
2168
+ top: 50% !important;
2169
+ right: 14px !important;
2170
+ transform: translateY(-50%) !important;
2171
+ width: 24px !important;
2172
+ height: 24px !important;
2173
+ border-radius: 8px !important;
2174
+ border: none !important;
2175
+ background: rgba(255, 255, 255, 0.06) !important;
2176
+ color: var(--text-secondary) !important;
2177
+ cursor: pointer !important;
2178
+ display: flex !important;
2179
+ align-items: center !important;
2180
+ justify-content: center !important;
2181
+ font-size: 14px !important;
2182
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
2183
+ flex-shrink: 0 !important;
2184
+ opacity: 0.7;
2185
+ z-index: 100 !important;
2186
+ }
2187
+
2188
+ .panel-close-btn:hover {
2189
+ background: rgba(255, 100, 100, 0.15) !important;
2190
+ color: #ff6b6b !important;
2191
+ transform: translateY(-50%) scale(1.1) !important;
2192
+ opacity: 1;
2193
+ }
2194
+
2195
+ .panel-close-btn:active {
2196
+ transform: translateY(-50%) scale(0.95) !important;
2197
+ }
2198
+
2199
+ [data-theme="light"] .panel-close-btn {
2200
+ background: rgba(0, 0, 0, 0.12) !important;
2201
+ color: var(--text-primary) !important;
2202
+ border: 1px solid rgba(0, 0, 0, 0.1) !important;
2203
+ }
2204
+
2205
+ [data-theme="light"] .panel-close-btn:hover {
2206
+ background: rgba(255, 100, 100, 0.2) !important;
2207
+ border-color: rgba(255, 100, 100, 0.3) !important;
2208
+ }
2209
+
2210
+ .panel-maximize-btn {
2211
+ position: absolute !important;
2212
+ top: 50% !important;
2213
+ right: 44px !important;
2214
+ transform: translateY(-50%) !important;
2215
+ width: 24px !important;
2216
+ height: 24px !important;
2217
+ border-radius: 8px !important;
2218
+ border: none !important;
2219
+ background: rgba(255, 255, 255, 0.06) !important;
2220
+ color: var(--text-secondary) !important;
2221
+ cursor: pointer !important;
2222
+ display: flex !important;
2223
+ align-items: center !important;
2224
+ justify-content: center !important;
2225
+ font-size: 14px !important;
2226
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
2227
+ flex-shrink: 0 !important;
2228
+ opacity: 0.7;
2229
+ z-index: 100 !important;
2230
+ }
2231
+
2232
+ .panel-maximize-btn:hover {
2233
+ background: rgba(100, 150, 255, 0.15) !important;
2234
+ color: var(--accent) !important;
2235
+ transform: translateY(-50%) scale(1.1) !important;
2236
+ opacity: 1;
2237
+ }
2238
+
2239
+ .panel-maximize-btn:active {
2240
+ transform: translateY(-50%) scale(0.95) !important;
2241
+ }
2242
+
2243
+ [data-theme="light"] .panel-maximize-btn {
2244
+ background: rgba(0, 0, 0, 0.12) !important;
2245
+ color: var(--text-primary) !important;
2246
+ border: 1px solid rgba(0, 0, 0, 0.1) !important;
2247
+ }
2248
+
2249
+ [data-theme="light"] .panel-maximize-btn:hover {
2250
+ background: rgba(100, 150, 255, 0.2) !important;
2251
+ border-color: var(--accent) !important;
2252
+ }
2253
+
2254
+ .joints-panel-controls {
2255
+ display: flex;
2256
+ align-items: center;
2257
+ gap: 6px;
2258
+ pointer-events: auto;
2259
+ cursor: default;
2260
+ margin-right: 0;
2261
+ }
2262
+
2263
+ #floating-joints-panel .floating-panel-header {
2264
+ justify-content: space-between;
2265
+ gap: 8px;
2266
+ padding: 16px;
2267
+ padding-right: 78px;
2268
+ }
2269
+
2270
+ #floating-joints-panel .floating-panel-header > span {
2271
+ flex-shrink: 0;
2272
+ margin-right: 0;
2273
+ }
2274
+
2275
+ .joints-panel-controls {
2276
+ display: flex;
2277
+ align-items: center;
2278
+ gap: 6px;
2279
+ flex: 1;
2280
+ pointer-events: auto;
2281
+ cursor: default;
2282
+ }
2283
+
2284
+ .joints-batch-toolbar {
2285
+ display: flex;
2286
+ align-items: center;
2287
+ justify-content: center;
2288
+ gap: 8px;
2289
+ padding: 8px 16px;
2290
+ border-bottom: 0.5px solid var(--glass-border);
2291
+ flex-shrink: 0;
2292
+ cursor: default;
2293
+ }
2294
+
2295
+ #floating-joints-panel #unit-toggle {
2296
+ display: flex;
2297
+ background: rgba(255, 255, 255, 0.08);
2298
+ border-radius: 12px;
2299
+ padding: 3px;
2300
+ gap: 2px;
2301
+ flex-shrink: 0;
2302
+ }
2303
+
2304
+ #floating-joints-panel #unit-toggle button {
2305
+ padding: 5px 12px;
2306
+ background: transparent;
2307
+ border: 1px solid rgba(255, 255, 255, 0.1);
2308
+ border-radius: 10px;
2309
+ color: var(--text-secondary);
2310
+ font-size: 11px;
2311
+ font-weight: 500;
2312
+ cursor: pointer;
2313
+ transition: all 0.3s var(--spring);
2314
+ white-space: nowrap;
2315
+ pointer-events: auto;
2316
+ min-width: 42px;
2317
+ }
2318
+
2319
+ #floating-joints-panel #unit-toggle button.active {
2320
+ background: var(--accent);
2321
+ color: white;
2322
+ box-shadow: 0 1px 4px rgba(30, 142, 62, 0.3);
2323
+ }
2324
+
2325
+ #floating-joints-panel #unit-toggle button.active:hover {
2326
+ box-shadow: 0 4px 16px rgba(30, 142, 62, 0.4);
2327
+ transform: scale(1.05);
2328
+ }
2329
+
2330
+ #floating-joints-panel #unit-toggle button.active:active {
2331
+ transform: scale(0.98);
2332
+ }
2333
+
2334
+ #floating-joints-panel #unit-toggle button:hover:not(.active) {
2335
+ color: var(--text-primary);
2336
+ background: rgba(255, 255, 255, 0.08);
2337
+ transform: scale(1.05);
2338
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
2339
+ }
2340
+
2341
+ #floating-joints-panel #unit-toggle button:active {
2342
+ transform: scale(0.98);
2343
+ }
2344
+
2345
+ [data-theme="light"] #floating-joints-panel #unit-toggle {
2346
+ background: rgba(0, 0, 0, 0.06);
2347
+ }
2348
+
2349
+ [data-theme="light"] #floating-joints-panel #unit-toggle button {
2350
+ border-color: rgba(0, 0, 0, 0.08);
2351
+ }
2352
+
2353
+ [data-theme="light"] #floating-joints-panel #unit-toggle button:hover:not(.active) {
2354
+ background: rgba(0, 0, 0, 0.06);
2355
+ }
2356
+
2357
+ #floating-joints-panel #reset-joints-btn {
2358
+ padding: 5px 12px;
2359
+ height: auto;
2360
+ background: transparent;
2361
+ border: 1px solid rgba(255, 255, 255, 0.1);
2362
+ border-radius: 12px;
2363
+ color: var(--text-secondary);
2364
+ font-size: 11px;
2365
+ font-weight: 500;
2366
+ cursor: pointer;
2367
+ transition: all 0.3s var(--spring);
2368
+ white-space: nowrap;
2369
+ flex-shrink: 0;
2370
+ pointer-events: auto;
2371
+ min-width: 42px;
2372
+ }
2373
+
2374
+ #floating-joints-panel #reset-joints-btn:hover {
2375
+ background: rgba(255, 255, 255, 0.1);
2376
+ color: var(--text-primary);
2377
+ transform: scale(1.05);
2378
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
2379
+ }
2380
+
2381
+ #floating-joints-panel #reset-joints-btn:active {
2382
+ transform: scale(0.98);
2383
+ }
2384
+
2385
+ [data-theme="light"] #floating-joints-panel #reset-joints-btn {
2386
+ border-color: rgba(0, 0, 0, 0.1);
2387
+ }
2388
+
2389
+ [data-theme="light"] #floating-joints-panel #reset-joints-btn:hover {
2390
+ background: rgba(0, 0, 0, 0.05);
2391
+ }
2392
+
2393
+ /* MuJoCo simulation control bar */
2394
+ #mujoco-simulation-bar {
2395
+ position: fixed;
2396
+ top: 80px;
2397
+ left: 50%;
2398
+ transform: translateX(-50%);
2399
+ z-index: 100;
2400
+ display: flex;
2401
+ align-items: center;
2402
+ gap: 6px;
2403
+ padding: 5px 9px;
2404
+ background: var(--glass-bg);
2405
+ backdrop-filter: blur(20px) saturate(150%);
2406
+ -webkit-backdrop-filter: blur(20px) saturate(150%);
2407
+ border: 0.5px solid var(--glass-border);
2408
+ border-radius: 21px;
2409
+ box-shadow: var(--glass-shadow);
2410
+ animation: slideDown 0.3s var(--spring);
2411
+ transition: all 0.4s var(--spring);
2412
+ }
2413
+
2414
+ @keyframes slideDown {
2415
+ from {
2416
+ opacity: 0;
2417
+ transform: translateX(-50%) translateY(-10px);
2418
+ }
2419
+ to {
2420
+ opacity: 1;
2421
+ transform: translateX(-50%) translateY(0);
2422
+ }
2423
+ }
2424
+
2425
+ #mujoco-simulation-bar button {
2426
+ padding: 5px 12px;
2427
+ height: auto;
2428
+ background: transparent;
2429
+ border: 1px solid rgba(255, 255, 255, 0.1);
2430
+ border-radius: 12px;
2431
+ color: var(--text-secondary);
2432
+ font-size: 11px;
2433
+ font-weight: 500;
2434
+ cursor: pointer;
2435
+ transition: all 0.3s var(--spring);
2436
+ white-space: nowrap;
2437
+ }
2438
+
2439
+ #mujoco-simulation-bar button:hover {
2440
+ background: rgba(255, 255, 255, 0.1);
2441
+ color: var(--text-primary);
2442
+ transform: scale(1.05);
2443
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
2444
+ }
2445
+
2446
+ #mujoco-simulation-bar button:active {
2447
+ transform: scale(0.98);
2448
+ }
2449
+
2450
+ #mujoco-simulate-btn-bar.active {
2451
+ background: var(--accent);
2452
+ color: white;
2453
+ box-shadow: 0 2px 8px rgba(30, 142, 62, 0.3);
2454
+ }
2455
+
2456
+ #mujoco-simulate-btn-bar.active:hover {
2457
+ background: var(--accent);
2458
+ box-shadow: 0 4px 16px rgba(30, 142, 62, 0.4);
2459
+ transform: scale(1.05);
2460
+ }
2461
+
2462
+ #mujoco-simulate-btn-bar.active:active {
2463
+ transform: scale(0.98);
2464
+ }
2465
+
2466
+ [data-theme="light"] #mujoco-simulation-bar {
2467
+ background: var(--glass-bg);
2468
+ border-color: var(--glass-border);
2469
+ }
2470
+
2471
+ [data-theme="light"] #mujoco-simulation-bar button {
2472
+ border: 1px solid rgba(0, 0, 0, 0.1);
2473
+ }
2474
+
2475
+ [data-theme="light"] #mujoco-simulation-bar button:hover {
2476
+ background: rgba(0, 0, 0, 0.05);
2477
+ }
2478
+
2479
+ #floating-joints-panel #ignore-limits {
2480
+ padding: 5px 12px;
2481
+ height: auto;
2482
+ background: transparent;
2483
+ border: 1px solid rgba(255, 255, 255, 0.1);
2484
+ border-radius: 12px;
2485
+ color: var(--text-secondary);
2486
+ font-size: 11px;
2487
+ font-weight: 500;
2488
+ cursor: pointer;
2489
+ transition: all 0.3s var(--spring);
2490
+ white-space: nowrap;
2491
+ flex-shrink: 0;
2492
+ pointer-events: auto;
2493
+ min-width: 42px;
2494
+ }
2495
+
2496
+ #floating-joints-panel #ignore-limits:hover {
2497
+ background: rgba(255, 255, 255, 0.1);
2498
+ color: var(--text-primary);
2499
+ transform: scale(1.05);
2500
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
2501
+ }
2502
+
2503
+ #floating-joints-panel #ignore-limits:active {
2504
+ transform: scale(0.98);
2505
+ }
2506
+
2507
+ #floating-joints-panel #ignore-limits.active {
2508
+ background: var(--accent);
2509
+ color: white;
2510
+ border-color: var(--accent);
2511
+ box-shadow: 0 1px 4px rgba(30, 142, 62, 0.3);
2512
+ }
2513
+
2514
+ #floating-joints-panel #ignore-limits.active:hover {
2515
+ box-shadow: 0 4px 16px rgba(30, 142, 62, 0.4);
2516
+ transform: scale(1.05);
2517
+ }
2518
+
2519
+ #floating-joints-panel #ignore-limits.active:active {
2520
+ transform: scale(0.98);
2521
+ }
2522
+
2523
+ [data-theme="light"] #floating-joints-panel #ignore-limits {
2524
+ border-color: rgba(0, 0, 0, 0.1);
2525
+ }
2526
+
2527
+ [data-theme="light"] #floating-joints-panel #ignore-limits:hover {
2528
+ background: rgba(0, 0, 0, 0.05);
2529
+ }
2530
+
2531
+ .model-graph-content {
2532
+ flex: 1;
2533
+ overflow: hidden;
2534
+ position: relative;
2535
+ border-radius: 0 0 16px 16px;
2536
+ padding: 12px;
2537
+ display: flex;
2538
+ flex-direction: column;
2539
+ }
2540
+
2541
+ .model-graph-wrapper {
2542
+ position: relative;
2543
+ width: 100%;
2544
+ height: 100%;
2545
+ border-radius: 8px;
2546
+ overflow: hidden;
2547
+ }
2548
+
2549
+ #model-graph-svg {
2550
+ width: 100%;
2551
+ height: 100%;
2552
+ background: transparent;
2553
+ cursor: grab;
2554
+ }
2555
+
2556
+ #model-graph-svg:active {
2557
+ cursor: grabbing;
2558
+ }
2559
+
2560
+ #graph-empty-state {
2561
+ position: absolute;
2562
+ top: 50%;
2563
+ left: 50%;
2564
+ transform: translate(-50%, -50%);
2565
+ display: block;
2566
+ }
2567
+
2568
+ #graph-empty-state.hidden {
2569
+ display: none;
2570
+ }
2571
+
2572
+ .graph-node {
2573
+ cursor: pointer;
2574
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
2575
+ }
2576
+
2577
+ .graph-node .node-bg {
2578
+ filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.15));
2579
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
2580
+ }
2581
+
2582
+ .graph-node:hover .node-bg {
2583
+ filter: drop-shadow(0 4px 16px rgba(0, 0, 0, 0.25));
2584
+ }
2585
+
2586
+ .graph-node .node-border {
2587
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
2588
+ }
2589
+
2590
+ .graph-node.selected .node-border {
2591
+ stroke: rgba(70, 170, 255, 0.8) !important;
2592
+ stroke-width: 3px !important;
2593
+ }
2594
+
2595
+ [data-theme="light"] .graph-node.selected .node-bg {
2596
+ fill: rgba(30, 142, 62, 0.15) !important;
2597
+ }
2598
+
2599
+ [data-theme="light"] .graph-node.selected .node-border {
2600
+ stroke: var(--accent) !important;
2601
+ }
2602
+
2603
+ [data-theme="light"] .graph-node.selected text {
2604
+ fill: var(--accent) !important;
2605
+ font-weight: 600;
2606
+ }
2607
+
2608
+ .graph-link {
2609
+ stroke: var(--glass-border);
2610
+ stroke-width: 2px;
2611
+ fill: none;
2612
+ marker-end: url(#arrowhead);
2613
+ }
2614
+
2615
+ .graph-joint-label {
2616
+ fill: rgba(255, 255, 255, 0.9);
2617
+ font-size: 13px;
2618
+ text-anchor: middle;
2619
+ cursor: pointer;
2620
+ font-weight: 500;
2621
+ letter-spacing: 0.3px;
2622
+ }
2623
+
2624
+ .graph-joint-label-bg {
2625
+ fill: rgba(50, 50, 55, 0.95);
2626
+ stroke: rgba(255, 255, 255, 0.1);
2627
+ stroke-width: 1px;
2628
+ cursor: pointer;
2629
+ filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.2));
2630
+ transition: all 0.25s var(--spring);
2631
+ }
2632
+
2633
+ .graph-joint-label-bg:hover {
2634
+ fill: rgba(70, 70, 75, 0.98);
2635
+ stroke: rgba(70, 170, 255, 0.5);
2636
+ filter: drop-shadow(0 3px 10px rgba(0, 0, 0, 0.3));
2637
+ transform: translateY(-1px);
2638
+ }
2639
+
2640
+ .graph-joint-group.selected .graph-joint-label-bg {
2641
+ fill: rgba(70, 170, 255, 0.25);
2642
+ stroke: rgba(70, 170, 255, 0.9);
2643
+ stroke-width: 2px;
2644
+ filter: drop-shadow(0 2px 8px rgba(70, 170, 255, 0.4));
2645
+ }
2646
+
2647
+ .graph-joint-group.selected .graph-joint-label {
2648
+ fill: rgba(70, 200, 255, 1);
2649
+ font-weight: 600;
2650
+ }
2651
+
2652
+ [data-theme="light"] .graph-joint-label-bg {
2653
+ fill: rgba(240, 240, 245, 0.95) !important;
2654
+ stroke: rgba(0, 0, 0, 0.15) !important;
2655
+ }
2656
+
2657
+ [data-theme="light"] .graph-joint-label {
2658
+ fill: rgba(0, 0, 0, 0.85) !important;
2659
+ }
2660
+
2661
+ [data-theme="light"] .graph-joint-label-bg:hover {
2662
+ fill: rgba(230, 235, 245, 1) !important;
2663
+ stroke: var(--accent) !important;
2664
+ }
2665
+
2666
+ [data-theme="light"] .graph-joint-group.selected .graph-joint-label-bg {
2667
+ fill: rgba(30, 142, 62, 0.15) !important;
2668
+ stroke: var(--accent) !important;
2669
+ stroke-width: 2px !important;
2670
+ }
2671
+
2672
+ [data-theme="light"] .graph-joint-group.selected .graph-joint-label {
2673
+ fill: var(--accent) !important;
2674
+ font-weight: 600;
2675
+ }
2676
+
2677
+ /* Measurement mode selected joint (Ctrl+click) */
2678
+ .graph-joint-group.measurement-selected .graph-joint-label-bg {
2679
+ fill: rgba(255, 200, 0, 0.25);
2680
+ stroke: rgba(255, 220, 0, 0.9);
2681
+ stroke-width: 2px;
2682
+ filter: drop-shadow(0 2px 8px rgba(255, 200, 0, 0.4));
2683
+ }
2684
+
2685
+ .graph-joint-group.measurement-selected .graph-joint-label {
2686
+ fill: rgba(255, 230, 0, 1);
2687
+ font-weight: 600;
2688
+ }
2689
+
2690
+ [data-theme="light"] .graph-joint-group.measurement-selected .graph-joint-label-bg {
2691
+ fill: rgba(255, 200, 0, 0.15) !important;
2692
+ stroke: rgba(255, 180, 0, 1) !important;
2693
+ }
2694
+
2695
+ [data-theme="light"] .graph-joint-group.measurement-selected .graph-joint-label {
2696
+ fill: rgba(200, 150, 0, 1) !important;
2697
+ }
2698
+
2699
+ /* Measurement mode selected Link node (Ctrl+click) */
2700
+ .graph-node.measurement-selected .node-bg {
2701
+ fill: rgba(255, 200, 0, 0.2) !important;
2702
+ }
2703
+
2704
+ .graph-node.measurement-selected .node-border {
2705
+ stroke: rgba(255, 220, 0, 0.9) !important;
2706
+ stroke-width: 3px !important;
2707
+ filter: drop-shadow(0 0 8px rgba(255, 200, 0, 0.5));
2708
+ }
2709
+
2710
+ .graph-node.measurement-selected text {
2711
+ fill: rgba(255, 230, 0, 1) !important;
2712
+ font-weight: 600;
2713
+ }
2714
+
2715
+ [data-theme="light"] .graph-node.measurement-selected .node-bg {
2716
+ fill: rgba(255, 200, 0, 0.15) !important;
2717
+ }
2718
+
2719
+ [data-theme="light"] .graph-node.measurement-selected .node-border {
2720
+ stroke: rgba(255, 180, 0, 1) !important;
2721
+ }
2722
+
2723
+ [data-theme="light"] .graph-node.measurement-selected text {
2724
+ fill: rgba(200, 150, 0, 1) !important;
2725
+ }
2726
+
2727
+ .graph-controls-hint {
2728
+ padding: 6px 12px;
2729
+ font-size: 10px;
2730
+ color: var(--text-tertiary);
2731
+ background: rgba(255, 255, 255, 0.04);
2732
+ border-bottom: 0.5px solid var(--glass-border);
2733
+ text-align: center;
2734
+ user-select: none;
2735
+ white-space: nowrap;
2736
+ overflow: hidden;
2737
+ text-overflow: ellipsis;
2738
+ }
2739
+
2740
+ [data-theme="light"] .graph-controls-hint {
2741
+ background: rgba(0, 0, 0, 0.03);
2742
+ border-bottom-color: rgba(0, 0, 0, 0.08);
2743
+ }
2744
+
2745
+ .joint-control {
2746
+ margin-bottom: 3px;
2747
+ padding: 5px 8px;
2748
+ background: rgba(255, 255, 255, 0.04);
2749
+ border: 1px solid rgba(255, 255, 255, 0.08);
2750
+ border-radius: 12px;
2751
+ display: flex;
2752
+ flex-direction: column;
2753
+ gap: 4px;
2754
+ transition: all 0.2s;
2755
+ }
2756
+
2757
+ .joint-control:hover {
2758
+ background: rgba(255, 255, 255, 0.06);
2759
+ border-color: rgba(255, 255, 255, 0.12);
2760
+ }
2761
+
2762
+ [data-theme="light"] .joint-control {
2763
+ background: rgba(0, 0, 0, 0.03);
2764
+ border-color: rgba(0, 0, 0, 0.08);
2765
+ }
2766
+
2767
+ [data-theme="light"] .joint-control:hover {
2768
+ background: rgba(0, 0, 0, 0.05);
2769
+ border-color: rgba(0, 0, 0, 0.12);
2770
+ }
2771
+
2772
+ .joint-header {
2773
+ display: flex;
2774
+ justify-content: space-between;
2775
+ align-items: center;
2776
+ gap: 10px;
2777
+ }
2778
+
2779
+ .joint-name {
2780
+ font-weight: 600;
2781
+ color: var(--text-primary);
2782
+ font-size: 12px;
2783
+ overflow: hidden;
2784
+ text-overflow: ellipsis;
2785
+ white-space: nowrap;
2786
+ flex: 1;
2787
+ min-width: 0;
2788
+ }
2789
+
2790
+ .joint-value-input-container {
2791
+ display: flex;
2792
+ align-items: center;
2793
+ gap: 3px;
2794
+ flex-shrink: 0;
2795
+ margin-left: 4px;
2796
+ }
2797
+
2798
+ .joint-value-input {
2799
+ width: 42px;
2800
+ padding: 2px 4px;
2801
+ background: rgba(255, 255, 255, 0.04);
2802
+ border: 1px solid rgba(255, 255, 255, 0.15);
2803
+ border-radius: 6px;
2804
+ color: var(--text-primary);
2805
+ font-size: 10px;
2806
+ font-variant-numeric: tabular-nums;
2807
+ transition: all 0.2s;
2808
+ text-align: right;
2809
+ font-weight: 500;
2810
+ }
2811
+
2812
+ .joint-value-input::-webkit-outer-spin-button,
2813
+ .joint-value-input::-webkit-inner-spin-button {
2814
+ -webkit-appearance: none;
2815
+ margin: 0;
2816
+ }
2817
+
2818
+ .joint-value-input[type=number] {
2819
+ -moz-appearance: textfield;
2820
+ appearance: textfield;
2821
+ }
2822
+
2823
+ .joint-value-input:focus {
2824
+ outline: none;
2825
+ border-color: var(--accent);
2826
+ background: rgba(255, 255, 255, 0.08);
2827
+ }
2828
+
2829
+ .joint-value-input:hover {
2830
+ background: rgba(255, 255, 255, 0.06);
2831
+ border-color: rgba(255, 255, 255, 0.2);
2832
+ }
2833
+
2834
+ .joint-slider-row {
2835
+ display: flex;
2836
+ align-items: center;
2837
+ gap: 8px;
2838
+ width: 100%;
2839
+ }
2840
+
2841
+ .joint-limit-min {
2842
+ font-size: 10px;
2843
+ color: var(--text-tertiary);
2844
+ width: 38px;
2845
+ text-align: right;
2846
+ flex-shrink: 0;
2847
+ font-variant-numeric: tabular-nums;
2848
+ font-weight: 500;
2849
+ background: rgba(255, 255, 255, 0.03);
2850
+ border: 1px solid rgba(255, 255, 255, 0.12);
2851
+ border-radius: 4px;
2852
+ padding: 1px 3px;
2853
+ transition: all 0.2s;
2854
+ cursor: pointer;
2855
+ }
2856
+
2857
+ .joint-limit-min:hover {
2858
+ background: rgba(255, 255, 255, 0.06);
2859
+ border-color: rgba(255, 255, 255, 0.18);
2860
+ }
2861
+
2862
+ .joint-limit-min:focus {
2863
+ outline: none;
2864
+ border-color: var(--accent);
2865
+ background: rgba(255, 255, 255, 0.08);
2866
+ color: var(--text-primary);
2867
+ }
2868
+
2869
+ .joint-limit-min::-webkit-outer-spin-button,
2870
+ .joint-limit-min::-webkit-inner-spin-button {
2871
+ -webkit-appearance: none;
2872
+ margin: 0;
2873
+ }
2874
+
2875
+ .joint-limit-min[type=number] {
2876
+ -moz-appearance: textfield;
2877
+ appearance: textfield;
2878
+ }
2879
+
2880
+ .joint-slider-container {
2881
+ flex: 1;
2882
+ min-width: 0;
2883
+ }
2884
+
2885
+ .joint-slider {
2886
+ width: 100%;
2887
+ height: 20px;
2888
+ cursor: pointer;
2889
+ -webkit-appearance: none;
2890
+ appearance: none;
2891
+ background: transparent;
2892
+ outline: none;
2893
+ }
2894
+
2895
+ .joint-slider::-webkit-slider-runnable-track {
2896
+ width: 100%;
2897
+ height: 4px;
2898
+ background: linear-gradient(
2899
+ to right,
2900
+ rgba(255, 255, 255, 0.08) 0%,
2901
+ rgba(255, 255, 255, 0.12) 100%
2902
+ );
2903
+ border-radius: 2px;
2904
+ border: 0.5px solid rgba(255, 255, 255, 0.1);
2905
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
2906
+ }
2907
+
2908
+ .joint-slider:hover::-webkit-slider-runnable-track {
2909
+ background: linear-gradient(
2910
+ to right,
2911
+ rgba(255, 255, 255, 0.12) 0%,
2912
+ rgba(255, 255, 255, 0.16) 100%
2913
+ );
2914
+ border-color: rgba(255, 255, 255, 0.15);
2915
+ }
2916
+
2917
+ .joint-slider::-webkit-slider-thumb {
2918
+ -webkit-appearance: none;
2919
+ appearance: none;
2920
+ width: 18px;
2921
+ height: 18px;
2922
+ border-radius: 50%;
2923
+ background: linear-gradient(
2924
+ 135deg,
2925
+ rgba(255, 255, 255, 0.95) 0%,
2926
+ rgba(255, 255, 255, 0.85) 100%
2927
+ );
2928
+ border: 2px solid rgba(30, 142, 62, 0.6);
2929
+ box-shadow:
2930
+ 0 2px 8px rgba(0, 0, 0, 0.15),
2931
+ 0 0 0 0 rgba(30, 142, 62, 0.3),
2932
+ inset 0 1px 2px rgba(255, 255, 255, 0.3);
2933
+ cursor: grab;
2934
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
2935
+ margin-top: -7px;
2936
+ position: relative;
2937
+ }
2938
+
2939
+ .joint-slider:hover::-webkit-slider-thumb {
2940
+ background: linear-gradient(
2941
+ 135deg,
2942
+ rgba(255, 255, 255, 1) 0%,
2943
+ rgba(255, 255, 255, 0.95) 100%
2944
+ );
2945
+ border-color: var(--accent);
2946
+ box-shadow:
2947
+ 0 4px 12px rgba(0, 0, 0, 0.2),
2948
+ 0 0 0 4px rgba(30, 142, 62, 0.15),
2949
+ inset 0 1px 2px rgba(255, 255, 255, 0.4);
2950
+ transform: scale(1.15);
2951
+ }
2952
+
2953
+ .joint-slider:active::-webkit-slider-thumb {
2954
+ cursor: grabbing;
2955
+ background: linear-gradient(
2956
+ 135deg,
2957
+ rgba(255, 255, 255, 1) 0%,
2958
+ rgba(240, 245, 255, 1) 100%
2959
+ );
2960
+ border-color: var(--accent);
2961
+ box-shadow:
2962
+ 0 2px 6px rgba(0, 0, 0, 0.25),
2963
+ 0 0 0 6px rgba(30, 142, 62, 0.25),
2964
+ inset 0 1px 2px rgba(255, 255, 255, 0.5);
2965
+ transform: scale(1.1);
2966
+ }
2967
+
2968
+ .joint-slider::-moz-range-track {
2969
+ width: 100%;
2970
+ height: 4px;
2971
+ background: linear-gradient(
2972
+ to right,
2973
+ rgba(255, 255, 255, 0.08) 0%,
2974
+ rgba(255, 255, 255, 0.12) 100%
2975
+ );
2976
+ border-radius: 2px;
2977
+ border: 0.5px solid rgba(255, 255, 255, 0.1);
2978
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
2979
+ }
2980
+
2981
+ .joint-slider:hover::-moz-range-track {
2982
+ background: linear-gradient(
2983
+ to right,
2984
+ rgba(255, 255, 255, 0.12) 0%,
2985
+ rgba(255, 255, 255, 0.16) 100%
2986
+ );
2987
+ border-color: rgba(255, 255, 255, 0.15);
2988
+ }
2989
+
2990
+ .joint-slider::-moz-range-thumb {
2991
+ width: 18px;
2992
+ height: 18px;
2993
+ border-radius: 50%;
2994
+ background: linear-gradient(
2995
+ 135deg,
2996
+ rgba(255, 255, 255, 0.95) 0%,
2997
+ rgba(255, 255, 255, 0.85) 100%
2998
+ );
2999
+ border: 2px solid rgba(30, 142, 62, 0.6);
3000
+ box-shadow:
3001
+ 0 2px 8px rgba(0, 0, 0, 0.15),
3002
+ 0 0 0 0 rgba(30, 142, 62, 0.3),
3003
+ inset 0 1px 2px rgba(255, 255, 255, 0.3);
3004
+ cursor: grab;
3005
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
3006
+ }
3007
+
3008
+ .joint-slider:hover::-moz-range-thumb {
3009
+ background: linear-gradient(
3010
+ 135deg,
3011
+ rgba(255, 255, 255, 1) 0%,
3012
+ rgba(255, 255, 255, 0.95) 100%
3013
+ );
3014
+ border-color: var(--accent);
3015
+ box-shadow:
3016
+ 0 4px 12px rgba(0, 0, 0, 0.2),
3017
+ 0 0 0 4px rgba(30, 142, 62, 0.15),
3018
+ inset 0 1px 2px rgba(255, 255, 255, 0.4);
3019
+ transform: scale(1.15);
3020
+ }
3021
+
3022
+ .joint-slider:active::-moz-range-thumb {
3023
+ cursor: grabbing;
3024
+ background: linear-gradient(
3025
+ 135deg,
3026
+ rgba(255, 255, 255, 1) 0%,
3027
+ rgba(240, 245, 255, 1) 100%
3028
+ );
3029
+ border-color: var(--accent);
3030
+ box-shadow:
3031
+ 0 2px 6px rgba(0, 0, 0, 0.25),
3032
+ 0 0 0 6px rgba(30, 142, 62, 0.25),
3033
+ inset 0 1px 2px rgba(255, 255, 255, 0.5);
3034
+ transform: scale(1.1);
3035
+ }
3036
+
3037
+ [data-theme="light"] .joint-slider::-webkit-slider-runnable-track {
3038
+ background: linear-gradient(
3039
+ to right,
3040
+ rgba(0, 0, 0, 0.06) 0%,
3041
+ rgba(0, 0, 0, 0.08) 100%
3042
+ );
3043
+ border-color: rgba(0, 0, 0, 0.1);
3044
+ }
3045
+
3046
+ [data-theme="light"] .joint-slider:hover::-webkit-slider-runnable-track {
3047
+ background: linear-gradient(
3048
+ to right,
3049
+ rgba(0, 0, 0, 0.08) 0%,
3050
+ rgba(0, 0, 0, 0.12) 100%
3051
+ );
3052
+ border-color: rgba(0, 0, 0, 0.15);
3053
+ }
3054
+
3055
+ [data-theme="light"] .joint-slider::-webkit-slider-thumb {
3056
+ background: linear-gradient(
3057
+ 135deg,
3058
+ rgba(255, 255, 255, 1) 0%,
3059
+ rgba(245, 247, 250, 1) 100%
3060
+ );
3061
+ border-color: rgba(30, 142, 62, 0.7);
3062
+ box-shadow:
3063
+ 0 2px 8px rgba(0, 0, 0, 0.12),
3064
+ 0 0 0 0 rgba(30, 142, 62, 0.2),
3065
+ inset 0 1px 2px rgba(255, 255, 255, 0.8);
3066
+ }
3067
+
3068
+ [data-theme="light"] .joint-slider:hover::-webkit-slider-thumb {
3069
+ border-color: #1e8e3e;
3070
+ box-shadow:
3071
+ 0 4px 12px rgba(0, 0, 0, 0.18),
3072
+ 0 0 0 4px rgba(30, 142, 62, 0.12),
3073
+ inset 0 1px 2px rgba(255, 255, 255, 0.9);
3074
+ }
3075
+
3076
+ [data-theme="light"] .joint-slider:active::-webkit-slider-thumb {
3077
+ box-shadow:
3078
+ 0 2px 6px rgba(0, 0, 0, 0.2),
3079
+ 0 0 0 6px rgba(30, 142, 62, 0.18),
3080
+ inset 0 1px 2px rgba(255, 255, 255, 1);
3081
+ }
3082
+
3083
+ [data-theme="light"] .joint-slider::-moz-range-track {
3084
+ background: linear-gradient(
3085
+ to right,
3086
+ rgba(0, 0, 0, 0.06) 0%,
3087
+ rgba(0, 0, 0, 0.08) 100%
3088
+ );
3089
+ border-color: rgba(0, 0, 0, 0.1);
3090
+ }
3091
+
3092
+ [data-theme="light"] .joint-slider:hover::-moz-range-track {
3093
+ background: linear-gradient(
3094
+ to right,
3095
+ rgba(0, 0, 0, 0.08) 0%,
3096
+ rgba(0, 0, 0, 0.12) 100%
3097
+ );
3098
+ border-color: rgba(0, 0, 0, 0.15);
3099
+ }
3100
+
3101
+ [data-theme="light"] .joint-slider::-moz-range-thumb {
3102
+ background: linear-gradient(
3103
+ 135deg,
3104
+ rgba(255, 255, 255, 1) 0%,
3105
+ rgba(245, 247, 250, 1) 100%
3106
+ );
3107
+ border-color: rgba(30, 142, 62, 0.7);
3108
+ box-shadow:
3109
+ 0 2px 8px rgba(0, 0, 0, 0.12),
3110
+ 0 0 0 0 rgba(30, 142, 62, 0.2),
3111
+ inset 0 1px 2px rgba(255, 255, 255, 0.8);
3112
+ }
3113
+
3114
+ [data-theme="light"] .joint-slider:hover::-moz-range-thumb {
3115
+ border-color: #1e8e3e;
3116
+ box-shadow:
3117
+ 0 4px 12px rgba(0, 0, 0, 0.18),
3118
+ 0 0 0 4px rgba(30, 142, 62, 0.12),
3119
+ inset 0 1px 2px rgba(255, 255, 255, 0.9);
3120
+ }
3121
+
3122
+ [data-theme="light"] .joint-slider:active::-moz-range-thumb {
3123
+ box-shadow:
3124
+ 0 2px 6px rgba(0, 0, 0, 0.2),
3125
+ 0 0 0 6px rgba(30, 142, 62, 0.18),
3126
+ inset 0 1px 2px rgba(255, 255, 255, 1);
3127
+ }
3128
+
3129
+ .joint-limit-max {
3130
+ font-size: 10px;
3131
+ color: var(--text-tertiary);
3132
+ width: 38px;
3133
+ text-align: left;
3134
+ flex-shrink: 0;
3135
+ font-variant-numeric: tabular-nums;
3136
+ font-weight: 500;
3137
+ background: rgba(255, 255, 255, 0.03);
3138
+ border: 1px solid rgba(255, 255, 255, 0.12);
3139
+ border-radius: 4px;
3140
+ padding: 1px 3px;
3141
+ transition: all 0.2s;
3142
+ cursor: pointer;
3143
+ }
3144
+
3145
+ .joint-limit-max:hover {
3146
+ background: rgba(255, 255, 255, 0.06);
3147
+ border-color: rgba(255, 255, 255, 0.18);
3148
+ }
3149
+
3150
+ .joint-limit-max:focus {
3151
+ outline: none;
3152
+ border-color: var(--accent);
3153
+ background: rgba(255, 255, 255, 0.08);
3154
+ color: var(--text-primary);
3155
+ }
3156
+
3157
+ .joint-limit-max::-webkit-outer-spin-button,
3158
+ .joint-limit-max::-webkit-inner-spin-button {
3159
+ -webkit-appearance: none;
3160
+ margin: 0;
3161
+ }
3162
+
3163
+ .joint-limit-max[type=number] {
3164
+ -moz-appearance: textfield;
3165
+ appearance: textfield;
3166
+ }
3167
+
3168
+ .joint-value-unit {
3169
+ font-size: 9px;
3170
+ color: var(--text-tertiary);
3171
+ min-width: 18px;
3172
+ text-align: left;
3173
+ font-weight: 500;
3174
+ }
3175
+
3176
+ .joint-extra-field {
3177
+ display: flex;
3178
+ align-items: center;
3179
+ gap: 4px;
3180
+ flex-shrink: 0;
3181
+ }
3182
+
3183
+ .joint-extra-label {
3184
+ font-size: 9px;
3185
+ color: var(--text-tertiary);
3186
+ font-weight: 500;
3187
+ white-space: nowrap;
3188
+ }
3189
+
3190
+ .joint-extra-input {
3191
+ width: 32px;
3192
+ padding: 2px 3px;
3193
+ background: rgba(255, 255, 255, 0.03);
3194
+ border: 1px solid rgba(255, 255, 255, 0.12);
3195
+ border-radius: 4px;
3196
+ color: var(--text-tertiary);
3197
+ font-size: 9px;
3198
+ font-variant-numeric: tabular-nums;
3199
+ transition: all 0.2s;
3200
+ text-align: right;
3201
+ font-weight: 500;
3202
+ cursor: pointer;
3203
+ }
3204
+
3205
+ .joint-extra-input:hover {
3206
+ background: rgba(255, 255, 255, 0.06);
3207
+ border-color: rgba(255, 255, 255, 0.18);
3208
+ }
3209
+
3210
+ .joint-extra-input::-webkit-outer-spin-button,
3211
+ .joint-extra-input::-webkit-inner-spin-button {
3212
+ -webkit-appearance: none;
3213
+ margin: 0;
3214
+ }
3215
+
3216
+ .joint-extra-input[type=number] {
3217
+ -moz-appearance: textfield;
3218
+ appearance: textfield;
3219
+ }
3220
+
3221
+ .joint-extra-input:focus {
3222
+ outline: none;
3223
+ border-color: var(--accent);
3224
+ background: rgba(255, 255, 255, 0.08);
3225
+ color: var(--text-primary);
3226
+ }
3227
+
3228
+ /* 批量操作按钮 */
3229
+ .batch-btn {
3230
+ display: inline-flex;
3231
+ align-items: center;
3232
+ gap: 5px;
3233
+ padding: 5px 12px;
3234
+ height: 28px;
3235
+ background: rgba(255, 255, 255, 0.06);
3236
+ border: 1px solid rgba(255, 255, 255, 0.12);
3237
+ border-radius: 8px;
3238
+ color: var(--text-secondary);
3239
+ font-size: 12px;
3240
+ font-weight: 500;
3241
+ cursor: pointer;
3242
+ transition: all 0.2s;
3243
+ white-space: nowrap;
3244
+ }
3245
+
3246
+ [data-theme="light"] .batch-btn {
3247
+ background: rgba(0, 0, 0, 0.04);
3248
+ border-color: rgba(0, 0, 0, 0.1);
3249
+ color: var(--text-secondary);
3250
+ }
3251
+
3252
+ .batch-btn:hover:not(:disabled) {
3253
+ background: rgba(255, 255, 255, 0.12);
3254
+ border-color: rgba(255, 255, 255, 0.22);
3255
+ color: var(--text-primary);
3256
+ }
3257
+
3258
+ [data-theme="light"] .batch-btn:hover:not(:disabled) {
3259
+ background: rgba(0, 0, 0, 0.08);
3260
+ border-color: rgba(0, 0, 0, 0.18);
3261
+ }
3262
+
3263
+ .batch-btn:disabled {
3264
+ opacity: 0.3;
3265
+ cursor: not-allowed;
3266
+ }
3267
+
3268
+ /* 全选按钮 — 无 hover 效果,仅选中/未选中两态 */
3269
+ .select-all-btn:hover:not(:disabled) {
3270
+ background: rgba(255, 255, 255, 0.06);
3271
+ border-color: rgba(255, 255, 255, 0.12);
3272
+ color: var(--text-secondary);
3273
+ }
3274
+
3275
+ [data-theme="light"] .select-all-btn:hover:not(:disabled) {
3276
+ background: rgba(0, 0, 0, 0.04);
3277
+ border-color: rgba(0, 0, 0, 0.1);
3278
+ color: var(--text-secondary);
3279
+ }
3280
+
3281
+ .select-all-btn.active {
3282
+ background: var(--accent);
3283
+ color: white;
3284
+ border-color: var(--accent);
3285
+ box-shadow: 0 2px 8px rgba(30, 142, 62, 0.3);
3286
+ }
3287
+
3288
+ [data-theme="light"] .select-all-btn.active {
3289
+ background: var(--accent);
3290
+ color: white;
3291
+ border-color: var(--accent);
3292
+ }
3293
+
3294
+ .show-mimic-btn.active {
3295
+ background: var(--accent);
3296
+ color: white;
3297
+ border-color: var(--accent);
3298
+ box-shadow: 0 2px 8px rgba(30, 142, 62, 0.3);
3299
+ }
3300
+
3301
+ [data-theme="light"] .show-mimic-btn.active {
3302
+ background: var(--accent);
3303
+ color: white;
3304
+ border-color: var(--accent);
3305
+ }
3306
+
3307
+ .joint-mimic-badge {
3308
+ display: inline-block;
3309
+ margin-left: 6px;
3310
+ padding: 0 6px;
3311
+ font-size: 10px;
3312
+ font-weight: 500;
3313
+ line-height: 16px;
3314
+ border-radius: 6px;
3315
+ background: rgba(134, 200, 150, 0.25);
3316
+ color: #4caf7d;
3317
+ vertical-align: middle;
3318
+ }
3319
+
3320
+ [data-theme="light"] .joint-mimic-badge {
3321
+ background: rgba(76, 175, 125, 0.15);
3322
+ color: #2f8a5e;
3323
+ }
3324
+
3325
+ /* 关节复选框 */
3326
+ .joint-checkbox-container {
3327
+ display: flex;
3328
+ align-items: center;
3329
+ margin-right: 8px;
3330
+ flex-shrink: 0;
3331
+ }
3332
+
3333
+ .joint-checkbox {
3334
+ width: 16px;
3335
+ height: 16px;
3336
+ cursor: pointer;
3337
+ accent-color: #4a9eff;
3338
+ margin: 0;
3339
+ }
3340
+
3341
+ .joint-control {
3342
+ display: flex;
3343
+ flex-direction: row;
3344
+ align-items: flex-start;
3345
+ }
3346
+
3347
+ .joint-control-content {
3348
+ flex: 1;
3349
+ display: flex;
3350
+ flex-direction: column;
3351
+ gap: 4px;
3352
+ }
3353
+
3354
+ /* Toast 提示 */
3355
+ .toast-notification {
3356
+ position: fixed;
3357
+ top: 80px;
3358
+ right: 20px;
3359
+ padding: 12px 20px;
3360
+ border-radius: 8px;
3361
+ color: #fff;
3362
+ font-size: 13px;
3363
+ z-index: 10000;
3364
+ animation: slideInRight 0.3s ease-out;
3365
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
3366
+ backdrop-filter: blur(10px);
3367
+ }
3368
+
3369
+ .toast-notification.success {
3370
+ background: rgba(76, 175, 80, 0.95);
3371
+ }
3372
+
3373
+ .toast-notification.error {
3374
+ background: rgba(244, 67, 54, 0.95);
3375
+ }
3376
+
3377
+ .toast-notification.warning {
3378
+ background: rgba(255, 152, 0, 0.95);
3379
+ }
3380
+
3381
+ @keyframes slideInRight {
3382
+ from {
3383
+ transform: translateX(400px);
3384
+ opacity: 0;
3385
+ }
3386
+ to {
3387
+ transform: translateX(0);
3388
+ opacity: 1;
3389
+ }
3390
+ }
3391
+
3392
+ /* 应用角度对话框 */
3393
+ .apply-dialog-overlay {
3394
+ position: fixed;
3395
+ top: 0;
3396
+ left: 0;
3397
+ right: 0;
3398
+ bottom: 0;
3399
+ background: rgba(0, 0, 0, 0.6);
3400
+ backdrop-filter: blur(4px);
3401
+ z-index: 9999;
3402
+ display: flex;
3403
+ align-items: center;
3404
+ justify-content: center;
3405
+ animation: fadeIn 0.2s ease-out;
3406
+ }
3407
+
3408
+ .apply-dialog {
3409
+ background: var(--panel-bg);
3410
+ border: 1px solid rgba(255, 255, 255, 0.1);
3411
+ border-radius: 12px;
3412
+ padding: 24px;
3413
+ width: 90%;
3414
+ max-width: 500px;
3415
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
3416
+ animation: scaleIn 0.2s ease-out;
3417
+ }
3418
+
3419
+ [data-theme="light"] .apply-dialog {
3420
+ border-color: rgba(0, 0, 0, 0.1);
3421
+ }
3422
+
3423
+ .apply-dialog-title {
3424
+ font-size: 16px;
3425
+ font-weight: 600;
3426
+ color: var(--text-primary);
3427
+ margin-bottom: 12px;
3428
+ }
3429
+
3430
+ .apply-dialog-hint {
3431
+ font-size: 12px;
3432
+ color: var(--text-secondary);
3433
+ margin-bottom: 12px;
3434
+ }
3435
+
3436
+ .apply-dialog-textarea {
3437
+ width: 100%;
3438
+ min-height: 120px;
3439
+ padding: 12px;
3440
+ background: rgba(255, 255, 255, 0.05);
3441
+ border: 1px solid rgba(255, 255, 255, 0.15);
3442
+ border-radius: 8px;
3443
+ color: var(--text-primary);
3444
+ font-size: 13px;
3445
+ font-family: 'Consolas', 'Monaco', monospace;
3446
+ resize: vertical;
3447
+ margin-bottom: 16px;
3448
+ box-sizing: border-box;
3449
+ }
3450
+
3451
+ [data-theme="light"] .apply-dialog-textarea {
3452
+ background: rgba(0, 0, 0, 0.05);
3453
+ border-color: rgba(0, 0, 0, 0.15);
3454
+ }
3455
+
3456
+ .apply-dialog-textarea:focus {
3457
+ outline: none;
3458
+ border-color: var(--accent);
3459
+ background: rgba(255, 255, 255, 0.08);
3460
+ }
3461
+
3462
+ [data-theme="light"] .apply-dialog-textarea:focus {
3463
+ background: rgba(0, 0, 0, 0.08);
3464
+ }
3465
+
3466
+ .apply-dialog-buttons {
3467
+ display: flex;
3468
+ gap: 12px;
3469
+ justify-content: flex-end;
3470
+ }
3471
+
3472
+ .apply-dialog-btn {
3473
+ padding: 8px 20px;
3474
+ border-radius: 6px;
3475
+ font-size: 13px;
3476
+ cursor: pointer;
3477
+ transition: all 0.2s;
3478
+ border: none;
3479
+ }
3480
+
3481
+ .apply-dialog-btn.primary {
3482
+ background: var(--accent);
3483
+ color: #fff;
3484
+ }
3485
+
3486
+ .apply-dialog-btn.primary:hover {
3487
+ background: var(--accent-hover);
3488
+ transform: translateY(-1px);
3489
+ }
3490
+
3491
+ .apply-dialog-btn.secondary {
3492
+ background: rgba(255, 255, 255, 0.1);
3493
+ color: var(--text-primary);
3494
+ border: 1px solid rgba(255, 255, 255, 0.2);
3495
+ }
3496
+
3497
+ [data-theme="light"] .apply-dialog-btn.secondary {
3498
+ background: rgba(0, 0, 0, 0.1);
3499
+ border-color: rgba(0, 0, 0, 0.2);
3500
+ }
3501
+
3502
+ .apply-dialog-btn.secondary:hover {
3503
+ background: rgba(255, 255, 255, 0.15);
3504
+ }
3505
+
3506
+ [data-theme="light"] .apply-dialog-btn.secondary:hover {
3507
+ background: rgba(0, 0, 0, 0.15);
3508
+ }
3509
+
3510
+ @keyframes fadeIn {
3511
+ from { opacity: 0; }
3512
+ to { opacity: 1; }
3513
+ }
3514
+
3515
+ @keyframes scaleIn {
3516
+ from {
3517
+ opacity: 0;
3518
+ transform: scale(0.9);
3519
+ }
3520
+ to {
3521
+ opacity: 1;
3522
+ transform: scale(1);
3523
+ }
3524
+ }
3525
+
3526
+ #model-list {
3527
+ flex: 1;
3528
+ min-height: 0;
3529
+ overflow-y: auto;
3530
+ padding: 4px;
3531
+ background: transparent;
3532
+ }
3533
+
3534
+ .model-item {
3535
+ padding: 8px 12px;
3536
+ margin: 4px 0;
3537
+ background: rgba(255, 255, 255, 0.06);
3538
+ backdrop-filter: blur(10px);
3539
+ -webkit-backdrop-filter: blur(10px);
3540
+ border: 1px solid rgba(255, 255, 255, 0.12);
3541
+ border-radius: 12px;
3542
+ cursor: pointer;
3543
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
3544
+ border-left: 3px solid transparent;
3545
+ }
3546
+
3547
+ .model-item:hover {
3548
+ background: rgba(255, 255, 255, 0.10);
3549
+ border-color: rgba(255, 255, 255, 0.20);
3550
+ border-left-color: var(--accent);
3551
+ transform: translateX(2px);
3552
+ }
3553
+
3554
+ .model-item.active {
3555
+ background: rgba(30, 142, 62, 0.15);
3556
+ border-color: rgba(30, 142, 62, 0.4);
3557
+ border-left-color: var(--accent);
3558
+ color: var(--accent);
3559
+ box-shadow: 0 2px 12px rgba(30, 142, 62, 0.15);
3560
+ }
3561
+
3562
+ .model-item-name {
3563
+ font-weight: 500;
3564
+ font-size: 12px;
3565
+ margin-bottom: 2px;
3566
+ letter-spacing: -0.1px;
3567
+ }
3568
+
3569
+ .model-item-type {
3570
+ font-size: 10px;
3571
+ color: var(--text-tertiary);
3572
+ text-transform: uppercase;
3573
+ letter-spacing: 0.5px;
3574
+ }
3575
+
3576
+ #up-select {
3577
+ padding: 5px 24px 5px 12px;
3578
+ height: auto;
3579
+ background-color: rgba(255, 255, 255, 0.08);
3580
+ border: none;
3581
+ border-radius: 12px;
3582
+ color: var(--text-primary);
3583
+ font-size: 11px;
3584
+ cursor: pointer;
3585
+ transition: background-color 0.2s, border-color 0.2s;
3586
+ appearance: none;
3587
+ background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L5 5L9 1' stroke='%23aaaaaa' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
3588
+ background-repeat: no-repeat;
3589
+ background-position: right 7px center;
3590
+ }
3591
+
3592
+ #up-select:hover {
3593
+ background-color: rgba(255, 255, 255, 0.12);
3594
+ }
3595
+
3596
+ #up-select:focus {
3597
+ outline: none;
3598
+ background-color: rgba(255, 255, 255, 0.12);
3599
+ background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9 5L5 1L1 5' stroke='%23aaaaaa' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
3600
+ }
3601
+
3602
+ [data-theme="dark"] #up-select option {
3603
+ background-color: #2a2a2a;
3604
+ color: var(--text-primary);
3605
+ padding: 8px 12px;
3606
+ }
3607
+
3608
+ [data-theme="dark"] #up-select option:hover {
3609
+ background-color: rgba(30, 142, 62, 0.15);
3610
+ }
3611
+
3612
+ [data-theme="dark"] #up-select option:checked {
3613
+ background: linear-gradient(135deg, var(--accent) 0%, rgba(30, 142, 62, 0.8) 100%);
3614
+ color: white;
3615
+ }
3616
+
3617
+ [data-theme="light"] #up-select {
3618
+ background-color: rgba(0, 0, 0, 0.04);
3619
+ border: 1px solid rgba(0, 0, 0, 0.1);
3620
+ background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L5 5L9 1' stroke='%23666666' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
3621
+ }
3622
+
3623
+ [data-theme="light"] #up-select:hover {
3624
+ background-color: rgba(0, 0, 0, 0.08);
3625
+ border-color: rgba(0, 0, 0, 0.15);
3626
+ }
3627
+
3628
+ [data-theme="light"] #up-select:focus {
3629
+ background-color: rgba(0, 0, 0, 0.08);
3630
+ border-color: var(--accent);
3631
+ background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9 5L5 1L1 5' stroke='%23666666' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
3632
+ }
3633
+
3634
+ [data-theme="light"] #up-select option {
3635
+ background-color: #ffffff;
3636
+ color: var(--text-primary);
3637
+ padding: 8px 12px;
3638
+ }
3639
+
3640
+ [data-theme="light"] #up-select option:hover {
3641
+ background-color: rgba(0, 132, 255, 0.08);
3642
+ }
3643
+
3644
+ [data-theme="light"] #up-select option:checked {
3645
+ background: linear-gradient(135deg, var(--accent) 0%, rgba(30, 142, 62, 0.8) 100%);
3646
+ color: white;
3647
+ }
3648
+
3649
+ .empty-state {
3650
+ color: var(--text-tertiary);
3651
+ text-align: center;
3652
+ padding: 16px;
3653
+ font-size: 12px;
3654
+ opacity: 0.6;
3655
+ }
3656
+
3657
+ /* Empty state in joint controls should be centered */
3658
+ #joint-controls .empty-state {
3659
+ display: flex;
3660
+ align-items: center;
3661
+ justify-content: center;
3662
+ width: 100%;
3663
+ height: 100%;
3664
+ min-height: 100px;
3665
+ }
3666
+
3667
+ /* Code editor panel */
3668
+ #code-editor-panel {
3669
+ top: calc(80px + 340px + 20px);
3670
+ left: calc(100vw - 360px - 20px);
3671
+ width: 360px;
3672
+ height: calc(100vh - 80px - 340px - 40px);
3673
+ display: none;
3674
+ transition: all 0.4s var(--spring);
3675
+ }
3676
+
3677
+ #code-editor-panel.visible {
3678
+ display: flex;
3679
+ }
3680
+
3681
+ #code-editor-panel.maximized {
3682
+ top: 80px !important;
3683
+ left: 20px !important;
3684
+ width: calc(100vw - 40px) !important;
3685
+ height: calc(100vh - 100px) !important;
3686
+ }
3687
+
3688
+ .code-editor-header {
3689
+ padding: 16px;
3690
+ padding-right: 78px;
3691
+ border-bottom: 0.5px solid var(--glass-border);
3692
+ display: flex;
3693
+ justify-content: space-between;
3694
+ align-items: center;
3695
+ font-weight: 600;
3696
+ font-size: 13px;
3697
+ color: var(--text-primary);
3698
+ letter-spacing: -0.2px;
3699
+ user-select: none;
3700
+ cursor: move;
3701
+ flex-shrink: 0;
3702
+ gap: 12px;
3703
+ position: relative;
3704
+ z-index: 10;
3705
+ }
3706
+
3707
+ .code-editor-title {
3708
+ flex-shrink: 0;
3709
+ }
3710
+
3711
+ .code-editor-title > span:first-child::after {
3712
+ content: none;
3713
+ }
3714
+
3715
+ .code-editor-info-bar {
3716
+ padding: 6px 12px;
3717
+ font-size: 10px;
3718
+ color: var(--text-tertiary);
3719
+ background: rgba(255, 255, 255, 0.04);
3720
+ border-bottom: 0.5px solid var(--glass-border);
3721
+ text-align: center;
3722
+ user-select: none;
3723
+ display: flex;
3724
+ align-items: center;
3725
+ justify-content: center;
3726
+ gap: 8px;
3727
+ flex-wrap: wrap;
3728
+ }
3729
+
3730
+ .code-editor-filename-input {
3731
+ padding: 4px 8px;
3732
+ background: rgba(255, 255, 255, 0.06);
3733
+ border: 1px solid rgba(255, 255, 255, 0.15);
3734
+ border-radius: 6px;
3735
+ color: var(--text-primary);
3736
+ font-size: 10px;
3737
+ font-weight: 500;
3738
+ min-width: 120px;
3739
+ transition: all 0.2s;
3740
+ }
3741
+
3742
+ .code-editor-filename-input:focus {
3743
+ outline: none;
3744
+ border-color: var(--accent);
3745
+ background: rgba(255, 255, 255, 0.08);
3746
+ }
3747
+
3748
+ .code-editor-filename-input::placeholder {
3749
+ color: var(--text-tertiary);
3750
+ }
3751
+
3752
+ [data-theme="light"] .code-editor-header {
3753
+ background: rgba(0, 0, 0, 0.03);
3754
+ border-bottom-color: rgba(0, 0, 0, 0.08);
3755
+ }
3756
+
3757
+ [data-theme="light"] .code-editor-info-bar {
3758
+ background: rgba(0, 0, 0, 0.03);
3759
+ border-bottom-color: rgba(0, 0, 0, 0.08);
3760
+ }
3761
+
3762
+ [data-theme="light"] .code-editor-filename-input {
3763
+ background: rgba(0, 0, 0, 0.04);
3764
+ border-color: rgba(0, 0, 0, 0.1);
3765
+ }
3766
+
3767
+ [data-theme="light"] .code-editor-filename-input:focus {
3768
+ background: rgba(0, 0, 0, 0.06);
3769
+ }
3770
+
3771
+
3772
+ .code-editor-filename {
3773
+ color: var(--accent);
3774
+ font-size: 10px;
3775
+ font-weight: 500;
3776
+ overflow: hidden;
3777
+ text-overflow: ellipsis;
3778
+ white-space: nowrap;
3779
+ max-width: 200px;
3780
+ }
3781
+
3782
+ .code-editor-save-status {
3783
+ font-size: 10px;
3784
+ font-weight: 500;
3785
+ padding: 2px 8px;
3786
+ border-radius: 6px;
3787
+ white-space: nowrap;
3788
+ flex-shrink: 0;
3789
+ transition: all 0.3s var(--spring);
3790
+ }
3791
+
3792
+ .code-editor-save-status.saved {
3793
+ color: #34c759;
3794
+ background: rgba(52, 199, 89, 0.15);
3795
+ border: 1px solid rgba(52, 199, 89, 0.3);
3796
+ }
3797
+
3798
+ .code-editor-save-status.unsaved {
3799
+ color: #ff453a;
3800
+ background: rgba(255, 69, 58, 0.15);
3801
+ border: 1px solid rgba(255, 69, 58, 0.3);
3802
+ }
3803
+
3804
+ .code-editor-save-status.hidden {
3805
+ display: none;
3806
+ }
3807
+
3808
+ .code-editor-actions {
3809
+ display: flex;
3810
+ gap: 8px;
3811
+ flex-shrink: 0;
3812
+ }
3813
+
3814
+ .code-editor-btn {
3815
+ padding: 5px 12px;
3816
+ height: auto;
3817
+ background: transparent;
3818
+ border: 1px solid rgba(255, 255, 255, 0.1);
3819
+ border-radius: 12px;
3820
+ color: var(--text-secondary);
3821
+ font-size: 11px;
3822
+ font-weight: 500;
3823
+ cursor: pointer;
3824
+ transition: all 0.3s var(--spring);
3825
+ white-space: nowrap;
3826
+ flex-shrink: 0;
3827
+ }
3828
+
3829
+ .code-editor-btn:hover {
3830
+ background: rgba(255, 255, 255, 0.1);
3831
+ color: var(--text-primary);
3832
+ transform: scale(1.05);
3833
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
3834
+ }
3835
+
3836
+ .code-editor-btn:active {
3837
+ transform: scale(0.98);
3838
+ }
3839
+
3840
+ .code-editor-btn.primary {
3841
+ background: var(--accent);
3842
+ color: white;
3843
+ border-color: var(--accent);
3844
+ box-shadow: 0 1px 4px rgba(30, 142, 62, 0.3);
3845
+ }
3846
+
3847
+ .code-editor-btn.primary:hover {
3848
+ box-shadow: 0 4px 16px rgba(30, 142, 62, 0.4);
3849
+ transform: scale(1.05);
3850
+ }
3851
+
3852
+ .code-editor-btn.primary:active {
3853
+ transform: scale(0.98);
3854
+ }
3855
+
3856
+ [data-theme="light"] .code-editor-btn {
3857
+ border-color: rgba(0, 0, 0, 0.1);
3858
+ }
3859
+
3860
+ [data-theme="light"] .code-editor-btn:hover {
3861
+ background: rgba(0, 0, 0, 0.05);
3862
+ }
3863
+
3864
+ #code-editor-content {
3865
+ flex: 1;
3866
+ overflow: hidden;
3867
+ position: relative;
3868
+ border-radius: 0 0 16px 16px;
3869
+ padding: 12px;
3870
+ display: flex;
3871
+ flex-direction: column;
3872
+ }
3873
+
3874
+ #code-editor-wrapper {
3875
+ position: relative;
3876
+ width: 100%;
3877
+ height: 100%;
3878
+ border-radius: 8px;
3879
+ overflow: hidden;
3880
+ background: rgba(255, 255, 255, 0.02);
3881
+ }
3882
+
3883
+ [data-theme="light"] #code-editor-content {
3884
+ background: rgba(0, 0, 0, 0.02);
3885
+ }
3886
+
3887
+ [data-theme="light"] #code-editor-wrapper {
3888
+ background: rgba(0, 0, 0, 0.04);
3889
+ border: 1px solid rgba(0, 0, 0, 0.08);
3890
+ }
3891
+
3892
+ [data-theme="light"] .cm-editor {
3893
+ background: rgba(0, 0, 0, 0.02) !important;
3894
+ }
3895
+
3896
+ [data-theme="light"] .cm-gutters {
3897
+ background: rgba(0, 0, 0, 0.04) !important;
3898
+ border-right: 1px solid rgba(0, 0, 0, 0.08) !important;
3899
+ }
3900
+
3901
+ [data-theme="light"] .cm-activeLineGutter {
3902
+ background: rgba(0, 132, 255, 0.1) !important;
3903
+ }
3904
+
3905
+ [data-theme="light"] .cm-activeLine {
3906
+ background: rgba(0, 132, 255, 0.05) !important;
3907
+ }
3908
+
3909
+ [data-theme="light"] .cm-lineNumbers .cm-gutterElement {
3910
+ color: rgba(0, 0, 0, 0.4) !important;
3911
+ }
3912
+
3913
+ [data-theme="light"] .cm-content {
3914
+ caret-color: var(--text-primary) !important;
3915
+ }
3916
+
3917
+ .error-details {
3918
+ background: rgba(255, 107, 107, 0.1);
3919
+ border: 1px solid #ff6b6b;
3920
+ border-radius: 12px;
3921
+ padding: 12px;
3922
+ margin-top: 12px;
3923
+ font-size: 12px;
3924
+ color: #ff6b6b;
3925
+ max-height: 200px;
3926
+ overflow-y: auto;
3927
+ font-family: monospace;
3928
+ }
3929
+
3930
+ #copyright-watermark {
3931
+ position: fixed;
3932
+ bottom: 5px;
3933
+ left: 50%;
3934
+ transform: translateX(-50%);
3935
+ font-size: 10px;
3936
+ color: var(--text-tertiary);
3937
+ opacity: 0.5;
3938
+ z-index: 999;
3939
+ pointer-events: none;
3940
+ user-select: none;
3941
+ font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', sans-serif;
3942
+ letter-spacing: 0.3px;
3943
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
3944
+ text-align: center;
3945
+ }
3946
+
3947
+ #bottom-help-bar {
3948
+ position: fixed;
3949
+ bottom: 20px;
3950
+ left: 50%;
3951
+ transform: translateX(-50%);
3952
+ display: flex;
3953
+ align-items: center;
3954
+ gap: 12px;
3955
+ padding: 6px 12px;
3956
+ background: var(--glass-bg);
3957
+ backdrop-filter: blur(20px) saturate(150%);
3958
+ -webkit-backdrop-filter: blur(20px) saturate(150%);
3959
+ border: 0.5px solid var(--glass-border);
3960
+ border-radius: 12px;
3961
+ box-shadow: var(--glass-shadow);
3962
+ font-size: 11px;
3963
+ color: var(--text-secondary);
3964
+ z-index: 998;
3965
+ pointer-events: none;
3966
+ user-select: none;
3967
+ opacity: 0.8;
3968
+ transition: opacity 0.3s ease;
3969
+ }
3970
+
3971
+ #bottom-help-bar:hover {
3972
+ opacity: 1;
3973
+ }
3974
+
3975
+ .bottom-help-item {
3976
+ display: flex;
3977
+ align-items: center;
3978
+ gap: 4px;
3979
+ white-space: nowrap;
3980
+ }
3981
+
3982
+ .bottom-help-key {
3983
+ font-weight: 600;
3984
+ color: var(--accent);
3985
+ }
3986
+
3987
+ .bottom-help-separator {
3988
+ width: 1px;
3989
+ height: 12px;
3990
+ background: var(--border-color);
3991
+ opacity: 0.3;
3992
+ }
3993
+
3994
+ /* ROS Connection Panel Styles */
3995
+ .ros-connection-panel {
3996
+ padding: 16px;
3997
+ }
3998
+
3999
+ .ros-connection-panel .panel-header {
4000
+ font-weight: 600;
4001
+ margin-bottom: 12px;
4002
+ color: var(--text-primary);
4003
+ }
4004
+
4005
+ .ros-connection-panel .panel-content {
4006
+ display: flex;
4007
+ flex-direction: column;
4008
+ gap: 12px;
4009
+ }
4010
+
4011
+ .ros-connection-panel .form-group {
4012
+ display: flex;
4013
+ flex-direction: column;
4014
+ gap: 6px;
4015
+ }
4016
+
4017
+ .ros-connection-panel .form-group label {
4018
+ font-size: 12px;
4019
+ color: var(--text-secondary);
4020
+ }
4021
+
4022
+ .ros-connection-panel .form-control {
4023
+ padding: 8px;
4024
+ border: 1px solid var(--border-color);
4025
+ border-radius: 6px;
4026
+ background: var(--bg-secondary);
4027
+ color: var(--text-primary);
4028
+ font-size: 13px;
4029
+ }
4030
+
4031
+ .ros-connection-panel .btn {
4032
+ padding: 8px 16px;
4033
+ border: none;
4034
+ border-radius: 6px;
4035
+ cursor: pointer;
4036
+ font-size: 13px;
4037
+ transition: all 0.2s;
4038
+ }
4039
+
4040
+ .ros-connection-panel .btn-primary {
4041
+ background: var(--accent);
4042
+ color: white;
4043
+ }
4044
+
4045
+ .ros-connection-panel .btn-primary:hover {
4046
+ opacity: 0.9;
4047
+ }
4048
+
4049
+ .ros-connection-panel .btn-secondary {
4050
+ background: var(--bg-tertiary);
4051
+ color: var(--text-primary);
4052
+ }
4053
+
4054
+ .ros-connection-panel .btn:disabled {
4055
+ opacity: 0.5;
4056
+ cursor: not-allowed;
4057
+ }
4058
+
4059
+ .ros-connection-panel .status-indicator {
4060
+ font-size: 12px;
4061
+ margin-left: 8px;
4062
+ }
4063
+
4064
+ .ros-connection-panel hr {
4065
+ border: none;
4066
+ border-top: 1px solid var(--border-color);
4067
+ margin: 8px 0;
4068
+ }
4069
+
4070
+ </style>
4071
+ <!-- MATOMO_PLACEHOLDER -->
4072
+ <script type="module" crossorigin src="./assets/js/index-56e94ce5.js"></script>
4073
+ <link rel="modulepreload" crossorigin href="./assets/js/three-9c891de0.js">
4074
+ <link rel="modulepreload" crossorigin href="./assets/js/d3-2e308866.js">
4075
+ <link rel="modulepreload" crossorigin href="./assets/js/codemirror-c3cec7b3.js">
4076
+ <link rel="modulepreload" crossorigin href="./assets/js/codemirror-themes-e6995a6a.js">
4077
+ </head>
4078
+ <body>
4079
+ <div id="container">
4080
+ <div id="canvas-container">
4081
+ <div id="top-control-bar">
4082
+ <div class="control-bar-section">
4083
+ <button class="control-button active" id="show-visual" data-checked="true">
4084
+ <span data-i18n="visual"></span>
4085
+ </button>
4086
+ <button class="control-button" id="show-collision" data-checked="false">
4087
+ <span data-i18n="collision"></span>
4088
+ </button>
4089
+ <button class="control-button" id="show-inertia" data-checked="false">
4090
+ <span data-i18n="inertia"></span>
4091
+ </button>
4092
+ <button class="control-button" id="show-com" data-checked="false">
4093
+ <span data-i18n="com"></span>
4094
+ </button>
4095
+ <button class="control-button" id="toggle-axes-btn" data-checked="false">
4096
+ <span data-i18n="axes"></span>
4097
+ </button>
4098
+ <button class="control-button" id="toggle-joint-axes-btn" data-checked="false">
4099
+ <span data-i18n="jointAxes"></span>
4100
+ </button>
4101
+ <button class="control-button active" id="toggle-shadow" data-checked="true">
4102
+ <span data-i18n="shadow"></span>
4103
+ </button>
4104
+ <button class="control-button active" id="toggle-lighting" data-checked="true">
4105
+ <span data-i18n="lighting"></span>
4106
+ </button>
4107
+ </div>
4108
+
4109
+ <div class="control-bar-divider"></div>
4110
+
4111
+ <div class="control-bar-section">
4112
+ <select id="up-select" class="control-bar-select">
4113
+ <option value="+Z">+Z</option>
4114
+ <option value="-Z">-Z</option>
4115
+ <option value="+Y">+Y</option>
4116
+ <option value="-Y">-Y</option>
4117
+ <option value="+X">+X</option>
4118
+ <option value="-X">-X</option>
4119
+ </select>
4120
+ </div>
4121
+
4122
+ <div class="control-bar-divider"></div>
4123
+
4124
+ <div class="control-bar-section">
4125
+ <button class="tool-button active" id="toggle-files-panel">
4126
+ <span class="tool-button-text" data-i18n="files"></span>
4127
+ </button>
4128
+ <button class="tool-button active" id="toggle-joints-panel">
4129
+ <span class="tool-button-text" data-i18n="joints"></span>
4130
+ </button>
4131
+ <button class="tool-button" id="toggle-model-tree">
4132
+ <span class="tool-button-text" data-i18n="structure"></span>
4133
+ </button>
4134
+ <button class="tool-button" id="toggle-ros-panel">
4135
+ <span class="tool-button-text">ROS</span>
4136
+ </button>
4137
+ <button class="tool-button" id="toggle-virtual-camera-panel">
4138
+ <span class="tool-button-text" data-i18n="camera">相机</span>
4139
+ </button>
4140
+ <button class="tool-button" id="toggle-orientation-panel">
4141
+ <span class="tool-button-text" data-i18n="base">基座</span>
4142
+ </button>
4143
+ <button class="tool-button" id="open-editor-btn">
4144
+ <span class="tool-button-text" data-i18n="edit"></span>
4145
+ </button>
4146
+ </div>
4147
+
4148
+ <div class="control-bar-divider"></div>
4149
+
4150
+ <div class="control-bar-section">
4151
+ <button class="tool-button" id="toggle-joint-info-panel">
4152
+ <span class="tool-button-text" data-i18n="jointInfo"></span>
4153
+ </button>
4154
+ <button class="tool-button" id="toggle-mocap-panel">
4155
+ <span class="tool-button-text" data-i18n="mocapControl"></span>
4156
+ </button>
4157
+ <button class="tool-button" id="help-button">
4158
+ <span class="tool-button-text" data-i18n="help"></span>
4159
+ </button>
4160
+ <button class="tool-button" id="language-toggle">
4161
+ <span class="tool-button-text" data-i18n="language"></span>
4162
+ </button>
4163
+ <button class="tool-button" id="theme-toggle">
4164
+ <span class="tool-button-icon">🌙</span>
4165
+ </button>
4166
+ </div>
4167
+ </div>
4168
+
4169
+ <div id="mujoco-simulation-bar" style="display:none;">
4170
+ <button id="mujoco-reset-btn-bar">
4171
+ <span data-i18n="mujocoReset"></span>
4172
+ </button>
4173
+ <button id="mujoco-simulate-btn-bar">
4174
+ <span data-i18n="mujocoSimulate"></span>
4175
+ </button>
4176
+ </div>
4177
+
4178
+ <canvas id="canvas"></canvas>
4179
+
4180
+ <div id="floating-files-panel" class="floating-panel">
4181
+ <div class="floating-panel-header">
4182
+ <span data-i18n="fileList"></span>
4183
+ <button class="panel-maximize-btn" data-panel-id="floating-files-panel">⛶</button>
4184
+ <button class="panel-close-btn" data-panel="floating-files-panel">✕</button>
4185
+ </div>
4186
+ <div class="floating-panel-content" id="model-list">
4187
+ <div class="empty-state" data-i18n="noFolder"></div>
4188
+ </div>
4189
+ </div>
4190
+
4191
+ <div id="floating-joints-panel" class="floating-panel">
4192
+ <div class="floating-panel-header">
4193
+ <span data-i18n="jointControl"></span>
4194
+ <div class="joints-panel-controls">
4195
+ <div id="unit-toggle">
4196
+ <button id="unit-rad" class="active" data-i18n="radian"></button>
4197
+ <button id="unit-deg" data-i18n="degree"></button>
4198
+ </div>
4199
+ <button class="control-button" id="reset-joints-btn">
4200
+ <span data-i18n="reset"></span>
4201
+ </button>
4202
+ <button class="control-button active" id="ignore-limits" data-checked="true">
4203
+ <span data-i18n="limits"></span>
4204
+ </button>
4205
+ </div>
4206
+ <button class="panel-maximize-btn" data-panel-id="floating-joints-panel">⛶</button>
4207
+ <button class="panel-close-btn" data-panel="floating-joints-panel">✕</button>
4208
+ </div>
4209
+ <div class="joints-batch-toolbar">
4210
+ <button class="batch-btn select-all-btn" id="select-all-btn" data-i18n="selectAllJoints">全选</button>
4211
+ <button class="batch-btn show-mimic-btn" id="show-mimic-btn">
4212
+ <span data-i18n="showMimicJoints">显示mimic</span>
4213
+ </button>
4214
+ <button class="batch-btn apply-angles-btn" disabled>
4215
+ <span data-i18n="applyAngles">应用</span>
4216
+ </button>
4217
+ <button class="batch-btn set-home-btn" disabled>
4218
+ <span data-i18n="setHome">设零位</span>
4219
+ </button>
4220
+ <button class="batch-btn reset-home-btn" disabled>
4221
+ <span data-i18n="resetToHome">回零</span>
4222
+ </button>
4223
+ </div>
4224
+ <div class="floating-panel-content" id="joint-controls">
4225
+ <div class="empty-state" data-i18n="noModel"></div>
4226
+ </div>
4227
+ </div>
4228
+
4229
+ <div id="floating-model-tree" class="floating-panel">
4230
+ <div class="floating-panel-header">
4231
+ <span data-i18n="modelStructure"></span>
4232
+ <button class="panel-maximize-btn" data-panel-id="floating-model-tree">⛶</button>
4233
+ <button class="panel-close-btn" data-panel="floating-model-tree">✕</button>
4234
+ </div>
4235
+ <div class="graph-controls-hint" data-i18n="graphHint"></div>
4236
+ <div class="model-graph-content">
4237
+ <div class="model-graph-wrapper">
4238
+ <svg id="model-graph-svg">
4239
+ <defs>
4240
+ <marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
4241
+ <polygon points="0 0, 10 3, 0 6" fill="#888" />
4242
+ </marker>
4243
+ </defs>
4244
+ </svg>
4245
+ <div id="graph-empty-state" class="empty-state" data-i18n="noModel"></div>
4246
+ </div>
4247
+ </div>
4248
+ </div>
4249
+
4250
+ <div id="drop-zone">
4251
+ <div id="drop-text">
4252
+ <strong data-i18n="dropHint"></strong><br>
4253
+ <span style="font-size: 12px; margin-top: 8px; display: block;" data-i18n="dropHintSub"></span>
4254
+ </div>
4255
+ </div>
4256
+
4257
+ <div id="hover-info">
4258
+ <div id="hover-link-name"></div>
4259
+ <div id="hover-joint-name"></div>
4260
+ <div id="hover-link-mass"></div>
4261
+ <div id="hover-link-pose"></div>
4262
+ <div id="hover-merged-links"></div>
4263
+ </div>
4264
+
4265
+ <div id="floating-help-panel" class="floating-panel" style="display: none;">
4266
+ <div class="floating-panel-header">
4267
+ <span data-i18n="helpTitle">Robot Viewer v1.1.2</span>
4268
+ <button class="panel-close-btn" data-panel="floating-help-panel">✕</button>
4269
+ </div>
4270
+ <div class="floating-panel-content">
4271
+ <div class="help-section">
4272
+ <div class="help-section-title" data-i18n="about"></div>
4273
+ <div class="help-section-content">
4274
+ <p data-i18n="aboutContent"></p>
4275
+ </div>
4276
+ </div>
4277
+
4278
+ <div class="help-section">
4279
+ <div class="help-section-title" data-i18n="projectHome"></div>
4280
+ <div class="help-section-content">
4281
+ <p><a href="https://git.io-ai.tech/io_public/io_robot_viewer" target="_blank" rel="noopener" style="color:var(--accent-color)">https://git.io-ai.tech/io_public/io_robot_viewer</a></p>
4282
+ </div>
4283
+ </div>
4284
+
4285
+ <!-- SUPPORT_SECTION_PLACEHOLDER -->
4286
+ </div>
4287
+ </div>
4288
+
4289
+ <div id="code-editor-panel" class="floating-panel">
4290
+ <div class="code-editor-header">
4291
+ <div class="code-editor-title">
4292
+ <span data-i18n="codeEditor"></span>
4293
+ </div>
4294
+ <div class="code-editor-actions">
4295
+ <button class="code-editor-btn" id="reload-btn" data-i18n="reload"></button>
4296
+ <button class="code-editor-btn primary" id="save-btn" data-i18n="download"></button>
4297
+ </div>
4298
+ <button class="panel-maximize-btn" data-panel-id="code-editor-panel">⛶</button>
4299
+ <button class="panel-close-btn" id="close-editor-btn">✕</button>
4300
+ </div>
4301
+ <div class="code-editor-info-bar">
4302
+ <input type="text" class="code-editor-filename-input" id="editor-filename-input" placeholder="newfile.xml" />
4303
+ <span class="code-editor-filename" id="editor-filename" data-i18n="noFileOpen"></span>
4304
+ <span class="code-editor-save-status saved" id="editor-save-status" data-i18n="saved"></span>
4305
+ </div>
4306
+ <div id="code-editor-content">
4307
+ <div id="code-editor-wrapper" style="height: 100%;"></div>
4308
+ </div>
4309
+ </div>
4310
+ </div>
4311
+ </div>
4312
+
4313
+ <!-- Mocap Control Panel -->
4314
+ <div id="mocap-control-panel" class="floating-panel" style="display: none;">
4315
+ <div class="floating-panel-header">
4316
+ <span data-i18n="mocapControl">动捕控制</span>
4317
+ <button class="panel-close-btn" data-panel="mocap-control-panel">✕</button>
4318
+ </div>
4319
+ <div class="panel-content" style="padding: 16px;">
4320
+ <div class="mocap-model-item">
4321
+ <div class="mocap-model-header">
4322
+ <span data-i18n="mocapHuman">动捕小人</span>
4323
+ </div>
4324
+ <button class="mocap-load-btn" id="mocap-human-load" data-i18n="load">加载</button>
4325
+ </div>
4326
+ <div class="mocap-model-item">
4327
+ <div class="mocap-model-header">
4328
+ <span data-i18n="exoskeleton">外骨骼手套</span>
4329
+ </div>
4330
+ <div style="display:flex;gap:6px;">
4331
+ <button class="mocap-load-btn" id="exoskeleton-load-v1" style="flex:1" data-i18n="mocapExoskeletonV1">v1.0版</button>
4332
+ <button class="mocap-load-btn" id="exoskeleton-load-v2" style="flex:1" data-i18n="mocapExoskeletonV2">v2.0版</button>
4333
+ </div>
4334
+ </div>
4335
+ </div>
4336
+ </div>
4337
+
4338
+ <!-- Joint Info Panel -->
4339
+ <div id="joint-info-panel"></div>
4340
+
4341
+ <div id="bottom-help-bar">
4342
+ <div class="bottom-help-item">
4343
+ <span class="bottom-help-key" data-i18n="leftDrag"></span>
4344
+ <span data-i18n="rotateView"></span>
4345
+ </div>
4346
+ <div class="bottom-help-separator"></div>
4347
+ <div class="bottom-help-item">
4348
+ <span class="bottom-help-key" data-i18n="rightDrag"></span>
4349
+ <span data-i18n="panView"></span>
4350
+ </div>
4351
+ <div class="bottom-help-separator"></div>
4352
+ <div class="bottom-help-item">
4353
+ <span class="bottom-help-key" data-i18n="scroll"></span>
4354
+ <span data-i18n="zoom"></span>
4355
+ </div>
4356
+ <div class="bottom-help-separator"></div>
4357
+ <div class="bottom-help-item">
4358
+ <span class="bottom-help-key" data-i18n="clickModel"></span>
4359
+ <span data-i18n="controlJoint"></span>
4360
+ </div>
4361
+ <div class="bottom-help-separator"></div>
4362
+ <div class="bottom-help-item">
4363
+ <span class="bottom-help-key" data-i18n="dragFile"></span>
4364
+ <span data-i18n="loadModel"></span>
4365
+ </div>
4366
+ </div>
4367
+
4368
+
4369
+
4370
+ <!-- Plot Mode Container (?plot=1) -->
4371
+ <div id="plot-container" style="display:none; flex-direction:row; position:fixed; inset:0; z-index:100; background:var(--bg-primary);"></div>
4372
+ </body>
4373
+ </html>