@leftium/logo 0.1.0 → 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.
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { AppLogoProps, GradientConfig } from './app-logo/types.js';
3
- import { APP_LOGO_DEFAULTS } from './app-logo/defaults.js';
3
+ import { APP_LOGO_DEFAULTS, DEFAULT_EMOJI_STYLE } from './app-logo/defaults.js';
4
4
  import { resolveIcon, type ResolvedIcon } from './app-logo/iconify.js';
5
5
  import { applyColorMode } from './app-logo/color-transform.js';
6
6
  import { generateCornerPath } from './app-logo/squircle.js';
@@ -13,11 +13,13 @@
13
13
  iconOffsetX = APP_LOGO_DEFAULTS.iconOffsetX,
14
14
  iconOffsetY = APP_LOGO_DEFAULTS.iconOffsetY,
15
15
  iconRotation = APP_LOGO_DEFAULTS.iconRotation,
16
+ grayscaleLightness = 100,
16
17
  cornerRadius = APP_LOGO_DEFAULTS.cornerRadius,
17
18
  cornerShape = APP_LOGO_DEFAULTS.cornerShape,
18
19
  background = APP_LOGO_DEFAULTS.background as AppLogoProps['background'],
19
- size = APP_LOGO_DEFAULTS.size
20
- }: AppLogoProps = $props();
20
+ size = APP_LOGO_DEFAULTS.size,
21
+ emojiStyle = DEFAULT_EMOJI_STYLE
22
+ }: AppLogoProps & { emojiStyle?: string } = $props();
21
23
 
22
24
  // Resolved icon state — use $state.raw since this is replaced, not mutated
23
25
  let resolved: ResolvedIcon | null = $state.raw(null);
@@ -25,11 +27,12 @@
25
27
  // Fetch icon when the icon prop changes
