@mhosaic/feedback 0.7.0 → 0.7.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.
@@ -486,6 +486,12 @@ var DEFAULT_STRINGS = {
486
486
  "detail.close_cta": "Mark as resolved",
487
487
  "detail.close_busy": "Marking\u2026",
488
488
  "detail.history": "Status history",
489
+ "detail.context.submitted_at": "Submitted",
490
+ "detail.context.page": "Page",
491
+ "detail.context.capture.manual": "Manual capture",
492
+ "detail.context.capture.html2canvas": "Auto capture",
493
+ "detail.context.capture.display_media": "Screen share",
494
+ "detail.context.capture.none": "No screenshot",
489
495
  "detail.author.staff": "Operator",
490
496
  "detail.author.mcp": "Mhosaic Team",
491
497
  "detail.author.system": "System",
@@ -559,6 +565,12 @@ var FRENCH_STRINGS = {
559
565
  "detail.close_cta": "Marquer comme r\xE9solu",
560
566
  "detail.close_busy": "Validation\u2026",
561
567
  "detail.history": "Historique du statut",
568
+ "detail.context.submitted_at": "Envoy\xE9",
569
+ "detail.context.page": "Page",
570
+ "detail.context.capture.manual": "Capture manuelle",
571
+ "detail.context.capture.html2canvas": "Capture automatique",
572
+ "detail.context.capture.display_media": "Partage d\u2019\xE9cran",
573
+ "detail.context.capture.none": "Sans capture",
562
574
  "detail.author.staff": "Op\xE9rateur",
563
575
  "detail.author.mcp": "\xC9quipe Mhosaic",
564
576
  "detail.author.system": "Syst\xE8me",
@@ -593,8 +605,28 @@ import { useCallback } from "preact/hooks";
593
605
 
594
606
  // src/widget/Fab.tsx
595
607
  import { jsx } from "preact/jsx-runtime";
608
+ function ChatBubbleIcon() {
609
+ return /* @__PURE__ */ jsx(
610
+ "svg",
611
+ {
612
+ width: "24",
613
+ height: "24",
614
+ viewBox: "0 0 24 24",
615
+ fill: "none",
616
+ "aria-hidden": "true",
617
+ focusable: "false",
618
+ children: /* @__PURE__ */ jsx(
619
+ "path",
620
+ {
621
+ d: "M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z",
622
+ fill: "currentColor"
623
+ }
624
+ )
625
+ }
626
+ );
627
+ }
596
628
  function Fab({ label, onClick }) {
597
- return /* @__PURE__ */ jsx("button", { type: "button", class: "fab", "aria-label": label, onClick, children: "\u{1F4AC}" });
629
+ return /* @__PURE__ */ jsx("button", { type: "button", class: "fab", "aria-label": label, title: label, onClick, children: /* @__PURE__ */ jsx(ChatBubbleIcon, {}) });
598
630
  }
599
631
 
600
632
  // src/widget/Form.tsx
@@ -1464,6 +1496,7 @@ function ReportDetailView({
1464
1496
  /* @__PURE__ */ jsx8("span", { class: `pill pill-status pill-status--${detail.status}`, children: strings[`status.${detail.status}`] ?? detail.status })
1465
1497
  ] }),
1466
1498
  /* @__PURE__ */ jsxs7("div", { class: "report-detail-body", children: [
1499
+ /* @__PURE__ */ jsx8(ContextBlock, { detail, strings }),
1467
1500
  /* @__PURE__ */ jsx8("p", { class: "report-detail-description", children: detail.description }),
1468
1501
  detail.screenshot_url && /* @__PURE__ */ jsx8(
1469
1502
  "a",
@@ -1518,6 +1551,44 @@ function appendComment(current, next) {
1518
1551
  if (current.some((c) => c.id === next.id)) return current;
1519
1552
  return [...current, next];
1520
1553
  }
1554
+ function ContextBlock({ detail, strings }) {
1555
+ const captureKey = `detail.context.capture.${detail.capture_method}`;
1556
+ const captureLabel = strings[captureKey] ?? detail.capture_method;
1557
+ return /* @__PURE__ */ jsxs7("div", { class: "report-detail-context", children: [
1558
+ /* @__PURE__ */ jsxs7("div", { class: "report-detail-context-pills", children: [
1559
+ /* @__PURE__ */ jsx8("span", { class: "pill pill-type", children: strings[`type.${detail.feedback_type}`] }),
1560
+ /* @__PURE__ */ jsx8("span", { class: `pill pill-severity pill-severity--${detail.severity}`, children: strings[`severity.${detail.severity}`] }),
1561
+ /* @__PURE__ */ jsx8("span", { class: "pill pill-capture", children: captureLabel })
1562
+ ] }),
1563
+ /* @__PURE__ */ jsxs7("div", { class: "report-detail-context-line", children: [
1564
+ /* @__PURE__ */ jsx8("span", { class: "report-detail-context-label", children: strings["detail.context.submitted_at"] }),
1565
+ /* @__PURE__ */ jsx8("span", { children: formatSubmittedAt(detail.created_at) })
1566
+ ] }),
1567
+ detail.page_url && /* @__PURE__ */ jsxs7("div", { class: "report-detail-context-line", title: detail.page_url, children: [
1568
+ /* @__PURE__ */ jsx8("span", { class: "report-detail-context-label", children: strings["detail.context.page"] }),
1569
+ /* @__PURE__ */ jsx8(
1570
+ "a",
1571
+ {
1572
+ class: "report-detail-context-url",
1573
+ href: detail.page_url,
1574
+ target: "_blank",
1575
+ rel: "noopener noreferrer",
1576
+ children: truncateUrl(detail.page_url, 64)
1577
+ }
1578
+ )
1579
+ ] })
1580
+ ] });
1581
+ }
1582
+ function formatSubmittedAt(iso) {
1583
+ try {
1584
+ return new Date(iso).toLocaleString(void 0, {
1585
+ dateStyle: "medium",
1586
+ timeStyle: "short"
1587
+ });
1588
+ } catch {
1589
+ return iso;
1590
+ }
1591
+ }
1521
1592
 
1522
1593
  // src/widget/styles.ts
1523
1594
  var WIDGET_STYLES = `
@@ -1550,28 +1621,60 @@ var WIDGET_STYLES = `
1550
1621
  }
1551
1622
  }
1552
1623
 
1624
+ /* FAB \u2014 56px (Material standard), two-layer elevation, scale-on-press,
1625
+ custom SVG icon (no emoji \u2014 emoji renders inconsistently across OSes
1626
+ and can't inherit color). */
1553
1627
  .fab {
1554
1628
  position: fixed;
1555
1629
  bottom: 24px;
1556
1630
  right: 24px;
1557
- width: 52px;
1558
- height: 52px;
1631
+ width: 56px;
1632
+ height: 56px;
1559
1633
  border-radius: 999px;
1560
1634
  background: var(--mfb-accent);
1561
1635
  color: var(--mfb-accent-contrast);
1562
1636
  border: none;
1563
1637
  cursor: pointer;
1564
- box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
1565
- font-size: 22px;
1638
+ /* Two-layer elevation: ambient (soft, large) + key (tighter, near). */
1639
+ box-shadow:
1640
+ 0 4px 12px rgba(0, 0, 0, 0.08),
1641
+ 0 2px 4px rgba(0, 0, 0, 0.12);
1566
1642
  display: grid;
1567
1643
  place-items: center;
1568
- transition: transform 120ms ease, box-shadow 120ms ease;
1644
+ transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1),
1645
+ box-shadow 180ms cubic-bezier(0.4, 0, 0.2, 1);
1569
1646
  }
1570
1647
 
1571
- .fab:hover { transform: translateY(-1px); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.24); }
1572
- .fab:active { transform: translateY(0) scale(0.96); box-shadow: 0 3px 10px rgba(0, 0, 0, 0.22); }
1573
- .fab:focus-visible { outline: 2px solid #fff; outline-offset: 3px; box-shadow: 0 0 0 4px var(--mfb-accent), 0 4px 14px rgba(0, 0, 0, 0.18); }
1574
- @media (prefers-reduced-motion: reduce) { .fab { transition: none; } .fab:hover, .fab:active { transform: none; } }
1648
+ .fab:hover {
1649
+ transform: translateY(-2px);
1650
+ box-shadow:
1651
+ 0 8px 24px rgba(0, 0, 0, 0.12),
1652
+ 0 3px 6px rgba(0, 0, 0, 0.16);
1653
+ }
1654
+ .fab:active {
1655
+ transform: translateY(0) scale(0.96);
1656
+ box-shadow:
1657
+ 0 3px 8px rgba(0, 0, 0, 0.10),
1658
+ 0 1px 2px rgba(0, 0, 0, 0.14);
1659
+ }
1660
+ .fab:focus-visible {
1661
+ outline: 2px solid var(--mfb-accent-contrast);
1662
+ outline-offset: 3px;
1663
+ box-shadow:
1664
+ 0 0 0 4px var(--mfb-accent),
1665
+ 0 4px 12px rgba(0, 0, 0, 0.08),
1666
+ 0 2px 4px rgba(0, 0, 0, 0.12);
1667
+ }
1668
+ @media (prefers-color-scheme: dark) {
1669
+ /* Slightly desaturate so the FAB doesn't glow against dark backgrounds. */
1670
+ .fab { box-shadow:
1671
+ 0 4px 12px rgba(0, 0, 0, 0.32),
1672
+ 0 2px 4px rgba(0, 0, 0, 0.40); }
1673
+ }
1674
+ @media (prefers-reduced-motion: reduce) {
1675
+ .fab { transition: none; }
1676
+ .fab:hover, .fab:active { transform: none; }
1677
+ }
1575
1678
 
1576
1679
  .backdrop {
1577
1680
  position: fixed;
@@ -1585,11 +1688,16 @@ var WIDGET_STYLES = `
1585
1688
  background: var(--mfb-bg);
1586
1689
  border-radius: calc(var(--mfb-radius) * 1.5);
1587
1690
  box-shadow: 0 20px 48px rgba(0, 0, 0, 0.25);
1588
- width: min(420px, 92vw);
1589
- padding: 20px;
1691
+ /* 440px is the industry sweet spot for short forms \u2014 Linear, Plain,
1692
+ Sentry all sit in the 420-480 range. 92vw caps it on narrow screens. */
1693
+ width: min(440px, 92vw);
1694
+ /* 24px is shadcn's default; gives every input room to breathe. */
1695
+ padding: 24px;
1590
1696
  display: flex;
1591
1697
  flex-direction: column;
1592
- gap: 12px;
1698
+ /* 18px between major blocks (h2 \u2192 field \u2192 field \u2192 footer). Within a
1699
+ field group, .field uses a tighter 6px label-to-input gap. */
1700
+ gap: 18px;
1593
1701
  position: relative;
1594
1702
  /* Cap modal height on short viewports (mobile landscape, tiny laptops)
1595
1703
  so the form scrolls inside the modal rather than overflowing the
@@ -1598,7 +1706,23 @@ var WIDGET_STYLES = `
1598
1706
  overflow-y: auto;
1599
1707
  }
1600
1708
 
1601
- .modal h2 { margin: 0 0 4px; font-size: 18px; font-weight: 600; padding-right: 28px; }
1709
+ .modal h2 {
1710
+ margin: 0;
1711
+ font-size: 17px;
1712
+ font-weight: 600;
1713
+ padding-right: 28px;
1714
+ letter-spacing: -0.01em;
1715
+ }
1716
+
1717
+ /* The form sits inside .modal as a single flex child, so the modal-level
1718
+ gap doesn't separate the form's *own* children. Mirror the column +
1719
+ gap pattern on the form itself; 22px lands in the middle of the
1720
+ 20-24px field-to-field range the design critic recommended. */
1721
+ .modal form {
1722
+ display: flex;
1723
+ flex-direction: column;
1724
+ gap: 22px;
1725
+ }
1602
1726
 
1603
1727
  .modal-close {
1604
1728
  position: absolute;
@@ -1620,15 +1744,22 @@ var WIDGET_STYLES = `
1620
1744
  .modal-close:hover { background: var(--mfb-surface); color: var(--mfb-text); }
1621
1745
  .modal-close:focus-visible { outline: 2px solid var(--mfb-accent); outline-offset: 2px; }
1622
1746
 
1623
- .field { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
1747
+ /* Each field group: label (13px medium, muted) + input (14px regular)
1748
+ stacked with a 6px gap. The 18px modal-level gap separates groups. */
1749
+ .field { display: flex; flex-direction: column; gap: 6px; font-size: 13px; }
1624
1750
 
1625
- .field label { color: var(--mfb-text-muted); }
1751
+ .field label {
1752
+ color: var(--mfb-text-muted);
1753
+ font-weight: 500;
1754
+ font-size: 12px;
1755
+ letter-spacing: 0.01em;
1756
+ }
1626
1757
 
1627
1758
  .field input, .field select, .field textarea {
1628
1759
  font-family: inherit;
1629
1760
  font-size: 14px;
1630
1761
  color: inherit;
1631
- padding: 8px 10px;
1762
+ padding: 9px 12px;
1632
1763
  border: 1px solid var(--mfb-border);
1633
1764
  border-radius: var(--mfb-radius);
1634
1765
  background: var(--mfb-surface);
@@ -1652,28 +1783,44 @@ var WIDGET_STYLES = `
1652
1783
  background-position: right 10px center;
1653
1784
  }
1654
1785
 
1655
- .field textarea { min-height: 88px; resize: vertical; }
1786
+ .field textarea { min-height: 96px; resize: vertical; line-height: 1.45; }
1656
1787
 
1657
- .row { display: flex; gap: 8px; }
1788
+ .row { display: flex; gap: 12px; }
1658
1789
  .row > * { flex: 1; }
1659
1790
 
1660
- .actions { display: flex; gap: 8px; justify-content: flex-end; padding-top: 8px; }
1791
+ /* Footer separation: border-top + 16px breathing room, Stripe/Linear pattern.
1792
+ Stops Cancel/Send from floating against the dropzone or page-URL footer. */
1793
+ .actions {
1794
+ display: flex;
1795
+ gap: 8px;
1796
+ justify-content: flex-end;
1797
+ padding-top: 16px;
1798
+ margin-top: 4px;
1799
+ border-top: 1px solid var(--mfb-border);
1800
+ }
1661
1801
 
1662
1802
  .btn {
1663
- padding: 8px 14px;
1803
+ padding: 9px 16px;
1664
1804
  border-radius: var(--mfb-radius);
1665
1805
  border: 1px solid var(--mfb-border);
1666
1806
  background: var(--mfb-bg);
1667
1807
  color: var(--mfb-text);
1668
1808
  font: inherit;
1809
+ font-weight: 500;
1669
1810
  cursor: pointer;
1811
+ transition: background 120ms ease, border-color 120ms ease;
1670
1812
  }
1813
+ .btn:hover { background: var(--mfb-surface); }
1671
1814
 
1672
1815
  .btn--primary {
1673
1816
  background: var(--mfb-accent);
1674
1817
  color: var(--mfb-accent-contrast);
1675
1818
  border-color: var(--mfb-accent);
1676
1819
  }
1820
+ .btn--primary:hover {
1821
+ background: color-mix(in srgb, var(--mfb-accent) 88%, black);
1822
+ border-color: color-mix(in srgb, var(--mfb-accent) 88%, black);
1823
+ }
1677
1824
 
1678
1825
  .btn[disabled] { opacity: 0.6; cursor: not-allowed; }
1679
1826
 
@@ -1697,7 +1844,7 @@ var WIDGET_STYLES = `
1697
1844
  .screenshot-dropzone {
1698
1845
  border: 1px dashed var(--mfb-border);
1699
1846
  border-radius: var(--mfb-radius);
1700
- padding: 14px 12px;
1847
+ padding: 18px 14px;
1701
1848
  text-align: center;
1702
1849
  cursor: pointer;
1703
1850
  background: var(--mfb-surface);
@@ -1770,12 +1917,12 @@ var WIDGET_STYLES = `
1770
1917
  text-transform: uppercase;
1771
1918
  font-weight: 600;
1772
1919
  letter-spacing: 0.04em;
1920
+ font-size: 10px;
1773
1921
  }
1774
1922
  .page-context-url {
1775
1923
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1776
- background: var(--mfb-surface);
1777
- padding: 4px 6px;
1778
- border-radius: 4px;
1924
+ font-size: 11px;
1925
+ color: var(--mfb-text-muted);
1779
1926
  flex: 1;
1780
1927
  overflow: hidden;
1781
1928
  text-overflow: ellipsis;
@@ -2052,6 +2199,43 @@ var WIDGET_STYLES = `
2052
2199
  margin: 8px 0 4px;
2053
2200
  font-weight: 600;
2054
2201
  }
2202
+
2203
+ .report-detail-context {
2204
+ display: flex;
2205
+ flex-direction: column;
2206
+ gap: 6px;
2207
+ padding: 10px 12px;
2208
+ background: var(--mfb-surface);
2209
+ border-radius: var(--mfb-radius);
2210
+ border: 1px solid var(--mfb-border);
2211
+ }
2212
+ .report-detail-context-pills { display: flex; gap: 4px; flex-wrap: wrap; }
2213
+ .report-detail-context-line {
2214
+ display: flex;
2215
+ align-items: baseline;
2216
+ gap: 8px;
2217
+ font-size: 12px;
2218
+ color: var(--mfb-text);
2219
+ }
2220
+ .report-detail-context-label {
2221
+ text-transform: uppercase;
2222
+ letter-spacing: 0.04em;
2223
+ font-size: 10px;
2224
+ font-weight: 600;
2225
+ color: var(--mfb-text-muted);
2226
+ min-width: 56px;
2227
+ }
2228
+ .report-detail-context-url {
2229
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
2230
+ color: var(--mfb-accent);
2231
+ text-decoration: none;
2232
+ font-size: 11px;
2233
+ overflow: hidden;
2234
+ text-overflow: ellipsis;
2235
+ white-space: nowrap;
2236
+ }
2237
+ .report-detail-context-url:hover { text-decoration: underline; }
2238
+ .pill-capture { background: var(--mfb-bg); color: var(--mfb-text-muted); border-color: var(--mfb-border); }
2055
2239
  .report-detail-empty {
2056
2240
  font-size: 12px;
2057
2241
  color: var(--mfb-text-muted);
@@ -2398,4 +2582,4 @@ function createFeedback(config) {
2398
2582
  export {
2399
2583
  createFeedback
2400
2584
  };
2401
- //# sourceMappingURL=chunk-W6JAJT2U.mjs.map
2585
+ //# sourceMappingURL=chunk-2TLTE2VZ.mjs.map