@helpway/react 0.14.0 → 0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helpway/react",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
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 {
@@ -3797,26 +3835,88 @@
3797
3835
  DISCONNECT BANNER
3798
3836
  ════════════════════════════════════════════════ */
3799
3837
 
3838
+ /* A socket that is still retrying is not a warning. Amber said "something is
3839
+ wrong, act on it" about a state the visitor cannot act on and that heals
3840
+ itself — and it said it in the loudest slot the panel has, a full-width band
3841
+ above the agent's own name. The dot made it worse: the docked variant already
3842
+ quieted the band to a neutral surface but left the amber dot on it, so the row
3843
+ argued with itself about how alarmed to be.
3844
+
3845
+ This now speaks the vocabulary the panel already uses for "wait a moment" —
3846
+ the typing indicator's neutral dot on the panel's own hover surface. Both
3847
+ colours are tokens, so dark mode follows without a second rule. */
3800
3848
  .hw-disconnect {
3801
3849
  display: flex;
3802
3850
  align-items: center;
3803
3851
  justify-content: center;
3804
- gap: 6px;
3805
- padding: 6px 12px;
3806
- background: #fef3c7;
3807
- color: #92400e;
3808
- font-size: 12px;
3852
+ gap: 7px;
3853
+ padding: 7px 14px;
3854
+ background: var(--hw-bg-hover);
3855
+ /* text-2, not text-3. Measured, because "quieter" is the easy way to make
3856
+ 11.5px unreadable: text-3 on bg-hover is 3.39:1 light and 2.88:1 dark,
3857
+ under the 4.5:1 floor — and under what the amber it replaced managed
3858
+ (6.37:1). text-2 comes in at 5.26:1 and 5.44:1. On docked glass both are
3859
+ translucent over a blurred page, so no static number applies there; the
3860
+ opaque themes are the ones that can be held to a figure. */
3861
+ color: var(--hw-text-2);
3862
+ font-size: 11.5px;
3809
3863
  font-weight: 500;
3864
+ letter-spacing: -0.01em;
3865
+ /* Turkish ships "Yeniden bağlanılıyor…" against English's "Reconnecting…" —
3866
+ 22 characters where the layout was measured at 14. One line, clipped at the
3867
+ edge rather than wrapping the band to double height in a 320px panel. */
3868
+ white-space: nowrap;
3869
+ overflow: hidden;
3870
+ text-overflow: ellipsis;
3810
3871
  flex-shrink: 0;
3811
- animation: hw-msg-appear 0.25s ease-out;
3872
+ animation: hw-disconnect-in 0.18s cubic-bezier(0.22, 1, 0.36, 1);
3812
3873
  }
3813
3874
 
3814
- .hw-disconnect-dot {
3815
- width: 6px;
3816
- height: 6px;
3875
+ /* Slides out of its own height rather than fading in place: the band displaces
3876
+ the panel when it appears, and matching the motion to the displacement is what
3877
+ keeps that from reading as a jump. */
3878
+ @keyframes hw-disconnect-in {
3879
+ from {
3880
+ opacity: 0;
3881
+ transform: translateY(-100%);
3882
+ }
3883
+ to {
3884
+ opacity: 1;
3885
+ transform: translateY(0);
3886
+ }
3887
+ }
3888
+
3889
+ /* An arc, not a pulsing dot. A dot that fades in and out only says "something is
3890
+ happening"; a rotating arc says a retry is in flight and going somewhere,
3891
+ which is what a reconnect is. It also speaks the widget's own loader
3892
+ vocabulary — .hw-composer-preview-loader builds its spinner the same way — so
3893
+ it reads as this product's spinner rather than a new invention.
3894
+
3895
+ currentColor, so it inherits the row's measured ink instead of carrying a
3896
+ second colour decision. The 3/4 arc uses `transparent` rather than color-mix:
3897
+ this bundle runs on whatever browser a customer's visitor brought. */
3898
+ .hw-disconnect-spinner {
3899
+ width: 9px;
3900
+ height: 9px;
3901
+ border: 1.5px solid currentColor;
3902
+ border-top-color: transparent;
3817
3903
  border-radius: 50%;
3818
- background: #f59e0b;
3819
- animation: hw-loading-pulse 1.4s infinite;
3904
+ opacity: 0.75;
3905
+ flex-shrink: 0;
3906
+ animation: hw-spin 0.7s linear infinite;
3907
+ }
3908
+
3909
+ @media (prefers-reduced-motion: reduce) {
3910
+ .hw-disconnect {
3911
+ animation: none;
3912
+ }
3913
+ .hw-disconnect-spinner {
3914
+ animation: none;
3915
+ /* Close the ring. A frozen 3/4 arc reads as a broken glyph, not as a
3916
+ paused one — the gap only makes sense while it is turning. */
3917
+ border-top-color: currentColor;
3918
+ opacity: 0.5;
3919
+ }
3820
3920
  }
3821
3921
 
3822
3922
  /* ════════════════════════════════════════════════
@@ -4335,10 +4435,9 @@
4335
4435
  background: var(--hw-bg-hover);
4336
4436
  }
4337
4437
 
4338
- .hw-disconnect {
4339
- background: #422006;
4340
- color: #fcd34d;
4341
- }
4438
+ /* .hw-disconnect needs no dark override any more — it is built from
4439
+ --hw-bg-hover and --hw-text-3, which this block already redefines. A
4440
+ hard-coded amber pair here was the other half of the same mixed signal. */
4342
4441
 
4343
4442
  .hw-send-error {
4344
4443
  background: #450a0a;
@@ -4936,9 +5035,12 @@
4936
5035
  answer restated them three times in a row, which is how the meta line stopped
4937
5036
  being read at all. A human agent keeps their line: their name is news (someone
4938
5037
  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
- }
5038
+ /* The AI's meta row used to be hidden here while the team's stayed visible, so
5039
+ in this placement an AI answer was unsigned and a human answer carried a naked
5040
+ clock — the asymmetry is what made the clock look arbitrary. Now both sign the
5041
+ same way, with role + time, which is the row Fin prints on an AI answer too.
5042
+ (To go back to hiding it, restore `display: none` for .hw-msg--ai .hw-msg-meta
5043
+ — it is one rule, not a rewrite.) */
4942
5044
  .hw-widget[data-hw-placement="docked"] .hw-msg--visitor .hw-msg-bubble {
4943
5045
  background: rgba(255, 255, 255, 0.16);
4944
5046
  color: #ffffff;
@@ -5349,11 +5451,11 @@
5349
5451
  /* Reconnecting. A socket blip is not an error — it resolves itself in a
5350
5452
  second or two — so it sits quietly at the top of the panel instead of
5351
5453
  shouting in amber across the full width. */
5454
+ /* The base rule now carries the padding, surface, ink and size this variant used
5455
+ to restate — the quiet treatment stopped being the docked exception and became
5456
+ how the band looks everywhere. Only the corners are placement-specific: docked
5457
+ is the one case where this band is the panel's actual top edge. */
5352
5458
  .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
5459
  border-radius: 20px 20px 0 0;
5358
5460
  }
5359
5461