@screenbook/ui 1.6.0 → 1.7.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.
- package/LICENSE +21 -0
- package/astro.config.mjs +20 -0
- package/dist/server/entry.mjs +1 -1
- package/dist/server/{manifest_bx_Ion-2.mjs → manifest_DHNGfdfn.mjs} +1 -1
- package/package.json +55 -51
- package/public/logo.svg +5 -0
- package/src/components/LinkIcon.astro +93 -0
- package/src/components/MockFormEditor.tsx +1280 -0
- package/src/components/MockPreview.astro +811 -0
- package/src/layouts/Layout.astro +123 -0
- package/src/pages/api/save-mock.ts +182 -0
- package/src/pages/coverage.astro +465 -0
- package/src/pages/editor.astro +33 -0
- package/src/pages/graph.astro +423 -0
- package/src/pages/impact.astro +555 -0
- package/src/pages/index.astro +227 -0
- package/src/pages/screen/[id].astro +299 -0
- package/src/styles/global.css +904 -0
- package/src/styles/mock-editor.css +1351 -0
- package/src/utils/impactAnalysis.ts +304 -0
- package/src/utils/loadCoverage.ts +30 -0
- package/src/utils/loadScreens.ts +18 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Layout from "@/layouts/Layout.astro"
|
|
3
|
+
import { loadScreens } from "@/utils/loadScreens"
|
|
4
|
+
import {
|
|
5
|
+
analyzeImpact,
|
|
6
|
+
getAllApis,
|
|
7
|
+
generateImpactMermaid,
|
|
8
|
+
formatImpactMarkdown,
|
|
9
|
+
} from "@/utils/impactAnalysis"
|
|
10
|
+
|
|
11
|
+
const screens = loadScreens()
|
|
12
|
+
const allApis = getAllApis(screens)
|
|
13
|
+
|
|
14
|
+
const url = Astro.url
|
|
15
|
+
const apiQuery = url.searchParams.get("api") || ""
|
|
16
|
+
|
|
17
|
+
let result = null
|
|
18
|
+
let mermaidGraph = ""
|
|
19
|
+
let markdown = ""
|
|
20
|
+
|
|
21
|
+
if (apiQuery && screens.length > 0) {
|
|
22
|
+
result = analyzeImpact(screens, apiQuery, 3)
|
|
23
|
+
mermaidGraph = generateImpactMermaid(screens, result)
|
|
24
|
+
markdown = formatImpactMarkdown(result)
|
|
25
|
+
}
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<Layout title="Impact Analysis" currentPage="impact">
|
|
29
|
+
<div class="container">
|
|
30
|
+
<div class="page-header">
|
|
31
|
+
<h1 class="page-title">Impact Analysis</h1>
|
|
32
|
+
<p class="page-description">
|
|
33
|
+
Analyze which screens are affected when an API changes.
|
|
34
|
+
</p>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
{
|
|
38
|
+
screens.length === 0 ? (
|
|
39
|
+
<div class="empty-state">
|
|
40
|
+
<svg
|
|
41
|
+
class="empty-state-icon"
|
|
42
|
+
aria-hidden="true"
|
|
43
|
+
fill="none"
|
|
44
|
+
viewBox="0 0 24 24"
|
|
45
|
+
stroke="currentColor"
|
|
46
|
+
stroke-width="1.5"
|
|
47
|
+
>
|
|
48
|
+
<path
|
|
49
|
+
stroke-linecap="round"
|
|
50
|
+
stroke-linejoin="round"
|
|
51
|
+
d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z"
|
|
52
|
+
/>
|
|
53
|
+
</svg>
|
|
54
|
+
<h2 class="empty-state-title">No screen data</h2>
|
|
55
|
+
<p class="empty-state-description">
|
|
56
|
+
Run the build command to generate screen metadata.
|
|
57
|
+
</p>
|
|
58
|
+
<code class="empty-state-code">
|
|
59
|
+
<span class="prompt">$</span> screenbook build
|
|
60
|
+
</code>
|
|
61
|
+
</div>
|
|
62
|
+
) : (
|
|
63
|
+
<>
|
|
64
|
+
<div class="impact-search">
|
|
65
|
+
<form method="get" class="search-form" role="search">
|
|
66
|
+
<div class="search-wrapper impact-search-wrapper">
|
|
67
|
+
<svg
|
|
68
|
+
class="search-icon"
|
|
69
|
+
aria-hidden="true"
|
|
70
|
+
fill="none"
|
|
71
|
+
viewBox="0 0 24 24"
|
|
72
|
+
stroke="currentColor"
|
|
73
|
+
stroke-width="2"
|
|
74
|
+
>
|
|
75
|
+
<path
|
|
76
|
+
stroke-linecap="round"
|
|
77
|
+
stroke-linejoin="round"
|
|
78
|
+
d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z"
|
|
79
|
+
/>
|
|
80
|
+
</svg>
|
|
81
|
+
<label for="api-input" class="sr-only">
|
|
82
|
+
API name to analyze
|
|
83
|
+
</label>
|
|
84
|
+
<input
|
|
85
|
+
type="text"
|
|
86
|
+
id="api-input"
|
|
87
|
+
name="api"
|
|
88
|
+
class="search-input"
|
|
89
|
+
placeholder="Enter API name (e.g., InvoiceAPI.getDetail)"
|
|
90
|
+
value={apiQuery}
|
|
91
|
+
list="api-suggestions"
|
|
92
|
+
/>
|
|
93
|
+
<datalist id="api-suggestions">
|
|
94
|
+
{allApis.map((api) => (
|
|
95
|
+
<option value={api} />
|
|
96
|
+
))}
|
|
97
|
+
</datalist>
|
|
98
|
+
<button type="submit" class="search-button">
|
|
99
|
+
Analyze
|
|
100
|
+
</button>
|
|
101
|
+
</div>
|
|
102
|
+
</form>
|
|
103
|
+
|
|
104
|
+
{allApis.length > 0 && !apiQuery && (
|
|
105
|
+
<div class="api-suggestions">
|
|
106
|
+
<p class="suggestions-label">Available APIs:</p>
|
|
107
|
+
<div class="tags">
|
|
108
|
+
{allApis.slice(0, 10).map((api) => (
|
|
109
|
+
<a
|
|
110
|
+
href={`/impact?api=${encodeURIComponent(api)}`}
|
|
111
|
+
class="tag"
|
|
112
|
+
>
|
|
113
|
+
{api}
|
|
114
|
+
</a>
|
|
115
|
+
))}
|
|
116
|
+
{allApis.length > 10 && (
|
|
117
|
+
<span class="tag tag-more">
|
|
118
|
+
+{allApis.length - 10} more
|
|
119
|
+
</span>
|
|
120
|
+
)}
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
)}
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
{result && (
|
|
127
|
+
<div class="impact-results">
|
|
128
|
+
<div class="impact-summary">
|
|
129
|
+
<div class="stats">
|
|
130
|
+
<div class="stat">
|
|
131
|
+
<div class="stat-value stat-total">{result.totalCount}</div>
|
|
132
|
+
<div class="stat-label">Total Affected</div>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="stat">
|
|
135
|
+
<div class="stat-value stat-direct">
|
|
136
|
+
{result.direct.length}
|
|
137
|
+
</div>
|
|
138
|
+
<div class="stat-label">Direct</div>
|
|
139
|
+
</div>
|
|
140
|
+
<div class="stat">
|
|
141
|
+
<div class="stat-value stat-transitive">
|
|
142
|
+
{result.transitive.length}
|
|
143
|
+
</div>
|
|
144
|
+
<div class="stat-label">Transitive</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<button
|
|
149
|
+
id="copy-markdown"
|
|
150
|
+
class="copy-button"
|
|
151
|
+
data-markdown={markdown}
|
|
152
|
+
aria-label="Copy impact analysis as Markdown"
|
|
153
|
+
>
|
|
154
|
+
<svg
|
|
155
|
+
aria-hidden="true"
|
|
156
|
+
fill="none"
|
|
157
|
+
viewBox="0 0 24 24"
|
|
158
|
+
stroke="currentColor"
|
|
159
|
+
stroke-width="2"
|
|
160
|
+
>
|
|
161
|
+
<path
|
|
162
|
+
stroke-linecap="round"
|
|
163
|
+
stroke-linejoin="round"
|
|
164
|
+
d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
|
|
165
|
+
/>
|
|
166
|
+
</svg>
|
|
167
|
+
Copy as Markdown
|
|
168
|
+
</button>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
{result.totalCount === 0 ? (
|
|
172
|
+
<div class="no-impact">
|
|
173
|
+
<svg
|
|
174
|
+
aria-hidden="true"
|
|
175
|
+
fill="none"
|
|
176
|
+
viewBox="0 0 24 24"
|
|
177
|
+
stroke="currentColor"
|
|
178
|
+
stroke-width="1.5"
|
|
179
|
+
>
|
|
180
|
+
<path
|
|
181
|
+
stroke-linecap="round"
|
|
182
|
+
stroke-linejoin="round"
|
|
183
|
+
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
184
|
+
/>
|
|
185
|
+
</svg>
|
|
186
|
+
<p>
|
|
187
|
+
No screens depend on <code>{apiQuery}</code>
|
|
188
|
+
</p>
|
|
189
|
+
</div>
|
|
190
|
+
) : (
|
|
191
|
+
<>
|
|
192
|
+
<div
|
|
193
|
+
class="impact-graph"
|
|
194
|
+
role="img"
|
|
195
|
+
aria-label={`Impact analysis graph showing ${result.direct.length} direct and ${result.transitive.length} transitive dependencies for ${apiQuery}`}
|
|
196
|
+
>
|
|
197
|
+
<div class="graph-container">
|
|
198
|
+
<pre class="mermaid" aria-hidden="true">
|
|
199
|
+
{mermaidGraph}
|
|
200
|
+
</pre>
|
|
201
|
+
</div>
|
|
202
|
+
<div class="graph-legend">
|
|
203
|
+
<div class="graph-legend-item">
|
|
204
|
+
<div class="legend-node legend-direct" />
|
|
205
|
+
<span>Direct dependency</span>
|
|
206
|
+
</div>
|
|
207
|
+
<div class="graph-legend-item">
|
|
208
|
+
<div class="legend-node legend-transitive" />
|
|
209
|
+
<span>Transitive</span>
|
|
210
|
+
</div>
|
|
211
|
+
<div class="graph-legend-item">
|
|
212
|
+
<div class="legend-node" />
|
|
213
|
+
<span>Not affected</span>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<div class="impact-details">
|
|
219
|
+
{result.direct.length > 0 && (
|
|
220
|
+
<div class="section">
|
|
221
|
+
<h3 class="section-title">
|
|
222
|
+
<svg
|
|
223
|
+
aria-hidden="true"
|
|
224
|
+
fill="none"
|
|
225
|
+
viewBox="0 0 24 24"
|
|
226
|
+
stroke="currentColor"
|
|
227
|
+
stroke-width="2"
|
|
228
|
+
>
|
|
229
|
+
<path
|
|
230
|
+
stroke-linecap="round"
|
|
231
|
+
stroke-linejoin="round"
|
|
232
|
+
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
|
|
233
|
+
/>
|
|
234
|
+
</svg>
|
|
235
|
+
Direct Dependencies ({result.direct.length})
|
|
236
|
+
</h3>
|
|
237
|
+
<div class="screen-link-list">
|
|
238
|
+
{result.direct.map((screen) => (
|
|
239
|
+
<a
|
|
240
|
+
href={`/screen/${screen.id}`}
|
|
241
|
+
class="screen-link impact-screen direct"
|
|
242
|
+
>
|
|
243
|
+
<div class="screen-link-info">
|
|
244
|
+
<div class="screen-link-title">
|
|
245
|
+
{screen.title}
|
|
246
|
+
</div>
|
|
247
|
+
<div class="screen-link-id">{screen.route}</div>
|
|
248
|
+
</div>
|
|
249
|
+
{screen.owner && screen.owner.length > 0 && (
|
|
250
|
+
<div class="screen-link-owner">
|
|
251
|
+
{screen.owner.join(", ")}
|
|
252
|
+
</div>
|
|
253
|
+
)}
|
|
254
|
+
</a>
|
|
255
|
+
))}
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
)}
|
|
259
|
+
|
|
260
|
+
{result.transitive.length > 0 && (
|
|
261
|
+
<div class="section">
|
|
262
|
+
<h3 class="section-title">
|
|
263
|
+
<svg
|
|
264
|
+
aria-hidden="true"
|
|
265
|
+
fill="none"
|
|
266
|
+
viewBox="0 0 24 24"
|
|
267
|
+
stroke="currentColor"
|
|
268
|
+
stroke-width="2"
|
|
269
|
+
>
|
|
270
|
+
<path
|
|
271
|
+
stroke-linecap="round"
|
|
272
|
+
stroke-linejoin="round"
|
|
273
|
+
d="M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5"
|
|
274
|
+
/>
|
|
275
|
+
</svg>
|
|
276
|
+
Transitive Dependencies ({result.transitive.length})
|
|
277
|
+
</h3>
|
|
278
|
+
<div class="screen-link-list">
|
|
279
|
+
{result.transitive.map(({ screen, path }) => (
|
|
280
|
+
<a
|
|
281
|
+
href={`/screen/${screen.id}`}
|
|
282
|
+
class="screen-link impact-screen transitive"
|
|
283
|
+
>
|
|
284
|
+
<div class="screen-link-info">
|
|
285
|
+
<div class="screen-link-title">
|
|
286
|
+
{screen.title}
|
|
287
|
+
</div>
|
|
288
|
+
<div class="screen-link-path">
|
|
289
|
+
{path.join(" → ")}
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
</a>
|
|
293
|
+
))}
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
)}
|
|
297
|
+
</div>
|
|
298
|
+
</>
|
|
299
|
+
)}
|
|
300
|
+
</div>
|
|
301
|
+
)}
|
|
302
|
+
</>
|
|
303
|
+
)
|
|
304
|
+
}
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<script>
|
|
308
|
+
import mermaid from "mermaid"
|
|
309
|
+
|
|
310
|
+
mermaid.initialize({
|
|
311
|
+
startOnLoad: true,
|
|
312
|
+
theme: "base",
|
|
313
|
+
themeVariables: {
|
|
314
|
+
darkMode: true,
|
|
315
|
+
background: "transparent",
|
|
316
|
+
fontFamily: "system-ui, sans-serif",
|
|
317
|
+
primaryColor: "#1e293b",
|
|
318
|
+
primaryTextColor: "#e2e8f0",
|
|
319
|
+
primaryBorderColor: "#64748b",
|
|
320
|
+
lineColor: "#64748b",
|
|
321
|
+
secondaryColor: "#1e293b",
|
|
322
|
+
tertiaryColor: "#1e293b",
|
|
323
|
+
nodeTextColor: "#ffffff",
|
|
324
|
+
},
|
|
325
|
+
flowchart: {
|
|
326
|
+
useMaxWidth: true,
|
|
327
|
+
htmlLabels: true,
|
|
328
|
+
curve: "basis",
|
|
329
|
+
padding: 20,
|
|
330
|
+
nodeSpacing: 50,
|
|
331
|
+
rankSpacing: 60,
|
|
332
|
+
},
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
// Copy markdown functionality
|
|
336
|
+
const copyButton = document.getElementById("copy-markdown")
|
|
337
|
+
if (copyButton) {
|
|
338
|
+
copyButton.addEventListener("click", async () => {
|
|
339
|
+
const markdown = copyButton.dataset.markdown
|
|
340
|
+
if (markdown) {
|
|
341
|
+
await navigator.clipboard.writeText(markdown)
|
|
342
|
+
const originalText = copyButton.innerHTML
|
|
343
|
+
copyButton.innerHTML = `
|
|
344
|
+
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
345
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" />
|
|
346
|
+
</svg>
|
|
347
|
+
Copied!
|
|
348
|
+
`
|
|
349
|
+
setTimeout(() => {
|
|
350
|
+
copyButton.innerHTML = originalText
|
|
351
|
+
}, 2000)
|
|
352
|
+
}
|
|
353
|
+
})
|
|
354
|
+
}
|
|
355
|
+
</script>
|
|
356
|
+
</Layout>
|
|
357
|
+
|
|
358
|
+
<style>
|
|
359
|
+
.impact-search {
|
|
360
|
+
margin-bottom: 32px;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.search-form {
|
|
364
|
+
margin-bottom: 16px;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.impact-search-wrapper {
|
|
368
|
+
display: flex;
|
|
369
|
+
gap: 12px;
|
|
370
|
+
max-width: 600px;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.impact-search-wrapper .search-input {
|
|
374
|
+
flex: 1;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.search-button {
|
|
378
|
+
padding: 10px 20px;
|
|
379
|
+
font-size: var(--text-sm);
|
|
380
|
+
font-weight: 500;
|
|
381
|
+
color: var(--color-bg);
|
|
382
|
+
background: var(--color-accent);
|
|
383
|
+
border: none;
|
|
384
|
+
border-radius: var(--radius-md);
|
|
385
|
+
cursor: pointer;
|
|
386
|
+
transition: background 0.15s ease;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.search-button:hover {
|
|
390
|
+
background: var(--color-accent-hover);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.api-suggestions {
|
|
394
|
+
margin-top: 16px;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.suggestions-label {
|
|
398
|
+
font-size: var(--text-sm);
|
|
399
|
+
color: var(--color-text-muted);
|
|
400
|
+
margin-bottom: 8px;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.tag-more {
|
|
404
|
+
background: transparent;
|
|
405
|
+
border-style: dashed;
|
|
406
|
+
cursor: default;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.impact-summary {
|
|
410
|
+
display: flex;
|
|
411
|
+
justify-content: space-between;
|
|
412
|
+
align-items: center;
|
|
413
|
+
padding: 24px;
|
|
414
|
+
background: var(--color-surface);
|
|
415
|
+
border: 1px solid var(--color-border);
|
|
416
|
+
border-radius: var(--radius-lg);
|
|
417
|
+
margin-bottom: 24px;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.stat-direct {
|
|
421
|
+
color: #f87171;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.stat-transitive {
|
|
425
|
+
color: #fb923c;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.copy-button {
|
|
429
|
+
display: flex;
|
|
430
|
+
align-items: center;
|
|
431
|
+
gap: 8px;
|
|
432
|
+
padding: 10px 16px;
|
|
433
|
+
font-size: var(--text-sm);
|
|
434
|
+
font-weight: 500;
|
|
435
|
+
color: var(--color-text-secondary);
|
|
436
|
+
background: var(--color-bg-muted);
|
|
437
|
+
border: 1px solid var(--color-border);
|
|
438
|
+
border-radius: var(--radius-md);
|
|
439
|
+
cursor: pointer;
|
|
440
|
+
transition: all 0.15s ease;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.copy-button:hover {
|
|
444
|
+
border-color: var(--color-border-hover);
|
|
445
|
+
color: var(--color-text);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.copy-button svg {
|
|
449
|
+
width: 16px;
|
|
450
|
+
height: 16px;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.no-impact {
|
|
454
|
+
display: flex;
|
|
455
|
+
flex-direction: column;
|
|
456
|
+
align-items: center;
|
|
457
|
+
gap: 12px;
|
|
458
|
+
padding: 48px 24px;
|
|
459
|
+
background: var(--color-surface);
|
|
460
|
+
border: 1px solid var(--color-border);
|
|
461
|
+
border-radius: var(--radius-lg);
|
|
462
|
+
text-align: center;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.no-impact svg {
|
|
466
|
+
width: 48px;
|
|
467
|
+
height: 48px;
|
|
468
|
+
color: var(--color-success);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.no-impact p {
|
|
472
|
+
font-size: var(--text-lg);
|
|
473
|
+
color: var(--color-text-secondary);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.impact-graph {
|
|
477
|
+
margin-bottom: 32px;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/* Override Mermaid text colors for better contrast */
|
|
481
|
+
.impact-graph :global(.mermaid .node.direct .nodeLabel),
|
|
482
|
+
.impact-graph :global(.mermaid .node.direct .nodeLabel p),
|
|
483
|
+
.impact-graph :global(.mermaid .node.direct .nodeLabel span),
|
|
484
|
+
.impact-graph :global(.mermaid .node.transitive .nodeLabel),
|
|
485
|
+
.impact-graph :global(.mermaid .node.transitive .nodeLabel p),
|
|
486
|
+
.impact-graph :global(.mermaid .node.transitive .nodeLabel span) {
|
|
487
|
+
fill: #ffffff !important;
|
|
488
|
+
color: #ffffff !important;
|
|
489
|
+
font-weight: 600 !important;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.impact-graph :global(.mermaid .node.normal .nodeLabel),
|
|
493
|
+
.impact-graph :global(.mermaid .node.normal .nodeLabel p),
|
|
494
|
+
.impact-graph :global(.mermaid .node.normal .nodeLabel span) {
|
|
495
|
+
fill: #e2e8f0 !important;
|
|
496
|
+
color: #e2e8f0 !important;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.impact-graph :global(.mermaid .nodeLabel) {
|
|
500
|
+
font-weight: 500;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.impact-graph :global(.mermaid .nodeLabel p) {
|
|
504
|
+
margin: 0;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.legend-direct {
|
|
508
|
+
background: #dc2626 !important;
|
|
509
|
+
border-color: #fef2f2 !important;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.legend-transitive {
|
|
513
|
+
background: #ea580c !important;
|
|
514
|
+
border-color: #fff7ed !important;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.impact-details {
|
|
518
|
+
display: grid;
|
|
519
|
+
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
|
520
|
+
gap: 24px;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.section-title {
|
|
524
|
+
display: flex;
|
|
525
|
+
align-items: center;
|
|
526
|
+
gap: 8px;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.section-title svg {
|
|
530
|
+
width: 18px;
|
|
531
|
+
height: 18px;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.impact-screen.direct {
|
|
535
|
+
border-left: 3px solid #dc2626;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.impact-screen.transitive {
|
|
539
|
+
border-left: 3px solid #ea580c;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.screen-link-path {
|
|
543
|
+
font-size: var(--text-xs);
|
|
544
|
+
font-family: ui-monospace, monospace;
|
|
545
|
+
color: var(--color-text-muted);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.screen-link-owner {
|
|
549
|
+
font-size: var(--text-xs);
|
|
550
|
+
color: var(--color-text-muted);
|
|
551
|
+
padding: 4px 8px;
|
|
552
|
+
background: var(--color-bg);
|
|
553
|
+
border-radius: var(--radius-sm);
|
|
554
|
+
}
|
|
555
|
+
</style>
|