json-object-editor 0.10.657 → 0.10.660

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/js/joe-ai.js CHANGED
@@ -773,7 +773,9 @@
773
773
  const payload = {
774
774
  model: this.model || undefined,
775
775
  ai_assistant_id: this.getAttribute('ai_assistant_id') || undefined,
776
- source: this.getAttribute('source') || 'widget'
776
+ source: this.getAttribute('source') || 'widget',
777
+ scope_itemtype: this.getAttribute('scope_itemtype') || undefined,
778
+ scope_id: this.getAttribute('scope_id') || undefined
777
779
  };
778
780
  // Resolve the effective user for this widget and pass id/name/color
779
781
  // explicitly to the server. This works for:
@@ -1362,29 +1364,11 @@
1362
1364
 
1363
1365
 
1364
1366
  //**YES**
1367
+ // Legacy assistants-based object chat helper (kept for reference); the new
1368
+ // object chat path uses the widget + ai_widget_conversation stack instead.
1365
1369
  Ai.spawnChatHelper = async function(object_id,user_id=_joe.User._id,conversation_id) {
1366
- //if not conversation_id, create a new one
1367
- let convo_id = conversation_id;
1368
- var newChat = false;
1369
- if(!convo_id){
1370
- const response = await fetch('/API/plugin/chatgpt-assistants/createConversation', {
1371
- method: 'POST',
1372
- headers: { 'Content-Type': 'application/json' },
1373
- body: JSON.stringify({
1374
- object_id,
1375
- user_id
1376
- })
1377
- }).then(res => res.json());
1378
- convo_id = response?.conversation?._id;
1379
- newChat = true;
1380
- if(response.error){
1381
- console.error('❌ Failed to create conversation:', response.error);
1382
- alert('Failed to create conversation: '+response.message);
1383
- return;
1384
- }
1385
- }
1386
-
1387
- await _joe.Ai.spawnContextualChat(convo_id,{object_id,newChat});
1370
+ console.warn('spawnChatHelper (assistants chat) is legacy; use object chat widget instead.');
1371
+ return;
1388
1372
  }
1389
1373
  Ai.getDefaultAssistant = function() {
1390
1374
 
@@ -1573,7 +1557,208 @@
1573
1557
  } catch (err) {
1574
1558
  console.error("❌ injectSystemMessage failed:", err);
1575
1559
  }
