json-object-editor 0.10.422 → 0.10.424

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 CHANGED
@@ -5,6 +5,7 @@
5
5
  - button field added
6
6
  - getFromServer added to object options
7
7
  - best practices doc added
8
+ 423 - added contextual items to chat and flattened them.
8
9
 
9
10
  ### 0.10.300
10
11
  - initial aws fix for bucketname
@@ -13,6 +14,7 @@
13
14
  312 - sourcemap fixed, status subset bug fixed.
14
15
  320 - Upgraded tinyMCE
15
16
  321 - added code to status
17
+
16
18
 
17
19
  ### 0.10.200
18
20
  227 - no product user select
@@ -0,0 +1,81 @@
1
+ /* JavaScript include for: Json Object Editor
2
+ last updated: CH March 2014
3
+ */
4
+ var ieNavigator = false;
5
+ var agent = window.navigator.userAgent;
6
+ if(agent.indexOf('Trident') != -1 || agent.indexOf('MSIE') != -1|| agent.indexOf('Edge') != -1){
7
+ ieNavigator = true;
8
+ }
9
+
10
+ var includes = "",
11
+ projectName = 'JsonObjectEditor',
12
+ web_dir = web_dir ||('//' + location.hostname + ':' +
13
+ (location.port||((location.protocol=="https:")?443:80)) + "/" + projectName + '/');
14
+
15
+
16
+ if(location && location.origin == 'file://'){
17
+ web_dir = location.href.slice(0,location.href.lastIndexOf('/')+1);
18
+ }
19
+ var
20
+ joe_web_dir = web_dir,
21
+ scripts_dir = web_dir+"js/",
22
+ scripts = [];
23
+ if (typeof jQuery == 'undefined') {
24
+ scripts.push("libs/jquery-1.11.3.min.js");
25
+ scripts.push("libs/jquery-ui.min.js");
26
+
27
+ }else if(typeof jQuery().resizable){
28
+ scripts.push("libs/jquery-ui.min.js");
29
+ }
30
+ scripts.push("libs/jquery.ui.touch-punch.min.js");
31
+ if (typeof Craydent == 'undefined' || (!Craydent.VERSION || Craydent.VERSION < '1.7.37')) {
32
+ //scripts.push("libs/craydent-1.8.1.js");
33
+ scripts.push("libs/craydent-1.9.2.min.js");
34
+ }
35
+ if(ieNavigator){
36
+ scripts.push(
37
+
38
+ "libs/moment.min.js",
39
+ "joe_es5.js",
40
+ 'plugins/tinymce.min.js',
41
+ "ace/ace.js"
42
+
43
+ );
44
+ }else{
45
+ scripts.push(
46
+ /* "JsonObjectEditor.jquery.craydent.js",
47
+ "leaflet.js",
48
+ "esri-leaflet-geocoder.js",
49
+ "zebra_datepicker.js",
50
+ */
51
+ "libs/moment.min.js",
52
+ "joe.js",
53
+ 'plugins/tinymce.min.js',
54
+ "ace/ace.js"
55
+
56
+ );
57
+ }
58
+
59
+ var
60
+ styles_dir = web_dir+"css/",
61
+ styles =[
62
+ "joe.css"
63
+ ],
64
+ script,style,sc,st,
65
+ sc_len = scripts.length,st_len = styles.length;
66
+
67
+ //scripts
68
+ for(sc = 0; sc < sc_len; sc++){
69
+ script = scripts[sc];
70
+ includes+='<script type="text/javascript" src="'+scripts_dir+script+'"></script>';
71
+ }
72
+ //styles
73
+ for(st = 0; st < st_len; st++){
74
+ style = styles[st];
75
+ includes+='<link href="'+styles_dir+style+'" rel="stylesheet" type="text/css">';
76
+ }
77
+
78
+ includes+='';
79
+
80
+
81
+ document.write(includes);
package/_joeinclude.js CHANGED
@@ -1,81 +1,83 @@
1
1
  /* JavaScript include for: Json Object Editor
2
- last updated: CH March 2014
2
+ last updated: Rewritten by CH + ChatGPT (April 2025)
3
3
  */
