@helpway/react 0.14.1 → 0.15.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helpway/react",
3
- "version": "0.14.1",
3
+ "version": "0.15.1",
4
4
  "description": "Official React SDK for Helpway — drop-in AI chat support widget with headless hooks for custom UIs.",
5
5
  "keywords": [
6
6
  "helpway",
@@ -1451,8 +1451,11 @@
1451
1451
  }
1452
1452
 
1453
1453
  .hw-quick-replies[data-cancelled="true"] .hw-quick-reply-btn {
1454
- border-color: rgba(0, 0, 0, 0.08);
1455
- color: var(--hw-text-muted, #9ca3af);
1454
+ /* Same undefined-token trap as the link preview's url line: --hw-text-muted
1455
+ does not exist, so a cancelled quick reply was painted a fixed grey on a
1456
+ fixed near-black border, on dark glass included. */
1457
+ border-color: var(--hw-border);
1458
+ color: var(--hw-text-3);
1456
1459
  }
1457
1460
 
1458
1461
  .hw-msg--ai .hw-code {
@@ -1466,7 +1469,11 @@
1466
1469
  align-items: center;
1467
1470
  gap: 4px;
1468
1471
  font-size: 11px;
1469
- color: var(--hw-text-3);
1472
+ /* text-2, measured: text-3 at this size is 3.39:1 on the light surface and
1473
+ 2.88:1 on the dark one, both under the 4.5:1 floor. A timestamp is
1474
+ information, not decoration, so it has to clear the floor — the row stays
1475
+ quiet through size and weight instead of through low contrast. */
1476
+ color: var(--hw-text-2);
1470
1477
  margin-top: 2px;
1471
1478
  padding: 0 4px;
1472
1479
  font-variant-numeric: tabular-nums;
@@ -1489,14 +1496,10 @@
1489
1496
  align-items: flex-end;
1490
1497
  }
1491
1498
 
1492
- /* Sender label above bubble */
1493
- .hw-msg-sender {
1494
- font-size: 11px;
1495
- font-weight: 500;
1496
- color: var(--hw-text-3);
1497
- margin-bottom: 3px;
1498
- padding: 0 4px;
1499
- }
1499
+ /* .hw-msg-sender is gone. The label above the bubble repeated on every message
1500
+ of a run and named whoever the header already named; .hw-msg-meta below the
1501
+ bubble now signs a run once, by role. Removed rather than left behind, so the
1502
+ next reader does not find a style for an element nothing renders. */
1500
1503
 
1501
1504
  /* ════════════════════════════════════════════════
1502
1505
  TYPING INDICATOR
@@ -1565,10 +1568,18 @@
1565
1568
 
1566
1569
  /* ── Link preview card ───────────────────── */
1567
1570
 
1571
+ /* A row, not a poster. The card used to lead with a full-width 140px hero image,
1572
+ which for a docs link is an OG banner — a generated picture of the very title
1573
+ printed underneath it. It cost the tallest element in the thread to repeat one
1574
+ line of text, and in a 320px panel that pushed the answer it belonged to off
1575
+ screen. Thumbnail beside the text is the shape a link takes in a conversation
1576
+ (Slack, iMessage, Linear all land here); the picture becomes a hint that a
1577
+ destination exists rather than the destination's billboard. */
1568
1578
  .hw-link-preview {
1569
1579
  display: flex;
1570
- flex-direction: column;
1571
- border: 1px solid rgba(0, 0, 0, 0.08);
1580
+ align-items: stretch;
1581
+ gap: 0;
1582
+ border: 1px solid var(--hw-border, rgba(0, 0, 0, 0.08));
1572
1583
  border-radius: 10px;
1573
1584
  overflow: hidden;
1574
1585
  text-decoration: none;
@@ -1578,20 +1589,31 @@
1578
1589
  }
1579
1590
 
1580
1591
  .hw-link-preview:hover {
1581
- background: rgba(0, 0, 0, 0.02);
1592
+ background: var(--hw-bg-hover, rgba(0, 0, 0, 0.02));
1582
1593
  }
1583
1594
 
1584
1595
  .hw-link-preview-img {
1585
- width: 100%;
1586
- max-height: 140px;
1596
+ width: 64px;
1597
+ /* Square and self-stretching: a fixed height would letterbox a 1200x630 OG
1598
+ image, and `stretch` keeps the thumbnail flush with however tall the text
1599
+ beside it turns out to be. */
1600
+ min-height: 100%;
1601
+ flex: 0 0 64px;
1587
1602
  object-fit: cover;
1603
+ /* The border-radius lives on the card; without this the image's own corner
1604
+ pokes through the rounded edge on Safari. */
1605
+ border-radius: 0;
1588
1606
  }
1589
1607
 
1590
1608
  .hw-link-preview-body {
1591
- padding: 10px 12px;
1609
+ padding: 8px 10px;
1592
1610
  display: flex;
1593
1611
  flex-direction: column;
1594
- gap: 3px;
1612
+ gap: 2px;
1613
+ /* min-width:0 or a long unbroken title refuses to shrink and blows the card
1614
+ past its container. */
1615
+ min-width: 0;
1616
+ justify-content: center;
1595
1617
  }
1596
1618
 
1597
1619
  .hw-link-preview-favicon {
@@ -1601,21 +1623,37 @@
1601
1623
  }
1602
1624
 
1603
1625
  .hw-link-preview-title {
1604
- font-size: 13px;
1626
+ font-size: 12.5px;
1605
1627
  font-weight: 600;
1606
1628
  line-height: 1.3;
1607
1629
  color: var(--hw-text, #1a1a1a);
1630
+ /* Two lines, then ellipsis. Docs titles arrive as "What is Better i18n? |
1631
+ Getting Started | Better i18n" — the site's whole breadcrumb, written for a
1632
+ search result, not for a chat bubble. Unclamped it was three lines of
1633
+ pipe-separated SEO. */
1634
+ display: -webkit-box;
1635
+ -webkit-line-clamp: 2;
1636
+ -webkit-box-orient: vertical;
1637
+ overflow: hidden;
1608
1638
  }
1609
1639
 
1610
1640
  .hw-link-preview-desc {
1611
- font-size: 12px;
1641
+ font-size: 11.5px;
1612
1642
  line-height: 1.4;
1613
- color: var(--hw-text-muted, #6b7280);
1643
+ color: var(--hw-text-2, #6b7280);
1644
+ display: -webkit-box;
1645
+ -webkit-line-clamp: 2;
1646
+ -webkit-box-orient: vertical;
1647
+ overflow: hidden;
1614
1648
  }
1615
1649
 
1616
1650
  .hw-link-preview-url {
1617
1651
  font-size: 11px;
1618
- color: var(--hw-text-muted, #9ca3af);
1652
+ /* --hw-text-muted is referenced in seven rules and defined in none, so every
1653
+ one of them silently falls back to a hardcoded grey and stops following
1654
+ dark mode or the docked glass theme. Fixed here; the other six are listed
1655
+ in the commit. */
1656
+ color: var(--hw-text-2);
1619
1657
  }
1620
1658
 
1621
1659
  .hw-msg-links {
@@ -2848,31 +2886,15 @@
2848
2886
  color: var(--hw-text-1);
2849
2887
  }
2850
2888
 
2889
+ /* Layout only. The buttons themselves are .hw-quick-reply-btn — the same control
2890
+ the message list renders — so this class contributes the card's padding and
2891
+ nothing else. It used to carry a full second copy of the button styling, which
2892
+ is how the two versions ended up disagreeing about ground, ink and hover. */
2851
2893
  .hw-preview-quick-replies {
2852
- display: flex;
2853
- flex-wrap: wrap;
2854
- gap: 6px;
2855
2894
  padding: 0 14px 14px;
2856
- }
2857
-
2858
- .hw-preview-qr-btn {
2859
- display: inline-flex;
2860
- align-items: center;
2861
- padding: 6px 14px;
2862
- font-size: 13px;
2863
- font-weight: 500;
2864
- line-height: 1.3;
2865
- color: var(--hw-brand, #000);
2866
- background: transparent;
2867
- border: 1px solid var(--hw-border, rgba(0, 0, 0, 0.1));
2868
- border-radius: 18px;
2869
- cursor: pointer;
2870
- transition: background 0.15s, border-color 0.15s;
2871
- }
2872
-
2873
- .hw-preview-qr-btn:hover {
2874
- background: var(--hw-hover, rgba(0, 0, 0, 0.04));
2875
- border-color: var(--hw-brand, #000);
2895
+ /* .hw-quick-replies brings display/flex-wrap/gap and a top margin meant for
2896
+ sitting under a bubble; inside the card the padding above is the spacing. */
2897
+ margin-top: 0;
2876
2898
  }
2877
2899
 
2878
2900
  /* ── Preview closing animation ──────────────────────────── */
@@ -3797,26 +3819,88 @@
3797
3819
  DISCONNECT BANNER
3798
3820
  ════════════════════════════════════════════════ */
3799
3821
 
3822
+ /* A socket that is still retrying is not a warning. Amber said "something is
3823
+ wrong, act on it" about a state the visitor cannot act on and that heals
3824
+ itself — and it said it in the loudest slot the panel has, a full-width band
3825
+ above the agent's own name. The dot made it worse: the docked variant already
3826
+ quieted the band to a neutral surface but left the amber dot on it, so the row
3827
+ argued with itself about how alarmed to be.
3828
+
3829
+ This now speaks the vocabulary the panel already uses for "wait a moment" —
3830
+ the typing indicator's neutral dot on the panel's own hover surface. Both
3831
+ colours are tokens, so dark mode follows without a second rule. */
3800
3832
  .hw-disconnect {
3801
3833
  display: flex;
3802
3834
  align-items: center;
3803
3835
  justify-content: center;
3804
- gap: 6px;
3805
- padding: 6px 12px;
3806
- background: #fef3c7;
3807
- color: #92400e;
3808
- font-size: 12px;
3836
+ gap: 7px;
3837
+ padding: 7px 14px;
3838
+ background: var(--hw-bg-hover);
3839
+ /* text-2, not text-3. Measured, because "quieter" is the easy way to make
3840
+ 11.5px unreadable: text-3 on bg-hover is 3.39:1 light and 2.88:1 dark,
3841
+ under the 4.5:1 floor — and under what the amber it replaced managed
3842
+ (6.37:1). text-2 comes in at 5.26:1 and 5.44:1. On docked glass both are
3843
+ translucent over a blurred page, so no static number applies there; the
3844
+ opaque themes are the ones that can be held to a figure. */
3845
+ color: var(--hw-text-2);
3846
+ font-size: 11.5px;
3809
3847
  font-weight: 500;
3848
+ letter-spacing: -0.01em;
3849
+ /* Turkish ships "Yeniden bağlanılıyor…" against English's "Reconnecting…" —
3850
+ 22 characters where the layout was measured at 14. One line, clipped at the
3851
+ edge rather than wrapping the band to double height in a 320px panel. */
3852
+ white-space: nowrap;
3853
+ overflow: hidden;
3854
+ text-overflow: ellipsis;
3810
3855
  flex-shrink: 0;
3811
- animation: hw-msg-appear 0.25s ease-out;
3856
+ animation: hw-disconnect-in 0.18s cubic-bezier(0.22, 1, 0.36, 1);
3812
3857
  }
3813
3858
 
3814
- .hw-disconnect-dot {
3815
- width: 6px;
3816
- height: 6px;
3859
+ /* Slides out of its own height rather than fading in place: the band displaces
3860
+ the panel when it appears, and matching the motion to the displacement is what
3861
+ keeps that from reading as a jump. */
3862
+ @keyframes hw-disconnect-in {
3863
+ from {
3864
+ opacity: 0;
3865
+ transform: translateY(-100%);
3866
+ }
3867
+ to {
3868
+ opacity: 1;
3869
+ transform: translateY(0);
3870
+ }
3871
+ }
3872
+
3873
+ /* An arc, not a pulsing dot. A dot that fades in and out only says "something is
3874
+ happening"; a rotating arc says a retry is in flight and going somewhere,
3875
+ which is what a reconnect is. It also speaks the widget's own loader
3876
+ vocabulary — .hw-composer-preview-loader builds its spinner the same way — so
3877
+ it reads as this product's spinner rather than a new invention.
3878
+
3879
+ currentColor, so it inherits the row's measured ink instead of carrying a
3880
+ second colour decision. The 3/4 arc uses `transparent` rather than color-mix:
3881
+ this bundle runs on whatever browser a customer's visitor brought. */
3882
+ .hw-disconnect-spinner {
3883
+ width: 9px;
3884
+ height: 9px;
3885
+ border: 1.5px solid currentColor;
3886
+ border-top-color: transparent;
3817
3887
  border-radius: 50%;
3818
- background: #f59e0b;
3819
- animation: hw-loading-pulse 1.4s infinite;
3888
+ opacity: 0.75;
3889
+ flex-shrink: 0;
3890
+ animation: hw-spin 0.7s linear infinite;
3891
+ }
3892
+
3893
+ @media (prefers-reduced-motion: reduce) {
3894
+ .hw-disconnect {
3895
+ animation: none;
3896
+ }
3897
+ .hw-disconnect-spinner {
3898
+ animation: none;
3899
+ /* Close the ring. A frozen 3/4 arc reads as a broken glyph, not as a
3900
+ paused one — the gap only makes sense while it is turning. */
3901
+ border-top-color: currentColor;
3902
+ opacity: 0.5;
3903
+ }
3820
3904
  }
3821
3905
 
3822
3906
  /* ════════════════════════════════════════════════
@@ -4335,10 +4419,9 @@
4335
4419
  background: var(--hw-bg-hover);
4336
4420
  }
4337
4421
 
4338
- .hw-disconnect {
4339
- background: #422006;
4340
- color: #fcd34d;
4341
- }
4422
+ /* .hw-disconnect needs no dark override any more — it is built from
4423
+ --hw-bg-hover and --hw-text-3, which this block already redefines. A
4424
+ hard-coded amber pair here was the other half of the same mixed signal. */
4342
4425
 
4343
4426
  .hw-send-error {
4344
4427
  background: #450a0a;
@@ -4936,9 +5019,12 @@
4936
5019
  answer restated them three times in a row, which is how the meta line stopped
4937
5020
  being read at all. A human agent keeps their line: their name is news (someone
4938
5021
  took over) in a way the AI's is not. */
4939
- .hw-widget[data-hw-placement="docked"] .hw-msg--ai .hw-msg-meta {
4940
- display: none;
4941
- }
5022
+ /* The AI's meta row used to be hidden here while the team's stayed visible, so
5023
+ in this placement an AI answer was unsigned and a human answer carried a naked
5024
+ clock — the asymmetry is what made the clock look arbitrary. Now both sign the
5025
+ same way, with role + time, which is the row Fin prints on an AI answer too.
5026
+ (To go back to hiding it, restore `display: none` for .hw-msg--ai .hw-msg-meta
5027
+ — it is one rule, not a rewrite.) */
4942
5028
  .hw-widget[data-hw-placement="docked"] .hw-msg--visitor .hw-msg-bubble {
4943
5029
  background: rgba(255, 255, 255, 0.16);
4944
5030
  color: #ffffff;
@@ -5349,11 +5435,11 @@
5349
5435
  /* Reconnecting. A socket blip is not an error — it resolves itself in a
5350
5436
  second or two — so it sits quietly at the top of the panel instead of
5351
5437
  shouting in amber across the full width. */
5438
+ /* The base rule now carries the padding, surface, ink and size this variant used
5439
+ to restate — the quiet treatment stopped being the docked exception and became
5440
+ how the band looks everywhere. Only the corners are placement-specific: docked
5441
+ is the one case where this band is the panel's actual top edge. */
5352
5442
  .hw-widget[data-hw-placement="docked"] .hw-disconnect {
5353
- padding: 7px 14px;
5354
- background: var(--hw-bg-hover);
5355
- color: var(--hw-text-3, var(--hw-text-muted));
5356
- font-size: 11.5px;
5357
5443
  border-radius: 20px 20px 0 0;
5358
5444
  }
5359
5445