vanilla-agent 1.13.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/widget.css CHANGED
@@ -861,3 +861,80 @@ form:focus-within textarea {
861
861
  color: inherit;
862
862
  text-decoration: underline;
863
863
  }
864
+
865
+ /* Markdown paragraph styles */
866
+ .vanilla-message-bubble p {
867
+ margin: 0;
868
+ }
869
+
870
+ .vanilla-message-bubble p + p {
871
+ margin-top: 0.75rem;
872
+ }
873
+
874
+ /* Markdown list styles */
875
+ .vanilla-message-bubble ul,
876
+ .vanilla-message-bubble ol {
877
+ margin: 0.5rem 0;
878
+ padding-left: 1.5rem;
879
+ }
880
+
881
+ .vanilla-message-bubble ul {
882
+ list-style-type: disc;
883
+ }
884
+
885
+ .vanilla-message-bubble ol {
886
+ list-style-type: decimal;
887
+ }
888
+
889
+ .vanilla-message-bubble li {
890
+ margin: 0.25rem 0;
891
+ padding-left: 0.25rem;
892
+ }
893
+
894
+ /* Nested lists */
895
+ .vanilla-message-bubble ul ul,
896
+ .vanilla-message-bubble ol ul {
897
+ list-style-type: circle;
898
+ margin: 0.25rem 0;
899
+ }
900
+
901
+ .vanilla-message-bubble ul ul ul,
902
+ .vanilla-message-bubble ol ul ul,
903
+ .vanilla-message-bubble ul ol ul,
904
+ .vanilla-message-bubble ol ol ul {
905
+ list-style-type: square;
906
+ }
907
+
908
+ .vanilla-message-bubble ul ol,
909
+ .vanilla-message-bubble ol ol {
910
+ list-style-type: lower-alpha;
911
+ margin: 0.25rem 0;
912
+ }
913
+
914
+ /* Ensure user message paragraphs and lists have proper styling too */
915
+ .vanilla-message-user-bubble p {
916
+ margin: 0;
917
+ }
918
+
919
+ .vanilla-message-user-bubble p + p {
920
+ margin-top: 0.75rem;
921
+ }
922
+
923
+ .vanilla-message-user-bubble ul,
924
+ .vanilla-message-user-bubble ol {
925
+ margin: 0.5rem 0;
926
+ padding-left: 1.5rem;
927
+ }
928
+
929
+ .vanilla-message-user-bubble ul {
930
+ list-style-type: disc;
931
+ }
932
+
933
+ .vanilla-message-user-bubble ol {
934
+ list-style-type: decimal;
935
+ }
936
+
937
+ .vanilla-message-user-bubble li {
938
+ margin: 0.25rem 0;
939
+ padding-left: 0.25rem;
940
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanilla-agent",
3
- "version": "1.13.0",
3
+ "version": "1.15.0",
4
4
  "description": "Themeable, plugable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -46,7 +46,7 @@ export const buildComposer = (context: ComposerBuildContext): ComposerElements =
46
46
 
47
47
  const footer = createElement(
48
48
  "div",
49
- "tvw-border-t-cw-divider tvw-bg-cw-surface tvw-px-6 tvw-py-4"
49
+ "tvw-widget-footer tvw-border-t-cw-divider tvw-bg-cw-surface tvw-px-6 tvw-py-4"
50
50
  );
51
51
 
52
52
  const suggestions = createElement(
@@ -67,7 +67,7 @@ export const buildComposer = (context: ComposerBuildContext): ComposerElements =
67
67
 
68
68
  const composerForm = createElement(
69
69
  "form",
70
- `tvw-flex tvw-items-end ${gapClass} tvw-rounded-2xl tvw-border tvw-border-gray-200 tvw-bg-cw-input-background tvw-px-4 tvw-py-3`
70
+ `tvw-widget-composer tvw-flex tvw-items-end ${gapClass} tvw-rounded-2xl tvw-border tvw-border-gray-200 tvw-bg-cw-input-background tvw-px-4 tvw-py-3`
71
71
  ) as HTMLFormElement;
72
72
  // Prevent form from getting focus styles
73
73
  composerForm.style.outline = "none";
@@ -364,3 +364,4 @@ export const buildComposer = (context: ComposerBuildContext): ComposerElements =
364
364
  };
365
365
  };
366
366
 
