json-object-editor 0.10.400 → 0.10.422

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.
@@ -534,9 +534,11 @@ function JsonObjectEditor(specs){
534
534
  var code = e.keyCode
535
535
  var nonBackElements = ['input','select','textarea'];
536
536
  var isInputElement = nonBackElements.indexOf(e.target.tagName.toLowerCase()) != -1;
537
+ var isEditable = e.target.isContentEditable || $(e.target).closest('joe-ai-chatbox').length;
538
+
537
539
  if (code == 8) {//BACKBUTTON PRESSED
538
- if(isInputElement){
539
- //return false;
540
+ if(isInputElement || isEditable){
541
+ return; // allow natural Backspace behavior
540
542
  }
541
543
  else{
542
544
  self.goBack();
@@ -5766,6 +5768,7 @@ Y | Object Field
5766
5768
 
5767
5769
  var display = self.propAsFuncOrValue(prop.display, itemObj);
5768
5770
  var method = self.propAsFuncOrValue(prop.method, itemObj);
5771
+ var onclick = self.propAsFuncOrValue(prop.onclick, itemObj);
5769
5772
  var color = self.propAsFuncOrValue(prop.color, itemObj);
5770
5773
  var schema = self.propAsFuncOrValue(prop.schema, itemObj);
5771
5774
  var svgOverride = self.propAsFuncOrValue(prop.svg, itemObj);
@@ -5794,6 +5797,7 @@ Y | Object Field
5794
5797
  + (method ? 'action="' + method + '" ' : '')
5795
5798
  + (color ? 'color="' + color + '" ' : '')
5796
5799
  + (title ? 'title="' + title + '" ' : '')
5800
+ +(onclick ? 'onclick="' + onclick + '" ' : '')
5797
5801
  + styleAttr + ' '
5798
5802
  + 'data-btnid="' + (prop.name || '') + '">';
5799
5803
 
@@ -10460,6 +10464,11 @@ logit(intent)
10460
10464
  <--------------------------------------------------------------------*/
10461
10465
  this.SVG = {
10462
10466
  icon:{
10467
+ assistant:`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
10468
+ <path d="M294.49 618.18v64.49c0 22.42.95 35.46 2.84 39.12 2.4 4.94 6.93 8.52 12.29 9.71 9.18 2.13 18.6 3.01 28.02 2.64h17.31c18.81 0 28.22 6.04 28.22 18.11 0 13.96-9.93 22.51-29.8 25.64-24.41 2.74-48.98 3.87-73.55 3.37-29.87 0-51.01-5.79-63.44-17.38-12.42-11.59-18.66-31.41-18.7-59.47v-81.08c.85-11.33-1.15-22.68-5.81-33.04-5.69-7.66-14.07-12.88-23.46-14.6-11.09-2.78-18.54-13.19-17.58-24.58-1.07-11.43 6.42-21.9 17.58-24.58 9.41-1.95 17.77-7.32 23.46-15.07 4.7-10.35 6.7-21.71 5.81-33.04v-80.88c0-27.62 6.21-47.22 18.63-58.81 12.42-11.59 33.57-17.36 63.44-17.31 26.13-.73 52.27.66 78.17 4.16 6.92 1.05 13.37 4.16 18.5 8.92 4.27 4.08 6.66 9.75 6.61 15.66 0 12.07-9.41 18.11-28.22 18.11h-17.84c-16.04 0-27.05 1.72-33.04 5.15-5.92 3.96-9.36 10.72-9.05 17.84v92.71c-.35 31.23-8.72 52.03-25.11 62.38-1.29.88-2.06 2.34-2.05 3.9 0 2.42 2.93 6.06 8.79 10.9 6.54 5.96 11.3 13.63 13.74 22.14 3.19 11.34 4.62 23.11 4.23 34.89Zm435.01-134.4v-64.23c0-22.42-.95-35.46-2.84-39.12-2.4-4.94-6.93-8.52-12.29-9.71-9.18-2.13-18.6-3.01-28.02-2.64h-17.18c-18.81 0-28.22-6.04-28.22-18.11 0-13.96 9.93-22.42 29.8-25.37 24.42-2.73 48.98-3.86 73.55-3.37 29.87 0 51.01 5.79 63.44 17.38 12.42 11.59 18.63 31.19 18.63 58.81v81.28c-.88 11.33 1.12 22.69 5.81 33.04 5.75 7.62 14.1 12.87 23.46 14.74 11.16 2.71 18.62 13.22 17.51 24.65.74 9.18-4.03 17.93-12.16 22.27-4.09 1.86-8.33 3.34-12.69 4.43-8.51 2.69-15.35 9.08-18.63 17.38-2.71 9.15-3.85 18.69-3.37 28.22v81.01c0 27.8-6.21 47.47-18.63 59.01-12.42 11.54-33.57 17.33-63.44 17.38-26.13.74-52.27-.65-78.17-4.16-6.92-1.05-13.37-4.16-18.5-8.92-4.31-4.17-6.7-9.93-6.61-15.93 0-12.07 9.41-18.11 28.22-18.11 2.25 0 8.19.18 17.84.53 11.25.56 22.5-1.19 33.04-5.15 4.1-2.12 7.02-5.98 7.93-10.51 1.33-11.91 1.82-23.9 1.45-35.88v-64.49c-.51-12.88 1.38-25.74 5.55-37.93 4.19-9.77 11.07-18.15 19.82-24.18 1.29-.88 2.06-2.34 2.05-3.9 0-2.42-2.93-6.06-8.79-10.9-6.55-5.96-11.31-13.63-13.74-22.14-3.43-11.46-5.05-23.39-4.82-35.35Z"/>
10469
+ <rect width="74.5" height="189" x="409.75" y="456.75" rx="33.94" ry="33.94"/>
10470
+ <rect width="74.5" height="189" x="539.75" y="456.75" rx="33.94" ry="33.94"/>
10471
+ </svg>`,
10463
10472
  'favorite':'<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M4 2h16a1 1 0 0 1 1 1v19.276a.5.5 0 0 1-.704.457L12 19.03l-8.296 3.702A.5.5 0 0 1 3 22.276V3a1 1 0 0 1 1-1zm8 11.5l2.939 1.545-.561-3.272 2.377-2.318-3.286-.478L12 6l-1.47 2.977-3.285.478 2.377 2.318-.56 3.272L12 13.5z"/></svg>',
10464
10473
  'left':'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -5 36 36"><path d="M11.3 21C7.1 18.2 3.3 13.8 3.2 13.6 3.1 13.5 3 13.2 3 13 3 12.8 3.1 12.5 3.2 12.3 3.3 12.1 7.2 7.7 11.3 4.9 11.6 4.7 12 4.7 12.4 4.8 12.8 5.1 13 5.4 13 5.8L13 9.5C13 9.5 21.7 10 22.1 10.3 22.7 10.7 23 12 23 13 23 14.1 22.6 15.2 22.1 15.6 21.7 15.9 13 16.5 13 16.5L13 20.2C13 20.6 12.8 20.9 12.4 21.1 12.1 21.3 11.7 21.2 11.3 21Z"/></svg>',
10465
10474
  'cancel':'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -5 36 36"><path d="M13 0.2C5.9 0.2 0.2 5.9 0.2 13 0.2 20.1 5.9 25.8 13 25.8 20.1 25.8 25.8 20.1 25.8 13 25.8 5.9 20.1 0.2 13 0.2ZM18.8 17.4L17.4 18.8C17.1 19 16.7 19 16.5 18.8L13 15.3 9.5 18.8C9.3 19 8.9 19 8.6 18.8L7.2 17.4C7 17.1 7 16.7 7.2 16.5L10.7 13 7.2 9.5C7 9.3 7 8.9 7.2 8.6L8.6 7.2C8.9 7 9.3 7 9.5 7.2L13 10.7 16.5 7.2C16.7 7 17.1 7 17.4 7.2L18.8 8.6C19 8.9 19 9.3 18.8 9.5L15.3 13 18.8 16.5C19 16.7 19 17.1 18.8 17.4Z"/></svg>',
package/js/joe-ai.js ADDED
@@ -0,0 +1,376 @@
1
+ (function(){
2
+ // Define the joeAI namespace
3
+ const Ai = {};
4
+
5
+ Ai._openChats = {}; // Conversation ID -> element
6
+ // ========== COMPONENTS ==========
7
+
8
+ class JoeAIChatbox extends HTMLElement {
9
+ constructor() {
10
+ super();
11
+ this.attachShadow({ mode: 'open' });
12
+ this.messages = [];
13
+ }
14
+
15
+ connectedCallback() {
16
+ this.conversation_id = this.getAttribute('conversation_id');
17
+ this.selected_assistant_id = null;
18
+ this.ui = {};
19
+ if (!this.conversation_id) {
20
+ this.renderError("Missing conversation_id");
21
+ return;
22
+ }
23
+ this.loadConversation();
24
+ this.getAllAssistants();
25
+
26
+ }
27
+ async getAllAssistants() {
28
+ const res = await fetch('/API/item/ai_assistant');
29
+ const result = await res.json();
30
+ if (result.error) {
31
+ console.error("Error fetching assistants:", result.error);
32
+ }
33
+
34
+ this.allAssistants = {};
35
+
36
+ if (Array.isArray(result.item)) {
37
+ result.item.map(a => {
38
+ if (a._id) {
39
+ this.allAssistants[a._id] = a;
40
+ }
41
+ if (a.openai_id) {
42
+ this.allAssistants[a.openai_id] = a; // Optional dual-key if you prefer
43
+ }
44
+ });
45
+ }
46
+
47
+ }
48
+ async loadConversation() {
49
+ try {
50
+ const res = await fetch(`/API/object/ai_conversation/_id/${this.conversation_id}`);
51
+ const convo = await res.json();
52
+
53
+ if (!convo || convo.error) {
54
+ this.renderError("Conversation not found.");
55
+ return;
56
+ }
57
+ this.conversation = convo;
58
+ this.messages = [];
59
+
60
+ if (convo.thread_id) {
61
+ const resThread = await fetch(`/API/plugin/chatgpt-assistants/getThreadMessages?thread_id=${convo.thread_id}`);
62
+ const threadMessages = await resThread.json();
63
+ this.messages = threadMessages?.messages || [];
64
+ }
65
+
66
+ this.render();
67
+ } catch (err) {
68
+ console.error("Chatbox load error:", err);
69
+ this.renderError("Error loading conversation.");
70
+ }
71
+ }
72
+ getAssistantInfo(id){
73
+ const assistant = this.allAssistants[id];
74
+ if (assistant) {
75
+ return assistant;
76
+ } else {
77
+ console.warn("Assistant not found:", id);
78
+ return null;
79
+ }
80
+ }
81
+ render() {
82
+ const convoName = this.conversation.name || "Untitled Conversation";
83
+ const convoInfo = this.conversation.info || "";
84
+
85
+ const chatMessages = this.messages.map(msg => this.renderMessage(msg)).reverse().join('');
86
+
87
+ const assistantOptions = (this.conversation.assistants || []).map(a => {
88
+ const meta = this.getAssistantInfo(a) || {};
89
+
90
+ const label = meta.name || a.name || a.title || 'Assistant';
91
+ const value = meta._id;
92
+ const selected = value === this.selected_assistant_id ? 'selected' : '';
93
+
94
+ return `<option value="${value}" ${selected}>${label}</option>`;
95
+ }).join('');
96
+
97
+ const assistantSelect = `
98
+ <label-select-wrapper>
99
+ <label class="assistant-select-label" title="joe ai assistants">${_joe && _joe.SVG.icon.assistant}</label>
100
+ <select id="assistant-select">
101
+ ${assistantOptions}
102
+ </select>
103
+ </label-select-wrapper>
104
+ `;
105
+
106
+ this.shadowRoot.innerHTML = `
107
+ <style>
108
+
109
+ .chatbox {
110
+ padding:10px; display:flex; flex-direction:column; height:100%;
111
+ }
112
+ .close-btn {
113
+ position: absolute;
114
+ top: 8px;
115
+ right: 8px;
116
+ width: 24px;
117
+ height: 24px;
118
+ cursor: pointer;
119
+ }
120
+ .close-btn svg { width:100%; height:100%; }
121
+ .header { margin-bottom:10px; }
122
+ .header h2 { margin:0; font-size:18px; }
123
+ .header p { margin:4px 0 0 0; font-size:12px; color:#666; }
124
+ .header svg{
125
+ width: 36px;
126
+ height: 36px;
127
+ }
128
+ label-select-wrapper{
129
+ display: flex; align-items: center; gap: 8px;
130
+ }
131
+ .messages { flex:1; overflow:auto; max-height:300px; margin-bottom:10px;
132
+ background: #fff;
133
+ border: 1px solid #ccc;
134
+ }
135
+ .thinking-message {
136
+ font-size: 13px;
137
+ color: #888;
138
+ font-style: italic;
139
+ margin: 10px 0;
140
+ text-align: center;
141
+ }
142
+ .meta {
143
+ font-size: 14px;
144
+ font-weight: bold;
145
+ margin-bottom: 2px;
146
+ display: flex;
147
+ align-items: center;
148
+ gap: 8px;
149
+ color:#99999999;
150
+ }
151
+ .content {
152
+ font-size: 14px;
153
+ white-space: pre-wrap;
154
+ }
155
+ .inputRow { display:flex; gap:8px; }
156
+ input[type="text"] { flex:1; padding:8px; border-radius:4px; border:1px solid #ccc; }
157
+ button { padding:8px 12px; background:#007bff; color:white; border:none; border-radius:4px; cursor:pointer; }
158
+ button:disabled { opacity:0.5; cursor:default; }
159
+ .message { margin-bottom:10px; padding:8px; border-radius:6px; background:#f5f5f5; }
160
+ .message.assistant { background:#eef; }
161
+ .message.system { background:#fee; }
162
+ </style>
163
+
164
+ <div class="chatbox">
165
+ <div class="close-btn" title="Close Chatbox">
166
+ ${_joe.SVG.icon.close}
167
+ </div>
168
+ <div class="header">
169
+ <h2>${convoName}</h2>
170
+ <p>${convoInfo}</p>
171
+ ${assistantSelect}
172
+ </div>
173
+ <div class="messages">
174
+ ${chatMessages}
175
+ </div>
176
+ <div class="inputRow">
177
+ <input id="chat-input" type="text" placeholder="Type a message..." />
178
+ <button id="send-button">Send</button>
179
+ </div>
180
+ </div>
181
+ `;
182
+ const messagesDiv = this.shadowRoot.querySelector('.messages');
183
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
184
+ this.shadowRoot.getElementById('send-button').addEventListener('click', () => this.sendMessage());
185
+
186
+ // Wire up the close button
187
+ this.shadowRoot.querySelector('.close-btn').addEventListener('click', () => this.closeChat());
188
+ this.ui.assistant_select = this.shadowRoot.querySelector('#assistant-select');
189
+ this.ui.assistant_select?.addEventListener('change', (e) => {
190
+ this.selected_assistant_id = e.target.value;
191
+ });
192
+ this.selected_assistant_id = this.ui.assistant_select?.value || null;
193
+
194
+ }
195
+
196
+ renderMessage(msg) {
197
+ const role = msg.role || 'user';
198
+ const classes = `message ${role}`;
199
+
200
+ let contentText = '';
201
+
202
+ if (Array.isArray(msg.content)) {
203
+ // OpenAI style: array of parts
204
+ contentText = msg.content.map(part => {
205
+ if (part.type === 'text' && part.text && part.text.value) {
206
+ return part.text.value;
207
+ }
208
+ return '';
209
+ }).join('\n');
210
+ } else if (typeof msg.content === 'string') {
211
+ contentText = msg.content;
212
+ } else {
213
+ contentText = '[Unsupported message format]';
214
+ }
215
+
216
+ // Build timestamp
217
+ const createdAt = msg.created_at ? new Date(msg.created_at * 1000) : null; // OpenAI sends timestamps in seconds
218
+ const timestamp = createdAt ? createdAt.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : '';
219
+
220
+ return `
221
+ <div class="${classes}">
222
+ <div class="meta"><strong>${role.toUpperCase()}</strong> ${timestamp ? `<span class="timestamp">${timestamp}</span>` : ''}</div>
223
+ <div class="content">${contentText}</div>
224
+ </div>
225
+ `;
226
+ }
227
+
228
+ renderError(message) {
229
+ this.shadowRoot.innerHTML = `<div style="color:red;">${message}</div>`;
230
+ }
231
+
232
+ async sendMessage() {
233
+ const input = this.shadowRoot.getElementById('chat-input');
234
+ const message = input.value.trim();
235
+ if (!message) return;
236
+
237
+ input.disabled = true;
238
+ this.shadowRoot.getElementById('send-button').disabled = true;
239
+
240
+ try {
241
+ const response = await fetch('/API/plugin/chatgpt-assistants/addMessage', {
242
+ method: 'POST',
243
+ headers: { 'Content-Type': 'application/json' },
244
+ body: JSON.stringify({
245
+ conversation_id: this.conversation_id,
246
+ content: message,
247
+ assistant_id: this.selected_assistant_id
248
+ })
249
+ }).then(res => res.json());
250
+
251
+ if (response.success && response.runObj) {
252
+ this.currentRunId = response.runObj.id; // Store the run ID for polling
253
+ await this.loadConversation(); // reload messages
254
+ this.startPolling(); // 🌸 start watching for assistant reply!
255
+ } else {
256
+ alert('Failed to send message.');
257
+ }
258
+ } catch (err) {
259
+ console.error('Send message error:', err);
260
+ alert('Error sending message.');
261
+ }
262
+
263
+ input.value = '';
264
+ input.disabled = false;
265
+ this.shadowRoot.getElementById('send-button').disabled = false;
266
+ }
267
+
268
+ startPolling() {
269
+ if (this.pollingInterval) return; // Already polling
270
+
271
+ // Insert thinking message
272
+ this.showThinkingMessage();
273
+
274
+ this.pollingInterval = setInterval(async () => {
275
+ const runRes = await fetch(`/API/plugin/chatgpt-assistants/getRunStatus?thread_id=${this.conversation.thread_id}&run_id=${this.currentRunId}`);
276
+ const run = await runRes.json();
277
+
278
+ if (run.status === 'completed') {
279
+ const resThread = await fetch(`/API/plugin/chatgpt-assistants/getThreadMessages?thread_id=${this.conversation.thread_id}`);
280
+ //const activeAssistant = this.conversation.assistants.find(a => a.openai_id === run.assistant_id);
281
+ const threadMessages = await resThread.json();
282
+
283
+ if (threadMessages?.messages) {
284
+ this.messages = threadMessages.messages;
285
+ this.render();
286
+ }
287
+
288
+ clearInterval(this.pollingInterval);
289
+ this.pollingInterval = null;
290
+ this.hideThinkingMessage();
291
+ }
292
+ }, 2000);
293
+ }
294
+ showThinkingMessage() {
295
+ const messagesDiv = this.shadowRoot.querySelector('.messages');
296
+ if (!messagesDiv) return;
297
+
298
+ // Pull assistant thinking text
299
+ const assistant = this.getAssistantInfo(this.selected_assistant_id);
300
+ const thinkingText = assistant?.assistant_thinking_text || 'Assistant is thinking...';
301
+
302
+ const div = document.createElement('div');
303
+ div.className = 'thinking-message';
304
+ div.textContent = thinkingText;
305
+ div.id = 'thinking-message';
306
+ messagesDiv.appendChild(div);
307
+
308
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
309
+ }
310
+
311
+ hideThinkingMessage() {
312
+ const existing = this.shadowRoot.querySelector('#thinking-message');
313
+ if (existing) existing.remove();
314
+ }
315
+ closeChat() {
316
+ // Remove the element
317
+ this.remove();
318
+
319
+ // Clean up from open chat registry if possible
320
+ if (_joe && _joe.Ai && _joe.Ai._openChats) {
321
+ delete _joe.Ai._openChats[this.conversation_id];
322
+ }
323
+ }
324
+ }
325
+
326
+ customElements.define('joe-ai-chatbox', JoeAIChatbox);
327
+
328
+ // ========== HELPERS ==========
329
+
330
+ Ai.spawnChat = function(conversationId, options = {}) {
331
+ if (!conversationId) {
332
+ console.warn("Missing conversation ID for chat spawn.");
333
+ return;
334
+ }
335
+
336
+ // 1. Check if chatbox already open
337
+ if (Ai._openChats[conversationId]) {
338
+ console.log("Chatbox already open for", conversationId);
339
+ Ai._openChats[conversationId].scrollIntoView({ behavior: 'smooth', block: 'center' });
340
+ return;
341
+ }
342
+
343
+ // 2. Create new chatbox
344
+ const chat = document.createElement('joe-ai-chatbox');
345
+ chat.setAttribute('conversation_id', conversationId);
346
+
347
+ // Apply styles
348
+ chat.style.width = options.width || '400px';
349
+ chat.style.height = options.height || '420px';
350
+ chat.style.bottom = options.bottom || '20px';
351
+ chat.style.right = options.right || '20px';
352
+ chat.style.position = 'fixed';
353
+ chat.style.zIndex = '10000';
354
+ chat.style.background = '#efefef';
355
+ chat.style.border = '1px solid #fff';
356
+ chat.style.borderRadius = '8px';
357
+ chat.style.boxShadow = '0px 2px 10px rgba(0,0,0,0.1)';
358
+ chat.style.padding = '5px';
359
+
360
+ document.body.appendChild(chat);
361
+
362
+ // 3. Track it
363
+ Ai._openChats[conversationId] = chat;
364
+ return chat;
365
+ // 4. Optionally clean up when chatbox is removed (if you wire close buttons later)
366
+ };
367
+
368
+ // Attach AI to _joe
369
+ if (window._joe) {
370
+ _joe.Ai = Ai;
371
+ } else {
372
+ console.warn('joeAI.js loaded before _joe was ready.');
373
+ }
374
+
375
+ })();
376
+
package/js/joe-full.js CHANGED
@@ -11839,9 +11839,11 @@ function JsonObjectEditor(specs){
11839
11839
  var code = e.keyCode
11840
11840
  var nonBackElements = ['input','select','textarea'];
11841
11841
  var isInputElement = nonBackElements.indexOf(e.target.tagName.toLowerCase()) != -1;
11842
+ var isEditable = e.target.isContentEditable || $(e.target).closest('joe-ai-chatbox').length;
11843
+
11842
11844
  if (code == 8) {//BACKBUTTON PRESSED
11843
- if(isInputElement){
11844
- //return false;
11845
+ if(isInputElement || isEditable){
11846
+ return; // allow natural Backspace behavior
11845
11847
  }
11846
11848
  else{
11847
11849
  self.goBack();
@@ -17071,6 +17073,7 @@ Y | Object Field
17071
17073
 
17072
17074
  var display = self.propAsFuncOrValue(prop.display, itemObj);
17073
17075
  var method = self.propAsFuncOrValue(prop.method, itemObj);
17076
+ var onclick = self.propAsFuncOrValue(prop.onclick, itemObj);
17074
17077
  var color = self.propAsFuncOrValue(prop.color, itemObj);
17075
17078
  var schema = self.propAsFuncOrValue(prop.schema, itemObj);
17076
17079
  var svgOverride = self.propAsFuncOrValue(prop.svg, itemObj);
@@ -17099,6 +17102,7 @@ Y | Object Field
17099
17102
  + (method ? 'action="' + method + '" ' : '')
17100
17103
  + (color ? 'color="' + color + '" ' : '')
17101
17104
  + (title ? 'title="' + title + '" ' : '')
17105
+ +(onclick ? 'onclick="' + onclick + '" ' : '')
17102
17106
  + styleAttr + ' '
17103
17107
  + 'data-btnid="' + (prop.name || '') + '">';
17104
17108
 
@@ -21765,6 +21769,11 @@ logit(intent)
21765
21769
  <--------------------------------------------------------------------*/
21766
21770
  this.SVG = {
21767
21771
  icon:{
21772
+ assistant:`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
21773
+ <path d="M294.49 618.18v64.49c0 22.42.95 35.46 2.84 39.12 2.4 4.94 6.93 8.52 12.29 9.71 9.18 2.13 18.6 3.01 28.02 2.64h17.31c18.81 0 28.22 6.04 28.22 18.11 0 13.96-9.93 22.51-29.8 25.64-24.41 2.74-48.98 3.87-73.55 3.37-29.87 0-51.01-5.79-63.44-17.38-12.42-11.59-18.66-31.41-18.7-59.47v-81.08c.85-11.33-1.15-22.68-5.81-33.04-5.69-7.66-14.07-12.88-23.46-14.6-11.09-2.78-18.54-13.19-17.58-24.58-1.07-11.43 6.42-21.9 17.58-24.58 9.41-1.95 17.77-7.32 23.46-15.07 4.7-10.35 6.7-21.71 5.81-33.04v-80.88c0-27.62 6.21-47.22 18.63-58.81 12.42-11.59 33.57-17.36 63.44-17.31 26.13-.73 52.27.66 78.17 4.16 6.92 1.05 13.37 4.16 18.5 8.92 4.27 4.08 6.66 9.75 6.61 15.66 0 12.07-9.41 18.11-28.22 18.11h-17.84c-16.04 0-27.05 1.72-33.04 5.15-5.92 3.96-9.36 10.72-9.05 17.84v92.71c-.35 31.23-8.72 52.03-25.11 62.38-1.29.88-2.06 2.34-2.05 3.9 0 2.42 2.93 6.06 8.79 10.9 6.54 5.96 11.3 13.63 13.74 22.14 3.19 11.34 4.62 23.11 4.23 34.89Zm435.01-134.4v-64.23c0-22.42-.95-35.46-2.84-39.12-2.4-4.94-6.93-8.52-12.29-9.71-9.18-2.13-18.6-3.01-28.02-2.64h-17.18c-18.81 0-28.22-6.04-28.22-18.11 0-13.96 9.93-22.42 29.8-25.37 24.42-2.73 48.98-3.86 73.55-3.37 29.87 0 51.01 5.79 63.44 17.38 12.42 11.59 18.63 31.19 18.63 58.81v81.28c-.88 11.33 1.12 22.69 5.81 33.04 5.75 7.62 14.1 12.87 23.46 14.74 11.16 2.71 18.62 13.22 17.51 24.65.74 9.18-4.03 17.93-12.16 22.27-4.09 1.86-8.33 3.34-12.69 4.43-8.51 2.69-15.35 9.08-18.63 17.38-2.71 9.15-3.85 18.69-3.37 28.22v81.01c0 27.8-6.21 47.47-18.63 59.01-12.42 11.54-33.57 17.33-63.44 17.38-26.13.74-52.27-.65-78.17-4.16-6.92-1.05-13.37-4.16-18.5-8.92-4.31-4.17-6.7-9.93-6.61-15.93 0-12.07 9.41-18.11 28.22-18.11 2.25 0 8.19.18 17.84.53 11.25.56 22.5-1.19 33.04-5.15 4.1-2.12 7.02-5.98 7.93-10.51 1.33-11.91 1.82-23.9 1.45-35.88v-64.49c-.51-12.88 1.38-25.74 5.55-37.93 4.19-9.77 11.07-18.15 19.82-24.18 1.29-.88 2.06-2.34 2.05-3.9 0-2.42-2.93-6.06-8.79-10.9-6.55-5.96-11.31-13.63-13.74-22.14-3.43-11.46-5.05-23.39-4.82-35.35Z"/>
21774
+ <rect width="74.5" height="189" x="409.75" y="456.75" rx="33.94" ry="33.94"/>
21775
+ <rect width="74.5" height="189" x="539.75" y="456.75" rx="33.94" ry="33.94"/>
21776
+ </svg>`,
21768
21777
  'favorite':'<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M4 2h16a1 1 0 0 1 1 1v19.276a.5.5 0 0 1-.704.457L12 19.03l-8.296 3.702A.5.5 0 0 1 3 22.276V3a1 1 0 0 1 1-1zm8 11.5l2.939 1.545-.561-3.272 2.377-2.318-3.286-.478L12 6l-1.47 2.977-3.285.478 2.377 2.318-.56 3.272L12 13.5z"/></svg>',
21769
21778
  'left':'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -5 36 36"><path d="M11.3 21C7.1 18.2 3.3 13.8 3.2 13.6 3.1 13.5 3 13.2 3 13 3 12.8 3.1 12.5 3.2 12.3 3.3 12.1 7.2 7.7 11.3 4.9 11.6 4.7 12 4.7 12.4 4.8 12.8 5.1 13 5.4 13 5.8L13 9.5C13 9.5 21.7 10 22.1 10.3 22.7 10.7 23 12 23 13 23 14.1 22.6 15.2 22.1 15.6 21.7 15.9 13 16.5 13 16.5L13 20.2C13 20.6 12.8 20.9 12.4 21.1 12.1 21.3 11.7 21.2 11.3 21Z"/></svg>',
21770
21779
  'cancel':'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -5 36 36"><path d="M13 0.2C5.9 0.2 0.2 5.9 0.2 13 0.2 20.1 5.9 25.8 13 25.8 20.1 25.8 25.8 20.1 25.8 13 25.8 5.9 20.1 0.2 13 0.2ZM18.8 17.4L17.4 18.8C17.1 19 16.7 19 16.5 18.8L13 15.3 9.5 18.8C9.3 19 8.9 19 8.6 18.8L7.2 17.4C7 17.1 7 16.7 7.2 16.5L10.7 13 7.2 9.5C7 9.3 7 8.9 7.2 8.6L8.6 7.2C8.9 7 9.3 7 9.5 7.2L13 10.7 16.5 7.2C16.7 7 17.1 7 17.4 7.2L18.8 8.6C19 8.9 19 9.3 18.8 9.5L15.3 13 18.8 16.5C19 16.7 19 17.1 18.8 17.4Z"/></svg>',
package/js/joe.js CHANGED
@@ -540,9 +540,11 @@ function JsonObjectEditor(specs){
540
540
  var code = e.keyCode
541
541
  var nonBackElements = ['input','select','textarea'];
542
542
  var isInputElement = nonBackElements.indexOf(e.target.tagName.toLowerCase()) != -1;
543
+ var isEditable = e.target.isContentEditable || $(e.target).closest('joe-ai-chatbox').length;
544
+
543
545
  if (code == 8) {//BACKBUTTON PRESSED
544
- if(isInputElement){
545
- //return false;
546
+ if(isInputElement || isEditable){
547
+ return; // allow natural Backspace behavior
546
548
  }
547
549
  else{
548
550
  self.goBack();
@@ -5772,6 +5774,7 @@ Y | Object Field
5772
5774
 
5773
5775
  var display = self.propAsFuncOrValue(prop.display, itemObj);
5774
5776
  var method = self.propAsFuncOrValue(prop.method, itemObj);
5777
+ var onclick = self.propAsFuncOrValue(prop.onclick, itemObj);
5775
5778
  var color = self.propAsFuncOrValue(prop.color, itemObj);
5776
5779
  var schema = self.propAsFuncOrValue(prop.schema, itemObj);
5777
5780
  var svgOverride = self.propAsFuncOrValue(prop.svg, itemObj);
@@ -5800,6 +5803,7 @@ Y | Object Field
5800
5803
  + (method ? 'action="' + method + '" ' : '')
5801
5804
  + (color ? 'color="' + color + '" ' : '')
5802
5805
  + (title ? 'title="' + title + '" ' : '')
5806
+ +(onclick ? 'onclick="' + onclick + '" ' : '')
5803
5807
  + styleAttr + ' '
5804
5808
  + 'data-btnid="' + (prop.name || '') + '">';
5805
5809
 
@@ -10466,6 +10470,11 @@ logit(intent)
10466
10470
  <--------------------------------------------------------------------*/
10467
10471
  this.SVG = {
10468
10472
  icon:{
10473
+ assistant:`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
10474
+ <path d="M294.49 618.18v64.49c0 22.42.95 35.46 2.84 39.12 2.4 4.94 6.93 8.52 12.29 9.71 9.18 2.13 18.6 3.01 28.02 2.64h17.31c18.81 0 28.22 6.04 28.22 18.11 0 13.96-9.93 22.51-29.8 25.64-24.41 2.74-48.98 3.87-73.55 3.37-29.87 0-51.01-5.79-63.44-17.38-12.42-11.59-18.66-31.41-18.7-59.47v-81.08c.85-11.33-1.15-22.68-5.81-33.04-5.69-7.66-14.07-12.88-23.46-14.6-11.09-2.78-18.54-13.19-17.58-24.58-1.07-11.43 6.42-21.9 17.58-24.58 9.41-1.95 17.77-7.32 23.46-15.07 4.7-10.35 6.7-21.71 5.81-33.04v-80.88c0-27.62 6.21-47.22 18.63-58.81 12.42-11.59 33.57-17.36 63.44-17.31 26.13-.73 52.27.66 78.17 4.16 6.92 1.05 13.37 4.16 18.5 8.92 4.27 4.08 6.66 9.75 6.61 15.66 0 12.07-9.41 18.11-28.22 18.11h-17.84c-16.04 0-27.05 1.72-33.04 5.15-5.92 3.96-9.36 10.72-9.05 17.84v92.71c-.35 31.23-8.72 52.03-25.11 62.38-1.29.88-2.06 2.34-2.05 3.9 0 2.42 2.93 6.06 8.79 10.9 6.54 5.96 11.3 13.63 13.74 22.14 3.19 11.34 4.62 23.11 4.23 34.89Zm435.01-134.4v-64.23c0-22.42-.95-35.46-2.84-39.12-2.4-4.94-6.93-8.52-12.29-9.71-9.18-2.13-18.6-3.01-28.02-2.64h-17.18c-18.81 0-28.22-6.04-28.22-18.11 0-13.96 9.93-22.42 29.8-25.37 24.42-2.73 48.98-3.86 73.55-3.37 29.87 0 51.01 5.79 63.44 17.38 12.42 11.59 18.63 31.19 18.63 58.81v81.28c-.88 11.33 1.12 22.69 5.81 33.04 5.75 7.62 14.1 12.87 23.46 14.74 11.16 2.71 18.62 13.22 17.51 24.65.74 9.18-4.03 17.93-12.16 22.27-4.09 1.86-8.33 3.34-12.69 4.43-8.51 2.69-15.35 9.08-18.63 17.38-2.71 9.15-3.85 18.69-3.37 28.22v81.01c0 27.8-6.21 47.47-18.63 59.01-12.42 11.54-33.57 17.33-63.44 17.38-26.13.74-52.27-.65-78.17-4.16-6.92-1.05-13.37-4.16-18.5-8.92-4.31-4.17-6.7-9.93-6.61-15.93 0-12.07 9.41-18.11 28.22-18.11 2.25 0 8.19.18 17.84.53 11.25.56 22.5-1.19 33.04-5.15 4.1-2.12 7.02-5.98 7.93-10.51 1.33-11.91 1.82-23.9 1.45-35.88v-64.49c-.51-12.88 1.38-25.74 5.55-37.93 4.19-9.77 11.07-18.15 19.82-24.18 1.29-.88 2.06-2.34 2.05-3.9 0-2.42-2.93-6.06-8.79-10.9-6.55-5.96-11.31-13.63-13.74-22.14-3.43-11.46-5.05-23.39-4.82-35.35Z"/>
10475
+ <rect width="74.5" height="189" x="409.75" y="456.75" rx="33.94" ry="33.94"/>
10476
+ <rect width="74.5" height="189" x="539.75" y="456.75" rx="33.94" ry="33.94"/>
10477
+ </svg>`,
10469
10478
  'favorite':'<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M4 2h16a1 1 0 0 1 1 1v19.276a.5.5 0 0 1-.704.457L12 19.03l-8.296 3.702A.5.5 0 0 1 3 22.276V3a1 1 0 0 1 1-1zm8 11.5l2.939 1.545-.561-3.272 2.377-2.318-3.286-.478L12 6l-1.47 2.977-3.285.478 2.377 2.318-.56 3.272L12 13.5z"/></svg>',
10470
10479
  'left':'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -5 36 36"><path d="M11.3 21C7.1 18.2 3.3 13.8 3.2 13.6 3.1 13.5 3 13.2 3 13 3 12.8 3.1 12.5 3.2 12.3 3.3 12.1 7.2 7.7 11.3 4.9 11.6 4.7 12 4.7 12.4 4.8 12.8 5.1 13 5.4 13 5.8L13 9.5C13 9.5 21.7 10 22.1 10.3 22.7 10.7 23 12 23 13 23 14.1 22.6 15.2 22.1 15.6 21.7 15.9 13 16.5 13 16.5L13 20.2C13 20.6 12.8 20.9 12.4 21.1 12.1 21.3 11.7 21.2 11.3 21Z"/></svg>',
10471
10480
  'cancel':'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -5 36 36"><path d="M13 0.2C5.9 0.2 0.2 5.9 0.2 13 0.2 20.1 5.9 25.8 13 25.8 20.1 25.8 25.8 20.1 25.8 13 25.8 5.9 20.1 0.2 13 0.2ZM18.8 17.4L17.4 18.8C17.1 19 16.7 19 16.5 18.8L13 15.3 9.5 18.8C9.3 19 8.9 19 8.6 18.8L7.2 17.4C7 17.1 7 16.7 7.2 16.5L10.7 13 7.2 9.5C7 9.3 7 8.9 7.2 8.6L8.6 7.2C8.9 7 9.3 7 9.5 7.2L13 10.7 16.5 7.2C16.7 7 17.1 7 17.4 7.2L18.8 8.6C19 8.9 19 9.3 18.8 9.5L15.3 13 18.8 16.5C19 16.7 19 17.1 18.8 17.4Z"/></svg>',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-object-editor",
3
- "version": "0.10.400",
3
+ "version": "0.10.422",
4
4
  "description": "JOE the Json Object Editor | Platform Edition",
5
5
  "main": "app.js",
6
6
  "scripts": {
@@ -337,6 +337,8 @@
337
337
  $('#cappJACMenu').show();
338
338
  }
339
339
  </script>
340
+
341
+ <script src="${JOEPATH}js/joe-ai.js"></script>
340
342
  <!-- <script src="https://apis.google.com/js/client.js?onload=checkGoogleAuth"></script> -->
341
343
  </body>
342
344
  </html>
@@ -68,6 +68,28 @@ var fields = {
68
68
  'coords':{type:'geo'},
69
69
  'state':{type:'select',values:usStates,idprop:'code',template:'${code}',width:'25%',minwidth:'100px',blank:true},
70
70
  'name':{type:'text',onblur:'_joe.TITLE.set()'},
71
+ instructions_format:{type:'select',values:['wysiwyg','code'], rerender:'instructions',display:'Instructions Format'},
72
+ instructions:{
73
+ height:'auto',
74
+ display:'Instructions',
75
+ type:function(item){
76
+ if(!item.instructions_format){
77
+ return 'wysiwyg';
78
+ }
79
+ if(["code"].indexOf(item.instructions_format) != -1){
80
+ return 'code';
81
+ }
82
+ return item.instructions_format;
83
+ }
84
+ },
85
+ ai_model:{
86
+ type: "select",
87
+ display: "Ai Model",
88
+ values: [
89
+ { value: "gpt-4o", name: "GPT-4o (Current Default)" }
90
+ ],
91
+ default: "gpt-4o",
92
+ },
71
93
  template:{
72
94
  height:'600px',
73
95
  type:function(item){
@@ -0,0 +1,84 @@
1
+ var schema = {
2
+ title: "Ai Conversation | ${name}",
3
+ info: "Tracks AI conversations across users, assistants, and external members, storing only summaries for performance.",
4
+
5
+ fields: function() {
6
+ return [
7
+
8
+ "name",
9
+ "info",
10
+
11
+ { section_start: "participants", display: "Participants", collapsed: false },
12
+ { name: "user", type: "objectReference", values: "user", display: "JOE User" },
13
+ { name: "members", type: "objectReference", values: "members", display: "External Members"},
14
+ { name: "assistants", type: "group", values: function() { return _joe.getDataset('ai_assistant'); }, display: "Assistants", cols:2 },
15
+ { section_end: "participants" },
16
+
17
+ { section_start: "thread", display: "Thread", collapsed: true },
18
+ { name: "thread_id", type: "text", display: "OpenAI Thread ID", locked: true },
19
+ { section_end: "thread" },
20
+
21
+ { section_start: "summary", display: "Summary", collapsed: false },
22
+ { name: "summary", type: "wysiwyg", display: "Conversation Summary", height: "300px", comment: "Auto-generated after key points or closing." },
23
+ { section_end: "summary" },
24
+
25
+ { section_start: "linked", display: "Linked Objects", collapsed: true },
26
+ { name: "linked_objects", type: "objectlist", display: "Linked Objects", fields: [
27
+ { name: "schema", type: "text", display: "Schema" },
28
+ { name: "object_id", type: "text", display: "Object ID" }
29
+ ]
30
+ },
31
+ { section_end: "linked" },
32
+
33
+ { section_start: "system", collapsed: true },
34
+ "_id",
35
+ "created",
36
+ "updated",
37
+ "itemtype",
38
+ { section_end: "system" },
39
+
40
+ { sidebar_start: "right", collapsed: false },
41
+ 'status',
42
+ "tags",
43
+ {
44
+ name: "openChat",
45
+ type: "button",
46
+ display: "Open Chat",
47
+ icon:"ai_assistant",
48
+ onclick:function(object){
49
+ if (!object || !object._id) return '';
50
+ return `_joe.Ai.spawnChat('${object._id}');`;
51
+ },
52
+ },
53
+ { sidebar_end: "right" },
54
+
55
+ ];
56
+ },
57
+
58
+ idprop: "_id",
59
+ sorter: ["-created"],
60
+ };
61
+
62
+ module.exports = schema;
63
+
64
+
65
+ /**
66
+ * AI Conversation Schema
67
+ *
68
+ * Purpose:
69
+ * - Tracks live AI conversations linked to users, assistants, and external members.
70
+ * - Stores only metadata and final conversation summaries (not full chat messages).
71
+ * - Supports dynamic pulling of live thread content from OpenAI at runtime.
72
+ *
73
+ * Key Concepts:
74
+ * - user: Internal staff or admin account who initiated the conversation.
75
+ * - members: External participants (clients, business users), optional and multiple.
76
+ * - assistants: One or more AI assistants participating in the conversation.
77
+ * - thread_id: The OpenAI thread ID for live runtime message retrieval.
78
+ * - summary: Final AI-generated overview of the conversation (saved on close).
79
+ *
80
+ * Best Practices:
81
+ * - Messages are *not* stored permanently in Joe for performance and privacy.
82
+ * - Summaries provide lightweight snapshots for audits and reviews.
83
+ * - Chat UIs should always fetch full threads from OpenAI at runtime.
84
+ */
@@ -55,7 +55,7 @@ class JoeButton extends HTMLElement {
55
55
  actString = atts.action;
56
56
  break;
57
57
  }
58
- this.setAttribute('onclick',actString);
58
+ !this.hasClass('joe-field') && this.setAttribute('onclick',actString);
59
59
  }
60
60
 
61
61
  var guts = actionPreset?(newHTML|| this.initialHTML):(this.initialHTML || newHTML);