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

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 (134) hide show
  1. package/README.md +3 -5
  2. package/dist/{panel.html → App.html} +3 -3
  3. package/dist/assets/App-CA1Fbh0I.js +25364 -0
  4. package/dist/assets/App-DoHQsY5s.css +1276 -0
  5. package/dist/event-source.cjs +22 -0
  6. package/dist/event-source.js +23 -0
  7. package/dist/react-native.cjs +8 -1
  8. package/dist/react-native.d.ts +1 -5
  9. package/dist/react-native.js +6 -171
  10. package/dist/rozenite.config.d.ts +7 -0
  11. package/dist/rozenite.json +1 -1
  12. package/dist/src/react-native/http/network-inspector.d.ts +8 -0
  13. package/dist/src/react-native/http/network-requests-registry.d.ts +6 -0
  14. package/dist/src/react-native/http/xhr-interceptor.d.ts +38 -0
  15. package/dist/src/react-native/sse/event-source.d.ts +2 -0
  16. package/dist/src/react-native/sse/sse-inspector.d.ts +9 -0
  17. package/dist/src/react-native/sse/sse-interceptor.d.ts +36 -0
  18. package/dist/src/react-native/sse/types.d.ts +6 -0
  19. package/dist/src/react-native/useNetworkActivityDevTools.d.ts +2 -0
  20. package/dist/src/react-native/utils.d.ts +6 -0
  21. package/dist/src/react-native/websocket/websocket-inspector.d.ts +9 -0
  22. package/dist/src/react-native/websocket/websocket-interceptor.d.ts +74 -0
  23. package/dist/src/shared/client.d.ts +68 -0
  24. package/dist/src/shared/sse-events.d.ts +35 -0
  25. package/dist/src/shared/websocket-events.d.ts +60 -0
  26. package/dist/src/ui/App.d.ts +1 -0
  27. package/dist/src/ui/components/Badge.d.ts +9 -0
  28. package/dist/src/ui/components/Button.d.ts +11 -0
  29. package/dist/src/ui/components/Input.d.ts +3 -0
  30. package/dist/src/ui/components/JsonTree.d.ts +5 -0
  31. package/dist/src/ui/components/JsonTreeCopyableItem.d.ts +7 -0
  32. package/dist/src/ui/components/RequestList.d.ts +25 -0
  33. package/dist/src/ui/components/ScrollArea.d.ts +4 -0
  34. package/dist/src/ui/components/Separator.d.ts +3 -0
  35. package/dist/src/ui/components/SidePanel.d.ts +1 -0
  36. package/dist/src/ui/components/Toolbar.d.ts +1 -0
  37. package/dist/src/ui/hooks/useCopyToClipboard.d.ts +4 -0
  38. package/dist/src/ui/state/derived.d.ts +5 -0
  39. package/dist/src/ui/state/hooks.d.ts +17 -0
  40. package/dist/src/ui/state/model.d.ts +98 -0
  41. package/dist/src/ui/state/store.d.ts +24 -0
  42. package/dist/src/ui/tabs/CookiesTab.d.ts +5 -0
  43. package/dist/src/ui/tabs/HeadersTab.d.ts +5 -0
  44. package/dist/src/ui/tabs/MessagesTab.d.ts +5 -0
  45. package/dist/src/ui/tabs/RequestTab.d.ts +5 -0
  46. package/dist/src/ui/tabs/ResponseTab.d.ts +6 -0
  47. package/dist/src/ui/tabs/SSEMessagesTab.d.ts +5 -0
  48. package/dist/src/ui/tabs/TimingTab.d.ts +5 -0
  49. package/dist/src/ui/types.d.ts +26 -0
  50. package/dist/src/ui/utils/assert.d.ts +1 -0
  51. package/dist/src/ui/utils/cn.d.ts +2 -0
  52. package/dist/src/ui/utils/copyToClipboard.d.ts +1 -0
  53. package/dist/src/ui/utils/getHttpHeaderValue.d.ts +2 -0
  54. package/dist/src/ui/utils/getId.d.ts +1 -0
  55. package/dist/src/ui/utils/getStatusColor.d.ts +1 -0
  56. package/dist/src/ui/views/InspectorView.d.ts +5 -0
  57. package/dist/src/ui/views/LoadingView.d.ts +1 -0
  58. package/dist/useNetworkActivityDevTools.cjs +759 -0
  59. package/dist/useNetworkActivityDevTools.js +757 -0
  60. package/package.json +31 -10
  61. package/postcss.config.js +6 -0
  62. package/project.json +12 -0
  63. package/react-native.ts +2 -1
  64. package/rozenite.config.ts +2 -2
  65. package/src/css-modules.d.ts +1 -1
  66. package/src/react-native/http/network-inspector.ts +226 -0
  67. package/src/react-native/http/network-requests-registry.ts +52 -0
  68. package/src/react-native/http/xhr-interceptor.ts +211 -0
  69. package/src/react-native/http/xml-request.d.ts +34 -0
  70. package/src/react-native/sse/event-source.ts +25 -0
  71. package/src/react-native/sse/sse-inspector.ts +117 -0
  72. package/src/react-native/sse/sse-interceptor.ts +162 -0
  73. package/src/react-native/sse/types.ts +9 -0
  74. package/src/react-native/useNetworkActivityDevTools.ts +73 -210
  75. package/src/react-native/utils.ts +43 -0
  76. package/src/react-native/websocket/websocket-inspector.ts +180 -0
  77. package/src/react-native/websocket/websocket-interceptor.d.ts +4 -0
  78. package/src/react-native/websocket/websocket-interceptor.ts +166 -0
  79. package/src/shared/client.ts +86 -0
  80. package/src/shared/sse-events.ts +44 -0
  81. package/src/shared/websocket-events.ts +79 -0
  82. package/src/ui/App.tsx +19 -0
  83. package/src/ui/components/Badge.tsx +36 -0
  84. package/src/ui/components/Button.tsx +56 -0
  85. package/src/ui/components/Input.tsx +22 -0
  86. package/src/ui/components/JsonTree.tsx +50 -0
  87. package/src/ui/components/JsonTreeCopyableItem.tsx +33 -0
  88. package/src/ui/components/RequestList.tsx +295 -0
  89. package/src/ui/components/ScrollArea.tsx +48 -0
  90. package/src/ui/components/Separator.tsx +31 -0
  91. package/src/ui/components/SidePanel.tsx +323 -0
  92. package/src/ui/components/Tabs.tsx +55 -0
  93. package/src/ui/components/Toolbar.tsx +45 -0
  94. package/src/ui/globals.css +90 -0
  95. package/src/ui/hooks/useCopyToClipboard.ts +28 -0
  96. package/src/ui/state/derived.ts +112 -0
  97. package/src/ui/state/hooks.ts +44 -0
  98. package/src/ui/state/model.ts +129 -0
  99. package/src/ui/state/store.ts +559 -0
  100. package/src/ui/tabs/CookiesTab.tsx +279 -0
  101. package/src/ui/tabs/HeadersTab.tsx +110 -0
  102. package/src/ui/tabs/MessagesTab.tsx +276 -0
  103. package/src/ui/tabs/RequestTab.tsx +69 -0
  104. package/src/ui/tabs/ResponseTab.tsx +138 -0
  105. package/src/ui/tabs/SSEMessagesTab.tsx +213 -0
  106. package/src/ui/tabs/TimingTab.tsx +60 -0
  107. package/src/ui/types.ts +34 -0
  108. package/src/ui/utils/assert.ts +5 -0
  109. package/src/ui/utils/cn.ts +6 -0
  110. package/src/ui/utils/copyToClipboard.ts +3 -0
  111. package/src/ui/utils/getHttpHeaderValue.ts +14 -0
  112. package/src/ui/utils/getId.ts +10 -0
  113. package/src/ui/utils/getStatusColor.ts +15 -0
  114. package/src/ui/views/InspectorView.tsx +53 -0
  115. package/src/ui/views/LoadingView.tsx +19 -0
  116. package/tailwind.config.ts +96 -0
  117. package/tsconfig.json +13 -6
  118. package/tsconfig.tsbuildinfo +1 -0
  119. package/vite.config.ts +13 -1
  120. package/dist/assets/panel-C5YgUUj5.js +0 -54
  121. package/dist/assets/panel-NCVczPb1.css +0 -1
  122. package/src/types/network.ts +0 -153
  123. package/src/ui/components.module.css +0 -158
  124. package/src/ui/components.tsx +0 -219
  125. package/src/ui/network-details.module.css +0 -57
  126. package/src/ui/network-details.tsx +0 -134
  127. package/src/ui/network-list.module.css +0 -122
  128. package/src/ui/network-list.tsx +0 -145
  129. package/src/ui/network-toolbar.module.css +0 -9
  130. package/src/ui/network-toolbar.tsx +0 -40
  131. package/src/ui/panel.module.css +0 -61
  132. package/src/ui/panel.tsx +0 -201
  133. package/src/ui/tanstack-query.tsx +0 -197
  134. package/src/ui/utils.ts +0 -89
