@mongoosejs/studio 0.1.20 → 0.2.0
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/backend/actions/ChatMessage/executeScript.js +4 -1
- package/backend/actions/ChatThread/createChatMessage.js +28 -22
- package/backend/helpers/evaluateFilter.js +38 -1
- package/express.js +4 -2
- package/frontend/public/app.js +702 -428
- package/frontend/public/index.html +1 -1
- package/frontend/public/style.css +1 -1
- package/frontend/public/tw.css +81 -62
- package/frontend/src/_util/document-search-autocomplete.js +229 -0
- package/frontend/src/chat/chat-message-script/chat-message-script.html +27 -20
- package/frontend/src/chat/chat.html +20 -17
- package/frontend/src/chat/chat.js +2 -0
- package/frontend/src/document/document.css +1 -8
- package/frontend/src/document/document.html +202 -164
- package/frontend/src/document/document.js +1 -0
- package/frontend/src/document-details/document-details.html +1 -11
- package/frontend/src/document-details/document-details.js +43 -1
- package/frontend/src/document-details/document-property/document-property.html +4 -4
- package/frontend/src/index.js +36 -15
- package/frontend/src/json-node/json-node.html +118 -0
- package/frontend/src/json-node/json-node.js +272 -0
- package/frontend/src/list-array/list-array.html +15 -3
- package/frontend/src/list-array/list-array.js +21 -3
- package/frontend/src/list-default/list-default.js +2 -2
- package/frontend/src/list-json/list-json.html +1 -1
- package/frontend/src/list-json/list-json.js +11 -273
- package/frontend/src/list-subdocument/list-subdocument.html +13 -4
- package/frontend/src/list-subdocument/list-subdocument.js +11 -6
- package/frontend/src/models/document-search/document-search.html +1 -1
- package/frontend/src/models/document-search/document-search.js +22 -116
- package/frontend/src/models/models.css +5 -15
- package/frontend/src/models/models.html +34 -34
- package/frontend/src/navbar/navbar.html +15 -6
- package/package.json +2 -2
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta content="width=device-width, initial-scale=1" name="viewport">
|
|
6
6
|
<meta name="msapplication-TileColor" />
|
|
7
7
|
<meta name="theme-color" content="#ffffff"/>
|
|
8
|
-
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=
|
|
8
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter"/>
|
|
9
9
|
<link rel="stylesheet" type="text/css" href="style.css"/>
|
|
10
10
|
<link rel="stylesheet" href="https://unpkg.com/prismjs@1.29.0/themes/prism.css"/>
|
|
11
11
|
<link rel="stylesheet" href="tw.css">
|
package/frontend/public/tw.css
CHANGED
|
@@ -731,6 +731,11 @@ video {
|
|
|
731
731
|
margin: 1rem;
|
|
732
732
|
}
|
|
733
733
|
|
|
734
|
+
.\!my-0 {
|
|
735
|
+
margin-top: 0px !important;
|
|
736
|
+
margin-bottom: 0px !important;
|
|
737
|
+
}
|
|
738
|
+
|
|
734
739
|
.-mx-4 {
|
|
735
740
|
margin-left: -1rem;
|
|
736
741
|
margin-right: -1rem;
|
|
@@ -756,6 +761,11 @@ video {
|
|
|
756
761
|
margin-right: auto;
|
|
757
762
|
}
|
|
758
763
|
|
|
764
|
+
.my-1 {
|
|
765
|
+
margin-top: 0.25rem;
|
|
766
|
+
margin-bottom: 0.25rem;
|
|
767
|
+
}
|
|
768
|
+
|
|
759
769
|
.my-4 {
|
|
760
770
|
margin-top: 1rem;
|
|
761
771
|
margin-bottom: 1rem;
|
|
@@ -924,6 +934,10 @@ video {
|
|
|
924
934
|
height: 90vh !important;
|
|
925
935
|
}
|
|
926
936
|
|
|
937
|
+
.h-10 {
|
|
938
|
+
height: 2.5rem;
|
|
939
|
+
}
|
|
940
|
+
|
|
927
941
|
.h-12 {
|
|
928
942
|
height: 3rem;
|
|
929
943
|
}
|
|
@@ -984,10 +998,6 @@ video {
|
|
|
984
998
|
height: 100%;
|
|
985
999
|
}
|
|
986
1000
|
|
|
987
|
-
.h-px {
|
|
988
|
-
height: 1px;
|
|
989
|
-
}
|
|
990
|
-
|
|
991
1001
|
.max-h-40 {
|
|
992
1002
|
max-height: 10rem;
|
|
993
1003
|
}
|
|
@@ -1000,10 +1010,6 @@ video {
|
|
|
1000
1010
|
max-height: 50vh;
|
|
1001
1011
|
}
|
|
1002
1012
|
|
|
1003
|
-
.max-h-\[6\.5em\] {
|
|
1004
|
-
max-height: 6.5em;
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
1013
|
.min-h-0 {
|
|
1008
1014
|
min-height: 0px;
|
|
1009
1015
|
}
|
|
@@ -1104,8 +1110,8 @@ video {
|
|
|
1104
1110
|
max-width: 64rem;
|
|
1105
1111
|
}
|
|
1106
1112
|
|
|
1107
|
-
.max-w
|
|
1108
|
-
max-width:
|
|
1113
|
+
.max-w-7xl {
|
|
1114
|
+
max-width: 80rem;
|
|
1109
1115
|
}
|
|
1110
1116
|
|
|
1111
1117
|
.max-w-\[calc\(100vw-3rem\)\] {
|
|
@@ -1116,10 +1122,6 @@ video {
|
|
|
1116
1122
|
max-width: calc(100vw - 4rem);
|
|
1117
1123
|
}
|
|
1118
1124
|
|
|
1119
|
-
.max-w-xs {
|
|
1120
|
-
max-width: 20rem;
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
1125
|
.flex-1 {
|
|
1124
1126
|
flex: 1 1 0%;
|
|
1125
1127
|
}
|
|
@@ -1200,6 +1202,10 @@ video {
|
|
|
1200
1202
|
animation: spin 1s linear infinite;
|
|
1201
1203
|
}
|
|
1202
1204
|
|
|
1205
|
+
.cursor-auto {
|
|
1206
|
+
cursor: auto;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1203
1209
|
.cursor-not-allowed {
|
|
1204
1210
|
cursor: not-allowed;
|
|
1205
1211
|
}
|
|
@@ -1338,12 +1344,6 @@ video {
|
|
|
1338
1344
|
margin-bottom: calc(1rem * var(--tw-space-y-reverse));
|
|
1339
1345
|
}
|
|
1340
1346
|
|
|
1341
|
-
.space-y-6 > :not([hidden]) ~ :not([hidden]) {
|
|
1342
|
-
--tw-space-y-reverse: 0;
|
|
1343
|
-
margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
|
|
1344
|
-
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
1347
|
.divide-y > :not([hidden]) ~ :not([hidden]) {
|
|
1348
1348
|
--tw-divide-y-reverse: 0;
|
|
1349
1349
|
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
|
@@ -1403,6 +1403,10 @@ video {
|
|
|
1403
1403
|
white-space: nowrap;
|
|
1404
1404
|
}
|
|
1405
1405
|
|
|
1406
|
+
.text-ellipsis {
|
|
1407
|
+
text-overflow: ellipsis;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1406
1410
|
.whitespace-nowrap {
|
|
1407
1411
|
white-space: nowrap;
|
|
1408
1412
|
}
|
|
@@ -1543,6 +1547,11 @@ video {
|
|
|
1543
1547
|
border-color: rgb(239 68 68 / var(--tw-border-opacity));
|
|
1544
1548
|
}
|
|
1545
1549
|
|
|
1550
|
+
.border-slate-100 {
|
|
1551
|
+
--tw-border-opacity: 1;
|
|
1552
|
+
border-color: rgb(241 245 249 / var(--tw-border-opacity));
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1546
1555
|
.border-transparent {
|
|
1547
1556
|
border-color: transparent;
|
|
1548
1557
|
}
|
|
@@ -1557,6 +1566,11 @@ video {
|
|
|
1557
1566
|
border-left-color: rgb(59 130 246 / var(--tw-border-opacity));
|
|
1558
1567
|
}
|
|
1559
1568
|
|
|
1569
|
+
.\!bg-zinc-50 {
|
|
1570
|
+
--tw-bg-opacity: 1 !important;
|
|
1571
|
+
background-color: rgb(250 250 250 / var(--tw-bg-opacity)) !important;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1560
1574
|
.bg-amber-100 {
|
|
1561
1575
|
--tw-bg-opacity: 1;
|
|
1562
1576
|
background-color: rgb(254 243 199 / var(--tw-bg-opacity));
|
|
@@ -1637,14 +1651,14 @@ video {
|
|
|
1637
1651
|
background-color: rgb(75 85 99 / var(--tw-bg-opacity));
|
|
1638
1652
|
}
|
|
1639
1653
|
|
|
1640
|
-
.bg-gray-
|
|
1654
|
+
.bg-gray-700 {
|
|
1641
1655
|
--tw-bg-opacity: 1;
|
|
1642
|
-
background-color: rgb(
|
|
1656
|
+
background-color: rgb(55 65 81 / var(--tw-bg-opacity));
|
|
1643
1657
|
}
|
|
1644
1658
|
|
|
1645
|
-
.bg-
|
|
1659
|
+
.bg-gray-800 {
|
|
1646
1660
|
--tw-bg-opacity: 1;
|
|
1647
|
-
background-color: rgb(
|
|
1661
|
+
background-color: rgb(31 41 55 / var(--tw-bg-opacity));
|
|
1648
1662
|
}
|
|
1649
1663
|
|
|
1650
1664
|
.bg-green-50 {
|
|
@@ -1652,29 +1666,19 @@ video {
|
|
|
1652
1666
|
background-color: rgb(240 253 244 / var(--tw-bg-opacity));
|
|
1653
1667
|
}
|
|
1654
1668
|
|
|
1655
|
-
.bg-green-500 {
|
|
1656
|
-
--tw-bg-opacity: 1;
|
|
1657
|
-
background-color: rgb(34 197 94 / var(--tw-bg-opacity));
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
1669
|
.bg-green-600 {
|
|
1661
1670
|
--tw-bg-opacity: 1;
|
|
1662
1671
|
background-color: rgb(22 163 74 / var(--tw-bg-opacity));
|
|
1663
1672
|
}
|
|
1664
1673
|
|
|
1665
|
-
.bg-pink-600 {
|
|
1666
|
-
--tw-bg-opacity: 1;
|
|
1667
|
-
background-color: rgb(219 39 119 / var(--tw-bg-opacity));
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
1674
|
.bg-red-100 {
|
|
1671
1675
|
--tw-bg-opacity: 1;
|
|
1672
1676
|
background-color: rgb(254 226 226 / var(--tw-bg-opacity));
|
|
1673
1677
|
}
|
|
1674
1678
|
|
|
1675
|
-
.bg-red-
|
|
1679
|
+
.bg-red-400 {
|
|
1676
1680
|
--tw-bg-opacity: 1;
|
|
1677
|
-
background-color: rgb(
|
|
1681
|
+
background-color: rgb(248 113 113 / var(--tw-bg-opacity));
|
|
1678
1682
|
}
|
|
1679
1683
|
|
|
1680
1684
|
.bg-red-50 {
|
|
@@ -1692,6 +1696,16 @@ video {
|
|
|
1692
1696
|
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
|
|
1693
1697
|
}
|
|
1694
1698
|
|
|
1699
|
+
.bg-slate-100 {
|
|
1700
|
+
--tw-bg-opacity: 1;
|
|
1701
|
+
background-color: rgb(241 245 249 / var(--tw-bg-opacity));
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
.bg-slate-50 {
|
|
1705
|
+
--tw-bg-opacity: 1;
|
|
1706
|
+
background-color: rgb(248 250 252 / var(--tw-bg-opacity));
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1695
1709
|
.bg-slate-500 {
|
|
1696
1710
|
--tw-bg-opacity: 1;
|
|
1697
1711
|
background-color: rgb(100 116 139 / var(--tw-bg-opacity));
|
|
@@ -1716,6 +1730,11 @@ video {
|
|
|
1716
1730
|
background-color: rgb(229 234 255 / var(--tw-bg-opacity));
|
|
1717
1731
|
}
|
|
1718
1732
|
|
|
1733
|
+
.bg-ultramarine-200 {
|
|
1734
|
+
--tw-bg-opacity: 1;
|
|
1735
|
+
background-color: rgb(206 218 255 / var(--tw-bg-opacity));
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1719
1738
|
.bg-ultramarine-600 {
|
|
1720
1739
|
--tw-bg-opacity: 1;
|
|
1721
1740
|
background-color: rgb(24 35 255 / var(--tw-bg-opacity));
|
|
@@ -1736,9 +1755,9 @@ video {
|
|
|
1736
1755
|
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
|
1737
1756
|
}
|
|
1738
1757
|
|
|
1739
|
-
.bg-yellow-
|
|
1758
|
+
.bg-yellow-400 {
|
|
1740
1759
|
--tw-bg-opacity: 1;
|
|
1741
|
-
background-color: rgb(
|
|
1760
|
+
background-color: rgb(250 204 21 / var(--tw-bg-opacity));
|
|
1742
1761
|
}
|
|
1743
1762
|
|
|
1744
1763
|
.bg-opacity-40 {
|
|
@@ -1749,6 +1768,10 @@ video {
|
|
|
1749
1768
|
--tw-bg-opacity: 0.6;
|
|
1750
1769
|
}
|
|
1751
1770
|
|
|
1771
|
+
.p-0 {
|
|
1772
|
+
padding: 0px;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1752
1775
|
.p-1 {
|
|
1753
1776
|
padding: 0.25rem;
|
|
1754
1777
|
}
|
|
@@ -1910,10 +1933,6 @@ video {
|
|
|
1910
1933
|
padding-top: 1rem;
|
|
1911
1934
|
}
|
|
1912
1935
|
|
|
1913
|
-
.pt-\[1px\] {
|
|
1914
|
-
padding-top: 1px;
|
|
1915
|
-
}
|
|
1916
|
-
|
|
1917
1936
|
.text-left {
|
|
1918
1937
|
text-align: left;
|
|
1919
1938
|
}
|
|
@@ -2398,6 +2417,11 @@ video {
|
|
|
2398
2417
|
border-color: rgb(209 213 219 / var(--tw-border-opacity));
|
|
2399
2418
|
}
|
|
2400
2419
|
|
|
2420
|
+
.hover\:border-slate-300:hover {
|
|
2421
|
+
--tw-border-opacity: 1;
|
|
2422
|
+
border-color: rgb(203 213 225 / var(--tw-border-opacity));
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2401
2425
|
.hover\:border-l-blue-600:hover {
|
|
2402
2426
|
--tw-border-opacity: 1;
|
|
2403
2427
|
border-left-color: rgb(37 99 235 / var(--tw-border-opacity));
|
|
@@ -2463,26 +2487,11 @@ video {
|
|
|
2463
2487
|
background-color: rgb(220 252 231 / var(--tw-bg-opacity));
|
|
2464
2488
|
}
|
|
2465
2489
|
|
|
2466
|
-
.hover\:bg-green-300:hover {
|
|
2467
|
-
--tw-bg-opacity: 1;
|
|
2468
|
-
background-color: rgb(134 239 172 / var(--tw-bg-opacity));
|
|
2469
|
-
}
|
|
2470
|
-
|
|
2471
2490
|
.hover\:bg-green-500:hover {
|
|
2472
2491
|
--tw-bg-opacity: 1;
|
|
2473
2492
|
background-color: rgb(34 197 94 / var(--tw-bg-opacity));
|
|
2474
2493
|
}
|
|
2475
2494
|
|
|
2476
|
-
.hover\:bg-green-600:hover {
|
|
2477
|
-
--tw-bg-opacity: 1;
|
|
2478
|
-
background-color: rgb(22 163 74 / var(--tw-bg-opacity));
|
|
2479
|
-
}
|
|
2480
|
-
|
|
2481
|
-
.hover\:bg-green-700:hover {
|
|
2482
|
-
--tw-bg-opacity: 1;
|
|
2483
|
-
background-color: rgb(21 128 61 / var(--tw-bg-opacity));
|
|
2484
|
-
}
|
|
2485
|
-
|
|
2486
2495
|
.hover\:bg-pink-100:hover {
|
|
2487
2496
|
--tw-bg-opacity: 1;
|
|
2488
2497
|
background-color: rgb(252 231 243 / var(--tw-bg-opacity));
|
|
@@ -2538,6 +2547,11 @@ video {
|
|
|
2538
2547
|
background-color: rgb(206 218 255 / var(--tw-bg-opacity));
|
|
2539
2548
|
}
|
|
2540
2549
|
|
|
2550
|
+
.hover\:bg-ultramarine-50:hover {
|
|
2551
|
+
--tw-bg-opacity: 1;
|
|
2552
|
+
background-color: rgb(241 245 255 / var(--tw-bg-opacity));
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2541
2555
|
.hover\:bg-ultramarine-500:hover {
|
|
2542
2556
|
--tw-bg-opacity: 1;
|
|
2543
2557
|
background-color: rgb(63 83 255 / var(--tw-bg-opacity));
|
|
@@ -2588,6 +2602,12 @@ video {
|
|
|
2588
2602
|
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
|
2589
2603
|
}
|
|
2590
2604
|
|
|
2605
|
+
.hover\:shadow-sm:hover {
|
|
2606
|
+
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
2607
|
+
--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
|
|
2608
|
+
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
|
2609
|
+
}
|
|
2610
|
+
|
|
2591
2611
|
.focus\:z-10:focus {
|
|
2592
2612
|
z-index: 10;
|
|
2593
2613
|
}
|
|
@@ -2663,11 +2683,6 @@ video {
|
|
|
2663
2683
|
--tw-ring-color: rgb(75 85 99 / var(--tw-ring-opacity));
|
|
2664
2684
|
}
|
|
2665
2685
|
|
|
2666
|
-
.focus\:ring-green-500:focus {
|
|
2667
|
-
--tw-ring-opacity: 1;
|
|
2668
|
-
--tw-ring-color: rgb(34 197 94 / var(--tw-ring-opacity));
|
|
2669
|
-
}
|
|
2670
|
-
|
|
2671
2686
|
.focus\:ring-red-500:focus {
|
|
2672
2687
|
--tw-ring-opacity: 1;
|
|
2673
2688
|
--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity));
|
|
@@ -2923,6 +2938,10 @@ video {
|
|
|
2923
2938
|
gap: 0.75rem;
|
|
2924
2939
|
}
|
|
2925
2940
|
|
|
2941
|
+
.md\:gap-6 {
|
|
2942
|
+
gap: 1.5rem;
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2926
2945
|
.md\:px-0 {
|
|
2927
2946
|
padding-left: 0px;
|
|
2928
2947
|
padding-right: 0px;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Trie } = require('../models/trie');
|
|
4
|
+
|
|
5
|
+
const QUERY_SELECTORS = [
|
|
6
|
+
'$eq',
|
|
7
|
+
'$ne',
|
|
8
|
+
'$gt',
|
|
9
|
+
'$gte',
|
|
10
|
+
'$lt',
|
|
11
|
+
'$lte',
|
|
12
|
+
'$in',
|
|
13
|
+
'$nin',
|
|
14
|
+
'$exists',
|
|
15
|
+
'$regex',
|
|
16
|
+
'$options',
|
|
17
|
+
'$text',
|
|
18
|
+
'$search',
|
|
19
|
+
'$and',
|
|
20
|
+
'$or',
|
|
21
|
+
'$nor',
|
|
22
|
+
'$not',
|
|
23
|
+
'$elemMatch',
|
|
24
|
+
'$size',
|
|
25
|
+
'$all',
|
|
26
|
+
'$type',
|
|
27
|
+
'$expr',
|
|
28
|
+
'$jsonSchema',
|
|
29
|
+
'$mod'
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const VALUE_HELPERS = [
|
|
33
|
+
'objectIdRange',
|
|
34
|
+
'ObjectId',
|
|
35
|
+
'Date',
|
|
36
|
+
'Math'
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
// Helpers that are function calls and should have () added
|
|
40
|
+
const FUNCTION_HELPERS = new Set([
|
|
41
|
+
'objectIdRange',
|
|
42
|
+
'ObjectId',
|
|
43
|
+
'Date'
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
function buildAutocompleteTrie(schemaPaths) {
|
|
47
|
+
const trie = new Trie();
|
|
48
|
+
// Query operators can appear as field names in nested objects, so add with both roles
|
|
49
|
+
trie.bulkInsert(QUERY_SELECTORS, 5, 'operator');
|
|
50
|
+
trie.bulkInsert(QUERY_SELECTORS, 5, 'fieldName');
|
|
51
|
+
trie.bulkInsert(VALUE_HELPERS, 5, 'value');
|
|
52
|
+
|
|
53
|
+
if (Array.isArray(schemaPaths) && schemaPaths.length > 0) {
|
|
54
|
+
const paths = schemaPaths
|
|
55
|
+
.map(path => path?.path)
|
|
56
|
+
.filter(path => typeof path === 'string' && path.length > 0);
|
|
57
|
+
for (const path of schemaPaths) {
|
|
58
|
+
if (path.schema) {
|
|
59
|
+
paths.push(...Object.keys(path.schema).map(subpath => `${path.path}.${subpath}`));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
trie.bulkInsert(paths, 10, 'fieldName');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return trie;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getAutocompleteContext(searchText, cursorPos) {
|
|
69
|
+
const before = searchText.slice(0, cursorPos);
|
|
70
|
+
|
|
71
|
+
// Check if we're in a field name context (after { or ,)
|
|
72
|
+
// This takes precedence over value context to handle cases like { _id: { $gt
|
|
73
|
+
const fieldMatch = before.match(/(?:\{|,)\s*([^:\s]*)$/);
|
|
74
|
+
if (fieldMatch) {
|
|
75
|
+
const token = fieldMatch[1];
|
|
76
|
+
return {
|
|
77
|
+
token,
|
|
78
|
+
role: 'fieldName',
|
|
79
|
+
startPos: cursorPos - token.length
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check if we're in a value context (after a colon)
|
|
84
|
+
// Match the last colon followed by optional whitespace and capture everything after
|
|
85
|
+
const valueMatch = before.match(/:\s*([^\s,\}\]:]*)$/);
|
|
86
|
+
if (valueMatch) {
|
|
87
|
+
const token = valueMatch[1];
|
|
88
|
+
return {
|
|
89
|
+
token,
|
|
90
|
+
role: 'value',
|
|
91
|
+
startPos: cursorPos - token.length
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getAutocompleteSuggestions(trie, searchText, cursorPos, schemaPaths) {
|
|
99
|
+
const context = getAutocompleteContext(searchText, cursorPos);
|
|
100
|
+
|
|
101
|
+
if (!context) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const { token, role } = context;
|
|
106
|
+
|
|
107
|
+
// Extract the actual term without quotes
|
|
108
|
+
const leadingQuoteMatch = token.match(/^["']/);
|
|
109
|
+
const trailingQuoteMatch = token.length > 1 && /["']$/.test(token)
|
|
110
|
+
? token[token.length - 1]
|
|
111
|
+
: '';
|
|
112
|
+
const term = token
|
|
113
|
+
.replace(/^["']/, '')
|
|
114
|
+
.replace(trailingQuoteMatch ? new RegExp(`[${trailingQuoteMatch}]$`) : '', '')
|
|
115
|
+
.trim();
|
|
116
|
+
|
|
117
|
+
if (!term) {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const primarySuggestions = trie.getSuggestions(term, 10, role);
|
|
122
|
+
const suggestionsSet = new Set(primarySuggestions);
|
|
123
|
+
|
|
124
|
+
// Add schema path suggestions for field names
|
|
125
|
+
if (role === 'fieldName' && Array.isArray(schemaPaths) && schemaPaths.length > 0) {
|
|
126
|
+
for (const schemaPath of schemaPaths) {
|
|
127
|
+
const path = schemaPath?.path;
|
|
128
|
+
if (
|
|
129
|
+
typeof path === 'string' &&
|
|
130
|
+
path.startsWith(`${term}.`) &&
|
|
131
|
+
!suggestionsSet.has(path)
|
|
132
|
+
) {
|
|
133
|
+
suggestionsSet.add(path);
|
|
134
|
+
if (suggestionsSet.size >= 10) {
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let suggestions = Array.from(suggestionsSet);
|
|
142
|
+
|
|
143
|
+
// Preserve quotes if present
|
|
144
|
+
if (leadingQuoteMatch) {
|
|
145
|
+
const leadingQuote = leadingQuoteMatch[0];
|
|
146
|
+
suggestions = suggestions.map(suggestion => `${leadingQuote}${suggestion}`);
|
|
147
|
+
}
|
|
148
|
+
if (trailingQuoteMatch) {
|
|
149
|
+
suggestions = suggestions.map(suggestion =>
|
|
150
|
+
suggestion.endsWith(trailingQuoteMatch) ? suggestion : `${suggestion}${trailingQuoteMatch}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return suggestions;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function applySuggestion(searchText, cursorPos, suggestion) {
|
|
158
|
+
const before = searchText.slice(0, cursorPos);
|
|
159
|
+
const after = searchText.slice(cursorPos);
|
|
160
|
+
|
|
161
|
+
// Check if we're in a value context
|
|
162
|
+
const valueMatch = before.match(/:\s*([^\s,\}\]:]*)$/);
|
|
163
|
+
if (valueMatch) {
|
|
164
|
+
const token = valueMatch[1];
|
|
165
|
+
const start = cursorPos - token.length;
|
|
166
|
+
let replacement = suggestion;
|
|
167
|
+
let cursorOffset = replacement.length;
|
|
168
|
+
|
|
169
|
+
// Add parentheses for function helpers and position cursor inside
|
|
170
|
+
if (FUNCTION_HELPERS.has(suggestion)) {
|
|
171
|
+
replacement = `${suggestion}()`;
|
|
172
|
+
cursorOffset = suggestion.length + 1; // Position cursor between ()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
text: searchText.slice(0, start) + replacement + after,
|
|
177
|
+
newCursorPos: start + cursorOffset
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Check if we're in a field name context
|
|
182
|
+
const fieldMatch = before.match(/(?:\{|,)\s*([^:\s]*)$/);
|
|
183
|
+
if (fieldMatch) {
|
|
184
|
+
const token = fieldMatch[1];
|
|
185
|
+
const start = cursorPos - token.length;
|
|
186
|
+
let replacement = suggestion;
|
|
187
|
+
|
|
188
|
+
const leadingQuote = token.startsWith('"') || token.startsWith('\'') ? token[0] : '';
|
|
189
|
+
const trailingQuote = token.length > 1 && (token.endsWith('"') || token.endsWith('\'')) ? token[token.length - 1] : '';
|
|
190
|
+
const colonNeeded = !/^\s*:/.test(after);
|
|
191
|
+
|
|
192
|
+
// If suggestion already has quotes, use it as-is
|
|
193
|
+
const suggestionHasQuotes = (suggestion.startsWith('"') || suggestion.startsWith('\'')) &&
|
|
194
|
+
(suggestion.endsWith('"') || suggestion.endsWith('\''));
|
|
195
|
+
if (suggestionHasQuotes) {
|
|
196
|
+
replacement = suggestion;
|
|
197
|
+
} else {
|
|
198
|
+
if (leadingQuote && !replacement.startsWith(leadingQuote)) {
|
|
199
|
+
replacement = `${leadingQuote}${replacement}`;
|
|
200
|
+
}
|
|
201
|
+
if (trailingQuote && !replacement.endsWith(trailingQuote)) {
|
|
202
|
+
replacement = `${replacement}${trailingQuote}`;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Only insert : if we know the user isn't entering in a nested path
|
|
207
|
+
// If suggestion has full quotes or user typed both quotes, add colon
|
|
208
|
+
if (colonNeeded && (suggestionHasQuotes || !leadingQuote || trailingQuote)) {
|
|
209
|
+
replacement = `${replacement}:`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
text: searchText.slice(0, start) + replacement + after,
|
|
214
|
+
newCursorPos: start + replacement.length
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
module.exports = {
|
|
222
|
+
buildAutocompleteTrie,
|
|
223
|
+
getAutocompleteContext,
|
|
224
|
+
getAutocompleteSuggestions,
|
|
225
|
+
applySuggestion,
|
|
226
|
+
QUERY_SELECTORS,
|
|
227
|
+
VALUE_HELPERS,
|
|
228
|
+
FUNCTION_HELPERS
|
|
229
|
+
};
|
|
@@ -1,26 +1,28 @@
|
|
|
1
|
-
<div class="relative border rounded bg-
|
|
2
|
-
<div class="flex border-b
|
|
1
|
+
<div class="relative border rounded bg-white my-1 text-black text-sm overflow-hidden">
|
|
2
|
+
<div class="flex border-b py-1 pl-1 text-xs font-medium bg-white">
|
|
3
3
|
<button
|
|
4
|
-
class="px-
|
|
5
|
-
:class="
|
|
4
|
+
class="px-4 py-1 border-r border-gray-300 text-gray-700 font-semibold transition-colors duration-200 focus:outline-none"
|
|
5
|
+
:class="[
|
|
6
|
+
'rounded-l-md',
|
|
7
|
+
activeTab === 'code'
|
|
8
|
+
? 'bg-gray-700 text-white shadow'
|
|
9
|
+
: 'bg-gray-100 hover:bg-gray-200 text-gray-600'
|
|
10
|
+
]"
|
|
6
11
|
@click="activeTab = 'code'">
|
|
7
12
|
Code
|
|
8
13
|
</button>
|
|
9
14
|
<button
|
|
10
|
-
class="px-
|
|
11
|
-
:class="
|
|
15
|
+
class="px-4 py-1 text-gray-700 font-semibold transition-colors duration-200 focus:outline-none"
|
|
16
|
+
:class="[
|
|
17
|
+
'rounded-r-md',
|
|
18
|
+
activeTab === 'output'
|
|
19
|
+
? 'bg-gray-700 text-white shadow'
|
|
20
|
+
: 'bg-gray-100 hover:bg-gray-200 text-gray-600'
|
|
21
|
+
]"
|
|
12
22
|
@click="activeTab = 'output'">
|
|
13
23
|
Output
|
|
14
24
|
</button>
|
|
15
25
|
<div class="ml-auto mr-1 flex">
|
|
16
|
-
<button
|
|
17
|
-
v-if="activeTab === 'output'"
|
|
18
|
-
class="px-2 py-1 mr-1 text-xs bg-gray-500 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center"
|
|
19
|
-
@click="copyOutput">
|
|
20
|
-
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
21
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
|
|
22
|
-
</svg>
|
|
23
|
-
</button>
|
|
24
26
|
<button
|
|
25
27
|
v-if="activeTab === 'output'"
|
|
26
28
|
class="px-2 py-1 mr-1 text-xs bg-blue-500 text-white border-none rounded cursor-pointer hover:bg-blue-600 transition-colors flex items-center"
|
|
@@ -39,9 +41,9 @@
|
|
|
39
41
|
</button>
|
|
40
42
|
<async-button
|
|
41
43
|
v-if="!isEditing"
|
|
42
|
-
class="px-2 py-1 text-xs bg-
|
|
44
|
+
class="px-2 py-1 text-xs bg-blue-600 hover:bg-blue-700 text-white border-none rounded cursor-pointer transition-colors disabled:bg-gray-400"
|
|
43
45
|
@click="executeScript">
|
|
44
|
-
|
|
46
|
+
Run
|
|
45
47
|
</async-button>
|
|
46
48
|
<div class="relative ml-1" ref="dropdown">
|
|
47
49
|
<button
|
|
@@ -59,6 +61,11 @@
|
|
|
59
61
|
@click="openCreateDashboardModal(); showDropdown = false">
|
|
60
62
|
Create Dashboard
|
|
61
63
|
</button>
|
|
64
|
+
<button
|
|
65
|
+
class="block w-full text-left px-4 py-2 text-xs text-gray-700 hover:bg-gray-100"
|
|
66
|
+
@click="copyOutput(); showDropdown = false">
|
|
67
|
+
Copy Output As Text
|
|
68
|
+
</button>
|
|
62
69
|
<button
|
|
63
70
|
v-if="canOverwriteDashboard"
|
|
64
71
|
class="block w-full text-left px-4 py-2 text-xs text-gray-700 hover:bg-gray-100"
|
|
@@ -75,25 +82,25 @@
|
|
|
75
82
|
</div>
|
|
76
83
|
</div>
|
|
77
84
|
|
|
78
|
-
<div class="p-
|
|
85
|
+
<div class="p-0 max-h-[50vh] max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] overflow-y-auto" v-show="activeTab === 'code'">
|
|
79
86
|
<div v-if="isEditing" class="flex flex-col space-y-2">
|
|
80
87
|
<div class="border border-gray-200">
|
|
81
88
|
<textarea ref="scriptEditor" class="w-full h-[45vh]" @input="handleScriptInput"></textarea>
|
|
82
89
|
</div>
|
|
83
|
-
<div class="flex justify-end gap-2">
|
|
90
|
+
<div class="flex justify-end gap-2 pb-2">
|
|
84
91
|
<button
|
|
85
92
|
class="px-2 py-1 text-xs bg-gray-300 text-gray-800 border-none rounded cursor-pointer hover:bg-gray-400 transition-colors"
|
|
86
93
|
@click.stop="cancelEditing">
|
|
87
94
|
Cancel
|
|
88
95
|
</button>
|
|
89
96
|
<async-button
|
|
90
|
-
class="px-2 py-1 text-xs bg-
|
|
97
|
+
class="px-2 py-1 text-xs bg-blue-600 text-white border-none rounded cursor-pointer hover:bg-blue-700 transition-colors disabled:bg-gray-400"
|
|
91
98
|
@click="executeScript">
|
|
92
99
|
Execute
|
|
93
100
|
</async-button>
|
|
94
101
|
</div>
|
|
95
102
|
</div>
|
|
96
|
-
<pre v-else class="whitespace-pre-wrap"><code v-text="script" ref="code" :class="'language-' + language"></code></pre>
|
|
103
|
+
<pre v-else class="whitespace-pre-wrap !my-0 !bg-zinc-50"><code v-text="script" ref="code" :class="'language-' + language"></code></pre>
|
|
97
104
|
</div>
|
|
98
105
|
|
|
99
106
|
<div class="p-3 whitespace-pre-wrap max-h-[50vh] overflow-y-auto bg-white border-t max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] relative" v-show="activeTab === 'output'">
|