@forge-kit/plugin-source-map-prase 0.0.1 → 0.0.3
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.
- package/dist/app.js +3 -5
- package/dist/node.js +2556 -0
- package/package.json +5 -3
- package/src/App.less +43 -74
- package/src/App.tsx +98 -54
- package/src/components/map-input-panel/index.less +71 -79
- package/src/components/map-input-panel/index.tsx +65 -69
- package/src/components/panel-card/index.less +21 -0
- package/src/components/panel-card/index.tsx +6 -8
- package/src/components/trace-chain/index.less +126 -125
- package/src/components/trace-chain/index.tsx +25 -24
- package/src/main.tsx +1 -1
- package/src/node/index.ts +6 -0
- package/src/{utils/source-map → node/trace/core}/base/registry.ts +14 -11
- package/src/{utils/source-map → node/trace/core}/base/types.ts +1 -0
- package/src/node/trace/core/domain/chain-slots.ts +33 -0
- package/src/{utils/source-map → node/trace/core}/domain/source-content.ts +15 -2
- package/src/node/trace/core/domain/trace-resolver.ts +179 -0
- package/src/node/trace/core/domain/view-model.ts +57 -0
- package/src/node/trace/resolve/context.ts +59 -0
- package/src/node/trace/resolve/index.ts +97 -0
- package/src/node/trace/resolve/input.ts +35 -0
- package/src/node/trace/resolve/snippet-limit.ts +35 -0
- package/src/node/trace/resolve/types.ts +37 -0
- package/src/node/trace/runner.ts +149 -0
- package/src/shared/trace-common.ts +104 -0
- package/src/shared/trace-contract.ts +29 -0
- package/src/types.ts +19 -0
- package/src/utils/trace-ui/index.ts +12 -0
- package/src/utils/trace-ui/state.ts +81 -0
- package/src/utils/source-map/domain/chain-slots.ts +0 -59
- package/src/utils/source-map/domain/trace-resolver.ts +0 -165
- package/src/utils/source-map/domain/view-model.ts +0 -20
- package/src/utils/source-map/facade/source-map-utils.ts +0 -212
- package/src/utils/source-map/index.ts +0 -18
- /package/src/{utils/source-map → node/trace/core}/base/constants.ts +0 -0
- /package/src/{utils/source-map → node/trace/core}/base/path.ts +0 -0
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import {Box, Button, Flex, Text, TextField} from "@forge-kit/component";
|
|
3
3
|
import {PanelCard} from "@/components/panel-card";
|
|
4
|
+
import type {ForgeKitBridge, MapInputConfig} from "@/types";
|
|
4
5
|
import {noop} from "@/utils";
|
|
6
|
+
import classNames from "classnames";
|
|
5
7
|
import "./index.less";
|
|
6
8
|
|
|
7
|
-
interface MapInputConfig {
|
|
8
|
-
entryLine: string
|
|
9
|
-
entryColumn: string
|
|
10
|
-
contextLineRadius: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
9
|
interface MapInputPanelProps {
|
|
10
|
+
className?: string
|
|
14
11
|
chainDepth: number
|
|
15
12
|
entryFileName: string
|
|
16
13
|
inputConfig: MapInputConfig
|
|
17
14
|
onInputConfigChange: (next: MapInputConfig) => void
|
|
18
15
|
onResolve: () => void
|
|
19
|
-
|
|
16
|
+
onAppendMapFilePath: (filePath: string) => void
|
|
20
17
|
}
|
|
21
18
|
|
|
22
19
|
interface MapInputFieldProps {
|
|
@@ -45,25 +42,27 @@ const MapInputField: React.FC<MapInputFieldProps> = (props) => {
|
|
|
45
42
|
|
|
46
43
|
export const MapInputPanel: React.FC<MapInputPanelProps> = (props) => {
|
|
47
44
|
const {
|
|
45
|
+
className,
|
|
48
46
|
chainDepth,
|
|
49
47
|
entryFileName,
|
|
50
48
|
inputConfig,
|
|
51
49
|
onInputConfigChange,
|
|
52
50
|
onResolve,
|
|
53
|
-
|
|
51
|
+
onAppendMapFilePath,
|
|
54
52
|
} = props
|
|
55
53
|
|
|
56
|
-
const openFilePicker = () => {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
const openFilePicker = async () => {
|
|
55
|
+
const openFileDialog = (window as Window & {forgeKit?: ForgeKitBridge}).forgeKit?.openFileDialog
|
|
56
|
+
if (!openFileDialog) return
|
|
57
|
+
|
|
58
|
+
const selected = await openFileDialog({
|
|
59
|
+
multiple: true,
|
|
60
|
+
filters: [{name: "Source Map", extensions: ["map", "json"]}],
|
|
61
|
+
})
|
|
62
|
+
const paths = Array.isArray(selected) ? selected : (selected ? [selected] : [])
|
|
63
|
+
paths.forEach((filePath) => {
|
|
64
|
+
if (typeof filePath === "string") onAppendMapFilePath(filePath)
|
|
65
|
+
})
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
const handleInputConfigChange = (field: keyof MapInputConfig, value: string) => {
|
|
@@ -71,58 +70,55 @@ export const MapInputPanel: React.FC<MapInputPanelProps> = (props) => {
|
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
return (
|
|
74
|
-
<PanelCard
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
<Text className="input-summary-label">链路层数</Text>
|
|
87
|
-
<Text className="input-summary-value">{`${chainDepth} 层`}</Text>
|
|
88
|
-
</Box>
|
|
89
|
-
</Box>
|
|
73
|
+
<PanelCard title="MAP INPUT" className={classNames("input-panel-card", className)}>
|
|
74
|
+
<Flex className="input-panel-content" direction='column' gap="3">
|
|
75
|
+
<Box className="input-summary-grid">
|
|
76
|
+
<Box className="input-summary-item">
|
|
77
|
+
<Text className="input-summary-label">入口 Map</Text>
|
|
78
|
+
<Text className="input-summary-value">{entryFileName || "-"}</Text>
|
|
79
|
+
</Box>
|
|
80
|
+
<Box className="input-summary-item">
|
|
81
|
+
<Text className="input-summary-label">链路层数</Text>
|
|
82
|
+
<Text className="input-summary-value">{`${chainDepth} 层`}</Text>
|
|
83
|
+
</Box>
|
|
84
|
+
</Box>
|
|
90
85
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
86
|
+
<Box className="input-hints">
|
|
87
|
+
<Text className="input-hints-title">使用说明</Text>
|
|
88
|
+
<Text className="input-hints-item">1. 按解析链路顺序添加 map 文件。</Text>
|
|
89
|
+
<Text className="input-hints-item">2. 入口 Map 会自动取第一层的 output file。</Text>
|
|
90
|
+
<Text className="input-hints-item">3. TRACE CHAIN 支持拖动序号来切换 map 顺序。</Text>
|
|
91
|
+
</Box>
|
|
97
92
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
93
|
+
<Box className="input-footer">
|
|
94
|
+
<Flex gap="2" className="entry-row" align="end" wrap="wrap">
|
|
95
|
+
<MapInputField
|
|
96
|
+
title="入口行号"
|
|
97
|
+
min={1}
|
|
98
|
+
value={inputConfig.entryLine}
|
|
99
|
+
onChange={(value) => handleInputConfigChange("entryLine", value)}
|
|
100
|
+
/>
|
|
101
|
+
<MapInputField
|
|
102
|
+
title="入口列号"
|
|
103
|
+
min={0}
|
|
104
|
+
value={inputConfig.entryColumn}
|
|
105
|
+
onChange={(value) => handleInputConfigChange("entryColumn", value)}
|
|
106
|
+
/>
|
|
107
|
+
<MapInputField
|
|
108
|
+
title="上下文行数"
|
|
109
|
+
min={0}
|
|
110
|
+
value={inputConfig.contextLineRadius}
|
|
111
|
+
onChange={(value) => handleInputConfigChange("contextLineRadius", value)}
|
|
112
|
+
/>
|
|
113
|
+
</Flex>
|
|
114
|
+
<Flex className="entry-actions" direction="column" justify="center" align="center" gap="2">
|
|
115
|
+
<Flex className="entry-action-row" gap="2" wrap="wrap" justify="center" align="center">
|
|
116
|
+
<Button size="2" radius="large" onClick={onResolve}>解析链路</Button>
|
|
117
|
+
<Button size="2" radius="large" variant="outline" onClick={openFilePicker}>添加 map 文件</Button>
|
|
118
|
+
</Flex>
|
|
119
|
+
</Flex>
|
|
120
|
+
</Box>
|
|
121
|
+
</Flex>
|
|
126
122
|
</PanelCard>
|
|
127
123
|
)
|
|
128
124
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.panel-card {
|
|
2
|
+
display: flex !important;
|
|
3
|
+
|
|
4
|
+
.panel-shell {
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
flex: 1;
|
|
7
|
+
|
|
8
|
+
.panel-title {
|
|
9
|
+
display: block;
|
|
10
|
+
padding: 0 0 var(--space-3);
|
|
11
|
+
margin-bottom: var(--space-3);
|
|
12
|
+
border-bottom: solid 1px var(--gray-5);
|
|
13
|
+
font-size: 12px;
|
|
14
|
+
letter-spacing: 0.1em;
|
|
15
|
+
color: var(--gray-11);
|
|
16
|
+
text-transform: uppercase;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import {Card, Flex, Text} from "@forge-kit/component";
|
|
3
|
+
import classNames from 'classnames'
|
|
4
|
+
import './index.less'
|
|
3
5
|
|
|
4
6
|
interface PanelCardProps {
|
|
5
7
|
title: string
|
|
6
|
-
|
|
7
|
-
contentClassName: string
|
|
8
|
-
contentProps?: Omit<React.ComponentProps<typeof Flex>, "className" | "children">
|
|
8
|
+
className?: string
|
|
9
9
|
children?: React.ReactNode
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export const PanelCard: React.FC<PanelCardProps> = (props) => {
|
|
13
|
-
const {title,
|
|
13
|
+
const {title, className, children} = props
|
|
14
14
|
|
|
15
15
|
return (
|
|
16
|
-
<Card className={
|
|
16
|
+
<Card className={classNames('panel-card', className)} >
|
|
17
17
|
<Flex className="panel-shell" direction="column">
|
|
18
18
|
<Text className="panel-title">{title}</Text>
|
|
19
|
-
|
|
20
|
-
{children}
|
|
21
|
-
</Flex>
|
|
19
|
+
<>{children}</>
|
|
22
20
|
</Flex>
|
|
23
21
|
</Card>
|
|
24
22
|
)
|
|
@@ -1,128 +1,129 @@
|
|
|
1
|
-
.trace-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
.trace-panel-card {
|
|
2
|
+
.trace-panel-content {
|
|
3
|
+
flex: 1;
|
|
4
|
+
overflow: auto;
|
|
5
|
+
|
|
6
|
+
.trace-chain-root {
|
|
7
|
+
flex: 1;
|
|
8
|
+
min-height: 0;
|
|
9
|
+
height: 100%;
|
|
10
|
+
overflow: auto;
|
|
11
|
+
padding: 4px;
|
|
12
|
+
|
|
13
|
+
.trace-chain-empty {
|
|
14
|
+
min-height: 180px;
|
|
15
|
+
border: 1px dashed var(--gray-6);
|
|
16
|
+
border-radius: var(--radius-3);
|
|
17
|
+
color: var(--gray-11);
|
|
18
|
+
font-size: 13px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.trace-chain-item {
|
|
22
|
+
margin-bottom: 10px;
|
|
23
|
+
|
|
24
|
+
&[data-dragging="true"] {
|
|
25
|
+
opacity: 0.85;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.trace-chain-node {
|
|
29
|
+
padding: 10px;
|
|
30
|
+
border-radius: var(--radius-3);
|
|
31
|
+
border: 1px solid var(--gray-6);
|
|
32
|
+
background: linear-gradient(160deg, color-mix(in srgb, var(--gray-3) 90%, transparent), color-mix(in srgb, var(--gray-2) 90%, transparent));
|
|
33
|
+
|
|
34
|
+
.trace-chain-drag-handle {
|
|
35
|
+
min-width: 46px;
|
|
36
|
+
height: 24px;
|
|
37
|
+
border-radius: 999px;
|
|
38
|
+
border: 1px solid var(--gray-7);
|
|
39
|
+
background: var(--gray-4);
|
|
40
|
+
color: var(--gray-12);
|
|
41
|
+
font-size: 12px;
|
|
42
|
+
font-weight: 600;
|
|
43
|
+
user-select: none;
|
|
44
|
+
cursor: grab;
|
|
45
|
+
|
|
46
|
+
&:active {
|
|
47
|
+
cursor: grabbing;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.trace-chain-content {
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
width: 100%;
|
|
54
|
+
|
|
55
|
+
.trace-chain-header {
|
|
56
|
+
width: 100%;
|
|
57
|
+
|
|
58
|
+
.trace-chain-main-title {
|
|
59
|
+
display: block;
|
|
60
|
+
font-size: 13px;
|
|
61
|
+
font-weight: 700;
|
|
62
|
+
color: var(--gray-12);
|
|
63
|
+
line-height: 1.35;
|
|
64
|
+
word-break: break-all;
|
|
65
|
+
user-select: text;
|
|
66
|
+
cursor: text;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.trace-chain-row {
|
|
71
|
+
display: grid;
|
|
72
|
+
grid-template-columns: 34px minmax(0, 1fr);
|
|
73
|
+
gap: 6px;
|
|
74
|
+
padding: 2px 0;
|
|
75
|
+
|
|
76
|
+
&[data-row-type="output"] {
|
|
77
|
+
margin-top: 2px;
|
|
78
|
+
padding-top: 8px;
|
|
79
|
+
border-top: 1px dashed color-mix(in srgb, var(--gray-6) 78%, transparent);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.trace-chain-label {
|
|
83
|
+
min-width: 30px;
|
|
84
|
+
font-size: 12px;
|
|
85
|
+
color: var(--gray-10);
|
|
86
|
+
user-select: none;
|
|
87
|
+
margin-top: 1px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.trace-chain-detail {
|
|
91
|
+
min-width: 0;
|
|
92
|
+
|
|
93
|
+
.trace-chain-file {
|
|
94
|
+
font-size: 13px;
|
|
95
|
+
font-weight: 600;
|
|
96
|
+
color: var(--gray-12);
|
|
97
|
+
white-space: normal;
|
|
98
|
+
overflow-wrap: anywhere;
|
|
99
|
+
word-break: break-all;
|
|
100
|
+
user-select: text;
|
|
101
|
+
cursor: text;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.trace-chain-position {
|
|
105
|
+
font-size: 12px;
|
|
106
|
+
color: var(--gray-11);
|
|
107
|
+
white-space: normal;
|
|
108
|
+
overflow-wrap: anywhere;
|
|
109
|
+
user-select: text;
|
|
110
|
+
cursor: text;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.trace-chain-error {
|
|
116
|
+
font-size: 12px;
|
|
117
|
+
color: #ff7b7b;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.trace-chain-pending {
|
|
121
|
+
font-size: 12px;
|
|
122
|
+
color: var(--gray-10);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
14
127
|
}
|
|
15
128
|
}
|
|
16
|
-
|
|
17
|
-
.trace-chain-empty {
|
|
18
|
-
min-height: 180px;
|
|
19
|
-
border: 1px dashed var(--gray-6);
|
|
20
|
-
border-radius: var(--radius-3);
|
|
21
|
-
color: var(--gray-11);
|
|
22
|
-
font-size: 13px;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.trace-chain-node {
|
|
26
|
-
padding: 10px;
|
|
27
|
-
border-radius: var(--radius-3);
|
|
28
|
-
border: 1px solid var(--gray-6);
|
|
29
|
-
background:
|
|
30
|
-
linear-gradient(160deg, color-mix(in srgb, var(--gray-3) 90%, transparent), color-mix(in srgb, var(--gray-2) 90%, transparent));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.trace-chain-drag-handle {
|
|
34
|
-
min-width: 46px;
|
|
35
|
-
height: 24px;
|
|
36
|
-
border-radius: 999px;
|
|
37
|
-
border: 1px solid var(--gray-7);
|
|
38
|
-
background: var(--gray-4);
|
|
39
|
-
color: var(--gray-12);
|
|
40
|
-
font-size: 12px;
|
|
41
|
-
font-weight: 600;
|
|
42
|
-
user-select: none;
|
|
43
|
-
cursor: grab;
|
|
44
|
-
|
|
45
|
-
&:active {
|
|
46
|
-
cursor: grabbing;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.trace-chain-content {
|
|
51
|
-
overflow: hidden;
|
|
52
|
-
width: 100%;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.trace-chain-main-title {
|
|
56
|
-
display: block;
|
|
57
|
-
font-size: 13px;
|
|
58
|
-
font-weight: 700;
|
|
59
|
-
color: var(--gray-12);
|
|
60
|
-
line-height: 1.35;
|
|
61
|
-
word-break: break-all;
|
|
62
|
-
user-select: text;
|
|
63
|
-
cursor: text;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.trace-chain-header {
|
|
67
|
-
width: 100%;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.trace-chain-row {
|
|
71
|
-
display: grid;
|
|
72
|
-
grid-template-columns: 34px minmax(0, 1fr);
|
|
73
|
-
gap: 6px;
|
|
74
|
-
padding: 2px 0;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.trace-chain-row[data-row-type="output"] {
|
|
78
|
-
margin-top: 2px;
|
|
79
|
-
padding-top: 8px;
|
|
80
|
-
border-top: 1px dashed color-mix(in srgb, var(--gray-6) 78%, transparent);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.trace-chain-label {
|
|
84
|
-
min-width: 30px;
|
|
85
|
-
font-size: 12px;
|
|
86
|
-
color: var(--gray-10);
|
|
87
|
-
user-select: none;
|
|
88
|
-
margin-top: 1px;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.trace-chain-detail {
|
|
92
|
-
min-width: 0;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.trace-chain-output-head {
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
.trace-chain-file {
|
|
99
|
-
font-size: 13px;
|
|
100
|
-
font-weight: 600;
|
|
101
|
-
color: var(--gray-12);
|
|
102
|
-
white-space: normal;
|
|
103
|
-
overflow-wrap: anywhere;
|
|
104
|
-
word-break: break-all;
|
|
105
|
-
user-select: text;
|
|
106
|
-
cursor: text;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.trace-chain-position {
|
|
110
|
-
font-size: 12px;
|
|
111
|
-
color: var(--gray-11);
|
|
112
|
-
white-space: normal;
|
|
113
|
-
overflow-wrap: anywhere;
|
|
114
|
-
user-select: text;
|
|
115
|
-
cursor: text;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.trace-chain-error {
|
|
119
|
-
font-size: 12px;
|
|
120
|
-
color: #ff7b7b;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.trace-chain-pending {
|
|
124
|
-
font-size: 12px;
|
|
125
|
-
color: var(--gray-10);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
129
|
}
|
|
@@ -3,7 +3,9 @@ import {Box, Button, Flex, Text} from "@forge-kit/component";
|
|
|
3
3
|
import {DndContext, PointerSensor, closestCenter, useSensor, useSensors, type DragEndEvent} from "@dnd-kit/core";
|
|
4
4
|
import {SortableContext, arrayMove, useSortable, verticalListSortingStrategy} from "@dnd-kit/sortable";
|
|
5
5
|
import {CSS} from "@dnd-kit/utilities";
|
|
6
|
+
import {PanelCard} from "@/components/panel-card";
|
|
6
7
|
import "./index.less";
|
|
8
|
+
import classNames from "classnames";
|
|
7
9
|
|
|
8
10
|
interface TraceChainNode {
|
|
9
11
|
fileName: string
|
|
@@ -73,7 +75,8 @@ const SortableTraceChainItem: React.FC<SortableTraceChainItemProps> = (props) =>
|
|
|
73
75
|
return (
|
|
74
76
|
<Box ref={setNodeRef} style={style} className="trace-chain-item" data-dragging={isDragging ? "true" : "false"}>
|
|
75
77
|
<Flex className="trace-chain-node" gap="2">
|
|
76
|
-
<Flex className="trace-chain-drag-handle" align="center"
|
|
78
|
+
<Flex className="trace-chain-drag-handle" align="center"
|
|
79
|
+
justify="center" {...attributes} {...listeners}>#{index + 1}</Flex>
|
|
77
80
|
<Flex className="trace-chain-content" direction="column" gap="2">
|
|
78
81
|
<Flex className="trace-chain-header" justify="between" align="start" gap="2">
|
|
79
82
|
<Text className="trace-chain-main-title">{slot.mapFileName || "(未命名 map 文件)"}</Text>
|
|
@@ -113,14 +116,6 @@ export const TraceChain: React.FC<TraceChainProps> = (props) => {
|
|
|
113
116
|
const {slots = [], data = [], onReorder, onDelete, className} = props
|
|
114
117
|
const sensors = useSensors(useSensor(PointerSensor, {activationConstraint: {distance: 6}}))
|
|
115
118
|
|
|
116
|
-
if (slots.length === 0) {
|
|
117
|
-
return (
|
|
118
|
-
<Box className={className || "trace-chain-root"}>
|
|
119
|
-
<Flex className="trace-chain-empty" align="center" justify="center">暂无解析链路,上传 map 并点击“解析链路”。</Flex>
|
|
120
|
-
</Box>
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
119
|
const handleDragEnd = (event: DragEndEvent) => {
|
|
125
120
|
const {active, over} = event
|
|
126
121
|
if (!over || active.id === over.id) return
|
|
@@ -132,20 +127,26 @@ export const TraceChain: React.FC<TraceChainProps> = (props) => {
|
|
|
132
127
|
}
|
|
133
128
|
|
|
134
129
|
return (
|
|
135
|
-
<
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
130
|
+
<PanelCard title="TRACE CHAIN" className={classNames('trace-panel-card', className)}>
|
|
131
|
+
<Flex className="trace-panel-content">
|
|
132
|
+
{Boolean(!slots.length) && (
|
|
133
|
+
<Box className="trace-chain-root">
|
|
134
|
+
<Flex className="trace-chain-empty" align="center" justify="center">暂无解析链路,上传 map 并点击“解析链路”。</Flex>
|
|
135
|
+
</Box>
|
|
136
|
+
)}
|
|
137
|
+
{Boolean(slots.length) && (
|
|
138
|
+
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
|
139
|
+
<SortableContext items={slots.map((item) => item.id)} strategy={verticalListSortingStrategy}>
|
|
140
|
+
<Box className="trace-chain-root">
|
|
141
|
+
{slots.map((slot, index) => {
|
|
142
|
+
return <SortableTraceChainItem key={slot.id} slot={slot} hop={data[index]}
|
|
143
|
+
index={index} onDelete={onDelete}/>
|
|
144
|
+
})}
|
|
145
|
+
</Box>
|
|
146
|
+
</SortableContext>
|
|
147
|
+
</DndContext>
|
|
148
|
+
)}
|
|
149
|
+
</Flex>
|
|
150
|
+
</PanelCard>
|
|
150
151
|
)
|
|
151
152
|
}
|
package/src/main.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {StrictMode} from 'react'
|
|
2
2
|
import ReactDOM from "react-dom/client";
|
|
3
|
-
import {definePlugin} from '@forge-kit/
|
|
3
|
+
import {definePlugin} from '@forge-kit/helper'
|
|
4
4
|
import {App} from '@/App.tsx'
|
|
5
5
|
|
|
6
6
|
let rootInstance: ReturnType<typeof ReactDOM.createRoot> | null = null;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import {defineInvokeMethod, registerInvokeMethod} from "@forge-kit/node-sdk";
|
|
2
|
+
import {getSourceSnippet, profileResolveTracePhases, resolveTrace} from "./trace/resolve";
|
|
3
|
+
|
|
4
|
+
const invokeMethod = defineInvokeMethod({resolveTrace, getSourceSnippet, profileResolveTracePhases})
|
|
5
|
+
|
|
6
|
+
registerInvokeMethod(invokeMethod)
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import type {SourceMapRecord, SourceMapRegistry} from "./types";
|
|
2
2
|
import {getBaseName, normalizePath} from "./path";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
interface RegisterSourceMapInput {
|
|
5
|
+
registry: SourceMapRegistry
|
|
6
|
+
key: string
|
|
7
|
+
sourceMapRecord: SourceMapRecord
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const registerSourceMap = (input: RegisterSourceMapInput) => {
|
|
11
|
+
const {registry, key, sourceMapRecord} = input
|
|
5
12
|
const normalized = normalizePath(key)
|
|
13
|
+
|
|
6
14
|
if (!normalized) return registry
|
|
7
15
|
|
|
8
|
-
const nextRegistry: SourceMapRegistry = {
|
|
9
|
-
...registry,
|
|
10
|
-
[normalized]: sourceMapRecord,
|
|
11
|
-
}
|
|
16
|
+
const nextRegistry: SourceMapRegistry = {...registry, [normalized]: sourceMapRecord}
|
|
12
17
|
|
|
13
18
|
const baseName = getBaseName(normalized)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
19
|
+
|
|
20
|
+
if (!nextRegistry[baseName]) nextRegistry[baseName] = sourceMapRecord
|
|
17
21
|
|
|
18
22
|
return nextRegistry
|
|
19
23
|
}
|
|
@@ -21,9 +25,8 @@ export const registerSourceMap = (registry: SourceMapRegistry, key: string, sour
|
|
|
21
25
|
export const listSourceMapRecords = (sourceMaps: SourceMapRegistry): SourceMapRecord[] => {
|
|
22
26
|
const map = new Map<string, SourceMapRecord>()
|
|
23
27
|
Object.values(sourceMaps).forEach((record) => {
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
}
|
|
28
|
+
if (map.has(record.mapFileName)) return
|
|
29
|
+
map.set(record.mapFileName, record)
|
|
27
30
|
})
|
|
28
31
|
return Array.from(map.values())
|
|
29
32
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {normalizePath, stripMapExt} from "../base/path";
|
|
2
|
+
import {registerSourceMap} from "../base/registry";
|
|
3
|
+
import type {ChainMapSlot, SourceMapRegistry} from "../base/types";
|
|
4
|
+
|
|
5
|
+
export const buildRegistryFromChainSlots = (slots: ChainMapSlot[]) => {
|
|
6
|
+
let registry: SourceMapRegistry = {}
|
|
7
|
+
const orderedMapFileNames: string[] = []
|
|
8
|
+
|
|
9
|
+
slots.forEach((slot) => {
|
|
10
|
+
const {sourceMapRecord: record} = slot
|
|
11
|
+
if (!record) return
|
|
12
|
+
|
|
13
|
+
orderedMapFileNames.push(record.mapFileName)
|
|
14
|
+
registry = registerSourceMap({registry, key: record.mapFileName, sourceMapRecord: record})
|
|
15
|
+
registry = registerSourceMap({registry, key: stripMapExt(record.mapFileName), sourceMapRecord: record})
|
|
16
|
+
|
|
17
|
+
if (record.rawSourceMap.file) {
|
|
18
|
+
registry = registerSourceMap({registry, key: record.rawSourceMap.file, sourceMapRecord: record})
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
return {registry, orderedMapFileNames}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const getAutoEntryFileNameFromSlots = (slots: ChainMapSlot[]) => {
|
|
26
|
+
const firstValidSlot = slots.find((slot) => slot.sourceMapRecord)
|
|
27
|
+
if (!firstValidSlot?.sourceMapRecord) return ""
|
|
28
|
+
|
|
29
|
+
const mappedFile = firstValidSlot.sourceMapRecord.rawSourceMap.file
|
|
30
|
+
if (mappedFile) return normalizePath(mappedFile)
|
|
31
|
+
|
|
32
|
+
return stripMapExt(firstValidSlot.sourceMapRecord.mapFileName)
|
|
33
|
+
}
|