@instruments/materialgraph 0.4.0 → 0.5.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/client.js CHANGED
@@ -60,6 +60,7 @@ export class MaterialGraphClient {
60
60
  });
61
61
  this.colours = new ColoursClient({
62
62
  get: (operationId, query, signal) => this.#get(operationId, query, signal),
63
+ post: (operationId, body, signal) => this.#post(operationId, body, signal),
63
64
  });
64
65
  }
65
66
  get lastRateLimit() {
@@ -1,26 +1,31 @@
1
1
  /**
2
- * The colour AGGREGATE surface, namespaced as `mg.colours`.
2
+ * The colour surface, namespaced as `mg.colours` — the aggregate reads
3
+ * (`stats`, `profiles`) plus MULTI-COLOUR search (`palette`, `gradient`).
3
4
  *
4
- * Two reads that answer the same question at two altitudes — "how much colour
5
- * does the graph hold at all" (`stats`) and "what colours does this category
6
- * come in" (`profiles`) — so they live together rather than as two more flat
7
- * methods on an already-large client facade. `mg.codes` and `mg.browse` set the
8
- * precedent for a namespace when a capability is a small family rather than a
9
- * single verb.
5
+ * One colour family rather than four more flat methods on an already-large
6
+ * client facade: "how much colour does the graph hold at all" (`stats`), "what
7
+ * colours does this category come in" (`profiles`), "what goes with these
8
+ * colours" (`palette`), and "what moves from this colour to that one"
9
+ * (`gradient`). `mg.codes` and `mg.browse` set the precedent for a namespace
10
+ * when a capability is a family rather than a single verb.
10
11
  *
11
12
  * The namespace holds no transport of its own: the client hands it a bound
12
- * `get`, so auth, rate-limit accounting and error shaping stay in one place.
13
+ * `get`/`post`, so auth, rate-limit accounting and error shaping stay in one
14
+ * place.
13
15
  *
14
- * Both responses carry a `basis`. These are reads of a materialized view that is
15
- * refreshed on a schedule, not on write, so a caller can and should check how
16
- * much of the corpus the answer actually covers before treating it as complete.
16
+ * The two AGGREGATE responses carry a `basis`. Those are reads of a
17
+ * materialized view refreshed on a schedule, not on write, so a caller can and
18
+ * should check how much of the corpus the answer actually covers before
19
+ * treating it as complete. The two SEARCH responses are live and uncached.
17
20
  */
18
21
  import type { CategoryColourProfilesResponse, ColourProfilesParams, ColourStatsResponse } from "./colour-aggregate-types.js";
19
22
  import type { QueryParams } from "./http-transport.js";
20
23
  import type { SdkOperationId } from "./operations.js";
21
- /** The client's own request primitive, handed down so this file owns no transport. */
24
+ import type { SearchGradientInput, SearchGradientResponse, SearchPaletteInput, SearchPaletteResponse } from "./palette-search-types.js";
25
+ /** The client's own request primitives, handed down so this file owns no transport. */
22
26
  export interface ColoursTransport {
23
27
  get<T>(operationId: SdkOperationId, query?: QueryParams, signal?: AbortSignal): Promise<T>;
28
+ post<T>(operationId: SdkOperationId, body: unknown, signal?: AbortSignal): Promise<T>;
24
29
  }
25
30
  export declare class ColoursClient {
26
31
  #private;
@@ -49,4 +54,42 @@ export declare class ColoursClient {
49
54
  * answer, listed rather than silently absent.
50
55
  */
51
56
  stats(signal?: AbortSignal): Promise<ColourStatsResponse>;
57
+ /**
58
+ * Find materials that go with a WHOLE PALETTE — N colours at once, not one
59
+ * colour repeated (POST `/api/v1/search/palettes`).
60
+ *
61
+ * Each colour scores `sim × prop`: `sim` is OKLab closeness on the same
62
+ * mapping `byColour` ranks with, and `prop` rewards a candidate whose own
63
+ * swatch SHARE sits near the `proportion` you asked for. So the same colours
64
+ * at different proportions come back in a different order — a colour at 60%
65
+ * of the surface is a different query from one at 5%. `logic: "all"` (the
66
+ * default) applies a per-colour eligibility floor and returns the weighted
67
+ * geometric mean, so a candidate missing one colour is rejected rather than
68
+ * merely ranked low; `logic: "any"` is a noisy-OR that accepts partial
69
+ * answers but saturates once several colours score well, so prefer `all`
70
+ * when the ORDER is what matters.
71
+ *
72
+ * Every match carries the candidate's full `palette` plus a per-colour
73
+ * receipt naming the swatch that answered each queried colour.
74
+ *
75
+ * @example
76
+ * const { matches } = await mg.colours.palette({
77
+ * colours: [
78
+ * { hex: "F5EFE0", proportion: 0.6 },
79
+ * { hex: "9CAF88", proportion: 0.2 },
80
+ * { hex: "3E3B36", proportion: 0.2 },
81
+ * ],
82
+ * });
83
+ */
84
+ palette(input: SearchPaletteInput, signal?: AbortSignal): Promise<SearchPaletteResponse>;
85
+ /**
86
+ * Find materials that move ALONG a colour transition — "cream to rust", in
87
+ * order (POST `/api/v1/search/gradients`).
88
+ *
89
+ * Not a palette: the score is multiplied by `coverage`, the share of the path
90
+ * the candidate actually spans, so a material owning only one end ranks below
91
+ * one that travels the whole way, and a flat single-colour material is
92
+ * rejected outright however close its hue sits to the path.
93
+ */
94
+ gradient(input: SearchGradientInput, signal?: AbortSignal): Promise<SearchGradientResponse>;
52
95
  }
package/colours-client.js CHANGED
@@ -36,4 +36,46 @@ export class ColoursClient {
36
36
  stats(signal) {
37
37
  return this.#transport.get("get_colour_stats", undefined, signal);
38
38
  }
39
+ /**
40
+ * Find materials that go with a WHOLE PALETTE — N colours at once, not one
41
+ * colour repeated (POST `/api/v1/search/palettes`).
42
+ *
43
+ * Each colour scores `sim × prop`: `sim` is OKLab closeness on the same
44
+ * mapping `byColour` ranks with, and `prop` rewards a candidate whose own
45
+ * swatch SHARE sits near the `proportion` you asked for. So the same colours
46
+ * at different proportions come back in a different order — a colour at 60%
47
+ * of the surface is a different query from one at 5%. `logic: "all"` (the
48
+ * default) applies a per-colour eligibility floor and returns the weighted
49
+ * geometric mean, so a candidate missing one colour is rejected rather than
50
+ * merely ranked low; `logic: "any"` is a noisy-OR that accepts partial
51
+ * answers but saturates once several colours score well, so prefer `all`
52
+ * when the ORDER is what matters.
53
+ *
54
+ * Every match carries the candidate's full `palette` plus a per-colour
55
+ * receipt naming the swatch that answered each queried colour.
56
+ *
57
+ * @example
58
+ * const { matches } = await mg.colours.palette({
59
+ * colours: [
60
+ * { hex: "F5EFE0", proportion: 0.6 },
61
+ * { hex: "9CAF88", proportion: 0.2 },
62
+ * { hex: "3E3B36", proportion: 0.2 },
63
+ * ],
64
+ * });
65
+ */
66
+ palette(input, signal) {
67
+ return this.#transport.post("search_by_palette", input, signal);
68
+ }
69
+ /**
70
+ * Find materials that move ALONG a colour transition — "cream to rust", in
71
+ * order (POST `/api/v1/search/gradients`).
72
+ *
73
+ * Not a palette: the score is multiplied by `coverage`, the share of the path
74
+ * the candidate actually spans, so a material owning only one end ranks below
75
+ * one that travels the whole way, and a flat single-colour material is
76
+ * rejected outright however close its hue sits to the path.
77
+ */
78
+ gradient(input, signal) {
79
+ return this.#transport.post("search_by_gradient", input, signal);
80
+ }
39
81
  }
package/index.d.ts CHANGED
@@ -21,6 +21,7 @@ export type * from "./codes-types.js";
21
21
  export { ColoursClient } from "./colours-client.js";
22
22
  export type { ColoursTransport } from "./colours-client.js";
23
23
  export type * from "./colour-aggregate-types.js";
24
+ export type * from "./palette-search-types.js";
24
25
  export type * from "./brand-types.js";
25
26
  export type * from "./material-asset-types.js";
26
27
  export type * from "./material-block-types.js";
package/operations.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export declare const MATERIALGRAPH_API_VERSION = "0.7.0";
2
- declare const OPERATION_DEFINITIONS: readonly [readonly ["search", "POST", "/api/v1/search", "search_materials_post"], readonly ["searchGet", "GET", "/api/v1/search", "search_materials_get"], readonly ["match", "POST", "/api/v1/search/match", "match_material"], readonly ["byColour", "GET", "/api/v1/search/colours", "search_by_colour"], readonly ["material", "GET", "/api/v1/materials/{variantId}", "get_material"], readonly ["siblings", "GET", "/api/v1/materials/{variantId}/siblings", "get_material_siblings"], readonly ["materialBlocks", "GET", "/api/v1/materials/{variantId}/blocks", "list_material_blocks"], readonly ["materialAssets", "GET", "/api/v1/materials/{variantId}/assets", "list_material_assets"], readonly ["schema", "GET", "/api/v1/schema/blocks", "list_schema_blocks"], readonly ["schema", "GET", "/api/v1/schema/attributes", "list_schema_attributes"], readonly ["schema", "GET", "/api/v1/schema/verticals", "list_schema_verticals"], readonly ["schema", "GET", "/api/v1/schema/product-types", "list_schema_product_types"], readonly ["schema", "GET", "/api/v1/schema/units", "list_schema_units"], readonly ["schema", "GET", "/api/v1/schema/value-dictionaries", "list_schema_value_dictionaries"], readonly ["schema", "GET", "/api/v1/schema/source-requirements", "list_schema_source_requirements"], readonly ["schema", "GET", "/api/v1/schema/product-type-templates", "list_schema_product_type_templates"], readonly ["schema", "GET", "/api/v1/schema/mappings", "list_schema_mappings"], readonly ["pairings", "GET", "/api/v1/materials/{variantId}/pairings", "get_material_pairings"], readonly ["materials", "POST", "/api/v1/materials/batch", "get_materials_batch_post"], readonly ["materialsCached", "GET", "/api/v1/materials/batch", "get_materials_batch_get"], readonly ["browse", "GET", "/api/v1/search/browse", "search_browse_by_colour"], readonly ["colourFacets", "GET", "/api/v1/colours/facets", "get_colour_facets"], readonly ["colours.profiles", "GET", "/api/v1/colours/profiles", "get_category_colour_profiles"], readonly ["colours.stats", "GET", "/api/v1/colours/stats", "get_colour_stats"], readonly ["resolveColour", "GET", "/api/v1/search/colours/resolve", "resolve_colour_name"], readonly ["suggestColour", "GET", "/api/v1/search/colours/suggest", "suggest_colour_names"], readonly ["suggest", "GET", "/api/v1/search/suggest", "search_suggest"], readonly ["resolveAttribute", "GET", "/api/v1/catalog/attributes/resolve", "catalog_resolve_attribute"], readonly ["resolveAttributeValue", "GET", "/api/v1/catalog/attributes/{attributeId}/values/resolve", "catalog_resolve_value"], readonly ["searchVocabulary", "GET", "/api/v1/catalog/vocabulary/search", "catalog_vocabulary_search"], readonly ["bySpec", "GET", "/api/v1/search/specifications", "catalog_by_spec"], readonly ["taxonomy", "GET", "/api/v1/taxonomy/categories", "get_category_taxonomy"], readonly ["whoami", "GET", "/api/v1/whoami", "whoami"], readonly ["coordinate", "POST", "/api/v1/pairings/coordinate", "coordinate_materials"], readonly ["demand", "GET", "/api/v1/demand", "get_material_demand"], readonly ["extractPalette", "POST", "/api/v1/search/palettes/extract", "extract_image_palette"], readonly ["specificationFacets", "GET", "/api/v1/catalog/specifications/facets", "list_spec_facets"], readonly ["attributeValues", "GET", "/api/v1/catalog/attributes/{attributeId}/values", "list_attribute_values"], readonly ["applicableSpecs", "GET", "/api/v1/taxonomy/categories/{key}/specs", "get_applicable_specs"], readonly ["glossary", "GET", "/api/v1/glossary/{term}", "explain_material_term"], readonly ["brands", "GET", "/api/v1/brands", "list_brands"], readonly ["brand", "GET", "/api/v1/brands/{brandSlug}", "get_brand_profile"], readonly ["uploadSourceMaterial", "POST", "/api/v1/source-material", "upload_source_material"], readonly ["sourceMaterialBatch", "GET", "/api/v1/source-material/{batchId}", "get_source_material_batch"], readonly ["capabilities", "GET", "/api/v1/capabilities", "list_public_capabilities"], readonly ["createRun", "POST", "/api/v1/runs", "create_public_run"], readonly ["run", "GET", "/api/v1/runs/{runId}", "get_public_run"], readonly ["runSchedule", "GET", "/api/v1/runs/{runId}/schedule", "get_run_finish_schedule"], readonly ["streamRunEvents", "GET", "/api/v1/runs/{runId}/events", "list_public_run_events"], readonly ["resumeRun", "POST", "/api/v1/runs/{runId}/resume", "resume_public_run"], readonly ["cancelRun", "POST", "/api/v1/runs/{runId}/cancel", "cancel_public_run"], readonly ["varyScheme", "POST", "/api/v1/schemes/vary", "vary_schemes"], readonly ["createProject", "POST", "/api/v1/projects", "create_box_project"], readonly ["listProjects", "GET", "/api/v1/projects", "list_box_projects"], readonly ["getProject", "GET", "/api/v1/projects/{projectId}", "get_box_project"], readonly ["appendProjectEvent", "POST", "/api/v1/projects/{projectId}/events", "append_box_project_event"], readonly ["demandBatch", "POST", "/api/v1/demand/batch", "get_demand_batch"], readonly ["codeAdvisory", "POST", "/api/v1/codes/advisory", "resolveCodeAdvisory"], readonly ["codes.context", "GET", "/api/v1/codes/context", "list_code_context"], readonly ["codes.requirements", "GET", "/api/v1/codes/requirements", "list_code_requirements"]];
2
+ declare const OPERATION_DEFINITIONS: readonly [readonly ["search", "POST", "/api/v1/search", "search_materials_post"], readonly ["searchGet", "GET", "/api/v1/search", "search_materials_get"], readonly ["match", "POST", "/api/v1/search/match", "match_material"], readonly ["byColour", "GET", "/api/v1/search/colours", "search_by_colour"], readonly ["material", "GET", "/api/v1/materials/{variantId}", "get_material"], readonly ["siblings", "GET", "/api/v1/materials/{variantId}/siblings", "get_material_siblings"], readonly ["materialBlocks", "GET", "/api/v1/materials/{variantId}/blocks", "list_material_blocks"], readonly ["materialAssets", "GET", "/api/v1/materials/{variantId}/assets", "list_material_assets"], readonly ["schema", "GET", "/api/v1/schema/blocks", "list_schema_blocks"], readonly ["schema", "GET", "/api/v1/schema/attributes", "list_schema_attributes"], readonly ["schema", "GET", "/api/v1/schema/verticals", "list_schema_verticals"], readonly ["schema", "GET", "/api/v1/schema/product-types", "list_schema_product_types"], readonly ["schema", "GET", "/api/v1/schema/units", "list_schema_units"], readonly ["schema", "GET", "/api/v1/schema/value-dictionaries", "list_schema_value_dictionaries"], readonly ["schema", "GET", "/api/v1/schema/source-requirements", "list_schema_source_requirements"], readonly ["schema", "GET", "/api/v1/schema/product-type-templates", "list_schema_product_type_templates"], readonly ["schema", "GET", "/api/v1/schema/mappings", "list_schema_mappings"], readonly ["pairings", "GET", "/api/v1/materials/{variantId}/pairings", "get_material_pairings"], readonly ["materials", "POST", "/api/v1/materials/batch", "get_materials_batch_post"], readonly ["materialsCached", "GET", "/api/v1/materials/batch", "get_materials_batch_get"], readonly ["browse", "GET", "/api/v1/search/browse", "search_browse_by_colour"], readonly ["colourFacets", "GET", "/api/v1/colours/facets", "get_colour_facets"], readonly ["colours.profiles", "GET", "/api/v1/colours/profiles", "get_category_colour_profiles"], readonly ["colours.stats", "GET", "/api/v1/colours/stats", "get_colour_stats"], readonly ["resolveColour", "GET", "/api/v1/search/colours/resolve", "resolve_colour_name"], readonly ["suggestColour", "GET", "/api/v1/search/colours/suggest", "suggest_colour_names"], readonly ["suggest", "GET", "/api/v1/search/suggest", "search_suggest"], readonly ["resolveAttribute", "GET", "/api/v1/catalog/attributes/resolve", "catalog_resolve_attribute"], readonly ["resolveAttributeValue", "GET", "/api/v1/catalog/attributes/{attributeId}/values/resolve", "catalog_resolve_value"], readonly ["searchVocabulary", "GET", "/api/v1/catalog/vocabulary/search", "catalog_vocabulary_search"], readonly ["bySpec", "GET", "/api/v1/search/specifications", "catalog_by_spec"], readonly ["taxonomy", "GET", "/api/v1/taxonomy/categories", "get_category_taxonomy"], readonly ["whoami", "GET", "/api/v1/whoami", "whoami"], readonly ["coordinate", "POST", "/api/v1/pairings/coordinate", "coordinate_materials"], readonly ["demand", "GET", "/api/v1/demand", "get_material_demand"], readonly ["colours.palette", "POST", "/api/v1/search/palettes", "search_by_palette"], readonly ["extractPalette", "POST", "/api/v1/search/palettes/extract", "extract_image_palette"], readonly ["colours.gradient", "POST", "/api/v1/search/gradients", "search_by_gradient"], readonly ["specificationFacets", "GET", "/api/v1/catalog/specifications/facets", "list_spec_facets"], readonly ["attributeValues", "GET", "/api/v1/catalog/attributes/{attributeId}/values", "list_attribute_values"], readonly ["applicableSpecs", "GET", "/api/v1/taxonomy/categories/{key}/specs", "get_applicable_specs"], readonly ["glossary", "GET", "/api/v1/glossary/{term}", "explain_material_term"], readonly ["brands", "GET", "/api/v1/brands", "list_brands"], readonly ["brand", "GET", "/api/v1/brands/{brandSlug}", "get_brand_profile"], readonly ["uploadSourceMaterial", "POST", "/api/v1/source-material", "upload_source_material"], readonly ["sourceMaterialBatch", "GET", "/api/v1/source-material/{batchId}", "get_source_material_batch"], readonly ["capabilities", "GET", "/api/v1/capabilities", "list_public_capabilities"], readonly ["createRun", "POST", "/api/v1/runs", "create_public_run"], readonly ["run", "GET", "/api/v1/runs/{runId}", "get_public_run"], readonly ["runSchedule", "GET", "/api/v1/runs/{runId}/schedule", "get_run_finish_schedule"], readonly ["streamRunEvents", "GET", "/api/v1/runs/{runId}/events", "list_public_run_events"], readonly ["resumeRun", "POST", "/api/v1/runs/{runId}/resume", "resume_public_run"], readonly ["cancelRun", "POST", "/api/v1/runs/{runId}/cancel", "cancel_public_run"], readonly ["varyScheme", "POST", "/api/v1/schemes/vary", "vary_schemes"], readonly ["createProject", "POST", "/api/v1/projects", "create_box_project"], readonly ["listProjects", "GET", "/api/v1/projects", "list_box_projects"], readonly ["getProject", "GET", "/api/v1/projects/{projectId}", "get_box_project"], readonly ["appendProjectEvent", "POST", "/api/v1/projects/{projectId}/events", "append_box_project_event"], readonly ["demandBatch", "POST", "/api/v1/demand/batch", "get_demand_batch"], readonly ["codeAdvisory", "POST", "/api/v1/codes/advisory", "resolveCodeAdvisory"], readonly ["codes.context", "GET", "/api/v1/codes/context", "list_code_context"], readonly ["codes.requirements", "GET", "/api/v1/codes/requirements", "list_code_requirements"]];
3
3
  export type SdkOperationId = (typeof OPERATION_DEFINITIONS)[number][3];
4
4
  export type SdkOperation = Readonly<{
5
5
  clientMethod: (typeof OPERATION_DEFINITIONS)[number][0];
package/operations.js CHANGED
@@ -40,7 +40,9 @@ const OPERATION_DEFINITIONS = [
40
40
  ["whoami", "GET", "/api/v1/whoami", "whoami"],
41
41
  ["coordinate", "POST", "/api/v1/pairings/coordinate", "coordinate_materials"],
42
42
  ["demand", "GET", "/api/v1/demand", "get_material_demand"],
43
+ ["colours.palette", "POST", "/api/v1/search/palettes", "search_by_palette"],
43
44
  ["extractPalette", "POST", "/api/v1/search/palettes/extract", "extract_image_palette"],
45
+ ["colours.gradient", "POST", "/api/v1/search/gradients", "search_by_gradient"],
44
46
  ["specificationFacets", "GET", "/api/v1/catalog/specifications/facets", "list_spec_facets"],
45
47
  [
46
48
  "attributeValues",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instruments/materialgraph",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Wire types for MULTI-COLOUR search — `POST /api/v1/search/palettes` and
3
+ * `POST /api/v1/search/gradients`.
4
+ *
5
+ * Kept out of `types.ts` for the same reason `colour-aggregate-types.ts` and
6
+ * `codes-types.ts` are: the shared mirror is at its source-size budget. Same
7
+ * tolerant-parsing contract as the rest of the mirror — response enums are
8
+ * widened so a new server value still type-checks.
9
+ *
10
+ * The MATCH SEMANTICS these types carry are the server's, stated on
11
+ * {@link SearchPaletteInput.logic} and {@link GradientMatch.coverage} rather
12
+ * than left to the endpoint docs.
13
+ */
14
+ import type { OklabTriple, OpenEnum } from "./types.js";
15
+ /** How the per-colour scores are combined across a palette. */
16
+ export type PaletteLogic = OpenEnum<"all" | "any">;
17
+ /** One queried palette colour — exactly one of `hex` or `oklab`. */
18
+ export interface PaletteQueryColour {
19
+ hex?: string;
20
+ oklab?: OklabTriple;
21
+ /**
22
+ * Share of the surface this colour should occupy, in (0,1]. Stating it
23
+ * CHANGES the ranking: a colour asked for at 0.6 rewards candidates whose own
24
+ * swatch sits near 0.6, not merely candidates that contain the colour.
25
+ */
26
+ proportion?: number;
27
+ /** Relative importance; defaults to 1 and is normalised across the query. */
28
+ weight?: number;
29
+ }
30
+ /** One gradient stop — exactly one of `hex` or `oklab`. Order is meaningful. */
31
+ export interface GradientStop {
32
+ hex?: string;
33
+ oklab?: OklabTriple;
34
+ }
35
+ export interface SearchPaletteInput {
36
+ /** The palette to match, 1–8 colours. */
37
+ colours: PaletteQueryColour[];
38
+ /**
39
+ * `all` (the default) requires every queried colour to clear a per-colour
40
+ * eligibility floor and scores the weighted geometric mean; `any` is a
41
+ * noisy-OR and never rejects a candidate for missing one colour.
42
+ */
43
+ logic?: PaletteLogic;
44
+ /** Results to return; defaults to 24, capped at 48. */
45
+ limit?: number;
46
+ }
47
+ export interface SearchGradientInput {
48
+ /** The ordered colour transition, 2–6 stops. */
49
+ stops: GradientStop[];
50
+ /** Results to return; defaults to 24, capped at 48. */
51
+ limit?: number;
52
+ }
53
+ /** One swatch of a candidate's colorscope-extracted palette. */
54
+ export interface CandidateSwatch {
55
+ hex: string;
56
+ proportion: number;
57
+ name: string | null;
58
+ }
59
+ /** Why a queried colour (or gradient sample) scored what it scored. */
60
+ export interface ColourMatchReceipt {
61
+ queryHex: string;
62
+ /** `colourScore × proportionScore` for the best-matching swatch. */
63
+ score: number;
64
+ /** OKLab closeness alone, in [0,1]. */
65
+ colourScore: number;
66
+ /** Proportion agreement alone, in [0,1]. */
67
+ proportionScore: number;
68
+ /** The swatch that produced the score, or null when nothing matched. */
69
+ matched: CandidateSwatch | null;
70
+ }
71
+ /** Identity and evidence shared by both multi-colour match shapes. */
72
+ export interface MultiColourMatchBase {
73
+ variantId: string;
74
+ brand: string;
75
+ product: string;
76
+ brandSlug: string | null;
77
+ productId: string | null;
78
+ color: string | null;
79
+ variantName: string | null;
80
+ imageUrl: string | null;
81
+ /** MB has retired or hidden this SKU. */
82
+ retired: boolean;
83
+ /** The candidate's full extracted palette — the evidence scored against. */
84
+ palette: CandidateSwatch[];
85
+ /** The aggregate match score in [0,1]; higher is closer. */
86
+ score: number;
87
+ }
88
+ export interface PaletteMatch extends MultiColourMatchBase {
89
+ /** One receipt per queried colour, in query order. */
90
+ colours: ColourMatchReceipt[];
91
+ }
92
+ export interface GradientMatch extends MultiColourMatchBase {
93
+ /**
94
+ * Distinct matched thirds of the path over 3. A material owning only one end
95
+ * of the transition caps out around 0.33 — coverage is what separates a real
96
+ * gradient match from a good match on a single stop.
97
+ */
98
+ coverage: number;
99
+ /** One receipt per sampled point along the path, in path order. */
100
+ samples: ColourMatchReceipt[];
101
+ }
102
+ export interface SearchPaletteResponse {
103
+ query: {
104
+ colours: Array<{
105
+ hex: string;
106
+ proportion?: number;
107
+ weight?: number;
108
+ }>;
109
+ logic: PaletteLogic;
110
+ };
111
+ count: number;
112
+ matches: PaletteMatch[];
113
+ }
114
+ export interface SearchGradientResponse {
115
+ query: {
116
+ stops: string[];
117
+ /** The sampled path points the search actually ran against. */
118
+ samples: string[];
119
+ };
120
+ count: number;
121
+ matches: GradientMatch[];
122
+ }
@@ -0,0 +1 @@
1
+ export {};