@mrrocket/sdk 0.4.0 → 0.9.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +137 -8
  3. package/dist/api-client.d.ts +213 -0
  4. package/dist/api-client.js +67 -0
  5. package/dist/api-client.js.map +1 -0
  6. package/dist/api.d.ts +9 -109
  7. package/dist/api.js +7 -62
  8. package/dist/api.js.map +1 -1
  9. package/dist/browser/mrrocket-flows.js +64 -0
  10. package/dist/browser/mrrocket-messenger.js +313 -0
  11. package/dist/browser/mrrocket-push.js +1 -0
  12. package/dist/browser/mrrocket-replay.js +3 -3
  13. package/dist/browser/mrrocket-sw.js +1 -0
  14. package/dist/browser/mrrocket.js +2 -1
  15. package/dist/browser.d.ts +3 -2
  16. package/dist/browser.js +6 -1
  17. package/dist/browser.js.map +1 -1
  18. package/dist/capture.d.ts +1 -13
  19. package/dist/capture.js +1 -22
  20. package/dist/capture.js.map +1 -1
  21. package/dist/client.d.ts +36 -1
  22. package/dist/client.js +161 -6
  23. package/dist/client.js.map +1 -1
  24. package/dist/context.d.ts +10 -0
  25. package/dist/context.js.map +1 -1
  26. package/dist/core.d.ts +64 -3
  27. package/dist/core.js +54 -2
  28. package/dist/core.js.map +1 -1
  29. package/dist/feedback.d.ts +19 -4
  30. package/dist/feedback.js +655 -190
  31. package/dist/feedback.js.map +1 -1
  32. package/dist/flows-ui.d.ts +23 -0
  33. package/dist/flows-ui.js +384 -0
  34. package/dist/flows-ui.js.map +1 -0
  35. package/dist/flows-web.d.ts +19 -0
  36. package/dist/flows-web.js +42 -0
  37. package/dist/flows-web.js.map +1 -0
  38. package/dist/flows.d.ts +222 -0
  39. package/dist/flows.js +409 -0
  40. package/dist/flows.js.map +1 -0
  41. package/dist/index.d.ts +5 -2
  42. package/dist/index.js +13 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/messenger-ui.d.ts +47 -0
  45. package/dist/messenger-ui.js +583 -0
  46. package/dist/messenger-ui.js.map +1 -0
  47. package/dist/messenger.d.ts +37 -0
  48. package/dist/messenger.js +69 -0
  49. package/dist/messenger.js.map +1 -0
  50. package/dist/proxy.d.ts +34 -1
  51. package/dist/proxy.js +14 -1
  52. package/dist/proxy.js.map +1 -1
  53. package/dist/push-web.d.ts +43 -0
  54. package/dist/push-web.js +81 -0
  55. package/dist/push-web.js.map +1 -0
  56. package/dist/rage.d.ts +14 -0
  57. package/dist/rage.js +27 -0
  58. package/dist/rage.js.map +1 -0
  59. package/dist/react-native/index.d.cts +640 -3
  60. package/dist/react-native/index.d.ts +640 -3
  61. package/dist/react-native/index.js +2015 -34
  62. package/dist/react-native/index.js.map +1 -1
  63. package/dist/styles-css.d.ts +6 -0
  64. package/dist/styles-css.js +327 -0
  65. package/dist/styles-css.js.map +1 -0
  66. package/dist/styles.d.ts +1 -2
  67. package/dist/styles.js +3 -134
  68. package/dist/styles.js.map +1 -1
  69. package/dist/ui.d.ts +3 -0
  70. package/dist/ui.js +12 -0
  71. package/dist/ui.js.map +1 -1
  72. package/package.json +20 -4
package/dist/styles.js CHANGED
@@ -2,142 +2,11 @@
2
2
 
3
3
  // src/styles.ts
4
4
  import { useEffect } from "react";