26
28
  $effect(() => {
27
29
  const currentIcon = icon;
30
+ const currentEmojiStyle = emojiStyle;
28
31
  resolved = null;
29
32
 
30
- resolveIcon(currentIcon).then((result) => {
31
- // Only update if icon hasn't changed while we were fetching
32
- if (icon === currentIcon) {
33
+ resolveIcon(currentIcon, currentEmojiStyle).then((result) => {
34
+ // Only update if icon/emojiStyle hasn't changed while we were fetching
35
+ if (icon === currentIcon && emojiStyle === currentEmojiStyle) {
33
36
  resolved = result;
34
37
  }
35
38
  });
@@ -39,7 +42,13 @@
39
42
  let coloredSvgContent = $derived.by(() => {
40
43
  const r = resolved;
41
44
  if (!r) return '';
42
- return applyColorMode(r.svgContent, r.isMonochrome, iconColorMode, iconColor);
45
+ return applyColorMode(
46
+ r.svgContent,
47
+ r.isMonochrome,
48
+ iconColorMode,
49
+ iconColor,
50
+ grayscaleLightness
51
+ );
43
52
  });
44
53
 
45
54
  // Build the full inline SVG with viewBox and 100% sizing
@@ -1,4 +1,7 @@
1
1
  import type { AppLogoProps } from './app-logo/types.js';
2
- declare const AppLogo: import("svelte").Component<AppLogoProps, {}, "">;
2
+ type $$ComponentProps = AppLogoProps & {
3
+ emojiStyle?: string;
4
+ };
5
+ declare const AppLogo: import("svelte").Component<$$ComponentProps, {}, "">;
3
6
  type AppLogo = ReturnType<typeof AppLogo>;
4
7
  export default AppLogo;
@@ -8,15 +8,17 @@ import type { IconColorMode } from './types.js';
8
8
  /**
9
9
  * Transform a single CSS color to grayscale using OKLCH.
10
10
  * Sets chroma to 0, preserving perceptual lightness.
11
+ * @param lightness Optional lightness multiplier (0-200), default 100 = no change
11
12
  */
12
- export declare function toGrayscale(color: string): string;
13
+ export declare function toGrayscale(color: string, lightness?: number): string;
13
14
  /**
14
15
  * Transform a single CSS color to grayscale, then tint toward a target color.
15
16
  *
16
17
  * The tint color's hue and chroma influence the result, while the original
17
18
  * color's lightness is preserved. This unifies a palette while keeping contrast.
19
+ * @param lightness Optional lightness multiplier (0-200), default 100 = no change
18
20
  */
19
- export declare function toGrayscaleTint(color: string, tintColor: string): string;
21
+ export declare function toGrayscaleTint(color: string, tintColor: string, lightness?: number): string;
20
22
  /**
21
23
  * Remap a single CSS color to a target hue, optionally overriding saturation.
22
24
  *
@@ -32,5 +34,7 @@ export declare function remapHue(color: string, targetHue: number, targetSaturat
32
34
  *
33
35
  * Color transforms operate on fill/stroke attribute values and style properties,
34
36
  * replacing each color with its transformed equivalent.
37
+ *
38
+ * @param grayscaleLightness Lightness multiplier for grayscale modes (0-200), default 100
35
39
  */
36
- export declare function applyColorMode(svgContent: string, isMonochrome: boolean, colorMode: IconColorMode, iconColor: string): string;
40
+ export declare function applyColorMode(svgContent: string, isMonochrome: boolean, colorMode: IconColorMode, iconColor: string, grayscaleLightness?: number): string;
@@ -37,30 +37,34 @@ function oklchToHex(l, c, h, alpha) {
37
37
  /**
38
38
  * Transform a single CSS color to grayscale using OKLCH.
39
39
  * Sets chroma to 0, preserving perceptual lightness.
40
+ * @param lightness Optional lightness multiplier (0-200), default 100 = no change
40
41
  */
41
- export function toGrayscale(color) {
42
+ export function toGrayscale(color, lightness = 100) {
42
43
  const oklch = parseToOklch(color);
43
44
  if (!oklch)
44
45
  return color;
45
- return oklchToHex(oklch.l, 0, 0, oklch.alpha);
46
+ const l = Math.min(1, Math.max(0, oklch.l * (lightness / 100)));
47
+ return oklchToHex(l, 0, 0, oklch.alpha);
46
48
  }
47
49
  /**
48
50
  * Transform a single CSS color to grayscale, then tint toward a target color.
49
51
  *
50
52
  * The tint color's hue and chroma influence the result, while the original
51
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
52
55
  */
53
- export function toGrayscaleTint(color, tintColor) {
56
+ export function toGrayscaleTint(color, tintColor, lightness = 100) {
54
57
  const oklch = parseToOklch(color);
55
58
  if (!oklch)
56
59
  return color;
57
60
  const tint = parseToOklch(tintColor);
58
61
  if (!tint)
59
62
  return color;
60
- // Use original lightness, tint's hue, and a fraction of tint's chroma
63
+ // Use original lightness (scaled by multiplier), tint's hue, and a fraction of tint's chroma
61
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)));
62
66
  const tintChroma = tint.c * 0.7; // 70% of tint chroma for a softer effect
63
- return oklchToHex(oklch.l, tintChroma, tint.h, oklch.alpha);
67
+ return oklchToHex(l, tintChroma, tint.h, oklch.alpha);
64
68
  }
65
69
  /**
66
70
  * Remap a single CSS color to a target hue, optionally overriding saturation.
@@ -85,8 +89,10 @@ export function remapHue(color, targetHue, targetSaturation) {
85
89
  *
86
90
  * Color transforms operate on fill/stroke attribute values and style properties,
87
91
  * replacing each color with its transformed equivalent.
92
+ *
93
+ * @param grayscaleLightness Lightness multiplier for grayscale modes (0-200), default 100
88
94
  */
89
- export function applyColorMode(svgContent, isMonochrome, colorMode, iconColor) {
95
+ export function applyColorMode(svgContent, isMonochrome, colorMode, iconColor, grayscaleLightness = 100) {
90
96
  // Handle simple string modes first
91
97
  if (colorMode === 'original') {
92
98
  return svgContent;
@@ -111,10 +117,10 @@ export function applyColorMode(svgContent, isMonochrome, colorMode, iconColor) {
111
117
  // Replace currentColor with iconColor before transforming
112
118
  const resolvedColor = color === 'currentColor' ? iconColor : color;
113
119
  if (colorMode === 'grayscale') {
114
- return toGrayscale(resolvedColor);
120
+ return toGrayscale(resolvedColor, grayscaleLightness);
115
121
  }
116
122
  if (colorMode === 'grayscale-tint') {
117
- return toGrayscaleTint(resolvedColor, iconColor);
123
+ return toGrayscaleTint(resolvedColor, iconColor, grayscaleLightness);
118
124
  }
119
125
  // Object mode: { hue, saturation? }
120
126
  if (typeof colorMode === 'object') {
@@ -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;