pynode-flow 0.2.0__py3-none-any.whl

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 (227) hide show
  1. pynode/__init__.py +37 -0
  2. pynode/__main__.py +9 -0
  3. pynode/_perf.py +65 -0
  4. pynode/_version.py +24 -0
  5. pynode/api/__init__.py +15 -0
  6. pynode/api/helpers.py +79 -0
  7. pynode/api/nodes.py +423 -0
  8. pynode/api/services.py +124 -0
  9. pynode/api/sse.py +58 -0
  10. pynode/api/uploads.py +59 -0
  11. pynode/api/workflows.py +466 -0
  12. pynode/config.py +93 -0
  13. pynode/install_nodes.py +143 -0
  14. pynode/main.py +118 -0
  15. pynode/node_registry.py +156 -0
  16. pynode/nodes/AutoSyncNode/auto_sync_node.py +151 -0
  17. pynode/nodes/BBoxMetricsNode/__init__.py +3 -0
  18. pynode/nodes/BBoxMetricsNode/bbox_metrics_node.py +250 -0
  19. pynode/nodes/BBoxMetricsNode/requirements.txt +1 -0
  20. pynode/nodes/BatchNode/__init__.py +3 -0
  21. pynode/nodes/BatchNode/batch_node.py +86 -0
  22. pynode/nodes/BatchNode/requirements.txt +1 -0
  23. pynode/nodes/CameraNode/__init__.py +4 -0
  24. pynode/nodes/CameraNode/camera_node.py +204 -0
  25. pynode/nodes/CameraNode/requirements.txt +2 -0
  26. pynode/nodes/ChangeNode/__init__.py +1 -0
  27. pynode/nodes/ChangeNode/change_node.py +255 -0
  28. pynode/nodes/ChangeNode/requirements.txt +1 -0
  29. pynode/nodes/ConfidenceFilterNode/__init__.py +4 -0
  30. pynode/nodes/ConfidenceFilterNode/confidence_filter_node.py +208 -0
  31. pynode/nodes/ConfidenceFilterNode/requirements.txt +1 -0
  32. pynode/nodes/CounterNode/__init__.py +3 -0
  33. pynode/nodes/CounterNode/counter_node.py +191 -0
  34. pynode/nodes/CropNode/__init__.py +4 -0
  35. pynode/nodes/CropNode/crop_node.py +317 -0
  36. pynode/nodes/CropNode/requirements.txt +2 -0
  37. pynode/nodes/DebugNode/__init__.py +4 -0
  38. pynode/nodes/DebugNode/debug_node.py +169 -0
  39. pynode/nodes/DebugNode/requirements.txt +1 -0
  40. pynode/nodes/DelayNode/delay_node.py +218 -0
  41. pynode/nodes/DelayNode/requirements.txt +1 -0
  42. pynode/nodes/DrawPredictionsNode/__init__.py +3 -0
  43. pynode/nodes/DrawPredictionsNode/draw_predictions_node.py +365 -0
  44. pynode/nodes/DrawPredictionsNode/requirements.txt +2 -0
  45. pynode/nodes/ErrorNode/error_node.py +94 -0
  46. pynode/nodes/ErrorNode/requirements.txt +1 -0
  47. pynode/nodes/FilterNode/__init__.py +3 -0
  48. pynode/nodes/FilterNode/filter_node.py +102 -0
  49. pynode/nodes/FilterNode/requirements.txt +1 -0
  50. pynode/nodes/FrameSourceNode/__init__.py +4 -0
  51. pynode/nodes/FrameSourceNode/frame_source_node.py +319 -0
  52. pynode/nodes/FrameSourceNode/requirements.txt +1 -0
  53. pynode/nodes/FunctionNode/__init__.py +3 -0
  54. pynode/nodes/FunctionNode/function_node.py +168 -0
  55. pynode/nodes/FunctionNode/requirements.txt +1 -0
  56. pynode/nodes/GateNode/gate_node.py +60 -0
  57. pynode/nodes/GateNode/requirements.txt +1 -0
  58. pynode/nodes/GetiUploadNode/__init__.py +3 -0
  59. pynode/nodes/GetiUploadNode/geti_upload_node.py +278 -0
  60. pynode/nodes/GetiUploadNode/requirements.txt +1 -0
  61. pynode/nodes/ImageFormatNode/__init__.py +4 -0
  62. pynode/nodes/ImageFormatNode/image_format_node.py +310 -0
  63. pynode/nodes/ImageFormatNode/requirements.txt +2 -0
  64. pynode/nodes/ImageUploadNode/image_upload_node.py +96 -0
  65. pynode/nodes/ImageViewerNode/__init__.py +4 -0
  66. pynode/nodes/ImageViewerNode/image_viewer_node.py +162 -0
  67. pynode/nodes/ImageViewerNode/requirements.txt +2 -0
  68. pynode/nodes/ImageWriterNode/__init__.py +3 -0
  69. pynode/nodes/ImageWriterNode/imagewriter_node.py +352 -0
  70. pynode/nodes/ImageWriterNode/requirements.txt +1 -0
  71. pynode/nodes/InferenceNode/InferenceEngine/__init__.py +15 -0
  72. pynode/nodes/InferenceNode/InferenceEngine/device_detection.py +149 -0
  73. pynode/nodes/InferenceNode/InferenceEngine/engines/base_engine.py +155 -0
  74. pynode/nodes/InferenceNode/InferenceEngine/engines/example_engine_template.py +272 -0
  75. pynode/nodes/InferenceNode/InferenceEngine/engines/geti_engine.py +309 -0
  76. pynode/nodes/InferenceNode/InferenceEngine/engines/onnx_engine.py +346 -0
  77. pynode/nodes/InferenceNode/InferenceEngine/engines/pass_engine.py +86 -0
  78. pynode/nodes/InferenceNode/InferenceEngine/engines/ultralytics_engine.py +863 -0
  79. pynode/nodes/InferenceNode/InferenceEngine/inference_engine_factory.py +533 -0
  80. pynode/nodes/InferenceNode/InferenceEngine/readme.md +107 -0
  81. pynode/nodes/InferenceNode/InferenceEngine/result_converters.py +121 -0
  82. pynode/nodes/InferenceNode/inference_node.py +451 -0
  83. pynode/nodes/InferenceNode/requirements.txt +13 -0
  84. pynode/nodes/InjectNode/inject_node.py +245 -0
  85. pynode/nodes/InjectNode/requirements.txt +1 -0
  86. pynode/nodes/JoinNode/__init__.py +3 -0
  87. pynode/nodes/JoinNode/join_node.py +156 -0
  88. pynode/nodes/JoinNode/requirements.txt +1 -0
  89. pynode/nodes/LabelFilterNode/__init__.py +3 -0
  90. pynode/nodes/LabelFilterNode/label_filter_node.py +188 -0
  91. pynode/nodes/LabelFilterNode/requirements.txt +1 -0
  92. pynode/nodes/MQTTNode/__init__.py +8 -0
  93. pynode/nodes/MQTTNode/mqtt_in_node.py +148 -0
  94. pynode/nodes/MQTTNode/mqtt_out_node.py +157 -0
  95. pynode/nodes/MQTTNode/mqtt_service.py +367 -0
  96. pynode/nodes/MQTTNode/requirements.txt +1 -0
  97. pynode/nodes/MessageWriterNode/__init__.py +7 -0
  98. pynode/nodes/MessageWriterNode/messagereader_node.py +568 -0
  99. pynode/nodes/MessageWriterNode/messagewriter_node.py +505 -0
  100. pynode/nodes/OmronCameraNode/__init__.py +3 -0
  101. pynode/nodes/OmronCameraNode/omron_camera_node.py +652 -0
  102. pynode/nodes/OmronCameraNode/requirements.txt +4 -0
  103. pynode/nodes/OpenCV/__init__.py +1 -0
  104. pynode/nodes/OpenCV/arithmetic_node.py +376 -0
  105. pynode/nodes/OpenCV/bitwise_node.py +127 -0
  106. pynode/nodes/OpenCV/blend_node.py +127 -0
  107. pynode/nodes/OpenCV/blob_detector_node.py +233 -0
  108. pynode/nodes/OpenCV/blur_node.py +141 -0
  109. pynode/nodes/OpenCV/color_space_node.py +139 -0
  110. pynode/nodes/OpenCV/colormap_node.py +262 -0
  111. pynode/nodes/OpenCV/contours_node.py +258 -0
  112. pynode/nodes/OpenCV/denormalize_coords_node.py +365 -0
  113. pynode/nodes/OpenCV/draw_node.py +312 -0
  114. pynode/nodes/OpenCV/edge_detector_node.py +177 -0
  115. pynode/nodes/OpenCV/equirectangular_node.py +447 -0
  116. pynode/nodes/OpenCV/fft_node.py +102 -0
  117. pynode/nodes/OpenCV/focus_estimation_node.py +189 -0
  118. pynode/nodes/OpenCV/frequency_filter_node.py +241 -0
  119. pynode/nodes/OpenCV/histogram_node.py +160 -0
  120. pynode/nodes/OpenCV/hough_circles_node.py +224 -0
  121. pynode/nodes/OpenCV/hough_lines_node.py +243 -0
  122. pynode/nodes/OpenCV/in_range_node.py +186 -0
  123. pynode/nodes/OpenCV/morphology_node.py +136 -0
  124. pynode/nodes/OpenCV/normalize_coords_node.py +344 -0
  125. pynode/nodes/OpenCV/paste_node.py +244 -0
  126. pynode/nodes/OpenCV/perspective_node.py +174 -0
  127. pynode/nodes/OpenCV/realsense_depth_node.py +253 -0
  128. pynode/nodes/OpenCV/requirements.txt +2 -0
  129. pynode/nodes/OpenCV/resize_node.py +175 -0
  130. pynode/nodes/OpenCV/rotate_node.py +159 -0
  131. pynode/nodes/OpenCV/template_match_node.py +227 -0
  132. pynode/nodes/OpenCV/threshold_node.py +167 -0
  133. pynode/nodes/PointInShapeNode/__init__.py +3 -0
  134. pynode/nodes/PointInShapeNode/point_in_shape_node.py +313 -0
  135. pynode/nodes/PointInShapeNode/requirements.txt +2 -0
  136. pynode/nodes/PolygonMetricsNode/__init__.py +3 -0
  137. pynode/nodes/PolygonMetricsNode/polygon_metrics_node.py +331 -0
  138. pynode/nodes/PolygonMetricsNode/requirements.txt +2 -0
  139. pynode/nodes/QueueLengthProbeNode/__init__.py +3 -0
  140. pynode/nodes/QueueLengthProbeNode/queue_length_probe_node.py +116 -0
  141. pynode/nodes/Qwen3VLMNode/__init__.py +3 -0
  142. pynode/nodes/Qwen3VLMNode/qwen3_vlm_node.py +519 -0
  143. pynode/nodes/Qwen3VLMNode/requirements.txt +5 -0
  144. pynode/nodes/RESTEndpointNode/__init__.py +3 -0
  145. pynode/nodes/RESTEndpointNode/rest_endpoint_node.py +491 -0
  146. pynode/nodes/RangeNode/__init__.py +3 -0
  147. pynode/nodes/RangeNode/range_node.py +115 -0
  148. pynode/nodes/RangeNode/requirements.txt +1 -0
  149. pynode/nodes/RateProbeNode/__init__.py +3 -0
  150. pynode/nodes/RateProbeNode/rate_probe_node.py +183 -0
  151. pynode/nodes/RateProbeNode/requirements.txt +1 -0
  152. pynode/nodes/RoboflowUploadNode/__init__.py +3 -0
  153. pynode/nodes/RoboflowUploadNode/requirements.txt +1 -0
  154. pynode/nodes/RoboflowUploadNode/roboflow_upload_node.py +229 -0
  155. pynode/nodes/SliceNode/README.md +149 -0
  156. pynode/nodes/SliceNode/__init__.py +5 -0
  157. pynode/nodes/SliceNode/merge_predictions_node.py +444 -0
  158. pynode/nodes/SliceNode/requirements.txt +3 -0
  159. pynode/nodes/SliceNode/slice_collector_node.py +370 -0
  160. pynode/nodes/SliceNode/slice_image_node.py +380 -0
  161. pynode/nodes/SplitNode/__init__.py +3 -0
  162. pynode/nodes/SplitNode/requirements.txt +1 -0
  163. pynode/nodes/SplitNode/split_node.py +155 -0
  164. pynode/nodes/SwitchNode/requirements.txt +1 -0
  165. pynode/nodes/SwitchNode/switch_node.py +259 -0
  166. pynode/nodes/SyncNode/__init__.py +3 -0
  167. pynode/nodes/SyncNode/sync_node.py +287 -0
  168. pynode/nodes/TemplateNode/requirements.txt +1 -0
  169. pynode/nodes/TemplateNode/template_node.py +86 -0
  170. pynode/nodes/TrackerNode/__init__.py +2 -0
  171. pynode/nodes/TrackerNode/supervision_tracker_node.py +248 -0
  172. pynode/nodes/TrackerNode/tracker_node.py +745 -0
  173. pynode/nodes/UltralyticsNode/__init__.py +4 -0
  174. pynode/nodes/UltralyticsNode/requirements.txt +2 -0
  175. pynode/nodes/UltralyticsNode/ultralytics_node.py +364 -0
  176. pynode/nodes/UnknownNode/__init__.py +93 -0
  177. pynode/nodes/VLLMNode/__init__.py +3 -0
  178. pynode/nodes/VLLMNode/vllm_node.py +345 -0
  179. pynode/nodes/VideoReaderNode/__init__.py +3 -0
  180. pynode/nodes/VideoReaderNode/video_reader_node.py +425 -0
  181. pynode/nodes/VideoWriterNode/__init__.py +3 -0
  182. pynode/nodes/VideoWriterNode/video_writer_node.py +441 -0
  183. pynode/nodes/WebhookNode/__init__.py +3 -0
  184. pynode/nodes/WebhookNode/webhook_node.py +591 -0
  185. pynode/nodes/__init__.py +109 -0
  186. pynode/nodes/base_node.py +562 -0
  187. pynode/nodes/image_utils.py +291 -0
  188. pynode/nodes/info.py +109 -0
  189. pynode/nodes/mDNSNode/__init__.py +13 -0
  190. pynode/nodes/mDNSNode/mdns_broadcast_node.py +214 -0
  191. pynode/nodes/mDNSNode/mdns_discovery_node.py +362 -0
  192. pynode/nodes/mDNSNode/mdns_manager.py +399 -0
  193. pynode/nodes/mDNSNode/requirements.txt +1 -0
  194. pynode/nodes/messages.py +81 -0
  195. pynode/server.py +187 -0
  196. pynode/static/images/favicon4.png +0 -0
  197. pynode/static/index.html +200 -0
  198. pynode/static/js/README.md +139 -0
  199. pynode/static/js/auth.js +89 -0
  200. pynode/static/js/clipboard.js +152 -0
  201. pynode/static/js/config.js +14 -0
  202. pynode/static/js/connections.js +844 -0
  203. pynode/static/js/debug.js +524 -0
  204. pynode/static/js/events.js +775 -0
  205. pynode/static/js/history.js +199 -0
  206. pynode/static/js/main.js +55 -0
  207. pynode/static/js/minimap.js +242 -0
  208. pynode/static/js/nodes.js +995 -0
  209. pynode/static/js/palette.js +255 -0
  210. pynode/static/js/properties.js +1270 -0
  211. pynode/static/js/selection.js +241 -0
  212. pynode/static/js/state.js +211 -0
  213. pynode/static/js/ui-utils.js +28 -0
  214. pynode/static/js/viewport.js +204 -0
  215. pynode/static/js/workflow.js +469 -0
  216. pynode/static/js/workflows.js +344 -0
  217. pynode/static/style.css +2751 -0
  218. pynode/workflow_engine.py +483 -0
  219. pynode/workflow_manager.py +440 -0
  220. pynode_flow-0.2.0.dist-info/METADATA +520 -0
  221. pynode_flow-0.2.0.dist-info/RECORD +227 -0
  222. pynode_flow-0.2.0.dist-info/WHEEL +5 -0
  223. pynode_flow-0.2.0.dist-info/entry_points.txt +3 -0
  224. pynode_flow-0.2.0.dist-info/licenses/LICENSE +21 -0
  225. pynode_flow-0.2.0.dist-info/scm_file_list.json +270 -0
  226. pynode_flow-0.2.0.dist-info/scm_version.json +8 -0
  227. pynode_flow-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2751 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
