@kodaris/krubble-app-components 1.0.67 → 1.0.69
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/dist/chatbot.d.ts +36 -27
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +429 -321
- package/dist/chatbot.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/krubble-app.bundled.js +3200 -521
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +395 -298
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +3198 -519
- package/dist/krubble-app.umd.js.map +1 -1
- package/dist/krubble-app.umd.min.js +395 -298
- package/dist/krubble-app.umd.min.js.map +1 -1
- package/dist/router.d.ts +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +8 -2
- package/dist/router.js.map +1 -1
- package/dist/scaffold/nav-item-edit.d.ts.map +1 -1
- package/dist/scaffold/nav-item-edit.js.map +1 -1
- package/dist/scaffold.d.ts +12 -12
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +196 -92
- package/dist/scaffold.js.map +1 -1
- package/dist/screen-detail.js +2 -2
- package/dist/screen-nav.d.ts +4 -4
- package/dist/screen-nav.d.ts.map +1 -1
- package/dist/screen-nav.js +14 -10
- package/dist/screen-nav.js.map +1 -1
- package/dist/subbar.d.ts.map +1 -1
- package/dist/subbar.js +43 -10
- package/dist/subbar.js.map +1 -1
- package/package.json +4 -3
package/dist/krubble-app.umd.js
CHANGED
|
@@ -359,18 +359,6 @@
|
|
|
359
359
|
* Default icon for nav items without an icon (shown at top level)
|
|
360
360
|
*/
|
|
361
361
|
this.defaultNavItemIcon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="12" r="10"/></svg>';
|
|
362
|
-
this.navItemsExpanded = new Set();
|
|
363
|
-
this.navQuery = '';
|
|
364
|
-
this.activeNavItemId = null;
|
|
365
|
-
this.isNavScrolled = false;
|
|
366
|
-
this.isNavOpened = localStorage.getItem('kr-scaffold-nav-opened') !== 'false';
|
|
367
|
-
this.isEditing = false;
|
|
368
|
-
this.isUserMenuOpen = false;
|
|
369
|
-
this.pref = { nav: {} };
|
|
370
|
-
this.draggedNavItemId = null;
|
|
371
|
-
this.navItemDropTargetId = null;
|
|
372
|
-
this.navItemDropPosition = 'above';
|
|
373
|
-
this.pendingRequests = 0;
|
|
374
362
|
this.originalFetch = null;
|
|
375
363
|
this.originalXhrOpen = null;
|
|
376
364
|
this.navItemDragPreview = null;
|
|
@@ -406,6 +394,18 @@
|
|
|
406
394
|
* Breadcrumbs to display in the subbar
|
|
407
395
|
*/
|
|
408
396
|
this.breadcrumbs = [];
|
|
397
|
+
this.navItemsExpanded = new Set();
|
|
398
|
+
this.navQuery = '';
|
|
399
|
+
this.activeNavItemId = null;
|
|
400
|
+
this.isNavScrolled = false;
|
|
401
|
+
this.isNavOpened = localStorage.getItem('kr-scaffold-nav-opened') !== 'false';
|
|
402
|
+
this.isEditing = false;
|
|
403
|
+
this.isUserMenuOpen = false;
|
|
404
|
+
this.pref = { nav: {} };
|
|
405
|
+
this.draggedNavItemId = null;
|
|
406
|
+
this.navItemDropTargetId = null;
|
|
407
|
+
this.navItemDropPosition = 'above';
|
|
408
|
+
this.pendingRequests = 0;
|
|
409
409
|
this.boundHandleMouseMove = this.handleMouseMove.bind(this);
|
|
410
410
|
this.boundHandleMouseUp = this.handleMouseUp.bind(this);
|
|
411
411
|
}
|
|
@@ -439,8 +439,9 @@
|
|
|
439
439
|
* Updates `pendingRequests` state when requests start/complete.
|
|
440
440
|
*/
|
|
441
441
|
installFetchInterceptor() {
|
|
442
|
-
if (this.originalFetch)
|
|
442
|
+
if (this.originalFetch) {
|
|
443
443
|
return;
|
|
444
|
+
}
|
|
444
445
|
const scaffold = this;
|
|
445
446
|
// Intercept fetch
|
|
446
447
|
this.originalFetch = window.fetch.bind(window);
|
|
@@ -491,17 +492,23 @@
|
|
|
491
492
|
* - resolveUrl(item) = "/operations/settings" (used for href and URL comparison)
|
|
492
493
|
*/
|
|
493
494
|
resolveUrl(item) {
|
|
494
|
-
if (!item.url)
|
|
495
|
+
if (!item.url) {
|
|
495
496
|
return '#';
|
|
497
|
+
}
|
|
496
498
|
// External URLs - return as-is
|
|
497
499
|
if (item.external) {
|
|
498
500
|
return item.url;
|
|
499
501
|
}
|
|
500
502
|
// Remove leading slash from url if present, then combine with base
|
|
501
|
-
|
|
503
|
+
let path = item.url;
|
|
504
|
+
if (item.url.startsWith('/')) {
|
|
505
|
+
path = item.url.slice(1);
|
|
506
|
+
}
|
|
502
507
|
const baseHref = document.querySelector('base')?.getAttribute('href') || '/';
|
|
503
|
-
|
|
504
|
-
|
|
508
|
+
if (baseHref.endsWith('/')) {
|
|
509
|
+
return baseHref + path;
|
|
510
|
+
}
|
|
511
|
+
return baseHref + '/' + path;
|
|
505
512
|
}
|
|
506
513
|
// =========================================================================
|
|
507
514
|
// Navigation Data
|
|
@@ -530,11 +537,15 @@
|
|
|
530
537
|
if (item.type === 'group' && item.children) {
|
|
531
538
|
item.children.forEach((child, childIndex) => {
|
|
532
539
|
const childOverride = this.pref.nav[child.id];
|
|
540
|
+
let parentId = item.id;
|
|
541
|
+
if (childOverride?.parentId !== undefined) {
|
|
542
|
+
parentId = childOverride.parentId;
|
|
543
|
+
}
|
|
533
544
|
result.push({
|
|
534
545
|
...child,
|
|
535
546
|
...childOverride,
|
|
536
547
|
order: childOverride?.order ?? child.order ?? childIndex,
|
|
537
|
-
parentId
|
|
548
|
+
parentId,
|
|
538
549
|
});
|
|
539
550
|
});
|
|
540
551
|
}
|
|
@@ -677,14 +688,18 @@
|
|
|
677
688
|
toggleNavItem(itemId) {
|
|
678
689
|
const navItem = this.shadowRoot?.querySelector(`.nav-item[data-id="${itemId}"]`);
|
|
679
690
|
const groupEl = navItem?.nextElementSibling;
|
|
680
|
-
if (!groupEl)
|
|
691
|
+
if (!groupEl) {
|
|
681
692
|
return;
|
|
693
|
+
}
|
|
682
694
|
if (this.navItemsExpanded.has(itemId)) {
|
|
683
695
|
this.navItemsExpanded.delete(itemId);
|
|
684
696
|
groupEl.animate([
|
|
685
697
|
{ height: `${groupEl.scrollHeight}px` },
|
|
686
698
|
{ height: '0px' }
|
|
687
|
-
], {
|
|
699
|
+
], {
|
|
700
|
+
duration: 300,
|
|
701
|
+
easing: 'ease-in-out'
|
|
702
|
+
}).onfinish = () => {
|
|
688
703
|
groupEl.classList.remove('nav-group--expanded');
|
|
689
704
|
};
|
|
690
705
|
}
|
|
@@ -694,7 +709,10 @@
|
|
|
694
709
|
groupEl.animate([
|
|
695
710
|
{ height: '0px' },
|
|
696
711
|
{ height: `${groupEl.scrollHeight}px` }
|
|
697
|
-
], {
|
|
712
|
+
], {
|
|
713
|
+
duration: 300,
|
|
714
|
+
easing: 'ease-in-out'
|
|
715
|
+
});
|
|
698
716
|
}
|
|
699
717
|
this.requestUpdate();
|
|
700
718
|
}
|
|
@@ -789,14 +807,18 @@
|
|
|
789
807
|
this.pref.nav = {};
|
|
790
808
|
}
|
|
791
809
|
savePref() {
|
|
792
|
-
if (this.userType === 'customer')
|
|
810
|
+
if (this.userType === 'customer') {
|
|
793
811
|
return;
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
812
|
+
}
|
|
813
|
+
let url = '/api/system/preference/json/scaffold?global=true';
|
|
814
|
+
let method = 'POST';
|
|
815
|
+
if (this.pref.uuid) {
|
|
816
|
+
url = `/api/system/preference/json/scaffold/${this.pref.uuid}?global=true`;
|
|
817
|
+
method = 'PUT';
|
|
818
|
+
}
|
|
797
819
|
krubbleHttp.Http.fetch({
|
|
798
820
|
url,
|
|
799
|
-
method
|
|
821
|
+
method,
|
|
800
822
|
body: JSON.stringify({ nav: this.pref.nav }),
|
|
801
823
|
})
|
|
802
824
|
.then((response) => response.json())
|
|
@@ -809,8 +831,9 @@
|
|
|
809
831
|
});
|
|
810
832
|
}
|
|
811
833
|
loadPref() {
|
|
812
|
-
if (this.userType === 'customer')
|
|
834
|
+
if (this.userType === 'customer') {
|
|
813
835
|
return;
|
|
836
|
+
}
|
|
814
837
|
krubbleHttp.Http.fetch({
|
|
815
838
|
url: '/api/system/preference/json/scaffold?global=true',
|
|
816
839
|
method: 'GET',
|
|
@@ -827,21 +850,36 @@
|
|
|
827
850
|
});
|
|
828
851
|
}
|
|
829
852
|
handleNavItemContextMenu(e, item) {
|
|
830
|
-
if (!this.isEditing)
|
|
853
|
+
if (!this.isEditing) {
|
|
831
854
|
return;
|
|
855
|
+
}
|
|
832
856
|
e.preventDefault();
|
|
833
857
|
krubbleComponents.ContextMenu.open({
|
|
834
858
|
x: e.clientX,
|
|
835
859
|
y: e.clientY,
|
|
836
860
|
items: [
|
|
837
|
-
{
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
861
|
+
{
|
|
862
|
+
id: 'edit',
|
|
863
|
+
label: 'Edit Item'
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
id: 'divider-1',
|
|
867
|
+
label: '',
|
|
868
|
+
divider: true
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
id: 'add-above',
|
|
872
|
+
label: 'Add Item Above'
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
id: 'add-below',
|
|
876
|
+
label: 'Add Item Below'
|
|
877
|
+
},
|
|
841
878
|
],
|
|
842
879
|
}).then((result) => {
|
|
843
|
-
if (!result)
|
|
880
|
+
if (!result) {
|
|
844
881
|
return;
|
|
882
|
+
}
|
|
845
883
|
switch (result.id) {
|
|
846
884
|
case 'edit':
|
|
847
885
|
this.openNavItemEdit(item);
|
|
@@ -867,8 +905,9 @@
|
|
|
867
905
|
openNavItemEdit(item) {
|
|
868
906
|
krubbleComponents.Dialog.open(KRNavItemEdit, { data: item }).afterClosed().then((res) => {
|
|
869
907
|
const result = res;
|
|
870
|
-
if (!result)
|
|
908
|
+
if (!result) {
|
|
871
909
|
return;
|
|
910
|
+
}
|
|
872
911
|
if (!this.pref.nav[item.id]) {
|
|
873
912
|
this.pref.nav[item.id] = {};
|
|
874
913
|
}
|
|
@@ -895,7 +934,13 @@
|
|
|
895
934
|
.filter(i => i.parentId === targetItem.parentId)
|
|
896
935
|
.sort((a, b) => a.order - b.order);
|
|
897
936
|
const targetIndex = siblings.findIndex(i => i.id === targetItem.id);
|
|
898
|
-
|
|
937
|
+
let newOrder;
|
|
938
|
+
if (position === 'above') {
|
|
939
|
+
newOrder = targetIndex;
|
|
940
|
+
}
|
|
941
|
+
else {
|
|
942
|
+
newOrder = targetIndex + 1;
|
|
943
|
+
}
|
|
899
944
|
// Shift existing items to make room for the new item
|
|
900
945
|
siblings.forEach((sibling, index) => {
|
|
901
946
|
if (index >= newOrder) {
|
|
@@ -932,8 +977,9 @@
|
|
|
932
977
|
* @param item - The nav item being pressed
|
|
933
978
|
*/
|
|
934
979
|
handleNavItemMouseDown(e, item) {
|
|
935
|
-
if (!this.isEditing)
|
|
980
|
+
if (!this.isEditing) {
|
|
936
981
|
return;
|
|
982
|
+
}
|
|
937
983
|
e.preventDefault();
|
|
938
984
|
this.draggedNavItemId = item.id;
|
|
939
985
|
this.navItemDragStartY = e.clientY;
|
|
@@ -951,8 +997,9 @@
|
|
|
951
997
|
* @param e - The mouse event
|
|
952
998
|
*/
|
|
953
999
|
handleMouseMove(e) {
|
|
954
|
-
if (!this.draggedNavItemId)
|
|
1000
|
+
if (!this.draggedNavItemId) {
|
|
955
1001
|
return;
|
|
1002
|
+
}
|
|
956
1003
|
// Start dragging after moving a few pixels (to distinguish from clicks)
|
|
957
1004
|
if (!this.isNavItemDragging && Math.abs(e.clientY - this.navItemDragStartY) > 5) {
|
|
958
1005
|
this.isNavItemDragging = true;
|
|
@@ -968,8 +1015,9 @@
|
|
|
968
1015
|
this.shadowRoot?.appendChild(this.navItemDragPreview);
|
|
969
1016
|
}
|
|
970
1017
|
}
|
|
971
|
-
if (!this.isNavItemDragging)
|
|
1018
|
+
if (!this.isNavItemDragging) {
|
|
972
1019
|
return;
|
|
1020
|
+
}
|
|
973
1021
|
// Update preview position
|
|
974
1022
|
if (this.navItemDragPreview) {
|
|
975
1023
|
this.navItemDragPreview.style.left = `${e.clientX + 10}px`;
|
|
@@ -1020,14 +1068,16 @@
|
|
|
1020
1068
|
updateNavItemDropTarget(e) {
|
|
1021
1069
|
// Get all nav items in shadow DOM
|
|
1022
1070
|
const navItems = this.shadowRoot?.querySelectorAll('.nav-item[data-id]');
|
|
1023
|
-
if (!navItems)
|
|
1071
|
+
if (!navItems) {
|
|
1024
1072
|
return;
|
|
1073
|
+
}
|
|
1025
1074
|
let foundTarget = false;
|
|
1026
1075
|
navItems.forEach((el) => {
|
|
1027
1076
|
const rect = el.getBoundingClientRect();
|
|
1028
1077
|
const itemId = el.getAttribute('data-id');
|
|
1029
|
-
if (!itemId || itemId === this.draggedNavItemId)
|
|
1078
|
+
if (!itemId || itemId === this.draggedNavItemId) {
|
|
1030
1079
|
return;
|
|
1080
|
+
}
|
|
1031
1081
|
// Skip if mouse is outside this element
|
|
1032
1082
|
if (e.clientX < rect.left || e.clientX > rect.right ||
|
|
1033
1083
|
e.clientY < rect.top || e.clientY > rect.bottom) {
|
|
@@ -1064,11 +1114,22 @@
|
|
|
1064
1114
|
}
|
|
1065
1115
|
else {
|
|
1066
1116
|
// Regular items only support above/below, not center
|
|
1067
|
-
|
|
1117
|
+
if (y < rect.height / 2) {
|
|
1118
|
+
position = 'above';
|
|
1119
|
+
}
|
|
1120
|
+
else {
|
|
1121
|
+
position = 'below';
|
|
1122
|
+
}
|
|
1068
1123
|
this.clearNavItemDragExpandTimeout();
|
|
1069
1124
|
}
|
|
1070
1125
|
// Determine what the new parent would be
|
|
1071
|
-
|
|
1126
|
+
let newParentId;
|
|
1127
|
+
if (position === 'center') {
|
|
1128
|
+
newParentId = item.id;
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
newParentId = item.parentId;
|
|
1132
|
+
}
|
|
1072
1133
|
const draggedItem = this.getComputedNav().find(i => i.id === this.draggedNavItemId);
|
|
1073
1134
|
// Don't show drop indicator if invalid drop:
|
|
1074
1135
|
// 1. Can't drop an item into itself (newParentId === draggedNavItemId)
|
|
@@ -1128,13 +1189,15 @@
|
|
|
1128
1189
|
* with new order values for the dragged item and shifts siblings as needed.
|
|
1129
1190
|
*/
|
|
1130
1191
|
executeNavItemDrop() {
|
|
1131
|
-
if (!this.draggedNavItemId || !this.navItemDropTargetId)
|
|
1192
|
+
if (!this.draggedNavItemId || !this.navItemDropTargetId) {
|
|
1132
1193
|
return;
|
|
1194
|
+
}
|
|
1133
1195
|
const allItems = this.getComputedNav();
|
|
1134
1196
|
const draggedItem = allItems.find(i => i.id === this.draggedNavItemId);
|
|
1135
1197
|
const targetItem = allItems.find(i => i.id === this.navItemDropTargetId);
|
|
1136
|
-
if (!draggedItem || !targetItem)
|
|
1198
|
+
if (!draggedItem || !targetItem) {
|
|
1137
1199
|
return;
|
|
1200
|
+
}
|
|
1138
1201
|
// Determine the new parentId for the dragged item
|
|
1139
1202
|
let newParentId;
|
|
1140
1203
|
if (this.navItemDropPosition === 'center' && targetItem.type === 'group') {
|
|
@@ -1146,8 +1209,9 @@
|
|
|
1146
1209
|
else {
|
|
1147
1210
|
newParentId = targetItem.parentId;
|
|
1148
1211
|
}
|
|
1149
|
-
if (newParentId === this.draggedNavItemId)
|
|
1212
|
+
if (newParentId === this.draggedNavItemId) {
|
|
1150
1213
|
return;
|
|
1214
|
+
}
|
|
1151
1215
|
// Get siblings in the destination parent
|
|
1152
1216
|
const siblings = allItems
|
|
1153
1217
|
.filter(i => i.parentId === newParentId && i.id !== this.draggedNavItemId)
|
|
@@ -1218,8 +1282,9 @@
|
|
|
1218
1282
|
// Rendering
|
|
1219
1283
|
// =========================================================================
|
|
1220
1284
|
renderNormalFooter() {
|
|
1221
|
-
if (!this.user)
|
|
1285
|
+
if (!this.user) {
|
|
1222
1286
|
return A;
|
|
1287
|
+
}
|
|
1223
1288
|
const initials = this.user.name
|
|
1224
1289
|
.split(' ')
|
|
1225
1290
|
.map((part) => part[0])
|
|
@@ -1316,7 +1381,11 @@
|
|
|
1316
1381
|
stroke="currentColor"
|
|
1317
1382
|
stroke-width="2"
|
|
1318
1383
|
>
|
|
1319
|
-
<path
|
|
1384
|
+
<path
|
|
1385
|
+
d="M6 9l6 6 6-6"
|
|
1386
|
+
stroke-linecap="round"
|
|
1387
|
+
stroke-linejoin="round"
|
|
1388
|
+
/>
|
|
1320
1389
|
</svg>
|
|
1321
1390
|
</button>
|
|
1322
1391
|
<div class=${e({
|
|
@@ -1368,7 +1437,11 @@
|
|
|
1368
1437
|
</div>
|
|
1369
1438
|
|
|
1370
1439
|
${this.subbar ? b `
|
|
1371
|
-
<kr-subbar
|
|
1440
|
+
<kr-subbar
|
|
1441
|
+
menu
|
|
1442
|
+
.breadcrumbs=${this.breadcrumbs}
|
|
1443
|
+
@menu-click=${this.handleMenuClick}
|
|
1444
|
+
>
|
|
1372
1445
|
<slot name="subbar"></slot>
|
|
1373
1446
|
</kr-subbar>
|
|
1374
1447
|
` : A}
|
|
@@ -1384,7 +1457,11 @@
|
|
|
1384
1457
|
${this.label
|
|
1385
1458
|
? b `<span class="nav-title">${this.label}</span>`
|
|
1386
1459
|
: this.logo
|
|
1387
|
-
? b `<img
|
|
1460
|
+
? b `<img
|
|
1461
|
+
class="nav-logo"
|
|
1462
|
+
src=${this.logo}
|
|
1463
|
+
alt="Logo"
|
|
1464
|
+
/>`
|
|
1388
1465
|
: A}
|
|
1389
1466
|
</div>
|
|
1390
1467
|
<div class="nav-content" @scroll=${this.handleNavScroll}>
|
|
@@ -1392,7 +1469,11 @@
|
|
|
1392
1469
|
<div class="nav-search">
|
|
1393
1470
|
<div class="nav-search__wrapper">
|
|
1394
1471
|
<span class="nav-search__icon">
|
|
1395
|
-
<svg
|
|
1472
|
+
<svg
|
|
1473
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1474
|
+
viewBox="0 0 24 24"
|
|
1475
|
+
fill="currentColor"
|
|
1476
|
+
>
|
|
1396
1477
|
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
|
|
1397
1478
|
</svg>
|
|
1398
1479
|
</span>
|
|
@@ -1405,7 +1486,11 @@
|
|
|
1405
1486
|
/>
|
|
1406
1487
|
${this.navQuery ? b `
|
|
1407
1488
|
<button class="nav-search__clear" @click=${this.handleNavQueryClear}>
|
|
1408
|
-
<svg
|
|
1489
|
+
<svg
|
|
1490
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1491
|
+
viewBox="0 0 24 24"
|
|
1492
|
+
fill="currentColor"
|
|
1493
|
+
>
|
|
1409
1494
|
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
|
|
1410
1495
|
</svg>
|
|
1411
1496
|
</button>
|
|
@@ -1597,7 +1682,7 @@
|
|
|
1597
1682
|
.nav-content {
|
|
1598
1683
|
flex: 1;
|
|
1599
1684
|
overflow-y: auto;
|
|
1600
|
-
padding: 0 9px
|
|
1685
|
+
padding: 0 9px 12px 8px;
|
|
1601
1686
|
}
|
|
1602
1687
|
|
|
1603
1688
|
.nav-footer {
|
|
@@ -1717,7 +1802,7 @@
|
|
|
1717
1802
|
|
|
1718
1803
|
.breadcrumbs {
|
|
1719
1804
|
height: 32px;
|
|
1720
|
-
padding: 0
|
|
1805
|
+
padding: 0 16px;
|
|
1721
1806
|
display: flex;
|
|
1722
1807
|
align-items: center;
|
|
1723
1808
|
gap: 6px;
|
|
@@ -1811,7 +1896,7 @@
|
|
|
1811
1896
|
white-space: nowrap;
|
|
1812
1897
|
overflow: hidden;
|
|
1813
1898
|
text-overflow: ellipsis;
|
|
1814
|
-
letter-spacing: 0.
|
|
1899
|
+
letter-spacing: 0.14px;
|
|
1815
1900
|
}
|
|
1816
1901
|
|
|
1817
1902
|
.nav-item__chevron {
|
|
@@ -1866,7 +1951,7 @@
|
|
|
1866
1951
|
font-size: 11px;
|
|
1867
1952
|
font-weight: 600;
|
|
1868
1953
|
text-transform: uppercase;
|
|
1869
|
-
letter-spacing: 0.
|
|
1954
|
+
letter-spacing: 0.55px;
|
|
1870
1955
|
color: var(--kr-scaffold-nav-text);
|
|
1871
1956
|
opacity: 0.6;
|
|
1872
1957
|
}
|
|
@@ -1989,7 +2074,7 @@
|
|
|
1989
2074
|
|
|
1990
2075
|
.breadcrumbs {
|
|
1991
2076
|
height: 32px;
|
|
1992
|
-
padding: 0
|
|
2077
|
+
padding: 0 16px;
|
|
1993
2078
|
display: flex;
|
|
1994
2079
|
align-items: center;
|
|
1995
2080
|
gap: 6px;
|
|
@@ -2157,6 +2242,61 @@
|
|
|
2157
2242
|
}
|
|
2158
2243
|
}
|
|
2159
2244
|
`;
|
|
2245
|
+
__decorate$5([
|
|
2246
|
+
n({ type: String })
|
|
2247
|
+
], exports.Scaffold.prototype, "logo", void 0);
|
|
2248
|
+
__decorate$5([
|
|
2249
|
+
n({ type: String })
|
|
2250
|
+
], exports.Scaffold.prototype, "label", void 0);
|
|
2251
|
+
__decorate$5([
|
|
2252
|
+
n({
|
|
2253
|
+
type: String,
|
|
2254
|
+
attribute: 'home-url'
|
|
2255
|
+
})
|
|
2256
|
+
], exports.Scaffold.prototype, "homeUrl", void 0);
|
|
2257
|
+
__decorate$5([
|
|
2258
|
+
n({
|
|
2259
|
+
type: String,
|
|
2260
|
+
reflect: true
|
|
2261
|
+
})
|
|
2262
|
+
], exports.Scaffold.prototype, "scheme", void 0);
|
|
2263
|
+
__decorate$5([
|
|
2264
|
+
n({ type: Array })
|
|
2265
|
+
], exports.Scaffold.prototype, "nav", void 0);
|
|
2266
|
+
__decorate$5([
|
|
2267
|
+
n({
|
|
2268
|
+
type: Boolean,
|
|
2269
|
+
attribute: 'nav-icons-displayed',
|
|
2270
|
+
reflect: true
|
|
2271
|
+
})
|
|
2272
|
+
], exports.Scaffold.prototype, "navIconsDisplayed", void 0);
|
|
2273
|
+
__decorate$5([
|
|
2274
|
+
n({
|
|
2275
|
+
type: Boolean,
|
|
2276
|
+
attribute: 'nav-expanded'
|
|
2277
|
+
})
|
|
2278
|
+
], exports.Scaffold.prototype, "navExpanded", void 0);
|
|
2279
|
+
__decorate$5([
|
|
2280
|
+
n({ type: Object })
|
|
2281
|
+
], exports.Scaffold.prototype, "user", void 0);
|
|
2282
|
+
__decorate$5([
|
|
2283
|
+
n({ type: Boolean })
|
|
2284
|
+
], exports.Scaffold.prototype, "subbar", void 0);
|
|
2285
|
+
__decorate$5([
|
|
2286
|
+
n({
|
|
2287
|
+
type: Boolean,
|
|
2288
|
+
attribute: 'nav-enabled'
|
|
2289
|
+
})
|
|
2290
|
+
], exports.Scaffold.prototype, "navEnabled", void 0);
|
|
2291
|
+
__decorate$5([
|
|
2292
|
+
n({
|
|
2293
|
+
type: String,
|
|
2294
|
+
attribute: 'user-type'
|
|
2295
|
+
})
|
|
2296
|
+
], exports.Scaffold.prototype, "userType", void 0);
|
|
2297
|
+
__decorate$5([
|
|
2298
|
+
n({ type: Array })
|
|
2299
|
+
], exports.Scaffold.prototype, "breadcrumbs", void 0);
|
|
2160
2300
|
__decorate$5([
|
|
2161
2301
|
r()
|
|
2162
2302
|
], exports.Scaffold.prototype, "navItemsExpanded", void 0);
|
|
@@ -2193,42 +2333,6 @@
|
|
|
2193
2333
|
__decorate$5([
|
|
2194
2334
|
r()
|
|
2195
2335
|
], exports.Scaffold.prototype, "pendingRequests", void 0);
|
|
2196
|
-
__decorate$5([
|
|
2197
|
-
n({ type: String })
|
|
2198
|
-
], exports.Scaffold.prototype, "logo", void 0);
|
|
2199
|
-
__decorate$5([
|
|
2200
|
-
n({ type: String })
|
|
2201
|
-
], exports.Scaffold.prototype, "label", void 0);
|
|
2202
|
-
__decorate$5([
|
|
2203
|
-
n({ type: String, attribute: 'home-url' })
|
|
2204
|
-
], exports.Scaffold.prototype, "homeUrl", void 0);
|
|
2205
|
-
__decorate$5([
|
|
2206
|
-
n({ type: String, reflect: true })
|
|
2207
|
-
], exports.Scaffold.prototype, "scheme", void 0);
|
|
2208
|
-
__decorate$5([
|
|
2209
|
-
n({ type: Array })
|
|
2210
|
-
], exports.Scaffold.prototype, "nav", void 0);
|
|
2211
|
-
__decorate$5([
|
|
2212
|
-
n({ type: Boolean, attribute: 'nav-icons-displayed', reflect: true })
|
|
2213
|
-
], exports.Scaffold.prototype, "navIconsDisplayed", void 0);
|
|
2214
|
-
__decorate$5([
|
|
2215
|
-
n({ type: Boolean, attribute: 'nav-expanded' })
|
|
2216
|
-
], exports.Scaffold.prototype, "navExpanded", void 0);
|
|
2217
|
-
__decorate$5([
|
|
2218
|
-
n({ type: Object })
|
|
2219
|
-
], exports.Scaffold.prototype, "user", void 0);
|
|
2220
|
-
__decorate$5([
|
|
2221
|
-
n({ type: Boolean })
|
|
2222
|
-
], exports.Scaffold.prototype, "subbar", void 0);
|
|
2223
|
-
__decorate$5([
|
|
2224
|
-
n({ type: Boolean, attribute: 'nav-enabled' })
|
|
2225
|
-
], exports.Scaffold.prototype, "navEnabled", void 0);
|
|
2226
|
-
__decorate$5([
|
|
2227
|
-
n({ type: String, attribute: 'user-type' })
|
|
2228
|
-
], exports.Scaffold.prototype, "userType", void 0);
|
|
2229
|
-
__decorate$5([
|
|
2230
|
-
n({ type: Array })
|
|
2231
|
-
], exports.Scaffold.prototype, "breadcrumbs", void 0);
|
|
2232
2336
|
exports.Scaffold = __decorate$5([
|
|
2233
2337
|
t$1('kr-scaffold')
|
|
2234
2338
|
], exports.Scaffold);
|
|
@@ -2249,7 +2353,7 @@
|
|
|
2249
2353
|
* <kr-screen-nav
|
|
2250
2354
|
* label="Acme Corporation"
|
|
2251
2355
|
* subLabel="Company"
|
|
2252
|
-
* .
|
|
2356
|
+
* .items=${[
|
|
2253
2357
|
* { id: 'overview', label: 'Overview', url: '/companies/123/overview' },
|
|
2254
2358
|
* { id: 'contacts', label: 'Contacts', url: '/companies/123/contacts' },
|
|
2255
2359
|
* { id: 'activity', label: 'Activity', url: '/companies/123/activity' },
|
|
@@ -2264,7 +2368,7 @@
|
|
|
2264
2368
|
*
|
|
2265
2369
|
* @property {string} label - Main label (e.g., entity name like "Acme Corporation")
|
|
2266
2370
|
* @property {string} subLabel - Sub label (e.g., entity type like "Company")
|
|
2267
|
-
* @property {KRScreenNavItem[]}
|
|
2371
|
+
* @property {KRScreenNavItem[]} items - Navigation items as JSON array
|
|
2268
2372
|
* @property {string} activeId - Currently active item ID
|
|
2269
2373
|
*/
|
|
2270
2374
|
exports.ScreenNav = class KRScreenNav extends i$1 {
|
|
@@ -2281,7 +2385,7 @@
|
|
|
2281
2385
|
/**
|
|
2282
2386
|
* Navigation items
|
|
2283
2387
|
*/
|
|
2284
|
-
this.
|
|
2388
|
+
this.items = [];
|
|
2285
2389
|
/**
|
|
2286
2390
|
* Currently active item ID. Auto-detected from the URL by default.
|
|
2287
2391
|
* Can be set explicitly by the parent to override.
|
|
@@ -2293,7 +2397,7 @@
|
|
|
2293
2397
|
}
|
|
2294
2398
|
connectedCallback() {
|
|
2295
2399
|
super.connectedCallback();
|
|
2296
|
-
// Defer so property bindings (.
|
|
2400
|
+
// Defer so property bindings (.items) have been applied
|
|
2297
2401
|
requestAnimationFrame(() => this.syncActiveFromUrl());
|
|
2298
2402
|
window.addEventListener('popstate', this.handlePopstate);
|
|
2299
2403
|
}
|
|
@@ -2332,7 +2436,7 @@
|
|
|
2332
2436
|
}
|
|
2333
2437
|
syncActiveFromUrl() {
|
|
2334
2438
|
const pathname = window.location.pathname;
|
|
2335
|
-
const match = this.
|
|
2439
|
+
const match = this.items.find((item) => {
|
|
2336
2440
|
if (!item.url) {
|
|
2337
2441
|
return false;
|
|
2338
2442
|
}
|
|
@@ -2360,9 +2464,12 @@
|
|
|
2360
2464
|
` : A}
|
|
2361
2465
|
<div class="nav-content">
|
|
2362
2466
|
<div class="nav-items">
|
|
2363
|
-
${this.
|
|
2467
|
+
${this.items.map((item) => b `
|
|
2364
2468
|
<a
|
|
2365
|
-
class
|
|
2469
|
+
class=${e({
|
|
2470
|
+
'nav-item': true,
|
|
2471
|
+
'nav-item--active': item.id === this.activeId
|
|
2472
|
+
})}
|
|
2366
2473
|
href=${item.url || '#'}
|
|
2367
2474
|
@click=${(e) => this.handleNavItemClick(e, item)}
|
|
2368
2475
|
>
|
|
@@ -2425,7 +2532,7 @@
|
|
|
2425
2532
|
.nav-content {
|
|
2426
2533
|
flex: 1;
|
|
2427
2534
|
overflow-y: auto;
|
|
2428
|
-
padding: 0 0
|
|
2535
|
+
padding: 0 0 12px;
|
|
2429
2536
|
}
|
|
2430
2537
|
|
|
2431
2538
|
.nav-items {
|
|
@@ -2439,7 +2546,7 @@
|
|
|
2439
2546
|
padding: 0 12px;
|
|
2440
2547
|
height: 32px;
|
|
2441
2548
|
line-height: 32px;
|
|
2442
|
-
letter-spacing: 0.
|
|
2549
|
+
letter-spacing: 0.13px;
|
|
2443
2550
|
color: rgb(32, 33, 36);
|
|
2444
2551
|
text-decoration: none;
|
|
2445
2552
|
font-size: 13px;
|
|
@@ -2480,7 +2587,7 @@
|
|
|
2480
2587
|
], exports.ScreenNav.prototype, "subLabel", void 0);
|
|
2481
2588
|
__decorate$4([
|
|
2482
2589
|
n({ type: Array })
|
|
2483
|
-
], exports.ScreenNav.prototype, "
|
|
2590
|
+
], exports.ScreenNav.prototype, "items", void 0);
|
|
2484
2591
|
__decorate$4([
|
|
2485
2592
|
n({ type: String })
|
|
2486
2593
|
], exports.ScreenNav.prototype, "activeId", void 0);
|
|
@@ -2550,7 +2657,7 @@
|
|
|
2550
2657
|
display: flex;
|
|
2551
2658
|
align-items: center;
|
|
2552
2659
|
justify-content: space-between;
|
|
2553
|
-
padding: 0
|
|
2660
|
+
padding: 0 24px;
|
|
2554
2661
|
background: #ffffff;
|
|
2555
2662
|
border-bottom: 1px solid #e5e7eb;
|
|
2556
2663
|
flex-shrink: 0;
|
|
@@ -2572,7 +2679,7 @@
|
|
|
2572
2679
|
.content {
|
|
2573
2680
|
flex: 1;
|
|
2574
2681
|
overflow-y: auto;
|
|
2575
|
-
padding:
|
|
2682
|
+
padding: 24px;
|
|
2576
2683
|
display: flex;
|
|
2577
2684
|
justify-content: center;
|
|
2578
2685
|
}
|
|
@@ -2613,7 +2720,10 @@
|
|
|
2613
2720
|
this.menu = false;
|
|
2614
2721
|
}
|
|
2615
2722
|
_handleMenuClick() {
|
|
2616
|
-
this.dispatchEvent(new CustomEvent('menu-click', {
|
|
2723
|
+
this.dispatchEvent(new CustomEvent('menu-click', {
|
|
2724
|
+
bubbles: true,
|
|
2725
|
+
composed: true
|
|
2726
|
+
}));
|
|
2617
2727
|
}
|
|
2618
2728
|
/**
|
|
2619
2729
|
* Resolves a breadcrumb URL by prepending the base href.
|
|
@@ -2630,13 +2740,19 @@
|
|
|
2630
2740
|
* - resolveUrl(crumb) = "/operations/settings" (used for href)
|
|
2631
2741
|
*/
|
|
2632
2742
|
resolveUrl(crumb) {
|
|
2633
|
-
if (!crumb.url)
|
|
2743
|
+
if (!crumb.url) {
|
|
2634
2744
|
return '#';
|
|
2745
|
+
}
|
|
2635
2746
|
// Remove leading slash from url if present, then combine with base
|
|
2636
|
-
|
|
2747
|
+
let path = crumb.url;
|
|
2748
|
+
if (crumb.url.startsWith('/')) {
|
|
2749
|
+
path = crumb.url.slice(1);
|
|
2750
|
+
}
|
|
2637
2751
|
const baseHref = document.querySelector('base')?.getAttribute('href') || '/';
|
|
2638
|
-
|
|
2639
|
-
|
|
2752
|
+
if (baseHref.endsWith('/')) {
|
|
2753
|
+
return baseHref + path;
|
|
2754
|
+
}
|
|
2755
|
+
return baseHref + '/' + path;
|
|
2640
2756
|
}
|
|
2641
2757
|
/**
|
|
2642
2758
|
* Handles click on a breadcrumb link.
|
|
@@ -2668,10 +2784,31 @@
|
|
|
2668
2784
|
return b `
|
|
2669
2785
|
${this.menu ? b `
|
|
2670
2786
|
<div class="menu" @click=${this._handleMenuClick}>
|
|
2671
|
-
<svg
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2787
|
+
<svg
|
|
2788
|
+
class="menu__icon"
|
|
2789
|
+
viewBox="0 0 24 24"
|
|
2790
|
+
fill="none"
|
|
2791
|
+
stroke="currentColor"
|
|
2792
|
+
stroke-width="2"
|
|
2793
|
+
>
|
|
2794
|
+
<line
|
|
2795
|
+
x1="3"
|
|
2796
|
+
y1="6"
|
|
2797
|
+
x2="21"
|
|
2798
|
+
y2="6"
|
|
2799
|
+
></line>
|
|
2800
|
+
<line
|
|
2801
|
+
x1="3"
|
|
2802
|
+
y1="12"
|
|
2803
|
+
x2="21"
|
|
2804
|
+
y2="12"
|
|
2805
|
+
></line>
|
|
2806
|
+
<line
|
|
2807
|
+
x1="3"
|
|
2808
|
+
y1="18"
|
|
2809
|
+
x2="21"
|
|
2810
|
+
y2="18"
|
|
2811
|
+
></line>
|
|
2675
2812
|
</svg>
|
|
2676
2813
|
</div>
|
|
2677
2814
|
` : A}
|
|
@@ -2681,7 +2818,10 @@
|
|
|
2681
2818
|
return b `
|
|
2682
2819
|
${index > 0 ? b `<span class="breadcrumb-separator">/</span>` : ''}
|
|
2683
2820
|
<a
|
|
2684
|
-
class=${e({
|
|
2821
|
+
class=${e({
|
|
2822
|
+
'breadcrumb': true,
|
|
2823
|
+
'breadcrumb--current': isCurrent
|
|
2824
|
+
})}
|
|
2685
2825
|
href=${this.resolveUrl(crumb)}
|
|
2686
2826
|
@click=${(e) => this._handleBreadcrumbClick(e, crumb, isCurrent)}
|
|
2687
2827
|
>${crumb.label}</a>
|
|
@@ -2850,7 +2990,10 @@
|
|
|
2850
2990
|
for (const route of routes) {
|
|
2851
2991
|
const routeSegments = route.path.split('/').filter(s => s);
|
|
2852
2992
|
if (routeSegments.length === 0 && segments.length === 0) {
|
|
2853
|
-
return [{
|
|
2993
|
+
return [{
|
|
2994
|
+
route,
|
|
2995
|
+
params: {}
|
|
2996
|
+
}];
|
|
2854
2997
|
}
|
|
2855
2998
|
if (routeSegments.length === 0) {
|
|
2856
2999
|
continue;
|
|
@@ -2877,7 +3020,10 @@
|
|
|
2877
3020
|
if (remaining.length > 0 && !route.children) {
|
|
2878
3021
|
continue;
|
|
2879
3022
|
}
|
|
2880
|
-
const result = [{
|
|
3023
|
+
const result = [{
|
|
3024
|
+
route,
|
|
3025
|
+
params
|
|
3026
|
+
}];
|
|
2881
3027
|
if (route.children && remaining.length > 0) {
|
|
2882
3028
|
const childMatches = KRRouter.matchRoutes(route.children, remaining);
|
|
2883
3029
|
if (childMatches.length > 0) {
|
|
@@ -2957,99 +3103,2530 @@
|
|
|
2957
3103
|
t$1('kr-router-outlet')
|
|
2958
3104
|
], exports.RouterOutlet);
|
|
2959
3105
|
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
3106
|
+
/**
|
|
3107
|
+
* marked v12.0.2 - a markdown parser
|
|
3108
|
+
* Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed)
|
|
3109
|
+
* https://github.com/markedjs/marked
|
|
3110
|
+
*/
|
|
3111
|
+
|
|
3112
|
+
/**
|
|
3113
|
+
* DO NOT EDIT THIS FILE
|
|
3114
|
+
* The code in this file is generated from files in ./src/
|
|
3115
|
+
*/
|
|
3116
|
+
|
|
3117
|
+
/**
|
|
3118
|
+
* Gets the original marked default options.
|
|
3119
|
+
*/
|
|
3120
|
+
function _getDefaults() {
|
|
3121
|
+
return {
|
|
3122
|
+
async: false,
|
|
3123
|
+
breaks: false,
|
|
3124
|
+
extensions: null,
|
|
3125
|
+
gfm: true,
|
|
3126
|
+
hooks: null,
|
|
3127
|
+
pedantic: false,
|
|
3128
|
+
renderer: null,
|
|
3129
|
+
silent: false,
|
|
3130
|
+
tokenizer: null,
|
|
3131
|
+
walkTokens: null
|
|
3132
|
+
};
|
|
3133
|
+
}
|
|
3134
|
+
let _defaults = _getDefaults();
|
|
3135
|
+
function changeDefaults(newDefaults) {
|
|
3136
|
+
_defaults = newDefaults;
|
|
3137
|
+
}
|
|
3138
|
+
|
|
3139
|
+
/**
|
|
3140
|
+
* Helpers
|
|
3141
|
+
*/
|
|
3142
|
+
const escapeTest = /[&<>"']/;
|
|
3143
|
+
const escapeReplace = new RegExp(escapeTest.source, 'g');
|
|
3144
|
+
const escapeTestNoEncode = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/;
|
|
3145
|
+
const escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, 'g');
|
|
3146
|
+
const escapeReplacements = {
|
|
3147
|
+
'&': '&',
|
|
3148
|
+
'<': '<',
|
|
3149
|
+
'>': '>',
|
|
3150
|
+
'"': '"',
|
|
3151
|
+
"'": '''
|
|
2965
3152
|
};
|
|
3153
|
+
const getEscapeReplacement = (ch) => escapeReplacements[ch];
|
|
3154
|
+
function escape$1(html, encode) {
|
|
3155
|
+
if (encode) {
|
|
3156
|
+
if (escapeTest.test(html)) {
|
|
3157
|
+
return html.replace(escapeReplace, getEscapeReplacement);
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
else {
|
|
3161
|
+
if (escapeTestNoEncode.test(html)) {
|
|
3162
|
+
return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
return html;
|
|
3166
|
+
}
|
|
3167
|
+
const unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
|
|
3168
|
+
function unescape(html) {
|
|
3169
|
+
// explicitly match decimal, hex, and named HTML entities
|
|
3170
|
+
return html.replace(unescapeTest, (_, n) => {
|
|
3171
|
+
n = n.toLowerCase();
|
|
3172
|
+
if (n === 'colon')
|
|
3173
|
+
return ':';
|
|
3174
|
+
if (n.charAt(0) === '#') {
|
|
3175
|
+
return n.charAt(1) === 'x'
|
|
3176
|
+
? String.fromCharCode(parseInt(n.substring(2), 16))
|
|
3177
|
+
: String.fromCharCode(+n.substring(1));
|
|
3178
|
+
}
|
|
3179
|
+
return '';
|
|
3180
|
+
});
|
|
3181
|
+
}
|
|
3182
|
+
const caret = /(^|[^\[])\^/g;
|
|
3183
|
+
function edit(regex, opt) {
|
|
3184
|
+
let source = typeof regex === 'string' ? regex : regex.source;
|
|
3185
|
+
opt = opt || '';
|
|
3186
|
+
const obj = {
|
|
3187
|
+
replace: (name, val) => {
|
|
3188
|
+
let valSource = typeof val === 'string' ? val : val.source;
|
|
3189
|
+
valSource = valSource.replace(caret, '$1');
|
|
3190
|
+
source = source.replace(name, valSource);
|
|
3191
|
+
return obj;
|
|
3192
|
+
},
|
|
3193
|
+
getRegex: () => {
|
|
3194
|
+
return new RegExp(source, opt);
|
|
3195
|
+
}
|
|
3196
|
+
};
|
|
3197
|
+
return obj;
|
|
3198
|
+
}
|
|
3199
|
+
function cleanUrl(href) {
|
|
3200
|
+
try {
|
|
3201
|
+
href = encodeURI(href).replace(/%25/g, '%');
|
|
3202
|
+
}
|
|
3203
|
+
catch (e) {
|
|
3204
|
+
return null;
|
|
3205
|
+
}
|
|
3206
|
+
return href;
|
|
3207
|
+
}
|
|
3208
|
+
const noopTest = { exec: () => null };
|
|
3209
|
+
function splitCells(tableRow, count) {
|
|
3210
|
+
// ensure that every cell-delimiting pipe has a space
|
|
3211
|
+
// before it to distinguish it from an escaped pipe
|
|
3212
|
+
const row = tableRow.replace(/\|/g, (match, offset, str) => {
|
|
3213
|
+
let escaped = false;
|
|
3214
|
+
let curr = offset;
|
|
3215
|
+
while (--curr >= 0 && str[curr] === '\\')
|
|
3216
|
+
escaped = !escaped;
|
|
3217
|
+
if (escaped) {
|
|
3218
|
+
// odd number of slashes means | is escaped
|
|
3219
|
+
// so we leave it alone
|
|
3220
|
+
return '|';
|
|
3221
|
+
}
|
|
3222
|
+
else {
|
|
3223
|
+
// add space before unescaped |
|
|
3224
|
+
return ' |';
|
|
3225
|
+
}
|
|
3226
|
+
}), cells = row.split(/ \|/);
|
|
3227
|
+
let i = 0;
|
|
3228
|
+
// First/last cell in a row cannot be empty if it has no leading/trailing pipe
|
|
3229
|
+
if (!cells[0].trim()) {
|
|
3230
|
+
cells.shift();
|
|
3231
|
+
}
|
|
3232
|
+
if (cells.length > 0 && !cells[cells.length - 1].trim()) {
|
|
3233
|
+
cells.pop();
|
|
3234
|
+
}
|
|
3235
|
+
if (count) {
|
|
3236
|
+
if (cells.length > count) {
|
|
3237
|
+
cells.splice(count);
|
|
3238
|
+
}
|
|
3239
|
+
else {
|
|
3240
|
+
while (cells.length < count)
|
|
3241
|
+
cells.push('');
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
for (; i < cells.length; i++) {
|
|
3245
|
+
// leading or trailing whitespace is ignored per the gfm spec
|
|
3246
|
+
cells[i] = cells[i].trim().replace(/\\\|/g, '|');
|
|
3247
|
+
}
|
|
3248
|
+
return cells;
|
|
3249
|
+
}
|
|
2966
3250
|
/**
|
|
2967
|
-
*
|
|
2968
|
-
*
|
|
2969
|
-
* Accepts a `send` callback that returns a streaming Response (SSE format).
|
|
2970
|
-
* Parses the stream for text tokens, status updates, errors, and frontend actions.
|
|
2971
|
-
* When an ACTION event arrives, dispatches a cancelable `action` custom event.
|
|
2972
|
-
* If not prevented, executes the native browser action (reload, navigate, etc.).
|
|
2973
|
-
*
|
|
2974
|
-
* ## SSE Stream Format
|
|
2975
|
-
* The component expects `data:` lines with JSON payloads containing a `type` field:
|
|
2976
|
-
* - `STATUS` — `{ type: "STATUS", message: "Thinking..." }`
|
|
2977
|
-
* - `RESPONSE_PART` — `{ type: "RESPONSE_PART", message: "token text" }`
|
|
2978
|
-
* - `ACTION` — `{ type: "ACTION", action: "RELOAD" }` (see KRChatbotAction)
|
|
2979
|
-
* - `ERROR` — `{ type: "ERROR", message: "Something went wrong" }`
|
|
2980
|
-
* - `HEARTBEAT` — `{ type: "HEARTBEAT" }` (ignored)
|
|
2981
|
-
*
|
|
2982
|
-
* The final event should include `finished: true` to signal stream completion.
|
|
2983
|
-
*
|
|
2984
|
-
* ## Usage
|
|
2985
|
-
* ```html
|
|
2986
|
-
* <kr-chatbot
|
|
2987
|
-
* title="AI Assistant"
|
|
2988
|
-
* .send=${(params) => {
|
|
2989
|
-
* return fetch('/api/ai/chat/stream', {
|
|
2990
|
-
* method: 'POST',
|
|
2991
|
-
* headers: { 'Content-Type': 'application/json', 'Accept': 'text/event-stream' },
|
|
2992
|
-
* credentials: 'include',
|
|
2993
|
-
* body: JSON.stringify({ userText: params.message })
|
|
2994
|
-
* });
|
|
2995
|
-
* }}
|
|
2996
|
-
* ></kr-chatbot>
|
|
2997
|
-
* ```
|
|
2998
|
-
*
|
|
2999
|
-
* @slot - Optional slot content displayed above the messages area
|
|
3251
|
+
* Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
|
|
3252
|
+
* /c*$/ is vulnerable to REDOS.
|
|
3000
3253
|
*
|
|
3001
|
-
* @
|
|
3002
|
-
* @
|
|
3254
|
+
* @param str
|
|
3255
|
+
* @param c
|
|
3256
|
+
* @param invert Remove suffix of non-c chars instead. Default falsey.
|
|
3003
3257
|
*/
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3258
|
+
function rtrim(str, c, invert) {
|
|
3259
|
+
const l = str.length;
|
|
3260
|
+
if (l === 0) {
|
|
3261
|
+
return '';
|
|
3262
|
+
}
|
|
3263
|
+
// Length of suffix matching the invert condition.
|
|
3264
|
+
let suffLen = 0;
|
|
3265
|
+
// Step left until we fail to match the invert condition.
|
|
3266
|
+
while (suffLen < l) {
|
|
3267
|
+
const currChar = str.charAt(l - suffLen - 1);
|
|
3268
|
+
if (currChar === c && true) {
|
|
3269
|
+
suffLen++;
|
|
3270
|
+
}
|
|
3271
|
+
else {
|
|
3272
|
+
break;
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
return str.slice(0, l - suffLen);
|
|
3276
|
+
}
|
|
3277
|
+
function findClosingBracket(str, b) {
|
|
3278
|
+
if (str.indexOf(b[1]) === -1) {
|
|
3279
|
+
return -1;
|
|
3280
|
+
}
|
|
3281
|
+
let level = 0;
|
|
3282
|
+
for (let i = 0; i < str.length; i++) {
|
|
3283
|
+
if (str[i] === '\\') {
|
|
3284
|
+
i++;
|
|
3285
|
+
}
|
|
3286
|
+
else if (str[i] === b[0]) {
|
|
3287
|
+
level++;
|
|
3288
|
+
}
|
|
3289
|
+
else if (str[i] === b[1]) {
|
|
3290
|
+
level--;
|
|
3291
|
+
if (level < 0) {
|
|
3292
|
+
return i;
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
3296
|
+
return -1;
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
function outputLink(cap, link, raw, lexer) {
|
|
3300
|
+
const href = link.href;
|
|
3301
|
+
const title = link.title ? escape$1(link.title) : null;
|
|
3302
|
+
const text = cap[1].replace(/\\([\[\]])/g, '$1');
|
|
3303
|
+
if (cap[0].charAt(0) !== '!') {
|
|
3304
|
+
lexer.state.inLink = true;
|
|
3305
|
+
const token = {
|
|
3306
|
+
type: 'link',
|
|
3307
|
+
raw,
|
|
3308
|
+
href,
|
|
3309
|
+
title,
|
|
3310
|
+
text,
|
|
3311
|
+
tokens: lexer.inlineTokens(text)
|
|
3312
|
+
};
|
|
3313
|
+
lexer.state.inLink = false;
|
|
3314
|
+
return token;
|
|
3315
|
+
}
|
|
3316
|
+
return {
|
|
3317
|
+
type: 'image',
|
|
3318
|
+
raw,
|
|
3319
|
+
href,
|
|
3320
|
+
title,
|
|
3321
|
+
text: escape$1(text)
|
|
3322
|
+
};
|
|
3323
|
+
}
|
|
3324
|
+
function indentCodeCompensation(raw, text) {
|
|
3325
|
+
const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
|
|
3326
|
+
if (matchIndentToCode === null) {
|
|
3327
|
+
return text;
|
|
3328
|
+
}
|
|
3329
|
+
const indentToCode = matchIndentToCode[1];
|
|
3330
|
+
return text
|
|
3331
|
+
.split('\n')
|
|
3332
|
+
.map(node => {
|
|
3333
|
+
const matchIndentInNode = node.match(/^\s+/);
|
|
3334
|
+
if (matchIndentInNode === null) {
|
|
3335
|
+
return node;
|
|
3336
|
+
}
|
|
3337
|
+
const [indentInNode] = matchIndentInNode;
|
|
3338
|
+
if (indentInNode.length >= indentToCode.length) {
|
|
3339
|
+
return node.slice(indentToCode.length);
|
|
3340
|
+
}
|
|
3341
|
+
return node;
|
|
3342
|
+
})
|
|
3343
|
+
.join('\n');
|
|
3344
|
+
}
|
|
3345
|
+
/**
|
|
3346
|
+
* Tokenizer
|
|
3347
|
+
*/
|
|
3348
|
+
class _Tokenizer {
|
|
3349
|
+
options;
|
|
3350
|
+
rules; // set by the lexer
|
|
3351
|
+
lexer; // set by the lexer
|
|
3352
|
+
constructor(options) {
|
|
3353
|
+
this.options = options || _defaults;
|
|
3354
|
+
}
|
|
3355
|
+
space(src) {
|
|
3356
|
+
const cap = this.rules.block.newline.exec(src);
|
|
3357
|
+
if (cap && cap[0].length > 0) {
|
|
3358
|
+
return {
|
|
3359
|
+
type: 'space',
|
|
3360
|
+
raw: cap[0]
|
|
3361
|
+
};
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
code(src) {
|
|
3365
|
+
const cap = this.rules.block.code.exec(src);
|
|
3366
|
+
if (cap) {
|
|
3367
|
+
const text = cap[0].replace(/^ {1,4}/gm, '');
|
|
3368
|
+
return {
|
|
3369
|
+
type: 'code',
|
|
3370
|
+
raw: cap[0],
|
|
3371
|
+
codeBlockStyle: 'indented',
|
|
3372
|
+
text: !this.options.pedantic
|
|
3373
|
+
? rtrim(text, '\n')
|
|
3374
|
+
: text
|
|
3375
|
+
};
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
fences(src) {
|
|
3379
|
+
const cap = this.rules.block.fences.exec(src);
|
|
3380
|
+
if (cap) {
|
|
3381
|
+
const raw = cap[0];
|
|
3382
|
+
const text = indentCodeCompensation(raw, cap[3] || '');
|
|
3383
|
+
return {
|
|
3384
|
+
type: 'code',
|
|
3385
|
+
raw,
|
|
3386
|
+
lang: cap[2] ? cap[2].trim().replace(this.rules.inline.anyPunctuation, '$1') : cap[2],
|
|
3387
|
+
text
|
|
3388
|
+
};
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
heading(src) {
|
|
3392
|
+
const cap = this.rules.block.heading.exec(src);
|
|
3393
|
+
if (cap) {
|
|
3394
|
+
let text = cap[2].trim();
|
|
3395
|
+
// remove trailing #s
|
|
3396
|
+
if (/#$/.test(text)) {
|
|
3397
|
+
const trimmed = rtrim(text, '#');
|
|
3398
|
+
if (this.options.pedantic) {
|
|
3399
|
+
text = trimmed.trim();
|
|
3400
|
+
}
|
|
3401
|
+
else if (!trimmed || / $/.test(trimmed)) {
|
|
3402
|
+
// CommonMark requires space before trailing #s
|
|
3403
|
+
text = trimmed.trim();
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
return {
|
|
3407
|
+
type: 'heading',
|
|
3408
|
+
raw: cap[0],
|
|
3409
|
+
depth: cap[1].length,
|
|
3410
|
+
text,
|
|
3411
|
+
tokens: this.lexer.inline(text)
|
|
3412
|
+
};
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
hr(src) {
|
|
3416
|
+
const cap = this.rules.block.hr.exec(src);
|
|
3417
|
+
if (cap) {
|
|
3418
|
+
return {
|
|
3419
|
+
type: 'hr',
|
|
3420
|
+
raw: cap[0]
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
blockquote(src) {
|
|
3425
|
+
const cap = this.rules.block.blockquote.exec(src);
|
|
3426
|
+
if (cap) {
|
|
3427
|
+
// precede setext continuation with 4 spaces so it isn't a setext
|
|
3428
|
+
let text = cap[0].replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g, '\n $1');
|
|
3429
|
+
text = rtrim(text.replace(/^ *>[ \t]?/gm, ''), '\n');
|
|
3430
|
+
const top = this.lexer.state.top;
|
|
3431
|
+
this.lexer.state.top = true;
|
|
3432
|
+
const tokens = this.lexer.blockTokens(text);
|
|
3433
|
+
this.lexer.state.top = top;
|
|
3434
|
+
return {
|
|
3435
|
+
type: 'blockquote',
|
|
3436
|
+
raw: cap[0],
|
|
3437
|
+
tokens,
|
|
3438
|
+
text
|
|
3439
|
+
};
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
list(src) {
|
|
3443
|
+
let cap = this.rules.block.list.exec(src);
|
|
3444
|
+
if (cap) {
|
|
3445
|
+
let bull = cap[1].trim();
|
|
3446
|
+
const isordered = bull.length > 1;
|
|
3447
|
+
const list = {
|
|
3448
|
+
type: 'list',
|
|
3449
|
+
raw: '',
|
|
3450
|
+
ordered: isordered,
|
|
3451
|
+
start: isordered ? +bull.slice(0, -1) : '',
|
|
3452
|
+
loose: false,
|
|
3453
|
+
items: []
|
|
3454
|
+
};
|
|
3455
|
+
bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`;
|
|
3456
|
+
if (this.options.pedantic) {
|
|
3457
|
+
bull = isordered ? bull : '[*+-]';
|
|
3458
|
+
}
|
|
3459
|
+
// Get next list item
|
|
3460
|
+
const itemRegex = new RegExp(`^( {0,3}${bull})((?:[\t ][^\\n]*)?(?:\\n|$))`);
|
|
3461
|
+
let raw = '';
|
|
3462
|
+
let itemContents = '';
|
|
3463
|
+
let endsWithBlankLine = false;
|
|
3464
|
+
// Check if current bullet point can start a new List Item
|
|
3465
|
+
while (src) {
|
|
3466
|
+
let endEarly = false;
|
|
3467
|
+
if (!(cap = itemRegex.exec(src))) {
|
|
3468
|
+
break;
|
|
3469
|
+
}
|
|
3470
|
+
if (this.rules.block.hr.test(src)) { // End list if bullet was actually HR (possibly move into itemRegex?)
|
|
3471
|
+
break;
|
|
3472
|
+
}
|
|
3473
|
+
raw = cap[0];
|
|
3474
|
+
src = src.substring(raw.length);
|
|
3475
|
+
let line = cap[2].split('\n', 1)[0].replace(/^\t+/, (t) => ' '.repeat(3 * t.length));
|
|
3476
|
+
let nextLine = src.split('\n', 1)[0];
|
|
3477
|
+
let indent = 0;
|
|
3478
|
+
if (this.options.pedantic) {
|
|
3479
|
+
indent = 2;
|
|
3480
|
+
itemContents = line.trimStart();
|
|
3481
|
+
}
|
|
3482
|
+
else {
|
|
3483
|
+
indent = cap[2].search(/[^ ]/); // Find first non-space char
|
|
3484
|
+
indent = indent > 4 ? 1 : indent; // Treat indented code blocks (> 4 spaces) as having only 1 indent
|
|
3485
|
+
itemContents = line.slice(indent);
|
|
3486
|
+
indent += cap[1].length;
|
|
3487
|
+
}
|
|
3488
|
+
let blankLine = false;
|
|
3489
|
+
if (!line && /^ *$/.test(nextLine)) { // Items begin with at most one blank line
|
|
3490
|
+
raw += nextLine + '\n';
|
|
3491
|
+
src = src.substring(nextLine.length + 1);
|
|
3492
|
+
endEarly = true;
|
|
3493
|
+
}
|
|
3494
|
+
if (!endEarly) {
|
|
3495
|
+
const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`);
|
|
3496
|
+
const hrRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`);
|
|
3497
|
+
const fencesBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`);
|
|
3498
|
+
const headingBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`);
|
|
3499
|
+
// Check if following lines should be included in List Item
|
|
3500
|
+
while (src) {
|
|
3501
|
+
const rawLine = src.split('\n', 1)[0];
|
|
3502
|
+
nextLine = rawLine;
|
|
3503
|
+
// Re-align to follow commonmark nesting rules
|
|
3504
|
+
if (this.options.pedantic) {
|
|
3505
|
+
nextLine = nextLine.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' ');
|
|
3506
|
+
}
|
|
3507
|
+
// End list item if found code fences
|
|
3508
|
+
if (fencesBeginRegex.test(nextLine)) {
|
|
3509
|
+
break;
|
|
3510
|
+
}
|
|
3511
|
+
// End list item if found start of new heading
|
|
3512
|
+
if (headingBeginRegex.test(nextLine)) {
|
|
3513
|
+
break;
|
|
3514
|
+
}
|
|
3515
|
+
// End list item if found start of new bullet
|
|
3516
|
+
if (nextBulletRegex.test(nextLine)) {
|
|
3517
|
+
break;
|
|
3518
|
+
}
|
|
3519
|
+
// Horizontal rule found
|
|
3520
|
+
if (hrRegex.test(src)) {
|
|
3521
|
+
break;
|
|
3522
|
+
}
|
|
3523
|
+
if (nextLine.search(/[^ ]/) >= indent || !nextLine.trim()) { // Dedent if possible
|
|
3524
|
+
itemContents += '\n' + nextLine.slice(indent);
|
|
3525
|
+
}
|
|
3526
|
+
else {
|
|
3527
|
+
// not enough indentation
|
|
3528
|
+
if (blankLine) {
|
|
3529
|
+
break;
|
|
3530
|
+
}
|
|
3531
|
+
// paragraph continuation unless last line was a different block level element
|
|
3532
|
+
if (line.search(/[^ ]/) >= 4) { // indented code block
|
|
3533
|
+
break;
|
|
3534
|
+
}
|
|
3535
|
+
if (fencesBeginRegex.test(line)) {
|
|
3536
|
+
break;
|
|
3537
|
+
}
|
|
3538
|
+
if (headingBeginRegex.test(line)) {
|
|
3539
|
+
break;
|
|
3540
|
+
}
|
|
3541
|
+
if (hrRegex.test(line)) {
|
|
3542
|
+
break;
|
|
3543
|
+
}
|
|
3544
|
+
itemContents += '\n' + nextLine;
|
|
3545
|
+
}
|
|
3546
|
+
if (!blankLine && !nextLine.trim()) { // Check if current line is blank
|
|
3547
|
+
blankLine = true;
|
|
3548
|
+
}
|
|
3549
|
+
raw += rawLine + '\n';
|
|
3550
|
+
src = src.substring(rawLine.length + 1);
|
|
3551
|
+
line = nextLine.slice(indent);
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
if (!list.loose) {
|
|
3555
|
+
// If the previous item ended with a blank line, the list is loose
|
|
3556
|
+
if (endsWithBlankLine) {
|
|
3557
|
+
list.loose = true;
|
|
3558
|
+
}
|
|
3559
|
+
else if (/\n *\n *$/.test(raw)) {
|
|
3560
|
+
endsWithBlankLine = true;
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
let istask = null;
|
|
3564
|
+
let ischecked;
|
|
3565
|
+
// Check for task list items
|
|
3566
|
+
if (this.options.gfm) {
|
|
3567
|
+
istask = /^\[[ xX]\] /.exec(itemContents);
|
|
3568
|
+
if (istask) {
|
|
3569
|
+
ischecked = istask[0] !== '[ ] ';
|
|
3570
|
+
itemContents = itemContents.replace(/^\[[ xX]\] +/, '');
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
list.items.push({
|
|
3574
|
+
type: 'list_item',
|
|
3575
|
+
raw,
|
|
3576
|
+
task: !!istask,
|
|
3577
|
+
checked: ischecked,
|
|
3578
|
+
loose: false,
|
|
3579
|
+
text: itemContents,
|
|
3580
|
+
tokens: []
|
|
3581
|
+
});
|
|
3582
|
+
list.raw += raw;
|
|
3583
|
+
}
|
|
3584
|
+
// Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic
|
|
3585
|
+
list.items[list.items.length - 1].raw = raw.trimEnd();
|
|
3586
|
+
(list.items[list.items.length - 1]).text = itemContents.trimEnd();
|
|
3587
|
+
list.raw = list.raw.trimEnd();
|
|
3588
|
+
// Item child tokens handled here at end because we needed to have the final item to trim it first
|
|
3589
|
+
for (let i = 0; i < list.items.length; i++) {
|
|
3590
|
+
this.lexer.state.top = false;
|
|
3591
|
+
list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []);
|
|
3592
|
+
if (!list.loose) {
|
|
3593
|
+
// Check if list should be loose
|
|
3594
|
+
const spacers = list.items[i].tokens.filter(t => t.type === 'space');
|
|
3595
|
+
const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t => /\n.*\n/.test(t.raw));
|
|
3596
|
+
list.loose = hasMultipleLineBreaks;
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
// Set all items to loose if list is loose
|
|
3600
|
+
if (list.loose) {
|
|
3601
|
+
for (let i = 0; i < list.items.length; i++) {
|
|
3602
|
+
list.items[i].loose = true;
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
return list;
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
html(src) {
|
|
3609
|
+
const cap = this.rules.block.html.exec(src);
|
|
3610
|
+
if (cap) {
|
|
3611
|
+
const token = {
|
|
3612
|
+
type: 'html',
|
|
3613
|
+
block: true,
|
|
3614
|
+
raw: cap[0],
|
|
3615
|
+
pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
|
|
3616
|
+
text: cap[0]
|
|
3617
|
+
};
|
|
3618
|
+
return token;
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3621
|
+
def(src) {
|
|
3622
|
+
const cap = this.rules.block.def.exec(src);
|
|
3623
|
+
if (cap) {
|
|
3624
|
+
const tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
|
|
3625
|
+
const href = cap[2] ? cap[2].replace(/^<(.*)>$/, '$1').replace(this.rules.inline.anyPunctuation, '$1') : '';
|
|
3626
|
+
const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline.anyPunctuation, '$1') : cap[3];
|
|
3627
|
+
return {
|
|
3628
|
+
type: 'def',
|
|
3629
|
+
tag,
|
|
3630
|
+
raw: cap[0],
|
|
3631
|
+
href,
|
|
3632
|
+
title
|
|
3633
|
+
};
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
table(src) {
|
|
3637
|
+
const cap = this.rules.block.table.exec(src);
|
|
3638
|
+
if (!cap) {
|
|
3639
|
+
return;
|
|
3640
|
+
}
|
|
3641
|
+
if (!/[:|]/.test(cap[2])) {
|
|
3642
|
+
// delimiter row must have a pipe (|) or colon (:) otherwise it is a setext heading
|
|
3643
|
+
return;
|
|
3644
|
+
}
|
|
3645
|
+
const headers = splitCells(cap[1]);
|
|
3646
|
+
const aligns = cap[2].replace(/^\||\| *$/g, '').split('|');
|
|
3647
|
+
const rows = cap[3] && cap[3].trim() ? cap[3].replace(/\n[ \t]*$/, '').split('\n') : [];
|
|
3648
|
+
const item = {
|
|
3649
|
+
type: 'table',
|
|
3650
|
+
raw: cap[0],
|
|
3651
|
+
header: [],
|
|
3652
|
+
align: [],
|
|
3653
|
+
rows: []
|
|
3654
|
+
};
|
|
3655
|
+
if (headers.length !== aligns.length) {
|
|
3656
|
+
// header and align columns must be equal, rows can be different.
|
|
3657
|
+
return;
|
|
3658
|
+
}
|
|
3659
|
+
for (const align of aligns) {
|
|
3660
|
+
if (/^ *-+: *$/.test(align)) {
|
|
3661
|
+
item.align.push('right');
|
|
3662
|
+
}
|
|
3663
|
+
else if (/^ *:-+: *$/.test(align)) {
|
|
3664
|
+
item.align.push('center');
|
|
3665
|
+
}
|
|
3666
|
+
else if (/^ *:-+ *$/.test(align)) {
|
|
3667
|
+
item.align.push('left');
|
|
3668
|
+
}
|
|
3669
|
+
else {
|
|
3670
|
+
item.align.push(null);
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3673
|
+
for (const header of headers) {
|
|
3674
|
+
item.header.push({
|
|
3675
|
+
text: header,
|
|
3676
|
+
tokens: this.lexer.inline(header)
|
|
3677
|
+
});
|
|
3678
|
+
}
|
|
3679
|
+
for (const row of rows) {
|
|
3680
|
+
item.rows.push(splitCells(row, item.header.length).map(cell => {
|
|
3681
|
+
return {
|
|
3682
|
+
text: cell,
|
|
3683
|
+
tokens: this.lexer.inline(cell)
|
|
3684
|
+
};
|
|
3685
|
+
}));
|
|
3686
|
+
}
|
|
3687
|
+
return item;
|
|
3688
|
+
}
|
|
3689
|
+
lheading(src) {
|
|
3690
|
+
const cap = this.rules.block.lheading.exec(src);
|
|
3691
|
+
if (cap) {
|
|
3692
|
+
return {
|
|
3693
|
+
type: 'heading',
|
|
3694
|
+
raw: cap[0],
|
|
3695
|
+
depth: cap[2].charAt(0) === '=' ? 1 : 2,
|
|
3696
|
+
text: cap[1],
|
|
3697
|
+
tokens: this.lexer.inline(cap[1])
|
|
3698
|
+
};
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
paragraph(src) {
|
|
3702
|
+
const cap = this.rules.block.paragraph.exec(src);
|
|
3703
|
+
if (cap) {
|
|
3704
|
+
const text = cap[1].charAt(cap[1].length - 1) === '\n'
|
|
3705
|
+
? cap[1].slice(0, -1)
|
|
3706
|
+
: cap[1];
|
|
3707
|
+
return {
|
|
3708
|
+
type: 'paragraph',
|
|
3709
|
+
raw: cap[0],
|
|
3710
|
+
text,
|
|
3711
|
+
tokens: this.lexer.inline(text)
|
|
3712
|
+
};
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
text(src) {
|
|
3716
|
+
const cap = this.rules.block.text.exec(src);
|
|
3717
|
+
if (cap) {
|
|
3718
|
+
return {
|
|
3719
|
+
type: 'text',
|
|
3720
|
+
raw: cap[0],
|
|
3721
|
+
text: cap[0],
|
|
3722
|
+
tokens: this.lexer.inline(cap[0])
|
|
3723
|
+
};
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
escape(src) {
|
|
3727
|
+
const cap = this.rules.inline.escape.exec(src);
|
|
3728
|
+
if (cap) {
|
|
3729
|
+
return {
|
|
3730
|
+
type: 'escape',
|
|
3731
|
+
raw: cap[0],
|
|
3732
|
+
text: escape$1(cap[1])
|
|
3733
|
+
};
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
tag(src) {
|
|
3737
|
+
const cap = this.rules.inline.tag.exec(src);
|
|
3738
|
+
if (cap) {
|
|
3739
|
+
if (!this.lexer.state.inLink && /^<a /i.test(cap[0])) {
|
|
3740
|
+
this.lexer.state.inLink = true;
|
|
3741
|
+
}
|
|
3742
|
+
else if (this.lexer.state.inLink && /^<\/a>/i.test(cap[0])) {
|
|
3743
|
+
this.lexer.state.inLink = false;
|
|
3744
|
+
}
|
|
3745
|
+
if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
|
|
3746
|
+
this.lexer.state.inRawBlock = true;
|
|
3747
|
+
}
|
|
3748
|
+
else if (this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
|
|
3749
|
+
this.lexer.state.inRawBlock = false;
|
|
3750
|
+
}
|
|
3751
|
+
return {
|
|
3752
|
+
type: 'html',
|
|
3753
|
+
raw: cap[0],
|
|
3754
|
+
inLink: this.lexer.state.inLink,
|
|
3755
|
+
inRawBlock: this.lexer.state.inRawBlock,
|
|
3756
|
+
block: false,
|
|
3757
|
+
text: cap[0]
|
|
3758
|
+
};
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
link(src) {
|
|
3762
|
+
const cap = this.rules.inline.link.exec(src);
|
|
3763
|
+
if (cap) {
|
|
3764
|
+
const trimmedUrl = cap[2].trim();
|
|
3765
|
+
if (!this.options.pedantic && /^</.test(trimmedUrl)) {
|
|
3766
|
+
// commonmark requires matching angle brackets
|
|
3767
|
+
if (!(/>$/.test(trimmedUrl))) {
|
|
3768
|
+
return;
|
|
3769
|
+
}
|
|
3770
|
+
// ending angle bracket cannot be escaped
|
|
3771
|
+
const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
|
|
3772
|
+
if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
|
|
3773
|
+
return;
|
|
3774
|
+
}
|
|
3775
|
+
}
|
|
3776
|
+
else {
|
|
3777
|
+
// find closing parenthesis
|
|
3778
|
+
const lastParenIndex = findClosingBracket(cap[2], '()');
|
|
3779
|
+
if (lastParenIndex > -1) {
|
|
3780
|
+
const start = cap[0].indexOf('!') === 0 ? 5 : 4;
|
|
3781
|
+
const linkLen = start + cap[1].length + lastParenIndex;
|
|
3782
|
+
cap[2] = cap[2].substring(0, lastParenIndex);
|
|
3783
|
+
cap[0] = cap[0].substring(0, linkLen).trim();
|
|
3784
|
+
cap[3] = '';
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
let href = cap[2];
|
|
3788
|
+
let title = '';
|
|
3789
|
+
if (this.options.pedantic) {
|
|
3790
|
+
// split pedantic href and title
|
|
3791
|
+
const link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
|
|
3792
|
+
if (link) {
|
|
3793
|
+
href = link[1];
|
|
3794
|
+
title = link[3];
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
else {
|
|
3798
|
+
title = cap[3] ? cap[3].slice(1, -1) : '';
|
|
3799
|
+
}
|
|
3800
|
+
href = href.trim();
|
|
3801
|
+
if (/^</.test(href)) {
|
|
3802
|
+
if (this.options.pedantic && !(/>$/.test(trimmedUrl))) {
|
|
3803
|
+
// pedantic allows starting angle bracket without ending angle bracket
|
|
3804
|
+
href = href.slice(1);
|
|
3805
|
+
}
|
|
3806
|
+
else {
|
|
3807
|
+
href = href.slice(1, -1);
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
return outputLink(cap, {
|
|
3811
|
+
href: href ? href.replace(this.rules.inline.anyPunctuation, '$1') : href,
|
|
3812
|
+
title: title ? title.replace(this.rules.inline.anyPunctuation, '$1') : title
|
|
3813
|
+
}, cap[0], this.lexer);
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
reflink(src, links) {
|
|
3817
|
+
let cap;
|
|
3818
|
+
if ((cap = this.rules.inline.reflink.exec(src))
|
|
3819
|
+
|| (cap = this.rules.inline.nolink.exec(src))) {
|
|
3820
|
+
const linkString = (cap[2] || cap[1]).replace(/\s+/g, ' ');
|
|
3821
|
+
const link = links[linkString.toLowerCase()];
|
|
3822
|
+
if (!link) {
|
|
3823
|
+
const text = cap[0].charAt(0);
|
|
3824
|
+
return {
|
|
3825
|
+
type: 'text',
|
|
3826
|
+
raw: text,
|
|
3827
|
+
text
|
|
3828
|
+
};
|
|
3829
|
+
}
|
|
3830
|
+
return outputLink(cap, link, cap[0], this.lexer);
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
emStrong(src, maskedSrc, prevChar = '') {
|
|
3834
|
+
let match = this.rules.inline.emStrongLDelim.exec(src);
|
|
3835
|
+
if (!match)
|
|
3836
|
+
return;
|
|
3837
|
+
// _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
|
|
3838
|
+
if (match[3] && prevChar.match(/[\p{L}\p{N}]/u))
|
|
3839
|
+
return;
|
|
3840
|
+
const nextChar = match[1] || match[2] || '';
|
|
3841
|
+
if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
|
|
3842
|
+
// unicode Regex counts emoji as 1 char; spread into array for proper count (used multiple times below)
|
|
3843
|
+
const lLength = [...match[0]].length - 1;
|
|
3844
|
+
let rDelim, rLength, delimTotal = lLength, midDelimTotal = 0;
|
|
3845
|
+
const endReg = match[0][0] === '*' ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd;
|
|
3846
|
+
endReg.lastIndex = 0;
|
|
3847
|
+
// Clip maskedSrc to same section of string as src (move to lexer?)
|
|
3848
|
+
maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
|
|
3849
|
+
while ((match = endReg.exec(maskedSrc)) != null) {
|
|
3850
|
+
rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
|
|
3851
|
+
if (!rDelim)
|
|
3852
|
+
continue; // skip single * in __abc*abc__
|
|
3853
|
+
rLength = [...rDelim].length;
|
|
3854
|
+
if (match[3] || match[4]) { // found another Left Delim
|
|
3855
|
+
delimTotal += rLength;
|
|
3856
|
+
continue;
|
|
3857
|
+
}
|
|
3858
|
+
else if (match[5] || match[6]) { // either Left or Right Delim
|
|
3859
|
+
if (lLength % 3 && !((lLength + rLength) % 3)) {
|
|
3860
|
+
midDelimTotal += rLength;
|
|
3861
|
+
continue; // CommonMark Emphasis Rules 9-10
|
|
3862
|
+
}
|
|
3863
|
+
}
|
|
3864
|
+
delimTotal -= rLength;
|
|
3865
|
+
if (delimTotal > 0)
|
|
3866
|
+
continue; // Haven't found enough closing delimiters
|
|
3867
|
+
// Remove extra characters. *a*** -> *a*
|
|
3868
|
+
rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
|
|
3869
|
+
// char length can be >1 for unicode characters;
|
|
3870
|
+
const lastCharLength = [...match[0]][0].length;
|
|
3871
|
+
const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);
|
|
3872
|
+
// Create `em` if smallest delimiter has odd char count. *a***
|
|
3873
|
+
if (Math.min(lLength, rLength) % 2) {
|
|
3874
|
+
const text = raw.slice(1, -1);
|
|
3875
|
+
return {
|
|
3876
|
+
type: 'em',
|
|
3877
|
+
raw,
|
|
3878
|
+
text,
|
|
3879
|
+
tokens: this.lexer.inlineTokens(text)
|
|
3880
|
+
};
|
|
3881
|
+
}
|
|
3882
|
+
// Create 'strong' if smallest delimiter has even char count. **a***
|
|
3883
|
+
const text = raw.slice(2, -2);
|
|
3884
|
+
return {
|
|
3885
|
+
type: 'strong',
|
|
3886
|
+
raw,
|
|
3887
|
+
text,
|
|
3888
|
+
tokens: this.lexer.inlineTokens(text)
|
|
3889
|
+
};
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
codespan(src) {
|
|
3894
|
+
const cap = this.rules.inline.code.exec(src);
|
|
3895
|
+
if (cap) {
|
|
3896
|
+
let text = cap[2].replace(/\n/g, ' ');
|
|
3897
|
+
const hasNonSpaceChars = /[^ ]/.test(text);
|
|
3898
|
+
const hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
|
|
3899
|
+
if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
|
|
3900
|
+
text = text.substring(1, text.length - 1);
|
|
3901
|
+
}
|
|
3902
|
+
text = escape$1(text, true);
|
|
3903
|
+
return {
|
|
3904
|
+
type: 'codespan',
|
|
3905
|
+
raw: cap[0],
|
|
3906
|
+
text
|
|
3907
|
+
};
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
br(src) {
|
|
3911
|
+
const cap = this.rules.inline.br.exec(src);
|
|
3912
|
+
if (cap) {
|
|
3913
|
+
return {
|
|
3914
|
+
type: 'br',
|
|
3915
|
+
raw: cap[0]
|
|
3916
|
+
};
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
del(src) {
|
|
3920
|
+
const cap = this.rules.inline.del.exec(src);
|
|
3921
|
+
if (cap) {
|
|
3922
|
+
return {
|
|
3923
|
+
type: 'del',
|
|
3924
|
+
raw: cap[0],
|
|
3925
|
+
text: cap[2],
|
|
3926
|
+
tokens: this.lexer.inlineTokens(cap[2])
|
|
3927
|
+
};
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
autolink(src) {
|
|
3931
|
+
const cap = this.rules.inline.autolink.exec(src);
|
|
3932
|
+
if (cap) {
|
|
3933
|
+
let text, href;
|
|
3934
|
+
if (cap[2] === '@') {
|
|
3935
|
+
text = escape$1(cap[1]);
|
|
3936
|
+
href = 'mailto:' + text;
|
|
3937
|
+
}
|
|
3938
|
+
else {
|
|
3939
|
+
text = escape$1(cap[1]);
|
|
3940
|
+
href = text;
|
|
3941
|
+
}
|
|
3942
|
+
return {
|
|
3943
|
+
type: 'link',
|
|
3944
|
+
raw: cap[0],
|
|
3945
|
+
text,
|
|
3946
|
+
href,
|
|
3947
|
+
tokens: [
|
|
3948
|
+
{
|
|
3949
|
+
type: 'text',
|
|
3950
|
+
raw: text,
|
|
3951
|
+
text
|
|
3952
|
+
}
|
|
3953
|
+
]
|
|
3954
|
+
};
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
url(src) {
|
|
3958
|
+
let cap;
|
|
3959
|
+
if (cap = this.rules.inline.url.exec(src)) {
|
|
3960
|
+
let text, href;
|
|
3961
|
+
if (cap[2] === '@') {
|
|
3962
|
+
text = escape$1(cap[0]);
|
|
3963
|
+
href = 'mailto:' + text;
|
|
3964
|
+
}
|
|
3965
|
+
else {
|
|
3966
|
+
// do extended autolink path validation
|
|
3967
|
+
let prevCapZero;
|
|
3968
|
+
do {
|
|
3969
|
+
prevCapZero = cap[0];
|
|
3970
|
+
cap[0] = this.rules.inline._backpedal.exec(cap[0])?.[0] ?? '';
|
|
3971
|
+
} while (prevCapZero !== cap[0]);
|
|
3972
|
+
text = escape$1(cap[0]);
|
|
3973
|
+
if (cap[1] === 'www.') {
|
|
3974
|
+
href = 'http://' + cap[0];
|
|
3975
|
+
}
|
|
3976
|
+
else {
|
|
3977
|
+
href = cap[0];
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3980
|
+
return {
|
|
3981
|
+
type: 'link',
|
|
3982
|
+
raw: cap[0],
|
|
3983
|
+
text,
|
|
3984
|
+
href,
|
|
3985
|
+
tokens: [
|
|
3986
|
+
{
|
|
3987
|
+
type: 'text',
|
|
3988
|
+
raw: text,
|
|
3989
|
+
text
|
|
3990
|
+
}
|
|
3991
|
+
]
|
|
3992
|
+
};
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
inlineText(src) {
|
|
3996
|
+
const cap = this.rules.inline.text.exec(src);
|
|
3997
|
+
if (cap) {
|
|
3998
|
+
let text;
|
|
3999
|
+
if (this.lexer.state.inRawBlock) {
|
|
4000
|
+
text = cap[0];
|
|
4001
|
+
}
|
|
4002
|
+
else {
|
|
4003
|
+
text = escape$1(cap[0]);
|
|
4004
|
+
}
|
|
4005
|
+
return {
|
|
4006
|
+
type: 'text',
|
|
4007
|
+
raw: cap[0],
|
|
4008
|
+
text
|
|
4009
|
+
};
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
|
|
4014
|
+
/**
|
|
4015
|
+
* Block-Level Grammar
|
|
4016
|
+
*/
|
|
4017
|
+
const newline = /^(?: *(?:\n|$))+/;
|
|
4018
|
+
const blockCode = /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/;
|
|
4019
|
+
const fences = /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/;
|
|
4020
|
+
const hr = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/;
|
|
4021
|
+
const heading = /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/;
|
|
4022
|
+
const bullet = /(?:[*+-]|\d{1,9}[.)])/;
|
|
4023
|
+
const lheading = edit(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/)
|
|
4024
|
+
.replace(/bull/g, bullet) // lists can interrupt
|
|
4025
|
+
.replace(/blockCode/g, / {4}/) // indented code blocks can interrupt
|
|
4026
|
+
.replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/) // fenced code blocks can interrupt
|
|
4027
|
+
.replace(/blockquote/g, / {0,3}>/) // blockquote can interrupt
|
|
4028
|
+
.replace(/heading/g, / {0,3}#{1,6}/) // ATX heading can interrupt
|
|
4029
|
+
.replace(/html/g, / {0,3}<[^\n>]+>\n/) // block html can interrupt
|
|
4030
|
+
.getRegex();
|
|
4031
|
+
const _paragraph = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/;
|
|
4032
|
+
const blockText = /^[^\n]+/;
|
|
4033
|
+
const _blockLabel = /(?!\s*\])(?:\\.|[^\[\]\\])+/;
|
|
4034
|
+
const def = edit(/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/)
|
|
4035
|
+
.replace('label', _blockLabel)
|
|
4036
|
+
.replace('title', /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/)
|
|
4037
|
+
.getRegex();
|
|
4038
|
+
const list = edit(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/)
|
|
4039
|
+
.replace(/bull/g, bullet)
|
|
4040
|
+
.getRegex();
|
|
4041
|
+
const _tag = 'address|article|aside|base|basefont|blockquote|body|caption'
|
|
4042
|
+
+ '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
|
|
4043
|
+
+ '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
|
|
4044
|
+
+ '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
|
|
4045
|
+
+ '|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title'
|
|
4046
|
+
+ '|tr|track|ul';
|
|
4047
|
+
const _comment = /<!--(?:-?>|[\s\S]*?(?:-->|$))/;
|
|
4048
|
+
const html = edit('^ {0,3}(?:' // optional indentation
|
|
4049
|
+
+ '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
|
|
4050
|
+
+ '|comment[^\\n]*(\\n+|$)' // (2)
|
|
4051
|
+
+ '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
|
|
4052
|
+
+ '|<![A-Z][\\s\\S]*?(?:>\\n*|$)' // (4)
|
|
4053
|
+
+ '|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)' // (5)
|
|
4054
|
+
+ '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (6)
|
|
4055
|
+
+ '|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) open tag
|
|
4056
|
+
+ '|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag
|
|
4057
|
+
+ ')', 'i')
|
|
4058
|
+
.replace('comment', _comment)
|
|
4059
|
+
.replace('tag', _tag)
|
|
4060
|
+
.replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
|
|
4061
|
+
.getRegex();
|
|
4062
|
+
const paragraph = edit(_paragraph)
|
|
4063
|
+
.replace('hr', hr)
|
|
4064
|
+
.replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
|
|
4065
|
+
.replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs
|
|
4066
|
+
.replace('|table', '')
|
|
4067
|
+
.replace('blockquote', ' {0,3}>')
|
|
4068
|
+
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
|
|
4069
|
+
.replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
|
|
4070
|
+
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
|
|
4071
|
+
.replace('tag', _tag) // pars can be interrupted by type (6) html blocks
|
|
4072
|
+
.getRegex();
|
|
4073
|
+
const blockquote = edit(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/)
|
|
4074
|
+
.replace('paragraph', paragraph)
|
|
4075
|
+
.getRegex();
|
|
4076
|
+
/**
|
|
4077
|
+
* Normal Block Grammar
|
|
4078
|
+
*/
|
|
4079
|
+
const blockNormal = {
|
|
4080
|
+
blockquote,
|
|
4081
|
+
code: blockCode,
|
|
4082
|
+
def,
|
|
4083
|
+
fences,
|
|
4084
|
+
heading,
|
|
4085
|
+
hr,
|
|
4086
|
+
html,
|
|
4087
|
+
lheading,
|
|
4088
|
+
list,
|
|
4089
|
+
newline,
|
|
4090
|
+
paragraph,
|
|
4091
|
+
table: noopTest,
|
|
4092
|
+
text: blockText
|
|
4093
|
+
};
|
|
4094
|
+
/**
|
|
4095
|
+
* GFM Block Grammar
|
|
4096
|
+
*/
|
|
4097
|
+
const gfmTable = edit('^ *([^\\n ].*)\\n' // Header
|
|
4098
|
+
+ ' {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)' // Align
|
|
4099
|
+
+ '(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)') // Cells
|
|
4100
|
+
.replace('hr', hr)
|
|
4101
|
+
.replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
|
|
4102
|
+
.replace('blockquote', ' {0,3}>')
|
|
4103
|
+
.replace('code', ' {4}[^\\n]')
|
|
4104
|
+
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
|
|
4105
|
+
.replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
|
|
4106
|
+
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
|
|
4107
|
+
.replace('tag', _tag) // tables can be interrupted by type (6) html blocks
|
|
4108
|
+
.getRegex();
|
|
4109
|
+
const blockGfm = {
|
|
4110
|
+
...blockNormal,
|
|
4111
|
+
table: gfmTable,
|
|
4112
|
+
paragraph: edit(_paragraph)
|
|
4113
|
+
.replace('hr', hr)
|
|
4114
|
+
.replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
|
|
4115
|
+
.replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs
|
|
4116
|
+
.replace('table', gfmTable) // interrupt paragraphs with table
|
|
4117
|
+
.replace('blockquote', ' {0,3}>')
|
|
4118
|
+
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
|
|
4119
|
+
.replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
|
|
4120
|
+
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
|
|
4121
|
+
.replace('tag', _tag) // pars can be interrupted by type (6) html blocks
|
|
4122
|
+
.getRegex()
|
|
4123
|
+
};
|
|
4124
|
+
/**
|
|
4125
|
+
* Pedantic grammar (original John Gruber's loose markdown specification)
|
|
4126
|
+
*/
|
|
4127
|
+
const blockPedantic = {
|
|
4128
|
+
...blockNormal,
|
|
4129
|
+
html: edit('^ *(?:comment *(?:\\n|\\s*$)'
|
|
4130
|
+
+ '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
|
|
4131
|
+
+ '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
|
|
4132
|
+
.replace('comment', _comment)
|
|
4133
|
+
.replace(/tag/g, '(?!(?:'
|
|
4134
|
+
+ 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
|
|
4135
|
+
+ '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
|
|
4136
|
+
+ '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
|
|
4137
|
+
.getRegex(),
|
|
4138
|
+
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
|
|
4139
|
+
heading: /^(#{1,6})(.*)(?:\n+|$)/,
|
|
4140
|
+
fences: noopTest, // fences not supported
|
|
4141
|
+
lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,
|
|
4142
|
+
paragraph: edit(_paragraph)
|
|
4143
|
+
.replace('hr', hr)
|
|
4144
|
+
.replace('heading', ' *#{1,6} *[^\n]')
|
|
4145
|
+
.replace('lheading', lheading)
|
|
4146
|
+
.replace('|table', '')
|
|
4147
|
+
.replace('blockquote', ' {0,3}>')
|
|
4148
|
+
.replace('|fences', '')
|
|
4149
|
+
.replace('|list', '')
|
|
4150
|
+
.replace('|html', '')
|
|
4151
|
+
.replace('|tag', '')
|
|
4152
|
+
.getRegex()
|
|
4153
|
+
};
|
|
4154
|
+
/**
|
|
4155
|
+
* Inline-Level Grammar
|
|
4156
|
+
*/
|
|
4157
|
+
const escape = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/;
|
|
4158
|
+
const inlineCode = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/;
|
|
4159
|
+
const br = /^( {2,}|\\)\n(?!\s*$)/;
|
|
4160
|
+
const inlineText = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/;
|
|
4161
|
+
// list of unicode punctuation marks, plus any missing characters from CommonMark spec
|
|
4162
|
+
const _punctuation = '\\p{P}\\p{S}';
|
|
4163
|
+
const punctuation = edit(/^((?![*_])[\spunctuation])/, 'u')
|
|
4164
|
+
.replace(/punctuation/g, _punctuation).getRegex();
|
|
4165
|
+
// sequences em should skip over [title](link), `code`, <html>
|
|
4166
|
+
const blockSkip = /\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g;
|
|
4167
|
+
const emStrongLDelim = edit(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/, 'u')
|
|
4168
|
+
.replace(/punct/g, _punctuation)
|
|
4169
|
+
.getRegex();
|
|
4170
|
+
const emStrongRDelimAst = edit('^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)' // Skip orphan inside strong
|
|
4171
|
+
+ '|[^*]+(?=[^*])' // Consume to delim
|
|
4172
|
+
+ '|(?!\\*)[punct](\\*+)(?=[\\s]|$)' // (1) #*** can only be a Right Delimiter
|
|
4173
|
+
+ '|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)' // (2) a***#, a*** can only be a Right Delimiter
|
|
4174
|
+
+ '|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])' // (3) #***a, ***a can only be Left Delimiter
|
|
4175
|
+
+ '|[\\s](\\*+)(?!\\*)(?=[punct])' // (4) ***# can only be Left Delimiter
|
|
4176
|
+
+ '|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])' // (5) #***# can be either Left or Right Delimiter
|
|
4177
|
+
+ '|[^punct\\s](\\*+)(?=[^punct\\s])', 'gu') // (6) a***a can be either Left or Right Delimiter
|
|
4178
|
+
.replace(/punct/g, _punctuation)
|
|
4179
|
+
.getRegex();
|
|
4180
|
+
// (6) Not allowed for _
|
|
4181
|
+
const emStrongRDelimUnd = edit('^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)' // Skip orphan inside strong
|
|
4182
|
+
+ '|[^_]+(?=[^_])' // Consume to delim
|
|
4183
|
+
+ '|(?!_)[punct](_+)(?=[\\s]|$)' // (1) #___ can only be a Right Delimiter
|
|
4184
|
+
+ '|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)' // (2) a___#, a___ can only be a Right Delimiter
|
|
4185
|
+
+ '|(?!_)[punct\\s](_+)(?=[^punct\\s])' // (3) #___a, ___a can only be Left Delimiter
|
|
4186
|
+
+ '|[\\s](_+)(?!_)(?=[punct])' // (4) ___# can only be Left Delimiter
|
|
4187
|
+
+ '|(?!_)[punct](_+)(?!_)(?=[punct])', 'gu') // (5) #___# can be either Left or Right Delimiter
|
|
4188
|
+
.replace(/punct/g, _punctuation)
|
|
4189
|
+
.getRegex();
|
|
4190
|
+
const anyPunctuation = edit(/\\([punct])/, 'gu')
|
|
4191
|
+
.replace(/punct/g, _punctuation)
|
|
4192
|
+
.getRegex();
|
|
4193
|
+
const autolink = edit(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/)
|
|
4194
|
+
.replace('scheme', /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/)
|
|
4195
|
+
.replace('email', /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/)
|
|
4196
|
+
.getRegex();
|
|
4197
|
+
const _inlineComment = edit(_comment).replace('(?:-->|$)', '-->').getRegex();
|
|
4198
|
+
const tag = edit('^comment'
|
|
4199
|
+
+ '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
|
|
4200
|
+
+ '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
|
|
4201
|
+
+ '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
|
|
4202
|
+
+ '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
|
|
4203
|
+
+ '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>') // CDATA section
|
|
4204
|
+
.replace('comment', _inlineComment)
|
|
4205
|
+
.replace('attribute', /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/)
|
|
4206
|
+
.getRegex();
|
|
4207
|
+
const _inlineLabel = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
|
|
4208
|
+
const link = edit(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/)
|
|
4209
|
+
.replace('label', _inlineLabel)
|
|
4210
|
+
.replace('href', /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/)
|
|
4211
|
+
.replace('title', /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/)
|
|
4212
|
+
.getRegex();
|
|
4213
|
+
const reflink = edit(/^!?\[(label)\]\[(ref)\]/)
|
|
4214
|
+
.replace('label', _inlineLabel)
|
|
4215
|
+
.replace('ref', _blockLabel)
|
|
4216
|
+
.getRegex();
|
|
4217
|
+
const nolink = edit(/^!?\[(ref)\](?:\[\])?/)
|
|
4218
|
+
.replace('ref', _blockLabel)
|
|
4219
|
+
.getRegex();
|
|
4220
|
+
const reflinkSearch = edit('reflink|nolink(?!\\()', 'g')
|
|
4221
|
+
.replace('reflink', reflink)
|
|
4222
|
+
.replace('nolink', nolink)
|
|
4223
|
+
.getRegex();
|
|
4224
|
+
/**
|
|
4225
|
+
* Normal Inline Grammar
|
|
4226
|
+
*/
|
|
4227
|
+
const inlineNormal = {
|
|
4228
|
+
_backpedal: noopTest, // only used for GFM url
|
|
4229
|
+
anyPunctuation,
|
|
4230
|
+
autolink,
|
|
4231
|
+
blockSkip,
|
|
4232
|
+
br,
|
|
4233
|
+
code: inlineCode,
|
|
4234
|
+
del: noopTest,
|
|
4235
|
+
emStrongLDelim,
|
|
4236
|
+
emStrongRDelimAst,
|
|
4237
|
+
emStrongRDelimUnd,
|
|
4238
|
+
escape,
|
|
4239
|
+
link,
|
|
4240
|
+
nolink,
|
|
4241
|
+
punctuation,
|
|
4242
|
+
reflink,
|
|
4243
|
+
reflinkSearch,
|
|
4244
|
+
tag,
|
|
4245
|
+
text: inlineText,
|
|
4246
|
+
url: noopTest
|
|
4247
|
+
};
|
|
4248
|
+
/**
|
|
4249
|
+
* Pedantic Inline Grammar
|
|
4250
|
+
*/
|
|
4251
|
+
const inlinePedantic = {
|
|
4252
|
+
...inlineNormal,
|
|
4253
|
+
link: edit(/^!?\[(label)\]\((.*?)\)/)
|
|
4254
|
+
.replace('label', _inlineLabel)
|
|
4255
|
+
.getRegex(),
|
|
4256
|
+
reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
|
|
4257
|
+
.replace('label', _inlineLabel)
|
|
4258
|
+
.getRegex()
|
|
4259
|
+
};
|
|
4260
|
+
/**
|
|
4261
|
+
* GFM Inline Grammar
|
|
4262
|
+
*/
|
|
4263
|
+
const inlineGfm = {
|
|
4264
|
+
...inlineNormal,
|
|
4265
|
+
escape: edit(escape).replace('])', '~|])').getRegex(),
|
|
4266
|
+
url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, 'i')
|
|
4267
|
+
.replace('email', /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/)
|
|
4268
|
+
.getRegex(),
|
|
4269
|
+
_backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,
|
|
4270
|
+
del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,
|
|
4271
|
+
text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/
|
|
4272
|
+
};
|
|
4273
|
+
/**
|
|
4274
|
+
* GFM + Line Breaks Inline Grammar
|
|
4275
|
+
*/
|
|
4276
|
+
const inlineBreaks = {
|
|
4277
|
+
...inlineGfm,
|
|
4278
|
+
br: edit(br).replace('{2,}', '*').getRegex(),
|
|
4279
|
+
text: edit(inlineGfm.text)
|
|
4280
|
+
.replace('\\b_', '\\b_| {2,}\\n')
|
|
4281
|
+
.replace(/\{2,\}/g, '*')
|
|
4282
|
+
.getRegex()
|
|
4283
|
+
};
|
|
4284
|
+
/**
|
|
4285
|
+
* exports
|
|
4286
|
+
*/
|
|
4287
|
+
const block = {
|
|
4288
|
+
normal: blockNormal,
|
|
4289
|
+
gfm: blockGfm,
|
|
4290
|
+
pedantic: blockPedantic
|
|
4291
|
+
};
|
|
4292
|
+
const inline = {
|
|
4293
|
+
normal: inlineNormal,
|
|
4294
|
+
gfm: inlineGfm,
|
|
4295
|
+
breaks: inlineBreaks,
|
|
4296
|
+
pedantic: inlinePedantic
|
|
4297
|
+
};
|
|
4298
|
+
|
|
4299
|
+
/**
|
|
4300
|
+
* Block Lexer
|
|
4301
|
+
*/
|
|
4302
|
+
class _Lexer {
|
|
4303
|
+
tokens;
|
|
4304
|
+
options;
|
|
4305
|
+
state;
|
|
4306
|
+
tokenizer;
|
|
4307
|
+
inlineQueue;
|
|
4308
|
+
constructor(options) {
|
|
4309
|
+
// TokenList cannot be created in one go
|
|
4310
|
+
this.tokens = [];
|
|
4311
|
+
this.tokens.links = Object.create(null);
|
|
4312
|
+
this.options = options || _defaults;
|
|
4313
|
+
this.options.tokenizer = this.options.tokenizer || new _Tokenizer();
|
|
4314
|
+
this.tokenizer = this.options.tokenizer;
|
|
4315
|
+
this.tokenizer.options = this.options;
|
|
4316
|
+
this.tokenizer.lexer = this;
|
|
4317
|
+
this.inlineQueue = [];
|
|
4318
|
+
this.state = {
|
|
4319
|
+
inLink: false,
|
|
4320
|
+
inRawBlock: false,
|
|
4321
|
+
top: true
|
|
4322
|
+
};
|
|
4323
|
+
const rules = {
|
|
4324
|
+
block: block.normal,
|
|
4325
|
+
inline: inline.normal
|
|
4326
|
+
};
|
|
4327
|
+
if (this.options.pedantic) {
|
|
4328
|
+
rules.block = block.pedantic;
|
|
4329
|
+
rules.inline = inline.pedantic;
|
|
4330
|
+
}
|
|
4331
|
+
else if (this.options.gfm) {
|
|
4332
|
+
rules.block = block.gfm;
|
|
4333
|
+
if (this.options.breaks) {
|
|
4334
|
+
rules.inline = inline.breaks;
|
|
4335
|
+
}
|
|
4336
|
+
else {
|
|
4337
|
+
rules.inline = inline.gfm;
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
this.tokenizer.rules = rules;
|
|
4341
|
+
}
|
|
4342
|
+
/**
|
|
4343
|
+
* Expose Rules
|
|
4344
|
+
*/
|
|
4345
|
+
static get rules() {
|
|
4346
|
+
return {
|
|
4347
|
+
block,
|
|
4348
|
+
inline
|
|
4349
|
+
};
|
|
4350
|
+
}
|
|
4351
|
+
/**
|
|
4352
|
+
* Static Lex Method
|
|
4353
|
+
*/
|
|
4354
|
+
static lex(src, options) {
|
|
4355
|
+
const lexer = new _Lexer(options);
|
|
4356
|
+
return lexer.lex(src);
|
|
4357
|
+
}
|
|
4358
|
+
/**
|
|
4359
|
+
* Static Lex Inline Method
|
|
4360
|
+
*/
|
|
4361
|
+
static lexInline(src, options) {
|
|
4362
|
+
const lexer = new _Lexer(options);
|
|
4363
|
+
return lexer.inlineTokens(src);
|
|
4364
|
+
}
|
|
4365
|
+
/**
|
|
4366
|
+
* Preprocessing
|
|
4367
|
+
*/
|
|
4368
|
+
lex(src) {
|
|
4369
|
+
src = src
|
|
4370
|
+
.replace(/\r\n|\r/g, '\n');
|
|
4371
|
+
this.blockTokens(src, this.tokens);
|
|
4372
|
+
for (let i = 0; i < this.inlineQueue.length; i++) {
|
|
4373
|
+
const next = this.inlineQueue[i];
|
|
4374
|
+
this.inlineTokens(next.src, next.tokens);
|
|
4375
|
+
}
|
|
4376
|
+
this.inlineQueue = [];
|
|
4377
|
+
return this.tokens;
|
|
4378
|
+
}
|
|
4379
|
+
blockTokens(src, tokens = []) {
|
|
4380
|
+
if (this.options.pedantic) {
|
|
4381
|
+
src = src.replace(/\t/g, ' ').replace(/^ +$/gm, '');
|
|
4382
|
+
}
|
|
4383
|
+
else {
|
|
4384
|
+
src = src.replace(/^( *)(\t+)/gm, (_, leading, tabs) => {
|
|
4385
|
+
return leading + ' '.repeat(tabs.length);
|
|
4386
|
+
});
|
|
4387
|
+
}
|
|
4388
|
+
let token;
|
|
4389
|
+
let lastToken;
|
|
4390
|
+
let cutSrc;
|
|
4391
|
+
let lastParagraphClipped;
|
|
4392
|
+
while (src) {
|
|
4393
|
+
if (this.options.extensions
|
|
4394
|
+
&& this.options.extensions.block
|
|
4395
|
+
&& this.options.extensions.block.some((extTokenizer) => {
|
|
4396
|
+
if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
|
|
4397
|
+
src = src.substring(token.raw.length);
|
|
4398
|
+
tokens.push(token);
|
|
4399
|
+
return true;
|
|
4400
|
+
}
|
|
4401
|
+
return false;
|
|
4402
|
+
})) {
|
|
4403
|
+
continue;
|
|
4404
|
+
}
|
|
4405
|
+
// newline
|
|
4406
|
+
if (token = this.tokenizer.space(src)) {
|
|
4407
|
+
src = src.substring(token.raw.length);
|
|
4408
|
+
if (token.raw.length === 1 && tokens.length > 0) {
|
|
4409
|
+
// if there's a single \n as a spacer, it's terminating the last line,
|
|
4410
|
+
// so move it there so that we don't get unnecessary paragraph tags
|
|
4411
|
+
tokens[tokens.length - 1].raw += '\n';
|
|
4412
|
+
}
|
|
4413
|
+
else {
|
|
4414
|
+
tokens.push(token);
|
|
4415
|
+
}
|
|
4416
|
+
continue;
|
|
4417
|
+
}
|
|
4418
|
+
// code
|
|
4419
|
+
if (token = this.tokenizer.code(src)) {
|
|
4420
|
+
src = src.substring(token.raw.length);
|
|
4421
|
+
lastToken = tokens[tokens.length - 1];
|
|
4422
|
+
// An indented code block cannot interrupt a paragraph.
|
|
4423
|
+
if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {
|
|
4424
|
+
lastToken.raw += '\n' + token.raw;
|
|
4425
|
+
lastToken.text += '\n' + token.text;
|
|
4426
|
+
this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
|
|
4427
|
+
}
|
|
4428
|
+
else {
|
|
4429
|
+
tokens.push(token);
|
|
4430
|
+
}
|
|
4431
|
+
continue;
|
|
4432
|
+
}
|
|
4433
|
+
// fences
|
|
4434
|
+
if (token = this.tokenizer.fences(src)) {
|
|
4435
|
+
src = src.substring(token.raw.length);
|
|
4436
|
+
tokens.push(token);
|
|
4437
|
+
continue;
|
|
4438
|
+
}
|
|
4439
|
+
// heading
|
|
4440
|
+
if (token = this.tokenizer.heading(src)) {
|
|
4441
|
+
src = src.substring(token.raw.length);
|
|
4442
|
+
tokens.push(token);
|
|
4443
|
+
continue;
|
|
4444
|
+
}
|
|
4445
|
+
// hr
|
|
4446
|
+
if (token = this.tokenizer.hr(src)) {
|
|
4447
|
+
src = src.substring(token.raw.length);
|
|
4448
|
+
tokens.push(token);
|
|
4449
|
+
continue;
|
|
4450
|
+
}
|
|
4451
|
+
// blockquote
|
|
4452
|
+
if (token = this.tokenizer.blockquote(src)) {
|
|
4453
|
+
src = src.substring(token.raw.length);
|
|
4454
|
+
tokens.push(token);
|
|
4455
|
+
continue;
|
|
4456
|
+
}
|
|
4457
|
+
// list
|
|
4458
|
+
if (token = this.tokenizer.list(src)) {
|
|
4459
|
+
src = src.substring(token.raw.length);
|
|
4460
|
+
tokens.push(token);
|
|
4461
|
+
continue;
|
|
4462
|
+
}
|
|
4463
|
+
// html
|
|
4464
|
+
if (token = this.tokenizer.html(src)) {
|
|
4465
|
+
src = src.substring(token.raw.length);
|
|
4466
|
+
tokens.push(token);
|
|
4467
|
+
continue;
|
|
4468
|
+
}
|
|
4469
|
+
// def
|
|
4470
|
+
if (token = this.tokenizer.def(src)) {
|
|
4471
|
+
src = src.substring(token.raw.length);
|
|
4472
|
+
lastToken = tokens[tokens.length - 1];
|
|
4473
|
+
if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {
|
|
4474
|
+
lastToken.raw += '\n' + token.raw;
|
|
4475
|
+
lastToken.text += '\n' + token.raw;
|
|
4476
|
+
this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
|
|
4477
|
+
}
|
|
4478
|
+
else if (!this.tokens.links[token.tag]) {
|
|
4479
|
+
this.tokens.links[token.tag] = {
|
|
4480
|
+
href: token.href,
|
|
4481
|
+
title: token.title
|
|
4482
|
+
};
|
|
4483
|
+
}
|
|
4484
|
+
continue;
|
|
4485
|
+
}
|
|
4486
|
+
// table (gfm)
|
|
4487
|
+
if (token = this.tokenizer.table(src)) {
|
|
4488
|
+
src = src.substring(token.raw.length);
|
|
4489
|
+
tokens.push(token);
|
|
4490
|
+
continue;
|
|
4491
|
+
}
|
|
4492
|
+
// lheading
|
|
4493
|
+
if (token = this.tokenizer.lheading(src)) {
|
|
4494
|
+
src = src.substring(token.raw.length);
|
|
4495
|
+
tokens.push(token);
|
|
4496
|
+
continue;
|
|
4497
|
+
}
|
|
4498
|
+
// top-level paragraph
|
|
4499
|
+
// prevent paragraph consuming extensions by clipping 'src' to extension start
|
|
4500
|
+
cutSrc = src;
|
|
4501
|
+
if (this.options.extensions && this.options.extensions.startBlock) {
|
|
4502
|
+
let startIndex = Infinity;
|
|
4503
|
+
const tempSrc = src.slice(1);
|
|
4504
|
+
let tempStart;
|
|
4505
|
+
this.options.extensions.startBlock.forEach((getStartIndex) => {
|
|
4506
|
+
tempStart = getStartIndex.call({ lexer: this }, tempSrc);
|
|
4507
|
+
if (typeof tempStart === 'number' && tempStart >= 0) {
|
|
4508
|
+
startIndex = Math.min(startIndex, tempStart);
|
|
4509
|
+
}
|
|
4510
|
+
});
|
|
4511
|
+
if (startIndex < Infinity && startIndex >= 0) {
|
|
4512
|
+
cutSrc = src.substring(0, startIndex + 1);
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4515
|
+
if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) {
|
|
4516
|
+
lastToken = tokens[tokens.length - 1];
|
|
4517
|
+
if (lastParagraphClipped && lastToken.type === 'paragraph') {
|
|
4518
|
+
lastToken.raw += '\n' + token.raw;
|
|
4519
|
+
lastToken.text += '\n' + token.text;
|
|
4520
|
+
this.inlineQueue.pop();
|
|
4521
|
+
this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
|
|
4522
|
+
}
|
|
4523
|
+
else {
|
|
4524
|
+
tokens.push(token);
|
|
4525
|
+
}
|
|
4526
|
+
lastParagraphClipped = (cutSrc.length !== src.length);
|
|
4527
|
+
src = src.substring(token.raw.length);
|
|
4528
|
+
continue;
|
|
4529
|
+
}
|
|
4530
|
+
// text
|
|
4531
|
+
if (token = this.tokenizer.text(src)) {
|
|
4532
|
+
src = src.substring(token.raw.length);
|
|
4533
|
+
lastToken = tokens[tokens.length - 1];
|
|
4534
|
+
if (lastToken && lastToken.type === 'text') {
|
|
4535
|
+
lastToken.raw += '\n' + token.raw;
|
|
4536
|
+
lastToken.text += '\n' + token.text;
|
|
4537
|
+
this.inlineQueue.pop();
|
|
4538
|
+
this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
|
|
4539
|
+
}
|
|
4540
|
+
else {
|
|
4541
|
+
tokens.push(token);
|
|
4542
|
+
}
|
|
4543
|
+
continue;
|
|
4544
|
+
}
|
|
4545
|
+
if (src) {
|
|
4546
|
+
const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
|
|
4547
|
+
if (this.options.silent) {
|
|
4548
|
+
console.error(errMsg);
|
|
4549
|
+
break;
|
|
4550
|
+
}
|
|
4551
|
+
else {
|
|
4552
|
+
throw new Error(errMsg);
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
this.state.top = true;
|
|
4557
|
+
return tokens;
|
|
4558
|
+
}
|
|
4559
|
+
inline(src, tokens = []) {
|
|
4560
|
+
this.inlineQueue.push({ src, tokens });
|
|
4561
|
+
return tokens;
|
|
4562
|
+
}
|
|
4563
|
+
/**
|
|
4564
|
+
* Lexing/Compiling
|
|
4565
|
+
*/
|
|
4566
|
+
inlineTokens(src, tokens = []) {
|
|
4567
|
+
let token, lastToken, cutSrc;
|
|
4568
|
+
// String with links masked to avoid interference with em and strong
|
|
4569
|
+
let maskedSrc = src;
|
|
4570
|
+
let match;
|
|
4571
|
+
let keepPrevChar, prevChar;
|
|
4572
|
+
// Mask out reflinks
|
|
4573
|
+
if (this.tokens.links) {
|
|
4574
|
+
const links = Object.keys(this.tokens.links);
|
|
4575
|
+
if (links.length > 0) {
|
|
4576
|
+
while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
|
|
4577
|
+
if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
|
|
4578
|
+
maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4583
|
+
// Mask out other blocks
|
|
4584
|
+
while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
|
|
4585
|
+
maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
|
|
4586
|
+
}
|
|
4587
|
+
// Mask out escaped characters
|
|
4588
|
+
while ((match = this.tokenizer.rules.inline.anyPunctuation.exec(maskedSrc)) != null) {
|
|
4589
|
+
maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);
|
|
4590
|
+
}
|
|
4591
|
+
while (src) {
|
|
4592
|
+
if (!keepPrevChar) {
|
|
4593
|
+
prevChar = '';
|
|
4594
|
+
}
|
|
4595
|
+
keepPrevChar = false;
|
|
4596
|
+
// extensions
|
|
4597
|
+
if (this.options.extensions
|
|
4598
|
+
&& this.options.extensions.inline
|
|
4599
|
+
&& this.options.extensions.inline.some((extTokenizer) => {
|
|
4600
|
+
if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
|
|
4601
|
+
src = src.substring(token.raw.length);
|
|
4602
|
+
tokens.push(token);
|
|
4603
|
+
return true;
|
|
4604
|
+
}
|
|
4605
|
+
return false;
|
|
4606
|
+
})) {
|
|
4607
|
+
continue;
|
|
4608
|
+
}
|
|
4609
|
+
// escape
|
|
4610
|
+
if (token = this.tokenizer.escape(src)) {
|
|
4611
|
+
src = src.substring(token.raw.length);
|
|
4612
|
+
tokens.push(token);
|
|
4613
|
+
continue;
|
|
4614
|
+
}
|
|
4615
|
+
// tag
|
|
4616
|
+
if (token = this.tokenizer.tag(src)) {
|
|
4617
|
+
src = src.substring(token.raw.length);
|
|
4618
|
+
lastToken = tokens[tokens.length - 1];
|
|
4619
|
+
if (lastToken && token.type === 'text' && lastToken.type === 'text') {
|
|
4620
|
+
lastToken.raw += token.raw;
|
|
4621
|
+
lastToken.text += token.text;
|
|
4622
|
+
}
|
|
4623
|
+
else {
|
|
4624
|
+
tokens.push(token);
|
|
4625
|
+
}
|
|
4626
|
+
continue;
|
|
4627
|
+
}
|
|
4628
|
+
// link
|
|
4629
|
+
if (token = this.tokenizer.link(src)) {
|
|
4630
|
+
src = src.substring(token.raw.length);
|
|
4631
|
+
tokens.push(token);
|
|
4632
|
+
continue;
|
|
4633
|
+
}
|
|
4634
|
+
// reflink, nolink
|
|
4635
|
+
if (token = this.tokenizer.reflink(src, this.tokens.links)) {
|
|
4636
|
+
src = src.substring(token.raw.length);
|
|
4637
|
+
lastToken = tokens[tokens.length - 1];
|
|
4638
|
+
if (lastToken && token.type === 'text' && lastToken.type === 'text') {
|
|
4639
|
+
lastToken.raw += token.raw;
|
|
4640
|
+
lastToken.text += token.text;
|
|
4641
|
+
}
|
|
4642
|
+
else {
|
|
4643
|
+
tokens.push(token);
|
|
4644
|
+
}
|
|
4645
|
+
continue;
|
|
4646
|
+
}
|
|
4647
|
+
// em & strong
|
|
4648
|
+
if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) {
|
|
4649
|
+
src = src.substring(token.raw.length);
|
|
4650
|
+
tokens.push(token);
|
|
4651
|
+
continue;
|
|
4652
|
+
}
|
|
4653
|
+
// code
|
|
4654
|
+
if (token = this.tokenizer.codespan(src)) {
|
|
4655
|
+
src = src.substring(token.raw.length);
|
|
4656
|
+
tokens.push(token);
|
|
4657
|
+
continue;
|
|
4658
|
+
}
|
|
4659
|
+
// br
|
|
4660
|
+
if (token = this.tokenizer.br(src)) {
|
|
4661
|
+
src = src.substring(token.raw.length);
|
|
4662
|
+
tokens.push(token);
|
|
4663
|
+
continue;
|
|
4664
|
+
}
|
|
4665
|
+
// del (gfm)
|
|
4666
|
+
if (token = this.tokenizer.del(src)) {
|
|
4667
|
+
src = src.substring(token.raw.length);
|
|
4668
|
+
tokens.push(token);
|
|
4669
|
+
continue;
|
|
4670
|
+
}
|
|
4671
|
+
// autolink
|
|
4672
|
+
if (token = this.tokenizer.autolink(src)) {
|
|
4673
|
+
src = src.substring(token.raw.length);
|
|
4674
|
+
tokens.push(token);
|
|
4675
|
+
continue;
|
|
4676
|
+
}
|
|
4677
|
+
// url (gfm)
|
|
4678
|
+
if (!this.state.inLink && (token = this.tokenizer.url(src))) {
|
|
4679
|
+
src = src.substring(token.raw.length);
|
|
4680
|
+
tokens.push(token);
|
|
4681
|
+
continue;
|
|
4682
|
+
}
|
|
4683
|
+
// text
|
|
4684
|
+
// prevent inlineText consuming extensions by clipping 'src' to extension start
|
|
4685
|
+
cutSrc = src;
|
|
4686
|
+
if (this.options.extensions && this.options.extensions.startInline) {
|
|
4687
|
+
let startIndex = Infinity;
|
|
4688
|
+
const tempSrc = src.slice(1);
|
|
4689
|
+
let tempStart;
|
|
4690
|
+
this.options.extensions.startInline.forEach((getStartIndex) => {
|
|
4691
|
+
tempStart = getStartIndex.call({ lexer: this }, tempSrc);
|
|
4692
|
+
if (typeof tempStart === 'number' && tempStart >= 0) {
|
|
4693
|
+
startIndex = Math.min(startIndex, tempStart);
|
|
4694
|
+
}
|
|
4695
|
+
});
|
|
4696
|
+
if (startIndex < Infinity && startIndex >= 0) {
|
|
4697
|
+
cutSrc = src.substring(0, startIndex + 1);
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4700
|
+
if (token = this.tokenizer.inlineText(cutSrc)) {
|
|
4701
|
+
src = src.substring(token.raw.length);
|
|
4702
|
+
if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
|
|
4703
|
+
prevChar = token.raw.slice(-1);
|
|
4704
|
+
}
|
|
4705
|
+
keepPrevChar = true;
|
|
4706
|
+
lastToken = tokens[tokens.length - 1];
|
|
4707
|
+
if (lastToken && lastToken.type === 'text') {
|
|
4708
|
+
lastToken.raw += token.raw;
|
|
4709
|
+
lastToken.text += token.text;
|
|
4710
|
+
}
|
|
4711
|
+
else {
|
|
4712
|
+
tokens.push(token);
|
|
4713
|
+
}
|
|
4714
|
+
continue;
|
|
4715
|
+
}
|
|
4716
|
+
if (src) {
|
|
4717
|
+
const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
|
|
4718
|
+
if (this.options.silent) {
|
|
4719
|
+
console.error(errMsg);
|
|
4720
|
+
break;
|
|
4721
|
+
}
|
|
4722
|
+
else {
|
|
4723
|
+
throw new Error(errMsg);
|
|
4724
|
+
}
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
return tokens;
|
|
4728
|
+
}
|
|
4729
|
+
}
|
|
4730
|
+
|
|
4731
|
+
/**
|
|
4732
|
+
* Renderer
|
|
4733
|
+
*/
|
|
4734
|
+
class _Renderer {
|
|
4735
|
+
options;
|
|
4736
|
+
constructor(options) {
|
|
4737
|
+
this.options = options || _defaults;
|
|
4738
|
+
}
|
|
4739
|
+
code(code, infostring, escaped) {
|
|
4740
|
+
const lang = (infostring || '').match(/^\S*/)?.[0];
|
|
4741
|
+
code = code.replace(/\n$/, '') + '\n';
|
|
4742
|
+
if (!lang) {
|
|
4743
|
+
return '<pre><code>'
|
|
4744
|
+
+ (escaped ? code : escape$1(code, true))
|
|
4745
|
+
+ '</code></pre>\n';
|
|
4746
|
+
}
|
|
4747
|
+
return '<pre><code class="language-'
|
|
4748
|
+
+ escape$1(lang)
|
|
4749
|
+
+ '">'
|
|
4750
|
+
+ (escaped ? code : escape$1(code, true))
|
|
4751
|
+
+ '</code></pre>\n';
|
|
4752
|
+
}
|
|
4753
|
+
blockquote(quote) {
|
|
4754
|
+
return `<blockquote>\n${quote}</blockquote>\n`;
|
|
4755
|
+
}
|
|
4756
|
+
html(html, block) {
|
|
4757
|
+
return html;
|
|
4758
|
+
}
|
|
4759
|
+
heading(text, level, raw) {
|
|
4760
|
+
// ignore IDs
|
|
4761
|
+
return `<h${level}>${text}</h${level}>\n`;
|
|
4762
|
+
}
|
|
4763
|
+
hr() {
|
|
4764
|
+
return '<hr>\n';
|
|
4765
|
+
}
|
|
4766
|
+
list(body, ordered, start) {
|
|
4767
|
+
const type = ordered ? 'ol' : 'ul';
|
|
4768
|
+
const startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
|
|
4769
|
+
return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
|
|
4770
|
+
}
|
|
4771
|
+
listitem(text, task, checked) {
|
|
4772
|
+
return `<li>${text}</li>\n`;
|
|
4773
|
+
}
|
|
4774
|
+
checkbox(checked) {
|
|
4775
|
+
return '<input '
|
|
4776
|
+
+ (checked ? 'checked="" ' : '')
|
|
4777
|
+
+ 'disabled="" type="checkbox">';
|
|
4778
|
+
}
|
|
4779
|
+
paragraph(text) {
|
|
4780
|
+
return `<p>${text}</p>\n`;
|
|
4781
|
+
}
|
|
4782
|
+
table(header, body) {
|
|
4783
|
+
if (body)
|
|
4784
|
+
body = `<tbody>${body}</tbody>`;
|
|
4785
|
+
return '<table>\n'
|
|
4786
|
+
+ '<thead>\n'
|
|
4787
|
+
+ header
|
|
4788
|
+
+ '</thead>\n'
|
|
4789
|
+
+ body
|
|
4790
|
+
+ '</table>\n';
|
|
4791
|
+
}
|
|
4792
|
+
tablerow(content) {
|
|
4793
|
+
return `<tr>\n${content}</tr>\n`;
|
|
4794
|
+
}
|
|
4795
|
+
tablecell(content, flags) {
|
|
4796
|
+
const type = flags.header ? 'th' : 'td';
|
|
4797
|
+
const tag = flags.align
|
|
4798
|
+
? `<${type} align="${flags.align}">`
|
|
4799
|
+
: `<${type}>`;
|
|
4800
|
+
return tag + content + `</${type}>\n`;
|
|
4801
|
+
}
|
|
4802
|
+
/**
|
|
4803
|
+
* span level renderer
|
|
4804
|
+
*/
|
|
4805
|
+
strong(text) {
|
|
4806
|
+
return `<strong>${text}</strong>`;
|
|
4807
|
+
}
|
|
4808
|
+
em(text) {
|
|
4809
|
+
return `<em>${text}</em>`;
|
|
4810
|
+
}
|
|
4811
|
+
codespan(text) {
|
|
4812
|
+
return `<code>${text}</code>`;
|
|
4813
|
+
}
|
|
4814
|
+
br() {
|
|
4815
|
+
return '<br>';
|
|
4816
|
+
}
|
|
4817
|
+
del(text) {
|
|
4818
|
+
return `<del>${text}</del>`;
|
|
4819
|
+
}
|
|
4820
|
+
link(href, title, text) {
|
|
4821
|
+
const cleanHref = cleanUrl(href);
|
|
4822
|
+
if (cleanHref === null) {
|
|
4823
|
+
return text;
|
|
4824
|
+
}
|
|
4825
|
+
href = cleanHref;
|
|
4826
|
+
let out = '<a href="' + href + '"';
|
|
4827
|
+
if (title) {
|
|
4828
|
+
out += ' title="' + title + '"';
|
|
4829
|
+
}
|
|
4830
|
+
out += '>' + text + '</a>';
|
|
4831
|
+
return out;
|
|
4832
|
+
}
|
|
4833
|
+
image(href, title, text) {
|
|
4834
|
+
const cleanHref = cleanUrl(href);
|
|
4835
|
+
if (cleanHref === null) {
|
|
4836
|
+
return text;
|
|
4837
|
+
}
|
|
4838
|
+
href = cleanHref;
|
|
4839
|
+
let out = `<img src="${href}" alt="${text}"`;
|
|
4840
|
+
if (title) {
|
|
4841
|
+
out += ` title="${title}"`;
|
|
4842
|
+
}
|
|
4843
|
+
out += '>';
|
|
4844
|
+
return out;
|
|
4845
|
+
}
|
|
4846
|
+
text(text) {
|
|
4847
|
+
return text;
|
|
4848
|
+
}
|
|
4849
|
+
}
|
|
4850
|
+
|
|
4851
|
+
/**
|
|
4852
|
+
* TextRenderer
|
|
4853
|
+
* returns only the textual part of the token
|
|
4854
|
+
*/
|
|
4855
|
+
class _TextRenderer {
|
|
4856
|
+
// no need for block level renderers
|
|
4857
|
+
strong(text) {
|
|
4858
|
+
return text;
|
|
4859
|
+
}
|
|
4860
|
+
em(text) {
|
|
4861
|
+
return text;
|
|
4862
|
+
}
|
|
4863
|
+
codespan(text) {
|
|
4864
|
+
return text;
|
|
4865
|
+
}
|
|
4866
|
+
del(text) {
|
|
4867
|
+
return text;
|
|
4868
|
+
}
|
|
4869
|
+
html(text) {
|
|
4870
|
+
return text;
|
|
4871
|
+
}
|
|
4872
|
+
text(text) {
|
|
4873
|
+
return text;
|
|
4874
|
+
}
|
|
4875
|
+
link(href, title, text) {
|
|
4876
|
+
return '' + text;
|
|
4877
|
+
}
|
|
4878
|
+
image(href, title, text) {
|
|
4879
|
+
return '' + text;
|
|
4880
|
+
}
|
|
4881
|
+
br() {
|
|
4882
|
+
return '';
|
|
4883
|
+
}
|
|
4884
|
+
}
|
|
4885
|
+
|
|
4886
|
+
/**
|
|
4887
|
+
* Parsing & Compiling
|
|
4888
|
+
*/
|
|
4889
|
+
class _Parser {
|
|
4890
|
+
options;
|
|
4891
|
+
renderer;
|
|
4892
|
+
textRenderer;
|
|
4893
|
+
constructor(options) {
|
|
4894
|
+
this.options = options || _defaults;
|
|
4895
|
+
this.options.renderer = this.options.renderer || new _Renderer();
|
|
4896
|
+
this.renderer = this.options.renderer;
|
|
4897
|
+
this.renderer.options = this.options;
|
|
4898
|
+
this.textRenderer = new _TextRenderer();
|
|
4899
|
+
}
|
|
4900
|
+
/**
|
|
4901
|
+
* Static Parse Method
|
|
4902
|
+
*/
|
|
4903
|
+
static parse(tokens, options) {
|
|
4904
|
+
const parser = new _Parser(options);
|
|
4905
|
+
return parser.parse(tokens);
|
|
4906
|
+
}
|
|
4907
|
+
/**
|
|
4908
|
+
* Static Parse Inline Method
|
|
4909
|
+
*/
|
|
4910
|
+
static parseInline(tokens, options) {
|
|
4911
|
+
const parser = new _Parser(options);
|
|
4912
|
+
return parser.parseInline(tokens);
|
|
4913
|
+
}
|
|
4914
|
+
/**
|
|
4915
|
+
* Parse Loop
|
|
4916
|
+
*/
|
|
4917
|
+
parse(tokens, top = true) {
|
|
4918
|
+
let out = '';
|
|
4919
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
4920
|
+
const token = tokens[i];
|
|
4921
|
+
// Run any renderer extensions
|
|
4922
|
+
if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
|
|
4923
|
+
const genericToken = token;
|
|
4924
|
+
const ret = this.options.extensions.renderers[genericToken.type].call({ parser: this }, genericToken);
|
|
4925
|
+
if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'paragraph', 'text'].includes(genericToken.type)) {
|
|
4926
|
+
out += ret || '';
|
|
4927
|
+
continue;
|
|
4928
|
+
}
|
|
4929
|
+
}
|
|
4930
|
+
switch (token.type) {
|
|
4931
|
+
case 'space': {
|
|
4932
|
+
continue;
|
|
4933
|
+
}
|
|
4934
|
+
case 'hr': {
|
|
4935
|
+
out += this.renderer.hr();
|
|
4936
|
+
continue;
|
|
4937
|
+
}
|
|
4938
|
+
case 'heading': {
|
|
4939
|
+
const headingToken = token;
|
|
4940
|
+
out += this.renderer.heading(this.parseInline(headingToken.tokens), headingToken.depth, unescape(this.parseInline(headingToken.tokens, this.textRenderer)));
|
|
4941
|
+
continue;
|
|
4942
|
+
}
|
|
4943
|
+
case 'code': {
|
|
4944
|
+
const codeToken = token;
|
|
4945
|
+
out += this.renderer.code(codeToken.text, codeToken.lang, !!codeToken.escaped);
|
|
4946
|
+
continue;
|
|
4947
|
+
}
|
|
4948
|
+
case 'table': {
|
|
4949
|
+
const tableToken = token;
|
|
4950
|
+
let header = '';
|
|
4951
|
+
// header
|
|
4952
|
+
let cell = '';
|
|
4953
|
+
for (let j = 0; j < tableToken.header.length; j++) {
|
|
4954
|
+
cell += this.renderer.tablecell(this.parseInline(tableToken.header[j].tokens), { header: true, align: tableToken.align[j] });
|
|
4955
|
+
}
|
|
4956
|
+
header += this.renderer.tablerow(cell);
|
|
4957
|
+
let body = '';
|
|
4958
|
+
for (let j = 0; j < tableToken.rows.length; j++) {
|
|
4959
|
+
const row = tableToken.rows[j];
|
|
4960
|
+
cell = '';
|
|
4961
|
+
for (let k = 0; k < row.length; k++) {
|
|
4962
|
+
cell += this.renderer.tablecell(this.parseInline(row[k].tokens), { header: false, align: tableToken.align[k] });
|
|
4963
|
+
}
|
|
4964
|
+
body += this.renderer.tablerow(cell);
|
|
4965
|
+
}
|
|
4966
|
+
out += this.renderer.table(header, body);
|
|
4967
|
+
continue;
|
|
4968
|
+
}
|
|
4969
|
+
case 'blockquote': {
|
|
4970
|
+
const blockquoteToken = token;
|
|
4971
|
+
const body = this.parse(blockquoteToken.tokens);
|
|
4972
|
+
out += this.renderer.blockquote(body);
|
|
4973
|
+
continue;
|
|
4974
|
+
}
|
|
4975
|
+
case 'list': {
|
|
4976
|
+
const listToken = token;
|
|
4977
|
+
const ordered = listToken.ordered;
|
|
4978
|
+
const start = listToken.start;
|
|
4979
|
+
const loose = listToken.loose;
|
|
4980
|
+
let body = '';
|
|
4981
|
+
for (let j = 0; j < listToken.items.length; j++) {
|
|
4982
|
+
const item = listToken.items[j];
|
|
4983
|
+
const checked = item.checked;
|
|
4984
|
+
const task = item.task;
|
|
4985
|
+
let itemBody = '';
|
|
4986
|
+
if (item.task) {
|
|
4987
|
+
const checkbox = this.renderer.checkbox(!!checked);
|
|
4988
|
+
if (loose) {
|
|
4989
|
+
if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') {
|
|
4990
|
+
item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
|
|
4991
|
+
if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
|
|
4992
|
+
item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
|
|
4993
|
+
}
|
|
4994
|
+
}
|
|
4995
|
+
else {
|
|
4996
|
+
item.tokens.unshift({
|
|
4997
|
+
type: 'text',
|
|
4998
|
+
text: checkbox + ' '
|
|
4999
|
+
});
|
|
5000
|
+
}
|
|
5001
|
+
}
|
|
5002
|
+
else {
|
|
5003
|
+
itemBody += checkbox + ' ';
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
itemBody += this.parse(item.tokens, loose);
|
|
5007
|
+
body += this.renderer.listitem(itemBody, task, !!checked);
|
|
5008
|
+
}
|
|
5009
|
+
out += this.renderer.list(body, ordered, start);
|
|
5010
|
+
continue;
|
|
5011
|
+
}
|
|
5012
|
+
case 'html': {
|
|
5013
|
+
const htmlToken = token;
|
|
5014
|
+
out += this.renderer.html(htmlToken.text, htmlToken.block);
|
|
5015
|
+
continue;
|
|
5016
|
+
}
|
|
5017
|
+
case 'paragraph': {
|
|
5018
|
+
const paragraphToken = token;
|
|
5019
|
+
out += this.renderer.paragraph(this.parseInline(paragraphToken.tokens));
|
|
5020
|
+
continue;
|
|
5021
|
+
}
|
|
5022
|
+
case 'text': {
|
|
5023
|
+
let textToken = token;
|
|
5024
|
+
let body = textToken.tokens ? this.parseInline(textToken.tokens) : textToken.text;
|
|
5025
|
+
while (i + 1 < tokens.length && tokens[i + 1].type === 'text') {
|
|
5026
|
+
textToken = tokens[++i];
|
|
5027
|
+
body += '\n' + (textToken.tokens ? this.parseInline(textToken.tokens) : textToken.text);
|
|
5028
|
+
}
|
|
5029
|
+
out += top ? this.renderer.paragraph(body) : body;
|
|
5030
|
+
continue;
|
|
5031
|
+
}
|
|
5032
|
+
default: {
|
|
5033
|
+
const errMsg = 'Token with "' + token.type + '" type was not found.';
|
|
5034
|
+
if (this.options.silent) {
|
|
5035
|
+
console.error(errMsg);
|
|
5036
|
+
return '';
|
|
5037
|
+
}
|
|
5038
|
+
else {
|
|
5039
|
+
throw new Error(errMsg);
|
|
5040
|
+
}
|
|
5041
|
+
}
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
return out;
|
|
5045
|
+
}
|
|
5046
|
+
/**
|
|
5047
|
+
* Parse Inline Tokens
|
|
5048
|
+
*/
|
|
5049
|
+
parseInline(tokens, renderer) {
|
|
5050
|
+
renderer = renderer || this.renderer;
|
|
5051
|
+
let out = '';
|
|
5052
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
5053
|
+
const token = tokens[i];
|
|
5054
|
+
// Run any renderer extensions
|
|
5055
|
+
if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
|
|
5056
|
+
const ret = this.options.extensions.renderers[token.type].call({ parser: this }, token);
|
|
5057
|
+
if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(token.type)) {
|
|
5058
|
+
out += ret || '';
|
|
5059
|
+
continue;
|
|
5060
|
+
}
|
|
5061
|
+
}
|
|
5062
|
+
switch (token.type) {
|
|
5063
|
+
case 'escape': {
|
|
5064
|
+
const escapeToken = token;
|
|
5065
|
+
out += renderer.text(escapeToken.text);
|
|
5066
|
+
break;
|
|
5067
|
+
}
|
|
5068
|
+
case 'html': {
|
|
5069
|
+
const tagToken = token;
|
|
5070
|
+
out += renderer.html(tagToken.text);
|
|
5071
|
+
break;
|
|
5072
|
+
}
|
|
5073
|
+
case 'link': {
|
|
5074
|
+
const linkToken = token;
|
|
5075
|
+
out += renderer.link(linkToken.href, linkToken.title, this.parseInline(linkToken.tokens, renderer));
|
|
5076
|
+
break;
|
|
5077
|
+
}
|
|
5078
|
+
case 'image': {
|
|
5079
|
+
const imageToken = token;
|
|
5080
|
+
out += renderer.image(imageToken.href, imageToken.title, imageToken.text);
|
|
5081
|
+
break;
|
|
5082
|
+
}
|
|
5083
|
+
case 'strong': {
|
|
5084
|
+
const strongToken = token;
|
|
5085
|
+
out += renderer.strong(this.parseInline(strongToken.tokens, renderer));
|
|
5086
|
+
break;
|
|
5087
|
+
}
|
|
5088
|
+
case 'em': {
|
|
5089
|
+
const emToken = token;
|
|
5090
|
+
out += renderer.em(this.parseInline(emToken.tokens, renderer));
|
|
5091
|
+
break;
|
|
5092
|
+
}
|
|
5093
|
+
case 'codespan': {
|
|
5094
|
+
const codespanToken = token;
|
|
5095
|
+
out += renderer.codespan(codespanToken.text);
|
|
5096
|
+
break;
|
|
5097
|
+
}
|
|
5098
|
+
case 'br': {
|
|
5099
|
+
out += renderer.br();
|
|
5100
|
+
break;
|
|
5101
|
+
}
|
|
5102
|
+
case 'del': {
|
|
5103
|
+
const delToken = token;
|
|
5104
|
+
out += renderer.del(this.parseInline(delToken.tokens, renderer));
|
|
5105
|
+
break;
|
|
5106
|
+
}
|
|
5107
|
+
case 'text': {
|
|
5108
|
+
const textToken = token;
|
|
5109
|
+
out += renderer.text(textToken.text);
|
|
5110
|
+
break;
|
|
5111
|
+
}
|
|
5112
|
+
default: {
|
|
5113
|
+
const errMsg = 'Token with "' + token.type + '" type was not found.';
|
|
5114
|
+
if (this.options.silent) {
|
|
5115
|
+
console.error(errMsg);
|
|
5116
|
+
return '';
|
|
5117
|
+
}
|
|
5118
|
+
else {
|
|
5119
|
+
throw new Error(errMsg);
|
|
5120
|
+
}
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5123
|
+
}
|
|
5124
|
+
return out;
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
|
|
5128
|
+
class _Hooks {
|
|
5129
|
+
options;
|
|
5130
|
+
constructor(options) {
|
|
5131
|
+
this.options = options || _defaults;
|
|
5132
|
+
}
|
|
5133
|
+
static passThroughHooks = new Set([
|
|
5134
|
+
'preprocess',
|
|
5135
|
+
'postprocess',
|
|
5136
|
+
'processAllTokens'
|
|
5137
|
+
]);
|
|
5138
|
+
/**
|
|
5139
|
+
* Process markdown before marked
|
|
5140
|
+
*/
|
|
5141
|
+
preprocess(markdown) {
|
|
5142
|
+
return markdown;
|
|
5143
|
+
}
|
|
5144
|
+
/**
|
|
5145
|
+
* Process HTML after marked is finished
|
|
5146
|
+
*/
|
|
5147
|
+
postprocess(html) {
|
|
5148
|
+
return html;
|
|
5149
|
+
}
|
|
5150
|
+
/**
|
|
5151
|
+
* Process all tokens before walk tokens
|
|
5152
|
+
*/
|
|
5153
|
+
processAllTokens(tokens) {
|
|
5154
|
+
return tokens;
|
|
5155
|
+
}
|
|
5156
|
+
}
|
|
5157
|
+
|
|
5158
|
+
class Marked {
|
|
5159
|
+
defaults = _getDefaults();
|
|
5160
|
+
options = this.setOptions;
|
|
5161
|
+
parse = this.#parseMarkdown(_Lexer.lex, _Parser.parse);
|
|
5162
|
+
parseInline = this.#parseMarkdown(_Lexer.lexInline, _Parser.parseInline);
|
|
5163
|
+
Parser = _Parser;
|
|
5164
|
+
Renderer = _Renderer;
|
|
5165
|
+
TextRenderer = _TextRenderer;
|
|
5166
|
+
Lexer = _Lexer;
|
|
5167
|
+
Tokenizer = _Tokenizer;
|
|
5168
|
+
Hooks = _Hooks;
|
|
5169
|
+
constructor(...args) {
|
|
5170
|
+
this.use(...args);
|
|
5171
|
+
}
|
|
5172
|
+
/**
|
|
5173
|
+
* Run callback for every token
|
|
5174
|
+
*/
|
|
5175
|
+
walkTokens(tokens, callback) {
|
|
5176
|
+
let values = [];
|
|
5177
|
+
for (const token of tokens) {
|
|
5178
|
+
values = values.concat(callback.call(this, token));
|
|
5179
|
+
switch (token.type) {
|
|
5180
|
+
case 'table': {
|
|
5181
|
+
const tableToken = token;
|
|
5182
|
+
for (const cell of tableToken.header) {
|
|
5183
|
+
values = values.concat(this.walkTokens(cell.tokens, callback));
|
|
5184
|
+
}
|
|
5185
|
+
for (const row of tableToken.rows) {
|
|
5186
|
+
for (const cell of row) {
|
|
5187
|
+
values = values.concat(this.walkTokens(cell.tokens, callback));
|
|
5188
|
+
}
|
|
5189
|
+
}
|
|
5190
|
+
break;
|
|
5191
|
+
}
|
|
5192
|
+
case 'list': {
|
|
5193
|
+
const listToken = token;
|
|
5194
|
+
values = values.concat(this.walkTokens(listToken.items, callback));
|
|
5195
|
+
break;
|
|
5196
|
+
}
|
|
5197
|
+
default: {
|
|
5198
|
+
const genericToken = token;
|
|
5199
|
+
if (this.defaults.extensions?.childTokens?.[genericToken.type]) {
|
|
5200
|
+
this.defaults.extensions.childTokens[genericToken.type].forEach((childTokens) => {
|
|
5201
|
+
const tokens = genericToken[childTokens].flat(Infinity);
|
|
5202
|
+
values = values.concat(this.walkTokens(tokens, callback));
|
|
5203
|
+
});
|
|
5204
|
+
}
|
|
5205
|
+
else if (genericToken.tokens) {
|
|
5206
|
+
values = values.concat(this.walkTokens(genericToken.tokens, callback));
|
|
5207
|
+
}
|
|
5208
|
+
}
|
|
5209
|
+
}
|
|
5210
|
+
}
|
|
5211
|
+
return values;
|
|
5212
|
+
}
|
|
5213
|
+
use(...args) {
|
|
5214
|
+
const extensions = this.defaults.extensions || { renderers: {}, childTokens: {} };
|
|
5215
|
+
args.forEach((pack) => {
|
|
5216
|
+
// copy options to new object
|
|
5217
|
+
const opts = { ...pack };
|
|
5218
|
+
// set async to true if it was set to true before
|
|
5219
|
+
opts.async = this.defaults.async || opts.async || false;
|
|
5220
|
+
// ==-- Parse "addon" extensions --== //
|
|
5221
|
+
if (pack.extensions) {
|
|
5222
|
+
pack.extensions.forEach((ext) => {
|
|
5223
|
+
if (!ext.name) {
|
|
5224
|
+
throw new Error('extension name required');
|
|
5225
|
+
}
|
|
5226
|
+
if ('renderer' in ext) { // Renderer extensions
|
|
5227
|
+
const prevRenderer = extensions.renderers[ext.name];
|
|
5228
|
+
if (prevRenderer) {
|
|
5229
|
+
// Replace extension with func to run new extension but fall back if false
|
|
5230
|
+
extensions.renderers[ext.name] = function (...args) {
|
|
5231
|
+
let ret = ext.renderer.apply(this, args);
|
|
5232
|
+
if (ret === false) {
|
|
5233
|
+
ret = prevRenderer.apply(this, args);
|
|
5234
|
+
}
|
|
5235
|
+
return ret;
|
|
5236
|
+
};
|
|
5237
|
+
}
|
|
5238
|
+
else {
|
|
5239
|
+
extensions.renderers[ext.name] = ext.renderer;
|
|
5240
|
+
}
|
|
5241
|
+
}
|
|
5242
|
+
if ('tokenizer' in ext) { // Tokenizer Extensions
|
|
5243
|
+
if (!ext.level || (ext.level !== 'block' && ext.level !== 'inline')) {
|
|
5244
|
+
throw new Error("extension level must be 'block' or 'inline'");
|
|
5245
|
+
}
|
|
5246
|
+
const extLevel = extensions[ext.level];
|
|
5247
|
+
if (extLevel) {
|
|
5248
|
+
extLevel.unshift(ext.tokenizer);
|
|
5249
|
+
}
|
|
5250
|
+
else {
|
|
5251
|
+
extensions[ext.level] = [ext.tokenizer];
|
|
5252
|
+
}
|
|
5253
|
+
if (ext.start) { // Function to check for start of token
|
|
5254
|
+
if (ext.level === 'block') {
|
|
5255
|
+
if (extensions.startBlock) {
|
|
5256
|
+
extensions.startBlock.push(ext.start);
|
|
5257
|
+
}
|
|
5258
|
+
else {
|
|
5259
|
+
extensions.startBlock = [ext.start];
|
|
5260
|
+
}
|
|
5261
|
+
}
|
|
5262
|
+
else if (ext.level === 'inline') {
|
|
5263
|
+
if (extensions.startInline) {
|
|
5264
|
+
extensions.startInline.push(ext.start);
|
|
5265
|
+
}
|
|
5266
|
+
else {
|
|
5267
|
+
extensions.startInline = [ext.start];
|
|
5268
|
+
}
|
|
5269
|
+
}
|
|
5270
|
+
}
|
|
5271
|
+
}
|
|
5272
|
+
if ('childTokens' in ext && ext.childTokens) { // Child tokens to be visited by walkTokens
|
|
5273
|
+
extensions.childTokens[ext.name] = ext.childTokens;
|
|
5274
|
+
}
|
|
5275
|
+
});
|
|
5276
|
+
opts.extensions = extensions;
|
|
5277
|
+
}
|
|
5278
|
+
// ==-- Parse "overwrite" extensions --== //
|
|
5279
|
+
if (pack.renderer) {
|
|
5280
|
+
const renderer = this.defaults.renderer || new _Renderer(this.defaults);
|
|
5281
|
+
for (const prop in pack.renderer) {
|
|
5282
|
+
if (!(prop in renderer)) {
|
|
5283
|
+
throw new Error(`renderer '${prop}' does not exist`);
|
|
5284
|
+
}
|
|
5285
|
+
if (prop === 'options') {
|
|
5286
|
+
// ignore options property
|
|
5287
|
+
continue;
|
|
5288
|
+
}
|
|
5289
|
+
const rendererProp = prop;
|
|
5290
|
+
const rendererFunc = pack.renderer[rendererProp];
|
|
5291
|
+
const prevRenderer = renderer[rendererProp];
|
|
5292
|
+
// Replace renderer with func to run extension, but fall back if false
|
|
5293
|
+
renderer[rendererProp] = (...args) => {
|
|
5294
|
+
let ret = rendererFunc.apply(renderer, args);
|
|
5295
|
+
if (ret === false) {
|
|
5296
|
+
ret = prevRenderer.apply(renderer, args);
|
|
5297
|
+
}
|
|
5298
|
+
return ret || '';
|
|
5299
|
+
};
|
|
5300
|
+
}
|
|
5301
|
+
opts.renderer = renderer;
|
|
5302
|
+
}
|
|
5303
|
+
if (pack.tokenizer) {
|
|
5304
|
+
const tokenizer = this.defaults.tokenizer || new _Tokenizer(this.defaults);
|
|
5305
|
+
for (const prop in pack.tokenizer) {
|
|
5306
|
+
if (!(prop in tokenizer)) {
|
|
5307
|
+
throw new Error(`tokenizer '${prop}' does not exist`);
|
|
5308
|
+
}
|
|
5309
|
+
if (['options', 'rules', 'lexer'].includes(prop)) {
|
|
5310
|
+
// ignore options, rules, and lexer properties
|
|
5311
|
+
continue;
|
|
5312
|
+
}
|
|
5313
|
+
const tokenizerProp = prop;
|
|
5314
|
+
const tokenizerFunc = pack.tokenizer[tokenizerProp];
|
|
5315
|
+
const prevTokenizer = tokenizer[tokenizerProp];
|
|
5316
|
+
// Replace tokenizer with func to run extension, but fall back if false
|
|
5317
|
+
// @ts-expect-error cannot type tokenizer function dynamically
|
|
5318
|
+
tokenizer[tokenizerProp] = (...args) => {
|
|
5319
|
+
let ret = tokenizerFunc.apply(tokenizer, args);
|
|
5320
|
+
if (ret === false) {
|
|
5321
|
+
ret = prevTokenizer.apply(tokenizer, args);
|
|
5322
|
+
}
|
|
5323
|
+
return ret;
|
|
5324
|
+
};
|
|
5325
|
+
}
|
|
5326
|
+
opts.tokenizer = tokenizer;
|
|
5327
|
+
}
|
|
5328
|
+
// ==-- Parse Hooks extensions --== //
|
|
5329
|
+
if (pack.hooks) {
|
|
5330
|
+
const hooks = this.defaults.hooks || new _Hooks();
|
|
5331
|
+
for (const prop in pack.hooks) {
|
|
5332
|
+
if (!(prop in hooks)) {
|
|
5333
|
+
throw new Error(`hook '${prop}' does not exist`);
|
|
5334
|
+
}
|
|
5335
|
+
if (prop === 'options') {
|
|
5336
|
+
// ignore options property
|
|
5337
|
+
continue;
|
|
5338
|
+
}
|
|
5339
|
+
const hooksProp = prop;
|
|
5340
|
+
const hooksFunc = pack.hooks[hooksProp];
|
|
5341
|
+
const prevHook = hooks[hooksProp];
|
|
5342
|
+
if (_Hooks.passThroughHooks.has(prop)) {
|
|
5343
|
+
// @ts-expect-error cannot type hook function dynamically
|
|
5344
|
+
hooks[hooksProp] = (arg) => {
|
|
5345
|
+
if (this.defaults.async) {
|
|
5346
|
+
return Promise.resolve(hooksFunc.call(hooks, arg)).then(ret => {
|
|
5347
|
+
return prevHook.call(hooks, ret);
|
|
5348
|
+
});
|
|
5349
|
+
}
|
|
5350
|
+
const ret = hooksFunc.call(hooks, arg);
|
|
5351
|
+
return prevHook.call(hooks, ret);
|
|
5352
|
+
};
|
|
5353
|
+
}
|
|
5354
|
+
else {
|
|
5355
|
+
// @ts-expect-error cannot type hook function dynamically
|
|
5356
|
+
hooks[hooksProp] = (...args) => {
|
|
5357
|
+
let ret = hooksFunc.apply(hooks, args);
|
|
5358
|
+
if (ret === false) {
|
|
5359
|
+
ret = prevHook.apply(hooks, args);
|
|
5360
|
+
}
|
|
5361
|
+
return ret;
|
|
5362
|
+
};
|
|
5363
|
+
}
|
|
5364
|
+
}
|
|
5365
|
+
opts.hooks = hooks;
|
|
5366
|
+
}
|
|
5367
|
+
// ==-- Parse WalkTokens extensions --== //
|
|
5368
|
+
if (pack.walkTokens) {
|
|
5369
|
+
const walkTokens = this.defaults.walkTokens;
|
|
5370
|
+
const packWalktokens = pack.walkTokens;
|
|
5371
|
+
opts.walkTokens = function (token) {
|
|
5372
|
+
let values = [];
|
|
5373
|
+
values.push(packWalktokens.call(this, token));
|
|
5374
|
+
if (walkTokens) {
|
|
5375
|
+
values = values.concat(walkTokens.call(this, token));
|
|
5376
|
+
}
|
|
5377
|
+
return values;
|
|
5378
|
+
};
|
|
5379
|
+
}
|
|
5380
|
+
this.defaults = { ...this.defaults, ...opts };
|
|
5381
|
+
});
|
|
5382
|
+
return this;
|
|
5383
|
+
}
|
|
5384
|
+
setOptions(opt) {
|
|
5385
|
+
this.defaults = { ...this.defaults, ...opt };
|
|
5386
|
+
return this;
|
|
5387
|
+
}
|
|
5388
|
+
lexer(src, options) {
|
|
5389
|
+
return _Lexer.lex(src, options ?? this.defaults);
|
|
5390
|
+
}
|
|
5391
|
+
parser(tokens, options) {
|
|
5392
|
+
return _Parser.parse(tokens, options ?? this.defaults);
|
|
5393
|
+
}
|
|
5394
|
+
#parseMarkdown(lexer, parser) {
|
|
5395
|
+
return (src, options) => {
|
|
5396
|
+
const origOpt = { ...options };
|
|
5397
|
+
const opt = { ...this.defaults, ...origOpt };
|
|
5398
|
+
// Show warning if an extension set async to true but the parse was called with async: false
|
|
5399
|
+
if (this.defaults.async === true && origOpt.async === false) {
|
|
5400
|
+
if (!opt.silent) {
|
|
5401
|
+
console.warn('marked(): The async option was set to true by an extension. The async: false option sent to parse will be ignored.');
|
|
5402
|
+
}
|
|
5403
|
+
opt.async = true;
|
|
5404
|
+
}
|
|
5405
|
+
const throwError = this.#onError(!!opt.silent, !!opt.async);
|
|
5406
|
+
// throw error in case of non string input
|
|
5407
|
+
if (typeof src === 'undefined' || src === null) {
|
|
5408
|
+
return throwError(new Error('marked(): input parameter is undefined or null'));
|
|
5409
|
+
}
|
|
5410
|
+
if (typeof src !== 'string') {
|
|
5411
|
+
return throwError(new Error('marked(): input parameter is of type '
|
|
5412
|
+
+ Object.prototype.toString.call(src) + ', string expected'));
|
|
5413
|
+
}
|
|
5414
|
+
if (opt.hooks) {
|
|
5415
|
+
opt.hooks.options = opt;
|
|
5416
|
+
}
|
|
5417
|
+
if (opt.async) {
|
|
5418
|
+
return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src)
|
|
5419
|
+
.then(src => lexer(src, opt))
|
|
5420
|
+
.then(tokens => opt.hooks ? opt.hooks.processAllTokens(tokens) : tokens)
|
|
5421
|
+
.then(tokens => opt.walkTokens ? Promise.all(this.walkTokens(tokens, opt.walkTokens)).then(() => tokens) : tokens)
|
|
5422
|
+
.then(tokens => parser(tokens, opt))
|
|
5423
|
+
.then(html => opt.hooks ? opt.hooks.postprocess(html) : html)
|
|
5424
|
+
.catch(throwError);
|
|
5425
|
+
}
|
|
5426
|
+
try {
|
|
5427
|
+
if (opt.hooks) {
|
|
5428
|
+
src = opt.hooks.preprocess(src);
|
|
5429
|
+
}
|
|
5430
|
+
let tokens = lexer(src, opt);
|
|
5431
|
+
if (opt.hooks) {
|
|
5432
|
+
tokens = opt.hooks.processAllTokens(tokens);
|
|
5433
|
+
}
|
|
5434
|
+
if (opt.walkTokens) {
|
|
5435
|
+
this.walkTokens(tokens, opt.walkTokens);
|
|
5436
|
+
}
|
|
5437
|
+
let html = parser(tokens, opt);
|
|
5438
|
+
if (opt.hooks) {
|
|
5439
|
+
html = opt.hooks.postprocess(html);
|
|
5440
|
+
}
|
|
5441
|
+
return html;
|
|
5442
|
+
}
|
|
5443
|
+
catch (e) {
|
|
5444
|
+
return throwError(e);
|
|
5445
|
+
}
|
|
5446
|
+
};
|
|
5447
|
+
}
|
|
5448
|
+
#onError(silent, async) {
|
|
5449
|
+
return (e) => {
|
|
5450
|
+
e.message += '\nPlease report this to https://github.com/markedjs/marked.';
|
|
5451
|
+
if (silent) {
|
|
5452
|
+
const msg = '<p>An error occurred:</p><pre>'
|
|
5453
|
+
+ escape$1(e.message + '', true)
|
|
5454
|
+
+ '</pre>';
|
|
5455
|
+
if (async) {
|
|
5456
|
+
return Promise.resolve(msg);
|
|
5457
|
+
}
|
|
5458
|
+
return msg;
|
|
5459
|
+
}
|
|
5460
|
+
if (async) {
|
|
5461
|
+
return Promise.reject(e);
|
|
5462
|
+
}
|
|
5463
|
+
throw e;
|
|
5464
|
+
};
|
|
5465
|
+
}
|
|
5466
|
+
}
|
|
5467
|
+
|
|
5468
|
+
const markedInstance = new Marked();
|
|
5469
|
+
function marked(src, opt) {
|
|
5470
|
+
return markedInstance.parse(src, opt);
|
|
5471
|
+
}
|
|
5472
|
+
/**
|
|
5473
|
+
* Sets the default options.
|
|
5474
|
+
*
|
|
5475
|
+
* @param options Hash of options
|
|
5476
|
+
*/
|
|
5477
|
+
marked.options =
|
|
5478
|
+
marked.setOptions = function (options) {
|
|
5479
|
+
markedInstance.setOptions(options);
|
|
5480
|
+
marked.defaults = markedInstance.defaults;
|
|
5481
|
+
changeDefaults(marked.defaults);
|
|
5482
|
+
return marked;
|
|
5483
|
+
};
|
|
5484
|
+
/**
|
|
5485
|
+
* Gets the original marked default options.
|
|
5486
|
+
*/
|
|
5487
|
+
marked.getDefaults = _getDefaults;
|
|
5488
|
+
marked.defaults = _defaults;
|
|
5489
|
+
/**
|
|
5490
|
+
* Use Extension
|
|
5491
|
+
*/
|
|
5492
|
+
marked.use = function (...args) {
|
|
5493
|
+
markedInstance.use(...args);
|
|
5494
|
+
marked.defaults = markedInstance.defaults;
|
|
5495
|
+
changeDefaults(marked.defaults);
|
|
5496
|
+
return marked;
|
|
5497
|
+
};
|
|
5498
|
+
/**
|
|
5499
|
+
* Run callback for every token
|
|
5500
|
+
*/
|
|
5501
|
+
marked.walkTokens = function (tokens, callback) {
|
|
5502
|
+
return markedInstance.walkTokens(tokens, callback);
|
|
5503
|
+
};
|
|
5504
|
+
/**
|
|
5505
|
+
* Compiles markdown to HTML without enclosing `p` tag.
|
|
5506
|
+
*
|
|
5507
|
+
* @param src String of markdown source to be compiled
|
|
5508
|
+
* @param options Hash of options
|
|
5509
|
+
* @return String of compiled HTML
|
|
5510
|
+
*/
|
|
5511
|
+
marked.parseInline = markedInstance.parseInline;
|
|
5512
|
+
/**
|
|
5513
|
+
* Expose
|
|
5514
|
+
*/
|
|
5515
|
+
marked.Parser = _Parser;
|
|
5516
|
+
marked.parser = _Parser.parse;
|
|
5517
|
+
marked.Renderer = _Renderer;
|
|
5518
|
+
marked.TextRenderer = _TextRenderer;
|
|
5519
|
+
marked.Lexer = _Lexer;
|
|
5520
|
+
marked.lexer = _Lexer.lex;
|
|
5521
|
+
marked.Tokenizer = _Tokenizer;
|
|
5522
|
+
marked.Hooks = _Hooks;
|
|
5523
|
+
marked.parse = marked;
|
|
5524
|
+
marked.options;
|
|
5525
|
+
marked.setOptions;
|
|
5526
|
+
marked.use;
|
|
5527
|
+
marked.walkTokens;
|
|
5528
|
+
marked.parseInline;
|
|
5529
|
+
_Parser.parse;
|
|
5530
|
+
_Lexer.lex;
|
|
5531
|
+
|
|
5532
|
+
var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
5533
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
5534
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5535
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5536
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5537
|
+
};
|
|
5538
|
+
/**
|
|
5539
|
+
* AI chatbot component with SSE streaming.
|
|
5540
|
+
*
|
|
5541
|
+
* Accepts a `send` callback that returns a streaming Response (SSE format).
|
|
5542
|
+
* Parses the stream for text tokens, status updates, and errors.
|
|
5543
|
+
* Dispatches a `stream-event` custom event for every SSE message so consumers can react.
|
|
5544
|
+
*
|
|
5545
|
+
* ## SSE Stream Format
|
|
5546
|
+
* The component expects `data:` lines with JSON payloads containing a `type` field:
|
|
5547
|
+
* - `STATUS` — `{ type: "STATUS", message: "Thinking..." }`
|
|
5548
|
+
* - `RESPONSE_PART` — `{ type: "RESPONSE_PART", message: "token text" }`
|
|
5549
|
+
* - `TOOL_RESULT` — `{ type: "TOOL_RESULT", message: "tool_name", data: {...} }`
|
|
5550
|
+
* - `ERROR` — `{ type: "ERROR", message: "Something went wrong" }`
|
|
5551
|
+
* - `HEARTBEAT` — `{ type: "HEARTBEAT" }` (ignored)
|
|
5552
|
+
*
|
|
5553
|
+
* The final event should include `finished: true` to signal stream completion.
|
|
5554
|
+
*
|
|
5555
|
+
* ## Usage
|
|
5556
|
+
* ```html
|
|
5557
|
+
* <kr-chatbot
|
|
5558
|
+
* title="AI Assistant"
|
|
5559
|
+
* .send=${(params) => {
|
|
5560
|
+
* return fetch('/api/ai/chat/stream', {
|
|
5561
|
+
* method: 'POST',
|
|
5562
|
+
* headers: { 'Content-Type': 'application/json', 'Accept': 'text/event-stream' },
|
|
5563
|
+
* credentials: 'include',
|
|
5564
|
+
* body: JSON.stringify({ userText: params.message })
|
|
5565
|
+
* });
|
|
5566
|
+
* }}
|
|
5567
|
+
* ></kr-chatbot>
|
|
5568
|
+
* ```
|
|
5569
|
+
*
|
|
5570
|
+
* @slot - Optional slot content displayed above the messages area
|
|
5571
|
+
*
|
|
5572
|
+
* @fires message-submit - When the user sends a message. Cancelable. Detail: `{ message: string }`
|
|
5573
|
+
* @fires stream-event - Dispatched for every SSE message received. Detail contains the parsed event data.
|
|
5574
|
+
*/
|
|
5575
|
+
exports.Chatbot = class KRChatbot extends i$1 {
|
|
5576
|
+
constructor() {
|
|
5577
|
+
super(...arguments);
|
|
5578
|
+
// --- Private properties ---
|
|
5579
|
+
this._reader = null;
|
|
5580
|
+
this._decoder = new TextDecoder();
|
|
5581
|
+
this._buffer = '';
|
|
5582
|
+
this._userHasScrolledUp = false;
|
|
5583
|
+
this._isDragging = false;
|
|
5584
|
+
this._isResizing = false;
|
|
5585
|
+
this._overlay = null;
|
|
5586
|
+
this._resizeDir = '';
|
|
5587
|
+
this._dragStartX = 0;
|
|
5588
|
+
this._dragStartY = 0;
|
|
5589
|
+
this._dragStartLeft = 0;
|
|
5590
|
+
this._dragStartTop = 0;
|
|
5591
|
+
this._resizeStartW = 0;
|
|
5592
|
+
this._resizeStartH = 0;
|
|
5593
|
+
// --- @property (public API) ---
|
|
5594
|
+
/**
|
|
5595
|
+
* Callback function for sending messages. Receives the user's message and
|
|
5596
|
+
* current conversation history. Must return a Response with a streaming SSE body.
|
|
5597
|
+
*/
|
|
5598
|
+
this.send = null;
|
|
5599
|
+
/**
|
|
5600
|
+
* Callback function called when the user clears the conversation.
|
|
5601
|
+
* Must return a Promise — messages are only cleared if it resolves.
|
|
5602
|
+
*/
|
|
5603
|
+
this.clear = null;
|
|
5604
|
+
/**
|
|
5605
|
+
* Callback function called on init to load existing conversation messages.
|
|
5606
|
+
* Must return a Promise that resolves with an array of KRChatbotMessage.
|
|
5607
|
+
*/
|
|
5608
|
+
this.load = null;
|
|
5609
|
+
/**
|
|
5610
|
+
* Conversation messages. Can be set externally for initial messages.
|
|
5611
|
+
* Updated internally as the conversation progresses.
|
|
5612
|
+
*/
|
|
5613
|
+
this.messages = [];
|
|
5614
|
+
/**
|
|
5615
|
+
* Header title text.
|
|
5616
|
+
*/
|
|
5617
|
+
this.title = 'Kat';
|
|
5618
|
+
/**
|
|
5619
|
+
* Header subtitle text (shown below title with a green status dot).
|
|
5620
|
+
*/
|
|
5621
|
+
this.subtitle = '';
|
|
5622
|
+
/**
|
|
5623
|
+
* Input placeholder text.
|
|
5624
|
+
*/
|
|
5625
|
+
this.placeholder = 'Type a message...';
|
|
5626
|
+
/**
|
|
5627
|
+
* Whether the chat panel is open. Only relevant in floating mode.
|
|
5628
|
+
*/
|
|
5629
|
+
this.opened = false;
|
|
3053
5630
|
// --- @state (internal) ---
|
|
3054
5631
|
this._inputValue = '';
|
|
3055
5632
|
this._isStreaming = false;
|
|
@@ -3060,9 +5637,8 @@
|
|
|
3060
5637
|
let newLeft = this._dragStartLeft + (e.clientX - this._dragStartX);
|
|
3061
5638
|
let newTop = this._dragStartTop + (e.clientY - this._dragStartY);
|
|
3062
5639
|
const w = this._panelEl.offsetWidth;
|
|
3063
|
-
const h = this._panelEl.offsetHeight;
|
|
3064
5640
|
newLeft = Math.max(-w + 48, Math.min(newLeft, window.innerWidth - 48));
|
|
3065
|
-
newTop = Math.max(
|
|
5641
|
+
newTop = Math.max(0, Math.min(newTop, window.innerHeight - 48));
|
|
3066
5642
|
this._panelEl.style.left = newLeft + 'px';
|
|
3067
5643
|
this._panelEl.style.top = newTop + 'px';
|
|
3068
5644
|
}
|
|
@@ -3081,21 +5657,15 @@
|
|
|
3081
5657
|
newW = Math.max(minW, Math.min(this._resizeStartW + dx, maxW));
|
|
3082
5658
|
}
|
|
3083
5659
|
if (this._resizeDir.includes('w')) {
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
newW = proposedW;
|
|
3087
|
-
newLeft = this._dragStartLeft + dx;
|
|
3088
|
-
}
|
|
5660
|
+
newW = Math.max(minW, Math.min(this._resizeStartW - dx, maxW));
|
|
5661
|
+
newLeft = this._dragStartLeft + (this._resizeStartW - newW);
|
|
3089
5662
|
}
|
|
3090
5663
|
if (this._resizeDir.includes('s')) {
|
|
3091
5664
|
newH = Math.max(minH, Math.min(this._resizeStartH + dy, maxH));
|
|
3092
5665
|
}
|
|
3093
5666
|
if (this._resizeDir.includes('n')) {
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
newH = proposedH;
|
|
3097
|
-
newTop = this._dragStartTop + dy;
|
|
3098
|
-
}
|
|
5667
|
+
newH = Math.max(minH, Math.min(this._resizeStartH - dy, maxH));
|
|
5668
|
+
newTop = this._dragStartTop + (this._resizeStartH - newH);
|
|
3099
5669
|
}
|
|
3100
5670
|
newLeft = Math.max(0, newLeft);
|
|
3101
5671
|
newTop = Math.max(0, newTop);
|
|
@@ -3108,17 +5678,30 @@
|
|
|
3108
5678
|
this._handleMouseUp = () => {
|
|
3109
5679
|
if (this._isDragging) {
|
|
3110
5680
|
this._isDragging = false;
|
|
3111
|
-
this._panelEl.classList.remove('
|
|
5681
|
+
this._panelEl.classList.remove('panel--dragging');
|
|
3112
5682
|
}
|
|
3113
5683
|
if (this._isResizing) {
|
|
3114
5684
|
this._isResizing = false;
|
|
3115
|
-
this._panelEl.classList.remove('
|
|
5685
|
+
this._panelEl.classList.remove('panel--resizing');
|
|
3116
5686
|
}
|
|
5687
|
+
this._hideOverlay();
|
|
3117
5688
|
document.removeEventListener('mousemove', this._handleMouseMove);
|
|
3118
5689
|
document.removeEventListener('mouseup', this._handleMouseUp);
|
|
5690
|
+
this._saveState();
|
|
3119
5691
|
};
|
|
3120
5692
|
}
|
|
3121
5693
|
// --- Lifecycle ---
|
|
5694
|
+
connectedCallback() {
|
|
5695
|
+
super.connectedCallback();
|
|
5696
|
+
if (this.load) {
|
|
5697
|
+
this.load().then((messages) => {
|
|
5698
|
+
if (messages && messages.length) {
|
|
5699
|
+
this.messages = messages;
|
|
5700
|
+
}
|
|
5701
|
+
}).catch(() => { });
|
|
5702
|
+
}
|
|
5703
|
+
this._restoreState();
|
|
5704
|
+
}
|
|
3122
5705
|
disconnectedCallback() {
|
|
3123
5706
|
super.disconnectedCallback();
|
|
3124
5707
|
if (this._reader) {
|
|
@@ -3130,7 +5713,7 @@
|
|
|
3130
5713
|
}
|
|
3131
5714
|
updated(changed) {
|
|
3132
5715
|
if (changed.has('messages') || changed.has('_statusText')) {
|
|
3133
|
-
if (!this._userHasScrolledUp && this._messagesEl) {
|
|
5716
|
+
if (!this._userHasScrolledUp && !this._isStreaming && this._messagesEl) {
|
|
3134
5717
|
requestAnimationFrame(() => {
|
|
3135
5718
|
if (this._messagesEl) {
|
|
3136
5719
|
this._messagesEl.scrollTop = this._messagesEl.scrollHeight;
|
|
@@ -3154,6 +5737,38 @@
|
|
|
3154
5737
|
this._handleStop();
|
|
3155
5738
|
}
|
|
3156
5739
|
// --- Private methods ---
|
|
5740
|
+
/**
|
|
5741
|
+
* Shows a full-viewport transparent overlay during drag/resize operations.
|
|
5742
|
+
*
|
|
5743
|
+
* This overlay solves a critical rendering issue when the chatbot is used inside
|
|
5744
|
+
* a page that contains iframes (e.g., the AI editor shell where the page content
|
|
5745
|
+
* is rendered in an iframe and the chatbot floats on top).
|
|
5746
|
+
*
|
|
5747
|
+
* Without the overlay, modifying the iframe's `pointer-events` style during drag
|
|
5748
|
+
* (to prevent it from swallowing mousemove events) causes Chrome to recalculate
|
|
5749
|
+
* compositing layers. This layer recalculation blocks the browser from repainting
|
|
5750
|
+
* the chatbot panel's style changes (width/height/position) even though they are
|
|
5751
|
+
* correctly applied to the DOM. The result is that the panel appears frozen while
|
|
5752
|
+
* the user drags, despite the inline styles updating on every mousemove.
|
|
5753
|
+
*
|
|
5754
|
+
* The overlay approach avoids this entirely: instead of touching the iframe's styles,
|
|
5755
|
+
* we place a transparent div (z-index 9999) above the iframe but below the chatbot
|
|
5756
|
+
* (z-index 10000). This div intercepts all mouse events that would otherwise hit the
|
|
5757
|
+
* iframe, without triggering any compositing layer changes on the iframe itself.
|
|
5758
|
+
*/
|
|
5759
|
+
_showOverlay() {
|
|
5760
|
+
if (!this._overlay) {
|
|
5761
|
+
this._overlay = document.createElement('div');
|
|
5762
|
+
this._overlay.style.cssText = 'position:fixed; inset:0; z-index:9999; display:none;';
|
|
5763
|
+
document.body.appendChild(this._overlay);
|
|
5764
|
+
}
|
|
5765
|
+
this._overlay.style.display = 'block';
|
|
5766
|
+
}
|
|
5767
|
+
_hideOverlay() {
|
|
5768
|
+
if (this._overlay) {
|
|
5769
|
+
this._overlay.style.display = 'none';
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
3157
5772
|
_handleHeaderMouseDown(e) {
|
|
3158
5773
|
// Ignore clicks on buttons inside the header
|
|
3159
5774
|
if (e.target.closest('button')) {
|
|
@@ -3163,7 +5778,8 @@
|
|
|
3163
5778
|
return;
|
|
3164
5779
|
}
|
|
3165
5780
|
this._isDragging = true;
|
|
3166
|
-
this._panelEl.classList.add('
|
|
5781
|
+
this._panelEl.classList.add('panel--dragging');
|
|
5782
|
+
this._showOverlay();
|
|
3167
5783
|
const rect = this._panelEl.getBoundingClientRect();
|
|
3168
5784
|
this._panelEl.style.left = rect.left + 'px';
|
|
3169
5785
|
this._panelEl.style.top = rect.top + 'px';
|
|
@@ -3184,7 +5800,8 @@
|
|
|
3184
5800
|
}
|
|
3185
5801
|
this._isResizing = true;
|
|
3186
5802
|
this._resizeDir = e.currentTarget.dataset.dir || '';
|
|
3187
|
-
this._panelEl.classList.add('
|
|
5803
|
+
this._panelEl.classList.add('panel--resizing');
|
|
5804
|
+
this._showOverlay();
|
|
3188
5805
|
const rect = this._panelEl.getBoundingClientRect();
|
|
3189
5806
|
this._panelEl.style.left = rect.left + 'px';
|
|
3190
5807
|
this._panelEl.style.top = rect.top + 'px';
|
|
@@ -3204,16 +5821,75 @@
|
|
|
3204
5821
|
}
|
|
3205
5822
|
_handleToggle() {
|
|
3206
5823
|
this.opened = !this.opened;
|
|
3207
|
-
// Reset position when opening so it returns to default bottom-right
|
|
3208
5824
|
if (this.opened && this._panelEl) {
|
|
3209
|
-
this.
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
this.
|
|
5825
|
+
this._applyStoredLayout();
|
|
5826
|
+
}
|
|
5827
|
+
this._saveState();
|
|
5828
|
+
}
|
|
5829
|
+
_saveState() {
|
|
5830
|
+
try {
|
|
5831
|
+
const state = { opened: String(this.opened) };
|
|
5832
|
+
if (this._panelEl) {
|
|
5833
|
+
const s = this._panelEl.style;
|
|
5834
|
+
if (s.left) {
|
|
5835
|
+
state.left = s.left;
|
|
5836
|
+
}
|
|
5837
|
+
if (s.top) {
|
|
5838
|
+
state.top = s.top;
|
|
5839
|
+
}
|
|
5840
|
+
if (s.width) {
|
|
5841
|
+
state.width = s.width;
|
|
5842
|
+
}
|
|
5843
|
+
if (s.height) {
|
|
5844
|
+
state.height = s.height;
|
|
5845
|
+
}
|
|
5846
|
+
if (s.position) {
|
|
5847
|
+
state.position = s.position;
|
|
5848
|
+
}
|
|
5849
|
+
}
|
|
5850
|
+
localStorage.setItem('kr-chatbot-state', JSON.stringify(state));
|
|
5851
|
+
}
|
|
5852
|
+
catch { /* localStorage not available */ }
|
|
5853
|
+
}
|
|
5854
|
+
_restoreState() {
|
|
5855
|
+
try {
|
|
5856
|
+
const raw = localStorage.getItem('kr-chatbot-state');
|
|
5857
|
+
if (!raw) {
|
|
5858
|
+
return;
|
|
5859
|
+
}
|
|
5860
|
+
const state = JSON.parse(raw);
|
|
5861
|
+
if (state.opened === 'true') {
|
|
5862
|
+
this.opened = true;
|
|
5863
|
+
// Apply layout after first render
|
|
5864
|
+
this.updateComplete.then(() => this._applyStoredLayout());
|
|
5865
|
+
}
|
|
5866
|
+
}
|
|
5867
|
+
catch { /* localStorage not available */ }
|
|
5868
|
+
}
|
|
5869
|
+
_applyStoredLayout() {
|
|
5870
|
+
try {
|
|
5871
|
+
const raw = localStorage.getItem('kr-chatbot-state');
|
|
5872
|
+
if (!raw || !this._panelEl) {
|
|
5873
|
+
return;
|
|
5874
|
+
}
|
|
5875
|
+
const state = JSON.parse(raw);
|
|
5876
|
+
if (state.position) {
|
|
5877
|
+
this._panelEl.style.position = state.position;
|
|
5878
|
+
}
|
|
5879
|
+
if (state.left) {
|
|
5880
|
+
this._panelEl.style.left = state.left;
|
|
5881
|
+
}
|
|
5882
|
+
if (state.top) {
|
|
5883
|
+
this._panelEl.style.top = state.top;
|
|
5884
|
+
}
|
|
5885
|
+
if (state.width) {
|
|
5886
|
+
this._panelEl.style.width = state.width;
|
|
5887
|
+
}
|
|
5888
|
+
if (state.height) {
|
|
5889
|
+
this._panelEl.style.height = state.height;
|
|
5890
|
+
}
|
|
3216
5891
|
}
|
|
5892
|
+
catch { /* localStorage not available */ }
|
|
3217
5893
|
}
|
|
3218
5894
|
_handleInputChange(e) {
|
|
3219
5895
|
this._inputValue = e.target.value;
|
|
@@ -3263,6 +5939,27 @@
|
|
|
3263
5939
|
}];
|
|
3264
5940
|
this._isStreaming = true;
|
|
3265
5941
|
this._statusText = '';
|
|
5942
|
+
// Scroll user's message to the top (Gemini-style).
|
|
5943
|
+
// Set min-height on the assistant message = container height so there's enough
|
|
5944
|
+
// scroll room below the user message to push it to the top.
|
|
5945
|
+
this.updateComplete.then(() => {
|
|
5946
|
+
if (!this._messagesEl) {
|
|
5947
|
+
return;
|
|
5948
|
+
}
|
|
5949
|
+
// Clear min-height from previous responses
|
|
5950
|
+
this._messagesEl.querySelectorAll('.message--assistant').forEach((el) => {
|
|
5951
|
+
el.style.minHeight = '';
|
|
5952
|
+
});
|
|
5953
|
+
const allMsgEls = this._messagesEl.querySelectorAll('.message');
|
|
5954
|
+
const userMsgEl = allMsgEls[this.messages.length - 2];
|
|
5955
|
+
const assistantMsgEl = allMsgEls[this.messages.length - 1];
|
|
5956
|
+
if (assistantMsgEl) {
|
|
5957
|
+
assistantMsgEl.style.minHeight = this._messagesEl.clientHeight + 'px';
|
|
5958
|
+
}
|
|
5959
|
+
if (userMsgEl) {
|
|
5960
|
+
this._messagesEl.scrollTop = userMsgEl.offsetTop - this._messagesEl.offsetTop - 24;
|
|
5961
|
+
}
|
|
5962
|
+
});
|
|
3266
5963
|
this.send({
|
|
3267
5964
|
message: messageText,
|
|
3268
5965
|
messages: this.messages.slice(0, -1),
|
|
@@ -3390,49 +6087,6 @@
|
|
|
3390
6087
|
}
|
|
3391
6088
|
}
|
|
3392
6089
|
}
|
|
3393
|
-
else if (data.type === 'ACTION') {
|
|
3394
|
-
const action = data;
|
|
3395
|
-
const actionEvent = new CustomEvent('action', {
|
|
3396
|
-
detail: {
|
|
3397
|
-
action: action,
|
|
3398
|
-
},
|
|
3399
|
-
bubbles: true,
|
|
3400
|
-
composed: true,
|
|
3401
|
-
cancelable: true,
|
|
3402
|
-
});
|
|
3403
|
-
this.dispatchEvent(actionEvent);
|
|
3404
|
-
if (!actionEvent.defaultPrevented) {
|
|
3405
|
-
if (action.action === 'RELOAD') {
|
|
3406
|
-
location.reload();
|
|
3407
|
-
}
|
|
3408
|
-
else if (action.action === 'NAVIGATE') {
|
|
3409
|
-
if (action.data && action.data.url) {
|
|
3410
|
-
window.location.href = action.data.url;
|
|
3411
|
-
}
|
|
3412
|
-
}
|
|
3413
|
-
else if (action.action === 'OPEN_TAB') {
|
|
3414
|
-
if (action.data && action.data.url) {
|
|
3415
|
-
window.open(action.data.url, '_blank');
|
|
3416
|
-
}
|
|
3417
|
-
}
|
|
3418
|
-
else if (action.action === 'UPDATE_HTML') {
|
|
3419
|
-
if (action.data && action.data.selector && action.data.html) {
|
|
3420
|
-
const el = document.querySelector(action.data.selector);
|
|
3421
|
-
if (el) {
|
|
3422
|
-
el.innerHTML = action.data.html;
|
|
3423
|
-
}
|
|
3424
|
-
}
|
|
3425
|
-
}
|
|
3426
|
-
else if (action.action === 'SCROLL_TO') {
|
|
3427
|
-
if (action.data && action.data.selector) {
|
|
3428
|
-
const el = document.querySelector(action.data.selector);
|
|
3429
|
-
if (el) {
|
|
3430
|
-
el.scrollIntoView({ behavior: 'smooth' });
|
|
3431
|
-
}
|
|
3432
|
-
}
|
|
3433
|
-
}
|
|
3434
|
-
}
|
|
3435
|
-
}
|
|
3436
6090
|
else if (data.type === 'ERROR') {
|
|
3437
6091
|
this._handleStreamError(String(data.message || 'Unknown error'));
|
|
3438
6092
|
}
|
|
@@ -3466,157 +6120,192 @@
|
|
|
3466
6120
|
}
|
|
3467
6121
|
}
|
|
3468
6122
|
}
|
|
3469
|
-
/**
|
|
3470
|
-
* Lightweight text formatting for assistant messages.
|
|
3471
|
-
* Handles code blocks, inline code, bold, and line breaks.
|
|
3472
|
-
* Escapes HTML entities first to prevent XSS.
|
|
3473
|
-
*/
|
|
3474
|
-
_formatAssistantContent(text) {
|
|
3475
|
-
// Escape HTML entities
|
|
3476
|
-
let escaped = text
|
|
3477
|
-
.replace(/&/g, '&')
|
|
3478
|
-
.replace(/</g, '<')
|
|
3479
|
-
.replace(/>/g, '>')
|
|
3480
|
-
.replace(/"/g, '"')
|
|
3481
|
-
.replace(/'/g, ''');
|
|
3482
|
-
// Code blocks: ```...```
|
|
3483
|
-
escaped = escaped.replace(/```(\w*)\n?([\s\S]*?)```/g, '<pre><code>$2</code></pre>');
|
|
3484
|
-
// Inline code: `...`
|
|
3485
|
-
escaped = escaped.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
3486
|
-
// Bold: **...**
|
|
3487
|
-
escaped = escaped.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
|
3488
|
-
// Split on code blocks to only convert newlines outside of pre tags
|
|
3489
|
-
const parts = escaped.split(/(<pre><code>[\s\S]*?<\/code><\/pre>)/g);
|
|
3490
|
-
for (let i = 0; i < parts.length; i++) {
|
|
3491
|
-
// Skip code blocks (odd indices from the split)
|
|
3492
|
-
if (i % 2 === 0) {
|
|
3493
|
-
// Convert double newlines to paragraph breaks
|
|
3494
|
-
parts[i] = parts[i].replace(/\n\n/g, '</p><p>');
|
|
3495
|
-
// Convert single newlines to line breaks
|
|
3496
|
-
parts[i] = parts[i].replace(/\n/g, '<br>');
|
|
3497
|
-
}
|
|
3498
|
-
}
|
|
3499
|
-
escaped = parts.join('');
|
|
3500
|
-
// Wrap in paragraph if we inserted paragraph breaks
|
|
3501
|
-
if (escaped.includes('</p><p>')) {
|
|
3502
|
-
escaped = '<p>' + escaped + '</p>';
|
|
3503
|
-
}
|
|
3504
|
-
return escaped;
|
|
3505
|
-
}
|
|
3506
6123
|
// --- Render ---
|
|
3507
6124
|
render() {
|
|
3508
6125
|
return b `
|
|
3509
6126
|
<button
|
|
3510
6127
|
class=${e({
|
|
3511
|
-
'
|
|
3512
|
-
'
|
|
6128
|
+
'toggle': true,
|
|
6129
|
+
'toggle--hidden': this.opened,
|
|
3513
6130
|
})}
|
|
3514
6131
|
@click=${this._handleToggle}
|
|
3515
6132
|
title="Open chat"
|
|
3516
6133
|
>
|
|
3517
|
-
<svg
|
|
6134
|
+
<svg
|
|
6135
|
+
viewBox="0 0 24 24"
|
|
6136
|
+
fill="none"
|
|
6137
|
+
stroke="currentColor"
|
|
6138
|
+
stroke-width="2"
|
|
6139
|
+
stroke-linecap="round"
|
|
6140
|
+
class="toggle-icon"
|
|
6141
|
+
>
|
|
3518
6142
|
<path d="M12 2C6.48 2 2 6.03 2 11c0 2.61 1.19 4.94 3.05 6.55L4 22l4.8-2.4C9.82 19.85 10.88 20 12 20c5.52 0 10-4.03 10-9S17.52 2 12 2z"/>
|
|
3519
6143
|
<path d="M8 11h.01M12 11h.01M16 11h.01" stroke-width="2.5"/>
|
|
3520
6144
|
</svg>
|
|
3521
6145
|
</button>
|
|
3522
6146
|
|
|
3523
6147
|
<div class=${e({
|
|
3524
|
-
'
|
|
3525
|
-
'
|
|
6148
|
+
'panel': true,
|
|
6149
|
+
'panel--opened': this.opened,
|
|
3526
6150
|
})}>
|
|
3527
|
-
<div
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
<div
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
6151
|
+
<div
|
|
6152
|
+
class="resize resize--n"
|
|
6153
|
+
data-dir="n"
|
|
6154
|
+
@mousedown=${this._handleResizeMouseDown}
|
|
6155
|
+
></div>
|
|
6156
|
+
<div
|
|
6157
|
+
class="resize resize--s"
|
|
6158
|
+
data-dir="s"
|
|
6159
|
+
@mousedown=${this._handleResizeMouseDown}
|
|
6160
|
+
></div>
|
|
6161
|
+
<div
|
|
6162
|
+
class="resize resize--w"
|
|
6163
|
+
data-dir="w"
|
|
6164
|
+
@mousedown=${this._handleResizeMouseDown}
|
|
6165
|
+
></div>
|
|
6166
|
+
<div
|
|
6167
|
+
class="resize resize--e"
|
|
6168
|
+
data-dir="e"
|
|
6169
|
+
@mousedown=${this._handleResizeMouseDown}
|
|
6170
|
+
></div>
|
|
6171
|
+
<div
|
|
6172
|
+
class="resize resize--nw"
|
|
6173
|
+
data-dir="nw"
|
|
6174
|
+
@mousedown=${this._handleResizeMouseDown}
|
|
6175
|
+
></div>
|
|
6176
|
+
<div
|
|
6177
|
+
class="resize resize--ne"
|
|
6178
|
+
data-dir="ne"
|
|
6179
|
+
@mousedown=${this._handleResizeMouseDown}
|
|
6180
|
+
></div>
|
|
6181
|
+
<div
|
|
6182
|
+
class="resize resize--sw"
|
|
6183
|
+
data-dir="sw"
|
|
6184
|
+
@mousedown=${this._handleResizeMouseDown}
|
|
6185
|
+
></div>
|
|
6186
|
+
<div
|
|
6187
|
+
class="resize resize--se"
|
|
6188
|
+
data-dir="se"
|
|
6189
|
+
@mousedown=${this._handleResizeMouseDown}
|
|
6190
|
+
></div>
|
|
6191
|
+
<div class="header" @mousedown=${this._handleHeaderMouseDown}>
|
|
6192
|
+
<div class="header-left">
|
|
6193
|
+
<div class="header-info">
|
|
6194
|
+
<span class="header-title">${this.title}</span>
|
|
3544
6195
|
${this.subtitle ? b `
|
|
3545
|
-
<span class="
|
|
6196
|
+
<span class="header-subtitle">${this.subtitle}</span>
|
|
3546
6197
|
` : A}
|
|
3547
6198
|
</div>
|
|
3548
6199
|
</div>
|
|
3549
|
-
<div class="
|
|
6200
|
+
<div class="header-actions">
|
|
3550
6201
|
${this.messages.length > 0 ? b `
|
|
3551
|
-
<button
|
|
3552
|
-
|
|
3553
|
-
|
|
6202
|
+
<button
|
|
6203
|
+
class="header-action"
|
|
6204
|
+
@click=${this._handleClear}
|
|
6205
|
+
title="Clear conversation"
|
|
6206
|
+
>
|
|
6207
|
+
<svg
|
|
6208
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
6209
|
+
fill="none"
|
|
6210
|
+
viewBox="0 0 24 24"
|
|
6211
|
+
stroke-width="1.5"
|
|
6212
|
+
stroke="currentColor"
|
|
6213
|
+
class="header-action-icon"
|
|
6214
|
+
>
|
|
6215
|
+
<path
|
|
6216
|
+
stroke-linecap="round"
|
|
6217
|
+
stroke-linejoin="round"
|
|
6218
|
+
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
|
|
6219
|
+
/>
|
|
3554
6220
|
</svg>
|
|
3555
6221
|
</button>
|
|
3556
6222
|
` : A}
|
|
3557
|
-
<button
|
|
3558
|
-
|
|
3559
|
-
|
|
6223
|
+
<button
|
|
6224
|
+
class="header-action"
|
|
6225
|
+
@click=${this._handleToggle}
|
|
6226
|
+
title="Close"
|
|
6227
|
+
>
|
|
6228
|
+
<svg
|
|
6229
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
6230
|
+
fill="none"
|
|
6231
|
+
viewBox="0 0 24 24"
|
|
6232
|
+
stroke-width="2"
|
|
6233
|
+
stroke="currentColor"
|
|
6234
|
+
class="header-action-icon"
|
|
6235
|
+
>
|
|
6236
|
+
<path
|
|
6237
|
+
stroke-linecap="round"
|
|
6238
|
+
stroke-linejoin="round"
|
|
6239
|
+
d="M6 18 18 6M6 6l12 12"
|
|
6240
|
+
/>
|
|
3560
6241
|
</svg>
|
|
3561
6242
|
</button>
|
|
3562
6243
|
</div>
|
|
3563
6244
|
</div>
|
|
3564
6245
|
|
|
3565
|
-
<div class="
|
|
6246
|
+
<div class="messages" @scroll=${this._handleMessagesScroll}>
|
|
3566
6247
|
${this.messages.length === 0 ? b `
|
|
3567
|
-
<div class="
|
|
3568
|
-
<
|
|
3569
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.087.16 2.185.283 3.293.369V21l4.076-4.076a1.526 1.526 0 0 1 1.037-.443 48.282 48.282 0 0 0 5.68-.494c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0 0 12 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018Z" />
|
|
3570
|
-
</svg>
|
|
3571
|
-
<span class="chatbot__empty-text">How can I help?</span>
|
|
6248
|
+
<div class="empty">
|
|
6249
|
+
<span class="empty-text">What can I help you with?</span>
|
|
3572
6250
|
</div>
|
|
3573
6251
|
` : A}
|
|
3574
6252
|
${this.messages.map((msg) => b `
|
|
3575
6253
|
<div class=${e({
|
|
3576
|
-
'
|
|
3577
|
-
'
|
|
3578
|
-
'
|
|
6254
|
+
'message': true,
|
|
6255
|
+
'message--user': msg.role === 'user',
|
|
6256
|
+
'message--assistant': msg.role === 'assistant',
|
|
3579
6257
|
})}>
|
|
3580
6258
|
${msg.reasoning ? b `
|
|
3581
|
-
<details class="
|
|
6259
|
+
<details class="reasoning">
|
|
3582
6260
|
<summary>Thinking</summary>
|
|
3583
|
-
<div class="
|
|
6261
|
+
<div class="reasoning-content">${msg.reasoning}</div>
|
|
3584
6262
|
</details>
|
|
3585
6263
|
` : A}
|
|
3586
|
-
|
|
3587
|
-
|
|
6264
|
+
${msg.content ? b `
|
|
6265
|
+
<div class="message-content">
|
|
6266
|
+
${msg.role === 'user'
|
|
3588
6267
|
? msg.content
|
|
3589
|
-
: o(
|
|
3590
|
-
|
|
6268
|
+
: o(marked.parse(msg.content, { breaks: true }))}
|
|
6269
|
+
</div>
|
|
6270
|
+
` : A}
|
|
6271
|
+
${msg.role === 'assistant' && this._isStreaming && msg === this.messages[this.messages.length - 1] ? b `
|
|
6272
|
+
<div class="typing">
|
|
6273
|
+
<span></span>
|
|
6274
|
+
<span></span>
|
|
6275
|
+
<span></span>
|
|
6276
|
+
</div>
|
|
6277
|
+
` : A}
|
|
3591
6278
|
</div>
|
|
3592
6279
|
`)}
|
|
3593
|
-
${this._statusText ? b `
|
|
3594
|
-
<div class="chatbot__message chatbot__message--assistant">
|
|
3595
|
-
<div class="chatbot__typing">
|
|
3596
|
-
<span></span>
|
|
3597
|
-
<span></span>
|
|
3598
|
-
<span></span>
|
|
3599
|
-
</div>
|
|
3600
|
-
</div>
|
|
3601
|
-
` : A}
|
|
3602
6280
|
</div>
|
|
3603
6281
|
|
|
3604
6282
|
${this._error ? b `
|
|
3605
|
-
<div class="
|
|
6283
|
+
<div class="error">
|
|
3606
6284
|
<span>${this._error}</span>
|
|
3607
|
-
<button class="
|
|
3608
|
-
<svg
|
|
3609
|
-
|
|
6285
|
+
<button class="error-dismiss" @click=${this._handleErrorDismiss}>
|
|
6286
|
+
<svg
|
|
6287
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
6288
|
+
fill="none"
|
|
6289
|
+
viewBox="0 0 24 24"
|
|
6290
|
+
stroke-width="2"
|
|
6291
|
+
stroke="currentColor"
|
|
6292
|
+
class="error-dismiss-icon"
|
|
6293
|
+
>
|
|
6294
|
+
<path
|
|
6295
|
+
stroke-linecap="round"
|
|
6296
|
+
stroke-linejoin="round"
|
|
6297
|
+
d="M6 18 18 6M6 6l12 12"
|
|
6298
|
+
/>
|
|
3610
6299
|
</svg>
|
|
3611
6300
|
</button>
|
|
3612
6301
|
</div>
|
|
3613
6302
|
` : A}
|
|
3614
6303
|
|
|
3615
|
-
<div class="
|
|
3616
|
-
<div class="
|
|
3617
|
-
<button class="
|
|
6304
|
+
<div class="footer">
|
|
6305
|
+
<div class="input-wrapper">
|
|
6306
|
+
<button class="input-plus" title="Attach">+</button>
|
|
3618
6307
|
<textarea
|
|
3619
|
-
class="
|
|
6308
|
+
class="input"
|
|
3620
6309
|
.value=${this._inputValue}
|
|
3621
6310
|
placeholder=${this.placeholder}
|
|
3622
6311
|
rows="1"
|
|
@@ -3624,21 +6313,45 @@
|
|
|
3624
6313
|
@keydown=${this._handleKeyDown}
|
|
3625
6314
|
></textarea>
|
|
3626
6315
|
${this._isStreaming ? b `
|
|
3627
|
-
<button
|
|
3628
|
-
|
|
3629
|
-
|
|
6316
|
+
<button
|
|
6317
|
+
class="stop"
|
|
6318
|
+
@click=${this._handleStop}
|
|
6319
|
+
title="Stop generating"
|
|
6320
|
+
>
|
|
6321
|
+
<svg
|
|
6322
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
6323
|
+
fill="currentColor"
|
|
6324
|
+
viewBox="0 0 24 24"
|
|
6325
|
+
class="stop-icon"
|
|
6326
|
+
>
|
|
6327
|
+
<rect
|
|
6328
|
+
x="6"
|
|
6329
|
+
y="6"
|
|
6330
|
+
width="12"
|
|
6331
|
+
height="12"
|
|
6332
|
+
rx="2"
|
|
6333
|
+
/>
|
|
3630
6334
|
</svg>
|
|
3631
6335
|
</button>
|
|
3632
6336
|
` : b `
|
|
3633
6337
|
<button
|
|
3634
6338
|
class=${e({
|
|
3635
|
-
'
|
|
3636
|
-
'
|
|
6339
|
+
'send': true,
|
|
6340
|
+
'send--disabled': !this._inputValue.trim(),
|
|
3637
6341
|
})}
|
|
3638
6342
|
@click=${this._handleSubmit}
|
|
3639
6343
|
title="Send message"
|
|
3640
6344
|
>
|
|
3641
|
-
<svg
|
|
6345
|
+
<svg
|
|
6346
|
+
width="16"
|
|
6347
|
+
height="16"
|
|
6348
|
+
viewBox="0 0 24 24"
|
|
6349
|
+
fill="none"
|
|
6350
|
+
stroke="currentColor"
|
|
6351
|
+
stroke-width="2.5"
|
|
6352
|
+
stroke-linecap="round"
|
|
6353
|
+
class="send-icon"
|
|
6354
|
+
>
|
|
3642
6355
|
<path d="M12 19V5M5 12l7-7 7 7"/>
|
|
3643
6356
|
</svg>
|
|
3644
6357
|
</button>
|
|
@@ -3669,7 +6382,6 @@
|
|
|
3669
6382
|
--kr-chatbot-text: #1a1a2e;
|
|
3670
6383
|
--kr-chatbot-text-secondary: #64668b;
|
|
3671
6384
|
--kr-chatbot-user-bg: #e9e9e980;
|
|
3672
|
-
--kr-chatbot-user-text: #1a1a2e;
|
|
3673
6385
|
--kr-chatbot-error-bg: #fef2f2;
|
|
3674
6386
|
--kr-chatbot-error-text: #dc2626;
|
|
3675
6387
|
--kr-chatbot-success: #00b894;
|
|
@@ -3686,7 +6398,7 @@
|
|
|
3686
6398
|
}
|
|
3687
6399
|
|
|
3688
6400
|
/* Toggle button */
|
|
3689
|
-
.
|
|
6401
|
+
.toggle {
|
|
3690
6402
|
width: var(--kr-chatbot-toggle-size);
|
|
3691
6403
|
height: var(--kr-chatbot-toggle-size);
|
|
3692
6404
|
border-radius: 18px;
|
|
@@ -3701,24 +6413,24 @@
|
|
|
3701
6413
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
3702
6414
|
}
|
|
3703
6415
|
|
|
3704
|
-
.
|
|
6416
|
+
.toggle:hover {
|
|
3705
6417
|
transform: scale(1.05);
|
|
3706
6418
|
box-shadow: 0 6px 32px rgba(22, 48, 82, 0.5);
|
|
3707
6419
|
}
|
|
3708
6420
|
|
|
3709
|
-
.
|
|
6421
|
+
.toggle--hidden {
|
|
3710
6422
|
transform: scale(0);
|
|
3711
6423
|
opacity: 0;
|
|
3712
6424
|
pointer-events: none;
|
|
3713
6425
|
}
|
|
3714
6426
|
|
|
3715
|
-
.
|
|
6427
|
+
.toggle-icon {
|
|
3716
6428
|
width: 26px;
|
|
3717
6429
|
height: 26px;
|
|
3718
6430
|
}
|
|
3719
6431
|
|
|
3720
6432
|
/* Panel */
|
|
3721
|
-
.
|
|
6433
|
+
.panel {
|
|
3722
6434
|
display: none;
|
|
3723
6435
|
flex-direction: column;
|
|
3724
6436
|
background: var(--kr-chatbot-bg);
|
|
@@ -3736,7 +6448,7 @@
|
|
|
3736
6448
|
transform-origin: bottom right;
|
|
3737
6449
|
}
|
|
3738
6450
|
|
|
3739
|
-
.
|
|
6451
|
+
.panel--opened {
|
|
3740
6452
|
display: flex;
|
|
3741
6453
|
}
|
|
3742
6454
|
|
|
@@ -3752,49 +6464,35 @@
|
|
|
3752
6464
|
}
|
|
3753
6465
|
|
|
3754
6466
|
/* Header */
|
|
3755
|
-
.
|
|
3756
|
-
padding:
|
|
3757
|
-
|
|
3758
|
-
|
|
6467
|
+
.header {
|
|
6468
|
+
padding: 0 16px 0 24px;
|
|
6469
|
+
height: 64px;
|
|
6470
|
+
background: #ffffff;
|
|
6471
|
+
color: #000000;
|
|
6472
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
3759
6473
|
display: flex;
|
|
3760
6474
|
align-items: center;
|
|
3761
6475
|
justify-content: space-between;
|
|
3762
6476
|
flex-shrink: 0;
|
|
3763
6477
|
}
|
|
3764
6478
|
|
|
3765
|
-
.
|
|
6479
|
+
.header-left {
|
|
3766
6480
|
display: flex;
|
|
3767
6481
|
align-items: center;
|
|
3768
6482
|
gap: 10px;
|
|
3769
6483
|
}
|
|
3770
6484
|
|
|
3771
|
-
|
|
3772
|
-
display: flex;
|
|
3773
|
-
flex-direction: column;
|
|
3774
|
-
gap: 2px;
|
|
3775
|
-
opacity: 0.35;
|
|
3776
|
-
}
|
|
3777
|
-
|
|
3778
|
-
.chatbot__header-drag-hint span {
|
|
3779
|
-
display: block;
|
|
3780
|
-
width: 14px;
|
|
3781
|
-
height: 2px;
|
|
3782
|
-
background: white;
|
|
3783
|
-
border-radius: 1px;
|
|
3784
|
-
}
|
|
3785
|
-
|
|
3786
|
-
.chatbot__header-info {
|
|
6485
|
+
.header-info {
|
|
3787
6486
|
display: flex;
|
|
3788
6487
|
flex-direction: column;
|
|
3789
6488
|
}
|
|
3790
6489
|
|
|
3791
|
-
.
|
|
3792
|
-
font-size:
|
|
6490
|
+
.header-title {
|
|
6491
|
+
font-size: 16px;
|
|
3793
6492
|
font-weight: 600;
|
|
3794
|
-
letter-spacing: -0.2px;
|
|
3795
6493
|
}
|
|
3796
6494
|
|
|
3797
|
-
.
|
|
6495
|
+
.header-subtitle {
|
|
3798
6496
|
color: rgba(255, 255, 255, 0.5);
|
|
3799
6497
|
font-size: 11px;
|
|
3800
6498
|
display: flex;
|
|
@@ -3802,7 +6500,7 @@
|
|
|
3802
6500
|
gap: 4px;
|
|
3803
6501
|
}
|
|
3804
6502
|
|
|
3805
|
-
.
|
|
6503
|
+
.header-subtitle::before {
|
|
3806
6504
|
content: '';
|
|
3807
6505
|
width: 5px;
|
|
3808
6506
|
height: 5px;
|
|
@@ -3810,18 +6508,18 @@
|
|
|
3810
6508
|
border-radius: 50%;
|
|
3811
6509
|
}
|
|
3812
6510
|
|
|
3813
|
-
.
|
|
6511
|
+
.header-actions {
|
|
3814
6512
|
display: flex;
|
|
3815
6513
|
align-items: center;
|
|
3816
6514
|
gap: 4px;
|
|
3817
6515
|
}
|
|
3818
6516
|
|
|
3819
|
-
.
|
|
3820
|
-
width:
|
|
3821
|
-
height:
|
|
6517
|
+
.header-action {
|
|
6518
|
+
width: 32px;
|
|
6519
|
+
height: 32px;
|
|
3822
6520
|
background: transparent;
|
|
3823
6521
|
border: none;
|
|
3824
|
-
color:
|
|
6522
|
+
color: rgb(0 0 0 / 70%);
|
|
3825
6523
|
border-radius: 8px;
|
|
3826
6524
|
cursor: pointer;
|
|
3827
6525
|
display: flex;
|
|
@@ -3830,18 +6528,18 @@
|
|
|
3830
6528
|
transition: all 0.15s;
|
|
3831
6529
|
}
|
|
3832
6530
|
|
|
3833
|
-
.
|
|
3834
|
-
background: rgba(
|
|
3835
|
-
color:
|
|
6531
|
+
.header-action:hover {
|
|
6532
|
+
background: rgba(0, 0, 0, 0.05);
|
|
6533
|
+
color: #000000;
|
|
3836
6534
|
}
|
|
3837
6535
|
|
|
3838
|
-
.
|
|
3839
|
-
width:
|
|
3840
|
-
height:
|
|
6536
|
+
.header-action-icon {
|
|
6537
|
+
width: 20px;
|
|
6538
|
+
height: 20px;
|
|
3841
6539
|
}
|
|
3842
6540
|
|
|
3843
6541
|
/* Messages */
|
|
3844
|
-
.
|
|
6542
|
+
.messages {
|
|
3845
6543
|
flex: 1;
|
|
3846
6544
|
overflow-y: auto;
|
|
3847
6545
|
padding: 32px;
|
|
@@ -3852,20 +6550,20 @@
|
|
|
3852
6550
|
background: white;
|
|
3853
6551
|
}
|
|
3854
6552
|
|
|
3855
|
-
.
|
|
6553
|
+
.messages::-webkit-scrollbar {
|
|
3856
6554
|
width: 4px;
|
|
3857
6555
|
}
|
|
3858
6556
|
|
|
3859
|
-
.
|
|
6557
|
+
.messages::-webkit-scrollbar-track {
|
|
3860
6558
|
background: transparent;
|
|
3861
6559
|
}
|
|
3862
6560
|
|
|
3863
|
-
.
|
|
6561
|
+
.messages::-webkit-scrollbar-thumb {
|
|
3864
6562
|
background: rgba(0, 0, 0, 0.08);
|
|
3865
6563
|
border-radius: 2px;
|
|
3866
6564
|
}
|
|
3867
6565
|
|
|
3868
|
-
.
|
|
6566
|
+
.empty {
|
|
3869
6567
|
flex: 1;
|
|
3870
6568
|
display: flex;
|
|
3871
6569
|
flex-direction: column;
|
|
@@ -3875,17 +6573,12 @@
|
|
|
3875
6573
|
gap: 8px;
|
|
3876
6574
|
}
|
|
3877
6575
|
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
opacity: 0.4;
|
|
3882
|
-
}
|
|
3883
|
-
|
|
3884
|
-
.chatbot__empty-text {
|
|
3885
|
-
font-size: 13px;
|
|
6576
|
+
.empty-text {
|
|
6577
|
+
font-size: 14px;
|
|
6578
|
+
color: rgb(0 0 0 / 80%);
|
|
3886
6579
|
}
|
|
3887
6580
|
|
|
3888
|
-
.
|
|
6581
|
+
.message {
|
|
3889
6582
|
display: flex;
|
|
3890
6583
|
flex-direction: column;
|
|
3891
6584
|
max-width: 100%;
|
|
@@ -3903,40 +6596,41 @@
|
|
|
3903
6596
|
}
|
|
3904
6597
|
}
|
|
3905
6598
|
|
|
3906
|
-
.
|
|
6599
|
+
.message--user {
|
|
3907
6600
|
align-self: flex-end;
|
|
3908
6601
|
align-items: flex-end;
|
|
3909
6602
|
max-width: 85%;
|
|
3910
6603
|
}
|
|
3911
6604
|
|
|
3912
|
-
.
|
|
6605
|
+
.message--assistant {
|
|
3913
6606
|
align-self: flex-start;
|
|
6607
|
+
gap: 24px;
|
|
3914
6608
|
}
|
|
3915
6609
|
|
|
3916
|
-
.
|
|
6610
|
+
.message-content {
|
|
3917
6611
|
line-height: 1.7;
|
|
3918
6612
|
word-wrap: break-word;
|
|
3919
6613
|
overflow-wrap: break-word;
|
|
3920
6614
|
}
|
|
3921
6615
|
|
|
3922
|
-
.
|
|
6616
|
+
.message--user .message-content {
|
|
3923
6617
|
display: inline-block;
|
|
3924
6618
|
background: var(--kr-chatbot-user-bg);
|
|
3925
|
-
color:
|
|
6619
|
+
color: #000000;
|
|
3926
6620
|
padding: 10px 16px;
|
|
3927
6621
|
border-radius: 20px;
|
|
3928
6622
|
border-bottom-right-radius: 6px;
|
|
3929
6623
|
font-size: 14px;
|
|
3930
6624
|
}
|
|
3931
6625
|
|
|
3932
|
-
.
|
|
6626
|
+
.message--assistant .message-content {
|
|
3933
6627
|
background: transparent;
|
|
3934
|
-
color:
|
|
6628
|
+
color: #000000;
|
|
3935
6629
|
padding: 0;
|
|
3936
6630
|
font-size: 14px;
|
|
3937
6631
|
}
|
|
3938
6632
|
|
|
3939
|
-
.
|
|
6633
|
+
.message--assistant .message-content pre {
|
|
3940
6634
|
background: #1e1e1e;
|
|
3941
6635
|
color: #d4d4d4;
|
|
3942
6636
|
padding: 12px;
|
|
@@ -3947,67 +6641,57 @@
|
|
|
3947
6641
|
line-height: 1.4;
|
|
3948
6642
|
}
|
|
3949
6643
|
|
|
3950
|
-
.
|
|
6644
|
+
.message--assistant .message-content code {
|
|
3951
6645
|
font-family: 'SF Mono', 'Fira Code', Monaco, monospace;
|
|
3952
6646
|
font-size: 13px;
|
|
3953
6647
|
}
|
|
3954
6648
|
|
|
3955
|
-
.
|
|
6649
|
+
.message--assistant .message-content :not(pre) > code {
|
|
3956
6650
|
background: rgba(0, 0, 0, 0.06);
|
|
3957
6651
|
padding: 2px 5px;
|
|
3958
6652
|
border-radius: 3px;
|
|
3959
6653
|
}
|
|
3960
6654
|
|
|
3961
|
-
.
|
|
6655
|
+
.message--assistant .message-content strong {
|
|
3962
6656
|
font-weight: 600;
|
|
3963
6657
|
}
|
|
3964
6658
|
|
|
3965
|
-
.
|
|
6659
|
+
.message--assistant .message-content p {
|
|
3966
6660
|
margin: 0 0 8px 0;
|
|
3967
6661
|
}
|
|
3968
6662
|
|
|
3969
|
-
.
|
|
6663
|
+
.message--assistant .message-content p:last-child {
|
|
3970
6664
|
margin-bottom: 0;
|
|
3971
6665
|
}
|
|
3972
6666
|
|
|
3973
6667
|
/* Reasoning */
|
|
3974
|
-
.
|
|
3975
|
-
margin-bottom: 8px;
|
|
6668
|
+
.reasoning {
|
|
3976
6669
|
border-radius: 6px;
|
|
3977
|
-
background:
|
|
6670
|
+
background: rgb(244 244 244 / 80%);
|
|
3978
6671
|
font-size: 12px;
|
|
3979
6672
|
}
|
|
3980
6673
|
|
|
3981
|
-
.
|
|
6674
|
+
.reasoning summary {
|
|
3982
6675
|
cursor: pointer;
|
|
3983
|
-
padding: 6px 10px;
|
|
3984
|
-
color:
|
|
3985
|
-
font-style: italic;
|
|
6676
|
+
padding: 6px 10px 6px 10px;
|
|
6677
|
+
color: black;
|
|
3986
6678
|
user-select: none;
|
|
3987
6679
|
}
|
|
3988
6680
|
|
|
3989
|
-
.
|
|
6681
|
+
.reasoning-content {
|
|
3990
6682
|
padding: 6px 10px 10px;
|
|
3991
|
-
color:
|
|
3992
|
-
line-height: 1.5;
|
|
6683
|
+
color: black;
|
|
3993
6684
|
white-space: pre-wrap;
|
|
3994
6685
|
word-wrap: break-word;
|
|
3995
6686
|
}
|
|
3996
6687
|
|
|
3997
|
-
|
|
3998
|
-
.chatbot__status {
|
|
3999
|
-
font-size: 13px;
|
|
4000
|
-
color: var(--kr-chatbot-text-secondary);
|
|
4001
|
-
font-style: italic;
|
|
4002
|
-
}
|
|
4003
|
-
|
|
4004
|
-
.chatbot__typing {
|
|
6688
|
+
.typing {
|
|
4005
6689
|
display: flex;
|
|
4006
6690
|
gap: 4px;
|
|
4007
6691
|
padding: 4px 0;
|
|
4008
6692
|
}
|
|
4009
6693
|
|
|
4010
|
-
.
|
|
6694
|
+
.typing span {
|
|
4011
6695
|
width: 6px;
|
|
4012
6696
|
height: 6px;
|
|
4013
6697
|
background: var(--kr-chatbot-text-secondary);
|
|
@@ -4016,11 +6700,11 @@
|
|
|
4016
6700
|
opacity: 0.4;
|
|
4017
6701
|
}
|
|
4018
6702
|
|
|
4019
|
-
.
|
|
6703
|
+
.typing span:nth-child(2) {
|
|
4020
6704
|
animation-delay: 0.2s;
|
|
4021
6705
|
}
|
|
4022
6706
|
|
|
4023
|
-
.
|
|
6707
|
+
.typing span:nth-child(3) {
|
|
4024
6708
|
animation-delay: 0.4s;
|
|
4025
6709
|
}
|
|
4026
6710
|
|
|
@@ -4036,7 +6720,7 @@
|
|
|
4036
6720
|
}
|
|
4037
6721
|
|
|
4038
6722
|
/* Error */
|
|
4039
|
-
.
|
|
6723
|
+
.error {
|
|
4040
6724
|
padding: 8px 16px;
|
|
4041
6725
|
background: var(--kr-chatbot-error-bg);
|
|
4042
6726
|
color: var(--kr-chatbot-error-text);
|
|
@@ -4048,7 +6732,7 @@
|
|
|
4048
6732
|
flex-shrink: 0;
|
|
4049
6733
|
}
|
|
4050
6734
|
|
|
4051
|
-
.
|
|
6735
|
+
.error-dismiss {
|
|
4052
6736
|
background: none;
|
|
4053
6737
|
border: none;
|
|
4054
6738
|
color: var(--kr-chatbot-error-text);
|
|
@@ -4059,19 +6743,19 @@
|
|
|
4059
6743
|
flex-shrink: 0;
|
|
4060
6744
|
}
|
|
4061
6745
|
|
|
4062
|
-
.
|
|
6746
|
+
.error-dismiss-icon {
|
|
4063
6747
|
width: 14px;
|
|
4064
6748
|
height: 14px;
|
|
4065
6749
|
}
|
|
4066
6750
|
|
|
4067
6751
|
/* Footer / Input */
|
|
4068
|
-
.
|
|
4069
|
-
padding:
|
|
6752
|
+
.footer {
|
|
6753
|
+
padding: 16px 16px 16px;
|
|
4070
6754
|
flex-shrink: 0;
|
|
4071
6755
|
background: white;
|
|
4072
6756
|
}
|
|
4073
6757
|
|
|
4074
|
-
.
|
|
6758
|
+
.input-wrapper {
|
|
4075
6759
|
display: flex;
|
|
4076
6760
|
align-items: center;
|
|
4077
6761
|
gap: 4px;
|
|
@@ -4082,12 +6766,12 @@
|
|
|
4082
6766
|
transition: border-color 0.2s, box-shadow 0.2s;
|
|
4083
6767
|
}
|
|
4084
6768
|
|
|
4085
|
-
.
|
|
6769
|
+
.input-wrapper:focus-within {
|
|
4086
6770
|
border-color: #b0b0b0;
|
|
4087
6771
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04);
|
|
4088
6772
|
}
|
|
4089
6773
|
|
|
4090
|
-
.
|
|
6774
|
+
.input-plus {
|
|
4091
6775
|
width: 34px;
|
|
4092
6776
|
height: 34px;
|
|
4093
6777
|
border: none;
|
|
@@ -4104,11 +6788,11 @@
|
|
|
4104
6788
|
transition: background 0.15s;
|
|
4105
6789
|
}
|
|
4106
6790
|
|
|
4107
|
-
.
|
|
6791
|
+
.input-plus:hover {
|
|
4108
6792
|
background: #f8f9fc;
|
|
4109
6793
|
}
|
|
4110
6794
|
|
|
4111
|
-
.
|
|
6795
|
+
.input {
|
|
4112
6796
|
flex: 1;
|
|
4113
6797
|
border: none;
|
|
4114
6798
|
outline: none;
|
|
@@ -4120,14 +6804,14 @@
|
|
|
4120
6804
|
min-height: 26px;
|
|
4121
6805
|
max-height: 120px;
|
|
4122
6806
|
background: transparent;
|
|
4123
|
-
color:
|
|
6807
|
+
color: black;
|
|
4124
6808
|
}
|
|
4125
6809
|
|
|
4126
|
-
.
|
|
6810
|
+
.input::placeholder {
|
|
4127
6811
|
color: #b0b0b0;
|
|
4128
6812
|
}
|
|
4129
6813
|
|
|
4130
|
-
.
|
|
6814
|
+
.send {
|
|
4131
6815
|
width: 34px;
|
|
4132
6816
|
height: 34px;
|
|
4133
6817
|
border-radius: 50%;
|
|
@@ -4142,25 +6826,25 @@
|
|
|
4142
6826
|
transition: all 0.15s;
|
|
4143
6827
|
}
|
|
4144
6828
|
|
|
4145
|
-
.
|
|
6829
|
+
.send:hover {
|
|
4146
6830
|
background: var(--kr-chatbot-primary-hover);
|
|
4147
6831
|
}
|
|
4148
6832
|
|
|
4149
|
-
.
|
|
6833
|
+
.send--disabled {
|
|
4150
6834
|
opacity: 0.4;
|
|
4151
6835
|
cursor: default;
|
|
4152
6836
|
}
|
|
4153
6837
|
|
|
4154
|
-
.
|
|
6838
|
+
.send--disabled:hover {
|
|
4155
6839
|
background: var(--kr-chatbot-primary);
|
|
4156
6840
|
}
|
|
4157
6841
|
|
|
4158
|
-
.
|
|
6842
|
+
.send-icon {
|
|
4159
6843
|
width: 16px;
|
|
4160
6844
|
height: 16px;
|
|
4161
6845
|
}
|
|
4162
6846
|
|
|
4163
|
-
.
|
|
6847
|
+
.stop {
|
|
4164
6848
|
width: 34px;
|
|
4165
6849
|
height: 34px;
|
|
4166
6850
|
border-radius: 50%;
|
|
@@ -4175,62 +6859,51 @@
|
|
|
4175
6859
|
transition: all 0.15s;
|
|
4176
6860
|
}
|
|
4177
6861
|
|
|
4178
|
-
.
|
|
6862
|
+
.stop:hover {
|
|
4179
6863
|
background: #b91c1c;
|
|
4180
6864
|
}
|
|
4181
6865
|
|
|
4182
|
-
.
|
|
6866
|
+
.stop-icon {
|
|
4183
6867
|
width: 14px;
|
|
4184
6868
|
height: 14px;
|
|
4185
6869
|
}
|
|
4186
6870
|
|
|
4187
6871
|
/* Header drag cursor */
|
|
4188
|
-
.
|
|
6872
|
+
.header {
|
|
4189
6873
|
cursor: grab;
|
|
4190
6874
|
user-select: none;
|
|
4191
6875
|
}
|
|
4192
6876
|
|
|
4193
|
-
.
|
|
6877
|
+
.header:active {
|
|
4194
6878
|
cursor: grabbing;
|
|
4195
6879
|
}
|
|
4196
6880
|
|
|
4197
6881
|
/* Drag / Resize states */
|
|
4198
|
-
.
|
|
4199
|
-
.
|
|
6882
|
+
.panel--dragging,
|
|
6883
|
+
.panel--resizing {
|
|
4200
6884
|
transition: none;
|
|
4201
6885
|
user-select: none;
|
|
4202
6886
|
}
|
|
4203
6887
|
|
|
4204
|
-
.
|
|
6888
|
+
.panel--dragging {
|
|
4205
6889
|
box-shadow: 0 32px 96px rgba(0, 0, 0, 0.18), 0 12px 40px rgba(0, 0, 0, 0.1);
|
|
4206
6890
|
}
|
|
4207
6891
|
|
|
4208
6892
|
/* Resize handles */
|
|
4209
|
-
.
|
|
6893
|
+
.resize {
|
|
4210
6894
|
position: absolute;
|
|
4211
6895
|
z-index: 10;
|
|
4212
6896
|
}
|
|
4213
6897
|
|
|
4214
|
-
.
|
|
4215
|
-
.
|
|
4216
|
-
.
|
|
4217
|
-
.
|
|
4218
|
-
.
|
|
4219
|
-
.
|
|
4220
|
-
.
|
|
4221
|
-
.
|
|
6898
|
+
.resize--n { top: -4px; left: 12px; right: 12px; height: 8px; cursor: n-resize; }
|
|
6899
|
+
.resize--s { bottom: -4px; left: 12px; right: 12px; height: 8px; cursor: s-resize; }
|
|
6900
|
+
.resize--w { left: -4px; top: 12px; bottom: 12px; width: 8px; cursor: w-resize; }
|
|
6901
|
+
.resize--e { right: -4px; top: 12px; bottom: 12px; width: 8px; cursor: e-resize; }
|
|
6902
|
+
.resize--nw { top: -4px; left: -4px; width: 16px; height: 16px; cursor: nw-resize; }
|
|
6903
|
+
.resize--ne { top: -4px; right: -4px; width: 16px; height: 16px; cursor: ne-resize; }
|
|
6904
|
+
.resize--sw { bottom: -4px; left: -4px; width: 16px; height: 16px; cursor: sw-resize; }
|
|
6905
|
+
.resize--se { bottom: -4px; right: -4px; width: 16px; height: 16px; cursor: se-resize; }
|
|
4222
6906
|
|
|
4223
|
-
.chatbot__resize--se::after {
|
|
4224
|
-
content: '';
|
|
4225
|
-
position: absolute;
|
|
4226
|
-
bottom: 7px;
|
|
4227
|
-
right: 7px;
|
|
4228
|
-
width: 10px;
|
|
4229
|
-
height: 10px;
|
|
4230
|
-
border-right: 2px solid rgba(0, 0, 0, 0.12);
|
|
4231
|
-
border-bottom: 2px solid rgba(0, 0, 0, 0.12);
|
|
4232
|
-
border-radius: 0 0 3px 0;
|
|
4233
|
-
}
|
|
4234
6907
|
`;
|
|
4235
6908
|
__decorate([
|
|
4236
6909
|
n({ attribute: false })
|
|
@@ -4238,6 +6911,9 @@
|
|
|
4238
6911
|
__decorate([
|
|
4239
6912
|
n({ attribute: false })
|
|
4240
6913
|
], exports.Chatbot.prototype, "clear", void 0);
|
|
6914
|
+
__decorate([
|
|
6915
|
+
n({ attribute: false })
|
|
6916
|
+
], exports.Chatbot.prototype, "load", void 0);
|
|
4241
6917
|
__decorate([
|
|
4242
6918
|
n({ attribute: false })
|
|
4243
6919
|
], exports.Chatbot.prototype, "messages", void 0);
|
|
@@ -4251,7 +6927,10 @@
|
|
|
4251
6927
|
n({ type: String })
|
|
4252
6928
|
], exports.Chatbot.prototype, "placeholder", void 0);
|
|
4253
6929
|
__decorate([
|
|
4254
|
-
n({
|
|
6930
|
+
n({
|
|
6931
|
+
type: Boolean,
|
|
6932
|
+
reflect: true
|
|
6933
|
+
})
|
|
4255
6934
|
], exports.Chatbot.prototype, "opened", void 0);
|
|
4256
6935
|
__decorate([
|
|
4257
6936
|
r()
|
|
@@ -4266,13 +6945,13 @@
|
|
|
4266
6945
|
r()
|
|
4267
6946
|
], exports.Chatbot.prototype, "_error", void 0);
|
|
4268
6947
|
__decorate([
|
|
4269
|
-
e$3('.
|
|
6948
|
+
e$3('.panel')
|
|
4270
6949
|
], exports.Chatbot.prototype, "_panelEl", void 0);
|
|
4271
6950
|
__decorate([
|
|
4272
|
-
e$3('.
|
|
6951
|
+
e$3('.messages')
|
|
4273
6952
|
], exports.Chatbot.prototype, "_messagesEl", void 0);
|
|
4274
6953
|
__decorate([
|
|
4275
|
-
e$3('.
|
|
6954
|
+
e$3('.input')
|
|
4276
6955
|
], exports.Chatbot.prototype, "_inputEl", void 0);
|
|
4277
6956
|
exports.Chatbot = __decorate([
|
|
4278
6957
|
t$1('kr-chatbot')
|