@quilltap/theme-storybook 1.0.57 → 1.0.59

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.
@@ -1899,6 +1899,73 @@ var Chat = () => {
1899
1899
  ] })
1900
1900
  ] })
1901
1901
  ] }),
1902
+ /* @__PURE__ */ jsxs14("section", { style: { marginBottom: "2rem" }, children: [
1903
+ /* @__PURE__ */ jsx14("h3", { style: { fontSize: "1.125rem", fontWeight: 700, marginBottom: "1rem", borderBottom: "1px solid var(--color-border)", paddingBottom: "0.5rem" }, children: "Composer Typeahead Menu" }),
1904
+ /* @__PURE__ */ jsxs14("p", { style: { marginBottom: "1rem", color: "var(--color-muted-foreground)", fontSize: "0.875rem" }, children: [
1905
+ "The inline `:name` emoji menu. The composer keeps focus throughout, so the highlighted row is marked by ",
1906
+ /* @__PURE__ */ jsx14("code", { children: ".qt-typeahead-option-active" }),
1907
+ " rather than by ",
1908
+ /* @__PURE__ */ jsx14("code", { children: ":focus" }),
1909
+ "."
1910
+ ] }),
1911
+ /* @__PURE__ */ jsxs14("p", { style: { marginBottom: "1rem", color: "var(--color-muted-foreground)", fontSize: "0.875rem" }, children: [
1912
+ "The menu positions itself against the caret element Lexical creates, so it is",
1913
+ /* @__PURE__ */ jsx14("code", { children: "position: absolute" }),
1914
+ " \u2014 these samples sit in a relatively-positioned host to stand in for that anchor."
1915
+ ] }),
1916
+ /* @__PURE__ */ jsxs14("div", { style: { display: "flex", gap: "1rem", flexWrap: "wrap" }, children: [
1917
+ /* @__PURE__ */ jsx14("div", { style: { position: "relative", width: "22rem", height: "9rem" }, children: /* @__PURE__ */ jsxs14("div", { className: "qt-typeahead-menu", style: { top: 0 }, children: [
1918
+ /* @__PURE__ */ jsxs14("div", { className: "qt-typeahead-option qt-typeahead-option-active", children: [
1919
+ /* @__PURE__ */ jsx14("span", { className: "qt-typeahead-option-glyph", children: "\u{1F604}" }),
1920
+ /* @__PURE__ */ jsx14("span", { className: "qt-typeahead-option-label", children: "grinning face with smiling eyes" }),
1921
+ /* @__PURE__ */ jsx14("span", { className: "qt-typeahead-option-detail", children: ":smile:" })
1922
+ ] }),
1923
+ /* @__PURE__ */ jsxs14("div", { className: "qt-typeahead-option", children: [
1924
+ /* @__PURE__ */ jsx14("span", { className: "qt-typeahead-option-glyph", children: "\u{1F603}" }),
1925
+ /* @__PURE__ */ jsx14("span", { className: "qt-typeahead-option-label", children: "grinning face with big eyes" }),
1926
+ /* @__PURE__ */ jsx14("span", { className: "qt-typeahead-option-detail", children: ":smiley:" })
1927
+ ] }),
1928
+ /* @__PURE__ */ jsxs14("div", { className: "qt-typeahead-option", children: [
1929
+ /* @__PURE__ */ jsx14("span", { className: "qt-typeahead-option-glyph", children: "\u{1F63A}" }),
1930
+ /* @__PURE__ */ jsx14("span", { className: "qt-typeahead-option-label", children: "grinning cat" }),
1931
+ /* @__PURE__ */ jsx14("span", { className: "qt-typeahead-option-detail", children: ":smile_cat:" })
1932
+ ] })
1933
+ ] }) }),
1934
+ /* @__PURE__ */ jsx14("div", { style: { position: "relative", width: "15rem", height: "9rem" }, children: /* @__PURE__ */ jsx14("div", { className: "qt-typeahead-menu", style: { top: 0 }, children: /* @__PURE__ */ jsx14("div", { className: "qt-typeahead-empty", children: "No emoji found" }) }) })
1935
+ ] })
1936
+ ] }),
1937
+ /* @__PURE__ */ jsxs14("section", { style: { marginBottom: "2rem" }, children: [
1938
+ /* @__PURE__ */ jsx14("h3", { style: { fontSize: "1.125rem", fontWeight: 700, marginBottom: "1rem", borderBottom: "1px solid var(--color-border)", paddingBottom: "0.5rem" }, children: "Emoji Picker" }),
1939
+ /* @__PURE__ */ jsxs14("p", { style: { marginBottom: "1rem", color: "var(--color-muted-foreground)", fontSize: "0.875rem" }, children: [
1940
+ "Opened from the formatting toolbar's ",
1941
+ /* @__PURE__ */ jsx14("code", { children: ".qt-formatting-button-emoji" }),
1942
+ ". The shell is ",
1943
+ /* @__PURE__ */ jsx14("code", { children: ".qt-popover" }),
1944
+ "; group headers stick while the grid scrolls."
1945
+ ] }),
1946
+ /* @__PURE__ */ jsxs14("div", { className: "qt-popover qt-emoji-picker", children: [
1947
+ /* @__PURE__ */ jsx14("input", { className: "qt-input qt-emoji-picker-search", placeholder: "Search emoji\u2026", readOnly: true }),
1948
+ /* @__PURE__ */ jsxs14("div", { className: "qt-emoji-picker-grid", children: [
1949
+ /* @__PURE__ */ jsxs14("div", { children: [
1950
+ /* @__PURE__ */ jsx14("div", { className: "qt-emoji-picker-group-header", children: "Recently used" }),
1951
+ /* @__PURE__ */ jsx14("div", { className: "qt-emoji-picker-row", children: ["\u{1F604}", "\u{1F389}", "\u{1F680}", "\u{1F44D}\uFE0F"].map((char) => /* @__PURE__ */ jsx14("button", { type: "button", className: "qt-emoji-picker-cell", children: char }, char)) })
1952
+ ] }),
1953
+ /* @__PURE__ */ jsxs14("div", { children: [
1954
+ /* @__PURE__ */ jsx14("div", { className: "qt-emoji-picker-group-header", children: "Smileys & Emotion" }),
1955
+ /* @__PURE__ */ jsx14("div", { className: "qt-emoji-picker-row", children: ["\u{1F600}", "\u{1F603}", "\u{1F604}", "\u{1F601}", "\u{1F606}", "\u{1F605}", "\u{1F923}", "\u{1F602}"].map((char) => /* @__PURE__ */ jsx14("button", { type: "button", className: "qt-emoji-picker-cell", children: char }, char)) }),
1956
+ /* @__PURE__ */ jsx14("div", { className: "qt-emoji-picker-row", children: ["\u{1F642}", "\u{1F643}", "\u{1F609}", "\u{1F60A}", "\u{1F607}", "\u{1F970}", "\u{1F60D}", "\u{1F929}"].map((char) => /* @__PURE__ */ jsx14("button", { type: "button", className: "qt-emoji-picker-cell", children: char }, char)) })
1957
+ ] }),
1958
+ /* @__PURE__ */ jsxs14("div", { children: [
1959
+ /* @__PURE__ */ jsx14("div", { className: "qt-emoji-picker-group-header", children: "Animals & Nature" }),
1960
+ /* @__PURE__ */ jsx14("div", { className: "qt-emoji-picker-row", children: ["\u{1F436}", "\u{1F431}", "\u{1F42D}", "\u{1F439}", "\u{1F430}", "\u{1F98A}", "\u{1F43B}", "\u{1F43C}"].map((char) => /* @__PURE__ */ jsx14("button", { type: "button", className: "qt-emoji-picker-cell", children: char }, char)) })
1961
+ ] })
1962
+ ] })
1963
+ ] }),
1964
+ /* @__PURE__ */ jsx14("div", { style: { marginTop: "1rem" }, children: /* @__PURE__ */ jsxs14("div", { className: "qt-popover qt-emoji-picker", children: [
1965
+ /* @__PURE__ */ jsx14("input", { className: "qt-input qt-emoji-picker-search", placeholder: "Search emoji\u2026", readOnly: true }),
1966
+ /* @__PURE__ */ jsx14("div", { className: "qt-emoji-picker-message", children: "Couldn't load emoji" })
1967
+ ] }) })
1968
+ ] }),
1902
1969
  /* @__PURE__ */ jsxs14("section", { children: [
1903
1970
  /* @__PURE__ */ jsx14("h3", { style: { fontSize: "1.125rem", fontWeight: 700, marginBottom: "1rem", borderBottom: "1px solid var(--color-border)", paddingBottom: "0.5rem" }, children: "Full Chat Layout" }),
1904
1971
  /* @__PURE__ */ jsxs14("div", { className: "qt-chat-layout", style: { height: "24rem", border: "1px solid var(--color-border)", borderRadius: "var(--radius-lg)", display: "flex", flexDirection: "column" }, children: [
package/dist/index.css CHANGED
@@ -1600,6 +1600,10 @@ body {
1600
1600
  font-size: 1.05rem;
1601
1601
  line-height: 1;
1602
1602
  }
1603
+ .qt-formatting-button-emoji {
1604
+ font-size: 1.05rem;
1605
+ line-height: 1;
1606
+ }
1603
1607
  .qt-formatting-button-source {
1604
1608
  display: flex;
1605
1609
  align-items: center;
@@ -1617,6 +1621,169 @@ body {
1617
1621
  background: color-mix(in srgb, var(--color-primary) 85%, black);
1618
1622
  border-color: color-mix(in srgb, var(--color-primary) 85%, black);
1619
1623
  }
1624
+ .qt-popover,
1625
+ .qt-dropdown {
1626
+ z-index: 50;
1627
+ min-width: 8rem;
1628
+ padding: 1rem;
1629
+ border-radius: var(--qt-popover-radius);
1630
+ background: var(--qt-popover-bg);
1631
+ color: var(--qt-popover-fg);
1632
+ border: 1px solid var(--qt-popover-border);
1633
+ box-shadow: var(--qt-popover-shadow);
1634
+ }
1635
+ .qt-dropdown {
1636
+ padding: 0.25rem;
1637
+ }
1638
+ .qt-dropdown-item {
1639
+ display: flex;
1640
+ align-items: center;
1641
+ gap: 0.5rem;
1642
+ padding: 0.5rem 0.75rem;
1643
+ font-size: 0.875rem;
1644
+ line-height: 1.25rem;
1645
+ border-radius: var(--radius-md);
1646
+ cursor: pointer;
1647
+ transition:
1648
+ color 150ms,
1649
+ background-color 150ms,
1650
+ border-color 150ms;
1651
+ }
1652
+ .qt-dropdown-item:hover {
1653
+ background: var(--color-muted);
1654
+ }
1655
+ .qt-dropdown-item:focus {
1656
+ background: var(--color-muted);
1657
+ outline: none;
1658
+ }
1659
+ .qt-typeahead-menu {
1660
+ position: absolute;
1661
+ z-index: 50;
1662
+ overflow-y: auto;
1663
+ padding: 0.25rem;
1664
+ top: 100%;
1665
+ left: 0;
1666
+ min-width: 14rem;
1667
+ max-width: 22rem;
1668
+ max-height: 17rem;
1669
+ border-radius: var(--qt-popover-radius);
1670
+ background: var(--qt-popover-bg);
1671
+ color: var(--qt-popover-fg);
1672
+ border: 1px solid var(--qt-popover-border);
1673
+ box-shadow: var(--qt-popover-shadow);
1674
+ }
1675
+ .qt-typeahead-menu[data-placement=above] {
1676
+ top: auto;
1677
+ bottom: 100%;
1678
+ }
1679
+ .qt-typeahead-menu[data-align=right] {
1680
+ left: auto;
1681
+ right: 0;
1682
+ }
1683
+ .qt-typeahead-option {
1684
+ display: flex;
1685
+ align-items: center;
1686
+ gap: 0.5rem;
1687
+ padding: 0.375rem 0.5rem;
1688
+ font-size: 0.875rem;
1689
+ line-height: 1.25rem;
1690
+ border-radius: var(--radius-md);
1691
+ cursor: pointer;
1692
+ transition:
1693
+ color 150ms,
1694
+ background-color 150ms,
1695
+ border-color 150ms;
1696
+ }
1697
+ .qt-typeahead-option-active {
1698
+ background: var(--color-muted);
1699
+ }
1700
+ .qt-typeahead-option:hover {
1701
+ background: var(--color-muted);
1702
+ }
1703
+ .qt-typeahead-option-glyph {
1704
+ flex: none;
1705
+ font-size: 1.15rem;
1706
+ line-height: 1;
1707
+ }
1708
+ .qt-typeahead-option-label {
1709
+ flex: 1 1 0%;
1710
+ overflow: hidden;
1711
+ text-overflow: ellipsis;
1712
+ white-space: nowrap;
1713
+ }
1714
+ .qt-typeahead-option-detail {
1715
+ flex: none;
1716
+ font-size: 0.75rem;
1717
+ line-height: 1rem;
1718
+ color: var(--qt-text-secondary-fg, var(--qt-text-secondary));
1719
+ }
1720
+ .qt-typeahead-empty {
1721
+ padding: 0.75rem 0.5rem;
1722
+ text-align: center;
1723
+ font-size: 0.875rem;
1724
+ line-height: 1.25rem;
1725
+ color: var(--qt-text-secondary-fg, var(--qt-text-secondary));
1726
+ }
1727
+ .qt-emoji-picker {
1728
+ display: flex;
1729
+ flex-direction: column;
1730
+ gap: 0.5rem;
1731
+ padding: 0.5rem;
1732
+ width: 20rem;
1733
+ }
1734
+ .qt-emoji-picker-search {
1735
+ width: 100%;
1736
+ }
1737
+ .qt-emoji-picker-grid {
1738
+ overflow-y: auto;
1739
+ overflow-x: hidden;
1740
+ max-height: 16rem;
1741
+ }
1742
+ .qt-emoji-picker-group-header {
1743
+ position: sticky;
1744
+ top: 0;
1745
+ z-index: 10;
1746
+ padding: 0.25rem;
1747
+ font-size: 0.75rem;
1748
+ line-height: 1rem;
1749
+ font-weight: 600;
1750
+ background: var(--qt-popover-bg);
1751
+ color: var(--qt-text-secondary-fg, var(--qt-text-secondary));
1752
+ }
1753
+ .qt-emoji-picker-row {
1754
+ display: flex;
1755
+ }
1756
+ .qt-emoji-picker-cell {
1757
+ display: flex;
1758
+ align-items: center;
1759
+ justify-content: center;
1760
+ border-radius: 0.25rem;
1761
+ cursor: pointer;
1762
+ transition:
1763
+ color 150ms,
1764
+ background-color 150ms,
1765
+ border-color 150ms;
1766
+ width: 2.25rem;
1767
+ height: 2.25rem;
1768
+ font-size: 1.25rem;
1769
+ line-height: 1;
1770
+ background: transparent;
1771
+ border: none;
1772
+ }
1773
+ .qt-emoji-picker-cell:hover {
1774
+ background: var(--qt-content-bg-hover);
1775
+ }
1776
+ .qt-emoji-picker-cell:focus-visible {
1777
+ outline: 2px solid var(--color-primary);
1778
+ outline-offset: -2px;
1779
+ }
1780
+ .qt-emoji-picker-message {
1781
+ padding: 1.5rem 0.5rem;
1782
+ text-align: center;
1783
+ font-size: 0.875rem;
1784
+ line-height: 1.25rem;
1785
+ color: var(--qt-text-secondary-fg, var(--qt-text-secondary));
1786
+ }
1620
1787
  .qt-lexical-contenteditable table {
1621
1788
  width: 100%;
1622
1789
  border-collapse: collapse;
package/dist/index.js CHANGED
@@ -2009,6 +2009,73 @@ var Chat = () => {
2009
2009
  ] })
2010
2010
  ] })
2011
2011
  ] }),