9
+ background: #1e1e1e;
10
+ color: #d4d4d4;
11
+ overflow: hidden;
12
+ }
13
+
14
+ .container {
15
+ display: flex;
16
+ flex-direction: column;
17
+ height: 100vh;
18
+ }
19
+
20
+ /* Toast notification */
21
+ .toast {
22
+ position: fixed;
23
+ top: 20px;
24
+ left: 50%;
25
+ transform: translateX(-50%);
26
+ background: #0e639c;
27
+ color: white;
28
+ padding: 12px 24px;
29
+ border-radius: 4px;
30
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
31
+ z-index: 10000;
32
+ opacity: 0;
33
+ transition: opacity 0.3s ease;
34
+ pointer-events: none;
35
+ }
36
+
37
+ .toast.show {
38
+ opacity: 1;
39
+ }
40
+
41
+ .toast.toast-error {
42
+ background: #c42b1c;
43
+ }
44
+
45
+ /* Header */
46
+ .header {
47
+ background: #252526;
48
+ padding: 5px 20px;
49
+ display: flex;
50
+ justify-content: space-between;
51
+ align-items: center;
52
+ border-bottom: 1px solid #3e3e42;
53
+ }
54
+
55
+ .header h1 {
56
+ font-size: 20px;
57
+ font-weight: 600;
58
+ display: flex;
59
+ align-items: center;
60
+ gap: 10px;
61
+ }
62
+
63
+ .header-logo {
64
+ height: 48px;
65
+ width: 48px;
66
+ vertical-align: middle;
67
+ }
68
+
69
+ .app-version {
70
+ font-size: 11px;
71
+ font-weight: 400;
72
+ color: #858585;
73
+ align-self: flex-end;
74
+ margin-bottom: 3px;
75
+ }
76
+
77
+ .header-actions {
78
+ display: flex;
79
+ gap: 10px;
80
+ align-items: center;
81
+ }
82
+
83
+ .menu-container {
84
+ position: relative;
85
+ }
86
+
87
+ .menu-dropdown {
88
+ position: absolute;
89
+ top: 100%;
90
+ right: 0;
91
+ margin-top: 5px;
92
+ background: #252526;
93
+ border: 1px solid #3e3e42;
94
+ border-radius: 4px;
95
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
96
+ min-width: 180px;
97
+ z-index: 1000;
98
+ }
99
+
100
+ .menu-dropdown.hidden {
101
+ display: none;
102
+ }
103
+
104
+ .menu-item {
105
+ display: block;
106
+ width: 100%;
107
+ padding: 10px 15px;
108
+ background: transparent;
109
+ border: none;
110
+ color: #d4d4d4;
111
+ text-align: left;
112
+ cursor: pointer;
113
+ font-size: 13px;
114
+ transition: background 0.2s;
115
+ }
116
+
117
+ .menu-item:hover {
118
+ background: #2a2d2e;
119
+ }
120
+
121
+ .menu-item:first-child {
122
+ border-radius: 4px 4px 0 0;
123
+ }
124
+
125
+ .menu-item:last-child {
126
+ border-radius: 0 0 4px 4px;
127
+ }
128
+
129
+ /* Submenu popout for Export */
130
+ .menu-submenu {
131
+ position: relative;
132
+ }
133
+
134
+ .submenu-dropdown {
135
+ position: absolute;
136
+ top: 0;
137
+ /* Pop out to the left by default to avoid going off-screen on the right */
138
+ right: 100%;
139
+ left: auto;
140
+ margin-right: 6px;
141
+ background: #252526;
142
+ border: 1px solid #3e3e42;
143
+ border-radius: 4px;
144
+ box-shadow: 0 6px 18px rgba(0,0,0,0.35);
145
+ min-width: 200px;
146
+ z-index: 1100;
147
+ }
148
+
149
+ .submenu-dropdown.hidden {
150
+ display: none;
151
+ }
152
+
153
+ /* Show submenu on hover or when focused for accessibility */
154
+ .menu-submenu:hover > .submenu-dropdown,
155
+ .menu-submenu:focus-within > .submenu-dropdown {
156
+ display: block;
157
+ }
158
+
159
+ /* Allow main menu dropdown to show overflow so submenu is visible */
160
+ .menu-dropdown {
161
+ overflow: visible;
162
+ }
163
+
164
+ /* Buttons */
165
+ .btn {
166
+ padding: 8px 16px;
167
+ border: none;
168
+ border-radius: 4px;
169
+ cursor: pointer;
170
+ font-size: 14px;
171
+ font-weight: 500;
172
+ transition: background 0.2s;
173
+ }
174
+
175
+ .btn-primary {
176
+ background: #0e639c;
177
+ color: white;
178
+ }
179
+
180
+ .btn-primary:hover {
181
+ background: #1177bb;
182
+ }
183
+
184
+ .btn-primary:disabled {
185
+ background: #3e3e42;
186
+ color: #808080;
187
+ cursor: not-allowed;
188
+ }
189
+
190
+ .btn-primary:disabled:hover {
191
+ background: #3e3e42;
192
+ }
193
+
194
+ /* Deploy button with dropdown */
195
+ .deploy-container {
196
+ position: relative;
197
+ display: flex;
198
+ }
199
+
200
+ .deploy-main {
201
+ border-radius: 4px 0 0 4px;
202
+ border-right: 1px solid rgba(0, 0, 0, 0.2);
203
+ display: flex;
204
+ align-items: center;
205
+ gap: 6px;
206
+ }
207
+
208
+ .deploy-btn-icon {
209
+ font-size: 12px;
210
+ }
211
+
212
+ .deploy-arrow {
213
+ border-radius: 0 4px 4px 0;
214
+ padding: 8px 6px;
215
+ min-width: 28px;
216
+ }
217
+
218
+ .deploy-arrow:disabled {
219
+ background: #3e3e42;
220
+ color: #808080;
221
+ cursor: not-allowed;
222
+ }
223
+
224
+ /* Deploy button while workflow processing is transiently stopped */
225
+ .deploy-main.deploy-stopped {
226
+ background: #a1260d;
227
+ }
228
+
229
+ .deploy-main.deploy-stopped:hover {
230
+ background: #c0331a;
231
+ }
232
+
233
+ .deploy-dropdown {
234
+ position: absolute;
235
+ top: 100%;
236
+ right: 0;
237
+ margin-top: 4px;
238
+ background: #2d2d30;
239
+ border: 1px solid #454545;
240
+ border-radius: 4px;
241
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
242
+ z-index: 1000;
243
+ min-width: 280px;
244
+ overflow: hidden;
245
+ }
246
+
247
+ .deploy-dropdown.hidden {
248
+ display: none;
249
+ }
250
+
251
+ .deploy-option {
252
+ display: flex;
253
+ align-items: flex-start;
254
+ gap: 12px;
255
+ padding: 10px 14px;
256
+ width: 100%;
257
+ text-align: left;
258
+ background: transparent;
259
+ border: none;
260
+ color: #d4d4d4;
261
+ cursor: pointer;
262
+ transition: background 0.15s;
263
+ }
264
+
265
+ .deploy-option:hover {
266
+ background: #094771;
267
+ }
268
+
269
+ .deploy-option.active {
270
+ background: #0e639c;
271
+ }
272
+
273
+ .deploy-option .deploy-icon {
274
+ font-size: 16px;
275
+ width: 20px;
276
+ text-align: center;
277
+ margin-top: 2px;
278
+ }
279
+
280
+ .deploy-option .deploy-text {
281
+ display: flex;
282
+ flex-direction: column;
283
+ gap: 2px;
284
+ }
285
+
286
+ .deploy-option .deploy-label {
287
+ font-weight: 500;
288
+ font-size: 13px;
289
+ }
290
+
291
+ .deploy-option .deploy-desc {
292
+ font-size: 11px;
293
+ color: #a0a0a0;
294
+ }
295
+
296
+ .deploy-option.active .deploy-desc {
297
+ color: #c0c0c0;
298
+ }
299
+
300
+ .btn-success {
301
+ background: #0e8a16;
302
+ color: white;
303
+ }
304
+
305
+ .btn-success:hover {
306
+ background: #0fa319;
307
+ }
308
+
309
+ .btn-danger {
310
+ background: #d73a49;
311
+ color: white;
312
+ }
313
+
314
+ .btn-danger:hover {
315
+ background: #e74856;
316
+ }
317
+
318
+ .btn-secondary {
319
+ background: #3e3e42;
320
+ color: #d4d4d4;
321
+ }
322
+
323
+ .btn-secondary:hover {
324
+ background: #4e4e52;
325
+ }
326
+
327
+ .btn-small {
328
+ padding: 4px 8px;
329
+ font-size: 12px;
330
+ }
331
+
332
+ /* Main content */
333
+ .main-content {
334
+ display: flex;
335
+ flex: 1;
336
+ overflow: hidden;
337
+ }
338
+
339
+ /* Node Palette */
340
+ .palette {
341
+ width: 250px;
342
+ background: #252526;
343
+ border-right: 1px solid #3e3e42;
344
+ overflow: visible;
345
+ padding: 0;
346
+ position: relative;
347
+ z-index: 200;
348
+ }
349
+
350
+ .palette-scroll {
351
+ height: 100%;
352
+ overflow-y: auto;
353
+ padding: 15px;
354
+ }
355
+
356
+ .palette.collapsed {
357
+ width: 18px;
358
+ padding: 0;
359
+ overflow: visible;
360
+ }
361
+
362
+ .palette.collapsed > :not(.panel-grab) {
363
+ display: none;
364
+ }
365
+
366
+ .palette h3 {
367
+ font-size: 14px;
368
+ margin-bottom: 15px;
369
+ color: #cccccc;
370
+ }
371
+
372
+ .node-list {
373
+ display: flex;
374
+ flex-direction: column;
375
+ gap: 8px;
376
+ }
377
+
378
+ .palette-category {
379
+ margin-bottom: 8px;
380
+ }
381
+
382
+ .palette-category-header {
383
+ font-size: 12px;
384
+ font-weight: 600;
385
+ color: #888;
386
+ text-transform: uppercase;
387
+ margin-bottom: 0;
388
+ padding: 4px 0;
389
+ border-bottom: 1px solid #3e3e42;
390
+ cursor: pointer;
391
+ display: flex;
392
+ align-items: center;
393
+ gap: 6px;
394
+ user-select: none;
395
+ }
396
+
397
+ .palette-category-header:hover {
398
+ color: #aaa;
399
+ }
400
+
401
+ .palette-category-arrow {
402
+ font-size: 10px;
403
+ width: 12px;
404
+ text-align: center;
405
+ transition: transform 0.2s ease;
406
+ }
407
+
408
+ .palette-category-arrow.collapsed {
409
+ transform: rotate(-90deg);
410
+ }
411
+
412
+ .palette-category-count {
413
+ margin-left: auto;
414
+ font-size: 10px;
415
+ color: #666;
416
+ background: #2d2d30;
417
+ padding: 1px 6px;
418
+ border-radius: 8px;
419
+ }
420
+
421
+ .palette-category-list {
422
+ display: flex;
423
+ flex-direction: column;
424
+ gap: 6px;
425
+ overflow: hidden;
426
+ transition: max-height 0.25s ease-out, opacity 0.2s ease-out, padding 0.25s ease-out;
427
+ padding-top: 6px;
428
+ }
429
+
430
+ .palette-category-list.collapsed {
431
+ max-height: 0 !important;
432
+ opacity: 0;
433
+ padding-top: 0;
434
+ }
435
+
436
+ .palette-node {
437
+ background: #3e3e42;
438
+ padding: 0;
439
+ border-radius: 6px;
440
+ cursor: grab;
441
+ font-size: 13px;
442
+ border: 2px solid #555;
443
+ transition: background 0.2s;
444
+ display: flex;
445
+ align-items: stretch;
446
+ min-height: 30px;
447
+ overflow: visible;
448
+ position: relative;
449
+ }
450
+
451
+ .palette-node:hover {
452
+ background: #4e4e52;
453
+ border-color: #0e639c;
454
+ }
455
+
456
+ .palette-node:active {
457
+ cursor: grabbing;
458
+ }
459
+
460
+ .palette-node-icon {
461
+ background: rgba(0, 0, 0, 0.2);
462
+ display: flex;
463
+ align-items: center;
464
+ justify-content: center;
465
+ width: 30px;
466
+ flex-shrink: 0;
467
+ font-size: 16px;
468
+ line-height: 1;
469
+ }
470
+
471
+ .palette-node-name {
472
+ flex: 1;
473
+ min-width: 0;
474
+ display: flex;
475
+ align-items: center;
476
+ justify-content: center;
477
+ padding: 4px 10px;
478
+ line-height: 1.2;
479
+ font-weight: 500;
480
+ text-align: center;
481
+ white-space: normal;
482
+ word-break: break-word;
483
+ overflow-wrap: anywhere;
484
+ }
485
+
486
+ .palette-node-ports {
487
+ position: absolute;
488
+ top: 50%;
489
+ left: 0;
490
+ right: 0;
491
+ transform: translateY(-50%);
492
+ display: flex;
493
+ justify-content: space-between;
494
+ align-items: center;
495
+ pointer-events: none;
496
+ }
497
+
498
+ .palette-port {
499
+ width: 8px;
500
+ height: 8px;
501
+ border-radius: 50%;
502
+ background: #555;
503
+ border: 1px solid #888;
504
+ position: relative;
505
+ }
506
+
507
+ .palette-port.input {
508
+ margin-left: -5px;
509
+ margin-right: auto;
510
+ }
511
+
512
+ .palette-port.output {
513
+ margin-right: -5px;
514
+ margin-left: auto;
515
+ }
516
+
517
+ /* Canvas area (tabs + canvas) */
518
+ .canvas-area {
519
+ flex: 1;
520
+ display: flex;
521
+ flex-direction: column;
522
+ overflow: hidden;
523
+ min-width: 0;
524
+ position: relative; /* anchor for .canvas-controls overlay */
525
+ }
526
+
527
+ /* Workflow Tabs */
528
+ .workflow-tabs {
529
+ display: flex;
530
+ align-items: stretch;
531
+ background: #252526;
532
+ border-bottom: 1px solid #3e3e42;
533
+ height: 35px;
534
+ min-height: 35px;
535
+ overflow-x: auto;
536
+ overflow-y: hidden;
537
+ scrollbar-width: none;
538
+ }
539
+
540
+ .workflow-tabs::-webkit-scrollbar {
541
+ display: none;
542
+ }
543
+
544
+ .workflow-tab {
545
+ display: flex;
546
+ align-items: center;
547
+ gap: 6px;
548
+ padding: 0 14px;
549
+ background: transparent;
550
+ border: none;
551
+ border-right: 1px solid #3e3e42;
552
+ color: #969696;
553
+ font-size: 13px;
554
+ cursor: pointer;
555
+ white-space: nowrap;
556
+ transition: background 0.15s, color 0.15s;
557
+ position: relative;
558
+ min-width: 0;
559
+ }
560
+
561
+ .workflow-tab:hover {
562
+ background: #2a2d2e;
563
+ color: #d4d4d4;
564
+ }
565
+
566
+ .workflow-tab.active {
567
+ background: #1e1e1e;
568
+ color: #ffffff;
569
+ border-bottom: 2px solid #0e639c;
570
+ }
571
+
572
+ .workflow-tab.disabled {
573
+ color: #555;
574
+ font-style: italic;
575
+ }
576
+
577
+ .workflow-tab .tab-name {
578
+ overflow: hidden;
579
+ text-overflow: ellipsis;
580
+ max-width: 150px;
581
+ }
582
+
583
+ .workflow-tab .tab-name[contenteditable="true"] {
584
+ outline: 1px solid #0e639c;
585
+ background: #1e1e1e;
586
+ padding: 1px 4px;
587
+ border-radius: 2px;
588
+ max-width: 200px;
589
+ cursor: text;
590
+ }
591
+
592
+ .workflow-tab .tab-close {
593
+ display: none;
594
+ font-size: 14px;
595
+ line-height: 1;
596
+ opacity: 0.5;
597
+ padding: 0 2px;
598
+ border: none;
599
+ background: none;
600
+ color: inherit;
601
+ cursor: pointer;
602
+ }
603
+
604
+ .workflow-tab .tab-close:hover {
605
+ opacity: 1;
606
+ background: rgba(255, 255, 255, 0.1);
607
+ border-radius: 3px;
608
+ }
609
+
610
+ .workflow-tab:hover .tab-close,
611
+ .workflow-tab.active .tab-close {
612
+ display: inline;
613
+ }
614
+
615
+ .workflow-tab-add {
616
+ display: flex;
617
+ align-items: center;
618
+ justify-content: center;
619
+ width: 30px;
620
+ min-width: 30px;
621
+ background: transparent;
622
+ border: none;
623
+ border-right: 1px solid #3e3e42;
624
+ color: #969696;
625
+ font-size: 18px;
626
+ cursor: pointer;
627
+ transition: background 0.15s, color 0.15s;
628
+ }
629
+
630
+ .workflow-tab-add:hover {
631
+ background: #2a2d2e;
632
+ color: #d4d4d4;
633
+ }
634
+
635
+ /* Canvas */
636
+ .canvas-container {
637
+ flex: 1;
638
+ position: relative;
639
+ overflow: auto;
640
+ background: #1e1e1e;
641
+ background-image:
642
+ linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px),
643
+ linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
644
+ background-size: 20px 20px;
645
+ background-attachment: local;
646
+ background-position: 0 0;
647
+ user-select: none;
648
+ -webkit-user-select: none;
649
+ -moz-user-select: none;
650
+ -ms-user-select: none;
651
+ }
652
+
653
+ .canvas-container::-webkit-scrollbar {
654
+ width: 12px;
655
+ height: 12px;
656
+ }
657
+
658
+ .canvas-container::-webkit-scrollbar-track {
659
+ background: #1e1e1e;
660
+ }
661
+
662
+ .canvas-container::-webkit-scrollbar-thumb {
663
+ background: #424242;
664
+ border-radius: 6px;
665
+ }
666
+
667
+ .canvas-container::-webkit-scrollbar-thumb:hover {
668
+ background: #555;
669
+ }
670
+
671
+ .canvas-container::-webkit-scrollbar-corner {
672
+ background: #1e1e1e;
673
+ }
674
+
675
+ /* Zoom root: viewport.js sets transform: scale(k) here. Both canvas layers
676
+ (connections SVG + HTML nodes) scale together; the scrollable overflow of
677
+ .canvas-container follows the transformed size, so native scroll panning
678
+ keeps working at every zoom level. */
679
+ #canvas-viewport {
680
+ position: absolute;
681
+ top: 0;
682
+ left: 0;
683
+ width: 5000px;
684
+ height: 5000px;
685
+ transform-origin: 0 0;
686
+ }
687
+
688
+ #canvas {
689
+ position: absolute;
690
+ top: 0;
691
+ left: 0;
692
+ width: 5000px;
693
+ height: 5000px;
694
+ pointer-events: none;
695
+ }
696
+
697
+ #canvas g#connections {
698
+ pointer-events: auto;
699
+ }
700
+
701
+ #canvas g#temp-line {
702
+ pointer-events: none;
703
+ }
704
+
705
+ #nodes-container {
706
+ position: absolute;
707
+ top: 0;
708
+ left: 0;
709
+ width: 5000px;
710
+ height: 5000px;
711
+ z-index: 1;
712
+ }
713
+
714
+ /* Canvas overlay controls: minimap + zoom (lower-right of canvas area) */
715
+ .canvas-controls {
716
+ position: absolute;
717
+ right: 18px;
718
+ bottom: 18px;
719
+ z-index: 50;
720
+ display: flex;
721
+ flex-direction: column;
722
+ align-items: flex-end;
723
+ gap: 6px;
724
+ pointer-events: none; /* let clicks pass except on the widgets */
725
+ }
726
+
727
+ .canvas-controls > * {
728
+ pointer-events: auto;
729
+ }
730
+
731
+ .minimap-panel {
732
+ background: #252526;
733
+ border: 1px solid #3e3e42;
734
+ border-radius: 6px;
735
+ padding: 4px;
736
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
737
+ }
738
+
739
+ .minimap-panel.hidden {
740
+ display: none;
741
+ }
742
+
743
+ #minimap-canvas {
744
+ display: block;
745
+ border-radius: 3px;
746
+ cursor: pointer;
747
+ }
748
+
749
+ .canvas-controls-bar {
750
+ display: flex;
751
+ align-items: center;
752
+ gap: 2px;
753
+ background: #252526;
754
+ border: 1px solid #3e3e42;
755
+ border-radius: 6px;
756
+ padding: 2px;
757
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
758
+ }
759
+
760
+ .canvas-ctrl-btn {
761
+ background: transparent;
762
+ border: none;
763
+ color: #d4d4d4;
764
+ min-width: 26px;
765
+ height: 24px;
766
+ padding: 0 6px;
767
+ border-radius: 4px;
768
+ font-size: 14px;
769
+ line-height: 1;
770
+ cursor: pointer;
771
+ }
772
+
773
+ .canvas-ctrl-btn:hover {
774
+ background: #3e3e42;
775
+ }
776
+
777
+ .canvas-ctrl-btn.active {
778
+ color: #0098ff;
779
+ }
780
+
781
+ .canvas-ctrl-btn.zoom-readout {
782
+ font-size: 11px;
783
+ min-width: 42px;
784
+ }
785
+
786
+ /* Nodes */
787
+ .node {
788
+ position: absolute;
789
+ background: #2d2d30;
790
+ border: 2px solid #555;
791
+ border-radius: 8px;
792
+ padding: 0;
793
+ min-width: 120px;
794
+ min-height: 30px;
795
+ cursor: move;
796
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
797
+ user-select: none;
798
+ -webkit-user-select: none;
799
+ -moz-user-select: none;
800
+ -ms-user-select: none;
801
+ overflow: visible;
802
+ display: flex;
803
+ flex-direction: column;
804
+ }
805
+
806
+ .node:hover {
807
+ border-color: #0e639c;
808
+ }
809
+
810
+ .node.selected {
811
+ border-color: #0098ff;
812
+ box-shadow: 0 0 0 4px rgba(0, 152, 255, 0.5), 0 0 16px rgba(0, 152, 255, 0.7);
813
+ }
814
+
815
+ .node.disabled {
816
+ opacity: 0.5;
817
+ }
818
+
819
+ .node.unknown-node {
820
+ border-style: dashed;
821
+ border-width: 2px;
822
+ background: repeating-linear-gradient(
823
+ 45deg,
824
+ #3d3d3d,
825
+ #3d3d3d 10px,
826
+ #353535 10px,
827
+ #353535 20px
828
+ );
829
+ }
830
+
831
+ .node.unknown-node:hover {
832
+ border-color: #ff6b6b;
833
+ }
834
+
835
+ .node.unknown-node .node-title {
836
+ font-style: italic;
837
+ }
838
+
839
+ .node-modified-indicator {
840
+ position: absolute;
841
+ top: -7px;
842
+ right: -7px;
843
+ width: 15px;
844
+ height: 15px;
845
+ background: #0098ff;
846
+ border: 2px solid #1e1e1e;
847
+ border-radius: 50%;
848
+ display: none;
849
+ }
850
+
851
+ .node.modified .node-modified-indicator {
852
+ display: block;
853
+ }
854
+
855
+ .image-viewer-container {
856
+ margin-top: 0;
857
+ background: #000;
858
+ border: 1px solid #555;
859
+ border-radius: 0 0 6px 6px;
860
+ overflow: hidden;
861
+ display: flex;
862
+ align-items: center;
863
+ justify-content: center;
864
+ position: relative;
865
+ }
866
+
867
+ /* Fix ImageViewerNode title bar height and input port position */
868
+ .image-viewer-node .node-content {
869
+ flex: 0 0 30px; /* Fixed height, don't grow */
870
+ }
871
+
872
+ .image-viewer-node .node-ports {
873
+ top: 0 !important;
874
+ transform: none !important;
875
+ }
876
+
877
+ .image-viewer-node .node-ports .port.input {
878
+ top: 10px; /* Port center at 15px (10 + half of 10px port height) */
879
+ position: relative;
880
+ }
881
+
882
+ .image-viewer-frame {
883
+ max-width: 100%;
884
+ max-height: 100%;
885
+ object-fit: contain;
886
+ display: block;
887
+ }
888
+
889
+ .image-viewer-resize-handle {
890
+ position: absolute;
891
+ bottom: 0;
892
+ right: 0;
893
+ width: 16px;
894
+ height: 16px;
895
+ cursor: nwse-resize;
896
+ background: linear-gradient(135deg, transparent 0%, transparent 50%, #666 50%, #666 100%);
897
+ border-bottom-right-radius: 3px;
898
+ opacity: 0.6;
899
+ transition: opacity 0.2s;
900
+ }
901
+
902
+ .image-viewer-resize-handle:hover {
903
+ opacity: 1;
904
+ background: linear-gradient(135deg, transparent 0%, transparent 50%, #888 50%, #888 100%);
905
+ }
906
+
907
+ /* Inject button for InjectNode */
908
+ .inject-btn {
909
+ width: 22px;
910
+ height: 22px;
911
+ border: 1px solid rgba(0, 0, 0, 0.3);
912
+ border-radius: 3px;
913
+ background: rgba(255, 255, 255, 0.25);
914
+ color: inherit;
915
+ cursor: pointer;
916
+ display: flex;
917
+ align-items: center;
918
+ justify-content: center;
919
+ font-size: 10px;
920
+ margin-left: 4px;
921
+ margin-right: 6px;
922
+ transition: all 0.15s;
923
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
924
+ }
925
+
926
+ .inject-btn:hover {
927
+ background: rgba(255, 255, 255, 0.5);
928
+ border-color: rgba(0, 0, 0, 0.4);
929
+ transform: scale(1.1);
930
+ }
931
+
932
+ .inject-btn:active {
933
+ background: rgba(255, 255, 255, 0.7);
934
+ transform: scale(0.95);
935
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
936
+ }
937
+
938
+ /* Rate display for RateProbeNode */
939
+ .rate-display {
940
+ font-size: 11px;
941
+ font-weight: 600;
942
+ color: #000;
943
+ background: rgba(255, 255, 255, 0.3);
944
+ padding: 2px 6px;
945
+ border-radius: 3px;
946
+ margin-left: 8px;
947
+ margin-right: 8px;
948
+ min-width: 40px;
949
+ text-align: center;
950
+ }
951
+
952
+ /* Queue length display for QueueLengthProbeNode */
953
+ .queue-length-display {
954
+ font-size: 11px;
955
+ font-weight: 600;
956
+ color: #000;
957
+ background: rgba(255, 255, 255, 0.3);
958
+ padding: 2px 6px;
959
+ border-radius: 3px;
960
+ margin-left: 8px;
961
+ margin-right: 8px;
962
+ min-width: 40px;
963
+ text-align: center;
964
+ }
965
+
966
+ /* Counter display for CounterNode */
967
+ .counter-display {
968
+ font-size: 11px;
969
+ font-weight: 600;
970
+ color: #000;
971
+ background: rgba(255, 255, 255, 0.3);
972
+ padding: 2px 6px;
973
+ border-radius: 3px;
974
+ margin-left: 8px;
975
+ margin-right: 8px;
976
+ min-width: 40px;
977
+ text-align: center;
978
+ }
979
+
980
+ /* Transport controls (VideoReaderNode) */
981
+ .transport-controls {
982
+ display: flex;
983
+ align-items: center;
984
+ gap: 2px;
985
+ margin-left: 6px;
986
+ margin-right: 6px;
987
+ }
988
+
989
+ .transport-btn {
990
+ width: 20px;
991
+ height: 20px;
992
+ border: 1px solid rgba(0, 0, 0, 0.3);
993
+ border-radius: 3px;
994
+ background: rgba(255, 255, 255, 0.25);
995
+ color: inherit;
996
+ cursor: pointer;
997
+ display: flex;
998
+ align-items: center;
999
+ justify-content: center;
1000
+ font-size: 10px;
1001
+ padding: 0;
1002
+ transition: all 0.15s;
1003
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
1004
+ }
1005
+
1006
+ .transport-btn:hover {
1007
+ background: rgba(255, 255, 255, 0.5);
1008
+ border-color: rgba(0, 0, 0, 0.4);
1009
+ }
1010
+
1011
+ .transport-btn:active {
1012
+ background: rgba(255, 255, 255, 0.7);
1013
+ transform: scale(0.95);
1014
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
1015
+ }
1016
+
1017
+ .transport-pos {
1018
+ font-size: 10px;
1019
+ font-weight: 600;
1020
+ font-family: 'Consolas', 'SF Mono', 'Menlo', 'Courier New', monospace;
1021
+ font-variant-numeric: tabular-nums;
1022
+ color: #000;
1023
+ background: rgba(255, 255, 255, 0.3);
1024
+ padding: 2px 4px;
1025
+ border-radius: 3px;
1026
+ margin-left: 4px;
1027
+ min-width: 32px;
1028
+ text-align: center;
1029
+ white-space: nowrap;
1030
+ }
1031
+
1032
+ /* Full-width video progress / scrub slider (VideoReaderNode) */
1033
+ .transport-progress {
1034
+ -webkit-appearance: none;
1035
+ appearance: none;
1036
+ display: block;
1037
+ width: 100%;
1038
+ height: 6px;
1039
+ margin: 0;
1040
+ padding: 0;
1041
+ background: rgba(0, 0, 0, 0.35);
1042
+ border-bottom-left-radius: 6px;
1043
+ border-bottom-right-radius: 6px;
1044
+ cursor: pointer;
1045
+ }
1046
+
1047
+ /* Square off the node-content bottom corners when a progress bar follows it */
1048
+ .node-content:has(+ .transport-progress) {
1049
+ border-bottom-left-radius: 0;
1050
+ border-bottom-right-radius: 0;
1051
+ }
1052
+
1053
+ .transport-progress::-webkit-slider-thumb {
1054
+ -webkit-appearance: none;
1055
+ appearance: none;
1056
+ width: 12px;
1057
+ height: 12px;
1058
+ border-radius: 50%;
1059
+ background: #fff;
1060
+ border: 1px solid rgba(0, 0, 0, 0.5);
1061
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
1062
+ cursor: grab;
1063
+ }
1064
+
1065
+ .transport-progress::-webkit-slider-thumb:active {
1066
+ cursor: grabbing;
1067
+ }
1068
+
1069
+ .transport-progress::-moz-range-thumb {
1070
+ width: 12px;
1071
+ height: 12px;
1072
+ border-radius: 50%;
1073
+ background: #fff;
1074
+ border: 1px solid rgba(0, 0, 0, 0.5);
1075
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
1076
+ cursor: grab;
1077
+ }
1078
+
1079
+ .transport-progress::-moz-range-track {
1080
+ height: 6px;
1081
+ background: transparent;
1082
+ }
1083
+
1084
+ /* Gate control toggle switch */
1085
+ .gate-switch {
1086
+ position: relative;
1087
+ display: inline-flex;
1088
+ align-items: center;
1089
+ align-self: center;
1090
+ width: 32px;
1091
+ height: 18px;
1092
+ cursor: pointer;
1093
+ margin-left: 8px;
1094
+ margin-right: 8px;
1095
+ flex-shrink: 0;
1096
+ }
1097
+
1098
+ .gate-switch input {
1099
+ opacity: 0;
1100
+ width: 0;
1101
+ height: 0;
1102
+ }
1103
+
1104
+ .gate-slider {
1105
+ position: absolute;
1106
+ top: 0;
1107
+ left: 0;
1108
+ right: 0;
1109
+ bottom: 0;
1110
+ background-color: #555;
1111
+ transition: 0.3s;
1112
+ border-radius: 18px;
1113
+ }
1114
+
1115
+ .gate-slider:before {
1116
+ position: absolute;
1117
+ content: "";
1118
+ height: 12px;
1119
+ width: 12px;
1120
+ left: 3px;
1121
+ bottom: 3px;
1122
+ background-color: white;
1123
+ transition: 0.3s;
1124
+ border-radius: 50%;
1125
+ }
1126
+
1127
+ .gate-switch input:checked + .gate-slider {
1128
+ background-color: #4CAF50;
1129
+ }
1130
+
1131
+ .gate-switch input:checked + .gate-slider:before {
1132
+ transform: translateX(14px);
1133
+ }
1134
+
1135
+ .selection-box {
1136
+ position: fixed;
1137
+ border: 2px dashed #007acc;
1138
+ background: rgba(0, 122, 204, 0.15);
1139
+ pointer-events: none;
1140
+ z-index: 10000;
1141
+ }
1142
+
1143
+ .node-content {
1144
+ display: flex;
1145
+ align-items: stretch;
1146
+ min-height: 30px;
1147
+ flex: 1;
1148
+ border-radius: 6px;
1149
+ overflow: hidden;
1150
+ }
1151
+
1152
+ .node-icon-container {
1153
+ display: flex;
1154
+ justify-content: center;
1155
+ align-items: center;
1156
+ width: 40px; /* Fixed width for the icon container */
1157
+ background-color: rgba(0, 0, 0, 0.2); /* Optional background */
1158
+ }
1159
+
1160
+ .node-icon {
1161
+ font-size: 16px; /* Adjust icon size */
1162
+ line-height: 1;
1163
+ text-align: center;
1164
+ color: inherit; /* Inherit text color */
1165
+ }
1166
+
1167
+ .node-title {
1168
+ font-size: 13px;
1169
+ font-weight: 500;
1170
+ flex: 1;
1171
+ min-width: 0;
1172
+ display: flex;
1173
+ align-items: center;
1174
+ justify-content: center;
1175
+ padding: 4px 10px;
1176
+ text-align: center;
1177
+ line-height: 1.2;
1178
+ white-space: normal;
1179
+ word-break: break-word;
1180
+ overflow-wrap: anywhere;
1181
+ }
1182
+
1183
+ /* Keep controls vertically centered even though .node-content stretches */
1184
+ .node-content .inject-btn,
1185
+ .node-content .gate-switch,
1186
+ .node-content .rate-display,
1187
+ .node-content .queue-length-display,
1188
+ .node-content .counter-display,
1189
+ .node-content .transport-controls,
1190
+ .node-content .image-drop-zone {
1191
+ align-self: center;
1192
+ }
1193
+
1194
+ /* Image drop zone for ImageUploadNode */
1195
+ .image-drop-zone {
1196
+ width: 22px;
1197
+ height: 22px;
1198
+ border: 1px dashed rgba(0, 0, 0, 0.3);
1199
+ border-radius: 3px;
1200
+ background: rgba(255, 255, 255, 0.15);
1201
+ color: inherit;
1202
+ cursor: pointer;
1203
+ display: flex;
1204
+ align-items: center;
1205
+ justify-content: center;
1206
+ font-size: 12px;
1207
+ margin-left: 4px;
1208
+ margin-right: 6px;
1209
+ transition: all 0.15s;
1210
+ }
1211
+
1212
+ .node.image-drop-active {
1213
+ outline: 2px dashed #4CAF50;
1214
+ outline-offset: 2px;
1215
+ background-color: rgba(76, 175, 80, 0.12) !important;
1216
+ }
1217
+
1218
+ .node.image-drop-active .image-drop-zone {
1219
+ border-color: #4CAF50;
1220
+ background: rgba(76, 175, 80, 0.25);
1221
+ }
1222
+
1223
+ .node-delete {
1224
+ background: transparent;
1225
+ border: none;
1226
+ color: #d73a49;
1227
+ cursor: pointer;
1228
+ font-size: 16px;
1229
+ padding: 0;
1230
+ width: 20px;
1231
+ height: 20px;
1232
+ }
1233
+
1234
+ .node-delete:hover {
1235
+ color: #e74856;
1236
+ }
1237
+
1238
+ .node-type {
1239
+ font-size: 11px;
1240
+ color: #888;
1241
+ margin-bottom: 8px;
1242
+ }
1243
+
1244
+ .node-ports {
1245
+ position: absolute;
1246
+ top: 50%;
1247
+ left: 0;
1248
+ right: 0;
1249
+ transform: translateY(-50%);
1250
+ display: flex;
1251
+ justify-content: space-between;
1252
+ align-items: center;
1253
+ pointer-events: none;
1254
+ z-index: 10;
1255
+ }
1256
+
1257
+ .node-ports .port.input {
1258
+ margin-right: auto;
1259
+ }
1260
+
1261
+ .node-ports .port.output {
1262
+ margin-left: auto;
1263
+ }
1264
+
1265
+ .node-ports .port {
1266
+ pointer-events: all;
1267
+ }
1268
+
1269
+ .output-ports-container {
1270
+ display: flex;
1271
+ flex-direction: column;
1272
+ gap: 4px;
1273
+ margin-left: auto;
1274
+ }
1275
+
1276
+ .output-ports-container .port.output {
1277
+ margin-right: -7px;
1278
+ }
1279
+
1280
+ .input-ports-container {
1281
+ display: flex;
1282
+ flex-direction: column;
1283
+ gap: 4px;
1284
+ margin-right: auto;
1285
+ }
1286
+
1287
+ .input-ports-container .port.input {
1288
+ margin-left: -7px;
1289
+ }
1290
+
1291
+ .port {
1292
+ width: 10px;
1293
+ height: 10px;
1294
+ border-radius: 50%;
1295
+ background: #555;
1296
+ border: 2px solid #888;
1297
+ cursor: crosshair;
1298
+ transition: all 0.2s;
1299
+ position: relative;
1300
+ }
1301
+
1302
+ .port:hover {
1303
+ background: #0e639c;
1304
+ border-color: #1177bb;
1305
+ transform: scale(1.3);
1306
+ }
1307
+
1308
+ .port.input {
1309
+ margin-left: -7px;
1310
+ }
1311
+
1312
+ .port.output {
1313
+ margin-right: -7px;
1314
+ }
1315
+
1316
+ /* Connections */
1317
+ .connection {
1318
+ fill: none;
1319
+ stroke: #999;
1320
+ stroke-width: 2;
1321
+ pointer-events: stroke;
1322
+ cursor: pointer;
1323
+ stroke-linecap: round;
1324
+ }
1325
+
1326
+ .connection:hover {
1327
+ stroke: #0e639c;
1328
+ stroke-width: 3;
1329
+ }
1330
+
1331
+ .connection.selected {
1332
+ stroke: #0098ff;
1333
+ filter: drop-shadow(0 0 6px rgba(0, 152, 255, 0.8));
1334
+ }
1335
+
1336
+ .connection.disabled {
1337
+ stroke-dasharray: 5, 5;
1338
+ opacity: 0.6;
1339
+ }
1340
+
1341
+ .connection.hover-insert {
1342
+ stroke-dasharray: 8, 8;
1343
+ stroke: #0e639c;
1344
+ stroke-width: 3;
1345
+ }
1346
+
1347
+ /* Properties Panel */
1348
+ .properties {
1349
+ width: 380px;
1350
+ min-width: 380px;
1351
+ max-width: 800px;
1352
+ background: #252526;
1353
+ border-left: 1px solid #3e3e42;
1354
+ overflow-y: auto;
1355
+ overflow-x: hidden;
1356
+ padding: 15px;
1357
+ position: relative;
1358
+ z-index: 100;
1359
+ flex-shrink: 0;
1360
+ transition: width 0.2s ease, min-width 0.2s ease, padding 0.2s ease, opacity 0.2s ease;
1361
+ }
1362
+
1363
+ .properties.hidden {
1364
+ opacity: 0;
1365
+ pointer-events: none;
1366
+ min-width: 0;
1367
+ max-width: 0;
1368
+ width: 0;
1369
+ padding: 0;
1370
+ border: none;
1371
+ overflow: hidden;
1372
+ }
1373
+
1374
+ .properties.hidden .resize-handle {
1375
+ display: none;
1376
+ }
1377
+
1378
+ .properties.resizing {
1379
+ user-select: none;
1380
+ }
1381
+
1382
+ .resize-handle {
1383
+ position: absolute;
1384
+ left: 0;
1385
+ top: 0;
1386
+ bottom: 0;
1387
+ width: 5px;
1388
+ cursor: ew-resize;
1389
+ z-index: 100;
1390
+ }
1391
+
1392
+ .resize-handle:hover {
1393
+ background: rgba(14, 99, 156, 0.3);
1394
+ }
1395
+
1396
+ .resize-handle:active {
1397
+ background: rgba(14, 99, 156, 0.5);
1398
+ }
1399
+
1400
+ .properties h3 {
1401
+ font-size: 14px;
1402
+ margin: 0;
1403
+ color: #cccccc;
1404
+ }
1405
+
1406
+ .properties-header {
1407
+ display: flex;
1408
+ justify-content: space-between;
1409
+ align-items: center;
1410
+ margin-bottom: 15px;
1411
+ }
1412
+
1413
+ .properties-close-btn {
1414
+ background: transparent;
1415
+ border: none;
1416
+ color: #888;
1417
+ font-size: 24px;
1418
+ cursor: pointer;
1419
+ padding: 0;
1420
+ line-height: 1;
1421
+ }
1422
+
1423
+ .properties-close-btn:hover {
1424
+ color: #d4d4d4;
1425
+ }
1426
+
1427
+ .placeholder {
1428
+ color: #888;
1429
+ font-size: 13px;
1430
+ }
1431
+
1432
+ .property-group {
1433
+ margin-bottom: 15px;
1434
+ }
1435
+
1436
+ .property-hint {
1437
+ display: block;
1438
+ font-size: 11px;
1439
+ color: #808080;
1440
+ margin-top: 4px;
1441
+ }
1442
+
1443
+ .workflow-properties {
1444
+ padding: 10px;
1445
+ }
1446
+
1447
+ .property-enabled-row {
1448
+ display: flex;
1449
+ align-items: center;
1450
+ justify-content: space-between;
1451
+ }
1452
+
1453
+ .property-enabled-row .property-label {
1454
+ margin-bottom: 0;
1455
+ }
1456
+
1457
+ .property-label {
1458
+ display: block;
1459
+ font-size: 12px;
1460
+ color: #cccccc;
1461
+ margin-bottom: 5px;
1462
+ }
1463
+
1464
+ .property-input {
1465
+ width: 100%;
1466
+ padding: 6px;
1467
+ background: #3e3e42;
1468
+ border: 1px solid #555;
1469
+ border-radius: 3px;
1470
+ color: #d4d4d4;
1471
+ font-size: 13px;
1472
+ }
1473
+
1474
+ .property-input:focus {
1475
+ outline: none;
1476
+ border-color: #0e639c;
1477
+ }
1478
+
1479
+ .property-checkbox {
1480
+ width: auto;
1481
+ margin-right: 8px;
1482
+ cursor: pointer;
1483
+ }
1484
+
1485
+ .property-textarea {
1486
+ min-height: 100px;
1487
+ font-family: 'Consolas', 'Monaco', monospace;
1488
+ resize: vertical;
1489
+ }
1490
+
1491
+ .property-help {
1492
+ display: block;
1493
+ font-size: 11px;
1494
+ color: #888;
1495
+ margin-top: 4px;
1496
+ font-style: italic;
1497
+ }
1498
+
1499
+ .property-select {
1500
+ width: 100%;
1501
+ padding: 6px;
1502
+ background: #3e3e42;
1503
+ border: 1px solid #555;
1504
+ border-radius: 3px;
1505
+ color: #d4d4d4;
1506
+ font-size: 13px;
1507
+ }
1508
+
1509
+ /* Multiselect property - checkbox list */
1510
+ .property-multiselect {
1511
+ display: flex;
1512
+ flex-direction: column;
1513
+ gap: 4px;
1514
+ padding: 8px;
1515
+ background: #2d2d30;
1516
+ border: 1px solid #555;
1517
+ border-radius: 3px;
1518
+ max-height: 150px;
1519
+ overflow-y: auto;
1520
+ }
1521
+
1522
+ .multiselect-option {
1523
+ display: flex;
1524
+ align-items: center;
1525
+ gap: 8px;
1526
+ cursor: pointer;
1527
+ padding: 4px 6px;
1528
+ border-radius: 3px;
1529
+ font-size: 13px;
1530
+ color: #d4d4d4;
1531
+ }
1532
+
1533
+ .multiselect-option:hover {
1534
+ background: #3e3e42;
1535
+ }
1536
+
1537
+ .multiselect-option input[type="checkbox"] {
1538
+ width: 14px;
1539
+ height: 14px;
1540
+ cursor: pointer;
1541
+ accent-color: #0e639c;
1542
+ }
1543
+
1544
+ .multiselect-option span {
1545
+ flex: 1;
1546
+ }
1547
+
1548
+ /* Toggle property row - label on left, toggle on right */
1549
+ .property-toggle-row {
1550
+ display: flex;
1551
+ align-items: center;
1552
+ justify-content: space-between;
1553
+ gap: 8px;
1554
+ }
1555
+
1556
+ .property-toggle-row .property-label {
1557
+ margin-bottom: 0;
1558
+ }
1559
+
1560
+ /* File property with browse button */
1561
+ .property-file-container {
1562
+ display: flex;
1563
+ gap: 4px;
1564
+ }
1565
+
1566
+ .property-file-container .property-file-path {
1567
+ flex: 1;
1568
+ }
1569
+
1570
+ .property-file-btn {
1571
+ padding: 6px 10px;
1572
+ background: #0e639c;
1573
+ border: none;
1574
+ border-radius: 3px;
1575
+ color: white;
1576
+ cursor: pointer;
1577
+ flex-shrink: 0;
1578
+ }
1579
+
1580
+ .property-file-btn:hover {
1581
+ background: #1177bb;
1582
+ }
1583
+
1584
+ /* Upload progress bar */
1585
+ .upload-progress-container {
1586
+ display: flex;
1587
+ align-items: center;
1588
+ gap: 8px;
1589
+ margin-top: 4px;
1590
+ }
1591
+
1592
+ .upload-progress-bar {
1593
+ flex: 1;
1594
+ height: 6px;
1595
+ background: #3e3e42;
1596
+ border-radius: 3px;
1597
+ overflow: hidden;
1598
+ }
1599
+
1600
+ .upload-progress-fill {
1601
+ height: 100%;
1602
+ width: 0%;
1603
+ background: #0e639c;
1604
+ border-radius: 3px;
1605
+ transition: width 0.15s ease;
1606
+ }
1607
+
1608
+ .upload-progress-text {
1609
+ font-size: 11px;
1610
+ color: #999;
1611
+ min-width: 32px;
1612
+ text-align: right;
1613
+ }
1614
+
1615
+ /* Service property with select and buttons */
1616
+ .property-service-container {
1617
+ display: flex;
1618
+ gap: 4px;
1619
+ }
1620
+
1621
+ .property-service-container .property-service-select {
1622
+ flex: 1;
1623
+ }
1624
+
1625
+ .property-service-btn {
1626
+ padding: 6px 10px;
1627
+ border: none;
1628
+ border-radius: 3px;
1629
+ cursor: pointer;
1630
+ flex-shrink: 0;
1631
+ min-width: 32px;
1632
+ }
1633
+
1634
+ /* Stream URL property */
1635
+ .property-stream-url-container {
1636
+ display: flex;
1637
+ gap: 4px;
1638
+ }
1639
+
1640
+ .property-stream-url-container .property-stream-url {
1641
+ flex: 1;
1642
+ font-family: 'Consolas', 'Monaco', monospace;
1643
+ font-size: 11px;
1644
+ cursor: text;
1645
+ }
1646
+
1647
+ .property-stream-btn {
1648
+ padding: 6px 10px;
1649
+ border: none;
1650
+ border-radius: 3px;
1651
+ cursor: pointer;
1652
+ flex-shrink: 0;
1653
+ min-width: 32px;
1654
+ }
1655
+
1656
+ /* MQTT Service Modal */
1657
+ .mqtt-service-modal {
1658
+ position: fixed;
1659
+ top: 0;
1660
+ left: 0;
1661
+ width: 100%;
1662
+ height: 100%;
1663
+ background: rgba(0, 0, 0, 0.6);
1664
+ display: flex;
1665
+ align-items: center;
1666
+ justify-content: center;
1667
+ z-index: 10001;
1668
+ }
1669
+
1670
+ .mqtt-service-modal-content {
1671
+ background: #252526;
1672
+ border: 1px solid #3e3e42;
1673
+ border-radius: 8px;
1674
+ width: 450px;
1675
+ max-width: 90vw;
1676
+ max-height: 90vh;
1677
+ overflow-y: auto;
1678
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
1679
+ }
1680
+
1681
+ .mqtt-service-modal-header {
1682
+ display: flex;
1683
+ justify-content: space-between;
1684
+ align-items: center;
1685
+ padding: 15px 20px;
1686
+ border-bottom: 1px solid #3e3e42;
1687
+ }
1688
+
1689
+ .mqtt-service-modal-header h3 {
1690
+ margin: 0;
1691
+ font-size: 16px;
1692
+ color: #cccccc;
1693
+ }
1694
+
1695
+ .mqtt-service-modal-close {
1696
+ background: transparent;
1697
+ border: none;
1698
+ color: #888;
1699
+ font-size: 24px;
1700
+ cursor: pointer;
1701
+ padding: 0;
1702
+ line-height: 1;
1703
+ }
1704
+
1705
+ .mqtt-service-modal-close:hover {
1706
+ color: #d4d4d4;
1707
+ }
1708
+
1709
+ .mqtt-service-modal-body {
1710
+ padding: 20px;
1711
+ }
1712
+
1713
+ .mqtt-service-modal-body .property-group {
1714
+ margin-bottom: 15px;
1715
+ }
1716
+
1717
+ .mqtt-service-modal-footer {
1718
+ display: flex;
1719
+ justify-content: space-between;
1720
+ align-items: center;
1721
+ padding: 15px 20px;
1722
+ border-top: 1px solid #3e3e42;
1723
+ gap: 10px;
1724
+ }
1725
+
1726
+ .mqtt-service-modal-actions {
1727
+ display: flex;
1728
+ gap: 8px;
1729
+ }
1730
+
1731
+ /* Debug Panel */
1732
+ .debug-panel {
1733
+ display: flex;
1734
+ flex-direction: column;
1735
+ height: 100%;
1736
+ }
1737
+
1738
+ .debug-header {
1739
+ display: flex;
1740
+ justify-content: space-between;
1741
+ align-items: center;
1742
+ padding: 10px 15px;
1743
+ border-bottom: 1px solid #3e3e42;
1744
+ flex-shrink: 0;
1745
+ }
1746
+
1747
+ .debug-header h3 {
1748
+ font-size: 14px;
1749
+ color: #cccccc;
1750
+ margin: 0;
1751
+ }
1752
+
1753
+ .debug-controls {
1754
+ display: flex;
1755
+ gap: 5px;
1756
+ }
1757
+
1758
+ .icon-btn {
1759
+ display: inline-flex;
1760
+ align-items: center;
1761
+ justify-content: center;
1762
+ color: #d4d4d4;
1763
+ }
1764
+
1765
+ /* Pulsing indicator while the debug list is paused, as a reminder that new
1766
+ messages are being withheld. */
1767
+ .debug-paused-active {
1768
+ background: #b80b0b;
1769
+ border-color: #4086c0;
1770
+ animation: debug-pause-pulse 1.2s ease-in-out infinite;
1771
+ }
1772
+
1773
+ @keyframes debug-pause-pulse {
1774
+ 0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgb(255, 0, 0); background: #b80b0b; }
1775
+ 50% { opacity: 1; box-shadow: 0 0 6px 3px rgb(255, 0, 0); background: #b80b0b73; }
1776
+ }
1777
+
1778
+ .debug-filters {
1779
+ display: flex;
1780
+ gap: 10px;
1781
+ padding: 10px 15px;
1782
+ border-bottom: 1px solid #3e3e42;
1783
+ flex-wrap: wrap;
1784
+ background: #2d2d30;
1785
+ }
1786
+
1787
+ .filter-toggle-btn {
1788
+ display: flex;
1789
+ align-items: center;
1790
+ justify-content: center;
1791
+ background: #3e3e42;
1792
+ border: 1px solid #555;
1793
+ border-radius: 4px;
1794
+ padding: 6px 12px;
1795
+ cursor: pointer;
1796
+ font-size: 12px;
1797
+ color: #d4d4d4;
1798
+ transition: all 0.15s;
1799
+ min-width: 80px;
1800
+ /* Raised 3D effect (off state) */
1801
+ box-shadow:
1802
+ 0 2px 4px rgba(0, 0, 0, 0.3),
1803
+ inset 0 1px 0 rgba(255, 255, 255, 0.1),
1804
+ inset 0 -1px 0 rgba(0, 0, 0, 0.2);
1805
+ transform: translateY(0);
1806
+ }
1807
+
1808
+ .filter-toggle-btn:hover {
1809
+ background: #45454a;
1810
+ border-color: #666;
1811
+ }
1812
+
1813
+ .filter-toggle-btn.active {
1814
+ color: #fff;
1815
+ background: #2d2d30;
1816
+ border-color: #0e639c;
1817
+ /* Embossed/pressed effect (on state) */
1818
+ box-shadow:
1819
+ inset 0 2px 4px rgba(0, 0, 0, 0.4),
1820
+ inset 0 1px 2px rgba(0, 0, 0, 0.3),
1821
+ 0 1px 0 rgba(255, 255, 255, 0.05);
1822
+ transform: translateY(1px);
1823
+ }
1824
+
1825
+ #debug-messages {
1826
+ flex: 1;
1827
+ padding: 10px 15px;
1828
+ overflow-y: auto;
1829
+ }
1830
+
1831
+ .debug-message {
1832
+ padding: 8px;
1833
+ margin-bottom: 8px;
1834
+ background: #2d2d30;
1835
+ border-left: 3px solid #0e639c;
1836
+ border-radius: 3px;
1837
+ font-size: 12px;
1838
+ font-family: 'Consolas', 'Monaco', monospace;
1839
+ cursor: pointer;
1840
+ transition: background 0.2s ease;
1841
+ position: relative;
1842
+ overflow: hidden;
1843
+ }
1844
+
1845
+ .debug-message:hover {
1846
+ background: #3d3d40;
1847
+ }
1848
+
1849
+ .debug-message.error-message {
1850
+ background: #3d2020;
1851
+ border-left: 3px solid #E63946;
1852
+ }
1853
+
1854
+ .debug-message.error-message:hover {
1855
+ background: #4d2828;
1856
+ }
1857
+
1858
+ .debug-message.hidden {
1859
+ display: none;
1860
+ }
1861
+
1862
+ .debug-message-header {
1863
+ display: flex;
1864
+ align-items: center;
1865
+ gap: 5px;
1866
+ }
1867
+
1868
+ .debug-count {
1869
+ display: inline-block;
1870
+ background: #0e639c;
1871
+ color: #fff;
1872
+ padding: 2px 6px;
1873
+ border-radius: 10px;
1874
+ font-size: 10px;
1875
+ font-weight: bold;
1876
+ margin-left: 5px;
1877
+ }
1878
+
1879
+ .debug-message.error-message .debug-count {
1880
+ background: #E63946;
1881
+ }
1882
+
1883
+ .debug-timestamp {
1884
+ color: #888;
1885
+ font-size: 11px;
1886
+ }
1887
+
1888
+ .debug-node {
1889
+ color: #4ec9b0;
1890
+ margin: 0 5px;
1891
+ }
1892
+
1893
+ .debug-node.error-node {
1894
+ color: #FF6B6B;
1895
+ font-weight: bold;
1896
+ }
1897
+
1898
+ .debug-output {
1899
+ color: #d4d4d4;
1900
+ margin-top: 4px;
1901
+ word-break: break-word;
1902
+ }
1903
+
1904
+ .debug-output.error-output {
1905
+ color: #FFB3B3;
1906
+ }
1907
+
1908
+ /* Compact Collapsible Tree Styles (Node-RED style) */
1909
+ .tree-root {
1910
+ font-family: 'Consolas', 'Monaco', monospace;
1911
+ font-size: 12px;
1912
+ line-height: 1.4;
1913
+ overflow: hidden;
1914
+ width: 100%;
1915
+ }
1916
+
1917
+ .ti { /* tree-item */
1918
+ white-space: nowrap;
1919
+ overflow: hidden;
1920
+ text-overflow: ellipsis;
1921
+ max-width: 100%;
1922
+ }
1923
+
1924
+ .tt { /* tree-toggle */
1925
+ display: inline-block;
1926
+ width: 12px;
1927
+ cursor: pointer;
1928
+ color: #888;
1929
+ font-size: 8px;
1930
+ transition: transform 0.1s;
1931
+ user-select: none;
1932
+ text-align: center;
1933
+ }
1934
+
1935
+ .tt:hover { color: #ccc; }
1936
+
1937
+ .ti.c > .tt { transform: rotate(0deg); }
1938
+ .ti:not(.c) > .tt { transform: rotate(90deg); }
1939
+
1940
+ .ti.c > .tc { display: none; }
1941
+
1942
+ /* .tc { tree-children } */
1943
+
1944
+ .tk { color: #9cdcfe; } /* tree-key */
1945
+ .tl { color: #888; font-style: italic; } /* tree-label (type) */
1946
+ .tv { color: #d4d4d4; } /* tree-value */
1947
+ .tv-str { color: #ce9178; } /* string */
1948
+ .tv-num { color: #b5cea8; } /* number */
1949
+ .tv-bool { color: #569cd6; } /* boolean */
1950
+ .tv-null, .tv-undefined { color: #808080; font-style: italic; }
1951
+ .tv-empty { color: #888; }
1952
+
1953
+ /* Scrollbar styling */
1954
+ ::-webkit-scrollbar {
1955
+ width: 10px;
1956
+ height: 10px;
1957
+ }
1958
+
1959
+ ::-webkit-scrollbar-track {
1960
+ background: #1e1e1e;
1961
+ }
1962
+
1963
+ ::-webkit-scrollbar-thumb {
1964
+ background: #555;
1965
+ border-radius: 5px;
1966
+ }
1967
+
1968
+ ::-webkit-scrollbar-thumb:hover {
1969
+ background: #666;
1970
+ }
1971
+
1972
+ /* Rules Editor */
1973
+ .rules-editor {
1974
+ margin-top: 6px;
1975
+ }
1976
+
1977
+ .rule-item {
1978
+ background: #2d2d30;
1979
+ border: 1px solid #555;
1980
+ border-radius: 4px;
1981
+ padding: 10px;
1982
+ margin-bottom: 10px;
1983
+ }
1984
+
1985
+ .rule-header {
1986
+ display: flex;
1987
+ justify-content: space-between;
1988
+ align-items: center;
1989
+ margin-bottom: 8px;
1990
+ }
1991
+
1992
+ .rule-label {
1993
+ font-size: 12px;
1994
+ font-weight: 600;
1995
+ color: #0e639c;
1996
+ }
1997
+
1998
+ .btn-icon {
1999
+ background: transparent;
2000
+ border: none;
2001
+ color: #d73a49;
2002
+ cursor: pointer;
2003
+ font-size: 16px;
2004
+ padding: 0;
2005
+ width: 20px;
2006
+ height: 20px;
2007
+ display: flex;
2008
+ align-items: center;
2009
+ justify-content: center;
2010
+ }
2011
+
2012
+ .btn-icon:hover {
2013
+ color: #e74856;
2014
+ }
2015
+
2016
+ .rule-config {
2017
+ display: flex;
2018
+ align-items: center;
2019
+ gap: 6px;
2020
+ flex-wrap: wrap;
2021
+ }
2022
+
2023
+ .rule-config-row {
2024
+ display: flex;
2025
+ align-items: center;
2026
+ gap: 6px;
2027
+ flex: 1;
2028
+ min-width: 0;
2029
+ }
2030
+
2031
+ .rule-operator {
2032
+ flex: 0 0 auto;
2033
+ padding: 6px;
2034
+ background: #3e3e42;
2035
+ border: 1px solid #555;
2036
+ border-radius: 3px;
2037
+ color: #d4d4d4;
2038
+ font-size: 12px;
2039
+ }
2040
+
2041
+ .rule-value {
2042
+ flex: 1;
2043
+ min-width: 60px;
2044
+ padding: 6px;
2045
+ background: #3e3e42;
2046
+ border: 1px solid #555;
2047
+ border-radius: 3px;
2048
+ color: #d4d4d4;
2049
+ font-size: 12px;
2050
+ }
2051
+
2052
+ .rule-type {
2053
+ flex: 0 0 auto;
2054
+ padding: 6px;
2055
+ background: #3e3e42;
2056
+ border: 1px solid #555;
2057
+ border-radius: 3px;
2058
+ color: #d4d4d4;
2059
+ font-size: 12px;
2060
+ }
2061
+
2062
+ .rule-operator:focus,
2063
+ .rule-value:focus,
2064
+ .rule-type:focus {
2065
+ outline: none;
2066
+ border-color: #0e639c;
2067
+ }
2068
+
2069
+ /* Inject Properties Editor */
2070
+ .inject-props-editor {
2071
+ margin-top: 6px;
2072
+ }
2073
+
2074
+ .inject-prop-row {
2075
+ display: flex;
2076
+ align-items: center;
2077
+ gap: 4px;
2078
+ margin-bottom: 6px;
2079
+ font-size: 12px;
2080
+ }
2081
+
2082
+ .inject-prop-prefix {
2083
+ color: #888;
2084
+ flex-shrink: 0;
2085
+ }
2086
+
2087
+ .inject-prop-eq {
2088
+ color: #888;
2089
+ flex-shrink: 0;
2090
+ }
2091
+
2092
+ .inject-prop-key {
2093
+ width: 90px;
2094
+ padding: 4px 6px;
2095
+ background: #3e3e42;
2096
+ border: 1px solid #555;
2097
+ border-radius: 3px;
2098
+ color: #d4d4d4;
2099
+ font-size: 12px;
2100
+ }
2101
+
2102
+ .inject-prop-type {
2103
+ width: 42px;
2104
+ padding: 4px;
2105
+ background: #3e3e42;
2106
+ border: 1px solid #555;
2107
+ border-radius: 3px;
2108
+ color: #d4d4d4;
2109
+ font-size: 11px;
2110
+ text-align: center;
2111
+ }
2112
+
2113
+ .inject-prop-value {
2114
+ flex: 1;
2115
+ min-width: 50px;
2116
+ padding: 4px 6px;
2117
+ background: #3e3e42;
2118
+ border: 1px solid #555;
2119
+ border-radius: 3px;
2120
+ color: #d4d4d4;
2121
+ font-size: 12px;
2122
+ }
2123
+
2124
+ .inject-prop-value:disabled {
2125
+ background: #2d2d30;
2126
+ color: #666;
2127
+ cursor: not-allowed;
2128
+ }
2129
+
2130
+ .inject-prop-json {
2131
+ font-family: 'Consolas', 'Monaco', monospace;
2132
+ }
2133
+
2134
+ .btn-icon-sm {
2135
+ background: transparent;
2136
+ border: none;
2137
+ color: #888;
2138
+ cursor: pointer;
2139
+ font-size: 12px;
2140
+ padding: 2px 4px;
2141
+ flex-shrink: 0;
2142
+ }
2143
+
2144
+ .btn-icon-sm:hover {
2145
+ color: #d73a49;
2146
+ }
2147
+
2148
+ .btn-sm {
2149
+ padding: 4px 8px;
2150
+ font-size: 11px;
2151
+ }
2152
+
2153
+ .inject-prop-key:focus,
2154
+ .inject-prop-type:focus,
2155
+ .inject-prop-value:focus {
2156
+ outline: none;
2157
+ border-color: #0e639c;
2158
+ }
2159
+
2160
+ /* Change Rules Editor */
2161
+ .change-rules-editor {
2162
+ margin-top: 6px;
2163
+ }
2164
+
2165
+ .change-rule-item {
2166
+ background: #2d2d30;
2167
+ border: 1px solid #555;
2168
+ border-radius: 4px;
2169
+ padding: 10px;
2170
+ margin-bottom: 10px;
2171
+ }
2172
+
2173
+ .change-rule-header {
2174
+ display: flex;
2175
+ justify-content: space-between;
2176
+ align-items: center;
2177
+ margin-bottom: 8px;
2178
+ }
2179
+
2180
+ .change-rule-type {
2181
+ flex: 0 0 90px;
2182
+ padding: 6px;
2183
+ background: #3e3e42;
2184
+ border: 1px solid #555;
2185
+ border-radius: 3px;
2186
+ color: #d4d4d4;
2187
+ font-size: 12px;
2188
+ font-weight: 600;
2189
+ }
2190
+
2191
+ .change-rule-config {
2192
+ display: flex;
2193
+ flex-direction: column;
2194
+ gap: 6px;
2195
+ }
2196
+
2197
+ .change-rule-row {
2198
+ display: flex;
2199
+ align-items: center;
2200
+ gap: 6px;
2201
+ flex-wrap: wrap;
2202
+ }
2203
+
2204
+ .change-rule-label {
2205
+ flex: 0 0 50px;
2206
+ font-size: 11px;
2207
+ color: #888;
2208
+ text-align: right;
2209
+ }
2210
+
2211
+ .change-rule-to {
2212
+ font-size: 11px;
2213
+ color: #888;
2214
+ flex-shrink: 0;
2215
+ }
2216
+
2217
+ .change-rule-path {
2218
+ flex: 1 1 100px;
2219
+ min-width: 80px;
2220
+ padding: 6px;
2221
+ background: #3e3e42;
2222
+ border: 1px solid #555;
2223
+ border-radius: 3px;
2224
+ color: #d4d4d4;
2225
+ font-size: 12px;
2226
+ font-family: 'Consolas', 'Monaco', monospace;
2227
+ }
2228
+
2229
+ .change-rule-to-path {
2230
+ flex: 1;
2231
+ padding: 6px;
2232
+ background: #3e3e42;
2233
+ border: 1px solid #555;
2234
+ border-radius: 3px;
2235
+ color: #d4d4d4;
2236
+ font-size: 12px;
2237
+ font-family: 'Consolas', 'Monaco', monospace;
2238
+ }
2239
+
2240
+ .change-rule-value-type {
2241
+ flex: 0 0 70px;
2242
+ min-width: 65px;
2243
+ padding: 6px 4px;
2244
+ background: #3e3e42;
2245
+ border: 1px solid #555;
2246
+ border-radius: 3px;
2247
+ color: #d4d4d4;
2248
+ font-size: 11px;
2249
+ }
2250
+
2251
+ .change-rule-value {
2252
+ flex: 1;
2253
+ min-width: 60px;
2254
+ padding: 6px;
2255
+ background: #3e3e42;
2256
+ border: 1px solid #555;
2257
+ border-radius: 3px;
2258
+ color: #d4d4d4;
2259
+ font-size: 12px;
2260
+ }
2261
+
2262
+ .change-rule-value:disabled {
2263
+ background: #2d2d30;
2264
+ color: #666;
2265
+ cursor: not-allowed;
2266
+ }
2267
+
2268
+ .change-rule-json {
2269
+ font-family: 'Consolas', 'Monaco', monospace;
2270
+ }
2271
+
2272
+ .change-rule-search-type,
2273
+ .change-rule-replace-type {
2274
+ flex: 0 0 65px;
2275
+ padding: 6px;
2276
+ background: #3e3e42;
2277
+ border: 1px solid #555;
2278
+ border-radius: 3px;
2279
+ color: #d4d4d4;
2280
+ font-size: 11px;
2281
+ }
2282
+
2283
+ .change-rule-search,
2284
+ .change-rule-replace {
2285
+ flex: 1;
2286
+ padding: 6px;
2287
+ background: #3e3e42;
2288
+ border: 1px solid #555;
2289
+ border-radius: 3px;
2290
+ color: #d4d4d4;
2291
+ font-size: 12px;
2292
+ }
2293
+
2294
+ .change-rule-type:focus,
2295
+ .change-rule-path:focus,
2296
+ .change-rule-to-path:focus,
2297
+ .change-rule-value-type:focus,
2298
+ .change-rule-value:focus,
2299
+ .change-rule-search-type:focus,
2300
+ .change-rule-replace-type:focus,
2301
+ .change-rule-search:focus,
2302
+ .change-rule-replace:focus {
2303
+ outline: none;
2304
+ border-color: #0e639c;
2305
+ }
2306
+
2307
+ /* Palette Search */
2308
+ .palette-search-container {
2309
+ padding: 8px 10px;
2310
+ border-bottom: 1px solid #3e3e42;
2311
+ }
2312
+
2313
+ .palette-search {
2314
+ width: 100%;
2315
+ padding: 6px 10px;
2316
+ background: #3c3c3c;
2317
+ border: 1px solid #3e3e42;
2318
+ border-radius: 4px;
2319
+ color: #cccccc;
2320
+ font-size: 12px;
2321
+ outline: none;
2322
+ box-sizing: border-box;
2323
+ }
2324
+
2325
+ /* Clear button inside the palette search */
2326
+ .palette-search-container {
2327
+ position: relative;
2328
+ }
2329
+
2330
+ .search-clear-btn {
2331
+ position: absolute;
2332
+ right: 12px;
2333
+ top: 50%;
2334
+ transform: translateY(-50%);
2335
+ background: transparent;
2336
+ border: none;
2337
+ color: #888;
2338
+ font-size: 14px;
2339
+ cursor: pointer;
2340
+ padding: 2px 6px;
2341
+ display: none;
2342
+ }
2343
+
2344
+ .search-clear-btn:hover {
2345
+ color: #fff;
2346
+ }
2347
+
2348
+ .palette-search:focus {
2349
+ border-color: #0e639c;
2350
+ background: #1e1e1e;
2351
+ }
2352
+
2353
+ .palette-search::placeholder {
2354
+ color: #888;
2355
+ }
2356
+
2357
+ /* Mini Palette */
2358
+ .mini-palette {
2359
+ position: fixed;
2360
+ background: #252526;
2361
+ border: 1px solid #3e3e42;
2362
+ border-radius: 6px;
2363
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
2364
+ z-index: 10000;
2365
+ max-height: 400px;
2366
+ overflow: hidden;
2367
+ min-width: 200px;
2368
+ max-width: 250px;
2369
+ transform: translate(-50%, 10px);
2370
+ }
2371
+
2372
+ .mini-palette-search-container {
2373
+ padding: 8px;
2374
+ border-bottom: 1px solid #3e3e42;
2375
+ position: sticky;
2376
+ position: relative;
2377
+ top: 0;
2378
+ background: #252526;
2379
+ z-index: 2;
2380
+ }
2381
+
2382
+ .mini-palette-search {
2383
+ width: 100%;
2384
+ padding: 6px 10px;
2385
+ background: #3c3c3c;
2386
+ border: 1px solid #3e3e42;
2387
+ border-radius: 4px;
2388
+ color: #cccccc;
2389
+ font-size: 12px;
2390
+ outline: none;
2391
+ box-sizing: border-box;
2392
+ }
2393
+
2394
+ .mini-palette-search:focus {
2395
+ border-color: #0e639c;
2396
+ background: #1e1e1e;
2397
+ }
2398
+
2399
+ .mini-palette-search::placeholder {
2400
+ color: #888;
2401
+ }
2402
+
2403
+ .mini-palette-category {
2404
+ margin-bottom: 8px;
2405
+ }
2406
+
2407
+ .mini-palette-category:last-child {
2408
+ margin-bottom: 0;
2409
+ }
2410
+
2411
+ .mini-palette-category-label {
2412
+ padding: 8px 12px;
2413
+ font-size: 11px;
2414
+ font-weight: 600;
2415
+ color: #888;
2416
+ text-transform: uppercase;
2417
+ letter-spacing: 0.5px;
2418
+ background: #2d2d30;
2419
+ border-bottom: 1px solid #3e3e42;
2420
+ }
2421
+
2422
+ .mini-palette-content {
2423
+ overflow-y: auto;
2424
+ max-height: 340px;
2425
+ }
2426
+
2427
+ .mini-palette-item {
2428
+ background: #3e3e42;
2429
+ padding: 0;
2430
+ margin: 4px 8px;
2431
+ border-radius: 6px;
2432
+ cursor: pointer;
2433
+ display: flex;
2434
+ align-items: stretch;
2435
+ min-height: 28px;
2436
+ font-size: 13px;
2437
+ border: 2px solid #555;
2438
+ transition: all 0.15s ease;
2439
+ overflow: hidden;
2440
+ }
2441
+
2442
+ .mini-palette-item:hover {
2443
+ border-color: #0e639c;
2444
+ filter: brightness(1.1);
2445
+ }
2446
+
2447
+ .mini-palette-icon {
2448
+ background: rgba(0, 0, 0, 0.2);
2449
+ display: flex;
2450
+ align-items: center;
2451
+ justify-content: center;
2452
+ width: 28px;
2453
+ flex-shrink: 0;
2454
+ font-size: 14px;
2455
+ line-height: 1;
2456
+ }
2457
+
2458
+ .mini-palette-item-name {
2459
+ flex: 1;
2460
+ display: flex;
2461
+ align-items: center;
2462
+ padding: 4px 10px;
2463
+ font-weight: 500;
2464
+ }
2465
+
2466
+ /* Mini palette scrollbar */
2467
+ .mini-palette-content::-webkit-scrollbar {
2468
+ width: 8px;
2469
+ }
2470
+
2471
+ .mini-palette-content::-webkit-scrollbar-track {
2472
+ background: #1e1e1e;
2473
+ }
2474
+
2475
+ .mini-palette-content::-webkit-scrollbar-thumb {
2476
+ background: #3e3e42;
2477
+ border-radius: 4px;
2478
+ }
2479
+
2480
+ .mini-palette-content::-webkit-scrollbar-thumb:hover {
2481
+ background: #555;
2482
+ }
2483
+
2484
+ /* Animations */
2485
+ @keyframes flash {
2486
+ 0%, 100% {
2487
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
2488
+ }
2489
+ 50% {
2490
+ box-shadow: 0 0 0 4px rgba(0, 152, 255, 0.8), 0 0 20px rgba(0, 152, 255, 0.9);
2491
+ border-color: #0098ff;
2492
+ }
2493
+ }
2494
+
2495
+ /* Right Sidebar */
2496
+ .right-sidebar {
2497
+ width: 350px;
2498
+ background: #252526;
2499
+ border-left: 1px solid #3e3e42;
2500
+ display: flex;
2501
+ flex-direction: row;
2502
+ flex-shrink: 0;
2503
+ position: relative;
2504
+ }
2505
+
2506
+ .right-sidebar.collapsed {
2507
+ width: 18px;
2508
+ overflow: visible;
2509
+ }
2510
+
2511
+ .right-sidebar.collapsed > :not(.panel-grab) {
2512
+ display: none;
2513
+ }
2514
+
2515
+ /* Side panel grab handles (resize + toggle) */
2516
+ .panel-grab {
2517
+ position: absolute;
2518
+ top: 50%;
2519
+ transform: translateY(-50%);
2520
+ width: 8px;
2521
+ height: 44px;
2522
+ background: #3e3e42;
2523
+ border: 1px solid #555;
2524
+ border-radius: 6px;
2525
+ cursor: ew-resize;
2526
+ z-index: 1000;
2527
+ display: flex;
2528
+ align-items: center;
2529
+ justify-content: center;
2530
+ transition: background 0.15s ease, border-color 0.15s ease;
2531
+ overflow: visible;
2532
+ isolation: isolate;
2533
+ }
2534
+
2535
+ /* Invisible hover bridge so moving from bar -> button doesn't drop :hover */
2536
+ .panel-grab::after {
2537
+ content: "";
2538
+ position: absolute;
2539
+ top: -6px;
2540
+ bottom: -6px;
2541
+ left: 0;
2542
+ right: 0;
2543
+ background: transparent;
2544
+ z-index: 0;
2545
+ }
2546
+
2547
+ .panel-grab-left::after {
2548
+ right: -40px;
2549
+ }
2550
+
2551
+ .panel-grab-right::after {
2552
+ left: -40px;
2553
+ }
2554
+
2555
+ .panel-grab:hover {
2556
+ background: #0e639c;
2557
+ border-color: #1177bb;
2558
+ }
2559
+
2560
+ .panel-grab-left {
2561
+ left: 100%;
2562
+ }
2563
+
2564
+ .panel-grab-right {
2565
+ right: 100%;
2566
+ }
2567
+
2568
+ .panel-toggle-btn {
2569
+ position: absolute;
2570
+ top: 50%;
2571
+ width: 28px;
2572
+ height: 28px;
2573
+ border-radius: 4px;
2574
+ border: 1px solid rgba(0, 0, 0, 0.35);
2575
+ background: rgba(0, 0, 0, 0.25);
2576
+ color: #fff;
2577
+ font-size: 16px;
2578
+ line-height: 1;
2579
+ cursor: pointer;
2580
+ display: flex;
2581
+ align-items: center;
2582
+ justify-content: center;
2583
+ opacity: 0;
2584
+ pointer-events: none;
2585
+ transform: translateY(-50%) translateX(0);
2586
+ transition: opacity 0.12s ease, transform 0.12s ease, background 0.12s ease;
2587
+ z-index: 2;
2588
+ }
2589
+
2590
+ .panel-grab:hover .panel-toggle-btn {
2591
+ opacity: 1;
2592
+ pointer-events: auto;
2593
+ }
2594
+
2595
+ /* Slide the toggle out from behind the grab point */
2596
+ .panel-grab-left .panel-toggle-btn {
2597
+ left: 50%;
2598
+ }
2599
+
2600
+ .panel-grab-right .panel-toggle-btn {
2601
+ right: 50%;
2602
+ }
2603
+
2604
+ .panel-grab-left:hover .panel-toggle-btn {
2605
+ transform: translateY(-50%) translateX(14px);
2606
+ }
2607
+
2608
+ .panel-grab-right:hover .panel-toggle-btn {
2609
+ transform: translateY(-50%) translateX(-14px);
2610
+ }
2611
+
2612
+ .panel-toggle-btn:hover {
2613
+ background: rgba(0, 0, 0, 0.4);
2614
+ }
2615
+
2616
+ .sidebar-tabs {
2617
+ display: flex;
2618
+ flex-direction: column;
2619
+ background: #1e1e1e;
2620
+ border-right: 1px solid #3e3e42;
2621
+ padding: 5px 0;
2622
+ }
2623
+
2624
+ .sidebar-tab {
2625
+ width: 40px;
2626
+ height: 40px;
2627
+ border: none;
2628
+ background: transparent;
2629
+ cursor: pointer;
2630
+ font-size: 18px;
2631
+ display: flex;
2632
+ align-items: center;
2633
+ justify-content: center;
2634
+ color: #888;
2635
+ transition: all 0.15s;
2636
+ border-left: 3px solid transparent;
2637
+ }
2638
+
2639
+ .sidebar-tab:hover {
2640
+ color: #fff;
2641
+ background: #2d2d30;
2642
+ }
2643
+
2644
+ .sidebar-tab.active {
2645
+ color: #fff;
2646
+ background: #252526;
2647
+ border-left-color: #0e639c;
2648
+ }
2649
+
2650
+ .sidebar-content {
2651
+ flex: 1;
2652
+ display: flex;
2653
+ flex-direction: column;
2654
+ overflow: hidden;
2655
+ }
2656
+
2657
+ .sidebar-panel {
2658
+ display: none;
2659
+ flex-direction: column;
2660
+ height: 100%;
2661
+ overflow: hidden;
2662
+ }
2663
+
2664
+ .sidebar-panel.active {
2665
+ display: flex;
2666
+ }
2667
+
2668
+ .info-header {
2669
+ display: flex;
2670
+ justify-content: space-between;
2671
+ align-items: center;
2672
+ padding: 10px 15px;
2673
+ border-bottom: 1px solid #3e3e42;
2674
+ flex-shrink: 0;
2675
+ }
2676
+
2677
+ .info-header h3 {
2678
+ font-size: 14px;
2679
+ color: #cccccc;
2680
+ margin: 0;
2681
+ }
2682
+
2683
+ .info-content {
2684
+ flex: 1;
2685
+ padding: 15px;
2686
+ overflow-y: auto;
2687
+ }
2688
+
2689
+ .info-content .placeholder {
2690
+ color: #888;
2691
+ font-style: italic;
2692
+ }
2693
+
2694
+ .info-node-header {
2695
+ display: flex;
2696
+ align-items: center;
2697
+ gap: 8px;
2698
+ margin-bottom: 12px;
2699
+ padding-bottom: 10px;
2700
+ border-bottom: 1px solid #3e3e42;
2701
+ }
2702
+
2703
+ .info-node-icon {
2704
+ font-size: 20px;
2705
+ }
2706
+
2707
+ .info-node-name {
2708
+ font-size: 16px;
2709
+ font-weight: 600;
2710
+ color: #fff;
2711
+ }
2712
+
2713
+ .info-node-content {
2714
+ font-size: 13px;
2715
+ line-height: 1.5;
2716
+ color: #ccc;
2717
+ }
2718
+
2719
+ .info-node-content p {
2720
+ margin: 0 0 10px 0;
2721
+ }
2722
+
2723
+ .info-node-content ul, .info-node-content ol {
2724
+ margin: 8px 0;
2725
+ padding-left: 20px;
2726
+ }
2727
+
2728
+ .info-node-content li {
2729
+ margin: 4px 0;
2730
+ }
2731
+
2732
+ .info-node-content code {
2733
+ background: #1e1e1e;
2734
+ padding: 2px 5px;
2735
+ border-radius: 3px;
2736
+ font-family: 'Consolas', 'Monaco', monospace;
2737
+ font-size: 12px;
2738
+ color: #ce9178;
2739
+ }
2740
+
2741
+ .info-node-content strong {
2742
+ color: #fff;
2743
+ }
2744
+
2745
+ .info-node-content h4 {
2746
+ font-size: 13px;
2747
+ font-weight: 600;
2748
+ color: #4ec9b0;
2749
+ margin: 12px 0 6px 0;
2750
+ }
2751
+