@kodaris/krubble-app-components 1.0.66 → 1.0.68
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 +3208 -513
- 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 +3206 -511
- 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 +18 -12
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +208 -88
- 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
|
@@ -345,6 +345,7 @@
|
|
|
345
345
|
* @property {KRNavItem[]} nav - Navigation items as JSON array
|
|
346
346
|
* @property {KRUser} user - User profile data
|
|
347
347
|
* @property {boolean} navEnabled - Whether to show the nav drawer (menu toggle remains visible when false)
|
|
348
|
+
* @property {'employee'|'customer'} user-type - User type. Customers skip all `/api/system` calls since that endpoint is employee-only. Defaults to 'employee'.
|
|
348
349
|
*
|
|
349
350
|
*
|
|
350
351
|
* TODO
|
|
@@ -358,18 +359,6 @@
|
|
|
358
359
|
* Default icon for nav items without an icon (shown at top level)
|
|
359
360
|
*/
|
|
360
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>';
|
|
361
|
-
this.navItemsExpanded = new Set();
|
|
362
|
-
this.navQuery = '';
|
|
363
|
-
this.activeNavItemId = null;
|
|
364
|
-
this.isNavScrolled = false;
|
|
365
|
-
this.isNavOpened = localStorage.getItem('kr-scaffold-nav-opened') !== 'false';
|
|
366
|
-
this.isEditing = false;
|
|
367
|
-
this.isUserMenuOpen = false;
|
|
368
|
-
this.pref = { nav: {} };
|
|
369
|
-
this.draggedNavItemId = null;
|
|
370
|
-
this.navItemDropTargetId = null;
|
|
371
|
-
this.navItemDropPosition = 'above';
|
|
372
|
-
this.pendingRequests = 0;
|
|
373
362
|
this.originalFetch = null;
|
|
374
363
|
this.originalXhrOpen = null;
|
|
375
364
|
this.navItemDragPreview = null;
|
|
@@ -397,18 +386,38 @@
|
|
|
397
386
|
* Menu toggle button remains visible and emits menu-click events.
|
|
398
387
|
*/
|
|
399
388
|
this.navEnabled = true;
|
|
389
|
+
/**
|
|
390
|
+
* User type. Set to 'customer' to skip all /api/system calls — customers don't have access to that endpoint.
|
|
391
|
+
*/
|
|
392
|
+
this.userType = 'employee';
|
|
400
393
|
/**
|
|
401
394
|
* Breadcrumbs to display in the subbar
|
|
402
395
|
*/
|
|
403
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;
|
|
404
409
|
this.boundHandleMouseMove = this.handleMouseMove.bind(this);
|
|
405
410
|
this.boundHandleMouseUp = this.handleMouseUp.bind(this);
|
|
406
411
|
}
|
|
407
412
|
connectedCallback() {
|
|
408
413
|
super.connectedCallback();
|
|
409
|
-
this.loadPref();
|
|
410
414
|
this.installFetchInterceptor();
|
|
411
415
|
}
|
|
416
|
+
// Attributes are not reflected into properties until after the first update,
|
|
417
|
+
// so loadPref() must run here (not connectedCallback) for user-type to be set.
|
|
418
|
+
firstUpdated() {
|
|
419
|
+
this.loadPref();
|
|
420
|
+
}
|
|
412
421
|
updated(changedProperties) {
|
|
413
422
|
super.updated(changedProperties);
|
|
414
423
|
if (changedProperties.has('nav') && this.nav.length > 0) {
|
|
@@ -430,8 +439,9 @@
|
|
|
430
439
|
* Updates `pendingRequests` state when requests start/complete.
|
|
431
440
|
*/
|
|
432
441
|
installFetchInterceptor() {
|
|
433
|
-
if (this.originalFetch)
|
|
442
|
+
if (this.originalFetch) {
|
|
434
443
|
return;
|
|
444
|
+
}
|
|
435
445
|
const scaffold = this;
|
|
436
446
|
// Intercept fetch
|
|
437
447
|
this.originalFetch = window.fetch.bind(window);
|
|
@@ -482,17 +492,23 @@
|
|
|
482
492
|
* - resolveUrl(item) = "/operations/settings" (used for href and URL comparison)
|
|
483
493
|
*/
|
|
484
494
|
resolveUrl(item) {
|
|
485
|
-
if (!item.url)
|
|
495
|
+
if (!item.url) {
|
|
486
496
|
return '#';
|
|
497
|
+
}
|
|
487
498
|
// External URLs - return as-is
|
|
488
499
|
if (item.external) {
|
|
489
500
|
return item.url;
|
|
490
501
|
}
|
|
491
502
|
// Remove leading slash from url if present, then combine with base
|
|
492
|
-
|
|
503
|
+
let path = item.url;
|
|
504
|
+
if (item.url.startsWith('/')) {
|
|
505
|
+
path = item.url.slice(1);
|
|
506
|
+
}
|
|
493
507
|
const baseHref = document.querySelector('base')?.getAttribute('href') || '/';
|
|
494
|
-
|
|
495
|
-
|
|
508
|
+
if (baseHref.endsWith('/')) {
|
|
509
|
+
return baseHref + path;
|
|
510
|
+
}
|
|
511
|
+
return baseHref + '/' + path;
|
|
496
512
|
}
|
|
497
513
|
// =========================================================================
|
|
498
514
|
// Navigation Data
|
|
@@ -521,11 +537,15 @@
|
|
|
521
537
|
if (item.type === 'group' && item.children) {
|
|
522
538
|
item.children.forEach((child, childIndex) => {
|
|
523
539
|
const childOverride = this.pref.nav[child.id];
|
|
540
|
+
let parentId = item.id;
|
|
541
|
+
if (childOverride?.parentId !== undefined) {
|
|
542
|
+
parentId = childOverride.parentId;
|
|
543
|
+
}
|
|
524
544
|
result.push({
|
|
525
545
|
...child,
|
|
526
546
|
...childOverride,
|
|
527
547
|
order: childOverride?.order ?? child.order ?? childIndex,
|
|
528
|
-
parentId
|
|
548
|
+
parentId,
|
|
529
549
|
});
|
|
530
550
|
});
|
|
531
551
|
}
|
|
@@ -668,14 +688,18 @@
|
|
|
668
688
|
toggleNavItem(itemId) {
|
|
669
689
|
const navItem = this.shadowRoot?.querySelector(`.nav-item[data-id="${itemId}"]`);
|
|
670
690
|
const groupEl = navItem?.nextElementSibling;
|
|
671
|
-
if (!groupEl)
|
|
691
|
+
if (!groupEl) {
|
|
672
692
|
return;
|
|
693
|
+
}
|
|
673
694
|
if (this.navItemsExpanded.has(itemId)) {
|
|
674
695
|
this.navItemsExpanded.delete(itemId);
|
|
675
696
|
groupEl.animate([
|
|
676
697
|
{ height: `${groupEl.scrollHeight}px` },
|
|
677
698
|
{ height: '0px' }
|
|
678
|
-
], {
|
|
699
|
+
], {
|
|
700
|
+
duration: 300,
|
|
701
|
+
easing: 'ease-in-out'
|
|
702
|
+
}).onfinish = () => {
|
|
679
703
|
groupEl.classList.remove('nav-group--expanded');
|
|
680
704
|
};
|
|
681
705
|
}
|
|
@@ -685,7 +709,10 @@
|
|
|
685
709
|
groupEl.animate([
|
|
686
710
|
{ height: '0px' },
|
|
687
711
|
{ height: `${groupEl.scrollHeight}px` }
|
|
688
|
-
], {
|
|
712
|
+
], {
|
|
713
|
+
duration: 300,
|
|
714
|
+
easing: 'ease-in-out'
|
|
715
|
+
});
|
|
689
716
|
}
|
|
690
717
|
this.requestUpdate();
|
|
691
718
|
}
|
|
@@ -780,12 +807,18 @@
|
|
|
780
807
|
this.pref.nav = {};
|
|
781
808
|
}
|
|
782
809
|
savePref() {
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
810
|
+
if (this.userType === 'customer') {
|
|
811
|
+
return;
|
|
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
|
+
}
|
|
786
819
|
krubbleHttp.Http.fetch({
|
|
787
820
|
url,
|
|
788
|
-
method
|
|
821
|
+
method,
|
|
789
822
|
body: JSON.stringify({ nav: this.pref.nav }),
|
|
790
823
|
})
|
|
791
824
|
.then((response) => response.json())
|
|
@@ -798,6 +831,9 @@
|
|
|
798
831
|
});
|
|
799
832
|
}
|
|
800
833
|
loadPref() {
|
|
834
|
+
if (this.userType === 'customer') {
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
801
837
|
krubbleHttp.Http.fetch({
|
|
802
838
|
url: '/api/system/preference/json/scaffold?global=true',
|
|
803
839
|
method: 'GET',
|
|
@@ -814,21 +850,36 @@
|
|
|
814
850
|
});
|
|
815
851
|
}
|
|
816
852
|
handleNavItemContextMenu(e, item) {
|
|
817
|
-
if (!this.isEditing)
|
|
853
|
+
if (!this.isEditing) {
|
|
818
854
|
return;
|
|
855
|
+
}
|
|
819
856
|
e.preventDefault();
|
|
820
857
|
krubbleComponents.ContextMenu.open({
|
|
821
858
|
x: e.clientX,
|
|
822
859
|
y: e.clientY,
|
|
823
860
|
items: [
|
|
824
|
-
{
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
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
|
+
},
|
|
828
878
|
],
|
|
829
879
|
}).then((result) => {
|
|
830
|
-
if (!result)
|
|
880
|
+
if (!result) {
|
|
831
881
|
return;
|
|
882
|
+
}
|
|
832
883
|
switch (result.id) {
|
|
833
884
|
case 'edit':
|
|
834
885
|
this.openNavItemEdit(item);
|
|
@@ -854,8 +905,9 @@
|
|
|
854
905
|
openNavItemEdit(item) {
|
|
855
906
|
krubbleComponents.Dialog.open(KRNavItemEdit, { data: item }).afterClosed().then((res) => {
|
|
856
907
|
const result = res;
|
|
857
|
-
if (!result)
|
|
908
|
+
if (!result) {
|
|
858
909
|
return;
|
|
910
|
+
}
|
|
859
911
|
if (!this.pref.nav[item.id]) {
|
|
860
912
|
this.pref.nav[item.id] = {};
|
|
861
913
|
}
|
|
@@ -882,7 +934,13 @@
|
|
|
882
934
|
.filter(i => i.parentId === targetItem.parentId)
|
|
883
935
|
.sort((a, b) => a.order - b.order);
|
|
884
936
|
const targetIndex = siblings.findIndex(i => i.id === targetItem.id);
|
|
885
|
-
|
|
937
|
+
let newOrder;
|
|
938
|
+
if (position === 'above') {
|
|
939
|
+
newOrder = targetIndex;
|
|
940
|
+
}
|
|
941
|
+
else {
|
|
942
|
+
newOrder = targetIndex + 1;
|
|
943
|
+
}
|
|
886
944
|
// Shift existing items to make room for the new item
|
|
887
945
|
siblings.forEach((sibling, index) => {
|
|
888
946
|
if (index >= newOrder) {
|
|
@@ -919,8 +977,9 @@
|
|
|
919
977
|
* @param item - The nav item being pressed
|
|
920
978
|
*/
|
|
921
979
|
handleNavItemMouseDown(e, item) {
|
|
922
|
-
if (!this.isEditing)
|
|
980
|
+
if (!this.isEditing) {
|
|
923
981
|
return;
|
|
982
|
+
}
|
|
924
983
|
e.preventDefault();
|
|
925
984
|
this.draggedNavItemId = item.id;
|
|
926
985
|
this.navItemDragStartY = e.clientY;
|
|
@@ -938,8 +997,9 @@
|
|
|
938
997
|
* @param e - The mouse event
|
|
939
998
|
*/
|
|
940
999
|
handleMouseMove(e) {
|
|
941
|
-
if (!this.draggedNavItemId)
|
|
1000
|
+
if (!this.draggedNavItemId) {
|
|
942
1001
|
return;
|
|
1002
|
+
}
|
|
943
1003
|
// Start dragging after moving a few pixels (to distinguish from clicks)
|
|
944
1004
|
if (!this.isNavItemDragging && Math.abs(e.clientY - this.navItemDragStartY) > 5) {
|
|
945
1005
|
this.isNavItemDragging = true;
|
|
@@ -955,8 +1015,9 @@
|
|
|
955
1015
|
this.shadowRoot?.appendChild(this.navItemDragPreview);
|
|
956
1016
|
}
|
|
957
1017
|
}
|
|
958
|
-
if (!this.isNavItemDragging)
|
|
1018
|
+
if (!this.isNavItemDragging) {
|
|
959
1019
|
return;
|
|
1020
|
+
}
|
|
960
1021
|
// Update preview position
|
|
961
1022
|
if (this.navItemDragPreview) {
|
|
962
1023
|
this.navItemDragPreview.style.left = `${e.clientX + 10}px`;
|
|
@@ -1007,14 +1068,16 @@
|
|
|
1007
1068
|
updateNavItemDropTarget(e) {
|
|
1008
1069
|
// Get all nav items in shadow DOM
|
|
1009
1070
|
const navItems = this.shadowRoot?.querySelectorAll('.nav-item[data-id]');
|
|
1010
|
-
if (!navItems)
|
|
1071
|
+
if (!navItems) {
|
|
1011
1072
|
return;
|
|
1073
|
+
}
|
|
1012
1074
|
let foundTarget = false;
|
|
1013
1075
|
navItems.forEach((el) => {
|
|
1014
1076
|
const rect = el.getBoundingClientRect();
|
|
1015
1077
|
const itemId = el.getAttribute('data-id');
|
|
1016
|
-
if (!itemId || itemId === this.draggedNavItemId)
|
|
1078
|
+
if (!itemId || itemId === this.draggedNavItemId) {
|
|
1017
1079
|
return;
|
|
1080
|
+
}
|
|
1018
1081
|
// Skip if mouse is outside this element
|
|
1019
1082
|
if (e.clientX < rect.left || e.clientX > rect.right ||
|
|
1020
1083
|
e.clientY < rect.top || e.clientY > rect.bottom) {
|
|
@@ -1051,11 +1114,22 @@
|
|
|
1051
1114
|
}
|
|
1052
1115
|
else {
|
|
1053
1116
|
// Regular items only support above/below, not center
|
|
1054
|
-
|
|
1117
|
+
if (y < rect.height / 2) {
|
|
1118
|
+
position = 'above';
|
|
1119
|
+
}
|
|
1120
|
+
else {
|
|
1121
|
+
position = 'below';
|
|
1122
|
+
}
|
|
1055
1123
|
this.clearNavItemDragExpandTimeout();
|
|
1056
1124
|
}
|
|
1057
1125
|
// Determine what the new parent would be
|
|
1058
|
-
|
|
1126
|
+
let newParentId;
|
|
1127
|
+
if (position === 'center') {
|
|
1128
|
+
newParentId = item.id;
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
newParentId = item.parentId;
|
|
1132
|
+
}
|
|
1059
1133
|
const draggedItem = this.getComputedNav().find(i => i.id === this.draggedNavItemId);
|
|
1060
1134
|
// Don't show drop indicator if invalid drop:
|
|
1061
1135
|
// 1. Can't drop an item into itself (newParentId === draggedNavItemId)
|
|
@@ -1115,13 +1189,15 @@
|
|
|
1115
1189
|
* with new order values for the dragged item and shifts siblings as needed.
|
|
1116
1190
|
*/
|
|
1117
1191
|
executeNavItemDrop() {
|
|
1118
|
-
if (!this.draggedNavItemId || !this.navItemDropTargetId)
|
|
1192
|
+
if (!this.draggedNavItemId || !this.navItemDropTargetId) {
|
|
1119
1193
|
return;
|
|
1194
|
+
}
|
|
1120
1195
|
const allItems = this.getComputedNav();
|
|
1121
1196
|
const draggedItem = allItems.find(i => i.id === this.draggedNavItemId);
|
|
1122
1197
|
const targetItem = allItems.find(i => i.id === this.navItemDropTargetId);
|
|
1123
|
-
if (!draggedItem || !targetItem)
|
|
1198
|
+
if (!draggedItem || !targetItem) {
|
|
1124
1199
|
return;
|
|
1200
|
+
}
|
|
1125
1201
|
// Determine the new parentId for the dragged item
|
|
1126
1202
|
let newParentId;
|
|
1127
1203
|
if (this.navItemDropPosition === 'center' && targetItem.type === 'group') {
|
|
@@ -1133,8 +1209,9 @@
|
|
|
1133
1209
|
else {
|
|
1134
1210
|
newParentId = targetItem.parentId;
|
|
1135
1211
|
}
|
|
1136
|
-
if (newParentId === this.draggedNavItemId)
|
|
1212
|
+
if (newParentId === this.draggedNavItemId) {
|
|
1137
1213
|
return;
|
|
1214
|
+
}
|
|
1138
1215
|
// Get siblings in the destination parent
|
|
1139
1216
|
const siblings = allItems
|
|
1140
1217
|
.filter(i => i.parentId === newParentId && i.id !== this.draggedNavItemId)
|
|
@@ -1205,8 +1282,9 @@
|
|
|
1205
1282
|
// Rendering
|
|
1206
1283
|
// =========================================================================
|
|
1207
1284
|
renderNormalFooter() {
|
|
1208
|
-
if (!this.user)
|
|
1285
|
+
if (!this.user) {
|
|
1209
1286
|
return A;
|
|
1287
|
+
}
|
|
1210
1288
|
const initials = this.user.name
|
|
1211
1289
|
.split(' ')
|
|
1212
1290
|
.map((part) => part[0])
|
|
@@ -1303,7 +1381,11 @@
|
|
|
1303
1381
|
stroke="currentColor"
|
|
1304
1382
|
stroke-width="2"
|
|
1305
1383
|
>
|
|
1306
|
-
<path
|
|
1384
|
+
<path
|
|
1385
|
+
d="M6 9l6 6 6-6"
|
|
1386
|
+
stroke-linecap="round"
|
|
1387
|
+
stroke-linejoin="round"
|
|
1388
|
+
/>
|
|
1307
1389
|
</svg>
|
|
1308
1390
|
</button>
|
|
1309
1391
|
<div class=${e({
|
|
@@ -1355,7 +1437,11 @@
|
|
|
1355
1437
|
</div>
|
|
1356
1438
|
|
|
1357
1439
|
${this.subbar ? b `
|
|
1358
|
-
<kr-subbar
|
|
1440
|
+
<kr-subbar
|
|
1441
|
+
menu
|
|
1442
|
+
.breadcrumbs=${this.breadcrumbs}
|
|
1443
|
+
@menu-click=${this.handleMenuClick}
|
|
1444
|
+
>
|
|
1359
1445
|
<slot name="subbar"></slot>
|
|
1360
1446
|
</kr-subbar>
|
|
1361
1447
|
` : A}
|
|
@@ -1371,7 +1457,11 @@
|
|
|
1371
1457
|
${this.label
|
|
1372
1458
|
? b `<span class="nav-title">${this.label}</span>`
|
|
1373
1459
|
: this.logo
|
|
1374
|
-
? b `<img
|
|
1460
|
+
? b `<img
|
|
1461
|
+
class="nav-logo"
|
|
1462
|
+
src=${this.logo}
|
|
1463
|
+
alt="Logo"
|
|
1464
|
+
/>`
|
|
1375
1465
|
: A}
|
|
1376
1466
|
</div>
|
|
1377
1467
|
<div class="nav-content" @scroll=${this.handleNavScroll}>
|
|
@@ -1379,7 +1469,11 @@
|
|
|
1379
1469
|
<div class="nav-search">
|
|
1380
1470
|
<div class="nav-search__wrapper">
|
|
1381
1471
|
<span class="nav-search__icon">
|
|
1382
|
-
<svg
|
|
1472
|
+
<svg
|
|
1473
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1474
|
+
viewBox="0 0 24 24"
|
|
1475
|
+
fill="currentColor"
|
|
1476
|
+
>
|
|
1383
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"/>
|
|
1384
1478
|
</svg>
|
|
1385
1479
|
</span>
|
|
@@ -1392,7 +1486,11 @@
|
|
|
1392
1486
|
/>
|
|
1393
1487
|
${this.navQuery ? b `
|
|
1394
1488
|
<button class="nav-search__clear" @click=${this.handleNavQueryClear}>
|
|
1395
|
-
<svg
|
|
1489
|
+
<svg
|
|
1490
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1491
|
+
viewBox="0 0 24 24"
|
|
1492
|
+
fill="currentColor"
|
|
1493
|
+
>
|
|
1396
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"/>
|
|
1397
1495
|
</svg>
|
|
1398
1496
|
</button>
|
|
@@ -1584,7 +1682,7 @@
|
|
|
1584
1682
|
.nav-content {
|
|
1585
1683
|
flex: 1;
|
|
1586
1684
|
overflow-y: auto;
|
|
1587
|
-
padding: 0 9px
|
|
1685
|
+
padding: 0 9px 12px 8px;
|
|
1588
1686
|
}
|
|
1589
1687
|
|
|
1590
1688
|
.nav-footer {
|
|
@@ -1704,7 +1802,7 @@
|
|
|
1704
1802
|
|
|
1705
1803
|
.breadcrumbs {
|
|
1706
1804
|
height: 32px;
|
|
1707
|
-
padding: 0
|
|
1805
|
+
padding: 0 16px;
|
|
1708
1806
|
display: flex;
|
|
1709
1807
|
align-items: center;
|
|
1710
1808
|
gap: 6px;
|
|
@@ -1798,7 +1896,7 @@
|
|
|
1798
1896
|
white-space: nowrap;
|
|
1799
1897
|
overflow: hidden;
|
|
1800
1898
|
text-overflow: ellipsis;
|
|
1801
|
-
letter-spacing: 0.
|
|
1899
|
+
letter-spacing: 0.14px;
|
|
1802
1900
|
}
|
|
1803
1901
|
|
|
1804
1902
|
.nav-item__chevron {
|
|
@@ -1853,7 +1951,7 @@
|
|
|
1853
1951
|
font-size: 11px;
|
|
1854
1952
|
font-weight: 600;
|
|
1855
1953
|
text-transform: uppercase;
|
|
1856
|
-
letter-spacing: 0.
|
|
1954
|
+
letter-spacing: 0.55px;
|
|
1857
1955
|
color: var(--kr-scaffold-nav-text);
|
|
1858
1956
|
opacity: 0.6;
|
|
1859
1957
|
}
|
|
@@ -1976,7 +2074,7 @@
|
|
|
1976
2074
|
|
|
1977
2075
|
.breadcrumbs {
|
|
1978
2076
|
height: 32px;
|
|
1979
|
-
padding: 0
|
|
2077
|
+
padding: 0 16px;
|
|
1980
2078
|
display: flex;
|
|
1981
2079
|
align-items: center;
|
|
1982
2080
|
gap: 6px;
|
|
@@ -2144,6 +2242,61 @@
|
|
|
2144
2242
|
}
|
|
2145
2243
|
}
|
|
2146
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);
|
|
2147
2300
|
__decorate$5([
|
|
2148
2301
|
r()
|
|
2149
2302
|
], exports.Scaffold.prototype, "navItemsExpanded", void 0);
|
|
@@ -2180,39 +2333,6 @@
|
|
|
2180
2333
|
__decorate$5([
|
|
2181
2334
|
r()
|
|
2182
2335
|
], exports.Scaffold.prototype, "pendingRequests", void 0);
|
|
2183
|
-
__decorate$5([
|
|
2184
|
-
n({ type: String })
|
|
2185
|
-
], exports.Scaffold.prototype, "logo", void 0);
|
|
2186
|
-
__decorate$5([
|
|
2187
|
-
n({ type: String })
|
|
2188
|
-
], exports.Scaffold.prototype, "label", void 0);
|
|
2189
|
-
__decorate$5([
|
|
2190
|
-
n({ type: String, attribute: 'home-url' })
|
|
2191
|
-
], exports.Scaffold.prototype, "homeUrl", void 0);
|
|
2192
|
-
__decorate$5([
|
|
2193
|
-
n({ type: String, reflect: true })
|
|
2194
|
-
], exports.Scaffold.prototype, "scheme", void 0);
|
|
2195
|
-
__decorate$5([
|
|
2196
|
-
n({ type: Array })
|
|
2197
|
-
], exports.Scaffold.prototype, "nav", void 0);
|
|
2198
|
-
__decorate$5([
|
|
2199
|
-
n({ type: Boolean, attribute: 'nav-icons-displayed', reflect: true })
|
|
2200
|
-
], exports.Scaffold.prototype, "navIconsDisplayed", void 0);
|
|
2201
|
-
__decorate$5([
|
|
2202
|
-
n({ type: Boolean, attribute: 'nav-expanded' })
|
|
2203
|
-
], exports.Scaffold.prototype, "navExpanded", void 0);
|
|
2204
|
-
__decorate$5([
|
|
2205
|
-
n({ type: Object })
|
|
2206
|
-
], exports.Scaffold.prototype, "user", void 0);
|
|
2207
|
-
__decorate$5([
|
|
2208
|
-
n({ type: Boolean })
|
|
2209
|
-
], exports.Scaffold.prototype, "subbar", void 0);
|
|
2210
|
-
__decorate$5([
|
|
2211
|
-
n({ type: Boolean, attribute: 'nav-enabled' })
|
|
2212
|
-
], exports.Scaffold.prototype, "navEnabled", void 0);
|
|
2213
|
-
__decorate$5([
|
|
2214
|
-
n({ type: Array })
|
|
2215
|
-
], exports.Scaffold.prototype, "breadcrumbs", void 0);
|
|
2216
2336
|
exports.Scaffold = __decorate$5([
|
|
2217
2337
|
t$1('kr-scaffold')
|
|
2218
2338
|
], exports.Scaffold);
|
|
@@ -2233,7 +2353,7 @@
|
|
|
2233
2353
|
* <kr-screen-nav
|
|
2234
2354
|
* label="Acme Corporation"
|
|
2235
2355
|
* subLabel="Company"
|
|
2236
|
-
* .
|
|
2356
|
+
* .items=${[
|
|
2237
2357
|
* { id: 'overview', label: 'Overview', url: '/companies/123/overview' },
|
|
2238
2358
|
* { id: 'contacts', label: 'Contacts', url: '/companies/123/contacts' },
|
|
2239
2359
|
* { id: 'activity', label: 'Activity', url: '/companies/123/activity' },
|
|
@@ -2248,7 +2368,7 @@
|
|
|
2248
2368
|
*
|
|
2249
2369
|
* @property {string} label - Main label (e.g., entity name like "Acme Corporation")
|
|
2250
2370
|
* @property {string} subLabel - Sub label (e.g., entity type like "Company")
|
|
2251
|
-
* @property {KRScreenNavItem[]}
|
|
2371
|
+
* @property {KRScreenNavItem[]} items - Navigation items as JSON array
|
|
2252
2372
|
* @property {string} activeId - Currently active item ID
|
|
2253
2373
|
*/
|
|
2254
2374
|
exports.ScreenNav = class KRScreenNav extends i$1 {
|
|
@@ -2265,7 +2385,7 @@
|
|
|
2265
2385
|
/**
|
|
2266
2386
|
* Navigation items
|
|
2267
2387
|
*/
|
|
2268
|
-
this.
|
|
2388
|
+
this.items = [];
|
|
2269
2389
|
/**
|
|
2270
2390
|
* Currently active item ID. Auto-detected from the URL by default.
|
|
2271
2391
|
* Can be set explicitly by the parent to override.
|
|
@@ -2277,7 +2397,7 @@
|
|
|
2277
2397
|
}
|
|
2278
2398
|
connectedCallback() {
|
|
2279
2399
|
super.connectedCallback();
|
|
2280
|
-
// Defer so property bindings (.
|
|
2400
|
+
// Defer so property bindings (.items) have been applied
|
|
2281
2401
|
requestAnimationFrame(() => this.syncActiveFromUrl());
|
|
2282
2402
|
window.addEventListener('popstate', this.handlePopstate);
|
|
2283
2403
|
}
|
|
@@ -2316,7 +2436,7 @@
|
|
|
2316
2436
|
}
|
|
2317
2437
|
syncActiveFromUrl() {
|
|
2318
2438
|
const pathname = window.location.pathname;
|
|
2319
|
-
const match = this.
|
|
2439
|
+
const match = this.items.find((item) => {
|
|
2320
2440
|
if (!item.url) {
|
|
2321
2441
|
return false;
|
|
2322
2442
|
}
|
|
@@ -2344,9 +2464,12 @@
|
|
|
2344
2464
|
` : A}
|
|
2345
2465
|
<div class="nav-content">
|
|
2346
2466
|
<div class="nav-items">
|
|
2347
|
-
${this.
|
|
2467
|
+
${this.items.map((item) => b `
|
|
2348
2468
|
<a
|
|
2349
|
-
class
|
|
2469
|
+
class=${e({
|
|
2470
|
+
'nav-item': true,
|
|
2471
|
+
'nav-item--active': item.id === this.activeId
|
|
2472
|
+
})}
|
|
2350
2473
|
href=${item.url || '#'}
|
|
2351
2474
|
@click=${(e) => this.handleNavItemClick(e, item)}
|
|
2352
2475
|
>
|
|
@@ -2409,7 +2532,7 @@
|
|
|
2409
2532
|
.nav-content {
|
|
2410
2533
|
flex: 1;
|
|
2411
2534
|
overflow-y: auto;
|
|
2412
|
-
padding: 0 0
|
|
2535
|
+
padding: 0 0 12px;
|
|
2413
2536
|
}
|
|
2414
2537
|
|
|
2415
2538
|
.nav-items {
|
|
@@ -2423,7 +2546,7 @@
|
|
|
2423
2546
|
padding: 0 12px;
|
|
2424
2547
|
height: 32px;
|
|
2425
2548
|
line-height: 32px;
|
|
2426
|
-
letter-spacing: 0.
|
|
2549
|
+
letter-spacing: 0.13px;
|
|
2427
2550
|
color: rgb(32, 33, 36);
|
|
2428
2551
|
text-decoration: none;
|
|
2429
2552
|
font-size: 13px;
|
|
@@ -2464,7 +2587,7 @@
|
|
|
2464
2587
|
], exports.ScreenNav.prototype, "subLabel", void 0);
|
|
2465
2588
|
__decorate$4([
|
|
2466
2589
|
n({ type: Array })
|
|
2467
|
-
], exports.ScreenNav.prototype, "
|
|
2590
|
+
], exports.ScreenNav.prototype, "items", void 0);
|
|
2468
2591
|
__decorate$4([
|
|
2469
2592
|
n({ type: String })
|
|
2470
2593
|
], exports.ScreenNav.prototype, "activeId", void 0);
|
|
@@ -2534,7 +2657,7 @@
|
|
|
2534
2657
|
display: flex;
|
|
2535
2658
|
align-items: center;
|
|
2536
2659
|
justify-content: space-between;
|
|
2537
|
-
padding: 0
|
|
2660
|
+
padding: 0 24px;
|
|
2538
2661
|
background: #ffffff;
|
|
2539
2662
|
border-bottom: 1px solid #e5e7eb;
|
|
2540
2663
|
flex-shrink: 0;
|
|
@@ -2556,7 +2679,7 @@
|
|
|
2556
2679
|
.content {
|
|
2557
2680
|
flex: 1;
|
|
2558
2681
|
overflow-y: auto;
|
|
2559
|
-
padding:
|
|
2682
|
+
padding: 24px;
|
|
2560
2683
|
display: flex;
|
|
2561
2684
|
justify-content: center;
|
|
2562
2685
|
}
|
|
@@ -2597,7 +2720,10 @@
|
|
|
2597
2720
|
this.menu = false;
|
|
2598
2721
|
}
|
|
2599
2722
|
_handleMenuClick() {
|
|
2600
|
-
this.dispatchEvent(new CustomEvent('menu-click', {
|
|
2723
|
+
this.dispatchEvent(new CustomEvent('menu-click', {
|
|
2724
|
+
bubbles: true,
|
|
2725
|
+
composed: true
|
|
2726
|
+
}));
|
|
2601
2727
|
}
|
|
2602
2728
|
/**
|
|
2603
2729
|
* Resolves a breadcrumb URL by prepending the base href.
|
|
@@ -2614,13 +2740,19 @@
|
|
|
2614
2740
|
* - resolveUrl(crumb) = "/operations/settings" (used for href)
|
|
2615
2741
|
*/
|
|
2616
2742
|
resolveUrl(crumb) {
|
|
2617
|
-
if (!crumb.url)
|
|
2743
|
+
if (!crumb.url) {
|
|
2618
2744
|
return '#';
|
|
2745
|
+
}
|
|
2619
2746
|
// Remove leading slash from url if present, then combine with base
|
|
2620
|
-
|
|
2747
|
+
let path = crumb.url;
|
|
2748
|
+
if (crumb.url.startsWith('/')) {
|
|
2749
|
+
path = crumb.url.slice(1);
|
|
2750
|
+
}
|
|
2621
2751
|
const baseHref = document.querySelector('base')?.getAttribute('href') || '/';
|
|
2622
|
-
|
|
2623
|
-
|
|
2752
|
+
if (baseHref.endsWith('/')) {
|
|
2753
|
+
return baseHref + path;
|
|
2754
|
+
}
|
|
2755
|
+
return baseHref + '/' + path;
|
|
2624
2756
|
}
|
|
2625
2757
|
/**
|
|
2626
2758
|
* Handles click on a breadcrumb link.
|
|
@@ -2652,10 +2784,31 @@
|
|
|
2652
2784
|
return b `
|
|
2653
2785
|
${this.menu ? b `
|
|
2654
2786
|
<div class="menu" @click=${this._handleMenuClick}>
|
|
2655
|
-
<svg
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
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>
|
|
2659
2812
|
</svg>
|
|
2660
2813
|
</div>
|
|
2661
2814
|
` : A}
|
|
@@ -2665,7 +2818,10 @@
|
|
|
2665
2818
|
return b `
|
|
2666
2819
|
${index > 0 ? b `<span class="breadcrumb-separator">/</span>` : ''}
|
|
2667
2820
|
<a
|
|
2668
|
-
class=${e({
|
|
2821
|
+
class=${e({
|
|
2822
|
+
'breadcrumb': true,
|
|
2823
|
+
'breadcrumb--current': isCurrent
|
|
2824
|
+
})}
|
|
2669
2825
|
href=${this.resolveUrl(crumb)}
|
|
2670
2826
|
@click=${(e) => this._handleBreadcrumbClick(e, crumb, isCurrent)}
|
|
2671
2827
|
>${crumb.label}</a>
|
|
@@ -2834,7 +2990,10 @@
|
|
|
2834
2990
|
for (const route of routes) {
|
|
2835
2991
|
const routeSegments = route.path.split('/').filter(s => s);
|
|
2836
2992
|
if (routeSegments.length === 0 && segments.length === 0) {
|
|
2837
|
-
return [{
|
|
2993
|
+
return [{
|
|
2994
|
+
route,
|
|
2995
|
+
params: {}
|
|
2996
|
+
}];
|
|
2838
2997
|
}
|
|
2839
2998
|
if (routeSegments.length === 0) {
|
|
2840
2999
|
continue;
|
|
@@ -2861,7 +3020,10 @@
|
|
|
2861
3020
|
if (remaining.length > 0 && !route.children) {
|
|
2862
3021
|
continue;
|
|
2863
3022
|
}
|
|
2864
|
-
const result = [{
|
|
3023
|
+
const result = [{
|
|
3024
|
+
route,
|
|
3025
|
+
params
|
|
3026
|
+
}];
|
|
2865
3027
|
if (route.children && remaining.length > 0) {
|
|
2866
3028
|
const childMatches = KRRouter.matchRoutes(route.children, remaining);
|
|
2867
3029
|
if (childMatches.length > 0) {
|
|
@@ -2941,95 +3103,2526 @@
|
|
|
2941
3103
|
t$1('kr-router-outlet')
|
|
2942
3104
|
], exports.RouterOutlet);
|
|
2943
3105
|
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
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
|
+
"'": '''
|
|
2949
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
|
+
}
|
|
2950
3250
|
/**
|
|
2951
|
-
*
|
|
2952
|
-
*
|
|
2953
|
-
* Accepts a `send` callback that returns a streaming Response (SSE format).
|
|
2954
|
-
* Parses the stream for text tokens, status updates, errors, and frontend actions.
|
|
2955
|
-
* When an ACTION event arrives, dispatches a cancelable `action` custom event.
|
|
2956
|
-
* If not prevented, executes the native browser action (reload, navigate, etc.).
|
|
2957
|
-
*
|
|
2958
|
-
* ## SSE Stream Format
|
|
2959
|
-
* The component expects `data:` lines with JSON payloads containing a `type` field:
|
|
2960
|
-
* - `STATUS` — `{ type: "STATUS", message: "Thinking..." }`
|
|
2961
|
-
* - `RESPONSE_PART` — `{ type: "RESPONSE_PART", message: "token text" }`
|
|
2962
|
-
* - `ACTION` — `{ type: "ACTION", action: "RELOAD" }` (see KRChatbotAction)
|
|
2963
|
-
* - `ERROR` — `{ type: "ERROR", message: "Something went wrong" }`
|
|
2964
|
-
* - `HEARTBEAT` — `{ type: "HEARTBEAT" }` (ignored)
|
|
3251
|
+
* Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
|
|
3252
|
+
* /c*$/ is vulnerable to REDOS.
|
|
2965
3253
|
*
|
|
2966
|
-
*
|
|
2967
|
-
*
|
|
2968
|
-
*
|
|
2969
|
-
* ```html
|
|
2970
|
-
* <kr-chatbot
|
|
2971
|
-
* title="AI Assistant"
|
|
2972
|
-
* .send=${(params) => {
|
|
2973
|
-
* return fetch('/api/ai/chat/stream', {
|
|
2974
|
-
* method: 'POST',
|
|
2975
|
-
* headers: { 'Content-Type': 'application/json', 'Accept': 'text/event-stream' },
|
|
2976
|
-
* credentials: 'include',
|
|
2977
|
-
* body: JSON.stringify({ userText: params.message })
|
|
2978
|
-
* });
|
|
2979
|
-
* }}
|
|
2980
|
-
* ></kr-chatbot>
|
|
2981
|
-
* ```
|
|
2982
|
-
*
|
|
2983
|
-
* @slot - Optional slot content displayed above the messages area
|
|
2984
|
-
*
|
|
2985
|
-
* @fires message-submit - When the user sends a message. Cancelable. Detail: `{ message: string }`
|
|
2986
|
-
* @fires action - When the AI requests a frontend action. Cancelable. Detail: `{ action: KRChatbotAction }`
|
|
3254
|
+
* @param str
|
|
3255
|
+
* @param c
|
|
3256
|
+
* @param invert Remove suffix of non-c chars instead. Default falsey.
|
|
2987
3257
|
*/
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
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
|
-
|
|
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...';
|
|
3033
5626
|
/**
|
|
3034
5627
|
* Whether the chat panel is open. Only relevant in floating mode.
|
|
3035
5628
|
*/
|
|
@@ -3044,9 +5637,8 @@
|
|
|
3044
5637
|
let newLeft = this._dragStartLeft + (e.clientX - this._dragStartX);
|
|
3045
5638
|
let newTop = this._dragStartTop + (e.clientY - this._dragStartY);
|
|
3046
5639
|
const w = this._panelEl.offsetWidth;
|
|
3047
|
-
const h = this._panelEl.offsetHeight;
|
|
3048
5640
|
newLeft = Math.max(-w + 48, Math.min(newLeft, window.innerWidth - 48));
|
|
3049
|
-
newTop = Math.max(
|
|
5641
|
+
newTop = Math.max(0, Math.min(newTop, window.innerHeight - 48));
|
|
3050
5642
|
this._panelEl.style.left = newLeft + 'px';
|
|
3051
5643
|
this._panelEl.style.top = newTop + 'px';
|
|
3052
5644
|
}
|
|
@@ -3065,21 +5657,15 @@
|
|
|
3065
5657
|
newW = Math.max(minW, Math.min(this._resizeStartW + dx, maxW));
|
|
3066
5658
|
}
|
|
3067
5659
|
if (this._resizeDir.includes('w')) {
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
newW = proposedW;
|
|
3071
|
-
newLeft = this._dragStartLeft + dx;
|
|
3072
|
-
}
|
|
5660
|
+
newW = Math.max(minW, Math.min(this._resizeStartW - dx, maxW));
|
|
5661
|
+
newLeft = this._dragStartLeft + (this._resizeStartW - newW);
|
|
3073
5662
|
}
|
|
3074
5663
|
if (this._resizeDir.includes('s')) {
|
|
3075
5664
|
newH = Math.max(minH, Math.min(this._resizeStartH + dy, maxH));
|
|
3076
5665
|
}
|
|
3077
5666
|
if (this._resizeDir.includes('n')) {
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
newH = proposedH;
|
|
3081
|
-
newTop = this._dragStartTop + dy;
|
|
3082
|
-
}
|
|
5667
|
+
newH = Math.max(minH, Math.min(this._resizeStartH - dy, maxH));
|
|
5668
|
+
newTop = this._dragStartTop + (this._resizeStartH - newH);
|
|
3083
5669
|
}
|
|
3084
5670
|
newLeft = Math.max(0, newLeft);
|
|
3085
5671
|
newTop = Math.max(0, newTop);
|
|
@@ -3092,17 +5678,30 @@
|
|
|
3092
5678
|
this._handleMouseUp = () => {
|
|
3093
5679
|
if (this._isDragging) {
|
|
3094
5680
|
this._isDragging = false;
|
|
3095
|
-
this._panelEl.classList.remove('
|
|
5681
|
+
this._panelEl.classList.remove('panel--dragging');
|
|
3096
5682
|
}
|
|
3097
5683
|
if (this._isResizing) {
|
|
3098
5684
|
this._isResizing = false;
|
|
3099
|
-
this._panelEl.classList.remove('
|
|
5685
|
+
this._panelEl.classList.remove('panel--resizing');
|
|
3100
5686
|
}
|
|
5687
|
+
this._hideOverlay();
|
|
3101
5688
|
document.removeEventListener('mousemove', this._handleMouseMove);
|
|
3102
5689
|
document.removeEventListener('mouseup', this._handleMouseUp);
|
|
5690
|
+
this._saveState();
|
|
3103
5691
|
};
|
|
3104
5692
|
}
|
|
3105
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
|
+
}
|
|
3106
5705
|
disconnectedCallback() {
|
|
3107
5706
|
super.disconnectedCallback();
|
|
3108
5707
|
if (this._reader) {
|
|
@@ -3114,7 +5713,7 @@
|
|
|
3114
5713
|
}
|
|
3115
5714
|
updated(changed) {
|
|
3116
5715
|
if (changed.has('messages') || changed.has('_statusText')) {
|
|
3117
|
-
if (!this._userHasScrolledUp && this._messagesEl) {
|
|
5716
|
+
if (!this._userHasScrolledUp && !this._isStreaming && this._messagesEl) {
|
|
3118
5717
|
requestAnimationFrame(() => {
|
|
3119
5718
|
if (this._messagesEl) {
|
|
3120
5719
|
this._messagesEl.scrollTop = this._messagesEl.scrollHeight;
|
|
@@ -3138,6 +5737,38 @@
|
|
|
3138
5737
|
this._handleStop();
|
|
3139
5738
|
}
|
|
3140
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
|
+
}
|
|
3141
5772
|
_handleHeaderMouseDown(e) {
|
|
3142
5773
|
// Ignore clicks on buttons inside the header
|
|
3143
5774
|
if (e.target.closest('button')) {
|
|
@@ -3147,7 +5778,8 @@
|
|
|
3147
5778
|
return;
|
|
3148
5779
|
}
|
|
3149
5780
|
this._isDragging = true;
|
|
3150
|
-
this._panelEl.classList.add('
|
|
5781
|
+
this._panelEl.classList.add('panel--dragging');
|
|
5782
|
+
this._showOverlay();
|
|
3151
5783
|
const rect = this._panelEl.getBoundingClientRect();
|
|
3152
5784
|
this._panelEl.style.left = rect.left + 'px';
|
|
3153
5785
|
this._panelEl.style.top = rect.top + 'px';
|
|
@@ -3168,7 +5800,8 @@
|
|
|
3168
5800
|
}
|
|
3169
5801
|
this._isResizing = true;
|
|
3170
5802
|
this._resizeDir = e.currentTarget.dataset.dir || '';
|
|
3171
|
-
this._panelEl.classList.add('
|
|
5803
|
+
this._panelEl.classList.add('panel--resizing');
|
|
5804
|
+
this._showOverlay();
|
|
3172
5805
|
const rect = this._panelEl.getBoundingClientRect();
|
|
3173
5806
|
this._panelEl.style.left = rect.left + 'px';
|
|
3174
5807
|
this._panelEl.style.top = rect.top + 'px';
|
|
@@ -3188,16 +5821,75 @@
|
|
|
3188
5821
|
}
|
|
3189
5822
|
_handleToggle() {
|
|
3190
5823
|
this.opened = !this.opened;
|
|
3191
|
-
// Reset position when opening so it returns to default bottom-right
|
|
3192
5824
|
if (this.opened && this._panelEl) {
|
|
3193
|
-
this.
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
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
|
+
}
|
|
3200
5891
|
}
|
|
5892
|
+
catch { /* localStorage not available */ }
|
|
3201
5893
|
}
|
|
3202
5894
|
_handleInputChange(e) {
|
|
3203
5895
|
this._inputValue = e.target.value;
|
|
@@ -3247,6 +5939,27 @@
|
|
|
3247
5939
|
}];
|
|
3248
5940
|
this._isStreaming = true;
|
|
3249
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
|
+
});
|
|
3250
5963
|
this.send({
|
|
3251
5964
|
message: messageText,
|
|
3252
5965
|
messages: this.messages.slice(0, -1),
|
|
@@ -3374,49 +6087,6 @@
|
|
|
3374
6087
|
}
|
|
3375
6088
|
}
|
|
3376
6089
|
}
|
|
3377
|
-
else if (data.type === 'ACTION') {
|
|
3378
|
-
const action = data;
|
|
3379
|
-
const actionEvent = new CustomEvent('action', {
|
|
3380
|
-
detail: {
|
|
3381
|
-
action: action,
|
|
3382
|
-
},
|
|
3383
|
-
bubbles: true,
|
|
3384
|
-
composed: true,
|
|
3385
|
-
cancelable: true,
|
|
3386
|
-
});
|
|
3387
|
-
this.dispatchEvent(actionEvent);
|
|
3388
|
-
if (!actionEvent.defaultPrevented) {
|
|
3389
|
-
if (action.action === 'RELOAD') {
|
|
3390
|
-
location.reload();
|
|
3391
|
-
}
|
|
3392
|
-
else if (action.action === 'NAVIGATE') {
|
|
3393
|
-
if (action.data && action.data.url) {
|
|
3394
|
-
window.location.href = action.data.url;
|
|
3395
|
-
}
|
|
3396
|
-
}
|
|
3397
|
-
else if (action.action === 'OPEN_TAB') {
|
|
3398
|
-
if (action.data && action.data.url) {
|
|
3399
|
-
window.open(action.data.url, '_blank');
|
|
3400
|
-
}
|
|
3401
|
-
}
|
|
3402
|
-
else if (action.action === 'UPDATE_HTML') {
|
|
3403
|
-
if (action.data && action.data.selector && action.data.html) {
|
|
3404
|
-
const el = document.querySelector(action.data.selector);
|
|
3405
|
-
if (el) {
|
|
3406
|
-
el.innerHTML = action.data.html;
|
|
3407
|
-
}
|
|
3408
|
-
}
|
|
3409
|
-
}
|
|
3410
|
-
else if (action.action === 'SCROLL_TO') {
|
|
3411
|
-
if (action.data && action.data.selector) {
|
|
3412
|
-
const el = document.querySelector(action.data.selector);
|
|
3413
|
-
if (el) {
|
|
3414
|
-
el.scrollIntoView({ behavior: 'smooth' });
|
|
3415
|
-
}
|
|
3416
|
-
}
|
|
3417
|
-
}
|
|
3418
|
-
}
|
|
3419
|
-
}
|
|
3420
6090
|
else if (data.type === 'ERROR') {
|
|
3421
6091
|
this._handleStreamError(String(data.message || 'Unknown error'));
|
|
3422
6092
|
}
|
|
@@ -3450,157 +6120,192 @@
|
|
|
3450
6120
|
}
|
|
3451
6121
|
}
|
|
3452
6122
|
}
|
|
3453
|
-
/**
|
|
3454
|
-
* Lightweight text formatting for assistant messages.
|
|
3455
|
-
* Handles code blocks, inline code, bold, and line breaks.
|
|
3456
|
-
* Escapes HTML entities first to prevent XSS.
|
|
3457
|
-
*/
|
|
3458
|
-
_formatAssistantContent(text) {
|
|
3459
|
-
// Escape HTML entities
|
|
3460
|
-
let escaped = text
|
|
3461
|
-
.replace(/&/g, '&')
|
|
3462
|
-
.replace(/</g, '<')
|
|
3463
|
-
.replace(/>/g, '>')
|
|
3464
|
-
.replace(/"/g, '"')
|
|
3465
|
-
.replace(/'/g, ''');
|
|
3466
|
-
// Code blocks: ```...```
|
|
3467
|
-
escaped = escaped.replace(/```(\w*)\n?([\s\S]*?)```/g, '<pre><code>$2</code></pre>');
|
|
3468
|
-
// Inline code: `...`
|
|
3469
|
-
escaped = escaped.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
3470
|
-
// Bold: **...**
|
|
3471
|
-
escaped = escaped.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
|
3472
|
-
// Split on code blocks to only convert newlines outside of pre tags
|
|
3473
|
-
const parts = escaped.split(/(<pre><code>[\s\S]*?<\/code><\/pre>)/g);
|
|
3474
|
-
for (let i = 0; i < parts.length; i++) {
|
|
3475
|
-
// Skip code blocks (odd indices from the split)
|
|
3476
|
-
if (i % 2 === 0) {
|
|
3477
|
-
// Convert double newlines to paragraph breaks
|
|
3478
|
-
parts[i] = parts[i].replace(/\n\n/g, '</p><p>');
|
|
3479
|
-
// Convert single newlines to line breaks
|
|
3480
|
-
parts[i] = parts[i].replace(/\n/g, '<br>');
|
|
3481
|
-
}
|
|
3482
|
-
}
|
|
3483
|
-
escaped = parts.join('');
|
|
3484
|
-
// Wrap in paragraph if we inserted paragraph breaks
|
|
3485
|
-
if (escaped.includes('</p><p>')) {
|
|
3486
|
-
escaped = '<p>' + escaped + '</p>';
|
|
3487
|
-
}
|
|
3488
|
-
return escaped;
|
|
3489
|
-
}
|
|
3490
6123
|
// --- Render ---
|
|
3491
6124
|
render() {
|
|
3492
6125
|
return b `
|
|
3493
6126
|
<button
|
|
3494
6127
|
class=${e({
|
|
3495
|
-
'
|
|
3496
|
-
'
|
|
6128
|
+
'toggle': true,
|
|
6129
|
+
'toggle--hidden': this.opened,
|
|
3497
6130
|
})}
|
|
3498
6131
|
@click=${this._handleToggle}
|
|
3499
6132
|
title="Open chat"
|
|
3500
6133
|
>
|
|
3501
|
-
<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
|
+
>
|
|
3502
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"/>
|
|
3503
6143
|
<path d="M8 11h.01M12 11h.01M16 11h.01" stroke-width="2.5"/>
|
|
3504
6144
|
</svg>
|
|
3505
6145
|
</button>
|
|
3506
6146
|
|
|
3507
6147
|
<div class=${e({
|
|
3508
|
-
'
|
|
3509
|
-
'
|
|
6148
|
+
'panel': true,
|
|
6149
|
+
'panel--opened': this.opened,
|
|
3510
6150
|
})}>
|
|
3511
|
-
<div
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
<div
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
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>
|
|
3528
6195
|
${this.subtitle ? b `
|
|
3529
|
-
<span class="
|
|
6196
|
+
<span class="header-subtitle">${this.subtitle}</span>
|
|
3530
6197
|
` : A}
|
|
3531
6198
|
</div>
|
|
3532
6199
|
</div>
|
|
3533
|
-
<div class="
|
|
6200
|
+
<div class="header-actions">
|
|
3534
6201
|
${this.messages.length > 0 ? b `
|
|
3535
|
-
<button
|
|
3536
|
-
|
|
3537
|
-
|
|
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
|
+
/>
|
|
3538
6220
|
</svg>
|
|
3539
6221
|
</button>
|
|
3540
6222
|
` : A}
|
|
3541
|
-
<button
|
|
3542
|
-
|
|
3543
|
-
|
|
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
|
+
/>
|
|
3544
6241
|
</svg>
|
|
3545
6242
|
</button>
|
|
3546
6243
|
</div>
|
|
3547
6244
|
</div>
|
|
3548
6245
|
|
|
3549
|
-
<div class="
|
|
6246
|
+
<div class="messages" @scroll=${this._handleMessagesScroll}>
|
|
3550
6247
|
${this.messages.length === 0 ? b `
|
|
3551
|
-
<div class="
|
|
3552
|
-
<
|
|
3553
|
-
<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" />
|
|
3554
|
-
</svg>
|
|
3555
|
-
<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>
|
|
3556
6250
|
</div>
|
|
3557
6251
|
` : A}
|
|
3558
6252
|
${this.messages.map((msg) => b `
|
|
3559
6253
|
<div class=${e({
|
|
3560
|
-
'
|
|
3561
|
-
'
|
|
3562
|
-
'
|
|
6254
|
+
'message': true,
|
|
6255
|
+
'message--user': msg.role === 'user',
|
|
6256
|
+
'message--assistant': msg.role === 'assistant',
|
|
3563
6257
|
})}>
|
|
3564
6258
|
${msg.reasoning ? b `
|
|
3565
|
-
<details class="
|
|
6259
|
+
<details class="reasoning">
|
|
3566
6260
|
<summary>Thinking</summary>
|
|
3567
|
-
<div class="
|
|
6261
|
+
<div class="reasoning-content">${msg.reasoning}</div>
|
|
3568
6262
|
</details>
|
|
3569
6263
|
` : A}
|
|
3570
|
-
|
|
3571
|
-
|
|
6264
|
+
${msg.content ? b `
|
|
6265
|
+
<div class="message-content">
|
|
6266
|
+
${msg.role === 'user'
|
|
3572
6267
|
? msg.content
|
|
3573
|
-
: o(
|
|
3574
|
-
|
|
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}
|
|
3575
6278
|
</div>
|
|
3576
6279
|
`)}
|
|
3577
|
-
${this._statusText ? b `
|
|
3578
|
-
<div class="chatbot__message chatbot__message--assistant">
|
|
3579
|
-
<div class="chatbot__typing">
|
|
3580
|
-
<span></span>
|
|
3581
|
-
<span></span>
|
|
3582
|
-
<span></span>
|
|
3583
|
-
</div>
|
|
3584
|
-
</div>
|
|
3585
|
-
` : A}
|
|
3586
6280
|
</div>
|
|
3587
6281
|
|
|
3588
6282
|
${this._error ? b `
|
|
3589
|
-
<div class="
|
|
6283
|
+
<div class="error">
|
|
3590
6284
|
<span>${this._error}</span>
|
|
3591
|
-
<button class="
|
|
3592
|
-
<svg
|
|
3593
|
-
|
|
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
|
+
/>
|
|
3594
6299
|
</svg>
|
|
3595
6300
|
</button>
|
|
3596
6301
|
</div>
|
|
3597
6302
|
` : A}
|
|
3598
6303
|
|
|
3599
|
-
<div class="
|
|
3600
|
-
<div class="
|
|
3601
|
-
<button class="
|
|
6304
|
+
<div class="footer">
|
|
6305
|
+
<div class="input-wrapper">
|
|
6306
|
+
<button class="input-plus" title="Attach">+</button>
|
|
3602
6307
|
<textarea
|
|
3603
|
-
class="
|
|
6308
|
+
class="input"
|
|
3604
6309
|
.value=${this._inputValue}
|
|
3605
6310
|
placeholder=${this.placeholder}
|
|
3606
6311
|
rows="1"
|
|
@@ -3608,21 +6313,45 @@
|
|
|
3608
6313
|
@keydown=${this._handleKeyDown}
|
|
3609
6314
|
></textarea>
|
|
3610
6315
|
${this._isStreaming ? b `
|
|
3611
|
-
<button
|
|
3612
|
-
|
|
3613
|
-
|
|
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
|
+
/>
|
|
3614
6334
|
</svg>
|
|
3615
6335
|
</button>
|
|
3616
6336
|
` : b `
|
|
3617
6337
|
<button
|
|
3618
6338
|
class=${e({
|
|
3619
|
-
'
|
|
3620
|
-
'
|
|
6339
|
+
'send': true,
|
|
6340
|
+
'send--disabled': !this._inputValue.trim(),
|
|
3621
6341
|
})}
|
|
3622
6342
|
@click=${this._handleSubmit}
|
|
3623
6343
|
title="Send message"
|
|
3624
6344
|
>
|
|
3625
|
-
<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
|
+
>
|
|
3626
6355
|
<path d="M12 19V5M5 12l7-7 7 7"/>
|
|
3627
6356
|
</svg>
|
|
3628
6357
|
</button>
|
|
@@ -3653,7 +6382,6 @@
|
|
|
3653
6382
|
--kr-chatbot-text: #1a1a2e;
|
|
3654
6383
|
--kr-chatbot-text-secondary: #64668b;
|
|
3655
6384
|
--kr-chatbot-user-bg: #e9e9e980;
|
|
3656
|
-
--kr-chatbot-user-text: #1a1a2e;
|
|
3657
6385
|
--kr-chatbot-error-bg: #fef2f2;
|
|
3658
6386
|
--kr-chatbot-error-text: #dc2626;
|
|
3659
6387
|
--kr-chatbot-success: #00b894;
|
|
@@ -3670,7 +6398,7 @@
|
|
|
3670
6398
|
}
|
|
3671
6399
|
|
|
3672
6400
|
/* Toggle button */
|
|
3673
|
-
.
|
|
6401
|
+
.toggle {
|
|
3674
6402
|
width: var(--kr-chatbot-toggle-size);
|
|
3675
6403
|
height: var(--kr-chatbot-toggle-size);
|
|
3676
6404
|
border-radius: 18px;
|
|
@@ -3685,24 +6413,24 @@
|
|
|
3685
6413
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
3686
6414
|
}
|
|
3687
6415
|
|
|
3688
|
-
.
|
|
6416
|
+
.toggle:hover {
|
|
3689
6417
|
transform: scale(1.05);
|
|
3690
6418
|
box-shadow: 0 6px 32px rgba(22, 48, 82, 0.5);
|
|
3691
6419
|
}
|
|
3692
6420
|
|
|
3693
|
-
.
|
|
6421
|
+
.toggle--hidden {
|
|
3694
6422
|
transform: scale(0);
|
|
3695
6423
|
opacity: 0;
|
|
3696
6424
|
pointer-events: none;
|
|
3697
6425
|
}
|
|
3698
6426
|
|
|
3699
|
-
.
|
|
6427
|
+
.toggle-icon {
|
|
3700
6428
|
width: 26px;
|
|
3701
6429
|
height: 26px;
|
|
3702
6430
|
}
|
|
3703
6431
|
|
|
3704
6432
|
/* Panel */
|
|
3705
|
-
.
|
|
6433
|
+
.panel {
|
|
3706
6434
|
display: none;
|
|
3707
6435
|
flex-direction: column;
|
|
3708
6436
|
background: var(--kr-chatbot-bg);
|
|
@@ -3720,7 +6448,7 @@
|
|
|
3720
6448
|
transform-origin: bottom right;
|
|
3721
6449
|
}
|
|
3722
6450
|
|
|
3723
|
-
.
|
|
6451
|
+
.panel--opened {
|
|
3724
6452
|
display: flex;
|
|
3725
6453
|
}
|
|
3726
6454
|
|
|
@@ -3736,49 +6464,35 @@
|
|
|
3736
6464
|
}
|
|
3737
6465
|
|
|
3738
6466
|
/* Header */
|
|
3739
|
-
.
|
|
3740
|
-
padding:
|
|
3741
|
-
|
|
3742
|
-
|
|
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);
|
|
3743
6473
|
display: flex;
|
|
3744
6474
|
align-items: center;
|
|
3745
6475
|
justify-content: space-between;
|
|
3746
6476
|
flex-shrink: 0;
|
|
3747
6477
|
}
|
|
3748
6478
|
|
|
3749
|
-
.
|
|
6479
|
+
.header-left {
|
|
3750
6480
|
display: flex;
|
|
3751
6481
|
align-items: center;
|
|
3752
6482
|
gap: 10px;
|
|
3753
6483
|
}
|
|
3754
6484
|
|
|
3755
|
-
|
|
3756
|
-
display: flex;
|
|
3757
|
-
flex-direction: column;
|
|
3758
|
-
gap: 2px;
|
|
3759
|
-
opacity: 0.35;
|
|
3760
|
-
}
|
|
3761
|
-
|
|
3762
|
-
.chatbot__header-drag-hint span {
|
|
3763
|
-
display: block;
|
|
3764
|
-
width: 14px;
|
|
3765
|
-
height: 2px;
|
|
3766
|
-
background: white;
|
|
3767
|
-
border-radius: 1px;
|
|
3768
|
-
}
|
|
3769
|
-
|
|
3770
|
-
.chatbot__header-info {
|
|
6485
|
+
.header-info {
|
|
3771
6486
|
display: flex;
|
|
3772
6487
|
flex-direction: column;
|
|
3773
6488
|
}
|
|
3774
6489
|
|
|
3775
|
-
.
|
|
3776
|
-
font-size:
|
|
6490
|
+
.header-title {
|
|
6491
|
+
font-size: 16px;
|
|
3777
6492
|
font-weight: 600;
|
|
3778
|
-
letter-spacing: -0.2px;
|
|
3779
6493
|
}
|
|
3780
6494
|
|
|
3781
|
-
.
|
|
6495
|
+
.header-subtitle {
|
|
3782
6496
|
color: rgba(255, 255, 255, 0.5);
|
|
3783
6497
|
font-size: 11px;
|
|
3784
6498
|
display: flex;
|
|
@@ -3786,7 +6500,7 @@
|
|
|
3786
6500
|
gap: 4px;
|
|
3787
6501
|
}
|
|
3788
6502
|
|
|
3789
|
-
.
|
|
6503
|
+
.header-subtitle::before {
|
|
3790
6504
|
content: '';
|
|
3791
6505
|
width: 5px;
|
|
3792
6506
|
height: 5px;
|
|
@@ -3794,18 +6508,18 @@
|
|
|
3794
6508
|
border-radius: 50%;
|
|
3795
6509
|
}
|
|
3796
6510
|
|
|
3797
|
-
.
|
|
6511
|
+
.header-actions {
|
|
3798
6512
|
display: flex;
|
|
3799
6513
|
align-items: center;
|
|
3800
6514
|
gap: 4px;
|
|
3801
6515
|
}
|
|
3802
6516
|
|
|
3803
|
-
.
|
|
3804
|
-
width:
|
|
3805
|
-
height:
|
|
6517
|
+
.header-action {
|
|
6518
|
+
width: 32px;
|
|
6519
|
+
height: 32px;
|
|
3806
6520
|
background: transparent;
|
|
3807
6521
|
border: none;
|
|
3808
|
-
color:
|
|
6522
|
+
color: rgb(0 0 0 / 70%);
|
|
3809
6523
|
border-radius: 8px;
|
|
3810
6524
|
cursor: pointer;
|
|
3811
6525
|
display: flex;
|
|
@@ -3814,18 +6528,18 @@
|
|
|
3814
6528
|
transition: all 0.15s;
|
|
3815
6529
|
}
|
|
3816
6530
|
|
|
3817
|
-
.
|
|
3818
|
-
background: rgba(
|
|
3819
|
-
color:
|
|
6531
|
+
.header-action:hover {
|
|
6532
|
+
background: rgba(0, 0, 0, 0.05);
|
|
6533
|
+
color: #000000;
|
|
3820
6534
|
}
|
|
3821
6535
|
|
|
3822
|
-
.
|
|
3823
|
-
width:
|
|
3824
|
-
height:
|
|
6536
|
+
.header-action-icon {
|
|
6537
|
+
width: 20px;
|
|
6538
|
+
height: 20px;
|
|
3825
6539
|
}
|
|
3826
6540
|
|
|
3827
6541
|
/* Messages */
|
|
3828
|
-
.
|
|
6542
|
+
.messages {
|
|
3829
6543
|
flex: 1;
|
|
3830
6544
|
overflow-y: auto;
|
|
3831
6545
|
padding: 32px;
|
|
@@ -3836,20 +6550,20 @@
|
|
|
3836
6550
|
background: white;
|
|
3837
6551
|
}
|
|
3838
6552
|
|
|
3839
|
-
.
|
|
6553
|
+
.messages::-webkit-scrollbar {
|
|
3840
6554
|
width: 4px;
|
|
3841
6555
|
}
|
|
3842
6556
|
|
|
3843
|
-
.
|
|
6557
|
+
.messages::-webkit-scrollbar-track {
|
|
3844
6558
|
background: transparent;
|
|
3845
6559
|
}
|
|
3846
6560
|
|
|
3847
|
-
.
|
|
6561
|
+
.messages::-webkit-scrollbar-thumb {
|
|
3848
6562
|
background: rgba(0, 0, 0, 0.08);
|
|
3849
6563
|
border-radius: 2px;
|
|
3850
6564
|
}
|
|
3851
6565
|
|
|
3852
|
-
.
|
|
6566
|
+
.empty {
|
|
3853
6567
|
flex: 1;
|
|
3854
6568
|
display: flex;
|
|
3855
6569
|
flex-direction: column;
|
|
@@ -3859,17 +6573,12 @@
|
|
|
3859
6573
|
gap: 8px;
|
|
3860
6574
|
}
|
|
3861
6575
|
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
opacity: 0.4;
|
|
3866
|
-
}
|
|
3867
|
-
|
|
3868
|
-
.chatbot__empty-text {
|
|
3869
|
-
font-size: 13px;
|
|
6576
|
+
.empty-text {
|
|
6577
|
+
font-size: 14px;
|
|
6578
|
+
color: rgb(0 0 0 / 80%);
|
|
3870
6579
|
}
|
|
3871
6580
|
|
|
3872
|
-
.
|
|
6581
|
+
.message {
|
|
3873
6582
|
display: flex;
|
|
3874
6583
|
flex-direction: column;
|
|
3875
6584
|
max-width: 100%;
|
|
@@ -3887,40 +6596,41 @@
|
|
|
3887
6596
|
}
|
|
3888
6597
|
}
|
|
3889
6598
|
|
|
3890
|
-
.
|
|
6599
|
+
.message--user {
|
|
3891
6600
|
align-self: flex-end;
|
|
3892
6601
|
align-items: flex-end;
|
|
3893
6602
|
max-width: 85%;
|
|
3894
6603
|
}
|
|
3895
6604
|
|
|
3896
|
-
.
|
|
6605
|
+
.message--assistant {
|
|
3897
6606
|
align-self: flex-start;
|
|
6607
|
+
gap: 24px;
|
|
3898
6608
|
}
|
|
3899
6609
|
|
|
3900
|
-
.
|
|
6610
|
+
.message-content {
|
|
3901
6611
|
line-height: 1.7;
|
|
3902
6612
|
word-wrap: break-word;
|
|
3903
6613
|
overflow-wrap: break-word;
|
|
3904
6614
|
}
|
|
3905
6615
|
|
|
3906
|
-
.
|
|
6616
|
+
.message--user .message-content {
|
|
3907
6617
|
display: inline-block;
|
|
3908
6618
|
background: var(--kr-chatbot-user-bg);
|
|
3909
|
-
color:
|
|
6619
|
+
color: #000000;
|
|
3910
6620
|
padding: 10px 16px;
|
|
3911
6621
|
border-radius: 20px;
|
|
3912
6622
|
border-bottom-right-radius: 6px;
|
|
3913
6623
|
font-size: 14px;
|
|
3914
6624
|
}
|
|
3915
6625
|
|
|
3916
|
-
.
|
|
6626
|
+
.message--assistant .message-content {
|
|
3917
6627
|
background: transparent;
|
|
3918
|
-
color:
|
|
6628
|
+
color: #000000;
|
|
3919
6629
|
padding: 0;
|
|
3920
6630
|
font-size: 14px;
|
|
3921
6631
|
}
|
|
3922
6632
|
|
|
3923
|
-
.
|
|
6633
|
+
.message--assistant .message-content pre {
|
|
3924
6634
|
background: #1e1e1e;
|
|
3925
6635
|
color: #d4d4d4;
|
|
3926
6636
|
padding: 12px;
|
|
@@ -3931,67 +6641,57 @@
|
|
|
3931
6641
|
line-height: 1.4;
|
|
3932
6642
|
}
|
|
3933
6643
|
|
|
3934
|
-
.
|
|
6644
|
+
.message--assistant .message-content code {
|
|
3935
6645
|
font-family: 'SF Mono', 'Fira Code', Monaco, monospace;
|
|
3936
6646
|
font-size: 13px;
|
|
3937
6647
|
}
|
|
3938
6648
|
|
|
3939
|
-
.
|
|
6649
|
+
.message--assistant .message-content :not(pre) > code {
|
|
3940
6650
|
background: rgba(0, 0, 0, 0.06);
|
|
3941
6651
|
padding: 2px 5px;
|
|
3942
6652
|
border-radius: 3px;
|
|
3943
6653
|
}
|
|
3944
6654
|
|
|
3945
|
-
.
|
|
6655
|
+
.message--assistant .message-content strong {
|
|
3946
6656
|
font-weight: 600;
|
|
3947
6657
|
}
|
|
3948
6658
|
|
|
3949
|
-
.
|
|
6659
|
+
.message--assistant .message-content p {
|
|
3950
6660
|
margin: 0 0 8px 0;
|
|
3951
6661
|
}
|
|
3952
6662
|
|
|
3953
|
-
.
|
|
6663
|
+
.message--assistant .message-content p:last-child {
|
|
3954
6664
|
margin-bottom: 0;
|
|
3955
6665
|
}
|
|
3956
6666
|
|
|
3957
6667
|
/* Reasoning */
|
|
3958
|
-
.
|
|
3959
|
-
margin-bottom: 8px;
|
|
6668
|
+
.reasoning {
|
|
3960
6669
|
border-radius: 6px;
|
|
3961
|
-
background:
|
|
6670
|
+
background: rgb(244 244 244 / 80%);
|
|
3962
6671
|
font-size: 12px;
|
|
3963
6672
|
}
|
|
3964
6673
|
|
|
3965
|
-
.
|
|
6674
|
+
.reasoning summary {
|
|
3966
6675
|
cursor: pointer;
|
|
3967
|
-
padding: 6px 10px;
|
|
3968
|
-
color:
|
|
3969
|
-
font-style: italic;
|
|
6676
|
+
padding: 6px 10px 6px 10px;
|
|
6677
|
+
color: black;
|
|
3970
6678
|
user-select: none;
|
|
3971
6679
|
}
|
|
3972
6680
|
|
|
3973
|
-
.
|
|
6681
|
+
.reasoning-content {
|
|
3974
6682
|
padding: 6px 10px 10px;
|
|
3975
|
-
color:
|
|
3976
|
-
line-height: 1.5;
|
|
6683
|
+
color: black;
|
|
3977
6684
|
white-space: pre-wrap;
|
|
3978
6685
|
word-wrap: break-word;
|
|
3979
6686
|
}
|
|
3980
6687
|
|
|
3981
|
-
|
|
3982
|
-
.chatbot__status {
|
|
3983
|
-
font-size: 13px;
|
|
3984
|
-
color: var(--kr-chatbot-text-secondary);
|
|
3985
|
-
font-style: italic;
|
|
3986
|
-
}
|
|
3987
|
-
|
|
3988
|
-
.chatbot__typing {
|
|
6688
|
+
.typing {
|
|
3989
6689
|
display: flex;
|
|
3990
6690
|
gap: 4px;
|
|
3991
6691
|
padding: 4px 0;
|
|
3992
6692
|
}
|
|
3993
6693
|
|
|
3994
|
-
.
|
|
6694
|
+
.typing span {
|
|
3995
6695
|
width: 6px;
|
|
3996
6696
|
height: 6px;
|
|
3997
6697
|
background: var(--kr-chatbot-text-secondary);
|
|
@@ -4000,11 +6700,11 @@
|
|
|
4000
6700
|
opacity: 0.4;
|
|
4001
6701
|
}
|
|
4002
6702
|
|
|
4003
|
-
.
|
|
6703
|
+
.typing span:nth-child(2) {
|
|
4004
6704
|
animation-delay: 0.2s;
|
|
4005
6705
|
}
|
|
4006
6706
|
|
|
4007
|
-
.
|
|
6707
|
+
.typing span:nth-child(3) {
|
|
4008
6708
|
animation-delay: 0.4s;
|
|
4009
6709
|
}
|
|
4010
6710
|
|
|
@@ -4020,7 +6720,7 @@
|
|
|
4020
6720
|
}
|
|
4021
6721
|
|
|
4022
6722
|
/* Error */
|
|
4023
|
-
.
|
|
6723
|
+
.error {
|
|
4024
6724
|
padding: 8px 16px;
|
|
4025
6725
|
background: var(--kr-chatbot-error-bg);
|
|
4026
6726
|
color: var(--kr-chatbot-error-text);
|
|
@@ -4032,7 +6732,7 @@
|
|
|
4032
6732
|
flex-shrink: 0;
|
|
4033
6733
|
}
|
|
4034
6734
|
|
|
4035
|
-
.
|
|
6735
|
+
.error-dismiss {
|
|
4036
6736
|
background: none;
|
|
4037
6737
|
border: none;
|
|
4038
6738
|
color: var(--kr-chatbot-error-text);
|
|
@@ -4043,19 +6743,19 @@
|
|
|
4043
6743
|
flex-shrink: 0;
|
|
4044
6744
|
}
|
|
4045
6745
|
|
|
4046
|
-
.
|
|
6746
|
+
.error-dismiss-icon {
|
|
4047
6747
|
width: 14px;
|
|
4048
6748
|
height: 14px;
|
|
4049
6749
|
}
|
|
4050
6750
|
|
|
4051
6751
|
/* Footer / Input */
|
|
4052
|
-
.
|
|
4053
|
-
padding:
|
|
6752
|
+
.footer {
|
|
6753
|
+
padding: 16px 16px 16px;
|
|
4054
6754
|
flex-shrink: 0;
|
|
4055
6755
|
background: white;
|
|
4056
6756
|
}
|
|
4057
6757
|
|
|
4058
|
-
.
|
|
6758
|
+
.input-wrapper {
|
|
4059
6759
|
display: flex;
|
|
4060
6760
|
align-items: center;
|
|
4061
6761
|
gap: 4px;
|
|
@@ -4066,12 +6766,12 @@
|
|
|
4066
6766
|
transition: border-color 0.2s, box-shadow 0.2s;
|
|
4067
6767
|
}
|
|
4068
6768
|
|
|
4069
|
-
.
|
|
6769
|
+
.input-wrapper:focus-within {
|
|
4070
6770
|
border-color: #b0b0b0;
|
|
4071
6771
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04);
|
|
4072
6772
|
}
|
|
4073
6773
|
|
|
4074
|
-
.
|
|
6774
|
+
.input-plus {
|
|
4075
6775
|
width: 34px;
|
|
4076
6776
|
height: 34px;
|
|
4077
6777
|
border: none;
|
|
@@ -4088,11 +6788,11 @@
|
|
|
4088
6788
|
transition: background 0.15s;
|
|
4089
6789
|
}
|
|
4090
6790
|
|
|
4091
|
-
.
|
|
6791
|
+
.input-plus:hover {
|
|
4092
6792
|
background: #f8f9fc;
|
|
4093
6793
|
}
|
|
4094
6794
|
|
|
4095
|
-
.
|
|
6795
|
+
.input {
|
|
4096
6796
|
flex: 1;
|
|
4097
6797
|
border: none;
|
|
4098
6798
|
outline: none;
|
|
@@ -4104,14 +6804,14 @@
|
|
|
4104
6804
|
min-height: 26px;
|
|
4105
6805
|
max-height: 120px;
|
|
4106
6806
|
background: transparent;
|
|
4107
|
-
color:
|
|
6807
|
+
color: black;
|
|
4108
6808
|
}
|
|
4109
6809
|
|
|
4110
|
-
.
|
|
6810
|
+
.input::placeholder {
|
|
4111
6811
|
color: #b0b0b0;
|
|
4112
6812
|
}
|
|
4113
6813
|
|
|
4114
|
-
.
|
|
6814
|
+
.send {
|
|
4115
6815
|
width: 34px;
|
|
4116
6816
|
height: 34px;
|
|
4117
6817
|
border-radius: 50%;
|
|
@@ -4126,25 +6826,25 @@
|
|
|
4126
6826
|
transition: all 0.15s;
|
|
4127
6827
|
}
|
|
4128
6828
|
|
|
4129
|
-
.
|
|
6829
|
+
.send:hover {
|
|
4130
6830
|
background: var(--kr-chatbot-primary-hover);
|
|
4131
6831
|
}
|
|
4132
6832
|
|
|
4133
|
-
.
|
|
6833
|
+
.send--disabled {
|
|
4134
6834
|
opacity: 0.4;
|
|
4135
6835
|
cursor: default;
|
|
4136
6836
|
}
|
|
4137
6837
|
|
|
4138
|
-
.
|
|
6838
|
+
.send--disabled:hover {
|
|
4139
6839
|
background: var(--kr-chatbot-primary);
|
|
4140
6840
|
}
|
|
4141
6841
|
|
|
4142
|
-
.
|
|
6842
|
+
.send-icon {
|
|
4143
6843
|
width: 16px;
|
|
4144
6844
|
height: 16px;
|
|
4145
6845
|
}
|
|
4146
6846
|
|
|
4147
|
-
.
|
|
6847
|
+
.stop {
|
|
4148
6848
|
width: 34px;
|
|
4149
6849
|
height: 34px;
|
|
4150
6850
|
border-radius: 50%;
|
|
@@ -4159,62 +6859,51 @@
|
|
|
4159
6859
|
transition: all 0.15s;
|
|
4160
6860
|
}
|
|
4161
6861
|
|
|
4162
|
-
.
|
|
6862
|
+
.stop:hover {
|
|
4163
6863
|
background: #b91c1c;
|
|
4164
6864
|
}
|
|
4165
6865
|
|
|
4166
|
-
.
|
|
6866
|
+
.stop-icon {
|
|
4167
6867
|
width: 14px;
|
|
4168
6868
|
height: 14px;
|
|
4169
6869
|
}
|
|
4170
6870
|
|
|
4171
6871
|
/* Header drag cursor */
|
|
4172
|
-
.
|
|
6872
|
+
.header {
|
|
4173
6873
|
cursor: grab;
|
|
4174
6874
|
user-select: none;
|
|
4175
6875
|
}
|
|
4176
6876
|
|
|
4177
|
-
.
|
|
6877
|
+
.header:active {
|
|
4178
6878
|
cursor: grabbing;
|
|
4179
6879
|
}
|
|
4180
6880
|
|
|
4181
6881
|
/* Drag / Resize states */
|
|
4182
|
-
.
|
|
4183
|
-
.
|
|
6882
|
+
.panel--dragging,
|
|
6883
|
+
.panel--resizing {
|
|
4184
6884
|
transition: none;
|
|
4185
6885
|
user-select: none;
|
|
4186
6886
|
}
|
|
4187
6887
|
|
|
4188
|
-
.
|
|
6888
|
+
.panel--dragging {
|
|
4189
6889
|
box-shadow: 0 32px 96px rgba(0, 0, 0, 0.18), 0 12px 40px rgba(0, 0, 0, 0.1);
|
|
4190
6890
|
}
|
|
4191
6891
|
|
|
4192
6892
|
/* Resize handles */
|
|
4193
|
-
.
|
|
6893
|
+
.resize {
|
|
4194
6894
|
position: absolute;
|
|
4195
6895
|
z-index: 10;
|
|
4196
6896
|
}
|
|
4197
6897
|
|
|
4198
|
-
.
|
|
4199
|
-
.
|
|
4200
|
-
.
|
|
4201
|
-
.
|
|
4202
|
-
.
|
|
4203
|
-
.
|
|
4204
|
-
.
|
|
4205
|
-
.
|
|
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; }
|
|
4206
6906
|
|
|
4207
|
-
.chatbot__resize--se::after {
|
|
4208
|
-
content: '';
|
|
4209
|
-
position: absolute;
|
|
4210
|
-
bottom: 7px;
|
|
4211
|
-
right: 7px;
|
|
4212
|
-
width: 10px;
|
|
4213
|
-
height: 10px;
|
|
4214
|
-
border-right: 2px solid rgba(0, 0, 0, 0.12);
|
|
4215
|
-
border-bottom: 2px solid rgba(0, 0, 0, 0.12);
|
|
4216
|
-
border-radius: 0 0 3px 0;
|
|
4217
|
-
}
|
|
4218
6907
|
`;
|
|
4219
6908
|
__decorate([
|
|
4220
6909
|
n({ attribute: false })
|
|
@@ -4222,6 +6911,9 @@
|
|
|
4222
6911
|
__decorate([
|
|
4223
6912
|
n({ attribute: false })
|
|
4224
6913
|
], exports.Chatbot.prototype, "clear", void 0);
|
|
6914
|
+
__decorate([
|
|
6915
|
+
n({ attribute: false })
|
|
6916
|
+
], exports.Chatbot.prototype, "load", void 0);
|
|
4225
6917
|
__decorate([
|
|
4226
6918
|
n({ attribute: false })
|
|
4227
6919
|
], exports.Chatbot.prototype, "messages", void 0);
|
|
@@ -4235,7 +6927,10 @@
|
|
|
4235
6927
|
n({ type: String })
|
|
4236
6928
|
], exports.Chatbot.prototype, "placeholder", void 0);
|
|
4237
6929
|
__decorate([
|
|
4238
|
-
n({
|
|
6930
|
+
n({
|
|
6931
|
+
type: Boolean,
|
|
6932
|
+
reflect: true
|
|
6933
|
+
})
|
|
4239
6934
|
], exports.Chatbot.prototype, "opened", void 0);
|
|
4240
6935
|
__decorate([
|
|
4241
6936
|
r()
|
|
@@ -4250,13 +6945,13 @@
|
|
|
4250
6945
|
r()
|
|
4251
6946
|
], exports.Chatbot.prototype, "_error", void 0);
|
|
4252
6947
|
__decorate([
|
|
4253
|
-
e$3('.
|
|
6948
|
+
e$3('.panel')
|
|
4254
6949
|
], exports.Chatbot.prototype, "_panelEl", void 0);
|
|
4255
6950
|
__decorate([
|
|
4256
|
-
e$3('.
|
|
6951
|
+
e$3('.messages')
|
|
4257
6952
|
], exports.Chatbot.prototype, "_messagesEl", void 0);
|
|
4258
6953
|
__decorate([
|
|
4259
|
-
e$3('.
|
|
6954
|
+
e$3('.input')
|
|
4260
6955
|
], exports.Chatbot.prototype, "_inputEl", void 0);
|
|
4261
6956
|
exports.Chatbot = __decorate([
|
|
4262
6957
|
t$1('kr-chatbot')
|