4
- var ieNavigator = false;
5
- var agent = window.navigator.userAgent;
6
- if(agent.indexOf('Trident') != -1 || agent.indexOf('MSIE') != -1|| agent.indexOf('Edge') != -1){
4
+
5
+ (function() {
6
+ var ieNavigator = false;
7
+ var agent = window.navigator.userAgent;
8
+ if (agent.indexOf('Trident') !== -1 || agent.indexOf('MSIE') !== -1 || agent.indexOf('Edge') !== -1) {
7
9
  ieNavigator = true;
8
- }
9
-
10
- var includes = "",
11
- projectName = 'JsonObjectEditor',
12
- web_dir = web_dir ||('//' + location.hostname + ':' +
13
- (location.port||((location.protocol=="https:")?443:80)) + "/" + projectName + '/');
10
+ }
11
+
12
+ var projectName = 'JsonObjectEditor';
13
+ var web_dir = window.web_dir || ('//' + location.hostname + ':' + (location.port || ((location.protocol === "https:") ? 443 : 80)) + "/" + projectName + '/');
14
+
15
+ if (location && location.origin === 'file://') {
16
+ web_dir = location.href.slice(0, location.href.lastIndexOf('/') + 1);
17
+ }
18
+
19
+ var joe_web_dir = web_dir;
20
+ var scripts_dir = web_dir + "js/";
21
+ var styles_dir = web_dir + "css/";
14
22
 
23
+ var scripts = [];
24
+ var styles = ["joe.css"];
15
25
 
16
- if(location && location.origin == 'file://'){
17
- web_dir = location.href.slice(0,location.href.lastIndexOf('/')+1);
18
- }
19
- var
20
- joe_web_dir = web_dir,
21
- scripts_dir = web_dir+"js/",
22
- scripts = [];
23
- if (typeof jQuery == 'undefined') {
24
- scripts.push("libs/jquery-1.11.3.min.js");
25
- scripts.push("libs/jquery-ui.min.js");
26
+ if (typeof jQuery === 'undefined') {
27
+ scripts.push("libs/jquery-1.11.3.min.js");
28
+ scripts.push("libs/jquery-ui.min.js");
29
+ } else if (typeof jQuery().resizable === 'undefined') {
30
+ scripts.push("libs/jquery-ui.min.js");
31
+ }
26
32
 
27
- }else if(typeof jQuery().resizable){
28
- scripts.push("libs/jquery-ui.min.js");
29
- }
30
- scripts.push("libs/jquery.ui.touch-punch.min.js");
31
- if (typeof Craydent == 'undefined' || (!Craydent.VERSION || Craydent.VERSION < '1.7.37')) {
32
- //scripts.push("libs/craydent-1.8.1.js");
33
- scripts.push("libs/craydent-1.9.2.min.js");
34
- }
35
- if(ieNavigator){
36
- scripts.push(
33
+ scripts.push("libs/jquery.ui.touch-punch.min.js");
37
34
 
38
- "libs/moment.min.js",
39
- "joe_es5.js",
40
- 'plugins/tinymce.min.js',
41
- "ace/ace.js"
42
-
43
- );
44
- }else{
45
- scripts.push(
46
- /* "JsonObjectEditor.jquery.craydent.js",
47
- "leaflet.js",
48
- "esri-leaflet-geocoder.js",
49
- "zebra_datepicker.js",
50
- */
51
- "libs/moment.min.js",
52
- "joe.js",
53
- 'plugins/tinymce.min.js',
54
- "ace/ace.js"
35
+ if (typeof Craydent === 'undefined' || (!Craydent.VERSION || Craydent.VERSION < '1.7.37')) {
36
+ scripts.push("libs/craydent-1.9.2.min.js");
37
+ }
55
38
 
56
- );
57
- }
39
+ if (ieNavigator) {
40
+ scripts.push(
41
+ "libs/moment.min.js",
42
+ "joe_es5.js",
43
+ "plugins/tinymce.min.js",
44
+ "ace/ace.js"
45
+ );
46
+ } else {
47
+ scripts.push(
48
+ "libs/moment.min.js",
49
+ "joe.js",
50
+ "plugins/tinymce.min.js",
51
+ "ace/ace.js"
52
+ );
53
+ }
58
54
 
59
- var
60
- styles_dir = web_dir+"css/",
61
- styles =[
62
- "joe.css"
63
- ],
64
- script,style,sc,st,
65
- sc_len = scripts.length,st_len = styles.length;
55
+ // Load styles
56
+ styles.forEach(function(style) {
57
+ var link = document.createElement('link');
58
+ link.href = styles_dir + style;
59
+ link.rel = 'stylesheet';
60
+ link.type = 'text/css';
61
+ document.head.appendChild(link);
62
+ });
66
63
 
67
- //scripts
68
- for(sc = 0; sc < sc_len; sc++){
69
- script = scripts[sc];
70
- includes+='<script type="text/javascript" src="'+scripts_dir+script+'"></script>';
71
- }
72
- //styles
73
- for(st = 0; st < st_len; st++){
74
- style = styles[st];
75
- includes+='<link href="'+styles_dir+style+'" rel="stylesheet" type="text/css">';
76
- }
64
+ // Load scripts in order
65
+ function loadScript(index) {
66
+ if (index >= scripts.length) return;
77
67
 
78
- includes+='';
68
+ var script = document.createElement('script');
69
+ script.src = scripts_dir + scripts[index];
70
+ script.type = 'text/javascript';
71
+ script.onload = function() {
72
+ loadScript(index + 1);
73
+ };
74
+ script.onerror = function() {
75
+ console.error("Failed to load script:", script.src);
76
+ loadScript(index + 1); // Continue even if one fails
77
+ };
79
78
 
79
+ document.head.appendChild(script);
80
+ }
80
81
 
81
- document.write(includes);
82
+ loadScript(0); // start the script loading chain
83
+ })();
@@ -1924,6 +1924,12 @@ joe-content-section {
1924
1924
  joe-card{
1925
1925
 
1926
1926
  }
1927
+
1928
+ .joe-content-sidebar .joe-field-item-content {
1929
+ padding:5px;
1930
+ font-size:18px;
1931
+ line-height: 1.1em;
1932
+ }
1927
1933
  /*-------------------------
1928
1934
  List Option Buttons
1929
1935
  -------------------------*/
package/css/joe.css CHANGED
@@ -1,3 +1,9 @@
1
+ /* --------------------------------------------------------
2
+ *
3
+ * JOE - v1.5.0
4
+ * Created by: Corey Hadden
5
+ *
6
+ * -------------------------------------------------------- */
1
7
  /* --------------------------------------------------------
2
8
  *
3
9
  * JOE - v1.5.0
@@ -2410,6 +2416,12 @@ joe-content-section {
2410
2416
  joe-card{
2411
2417
 
2412
2418
  }
2419
+
2420
+ .joe-content-sidebar .joe-field-item-content {
2421
+ padding:5px;
2422
+ font-size:18px;
2423
+ line-height: 1.1em;
2424
+ }
2413
2425
  /*-------------------------
2414
2426
  List Option Buttons
2415
2427
  -------------------------*/
@@ -3173,6 +3173,8 @@ this.renderHTMLContent = function(specs){
3173
3173
  //Locked
3174
3174
  prop.locked = self.propAsFuncOrValue(prop.locked);
3175
3175
 
3176
+ //cssClass
3177
+ var cssClass = prop.cssClass?self.propAsFuncOrValue(prop.cssClass):'';
3176
3178
 
3177
3179
  //required
3178
3180
  var required = '';
@@ -3216,7 +3218,7 @@ this.renderHTMLContent = function(specs){
3216
3218
  icon = self.schemas[icon].menuicon;
3217
3219
  }
3218
3220
  var hiddenlabel = (prop.label === false)?' hide-label ':''; html+=
3219
- '<joe-field class="joe-object-field '+hidden+' '+required+' '+proptype+'-field '+hiddenlabel+'" data-type="'+proptype+'" data-name="'+prop.name+'">'
3221
+ '<joe-field class="joe-object-field '+hidden+' '+required+' '+proptype+'-field '+hiddenlabel+' '+cssClass+'" data-type="'+proptype+'" data-name="'+prop.name+'">'
3220
3222
  +renderFieldAttribute('before')
3221
3223
 
3222
3224
  +'<label class="joe-field-label '+(icon && 'iconed' ||'')+' " title="'+prop.name+'">'
@@ -8259,6 +8261,61 @@ Field Rendering Helpers
8259
8261
  if (callback) callback(false);
8260
8262
  });
