@aiready/components 0.1.30 → 0.1.32
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/README.md +2 -2
- package/dist/charts/ForceDirectedGraph.js +49 -13
- package/dist/charts/ForceDirectedGraph.js.map +1 -1
- package/dist/components/badge.js.map +1 -1
- package/dist/components/button.js.map +1 -1
- package/dist/components/card.js.map +1 -1
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/container.js.map +1 -1
- package/dist/components/grid.js.map +1 -1
- package/dist/components/input.d.ts +1 -2
- package/dist/components/input.js.map +1 -1
- package/dist/components/label.js +1 -8
- package/dist/components/label.js.map +1 -1
- package/dist/components/radio-group.js.map +1 -1
- package/dist/components/select.js.map +1 -1
- package/dist/components/separator.js.map +1 -1
- package/dist/components/stack.js.map +1 -1
- package/dist/components/switch.js +29 -22
- package/dist/components/switch.js.map +1 -1
- package/dist/components/textarea.d.ts +1 -2
- package/dist/components/textarea.js.map +1 -1
- package/dist/hooks/useD3.js.map +1 -1
- package/dist/hooks/useDebounce.js.map +1 -1
- package/dist/hooks/useForceSimulation.d.ts +1 -0
- package/dist/hooks/useForceSimulation.js +37 -14
- package/dist/hooks/useForceSimulation.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +337 -141
- package/dist/index.js.map +1 -1
- package/dist/utils/cn.js.map +1 -1
- package/dist/utils/colors.js.map +1 -1
- package/dist/utils/formatters.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/smoke.test.js +1 -1
- package/src/__tests__/smoke.test.ts +3 -3
- package/src/charts/ForceDirectedGraph.tsx +583 -517
- package/src/charts/GraphControls.tsx +5 -2
- package/src/charts/LinkItem.tsx +17 -5
- package/src/charts/NodeItem.tsx +17 -2
- package/src/code-block/CodeBlock.tsx +53 -16
- package/src/code-block/index.ts +1 -1
- package/src/components/badge.tsx +3 -2
- package/src/components/button.tsx +3 -2
- package/src/components/card.tsx +8 -1
- package/src/components/checkbox.tsx +6 -4
- package/src/components/container.tsx +1 -1
- package/src/components/grid.tsx +1 -1
- package/src/components/input.tsx +2 -3
- package/src/components/label.tsx +4 -7
- package/src/components/radio-group.tsx +5 -3
- package/src/components/select.tsx +5 -3
- package/src/components/separator.tsx +1 -1
- package/src/components/stack.tsx +1 -1
- package/src/components/switch.tsx +15 -7
- package/src/components/textarea.tsx +2 -3
- package/src/data-display/ScoreBar.tsx +52 -15
- package/src/data-display/index.ts +7 -1
- package/src/feedback/ErrorDisplay.tsx +17 -4
- package/src/feedback/LoadingSpinner.tsx +8 -3
- package/src/feedback/index.ts +12 -2
- package/src/hooks/useD3.ts +1 -3
- package/src/hooks/useDebounce.ts +1 -1
- package/src/hooks/useForceSimulation.ts +142 -44
- package/src/index.ts +29 -9
- package/src/navigation/Breadcrumb.tsx +17 -8
- package/src/navigation/index.ts +5 -1
- package/src/theme/ThemeProvider.tsx +11 -3
- package/src/theme/index.ts +6 -1
- package/src/utils/cn.ts +1 -1
- package/src/utils/colors.ts +1 -1
- package/src/utils/formatters.ts +1 -1
- package/src/utils/score.ts +3 -1
- package/tailwind.config.js +1 -1
package/src/feedback/index.ts
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
1
|
+
export {
|
|
2
|
+
LoadingSpinner,
|
|
3
|
+
LoadingOverlay,
|
|
4
|
+
type LoadingSpinnerProps,
|
|
5
|
+
type LoadingOverlayProps,
|
|
6
|
+
} from './LoadingSpinner';
|
|
7
|
+
export {
|
|
8
|
+
ErrorDisplay,
|
|
9
|
+
EmptyState,
|
|
10
|
+
type ErrorDisplayProps,
|
|
11
|
+
type EmptyStateProps,
|
|
12
|
+
} from './ErrorDisplay';
|
package/src/hooks/useD3.ts
CHANGED
|
@@ -59,7 +59,6 @@ export function useD3<T extends SVGSVGElement | HTMLDivElement>(
|
|
|
59
59
|
const selection = d3.select(ref.current);
|
|
60
60
|
renderFn(selection);
|
|
61
61
|
}
|
|
62
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
63
62
|
}, dependencies);
|
|
64
63
|
|
|
65
64
|
return ref;
|
|
@@ -118,8 +117,7 @@ export function useD3WithResize<T extends SVGSVGElement | HTMLDivElement>(
|
|
|
118
117
|
return () => {
|
|
119
118
|
resizeObserver.disconnect();
|
|
120
119
|
};
|
|
121
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
122
120
|
}, dependencies);
|
|
123
121
|
|
|
124
122
|
return ref;
|
|
125
|
-
}
|
|
123
|
+
}
|
package/src/hooks/useDebounce.ts
CHANGED
|
@@ -95,6 +95,13 @@ export interface ForceSimulationOptions {
|
|
|
95
95
|
*/
|
|
96
96
|
stabilizeOnStop?: boolean;
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Throttle for tick updates in milliseconds to reduce update frequency
|
|
100
|
+
* (helps avoid excessive React re-renders).
|
|
101
|
+
* @default 33
|
|
102
|
+
*/
|
|
103
|
+
tickThrottleMs?: number;
|
|
104
|
+
|
|
98
105
|
/**
|
|
99
106
|
* Maximum time (ms) to allow the simulation to run after creation/restart.
|
|
100
107
|
* If the simulation hasn't cooled by this time, it will be force-stopped
|
|
@@ -112,7 +119,11 @@ export interface ForceSimulationOptions {
|
|
|
112
119
|
/**
|
|
113
120
|
* Optional tick callback invoked on each simulation tick with current nodes/links and the simulation instance
|
|
114
121
|
*/
|
|
115
|
-
onTick?: (
|
|
122
|
+
onTick?: (
|
|
123
|
+
nodes: SimulationNode[],
|
|
124
|
+
links: SimulationLink[],
|
|
125
|
+
sim: d3.Simulation<SimulationNode, SimulationLink>
|
|
126
|
+
) => void;
|
|
116
127
|
}
|
|
117
128
|
|
|
118
129
|
export interface UseForceSimulationReturn {
|
|
@@ -226,14 +237,11 @@ export function useForceSimulation(
|
|
|
226
237
|
alphaTarget = 0,
|
|
227
238
|
warmAlpha = 0.3,
|
|
228
239
|
alphaMin = 0.01,
|
|
229
|
-
// @ts-ignore allow extra option
|
|
230
|
-
stabilizeOnStop = true,
|
|
231
240
|
onTick,
|
|
232
241
|
// Optional throttle in milliseconds for tick updates (reduce React re-renders)
|
|
233
242
|
// Lower values = smoother but more CPU; default ~30ms (~33fps)
|
|
234
|
-
|
|
243
|
+
stabilizeOnStop = true,
|
|
235
244
|
tickThrottleMs = 33,
|
|
236
|
-
// @ts-ignore allow extra option
|
|
237
245
|
maxSimulationTimeMs = 3000,
|
|
238
246
|
} = options;
|
|
239
247
|
|
|
@@ -242,18 +250,23 @@ export function useForceSimulation(
|
|
|
242
250
|
const [isRunning, setIsRunning] = useState(false);
|
|
243
251
|
const [alpha, setAlpha] = useState(1);
|
|
244
252
|
|
|
245
|
-
const simulationRef = useRef<d3.Simulation<
|
|
253
|
+
const simulationRef = useRef<d3.Simulation<
|
|
254
|
+
SimulationNode,
|
|
255
|
+
SimulationLink
|
|
256
|
+
> | null>(null);
|
|
246
257
|
const stopTimeoutRef = useRef<number | null>(null);
|
|
247
258
|
|
|
248
259
|
// Create lightweight keys for nodes/links so we only recreate the simulation
|
|
249
260
|
// when the actual identity/content of inputs change (not when parent passes
|
|
250
261
|
// new array references on each render).
|
|
251
262
|
const nodesKey = initialNodes.map((n) => n.id).join('|');
|
|
252
|
-
const linksKey = (initialLinks || [])
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
263
|
+
const linksKey = (initialLinks || [])
|
|
264
|
+
.map((l) => {
|
|
265
|
+
const s = typeof l.source === 'string' ? l.source : (l.source as any)?.id;
|
|
266
|
+
const t = typeof l.target === 'string' ? l.target : (l.target as any)?.id;
|
|
267
|
+
return `${s}->${t}:${(l as any).type || ''}`;
|
|
268
|
+
})
|
|
269
|
+
.join('|');
|
|
257
270
|
|
|
258
271
|
useEffect(() => {
|
|
259
272
|
// Create a copy of nodes and links to avoid mutating the original data
|
|
@@ -277,6 +290,7 @@ export function useForceSimulation(
|
|
|
277
290
|
(n as any).vy = (Math.random() - 0.5) * 2;
|
|
278
291
|
});
|
|
279
292
|
} catch (e) {
|
|
293
|
+
void e;
|
|
280
294
|
// If error, fall back to random positions
|
|
281
295
|
nodesCopy.forEach((n) => {
|
|
282
296
|
n.x = Math.random() * width;
|
|
@@ -287,35 +301,75 @@ export function useForceSimulation(
|
|
|
287
301
|
}
|
|
288
302
|
|
|
289
303
|
// Create the simulation
|
|
290
|
-
const simulation =
|
|
304
|
+
const simulation = d3.forceSimulation(
|
|
305
|
+
nodesCopy as any
|
|
306
|
+
) as unknown as d3.Simulation<SimulationNode, SimulationLink>;
|
|
291
307
|
|
|
292
308
|
// Configure link force separately to avoid using generic type args on d3 helpers
|
|
293
309
|
try {
|
|
294
|
-
const linkForce =
|
|
295
|
-
|
|
310
|
+
const linkForce = d3.forceLink(
|
|
311
|
+
linksCopy as any
|
|
312
|
+
) as unknown as d3.ForceLink<SimulationNode, SimulationLink>;
|
|
313
|
+
linkForce
|
|
314
|
+
.id((d: any) => d.id)
|
|
315
|
+
.distance((d: any) =>
|
|
316
|
+
d && d.distance != null ? d.distance : linkDistance
|
|
317
|
+
)
|
|
318
|
+
.strength(linkStrength);
|
|
296
319
|
simulation.force('link', linkForce as any);
|
|
297
320
|
} catch (e) {
|
|
321
|
+
void e;
|
|
298
322
|
// fallback: attach a plain link force
|
|
299
|
-
try {
|
|
323
|
+
try {
|
|
324
|
+
simulation.force('link', d3.forceLink(linksCopy as any) as any);
|
|
325
|
+
} catch (e) {
|
|
326
|
+
void e;
|
|
327
|
+
}
|
|
300
328
|
}
|
|
301
|
-
;
|
|
302
|
-
|
|
303
329
|
try {
|
|
304
|
-
simulation.force(
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
330
|
+
simulation.force(
|
|
331
|
+
'charge',
|
|
332
|
+
d3.forceManyBody().strength(chargeStrength) as any
|
|
333
|
+
);
|
|
334
|
+
simulation.force(
|
|
335
|
+
'center',
|
|
336
|
+
d3.forceCenter(width / 2, height / 2).strength(centerStrength) as any
|
|
337
|
+
);
|
|
338
|
+
const collide = d3
|
|
339
|
+
.forceCollide()
|
|
340
|
+
.radius((d: any) => {
|
|
341
|
+
const nodeSize = d && d.size ? d.size : 10;
|
|
342
|
+
return nodeSize + collisionRadius;
|
|
343
|
+
})
|
|
344
|
+
.strength(collisionStrength as any) as any;
|
|
310
345
|
simulation.force('collision', collide);
|
|
311
|
-
simulation.force(
|
|
312
|
-
|
|
346
|
+
simulation.force(
|
|
347
|
+
'x',
|
|
348
|
+
d3
|
|
349
|
+
.forceX(width / 2)
|
|
350
|
+
.strength(Math.max(0.02, centerStrength * 0.5)) as any
|
|
351
|
+
);
|
|
352
|
+
simulation.force(
|
|
353
|
+
'y',
|
|
354
|
+
d3
|
|
355
|
+
.forceY(height / 2)
|
|
356
|
+
.strength(Math.max(0.02, centerStrength * 0.5)) as any
|
|
357
|
+
);
|
|
313
358
|
simulation.alphaDecay(alphaDecay);
|
|
314
359
|
simulation.velocityDecay(velocityDecay);
|
|
315
360
|
simulation.alphaMin(alphaMin);
|
|
316
|
-
try {
|
|
317
|
-
|
|
361
|
+
try {
|
|
362
|
+
simulation.alphaTarget(alphaTarget);
|
|
363
|
+
} catch (e) {
|
|
364
|
+
void e;
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
simulation.alpha(warmAlpha);
|
|
368
|
+
} catch (e) {
|
|
369
|
+
void e;
|
|
370
|
+
}
|
|
318
371
|
} catch (e) {
|
|
372
|
+
void e;
|
|
319
373
|
// ignore force configuration errors
|
|
320
374
|
}
|
|
321
375
|
|
|
@@ -323,7 +377,11 @@ export function useForceSimulation(
|
|
|
323
377
|
|
|
324
378
|
// Force-stop timeout to ensure simulation doesn't run forever.
|
|
325
379
|
if (stopTimeoutRef.current != null) {
|
|
326
|
-
try {
|
|
380
|
+
try {
|
|
381
|
+
(globalThis.clearTimeout as any)(stopTimeoutRef.current);
|
|
382
|
+
} catch (e) {
|
|
383
|
+
void e;
|
|
384
|
+
}
|
|
327
385
|
stopTimeoutRef.current = null;
|
|
328
386
|
}
|
|
329
387
|
if (maxSimulationTimeMs && maxSimulationTimeMs > 0) {
|
|
@@ -339,7 +397,9 @@ export function useForceSimulation(
|
|
|
339
397
|
}
|
|
340
398
|
simulation.alpha(0);
|
|
341
399
|
simulation.stop();
|
|
342
|
-
} catch (e) {
|
|
400
|
+
} catch (e) {
|
|
401
|
+
void e;
|
|
402
|
+
}
|
|
343
403
|
setIsRunning(false);
|
|
344
404
|
setNodes([...nodesCopy]);
|
|
345
405
|
setLinks([...linksCopy]);
|
|
@@ -352,9 +412,10 @@ export function useForceSimulation(
|
|
|
352
412
|
let lastUpdate = 0;
|
|
353
413
|
const tickHandler = () => {
|
|
354
414
|
try {
|
|
355
|
-
if (typeof onTick === 'function')
|
|
415
|
+
if (typeof onTick === 'function')
|
|
416
|
+
onTick(nodesCopy, linksCopy, simulation);
|
|
356
417
|
} catch (e) {
|
|
357
|
-
|
|
418
|
+
void e;
|
|
358
419
|
}
|
|
359
420
|
|
|
360
421
|
// If simulation alpha has cooled below the configured minimum, stop it to
|
|
@@ -371,7 +432,9 @@ export function useForceSimulation(
|
|
|
371
432
|
});
|
|
372
433
|
}
|
|
373
434
|
simulation.stop();
|
|
374
|
-
} catch (e) {
|
|
435
|
+
} catch (e) {
|
|
436
|
+
void e;
|
|
437
|
+
}
|
|
375
438
|
setAlpha(simulation.alpha());
|
|
376
439
|
setIsRunning(false);
|
|
377
440
|
setNodes([...nodesCopy]);
|
|
@@ -379,13 +442,16 @@ export function useForceSimulation(
|
|
|
379
442
|
return;
|
|
380
443
|
}
|
|
381
444
|
} catch (e) {
|
|
382
|
-
|
|
445
|
+
void e;
|
|
383
446
|
}
|
|
384
447
|
|
|
385
448
|
const now = Date.now();
|
|
386
449
|
const shouldUpdate = now - lastUpdate >= (tickThrottleMs as number);
|
|
387
450
|
if (rafId == null && shouldUpdate) {
|
|
388
|
-
rafId = (
|
|
451
|
+
rafId = (
|
|
452
|
+
globalThis.requestAnimationFrame ||
|
|
453
|
+
((cb: FrameRequestCallback) => setTimeout(cb, 16))
|
|
454
|
+
)(() => {
|
|
389
455
|
rafId = null;
|
|
390
456
|
lastUpdate = Date.now();
|
|
391
457
|
setNodes([...nodesCopy]);
|
|
@@ -406,13 +472,26 @@ export function useForceSimulation(
|
|
|
406
472
|
return () => {
|
|
407
473
|
try {
|
|
408
474
|
simulation.on('tick', null as any);
|
|
409
|
-
} catch (e) {
|
|
475
|
+
} catch (e) {
|
|
476
|
+
void e;
|
|
477
|
+
}
|
|
410
478
|
if (stopTimeoutRef.current != null) {
|
|
411
|
-
try {
|
|
479
|
+
try {
|
|
480
|
+
(globalThis.clearTimeout as any)(stopTimeoutRef.current);
|
|
481
|
+
} catch (e) {
|
|
482
|
+
void e;
|
|
483
|
+
}
|
|
412
484
|
stopTimeoutRef.current = null;
|
|
413
485
|
}
|
|
414
486
|
if (rafId != null) {
|
|
415
|
-
try {
|
|
487
|
+
try {
|
|
488
|
+
(
|
|
489
|
+
globalThis.cancelAnimationFrame ||
|
|
490
|
+
((id: number) => clearTimeout(id))
|
|
491
|
+
)(rafId);
|
|
492
|
+
} catch (e) {
|
|
493
|
+
void e;
|
|
494
|
+
}
|
|
416
495
|
rafId = null;
|
|
417
496
|
}
|
|
418
497
|
simulation.stop();
|
|
@@ -441,16 +520,29 @@ export function useForceSimulation(
|
|
|
441
520
|
if (simulationRef.current) {
|
|
442
521
|
// Reheat the simulation to a modest alpha target rather than forcing
|
|
443
522
|
// full heat; this matches the Observable pattern and helps stability.
|
|
444
|
-
try {
|
|
523
|
+
try {
|
|
524
|
+
simulationRef.current.alphaTarget(warmAlpha).restart();
|
|
525
|
+
} catch {
|
|
526
|
+
simulationRef.current.restart();
|
|
527
|
+
}
|
|
445
528
|
setIsRunning(true);
|
|
446
529
|
// Reset safety timeout when simulation is manually restarted
|
|
447
530
|
if (stopTimeoutRef.current != null) {
|
|
448
|
-
try {
|
|
531
|
+
try {
|
|
532
|
+
(globalThis.clearTimeout as any)(stopTimeoutRef.current);
|
|
533
|
+
} catch (e) {
|
|
534
|
+
void e;
|
|
535
|
+
}
|
|
449
536
|
stopTimeoutRef.current = null;
|
|
450
537
|
}
|
|
451
538
|
if (maxSimulationTimeMs && maxSimulationTimeMs > 0) {
|
|
452
539
|
stopTimeoutRef.current = (globalThis.setTimeout as any)(() => {
|
|
453
|
-
try {
|
|
540
|
+
try {
|
|
541
|
+
simulationRef.current?.alpha(0);
|
|
542
|
+
simulationRef.current?.stop();
|
|
543
|
+
} catch (e) {
|
|
544
|
+
void e;
|
|
545
|
+
}
|
|
454
546
|
setIsRunning(false);
|
|
455
547
|
}, maxSimulationTimeMs) as unknown as number;
|
|
456
548
|
}
|
|
@@ -464,7 +556,11 @@ export function useForceSimulation(
|
|
|
464
556
|
}
|
|
465
557
|
};
|
|
466
558
|
|
|
467
|
-
const originalForcesRef = useRef({
|
|
559
|
+
const originalForcesRef = useRef({
|
|
560
|
+
charge: chargeStrength,
|
|
561
|
+
link: linkStrength,
|
|
562
|
+
collision: collisionStrength,
|
|
563
|
+
});
|
|
468
564
|
const forcesEnabledRef = useRef(true);
|
|
469
565
|
|
|
470
566
|
const setForcesEnabled = (enabled: boolean) => {
|
|
@@ -486,7 +582,7 @@ export function useForceSimulation(
|
|
|
486
582
|
link.strength(enabled ? originalForcesRef.current.link : 0);
|
|
487
583
|
}
|
|
488
584
|
} catch (e) {
|
|
489
|
-
|
|
585
|
+
void e;
|
|
490
586
|
}
|
|
491
587
|
};
|
|
492
588
|
|
|
@@ -530,7 +626,9 @@ export function useForceSimulation(
|
|
|
530
626
|
* }
|
|
531
627
|
* ```
|
|
532
628
|
*/
|
|
533
|
-
export function useDrag(
|
|
629
|
+
export function useDrag(
|
|
630
|
+
simulation: d3.Simulation<SimulationNode, any> | null | undefined
|
|
631
|
+
) {
|
|
534
632
|
const dragStarted = (event: any, node: SimulationNode) => {
|
|
535
633
|
if (!simulation) return;
|
|
536
634
|
if (!event.active) simulation.alphaTarget(0.3).restart();
|
|
@@ -555,4 +653,4 @@ export function useDrag(simulation: d3.Simulation<SimulationNode, any> | null |
|
|
|
555
653
|
onDrag: dragged,
|
|
556
654
|
onDragEnd: dragEnded,
|
|
557
655
|
};
|
|
558
|
-
}
|
|
656
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -20,19 +20,37 @@ export { Separator, type SeparatorProps } from './components/separator';
|
|
|
20
20
|
|
|
21
21
|
// Form Components
|
|
22
22
|
export { Checkbox, type CheckboxProps } from './components/checkbox';
|
|
23
|
-
export {
|
|
23
|
+
export {
|
|
24
|
+
RadioGroup,
|
|
25
|
+
type RadioGroupProps,
|
|
26
|
+
type RadioOption,
|
|
27
|
+
} from './components/radio-group';
|
|
24
28
|
export { Switch, type SwitchProps } from './components/switch';
|
|
25
29
|
export { Textarea, type TextareaProps } from './components/textarea';
|
|
26
|
-
export {
|
|
30
|
+
export {
|
|
31
|
+
Select,
|
|
32
|
+
type SelectProps,
|
|
33
|
+
type SelectOption,
|
|
34
|
+
} from './components/select';
|
|
27
35
|
|
|
28
36
|
// Code Block
|
|
29
37
|
export { CodeBlock, InlineCode, type CodeBlockProps } from './code-block';
|
|
30
38
|
|
|
31
39
|
// Navigation
|
|
32
|
-
export {
|
|
40
|
+
export {
|
|
41
|
+
Breadcrumb,
|
|
42
|
+
type BreadcrumbProps,
|
|
43
|
+
type BreadcrumbItem,
|
|
44
|
+
} from './navigation';
|
|
33
45
|
|
|
34
46
|
// Data Display
|
|
35
|
-
export {
|
|
47
|
+
export {
|
|
48
|
+
ScoreBar,
|
|
49
|
+
ScoreCard,
|
|
50
|
+
type ScoreBarProps,
|
|
51
|
+
type ScoreCardProps,
|
|
52
|
+
type ScoreRating,
|
|
53
|
+
} from './data-display';
|
|
36
54
|
|
|
37
55
|
// Feedback
|
|
38
56
|
export {
|
|
@@ -47,7 +65,12 @@ export {
|
|
|
47
65
|
} from './feedback';
|
|
48
66
|
|
|
49
67
|
// Theme
|
|
50
|
-
export {
|
|
68
|
+
export {
|
|
69
|
+
ThemeProvider,
|
|
70
|
+
useTheme,
|
|
71
|
+
type Theme,
|
|
72
|
+
type EffectiveTheme,
|
|
73
|
+
} from './theme';
|
|
51
74
|
|
|
52
75
|
// Utils
|
|
53
76
|
export { cn } from './utils/cn';
|
|
@@ -76,7 +99,4 @@ export {
|
|
|
76
99
|
type ForceDirectedGraphHandle,
|
|
77
100
|
type LayoutType,
|
|
78
101
|
} from './charts/ForceDirectedGraph';
|
|
79
|
-
export {
|
|
80
|
-
GraphControls,
|
|
81
|
-
type GraphControlsProps,
|
|
82
|
-
} from './charts/GraphControls';
|
|
102
|
+
export { GraphControls, type GraphControlsProps } from './charts/GraphControls';
|
|
@@ -22,7 +22,11 @@ const DefaultSeparator = () => (
|
|
|
22
22
|
strokeWidth="1.5"
|
|
23
23
|
stroke="currentColor"
|
|
24
24
|
>
|
|
25
|
-
<path
|
|
25
|
+
<path
|
|
26
|
+
strokeLinecap="round"
|
|
27
|
+
strokeLinejoin="round"
|
|
28
|
+
d="M8.25 4.5l7.5 7.5-7.5 7.5"
|
|
29
|
+
/>
|
|
26
30
|
</svg>
|
|
27
31
|
);
|
|
28
32
|
|
|
@@ -30,16 +34,17 @@ export function Breadcrumb({ items, separator, className }: BreadcrumbProps) {
|
|
|
30
34
|
const Separator = separator || <DefaultSeparator />;
|
|
31
35
|
|
|
32
36
|
return (
|
|
33
|
-
<nav
|
|
37
|
+
<nav
|
|
38
|
+
className={cn('flex items-center gap-1 text-sm', className)}
|
|
39
|
+
aria-label="Breadcrumb"
|
|
40
|
+
>
|
|
34
41
|
<ol className="flex items-center gap-1">
|
|
35
42
|
{items.map((item, index) => {
|
|
36
43
|
const isLast = index === items.length - 1;
|
|
37
|
-
|
|
44
|
+
|
|
38
45
|
return (
|
|
39
46
|
<li key={index} className="flex items-center gap-1">
|
|
40
|
-
{index > 0 &&
|
|
41
|
-
<span className="flex-shrink-0">{Separator}</span>
|
|
42
|
-
)}
|
|
47
|
+
{index > 0 && <span className="flex-shrink-0">{Separator}</span>}
|
|
43
48
|
{item.href && !isLast ? (
|
|
44
49
|
<a
|
|
45
50
|
href={item.href}
|
|
@@ -48,7 +53,11 @@ export function Breadcrumb({ items, separator, className }: BreadcrumbProps) {
|
|
|
48
53
|
{item.label}
|
|
49
54
|
</a>
|
|
50
55
|
) : (
|
|
51
|
-
<span
|
|
56
|
+
<span
|
|
57
|
+
className={
|
|
58
|
+
isLast ? 'font-medium text-slate-900' : 'text-slate-600'
|
|
59
|
+
}
|
|
60
|
+
>
|
|
52
61
|
{item.label}
|
|
53
62
|
</span>
|
|
54
63
|
)}
|
|
@@ -58,4 +67,4 @@ export function Breadcrumb({ items, separator, className }: BreadcrumbProps) {
|
|
|
58
67
|
</ol>
|
|
59
68
|
</nav>
|
|
60
69
|
);
|
|
61
|
-
}
|
|
70
|
+
}
|
package/src/navigation/index.ts
CHANGED
|
@@ -17,7 +17,9 @@ const STORAGE_KEY = 'aiready-theme';
|
|
|
17
17
|
|
|
18
18
|
function getSystemTheme(): EffectiveTheme {
|
|
19
19
|
if (typeof window === 'undefined') return 'light';
|
|
20
|
-
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
20
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
21
|
+
? 'dark'
|
|
22
|
+
: 'light';
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
function getStoredTheme(): Theme {
|
|
@@ -102,7 +104,13 @@ export function ThemeProvider({
|
|
|
102
104
|
// Prevent hydration mismatch
|
|
103
105
|
if (!mounted) {
|
|
104
106
|
return (
|
|
105
|
-
<ThemeContext.Provider
|
|
107
|
+
<ThemeContext.Provider
|
|
108
|
+
value={{
|
|
109
|
+
theme: defaultTheme,
|
|
110
|
+
setTheme: () => {},
|
|
111
|
+
effectiveTheme: 'light',
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
106
114
|
{children}
|
|
107
115
|
</ThemeContext.Provider>
|
|
108
116
|
);
|
|
@@ -121,4 +129,4 @@ export function useTheme(): ThemeContextValue {
|
|
|
121
129
|
throw new Error('useTheme must be used within a ThemeProvider');
|
|
122
130
|
}
|
|
123
131
|
return context;
|
|
124
|
-
}
|
|
132
|
+
}
|
package/src/theme/index.ts
CHANGED
package/src/utils/cn.ts
CHANGED
package/src/utils/colors.ts
CHANGED
package/src/utils/formatters.ts
CHANGED
package/src/utils/score.ts
CHANGED
|
@@ -46,7 +46,9 @@ export function scoreGlow(score: number | null | undefined): string {
|
|
|
46
46
|
/**
|
|
47
47
|
* Get rating from score (for use with ScoreBar component)
|
|
48
48
|
*/
|
|
49
|
-
export function getScoreRating(
|
|
49
|
+
export function getScoreRating(
|
|
50
|
+
score: number | null | undefined
|
|
51
|
+
): 'excellent' | 'good' | 'fair' | 'needs-work' | 'critical' {
|
|
50
52
|
if (score == null) return 'critical';
|
|
51
53
|
if (score >= 90) return 'excellent';
|
|
52
54
|
if (score >= 75) return 'good';
|
package/tailwind.config.js
CHANGED