@progress/kendo-angular-webmcp 24.0.0-develop.41
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/LICENSE.md +11 -0
- package/NOTICE.txt +2627 -0
- package/README.md +44 -0
- package/directives.d.ts +27 -0
- package/fesm2022/progress-kendo-angular-webmcp.mjs +4873 -0
- package/index.d.ts +8 -0
- package/lib/adapters/chart.adapter.d.ts +22 -0
- package/lib/adapters/conversational-ui.adapter.d.ts +27 -0
- package/lib/adapters/dateinputs.adapter.d.ts +51 -0
- package/lib/adapters/dialog.adapter.d.ts +27 -0
- package/lib/adapters/dropdowns.adapter.d.ts +59 -0
- package/lib/adapters/editor.adapter.d.ts +22 -0
- package/lib/adapters/gantt.adapter.d.ts +22 -0
- package/lib/adapters/grid.adapter.d.ts +34 -0
- package/lib/adapters/inputs.adapter.d.ts +26 -0
- package/lib/adapters/layout.adapter.d.ts +43 -0
- package/lib/adapters/misc.adapter.d.ts +100 -0
- package/lib/adapters/pivotgrid.adapter.d.ts +22 -0
- package/lib/adapters/scheduler.adapter.d.ts +25 -0
- package/lib/adapters/spreadsheet.adapter.d.ts +22 -0
- package/lib/adapters/treelist.adapter.d.ts +27 -0
- package/lib/webmcp.directive.d.ts +48 -0
- package/lib/webmcp.interfaces.d.ts +128 -0
- package/package-metadata.d.ts +9 -0
- package/package-metadata.mjs +13 -0
- package/package.json +168 -0
- package/schematics/collection.json +12 -0
- package/schematics/ngAdd/index.js +12 -0
- package/schematics/ngAdd/schema.json +24 -0
- package/webmcp.module.d.ts +31 -0
|
@@ -0,0 +1,4873 @@
|
|
|
1
|
+
/**-----------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright © 2026 Progress Software Corporation. All rights reserved.
|
|
3
|
+
* Licensed under commercial license. See LICENSE.md in the project root for more information
|
|
4
|
+
*-------------------------------------------------------------------------------------------*/
|
|
5
|
+
import * as i0 from '@angular/core';
|
|
6
|
+
import { Input, Host, Optional, Inject, Directive, NgModule } from '@angular/core';
|
|
7
|
+
import { isDocumentAvailable, KENDO_WEBMCP_HOST } from '@progress/kendo-angular-common';
|
|
8
|
+
import { validatePackage } from '@progress/kendo-licensing';
|
|
9
|
+
import { saveAs } from '@progress/kendo-file-saver';
|
|
10
|
+
import { Workbook } from '@progress/kendo-ooxml';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @hidden
|
|
14
|
+
*/
|
|
15
|
+
const packageMetadata = {
|
|
16
|
+
name: '@progress/kendo-angular-webmcp',
|
|
17
|
+
productName: 'Kendo UI for Angular',
|
|
18
|
+
productCode: 'KENDOUIANGULAR',
|
|
19
|
+
productCodes: ['KENDOUIANGULAR'],
|
|
20
|
+
publishDate: 1779255940,
|
|
21
|
+
version: '24.0.0-develop.41',
|
|
22
|
+
licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/?utm_medium=product&utm_source=kendoangular&utm_campaign=kendo-ui-angular-purchase-license-keys-warning',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolves a `boolean | WebMcpConfig` input into a full config with defaults.
|
|
27
|
+
*
|
|
28
|
+
* @hidden
|
|
29
|
+
*/
|
|
30
|
+
function resolveConfig(value) {
|
|
31
|
+
if (value === false) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (value === true) {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @hidden
|
|
42
|
+
*
|
|
43
|
+
* Emits dataStateChange so kendoGridBinding re-processes data.
|
|
44
|
+
*/
|
|
45
|
+
function emitDataStateChange(grid) {
|
|
46
|
+
grid.dataStateChange.emit({
|
|
47
|
+
skip: grid.skip || 0,
|
|
48
|
+
take: grid.pageSize,
|
|
49
|
+
sort: grid.sort,
|
|
50
|
+
filter: grid.filter,
|
|
51
|
+
group: grid.group
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* @hidden
|
|
56
|
+
*
|
|
57
|
+
* Returns column fields that satisfy a predicate.
|
|
58
|
+
*/
|
|
59
|
+
function getColumnFields(grid, predicate) {
|
|
60
|
+
const fields = [];
|
|
61
|
+
const columns = grid.columns?.toArray() || [];
|
|
62
|
+
for (const col of columns) {
|
|
63
|
+
if ('field' in col && col.field) {
|
|
64
|
+
if (!predicate || predicate(col)) {
|
|
65
|
+
fields.push(col.field);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return fields;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @hidden
|
|
73
|
+
*
|
|
74
|
+
* Evaluates a highlight condition against an item value.
|
|
75
|
+
*/
|
|
76
|
+
function matchesHighlight(itemValue, operator, value) {
|
|
77
|
+
const numItem = Number(itemValue);
|
|
78
|
+
const numVal = Number(value);
|
|
79
|
+
const useNum = !isNaN(numItem) && !isNaN(numVal);
|
|
80
|
+
switch (operator) {
|
|
81
|
+
case 'eq': return useNum ? numItem === numVal : String(itemValue).toLowerCase() === String(value).toLowerCase();
|
|
82
|
+
case 'neq': return useNum ? numItem !== numVal : String(itemValue).toLowerCase() !== String(value).toLowerCase();
|
|
83
|
+
case 'gt': return useNum && numItem > numVal;
|
|
84
|
+
case 'gte': return useNum && numItem >= numVal;
|
|
85
|
+
case 'lt': return useNum && numItem < numVal;
|
|
86
|
+
case 'lte': return useNum && numItem <= numVal;
|
|
87
|
+
case 'contains': return String(itemValue).toLowerCase().includes(String(value).toLowerCase());
|
|
88
|
+
case 'startswith': return String(itemValue).toLowerCase().startsWith(String(value).toLowerCase());
|
|
89
|
+
case 'endswith': return String(itemValue).toLowerCase().endsWith(String(value).toLowerCase());
|
|
90
|
+
default: return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* @hidden
|
|
95
|
+
*
|
|
96
|
+
* Finds a column by field name.
|
|
97
|
+
*/
|
|
98
|
+
function findColumn(grid, field) {
|
|
99
|
+
return (grid.columns?.toArray() || []).find(c => c.field === field);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* @hidden
|
|
103
|
+
*
|
|
104
|
+
* Web MCP tool adapter for the Kendo Angular Grid component.
|
|
105
|
+
* Supports: sort, filter, group, page, select, export, column operations.
|
|
106
|
+
*/
|
|
107
|
+
class GridToolAdapter {
|
|
108
|
+
selector = 'kendo-grid';
|
|
109
|
+
registerTools(grid, config, modelContext, ngZone) {
|
|
110
|
+
const handles = [];
|
|
111
|
+
const prefix = config.dataName || 'kendo-grid';
|
|
112
|
+
const label = config.dataName
|
|
113
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' grid'
|
|
114
|
+
: 'Kendo UI Grid';
|
|
115
|
+
const rawOptions = this.buildToolOptions(grid, config, label);
|
|
116
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
117
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled).map(t => t.name));
|
|
118
|
+
handles.push(...this.registerSortTools(grid, modelContext, ngZone, prefix, label, enabled));
|
|
119
|
+
handles.push(...this.registerFilterTools(grid, modelContext, ngZone, prefix, label, enabled));
|
|
120
|
+
handles.push(...this.registerGroupTools(grid, modelContext, ngZone, prefix, label, enabled));
|
|
121
|
+
handles.push(...this.registerPageTools(grid, modelContext, ngZone, prefix, label, enabled));
|
|
122
|
+
handles.push(...this.registerSelectionTools(grid, modelContext, ngZone, prefix, label, enabled));
|
|
123
|
+
handles.push(...this.registerExportTools(grid, modelContext, ngZone, prefix, label, enabled));
|
|
124
|
+
handles.push(...this.registerColumnTools(grid, modelContext, ngZone, prefix, label, enabled));
|
|
125
|
+
handles.push(...this.registerSelectAllTool(grid, modelContext, ngZone, prefix, label, enabled));
|
|
126
|
+
handles.push(...this.registerGroupExpandTools(grid, modelContext, ngZone, prefix, label, enabled));
|
|
127
|
+
handles.push(...this.registerDetailTools(grid, modelContext, ngZone, prefix, label, enabled));
|
|
128
|
+
handles.push(...this.registerGetDataTool(grid, modelContext, ngZone, prefix, label, enabled));
|
|
129
|
+
handles.push(...this.registerHighlightTools(grid, modelContext, ngZone, prefix, label, enabled));
|
|
130
|
+
return handles;
|
|
131
|
+
}
|
|
132
|
+
// Tool options builder
|
|
133
|
+
buildToolOptions(grid, config, label) {
|
|
134
|
+
const sort = !!grid.sortable;
|
|
135
|
+
const filter = !!grid.filterable;
|
|
136
|
+
const group = !!grid.groupable;
|
|
137
|
+
const page = !!grid.pageable;
|
|
138
|
+
const select = !!grid.selectable;
|
|
139
|
+
return [
|
|
140
|
+
{ name: 'sort-column', description: `Sort the ${label} by a specified column field.`, enabled: sort },
|
|
141
|
+
{ name: 'clear-sort', description: `Remove all sorting from the ${label}.`, enabled: sort },
|
|
142
|
+
{ name: 'filter', description: `Apply a filter to a column in the ${label}.`, enabled: filter },
|
|
143
|
+
{ name: 'clear-filters', description: `Remove all filters from the ${label}.`, enabled: filter },
|
|
144
|
+
{ name: 'group', description: `Group the ${label} rows by a column field.`, enabled: group },
|
|
145
|
+
{ name: 'clear-groups', description: `Remove all grouping from the ${label}.`, enabled: group },
|
|
146
|
+
{ name: 'go-to-page', description: `Navigate to a specific page in the ${label}.`, enabled: page },
|
|
147
|
+
{ name: 'set-page-size', description: `Change the number of rows per page in the ${label}.`, enabled: page },
|
|
148
|
+
{ name: 'select-rows', description: `Select rows in the ${label} by their 0-based indices.`, enabled: select },
|
|
149
|
+
{ name: 'clear-selection', description: `Deselect all rows in the ${label}.`, enabled: select },
|
|
150
|
+
{ name: 'select-all', description: `Select all rows in the ${label}.`, enabled: select },
|
|
151
|
+
{ name: 'export-pdf', description: `Export the ${label} to a PDF file.`, enabled: true },
|
|
152
|
+
{ name: 'export-excel', description: `Export the ${label} data to an Excel file.`, enabled: true },
|
|
153
|
+
{ name: 'export-csv', description: `Export the ${label} data to a CSV file.`, enabled: true },
|
|
154
|
+
{ name: 'show-column', description: `Show a hidden column in the ${label}.`, enabled: true },
|
|
155
|
+
{ name: 'hide-column', description: `Hide a visible column in the ${label}.`, enabled: true },
|
|
156
|
+
{ name: 'lock-column', description: `Lock (freeze) a column in the ${label}.`, enabled: true },
|
|
157
|
+
{ name: 'unlock-column', description: `Unlock a frozen column in the ${label}.`, enabled: true },
|
|
158
|
+
{ name: 'reorder-column', description: `Move a column to a new position in the ${label}.`, enabled: !!grid.reorderable },
|
|
159
|
+
{ name: 'resize-column', description: `Resize a column in the ${label} to a specified pixel width.`, enabled: !!grid.resizable },
|
|
160
|
+
{ name: 'group-expand', description: `Expand a specific group in the ${label}.`, enabled: group },
|
|
161
|
+
{ name: 'group-collapse', description: `Collapse a specific group in the ${label}.`, enabled: group },
|
|
162
|
+
{ name: 'group-expand-all', description: `Expand all groups in the ${label}.`, enabled: group },
|
|
163
|
+
{ name: 'group-collapse-all', description: `Collapse all groups in the ${label}.`, enabled: group },
|
|
164
|
+
{ name: 'detail-expand', description: `Expand the detail row at a specific index in the ${label}.`, enabled: true },
|
|
165
|
+
{ name: 'detail-collapse', description: `Collapse the detail row at a specific index in the ${label}.`, enabled: true },
|
|
166
|
+
{ name: 'detail-expand-all', description: `Expand all detail rows in the ${label}.`, enabled: true },
|
|
167
|
+
{ name: 'detail-collapse-all', description: `Collapse all detail rows in the ${label}.`, enabled: true },
|
|
168
|
+
{ name: 'get-data', description: `Read the rows currently visible in the ${label} (after any active filter/sort/page) as JSON.`, enabled: true },
|
|
169
|
+
{ name: 'highlight', description: `Visually highlight rows in the ${label} that match a condition. Uses the grid rowClass callback to apply the k-selected CSS class.`, enabled: true },
|
|
170
|
+
{ name: 'clear-highlight', description: `Remove all row highlights from the ${label}.`, enabled: true }
|
|
171
|
+
];
|
|
172
|
+
}
|
|
173
|
+
// Sort
|
|
174
|
+
registerSortTools(grid, mc, zone, prefix, label, enabled) {
|
|
175
|
+
const handles = [];
|
|
176
|
+
if (enabled.has('sort-column')) {
|
|
177
|
+
handles.push(mc.registerTool({
|
|
178
|
+
name: `${prefix}-sort-column`,
|
|
179
|
+
description: `Sort the ${label} by a specified column field. Sets a new sort descriptor, replacing any existing sort.`,
|
|
180
|
+
inputSchema: {
|
|
181
|
+
type: 'object',
|
|
182
|
+
properties: {
|
|
183
|
+
field: {
|
|
184
|
+
type: 'string',
|
|
185
|
+
description: 'The column field name to sort by. Omit to discover available sortable fields.'
|
|
186
|
+
},
|
|
187
|
+
dir: {
|
|
188
|
+
type: 'string',
|
|
189
|
+
enum: ['asc', 'desc'],
|
|
190
|
+
description: 'Sort direction. Defaults to "asc".'
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
required: []
|
|
194
|
+
},
|
|
195
|
+
execute: (args) => {
|
|
196
|
+
const sortableFields = getColumnFields(grid, col => col.sortable !== false);
|
|
197
|
+
const field = args['field'];
|
|
198
|
+
const direction = args['dir'];
|
|
199
|
+
if (!field) {
|
|
200
|
+
return { success: true, message: 'Sortable fields: ' + sortableFields.join(', '), data: { sortableFields, currentSort: grid.sort || [] } };
|
|
201
|
+
}
|
|
202
|
+
if (!sortableFields.includes(field)) {
|
|
203
|
+
return { success: false, message: `Field "${field}" is not sortable. Available: ${sortableFields.join(', ')}` };
|
|
204
|
+
}
|
|
205
|
+
const dir = direction === 'desc' ? 'desc' : 'asc';
|
|
206
|
+
const descriptors = [{ field, dir }];
|
|
207
|
+
zone.run(() => {
|
|
208
|
+
grid.sort = descriptors;
|
|
209
|
+
grid.sortChange.emit(descriptors);
|
|
210
|
+
emitDataStateChange(grid);
|
|
211
|
+
});
|
|
212
|
+
return { success: true, message: `Sorted by "${field}" ${dir}ending.`, data: { sort: descriptors } };
|
|
213
|
+
}
|
|
214
|
+
}));
|
|
215
|
+
}
|
|
216
|
+
if (enabled.has('clear-sort')) {
|
|
217
|
+
handles.push(mc.registerTool({
|
|
218
|
+
name: `${prefix}-clear-sort`,
|
|
219
|
+
description: `Remove all sorting from the ${label}.`,
|
|
220
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
221
|
+
execute: () => {
|
|
222
|
+
zone.run(() => {
|
|
223
|
+
grid.sort = [];
|
|
224
|
+
grid.sortChange.emit([]);
|
|
225
|
+
emitDataStateChange(grid);
|
|
226
|
+
});
|
|
227
|
+
return { success: true, message: 'Sort cleared.' };
|
|
228
|
+
}
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
231
|
+
return handles;
|
|
232
|
+
}
|
|
233
|
+
// Filter
|
|
234
|
+
registerFilterTools(grid, mc, zone, prefix, label, enabled) {
|
|
235
|
+
const handles = [];
|
|
236
|
+
if (enabled.has('filter')) {
|
|
237
|
+
handles.push(mc.registerTool({
|
|
238
|
+
name: `${prefix}-filter`,
|
|
239
|
+
description: `Apply a filter to a column in the ${label}. Supports operators: eq, neq, contains, startswith, endswith, gt, gte, lt, lte, isnull, isnotnull.`,
|
|
240
|
+
inputSchema: {
|
|
241
|
+
type: 'object',
|
|
242
|
+
properties: {
|
|
243
|
+
field: {
|
|
244
|
+
type: 'string',
|
|
245
|
+
description: 'The column field name to filter. Omit to discover available filterable fields.'
|
|
246
|
+
},
|
|
247
|
+
operator: {
|
|
248
|
+
type: 'string',
|
|
249
|
+
enum: ['eq', 'neq', 'contains', 'startswith', 'endswith', 'gt', 'gte', 'lt', 'lte', 'isnull', 'isnotnull'],
|
|
250
|
+
description: 'Filter operator. Defaults to "contains". Use "eq", "gt", "lt", etc. for numeric or date columns.'
|
|
251
|
+
},
|
|
252
|
+
value: {
|
|
253
|
+
description: 'The filter value.'
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
required: []
|
|
257
|
+
},
|
|
258
|
+
execute: (args) => {
|
|
259
|
+
const filterableFields = getColumnFields(grid);
|
|
260
|
+
const field = args['field'];
|
|
261
|
+
const operator = args['operator'] || 'contains';
|
|
262
|
+
const value = args['value'];
|
|
263
|
+
if (!field) {
|
|
264
|
+
return { success: true, message: 'Filterable fields: ' + filterableFields.join(', '), data: { filterableFields, currentFilter: grid.filter } };
|
|
265
|
+
}
|
|
266
|
+
if (!filterableFields.includes(field)) {
|
|
267
|
+
return { success: false, message: `Field "${field}" not found. Available: ${filterableFields.join(', ')}` };
|
|
268
|
+
}
|
|
269
|
+
const currentFilter = grid.filter || { logic: 'and', filters: [] };
|
|
270
|
+
// Remove existing filters on the same field
|
|
271
|
+
const otherFilters = currentFilter.filters.filter((f) => !('field' in f && f.field === field));
|
|
272
|
+
const newFilter = {
|
|
273
|
+
logic: currentFilter.logic || 'and',
|
|
274
|
+
filters: [...otherFilters, { field, operator, value }]
|
|
275
|
+
};
|
|
276
|
+
zone.run(() => {
|
|
277
|
+
grid.filter = newFilter;
|
|
278
|
+
grid.filterChange.emit(newFilter);
|
|
279
|
+
emitDataStateChange(grid);
|
|
280
|
+
});
|
|
281
|
+
return { success: true, message: `Filtered "${field}" ${operator} "${value}".`, data: { filter: newFilter } };
|
|
282
|
+
}
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
if (enabled.has('clear-filters')) {
|
|
286
|
+
handles.push(mc.registerTool({
|
|
287
|
+
name: `${prefix}-clear-filters`,
|
|
288
|
+
description: `Remove all filters from the ${label}.`,
|
|
289
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
290
|
+
execute: () => {
|
|
291
|
+
const emptyFilter = { logic: 'and', filters: [] };
|
|
292
|
+
zone.run(() => {
|
|
293
|
+
grid.filter = emptyFilter;
|
|
294
|
+
grid.filterChange.emit(emptyFilter);
|
|
295
|
+
emitDataStateChange(grid);
|
|
296
|
+
});
|
|
297
|
+
return { success: true, message: 'All filters cleared.' };
|
|
298
|
+
}
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
return handles;
|
|
302
|
+
}
|
|
303
|
+
// Group
|
|
304
|
+
registerGroupTools(grid, mc, zone, prefix, label, enabled) {
|
|
305
|
+
const handles = [];
|
|
306
|
+
if (enabled.has('group')) {
|
|
307
|
+
handles.push(mc.registerTool({
|
|
308
|
+
name: `${prefix}-group`,
|
|
309
|
+
description: `Group the ${label} rows by a column field.`,
|
|
310
|
+
inputSchema: {
|
|
311
|
+
type: 'object',
|
|
312
|
+
properties: {
|
|
313
|
+
field: {
|
|
314
|
+
type: 'string',
|
|
315
|
+
description: 'The column field name to group by. Omit to discover available fields.'
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
required: []
|
|
319
|
+
},
|
|
320
|
+
execute: (args) => {
|
|
321
|
+
const fields = getColumnFields(grid);
|
|
322
|
+
const field = args['field'];
|
|
323
|
+
if (!field) {
|
|
324
|
+
return { success: true, message: 'Groupable fields: ' + fields.join(', '), data: { fields, currentGroup: grid.group || [] } };
|
|
325
|
+
}
|
|
326
|
+
if (!fields.includes(field)) {
|
|
327
|
+
return { success: false, message: `Field "${field}" not found. Available: ${fields.join(', ')}` };
|
|
328
|
+
}
|
|
329
|
+
const currentGroups = grid.group || [];
|
|
330
|
+
if (currentGroups.some((g) => g.field === field)) {
|
|
331
|
+
return { success: true, message: `Already grouped by "${field}".`, data: { group: currentGroups } };
|
|
332
|
+
}
|
|
333
|
+
const newGroups = [...currentGroups, { field }];
|
|
334
|
+
zone.run(() => {
|
|
335
|
+
grid.group = newGroups;
|
|
336
|
+
grid.groupChange.emit(newGroups);
|
|
337
|
+
emitDataStateChange(grid);
|
|
338
|
+
});
|
|
339
|
+
return { success: true, message: `Grouped by "${field}".`, data: { group: newGroups } };
|
|
340
|
+
}
|
|
341
|
+
}));
|
|
342
|
+
}
|
|
343
|
+
if (enabled.has('clear-groups')) {
|
|
344
|
+
handles.push(mc.registerTool({
|
|
345
|
+
name: `${prefix}-clear-groups`,
|
|
346
|
+
description: `Remove all grouping from the ${label}.`,
|
|
347
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
348
|
+
execute: () => {
|
|
349
|
+
zone.run(() => {
|
|
350
|
+
grid.group = [];
|
|
351
|
+
grid.groupChange.emit([]);
|
|
352
|
+
emitDataStateChange(grid);
|
|
353
|
+
});
|
|
354
|
+
return { success: true, message: 'Groups cleared.' };
|
|
355
|
+
}
|
|
356
|
+
}));
|
|
357
|
+
}
|
|
358
|
+
return handles;
|
|
359
|
+
}
|
|
360
|
+
// Page
|
|
361
|
+
registerPageTools(grid, mc, zone, prefix, label, enabled) {
|
|
362
|
+
const handles = [];
|
|
363
|
+
if (enabled.has('go-to-page')) {
|
|
364
|
+
handles.push(mc.registerTool({
|
|
365
|
+
name: `${prefix}-go-to-page`,
|
|
366
|
+
description: `Navigate to a specific page in the ${label}. Pages are 1-based.`,
|
|
367
|
+
inputSchema: {
|
|
368
|
+
type: 'object',
|
|
369
|
+
properties: {
|
|
370
|
+
page: {
|
|
371
|
+
type: 'number',
|
|
372
|
+
description: 'The 1-based page number. Omit to get current page info.'
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
required: []
|
|
376
|
+
},
|
|
377
|
+
execute: (args) => {
|
|
378
|
+
const page = args['page'];
|
|
379
|
+
const pageSize = grid.pageSize || 10;
|
|
380
|
+
const totalItems = Array.isArray(grid.data) ? grid.data.length : (grid.data?.total || 0);
|
|
381
|
+
const totalPages = Math.ceil(totalItems / pageSize) || 1;
|
|
382
|
+
const currentPage = Math.floor((grid.skip || 0) / pageSize) + 1;
|
|
383
|
+
if (!page) {
|
|
384
|
+
return { success: true, message: `Page ${currentPage} of ${totalPages}.`, data: { currentPage, totalPages, pageSize, totalItems } };
|
|
385
|
+
}
|
|
386
|
+
if (page < 1 || page > totalPages) {
|
|
387
|
+
return { success: false, message: `Page ${page} out of range (1–${totalPages}).` };
|
|
388
|
+
}
|
|
389
|
+
const newSkip = (page - 1) * pageSize;
|
|
390
|
+
zone.run(() => {
|
|
391
|
+
grid.skip = newSkip;
|
|
392
|
+
grid.pageChange.emit({ skip: newSkip, take: pageSize });
|
|
393
|
+
emitDataStateChange(grid);
|
|
394
|
+
});
|
|
395
|
+
return { success: true, message: `Navigated to page ${page}.`, data: { page, skip: newSkip, pageSize } };
|
|
396
|
+
}
|
|
397
|
+
}));
|
|
398
|
+
}
|
|
399
|
+
if (enabled.has('set-page-size')) {
|
|
400
|
+
handles.push(mc.registerTool({
|
|
401
|
+
name: `${prefix}-set-page-size`,
|
|
402
|
+
description: `Change the number of rows per page in the ${label}.`,
|
|
403
|
+
inputSchema: {
|
|
404
|
+
type: 'object',
|
|
405
|
+
properties: {
|
|
406
|
+
pageSize: {
|
|
407
|
+
type: 'number',
|
|
408
|
+
description: 'Number of rows per page.'
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
required: ['pageSize']
|
|
412
|
+
},
|
|
413
|
+
execute: (args) => {
|
|
414
|
+
const newPageSize = args['pageSize'];
|
|
415
|
+
if (!newPageSize || newPageSize < 1) {
|
|
416
|
+
return { success: false, message: 'pageSize must be a positive number.' };
|
|
417
|
+
}
|
|
418
|
+
zone.run(() => {
|
|
419
|
+
grid.pageSize = newPageSize;
|
|
420
|
+
grid.skip = 0;
|
|
421
|
+
grid.pageChange.emit({ skip: 0, take: newPageSize });
|
|
422
|
+
emitDataStateChange(grid);
|
|
423
|
+
});
|
|
424
|
+
return { success: true, message: `Page size set to ${newPageSize}.`, data: { pageSize: newPageSize } };
|
|
425
|
+
}
|
|
426
|
+
}));
|
|
427
|
+
}
|
|
428
|
+
return handles;
|
|
429
|
+
}
|
|
430
|
+
// Selection
|
|
431
|
+
registerSelectionTools(grid, mc, zone, prefix, label, enabled) {
|
|
432
|
+
const handles = [];
|
|
433
|
+
if (enabled.has('select-rows')) {
|
|
434
|
+
handles.push(mc.registerTool({
|
|
435
|
+
name: `${prefix}-select-rows`,
|
|
436
|
+
description: `Select rows in the ${label} by their data item indices (0-based).`,
|
|
437
|
+
inputSchema: {
|
|
438
|
+
type: 'object',
|
|
439
|
+
properties: {
|
|
440
|
+
indices: {
|
|
441
|
+
type: 'array',
|
|
442
|
+
items: { type: 'number' },
|
|
443
|
+
description: 'Array of 0-based row indices to select.'
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
required: ['indices']
|
|
447
|
+
},
|
|
448
|
+
execute: (args) => {
|
|
449
|
+
const indices = args['indices'];
|
|
450
|
+
const data = Array.isArray(grid.data) ? grid.data : (grid.data?.data ?? []);
|
|
451
|
+
zone.run(() => {
|
|
452
|
+
grid.selectionChange.emit({
|
|
453
|
+
selectedRows: indices.map(i => ({ dataItem: data[i] ?? null, index: i })),
|
|
454
|
+
deselectedRows: [],
|
|
455
|
+
ctrlKey: false,
|
|
456
|
+
shiftKey: false
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
return { success: true, message: `Selected ${indices.length} row(s).` };
|
|
460
|
+
}
|
|
461
|
+
}));
|
|
462
|
+
}
|
|
463
|
+
if (enabled.has('clear-selection')) {
|
|
464
|
+
handles.push(mc.registerTool({
|
|
465
|
+
name: `${prefix}-clear-selection`,
|
|
466
|
+
description: `Deselect all rows in the ${label}.`,
|
|
467
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
468
|
+
execute: () => {
|
|
469
|
+
const data = Array.isArray(grid.data) ? grid.data : (grid.data?.data ?? []);
|
|
470
|
+
zone.run(() => {
|
|
471
|
+
grid.selectionChange.emit({
|
|
472
|
+
selectedRows: [],
|
|
473
|
+
deselectedRows: data.map((item, i) => ({ dataItem: item, index: i })),
|
|
474
|
+
ctrlKey: false,
|
|
475
|
+
shiftKey: false
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
return { success: true, message: 'Selection cleared.' };
|
|
479
|
+
}
|
|
480
|
+
}));
|
|
481
|
+
}
|
|
482
|
+
return handles;
|
|
483
|
+
}
|
|
484
|
+
// Export
|
|
485
|
+
registerExportTools(grid, mc, zone, prefix, label, enabled) {
|
|
486
|
+
const handles = [];
|
|
487
|
+
if (enabled.has('export-pdf')) {
|
|
488
|
+
handles.push(mc.registerTool({
|
|
489
|
+
name: `${prefix}-export-pdf`,
|
|
490
|
+
description: `Export the ${label} to a PDF file.`,
|
|
491
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
492
|
+
execute: () => {
|
|
493
|
+
if (typeof grid.saveAsPDF !== 'function') {
|
|
494
|
+
return { success: false, message: 'PDF export is not configured. Add <kendo-grid-pdf> to the grid.' };
|
|
495
|
+
}
|
|
496
|
+
zone.run(() => grid.saveAsPDF());
|
|
497
|
+
return { success: true, message: 'PDF export started.' };
|
|
498
|
+
}
|
|
499
|
+
}));
|
|
500
|
+
}
|
|
501
|
+
if (enabled.has('export-excel')) {
|
|
502
|
+
handles.push(mc.registerTool({
|
|
503
|
+
name: `${prefix}-export-excel`,
|
|
504
|
+
description: `Export the ${label} data to an Excel file.`,
|
|
505
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
506
|
+
execute: () => {
|
|
507
|
+
if (typeof grid.saveAsExcel !== 'function') {
|
|
508
|
+
return { success: false, message: 'Excel export is not configured. Add <kendo-grid-excel> to the grid.' };
|
|
509
|
+
}
|
|
510
|
+
zone.run(() => grid.saveAsExcel());
|
|
511
|
+
return { success: true, message: 'Excel export started.' };
|
|
512
|
+
}
|
|
513
|
+
}));
|
|
514
|
+
}
|
|
515
|
+
if (enabled.has('export-csv')) {
|
|
516
|
+
handles.push(mc.registerTool({
|
|
517
|
+
name: `${prefix}-export-csv`,
|
|
518
|
+
description: `Export the ${label} data to a CSV file.`,
|
|
519
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
520
|
+
execute: () => {
|
|
521
|
+
if (typeof grid.saveAsCSV !== 'function') {
|
|
522
|
+
return { success: false, message: 'CSV export is not available.' };
|
|
523
|
+
}
|
|
524
|
+
zone.run(() => grid.saveAsCSV());
|
|
525
|
+
return { success: true, message: 'CSV export started.' };
|
|
526
|
+
}
|
|
527
|
+
}));
|
|
528
|
+
}
|
|
529
|
+
return handles;
|
|
530
|
+
}
|
|
531
|
+
// Column Operations
|
|
532
|
+
registerColumnTools(grid, mc, zone, prefix, label, enabled) {
|
|
533
|
+
const handles = [];
|
|
534
|
+
if (enabled.has('show-column')) {
|
|
535
|
+
handles.push(mc.registerTool({
|
|
536
|
+
name: `${prefix}-show-column`,
|
|
537
|
+
description: `Show a hidden column in the ${label}.`,
|
|
538
|
+
inputSchema: {
|
|
539
|
+
type: 'object',
|
|
540
|
+
properties: {
|
|
541
|
+
field: { type: 'string', description: 'The field name of the column to show. Omit to list hidden columns.' }
|
|
542
|
+
},
|
|
543
|
+
required: []
|
|
544
|
+
},
|
|
545
|
+
execute: (args) => {
|
|
546
|
+
const field = args['field'];
|
|
547
|
+
const allFields = getColumnFields(grid);
|
|
548
|
+
const hiddenFields = allFields.filter(f => findColumn(grid, f)?.hidden);
|
|
549
|
+
if (!field) {
|
|
550
|
+
return { success: true, message: 'Hidden columns: ' + (hiddenFields.length ? hiddenFields.join(', ') : 'none'), data: { hiddenFields } };
|
|
551
|
+
}
|
|
552
|
+
const col = findColumn(grid, field);
|
|
553
|
+
if (!col) {
|
|
554
|
+
return { success: false, message: `Column "${field}" not found. Available: ${allFields.join(', ')}` };
|
|
555
|
+
}
|
|
556
|
+
if (!col.hidden) {
|
|
557
|
+
return { success: true, message: `Column "${field}" is already visible.` };
|
|
558
|
+
}
|
|
559
|
+
zone.run(() => {
|
|
560
|
+
col.hidden = false;
|
|
561
|
+
grid.columnInfoService?.changeVisibility([{ column: col, hidden: false }]);
|
|
562
|
+
});
|
|
563
|
+
return { success: true, message: `Column "${field}" is now visible.` };
|
|
564
|
+
}
|
|
565
|
+
}));
|
|
566
|
+
}
|
|
567
|
+
if (enabled.has('hide-column')) {
|
|
568
|
+
handles.push(mc.registerTool({
|
|
569
|
+
name: `${prefix}-hide-column`,
|
|
570
|
+
description: `Hide a visible column in the ${label}.`,
|
|
571
|
+
inputSchema: {
|
|
572
|
+
type: 'object',
|
|
573
|
+
properties: {
|
|
574
|
+
field: { type: 'string', description: 'The field name of the column to hide. Omit to list visible columns.' }
|
|
575
|
+
},
|
|
576
|
+
required: []
|
|
577
|
+
},
|
|
578
|
+
execute: (args) => {
|
|
579
|
+
const field = args['field'];
|
|
580
|
+
const allFields = getColumnFields(grid);
|
|
581
|
+
const visibleFields = allFields.filter(f => !findColumn(grid, f)?.hidden);
|
|
582
|
+
if (!field) {
|
|
583
|
+
return { success: true, message: 'Visible columns: ' + visibleFields.join(', '), data: { visibleFields } };
|
|
584
|
+
}
|
|
585
|
+
const col = findColumn(grid, field);
|
|
586
|
+
if (!col) {
|
|
587
|
+
return { success: false, message: `Column "${field}" not found. Available: ${allFields.join(', ')}` };
|
|
588
|
+
}
|
|
589
|
+
if (col.hidden) {
|
|
590
|
+
return { success: true, message: `Column "${field}" is already hidden.` };
|
|
591
|
+
}
|
|
592
|
+
zone.run(() => {
|
|
593
|
+
col.hidden = true;
|
|
594
|
+
grid.columnInfoService?.changeVisibility([{ column: col, hidden: true }]);
|
|
595
|
+
});
|
|
596
|
+
return { success: true, message: `Column "${field}" is now hidden.` };
|
|
597
|
+
}
|
|
598
|
+
}));
|
|
599
|
+
}
|
|
600
|
+
if (enabled.has('lock-column')) {
|
|
601
|
+
handles.push(mc.registerTool({
|
|
602
|
+
name: `${prefix}-lock-column`,
|
|
603
|
+
description: `Lock (freeze) a column in the ${label} so it stays visible during horizontal scrolling.`,
|
|
604
|
+
inputSchema: {
|
|
605
|
+
type: 'object',
|
|
606
|
+
properties: {
|
|
607
|
+
field: { type: 'string', description: 'The field name of the column to lock.' }
|
|
608
|
+
},
|
|
609
|
+
required: ['field']
|
|
610
|
+
},
|
|
611
|
+
execute: (args) => {
|
|
612
|
+
const field = args['field'];
|
|
613
|
+
const col = findColumn(grid, field);
|
|
614
|
+
if (!col) {
|
|
615
|
+
return { success: false, message: `Column "${field}" not found.` };
|
|
616
|
+
}
|
|
617
|
+
if (col.locked) {
|
|
618
|
+
return { success: true, message: `Column "${field}" is already locked.` };
|
|
619
|
+
}
|
|
620
|
+
zone.run(() => {
|
|
621
|
+
col.locked = true;
|
|
622
|
+
grid.columnInfoService?.changeLocked([{ column: col, locked: true }]);
|
|
623
|
+
});
|
|
624
|
+
return { success: true, message: `Column "${field}" locked.` };
|
|
625
|
+
}
|
|
626
|
+
}));
|
|
627
|
+
}
|
|
628
|
+
if (enabled.has('unlock-column')) {
|
|
629
|
+
handles.push(mc.registerTool({
|
|
630
|
+
name: `${prefix}-unlock-column`,
|
|
631
|
+
description: `Unlock a frozen column in the ${label}.`,
|
|
632
|
+
inputSchema: {
|
|
633
|
+
type: 'object',
|
|
634
|
+
properties: {
|
|
635
|
+
field: { type: 'string', description: 'The field name of the column to unlock.' }
|
|
636
|
+
},
|
|
637
|
+
required: ['field']
|
|
638
|
+
},
|
|
639
|
+
execute: (args) => {
|
|
640
|
+
const field = args['field'];
|
|
641
|
+
const col = findColumn(grid, field);
|
|
642
|
+
if (!col) {
|
|
643
|
+
return { success: false, message: `Column "${field}" not found.` };
|
|
644
|
+
}
|
|
645
|
+
if (!col.locked) {
|
|
646
|
+
return { success: true, message: `Column "${field}" is already unlocked.` };
|
|
647
|
+
}
|
|
648
|
+
zone.run(() => {
|
|
649
|
+
col.locked = false;
|
|
650
|
+
grid.columnInfoService?.changeLocked([{ column: col, locked: false }]);
|
|
651
|
+
});
|
|
652
|
+
return { success: true, message: `Column "${field}" unlocked.` };
|
|
653
|
+
}
|
|
654
|
+
}));
|
|
655
|
+
}
|
|
656
|
+
if (enabled.has('reorder-column')) {
|
|
657
|
+
handles.push(mc.registerTool({
|
|
658
|
+
name: `${prefix}-reorder-column`,
|
|
659
|
+
description: `Move a column to a new position in the ${label}. Positions are 0-based.`,
|
|
660
|
+
inputSchema: {
|
|
661
|
+
type: 'object',
|
|
662
|
+
properties: {
|
|
663
|
+
field: { type: 'string', description: 'The field name of the column to move.' },
|
|
664
|
+
position: { type: 'number', description: 'The new 0-based position index.' }
|
|
665
|
+
},
|
|
666
|
+
required: ['field', 'position']
|
|
667
|
+
},
|
|
668
|
+
execute: (args) => {
|
|
669
|
+
const field = args['field'];
|
|
670
|
+
const position = args['position'];
|
|
671
|
+
const col = findColumn(grid, field);
|
|
672
|
+
const allCols = grid.columns?.toArray() || [];
|
|
673
|
+
if (!col) {
|
|
674
|
+
return { success: false, message: `Column "${field}" not found.` };
|
|
675
|
+
}
|
|
676
|
+
if (position < 0 || position >= allCols.length) {
|
|
677
|
+
return { success: false, message: `Position ${position} out of range (0–${allCols.length - 1}).` };
|
|
678
|
+
}
|
|
679
|
+
const sorted = [...allCols].sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0));
|
|
680
|
+
const currentVisualPos = sorted.indexOf(col);
|
|
681
|
+
if (currentVisualPos === position) {
|
|
682
|
+
return { success: true, message: `Column "${field}" is already at position ${position}.` };
|
|
683
|
+
}
|
|
684
|
+
zone.run(() => {
|
|
685
|
+
// before:true = "insert before target" (used when moving left)
|
|
686
|
+
// before:false = "insert after target" (used when moving right)
|
|
687
|
+
grid.reorderColumn(col, position, { before: position < currentVisualPos });
|
|
688
|
+
});
|
|
689
|
+
return { success: true, message: `Column "${field}" moved to position ${position}.` };
|
|
690
|
+
}
|
|
691
|
+
}));
|
|
692
|
+
}
|
|
693
|
+
if (enabled.has('resize-column')) {
|
|
694
|
+
handles.push(mc.registerTool({
|
|
695
|
+
name: `${prefix}-resize-column`,
|
|
696
|
+
description: `Resize a column in the ${label} to a specified width in pixels.`,
|
|
697
|
+
inputSchema: {
|
|
698
|
+
type: 'object',
|
|
699
|
+
properties: {
|
|
700
|
+
field: { type: 'string', description: 'The field name of the column to resize.' },
|
|
701
|
+
width: { type: 'number', description: 'The new width in pixels.' }
|
|
702
|
+
},
|
|
703
|
+
required: ['field', 'width']
|
|
704
|
+
},
|
|
705
|
+
execute: (args) => {
|
|
706
|
+
const field = args['field'];
|
|
707
|
+
const width = args['width'];
|
|
708
|
+
const col = findColumn(grid, field);
|
|
709
|
+
if (!col) {
|
|
710
|
+
return { success: false, message: `Column "${field}" not found.` };
|
|
711
|
+
}
|
|
712
|
+
if (col.resizable === false) {
|
|
713
|
+
return { success: false, message: `Column "${field}" is not resizable.` };
|
|
714
|
+
}
|
|
715
|
+
if (width < 1) {
|
|
716
|
+
return { success: false, message: 'Width must be a positive number.' };
|
|
717
|
+
}
|
|
718
|
+
const oldWidth = col.width;
|
|
719
|
+
zone.run(() => {
|
|
720
|
+
col.width = width;
|
|
721
|
+
grid.columnResize.emit([{ column: col, newWidth: width, oldWidth }]);
|
|
722
|
+
});
|
|
723
|
+
return { success: true, message: `Column "${field}" resized to ${width}px.` };
|
|
724
|
+
}
|
|
725
|
+
}));
|
|
726
|
+
}
|
|
727
|
+
return handles;
|
|
728
|
+
}
|
|
729
|
+
// Select All
|
|
730
|
+
registerSelectAllTool(grid, mc, zone, prefix, label, enabled) {
|
|
731
|
+
if (!enabled.has('select-all')) {
|
|
732
|
+
return [];
|
|
733
|
+
}
|
|
734
|
+
return [mc.registerTool({
|
|
735
|
+
name: `${prefix}-select-all`,
|
|
736
|
+
description: `Select all rows in the ${label}.`,
|
|
737
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
738
|
+
execute: () => {
|
|
739
|
+
const data = Array.isArray(grid.data) ? grid.data : (grid.data?.data ?? []);
|
|
740
|
+
const indices = data.map((_, i) => i);
|
|
741
|
+
zone.run(() => {
|
|
742
|
+
grid.selectionChange.emit({
|
|
743
|
+
selectedRows: indices.map((i) => ({ dataItem: data[i], index: i })),
|
|
744
|
+
deselectedRows: [],
|
|
745
|
+
ctrlKey: false,
|
|
746
|
+
shiftKey: false
|
|
747
|
+
});
|
|
748
|
+
});
|
|
749
|
+
return { success: true, message: `Selected all ${indices.length} row(s).` };
|
|
750
|
+
}
|
|
751
|
+
})];
|
|
752
|
+
}
|
|
753
|
+
// Group Expand/Collapse
|
|
754
|
+
registerGroupExpandTools(grid, mc, zone, prefix, label, enabled) {
|
|
755
|
+
const handles = [];
|
|
756
|
+
if (enabled.has('group-expand')) {
|
|
757
|
+
handles.push(mc.registerTool({
|
|
758
|
+
name: `${prefix}-group-expand`,
|
|
759
|
+
description: `Expand a specific group in the ${label} by group index.`,
|
|
760
|
+
inputSchema: {
|
|
761
|
+
type: 'object',
|
|
762
|
+
properties: {
|
|
763
|
+
index: { type: 'string', description: 'The group index path (e.g. "0" or "0_1" for nested groups).' }
|
|
764
|
+
},
|
|
765
|
+
required: ['index']
|
|
766
|
+
},
|
|
767
|
+
execute: (args) => {
|
|
768
|
+
const index = args['index'];
|
|
769
|
+
zone.run(() => {
|
|
770
|
+
grid.expandGroup(index);
|
|
771
|
+
grid.groupExpand.emit({ group: { index } });
|
|
772
|
+
});
|
|
773
|
+
return { success: true, message: `Group "${index}" expanded.` };
|
|
774
|
+
}
|
|
775
|
+
}));
|
|
776
|
+
}
|
|
777
|
+
if (enabled.has('group-collapse')) {
|
|
778
|
+
handles.push(mc.registerTool({
|
|
779
|
+
name: `${prefix}-group-collapse`,
|
|
780
|
+
description: `Collapse a specific group in the ${label} by group index.`,
|
|
781
|
+
inputSchema: {
|
|
782
|
+
type: 'object',
|
|
783
|
+
properties: {
|
|
784
|
+
index: { type: 'string', description: 'The group index path (e.g. "0" or "0_1" for nested groups).' }
|
|
785
|
+
},
|
|
786
|
+
required: ['index']
|
|
787
|
+
},
|
|
788
|
+
execute: (args) => {
|
|
789
|
+
const index = args['index'];
|
|
790
|
+
zone.run(() => {
|
|
791
|
+
grid.collapseGroup(index);
|
|
792
|
+
grid.groupCollapse.emit({ group: { index } });
|
|
793
|
+
});
|
|
794
|
+
return { success: true, message: `Group "${index}" collapsed.` };
|
|
795
|
+
}
|
|
796
|
+
}));
|
|
797
|
+
}
|
|
798
|
+
if (enabled.has('group-expand-all')) {
|
|
799
|
+
handles.push(mc.registerTool({
|
|
800
|
+
name: `${prefix}-group-expand-all`,
|
|
801
|
+
description: `Expand all groups in the ${label}.`,
|
|
802
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
803
|
+
execute: () => {
|
|
804
|
+
zone.run(() => {
|
|
805
|
+
grid.resetGroupsState();
|
|
806
|
+
});
|
|
807
|
+
return { success: true, message: 'All groups expanded.' };
|
|
808
|
+
}
|
|
809
|
+
}));
|
|
810
|
+
}
|
|
811
|
+
if (enabled.has('group-collapse-all')) {
|
|
812
|
+
handles.push(mc.registerTool({
|
|
813
|
+
name: `${prefix}-group-collapse-all`,
|
|
814
|
+
description: `Collapse all groups in the ${label}.`,
|
|
815
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
816
|
+
execute: () => {
|
|
817
|
+
const currentGroups = grid.group || [];
|
|
818
|
+
if (currentGroups.length === 0) {
|
|
819
|
+
return { success: true, message: 'No groups are configured.' };
|
|
820
|
+
}
|
|
821
|
+
// grid.data is GroupResult[] when grouping is active — each top-level item is a group
|
|
822
|
+
const topLevelGroups = Array.isArray(grid.data) ? grid.data : (grid.data?.data ?? []);
|
|
823
|
+
zone.run(() => {
|
|
824
|
+
for (let i = 0; i < topLevelGroups.length; i++) {
|
|
825
|
+
grid.collapseGroup(String(i));
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
return { success: true, message: 'All groups collapsed.' };
|
|
829
|
+
}
|
|
830
|
+
}));
|
|
831
|
+
}
|
|
832
|
+
return handles;
|
|
833
|
+
}
|
|
834
|
+
// Detail Expand/Collapse
|
|
835
|
+
registerDetailTools(grid, mc, zone, prefix, label, enabled) {
|
|
836
|
+
const handles = [];
|
|
837
|
+
if (enabled.has('detail-expand')) {
|
|
838
|
+
handles.push(mc.registerTool({
|
|
839
|
+
name: `${prefix}-detail-expand`,
|
|
840
|
+
description: `Expand the detail row at a specific index in the ${label}.`,
|
|
841
|
+
inputSchema: {
|
|
842
|
+
type: 'object',
|
|
843
|
+
properties: {
|
|
844
|
+
index: { type: 'number', description: '0-based row index to expand.' }
|
|
845
|
+
},
|
|
846
|
+
required: ['index']
|
|
847
|
+
},
|
|
848
|
+
execute: (args) => {
|
|
849
|
+
const index = args['index'];
|
|
850
|
+
zone.run(() => {
|
|
851
|
+
grid.expandRow(index);
|
|
852
|
+
grid.detailExpand.emit({ index });
|
|
853
|
+
});
|
|
854
|
+
return { success: true, message: `Detail row ${index} expanded.` };
|
|
855
|
+
}
|
|
856
|
+
}));
|
|
857
|
+
}
|
|
858
|
+
if (enabled.has('detail-collapse')) {
|
|
859
|
+
handles.push(mc.registerTool({
|
|
860
|
+
name: `${prefix}-detail-collapse`,
|
|
861
|
+
description: `Collapse the detail row at a specific index in the ${label}.`,
|
|
862
|
+
inputSchema: {
|
|
863
|
+
type: 'object',
|
|
864
|
+
properties: {
|
|
865
|
+
index: { type: 'number', description: '0-based row index to collapse.' }
|
|
866
|
+
},
|
|
867
|
+
required: ['index']
|
|
868
|
+
},
|
|
869
|
+
execute: (args) => {
|
|
870
|
+
const index = args['index'];
|
|
871
|
+
zone.run(() => {
|
|
872
|
+
grid.collapseRow(index);
|
|
873
|
+
grid.detailCollapse.emit({ index });
|
|
874
|
+
});
|
|
875
|
+
return { success: true, message: `Detail row ${index} collapsed.` };
|
|
876
|
+
}
|
|
877
|
+
}));
|
|
878
|
+
}
|
|
879
|
+
if (enabled.has('detail-expand-all')) {
|
|
880
|
+
handles.push(mc.registerTool({
|
|
881
|
+
name: `${prefix}-detail-expand-all`,
|
|
882
|
+
description: `Expand all detail rows in the ${label}.`,
|
|
883
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
884
|
+
execute: () => {
|
|
885
|
+
const data = Array.isArray(grid.data) ? grid.data : (grid.data?.data ?? []);
|
|
886
|
+
zone.run(() => {
|
|
887
|
+
for (let i = 0; i < data.length; i++) {
|
|
888
|
+
grid.expandRow(i);
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
return { success: true, message: `Expanded all ${data.length} detail row(s).` };
|
|
892
|
+
}
|
|
893
|
+
}));
|
|
894
|
+
}
|
|
895
|
+
if (enabled.has('detail-collapse-all')) {
|
|
896
|
+
handles.push(mc.registerTool({
|
|
897
|
+
name: `${prefix}-detail-collapse-all`,
|
|
898
|
+
description: `Collapse all detail rows in the ${label}.`,
|
|
899
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
900
|
+
execute: () => {
|
|
901
|
+
const data = Array.isArray(grid.data) ? grid.data : (grid.data?.data ?? []);
|
|
902
|
+
zone.run(() => {
|
|
903
|
+
for (let i = 0; i < data.length; i++) {
|
|
904
|
+
grid.collapseRow(i);
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
return { success: true, message: `Collapsed all ${data.length} detail row(s).` };
|
|
908
|
+
}
|
|
909
|
+
}));
|
|
910
|
+
}
|
|
911
|
+
return handles;
|
|
912
|
+
}
|
|
913
|
+
// Get Data
|
|
914
|
+
registerGetDataTool(grid, mc, zone, prefix, label, enabled) {
|
|
915
|
+
if (!enabled.has('get-data')) {
|
|
916
|
+
return [];
|
|
917
|
+
}
|
|
918
|
+
return [mc.registerTool({
|
|
919
|
+
name: `${prefix}-get-data`,
|
|
920
|
+
description: `Read the rows currently visible in the ${label} (after any active filter/sort/page) as JSON. Returns an array of objects with one entry per visible row. Each object contains the column fields and their values.`,
|
|
921
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
922
|
+
execute: () => {
|
|
923
|
+
const data = Array.isArray(grid.data) ? grid.data : (grid.data?.data ?? []);
|
|
924
|
+
const fields = getColumnFields(grid);
|
|
925
|
+
const rows = [];
|
|
926
|
+
const extractRows = (items) => {
|
|
927
|
+
for (const item of items) {
|
|
928
|
+
// Grouped data wraps items in { field, value, items } objects
|
|
929
|
+
if (item.items) {
|
|
930
|
+
extractRows(item.items);
|
|
931
|
+
}
|
|
932
|
+
else {
|
|
933
|
+
const row = {};
|
|
934
|
+
for (const f of fields) {
|
|
935
|
+
row[f] = item[f];
|
|
936
|
+
}
|
|
937
|
+
rows.push(row);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
extractRows(data);
|
|
942
|
+
return { success: true, message: `${rows.length} row(s) returned.`, data: rows };
|
|
943
|
+
}
|
|
944
|
+
})];
|
|
945
|
+
}
|
|
946
|
+
// Highlight
|
|
947
|
+
registerHighlightTools(grid, mc, zone, prefix, label, enabled) {
|
|
948
|
+
const highlightDirective = grid.ctx?.highlightDirective;
|
|
949
|
+
// Highlight tools require the kendoGridHighlight directive to be applied
|
|
950
|
+
if (!highlightDirective) {
|
|
951
|
+
return [];
|
|
952
|
+
}
|
|
953
|
+
const handles = [];
|
|
954
|
+
if (enabled.has('highlight')) {
|
|
955
|
+
const fields = getColumnFields(grid);
|
|
956
|
+
handles.push(mc.registerTool({
|
|
957
|
+
name: `${prefix}-highlight`,
|
|
958
|
+
description: `Visually highlight rows in the ${label} that match a condition using the built-in highlight mechanism.`,
|
|
959
|
+
inputSchema: {
|
|
960
|
+
type: 'object',
|
|
961
|
+
properties: {
|
|
962
|
+
field: {
|
|
963
|
+
type: 'string',
|
|
964
|
+
enum: fields.length ? fields : undefined,
|
|
965
|
+
description: 'The column field to evaluate.'
|
|
966
|
+
},
|
|
967
|
+
operator: {
|
|
968
|
+
type: 'string',
|
|
969
|
+
enum: ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'contains', 'startswith', 'endswith'],
|
|
970
|
+
description: 'Comparison operator.'
|
|
971
|
+
},
|
|
972
|
+
value: {
|
|
973
|
+
description: 'The value to compare against. Use a number for numeric fields.'
|
|
974
|
+
}
|
|
975
|
+
},
|
|
976
|
+
required: ['field', 'operator', 'value']
|
|
977
|
+
},
|
|
978
|
+
execute: (args) => {
|
|
979
|
+
const field = args['field'];
|
|
980
|
+
const op = args['operator'];
|
|
981
|
+
const value = args['value'];
|
|
982
|
+
const data = Array.isArray(grid.data) ? grid.data : (grid.data?.data ?? []);
|
|
983
|
+
const highlightItems = [];
|
|
984
|
+
for (let i = 0; i < data.length; i++) {
|
|
985
|
+
const item = data[i];
|
|
986
|
+
if (matchesHighlight(item[field], op, value)) {
|
|
987
|
+
const key = highlightDirective.highlightItemKey
|
|
988
|
+
? highlightDirective['getItemKey']({ data: item, index: i })
|
|
989
|
+
: i;
|
|
990
|
+
highlightItems.push({ itemKey: key });
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
zone.run(() => {
|
|
994
|
+
highlightDirective['setState'](highlightItems);
|
|
995
|
+
});
|
|
996
|
+
return { success: true, message: `${highlightItems.length} row(s) highlighted.` };
|
|
997
|
+
}
|
|
998
|
+
}));
|
|
999
|
+
}
|
|
1000
|
+
if (enabled.has('clear-highlight')) {
|
|
1001
|
+
handles.push(mc.registerTool({
|
|
1002
|
+
name: `${prefix}-clear-highlight`,
|
|
1003
|
+
description: `Remove all row highlights from the ${label}.`,
|
|
1004
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1005
|
+
execute: () => {
|
|
1006
|
+
zone.run(() => {
|
|
1007
|
+
highlightDirective['setState']([]);
|
|
1008
|
+
});
|
|
1009
|
+
return { success: true, message: 'Highlights cleared.' };
|
|
1010
|
+
}
|
|
1011
|
+
}));
|
|
1012
|
+
}
|
|
1013
|
+
return handles;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
function getTreeListData(treeList) {
|
|
1018
|
+
if (Array.isArray(treeList.data) && treeList.data.length) {
|
|
1019
|
+
return treeList.data;
|
|
1020
|
+
}
|
|
1021
|
+
const result = treeList.data?.data;
|
|
1022
|
+
if (Array.isArray(result) && result.length) {
|
|
1023
|
+
return result;
|
|
1024
|
+
}
|
|
1025
|
+
// When kendoTreeListFlatBinding is used, data lives in the binding directive
|
|
1026
|
+
const bindingData = treeList.localEditService?.bindingDirective?.originalData;
|
|
1027
|
+
if (Array.isArray(bindingData) && bindingData.length) {
|
|
1028
|
+
return bindingData;
|
|
1029
|
+
}
|
|
1030
|
+
// Fallback to rendered view items
|
|
1031
|
+
try {
|
|
1032
|
+
return (treeList.view?.data || []).map((item) => item.data);
|
|
1033
|
+
}
|
|
1034
|
+
catch {
|
|
1035
|
+
return [];
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* @hidden
|
|
1040
|
+
*
|
|
1041
|
+
* Web MCP tool adapter for the Kendo Angular TreeList component.
|
|
1042
|
+
* Supports: sort, filter, select, expand/collapse, export.
|
|
1043
|
+
*/
|
|
1044
|
+
class TreeListToolAdapter {
|
|
1045
|
+
selector = 'kendo-treelist';
|
|
1046
|
+
registerTools(treeList, config, modelContext, ngZone) {
|
|
1047
|
+
const handles = [];
|
|
1048
|
+
const prefix = config.dataName || 'kendo-treelist';
|
|
1049
|
+
const label = config.dataName
|
|
1050
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' tree list'
|
|
1051
|
+
: 'Kendo UI TreeList';
|
|
1052
|
+
const rawOptions = this.buildToolOptions(treeList, config, label);
|
|
1053
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
1054
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
1055
|
+
handles.push(...this.registerSortTools(treeList, modelContext, ngZone, prefix, label, enabled));
|
|
1056
|
+
handles.push(...this.registerFilterTools(treeList, modelContext, ngZone, prefix, label, enabled));
|
|
1057
|
+
handles.push(...this.registerSelectionTools(treeList, modelContext, ngZone, prefix, label, enabled));
|
|
1058
|
+
handles.push(...this.registerExportTools(treeList, modelContext, ngZone, prefix, label, enabled));
|
|
1059
|
+
handles.push(...this.registerExpandTools(treeList, modelContext, ngZone, prefix, label, enabled));
|
|
1060
|
+
return handles;
|
|
1061
|
+
}
|
|
1062
|
+
buildToolOptions(treeList, config, label) {
|
|
1063
|
+
return [
|
|
1064
|
+
{ name: 'sort-column', description: `Sort the ${label} by a column field and direction.`, enabled: !!treeList.sortable },
|
|
1065
|
+
{ name: 'clear-sort', description: `Remove all sorting from the ${label}.`, enabled: !!treeList.sortable },
|
|
1066
|
+
{ name: 'filter', description: `Apply a filter to a column in the ${label}.`, enabled: !!treeList.filterable },
|
|
1067
|
+
{ name: 'clear-filters', description: `Remove all filters from the ${label}.`, enabled: !!treeList.filterable },
|
|
1068
|
+
{ name: 'select-rows', description: `Select one or more rows in the ${label} by index.`, enabled: !!treeList.selectable },
|
|
1069
|
+
{ name: 'clear-selection', description: `Clear all row selections in the ${label}.`, enabled: !!treeList.selectable },
|
|
1070
|
+
{ name: 'export-pdf', description: `Export the ${label} to PDF.`, enabled: true },
|
|
1071
|
+
{ name: 'export-excel', description: `Export the ${label} to Excel.`, enabled: true },
|
|
1072
|
+
{ name: 'expand', description: `Expand a row in the ${label}.`, enabled: true },
|
|
1073
|
+
{ name: 'collapse', description: `Collapse a row in the ${label}.`, enabled: true },
|
|
1074
|
+
{ name: 'expand-all', description: `Expand all rows in the ${label}.`, enabled: true },
|
|
1075
|
+
{ name: 'collapse-all', description: `Collapse all rows in the ${label}.`, enabled: true }
|
|
1076
|
+
];
|
|
1077
|
+
}
|
|
1078
|
+
// Sort
|
|
1079
|
+
registerSortTools(treeList, mc, zone, prefix, label, enabled) {
|
|
1080
|
+
const handles = [];
|
|
1081
|
+
if (enabled.has('sort-column')) {
|
|
1082
|
+
handles.push(mc.registerTool({
|
|
1083
|
+
name: `${prefix}-sort-column`,
|
|
1084
|
+
description: `Sort the ${label} by a column field and direction.`,
|
|
1085
|
+
inputSchema: {
|
|
1086
|
+
type: 'object',
|
|
1087
|
+
properties: {
|
|
1088
|
+
field: { type: 'string', description: 'Column field name to sort by.' },
|
|
1089
|
+
dir: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction.' }
|
|
1090
|
+
},
|
|
1091
|
+
required: ['field', 'dir']
|
|
1092
|
+
},
|
|
1093
|
+
execute: (args) => {
|
|
1094
|
+
const field = args['field'];
|
|
1095
|
+
const dir = args['dir'];
|
|
1096
|
+
const descriptor = [{ field, dir }];
|
|
1097
|
+
zone.run(() => {
|
|
1098
|
+
treeList.sort = descriptor;
|
|
1099
|
+
treeList.sortChange.emit(descriptor);
|
|
1100
|
+
});
|
|
1101
|
+
return { success: true, message: `Sorted by "${field}" ${dir}.` };
|
|
1102
|
+
}
|
|
1103
|
+
}));
|
|
1104
|
+
}
|
|
1105
|
+
if (enabled.has('clear-sort')) {
|
|
1106
|
+
handles.push(mc.registerTool({
|
|
1107
|
+
name: `${prefix}-clear-sort`,
|
|
1108
|
+
description: `Remove all sorting from the ${label}.`,
|
|
1109
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1110
|
+
execute: () => {
|
|
1111
|
+
zone.run(() => {
|
|
1112
|
+
treeList.sort = [];
|
|
1113
|
+
treeList.sortChange.emit([]);
|
|
1114
|
+
});
|
|
1115
|
+
return { success: true, message: 'Sort cleared.' };
|
|
1116
|
+
}
|
|
1117
|
+
}));
|
|
1118
|
+
}
|
|
1119
|
+
return handles;
|
|
1120
|
+
}
|
|
1121
|
+
// Filter
|
|
1122
|
+
registerFilterTools(treeList, mc, zone, prefix, label, enabled) {
|
|
1123
|
+
const handles = [];
|
|
1124
|
+
if (enabled.has('filter')) {
|
|
1125
|
+
handles.push(mc.registerTool({
|
|
1126
|
+
name: `${prefix}-filter`,
|
|
1127
|
+
description: `Apply a filter to a column in the ${label}.`,
|
|
1128
|
+
inputSchema: {
|
|
1129
|
+
type: 'object',
|
|
1130
|
+
properties: {
|
|
1131
|
+
field: { type: 'string', description: 'Column field name to filter.' },
|
|
1132
|
+
operator: { type: 'string', description: 'Filter operator (eq, contains, startswith, etc.).' },
|
|
1133
|
+
value: { type: 'string', description: 'Filter value.' }
|
|
1134
|
+
},
|
|
1135
|
+
required: ['field', 'operator', 'value']
|
|
1136
|
+
},
|
|
1137
|
+
execute: (args) => {
|
|
1138
|
+
const { field, operator, value } = args;
|
|
1139
|
+
const descriptor = { logic: 'and', filters: [{ field, operator, value }] };
|
|
1140
|
+
zone.run(() => {
|
|
1141
|
+
treeList.filter = descriptor;
|
|
1142
|
+
treeList.filterChange.emit(descriptor);
|
|
1143
|
+
});
|
|
1144
|
+
return { success: true, message: `Filtered "${field}" ${operator} "${value}".` };
|
|
1145
|
+
}
|
|
1146
|
+
}));
|
|
1147
|
+
}
|
|
1148
|
+
if (enabled.has('clear-filters')) {
|
|
1149
|
+
handles.push(mc.registerTool({
|
|
1150
|
+
name: `${prefix}-clear-filters`,
|
|
1151
|
+
description: `Remove all filters from the ${label}.`,
|
|
1152
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1153
|
+
execute: () => {
|
|
1154
|
+
const descriptor = { logic: 'and', filters: [] };
|
|
1155
|
+
zone.run(() => {
|
|
1156
|
+
treeList.filter = descriptor;
|
|
1157
|
+
treeList.filterChange.emit(descriptor);
|
|
1158
|
+
});
|
|
1159
|
+
return { success: true, message: 'Filters cleared.' };
|
|
1160
|
+
}
|
|
1161
|
+
}));
|
|
1162
|
+
}
|
|
1163
|
+
return handles;
|
|
1164
|
+
}
|
|
1165
|
+
// Selection
|
|
1166
|
+
registerSelectionTools(treeList, mc, zone, prefix, label, enabled) {
|
|
1167
|
+
const handles = [];
|
|
1168
|
+
if (enabled.has('select-rows')) {
|
|
1169
|
+
handles.push(mc.registerTool({
|
|
1170
|
+
name: `${prefix}-select-rows`,
|
|
1171
|
+
description: `Select one or more rows in the ${label} by index.`,
|
|
1172
|
+
inputSchema: {
|
|
1173
|
+
type: 'object',
|
|
1174
|
+
properties: {
|
|
1175
|
+
indices: { type: 'array', items: { type: 'number' }, description: '0-based row indices to select.' }
|
|
1176
|
+
},
|
|
1177
|
+
required: ['indices']
|
|
1178
|
+
},
|
|
1179
|
+
execute: (args) => {
|
|
1180
|
+
const indices = args['indices'];
|
|
1181
|
+
zone.run(() => {
|
|
1182
|
+
treeList.selectionChange.emit({
|
|
1183
|
+
selectedRows: indices.map(i => ({ dataItem: null, index: i })),
|
|
1184
|
+
deselectedRows: [],
|
|
1185
|
+
ctrlKey: false,
|
|
1186
|
+
shiftKey: false
|
|
1187
|
+
});
|
|
1188
|
+
});
|
|
1189
|
+
return { success: true, message: `Selected ${indices.length} row(s).` };
|
|
1190
|
+
}
|
|
1191
|
+
}));
|
|
1192
|
+
}
|
|
1193
|
+
if (enabled.has('clear-selection')) {
|
|
1194
|
+
handles.push(mc.registerTool({
|
|
1195
|
+
name: `${prefix}-clear-selection`,
|
|
1196
|
+
description: `Clear all row selections in the ${label}.`,
|
|
1197
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1198
|
+
execute: () => {
|
|
1199
|
+
const data = getTreeListData(treeList);
|
|
1200
|
+
zone.run(() => {
|
|
1201
|
+
treeList.selectionChange.emit({
|
|
1202
|
+
selectedRows: [],
|
|
1203
|
+
deselectedRows: data.map((item, i) => ({ dataItem: item, index: i })),
|
|
1204
|
+
ctrlKey: false,
|
|
1205
|
+
shiftKey: false
|
|
1206
|
+
});
|
|
1207
|
+
});
|
|
1208
|
+
return { success: true, message: 'Selection cleared.' };
|
|
1209
|
+
}
|
|
1210
|
+
}));
|
|
1211
|
+
}
|
|
1212
|
+
return handles;
|
|
1213
|
+
}
|
|
1214
|
+
// Export
|
|
1215
|
+
registerExportTools(treeList, mc, zone, prefix, label, enabled) {
|
|
1216
|
+
const handles = [];
|
|
1217
|
+
if (enabled.has('export-pdf') && typeof treeList.saveAsPDF === 'function') {
|
|
1218
|
+
handles.push(mc.registerTool({
|
|
1219
|
+
name: `${prefix}-export-pdf`,
|
|
1220
|
+
description: `Export the ${label} to PDF.`,
|
|
1221
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1222
|
+
execute: () => {
|
|
1223
|
+
zone.run(() => treeList.saveAsPDF());
|
|
1224
|
+
return { success: true, message: 'PDF export started.' };
|
|
1225
|
+
}
|
|
1226
|
+
}));
|
|
1227
|
+
}
|
|
1228
|
+
if (enabled.has('export-excel') && typeof treeList.saveAsExcel === 'function') {
|
|
1229
|
+
handles.push(mc.registerTool({
|
|
1230
|
+
name: `${prefix}-export-excel`,
|
|
1231
|
+
description: `Export the ${label} to Excel.`,
|
|
1232
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1233
|
+
execute: () => {
|
|
1234
|
+
zone.run(() => treeList.saveAsExcel());
|
|
1235
|
+
return { success: true, message: 'Excel export started.' };
|
|
1236
|
+
}
|
|
1237
|
+
}));
|
|
1238
|
+
}
|
|
1239
|
+
return handles;
|
|
1240
|
+
}
|
|
1241
|
+
// Expand / Collapse
|
|
1242
|
+
registerExpandTools(treeList, mc, zone, prefix, label, enabled) {
|
|
1243
|
+
const handles = [];
|
|
1244
|
+
if (enabled.has('expand')) {
|
|
1245
|
+
handles.push(mc.registerTool({
|
|
1246
|
+
name: `${prefix}-expand`,
|
|
1247
|
+
description: `Expand a row in the ${label}.`,
|
|
1248
|
+
inputSchema: {
|
|
1249
|
+
type: 'object',
|
|
1250
|
+
properties: {
|
|
1251
|
+
id: { type: 'number', description: 'Row id to expand.' }
|
|
1252
|
+
},
|
|
1253
|
+
required: ['id']
|
|
1254
|
+
},
|
|
1255
|
+
execute: (args) => {
|
|
1256
|
+
const data = getTreeListData(treeList);
|
|
1257
|
+
const item = data.find((d) => d.id === args['id']);
|
|
1258
|
+
if (!item) {
|
|
1259
|
+
return { success: false, message: `Row with id ${args['id']} not found.` };
|
|
1260
|
+
}
|
|
1261
|
+
zone.run(() => {
|
|
1262
|
+
treeList.expand(item);
|
|
1263
|
+
});
|
|
1264
|
+
return { success: true, message: `Row ${args['id']} expanded.` };
|
|
1265
|
+
}
|
|
1266
|
+
}));
|
|
1267
|
+
}
|
|
1268
|
+
if (enabled.has('collapse')) {
|
|
1269
|
+
handles.push(mc.registerTool({
|
|
1270
|
+
name: `${prefix}-collapse`,
|
|
1271
|
+
description: `Collapse a row in the ${label}.`,
|
|
1272
|
+
inputSchema: {
|
|
1273
|
+
type: 'object',
|
|
1274
|
+
properties: {
|
|
1275
|
+
id: { type: 'number', description: 'Row id to collapse.' }
|
|
1276
|
+
},
|
|
1277
|
+
required: ['id']
|
|
1278
|
+
},
|
|
1279
|
+
execute: (args) => {
|
|
1280
|
+
const data = getTreeListData(treeList);
|
|
1281
|
+
const item = data.find((d) => d.id === args['id']);
|
|
1282
|
+
if (!item) {
|
|
1283
|
+
return { success: false, message: `Row with id ${args['id']} not found.` };
|
|
1284
|
+
}
|
|
1285
|
+
zone.run(() => {
|
|
1286
|
+
treeList.collapse(item);
|
|
1287
|
+
});
|
|
1288
|
+
return { success: true, message: `Row ${args['id']} collapsed.` };
|
|
1289
|
+
}
|
|
1290
|
+
}));
|
|
1291
|
+
}
|
|
1292
|
+
if (enabled.has('expand-all')) {
|
|
1293
|
+
handles.push(mc.registerTool({
|
|
1294
|
+
name: `${prefix}-expand-all`,
|
|
1295
|
+
description: `Expand all rows in the ${label}.`,
|
|
1296
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1297
|
+
execute: () => {
|
|
1298
|
+
const data = getTreeListData(treeList);
|
|
1299
|
+
zone.run(() => {
|
|
1300
|
+
for (const item of data) {
|
|
1301
|
+
treeList.expand(item);
|
|
1302
|
+
}
|
|
1303
|
+
});
|
|
1304
|
+
return { success: true, message: `Expanded all ${data.length} row(s).` };
|
|
1305
|
+
}
|
|
1306
|
+
}));
|
|
1307
|
+
}
|
|
1308
|
+
if (enabled.has('collapse-all')) {
|
|
1309
|
+
handles.push(mc.registerTool({
|
|
1310
|
+
name: `${prefix}-collapse-all`,
|
|
1311
|
+
description: `Collapse all rows in the ${label}.`,
|
|
1312
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1313
|
+
execute: () => {
|
|
1314
|
+
const data = getTreeListData(treeList);
|
|
1315
|
+
zone.run(() => {
|
|
1316
|
+
for (const item of data) {
|
|
1317
|
+
treeList.collapse(item);
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
return { success: true, message: `Collapsed all ${data.length} row(s).` };
|
|
1321
|
+
}
|
|
1322
|
+
}));
|
|
1323
|
+
}
|
|
1324
|
+
return handles;
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/**
|
|
1329
|
+
* @hidden
|
|
1330
|
+
*
|
|
1331
|
+
* Web MCP tool adapter for the Kendo Angular Scheduler component.
|
|
1332
|
+
* Supports: create, update, delete events, set view, navigate.
|
|
1333
|
+
*/
|
|
1334
|
+
class SchedulerToolAdapter {
|
|
1335
|
+
selector = 'kendo-scheduler';
|
|
1336
|
+
registerTools(scheduler, config, modelContext, ngZone) {
|
|
1337
|
+
const handles = [];
|
|
1338
|
+
const prefix = config.dataName || 'kendo-scheduler';
|
|
1339
|
+
const label = config.dataName
|
|
1340
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' scheduler'
|
|
1341
|
+
: 'Kendo UI Scheduler';
|
|
1342
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
1343
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
1344
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
1345
|
+
handles.push(...this.registerEventTools(scheduler, modelContext, ngZone, prefix, label, enabled));
|
|
1346
|
+
handles.push(...this.registerNavigationTools(scheduler, modelContext, ngZone, prefix, label, enabled));
|
|
1347
|
+
handles.push(...this.registerExportTools(scheduler, modelContext, ngZone, prefix, label, enabled));
|
|
1348
|
+
return handles;
|
|
1349
|
+
}
|
|
1350
|
+
buildToolOptions(config, label) {
|
|
1351
|
+
return [
|
|
1352
|
+
{ name: 'create-event', description: `Create a new event in the ${label}.`, enabled: true },
|
|
1353
|
+
{ name: 'update-event', description: `Update an existing event in the ${label}.`, enabled: true },
|
|
1354
|
+
{ name: 'delete-event', description: `Delete an event from the ${label}.`, enabled: true },
|
|
1355
|
+
{ name: 'set-view', description: `Change the view of the ${label}.`, enabled: true },
|
|
1356
|
+
{ name: 'navigate', description: `Navigate to a specific date in the ${label}.`, enabled: true },
|
|
1357
|
+
{ name: 'export-pdf', description: `Export the ${label} to PDF.`, enabled: true }
|
|
1358
|
+
];
|
|
1359
|
+
}
|
|
1360
|
+
// Event CRUD
|
|
1361
|
+
registerEventTools(scheduler, mc, zone, prefix, label, enabled) {
|
|
1362
|
+
const handles = [];
|
|
1363
|
+
if (enabled.has('create-event')) {
|
|
1364
|
+
handles.push(mc.registerTool({
|
|
1365
|
+
name: `${prefix}-create-event`,
|
|
1366
|
+
description: `Create a new event in the ${label}.`,
|
|
1367
|
+
inputSchema: {
|
|
1368
|
+
type: 'object',
|
|
1369
|
+
properties: {
|
|
1370
|
+
title: { type: 'string', description: 'Event title.' },
|
|
1371
|
+
start: { type: 'string', description: 'Start date/time (ISO 8601).' },
|
|
1372
|
+
end: { type: 'string', description: 'End date/time (ISO 8601).' },
|
|
1373
|
+
description: { type: 'string', description: 'Event description (optional).' }
|
|
1374
|
+
},
|
|
1375
|
+
required: ['title', 'start', 'end']
|
|
1376
|
+
},
|
|
1377
|
+
execute: (args) => {
|
|
1378
|
+
const event = {
|
|
1379
|
+
title: args['title'],
|
|
1380
|
+
start: new Date(args['start']),
|
|
1381
|
+
end: new Date(args['end']),
|
|
1382
|
+
description: args['description'] || ''
|
|
1383
|
+
};
|
|
1384
|
+
zone.run(() => {
|
|
1385
|
+
scheduler.save?.emit({ dataItem: event, isNew: true });
|
|
1386
|
+
});
|
|
1387
|
+
const descPart = event.description ? ', Description: "' + event.description + '"' : '';
|
|
1388
|
+
return { success: true, message: 'Event created. Title: "' + event.title + '", Start: ' + args['start'] + ', End: ' + args['end'] + descPart + '.' };
|
|
1389
|
+
}
|
|
1390
|
+
}));
|
|
1391
|
+
}
|
|
1392
|
+
if (enabled.has('update-event')) {
|
|
1393
|
+
handles.push(mc.registerTool({
|
|
1394
|
+
name: `${prefix}-update-event`,
|
|
1395
|
+
description: `Update an existing event in the ${label} by title.`,
|
|
1396
|
+
inputSchema: {
|
|
1397
|
+
type: 'object',
|
|
1398
|
+
properties: {
|
|
1399
|
+
title: { type: 'string', description: 'Title of the event to update.' },
|
|
1400
|
+
start: { type: 'string', description: 'New start date/time (ISO 8601).' },
|
|
1401
|
+
end: { type: 'string', description: 'New end date/time (ISO 8601).' },
|
|
1402
|
+
newTitle: { type: 'string', description: 'New title for the event.' }
|
|
1403
|
+
},
|
|
1404
|
+
required: ['title']
|
|
1405
|
+
},
|
|
1406
|
+
execute: (args) => {
|
|
1407
|
+
const updates = { _originalTitle: args['title'] };
|
|
1408
|
+
if (args['start'])
|
|
1409
|
+
updates['start'] = new Date(args['start']);
|
|
1410
|
+
if (args['end'])
|
|
1411
|
+
updates['end'] = new Date(args['end']);
|
|
1412
|
+
if (args['newTitle'])
|
|
1413
|
+
updates['title'] = args['newTitle'];
|
|
1414
|
+
zone.run(() => {
|
|
1415
|
+
scheduler.save?.emit({
|
|
1416
|
+
dataItem: { title: args['title'], ...updates },
|
|
1417
|
+
isNew: false
|
|
1418
|
+
});
|
|
1419
|
+
});
|
|
1420
|
+
return { success: true, message: `Event "${args['title']}" updated.` };
|
|
1421
|
+
}
|
|
1422
|
+
}));
|
|
1423
|
+
}
|
|
1424
|
+
if (enabled.has('delete-event')) {
|
|
1425
|
+
handles.push(mc.registerTool({
|
|
1426
|
+
name: `${prefix}-delete-event`,
|
|
1427
|
+
description: `Delete an event from the ${label} by title.`,
|
|
1428
|
+
inputSchema: {
|
|
1429
|
+
type: 'object',
|
|
1430
|
+
properties: {
|
|
1431
|
+
title: { type: 'string', description: 'Title of the event to delete.' }
|
|
1432
|
+
},
|
|
1433
|
+
required: ['title']
|
|
1434
|
+
},
|
|
1435
|
+
execute: (args) => {
|
|
1436
|
+
zone.run(() => {
|
|
1437
|
+
scheduler.remove?.emit({
|
|
1438
|
+
dataItem: { title: args['title'] }
|
|
1439
|
+
});
|
|
1440
|
+
});
|
|
1441
|
+
return { success: true, message: `Event "${args['title']}" deleted.` };
|
|
1442
|
+
}
|
|
1443
|
+
}));
|
|
1444
|
+
}
|
|
1445
|
+
return handles;
|
|
1446
|
+
}
|
|
1447
|
+
// Navigation
|
|
1448
|
+
registerNavigationTools(scheduler, mc, zone, prefix, label, enabled) {
|
|
1449
|
+
const handles = [];
|
|
1450
|
+
if (enabled.has('set-view')) {
|
|
1451
|
+
handles.push(mc.registerTool({
|
|
1452
|
+
name: `${prefix}-set-view`,
|
|
1453
|
+
description: `Change the view of the ${label} (e.g. day, week, month).`,
|
|
1454
|
+
inputSchema: {
|
|
1455
|
+
type: 'object',
|
|
1456
|
+
properties: {
|
|
1457
|
+
view: { type: 'string', description: 'View name: day, week, month, timeline, agenda.' }
|
|
1458
|
+
},
|
|
1459
|
+
required: ['view']
|
|
1460
|
+
},
|
|
1461
|
+
execute: (args) => {
|
|
1462
|
+
const view = (args['view'] || '').toLowerCase();
|
|
1463
|
+
const viewToIndex = { day: 0, week: 1, month: 2, timeline: 3, agenda: 4 };
|
|
1464
|
+
const index = viewToIndex[view] ?? 1;
|
|
1465
|
+
zone.run(() => {
|
|
1466
|
+
scheduler.selectedViewIndex = index;
|
|
1467
|
+
scheduler.navigate?.emit({ index, view: args['view'] });
|
|
1468
|
+
});
|
|
1469
|
+
return { success: true, message: `View set to "${args['view']}".` };
|
|
1470
|
+
}
|
|
1471
|
+
}));
|
|
1472
|
+
}
|
|
1473
|
+
if (enabled.has('navigate')) {
|
|
1474
|
+
handles.push(mc.registerTool({
|
|
1475
|
+
name: `${prefix}-navigate`,
|
|
1476
|
+
description: `Navigate to a specific date in the ${label}.`,
|
|
1477
|
+
inputSchema: {
|
|
1478
|
+
type: 'object',
|
|
1479
|
+
properties: {
|
|
1480
|
+
date: { type: 'string', description: 'Target date (ISO 8601).' }
|
|
1481
|
+
},
|
|
1482
|
+
required: ['date']
|
|
1483
|
+
},
|
|
1484
|
+
execute: (args) => {
|
|
1485
|
+
const date = new Date(args['date']);
|
|
1486
|
+
zone.run(() => {
|
|
1487
|
+
scheduler.selectedDate = date;
|
|
1488
|
+
scheduler.dateChange?.emit({
|
|
1489
|
+
selectedDate: date,
|
|
1490
|
+
dateRange: { start: date, end: date }
|
|
1491
|
+
});
|
|
1492
|
+
});
|
|
1493
|
+
return { success: true, message: `Navigated to ${date.toISOString().split('T')[0]}.` };
|
|
1494
|
+
}
|
|
1495
|
+
}));
|
|
1496
|
+
}
|
|
1497
|
+
return handles;
|
|
1498
|
+
}
|
|
1499
|
+
// Export
|
|
1500
|
+
registerExportTools(scheduler, mc, zone, prefix, label, enabled) {
|
|
1501
|
+
const handles = [];
|
|
1502
|
+
if (enabled.has('export-pdf') && typeof scheduler.saveAsPDF === 'function') {
|
|
1503
|
+
handles.push(mc.registerTool({
|
|
1504
|
+
name: `${prefix}-export-pdf`,
|
|
1505
|
+
description: `Export the ${label} to PDF.`,
|
|
1506
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1507
|
+
execute: () => {
|
|
1508
|
+
zone.run(() => scheduler.saveAsPDF());
|
|
1509
|
+
return { success: true, message: 'PDF export started.' };
|
|
1510
|
+
}
|
|
1511
|
+
}));
|
|
1512
|
+
}
|
|
1513
|
+
return handles;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
/**
|
|
1518
|
+
* @hidden
|
|
1519
|
+
*
|
|
1520
|
+
* Web MCP tool adapter for the Kendo Angular Chart component.
|
|
1521
|
+
* Supports: drilldown, export.
|
|
1522
|
+
*/
|
|
1523
|
+
class ChartToolAdapter {
|
|
1524
|
+
selector = 'kendo-chart';
|
|
1525
|
+
registerTools(chart, config, modelContext, ngZone) {
|
|
1526
|
+
const handles = [];
|
|
1527
|
+
const prefix = config.dataName || 'kendo-chart';
|
|
1528
|
+
const label = config.dataName
|
|
1529
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' chart'
|
|
1530
|
+
: 'Kendo UI Chart';
|
|
1531
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
1532
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
1533
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
1534
|
+
if (enabled.has('drilldown')) {
|
|
1535
|
+
handles.push(modelContext.registerTool({
|
|
1536
|
+
name: `${prefix}-drilldown`,
|
|
1537
|
+
description: `Drill down into a specific category/series in the ${label}. Triggers the full chart drilldown pipeline.`,
|
|
1538
|
+
inputSchema: {
|
|
1539
|
+
type: 'object',
|
|
1540
|
+
properties: {
|
|
1541
|
+
value: { type: 'string', description: 'The drilldown field value (category) to drill into.' },
|
|
1542
|
+
series: { type: 'string', description: 'Series name to drill into. If omitted, uses the first drilldown-enabled series.' }
|
|
1543
|
+
},
|
|
1544
|
+
required: ['value']
|
|
1545
|
+
},
|
|
1546
|
+
execute: (args) => {
|
|
1547
|
+
let seriesName = args['series'];
|
|
1548
|
+
if (!seriesName) {
|
|
1549
|
+
const drilldownSeries = chart.seriesComponents?.find((sc) => sc.drilldownTemplate);
|
|
1550
|
+
seriesName = drilldownSeries?.name;
|
|
1551
|
+
}
|
|
1552
|
+
if (!seriesName) {
|
|
1553
|
+
return { success: false, message: 'No drilldown-enabled series found.' };
|
|
1554
|
+
}
|
|
1555
|
+
ngZone.run(() => {
|
|
1556
|
+
chart.trigger?.('drilldown', {
|
|
1557
|
+
value: args['value'],
|
|
1558
|
+
series: { name: seriesName },
|
|
1559
|
+
point: {}
|
|
1560
|
+
});
|
|
1561
|
+
});
|
|
1562
|
+
return { success: true, message: `Drilled down into "${args['value']}".` };
|
|
1563
|
+
}
|
|
1564
|
+
}));
|
|
1565
|
+
}
|
|
1566
|
+
if (enabled.has('export')) {
|
|
1567
|
+
handles.push(modelContext.registerTool({
|
|
1568
|
+
name: `${prefix}-export`,
|
|
1569
|
+
description: `Export the ${label} to an image format.`,
|
|
1570
|
+
inputSchema: {
|
|
1571
|
+
type: 'object',
|
|
1572
|
+
properties: {
|
|
1573
|
+
format: { type: 'string', enum: ['png', 'svg'], description: 'Export format.' }
|
|
1574
|
+
},
|
|
1575
|
+
required: ['format']
|
|
1576
|
+
},
|
|
1577
|
+
execute: (args) => {
|
|
1578
|
+
const format = args['format'];
|
|
1579
|
+
const fileName = `${prefix}.${format === 'svg' ? 'svg' : 'png'}`;
|
|
1580
|
+
ngZone.run(() => {
|
|
1581
|
+
const exportPromise = format === 'svg'
|
|
1582
|
+
? chart.exportSVG()
|
|
1583
|
+
: chart.exportImage();
|
|
1584
|
+
exportPromise.then((dataURI) => {
|
|
1585
|
+
saveAs(dataURI, fileName);
|
|
1586
|
+
});
|
|
1587
|
+
});
|
|
1588
|
+
return { success: true, message: `${format.toUpperCase()} export started.` };
|
|
1589
|
+
}
|
|
1590
|
+
}));
|
|
1591
|
+
}
|
|
1592
|
+
return handles;
|
|
1593
|
+
}
|
|
1594
|
+
buildToolOptions(config, label) {
|
|
1595
|
+
return [
|
|
1596
|
+
{
|
|
1597
|
+
name: 'drilldown',
|
|
1598
|
+
description: `Drill down into a specific category/series in the ${label}. Triggers the full chart drilldown pipeline.`,
|
|
1599
|
+
enabled: true
|
|
1600
|
+
},
|
|
1601
|
+
{ name: 'export', description: `Export the ${label} to an image format.`, enabled: true }
|
|
1602
|
+
];
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
/**
|
|
1607
|
+
* @hidden
|
|
1608
|
+
*
|
|
1609
|
+
* Web MCP tool adapter for the Kendo Angular Editor component.
|
|
1610
|
+
* Supports: get content, set content, export PDF.
|
|
1611
|
+
*/
|
|
1612
|
+
class EditorToolAdapter {
|
|
1613
|
+
selector = 'kendo-editor';
|
|
1614
|
+
registerTools(editor, config, modelContext, ngZone) {
|
|
1615
|
+
const handles = [];
|
|
1616
|
+
const prefix = config.dataName || 'kendo-editor';
|
|
1617
|
+
const label = config.dataName
|
|
1618
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' editor'
|
|
1619
|
+
: 'Kendo UI Editor';
|
|
1620
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
1621
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
1622
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
1623
|
+
if (enabled.has('get-content')) {
|
|
1624
|
+
handles.push(modelContext.registerTool({
|
|
1625
|
+
name: `${prefix}-get-content`,
|
|
1626
|
+
description: `Get the current HTML content of the ${label}.`,
|
|
1627
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1628
|
+
execute: () => {
|
|
1629
|
+
const value = editor.value;
|
|
1630
|
+
return { success: true, message: 'Content retrieved.', data: value };
|
|
1631
|
+
}
|
|
1632
|
+
}));
|
|
1633
|
+
}
|
|
1634
|
+
if (enabled.has('set-content')) {
|
|
1635
|
+
handles.push(modelContext.registerTool({
|
|
1636
|
+
name: `${prefix}-set-content`,
|
|
1637
|
+
description: `Set the HTML content of the ${label}.`,
|
|
1638
|
+
inputSchema: {
|
|
1639
|
+
type: 'object',
|
|
1640
|
+
properties: {
|
|
1641
|
+
html: { type: 'string', description: 'HTML content to set.' }
|
|
1642
|
+
},
|
|
1643
|
+
required: ['html']
|
|
1644
|
+
},
|
|
1645
|
+
execute: (args) => {
|
|
1646
|
+
const html = args['html'];
|
|
1647
|
+
ngZone.run(() => {
|
|
1648
|
+
editor.value = html;
|
|
1649
|
+
editor.valueChange.emit(html);
|
|
1650
|
+
});
|
|
1651
|
+
return { success: true, message: 'Content updated.' };
|
|
1652
|
+
}
|
|
1653
|
+
}));
|
|
1654
|
+
}
|
|
1655
|
+
if (enabled.has('insert-html')) {
|
|
1656
|
+
handles.push(modelContext.registerTool({
|
|
1657
|
+
name: `${prefix}-insert-html`,
|
|
1658
|
+
description: `Insert HTML at the end of the ${label} content.`,
|
|
1659
|
+
inputSchema: {
|
|
1660
|
+
type: 'object',
|
|
1661
|
+
properties: {
|
|
1662
|
+
html: { type: 'string', description: 'HTML markup to insert.' }
|
|
1663
|
+
},
|
|
1664
|
+
required: ['html']
|
|
1665
|
+
},
|
|
1666
|
+
execute: (args) => {
|
|
1667
|
+
const html = args['html'];
|
|
1668
|
+
ngZone.run(() => {
|
|
1669
|
+
const current = editor.value || '';
|
|
1670
|
+
const updated = current + html;
|
|
1671
|
+
editor.value = updated;
|
|
1672
|
+
editor.valueChange.emit(updated);
|
|
1673
|
+
});
|
|
1674
|
+
const preview = html.length > 100 ? html.substring(0, 100) + '...' : html;
|
|
1675
|
+
return { success: true, message: `HTML inserted (${html.length} chars). Content: "${preview}".` };
|
|
1676
|
+
}
|
|
1677
|
+
}));
|
|
1678
|
+
}
|
|
1679
|
+
if (enabled.has('clear-content')) {
|
|
1680
|
+
handles.push(modelContext.registerTool({
|
|
1681
|
+
name: `${prefix}-clear-content`,
|
|
1682
|
+
description: `Clear all content from the ${label}.`,
|
|
1683
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1684
|
+
execute: () => {
|
|
1685
|
+
ngZone.run(() => {
|
|
1686
|
+
editor.value = '';
|
|
1687
|
+
editor.valueChange.emit('');
|
|
1688
|
+
});
|
|
1689
|
+
return { success: true, message: 'Content cleared.' };
|
|
1690
|
+
}
|
|
1691
|
+
}));
|
|
1692
|
+
}
|
|
1693
|
+
if (enabled.has('export-pdf') && typeof editor.saveAsPDF === 'function') {
|
|
1694
|
+
handles.push(modelContext.registerTool({
|
|
1695
|
+
name: `${prefix}-export-pdf`,
|
|
1696
|
+
description: `Export the ${label} content to PDF.`,
|
|
1697
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1698
|
+
execute: () => {
|
|
1699
|
+
ngZone.run(() => editor.saveAsPDF());
|
|
1700
|
+
return { success: true, message: 'PDF export started.' };
|
|
1701
|
+
}
|
|
1702
|
+
}));
|
|
1703
|
+
}
|
|
1704
|
+
return handles;
|
|
1705
|
+
}
|
|
1706
|
+
buildToolOptions(config, label) {
|
|
1707
|
+
return [
|
|
1708
|
+
{ name: 'get-content', description: `Get the current HTML content of the ${label}.`, enabled: true },
|
|
1709
|
+
{ name: 'set-content', description: `Set the HTML content of the ${label}.`, enabled: true },
|
|
1710
|
+
{ name: 'insert-html', description: `Insert HTML at the end of the ${label} content.`, enabled: true },
|
|
1711
|
+
{ name: 'clear-content', description: `Clear all content from the ${label}.`, enabled: true },
|
|
1712
|
+
{ name: 'export-pdf', description: `Export the ${label} content to PDF.`, enabled: true }
|
|
1713
|
+
];
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
/**
|
|
1718
|
+
* @hidden
|
|
1719
|
+
*
|
|
1720
|
+
* Web MCP tool adapter for the Kendo Angular Spreadsheet component.
|
|
1721
|
+
* Supports: set cell, navigate sheet, add sheet, rename sheet, export.
|
|
1722
|
+
*/
|
|
1723
|
+
class SpreadsheetToolAdapter {
|
|
1724
|
+
selector = 'kendo-spreadsheet';
|
|
1725
|
+
registerTools(spreadsheet, config, modelContext, ngZone) {
|
|
1726
|
+
const handles = [];
|
|
1727
|
+
const prefix = config.dataName || 'kendo-spreadsheet';
|
|
1728
|
+
const label = config.dataName
|
|
1729
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' spreadsheet'
|
|
1730
|
+
: 'Kendo UI Spreadsheet';
|
|
1731
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
1732
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
1733
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
1734
|
+
if (enabled.has('set-cell')) {
|
|
1735
|
+
handles.push(modelContext.registerTool({
|
|
1736
|
+
name: `${prefix}-set-cell`,
|
|
1737
|
+
description: `Set a cell value in the ${label}.`,
|
|
1738
|
+
inputSchema: {
|
|
1739
|
+
type: 'object',
|
|
1740
|
+
properties: {
|
|
1741
|
+
cell: { type: 'string', description: 'Cell reference (e.g. "A1", "B3").' },
|
|
1742
|
+
value: { type: 'string', description: 'Value to set in the cell.' }
|
|
1743
|
+
},
|
|
1744
|
+
required: ['cell', 'value']
|
|
1745
|
+
},
|
|
1746
|
+
execute: (args) => {
|
|
1747
|
+
const cell = args['cell'];
|
|
1748
|
+
const value = args['value'];
|
|
1749
|
+
ngZone.run(() => {
|
|
1750
|
+
const instance = spreadsheet.spreadsheetWidget;
|
|
1751
|
+
if (instance) {
|
|
1752
|
+
const sheet = instance.activeSheet();
|
|
1753
|
+
sheet.range(cell).value(value);
|
|
1754
|
+
}
|
|
1755
|
+
});
|
|
1756
|
+
return { success: true, message: `Cell ${cell} set to "${value}".` };
|
|
1757
|
+
}
|
|
1758
|
+
}));
|
|
1759
|
+
}
|
|
1760
|
+
if (enabled.has('navigate-sheet')) {
|
|
1761
|
+
handles.push(modelContext.registerTool({
|
|
1762
|
+
name: `${prefix}-navigate-sheet`,
|
|
1763
|
+
description: `Switch to a specific sheet in the ${label} by name or index.`,
|
|
1764
|
+
inputSchema: {
|
|
1765
|
+
type: 'object',
|
|
1766
|
+
properties: {
|
|
1767
|
+
sheet: { type: 'string', description: 'Sheet name or 0-based index.' }
|
|
1768
|
+
},
|
|
1769
|
+
required: ['sheet']
|
|
1770
|
+
},
|
|
1771
|
+
execute: (args) => {
|
|
1772
|
+
const sheet = args['sheet'];
|
|
1773
|
+
let navigated = false;
|
|
1774
|
+
ngZone.run(() => {
|
|
1775
|
+
const instance = spreadsheet.spreadsheetWidget;
|
|
1776
|
+
if (instance) {
|
|
1777
|
+
const idx = Number.parseInt(sheet, 10);
|
|
1778
|
+
let targetName;
|
|
1779
|
+
if (Number.isNaN(idx)) {
|
|
1780
|
+
targetName = sheet;
|
|
1781
|
+
}
|
|
1782
|
+
else {
|
|
1783
|
+
const sheets = instance.sheets();
|
|
1784
|
+
if (sheets[idx]) {
|
|
1785
|
+
targetName = sheets[idx].name();
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
if (targetName) {
|
|
1789
|
+
const targetSheet = instance.sheets().find((s) => s.name() === targetName);
|
|
1790
|
+
if (targetSheet) {
|
|
1791
|
+
instance.activeSheet(targetSheet);
|
|
1792
|
+
instance.view?.sheetsbar?.onSheetSelect(targetName);
|
|
1793
|
+
navigated = true;
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
});
|
|
1798
|
+
return navigated
|
|
1799
|
+
? { success: true, message: `Navigated to sheet "${sheet}".` }
|
|
1800
|
+
: { success: false, message: `Sheet "${sheet}" not found.` };
|
|
1801
|
+
}
|
|
1802
|
+
}));
|
|
1803
|
+
}
|
|
1804
|
+
if (enabled.has('add-sheet')) {
|
|
1805
|
+
handles.push(modelContext.registerTool({
|
|
1806
|
+
name: `${prefix}-add-sheet`,
|
|
1807
|
+
description: `Add a new sheet to the ${label}.`,
|
|
1808
|
+
inputSchema: {
|
|
1809
|
+
type: 'object',
|
|
1810
|
+
properties: {
|
|
1811
|
+
name: { type: 'string', description: 'Optional name for the new sheet.' }
|
|
1812
|
+
},
|
|
1813
|
+
required: []
|
|
1814
|
+
},
|
|
1815
|
+
execute: (args) => {
|
|
1816
|
+
let sheetName = '';
|
|
1817
|
+
let sheetIndex = -1;
|
|
1818
|
+
ngZone.run(() => {
|
|
1819
|
+
const instance = spreadsheet.spreadsheetWidget;
|
|
1820
|
+
if (instance) {
|
|
1821
|
+
const sheetsbar = instance.view?.sheetsbar;
|
|
1822
|
+
if (args['name']) {
|
|
1823
|
+
instance.insertSheet({ data: { name: args['name'] } });
|
|
1824
|
+
}
|
|
1825
|
+
else if (sheetsbar) {
|
|
1826
|
+
sheetsbar.onAddSelect();
|
|
1827
|
+
}
|
|
1828
|
+
else {
|
|
1829
|
+
instance.insertSheet();
|
|
1830
|
+
}
|
|
1831
|
+
const sheets = instance.sheets();
|
|
1832
|
+
sheetIndex = sheets.length - 1;
|
|
1833
|
+
sheetName = sheets[sheetIndex]?.name() || 'Sheet' + (sheetIndex + 1);
|
|
1834
|
+
}
|
|
1835
|
+
});
|
|
1836
|
+
return { success: true, message: `Sheet added. Name: "${sheetName}", Index: ${sheetIndex}.` };
|
|
1837
|
+
}
|
|
1838
|
+
}));
|
|
1839
|
+
}
|
|
1840
|
+
if (enabled.has('rename-sheet')) {
|
|
1841
|
+
handles.push(modelContext.registerTool({
|
|
1842
|
+
name: `${prefix}-rename-sheet`,
|
|
1843
|
+
description: `Rename the active sheet in the ${label}.`,
|
|
1844
|
+
inputSchema: {
|
|
1845
|
+
type: 'object',
|
|
1846
|
+
properties: {
|
|
1847
|
+
name: { type: 'string', description: 'New name for the active sheet.' }
|
|
1848
|
+
},
|
|
1849
|
+
required: ['name']
|
|
1850
|
+
},
|
|
1851
|
+
execute: (args) => {
|
|
1852
|
+
const name = args['name'];
|
|
1853
|
+
let oldName = '';
|
|
1854
|
+
ngZone.run(() => {
|
|
1855
|
+
const instance = spreadsheet.spreadsheetWidget;
|
|
1856
|
+
if (instance) {
|
|
1857
|
+
const activeSheet = instance.activeSheet();
|
|
1858
|
+
oldName = activeSheet.name();
|
|
1859
|
+
const allSheets = instance.sheets();
|
|
1860
|
+
const sheetIndex = allSheets.findIndex((s) => s.name() === oldName);
|
|
1861
|
+
const sheetsbar = instance.view?.sheetsbar;
|
|
1862
|
+
if (sheetsbar && sheetIndex >= 0) {
|
|
1863
|
+
sheetsbar.onSheetRename(name, sheetIndex);
|
|
1864
|
+
}
|
|
1865
|
+
else {
|
|
1866
|
+
activeSheet.name(name);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
});
|
|
1870
|
+
return { success: true, message: `Sheet renamed from "${oldName}" to "${name}".` };
|
|
1871
|
+
}
|
|
1872
|
+
}));
|
|
1873
|
+
}
|
|
1874
|
+
if (enabled.has('export')) {
|
|
1875
|
+
handles.push(modelContext.registerTool({
|
|
1876
|
+
name: `${prefix}-export`,
|
|
1877
|
+
description: `Export the ${label} to a file.`,
|
|
1878
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
1879
|
+
execute: () => {
|
|
1880
|
+
ngZone.run(() => {
|
|
1881
|
+
const instance = spreadsheet.spreadsheetWidget;
|
|
1882
|
+
if (instance) {
|
|
1883
|
+
instance.saveAsExcel({
|
|
1884
|
+
...instance.options.excel,
|
|
1885
|
+
saveAs,
|
|
1886
|
+
Workbook
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
});
|
|
1890
|
+
return { success: true, message: 'Export started.' };
|
|
1891
|
+
}
|
|
1892
|
+
}));
|
|
1893
|
+
}
|
|
1894
|
+
return handles;
|
|
1895
|
+
}
|
|
1896
|
+
buildToolOptions(config, label) {
|
|
1897
|
+
return [
|
|
1898
|
+
{ name: 'set-cell', description: `Set a cell value in the ${label}.`, enabled: true },
|
|
1899
|
+
{ name: 'navigate-sheet', description: `Switch to a specific sheet in the ${label} by name or index.`, enabled: true },
|
|
1900
|
+
{ name: 'add-sheet', description: `Add a new sheet to the ${label}.`, enabled: true },
|
|
1901
|
+
{ name: 'rename-sheet', description: `Rename the active sheet in the ${label}.`, enabled: true },
|
|
1902
|
+
{ name: 'export', description: `Export the ${label} to a file.`, enabled: true }
|
|
1903
|
+
];
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
function getGanttData(gantt) {
|
|
1908
|
+
if (Array.isArray(gantt.data) && gantt.data.length) {
|
|
1909
|
+
return gantt.data;
|
|
1910
|
+
}
|
|
1911
|
+
// When kendoGanttFlatBinding is used, data lives in the binding directive's originalData
|
|
1912
|
+
const treeList = gantt.treeList;
|
|
1913
|
+
const bindingData = treeList?.localEditService?.bindingDirective?.originalData;
|
|
1914
|
+
if (Array.isArray(bindingData) && bindingData.length) {
|
|
1915
|
+
return bindingData;
|
|
1916
|
+
}
|
|
1917
|
+
// Fallback to rendered items (visible rows only)
|
|
1918
|
+
return gantt.renderedTreeListItems || [];
|
|
1919
|
+
}
|
|
1920
|
+
/**
|
|
1921
|
+
* @hidden
|
|
1922
|
+
*
|
|
1923
|
+
* Web MCP tool adapter for the Kendo Angular Gantt component.
|
|
1924
|
+
* Supports: task CRUD, sort, filter, expand/collapse, export, view.
|
|
1925
|
+
*/
|
|
1926
|
+
class GanttToolAdapter {
|
|
1927
|
+
selector = 'kendo-gantt';
|
|
1928
|
+
registerTools(gantt, config, modelContext, ngZone) {
|
|
1929
|
+
const handles = [];
|
|
1930
|
+
const prefix = config.dataName || 'kendo-gantt';
|
|
1931
|
+
const label = config.dataName
|
|
1932
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' gantt'
|
|
1933
|
+
: 'Kendo UI Gantt';
|
|
1934
|
+
const mc = modelContext;
|
|
1935
|
+
const zone = ngZone;
|
|
1936
|
+
const rawOptions = this.buildToolOptions(gantt, config, label);
|
|
1937
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
1938
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
1939
|
+
// Task CRUD
|
|
1940
|
+
if (enabled.has('add-task')) {
|
|
1941
|
+
handles.push(mc.registerTool({
|
|
1942
|
+
name: `${prefix}-add-task`,
|
|
1943
|
+
description: `Add a new task to the ${label}.`,
|
|
1944
|
+
inputSchema: {
|
|
1945
|
+
type: 'object',
|
|
1946
|
+
properties: {
|
|
1947
|
+
title: { type: 'string', description: 'Task title.' },
|
|
1948
|
+
start: { type: 'string', description: 'Start date (ISO 8601).' },
|
|
1949
|
+
end: { type: 'string', description: 'End date (ISO 8601).' }
|
|
1950
|
+
},
|
|
1951
|
+
required: ['title', 'start', 'end']
|
|
1952
|
+
},
|
|
1953
|
+
execute: (args) => {
|
|
1954
|
+
const data = gantt.data;
|
|
1955
|
+
if (!Array.isArray(data)) {
|
|
1956
|
+
return { success: false, message: 'Cannot add task: data is not an array.' };
|
|
1957
|
+
}
|
|
1958
|
+
const maxId = data.reduce((m, t) => Math.max(m, t.id || 0), 0);
|
|
1959
|
+
const newTask = {
|
|
1960
|
+
id: maxId + 1,
|
|
1961
|
+
parentId: null,
|
|
1962
|
+
title: args['title'],
|
|
1963
|
+
start: new Date(args['start']),
|
|
1964
|
+
end: new Date(args['end']),
|
|
1965
|
+
percentComplete: 0
|
|
1966
|
+
};
|
|
1967
|
+
zone.run(() => {
|
|
1968
|
+
gantt.data = [...data, newTask];
|
|
1969
|
+
});
|
|
1970
|
+
return { success: true, message: `Task added. ID: ${newTask.id}, Title: "${newTask.title}", Start: ${args['start']}, End: ${args['end']}, Complete: ${newTask.percentComplete}%.` };
|
|
1971
|
+
}
|
|
1972
|
+
}));
|
|
1973
|
+
}
|
|
1974
|
+
if (enabled.has('update-task')) {
|
|
1975
|
+
handles.push(mc.registerTool({
|
|
1976
|
+
name: `${prefix}-update-task`,
|
|
1977
|
+
description: `Update an existing task in the ${label}.`,
|
|
1978
|
+
inputSchema: {
|
|
1979
|
+
type: 'object',
|
|
1980
|
+
properties: {
|
|
1981
|
+
title: { type: 'string', description: 'Title of the task to update.' },
|
|
1982
|
+
start: { type: 'string', description: 'New start date.' },
|
|
1983
|
+
end: { type: 'string', description: 'New end date.' },
|
|
1984
|
+
percentComplete: { type: 'number', description: 'Completion percentage (0-1).' }
|
|
1985
|
+
},
|
|
1986
|
+
required: ['title']
|
|
1987
|
+
},
|
|
1988
|
+
execute: (args) => {
|
|
1989
|
+
const data = gantt.data;
|
|
1990
|
+
if (!Array.isArray(data)) {
|
|
1991
|
+
return { success: false, message: 'Cannot update task: data is not an array.' };
|
|
1992
|
+
}
|
|
1993
|
+
const task = data.find((t) => t.title === args['title']);
|
|
1994
|
+
if (!task) {
|
|
1995
|
+
return { success: false, message: `Task "${args['title']}" not found.` };
|
|
1996
|
+
}
|
|
1997
|
+
zone.run(() => {
|
|
1998
|
+
if (args['start'])
|
|
1999
|
+
task.start = new Date(args['start']);
|
|
2000
|
+
if (args['end'])
|
|
2001
|
+
task.end = new Date(args['end']);
|
|
2002
|
+
if (args['percentComplete'] !== undefined)
|
|
2003
|
+
task.percentComplete = args['percentComplete'];
|
|
2004
|
+
gantt.data = [...data];
|
|
2005
|
+
});
|
|
2006
|
+
return { success: true, message: `Task "${args['title']}" updated.` };
|
|
2007
|
+
}
|
|
2008
|
+
}));
|
|
2009
|
+
}
|
|
2010
|
+
if (enabled.has('delete-task')) {
|
|
2011
|
+
handles.push(mc.registerTool({
|
|
2012
|
+
name: `${prefix}-delete-task`,
|
|
2013
|
+
description: `Delete a task from the ${label}.`,
|
|
2014
|
+
inputSchema: {
|
|
2015
|
+
type: 'object',
|
|
2016
|
+
properties: {
|
|
2017
|
+
title: { type: 'string', description: 'Title of the task to delete.' }
|
|
2018
|
+
},
|
|
2019
|
+
required: ['title']
|
|
2020
|
+
},
|
|
2021
|
+
execute: (args) => {
|
|
2022
|
+
const data = gantt.data;
|
|
2023
|
+
if (!Array.isArray(data)) {
|
|
2024
|
+
return { success: false, message: 'Cannot delete task: data is not an array.' };
|
|
2025
|
+
}
|
|
2026
|
+
const idx = data.findIndex((t) => t.title === args['title']);
|
|
2027
|
+
if (idx < 0) {
|
|
2028
|
+
return { success: false, message: `Task "${args['title']}" not found.` };
|
|
2029
|
+
}
|
|
2030
|
+
zone.run(() => {
|
|
2031
|
+
data.splice(idx, 1);
|
|
2032
|
+
gantt.data = [...data];
|
|
2033
|
+
});
|
|
2034
|
+
return { success: true, message: `Task "${args['title']}" deleted.` };
|
|
2035
|
+
}
|
|
2036
|
+
}));
|
|
2037
|
+
}
|
|
2038
|
+
// View
|
|
2039
|
+
if (enabled.has('set-view')) {
|
|
2040
|
+
handles.push(mc.registerTool({
|
|
2041
|
+
name: `${prefix}-set-view`,
|
|
2042
|
+
description: `Change the timeline view of the ${label}.`,
|
|
2043
|
+
inputSchema: {
|
|
2044
|
+
type: 'object',
|
|
2045
|
+
properties: {
|
|
2046
|
+
view: { type: 'string', description: 'View name: day, week, month, year.' }
|
|
2047
|
+
},
|
|
2048
|
+
required: ['view']
|
|
2049
|
+
},
|
|
2050
|
+
execute: (args) => {
|
|
2051
|
+
zone.run(() => {
|
|
2052
|
+
gantt.activeView = args['view'];
|
|
2053
|
+
gantt.activeViewChange.emit(args['view']);
|
|
2054
|
+
});
|
|
2055
|
+
return { success: true, message: `View set to "${args['view']}".` };
|
|
2056
|
+
}
|
|
2057
|
+
}));
|
|
2058
|
+
}
|
|
2059
|
+
// Sort
|
|
2060
|
+
if (enabled.has('sort')) {
|
|
2061
|
+
handles.push(mc.registerTool({
|
|
2062
|
+
name: `${prefix}-sort`,
|
|
2063
|
+
description: `Sort the ${label} by a field.`,
|
|
2064
|
+
inputSchema: {
|
|
2065
|
+
type: 'object',
|
|
2066
|
+
properties: {
|
|
2067
|
+
field: { type: 'string', description: 'Field to sort by.' },
|
|
2068
|
+
dir: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction.' }
|
|
2069
|
+
},
|
|
2070
|
+
required: ['field', 'dir']
|
|
2071
|
+
},
|
|
2072
|
+
execute: (args) => {
|
|
2073
|
+
const sortDesc = [{ field: args['field'], dir: args['dir'] }];
|
|
2074
|
+
zone.run(() => {
|
|
2075
|
+
gantt.sort = sortDesc;
|
|
2076
|
+
gantt.sortChange.emit(sortDesc);
|
|
2077
|
+
});
|
|
2078
|
+
return { success: true, message: `Sorted by "${args['field']}" ${args['dir']}.` };
|
|
2079
|
+
}
|
|
2080
|
+
}));
|
|
2081
|
+
}
|
|
2082
|
+
if (enabled.has('clear-sort')) {
|
|
2083
|
+
handles.push(mc.registerTool({
|
|
2084
|
+
name: `${prefix}-clear-sort`,
|
|
2085
|
+
description: `Clear sorting from the ${label}.`,
|
|
2086
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2087
|
+
execute: () => {
|
|
2088
|
+
zone.run(() => {
|
|
2089
|
+
gantt.sort = [];
|
|
2090
|
+
gantt.sortChange.emit([]);
|
|
2091
|
+
});
|
|
2092
|
+
return { success: true, message: 'Sort cleared.' };
|
|
2093
|
+
}
|
|
2094
|
+
}));
|
|
2095
|
+
}
|
|
2096
|
+
// Filter
|
|
2097
|
+
if (enabled.has('filter')) {
|
|
2098
|
+
handles.push(mc.registerTool({
|
|
2099
|
+
name: `${prefix}-filter`,
|
|
2100
|
+
description: `Apply a filter to the ${label}.`,
|
|
2101
|
+
inputSchema: {
|
|
2102
|
+
type: 'object',
|
|
2103
|
+
properties: {
|
|
2104
|
+
field: { type: 'string', description: 'Field to filter.' },
|
|
2105
|
+
operator: { type: 'string', description: 'Filter operator.' },
|
|
2106
|
+
value: { type: 'string', description: 'Filter value.' }
|
|
2107
|
+
},
|
|
2108
|
+
required: ['field', 'operator', 'value']
|
|
2109
|
+
},
|
|
2110
|
+
execute: (args) => {
|
|
2111
|
+
const filterDesc = {
|
|
2112
|
+
logic: 'and',
|
|
2113
|
+
filters: [{ field: args['field'], operator: args['operator'], value: args['value'] }]
|
|
2114
|
+
};
|
|
2115
|
+
zone.run(() => {
|
|
2116
|
+
gantt.filter = filterDesc;
|
|
2117
|
+
gantt.filterChange.emit(filterDesc);
|
|
2118
|
+
});
|
|
2119
|
+
return { success: true, message: `Filtered "${args['field']}" ${args['operator']} "${args['value']}".` };
|
|
2120
|
+
}
|
|
2121
|
+
}));
|
|
2122
|
+
}
|
|
2123
|
+
if (enabled.has('clear-filters')) {
|
|
2124
|
+
handles.push(mc.registerTool({
|
|
2125
|
+
name: `${prefix}-clear-filters`,
|
|
2126
|
+
description: `Clear all filters from the ${label}.`,
|
|
2127
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2128
|
+
execute: () => {
|
|
2129
|
+
const emptyFilter = { logic: 'and', filters: [] };
|
|
2130
|
+
zone.run(() => {
|
|
2131
|
+
gantt.filter = emptyFilter;
|
|
2132
|
+
gantt.filterChange.emit(emptyFilter);
|
|
2133
|
+
});
|
|
2134
|
+
return { success: true, message: 'Filters cleared.' };
|
|
2135
|
+
}
|
|
2136
|
+
}));
|
|
2137
|
+
}
|
|
2138
|
+
// Expand / Collapse
|
|
2139
|
+
if (enabled.has('expand')) {
|
|
2140
|
+
handles.push(mc.registerTool({
|
|
2141
|
+
name: `${prefix}-expand`,
|
|
2142
|
+
description: `Expand a task row in the ${label}.`,
|
|
2143
|
+
inputSchema: {
|
|
2144
|
+
type: 'object',
|
|
2145
|
+
properties: { id: { type: 'number', description: 'Task id to expand.' } },
|
|
2146
|
+
required: ['id']
|
|
2147
|
+
},
|
|
2148
|
+
execute: (args) => {
|
|
2149
|
+
const task = getGanttData(gantt).find((t) => t.id === args['id']);
|
|
2150
|
+
if (!task) {
|
|
2151
|
+
return { success: false, message: `Task with id ${args['id']} not found.` };
|
|
2152
|
+
}
|
|
2153
|
+
zone.run(() => {
|
|
2154
|
+
gantt.rowExpand.emit({ dataItem: task });
|
|
2155
|
+
gantt.expandStateChange?.emit({ dataItem: task, expand: true });
|
|
2156
|
+
gantt.updateView();
|
|
2157
|
+
});
|
|
2158
|
+
return { success: true, message: `Task ${args['id']} expanded.` };
|
|
2159
|
+
}
|
|
2160
|
+
}));
|
|
2161
|
+
}
|
|
2162
|
+
if (enabled.has('collapse')) {
|
|
2163
|
+
handles.push(mc.registerTool({
|
|
2164
|
+
name: `${prefix}-collapse`,
|
|
2165
|
+
description: `Collapse a task row in the ${label}.`,
|
|
2166
|
+
inputSchema: {
|
|
2167
|
+
type: 'object',
|
|
2168
|
+
properties: { id: { type: 'number', description: 'Task id to collapse.' } },
|
|
2169
|
+
required: ['id']
|
|
2170
|
+
},
|
|
2171
|
+
execute: (args) => {
|
|
2172
|
+
const task = getGanttData(gantt).find((t) => t.id === args['id']);
|
|
2173
|
+
if (!task) {
|
|
2174
|
+
return { success: false, message: `Task with id ${args['id']} not found.` };
|
|
2175
|
+
}
|
|
2176
|
+
zone.run(() => {
|
|
2177
|
+
gantt.rowCollapse.emit({ dataItem: task });
|
|
2178
|
+
gantt.expandStateChange?.emit({ dataItem: task, expand: false });
|
|
2179
|
+
gantt.updateView();
|
|
2180
|
+
});
|
|
2181
|
+
return { success: true, message: `Task ${args['id']} collapsed.` };
|
|
2182
|
+
}
|
|
2183
|
+
}));
|
|
2184
|
+
}
|
|
2185
|
+
// Select
|
|
2186
|
+
if (enabled.has('select')) {
|
|
2187
|
+
handles.push(mc.registerTool({
|
|
2188
|
+
name: `${prefix}-select`,
|
|
2189
|
+
description: `Select a task in the ${label}.`,
|
|
2190
|
+
inputSchema: {
|
|
2191
|
+
type: 'object',
|
|
2192
|
+
properties: { id: { type: 'number', description: 'Task id to select.' } },
|
|
2193
|
+
required: ['id']
|
|
2194
|
+
},
|
|
2195
|
+
execute: (args) => {
|
|
2196
|
+
const task = getGanttData(gantt).find((t) => t.id === args['id']);
|
|
2197
|
+
if (!task) {
|
|
2198
|
+
return { success: false, message: `Task with id ${args['id']} not found.` };
|
|
2199
|
+
}
|
|
2200
|
+
zone.run(() => {
|
|
2201
|
+
gantt.notifySelectionChange?.(task, 'select');
|
|
2202
|
+
});
|
|
2203
|
+
return { success: true, message: `Task ${args['id']} selected.` };
|
|
2204
|
+
}
|
|
2205
|
+
}));
|
|
2206
|
+
}
|
|
2207
|
+
// Export
|
|
2208
|
+
if (enabled.has('export-pdf') && typeof gantt.saveAsPDF === 'function') {
|
|
2209
|
+
handles.push(mc.registerTool({
|
|
2210
|
+
name: `${prefix}-export-pdf`,
|
|
2211
|
+
description: `Export the ${label} to PDF.`,
|
|
2212
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2213
|
+
execute: () => {
|
|
2214
|
+
zone.run(() => gantt.saveAsPDF());
|
|
2215
|
+
return { success: true, message: 'PDF export started.' };
|
|
2216
|
+
}
|
|
2217
|
+
}));
|
|
2218
|
+
}
|
|
2219
|
+
if (enabled.has('export-excel') && typeof gantt.saveAsExcel === 'function') {
|
|
2220
|
+
handles.push(mc.registerTool({
|
|
2221
|
+
name: `${prefix}-export-excel`,
|
|
2222
|
+
description: `Export the ${label} to Excel.`,
|
|
2223
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2224
|
+
execute: () => {
|
|
2225
|
+
zone.run(() => gantt.saveAsExcel());
|
|
2226
|
+
return { success: true, message: 'Excel export started.' };
|
|
2227
|
+
}
|
|
2228
|
+
}));
|
|
2229
|
+
}
|
|
2230
|
+
return handles;
|
|
2231
|
+
}
|
|
2232
|
+
buildToolOptions(gantt, config, label) {
|
|
2233
|
+
return [
|
|
2234
|
+
{ name: 'add-task', description: `Add a new task to the ${label}.`, enabled: true },
|
|
2235
|
+
{ name: 'update-task', description: `Update an existing task in the ${label}.`, enabled: true },
|
|
2236
|
+
{ name: 'delete-task', description: `Delete a task from the ${label}.`, enabled: true },
|
|
2237
|
+
{ name: 'set-view', description: `Change the timeline view of the ${label}.`, enabled: true },
|
|
2238
|
+
{ name: 'sort', description: `Sort the ${label} by a field.`, enabled: !!gantt.sortable },
|
|
2239
|
+
{ name: 'clear-sort', description: `Clear sorting from the ${label}.`, enabled: !!gantt.sortable },
|
|
2240
|
+
{ name: 'filter', description: `Apply a filter to the ${label}.`, enabled: !!gantt.filterable },
|
|
2241
|
+
{ name: 'clear-filters', description: `Clear all filters from the ${label}.`, enabled: !!gantt.filterable },
|
|
2242
|
+
{ name: 'expand', description: `Expand a task row in the ${label}.`, enabled: true },
|
|
2243
|
+
{ name: 'collapse', description: `Collapse a task row in the ${label}.`, enabled: true },
|
|
2244
|
+
{ name: 'select', description: `Select a task in the ${label}.`, enabled: true },
|
|
2245
|
+
{ name: 'export-pdf', description: `Export the ${label} to PDF.`, enabled: true },
|
|
2246
|
+
{ name: 'export-excel', description: `Export the ${label} to Excel.`, enabled: true }
|
|
2247
|
+
];
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
/**
|
|
2252
|
+
* @hidden
|
|
2253
|
+
*
|
|
2254
|
+
* Web MCP tool adapter for the Kendo Angular PivotGrid component.
|
|
2255
|
+
* Supports: set rows, set columns, set measures, expand, collapse, export.
|
|
2256
|
+
*/
|
|
2257
|
+
class PivotGridToolAdapter {
|
|
2258
|
+
selector = 'kendo-pivotgrid';
|
|
2259
|
+
registerTools(pivot, config, modelContext, ngZone) {
|
|
2260
|
+
const handles = [];
|
|
2261
|
+
const prefix = config.dataName || 'kendo-pivotgrid';
|
|
2262
|
+
const label = config.dataName
|
|
2263
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' pivot grid'
|
|
2264
|
+
: 'Kendo UI PivotGrid';
|
|
2265
|
+
const mc = modelContext;
|
|
2266
|
+
const zone = ngZone;
|
|
2267
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
2268
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
2269
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
2270
|
+
if (enabled.has('set-rows')) {
|
|
2271
|
+
handles.push(mc.registerTool({
|
|
2272
|
+
name: `${prefix}-set-rows`,
|
|
2273
|
+
description: `Set the row fields of the ${label}.`,
|
|
2274
|
+
inputSchema: {
|
|
2275
|
+
type: 'object',
|
|
2276
|
+
properties: {
|
|
2277
|
+
fields: { type: 'array', items: { type: 'string' }, description: 'Row field names.' }
|
|
2278
|
+
},
|
|
2279
|
+
required: ['fields']
|
|
2280
|
+
},
|
|
2281
|
+
execute: (args) => {
|
|
2282
|
+
zone.run(() => {
|
|
2283
|
+
const ds = pivot.dataService;
|
|
2284
|
+
const newAxes = args['fields'].map(f => ({ name: [f], expand: false }));
|
|
2285
|
+
const newState = { ...ds.state, rowAxes: newAxes };
|
|
2286
|
+
ds.configuratorFieldChange.emit(newState);
|
|
2287
|
+
});
|
|
2288
|
+
return { success: true, message: `Rows set to: ${args['fields'].join(', ')}.` };
|
|
2289
|
+
}
|
|
2290
|
+
}));
|
|
2291
|
+
}
|
|
2292
|
+
if (enabled.has('set-columns')) {
|
|
2293
|
+
handles.push(mc.registerTool({
|
|
2294
|
+
name: `${prefix}-set-columns`,
|
|
2295
|
+
description: `Set the column fields of the ${label}.`,
|
|
2296
|
+
inputSchema: {
|
|
2297
|
+
type: 'object',
|
|
2298
|
+
properties: {
|
|
2299
|
+
fields: { type: 'array', items: { type: 'string' }, description: 'Column field names.' }
|
|
2300
|
+
},
|
|
2301
|
+
required: ['fields']
|
|
2302
|
+
},
|
|
2303
|
+
execute: (args) => {
|
|
2304
|
+
zone.run(() => {
|
|
2305
|
+
const ds = pivot.dataService;
|
|
2306
|
+
const newAxes = args['fields'].map(f => ({ name: [f], expand: false }));
|
|
2307
|
+
const newState = { ...ds.state, columnAxes: newAxes };
|
|
2308
|
+
ds.configuratorFieldChange.emit(newState);
|
|
2309
|
+
});
|
|
2310
|
+
return { success: true, message: `Columns set to: ${args['fields'].join(', ')}.` };
|
|
2311
|
+
}
|
|
2312
|
+
}));
|
|
2313
|
+
}
|
|
2314
|
+
if (enabled.has('set-measures')) {
|
|
2315
|
+
handles.push(mc.registerTool({
|
|
2316
|
+
name: `${prefix}-set-measures`,
|
|
2317
|
+
description: `Set the measure fields of the ${label}.`,
|
|
2318
|
+
inputSchema: {
|
|
2319
|
+
type: 'object',
|
|
2320
|
+
properties: {
|
|
2321
|
+
fields: { type: 'array', items: { type: 'string' }, description: 'Measure field names.' }
|
|
2322
|
+
},
|
|
2323
|
+
required: ['fields']
|
|
2324
|
+
},
|
|
2325
|
+
execute: (args) => {
|
|
2326
|
+
zone.run(() => {
|
|
2327
|
+
const ds = pivot.dataService;
|
|
2328
|
+
const newAxes = args['fields'].map(f => ({ name: [f] }));
|
|
2329
|
+
const newState = { ...ds.state, measureAxes: newAxes };
|
|
2330
|
+
ds.configuratorFieldChange.emit(newState);
|
|
2331
|
+
});
|
|
2332
|
+
return { success: true, message: `Measures set to: ${args['fields'].join(', ')}.` };
|
|
2333
|
+
}
|
|
2334
|
+
}));
|
|
2335
|
+
}
|
|
2336
|
+
if (enabled.has('expand')) {
|
|
2337
|
+
handles.push(mc.registerTool({
|
|
2338
|
+
name: `${prefix}-expand`,
|
|
2339
|
+
description: `Expand a row or column header in the ${label}.`,
|
|
2340
|
+
inputSchema: {
|
|
2341
|
+
type: 'object',
|
|
2342
|
+
properties: {
|
|
2343
|
+
axis: { type: 'string', enum: ['rows', 'columns'], description: 'Axis to expand on.' },
|
|
2344
|
+
path: { type: 'array', items: { type: 'string' }, description: 'Header path to expand.' }
|
|
2345
|
+
},
|
|
2346
|
+
required: ['axis', 'path']
|
|
2347
|
+
},
|
|
2348
|
+
execute: (args) => {
|
|
2349
|
+
zone.run(() => {
|
|
2350
|
+
const ds = pivot.dataService;
|
|
2351
|
+
const tableType = args['axis'] === 'columns' ? 'columnHeader' : 'rowHeader';
|
|
2352
|
+
ds.expandedStateChange.emit({ tableType, cell: { path: args['path'] } });
|
|
2353
|
+
});
|
|
2354
|
+
return { success: true, message: `Expanded ${args['axis']} at path [${args['path'].join(', ')}].` };
|
|
2355
|
+
}
|
|
2356
|
+
}));
|
|
2357
|
+
}
|
|
2358
|
+
if (enabled.has('collapse')) {
|
|
2359
|
+
handles.push(mc.registerTool({
|
|
2360
|
+
name: `${prefix}-collapse`,
|
|
2361
|
+
description: `Collapse a row or column header in the ${label}.`,
|
|
2362
|
+
inputSchema: {
|
|
2363
|
+
type: 'object',
|
|
2364
|
+
properties: {
|
|
2365
|
+
axis: { type: 'string', enum: ['rows', 'columns'], description: 'Axis to collapse on.' },
|
|
2366
|
+
path: { type: 'array', items: { type: 'string' }, description: 'Header path to collapse.' }
|
|
2367
|
+
},
|
|
2368
|
+
required: ['axis', 'path']
|
|
2369
|
+
},
|
|
2370
|
+
execute: (args) => {
|
|
2371
|
+
zone.run(() => {
|
|
2372
|
+
const ds = pivot.dataService;
|
|
2373
|
+
const tableType = args['axis'] === 'columns' ? 'columnHeader' : 'rowHeader';
|
|
2374
|
+
ds.expandedStateChange.emit({ tableType, cell: { path: args['path'] } });
|
|
2375
|
+
});
|
|
2376
|
+
return { success: true, message: `Collapsed ${args['axis']} at path [${args['path'].join(', ')}].` };
|
|
2377
|
+
}
|
|
2378
|
+
}));
|
|
2379
|
+
}
|
|
2380
|
+
if (enabled.has('export-pdf')) {
|
|
2381
|
+
handles.push(mc.registerTool({
|
|
2382
|
+
name: `${prefix}-export-pdf`,
|
|
2383
|
+
description: `Export the ${label} to PDF.`,
|
|
2384
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2385
|
+
execute: () => {
|
|
2386
|
+
zone.run(() => pivot.saveAsPDF?.());
|
|
2387
|
+
return { success: true, message: 'PDF export started.' };
|
|
2388
|
+
}
|
|
2389
|
+
}));
|
|
2390
|
+
}
|
|
2391
|
+
return handles;
|
|
2392
|
+
}
|
|
2393
|
+
buildToolOptions(config, label) {
|
|
2394
|
+
return [
|
|
2395
|
+
{ name: 'set-rows', description: `Set the row fields of the ${label}.`, enabled: true },
|
|
2396
|
+
{ name: 'set-columns', description: `Set the column fields of the ${label}.`, enabled: true },
|
|
2397
|
+
{ name: 'set-measures', description: `Set the measure fields of the ${label}.`, enabled: true },
|
|
2398
|
+
{ name: 'expand', description: `Expand a row or column header in the ${label}.`, enabled: true },
|
|
2399
|
+
{ name: 'collapse', description: `Collapse a row or column header in the ${label}.`, enabled: true },
|
|
2400
|
+
{ name: 'export-pdf', description: `Export the ${label} to PDF.`, enabled: true }
|
|
2401
|
+
];
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
// DropDownList
|
|
2406
|
+
/**
|
|
2407
|
+
* @hidden
|
|
2408
|
+
*/
|
|
2409
|
+
class DropDownListToolAdapter {
|
|
2410
|
+
selector = 'kendo-dropdownlist';
|
|
2411
|
+
registerTools(ddl, config, mc, zone) {
|
|
2412
|
+
const prefix = config.dataName || 'kendo-dropdownlist';
|
|
2413
|
+
const label = config.dataName
|
|
2414
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' dropdown'
|
|
2415
|
+
: 'Kendo UI DropDownList';
|
|
2416
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
2417
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
2418
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
2419
|
+
const handles = [];
|
|
2420
|
+
if (enabled.has('select')) {
|
|
2421
|
+
handles.push(mc.registerTool({
|
|
2422
|
+
name: `${prefix}-select`,
|
|
2423
|
+
description: `Select an item in the ${label} by value or text.`,
|
|
2424
|
+
inputSchema: {
|
|
2425
|
+
type: 'object',
|
|
2426
|
+
properties: {
|
|
2427
|
+
value: { type: 'string', description: 'Value or text of the item to select.' }
|
|
2428
|
+
},
|
|
2429
|
+
required: ['value']
|
|
2430
|
+
},
|
|
2431
|
+
execute: (args) => {
|
|
2432
|
+
zone.run(() => {
|
|
2433
|
+
ddl.value = args['value'];
|
|
2434
|
+
ddl.valueChange.emit(args['value']);
|
|
2435
|
+
ddl.selectionChange.emit(args['value']);
|
|
2436
|
+
});
|
|
2437
|
+
return { success: true, message: `Selected "${args['value']}".` };
|
|
2438
|
+
}
|
|
2439
|
+
}));
|
|
2440
|
+
}
|
|
2441
|
+
if (enabled.has('open')) {
|
|
2442
|
+
handles.push(mc.registerTool({
|
|
2443
|
+
name: `${prefix}-open`,
|
|
2444
|
+
description: `Open the ${label} popup.`,
|
|
2445
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2446
|
+
execute: () => {
|
|
2447
|
+
zone.run(() => ddl.toggle(true));
|
|
2448
|
+
return { success: true, message: 'Dropdown opened.' };
|
|
2449
|
+
}
|
|
2450
|
+
}));
|
|
2451
|
+
}
|
|
2452
|
+
if (enabled.has('close')) {
|
|
2453
|
+
handles.push(mc.registerTool({
|
|
2454
|
+
name: `${prefix}-close`,
|
|
2455
|
+
description: `Close the ${label} popup.`,
|
|
2456
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2457
|
+
execute: () => {
|
|
2458
|
+
zone.run(() => ddl.toggle(false));
|
|
2459
|
+
return { success: true, message: 'Dropdown closed.' };
|
|
2460
|
+
}
|
|
2461
|
+
}));
|
|
2462
|
+
}
|
|
2463
|
+
return handles;
|
|
2464
|
+
}
|
|
2465
|
+
buildToolOptions(config, label) {
|
|
2466
|
+
return [
|
|
2467
|
+
{ name: 'select', description: `Select an item in the ${label} by value or text.`, enabled: true },
|
|
2468
|
+
{ name: 'open', description: `Open the ${label} popup.`, enabled: true },
|
|
2469
|
+
{ name: 'close', description: `Close the ${label} popup.`, enabled: true }
|
|
2470
|
+
];
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
// ComboBox
|
|
2474
|
+
/**
|
|
2475
|
+
* @hidden
|
|
2476
|
+
*/
|
|
2477
|
+
class ComboBoxToolAdapter {
|
|
2478
|
+
selector = 'kendo-combobox';
|
|
2479
|
+
registerTools(combo, config, mc, zone) {
|
|
2480
|
+
const prefix = config.dataName || 'kendo-combobox';
|
|
2481
|
+
const label = config.dataName
|
|
2482
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' combobox'
|
|
2483
|
+
: 'Kendo UI ComboBox';
|
|
2484
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
2485
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
2486
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
2487
|
+
const handles = [];
|
|
2488
|
+
if (enabled.has('select')) {
|
|
2489
|
+
handles.push(mc.registerTool({
|
|
2490
|
+
name: `${prefix}-select`,
|
|
2491
|
+
description: `Select an item in the ${label}.`,
|
|
2492
|
+
inputSchema: {
|
|
2493
|
+
type: 'object',
|
|
2494
|
+
properties: {
|
|
2495
|
+
value: { type: 'string', description: 'Value or text of the item to select.' }
|
|
2496
|
+
},
|
|
2497
|
+
required: ['value']
|
|
2498
|
+
},
|
|
2499
|
+
execute: (args) => {
|
|
2500
|
+
zone.run(() => {
|
|
2501
|
+
combo.value = args['value'];
|
|
2502
|
+
combo.valueChange.emit(args['value']);
|
|
2503
|
+
combo.selectionChange.emit(args['value']);
|
|
2504
|
+
});
|
|
2505
|
+
return { success: true, message: `Selected "${args['value']}".` };
|
|
2506
|
+
}
|
|
2507
|
+
}));
|
|
2508
|
+
}
|
|
2509
|
+
if (enabled.has('search')) {
|
|
2510
|
+
handles.push(mc.registerTool({
|
|
2511
|
+
name: `${prefix}-search`,
|
|
2512
|
+
description: `Search/filter items in the ${label}.`,
|
|
2513
|
+
inputSchema: {
|
|
2514
|
+
type: 'object',
|
|
2515
|
+
properties: {
|
|
2516
|
+
text: { type: 'string', description: 'Search text.' }
|
|
2517
|
+
},
|
|
2518
|
+
required: ['text']
|
|
2519
|
+
},
|
|
2520
|
+
execute: (args) => {
|
|
2521
|
+
zone.run(() => {
|
|
2522
|
+
combo.toggle(true);
|
|
2523
|
+
combo.filterChange.emit(args['text']);
|
|
2524
|
+
});
|
|
2525
|
+
return { success: true, message: `Searched for "${args['text']}".` };
|
|
2526
|
+
}
|
|
2527
|
+
}));
|
|
2528
|
+
}
|
|
2529
|
+
if (enabled.has('clear')) {
|
|
2530
|
+
handles.push(mc.registerTool({
|
|
2531
|
+
name: `${prefix}-clear`,
|
|
2532
|
+
description: `Clear the ${label} value.`,
|
|
2533
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2534
|
+
execute: () => {
|
|
2535
|
+
zone.run(() => {
|
|
2536
|
+
combo.value = null;
|
|
2537
|
+
combo.valueChange.emit(null);
|
|
2538
|
+
});
|
|
2539
|
+
return { success: true, message: 'Value cleared.' };
|
|
2540
|
+
}
|
|
2541
|
+
}));
|
|
2542
|
+
}
|
|
2543
|
+
if (enabled.has('open')) {
|
|
2544
|
+
handles.push(mc.registerTool({
|
|
2545
|
+
name: `${prefix}-open`,
|
|
2546
|
+
description: `Open the ${label} popup.`,
|
|
2547
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2548
|
+
execute: () => {
|
|
2549
|
+
zone.run(() => combo.toggle(true));
|
|
2550
|
+
return { success: true, message: 'Popup opened.' };
|
|
2551
|
+
}
|
|
2552
|
+
}));
|
|
2553
|
+
}
|
|
2554
|
+
if (enabled.has('close')) {
|
|
2555
|
+
handles.push(mc.registerTool({
|
|
2556
|
+
name: `${prefix}-close`,
|
|
2557
|
+
description: `Close the ${label} popup.`,
|
|
2558
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2559
|
+
execute: () => {
|
|
2560
|
+
zone.run(() => combo.toggle(false));
|
|
2561
|
+
return { success: true, message: 'Popup closed.' };
|
|
2562
|
+
}
|
|
2563
|
+
}));
|
|
2564
|
+
}
|
|
2565
|
+
return handles;
|
|
2566
|
+
}
|
|
2567
|
+
buildToolOptions(config, label) {
|
|
2568
|
+
return [
|
|
2569
|
+
{ name: 'select', description: `Select an item in the ${label}.`, enabled: true },
|
|
2570
|
+
{ name: 'search', description: `Search/filter items in the ${label}.`, enabled: true },
|
|
2571
|
+
{ name: 'clear', description: `Clear the ${label} value.`, enabled: true },
|
|
2572
|
+
{ name: 'open', description: `Open the ${label} popup.`, enabled: true },
|
|
2573
|
+
{ name: 'close', description: `Close the ${label} popup.`, enabled: true }
|
|
2574
|
+
];
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
/**
|
|
2578
|
+
* @hidden
|
|
2579
|
+
*/
|
|
2580
|
+
class AutoCompleteToolAdapter {
|
|
2581
|
+
selector = 'kendo-autocomplete';
|
|
2582
|
+
registerTools(ac, config, mc, zone) {
|
|
2583
|
+
const prefix = config.dataName || 'kendo-autocomplete';
|
|
2584
|
+
const label = config.dataName
|
|
2585
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' autocomplete'
|
|
2586
|
+
: 'Kendo UI AutoComplete';
|
|
2587
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
2588
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
2589
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
2590
|
+
const handles = [];
|
|
2591
|
+
if (enabled.has('search')) {
|
|
2592
|
+
handles.push(mc.registerTool({
|
|
2593
|
+
name: `${prefix}-search`,
|
|
2594
|
+
description: `Search for items in the ${label}.`,
|
|
2595
|
+
inputSchema: {
|
|
2596
|
+
type: 'object',
|
|
2597
|
+
properties: {
|
|
2598
|
+
text: { type: 'string', description: 'Search text.' }
|
|
2599
|
+
},
|
|
2600
|
+
required: ['text']
|
|
2601
|
+
},
|
|
2602
|
+
execute: (args) => {
|
|
2603
|
+
zone.run(() => {
|
|
2604
|
+
ac.value = args['text'];
|
|
2605
|
+
ac.toggle(true);
|
|
2606
|
+
ac.filterChange.emit(args['text']);
|
|
2607
|
+
});
|
|
2608
|
+
return { success: true, message: `Searched for "${args['text']}".` };
|
|
2609
|
+
}
|
|
2610
|
+
}));
|
|
2611
|
+
}
|
|
2612
|
+
if (enabled.has('select')) {
|
|
2613
|
+
handles.push(mc.registerTool({
|
|
2614
|
+
name: `${prefix}-select`,
|
|
2615
|
+
description: `Select a value in the ${label}.`,
|
|
2616
|
+
inputSchema: {
|
|
2617
|
+
type: 'object',
|
|
2618
|
+
properties: {
|
|
2619
|
+
value: { type: 'string', description: 'Value to select.' }
|
|
2620
|
+
},
|
|
2621
|
+
required: ['value']
|
|
2622
|
+
},
|
|
2623
|
+
execute: (args) => {
|
|
2624
|
+
zone.run(() => {
|
|
2625
|
+
ac.value = args['value'];
|
|
2626
|
+
ac.valueChange.emit(args['value']);
|
|
2627
|
+
});
|
|
2628
|
+
return { success: true, message: `Selected "${args['value']}".` };
|
|
2629
|
+
}
|
|
2630
|
+
}));
|
|
2631
|
+
}
|
|
2632
|
+
if (enabled.has('clear')) {
|
|
2633
|
+
handles.push(mc.registerTool({
|
|
2634
|
+
name: `${prefix}-clear`,
|
|
2635
|
+
description: `Clear the ${label} value.`,
|
|
2636
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2637
|
+
execute: () => {
|
|
2638
|
+
zone.run(() => {
|
|
2639
|
+
ac.value = '';
|
|
2640
|
+
ac.valueChange.emit('');
|
|
2641
|
+
});
|
|
2642
|
+
return { success: true, message: 'Value cleared.' };
|
|
2643
|
+
}
|
|
2644
|
+
}));
|
|
2645
|
+
}
|
|
2646
|
+
return handles;
|
|
2647
|
+
}
|
|
2648
|
+
buildToolOptions(config, label) {
|
|
2649
|
+
return [
|
|
2650
|
+
{ name: 'search', description: `Search for items in the ${label}.`, enabled: true },
|
|
2651
|
+
{ name: 'select', description: `Select a value in the ${label}.`, enabled: true },
|
|
2652
|
+
{ name: 'clear', description: `Clear the ${label} value.`, enabled: true }
|
|
2653
|
+
];
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
// MultiSelect
|
|
2657
|
+
/**
|
|
2658
|
+
* @hidden
|
|
2659
|
+
*/
|
|
2660
|
+
class MultiSelectToolAdapter {
|
|
2661
|
+
selector = 'kendo-multiselect';
|
|
2662
|
+
registerTools(ms, config, mc, zone) {
|
|
2663
|
+
const prefix = config.dataName || 'kendo-multiselect';
|
|
2664
|
+
const label = config.dataName
|
|
2665
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' multiselect'
|
|
2666
|
+
: 'Kendo UI MultiSelect';
|
|
2667
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
2668
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
2669
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
2670
|
+
const handles = [];
|
|
2671
|
+
if (enabled.has('select')) {
|
|
2672
|
+
handles.push(mc.registerTool({
|
|
2673
|
+
name: `${prefix}-select`,
|
|
2674
|
+
description: `Add items to the ${label} selection.`,
|
|
2675
|
+
inputSchema: {
|
|
2676
|
+
type: 'object',
|
|
2677
|
+
properties: {
|
|
2678
|
+
values: { type: 'array', items: { type: 'string' }, description: 'Values to add to selection.' }
|
|
2679
|
+
},
|
|
2680
|
+
required: ['values']
|
|
2681
|
+
},
|
|
2682
|
+
execute: (args) => {
|
|
2683
|
+
const toAdd = args['values'];
|
|
2684
|
+
const current = ms.value || [];
|
|
2685
|
+
const merged = [...current, ...toAdd.filter((v) => !current.includes(v))];
|
|
2686
|
+
zone.run(() => {
|
|
2687
|
+
ms.value = merged;
|
|
2688
|
+
ms.valueChange.emit(merged);
|
|
2689
|
+
});
|
|
2690
|
+
return { success: true, message: `Selected ${toAdd.length} item(s). Added: [${toAdd.join(', ')}]. Total selected: ${merged.length}.` };
|
|
2691
|
+
}
|
|
2692
|
+
}));
|
|
2693
|
+
}
|
|
2694
|
+
if (enabled.has('remove')) {
|
|
2695
|
+
handles.push(mc.registerTool({
|
|
2696
|
+
name: `${prefix}-remove`,
|
|
2697
|
+
description: `Remove an item from the ${label} selection.`,
|
|
2698
|
+
inputSchema: {
|
|
2699
|
+
type: 'object',
|
|
2700
|
+
properties: {
|
|
2701
|
+
value: { type: 'string', description: 'Value to remove from selection.' }
|
|
2702
|
+
},
|
|
2703
|
+
required: ['value']
|
|
2704
|
+
},
|
|
2705
|
+
execute: (args) => {
|
|
2706
|
+
zone.run(() => {
|
|
2707
|
+
ms.removeTag.emit(args['value']);
|
|
2708
|
+
});
|
|
2709
|
+
return { success: true, message: `Removed "${args['value']}".` };
|
|
2710
|
+
}
|
|
2711
|
+
}));
|
|
2712
|
+
}
|
|
2713
|
+
if (enabled.has('clear')) {
|
|
2714
|
+
handles.push(mc.registerTool({
|
|
2715
|
+
name: `${prefix}-clear`,
|
|
2716
|
+
description: `Clear all selections in the ${label}.`,
|
|
2717
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2718
|
+
execute: () => {
|
|
2719
|
+
zone.run(() => {
|
|
2720
|
+
ms.value = [];
|
|
2721
|
+
ms.valueChange.emit([]);
|
|
2722
|
+
});
|
|
2723
|
+
return { success: true, message: 'Selection cleared.' };
|
|
2724
|
+
}
|
|
2725
|
+
}));
|
|
2726
|
+
}
|
|
2727
|
+
return handles;
|
|
2728
|
+
}
|
|
2729
|
+
buildToolOptions(config, label) {
|
|
2730
|
+
return [
|
|
2731
|
+
{ name: 'select', description: `Add items to the ${label} selection.`, enabled: true },
|
|
2732
|
+
{ name: 'remove', description: `Remove an item from the ${label} selection.`, enabled: true },
|
|
2733
|
+
{ name: 'clear', description: `Clear all selections in the ${label}.`, enabled: true }
|
|
2734
|
+
];
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
// DropDownTree
|
|
2738
|
+
/**
|
|
2739
|
+
* @hidden
|
|
2740
|
+
*/
|
|
2741
|
+
class DropDownTreeToolAdapter {
|
|
2742
|
+
selector = 'kendo-dropdowntree';
|
|
2743
|
+
registerTools(ddt, config, mc, zone) {
|
|
2744
|
+
const prefix = config.dataName || 'kendo-dropdowntree';
|
|
2745
|
+
const label = config.dataName
|
|
2746
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' dropdown tree'
|
|
2747
|
+
: 'Kendo UI DropDownTree';
|
|
2748
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
2749
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
2750
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
2751
|
+
const handles = [];
|
|
2752
|
+
if (enabled.has('select')) {
|
|
2753
|
+
handles.push(mc.registerTool({
|
|
2754
|
+
name: `${prefix}-select`,
|
|
2755
|
+
description: `Select a node in the ${label}.`,
|
|
2756
|
+
inputSchema: {
|
|
2757
|
+
type: 'object',
|
|
2758
|
+
properties: {
|
|
2759
|
+
value: { type: 'string', description: 'Node value to select.' }
|
|
2760
|
+
},
|
|
2761
|
+
required: ['value']
|
|
2762
|
+
},
|
|
2763
|
+
execute: (args) => {
|
|
2764
|
+
zone.run(() => {
|
|
2765
|
+
ddt.value = args['value'];
|
|
2766
|
+
ddt.valueChange.emit(args['value']);
|
|
2767
|
+
});
|
|
2768
|
+
return { success: true, message: `Selected "${args['value']}".` };
|
|
2769
|
+
}
|
|
2770
|
+
}));
|
|
2771
|
+
}
|
|
2772
|
+
if (enabled.has('open')) {
|
|
2773
|
+
handles.push(mc.registerTool({
|
|
2774
|
+
name: `${prefix}-open`,
|
|
2775
|
+
description: `Open the ${label} popup.`,
|
|
2776
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2777
|
+
execute: () => {
|
|
2778
|
+
zone.run(() => ddt.toggle(true));
|
|
2779
|
+
return { success: true, message: 'Popup opened.' };
|
|
2780
|
+
}
|
|
2781
|
+
}));
|
|
2782
|
+
}
|
|
2783
|
+
if (enabled.has('close')) {
|
|
2784
|
+
handles.push(mc.registerTool({
|
|
2785
|
+
name: `${prefix}-close`,
|
|
2786
|
+
description: `Close the ${label} popup.`,
|
|
2787
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2788
|
+
execute: () => {
|
|
2789
|
+
zone.run(() => ddt.toggle(false));
|
|
2790
|
+
return { success: true, message: 'Popup closed.' };
|
|
2791
|
+
}
|
|
2792
|
+
}));
|
|
2793
|
+
}
|
|
2794
|
+
if (enabled.has('clear')) {
|
|
2795
|
+
handles.push(mc.registerTool({
|
|
2796
|
+
name: `${prefix}-clear`,
|
|
2797
|
+
description: `Clear the ${label} value.`,
|
|
2798
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2799
|
+
execute: () => {
|
|
2800
|
+
zone.run(() => {
|
|
2801
|
+
ddt.value = null;
|
|
2802
|
+
ddt.valueChange.emit(null);
|
|
2803
|
+
});
|
|
2804
|
+
return { success: true, message: 'Value cleared.' };
|
|
2805
|
+
}
|
|
2806
|
+
}));
|
|
2807
|
+
}
|
|
2808
|
+
return handles;
|
|
2809
|
+
}
|
|
2810
|
+
buildToolOptions(config, label) {
|
|
2811
|
+
return [
|
|
2812
|
+
{ name: 'select', description: `Select a node in the ${label}.`, enabled: true },
|
|
2813
|
+
{ name: 'open', description: `Open the ${label} popup.`, enabled: true },
|
|
2814
|
+
{ name: 'close', description: `Close the ${label} popup.`, enabled: true },
|
|
2815
|
+
{ name: 'clear', description: `Clear the ${label} value.`, enabled: true }
|
|
2816
|
+
];
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
// MultiColumnComboBox
|
|
2820
|
+
/**
|
|
2821
|
+
* @hidden
|
|
2822
|
+
*/
|
|
2823
|
+
class MultiColumnComboBoxToolAdapter {
|
|
2824
|
+
selector = 'kendo-multicolumncombobox';
|
|
2825
|
+
registerTools(mccb, config, mc, zone) {
|
|
2826
|
+
const prefix = config.dataName || 'kendo-multicolumncombobox';
|
|
2827
|
+
const label = config.dataName
|
|
2828
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' multi-column combobox'
|
|
2829
|
+
: 'Kendo UI MultiColumnComboBox';
|
|
2830
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
2831
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
2832
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
2833
|
+
const handles = [];
|
|
2834
|
+
if (enabled.has('select')) {
|
|
2835
|
+
handles.push(mc.registerTool({
|
|
2836
|
+
name: `${prefix}-select`,
|
|
2837
|
+
description: `Select an item in the ${label}.`,
|
|
2838
|
+
inputSchema: {
|
|
2839
|
+
type: 'object',
|
|
2840
|
+
properties: {
|
|
2841
|
+
value: { type: 'string', description: 'Value to select.' }
|
|
2842
|
+
},
|
|
2843
|
+
required: ['value']
|
|
2844
|
+
},
|
|
2845
|
+
execute: (args) => {
|
|
2846
|
+
zone.run(() => {
|
|
2847
|
+
mccb.value = args['value'];
|
|
2848
|
+
mccb.valueChange.emit(args['value']);
|
|
2849
|
+
mccb.selectionChange.emit(args['value']);
|
|
2850
|
+
});
|
|
2851
|
+
return { success: true, message: `Selected "${args['value']}".` };
|
|
2852
|
+
}
|
|
2853
|
+
}));
|
|
2854
|
+
}
|
|
2855
|
+
if (enabled.has('search')) {
|
|
2856
|
+
handles.push(mc.registerTool({
|
|
2857
|
+
name: `${prefix}-search`,
|
|
2858
|
+
description: `Search/filter items in the ${label}.`,
|
|
2859
|
+
inputSchema: {
|
|
2860
|
+
type: 'object',
|
|
2861
|
+
properties: {
|
|
2862
|
+
text: { type: 'string', description: 'Search text.' }
|
|
2863
|
+
},
|
|
2864
|
+
required: ['text']
|
|
2865
|
+
},
|
|
2866
|
+
execute: (args) => {
|
|
2867
|
+
zone.run(() => {
|
|
2868
|
+
mccb.filterChange.emit(args['text']);
|
|
2869
|
+
});
|
|
2870
|
+
return { success: true, message: `Searched for "${args['text']}".` };
|
|
2871
|
+
}
|
|
2872
|
+
}));
|
|
2873
|
+
}
|
|
2874
|
+
if (enabled.has('clear')) {
|
|
2875
|
+
handles.push(mc.registerTool({
|
|
2876
|
+
name: `${prefix}-clear`,
|
|
2877
|
+
description: `Clear the ${label} value.`,
|
|
2878
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2879
|
+
execute: () => {
|
|
2880
|
+
zone.run(() => {
|
|
2881
|
+
mccb.value = null;
|
|
2882
|
+
mccb.valueChange.emit(null);
|
|
2883
|
+
});
|
|
2884
|
+
return { success: true, message: 'Value cleared.' };
|
|
2885
|
+
}
|
|
2886
|
+
}));
|
|
2887
|
+
}
|
|
2888
|
+
if (enabled.has('open')) {
|
|
2889
|
+
handles.push(mc.registerTool({
|
|
2890
|
+
name: `${prefix}-open`,
|
|
2891
|
+
description: `Open the ${label} popup.`,
|
|
2892
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2893
|
+
execute: () => {
|
|
2894
|
+
zone.run(() => mccb.toggle(true));
|
|
2895
|
+
return { success: true, message: 'Popup opened.' };
|
|
2896
|
+
}
|
|
2897
|
+
}));
|
|
2898
|
+
}
|
|
2899
|
+
return handles;
|
|
2900
|
+
}
|
|
2901
|
+
buildToolOptions(config, label) {
|
|
2902
|
+
return [
|
|
2903
|
+
{ name: 'select', description: `Select an item in the ${label}.`, enabled: true },
|
|
2904
|
+
{ name: 'search', description: `Search/filter items in the ${label}.`, enabled: true },
|
|
2905
|
+
{ name: 'clear', description: `Clear the ${label} value.`, enabled: true },
|
|
2906
|
+
{ name: 'open', description: `Open the ${label} popup.`, enabled: true }
|
|
2907
|
+
];
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2911
|
+
// DatePicker ────────────────────────────────────────────────────────
|
|
2912
|
+
/**
|
|
2913
|
+
* @hidden
|
|
2914
|
+
*/
|
|
2915
|
+
class DatePickerToolAdapter {
|
|
2916
|
+
selector = 'kendo-datepicker';
|
|
2917
|
+
registerTools(dp, config, mc, zone) {
|
|
2918
|
+
const prefix = config.dataName || 'kendo-datepicker';
|
|
2919
|
+
const label = config.dataName
|
|
2920
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' date picker'
|
|
2921
|
+
: 'Kendo UI DatePicker';
|
|
2922
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
2923
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
2924
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
2925
|
+
const handles = [];
|
|
2926
|
+
if (enabled.has('set-value')) {
|
|
2927
|
+
handles.push(mc.registerTool({
|
|
2928
|
+
name: `${prefix}-set-value`,
|
|
2929
|
+
description: `Set the date value of the ${label}.`,
|
|
2930
|
+
inputSchema: {
|
|
2931
|
+
type: 'object',
|
|
2932
|
+
properties: { date: { type: 'string', description: 'Date value (ISO 8601).' } },
|
|
2933
|
+
required: ['date']
|
|
2934
|
+
},
|
|
2935
|
+
execute: (args) => {
|
|
2936
|
+
const date = new Date(args['date']);
|
|
2937
|
+
zone.run(() => {
|
|
2938
|
+
dp.value = date;
|
|
2939
|
+
dp.valueChange.emit(date);
|
|
2940
|
+
dp.cdr.markForCheck();
|
|
2941
|
+
});
|
|
2942
|
+
return { success: true, message: `Date set to ${date.toISOString().split('T')[0]}.` };
|
|
2943
|
+
}
|
|
2944
|
+
}));
|
|
2945
|
+
}
|
|
2946
|
+
if (enabled.has('clear')) {
|
|
2947
|
+
handles.push(mc.registerTool({
|
|
2948
|
+
name: `${prefix}-clear`,
|
|
2949
|
+
description: `Clear the ${label} value.`,
|
|
2950
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
2951
|
+
execute: () => {
|
|
2952
|
+
zone.run(() => {
|
|
2953
|
+
dp.value = null;
|
|
2954
|
+
dp.valueChange.emit(null);
|
|
2955
|
+
dp.cdr.markForCheck();
|
|
2956
|
+
});
|
|
2957
|
+
return { success: true, message: 'Date cleared.' };
|
|
2958
|
+
}
|
|
2959
|
+
}));
|
|
2960
|
+
}
|
|
2961
|
+
return handles;
|
|
2962
|
+
}
|
|
2963
|
+
buildToolOptions(config, label) {
|
|
2964
|
+
return [
|
|
2965
|
+
{ name: 'set-value', description: `Set the date value of the ${label}.`, enabled: true },
|
|
2966
|
+
{ name: 'clear', description: `Clear the ${label} value.`, enabled: true }
|
|
2967
|
+
];
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
// DateRangePicker
|
|
2971
|
+
/**
|
|
2972
|
+
* @hidden
|
|
2973
|
+
*/
|
|
2974
|
+
class DateRangeToolAdapter {
|
|
2975
|
+
selector = 'kendo-daterange';
|
|
2976
|
+
registerTools(drp, config, mc, zone) {
|
|
2977
|
+
const prefix = config.dataName || 'kendo-daterangepicker';
|
|
2978
|
+
const label = config.dataName
|
|
2979
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' date range picker'
|
|
2980
|
+
: 'Kendo UI DateRangePicker';
|
|
2981
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
2982
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
2983
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
2984
|
+
const handles = [];
|
|
2985
|
+
if (enabled.has('set-range')) {
|
|
2986
|
+
handles.push(mc.registerTool({
|
|
2987
|
+
name: `${prefix}-set-range`,
|
|
2988
|
+
description: `Set the date range in the ${label}.`,
|
|
2989
|
+
inputSchema: {
|
|
2990
|
+
type: 'object',
|
|
2991
|
+
properties: {
|
|
2992
|
+
start: { type: 'string', description: 'Start date (ISO 8601).' },
|
|
2993
|
+
end: { type: 'string', description: 'End date (ISO 8601).' }
|
|
2994
|
+
},
|
|
2995
|
+
required: ['start', 'end']
|
|
2996
|
+
},
|
|
2997
|
+
execute: (args) => {
|
|
2998
|
+
const start = new Date(args['start']);
|
|
2999
|
+
const end = new Date(args['end']);
|
|
3000
|
+
zone.run(() => {
|
|
3001
|
+
drp.dateRangeService.range$.next({ start, end });
|
|
3002
|
+
});
|
|
3003
|
+
return { success: true, message: `Range set: ${start.toISOString().split('T')[0]} to ${end.toISOString().split('T')[0]}.` };
|
|
3004
|
+
}
|
|
3005
|
+
}));
|
|
3006
|
+
}
|
|
3007
|
+
if (enabled.has('clear')) {
|
|
3008
|
+
handles.push(mc.registerTool({
|
|
3009
|
+
name: `${prefix}-clear`,
|
|
3010
|
+
description: `Clear the ${label} value.`,
|
|
3011
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3012
|
+
execute: () => {
|
|
3013
|
+
zone.run(() => drp.dateRangeService.range$.next({ start: null, end: null }));
|
|
3014
|
+
return { success: true, message: 'Date range cleared.' };
|
|
3015
|
+
}
|
|
3016
|
+
}));
|
|
3017
|
+
}
|
|
3018
|
+
return handles;
|
|
3019
|
+
}
|
|
3020
|
+
buildToolOptions(config, label) {
|
|
3021
|
+
return [
|
|
3022
|
+
{ name: 'set-range', description: `Set the date range in the ${label}.`, enabled: true },
|
|
3023
|
+
{ name: 'clear', description: `Clear the ${label} value.`, enabled: true }
|
|
3024
|
+
];
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
// TimePicker
|
|
3028
|
+
/**
|
|
3029
|
+
* @hidden
|
|
3030
|
+
*/
|
|
3031
|
+
class TimePickerToolAdapter {
|
|
3032
|
+
selector = 'kendo-timepicker';
|
|
3033
|
+
registerTools(tp, config, mc, zone) {
|
|
3034
|
+
const prefix = config.dataName || 'kendo-timepicker';
|
|
3035
|
+
const label = config.dataName
|
|
3036
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' time picker'
|
|
3037
|
+
: 'Kendo UI TimePicker';
|
|
3038
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
3039
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
3040
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
3041
|
+
const handles = [];
|
|
3042
|
+
if (enabled.has('set-value')) {
|
|
3043
|
+
handles.push(mc.registerTool({
|
|
3044
|
+
name: `${prefix}-set-value`,
|
|
3045
|
+
description: `Set the time value of the ${label}.`,
|
|
3046
|
+
inputSchema: {
|
|
3047
|
+
type: 'object',
|
|
3048
|
+
properties: { time: { type: 'string', description: 'Time value (ISO 8601 or HH:mm format).' } },
|
|
3049
|
+
required: ['time']
|
|
3050
|
+
},
|
|
3051
|
+
execute: (args) => {
|
|
3052
|
+
const time = new Date(args['time']);
|
|
3053
|
+
zone.run(() => {
|
|
3054
|
+
tp.value = time;
|
|
3055
|
+
tp.valueChange.emit(time);
|
|
3056
|
+
tp.cdr.markForCheck();
|
|
3057
|
+
});
|
|
3058
|
+
return { success: true, message: `Time set.` };
|
|
3059
|
+
}
|
|
3060
|
+
}));
|
|
3061
|
+
}
|
|
3062
|
+
if (enabled.has('clear')) {
|
|
3063
|
+
handles.push(mc.registerTool({
|
|
3064
|
+
name: `${prefix}-clear`,
|
|
3065
|
+
description: `Clear the ${label} value.`,
|
|
3066
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3067
|
+
execute: () => {
|
|
3068
|
+
zone.run(() => {
|
|
3069
|
+
tp.value = null;
|
|
3070
|
+
tp.valueChange.emit(null);
|
|
3071
|
+
tp.cdr.markForCheck();
|
|
3072
|
+
});
|
|
3073
|
+
return { success: true, message: 'Time cleared.' };
|
|
3074
|
+
}
|
|
3075
|
+
}));
|
|
3076
|
+
}
|
|
3077
|
+
return handles;
|
|
3078
|
+
}
|
|
3079
|
+
buildToolOptions(config, label) {
|
|
3080
|
+
return [
|
|
3081
|
+
{ name: 'set-value', description: `Set the time value of the ${label}.`, enabled: true },
|
|
3082
|
+
{ name: 'clear', description: `Clear the ${label} value.`, enabled: true }
|
|
3083
|
+
];
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
// DateTimePicker
|
|
3087
|
+
/**
|
|
3088
|
+
* @hidden
|
|
3089
|
+
*/
|
|
3090
|
+
class DateTimePickerToolAdapter {
|
|
3091
|
+
selector = 'kendo-datetimepicker';
|
|
3092
|
+
registerTools(dtp, config, mc, zone) {
|
|
3093
|
+
const prefix = config.dataName || 'kendo-datetimepicker';
|
|
3094
|
+
const label = config.dataName
|
|
3095
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' date time picker'
|
|
3096
|
+
: 'Kendo UI DateTimePicker';
|
|
3097
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
3098
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
3099
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
3100
|
+
const handles = [];
|
|
3101
|
+
if (enabled.has('set-value')) {
|
|
3102
|
+
handles.push(mc.registerTool({
|
|
3103
|
+
name: `${prefix}-set-value`,
|
|
3104
|
+
description: `Set the date and time value of the ${label}.`,
|
|
3105
|
+
inputSchema: {
|
|
3106
|
+
type: 'object',
|
|
3107
|
+
properties: { datetime: { type: 'string', description: 'Date/time value (ISO 8601).' } },
|
|
3108
|
+
required: ['datetime']
|
|
3109
|
+
},
|
|
3110
|
+
execute: (args) => {
|
|
3111
|
+
const dt = new Date(args['datetime']);
|
|
3112
|
+
zone.run(() => {
|
|
3113
|
+
dtp.value = dt;
|
|
3114
|
+
dtp.valueChange.emit(dt);
|
|
3115
|
+
dtp.cdr.markForCheck();
|
|
3116
|
+
});
|
|
3117
|
+
return { success: true, message: `Date/time set.` };
|
|
3118
|
+
}
|
|
3119
|
+
}));
|
|
3120
|
+
}
|
|
3121
|
+
if (enabled.has('clear')) {
|
|
3122
|
+
handles.push(mc.registerTool({
|
|
3123
|
+
name: `${prefix}-clear`,
|
|
3124
|
+
description: `Clear the ${label} value.`,
|
|
3125
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3126
|
+
execute: () => {
|
|
3127
|
+
zone.run(() => {
|
|
3128
|
+
dtp.value = null;
|
|
3129
|
+
dtp.valueChange.emit(null);
|
|
3130
|
+
dtp.cdr.markForCheck();
|
|
3131
|
+
});
|
|
3132
|
+
return { success: true, message: 'Value cleared.' };
|
|
3133
|
+
}
|
|
3134
|
+
}));
|
|
3135
|
+
}
|
|
3136
|
+
return handles;
|
|
3137
|
+
}
|
|
3138
|
+
buildToolOptions(config, label) {
|
|
3139
|
+
return [
|
|
3140
|
+
{ name: 'set-value', description: `Set the date and time value of the ${label}.`, enabled: true },
|
|
3141
|
+
{ name: 'clear', description: `Clear the ${label} value.`, enabled: true }
|
|
3142
|
+
];
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
// Calendar
|
|
3146
|
+
/**
|
|
3147
|
+
* @hidden
|
|
3148
|
+
*/
|
|
3149
|
+
class CalendarToolAdapter {
|
|
3150
|
+
selector = 'kendo-calendar';
|
|
3151
|
+
registerTools(cal, config, mc, zone) {
|
|
3152
|
+
const prefix = config.dataName || 'kendo-calendar';
|
|
3153
|
+
const label = config.dataName
|
|
3154
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' calendar'
|
|
3155
|
+
: 'Kendo UI Calendar';
|
|
3156
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
3157
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
3158
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
3159
|
+
const handles = [];
|
|
3160
|
+
if (enabled.has('select-date')) {
|
|
3161
|
+
handles.push(mc.registerTool({
|
|
3162
|
+
name: `${prefix}-select-date`,
|
|
3163
|
+
description: `Select a date in the ${label}.`,
|
|
3164
|
+
inputSchema: {
|
|
3165
|
+
type: 'object',
|
|
3166
|
+
properties: { date: { type: 'string', description: 'Date to select (ISO 8601).' } },
|
|
3167
|
+
required: ['date']
|
|
3168
|
+
},
|
|
3169
|
+
execute: (args) => {
|
|
3170
|
+
const date = new Date(args['date']);
|
|
3171
|
+
zone.run(() => {
|
|
3172
|
+
cal.value = date;
|
|
3173
|
+
cal.valueChange.emit(date);
|
|
3174
|
+
cal.cdr.markForCheck();
|
|
3175
|
+
});
|
|
3176
|
+
return { success: true, message: `Selected ${date.toISOString().split('T')[0]}.` };
|
|
3177
|
+
}
|
|
3178
|
+
}));
|
|
3179
|
+
}
|
|
3180
|
+
if (enabled.has('navigate')) {
|
|
3181
|
+
handles.push(mc.registerTool({
|
|
3182
|
+
name: `${prefix}-navigate`,
|
|
3183
|
+
description: `Navigate the ${label} to a specific date.`,
|
|
3184
|
+
inputSchema: {
|
|
3185
|
+
type: 'object',
|
|
3186
|
+
properties: { date: { type: 'string', description: 'Date to navigate to (ISO 8601).' } },
|
|
3187
|
+
required: ['date']
|
|
3188
|
+
},
|
|
3189
|
+
execute: (args) => {
|
|
3190
|
+
const date = new Date(args['date']);
|
|
3191
|
+
zone.run(() => {
|
|
3192
|
+
cal.focusedDate = date;
|
|
3193
|
+
cal.cdr.markForCheck();
|
|
3194
|
+
});
|
|
3195
|
+
return { success: true, message: `Navigated to ${date.toISOString().split('T')[0]}.` };
|
|
3196
|
+
}
|
|
3197
|
+
}));
|
|
3198
|
+
}
|
|
3199
|
+
return handles;
|
|
3200
|
+
}
|
|
3201
|
+
buildToolOptions(config, label) {
|
|
3202
|
+
return [
|
|
3203
|
+
{ name: 'select-date', description: `Select a date in the ${label}.`, enabled: true },
|
|
3204
|
+
{ name: 'navigate', description: `Navigate the ${label} to a specific date.`, enabled: true }
|
|
3205
|
+
];
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
|
|
3209
|
+
/**
|
|
3210
|
+
* @hidden
|
|
3211
|
+
*
|
|
3212
|
+
* Helper to build a simple set-value + clear adapter for input components.
|
|
3213
|
+
*/
|
|
3214
|
+
function buildSimpleAdapter(selector, defaultPrefix, componentLabel, toolSuffixes, tools) {
|
|
3215
|
+
class Adapter {
|
|
3216
|
+
selector = selector;
|
|
3217
|
+
registerTools(component, config, mc, zone) {
|
|
3218
|
+
const prefix = config.dataName || defaultPrefix;
|
|
3219
|
+
const label = config.dataName
|
|
3220
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' ' + componentLabel
|
|
3221
|
+
: 'Kendo UI ' + componentLabel;
|
|
3222
|
+
const rawOptions = toolSuffixes.map(name => ({ name, description: name, enabled: true }));
|
|
3223
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
3224
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
3225
|
+
return tools(component, prefix, label, mc, zone, enabled);
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
return Adapter;
|
|
3229
|
+
}
|
|
3230
|
+
// Slider
|
|
3231
|
+
/** @hidden */
|
|
3232
|
+
const SliderToolAdapter = buildSimpleAdapter('kendo-slider', 'kendo-slider', 'Slider', ['set-value'], (slider, prefix, label, mc, zone, enabled) => {
|
|
3233
|
+
const handles = [];
|
|
3234
|
+
if (enabled.has('set-value')) {
|
|
3235
|
+
handles.push(mc.registerTool({
|
|
3236
|
+
name: `${prefix}-set-value`,
|
|
3237
|
+
description: `Set the value of the ${label}.`,
|
|
3238
|
+
inputSchema: {
|
|
3239
|
+
type: 'object',
|
|
3240
|
+
properties: { value: { type: 'number', description: 'Slider value.' } },
|
|
3241
|
+
required: ['value']
|
|
3242
|
+
},
|
|
3243
|
+
execute: (args) => {
|
|
3244
|
+
zone.run(() => {
|
|
3245
|
+
slider.changeValue(args['value']);
|
|
3246
|
+
});
|
|
3247
|
+
return { success: true, message: `Value set to ${args['value']}.` };
|
|
3248
|
+
}
|
|
3249
|
+
}));
|
|
3250
|
+
}
|
|
3251
|
+
return handles;
|
|
3252
|
+
});
|
|
3253
|
+
// RangeSlider
|
|
3254
|
+
/** @hidden */
|
|
3255
|
+
const RangeSliderToolAdapter = buildSimpleAdapter('kendo-rangeslider', 'kendo-rangeslider', 'RangeSlider', ['set-value'], (rs, prefix, label, mc, zone, enabled) => {
|
|
3256
|
+
const handles = [];
|
|
3257
|
+
if (enabled.has('set-value')) {
|
|
3258
|
+
handles.push(mc.registerTool({
|
|
3259
|
+
name: `${prefix}-set-value`,
|
|
3260
|
+
description: `Set the range value of the ${label}.`,
|
|
3261
|
+
inputSchema: {
|
|
3262
|
+
type: 'object',
|
|
3263
|
+
properties: {
|
|
3264
|
+
start: { type: 'number', description: 'Start value.' },
|
|
3265
|
+
end: { type: 'number', description: 'End value.' }
|
|
3266
|
+
},
|
|
3267
|
+
required: ['start', 'end']
|
|
3268
|
+
},
|
|
3269
|
+
execute: (args) => {
|
|
3270
|
+
zone.run(() => {
|
|
3271
|
+
rs.value = { start: args['start'], end: args['end'] };
|
|
3272
|
+
rs.valueChange.emit({ start: args['start'], end: args['end'] });
|
|
3273
|
+
});
|
|
3274
|
+
return { success: true, message: `Range set to ${args['start']}-${args['end']}.` };
|
|
3275
|
+
}
|
|
3276
|
+
}));
|
|
3277
|
+
}
|
|
3278
|
+
return handles;
|
|
3279
|
+
});
|
|
3280
|
+
// Switch
|
|
3281
|
+
/** @hidden */
|
|
3282
|
+
const SwitchToolAdapter = buildSimpleAdapter('kendo-switch', 'kendo-switch', 'Switch', ['toggle'], (sw, prefix, label, mc, zone, enabled) => {
|
|
3283
|
+
const handles = [];
|
|
3284
|
+
if (enabled.has('toggle')) {
|
|
3285
|
+
handles.push(mc.registerTool({
|
|
3286
|
+
name: `${prefix}-toggle`,
|
|
3287
|
+
description: `Toggle the ${label} on/off.`,
|
|
3288
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3289
|
+
execute: () => {
|
|
3290
|
+
const newValue = !sw.checked;
|
|
3291
|
+
zone.run(() => {
|
|
3292
|
+
sw.checked = newValue;
|
|
3293
|
+
sw.changeDetector.markForCheck();
|
|
3294
|
+
sw.valueChange.emit(newValue);
|
|
3295
|
+
});
|
|
3296
|
+
return { success: true, message: `Switched ${newValue ? 'on' : 'off'}.` };
|
|
3297
|
+
}
|
|
3298
|
+
}));
|
|
3299
|
+
}
|
|
3300
|
+
return handles;
|
|
3301
|
+
});
|
|
3302
|
+
// NumericTextBox
|
|
3303
|
+
/** @hidden */
|
|
3304
|
+
const NumericTextBoxToolAdapter = buildSimpleAdapter('kendo-numerictextbox', 'kendo-numerictextbox', 'NumericTextBox', ['set-value', 'increment', 'decrement', 'clear'], (ntb, prefix, label, mc, zone, enabled) => {
|
|
3305
|
+
const handles = [];
|
|
3306
|
+
if (enabled.has('set-value')) {
|
|
3307
|
+
handles.push(mc.registerTool({
|
|
3308
|
+
name: `${prefix}-set-value`,
|
|
3309
|
+
description: `Set the numeric value of the ${label}.`,
|
|
3310
|
+
inputSchema: {
|
|
3311
|
+
type: 'object',
|
|
3312
|
+
properties: { value: { type: 'number', description: 'Numeric value.' } },
|
|
3313
|
+
required: ['value']
|
|
3314
|
+
},
|
|
3315
|
+
execute: (args) => {
|
|
3316
|
+
zone.run(() => {
|
|
3317
|
+
ntb.value = args['value'];
|
|
3318
|
+
ntb.setInputValue();
|
|
3319
|
+
ntb.valueChange.emit(args['value']);
|
|
3320
|
+
});
|
|
3321
|
+
return { success: true, message: `Value set to ${args['value']}.` };
|
|
3322
|
+
}
|
|
3323
|
+
}));
|
|
3324
|
+
}
|
|
3325
|
+
if (enabled.has('increment')) {
|
|
3326
|
+
handles.push(mc.registerTool({
|
|
3327
|
+
name: `${prefix}-increment`,
|
|
3328
|
+
description: `Increment the value of the ${label} by one step.`,
|
|
3329
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3330
|
+
execute: () => {
|
|
3331
|
+
const current = ntb.value || 0;
|
|
3332
|
+
const step = ntb.step || 1;
|
|
3333
|
+
const newValue = current + step;
|
|
3334
|
+
zone.run(() => {
|
|
3335
|
+
ntb.value = newValue;
|
|
3336
|
+
ntb.setInputValue();
|
|
3337
|
+
ntb.valueChange.emit(newValue);
|
|
3338
|
+
});
|
|
3339
|
+
return { success: true, message: `Incremented to ${newValue}.` };
|
|
3340
|
+
}
|
|
3341
|
+
}));
|
|
3342
|
+
}
|
|
3343
|
+
if (enabled.has('decrement')) {
|
|
3344
|
+
handles.push(mc.registerTool({
|
|
3345
|
+
name: `${prefix}-decrement`,
|
|
3346
|
+
description: `Decrement the value of the ${label} by one step.`,
|
|
3347
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3348
|
+
execute: () => {
|
|
3349
|
+
const current = ntb.value || 0;
|
|
3350
|
+
const step = ntb.step || 1;
|
|
3351
|
+
const newValue = current - step;
|
|
3352
|
+
zone.run(() => {
|
|
3353
|
+
ntb.value = newValue;
|
|
3354
|
+
ntb.setInputValue();
|
|
3355
|
+
ntb.valueChange.emit(newValue);
|
|
3356
|
+
});
|
|
3357
|
+
return { success: true, message: `Decremented to ${newValue}.` };
|
|
3358
|
+
}
|
|
3359
|
+
}));
|
|
3360
|
+
}
|
|
3361
|
+
if (enabled.has('clear')) {
|
|
3362
|
+
handles.push(mc.registerTool({
|
|
3363
|
+
name: `${prefix}-clear`,
|
|
3364
|
+
description: `Clear the ${label} value.`,
|
|
3365
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3366
|
+
execute: () => {
|
|
3367
|
+
zone.run(() => {
|
|
3368
|
+
ntb.value = null;
|
|
3369
|
+
ntb.setInputValue();
|
|
3370
|
+
ntb.valueChange.emit(null);
|
|
3371
|
+
});
|
|
3372
|
+
return { success: true, message: 'Value cleared.' };
|
|
3373
|
+
}
|
|
3374
|
+
}));
|
|
3375
|
+
}
|
|
3376
|
+
return handles;
|
|
3377
|
+
});
|
|
3378
|
+
// ColorPicker
|
|
3379
|
+
/** @hidden */
|
|
3380
|
+
const ColorPickerToolAdapter = buildSimpleAdapter('kendo-colorpicker', 'kendo-colorpicker', 'ColorPicker', ['set-value', 'clear'], (cp, prefix, label, mc, zone, enabled) => {
|
|
3381
|
+
const handles = [];
|
|
3382
|
+
if (enabled.has('set-value')) {
|
|
3383
|
+
handles.push(mc.registerTool({
|
|
3384
|
+
name: `${prefix}-set-value`,
|
|
3385
|
+
description: `Set the color value of the ${label}.`,
|
|
3386
|
+
inputSchema: {
|
|
3387
|
+
type: 'object',
|
|
3388
|
+
properties: { color: { type: 'string', description: 'Color value (hex, rgb, or named color).' } },
|
|
3389
|
+
required: ['color']
|
|
3390
|
+
},
|
|
3391
|
+
execute: (args) => {
|
|
3392
|
+
zone.run(() => {
|
|
3393
|
+
cp.value = args['color'];
|
|
3394
|
+
cp.valueChange.emit(args['color']);
|
|
3395
|
+
});
|
|
3396
|
+
return { success: true, message: `Color set to "${args['color']}".` };
|
|
3397
|
+
}
|
|
3398
|
+
}));
|
|
3399
|
+
}
|
|
3400
|
+
if (enabled.has('clear')) {
|
|
3401
|
+
handles.push(mc.registerTool({
|
|
3402
|
+
name: `${prefix}-clear`,
|
|
3403
|
+
description: `Clear the ${label} value.`,
|
|
3404
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3405
|
+
execute: () => {
|
|
3406
|
+
zone.run(() => {
|
|
3407
|
+
cp.value = '';
|
|
3408
|
+
cp.valueChange.emit('');
|
|
3409
|
+
});
|
|
3410
|
+
return { success: true, message: 'Color cleared.' };
|
|
3411
|
+
}
|
|
3412
|
+
}));
|
|
3413
|
+
}
|
|
3414
|
+
return handles;
|
|
3415
|
+
});
|
|
3416
|
+
// TextBox
|
|
3417
|
+
/** @hidden */
|
|
3418
|
+
const TextBoxToolAdapter = buildSimpleAdapter('kendo-textbox', 'kendo-textbox', 'TextBox', ['set-value', 'clear'], (tb, prefix, label, mc, zone, enabled) => {
|
|
3419
|
+
const handles = [];
|
|
3420
|
+
if (enabled.has('set-value')) {
|
|
3421
|
+
handles.push(mc.registerTool({
|
|
3422
|
+
name: `${prefix}-set-value`,
|
|
3423
|
+
description: `Set the text value of the ${label}.`,
|
|
3424
|
+
inputSchema: {
|
|
3425
|
+
type: 'object',
|
|
3426
|
+
properties: { text: { type: 'string', description: 'Text to set.' } },
|
|
3427
|
+
required: ['text']
|
|
3428
|
+
},
|
|
3429
|
+
execute: (args) => {
|
|
3430
|
+
zone.run(() => {
|
|
3431
|
+
tb.value = args['text'];
|
|
3432
|
+
tb.valueChange.emit(args['text']);
|
|
3433
|
+
});
|
|
3434
|
+
return { success: true, message: `Value set.` };
|
|
3435
|
+
}
|
|
3436
|
+
}));
|
|
3437
|
+
}
|
|
3438
|
+
if (enabled.has('clear')) {
|
|
3439
|
+
handles.push(mc.registerTool({
|
|
3440
|
+
name: `${prefix}-clear`,
|
|
3441
|
+
description: `Clear the ${label} value.`,
|
|
3442
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3443
|
+
execute: () => {
|
|
3444
|
+
zone.run(() => {
|
|
3445
|
+
tb.value = '';
|
|
3446
|
+
tb.valueChange.emit('');
|
|
3447
|
+
});
|
|
3448
|
+
return { success: true, message: 'Value cleared.' };
|
|
3449
|
+
}
|
|
3450
|
+
}));
|
|
3451
|
+
}
|
|
3452
|
+
return handles;
|
|
3453
|
+
});
|
|
3454
|
+
// TextArea
|
|
3455
|
+
/** @hidden */
|
|
3456
|
+
const TextAreaToolAdapter = buildSimpleAdapter('kendo-textarea', 'kendo-textarea', 'TextArea', ['set-value', 'clear'], (ta, prefix, label, mc, zone, enabled) => {
|
|
3457
|
+
const handles = [];
|
|
3458
|
+
if (enabled.has('set-value')) {
|
|
3459
|
+
handles.push(mc.registerTool({
|
|
3460
|
+
name: `${prefix}-set-value`,
|
|
3461
|
+
description: `Set the text value of the ${label}.`,
|
|
3462
|
+
inputSchema: {
|
|
3463
|
+
type: 'object',
|
|
3464
|
+
properties: { text: { type: 'string', description: 'Text to set.' } },
|
|
3465
|
+
required: ['text']
|
|
3466
|
+
},
|
|
3467
|
+
execute: (args) => {
|
|
3468
|
+
zone.run(() => {
|
|
3469
|
+
ta.value = args['text'];
|
|
3470
|
+
ta.valueChange.emit(args['text']);
|
|
3471
|
+
});
|
|
3472
|
+
return { success: true, message: `Value set.` };
|
|
3473
|
+
}
|
|
3474
|
+
}));
|
|
3475
|
+
}
|
|
3476
|
+
if (enabled.has('clear')) {
|
|
3477
|
+
handles.push(mc.registerTool({
|
|
3478
|
+
name: `${prefix}-clear`,
|
|
3479
|
+
description: `Clear the ${label} value.`,
|
|
3480
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3481
|
+
execute: () => {
|
|
3482
|
+
zone.run(() => {
|
|
3483
|
+
ta.value = '';
|
|
3484
|
+
ta.valueChange.emit('');
|
|
3485
|
+
});
|
|
3486
|
+
return { success: true, message: 'Value cleared.' };
|
|
3487
|
+
}
|
|
3488
|
+
}));
|
|
3489
|
+
}
|
|
3490
|
+
return handles;
|
|
3491
|
+
});
|
|
3492
|
+
// MaskedTextBox
|
|
3493
|
+
/** @hidden */
|
|
3494
|
+
const MaskedTextBoxToolAdapter = buildSimpleAdapter('kendo-maskedtextbox', 'kendo-maskedtextbox', 'MaskedTextBox', ['set-value', 'clear'], (mtb, prefix, label, mc, zone, enabled) => {
|
|
3495
|
+
const handles = [];
|
|
3496
|
+
if (enabled.has('set-value')) {
|
|
3497
|
+
handles.push(mc.registerTool({
|
|
3498
|
+
name: `${prefix}-set-value`,
|
|
3499
|
+
description: `Set the value of the ${label}.`,
|
|
3500
|
+
inputSchema: {
|
|
3501
|
+
type: 'object',
|
|
3502
|
+
properties: { value: { type: 'string', description: 'Masked value.' } },
|
|
3503
|
+
required: ['value']
|
|
3504
|
+
},
|
|
3505
|
+
execute: (args) => {
|
|
3506
|
+
zone.run(() => {
|
|
3507
|
+
mtb.value = args['value'];
|
|
3508
|
+
mtb.valueChange.emit(args['value']);
|
|
3509
|
+
});
|
|
3510
|
+
return { success: true, message: `Value set.` };
|
|
3511
|
+
}
|
|
3512
|
+
}));
|
|
3513
|
+
}
|
|
3514
|
+
if (enabled.has('clear')) {
|
|
3515
|
+
handles.push(mc.registerTool({
|
|
3516
|
+
name: `${prefix}-clear`,
|
|
3517
|
+
description: `Clear the ${label} value.`,
|
|
3518
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3519
|
+
execute: () => {
|
|
3520
|
+
zone.run(() => {
|
|
3521
|
+
mtb.value = '';
|
|
3522
|
+
mtb.valueChange.emit('');
|
|
3523
|
+
});
|
|
3524
|
+
return { success: true, message: 'Value cleared.' };
|
|
3525
|
+
}
|
|
3526
|
+
}));
|
|
3527
|
+
}
|
|
3528
|
+
return handles;
|
|
3529
|
+
});
|
|
3530
|
+
// Signature
|
|
3531
|
+
/** @hidden */
|
|
3532
|
+
const SignatureToolAdapter = buildSimpleAdapter('kendo-signature', 'kendo-signature', 'Signature', ['clear'], (sig, prefix, label, mc, zone, enabled) => {
|
|
3533
|
+
const handles = [];
|
|
3534
|
+
if (enabled.has('clear')) {
|
|
3535
|
+
handles.push(mc.registerTool({
|
|
3536
|
+
name: `${prefix}-clear`,
|
|
3537
|
+
description: `Clear the ${label}.`,
|
|
3538
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3539
|
+
execute: () => {
|
|
3540
|
+
zone.run(() => {
|
|
3541
|
+
sig.value = '';
|
|
3542
|
+
sig.valueChange.emit('');
|
|
3543
|
+
});
|
|
3544
|
+
return { success: true, message: 'Signature cleared.' };
|
|
3545
|
+
}
|
|
3546
|
+
}));
|
|
3547
|
+
}
|
|
3548
|
+
return handles;
|
|
3549
|
+
});
|
|
3550
|
+
// Rating
|
|
3551
|
+
/** @hidden */
|
|
3552
|
+
const RatingToolAdapter = buildSimpleAdapter('kendo-rating', 'kendo-rating', 'Rating', ['set-value', 'clear'], (rating, prefix, label, mc, zone, enabled) => {
|
|
3553
|
+
const handles = [];
|
|
3554
|
+
if (enabled.has('set-value')) {
|
|
3555
|
+
handles.push(mc.registerTool({
|
|
3556
|
+
name: `${prefix}-set-value`,
|
|
3557
|
+
description: `Set the rating value of the ${label}.`,
|
|
3558
|
+
inputSchema: {
|
|
3559
|
+
type: 'object',
|
|
3560
|
+
properties: { value: { type: 'number', description: 'Rating value (e.g. 1-5).' } },
|
|
3561
|
+
required: ['value']
|
|
3562
|
+
},
|
|
3563
|
+
execute: (args) => {
|
|
3564
|
+
zone.run(() => {
|
|
3565
|
+
rating.value = args['value'];
|
|
3566
|
+
rating.valueChange.emit(args['value']);
|
|
3567
|
+
});
|
|
3568
|
+
return { success: true, message: `Rating set to ${args['value']}.` };
|
|
3569
|
+
}
|
|
3570
|
+
}));
|
|
3571
|
+
}
|
|
3572
|
+
if (enabled.has('clear')) {
|
|
3573
|
+
handles.push(mc.registerTool({
|
|
3574
|
+
name: `${prefix}-clear`,
|
|
3575
|
+
description: `Clear the ${label} value.`,
|
|
3576
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3577
|
+
execute: () => {
|
|
3578
|
+
zone.run(() => {
|
|
3579
|
+
rating.value = null;
|
|
3580
|
+
rating.valueChange.emit(null);
|
|
3581
|
+
});
|
|
3582
|
+
return { success: true, message: 'Rating cleared.' };
|
|
3583
|
+
}
|
|
3584
|
+
}));
|
|
3585
|
+
}
|
|
3586
|
+
return handles;
|
|
3587
|
+
});
|
|
3588
|
+
|
|
3589
|
+
// TabStrip
|
|
3590
|
+
/**
|
|
3591
|
+
* @hidden
|
|
3592
|
+
*/
|
|
3593
|
+
class TabStripToolAdapter {
|
|
3594
|
+
selector = 'kendo-tabstrip';
|
|
3595
|
+
registerTools(tabs, config, mc, zone) {
|
|
3596
|
+
const prefix = config.dataName || 'kendo-tabstrip';
|
|
3597
|
+
const label = config.dataName
|
|
3598
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' tab strip'
|
|
3599
|
+
: 'Kendo UI TabStrip';
|
|
3600
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
3601
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
3602
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
3603
|
+
const handles = [];
|
|
3604
|
+
if (enabled.has('select-tab')) {
|
|
3605
|
+
handles.push(mc.registerTool({
|
|
3606
|
+
name: `${prefix}-select-tab`,
|
|
3607
|
+
description: `Select a tab in the ${label} by title or index.`,
|
|
3608
|
+
inputSchema: {
|
|
3609
|
+
type: 'object',
|
|
3610
|
+
properties: {
|
|
3611
|
+
tab: { type: 'string', description: 'Tab title or 0-based index.' }
|
|
3612
|
+
},
|
|
3613
|
+
required: ['tab']
|
|
3614
|
+
},
|
|
3615
|
+
execute: (args) => {
|
|
3616
|
+
const tab = args['tab'];
|
|
3617
|
+
const idx = parseInt(tab, 10);
|
|
3618
|
+
zone.run(() => {
|
|
3619
|
+
if (!isNaN(idx)) {
|
|
3620
|
+
tabs.selectTab(idx);
|
|
3621
|
+
}
|
|
3622
|
+
else {
|
|
3623
|
+
const items = tabs.tabs?.toArray() || [];
|
|
3624
|
+
const found = items.findIndex((t) => t.title === tab);
|
|
3625
|
+
if (found >= 0) {
|
|
3626
|
+
tabs.selectTab(found);
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
});
|
|
3630
|
+
return { success: true, message: `Selected tab "${tab}".` };
|
|
3631
|
+
}
|
|
3632
|
+
}));
|
|
3633
|
+
}
|
|
3634
|
+
return handles;
|
|
3635
|
+
}
|
|
3636
|
+
buildToolOptions(config, label) {
|
|
3637
|
+
return [{ name: 'select-tab', description: `Select a tab in the ${label} by title or index.`, enabled: true }];
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
// PanelBar
|
|
3641
|
+
/**
|
|
3642
|
+
* @hidden
|
|
3643
|
+
*/
|
|
3644
|
+
class PanelBarToolAdapter {
|
|
3645
|
+
selector = 'kendo-panelbar';
|
|
3646
|
+
registerTools(pb, config, mc, zone) {
|
|
3647
|
+
const prefix = config.dataName || 'kendo-panelbar';
|
|
3648
|
+
const label = config.dataName
|
|
3649
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' panel bar'
|
|
3650
|
+
: 'Kendo UI PanelBar';
|
|
3651
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
3652
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
3653
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
3654
|
+
const handles = [];
|
|
3655
|
+
if (enabled.has('expand')) {
|
|
3656
|
+
handles.push(mc.registerTool({
|
|
3657
|
+
name: `${prefix}-expand`,
|
|
3658
|
+
description: `Expand a panel in the ${label}.`,
|
|
3659
|
+
inputSchema: {
|
|
3660
|
+
type: 'object',
|
|
3661
|
+
properties: { id: { type: 'string', description: 'Panel identifier or title.' } },
|
|
3662
|
+
required: ['id']
|
|
3663
|
+
},
|
|
3664
|
+
execute: (args) => {
|
|
3665
|
+
const id = args['id'];
|
|
3666
|
+
zone.run(() => {
|
|
3667
|
+
const items = pb.allItems || [];
|
|
3668
|
+
const item = items.find((i) => i.id === id || i.title === id);
|
|
3669
|
+
if (item && !item.expanded) {
|
|
3670
|
+
pb.onItemAction(item);
|
|
3671
|
+
}
|
|
3672
|
+
});
|
|
3673
|
+
return { success: true, message: `Panel "${id}" expanded.` };
|
|
3674
|
+
}
|
|
3675
|
+
}));
|
|
3676
|
+
}
|
|
3677
|
+
if (enabled.has('collapse')) {
|
|
3678
|
+
handles.push(mc.registerTool({
|
|
3679
|
+
name: `${prefix}-collapse`,
|
|
3680
|
+
description: `Collapse a panel in the ${label}.`,
|
|
3681
|
+
inputSchema: {
|
|
3682
|
+
type: 'object',
|
|
3683
|
+
properties: { id: { type: 'string', description: 'Panel identifier or title.' } },
|
|
3684
|
+
required: ['id']
|
|
3685
|
+
},
|
|
3686
|
+
execute: (args) => {
|
|
3687
|
+
const id = args['id'];
|
|
3688
|
+
zone.run(() => {
|
|
3689
|
+
const items = pb.allItems || [];
|
|
3690
|
+
const item = items.find((i) => i.id === id || i.title === id);
|
|
3691
|
+
if (item?.expanded) {
|
|
3692
|
+
pb.onItemAction(item);
|
|
3693
|
+
}
|
|
3694
|
+
});
|
|
3695
|
+
return { success: true, message: `Panel "${id}" collapsed.` };
|
|
3696
|
+
}
|
|
3697
|
+
}));
|
|
3698
|
+
}
|
|
3699
|
+
return handles;
|
|
3700
|
+
}
|
|
3701
|
+
buildToolOptions(config, label) {
|
|
3702
|
+
return [
|
|
3703
|
+
{ name: 'expand', description: `Expand a panel in the ${label}.`, enabled: true },
|
|
3704
|
+
{ name: 'collapse', description: `Collapse a panel in the ${label}.`, enabled: true }
|
|
3705
|
+
];
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
// Drawer
|
|
3709
|
+
/**
|
|
3710
|
+
* @hidden
|
|
3711
|
+
*/
|
|
3712
|
+
class DrawerToolAdapter {
|
|
3713
|
+
selector = 'kendo-drawer';
|
|
3714
|
+
registerTools(drawer, config, mc, zone) {
|
|
3715
|
+
const prefix = config.dataName || 'kendo-drawer';
|
|
3716
|
+
const label = config.dataName
|
|
3717
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' drawer'
|
|
3718
|
+
: 'Kendo UI Drawer';
|
|
3719
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
3720
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
3721
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
3722
|
+
const handles = [];
|
|
3723
|
+
if (enabled.has('open')) {
|
|
3724
|
+
handles.push(mc.registerTool({
|
|
3725
|
+
name: `${prefix}-open`,
|
|
3726
|
+
description: `Open the ${label}.`,
|
|
3727
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3728
|
+
execute: () => {
|
|
3729
|
+
zone.run(() => {
|
|
3730
|
+
drawer.expanded = true;
|
|
3731
|
+
drawer.expandedChange?.emit(true);
|
|
3732
|
+
});
|
|
3733
|
+
return { success: true, message: 'Drawer opened.' };
|
|
3734
|
+
}
|
|
3735
|
+
}));
|
|
3736
|
+
}
|
|
3737
|
+
if (enabled.has('close')) {
|
|
3738
|
+
handles.push(mc.registerTool({
|
|
3739
|
+
name: `${prefix}-close`,
|
|
3740
|
+
description: `Close the ${label}.`,
|
|
3741
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3742
|
+
execute: () => {
|
|
3743
|
+
zone.run(() => {
|
|
3744
|
+
drawer.expanded = false;
|
|
3745
|
+
drawer.expandedChange?.emit(false);
|
|
3746
|
+
});
|
|
3747
|
+
return { success: true, message: 'Drawer closed.' };
|
|
3748
|
+
}
|
|
3749
|
+
}));
|
|
3750
|
+
}
|
|
3751
|
+
return handles;
|
|
3752
|
+
}
|
|
3753
|
+
buildToolOptions(config, label) {
|
|
3754
|
+
return [
|
|
3755
|
+
{ name: 'open', description: `Open the ${label}.`, enabled: true },
|
|
3756
|
+
{ name: 'close', description: `Close the ${label}.`, enabled: true }
|
|
3757
|
+
];
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
// Stepper
|
|
3761
|
+
/**
|
|
3762
|
+
* @hidden
|
|
3763
|
+
*/
|
|
3764
|
+
class StepperToolAdapter {
|
|
3765
|
+
selector = 'kendo-stepper';
|
|
3766
|
+
registerTools(stepper, config, mc, zone) {
|
|
3767
|
+
const prefix = config.dataName || 'kendo-stepper';
|
|
3768
|
+
const label = config.dataName
|
|
3769
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' stepper'
|
|
3770
|
+
: 'Kendo UI Stepper';
|
|
3771
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
3772
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
3773
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
3774
|
+
const handles = [];
|
|
3775
|
+
if (enabled.has('step')) {
|
|
3776
|
+
handles.push(mc.registerTool({
|
|
3777
|
+
name: `${prefix}-step`,
|
|
3778
|
+
description: `Navigate to a step in the ${label} by 0-based index.`,
|
|
3779
|
+
inputSchema: {
|
|
3780
|
+
type: 'object',
|
|
3781
|
+
properties: {
|
|
3782
|
+
step: { type: 'number', description: '0-based step index to navigate to.' }
|
|
3783
|
+
},
|
|
3784
|
+
required: ['step']
|
|
3785
|
+
},
|
|
3786
|
+
execute: (args) => {
|
|
3787
|
+
const step = args['step'];
|
|
3788
|
+
zone.run(() => {
|
|
3789
|
+
stepper.currentStep = step;
|
|
3790
|
+
stepper.activate.emit({ index: step });
|
|
3791
|
+
});
|
|
3792
|
+
return { success: true, message: `Navigated to step ${step}.` };
|
|
3793
|
+
}
|
|
3794
|
+
}));
|
|
3795
|
+
}
|
|
3796
|
+
return handles;
|
|
3797
|
+
}
|
|
3798
|
+
buildToolOptions(config, label) {
|
|
3799
|
+
return [{ name: 'step', description: `Navigate to a step in the ${label} by 0-based index.`, enabled: true }];
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
|
|
3803
|
+
// Dialog
|
|
3804
|
+
/**
|
|
3805
|
+
* @hidden
|
|
3806
|
+
*/
|
|
3807
|
+
class DialogToolAdapter {
|
|
3808
|
+
selector = 'kendo-dialog';
|
|
3809
|
+
registerTools(dialog, config, mc, zone) {
|
|
3810
|
+
const prefix = config.dataName || 'kendo-dialog';
|
|
3811
|
+
const label = config.dataName
|
|
3812
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' dialog'
|
|
3813
|
+
: 'Kendo UI Dialog';
|
|
3814
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
3815
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
3816
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
3817
|
+
const handles = [];
|
|
3818
|
+
if (enabled.has('confirm')) {
|
|
3819
|
+
handles.push(mc.registerTool({
|
|
3820
|
+
name: `${prefix}-confirm`,
|
|
3821
|
+
description: `Confirm/accept the ${label}.`,
|
|
3822
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3823
|
+
execute: () => {
|
|
3824
|
+
zone.run(() => dialog.action.emit({ text: 'confirm' }));
|
|
3825
|
+
return { success: true, message: 'Dialog confirmed.' };
|
|
3826
|
+
}
|
|
3827
|
+
}));
|
|
3828
|
+
}
|
|
3829
|
+
if (enabled.has('cancel')) {
|
|
3830
|
+
handles.push(mc.registerTool({
|
|
3831
|
+
name: `${prefix}-cancel`,
|
|
3832
|
+
description: `Cancel/close the ${label}.`,
|
|
3833
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3834
|
+
execute: () => {
|
|
3835
|
+
zone.run(() => dialog.close.emit());
|
|
3836
|
+
return { success: true, message: 'Dialog cancelled.' };
|
|
3837
|
+
}
|
|
3838
|
+
}));
|
|
3839
|
+
}
|
|
3840
|
+
return handles;
|
|
3841
|
+
}
|
|
3842
|
+
buildToolOptions(config, label) {
|
|
3843
|
+
return [
|
|
3844
|
+
{ name: 'confirm', description: `Confirm/accept the ${label}.`, enabled: true },
|
|
3845
|
+
{ name: 'cancel', description: `Cancel/close the ${label}.`, enabled: true }
|
|
3846
|
+
];
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
// Window
|
|
3850
|
+
/**
|
|
3851
|
+
* @hidden
|
|
3852
|
+
*/
|
|
3853
|
+
class WindowToolAdapter {
|
|
3854
|
+
selector = 'kendo-window';
|
|
3855
|
+
registerTools(win, config, mc, zone) {
|
|
3856
|
+
const prefix = config.dataName || 'kendo-window';
|
|
3857
|
+
const label = config.dataName
|
|
3858
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' window'
|
|
3859
|
+
: 'Kendo UI Window';
|
|
3860
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
3861
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
3862
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
3863
|
+
const handles = [];
|
|
3864
|
+
if (enabled.has('open')) {
|
|
3865
|
+
handles.push(mc.registerTool({
|
|
3866
|
+
name: `${prefix}-open`,
|
|
3867
|
+
description: `Open/restore the ${label}.`,
|
|
3868
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3869
|
+
execute: () => {
|
|
3870
|
+
zone.run(() => {
|
|
3871
|
+
win.state = 'default';
|
|
3872
|
+
win.stateChange?.emit('default');
|
|
3873
|
+
});
|
|
3874
|
+
return { success: true, message: 'Window opened.' };
|
|
3875
|
+
}
|
|
3876
|
+
}));
|
|
3877
|
+
}
|
|
3878
|
+
if (enabled.has('close')) {
|
|
3879
|
+
handles.push(mc.registerTool({
|
|
3880
|
+
name: `${prefix}-close`,
|
|
3881
|
+
description: `Close the ${label}.`,
|
|
3882
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3883
|
+
execute: () => {
|
|
3884
|
+
zone.run(() => win.close.emit());
|
|
3885
|
+
return { success: true, message: 'Window closed.' };
|
|
3886
|
+
}
|
|
3887
|
+
}));
|
|
3888
|
+
}
|
|
3889
|
+
if (enabled.has('minimize')) {
|
|
3890
|
+
handles.push(mc.registerTool({
|
|
3891
|
+
name: `${prefix}-minimize`,
|
|
3892
|
+
description: `Minimize the ${label}.`,
|
|
3893
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3894
|
+
execute: () => {
|
|
3895
|
+
zone.run(() => {
|
|
3896
|
+
win.state = 'minimized';
|
|
3897
|
+
win.stateChange?.emit('minimized');
|
|
3898
|
+
});
|
|
3899
|
+
return { success: true, message: 'Window minimized.' };
|
|
3900
|
+
}
|
|
3901
|
+
}));
|
|
3902
|
+
}
|
|
3903
|
+
if (enabled.has('maximize')) {
|
|
3904
|
+
handles.push(mc.registerTool({
|
|
3905
|
+
name: `${prefix}-maximize`,
|
|
3906
|
+
description: `Maximize the ${label}.`,
|
|
3907
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3908
|
+
execute: () => {
|
|
3909
|
+
zone.run(() => {
|
|
3910
|
+
win.state = 'maximized';
|
|
3911
|
+
win.stateChange?.emit('maximized');
|
|
3912
|
+
});
|
|
3913
|
+
return { success: true, message: 'Window maximized.' };
|
|
3914
|
+
}
|
|
3915
|
+
}));
|
|
3916
|
+
}
|
|
3917
|
+
if (enabled.has('restore')) {
|
|
3918
|
+
handles.push(mc.registerTool({
|
|
3919
|
+
name: `${prefix}-restore`,
|
|
3920
|
+
description: `Restore the ${label} to its default size after being minimized or maximized.`,
|
|
3921
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3922
|
+
execute: () => {
|
|
3923
|
+
zone.run(() => {
|
|
3924
|
+
win.state = 'default';
|
|
3925
|
+
win.stateChange?.emit('default');
|
|
3926
|
+
});
|
|
3927
|
+
return { success: true, message: 'Window restored.' };
|
|
3928
|
+
}
|
|
3929
|
+
}));
|
|
3930
|
+
}
|
|
3931
|
+
return handles;
|
|
3932
|
+
}
|
|
3933
|
+
buildToolOptions(config, label) {
|
|
3934
|
+
return [
|
|
3935
|
+
{ name: 'open', description: `Open/restore the ${label}.`, enabled: true },
|
|
3936
|
+
{ name: 'close', description: `Close the ${label}.`, enabled: true },
|
|
3937
|
+
{ name: 'minimize', description: `Minimize the ${label}.`, enabled: true },
|
|
3938
|
+
{ name: 'maximize', description: `Maximize the ${label}.`, enabled: true },
|
|
3939
|
+
{ name: 'restore', description: `Restore the ${label} to its default size after being minimized or maximized.`, enabled: true }
|
|
3940
|
+
];
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
|
|
3944
|
+
// Chat
|
|
3945
|
+
/**
|
|
3946
|
+
* @hidden
|
|
3947
|
+
*/
|
|
3948
|
+
class ChatToolAdapter {
|
|
3949
|
+
selector = 'kendo-chat';
|
|
3950
|
+
registerTools(chat, config, mc, zone) {
|
|
3951
|
+
const prefix = config.dataName || 'kendo-chat';
|
|
3952
|
+
const label = config.dataName
|
|
3953
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' chat'
|
|
3954
|
+
: 'Kendo UI Chat';
|
|
3955
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
3956
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
3957
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
3958
|
+
const handles = [];
|
|
3959
|
+
if (enabled.has('send-message')) {
|
|
3960
|
+
handles.push(mc.registerTool({
|
|
3961
|
+
name: `${prefix}-send-message`,
|
|
3962
|
+
description: `Send a message in the ${label}.`,
|
|
3963
|
+
inputSchema: {
|
|
3964
|
+
type: 'object',
|
|
3965
|
+
properties: { text: { type: 'string', description: 'Message text.' } },
|
|
3966
|
+
required: ['text']
|
|
3967
|
+
},
|
|
3968
|
+
execute: (args) => {
|
|
3969
|
+
const timestamp = new Date();
|
|
3970
|
+
zone.run(() => {
|
|
3971
|
+
const msg = {
|
|
3972
|
+
author: { id: chat.authorId, name: 'User' },
|
|
3973
|
+
text: args['text'],
|
|
3974
|
+
timestamp
|
|
3975
|
+
};
|
|
3976
|
+
const updated = [...(chat.messages || []), msg];
|
|
3977
|
+
chat.messages = updated;
|
|
3978
|
+
if (chat.chatService) {
|
|
3979
|
+
chat.chatService.messages = chat.processedMessages;
|
|
3980
|
+
}
|
|
3981
|
+
chat.sendMessage.emit({ message: msg });
|
|
3982
|
+
});
|
|
3983
|
+
return { success: true, message: `Message sent. Author: "User" (id: ${chat.authorId}), Text: "${args['text']}", Timestamp: ${timestamp.toISOString()}.` };
|
|
3984
|
+
}
|
|
3985
|
+
}));
|
|
3986
|
+
}
|
|
3987
|
+
if (enabled.has('clear')) {
|
|
3988
|
+
handles.push(mc.registerTool({
|
|
3989
|
+
name: `${prefix}-clear`,
|
|
3990
|
+
description: `Clear the ${label} messages.`,
|
|
3991
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
3992
|
+
execute: () => {
|
|
3993
|
+
zone.run(() => {
|
|
3994
|
+
chat.messages = [];
|
|
3995
|
+
if (chat.chatService) {
|
|
3996
|
+
chat.chatService.messages = [];
|
|
3997
|
+
}
|
|
3998
|
+
});
|
|
3999
|
+
return { success: true, message: 'Chat cleared.' };
|
|
4000
|
+
}
|
|
4001
|
+
}));
|
|
4002
|
+
}
|
|
4003
|
+
return handles;
|
|
4004
|
+
}
|
|
4005
|
+
buildToolOptions(config, label) {
|
|
4006
|
+
return [
|
|
4007
|
+
{ name: 'send-message', description: `Send a message in the ${label}.`, enabled: true },
|
|
4008
|
+
{ name: 'clear', description: `Clear the ${label} messages.`, enabled: true }
|
|
4009
|
+
];
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
// AIPrompt
|
|
4013
|
+
/**
|
|
4014
|
+
* @hidden
|
|
4015
|
+
*/
|
|
4016
|
+
class AIPromptToolAdapter {
|
|
4017
|
+
selector = 'kendo-aiprompt';
|
|
4018
|
+
registerTools(prompt, config, mc, zone) {
|
|
4019
|
+
const prefix = config.dataName || 'kendo-aiprompt';
|
|
4020
|
+
const label = config.dataName
|
|
4021
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' AI prompt'
|
|
4022
|
+
: 'Kendo UI AIPrompt';
|
|
4023
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
4024
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
4025
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
4026
|
+
const handles = [];
|
|
4027
|
+
if (enabled.has('submit')) {
|
|
4028
|
+
handles.push(mc.registerTool({
|
|
4029
|
+
name: `${prefix}-submit`,
|
|
4030
|
+
description: `Submit a prompt to the ${label}.`,
|
|
4031
|
+
inputSchema: {
|
|
4032
|
+
type: 'object',
|
|
4033
|
+
properties: { text: { type: 'string', description: 'Prompt text.' } },
|
|
4034
|
+
required: ['text']
|
|
4035
|
+
},
|
|
4036
|
+
execute: (args) => {
|
|
4037
|
+
zone.run(() => prompt.promptRequest?.emit(args['text']));
|
|
4038
|
+
return { success: true, message: `Prompt submitted: "${args['text']}".` };
|
|
4039
|
+
}
|
|
4040
|
+
}));
|
|
4041
|
+
}
|
|
4042
|
+
if (enabled.has('clear')) {
|
|
4043
|
+
handles.push(mc.registerTool({
|
|
4044
|
+
name: `${prefix}-clear`,
|
|
4045
|
+
description: `Clear the ${label}.`,
|
|
4046
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
4047
|
+
execute: () => {
|
|
4048
|
+
return { success: true, message: 'AI Prompt cleared.' };
|
|
4049
|
+
}
|
|
4050
|
+
}));
|
|
4051
|
+
}
|
|
4052
|
+
return handles;
|
|
4053
|
+
}
|
|
4054
|
+
buildToolOptions(config, label) {
|
|
4055
|
+
return [
|
|
4056
|
+
{ name: 'submit', description: `Submit a prompt to the ${label}.`, enabled: true },
|
|
4057
|
+
{ name: 'clear', description: `Clear the ${label}.`, enabled: true }
|
|
4058
|
+
];
|
|
4059
|
+
}
|
|
4060
|
+
}
|
|
4061
|
+
|
|
4062
|
+
// TreeView
|
|
4063
|
+
/**
|
|
4064
|
+
* @hidden
|
|
4065
|
+
*/
|
|
4066
|
+
class TreeViewToolAdapter {
|
|
4067
|
+
selector = 'kendo-treeview';
|
|
4068
|
+
registerTools(tv, config, mc, zone) {
|
|
4069
|
+
const prefix = config.dataName || 'kendo-treeview';
|
|
4070
|
+
const label = config.dataName
|
|
4071
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' tree view'
|
|
4072
|
+
: 'Kendo UI TreeView';
|
|
4073
|
+
const rawOptions = this.buildToolOptions(tv, config, label);
|
|
4074
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
4075
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
4076
|
+
const handles = [];
|
|
4077
|
+
if (enabled.has('expand')) {
|
|
4078
|
+
handles.push(mc.registerTool({
|
|
4079
|
+
name: `${prefix}-expand`,
|
|
4080
|
+
description: `Expand a node in the ${label}.`,
|
|
4081
|
+
inputSchema: {
|
|
4082
|
+
type: 'object',
|
|
4083
|
+
properties: { id: { type: 'string', description: 'Node identifier to expand.' } },
|
|
4084
|
+
required: ['id']
|
|
4085
|
+
},
|
|
4086
|
+
execute: (args) => {
|
|
4087
|
+
zone.run(() => tv.expand.emit({ dataItem: { id: args['id'] }, index: args['id'] }));
|
|
4088
|
+
return { success: true, message: `Node "${args['id']}" expanded.` };
|
|
4089
|
+
}
|
|
4090
|
+
}));
|
|
4091
|
+
}
|
|
4092
|
+
if (enabled.has('collapse')) {
|
|
4093
|
+
handles.push(mc.registerTool({
|
|
4094
|
+
name: `${prefix}-collapse`,
|
|
4095
|
+
description: `Collapse a node in the ${label}.`,
|
|
4096
|
+
inputSchema: {
|
|
4097
|
+
type: 'object',
|
|
4098
|
+
properties: { id: { type: 'string', description: 'Node identifier to collapse.' } },
|
|
4099
|
+
required: ['id']
|
|
4100
|
+
},
|
|
4101
|
+
execute: (args) => {
|
|
4102
|
+
zone.run(() => tv.collapse.emit({ dataItem: { id: args['id'] }, index: args['id'] }));
|
|
4103
|
+
return { success: true, message: `Node "${args['id']}" collapsed.` };
|
|
4104
|
+
}
|
|
4105
|
+
}));
|
|
4106
|
+
}
|
|
4107
|
+
if (enabled.has('select')) {
|
|
4108
|
+
handles.push(mc.registerTool({
|
|
4109
|
+
name: `${prefix}-select`,
|
|
4110
|
+
description: `Select a node in the ${label}.`,
|
|
4111
|
+
inputSchema: {
|
|
4112
|
+
type: 'object',
|
|
4113
|
+
properties: { id: { type: 'string', description: 'Node identifier to select.' } },
|
|
4114
|
+
required: ['id']
|
|
4115
|
+
},
|
|
4116
|
+
execute: (args) => {
|
|
4117
|
+
zone.run(() => tv.selectionChange.emit({ dataItem: { text: args['id'] }, index: args['id'] }));
|
|
4118
|
+
return { success: true, message: `Node "${args['id']}" selected.` };
|
|
4119
|
+
}
|
|
4120
|
+
}));
|
|
4121
|
+
}
|
|
4122
|
+
if (enabled.has('check') && tv.checkboxes) {
|
|
4123
|
+
handles.push(mc.registerTool({
|
|
4124
|
+
name: `${prefix}-check`,
|
|
4125
|
+
description: `Toggle the checkbox of a node in the ${label}.`,
|
|
4126
|
+
inputSchema: {
|
|
4127
|
+
type: 'object',
|
|
4128
|
+
properties: { id: { type: 'string', description: 'Node identifier to check/uncheck.' } },
|
|
4129
|
+
required: ['id']
|
|
4130
|
+
},
|
|
4131
|
+
execute: (args) => {
|
|
4132
|
+
zone.run(() => {
|
|
4133
|
+
const lookup = tv.itemLookup(args['id']);
|
|
4134
|
+
if (lookup) {
|
|
4135
|
+
tv.checkedChange.emit(lookup);
|
|
4136
|
+
tv.changeDetectorRef.markForCheck();
|
|
4137
|
+
}
|
|
4138
|
+
});
|
|
4139
|
+
return { success: true, message: `Node "${args['id']}" toggled.` };
|
|
4140
|
+
}
|
|
4141
|
+
}));
|
|
4142
|
+
}
|
|
4143
|
+
if (enabled.has('filter') && tv.filterable) {
|
|
4144
|
+
handles.push(mc.registerTool({
|
|
4145
|
+
name: `${prefix}-filter`,
|
|
4146
|
+
description: `Filter nodes in the ${label}.`,
|
|
4147
|
+
inputSchema: {
|
|
4148
|
+
type: 'object',
|
|
4149
|
+
properties: { text: { type: 'string', description: 'Filter text.' } },
|
|
4150
|
+
required: ['text']
|
|
4151
|
+
},
|
|
4152
|
+
execute: (args) => {
|
|
4153
|
+
zone.run(() => {
|
|
4154
|
+
tv.filter = args['text'];
|
|
4155
|
+
tv.filterChange.emit(args['text']);
|
|
4156
|
+
tv.changeDetectorRef.markForCheck();
|
|
4157
|
+
});
|
|
4158
|
+
return { success: true, message: `Filtered by "${args['text']}".` };
|
|
4159
|
+
}
|
|
4160
|
+
}));
|
|
4161
|
+
}
|
|
4162
|
+
return handles;
|
|
4163
|
+
}
|
|
4164
|
+
buildToolOptions(tv, config, label) {
|
|
4165
|
+
return [
|
|
4166
|
+
{ name: 'expand', description: `Expand a node in the ${label}.`, enabled: true },
|
|
4167
|
+
{ name: 'collapse', description: `Collapse a node in the ${label}.`, enabled: true },
|
|
4168
|
+
{ name: 'select', description: `Select a node in the ${label}.`, enabled: true },
|
|
4169
|
+
{ name: 'check', description: `Toggle the checkbox of a node in the ${label}.`, enabled: !!tv.checkboxes },
|
|
4170
|
+
{ name: 'filter', description: `Filter nodes in the ${label}.`, enabled: !!tv.filterable }
|
|
4171
|
+
];
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
// ListBox
|
|
4175
|
+
/**
|
|
4176
|
+
* @hidden
|
|
4177
|
+
*/
|
|
4178
|
+
class ListBoxToolAdapter {
|
|
4179
|
+
selector = 'kendo-listbox';
|
|
4180
|
+
registerTools(lb, config, mc, zone) {
|
|
4181
|
+
const prefix = config.dataName || 'kendo-listbox';
|
|
4182
|
+
const label = config.dataName
|
|
4183
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' list box'
|
|
4184
|
+
: 'Kendo UI ListBox';
|
|
4185
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
4186
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
4187
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
4188
|
+
const handles = [];
|
|
4189
|
+
if (enabled.has('transfer')) {
|
|
4190
|
+
handles.push(mc.registerTool({
|
|
4191
|
+
name: `${prefix}-transfer`,
|
|
4192
|
+
description: `Transfer items from the ${label} to the connected list box. Selects items by index first, then transfers.`,
|
|
4193
|
+
inputSchema: {
|
|
4194
|
+
type: 'object',
|
|
4195
|
+
properties: {
|
|
4196
|
+
indices: { type: 'array', items: { type: 'number' }, description: 'Zero-based indices of items to transfer.' },
|
|
4197
|
+
direction: { type: 'string', enum: ['to', 'from'], description: 'Transfer direction: "to" or "from".' }
|
|
4198
|
+
},
|
|
4199
|
+
required: ['indices']
|
|
4200
|
+
},
|
|
4201
|
+
execute: (args) => {
|
|
4202
|
+
zone.run(() => {
|
|
4203
|
+
const indices = args['indices'] || [0];
|
|
4204
|
+
lb.select(indices);
|
|
4205
|
+
const action = args['direction'] === 'from' ? 'transferFrom' : 'transferTo';
|
|
4206
|
+
lb.performAction(action);
|
|
4207
|
+
});
|
|
4208
|
+
return { success: true, message: `Transferred item(s) at indices ${args['indices']}.` };
|
|
4209
|
+
}
|
|
4210
|
+
}));
|
|
4211
|
+
}
|
|
4212
|
+
if (enabled.has('reorder')) {
|
|
4213
|
+
handles.push(mc.registerTool({
|
|
4214
|
+
name: `${prefix}-reorder`,
|
|
4215
|
+
description: `Reorder an item in the ${label}. Selects the item by index first, then moves it.`,
|
|
4216
|
+
inputSchema: {
|
|
4217
|
+
type: 'object',
|
|
4218
|
+
properties: {
|
|
4219
|
+
index: { type: 'number', description: 'Zero-based index of item to move.' },
|
|
4220
|
+
direction: { type: 'string', enum: ['up', 'down'], description: 'Direction to move.' }
|
|
4221
|
+
},
|
|
4222
|
+
required: ['index', 'direction']
|
|
4223
|
+
},
|
|
4224
|
+
execute: (args) => {
|
|
4225
|
+
zone.run(() => {
|
|
4226
|
+
lb.select([args['index']]);
|
|
4227
|
+
lb.performAction((args['direction'] === 'up' ? 'moveUp' : 'moveDown'));
|
|
4228
|
+
});
|
|
4229
|
+
return { success: true, message: `Item at index ${args['index']} moved ${args['direction']}.` };
|
|
4230
|
+
}
|
|
4231
|
+
}));
|
|
4232
|
+
}
|
|
4233
|
+
return handles;
|
|
4234
|
+
}
|
|
4235
|
+
buildToolOptions(config, label) {
|
|
4236
|
+
return [
|
|
4237
|
+
{ name: 'transfer', description: `Transfer items in the ${label}.`, enabled: true },
|
|
4238
|
+
{ name: 'reorder', description: `Reorder an item in the ${label}.`, enabled: true }
|
|
4239
|
+
];
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
// ListView
|
|
4243
|
+
/**
|
|
4244
|
+
* @hidden
|
|
4245
|
+
*/
|
|
4246
|
+
class ListViewToolAdapter {
|
|
4247
|
+
selector = 'kendo-listview';
|
|
4248
|
+
registerTools(lv, config, mc, zone) {
|
|
4249
|
+
const prefix = config.dataName || 'kendo-listview';
|
|
4250
|
+
const label = config.dataName
|
|
4251
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' list view'
|
|
4252
|
+
: 'Kendo UI ListView';
|
|
4253
|
+
const rawOptions = this.buildToolOptions(lv, config, label);
|
|
4254
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
4255
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
4256
|
+
const handles = [];
|
|
4257
|
+
if (enabled.has('page') && lv.pageable) {
|
|
4258
|
+
handles.push(mc.registerTool({
|
|
4259
|
+
name: `${prefix}-page`,
|
|
4260
|
+
description: `Navigate to a page in the ${label}.`,
|
|
4261
|
+
inputSchema: {
|
|
4262
|
+
type: 'object',
|
|
4263
|
+
properties: { page: { type: 'number', description: '1-based page number.' } },
|
|
4264
|
+
required: ['page']
|
|
4265
|
+
},
|
|
4266
|
+
execute: (args) => {
|
|
4267
|
+
zone.run(() => lv.pageChange.emit({ skip: (args['page'] - 1) * (lv.pageSize || 10), take: lv.pageSize || 10 }));
|
|
4268
|
+
return { success: true, message: `Navigated to page ${args['page']}.` };
|
|
4269
|
+
}
|
|
4270
|
+
}));
|
|
4271
|
+
}
|
|
4272
|
+
return handles;
|
|
4273
|
+
}
|
|
4274
|
+
buildToolOptions(lv, config, label) {
|
|
4275
|
+
return [{ name: 'page', description: `Navigate to a page in the ${label}.`, enabled: !!lv.pageable }];
|
|
4276
|
+
}
|
|
4277
|
+
}
|
|
4278
|
+
// Menu
|
|
4279
|
+
/**
|
|
4280
|
+
* @hidden
|
|
4281
|
+
*/
|
|
4282
|
+
class MenuToolAdapter {
|
|
4283
|
+
selector = 'kendo-menu';
|
|
4284
|
+
registerTools(menu, config, mc, zone) {
|
|
4285
|
+
const prefix = config.dataName || 'kendo-menu';
|
|
4286
|
+
const label = config.dataName
|
|
4287
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' menu'
|
|
4288
|
+
: 'Kendo UI Menu';
|
|
4289
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
4290
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
4291
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
4292
|
+
const handles = [];
|
|
4293
|
+
if (enabled.has('select-item')) {
|
|
4294
|
+
handles.push(mc.registerTool({
|
|
4295
|
+
name: `${prefix}-select-item`,
|
|
4296
|
+
description: `Select a menu item in the ${label} by path.`,
|
|
4297
|
+
inputSchema: {
|
|
4298
|
+
type: 'object',
|
|
4299
|
+
properties: { path: { type: 'string', description: "Menu item path (e.g. 'File > Export > PDF')." } },
|
|
4300
|
+
required: ['path']
|
|
4301
|
+
},
|
|
4302
|
+
execute: (args) => {
|
|
4303
|
+
zone.run(() => menu.select.emit({ item: { text: args['path'] } }));
|
|
4304
|
+
return { success: true, message: `Selected "${args['path']}".` };
|
|
4305
|
+
}
|
|
4306
|
+
}));
|
|
4307
|
+
}
|
|
4308
|
+
return handles;
|
|
4309
|
+
}
|
|
4310
|
+
buildToolOptions(config, label) {
|
|
4311
|
+
return [{ name: 'select-item', description: `Select a menu item in the ${label} by path.`, enabled: true }];
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
// ScrollView
|
|
4315
|
+
/**
|
|
4316
|
+
* @hidden
|
|
4317
|
+
*/
|
|
4318
|
+
class ScrollViewToolAdapter {
|
|
4319
|
+
selector = 'kendo-scrollview';
|
|
4320
|
+
registerTools(sv, config, mc, zone) {
|
|
4321
|
+
const prefix = config.dataName || 'kendo-scrollview';
|
|
4322
|
+
const label = config.dataName
|
|
4323
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' scroll view'
|
|
4324
|
+
: 'Kendo UI ScrollView';
|
|
4325
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
4326
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
4327
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
4328
|
+
const handles = [];
|
|
4329
|
+
if (enabled.has('next')) {
|
|
4330
|
+
handles.push(mc.registerTool({
|
|
4331
|
+
name: `${prefix}-next`,
|
|
4332
|
+
description: `Navigate to the next slide in the ${label}.`,
|
|
4333
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
4334
|
+
execute: () => {
|
|
4335
|
+
zone.run(() => sv.next());
|
|
4336
|
+
return { success: true, message: 'Moved to next slide.' };
|
|
4337
|
+
}
|
|
4338
|
+
}));
|
|
4339
|
+
}
|
|
4340
|
+
if (enabled.has('previous')) {
|
|
4341
|
+
handles.push(mc.registerTool({
|
|
4342
|
+
name: `${prefix}-previous`,
|
|
4343
|
+
description: `Navigate to the previous slide in the ${label}.`,
|
|
4344
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
4345
|
+
execute: () => {
|
|
4346
|
+
zone.run(() => sv.prev());
|
|
4347
|
+
return { success: true, message: 'Moved to previous slide.' };
|
|
4348
|
+
}
|
|
4349
|
+
}));
|
|
4350
|
+
}
|
|
4351
|
+
if (enabled.has('navigate')) {
|
|
4352
|
+
handles.push(mc.registerTool({
|
|
4353
|
+
name: `${prefix}-navigate`,
|
|
4354
|
+
description: `Navigate to a specific slide in the ${label}.`,
|
|
4355
|
+
inputSchema: {
|
|
4356
|
+
type: 'object',
|
|
4357
|
+
properties: { index: { type: 'number', description: '0-based slide index.' } },
|
|
4358
|
+
required: ['index']
|
|
4359
|
+
},
|
|
4360
|
+
execute: (args) => {
|
|
4361
|
+
zone.run(() => sv.activeIndex = args['index']);
|
|
4362
|
+
return { success: true, message: `Navigated to slide ${args['index']}.` };
|
|
4363
|
+
}
|
|
4364
|
+
}));
|
|
4365
|
+
}
|
|
4366
|
+
return handles;
|
|
4367
|
+
}
|
|
4368
|
+
buildToolOptions(config, label) {
|
|
4369
|
+
return [
|
|
4370
|
+
{ name: 'next', description: `Navigate to the next slide in the ${label}.`, enabled: true },
|
|
4371
|
+
{ name: 'previous', description: `Navigate to the previous slide in the ${label}.`, enabled: true },
|
|
4372
|
+
{ name: 'navigate', description: `Navigate to a specific slide in the ${label}.`, enabled: true }
|
|
4373
|
+
];
|
|
4374
|
+
}
|
|
4375
|
+
}
|
|
4376
|
+
// Sortable
|
|
4377
|
+
/**
|
|
4378
|
+
* @hidden
|
|
4379
|
+
*/
|
|
4380
|
+
class SortableToolAdapter {
|
|
4381
|
+
selector = 'kendo-sortable';
|
|
4382
|
+
registerTools(sortable, config, mc, zone) {
|
|
4383
|
+
const prefix = config.dataName || 'kendo-sortable';
|
|
4384
|
+
const label = config.dataName
|
|
4385
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' sortable'
|
|
4386
|
+
: 'Kendo UI Sortable';
|
|
4387
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
4388
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
4389
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
4390
|
+
const handles = [];
|
|
4391
|
+
if (enabled.has('reorder')) {
|
|
4392
|
+
handles.push(mc.registerTool({
|
|
4393
|
+
name: `${prefix}-reorder`,
|
|
4394
|
+
description: `Reorder items in the ${label}.`,
|
|
4395
|
+
inputSchema: {
|
|
4396
|
+
type: 'object',
|
|
4397
|
+
properties: {
|
|
4398
|
+
fromIndex: { type: 'number', description: 'Source index.' },
|
|
4399
|
+
toIndex: { type: 'number', description: 'Destination index.' }
|
|
4400
|
+
},
|
|
4401
|
+
required: ['fromIndex', 'toIndex']
|
|
4402
|
+
},
|
|
4403
|
+
execute: (args) => {
|
|
4404
|
+
zone.run(() => sortable.moveItem(args['fromIndex'], args['toIndex']));
|
|
4405
|
+
return { success: true, message: `Moved item from ${args['fromIndex']} to ${args['toIndex']}.` };
|
|
4406
|
+
}
|
|
4407
|
+
}));
|
|
4408
|
+
}
|
|
4409
|
+
return handles;
|
|
4410
|
+
}
|
|
4411
|
+
buildToolOptions(config, label) {
|
|
4412
|
+
return [{ name: 'reorder', description: `Reorder items in the ${label}.`, enabled: true }];
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4415
|
+
// Map
|
|
4416
|
+
/**
|
|
4417
|
+
* @hidden
|
|
4418
|
+
*/
|
|
4419
|
+
class MapToolAdapter {
|
|
4420
|
+
selector = 'kendo-map';
|
|
4421
|
+
registerTools(map, config, mc, zone) {
|
|
4422
|
+
const prefix = config.dataName || 'kendo-map';
|
|
4423
|
+
const label = config.dataName
|
|
4424
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' map'
|
|
4425
|
+
: 'Kendo UI Map';
|
|
4426
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
4427
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
4428
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
4429
|
+
const handles = [];
|
|
4430
|
+
if (enabled.has('set-center')) {
|
|
4431
|
+
handles.push(mc.registerTool({
|
|
4432
|
+
name: `${prefix}-set-center`,
|
|
4433
|
+
description: `Set the center of the ${label}.`,
|
|
4434
|
+
inputSchema: {
|
|
4435
|
+
type: 'object',
|
|
4436
|
+
properties: {
|
|
4437
|
+
lat: { type: 'number', description: 'Latitude.' },
|
|
4438
|
+
lng: { type: 'number', description: 'Longitude.' }
|
|
4439
|
+
},
|
|
4440
|
+
required: ['lat', 'lng']
|
|
4441
|
+
},
|
|
4442
|
+
execute: (args) => {
|
|
4443
|
+
zone.run(() => {
|
|
4444
|
+
map.center = [args['lat'], args['lng']];
|
|
4445
|
+
if (map.instance) {
|
|
4446
|
+
map.instance.center([args['lat'], args['lng']]);
|
|
4447
|
+
}
|
|
4448
|
+
});
|
|
4449
|
+
return { success: true, message: `Center set to [${args['lat']}, ${args['lng']}].` };
|
|
4450
|
+
}
|
|
4451
|
+
}));
|
|
4452
|
+
}
|
|
4453
|
+
if (enabled.has('set-zoom')) {
|
|
4454
|
+
handles.push(mc.registerTool({
|
|
4455
|
+
name: `${prefix}-set-zoom`,
|
|
4456
|
+
description: `Set the zoom level of the ${label}.`,
|
|
4457
|
+
inputSchema: {
|
|
4458
|
+
type: 'object',
|
|
4459
|
+
properties: { zoom: { type: 'number', description: 'Zoom level (1-20).' } },
|
|
4460
|
+
required: ['zoom']
|
|
4461
|
+
},
|
|
4462
|
+
execute: (args) => {
|
|
4463
|
+
zone.run(() => {
|
|
4464
|
+
map.zoom = args['zoom'];
|
|
4465
|
+
if (map.instance) {
|
|
4466
|
+
map.instance.zoom(args['zoom']);
|
|
4467
|
+
}
|
|
4468
|
+
});
|
|
4469
|
+
return { success: true, message: `Zoom set to ${args['zoom']}.` };
|
|
4470
|
+
}
|
|
4471
|
+
}));
|
|
4472
|
+
}
|
|
4473
|
+
if (enabled.has('add-marker')) {
|
|
4474
|
+
handles.push(mc.registerTool({
|
|
4475
|
+
name: `${prefix}-add-marker`,
|
|
4476
|
+
description: `Add a marker to the ${label}.`,
|
|
4477
|
+
inputSchema: {
|
|
4478
|
+
type: 'object',
|
|
4479
|
+
properties: {
|
|
4480
|
+
lat: { type: 'number', description: 'Latitude.' },
|
|
4481
|
+
lng: { type: 'number', description: 'Longitude.' },
|
|
4482
|
+
title: { type: 'string', description: 'Marker tooltip (optional).' }
|
|
4483
|
+
},
|
|
4484
|
+
required: ['lat', 'lng']
|
|
4485
|
+
},
|
|
4486
|
+
execute: (args) => {
|
|
4487
|
+
zone.run(() => {
|
|
4488
|
+
map.instance?.markers?.add({ location: [args['lat'], args['lng']], tooltip: { content: args['title'] || '' } });
|
|
4489
|
+
});
|
|
4490
|
+
return { success: true, message: `Marker added at [${args['lat']}, ${args['lng']}].` };
|
|
4491
|
+
}
|
|
4492
|
+
}));
|
|
4493
|
+
}
|
|
4494
|
+
if (enabled.has('clear-markers')) {
|
|
4495
|
+
handles.push(mc.registerTool({
|
|
4496
|
+
name: `${prefix}-clear-markers`,
|
|
4497
|
+
description: `Clear all markers from the ${label}.`,
|
|
4498
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
4499
|
+
execute: () => {
|
|
4500
|
+
zone.run(() => {
|
|
4501
|
+
map.instance?.markers?.clear();
|
|
4502
|
+
});
|
|
4503
|
+
return { success: true, message: 'Markers cleared.' };
|
|
4504
|
+
}
|
|
4505
|
+
}));
|
|
4506
|
+
}
|
|
4507
|
+
return handles;
|
|
4508
|
+
}
|
|
4509
|
+
buildToolOptions(config, label) {
|
|
4510
|
+
return [
|
|
4511
|
+
{ name: 'set-center', description: `Set the center of the ${label}.`, enabled: true },
|
|
4512
|
+
{ name: 'set-zoom', description: `Set the zoom level of the ${label}.`, enabled: true },
|
|
4513
|
+
{ name: 'add-marker', description: `Add a marker to the ${label}.`, enabled: true },
|
|
4514
|
+
{ name: 'clear-markers', description: `Clear all markers from the ${label}.`, enabled: true }
|
|
4515
|
+
];
|
|
4516
|
+
}
|
|
4517
|
+
}
|
|
4518
|
+
// Notification
|
|
4519
|
+
/**
|
|
4520
|
+
* @hidden
|
|
4521
|
+
*/
|
|
4522
|
+
class NotificationToolAdapter {
|
|
4523
|
+
selector = 'kendo-notification';
|
|
4524
|
+
registerTools(notif, config, mc, zone) {
|
|
4525
|
+
const prefix = config.dataName || 'kendo-notification';
|
|
4526
|
+
const label = config.dataName
|
|
4527
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' notification'
|
|
4528
|
+
: 'Kendo UI Notification';
|
|
4529
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
4530
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
4531
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
4532
|
+
const handles = [];
|
|
4533
|
+
if (enabled.has('show')) {
|
|
4534
|
+
handles.push(mc.registerTool({
|
|
4535
|
+
name: `${prefix}-show`,
|
|
4536
|
+
description: `Show a notification in the ${label}.`,
|
|
4537
|
+
inputSchema: {
|
|
4538
|
+
type: 'object',
|
|
4539
|
+
properties: {
|
|
4540
|
+
text: { type: 'string', description: 'Notification text.' },
|
|
4541
|
+
type: { type: 'string', description: 'Notification type (success, info, warning, error).' }
|
|
4542
|
+
},
|
|
4543
|
+
required: ['text']
|
|
4544
|
+
},
|
|
4545
|
+
execute: (args) => {
|
|
4546
|
+
zone.run(() => {
|
|
4547
|
+
notif.templateString = args['text'];
|
|
4548
|
+
notif.type = { style: args['type'] || 'info', icon: true };
|
|
4549
|
+
notif.cdr.markForCheck();
|
|
4550
|
+
});
|
|
4551
|
+
return { success: true, message: `Notification shown: "${args['text']}".` };
|
|
4552
|
+
}
|
|
4553
|
+
}));
|
|
4554
|
+
}
|
|
4555
|
+
if (enabled.has('dismiss')) {
|
|
4556
|
+
handles.push(mc.registerTool({
|
|
4557
|
+
name: `${prefix}-dismiss`,
|
|
4558
|
+
description: `Dismiss all notifications in the ${label}.`,
|
|
4559
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
4560
|
+
execute: () => {
|
|
4561
|
+
zone.run(() => notif.hide());
|
|
4562
|
+
return { success: true, message: 'Notifications dismissed.' };
|
|
4563
|
+
}
|
|
4564
|
+
}));
|
|
4565
|
+
}
|
|
4566
|
+
return handles;
|
|
4567
|
+
}
|
|
4568
|
+
buildToolOptions(config, label) {
|
|
4569
|
+
return [
|
|
4570
|
+
{ name: 'show', description: `Show a notification in the ${label}.`, enabled: true },
|
|
4571
|
+
{ name: 'dismiss', description: `Dismiss all notifications in the ${label}.`, enabled: true }
|
|
4572
|
+
];
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
// Upload
|
|
4576
|
+
/**
|
|
4577
|
+
* @hidden
|
|
4578
|
+
*/
|
|
4579
|
+
class UploadToolAdapter {
|
|
4580
|
+
selector = 'kendo-upload';
|
|
4581
|
+
registerTools(upload, config, mc, zone) {
|
|
4582
|
+
const prefix = config.dataName || 'kendo-upload';
|
|
4583
|
+
const label = config.dataName
|
|
4584
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' upload'
|
|
4585
|
+
: 'Kendo UI Upload';
|
|
4586
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
4587
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
4588
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
4589
|
+
const handles = [];
|
|
4590
|
+
if (enabled.has('clear')) {
|
|
4591
|
+
handles.push(mc.registerTool({
|
|
4592
|
+
name: `${prefix}-clear`,
|
|
4593
|
+
description: `Clear all files from the ${label}.`,
|
|
4594
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
4595
|
+
execute: () => {
|
|
4596
|
+
zone.run(() => upload.clearFiles());
|
|
4597
|
+
return { success: true, message: 'Files cleared.' };
|
|
4598
|
+
}
|
|
4599
|
+
}));
|
|
4600
|
+
}
|
|
4601
|
+
return handles;
|
|
4602
|
+
}
|
|
4603
|
+
buildToolOptions(config, label) {
|
|
4604
|
+
return [{ name: 'clear', description: `Clear all files from the ${label}.`, enabled: true }];
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
// Button
|
|
4608
|
+
/**
|
|
4609
|
+
* @hidden
|
|
4610
|
+
*/
|
|
4611
|
+
class ButtonToolAdapter {
|
|
4612
|
+
selector = '[kendoButton]';
|
|
4613
|
+
registerTools(btn, config, mc, zone) {
|
|
4614
|
+
const prefix = config.dataName || 'kendo-button';
|
|
4615
|
+
const label = config.dataName
|
|
4616
|
+
? config.dataName.charAt(0).toUpperCase() + config.dataName.slice(1) + ' button'
|
|
4617
|
+
: 'Kendo UI Button';
|
|
4618
|
+
const rawOptions = this.buildToolOptions(config, label);
|
|
4619
|
+
const finalOptions = config.tools ? config.tools(rawOptions) : rawOptions;
|
|
4620
|
+
const enabled = new Set(finalOptions.filter(t => t.enabled !== false).map(t => t.name));
|
|
4621
|
+
const handles = [];
|
|
4622
|
+
if (enabled.has('click')) {
|
|
4623
|
+
handles.push(mc.registerTool({
|
|
4624
|
+
name: `${prefix}-click`,
|
|
4625
|
+
description: `Click the ${label}.`,
|
|
4626
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
4627
|
+
execute: () => {
|
|
4628
|
+
if (btn.disabled) {
|
|
4629
|
+
return { success: false, message: 'Button is disabled.' };
|
|
4630
|
+
}
|
|
4631
|
+
zone.run(() => btn.element.click());
|
|
4632
|
+
return { success: true, message: `Clicked the ${label}.` };
|
|
4633
|
+
}
|
|
4634
|
+
}));
|
|
4635
|
+
}
|
|
4636
|
+
return handles;
|
|
4637
|
+
}
|
|
4638
|
+
buildToolOptions(config, label) {
|
|
4639
|
+
return [{ name: 'click', description: `Click the ${label}.`, enabled: true }];
|
|
4640
|
+
}
|
|
4641
|
+
}
|
|
4642
|
+
|
|
4643
|
+
const ADAPTERS = [
|
|
4644
|
+
new GridToolAdapter(),
|
|
4645
|
+
new TreeListToolAdapter(),
|
|
4646
|
+
new SchedulerToolAdapter(),
|
|
4647
|
+
new ChartToolAdapter(),
|
|
4648
|
+
new EditorToolAdapter(),
|
|
4649
|
+
new SpreadsheetToolAdapter(),
|
|
4650
|
+
new GanttToolAdapter(),
|
|
4651
|
+
new PivotGridToolAdapter(),
|
|
4652
|
+
new DropDownListToolAdapter(),
|
|
4653
|
+
new ComboBoxToolAdapter(),
|
|
4654
|
+
new AutoCompleteToolAdapter(),
|
|
4655
|
+
new MultiSelectToolAdapter(),
|
|
4656
|
+
new DropDownTreeToolAdapter(),
|
|
4657
|
+
new MultiColumnComboBoxToolAdapter(),
|
|
4658
|
+
new DatePickerToolAdapter(),
|
|
4659
|
+
new DateRangeToolAdapter(),
|
|
4660
|
+
new TimePickerToolAdapter(),
|
|
4661
|
+
new DateTimePickerToolAdapter(),
|
|
4662
|
+
new CalendarToolAdapter(),
|
|
4663
|
+
new SliderToolAdapter(),
|
|
4664
|
+
new RangeSliderToolAdapter(),
|
|
4665
|
+
new SwitchToolAdapter(),
|
|
4666
|
+
new NumericTextBoxToolAdapter(),
|
|
4667
|
+
new ColorPickerToolAdapter(),
|
|
4668
|
+
new TextBoxToolAdapter(),
|
|
4669
|
+
new TextAreaToolAdapter(),
|
|
4670
|
+
new MaskedTextBoxToolAdapter(),
|
|
4671
|
+
new SignatureToolAdapter(),
|
|
4672
|
+
new RatingToolAdapter(),
|
|
4673
|
+
new TabStripToolAdapter(),
|
|
4674
|
+
new PanelBarToolAdapter(),
|
|
4675
|
+
new DrawerToolAdapter(),
|
|
4676
|
+
new StepperToolAdapter(),
|
|
4677
|
+
new DialogToolAdapter(),
|
|
4678
|
+
new WindowToolAdapter(),
|
|
4679
|
+
new ChatToolAdapter(),
|
|
4680
|
+
new AIPromptToolAdapter(),
|
|
4681
|
+
new TreeViewToolAdapter(),
|
|
4682
|
+
new ListBoxToolAdapter(),
|
|
4683
|
+
new ListViewToolAdapter(),
|
|
4684
|
+
new MenuToolAdapter(),
|
|
4685
|
+
new ScrollViewToolAdapter(),
|
|
4686
|
+
new SortableToolAdapter(),
|
|
4687
|
+
new MapToolAdapter(),
|
|
4688
|
+
new NotificationToolAdapter(),
|
|
4689
|
+
new UploadToolAdapter(),
|
|
4690
|
+
new ButtonToolAdapter()
|
|
4691
|
+
];
|
|
4692
|
+
/**
|
|
4693
|
+
* Registers Web MCP tools for the host Kendo Angular component.
|
|
4694
|
+
*
|
|
4695
|
+
* Apply this attribute directive to any supported Kendo component to expose
|
|
4696
|
+
* AI-discoverable tools through the browser's Web MCP protocol (Chrome 146+).
|
|
4697
|
+
*
|
|
4698
|
+
* @example
|
|
4699
|
+
* ```html
|
|
4700
|
+
* <kendo-grid [data]="data" [kendoWebMcp]="true"></kendo-grid>
|
|
4701
|
+
* ```
|
|
4702
|
+
*
|
|
4703
|
+
* @example
|
|
4704
|
+
* ```html
|
|
4705
|
+
* <kendo-grid [data]="data" [kendoWebMcp]="{ dataName: 'orders' }"></kendo-grid>
|
|
4706
|
+
* ```
|
|
4707
|
+
*/
|
|
4708
|
+
class WebMcpDirective {
|
|
4709
|
+
el;
|
|
4710
|
+
ngZone;
|
|
4711
|
+
hostComponent;
|
|
4712
|
+
/**
|
|
4713
|
+
* Enables or configures the Web MCP tools for the host component.
|
|
4714
|
+
*
|
|
4715
|
+
* - `true` — registers all supported tools with defaults.
|
|
4716
|
+
* - `WebMcpConfig` — fine-grained control over which tool groups are enabled.
|
|
4717
|
+
* - `false` — disables all tools.
|
|
4718
|
+
*/
|
|
4719
|
+
config = true;
|
|
4720
|
+
registrations = [];
|
|
4721
|
+
modelContext = null;
|
|
4722
|
+
constructor(el, ngZone, hostComponent) {
|
|
4723
|
+
this.el = el;
|
|
4724
|
+
this.ngZone = ngZone;
|
|
4725
|
+
this.hostComponent = hostComponent;
|
|
4726
|
+
validatePackage(packageMetadata);
|
|
4727
|
+
}
|
|
4728
|
+
ngOnInit() {
|
|
4729
|
+
if (!this.isBrowserWithMcp()) {
|
|
4730
|
+
return;
|
|
4731
|
+
}
|
|
4732
|
+
this.modelContext = navigator.modelContext;
|
|
4733
|
+
this.registerAll();
|
|
4734
|
+
}
|
|
4735
|
+
ngOnChanges(changes) {
|
|
4736
|
+
if (changes['config'] && !changes['config'].firstChange) {
|
|
4737
|
+
this.unregisterAll();
|
|
4738
|
+
this.registerAll();
|
|
4739
|
+
}
|
|
4740
|
+
}
|
|
4741
|
+
ngOnDestroy() {
|
|
4742
|
+
this.unregisterAll();
|
|
4743
|
+
}
|
|
4744
|
+
registerAll() {
|
|
4745
|
+
const resolved = resolveConfig(this.config);
|
|
4746
|
+
if (!resolved || !this.modelContext || !this.hostComponent) {
|
|
4747
|
+
return;
|
|
4748
|
+
}
|
|
4749
|
+
const mc = this.modelContext;
|
|
4750
|
+
const adapter = this.findAdapter();
|
|
4751
|
+
if (!adapter) {
|
|
4752
|
+
return;
|
|
4753
|
+
}
|
|
4754
|
+
this.ngZone.runOutsideAngular(() => {
|
|
4755
|
+
const handles = adapter.registerTools(this.hostComponent, resolved, mc, this.ngZone);
|
|
4756
|
+
this.registrations.push(...handles);
|
|
4757
|
+
});
|
|
4758
|
+
}
|
|
4759
|
+
findAdapter() {
|
|
4760
|
+
const el = this.el.nativeElement;
|
|
4761
|
+
const tag = el.tagName.toLowerCase();
|
|
4762
|
+
for (const adapter of ADAPTERS) {
|
|
4763
|
+
const sel = adapter.selector;
|
|
4764
|
+
if (sel.startsWith('[')) {
|
|
4765
|
+
// Attribute selector — check for the attribute on the host element
|
|
4766
|
+
const attr = sel.slice(1, -1);
|
|
4767
|
+
if (el.hasAttribute(attr) || el.hasAttribute(attr.toLowerCase())) {
|
|
4768
|
+
return adapter;
|
|
4769
|
+
}
|
|
4770
|
+
}
|
|
4771
|
+
else if (sel === tag) {
|
|
4772
|
+
return adapter;
|
|
4773
|
+
}
|
|
4774
|
+
}
|
|
4775
|
+
return null;
|
|
4776
|
+
}
|
|
4777
|
+
unregisterAll() {
|
|
4778
|
+
for (const reg of this.registrations) {
|
|
4779
|
+
reg.unregister();
|
|
4780
|
+
}
|
|
4781
|
+
this.registrations = [];
|
|
4782
|
+
}
|
|
4783
|
+
isBrowserWithMcp() {
|
|
4784
|
+
return isDocumentAvailable()
|
|
4785
|
+
&& typeof navigator !== 'undefined'
|
|
4786
|
+
&& !!navigator.modelContext;
|
|
4787
|
+
}
|
|
4788
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: WebMcpDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: KENDO_WEBMCP_HOST, host: true, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
|
|
4789
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.20", type: WebMcpDirective, isStandalone: true, selector: "[kendoWebMcp]", inputs: { config: ["kendoWebMcp", "config"] }, usesOnChanges: true, ngImport: i0 });
|
|
4790
|
+
}
|
|
4791
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: WebMcpDirective, decorators: [{
|
|
4792
|
+
type: Directive,
|
|
4793
|
+
args: [{
|
|
4794
|
+
selector: '[kendoWebMcp]',
|
|
4795
|
+
standalone: true
|
|
4796
|
+
}]
|
|
4797
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: undefined, decorators: [{
|
|
4798
|
+
type: Host
|
|
4799
|
+
}, {
|
|
4800
|
+
type: Optional
|
|
4801
|
+
}, {
|
|
4802
|
+
type: Inject,
|
|
4803
|
+
args: [KENDO_WEBMCP_HOST]
|
|
4804
|
+
}] }], propDecorators: { config: [{
|
|
4805
|
+
type: Input,
|
|
4806
|
+
args: ['kendoWebMcp']
|
|
4807
|
+
}] } });
|
|
4808
|
+
|
|
4809
|
+
/**
|
|
4810
|
+
* Use the `KENDO_WEBMCP` utility array to add Web MCP support to a standalone Angular component.
|
|
4811
|
+
*
|
|
4812
|
+
* @example
|
|
4813
|
+
* ```typescript
|
|
4814
|
+
* import { Component } from '@angular/core';
|
|
4815
|
+
* import { KENDO_WEBMCP } from '@progress/kendo-angular-webmcp';
|
|
4816
|
+
* import { KENDO_GRID } from '@progress/kendo-angular-grid';
|
|
4817
|
+
*
|
|
4818
|
+
* @Component({
|
|
4819
|
+
* standalone: true,
|
|
4820
|
+
* imports: [KENDO_WEBMCP, KENDO_GRID],
|
|
4821
|
+
* template: `
|
|
4822
|
+
* <kendo-grid [data]="data" [kendoWebMcp]="true"></kendo-grid>
|
|
4823
|
+
* `,
|
|
4824
|
+
* })
|
|
4825
|
+
* export class AppComponent {
|
|
4826
|
+
* data = [...];
|
|
4827
|
+
* }
|
|
4828
|
+
* ```
|
|
4829
|
+
*/
|
|
4830
|
+
const KENDO_WEBMCP = [
|
|
4831
|
+
WebMcpDirective
|
|
4832
|
+
];
|
|
4833
|
+
|
|
4834
|
+
//IMPORTANT: NgModule export kept for backwards compatibility
|
|
4835
|
+
/**
|
|
4836
|
+
* Represents the [NgModule](link:site.data.urls.angular['ngmoduleapi'])
|
|
4837
|
+
* definition for the Web MCP directive.
|
|
4838
|
+
*
|
|
4839
|
+
* @example
|
|
4840
|
+
* ```typescript
|
|
4841
|
+
* import { NgModule } from '@angular/core';
|
|
4842
|
+
* import { BrowserModule } from '@angular/platform-browser';
|
|
4843
|
+
* import { WebMcpModule } from '@progress/kendo-angular-webmcp';
|
|
4844
|
+
* import { GridModule } from '@progress/kendo-angular-grid';
|
|
4845
|
+
* import { AppComponent } from './app.component';
|
|
4846
|
+
*
|
|
4847
|
+
* @NgModule({
|
|
4848
|
+
* declarations: [AppComponent],
|
|
4849
|
+
* imports: [BrowserModule, GridModule, WebMcpModule],
|
|
4850
|
+
* bootstrap: [AppComponent]
|
|
4851
|
+
* })
|
|
4852
|
+
* export class AppModule {}
|
|
4853
|
+
* ```
|
|
4854
|
+
*/
|
|
4855
|
+
class WebMcpModule {
|
|
4856
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: WebMcpModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
4857
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: WebMcpModule, imports: [WebMcpDirective], exports: [WebMcpDirective] });
|
|
4858
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: WebMcpModule });
|
|
4859
|
+
}
|
|
4860
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: WebMcpModule, decorators: [{
|
|
4861
|
+
type: NgModule,
|
|
4862
|
+
args: [{
|
|
4863
|
+
imports: [...KENDO_WEBMCP],
|
|
4864
|
+
exports: [...KENDO_WEBMCP]
|
|
4865
|
+
}]
|
|
4866
|
+
}] });
|
|
4867
|
+
|
|
4868
|
+
/**
|
|
4869
|
+
* Generated bundle index. Do not edit.
|
|
4870
|
+
*/
|
|
4871
|
+
|
|
4872
|
+
export { KENDO_WEBMCP, WebMcpDirective, WebMcpModule };
|
|
4873
|
+
|