@midscene/visualizer 0.2.2 → 0.2.3-beta-20240815082813.0

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 (46) hide show
  1. package/dist/es/assets/logo-plain.0f78df8a.png +0 -0
  2. package/dist/es/component/blackboard.js +32 -104
  3. package/dist/es/component/detail-panel.css +6 -0
  4. package/dist/es/component/detail-panel.js +1 -1
  5. package/dist/es/component/misc.js +29 -0
  6. package/dist/es/component/panel-title.css +5 -3
  7. package/dist/es/component/panel-title.js +6 -2
  8. package/dist/es/component/sidebar.css +1 -11
  9. package/dist/es/component/sidebar.js +5 -47
  10. package/dist/es/component/store.js +18 -20
  11. package/dist/es/index.css +42 -0
  12. package/dist/es/index.js +160 -34
  13. package/dist/es/utils.js +1 -1
  14. package/dist/index.css +445 -0
  15. package/dist/index.js +1 -0
  16. package/dist/lib/assets/logo-plain.0f78df8a.png +0 -0
  17. package/dist/lib/component/blackboard.js +32 -104
  18. package/dist/lib/component/detail-panel.css +6 -0
  19. package/dist/lib/component/detail-panel.js +1 -1
  20. package/dist/lib/component/misc.js +23 -0
  21. package/dist/lib/component/panel-title.css +5 -3
  22. package/dist/lib/component/panel-title.js +5 -1
  23. package/dist/lib/component/sidebar.css +1 -11
  24. package/dist/lib/component/sidebar.js +3 -37
  25. package/dist/lib/component/store.js +31 -23
  26. package/dist/lib/index.css +42 -0
  27. package/dist/lib/index.js +159 -33
  28. package/dist/lib/utils.js +1 -1
  29. package/dist/report/demo.html +5449 -0
  30. package/dist/report/index.css +445 -0
  31. package/dist/report/index.html +479 -0
  32. package/dist/report/index.js +1 -0
  33. package/dist/report/multi.html +24279 -0
  34. package/dist/types/component/misc.d.ts +1 -0
  35. package/dist/types/component/panel-title.d.ts +1 -0
  36. package/dist/types/component/sidebar.d.ts +0 -1
  37. package/dist/types/component/store.d.ts +7 -6
  38. package/dist/types/index.d.ts +11 -3
  39. package/html/tpl.html +29 -0
  40. package/package.json +20 -11
  41. package/dist/es/component/assets/logo-plain.js +0 -128
  42. package/dist/es/component/assets/logo-plain2.js +0 -128
  43. package/dist/es/demo-dump.json +0 -1
  44. package/dist/lib/component/assets/logo-plain.js +0 -156
  45. package/dist/lib/component/assets/logo-plain2.js +0 -156
  46. package/dist/lib/demo-dump.json +0 -1
package/dist/es/index.js CHANGED
@@ -22,27 +22,29 @@ import "./index.css";
22
22
  import DetailSide from "./component/detail-side";
23
23
  import Sidebar from "./component/sidebar";
24
24
  import { useExecutionDump } from "./component/store";
25
+ import { DownOutlined } from "@ant-design/icons";
25
26
  import { Helmet } from "@modern-js/runtime/head";
26
- import { Button, ConfigProvider, Upload, message } from "antd";
27
+ import { ConfigProvider, Dropdown, Select, Upload, message } from "antd";
27
28
  import { useEffect, useRef, useState } from "react";
29
+ import ReactDOM from "react-dom/client";
28
30
  import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
29
- import Logo from "./component/assets/logo-plain.js";
31
+ import logo from "./assets/logo-plain.0f78df8a.png";
30
32
  import DetailPanel from "./component/detail-panel";
31
33
  import GlobalHoverPreview from "./component/global-hover-preview";
34
+ import { iconForStatus, timeCostStrElement } from "./component/misc";
32
35
  import Timeline from "./component/timeline";
33
- import demoDump from "./demo-dump.json";
34
36
  const { Dragger } = Upload;
35
37
  let globalRenderCount = 1;
