@helpway/react 0.3.0 → 0.3.2
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/index.d.ts +2 -2
- package/dist/index.js +638 -396
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
- package/src/themed/styles.css +190 -38
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helpway/react",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
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",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsup",
|
|
42
42
|
"typecheck": "tsc --noEmit",
|
|
43
|
+
"sync:icons": "bun scripts/sync-icons.ts",
|
|
43
44
|
"prepublishOnly": "bun run build",
|
|
44
45
|
"prepack": "bun run build && bun scripts/apply-publish-config.ts",
|
|
45
46
|
"postpack": "bun scripts/apply-publish-config.ts --restore"
|
|
@@ -47,14 +48,15 @@
|
|
|
47
48
|
"//": "publishConfig swaps main/types/exports to the built dist on publish. Workspace consumers get raw src (fast HMR, no build step); npm consumers get compiled dist with .d.ts. Standard monorepo pattern.",
|
|
48
49
|
"dependencies": {
|
|
49
50
|
"@trpc/client": "^11.5.1",
|
|
50
|
-
"zustand": "^5.0.11"
|
|
51
|
-
"@central-icons-react/round-outlined-radius-2-stroke-2": "^1.1.178"
|
|
51
|
+
"zustand": "^5.0.11"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@helpway/types": "workspace:*",
|
|
55
55
|
"@helpway/widget-core": "workspace:*",
|
|
56
56
|
"@types/react": "^19.1.6",
|
|
57
57
|
"@types/react-dom": "^19.1.6",
|
|
58
|
+
"@central-icons-react/round-outlined-radius-2-stroke-2": "^1.1.213",
|
|
59
|
+
"react-dom": "^19.1.6",
|
|
58
60
|
"tsup": "^8.3.5",
|
|
59
61
|
"typescript": "~5.9.3"
|
|
60
62
|
},
|
package/src/themed/styles.css
CHANGED
|
@@ -45,6 +45,15 @@
|
|
|
45
45
|
.hw-widget {
|
|
46
46
|
--hw-primary: #171717;
|
|
47
47
|
--hw-primary-hover: #2a2a2a;
|
|
48
|
+
/* Brand surface used by team-avatar fallbacks and primary CTAs.
|
|
49
|
+
In light mode this is the host brand color (default near-black).
|
|
50
|
+
Dark overrides redefine to a high-contrast value so the brand tile
|
|
51
|
+
stays visible on dark surfaces. Customers override --hw-brand to
|
|
52
|
+
theme team avatars + primary CTAs in one shot. */
|
|
53
|
+
--hw-brand: #171717;
|
|
54
|
+
/* Foreground (text/svg) that sits on --hw-brand. Inverts in dark so
|
|
55
|
+
light-on-light never happens after the brand-token flip. */
|
|
56
|
+
--hw-on-brand: #ffffff;
|
|
48
57
|
/* Focus ring tokens — used by composer + content search + any future
|
|
49
58
|
input. Kept as variables so the dark override gets a proper contrast
|
|
50
59
|
pair instead of a one-off per-component tweak. */
|
|
@@ -304,6 +313,20 @@
|
|
|
304
313
|
flex-direction: column;
|
|
305
314
|
overflow: hidden;
|
|
306
315
|
animation: hw-window-open 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
316
|
+
/* Smooth size transition for reading-mode expansion. The open animation
|
|
317
|
+
is one-shot (transform/opacity) so it doesn't conflict with this. */
|
|
318
|
+
transition:
|
|
319
|
+
width 280ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
320
|
+
height 280ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* Reading mode — applied when the visitor enters a content detail view
|
|
324
|
+
(help article, changelog entry). Wider + taller for comfortable long-form
|
|
325
|
+
reading. Mobile (<480px) ignores this — the @media block forces 100% +
|
|
326
|
+
beats this rule by source order. */
|
|
327
|
+
.hw-window--reading {
|
|
328
|
+
width: min(720px, calc(100vw - 40px));
|
|
329
|
+
height: min(800px, calc(100vh - 100px));
|
|
307
330
|
}
|
|
308
331
|
|
|
309
332
|
@keyframes hw-window-open {
|
|
@@ -536,7 +559,7 @@
|
|
|
536
559
|
}
|
|
537
560
|
.hw-header-bot-icon--team {
|
|
538
561
|
background: var(--hw-brand, #0a0a0a);
|
|
539
|
-
color: #fff;
|
|
562
|
+
color: var(--hw-on-brand, #fff);
|
|
540
563
|
}
|
|
541
564
|
.hw-header-agent-img {
|
|
542
565
|
width: 100%;
|
|
@@ -907,7 +930,7 @@
|
|
|
907
930
|
}
|
|
908
931
|
.hw-conv-card-avatar--team {
|
|
909
932
|
background: var(--hw-brand, #0a0a0a);
|
|
910
|
-
color: #fff;
|
|
933
|
+
color: var(--hw-on-brand, #fff);
|
|
911
934
|
}
|
|
912
935
|
.hw-conv-card-avatar-initials {
|
|
913
936
|
font-size: 12px;
|
|
@@ -1173,6 +1196,11 @@
|
|
|
1173
1196
|
opacity: 0.3;
|
|
1174
1197
|
}
|
|
1175
1198
|
|
|
1199
|
+
.hw-empty-text {
|
|
1200
|
+
font-size: 13px;
|
|
1201
|
+
/* color inherited from .hw-empty (--hw-text-3) — adapts in dark */
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1176
1204
|
/* ── Date Separator ──────────────────────────── */
|
|
1177
1205
|
.hw-date-sep {
|
|
1178
1206
|
display: flex;
|
|
@@ -1294,7 +1322,7 @@
|
|
|
1294
1322
|
box-sizing: border-box;
|
|
1295
1323
|
}
|
|
1296
1324
|
|
|
1297
|
-
/* Meta (
|
|
1325
|
+
/* Meta (fully hidden; sender label + copy icon reveal on hover) */
|
|
1298
1326
|
.hw-msg-meta {
|
|
1299
1327
|
display: inline-flex;
|
|
1300
1328
|
align-items: center;
|
|
@@ -1303,29 +1331,25 @@
|
|
|
1303
1331
|
color: var(--hw-text-3);
|
|
1304
1332
|
margin-top: 3px;
|
|
1305
1333
|
padding: 0 4px;
|
|
1306
|
-
opacity: 1;
|
|
1307
|
-
font-variant-numeric: tabular-nums;
|
|
1308
|
-
min-height: 20px;
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
.hw-msg-time {
|
|
1312
|
-
opacity: 0.72;
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
.hw-msg-hover-meta {
|
|
1316
|
-
max-width: 0;
|
|
1317
1334
|
opacity: 0;
|
|
1335
|
+
font-variant-numeric: tabular-nums;
|
|
1336
|
+
min-height: 0;
|
|
1337
|
+
height: 0;
|
|
1318
1338
|
overflow: hidden;
|
|
1319
|
-
white-space: nowrap;
|
|
1320
1339
|
transition:
|
|
1321
|
-
|
|
1322
|
-
|
|
1340
|
+
opacity 140ms ease,
|
|
1341
|
+
height 140ms ease;
|
|
1323
1342
|
}
|
|
1324
1343
|
|
|
1325
|
-
.hw-msg:hover .hw-msg-
|
|
1326
|
-
.hw-msg:focus-within .hw-msg-
|
|
1327
|
-
max-width: 180px;
|
|
1344
|
+
.hw-msg:hover .hw-msg-meta,
|
|
1345
|
+
.hw-msg:focus-within .hw-msg-meta {
|
|
1328
1346
|
opacity: 1;
|
|
1347
|
+
height: 20px;
|
|
1348
|
+
min-height: 20px;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
.hw-msg-sender-label {
|
|
1352
|
+
white-space: nowrap;
|
|
1329
1353
|
}
|
|
1330
1354
|
|
|
1331
1355
|
.hw-msg--visitor .hw-msg-meta {
|
|
@@ -1556,6 +1580,59 @@
|
|
|
1556
1580
|
background: var(--hw-bg);
|
|
1557
1581
|
}
|
|
1558
1582
|
|
|
1583
|
+
/* ── Skeleton: Article detail ──────────────────
|
|
1584
|
+
Designed to be PIXEL-EQUIVALENT to the rendered article so content can land
|
|
1585
|
+
without reflow. Maps 1:1 to:
|
|
1586
|
+
<div class="hw-content-detail">
|
|
1587
|
+
<div class="hw-content-detail-header"> ← title (22px line) + meta row
|
|
1588
|
+
<div class="hw-content-detail-body">
|
|
1589
|
+
<div class="hw-markdown">
|
|
1590
|
+
<h2>...</h2> ← section header (~16px line)
|
|
1591
|
+
<ul><li>**Bold:** desc...</li></ul> ← bullet item, 2 visible lines
|
|
1592
|
+
<ul><li>...</li></ul>
|
|
1593
|
+
The skeleton uses the SAME outer classes for padding/spacing, then injects
|
|
1594
|
+
shimmer bars where text would render. */
|
|
1595
|
+
|
|
1596
|
+
.hw-skeleton-title {
|
|
1597
|
+
height: 22px;
|
|
1598
|
+
width: 65%;
|
|
1599
|
+
border-radius: 6px;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
.hw-skeleton-badge {
|
|
1603
|
+
width: 42px;
|
|
1604
|
+
height: 18px;
|
|
1605
|
+
border-radius: 9px;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
.hw-skeleton-date {
|
|
1609
|
+
width: 60px;
|
|
1610
|
+
height: 12px;
|
|
1611
|
+
border-radius: 6px;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
.hw-skeleton-h2 {
|
|
1615
|
+
height: 16px;
|
|
1616
|
+
border-radius: 6px;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
/* Bullet wrapper — emulates <ul> padding-left: 20px so the skeleton bullet
|
|
1620
|
+
text starts at the same x-coordinate as the real <li>'s text content. */
|
|
1621
|
+
.hw-skeleton-bullet {
|
|
1622
|
+
padding-left: 20px;
|
|
1623
|
+
display: flex;
|
|
1624
|
+
flex-direction: column;
|
|
1625
|
+
gap: 8px;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
/* Each "line" inside a bullet — line-height: 14 matches the visible text mass
|
|
1629
|
+
of body text (font-size 14 × line-height 1.6 = 22.4 visible bbox, but skeleton
|
|
1630
|
+
bar is the visible-character thickness only). */
|
|
1631
|
+
.hw-skeleton-bullet-line {
|
|
1632
|
+
height: 14px;
|
|
1633
|
+
border-radius: 7px;
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1559
1636
|
/* ════════════════════════════════════════════════
|
|
1560
1637
|
EMAIL COLLECTION CARD (fixed above composer)
|
|
1561
1638
|
════════════════════════════════════════════════ */
|
|
@@ -1565,7 +1642,7 @@
|
|
|
1565
1642
|
padding: 14px 16px 10px;
|
|
1566
1643
|
border: 1px solid var(--hw-border, rgba(0,0,0,0.08));
|
|
1567
1644
|
border-radius: 14px;
|
|
1568
|
-
background: var(--hw-
|
|
1645
|
+
background: var(--hw-bg, #fff);
|
|
1569
1646
|
box-shadow: 0 1px 4px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.03);
|
|
1570
1647
|
animation: hw-email-slide-in 0.25s ease-out;
|
|
1571
1648
|
}
|
|
@@ -1587,11 +1664,11 @@
|
|
|
1587
1664
|
width: 32px;
|
|
1588
1665
|
height: 32px;
|
|
1589
1666
|
border-radius: 8px;
|
|
1590
|
-
background: var(--hw-
|
|
1667
|
+
background: var(--hw-bg-hover, #f5f5f5);
|
|
1591
1668
|
display: flex;
|
|
1592
1669
|
align-items: center;
|
|
1593
1670
|
justify-content: center;
|
|
1594
|
-
color: #
|
|
1671
|
+
color: var(--hw-text, #1a1a1a);
|
|
1595
1672
|
}
|
|
1596
1673
|
|
|
1597
1674
|
.hw-email-card-title {
|
|
@@ -1604,7 +1681,7 @@
|
|
|
1604
1681
|
|
|
1605
1682
|
.hw-email-card-subtitle {
|
|
1606
1683
|
font-size: 12px;
|
|
1607
|
-
color: var(--hw-text-
|
|
1684
|
+
color: var(--hw-text-2, #888);
|
|
1608
1685
|
line-height: 1.4;
|
|
1609
1686
|
margin: 0;
|
|
1610
1687
|
}
|
|
@@ -1614,18 +1691,22 @@
|
|
|
1614
1691
|
padding: 9px 12px;
|
|
1615
1692
|
border-radius: 10px;
|
|
1616
1693
|
border: 1px solid var(--hw-border, rgba(0,0,0,0.1));
|
|
1617
|
-
background: var(--hw-bg, #fafafa);
|
|
1694
|
+
background: var(--hw-bg-subtle, #fafafa);
|
|
1618
1695
|
color: var(--hw-text, #1a1a1a);
|
|
1619
1696
|
font-size: 13px;
|
|
1620
1697
|
outline: none;
|
|
1621
1698
|
margin-bottom: 8px;
|
|
1622
1699
|
box-sizing: border-box;
|
|
1623
|
-
transition: border-color 0.15s;
|
|
1700
|
+
transition: border-color 0.15s, box-shadow 0.15s;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
.hw-email-card-input::placeholder {
|
|
1704
|
+
color: var(--hw-text-3, #999);
|
|
1624
1705
|
}
|
|
1625
1706
|
|
|
1626
1707
|
.hw-email-card-input:focus {
|
|
1627
|
-
border-color: var(--hw-
|
|
1628
|
-
box-shadow: 0 0 0 2px rgba(0,0,0,0.06);
|
|
1708
|
+
border-color: var(--hw-focus-border, rgba(0,0,0,0.2));
|
|
1709
|
+
box-shadow: 0 0 0 2px var(--hw-focus-ring, rgba(0,0,0,0.06));
|
|
1629
1710
|
}
|
|
1630
1711
|
|
|
1631
1712
|
.hw-email-card-input--error {
|
|
@@ -1638,7 +1719,7 @@
|
|
|
1638
1719
|
border-radius: 10px;
|
|
1639
1720
|
border: none;
|
|
1640
1721
|
background: var(--hw-brand, #0a0a0a);
|
|
1641
|
-
color: #fff;
|
|
1722
|
+
color: var(--hw-on-brand, #fff);
|
|
1642
1723
|
font-size: 13px;
|
|
1643
1724
|
font-weight: 500;
|
|
1644
1725
|
cursor: pointer;
|
|
@@ -1665,7 +1746,7 @@
|
|
|
1665
1746
|
gap: 6px;
|
|
1666
1747
|
padding: 10px 16px;
|
|
1667
1748
|
font-size: 13px;
|
|
1668
|
-
color: var(--hw-text-
|
|
1749
|
+
color: var(--hw-text-2, #888);
|
|
1669
1750
|
animation: hw-email-slide-in 0.2s ease-out;
|
|
1670
1751
|
}
|
|
1671
1752
|
|
|
@@ -2159,8 +2240,8 @@
|
|
|
2159
2240
|
width: 32px;
|
|
2160
2241
|
height: 32px;
|
|
2161
2242
|
border-radius: 50%;
|
|
2162
|
-
background: #e0e0e0;
|
|
2163
|
-
color: #555;
|
|
2243
|
+
background: var(--hw-bg-hover, #e0e0e0);
|
|
2244
|
+
color: var(--hw-text-2, #555);
|
|
2164
2245
|
display: flex;
|
|
2165
2246
|
align-items: center;
|
|
2166
2247
|
justify-content: center;
|
|
@@ -2685,31 +2766,92 @@
|
|
|
2685
2766
|
color: var(--hw-text-1);
|
|
2686
2767
|
}
|
|
2687
2768
|
|
|
2688
|
-
/* ── Markdown Rendering ──────────────────────────────────
|
|
2769
|
+
/* ── Markdown Rendering ──────────────────────────────────
|
|
2770
|
+
Heading vertical spacing is governed by the adjacency rules further
|
|
2771
|
+
down (.hw-markdown > * + h2, etc.). Keeping margin: 0 here prevents
|
|
2772
|
+
the heading's own bottom margin from compounding with the sibling's
|
|
2773
|
+
top margin (e.g. h2 8px + p 12px = 20px instead of 12px). */
|
|
2689
2774
|
.hw-markdown h1 {
|
|
2690
2775
|
font-size: 17px;
|
|
2691
2776
|
font-weight: 700;
|
|
2692
|
-
margin:
|
|
2777
|
+
margin: 0;
|
|
2693
2778
|
}
|
|
2694
2779
|
|
|
2695
2780
|
.hw-markdown h2 {
|
|
2696
2781
|
font-size: 15px;
|
|
2697
2782
|
font-weight: 600;
|
|
2698
|
-
margin:
|
|
2783
|
+
margin: 0;
|
|
2699
2784
|
}
|
|
2700
2785
|
|
|
2701
2786
|
.hw-markdown h3 {
|
|
2702
2787
|
font-size: 14px;
|
|
2703
2788
|
font-weight: 600;
|
|
2704
|
-
margin:
|
|
2789
|
+
margin: 0;
|
|
2705
2790
|
}
|
|
2706
2791
|
|
|
2707
2792
|
.hw-markdown p {
|
|
2708
|
-
margin: 0
|
|
2793
|
+
margin: 0;
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
/* Rich-text editors (PlateJS, Slate, ProseMirror) emit any of:
|
|
2797
|
+
<p><br></p> ← visual line break paragraph
|
|
2798
|
+
<p></p> ← empty paragraph
|
|
2799
|
+
<div><br></div> ← editor block element (PlateJS default)
|
|
2800
|
+
<div></div> ← empty block
|
|
2801
|
+
Each contributes line-height + margin twice → 50-70px gap between items.
|
|
2802
|
+
Hide them all so spacing is governed by the adjacency rules below. */
|
|
2803
|
+
.hw-markdown p:empty,
|
|
2804
|
+
.hw-markdown div:empty,
|
|
2805
|
+
.hw-markdown p:has(> br:only-child),
|
|
2806
|
+
.hw-markdown div:has(> br:only-child) {
|
|
2807
|
+
display: none;
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2810
|
+
/* Adjacency-based spacing — works for any block element type the CMS emits
|
|
2811
|
+
(<p>, <div>, custom). Combined with the JS post-render cleanup in
|
|
2812
|
+
simple-markdown.tsx (which strips whitespace-only blocks like <p> </p>
|
|
2813
|
+
that CSS :empty can't match), this gives consistent spacing regardless of
|
|
2814
|
+
editor format. */
|
|
2815
|
+
.hw-markdown > * + * {
|
|
2816
|
+
margin-top: 12px;
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
.hw-markdown > * + h1,
|
|
2820
|
+
.hw-markdown > * + h2 {
|
|
2821
|
+
margin-top: 20px;
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
.hw-markdown > * + h3 {
|
|
2825
|
+
margin-top: 16px;
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
.hw-markdown > h1 + *,
|
|
2829
|
+
.hw-markdown > h2 + *,
|
|
2830
|
+
.hw-markdown > h3 + * {
|
|
2831
|
+
margin-top: 8px;
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
/* First/last child margin reset so the body's outer padding is the only
|
|
2835
|
+
spacing that surrounds the article. */
|
|
2836
|
+
.hw-markdown > *:first-child {
|
|
2837
|
+
margin-top: 0;
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
.hw-markdown > *:last-child {
|
|
2841
|
+
margin-bottom: 0;
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
/* Better i18n CMS emits standalone <br> elements at the markdown root as
|
|
2845
|
+
visual separators between consecutive <ul> bullet groups. Each <br>
|
|
2846
|
+
contributed ~22px line-height + adjacency margins on both sides, creating
|
|
2847
|
+
the 40-50px gap between items. Hide them — the adjacency rule alone gives
|
|
2848
|
+
the 12px gap we want. */
|
|
2849
|
+
.hw-markdown > br {
|
|
2850
|
+
display: none;
|
|
2709
2851
|
}
|
|
2710
2852
|
|
|
2711
2853
|
.hw-markdown ul {
|
|
2712
|
-
margin: 0
|
|
2854
|
+
margin: 0;
|
|
2713
2855
|
padding-left: 20px;
|
|
2714
2856
|
}
|
|
2715
2857
|
|
|
@@ -3069,6 +3211,9 @@
|
|
|
3069
3211
|
}
|
|
3070
3212
|
.hw-msg-meta {
|
|
3071
3213
|
opacity: 1;
|
|
3214
|
+
height: auto;
|
|
3215
|
+
min-height: 20px;
|
|
3216
|
+
overflow: visible;
|
|
3072
3217
|
}
|
|
3073
3218
|
.hw-msg-copy {
|
|
3074
3219
|
opacity: 1;
|
|
@@ -3524,6 +3669,10 @@
|
|
|
3524
3669
|
--hw-visitor-bubble-text: #f5f5f5;
|
|
3525
3670
|
--hw-focus-border: rgba(255, 255, 255, 0.28);
|
|
3526
3671
|
--hw-focus-ring: rgba(255, 255, 255, 0.08);
|
|
3672
|
+
/* Brand inverts in dark — light tile on dark surface keeps team avatars
|
|
3673
|
+
and primary CTAs readable. Same pattern as iOS dark mode tints. */
|
|
3674
|
+
--hw-brand: #f5f5f5;
|
|
3675
|
+
--hw-on-brand: #1c1c1e;
|
|
3527
3676
|
}
|
|
3528
3677
|
|
|
3529
3678
|
@media (prefers-color-scheme: dark) {
|
|
@@ -3560,6 +3709,9 @@
|
|
|
3560
3709
|
/* Focus ring in dark needs lighter border + subtle halo. */
|
|
3561
3710
|
--hw-focus-border: rgba(255, 255, 255, 0.28);
|
|
3562
3711
|
--hw-focus-ring: rgba(255, 255, 255, 0.08);
|
|
3712
|
+
/* See --hw-brand comment in [data-theme="dark"] block. */
|
|
3713
|
+
--hw-brand: #f5f5f5;
|
|
3714
|
+
--hw-on-brand: #1c1c1e;
|
|
3563
3715
|
}
|
|
3564
3716
|
}
|
|
3565
3717
|
|