json-object-editor 0.10.625 → 0.10.632
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/CHANGELOG.md +6 -0
- package/_www/ai-widget-test.html +367 -0
- package/_www/mcp-test.html +10 -1
- package/css/joe-styles.css +11 -3
- package/css/joe.css +12 -4
- package/css/joe.min.css +1 -1
- package/docs/joe_agent_custom_gpt_instructions_v_3.md +9 -0
- package/dummy +10 -0
- package/img/svgs/ai_assistant.svg +1 -0
- package/img/svgs/ai_assistant_white.svg +1 -0
- package/js/JsonObjectEditor.jquery.craydent.js +34 -3
- package/js/joe-ai.js +784 -52
- package/js/joe.js +52 -21
- package/js/joe.min.js +1 -1
- package/package.json +1 -1
- package/readme.md +8 -1
- package/server/apps/aihub.js +97 -0
- package/server/fields/core.js +4 -1
- package/server/modules/MCP.js +233 -2
- package/server/modules/Server.js +1 -46
- package/server/plugins/auth.js +34 -30
- package/server/plugins/chatgpt-assistants.js +70 -35
- package/server/plugins/chatgpt.js +560 -44
- package/server/schemas/ai_assistant.js +149 -1
- package/server/schemas/ai_conversation.js +14 -1
- package/server/schemas/ai_widget_conversation.js +133 -14
- package/server/schemas/project.js +27 -3
- package/server/schemas/task.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## 0.10.632
|
|
2
|
+
- AI / Widget: Responses-based tool runner for `<joe-ai-widget>` wired to MCP tools via `chatgpt.runWithTools`, plus safer user handling (explicit user id/name/color on `ai_widget_conversation`).
|
|
3
|
+
- Auth: Updated Google OAuth token exchange to use the current `oauth2.googleapis.com/token` endpoint and surface detailed error payloads when login fails.
|
|
4
|
+
- AI Autofill: Added schema-level `ai` config for fields (e.g. `{ name:'ai_summary', type:'rendering', ai:{ prompt:'Summarize this project...' } }`) and a core autofill button that calls `/API/plugin/chatgpt/autofill` via `_joe.Ai.populateField(...)`, with client-side confirm-before-overwrite and robust JSON parsing of the model’s patch output.
|
|
5
|
+
- Docs / UX: Expanded AI widget test page (assistant picker, tools viewer, conversation list) and updated README to document the new widget, schemas, tool-calling flow, and field-level AI autofill.
|
|
6
|
+
|
|
1
7
|
## 0.10.625
|
|
2
8
|
- MCP (breaking change)
|
|
3
9
|
- Replaced param name `schema` with `itemtype` across tools: `search`, `getObject`, and `fuzzySearch`.
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>JOE AI Widget Test</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
|
+
|
|
14
|
+
joe-ai-widget#widget {
|
|
15
|
+
float: left;
|
|
16
|
+
width: calc(100% - 300px);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
div#ai-convo-panel {
|
|
20
|
+
float: left;
|
|
21
|
+
}
|
|
22
|
+
</style>
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<div id="mcp-nav"></div>
|
|
26
|
+
<script src="/JsonObjectEditor/_www/mcp-nav.js"></script>
|
|
27
|
+
<!-- Optional markdown + sanitizer libs used by joe-ai.js when present -->
|
|
28
|
+
<script src="https://cdn.jsdelivr.net/npm/marked@12.0.2/marked.min.js"></script>
|
|
29
|
+
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js"></script>
|
|
30
|
+
|
|
31
|
+
<div class="container">
|
|
32
|
+
<h1>AI Widget Test</h1>
|
|
33
|
+
<div class="small">
|
|
34
|
+
This page mounts <code><joe-ai-widget></code> and sends messages through
|
|
35
|
+
<code>/API/plugin/chatgpt/widget*</code> using the OpenAI Responses API.
|
|
36
|
+
It will use the <code>DEFAULT_AI_ASSISTANT</code> if configured, or fall back to model-only.
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div style="margin-bottom:8px; display:flex; align-items:center; justify-content:space-between; gap:8px;">
|
|
40
|
+
<div>
|
|
41
|
+
<label for="ai-assistant-select" style="font-size:12px;color:#374151;">Assistant:</label>
|
|
42
|
+
<select id="ai-assistant-select" style="margin-left:4px;padding:2px 4px;font-size:12px;"></select>
|
|
43
|
+
<span id="ai-assistant-hint" style="font-size:11px;color:#6b7280;margin-left:4px;"></span>
|
|
44
|
+
</div>
|
|
45
|
+
<div id="ai-user-info" style="font-size:11px;color:#4b5563;text-align:right;white-space:nowrap;"></div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div id="ai-tools-panel" class="small" style="margin-bottom:8px; max-height:40px; overflow:auto; background:#f9fafb; border:1px solid #e5e7eb; border-radius:6px; padding:6px;">
|
|
49
|
+
<div style="font-weight:600;margin-bottom:4px;">Tools for selected assistant</div>
|
|
50
|
+
<div id="ai-tools-summary" style="margin-bottom:4px;"></div>
|
|
51
|
+
<pre id="ai-tools-json" style="white-space:pre-wrap;font-size:11px;margin:0;"></pre>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div id="ai-convo-panel" class="small" style="margin-bottom:8px; max-height:200px; overflow:auto; background:#f9fafb; border:1px solid #e5e7eb; border-radius:6px; padding:6px;">
|
|
55
|
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px;">
|
|
56
|
+
<span style="font-weight:600;">Widget Conversations</span>
|
|
57
|
+
<button id="ai-convo-refresh" style="font-size:11px;padding:2px 6px;">Refresh</button>
|
|
58
|
+
</div>
|
|
59
|
+
<div id="ai-convo-status" style="margin-bottom:4px;"></div>
|
|
60
|
+
<ul id="ai-convo-list" style="list-style:none;padding:0;margin:0;"></ul>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div id="ai-convo-debug" class="small" style="margin-bottom:8px; max-height:260px; overflow:auto; background:#f9fafb; border:1px solid #e5e7eb; border-radius:6px; padding:6px;">
|
|
64
|
+
<div style="font-weight:600;margin-bottom:4px;">Selected Conversation (raw ai_widget_conversation)</div>
|
|
65
|
+
<div id="ai-convo-debug-status" style="margin-bottom:4px;"></div>
|
|
66
|
+
<pre id="ai-convo-debug-json" style="white-space:pre-wrap;font-size:11px;margin:0;"></pre>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<joe-ai-widget id="widget" title="JOE AI Assistant"></joe-ai-widget>
|
|
70
|
+
<div style="clear:both;"></div>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<script src="/JsonObjectEditor/js/joe-ai.js"></script>
|
|
74
|
+
<script>
|
|
75
|
+
(function(){
|
|
76
|
+
function $(id){ return document.getElementById(id); }
|
|
77
|
+
|
|
78
|
+
// Simple contrast helper: choose black/white text for a hex background.
|
|
79
|
+
function textColorForBg(hex){
|
|
80
|
+
if (!hex || typeof hex !== 'string' || !/^#?[0-9a-fA-F]{6}$/.test(hex)) return '#000';
|
|
81
|
+
const h = hex[0] === '#' ? hex.slice(1) : hex;
|
|
82
|
+
const n = parseInt(h, 16);
|
|
83
|
+
const r = (n >> 16) & 0xff;
|
|
84
|
+
const g = (n >> 8) & 0xff;
|
|
85
|
+
const b = n & 0xff;
|
|
86
|
+
const luminance = r * 0.299 + g * 0.587 + b * 0.114;
|
|
87
|
+
return luminance > 186 ? '#000' : '#fff';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function fetchJSON(url){
|
|
91
|
+
const res = await fetch(url, { credentials: 'include' });
|
|
92
|
+
const text = await res.text();
|
|
93
|
+
try{
|
|
94
|
+
return JSON.parse(text);
|
|
95
|
+
}catch(e){
|
|
96
|
+
console.error('[ai-widget-test] Bad JSON from', url, '->', text);
|
|
97
|
+
throw e;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function initAssistantSelect(){
|
|
102
|
+
const select = $('ai-assistant-select');
|
|
103
|
+
const hint = $('ai-assistant-hint');
|
|
104
|
+
const widget = $('widget');
|
|
105
|
+
const userInfoEl = $('ai-user-info');
|
|
106
|
+
const convoStatus = $('ai-convo-status');
|
|
107
|
+
const convoList = $('ai-convo-list');
|
|
108
|
+
const convoRefresh = $('ai-convo-refresh');
|
|
109
|
+
const convoDebugStatus = $('ai-convo-debug-status');
|
|
110
|
+
const convoDebugJson = $('ai-convo-debug-json');
|
|
111
|
+
if (!select || !widget) { return; }
|
|
112
|
+
|
|
113
|
+
const toolsSummaryEl = $('ai-tools-summary');
|
|
114
|
+
const toolsJsonEl = $('ai-tools-json');
|
|
115
|
+
|
|
116
|
+
let assistants = [];
|
|
117
|
+
let currentUser = null;
|
|
118
|
+
let defaultId = null;
|
|
119
|
+
|
|
120
|
+
// Load current user (for color/id) from JOE auth API
|
|
121
|
+
try{
|
|
122
|
+
const uResp = await fetchJSON('/API/user/current');
|
|
123
|
+
currentUser = (uResp && uResp.user) || null;
|
|
124
|
+
// Expose to joe-ai.js so the widget can pick it up.
|
|
125
|
+
window._aiWidgetUser = currentUser;
|
|
126
|
+
if (currentUser && userInfoEl){
|
|
127
|
+
var uname = currentUser.fullname || currentUser.name || '(no name)';
|
|
128
|
+
var uid = currentUser._id || '(no _id)';
|
|
129
|
+
userInfoEl.textContent = 'User: ' + uname + ' [' + uid + ']';
|
|
130
|
+
}
|
|
131
|
+
}catch(e){
|
|
132
|
+
console.error('[ai-widget-test] error loading current user', e);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Load assistants
|
|
136
|
+
try{
|
|
137
|
+
const resp = await fetchJSON('/API/item/ai_assistant');
|
|
138
|
+
assistants = Array.isArray(resp.item) ? resp.item : [];
|
|
139
|
+
}catch(e){
|
|
140
|
+
console.error('[ai-widget-test] error loading assistants', e);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Load settings to find DEFAULT_AI_ASSISTANT
|
|
144
|
+
try{
|
|
145
|
+
const sResp = await fetchJSON('/API/item/setting');
|
|
146
|
+
const settings = Array.isArray(sResp.item) ? sResp.item : [];
|
|
147
|
+
const def = settings.find(function(s){ return s && s.name === 'DEFAULT_AI_ASSISTANT'; });
|
|
148
|
+
defaultId = def && def.value;
|
|
149
|
+
}catch(e){
|
|
150
|
+
console.error('[ai-widget-test] error loading settings', e);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const params = new URLSearchParams(location.search);
|
|
154
|
+
const overrideId = params.get('assistant_id') || params.get('assistant') || '';
|
|
155
|
+
|
|
156
|
+
// Build options
|
|
157
|
+
select.innerHTML = '';
|
|
158
|
+
const noneOption = document.createElement('option');
|
|
159
|
+
noneOption.value = '';
|
|
160
|
+
noneOption.textContent = 'None (model only)';
|
|
161
|
+
select.appendChild(noneOption);
|
|
162
|
+
|
|
163
|
+
assistants.forEach(function(a){
|
|
164
|
+
const opt = document.createElement('option');
|
|
165
|
+
opt.value = a._id;
|
|
166
|
+
opt.textContent = a.name || a.title || a._id;
|
|
167
|
+
if (a._id === defaultId){
|
|
168
|
+
opt.textContent += ' (default)';
|
|
169
|
+
}
|
|
170
|
+
select.appendChild(opt);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Decide initial selection
|
|
174
|
+
let initialId = '';
|
|
175
|
+
if (overrideId && assistants.some(function(a){ return a._id === overrideId; })){
|
|
176
|
+
initialId = overrideId;
|
|
177
|
+
if (hint){ hint.textContent = 'Using assistant from query string.'; }
|
|
178
|
+
} else if (defaultId && assistants.some(function(a){ return a._id === defaultId; })){
|
|
179
|
+
initialId = defaultId;
|
|
180
|
+
if (hint){ hint.textContent = 'Using DEFAULT_AI_ASSISTANT.'; }
|
|
181
|
+
} else {
|
|
182
|
+
initialId = '';
|
|
183
|
+
if (hint){
|
|
184
|
+
hint.textContent = assistants.length
|
|
185
|
+
? 'No default; using model only.'
|
|
186
|
+
: 'No assistants defined; using model only.';
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
select.value = initialId;
|
|
191
|
+
|
|
192
|
+
// Render tools + apply initial assistant to widget and restart conversation
|
|
193
|
+
renderToolsForAssistant(assistants, select.value, toolsSummaryEl, toolsJsonEl);
|
|
194
|
+
applyAssistantToWidget(widget, select.value);
|
|
195
|
+
|
|
196
|
+
select.addEventListener('change', function(){
|
|
197
|
+
renderToolsForAssistant(assistants, select.value, toolsSummaryEl, toolsJsonEl);
|
|
198
|
+
applyAssistantToWidget(widget, select.value);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Load initial conversation list and wire refresh button
|
|
202
|
+
async function refreshConversations(){
|
|
203
|
+
if (!convoStatus || !convoList) { return; }
|
|
204
|
+
try{
|
|
205
|
+
convoStatus.textContent = 'Loading conversations...';
|
|
206
|
+
convoList.innerHTML = '';
|
|
207
|
+
const resp = await fetchJSON('/API/item/ai_widget_conversation');
|
|
208
|
+
const items = Array.isArray(resp.item) ? resp.item : [];
|
|
209
|
+
if (!items.length){
|
|
210
|
+
convoStatus.textContent = 'No widget conversations found.';
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
// Sort by last_message_at or created desc
|
|
214
|
+
items.sort(function(a,b){
|
|
215
|
+
var da = a.last_message_at || a.joeUpdated || a.created || '';
|
|
216
|
+
var db = b.last_message_at || b.joeUpdated || b.created || '';
|
|
217
|
+
return db.localeCompare(da);
|
|
218
|
+
});
|
|
219
|
+
convoStatus.textContent = 'Click a conversation to resume it.';
|
|
220
|
+
items.forEach(function(c){
|
|
221
|
+
var li = document.createElement('li');
|
|
222
|
+
li.style.cursor = 'pointer';
|
|
223
|
+
li.style.padding = '2px 0';
|
|
224
|
+
li.style.borderBottom = '1px solid #e5e7eb';
|
|
225
|
+
li.dataset.id = c._id;
|
|
226
|
+
var label = c.name || (c.source ? ('['+c.source+']') : '') || c._id;
|
|
227
|
+
var ts = c.last_message_at || c.joeUpdated || c.created || '';
|
|
228
|
+
var msgCount = Array.isArray(c.messages) ? c.messages.length : 0;
|
|
229
|
+
var userLabel = c.user_name || c.user || '';
|
|
230
|
+
var userColor = c.user_color || '';
|
|
231
|
+
var userChip = '';
|
|
232
|
+
if (userLabel){
|
|
233
|
+
if (userColor){
|
|
234
|
+
var fg = textColorForBg(userColor);
|
|
235
|
+
userChip = ' <span style="margin-left:4px;padding:1px 6px;border-radius:10px;background:'+userColor+';color:'+fg+';">'+userLabel+'</span>';
|
|
236
|
+
} else {
|
|
237
|
+
userChip = ' <span style="margin-left:4px;color:#111827;">'+userLabel+'</span>';
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
li.innerHTML = '<strong>'+label+'</strong>' +
|
|
241
|
+
(ts ? ' <span style="color:#6b7280;">'+ts+'</span>' : '') +
|
|
242
|
+
userChip +
|
|
243
|
+
' <span style="color:#4b5563;">['+msgCount+' msgs]</span>';
|
|
244
|
+
li.addEventListener('click', function(){
|
|
245
|
+
try{
|
|
246
|
+
applyConversationToWidget(widget, c._id);
|
|
247
|
+
loadConversationDebug(c._id);
|
|
248
|
+
}catch(e){
|
|
249
|
+
console.error('[ai-widget-test] error applying conversation', e);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
convoList.appendChild(li);
|
|
253
|
+
});
|
|
254
|
+
}catch(e){
|
|
255
|
+
console.error('[ai-widget-test] error loading conversations', e);
|
|
256
|
+
convoStatus.textContent = 'Error loading conversations; see console.';
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (convoRefresh){
|
|
261
|
+
convoRefresh.addEventListener('click', function(){
|
|
262
|
+
refreshConversations();
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// Initial load
|
|
266
|
+
refreshConversations();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function loadConversationDebug(conversationId){
|
|
270
|
+
if (!conversationId || !document.getElementById('ai-convo-debug-json')) return;
|
|
271
|
+
const statusEl = document.getElementById('ai-convo-debug-status');
|
|
272
|
+
const preEl = document.getElementById('ai-convo-debug-json');
|
|
273
|
+
try{
|
|
274
|
+
statusEl.textContent = 'Loading conversation '+conversationId+'...';
|
|
275
|
+
preEl.textContent = '';
|
|
276
|
+
const resp = await fetchJSON('/API/object/ai_widget_conversation/_id/' + encodeURIComponent(conversationId));
|
|
277
|
+
if (!resp || resp.error){
|
|
278
|
+
statusEl.textContent = (resp && resp.error) || 'Conversation not found.';
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
statusEl.textContent = 'ai_widget_conversation '+conversationId;
|
|
282
|
+
preEl.textContent = JSON.stringify(resp, null, 2);
|
|
283
|
+
}catch(e){
|
|
284
|
+
console.error('[ai-widget-test] error loading conversation debug', e);
|
|
285
|
+
statusEl.textContent = 'Error loading conversation; see console.';
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function renderToolsForAssistant(assistants, assistantId, summaryEl, jsonEl){
|
|
290
|
+
if (!summaryEl || !jsonEl) { return; }
|
|
291
|
+
const id = assistantId || '';
|
|
292
|
+
const asst = assistants.find(function(a){ return a && a._id === id; }) || null;
|
|
293
|
+
if (!asst) {
|
|
294
|
+
summaryEl.textContent = 'No assistant selected; no tools.';
|
|
295
|
+
jsonEl.textContent = '';
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
let tools = [];
|
|
299
|
+
try{
|
|
300
|
+
if (Array.isArray(asst.tools)) {
|
|
301
|
+
tools = asst.tools;
|
|
302
|
+
} else if (typeof asst.tools === 'string' && asst.tools.trim()) {
|
|
303
|
+
// Many JOE schemas store codeeditor JSON as a string; try to parse.
|
|
304
|
+
tools = JSON.parse(asst.tools);
|
|
305
|
+
}
|
|
306
|
+
}catch(e){
|
|
307
|
+
console.error('[ai-widget-test] error parsing assistant.tools JSON', e, asst.tools);
|
|
308
|
+
summaryEl.textContent = 'Error parsing tools JSON for this assistant. See console.';
|
|
309
|
+
jsonEl.textContent = String(asst.tools || '');
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (!tools.length) {
|
|
313
|
+
summaryEl.textContent = 'This assistant has no tools configured.';
|
|
314
|
+
jsonEl.textContent = '';
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const names = tools
|
|
318
|
+
.map(function(t){
|
|
319
|
+
return t && t.function && t.function.name || t.name || '[unnamed]';
|
|
320
|
+
})
|
|
321
|
+
.join(', ');
|
|
322
|
+
summaryEl.textContent = 'Configured tools: ' + names;
|
|
323
|
+
jsonEl.textContent = JSON.stringify(tools, null, 2);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function applyAssistantToWidget(widget, assistantId){
|
|
327
|
+
try{
|
|
328
|
+
if (!widget) { return; }
|
|
329
|
+
const val = assistantId || '';
|
|
330
|
+
if (val){
|
|
331
|
+
widget.setAttribute('ai_assistant_id', val);
|
|
332
|
+
} else {
|
|
333
|
+
widget.removeAttribute('ai_assistant_id');
|
|
334
|
+
}
|
|
335
|
+
// Reset conversation so the widget starts fresh with new assistant.
|
|
336
|
+
// Do NOT create a new conversation here; the widget will lazily
|
|
337
|
+
// create one on first user message via startConversation().
|
|
338
|
+
widget.removeAttribute('conversation_id');
|
|
339
|
+
widget.conversation_id = null;
|
|
340
|
+
}catch(e){
|
|
341
|
+
console.error('[ai-widget-test] error applying assistant selection', e);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function applyConversationToWidget(widget, conversationId){
|
|
346
|
+
if (!widget || !conversationId) { return; }
|
|
347
|
+
widget.setAttribute('conversation_id', conversationId);
|
|
348
|
+
widget.conversation_id = conversationId;
|
|
349
|
+
// Let the widget load the existing history instead of starting a new convo
|
|
350
|
+
if (typeof widget.loadHistory === 'function'){
|
|
351
|
+
widget.loadHistory();
|
|
352
|
+
} else if (widget.connectedCallback){
|
|
353
|
+
widget.connectedCallback();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (document.readyState === 'loading'){
|
|
358
|
+
document.addEventListener('DOMContentLoaded', initAssistantSelect);
|
|
359
|
+
} else {
|
|
360
|
+
initAssistantSelect();
|
|
361
|
+
}
|
|
362
|
+
})();
|
|
363
|
+
</script>
|
|
364
|
+
</body>
|
|
365
|
+
</html>
|
|
366
|
+
|
|
367
|
+
|
package/_www/mcp-test.html
CHANGED
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
<button id="presetSearchSlimRecent">search clients (slim, recent)</button>
|
|
45
45
|
<button id="presetSearchWithCount">search clients (withCount)</button>
|
|
46
46
|
<button id="presetSearchCountOnly">search clients (countOnly)</button>
|
|
47
|
-
|
|
47
|
+
<button id="presetSaveObjects">saveObjects: batch save (example)</button>
|
|
48
|
+
<button id="presetUnderstandObject">understandObject: by _id</button>
|
|
48
49
|
</div>
|
|
49
50
|
|
|
50
51
|
<h3>Call JSON-RPC</h3>
|
|
@@ -77,6 +78,7 @@
|
|
|
77
78
|
const presetSearchWithCount = $('presetSearchWithCount');
|
|
78
79
|
const presetSearchCountOnly = $('presetSearchCountOnly');
|
|
79
80
|
const presetSaveObjects = $('presetSaveObjects');
|
|
81
|
+
const presetUnderstandObject = $('presetUnderstandObject');
|
|
80
82
|
|
|
81
83
|
// Try to infer base from window location
|
|
82
84
|
base.value = base.value || (location.origin);
|
|
@@ -219,6 +221,13 @@
|
|
|
219
221
|
concurrency: 5
|
|
220
222
|
}, null, 2);
|
|
221
223
|
};
|
|
224
|
+
|
|
225
|
+
presetUnderstandObject.onclick = function(){
|
|
226
|
+
toolSel.value = 'understandObject';
|
|
227
|
+
renderToolInfo();
|
|
228
|
+
// Provide a template; user should replace _id with a real object id.
|
|
229
|
+
params.value = JSON.stringify({ _id: "REPLACE_WITH_OBJECT_ID", depth: 2 }, null, 2);
|
|
230
|
+
};
|
|
222
231
|
|
|
223
232
|
callBtn.onclick = async function(){
|
|
224
233
|
setStatus(callStatus, 'Calling...', null);
|
package/css/joe-styles.css
CHANGED
|
@@ -344,7 +344,15 @@ html.no-touch .joe-button.joe-orangegrey-button:hover{background-color:#ff8804 !
|
|
|
344
344
|
.joe-blue-button, .joe-preview-button{background-color:#336699; color:#ddd;}
|
|
345
345
|
html.no-touch .joe-button.joe-blue-button:hover,html.no-touch .joe-button.joe-preview-button:hover{background-color:#447799;color: #fff;}
|
|
346
346
|
|
|
347
|
-
.joe-ai-button{
|
|
347
|
+
.joe-ai-button,.joe-ai-autofill-button{
|
|
348
|
+
background-color:#0D9488;
|
|
349
|
+
color:#fff;
|
|
350
|
+
background-image: url("../img/svgs/ai_assistant_white.svg");
|
|
351
|
+
background-size: 32px;
|
|
352
|
+
background-position-x: 6px !important;
|
|
353
|
+
float:right;
|
|
354
|
+
}
|
|
355
|
+
html.no-touch .joe-button.joe-ai-button:hover,html.no-touch .joe-button.joe-ai-autofill-button:hover{background-color:#0F766E;color: #fff;}
|
|
348
356
|
|
|
349
357
|
.joe-red-button{background-color:#Bb6655;}
|
|
350
358
|
html.no-touch .joe-button.joe-red-button:hover{background-color:#ff7755;color: #fff;}
|
|
@@ -2915,7 +2923,7 @@ joe-datepicker-holder > .Zebra_DatePicker.dp_visible {
|
|
|
2915
2923
|
}
|
|
2916
2924
|
|
|
2917
2925
|
|
|
2918
|
-
.joe-object-field .joe-button {
|
|
2926
|
+
.joe-object-field.button-field .joe-button {
|
|
2919
2927
|
position:relative;
|
|
2920
2928
|
/* max-width: 200px; */
|
|
2921
2929
|
/* margin: 5px auto; */
|
|
@@ -2946,7 +2954,7 @@ capp-card joe-button {
|
|
|
2946
2954
|
.joe-button.multiline {
|
|
2947
2955
|
line-height: 16px;
|
|
2948
2956
|
}
|
|
2949
|
-
html.no-touch .joe-object-field .joe-button:hover{
|
|
2957
|
+
html.no-touch .joe-object-field.joe-button-field .joe-button:hover{
|
|
2950
2958
|
background-color:#ccc;
|
|
2951
2959
|
}
|
|
2952
2960
|
.url-field .joe-button {
|
package/css/joe.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* --------------------------------------------------------
|
|
2
2
|
*
|
|
3
|
-
* json-object-editor - v0.10.
|
|
3
|
+
* json-object-editor - v0.10.632
|
|
4
4
|
* Created by: Corey Hadden
|
|
5
5
|
*
|
|
6
6
|
* -------------------------------------------------------- */
|
|
@@ -832,7 +832,15 @@ html.no-touch .joe-button.joe-orangegrey-button:hover{background-color:#ff8804 !
|
|
|
832
832
|
.joe-blue-button, .joe-preview-button{background-color:#336699; color:#ddd;}
|
|
833
833
|
html.no-touch .joe-button.joe-blue-button:hover,html.no-touch .joe-button.joe-preview-button:hover{background-color:#447799;color: #fff;}
|
|
834
834
|
|
|
835
|
-
.joe-ai-button{
|
|
835
|
+
.joe-ai-button,.joe-ai-autofill-button{
|
|
836
|
+
background-color:#0D9488;
|
|
837
|
+
color:#fff;
|
|
838
|
+
background-image: url("../img/svgs/ai_assistant_white.svg");
|
|
839
|
+
background-size: 32px;
|
|
840
|
+
background-position-x: 6px !important;
|
|
841
|
+
float:right;
|
|
842
|
+
}
|
|
843
|
+
html.no-touch .joe-button.joe-ai-button:hover,html.no-touch .joe-button.joe-ai-autofill-button:hover{background-color:#0F766E;color: #fff;}
|
|
836
844
|
|
|
837
845
|
.joe-red-button{background-color:#Bb6655;}
|
|
838
846
|
html.no-touch .joe-button.joe-red-button:hover{background-color:#ff7755;color: #fff;}
|
|
@@ -3403,7 +3411,7 @@ joe-datepicker-holder > .Zebra_DatePicker.dp_visible {
|
|
|
3403
3411
|
}
|
|
3404
3412
|
|
|
3405
3413
|
|
|
3406
|
-
.joe-object-field .joe-button {
|
|
3414
|
+
.joe-object-field.button-field .joe-button {
|
|
3407
3415
|
position:relative;
|
|
3408
3416
|
/* max-width: 200px; */
|
|
3409
3417
|
/* margin: 5px auto; */
|
|
@@ -3434,7 +3442,7 @@ capp-card joe-button {
|
|
|
3434
3442
|
.joe-button.multiline {
|
|
3435
3443
|
line-height: 16px;
|
|
3436
3444
|
}
|
|
3437
|
-
html.no-touch .joe-object-field .joe-button:hover{
|
|
3445
|
+
html.no-touch .joe-object-field.joe-button-field .joe-button:hover{
|
|
3438
3446
|
background-color:#ccc;
|
|
3439
3447
|
}
|
|
3440
3448
|
.url-field .joe-button {
|