5
- var STYLE_ID = "ckw-styles";
6
- var CSS = `
7
- .ckw-root{--ckw-accent:#3f76ff;--ckw-accent-ink:#fff;--ckw-bg:#fff;--ckw-bg2:#f7f7f8;--ckw-bg3:#eeeff1;--ckw-fg:#1c1d20;--ckw-fg2:#5d6068;--ckw-fg3:#8b8e96;--ckw-line:#e4e5e8;--ckw-danger:#d92d20;--ckw-shadow:0 8px 30px rgba(16,18,24,.16);
8
- font-size:14px;line-height:1.45;font-family:inherit;color:var(--ckw-fg);box-sizing:border-box;-webkit-font-smoothing:antialiased}
9
- .ckw-root[data-theme=dark]{--ckw-bg:#17181b;--ckw-bg2:#1f2024;--ckw-bg3:#292a30;--ckw-fg:#f1f2f4;--ckw-fg2:#b0b3ba;--ckw-fg3:#7e818a;--ckw-line:#2e2f35;--ckw-shadow:0 8px 30px rgba(0,0,0,.5)}
10
- @media (prefers-color-scheme:dark){.ckw-root[data-theme=auto]{--ckw-bg:#17181b;--ckw-bg2:#1f2024;--ckw-bg3:#292a30;--ckw-fg:#f1f2f4;--ckw-fg2:#b0b3ba;--ckw-fg3:#7e818a;--ckw-line:#2e2f35;--ckw-shadow:0 8px 30px rgba(0,0,0,.5)}}
11
- .ckw-root *,.ckw-root *::before,.ckw-root *::after{box-sizing:border-box}
12
- .ckw-root button,.ckw-root input,.ckw-root select,.ckw-root textarea{font:inherit;color:inherit;margin:0}
13
- .ckw-root button{cursor:pointer}
14
- .ckw-root :focus-visible{outline:2px solid var(--ckw-accent);outline-offset:2px}
15
- .ckw-board{width:100%;max-width:760px;margin:0 auto;background:var(--ckw-bg);min-width:0}
16
- .ckw-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:12px}
17
- .ckw-title{font-size:20px;font-weight:600;margin:0;letter-spacing:-.01em}
18
- .ckw-sub{color:var(--ckw-fg2);margin:2px 0 0;font-size:13px}
19
- .ckw-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;height:32px;padding:0 12px;border-radius:8px;border:1px solid var(--ckw-line);background:var(--ckw-bg);font-weight:500;font-size:13px;white-space:nowrap}
20
- .ckw-btn:hover{background:var(--ckw-bg2)}
21
- .ckw-btn[disabled]{opacity:.55;cursor:not-allowed}
22
- .ckw-btn-primary{background:var(--ckw-accent);border-color:var(--ckw-accent);color:var(--ckw-accent-ink)}
23
- .ckw-btn-primary:hover{background:var(--ckw-accent);filter:brightness(1.08)}
24
- .ckw-btn-ghost{border-color:transparent;background:transparent;color:var(--ckw-fg2)}
25
- .ckw-icon{width:16px;height:16px;flex:none;display:block}
26
- .ckw-tabs{display:flex;gap:4px;border-bottom:1px solid var(--ckw-line);margin-bottom:12px;overflow-x:auto}
27
- .ckw-tab{background:none;border:0;border-bottom:2px solid transparent;padding:8px 10px;margin-bottom:-1px;color:var(--ckw-fg2);font-weight:500;font-size:13px;white-space:nowrap}
28
- .ckw-tab[aria-selected=true]{color:var(--ckw-fg);border-bottom-color:var(--ckw-accent)}
29
- .ckw-tools{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:10px}
30
- .ckw-input,.ckw-select,.ckw-textarea{width:100%;border:1px solid var(--ckw-line);border-radius:8px;background:var(--ckw-bg);padding:7px 10px;font-size:14px;min-height:34px}
31
- .ckw-input::placeholder,.ckw-textarea::placeholder{color:var(--ckw-fg3)}
32
- .ckw-textarea{resize:vertical;min-height:72px}
33
- .ckw-search{flex:1 1 160px;min-width:0}
34
- .ckw-seg{display:inline-flex;border:1px solid var(--ckw-line);border-radius:8px;overflow:hidden}
35
- .ckw-seg button{border:0;background:var(--ckw-bg);padding:0 10px;height:32px;font-size:13px;color:var(--ckw-fg2)}
36
- .ckw-seg button[aria-pressed=true]{background:var(--ckw-bg3);color:var(--ckw-fg);font-weight:500}
37
- .ckw-chips{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}
38
- .ckw-chip{border:1px solid var(--ckw-line);background:var(--ckw-bg);border-radius:999px;padding:3px 10px;font-size:12px;color:var(--ckw-fg2)}
39
- .ckw-chip[aria-pressed=true]{background:var(--ckw-fg);color:var(--ckw-bg);border-color:var(--ckw-fg)}
40
- .ckw-list{list-style:none;margin:0;padding:0;border:1px solid var(--ckw-line);border-radius:8px;overflow:hidden}
41
- .ckw-list>li+li{border-top:1px solid var(--ckw-line)}
42
- .ckw-row{display:flex;gap:12px;align-items:flex-start;padding:10px 12px;background:var(--ckw-bg)}
43
- .ckw-row:hover{background:var(--ckw-bg2)}
44
- .ckw-vote{flex:none;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0;width:44px;min-height:48px;border:1px solid var(--ckw-line);border-radius:8px;background:var(--ckw-bg);font-size:13px;font-weight:600;font-variant-numeric:tabular-nums;padding:4px 0;color:var(--ckw-fg2)}
45
- .ckw-vote:hover{border-color:var(--ckw-accent);color:var(--ckw-accent)}
46
- .ckw-vote[aria-pressed=true]{background:var(--ckw-accent);border-color:var(--ckw-accent);color:var(--ckw-accent-ink)}
47
- .ckw-vote-sm{width:auto;min-height:0;flex-direction:row;gap:4px;padding:2px 8px}
48
- .ckw-main{flex:1;min-width:0;text-align:left}
49
- .ckw-link{display:block;width:100%;text-align:left;background:none;border:0;padding:0;font-weight:600;font-size:14px;color:var(--ckw-fg);overflow-wrap:anywhere}
50
- .ckw-link:hover{color:var(--ckw-accent)}
51
- .ckw-excerpt{margin:2px 0 0;color:var(--ckw-fg2);font-size:13px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
52
- .ckw-meta{display:flex;gap:8px;align-items:center;margin-top:6px;color:var(--ckw-fg3);font-size:12px;flex-wrap:wrap}
53
- .ckw-pill{display:inline-flex;align-items:center;border-radius:999px;padding:1px 8px;font-size:11px;font-weight:600;background:var(--ckw-bg3);color:var(--ckw-fg2)}
54
- .ckw-pill[data-s=planned]{background:rgba(63,118,255,.14);color:#3f76ff}
55
- .ckw-pill[data-s=progress]{background:rgba(245,158,11,.16);color:#b45309}
56
- .ckw-pill[data-s=shipped]{background:rgba(22,163,74,.15);color:#15803d}
57
- .ckw-root[data-theme=dark] .ckw-pill[data-s=progress]{color:#fbbf24}
58
- .ckw-root[data-theme=dark] .ckw-pill[data-s=shipped]{color:#4ade80}
59
- @media (prefers-color-scheme:dark){.ckw-root[data-theme=auto] .ckw-pill[data-s=progress]{color:#fbbf24}.ckw-root[data-theme=auto] .ckw-pill[data-s=shipped]{color:#4ade80}}
60
- .ckw-stat{display:inline-flex;align-items:center;gap:4px}
61
- .ckw-empty{border:1px dashed var(--ckw-line);border-radius:8px;padding:28px 16px;text-align:center;color:var(--ckw-fg2)}
62
- .ckw-error{border:1px solid color-mix(in srgb,var(--ckw-danger) 40%,transparent);background:color-mix(in srgb,var(--ckw-danger) 8%,transparent);color:var(--ckw-danger);border-radius:8px;padding:8px 10px;font-size:13px;display:flex;gap:8px;align-items:center;justify-content:space-between;margin-bottom:10px}
63
- .ckw-skel{height:68px;border-radius:8px;background:var(--ckw-bg2);animation:ckw-sh 1.2s ease-in-out infinite;margin-bottom:6px}
64
- @keyframes ckw-sh{50%{opacity:.5}}
65
- @media (prefers-reduced-motion:reduce){.ckw-skel{animation:none}.ckw-root *{transition:none!important}}
66
- .ckw-form{display:flex;flex-direction:column;gap:10px;border:1px solid var(--ckw-line);border-radius:8px;padding:12px;margin-bottom:12px;background:var(--ckw-bg)}
67
- .ckw-field{display:flex;flex-direction:column;gap:4px}
68
- .ckw-label{font-size:12px;font-weight:600;color:var(--ckw-fg2)}
69
- .ckw-actions{display:flex;gap:8px;justify-content:flex-end}
70
- .ckw-dupes{border:1px solid var(--ckw-line);border-radius:8px;background:var(--ckw-bg2);padding:8px 10px;font-size:13px}
71
- .ckw-dupes p{margin:0 0 4px;color:var(--ckw-fg2);font-size:12px}
72
- .ckw-dupe{display:flex;gap:8px;align-items:center;justify-content:space-between;padding:3px 0}
73
- .ckw-dupe span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
74
- .ckw-cols{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}
75
- @media (max-width:640px){.ckw-cols{grid-template-columns:1fr}}
76
- .ckw-col h3{font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--ckw-fg2);margin:0 0 8px;display:flex;gap:6px;align-items:center}
77
- .ckw-card{border:1px solid var(--ckw-line);border-radius:8px;padding:10px;margin-bottom:8px;background:var(--ckw-bg)}
78
- .ckw-card .ckw-link{margin-bottom:6px}
79
- .ckw-entry{padding:12px 0;border-bottom:1px solid var(--ckw-line)}
80
- .ckw-entry time{font-size:12px;color:var(--ckw-fg3)}
81
- .ckw-entry h3{font-size:15px;margin:2px 0 4px}
82
- .ckw-entry p{margin:0;color:var(--ckw-fg2);white-space:pre-wrap;overflow-wrap:anywhere}
83
- .ckw-back{margin-bottom:8px;padding-left:4px}
84
- .ckw-detail-head{display:flex;gap:12px;align-items:flex-start}
85
- .ckw-detail-head h2{margin:0 0 4px;font-size:18px;overflow-wrap:anywhere}
86
- .ckw-body{white-space:pre-wrap;overflow-wrap:anywhere;color:var(--ckw-fg2);margin:12px 0}
87
- .ckw-h{font-size:13px;font-weight:600;margin:16px 0 8px}
88
- .ckw-comment{padding:8px 0;border-top:1px solid var(--ckw-line)}
89
- .ckw-comment b{font-size:13px}
90
- .ckw-comment p{margin:2px 0 0;white-space:pre-wrap;overflow-wrap:anywhere}
91
- .ckw-team{margin-left:6px;background:var(--ckw-accent);color:var(--ckw-accent-ink);border-radius:4px;padding:0 5px;font-size:10px;font-weight:700;text-transform:uppercase;vertical-align:1px}
92
- .ckw-time{color:var(--ckw-fg3);font-size:12px;margin-left:6px}
93
- .ckw-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
94
- /* Floating widget */
95
- .ckw-fab{position:fixed;z-index:2147483000;bottom:20px;display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 16px;border-radius:999px;border:0;background:var(--ckw-accent);color:var(--ckw-accent-ink);font-weight:600;font-size:14px;box-shadow:var(--ckw-shadow)}
96
- .ckw-fab[data-pos=bottom-right],.ckw-pop[data-pos=bottom-right]{right:20px}
97
- .ckw-fab[data-pos=bottom-left],.ckw-pop[data-pos=bottom-left]{left:20px}
98
- .ckw-pop{position:fixed;z-index:2147483001;bottom:70px;width:380px;max-width:calc(100vw - 24px);max-height:min(560px,calc(100vh - 90px));display:flex;flex-direction:column;background:var(--ckw-bg);border:1px solid var(--ckw-line);border-radius:12px;box-shadow:var(--ckw-shadow);overflow:hidden}
99
- .ckw-pop-head{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--ckw-line);font-weight:600}
100
- .ckw-pop-body{padding:12px;overflow-y:auto;flex:1}
101
- .ckw-pop-foot{padding:8px 12px;border-top:1px solid var(--ckw-line);display:flex;justify-content:space-between;align-items:center;font-size:13px}
102
- .ckw-pop-foot a{color:var(--ckw-accent);text-decoration:none;font-weight:500}
103
- .ckw-pop .ckw-list{border:0;border-radius:0}
104
- .ckw-pop .ckw-row{padding:8px 0}
105
- .ckw-pop .ckw-form{border:0;padding:0;margin:12px 0 0}
106
- @media (max-width:520px){.ckw-pop{left:0!important;right:0!important;bottom:0;width:100%;max-width:100%;border-radius:12px 12px 0 0;max-height:85vh}.ckw-fab{bottom:14px}.ckw-fab[data-pos=bottom-right]{right:14px}.ckw-fab[data-pos=bottom-left]{left:14px}}
107
- /* Survey */
108
- .ckw-survey{position:fixed;z-index:2147483000;left:20px;bottom:20px;width:360px;max-width:calc(100vw - 24px);background:var(--ckw-bg);border:1px solid var(--ckw-line);border-radius:12px;box-shadow:var(--ckw-shadow);padding:14px;animation:ckw-in .2s ease-out}
109
- @keyframes ckw-in{from{opacity:0;transform:translateY(8px)}}
110
- @media (max-width:520px){.ckw-survey{left:0;right:0;bottom:0;width:100%;max-width:100%;border-radius:12px 12px 0 0}}
111
- .ckw-sv-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}
112
- .ckw-dots{display:flex;gap:4px}
113
- .ckw-dot{width:6px;height:6px;border-radius:50%;background:var(--ckw-bg3)}
114
- .ckw-dot[data-on=true]{background:var(--ckw-accent)}
115
- .ckw-q{font-weight:600;font-size:15px;margin:0 0 2px;border:0;padding:0}
116
- .ckw-root .ckw-q:focus-visible{outline:none}
117
- .ckw-qd{color:var(--ckw-fg2);font-size:13px;margin:0 0 10px}
118
- .ckw-scale{display:flex;gap:4px;flex-wrap:wrap}
119
- .ckw-num{flex:1 1 0;min-width:26px;height:34px;border:1px solid var(--ckw-line);background:var(--ckw-bg);border-radius:8px;font-size:13px;font-weight:600;padding:0}
120
- .ckw-num:hover{border-color:var(--ckw-accent)}
121
- .ckw-num[aria-pressed=true]{background:var(--ckw-accent);color:var(--ckw-accent-ink);border-color:var(--ckw-accent)}
122
- .ckw-root .ckw-star{background:none;border:0;padding:2px;color:var(--ckw-bg3)}
123
- .ckw-root .ckw-star[data-on=true]{color:#f5a524}
124
- .ckw-star svg{width:28px;height:28px}
125
- .ckw-lh{display:flex;justify-content:space-between;color:var(--ckw-fg3);font-size:11px;margin-top:4px}
126
- .ckw-opts{display:flex;flex-direction:column;gap:6px}
127
- .ckw-opt{display:flex;gap:8px;align-items:center;border:1px solid var(--ckw-line);border-radius:8px;padding:7px 10px;background:var(--ckw-bg);cursor:pointer}
128
- .ckw-opt:has(input:checked){border-color:var(--ckw-accent);background:color-mix(in srgb,var(--ckw-accent) 8%,transparent)}
129
- .ckw-opt input{accent-color:var(--ckw-accent)}
130
- .ckw-sv-foot{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-top:12px}
131
- .ckw-thanks{text-align:center;padding:14px 0;display:flex;flex-direction:column;align-items:center;gap:8px}
132
- .ckw-tick{width:32px;height:32px;border-radius:50%;background:var(--ckw-accent);color:var(--ckw-accent-ink);display:flex;align-items:center;justify-content:center}
133
- `;
5
+ import { injectStyles } from "./styles-css.js";
6
+ import { CSS } from "./styles-css.js";
134
7
  var useStyles = () => {
135
8
  useEffect(() => {
136
- if (document.getElementById(STYLE_ID)) return;
137
- const el = document.createElement("style");
138
- el.id = STYLE_ID;
139
- el.textContent = CSS;
140
- document.head.appendChild(el);
9
+ injectStyles();
141
10
  }, []);
142
11
  };
