@leftium/logo 0.0.2 → 0.2.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 (34) hide show
  1. package/dist/AppLogo.svelte +146 -0
  2. package/dist/AppLogo.svelte.d.ts +7 -0
  3. package/dist/LeftiumLogo.svelte +267 -186
  4. package/dist/LeftiumLogo.svelte.d.ts +1 -0
  5. package/dist/app-logo/color-transform.d.ts +40 -0
  6. package/dist/app-logo/color-transform.js +142 -0
  7. package/dist/app-logo/config-serialization.d.ts +80 -0
  8. package/dist/app-logo/config-serialization.js +489 -0
  9. package/dist/app-logo/defaults.d.ts +60 -0
  10. package/dist/app-logo/defaults.js +55 -0
  11. package/dist/app-logo/generate-favicon-set.d.ts +44 -0
  12. package/dist/app-logo/generate-favicon-set.js +97 -0
  13. package/dist/app-logo/generate-ico.d.ts +18 -0
  14. package/dist/app-logo/generate-ico.js +63 -0
  15. package/dist/app-logo/generate-png.d.ts +16 -0
  16. package/dist/app-logo/generate-png.js +60 -0
  17. package/dist/app-logo/generate-svg.d.ts +9 -0
  18. package/dist/app-logo/generate-svg.js +160 -0
  19. package/dist/app-logo/iconify.d.ts +35 -0
  20. package/dist/app-logo/iconify.js +223 -0
  21. package/dist/app-logo/squircle.d.ts +43 -0
  22. package/dist/app-logo/squircle.js +213 -0
  23. package/dist/app-logo/types.d.ts +39 -0
  24. package/dist/app-logo/types.js +1 -0
  25. package/dist/assets/logo-parts/glow-squircle.svg +44 -0
  26. package/dist/index.d.ts +8 -3
  27. package/dist/index.js +9 -3
  28. package/dist/leftium-logo/generate-svg.d.ts +29 -0
  29. package/dist/leftium-logo/generate-svg.js +470 -0
  30. package/dist/tooltip.d.ts +18 -0
  31. package/dist/tooltip.js +38 -0
  32. package/dist/webgl-ripples/webgl-ripples.d.ts +0 -4
  33. package/dist/webgl-ripples/webgl-ripples.js +1 -1
  34. package/package.json +35 -20
