Skip to content

Commit 2cda361

Browse files
GarthDBcursoragent
andauthored
feat: Leonardo AI tooling (fixes #266) (#273)
* docs: split index into 3 static HTML pages (Welcome, API, Articles) - Add index.html (Welcome), api.html (Leonardo JS API), articles.html - Add home_sidenav_links partial with <a href> nav; highlight current page via home.js - Add home.js for doc pages (dark mode, page loader, nav active state) - Use header_home and index.css for doc pages; theme/scales/tools unchanged - Add api, articles to Vite rollup input Co-authored-by: Cursor <cursoragent@cursor.com> * feat: add Leonardo AI tooling (fixes #266) - Add llms.txt at repo root for LLM context (#267) - Add Agent Skill skills/leonardo-colors/ for @adobe/leonardo-contrast-colors (#268) - Add @adobe/leonardo-mcp package with MCP server and four tools (#269) - Add .cursor/mcp.json for local Cursor MCP integration - Add mcp project to moon workspace; changeset for release Co-authored-by: Cursor <cursoragent@cursor.com> * fix(docs): set header aria-current only on index page Remove hardcoded aria-current and is-selected from header Home tab. Highlight header tab in home.js from pathname so api/articles do not announce as current page for assistive tech. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): improve error handling, dynamic version, and palette options - Add try/catch with descriptive errors in all tool handlers; return isError MCP responses instead of crashing on invalid input - Read server version from package.json instead of hardcoding - Reuse colorDefSchema for backgroundColor input validation - Expose smooth, shift, fullScale, distributeLightness, sortColor options in create-palette tool - Fix check-contrast JSDoc to include largeText in return type - Rename test file to tools.test.js to reflect full coverage Co-authored-by: Cursor <cursoragent@cursor.com> * docs(mcp): add README for npm package page - Badges, quick start, tools reference (generate-theme, check-contrast, convert-color, create-palette) - Cursor and Claude Desktop config examples - Links to leonardo-contrast-colors, web app, agent skill - Contributing and licensing Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 246d9f7 commit 2cda361

File tree

24 files changed

+1746
-3
lines changed

24 files changed

+1746
-3
lines changed

.changeset/leonardo-mcp-initial.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@adobe/leonardo-mcp": minor
3+
---
4+
5+
Add @adobe/leonardo-mcp — MCP server for Leonardo contrast colors (generate-theme, check-contrast, convert-color, create-palette).

.cursor/mcp.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"mcpServers": {
3+
"leonardo": {
4+
"command": "node",
5+
"args": ["packages/mcp/src/cli.js"]
6+
}
7+
}
8+
}

.moon/workspace.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ $schema: 'https://moonrepo.dev/schemas/workspace.json'
33
projects:
44
ui: 'docs/ui'
55
contrast-colors: 'packages/contrast-colors'
6+
mcp: 'packages/mcp'
67
vcs:
78
manager: 'git'
89
defaultBranch: 'main'

docs/ui/src/ai-tools.html

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE html>
2+
<html dir="ltr">
3+
<head>
4+
{{> htmlHead }}
5+
</head>
6+
<body class="spectrum spectrum--medium appFrame">
7+
<div id="pageLoader">
8+
<div class="spectrum-ProgressCircle spectrum-ProgressCircle--indeterminate spectrum-ProgressCircle--large">
9+
<div class="spectrum-ProgressCircle-track"></div>
10+
<div class="spectrum-ProgressCircle-fills">
11+
<div class="spectrum-ProgressCircle-fillMask1">
12+
<div class="spectrum-ProgressCircle-fillSubMask1">
13+
<div class="spectrum-ProgressCircle-fill"></div>
14+
</div>
15+
</div>
16+
<div class="spectrum-ProgressCircle-fillMask2">
17+
<div class="spectrum-ProgressCircle-fillSubMask2">
18+
<div class="spectrum-ProgressCircle-fill"></div>
19+
</div>
20+
</div>
21+
</div>
22+
</div>
23+
</div>
24+
25+
<div id="page" style="opacity: 0;">
26+
{{> header_home }}
27+
28+
<div id="home" class="AppTab appFrameSideNav">
29+
{{> home_sidenav_links }}
30+
31+
<main class="home-Wrapper spectrum-Typography">
32+
{{> home_ai_tools }}
33+
</main>
34+
</div>
35+
</div>
36+
37+
<script src="home.js" type="module"></script>
38+
</body>
39+
</html>

docs/ui/src/home.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ governing permissions and limitations under the License.
1212

1313
import './index.css';
1414
import {pageLoader} from './js/pageLoader';
15+
import hljs from 'highlight.js/lib/core';
16+
import javascript from 'highlight.js/lib/languages/javascript';
17+
import bash from 'highlight.js/lib/languages/bash';
18+
import json from 'highlight.js/lib/languages/json';
19+
hljs.registerLanguage('javascript', javascript);
20+
hljs.registerLanguage('bash', bash);
21+
hljs.registerLanguage('json', json);
1522

1623
// Dark mode: match system preference
1724
const mq = window.matchMedia('(prefers-color-scheme: dark)');
@@ -22,12 +29,27 @@ function applyColorScheme() {
2229
mq.addEventListener('change', applyColorScheme);
2330
applyColorScheme();
2431

32+
// Header tab: set aria-current and is-selected only when on index
33+
function highlightHeaderTab() {
34+
const headerTab = document.querySelector('.spectrum-AppHeader a[href="index.html"]');
35+
if (!headerTab) return;
36+
const pathname = window.location.pathname;
37+
const isIndex = pathname.endsWith('/') || pathname.endsWith('index.html') || pathname === '' || pathname.endsWith('/index');
38+
if (isIndex) {
39+
headerTab.setAttribute('aria-current', 'page');
40+
headerTab.classList.add('is-selected');
41+
} else {
42+
headerTab.removeAttribute('aria-current');
43+
headerTab.classList.remove('is-selected');
44+
}
45+
}
46+
2547
// Side nav: highlight current page based on pathname
2648
function highlightDocsSideNav() {
2749
const nav = document.getElementById('docsSideNav');
2850
if (!nav) return;
2951
const pathname = window.location.pathname;
30-
const page = pathname.endsWith('api.html') ? 'api' : pathname.endsWith('articles.html') ? 'articles' : 'index';
52+
const page = pathname.endsWith('api.html') ? 'api' : pathname.endsWith('articles.html') ? 'articles' : pathname.endsWith('ai-tools.html') ? 'ai-tools' : 'index';
3153
const currentLink = nav.querySelector(`a[data-page="${page}"]`);
3254
if (currentLink) {
3355
const item = currentLink.closest('.spectrum-SideNav-item');
@@ -36,6 +58,8 @@ function highlightDocsSideNav() {
3658
}
3759

3860
window.addEventListener('load', () => {
61+
highlightHeaderTab();
3962
highlightDocsSideNav();
63+
hljs.highlightAll();
4064
pageLoader();
4165
});

docs/ui/src/index.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ governing permissions and limitations under the License.
3737
/* Doc static pages: single content section visible without JS */
3838
#homeContent,
3939
#getstartedContent,
40-
#articlesContent {
40+
#articlesContent,
41+
#aiToolsContent {
4142
display: block;
4243
}

docs/ui/src/views/header_home.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{{> LogoLink }}
44

55
<div role="tablist" class="spectrum-Tabs spectrum-Tabs--horizontal spectrum-Tabs--quiet spectrum-Tabs--fluid">
6-
<a href="index.html" role="tab" aria-current="page" class="spectrum-Tabs-item app-Tabs-item is-selected">
6+
<a href="index.html" role="tab" class="spectrum-Tabs-item app-Tabs-item">
77
<span class="spectrum-Tabs-itemLabel">
88
<svg xmlns="http://www.w3.org/2000/svg" class="spectrum-Icon spectrum-Icon--sizeS" viewBox="0 0 36 36" focusable="false" aria-hidden="true" aria-label="Home">
99
<path d="M35.332 20.25L18.75 3.668a1.063 1.063 0 00-1.5 0L.668 20.25a1.061 1.061 0 000 1.5l1.958 1.957a1 1 0 00.707.293H4v9a1 1 0 001 1h8a1 1 0 001-1V23a1 1 0 011-1h6a1 1 0 011 1v10a1 1 0 001 1h8a1 1 0 001-1v-9h.667a1 1 0 00.707-.293l1.958-1.957a1.061 1.061 0 000-1.5z"/>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<div class="sideNavContent" id="aiToolsContent">
2+
<div class="spectrum-Typography">
3+
<h1 class="spectrum-Heading spectrum-Heading--sizeXXL spectrum-Heading--heavy">AI tooling</h1>
4+
<hr class="spectrum-Divider spectrum-Divider--sizeM" />
5+
<p class="spectrum-Body spectrum-Body--sizeL">
6+
Leonardo provides three layers of AI tooling so developers and LLMs can get contextual help building accessible color systems.
7+
</p>
8+
9+
<h2 class="spectrum-Heading spectrum-Heading--sizeXL spectrum-Heading--heavy" id="ai-tools-llms-txt"><code>llms.txt</code></h2>
10+
<p class="spectrum-Body spectrum-Body--sizeM">
11+
A lightweight file at the repository root that lets any LLM orient itself to Leonardo’s purpose, package structure, and API without reading source. It covers the public API, supported colorspaces and output formats, contrast methods (WCAG 2 and APCA), and a quick usage example.
12+
</p>
13+
<p class="spectrum-Body spectrum-Body--sizeM">
14+
<strong>Location:</strong> <code>llms.txt</code> at the repo root. See the <a href="https://github.com/adobe/leonardo/blob/main/llms.txt" class="spectrum-Link" target="_blank" rel="noopener">file on GitHub</a>.
15+
</p>
16+
17+
<h2 class="spectrum-Heading spectrum-Heading--sizeXL spectrum-Heading--heavy" id="ai-tools-agent-skill">Agent Skill</h2>
18+
<p class="spectrum-Body spectrum-Body--sizeM">
19+
A structured skill following the <a href="https://agentskills.io/specification" class="spectrum-Link" target="_blank" rel="noopener">agentskills.io</a> spec so tools like Cursor can activate targeted guidance when helping with <code>@adobe/leonardo-contrast-colors</code>. The skill covers installation, creating colors and themes, reading output, mutating themes at runtime, utility functions, and common accessibility recipes (AA, AAA, large text, APCA).
20+
</p>
21+
<p class="spectrum-Body spectrum-Body--sizeM">
22+
<strong>Location:</strong> <code>skills/leonardo-colors/</code> in the repo (<code>SKILL.md</code> and <code>references/api.md</code>). Install with:
23+
</p>
24+
<pre><code class="lang-bash">npx skills add https://github.com/adobe/leonardo
25+
</code></pre>
26+
27+
<h2 class="spectrum-Heading spectrum-Heading--sizeXL spectrum-Heading--heavy" id="ai-tools-mcp">MCP server</h2>
28+
<p class="spectrum-Body spectrum-Body--sizeM">
29+
<code>@adobe/leonardo-mcp</code> is a runtime MCP (Model Context Protocol) server that wraps the library. AI assistants can call tools to generate themes, check contrast, convert colors, and create palettes—producing verified, runnable output instead of guessing from docs.
30+
</p>
31+
<p class="spectrum-Body spectrum-Body--sizeM">
32+
<strong>Tools:</strong>
33+
</p>
34+
<ul class="spectrum-Body spectrum-Body--sizeM">
35+
<li><strong>generate-theme</strong> — Color definitions + background, lightness, contrast, etc. → <code>theme.contrastColors</code> JSON</li>
36+
<li><strong>check-contrast</strong> — Foreground and background colors → ratio and WCAG 2 AA/AAA pass/fail or APCA Lc</li>
37+
<li><strong>convert-color</strong> — Color value + target format → converted string</li>
38+
<li><strong>create-palette</strong> — Color keys + colorspace + steps → interpolated color array</li>
39+
</ul>
40+
<p class="spectrum-Body spectrum-Body--sizeM">
41+
<strong>Run locally (from repo root):</strong>
42+
</p>
43+
<pre><code class="lang-bash">npx @adobe/leonardo-mcp
44+
</code></pre>
45+
<p class="spectrum-Body spectrum-Body--sizeM">
46+
For Cursor, add the Leonardo MCP server in <code>.cursor/mcp.json</code>; see the repo’s <a href="https://github.com/adobe/leonardo/blob/main/.cursor/mcp.json" class="spectrum-Link" target="_blank" rel="noopener">example config</a>.
47+
</p>
48+
</div>
49+
50+
{{> footer }}
51+
</div>

docs/ui/src/views/home_sidenav_links.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,8 @@
99
<li class="spectrum-SideNav-item">
1010
<a href="articles.html" class="spectrum-SideNav-itemLink" data-page="articles">Articles</a>
1111
</li>
12+
<li class="spectrum-SideNav-item">
13+
<a href="ai-tools.html" class="spectrum-SideNav-itemLink" data-page="ai-tools">AI tooling</a>
14+
</li>
1215
</ul>
1316
</nav>

docs/ui/vite.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export default {
5050
index: resolve(__dirname, 'src/index.html'),
5151
api: resolve(__dirname, 'src/api.html'),
5252
articles: resolve(__dirname, 'src/articles.html'),
53+
aiTools: resolve(__dirname, 'src/ai-tools.html'),
5354
theme: resolve(__dirname, 'src/theme.html'),
5455
scales: resolve(__dirname, 'src/scales.html'),
5556
tools: resolve(__dirname, 'src/tools.html'),

0 commit comments

Comments
 (0)