json-object-editor 0.10.425 → 0.10.430

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/js/joe-ai.js CHANGED
@@ -3,7 +3,7 @@
3
3
  const Ai = {};
4
4
  const self = this;
5
5
  Ai._openChats = {}; // Conversation ID -> element
6
- Ai.default_ai = null; // Default AI assistant ID
6
+ Ai.default_ai = null; // Default AI assistant object
7
7
  // ========== COMPONENTS ==========
8
8
 
9
9
  class JoeAIChatbox extends HTMLElement {
@@ -13,10 +13,15 @@
13
13
 
14
14
  this.attachShadow({ mode: 'open' });
15
15
  this.messages = [];
16
-
16
+ this.UI={header:null,content:null,footer:null};
17
+ this.conversation = null;
18
+ this.conversation_id = null;
19
+ this.currentRunId = null;
20
+ this.thread_id = null;
21
+ this.user_id = null;
17
22
  }
18
23
 
19
- connectedCallback() {
24
+ async connectedCallback() {
20
25
  this.conversation_id = this.getAttribute('conversation_id');
21
26
 
22
27
  this.selected_assistant_id = Ai.default_ai?Ai.default_ai.value:null;
@@ -25,9 +30,102 @@
25
30
  this.renderError("Missing conversation_id");
26
31
  return;
27
32
  }
28
- this.loadConversation();
33
+
34
+ var c = await this.loadConversation();
29
35
  this.getAllAssistants();
36
+ this.setupUI();
37
+ }
38
+
39
+ setupUI() {
40
+ // Set up UI elements and event listeners
41
+ // Inject external CSS
42
+ const styleLink = document.createElement('link');
43
+ styleLink.setAttribute('rel', 'stylesheet');
44
+ styleLink.setAttribute('href', '/JsonObjectEditor/css/joe-ai.css'); // Adjust path as needed
45
+ this.shadowRoot.appendChild(styleLink);
46
+
47
+
48
+ Ai.default_ai
49
+ /*HEADER*/
50
+ Ai.getDefaultAssistant();
51
+ const assistantOptions = _joe.Data.ai_assistant.map(a => {
52
+
53
+ const label = a.name || a.title || 'Assistant';
54
+ const value = a._id;
55
+ const selected = value === this.selected_assistant_id ? 'selected' : '';
56
+
57
+ return `<option value="${value}" ${selected}>${label}</option>`;
58
+ }).join('');
59
+
60
+ const assistantSelect = `
61
+ <label-select-wrapper>
62
+ <label class="assistant-select-label" title="joe ai assistants">${_joe && _joe.SVG.icon.assistant}</label>
63
+ <select id="assistant-select">
64
+ ${assistantOptions}
65
+ </select>
66
+ </label-select-wrapper>
67
+ `;
68
+
69
+ /*CONTENT*/
70
+ const chatMessages = this.messages.map(msg => this.renderMessage(msg)).reverse().join('');
71
+
72
+ // Build inner HTML in a wrapper
73
+ const wrapper = document.createElement('chatbox-wrapper');
74
+ wrapper.className = 'chatbox';
75
+ wrapper.innerHTML = `
76
+ <div class="close-btn" title="Close Chatbox" >${_joe.SVG.icon.close}</div>
77
+ <chat-header>
78
+ <chat-title>${this.conversation.name}</chat-title>
79
+ <p>${this.conversation.info||''}</p>
80
+ ${assistantSelect}
81
+ </chat-header>
82
+ <chat-content>${chatMessages}</chat-content>
83
+ <chat-footer>
84
+ <textarea id="chat-input" type="text" placeholder="Type a message..."></textarea>
85
+ <button id="send-button">Send</button>
86
+ </chat-footer>
87
+ `;
88
+
89
+ this.shadowRoot.appendChild(wrapper);
90
+
91
+ ['header','content','header'].map(u=>{
92
+ this.UI[u] = this.shadowRoot.querySelector('chat-'+u);
93
+ })
94
+ this.UI.content.update = this.updateMessages.bind(this);
95
+ setTimeout(() => {
96
+ this.UI.content.scrollTop = this.UI.content.scrollHeight;
97
+ }, 100);
98
+ this.UI.content.scrollTop = this.UI.content.scrollHeight;
99
+
100
+ this.UI.textarea = this.shadowRoot.getElementById('chat-input');
101
+ this.UI.textarea.addEventListener('keydown', (e) => {
102
+ if (e.key === 'Enter' && !e.shiftKey) {
103
+ e.preventDefault(); // Prevent newline if needed
104
+ console.log('Enter pressed!');
105
+ this.sendMessage();
106
+ }
107
+ });
30
108
 
109
+ this.UI.sendButton = this.shadowRoot.getElementById('send-button');
110
+ this.UI.sendButton.addEventListener('click', () => this.sendMessage());
111
+
112
+ // Wire up the close button
113
+ this.shadowRoot.querySelector('.close-btn').addEventListener('click', () => {
114
+ //this.closeChat()
115
+ this.closest('joe-ai-chatbox').closeChat();
116
+ });
117
+ // this.ui.assistant_select = this.shadowRoot.querySelector('#assistant-select');
118
+ // this.ui.assistant_select?.addEventListener('change', (e) => {
119
+ // this.selected_assistant_id = e.target.value;
120
+ // });
121
+ //this.selected_assistant_id = this.ui.assistant_select?.value || null;
122
+
123
+ }
124
+ updateMessages(messages){
125
+ messages && (this.messages = messages);
126
+ const chatMessages = this.messages.map(msg => this.renderMessage(msg)).reverse().join('');
127
+ this.UI.content.innerHTML = chatMessages;
128
+ this.UI.content.scrollTop = this.UI.content.scrollHeight;
31
129
  }