2012
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("section", { style: { marginBottom: "2rem" }, children: [
2013
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h3", { style: { fontSize: "1.125rem", fontWeight: 700, marginBottom: "1rem", borderBottom: "1px solid var(--color-border)", paddingBottom: "0.5rem" }, children: "Composer Typeahead Menu" }),
2014
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { style: { marginBottom: "1rem", color: "var(--color-muted-foreground)", fontSize: "0.875rem" }, children: [
2015
+ "The inline `:name` emoji menu. The composer keeps focus throughout, so the highlighted row is marked by ",
2016
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { children: ".qt-typeahead-option-active" }),
2017
+ " rather than by ",
2018
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { children: ":focus" }),
2019
+ "."
2020
+ ] }),
2021
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { style: { marginBottom: "1rem", color: "var(--color-muted-foreground)", fontSize: "0.875rem" }, children: [
2022
+ "The menu positions itself against the caret element Lexical creates, so it is",
2023
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { children: "position: absolute" }),
2024
+ " \u2014 these samples sit in a relatively-positioned host to stand in for that anchor."
2025
+ ] }),
2026
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { display: "flex", gap: "1rem", flexWrap: "wrap" }, children: [
2027
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { position: "relative", width: "22rem", height: "9rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "qt-typeahead-menu", style: { top: 0 }, children: [
2028
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "qt-typeahead-option qt-typeahead-option-active", children: [
2029
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "qt-typeahead-option-glyph", children: "\u{1F604}" }),
2030
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "qt-typeahead-option-label", children: "grinning face with smiling eyes" }),
2031
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "qt-typeahead-option-detail", children: ":smile:" })
2032
+ ] }),
2033
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "qt-typeahead-option", children: [
2034
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "qt-typeahead-option-glyph", children: "\u{1F603}" }),
2035
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "qt-typeahead-option-label", children: "grinning face with big eyes" }),
2036
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "qt-typeahead-option-detail", children: ":smiley:" })
2037
+ ] }),
2038
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "qt-typeahead-option", children: [
2039
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "qt-typeahead-option-glyph", children: "\u{1F63A}" }),
2040
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "qt-typeahead-option-label", children: "grinning cat" }),
2041
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "qt-typeahead-option-detail", children: ":smile_cat:" })
2042
+ ] })
2043
+ ] }) }),
2044
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { position: "relative", width: "15rem", height: "9rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "qt-typeahead-menu", style: { top: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "qt-typeahead-empty", children: "No emoji found" }) }) })
2045
+ ] })
2046
+ ] }),
2047
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("section", { style: { marginBottom: "2rem" }, children: [
2048
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h3", { style: { fontSize: "1.125rem", fontWeight: 700, marginBottom: "1rem", borderBottom: "1px solid var(--color-border)", paddingBottom: "0.5rem" }, children: "Emoji Picker" }),
2049
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { style: { marginBottom: "1rem", color: "var(--color-muted-foreground)", fontSize: "0.875rem" }, children: [
2050
+ "Opened from the formatting toolbar's ",
2051
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { children: ".qt-formatting-button-emoji" }),
2052
+ ". The shell is ",
2053
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { children: ".qt-popover" }),
2054
+ "; group headers stick while the grid scrolls."
2055
+ ] }),
2056
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "qt-popover qt-emoji-picker", children: [
2057
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("input", { className: "qt-input qt-emoji-picker-search", placeholder: "Search emoji\u2026", readOnly: true }),
2058
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "qt-emoji-picker-grid", children: [
2059
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
2060
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "qt-emoji-picker-group-header", children: "Recently used" }),
2061
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "qt-emoji-picker-row", children: ["\u{1F604}", "\u{1F389}", "\u{1F680}", "\u{1F44D}\uFE0F"].map((char) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: "qt-emoji-picker-cell", children: char }, char)) })
2062
+ ] }),
2063
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
2064
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "qt-emoji-picker-group-header", children: "Smileys & Emotion" }),
2065
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "qt-emoji-picker-row", children: ["\u{1F600}", "\u{1F603}", "\u{1F604}", "\u{1F601}", "\u{1F606}", "\u{1F605}", "\u{1F923}", "\u{1F602}"].map((char) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: "qt-emoji-picker-cell", children: char }, char)) }),
2066
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "qt-emoji-picker-row", children: ["\u{1F642}", "\u{1F643}", "\u{1F609}", "\u{1F60A}", "\u{1F607}", "\u{1F970}", "\u{1F60D}", "\u{1F929}"].map((char) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: "qt-emoji-picker-cell", children: char }, char)) })
2067
+ ] }),
2068
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
2069
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "qt-emoji-picker-group-header", children: "Animals & Nature" }),
2070
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "qt-emoji-picker-row", children: ["\u{1F436}", "\u{1F431}", "\u{1F42D}", "\u{1F439}", "\u{1F430}", "\u{1F98A}", "\u{1F43B}", "\u{1F43C}"].map((char) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: "qt-emoji-picker-cell", children: char }, char)) })
2071
+ ] })
2072
+ ] })
2073
+ ] }),
2074
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { marginTop: "1rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "qt-popover qt-emoji-picker", children: [
2075
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("input", { className: "qt-input qt-emoji-picker-search", placeholder: "Search emoji\u2026", readOnly: true }),
2076
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "qt-emoji-picker-message", children: "Couldn't load emoji" })
2077
+ ] }) })
2078
+ ] }),
2012
2079
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("section", { children: [
2013
2080
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h3", { style: { fontSize: "1.125rem", fontWeight: 700, marginBottom: "1rem", borderBottom: "1px solid var(--color-border)", paddingBottom: "0.5rem" }, children: "Full Chat Layout" }),
2014
2081
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "qt-chat-layout", style: { height: "24rem", border: "1px solid var(--color-border)", borderRadius: "var(--radius-lg)", display: "flex", flexDirection: "column" }, children: [
package/dist/index.mjs CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  ThemeComparison,
22
22
  Typography,
23
23
  Workspace
24
- } from "./chunk-3RAUK5HZ.mjs";
24
+ } from "./chunk-4XVTTTDV.mjs";
25
25
  import "./chunk-WUKYLWAZ.mjs";
26
26
 
27
27
  // src/preview.tsx
@@ -1946,6 +1946,73 @@ var Chat = () => {
1946
1946
  ] })