367
+
@@ -29,7 +29,7 @@ export const buildHeader = (context: HeaderBuildContext): HeaderElements => {
29
29
 
30
30
  const header = createElement(
31
31
  "div",
32
- "tvw-flex tvw-items-center tvw-gap-3 tvw-bg-cw-surface tvw-px-6 tvw-py-5 tvw-border-b-cw-divider"
32
+ "tvw-widget-header tvw-flex tvw-items-center tvw-gap-3 tvw-bg-cw-surface tvw-px-6 tvw-py-5 tvw-border-b-cw-divider"
33
33
  );
34
34
 
35
35
  const launcher = config?.launcher ?? {};
@@ -452,3 +452,4 @@ export const attachHeaderToContainer = (
452
452
  }
453
453
  };
454
454
 
455
+
@@ -36,12 +36,12 @@ export const createWrapper = (config?: AgentWidgetConfig): PanelWrapper => {
36
36
 
37
37
  const wrapper = createElement(
38
38
  "div",
39
- `tvw-fixed ${position} tvw-z-50 tvw-transition`
39
+ `tvw-widget-wrapper tvw-fixed ${position} tvw-z-50 tvw-transition`
40
40
  );
41
41
 
42
42
  const panel = createElement(
43
43
  "div",
44
- "tvw-relative tvw-min-h-[320px]"
44
+ "tvw-widget-panel tvw-relative tvw-min-h-[320px]"
45
45
  );
46
46
  const launcherWidth = config?.launcher?.width ?? config?.launcherWidth;
47
47
  const width = launcherWidth ?? "min(400px, calc(100vw - 24px))";
@@ -83,7 +83,7 @@ export const buildPanel = (config?: AgentWidgetConfig, showClose = true): PanelE
83
83
  // the body (chat messages area) to scroll while header/footer stay fixed
84
84
  const container = createElement(
85
85
  "div",
86
- "tvw-flex tvw-h-full tvw-w-full tvw-flex-1 tvw-min-h-0 tvw-flex-col tvw-bg-cw-surface tvw-text-cw-primary tvw-rounded-2xl tvw-overflow-hidden tvw-shadow-2xl tvw-border tvw-border-cw-border"
86
+ "tvw-widget-container tvw-flex tvw-h-full tvw-w-full tvw-flex-1 tvw-min-h-0 tvw-flex-col tvw-bg-cw-surface tvw-text-cw-primary tvw-rounded-2xl tvw-overflow-hidden tvw-border tvw-border-cw-border"
87
87
  );
88
88
 
89
89
  // Build header using layout config if available, otherwise use standard builder
@@ -95,7 +95,7 @@ export const buildPanel = (config?: AgentWidgetConfig, showClose = true): PanelE
95
95
  // Build body with intro card and messages wrapper
96
96
  const body = createElement(
97
97
  "div",
98
- "tvw-flex tvw-flex-1 tvw-min-h-0 tvw-flex-col tvw-gap-6 tvw-overflow-y-auto tvw-bg-cw-container tvw-px-6 tvw-py-6"
98
+ "tvw-widget-body tvw-flex tvw-flex-1 tvw-min-h-0 tvw-flex-col tvw-gap-6 tvw-overflow-y-auto tvw-bg-cw-container tvw-px-6 tvw-py-6"
99
99
  );
100
100
  body.id = "vanilla-agent-scroll-container";
101
101
 
@@ -861,3 +861,80 @@ form:focus-within textarea {
861
861
  color: inherit;
862
862
  text-decoration: underline;
863
863
  }
864
+
865
+ /* Markdown paragraph styles */
866
+ .vanilla-message-bubble p {
867
+ margin: 0;
868
+ }
869
+
870
+ .vanilla-message-bubble p + p {
871
+ margin-top: 0.75rem;
872
+ }
873
+
874
+ /* Markdown list styles */
875
+ .vanilla-message-bubble ul,
876
+ .vanilla-message-bubble ol {
877
+ margin: 0.5rem 0;
878
+ padding-left: 1.5rem;
879
+ }
880
+
881
+ .vanilla-message-bubble ul {
882
+ list-style-type: disc;
883
+ }
884
+
885
+ .vanilla-message-bubble ol {
886
+ list-style-type: decimal;
887
+ }
888
+
889
+ .vanilla-message-bubble li {
890
+ margin: 0.25rem 0;
891
+ padding-left: 0.25rem;
892
+ }
893
+
894
+ /* Nested lists */
895
+ .vanilla-message-bubble ul ul,
896
+ .vanilla-message-bubble ol ul {
897
+ list-style-type: circle;
898
+ margin: 0.25rem 0;
899
+ }
900
+
901
+ .vanilla-message-bubble ul ul ul,
902
+ .vanilla-message-bubble ol ul ul,
903
+ .vanilla-message-bubble ul ol ul,
904
+ .vanilla-message-bubble ol ol ul {
905
+ list-style-type: square;
906
+ }
907
+
908
+ .vanilla-message-bubble ul ol,
909
+ .vanilla-message-bubble ol ol {
910
+ list-style-type: lower-alpha;
911
+ margin: 0.25rem 0;
912
+ }
913
+
914
+ /* Ensure user message paragraphs and lists have proper styling too */
915
+ .vanilla-message-user-bubble p {
916
+ margin: 0;
917
+ }
918
+
919
+ .vanilla-message-user-bubble p + p {
920
+ margin-top: 0.75rem;
921
+ }
922
+
923
+ .vanilla-message-user-bubble ul,
924
+ .vanilla-message-user-bubble ol {
925
+ margin: 0.5rem 0;
926
+ padding-left: 1.5rem;
927
+ }
928
+
929
+ .vanilla-message-user-bubble ul {
930
+ list-style-type: disc;
931
+ }
932
+
933
+ .vanilla-message-user-bubble ol {
934
+ list-style-type: decimal;
935
+ }
936
+
937
+ .vanilla-message-user-bubble li {
938
+ margin: 0.25rem 0;
939
+ padding-left: 0.25rem;
940
+ }
package/src/ui.ts CHANGED
@@ -19,6 +19,7 @@ import { createElement } from "./utils/dom";
19
19
  import { statusCopy } from "./utils/constants";
20
20
  import { createLauncherButton } from "./components/launcher";
21
21
  import { createWrapper, buildPanel, buildHeader, buildComposer, attachHeaderToContainer } from "./components/panel";
22
+ import { positionMap } from "./utils/positioning";
22
23
  import type { HeaderElements, ComposerElements } from "./components/panel";
23
24
  import { MessageTransform } from "./components/message-bubble";
24
25
  import { createStandardBubble, createTypingIndicator } from "./components/message-bubble";
@@ -418,9 +419,30 @@ export const createAgentExperience = (
418
419
  const panelShadow = theme.panelShadow ?? defaultPanelShadow;
419
420
  const panelBorderRadius = theme.panelBorderRadius ?? defaultPanelBorderRadius;
420
421
 
421
- // Apply panel styling to container (works in all modes)
422
+ // Reset all inline styles first to handle mode toggling
423
+ // This ensures styles don't persist when switching between modes
424
+ mount.style.cssText = '';
425
+ wrapper.style.cssText = '';
426
+ panel.style.cssText = '';
427
+ container.style.cssText = '';
428
+ body.style.cssText = '';
429
+ footer.style.cssText = '';
430
+
431
+ // Re-apply panel width/maxWidth from initial setup
432
+ const launcherWidth = config?.launcher?.width ?? config?.launcherWidth;
433
+ const width = launcherWidth ?? "min(400px, calc(100vw - 24px))";
434
+ if (!sidebarMode) {
435
+ panel.style.width = width;
436
+ panel.style.maxWidth = width;
437
+ }
438
+
439
+ // Apply panel styling
440
+ // Box-shadow is applied to panel (parent) instead of container to avoid
441
+ // rendering artifacts when container has overflow:hidden + border-radius
442
+ // Panel also gets border-radius to make the shadow follow the rounded corners
443
+ panel.style.boxShadow = panelShadow;
444
+ panel.style.borderRadius = panelBorderRadius;
422
445
  container.style.border = panelBorder;
423
- container.style.boxShadow = panelShadow;
424
446
  container.style.borderRadius = panelBorderRadius;
425
447
 
426
448
  if (fullHeight) {
@@ -430,14 +452,13 @@ export const createAgentExperience = (
430
452
  mount.style.height = '100%';
431
453
  mount.style.minHeight = '0';
432
454
 
433
- // Wrapper
455
+ // Wrapper - no overflow:hidden to allow panel's box-shadow to render fully
434
456
  wrapper.style.display = 'flex';
435
457
  wrapper.style.flexDirection = 'column';
436
458
  wrapper.style.flex = '1 1 0%';
437
459
  wrapper.style.minHeight = '0';
438
460
  wrapper.style.maxHeight = '100%';
439
461
  wrapper.style.height = '100%';
440
- wrapper.style.overflow = 'hidden';
441
462
 
442
463
  // Panel
443
464
  panel.style.display = 'flex';
@@ -465,16 +486,23 @@ export const createAgentExperience = (
465
486
  footer.style.flexShrink = '0';
466
487
  }
467
488
 
489
+ // Handle positioning classes based on mode
490
+ // First remove all position classes to reset state
491
+ wrapper.classList.remove(
492
+ 'tvw-bottom-6', 'tvw-right-6', 'tvw-left-6', 'tvw-top-6',
493
+ 'tvw-bottom-4', 'tvw-right-4', 'tvw-left-4', 'tvw-top-4'
494
+ );
495
+
496
+ if (!sidebarMode) {
497
+ // Restore positioning classes when not in sidebar mode
498
+ const positionClasses = positionMap[position as keyof typeof positionMap] ?? positionMap['bottom-right'];
499
+ positionClasses.split(' ').forEach(cls => wrapper.classList.add(cls));
500
+ }
501
+
468
502
  // Apply sidebar-specific styles
469
503
  if (sidebarMode) {
470
504
  const sidebarWidth = config.launcher?.sidebarWidth ?? '420px';
471
505
 
472
- // Remove Tailwind positioning classes that add spacing (tvw-bottom-6, tvw-right-6, etc.)
473
- wrapper.classList.remove(
474
- 'tvw-bottom-6', 'tvw-right-6', 'tvw-left-6', 'tvw-top-6',
475
- 'tvw-bottom-4', 'tvw-right-4', 'tvw-left-4', 'tvw-top-4'
476
- );
477
-
478
506
  // Wrapper - fixed position, flush with edges
479
507
  wrapper.style.cssText = `
480
508
  position: fixed !important;
@@ -491,6 +519,8 @@ export const createAgentExperience = (
491
519
  `;
492
520
 
493
521
  // Panel - fill wrapper (override inline width/max-width from panel.ts)
522
+ // Box-shadow is on panel to avoid rendering artifacts with container's overflow:hidden
523
+ // Border-radius on panel ensures shadow follows rounded corners
494
524
  panel.style.cssText = `
495
525
  position: relative !important;
496
526
  display: flex !important;
@@ -502,12 +532,15 @@ export const createAgentExperience = (
502
532
  min-height: 0 !important;
503
533
  margin: 0 !important;
504
534
  padding: 0 !important;
535
+ box-shadow: ${panelShadow} !important;
536
+ border-radius: ${panelBorderRadius} !important;
505
537
  `;
506
538
  // Force override any inline width/maxWidth that may be set elsewhere
507
539
  panel.style.setProperty('width', '100%', 'important');
508
540
  panel.style.setProperty('max-width', '100%', 'important');
509
541
 
510
542
  // Container - apply configurable styles with sidebar layout
543
+ // Note: box-shadow is on panel, not container
511
544
  container.style.cssText = `
512
545
  display: flex !important;
513
546
  flex-direction: column !important;
@@ -519,7 +552,6 @@ export const createAgentExperience = (
519
552
  overflow: hidden !important;
520
553
  border-radius: ${panelBorderRadius} !important;
521
554
  border: ${panelBorder} !important;
522
- box-shadow: ${panelShadow} !important;
523
555
  `;
524
556
 
525
557
  // Remove footer border in sidebar mode
@@ -529,6 +561,13 @@ export const createAgentExperience = (
529
561
  padding: 8px 16px 12px 16px !important;
530
562
  `;
531
563
  }
564
+
565
+ // Apply max-height constraints to wrapper to prevent expanding past viewport top
566
+ // Use both -moz-available (Firefox) and stretch (standard) for cross-browser support
567
+ // Append to cssText to allow multiple fallback values for the same property
568
+ const maxHeightStyles = 'max-height: -moz-available !important; max-height: stretch !important;';
569
+ const paddingStyles = sidebarMode ? '' : 'padding-top: 1.25em !important;';
570
+ wrapper.style.cssText += maxHeightStyles + paddingStyles;
532
571
  };
533
572
  applyFullHeightStyles();
534
573
 
@@ -1025,7 +1064,8 @@ export const createAgentExperience = (
1025
1064
  };
1026
1065
 
1027
1066
  const setComposerDisabled = (disabled: boolean) => {
1028
- textarea.disabled = disabled;
1067
+ // Keep textarea always enabled so users can type while streaming
1068
+ // Only disable submit controls to prevent sending during streaming
1029
1069
  sendButton.disabled = disabled;
1030
1070
  if (micButton) {
1031
1071
  micButton.disabled = disabled;