8261
8263
  };
8264
+ this.Object.flatten = function(id, options = {}) {
8265
+ const { recursive = true, depth = 1, visited = new Set() } = options;
8266
+
8267
+ let object = null;
8268
+
8269
+ if (id) {
8270
+ object = $J.get(id);
8271
+ } else if (_joe?.current?.object) {
8272
+ object = _joe.current.object;
8273
+ }
8274
+
8275
+ if (!object) return null;
8276
+
8277
+ const flattened = {};
8278
+ const currentId = object._id || object.id;
8279
+ if (currentId) visited.add(currentId);
8280
+
8281
+ for (const [field, val] of Object.entries(object)) {
8282
+ if (val === undefined || val === null) continue;
8283
+ if (field == '_id') continue;
8284
+
8285
+ if (typeof val === 'string' && $c.isCuid(val) && recursive && depth > 0) {
8286
+ if (!visited.has(val)) {
8287
+ const expanded = _joe.Object.flatten(val, {
8288
+ recursive,
8289
+ depth: depth - 1,
8290
+ visited: new Set(visited)
8291
+ });
8292
+ flattened[field] = expanded || val;
8293
+ } else {
8294
+ flattened[field] = val;
8295
+ }
8296
+ }
8297
+ else if (Array.isArray(val) && val.every(v => typeof v === 'string' && $c.isCuid(v)) && recursive && depth > 0) {
8298
+ flattened[field] = val.map(refId => {
8299
+ if (!visited.has(refId)) {
8300
+ const expanded = _joe.Object.flatten(refId, {
8301
+ recursive,
8302
+ depth: depth - 1,
8303
+ visited: new Set(visited)
8304
+ });
8305
+ return expanded || refId;
8306
+ }
8307
+ return refId;
8308
+ });
8309
+ }
8310
+ else {
8311
+ flattened[field] = val;
8312
+ }
8313
+ }
8314
+
8315
+ return flattened;
8316
+ };
8317
+
8318
+
8262
8319
  /*-------------------------------------------------------------------->
8263
8320
  EXPORT DATA
8264
8321
  <--------------------------------------------------------------------*/