32
130
  async getAllAssistants() {
33
131
  const res = await fetch('/API/item/ai_assistant');
@@ -51,6 +149,7 @@
51
149
 
52
150
  }
53
151
  async loadConversation() {
152
+ //load conversation and messages into this
54
153
  try {
55
154
  const res = await fetch(`/API/object/ai_conversation/_id/${this.conversation_id}`);
56
155
  const convo = await res.json();
@@ -66,9 +165,11 @@
66
165
  const resThread = await fetch(`/API/plugin/chatgpt-assistants/getThreadMessages?thread_id=${convo.thread_id}`);
67
166
  const threadMessages = await resThread.json();
68
167
  this.messages = threadMessages?.messages || [];
168
+ this.thread_id = convo.thread_id;
169
+ this.user = $J.get(convo.user);
69
170
  }
70
171
 
71
- this.render();
172
+ return {conversation:convo,messages:this.messages};
72
173
  } catch (err) {
73
174
  console.error("Chatbox load error:", err);
74
175
  this.renderError("Error loading conversation.");
@@ -87,121 +188,40 @@
87
188
  const convoName = this.conversation.name || "Untitled Conversation";
88
189
  const convoInfo = this.conversation.info || "";
89
190
 
90
- const chatMessages = this.messages.map(msg => this.renderMessage(msg)).reverse().join('');
91
191
 
92
- const assistantOptions = (this.conversation.assistants || []).map(a => {
93
- const meta = this.getAssistantInfo(a) || {};
94
-
95
- const label = meta.name || a.name || a.title || 'Assistant';
96
- const value = meta._id;
97
- const selected = value === this.selected_assistant_id ? 'selected' : '';
98
-
99
- return `<option value="${value}" ${selected}>${label}</option>`;
100
- }).join('');
101
-
102
- const assistantSelect = `
103
- <label-select-wrapper>
104
- <label class="assistant-select-label" title="joe ai assistants">${_joe && _joe.SVG.icon.assistant}</label>
105
- <select id="assistant-select">
106
- ${assistantOptions}
107
- </select>
108
- </label-select-wrapper>
109
- `;
110
192
 
111
- this.shadowRoot.innerHTML = `
112
- <style>
113
-
114
- .chatbox {
115
- padding:10px; display:flex; flex-direction:column; height:100%;
116
- }
117
- .close-btn {
118
- position: absolute;
119
- top: 8px;
120
- right: 8px;
121
- width: 24px;
122
- height: 24px;
123
- cursor: pointer;
124
- }
125
- .close-btn svg { width:100%; height:100%; }
126
- .header { margin-bottom:10px; }
127
- .header h2 { margin:0; font-size:18px; }
128
- .header p { margin:4px 0 0 0; font-size:12px; color:#666; }
129
- .header svg{
130
- width: 36px;
131
- height: 36px;
132
- }
133
- label-select-wrapper{
134
- display: flex; align-items: center; gap: 8px;
135
- }
136
- .messages { flex:1; overflow:auto; max-height:300px; margin-bottom:10px;
137
- background: #fff;
138
- border: 1px solid #ccc;
139
- }
140
- .thinking-message {
141
- font-size: 13px;
142
- color: #888;
143
- font-style: italic;
144
- margin: 10px 0;
145
- text-align: center;
146
- }
147
- .meta {
148
- font-size: 14px;
149
- font-weight: bold;
150
- margin-bottom: 2px;
151
- display: flex;
152
- align-items: center;
153
- gap: 8px;
154
- color:#99999999;
155
- }
156
- .content {
157
- font-size: 14px;
158
- white-space: pre-wrap;
159
- }
160
- .inputRow { display:flex; gap:8px; }
161
- input[type="text"] { flex:1; padding:8px; border-radius:4px; border:1px solid #ccc; }
162
- button { padding:8px 12px; background:#007bff; color:white; border:none; border-radius:4px; cursor:pointer; }
163
- button:disabled { opacity:0.5; cursor:default; }
164
- .message { margin-bottom:10px; padding:8px; border-radius:6px; background:#f5f5f5; }
165
- .message.assistant { background:#eef; }
166
- .message.system { background:#fee; }
167
- </style>
168
-
169
- <div class="chatbox">
170
- <div class="close-btn" title="Close Chatbox">
171
- ${_joe.SVG.icon.close}
172
- </div>
173
- <div class="header">
174
- <h2>${convoName}</h2>
175
- <p>${convoInfo}</p>
176
- ${assistantSelect}
177
- </div>
178
- <div class="messages">
179
- ${chatMessages}
180
- </div>
181
- <div class="inputRow">
182
- <input id="chat-input" type="text" placeholder="Type a message..." />
183
- <button id="send-button">Send</button>
184
- </div>
185
- </div>
186
- `;
187
- const messagesDiv = this.shadowRoot.querySelector('.messages');
188
- messagesDiv.scrollTop = messagesDiv.scrollHeight;
189
- this.shadowRoot.getElementById('send-button').addEventListener('click', () => this.sendMessage());
190
-
191
- // Wire up the close button
192
- this.shadowRoot.querySelector('.close-btn').addEventListener('click', () => this.closeChat());
193
- this.ui.assistant_select = this.shadowRoot.querySelector('#assistant-select');
194
- this.ui.assistant_select?.addEventListener('change', (e) => {
195
- this.selected_assistant_id = e.target.value;
196
- });
197
- this.selected_assistant_id = this.ui.assistant_select?.value || null;
193
+
194
+
195
+
196
+
197
+
198
198
 
199
199
  }