1576
- };
1560
+ };
1561
+
1562
+ // Optional helpers for making object chat shells draggable / resizable.
1563
+ // Kept behind flags so they can be toggled or iterated on without
1564
+ // affecting core behavior.
1565
+ Ai.objectChatDraggable = false;
1566
+ Ai.objectChatResizable = false;
1567
+ Ai.enableObjectChatDrag = function(shell){
1568
+ try{
1569
+ var header = shell.querySelector('.joe-object-chat-header');
1570
+ if (!header) { return; }
1571
+ let dragging = false;
1572
+ let dragOffsetX = 0, dragOffsetY = 0;
1573
+ header.addEventListener('pointerdown', function(e){
1574
+ dragging = true;
1575
+ shell.setPointerCapture(e.pointerId);
1576
+ dragOffsetX = e.clientX - shell.offsetLeft;
1577
+ dragOffsetY = e.clientY - shell.offsetTop;
1578
+ });
1579
+ header.addEventListener('pointermove', function(e){
1580
+ if (!dragging) return;
1581
+ const x = e.clientX - dragOffsetX;
1582
+ const y = e.clientY - dragOffsetY;
1583
+ shell.style.left = x + 'px';
1584
+ shell.style.top = y + 'px';
1585
+ shell.style.right = 'auto';
1586
+ shell.style.bottom = 'auto';
1587
+ });
1588
+ header.addEventListener('pointerup', function(e){
1589
+ if (!dragging) return;
1590
+ dragging = false;
1591
+ try{ shell.releasePointerCapture(e.pointerId); }catch(_e){}
1592
+ });
1593
+ }catch(e){
1594
+ console.warn('enableObjectChatDrag error', e);
1595
+ }
1596
+ };
1597
+ Ai.enableObjectChatResize = function(shell){
1598
+ try{
1599
+ shell.style.resize = 'both';
1600
+ shell.style.overflow = 'hidden';
1601
+ }catch(e){
1602
+ console.warn('enableObjectChatResize error', e);
1603
+ }
1604
+ };
1605
+
1606
+ // Simple object-scoped widget chat launcher. For a given object id +
1607
+ // itemtype we maintain (per-page) a single floating panel containing:
1608
+ // - <joe-ai-assistant-picker>
1609
+ // - <joe-ai-widget>
1610
+ // This reuses the AIHub chat stack, but adds scope_itemtype/scope_id so
1611
+ // the backend can attach object files and context.
1612
+ Ai._objectWidgets = Ai._objectWidgets || {};
1613
+ Ai.openObjectChatLauncher = function(object_id, itemtype, name){
1614
+ try{
1615
+ if (!object_id) { return; }
1616
+ var key = (itemtype || 'item') + ':' + object_id;
1617
+ var existing = Ai._objectWidgets[key];
1618
+ if (existing && document.body.contains(existing)) {
1619
+ existing.scrollIntoView({ behavior:'smooth', block:'end' });
1620
+ return;
1621
+ }
1622
+ var isMobile = (window.innerWidth || document.documentElement.clientWidth || 0) <= 768;
1623
+ var shell = document.createElement('div');
1624
+ shell.className = 'joe-object-chat-shell';
1625
+
1626
+ var safeKey = key.replace(/[^a-zA-Z0-9_\-]/g,'_');
1627
+ var widgetId = 'object_chat_'+safeKey;
1628
+ var title = name || (itemtype+': '+object_id);
1629
+
1630
+ // Header with assistant picker + close button
1631
+ var header = document.createElement('div');
1632
+ header.className = 'joe-object-chat-header';
1633
+ header.style.display = 'flex';
1634
+ header.style.alignItems = 'center';
1635
+ header.style.justifyContent = 'space-between';
1636
+ header.style.padding = '4px 8px';
1637
+ header.style.background = '#111827';
1638
+ header.style.color = '#f9fafb';
1639
+ header.style.cursor = isMobile ? 'default' : 'move';
1640
+
1641
+ var titleSpan = document.createElement('span');
1642
+ titleSpan.textContent = title;
1643
+ titleSpan.style.fontSize = '12px';
1644
+ titleSpan.style.marginRight = '8px';
1645
+
1646
+ var picker = document.createElement('joe-ai-assistant-picker');
1647
+ picker.setAttribute('for_widget', widgetId);
1648
+ picker.setAttribute('source', 'object_chat');
1649
+ picker.style.flex = '1 1 auto';
1650
+ picker.style.marginRight = '8px';
1651
+
1652
+ var closeBtn = document.createElement('button');
1653
+ closeBtn.type = 'button';
1654
+ closeBtn.textContent = '×';
1655
+ closeBtn.title = 'Close chat';
1656
+ closeBtn.style.border = 'none';
1657
+ closeBtn.style.background = 'transparent';
1658
+ closeBtn.style.color = '#f9fafb';
1659
+ closeBtn.style.cursor = 'pointer';
1660
+ closeBtn.style.fontSize = '16px';
1661
+ closeBtn.style.lineHeight = '16px';
1662
+ closeBtn.style.padding = '0 4px';
1663
+
1664
+ header.appendChild(titleSpan);
1665
+ header.appendChild(picker);
1666
+ header.appendChild(closeBtn);
1667
+
1668
+ // Widget body
1669
+ var w = document.createElement('joe-ai-widget');
1670
+ w.setAttribute('id', widgetId);
1671
+ w.setAttribute('title', title);
1672
+ w.setAttribute('source','object_chat');
1673
+ w.setAttribute('scope_itemtype', itemtype || '');
1674
+ w.setAttribute('scope_id', object_id);
1675
+ // Default assistant for this widget; picker can override per-conversation.
1676
+ try{
1677
+ // Ai.getDefaultAssistant returns the DEFAULT_AI_ASSISTANT setting
1678
+ // record, whose .value is the ai_assistant _id we want to use here.
1679
+ var defSetting = Ai.getDefaultAssistant && Ai.getDefaultAssistant();
1680
+ if (defSetting && defSetting.value){
1681
+ w.setAttribute('ai_assistant_id', defSetting.value);
1682
+ }
1683
+ }catch(_e){}
1684
+ // Pass current user id through so widgetStart/widgetMessage can
1685
+ // attribute the conversation correctly.
1686
+ try{
1687
+ if (_joe && _joe.User && _joe.User._id){
1688
+ w.setAttribute('user_id', _joe.User._id);
1689
+ // Also set the property so resolveUser sees it even if the
1690
+ // attribute change happens after the custom element constructor.
1691
+ w.user_id = _joe.User._id;
1692
+ }
1693
+ }catch(_e){}
1694
+ w.style.display = 'block';
1695
+ w.style.width = '100%';
1696
+ w.style.height = '100%';
1697
+
1698
+ shell.appendChild(header);
1699
+ shell.appendChild(w);
1700
+
1701
+ // Positioning and sizing
1702
+ shell.style.position = 'fixed';
1703
+ shell.style.zIndex = '10000';
1704
+ shell.style.boxShadow = '0 8px 24px rgba(15,23,42,0.4)';
1705
+ shell.style.background = '#f3f4f6';
1706
+ shell.style.border = '1px solid #e5e7eb';
1707
+ shell.style.borderRadius = '10px 0 0 10px';
1708
+ shell.style.overflow = 'hidden';
1709
+ shell.style.display = 'flex';
1710
+ shell.style.flexDirection = 'column';
1711
+
1712
+ function applyDesktopLayout(){
1713
+ shell.style.width = '420px';
1714
+ shell.style.height = '60vh';
1715
+ shell.style.right = '0px';
1716
+ shell.style.bottom = '50px';
1717
+ shell.style.left = 'auto';
1718
+ shell.style.top = 'auto';
1719
+ shell.style.resize = 'both';
1720
+ }
1721
+ function applyMobileLayout(){
1722
+ shell.style.width = '100vw';
1723
+ shell.style.height = '50vh';
1724
+ shell.style.left = '0px';
1725
+ shell.style.right = '0px';
1726
+ shell.style.bottom = '0px';
1727
+ shell.style.top = 'auto';
1728
+ shell.style.resize = 'none';
1729
+ }
1730
+
1731
+ if (isMobile){
1732
+ applyMobileLayout();
1733
+ }else{
1734
+ applyDesktopLayout();
1735
+ }
1736
+
1737
+ document.body.appendChild(shell);
1738
+ Ai._objectWidgets[key] = shell;
1739
+
1740
+ // Close behavior
1741
+ closeBtn.addEventListener('click', function(){
1742
+ try{
1743
+ if (Ai._objectWidgets[key] === shell){
1744
+ delete Ai._objectWidgets[key];
1745
+ }
1746
+ }catch(_e){}
1747
+ shell.remove();
1748
+ });
1749
+
1750
+ if (!isMobile){
1751
+ if (Ai.objectChatDraggable) {
1752
+ Ai.enableObjectChatDrag(shell);
1753
+ }
1754
+ if (Ai.objectChatResizable) {
1755
+ Ai.enableObjectChatResize(shell);
1756
+ }
1757
+ }
1758
+ }catch(e){
1759
+ console.error('openObjectChatLauncher error', e);
1760
+ }
1761
+ };
1577
1762
 