1947
1947
  ] })
1948
1948
  ] }),
1949
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("section", { style: { marginBottom: "2rem" }, children: [
1950
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { style: { fontSize: "1.125rem", fontWeight: 700, marginBottom: "1rem", borderBottom: "1px solid var(--color-border)", paddingBottom: "0.5rem" }, children: "Composer Typeahead Menu" }),
1951
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { style: { marginBottom: "1rem", color: "var(--color-muted-foreground)", fontSize: "0.875rem" }, children: [
1952
+ "The inline `:name` emoji menu. The composer keeps focus throughout, so the highlighted row is marked by ",
1953
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("code", { children: ".qt-typeahead-option-active" }),
1954
+ " rather than by ",
1955
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("code", { children: ":focus" }),
1956
+ "."
1957
+ ] }),
1958
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { style: { marginBottom: "1rem", color: "var(--color-muted-foreground)", fontSize: "0.875rem" }, children: [
1959
+ "The menu positions itself against the caret element Lexical creates, so it is",
1960
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("code", { children: "position: absolute" }),
1961
+ " \u2014 these samples sit in a relatively-positioned host to stand in for that anchor."
1962
+ ] }),
1963
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { display: "flex", gap: "1rem", flexWrap: "wrap" }, children: [
1964
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { position: "relative", width: "22rem", height: "9rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "qt-typeahead-menu", style: { top: 0 }, children: [
1965
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "qt-typeahead-option qt-typeahead-option-active", children: [
1966
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "qt-typeahead-option-glyph", children: "\u{1F604}" }),
1967
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "qt-typeahead-option-label", children: "grinning face with smiling eyes" }),
1968
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "qt-typeahead-option-detail", children: ":smile:" })
1969
+ ] }),
1970
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "qt-typeahead-option", children: [
1971
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "qt-typeahead-option-glyph", children: "\u{1F603}" }),
1972
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "qt-typeahead-option-label", children: "grinning face with big eyes" }),
1973
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "qt-typeahead-option-detail", children: ":smiley:" })
1974
+ ] }),
1975
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "qt-typeahead-option", children: [
1976
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "qt-typeahead-option-glyph", children: "\u{1F63A}" }),
1977
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "qt-typeahead-option-label", children: "grinning cat" }),
1978
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "qt-typeahead-option-detail", children: ":smile_cat:" })
1979
+ ] })
1980
+ ] }) }),
1981
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { position: "relative", width: "15rem", height: "9rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "qt-typeahead-menu", style: { top: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "qt-typeahead-empty", children: "No emoji found" }) }) })
1982
+ ] })
1983
+ ] }),
1984
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("section", { style: { marginBottom: "2rem" }, children: [
1985
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { style: { fontSize: "1.125rem", fontWeight: 700, marginBottom: "1rem", borderBottom: "1px solid var(--color-border)", paddingBottom: "0.5rem" }, children: "Emoji Picker" }),
1986
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { style: { marginBottom: "1rem", color: "var(--color-muted-foreground)", fontSize: "0.875rem" }, children: [
1987
+ "Opened from the formatting toolbar's ",
1988
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("code", { children: ".qt-formatting-button-emoji" }),
1989
+ ". The shell is ",
1990
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("code", { children: ".qt-popover" }),
1991
+ "; group headers stick while the grid scrolls."
1992
+ ] }),
1993
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "qt-popover qt-emoji-picker", children: [
1994
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("input", { className: "qt-input qt-emoji-picker-search", placeholder: "Search emoji\u2026", readOnly: true }),
1995
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "qt-emoji-picker-grid", children: [
1996
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
1997
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "qt-emoji-picker-group-header", children: "Recently used" }),
1998
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "qt-emoji-picker-row", children: ["\u{1F604}", "\u{1F389}", "\u{1F680}", "\u{1F44D}\uFE0F"].map((char) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "qt-emoji-picker-cell", children: char }, char)) })
1999
+ ] }),
2000
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
2001
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "qt-emoji-picker-group-header", children: "Smileys & Emotion" }),
2002
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "qt-emoji-picker-row", children: ["\u{1F600}", "\u{1F603}", "\u{1F604}", "\u{1F601}", "\u{1F606}", "\u{1F605}", "\u{1F923}", "\u{1F602}"].map((char) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "qt-emoji-picker-cell", children: char }, char)) }),
2003
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "qt-emoji-picker-row", children: ["\u{1F642}", "\u{1F643}", "\u{1F609}", "\u{1F60A}", "\u{1F607}", "\u{1F970}", "\u{1F60D}", "\u{1F929}"].map((char) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "qt-emoji-picker-cell", children: char }, char)) })
2004
+ ] }),
2005
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
2006
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "qt-emoji-picker-group-header", children: "Animals & Nature" }),
2007
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "qt-emoji-picker-row", children: ["\u{1F436}", "\u{1F431}", "\u{1F42D}", "\u{1F439}", "\u{1F430}", "\u{1F98A}", "\u{1F43B}", "\u{1F43C}"].map((char) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "qt-emoji-picker-cell", children: char }, char)) })
2008
+ ] })
2009
+ ] })
2010
+ ] }),
2011
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { marginTop: "1rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "qt-popover qt-emoji-picker", children: [
2012
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("input", { className: "qt-input qt-emoji-picker-search", placeholder: "Search emoji\u2026", readOnly: true }),
2013
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "qt-emoji-picker-message", children: "Couldn't load emoji" })
2014
+ ] }) })
2015
+ ] }),
1949
2016
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("section", { children: [
1950
2017
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { style: { fontSize: "1.125rem", fontWeight: 700, marginBottom: "1rem", borderBottom: "1px solid var(--color-border)", paddingBottom: "0.5rem" }, children: "Full Chat Layout" }),
1951
2018
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "qt-chat-layout", style: { height: "24rem", border: "1px solid var(--color-border)", borderRadius: "var(--radius-lg)", display: "flex", flexDirection: "column" }, children: [
@@ -21,7 +21,7 @@ import {
21
21
  ThemeComparison,
22
22
  Typography,
23
23
  Workspace
24
- } from "../chunk-3RAUK5HZ.mjs";
24
+ } from "../chunk-4XVTTTDV.mjs";
25
25
  import "../chunk-WUKYLWAZ.mjs";
26
26
  export {
27
27
  Avatars,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quilltap/theme-storybook",
3
- "version": "1.0.57",
3
+ "version": "1.0.59",
4
4
  "description": "Storybook preset and stories for developing Quilltap theme plugins",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -43,10 +43,10 @@
43
43
  "storybook": ">=8.0.0"
44
44
  },
45
45
  "devDependencies": {
46
- "@storybook/react": "^10.5.5",
47
- "@types/react": "^19.2.17",
46
+ "@storybook/react": "^10.5.8",
47
+ "@types/react": "^19.2.18",
48
48
  "react": "^19.2.8",
49
- "storybook": "^10.5.5",
49
+ "storybook": "^10.5.8",
50
50
  "tsup": "^8.5.1",
51
51
  "typescript": "^5.9.3"
52
52
  },
@@ -1861,6 +1861,13 @@
1861
1861
  line-height: 1;
1862
1862
  }
1863
1863
 
1864
+ /* Emoji picker button - the glyph carries the meaning, so size it like the
1865
+ arrow-glyph buttons rather than the text-label ones */
1866
+ .qt-formatting-button-emoji {
1867
+ font-size: 1.05rem;
1868
+ line-height: 1;
1869
+ }
1870
+
1864
1871
  .qt-formatting-button-source {
1865
1872
  display: flex;
1866
1873
  align-items: center;
@@ -1881,6 +1888,216 @@
1881
1888
  border-color: color-mix(in srgb, var(--color-primary) 85%, black);
1882
1889
  }
1883
1890
 
1891
+ /* ==========================================================================
1892
+ POPOVER / DROPDOWN
1893
+ ========================================================================== */
1894
+
1895
+ /* All popover variants share these base styles */
1896
+ .qt-popover,
1897
+ .qt-dropdown {
1898
+ z-index: 50;
1899
+ min-width: 8rem;
1900
+ padding: 1rem;
1901
+ border-radius: var(--qt-popover-radius);
1902
+ background: var(--qt-popover-bg);
1903
+ color: var(--qt-popover-fg);
1904
+ border: 1px solid var(--qt-popover-border);
1905
+ box-shadow: var(--qt-popover-shadow);
1906
+ }
1907
+
1908
+ /* Dropdown menu - popover variant for menus */
1909
+ .qt-dropdown {
1910
+ padding: 0.25rem;
1911
+ }
1912
+
1913
+ /* Dropdown menu item */
1914
+ .qt-dropdown-item {
1915
+ display: flex;
1916
+ align-items: center;
1917
+ gap: 0.5rem;
1918
+ padding: 0.5rem 0.75rem;
1919
+ font-size: 0.875rem;
1920
+ line-height: 1.25rem;
1921
+ border-radius: var(--radius-md);
1922
+ cursor: pointer;
1923
+ transition: color 150ms, background-color 150ms, border-color 150ms;
1924
+ }
1925
+
1926
+ .qt-dropdown-item:hover {
1927
+ background: var(--color-muted);
1928
+ }
1929
+
1930
+ .qt-dropdown-item:focus {
1931
+ background: var(--color-muted);
1932
+ outline: none;
1933
+ }
1934
+
1935
+ /* ==========================================================================
1936
+ TYPEAHEAD MENU
1937
+ ==========================================================================
1938
+ The composer's inline typeahead popover — emoji today (`:name`), and the
1939
+ `@` / `#` / `/` menus of Layer 2 tomorrow. Positioned by Lexical's anchor
1940
+ element, so these classes own appearance only, never placement. */
1941
+
1942
+ .qt-typeahead-menu {
1943
+ position: absolute;
1944
+ z-index: 50;
1945
+ overflow-y: auto;
1946
+ padding: 0.25rem;
1947
+ /* Anchored to the caret element Lexical positions for us. `top: 100%` clears
1948
+ the caret's own line rather than covering it. */
1949
+ top: 100%;
1950
+ left: 0;
1951
+ min-width: 14rem;
1952
+ max-width: 22rem;
1953
+ max-height: 17rem;
1954
+ border-radius: var(--qt-popover-radius);
1955
+ background: var(--qt-popover-bg);
1956
+ color: var(--qt-popover-fg);
1957
+ border: 1px solid var(--qt-popover-border);
1958
+ box-shadow: var(--qt-popover-shadow);
1959
+ }
1960
+
1961
+ /* Flipped by MenuPortal when the viewport has no room. The Salon composer lives
1962
+ at the bottom of the window, so `above` is the COMMON case there — without it
1963
+ the menu is clipped every time. */
1964
+ .qt-typeahead-menu[data-placement='above'] {
1965
+ top: auto;
1966
+ bottom: 100%;
1967
+ }
1968
+
1969
+ .qt-typeahead-menu[data-align='right'] {
1970
+ left: auto;
1971
+ right: 0;
1972
+ }
1973
+
1974
+ .qt-typeahead-option {
1975
+ display: flex;
1976
+ align-items: center;
1977
+ gap: 0.5rem;
1978
+ padding: 0.375rem 0.5rem;
1979
+ font-size: 0.875rem;
1980
+ line-height: 1.25rem;
1981
+ border-radius: var(--radius-md);
1982
+ cursor: pointer;
1983
+ transition: color 150ms, background-color 150ms, border-color 150ms;
1984
+ }
1985
+
1986
+ /* Keyboard highlight. Focus never leaves the composer, so the active row is
1987
+ marked here rather than by :focus. */
1988
+ .qt-typeahead-option-active {
1989
+ background: var(--color-muted);
1990
+ }
1991
+
1992
+ .qt-typeahead-option:hover {
1993
+ background: var(--color-muted);
1994
+ }
1995
+
1996
+ .qt-typeahead-option-glyph {
1997
+ flex: none;
1998
+ font-size: 1.15rem;
1999
+ line-height: 1;
2000
+ }
2001
+
2002
+ .qt-typeahead-option-label {
2003
+ flex: 1 1 0%;
2004
+ overflow: hidden;
2005
+ text-overflow: ellipsis;
2006
+ white-space: nowrap;
2007
+ }
2008
+
2009
+ .qt-typeahead-option-detail {
2010
+ flex: none;
2011
+ font-size: 0.75rem;
2012
+ line-height: 1rem;
2013
+ color: var(--qt-text-secondary-fg, var(--qt-text-secondary));
2014
+ }
2015
+
2016
+ .qt-typeahead-empty {
2017
+ padding: 0.75rem 0.5rem;
2018
+ text-align: center;
2019
+ font-size: 0.875rem;
2020
+ line-height: 1.25rem;
2021
+ color: var(--qt-text-secondary-fg, var(--qt-text-secondary));
2022
+ }
2023
+
2024
+ /* ==========================================================================
2025
+ EMOJI PICKER
2026
+ ==========================================================================
2027
+ The toolbar's browsable half of the composer emoji feature. The popover
2028
+ shell itself is `.qt-popover`; these classes shape what sits inside it. */
2029
+
2030
+ .qt-emoji-picker {
2031
+ display: flex;
2032
+ flex-direction: column;
2033
+ gap: 0.5rem;
2034
+ padding: 0.5rem;
2035
+ width: 20rem;
2036
+ }
2037
+
2038
+ .qt-emoji-picker-search {
2039
+ width: 100%;
2040
+ }
2041
+
2042
+ /* The scrolling body. A fixed height keeps the popover from growing to the
2043
+ full 1,900-emoji set when the query is empty. */
2044
+ .qt-emoji-picker-grid {
2045
+ overflow-y: auto;
2046
+ overflow-x: hidden;
2047
+ max-height: 16rem;
2048
+ }
2049
+
2050
+ /* Sticky group headers, so the category stays legible while scrolling. */
2051
+ .qt-emoji-picker-group-header {
2052
+ position: sticky;
2053
+ top: 0;
2054
+ z-index: 10;
2055
+ padding: 0.25rem;
2056
+ font-size: 0.75rem;
2057
+ line-height: 1rem;
2058
+ font-weight: 600;
2059
+ background: var(--qt-popover-bg);
2060
+ color: var(--qt-text-secondary-fg, var(--qt-text-secondary));
2061
+ }
2062
+
2063
+ .qt-emoji-picker-row {
2064
+ display: flex;
2065
+ }
2066
+
2067
+ .qt-emoji-picker-cell {
2068
+ display: flex;
2069
+ align-items: center;
2070
+ justify-content: center;
2071
+ border-radius: 0.25rem;
2072
+ cursor: pointer;
2073
+ transition: color 150ms, background-color 150ms, border-color 150ms;
2074
+ width: 2.25rem;
2075
+ height: 2.25rem;
2076
+ font-size: 1.25rem;
2077
+ line-height: 1;
2078
+ background: transparent;
2079
+ border: none;
2080
+ }
2081
+
2082
+ .qt-emoji-picker-cell:hover {
2083
+ background: var(--qt-content-bg-hover);
2084
+ }
2085
+
2086
+ .qt-emoji-picker-cell:focus-visible {
2087
+ outline: 2px solid var(--color-primary);
2088
+ outline-offset: -2px;
2089
+ }
2090
+
2091
+ /* Loading / failure / empty states. Failure is silent and non-blocking by
2092
+ design — typing is never impeded, the picker just says so. */
2093
+ .qt-emoji-picker-message {
2094
+ padding: 1.5rem 0.5rem;
2095
+ text-align: center;
2096
+ font-size: 0.875rem;
2097
+ line-height: 1.25rem;
2098
+ color: var(--qt-text-secondary-fg, var(--qt-text-secondary));
2099
+ }
2100
+
1884
2101
  /* Lexical table styling */
1885
2102
  .qt-lexical-contenteditable table {
1886
2103
  width: 100%;
@@ -594,6 +594,100 @@ export const Chat: React.FC = () => {
594
594
  </div>
595
595
  </section>
596
596
 
597
+ {/* Composer Typeahead Menu */}
598
+ <section style={{ marginBottom: '2rem' }}>
599
+ <h3 style={{ fontSize: '1.125rem', fontWeight: 700, marginBottom: '1rem', borderBottom: '1px solid var(--color-border)', paddingBottom: '0.5rem' }}>
600
+ Composer Typeahead Menu
601
+ </h3>
602
+ <p style={{ marginBottom: '1rem', color: 'var(--color-muted-foreground)', fontSize: '0.875rem' }}>
603
+ The inline `:name` emoji menu. The composer keeps focus throughout, so the
604
+ highlighted row is marked by <code>.qt-typeahead-option-active</code> rather than
605
+ by <code>:focus</code>.
606
+ </p>
607
+ <p style={{ marginBottom: '1rem', color: 'var(--color-muted-foreground)', fontSize: '0.875rem' }}>
608
+ The menu positions itself against the caret element Lexical creates, so it is
609
+ <code>position: absolute</code> — these samples sit in a relatively-positioned host to
610
+ stand in for that anchor.
611
+ </p>
612
+ <div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
613
+ <div style={{ position: 'relative', width: '22rem', height: '9rem' }}>
614
+ <div className="qt-typeahead-menu" style={{ top: 0 }}>
615
+ <div className="qt-typeahead-option qt-typeahead-option-active">
616
+ <span className="qt-typeahead-option-glyph">😄</span>
617
+ <span className="qt-typeahead-option-label">grinning face with smiling eyes</span>
618
+ <span className="qt-typeahead-option-detail">:smile:</span>
619
+ </div>
620
+ <div className="qt-typeahead-option">
621
+ <span className="qt-typeahead-option-glyph">😃</span>
622
+ <span className="qt-typeahead-option-label">grinning face with big eyes</span>
623
+ <span className="qt-typeahead-option-detail">:smiley:</span>
624
+ </div>
625
+ <div className="qt-typeahead-option">
626
+ <span className="qt-typeahead-option-glyph">😺</span>
627
+ <span className="qt-typeahead-option-label">grinning cat</span>
628
+ <span className="qt-typeahead-option-detail">:smile_cat:</span>
629
+ </div>
630
+ </div>
631
+ </div>
632
+ <div style={{ position: 'relative', width: '15rem', height: '9rem' }}>
633
+ <div className="qt-typeahead-menu" style={{ top: 0 }}>
634
+ <div className="qt-typeahead-empty">No emoji found</div>
635
+ </div>
636
+ </div>
637
+ </div>
638
+ </section>
639
+
640
+ {/* Emoji Picker */}
641
+ <section style={{ marginBottom: '2rem' }}>
642
+ <h3 style={{ fontSize: '1.125rem', fontWeight: 700, marginBottom: '1rem', borderBottom: '1px solid var(--color-border)', paddingBottom: '0.5rem' }}>
643
+ Emoji Picker
644
+ </h3>
645
+ <p style={{ marginBottom: '1rem', color: 'var(--color-muted-foreground)', fontSize: '0.875rem' }}>
646
+ Opened from the formatting toolbar&apos;s <code>.qt-formatting-button-emoji</code>.
647
+ The shell is <code>.qt-popover</code>; group headers stick while the grid scrolls.
648
+ </p>
649
+ <div className="qt-popover qt-emoji-picker">
650
+ <input className="qt-input qt-emoji-picker-search" placeholder="Search emoji…" readOnly />
651
+ <div className="qt-emoji-picker-grid">
652
+ <div>
653
+ <div className="qt-emoji-picker-group-header">Recently used</div>
654
+ <div className="qt-emoji-picker-row">
655
+ {['😄', '🎉', '🚀', '👍️'].map((char) => (
656
+ <button key={char} type="button" className="qt-emoji-picker-cell">{char}</button>
657
+ ))}
658
+ </div>
659
+ </div>
660
+ <div>
661
+ <div className="qt-emoji-picker-group-header">Smileys &amp; Emotion</div>
662
+ <div className="qt-emoji-picker-row">
663
+ {['😀', '😃', '😄', '😁', '😆', '😅', '🤣', '😂'].map((char) => (
664
+ <button key={char} type="button" className="qt-emoji-picker-cell">{char}</button>
665
+ ))}
666
+ </div>
667
+ <div className="qt-emoji-picker-row">
668
+ {['🙂', '🙃', '😉', '😊', '😇', '🥰', '😍', '🤩'].map((char) => (
669
+ <button key={char} type="button" className="qt-emoji-picker-cell">{char}</button>
670
+ ))}
671
+ </div>
672
+ </div>
673
+ <div>
674
+ <div className="qt-emoji-picker-group-header">Animals &amp; Nature</div>
675
+ <div className="qt-emoji-picker-row">
676
+ {['🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼'].map((char) => (
677
+ <button key={char} type="button" className="qt-emoji-picker-cell">{char}</button>
678
+ ))}
679
+ </div>
680
+ </div>
681
+ </div>
682
+ </div>
683
+ <div style={{ marginTop: '1rem' }}>
684
+ <div className="qt-popover qt-emoji-picker">
685
+ <input className="qt-input qt-emoji-picker-search" placeholder="Search emoji…" readOnly />
686
+ <div className="qt-emoji-picker-message">Couldn&apos;t load emoji</div>
687
+ </div>
688
+ </div>
689
+ </section>
690
+
597
691
  {/* Full Chat Layout Preview */}
598
692
  <section>
599
693
  <h3 style={{ fontSize: '1.125rem', fontWeight: 700, marginBottom: '1rem', borderBottom: '1px solid var(--color-border)', paddingBottom: '0.5rem' }}>