@xivdyetools/svg 1.0.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.
Files changed (50) hide show
  1. package/README.md +136 -0
  2. package/dist/accessibility-comparison.d.ts +66 -0
  3. package/dist/accessibility-comparison.d.ts.map +1 -0
  4. package/dist/accessibility-comparison.js +201 -0
  5. package/dist/accessibility-comparison.js.map +1 -0
  6. package/dist/base.d.ts +115 -0
  7. package/dist/base.d.ts.map +1 -0
  8. package/dist/base.js +201 -0
  9. package/dist/base.js.map +1 -0
  10. package/dist/budget-comparison.d.ts +121 -0
  11. package/dist/budget-comparison.d.ts.map +1 -0
  12. package/dist/budget-comparison.js +331 -0
  13. package/dist/budget-comparison.js.map +1 -0
  14. package/dist/comparison-grid.d.ts +61 -0
  15. package/dist/comparison-grid.d.ts.map +1 -0
  16. package/dist/comparison-grid.js +366 -0
  17. package/dist/comparison-grid.js.map +1 -0
  18. package/dist/contrast-matrix.d.ts +78 -0
  19. package/dist/contrast-matrix.d.ts.map +1 -0
  20. package/dist/contrast-matrix.js +303 -0
  21. package/dist/contrast-matrix.js.map +1 -0
  22. package/dist/dye-info-card.d.ts +34 -0
  23. package/dist/dye-info-card.d.ts.map +1 -0
  24. package/dist/dye-info-card.js +218 -0
  25. package/dist/dye-info-card.js.map +1 -0
  26. package/dist/gradient.d.ts +49 -0
  27. package/dist/gradient.d.ts.map +1 -0
  28. package/dist/gradient.js +131 -0
  29. package/dist/gradient.js.map +1 -0
  30. package/dist/harmony-wheel.d.ts +38 -0
  31. package/dist/harmony-wheel.d.ts.map +1 -0
  32. package/dist/harmony-wheel.js +158 -0
  33. package/dist/harmony-wheel.js.map +1 -0
  34. package/dist/index.d.ts +31 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +32 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/palette-grid.d.ts +105 -0
  39. package/dist/palette-grid.d.ts.map +1 -0
  40. package/dist/palette-grid.js +277 -0
  41. package/dist/palette-grid.js.map +1 -0
  42. package/dist/preset-swatch.d.ts +76 -0
  43. package/dist/preset-swatch.d.ts.map +1 -0
  44. package/dist/preset-swatch.js +226 -0
  45. package/dist/preset-swatch.js.map +1 -0
  46. package/dist/random-dyes-grid.d.ts +45 -0
  47. package/dist/random-dyes-grid.d.ts.map +1 -0
  48. package/dist/random-dyes-grid.js +143 -0
  49. package/dist/random-dyes-grid.js.map +1 -0
  50. package/package.json +56 -0