1578
1763
  // ---------- Autofill (Responses) ----------
1579
1764
  // Usage from schema: add `ai:{ prompt:'...' }` to a field. Core will render a
@@ -1879,13 +2064,23 @@
1879
2064
  var schema = (_joe && _joe.current && _joe.current.schema) || {};
1880
2065
  // Prefer the live, constructed field defs; fall back to schema.fields
1881
2066
  var liveFieldDefs = (_joe && _joe.current && Array.isArray(_joe.current.fields) && _joe.current.fields) || [];
1882
- var schemaFields = (schema && schema.fields) || [];
2067
+ var schemaFields = [];
2068
+ if (schema && schema.fields) {
2069
+ if (Array.isArray(schema.fields)) {
2070
+ schemaFields = schema.fields;
2071
+ } else if (typeof schema.fields === 'function') {
2072
+ try{
2073
+ var tmp = schema.fields();
2074
+ if (Array.isArray(tmp)) { schemaFields = tmp; }
2075
+ }catch(_e){}
2076
+ }
2077
+ }
1883
2078
  var fieldNames = [];
1884
2079
  // collect from live constructed defs
1885
2080
  liveFieldDefs.forEach(function(fd){
1886
2081
  if(fd && fd.name && fd.type === 'uploader'){ fieldNames.push(fd.name); }
1887
2082
  });
1888
- // also collect from raw schema (string or object), dedupe
2083
+ // also collect from raw schema (string or object or spec), dedupe
1889
2084
  schemaFields.forEach(function(f){
1890
2085
  var fname = (typeof f === 'string') ? f : (f && (f.name || f.field || f.prop));
1891
2086
  if(!fname){ return; }
@@ -2065,11 +2260,16 @@
2065
2260
  };
2066
2261
 
2067
2262
 
2068
- // Attach AI to _joe
2069
- if (window._joe) {
2070
- _joe.Ai = Ai;
2071
- } else {
2072
- console.warn('joeAI.js loaded before _joe was ready.');
2263
+ // Attach AI to _joe. On some pages joe-ai.js may load before _joe is
2264
+ // constructed; in that case, defer attachment until window load.
2265
+ function attachAItoJoe(){
2266
+ if (!window._joe) { return false; }
2267
+ if (!_joe.Ai) { _joe.Ai = Ai; }
2268
+ return true;
2269
+ }
2270
+ if (!attachAItoJoe()) {
2271
+ console.warn('joeAI.js loaded before _joe was ready; deferring Ai attach.');
2272
+ window.addEventListener('load', attachAItoJoe);
2073
2273
  }
2074
2274
 
2075
2275
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-object-editor",
3
- "version": "0.10.657",
3
+ "version": "0.10.660",
4
4
  "description": "JOE the Json Object Editor | Platform Edition",
5
5
  "main": "app.js",
6
6
  "scripts": {