143
12
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/styles.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect } from \"react\";\n\nconst STYLE_ID = \"ckw-styles\";\n\n/** The one stylesheet for every Cockpit widget. Everything is scoped under `.ckw-root` and prefixed `ckw-`. */\nexport const CSS = `\n.ckw-root{--ckw-accent:#3f76ff;--ckw-accent-ink:#fff;--ckw-bg:#fff;--ckw-bg2:#f7f7f8;--ckw-bg3:#eeeff1;--ckw-fg:#1c1d20;--ckw-fg2:#5d6068;--ckw-fg3:#8b8e96;--ckw-line:#e4e5e8;--ckw-danger:#d92d20;--ckw-shadow:0 8px 30px rgba(16,18,24,.16);\nfont-size:14px;line-height:1.45;font-family:inherit;color:var(--ckw-fg);box-sizing:border-box;-webkit-font-smoothing:antialiased}\n.ckw-root[data-theme=dark]{--ckw-bg:#17181b;--ckw-bg2:#1f2024;--ckw-bg3:#292a30;--ckw-fg:#f1f2f4;--ckw-fg2:#b0b3ba;--ckw-fg3:#7e818a;--ckw-line:#2e2f35;--ckw-shadow:0 8px 30px rgba(0,0,0,.5)}\n@media (prefers-color-scheme:dark){.ckw-root[data-theme=auto]{--ckw-bg:#17181b;--ckw-bg2:#1f2024;--ckw-bg3:#292a30;--ckw-fg:#f1f2f4;--ckw-fg2:#b0b3ba;--ckw-fg3:#7e818a;--ckw-line:#2e2f35;--ckw-shadow:0 8px 30px rgba(0,0,0,.5)}}\n.ckw-root *,.ckw-root *::before,.ckw-root *::after{box-sizing:border-box}\n.ckw-root button,.ckw-root input,.ckw-root select,.ckw-root textarea{font:inherit;color:inherit;margin:0}\n.ckw-root button{cursor:pointer}\n.ckw-root :focus-visible{outline:2px solid var(--ckw-accent);outline-offset:2px}\n.ckw-board{width:100%;max-width:760px;margin:0 auto;background:var(--ckw-bg);min-width:0}\n.ckw-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:12px}\n.ckw-title{font-size:20px;font-weight:600;margin:0;letter-spacing:-.01em}\n.ckw-sub{color:var(--ckw-fg2);margin:2px 0 0;font-size:13px}\n.ckw-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;height:32px;padding:0 12px;border-radius:8px;border:1px solid var(--ckw-line);background:var(--ckw-bg);font-weight:500;font-size:13px;white-space:nowrap}\n.ckw-btn:hover{background:var(--ckw-bg2)}\n.ckw-btn[disabled]{opacity:.55;cursor:not-allowed}\n.ckw-btn-primary{background:var(--ckw-accent);border-color:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-btn-primary:hover{background:var(--ckw-accent);filter:brightness(1.08)}\n.ckw-btn-ghost{border-color:transparent;background:transparent;color:var(--ckw-fg2)}\n.ckw-icon{width:16px;height:16px;flex:none;display:block}\n.ckw-tabs{display:flex;gap:4px;border-bottom:1px solid var(--ckw-line);margin-bottom:12px;overflow-x:auto}\n.ckw-tab{background:none;border:0;border-bottom:2px solid transparent;padding:8px 10px;margin-bottom:-1px;color:var(--ckw-fg2);font-weight:500;font-size:13px;white-space:nowrap}\n.ckw-tab[aria-selected=true]{color:var(--ckw-fg);border-bottom-color:var(--ckw-accent)}\n.ckw-tools{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:10px}\n.ckw-input,.ckw-select,.ckw-textarea{width:100%;border:1px solid var(--ckw-line);border-radius:8px;background:var(--ckw-bg);padding:7px 10px;font-size:14px;min-height:34px}\n.ckw-input::placeholder,.ckw-textarea::placeholder{color:var(--ckw-fg3)}\n.ckw-textarea{resize:vertical;min-height:72px}\n.ckw-search{flex:1 1 160px;min-width:0}\n.ckw-seg{display:inline-flex;border:1px solid var(--ckw-line);border-radius:8px;overflow:hidden}\n.ckw-seg button{border:0;background:var(--ckw-bg);padding:0 10px;height:32px;font-size:13px;color:var(--ckw-fg2)}\n.ckw-seg button[aria-pressed=true]{background:var(--ckw-bg3);color:var(--ckw-fg);font-weight:500}\n.ckw-chips{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}\n.ckw-chip{border:1px solid var(--ckw-line);background:var(--ckw-bg);border-radius:999px;padding:3px 10px;font-size:12px;color:var(--ckw-fg2)}\n.ckw-chip[aria-pressed=true]{background:var(--ckw-fg);color:var(--ckw-bg);border-color:var(--ckw-fg)}\n.ckw-list{list-style:none;margin:0;padding:0;border:1px solid var(--ckw-line);border-radius:8px;overflow:hidden}\n.ckw-list>li+li{border-top:1px solid var(--ckw-line)}\n.ckw-row{display:flex;gap:12px;align-items:flex-start;padding:10px 12px;background:var(--ckw-bg)}\n.ckw-row:hover{background:var(--ckw-bg2)}\n.ckw-vote{flex:none;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0;width:44px;min-height:48px;border:1px solid var(--ckw-line);border-radius:8px;background:var(--ckw-bg);font-size:13px;font-weight:600;font-variant-numeric:tabular-nums;padding:4px 0;color:var(--ckw-fg2)}\n.ckw-vote:hover{border-color:var(--ckw-accent);color:var(--ckw-accent)}\n.ckw-vote[aria-pressed=true]{background:var(--ckw-accent);border-color:var(--ckw-accent);color:var(--ckw-accent-ink)}\n.ckw-vote-sm{width:auto;min-height:0;flex-direction:row;gap:4px;padding:2px 8px}\n.ckw-main{flex:1;min-width:0;text-align:left}\n.ckw-link{display:block;width:100%;text-align:left;background:none;border:0;padding:0;font-weight:600;font-size:14px;color:var(--ckw-fg);overflow-wrap:anywhere}\n.ckw-link:hover{color:var(--ckw-accent)}\n.ckw-excerpt{margin:2px 0 0;color:var(--ckw-fg2);font-size:13px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}\n.ckw-meta{display:flex;gap:8px;align-items:center;margin-top:6px;color:var(--ckw-fg3);font-size:12px;flex-wrap:wrap}\n.ckw-pill{display:inline-flex;align-items:center;border-radius:999px;padding:1px 8px;font-size:11px;font-weight:600;background:var(--ckw-bg3);color:var(--ckw-fg2)}\n.ckw-pill[data-s=planned]{background:rgba(63,118,255,.14);color:#3f76ff}\n.ckw-pill[data-s=progress]{background:rgba(245,158,11,.16);color:#b45309}\n.ckw-pill[data-s=shipped]{background:rgba(22,163,74,.15);color:#15803d}\n.ckw-root[data-theme=dark] .ckw-pill[data-s=progress]{color:#fbbf24}\n.ckw-root[data-theme=dark] .ckw-pill[data-s=shipped]{color:#4ade80}\n@media (prefers-color-scheme:dark){.ckw-root[data-theme=auto] .ckw-pill[data-s=progress]{color:#fbbf24}.ckw-root[data-theme=auto] .ckw-pill[data-s=shipped]{color:#4ade80}}\n.ckw-stat{display:inline-flex;align-items:center;gap:4px}\n.ckw-empty{border:1px dashed var(--ckw-line);border-radius:8px;padding:28px 16px;text-align:center;color:var(--ckw-fg2)}\n.ckw-error{border:1px solid color-mix(in srgb,var(--ckw-danger) 40%,transparent);background:color-mix(in srgb,var(--ckw-danger) 8%,transparent);color:var(--ckw-danger);border-radius:8px;padding:8px 10px;font-size:13px;display:flex;gap:8px;align-items:center;justify-content:space-between;margin-bottom:10px}\n.ckw-skel{height:68px;border-radius:8px;background:var(--ckw-bg2);animation:ckw-sh 1.2s ease-in-out infinite;margin-bottom:6px}\n@keyframes ckw-sh{50%{opacity:.5}}\n@media (prefers-reduced-motion:reduce){.ckw-skel{animation:none}.ckw-root *{transition:none!important}}\n.ckw-form{display:flex;flex-direction:column;gap:10px;border:1px solid var(--ckw-line);border-radius:8px;padding:12px;margin-bottom:12px;background:var(--ckw-bg)}\n.ckw-field{display:flex;flex-direction:column;gap:4px}\n.ckw-label{font-size:12px;font-weight:600;color:var(--ckw-fg2)}\n.ckw-actions{display:flex;gap:8px;justify-content:flex-end}\n.ckw-dupes{border:1px solid var(--ckw-line);border-radius:8px;background:var(--ckw-bg2);padding:8px 10px;font-size:13px}\n.ckw-dupes p{margin:0 0 4px;color:var(--ckw-fg2);font-size:12px}\n.ckw-dupe{display:flex;gap:8px;align-items:center;justify-content:space-between;padding:3px 0}\n.ckw-dupe span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ckw-cols{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}\n@media (max-width:640px){.ckw-cols{grid-template-columns:1fr}}\n.ckw-col h3{font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--ckw-fg2);margin:0 0 8px;display:flex;gap:6px;align-items:center}\n.ckw-card{border:1px solid var(--ckw-line);border-radius:8px;padding:10px;margin-bottom:8px;background:var(--ckw-bg)}\n.ckw-card .ckw-link{margin-bottom:6px}\n.ckw-entry{padding:12px 0;border-bottom:1px solid var(--ckw-line)}\n.ckw-entry time{font-size:12px;color:var(--ckw-fg3)}\n.ckw-entry h3{font-size:15px;margin:2px 0 4px}\n.ckw-entry p{margin:0;color:var(--ckw-fg2);white-space:pre-wrap;overflow-wrap:anywhere}\n.ckw-back{margin-bottom:8px;padding-left:4px}\n.ckw-detail-head{display:flex;gap:12px;align-items:flex-start}\n.ckw-detail-head h2{margin:0 0 4px;font-size:18px;overflow-wrap:anywhere}\n.ckw-body{white-space:pre-wrap;overflow-wrap:anywhere;color:var(--ckw-fg2);margin:12px 0}\n.ckw-h{font-size:13px;font-weight:600;margin:16px 0 8px}\n.ckw-comment{padding:8px 0;border-top:1px solid var(--ckw-line)}\n.ckw-comment b{font-size:13px}\n.ckw-comment p{margin:2px 0 0;white-space:pre-wrap;overflow-wrap:anywhere}\n.ckw-team{margin-left:6px;background:var(--ckw-accent);color:var(--ckw-accent-ink);border-radius:4px;padding:0 5px;font-size:10px;font-weight:700;text-transform:uppercase;vertical-align:1px}\n.ckw-time{color:var(--ckw-fg3);font-size:12px;margin-left:6px}\n.ckw-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}\n/* Floating widget */\n.ckw-fab{position:fixed;z-index:2147483000;bottom:20px;display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 16px;border-radius:999px;border:0;background:var(--ckw-accent);color:var(--ckw-accent-ink);font-weight:600;font-size:14px;box-shadow:var(--ckw-shadow)}\n.ckw-fab[data-pos=bottom-right],.ckw-pop[data-pos=bottom-right]{right:20px}\n.ckw-fab[data-pos=bottom-left],.ckw-pop[data-pos=bottom-left]{left:20px}\n.ckw-pop{position:fixed;z-index:2147483001;bottom:70px;width:380px;max-width:calc(100vw - 24px);max-height:min(560px,calc(100vh - 90px));display:flex;flex-direction:column;background:var(--ckw-bg);border:1px solid var(--ckw-line);border-radius:12px;box-shadow:var(--ckw-shadow);overflow:hidden}\n.ckw-pop-head{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--ckw-line);font-weight:600}\n.ckw-pop-body{padding:12px;overflow-y:auto;flex:1}\n.ckw-pop-foot{padding:8px 12px;border-top:1px solid var(--ckw-line);display:flex;justify-content:space-between;align-items:center;font-size:13px}\n.ckw-pop-foot a{color:var(--ckw-accent);text-decoration:none;font-weight:500}\n.ckw-pop .ckw-list{border:0;border-radius:0}\n.ckw-pop .ckw-row{padding:8px 0}\n.ckw-pop .ckw-form{border:0;padding:0;margin:12px 0 0}\n@media (max-width:520px){.ckw-pop{left:0!important;right:0!important;bottom:0;width:100%;max-width:100%;border-radius:12px 12px 0 0;max-height:85vh}.ckw-fab{bottom:14px}.ckw-fab[data-pos=bottom-right]{right:14px}.ckw-fab[data-pos=bottom-left]{left:14px}}\n/* Survey */\n.ckw-survey{position:fixed;z-index:2147483000;left:20px;bottom:20px;width:360px;max-width:calc(100vw - 24px);background:var(--ckw-bg);border:1px solid var(--ckw-line);border-radius:12px;box-shadow:var(--ckw-shadow);padding:14px;animation:ckw-in .2s ease-out}\n@keyframes ckw-in{from{opacity:0;transform:translateY(8px)}}\n@media (max-width:520px){.ckw-survey{left:0;right:0;bottom:0;width:100%;max-width:100%;border-radius:12px 12px 0 0}}\n.ckw-sv-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}\n.ckw-dots{display:flex;gap:4px}\n.ckw-dot{width:6px;height:6px;border-radius:50%;background:var(--ckw-bg3)}\n.ckw-dot[data-on=true]{background:var(--ckw-accent)}\n.ckw-q{font-weight:600;font-size:15px;margin:0 0 2px;border:0;padding:0}\n.ckw-root .ckw-q:focus-visible{outline:none}\n.ckw-qd{color:var(--ckw-fg2);font-size:13px;margin:0 0 10px}\n.ckw-scale{display:flex;gap:4px;flex-wrap:wrap}\n.ckw-num{flex:1 1 0;min-width:26px;height:34px;border:1px solid var(--ckw-line);background:var(--ckw-bg);border-radius:8px;font-size:13px;font-weight:600;padding:0}\n.ckw-num:hover{border-color:var(--ckw-accent)}\n.ckw-num[aria-pressed=true]{background:var(--ckw-accent);color:var(--ckw-accent-ink);border-color:var(--ckw-accent)}\n.ckw-root .ckw-star{background:none;border:0;padding:2px;color:var(--ckw-bg3)}\n.ckw-root .ckw-star[data-on=true]{color:#f5a524}\n.ckw-star svg{width:28px;height:28px}\n.ckw-lh{display:flex;justify-content:space-between;color:var(--ckw-fg3);font-size:11px;margin-top:4px}\n.ckw-opts{display:flex;flex-direction:column;gap:6px}\n.ckw-opt{display:flex;gap:8px;align-items:center;border:1px solid var(--ckw-line);border-radius:8px;padding:7px 10px;background:var(--ckw-bg);cursor:pointer}\n.ckw-opt:has(input:checked){border-color:var(--ckw-accent);background:color-mix(in srgb,var(--ckw-accent) 8%,transparent)}\n.ckw-opt input{accent-color:var(--ckw-accent)}\n.ckw-sv-foot{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-top:12px}\n.ckw-thanks{text-align:center;padding:14px 0;display:flex;flex-direction:column;align-items:center;gap:8px}\n.ckw-tick{width:32px;height:32px;border-radius:50%;background:var(--ckw-accent);color:var(--ckw-accent-ink);display:flex;align-items:center;justify-content:center}\n`;\n\n/** Puts the stylesheet into the page once, however many widgets mount. */\nexport const useStyles = () => {\n useEffect(() => {\n if (document.getElementById(STYLE_ID)) return;\n const el = document.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = CSS;\n document.head.appendChild(el);\n }, []);\n};\n"],"mappings":";;;AAEA,SAAS,iBAAiB;AAE1B,IAAM,WAAW;AAGV,IAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkIZ,IAAM,YAAY,MAAM;AAC3B,YAAU,MAAM;AACZ,QAAI,SAAS,eAAe,QAAQ,EAAG;AACvC,UAAM,KAAK,SAAS,cAAc,OAAO;AACzC,OAAG,KAAK;AACR,OAAG,cAAc;AACjB,aAAS,KAAK,YAAY,EAAE;AAAA,EAChC,GAAG,CAAC,CAAC;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/styles.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect } from \"react\";\nimport { injectStyles } from \"./styles-css\";\n\nexport { CSS } from \"./styles-css\";\n\n/** Puts the stylesheet into the page once, however many widgets mount. */\nexport const useStyles = () => {\n useEffect(() => {\n injectStyles();\n }, []);\n};\n"],"mappings":";;;AAEA,SAAS,iBAAiB;AAC1B,SAAS,oBAAoB;AAE7B,SAAS,WAAW;AAGb,IAAM,YAAY,MAAM;AAC3B,YAAU,MAAM;AACZ,iBAAa;AAAA,EACjB,GAAG,CAAC,CAAC;AACT;","names":[]}
package/dist/ui.d.ts CHANGED
@@ -13,6 +13,9 @@ export declare const Plus: () => import("react").JSX.Element;
13
13
  export declare const Cross: () => import("react").JSX.Element;