@@ -10835,7 +10892,7 @@ this.Snippets.priorityStripeColor = function(item){
10835
10892
  }
10836
10893
  return this;
10837
10894
  }
10838
- function unstringifyFunctions(propObject){
10895
+ function _classic_unstringifyFunctions(propObject){
10839
10896
  for(var p in propObject){
10840
10897
  if(typeof propObject[p] == 'string' && propObject[p].indexOf('(function') ==0 ){
10841
10898
  propObject[p] = tryEval('('+propObject[p].replace(/;r\n/g,';\n').replace(/<br \/>/g,'')+')');
@@ -10847,7 +10904,19 @@ function unstringifyFunctions(propObject){
10847
10904
  }
10848
10905
  return propObject;
10849
10906
  }
10850
-
10907
+ function unstringifyFunctions(propObject) {
10908
+ for (var p in propObject) {
10909
+ if (typeof propObject[p] == 'string') {
10910
+ var trimmed = propObject[p].trim();
10911
+ if (trimmed.startsWith('(function') || trimmed.startsWith('(async function')) {
10912
+ propObject[p] = tryEval(trimmed.replace(/;r\n/g, ';\n').replace(/<br \/>/g, ''));
10913
+ }
10914
+ } else if (typeof propObject[p] == "object" && propObject[p] !== null) {
10915
+ unstringifyFunctions(propObject[p]);
10916
+ }
10917
+ }
10918
+ return propObject;
10919
+ }
10851
10920
  var __gotoJoeSection;
10852
10921
  var __clearDiv__ = '<div class="clear"></div>';
10853
10922
 
package/js/joe-ai.js CHANGED
@@ -1,20 +1,25 @@
1
1
  (function(){
2
2
  // Define the joeAI namespace
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
7
  // ========== COMPONENTS ==========
7
-
8
+
8
9
  class JoeAIChatbox extends HTMLElement {
9
10
  constructor() {
10
11
  super();
12
+
13
+
11
14
  this.attachShadow({ mode: 'open' });
12
15
  this.messages = [];
16
+
13
17
  }
14
18
 
15
19
  connectedCallback() {
16
20
  this.conversation_id = this.getAttribute('conversation_id');
17
- this.selected_assistant_id = null;
21
+
22
+ this.selected_assistant_id = Ai.default_ai?Ai.default_ai.value:null;
18
23
  this.ui = {};
19
24
  if (!this.conversation_id) {
20
25
  this.renderError("Missing conversation_id");
@@ -244,7 +249,7 @@
244
249
  body: JSON.stringify({
245
250
  conversation_id: this.conversation_id,
246
251
  content: message,
247
- assistant_id: this.selected_assistant_id
252
+ assistant_id: this.selected_assistant_id||Ai.default_ai?Ai.default_ai.value:null
248
253
  })
249
254
  }).then(res => res.json());
250
255
 
@@ -326,6 +331,71 @@
326
331
  customElements.define('joe-ai-chatbox', JoeAIChatbox);
327
332
 
328
333
  // ========== HELPERS ==========
334
+ Ai.spawnContextualChat = async function(conversationId, options = {}) {
335
+ if (!conversationId) {
336
+ console.warn("Missing conversation ID for chat spawn.");
337
+ return;
338
+ }
339
+ Ai.default_ai = _joe.Data.setting.where({name:'DEFAULT_AI_ASSISTANT'})[0]||false;
340
+
341
+ // 1. Check if chat already open
342
+ if (Ai._openChats[conversationId]) {
343
+ console.log("Chatbox already open for", conversationId);
344
+ Ai._openChats[conversationId].scrollIntoView({ behavior: 'smooth', block: 'center' });
345
+ return;
346
+ }
347
+
348
+ try {
349
+ // 2. Prepare context
350
+ const flattened = _joe.Object.flatten();
351
+ const contextInstructions = _joe.Ai.generateContextInstructions(flattened);
352
+
353
+ // 3. Inject context into backend
354
+ const contextResult = await fetch('/API/plugin/chatgpt-assistants/addMessage', {
355
+ method: 'POST',
356
+ headers: { 'Content-Type': 'application/json' },
357
+ body: JSON.stringify({
358
+ conversation_id: conversationId,
359
+ //role: 'system',
360
+ content: contextInstructions,
361
+ assistant_id: Ai.default_ai.value
362
+ })
363
+ }).then(res => res.json());
364
+
365
+ if (!contextResult || contextResult.error) {
366
+ console.error('❌ Failed to inject context:', contextResult?.error);
367
+ return;
368
+ }
369
+
370
+ // 4. Create new chatbox
371
+ const chat = document.createElement('joe-ai-chatbox');
372
+ chat.setAttribute('conversation_id', conversationId);
373
+
374
+ // 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';
379
+ chat.style.position = 'fixed';
380
+ chat.style.zIndex = '10000';
381
+ chat.style.background = '#efefef';
382
+ chat.style.border = '1px solid #fff';
383
+ chat.style.borderRadius = '8px';
384
+ chat.style.boxShadow = '0px 2px 10px rgba(0,0,0,0.1)';
385
+ chat.style.padding = '5px';
386
+
387
+ document.body.appendChild(chat);
388
+
389
+ // 5. Track it
390
+ Ai._openChats[conversationId] = chat;
391
+
392
+ // 6. Show soft local UI message
393
+ _joe.Ai.injectSystemMessage(conversationId, `Context injected: ${flattened.name || flattened.title || 'Object'} (${flattened._id})`);
394
+
395
+ } catch (err) {
396
+ console.error('❌ spawnChat context injection failed:', err);
397
+ }
398
+ };
329
399
 
330
400
  Ai.spawnChat = function(conversationId, options = {}) {
331
401
  if (!conversationId) {
@@ -344,6 +414,14 @@
344
414
  const chat = document.createElement('joe-ai-chatbox');
345
415
  chat.setAttribute('conversation_id', conversationId);
346
416
 
417
+ const flattened = _joe.Object.flatten();
418
+ const contextInstructions = _joe.Ai.generateContextInstructions(flattened);
419
+
420
+ // Actually inject into AI backend (for assistant awareness) if you have that later
421
+ // For now: silently show a soft system bubble
422
+ _joe.Ai.injectSystemMessage(conversationId, `Context injected: ${flattened.name || flattened.title || 'Object'} (${flattened._id})`);
423
+
424
+
347
425
  // Apply styles
348
426
  chat.style.width = options.width || '400px';
349
427
  chat.style.height = options.height || '420px';
@@ -365,6 +443,57 @@
365
443
  // 4. Optionally clean up when chatbox is removed (if you wire close buttons later)
366
444
  };
367
445
 
446
+ Ai.generateContextInstructions = function(flattenedObj) {
447
+ if (!flattenedObj) return '';
448
+
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
+ }
461
+
462
+ context += `\nAlways refer to this context when answering questions or completing tasks related to this object.\n`;
463
+
464
+ return context;
465
+ };
466
+ Ai.injectSystemMessage = async function(conversationId, text) {
467
+ if (!conversationId || !text) return;
468
+
469
+ try {
470
+ // Create a system-style message object
471
+ const messageObj = {
472
+ conversation_id: conversationId,
473
+ role: 'system',
474
+ content: text,
475
+ created: new Date().toISOString()
476
+ };
477
+
478
+ // You could either push this directly into chatbox if loaded, or update server messages if you have backend ready
479
+ const chatbox = document.querySelector(`joe-ai-chatbox[conversation_id="${conversationId}"]`);
480
+ if (chatbox) {
481
+ if (!chatbox.messages) {
482
+ chatbox.messages = [];
483
+ }
484
+ chatbox.messages.push(messageObj);
485
+
486
+ // Optionally trigger a soft re-render of chatbox if needed
487
+ if (typeof chatbox.renderMessages === 'function') {
488
+ chatbox.renderMessages();
489
+ }
490
+ }
491
+ } catch (err) {
492
+ console.error("❌ injectSystemMessage failed:", err);
493
+ }
494
+ };
495
+
496
+
368
497
  // Attach AI to _joe
369
498
  if (window._joe) {
370
499
  _joe.Ai = Ai;
package/js/joe-full.js CHANGED
@@ -14478,6 +14478,8 @@ this.renderHTMLContent = function(specs){
14478
14478
  //Locked
14479
14479
  prop.locked = self.propAsFuncOrValue(prop.locked);
14480
14480
 
14481
+ //cssClass
14482
+ var cssClass = prop.cssClass?self.propAsFuncOrValue(prop.cssClass):'';
14481
14483
 
14482
14484
  //required
14483
14485
  var required = '';
@@ -14521,7 +14523,7 @@ this.renderHTMLContent = function(specs){
14521
14523
  icon = self.schemas[icon].menuicon;
14522
14524
  }
14523
14525
  var hiddenlabel = (prop.label === false)?' hide-label ':''; html+=
14524
- '<joe-field class="joe-object-field '+hidden+' '+required+' '+proptype+'-field '+hiddenlabel+'" data-type="'+proptype+'" data-name="'+prop.name+'">'
14526
+ '<joe-field class="joe-object-field '+hidden+' '+required+' '+proptype+'-field '+hiddenlabel+' '+cssClass+'" data-type="'+proptype+'" data-name="'+prop.name+'">'
14525
14527
  +renderFieldAttribute('before')
14526
14528
 
14527
14529
  +'<label class="joe-field-label '+(icon && 'iconed' ||'')+' " title="'+prop.name+'">'
@@ -19564,6 +19566,61 @@ Field Rendering Helpers
19564
19566
  if (callback) callback(false);
19565
19567
  });
19566
19568
  };
19569
+ this.Object.flatten = function(id, options = {}) {
19570
+ const { recursive = true, depth = 1, visited = new Set() } = options;
19571
+
19572
+ let object = null;
19573
+
19574
+ if (id) {
19575
+ object = $J.get(id);
19576
+ } else if (_joe?.current?.object) {
19577
+ object = _joe.current.object;
19578
+ }
19579
+
19580
+ if (!object) return null;
19581
+
19582
+ const flattened = {};
19583
+ const currentId = object._id || object.id;
19584
+ if (currentId) visited.add(currentId);
19585
+
19586
+ for (const [field, val] of Object.entries(object)) {
19587
+ if (val === undefined || val === null) continue;
19588
+ if (field == '_id') continue;
19589
+
19590
+ if (typeof val === 'string' && $c.isCuid(val) && recursive && depth > 0) {
19591
+ if (!visited.has(val)) {
19592
+ const expanded = _joe.Object.flatten(val, {
19593
+ recursive,
19594
+ depth: depth - 1,
19595
+ visited: new Set(visited)
19596
+ });
19597
+ flattened[field] = expanded || val;
19598
+ } else {
19599
+ flattened[field] = val;
19600
+ }
19601
+ }
19602
+ else if (Array.isArray(val) && val.every(v => typeof v === 'string' && $c.isCuid(v)) && recursive && depth > 0) {
19603
+ flattened[field] = val.map(refId => {
19604
+ if (!visited.has(refId)) {
19605
+ const expanded = _joe.Object.flatten(refId, {
19606
+ recursive,
19607
+ depth: depth - 1,
19608
+ visited: new Set(visited)
19609
+ });
19610
+ return expanded || refId;
19611
+ }
19612
+ return refId;
19613
+ });
19614
+ }
19615
+ else {
19616
+ flattened[field] = val;
19617
+ }
19618
+ }
19619
+
19620
+ return flattened;
19621
+ };
19622
+
19623
+
19567
19624
  /*-------------------------------------------------------------------->
19568
19625
  EXPORT DATA
19569
19626
  <--------------------------------------------------------------------*/
@@ -22140,7 +22197,7 @@ this.Snippets.priorityStripeColor = function(item){
22140
22197
  }
22141
22198
  return this;
22142
22199
  }
22143
- function unstringifyFunctions(propObject){
22200
+ function _classic_unstringifyFunctions(propObject){
22144
22201
  for(var p in propObject){
22145
22202
  if(typeof propObject[p] == 'string' && propObject[p].indexOf('(function') ==0 ){
22146
22203
  propObject[p] = tryEval('('+propObject[p].replace(/;r\n/g,';\n').replace(/<br \/>/g,'')+')');
@@ -22152,7 +22209,19 @@ function unstringifyFunctions(propObject){
22152
22209
  }
22153
22210
  return propObject;
22154
22211
  }
22155
-
22212
+ function unstringifyFunctions(propObject) {
22213
+ for (var p in propObject) {
22214
+ if (typeof propObject[p] == 'string') {
22215
+ var trimmed = propObject[p].trim();
22216
+ if (trimmed.startsWith('(function') || trimmed.startsWith('(async function')) {
22217
+ propObject[p] = tryEval(trimmed.replace(/;r\n/g, ';\n').replace(/<br \/>/g, ''));
22218
+ }
22219
+ } else if (typeof propObject[p] == "object" && propObject[p] !== null) {
22220
+ unstringifyFunctions(propObject[p]);
22221
+ }
22222
+ }
22223
+ return propObject;
22224
+ }
22156
22225
  var __gotoJoeSection;
22157
22226
  var __clearDiv__ = '<div class="clear"></div>';
22158
22227
 
package/js/joe.js CHANGED
@@ -1,3 +1,9 @@
1
+ /* --------------------------------------------------------
2
+ *
3
+ * JOE - v1.5.0
4
+ * Created by: Corey Hadden
5
+ *
6
+ * -------------------------------------------------------- */
1
7
  /* --------------------------------------------------------
2
8
  *
3
9
  * JOE - v1.5.0
@@ -3179,6 +3185,8 @@ this.renderHTMLContent = function(specs){
3179
3185
  //Locked
3180
3186
  prop.locked = self.propAsFuncOrValue(prop.locked);
3181
3187
 
3188
+ //cssClass
3189
+ var cssClass = prop.cssClass?self.propAsFuncOrValue(prop.cssClass):'';
3182
3190
 
3183
3191
  //required
3184
3192
  var required = '';
@@ -3222,7 +3230,7 @@ this.renderHTMLContent = function(specs){
3222
3230
  icon = self.schemas[icon].menuicon;
3223
3231
  }
3224
3232
  var hiddenlabel = (prop.label === false)?' hide-label ':''; html+=
3225
- '<joe-field class="joe-object-field '+hidden+' '+required+' '+proptype+'-field '+hiddenlabel+'" data-type="'+proptype+'" data-name="'+prop.name+'">'
3233
+ '<joe-field class="joe-object-field '+hidden+' '+required+' '+proptype+'-field '+hiddenlabel+' '+cssClass+'" data-type="'+proptype+'" data-name="'+prop.name+'">'
3226
3234
  +renderFieldAttribute('before')
3227
3235
 
3228
3236
  +'<label class="joe-field-label '+(icon && 'iconed' ||'')+' " title="'+prop.name+'">'
@@ -8265,6 +8273,61 @@ Field Rendering Helpers
8265
8273
  if (callback) callback(false);
8266
8274
  });
8267
8275
  };
8276
+ this.Object.flatten = function(id, options = {}) {
8277
+ const { recursive = true, depth = 1, visited = new Set() } = options;
8278
+
8279
+ let object = null;
8280
+
8281
+ if (id) {
8282
+ object = $J.get(id);
8283
+ } else if (_joe?.current?.object) {
8284
+ object = _joe.current.object;
8285
+ }
8286
+
8287
+ if (!object) return null;
8288
+
8289
+ const flattened = {};
8290
+ const currentId = object._id || object.id;
8291
+ if (currentId) visited.add(currentId);
8292
+
8293
+ for (const [field, val] of Object.entries(object)) {
8294
+ if (val === undefined || val === null) continue;
8295
+ if (field == '_id') continue;
8296
+
8297
+ if (typeof val === 'string' && $c.isCuid(val) && recursive && depth > 0) {
8298
+ if (!visited.has(val)) {
8299
+ const expanded = _joe.Object.flatten(val, {
8300
+ recursive,
8301
+ depth: depth - 1,
8302
+ visited: new Set(visited)
8303
+ });
8304
+ flattened[field] = expanded || val;
8305
+ } else {
8306
+ flattened[field] = val;
8307
+ }
8308
+ }
8309
+ else if (Array.isArray(val) && val.every(v => typeof v === 'string' && $c.isCuid(v)) && recursive && depth > 0) {
8310
+ flattened[field] = val.map(refId => {
8311
+ if (!visited.has(refId)) {
8312
+ const expanded = _joe.Object.flatten(refId, {
8313
+ recursive,
8314
+ depth: depth - 1,
8315
+ visited: new Set(visited)
8316
+ });
8317
+ return expanded || refId;
8318
+ }
8319
+ return refId;
8320
+ });
8321
+ }
8322
+ else {
8323
+ flattened[field] = val;
8324
+ }
8325
+ }
8326
+
8327
+ return flattened;
8328
+ };
8329
+
8330
+
8268
8331
  /*-------------------------------------------------------------------->
8269
8332
  EXPORT DATA
8270
8333
  <--------------------------------------------------------------------*/
@@ -10841,7 +10904,7 @@ this.Snippets.priorityStripeColor = function(item){
10841
10904
  }
10842
10905
  return this;
10843
10906
  }
10844
- function unstringifyFunctions(propObject){
10907
+ function _classic_unstringifyFunctions(propObject){
10845
10908
  for(var p in propObject){
10846
10909
  if(typeof propObject[p] == 'string' && propObject[p].indexOf('(function') ==0 ){
10847
10910
  propObject[p] = tryEval('('+propObject[p].replace(/;r\n/g,';\n').replace(/<br \/>/g,'')+')');
@@ -10853,7 +10916,19 @@ function unstringifyFunctions(propObject){
10853
10916
  }
10854
10917
  return propObject;
10855
10918
  }
10856
-
10919
+ function unstringifyFunctions(propObject) {
10920
+ for (var p in propObject) {
10921
+ if (typeof propObject[p] == 'string') {
10922
+ var trimmed = propObject[p].trim();
10923
+ if (trimmed.startsWith('(function') || trimmed.startsWith('(async function')) {
10924
+ propObject[p] = tryEval(trimmed.replace(/;r\n/g, ';\n').replace(/<br \/>/g, ''));
10925
+ }
10926
+ } else if (typeof propObject[p] == "object" && propObject[p] !== null) {
10927
+ unstringifyFunctions(propObject[p]);
10928
+ }
10929
+ }
10930
+ return propObject;
10931
+ }
10857
10932
  var __gotoJoeSection;
10858
10933
  var __clearDiv__ = '<div class="clear"></div>';
10859
10934
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-object-editor",
3
- "version": "0.10.422",
3
+ "version": "0.10.424",
4
4
  "description": "JOE the Json Object Editor | Platform Edition",
5
5
  "main": "app.js",
6
6
  "scripts": {
@@ -1,7 +1,11 @@
1
1
  var schema = {
2
2
  title: "Ai Conversation | ${name}",
3
3
  info: "Tracks AI conversations across users, assistants, and external members, storing only summaries for performance.",
4
-
4
+ methods:{
5
+ chatSpawner:async function(object_id){
6
+ await _joe.Ai.spawnContextualChat(object_id);
7
+ }
8
+ },
5
9
  fields: function() {
6
10
  return [
7
11
 
@@ -11,7 +15,9 @@ var schema = {
11
15
  { section_start: "participants", display: "Participants", collapsed: false },
12
16
  { name: "user", type: "objectReference", values: "user", display: "JOE User" },
13
17
  { 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 },
18
+ { name: "assistants", type: "group", cssClass:"ai-options", template:function(ass,curObj){
19
+ return `${ass.name} || ${curObj.name}`;
20
+ }, values: function() { return _joe.getDataset('ai_assistant'); }, display: "Assistants", cols:2 },
15
21
  { section_end: "participants" },
16
22
 
17
23
  { section_start: "thread", display: "Thread", collapsed: true },
@@ -45,9 +51,13 @@ var schema = {
45
51
  type: "button",
46
52
  display: "Open Chat",
47
53
  icon:"ai_assistant",
54
+ onclick2:function(object){
55
+ if (!object || !object._id) return '';
56
+ return `_joe.Ai.spawnChat('${object._id}');`;
57
+ },
48
58
  onclick:function(object){
49
59
  if (!object || !object._id) return '';
50
- return `_joe.Ai.spawnChat('${object._id}');`;
60
+ return `_joe.schemas.ai_conversation.methods.chatSpawner('${object._id}');`;
51
61
  },
52
62
  },
53
63
  { sidebar_end: "right" },
@@ -6,7 +6,7 @@ var setting ={
6
6
  fields:[
7
7
  'name',
8
8
  'info',
9
- {name:'setting_type',type:'select',values:['text','code'],rerender:'value'},
9
+ {name:'setting_type',type:'select',values:['text','code','wysiwyg'],rerender:'value'},
10
10
  {name:'value',type:function(item){
11
11
  return (item.setting_type || '');
12
12
  },language:'javascript'},