@progress/kendo-angular-mcp 1.5.0 → 1.5.1-beta.8b4a77bb.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/README.md +16 -72
- package/dist/index.js +2870 -226
- package/package.json +1 -1
- package/proto/service.proto +66 -0
package/dist/index.js
CHANGED
|
@@ -1,15 +1,890 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{McpServer as kt}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as St}from"@modelcontextprotocol/sdk/server/stdio.js";import{dirname as ct,join as dt}from"path";import{fileURLToPath as pt}from"url";import{loadLicense as Ze,decodeLicenseKey as Xe,decodeLicenseEvidence as et}from"@progress/kendo-licensing/utils";var q=class extends Error{constructor(t){super(t),this.name="LicenseError"}};function te(e){return e.replace(/^\uFEFF/,"")}async function oe(e){let t=null,s=null;try{let o=Ze();if(!o.success){let n=new q(o.message);throw e&&await e("license_load_failed",{error:o.message}).catch(()=>{}),n}try{t=(await Xe(o.value)).licenses.find(async a=>!!(await et(a)).userId)}catch(n){console.log(n.message),e&&await e("license_load_failed",{error:n.message||"Unknown decoding error"}).catch(()=>{})}if(!t)return[te(o.value),null]}catch(o){return s=o.message||"Unknown error loading license.",e&&await e("license_load_failed",{error:s}).catch(()=>{}),[null,s]}return[te(t),null]}import P from"node:fs";import G from"node:path";var B=!1,E=process.env.DEBUG_LOG_FILE,ne=!!(E&&typeof E=="string");if(ne){let e=G.resolve(E),t=G.dirname(e);if(t!=="."&&t!==process.cwd()&&!P.existsSync(t))try{P.mkdirSync(t,{recursive:!0})}catch(s){console.error("Error creating log directory:",s)}if(!P.existsSync(e))try{P.writeFileSync(e,"")}catch(s){console.error("Error creating log file:",s)}}else B=!1;B=ne&&!!E&&P.existsSync(G.resolve(E));function m(...e){if(B&&E){let o=`${new Date().toISOString()}: ${e.join(" ")}
|
|
3
|
-
`,n=G.resolve(E);P.appendFile(n,o,a=>{a&&console.error("Error writing to log file:",a)})}}import tt from"@grpc/proto-loader";import O from"@grpc/grpc-js";import ae from"path";import se from"fs";import{fileURLToPath as ot}from"url";var nt=ot(import.meta.url),st=ae.dirname(nt),ie={version:"unknown"},re=ae.resolve(st,"../package.json");se.existsSync(re)&&(ie=JSON.parse(se.readFileSync(re,"utf-8")));function rt(e){return e.replace(/\u2014/g,"--").replace(/\u2013/g,"-").replace(/[^\x20-\x7E]/g,"")}var M=class{constructor(t,s,o){this.contextApiUrl=t;this.protoPath=s;this.options=o;this.toolNameHeader="assistant";let n=tt.loadSync(this.protoPath,{keepCase:!0,longs:String,enums:String,defaults:!0,oneofs:!0});this.isDev=this.contextApiUrl?.includes("localhost")||this.contextApiUrl?.includes("127.0.0.1"),m("Using ContextApi URl:",this.contextApiUrl);let a=O.loadPackageDefinition(n);this.client=new a.ContextQueryService(this.contextApiUrl||"contextapi.telerik.com:443",this.isDev?O.credentials.createInsecure():O.credentials.createSsl())}async getMetadata({meta:t}={meta:{}}){let[s,o]=this.isDev?["fake_license",null]:await oe((g,A)=>this.logEvent(g,A)),n=process.env.CONTEXTAPI_API_KEY,a=n&&typeof n=="string",r=s&&typeof s=="string";if(!r&&!a&&o)throw new Error(`Failed to authenticate with API key or License key!
|
|
4
2
|
|
|
5
|
-
License Error: ${o}`);let i=new O.Metadata;if(r)try{i.add("x-license-key",s)}catch(g){throw m("Error adding license key to metadata:",g),new Error(`Failed to use license key: ${g.message}`)}a&&i.add("x-api-key",n),this.options?.libName&&i.add("x-library-name",this.options.libName),i.add("x-tool-name",this.toolNameHeader);let d=JSON.stringify({...t,source:"npm",version:ie.version,os:process.platform,node_version:process.version});return i.add("x-request-metadata",rt(d)),i}async query(t,s){let o=await this.getMetadata({meta:s});return new Promise((n,a)=>{this.client.query(t,o,(r,i)=>{r?a(new Error(r)):n(i)})})}async getApiRefMatches(t,s){let o=await this.getMetadata({meta:s});return new Promise((n,a)=>{this.client.getApiRefMatches(t,o,(r,i)=>{r?a(new Error(r)):n(i)})})}async queryCssUtils(t,s){let o=await this.getMetadata({meta:s});return new Promise((n,a)=>{this.client.cssUtilsSearch(t,o,(r,i)=>{r?(m("Error querying CSS Utils:",r),a(new Error(r))):n(i)})})}async queryIconsSearch(t,s){let o=await this.getMetadata({meta:s});return new Promise((n,a)=>{this.client.iconsSearch(t,o,(r,i)=>{r?(m("Error querying Icons Search:",r),a(new Error(r))):n(i)})})}async queryBuildingBlocks(t,s){let o=await this.getMetadata({meta:s});return new Promise((n,a)=>{this.client.buildingBlocksSearch(t,o,(r,i)=>{r?(m("Error querying Building Blocks:",r),a(new Error(r))):n(i)})})}async validateUserLicense(){let t=await this.getMetadata();return new Promise((s,o)=>{this.client.validateUserLicense(null,t,(n,a)=>{n?(m("Error validating user license:",n),o(new Error(n))):s(a)})})}async queryThemeGenerate(t,s){let o=await this.getMetadata({meta:s});return new Promise((n,a)=>{this.client.generateTheme(t,o,(r,i)=>{r?(m("Error querying context API/GenerateTheme:",r),a(new Error(r))):n(i)})})}async recordOrchestratorUserRequest({meta:t}){let s=await this.getMetadata({meta:t});return new Promise((o,n)=>{this.client.recordOrchestratorUserRequest(null,s,(a,r)=>{a?(m("Error recording orchestrator user request:",a),n(new Error(a))):o(r)})})}async recordToolUserRequest({toolName:t,meta:s}){let o=await this.getMetadata({meta:s});return new Promise((n,a)=>{this.client.recordToolUserRequest({tool_name:t},o,(r,i)=>{r?(m("Error recording tool user request:",r),a(new Error(r))):n(i)})})}async logEvent(t,s){let o="h8utJ6rk4PpIWX4JMn9beQDsjVUri6KV0XYfiVwq";if(!o||typeof o!="string"){m("LOGGING_API_KEY not configured, skipping log event");return}let n=new O.Metadata;return n.add("x-api-key",o),new Promise((a,r)=>{this.client.logEvent({event_name:t,fields:s||{}},n,(i,d)=>{i?(m("Error logging event:",i),r(new Error(i))):a()})})}};import{z as F}from"zod";var Vt=F.object({user_id:F.string(),user_email:F.string().email()});import{z as k}from"zod";var at=k.object({Product:k.object({Id:k.number().int(),Code:k.string(),Name:k.string()}),License:k.nullable(k.object({Id:k.number().int(),Name:k.string(),Type:k.string(),Active:k.boolean()})),Availability:k.array(k.object({LicenseStart:k.nullable(k.string().datetime()),LicenseEnd:k.nullable(k.string().datetime())})),IsLicenseAvailable:k.optional(k.boolean())}),Yt=k.array(at);async function le(e){let t=process.env.KENDO_MCP_ACCESS;if(t==="true"||t==="1"||t==="full")return"full";if(typeof t=="string"&&(t.toLowerCase()==="agent"||t.toLowerCase()==="assistant"))return t.toLowerCase();let s=await e();return s.toLocaleLowerCase()==="agentic_ui_generator"?"agent":s.toLocaleLowerCase()==="ai_coding_assistants"?"assistant":"none"}function ce(){return'**Accessibility Guidelines: WCAG 2.2 Level AA Compliance**\n\n**Core Principles:**\n1. Use semantic HTML and ARIA correctly\n2. Ensure WCAG 2.2 AA compliance\n3. Follow W3C ARIA Authoring Practices\n4. Provide standards-based accessibility fixes\n\n**1. Form Control Labels** (WCAG 1.3.1, 3.3.2, 4.1.2)\nEvery input must have an associated label.\nCORRECT: `<input type="search" aria-label="Search products" placeholder="Search...">`\nWRONG: `<span>Email</span><input type="email">` (no association)\nWRONG: `<input type="email" placeholder="Email">` (placeholder as label)\n\n**2. Button Accessibility** (WCAG 4.1.2, 2.4.6, 2.5.3)\nAll buttons need accessible names describing their purpose.\nCORRECT: `<button type="button">Save Changes</button>`\nCORRECT: `<button aria-label="Close dialog"><svg aria-hidden="true" focusable="false">...</svg></button>`\nWRONG: `<button><svg>...</svg></button>` (no accessible name)\nWRONG: `<button>Click here</button>` (generic text)\n\n**3. Popup/Menu Triggers** (WCAG 4.1.2, 1.3.1)\nIndicate popup purpose and state using aria-haspopup, aria-expanded, aria-controls.\nCORRECT: `<button aria-expanded="false" aria-haspopup="menu" aria-controls="menu-id">Settings</button>`\n\n**4. Image Accessibility** (WCAG 1.1.1)\nInformative: `<img src="chart.png" alt="Sales increased 25% in Q4 2024">`\nFunctional: `<a href="/home"><img src="logo.png" alt="Company Name Home"></a>`\nDecorative: `<img src="border.png" alt="" role="presentation">`\nComplex: `<img src="chart.png" alt="Sales data" aria-describedby="desc-id">`\nWRONG: Missing alt, filename as alt, generic alt for decorative images\n\n**5. Link Accessibility** (WCAG 2.4.4, 2.4.6)\nLinks must be descriptive and understandable out of context.\nCORRECT: `<a href="/products">View All Products</a>`\nCORRECT: `<a href="/article" aria-label="Read more about New Product Launch">Read more</a>`\nWRONG: "Click here", "Read more", "Learn more" without context\n\n**6. Form Validation** (WCAG 3.3.1, 3.3.2, 3.3.3)\nErrors must be identified, associated with inputs, and provide suggestions. Use aria-describedby, aria-invalid="true", and role="alert".\n\n**7. Heading Structure** (WCAG 1.3.1, 2.4.6)\nEach page should have an `<h1>` that describes its content. Use hierarchical, descriptive headings with no skipped levels (h1\u2192h2\u2192h3).\nCORRECT: `<h1>Dashboard</h1> <h2>Sales</h2> <h3>By Region</h3>`\nWRONG: No `<h1>` on the page, or skipping levels (h1\u2192h3)\nIf a heading should look smaller than its semantic level, use CSS: `<h2 class="text-sm">Subtitle</h2>`\n\n**8. Page Component Landmark Rules** (WCAG 1.3.1, 4.1.2)\nPage components (route components) may be rendered inside a layout that already provides `<main>`, `<header>`, or `<footer>`. Since the layout configuration is unknown, avoid using these landmarks in page components to prevent potential duplication and nesting violations. Use `<div>`, `<section>`, or `<article>` as the top-level wrapper instead.\n\n**Quick Checklist:**\n- **Forms:** Associated labels, aria-required on required fields, autocomplete attributes, aria-describedby for errors, aria-invalid for invalid fields, fieldsets with legends\n- **Buttons/Links:** Accessible names, aria-label for icon buttons, aria-hidden on decorative icons, descriptive link text\n- **Popups/Dialogs:** aria-haspopup, aria-expanded, aria-controls, role="dialog", aria-modal="true", aria-labelledby, focus trap, Escape to close\n- **Images:** alt on all images, descriptive alt for informative, empty alt for decorative, aria-describedby for complex\n- **Structure:** One h1, logical heading hierarchy, avoid `<main>`, `<header>`, or `<footer>` in page/route components (the layout may already provide them), use `<section>` or `<div>` as page-level wrappers, semantic HTML\n- **Dynamic Content:** role="status"/aria-live="polite" for updates, role="alert"/aria-live="assertive" for critical alerts\n- **Keyboard/Focus:** All interactive elements keyboard accessible, logical focus order, visible focus indicators, no keyboard traps (except modals)\n- **Color/Contrast:** 4.5:1 text contrast (3:1 large text), 3:1 UI component contrast, no color-only information, 3:1 focus indicator contrast'}var it=1440*60*1e3;function de(e){let{serverName:t,contextApi:s,additionalFields:o={},intervalMs:n=it}=e,a="mcp_server_heartbeat",r=async()=>{try{let d={server_name:t,timestamp:new Date().toISOString(),...o};await s.logEvent(a,d)}catch(d){m(`Failed to send heartbeat for ${t}:`,d)}};r();let i=setInterval(r,n);return()=>{clearInterval(i),m(`Heartbeat stopped for ${t}`)}}import{z as K}from"zod";var lt=["default","bootstrap","material","fluent"];function pe(e,t){return{projectName:K.string().default(t).describe("The name of the project. Used as the project folder name when creating a new project."),createNewProject:K.boolean().default(!0).describe(`Whether to create a new project before configuring ${e}. Set to true to scaffold a brand-new project, or false if the project already exists and you only want to add ${e} to it.`),theme:K.enum(lt).default("default").describe("The Kendo UI theme to use. Determines which theme package is installed and imported.")}}var c="Kendo UI for Angular",w="angular",N="kendo",$=["AIPrompt","ActionSheet","AppBar","ArcGauge","AutoComplete","Avatar","Badge","Barcode","BottomNavigation","BreadCrumb","Button","ButtonGroup","Calendar","Card","ChartWizard","Charts","CheckBox","Chip","ChipList","ChunkProgressBar","CircularGauge","CircularProgressBar","ColorGradient","ColorPalette","ColorPicker","ComboBox","ContextMenu","ConversationalUI","DataQuery","DateInput","DateMath","DatePicker","DateRange","DateTimePicker","Diagram","Dialog","DragandDrop","Drawer","Drawing","DropDownButton","DropDownList","DropDownTree","Editor","ExcelExport","ExpansionPanel","FileSaver","FileSelect","Filter","FlatColorPicker","FloatingActionButton","FloatingLabel","Forms","FormField","Gantt","Gauges","General","Grid","GridLayout","Icon","InlineAIPrompt","Label","LinearGauge","ListBox","ListView","Loader","Map","MaskedTextBox","Menu","MultiColumnComboBox","MultiSelect","MultiSelectTree","MultiViewCalendar","Notification","NumericTextBox","OTPInput","Pager","PanelBar","PDFExport","PDFViewer","PivotGrid","Popover","Popup","ProgressBar","PromptBox","QRCode","RadialGauge","RadioButton","RangeSlider","Rating","Ripple","Sankey","Scheduler","ScrollView","Signature","Skeleton","Slider","SmartPasteButton","Sortable","StockChart","Sparkline","SpeechToTextButton","SplitButton","Splitter","Spreadsheet","StackLayout","Stepper","SVGIcon","Switch","TabStrip","TextArea","TextBox","TileLayout","Timeline","TimePicker","ToolBar","Tooltip","TreeList","TreeView","Typography","Upload","Window"],ue=new Map([["actionsheet","@progress/kendo-angular-navigation"],["aiprompt","@progress/kendo-angular-conversational-ui"],["appbar","@progress/kendo-angular-navigation"],["arcgauge","@progress/kendo-angular-gauges"],["autocomplete","@progress/kendo-angular-dropdowns"],["avatar","@progress/kendo-angular-layout"],["badge","@progress/kendo-angular-indicators"],["barcode","@progress/kendo-angular-barcodes"],["bottomnavigation","@progress/kendo-angular-navigation"],["breadcrumb","@progress/kendo-angular-navigation"],["button","@progress/kendo-angular-buttons"],["buttongroup","@progress/kendo-angular-buttons"],["calendar","@progress/kendo-angular-dateinputs"],["card","@progress/kendo-angular-layout"],["chartwizard","@progress/kendo-angular-chart-wizard"],["checkbox","@progress/kendo-angular-inputs"],["chip","@progress/kendo-angular-buttons"],["chiplist","@progress/kendo-angular-buttons"],["chunkprogressbar","@progress/kendo-angular-progressbar"],["circulargauge","@progress/kendo-angular-gauges"],["circularprogressbar","@progress/kendo-angular-progressbar"],["colorgradient","@progress/kendo-angular-inputs"],["colorpalette","@progress/kendo-angular-inputs"],["colorpicker","@progress/kendo-angular-inputs"],["combobox","@progress/kendo-angular-dropdowns"],["contextmenu","@progress/kendo-angular-menu"],["dataquery","@progress/kendo-data-query"],["dateinput","@progress/kendo-angular-dateinputs"],["datemath","@progress/kendo-date-math"],["datepicker","@progress/kendo-angular-dateinputs"],["daterangepicker","@progress/kendo-angular-dateinputs"],["datetimepicker","@progress/kendo-angular-dateinputs"],["diagram","@progress/kendo-angular-diagrams"],["dialog","@progress/kendo-angular-dialog"],["drawer","@progress/kendo-angular-layout"],["drawing","@progress/kendo-drawing"],["dropdownbutton","@progress/kendo-angular-buttons"],["dropdownlist","@progress/kendo-angular-dropdowns"],["dropdowntree","@progress/kendo-angular-dropdowns"],["editor","@progress/kendo-angular-editor"],["excelexport","@progress/kendo-angular-excel-export"],["expansionpanel","@progress/kendo-angular-layout"],["filesaver","@progress/kendo-file-saver"],["fileselect","@progress/kendo-angular-upload"],["filter","@progress/kendo-angular-filter"],["flatcolorpicker","@progress/kendo-angular-inputs"],["floatingactionbutton","@progress/kendo-angular-buttons"],["floatinglabel","@progress/kendo-angular-label"],["formfield","@progress/kendo-angular-inputs"],["forms","@progress/kendo-angular-inputs"],["gantt","@progress/kendo-angular-gantt"],["grid","@progress/kendo-angular-grid"],["gridlayout","@progress/kendo-angular-layout"],["icon","@progress/kendo-angular-icons"],["inlineaiprompt","@progress/kendo-angular-conversational-ui"],["label","@progress/kendo-angular-label"],["lineargauge","@progress/kendo-angular-gauges"],["listbox","@progress/kendo-angular-listbox"],["listview","@progress/kendo-angular-listview"],["loader","@progress/kendo-angular-indicators"],["map","@progress/kendo-angular-map"],["maskedtextbox","@progress/kendo-angular-inputs"],["menu","@progress/kendo-angular-menu"],["multicolumncombobox","@progress/kendo-angular-dropdowns"],["multiselect","@progress/kendo-angular-dropdowns"],["multiselecttree","@progress/kendo-angular-dropdowns"],["multiviewcalendar","@progress/kendo-angular-dateinputs"],["notification","@progress/kendo-angular-notification"],["numerictextbox","@progress/kendo-angular-inputs"],["otpinput","@progress/kendo-angular-inputs"],["pager","@progress/kendo-angular-pager"],["panelbar","@progress/kendo-angular-layout"],["pdfexport","@progress/kendo-angular-pdf-export"],["pdfviewer","@progress/kendo-angular-pdfviewer"],["pivotgrid","@progress/kendo-angular-pivotgrid"],["popover","@progress/kendo-angular-tooltip"],["popup","@progress/kendo-angular-popup"],["progressbar","@progress/kendo-angular-progressbar"],["promptbox","@progress/kendo-angular-conversational-ui"],["qrcode","@progress/kendo-angular-barcodes"],["radialgauge","@progress/kendo-angular-gauges"],["radiobutton","@progress/kendo-angular-inputs"],["rangeslider","@progress/kendo-angular-inputs"],["rating","@progress/kendo-angular-inputs"],["ripple","@progress/kendo-angular-ripple"],["sankey","@progress/kendo-angular-charts"],["scheduler","@progress/kendo-angular-scheduler"],["scrollview","@progress/kendo-angular-scrollview"],["signature","@progress/kendo-angular-inputs"],["skeleton","@progress/kendo-angular-indicators"],["slider","@progress/kendo-angular-inputs"],["smartpastebutton","@progress/kendo-angular-buttons"],["sortable","@progress/kendo-angular-sortable"],["sparkline","@progress/kendo-angular-charts"],["speechtotextbutton","@progress/kendo-angular-buttons"],["splitbutton","@progress/kendo-angular-buttons"],["splitter","@progress/kendo-angular-layout"],["spreadsheet","@progress/kendo-angular-spreadsheet"],["stacklayout","@progress/kendo-angular-layout"],["stepper","@progress/kendo-angular-layout"],["stockchart","@progress/kendo-angular-charts"],["svgicon","@progress/kendo-angular-icons"],["switch","@progress/kendo-angular-inputs"],["tabstrip","@progress/kendo-angular-layout"],["textarea","@progress/kendo-angular-inputs"],["textbox","@progress/kendo-angular-inputs"],["tilelayout","@progress/kendo-angular-layout"],["timeline","@progress/kendo-angular-layout"],["timepicker","@progress/kendo-angular-dateinputs"],["toolbar","@progress/kendo-angular-toolbar"],["treelist","@progress/kendo-angular-treelist"],["treeview","@progress/kendo-angular-treeview"],["typography","@progress/kendo-angular-typography"],["upload","@progress/kendo-angular-upload"],["window","@progress/kendo-angular-dialog"],["charts","@progress/kendo-angular-charts"],["conversationalui","@progress/kendo-angular-conversational-ui"],["tooltip","@progress/kendo-angular-tooltip"]]);var ut=dt(ct(pt(import.meta.url)),"../proto/service.proto"),_=new M("contextapi.telerik.com:443",ut,{libName:w});var me=async()=>_.validateUserLicense();import Ct from"fs";import Ye from"path";import{fileURLToPath as vt}from"url";import{z as b}from"zod";var v=class{constructor(t,s,o){this.name=t;this.config=s;this.cb=o}register(t){this.server=t,this.server.registerTool(this.name,this.config,this.cb)}hasAccess(t){return!this.config.requiredAccess||this.config.requiredAccess.length===0?!0:t==="full"||this.config.requiredAccess.includes(t)}};var U=(e,t)=>`# ${e.suiteName} - Code Validation & Quality Assurance
|
|
3
|
+
// packages/angular/src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
|
|
7
|
+
// packages/angular/src/lib/grpc-client.ts
|
|
8
|
+
import { dirname, join } from "path";
|
|
9
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10
|
+
|
|
11
|
+
// packages/mcp-common/src/lib/read-license.ts
|
|
12
|
+
import {
|
|
13
|
+
loadLicense,
|
|
14
|
+
decodeLicenseKey,
|
|
15
|
+
decodeLicenseEvidence
|
|
16
|
+
} from "@progress/kendo-licensing/utils";
|
|
17
|
+
var LicenseError = class extends Error {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "LicenseError";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
function fixBom(licenseKey) {
|
|
24
|
+
return licenseKey.replace(/^\uFEFF/, "");
|
|
25
|
+
}
|
|
26
|
+
async function readLicense(logCallback) {
|
|
27
|
+
let licenseEvidence = null;
|
|
28
|
+
let error = null;
|
|
29
|
+
try {
|
|
30
|
+
const license = loadLicense();
|
|
31
|
+
if (!license.success) {
|
|
32
|
+
const licenseError = new LicenseError(license.message);
|
|
33
|
+
if (logCallback) {
|
|
34
|
+
await logCallback("license_load_failed", {
|
|
35
|
+
error: license.message
|
|
36
|
+
}).catch(() => {
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
throw licenseError;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const decodedLicense = await decodeLicenseKey(license.value);
|
|
43
|
+
licenseEvidence = decodedLicense.licenses.find(async (lic) => {
|
|
44
|
+
const evidence = await decodeLicenseEvidence(lic);
|
|
45
|
+
if (evidence.userId) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
});
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.log(err.message);
|
|
52
|
+
if (logCallback) {
|
|
53
|
+
await logCallback("license_load_failed", {
|
|
54
|
+
error: err.message || "Unknown decoding error"
|
|
55
|
+
}).catch(() => {
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!licenseEvidence) {
|
|
60
|
+
return [fixBom(license.value), null];
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
error = err.message || "Unknown error loading license.";
|
|
64
|
+
if (logCallback) {
|
|
65
|
+
await logCallback("license_load_failed", {
|
|
66
|
+
error
|
|
67
|
+
}).catch(() => {
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return [null, error];
|
|
71
|
+
}
|
|
72
|
+
return [fixBom(licenseEvidence), null];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// packages/mcp-common/src/lib/logger.ts
|
|
76
|
+
import fs from "node:fs";
|
|
77
|
+
import path from "node:path";
|
|
78
|
+
var LOG_ENABLED = false;
|
|
79
|
+
var LOG_FILE = process.env.DEBUG_LOG_FILE;
|
|
80
|
+
var LOG_FILE_DEFINED = !!(LOG_FILE && typeof LOG_FILE === "string");
|
|
81
|
+
if (LOG_FILE_DEFINED) {
|
|
82
|
+
const resolvedLogFile = path.resolve(LOG_FILE);
|
|
83
|
+
const logDir = path.dirname(resolvedLogFile);
|
|
84
|
+
if (logDir !== "." && logDir !== process.cwd() && !fs.existsSync(logDir)) {
|
|
85
|
+
try {
|
|
86
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
87
|
+
} catch (err) {
|
|
88
|
+
console.error("Error creating log directory:", err);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!fs.existsSync(resolvedLogFile)) {
|
|
92
|
+
try {
|
|
93
|
+
fs.writeFileSync(resolvedLogFile, "");
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.error("Error creating log file:", err);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
LOG_ENABLED = false;
|
|
100
|
+
}
|
|
101
|
+
LOG_ENABLED = LOG_FILE_DEFINED && !!LOG_FILE && fs.existsSync(path.resolve(LOG_FILE));
|
|
102
|
+
function log(...args) {
|
|
103
|
+
if (LOG_ENABLED && LOG_FILE) {
|
|
104
|
+
const date = /* @__PURE__ */ new Date();
|
|
105
|
+
const dateString = date.toISOString();
|
|
106
|
+
const logString = `${dateString}: ${args.join(" ")}
|
|
107
|
+
`;
|
|
108
|
+
const resolvedLogFile = path.resolve(LOG_FILE);
|
|
109
|
+
fs.appendFile(resolvedLogFile, logString, (err) => {
|
|
110
|
+
if (err) {
|
|
111
|
+
console.error("Error writing to log file:", err);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// packages/mcp-common/src/lib/context-api.ts
|
|
118
|
+
import protoLoader from "@grpc/proto-loader";
|
|
119
|
+
import grpc from "@grpc/grpc-js";
|
|
120
|
+
import path2 from "path";
|
|
121
|
+
import fs2 from "fs";
|
|
122
|
+
import { fileURLToPath } from "url";
|
|
123
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
124
|
+
var __dirname = path2.dirname(__filename);
|
|
125
|
+
var packageJson = { version: "unknown" };
|
|
126
|
+
var packageJsonPath = path2.resolve(__dirname, "../package.json");
|
|
127
|
+
if (fs2.existsSync(packageJsonPath)) {
|
|
128
|
+
packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
|
|
129
|
+
}
|
|
130
|
+
function sanitizeMetadata(value) {
|
|
131
|
+
return value.replace(/\u2014/g, "--").replace(/\u2013/g, "-").replace(/[^\x20-\x7E]/g, "");
|
|
132
|
+
}
|
|
133
|
+
var D2CError = class extends Error {
|
|
134
|
+
constructor(code, message, details) {
|
|
135
|
+
super(message);
|
|
136
|
+
this.code = code;
|
|
137
|
+
this.details = details;
|
|
138
|
+
this.name = "D2CError";
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
var ContextAPI = class {
|
|
142
|
+
constructor(contextApiUrl, protoPath2, options) {
|
|
143
|
+
this.contextApiUrl = contextApiUrl;
|
|
144
|
+
this.protoPath = protoPath2;
|
|
145
|
+
this.options = options;
|
|
146
|
+
this.toolNameHeader = "assistant";
|
|
147
|
+
const proto = protoLoader.loadSync(this.protoPath, {
|
|
148
|
+
keepCase: true,
|
|
149
|
+
longs: String,
|
|
150
|
+
enums: String,
|
|
151
|
+
defaults: true,
|
|
152
|
+
oneofs: true
|
|
153
|
+
});
|
|
154
|
+
this.isDev = this.contextApiUrl?.includes("localhost") || this.contextApiUrl?.includes("127.0.0.1");
|
|
155
|
+
log("Using ContextApi URl:", this.contextApiUrl);
|
|
156
|
+
const protoDescriptor = grpc.loadPackageDefinition(proto);
|
|
157
|
+
const maxMsgSize = 100 * 1024 * 1024;
|
|
158
|
+
const channelOptions = {
|
|
159
|
+
"grpc.max_receive_message_length": maxMsgSize,
|
|
160
|
+
"grpc.max_send_message_length": maxMsgSize
|
|
161
|
+
};
|
|
162
|
+
this.client = new protoDescriptor.ContextQueryService(
|
|
163
|
+
this.contextApiUrl || "contextapi.telerik.com:443",
|
|
164
|
+
this.isDev ? grpc.credentials.createInsecure() : grpc.credentials.createSsl(),
|
|
165
|
+
channelOptions
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
async getMetadata({ meta } = { meta: {} }) {
|
|
169
|
+
const [license, error] = this.isDev ? ["fake_license", null] : await readLicense((eventName, fields) => this.logEvent(eventName, fields));
|
|
170
|
+
const apiKey = process.env.CONTEXTAPI_API_KEY;
|
|
171
|
+
const hasApiKey = apiKey && typeof apiKey === "string";
|
|
172
|
+
const hasLicense = license && typeof license === "string";
|
|
173
|
+
if (!hasLicense && !hasApiKey && error) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
`Failed to authenticate with API key or License key!
|
|
176
|
+
|
|
177
|
+
License Error: ${error}`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
const metadata = new grpc.Metadata();
|
|
181
|
+
if (hasLicense) {
|
|
182
|
+
try {
|
|
183
|
+
metadata.add("x-license-key", license);
|
|
184
|
+
} catch (error2) {
|
|
185
|
+
log("Error adding license key to metadata:", error2);
|
|
186
|
+
throw new Error(`Failed to use license key: ${error2.message}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (hasApiKey) {
|
|
190
|
+
metadata.add("x-api-key", apiKey);
|
|
191
|
+
}
|
|
192
|
+
if (this.options?.libName) {
|
|
193
|
+
metadata.add("x-library-name", this.options.libName);
|
|
194
|
+
}
|
|
195
|
+
metadata.add("x-tool-name", this.toolNameHeader);
|
|
196
|
+
const requestMetadata = JSON.stringify({
|
|
197
|
+
...meta,
|
|
198
|
+
source: "npm",
|
|
199
|
+
version: packageJson.version,
|
|
200
|
+
os: process.platform,
|
|
201
|
+
node_version: process.version
|
|
202
|
+
});
|
|
203
|
+
metadata.add("x-request-metadata", sanitizeMetadata(requestMetadata));
|
|
204
|
+
return metadata;
|
|
205
|
+
}
|
|
206
|
+
async query(params, meta) {
|
|
207
|
+
const metadata = await this.getMetadata({ meta });
|
|
208
|
+
return new Promise((resolve, reject) => {
|
|
209
|
+
this.client.query(
|
|
210
|
+
params,
|
|
211
|
+
metadata,
|
|
212
|
+
(err, response) => {
|
|
213
|
+
if (err) {
|
|
214
|
+
reject(new Error(err));
|
|
215
|
+
} else {
|
|
216
|
+
resolve(response);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
async getApiRefMatches(params, meta) {
|
|
223
|
+
const metadata = await this.getMetadata({ meta });
|
|
224
|
+
return new Promise((resolve, reject) => {
|
|
225
|
+
this.client.getApiRefMatches(
|
|
226
|
+
params,
|
|
227
|
+
metadata,
|
|
228
|
+
(err, response) => {
|
|
229
|
+
if (err) {
|
|
230
|
+
reject(new Error(err));
|
|
231
|
+
} else {
|
|
232
|
+
resolve(response);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
async queryCssUtils(params, meta) {
|
|
239
|
+
const metadata = await this.getMetadata({ meta });
|
|
240
|
+
return new Promise((resolve, reject) => {
|
|
241
|
+
this.client.cssUtilsSearch(
|
|
242
|
+
params,
|
|
243
|
+
metadata,
|
|
244
|
+
(err, response) => {
|
|
245
|
+
if (err) {
|
|
246
|
+
log("Error querying CSS Utils:", err);
|
|
247
|
+
reject(new Error(err));
|
|
248
|
+
} else {
|
|
249
|
+
resolve(response);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
async queryIconsSearch(params, meta) {
|
|
256
|
+
const metadata = await this.getMetadata({ meta });
|
|
257
|
+
return new Promise((resolve, reject) => {
|
|
258
|
+
this.client.iconsSearch(params, metadata, (err, response) => {
|
|
259
|
+
if (err) {
|
|
260
|
+
log("Error querying Icons Search:", err);
|
|
261
|
+
reject(new Error(err));
|
|
262
|
+
} else {
|
|
263
|
+
resolve(response);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
async queryBuildingBlocks(params, meta) {
|
|
269
|
+
const metadata = await this.getMetadata({ meta });
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
this.client.buildingBlocksSearch(
|
|
272
|
+
params,
|
|
273
|
+
metadata,
|
|
274
|
+
(err, response) => {
|
|
275
|
+
if (err) {
|
|
276
|
+
log("Error querying Building Blocks:", err);
|
|
277
|
+
reject(new Error(err));
|
|
278
|
+
} else {
|
|
279
|
+
resolve(response);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
async validateUserLicense() {
|
|
286
|
+
const metadata = await this.getMetadata();
|
|
287
|
+
return new Promise((resolve, reject) => {
|
|
288
|
+
this.client.validateUserLicense(
|
|
289
|
+
null,
|
|
290
|
+
metadata,
|
|
291
|
+
(err, response) => {
|
|
292
|
+
if (err) {
|
|
293
|
+
log("Error validating user license:", err);
|
|
294
|
+
reject(new Error(err));
|
|
295
|
+
} else {
|
|
296
|
+
resolve(response);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
async queryThemeGenerate(params, meta) {
|
|
303
|
+
const metadata = await this.getMetadata({ meta });
|
|
304
|
+
return new Promise((resolve, reject) => {
|
|
305
|
+
this.client.generateTheme(params, metadata, (err, response) => {
|
|
306
|
+
if (err) {
|
|
307
|
+
log("Error querying context API/GenerateTheme:", err);
|
|
308
|
+
reject(new Error(err));
|
|
309
|
+
} else {
|
|
310
|
+
resolve(response);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
async recordOrchestratorUserRequest({ meta }) {
|
|
316
|
+
const metadata = await this.getMetadata({ meta });
|
|
317
|
+
return new Promise((resolve, reject) => {
|
|
318
|
+
this.client.recordOrchestratorUserRequest(
|
|
319
|
+
null,
|
|
320
|
+
metadata,
|
|
321
|
+
(err, response) => {
|
|
322
|
+
if (err) {
|
|
323
|
+
log("Error recording orchestrator user request:", err);
|
|
324
|
+
reject(new Error(err));
|
|
325
|
+
} else {
|
|
326
|
+
resolve(response);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
async recordToolUserRequest({ toolName, meta }) {
|
|
333
|
+
const metadata = await this.getMetadata({ meta });
|
|
334
|
+
return new Promise((resolve, reject) => {
|
|
335
|
+
this.client.recordToolUserRequest(
|
|
336
|
+
{ tool_name: toolName },
|
|
337
|
+
metadata,
|
|
338
|
+
(err, response) => {
|
|
339
|
+
if (err) {
|
|
340
|
+
log("Error recording tool user request:", err);
|
|
341
|
+
reject(new Error(err));
|
|
342
|
+
} else {
|
|
343
|
+
resolve(response);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
);
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
async logEvent(eventName, fields) {
|
|
350
|
+
const apiKey = "h8utJ6rk4PpIWX4JMn9beQDsjVUri6KV0XYfiVwq";
|
|
351
|
+
if (!apiKey || typeof apiKey !== "string") {
|
|
352
|
+
log("LOGGING_API_KEY not configured, skipping log event");
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const metadata = new grpc.Metadata();
|
|
356
|
+
metadata.add("x-api-key", apiKey);
|
|
357
|
+
return new Promise((resolve, reject) => {
|
|
358
|
+
this.client.logEvent(
|
|
359
|
+
{ event_name: eventName, fields: fields || {} },
|
|
360
|
+
metadata,
|
|
361
|
+
(err, response) => {
|
|
362
|
+
if (err) {
|
|
363
|
+
log("Error logging event:", err);
|
|
364
|
+
reject(new Error(err));
|
|
365
|
+
} else {
|
|
366
|
+
resolve();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Process a Figma design through the D2C pipeline
|
|
374
|
+
*
|
|
375
|
+
* @param params - Figma design data and processing options
|
|
376
|
+
* @returns Processed design with layout, styles, and component matches
|
|
377
|
+
* @throws D2CError if processing fails
|
|
378
|
+
*/
|
|
379
|
+
async processFigmaDesign(params) {
|
|
380
|
+
const metadata = await this.getMetadata();
|
|
381
|
+
const request = {};
|
|
382
|
+
if (params.node_jsons && params.node_jsons.length > 0) {
|
|
383
|
+
request.node_jsons = params.node_jsons;
|
|
384
|
+
} else if (params.root_node_json) {
|
|
385
|
+
request.node_jsons = [params.root_node_json];
|
|
386
|
+
} else {
|
|
387
|
+
throw new Error("Either node_jsons or root_node_json must be provided");
|
|
388
|
+
}
|
|
389
|
+
return new Promise((resolve, reject) => {
|
|
390
|
+
this.client.processFigmaDesign(
|
|
391
|
+
request,
|
|
392
|
+
metadata,
|
|
393
|
+
(err, response) => {
|
|
394
|
+
if (err) {
|
|
395
|
+
log("Error processing Figma design:", err);
|
|
396
|
+
reject(new Error(`gRPC error: ${err.message}`));
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
if (response.error) {
|
|
400
|
+
const error = new D2CError(
|
|
401
|
+
response.error.code,
|
|
402
|
+
response.error.message,
|
|
403
|
+
response.error.details
|
|
404
|
+
);
|
|
405
|
+
log("D2C processing error:", error);
|
|
406
|
+
reject(error);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (response.success) {
|
|
410
|
+
const layoutPseudoCodes = response.success.layout_pseudo_codes || [];
|
|
411
|
+
const result = {
|
|
412
|
+
// Backward compatibility: first layout or empty string
|
|
413
|
+
layout_pseudo_code: layoutPseudoCodes[0] || response.success.layout_pseudo_code || "",
|
|
414
|
+
layout_pseudo_codes: layoutPseudoCodes.length > 0 ? layoutPseudoCodes : [response.success.layout_pseudo_code || ""],
|
|
415
|
+
design_tokens: response.success.design_tokens || "",
|
|
416
|
+
stats: {
|
|
417
|
+
total_nodes: response.success.stats?.total_nodes || 0,
|
|
418
|
+
processing_time_ms: response.success.stats?.processing_time_ms || 0
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
resolve(result);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
reject(new Error("Invalid response format from server"));
|
|
425
|
+
}
|
|
426
|
+
);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
// packages/mcp-common/src/models/user.ts
|
|
432
|
+
import { z } from "zod";
|
|
433
|
+
var userInfoSchema = z.object({
|
|
434
|
+
user_id: z.string(),
|
|
435
|
+
user_email: z.string().email()
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// packages/mcp-common/src/models/entitlements.ts
|
|
439
|
+
import { z as z2 } from "zod";
|
|
440
|
+
var itemSchema = z2.object({
|
|
441
|
+
Product: z2.object({
|
|
442
|
+
Id: z2.number().int(),
|
|
443
|
+
Code: z2.string(),
|
|
444
|
+
Name: z2.string()
|
|
445
|
+
}),
|
|
446
|
+
License: z2.nullable(
|
|
447
|
+
z2.object({
|
|
448
|
+
Id: z2.number().int(),
|
|
449
|
+
Name: z2.string(),
|
|
450
|
+
Type: z2.string(),
|
|
451
|
+
Active: z2.boolean()
|
|
452
|
+
})
|
|
453
|
+
),
|
|
454
|
+
Availability: z2.array(
|
|
455
|
+
z2.object({
|
|
456
|
+
LicenseStart: z2.nullable(z2.string().datetime()),
|
|
457
|
+
LicenseEnd: z2.nullable(z2.string().datetime())
|
|
458
|
+
})
|
|
459
|
+
),
|
|
460
|
+
IsLicenseAvailable: z2.optional(z2.boolean())
|
|
461
|
+
});
|
|
462
|
+
var entitlementsSchema = z2.array(itemSchema);
|
|
463
|
+
|
|
464
|
+
// packages/mcp-common/src/lib/utils.ts
|
|
465
|
+
async function checkAccess(cb) {
|
|
466
|
+
const access = process.env.KENDO_MCP_ACCESS;
|
|
467
|
+
if (access === "true" || access === "1" || access === "full") {
|
|
468
|
+
return "full";
|
|
469
|
+
} else if (typeof access === "string" && (access.toLowerCase() === "agent" || access.toLowerCase() === "assistant")) {
|
|
470
|
+
return access.toLowerCase();
|
|
471
|
+
}
|
|
472
|
+
const productCode = await cb();
|
|
473
|
+
if (productCode.toLocaleLowerCase() === "agentic_ui_generator") {
|
|
474
|
+
return "agent";
|
|
475
|
+
}
|
|
476
|
+
if (productCode.toLocaleLowerCase() === "ai_coding_assistants") {
|
|
477
|
+
return "assistant";
|
|
478
|
+
}
|
|
479
|
+
return "none";
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// packages/mcp-common/src/lib/instructions/accessibility.ts
|
|
483
|
+
function getAccessibilityGuidelines() {
|
|
484
|
+
return `**Accessibility Guidelines: WCAG 2.2 Level AA Compliance**
|
|
485
|
+
|
|
486
|
+
**Core Principles:**
|
|
487
|
+
1. Use semantic HTML and ARIA correctly
|
|
488
|
+
2. Ensure WCAG 2.2 AA compliance
|
|
489
|
+
3. Follow W3C ARIA Authoring Practices
|
|
490
|
+
4. Provide standards-based accessibility fixes
|
|
491
|
+
|
|
492
|
+
**1. Form Control Labels** (WCAG 1.3.1, 3.3.2, 4.1.2)
|
|
493
|
+
Every input must have an associated label.
|
|
494
|
+
CORRECT: \`<input type="search" aria-label="Search products" placeholder="Search...">\`
|
|
495
|
+
WRONG: \`<span>Email</span><input type="email">\` (no association)
|
|
496
|
+
WRONG: \`<input type="email" placeholder="Email">\` (placeholder as label)
|
|
497
|
+
|
|
498
|
+
**2. Button Accessibility** (WCAG 4.1.2, 2.4.6, 2.5.3)
|
|
499
|
+
All buttons need accessible names describing their purpose.
|
|
500
|
+
CORRECT: \`<button type="button">Save Changes</button>\`
|
|
501
|
+
CORRECT: \`<button aria-label="Close dialog"><svg aria-hidden="true" focusable="false">...</svg></button>\`
|
|
502
|
+
WRONG: \`<button><svg>...</svg></button>\` (no accessible name)
|
|
503
|
+
WRONG: \`<button>Click here</button>\` (generic text)
|
|
504
|
+
|
|
505
|
+
**3. Popup/Menu Triggers** (WCAG 4.1.2, 1.3.1)
|
|
506
|
+
Indicate popup purpose and state using aria-haspopup, aria-expanded, aria-controls.
|
|
507
|
+
CORRECT: \`<button aria-expanded="false" aria-haspopup="menu" aria-controls="menu-id">Settings</button>\`
|
|
508
|
+
|
|
509
|
+
**4. Image Accessibility** (WCAG 1.1.1)
|
|
510
|
+
Informative: \`<img src="chart.png" alt="Sales increased 25% in Q4 2024">\`
|
|
511
|
+
Functional: \`<a href="/home"><img src="logo.png" alt="Company Name Home"></a>\`
|
|
512
|
+
Decorative: \`<img src="border.png" alt="" role="presentation">\`
|
|
513
|
+
Complex: \`<img src="chart.png" alt="Sales data" aria-describedby="desc-id">\`
|
|
514
|
+
WRONG: Missing alt, filename as alt, generic alt for decorative images
|
|
515
|
+
|
|
516
|
+
**5. Link Accessibility** (WCAG 2.4.4, 2.4.6)
|
|
517
|
+
Links must be descriptive and understandable out of context.
|
|
518
|
+
CORRECT: \`<a href="/products">View All Products</a>\`
|
|
519
|
+
CORRECT: \`<a href="/article" aria-label="Read more about New Product Launch">Read more</a>\`
|
|
520
|
+
WRONG: "Click here", "Read more", "Learn more" without context
|
|
521
|
+
|
|
522
|
+
**6. Form Validation** (WCAG 3.3.1, 3.3.2, 3.3.3)
|
|
523
|
+
Errors must be identified, associated with inputs, and provide suggestions. Use aria-describedby, aria-invalid="true", and role="alert".
|
|
524
|
+
|
|
525
|
+
**7. Heading Structure** (WCAG 1.3.1, 2.4.6)
|
|
526
|
+
Each page should have an \`<h1>\` that describes its content. Use hierarchical, descriptive headings with no skipped levels (h1\u2192h2\u2192h3).
|
|
527
|
+
CORRECT: \`<h1>Dashboard</h1> <h2>Sales</h2> <h3>By Region</h3>\`
|
|
528
|
+
WRONG: No \`<h1>\` on the page, or skipping levels (h1\u2192h3)
|
|
529
|
+
If a heading should look smaller than its semantic level, use CSS: \`<h2 class="text-sm">Subtitle</h2>\`
|
|
530
|
+
|
|
531
|
+
**8. Page Component Landmark Rules** (WCAG 1.3.1, 4.1.2)
|
|
532
|
+
Page components (route components) may be rendered inside a layout that already provides \`<main>\`, \`<header>\`, or \`<footer>\`. Since the layout configuration is unknown, avoid using these landmarks in page components to prevent potential duplication and nesting violations. Use \`<div>\`, \`<section>\`, or \`<article>\` as the top-level wrapper instead.
|
|
533
|
+
|
|
534
|
+
**Quick Checklist:**
|
|
535
|
+
- **Forms:** Associated labels, aria-required on required fields, autocomplete attributes, aria-describedby for errors, aria-invalid for invalid fields, fieldsets with legends
|
|
536
|
+
- **Buttons/Links:** Accessible names, aria-label for icon buttons, aria-hidden on decorative icons, descriptive link text
|
|
537
|
+
- **Popups/Dialogs:** aria-haspopup, aria-expanded, aria-controls, role="dialog", aria-modal="true", aria-labelledby, focus trap, Escape to close
|
|
538
|
+
- **Images:** alt on all images, descriptive alt for informative, empty alt for decorative, aria-describedby for complex
|
|
539
|
+
- **Structure:** One h1, logical heading hierarchy, avoid \`<main>\`, \`<header>\`, or \`<footer>\` in page/route components (the layout may already provide them), use \`<section>\` or \`<div>\` as page-level wrappers, semantic HTML
|
|
540
|
+
- **Dynamic Content:** role="status"/aria-live="polite" for updates, role="alert"/aria-live="assertive" for critical alerts
|
|
541
|
+
- **Keyboard/Focus:** All interactive elements keyboard accessible, logical focus order, visible focus indicators, no keyboard traps (except modals)
|
|
542
|
+
- **Color/Contrast:** 4.5:1 text contrast (3:1 large text), 3:1 UI component contrast, no color-only information, 3:1 focus indicator contrast`;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// packages/mcp-common/src/lib/heartbeat.ts
|
|
546
|
+
var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
|
|
547
|
+
function startHeartbeat(options) {
|
|
548
|
+
const {
|
|
549
|
+
serverName,
|
|
550
|
+
contextApi: contextApi2,
|
|
551
|
+
additionalFields = {},
|
|
552
|
+
intervalMs = ONE_DAY_MS
|
|
553
|
+
} = options;
|
|
554
|
+
const eventName = `mcp_server_heartbeat`;
|
|
555
|
+
const sendHeartbeat = async () => {
|
|
556
|
+
try {
|
|
557
|
+
const fields = {
|
|
558
|
+
server_name: serverName,
|
|
559
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
560
|
+
...additionalFields
|
|
561
|
+
};
|
|
562
|
+
await contextApi2.logEvent(eventName, fields);
|
|
563
|
+
} catch (error) {
|
|
564
|
+
log(`Failed to send heartbeat for ${serverName}:`, error);
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
sendHeartbeat();
|
|
568
|
+
const intervalId = setInterval(sendHeartbeat, intervalMs);
|
|
569
|
+
return () => {
|
|
570
|
+
clearInterval(intervalId);
|
|
571
|
+
log(`Heartbeat stopped for ${serverName}`);
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// packages/mcp-common/src/lib/getting-started-params.ts
|
|
576
|
+
import { z as z3 } from "zod";
|
|
577
|
+
var KendoThemes = ["default", "bootstrap", "material", "fluent"];
|
|
578
|
+
function createGettingStartedInputSchema(suiteName, defaultProjectName) {
|
|
579
|
+
return {
|
|
580
|
+
projectName: z3.string().default(defaultProjectName).describe(
|
|
581
|
+
"The name of the project. Used as the project folder name when creating a new project."
|
|
582
|
+
),
|
|
583
|
+
createNewProject: z3.boolean().default(true).describe(
|
|
584
|
+
`Whether to create a new project before configuring ${suiteName}. Set to true to scaffold a brand-new project, or false if the project already exists and you only want to add ${suiteName} to it.`
|
|
585
|
+
),
|
|
586
|
+
theme: z3.enum(KendoThemes).default("default").describe(
|
|
587
|
+
"The Kendo UI theme to use. Determines which theme package is installed and imported."
|
|
588
|
+
)
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// packages/angular/src/lib/constants.ts
|
|
593
|
+
var ANGULAR_SUITE_NAME = "Kendo UI for Angular";
|
|
594
|
+
var Suite = "angular";
|
|
595
|
+
var PRODUCT_LINE = "kendo";
|
|
596
|
+
var AngularComponents = [
|
|
597
|
+
"AIPrompt",
|
|
598
|
+
"ActionSheet",
|
|
599
|
+
"AppBar",
|
|
600
|
+
"ArcGauge",
|
|
601
|
+
"AutoComplete",
|
|
602
|
+
"Avatar",
|
|
603
|
+
"Badge",
|
|
604
|
+
"Barcode",
|
|
605
|
+
"BottomNavigation",
|
|
606
|
+
"BreadCrumb",
|
|
607
|
+
"Button",
|
|
608
|
+
"ButtonGroup",
|
|
609
|
+
"Calendar",
|
|
610
|
+
"Card",
|
|
611
|
+
"ChartWizard",
|
|
612
|
+
"Charts",
|
|
613
|
+
"CheckBox",
|
|
614
|
+
"Chip",
|
|
615
|
+
"ChipList",
|
|
616
|
+
"ChunkProgressBar",
|
|
617
|
+
"CircularGauge",
|
|
618
|
+
"CircularProgressBar",
|
|
619
|
+
"ColorGradient",
|
|
620
|
+
"ColorPalette",
|
|
621
|
+
"ColorPicker",
|
|
622
|
+
"ComboBox",
|
|
623
|
+
"ContextMenu",
|
|
624
|
+
"ConversationalUI",
|
|
625
|
+
"DataQuery",
|
|
626
|
+
"DateInput",
|
|
627
|
+
"DateMath",
|
|
628
|
+
"DatePicker",
|
|
629
|
+
"DateRange",
|
|
630
|
+
"DateTimePicker",
|
|
631
|
+
"Diagram",
|
|
632
|
+
"Dialog",
|
|
633
|
+
"DragandDrop",
|
|
634
|
+
"Drawer",
|
|
635
|
+
"Drawing",
|
|
636
|
+
"DropDownButton",
|
|
637
|
+
"DropDownList",
|
|
638
|
+
"DropDownTree",
|
|
639
|
+
"Editor",
|
|
640
|
+
"ExcelExport",
|
|
641
|
+
"ExpansionPanel",
|
|
642
|
+
"FileSaver",
|
|
643
|
+
"FileSelect",
|
|
644
|
+
"Filter",
|
|
645
|
+
"FlatColorPicker",
|
|
646
|
+
"FloatingActionButton",
|
|
647
|
+
"FloatingLabel",
|
|
648
|
+
"Forms",
|
|
649
|
+
"FormField",
|
|
650
|
+
"Gantt",
|
|
651
|
+
"Gauges",
|
|
652
|
+
"General",
|
|
653
|
+
"Grid",
|
|
654
|
+
"GridLayout",
|
|
655
|
+
"Icon",
|
|
656
|
+
"InlineAIPrompt",
|
|
657
|
+
"Label",
|
|
658
|
+
"LinearGauge",
|
|
659
|
+
"ListBox",
|
|
660
|
+
"ListView",
|
|
661
|
+
"Loader",
|
|
662
|
+
"Map",
|
|
663
|
+
"MaskedTextBox",
|
|
664
|
+
"Menu",
|
|
665
|
+
"MultiColumnComboBox",
|
|
666
|
+
"MultiSelect",
|
|
667
|
+
"MultiSelectTree",
|
|
668
|
+
"MultiViewCalendar",
|
|
669
|
+
"Notification",
|
|
670
|
+
"NumericTextBox",
|
|
671
|
+
"OTPInput",
|
|
672
|
+
"Pager",
|
|
673
|
+
"PanelBar",
|
|
674
|
+
"PDFExport",
|
|
675
|
+
"PDFViewer",
|
|
676
|
+
"PivotGrid",
|
|
677
|
+
"Popover",
|
|
678
|
+
"Popup",
|
|
679
|
+
"ProgressBar",
|
|
680
|
+
"PromptBox",
|
|
681
|
+
"QRCode",
|
|
682
|
+
"RadialGauge",
|
|
683
|
+
"RadioButton",
|
|
684
|
+
"RangeSlider",
|
|
685
|
+
"Rating",
|
|
686
|
+
"Ripple",
|
|
687
|
+
"Sankey",
|
|
688
|
+
"Scheduler",
|
|
689
|
+
"ScrollView",
|
|
690
|
+
"Signature",
|
|
691
|
+
"Skeleton",
|
|
692
|
+
"Slider",
|
|
693
|
+
"SmartPasteButton",
|
|
694
|
+
"Sortable",
|
|
695
|
+
"StockChart",
|
|
696
|
+
"Sparkline",
|
|
697
|
+
"SpeechToTextButton",
|
|
698
|
+
"SplitButton",
|
|
699
|
+
"Splitter",
|
|
700
|
+
"Spreadsheet",
|
|
701
|
+
"StackLayout",
|
|
702
|
+
"Stepper",
|
|
703
|
+
"SVGIcon",
|
|
704
|
+
"Switch",
|
|
705
|
+
"TabStrip",
|
|
706
|
+
"TextArea",
|
|
707
|
+
"TextBox",
|
|
708
|
+
"TileLayout",
|
|
709
|
+
"Timeline",
|
|
710
|
+
"TimePicker",
|
|
711
|
+
"ToolBar",
|
|
712
|
+
"Tooltip",
|
|
713
|
+
"TreeList",
|
|
714
|
+
"TreeView",
|
|
715
|
+
"Typography",
|
|
716
|
+
"Upload",
|
|
717
|
+
"Window"
|
|
718
|
+
];
|
|
719
|
+
var COMPONENT_PACKAGE_MAP = /* @__PURE__ */ new Map([
|
|
720
|
+
["actionsheet", "@progress/kendo-angular-navigation"],
|
|
721
|
+
["aiprompt", "@progress/kendo-angular-conversational-ui"],
|
|
722
|
+
["appbar", "@progress/kendo-angular-navigation"],
|
|
723
|
+
["arcgauge", "@progress/kendo-angular-gauges"],
|
|
724
|
+
["autocomplete", "@progress/kendo-angular-dropdowns"],
|
|
725
|
+
["avatar", "@progress/kendo-angular-layout"],
|
|
726
|
+
["badge", "@progress/kendo-angular-indicators"],
|
|
727
|
+
["barcode", "@progress/kendo-angular-barcodes"],
|
|
728
|
+
["bottomnavigation", "@progress/kendo-angular-navigation"],
|
|
729
|
+
["breadcrumb", "@progress/kendo-angular-navigation"],
|
|
730
|
+
["button", "@progress/kendo-angular-buttons"],
|
|
731
|
+
["buttongroup", "@progress/kendo-angular-buttons"],
|
|
732
|
+
["calendar", "@progress/kendo-angular-dateinputs"],
|
|
733
|
+
["card", "@progress/kendo-angular-layout"],
|
|
734
|
+
["chartwizard", "@progress/kendo-angular-chart-wizard"],
|
|
735
|
+
["checkbox", "@progress/kendo-angular-inputs"],
|
|
736
|
+
["chip", "@progress/kendo-angular-buttons"],
|
|
737
|
+
["chiplist", "@progress/kendo-angular-buttons"],
|
|
738
|
+
["chunkprogressbar", "@progress/kendo-angular-progressbar"],
|
|
739
|
+
["circulargauge", "@progress/kendo-angular-gauges"],
|
|
740
|
+
["circularprogressbar", "@progress/kendo-angular-progressbar"],
|
|
741
|
+
["colorgradient", "@progress/kendo-angular-inputs"],
|
|
742
|
+
["colorpalette", "@progress/kendo-angular-inputs"],
|
|
743
|
+
["colorpicker", "@progress/kendo-angular-inputs"],
|
|
744
|
+
["combobox", "@progress/kendo-angular-dropdowns"],
|
|
745
|
+
["contextmenu", "@progress/kendo-angular-menu"],
|
|
746
|
+
["dataquery", "@progress/kendo-data-query"],
|
|
747
|
+
["dateinput", "@progress/kendo-angular-dateinputs"],
|
|
748
|
+
["datemath", "@progress/kendo-date-math"],
|
|
749
|
+
["datepicker", "@progress/kendo-angular-dateinputs"],
|
|
750
|
+
["daterangepicker", "@progress/kendo-angular-dateinputs"],
|
|
751
|
+
["datetimepicker", "@progress/kendo-angular-dateinputs"],
|
|
752
|
+
["diagram", "@progress/kendo-angular-diagrams"],
|
|
753
|
+
["dialog", "@progress/kendo-angular-dialog"],
|
|
754
|
+
["drawer", "@progress/kendo-angular-layout"],
|
|
755
|
+
["drawing", "@progress/kendo-drawing"],
|
|
756
|
+
["dropdownbutton", "@progress/kendo-angular-buttons"],
|
|
757
|
+
["dropdownlist", "@progress/kendo-angular-dropdowns"],
|
|
758
|
+
["dropdowntree", "@progress/kendo-angular-dropdowns"],
|
|
759
|
+
["editor", "@progress/kendo-angular-editor"],
|
|
760
|
+
["excelexport", "@progress/kendo-angular-excel-export"],
|
|
761
|
+
["expansionpanel", "@progress/kendo-angular-layout"],
|
|
762
|
+
["filesaver", "@progress/kendo-file-saver"],
|
|
763
|
+
["fileselect", "@progress/kendo-angular-upload"],
|
|
764
|
+
["filter", "@progress/kendo-angular-filter"],
|
|
765
|
+
["flatcolorpicker", "@progress/kendo-angular-inputs"],
|
|
766
|
+
["floatingactionbutton", "@progress/kendo-angular-buttons"],
|
|
767
|
+
["floatinglabel", "@progress/kendo-angular-label"],
|
|
768
|
+
["formfield", "@progress/kendo-angular-inputs"],
|
|
769
|
+
["forms", "@progress/kendo-angular-inputs"],
|
|
770
|
+
["gantt", "@progress/kendo-angular-gantt"],
|
|
771
|
+
["grid", "@progress/kendo-angular-grid"],
|
|
772
|
+
["gridlayout", "@progress/kendo-angular-layout"],
|
|
773
|
+
["icon", "@progress/kendo-angular-icons"],
|
|
774
|
+
["inlineaiprompt", "@progress/kendo-angular-conversational-ui"],
|
|
775
|
+
["label", "@progress/kendo-angular-label"],
|
|
776
|
+
["lineargauge", "@progress/kendo-angular-gauges"],
|
|
777
|
+
["listbox", "@progress/kendo-angular-listbox"],
|
|
778
|
+
["listview", "@progress/kendo-angular-listview"],
|
|
779
|
+
["loader", "@progress/kendo-angular-indicators"],
|
|
780
|
+
["map", "@progress/kendo-angular-map"],
|
|
781
|
+
["maskedtextbox", "@progress/kendo-angular-inputs"],
|
|
782
|
+
["menu", "@progress/kendo-angular-menu"],
|
|
783
|
+
["multicolumncombobox", "@progress/kendo-angular-dropdowns"],
|
|
784
|
+
["multiselect", "@progress/kendo-angular-dropdowns"],
|
|
785
|
+
["multiselecttree", "@progress/kendo-angular-dropdowns"],
|
|
786
|
+
["multiviewcalendar", "@progress/kendo-angular-dateinputs"],
|
|
787
|
+
["notification", "@progress/kendo-angular-notification"],
|
|
788
|
+
["numerictextbox", "@progress/kendo-angular-inputs"],
|
|
789
|
+
["otpinput", "@progress/kendo-angular-inputs"],
|
|
790
|
+
["pager", "@progress/kendo-angular-pager"],
|
|
791
|
+
["panelbar", "@progress/kendo-angular-layout"],
|
|
792
|
+
["pdfexport", "@progress/kendo-angular-pdf-export"],
|
|
793
|
+
["pdfviewer", "@progress/kendo-angular-pdfviewer"],
|
|
794
|
+
["pivotgrid", "@progress/kendo-angular-pivotgrid"],
|
|
795
|
+
["popover", "@progress/kendo-angular-tooltip"],
|
|
796
|
+
["popup", "@progress/kendo-angular-popup"],
|
|
797
|
+
["progressbar", "@progress/kendo-angular-progressbar"],
|
|
798
|
+
["promptbox", "@progress/kendo-angular-conversational-ui"],
|
|
799
|
+
["qrcode", "@progress/kendo-angular-barcodes"],
|
|
800
|
+
["radialgauge", "@progress/kendo-angular-gauges"],
|
|
801
|
+
["radiobutton", "@progress/kendo-angular-inputs"],
|
|
802
|
+
["rangeslider", "@progress/kendo-angular-inputs"],
|
|
803
|
+
["rating", "@progress/kendo-angular-inputs"],
|
|
804
|
+
["ripple", "@progress/kendo-angular-ripple"],
|
|
805
|
+
["sankey", "@progress/kendo-angular-charts"],
|
|
806
|
+
["scheduler", "@progress/kendo-angular-scheduler"],
|
|
807
|
+
["scrollview", "@progress/kendo-angular-scrollview"],
|
|
808
|
+
["signature", "@progress/kendo-angular-inputs"],
|
|
809
|
+
["skeleton", "@progress/kendo-angular-indicators"],
|
|
810
|
+
["slider", "@progress/kendo-angular-inputs"],
|
|
811
|
+
["smartpastebutton", "@progress/kendo-angular-buttons"],
|
|
812
|
+
["sortable", "@progress/kendo-angular-sortable"],
|
|
813
|
+
["sparkline", "@progress/kendo-angular-charts"],
|
|
814
|
+
["speechtotextbutton", "@progress/kendo-angular-buttons"],
|
|
815
|
+
["splitbutton", "@progress/kendo-angular-buttons"],
|
|
816
|
+
["splitter", "@progress/kendo-angular-layout"],
|
|
817
|
+
["spreadsheet", "@progress/kendo-angular-spreadsheet"],
|
|
818
|
+
["stacklayout", "@progress/kendo-angular-layout"],
|
|
819
|
+
["stepper", "@progress/kendo-angular-layout"],
|
|
820
|
+
["stockchart", "@progress/kendo-angular-charts"],
|
|
821
|
+
["svgicon", "@progress/kendo-angular-icons"],
|
|
822
|
+
["switch", "@progress/kendo-angular-inputs"],
|
|
823
|
+
["tabstrip", "@progress/kendo-angular-layout"],
|
|
824
|
+
["textarea", "@progress/kendo-angular-inputs"],
|
|
825
|
+
["textbox", "@progress/kendo-angular-inputs"],
|
|
826
|
+
["tilelayout", "@progress/kendo-angular-layout"],
|
|
827
|
+
["timeline", "@progress/kendo-angular-layout"],
|
|
828
|
+
["timepicker", "@progress/kendo-angular-dateinputs"],
|
|
829
|
+
["toolbar", "@progress/kendo-angular-toolbar"],
|
|
830
|
+
["treelist", "@progress/kendo-angular-treelist"],
|
|
831
|
+
["treeview", "@progress/kendo-angular-treeview"],
|
|
832
|
+
["typography", "@progress/kendo-angular-typography"],
|
|
833
|
+
["upload", "@progress/kendo-angular-upload"],
|
|
834
|
+
["window", "@progress/kendo-angular-dialog"],
|
|
835
|
+
["charts", "@progress/kendo-angular-charts"],
|
|
836
|
+
["conversationalui", "@progress/kendo-angular-conversational-ui"],
|
|
837
|
+
["tooltip", "@progress/kendo-angular-tooltip"]
|
|
838
|
+
]);
|
|
839
|
+
|
|
840
|
+
// packages/angular/src/lib/grpc-client.ts
|
|
841
|
+
var protoPath = join(
|
|
842
|
+
dirname(fileURLToPath2(import.meta.url)),
|
|
843
|
+
"../proto/service.proto"
|
|
844
|
+
);
|
|
845
|
+
var contextApi = new ContextAPI("contextapi.telerik.com:443", protoPath, {
|
|
846
|
+
libName: Suite
|
|
847
|
+
});
|
|
848
|
+
var validateUserLicense = async () => {
|
|
849
|
+
return contextApi.validateUserLicense();
|
|
850
|
+
};
|
|
851
|
+
|
|
852
|
+
// packages/angular/src/index.ts
|
|
853
|
+
import fs3 from "fs";
|
|
854
|
+
import path3 from "path";
|
|
855
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
856
|
+
|
|
857
|
+
// packages/kendo-ui-agent/src/lib/tools-factory.ts
|
|
858
|
+
import { z as z4 } from "zod";
|
|
859
|
+
|
|
860
|
+
// packages/kendo-ui-agent/src/lib/ui-agent-tool.ts
|
|
861
|
+
var UIAgentTool = class {
|
|
862
|
+
constructor(name, config, cb) {
|
|
863
|
+
this.name = name;
|
|
864
|
+
this.config = config;
|
|
865
|
+
this.cb = cb;
|
|
866
|
+
}
|
|
867
|
+
register(mcpServer) {
|
|
868
|
+
this.server = mcpServer;
|
|
869
|
+
this.server.registerTool(this.name, this.config, this.cb);
|
|
870
|
+
}
|
|
871
|
+
hasAccess(accessType) {
|
|
872
|
+
if (!this.config.requiredAccess || this.config.requiredAccess.length === 0) {
|
|
873
|
+
return true;
|
|
874
|
+
}
|
|
875
|
+
return accessType === "full" || this.config.requiredAccess.includes(accessType);
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
// packages/kendo-ui-agent/src/lib/templates/validator.ts
|
|
880
|
+
var renderValidatorResponse = (ctx, frameworkValidationSteps) => `# ${ctx.suiteName} - Code Validation & Quality Assurance
|
|
6
881
|
|
|
7
882
|
## Comprehensive Code Validation Checklist
|
|
8
883
|
Execute these validation steps systematically to ensure high-quality, maintainable code:
|
|
9
884
|
|
|
10
885
|
### Validation Phase: Framework-Specific Validation
|
|
11
886
|
|
|
12
|
-
${
|
|
887
|
+
${frameworkValidationSteps}
|
|
13
888
|
|
|
14
889
|
### Validation Phase: General Code Quality & Best Practices
|
|
15
890
|
|
|
@@ -46,45 +921,99 @@ Responsive Design:
|
|
|
46
921
|
- Graceful error handling for all user interactions
|
|
47
922
|
- Meaningful error messages for users
|
|
48
923
|
- Network error handling for API calls
|
|
49
|
-
`;
|
|
924
|
+
`;
|
|
925
|
+
|
|
926
|
+
// packages/kendo-ui-agent/src/lib/templates/orchestrator.ts
|
|
927
|
+
var responsiveKendoDesignSystemGuidance = (ctx) => `**Tool call example:**
|
|
928
|
+
\`\`\`
|
|
929
|
+
${ctx.layoutAssistantName}({
|
|
930
|
+
prompt="Provide Kendo Design System utility classes to make the following layout structure responsive across mobile, tablet, and desktop views: [LAYOUT DESCRIPTION HERE]",
|
|
931
|
+
includeBuildingBlockExamples=false // Optional, include examples of Kendo building blocks (Grid, Flex, Stack, etc.)
|
|
932
|
+
})
|
|
933
|
+
\`\`\`
|
|
934
|
+
`;
|
|
935
|
+
var vanillaCSSResponsiveGuidance = `**General Responsive Design Guidance:**
|
|
936
|
+
- Use CSS media queries to apply different styles at various breakpoints (e.g., mobile, tablet, desktop).
|
|
937
|
+
- Consider a mobile-first approach, starting with styles for smaller screens and adding complexity for larger screens.
|
|
938
|
+
- Ensure flexible grid layouts that can adapt to different screen sizes.
|
|
939
|
+
- Use relative units (%, em, rem) instead of fixed units (px) for widths and spacing to allow for better scalability.
|
|
940
|
+
- Test the design across multiple devices and screen sizes to ensure a consistent user experience.`;
|
|
941
|
+
var tailwindResponsiveGuidance = `**Tailwind CSS Responsive Design Guidance:**
|
|
942
|
+
- Use Tailwind's responsive utility variants (e.g., sm:, md:, lg:, xl:) to apply styles at specific breakpoints.
|
|
943
|
+
- Start with mobile styles (no prefix) and layer on additional styles for larger screens using the appropriate prefixes.
|
|
944
|
+
- Leverage Tailwind's grid and flex utilities to create flexible layouts that adapt to different screen sizes.
|
|
945
|
+
- Use Tailwind's spacing utilities to maintain consistent spacing across breakpoints.
|
|
946
|
+
- Test the design across multiple devices and screen sizes to ensure a consistent user experience.`;
|
|
947
|
+
var renderOrchestratorResponse = (ctx, options) => {
|
|
948
|
+
const validationInstructions = renderValidatorResponse(
|
|
949
|
+
{ suiteName: ctx.suiteName, libKey: ctx.libKey },
|
|
950
|
+
options.validationSteps
|
|
951
|
+
);
|
|
952
|
+
const includeLayout = ctx.includeLayout && ctx.layoutMode === "utils";
|
|
953
|
+
const dragDropGuidance = ctx.libKey === "angular" ? `- **Drag and Drop**: Consider using the DragandDrop and Sortable components when drag-and-drop functionality is needed.` : "";
|
|
954
|
+
const componentsList = ctx.components ? ctx.components.join(", ") : "components";
|
|
955
|
+
const responsiveLayoutHint = includeLayout ? `- And use the ${ctx.layoutAssistantName} tool to get Kendo CSS utility classes needed for responsive design.` : "";
|
|
956
|
+
const responsiveKDSGuidance = includeLayout ? responsiveKendoDesignSystemGuidance(ctx) : "";
|
|
957
|
+
const responsiveVanillaCSSGuidance = ctx.layoutMode === "vanilla" ? vanillaCSSResponsiveGuidance : "";
|
|
958
|
+
const responsiveTailwindCSSGuidance = ctx.layoutMode === "tailwind" ? tailwindResponsiveGuidance : "";
|
|
959
|
+
return `# \u{1F3AF} ${ctx.suiteName} - Execution Plan
|
|
50
960
|
|
|
51
961
|
**Current Goal:**
|
|
52
962
|
|
|
53
|
-
${
|
|
963
|
+
${ctx.userIntent}
|
|
54
964
|
|
|
55
965
|
**Requirements Summary:**
|
|
56
|
-
- **Framework:** ${
|
|
57
|
-
${
|
|
58
|
-
- Ensure to use ONLY ${
|
|
59
|
-
- And use the ${
|
|
60
|
-
- Ensure that after you read the plan you use exactly the ${
|
|
61
|
-
|
|
62
|
-
- And use the ${
|
|
63
|
-
|
|
966
|
+
- **Framework:** ${ctx.libKey}
|
|
967
|
+
${ctx.includeComponentsDocs ? `- **Component Library:** ${ctx.suiteName}
|
|
968
|
+
- Ensure to use ONLY ${ctx.suiteName} components for all UI elements.
|
|
969
|
+
- And use the ${ctx.codeAssistantName} tool to get ${ctx.suiteName} component documentation and code snippets.
|
|
970
|
+
- Ensure that after you read the plan you use exactly the ${ctx.codeAssistantName} no matter if there is another tool that can provide code snippets, documentation or other resources related to ${ctx.suiteName}.
|
|
971
|
+
` : ""}${includeLayout ? `- **Layout:** Yes
|
|
972
|
+
- And use the ${ctx.layoutAssistantName} tool to get Kendo CSS utility classes needed for layout structure and responsive design.
|
|
973
|
+
` : ""}${ctx.includeIcons ? `- **Icons:** Yes
|
|
64
974
|
- Use icons that align with the project theme and enhance usability.
|
|
65
|
-
- Use the ${
|
|
66
|
-
|
|
975
|
+
- Use the ${ctx.iconAssistantName} tool to search for icons that fit the theme and purpose of your project.
|
|
976
|
+
` : ""}${ctx.includeTheming ? `- **Custom Theming:**
|
|
67
977
|
- Create a custom theme that aligns with the current goal and branding needs.
|
|
68
978
|
- Use Kendo Themes and Design System guidelines.
|
|
69
979
|
- Make sure to include strict CSS variables or theme requirements from the current goal.
|
|
70
|
-
- And use the ${
|
|
71
|
-
|
|
980
|
+
- And use the ${ctx.styleAssistantName} tool to create a custom theme for your project.
|
|
981
|
+
` : ""}${ctx.includeResponsive ? `- **Responsive:**
|
|
72
982
|
- Ensure the layout is fully responsive across mobile, tablet, and desktop views.
|
|
73
|
-
|
|
74
|
-
|
|
983
|
+
${responsiveLayoutHint}
|
|
984
|
+
` : ""}
|
|
75
985
|
- **Accessible (WCAG 2.2 Level AA):**
|
|
76
|
-
- CRITICAL: You must call the ${
|
|
986
|
+
- CRITICAL: You must call the ${ctx.accessibilityAssistantName} tool with includeGeneralGuidelines=true on your first call to get WCAG 2.2 Level AA guidelines.
|
|
77
987
|
- For subsequent calls, use includeGeneralGuidelines=false to get only component-specific accessibility information.
|
|
78
988
|
- Ensure all components meet WCAG 2.2 Level AA standards.
|
|
79
989
|
|
|
80
|
-
${
|
|
990
|
+
${includeLayout ? `**STYLING AND LAYOUT REQUIREMENTS:**
|
|
991
|
+
**LAYOUT MODE: KENDO DESIGN SYSTEM CSS UTILITIES**
|
|
81
992
|
## CRITICAL DESIGN SYSTEM CONSTRAINTS
|
|
82
993
|
- **USE ONLY KENDO DESIGN SYSTEM UTILITIES** for ALL styling, layout, spacing, positioning, and responsive behavior
|
|
83
994
|
- Avoid custom CSS, inline styles, or other CSS framework classes
|
|
84
|
-
- The ${
|
|
995
|
+
- The ${ctx.layoutAssistantName} tool will provide the specific Kendo CSS utility classes needed
|
|
85
996
|
- All layout requirements must use Kendo utilities exclusively
|
|
997
|
+
` : ""}
|
|
998
|
+
${ctx.layoutMode === "vanilla" ? `**STYLING AND LAYOUT REQUIREMENTS:**
|
|
999
|
+
**LAYOUT MODE: VANILLA**
|
|
1000
|
+
- In this mode, you should use Kendo UI components and vanilla CSS for layout and styling.
|
|
1001
|
+
- Do NOT use any utility-first CSS classes (e.g., from Tailwind) for layout or styling.
|
|
1002
|
+
- Focus on leveraging the built-in capabilities of Kendo UI components and writing custom CSS as needed to achieve the desired design and layout.
|
|
1003
|
+
` : ""}
|
|
1004
|
+
${ctx.layoutMode === "tailwind" ? `**STYLING AND LAYOUT REQUIREMENTS:**
|
|
1005
|
+
**LAYOUT MODE: TAILWIND**
|
|
1006
|
+
- In this mode, you should use Tailwind CSS utility classes for layout and styling.
|
|
1007
|
+
- Do NOT write custom CSS or use vanilla CSS for layout or styling.
|
|
1008
|
+
- Focus on leveraging Tailwind's utility-first approach to achieve the desired design and layout, while still using Kendo UI components for functionality.
|
|
1009
|
+
` : ""}
|
|
1010
|
+
${ctx.includeFigmaD2C ? `**DESIGN EXTRACTION FROM FIGMA FILE**
|
|
1011
|
+
- You are given a Figma file URL that contains the design for the current goal. You MUST use the ${ctx.figmaD2CToolName} tool to extract design information from the Figma file.
|
|
1012
|
+
- Extracted design information should be used to get information about the layout, styles, design tokens and assets that can be used within the project.
|
|
1013
|
+
- The ${ctx.figmaD2CToolName} tool will output you Layout structure of the design, design tokens for CSS and any decoration and a snapshot of the design.
|
|
1014
|
+
` : ""}
|
|
86
1015
|
|
|
87
|
-
|
|
1016
|
+
---
|
|
88
1017
|
|
|
89
1018
|
## Detailed Execution Plan
|
|
90
1019
|
Follow these steps strictly in order to achieve a complete and polished implementation of the current goal:
|
|
@@ -96,13 +1025,13 @@ Follow these steps strictly in order to achieve a complete and polished implemen
|
|
|
96
1025
|
**MANDATORY REQUIREMENT:**
|
|
97
1026
|
|
|
98
1027
|
- Before implementing any UI components or features, you must call the accessibility assistant with general guidelines
|
|
99
|
-
- On your first call to "${
|
|
1028
|
+
- On your first call to "${ctx.accessibilityAssistantName}", set "includeGeneralGuidelines=true" to get foundational WCAG 2.2 Level AA best practices
|
|
100
1029
|
- These general guidelines apply to all components and must be considered throughout implementation
|
|
101
1030
|
- For all subsequent calls, set "includeGeneralGuidelines=false" to get only component-specific information
|
|
102
1031
|
|
|
103
1032
|
**First tool call (includes general WCAG guidelines):**
|
|
104
1033
|
\`\`\`
|
|
105
|
-
${
|
|
1034
|
+
${ctx.accessibilityAssistantName}({
|
|
106
1035
|
query="What are the accessibility requirements for [COMPONENT_NAME]?",
|
|
107
1036
|
component="[COMPONENT_NAME]",
|
|
108
1037
|
includeGeneralGuidelines=true // ONLY true on first call
|
|
@@ -111,11 +1040,11 @@ ${e.accessibilityAssistantName}({
|
|
|
111
1040
|
|
|
112
1041
|
**Step 2: Get Component-Specific Accessibility Information**
|
|
113
1042
|
|
|
114
|
-
For each additional ${
|
|
1043
|
+
For each additional ${ctx.suiteName} component you plan to use, call the tool with "includeGeneralGuidelines=false":
|
|
115
1044
|
|
|
116
1045
|
**Tool call example:**
|
|
117
1046
|
\`\`\`
|
|
118
|
-
${
|
|
1047
|
+
${ctx.accessibilityAssistantName}({
|
|
119
1048
|
query="What are the accessibility features and requirements for [COMPONENT_NAME]? Include ARIA attributes, keyboard navigation, and screen reader support.",
|
|
120
1049
|
component="[COMPONENT_NAME]",
|
|
121
1050
|
includeGeneralGuidelines=false // false for all calls after the first
|
|
@@ -124,13 +1053,112 @@ ${e.accessibilityAssistantName}({
|
|
|
124
1053
|
|
|
125
1054
|
**Example:**
|
|
126
1055
|
\`\`\`
|
|
127
|
-
${
|
|
1056
|
+
${ctx.accessibilityAssistantName}({
|
|
128
1057
|
query="How to associate the label with another component or HTML input?",
|
|
129
1058
|
component="Label",
|
|
130
1059
|
includeGeneralGuidelines=false
|
|
131
1060
|
})
|
|
132
1061
|
\`\`\`
|
|
133
|
-
|
|
1062
|
+
|
|
1063
|
+
${ctx.includeFigmaD2C ? `### Phase: Design Extraction and Analysis
|
|
1064
|
+
|
|
1065
|
+
**IMPORTANT DESIGN EXTRACTION REQUIRMENTS**
|
|
1066
|
+
- Use the ${ctx.figmaD2CToolName} tool to extract design information from the provided Figma file URL.
|
|
1067
|
+
- Analyze the extracted design information to identify key layout structures, components, styles, and design tokens.
|
|
1068
|
+
- Use the insights from the design analysis to inform your implementation in the next phases, ensuring alignment with the original design intent.
|
|
1069
|
+
- Follow ALL three extraction steps sequentially \u2014 each step is MANDATORY.
|
|
1070
|
+
- Complete the verification checklist after each step before proceeding to the next.
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
**Step 1: Extract Base Design Information from Figma**
|
|
1074
|
+
- Extract only base the first layer of the design to get the overall structure and layout without going too deep into details. This will help you to understand the design hierarchy and identify key components and layout patterns.
|
|
1075
|
+
- Use the downloaded design snapshot to visually analyze the design and correlate it with the extracted structure. This will help you to identify which nodes correspond to which design elements and to plan your implementation accordingly.
|
|
1076
|
+
- **REQUIRED OUTPUT:** After extraction, you MUST output a structured list of ALL discovered node IDs with their labels/names before proceeding to Step 2. Format this as a clear inventory that will be referenced in subsequent steps. For each node, extract the node ID from the node-id attribute of the node XML tag.
|
|
1077
|
+
- **Depth escalation:** If a node at this depth shows it contains more children than can be captured (e.g., children are truncated), re-fetch that specific node with depth + 5 to ensure complete extraction.
|
|
1078
|
+
|
|
1079
|
+
**Tool call example:**
|
|
1080
|
+
\`\`\`
|
|
1081
|
+
${ctx.figmaD2CToolName}({
|
|
1082
|
+
url: [Figma URL from prompt]
|
|
1083
|
+
depth: 10 // Consider more depth only if the user prompt explicitly states to extract deeply.
|
|
1084
|
+
})
|
|
1085
|
+
\`\`\`
|
|
1086
|
+
|
|
1087
|
+
**Step 1 Completion Checklist (MUST satisfy before proceeding):**
|
|
1088
|
+
- [ ] Have I captured all top-level layout containers (headers, footers, main sections)?
|
|
1089
|
+
- [ ] Have I identified all major structural components?
|
|
1090
|
+
- [ ] Have I created and outputted a structured list of node IDs and their labels?
|
|
1091
|
+
- [ ] Have I visually analyzed the design snapshot to correlate nodes with design elements?
|
|
1092
|
+
|
|
1093
|
+
**Step 2: Extract Main Nodes Design**
|
|
1094
|
+
- Based on the initial extraction and analysis, identify the main nodes that are critical for the design implementation. These could be reusable headers, body, navigation, sidebar or footer elements that illustrate the main structure.
|
|
1095
|
+
- Use the node IDs from the node-id attribute of the node XML tag to fetch these specific nodes with more depth to capture necessary details for implementation. This targeted extraction will help you to get the relevant design information without overwhelming the process with too much data.
|
|
1096
|
+
- **Depth escalation:** If any main node shows truncated children or contains more elements than expected at the given depth, immediately re-fetch with depth + 5.
|
|
1097
|
+
|
|
1098
|
+
**Tool call example:**
|
|
1099
|
+
\`\`\`
|
|
1100
|
+
${ctx.figmaD2CToolName}({
|
|
1101
|
+
fileKey: [The fileKey extracted from Step 1]
|
|
1102
|
+
nodeIds: [Array of main node IDs identified from Step 1]
|
|
1103
|
+
depth: 10 // Deeper extraction for main nodes to capture necessary details.
|
|
1104
|
+
})
|
|
1105
|
+
\`\`\`
|
|
1106
|
+
|
|
1107
|
+
**Step 2 Completion Checklist (MUST satisfy before proceeding):**
|
|
1108
|
+
- [ ] Have I captured all interactive components (buttons, inputs, navigation elements)?
|
|
1109
|
+
- [ ] Have I identified all typography styles and text elements?
|
|
1110
|
+
- [ ] Have I extracted all color tokens and design system values?
|
|
1111
|
+
- [ ] Have I documented child node IDs that need deeper extraction in Step 3?
|
|
1112
|
+
|
|
1113
|
+
**Step 3: Extract Child Node Details (MANDATORY)**
|
|
1114
|
+
**CRITICAL:** After Step 2, you MUST proceed to Step 3. Extract ALL child nodes identified in Step 2. Do not skip this step.
|
|
1115
|
+
|
|
1116
|
+
- Extract all child nodes that were identified in Step 2 as requiring deeper analysis. These include specific components, styles, or interactions that are essential for accurate implementation.
|
|
1117
|
+
- Use the node IDs from the node-id attribute to fetch these specific child nodes with sufficient depth to capture all critical design details.
|
|
1118
|
+
- **Depth escalation:** If child nodes still show incomplete extraction (truncated elements, nested components not fully visible), re-fetch with depth + 5 until all leaf-level components are captured.
|
|
1119
|
+
|
|
1120
|
+
**Tool call example:**
|
|
1121
|
+
\`\`\`
|
|
1122
|
+
${ctx.figmaD2CToolName}({
|
|
1123
|
+
fileKey: [The fileKey extracted from Step 1]
|
|
1124
|
+
nodeIds: [Array of ALL child node IDs identified from Step 2]
|
|
1125
|
+
depth: 15 // Targeted deeper extraction for specific nodes. Increase if needed.
|
|
1126
|
+
})
|
|
1127
|
+
\`\`\`
|
|
1128
|
+
|
|
1129
|
+
**Step 3 Completion Checklist (MUST satisfy before proceeding):**
|
|
1130
|
+
- [ ] Have I captured ALL leaf-level components (buttons, icons, form inputs)?
|
|
1131
|
+
- [ ] Have I extracted all spacing, padding, and margin values?
|
|
1132
|
+
- [ ] Have I identified all border styles, shadows, and visual effects?
|
|
1133
|
+
- [ ] Are there any remaining truncated nodes? (If yes, apply depth escalation)
|
|
1134
|
+
|
|
1135
|
+
**Iterative Extraction and Stopping Condition**
|
|
1136
|
+
Continue iterating over the extraction process following these rules:
|
|
1137
|
+
- **Continue until:** (a) All leaf-level components have been captured with complete details, OR (b) You have completed 3 extraction passes \u2014 whichever comes first.
|
|
1138
|
+
- After each iteration, verify that no new child nodes require deeper extraction.
|
|
1139
|
+
- If depth escalation is needed after 3 passes, document the limitation and proceed with available data.
|
|
1140
|
+
|
|
1141
|
+
**MANDATORY: Design Extraction Verification (Complete BEFORE Code Generation Phase)**
|
|
1142
|
+
Before proceeding to any code generation or implementation phase, you MUST produce and output a verification summary containing:
|
|
1143
|
+
|
|
1144
|
+
1. **Component Inventory:** List all UI components discovered (buttons, inputs, cards, navigation elements, etc.)
|
|
1145
|
+
2. **Hierarchy Map:** Show the parent-child relationships of major layout sections
|
|
1146
|
+
3. **Design Tokens Identified:**
|
|
1147
|
+
- Colors (with hex/rgba values)
|
|
1148
|
+
- Typography (font families, sizes, weights)
|
|
1149
|
+
- Spacing values (margins, paddings, gaps)
|
|
1150
|
+
- Border styles and effects
|
|
1151
|
+
4. **Gaps and Limitations:** Explicitly state any design elements that could not be fully extracted
|
|
1152
|
+
|
|
1153
|
+
**This verification summary serves as the design specification for implementation. Do not proceed to Code Generation until this summary is complete.**
|
|
1154
|
+
|
|
1155
|
+
**Additional Extraction During Implementation:**
|
|
1156
|
+
- During the implementation phase (Code Generation), you can and should use the ${ctx.figmaD2CToolName} tool with the relevant node IDs from the node-id attributes again if you discover you need more specific design details for certain components or sections that were not captured in earlier phases.
|
|
1157
|
+
- If additional extraction is needed, apply the same depth escalation logic and verification steps.
|
|
1158
|
+
` : ""}
|
|
1159
|
+
|
|
1160
|
+
|
|
1161
|
+
${includeLayout ? `
|
|
134
1162
|
### Phase: Layout Foundation
|
|
135
1163
|
|
|
136
1164
|
**Step 1: Create Base Layout Structure**
|
|
@@ -141,16 +1169,16 @@ ${e.includeLayout?`
|
|
|
141
1169
|
|
|
142
1170
|
**Tool call example:**
|
|
143
1171
|
\`\`\`
|
|
144
|
-
${
|
|
1172
|
+
${ctx.layoutAssistantName}({
|
|
145
1173
|
prompt="Provide Kendo Design System utility classes for the following layout structure: [LAYOUT DESCRIPTION HERE]",
|
|
146
1174
|
includeBuildingBlockExamples=true // Optional, include examples of Kendo building blocks (Grid, Flex, Stack, etc.)
|
|
147
1175
|
})
|
|
148
1176
|
\`\`\`
|
|
149
|
-
|
|
150
|
-
${
|
|
1177
|
+
` : ""}
|
|
1178
|
+
${ctx.includeComponentsDocs ? `
|
|
151
1179
|
### Phase: Component Integration
|
|
152
1180
|
**Step 1: Identify and Implement Core Components**
|
|
153
|
-
Based on your current goal, analyze what other ${
|
|
1181
|
+
Based on your current goal, analyze what other ${ctx.suiteName} components are needed and implement them one by one. Here you are some appropriate components based on the different project type:
|
|
154
1182
|
|
|
155
1183
|
**Component Selection Guidelines:**
|
|
156
1184
|
- **Landing Pages**: Consider Button, Card, Form components, Layout containers, Navigation elements
|
|
@@ -158,17 +1186,17 @@ Based on your current goal, analyze what other ${e.suiteName} components are nee
|
|
|
158
1186
|
- **Data Entry**: Consider Form, TextBox, DatePicker, DropDownList, Upload, Validation components
|
|
159
1187
|
- **Content Management**: Consider Grid, Editor, TreeView, FileManager, Dialog components
|
|
160
1188
|
- **E-commerce**: Consider ListView, Card, Button, Rating, Badge, Carousel components
|
|
161
|
-
${
|
|
1189
|
+
${dragDropGuidance}
|
|
162
1190
|
|
|
163
1191
|
**IMPORTANT COMPONENT USAGE REQUIREMENTS:**
|
|
164
|
-
- Make sure to identify all necessary features related to the ${
|
|
165
|
-
- Make sure to identify specific features needed for other ${
|
|
166
|
-
- Make sure to generate a query for ${
|
|
1192
|
+
- Make sure to identify all necessary features related to the ${componentsList} based on the current goal.
|
|
1193
|
+
- Make sure to identify specific features needed for other ${ctx.suiteName} components based on the current goal.
|
|
1194
|
+
- Make sure to generate a query for ${ctx.codeAssistantName} tool for each component and call the tool for each specific feature or functionality.
|
|
167
1195
|
- Always specify the exact features you want for each component. Describe only the needed functionality, and behavior you need. Be concise and don't include any information about the use case.
|
|
168
1196
|
|
|
169
1197
|
**Tool call examples:**
|
|
170
1198
|
\`\`\`
|
|
171
|
-
${
|
|
1199
|
+
${ctx.codeAssistantName}({
|
|
172
1200
|
query="Give me information about [COMPONENT_NAME] with [SPECIFIC_FEATURES]." <!-- For example: 'Grid with paging' or 'Button with click events' or 'Chart with multiple series' -->
|
|
173
1201
|
component="[COMPONENT_NAME]" <!-- For example: 'Grid', 'Button', 'Chart', etc. -->
|
|
174
1202
|
})
|
|
@@ -176,40 +1204,33 @@ ${o}
|
|
|
176
1204
|
|
|
177
1205
|
For broad queries, break them down into specific features (e.g., paging, sorting, filtering for Grid) and call the tool separately for each feature:
|
|
178
1206
|
\`\`\`
|
|
179
|
-
${
|
|
1207
|
+
${ctx.codeAssistantName}({
|
|
180
1208
|
query="How to implement [SPECIFIC_FEATURE] in [COMPONENT_NAME]?" <!-- For example: 'paging in Grid' -->
|
|
181
1209
|
component="[COMPONENT_NAME]" <!-- For example: 'Grid', 'Button', etc. -->
|
|
182
1210
|
})
|
|
183
1211
|
\`\`\`
|
|
184
1212
|
|
|
185
1213
|
\`\`\`
|
|
186
|
-
${
|
|
1214
|
+
${ctx.codeAssistantName}({
|
|
187
1215
|
query="How to implement [SPECIFIC_FEATURE] in [COMPONENT_NAME]?" <!-- For example: 'sorting in Grid' -->
|
|
188
1216
|
component="[COMPONENT_NAME]" <!-- For example: 'Grid', 'Button', etc. -->
|
|
189
1217
|
})
|
|
190
1218
|
\`\`\`
|
|
191
1219
|
|
|
192
1220
|
\`\`\`
|
|
193
|
-
${
|
|
1221
|
+
${ctx.codeAssistantName}({
|
|
194
1222
|
query="How to implement [SPECIFIC_FEATURE] in [COMPONENT_NAME]?" <!-- For example: 'filtering in Grid' -->
|
|
195
1223
|
component="[COMPONENT_NAME]" <!-- For example: 'Grid', 'Button', etc. -->
|
|
196
1224
|
})
|
|
197
1225
|
\`\`\`
|
|
198
|
-
|
|
199
|
-
${
|
|
1226
|
+
` : ""}
|
|
1227
|
+
${ctx.includeResponsive ? `
|
|
200
1228
|
### Phase: Layout Responsive Finalization
|
|
201
1229
|
|
|
202
1230
|
**Step 1: Implement Responsive Behavior**
|
|
1231
|
+
${responsiveKDSGuidance}${responsiveVanillaCSSGuidance}${responsiveTailwindCSSGuidance}` : ""}
|
|
203
1232
|
|
|
204
|
-
|
|
205
|
-
\`\`\`
|
|
206
|
-
${e.layoutAssistantName}({
|
|
207
|
-
prompt="Provide Kendo Design System utility classes to make the following layout structure responsive across mobile, tablet, and desktop views: [LAYOUT DESCRIPTION HERE]",
|
|
208
|
-
includeBuildingBlockExamples=false // Optional, include examples of Kendo building blocks (Grid, Flex, Stack, etc.)
|
|
209
|
-
})
|
|
210
|
-
\`\`\`
|
|
211
|
-
`:""}
|
|
212
|
-
${e.includeTheming?`
|
|
1233
|
+
${ctx.includeTheming ? `
|
|
213
1234
|
**Step 2: Custom Theme Implementation**
|
|
214
1235
|
|
|
215
1236
|
**CUSTOM THEME REQUIREMENTS:**
|
|
@@ -219,13 +1240,13 @@ ${e.includeTheming?`
|
|
|
219
1240
|
|
|
220
1241
|
**Tool call example:**
|
|
221
1242
|
\`\`\`
|
|
222
|
-
${
|
|
1243
|
+
${ctx.styleAssistantName}({
|
|
223
1244
|
query="Create a custom Kendo UI theme that aligns with the following current goal and branding needs: [CURRENT GOAL HERE]. Include strict CSS variables or theme requirements as needed.",
|
|
224
1245
|
includeThemeVariables=true // Optional, include specific CSS variables for colors, typography, spacing, etc.
|
|
225
1246
|
})
|
|
226
1247
|
\`\`\`
|
|
227
|
-
|
|
228
|
-
${
|
|
1248
|
+
` : ""}
|
|
1249
|
+
${ctx.includeIcons ? `
|
|
229
1250
|
**Step 3: Iconography and Visual Enhancements**
|
|
230
1251
|
|
|
231
1252
|
**ICONOGRAPHY GUIDELINES:**
|
|
@@ -235,59 +1256,64 @@ ${e.includeIcons?`
|
|
|
235
1256
|
|
|
236
1257
|
**Tool call example:**
|
|
237
1258
|
\`\`\`
|
|
238
|
-
${
|
|
1259
|
+
${ctx.iconAssistantName}({
|
|
239
1260
|
query="Suggest icons that fit the theme and purpose of the following current goal: [CURRENT GOAL HERE]. Provide icon names and usage context.",
|
|
240
1261
|
})
|
|
241
1262
|
\`\`\`
|
|
242
|
-
|
|
1263
|
+
` : ""}
|
|
243
1264
|
### Phase: Code Generation
|
|
244
1265
|
**IMPORTANT:** Do NOT generate ANY code or create files until ALL information gathering phases (1-3) are complete.
|
|
245
1266
|
**Step 1: Generate Complete Implementation**
|
|
246
|
-
After completing all information gathering phases above, generate the complete ${
|
|
1267
|
+
After completing all information gathering phases above, generate the complete ${ctx.libKey} component/page implementation:
|
|
247
1268
|
**Code Generation Requirements:**
|
|
248
|
-
- Create complete, functional ${
|
|
249
|
-
- Include all identified ${
|
|
1269
|
+
- Create complete, functional ${ctx.libKey} components/pages
|
|
1270
|
+
- Include all identified ${ctx.suiteName} components with their configurations
|
|
250
1271
|
- Apply ALL Kendo Design System utility classes gathered from layout assistant
|
|
251
1272
|
- Implement proper component event handling and data binding
|
|
252
1273
|
- Ensure responsive behavior using Kendo utility classes
|
|
253
1274
|
- Include necessary import statements and dependencies
|
|
254
1275
|
|
|
255
|
-
${
|
|
1276
|
+
${options.injectedPlan ?? ""}
|
|
256
1277
|
|
|
257
1278
|
### Phase: Validation and Finalization
|
|
258
1279
|
|
|
259
1280
|
**Step 1: Code Validation**
|
|
260
1281
|
** CRITICAL REQUIREMENT: You MUST validate the generated UI code using this comprehensive checklist to ensure code quality and compliance:**
|
|
261
1282
|
|
|
262
|
-
${
|
|
1283
|
+
${validationInstructions}
|
|
263
1284
|
|
|
264
1285
|
**Step 2: Final Review and Testing**
|
|
265
1286
|
** Implementation Validation:**
|
|
266
|
-
${
|
|
267
|
-
- All ${
|
|
268
|
-
- Event handling and data binding operate correctly
|
|
269
|
-
${
|
|
1287
|
+
${ctx.includeComponentsDocs ? ` - Components render correctly without errors
|
|
1288
|
+
- All ${ctx.suiteName} component features function as expected
|
|
1289
|
+
- Event handling and data binding operate correctly` : ""}
|
|
1290
|
+
${ctx.includeResponsive ? `- Responsive layout works across breakpoints` : ""}
|
|
270
1291
|
|
|
271
1292
|
**Quick Fixes (if needed):**
|
|
272
1293
|
|
|
273
1294
|
After code implementation and validation you can make quick fixes as needed:
|
|
274
|
-
- Use ${
|
|
275
|
-
- Use ${
|
|
1295
|
+
- Use ${ctx.codeAssistantName} for component-specific issues
|
|
1296
|
+
- Use ${ctx.layoutAssistantName} for styling adjustments
|
|
276
1297
|
|
|
277
1298
|
+### Before You Begin
|
|
278
|
-
- When following/executing the plan call tools are defined from the same function_ecosystem/MCP_server which ${
|
|
1299
|
+
- When following/executing the plan call tools are defined from the same function_ecosystem/MCP_server which ${ctx.name} was called.
|
|
279
1300
|
- Do NOT generate or suggest any code until all tools have been used and validated responses are incorporated.
|
|
280
1301
|
- Focus on planning and sequencing.
|
|
281
1302
|
- Summarize the components, layouts, and utilities gathered.
|
|
282
|
-
- Confirm all required inputs from tools have been provided
|
|
1303
|
+
- Confirm all required inputs from tools have been provided.`;
|
|
1304
|
+
};
|
|
1305
|
+
|
|
1306
|
+
// packages/kendo-ui-agent/src/lib/templates/assistant-orchestrator.ts
|
|
1307
|
+
var renderAssistantOrchestratorResponse = (ctx) => {
|
|
1308
|
+
return `# ${ctx.suiteName} Assistant
|
|
283
1309
|
|
|
284
1310
|
**What you're looking for:**
|
|
285
1311
|
|
|
286
|
-
${
|
|
1312
|
+
${ctx.userIntent}
|
|
287
1313
|
|
|
288
1314
|
**Available Tools:**
|
|
289
|
-
- **${
|
|
290
|
-
- **${
|
|
1315
|
+
- **${ctx.codeAssistantName}** - Get component documentation, features, and usage examples
|
|
1316
|
+
- **${ctx.iconAssistantName}** - Search for icons from the Kendo icon library
|
|
291
1317
|
|
|
292
1318
|
---
|
|
293
1319
|
|
|
@@ -297,7 +1323,7 @@ Follow these steps to find the right components and resources:
|
|
|
297
1323
|
|
|
298
1324
|
### Step 1: Identify Components
|
|
299
1325
|
|
|
300
|
-
Based on your needs, consider these ${
|
|
1326
|
+
Based on your needs, consider these ${ctx.suiteName} component categories:
|
|
301
1327
|
|
|
302
1328
|
**Common Use Cases:**
|
|
303
1329
|
- **Data Display**: Grid, ListView, TreeView, Card, TileLayout
|
|
@@ -310,10 +1336,10 @@ Based on your needs, consider these ${e.suiteName} component categories:
|
|
|
310
1336
|
|
|
311
1337
|
**How to explore components:**
|
|
312
1338
|
|
|
313
|
-
Use the ${
|
|
1339
|
+
Use the ${ctx.codeAssistantName} tool to learn about specific components:
|
|
314
1340
|
|
|
315
1341
|
\`\`\`
|
|
316
|
-
${
|
|
1342
|
+
${ctx.codeAssistantName}({
|
|
317
1343
|
query: "What are the main features of [COMPONENT_NAME]?",
|
|
318
1344
|
component: "[COMPONENT_NAME]"
|
|
319
1345
|
})
|
|
@@ -321,14 +1347,14 @@ ${e.codeAssistantName}({
|
|
|
321
1347
|
|
|
322
1348
|
**Examples:**
|
|
323
1349
|
\`\`\`
|
|
324
|
-
${
|
|
1350
|
+
${ctx.codeAssistantName}({
|
|
325
1351
|
query: "How to use Grid with paging and sorting?",
|
|
326
1352
|
component: "Grid"
|
|
327
1353
|
})
|
|
328
1354
|
\`\`\`
|
|
329
1355
|
|
|
330
1356
|
\`\`\`
|
|
331
|
-
${
|
|
1357
|
+
${ctx.codeAssistantName}({
|
|
332
1358
|
query: "What features does DatePicker have?",
|
|
333
1359
|
component: "DatePicker"
|
|
334
1360
|
})
|
|
@@ -339,37 +1365,37 @@ ${e.codeAssistantName}({
|
|
|
339
1365
|
- Break complex queries into multiple calls focusing on one feature at a time
|
|
340
1366
|
- Specify the exact component name in the component parameter
|
|
341
1367
|
|
|
342
|
-
${
|
|
1368
|
+
${ctx.includeIcons ? `
|
|
343
1369
|
### Step 2: Find Icons
|
|
344
1370
|
|
|
345
|
-
Use the ${
|
|
1371
|
+
Use the ${ctx.iconAssistantName} tool to search for icons:
|
|
346
1372
|
|
|
347
1373
|
\`\`\`
|
|
348
|
-
${
|
|
1374
|
+
${ctx.iconAssistantName}({
|
|
349
1375
|
query: "Search for icons related to [YOUR THEME/PURPOSE]"
|
|
350
1376
|
})
|
|
351
1377
|
\`\`\`
|
|
352
1378
|
|
|
353
1379
|
**Examples:**
|
|
354
1380
|
\`\`\`
|
|
355
|
-
${
|
|
1381
|
+
${ctx.iconAssistantName}({
|
|
356
1382
|
query: "icons for navigation menu"
|
|
357
1383
|
})
|
|
358
1384
|
\`\`\`
|
|
359
1385
|
|
|
360
1386
|
\`\`\`
|
|
361
|
-
${
|
|
1387
|
+
${ctx.iconAssistantName}({
|
|
362
1388
|
query: "icons for user actions like edit, delete, save"
|
|
363
1389
|
})
|
|
364
1390
|
\`\`\`
|
|
365
|
-
|
|
1391
|
+
` : ""}
|
|
366
1392
|
|
|
367
1393
|
### Step 3: Review and Implement
|
|
368
1394
|
|
|
369
1395
|
After gathering information:
|
|
370
|
-
1. Review the component documentation provided by ${
|
|
371
|
-
2. Check which icons are available from ${
|
|
372
|
-
3. Use the official ${
|
|
1396
|
+
1. Review the component documentation provided by ${ctx.codeAssistantName}
|
|
1397
|
+
2. Check which icons are available from ${ctx.iconAssistantName}
|
|
1398
|
+
3. Use the official ${ctx.suiteName} documentation for detailed implementation guides
|
|
373
1399
|
4. Refer to the component examples and code snippets provided
|
|
374
1400
|
|
|
375
1401
|
---
|
|
@@ -377,144 +1403,1487 @@ After gathering information:
|
|
|
377
1403
|
## Getting Started
|
|
378
1404
|
|
|
379
1405
|
**Next Actions:**
|
|
380
|
-
1. Start by calling ${
|
|
381
|
-
2. ${
|
|
382
|
-
3. Follow the ${
|
|
1406
|
+
1. Start by calling ${ctx.codeAssistantName} for the components you need
|
|
1407
|
+
2. ${ctx.includeIcons ? `Search for icons using ${ctx.iconAssistantName}` : "Review the component documentation"}
|
|
1408
|
+
3. Follow the ${ctx.suiteName} official documentation for implementation details
|
|
383
1409
|
|
|
384
1410
|
**Important Notes:**
|
|
385
1411
|
- These tools provide documentation and guidance, not code generation
|
|
386
|
-
- Always refer to official ${
|
|
387
|
-
- Tool responses include usage examples and best practices`;
|
|
1412
|
+
- Always refer to official ${ctx.suiteName} documentation for the most up-to-date information
|
|
1413
|
+
- Tool responses include usage examples and best practices`;
|
|
1414
|
+
};
|
|
1415
|
+
|
|
1416
|
+
// packages/kendo-ui-agent/src/lib/templates/shared-validation.ts
|
|
1417
|
+
var getSharedValidationSteps = (uiLibrary, designSystem) => ({
|
|
1418
|
+
SETUP_VALIDATION: `
|
|
388
1419
|
UI Library Setup:
|
|
389
|
-
- ${
|
|
390
|
-
- ${
|
|
391
|
-
- ${
|
|
392
|
-
- Globalization and localization properly configured for ${
|
|
393
|
-
- Required ${
|
|
1420
|
+
- ${uiLibrary} modules properly imported and configured
|
|
1421
|
+
- ${uiLibrary} theme properly loaded and applied
|
|
1422
|
+
- ${uiLibrary} license properly configured
|
|
1423
|
+
- Globalization and localization properly configured for ${uiLibrary} components
|
|
1424
|
+
- Required ${uiLibrary} dependencies installed and up-to-date`,
|
|
1425
|
+
PERFORMANCE_OPTIMIZATION: `
|
|
394
1426
|
Performance Optimization:
|
|
395
1427
|
- Efficient rendering patterns to prevent unnecessary re-renders
|
|
396
|
-
- Lazy loading of ${
|
|
397
|
-
- Virtualization enabled for large datasets in ${
|
|
398
|
-
- Only required ${
|
|
399
|
-
|
|
400
|
-
|
|
1428
|
+
- Lazy loading of ${uiLibrary} components when appropriate
|
|
1429
|
+
- Virtualization enabled for large datasets in ${uiLibrary} components
|
|
1430
|
+
- Only required ${uiLibrary} modules imported and tree-shaken`,
|
|
1431
|
+
THEME_AND_STYLING: `
|
|
1432
|
+
${uiLibrary} Theme & Styling:
|
|
1433
|
+
- Prioritize the ${designSystem} Design System utilities
|
|
401
1434
|
- Avoid custom CSS or other framework classes
|
|
402
1435
|
- Do not use inline styles
|
|
403
|
-
- Consistent ${
|
|
1436
|
+
- Consistent ${uiLibrary} theme application across all components
|
|
404
1437
|
- Custom theme variables properly defined and scoped
|
|
405
|
-
- ${
|
|
406
|
-
- Brand colors integrated with ${
|
|
407
|
-
|
|
408
|
-
- Type: ${r}
|
|
409
|
-
- Summary: ${i}
|
|
410
|
-
- Syntax: ${d}`);let g=s.RelatedItemsByType?.return_type;if(g?.length){let p=g[0].Text;p&&n.push(`- Return Type: ${p}`)}let A=o.Examples??[];if(A.length>0){let p=`## Examples
|
|
411
|
-
`,l=0;for(let y of A){let S=(y.Component??"").toLowerCase(),C=t.toLowerCase();if(S&&S!==C&&S!=="general")continue;l+=1;let T=y.Code??"";p+=`### Example ${l}
|
|
412
|
-
\`\`\`
|
|
413
|
-
${T}
|
|
414
|
-
\`\`\`
|
|
415
|
-
`}l>0&&n.push(p)}let h=s.RelatedItemsByType??{},f=[];for(let[p,l]of Object.entries(h))if(!(p==="return_type"||p==="render_fragment"))for(let y of l??[]){let S=y.FullName??"",C=y.ShortText??"";f.push(`### ${S} (${p})
|
|
416
|
-
${C}`)}return f.length>0&&n.push(`## Additional Context
|
|
417
|
-
`+f.join(`
|
|
418
|
-
---
|
|
419
|
-
`)),n.join(`
|
|
1438
|
+
- ${uiLibrary} icon fonts properly loaded and optimized
|
|
1439
|
+
- Brand colors integrated with ${uiLibrary} component themes`
|
|
1440
|
+
});
|
|
420
1441
|
|
|
421
|
-
|
|
422
|
-
|
|
1442
|
+
// packages/kendo-ui-agent/src/lib/templates/tailwind-bridge.ts
|
|
1443
|
+
var tailwindBridgeExample = `
|
|
1444
|
+
@import "tailwindcss/theme.css";
|
|
1445
|
+
@import "tailwindcss/preflight.css";
|
|
1446
|
+
@import "tailwindcss/utilities.css";
|
|
1447
|
+
|
|
1448
|
+
:root {
|
|
1449
|
+
/* The generated Kendo Theme CSS variables */
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
|
|
1453
|
+
@theme inline { /* The Tailwind bridge mapping */
|
|
1454
|
+
/* Misc */
|
|
1455
|
+
--color-app-surface: var( --kendo-color-app-surface );
|
|
1456
|
+
--color-on-app-surface: var( --kendo-color-on-app-surface );
|
|
1457
|
+
--color-subtle: var( --kendo-color-subtle );
|
|
1458
|
+
--color-surface: var( --kendo-color-surface );
|
|
1459
|
+
--color-surface-alt: var( --kendo-color-surface-alt );
|
|
1460
|
+
--color-border: var( --kendo-color-border );
|
|
1461
|
+
--color-border-alt: var( --kendo-color-border-alt );
|
|
1462
|
+
/* Base */
|
|
1463
|
+
--color-base-subtle: var( --kendo-color-base-subtle );
|
|
1464
|
+
--color-base-subtle-hover: var( --kendo-color-base-subtle-hover );
|
|
1465
|
+
--color-base-subtle-active: var( --kendo-color-base-subtle-active );
|
|
1466
|
+
--color-base: var( --kendo-color-base );
|
|
1467
|
+
--color-base-hover: var( --kendo-color-base-hover );
|
|
1468
|
+
--color-base-active: var( --kendo-color-base-active );
|
|
1469
|
+
--color-base-emphasis: var( --kendo-color-base-emphasis );
|
|
1470
|
+
--color-base-on-subtle: var( --kendo-color-base-on-subtle );
|
|
1471
|
+
--color-on-base: var( --kendo-color-on-base );
|
|
1472
|
+
--color-base-on-surface: var( --kendo-color-base-on-surface );
|
|
1473
|
+
/* Primary */
|
|
1474
|
+
--color-primary-subtle: var( --kendo-color-primary-subtle );
|
|
1475
|
+
--color-primary-subtle-hover: var( --kendo-color-primary-subtle-hover );
|
|
1476
|
+
--color-primary-subtle-active: var( --kendo-color-primary-subtle-active );
|
|
1477
|
+
--color-primary: var( --kendo-color-primary );
|
|
1478
|
+
--color-primary-hover: var( --kendo-color-primary-hover );
|
|
1479
|
+
--color-primary-active: var( --kendo-color-primary-active );
|
|
1480
|
+
--color-primary-emphasis: var( --kendo-color-primary-emphasis );
|
|
1481
|
+
--color-primary-on-subtle: var( --kendo-color-primary-on-subtle );
|
|
1482
|
+
--color-on-primary: var( --kendo-color-on-primary );
|
|
1483
|
+
--color-primary-on-surface: var( --kendo-color-primary-on-surface );
|
|
1484
|
+
/* Secondary */
|
|
1485
|
+
--color-secondary-subtle: var( --kendo-color-secondary-subtle );
|
|
1486
|
+
--color-secondary-subtle-hover: var( --kendo-color-secondary-subtle-hover );
|
|
1487
|
+
--color-secondary-subtle-active: var( --kendo-color-secondary-subtle-active );
|
|
1488
|
+
--color-secondary: var( --kendo-color-secondary );
|
|
1489
|
+
--color-secondary-hover: var( --kendo-color-secondary-hover );
|
|
1490
|
+
--color-secondary-active: var( --kendo-color-secondary-active );
|
|
1491
|
+
--color-secondary-emphasis: var( --kendo-color-secondary-emphasis );
|
|
1492
|
+
--color-secondary-on-subtle: var( --kendo-color-secondary-on-subtle );
|
|
1493
|
+
--color-on-secondary: var( --kendo-color-on-secondary );
|
|
1494
|
+
--color-secondary-on-surface: var( --kendo-color-secondary-on-surface );
|
|
1495
|
+
/* Tertiary */
|
|
1496
|
+
--color-tertiary-subtle: var( --kendo-color-tertiary-subtle );
|
|
1497
|
+
--color-tertiary-subtle-hover: var( --kendo-color-tertiary-subtle-hover );
|
|
1498
|
+
--color-tertiary-subtle-active: var( --kendo-color-tertiary-subtle-active );
|
|
1499
|
+
--color-tertiary: var( --kendo-color-tertiary );
|
|
1500
|
+
--color-tertiary-hover: var( --kendo-color-tertiary-hover );
|
|
1501
|
+
--color-tertiary-active: var( --kendo-color-tertiary-active );
|
|
1502
|
+
--color-tertiary-emphasis: var( --kendo-color-tertiary-emphasis );
|
|
1503
|
+
--color-tertiary-on-subtle: var( --kendo-color-tertiary-on-subtle );
|
|
1504
|
+
--color-on-tertiary: var( --kendo-color-on-tertiary );
|
|
1505
|
+
--color-tertiary-on-surface: var( --kendo-color-tertiary-on-surface );
|
|
1506
|
+
/* Info */
|
|
1507
|
+
--color-info-subtle: var( --kendo-color-info-subtle );
|
|
1508
|
+
--color-info-subtle-hover: var( --kendo-color-info-subtle-hover );
|
|
1509
|
+
--color-info-subtle-active: var( --kendo-color-info-subtle-active );
|
|
1510
|
+
--color-info: var( --kendo-color-info );
|
|
1511
|
+
--color-info-hover: var( --kendo-color-info-hover );
|
|
1512
|
+
--color-info-active: var( --kendo-color-info-active );
|
|
1513
|
+
--color-info-emphasis: var( --kendo-color-info-emphasis );
|
|
1514
|
+
--color-info-on-subtle: var( --kendo-color-info-on-subtle );
|
|
1515
|
+
--color-on-info: var( --kendo-color-on-info );
|
|
1516
|
+
--color-info-on-surface: var( --kendo-color-info-on-surface );
|
|
1517
|
+
/* Success */
|
|
1518
|
+
--color-success-subtle: var( --kendo-color-success-subtle );
|
|
1519
|
+
--color-success-subtle-hover: var( --kendo-color-success-subtle-hover );
|
|
1520
|
+
--color-success-subtle-active: var( --kendo-color-success-subtle-active );
|
|
1521
|
+
--color-success: var( --kendo-color-success );
|
|
1522
|
+
--color-success-hover: var( --kendo-color-success-hover );
|
|
1523
|
+
--color-success-active: var( --kendo-color-success-active );
|
|
1524
|
+
--color-success-emphasis: var( --kendo-color-success-emphasis );
|
|
1525
|
+
--color-success-on-subtle: var( --kendo-color-success-on-subtle );
|
|
1526
|
+
--color-on-success: var( --kendo-color-on-success );
|
|
1527
|
+
--color-success-on-surface: var( --kendo-color-success-on-surface );
|
|
1528
|
+
/* Warning */
|
|
1529
|
+
--color-warning-subtle: var( --kendo-color-warning-subtle );
|
|
1530
|
+
--color-warning-subtle-hover: var( --kendo-color-warning-subtle-hover );
|
|
1531
|
+
--color-warning-subtle-active: var( --kendo-color-warning-subtle-active );
|
|
1532
|
+
--color-warning: var( --kendo-color-warning );
|
|
1533
|
+
--color-warning-hover: var( --kendo-color-warning-hover );
|
|
1534
|
+
--color-warning-active: var( --kendo-color-warning-active );
|
|
1535
|
+
--color-warning-emphasis: var( --kendo-color-warning-emphasis );
|
|
1536
|
+
--color-warning-on-subtle: var( --kendo-color-warning-on-subtle );
|
|
1537
|
+
--color-on-warning: var( --kendo-color-on-warning );
|
|
1538
|
+
--color-warning-on-surface: var( --kendo-color-warning-on-surface );
|
|
1539
|
+
/* Error */
|
|
1540
|
+
--color-error-subtle: var( --kendo-color-error-subtle );
|
|
1541
|
+
--color-error-subtle-hover: var( --kendo-color-error-subtle-hover );
|
|
1542
|
+
--color-error-subtle-active: var( --kendo-color-error-subtle-active );
|
|
1543
|
+
--color-error: var( --kendo-color-error );
|
|
1544
|
+
--color-error-hover: var( --kendo-color-error-hover );
|
|
1545
|
+
--color-error-active: var( --kendo-color-error-active );
|
|
1546
|
+
--color-error-emphasis: var( --kendo-color-error-emphasis );
|
|
1547
|
+
--color-error-on-subtle: var( --kendo-color-error-on-subtle );
|
|
1548
|
+
--color-on-error: var( --kendo-color-on-error );
|
|
1549
|
+
--color-error-on-surface: var( --kendo-color-error-on-surface );
|
|
1550
|
+
/* Light */
|
|
1551
|
+
--color-light-subtle: var( --kendo-color-light-subtle );
|
|
1552
|
+
--color-light-subtle-hover: var( --kendo-color-light-subtle-hover );
|
|
1553
|
+
--color-light-subtle-active: var( --kendo-color-light-subtle-active );
|
|
1554
|
+
--color-light: var( --kendo-color-light );
|
|
1555
|
+
--color-light-hover: var( --kendo-color-light-hover );
|
|
1556
|
+
--color-light-active: var( --kendo-color-light-active );
|
|
1557
|
+
--color-light-emphasis: var( --kendo-color-light-emphasis );
|
|
1558
|
+
--color-light-on-subtle: var( --kendo-color-light-on-subtle );
|
|
1559
|
+
--color-on-light: var( --kendo-color-on-light );
|
|
1560
|
+
--color-light-on-surface: var( --kendo-color-light-on-surface );
|
|
1561
|
+
/* Dark */
|
|
1562
|
+
--color-dark-subtle: var( --kendo-color-dark-subtle );
|
|
1563
|
+
--color-dark-subtle-hover: var( --kendo-color-dark-subtle-hover );
|
|
1564
|
+
--color-dark-subtle-active: var( --kendo-color-dark-subtle-active );
|
|
1565
|
+
--color-dark: var( --kendo-color-dark );
|
|
1566
|
+
--color-dark-hover: var( --kendo-color-dark-hover );
|
|
1567
|
+
--color-dark-active: var( --kendo-color-dark-active );
|
|
1568
|
+
--color-dark-emphasis: var( --kendo-color-dark-emphasis );
|
|
1569
|
+
--color-dark-on-subtle: var( --kendo-color-dark-on-subtle );
|
|
1570
|
+
--color-on-dark: var( --kendo-color-on-dark );
|
|
1571
|
+
--color-dark-on-surface: var( --kendo-color-dark-on-surface );
|
|
1572
|
+
/* Inverse */
|
|
1573
|
+
--color-inverse-subtle: var( --kendo-color-inverse-subtle );
|
|
1574
|
+
--color-inverse-subtle-hover: var( --kendo-color-inverse-subtle-hover );
|
|
1575
|
+
--color-inverse-subtle-active: var( --kendo-color-inverse-subtle-active );
|
|
1576
|
+
--color-inverse: var( --kendo-color-inverse );
|
|
1577
|
+
--color-inverse-hover: var( --kendo-color-inverse-hover );
|
|
1578
|
+
--color-inverse-active: var( --kendo-color-inverse-active );
|
|
1579
|
+
--color-inverse-emphasis: var( --kendo-color-inverse-emphasis );
|
|
1580
|
+
--color-inverse-on-subtle: var( --kendo-color-inverse-on-subtle );
|
|
1581
|
+
--color-on-inverse: var( --kendo-color-on-inverse );
|
|
1582
|
+
--color-inverse-on-surface: var( --kendo-color-inverse-on-surface );
|
|
1583
|
+
/* Series A */
|
|
1584
|
+
--color-series-a: var( --kendo-color-series-a );
|
|
1585
|
+
--color-series-a-bold: var( --kendo-color-series-a-bold );
|
|
1586
|
+
--color-series-a-bolder: var( --kendo-color-series-a-bolder );
|
|
1587
|
+
--color-series-a-subtle: var( --kendo-color-series-a-subtle );
|
|
1588
|
+
--color-series-a-subtler: var( --kendo-color-series-a-subtler );
|
|
1589
|
+
/* Series B */
|
|
1590
|
+
--color-series-b: var( --kendo-color-series-b );
|
|
1591
|
+
--color-series-b-bold: var( --kendo-color-series-b-bold );
|
|
1592
|
+
--color-series-b-bolder: var( --kendo-color-series-b-bolder );
|
|
1593
|
+
--color-series-b-subtle: var( --kendo-color-series-b-subtle );
|
|
1594
|
+
--color-series-b-subtler: var( --kendo-color-series-b-subtler );
|
|
1595
|
+
/* Series C */
|
|
1596
|
+
--color-series-c: var( --kendo-color-series-c );
|
|
1597
|
+
--color-series-c-bold: var( --kendo-color-series-c-bold );
|
|
1598
|
+
--color-series-c-bolder: var( --kendo-color-series-c-bolder );
|
|
1599
|
+
--color-series-c-subtle: var( --kendo-color-series-c-subtle );
|
|
1600
|
+
--color-series-c-subtler: var( --kendo-color-series-c-subtler );
|
|
1601
|
+
/* Series D */
|
|
1602
|
+
--color-series-d: var( --kendo-color-series-d );
|
|
1603
|
+
--color-series-d-bold: var( --kendo-color-series-d-bold );
|
|
1604
|
+
--color-series-d-bolder: var( --kendo-color-series-d-bolder );
|
|
1605
|
+
--color-series-d-subtle: var( --kendo-color-series-d-subtle );
|
|
1606
|
+
--color-series-d-subtler: var( --kendo-color-series-d-subtler );
|
|
1607
|
+
/* Series E */
|
|
1608
|
+
--color-series-e: var( --kendo-color-series-e );
|
|
1609
|
+
--color-series-e-bold: var( --kendo-color-series-e-bold );
|
|
1610
|
+
--color-series-e-bolder: var( --kendo-color-series-e-bolder );
|
|
1611
|
+
--color-series-e-subtle: var( --kendo-color-series-e-subtle );
|
|
1612
|
+
--color-series-e-subtler: var( --kendo-color-series-e-subtler );
|
|
1613
|
+
/* Series F */
|
|
1614
|
+
--color-series-f: var( --kendo-color-series-f );
|
|
1615
|
+
--color-series-f-bold: var( --kendo-color-series-f-bold );
|
|
1616
|
+
--color-series-f-bolder: var( --kendo-color-series-f-bolder );
|
|
1617
|
+
--color-series-f-subtle: var( --kendo-color-series-f-subtle );
|
|
1618
|
+
--color-series-f-subtler: var( --kendo-color-series-f-subtler );
|
|
1619
|
+
|
|
1620
|
+
/* Typography */
|
|
1621
|
+
--font-sans: var( --kendo-font-family-sans );
|
|
1622
|
+
--font-serif: var( --kendo-font-family-sans-serif );
|
|
1623
|
+
--font-mono: var( --kendo-font-family-monospace );
|
|
1624
|
+
--text-xs: var( --kendo-font-size-xs );
|
|
1625
|
+
--text-xs--line-height: var( --kendo-line-height-xs );
|
|
1626
|
+
--text-sm: var( --kendo-font-size-sm );
|
|
1627
|
+
--text-sm--line-height: var( --kendo-line-height-sm );
|
|
1628
|
+
--text-base: var( --kendo-font-size );
|
|
1629
|
+
--text-base--line-height: var( --kendo-line-height );
|
|
1630
|
+
--text-lg: var( --kendo-font-size-lg );
|
|
1631
|
+
--text-lg--line-height: var( --kendo-line-height-lg );
|
|
1632
|
+
|
|
1633
|
+
--radius-none: var( --kendo-border-radius-none );
|
|
1634
|
+
--radius-xs: var( --kendo-border-radius-xs );
|
|
1635
|
+
--radius-sm: var( --kendo-border-radius-sm );
|
|
1636
|
+
--radius-md: var( --kendo-border-radius-md );
|
|
1637
|
+
--radius-lg: var( --kendo-border-radius-lg );
|
|
1638
|
+
--radius-xl: var( --kendo-border-radius-xl );
|
|
1639
|
+
--radius-2xl: var( --kendo-border-radius-xxl );
|
|
1640
|
+
--radius-3xl: var( --kendo-border-radius-xxxl );
|
|
1641
|
+
--radius-4xl: 1.5rem;
|
|
1642
|
+
--radius-full: var( --kendo-border-radius-full );
|
|
1643
|
+
|
|
1644
|
+
/* Breakpoints */
|
|
1645
|
+
--breakpoint-sm: 576px;
|
|
1646
|
+
--breakpoint-md: 768px;
|
|
1647
|
+
--breakpoint-lg: 992px;
|
|
1648
|
+
--breakpoint-xl: 1200px;
|
|
1649
|
+
--breakpoint-2xl: 1400px;
|
|
1650
|
+
}`;
|
|
1651
|
+
|
|
1652
|
+
// packages/kendo-ui-agent/src/lib/tools-factory.ts
|
|
1653
|
+
function formatApiRefItemFromJson(rawValue, component) {
|
|
1654
|
+
let data;
|
|
1655
|
+
try {
|
|
1656
|
+
data = JSON.parse(rawValue);
|
|
1657
|
+
} catch {
|
|
1658
|
+
return rawValue;
|
|
1659
|
+
}
|
|
1660
|
+
const item = data?.Item;
|
|
1661
|
+
if (!item) {
|
|
1662
|
+
return rawValue;
|
|
1663
|
+
}
|
|
1664
|
+
const sectionParts = [];
|
|
1665
|
+
const name = item.FullName ?? item.Name ?? "";
|
|
1666
|
+
const type = item.Type ?? "";
|
|
1667
|
+
const summary = item.Summary ?? "";
|
|
1668
|
+
const syntax = item.SyntaxContent ?? "";
|
|
1669
|
+
sectionParts.push(
|
|
1670
|
+
`## API Reference Item
|
|
1671
|
+
- Name: ${name}
|
|
1672
|
+
- Type: ${type}
|
|
1673
|
+
- Summary: ${summary}
|
|
1674
|
+
- Syntax: ${syntax}`
|
|
1675
|
+
);
|
|
1676
|
+
const returnTypeItems = data.RelatedItemsByType?.["return_type"];
|
|
1677
|
+
if (returnTypeItems?.length) {
|
|
1678
|
+
const returnType = returnTypeItems[0].Text;
|
|
1679
|
+
if (returnType) {
|
|
1680
|
+
sectionParts.push(`- Return Type: ${returnType}`);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
const examples = item.Examples ?? [];
|
|
1684
|
+
if (examples.length > 0) {
|
|
1685
|
+
let exampleSection = "## Examples\n";
|
|
1686
|
+
let exampleIndex = 0;
|
|
1687
|
+
for (const example of examples) {
|
|
1688
|
+
const exComponent = (example.Component ?? "").toLowerCase();
|
|
1689
|
+
const compLower = component.toLowerCase();
|
|
1690
|
+
if (exComponent && exComponent !== compLower && exComponent !== "general") {
|
|
1691
|
+
continue;
|
|
1692
|
+
}
|
|
1693
|
+
exampleIndex += 1;
|
|
1694
|
+
const code = example.Code ?? "";
|
|
1695
|
+
exampleSection += `### Example ${exampleIndex}
|
|
1696
|
+
\`\`\`
|
|
1697
|
+
${code}
|
|
1698
|
+
\`\`\`
|
|
1699
|
+
`;
|
|
1700
|
+
}
|
|
1701
|
+
if (exampleIndex > 0) {
|
|
1702
|
+
sectionParts.push(exampleSection);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
const related = data.RelatedItemsByType ?? {};
|
|
1706
|
+
const additionalContextItems = [];
|
|
1707
|
+
for (const [relationType, relatedItems] of Object.entries(related)) {
|
|
1708
|
+
if (relationType === "return_type" || relationType === "render_fragment") {
|
|
1709
|
+
continue;
|
|
1710
|
+
}
|
|
1711
|
+
for (const relatedItem of relatedItems ?? []) {
|
|
1712
|
+
const fullName = relatedItem.FullName ?? "";
|
|
1713
|
+
const shortText = relatedItem.ShortText ?? "";
|
|
1714
|
+
additionalContextItems.push(`### ${fullName} (${relationType})
|
|
1715
|
+
${shortText}`);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
if (additionalContextItems.length > 0) {
|
|
1719
|
+
sectionParts.push("## Additional Context\n" + additionalContextItems.join("\n---\n"));
|
|
1720
|
+
}
|
|
1721
|
+
return sectionParts.join("\n\n");
|
|
1722
|
+
}
|
|
1723
|
+
var LAYOUT_MODE = process.env.KENDO_UIA_LAYOUT_MODE || "utils";
|
|
1724
|
+
function createOrchestratorTool(config, templateOptions, contextApi2) {
|
|
1725
|
+
const {
|
|
1726
|
+
name,
|
|
1727
|
+
title,
|
|
1728
|
+
description,
|
|
1729
|
+
layoutAssistantName: layoutAssistantName2,
|
|
1730
|
+
codeAssistantName: codeAssistantName2,
|
|
1731
|
+
styleAssistantName: styleAssistantName2,
|
|
1732
|
+
iconAssistantName: iconAssistantName2,
|
|
1733
|
+
accessibilityAssistantName: accessibilityAssistantName2,
|
|
1734
|
+
figmaD2CToolName: figmaD2CToolName2,
|
|
1735
|
+
suiteName,
|
|
1736
|
+
libKey,
|
|
1737
|
+
components
|
|
1738
|
+
} = config;
|
|
1739
|
+
const tool = new UIAgentTool(
|
|
1740
|
+
name,
|
|
1741
|
+
{
|
|
1742
|
+
title,
|
|
1743
|
+
description,
|
|
1744
|
+
requiredAccess: config.requiredAccess,
|
|
1745
|
+
inputSchema: {
|
|
1746
|
+
userIntent: z4.string().describe(
|
|
1747
|
+
`Detailed description and intent of what the user wants to build. Include the type of page/component, main functionality, visual requirements, and any specific ${suiteName} components you want to use.`
|
|
1748
|
+
),
|
|
1749
|
+
needsComponentsDocsConfidence: z4.number().min(0).max(1).default(1).describe(
|
|
1750
|
+
"Confidence (0-1) to include Component Documentation assistance. Default is 1."
|
|
1751
|
+
),
|
|
1752
|
+
needsLayoutConfidence: z4.number().min(0).max(1).default(0).describe(
|
|
1753
|
+
"Confidence (0-1) to include Layout assistance. Default is 0."
|
|
1754
|
+
),
|
|
1755
|
+
needsStylingConfidence: z4.number().min(0).max(1).default(0).describe(
|
|
1756
|
+
"Confidence (0-1) to include Theming/Styling assistance. Default is 0."
|
|
1757
|
+
),
|
|
1758
|
+
needsResponsiveBehaviorConfidence: z4.number().min(0).max(1).default(0).describe(
|
|
1759
|
+
"Confidence (0-1) to include Responsive Behavior assistance. Default is 0."
|
|
1760
|
+
),
|
|
1761
|
+
needsIconsConfidence: z4.number().min(0).max(1).default(0).describe(
|
|
1762
|
+
"Confidence (0-1) to include Icons assistance. Default is 0."
|
|
1763
|
+
),
|
|
1764
|
+
needsFigmaD2CConfidence: z4.boolean().describe("Whether to include Figma D2C assistance. If the prompt includes a Figma URL always make sure to set to true.").default(false),
|
|
1765
|
+
componentsNeeded: z4.array(z4.enum(components)).optional().describe(
|
|
1766
|
+
`Optional list of specific ${suiteName} components needed for the project.`
|
|
1767
|
+
)
|
|
1768
|
+
}
|
|
1769
|
+
},
|
|
1770
|
+
async ({
|
|
1771
|
+
userIntent,
|
|
1772
|
+
needsComponentsDocsConfidence,
|
|
1773
|
+
needsLayoutConfidence,
|
|
1774
|
+
needsStylingConfidence,
|
|
1775
|
+
needsResponsiveBehaviorConfidence,
|
|
1776
|
+
needsIconsConfidence,
|
|
1777
|
+
needsFigmaD2CConfidence,
|
|
1778
|
+
componentsNeeded
|
|
1779
|
+
}, { _meta }) => {
|
|
1780
|
+
log("UI Orchestrator called with:", {
|
|
1781
|
+
userIntent,
|
|
1782
|
+
needsComponentsDocsConfidence,
|
|
1783
|
+
needsLayoutConfidence,
|
|
1784
|
+
needsStylingConfidence,
|
|
1785
|
+
needsResponsiveBehaviorConfidence,
|
|
1786
|
+
needsIconsConfidence
|
|
1787
|
+
});
|
|
1788
|
+
try {
|
|
1789
|
+
await contextApi2.recordOrchestratorUserRequest({
|
|
1790
|
+
meta: {
|
|
1791
|
+
userIntent,
|
|
1792
|
+
componentsNeeded,
|
|
1793
|
+
_meta
|
|
1794
|
+
}
|
|
1795
|
+
});
|
|
1796
|
+
} catch (error) {
|
|
1797
|
+
throw getGrpcError(error);
|
|
1798
|
+
}
|
|
1799
|
+
const clientCapabilities = tool.server?.server.getClientCapabilities?.();
|
|
1800
|
+
const supportsElicitation = clientCapabilities?.elicitation;
|
|
1801
|
+
let forceIncludeCustomTheming;
|
|
1802
|
+
if (supportsElicitation && needsStylingConfidence > 0.3 && needsStylingConfidence <= 0.6) {
|
|
1803
|
+
const result = await tool.server?.server.elicitInput({
|
|
1804
|
+
message: "I was unable to determine if you need custom Kendo Theme assistant. Can you please confirm?",
|
|
1805
|
+
requestedSchema: {
|
|
1806
|
+
type: "object",
|
|
1807
|
+
properties: {
|
|
1808
|
+
includeCustomTheming: {
|
|
1809
|
+
type: "boolean",
|
|
1810
|
+
description: "Whether to include Custom Theming assistance in the workflow."
|
|
1811
|
+
}
|
|
1812
|
+
},
|
|
1813
|
+
required: ["includeCustomTheming"]
|
|
1814
|
+
}
|
|
1815
|
+
});
|
|
1816
|
+
if (result?.action === "accept" && result.content?.includeCustomTheming) {
|
|
1817
|
+
forceIncludeCustomTheming = true;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
const response = renderOrchestratorResponse(
|
|
1821
|
+
{
|
|
1822
|
+
name,
|
|
1823
|
+
userIntent,
|
|
1824
|
+
includeComponentsDocs: needsComponentsDocsConfidence > 0.3,
|
|
1825
|
+
includeTheming: needsStylingConfidence > 0.6 || !!forceIncludeCustomTheming,
|
|
1826
|
+
includeLayout: needsLayoutConfidence > 0.5,
|
|
1827
|
+
includeIcons: needsIconsConfidence > 0.6,
|
|
1828
|
+
includeResponsive: needsLayoutConfidence > 0.5 && needsResponsiveBehaviorConfidence > 0.4,
|
|
1829
|
+
includeFigmaD2C: needsFigmaD2CConfidence,
|
|
1830
|
+
layoutAssistantName: layoutAssistantName2,
|
|
1831
|
+
codeAssistantName: codeAssistantName2,
|
|
1832
|
+
styleAssistantName: styleAssistantName2,
|
|
1833
|
+
iconAssistantName: iconAssistantName2,
|
|
1834
|
+
accessibilityAssistantName: accessibilityAssistantName2,
|
|
1835
|
+
figmaD2CToolName: figmaD2CToolName2,
|
|
1836
|
+
suiteName,
|
|
1837
|
+
libKey,
|
|
1838
|
+
layoutMode: LAYOUT_MODE,
|
|
1839
|
+
components: componentsNeeded
|
|
1840
|
+
},
|
|
1841
|
+
templateOptions
|
|
1842
|
+
);
|
|
1843
|
+
return {
|
|
1844
|
+
content: [
|
|
1845
|
+
{
|
|
1846
|
+
type: "text",
|
|
1847
|
+
text: response
|
|
1848
|
+
}
|
|
1849
|
+
]
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1852
|
+
);
|
|
1853
|
+
return tool;
|
|
1854
|
+
}
|
|
1855
|
+
function createAssistantOrchestratorTool(config, contextApi2) {
|
|
1856
|
+
const {
|
|
1857
|
+
name,
|
|
1858
|
+
title,
|
|
1859
|
+
description,
|
|
1860
|
+
iconAssistantName: iconAssistantName2,
|
|
1861
|
+
codeAssistantName: codeAssistantName2,
|
|
1862
|
+
suiteName,
|
|
1863
|
+
libKey,
|
|
1864
|
+
components
|
|
1865
|
+
} = config;
|
|
1866
|
+
const tool = new UIAgentTool(
|
|
1867
|
+
name,
|
|
1868
|
+
{
|
|
1869
|
+
title,
|
|
1870
|
+
description,
|
|
1871
|
+
inputSchema: {
|
|
1872
|
+
userIntent: z4.string().describe(
|
|
1873
|
+
`Describe what you want to build or what components you're looking for. Include the type of functionality, features, or UI elements you need from ${suiteName}.`
|
|
1874
|
+
),
|
|
1875
|
+
needsIconsConfidence: z4.number().min(0).max(1).default(0.7).describe(
|
|
1876
|
+
"Confidence (0-1) to include Icons assistance. Default is 0.7."
|
|
1877
|
+
),
|
|
1878
|
+
componentsNeeded: z4.array(z4.enum(components)).optional().describe(
|
|
1879
|
+
`Optional list of specific ${suiteName} components you want to learn about.`
|
|
1880
|
+
)
|
|
1881
|
+
},
|
|
1882
|
+
requiredAccess: config.requiredAccess
|
|
1883
|
+
},
|
|
1884
|
+
async ({
|
|
1885
|
+
userIntent,
|
|
1886
|
+
needsIconsConfidence,
|
|
1887
|
+
componentsNeeded
|
|
1888
|
+
}, { _meta }) => {
|
|
1889
|
+
log("Assistant Orchestrator called with:", {
|
|
1890
|
+
userIntent,
|
|
1891
|
+
needsIconsConfidence,
|
|
1892
|
+
componentsNeeded
|
|
1893
|
+
});
|
|
1894
|
+
try {
|
|
1895
|
+
await contextApi2.recordOrchestratorUserRequest({
|
|
1896
|
+
meta: {
|
|
1897
|
+
userIntent,
|
|
1898
|
+
componentsNeeded,
|
|
1899
|
+
_meta
|
|
1900
|
+
}
|
|
1901
|
+
});
|
|
1902
|
+
} catch (error) {
|
|
1903
|
+
throw getGrpcError(error);
|
|
1904
|
+
}
|
|
1905
|
+
const response = renderAssistantOrchestratorResponse({
|
|
1906
|
+
name,
|
|
1907
|
+
userIntent,
|
|
1908
|
+
includeComponentsDocs: true,
|
|
1909
|
+
// Always true for assistant mode
|
|
1910
|
+
includeIcons: needsIconsConfidence > 0.5,
|
|
1911
|
+
codeAssistantName: codeAssistantName2,
|
|
1912
|
+
iconAssistantName: iconAssistantName2,
|
|
1913
|
+
suiteName,
|
|
1914
|
+
libKey,
|
|
1915
|
+
components: componentsNeeded
|
|
1916
|
+
});
|
|
1917
|
+
return {
|
|
1918
|
+
content: [
|
|
1919
|
+
{
|
|
1920
|
+
type: "text",
|
|
1921
|
+
text: response
|
|
1922
|
+
}
|
|
1923
|
+
]
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
);
|
|
1927
|
+
return tool;
|
|
1928
|
+
}
|
|
1929
|
+
function createLayoutAssistantTool(config, contextApi2) {
|
|
1930
|
+
const {
|
|
1931
|
+
name,
|
|
1932
|
+
title,
|
|
1933
|
+
description,
|
|
1934
|
+
suiteName: _suiteName,
|
|
1935
|
+
libKey,
|
|
1936
|
+
getPreContent
|
|
1937
|
+
} = config;
|
|
1938
|
+
return new UIAgentTool(
|
|
1939
|
+
name,
|
|
1940
|
+
{
|
|
1941
|
+
title,
|
|
1942
|
+
description,
|
|
1943
|
+
requiredAccess: LAYOUT_MODE === "utils" ? config.requiredAccess : ["none"],
|
|
1944
|
+
inputSchema: {
|
|
1945
|
+
prompt: z4.string().describe(
|
|
1946
|
+
`An enhanced prompt describing the HTML layout structure, design requirements or specific visual effects you want to achieve.`
|
|
1947
|
+
),
|
|
1948
|
+
includeBuildingBlockExamples: z4.boolean().describe(
|
|
1949
|
+
"Whether to include building block examples. Defaults to false."
|
|
1950
|
+
).default(false)
|
|
1951
|
+
}
|
|
1952
|
+
},
|
|
1953
|
+
async ({ prompt, includeBuildingBlockExamples }, { _meta }) => {
|
|
1954
|
+
log("Layout Assistant called with:", {
|
|
1955
|
+
prompt,
|
|
1956
|
+
includeBuildingBlockExamples
|
|
1957
|
+
});
|
|
1958
|
+
try {
|
|
1959
|
+
const cssResponse = await contextApi2.queryCssUtils({ query: prompt }, { _meta });
|
|
1960
|
+
let finalMarkdown = cssResponse.values.join("\n") || "No CSS utilities found for the given prompt.";
|
|
1961
|
+
const preContent = getPreContent?.() ?? "";
|
|
1962
|
+
const buildLayout = (buildingBlockMd) => {
|
|
1963
|
+
return `${preContent}
|
|
423
1964
|
|
|
424
1965
|
# Kendo Design System CSS Utilities
|
|
425
1966
|
|
|
426
|
-
${
|
|
427
|
-
${
|
|
428
|
-
`;
|
|
1967
|
+
${finalMarkdown}
|
|
1968
|
+
${buildingBlockMd ?? ""}
|
|
1969
|
+
`;
|
|
1970
|
+
};
|
|
1971
|
+
if (includeBuildingBlockExamples) {
|
|
1972
|
+
try {
|
|
1973
|
+
const buildingBlocksResponse = await contextApi2.queryBuildingBlocks({
|
|
1974
|
+
query: prompt,
|
|
1975
|
+
lib_name: libKey
|
|
1976
|
+
}, { _meta });
|
|
1977
|
+
let buildingBlockMd = "";
|
|
1978
|
+
if (buildingBlocksResponse.items?.length > 0) {
|
|
1979
|
+
buildingBlockMd = `
|
|
429
1980
|
## Building Blocks Examples
|
|
430
1981
|
|
|
431
1982
|
The examples here are for demonstration purpose how CSS utils can be implemented in various building blocks.
|
|
432
1983
|
However, consider your own design requirements and specifics in order to implement your templates/building blocks correctly.
|
|
433
1984
|
|
|
434
|
-
${
|
|
435
|
-
|
|
1985
|
+
${buildingBlocksResponse.items.map(
|
|
1986
|
+
(item) => `
|
|
1987
|
+
### ${item.name}
|
|
436
1988
|
|
|
437
|
-
${
|
|
1989
|
+
${item.description}
|
|
438
1990
|
|
|
439
1991
|
\`\`\`code
|
|
440
|
-
${
|
|
1992
|
+
${item.code}
|
|
441
1993
|
\`\`\`
|
|
442
|
-
`
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
1994
|
+
`
|
|
1995
|
+
).join("\n")}`;
|
|
1996
|
+
}
|
|
1997
|
+
finalMarkdown = buildLayout(buildingBlockMd);
|
|
1998
|
+
} catch (buildingBlockError) {
|
|
1999
|
+
log("Error calling building-blocks service:", buildingBlockError);
|
|
2000
|
+
finalMarkdown = buildLayout();
|
|
2001
|
+
}
|
|
2002
|
+
} else {
|
|
2003
|
+
finalMarkdown = buildLayout();
|
|
2004
|
+
}
|
|
2005
|
+
return {
|
|
2006
|
+
content: [
|
|
2007
|
+
{
|
|
2008
|
+
type: "text",
|
|
2009
|
+
text: finalMarkdown
|
|
2010
|
+
}
|
|
2011
|
+
]
|
|
2012
|
+
};
|
|
2013
|
+
} catch (error) {
|
|
2014
|
+
throw getGrpcError(error);
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
);
|
|
2018
|
+
}
|
|
2019
|
+
function createStyleAssistantTool(config, contextApi2) {
|
|
2020
|
+
const { name, title, description, libKey: _libKey, getPreContent } = config;
|
|
2021
|
+
return new UIAgentTool(
|
|
2022
|
+
name,
|
|
2023
|
+
{
|
|
2024
|
+
title,
|
|
2025
|
+
description,
|
|
2026
|
+
requiredAccess: config.requiredAccess,
|
|
2027
|
+
inputSchema: {
|
|
2028
|
+
prompt: z4.string().describe(
|
|
2029
|
+
`A natural language description of the desired visual style or theme. For example: 'Create a modern dark theme with high contrast'.`
|
|
2030
|
+
)
|
|
2031
|
+
}
|
|
2032
|
+
},
|
|
2033
|
+
async ({ prompt }, { _meta }) => {
|
|
2034
|
+
log("Style Assistant called with:", { prompt });
|
|
2035
|
+
let formattedCSS = "";
|
|
2036
|
+
try {
|
|
2037
|
+
const result = await contextApi2.queryThemeGenerate({ prompt }, { _meta });
|
|
2038
|
+
if (result.variables_json) {
|
|
2039
|
+
const theme = JSON.parse(result.variables_json);
|
|
2040
|
+
let cssBlocks = [];
|
|
2041
|
+
if (theme) {
|
|
2042
|
+
for (const key of Object.keys(theme)) {
|
|
2043
|
+
if (typeof theme[key] === "string") {
|
|
2044
|
+
cssBlocks.push(`${key}: ${theme[key]}`);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
formattedCSS = cssBlocks.join("\n\n");
|
|
2049
|
+
}
|
|
2050
|
+
} catch (error) {
|
|
2051
|
+
throw getGrpcError(error);
|
|
2052
|
+
}
|
|
2053
|
+
const preContent = getPreContent?.() ?? "";
|
|
2054
|
+
const response = formattedCSS ? `Use the following CSS variables to style the web application, including colors, typography, spacing, and component appearance.
|
|
446
2055
|
Ensure the variables are applied consistently across all relevant elements.
|
|
447
2056
|
Do NOT set any inline styles. CSS variables should be defined in global or component-level CSS files, or via supported theming mechanisms.
|
|
448
2057
|
|
|
449
|
-
${
|
|
2058
|
+
${preContent}
|
|
450
2059
|
|
|
451
2060
|
## Generated CSS Variables
|
|
452
2061
|
|
|
453
2062
|
\`\`\`css
|
|
454
|
-
${
|
|
2063
|
+
${formattedCSS}
|
|
455
2064
|
\`\`\`
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
2065
|
+
` : "No CSS variables were generated for the given prompt.";
|
|
2066
|
+
return {
|
|
2067
|
+
content: [
|
|
2068
|
+
{
|
|
2069
|
+
type: "text",
|
|
2070
|
+
text: response
|
|
2071
|
+
},
|
|
2072
|
+
LAYOUT_MODE === "tailwind" ? {
|
|
2073
|
+
type: "text",
|
|
2074
|
+
text: `Note: For layout and spacing, use Tailwind CSS utility classes to structure your layout and apply spacing between elements.
|
|
2075
|
+
IMPORTANT: In order to use the Kendo Theme CSS variables to work in a Tailwind CSS project, make sure to configure Tailwind to allow custom CSS properties and use the generated CSS variables in your Tailwind configuration for colors, spacing, etc. This way you can leverage the design tokens generated by the Style Assistant while using Tailwind for layout and utility classes. Follow the tailwind_bridge_example provided as insperation for how to bridge Kendo Theme CSS variables with Tailwind CSS.
|
|
2076
|
+
|
|
2077
|
+
<taiwind_bridge_example>
|
|
2078
|
+
${tailwindBridgeExample}
|
|
2079
|
+
</taiwind_bridge_example>
|
|
2080
|
+
`
|
|
2081
|
+
} : {
|
|
2082
|
+
type: "text",
|
|
2083
|
+
text: "Note: For layout and spacing, use the Layout Assistant tool to get the appropriate Kendo CSS utility classes."
|
|
2084
|
+
}
|
|
2085
|
+
]
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
);
|
|
2089
|
+
}
|
|
2090
|
+
function createIconSearchTool(config, contextApi2) {
|
|
2091
|
+
const { name, title, description, libKey: _libKey, getPostContent } = config;
|
|
2092
|
+
return new UIAgentTool(
|
|
2093
|
+
name,
|
|
2094
|
+
{
|
|
2095
|
+
title,
|
|
2096
|
+
description,
|
|
2097
|
+
requiredAccess: config.requiredAccess,
|
|
2098
|
+
inputSchema: {
|
|
2099
|
+
query: z4.string().describe(
|
|
2100
|
+
"A search query for finding icons. Be specific about the icon purpose, style, or action."
|
|
2101
|
+
),
|
|
2102
|
+
limit: z4.number().min(0.1).max(1).default(0.1).describe(
|
|
2103
|
+
`The proportion of matching icons to return (0.1 = 1 icon, 0.5 = 5 icons, 1.0 = many icons). Default is 0.1.`
|
|
2104
|
+
)
|
|
2105
|
+
}
|
|
2106
|
+
},
|
|
2107
|
+
async ({ query, limit }, { _meta }) => {
|
|
2108
|
+
log("Icon Search called with:", { query, limit });
|
|
2109
|
+
try {
|
|
2110
|
+
const result = await contextApi2.queryIconsSearch({
|
|
2111
|
+
query,
|
|
2112
|
+
limit: limit * 100
|
|
2113
|
+
}, { _meta });
|
|
2114
|
+
const postContent = getPostContent?.() ?? "";
|
|
2115
|
+
const response = `${result.values.join("\n\n")}
|
|
2116
|
+
|
|
2117
|
+
${postContent}`;
|
|
2118
|
+
return {
|
|
2119
|
+
content: [
|
|
2120
|
+
{
|
|
2121
|
+
type: "text",
|
|
2122
|
+
text: response
|
|
2123
|
+
}
|
|
2124
|
+
]
|
|
2125
|
+
};
|
|
2126
|
+
} catch (error) {
|
|
2127
|
+
throw getGrpcError(error);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
);
|
|
2131
|
+
}
|
|
2132
|
+
function createCodeAssistantTool(config, contextApi2) {
|
|
2133
|
+
const {
|
|
2134
|
+
name,
|
|
2135
|
+
title,
|
|
2136
|
+
description,
|
|
2137
|
+
libKey,
|
|
2138
|
+
suiteName: _suiteName,
|
|
2139
|
+
components,
|
|
2140
|
+
componentAliases,
|
|
2141
|
+
getPostContent
|
|
2142
|
+
} = config;
|
|
2143
|
+
return new UIAgentTool(
|
|
2144
|
+
name,
|
|
2145
|
+
{
|
|
2146
|
+
title,
|
|
2147
|
+
description,
|
|
2148
|
+
requiredAccess: config.requiredAccess,
|
|
2149
|
+
inputSchema: {
|
|
2150
|
+
query: z4.string().describe("The query to search for."),
|
|
2151
|
+
component: z4.enum(components).describe(
|
|
2152
|
+
"The component to search for. If not specified, use General."
|
|
2153
|
+
)
|
|
2154
|
+
}
|
|
2155
|
+
},
|
|
2156
|
+
async ({ query, component }, { _meta }) => {
|
|
2157
|
+
log("Code Assistant called with:", { query, component });
|
|
2158
|
+
let resolvedComponent = component;
|
|
2159
|
+
if (componentAliases) {
|
|
2160
|
+
const alias = componentAliases[component];
|
|
2161
|
+
if (alias) {
|
|
2162
|
+
resolvedComponent = alias;
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
try {
|
|
2166
|
+
const response = await contextApi2.query({
|
|
2167
|
+
query,
|
|
2168
|
+
component: resolvedComponent?.toLowerCase() ?? "general",
|
|
2169
|
+
lib_name: libKey,
|
|
2170
|
+
text_matches_count: 3,
|
|
2171
|
+
code_matches_count: 0,
|
|
2172
|
+
allowed_types: ["documentation"]
|
|
2173
|
+
}, { _meta });
|
|
2174
|
+
const cleanedValues = response.values.map(
|
|
2175
|
+
(val) => val.replace(/\0/g, "")
|
|
2176
|
+
);
|
|
2177
|
+
const postContent = getPostContent?.({ component }) ?? "";
|
|
2178
|
+
const combinedResponse = `${cleanedValues.join("\n\n")}
|
|
2179
|
+
|
|
2180
|
+
${postContent}`;
|
|
2181
|
+
return {
|
|
2182
|
+
content: [
|
|
2183
|
+
{
|
|
2184
|
+
type: "text",
|
|
2185
|
+
text: combinedResponse
|
|
2186
|
+
}
|
|
2187
|
+
]
|
|
2188
|
+
};
|
|
2189
|
+
} catch (error) {
|
|
2190
|
+
throw getGrpcError(error);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
);
|
|
2194
|
+
}
|
|
2195
|
+
function createAccessibilityAssistantTool(config, contextApi2) {
|
|
2196
|
+
const {
|
|
2197
|
+
name,
|
|
2198
|
+
title,
|
|
2199
|
+
description,
|
|
2200
|
+
libKey,
|
|
2201
|
+
suiteName: _suiteName,
|
|
2202
|
+
components,
|
|
2203
|
+
componentAliases
|
|
2204
|
+
} = config;
|
|
2205
|
+
return new UIAgentTool(
|
|
2206
|
+
name,
|
|
2207
|
+
{
|
|
2208
|
+
title,
|
|
2209
|
+
description,
|
|
2210
|
+
requiredAccess: config.requiredAccess,
|
|
2211
|
+
inputSchema: {
|
|
2212
|
+
query: z4.string().describe("The query to search for."),
|
|
2213
|
+
component: z4.enum(components).describe(
|
|
2214
|
+
"The component to search for. If not specified, use General."
|
|
2215
|
+
),
|
|
2216
|
+
includeGeneralGuidelines: z4.boolean().default(false).describe(
|
|
2217
|
+
"Set to true only on the first call to include comprehensive WCAG 2.2 Level AA general guidelines. After the first call, set to false to get only component-specific accessibility information. Default is false."
|
|
2218
|
+
)
|
|
2219
|
+
}
|
|
2220
|
+
},
|
|
2221
|
+
async ({ query, component, includeGeneralGuidelines }, { _meta }) => {
|
|
2222
|
+
log("Accessibility Assistant called with:", {
|
|
2223
|
+
query,
|
|
2224
|
+
component,
|
|
2225
|
+
includeGeneralGuidelines
|
|
2226
|
+
});
|
|
2227
|
+
let resolvedComponent = component;
|
|
2228
|
+
if (componentAliases) {
|
|
2229
|
+
const alias = componentAliases[component];
|
|
2230
|
+
if (alias) {
|
|
2231
|
+
resolvedComponent = alias;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
try {
|
|
2235
|
+
let finalResponse = "";
|
|
2236
|
+
if (includeGeneralGuidelines) {
|
|
2237
|
+
const generalGuidelines = getAccessibilityGuidelines();
|
|
2238
|
+
finalResponse += "# WCAG 2.2 Level AA General Accessibility Guidelines\n\n";
|
|
2239
|
+
finalResponse += generalGuidelines;
|
|
2240
|
+
finalResponse += "\n\n---\n\n";
|
|
2241
|
+
}
|
|
2242
|
+
const response = await contextApi2.getApiRefMatches({
|
|
2243
|
+
query,
|
|
2244
|
+
component: resolvedComponent?.toLowerCase() ?? "general",
|
|
2245
|
+
lib_name: libKey,
|
|
2246
|
+
filters_json: '{"type":{"$in":["accessibility"]}}'
|
|
2247
|
+
}, { _meta });
|
|
2248
|
+
const cleanedValues = response.values.map(
|
|
2249
|
+
(val) => val.replace(/\0/g, "")
|
|
2250
|
+
);
|
|
2251
|
+
const formattedValues = cleanedValues.map(
|
|
2252
|
+
(val) => formatApiRefItemFromJson(val, resolvedComponent?.toLowerCase() ?? "general")
|
|
2253
|
+
);
|
|
2254
|
+
const componentResult = formattedValues.join("\n\n");
|
|
2255
|
+
if (componentResult) {
|
|
2256
|
+
if (includeGeneralGuidelines) {
|
|
2257
|
+
finalResponse += "# Component-Specific Accessibility Information\n\n";
|
|
2258
|
+
}
|
|
2259
|
+
finalResponse += componentResult;
|
|
2260
|
+
} else if (!includeGeneralGuidelines) {
|
|
2261
|
+
finalResponse = "No component-specific accessibility guidelines found.";
|
|
2262
|
+
}
|
|
2263
|
+
return {
|
|
2264
|
+
content: [
|
|
2265
|
+
{
|
|
2266
|
+
type: "text",
|
|
2267
|
+
text: finalResponse
|
|
2268
|
+
}
|
|
2269
|
+
]
|
|
2270
|
+
};
|
|
2271
|
+
} catch (error) {
|
|
2272
|
+
throw getGrpcError(error);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
);
|
|
2276
|
+
}
|
|
2277
|
+
function getGrpcError(error) {
|
|
2278
|
+
const errorObj = error;
|
|
2279
|
+
if (errorObj.code === 16 /* UNAUTHENTICATED */) {
|
|
2280
|
+
return new Error(
|
|
2281
|
+
"Error: You are not authenticated. Please verify the Telerik License Key provided is valid."
|
|
2282
|
+
);
|
|
2283
|
+
} else if (errorObj.code === 8 /* RESOURCE_EXHAUSTED */) {
|
|
2284
|
+
return new Error(
|
|
2285
|
+
`Error: You have exceeded the extension quota. Upgrade to subscription licensing to unlock full access. Message: ${errorObj.message}.`
|
|
2286
|
+
);
|
|
2287
|
+
} else if (errorObj.code === 7 /* PERMISSION_DENIED */) {
|
|
2288
|
+
return new Error(
|
|
2289
|
+
"Error: You do not have permission to access this resource. Please verify that you have a valid license."
|
|
2290
|
+
);
|
|
2291
|
+
} else {
|
|
2292
|
+
return new Error(
|
|
2293
|
+
`Error: An error occurred while calling the service. Please try again later.
|
|
2294
|
+
|
|
2295
|
+
Error Code: ${errorObj.code} Error Message: ${errorObj.message}`
|
|
2296
|
+
);
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
463
2299
|
|
|
464
|
-
|
|
2300
|
+
// packages/kendo-ui-agent/src/lib/figma-d2c/types.ts
|
|
2301
|
+
var FigmaAPIError = class extends Error {
|
|
2302
|
+
constructor(status, body) {
|
|
2303
|
+
super(`Figma API error ${status}: ${body}`);
|
|
2304
|
+
this.status = status;
|
|
2305
|
+
this.body = body;
|
|
2306
|
+
this.name = "FigmaAPIError";
|
|
2307
|
+
}
|
|
2308
|
+
};
|
|
465
2309
|
|
|
466
|
-
|
|
2310
|
+
// packages/kendo-ui-agent/src/lib/figma-d2c/figma-client.ts
|
|
2311
|
+
var FigmaClient = class {
|
|
2312
|
+
constructor(apiKey) {
|
|
2313
|
+
this.retryConfig = {
|
|
2314
|
+
maxRetries: 3,
|
|
2315
|
+
baseDelayMs: 1e3,
|
|
2316
|
+
maxDelayMs: 3e4
|
|
2317
|
+
};
|
|
2318
|
+
this.apiKey = apiKey;
|
|
2319
|
+
}
|
|
2320
|
+
/**
|
|
2321
|
+
* Fetch file metadata including node and component counts
|
|
2322
|
+
*/
|
|
2323
|
+
async getFileMeta(fileKey) {
|
|
2324
|
+
const fileResponse = await this.getFile(fileKey, { depth: 1 });
|
|
2325
|
+
const counts = this.countNodesAndComponents(fileResponse.document);
|
|
2326
|
+
return {
|
|
2327
|
+
nodeCount: counts.nodes,
|
|
2328
|
+
componentCount: counts.components
|
|
2329
|
+
};
|
|
2330
|
+
}
|
|
2331
|
+
/**
|
|
2332
|
+
* Fetch a specific file with optional depth parameter
|
|
2333
|
+
*/
|
|
2334
|
+
async getFile(fileKey, options) {
|
|
2335
|
+
const url = this.buildFileUrl(fileKey, options);
|
|
2336
|
+
return this.fetchWithRetry(url);
|
|
2337
|
+
}
|
|
2338
|
+
/**
|
|
2339
|
+
* Fetch specific nodes by ID
|
|
2340
|
+
*/
|
|
2341
|
+
async getNodes(fileKey, nodeIds, options) {
|
|
2342
|
+
const url = this.buildNodesUrl(fileKey, nodeIds, options);
|
|
2343
|
+
return this.fetchWithRetry(url);
|
|
2344
|
+
}
|
|
2345
|
+
/**
|
|
2346
|
+
* Get export URLs for node images
|
|
2347
|
+
*/
|
|
2348
|
+
async getImages(fileKey, nodeIds, options) {
|
|
2349
|
+
const url = this.buildImagesUrl(fileKey, nodeIds, options);
|
|
2350
|
+
return this.fetchWithRetry(url);
|
|
2351
|
+
}
|
|
2352
|
+
/**
|
|
2353
|
+
* Get download URLs for embedded image fills
|
|
2354
|
+
* Returns a map of imageRef hashes to temporary S3 download URLs
|
|
2355
|
+
*/
|
|
2356
|
+
async getImageFills(fileKey) {
|
|
2357
|
+
const url = `https://api.figma.com/v1/files/${fileKey}/images`;
|
|
2358
|
+
return this.fetchWithRetry(url);
|
|
2359
|
+
}
|
|
2360
|
+
/**
|
|
2361
|
+
* Core fetch method with retry logic
|
|
2362
|
+
*/
|
|
2363
|
+
async fetchWithRetry(url) {
|
|
2364
|
+
let lastError = null;
|
|
2365
|
+
for (let attempt = 0; attempt < this.retryConfig.maxRetries; attempt++) {
|
|
2366
|
+
try {
|
|
2367
|
+
const response = await fetch(url, {
|
|
2368
|
+
headers: {
|
|
2369
|
+
"X-Figma-Token": this.apiKey,
|
|
2370
|
+
"Content-Type": "application/json"
|
|
2371
|
+
}
|
|
2372
|
+
});
|
|
2373
|
+
if (response.status === 429) {
|
|
2374
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
2375
|
+
const delayMs = retryAfter ? parseInt(retryAfter) * 1e3 : this.calculateBackoff(attempt);
|
|
2376
|
+
await this.sleep(delayMs);
|
|
2377
|
+
continue;
|
|
2378
|
+
}
|
|
2379
|
+
if (!response.ok) {
|
|
2380
|
+
const body = await response.text();
|
|
2381
|
+
throw new FigmaAPIError(response.status, body);
|
|
2382
|
+
}
|
|
2383
|
+
return await response.json();
|
|
2384
|
+
} catch (err) {
|
|
2385
|
+
lastError = err;
|
|
2386
|
+
if (err instanceof FigmaAPIError && err.status >= 400 && err.status < 500) {
|
|
2387
|
+
throw err;
|
|
2388
|
+
}
|
|
2389
|
+
if (attempt < this.retryConfig.maxRetries - 1) {
|
|
2390
|
+
await this.sleep(this.calculateBackoff(attempt));
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
throw lastError ?? new Error("Max retries exceeded");
|
|
2395
|
+
}
|
|
2396
|
+
/**
|
|
2397
|
+
* Calculate exponential backoff with jitter
|
|
2398
|
+
*/
|
|
2399
|
+
calculateBackoff(attempt) {
|
|
2400
|
+
const delay = this.retryConfig.baseDelayMs * Math.pow(2, attempt);
|
|
2401
|
+
const jitter = Math.random() * 0.3 * delay;
|
|
2402
|
+
return Math.min(delay + jitter, this.retryConfig.maxDelayMs);
|
|
2403
|
+
}
|
|
2404
|
+
sleep(ms) {
|
|
2405
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2406
|
+
}
|
|
2407
|
+
buildFileUrl(fileKey, options) {
|
|
2408
|
+
const base = `https://api.figma.com/v1/files/${fileKey}`;
|
|
2409
|
+
const params = new URLSearchParams();
|
|
2410
|
+
if (options?.depth) {
|
|
2411
|
+
params.set("depth", options.depth.toString());
|
|
2412
|
+
}
|
|
2413
|
+
return params.toString() ? `${base}?${params}` : base;
|
|
2414
|
+
}
|
|
2415
|
+
buildNodesUrl(fileKey, nodeIds, options) {
|
|
2416
|
+
const base = `https://api.figma.com/v1/files/${fileKey}/nodes`;
|
|
2417
|
+
const params = new URLSearchParams();
|
|
2418
|
+
params.set("ids", nodeIds.join(","));
|
|
2419
|
+
if (options?.depth) {
|
|
2420
|
+
params.set("depth", options.depth.toString());
|
|
2421
|
+
}
|
|
2422
|
+
return `${base}?${params}`;
|
|
2423
|
+
}
|
|
2424
|
+
buildImagesUrl(fileKey, nodeIds, options) {
|
|
2425
|
+
const base = `https://api.figma.com/v1/images/${fileKey}`;
|
|
2426
|
+
const params = new URLSearchParams();
|
|
2427
|
+
params.set("ids", nodeIds.join(","));
|
|
2428
|
+
if (options?.format) params.set("format", options.format);
|
|
2429
|
+
if (options?.scale) params.set("scale", options.scale.toString());
|
|
2430
|
+
return `${base}?${params}`;
|
|
2431
|
+
}
|
|
2432
|
+
/**
|
|
2433
|
+
* Recursively count nodes and components in the document tree
|
|
2434
|
+
*/
|
|
2435
|
+
countNodesAndComponents(node) {
|
|
2436
|
+
let nodes = 1;
|
|
2437
|
+
let components = node.type === "COMPONENT" || node.type === "COMPONENT_SET" ? 1 : 0;
|
|
2438
|
+
if (node.children) {
|
|
2439
|
+
for (const child of node.children) {
|
|
2440
|
+
const childCounts = this.countNodesAndComponents(child);
|
|
2441
|
+
nodes += childCounts.nodes;
|
|
2442
|
+
components += childCounts.components;
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
return { nodes, components };
|
|
2446
|
+
}
|
|
2447
|
+
};
|
|
467
2448
|
|
|
468
|
-
|
|
2449
|
+
// packages/kendo-ui-agent/src/lib/figma-d2c/utils.ts
|
|
2450
|
+
function parseFigmaUrl(url) {
|
|
2451
|
+
const invalidResult = {
|
|
2452
|
+
fileKey: "",
|
|
2453
|
+
type: "unknown",
|
|
2454
|
+
isDevMode: false,
|
|
2455
|
+
isValid: false
|
|
2456
|
+
};
|
|
2457
|
+
if (!url || typeof url !== "string") {
|
|
2458
|
+
return invalidResult;
|
|
2459
|
+
}
|
|
2460
|
+
let parsed;
|
|
2461
|
+
try {
|
|
2462
|
+
parsed = new URL(url.trim());
|
|
2463
|
+
} catch {
|
|
2464
|
+
return invalidResult;
|
|
2465
|
+
}
|
|
2466
|
+
const validDomains = ["figma.com", "www.figma.com"];
|
|
2467
|
+
if (!validDomains.includes(parsed.hostname)) {
|
|
2468
|
+
return invalidResult;
|
|
2469
|
+
}
|
|
2470
|
+
const pathParts = parsed.pathname.split("/").filter(Boolean);
|
|
2471
|
+
if (pathParts.length < 2) {
|
|
2472
|
+
return invalidResult;
|
|
2473
|
+
}
|
|
2474
|
+
const typeMap = {
|
|
2475
|
+
file: "file",
|
|
2476
|
+
design: "design",
|
|
2477
|
+
proto: "proto",
|
|
2478
|
+
board: "board"
|
|
2479
|
+
};
|
|
2480
|
+
const type = typeMap[pathParts[0]] || "unknown";
|
|
2481
|
+
const fileKey = pathParts[1];
|
|
2482
|
+
if (!/^[a-zA-Z0-9]+$/.test(fileKey)) {
|
|
2483
|
+
return invalidResult;
|
|
2484
|
+
}
|
|
2485
|
+
const fileName = pathParts[2] ? decodeURIComponent(pathParts[2]).replace(/-/g, " ") : void 0;
|
|
2486
|
+
const nodeIdParam = parsed.searchParams.get("node-id");
|
|
2487
|
+
let nodeId;
|
|
2488
|
+
let nodeIds;
|
|
2489
|
+
if (nodeIdParam) {
|
|
2490
|
+
const rawIds = nodeIdParam.split(",").map((id) => id.trim()).filter(Boolean);
|
|
2491
|
+
nodeIds = rawIds.map((id) => id.replace(/-/g, ":"));
|
|
2492
|
+
nodeId = nodeIds[0];
|
|
2493
|
+
}
|
|
2494
|
+
const branchKey = parsed.searchParams.get("t") ?? void 0;
|
|
2495
|
+
const isDevMode = parsed.searchParams.get("m") === "dev";
|
|
2496
|
+
return {
|
|
2497
|
+
fileKey,
|
|
2498
|
+
fileName,
|
|
2499
|
+
nodeId,
|
|
2500
|
+
nodeIds,
|
|
2501
|
+
type,
|
|
2502
|
+
branchKey,
|
|
2503
|
+
isDevMode,
|
|
2504
|
+
isValid: true
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
469
2507
|
|
|
470
|
-
|
|
2508
|
+
// packages/kendo-ui-agent/src/lib/figma-d2c/fetcher.ts
|
|
2509
|
+
var NodeFetcher = class {
|
|
2510
|
+
constructor(client) {
|
|
2511
|
+
this.client = client;
|
|
2512
|
+
}
|
|
2513
|
+
/**
|
|
2514
|
+
* Fetch a design starting from a file or specific node(s)
|
|
2515
|
+
*
|
|
2516
|
+
* @param fileKey - The Figma file key
|
|
2517
|
+
* @param nodeIds - Optional node ID(s) - can be string or string[]
|
|
2518
|
+
* @param options - Fetch options including depth
|
|
2519
|
+
* @returns Fetch result with root node and stats
|
|
2520
|
+
*/
|
|
2521
|
+
async fetch(fileKey, nodeIds, options = {}) {
|
|
2522
|
+
const initialDepth = options.initialDepth ?? 10;
|
|
2523
|
+
const fetchImages = options.fetchImages ?? true;
|
|
2524
|
+
const imageFormat = options.imageFormat ?? "png";
|
|
2525
|
+
const imageScale = options.imageScale ?? 2;
|
|
2526
|
+
const stats = {
|
|
2527
|
+
totalNodesFetched: 0,
|
|
2528
|
+
apiCallsMade: 0,
|
|
2529
|
+
imagesFetched: 0
|
|
2530
|
+
};
|
|
2531
|
+
let nodeIdArray;
|
|
2532
|
+
if (!nodeIds) {
|
|
2533
|
+
nodeIdArray = [];
|
|
2534
|
+
} else if (typeof nodeIds === "string") {
|
|
2535
|
+
nodeIdArray = [nodeIds];
|
|
2536
|
+
} else {
|
|
2537
|
+
nodeIdArray = nodeIds;
|
|
2538
|
+
}
|
|
2539
|
+
let rootNode;
|
|
2540
|
+
let fetchedNodes = [];
|
|
2541
|
+
if (nodeIdArray.length > 0) {
|
|
2542
|
+
const response = await this.client.getNodes(fileKey, nodeIdArray, { depth: initialDepth });
|
|
2543
|
+
stats.apiCallsMade++;
|
|
2544
|
+
for (const nodeId of nodeIdArray) {
|
|
2545
|
+
const nodeData = response.nodes[nodeId]?.document;
|
|
2546
|
+
if (!nodeData) {
|
|
2547
|
+
throw new Error(`Node ${nodeId} not found`);
|
|
2548
|
+
}
|
|
2549
|
+
fetchedNodes.push(nodeData);
|
|
2550
|
+
}
|
|
2551
|
+
if (fetchedNodes.length === 1) {
|
|
2552
|
+
rootNode = fetchedNodes[0];
|
|
2553
|
+
} else {
|
|
2554
|
+
rootNode = this.createContainerNode(fetchedNodes);
|
|
2555
|
+
}
|
|
2556
|
+
} else {
|
|
2557
|
+
const response = await this.client.getFile(fileKey, { depth: initialDepth });
|
|
2558
|
+
stats.apiCallsMade++;
|
|
2559
|
+
rootNode = response.document;
|
|
2560
|
+
fetchedNodes = [rootNode];
|
|
2561
|
+
}
|
|
2562
|
+
stats.totalNodesFetched += this.countNodes(rootNode);
|
|
2563
|
+
const images = [];
|
|
2564
|
+
if (fetchImages && nodeIdArray.length > 0) {
|
|
2565
|
+
try {
|
|
2566
|
+
const imageResult = await this.fetchNodeImages(
|
|
2567
|
+
fileKey,
|
|
2568
|
+
nodeIdArray,
|
|
2569
|
+
fetchedNodes,
|
|
2570
|
+
{ format: imageFormat, scale: imageScale }
|
|
2571
|
+
);
|
|
2572
|
+
images.push(...imageResult.images);
|
|
2573
|
+
stats.apiCallsMade += imageResult.apiCalls;
|
|
2574
|
+
stats.imagesFetched = images.length;
|
|
2575
|
+
} catch (error) {
|
|
2576
|
+
console.warn("Failed to fetch images:", error);
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
return {
|
|
2580
|
+
rootNode,
|
|
2581
|
+
fetchedNodes,
|
|
2582
|
+
images,
|
|
2583
|
+
stats
|
|
2584
|
+
};
|
|
2585
|
+
}
|
|
2586
|
+
/**
|
|
2587
|
+
* Fetch and download images for specified nodes
|
|
2588
|
+
*/
|
|
2589
|
+
async fetchNodeImages(fileKey, nodeIds, nodes, options) {
|
|
2590
|
+
const imageResponse = await this.client.getImages(fileKey, nodeIds, options);
|
|
2591
|
+
let apiCalls = 1;
|
|
2592
|
+
const images = [];
|
|
2593
|
+
for (let i = 0; i < nodeIds.length; i++) {
|
|
2594
|
+
const nodeId = nodeIds[i];
|
|
2595
|
+
const imageUrl = imageResponse.images[nodeId];
|
|
2596
|
+
if (imageUrl) {
|
|
2597
|
+
try {
|
|
2598
|
+
const imageData = await this.downloadImage(imageUrl, options.format);
|
|
2599
|
+
const node = nodes[i];
|
|
2600
|
+
images.push({
|
|
2601
|
+
nodeId,
|
|
2602
|
+
nodeName: node?.name || "Unknown",
|
|
2603
|
+
data: imageData,
|
|
2604
|
+
mimeType: this.getMimeType(options.format),
|
|
2605
|
+
url: imageUrl
|
|
2606
|
+
});
|
|
2607
|
+
} catch (error) {
|
|
2608
|
+
console.warn(`Failed to download image for node ${nodeId}:`, error);
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
return { images, apiCalls };
|
|
2613
|
+
}
|
|
2614
|
+
/**
|
|
2615
|
+
* Download an image from a URL and convert to base64
|
|
2616
|
+
*/
|
|
2617
|
+
async downloadImage(url, format) {
|
|
2618
|
+
const response = await fetch(url);
|
|
2619
|
+
if (!response.ok) {
|
|
2620
|
+
throw new Error(`Failed to download image: ${response.statusText}`);
|
|
2621
|
+
}
|
|
2622
|
+
if (format === "svg") {
|
|
2623
|
+
const svgText = await response.text();
|
|
2624
|
+
return Buffer.from(svgText, "utf-8").toString("base64");
|
|
2625
|
+
} else {
|
|
2626
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
2627
|
+
return Buffer.from(arrayBuffer).toString("base64");
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
/**
|
|
2631
|
+
* Get MIME type for image format
|
|
2632
|
+
*/
|
|
2633
|
+
getMimeType(format) {
|
|
2634
|
+
switch (format) {
|
|
2635
|
+
case "png":
|
|
2636
|
+
return "image/png";
|
|
2637
|
+
case "jpg":
|
|
2638
|
+
return "image/jpeg";
|
|
2639
|
+
case "svg":
|
|
2640
|
+
return "image/svg+xml";
|
|
2641
|
+
default:
|
|
2642
|
+
return "image/png";
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
/**
|
|
2646
|
+
* Create a synthetic container node that wraps multiple nodes
|
|
2647
|
+
*/
|
|
2648
|
+
createContainerNode(nodes) {
|
|
2649
|
+
return {
|
|
2650
|
+
id: "container-" + Date.now(),
|
|
2651
|
+
name: "Multi-Node Container",
|
|
2652
|
+
type: "GROUP",
|
|
2653
|
+
visible: true,
|
|
2654
|
+
children: nodes
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
/**
|
|
2658
|
+
* Count total nodes in a tree
|
|
2659
|
+
*/
|
|
2660
|
+
countNodes(node) {
|
|
2661
|
+
let count = 1;
|
|
2662
|
+
if (node.children) {
|
|
2663
|
+
for (const child of node.children) {
|
|
2664
|
+
count += this.countNodes(child);
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
return count;
|
|
2668
|
+
}
|
|
2669
|
+
};
|
|
471
2670
|
|
|
472
|
-
|
|
2671
|
+
// packages/kendo-ui-agent/src/lib/figma-d2c/figma-d2c-tool.ts
|
|
2672
|
+
import { z as z5 } from "zod";
|
|
2673
|
+
function createFigmaD2CTool({
|
|
2674
|
+
name,
|
|
2675
|
+
figmaApiKey,
|
|
2676
|
+
requiredAccess
|
|
2677
|
+
}, contextApi2) {
|
|
2678
|
+
return new UIAgentTool(
|
|
2679
|
+
name,
|
|
2680
|
+
{
|
|
2681
|
+
title: "Figma Design to Code",
|
|
2682
|
+
description: "Converts Figma designs to code by analyzing the design structure and matching Kendo UI components. Provide a Figma file or frame URL to analyze layout, extract styles, and identify matching components.",
|
|
2683
|
+
inputSchema: {
|
|
2684
|
+
url: z5.string().describe(
|
|
2685
|
+
"Figma file or node URL (e.g., https://www.figma.com/file/...)"
|
|
2686
|
+
).optional(),
|
|
2687
|
+
fileKey: z5.string().describe("Figma file key (alternative to URL)").optional(),
|
|
2688
|
+
nodeIds: z5.array(z5.string()).describe("Array of specific node IDs to fetch (alternative to URL)").optional(),
|
|
2689
|
+
depth: z5.number().min(1).max(20).default(10).describe("Initial fetch depth (1-20, default: 10)")
|
|
2690
|
+
},
|
|
2691
|
+
requiredAccess
|
|
2692
|
+
},
|
|
2693
|
+
async ({ url, fileKey, nodeIds, depth }) => {
|
|
2694
|
+
const client = new FigmaClient(figmaApiKey);
|
|
2695
|
+
const fetcher = new NodeFetcher(client);
|
|
2696
|
+
let fileKeyToUse = fileKey;
|
|
2697
|
+
let nodeIdsToUse;
|
|
2698
|
+
if (fileKeyToUse) {
|
|
2699
|
+
if (nodeIds && nodeIds.length > 0) {
|
|
2700
|
+
nodeIdsToUse = nodeIds.length === 1 ? nodeIds[0] : nodeIds;
|
|
2701
|
+
}
|
|
2702
|
+
} else if (url) {
|
|
2703
|
+
const parsed = parseFigmaUrl(url);
|
|
2704
|
+
if (!parsed.isValid || !parsed.fileKey) {
|
|
2705
|
+
throw new Error(
|
|
2706
|
+
"Invalid Figma URL. Please provide a valid Figma file or frame URL."
|
|
2707
|
+
);
|
|
2708
|
+
}
|
|
2709
|
+
fileKeyToUse = parsed.fileKey;
|
|
2710
|
+
if (parsed.nodeId) {
|
|
2711
|
+
nodeIdsToUse = parsed.nodeId;
|
|
2712
|
+
}
|
|
2713
|
+
} else {
|
|
2714
|
+
throw new Error("Please provide either a Figma URL, fileKey, or both.");
|
|
2715
|
+
}
|
|
2716
|
+
const fetchResult = await fetcher.fetch(fileKeyToUse, nodeIdsToUse, {
|
|
2717
|
+
initialDepth: depth,
|
|
2718
|
+
fetchImages: true,
|
|
2719
|
+
imageFormat: "png",
|
|
2720
|
+
imageScale: 2
|
|
2721
|
+
});
|
|
2722
|
+
const resultJson = {
|
|
2723
|
+
name: fetchResult.rootNode.name,
|
|
2724
|
+
type: fetchResult.rootNode.type,
|
|
2725
|
+
rootNode: fetchResult.rootNode,
|
|
2726
|
+
fetchedNodes: fetchResult.fetchedNodes,
|
|
2727
|
+
meta: {
|
|
2728
|
+
fileKey,
|
|
2729
|
+
nodeIds
|
|
2730
|
+
}
|
|
2731
|
+
};
|
|
2732
|
+
let backendResponse;
|
|
2733
|
+
try {
|
|
2734
|
+
const nodeJsons = fetchResult.fetchedNodes.map(
|
|
2735
|
+
(node) => JSON.stringify(node)
|
|
2736
|
+
);
|
|
2737
|
+
backendResponse = await contextApi2.processFigmaDesign({
|
|
2738
|
+
node_jsons: nodeJsons
|
|
2739
|
+
});
|
|
2740
|
+
} catch (error) {
|
|
2741
|
+
console.error("Backend processing failed:", error);
|
|
2742
|
+
return {
|
|
2743
|
+
content: [
|
|
2744
|
+
{
|
|
2745
|
+
type: "text",
|
|
2746
|
+
text: JSON.stringify(
|
|
2747
|
+
{
|
|
2748
|
+
error: {
|
|
2749
|
+
message: `Backend processing failed: ${error.message}`,
|
|
2750
|
+
code: error.code || "UNKNOWN"
|
|
2751
|
+
},
|
|
2752
|
+
figma_data: resultJson
|
|
2753
|
+
},
|
|
2754
|
+
null,
|
|
2755
|
+
2
|
|
2756
|
+
)
|
|
2757
|
+
}
|
|
2758
|
+
]
|
|
2759
|
+
};
|
|
2760
|
+
}
|
|
2761
|
+
const content = [];
|
|
2762
|
+
content.push({
|
|
2763
|
+
type: "text",
|
|
2764
|
+
text: `# Figma Design Extraction Results
|
|
2765
|
+
Successfully fetched design data for file key ${fileKeyToUse} with for nodes [${nodeIds?.join(", ")}].
|
|
2766
|
+
The following is the processed design data including layout pseudo-code and design tokens extracted from the Figma design, plus images for visual references of the desired nodes:`
|
|
2767
|
+
});
|
|
2768
|
+
backendResponse.layout_pseudo_codes.forEach((layoutCode, index) => {
|
|
2769
|
+
const [nodeName, nodeId] = ((index2) => {
|
|
2770
|
+
const node = fetchResult.fetchedNodes[index2];
|
|
2771
|
+
return node ? [node.name, node.id] : ["Unknown Node", "unknown_id"];
|
|
2772
|
+
})(index);
|
|
2773
|
+
content.push({
|
|
2774
|
+
type: "text",
|
|
2775
|
+
text: `## Psuedo-code Layout - Node: ${nodeName} (${nodeId})
|
|
2776
|
+
|
|
2777
|
+
` + layoutCode,
|
|
2778
|
+
_meta: {
|
|
2779
|
+
description: `Psuedo-code Layout - Node: ${nodeName} (${nodeId})`
|
|
2780
|
+
},
|
|
2781
|
+
annotations: {
|
|
2782
|
+
audience: ["assistant"]
|
|
2783
|
+
}
|
|
2784
|
+
});
|
|
2785
|
+
});
|
|
2786
|
+
content.push({
|
|
2787
|
+
type: "text",
|
|
2788
|
+
text: `## Design Tokens - File: ${fileKey} for Nodes: [${nodeIds?.join(", ")}]
|
|
2789
|
+
|
|
2790
|
+
` + backendResponse.design_tokens,
|
|
2791
|
+
_meta: {
|
|
2792
|
+
description: `Design Tokens - File: ${fileKey} for Nodes: [${nodeIds?.join(", ")}]`
|
|
2793
|
+
},
|
|
2794
|
+
annotations: {
|
|
2795
|
+
audience: ["assistant"]
|
|
2796
|
+
}
|
|
2797
|
+
});
|
|
2798
|
+
if (fetchResult.images && fetchResult.images.length > 0) {
|
|
2799
|
+
for (const image of fetchResult.images) {
|
|
2800
|
+
content.push({
|
|
2801
|
+
type: "image",
|
|
2802
|
+
data: image.data,
|
|
2803
|
+
mimeType: image.mimeType,
|
|
2804
|
+
_meta: {
|
|
2805
|
+
description: `Design snapshot of ${image.nodeName} (${image.nodeId})`
|
|
2806
|
+
},
|
|
2807
|
+
annotations: {
|
|
2808
|
+
audience: ["assistant"]
|
|
2809
|
+
}
|
|
2810
|
+
});
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
content.push({
|
|
2814
|
+
type: "text",
|
|
2815
|
+
text: `# Analysis Instructions and Next Steps
|
|
2816
|
+
|
|
2817
|
+
Use the above design data, layout pseudo-code, and design tokens to analyze the Figma design and identify components. Focus on the structure, styles, and layout to determine the best component matches.
|
|
2818
|
+
If more design details are needed, consider asking for additional nodes or deeper fetches to gather more information about specific areas of the design.`
|
|
2819
|
+
});
|
|
2820
|
+
return {
|
|
2821
|
+
content
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
);
|
|
2825
|
+
}
|
|
473
2826
|
|
|
474
|
-
|
|
2827
|
+
// packages/kendo-ui-agent/src/lib/tool-names.ts
|
|
2828
|
+
var orchestratorToolName = (prefix) => `${prefix}_ui_generator`;
|
|
2829
|
+
var assistantOrchestratorToolName = (prefix) => `${prefix}_assistant`;
|
|
2830
|
+
var layoutAssistantName = (prefix) => `${prefix}_layout_assistant`;
|
|
2831
|
+
var codeAssistantName = (prefix) => `${prefix}_component_assistant`;
|
|
2832
|
+
var styleAssistantName = (prefix) => `${prefix}_style_assistant`;
|
|
2833
|
+
var iconAssistantName = (prefix) => `${prefix}_icon_assistant`;
|
|
2834
|
+
var accessibilityAssistantName = (prefix) => `${prefix}_accessibility_assistant`;
|
|
2835
|
+
var gettingStartedAssistantName = (prefix) => `${prefix}_getting_started_assistant`;
|
|
2836
|
+
var figmaD2CToolName = (prefix) => `${prefix}_figma_d2c`;
|
|
475
2837
|
|
|
476
|
-
|
|
2838
|
+
// packages/kendo-ui-agent/src/lib/tool-descriptions.ts
|
|
2839
|
+
var orchestratorToolDescription = (suiteName) => `This is the single entry point for all UI-related tasks. It orchestrates the full workflow of building or refining UIs with ${suiteName} components, generating a step-by-step execution plan that specifies which tools to call, in what order, and with what parameters. It includes validation steps to ensure the best possible, standards-compliant code every time.
|
|
477
2840
|
|
|
478
2841
|
Use the tool when:
|
|
479
2842
|
- Want to build or modify a complete page or section layout
|
|
480
2843
|
- You are creating and applying custom themes via the Kendo Design System
|
|
481
|
-
- You need guidance on correct ${
|
|
2844
|
+
- You need guidance on correct ${suiteName} component selection and configuration
|
|
482
2845
|
- You want to enforce best practices in styling, performance, and accessibility
|
|
483
2846
|
|
|
484
2847
|
Returns:
|
|
485
|
-
A detailed execution plan with validated tool calls, parameters, and reasoning \u2014 ensuring consistent, high-quality UI generation
|
|
2848
|
+
A detailed execution plan with validated tool calls, parameters, and reasoning \u2014 ensuring consistent, high-quality UI generation.`;
|
|
2849
|
+
var assistantOrchestratorToolDescription = (suiteName) => `Provides guidance for discovering and using ${suiteName} components and icons. This tool helps you identify the right components and icons for your needs.
|
|
486
2850
|
|
|
487
2851
|
Use this tool when:
|
|
488
|
-
- You need to discover which ${
|
|
2852
|
+
- You need to discover which ${suiteName} components are suitable for a specific use case
|
|
489
2853
|
- You want to understand component features and capabilities
|
|
490
2854
|
- You need to find appropriate icons for your UI
|
|
491
2855
|
- You want to gather component documentation and examples
|
|
492
2856
|
|
|
493
2857
|
Returns:
|
|
494
|
-
A focused plan for gathering component documentation and icon suggestions using the Component Assistant and Icon Assistant tools
|
|
2858
|
+
A focused plan for gathering component documentation and icon suggestions using the Component Assistant and Icon Assistant tools.`;
|
|
2859
|
+
var layoutToolDescription = `Retrieves Progress/Kendo Design System CSS utility classes and styling recommendations based on layout and design requirements.
|
|
495
2860
|
Use this tool to find the right CSS utilities for styling Kendo UI components or general web elements.
|
|
496
2861
|
It analyzes the provided design context and returns relevant utility classes with explanations and examples.
|
|
497
|
-
Best suited for styling questions related to typography, spacing, colors, layout, transforms, and other utilities in the Progress/Kendo Design System
|
|
2862
|
+
Best suited for styling questions related to typography, spacing, colors, layout, transforms, and other utilities in the Progress/Kendo Design System.`;
|
|
2863
|
+
var styleToolDescription = `Generates CSS variables using an external style agent based on the user's styling prompt.
|
|
498
2864
|
|
|
499
2865
|
Use this tool when the user wants to:
|
|
500
2866
|
- Update the visual theme of an application
|
|
501
2867
|
- Modify color schemes
|
|
502
2868
|
- Restyle the UI with CSS variables
|
|
503
2869
|
|
|
504
|
-
The tool outputs a set of CSS variable definitions ready to be applied to style files
|
|
2870
|
+
The tool outputs a set of CSS variable definitions ready to be applied to style files.`;
|
|
2871
|
+
var iconToolDescription = `Searches for icons in the Telerik Icons library.
|
|
505
2872
|
Use this tool when the user needs to find specific icons for UI components or design elements.
|
|
506
2873
|
It supports searching by icon name, category, or keyword and returns matching icons from the Telerik Icons library.
|
|
507
|
-
|
|
2874
|
+
`;
|
|
2875
|
+
var codeAssistantToolDescription = (suiteName) => `Provides answers and retrieves official documentation for ${suiteName}.
|
|
508
2876
|
|
|
509
2877
|
Use this tool when the user asks about:
|
|
510
|
-
- ${
|
|
2878
|
+
- ${suiteName} features or capabilities
|
|
511
2879
|
- Specific components (e.g., Grid, Chart, Editor)
|
|
512
2880
|
- Implementation details, configuration, or best practices
|
|
513
|
-
- How to use or customize components in ${
|
|
2881
|
+
- How to use or customize components in ${suiteName}
|
|
514
2882
|
|
|
515
2883
|
Set the user's full question as the 'query' parameter.
|
|
516
|
-
If the question refers to a specific ${
|
|
517
|
-
|
|
2884
|
+
If the question refers to a specific ${suiteName} component, include its name in the 'component' parameter.
|
|
2885
|
+
`;
|
|
2886
|
+
var accessibilityToolDescription = (suiteName) => `Retrieves accessibility guidance for ${suiteName} components with optional general WCAG 2.2 Level AA guidelines.
|
|
518
2887
|
|
|
519
2888
|
Use this tool when:
|
|
520
2889
|
- You need component-specific accessibility information and API references
|
|
@@ -532,7 +2901,11 @@ Set 'includeGeneralGuidelines=true' ONLY on the first call to include general WC
|
|
|
532
2901
|
Returns:
|
|
533
2902
|
- When includeGeneralGuidelines=true: General WCAG 2.2 Level AA guidelines + component-specific recommendations
|
|
534
2903
|
- When includeGeneralGuidelines=false: Only component-specific accessibility recommendations
|
|
535
|
-
`;
|
|
2904
|
+
`;
|
|
2905
|
+
|
|
2906
|
+
// packages/kendo-ui-agent/src/lib/validation/angular-validation-steps.ts
|
|
2907
|
+
var sharedSteps = getSharedValidationSteps("Kendo UI", "Kendo");
|
|
2908
|
+
var ANGULAR_VALIDATION_STEPS = `
|
|
536
2909
|
### Angular Component Validation
|
|
537
2910
|
Component Structure:
|
|
538
2911
|
- Use proper @Component decorator configuration
|
|
@@ -540,22 +2913,25 @@ Component Structure:
|
|
|
540
2913
|
- Correct selector naming convention (kebab-case)
|
|
541
2914
|
- Appropriate template and style file organization
|
|
542
2915
|
|
|
543
|
-
${
|
|
2916
|
+
${sharedSteps.SETUP_VALIDATION}
|
|
544
2917
|
- Kendo animations module configured (if animations are used)
|
|
545
2918
|
- Internationalization properly configured for Kendo components
|
|
546
2919
|
|
|
547
|
-
${
|
|
2920
|
+
${sharedSteps.PERFORMANCE_OPTIMIZATION}
|
|
548
2921
|
- OnPush change detection strategy where appropriate
|
|
549
2922
|
- Efficient trackBy functions for *ngFor with Kendo components
|
|
550
2923
|
- Minimal unnecessary change detection cycles
|
|
551
2924
|
|
|
552
|
-
${
|
|
2925
|
+
${sharedSteps.THEME_AND_STYLING}
|
|
553
2926
|
- Proper theme switching functionality (if implemented)
|
|
554
2927
|
- Only ONE theme reference exists across angular.json and styles files
|
|
555
2928
|
- Make sure theme import mechanism is consistent with existing project setup
|
|
556
2929
|
|
|
557
2930
|
Accessibility
|
|
558
|
-
- Accessibility guidelines retrieved and applied to all components`;
|
|
2931
|
+
- Accessibility guidelines retrieved and applied to all components`;
|
|
2932
|
+
|
|
2933
|
+
// packages/kendo-ui-agent/src/lib/validation/angular-additional-plan.ts
|
|
2934
|
+
var ANGULAR_ADDITIONAL_PLAN = `**Angular Specific Requirements:**
|
|
559
2935
|
|
|
560
2936
|
**NgModule vs Standalone setup**: Search and detect the current project setup to determine whether to use NgModule or Standalone components based on the following rules:
|
|
561
2937
|
1. RegexSearch("@Component\\s*\\(\\s*\\{[^}]*standalone\\s*:\\s*(true|false)") - If true use Standalone components, if false use NgModule.
|
|
@@ -586,7 +2962,11 @@ Accessibility
|
|
|
586
2962
|
2. Match the discovered patterns in all generated code
|
|
587
2963
|
3. Maintain consistent formatting, indentation, and code organization with existing files
|
|
588
2964
|
4. Always use the same theme import mechanism (unless user specifies otherwise)
|
|
589
|
-
`;
|
|
2965
|
+
`;
|
|
2966
|
+
|
|
2967
|
+
// packages/kendo-ui-agent/src/lib/validation/react-validation-steps.ts
|
|
2968
|
+
var sharedSteps2 = getSharedValidationSteps("Kendo UI", "KendoReact");
|
|
2969
|
+
var REACT_VALIDATION_STEPS = `
|
|
590
2970
|
### React Component Validation
|
|
591
2971
|
Component Structure:
|
|
592
2972
|
- Use proper function component patterns with Hooks
|
|
@@ -594,22 +2974,85 @@ Component Structure:
|
|
|
594
2974
|
- Appropriate file organization (components, hooks, utils)
|
|
595
2975
|
- Props interface/type definitions where needed
|
|
596
2976
|
|
|
597
|
-
${
|
|
2977
|
+
${sharedSteps2.SETUP_VALIDATION}
|
|
598
2978
|
- KendoReact license validation (if required)
|
|
599
2979
|
- Appropriate bundler configuration for KendoReact packages
|
|
600
2980
|
|
|
601
|
-
${
|
|
2981
|
+
${sharedSteps2.PERFORMANCE_OPTIMIZATION}
|
|
602
2982
|
- React.memo for components that receive stable props
|
|
603
2983
|
- useMemo/useCallback for expensive computations and callbacks
|
|
604
2984
|
- Proper key props for list rendering with KendoReact components
|
|
605
2985
|
|
|
606
|
-
${
|
|
2986
|
+
${sharedSteps2.THEME_AND_STYLING}
|
|
607
2987
|
- Proper theme switching functionality (if implemented)
|
|
608
2988
|
- Only ONE theme import exists across the application
|
|
609
2989
|
- Make sure theme import mechanism is consistent with existing project setup
|
|
610
2990
|
|
|
611
2991
|
Accessibility
|
|
612
|
-
- Accessibility guidelines retrieved and applied to all components`;
|
|
2992
|
+
- Accessibility guidelines retrieved and applied to all components`;
|
|
2993
|
+
|
|
2994
|
+
// packages/kendo-ui-agent/src/index.ts
|
|
2995
|
+
var Tools = {
|
|
2996
|
+
createOrchestrator: createOrchestratorTool,
|
|
2997
|
+
createAssistantOrchestrator: createAssistantOrchestratorTool,
|
|
2998
|
+
createLayoutAssistant: createLayoutAssistantTool,
|
|
2999
|
+
createStyleAssistant: createStyleAssistantTool,
|
|
3000
|
+
createIconSearch: createIconSearchTool,
|
|
3001
|
+
createCodeAssistant: createCodeAssistantTool,
|
|
3002
|
+
createAccessibilityAssistant: createAccessibilityAssistantTool,
|
|
3003
|
+
createFigmaD2C: createFigmaD2CTool
|
|
3004
|
+
};
|
|
3005
|
+
var ToolNames = {
|
|
3006
|
+
orchestrator: orchestratorToolName,
|
|
3007
|
+
assistantOrchestrator: assistantOrchestratorToolName,
|
|
3008
|
+
layoutAssistant: layoutAssistantName,
|
|
3009
|
+
codeAssistant: codeAssistantName,
|
|
3010
|
+
styleAssistant: styleAssistantName,
|
|
3011
|
+
iconAssistant: iconAssistantName,
|
|
3012
|
+
accessibilityAssistant: accessibilityAssistantName,
|
|
3013
|
+
gettingStartedAssistant: gettingStartedAssistantName,
|
|
3014
|
+
figmaD2C: figmaD2CToolName
|
|
3015
|
+
};
|
|
3016
|
+
var ToolDescriptions = {
|
|
3017
|
+
orchestrator: orchestratorToolDescription,
|
|
3018
|
+
assistantOrchestrator: assistantOrchestratorToolDescription,
|
|
3019
|
+
layoutAssistant: layoutToolDescription,
|
|
3020
|
+
styleAssistant: styleToolDescription,
|
|
3021
|
+
iconAssistant: iconToolDescription,
|
|
3022
|
+
codeAssistant: codeAssistantToolDescription,
|
|
3023
|
+
accessibilityAssistant: accessibilityToolDescription
|
|
3024
|
+
};
|
|
3025
|
+
var Templates = {
|
|
3026
|
+
Orchestrator: {
|
|
3027
|
+
render: renderOrchestratorResponse
|
|
3028
|
+
},
|
|
3029
|
+
Validator: {
|
|
3030
|
+
render: renderValidatorResponse
|
|
3031
|
+
},
|
|
3032
|
+
SharedValidation: {
|
|
3033
|
+
getSteps: getSharedValidationSteps
|
|
3034
|
+
}
|
|
3035
|
+
};
|
|
3036
|
+
var Validation = {
|
|
3037
|
+
Angular: {
|
|
3038
|
+
steps: ANGULAR_VALIDATION_STEPS,
|
|
3039
|
+
additionalPlan: ANGULAR_ADDITIONAL_PLAN
|
|
3040
|
+
},
|
|
3041
|
+
React: {
|
|
3042
|
+
steps: REACT_VALIDATION_STEPS
|
|
3043
|
+
}
|
|
3044
|
+
};
|
|
3045
|
+
var UIAgent = {
|
|
3046
|
+
Tools,
|
|
3047
|
+
ToolNames,
|
|
3048
|
+
ToolDescriptions,
|
|
3049
|
+
Templates,
|
|
3050
|
+
Validation
|
|
3051
|
+
};
|
|
3052
|
+
|
|
3053
|
+
// packages/angular/src/lib/instructions/layout.ts
|
|
3054
|
+
var CODE_ASSISTANT_NAME = UIAgent.ToolNames.codeAssistant("kendo");
|
|
3055
|
+
var getLayoutInstructions = () => `# Kendo Design System Guidelines for ${ANGULAR_SUITE_NAME}
|
|
613
3056
|
|
|
614
3057
|
## Setting up Styles/CSS
|
|
615
3058
|
|
|
@@ -636,14 +3079,17 @@ Alternatively, you can also consume them directly from the Unpkg CDN.
|
|
|
636
3079
|
|
|
637
3080
|
## Ready-to-use Layout Components
|
|
638
3081
|
|
|
639
|
-
In the ${
|
|
3082
|
+
In the ${ANGULAR_SUITE_NAME} there are available components to create layout. Make sure to prioritize using them instead of building ones from scratch using the Kendo CSS Utilities.
|
|
640
3083
|
|
|
641
|
-
For any Layout component you can also call the ${
|
|
3084
|
+
For any Layout component you can also call the ${CODE_ASSISTANT_NAME} tool in order to get specific API reference and docs.
|
|
642
3085
|
|
|
643
|
-
- ${
|
|
3086
|
+
- ${ANGULAR_SUITE_NAME} Card component.
|
|
644
3087
|
- Generally, it is recommended to use 100% width (e.g., \`<kendo-card width="100%">\`)
|
|
645
3088
|
- If needed, use specific width based on the layout requirements (e.g., \`<kendo-card width="150px">\`)
|
|
646
|
-
`;
|
|
3089
|
+
`;
|
|
3090
|
+
|
|
3091
|
+
// packages/angular/src/lib/instructions/style.ts
|
|
3092
|
+
var getStyleInstructions = () => `## Applying CSS Variables in ${ANGULAR_SUITE_NAME}
|
|
647
3093
|
|
|
648
3094
|
Apply these CSS variables in the Angular application using one of these methods:
|
|
649
3095
|
|
|
@@ -705,7 +3151,11 @@ export class ThemeService {
|
|
|
705
3151
|
}
|
|
706
3152
|
}
|
|
707
3153
|
\`\`\`
|
|
708
|
-
`;
|
|
3154
|
+
`;
|
|
3155
|
+
|
|
3156
|
+
// packages/angular/src/lib/instructions/icons.ts
|
|
3157
|
+
var CODE_ASSISTANT_NAME2 = UIAgent.ToolNames.codeAssistant("kendo");
|
|
3158
|
+
var getIconsInstructions = () => `## Using ${ANGULAR_SUITE_NAME} Icons
|
|
709
3159
|
|
|
710
3160
|
Important: Attempt to prioritize SVG icons over font icons.
|
|
711
3161
|
|
|
@@ -713,13 +3163,13 @@ Important: Attempt to prioritize SVG icons over font icons.
|
|
|
713
3163
|
|
|
714
3164
|
- Make sure @progress/kendo-svg-icons is installed.
|
|
715
3165
|
- Make sure @progress/kendo-angular-icons is installed.
|
|
716
|
-
- You can call ${
|
|
3166
|
+
- You can call ${CODE_ASSISTANT_NAME2} tool for ${ANGULAR_SUITE_NAME} SVGIcon component for docs and API reference.
|
|
717
3167
|
- [optional] If SVG is not an option fallback to fonticon - make sure \`<link rel="stylesheet" href="https://unpkg.com/@progress/kendo-font-icons/dist/index.css" />\` is added as a reference.
|
|
718
|
-
- [optional] If using font icons you can call ${
|
|
3168
|
+
- [optional] If using font icons you can call ${CODE_ASSISTANT_NAME2} tool for ${ANGULAR_SUITE_NAME} Icon component for docs and API reference.
|
|
719
3169
|
|
|
720
3170
|
### Examples
|
|
721
3171
|
|
|
722
|
-
Using ${
|
|
3172
|
+
Using ${ANGULAR_SUITE_NAME} SVGIcon component:
|
|
723
3173
|
|
|
724
3174
|
\`\`\`ts
|
|
725
3175
|
import { Component } from "@angular/core";
|
|
@@ -740,7 +3190,7 @@ export class AppComponent {
|
|
|
740
3190
|
|
|
741
3191
|
\`\`\`
|
|
742
3192
|
|
|
743
|
-
Using ${
|
|
3193
|
+
Using ${ANGULAR_SUITE_NAME} Icon component:
|
|
744
3194
|
|
|
745
3195
|
\`\`\`ts
|
|
746
3196
|
import { Component } from "@angular/core";
|
|
@@ -756,23 +3206,43 @@ import { KENDO_ICONS } from "@progress/kendo-angular-icons";
|
|
|
756
3206
|
})
|
|
757
3207
|
export class AppComponent {}
|
|
758
3208
|
\`\`\`
|
|
759
|
-
`;
|
|
3209
|
+
`;
|
|
760
3210
|
|
|
761
|
-
|
|
3211
|
+
// packages/angular/src/lib/instructions/components-common.ts
|
|
3212
|
+
var getComponentsCommonInstructions = (ctx) => {
|
|
3213
|
+
const normComponent = ctx.component?.trim().toLowerCase();
|
|
3214
|
+
const installPackage = COMPONENT_PACKAGE_MAP.get(normComponent);
|
|
3215
|
+
let content = `## ${ANGULAR_SUITE_NAME} Common Guidelines
|
|
3216
|
+
|
|
3217
|
+
`;
|
|
3218
|
+
if (installPackage) {
|
|
3219
|
+
content += `### Package Installation
|
|
762
3220
|
Always use minimal, targeted package installation.
|
|
763
3221
|
Ensure all necessary Angular packages are installed, including @angular/localize.
|
|
764
3222
|
|
|
765
3223
|
Process:
|
|
766
3224
|
1. Identify the required components
|
|
767
3225
|
2. Determine the specific Kendo Angular packages needed
|
|
768
|
-
3. Use the "ng add ${
|
|
3226
|
+
3. Use the "ng add ${installPackage}" command
|
|
769
3227
|
|
|
770
|
-
For ${
|
|
771
|
-
|
|
3228
|
+
For ${ctx.component} component: \`ng add ${installPackage}\`
|
|
3229
|
+
`;
|
|
3230
|
+
}
|
|
3231
|
+
if (ctx.componentInstructions) {
|
|
3232
|
+
content += `
|
|
772
3233
|
## Component-Specific Guidelines
|
|
773
3234
|
|
|
774
|
-
${
|
|
775
|
-
|
|
3235
|
+
${ctx.componentInstructions}
|
|
3236
|
+
`;
|
|
3237
|
+
}
|
|
3238
|
+
return content;
|
|
3239
|
+
};
|
|
3240
|
+
|
|
3241
|
+
// packages/angular/src/lib/instructions/getting-started.ts
|
|
3242
|
+
function getGettingStartedInstructions(projectName = "kendo-angular-app", createNewProject = true, theme = "default") {
|
|
3243
|
+
const introLine = createNewProject ? `These instructions will walk you through creating a new Angular project and setting up ${ANGULAR_SUITE_NAME}.` : `These instructions will walk you through adding ${ANGULAR_SUITE_NAME} to your existing Angular project.`;
|
|
3244
|
+
let stepCounter = 1;
|
|
3245
|
+
const createProjectSection = createNewProject ? `## Step ${stepCounter++}: Install Angular CLI
|
|
776
3246
|
|
|
777
3247
|
First, ensure the Angular CLI is installed globally:
|
|
778
3248
|
|
|
@@ -780,23 +3250,24 @@ First, ensure the Angular CLI is installed globally:
|
|
|
780
3250
|
npm install -g @angular/cli@latest
|
|
781
3251
|
\`\`\`
|
|
782
3252
|
|
|
783
|
-
## Step ${
|
|
3253
|
+
## Step ${stepCounter++}: Create the Project
|
|
784
3254
|
|
|
785
3255
|
Create a new Angular project using the Angular CLI:
|
|
786
3256
|
|
|
787
3257
|
\`\`\`bash
|
|
788
|
-
ng new ${
|
|
3258
|
+
ng new ${projectName} --style=css --ai-config=none --no-ssr --routing --zoneless=false --minimal=true
|
|
789
3259
|
\`\`\`
|
|
790
3260
|
|
|
791
3261
|
Navigate to the project directory:
|
|
792
3262
|
|
|
793
3263
|
\`\`\`bash
|
|
794
|
-
cd ${
|
|
3264
|
+
cd ${projectName}
|
|
795
3265
|
\`\`\`
|
|
796
3266
|
|
|
797
|
-
|
|
3267
|
+
` : "";
|
|
3268
|
+
return `# Getting Started with ${ANGULAR_SUITE_NAME}
|
|
798
3269
|
|
|
799
|
-
${
|
|
3270
|
+
${introLine}
|
|
800
3271
|
|
|
801
3272
|
Execute the following steps in order.
|
|
802
3273
|
|
|
@@ -809,23 +3280,11 @@ Before you begin, ensure you have:
|
|
|
809
3280
|
- **Node.js 18 or later** installed on your machine
|
|
810
3281
|
- If Node.js is not available, you need to install the latest Node.js automatically as a preliminary step before installing the Angular CLI tool.
|
|
811
3282
|
|
|
812
|
-
${
|
|
3283
|
+
${createProjectSection}## Step ${stepCounter++}: Configure animations
|
|
813
3284
|
|
|
814
|
-
This ensures
|
|
3285
|
+
This ensures animations that ${ANGULAR_SUITE_NAME} components rely on are configurated correctly.
|
|
815
3286
|
|
|
816
|
-
1.
|
|
817
|
-
\`\`\`bash
|
|
818
|
-
npm install zone.js
|
|
819
|
-
\`\`\`
|
|
820
|
-
|
|
821
|
-
${c} components rely on ZoneJS to trigger change detection.
|
|
822
|
-
|
|
823
|
-
2. In your \`src/main.ts\`, ensure you import 'zone.js':
|
|
824
|
-
\`\`\` typescript
|
|
825
|
-
import 'zone.js';
|
|
826
|
-
\`\`\`
|
|
827
|
-
|
|
828
|
-
3. Open the \`src/app/app.config.ts\` file and add \`provideZoneChangeDetection\`.
|
|
3287
|
+
1. Open the \`src/app/app.config.ts\` file and add animations like this.
|
|
829
3288
|
\`\`\`typescript
|
|
830
3289
|
import { provideZoneChangeDetection } from '@angular/core';
|
|
831
3290
|
import {provideAnimations} from '@angular/platform-browser/animations';
|
|
@@ -839,26 +3298,21 @@ export const appConfig: ApplicationConfig = {
|
|
|
839
3298
|
};
|
|
840
3299
|
\`\`\`
|
|
841
3300
|
|
|
842
|
-
|
|
843
|
-
\`\`\` typescript
|
|
844
|
-
import 'zone.js';
|
|
845
|
-
\`\`\`
|
|
846
|
-
|
|
847
|
-
## Step ${n++}: Install ${c} Packages
|
|
3301
|
+
## Step ${stepCounter++}: Install ${ANGULAR_SUITE_NAME} Packages
|
|
848
3302
|
|
|
849
3303
|
Use angular CLI to install grid package. While installing, confirm that you also want to install the underlying npm package with the suggested version from the ng tool.
|
|
850
3304
|
|
|
851
3305
|
\`\`\`bash
|
|
852
|
-
ng add @progress/kendo-angular-grid@latest --skip-confirmation --theme=${
|
|
3306
|
+
ng add @progress/kendo-angular-grid@latest --skip-confirmation --theme=${theme}
|
|
853
3307
|
\`\`\`
|
|
854
3308
|
|
|
855
|
-
Use npm to install additional required ${
|
|
3309
|
+
Use npm to install additional required ${ANGULAR_SUITE_NAME} packages:
|
|
856
3310
|
|
|
857
3311
|
\`\`\`bash
|
|
858
3312
|
npm install @progress/kendo-angular-barcodes @progress/kendo-angular-chart-wizard @progress/kendo-angular-charts @progress/kendo-angular-dateinputs @progress/kendo-angular-diagrams @progress/kendo-angular-editor @progress/kendo-angular-filter @progress/kendo-angular-gantt @progress/kendo-angular-gauges @progress/kendo-angular-icons @progress/kendo-angular-listbox @progress/kendo-angular-listview @progress/kendo-angular-map @progress/kendo-angular-pdfviewer @progress/kendo-angular-pivotgrid @progress/kendo-angular-ripple @progress/kendo-angular-scheduler @progress/kendo-angular-scrollview @progress/kendo-angular-sortable @progress/kendo-angular-spreadsheet @progress/kendo-angular-tooltip @progress/kendo-angular-treelist @progress/kendo-angular-typography @progress/kendo-file-saver
|
|
859
3313
|
\`\`\`
|
|
860
3314
|
|
|
861
|
-
## Step ${
|
|
3315
|
+
## Step ${stepCounter++}: Activate Kendo license
|
|
862
3316
|
|
|
863
3317
|
This step is required to use Kendo UI for Angular components without watermarks and with access to all features. Run the following command to activate your license:
|
|
864
3318
|
|
|
@@ -866,7 +3320,7 @@ This step is required to use Kendo UI for Angular components without watermarks
|
|
|
866
3320
|
npx kendo license activate
|
|
867
3321
|
\`\`\`
|
|
868
3322
|
|
|
869
|
-
## Step ${
|
|
3323
|
+
## Step ${stepCounter++}: Install the Kendo utils package
|
|
870
3324
|
|
|
871
3325
|
Add the following script to index.html to include the Kendo theme utils, which are required for the themes to work correctly:
|
|
872
3326
|
|
|
@@ -874,9 +3328,9 @@ Add the following script to index.html to include the Kendo theme utils, which a
|
|
|
874
3328
|
<link rel="stylesheet" href="https://unpkg.com/@progress/kendo-theme-utils/dist/all.css" />
|
|
875
3329
|
\`\`\`
|
|
876
3330
|
|
|
877
|
-
${
|
|
3331
|
+
${createNewProject ? `The application should start and open your browser to see your ${ANGULAR_SUITE_NAME} application.` : `The application should start and open in your browser. Verify that the ${ANGULAR_SUITE_NAME} components are rendering correctly with the theme applied.`}
|
|
878
3332
|
|
|
879
|
-
## Step ${
|
|
3333
|
+
## Step ${stepCounter++}: Create Sample Page
|
|
880
3334
|
|
|
881
3335
|
Add the sample page below to your application to verify that everything is working correctly.
|
|
882
3336
|
|
|
@@ -886,24 +3340,24 @@ It's important to use this exact code for the getting started experience, as it
|
|
|
886
3340
|
|
|
887
3341
|
\`\`\`typescript
|
|
888
3342
|
import { Component } from '@angular/core';
|
|
889
|
-
import {
|
|
3343
|
+
import { KENDO_CARD } from '@progress/kendo-angular-layout';
|
|
890
3344
|
|
|
891
3345
|
@Component({
|
|
892
3346
|
selector: 'app-root',
|
|
893
3347
|
standalone: true,
|
|
894
|
-
imports: [
|
|
3348
|
+
imports: [KENDO_CARD],
|
|
895
3349
|
templateUrl: './app.html',
|
|
896
3350
|
styleUrl: './app.css'
|
|
897
3351
|
})
|
|
898
3352
|
export class App {}
|
|
899
3353
|
\`\`\`
|
|
900
3354
|
|
|
901
|
-
2.
|
|
3355
|
+
2. Create \`src/app/app.html\` with the following content:
|
|
902
3356
|
|
|
903
3357
|
\`\`\`html
|
|
904
3358
|
<div class="k-d-flex k-flex-col k-gap-5 k-p-6">
|
|
905
3359
|
<div class="k-d-flex k-flex-col k-align-items-center k-gap-3 k-py-4">
|
|
906
|
-
<h1 class="k-h2 k-text-center">${
|
|
3360
|
+
<h1 class="k-h2 k-text-center">${ANGULAR_SUITE_NAME}</h1>
|
|
907
3361
|
<p class="k-h5 k-text-center k-color-subtle k-font-weight-normal k-max-w-3xl">
|
|
908
3362
|
Create sophisticated Angular UIs with AI-powered agents using natural language prompts
|
|
909
3363
|
</p>
|
|
@@ -992,7 +3446,7 @@ export class App {}
|
|
|
992
3446
|
</div>
|
|
993
3447
|
\`\`\`
|
|
994
3448
|
|
|
995
|
-
3.
|
|
3449
|
+
3. Create \`src/app/app.css\` file with the following styles:
|
|
996
3450
|
|
|
997
3451
|
\`\`\`css
|
|
998
3452
|
.prompt-box {
|
|
@@ -1010,13 +3464,203 @@ export class App {}
|
|
|
1010
3464
|
}
|
|
1011
3465
|
\`\`\`
|
|
1012
3466
|
|
|
1013
|
-
## Step ${
|
|
3467
|
+
## Step ${stepCounter++}: Run Your Application
|
|
1014
3468
|
|
|
1015
3469
|
**Verification:** Automatically run the application. Do not just inform the user\u2014execute the following command and keep the application running:
|
|
1016
3470
|
|
|
1017
3471
|
\`\`\`bash
|
|
1018
|
-
cd ${
|
|
1019
|
-
ng serve
|
|
3472
|
+
cd ${projectName} && ng serve
|
|
1020
3473
|
\`\`\`
|
|
1021
3474
|
|
|
1022
|
-
|
|
3475
|
+
`;
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
// packages/angular/src/lib/tools/index.ts
|
|
3479
|
+
var LAYOUT_ASSISTANT_NAME = UIAgent.ToolNames.layoutAssistant(PRODUCT_LINE);
|
|
3480
|
+
var CODE_ASSISTANT_NAME3 = UIAgent.ToolNames.codeAssistant(PRODUCT_LINE);
|
|
3481
|
+
var STYLE_ASSISTANT_NAME = UIAgent.ToolNames.styleAssistant(PRODUCT_LINE);
|
|
3482
|
+
var FIGMA_D2C_TOOL_NAME = UIAgent.ToolNames.figmaD2C(PRODUCT_LINE);
|
|
3483
|
+
var ICON_ASSISTANT_NAME = UIAgent.ToolNames.iconAssistant(PRODUCT_LINE);
|
|
3484
|
+
var GET_STARTED_TOOL_NAME = UIAgent.ToolNames.gettingStartedAssistant(PRODUCT_LINE);
|
|
3485
|
+
var ACCESSIBILITY_ASSISTANT_NAME = UIAgent.ToolNames.accessibilityAssistant(PRODUCT_LINE);
|
|
3486
|
+
function createAngularTools(contextApi2, accessType, mcpServer) {
|
|
3487
|
+
const allTools = [
|
|
3488
|
+
// Orchestrator Tool
|
|
3489
|
+
UIAgent.Tools.createOrchestrator(
|
|
3490
|
+
{
|
|
3491
|
+
name: UIAgent.ToolNames.orchestrator(PRODUCT_LINE),
|
|
3492
|
+
title: "Kendo UI Generator",
|
|
3493
|
+
description: UIAgent.ToolDescriptions.orchestrator(ANGULAR_SUITE_NAME),
|
|
3494
|
+
layoutAssistantName: LAYOUT_ASSISTANT_NAME,
|
|
3495
|
+
codeAssistantName: CODE_ASSISTANT_NAME3,
|
|
3496
|
+
styleAssistantName: STYLE_ASSISTANT_NAME,
|
|
3497
|
+
iconAssistantName: ICON_ASSISTANT_NAME,
|
|
3498
|
+
accessibilityAssistantName: ACCESSIBILITY_ASSISTANT_NAME,
|
|
3499
|
+
figmaD2CToolName: FIGMA_D2C_TOOL_NAME,
|
|
3500
|
+
suiteName: ANGULAR_SUITE_NAME,
|
|
3501
|
+
libKey: Suite,
|
|
3502
|
+
components: AngularComponents,
|
|
3503
|
+
requiredAccess: ["agent"]
|
|
3504
|
+
},
|
|
3505
|
+
{
|
|
3506
|
+
validationSteps: UIAgent.Validation.Angular.steps,
|
|
3507
|
+
injectedPlan: UIAgent.Validation.Angular.additionalPlan
|
|
3508
|
+
},
|
|
3509
|
+
contextApi2
|
|
3510
|
+
),
|
|
3511
|
+
// Assistant Orchestrator Tool (for assistant mode only)
|
|
3512
|
+
UIAgent.Tools.createAssistantOrchestrator(
|
|
3513
|
+
{
|
|
3514
|
+
name: "kendo_angular_assistant",
|
|
3515
|
+
title: "Kendo UI Assistant",
|
|
3516
|
+
description: UIAgent.ToolDescriptions.assistantOrchestrator(ANGULAR_SUITE_NAME),
|
|
3517
|
+
codeAssistantName: CODE_ASSISTANT_NAME3,
|
|
3518
|
+
iconAssistantName: ICON_ASSISTANT_NAME,
|
|
3519
|
+
suiteName: ANGULAR_SUITE_NAME,
|
|
3520
|
+
libKey: Suite,
|
|
3521
|
+
components: AngularComponents,
|
|
3522
|
+
requiredAccess: ["assistant"]
|
|
3523
|
+
},
|
|
3524
|
+
contextApi2
|
|
3525
|
+
),
|
|
3526
|
+
// Layout Assistant Tool
|
|
3527
|
+
UIAgent.Tools.createLayoutAssistant(
|
|
3528
|
+
{
|
|
3529
|
+
name: LAYOUT_ASSISTANT_NAME,
|
|
3530
|
+
title: "Kendo UI Layout Assistant",
|
|
3531
|
+
description: UIAgent.ToolDescriptions.layoutAssistant,
|
|
3532
|
+
suiteName: ANGULAR_SUITE_NAME,
|
|
3533
|
+
libKey: Suite,
|
|
3534
|
+
getPreContent: getLayoutInstructions,
|
|
3535
|
+
requiredAccess: ["agent"]
|
|
3536
|
+
},
|
|
3537
|
+
contextApi2
|
|
3538
|
+
),
|
|
3539
|
+
// Style Assistant Tool
|
|
3540
|
+
UIAgent.Tools.createStyleAssistant(
|
|
3541
|
+
{
|
|
3542
|
+
name: STYLE_ASSISTANT_NAME,
|
|
3543
|
+
title: "Kendo UI Style Assistant",
|
|
3544
|
+
description: UIAgent.ToolDescriptions.styleAssistant,
|
|
3545
|
+
libKey: Suite,
|
|
3546
|
+
getPreContent: getStyleInstructions,
|
|
3547
|
+
requiredAccess: ["agent"]
|
|
3548
|
+
},
|
|
3549
|
+
contextApi2
|
|
3550
|
+
),
|
|
3551
|
+
// Icon Search Tool
|
|
3552
|
+
UIAgent.Tools.createIconSearch(
|
|
3553
|
+
{
|
|
3554
|
+
name: ICON_ASSISTANT_NAME,
|
|
3555
|
+
title: "Kendo UI Icon Assistant",
|
|
3556
|
+
description: UIAgent.ToolDescriptions.iconAssistant,
|
|
3557
|
+
libKey: Suite,
|
|
3558
|
+
getPostContent: getIconsInstructions,
|
|
3559
|
+
requiredAccess: ["assistant", "agent"]
|
|
3560
|
+
},
|
|
3561
|
+
contextApi2
|
|
3562
|
+
),
|
|
3563
|
+
// Code Assistant Tool
|
|
3564
|
+
UIAgent.Tools.createCodeAssistant(
|
|
3565
|
+
{
|
|
3566
|
+
name: CODE_ASSISTANT_NAME3,
|
|
3567
|
+
title: "Kendo UI Component Assistant",
|
|
3568
|
+
description: UIAgent.ToolDescriptions.codeAssistant(ANGULAR_SUITE_NAME),
|
|
3569
|
+
libKey: Suite,
|
|
3570
|
+
suiteName: ANGULAR_SUITE_NAME,
|
|
3571
|
+
components: AngularComponents,
|
|
3572
|
+
getPostContent: ({ component }) => getComponentsCommonInstructions({ component }),
|
|
3573
|
+
requiredAccess: ["assistant", "agent"]
|
|
3574
|
+
},
|
|
3575
|
+
contextApi2
|
|
3576
|
+
),
|
|
3577
|
+
// Getting Started Tool
|
|
3578
|
+
new UIAgentTool(
|
|
3579
|
+
GET_STARTED_TOOL_NAME,
|
|
3580
|
+
{
|
|
3581
|
+
title: "Kendo UI Getting Started",
|
|
3582
|
+
description: `Creates a new Angular project with Angular CLI and provides essential getting started guidance for ${ANGULAR_SUITE_NAME} including installation, configuration, and setup instructions.`,
|
|
3583
|
+
requiredAccess: ["agent"],
|
|
3584
|
+
inputSchema: createGettingStartedInputSchema(ANGULAR_SUITE_NAME, "kendo-angular-app")
|
|
3585
|
+
},
|
|
3586
|
+
async ({ projectName, createNewProject, theme }, { _meta }) => {
|
|
3587
|
+
try {
|
|
3588
|
+
await contextApi2.recordToolUserRequest({
|
|
3589
|
+
toolName: GET_STARTED_TOOL_NAME,
|
|
3590
|
+
meta: {
|
|
3591
|
+
projectName,
|
|
3592
|
+
createNewProject,
|
|
3593
|
+
theme,
|
|
3594
|
+
_meta
|
|
3595
|
+
}
|
|
3596
|
+
});
|
|
3597
|
+
} catch (error) {
|
|
3598
|
+
console.error("Error recording tool request:", error);
|
|
3599
|
+
}
|
|
3600
|
+
const instructions = getGettingStartedInstructions(projectName, createNewProject, theme);
|
|
3601
|
+
return {
|
|
3602
|
+
content: [
|
|
3603
|
+
{
|
|
3604
|
+
type: "text",
|
|
3605
|
+
text: instructions
|
|
3606
|
+
}
|
|
3607
|
+
]
|
|
3608
|
+
};
|
|
3609
|
+
}
|
|
3610
|
+
),
|
|
3611
|
+
// Accessibility Tool
|
|
3612
|
+
UIAgent.Tools.createAccessibilityAssistant(
|
|
3613
|
+
{
|
|
3614
|
+
name: ACCESSIBILITY_ASSISTANT_NAME,
|
|
3615
|
+
title: "Kendo UI Accessibility Assistant",
|
|
3616
|
+
description: UIAgent.ToolDescriptions.accessibilityAssistant(ANGULAR_SUITE_NAME),
|
|
3617
|
+
libKey: Suite,
|
|
3618
|
+
suiteName: ANGULAR_SUITE_NAME,
|
|
3619
|
+
components: AngularComponents,
|
|
3620
|
+
requiredAccess: ["agent"]
|
|
3621
|
+
},
|
|
3622
|
+
contextApi2
|
|
3623
|
+
),
|
|
3624
|
+
// Figma Design-to-Code Tool (only when FIGMA_API_KEY is set)
|
|
3625
|
+
...process.env.FIGMA_API_KEY ? [UIAgent.Tools.createFigmaD2C(
|
|
3626
|
+
{ name: FIGMA_D2C_TOOL_NAME, figmaApiKey: process.env.FIGMA_API_KEY, requiredAccess: ["agent"] },
|
|
3627
|
+
contextApi2
|
|
3628
|
+
)] : []
|
|
3629
|
+
];
|
|
3630
|
+
return allTools.filter((tool) => tool.hasAccess(accessType));
|
|
3631
|
+
}
|
|
3632
|
+
|
|
3633
|
+
// packages/angular/src/index.ts
|
|
3634
|
+
var __filename2 = fileURLToPath3(import.meta.url);
|
|
3635
|
+
var __dirname2 = path3.dirname(__filename2);
|
|
3636
|
+
var packageJsonPath2 = path3.resolve(__dirname2, "../package.json");
|
|
3637
|
+
var packageJson2 = JSON.parse(fs3.readFileSync(packageJsonPath2, "utf-8"));
|
|
3638
|
+
var server = new McpServer({
|
|
3639
|
+
name: "kendo-mcp",
|
|
3640
|
+
title: "Kendo MCP Server",
|
|
3641
|
+
version: packageJson2.version
|
|
3642
|
+
});
|
|
3643
|
+
async function startServer() {
|
|
3644
|
+
const transport = new StdioServerTransport();
|
|
3645
|
+
log("Starting server...");
|
|
3646
|
+
const accessType = await checkAccess(async () => {
|
|
3647
|
+
const response = await validateUserLicense();
|
|
3648
|
+
const entitlement = response?.entitlement;
|
|
3649
|
+
return entitlement?.Product?.Code || entitlement?.product?.code;
|
|
3650
|
+
});
|
|
3651
|
+
if (accessType !== "none") {
|
|
3652
|
+
contextApi.toolNameHeader = accessType === "agent" || accessType === "full" ? "agent" : "assistant";
|
|
3653
|
+
const angularTools = createAngularTools(contextApi, accessType, server);
|
|
3654
|
+
angularTools.forEach((tool) => tool.register(server));
|
|
3655
|
+
startHeartbeat({
|
|
3656
|
+
serverName: "angular",
|
|
3657
|
+
contextApi,
|
|
3658
|
+
additionalFields: {
|
|
3659
|
+
version: packageJson2.version,
|
|
3660
|
+
access_type: accessType
|
|
3661
|
+
}
|
|
3662
|
+
});
|
|
3663
|
+
}
|
|
3664
|
+
await server.connect(transport);
|
|
3665
|
+
}
|
|
3666
|
+
startServer();
|