14
14
  export declare const Check: () => import("react").JSX.Element;
15
15
  export declare const Chat: () => import("react").JSX.Element;
16
+ export declare const Search: () => import("react").JSX.Element;
17
+ export declare const Sun: () => import("react").JSX.Element;
18
+ export declare const Moon: () => import("react").JSX.Element;
16
19
  export declare const Star: () => import("react").JSX.Element;
17
20
  export declare const StatusPill: ({ status }: {
18
21
  status: PostStatus;
package/dist/ui.js CHANGED
@@ -15,6 +15,15 @@ var Plus = () => /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path"
15
15
  var Cross = () => /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "m4 4 8 8M12 4l-8 8" }) });
16
16
  var Check = () => /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "m3 8.5 3.2 3.2L13 4.8" }) });
17
17
  var Chat = () => /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "M2.5 4.5a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v4.5a2 2 0 0 1-2 2H7l-3 2.5V11a2 2 0 0 1-1.5-2z" }) });
18
+ var Search = () => /* @__PURE__ */ jsxs(Svg, { children: [
19
+ /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "4.5" }),
20
+ /* @__PURE__ */ jsx("path", { d: "m10.5 10.5 3 3" })
21
+ ] });
22
+ var Sun = () => /* @__PURE__ */ jsxs(Svg, { children: [
23
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "3" }),
24
+ /* @__PURE__ */ jsx("path", { d: "M8 1.5v1.5M8 13v1.5M1.5 8H3M13 8h1.5M3.4 3.4l1 1M11.6 11.6l1 1M3.4 12.6l1-1M11.6 4.4l1-1" })
25
+ ] });
26
+ var Moon = () => /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "M13.5 9.5A5.5 5.5 0 0 1 6.5 2.5a5.5 5.5 0 1 0 7 7z" }) });
18
27
  var Star = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "m12 2.8 2.8 5.8 6.4.9-4.6 4.5 1.1 6.3L12 17.2l-5.7 3.1 1.1-6.3L2.8 9.5l6.4-.9z" }) });