36
38
  function Visualizer(props) {
37
- const { dump } = props;
39
+ const { dumps, hideLogo = false } = props;
38
40
  const executionDump = useExecutionDump((store) => store.dump);
39
41
  const setGroupedDump = useExecutionDump((store) => store.setGroupedDump);
40
42
  const reset = useExecutionDump((store) => store.reset);
41
43
  const [mainLayoutChangeFlag, setMainLayoutChangeFlag] = useState(0);
42
44
  const mainLayoutChangedRef = useRef(false);
43
45
  useEffect(() => {
44
- if (dump) {
45
- setGroupedDump(dump);
46
+ if (dumps) {
47
+ setGroupedDump(dumps[0]);
46
48
  }
47
49
  return () => {
48
50
  reset();
@@ -77,7 +79,7 @@ function Visualizer(props) {
77
79
  if (typeof result === "string") {
78
80
  try {
79
81
  const data = JSON.parse(result);
80
- setGroupedDump(data);
82
+ setGroupedDump(data[0]);
81
83
  } catch (e2) {
82
84
  console.error(e2);
83
85
  message.error("failed to parse dump data", e2.message);
@@ -89,30 +91,31 @@ function Visualizer(props) {
89
91
  return false;
90
92
  }
91
93
  };
92
- const loadDemoDump = () => {
93
- setGroupedDump(demoDump);
94
- };
95
94
  let mainContent;
96
95
  if (!executionDump) {
97
- mainContent = /* @__PURE__ */ jsxs("div", { className: "main-right uploader-wrapper", children: [
98
- /* @__PURE__ */ jsxs(Dragger, __spreadProps(__spreadValues({ className: "uploader" }, uploadProps), { children: [
99
- /* @__PURE__ */ jsx("p", { className: "ant-upload-drag-icon", children: /* @__PURE__ */ jsx(Logo, { style: { width: 100, height: 100, margin: "auto" } }) }),
100
- /* @__PURE__ */ jsxs("p", { className: "ant-upload-text", children: [
101
- "Click or drag the",
102
- " ",
103
- /* @__PURE__ */ jsx("b", { children: /* @__PURE__ */ jsx("i", { children: ".web-dump.json" }) }),
104
- " ",
105
- "file into this area."
106
- ] }),
107
- /* @__PURE__ */ jsxs("p", { className: "ant-upload-text", children: [
108
- "The latest dump file is usually placed in",
109
- " ",
110
- /* @__PURE__ */ jsx("b", { children: /* @__PURE__ */ jsx("i", { children: "./midscene_run/report" }) })
111
- ] }),
112
- /* @__PURE__ */ jsx("p", { className: "ant-upload-text", children: "All data will be processed locally by the browser. No data will be sent to the server." })
113
- ] })),
114
- /* @__PURE__ */ jsx("div", { className: "demo-loader", children: /* @__PURE__ */ jsx(Button, { type: "link", onClick: loadDemoDump, children: "Load Demo" }) })
115
- ] });
96
+ mainContent = /* @__PURE__ */ jsx("div", { className: "main-right uploader-wrapper", children: /* @__PURE__ */ jsxs(Dragger, __spreadProps(__spreadValues({ className: "uploader" }, uploadProps), { children: [
97
+ /* @__PURE__ */ jsx("p", { className: "ant-upload-drag-icon", children: /* @__PURE__ */ jsx(
98
+ "img",
99
+ {
100
+ alt: "Midscene_logo",
101
+ style: { width: 80, margin: "auto" },
102
+ src: logo
103
+ }
104
+ ) }),
105
+ /* @__PURE__ */ jsxs("p", { className: "ant-upload-text", children: [
106
+ "Click or drag the",
107
+ " ",
108
+ /* @__PURE__ */ jsx("b", { children: /* @__PURE__ */ jsx("i", { children: ".web-dump.json" }) }),
109
+ " ",
110
+ "file into this area."
111
+ ] }),
112
+ /* @__PURE__ */ jsxs("p", { className: "ant-upload-text", children: [
113
+ "The latest dump file is usually placed in",
114
+ " ",
115
+ /* @__PURE__ */ jsx("b", { children: /* @__PURE__ */ jsx("i", { children: "./midscene_run/report" }) })
116
+ ] }),
117
+ /* @__PURE__ */ jsx("p", { className: "ant-upload-text", children: "All data will be processed locally by the browser. No data will be sent to the server." })
118
+ ] })) });
116
119
  } else {
117
120
  mainContent = /* @__PURE__ */ jsxs(
118
121
  PanelGroup,
@@ -125,7 +128,7 @@ function Visualizer(props) {
125
128
  }
126
129
  },
127
130
  children: [
128
- /* @__PURE__ */ jsx(Panel, { maxSize: 95, defaultSize: 20, children: /* @__PURE__ */ jsx(Sidebar, { hideLogo: props == null ? void 0 : props.hideLogo, logoAction: props == null ? void 0 : props.logoAction }) }),
131
+ /* @__PURE__ */ jsx(Panel, { maxSize: 95, defaultSize: 20, children: /* @__PURE__ */ jsx(Sidebar, { logoAction: props == null ? void 0 : props.logoAction }) }),
129
132
  /* @__PURE__ */ jsx(
130
133
  PanelResizeHandle,
131
134
  {
@@ -182,6 +185,25 @@ function Visualizer(props) {
182
185
  globalRenderCount += 1;
183
186
  };
184
187
  }, []);
188
+ const selectOptions = dumps == null ? void 0 : dumps.map((dump, index) => ({
189
+ value: index,
190
+ label: `${dump.groupName} - ${dump.groupDescription}`,
191
+ groupName: dump.groupName,
192
+ groupDescription: dump.groupDescription
193
+ }));
194
+ const selectWidget = selectOptions && selectOptions.length > 1 ? /* @__PURE__ */ jsx(
195
+ Select,
196
+ {
197
+ options: selectOptions,
198
+ defaultValue: 0,
199
+ onChange: (value) => {
200
+ const dump = dumps[value];
201
+ setGroupedDump(dump);
202
+ },
203
+ defaultOpen: true,
204
+ style: { width: "100%" }
205
+ }
206
+ ) : null;
185
207
  return /* @__PURE__ */ jsxs(
186
208
  ConfigProvider,
187
209
  {
@@ -196,13 +218,34 @@ function Visualizer(props) {
196
218
  }
197
219
  },
198
220
  children: [
199
- /* @__PURE__ */ jsx(Helmet, { children: /* @__PURE__ */ jsx("title", { children: "Midscene.js - Visualization Tool" }) }),
200
- /* @__PURE__ */ jsx(
221
+ /* @__PURE__ */ jsx(Helmet, { children: /* @__PURE__ */ jsx("title", { children: "Visualization - Midscene.js" }) }),
222
+ /* @__PURE__ */ jsxs(
201
223
  "div",
202
224
  {
203
225
  className: "page-container",
204
226
  style: { height: containerHeight },
205
- children: mainContent
227
+ children: [
228
+ hideLogo ? null : /* @__PURE__ */ jsxs("div", { className: "page-nav", children: [
229
+ /* @__PURE__ */ jsx("div", { className: "logo", children: /* @__PURE__ */ jsx(
230
+ "img",
231
+ {
232
+ alt: "Midscene_logo",
233
+ src: "https://lf3-static.bytednsdoc.com/obj/eden-cn/vhaeh7vhabf/logo-light-with-text.png"
234
+ }
235
+ ) }),
236
+ /* @__PURE__ */ jsx(
237
+ PlaywrightCaseSelector,
238
+ {
239
+ dumps: props.dumps,
240
+ selected: executionDump,
241
+ onSelect: (dump) => {
242
+ setGroupedDump(dump);
243
+ }
244
+ }
245
+ )
246
+ ] }),
247
+ mainContent
248
+ ]
206
249
  },
207
250
  `render-${globalRenderCount}`
208
251
  ),
@@ -211,7 +254,90 @@ function Visualizer(props) {
211
254
  }
212
255
  );
213
256
  }
214
- var src_default = Visualizer;
257
+ function PlaywrightCaseSelector(props) {
258
+ if (!props.dumps || props.dumps.length <= 1)
259
+ return null;
260
+ const nameForDump = (dump) => `${dump.groupName} - ${dump.groupDescription}`;
261
+ const items = (props.dumps || []).map((dump, index) => {
262
+ var _a, _b;
263
+ const status = iconForStatus((_a = dump.attributes) == null ? void 0 : _a.playwright_test_status);
264
+ const costStr = (_b = dump.attributes) == null ? void 0 : _b.playwright_test_duration;
265
+ const cost = costStr ? /* @__PURE__ */ jsxs("span", { className: "cost-str", children: [
266
+ " ",
267
+ "(",
268
+ timeCostStrElement(Number.parseInt(costStr, 10)),
269
+ ")"
270
+ ] }, index) : null;
271
+ return {
272
+ key: index,
273
+ label: /* @__PURE__ */ jsx(
274
+ "a",
275
+ {
276
+ onClick: (e) => {
277
+ e.preventDefault();
278
+ if (props.onSelect) {
279
+ props.onSelect(dump);
280
+ }
281
+ },
282
+ children: /* @__PURE__ */ jsxs("div", { children: [
283
+ status,
284
+ " ",
285
+ nameForDump(dump),
286
+ cost
287
+ ] })
288
+ }
289
+ )
290
+ };
291
+ });
292
+ const btnName = props.selected ? nameForDump(props.selected) : "Select a case";
293
+ return /* @__PURE__ */ jsx("div", { className: "playwright-case-selector", children: /* @__PURE__ */ jsx(Dropdown, { menu: { items }, children: /* @__PURE__ */ jsxs("a", { onClick: (e) => e.preventDefault(), children: [
294
+ btnName,
295
+ " ",
296
+ /* @__PURE__ */ jsx(DownOutlined, {})
297
+ ] }) }) });
298
+ }
299
+ function mount(id) {
300
+ const element = document.getElementById(id);
301
+ if (!element) {
302
+ throw new Error(`failed to get element for id: ${id}`);
303
+ }
304
+ const root = ReactDOM.createRoot(element);
305
+ const dumpElements = document.querySelectorAll(
306
+ 'script[type="midscene_web_dump"]'
307
+ );
308
+ const reportDump = [];
309
+ Array.from(dumpElements).filter((el) => {
310
+ const textContent = el.textContent;
311
+ if (!textContent) {
312
+ console.warn("empty content in script tag", el);
313
+ }
314
+ return !!textContent;
315
+ }).forEach((el) => {
316
+ const attributes = {};
317
+ Array.from(el.attributes).forEach((attr) => {
318
+ const { name, value } = attr;
319
+ const valueDecoded = decodeURIComponent(value);
320
+ if (name.startsWith("playwright_")) {
321
+ attributes[attr.name] = valueDecoded;
322
+ }
323
+ });
324
+ const content = el.textContent;
325
+ let jsonContent;
326
+ try {
327
+ jsonContent = JSON.parse(content);
328
+ jsonContent.attributes = attributes;
329
+ reportDump.push(jsonContent);
330
+ } catch (e) {
331
+ console.error(el);
332
+ console.error("failed to parse json content", e);
333
+ }
334
+ });
335
+ root.render(/* @__PURE__ */ jsx(Visualizer, { dumps: reportDump }));
336
+ }
337
+ var src_default = {
338
+ mount,
339
+ Visualizer
340
+ };
215
341
  export {
216
342
  Visualizer,
217
343
  src_default as default
package/dist/es/utils.js CHANGED
@@ -24,7 +24,7 @@ function insightDumpToExecutionDump(insightDump) {
24
24
  const task = {
25
25
  type: "Insight",
26
26
  subType: insightDump2.type === "locate" ? "Locate" : "Query",
27
- status: insightDump2.error ? "fail" : "success",
27
+ status: insightDump2.error ? "failed" : "success",
28
28
  param: __spreadProps(__spreadValues(__spreadValues({}, insightDump2.userQuery.element ? { query: insightDump2.userQuery } : {}), insightDump2.userQuery.dataDemand ? { dataDemand: insightDump2.userQuery.dataDemand } : {}), {
29
29
  insight: {}
30
30
  }),
package/dist/index.css ADDED
@@ -0,0 +1,445 @@
1
+ /* src/index.less */
2
+ html,
3
+ body {
4
+ padding: 0;
5
+ margin: 0;
6
+ line-height: 1;
7
+ }
8
+ .rspress-nav {
9
+ transition: 0.2s;
10
+ }
11
+ :root {
12
+ --modern-sidebar-width: 0 !important;
13
+ --modern-aside-width: 0 !important;
14
+ --modern-preview-padding: 0 !important;
15
+ }
16
+ .modern-doc-layout,
17
+ .modern-doc {
18
+ width: 100% !important;
19
+ margin: 0 !important;
20
+ padding: 0 !important;
21
+ height: 100vh;
22
+ }
23
+ .modern-sidebar,
24
+ header.w-full {
25
+ display: none !important;
26
+ }
27
+ .modern-doc-container {
28
+ padding: 0 !important;
29
+ }
30
+ footer.mt-8 {
31
+ display: none;
32
+ }
33
+ .page-container {
34
+ display: flex;
35
+ flex-direction: column;
36
+ height: 100%;
37
+ color: #3b3b3b;
38
+ font-family:
39
+ -apple-system,
40
+ BlinkMacSystemFont,
41
+ "Segoe UI",
42
+ "Noto Sans",
43
+ Helvetica,
44
+ Arial,
45
+ sans-serif,
46
+ "Apple Color Emoji",
47
+ "Segoe UI Emoji";
48
+ font-size: 14px;
49
+ border-top: 1px solid #E5E5E5;
50
+ border-bottom: 1px solid #E5E5E5;
51
+ font-synthesis: style;
52
+ text-rendering: optimizelegibility;
53
+ -webkit-font-smoothing: antialiased;
54
+ line-height: 1.5;
55
+ }
56
+ .page-container blockquote,
57
+ .page-container dl,
58
+ .page-container dd,
59
+ .page-container h1,
60
+ .page-container h2,
61
+ .page-container h3,
62
+ .page-container h4,
63
+ .page-container h5,
64
+ .page-container h6,
65
+ .page-container hr,
66
+ .page-container figure,
67
+ .page-container p,
68
+ .page-container pre {
69
+ margin: 0;
70
+ }
71
+ .page-nav {
72
+ height: 30px;
73
+ padding: 10px;
74
+ border-bottom: 1px solid #E5E5E5;
75
+ display: flex;
76
+ flex-direction: row;
77
+ }
78
+ .page-nav .logo img {
79
+ height: 20px;
80
+ line-height: 30px;
81
+ vertical-align: baseline;
82
+ vertical-align: -webkit-baseline-middle;
83
+ }
84
+ .page-nav .playwright-case-selector {
85
+ margin-left: 20px;
86
+ line-height: 30px;
87
+ width: 320px;
88
+ }
89
+ .cost-str {
90
+ color: #777;
91
+ }
92
+ .ant-layout {
93
+ flex-grow: 1;
94
+ height: 100%;
95
+ }
96
+ .main-right {
97
+ display: flex;
98
+ flex-direction: column;
99
+ width: 100%;
100
+ height: 100%;
101
+ box-sizing: border-box;
102
+ }
103
+ .main-right .main-content {
104
+ display: flex;
105
+ flex-direction: row;
106
+ flex-grow: 1;
107
+ overflow: hidden;
108
+ background: #FFF;
109
+ }
110
+ .main-right.uploader-wrapper {
111
+ box-sizing: border-box;
112
+ margin: auto;
113
+ max-width: 800px;
114
+ flex-direction: column;
115
+ justify-content: center;
116
+ }
117
+ .main-right.uploader-wrapper .uploader {
118
+ width: 100%;
119
+ }
120
+ .main-right.uploader-wrapper .demo-loader {
121
+ width: 100%;
122
+ text-align: center;
123
+ margin-top: 10px;
124
+ }
125
+ .main-right .main-canvas-container {
126
+ flex-grow: 1;
127
+ height: 100%;
128
+ background: #ffffff;
129
+ overflow-x: hidden;
130
+ overflow-y: scroll;
131
+ border-left: 1px solid #E5E5E5;
132
+ }
133
+ .main-right .main-side {
134
+ box-sizing: border-box;
135
+ overflow-y: scroll;
136
+ height: 100%;
137
+ }
138
+ .main-right .json-content {
139
+ word-wrap: break-word;
140
+ white-space: pre-wrap;
141
+ }
142
+
143
+ /* src/component/detail-side.less */
144
+ .detail-side h2 {
145
+ padding-top: 0;
146
+ margin-top: 0;
147
+ margin-bottom: 4px;
148
+ }
149
+ .detail-side .ant-tag {
150
+ margin-top: 2px;
151
+ }
152
+ .detail-side .meta-kv {
153
+ padding: 10px 10px calc(10px + 4px);
154
+ }
155
+ .detail-side .meta-kv .meta {
156
+ box-sizing: border-box;
157
+ padding: 2px 0;
158
+ width: 100%;
159
+ display: flex;
160
+ flex-direction: row;
161
+ line-height: 1.5;
162
+ }
163
+ .detail-side .meta-kv .meta .meta-key {
164
+ width: 100px;
165
+ text-align: right;
166
+ padding-right: 16px;
167
+ }
168
+ .detail-side .meta-kv .meta .meta-value {
169
+ flex: 1 1;
170
+ }
171
+ .detail-side .item-list {
172
+ padding: 10px 10px;
173
+ cursor: default;
174
+ margin-bottom: 10px;
175
+ }
176
+ .detail-side .item-list .item {
177
+ padding: 16px 10px 10px;
178
+ transition: 0.1s;
179
+ border: 1px solid #DDD;
180
+ border-radius: 5px;
181
+ margin-bottom: 10px;
182
+ position: relative;
183
+ }
184
+ .detail-side .item-list .item.item-lite {
185
+ border: none;
186
+ padding: 0;
187
+ }
188
+ .detail-side .item-list .item-highlight {
189
+ color: #FFF;
190
+ }
191
+ .detail-side .item-list .item-highlight .subtitle {
192
+ color: #CCC;
193
+ }
194
+ .detail-side .item-list .item-extra {
195
+ position: absolute;
196
+ right: 10px;
197
+ top: 10px;
198
+ color: #777;
199
+ }
200
+ .detail-side .item-list .title-right-padding {
201
+ padding-right: 15px;
202
+ }
203
+ .detail-side .item-list .title {
204
+ font-size: 18px;
205
+ font-weight: bold;
206
+ margin-bottom: 5px;
207
+ }
208
+ .detail-side .item-list .title .title-tag {
209
+ display: inline-block;
210
+ margin-left: 6px;
211
+ color: #777;
212
+ font-size: 14px;
213
+ line-height: 18px;
214
+ }
215
+ .detail-side .item-list .subtitle {
216
+ font-weight: normal;
217
+ font-size: 14px;
218
+ color: #777;
219
+ }
220
+ .detail-side .item-list .description {
221
+ margin-top: 5px;
222
+ }
223
+ .detail-side .item-list .description-content {
224
+ font-size: 14px;
225
+ margin-top: 10px;
226
+ white-space: break-spaces;
227
+ word-wrap: break-word;
228
+ margin: 0;
229
+ }
230
+ .detail-side .item-list .element-button:hover {
231
+ color: #fff;
232
+ background: #F9483E;
233
+ }
234
+ .detail-side .item-list .section-button:hover {
235
+ color: #fff;
236
+ background: #01204E;
237
+ }
238
+ .detail-side pre {
239
+ text-wrap: balance;
240
+ }
241
+ .detail-side .item-list-space-up {
242
+ margin-top: 10px;
243
+ }
244
+
245
+ /* src/component/panel-title.less */
246
+ .panel-title {
247
+ background: #F8F8F8;
248
+ border-top: 1px solid #E5E5E5;
249
+ border-bottom: 1px solid #E5E5E5;
250
+ margin-top: -1px;
251
+ padding: 2px 10px;
252
+ }
253
+ .panel-title .task-list-name {
254
+ font-weight: bold;
255
+ }
256
+
257
+ /* src/component/sidebar.less */
258
+ .side-bar {
259
+ display: flex;
260
+ flex-direction: column;
261
+ justify-content: space-between;
262
+ width: 100%;
263
+ height: 100%;
264
+ border-right: 1px solid #E5E5E5;
265
+ overflow: auto;
266
+ background: #F8F8F8;
267
+ box-sizing: border-box;
268
+ }
269
+ .side-bar .task-meta-section {
270
+ margin-top: 6px;
271
+ }
272
+ .side-bar .task-meta {
273
+ color: #777;
274
+ font-weight: normal;
275
+ padding-left: 10px;
276
+ }
277
+ .side-bar .side-seperator {
278
+ border-top: 1px solid none;
279
+ }
280
+ .side-bar .side-seperator.side-seperator-line {
281
+ border-top: 1px solid #E5E5E5;
282
+ }
283
+ .side-bar .side-seperator.side-seperator-space-up {
284
+ margin-top: 10px;
285
+ }
286
+ .side-bar .side-seperator.side-seperator-space-down {
287
+ margin-bottom: 10px;
288
+ }
289
+ .side-bar .side-sub-title {
290
+ padding: 0 10px;
291
+ margin-bottom: 6px;
292
+ }
293
+ .side-bar .name-status {
294
+ font-size: 12px;
295
+ display: inline-block;
296
+ margin-right: 6px;
297
+ }
298
+ .side-bar .side-item {
299
+ cursor: pointer;
300
+ transition: 0.1s;
301
+ padding: 2px 0;
302
+ }
303
+ .side-bar .side-item:hover {
304
+ background: #dcdcdc80;
305
+ }
306
+ .side-bar .side-item.selected {
307
+ background: #bfc4da80;
308
+ }
309
+ .side-bar .side-item .side-item-content {
310
+ padding: 0 10px 0 calc(10px + 18px);
311
+ }
312
+ .side-bar .side-item-name {
313
+ padding: 0 10px 0 calc(10px + 18px);
314
+ position: relative;
315
+ display: flex;
316
+ justify-content: space-between;
317
+ }
318
+ .side-bar .side-item-name .status-icon {
319
+ position: absolute;
320
+ left: 10px;
321
+ display: inline-block;
322
+ color: #AAA;
323
+ font-size: 12px;
324
+ line-height: 10px;
325
+ top: 50%;
326
+ margin-top: -5px;
327
+ }
328
+ .side-bar .side-item-name .status-text {
329
+ color: #777;
330
+ }
331
+ .side-bar .bottom-controls {
332
+ padding: 10px 10px;
333
+ text-align: left;
334
+ text-align: center;
335
+ }
336
+
337
+ /* src/component/detail-panel.less */
338
+ .detail-panel {
339
+ display: flex;
340
+ flex-direction: column;
341
+ height: 100%;
342
+ padding: 10px;
343
+ background: #FFF;
344
+ }
345
+ .detail-panel .ant-segmented {
346
+ padding: 0;
347
+ }
348
+ .detail-panel .view-switcher {
349
+ margin-bottom: 10px;
350
+ flex-shrink: 0;
351
+ }
352
+ .detail-panel .detail-content {
353
+ box-sizing: border-box;
354
+ justify-content: center;
355
+ flex-direction: column;
356
+ display: flex;
357
+ flex-grow: 1;
358
+ }
359
+ .detail-panel .blackboard {
360
+ margin: 0 auto;
361
+ }
362
+ .detail-panel .screenshot-item-wrapper {
363
+ display: flex;
364
+ flex-wrap: wrap;
365
+ justify-content: space-around;
366
+ align-items: center;
367
+ }
368
+ .detail-panel .screenshot-item {
369
+ margin-bottom: 10px;
370
+ }
371
+ .detail-panel .screenshot-item .screenshot-item-title {
372
+ margin-bottom: 5px;
373
+ }
374
+ .detail-panel .screenshot-item img {
375
+ border: 1px solid #888;
376
+ max-width: 100%;
377
+ }
378
+
379
+ /* src/component/blackboard.less */
380
+ .blackboard .footer {
381
+ color: #aaa;
382
+ }
383
+ .blackboard ul {
384
+ padding-left: 0px;
385
+ }
386
+ .blackboard li {
387
+ list-style: none;
388
+ }
389
+ .blackboard .bottom-tip {
390
+ height: 30px;
391
+ }
392
+ .blackboard .bottom-tip-item {
393
+ max-width: 500px;
394
+ color: #AAA;
395
+ text-overflow: ellipsis;
396
+ word-wrap: break-word;
397
+ }
398
+ .blackboard-filter {
399
+ margin: 10px 0;
400
+ }
401
+ .blackboard-main-content canvas {
402
+ width: 100%;
403
+ border: 1px solid #888;
404
+ }
405
+
406
+ /* src/component/global-hover-preview.less */
407
+ .global-hover-preview {
408
+ position: fixed;
409
+ display: block;
410
+ max-width: 400px;
411
+ max-height: 400px;
412
+ overflow: hidden;
413
+ z-index: 10;
414
+ text-align: center;
415
+ border: 1px solid #E5E5E5;
416
+ box-sizing: border-box;
417
+ background: #F8F8F8;
418
+ box-shadow: 1px 1px 5px 0 rgba(0, 0, 0, 0.2);
419
+ }
420
+ .global-hover-preview img {
421
+ max-width: 400px;
422
+ max-height: 400px;
423
+ width: auto;
424
+ height: auto;
425
+ }
426
+
427
+ /* src/component/timeline.less */
428
+ .timeline-wrapper {
429
+ flex-basis: 110px;
430
+ flex-grow: 0;
431
+ flex-shrink: 0;
432
+ width: 100%;
433
+ height: 100%;
434
+ border-bottom: 1px solid #E5E5E5;
435
+ position: relative;
436
+ box-sizing: border-box;
437
+ }
438
+ .timeline-wrapper .timeline-canvas-wrapper {
439
+ width: 100%;
440
+ height: 100%;
441
+ }
442
+ .timeline-wrapper canvas {
443
+ width: 100%;
444
+ height: 100%;
445
+ }