200
-
200
+ processMessageText(text) {
201
+
202
+ // const replaced = text.replace(
203
+ // /\{\{\{BEGIN_OBJECT:(.*?)\}\}\}[\s\S]*?\{\{\{END_OBJECT:\1\}\}\}/g,
204
+ // (match, cuid) => `<joe-object object_id="${cuid}"></joe-object>`
205
+ // );
206
+ let didReplace = false;
207
+
208
+ const replaced = text.replace(
209
+ /\{\{\{BEGIN_OBJECT:(.*?)\}\}\}[\s\S]*?\{\{\{END_OBJECT:\1\}\}\}/g,
210
+ (match, cuid) => {
211
+ didReplace = true;
212
+ return `<joe-object object_id="${cuid}"></joe-object>`;
213
+ }
214
+ );
215
+
216
+ return {text:replaced, replaced:didReplace};
217
+ }
201
218
  renderMessage(msg) {
202
219
  const role = msg.role || 'user';
203
220
  const classes = `message ${role}`;
204
-
221
+ var username = role;
222
+ if(role === 'user'){
223
+ username = this.user.name||'User';
224
+ }
205
225
  let contentText = '';
206
226
 
207
227
  if (Array.isArray(msg.content)) {
@@ -217,14 +237,22 @@
217
237
  } else {
218
238
  contentText = '[Unsupported message format]';
219
239
  }
220
-
240
+
241
+ const ctInfo = this.processMessageText(contentText);
242
+ contentText = ctInfo.text;
243
+ if(ctInfo.replaced){
244
+ username = 'platform'
245
+ }
246
+
221
247
  // Build timestamp
222
248
  const createdAt = msg.created_at ? new Date(msg.created_at * 1000) : null; // OpenAI sends timestamps in seconds
223
249
  const timestamp = createdAt ? createdAt.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : '';
224
250
 
225
251
  return `
226
252
  <div class="${classes}">
227
- <div class="meta"><strong>${role.toUpperCase()}</strong> ${timestamp ? `<span class="timestamp">${timestamp}</span>` : ''}</div>
253
+ <div class="meta">
254
+ <participant-name>${username.toUpperCase()}</participant-name>
255
+ ${timestamp ? `<span class="timestamp">${timestamp}</span>` : ''}</div>
228
256
  <div class="content">${contentText}</div>
229
257
  </div>
230
258
  `;
@@ -233,14 +261,29 @@
233
261
  renderError(message) {
234
262
  this.shadowRoot.innerHTML = `<div style="color:red;">${message}</div>`;
235
263
  }
236
-
237
- async sendMessage() {
238
- const input = this.shadowRoot.getElementById('chat-input');
264
+
265
+
266
+ async getResponse(conversation_id,content,role,assistant_id){
267
+ const response = await fetch('/API/plugin/chatgpt-assistants/addMessage', {
268
+ method: 'POST',
269
+ headers: { 'Content-Type': 'application/json' },
270
+ body: JSON.stringify({
271
+ conversation_id: conversation_id,
272
+ content: content,
273
+ role: role||'system',
274
+ assistant_id: assistant_id||Ai.default_ai?Ai.default_ai.value:null
275
+ })
276
+ }).then(res => res.json());
277
+ }
278
+
279
+ async sendMessage() {//** */
280
+
281
+ const input = this.UI.textarea;
239
282
  const message = input.value.trim();
240
283
  if (!message) return;
241
284
 
242
285
  input.disabled = true;
243
- this.shadowRoot.getElementById('send-button').disabled = true;
286
+ this.UI.sendButton.disabled = true;
244
287
 
245
288
  try {
246
289
  const response = await fetch('/API/plugin/chatgpt-assistants/addMessage', {
@@ -256,7 +299,9 @@
256
299
  if (response.success && response.runObj) {
257
300
  this.currentRunId = response.runObj.id; // Store the run ID for polling
258
301
  await this.loadConversation(); // reload messages
302
+ this.UI.content.update(); // update messages
259
303
  this.startPolling(); // 🌸 start watching for assistant reply!
304
+ input.value = '';
260
305
  } else {
261
306
  alert('Failed to send message.');
262
307
  }
@@ -265,9 +310,9 @@
265
310
  alert('Error sending message.');
266
311
  }
267
312
 
268
- input.value = '';
269
- input.disabled = false;
270
- this.shadowRoot.getElementById('send-button').disabled = false;
313
+
314
+ // input.disabled = false;
315
+ // this.shadowRoot.getElementById('send-button').disabled = false;
271
316
  }
272
317
 
273
318
  startPolling() {
@@ -286,18 +331,21 @@
286
331
  const threadMessages = await resThread.json();
287
332
 
288
333
  if (threadMessages?.messages) {
289
- this.messages = threadMessages.messages;
290
- this.render();
334
+ this.UI.content.update(threadMessages.messages);
335
+ //this.messages = threadMessages.messages;
336
+ //this.render();
291
337
  }
292
338
 
293
339
  clearInterval(this.pollingInterval);
294
340
  this.pollingInterval = null;
295
341
  this.hideThinkingMessage();
342
+ this.UI.textarea.disabled = false;
343
+ this.UI.sendButton.disabled = false;
296
344
  }
297
345
  }, 2000);
298
346
  }
299
347
  showThinkingMessage() {
300
- const messagesDiv = this.shadowRoot.querySelector('.messages');
348
+ const messagesDiv = this.UI.content;
301
349
  if (!messagesDiv) return;
302
350
 
303
351
  // Pull assistant thinking text
@@ -329,14 +377,69 @@
329
377
  }
330
378
 
331
379
  customElements.define('joe-ai-chatbox', JoeAIChatbox);
332
-
380
+
381
+
382
+ class JoeObject extends HTMLElement {
383
+ constructor() {
384
+ super();
385
+
386
+
387
+ this.object_id = this.getAttribute('object_id');
388
+ this.object = $J.get(this.object_id);
389
+
390
+ }
391
+ connectedCallback() {
392
+ const id = this.getAttribute('object_id');
393
+ var sTemp = $J.schema('business')?.listView?.title||false;
394
+ this.innerHTML = (sTemp)?JOE.propAsFuncOrValue(sTemp,this.object) :`<jo-title>${this.object.name}</jo-title>
395
+ <jo-subtitle>${this.object.info} - ${this.object._id}</jo-subtitle>`;
396
+ this.addEventListener('click', () => {
397
+ // Handle click event here, e.g., open the object in a new tab or show details
398
+ goJoe(_joe.search(this.object._id)[0],{schema:this.object.itemtype})
399
+ //window.open(`/object/${this.object_id}`, '_blank');
400
+ });
401
+ }
402
+
403
+ }
404
+ customElements.define('joe-object', JoeObject);
405
+
406
+
407
+ //**YES**
408
+ Ai.spawnChatHelper = async function(object_id,user_id=_joe.User._id,conversation_id) {
409
+ //if not conversation_id, create a new one
410
+ let convo_id = conversation_id;
411
+ var newChat = false;
412
+ if(!convo_id){
413
+ const response = await fetch('/API/plugin/chatgpt-assistants/createConversation', {
414
+ method: 'POST',
415
+ headers: { 'Content-Type': 'application/json' },
416
+ body: JSON.stringify({
417
+ object_id,
418
+ user_id
419
+ })
420
+ }).then(res => res.json());
421
+ convo_id = response?.conversation?._id;
422
+ newChat = true;
423
+ if(response.error){
424
+ console.error('❌ Failed to create conversation:', response.error);
425
+ return;
426
+ }
427
+ }
428
+
429
+ await _joe.Ai.spawnContextualChat(convo_id,{object_id,newChat});
430
+ }
431
+ Ai.getDefaultAssistant = function() {
432
+
433
+ Ai.default_ai = Ai.default_ai ||_joe.Data.setting.where({name:'DEFAULT_AI_ASSISTANT'})[0]||false;
434
+ return Ai.default_ai;
435
+ }
333
436
  // ========== HELPERS ==========
334
437
  Ai.spawnContextualChat = async function(conversationId, options = {}) {
335
438
  if (!conversationId) {
336
439
  console.warn("Missing conversation ID for chat spawn.");
337
440
  return;
338
441
  }
339
- Ai.default_ai = _joe.Data.setting.where({name:'DEFAULT_AI_ASSISTANT'})[0]||false;
442
+ Ai.getDefaultAssistant();
340
443
 
341
444
  // 1. Check if chat already open
342
445
  if (Ai._openChats[conversationId]) {
@@ -346,9 +449,11 @@
346
449
  }
347
450
 
348
451
  try {
452
+ const flattened = _joe.Object.flatten(options.object_id);
453
+ if (options.newChat) {
349
454
  // 2. Prepare context
350
- const flattened = _joe.Object.flatten();
351
- const contextInstructions = _joe.Ai.generateContextInstructions(flattened);
455
+
456
+ const contextInstructions = _joe.Ai.generateContextInstructions(flattened,options.object_id);
352
457
 
353
458
  // 3. Inject context into backend
354
459
  const contextResult = await fetch('/API/plugin/chatgpt-assistants/addMessage', {
@@ -356,9 +461,10 @@
356
461
  headers: { 'Content-Type': 'application/json' },
357
462
  body: JSON.stringify({
358
463
  conversation_id: conversationId,
359
- //role: 'system',
464
+ role: 'JOE',
360
465
  content: contextInstructions,
361
- assistant_id: Ai.default_ai.value
466
+ assistant_id: Ai.default_ai.value,
467
+ object_id: options.object_id
362
468
  })
363
469
  }).then(res => res.json());
364
470
 
@@ -366,38 +472,51 @@
366
472
  console.error('❌ Failed to inject context:', contextResult?.error);
367
473
  return;
368
474
  }
369
-
475
+ }
370
476
  // 4. Create new chatbox
371
477
  const chat = document.createElement('joe-ai-chatbox');
372
478
  chat.setAttribute('conversation_id', conversationId);
373
-
479
+ const screenWidth = window.innerWidth;
480
+ if(screenWidth <= 768){
481
+ chat.setAttribute('mobile', 'true');
482
+ chat.style.width = 'auto';
483
+ chat.style.left = '0px';
484
+ }
485
+ else{
486
+ chat.setAttribute('mobile', 'false');
487
+ chat.style.width = options.width || '640px';
488
+ chat.style.left = 'auto';
489
+ }
374
490
  // Apply styles
375
- chat.style.width = options.width || '400px';
376
- chat.style.height = options.height || '420px';
377
- chat.style.bottom = options.bottom || '20px';
378
- chat.style.right = options.right || '20px';
491
+
492
+ //chat.style.height = options.height || '420px';
493
+ chat.style.bottom = options.bottom || '50px';
494
+ chat.style.right = options.right || '0px';
495
+ chat.style.top = options.top || '50px';
379
496
  chat.style.position = 'fixed';
380
497
  chat.style.zIndex = '10000';
381
498
  chat.style.background = '#efefef';
382
499
  chat.style.border = '1px solid #fff';
383
500
  chat.style.borderRadius = '8px';
384
- chat.style.boxShadow = '0px 2px 10px rgba(0,0,0,0.1)';
501
+ chat.style.boxShadow = '0px 1px 4px rgba(0, 0, 0, 0.3)';
385
502
  chat.style.padding = '5px';
386
-
503
+ chat.style.margin = '5px';
504
+
387
505
  document.body.appendChild(chat);
388
506
 
389
507
  // 5. Track it
390
508
  Ai._openChats[conversationId] = chat;
391
509
 
510
+ if (options.newChat) {
392
511
  // 6. Show soft local UI message
393
- _joe.Ai.injectSystemMessage(conversationId, `Context injected: ${flattened.name || flattened.title || 'Object'} (${flattened._id})`);
394
-
512
+ //_joe.Ai.injectSystemMessage(conversationId, `Context injected: ${flattened.name || flattened.title || 'Object'} (${flattened._id})`);
513
+ }
395
514
  } catch (err) {
396
515
  console.error('❌ spawnChat context injection failed:', err);
397
516
  }
398
517
  };