19
28
  var StatusPill = ({ status }) => /* @__PURE__ */ jsx("span", { className: "ckw-pill", "data-s": status, children: STATUS_LABEL[status] ?? status });
20
29
  var VoteButton = ({ post, onVote, small }) => /* @__PURE__ */ jsxs(
@@ -65,10 +74,13 @@ export {
65
74
  Check,
66
75
  ChevronUp,
67
76
  Cross,
77
+ Moon,
68
78
  Plus,
69
79
  Root,
80
+ Search,
70
81
  Star,
71
82
  StatusPill,
83
+ Sun,
72
84
  VoteButton,
73
85
  similarPosts,
74
86
  timeAgo,
package/dist/ui.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ui.tsx"],"sourcesContent":["\"use client\";\n\nimport { type ReactNode, useEffect, useRef } from \"react\";\nimport { type FeedbackPost, type PostStatus, STATUS_LABEL } from \"./api\";\nimport { useStyles } from \"./styles\";\n\nexport type Theme = \"light\" | \"dark\" | \"auto\";\n\n/** The scoped wrapper every widget renders inside. Carries the accent and the theme. */\nexport const Root = ({ theme = \"auto\", accent = \"#3f76ff\", className, children }: { theme?: Theme; accent?: string; className?: string; children: ReactNode }) => {\n useStyles();\n return (\n <div className={`ckw-root${className ? ` ${className}` : \"\"}`} data-theme={theme} style={{ [\"--ckw-accent\" as string]: accent }}>\n {children}\n </div>\n );\n};\n\nconst Svg = ({ children, size = 16 }: { children: ReactNode; size?: number }) => (\n <svg className=\"ckw-icon\" width={size} height={size} viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n {children}\n </svg>\n);\n\nexport const ChevronUp = () => (\n <Svg>\n <path d=\"M3.5 10 8 5.5 12.5 10\" />\n </Svg>\n);\nexport const Plus = () => (\n <Svg>\n <path d=\"M8 3v10M3 8h10\" />\n </Svg>\n);\nexport const Cross = () => (\n <Svg>\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </Svg>\n);\nexport const Check = () => (\n <Svg>\n <path d=\"m3 8.5 3.2 3.2L13 4.8\" />\n </Svg>\n);\nexport const Chat = () => (\n <Svg>\n <path d=\"M2.5 4.5a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v4.5a2 2 0 0 1-2 2H7l-3 2.5V11a2 2 0 0 1-1.5-2z\" />\n </Svg>\n);\nexport const Star = () => (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"m12 2.8 2.8 5.8 6.4.9-4.6 4.5 1.1 6.3L12 17.2l-5.7 3.1 1.1-6.3L2.8 9.5l6.4-.9z\" />\n </svg>\n);\n\nexport const StatusPill = ({ status }: { status: PostStatus }) => (\n <span className=\"ckw-pill\" data-s={status}>\n {STATUS_LABEL[status] ?? status}\n </span>\n);\n\nexport const VoteButton = ({ post, onVote, small }: { post: FeedbackPost; onVote: (p: FeedbackPost) => void; small?: boolean }) => (\n <button\n type=\"button\"\n className={`ckw-vote${small ? \" ckw-vote-sm\" : \"\"}`}\n aria-pressed={post.voted}\n aria-label={`${post.voted ? \"Remove your vote from\" : \"Upvote\"} ${post.title}. ${post.votes} votes`}\n onClick={() => onVote(post)}\n >\n <ChevronUp />\n <span>{post.votes}</span>\n </button>\n);\n\nexport const timeAgo = (iso: string) => {\n const t = new Date(iso).getTime();\n if (Number.isNaN(t)) return \"\";\n const d = Math.max(0, Date.now() - t) / 1000;\n if (d < 60) return \"just now\";\n if (d < 3600) return `${Math.floor(d / 60)}m ago`;\n if (d < 86400) return `${Math.floor(d / 3600)}h ago`;\n if (d < 86400 * 30) return `${Math.floor(d / 86400)}d ago`;\n return new Date(iso).toLocaleDateString(undefined, { month: \"short\", day: \"numeric\", year: \"numeric\" });\n};\n\n/** Calls `onClose` on Esc while mounted. */\nexport const useEscape = (onClose: () => void) => {\n const ref = useRef(onClose);\n ref.current = onClose;\n useEffect(() => {\n const h = (e: KeyboardEvent) => e.key === \"Escape\" && ref.current();\n document.addEventListener(\"keydown\", h);\n return () => document.removeEventListener(\"keydown\", h);\n }, []);\n};\n\n/** Words of a title, lowercased, without filler. */\nconst words = (s: string) =>\n s\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((w) => w.length > 2 && ![\"the\", \"and\", \"for\", \"with\", \"add\", \"can\", \"please\", \"want\", \"would\", \"our\", \"you\", \"have\"].includes(w));\n\n/** Up to three existing posts that share words with what the visitor is typing. */\nexport const similarPosts = (title: string, posts: FeedbackPost[]) => {\n const mine = words(title);\n if (mine.length === 0 || title.trim().length < 4) return [];\n return posts\n .map((p) => {\n const theirs = new Set(words(`${p.title} ${p.body}`.slice(0, 300)));\n const hit = mine.filter((w) => theirs.has(w) || [...theirs].some((t) => t.startsWith(w) || w.startsWith(t))).length;\n return { p, score: hit / mine.length };\n })\n .filter((x) => x.score >= 0.5)\n .sort((a, b) => b.score - a.score || b.p.votes - a.p.votes)\n .slice(0, 3)\n .map((x) => x.p);\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/ui.tsx"],"sourcesContent":["\"use client\";\n\nimport { type ReactNode, useEffect, useRef } from \"react\";\nimport { type FeedbackPost, type PostStatus, STATUS_LABEL } from \"./api\";\nimport { useStyles } from \"./styles\";\n\nexport type Theme = \"light\" | \"dark\" | \"auto\";\n\n/** The scoped wrapper every widget renders inside. Carries the accent and the theme. */\nexport const Root = ({ theme = \"auto\", accent = \"#3f76ff\", className, children }: { theme?: Theme; accent?: string; className?: string; children: ReactNode }) => {\n useStyles();\n return (\n <div className={`ckw-root${className ? ` ${className}` : \"\"}`} data-theme={theme} style={{ [\"--ckw-accent\" as string]: accent }}>\n {children}\n </div>\n );\n};\n\nconst Svg = ({ children, size = 16 }: { children: ReactNode; size?: number }) => (\n <svg className=\"ckw-icon\" width={size} height={size} viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n {children}\n </svg>\n);\n\nexport const ChevronUp = () => (\n <Svg>\n <path d=\"M3.5 10 8 5.5 12.5 10\" />\n </Svg>\n);\nexport const Plus = () => (\n <Svg>\n <path d=\"M8 3v10M3 8h10\" />\n </Svg>\n);\nexport const Cross = () => (\n <Svg>\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </Svg>\n);\nexport const Check = () => (\n <Svg>\n <path d=\"m3 8.5 3.2 3.2L13 4.8\" />\n </Svg>\n);\nexport const Chat = () => (\n <Svg>\n <path d=\"M2.5 4.5a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v4.5a2 2 0 0 1-2 2H7l-3 2.5V11a2 2 0 0 1-1.5-2z\" />\n </Svg>\n);\nexport const Search = () => (\n <Svg>\n <circle cx=\"7\" cy=\"7\" r=\"4.5\" />\n <path d=\"m10.5 10.5 3 3\" />\n </Svg>\n);\nexport const Sun = () => (\n <Svg>\n <circle cx=\"8\" cy=\"8\" r=\"3\" />\n <path d=\"M8 1.5v1.5M8 13v1.5M1.5 8H3M13 8h1.5M3.4 3.4l1 1M11.6 11.6l1 1M3.4 12.6l1-1M11.6 4.4l1-1\" />\n </Svg>\n);\nexport const Moon = () => (\n <Svg>\n <path d=\"M13.5 9.5A5.5 5.5 0 0 1 6.5 2.5a5.5 5.5 0 1 0 7 7z\" />\n </Svg>\n);\nexport const Star = () => (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"m12 2.8 2.8 5.8 6.4.9-4.6 4.5 1.1 6.3L12 17.2l-5.7 3.1 1.1-6.3L2.8 9.5l6.4-.9z\" />\n </svg>\n);\n\nexport const StatusPill = ({ status }: { status: PostStatus }) => (\n <span className=\"ckw-pill\" data-s={status}>\n {STATUS_LABEL[status] ?? status}\n </span>\n);\n\nexport const VoteButton = ({ post, onVote, small }: { post: FeedbackPost; onVote: (p: FeedbackPost) => void; small?: boolean }) => (\n <button\n type=\"button\"\n className={`ckw-vote${small ? \" ckw-vote-sm\" : \"\"}`}\n aria-pressed={post.voted}\n aria-label={`${post.voted ? \"Remove your vote from\" : \"Upvote\"} ${post.title}. ${post.votes} votes`}\n onClick={() => onVote(post)}\n >\n <ChevronUp />\n <span>{post.votes}</span>\n </button>\n);\n\nexport const timeAgo = (iso: string) => {\n const t = new Date(iso).getTime();\n if (Number.isNaN(t)) return \"\";\n const d = Math.max(0, Date.now() - t) / 1000;\n if (d < 60) return \"just now\";\n if (d < 3600) return `${Math.floor(d / 60)}m ago`;\n if (d < 86400) return `${Math.floor(d / 3600)}h ago`;\n if (d < 86400 * 30) return `${Math.floor(d / 86400)}d ago`;\n return new Date(iso).toLocaleDateString(undefined, { month: \"short\", day: \"numeric\", year: \"numeric\" });\n};\n\n/** Calls `onClose` on Esc while mounted. */\nexport const useEscape = (onClose: () => void) => {\n const ref = useRef(onClose);\n ref.current = onClose;\n useEffect(() => {\n const h = (e: KeyboardEvent) => e.key === \"Escape\" && ref.current();\n document.addEventListener(\"keydown\", h);\n return () => document.removeEventListener(\"keydown\", h);\n }, []);\n};\n\n/** Words of a title, lowercased, without filler. */\nconst words = (s: string) =>\n s\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((w) => w.length > 2 && ![\"the\", \"and\", \"for\", \"with\", \"add\", \"can\", \"please\", \"want\", \"would\", \"our\", \"you\", \"have\"].includes(w));\n\n/** Up to three existing posts that share words with what the visitor is typing. */\nexport const similarPosts = (title: string, posts: FeedbackPost[]) => {\n const mine = words(title);\n if (mine.length === 0 || title.trim().length < 4) return [];\n return posts\n .map((p) => {\n const theirs = new Set(words(`${p.title} ${p.body}`.slice(0, 300)));\n const hit = mine.filter((w) => theirs.has(w) || [...theirs].some((t) => t.startsWith(w) || w.startsWith(t))).length;\n return { p, score: hit / mine.length };\n })\n .filter((x) => x.score >= 0.5)\n .sort((a, b) => b.score - a.score || b.p.votes - a.p.votes)\n .slice(0, 3)\n .map((x) => x.p);\n};\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mrrocket/sdk",
3
- "version": "0.4.0",
4
- "description": "MRRocket SDK: page views, events, heatmap clicks, session recordings, A/B tests, feature flags, error tracking, feedback board and surveys that your MRRocket agents can run and read. Next.js (@mrrocket/sdk/next), React Native and Expo (@mrrocket/sdk/react-native), and a script tag for any website.",
3
+ "version": "0.9.0",
4
+ "description": "MRRocket SDK: page views, events, heatmap clicks, session recordings, A/B tests, feature flags, error tracking, feedback board, surveys, a chat messenger, a cancel flow, onboarding tours and checklists, in-app messages, push notifications and redirect tests that your MRRocket agents can run and read. Next.js (@mrrocket/sdk/next), React Native and Expo (@mrrocket/sdk/react-native), and a script tag for any website.",
5
5
  "keywords": [
6
6
  "nextjs",
7
7
  "analytics",
@@ -92,15 +92,19 @@
92
92
  "@rrweb/record": "2.1.6"
93
93
  },
94
94
  "peerDependencies": {
95
- "@react-native-async-storage/async-storage": ">=1.17",
96
95
  "expo-application": ">=5",
96
+ "expo-constants": ">=14",
97
97
  "expo-device": ">=5",
98
+ "expo-haptics": ">=12",
98
99
  "expo-localization": ">=14",
100
+ "expo-notifications": ">=0.20",
99
101
  "expo-router": ">=3",
100
102
  "next": ">=15",
101
103
  "react": ">=18",
102
104
  "react-dom": ">=18",
103
- "react-native": ">=0.72"
105
+ "react-native": ">=0.72",
106
+ "@react-native-async-storage/async-storage": ">=1.17",
107
+ "react-native-view-shot": ">=3.8"
104
108
  },
105
109
  "peerDependenciesMeta": {
106
110
  "@react-native-async-storage/async-storage": {
@@ -112,9 +116,15 @@
112
116
  "expo-device": {
113
117
  "optional": true
114
118
  },
119
+ "expo-constants": {
120
+ "optional": true
121
+ },
115
122
  "expo-localization": {
116
123
  "optional": true
117
124
  },
125
+ "expo-notifications": {
126
+ "optional": true
127
+ },
118
128
  "expo-router": {
119
129
  "optional": true
120
130
  },
@@ -126,6 +136,12 @@
126
136
  },
127
137
  "react-native": {
128
138
  "optional": true
139
+ },
140
+ "expo-haptics": {
141
+ "optional": true
142
+ },
143
+ "react-native-view-shot": {
144
+ "optional": true
129
145
  }
130
146
  },
131
147
  "devDependencies": {