@alphacifer/slidev-addon-theme 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +163 -2
  2. package/components/arc-arrow-process/ArcArrowProcess.vue +361 -0
  3. package/components/arc-arrow-process/ArcArrowProcessArrow.vue +143 -0
  4. package/components/arc-arrow-process/ArcArrowProcessCallout.vue +361 -0
  5. package/components/arc-arrow-process/ArcArrowProcessContent.vue +38 -0
  6. package/components/arc-arrow-process/ArcArrowProcessHeading.vue +43 -0
  7. package/components/arc-arrow-process/ArcArrowProcessIcon.vue +154 -0
  8. package/components/arc-compare/ArcCompare.vue +443 -0
  9. package/components/arc-compare/ArcCompareBadge.vue +63 -0
  10. package/components/arc-compare/ArcCompareCallout.vue +386 -0
  11. package/components/arc-compare/ArcCompareContent.vue +52 -0
  12. package/components/arc-compare/ArcCompareContents.vue +18 -0
  13. package/components/arc-compare/ArcCompareHeading.vue +52 -0
  14. package/components/arc-compare/ArcCompareLeft.vue +183 -0
  15. package/components/arc-compare/ArcCompareRight.vue +183 -0
  16. package/components/arc-compare/ArcCompareTitle.vue +47 -0
  17. package/components/arrow-triad/ArrowTriad.vue +368 -0
  18. package/components/arrow-triad/ArrowTriadCallout.vue +300 -0
  19. package/components/arrow-triad/ArrowTriadContent.vue +38 -0
  20. package/components/arrow-triad/ArrowTriadHeading.vue +38 -0
  21. package/components/arrow-triad/ArrowTriadIcon.vue +96 -0
  22. package/components/chevron-card/ChevronCard.vue +237 -0
  23. package/components/chevron-card/ChevronCardBadge.vue +74 -0
  24. package/components/chevron-card/ChevronCardContent.vue +39 -0
  25. package/components/chevron-card/ChevronCardHeading.vue +39 -0
  26. package/components/comparison-table/ComparisonTable.vue +244 -0
  27. package/components/comparison-table/ComparisonTableCell.vue +88 -0
  28. package/components/comparison-table/ComparisonTableCol.vue +97 -0
  29. package/components/comparison-table/ComparisonTableCols.vue +74 -0
  30. package/components/comparison-table/ComparisonTableRow.vue +92 -0
  31. package/components/comparison-table/ComparisonTableRows.vue +21 -0
  32. package/components/hex-triad/HexTriad.vue +438 -0
  33. package/components/hex-triad/HexTriadBadge.vue +110 -0
  34. package/components/hex-triad/HexTriadCallout.vue +200 -0
  35. package/components/hex-triad/HexTriadCenterBadge.vue +51 -0
  36. package/components/hex-triad/HexTriadContent.vue +20 -0
  37. package/components/hex-triad/HexTriadHeading.vue +28 -0
  38. package/components/hex-triad/HexTriadLayer.vue +28 -0
  39. package/components/horiz-card/HorizCard.vue +318 -0
  40. package/components/horiz-card/HorizCardBadge.vue +75 -0
  41. package/components/horiz-card/HorizCardContent.vue +46 -0
  42. package/components/horiz-card/HorizCardHeading.vue +47 -0
  43. package/components/horiz-card/HorizCardIcon.vue +118 -0
  44. package/components/quad-hub/QuadHub.vue +642 -0
  45. package/components/quad-hub/QuadHubCallout.vue +267 -0
  46. package/components/quad-hub/QuadHubCenter.vue +53 -0
  47. package/components/quad-hub/QuadHubContent.vue +39 -0
  48. package/components/quad-hub/QuadHubHeading.vue +44 -0
  49. package/components/quad-hub/QuadHubIcon.vue +112 -0
  50. package/components/shifting-heading/TransitionHeading.vue +2 -2
  51. package/components/vert-card/VertCard.vue +389 -0
  52. package/components/vert-card/VertCardBadge.vue +72 -0
  53. package/components/vert-card/VertCardContent.vue +45 -0
  54. package/components/vert-card/VertCardHeading.vue +68 -0
  55. package/components/vert-card/VertCardIcon.vue +126 -0
  56. package/components/vert-card/VertCardTitle.vue +76 -0
  57. package/layouts/core/arc-toc.vue +76 -113
  58. package/layouts/core/bg-center.vue +32 -4
  59. package/layouts/shifting-heading/shifting-intro.vue +35 -7
  60. package/package.json +3 -2
  61. package/utils/arcArrowProcess/context.ts +28 -0
  62. package/utils/arcArrowProcess/createArcArrowProcessGeometry.ts +137 -0
  63. package/utils/arcArrowProcess/index.ts +3 -0
  64. package/utils/arcArrowProcess/resolveArcArrowProcessItems.ts +189 -0
  65. package/utils/arcCompare/context.ts +36 -0
  66. package/utils/arcCompare/createArcCompareGeometry.ts +254 -0
  67. package/utils/arcCompare/index.ts +3 -0
  68. package/utils/arcCompare/resolveArcCompareItems.ts +123 -0
  69. package/utils/arrowTriad/context.ts +38 -0
  70. package/utils/arrowTriad/createArrowTriadGeometry.ts +63 -0
  71. package/utils/arrowTriad/index.ts +3 -0
  72. package/utils/arrowTriad/resolveArrowTriadItems.ts +121 -0
  73. package/utils/chevronCard/context.ts +12 -0
  74. package/utils/chevronCard/createChevronCardGeometry.ts +48 -0
  75. package/utils/chevronCard/index.ts +2 -0
  76. package/utils/comparisonTable/constants.ts +72 -0
  77. package/utils/comparisonTable/context.ts +24 -0
  78. package/utils/comparisonTable/index.ts +2 -0
  79. package/utils/hexTriad/context.ts +25 -0
  80. package/utils/hexTriad/createHexTriadGeometry.ts +182 -0
  81. package/utils/hexTriad/index.ts +3 -0
  82. package/utils/hexTriad/resolveHexTriadItems.ts +63 -0
  83. package/utils/horizCard/context.ts +13 -0
  84. package/utils/horizCard/createHorizCardGeometry.ts +243 -0
  85. package/utils/horizCard/index.ts +2 -0
  86. package/utils/quadHub/context.ts +26 -0
  87. package/utils/quadHub/createQuadHubGeometry.ts +331 -0
  88. package/utils/quadHub/index.ts +3 -0
  89. package/utils/quadHub/resolveQuadHubItems.ts +214 -0
  90. package/utils/useDiagramAutoScale.ts +300 -0
  91. package/utils/vertCard/context.ts +18 -0
  92. package/utils/vertCard/createVertCardGeometry.ts +242 -0
  93. package/utils/vertCard/index.ts +2 -0