399
518
 
400
- Ai.spawnChat = function(conversationId, options = {}) {
519
+ /* Ai.spawnChat = function(conversationId, options = {}) {
401
520
  if (!conversationId) {
402
521
  console.warn("Missing conversation ID for chat spawn.");
403
522
  return;
@@ -442,27 +561,31 @@
442
561
  return chat;
443
562
  // 4. Optionally clean up when chatbox is removed (if you wire close buttons later)
444
563
  };
445
-
446
- Ai.generateContextInstructions = function(flattenedObj) {
564
+ */
565
+ Ai.generateContextInstructions = function(flattenedObj,object_id) {
447
566
  if (!flattenedObj) return '';
448
567
 
449
- let context = "Context: You are assisting the user with the following object:\n\n";
450
-
451
- for (const [key, value] of Object.entries(flattenedObj)) {
452
- if (typeof value === 'object' && value !== null) {
453
- context += `- ${key}: (linked object)\n`;
454
- for (const [subkey, subval] of Object.entries(value)) {
455
- context += ` • ${subkey}: ${subval}\n`;
456
- }
457
- } else {
458
- context += `- ${key}: ${value}\n`;
459
- }
460
- }
568
+ let context = `{{{BEGIN_OBJECT:${object_id}}}}`+
569
+ "Context: You are assisting the user with the following object:\n\n";
570
+
571
+ context += JSON.stringify(flattenedObj, null, 2) + "\n\n";
572
+ // for (const [key, value] of Object.entries(flattenedObj)) {
573
+ // if (typeof value === 'object' && value !== null) {
574
+ // context += `- ${key}: (linked object)\n`;
575
+ // for (const [subkey, subval] of Object.entries(value)) {
576
+ // context += ` • ${subkey}: ${subval}\n`;
577
+ // }
578
+ // } else {
579
+ // context += `- ${key}: ${value}\n`;
580
+ // }
581
+ // }
461
582
 
462
- context += `\nAlways refer to this context when answering questions or completing tasks related to this object.\n`;
583
+ context += `\nAlways refer to this context when answering questions or completing tasks related to this object.\n`+
584
+ `{{{END_OBJECT:${object_id}}}}`;
463
585
 
464
586
  return context;
465
587
  };
588
+
466
589
  Ai.injectSystemMessage = async function(conversationId, text) {
467
590
  if (!conversationId || !text) return;
468
591
 
@@ -470,7 +593,7 @@
470
593
  // Create a system-style message object
471
594
  const messageObj = {
472
595
  conversation_id: conversationId,
473
- role: 'system',
596
+ role: 'joe',
474
597
  content: text,
475
598
  created: new Date().toISOString()
476
599
  };
package/js/joe-full.js CHANGED
@@ -19585,7 +19585,10 @@ Field Rendering Helpers
19585
19585
 
19586
19586
  for (const [field, val] of Object.entries(object)) {
19587
19587
  if (val === undefined || val === null) continue;
19588
- if (field == '_id') continue;
19588
+ if (field == '_id'){
19589
+ flattened[field] = val;
19590
+ continue;
19591
+ }
19589
19592
 
19590
19593
  if (typeof val === 'string' && $c.isCuid(val) && recursive && depth > 0) {
19591
19594
  if (!visited.has(val)) {
package/js/joe.js CHANGED
@@ -1,9 +1,3 @@
1
- /* --------------------------------------------------------
2
- *
3
- * JOE - v1.5.0
4
- * Created by: Corey Hadden
5
- *
6
- * -------------------------------------------------------- */
7
1
  /* --------------------------------------------------------
8
2
  *
9
3
  * JOE - v1.5.0
@@ -8292,7 +8286,10 @@ Field Rendering Helpers
8292
8286
 
8293
8287
  for (const [field, val] of Object.entries(object)) {
8294
8288
  if (val === undefined || val === null) continue;
8295
- if (field == '_id') continue;
8289
+ if (field == '_id'){
8290
+ flattened[field] = val;
8291
+ continue;
8292
+ }
8296
8293
 
8297
8294
  if (typeof val === 'string' && $c.isCuid(val) && recursive && depth > 0) {
8298
8295
  if (!visited.has(val)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-object-editor",
3
- "version": "0.10.425",
3
+ "version": "0.10.430",
4
4
  "description": "JOE the Json Object Editor | Platform Edition",
5
5
  "main": "app.js",
6
6
  "scripts": {
@@ -311,8 +311,9 @@
311
311
  JOE.init();
312
312
  //capp.init();
313
313
  var capp_apps = '${USERAPPS}'.split(',') || [];
314
+ var caobj = ${USERAPPSOBJ};
314
315
  var all_apps = '${APPS}'.split(',') || [];
315
- capp.Menu.addFromApps(capp_apps,'<joe-subtext>${this.webconfig.name}</joe-subtext>${APPNAME}');
316
+ capp.Menu.addFromApps(caobj,'<joe-subtext>${this.webconfig.name}</joe-subtext>${APPNAME}');
316
317
  //capp.Button.add('Docs',null,'window.open(\'/JsonObjectEditor/docs.html\')','capp-header');
317
318
 
318
319