@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.
@@ -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
- const path = item.url.startsWith('/') ? item.url.slice(1) : item.url;
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
- const base = baseHref.endsWith('/') ? baseHref : (baseHref + '/');
495
- return base + path;
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: childOverride?.parentId !== undefined ? childOverride.parentId : item.id,
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
- ], { duration: 300, easing: 'ease-in-out' }).onfinish = () => {
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
- ], { duration: 300, easing: 'ease-in-out' });
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
- const url = this.pref.uuid
784
- ? `/api/system/preference/json/scaffold/${this.pref.uuid}?global=true`
785
- : `/api/system/preference/json/scaffold?global=true`;
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: this.pref.uuid ? 'PUT' : 'POST',
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
- { id: 'edit', label: 'Edit Item' },
825
- { id: 'divider-1', label: '', divider: true },
826
- { id: 'add-above', label: 'Add Item Above' },
827
- { id: 'add-below', label: 'Add Item Below' },
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
- const newOrder = position === 'above' ? targetIndex : targetIndex + 1;
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
- position = y < rect.height / 2 ? 'above' : 'below';
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
- const newParentId = position === 'center' ? item.id : item.parentId;
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 d="M6 9l6 6 6-6" stroke-linecap="round" stroke-linejoin="round" />
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 menu .breadcrumbs=${this.breadcrumbs} @menu-click=${this.handleMenuClick}>
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 class="nav-logo" src=${this.logo} alt="Logo" />`
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 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
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 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
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 0.75rem 8px;
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 1rem;
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.01em;
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.05em;
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 1rem;
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
- * .navItems=${[
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[]} navItems - Navigation items as JSON array
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.navItems = [];
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 (.navItems) have been applied
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.navItems.find((item) => {
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.navItems.map((item) => b `
2467
+ ${this.items.map((item) => b `
2348
2468
  <a
2349
- class="nav-item ${item.id === this.activeId ? 'nav-item--active' : ''}"
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 0.75rem;
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.01em;
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, "navItems", void 0);
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 1.5rem;
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: 1.5rem;
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', { bubbles: true, composed: true }));
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
- const path = crumb.url.startsWith('/') ? crumb.url.slice(1) : crumb.url;
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
- const base = baseHref.endsWith('/') ? baseHref : (baseHref + '/');
2623
- return base + path;
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 class="menu__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2656
- <line x1="3" y1="6" x2="21" y2="6"></line>
2657
- <line x1="3" y1="12" x2="21" y2="12"></line>
2658
- <line x1="3" y1="18" x2="21" y2="18"></line>
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({ 'breadcrumb': true, 'breadcrumb--current': isCurrent })}
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 [{ route, params: {} }];
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 = [{ route, params }];
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
- var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
2945
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2946
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2947
- 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;
2948
- return c > 3 && r && Object.defineProperty(target, key, r), r;
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
+ '&': '&amp;',
3148
+ '<': '&lt;',
3149
+ '>': '&gt;',
3150
+ '"': '&quot;',
3151
+ "'": '&#39;'
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
- * AI chatbot component with SSE streaming and native browser action execution.
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
- * The final event should include `finished: true` to signal stream completion.
2967
- *
2968
- * ## Usage
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
- exports.Chatbot = class KRChatbot extends i$1 {
2989
- constructor() {
2990
- super(...arguments);
2991
- // --- Private properties ---
2992
- this._reader = null;
2993
- this._decoder = new TextDecoder();
2994
- this._buffer = '';
2995
- this._userHasScrolledUp = false;
2996
- this._isDragging = false;
2997
- this._isResizing = false;
2998
- this._resizeDir = '';
2999
- this._dragStartX = 0;
3000
- this._dragStartY = 0;
3001
- this._dragStartLeft = 0;
3002
- this._dragStartTop = 0;
3003
- this._resizeStartW = 0;
3004
- this._resizeStartH = 0;
3005
- // --- @property (public API) ---
3006
- /**
3007
- * Callback function for sending messages. Receives the user's message and
3008
- * current conversation history. Must return a Response with a streaming SSE body.
3009
- */
3010
- this.send = null;
3011
- /**
3012
- * Callback function called when the user clears the conversation.
3013
- * Must return a Promise — messages are only cleared if it resolves.
3014
- */
3015
- this.clear = null;
3016
- /**
3017
- * Conversation messages. Can be set externally for initial messages.
3018
- * Updated internally as the conversation progresses.
3019
- */
3020
- this.messages = [];
3021
- /**
3022
- * Header title text.
3023
- */
3024
- this.title = 'AI Assistant';
3025
- /**
3026
- * Header subtitle text (shown below title with a green status dot).
3027
- */
3028
- this.subtitle = '';
3029
- /**
3030
- * Input placeholder text.
3031
- */
3032
- this.placeholder = 'Type a message...';
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(-h + 48, Math.min(newTop, window.innerHeight - 48));
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
- const proposedW = this._resizeStartW - dx;
3069
- if (proposedW >= minW && proposedW <= maxW) {
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
- const proposedH = this._resizeStartH - dy;
3079
- if (proposedH >= minH && proposedH <= maxH) {
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('chatbot__panel--dragging');
5681
+ this._panelEl.classList.remove('panel--dragging');
3096
5682
  }
3097
5683
  if (this._isResizing) {
3098
5684
  this._isResizing = false;
3099
- this._panelEl.classList.remove('chatbot__panel--resizing');
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('chatbot__panel--dragging');
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('chatbot__panel--resizing');
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._panelEl.style.left = '';
3194
- this._panelEl.style.top = '';
3195
- this._panelEl.style.right = '';
3196
- this._panelEl.style.bottom = '';
3197
- this._panelEl.style.width = '';
3198
- this._panelEl.style.height = '';
3199
- this._panelEl.style.position = '';
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, '&amp;')
3462
- .replace(/</g, '&lt;')
3463
- .replace(/>/g, '&gt;')
3464
- .replace(/"/g, '&quot;')
3465
- .replace(/'/g, '&#039;');
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
- 'chatbot__toggle': true,
3496
- 'chatbot__toggle--hidden': this.opened,
6128
+ 'toggle': true,
6129
+ 'toggle--hidden': this.opened,
3497
6130
  })}
3498
6131
  @click=${this._handleToggle}
3499
6132
  title="Open chat"
3500
6133
  >
3501
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" class="chatbot__toggle-icon">
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
- 'chatbot__panel': true,
3509
- 'chatbot__panel--opened': this.opened,
6148
+ 'panel': true,
6149
+ 'panel--opened': this.opened,
3510
6150
  })}>
3511
- <div class="chatbot__resize chatbot__resize--n" data-dir="n" @mousedown=${this._handleResizeMouseDown}></div>
3512
- <div class="chatbot__resize chatbot__resize--s" data-dir="s" @mousedown=${this._handleResizeMouseDown}></div>
3513
- <div class="chatbot__resize chatbot__resize--w" data-dir="w" @mousedown=${this._handleResizeMouseDown}></div>
3514
- <div class="chatbot__resize chatbot__resize--e" data-dir="e" @mousedown=${this._handleResizeMouseDown}></div>
3515
- <div class="chatbot__resize chatbot__resize--nw" data-dir="nw" @mousedown=${this._handleResizeMouseDown}></div>
3516
- <div class="chatbot__resize chatbot__resize--ne" data-dir="ne" @mousedown=${this._handleResizeMouseDown}></div>
3517
- <div class="chatbot__resize chatbot__resize--sw" data-dir="sw" @mousedown=${this._handleResizeMouseDown}></div>
3518
- <div class="chatbot__resize chatbot__resize--se" data-dir="se" @mousedown=${this._handleResizeMouseDown}></div>
3519
- <div class="chatbot__header" @mousedown=${this._handleHeaderMouseDown}>
3520
- <div class="chatbot__header-left">
3521
- <div class="chatbot__header-drag-hint">
3522
- <span></span>
3523
- <span></span>
3524
- <span></span>
3525
- </div>
3526
- <div class="chatbot__header-info">
3527
- <span class="chatbot__header-title">${this.title}</span>
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="chatbot__header-subtitle">${this.subtitle}</span>
6196
+ <span class="header-subtitle">${this.subtitle}</span>
3530
6197
  ` : A}
3531
6198
  </div>
3532
6199
  </div>
3533
- <div class="chatbot__header-actions">
6200
+ <div class="header-actions">
3534
6201
  ${this.messages.length > 0 ? b `
3535
- <button class="chatbot__header-action" @click=${this._handleClear} title="Clear conversation">
3536
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="chatbot__header-action-icon">
3537
- <path stroke-linecap="round" stroke-linejoin="round" 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" />
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 class="chatbot__header-action" @click=${this._handleToggle} title="Close">
3542
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="chatbot__header-action-icon">
3543
- <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
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="chatbot__messages" @scroll=${this._handleMessagesScroll}>
6246
+ <div class="messages" @scroll=${this._handleMessagesScroll}>
3550
6247
  ${this.messages.length === 0 ? b `
3551
- <div class="chatbot__empty">
3552
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" class="chatbot__empty-icon">
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
- 'chatbot__message': true,
3561
- 'chatbot__message--user': msg.role === 'user',
3562
- 'chatbot__message--assistant': msg.role === 'assistant',
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="chatbot__reasoning">
6259
+ <details class="reasoning">
3566
6260
  <summary>Thinking</summary>
3567
- <div class="chatbot__reasoning-content">${msg.reasoning}</div>
6261
+ <div class="reasoning-content">${msg.reasoning}</div>
3568
6262
  </details>
3569
6263
  ` : A}
3570
- <div class="chatbot__message-content">
3571
- ${msg.role === 'user'
6264
+ ${msg.content ? b `
6265
+ <div class="message-content">
6266
+ ${msg.role === 'user'
3572
6267
  ? msg.content
3573
- : o(this._formatAssistantContent(msg.content))}
3574
- </div>
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="chatbot__error">
6283
+ <div class="error">
3590
6284
  <span>${this._error}</span>
3591
- <button class="chatbot__error-dismiss" @click=${this._handleErrorDismiss}>
3592
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="chatbot__error-dismiss-icon">
3593
- <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
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="chatbot__footer">
3600
- <div class="chatbot__input-wrapper">
3601
- <button class="chatbot__input-plus" title="Attach">+</button>
6304
+ <div class="footer">
6305
+ <div class="input-wrapper">
6306
+ <button class="input-plus" title="Attach">+</button>
3602
6307
  <textarea
3603
- class="chatbot__input"
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 class="chatbot__stop" @click=${this._handleStop} title="Stop generating">
3612
- <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" class="chatbot__stop-icon">
3613
- <rect x="6" y="6" width="12" height="12" rx="2" />
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
- 'chatbot__send': true,
3620
- 'chatbot__send--disabled': !this._inputValue.trim(),
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 width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="chatbot__send-icon">
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
- .chatbot__toggle {
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
- .chatbot__toggle:hover {
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
- .chatbot__toggle--hidden {
6421
+ .toggle--hidden {
3694
6422
  transform: scale(0);
3695
6423
  opacity: 0;
3696
6424
  pointer-events: none;
3697
6425
  }
3698
6426
 
3699
- .chatbot__toggle-icon {
6427
+ .toggle-icon {
3700
6428
  width: 26px;
3701
6429
  height: 26px;
3702
6430
  }
3703
6431
 
3704
6432
  /* Panel */
3705
- .chatbot__panel {
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
- .chatbot__panel--opened {
6451
+ .panel--opened {
3724
6452
  display: flex;
3725
6453
  }
3726
6454
 
@@ -3736,49 +6464,35 @@
3736
6464
  }
3737
6465
 
3738
6466
  /* Header */
3739
- .chatbot__header {
3740
- padding: 10px 16px;
3741
- background: var(--kr-chatbot-primary);
3742
- color: white;
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
- .chatbot__header-left {
6479
+ .header-left {
3750
6480
  display: flex;
3751
6481
  align-items: center;
3752
6482
  gap: 10px;
3753
6483
  }
3754
6484
 
3755
- .chatbot__header-drag-hint {
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
- .chatbot__header-title {
3776
- font-size: 13px;
6490
+ .header-title {
6491
+ font-size: 16px;
3777
6492
  font-weight: 600;
3778
- letter-spacing: -0.2px;
3779
6493
  }
3780
6494
 
3781
- .chatbot__header-subtitle {
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
- .chatbot__header-subtitle::before {
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
- .chatbot__header-actions {
6511
+ .header-actions {
3798
6512
  display: flex;
3799
6513
  align-items: center;
3800
6514
  gap: 4px;
3801
6515
  }
3802
6516
 
3803
- .chatbot__header-action {
3804
- width: 28px;
3805
- height: 28px;
6517
+ .header-action {
6518
+ width: 32px;
6519
+ height: 32px;
3806
6520
  background: transparent;
3807
6521
  border: none;
3808
- color: rgba(255, 255, 255, 0.6);
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
- .chatbot__header-action:hover {
3818
- background: rgba(255, 255, 255, 0.1);
3819
- color: white;
6531
+ .header-action:hover {
6532
+ background: rgba(0, 0, 0, 0.05);
6533
+ color: #000000;
3820
6534
  }
3821
6535
 
3822
- .chatbot__header-action-icon {
3823
- width: 16px;
3824
- height: 16px;
6536
+ .header-action-icon {
6537
+ width: 20px;
6538
+ height: 20px;
3825
6539
  }
3826
6540
 
3827
6541
  /* Messages */
3828
- .chatbot__messages {
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
- .chatbot__messages::-webkit-scrollbar {
6553
+ .messages::-webkit-scrollbar {
3840
6554
  width: 4px;
3841
6555
  }
3842
6556
 
3843
- .chatbot__messages::-webkit-scrollbar-track {
6557
+ .messages::-webkit-scrollbar-track {
3844
6558
  background: transparent;
3845
6559
  }
3846
6560
 
3847
- .chatbot__messages::-webkit-scrollbar-thumb {
6561
+ .messages::-webkit-scrollbar-thumb {
3848
6562
  background: rgba(0, 0, 0, 0.08);
3849
6563
  border-radius: 2px;
3850
6564
  }
3851
6565
 
3852
- .chatbot__empty {
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
- .chatbot__empty-icon {
3863
- width: 40px;
3864
- height: 40px;
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
- .chatbot__message {
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
- .chatbot__message--user {
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
- .chatbot__message--assistant {
6605
+ .message--assistant {
3897
6606
  align-self: flex-start;
6607
+ gap: 24px;
3898
6608
  }
3899
6609
 
3900
- .chatbot__message-content {
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
- .chatbot__message--user .chatbot__message-content {
6616
+ .message--user .message-content {
3907
6617
  display: inline-block;
3908
6618
  background: var(--kr-chatbot-user-bg);
3909
- color: var(--kr-chatbot-user-text);
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
- .chatbot__message--assistant .chatbot__message-content {
6626
+ .message--assistant .message-content {
3917
6627
  background: transparent;
3918
- color: var(--kr-chatbot-text);
6628
+ color: #000000;
3919
6629
  padding: 0;
3920
6630
  font-size: 14px;
3921
6631
  }
3922
6632
 
3923
- .chatbot__message--assistant .chatbot__message-content pre {
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
- .chatbot__message--assistant .chatbot__message-content code {
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
- .chatbot__message--assistant .chatbot__message-content :not(pre) > code {
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
- .chatbot__message--assistant .chatbot__message-content strong {
6655
+ .message--assistant .message-content strong {
3946
6656
  font-weight: 600;
3947
6657
  }
3948
6658
 
3949
- .chatbot__message--assistant .chatbot__message-content p {
6659
+ .message--assistant .message-content p {
3950
6660
  margin: 0 0 8px 0;
3951
6661
  }
3952
6662
 
3953
- .chatbot__message--assistant .chatbot__message-content p:last-child {
6663
+ .message--assistant .message-content p:last-child {
3954
6664
  margin-bottom: 0;
3955
6665
  }
3956
6666
 
3957
6667
  /* Reasoning */
3958
- .chatbot__reasoning {
3959
- margin-bottom: 8px;
6668
+ .reasoning {
3960
6669
  border-radius: 6px;
3961
- background: rgba(0, 0, 0, 0.03);
6670
+ background: rgb(244 244 244 / 80%);
3962
6671
  font-size: 12px;
3963
6672
  }
3964
6673
 
3965
- .chatbot__reasoning summary {
6674
+ .reasoning summary {
3966
6675
  cursor: pointer;
3967
- padding: 6px 10px;
3968
- color: var(--kr-chatbot-text-secondary);
3969
- font-style: italic;
6676
+ padding: 6px 10px 6px 10px;
6677
+ color: black;
3970
6678
  user-select: none;
3971
6679
  }
3972
6680
 
3973
- .chatbot__reasoning-content {
6681
+ .reasoning-content {
3974
6682
  padding: 6px 10px 10px;
3975
- color: var(--kr-chatbot-text-secondary);
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
- /* Status */
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
- .chatbot__typing span {
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
- .chatbot__typing span:nth-child(2) {
6703
+ .typing span:nth-child(2) {
4004
6704
  animation-delay: 0.2s;
4005
6705
  }
4006
6706
 
4007
- .chatbot__typing span:nth-child(3) {
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
- .chatbot__error {
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
- .chatbot__error-dismiss {
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
- .chatbot__error-dismiss-icon {
6746
+ .error-dismiss-icon {
4047
6747
  width: 14px;
4048
6748
  height: 14px;
4049
6749
  }
4050
6750
 
4051
6751
  /* Footer / Input */
4052
- .chatbot__footer {
4053
- padding: 12px 16px 16px;
6752
+ .footer {
6753
+ padding: 16px 16px 16px;
4054
6754
  flex-shrink: 0;
4055
6755
  background: white;
4056
6756
  }
4057
6757
 
4058
- .chatbot__input-wrapper {
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
- .chatbot__input-wrapper:focus-within {
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
- .chatbot__input-plus {
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
- .chatbot__input-plus:hover {
6791
+ .input-plus:hover {
4092
6792
  background: #f8f9fc;
4093
6793
  }
4094
6794
 
4095
- .chatbot__input {
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: var(--kr-chatbot-text);
6807
+ color: black;
4108
6808
  }
4109
6809
 
4110
- .chatbot__input::placeholder {
6810
+ .input::placeholder {
4111
6811
  color: #b0b0b0;
4112
6812
  }
4113
6813
 
4114
- .chatbot__send {
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
- .chatbot__send:hover {
6829
+ .send:hover {
4130
6830
  background: var(--kr-chatbot-primary-hover);
4131
6831
  }
4132
6832
 
4133
- .chatbot__send--disabled {
6833
+ .send--disabled {
4134
6834
  opacity: 0.4;
4135
6835
  cursor: default;
4136
6836
  }
4137
6837
 
4138
- .chatbot__send--disabled:hover {
6838
+ .send--disabled:hover {
4139
6839
  background: var(--kr-chatbot-primary);
4140
6840
  }
4141
6841
 
4142
- .chatbot__send-icon {
6842
+ .send-icon {
4143
6843
  width: 16px;
4144
6844
  height: 16px;
4145
6845
  }
4146
6846
 
4147
- .chatbot__stop {
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
- .chatbot__stop:hover {
6862
+ .stop:hover {
4163
6863
  background: #b91c1c;
4164
6864
  }
4165
6865
 
4166
- .chatbot__stop-icon {
6866
+ .stop-icon {
4167
6867
  width: 14px;
4168
6868
  height: 14px;
4169
6869
  }
4170
6870
 
4171
6871
  /* Header drag cursor */
4172
- .chatbot__header {
6872
+ .header {
4173
6873
  cursor: grab;
4174
6874
  user-select: none;
4175
6875
  }
4176
6876
 
4177
- .chatbot__header:active {
6877
+ .header:active {
4178
6878
  cursor: grabbing;
4179
6879
  }
4180
6880
 
4181
6881
  /* Drag / Resize states */
4182
- .chatbot__panel--dragging,
4183
- .chatbot__panel--resizing {
6882
+ .panel--dragging,
6883
+ .panel--resizing {
4184
6884
  transition: none;
4185
6885
  user-select: none;
4186
6886
  }
4187
6887
 
4188
- .chatbot__panel--dragging {
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
- .chatbot__resize {
6893
+ .resize {
4194
6894
  position: absolute;
4195
6895
  z-index: 10;
4196
6896
  }
4197
6897
 
4198
- .chatbot__resize--n { top: -4px; left: 12px; right: 12px; height: 8px; cursor: n-resize; }
4199
- .chatbot__resize--s { bottom: -4px; left: 12px; right: 12px; height: 8px; cursor: s-resize; }
4200
- .chatbot__resize--w { left: -4px; top: 12px; bottom: 12px; width: 8px; cursor: w-resize; }
4201
- .chatbot__resize--e { right: -4px; top: 12px; bottom: 12px; width: 8px; cursor: e-resize; }
4202
- .chatbot__resize--nw { top: -4px; left: -4px; width: 16px; height: 16px; cursor: nw-resize; }
4203
- .chatbot__resize--ne { top: -4px; right: -4px; width: 16px; height: 16px; cursor: ne-resize; }
4204
- .chatbot__resize--sw { bottom: -4px; left: -4px; width: 16px; height: 16px; cursor: sw-resize; }
4205
- .chatbot__resize--se { bottom: -4px; right: -4px; width: 16px; height: 16px; cursor: se-resize; }
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({ type: Boolean, reflect: true })
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('.chatbot__panel')
6948
+ e$3('.panel')
4254
6949
  ], exports.Chatbot.prototype, "_panelEl", void 0);
4255
6950
  __decorate([
4256
- e$3('.chatbot__messages')
6951
+ e$3('.messages')
4257
6952
  ], exports.Chatbot.prototype, "_messagesEl", void 0);
4258
6953
  __decorate([
4259
- e$3('.chatbot__input')
6954
+ e$3('.input')
4260
6955
  ], exports.Chatbot.prototype, "_inputEl", void 0);
4261
6956
  exports.Chatbot = __decorate([
4262
6957
  t$1('kr-chatbot')