@ankhorage/color-theory 0.1.0 → 0.3.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @ankhorage/color-theory
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a4068c6: Add a canonical informational status seed and generated swatch independent of brand harmony.
8
+
9
+ ## 0.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 78cd2ca: Publish the canonical seven-harmony catalog, distinguish tetradic and square geometry, and report unreliable hue generation for achromatic or low-chroma primary colors.
14
+
3
15
  ## 0.1.0
4
16
 
5
17
  ### Minor Changes
package/dist/harmony.d.ts CHANGED
@@ -1,7 +1,69 @@
1
1
  import type { HexColor } from './hex';
2
- export declare const COLOR_HARMONIES: readonly ["monochromatic", "analogous", "complementary", "triadic", "tetradic", "splitComplementary"];
3
- export type ColorHarmony = (typeof COLOR_HARMONIES)[number];
4
2
  export type GeneratedColorRole = 'primary' | 'secondary' | 'tertiary' | 'quaternary';
3
+ export declare const COLOR_HARMONY_CATALOG: readonly [{
4
+ readonly id: "monochromatic";
5
+ readonly label: "Monochromatic";
6
+ readonly description: "Uses one hue with ramp variation.";
7
+ readonly offsets: readonly [0];
8
+ readonly roles: readonly ["primary"];
9
+ readonly roleCount: 1;
10
+ }, {
11
+ readonly id: "analogous";
12
+ readonly label: "Analogous";
13
+ readonly description: "Uses the primary hue and its two neighboring hues at minus and plus 30 degrees.";
14
+ readonly offsets: readonly [0, -30, 30];
15
+ readonly roles: readonly ["primary", "secondary", "tertiary"];
16
+ readonly roleCount: 3;
17
+ }, {
18
+ readonly id: "complementary";
19
+ readonly label: "Complementary";
20
+ readonly description: "Pairs the primary hue with its opposite hue at 180 degrees.";
21
+ readonly offsets: readonly [0, 180];
22
+ readonly roles: readonly ["primary", "secondary"];
23
+ readonly roleCount: 2;
24
+ }, {
25
+ readonly id: "splitComplementary";
26
+ readonly label: "Split complementary";
27
+ readonly description: "Uses the primary hue with hues at 150 and 210 degrees.";
28
+ readonly offsets: readonly [0, 150, 210];
29
+ readonly roles: readonly ["primary", "secondary", "tertiary"];
30
+ readonly roleCount: 3;
31
+ }, {
32
+ readonly id: "triadic";
33
+ readonly label: "Triadic";
34
+ readonly description: "Uses three evenly spaced hues at 120-degree intervals.";
35
+ readonly offsets: readonly [0, 120, 240];
36
+ readonly roles: readonly ["primary", "secondary", "tertiary"];
37
+ readonly roleCount: 3;
38
+ }, {
39
+ readonly id: "tetradic";
40
+ readonly label: "Tetradic";
41
+ readonly description: "Uses a rectangular tetrad at 0, 60, 180, and 240 degrees.";
42
+ readonly offsets: readonly [0, 60, 180, 240];
43
+ readonly roles: readonly ["primary", "secondary", "tertiary", "quaternary"];
44
+ readonly roleCount: 4;
45
+ }, {
46
+ readonly id: "square";
47
+ readonly label: "Square";
48
+ readonly description: "Uses four evenly spaced hues at 90-degree intervals.";
49
+ readonly offsets: readonly [0, 90, 180, 270];
50
+ readonly roles: readonly ["primary", "secondary", "tertiary", "quaternary"];
51
+ readonly roleCount: 4;
52
+ }];
53
+ export type ColorHarmonyDefinition = (typeof COLOR_HARMONY_CATALOG)[number];
54
+ export type ColorHarmony = ColorHarmonyDefinition['id'];
55
+ export declare const COLOR_HARMONIES: readonly ColorHarmony[];
56
+ export declare const MIN_HUEFUL_CHROMA = 0.015;
57
+ export type HarmonyGenerationWarningCode = 'achromatic_primary' | 'low_chroma_primary';
58
+ export interface HarmonyGenerationWarning {
59
+ readonly code: HarmonyGenerationWarningCode;
60
+ readonly message: string;
61
+ }
62
+ export interface HarmonyGenerationDiagnostics {
63
+ readonly isHueReliable: boolean;
64
+ readonly primaryChroma: number;
65
+ readonly warnings: readonly HarmonyGenerationWarning[];
66
+ }
5
67
  export interface GeneratedHarmonyRoleColor {
6
68
  role: GeneratedColorRole;
7
69
  hex: HexColor;
@@ -15,6 +77,7 @@ export interface GeneratedHarmonyRoleColors {
15
77
  secondary?: GeneratedHarmonyRoleColor;
16
78
  tertiary?: GeneratedHarmonyRoleColor;
17
79
  quaternary?: GeneratedHarmonyRoleColor;
80
+ diagnostics: HarmonyGenerationDiagnostics;
18
81
  }
19
82
  /***
20
83
  Generate role-based harmony colors from a primary color and harmony strategy.
@@ -1 +1 @@
1
- {"version":3,"file":"harmony.d.ts","sourceRoot":"","sources":["../src/harmony.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC,eAAO,MAAM,eAAe,uGAOlB,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,YAAY,CAAC;AAErF,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,kBAAkB,CAAC;IACzB,GAAG,EAAE,QAAQ,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,UAAU,GAAG,WAAW,CAAC;CAClC;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC7C,OAAO,EAAE,yBAAyB,CAAC;IACnC,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IACrC,UAAU,CAAC,EAAE,yBAAyB,CAAC;CACxC;AAmCD;;EAEE;AACF,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,QAAQ,EACtB,OAAO,EAAE,YAAY,GACpB,0BAA0B,CAqC5B"}
1
+ {"version":3,"file":"harmony.d.ts","sourceRoot":"","sources":["../src/harmony.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,YAAY,CAAC;AAWrF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyDsB,CAAC;AAEzD,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC5E,MAAM,MAAM,YAAY,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;AAExD,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAA8C,CAAC;AAElG,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC,MAAM,MAAM,4BAA4B,GAAG,oBAAoB,GAAG,oBAAoB,CAAC;AAEvF,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,4BAA4B,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,SAAS,wBAAwB,EAAE,CAAC;CACxD;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,kBAAkB,CAAC;IACzB,GAAG,EAAE,QAAQ,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,UAAU,GAAG,WAAW,CAAC;CAClC;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC7C,OAAO,EAAE,yBAAyB,CAAC;IACnC,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IACrC,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,WAAW,EAAE,4BAA4B,CAAC;CAC3C;AAqCD;;EAEE;AACF,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,QAAQ,EACtB,OAAO,EAAE,YAAY,GACpB,0BAA0B,CAiC5B"}
package/dist/harmony.js CHANGED
@@ -1,59 +1,113 @@
1
1
  import { normalizeHueDegrees, oklchToHex, parseHexToOklch } from './internal-culori';
2
- export const COLOR_HARMONIES = [
3
- 'monochromatic',
4
- 'analogous',
5
- 'complementary',
6
- 'triadic',
7
- 'tetradic',
8
- 'splitComplementary',
2
+ export const COLOR_HARMONY_CATALOG = [
3
+ {
4
+ id: 'monochromatic',
5
+ label: 'Monochromatic',
6
+ description: 'Uses one hue with ramp variation.',
7
+ offsets: [0],
8
+ roles: ['primary'],
9
+ roleCount: 1,
10
+ },
11
+ {
12
+ id: 'analogous',
13
+ label: 'Analogous',
14
+ description: 'Uses the primary hue and its two neighboring hues at minus and plus 30 degrees.',
15
+ offsets: [0, -30, 30],
16
+ roles: ['primary', 'secondary', 'tertiary'],
17
+ roleCount: 3,
18
+ },
19
+ {
20
+ id: 'complementary',
21
+ label: 'Complementary',
22
+ description: 'Pairs the primary hue with its opposite hue at 180 degrees.',
23
+ offsets: [0, 180],
24
+ roles: ['primary', 'secondary'],
25
+ roleCount: 2,
26
+ },
27
+ {
28
+ id: 'splitComplementary',
29
+ label: 'Split complementary',
30
+ description: 'Uses the primary hue with hues at 150 and 210 degrees.',
31
+ offsets: [0, 150, 210],
32
+ roles: ['primary', 'secondary', 'tertiary'],
33
+ roleCount: 3,
34
+ },
35
+ {
36
+ id: 'triadic',
37
+ label: 'Triadic',
38
+ description: 'Uses three evenly spaced hues at 120-degree intervals.',
39
+ offsets: [0, 120, 240],
40
+ roles: ['primary', 'secondary', 'tertiary'],
41
+ roleCount: 3,
42
+ },
43
+ {
44
+ id: 'tetradic',
45
+ label: 'Tetradic',
46
+ description: 'Uses a rectangular tetrad at 0, 60, 180, and 240 degrees.',
47
+ offsets: [0, 60, 180, 240],
48
+ roles: ['primary', 'secondary', 'tertiary', 'quaternary'],
49
+ roleCount: 4,
50
+ },
51
+ {
52
+ id: 'square',
53
+ label: 'Square',
54
+ description: 'Uses four evenly spaced hues at 90-degree intervals.',
55
+ offsets: [0, 90, 180, 270],
56
+ roles: ['primary', 'secondary', 'tertiary', 'quaternary'],
57
+ roleCount: 4,
58
+ },
9
59
  ];
10
- const ROLE_ORDER_BY_HARMONY = new Map([
11
- ['monochromatic', ['primary']],
12
- ['complementary', ['primary', 'secondary']],
13
- ['analogous', ['primary', 'secondary', 'tertiary']],
14
- ['splitComplementary', ['primary', 'secondary', 'tertiary']],
15
- ['triadic', ['primary', 'secondary', 'tertiary']],
16
- ['tetradic', ['primary', 'secondary', 'tertiary', 'quaternary']],
17
- ]);
18
- const OFFSETS_BY_HARMONY = new Map([
19
- ['monochromatic', [0]],
20
- ['analogous', [0, -30, 30]],
21
- ['complementary', [0, 180]],
22
- ['splitComplementary', [0, 150, 210]],
23
- ['triadic', [0, 120, 240]],
24
- ['tetradic', [0, 90, 180, 270]],
25
- ]);
60
+ export const COLOR_HARMONIES = COLOR_HARMONY_CATALOG.map(({ id }) => id);
61
+ export const MIN_HUEFUL_CHROMA = 0.015;
26
62
  /***
27
- Resolve a complete harmony definition from the canonical maps.
63
+ Create diagnostics for the reliability of generated hue relationships.
64
+ */
65
+ function createHarmonyGenerationDiagnostics(primaryChroma) {
66
+ const warnings = [];
67
+ if (primaryChroma === 0) {
68
+ warnings.push({
69
+ code: 'achromatic_primary',
70
+ message: 'The selected primary is achromatic, so generated hue relationships are not visible.',
71
+ });
72
+ }
73
+ else if (primaryChroma < MIN_HUEFUL_CHROMA) {
74
+ warnings.push({
75
+ code: 'low_chroma_primary',
76
+ message: 'The selected primary has low chroma, so generated hue relationships may be weak.',
77
+ });
78
+ }
79
+ return {
80
+ isHueReliable: primaryChroma >= MIN_HUEFUL_CHROMA,
81
+ primaryChroma,
82
+ warnings,
83
+ };
84
+ }
85
+ /***
86
+ Resolve a complete harmony definition from the canonical public catalog.
28
87
  */
29
88
  function getHarmonyDefinition(harmony) {
30
- const roles = ROLE_ORDER_BY_HARMONY.get(harmony);
31
- const offsets = OFFSETS_BY_HARMONY.get(harmony);
32
- if (!roles || !offsets) {
89
+ const definition = COLOR_HARMONY_CATALOG.find(({ id }) => id === harmony);
90
+ if (!definition) {
33
91
  throw new Error(`[color-theory] Missing definition for harmony ${harmony}.`);
34
92
  }
35
- return { offsets, roles };
93
+ return definition;
36
94
  }
37
95
  /***
38
96
  Generate role-based harmony colors from a primary color and harmony strategy.
39
97
  */
40
98
  export function generateHarmonyRoleColors(primaryColor, harmony) {
41
99
  const base = parseHexToOklch(primaryColor);
42
- const { offsets, roles } = getHarmonyDefinition(harmony);
100
+ const definition = getHarmonyDefinition(harmony);
43
101
  const baseHue = normalizeHueDegrees(base.h);
44
- const colors = [];
45
- for (let index = 0; index < roles.length; index++) {
46
- const role = roles.at(index);
47
- if (!role)
48
- continue;
49
- const hueDegrees = normalizeHueDegrees(baseHue + (offsets.at(index) ?? 0));
50
- colors.push({
102
+ const colors = definition.roles.map((role, index) => {
103
+ const hueDegrees = normalizeHueDegrees(baseHue + (definition.offsets.at(index) ?? 0));
104
+ return {
51
105
  role,
52
106
  hex: role === 'primary' ? primaryColor : oklchToHex({ ...base, h: hueDegrees }),
53
107
  hueDegrees,
54
108
  source: role === 'primary' ? 'selected' : 'generated',
55
- });
56
- }
109
+ };
110
+ });
57
111
  const primary = colors.find((color) => color.role === 'primary');
58
112
  if (!primary) {
59
113
  throw new Error('[color-theory] Expected generated harmony role colors to include primary.');
@@ -68,6 +122,7 @@ export function generateHarmonyRoleColors(primaryColor, harmony) {
68
122
  ...(secondary ? { secondary } : {}),
69
123
  ...(tertiary ? { tertiary } : {}),
70
124
  ...(quaternary ? { quaternary } : {}),
125
+ diagnostics: createHarmonyGenerationDiagnostics(base.c),
71
126
  };
72
127
  }
73
128
  //# sourceMappingURL=harmony.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"harmony.js","sourceRoot":"","sources":["../src/harmony.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HexColor } from './hex';\nimport { normalizeHueDegrees, oklchToHex, parseHexToOklch } from './internal-culori';\n\nexport const COLOR_HARMONIES = [\n 'monochromatic',\n 'analogous',\n 'complementary',\n 'triadic',\n 'tetradic',\n 'splitComplementary',\n] as const;\n\nexport type ColorHarmony = (typeof COLOR_HARMONIES)[number];\n\nexport type GeneratedColorRole = 'primary' | 'secondary' | 'tertiary' | 'quaternary';\n\nexport interface GeneratedHarmonyRoleColor {\n role: GeneratedColorRole;\n hex: HexColor;\n hueDegrees: number;\n source: 'selected' | 'generated';\n}\n\nexport interface GeneratedHarmonyRoleColors {\n harmony: ColorHarmony;\n colors: readonly GeneratedHarmonyRoleColor[];\n primary: GeneratedHarmonyRoleColor;\n secondary?: GeneratedHarmonyRoleColor;\n tertiary?: GeneratedHarmonyRoleColor;\n quaternary?: GeneratedHarmonyRoleColor;\n}\n\nconst ROLE_ORDER_BY_HARMONY = new Map<ColorHarmony, readonly GeneratedColorRole[]>([\n ['monochromatic', ['primary']],\n ['complementary', ['primary', 'secondary']],\n ['analogous', ['primary', 'secondary', 'tertiary']],\n ['splitComplementary', ['primary', 'secondary', 'tertiary']],\n ['triadic', ['primary', 'secondary', 'tertiary']],\n ['tetradic', ['primary', 'secondary', 'tertiary', 'quaternary']],\n]);\n\nconst OFFSETS_BY_HARMONY = new Map<ColorHarmony, readonly number[]>([\n ['monochromatic', [0]],\n ['analogous', [0, -30, 30]],\n ['complementary', [0, 180]],\n ['splitComplementary', [0, 150, 210]],\n ['triadic', [0, 120, 240]],\n ['tetradic', [0, 90, 180, 270]],\n]);\n\n/***\n Resolve a complete harmony definition from the canonical maps.\n*/\nfunction getHarmonyDefinition(harmony: ColorHarmony): {\n readonly offsets: readonly number[];\n readonly roles: readonly GeneratedColorRole[];\n} {\n const roles = ROLE_ORDER_BY_HARMONY.get(harmony);\n const offsets = OFFSETS_BY_HARMONY.get(harmony);\n if (!roles || !offsets) {\n throw new Error(`[color-theory] Missing definition for harmony ${harmony}.`);\n }\n return { offsets, roles };\n}\n\n/***\n Generate role-based harmony colors from a primary color and harmony strategy.\n*/\nexport function generateHarmonyRoleColors(\n primaryColor: HexColor,\n harmony: ColorHarmony,\n): GeneratedHarmonyRoleColors {\n const base = parseHexToOklch(primaryColor);\n const { offsets, roles } = getHarmonyDefinition(harmony);\n const baseHue = normalizeHueDegrees(base.h);\n\n const colors: GeneratedHarmonyRoleColor[] = [];\n\n for (let index = 0; index < roles.length; index++) {\n const role = roles.at(index);\n if (!role) continue;\n\n const hueDegrees = normalizeHueDegrees(baseHue + (offsets.at(index) ?? 0));\n colors.push({\n role,\n hex: role === 'primary' ? primaryColor : oklchToHex({ ...base, h: hueDegrees }),\n hueDegrees,\n source: role === 'primary' ? 'selected' : 'generated',\n });\n }\n\n const primary = colors.find((color) => color.role === 'primary');\n if (!primary) {\n throw new Error('[color-theory] Expected generated harmony role colors to include primary.');\n }\n\n const secondary = colors.find((color) => color.role === 'secondary');\n const tertiary = colors.find((color) => color.role === 'tertiary');\n const quaternary = colors.find((color) => color.role === 'quaternary');\n\n return {\n harmony,\n colors,\n primary,\n ...(secondary ? { secondary } : {}),\n ...(tertiary ? { tertiary } : {}),\n ...(quaternary ? { quaternary } : {}),\n };\n}\n"]}
1
+ {"version":3,"file":"harmony.js","sourceRoot":"","sources":["../src/harmony.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HexColor } from './hex';\nimport { normalizeHueDegrees, oklchToHex, parseHexToOklch } from './internal-culori';\n\nexport type GeneratedColorRole = 'primary' | 'secondary' | 'tertiary' | 'quaternary';\n\ninterface ColorHarmonyCatalogEntry {\n readonly id: string;\n readonly label: string;\n readonly description: string;\n readonly offsets: readonly number[];\n readonly roles: readonly GeneratedColorRole[];\n readonly roleCount: 1 | 2 | 3 | 4;\n}\n\nexport const COLOR_HARMONY_CATALOG = [\n {\n id: 'monochromatic',\n label: 'Monochromatic',\n description: 'Uses one hue with ramp variation.',\n offsets: [0],\n roles: ['primary'],\n roleCount: 1,\n },\n {\n id: 'analogous',\n label: 'Analogous',\n description: 'Uses the primary hue and its two neighboring hues at minus and plus 30 degrees.',\n offsets: [0, -30, 30],\n roles: ['primary', 'secondary', 'tertiary'],\n roleCount: 3,\n },\n {\n id: 'complementary',\n label: 'Complementary',\n description: 'Pairs the primary hue with its opposite hue at 180 degrees.',\n offsets: [0, 180],\n roles: ['primary', 'secondary'],\n roleCount: 2,\n },\n {\n id: 'splitComplementary',\n label: 'Split complementary',\n description: 'Uses the primary hue with hues at 150 and 210 degrees.',\n offsets: [0, 150, 210],\n roles: ['primary', 'secondary', 'tertiary'],\n roleCount: 3,\n },\n {\n id: 'triadic',\n label: 'Triadic',\n description: 'Uses three evenly spaced hues at 120-degree intervals.',\n offsets: [0, 120, 240],\n roles: ['primary', 'secondary', 'tertiary'],\n roleCount: 3,\n },\n {\n id: 'tetradic',\n label: 'Tetradic',\n description: 'Uses a rectangular tetrad at 0, 60, 180, and 240 degrees.',\n offsets: [0, 60, 180, 240],\n roles: ['primary', 'secondary', 'tertiary', 'quaternary'],\n roleCount: 4,\n },\n {\n id: 'square',\n label: 'Square',\n description: 'Uses four evenly spaced hues at 90-degree intervals.',\n offsets: [0, 90, 180, 270],\n roles: ['primary', 'secondary', 'tertiary', 'quaternary'],\n roleCount: 4,\n },\n] as const satisfies readonly ColorHarmonyCatalogEntry[];\n\nexport type ColorHarmonyDefinition = (typeof COLOR_HARMONY_CATALOG)[number];\nexport type ColorHarmony = ColorHarmonyDefinition['id'];\n\nexport const COLOR_HARMONIES: readonly ColorHarmony[] = COLOR_HARMONY_CATALOG.map(({ id }) => id);\n\nexport const MIN_HUEFUL_CHROMA = 0.015;\n\nexport type HarmonyGenerationWarningCode = 'achromatic_primary' | 'low_chroma_primary';\n\nexport interface HarmonyGenerationWarning {\n readonly code: HarmonyGenerationWarningCode;\n readonly message: string;\n}\n\nexport interface HarmonyGenerationDiagnostics {\n readonly isHueReliable: boolean;\n readonly primaryChroma: number;\n readonly warnings: readonly HarmonyGenerationWarning[];\n}\n\nexport interface GeneratedHarmonyRoleColor {\n role: GeneratedColorRole;\n hex: HexColor;\n hueDegrees: number;\n source: 'selected' | 'generated';\n}\n\nexport interface GeneratedHarmonyRoleColors {\n harmony: ColorHarmony;\n colors: readonly GeneratedHarmonyRoleColor[];\n primary: GeneratedHarmonyRoleColor;\n secondary?: GeneratedHarmonyRoleColor;\n tertiary?: GeneratedHarmonyRoleColor;\n quaternary?: GeneratedHarmonyRoleColor;\n diagnostics: HarmonyGenerationDiagnostics;\n}\n\n/***\n Create diagnostics for the reliability of generated hue relationships.\n*/\nfunction createHarmonyGenerationDiagnostics(primaryChroma: number): HarmonyGenerationDiagnostics {\n const warnings: HarmonyGenerationWarning[] = [];\n if (primaryChroma === 0) {\n warnings.push({\n code: 'achromatic_primary',\n message:\n 'The selected primary is achromatic, so generated hue relationships are not visible.',\n });\n } else if (primaryChroma < MIN_HUEFUL_CHROMA) {\n warnings.push({\n code: 'low_chroma_primary',\n message: 'The selected primary has low chroma, so generated hue relationships may be weak.',\n });\n }\n return {\n isHueReliable: primaryChroma >= MIN_HUEFUL_CHROMA,\n primaryChroma,\n warnings,\n };\n}\n\n/***\n Resolve a complete harmony definition from the canonical public catalog.\n*/\nfunction getHarmonyDefinition(harmony: ColorHarmony): ColorHarmonyDefinition {\n const definition = COLOR_HARMONY_CATALOG.find(({ id }) => id === harmony);\n if (!definition) {\n throw new Error(`[color-theory] Missing definition for harmony ${harmony}.`);\n }\n return definition;\n}\n\n/***\n Generate role-based harmony colors from a primary color and harmony strategy.\n*/\nexport function generateHarmonyRoleColors(\n primaryColor: HexColor,\n harmony: ColorHarmony,\n): GeneratedHarmonyRoleColors {\n const base = parseHexToOklch(primaryColor);\n const definition = getHarmonyDefinition(harmony);\n const baseHue = normalizeHueDegrees(base.h);\n\n const colors = definition.roles.map((role, index): GeneratedHarmonyRoleColor => {\n const hueDegrees = normalizeHueDegrees(baseHue + (definition.offsets.at(index) ?? 0));\n return {\n role,\n hex: role === 'primary' ? primaryColor : oklchToHex({ ...base, h: hueDegrees }),\n hueDegrees,\n source: role === 'primary' ? 'selected' : 'generated',\n };\n });\n\n const primary = colors.find((color) => color.role === 'primary');\n if (!primary) {\n throw new Error('[color-theory] Expected generated harmony role colors to include primary.');\n }\n\n const secondary = colors.find((color) => color.role === 'secondary');\n const tertiary = colors.find((color) => color.role === 'tertiary');\n const quaternary = colors.find((color) => color.role === 'quaternary');\n\n return {\n harmony,\n colors,\n primary,\n ...(secondary ? { secondary } : {}),\n ...(tertiary ? { tertiary } : {}),\n ...(quaternary ? { quaternary } : {}),\n diagnostics: createHarmonyGenerationDiagnostics(base.c),\n };\n}\n"]}
package/dist/neutral.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import type { GeneratedHarmonyRoleColors } from './harmony';
1
+ import { type GeneratedHarmonyRoleColors } from './harmony';
2
2
  import type { HexColor } from './hex';
3
3
  import { type ColorSwatch, type ColorSwatchDiagnostics } from './swatches';
4
- export declare const MIN_HUEFUL_CHROMA = 0.015;
5
4
  export interface NeutralSwatchResult {
6
5
  neutralKeyColor: HexColor;
7
6
  neutral: ColorSwatch;
@@ -1 +1 @@
1
- {"version":3,"file":"neutral.d.ts","sourceRoot":"","sources":["../src/neutral.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAC1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAE5B,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,QAAQ,CAAC;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,sBAAsB,CAAC;CACrC;AAED,MAAM,WAAW,wBAAwB;IACvC,eAAe,EAAE,QAAQ,CAAC;IAC1B,WAAW,EAAE,sBAAsB,CAAC;CACrC;AAwBD;;EAEE;AACF,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,0BAA0B,GAAG,mBAAmB,CAuBjG"}
1
+ {"version":3,"file":"neutral.d.ts","sourceRoot":"","sources":["../src/neutral.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,0BAA0B,EAAqB,MAAM,WAAW,CAAC;AAClG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAE5B,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,QAAQ,CAAC;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,sBAAsB,CAAC;CACrC;AAED,MAAM,WAAW,wBAAwB;IACvC,eAAe,EAAE,QAAQ,CAAC;IAC1B,WAAW,EAAE,sBAAsB,CAAC;CACrC;AAwBD;;EAEE;AACF,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,0BAA0B,GAAG,mBAAmB,CAuBjG"}
package/dist/neutral.js CHANGED
@@ -1,6 +1,6 @@
1
+ import { MIN_HUEFUL_CHROMA } from './harmony';
1
2
  import { normalizeHueDegrees, oklchToHex, parseHexToOklch } from './internal-culori';
2
3
  import { COLOR_SWATCH_BASE_LIGHTNESS, generateColorSwatch, } from './swatches';
3
- export const MIN_HUEFUL_CHROMA = 0.015;
4
4
  /***
5
5
  Pick the harmony color that should tint the generated neutral swatch.
6
6
  */
@@ -1 +1 @@
1
- {"version":3,"file":"neutral.js","sourceRoot":"","sources":["../src/neutral.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAG3B,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAavC;;EAEE;AACF,SAAS,iBAAiB,CACxB,UAAsC,EACtC,OAAqB;IAErB,IAAI,OAAO,KAAK,eAAe;QAAE,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC;IAC/D,IAAI,OAAO,KAAK,eAAe,EAAE,CAAC;QAChC,OAAO,UAAU,CAAC,SAAS,EAAE,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC;IAC7D,CAAC;IACD,OAAO,UAAU,CAAC,QAAQ,EAAE,GAAG,IAAI,UAAU,CAAC,SAAS,EAAE,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC;AACzF,CAAC;AAED;;EAEE;AACF,SAAS,WAAW,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;IAC1D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,qBAAqB,CAAC,UAAsC;IAC1E,MAAM,aAAa,GAAG,iBAAiB,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACxE,MAAM,UAAU,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC;IAElD,MAAM,OAAO,GAAG,eAAe,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAExD,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,GAAG,iBAAiB,CAAC;IAEpD,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC9E,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACvF,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,aAAa,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAEtF,MAAM,eAAe,GAAG,UAAU,CAAC;QACjC,IAAI,EAAE,OAAO;QACb,CAAC,EAAE,2BAA2B;QAC9B,CAAC,EAAE,aAAa;QAChB,CAAC,EAAE,UAAU;KACd,CAAC,CAAC;IAEH,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,mBAAmB,CAAC,eAAe,CAAC,CAAC;IAErE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AAC3D,CAAC","sourcesContent":["import type { ColorHarmony, GeneratedHarmonyRoleColors } from './harmony';\nimport type { HexColor } from './hex';\nimport { normalizeHueDegrees, oklchToHex, parseHexToOklch } from './internal-culori';\nimport {\n COLOR_SWATCH_BASE_LIGHTNESS,\n type ColorSwatch,\n type ColorSwatchDiagnostics,\n generateColorSwatch,\n} from './swatches';\n\nexport const MIN_HUEFUL_CHROMA = 0.015;\n\nexport interface NeutralSwatchResult {\n neutralKeyColor: HexColor;\n neutral: ColorSwatch;\n diagnostics: ColorSwatchDiagnostics;\n}\n\nexport interface GeneratedNeutralMetadata {\n neutralKeyColor: HexColor;\n diagnostics: ColorSwatchDiagnostics;\n}\n\n/***\n Pick the harmony color that should tint the generated neutral swatch.\n*/\nfunction pickTintSourceHex(\n roleColors: GeneratedHarmonyRoleColors,\n harmony: ColorHarmony,\n): HexColor {\n if (harmony === 'monochromatic') return roleColors.primary.hex;\n if (harmony === 'complementary') {\n return roleColors.secondary?.hex ?? roleColors.primary.hex;\n }\n return roleColors.tertiary?.hex ?? roleColors.secondary?.hex ?? roleColors.primary.hex;\n}\n\n/***\n Clamp a number to a finite minimum and maximum range.\n*/\nfunction clampNumber(value: number, min: number, max: number): number {\n if (!Number.isFinite(value)) return min;\n return Math.min(max, Math.max(min, value));\n}\n\n/***\n Generate a softly tinted neutral swatch from generated harmony role colors.\n*/\nexport function generateNeutralSwatch(roleColors: GeneratedHarmonyRoleColors): NeutralSwatchResult {\n const tintSourceHex = pickTintSourceHex(roleColors, roleColors.harmony);\n const tintSource = parseHexToOklch(tintSourceHex);\n\n const primary = parseHexToOklch(roleColors.primary.hex);\n\n const preferGray = tintSource.c < MIN_HUEFUL_CHROMA;\n\n const hue = roleColors.harmony === 'monochromatic' ? primary.h : tintSource.h;\n const sourceChroma = roleColors.harmony === 'monochromatic' ? primary.c : tintSource.c;\n const neutralHue = preferGray ? 0 : normalizeHueDegrees(hue);\n const neutralChroma = preferGray ? 0 : clampNumber(sourceChroma * 0.06, 0.004, 0.012);\n\n const neutralKeyColor = oklchToHex({\n mode: 'oklch',\n l: COLOR_SWATCH_BASE_LIGHTNESS,\n c: neutralChroma,\n h: neutralHue,\n });\n\n const { swatch, diagnostics } = generateColorSwatch(neutralKeyColor);\n\n return { neutralKeyColor, neutral: swatch, diagnostics };\n}\n"]}
1
+ {"version":3,"file":"neutral.js","sourceRoot":"","sources":["../src/neutral.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsD,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAElG,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAG3B,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAapB;;EAEE;AACF,SAAS,iBAAiB,CACxB,UAAsC,EACtC,OAAqB;IAErB,IAAI,OAAO,KAAK,eAAe;QAAE,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC;IAC/D,IAAI,OAAO,KAAK,eAAe,EAAE,CAAC;QAChC,OAAO,UAAU,CAAC,SAAS,EAAE,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC;IAC7D,CAAC;IACD,OAAO,UAAU,CAAC,QAAQ,EAAE,GAAG,IAAI,UAAU,CAAC,SAAS,EAAE,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC;AACzF,CAAC;AAED;;EAEE;AACF,SAAS,WAAW,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;IAC1D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,qBAAqB,CAAC,UAAsC;IAC1E,MAAM,aAAa,GAAG,iBAAiB,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACxE,MAAM,UAAU,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC;IAElD,MAAM,OAAO,GAAG,eAAe,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAExD,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,GAAG,iBAAiB,CAAC;IAEpD,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC9E,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACvF,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,aAAa,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAEtF,MAAM,eAAe,GAAG,UAAU,CAAC;QACjC,IAAI,EAAE,OAAO;QACb,CAAC,EAAE,2BAA2B;QAC9B,CAAC,EAAE,aAAa;QAChB,CAAC,EAAE,UAAU;KACd,CAAC,CAAC;IAEH,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,mBAAmB,CAAC,eAAe,CAAC,CAAC;IAErE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AAC3D,CAAC","sourcesContent":["import { type ColorHarmony, type GeneratedHarmonyRoleColors, MIN_HUEFUL_CHROMA } from './harmony';\nimport type { HexColor } from './hex';\nimport { normalizeHueDegrees, oklchToHex, parseHexToOklch } from './internal-culori';\nimport {\n COLOR_SWATCH_BASE_LIGHTNESS,\n type ColorSwatch,\n type ColorSwatchDiagnostics,\n generateColorSwatch,\n} from './swatches';\n\nexport interface NeutralSwatchResult {\n neutralKeyColor: HexColor;\n neutral: ColorSwatch;\n diagnostics: ColorSwatchDiagnostics;\n}\n\nexport interface GeneratedNeutralMetadata {\n neutralKeyColor: HexColor;\n diagnostics: ColorSwatchDiagnostics;\n}\n\n/***\n Pick the harmony color that should tint the generated neutral swatch.\n*/\nfunction pickTintSourceHex(\n roleColors: GeneratedHarmonyRoleColors,\n harmony: ColorHarmony,\n): HexColor {\n if (harmony === 'monochromatic') return roleColors.primary.hex;\n if (harmony === 'complementary') {\n return roleColors.secondary?.hex ?? roleColors.primary.hex;\n }\n return roleColors.tertiary?.hex ?? roleColors.secondary?.hex ?? roleColors.primary.hex;\n}\n\n/***\n Clamp a number to a finite minimum and maximum range.\n*/\nfunction clampNumber(value: number, min: number, max: number): number {\n if (!Number.isFinite(value)) return min;\n return Math.min(max, Math.max(min, value));\n}\n\n/***\n Generate a softly tinted neutral swatch from generated harmony role colors.\n*/\nexport function generateNeutralSwatch(roleColors: GeneratedHarmonyRoleColors): NeutralSwatchResult {\n const tintSourceHex = pickTintSourceHex(roleColors, roleColors.harmony);\n const tintSource = parseHexToOklch(tintSourceHex);\n\n const primary = parseHexToOklch(roleColors.primary.hex);\n\n const preferGray = tintSource.c < MIN_HUEFUL_CHROMA;\n\n const hue = roleColors.harmony === 'monochromatic' ? primary.h : tintSource.h;\n const sourceChroma = roleColors.harmony === 'monochromatic' ? primary.c : tintSource.c;\n const neutralHue = preferGray ? 0 : normalizeHueDegrees(hue);\n const neutralChroma = preferGray ? 0 : clampNumber(sourceChroma * 0.06, 0.004, 0.012);\n\n const neutralKeyColor = oklchToHex({\n mode: 'oklch',\n l: COLOR_SWATCH_BASE_LIGHTNESS,\n c: neutralChroma,\n h: neutralHue,\n });\n\n const { swatch, diagnostics } = generateColorSwatch(neutralKeyColor);\n\n return { neutralKeyColor, neutral: swatch, diagnostics };\n}\n"]}
@@ -2,6 +2,7 @@ import type { HexColor } from './hex';
2
2
  import type { ColorSwatch, ColorSwatchDiagnostics } from './swatches';
3
3
  export declare const DEFAULT_SEMANTIC_STATUS_COLOR_SEEDS: {
4
4
  readonly danger: "#ef4444";
5
+ readonly info: "#3b82f6";
5
6
  readonly success: "#22c55e";
6
7
  readonly warning: "#f59e0b";
7
8
  };
@@ -1 +1 @@
1
- {"version":3,"file":"semantic-status.d.ts","sourceRoot":"","sources":["../src/semantic-status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,KAAK,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGtE,eAAO,MAAM,mCAAmC;;;;CAIL,CAAC;AAE5C,MAAM,MAAM,yBAAyB,GAAG,MAAM,OAAO,mCAAmC,CAAC;AAEzF,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAExD,MAAM,WAAW,sBAAsB,CAAC,IAAI,SAAS,MAAM;IACzD,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;CACnD;AAmCD;;EAEE;AACF,wBAAgB,4BAA4B,CAAC,IAAI,SAAS,MAAM,EAC9D,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,uBAAuB,CAAC,GAC3C,sBAAsB,CAAC,IAAI,CAAC,CAS9B;AAED;;EAEE;AACF,wBAAgB,mCAAmC,IAAI,sBAAsB,CAAC,yBAAyB,CAAC,CAEvG"}
1
+ {"version":3,"file":"semantic-status.d.ts","sourceRoot":"","sources":["../src/semantic-status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,KAAK,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGtE,eAAO,MAAM,mCAAmC;;;;;CAKL,CAAC;AAE5C,MAAM,MAAM,yBAAyB,GAAG,MAAM,OAAO,mCAAmC,CAAC;AAEzF,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAExD,MAAM,WAAW,sBAAsB,CAAC,IAAI,SAAS,MAAM;IACzD,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;CACnD;AAmCD;;EAEE;AACF,wBAAgB,4BAA4B,CAAC,IAAI,SAAS,MAAM,EAC9D,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,uBAAuB,CAAC,GAC3C,sBAAsB,CAAC,IAAI,CAAC,CAS9B;AAED;;EAEE;AACF,wBAAgB,mCAAmC,IAAI,sBAAsB,CAAC,yBAAyB,CAAC,CAEvG"}
@@ -2,6 +2,7 @@ import { parseHexColorOrThrow } from './hex';
2
2
  import { generateColorSwatch } from './swatches';
3
3
  export const DEFAULT_SEMANTIC_STATUS_COLOR_SEEDS = {
4
4
  danger: '#ef4444',
5
+ info: '#3b82f6',
5
6
  success: '#22c55e',
6
7
  warning: '#f59e0b',
7
8
  };
@@ -1 +1 @@
1
- {"version":3,"file":"semantic-status.js","sourceRoot":"","sources":["../src/semantic-status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,CAAC,MAAM,mCAAmC,GAAG;IACjD,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;CACuB,CAAC;AAmB5C;;EAEE;AACF,SAAS,gBAAgB,CACvB,OAA4C;IAE5C,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAwB,CAAC;AAC5D,CAAC;AAED;;EAEE;AACF,SAAS,sBAAsB,CAC7B,IAAU,EACV,IAA6B;IAE7B,MAAM,OAAO,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7E,MAAM,SAAS,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC/C,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,SAAS,CAAC,MAAM;QACxB,WAAW,EAAE,SAAS,CAAC,WAAW;KACnC,CAAC;AACJ,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,4BAA4B,CAC1C,KAA4C;IAE5C,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAC3D,sBAAsB,CAAC,IAAY,EAAE,IAA+B,CAAC,CACtE,CAAC;IACF,OAAO;QACL,KAAK,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QACxE,QAAQ,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAC/E,WAAW,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;KAC7F,CAAC;AACJ,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,mCAAmC;IACjD,OAAO,4BAA4B,CAAC,mCAAmC,CAAC,CAAC;AAC3E,CAAC","sourcesContent":["import type { HexColor } from './hex';\nimport { parseHexColorOrThrow } from './hex';\nimport type { ColorSwatch, ColorSwatchDiagnostics } from './swatches';\nimport { generateColorSwatch } from './swatches';\n\nexport const DEFAULT_SEMANTIC_STATUS_COLOR_SEEDS = {\n danger: '#ef4444',\n success: '#22c55e',\n warning: '#f59e0b',\n} as const satisfies Record<string, string>;\n\nexport type DefaultSemanticStatusRole = keyof typeof DEFAULT_SEMANTIC_STATUS_COLOR_SEEDS;\n\nexport type SemanticStatusSeedInput = string | HexColor;\n\nexport interface SemanticStatusSwatches<Role extends string> {\n seeds: Record<Role, HexColor>;\n swatches: Record<Role, ColorSwatch>;\n diagnostics: Record<Role, ColorSwatchDiagnostics>;\n}\n\ninterface GeneratedSemanticStatus<Role extends string> {\n readonly diagnostics: ColorSwatchDiagnostics;\n readonly role: Role;\n readonly seed: HexColor;\n readonly swatch: ColorSwatch;\n}\n\n/***\n Convert typed entries into a role-keyed immutable result record.\n*/\nfunction createRoleRecord<Role extends string, Value>(\n entries: readonly (readonly [Role, Value])[],\n): Record<Role, Value> {\n return Object.fromEntries(entries) as Record<Role, Value>;\n}\n\n/***\n Normalize and generate one semantic status role.\n*/\nfunction generateSemanticStatus<Role extends string>(\n role: Role,\n seed: SemanticStatusSeedInput,\n): GeneratedSemanticStatus<Role> {\n const seedHex = typeof seed === 'string' ? parseHexColorOrThrow(seed) : seed;\n const generated = generateColorSwatch(seedHex);\n return {\n role,\n seed: seedHex,\n swatch: generated.swatch,\n diagnostics: generated.diagnostics,\n };\n}\n\n/***\n Generate semantic status swatches from a role-to-seed record.\n*/\nexport function createSemanticStatusSwatches<Role extends string>(\n seeds: Record<Role, SemanticStatusSeedInput>,\n): SemanticStatusSwatches<Role> {\n const generated = Object.entries(seeds).map(([role, seed]) =>\n generateSemanticStatus(role as Role, seed as SemanticStatusSeedInput),\n );\n return {\n seeds: createRoleRecord(generated.map(({ role, seed }) => [role, seed])),\n swatches: createRoleRecord(generated.map(({ role, swatch }) => [role, swatch])),\n diagnostics: createRoleRecord(generated.map(({ diagnostics, role }) => [role, diagnostics])),\n };\n}\n\n/***\n Generate the canonical default semantic status swatches.\n*/\nexport function createDefaultSemanticStatusSwatches(): SemanticStatusSwatches<DefaultSemanticStatusRole> {\n return createSemanticStatusSwatches(DEFAULT_SEMANTIC_STATUS_COLOR_SEEDS);\n}\n"]}
1
+ {"version":3,"file":"semantic-status.js","sourceRoot":"","sources":["../src/semantic-status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,CAAC,MAAM,mCAAmC,GAAG;IACjD,MAAM,EAAE,SAAS;IACjB,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;CACuB,CAAC;AAmB5C;;EAEE;AACF,SAAS,gBAAgB,CACvB,OAA4C;IAE5C,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAwB,CAAC;AAC5D,CAAC;AAED;;EAEE;AACF,SAAS,sBAAsB,CAC7B,IAAU,EACV,IAA6B;IAE7B,MAAM,OAAO,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7E,MAAM,SAAS,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC/C,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,SAAS,CAAC,MAAM;QACxB,WAAW,EAAE,SAAS,CAAC,WAAW;KACnC,CAAC;AACJ,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,4BAA4B,CAC1C,KAA4C;IAE5C,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAC3D,sBAAsB,CAAC,IAAY,EAAE,IAA+B,CAAC,CACtE,CAAC;IACF,OAAO;QACL,KAAK,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QACxE,QAAQ,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAC/E,WAAW,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;KAC7F,CAAC;AACJ,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,mCAAmC;IACjD,OAAO,4BAA4B,CAAC,mCAAmC,CAAC,CAAC;AAC3E,CAAC","sourcesContent":["import type { HexColor } from './hex';\nimport { parseHexColorOrThrow } from './hex';\nimport type { ColorSwatch, ColorSwatchDiagnostics } from './swatches';\nimport { generateColorSwatch } from './swatches';\n\nexport const DEFAULT_SEMANTIC_STATUS_COLOR_SEEDS = {\n danger: '#ef4444',\n info: '#3b82f6',\n success: '#22c55e',\n warning: '#f59e0b',\n} as const satisfies Record<string, string>;\n\nexport type DefaultSemanticStatusRole = keyof typeof DEFAULT_SEMANTIC_STATUS_COLOR_SEEDS;\n\nexport type SemanticStatusSeedInput = string | HexColor;\n\nexport interface SemanticStatusSwatches<Role extends string> {\n seeds: Record<Role, HexColor>;\n swatches: Record<Role, ColorSwatch>;\n diagnostics: Record<Role, ColorSwatchDiagnostics>;\n}\n\ninterface GeneratedSemanticStatus<Role extends string> {\n readonly diagnostics: ColorSwatchDiagnostics;\n readonly role: Role;\n readonly seed: HexColor;\n readonly swatch: ColorSwatch;\n}\n\n/***\n Convert typed entries into a role-keyed immutable result record.\n*/\nfunction createRoleRecord<Role extends string, Value>(\n entries: readonly (readonly [Role, Value])[],\n): Record<Role, Value> {\n return Object.fromEntries(entries) as Record<Role, Value>;\n}\n\n/***\n Normalize and generate one semantic status role.\n*/\nfunction generateSemanticStatus<Role extends string>(\n role: Role,\n seed: SemanticStatusSeedInput,\n): GeneratedSemanticStatus<Role> {\n const seedHex = typeof seed === 'string' ? parseHexColorOrThrow(seed) : seed;\n const generated = generateColorSwatch(seedHex);\n return {\n role,\n seed: seedHex,\n swatch: generated.swatch,\n diagnostics: generated.diagnostics,\n };\n}\n\n/***\n Generate semantic status swatches from a role-to-seed record.\n*/\nexport function createSemanticStatusSwatches<Role extends string>(\n seeds: Record<Role, SemanticStatusSeedInput>,\n): SemanticStatusSwatches<Role> {\n const generated = Object.entries(seeds).map(([role, seed]) =>\n generateSemanticStatus(role as Role, seed as SemanticStatusSeedInput),\n );\n return {\n seeds: createRoleRecord(generated.map(({ role, seed }) => [role, seed])),\n swatches: createRoleRecord(generated.map(({ role, swatch }) => [role, swatch])),\n diagnostics: createRoleRecord(generated.map(({ diagnostics, role }) => [role, diagnostics])),\n };\n}\n\n/***\n Generate the canonical default semantic status swatches.\n*/\nexport function createDefaultSemanticStatusSwatches(): SemanticStatusSwatches<DefaultSemanticStatusRole> {\n return createSemanticStatusSwatches(DEFAULT_SEMANTIC_STATUS_COLOR_SEEDS);\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/color-theory",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.3.0",
5
5
  "description": "Standalone color theory, harmony, swatch, contrast, and theme color generation utilities.",
6
6
  "homepage": "https://github.com/ankhorage/color-theory#readme",
7
7
  "bugs": {