@salesforce/b2c-dx-mcp 2.0.0 → 2.1.1
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/README.md +15 -62
- package/dist/commands/mcp.d.ts +1 -0
- package/dist/commands/mcp.js +41 -9
- package/dist/registry.d.ts +3 -4
- package/dist/registry.js +7 -14
- package/dist/server.d.ts +1 -1
- package/dist/server.js +3 -1
- package/dist/services.d.ts +21 -1
- package/dist/services.js +39 -6
- package/dist/tools/adapter.d.ts +18 -2
- package/dist/tools/adapter.js +49 -10
- package/dist/tools/cartridges/index.js +16 -22
- package/dist/tools/diagnostics/config-inspect.js +4 -8
- package/dist/tools/diagnostics/debug-capture-at-breakpoint.js +3 -9
- package/dist/tools/diagnostics/debug-list-sessions.js +1 -0
- package/dist/tools/diagnostics/debug-start-session.js +16 -9
- package/dist/tools/diagnostics/debug-wait-for-stop.js +1 -3
- package/dist/tools/diagnostics/log-watch-registry.d.ts +3 -0
- package/dist/tools/diagnostics/log-watch-registry.js +1 -0
- package/dist/tools/diagnostics/logs-get-recent.js +2 -5
- package/dist/tools/diagnostics/logs-list-files.js +1 -1
- package/dist/tools/diagnostics/logs-watch-list.js +1 -0
- package/dist/tools/diagnostics/logs-watch-start.js +10 -10
- package/dist/tools/diagnostics/mrt-log-watch-registry.d.ts +3 -0
- package/dist/tools/diagnostics/mrt-log-watch-registry.js +0 -0
- package/dist/tools/diagnostics/mrt-logs-watch-list.js +1 -0
- package/dist/tools/diagnostics/mrt-logs-watch-poll.js +2 -5
- package/dist/tools/diagnostics/mrt-logs-watch-start.js +9 -8
- package/dist/tools/diagnostics/session-registry.d.ts +3 -0
- package/dist/tools/diagnostics/session-registry.js +2 -1
- package/dist/tools/docs/docs-list.js +4 -6
- package/dist/tools/docs/docs-read.js +2 -9
- package/dist/tools/docs/docs-schema-list.js +1 -1
- package/dist/tools/docs/docs-schema-read.js +2 -3
- package/dist/tools/docs/docs-schema-search.js +2 -2
- package/dist/tools/docs/docs-search.js +3 -9
- package/dist/tools/docs/storefront.js +3 -3
- package/dist/tools/docs/topics.js +3 -1
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.js +0 -1
- package/dist/tools/mrt/index.js +6 -2
- package/dist/tools/project-context.d.ts +38 -11
- package/dist/tools/project-context.js +32 -15
- package/dist/tools/pwav3/pwa-kit-development-guidelines.js +3 -43
- package/dist/tools/scapi/metrics-get.js +5 -23
- package/dist/tools/scapi/scapi-custom-api-generate-scaffold.d.ts +4 -0
- package/dist/tools/scapi/scapi-custom-api-generate-scaffold.js +29 -14
- package/dist/tools/scapi/scapi-custom-apis-get-status.js +3 -14
- package/dist/tools/scapi/scapi-schemas-list.js +11 -11
- package/dist/utils/constants.d.ts +2 -13
- package/dist/utils/constants.js +1 -20
- package/oclif.manifest.json +2 -2
- package/package.json +5 -5
- package/content/sfnext/auth.md +0 -62
- package/content/sfnext/components.md +0 -123
- package/content/sfnext/config.md +0 -180
- package/content/sfnext/data-fetching.md +0 -323
- package/content/sfnext/extensions.md +0 -80
- package/content/sfnext/i18n.md +0 -121
- package/content/sfnext/page-designer.md +0 -78
- package/content/sfnext/performance.md +0 -80
- package/content/sfnext/pitfalls.md +0 -141
- package/content/sfnext/quick-reference.md +0 -226
- package/content/sfnext/state-management.md +0 -75
- package/content/sfnext/styling.md +0 -51
- package/content/sfnext/testing.md +0 -232
- package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.d.ts +0 -24
- package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.js +0 -53
- package/dist/tools/storefrontnext/figma/figma-to-component/index.d.ts +0 -45
- package/dist/tools/storefrontnext/figma/figma-to-component/index.js +0 -332
- package/dist/tools/storefrontnext/figma/generate-component/decision.d.ts +0 -40
- package/dist/tools/storefrontnext/figma/generate-component/decision.js +0 -312
- package/dist/tools/storefrontnext/figma/generate-component/formatter.d.ts +0 -9
- package/dist/tools/storefrontnext/figma/generate-component/formatter.js +0 -92
- package/dist/tools/storefrontnext/figma/generate-component/index.d.ts +0 -117
- package/dist/tools/storefrontnext/figma/generate-component/index.js +0 -102
- package/dist/tools/storefrontnext/figma/map-tokens/css-parser.d.ts +0 -71
- package/dist/tools/storefrontnext/figma/map-tokens/css-parser.js +0 -260
- package/dist/tools/storefrontnext/figma/map-tokens/index.d.ts +0 -64
- package/dist/tools/storefrontnext/figma/map-tokens/index.js +0 -242
- package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.d.ts +0 -65
- package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.js +0 -268
- package/dist/tools/storefrontnext/index.d.ts +0 -30
- package/dist/tools/storefrontnext/index.js +0 -28
- package/dist/tools/storefrontnext/page-designer-decorator/analyzer.d.ts +0 -169
- package/dist/tools/storefrontnext/page-designer-decorator/analyzer.js +0 -567
- package/dist/tools/storefrontnext/page-designer-decorator/index.d.ts +0 -259
- package/dist/tools/storefrontnext/page-designer-decorator/index.js +0 -622
- package/dist/tools/storefrontnext/page-designer-decorator/rules/1-mode-selection.d.ts +0 -8
- package/dist/tools/storefrontnext/page-designer-decorator/rules/1-mode-selection.js +0 -65
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2a-auto-mode.d.ts +0 -13
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2a-auto-mode.js +0 -87
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-0-interactive-overview.d.ts +0 -4
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-0-interactive-overview.js +0 -55
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-1-interactive-analyze.d.ts +0 -22
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-1-interactive-analyze.js +0 -109
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-2-interactive-select-props.d.ts +0 -21
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-2-interactive-select-props.js +0 -60
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-3-interactive-configure-attrs.d.ts +0 -27
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-3-interactive-configure-attrs.js +0 -68
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-4-interactive-configure-regions.d.ts +0 -4
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-4-interactive-configure-regions.js +0 -65
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-5-interactive-confirm-generation.d.ts +0 -11
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-5-interactive-confirm-generation.js +0 -92
- package/dist/tools/storefrontnext/page-designer-decorator/rules.d.ts +0 -51
- package/dist/tools/storefrontnext/page-designer-decorator/rules.js +0 -70
- package/dist/tools/storefrontnext/page-designer-decorator/templates/decorator-generator.d.ts +0 -116
- package/dist/tools/storefrontnext/page-designer-decorator/templates/decorator-generator.js +0 -350
- package/dist/tools/storefrontnext/sfnext-development-guidelines.d.ts +0 -9
- package/dist/tools/storefrontnext/sfnext-development-guidelines.js +0 -141
- package/dist/tools/storefrontnext/site-theming/color-contrast.d.ts +0 -92
- package/dist/tools/storefrontnext/site-theming/color-contrast.js +0 -186
- package/dist/tools/storefrontnext/site-theming/color-mapping.d.ts +0 -16
- package/dist/tools/storefrontnext/site-theming/color-mapping.js +0 -131
- package/dist/tools/storefrontnext/site-theming/guidance-merger.d.ts +0 -11
- package/dist/tools/storefrontnext/site-theming/guidance-merger.js +0 -78
- package/dist/tools/storefrontnext/site-theming/index.d.ts +0 -14
- package/dist/tools/storefrontnext/site-theming/index.js +0 -128
- package/dist/tools/storefrontnext/site-theming/response-builder.d.ts +0 -16
- package/dist/tools/storefrontnext/site-theming/response-builder.js +0 -316
- package/dist/tools/storefrontnext/site-theming/theming-store.d.ts +0 -64
- package/dist/tools/storefrontnext/site-theming/theming-store.js +0 -417
- package/dist/tools/storefrontnext/site-theming/types.d.ts +0 -36
- package/dist/tools/storefrontnext/site-theming/types.js +0 -7
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
-
* SPDX-License-Identifier: Apache-2
|
|
4
|
-
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Developer Guidelines tool for Storefront Next.
|
|
8
|
-
*
|
|
9
|
-
* Provides critical development guidelines and best practices for building
|
|
10
|
-
* Storefront Next applications with React Server Components.
|
|
11
|
-
*
|
|
12
|
-
* @module tools/storefrontnext/sfnext-development-guidelines
|
|
13
|
-
*/
|
|
14
|
-
import { readFileSync } from 'node:fs';
|
|
15
|
-
import { createRequire } from 'node:module';
|
|
16
|
-
import path from 'node:path';
|
|
17
|
-
import { z } from 'zod';
|
|
18
|
-
import { createToolAdapter, textResult } from '../adapter.js';
|
|
19
|
-
// Resolve the content directory from the package root
|
|
20
|
-
// Uses createRequire to find the package.json location, which is robust
|
|
21
|
-
// regardless of where this module is located in the build output
|
|
22
|
-
const require = createRequire(import.meta.url);
|
|
23
|
-
const packageRoot = path.dirname(require.resolve('@salesforce/b2c-dx-mcp/package.json'));
|
|
24
|
-
const CONTENT_DIR = path.join(packageRoot, 'content', 'sfnext');
|
|
25
|
-
/**
|
|
26
|
-
* Section metadata with key and optional description.
|
|
27
|
-
* Single source of truth for all available sections.
|
|
28
|
-
*/
|
|
29
|
-
const SECTIONS_METADATA = [
|
|
30
|
-
{ key: 'quick-reference', description: null }, // Meta-section, excluded from topics list
|
|
31
|
-
{
|
|
32
|
-
key: 'data-fetching',
|
|
33
|
-
description: 'server-only data loading (no client loaders), synchronous loaders for streaming, data fetching patterns',
|
|
34
|
-
},
|
|
35
|
-
{ key: 'state-management', description: 'state management patterns' },
|
|
36
|
-
{ key: 'auth', description: 'authentication and session management' },
|
|
37
|
-
{ key: 'config', description: 'configuration' },
|
|
38
|
-
{ key: 'i18n', description: 'i18n patterns and internationalization' },
|
|
39
|
-
{ key: 'components', description: 'component best practices' },
|
|
40
|
-
{ key: 'styling', description: 'Tailwind CSS 4, Shadcn/ui, styling guidelines' },
|
|
41
|
-
{ key: 'page-designer', description: 'Page Designer integration' },
|
|
42
|
-
{ key: 'performance', description: 'performance optimization' },
|
|
43
|
-
{ key: 'testing', description: 'testing strategies' },
|
|
44
|
-
{ key: 'extensions', description: 'framework extensions' },
|
|
45
|
-
{ key: 'pitfalls', description: 'common pitfalls' },
|
|
46
|
-
];
|
|
47
|
-
/**
|
|
48
|
-
* Derived: array of section keys for validation.
|
|
49
|
-
*/
|
|
50
|
-
const _SECTIONS = SECTIONS_METADATA.map((s) => s.key);
|
|
51
|
-
/**
|
|
52
|
-
* Generates the topics list for the tool description.
|
|
53
|
-
* Excludes meta-sections (like quick-reference) that don't have descriptions.
|
|
54
|
-
* @returns Comma-separated list of topics
|
|
55
|
-
*/
|
|
56
|
-
function generateTopicsList() {
|
|
57
|
-
return SECTIONS_METADATA.filter((s) => s.description !== null)
|
|
58
|
-
.map((s) => s.description)
|
|
59
|
-
.join(', ');
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Detailed section content loaded from markdown files.
|
|
63
|
-
* Built dynamically from SECTIONS_METADATA to avoid duplication.
|
|
64
|
-
*/
|
|
65
|
-
const SECTION_CONTENT = Object.fromEntries(SECTIONS_METADATA.map((section) => {
|
|
66
|
-
const filename = `${section.key}.md`;
|
|
67
|
-
const filePath = path.join(CONTENT_DIR, filename);
|
|
68
|
-
const content = readFileSync(filePath, 'utf8');
|
|
69
|
-
return [section.key, content];
|
|
70
|
-
}));
|
|
71
|
-
/**
|
|
72
|
-
* Default sections to return when no sections are specified.
|
|
73
|
-
* Includes quick-reference plus the most critical detailed sections
|
|
74
|
-
* to provide comprehensive guidelines by default.
|
|
75
|
-
*/
|
|
76
|
-
const DEFAULT_SECTIONS = ['quick-reference', 'data-fetching', 'components', 'testing'];
|
|
77
|
-
/**
|
|
78
|
-
* Creates the developer guidelines tool for Storefront Next.
|
|
79
|
-
*
|
|
80
|
-
* @param loadServices - Function that loads configuration and returns Services instance
|
|
81
|
-
* @returns The configured MCP tool
|
|
82
|
-
*/
|
|
83
|
-
export function createDeveloperGuidelinesTool(loadServices) {
|
|
84
|
-
return createToolAdapter({
|
|
85
|
-
name: 'sfnext_get_guidelines',
|
|
86
|
-
description: '[DEPRECATED] Superseded by the storefront-next and storefront-next-figma agent-skills plugins and NOT compatible with the Storefront Next 1.0 GA release. Will be removed in a future release. ' +
|
|
87
|
-
'ESSENTIAL FIRST STEP for Storefront Next development. Returns critical architecture rules, coding standards, and best practices. ' +
|
|
88
|
-
'Use this tool FIRST before writing any Storefront Next code to understand non-negotiable patterns for React Server Components, ' +
|
|
89
|
-
'data loading, and framework constraints. Returns comprehensive guidelines by default (quick-reference + key sections); ' +
|
|
90
|
-
'supports retrieving specific topic sections. ' +
|
|
91
|
-
'CRITICAL INSTRUCTION: ALWAYS present ALL returned content in FULL - DO NOT SUMMARIZE, DO NOT ADD SUMMARIES, ' +
|
|
92
|
-
'DO NOT ADD OVERVIEWS. The returned content IS the complete answer - display it exactly as provided.',
|
|
93
|
-
toolsets: ['STOREFRONTNEXT_DEPRECATED'],
|
|
94
|
-
isGA: false,
|
|
95
|
-
requiresInstance: false,
|
|
96
|
-
inputSchema: {
|
|
97
|
-
sections: z
|
|
98
|
-
.array(z.enum([..._SECTIONS]))
|
|
99
|
-
.optional()
|
|
100
|
-
.describe('Optional array of specific sections to retrieve. If not specified, returns comprehensive guidelines ' +
|
|
101
|
-
'(quick-reference, data-fetching, components, testing). ' +
|
|
102
|
-
'CRITICAL: Present ALL returned content in FULL - DO NOT SUMMARIZE. ' +
|
|
103
|
-
'Available sections: quick-reference, data-fetching, state-management, auth, config, i18n, ' +
|
|
104
|
-
'components, page-designer, performance, testing, extensions, pitfalls. ' +
|
|
105
|
-
`Topics covered: ${generateTopicsList()}. ` +
|
|
106
|
-
'Content is complete - present exactly as provided, no summaries.'),
|
|
107
|
-
},
|
|
108
|
-
async execute(args) {
|
|
109
|
-
// Handle empty array case explicitly
|
|
110
|
-
if (args.sections && args.sections.length === 0) {
|
|
111
|
-
return '';
|
|
112
|
-
}
|
|
113
|
-
// Default to comprehensive set of key sections if no sections specified
|
|
114
|
-
const sections = args.sections || DEFAULT_SECTIONS;
|
|
115
|
-
// Multiple sections: combine with separators
|
|
116
|
-
const combinedContent = sections.map((section) => SECTION_CONTENT[section]).join('\n\n---\n\n');
|
|
117
|
-
// Apply instructions for all multi-section responses to ensure full content display
|
|
118
|
-
// Single sections are typically smaller and don't need heavy instructions
|
|
119
|
-
const isMultiSection = sections.length > 1;
|
|
120
|
-
// Prepend explicit instruction to present full content (not summarized)
|
|
121
|
-
// This helps AI assistants understand they should show the complete guidelines without summarization
|
|
122
|
-
const fullContentInstruction = isMultiSection
|
|
123
|
-
? '⚠️ CRITICAL: Display the FULL content below. DO NOT summarize, condense, or add overviews.\n\n' +
|
|
124
|
-
'📋 STOREFRONT NEXT DEVELOPMENT GUIDELINES\n\n' +
|
|
125
|
-
'---\n\n'
|
|
126
|
-
: '';
|
|
127
|
-
// Add footer instruction to reinforce the message for multi-section responses
|
|
128
|
-
const footerInstruction = isMultiSection
|
|
129
|
-
? '\n\n---\n\n⚠️ END OF CONTENT - Full content displayed above. Do not add summaries.\n'
|
|
130
|
-
: '';
|
|
131
|
-
// For single sections, return directly (backward compatible)
|
|
132
|
-
// For multiple sections, wrap with instructions
|
|
133
|
-
if (sections.length === 1) {
|
|
134
|
-
return SECTION_CONTENT[sections[0]];
|
|
135
|
-
}
|
|
136
|
-
return fullContentInstruction + combinedContent + footerInstruction;
|
|
137
|
-
},
|
|
138
|
-
formatOutput: (output) => textResult(output),
|
|
139
|
-
}, loadServices);
|
|
140
|
-
}
|
|
141
|
-
//# sourceMappingURL=sfnext-development-guidelines.js.map
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validates that a string is a valid 6-digit hex color.
|
|
3
|
-
* @param hex - Hex color string to validate
|
|
4
|
-
* @returns true if valid
|
|
5
|
-
*/
|
|
6
|
-
export declare function isValidHex(hex: string): boolean;
|
|
7
|
-
/**
|
|
8
|
-
* Calculates the relative luminance of a color according to WCAG 2.1
|
|
9
|
-
* @param hex - Hex color string (e.g., "#635BFF")
|
|
10
|
-
* @returns Relative luminance value between 0 and 1
|
|
11
|
-
* @throws Error if hex format is invalid
|
|
12
|
-
*/
|
|
13
|
-
export declare function getLuminance(hex: string): number;
|
|
14
|
-
/**
|
|
15
|
-
* Calculates the contrast ratio between two colors according to WCAG 2.1
|
|
16
|
-
* @param color1 - First hex color string
|
|
17
|
-
* @param color2 - Second hex color string
|
|
18
|
-
* @returns Contrast ratio (1:1 to 21:1)
|
|
19
|
-
*/
|
|
20
|
-
export declare function getContrastRatio(color1: string, color2: string): number;
|
|
21
|
-
/**
|
|
22
|
-
* WCAG compliance levels
|
|
23
|
-
*/
|
|
24
|
-
export declare enum WCAGLevel {
|
|
25
|
-
AA = "AA",// 4.5:1 for normal text
|
|
26
|
-
AA_LARGE = "AA_LARGE",// 3:1 for large text
|
|
27
|
-
AAA = "AAA",// 7:1 for normal text
|
|
28
|
-
AAA_LARGE = "AAA_LARGE",// 4.5:1 for large text
|
|
29
|
-
FAIL = "FAIL"
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Determines WCAG compliance level for a contrast ratio
|
|
33
|
-
* @param ratio - Contrast ratio
|
|
34
|
-
* @param isLargeText - Whether this is for large text (18pt+ or 14pt+ bold)
|
|
35
|
-
* @returns WCAG compliance level
|
|
36
|
-
*/
|
|
37
|
-
export declare function getWCAGLevel(ratio: number, isLargeText?: boolean): WCAGLevel;
|
|
38
|
-
/**
|
|
39
|
-
* Result of color contrast validation for a single foreground/background pair.
|
|
40
|
-
*
|
|
41
|
-
* @property {string} color1 - First hex color (typically foreground)
|
|
42
|
-
* @property {string} color2 - Second hex color (typically background)
|
|
43
|
-
* @property {number} ratio - Contrast ratio (1:1 to 21:1)
|
|
44
|
-
* @property {WCAGLevel} wcagLevel - WCAG compliance level
|
|
45
|
-
* @property {boolean} passesAA - Whether the combination meets WCAG AA
|
|
46
|
-
* @property {boolean} passesAAA - Whether the combination meets WCAG AAA
|
|
47
|
-
* @property {boolean} isLargeText - Whether validation used large-text thresholds
|
|
48
|
-
* @property {string} visualAssessment - Readability assessment (excellent, good, acceptable, poor)
|
|
49
|
-
* @property {string} [recommendation] - Optional suggestion when contrast is suboptimal
|
|
50
|
-
*/
|
|
51
|
-
export interface ContrastValidationResult {
|
|
52
|
-
color1: string;
|
|
53
|
-
color2: string;
|
|
54
|
-
ratio: number;
|
|
55
|
-
wcagLevel: WCAGLevel;
|
|
56
|
-
passesAA: boolean;
|
|
57
|
-
passesAAA: boolean;
|
|
58
|
-
isLargeText: boolean;
|
|
59
|
-
visualAssessment: 'acceptable' | 'excellent' | 'good' | 'poor';
|
|
60
|
-
recommendation?: string;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Validates contrast between two colors
|
|
64
|
-
* @param color1 - First hex color string
|
|
65
|
-
* @param color2 - Second hex color string
|
|
66
|
-
* @param isLargeText - Whether this is for large text
|
|
67
|
-
* @returns Validation result with contrast ratio and compliance info
|
|
68
|
-
*/
|
|
69
|
-
export declare function validateContrast(color1: string, color2: string, isLargeText?: boolean): ContrastValidationResult;
|
|
70
|
-
/**
|
|
71
|
-
* Validates multiple color combinations for WCAG compliance.
|
|
72
|
-
*
|
|
73
|
-
* @param combinations - Array of foreground/background pairs with optional label and large-text flag
|
|
74
|
-
* @returns Array of validation results, each including the input label if provided
|
|
75
|
-
*/
|
|
76
|
-
export declare function validateColorCombinations(combinations: Array<{
|
|
77
|
-
foreground: string;
|
|
78
|
-
background: string;
|
|
79
|
-
isLargeText?: boolean;
|
|
80
|
-
label?: string;
|
|
81
|
-
}>): Array<ContrastValidationResult & {
|
|
82
|
-
label?: string;
|
|
83
|
-
}>;
|
|
84
|
-
/**
|
|
85
|
-
* Formats a validation result as a human-readable string for display to users.
|
|
86
|
-
*
|
|
87
|
-
* @param result - Validation result, optionally with a label for the color combination
|
|
88
|
-
* @returns Multi-line string with contrast ratio, WCAG status, and recommendation (if any)
|
|
89
|
-
*/
|
|
90
|
-
export declare function formatValidationResult(result: ContrastValidationResult & {
|
|
91
|
-
label?: string;
|
|
92
|
-
}): string;
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
-
* SPDX-License-Identifier: Apache-2
|
|
4
|
-
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* WCAG 2.1 color contrast utilities for accessibility validation.
|
|
8
|
-
*
|
|
9
|
-
* Provides luminance calculation, contrast ratio computation, and WCAG compliance
|
|
10
|
-
* checking for theming and color validation in Storefront Next.
|
|
11
|
-
*
|
|
12
|
-
* @module tools/storefrontnext/site-theming/color-contrast
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* WCAG 2.1 constants for contrast ratio calculation
|
|
16
|
-
* These values are specified in the WCAG 2.1 standard
|
|
17
|
-
*/
|
|
18
|
-
const WCAG_CONTRAST_OFFSET = 0.05; // Offset added to luminance values in contrast ratio formula
|
|
19
|
-
// Linear RGB conversion constants (sRGB to linear RGB)
|
|
20
|
-
const LINEAR_RGB_THRESHOLD = 0.039_28; // Threshold for linear RGB conversion
|
|
21
|
-
const LINEAR_RGB_DIVISOR = 12.92; // Divisor for values below threshold
|
|
22
|
-
const GAMMA_CORRECTION_OFFSET = 0.055; // Offset for gamma correction
|
|
23
|
-
const GAMMA_CORRECTION_DIVISOR = 1.055; // Divisor for gamma correction
|
|
24
|
-
const GAMMA_EXPONENT = 2.4; // Gamma exponent for sRGB
|
|
25
|
-
// Relative luminance weights (WCAG 2.1 standard)
|
|
26
|
-
const LUMINANCE_RED_WEIGHT = 0.2126;
|
|
27
|
-
const LUMINANCE_GREEN_WEIGHT = 0.7152;
|
|
28
|
-
const LUMINANCE_BLUE_WEIGHT = 0.0722;
|
|
29
|
-
/** Valid 6-digit hex color pattern (with optional # prefix) */
|
|
30
|
-
const HEX_PATTERN = /^#?[0-9A-Fa-f]{6}$/;
|
|
31
|
-
/**
|
|
32
|
-
* Validates that a string is a valid 6-digit hex color.
|
|
33
|
-
* @param hex - Hex color string to validate
|
|
34
|
-
* @returns true if valid
|
|
35
|
-
*/
|
|
36
|
-
export function isValidHex(hex) {
|
|
37
|
-
return typeof hex === 'string' && HEX_PATTERN.test(hex.trim());
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Calculates the relative luminance of a color according to WCAG 2.1
|
|
41
|
-
* @param hex - Hex color string (e.g., "#635BFF")
|
|
42
|
-
* @returns Relative luminance value between 0 and 1
|
|
43
|
-
* @throws Error if hex format is invalid
|
|
44
|
-
*/
|
|
45
|
-
export function getLuminance(hex) {
|
|
46
|
-
const trimmed = hex.trim();
|
|
47
|
-
if (!HEX_PATTERN.test(trimmed)) {
|
|
48
|
-
throw new Error(`Invalid hex color: "${hex}". Expected 6-digit hex (e.g., #635BFF).`);
|
|
49
|
-
}
|
|
50
|
-
const cleanHex = trimmed.replace('#', '');
|
|
51
|
-
// Parse RGB values
|
|
52
|
-
const r = Number.parseInt(cleanHex.slice(0, 2), 16) / 255;
|
|
53
|
-
const g = Number.parseInt(cleanHex.slice(2, 4), 16) / 255;
|
|
54
|
-
const b = Number.parseInt(cleanHex.slice(4, 6), 16) / 255;
|
|
55
|
-
// Convert to linear RGB
|
|
56
|
-
const [rs, gs, bs] = [r, g, b].map((c) => {
|
|
57
|
-
return c <= LINEAR_RGB_THRESHOLD
|
|
58
|
-
? c / LINEAR_RGB_DIVISOR
|
|
59
|
-
: ((c + GAMMA_CORRECTION_OFFSET) / GAMMA_CORRECTION_DIVISOR) ** GAMMA_EXPONENT;
|
|
60
|
-
});
|
|
61
|
-
// Calculate relative luminance
|
|
62
|
-
return LUMINANCE_RED_WEIGHT * rs + LUMINANCE_GREEN_WEIGHT * gs + LUMINANCE_BLUE_WEIGHT * bs;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Calculates the contrast ratio between two colors according to WCAG 2.1
|
|
66
|
-
* @param color1 - First hex color string
|
|
67
|
-
* @param color2 - Second hex color string
|
|
68
|
-
* @returns Contrast ratio (1:1 to 21:1)
|
|
69
|
-
*/
|
|
70
|
-
export function getContrastRatio(color1, color2) {
|
|
71
|
-
const l1 = getLuminance(color1);
|
|
72
|
-
const l2 = getLuminance(color2);
|
|
73
|
-
const lighter = Math.max(l1, l2);
|
|
74
|
-
const darker = Math.min(l1, l2);
|
|
75
|
-
return (lighter + WCAG_CONTRAST_OFFSET) / (darker + WCAG_CONTRAST_OFFSET);
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* WCAG compliance levels
|
|
79
|
-
*/
|
|
80
|
-
export var WCAGLevel;
|
|
81
|
-
(function (WCAGLevel) {
|
|
82
|
-
WCAGLevel["AA"] = "AA";
|
|
83
|
-
WCAGLevel["AA_LARGE"] = "AA_LARGE";
|
|
84
|
-
WCAGLevel["AAA"] = "AAA";
|
|
85
|
-
WCAGLevel["AAA_LARGE"] = "AAA_LARGE";
|
|
86
|
-
WCAGLevel["FAIL"] = "FAIL";
|
|
87
|
-
})(WCAGLevel || (WCAGLevel = {}));
|
|
88
|
-
/**
|
|
89
|
-
* Determines WCAG compliance level for a contrast ratio
|
|
90
|
-
* @param ratio - Contrast ratio
|
|
91
|
-
* @param isLargeText - Whether this is for large text (18pt+ or 14pt+ bold)
|
|
92
|
-
* @returns WCAG compliance level
|
|
93
|
-
*/
|
|
94
|
-
export function getWCAGLevel(ratio, isLargeText = false) {
|
|
95
|
-
if (isLargeText) {
|
|
96
|
-
if (ratio >= 4.5) {
|
|
97
|
-
return WCAGLevel.AAA_LARGE;
|
|
98
|
-
}
|
|
99
|
-
if (ratio >= 3) {
|
|
100
|
-
return WCAGLevel.AA_LARGE;
|
|
101
|
-
}
|
|
102
|
-
return WCAGLevel.FAIL;
|
|
103
|
-
}
|
|
104
|
-
if (ratio >= 7) {
|
|
105
|
-
return WCAGLevel.AAA;
|
|
106
|
-
}
|
|
107
|
-
if (ratio >= 4.5) {
|
|
108
|
-
return WCAGLevel.AA;
|
|
109
|
-
}
|
|
110
|
-
return WCAGLevel.FAIL;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Validates contrast between two colors
|
|
114
|
-
* @param color1 - First hex color string
|
|
115
|
-
* @param color2 - Second hex color string
|
|
116
|
-
* @param isLargeText - Whether this is for large text
|
|
117
|
-
* @returns Validation result with contrast ratio and compliance info
|
|
118
|
-
*/
|
|
119
|
-
export function validateContrast(color1, color2, isLargeText = false) {
|
|
120
|
-
const ratio = getContrastRatio(color1, color2);
|
|
121
|
-
const wcagLevel = getWCAGLevel(ratio, isLargeText);
|
|
122
|
-
const passesAA = ratio >= (isLargeText ? 3 : 4.5);
|
|
123
|
-
const passesAAA = ratio >= (isLargeText ? 4.5 : 7);
|
|
124
|
-
// Visual assessment based on ratio
|
|
125
|
-
let visualAssessment;
|
|
126
|
-
let recommendation;
|
|
127
|
-
if (ratio >= 7) {
|
|
128
|
-
visualAssessment = 'excellent';
|
|
129
|
-
}
|
|
130
|
-
else if (ratio >= 5) {
|
|
131
|
-
visualAssessment = 'good';
|
|
132
|
-
}
|
|
133
|
-
else if (ratio >= 4.5) {
|
|
134
|
-
visualAssessment = 'acceptable';
|
|
135
|
-
recommendation =
|
|
136
|
-
'Meets minimum WCAG AA but may be difficult to read, especially for body text. Consider using a darker/lighter color for better readability.';
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
visualAssessment = 'poor';
|
|
140
|
-
recommendation =
|
|
141
|
-
'Does not meet WCAG AA standards. Text will be difficult to read. Strongly recommend using a color with better contrast.';
|
|
142
|
-
}
|
|
143
|
-
return {
|
|
144
|
-
color1,
|
|
145
|
-
color2,
|
|
146
|
-
ratio,
|
|
147
|
-
wcagLevel,
|
|
148
|
-
passesAA,
|
|
149
|
-
passesAAA,
|
|
150
|
-
isLargeText,
|
|
151
|
-
visualAssessment,
|
|
152
|
-
recommendation,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Validates multiple color combinations for WCAG compliance.
|
|
157
|
-
*
|
|
158
|
-
* @param combinations - Array of foreground/background pairs with optional label and large-text flag
|
|
159
|
-
* @returns Array of validation results, each including the input label if provided
|
|
160
|
-
*/
|
|
161
|
-
export function validateColorCombinations(combinations) {
|
|
162
|
-
return combinations.map((combo) => ({
|
|
163
|
-
...validateContrast(combo.foreground, combo.background, combo.isLargeText ?? false),
|
|
164
|
-
label: combo.label,
|
|
165
|
-
}));
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Formats a validation result as a human-readable string for display to users.
|
|
169
|
-
*
|
|
170
|
-
* @param result - Validation result, optionally with a label for the color combination
|
|
171
|
-
* @returns Multi-line string with contrast ratio, WCAG status, and recommendation (if any)
|
|
172
|
-
*/
|
|
173
|
-
export function formatValidationResult(result) {
|
|
174
|
-
const label = result.label ? `${result.label}: ` : '';
|
|
175
|
-
const textType = result.isLargeText ? 'large text' : 'normal text';
|
|
176
|
-
const wcagStatus = result.passesAAA ? '✅ AAA' : result.passesAA ? '✅ AA' : '❌ FAIL';
|
|
177
|
-
let output = `${label}${result.color1} on ${result.color2}\n`;
|
|
178
|
-
output += ` Contrast Ratio: ${result.ratio.toFixed(2)}:1\n`;
|
|
179
|
-
output += ` WCAG ${textType}: ${wcagStatus}\n`;
|
|
180
|
-
output += ` Visual Assessment: ${result.visualAssessment.toUpperCase()}\n`;
|
|
181
|
-
if (result.recommendation) {
|
|
182
|
-
output += ` ⚠️ ${result.recommendation}\n`;
|
|
183
|
-
}
|
|
184
|
-
return output;
|
|
185
|
-
}
|
|
186
|
-
//# sourceMappingURL=color-contrast.js.map
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/** A foreground/background color pair for contrast validation */
|
|
2
|
-
export type ColorCombination = {
|
|
3
|
-
foreground: string;
|
|
4
|
-
background: string;
|
|
5
|
-
label: string;
|
|
6
|
-
isLargeText?: boolean;
|
|
7
|
-
};
|
|
8
|
-
/**
|
|
9
|
-
* Builds foreground/background color combinations from a semantic color mapping.
|
|
10
|
-
* Derives pairs for text-on-background, button text, links, etc.
|
|
11
|
-
*/
|
|
12
|
-
export declare function buildColorCombinations(colorMapping: Record<string, string>): ColorCombination[];
|
|
13
|
-
/**
|
|
14
|
-
* Appends WCAG color contrast validation results to the given instructions string.
|
|
15
|
-
*/
|
|
16
|
-
export declare function appendValidationSection(internalInstructions: string, combinations: ColorCombination[]): string;
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
-
* SPDX-License-Identifier: Apache-2
|
|
4
|
-
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Derives foreground/background color combinations from a color mapping and
|
|
8
|
-
* appends WCAG validation results to response text.
|
|
9
|
-
*
|
|
10
|
-
* @module tools/storefrontnext/site-theming/color-mapping
|
|
11
|
-
*/
|
|
12
|
-
import { validateColorCombinations, formatValidationResult, isValidHex } from './color-contrast.js';
|
|
13
|
-
function tryTextCombo(key, color, keyLower, ctx) {
|
|
14
|
-
if (keyLower.includes('text') && keyLower.includes('light') && isValidHex(ctx.lightBg)) {
|
|
15
|
-
return { foreground: color, background: ctx.lightBg, label: `${key}: ${color} on light background (${ctx.lightBg})` };
|
|
16
|
-
}
|
|
17
|
-
if (keyLower.includes('text') && keyLower.includes('dark') && isValidHex(ctx.darkBg)) {
|
|
18
|
-
return { foreground: color, background: ctx.darkBg, label: `${key}: ${color} on dark background (${ctx.darkBg})` };
|
|
19
|
-
}
|
|
20
|
-
const isButtonText = keyLower === 'buttontext' || (keyLower.includes('button') && keyLower.includes('text'));
|
|
21
|
-
if (isButtonText && isValidHex(ctx.buttonBg)) {
|
|
22
|
-
return {
|
|
23
|
-
foreground: color,
|
|
24
|
-
background: ctx.buttonBg,
|
|
25
|
-
label: `${key}: ${color} on button background (${ctx.buttonBg})`,
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
if (keyLower.includes('link') && isValidHex(ctx.lightBg)) {
|
|
29
|
-
return { foreground: color, background: ctx.lightBg, label: `${key}: ${color} on light background (${ctx.lightBg})` };
|
|
30
|
-
}
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
function tryBackgroundCombo(key, color, ctx) {
|
|
34
|
-
const foregroundKey = key.replace(/Background|Bg/i, 'Text') || key.replace(/Background|Bg/i, 'Foreground');
|
|
35
|
-
const foreground = ctx.colorMapping[foregroundKey] || ctx.colorMapping[`${key.replace(/Background|Bg/i, '')}Text`];
|
|
36
|
-
if (foreground?.startsWith('#') && isValidHex(foreground)) {
|
|
37
|
-
return { foreground, background: color, label: `${foregroundKey || 'text'} (${foreground}) on ${key} (${color})` };
|
|
38
|
-
}
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
function tryTextForegroundCombo(key, color, keyLower, ctx) {
|
|
42
|
-
const backgroundKey = key.replace(/Text|Foreground/i, 'Background') || key.replace(/Text|Foreground/i, 'Bg');
|
|
43
|
-
let background = ctx.colorMapping[backgroundKey];
|
|
44
|
-
let backgroundLabel = backgroundKey;
|
|
45
|
-
if (!background) {
|
|
46
|
-
background = keyLower.includes('button') ? ctx.buttonBg : keyLower.includes('dark') ? ctx.darkBg : ctx.lightBg;
|
|
47
|
-
backgroundLabel = keyLower.includes('button')
|
|
48
|
-
? 'button background'
|
|
49
|
-
: keyLower.includes('dark')
|
|
50
|
-
? 'dark background'
|
|
51
|
-
: 'light background';
|
|
52
|
-
}
|
|
53
|
-
if (background?.startsWith('#') && isValidHex(background)) {
|
|
54
|
-
return { foreground: color, background, label: `${key} (${color}) on ${backgroundLabel} (${background})` };
|
|
55
|
-
}
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
function tryComboForEntry(key, color, ctx) {
|
|
59
|
-
const keyLower = key.toLowerCase();
|
|
60
|
-
const textCombo = tryTextCombo(key, color, keyLower, ctx);
|
|
61
|
-
if (textCombo)
|
|
62
|
-
return textCombo;
|
|
63
|
-
if (keyLower.includes('background') || keyLower.includes('bg'))
|
|
64
|
-
return tryBackgroundCombo(key, color, ctx);
|
|
65
|
-
if (keyLower.includes('text') || keyLower.includes('foreground'))
|
|
66
|
-
return tryTextForegroundCombo(key, color, keyLower, ctx);
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Builds foreground/background color combinations from a semantic color mapping.
|
|
71
|
-
* Derives pairs for text-on-background, button text, links, etc.
|
|
72
|
-
*/
|
|
73
|
-
export function buildColorCombinations(colorMapping) {
|
|
74
|
-
const ctx = {
|
|
75
|
-
colorMapping,
|
|
76
|
-
lightBg: colorMapping.lightBackground || colorMapping.background || '#FFFFFF',
|
|
77
|
-
darkBg: colorMapping.darkBackground || '#18181B',
|
|
78
|
-
buttonBg: colorMapping.buttonBackground || colorMapping.primary || '#0A2540',
|
|
79
|
-
};
|
|
80
|
-
const combinations = [];
|
|
81
|
-
for (const [key, color] of Object.entries(colorMapping)) {
|
|
82
|
-
if (!color || !color.startsWith('#') || !isValidHex(color))
|
|
83
|
-
continue;
|
|
84
|
-
const combo = tryComboForEntry(key, color, ctx);
|
|
85
|
-
if (combo)
|
|
86
|
-
combinations.push(combo);
|
|
87
|
-
}
|
|
88
|
-
if (combinations.length === 0) {
|
|
89
|
-
const whiteBg = '#FFFFFF';
|
|
90
|
-
const darkBgFallback = '#18181B';
|
|
91
|
-
for (const [key, color] of Object.entries(colorMapping)) {
|
|
92
|
-
if (!color || !color.startsWith('#') || !isValidHex(color))
|
|
93
|
-
continue;
|
|
94
|
-
if (key.toLowerCase().includes('background') || key.toLowerCase().includes('bg'))
|
|
95
|
-
continue;
|
|
96
|
-
combinations.push({ foreground: color, background: whiteBg, label: `${key} (${color}) on white background` }, { foreground: color, background: darkBgFallback, label: `${key} (${color}) on dark background` });
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return combinations;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Appends WCAG color contrast validation results to the given instructions string.
|
|
103
|
-
*/
|
|
104
|
-
export function appendValidationSection(internalInstructions, combinations) {
|
|
105
|
-
if (combinations.length === 0) {
|
|
106
|
-
return internalInstructions;
|
|
107
|
-
}
|
|
108
|
-
const results = validateColorCombinations(combinations);
|
|
109
|
-
let output = internalInstructions;
|
|
110
|
-
for (const result of results) {
|
|
111
|
-
output += formatValidationResult(result);
|
|
112
|
-
output += '\n';
|
|
113
|
-
}
|
|
114
|
-
const hasIssues = results.some((r) => !r.passesAA || r.visualAssessment === 'poor' || r.visualAssessment === 'acceptable');
|
|
115
|
-
if (hasIssues) {
|
|
116
|
-
output += '### ⚠️ VALIDATION SUMMARY\n\n';
|
|
117
|
-
output += '**Issues found that should be addressed:**\n\n';
|
|
118
|
-
for (const result of results.filter((r) => !r.passesAA || r.visualAssessment === 'poor' || r.visualAssessment === 'acceptable')) {
|
|
119
|
-
output += `- ${result.label || 'Color combination'}: ${result.recommendation || 'Needs improvement'}\n`;
|
|
120
|
-
}
|
|
121
|
-
output += '\n';
|
|
122
|
-
output +=
|
|
123
|
-
'**You MUST present these findings to the user BEFORE implementing and wait for their confirmation.**\n\n';
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
output += '### ✅ VALIDATION SUMMARY\n\n';
|
|
127
|
-
output += 'All color combinations meet WCAG AA standards and have good visual assessment.\n\n';
|
|
128
|
-
}
|
|
129
|
-
return output;
|
|
130
|
-
}
|
|
131
|
-
//# sourceMappingURL=color-mapping.js.map
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Merges multiple ThemingGuidance objects from different theming files.
|
|
3
|
-
*
|
|
4
|
-
* @module tools/storefrontnext/site-theming/guidance-merger
|
|
5
|
-
*/
|
|
6
|
-
import type { ThemingGuidance } from './theming-store.js';
|
|
7
|
-
/**
|
|
8
|
-
* Merges multiple ThemingGuidance objects into one.
|
|
9
|
-
* Questions are deduplicated by ID; guidelines, rules, workflows, and validations are combined.
|
|
10
|
-
*/
|
|
11
|
-
export declare function mergeGuidance(guidanceArray: ThemingGuidance[]): ThemingGuidance;
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
-
* SPDX-License-Identifier: Apache-2
|
|
4
|
-
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
-
*/
|
|
6
|
-
function mergeWorkflows(guidanceArray) {
|
|
7
|
-
const workflows = guidanceArray.filter((g) => g.workflow);
|
|
8
|
-
if (workflows.length === 0)
|
|
9
|
-
return undefined;
|
|
10
|
-
const merged = {
|
|
11
|
-
steps: [],
|
|
12
|
-
extractionInstructions: workflows[0].workflow?.extractionInstructions,
|
|
13
|
-
preImplementationChecklist: workflows[0].workflow?.preImplementationChecklist,
|
|
14
|
-
};
|
|
15
|
-
for (const g of workflows) {
|
|
16
|
-
if (g.workflow?.steps)
|
|
17
|
-
merged.steps.push(...g.workflow.steps);
|
|
18
|
-
if (!merged.extractionInstructions && g.workflow?.extractionInstructions) {
|
|
19
|
-
merged.extractionInstructions = g.workflow.extractionInstructions;
|
|
20
|
-
}
|
|
21
|
-
if (!merged.preImplementationChecklist && g.workflow?.preImplementationChecklist) {
|
|
22
|
-
merged.preImplementationChecklist = g.workflow.preImplementationChecklist;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return merged;
|
|
26
|
-
}
|
|
27
|
-
function mergeValidations(guidanceArray) {
|
|
28
|
-
const validations = guidanceArray.filter((g) => g.validation);
|
|
29
|
-
if (validations.length === 0)
|
|
30
|
-
return undefined;
|
|
31
|
-
const joinField = (field) => validations
|
|
32
|
-
.map((g) => g.validation?.[field])
|
|
33
|
-
.filter((x) => typeof x === 'string')
|
|
34
|
-
.join('\n\n');
|
|
35
|
-
return {
|
|
36
|
-
colorValidation: joinField('colorValidation'),
|
|
37
|
-
fontValidation: joinField('fontValidation'),
|
|
38
|
-
generalValidation: joinField('generalValidation'),
|
|
39
|
-
requirements: joinField('requirements'),
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
function buildQuestionMap(guidanceArray) {
|
|
43
|
-
const questionMap = new Map();
|
|
44
|
-
for (const guidance of guidanceArray) {
|
|
45
|
-
for (const q of guidance.questions) {
|
|
46
|
-
if (!questionMap.has(q.id))
|
|
47
|
-
questionMap.set(q.id, q);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return questionMap;
|
|
51
|
-
}
|
|
52
|
-
function buildMergedMetadata(guidanceArray) {
|
|
53
|
-
return {
|
|
54
|
-
filePath: guidanceArray.map((g) => g.metadata.filePath).join(', '),
|
|
55
|
-
fileName: guidanceArray.map((g) => g.metadata.fileName).join(', '),
|
|
56
|
-
loadedAt: new Date(),
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Merges multiple ThemingGuidance objects into one.
|
|
61
|
-
* Questions are deduplicated by ID; guidelines, rules, workflows, and validations are combined.
|
|
62
|
-
*/
|
|
63
|
-
export function mergeGuidance(guidanceArray) {
|
|
64
|
-
if (guidanceArray.length === 0)
|
|
65
|
-
throw new Error('Cannot merge empty guidance array');
|
|
66
|
-
if (guidanceArray.length === 1)
|
|
67
|
-
return guidanceArray[0];
|
|
68
|
-
const questionMap = buildQuestionMap(guidanceArray);
|
|
69
|
-
return {
|
|
70
|
-
questions: [...questionMap.values()],
|
|
71
|
-
guidelines: guidanceArray.flatMap((g) => g.guidelines),
|
|
72
|
-
rules: guidanceArray.flatMap((g) => g.rules),
|
|
73
|
-
metadata: buildMergedMetadata(guidanceArray),
|
|
74
|
-
workflow: mergeWorkflows(guidanceArray),
|
|
75
|
-
validation: mergeValidations(guidanceArray),
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
//# sourceMappingURL=guidance-merger.js.map
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { McpTool } from '../../../utils/index.js';
|
|
2
|
-
import type { Services } from '../../../services.js';
|
|
3
|
-
export type { ColorEntry, ColorMapping, CollectedAnswers, ConversationContext, FontEntry, SiteThemingInput, } from './types.js';
|
|
4
|
-
/**
|
|
5
|
-
* Creates the site theming MCP tool for Storefront Next.
|
|
6
|
-
*
|
|
7
|
-
* The tool guides theming changes (colors, fonts, visual styling) and validates color
|
|
8
|
-
* combinations for WCAG accessibility. It must be called before implementing any
|
|
9
|
-
* theming changes.
|
|
10
|
-
*
|
|
11
|
-
* @param loadServices - Function that loads configuration and returns Services instance
|
|
12
|
-
* @returns The configured MCP tool
|
|
13
|
-
*/
|
|
14
|
-
export declare function createSiteThemingTool(loadServices: () => Promise<Services> | Services): McpTool;
|