kyd-shared-badge 0.3.24 → 0.3.26
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/package.json +1 -1
- package/src/SharedBadgeDisplay.tsx +21 -21
- package/src/components/Reveal.tsx +12 -0
- package/src/components/Skills.tsx +36 -5
package/package.json
CHANGED
|
@@ -160,7 +160,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
160
160
|
</style>
|
|
161
161
|
)}
|
|
162
162
|
{/* Share controls removed; app-level pages render their own actions */}
|
|
163
|
-
<Reveal offsetY={8} durationMs={500}>
|
|
163
|
+
<Reveal headless={isHeadless} offsetY={8} durationMs={500}>
|
|
164
164
|
<ReportHeader
|
|
165
165
|
badgeId={badgeId}
|
|
166
166
|
developerName={badgeData.developerName}
|
|
@@ -184,8 +184,8 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
184
184
|
>
|
|
185
185
|
<div className={'space-y-12 divide-y'} style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
186
186
|
<div className="pt-8 first:pt-0 kyd-avoid-break">
|
|
187
|
-
<Reveal as={'h4'} offsetY={8} durationMs={500} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Report Summary</Reveal>
|
|
188
|
-
<Reveal as={'div'} offsetY={8} durationMs={500} className={'space-y-12 divide-y'} style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
187
|
+
<Reveal headless={isHeadless} as={'h4'} offsetY={8} durationMs={500} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Report Summary</Reveal>
|
|
188
|
+
<Reveal headless={isHeadless} as={'div'} offsetY={8} durationMs={500} className={'space-y-12 divide-y'} style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
189
189
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 *:min-h-full kyd-avoid-break">
|
|
190
190
|
{/* Technical semicircle gauge (refactored) */}
|
|
191
191
|
{(() => {
|
|
@@ -251,9 +251,9 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
251
251
|
{/* Technical Scores */}
|
|
252
252
|
<div className="mt-8" >
|
|
253
253
|
<div key={'Technical'} className='pt-8 space-y-8 kyd-avoid-break' style={{ borderColor: 'var(--icon-button-secondary)'}}>
|
|
254
|
-
<Reveal as={'h4'} offsetY={8} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>KYD Technical</Reveal>
|
|
254
|
+
<Reveal headless={isHeadless} as={'h4'} offsetY={8} className={'text-2xl font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>KYD Technical</Reveal>
|
|
255
255
|
{/* technical graph insights */}
|
|
256
|
-
<Reveal>
|
|
256
|
+
<Reveal headless={isHeadless}>
|
|
257
257
|
<div className="">
|
|
258
258
|
<GraphInsights
|
|
259
259
|
graphInsights={graphInsights}
|
|
@@ -269,7 +269,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
269
269
|
<div className="grid grid-cols-1 lg:grid-cols-12 w-full gap-8 items-stretch py-8 border-t" style={{ borderColor: 'var(--icon-button-secondary)'}}>
|
|
270
270
|
|
|
271
271
|
{/* Left: Bars */}
|
|
272
|
-
<Reveal className="lg:col-span-8 h-full">
|
|
272
|
+
<Reveal headless={isHeadless} className="lg:col-span-8 h-full">
|
|
273
273
|
<CategoryBars
|
|
274
274
|
title={'Technical Category Contributions'}
|
|
275
275
|
categories={genreMapping?.['Technical'] as string[]}
|
|
@@ -281,7 +281,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
281
281
|
</Reveal>
|
|
282
282
|
|
|
283
283
|
{/* Right: Contributing Factors (hidden on small screens) */}
|
|
284
|
-
<Reveal className="lg:col-span-4 w-full ml-0 lg:ml-20 hidden lg:flex flex-col items-start justify-start" delayMs={80}>
|
|
284
|
+
<Reveal headless={isHeadless} className="lg:col-span-4 w-full ml-0 lg:ml-20 hidden lg:flex flex-col items-start justify-start" delayMs={80}>
|
|
285
285
|
<div className={'text-sm font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Top Contributing Factors</div>
|
|
286
286
|
<div className="space-y-4">
|
|
287
287
|
{(genreMapping?.['Technical'] || []).map((cat: string) => {
|
|
@@ -313,7 +313,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
313
313
|
</Reveal>
|
|
314
314
|
</div>
|
|
315
315
|
|
|
316
|
-
<Reveal>
|
|
316
|
+
<Reveal headless={isHeadless}>
|
|
317
317
|
<div className="pt-8 border-t kyd-avoid-break" style={{ borderColor: 'var(--icon-button-secondary)'}}>
|
|
318
318
|
<h3 className={'text-xl font-bold mb-3 kyd-keep-with-next'} style={{ color: 'var(--text-main)' }}>KYD Technical - Skills Insights</h3>
|
|
319
319
|
<div className={'prose prose-sm max-w-none mb-6 space-y-4'} style={{ color: 'var(--text-secondary)' }}>
|
|
@@ -328,10 +328,10 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
328
328
|
|
|
329
329
|
|
|
330
330
|
<div className="pt-8 space-y-8">
|
|
331
|
-
<Reveal as={'h3'} offsetY={8} className={`text-2xl font-bold ${isHeadless ? 'kyd-break-before' : ''}`} style={{ color: 'var(--text-main)' }}>KYD Risk - Overview</Reveal>
|
|
331
|
+
<Reveal headless={isHeadless} as={'h3'} offsetY={8} className={`text-2xl font-bold ${isHeadless ? 'kyd-break-before' : ''}`} style={{ color: 'var(--text-main)' }}>KYD Risk - Overview</Reveal>
|
|
332
332
|
|
|
333
333
|
{/* Risk Graph Insights and Category Bars */}
|
|
334
|
-
<Reveal>
|
|
334
|
+
<Reveal headless={isHeadless}>
|
|
335
335
|
<div className="">
|
|
336
336
|
<GraphInsights
|
|
337
337
|
graphInsights={graphInsights}
|
|
@@ -344,7 +344,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
344
344
|
</Reveal>
|
|
345
345
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 w-full items-stretch py-8 border-y kyd-avoid-break" style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
346
346
|
{/* Left: Bars */}
|
|
347
|
-
<Reveal className="lg:col-span-8 h-full">
|
|
347
|
+
<Reveal headless={isHeadless} className="lg:col-span-8 h-full">
|
|
348
348
|
<CategoryBars
|
|
349
349
|
title={'KYD Risk - Category Insights'}
|
|
350
350
|
categories={genreMapping?.['Risk'] as string[]}
|
|
@@ -355,7 +355,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
355
355
|
/>
|
|
356
356
|
</Reveal>
|
|
357
357
|
{/* Right: Contributing Factors (hidden on small screens) */}
|
|
358
|
-
<Reveal className="lg:col-span-4 w-full ml-0 lg:ml-20 hidden lg:flex flex-col items-start justify-start" delayMs={80}>
|
|
358
|
+
<Reveal headless={isHeadless} className="lg:col-span-4 w-full ml-0 lg:ml-20 hidden lg:flex flex-col items-start justify-start" delayMs={80}>
|
|
359
359
|
<div className={'text-sm font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Top Contributing Factors</div>
|
|
360
360
|
<div className="space-y-4">
|
|
361
361
|
{genreMapping?.['Risk']?.map((cat: string) => {
|
|
@@ -389,7 +389,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
389
389
|
|
|
390
390
|
{/* cyber risk display */}
|
|
391
391
|
{badgeData.optOutScreening ? (
|
|
392
|
-
<Reveal className={'p-4 rounded-lg border'} style={{ backgroundColor: 'var(--icon-button-secondary)', borderColor: 'var(--icon-button-secondary)' }}>
|
|
392
|
+
<Reveal headless={isHeadless} className={'p-4 rounded-lg border'} style={{ backgroundColor: 'var(--icon-button-secondary)', borderColor: 'var(--icon-button-secondary)' }}>
|
|
393
393
|
<div className="flex items-start">
|
|
394
394
|
<span className="h-5 w-5 mr-3 mt-0.5 flex-shrink-0" style={{ color: yellow }}>
|
|
395
395
|
<FiAlertTriangle size={20} />
|
|
@@ -411,7 +411,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
411
411
|
const fbiMatches = ss?.fbi_matches && (ss.fbi_matches.length > 0);
|
|
412
412
|
if (!(ofacMatches || cslDetails || fbiMatches)) return null;
|
|
413
413
|
return (
|
|
414
|
-
<Reveal className={'mb-8 rounded-lg border p-4'} style={{ borderColor: 'var(--icon-button-secondary)', backgroundColor: 'var(--content-card-background)' }}>
|
|
414
|
+
<Reveal headless={isHeadless} className={'mb-8 rounded-lg border p-4'} style={{ borderColor: 'var(--icon-button-secondary)', backgroundColor: 'var(--content-card-background)' }}>
|
|
415
415
|
<h4 className={'text-lg font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>3A. Sanctions Matches</h4>
|
|
416
416
|
{/* OFAC matches */}
|
|
417
417
|
{ofacMatches && (
|
|
@@ -474,7 +474,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
474
474
|
</Reveal>
|
|
475
475
|
);
|
|
476
476
|
})()}
|
|
477
|
-
<Reveal>
|
|
477
|
+
<Reveal headless={isHeadless}>
|
|
478
478
|
<IpRiskAnalysisDisplay ipRiskAnalysis={screening_sources?.ip_risk_analysis} />
|
|
479
479
|
</Reveal>
|
|
480
480
|
</>
|
|
@@ -483,7 +483,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
483
483
|
</div>
|
|
484
484
|
|
|
485
485
|
{/* Connected Platforms */}
|
|
486
|
-
<Reveal>
|
|
486
|
+
<Reveal headless={isHeadless}>
|
|
487
487
|
<ConnectedPlatforms accounts={connected} />
|
|
488
488
|
</Reveal>
|
|
489
489
|
|
|
@@ -493,7 +493,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
493
493
|
<div className="space-y-8">
|
|
494
494
|
|
|
495
495
|
{/* Skills */}
|
|
496
|
-
<Reveal>
|
|
496
|
+
<Reveal headless={isHeadless}>
|
|
497
497
|
<div>
|
|
498
498
|
<h4 id="appendix-skills" className={'text-lg font-bold mb-1'} style={{ color: 'var(--text-main)' }}>Skills</h4>
|
|
499
499
|
<div className="text-sm mb-4" style={{ color: 'var(--text-secondary)' }}>
|
|
@@ -505,7 +505,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
505
505
|
|
|
506
506
|
{/* Observations */}
|
|
507
507
|
{Array.isArray(graphInsights?.business_rules_all) && graphInsights.business_rules_all.length > 0 && (
|
|
508
|
-
<Reveal>
|
|
508
|
+
<Reveal headless={isHeadless}>
|
|
509
509
|
<div>
|
|
510
510
|
<h4 className={'text-lg font-bold mb-4'} style={{ color: 'var(--text-main)' }}>Observations</h4>
|
|
511
511
|
<AppendixTables
|
|
@@ -523,7 +523,7 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
523
523
|
|
|
524
524
|
{/* Sanctions & Watchlists */}
|
|
525
525
|
{!badgeData.optOutScreening && screening_sources && (
|
|
526
|
-
<Reveal>
|
|
526
|
+
<Reveal headless={isHeadless}>
|
|
527
527
|
<div>
|
|
528
528
|
<h4 className={'text-lg font-bold mb-4'} style={{ color: 'var(--text-main)' }}>Sanctions & Watchlists</h4>
|
|
529
529
|
{(() => {
|
|
@@ -554,14 +554,14 @@ const SharedBadgeDisplay = ({ badgeData, chatProps, headless }: { badgeData: Pub
|
|
|
554
554
|
</div>
|
|
555
555
|
</div>
|
|
556
556
|
|
|
557
|
-
<Reveal>
|
|
557
|
+
<Reveal headless={isHeadless}>
|
|
558
558
|
<div className={'pt-8 text-sm text-center'} style={{ color: 'var(--text-secondary)' }}>
|
|
559
559
|
Report Completed: {formatLocalDateTime(updatedAt)}
|
|
560
560
|
</div>
|
|
561
561
|
</Reveal>
|
|
562
562
|
</div>
|
|
563
563
|
</div>
|
|
564
|
-
<Reveal>
|
|
564
|
+
<Reveal headless={isHeadless}>
|
|
565
565
|
<footer className={'mt-12 pt-6 border-t'} style={{ borderColor: 'var(--icon-button-secondary)' }}>
|
|
566
566
|
<p className={'text-center text-xs max-w-4xl mx-auto'} style={{ color: 'var(--text-secondary)' }}>
|
|
567
567
|
© 2025 Know Your Developer, LLC. All rights reserved. KYD Self-Check™, and associated marks are trademarks of Know Your Developer, LLC. This document is confidential, proprietary, and intended solely for the individual or entity to whom it is addressed. Unauthorized use, disclosure, copying, or distribution of this document or any of its contents is strictly prohibited and may be unlawful. Know Your Developer, LLC assumes no responsibility or liability for any errors or omissions contained herein. Report validity subject to the terms and conditions stated on the official Know Your Developer website located at https://knowyourdeveloper.ai.
|
|
@@ -21,6 +21,8 @@ type RevealProps = PropsWithChildren<{
|
|
|
21
21
|
threshold?: number | number[];
|
|
22
22
|
/** Root margin for preloading */
|
|
23
23
|
rootMargin?: string;
|
|
24
|
+
/** When true, disable animations and render normally */
|
|
25
|
+
headless?: boolean;
|
|
24
26
|
}>;
|
|
25
27
|
|
|
26
28
|
const Reveal: React.FC<RevealProps> = ({
|
|
@@ -33,11 +35,21 @@ const Reveal: React.FC<RevealProps> = ({
|
|
|
33
35
|
style,
|
|
34
36
|
threshold,
|
|
35
37
|
rootMargin,
|
|
38
|
+
headless,
|
|
36
39
|
children,
|
|
37
40
|
}) => {
|
|
38
41
|
const Tag = as as any;
|
|
39
42
|
const { ref, hasIntersected } = useInViewOnce({ threshold, rootMargin });
|
|
40
43
|
|
|
44
|
+
// In headless mode, render without any animation or transition.
|
|
45
|
+
if (headless) {
|
|
46
|
+
return (
|
|
47
|
+
<Tag ref={ref} className={className} style={style}>
|
|
48
|
+
{children}
|
|
49
|
+
</Tag>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
41
53
|
// Respect reduced motion is handled in the hook by triggering immediately.
|
|
42
54
|
const baseTransform = `translate(${offsetX}px, ${offsetY}px)`;
|
|
43
55
|
const eased = 'cubic-bezier(0.22, 1, 0.36, 1)'; // subtle spring-ish ease
|
|
@@ -197,6 +197,24 @@ const Skills = ({ skillsCategoryRadar, headless }: { skillsMatrix?: SkillsMatrix
|
|
|
197
197
|
return () => window.removeEventListener('resize', measure);
|
|
198
198
|
}, []);
|
|
199
199
|
|
|
200
|
+
// Track container width changes more reliably (including print/headless layouts)
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
const el = containerRef.current;
|
|
203
|
+
if (!el || typeof ResizeObserver === 'undefined') return;
|
|
204
|
+
try {
|
|
205
|
+
const ro = new ResizeObserver((entries) => {
|
|
206
|
+
for (const entry of entries) {
|
|
207
|
+
const width = entry.contentRect?.width || el.clientWidth || 0;
|
|
208
|
+
setContainerWidth(width);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
ro.observe(el);
|
|
212
|
+
return () => ro.disconnect();
|
|
213
|
+
} catch {
|
|
214
|
+
// no-op: older engines
|
|
215
|
+
}
|
|
216
|
+
}, []);
|
|
217
|
+
|
|
200
218
|
// Nudge layout for headless/print contexts so responsive containers measure correctly
|
|
201
219
|
useEffect(() => {
|
|
202
220
|
if (typeof window !== 'undefined') {
|
|
@@ -234,16 +252,29 @@ const Skills = ({ skillsCategoryRadar, headless }: { skillsMatrix?: SkillsMatrix
|
|
|
234
252
|
<div className={'rounded-lg p-4 border kyd-avoid-break'} style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)', breakInside: 'avoid', pageBreakInside: 'avoid' as unknown as undefined }}>
|
|
235
253
|
<h4 className={'font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Skills Footprint</h4>
|
|
236
254
|
<p className={'text-sm mb-4'} style={{ color: 'var(--text-secondary)' }}>The bubble chart visualizes individual skills, where bubble size reflects the weight of supporting evidence and placement indicates relative strength across the skill set.</p>
|
|
237
|
-
<div
|
|
255
|
+
<div
|
|
256
|
+
ref={containerRef}
|
|
257
|
+
className={'kyd-avoid-break'}
|
|
258
|
+
style={{
|
|
259
|
+
width: '100%',
|
|
260
|
+
height: 340,
|
|
261
|
+
position: 'relative',
|
|
262
|
+
overflow: 'visible',
|
|
263
|
+
breakInside: 'avoid',
|
|
264
|
+
pageBreakInside: 'avoid' as unknown as undefined,
|
|
265
|
+
}}
|
|
266
|
+
>
|
|
238
267
|
{(() => {
|
|
239
|
-
const
|
|
268
|
+
const measured = containerWidth || (containerRef.current?.clientWidth || 0);
|
|
269
|
+
const parentWidth = containerRef.current?.parentElement?.clientWidth || measured;
|
|
270
|
+
const width = Math.max(0, Math.min(measured, parentWidth));
|
|
240
271
|
const height = 300;
|
|
241
272
|
const innerWidth = Math.max(0, width);
|
|
242
273
|
const innerHeight = Math.max(0, height);
|
|
243
|
-
const packed = packBubbles(combinedBubbleData, innerWidth, innerHeight, 6);
|
|
274
|
+
const packed = packBubbles(combinedBubbleData, innerWidth, innerHeight, headless ? 10 : 6);
|
|
244
275
|
return (
|
|
245
|
-
<svg width={
|
|
246
|
-
<g transform={
|
|
276
|
+
<svg width={'100%'} height={height} viewBox={`0 0 ${width} ${height}`} preserveAspectRatio="xMidYMid meet">
|
|
277
|
+
<g transform={headless ? 'translate(0, 0)' : 'translate(-25, -20)'}>
|
|
247
278
|
{packed.map((b, idx) => {
|
|
248
279
|
const clipId = `bubble-clip-${idx}`;
|
|
249
280
|
const canShowText = b.r >= 14;
|