@@ -1,134 +0,0 @@
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
- };
@@ -1,122 +0,0 @@
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
- }
@@ -1,145 +0,0 @@
1
- import React from 'react';
2
- import { useVirtualizer } from '@tanstack/react-virtual';
3
- import { NetworkEntry } from '../types/network';
4
- import { getStatusColor, getMethodColor, formatDuration, formatFileSize, parseUrl } from './utils';
5
- import { Badge, Tooltip } from './components';
6
- import styles from './network-list.module.css';
7
-
8
- interface NetworkListProps {
9
- entries: NetworkEntry[];
10
- selectedRequestId: string | null;
11
- onSelect: (requestId: string) => void;
12
- height: number;
13
- }
14
-
15
- const ITEM_HEIGHT = 60; // Height of each network list item
16
-
17
- export const NetworkList: React.FC<NetworkListProps> = ({
18
- entries,
19
- selectedRequestId,
20
- onSelect,
21
- height,
22
- }) => {
23
- const parentRef = React.useRef<HTMLDivElement>(null);
24
-
25
- const virtualizer = useVirtualizer({
26
- count: entries.length,
27
- getScrollElement: () => parentRef.current,
28
- estimateSize: () => ITEM_HEIGHT,
29
- overscan: 5,
30
- });
31
-
32
- const NetworkListItem: React.FC<{ entry: NetworkEntry; index: number }> = ({ entry, index }) => {
33
- const status = entry.response?.response.status || 0;
34
- const method = entry.request.request.method;
35
- const url = entry.request.request.url;
36
- const { domain, path } = parseUrl(url);
37
-
38
- // Get size information
39
- const encodedSize = entry.response?.response.encodedDataLength || 0;
40
- const decodedSize = entry.response?.response.decodedBodySize || 0;
41
- const displaySize = decodedSize > 0 ? decodedSize : encodedSize;
42
-
43
- const isSelected = selectedRequestId === entry.requestId;
44
-
45
- return (
46
- <div
47
- className={isSelected ? styles.listItemSelected : styles.listItem}
48
- onClick={() => onSelect(entry.requestId)}
49
- >
50
- <div className={styles.statusColumn}>
51
- <Tooltip
52
- content={`Status: ${status || 'Pending'}`}
53
- showOnlyWhenTruncated
54
- variant={status >= 400 ? 'error' : status >= 300 ? 'warning' : 'info'}
55
- >
56
- <Badge color={getStatusColor(status)}>
57
- {status || '...'}
58
- </Badge>
59
- </Tooltip>
60
- </div>
61
- <div className={styles.methodColumn}>
62
- <Tooltip
63
- content={`Method: ${method}`}
64
- showOnlyWhenTruncated
65
- variant="info"
66
- >
67
- <Badge color={getMethodColor(method)}>
68
- {method}
69
- </Badge>
70
- </Tooltip>
71
- </div>
72
- <div className={styles.urlColumn}>
73
- <Tooltip content={domain} showOnlyWhenTruncated>
74
- <div className={styles.domainText}>
75
- {domain}
76
- </div>
77
- </Tooltip>
78
- <Tooltip content={path} showOnlyWhenTruncated>
79
- <div className={styles.pathText}>
80
- {path}
81
- </div>
82
- </Tooltip>
83
- <Tooltip content={url} showOnlyWhenTruncated>
84
- <div className={styles.fullUrlText}>
85
- {url}
86
- </div>
87
- </Tooltip>
88
- </div>
89
- <div className={styles.durationColumn}>
90
- <Tooltip content={`Duration: ${entry.duration ? formatDuration(entry.duration) : 'Pending'}`} showOnlyWhenTruncated>
91
- <span className={styles.columnText}>
92
- {entry.duration ? formatDuration(entry.duration) : '...'}
93
- </span>
94
- </Tooltip>
95
- </div>
96
- <div className={styles.sizeColumn}>
97
- <Tooltip content={`Size: ${displaySize > 0 ? formatFileSize(displaySize) : 'Unknown'}`} showOnlyWhenTruncated>
98
- <span className={styles.columnText}>
99
- {displaySize > 0 ? formatFileSize(displaySize) : '...'}
100
- </span>
101
- </Tooltip>
102
- </div>
103
- </div>
104
- );
105
- };
106
-
107
- if (entries.length === 0) {
108
- return (
109
- <div className={styles.emptyContainer} style={{ height }}>
110
- <div className={styles.emptyText}>
111
- No network requests recorded
112
- </div>
113
- </div>
114
- );
115
- }
116
-
117
- return (
118
- <div
119
- ref={parentRef}
120
- className={styles.container}
121
- style={{ height }}
122
- >
123
- <div
124
- className={styles.virtualContainer}
125
- style={{ height: `${virtualizer.getTotalSize()}px` }}
126
- >
127
- {virtualizer.getVirtualItems().map((virtualItem: any) => (
128
- <div
129
- key={virtualItem.key}
130
- className={styles.virtualItem}
131
- style={{
132
- height: `${virtualItem.size}px`,
133
- transform: `translateY(${virtualItem.start}px)`,
134
- }}
135
- >
136
- <NetworkListItem
137
- entry={entries[virtualItem.index]}
138
- index={virtualItem.index}
139
- />
140
- </div>
141
- ))}
142
- </div>
143
- </div>
144
- );
145
- };
@@ -1,9 +0,0 @@
1
- .recordingButton {
2
- margin-right: 8px;
3
- }
4
-
5
- .requestCount {
6
- margin-left: auto;
7
- font-size: 12px;
8
- color: #666;
9
- }
@@ -1,40 +0,0 @@
1
- import React from 'react';
2
- import { Button, Toolbar } from './components';
3
- import styles from './network-toolbar.module.css';
4
-
5
- interface NetworkToolbarProps {
6
- isRecording: boolean;
7
- onToggleRecording: () => void;
8
- onClear: () => void;
9
- requestCount: number;
10
- }
11
-
12
- export const NetworkToolbar: React.FC<NetworkToolbarProps> = ({
13
- isRecording,
14
- onToggleRecording,
15
- onClear,
16
- requestCount,
17
- }) => {
18
- return (
19
- <Toolbar>
20
- <Button
21
- onClick={onToggleRecording}
22
- variant={isRecording ? 'danger' : 'success'}
23
- size="small"
24
- className={styles.recordingButton}
25
- >
26
- {isRecording ? 'Stop' : 'Start'} Recording
27
- </Button>
28
- <Button
29
- onClick={onClear}
30
- variant="secondary"
31
- size="small"
32
- >
33
- Clear
34
- </Button>
35
- <div className={styles.requestCount}>
36
- {requestCount} requests
37
- </div>
38
- </Toolbar>
39
- );
40
- };
@@ -1,61 +0,0 @@
1
- .container {
2
- height: 100vh;
3
- display: flex;
4
- flex-direction: column;
5
- font-family: system-ui, -apple-system, sans-serif;
6
- }
7
-
8
- .mainContent {
9
- flex: 1;
10
- display: flex;
11
- overflow: hidden;
12
- min-height: 0;
13
- }
14
-
15
- .networkListContainer {
16
- width: 60%;
17
- border-right: 1px solid #e0e0e0;
18
- display: flex;
19
- flex-direction: column;
20
- min-width: 0;
21
- }
22
-
23
- .listContent {
24
- flex: 1;
25
- }
26
-
27
- .detailsContainer {
28
- width: 40%;
29
- display: flex;
30
- flex-direction: column;
31
- min-width: 0;
32
- }
33
-
34
- .headerStatus {
35
- width: 60px;
36
- text-align: center;
37
- flex-shrink: 0;
38
- }
39
-
40
- .headerMethod {
41
- width: 80px;
42
- text-align: center;
43
- flex-shrink: 0;
44
- }
45
-
46
- .headerName {
47
- flex: 1;
48
- min-width: 0;
49
- }
50
-
51
- .headerTime {
52
- width: 80px;
53
- text-align: right;
54
- flex-shrink: 0;
55
- }
56
-
57
- .headerSize {
58
- width: 80px;
59
- text-align: right;
60
- flex-shrink: 0;
61
- }