@bene-npm/shield-ui 0.1.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.
@@ -0,0 +1,138 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ declare const T: {
4
+ readonly bg: "#06080c";
5
+ readonly surface: "#0b0f18";
6
+ readonly surfaceAlt: "#0e1320";
7
+ readonly border: "#151c2e";
8
+ readonly borderHover: "#1e2a44";
9
+ readonly text: "#c8d4e6";
10
+ readonly textMuted: "#5a6a88";
11
+ readonly textDim: "#2e3a52";
12
+ readonly cyan: "#00e5ff";
13
+ readonly cyanDim: "rgba(0,229,255,0.08)";
14
+ readonly cyanBorder: "rgba(0,229,255,0.2)";
15
+ readonly critical: "#ff2d55";
16
+ readonly high: "#ff6b2b";
17
+ readonly medium: "#ffb800";
18
+ readonly low: "#00e5ff";
19
+ readonly info: "#6e7fff";
20
+ readonly success: "#00e676";
21
+ readonly font: "'Outfit', sans-serif";
22
+ readonly mono: "'JetBrains Mono', 'Fira Code', monospace";
23
+ };
24
+ declare const SEV: {
25
+ readonly CRITICAL: {
26
+ readonly color: "#ff2d55";
27
+ readonly bg: "rgba(255,45,85,0.08)";
28
+ readonly glow: "rgba(255,45,85,0.25)";
29
+ };
30
+ readonly HIGH: {
31
+ readonly color: "#ff6b2b";
32
+ readonly bg: "rgba(255,107,43,0.08)";
33
+ readonly glow: "rgba(255,107,43,0.25)";
34
+ };
35
+ readonly MEDIUM: {
36
+ readonly color: "#ffb800";
37
+ readonly bg: "rgba(255,184,0,0.08)";
38
+ readonly glow: "rgba(255,184,0,0.25)";
39
+ };
40
+ readonly LOW: {
41
+ readonly color: "#00e5ff";
42
+ readonly bg: "rgba(0,229,255,0.08)";
43
+ readonly glow: "rgba(0,229,255,0.25)";
44
+ };
45
+ readonly INFO: {
46
+ readonly color: "#6e7fff";
47
+ readonly bg: "rgba(110,127,255,0.08)";
48
+ readonly glow: "rgba(110,127,255,0.25)";
49
+ };
50
+ };
51
+ type SeverityLevel = keyof typeof SEV;
52
+ type ScanStatus = "scanning" | "complete" | "error" | "warning";
53
+ type StatusState = "online" | "warning" | "critical" | "offline" | "scanning";
54
+ type ShieldStatus = "verified" | "warning" | "compromised" | "unknown";
55
+ type Size = "sm" | "md" | "lg";
56
+
57
+ interface AlertBannerProps {
58
+ severity?: SeverityLevel;
59
+ title?: string;
60
+ children?: React.ReactNode;
61
+ dismissible?: boolean;
62
+ onDismiss?: () => void;
63
+ }
64
+ declare function AlertBanner({ severity, title, children, dismissible, onDismiss }: AlertBannerProps): react_jsx_runtime.JSX.Element | null;
65
+
66
+ interface CveTagProps {
67
+ id?: string;
68
+ severity?: SeverityLevel;
69
+ }
70
+ declare function CveTag({ id, severity }: CveTagProps): react_jsx_runtime.JSX.Element;
71
+
72
+ interface DataFlowLineProps {
73
+ from?: string;
74
+ to?: string;
75
+ encrypted?: boolean;
76
+ threat?: boolean;
77
+ }
78
+ declare function DataFlowLine({ from, to, encrypted, threat }: DataFlowLineProps): react_jsx_runtime.JSX.Element;
79
+
80
+ interface ScanProgressProps {
81
+ progress?: number;
82
+ status?: ScanStatus;
83
+ label?: string;
84
+ }
85
+ declare function ScanProgress({ progress, status, label }: ScanProgressProps): react_jsx_runtime.JSX.Element;
86
+
87
+ interface SecurityScoreProps {
88
+ score?: number;
89
+ size?: number;
90
+ label?: string;
91
+ }
92
+ declare function SecurityScore({ score, size, label }: SecurityScoreProps): react_jsx_runtime.JSX.Element;
93
+
94
+ interface SeverityBadgeProps {
95
+ level?: SeverityLevel;
96
+ size?: Size;
97
+ pulse?: boolean;
98
+ count?: number;
99
+ }
100
+ declare function SeverityBadge({ level, size, pulse, count }: SeverityBadgeProps): react_jsx_runtime.JSX.Element;
101
+
102
+ interface ShieldBadgeProps {
103
+ status?: ShieldStatus;
104
+ label?: string;
105
+ }
106
+ declare function ShieldBadge({ status, label }: ShieldBadgeProps): react_jsx_runtime.JSX.Element;
107
+
108
+ interface StatusIndicatorProps {
109
+ status?: StatusState;
110
+ label?: string;
111
+ size?: Size;
112
+ }
113
+ declare function StatusIndicator({ status, label, size }: StatusIndicatorProps): react_jsx_runtime.JSX.Element;
114
+
115
+ type TerminalLine = string | {
116
+ text: string;
117
+ color: string;
118
+ };
119
+ interface TerminalOutputProps {
120
+ lines?: TerminalLine[];
121
+ title?: string;
122
+ typing?: boolean;
123
+ }
124
+ declare function TerminalOutput({ lines, title, typing }: TerminalOutputProps): react_jsx_runtime.JSX.Element;
125
+
126
+ interface ThreatCardProps {
127
+ id?: string;
128
+ severity?: SeverityLevel;
129
+ title?: string;
130
+ pkg?: string;
131
+ version?: string;
132
+ fixed?: string;
133
+ description?: string;
134
+ published?: string;
135
+ }
136
+ declare function ThreatCard({ id, severity, title, pkg, version, fixed, description, published, }: ThreatCardProps): react_jsx_runtime.JSX.Element;
137
+
138
+ export { AlertBanner, CveTag, DataFlowLine, SEV, ScanProgress, type ScanStatus, SecurityScore, SeverityBadge, type SeverityLevel, ShieldBadge, type ShieldStatus, type Size, StatusIndicator, type StatusState, T, type TerminalLine, TerminalOutput, ThreatCard };
@@ -0,0 +1,138 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ declare const T: {
4
+ readonly bg: "#06080c";
5
+ readonly surface: "#0b0f18";
6
+ readonly surfaceAlt: "#0e1320";
7
+ readonly border: "#151c2e";
8
+ readonly borderHover: "#1e2a44";
9
+ readonly text: "#c8d4e6";
10
+ readonly textMuted: "#5a6a88";
11
+ readonly textDim: "#2e3a52";
12
+ readonly cyan: "#00e5ff";
13
+ readonly cyanDim: "rgba(0,229,255,0.08)";
14
+ readonly cyanBorder: "rgba(0,229,255,0.2)";
15
+ readonly critical: "#ff2d55";
16
+ readonly high: "#ff6b2b";
17
+ readonly medium: "#ffb800";
18
+ readonly low: "#00e5ff";
19
+ readonly info: "#6e7fff";
20
+ readonly success: "#00e676";
21
+ readonly font: "'Outfit', sans-serif";
22
+ readonly mono: "'JetBrains Mono', 'Fira Code', monospace";
23
+ };
24
+ declare const SEV: {
25
+ readonly CRITICAL: {
26
+ readonly color: "#ff2d55";
27
+ readonly bg: "rgba(255,45,85,0.08)";
28
+ readonly glow: "rgba(255,45,85,0.25)";
29
+ };
30
+ readonly HIGH: {
31
+ readonly color: "#ff6b2b";
32
+ readonly bg: "rgba(255,107,43,0.08)";
33
+ readonly glow: "rgba(255,107,43,0.25)";
34
+ };
35
+ readonly MEDIUM: {
36
+ readonly color: "#ffb800";
37
+ readonly bg: "rgba(255,184,0,0.08)";
38
+ readonly glow: "rgba(255,184,0,0.25)";
39
+ };
40
+ readonly LOW: {
41
+ readonly color: "#00e5ff";
42
+ readonly bg: "rgba(0,229,255,0.08)";
43
+ readonly glow: "rgba(0,229,255,0.25)";
44
+ };
45
+ readonly INFO: {
46
+ readonly color: "#6e7fff";
47
+ readonly bg: "rgba(110,127,255,0.08)";
48
+ readonly glow: "rgba(110,127,255,0.25)";
49
+ };
50
+ };
51
+ type SeverityLevel = keyof typeof SEV;
52
+ type ScanStatus = "scanning" | "complete" | "error" | "warning";
53
+ type StatusState = "online" | "warning" | "critical" | "offline" | "scanning";
54
+ type ShieldStatus = "verified" | "warning" | "compromised" | "unknown";
55
+ type Size = "sm" | "md" | "lg";
56
+
57
+ interface AlertBannerProps {
58
+ severity?: SeverityLevel;
59
+ title?: string;
60
+ children?: React.ReactNode;
61
+ dismissible?: boolean;
62
+ onDismiss?: () => void;
63
+ }
64
+ declare function AlertBanner({ severity, title, children, dismissible, onDismiss }: AlertBannerProps): react_jsx_runtime.JSX.Element | null;
65
+
66
+ interface CveTagProps {
67
+ id?: string;
68
+ severity?: SeverityLevel;
69
+ }
70
+ declare function CveTag({ id, severity }: CveTagProps): react_jsx_runtime.JSX.Element;
71
+
72
+ interface DataFlowLineProps {
73
+ from?: string;
74
+ to?: string;
75
+ encrypted?: boolean;
76
+ threat?: boolean;
77
+ }
78
+ declare function DataFlowLine({ from, to, encrypted, threat }: DataFlowLineProps): react_jsx_runtime.JSX.Element;
79
+
80
+ interface ScanProgressProps {
81
+ progress?: number;
82
+ status?: ScanStatus;
83
+ label?: string;
84
+ }
85
+ declare function ScanProgress({ progress, status, label }: ScanProgressProps): react_jsx_runtime.JSX.Element;
86
+
87
+ interface SecurityScoreProps {
88
+ score?: number;
89
+ size?: number;
90
+ label?: string;
91
+ }
92
+ declare function SecurityScore({ score, size, label }: SecurityScoreProps): react_jsx_runtime.JSX.Element;
93
+
94
+ interface SeverityBadgeProps {
95
+ level?: SeverityLevel;
96
+ size?: Size;
97
+ pulse?: boolean;
98
+ count?: number;
99
+ }
100
+ declare function SeverityBadge({ level, size, pulse, count }: SeverityBadgeProps): react_jsx_runtime.JSX.Element;
101
+
102
+ interface ShieldBadgeProps {
103
+ status?: ShieldStatus;
104
+ label?: string;
105
+ }
106
+ declare function ShieldBadge({ status, label }: ShieldBadgeProps): react_jsx_runtime.JSX.Element;
107
+
108
+ interface StatusIndicatorProps {
109
+ status?: StatusState;
110
+ label?: string;
111
+ size?: Size;
112
+ }
113
+ declare function StatusIndicator({ status, label, size }: StatusIndicatorProps): react_jsx_runtime.JSX.Element;
114
+
115
+ type TerminalLine = string | {
116
+ text: string;
117
+ color: string;
118
+ };
119
+ interface TerminalOutputProps {
120
+ lines?: TerminalLine[];
121
+ title?: string;
122
+ typing?: boolean;
123
+ }
124
+ declare function TerminalOutput({ lines, title, typing }: TerminalOutputProps): react_jsx_runtime.JSX.Element;
125
+
126
+ interface ThreatCardProps {
127
+ id?: string;
128
+ severity?: SeverityLevel;
129
+ title?: string;
130
+ pkg?: string;
131
+ version?: string;
132
+ fixed?: string;
133
+ description?: string;
134
+ published?: string;
135
+ }
136
+ declare function ThreatCard({ id, severity, title, pkg, version, fixed, description, published, }: ThreatCardProps): react_jsx_runtime.JSX.Element;
137
+
138
+ export { AlertBanner, CveTag, DataFlowLine, SEV, ScanProgress, type ScanStatus, SecurityScore, SeverityBadge, type SeverityLevel, ShieldBadge, type ShieldStatus, type Size, StatusIndicator, type StatusState, T, type TerminalLine, TerminalOutput, ThreatCard };
package/dist/index.js ADDED
@@ -0,0 +1,391 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // components/AlertBanner.tsx
7
+
8
+ // lib/tokens.ts
9
+ var T = {
10
+ bg: "#06080c",
11
+ surface: "#0b0f18",
12
+ surfaceAlt: "#0e1320",
13
+ border: "#151c2e",
14
+ borderHover: "#1e2a44",
15
+ text: "#c8d4e6",
16
+ textMuted: "#5a6a88",
17
+ textDim: "#2e3a52",
18
+ cyan: "#00e5ff",
19
+ cyanDim: "rgba(0,229,255,0.08)",
20
+ cyanBorder: "rgba(0,229,255,0.2)",
21
+ critical: "#ff2d55",
22
+ high: "#ff6b2b",
23
+ medium: "#ffb800",
24
+ low: "#00e5ff",
25
+ info: "#6e7fff",
26
+ success: "#00e676",
27
+ font: "'Outfit', sans-serif",
28
+ mono: "'JetBrains Mono', 'Fira Code', monospace"
29
+ };
30
+ var SEV = {
31
+ CRITICAL: { color: T.critical, bg: "rgba(255,45,85,0.08)", glow: "rgba(255,45,85,0.25)" },
32
+ HIGH: { color: T.high, bg: "rgba(255,107,43,0.08)", glow: "rgba(255,107,43,0.25)" },
33
+ MEDIUM: { color: T.medium, bg: "rgba(255,184,0,0.08)", glow: "rgba(255,184,0,0.25)" },
34
+ LOW: { color: T.low, bg: "rgba(0,229,255,0.08)", glow: "rgba(0,229,255,0.25)" },
35
+ INFO: { color: T.info, bg: "rgba(110,127,255,0.08)", glow: "rgba(110,127,255,0.25)" }
36
+ };
37
+ var ICONS = {
38
+ CRITICAL: "\u{1F6A8}",
39
+ HIGH: "\u26A0\uFE0F",
40
+ MEDIUM: "\u26A1",
41
+ LOW: "\u2139\uFE0F",
42
+ INFO: "\u{1F4A1}"
43
+ };
44
+ function AlertBanner({ severity = "HIGH", title, children, dismissible = true, onDismiss }) {
45
+ const [dismissed, setDismissed] = react.useState(false);
46
+ if (dismissed) return null;
47
+ const cfg = SEV[severity] ?? SEV.INFO;
48
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
49
+ display: "flex",
50
+ alignItems: "flex-start",
51
+ gap: 10,
52
+ width: "100%",
53
+ boxSizing: "border-box",
54
+ background: cfg.bg,
55
+ border: `1px solid ${cfg.color}25`,
56
+ borderLeft: `3px solid ${cfg.color}`,
57
+ borderRadius: 6,
58
+ padding: "12px 14px"
59
+ }, children: [
60
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 15, flexShrink: 0, marginTop: 1 }, children: ICONS[severity] }),
61
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
62
+ title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, fontWeight: 700, color: cfg.color, fontFamily: T.font, marginBottom: 3 }, children: title }),
63
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: T.text, lineHeight: 1.5, fontFamily: T.font }, children })
64
+ ] }),
65
+ dismissible && /* @__PURE__ */ jsxRuntime.jsx(
66
+ "button",
67
+ {
68
+ onClick: () => {
69
+ setDismissed(true);
70
+ onDismiss?.();
71
+ },
72
+ style: { background: "none", border: "none", color: T.textDim, cursor: "pointer", fontSize: 14, padding: 0, lineHeight: 1, flexShrink: 0 },
73
+ children: "\xD7"
74
+ }
75
+ )
76
+ ] });
77
+ }
78
+ function CveTag({ id = "CVE-2024-0001", severity = "HIGH" }) {
79
+ const cfg = SEV[severity] ?? SEV.INFO;
80
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { style: {
81
+ display: "inline-flex",
82
+ alignItems: "center",
83
+ gap: 5,
84
+ background: T.surfaceAlt,
85
+ border: `1px solid ${T.border}`,
86
+ borderRadius: 4,
87
+ padding: "3px 8px",
88
+ fontSize: 11,
89
+ fontFamily: T.mono,
90
+ color: T.text,
91
+ fontWeight: 500
92
+ }, children: [
93
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 6, height: 6, borderRadius: "50%", background: cfg.color, boxShadow: `0 0 4px ${cfg.glow}` } }),
94
+ id
95
+ ] });
96
+ }
97
+ function DataFlowLine({ from = "Client", to = "Server", encrypted = true, threat = false }) {
98
+ const lineColor = threat ? T.critical : encrypted ? T.cyan : T.textDim;
99
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 0, width: "100%" }, children: [
100
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { background: T.surface, border: `1px solid ${T.border}`, borderRadius: 20, padding: "6px 14px", fontSize: 11, fontFamily: T.mono, color: T.text, fontWeight: 500, whiteSpace: "nowrap", flexShrink: 0 }, children: from }),
101
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, height: 2, position: "relative", margin: "0 -1px", minWidth: 40 }, children: [
102
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, background: lineColor, opacity: 0.3 } }),
103
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", top: 0, left: 0, height: "100%", width: 30, background: lineColor, opacity: 0.8, animation: "flowPulse 1.5s infinite linear", borderRadius: 1 } }),
104
+ encrypted && !threat && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { position: "absolute", top: -9, left: "50%", transform: "translateX(-50%)", fontSize: 10 }, children: "\u{1F512}" }),
105
+ threat && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { position: "absolute", top: -9, left: "50%", transform: "translateX(-50%)", fontSize: 10 }, children: "\u26A0\uFE0F" })
106
+ ] }),
107
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", flexShrink: 0 }, children: [
108
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { position: "absolute", left: -6, top: "50%", transform: "translateY(-50%)", color: lineColor, fontSize: 8 }, children: "\u25B6" }),
109
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { background: T.surface, border: `1px solid ${T.border}`, borderRadius: 20, padding: "6px 14px", fontSize: 11, fontFamily: T.mono, color: T.text, fontWeight: 500, whiteSpace: "nowrap", marginLeft: 4 }, children: to })
110
+ ] })
111
+ ] });
112
+ }
113
+ var STATUS_COLORS = {
114
+ scanning: T.cyan,
115
+ complete: T.success,
116
+ error: T.critical,
117
+ warning: T.medium
118
+ };
119
+ function ScanProgress({ progress = 0, status = "scanning", label = "Scanning dependencies..." }) {
120
+ const c = STATUS_COLORS[status];
121
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { width: "100%" }, children: [
122
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6 }, children: [
123
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
124
+ status === "scanning" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 6, height: 6, borderRadius: "50%", background: c, animation: "dotPulse 1s infinite" } }),
125
+ status === "complete" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: c, fontSize: 11 }, children: "\u2713" }),
126
+ status === "error" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: c, fontSize: 11 }, children: "\u2715" }),
127
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: T.text, fontFamily: T.font }, children: label })
128
+ ] }),
129
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: 11, color: c, fontFamily: T.mono, fontWeight: 600 }, children: [
130
+ Math.round(progress),
131
+ "%"
132
+ ] })
133
+ ] }),
134
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: "100%", height: 4, background: T.border, borderRadius: 2, overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "100%", borderRadius: 2, background: c, width: `${progress}%`, transition: "width 0.4s ease", boxShadow: `0 0 8px ${c}40` }, children: status === "scanning" && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: "100%", height: "100%", background: `linear-gradient(90deg, transparent, ${c}60, transparent)`, animation: "scanShimmer 1.2s infinite" } }) }) })
135
+ ] });
136
+ }
137
+ function SecurityScore({ score = 72, size = 120, label = "Security Score" }) {
138
+ const [animScore, setAnimScore] = react.useState(0);
139
+ react.useEffect(() => {
140
+ let frame = 0;
141
+ const dur = 40;
142
+ const iv = setInterval(() => {
143
+ frame++;
144
+ setAnimScore(Math.round(frame / dur * score));
145
+ if (frame >= dur) clearInterval(iv);
146
+ }, 20);
147
+ return () => clearInterval(iv);
148
+ }, [score]);
149
+ const color = animScore >= 80 ? T.success : animScore >= 60 ? T.medium : animScore >= 40 ? T.high : T.critical;
150
+ const r = (size - 12) / 2;
151
+ const circ = 2 * Math.PI * r;
152
+ const offset = circ - circ * animScore / 100;
153
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8 }, children: [
154
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", width: size, height: size }, children: [
155
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, style: { transform: "rotate(-90deg)" }, children: [
156
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: size / 2, cy: size / 2, r, fill: "none", stroke: T.border, strokeWidth: 5 }),
157
+ /* @__PURE__ */ jsxRuntime.jsx(
158
+ "circle",
159
+ {
160
+ cx: size / 2,
161
+ cy: size / 2,
162
+ r,
163
+ fill: "none",
164
+ stroke: color,
165
+ strokeWidth: 5,
166
+ strokeDasharray: circ,
167
+ strokeDashoffset: offset,
168
+ strokeLinecap: "round",
169
+ style: { transition: "stroke-dashoffset 0.3s ease, stroke 0.3s ease", filter: `drop-shadow(0 0 6px ${color}60)` }
170
+ }
171
+ )
172
+ ] }),
173
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }, children: [
174
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: size * 0.28, fontWeight: 800, fontFamily: T.mono, color, lineHeight: 1 }, children: animScore }),
175
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: size * 0.08, color: T.textMuted, fontFamily: T.mono, letterSpacing: "0.1em", marginTop: 2 }, children: "/100" })
176
+ ] })
177
+ ] }),
178
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 11, color: T.textMuted, fontFamily: T.mono, letterSpacing: "0.04em" }, children: label })
179
+ ] });
180
+ }
181
+ function SeverityBadge({ level = "MEDIUM", size = "md", pulse = false, count }) {
182
+ const cfg = SEV[level] ?? SEV.INFO;
183
+ const sizes = {
184
+ sm: { fs: 9, px: 6, py: 2 },
185
+ md: { fs: 10, px: 10, py: 3 },
186
+ lg: { fs: 12, px: 14, py: 5 }
187
+ };
188
+ const s = sizes[size];
189
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { style: {
190
+ display: "inline-flex",
191
+ alignItems: "center",
192
+ gap: 5,
193
+ background: cfg.bg,
194
+ color: cfg.color,
195
+ border: `1px solid ${cfg.color}30`,
196
+ padding: `${s.py}px ${s.px}px`,
197
+ borderRadius: 4,
198
+ fontSize: s.fs,
199
+ fontWeight: 700,
200
+ fontFamily: T.mono,
201
+ letterSpacing: "0.06em",
202
+ lineHeight: 1,
203
+ animation: pulse ? "badgePulse 2s infinite" : "none",
204
+ boxShadow: pulse ? `0 0 12px ${cfg.glow}` : "none"
205
+ }, children: [
206
+ pulse && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 6, height: 6, borderRadius: "50%", background: cfg.color, animation: "dotPulse 1.5s infinite" } }),
207
+ level,
208
+ count != null && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { background: cfg.color, color: "#000", borderRadius: 10, padding: "1px 5px", fontSize: s.fs - 1, fontWeight: 800 }, children: count })
209
+ ] });
210
+ }
211
+ var SHIELD_CFG = {
212
+ verified: { color: T.success, icon: "\u2713", text: "Verified" },
213
+ warning: { color: T.medium, icon: "!", text: "Warning" },
214
+ compromised: { color: T.critical, icon: "\u2715", text: "Compromised" },
215
+ unknown: { color: T.textDim, icon: "?", text: "Unknown" }
216
+ };
217
+ function ShieldBadge({ status = "verified", label }) {
218
+ const c = SHIELD_CFG[status];
219
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "inline-flex", alignItems: "center", gap: 8, background: T.surface, border: `1px solid ${T.border}`, borderRadius: 6, padding: "8px 14px" }, children: [
220
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "26", viewBox: "0 0 22 26", children: [
221
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M11 1L2 5v7c0 6.5 3.8 10.7 9 12.5 5.2-1.8 9-6 9-12.5V5L11 1z", fill: `${c.color}15`, stroke: c.color, strokeWidth: "1.5" }),
222
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: "11", y: "17", textAnchor: "middle", fill: c.color, fontSize: "11", fontWeight: "800", fontFamily: T.mono, children: c.icon })
223
+ ] }),
224
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
225
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, fontWeight: 700, color: c.color, fontFamily: T.mono }, children: label ?? c.text }),
226
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 9, color: T.textDim, fontFamily: T.mono, textTransform: "uppercase", letterSpacing: "0.08em" }, children: status })
227
+ ] })
228
+ ] });
229
+ }
230
+ var STATUS_CFG = {
231
+ online: { color: T.success, label: "Secure" },
232
+ warning: { color: T.medium, label: "Warning" },
233
+ critical: { color: T.critical, label: "Critical" },
234
+ offline: { color: T.textDim, label: "Offline" },
235
+ scanning: { color: T.cyan, label: "Scanning" }
236
+ };
237
+ function StatusIndicator({ status = "online", label, size = "md" }) {
238
+ const c = STATUS_CFG[status];
239
+ const dotSize = size === "sm" ? 7 : size === "lg" ? 11 : 9;
240
+ const fontSize = size === "sm" ? 11 : size === "lg" ? 14 : 12;
241
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "inline-flex", alignItems: "center", gap: 7 }, children: [
242
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
243
+ width: dotSize,
244
+ height: dotSize,
245
+ borderRadius: "50%",
246
+ background: c.color,
247
+ boxShadow: `0 0 6px ${c.color}50`,
248
+ animation: status === "offline" ? "none" : "dotPulse 2s infinite"
249
+ } }),
250
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize, color: T.text, fontFamily: T.font, fontWeight: 500 }, children: label ?? c.label })
251
+ ] });
252
+ }
253
+ function TerminalOutput({ lines = [], title = "terminal", typing = false }) {
254
+ const [visibleLines, setVisibleLines] = react.useState(typing ? [] : lines);
255
+ const containerRef = react.useRef(null);
256
+ react.useEffect(() => {
257
+ if (!typing || lines.length === 0) {
258
+ setVisibleLines(lines);
259
+ return;
260
+ }
261
+ setVisibleLines([]);
262
+ let ln = 0;
263
+ let ch = 0;
264
+ const iv = setInterval(() => {
265
+ if (ln >= lines.length) {
266
+ clearInterval(iv);
267
+ return;
268
+ }
269
+ const raw = lines[ln];
270
+ if (!raw) {
271
+ clearInterval(iv);
272
+ return;
273
+ }
274
+ const lineText = typeof raw === "string" ? raw : raw.text;
275
+ if (ch <= lineText.length) {
276
+ setVisibleLines(() => {
277
+ const next = lines.slice(0, ln).map(
278
+ (l) => typeof l === "string" ? l : { text: l.text, color: l.color }
279
+ );
280
+ const current = lines[ln] ?? lineText;
281
+ if (typeof current === "object") {
282
+ next.push({ text: lineText.slice(0, ch), color: current.color });
283
+ } else {
284
+ next.push(lineText.slice(0, ch));
285
+ }
286
+ return next;
287
+ });
288
+ ch++;
289
+ } else {
290
+ ln++;
291
+ ch = 0;
292
+ }
293
+ }, 18);
294
+ return () => clearInterval(iv);
295
+ }, [typing, lines]);
296
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { background: "#04060a", border: `1px solid ${T.border}`, borderRadius: 8, overflow: "hidden", width: "100%", boxSizing: "border-box" }, children: [
297
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, padding: "8px 12px", background: T.surface, borderBottom: `1px solid ${T.border}` }, children: [
298
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 10, height: 10, borderRadius: "50%", background: "#ff5f57" } }),
299
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 10, height: 10, borderRadius: "50%", background: "#febc2e" } }),
300
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 10, height: 10, borderRadius: "50%", background: "#28c840" } }),
301
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, color: T.textDim, fontFamily: T.mono, marginLeft: 6 }, children: title })
302
+ ] }),
303
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: { padding: "12px 14px", maxHeight: 200, overflowY: "auto", fontFamily: T.mono, fontSize: 12, lineHeight: 1.7 }, children: [
304
+ visibleLines.map((line, i) => {
305
+ const text = typeof line === "string" ? line : line.text;
306
+ const color = typeof line === "object" ? line.color : T.textMuted;
307
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color, whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: text || "\xA0" }, i);
308
+ }),
309
+ typing && visibleLines.length < lines.length && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: T.cyan, animation: "blink 0.8s infinite" }, children: "\u258C" })
310
+ ] })
311
+ ] });
312
+ }
313
+ function ThreatCard({
314
+ id = "CVE-2024-38816",
315
+ severity = "HIGH",
316
+ title = "Path Traversal in Spring Framework",
317
+ pkg = "spring-webmvc",
318
+ version = "6.1.12",
319
+ fixed = "6.1.13",
320
+ description = "Applications serving static resources through functional web frameworks are vulnerable to path traversal attacks.",
321
+ published = "2024-09-13"
322
+ }) {
323
+ const [expanded, setExpanded] = react.useState(false);
324
+ const cfg = SEV[severity] ?? SEV.INFO;
325
+ return /* @__PURE__ */ jsxRuntime.jsxs(
326
+ "div",
327
+ {
328
+ onClick: () => setExpanded(!expanded),
329
+ style: {
330
+ background: T.surface,
331
+ border: `1px solid ${T.border}`,
332
+ borderLeft: `3px solid ${cfg.color}`,
333
+ borderRadius: 6,
334
+ padding: "14px 16px",
335
+ cursor: "pointer",
336
+ transition: "all 0.2s ease",
337
+ width: "100%",
338
+ boxSizing: "border-box"
339
+ },
340
+ children: [
341
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 10 }, children: [
342
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
343
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }, children: [
344
+ /* @__PURE__ */ jsxRuntime.jsx(SeverityBadge, { level: severity, size: "sm" }),
345
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: T.cyan, fontFamily: T.mono, fontWeight: 600 }, children: id })
346
+ ] }),
347
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, color: T.text, fontWeight: 600, fontFamily: T.font, lineHeight: 1.4 }, children: title })
348
+ ] }),
349
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: T.textMuted, fontSize: 10, transform: expanded ? "rotate(180deg)" : "", transition: "transform 0.2s" }, children: "\u25BC" })
350
+ ] }),
351
+ expanded && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 12, paddingTop: 10, borderTop: `1px solid ${T.border}` }, children: [
352
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: 12, color: T.textMuted, lineHeight: 1.6, margin: "0 0 10px" }, children: description }),
353
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 14, fontSize: 11 }, children: [
354
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
355
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: T.textDim }, children: "Package: " }),
356
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: T.text, fontFamily: T.mono }, children: [
357
+ pkg,
358
+ "@",
359
+ version
360
+ ] })
361
+ ] }),
362
+ fixed && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
363
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: T.textDim }, children: "Fix: " }),
364
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: T.success, fontFamily: T.mono }, children: [
365
+ "\u2265",
366
+ fixed
367
+ ] })
368
+ ] }),
369
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
370
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: T.textDim }, children: "Published: " }),
371
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: T.textMuted, fontFamily: T.mono }, children: published })
372
+ ] })
373
+ ] })
374
+ ] })
375
+ ]
376
+ }
377
+ );
378
+ }
379
+
380
+ exports.AlertBanner = AlertBanner;
381
+ exports.CveTag = CveTag;
382
+ exports.DataFlowLine = DataFlowLine;
383
+ exports.SEV = SEV;
384
+ exports.ScanProgress = ScanProgress;
385
+ exports.SecurityScore = SecurityScore;
386
+ exports.SeverityBadge = SeverityBadge;
387
+ exports.ShieldBadge = ShieldBadge;
388
+ exports.StatusIndicator = StatusIndicator;
389
+ exports.T = T;
390
+ exports.TerminalOutput = TerminalOutput;
391
+ exports.ThreatCard = ThreatCard;
package/dist/index.mjs ADDED
@@ -0,0 +1,378 @@
1
+ import { useState, useEffect, useRef } from 'react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ // components/AlertBanner.tsx
5
+
6
+ // lib/tokens.ts
7
+ var T = {
8
+ bg: "#06080c",
9
+ surface: "#0b0f18",
10
+ surfaceAlt: "#0e1320",
11
+ border: "#151c2e",
12
+ borderHover: "#1e2a44",
13
+ text: "#c8d4e6",
14
+ textMuted: "#5a6a88",
15
+ textDim: "#2e3a52",
16
+ cyan: "#00e5ff",
17
+ cyanDim: "rgba(0,229,255,0.08)",
18
+ cyanBorder: "rgba(0,229,255,0.2)",
19
+ critical: "#ff2d55",
20
+ high: "#ff6b2b",
21
+ medium: "#ffb800",
22
+ low: "#00e5ff",
23
+ info: "#6e7fff",
24
+ success: "#00e676",
25
+ font: "'Outfit', sans-serif",
26
+ mono: "'JetBrains Mono', 'Fira Code', monospace"
27
+ };
28
+ var SEV = {
29
+ CRITICAL: { color: T.critical, bg: "rgba(255,45,85,0.08)", glow: "rgba(255,45,85,0.25)" },
30
+ HIGH: { color: T.high, bg: "rgba(255,107,43,0.08)", glow: "rgba(255,107,43,0.25)" },
31
+ MEDIUM: { color: T.medium, bg: "rgba(255,184,0,0.08)", glow: "rgba(255,184,0,0.25)" },
32
+ LOW: { color: T.low, bg: "rgba(0,229,255,0.08)", glow: "rgba(0,229,255,0.25)" },
33
+ INFO: { color: T.info, bg: "rgba(110,127,255,0.08)", glow: "rgba(110,127,255,0.25)" }
34
+ };
35
+ var ICONS = {
36
+ CRITICAL: "\u{1F6A8}",
37
+ HIGH: "\u26A0\uFE0F",
38
+ MEDIUM: "\u26A1",
39
+ LOW: "\u2139\uFE0F",
40
+ INFO: "\u{1F4A1}"
41
+ };
42
+ function AlertBanner({ severity = "HIGH", title, children, dismissible = true, onDismiss }) {
43
+ const [dismissed, setDismissed] = useState(false);
44
+ if (dismissed) return null;
45
+ const cfg = SEV[severity] ?? SEV.INFO;
46
+ return /* @__PURE__ */ jsxs("div", { style: {
47
+ display: "flex",
48
+ alignItems: "flex-start",
49
+ gap: 10,
50
+ width: "100%",
51
+ boxSizing: "border-box",
52
+ background: cfg.bg,
53
+ border: `1px solid ${cfg.color}25`,
54
+ borderLeft: `3px solid ${cfg.color}`,
55
+ borderRadius: 6,
56
+ padding: "12px 14px"
57
+ }, children: [
58
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 15, flexShrink: 0, marginTop: 1 }, children: ICONS[severity] }),
59
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
60
+ title && /* @__PURE__ */ jsx("div", { style: { fontSize: 13, fontWeight: 700, color: cfg.color, fontFamily: T.font, marginBottom: 3 }, children: title }),
61
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: T.text, lineHeight: 1.5, fontFamily: T.font }, children })
62
+ ] }),
63
+ dismissible && /* @__PURE__ */ jsx(
64
+ "button",
65
+ {
66
+ onClick: () => {
67
+ setDismissed(true);
68
+ onDismiss?.();
69
+ },
70
+ style: { background: "none", border: "none", color: T.textDim, cursor: "pointer", fontSize: 14, padding: 0, lineHeight: 1, flexShrink: 0 },
71
+ children: "\xD7"
72
+ }
73
+ )
74
+ ] });
75
+ }
76
+ function CveTag({ id = "CVE-2024-0001", severity = "HIGH" }) {
77
+ const cfg = SEV[severity] ?? SEV.INFO;
78
+ return /* @__PURE__ */ jsxs("span", { style: {
79
+ display: "inline-flex",
80
+ alignItems: "center",
81
+ gap: 5,
82
+ background: T.surfaceAlt,
83
+ border: `1px solid ${T.border}`,
84
+ borderRadius: 4,
85
+ padding: "3px 8px",
86
+ fontSize: 11,
87
+ fontFamily: T.mono,
88
+ color: T.text,
89
+ fontWeight: 500
90
+ }, children: [
91
+ /* @__PURE__ */ jsx("span", { style: { width: 6, height: 6, borderRadius: "50%", background: cfg.color, boxShadow: `0 0 4px ${cfg.glow}` } }),
92
+ id
93
+ ] });
94
+ }
95
+ function DataFlowLine({ from = "Client", to = "Server", encrypted = true, threat = false }) {
96
+ const lineColor = threat ? T.critical : encrypted ? T.cyan : T.textDim;
97
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 0, width: "100%" }, children: [
98
+ /* @__PURE__ */ jsx("div", { style: { background: T.surface, border: `1px solid ${T.border}`, borderRadius: 20, padding: "6px 14px", fontSize: 11, fontFamily: T.mono, color: T.text, fontWeight: 500, whiteSpace: "nowrap", flexShrink: 0 }, children: from }),
99
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, height: 2, position: "relative", margin: "0 -1px", minWidth: 40 }, children: [
100
+ /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: lineColor, opacity: 0.3 } }),
101
+ /* @__PURE__ */ jsx("div", { style: { position: "absolute", top: 0, left: 0, height: "100%", width: 30, background: lineColor, opacity: 0.8, animation: "flowPulse 1.5s infinite linear", borderRadius: 1 } }),
102
+ encrypted && !threat && /* @__PURE__ */ jsx("span", { style: { position: "absolute", top: -9, left: "50%", transform: "translateX(-50%)", fontSize: 10 }, children: "\u{1F512}" }),
103
+ threat && /* @__PURE__ */ jsx("span", { style: { position: "absolute", top: -9, left: "50%", transform: "translateX(-50%)", fontSize: 10 }, children: "\u26A0\uFE0F" })
104
+ ] }),
105
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", flexShrink: 0 }, children: [
106
+ /* @__PURE__ */ jsx("span", { style: { position: "absolute", left: -6, top: "50%", transform: "translateY(-50%)", color: lineColor, fontSize: 8 }, children: "\u25B6" }),
107
+ /* @__PURE__ */ jsx("div", { style: { background: T.surface, border: `1px solid ${T.border}`, borderRadius: 20, padding: "6px 14px", fontSize: 11, fontFamily: T.mono, color: T.text, fontWeight: 500, whiteSpace: "nowrap", marginLeft: 4 }, children: to })
108
+ ] })
109
+ ] });
110
+ }
111
+ var STATUS_COLORS = {
112
+ scanning: T.cyan,
113
+ complete: T.success,
114
+ error: T.critical,
115
+ warning: T.medium
116
+ };
117
+ function ScanProgress({ progress = 0, status = "scanning", label = "Scanning dependencies..." }) {
118
+ const c = STATUS_COLORS[status];
119
+ return /* @__PURE__ */ jsxs("div", { style: { width: "100%" }, children: [
120
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6 }, children: [
121
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
122
+ status === "scanning" && /* @__PURE__ */ jsx("span", { style: { width: 6, height: 6, borderRadius: "50%", background: c, animation: "dotPulse 1s infinite" } }),
123
+ status === "complete" && /* @__PURE__ */ jsx("span", { style: { color: c, fontSize: 11 }, children: "\u2713" }),
124
+ status === "error" && /* @__PURE__ */ jsx("span", { style: { color: c, fontSize: 11 }, children: "\u2715" }),
125
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: T.text, fontFamily: T.font }, children: label })
126
+ ] }),
127
+ /* @__PURE__ */ jsxs("span", { style: { fontSize: 11, color: c, fontFamily: T.mono, fontWeight: 600 }, children: [
128
+ Math.round(progress),
129
+ "%"
130
+ ] })
131
+ ] }),
132
+ /* @__PURE__ */ jsx("div", { style: { width: "100%", height: 4, background: T.border, borderRadius: 2, overflow: "hidden" }, children: /* @__PURE__ */ jsx("div", { style: { height: "100%", borderRadius: 2, background: c, width: `${progress}%`, transition: "width 0.4s ease", boxShadow: `0 0 8px ${c}40` }, children: status === "scanning" && /* @__PURE__ */ jsx("div", { style: { width: "100%", height: "100%", background: `linear-gradient(90deg, transparent, ${c}60, transparent)`, animation: "scanShimmer 1.2s infinite" } }) }) })
133
+ ] });
134
+ }
135
+ function SecurityScore({ score = 72, size = 120, label = "Security Score" }) {
136
+ const [animScore, setAnimScore] = useState(0);
137
+ useEffect(() => {
138
+ let frame = 0;
139
+ const dur = 40;
140
+ const iv = setInterval(() => {
141
+ frame++;
142
+ setAnimScore(Math.round(frame / dur * score));
143
+ if (frame >= dur) clearInterval(iv);
144
+ }, 20);
145
+ return () => clearInterval(iv);
146
+ }, [score]);
147
+ const color = animScore >= 80 ? T.success : animScore >= 60 ? T.medium : animScore >= 40 ? T.high : T.critical;
148
+ const r = (size - 12) / 2;
149
+ const circ = 2 * Math.PI * r;
150
+ const offset = circ - circ * animScore / 100;
151
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8 }, children: [
152
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", width: size, height: size }, children: [
153
+ /* @__PURE__ */ jsxs("svg", { width: size, height: size, style: { transform: "rotate(-90deg)" }, children: [
154
+ /* @__PURE__ */ jsx("circle", { cx: size / 2, cy: size / 2, r, fill: "none", stroke: T.border, strokeWidth: 5 }),
155
+ /* @__PURE__ */ jsx(
156
+ "circle",
157
+ {
158
+ cx: size / 2,
159
+ cy: size / 2,
160
+ r,
161
+ fill: "none",
162
+ stroke: color,
163
+ strokeWidth: 5,
164
+ strokeDasharray: circ,
165
+ strokeDashoffset: offset,
166
+ strokeLinecap: "round",
167
+ style: { transition: "stroke-dashoffset 0.3s ease, stroke 0.3s ease", filter: `drop-shadow(0 0 6px ${color}60)` }
168
+ }
169
+ )
170
+ ] }),
171
+ /* @__PURE__ */ jsxs("div", { style: { position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }, children: [
172
+ /* @__PURE__ */ jsx("span", { style: { fontSize: size * 0.28, fontWeight: 800, fontFamily: T.mono, color, lineHeight: 1 }, children: animScore }),
173
+ /* @__PURE__ */ jsx("span", { style: { fontSize: size * 0.08, color: T.textMuted, fontFamily: T.mono, letterSpacing: "0.1em", marginTop: 2 }, children: "/100" })
174
+ ] })
175
+ ] }),
176
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 11, color: T.textMuted, fontFamily: T.mono, letterSpacing: "0.04em" }, children: label })
177
+ ] });
178
+ }
179
+ function SeverityBadge({ level = "MEDIUM", size = "md", pulse = false, count }) {
180
+ const cfg = SEV[level] ?? SEV.INFO;
181
+ const sizes = {
182
+ sm: { fs: 9, px: 6, py: 2 },
183
+ md: { fs: 10, px: 10, py: 3 },
184
+ lg: { fs: 12, px: 14, py: 5 }
185
+ };
186
+ const s = sizes[size];
187
+ return /* @__PURE__ */ jsxs("span", { style: {
188
+ display: "inline-flex",
189
+ alignItems: "center",
190
+ gap: 5,
191
+ background: cfg.bg,
192
+ color: cfg.color,
193
+ border: `1px solid ${cfg.color}30`,
194
+ padding: `${s.py}px ${s.px}px`,
195
+ borderRadius: 4,
196
+ fontSize: s.fs,
197
+ fontWeight: 700,
198
+ fontFamily: T.mono,
199
+ letterSpacing: "0.06em",
200
+ lineHeight: 1,
201
+ animation: pulse ? "badgePulse 2s infinite" : "none",
202
+ boxShadow: pulse ? `0 0 12px ${cfg.glow}` : "none"
203
+ }, children: [
204
+ pulse && /* @__PURE__ */ jsx("span", { style: { width: 6, height: 6, borderRadius: "50%", background: cfg.color, animation: "dotPulse 1.5s infinite" } }),
205
+ level,
206
+ count != null && /* @__PURE__ */ jsx("span", { style: { background: cfg.color, color: "#000", borderRadius: 10, padding: "1px 5px", fontSize: s.fs - 1, fontWeight: 800 }, children: count })
207
+ ] });
208
+ }
209
+ var SHIELD_CFG = {
210
+ verified: { color: T.success, icon: "\u2713", text: "Verified" },
211
+ warning: { color: T.medium, icon: "!", text: "Warning" },
212
+ compromised: { color: T.critical, icon: "\u2715", text: "Compromised" },
213
+ unknown: { color: T.textDim, icon: "?", text: "Unknown" }
214
+ };
215
+ function ShieldBadge({ status = "verified", label }) {
216
+ const c = SHIELD_CFG[status];
217
+ return /* @__PURE__ */ jsxs("div", { style: { display: "inline-flex", alignItems: "center", gap: 8, background: T.surface, border: `1px solid ${T.border}`, borderRadius: 6, padding: "8px 14px" }, children: [
218
+ /* @__PURE__ */ jsxs("svg", { width: "22", height: "26", viewBox: "0 0 22 26", children: [
219
+ /* @__PURE__ */ jsx("path", { d: "M11 1L2 5v7c0 6.5 3.8 10.7 9 12.5 5.2-1.8 9-6 9-12.5V5L11 1z", fill: `${c.color}15`, stroke: c.color, strokeWidth: "1.5" }),
220
+ /* @__PURE__ */ jsx("text", { x: "11", y: "17", textAnchor: "middle", fill: c.color, fontSize: "11", fontWeight: "800", fontFamily: T.mono, children: c.icon })
221
+ ] }),
222
+ /* @__PURE__ */ jsxs("div", { children: [
223
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12, fontWeight: 700, color: c.color, fontFamily: T.mono }, children: label ?? c.text }),
224
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 9, color: T.textDim, fontFamily: T.mono, textTransform: "uppercase", letterSpacing: "0.08em" }, children: status })
225
+ ] })
226
+ ] });
227
+ }
228
+ var STATUS_CFG = {
229
+ online: { color: T.success, label: "Secure" },
230
+ warning: { color: T.medium, label: "Warning" },
231
+ critical: { color: T.critical, label: "Critical" },
232
+ offline: { color: T.textDim, label: "Offline" },
233
+ scanning: { color: T.cyan, label: "Scanning" }
234
+ };
235
+ function StatusIndicator({ status = "online", label, size = "md" }) {
236
+ const c = STATUS_CFG[status];
237
+ const dotSize = size === "sm" ? 7 : size === "lg" ? 11 : 9;
238
+ const fontSize = size === "sm" ? 11 : size === "lg" ? 14 : 12;
239
+ return /* @__PURE__ */ jsxs("div", { style: { display: "inline-flex", alignItems: "center", gap: 7 }, children: [
240
+ /* @__PURE__ */ jsx("span", { style: {
241
+ width: dotSize,
242
+ height: dotSize,
243
+ borderRadius: "50%",
244
+ background: c.color,
245
+ boxShadow: `0 0 6px ${c.color}50`,
246
+ animation: status === "offline" ? "none" : "dotPulse 2s infinite"
247
+ } }),
248
+ /* @__PURE__ */ jsx("span", { style: { fontSize, color: T.text, fontFamily: T.font, fontWeight: 500 }, children: label ?? c.label })
249
+ ] });
250
+ }
251
+ function TerminalOutput({ lines = [], title = "terminal", typing = false }) {
252
+ const [visibleLines, setVisibleLines] = useState(typing ? [] : lines);
253
+ const containerRef = useRef(null);
254
+ useEffect(() => {
255
+ if (!typing || lines.length === 0) {
256
+ setVisibleLines(lines);
257
+ return;
258
+ }
259
+ setVisibleLines([]);
260
+ let ln = 0;
261
+ let ch = 0;
262
+ const iv = setInterval(() => {
263
+ if (ln >= lines.length) {
264
+ clearInterval(iv);
265
+ return;
266
+ }
267
+ const raw = lines[ln];
268
+ if (!raw) {
269
+ clearInterval(iv);
270
+ return;
271
+ }
272
+ const lineText = typeof raw === "string" ? raw : raw.text;
273
+ if (ch <= lineText.length) {
274
+ setVisibleLines(() => {
275
+ const next = lines.slice(0, ln).map(
276
+ (l) => typeof l === "string" ? l : { text: l.text, color: l.color }
277
+ );
278
+ const current = lines[ln] ?? lineText;
279
+ if (typeof current === "object") {
280
+ next.push({ text: lineText.slice(0, ch), color: current.color });
281
+ } else {
282
+ next.push(lineText.slice(0, ch));
283
+ }
284
+ return next;
285
+ });
286
+ ch++;
287
+ } else {
288
+ ln++;
289
+ ch = 0;
290
+ }
291
+ }, 18);
292
+ return () => clearInterval(iv);
293
+ }, [typing, lines]);
294
+ return /* @__PURE__ */ jsxs("div", { style: { background: "#04060a", border: `1px solid ${T.border}`, borderRadius: 8, overflow: "hidden", width: "100%", boxSizing: "border-box" }, children: [
295
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, padding: "8px 12px", background: T.surface, borderBottom: `1px solid ${T.border}` }, children: [
296
+ /* @__PURE__ */ jsx("span", { style: { width: 10, height: 10, borderRadius: "50%", background: "#ff5f57" } }),
297
+ /* @__PURE__ */ jsx("span", { style: { width: 10, height: 10, borderRadius: "50%", background: "#febc2e" } }),
298
+ /* @__PURE__ */ jsx("span", { style: { width: 10, height: 10, borderRadius: "50%", background: "#28c840" } }),
299
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 10, color: T.textDim, fontFamily: T.mono, marginLeft: 6 }, children: title })
300
+ ] }),
301
+ /* @__PURE__ */ jsxs("div", { ref: containerRef, style: { padding: "12px 14px", maxHeight: 200, overflowY: "auto", fontFamily: T.mono, fontSize: 12, lineHeight: 1.7 }, children: [
302
+ visibleLines.map((line, i) => {
303
+ const text = typeof line === "string" ? line : line.text;
304
+ const color = typeof line === "object" ? line.color : T.textMuted;
305
+ return /* @__PURE__ */ jsx("div", { style: { color, whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: text || "\xA0" }, i);
306
+ }),
307
+ typing && visibleLines.length < lines.length && /* @__PURE__ */ jsx("span", { style: { color: T.cyan, animation: "blink 0.8s infinite" }, children: "\u258C" })
308
+ ] })
309
+ ] });
310
+ }
311
+ function ThreatCard({
312
+ id = "CVE-2024-38816",
313
+ severity = "HIGH",
314
+ title = "Path Traversal in Spring Framework",
315
+ pkg = "spring-webmvc",
316
+ version = "6.1.12",
317
+ fixed = "6.1.13",
318
+ description = "Applications serving static resources through functional web frameworks are vulnerable to path traversal attacks.",
319
+ published = "2024-09-13"
320
+ }) {
321
+ const [expanded, setExpanded] = useState(false);
322
+ const cfg = SEV[severity] ?? SEV.INFO;
323
+ return /* @__PURE__ */ jsxs(
324
+ "div",
325
+ {
326
+ onClick: () => setExpanded(!expanded),
327
+ style: {
328
+ background: T.surface,
329
+ border: `1px solid ${T.border}`,
330
+ borderLeft: `3px solid ${cfg.color}`,
331
+ borderRadius: 6,
332
+ padding: "14px 16px",
333
+ cursor: "pointer",
334
+ transition: "all 0.2s ease",
335
+ width: "100%",
336
+ boxSizing: "border-box"
337
+ },
338
+ children: [
339
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 10 }, children: [
340
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
341
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }, children: [
342
+ /* @__PURE__ */ jsx(SeverityBadge, { level: severity, size: "sm" }),
343
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: T.cyan, fontFamily: T.mono, fontWeight: 600 }, children: id })
344
+ ] }),
345
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 13, color: T.text, fontWeight: 600, fontFamily: T.font, lineHeight: 1.4 }, children: title })
346
+ ] }),
347
+ /* @__PURE__ */ jsx("span", { style: { color: T.textMuted, fontSize: 10, transform: expanded ? "rotate(180deg)" : "", transition: "transform 0.2s" }, children: "\u25BC" })
348
+ ] }),
349
+ expanded && /* @__PURE__ */ jsxs("div", { style: { marginTop: 12, paddingTop: 10, borderTop: `1px solid ${T.border}` }, children: [
350
+ /* @__PURE__ */ jsx("p", { style: { fontSize: 12, color: T.textMuted, lineHeight: 1.6, margin: "0 0 10px" }, children: description }),
351
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 14, fontSize: 11 }, children: [
352
+ /* @__PURE__ */ jsxs("div", { children: [
353
+ /* @__PURE__ */ jsx("span", { style: { color: T.textDim }, children: "Package: " }),
354
+ /* @__PURE__ */ jsxs("span", { style: { color: T.text, fontFamily: T.mono }, children: [
355
+ pkg,
356
+ "@",
357
+ version
358
+ ] })
359
+ ] }),
360
+ fixed && /* @__PURE__ */ jsxs("div", { children: [
361
+ /* @__PURE__ */ jsx("span", { style: { color: T.textDim }, children: "Fix: " }),
362
+ /* @__PURE__ */ jsxs("span", { style: { color: T.success, fontFamily: T.mono }, children: [
363
+ "\u2265",
364
+ fixed
365
+ ] })
366
+ ] }),
367
+ /* @__PURE__ */ jsxs("div", { children: [
368
+ /* @__PURE__ */ jsx("span", { style: { color: T.textDim }, children: "Published: " }),
369
+ /* @__PURE__ */ jsx("span", { style: { color: T.textMuted, fontFamily: T.mono }, children: published })
370
+ ] })
371
+ ] })
372
+ ] })
373
+ ]
374
+ }
375
+ );
376
+ }
377
+
378
+ export { AlertBanner, CveTag, DataFlowLine, SEV, ScanProgress, SecurityScore, SeverityBadge, ShieldBadge, StatusIndicator, T, TerminalOutput, ThreatCard };
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@bene-npm/shield-ui",
3
+ "version": "0.1.0",
4
+ "description": "Security-themed React component library for dashboards, scanners, and threat visualization",
5
+ "author": "Benedikt Pankratz <https://github.com/Beneking102>",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Beneking102/bene-ui.git"
10
+ },
11
+ "private": false,
12
+ "main": "./dist/index.js",
13
+ "module": "./dist/index.mjs",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "import": {
18
+ "types": "./dist/index.d.mts",
19
+ "default": "./dist/index.mjs"
20
+ },
21
+ "require": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ }
25
+ }
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "sideEffects": false,
31
+ "scripts": {
32
+ "dev": "next dev",
33
+ "build": "next build",
34
+ "start": "next start",
35
+ "lint": "eslint .",
36
+ "type-check": "tsc --noEmit",
37
+ "build:pkg": "tsup",
38
+ "prepublishOnly": "npm run build:pkg && npm run type-check"
39
+ },
40
+ "peerDependencies": {
41
+ "react": "^18 || ^19",
42
+ "react-dom": "^18 || ^19"
43
+ },
44
+ "dependencies": {
45
+ "next": "^16.1.6",
46
+ "react": "^19.2.4",
47
+ "react-dom": "^19.2.4"
48
+ },
49
+ "devDependencies": {
50
+ "@tailwindcss/postcss": "^4.2.1",
51
+ "@types/node": "^22",
52
+ "@types/react": "^19.2.14",
53
+ "@types/react-dom": "^19.2.3",
54
+ "eslint": "^9.39.3",
55
+ "eslint-config-next": "^16.1.6",
56
+ "postcss": "^8.5.3",
57
+ "tailwindcss": "^4.2.1",
58
+ "tsup": "^8.5.0",
59
+ "typescript": "^5.8.3"
60
+ },
61
+ "engines": {
62
+ "node": ">=18.17.0"
63
+ },
64
+ "publishConfig": {
65
+ "access": "public"
66
+ },
67
+ "keywords": [
68
+ "react",
69
+ "components",
70
+ "security",
71
+ "ui",
72
+ "dashboard",
73
+ "cve",
74
+ "vulnerability",
75
+ "dark-theme"
76
+ ]
77
+ }