@@ -0,0 +1,142 @@
1
+ /**
2
+ * OKLCH-based color transforms for icon color modes.
3
+ *
4
+ * Uses culori for perceptually uniform color manipulation.
5
+ * All transforms operate on individual CSS color strings.
6
+ */
7
+ import { parse, converter, formatHex, clampChroma, displayable } from 'culori';
8
+ const toOklch = converter('oklch');
9
+ /**
10
+ * Parse a CSS color string to OKLCH components.
11
+ * Returns null if the color can't be parsed.
12
+ */
13
+ function parseToOklch(color) {
14
+ const parsed = parse(color);
15
+ if (!parsed)
16
+ return null;
17
+ const oklch = toOklch(parsed);
18
+ return {
19
+ l: oklch.l ?? 0,
20
+ c: oklch.c ?? 0,
21
+ h: oklch.h ?? 0,
22
+ alpha: oklch.alpha
23
+ };
24
+ }
25
+ /**
26
+ * Convert OKLCH components back to a hex color string.
27
+ * Clamps to sRGB gamut.
28
+ */
29
+ function oklchToHex(l, c, h, alpha) {
30
+ let color = { mode: 'oklch', l, c, h, alpha };
31
+ // Clamp to displayable sRGB gamut
32
+ if (!displayable(color)) {
33
+ color = clampChroma(color, 'oklch');
34
+ }
35
+ return formatHex(color);
36
+ }
37
+ /**
38
+ * Transform a single CSS color to grayscale using OKLCH.
39
+ * Sets chroma to 0, preserving perceptual lightness.
40
+ * @param lightness Optional lightness multiplier (0-200), default 100 = no change
41
+ */
42
+ export function toGrayscale(color, lightness = 100) {
43
+ const oklch = parseToOklch(color);
44
+ if (!oklch)
45
+ return color;
46
+ const l = Math.min(1, Math.max(0, oklch.l * (lightness / 100)));
47
+ return oklchToHex(l, 0, 0, oklch.alpha);
48
+ }
49
+ /**
50
+ * Transform a single CSS color to grayscale, then tint toward a target color.
51
+ *
52
+ * The tint color's hue and chroma influence the result, while the original
53
+ * color's lightness is preserved. This unifies a palette while keeping contrast.
54
+ * @param lightness Optional lightness multiplier (0-200), default 100 = no change
55
+ */
56
+ export function toGrayscaleTint(color, tintColor, lightness = 100) {
57
+ const oklch = parseToOklch(color);
58
+ if (!oklch)
59
+ return color;
60
+ const tint = parseToOklch(tintColor);
61
+ if (!tint)
62
+ return color;
63
+ // Use original lightness (scaled by multiplier), tint's hue, and a fraction of tint's chroma
64
+ // Scale chroma by the ratio of original chroma to max, to preserve some variation
65
+ const l = Math.min(1, Math.max(0, oklch.l * (lightness / 100)));
66
+ const tintChroma = tint.c * 0.7; // 70% of tint chroma for a softer effect
67
+ return oklchToHex(l, tintChroma, tint.h, oklch.alpha);
68
+ }
69
+ /**
70
+ * Remap a single CSS color to a target hue, optionally overriding saturation.
71
+ *
72
+ * Preserves the original lightness. If saturation is provided, it's used as
73
+ * a percentage of the maximum chroma; otherwise, the original chroma is preserved.
74
+ */
75
+ export function remapHue(color, targetHue, targetSaturation) {
76
+ const oklch = parseToOklch(color);
77
+ if (!oklch)
78
+ return color;
79
+ const c = targetSaturation !== undefined
80
+ ? (targetSaturation / 100) * 0.4 // Map 0-100 to 0-0.4 OKLCH chroma range
81
+ : oklch.c;
82
+ return oklchToHex(oklch.l, c, targetHue, oklch.alpha);
83
+ }
84
+ /**
85
+ * Apply a full IconColorMode transformation to SVG content.
86
+ *
87
+ * Handles all modes: 'auto', 'original', 'monochrome', 'grayscale',
88
+ * 'grayscale-tint', and { hue, saturation? }.
89
+ *
90
+ * Color transforms operate on fill/stroke attribute values and style properties,
91
+ * replacing each color with its transformed equivalent.
92
+ *
93
+ * @param grayscaleLightness Lightness multiplier for grayscale modes (0-200), default 100
94
+ */
95
+ export function applyColorMode(svgContent, isMonochrome, colorMode, iconColor, grayscaleLightness = 100) {
96
+ // Handle simple string modes first
97
+ if (colorMode === 'original') {
98
+ return svgContent;
99
+ }
100
+ if (colorMode === 'auto') {
101
+ if (isMonochrome) {
102
+ return svgContent.replace(/currentColor/g, iconColor);
103
+ }
104
+ return svgContent;
105
+ }
106
+ if (colorMode === 'monochrome') {
107
+ return svgContent
108
+ .replace(/(fill=["'])(?!none)([^"']+)(["'])/gi, `$1${iconColor}$3`)
109
+ .replace(/(stroke=["'])(?!none)([^"']+)(["'])/gi, `$1${iconColor}$3`)
110
+ .replace(/currentColor/g, iconColor);
111
+ }
112
+ // For grayscale, grayscale-tint, and hue remap: transform each color value
113
+ const transformColor = (color) => {
114
+ if (color === 'none' || color === 'inherit' || color === 'transparent') {
115
+ return color;
116
+ }
117
+ // Replace currentColor with iconColor before transforming
118
+ const resolvedColor = color === 'currentColor' ? iconColor : color;
119
+ if (colorMode === 'grayscale') {
120
+ return toGrayscale(resolvedColor, grayscaleLightness);
121
+ }
122
+ if (colorMode === 'grayscale-tint') {
123
+ return toGrayscaleTint(resolvedColor, iconColor, grayscaleLightness);
124
+ }
125
+ // Object mode: { hue, saturation? }
126
+ if (typeof colorMode === 'object') {
127
+ return remapHue(resolvedColor, colorMode.hue, colorMode.saturation);
128
+ }
129
+ return color;
130
+ };
131
+ // Transform fill/stroke attribute values
132
+ let result = svgContent.replace(/((?:fill|stroke)=["'])([^"']+)(["'])/gi, (_match, prefix, value, suffix) => {
133
+ return `${prefix}${transformColor(value.trim())}${suffix}`;
134
+ });
135
+ // Transform fill/stroke in style attributes
136
+ result = result.replace(/((?:fill|stroke)\s*:\s*)([^;"']+)/gi, (_match, prefix, value) => {
137
+ return `${prefix}${transformColor(value.trim())}`;
138
+ });
139
+ // Also replace any remaining currentColor references
140
+ result = result.replace(/currentColor/g, transformColor('currentColor'));
141
+ return result;
142
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Config serialization: bidirectional conversion between the UI-level ColumnState
3
+ * (flat, slider-friendly) and the export-level AppLogoConfig (structured, spec-compliant).
4
+ *
5
+ * Also handles URL hash encoding/decoding and Svelte snippet generation.
6
+ */
7
+ import type { AppLogoConfig, CornerShape, GradientConfig, IconColorMode } from './types.js';
8
+ export interface ColumnState {
9
+ icon: string;
10
+ iconColor: string;
11
+ iconColorModeKey: string;
12
+ hueValue: number;
13
+ saturationValue: number;
14
+ iconSize: number;
15
+ iconOffsetX: number;
16
+ iconOffsetY: number;
17
+ iconRotation: number;
18
+ grayscaleLightness: number;
19
+ cornerRadius: number;
20
+ cornerK: number;
21
+ solidColor: string;
22
+ useGradient: boolean;
23
+ gradientAngle: number;
24
+ gradientPosition: number;
25
+ gradientScale: number;
26
+ }
27
+ export declare const DEFAULT_STATE: ColumnState;
28
+ export declare const DEFAULT_LOCKS: Record<keyof ColumnState, boolean>;
29
+ export declare function cornerKToShape(k: number): CornerShape;
30
+ export declare function cornerShapeToK(shape: CornerShape): number;
31
+ export declare function cornerKLabel(k: number): string;
32
+ export declare function iconColorModeFromFlat(key: string, hue: number, saturation: number): IconColorMode;
33
+ export declare function iconColorModeToFlat(mode: IconColorMode): {
34
+ key: string;
35
+ hue: number;
36
+ saturation: number;
37
+ };
38
+ export declare function backgroundFromFlat(col: ColumnState): string | GradientConfig;
39
+ /**
40
+ * Build an AppLogoConfig from the logo and effective favicon ColumnState.
41
+ * Also accepts the lock state to determine which favicon fields are overrides.
42
+ */
43
+ export declare function buildFullConfig(logoState: ColumnState, effectiveFaviconState: ColumnState, lockState: Record<keyof ColumnState, boolean>, emojiStyle?: string): AppLogoConfig;
44
+ /**
45
+ * Build a single-variant AppLogoConfig for export/rendering.
46
+ */
47
+ export declare function buildVariantConfig(col: ColumnState, variant: 'logo' | 'favicon', emojiStyle?: string): AppLogoConfig;
48
+ /**
49
+ * Convert an AppLogoConfig back to ColumnState + lock state.
50
+ * Shared fields populate the logo column. Favicon overrides populate
51
+ * the favicon column and unlock the corresponding locks.
52
+ */
53
+ export declare function configToUIState(config: AppLogoConfig): {
54
+ logo: ColumnState;
55
+ favicon: ColumnState;
56
+ locks: Record<keyof ColumnState, boolean>;
57
+ emojiStyle: string;
58
+ };
59
+ /**
60
+ * Encode a config object to a base64url string for URL hash.
61
+ * Uses TextEncoder for Unicode safety (emoji icon names, non-ASCII app names).
62
+ */
63
+ export declare function encodeConfigToHash(config: AppLogoConfig): string;
64
+ /**
65
+ * Decode a base64url string from URL hash back to AppLogoConfig.
66
+ * Returns null if decoding fails.
67
+ */
68
+ export declare function decodeConfigFromHash(hash: string): AppLogoConfig | null;
69
+ /**
70
+ * Generate a minimal <AppLogo> Svelte snippet with only non-default props.
71
+ */
72
+ export declare function generateSvelteSnippet(config: AppLogoConfig): string;
73
+ /**
74
+ * Generate the HTML favicon snippet for clipboard copy.
75
+ * Re-exports the existing function for convenience.
76
+ */
77
+ export declare function generateHtmlSnippet(appInfo: {
78
+ name?: string;
79
+ shortName?: string;
80
+ }): string;