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 +2 -0
- package/_joeinclude copy.js +81 -0
- package/_joeinclude.js +70 -68
- package/css/joe-styles.css +6 -0
- package/css/joe.css +12 -0
- package/js/JsonObjectEditor.jquery.craydent.js +72 -3
- package/js/joe-ai.js +133 -4
- package/js/joe-full.js +72 -3
- package/js/joe.js +78 -3
- package/package.json +1 -1
- package/server/schemas/ai_conversation.js +13 -3
- package/server/schemas/setting.js +1 -1
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
|
|
2
|
+
last updated: Rewritten by CH + ChatGPT (April 2025)
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
82
|
+
loadScript(0); // start the script loading chain
|
|
83
|
+
})();
|
package/css/joe-styles.css
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
|
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,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",
|
|
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.
|
|
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'},
|