json-object-editor 0.10.670 → 0.10.671
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/_www/ai-widget-test.html +1 -0
- package/_www/intent-operator.html +387 -0
- package/_www/mcp-nav.js +1 -0
- package/docs/JOE_AI_Overview.md +219 -250
- package/docs/schema_summary_guidelines.md +42 -3
- package/package.json +1 -1
- package/server/app-config.js +1 -1
- package/server/modules/Server.js +6 -0
- package/server/plugins/intent-operator.js +704 -0
package/_www/ai-widget-test.html
CHANGED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>JOE Intent Operator</title>
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
|
+
<style>
|
|
8
|
+
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;margin:20px;background:#f3f4f6;}
|
|
9
|
+
h1{margin-top:0;}
|
|
10
|
+
.small{font-size:13px;color:#6b7280;margin-bottom:16px; box-sizing: border-box;}
|
|
11
|
+
.container{max-width:85%;margin:0 auto;background:#fff;border-radius:12px;box-shadow:0 4px 14px rgba(0,0,0,0.06);padding:16px;}
|
|
12
|
+
code{background:#e5e7eb;border-radius:4px;padding:2px 4px;font-size:12px;}
|
|
13
|
+
.result-container{display:flex;gap:8px;margin-top:8px;}
|
|
14
|
+
.result-json{flex:1;}
|
|
15
|
+
.enum-button{display:block;width:100%;padding:6px 8px;background:#3b82f6;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:11px;font-weight:500;margin-bottom:4px;}
|
|
16
|
+
.enum-list{font-size:10px;color:#4b5563;line-height:1.4;margin-top:4px;}
|
|
17
|
+
.enum-item{margin:2px 0;}
|
|
18
|
+
.enum-link{color:#3b82f6;text-decoration:none;}
|
|
19
|
+
.enum-link:hover{text-decoration:underline;}
|
|
20
|
+
</style>
|
|
21
|
+
</head>
|
|
22
|
+
<body>
|
|
23
|
+
<div id="mcp-nav"></div>
|
|
24
|
+
<script src="/JsonObjectEditor/_www/mcp-nav.js"></script>
|
|
25
|
+
|
|
26
|
+
<div class="container">
|
|
27
|
+
<h1>Intent Operator </h1>
|
|
28
|
+
<div class="small">
|
|
29
|
+
Test the intent-operator plugin which classifies natural language requests and returns structured intent objects.
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div style="display:flex;gap:8px;margin-bottom:8px;">
|
|
33
|
+
<div id="intent-operator-panel" class="small" style="flex:1; background:#f9fafb; border:1px solid #e5e7eb; border-radius:6px; padding:6px;">
|
|
34
|
+
<div style="font-weight:600;margin-bottom:4px;">Intent Operator Test (Phase 1)</div>
|
|
35
|
+
<div style="margin-bottom:4px;">
|
|
36
|
+
<label for="example-requests" style="font-size:11px;color:#6b7280;margin-right:4px;">Example requests:</label>
|
|
37
|
+
<select id="example-requests" style="padding:4px;border:1px solid #d1d5db;border-radius:4px;font-size:12px;width:100%;max-width:400px;">
|
|
38
|
+
<option value="">-- Select an example --</option>
|
|
39
|
+
<option value="add a new task to the project home improvement called fix the sink">[Valid] add a new task to the project home improvement called fix the sink</option>
|
|
40
|
+
<option value="add a new tack to the project home called clean my room">[Typo] add a new tack to the project home called clean my room</option>
|
|
41
|
+
<option value="add a new projct called website redesign">[Typo] add a new projct called website redesign</option>
|
|
42
|
+
<option value="create a new page for the project marketing">[Semantic mismatch] create a new page for the project marketing</option>
|
|
43
|
+
<option value="add a new page to the site main website with path /about">[Valid] add a new page to the site main website with path /about</option>
|
|
44
|
+
<option value="create a new item called quarterly report">[Ambiguous] create a new item called quarterly report</option>
|
|
45
|
+
<option value="create a task in project alpha and link it to page beta">[Multiple] create a task in project alpha and link it to page beta</option>
|
|
46
|
+
<option value="update the page status to published">[Update] update the page status to published</option>
|
|
47
|
+
<option value="find all tasks in the project engineering">[Search] find all tasks in the project engineering</option>
|
|
48
|
+
<option value="add a new taks to the projct home called clean room">[Complex typos] add a new taks to the projct home called clean room</option>
|
|
49
|
+
</select>
|
|
50
|
+
</div>
|
|
51
|
+
<div style="display:flex;gap:8px;margin-bottom:4px;">
|
|
52
|
+
<input type="text" id="intent-input" placeholder="Enter natural language command..." style="flex:1;padding:6px;border:1px solid #d1d5db;border-radius:4px;font-size:13px;" />
|
|
53
|
+
<button id="intent-submit" style="padding:6px 12px;background:#3b82f6;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;">Get Intent</button>
|
|
54
|
+
</div>
|
|
55
|
+
<div id="intent-status" style="font-size:11px;color:#6b7280;margin-bottom:4px;"></div>
|
|
56
|
+
<div class="result-container">
|
|
57
|
+
<pre id="intent-result" class="result-json" style="white-space:pre-wrap;font-size:11px;margin:0;max-height:400px;overflow:auto;background:#fff;border:1px solid #e5e7eb;border-radius:4px;padding:8px;display:none;"></pre>
|
|
58
|
+
<div id="clickable-links-section" class="small" style="width:300px; background:#f9fafb; border:1px solid #e5e7eb; border-radius:6px; padding:8px; display:none;">
|
|
59
|
+
<div style="font-weight:600;margin-bottom:8px;">Intent Details</div>
|
|
60
|
+
<div id="clickable-links-content" style="font-size:11px;">
|
|
61
|
+
<div class="enum-section" style="margin-bottom:8px;">
|
|
62
|
+
<div style="font-weight:600;margin-bottom:2px;font-size:10px;">Elapsed</div>
|
|
63
|
+
<div id="enum-elapsed" style="font-size:10px;color:#4b5563;">-</div>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="enum-section" style="margin-bottom:8px;">
|
|
66
|
+
<div style="font-weight:600;margin-bottom:2px;font-size:10px;">Intent</div>
|
|
67
|
+
<div id="enum-intent" style="font-size:10px;color:#4b5563;">-</div>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="enum-section" style="margin-bottom:8px;">
|
|
70
|
+
<div style="font-weight:600;margin-bottom:2px;font-size:10px;">Primary Itemtype</div>
|
|
71
|
+
<div id="enum-primary-itemtype" style="font-size:10px;color:#4b5563;">-</div>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="enum-section" style="margin-bottom:8px;">
|
|
74
|
+
<div style="font-weight:600;margin-bottom:2px;font-size:10px;">Operator</div>
|
|
75
|
+
<div id="enum-operator" style="font-size:10px;color:#4b5563;">-</div>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="enum-section" style="margin-bottom:8px;">
|
|
78
|
+
<div style="font-weight:600;margin-bottom:2px;font-size:10px;">Confidence</div>
|
|
79
|
+
<div id="enum-confidence" style="font-size:10px;color:#4b5563;">-</div>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="enum-section" style="margin-bottom:8px;">
|
|
82
|
+
<div style="font-weight:600;margin-bottom:2px;font-size:10px;">Needs Clarification</div>
|
|
83
|
+
<div id="enum-clarification" style="font-size:10px;color:#4b5563;">-</div>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="enum-section" style="margin-bottom:8px;">
|
|
86
|
+
<button class="enum-button" id="schemas-button" style="display:none;" onclick="window.open(document.getElementById('schemas-url').href, '_blank')">Schemas to Load</button>
|
|
87
|
+
<a id="schemas-url" href="#" style="display:none;"></a>
|
|
88
|
+
<div id="schemas-list" class="enum-list" style="display:none;"></div>
|
|
89
|
+
</div>
|
|
90
|
+
<div class="enum-section" style="margin-bottom:8px;">
|
|
91
|
+
<div id="objects-section" style="display:none;">
|
|
92
|
+
<div style="font-weight:600;margin-bottom:4px;font-size:10px;">Objects to Resolve</div>
|
|
93
|
+
<div id="objects-list" class="enum-list"></div>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
<div id="intent-enums-panel" class="small" style="width:300px; background:#f9fafb; border:1px solid #e5e7eb; border-radius:6px; padding:6px;">
|
|
101
|
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px;">
|
|
102
|
+
<div style="font-weight:600;">Intents</div>
|
|
103
|
+
<a href="/API/plugin/intent-operator/intents" target="intent-operator-intents" style="font-size:10px;color:#3b82f6;text-decoration:none;">View API →</a>
|
|
104
|
+
</div>
|
|
105
|
+
<div id="intents-accordion" style="font-size:11px;">
|
|
106
|
+
<div style="color:#6b7280;font-size:10px;margin-bottom:4px;">Loading intents...</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<script>
|
|
113
|
+
(function(){
|
|
114
|
+
function $(id){ return document.getElementById(id); }
|
|
115
|
+
|
|
116
|
+
// Wait for DOM to be ready
|
|
117
|
+
function init() {
|
|
118
|
+
try {
|
|
119
|
+
// Example requests dropdown
|
|
120
|
+
const dropdown = $('example-requests');
|
|
121
|
+
const input = $('intent-input');
|
|
122
|
+
if (dropdown && input) {
|
|
123
|
+
dropdown.addEventListener('change', function() {
|
|
124
|
+
if (dropdown.value) {
|
|
125
|
+
input.value = dropdown.value;
|
|
126
|
+
dropdown.selectedIndex = 0; // Reset to first option
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
} else {
|
|
130
|
+
console.error('[intent-operator] Missing elements: dropdown=' + !!dropdown + ', input=' + !!input);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Intent operator test
|
|
134
|
+
const submit = $('intent-submit');
|
|
135
|
+
const status = $('intent-status');
|
|
136
|
+
const result = $('intent-result');
|
|
137
|
+
|
|
138
|
+
if (input && submit && status && result) {
|
|
139
|
+
async function getIntent() {
|
|
140
|
+
const text = input.value.trim();
|
|
141
|
+
if (!text) {
|
|
142
|
+
status.textContent = 'Please enter some text.';
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
status.textContent = 'Calling intent-operator...';
|
|
147
|
+
result.style.display = 'none';
|
|
148
|
+
const linksSection = $('clickable-links-section');
|
|
149
|
+
if (linksSection) linksSection.style.display = 'none';
|
|
150
|
+
submit.disabled = true;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const resp = await fetch('/API/plugin/intent-operator', {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: { 'Content-Type': 'application/json' },
|
|
156
|
+
credentials: 'include',
|
|
157
|
+
body: JSON.stringify({ text: text })
|
|
158
|
+
});
|
|
159
|
+
const data = await resp.json();
|
|
160
|
+
|
|
161
|
+
status.textContent = resp.ok ? 'Intent classified successfully.' : 'Error: ' + (data.errors && data.errors.join(', ') || 'Unknown error');
|
|
162
|
+
result.textContent = JSON.stringify(data, null, 2);
|
|
163
|
+
result.style.display = 'block';
|
|
164
|
+
|
|
165
|
+
// Populate clickable links section
|
|
166
|
+
if (resp.ok && linksSection) {
|
|
167
|
+
// Elapsed
|
|
168
|
+
const elapsedEl = $('enum-elapsed');
|
|
169
|
+
if (elapsedEl) elapsedEl.textContent = (data.elapsed || 0).toFixed(3) + 's';
|
|
170
|
+
|
|
171
|
+
// Intent
|
|
172
|
+
const intentEl = $('enum-intent');
|
|
173
|
+
if (intentEl && data.intent) {
|
|
174
|
+
intentEl.textContent = data.intent.intent || '-';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Primary Itemtype
|
|
178
|
+
const primaryEl = $('enum-primary-itemtype');
|
|
179
|
+
if (primaryEl && data.intent) {
|
|
180
|
+
primaryEl.textContent = data.intent.primary_itemtype || '-';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Operator
|
|
184
|
+
const operatorEl = $('enum-operator');
|
|
185
|
+
if (operatorEl && data.intent) {
|
|
186
|
+
operatorEl.textContent = data.intent.operator || '-';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Confidence
|
|
190
|
+
const confidenceEl = $('enum-confidence');
|
|
191
|
+
if (confidenceEl && data.intent) {
|
|
192
|
+
const conf = data.intent.confidence || 0;
|
|
193
|
+
confidenceEl.textContent = (conf * 100).toFixed(0) + '%';
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Needs Clarification
|
|
197
|
+
const clarificationEl = $('enum-clarification');
|
|
198
|
+
if (clarificationEl && data.intent) {
|
|
199
|
+
clarificationEl.textContent = data.intent.needs_clarification ? 'Yes' : 'No';
|
|
200
|
+
clarificationEl.style.color = data.intent.needs_clarification ? '#ef4444' : '#10b981';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Schemas to Load - single combined request
|
|
204
|
+
const schemasButton = $('schemas-button');
|
|
205
|
+
const schemasUrl = $('schemas-url');
|
|
206
|
+
const schemasList = $('schemas-list');
|
|
207
|
+
if (schemasButton && schemasUrl && schemasList) {
|
|
208
|
+
if (data.schemas_to_load && data.schemas_to_load.length > 0) {
|
|
209
|
+
const schemas = data.schemas_to_load;
|
|
210
|
+
// Build single URL with comma-separated schemas
|
|
211
|
+
const schemaNames = schemas.map(function(s) { return encodeURIComponent(s); }).join(',');
|
|
212
|
+
const url = '/API/schema/' + schemaNames;
|
|
213
|
+
schemasUrl.href = url;
|
|
214
|
+
// Show comma-delimited list below button
|
|
215
|
+
schemasList.textContent = schemas.join(', ');
|
|
216
|
+
schemasButton.style.display = 'block';
|
|
217
|
+
schemasList.style.display = 'block';
|
|
218
|
+
} else {
|
|
219
|
+
schemasButton.style.display = 'none';
|
|
220
|
+
schemasList.style.display = 'none';
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Objects to Resolve (multiple individual links with proper itemtype filtering)
|
|
225
|
+
const objectsSection = $('objects-section');
|
|
226
|
+
const objectsList = $('objects-list');
|
|
227
|
+
if (objectsSection && objectsList) {
|
|
228
|
+
if (data.objects_to_resolve && data.objects_to_resolve.length > 0) {
|
|
229
|
+
const objects = data.objects_to_resolve;
|
|
230
|
+
const objectLinks = objects.map(function(obj) {
|
|
231
|
+
const itemtype = obj.itemtype || '';
|
|
232
|
+
const name = obj.name || '';
|
|
233
|
+
// Build URL with itemtype in query string (only if itemtype exists)
|
|
234
|
+
const params = new URLSearchParams();
|
|
235
|
+
params.set('mode', 'fuzzy');
|
|
236
|
+
params.set('q', name);
|
|
237
|
+
if (itemtype) {
|
|
238
|
+
params.set('itemtype', itemtype);
|
|
239
|
+
}
|
|
240
|
+
const url = '/API/search?' + params.toString();
|
|
241
|
+
const label = (itemtype ? itemtype + ': ' : '') + name;
|
|
242
|
+
return '<div class="enum-item"><a href="' + url + '" target="_blank" class="enum-link">' + label + '</a></div>';
|
|
243
|
+
}).join('');
|
|
244
|
+
objectsList.innerHTML = objectLinks;
|
|
245
|
+
objectsSection.style.display = 'block';
|
|
246
|
+
} else {
|
|
247
|
+
objectsSection.style.display = 'none';
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
linksSection.style.display = 'block';
|
|
252
|
+
}
|
|
253
|
+
} catch (e) {
|
|
254
|
+
status.textContent = 'Error: ' + e.message;
|
|
255
|
+
result.textContent = String(e);
|
|
256
|
+
result.style.display = 'block';
|
|
257
|
+
} finally {
|
|
258
|
+
submit.disabled = false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
submit.addEventListener('click', getIntent);
|
|
263
|
+
input.addEventListener('keydown', function(e) {
|
|
264
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
265
|
+
e.preventDefault();
|
|
266
|
+
getIntent();
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
} else {
|
|
270
|
+
console.error('[intent-operator] Missing elements: input=' + !!input + ', submit=' + !!submit + ', status=' + !!status + ', result=' + !!result);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Load and render intents accordion
|
|
274
|
+
const accordion = document.getElementById('intents-accordion');
|
|
275
|
+
if (accordion) {
|
|
276
|
+
async function loadIntents() {
|
|
277
|
+
try {
|
|
278
|
+
const resp = await fetch('/API/plugin/intent-operator/intents', {
|
|
279
|
+
credentials: 'include'
|
|
280
|
+
});
|
|
281
|
+
const data = await resp.json();
|
|
282
|
+
|
|
283
|
+
if (!data || !data.intents || !Array.isArray(data.intents)) {
|
|
284
|
+
accordion.innerHTML = '<div style="color:#ef4444;font-size:10px;">Failed to load intents</div>';
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const intents = data.intents;
|
|
289
|
+
let html = '';
|
|
290
|
+
|
|
291
|
+
intents.forEach(function(intent, index) {
|
|
292
|
+
const isCustom = intent.overwrite === true || (index >= data.defaults_count && intent.overwrite !== false);
|
|
293
|
+
const intentId = 'intent-' + index;
|
|
294
|
+
|
|
295
|
+
html += '<div style="margin-bottom:4px;border:1px solid #e5e7eb;border-radius:4px;overflow:hidden;">';
|
|
296
|
+
html += '<input type="checkbox" id="' + intentId + '" style="display:none;">';
|
|
297
|
+
html += '<label for="' + intentId + '" style="display:block;padding:4px 6px;background:#fff;cursor:pointer;font-weight:500;font-size:11px;color:#374151;user-select:none;">';
|
|
298
|
+
html += intent.name + (isCustom ? ' <span style="color:#6b7280;font-weight:400;">(custom)</span>' : '');
|
|
299
|
+
html += ' <span class="intent-arrow" style="float:right;color:#9ca3af;">▼</span>';
|
|
300
|
+
html += '</label>';
|
|
301
|
+
html += '<div class="intent-content" style="padding:6px;background:#fff;font-size:10px;color:#4b5563;display:none;">';
|
|
302
|
+
|
|
303
|
+
// Description
|
|
304
|
+
if (intent.description) {
|
|
305
|
+
html += '<div style="margin-bottom:4px;"><strong>Description:</strong> ' + (intent.description || '') + '</div>';
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Handoff
|
|
309
|
+
if (intent.handoff) {
|
|
310
|
+
html += '<div style="margin-bottom:4px;"><strong>Handoff:</strong> <code style="background:#f3f4f6;padding:1px 4px;border-radius:2px;">' + intent.handoff + '</code></div>';
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Examples
|
|
314
|
+
if (intent.examples && Array.isArray(intent.examples) && intent.examples.length > 0) {
|
|
315
|
+
html += '<div style="margin-bottom:4px;"><strong>Examples:</strong><ul style="margin:2px 0 0 16px;padding:0;">';
|
|
316
|
+
intent.examples.forEach(function(ex) {
|
|
317
|
+
html += '<li style="margin:1px 0;">"' + ex + '"</li>';
|
|
318
|
+
});
|
|
319
|
+
html += '</ul></div>';
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Operators
|
|
323
|
+
if (intent.operators && Array.isArray(intent.operators) && intent.operators.length > 0) {
|
|
324
|
+
html += '<div style="margin-bottom:4px;"><strong>Operators:</strong> ' + intent.operators.join(', ') + '</div>';
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Required information
|
|
328
|
+
if (intent.required_information && Array.isArray(intent.required_information) && intent.required_information.length > 0) {
|
|
329
|
+
html += '<div style="margin-bottom:4px;"><strong>Required Information:</strong> ' + intent.required_information.join(', ') + '</div>';
|
|
330
|
+
} else if (intent.required_information !== undefined) {
|
|
331
|
+
html += '<div style="margin-bottom:4px;"><strong>Required Information:</strong> <em style="color:#9ca3af;">none</em></div>';
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
html += '</div>';
|
|
335
|
+
html += '</div>';
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
accordion.innerHTML = html;
|
|
339
|
+
|
|
340
|
+
// Wire up accordion behavior
|
|
341
|
+
intents.forEach(function(intent, index) {
|
|
342
|
+
const checkbox = document.getElementById('intent-' + index);
|
|
343
|
+
if (!checkbox) return;
|
|
344
|
+
|
|
345
|
+
const label = checkbox.nextElementSibling;
|
|
346
|
+
const content = label ? label.nextElementSibling : null;
|
|
347
|
+
const arrow = label ? label.querySelector('.intent-arrow') : null;
|
|
348
|
+
|
|
349
|
+
if (checkbox && label && content) {
|
|
350
|
+
checkbox.addEventListener('change', function() {
|
|
351
|
+
content.style.display = checkbox.checked ? 'block' : 'none';
|
|
352
|
+
if (arrow) {
|
|
353
|
+
arrow.textContent = checkbox.checked ? '▲' : '▼';
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
// Initialize display state (all collapsed)
|
|
357
|
+
content.style.display = 'none';
|
|
358
|
+
if (arrow) {
|
|
359
|
+
arrow.textContent = '▼';
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
} catch (e) {
|
|
365
|
+
console.error('[intent-operator] error loading intents', e);
|
|
366
|
+
accordion.innerHTML = '<div style="color:#ef4444;font-size:10px;">Error loading intents: ' + e.message + '</div>';
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
loadIntents();
|
|
371
|
+
}
|
|
372
|
+
} catch (e) {
|
|
373
|
+
console.error('[intent-operator] Error in init:', e);
|
|
374
|
+
throw e;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Wait for DOMContentLoaded, or call immediately if already loaded
|
|
379
|
+
if (document.readyState === 'loading') {
|
|
380
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
381
|
+
} else {
|
|
382
|
+
init(); // DOM is already loaded, elements should be available
|
|
383
|
+
}
|
|
384
|
+
})();
|
|
385
|
+
</script>
|
|
386
|
+
</body>
|
|
387
|
+
</html>
|
package/_www/mcp-nav.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
'<a href="/mcp-schemas.html" target="mcp_schemas_win">Schemas</a>',
|
|
21
21
|
'<a href="/matrix.html" target="matrix_win">Matrix</a>',
|
|
22
22
|
'<a href="/ai-widget-test.html" target="ai_widget_test_win">AI Widget</a>',
|
|
23
|
+
'<a href="/intent-operator.html" target="intent_operator_win">Intent Operator</a>',
|
|
23
24
|
'<a href="/ai-jobs.html" target="ai_jobs_win">AI Jobs</a>',
|
|
24
25
|
'<a href="/plugins-test.html" target="plugins_test_win">Plugins</a>',
|
|
25
26
|
'</div>'
|