package/README.md ADDED
@@ -0,0 +1,136 @@
1
+ # @xivdyetools/svg
2
+
3
+ > Platform-agnostic SVG card generators for the XIV Dye Tools ecosystem — pure functions: data in, SVG string out.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@xivdyetools/svg)](https://www.npmjs.com/package/@xivdyetools/svg)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Overview
9
+
10
+ `@xivdyetools/svg` generates all visual output for XIV Dye Tools bots and the web app as SVG strings. Every generator is a **pure function** — it takes data and returns an SVG string. PNG rasterization is handled by consumers using their platform's renderer (`@resvg/resvg-wasm` for Cloudflare Workers, `@resvg/resvg-js` for Node.js).
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @xivdyetools/svg
16
+ ```
17
+
18
+ ## Generators
19
+
20
+ ### Card Generators
21
+
22
+ | Function | Description |
23
+ |----------|-------------|
24
+ | `generateDyeInfoCard(options)` | Single dye info card with color values |
25
+ | `generateRandomDyesGrid(options)` | Grid of randomly selected dyes |
26
+ | `generateComparisonGrid(options)` | Side-by-side dye comparison |
27
+ | `generatePresetSwatch(options)` | Full preset swatch with all dye slots |
28
+ | `generateCompactPresetSwatch(options)` | Compact single-row preset swatch |
29
+
30
+ ### Color Tool Generators
31
+
32
+ | Function | Description |
33
+ |----------|-------------|
34
+ | `generateHarmonyWheel(options)` | Color harmony wheel (triadic, complementary, etc.) |
35
+ | `generateGradientBar(options)` | Gradient bar between two colors |
36
+ | `generatePaletteGrid(options)` | Color match palette grid with quality labels |
37
+ | `generateAccessibilityComparison(options)` | Colorblind simulation comparison |
38
+ | `generateCompactAccessibilityRow(options)` | Compact single-row accessibility view |
39
+ | `generateContrastMatrix(options)` | WCAG contrast ratio matrix |
40
+ | `generateBudgetComparison(options)` | Market price comparison chart |
41
+
42
+ ### Utility Generators
43
+
44
+ | Function | Description |
45
+ |----------|-------------|
46
+ | `generateErrorSvg(message)` | Generic error message SVG |
47
+ | `generateNoWorldSetSvg()` | "No world set" prompt SVG |
48
+
49
+ ## Usage
50
+
51
+ ```typescript
52
+ import { generateDyeInfoCard, generateHarmonyWheel } from '@xivdyetools/svg';
53
+
54
+ // Generate a dye info card
55
+ const svg = generateDyeInfoCard({
56
+ dye: { name: 'Snow White', hex: '#FFFFFF', rgb: { r: 255, g: 255, b: 255 }, ... },
57
+ localizedName: 'Snow White',
58
+ localizedCategory: 'White',
59
+ });
60
+ // → '<svg xmlns="http://www.w3.org/2000/svg" ...>...</svg>'
61
+
62
+ // Generate a harmony wheel
63
+ const wheelSvg = generateHarmonyWheel({
64
+ baseDye: { name: 'Coral Pink', hex: '#FF6B6B', ... },
65
+ harmonyDyes: [/* complementary, triadic dyes */],
66
+ harmonyType: 'triadic',
67
+ });
68
+ ```
69
+
70
+ ### SVG Primitives
71
+
72
+ The package also exports low-level SVG building blocks for custom compositions:
73
+
74
+ ```typescript
75
+ import { createSvgDocument, rect, circle, text, group, THEME, FONTS } from '@xivdyetools/svg';
76
+
77
+ // Build custom SVG
78
+ const svg = createSvgDocument(400, 300,
79
+ rect(0, 0, 400, 300, { fill: THEME.background }),
80
+ circle(200, 150, 50, { fill: '#FF6B6B' }),
81
+ text(200, 150, 'Hello', { fill: '#FFFFFF', fontFamily: FONTS.primary }),
82
+ );
83
+ ```
84
+
85
+ ### Color Utilities
86
+
87
+ ```typescript
88
+ import {
89
+ hexToRgb, rgbToHex,
90
+ getLuminance, getContrastTextColor,
91
+ interpolateColor, generateGradientColors,
92
+ calculateContrast, getMatchQuality,
93
+ escapeXml, formatGil,
94
+ } from '@xivdyetools/svg';
95
+
96
+ // Contrast-safe text color
97
+ const textColor = getContrastTextColor('#1a1a2e');
98
+ // → '#FFFFFF' (white text on dark background)
99
+
100
+ // WCAG contrast ratio
101
+ const ratio = calculateContrast('#FFFFFF', '#000000');
102
+ // → 21 (maximum contrast)
103
+
104
+ // Gradient interpolation
105
+ const colors = generateGradientColors('#FF0000', '#0000FF', 5);
106
+ // → ['#FF0000', '#BF003F', '#7F007F', '#3F00BF', '#0000FF']
107
+ ```
108
+
109
+ ## Constants
110
+
111
+ | Export | Description |
112
+ |--------|-------------|
113
+ | `THEME` | Shared theme (background, text, accent colors, spacing) |
114
+ | `FONTS` | Font family definitions (primary, mono, CJK) |
115
+ | `MATCH_QUALITIES` | Color match quality thresholds |
116
+ | `CATEGORY_DISPLAY` | Dye category display name mapping |
117
+
118
+ ## Design Principles
119
+
120
+ - **Pure functions** — no side effects, no file I/O, no network calls
121
+ - **No rendering** — outputs SVG strings only; consumers handle PNG rasterization
122
+ - **CJK support** — subset Noto Sans SC/KR fonts for Chinese and Korean text
123
+ - **Consistent theming** — all generators share `THEME` and `FONTS` constants
124
+ - **Accessible** — contrast-safe text colors, WCAG ratio calculations built in
125
+
126
+ ## Dependencies
127
+
128
+ | Package | Purpose |
129
+ |---------|---------|
130
+ | `@xivdyetools/core` | Dye database, color algorithms |
131
+ | `@xivdyetools/types` | Shared type definitions (Dye, RGB, HSV) |
132
+ | `@xivdyetools/color-blending` | Color interpolation for gradients |
133
+
134
+ ## License
135
+
136
+ MIT
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Accessibility Comparison SVG Generator
3
+ *
4
+ * Generates a visual comparison showing how a dye color appears
5
+ * under different types of color vision deficiency.
6
+ *
7
+ * Layout (2x2 grid):
8
+ * +------------------------------------------+
9
+ * | Dalamud Red |
10
+ * +------------------------------------------+
11
+ * | [Normal Vision] | [Protanopia] |
12
+ * | #AA1111 | #967D34 |
13
+ * +------------------------------------------+
14
+ * | [Deuteranopia] | [Tritanopia] |
15
+ * | #8F8020 | #A81825 |
16
+ * +------------------------------------------+
17
+ *
18
+ * @module services/svg/accessibility-comparison
19
+ */
20
+ /**
21
+ * Vision types for colorblind simulation
22
+ */
23
+ export type VisionType = 'protanopia' | 'deuteranopia' | 'tritanopia';
24
+ /**
25
+ * All vision types including normal
26
+ */
27
+ export type AllVisionTypes = 'normal' | VisionType;
28
+ /**
29
+ * Options for generating the accessibility comparison
30
+ */
31
+ export interface AccessibilityComparisonOptions {
32
+ /** Hex color of the dye */
33
+ dyeHex: string;
34
+ /** Name of the dye */
35
+ dyeName: string;
36
+ /** Vision types to show (default: all) */
37
+ visionTypes?: VisionType[];
38
+ /** Canvas width (default: 500) */
39
+ width?: number;
40
+ }
41
+ /**
42
+ * Generate an accessibility comparison SVG showing colorblind simulations
43
+ *
44
+ * @param options - Comparison configuration
45
+ * @returns SVG string
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const svg = generateAccessibilityComparison({
50
+ * dyeHex: '#AA1111',
51
+ * dyeName: 'Dalamud Red',
52
+ * });
53
+ * const png = await renderSvgToPng(svg);
54
+ * ```
55
+ */
56
+ export declare function generateAccessibilityComparison(options: AccessibilityComparisonOptions): string;
57
+ /**
58
+ * Generate a compact accessibility comparison (single row)
59
+ * Useful for inline display in other components
60
+ */
61
+ export declare function generateCompactAccessibilityRow(hex: string, visionTypes?: VisionType[]): {
62
+ type: AllVisionTypes;
63
+ hex: string;
64
+ label: string;
65
+ }[];
66
+ //# sourceMappingURL=accessibility-comparison.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessibility-comparison.d.ts","sourceRoot":"","sources":["../src/accessibility-comparison.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAmBH;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,cAAc,GAAG,YAAY,CAAC;AAEtE;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoDD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,8BAA8B,GACtC,MAAM,CAwDR;AA+FD;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,MAAM,EACX,WAAW,GAAE,UAAU,EAAiD,GACvE;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAwBxD"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Accessibility Comparison SVG Generator
3
+ *
4
+ * Generates a visual comparison showing how a dye color appears
5
+ * under different types of color vision deficiency.
6
+ *
7
+ * Layout (2x2 grid):
8
+ * +------------------------------------------+
9
+ * | Dalamud Red |
10
+ * +------------------------------------------+
11
+ * | [Normal Vision] | [Protanopia] |
12
+ * | #AA1111 | #967D34 |
13
+ * +------------------------------------------+
14
+ * | [Deuteranopia] | [Tritanopia] |
15
+ * | #8F8020 | #A81825 |
16
+ * +------------------------------------------+
17
+ *
18
+ * @module services/svg/accessibility-comparison
19
+ */
20
+ import { ColorService } from '@xivdyetools/core';
21
+ import { createSvgDocument, rect, text, THEME, FONTS, escapeXml, getContrastTextColor, rgbToHex, hexToRgb, } from './base.js';
22
+ // ============================================================================
23
+ // Constants
24
+ // ============================================================================
25
+ const DEFAULT_WIDTH = 500;
26
+ const PADDING = 20;
27
+ const TITLE_HEIGHT = 50;
28
+ const SWATCH_SIZE = 140;
29
+ const SWATCH_GAP = 20;
30
+ const LABEL_HEIGHT = 60;
31
+ /**
32
+ * Vision type metadata
33
+ */
34
+ const VISION_LABELS = {
35
+ normal: {
36
+ label: 'Normal Vision',
37
+ description: 'Full color perception',
38
+ },
39
+ protanopia: {
40
+ label: 'Protanopia',
41
+ description: 'Red-blind (~1% of males)',
42
+ },
43
+ deuteranopia: {
44
+ label: 'Deuteranopia',
45
+ description: 'Green-blind (~1% of males)',
46
+ },
47
+ tritanopia: {
48
+ label: 'Tritanopia',
49
+ description: 'Blue-blind (rare)',
50
+ },
51
+ };
52
+ // ColorService methods are static - no need for instance
53
+ // ============================================================================
54
+ // SVG Generation
55
+ // ============================================================================
56
+ /**
57
+ * Generate an accessibility comparison SVG showing colorblind simulations
58
+ *
59
+ * @param options - Comparison configuration
60
+ * @returns SVG string
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const svg = generateAccessibilityComparison({
65
+ * dyeHex: '#AA1111',
66
+ * dyeName: 'Dalamud Red',
67
+ * });
68
+ * const png = await renderSvgToPng(svg);
69
+ * ```
70
+ */
71
+ export function generateAccessibilityComparison(options) {
72
+ const { dyeHex, dyeName, visionTypes = ['protanopia', 'deuteranopia', 'tritanopia'], width = DEFAULT_WIDTH, } = options;
73
+ // Simulate colors for all vision types
74
+ const simulations = simulateAllVisions(dyeHex, visionTypes);
75
+ // Calculate dimensions for 2x2 grid
76
+ const gridCols = 2;
77
+ const gridRows = Math.ceil(simulations.length / gridCols);
78
+ const cellWidth = (width - PADDING * 3) / gridCols;
79
+ const cellHeight = SWATCH_SIZE + LABEL_HEIGHT;
80
+ const gridHeight = gridRows * (cellHeight + SWATCH_GAP) - SWATCH_GAP;
81
+ const height = PADDING * 2 + TITLE_HEIGHT + gridHeight;
82
+ const elements = [];
83
+ // Background
84
+ elements.push(rect(0, 0, width, height, THEME.background, { rx: 12, ry: 12 }));
85
+ // Title
86
+ elements.push(text(width / 2, PADDING + 24, escapeXml(dyeName), {
87
+ fill: THEME.text,
88
+ fontSize: 22,
89
+ fontFamily: FONTS.headerCjk,
90
+ fontWeight: 600,
91
+ textAnchor: 'middle',
92
+ }));
93
+ elements.push(text(width / 2, PADDING + 44, 'Color Vision Accessibility', {
94
+ fill: THEME.textMuted,
95
+ fontSize: 12,
96
+ fontFamily: FONTS.primary,
97
+ textAnchor: 'middle',
98
+ }));
99
+ // Render grid of swatches
100
+ const gridStartY = PADDING + TITLE_HEIGHT;
101
+ simulations.forEach((sim, index) => {
102
+ const col = index % gridCols;
103
+ const row = Math.floor(index / gridCols);
104
+ const x = PADDING + col * (cellWidth + SWATCH_GAP);
105
+ const y = gridStartY + row * (cellHeight + SWATCH_GAP);
106
+ elements.push(generateVisionSwatch(sim, x, y, cellWidth));
107
+ });
108
+ return createSvgDocument(width, height, elements.join('\n'));
109
+ }
110
+ /**
111
+ * Simulate a color for all requested vision types (plus normal)
112
+ */
113
+ function simulateAllVisions(hex, visionTypes) {
114
+ const results = [];
115
+ const rgb = hexToRgb(hex);
116
+ // Always include normal vision first
117
+ results.push({
118
+ type: 'normal',
119
+ ...VISION_LABELS.normal,
120
+ hex,
121
+ rgb,
122
+ });
123
+ // Add simulated versions
124
+ for (const visionType of visionTypes) {
125
+ const simulated = ColorService.simulateColorblindness(rgb, visionType);
126
+ const simHex = rgbToHex(simulated.r, simulated.g, simulated.b);
127
+ results.push({
128
+ type: visionType,
129
+ ...VISION_LABELS[visionType],
130
+ hex: simHex,
131
+ rgb: simulated,
132
+ });
133
+ }
134
+ return results;
135
+ }
136
+ /**
137
+ * Generate a single vision swatch
138
+ */
139
+ function generateVisionSwatch(sim, x, y, width) {
140
+ const elements = [];
141
+ const swatchX = x + (width - SWATCH_SIZE) / 2;
142
+ // Swatch background (slightly larger for border effect)
143
+ elements.push(rect(swatchX - 2, y - 2, SWATCH_SIZE + 4, SWATCH_SIZE + 4, THEME.border, {
144
+ rx: 10,
145
+ ry: 10,
146
+ }));
147
+ // Color swatch
148
+ elements.push(rect(swatchX, y, SWATCH_SIZE, SWATCH_SIZE, sim.hex, {
149
+ rx: 8,
150
+ ry: 8,
151
+ }));
152
+ // Vision type label
153
+ elements.push(text(x + width / 2, y + SWATCH_SIZE + 20, sim.label, {
154
+ fill: THEME.text,
155
+ fontSize: 13,
156
+ fontFamily: FONTS.primary,
157
+ fontWeight: 600,
158
+ textAnchor: 'middle',
159
+ }));
160
+ // Hex code
161
+ elements.push(text(x + width / 2, y + SWATCH_SIZE + 38, sim.hex.toUpperCase(), {
162
+ fill: THEME.textMuted,
163
+ fontSize: 12,
164
+ fontFamily: FONTS.mono,
165
+ textAnchor: 'middle',
166
+ }));
167
+ // Description
168
+ elements.push(text(x + width / 2, y + SWATCH_SIZE + 54, sim.description, {
169
+ fill: THEME.textDim,
170
+ fontSize: 10,
171
+ fontFamily: FONTS.primary,
172
+ textAnchor: 'middle',
173
+ }));
174
+ return elements.join('\n');
175
+ }
176
+ /**
177
+ * Generate a compact accessibility comparison (single row)
178
+ * Useful for inline display in other components
179
+ */
180
+ export function generateCompactAccessibilityRow(hex, visionTypes = ['protanopia', 'deuteranopia', 'tritanopia']) {
181
+ const rgb = hexToRgb(hex);
182
+ const results = [];
183
+ // Normal vision
184
+ results.push({
185
+ type: 'normal',
186
+ hex,
187
+ label: 'Normal',
188
+ });
189
+ // Simulated versions
190
+ for (const visionType of visionTypes) {
191
+ const simulated = ColorService.simulateColorblindness(rgb, visionType);
192
+ const simHex = rgbToHex(simulated.r, simulated.g, simulated.b);
193
+ results.push({
194
+ type: visionType,
195
+ hex: simHex,
196
+ label: VISION_LABELS[visionType].label.split(' ')[0], // Just "Protanopia" etc
197
+ });
198
+ }
199
+ return results;
200
+ }
201
+ //# sourceMappingURL=accessibility-comparison.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessibility-comparison.js","sourceRoot":"","sources":["../src/accessibility-comparison.ts"],"names":[],"mappings":"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"}
package/dist/base.d.ts ADDED
@@ -0,0 +1,115 @@
1
+ /**
2
+ * SVG Base Utilities
3
+ *
4
+ * Core utilities for generating SVG graphics as strings.
5
+ * These SVGs are later converted to PNG using a platform-specific renderer
6
+ * (e.g., resvg-wasm for Cloudflare Workers, @resvg/resvg-js for Node.js).
7
+ */
8
+ /**
9
+ * XML-escapes a string for safe SVG inclusion
10
+ */
11
+ export declare function escapeXml(str: string): string;
12
+ /**
13
+ * Converts a hex color to RGB components
14
+ */
15
+ export declare function hexToRgb(hex: string): {
16
+ r: number;
17
+ g: number;
18
+ b: number;
19
+ };
20
+ /**
21
+ * Converts RGB to hex color string
22
+ */
23
+ export declare function rgbToHex(r: number, g: number, b: number): string;
24
+ /**
25
+ * Calculates the luminance of a color (for contrast calculations)
26
+ */
27
+ export declare function getLuminance(hex: string): number;
28
+ /**
29
+ * Determines if text should be light or dark based on background color
30
+ */
31
+ export declare function getContrastTextColor(bgHex: string): string;
32
+ /**
33
+ * Creates an SVG document wrapper
34
+ */
35
+ export declare function createSvgDocument(width: number, height: number, content: string): string;
36
+ /**
37
+ * Creates a rectangle element
38
+ */
39
+ export declare function rect(x: number, y: number, width: number, height: number, fill: string, options?: {
40
+ rx?: number;
41
+ ry?: number;
42
+ stroke?: string;
43
+ strokeWidth?: number;
44
+ opacity?: number;
45
+ }): string;
46
+ /**
47
+ * Creates a circle element
48
+ */
49
+ export declare function circle(cx: number, cy: number, r: number, fill: string, options?: {
50
+ stroke?: string;
51
+ strokeWidth?: number;
52
+ opacity?: number;
53
+ }): string;
54
+ /**
55
+ * Creates a line element
56
+ */
57
+ export declare function line(x1: number, y1: number, x2: number, y2: number, stroke: string, strokeWidth?: number, options?: {
58
+ opacity?: number;
59
+ dashArray?: string;
60
+ }): string;
61
+ /**
62
+ * Creates a text element
63
+ */
64
+ export declare function text(x: number, y: number, content: string, options?: {
65
+ fill?: string;
66
+ fontSize?: number;
67
+ fontFamily?: string;
68
+ fontWeight?: number | string;
69
+ textAnchor?: 'start' | 'middle' | 'end';
70
+ dominantBaseline?: 'auto' | 'middle' | 'hanging';
71
+ }): string;
72
+ /**
73
+ * Creates an arc path for pie/donut charts
74
+ */
75
+ export declare function arcPath(cx: number, cy: number, radius: number, startAngle: number, endAngle: number): string;
76
+ /**
77
+ * Creates a group element
78
+ */
79
+ export declare function group(content: string, transform?: string): string;
80
+ /**
81
+ * Theme colors for consistent styling
82
+ */
83
+ export declare const THEME: {
84
+ readonly background: "#1a1a2e";
85
+ readonly backgroundLight: "#2d2d3d";
86
+ readonly text: "#ffffff";
87
+ readonly textMuted: "#909090";
88
+ readonly textDim: "#666666";
89
+ readonly accent: "#5865f2";
90
+ readonly border: "#404050";
91
+ readonly success: "#57f287";
92
+ readonly warning: "#fee75c";
93
+ readonly error: "#ed4245";
94
+ };
95
+ /**
96
+ * Font families for consistent typography.
97
+ * These names match the bundled font files loaded by the renderer.
98
+ *
99
+ * - header: Space Grotesk (variable 300-700) - titles, headers
100
+ * - primary: Onest (variable 100-900) - body text, labels
101
+ * - mono: Habibi (regular only) - hex codes, monospace-like text
102
+ * - cjk: Noto Sans SC + Noto Sans KR - Japanese, Korean, Chinese text
103
+ * - primaryCjk: Onest with CJK/KR fallback - for localized text that may contain CJK
104
+ */
105
+ export declare const FONTS: {
106
+ readonly header: "Space Grotesk";
107
+ readonly primary: "Onest";
108
+ readonly mono: "Habibi";
109
+ readonly cjk: "Noto Sans SC, Noto Sans KR";
110
+ /** Use this for headings that may contain CJK characters (e.g., dye names) */
111
+ readonly headerCjk: "Space Grotesk, Noto Sans SC, Noto Sans KR";
112
+ /** Use this for body text that may contain CJK characters (e.g., dye names) */
113
+ readonly primaryCjk: "Onest, Noto Sans SC, Noto Sans KR";
114
+ };
115
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../src/base.ts"],"names":[],"mappings":"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"}