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.
@@ -359,6 +359,7 @@ div#ai-convo-panel {
359
359
  } else {
360
360
  initAssistantSelect();
361
361
  }
362
+
362
363
  })();
363
364
  </script>
364
365
  </body>
@@ -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>'