@@ -0,0 +1,182 @@
1
+ export interface IHexTriadPoint {
2
+ readonly x: number;
3
+ readonly y: number;
4
+ }
5
+
6
+ export interface IHexTriadCoordinates {
7
+ readonly centerX: number;
8
+ readonly centerY: number;
9
+ readonly hexRadius: number;
10
+ readonly triadRadius: number;
11
+ readonly badgeRadius: number;
12
+ readonly hex1: IHexTriadPoint;
13
+ readonly hex2: IHexTriadPoint;
14
+ readonly hex3: IHexTriadPoint;
15
+ readonly badge1: IHexTriadPoint;
16
+ readonly badge2: IHexTriadPoint;
17
+ readonly badge3: IHexTriadPoint;
18
+ }
19
+
20
+ /**
21
+ * Creates pointy-topped hexagon SVG points with sharp vertices
22
+ */
23
+ export function createHexagonPoints(cx: number, cy: number, r: number): string {
24
+ const angles = [-90, -30, 30, 90, 150, 210];
25
+
26
+ return angles
27
+ .map((deg) => {
28
+ const rad = (deg * Math.PI) / 180;
29
+ const x = (cx + r * Math.cos(rad)).toFixed(1);
30
+ const y = (cy + r * Math.sin(rad)).toFixed(1);
31
+
32
+ return `${x},${y}`;
33
+ })
34
+ .join(' ');
35
+ }
36
+
37
+ /**
38
+ * Creates a pointy-topped hexagon SVG path with smoothly rounded corners
39
+ */
40
+ export function createRoundedHexagonPath(
41
+ cx: number,
42
+ cy: number,
43
+ r: number,
44
+ cornerRadius = 16,
45
+ ): string {
46
+ const angles = [-90, -30, 30, 90, 150, 210];
47
+ const vertices = angles.map((deg) => {
48
+ const rad = (deg * Math.PI) / 180;
49
+ return {
50
+ x: cx + r * Math.cos(rad),
51
+ y: cy + r * Math.sin(rad),
52
+ };
53
+ });
54
+
55
+ const d = Math.min(cornerRadius, r * 0.3);
56
+ const commands: string[] = [];
57
+
58
+ for (let i = 0; i < 6; i += 1) {
59
+ const prev = vertices[(i + 5) % 6];
60
+ const curr = vertices[i];
61
+ const next = vertices[(i + 1) % 6];
62
+
63
+ if (!prev || !curr || !next) {
64
+ continue;
65
+ }
66
+
67
+ const lenPrev = Math.hypot(prev.x - curr.x, prev.y - curr.y);
68
+ const lenNext = Math.hypot(next.x - curr.x, next.y - curr.y);
69
+
70
+ const a = {
71
+ x: curr.x + ((prev.x - curr.x) / lenPrev) * d,
72
+ y: curr.y + ((prev.y - curr.y) / lenPrev) * d,
73
+ };
74
+ const b = {
75
+ x: curr.x + ((next.x - curr.x) / lenNext) * d,
76
+ y: curr.y + ((next.y - curr.y) / lenNext) * d,
77
+ };
78
+
79
+ if (i === 0) {
80
+ commands.push(`M ${b.x.toFixed(1)} ${b.y.toFixed(1)}`);
81
+ } else {
82
+ commands.push(
83
+ `L ${a.x.toFixed(1)} ${a.y.toFixed(1)} Q ${curr.x.toFixed(1)} ${curr.y.toFixed(1)} ${b.x.toFixed(1)} ${b.y.toFixed(1)}`,
84
+ );
85
+ }
86
+ }
87
+
88
+ const prev0 = vertices[5];
89
+ const curr0 = vertices[0];
90
+ const next0 = vertices[1];
91
+
92
+ if (prev0 && curr0 && next0) {
93
+ const len0 = Math.hypot(prev0.x - curr0.x, prev0.y - curr0.y);
94
+ const lenNext0 = Math.hypot(next0.x - curr0.x, next0.y - curr0.y);
95
+ const a0 = {
96
+ x: curr0.x + ((prev0.x - curr0.x) / len0) * d,
97
+ y: curr0.y + ((prev0.y - curr0.y) / len0) * d,
98
+ };
99
+ const b0 = {
100
+ x: curr0.x + ((next0.x - curr0.x) / lenNext0) * d,
101
+ y: curr0.y + ((next0.y - curr0.y) / lenNext0) * d,
102
+ };
103
+ commands.push(
104
+ `L ${a0.x.toFixed(1)} ${a0.y.toFixed(1)} Q ${curr0.x.toFixed(1)} ${curr0.y.toFixed(1)} ${b0.x.toFixed(1)} ${b0.y.toFixed(1)} Z`,
105
+ );
106
+ }
107
+
108
+ return commands.join(' ');
109
+ }
110
+
111
+ export function calculateHexTriadCoordinates(
112
+ centerX = 270,
113
+ centerY = 224,
114
+ triadRadius = 66,
115
+ hexRadius = 114,
116
+ badgeRadius = 28,
117
+ horizontalSpread = 28,
118
+ ): IHexTriadCoordinates {
119
+ const angleRad30 = (30 * Math.PI) / 180;
120
+
121
+ const hex2X = Number(
122
+ (centerX + triadRadius * Math.cos(angleRad30) + horizontalSpread).toFixed(
123
+ 2,
124
+ ),
125
+ );
126
+ const hex3X = Number(
127
+ (centerX - triadRadius * Math.cos(angleRad30) - horizontalSpread).toFixed(
128
+ 2,
129
+ ),
130
+ );
131
+ const bottomY = Number(
132
+ (centerY + triadRadius * Math.sin(angleRad30)).toFixed(2),
133
+ );
134
+
135
+ // Exact equilateral height so that all three pairwise overlap areas are equal:
136
+ const spreadDistance = hex2X - hex3X;
137
+ const equilateralHeight = Number(
138
+ (spreadDistance * (Math.sqrt(3) / 2)).toFixed(2),
139
+ );
140
+ const hex1Y = Number((bottomY - equilateralHeight).toFixed(2));
141
+
142
+ const circumCenterY = Number(((hex1Y + 2 * bottomY) / 3).toFixed(2));
143
+ // Set circle radius to hexRadius (114) so it crosses the three yellow points
144
+ const circleRadius = hexRadius;
145
+
146
+ // Badges centered directly ON the white circle:
147
+ const badge1 = {
148
+ x: centerX,
149
+ y: Number((circumCenterY - circleRadius).toFixed(2)),
150
+ };
151
+ const badge2 = {
152
+ x: Number((centerX + circleRadius * Math.cos(angleRad30)).toFixed(2)),
153
+ y: Number((circumCenterY + circleRadius * Math.sin(angleRad30)).toFixed(2)),
154
+ };
155
+ const badge3 = {
156
+ x: Number((centerX - circleRadius * Math.cos(angleRad30)).toFixed(2)),
157
+ y: Number((circumCenterY + circleRadius * Math.sin(angleRad30)).toFixed(2)),
158
+ };
159
+
160
+ return {
161
+ centerX,
162
+ centerY: circumCenterY,
163
+ hexRadius,
164
+ triadRadius: circleRadius,
165
+ badgeRadius,
166
+ hex1: {
167
+ x: centerX,
168
+ y: hex1Y,
169
+ },
170
+ hex2: {
171
+ x: hex2X,
172
+ y: bottomY,
173
+ },
174
+ hex3: {
175
+ x: hex3X,
176
+ y: bottomY,
177
+ },
178
+ badge1,
179
+ badge2,
180
+ badge3,
181
+ };
182
+ }
@@ -0,0 +1,3 @@
1
+ export * from './context';
2
+ export * from './createHexTriadGeometry';
3
+ export * from './resolveHexTriadItems';
@@ -0,0 +1,63 @@
1
+ export interface IHexTriadItem {
2
+ readonly id?: string | number;
3
+ readonly title: string;
4
+ readonly description?: string;
5
+ readonly color?: string;
6
+ readonly depthColor?: string;
7
+ readonly lightColor?: string;
8
+ }
9
+
10
+ export const DEFAULT_HEX_TRIAD_ITEMS: readonly [
11
+ IHexTriadItem,
12
+ IHexTriadItem,
13
+ IHexTriadItem,
14
+ ] = [
15
+ {
16
+ id: '01',
17
+ title: 'Text Here',
18
+ description:
19
+ 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod sed diam nonummy nibh',
20
+ color: '#ee3248',
21
+ depthColor: '#9e1526',
22
+ lightColor: '#ff8595',
23
+ },
24
+ {
25
+ id: '02',
26
+ title: 'Text Here',
27
+ description:
28
+ 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod sed diam nonummy nibh',
29
+ color: '#f26522',
30
+ depthColor: '#a83908',
31
+ lightColor: '#ffb088',
32
+ },
33
+ {
34
+ id: '03',
35
+ title: 'Text Here',
36
+ description:
37
+ 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod sed diam nonummy nibh',
38
+ color: '#2b4594',
39
+ depthColor: '#172554',
40
+ lightColor: '#8da4e2',
41
+ },
42
+ ];
43
+
44
+ export function resolveHexTriadItems(
45
+ userItems?: readonly IHexTriadItem[],
46
+ ): readonly [IHexTriadItem, IHexTriadItem, IHexTriadItem] {
47
+ const items = userItems ?? [];
48
+
49
+ return [
50
+ {
51
+ ...DEFAULT_HEX_TRIAD_ITEMS[0],
52
+ ...(items[0] ?? {}),
53
+ },
54
+ {
55
+ ...DEFAULT_HEX_TRIAD_ITEMS[1],
56
+ ...(items[1] ?? {}),
57
+ },
58
+ {
59
+ ...DEFAULT_HEX_TRIAD_ITEMS[2],
60
+ ...(items[2] ?? {}),
61
+ },
62
+ ] as const;
63
+ }
@@ -0,0 +1,13 @@
1
+ import type { ComputedRef, InjectionKey } from 'vue';
2
+
3
+ export interface IHorizCardContext {
4
+ readonly color: ComputedRef<string>;
5
+ readonly cardBg: ComputedRef<string>;
6
+ readonly lightColor: ComputedRef<string>;
7
+ readonly titleColor: ComputedRef<string>;
8
+ readonly step: ComputedRef<string | number>;
9
+ readonly textColor: ComputedRef<string | undefined>;
10
+ }
11
+
12
+ export const HORIZ_CARD_KEY: InjectionKey<IHorizCardContext> =
13
+ Symbol('HORIZ_CARD');
@@ -0,0 +1,243 @@
1
+ export interface ICreateHorizCardPathParams {
2
+ readonly width: number;
3
+ readonly height: number;
4
+ readonly chamferX?: number;
5
+ readonly chamferY?: number;
6
+ readonly borderRadius?: number;
7
+ readonly chamferFillet?: number;
8
+ }
9
+
10
+ export interface ICreateHorizCardAccentPathParams {
11
+ readonly width: number;
12
+ readonly height: number;
13
+ readonly chamferX?: number;
14
+ readonly chamferY?: number;
15
+ readonly accentEndX?: number;
16
+ readonly accentStartY?: number;
17
+ readonly chamferFillet?: number;
18
+ readonly accentOutsetX?: number;
19
+ readonly accentThickness?: number;
20
+ readonly accentTopFillet?: number;
21
+ readonly accentBottomFillet?: number;
22
+ }
23
+
24
+ export interface ICreateHorizCardBadgePathParams {
25
+ readonly width?: number;
26
+ readonly height?: number;
27
+ readonly topRadius?: number;
28
+ readonly tipRadius?: number;
29
+ readonly sideHeight?: number;
30
+ readonly bottomRadius?: number;
31
+ }
32
+
33
+ export interface IHexToRgbaParams {
34
+ readonly hex: string;
35
+ readonly alpha?: number;
36
+ }
37
+
38
+ /**
39
+ * Generates an SVG path for the main HorizCard body.
40
+ * Features rounded top-left, top-right, and bottom-right corners,
41
+ * and a smooth filleted chamfer at the bottom-left corner matching the design.
42
+ */
43
+ export function createHorizCardPath({
44
+ width,
45
+ height,
46
+ chamferX = Math.min(60, Math.round(width * 0.17)),
47
+ chamferY = Math.min(76, Math.round(height * 0.29)),
48
+ borderRadius = 24,
49
+ chamferFillet = 22,
50
+ }: ICreateHorizCardPathParams): string {
51
+ const safeR = Math.max(
52
+ 0,
53
+ Math.min(borderRadius, width / 2, (height - chamferY) / 2),
54
+ );
55
+
56
+ const diagLen = Math.hypot(chamferX, chamferY);
57
+ const ux = diagLen > 0 ? chamferX / diagLen : 0;
58
+ const uy = diagLen > 0 ? chamferY / diagLen : 0;
59
+
60
+ const p1Y = height - chamferY - chamferFillet;
61
+ const p2X = chamferFillet * ux;
62
+ const p2Y = height - chamferY + chamferFillet * uy;
63
+
64
+ const p3X = chamferX - chamferFillet * ux;
65
+ const p3Y = height - chamferFillet * uy;
66
+ const p4X = chamferX + chamferFillet;
67
+
68
+ return [
69
+ `M ${safeR} 0`,
70
+ `H ${width - safeR}`,
71
+ `A ${safeR} ${safeR} 0 0 1 ${width} ${safeR}`,
72
+ `V ${height - safeR}`,
73
+ `A ${safeR} ${safeR} 0 0 1 ${width - safeR} ${height}`,
74
+ `H ${p4X.toFixed(2)}`,
75
+ `Q ${chamferX.toFixed(2)} ${height.toFixed(2)} ${p3X.toFixed(2)} ${p3Y.toFixed(2)}`,
76
+ `L ${p2X.toFixed(2)} ${p2Y.toFixed(2)}`,
77
+ `Q 0 ${(height - chamferY).toFixed(2)} 0 ${p1Y.toFixed(2)}`,
78
+ `V ${safeR}`,
79
+ `A ${safeR} ${safeR} 0 0 1 ${safeR} 0`,
80
+ 'Z',
81
+ ].join(' ');
82
+ }
83
+
84
+ /**
85
+ * Generates an SVG path for the organic accent shape at the bottom-left of the card.
86
+ * Curves in parallel along with the white card body, featuring 2 distinct rounded curves
87
+ * (a top fillet and a bottom fillet) flanking the diagonal segment, perfectly mirroring
88
+ * the white card's dual filleted transitions.
89
+ */
90
+ export function createHorizCardAccentPath({
91
+ width,
92
+ height,
93
+ chamferX = Math.min(60, Math.round(width * 0.17)),
94
+ chamferY = Math.min(76, Math.round(height * 0.29)),
95
+ accentEndX,
96
+ accentStartY = Math.round(height * 0.28),
97
+ chamferFillet = 22,
98
+ accentOutsetX,
99
+ accentThickness = 16,
100
+ accentTopFillet = 22,
101
+ accentBottomFillet = 22,
102
+ }: ICreateHorizCardAccentPathParams): string {
103
+ const diagLen = Math.hypot(chamferX, chamferY);
104
+ const ux = diagLen > 0 ? chamferX / diagLen : 0;
105
+ const uy = diagLen > 0 ? chamferY / diagLen : 0;
106
+
107
+ const p1Y = height - chamferY - chamferFillet;
108
+ const p2X = chamferFillet * ux;
109
+ const p2Y = height - chamferY + chamferFillet * uy;
110
+
111
+ const p3X = chamferX - chamferFillet * ux;
112
+ const p3Y = height - chamferFillet * uy;
113
+ const p4X = chamferX + chamferFillet;
114
+
115
+ // Normal vector pointing outward/down-left
116
+ const nx = -uy;
117
+ const ny = ux;
118
+
119
+ // Parallel offset thickness
120
+ const o2X = p2X + accentThickness * nx;
121
+ const o2Y = p2Y + accentThickness * ny;
122
+ const o3X = p3X + accentThickness * nx;
123
+ const o3Y = p3Y + accentThickness * ny;
124
+
125
+ // 1. Bottom vertex & curve (matching white bottom fillet)
126
+ const tBottom = (height - o3Y) / uy;
127
+ const vBottomX = o3X + tBottom * ux;
128
+ const vBottomY = height;
129
+ const bStartX = vBottomX - accentBottomFillet * ux;
130
+ const bStartY = vBottomY - accentBottomFillet * uy;
131
+ const bEndX = vBottomX + accentBottomFillet;
132
+
133
+ // 2. Top vertex & curve (matching white top fillet)
134
+ const outsetX = accentOutsetX ?? accentThickness;
135
+ const vTopX = -outsetX;
136
+ const tTop = (vTopX - o2X) / ux;
137
+ const vTopY = o2Y + tTop * uy;
138
+ const tStartX = vTopX;
139
+ const tStartY = vTopY - accentTopFillet;
140
+ const tEndX = vTopX + accentTopFillet * ux;
141
+ const tEndY = vTopY + accentTopFillet * uy;
142
+
143
+ const startY = accentStartY;
144
+ const endX = accentEndX ?? Math.round(p4X + 4);
145
+
146
+ return [
147
+ `M 0 ${startY}`,
148
+ `C -8 ${startY}, ${vTopX} ${startY + 20}, ${tStartX.toFixed(2)} ${tStartY.toFixed(2)}`,
149
+ // Curve 1: Top fillet transitioning vertical edge into diagonal
150
+ `Q ${vTopX.toFixed(2)} ${vTopY.toFixed(2)} ${tEndX.toFixed(2)} ${tEndY.toFixed(2)}`,
151
+ // Straight diagonal running parallel to the white card
152
+ `L ${bStartX.toFixed(2)} ${bStartY.toFixed(2)}`,
153
+ // Curve 2: Bottom fillet transitioning diagonal into horizontal edge
154
+ `Q ${vBottomX.toFixed(2)} ${vBottomY.toFixed(2)} ${bEndX.toFixed(2)} ${height}`,
155
+ `H ${endX}`,
156
+ `L ${p4X.toFixed(2)} ${height}`,
157
+ `Q ${chamferX.toFixed(2)} ${height.toFixed(2)} ${p3X.toFixed(2)} ${p3Y.toFixed(2)}`,
158
+ `L ${p2X.toFixed(2)} ${p2Y.toFixed(2)}`,
159
+ `Q 0 ${(height - chamferY).toFixed(2)} 0 ${p1Y.toFixed(2)}`,
160
+ `L 0 ${startY}`,
161
+ 'Z',
162
+ ].join(' ');
163
+ }
164
+
165
+ /**
166
+ * Generates an SVG path for the top ribbon step badge.
167
+ * Features 5 distinct rounded curves matching the design:
168
+ * 1. Top-Left rounded corner
169
+ * 2. Top-Right rounded corner
170
+ * 3. Bottom-Right rounded corner
171
+ * 4. Bottom-Center pointed tip curve
172
+ * 5. Bottom-Left rounded corner
173
+ */
174
+ export function createHorizCardBadgePath({
175
+ width = 84,
176
+ height = 58,
177
+ topRadius = 12,
178
+ tipRadius = 8,
179
+ sideHeight = 46,
180
+ bottomRadius = 8,
181
+ }: ICreateHorizCardBadgePathParams = {}): string {
182
+ const halfW = width / 2;
183
+ const dx = halfW;
184
+ const dy = height - sideHeight;
185
+ const len = Math.hypot(dx, dy);
186
+ const ux = len > 0 ? dx / len : 0;
187
+ const uy = len > 0 ? dy / len : 0;
188
+
189
+ return [
190
+ // 1. Top-Left curve
191
+ `M ${topRadius} 0`,
192
+ // Top horizontal edge
193
+ `H ${width - topRadius}`,
194
+ // 2. Top-Right curve
195
+ `A ${topRadius} ${topRadius} 0 0 1 ${width} ${topRadius}`,
196
+ // Right vertical edge
197
+ `V ${sideHeight - bottomRadius}`,
198
+ // 3. Bottom-Right curve
199
+ `Q ${width} ${sideHeight} ${(width - bottomRadius * ux).toFixed(2)} ${(sideHeight + bottomRadius * uy).toFixed(2)}`,
200
+ // Bottom-right diagonal edge
201
+ `L ${(halfW + tipRadius * ux).toFixed(2)} ${(height - tipRadius * uy).toFixed(2)}`,
202
+ // 4. Bottom-Center tip curve
203
+ `Q ${halfW} ${height} ${(halfW - tipRadius * ux).toFixed(2)} ${(height - tipRadius * uy).toFixed(2)}`,
204
+ // Bottom-left diagonal edge
205
+ `L ${(bottomRadius * ux).toFixed(2)} ${(sideHeight + bottomRadius * uy).toFixed(2)}`,
206
+ // 5. Bottom-Left curve
207
+ `Q 0 ${sideHeight} 0 ${sideHeight - bottomRadius}`,
208
+ // Left vertical edge
209
+ `V ${topRadius}`,
210
+ `A ${topRadius} ${topRadius} 0 0 1 ${topRadius} 0`,
211
+ 'Z',
212
+ ].join(' ');
213
+ }
214
+
215
+ /**
216
+ * Converts a hex color string to an RGBA string with the given alpha transparency.
217
+ */
218
+ export function hexToRgba({ hex, alpha = 0.15 }: IHexToRgbaParams): string {
219
+ let sanitized = hex.trim().replace(/^#/, '');
220
+
221
+ if (sanitized.length === 3) {
222
+ sanitized = sanitized
223
+ .split('')
224
+ .map((char) => {
225
+ return char + char;
226
+ })
227
+ .join('');
228
+ }
229
+
230
+ if (sanitized.length !== 6) {
231
+ return hex;
232
+ }
233
+
234
+ const r = Number.parseInt(sanitized.slice(0, 2), 16);
235
+ const g = Number.parseInt(sanitized.slice(2, 4), 16);
236
+ const b = Number.parseInt(sanitized.slice(4, 6), 16);
237
+
238
+ if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
239
+ return hex;
240
+ }
241
+
242
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
243
+ }
@@ -0,0 +1,2 @@
1
+ export * from './context';
2
+ export * from './createHorizCardGeometry';
@@ -0,0 +1,26 @@
1
+ import type { ComputedRef, InjectionKey } from 'vue';
2
+
3
+ import type { IQuadHubItem, IResolvedQuadHubItem } from './resolveQuadHubItems';
4
+
5
+ export interface IQuadHubRegistration {
6
+ readonly index: number;
7
+ readonly item: ComputedRef<IResolvedQuadHubItem>;
8
+ readonly setCustomBadge?: (text: string | number) => void;
9
+ }
10
+
11
+ export interface IQuadHubRootContext {
12
+ readonly animation?: ComputedRef<boolean>;
13
+ readonly registerCallout: (props?: IQuadHubItem) => IQuadHubRegistration;
14
+ readonly items: ComputedRef<readonly IResolvedQuadHubItem[]>;
15
+ }
16
+
17
+ export interface IQuadHubCalloutContext {
18
+ readonly index: number;
19
+ readonly item: ComputedRef<IResolvedQuadHubItem>;
20
+ }
21
+
22
+ export const QUAD_HUB_ROOT_KEY: InjectionKey<IQuadHubRootContext> =
23
+ Symbol('QUAD_HUB_ROOT_KEY');
24
+
25
+ export const QUAD_HUB_CALLOUT_KEY: InjectionKey<IQuadHubCalloutContext> =
26
+ Symbol('QUAD_HUB_CALLOUT_KEY');