@rozenite/network-activity-plugin 1.0.0-alpha.1

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.
@@ -0,0 +1,158 @@
1
+ /* Button Component */
2
+ .button {
3
+ border: none;
4
+ border-radius: 4px;
5
+ cursor: pointer;
6
+ font-weight: 500;
7
+ transition: all 0.2s ease;
8
+ }
9
+
10
+ .button:disabled {
11
+ opacity: 0.6;
12
+ cursor: not-allowed;
13
+ }
14
+
15
+ .buttonSmall {
16
+ font-size: 12px;
17
+ padding: 4px 8px;
18
+ }
19
+
20
+ .buttonMedium {
21
+ font-size: 14px;
22
+ padding: 8px 16px;
23
+ }
24
+
25
+ .buttonLarge {
26
+ font-size: 16px;
27
+ padding: 12px 24px;
28
+ }
29
+
30
+ .buttonPrimary {
31
+ background-color: #007AFF;
32
+ color: white;
33
+ }
34
+
35
+ .buttonSecondary {
36
+ background-color: #6c757d;
37
+ color: white;
38
+ }
39
+
40
+ .buttonDanger {
41
+ background-color: #dc3545;
42
+ color: white;
43
+ }
44
+
45
+ .buttonSuccess {
46
+ background-color: #28a745;
47
+ color: white;
48
+ }
49
+
50
+ /* Card Component */
51
+ .card {
52
+ background-color: #ffffff;
53
+ border: 1px solid #e0e0e0;
54
+ border-radius: 8px;
55
+ padding: 16px;
56
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
57
+ }
58
+
59
+ /* Badge Component */
60
+ .badge {
61
+ background-color: #007AFF;
62
+ color: white;
63
+ padding: 2px 6px;
64
+ border-radius: 12px;
65
+ font-size: 11px;
66
+ font-weight: 500;
67
+ display: inline-block;
68
+ }
69
+
70
+ /* Toolbar Component */
71
+ .toolbar {
72
+ display: flex;
73
+ align-items: center;
74
+ padding: 8px 12px;
75
+ border-bottom: 1px solid #e0e0e0;
76
+ background-color: #f8f9fa;
77
+ }
78
+
79
+ /* Panel Header Component */
80
+ .panelHeader {
81
+ display: flex;
82
+ align-items: center;
83
+ padding: 8px 12px;
84
+ border-bottom: 1px solid #e0e0e0;
85
+ background-color: #f8f9fa;
86
+ font-size: 12px;
87
+ font-weight: bold;
88
+ color: #333;
89
+ }
90
+
91
+ /* Empty State Component */
92
+ .emptyState {
93
+ display: flex;
94
+ align-items: center;
95
+ justify-content: center;
96
+ padding: 40px 20px;
97
+ color: #666;
98
+ font-size: 14px;
99
+ text-align: center;
100
+ }
101
+
102
+ /* Loading Spinner Component */
103
+ .loadingSpinner {
104
+ border: 2px solid #e0e0e0;
105
+ border-top: 2px solid #007AFF;
106
+ border-radius: 50%;
107
+ animation: spin 1s linear infinite;
108
+ }
109
+
110
+ /* Tooltip Component */
111
+ .tooltipReference {
112
+ display: inline-block;
113
+ }
114
+
115
+ .tooltipFloating {
116
+ padding: 6px 10px;
117
+ border-radius: 6px;
118
+ font-size: 12px;
119
+ white-space: nowrap;
120
+ z-index: 1000;
121
+ max-width: 300px;
122
+ word-break: break-all;
123
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
124
+ -webkit-backdrop-filter: blur(8px);
125
+ backdrop-filter: blur(8px);
126
+ font-family: system-ui, -apple-system, sans-serif;
127
+ line-height: 1.4;
128
+ }
129
+
130
+ .tooltipDefault {
131
+ background-color: #333;
132
+ color: white;
133
+ border: 1px solid rgba(255, 255, 255, 0.1);
134
+ }
135
+
136
+ .tooltipInfo {
137
+ background-color: #0066cc;
138
+ color: white;
139
+ border: 1px solid rgba(255, 255, 255, 0.2);
140
+ }
141
+
142
+ .tooltipWarning {
143
+ background-color: #ff8c00;
144
+ color: white;
145
+ border: 1px solid rgba(255, 255, 255, 0.2);
146
+ }
147
+
148
+ .tooltipError {
149
+ background-color: #dc3545;
150
+ color: white;
151
+ border: 1px solid rgba(255, 255, 255, 0.2);
152
+ }
153
+
154
+ /* Spinner Animation */
155
+ @keyframes spin {
156
+ 0% { transform: rotate(0deg); }
157
+ 100% { transform: rotate(360deg); }
158
+ }
@@ -0,0 +1,219 @@
1
+ import React, { useState, useRef } from 'react';
2
+ import {
3
+ useFloating,
4
+ useHover,
5
+ useInteractions,
6
+ useRole,
7
+ useDismiss,
8
+ FloatingPortal,
9
+ offset,
10
+ shift,
11
+ flip,
12
+ autoUpdate,
13
+ } from '@floating-ui/react';
14
+ import styles from './components.module.css';
15
+
16
+ // Button Component
17
+ interface ButtonProps {
18
+ onClick: () => void;
19
+ children: React.ReactNode;
20
+ variant?: 'primary' | 'secondary' | 'danger' | 'success';
21
+ size?: 'small' | 'medium' | 'large';
22
+ disabled?: boolean;
23
+ style?: React.CSSProperties;
24
+ className?: string;
25
+ }
26
+
27
+ export const Button: React.FC<ButtonProps> = ({
28
+ onClick,
29
+ children,
30
+ variant = 'primary',
31
+ size = 'medium',
32
+ disabled = false,
33
+ style,
34
+ className,
35
+ }) => {
36
+ const sizeClass = size === 'small' ? styles.buttonSmall : size === 'large' ? styles.buttonLarge : styles.buttonMedium;
37
+ const variantClass = styles[`button${variant.charAt(0).toUpperCase() + variant.slice(1)}` as keyof typeof styles];
38
+
39
+ return (
40
+ <button
41
+ onClick={onClick}
42
+ disabled={disabled}
43
+ className={`${styles.button} ${sizeClass} ${variantClass} ${className || ''}`}
44
+ style={style}
45
+ >
46
+ {children}
47
+ </button>
48
+ );
49
+ };
50
+
51
+ // Card Component
52
+ interface CardProps {
53
+ children: React.ReactNode;
54
+ style?: React.CSSProperties;
55
+ className?: string;
56
+ }
57
+
58
+ export const Card: React.FC<CardProps> = ({ children, style, className }) => (
59
+ <div className={`${styles.card} ${className || ''}`} style={style}>
60
+ {children}
61
+ </div>
62
+ );
63
+
64
+ // Badge Component
65
+ interface BadgeProps {
66
+ children: React.ReactNode;
67
+ color?: string;
68
+ style?: React.CSSProperties;
69
+ }
70
+
71
+ export const Badge: React.FC<BadgeProps> = ({ children, color = '#007AFF', style }) => (
72
+ <span
73
+ className={styles.badge}
74
+ style={{ backgroundColor: color, ...style }}
75
+ >
76
+ {children}
77
+ </span>
78
+ );
79
+
80
+ // Toolbar Component
81
+ interface ToolbarProps {
82
+ children: React.ReactNode;
83
+ style?: React.CSSProperties;
84
+ }
85
+
86
+ export const Toolbar: React.FC<ToolbarProps> = ({ children, style }) => (
87
+ <div className={styles.toolbar} style={style}>
88
+ {children}
89
+ </div>
90
+ );
91
+
92
+ // Panel Header Component
93
+ interface PanelHeaderProps {
94
+ children: React.ReactNode;
95
+ style?: React.CSSProperties;
96
+ }
97
+
98
+ export const PanelHeader: React.FC<PanelHeaderProps> = ({ children, style }) => (
99
+ <div className={styles.panelHeader} style={style}>
100
+ {children}
101
+ </div>
102
+ );
103
+
104
+ // Empty State Component
105
+ interface EmptyStateProps {
106
+ message: string;
107
+ style?: React.CSSProperties;
108
+ }
109
+
110
+ export const EmptyState: React.FC<EmptyStateProps> = ({ message, style }) => (
111
+ <div className={styles.emptyState} style={style}>
112
+ {message}
113
+ </div>
114
+ );
115
+
116
+ // Loading Spinner Component
117
+ interface LoadingSpinnerProps {
118
+ size?: number;
119
+ color?: string;
120
+ style?: React.CSSProperties;
121
+ }
122
+
123
+ export const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({
124
+ size = 16,
125
+ color = '#007AFF',
126
+ style
127
+ }) => (
128
+ <div
129
+ className={styles.loadingSpinner}
130
+ style={{
131
+ width: size,
132
+ height: size,
133
+ borderTopColor: color,
134
+ ...style,
135
+ }}
136
+ />
137
+ );
138
+
139
+ // Tooltip Component using Floating UI
140
+ interface TooltipProps {
141
+ children: React.ReactNode;
142
+ content: string;
143
+ style?: React.CSSProperties;
144
+ showOnlyWhenTruncated?: boolean;
145
+ placement?: 'top' | 'bottom' | 'left' | 'right';
146
+ variant?: 'default' | 'info' | 'warning' | 'error';
147
+ }
148
+
149
+ export const Tooltip: React.FC<TooltipProps> = ({
150
+ children,
151
+ content,
152
+ style,
153
+ showOnlyWhenTruncated = false,
154
+ placement = 'top',
155
+ variant = 'default'
156
+ }) => {
157
+ const [isOpen, setIsOpen] = useState(false);
158
+ const [isTruncated, setIsTruncated] = useState(false);
159
+
160
+ const { refs, floatingStyles, context } = useFloating({
161
+ open: isOpen,
162
+ onOpenChange: setIsOpen,
163
+ placement,
164
+ middleware: [offset(8), shift(), flip()],
165
+ whileElementsMounted: autoUpdate,
166
+ });
167
+
168
+ const hover = useHover(context, {
169
+ move: false,
170
+ delay: { open: 200, close: 0 },
171
+ });
172
+
173
+ const dismiss = useDismiss(context);
174
+ const role = useRole(context, { role: 'tooltip' });
175
+
176
+ const { getReferenceProps, getFloatingProps } = useInteractions([hover, dismiss, role]);
177
+
178
+ React.useEffect(() => {
179
+ if (showOnlyWhenTruncated && refs.reference.current) {
180
+ const element = refs.reference.current as HTMLElement;
181
+ if (element && 'scrollWidth' in element && 'clientWidth' in element) {
182
+ setIsTruncated(element.scrollWidth > element.clientWidth);
183
+ }
184
+ }
185
+ }, [content, showOnlyWhenTruncated, refs.reference]);
186
+
187
+ const shouldShowTooltip = showOnlyWhenTruncated ? isTruncated : true;
188
+
189
+ const variantClass = styles[`tooltip${variant.charAt(0).toUpperCase() + variant.slice(1)}` as keyof typeof styles];
190
+
191
+ return (
192
+ <>
193
+ <div
194
+ ref={refs.setReference}
195
+ className={styles.tooltipReference}
196
+ style={style}
197
+ {...getReferenceProps()}
198
+ >
199
+ {children}
200
+ </div>
201
+
202
+ <FloatingPortal>
203
+ {isOpen && (
204
+ <div
205
+ ref={refs.setFloating}
206
+ className={`${styles.tooltipFloating} ${variantClass}`}
207
+ style={floatingStyles}
208
+ {...getFloatingProps()}
209
+ >
210
+ {content}
211
+ </div>
212
+ )}
213
+ </FloatingPortal>
214
+
215
+ </>
216
+ );
217
+ };
218
+
219
+
@@ -0,0 +1,57 @@
1
+ .container {
2
+ padding: 16px;
3
+ height: 100%;
4
+ overflow: auto;
5
+ }
6
+
7
+ .card {
8
+ margin-bottom: 20px;
9
+ }
10
+
11
+ .cardTitle {
12
+ margin: 0 0 8px 0;
13
+ font-size: 16px;
14
+ }
15
+
16
+ .cardTitleError {
17
+ margin: 0 0 8px 0;
18
+ font-size: 16px;
19
+ color: #f44336;
20
+ }
21
+
22
+ .infoText {
23
+ font-size: 12px;
24
+ font-family: monospace;
25
+ }
26
+
27
+ .infoRow {
28
+ margin-bottom: 4px;
29
+ word-break: break-all;
30
+ }
31
+
32
+ .infoRowUrl {
33
+ margin-bottom: 4px;
34
+ word-break: break-all;
35
+ }
36
+
37
+ .urlText {
38
+ cursor: help;
39
+ border-bottom: 1px dotted #666;
40
+ }
41
+
42
+ .headersContainer {
43
+ font-size: 12px;
44
+ font-family: monospace;
45
+ background-color: #f5f5f5;
46
+ padding: 8px;
47
+ border-radius: 4px;
48
+ }
49
+
50
+ .headerRow {
51
+ margin-bottom: 2px;
52
+ word-break: break-all;
53
+ }
54
+
55
+ .headerValue {
56
+ word-break: break-all;
57
+ }
@@ -0,0 +1,134 @@
1
+ import React from 'react';
2
+ import { NetworkEntry } from '../types/network';
3
+ import { formatFileSize, formatDuration, formatLongUrl } from './utils';
4
+ import { Card, EmptyState, Tooltip } from './components';
5
+ import styles from './network-details.module.css';
6
+
7
+ interface NetworkDetailsProps {
8
+ entry: NetworkEntry | null;
9
+ }
10
+
11
+ export const NetworkDetails: React.FC<NetworkDetailsProps> = ({ entry }) => {
12
+ if (!entry) {
13
+ return <EmptyState message="Select a request to view details" />;
14
+ }
15
+
16
+ return (
17
+ <div className={styles.container}>
18
+ {/* General Information */}
19
+ <Card className={styles.card}>
20
+ <h3 className={styles.cardTitle}>General</h3>
21
+ <div className={styles.infoText}>
22
+ <div className={styles.infoRowUrl}>
23
+ <strong>Request URL:</strong>
24
+ <Tooltip content={entry.request.request.url} showOnlyWhenTruncated>
25
+ <span className={styles.urlText}>
26
+ {formatLongUrl(entry.request.request.url, 100)}
27
+ </span>
28
+ </Tooltip>
29
+ </div>
30
+ <div className={styles.infoRow}>
31
+ <strong>Request Method:</strong> {entry.request.request.method}
32
+ </div>
33
+ <div className={styles.infoRow}>
34
+ <strong>Status Code:</strong> {entry.response?.response.status || 'Pending'}
35
+ </div>
36
+ <div className={styles.infoRow}>
37
+ <strong>Remote Address:</strong> {entry.response?.response.remoteIPAddress || 'Unknown'}
38
+ </div>
39
+ <div className={styles.infoRow}>
40
+ <strong>Referrer Policy:</strong> {entry.request.request.headers['referer'] || 'no-referrer'}
41
+ </div>
42
+ </div>
43
+ </Card>
44
+
45
+ {/* Response Headers */}
46
+ {entry.response && (
47
+ <Card className={styles.card}>
48
+ <h3 className={styles.cardTitle}>Response Headers</h3>
49
+ <div className={styles.headersContainer}>
50
+ {Object.entries(entry.response.response.headers).map(([key, value]) => (
51
+ <div key={key} className={styles.headerRow}>
52
+ <strong>{key}:</strong>
53
+ <Tooltip content={value} showOnlyWhenTruncated>
54
+ <span className={styles.headerValue}>
55
+ {value}
56
+ </span>
57
+ </Tooltip>
58
+ </div>
59
+ ))}
60
+ </div>
61
+ </Card>
62
+ )}
63
+
64
+ {/* Request Headers */}
65
+ <Card className={styles.card}>
66
+ <h3 className={styles.cardTitle}>Request Headers</h3>
67
+ <div className={styles.headersContainer}>
68
+ {Object.entries(entry.request.request.headers).map(([key, value]) => (
69
+ <div key={key} className={styles.headerRow}>
70
+ <strong>{key}:</strong>
71
+ <Tooltip content={value} showOnlyWhenTruncated>
72
+ <span className={styles.headerValue}>
73
+ {value}
74
+ </span>
75
+ </Tooltip>
76
+ </div>
77
+ ))}
78
+ </div>
79
+ </Card>
80
+
81
+ {/* Size Information */}
82
+ {entry.response && (
83
+ <Card className={styles.card}>
84
+ <h3 className={styles.cardTitle}>Size Information</h3>
85
+ <div className={styles.infoText}>
86
+ <div className={styles.infoRow}>
87
+ <strong>Decoded Body Size:</strong> {formatFileSize(entry.response.response.decodedBodySize)}
88
+ </div>
89
+ </div>
90
+ </Card>
91
+ )}
92
+
93
+ {/* Timing Information */}
94
+ {entry.response?.response.timing && (
95
+ <Card className={styles.card}>
96
+ <h3 className={styles.cardTitle}>Timing</h3>
97
+ <div className={styles.infoText}>
98
+ <div className={styles.infoRow}>
99
+ <strong>Time to First Byte:</strong> {entry.response.response.timing.receiveHeadersEnd - entry.response.response.timing.sendEnd}ms
100
+ </div>
101
+ <div className={styles.infoRow}>
102
+ <strong>Total Duration:</strong> {entry.duration ? formatDuration(entry.duration) : 'Unknown'}
103
+ </div>
104
+ </div>
105
+ </Card>
106
+ )}
107
+
108
+ {/* Error Information */}
109
+ {entry.loadingFailed && (
110
+ <Card className={styles.card}>
111
+ <h3 className={styles.cardTitleError}>Error</h3>
112
+ <div className={styles.infoText}>
113
+ <div className={styles.infoRow}>
114
+ <strong>Error Text:</strong> {entry.loadingFailed.errorText}
115
+ </div>
116
+ <div className={styles.infoRow}>
117
+ <strong>Type:</strong> {entry.loadingFailed.type}
118
+ </div>
119
+ {entry.loadingFailed.blockedReason && (
120
+ <div className={styles.infoRow}>
121
+ <strong>Blocked Reason:</strong> {entry.loadingFailed.blockedReason}
122
+ </div>
123
+ )}
124
+ {entry.loadingFailed.canceled && (
125
+ <div className={styles.infoRow}>
126
+ <strong>Canceled:</strong> Yes
127
+ </div>
128
+ )}
129
+ </div>
130
+ </Card>
131
+ )}
132
+ </div>
133
+ );
134
+ };
@@ -0,0 +1,122 @@
1
+ .container {
2
+ height: 100%;
3
+ overflow: auto;
4
+ }
5
+
6
+ .virtualContainer {
7
+ height: 100%;
8
+ width: 100%;
9
+ position: relative;
10
+ }
11
+
12
+ .virtualItem {
13
+ position: absolute;
14
+ top: 0;
15
+ left: 0;
16
+ width: 100%;
17
+ height: 60px;
18
+ }
19
+
20
+ .listItem {
21
+ display: flex;
22
+ align-items: center;
23
+ padding: 8px 12px;
24
+ border-bottom: 1px solid #e0e0e0;
25
+ background-color: white;
26
+ cursor: pointer;
27
+ font-size: 12px;
28
+ font-family: monospace;
29
+ height: 60px;
30
+ box-sizing: border-box;
31
+ min-width: 0;
32
+ }
33
+
34
+ .listItemSelected {
35
+ display: flex;
36
+ align-items: center;
37
+ padding: 8px 12px;
38
+ border-bottom: 1px solid #e0e0e0;
39
+ background-color: #f5f5f5;
40
+ cursor: pointer;
41
+ font-size: 12px;
42
+ font-family: monospace;
43
+ height: 60px;
44
+ box-sizing: border-box;
45
+ min-width: 0;
46
+ }
47
+
48
+ .statusColumn {
49
+ width: 60px;
50
+ text-align: center;
51
+ flex-shrink: 0;
52
+ }
53
+
54
+ .methodColumn {
55
+ width: 80px;
56
+ text-align: center;
57
+ flex-shrink: 0;
58
+ }
59
+
60
+ .urlColumn {
61
+ flex: 1;
62
+ overflow: hidden;
63
+ min-width: 0;
64
+ }
65
+
66
+ .domainText {
67
+ font-weight: bold;
68
+ color: #333;
69
+ margin-bottom: 2px;
70
+ white-space: nowrap;
71
+ overflow: hidden;
72
+ text-overflow: ellipsis;
73
+ }
74
+
75
+ .pathText {
76
+ color: #666;
77
+ font-size: 11px;
78
+ white-space: nowrap;
79
+ overflow: hidden;
80
+ text-overflow: ellipsis;
81
+ }
82
+
83
+ .fullUrlText {
84
+ color: #999;
85
+ font-size: 10px;
86
+ white-space: nowrap;
87
+ overflow: hidden;
88
+ text-overflow: ellipsis;
89
+ margin-top: 1px;
90
+ }
91
+
92
+ .durationColumn {
93
+ width: 80px;
94
+ text-align: right;
95
+ margin-right: 8px;
96
+ flex-shrink: 0;
97
+ }
98
+
99
+ .sizeColumn {
100
+ width: 80px;
101
+ text-align: right;
102
+ flex-shrink: 0;
103
+ }
104
+
105
+ .columnText {
106
+ white-space: nowrap;
107
+ overflow: hidden;
108
+ text-overflow: ellipsis;
109
+ display: block;
110
+ }
111
+
112
+ .emptyContainer {
113
+ height: 100%;
114
+ display: flex;
115
+ align-items: center;
116
+ justify-content: center;
117
+ }
118
+
119
+ .emptyText {
120
+ color: #666;
121
+ font-size: 14px;
122
+ }