beeport 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,268 @@
1
+ # BeePort
2
+
3
+ [![npm version](https://img.shields.io/npm/v/beeport.svg)](https://www.npmjs.com/package/beeport)
4
+ [![Bundle Size](https://img.shields.io/badge/gzip-3KB-brightgreen.svg)](https://www.npmjs.com/package/beeport)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
7
+
8
+ Embeddable feedback widget that turns user feedback into **agent-ready GitHub issues**. Automatically captures 90% of technical context (browser, OS, screen, URL) so users only need to describe their intent.
9
+
10
+ ## Why BeePort?
11
+
12
+ - **Zero-friction feedback**: Users describe what they want, BeePort captures the technical details
13
+ - **Agent-ready issues**: Structured format optimized for AI/agent consumption and automation
14
+ - **Tiny bundle**: ~3KB gzipped, zero dependencies
15
+ - **Privacy-first**: No tracking, no analytics, issues go directly to your GitHub repo
16
+ - **Shadow DOM isolation**: Won't break your styles or scripts
17
+
18
+ ## Installation
19
+
20
+ ### Via npm/yarn/pnpm
21
+
22
+ ```bash
23
+ npm install beeport
24
+ # or
25
+ yarn add beeport
26
+ # or
27
+ pnpm add beeport
28
+ ```
29
+
30
+ ### Via CDN
31
+
32
+ ```html
33
+ <!-- unpkg -->
34
+ <script src="https://unpkg.com/beeport@latest/dist/beeport.min.js"></script>
35
+
36
+ <!-- jsdelivr -->
37
+ <script src="https://cdn.jsdelivr.net/npm/beeport@latest/dist/beeport.min.js"></script>
38
+ ```
39
+
40
+ ## Quick Start
41
+
42
+ ### ES Modules
43
+
44
+ ```javascript
45
+ import { init } from 'beeport';
46
+
47
+ const beeport = init({
48
+ repo: 'your-org/your-repo',
49
+ token: 'ghp_your_github_token'
50
+ });
51
+ ```
52
+
53
+ ### Script Tag (CDN)
54
+
55
+ ```html
56
+ <script src="https://unpkg.com/beeport@latest/dist/beeport.min.js"></script>
57
+ <script>
58
+ const beeport = BeePort.init({
59
+ repo: 'your-org/your-repo',
60
+ token: 'ghp_your_github_token'
61
+ });
62
+ </script>
63
+ ```
64
+
65
+ ### TypeScript
66
+
67
+ ```typescript
68
+ import { init, BeePortConfig, BeePortInstance } from 'beeport';
69
+
70
+ const config: BeePortConfig = {
71
+ repo: 'your-org/your-repo',
72
+ token: 'ghp_your_github_token',
73
+ position: 'bottom-right',
74
+ theme: 'auto'
75
+ };
76
+
77
+ const beeport: BeePortInstance = init(config);
78
+ ```
79
+
80
+ ## Configuration API
81
+
82
+ ### `init(config)`
83
+
84
+ Initialize the BeePort widget. Returns an `BeePortInstance`.
85
+
86
+ ```typescript
87
+ interface BeePortConfig {
88
+ /** GitHub repository in "owner/repo" format (required) */
89
+ repo: string;
90
+
91
+ /** GitHub Personal Access Token with repo:write scope (required) */
92
+ token: string;
93
+
94
+ /** Position of the widget button */
95
+ position?: 'bottom-right' | 'bottom-left'; // default: 'bottom-right'
96
+
97
+ /** Widget theme */
98
+ theme?: 'light' | 'dark' | 'auto'; // default: 'auto'
99
+ }
100
+ ```
101
+
102
+ ### `BeePortInstance` Methods
103
+
104
+ ```typescript
105
+ interface BeePortInstance {
106
+ /** Show the widget */
107
+ show(): void;
108
+
109
+ /** Hide the widget */
110
+ hide(): void;
111
+
112
+ /** Change the widget theme */
113
+ setTheme(theme: 'light' | 'dark' | 'auto'): void;
114
+
115
+ /** Destroy the widget and remove from DOM */
116
+ destroy(): void;
117
+ }
118
+ ```
119
+
120
+ ## Examples
121
+
122
+ ### Basic Bug Report Widget
123
+
124
+ ```javascript
125
+ import { init } from 'beeport';
126
+
127
+ init({
128
+ repo: 'acme/web-app',
129
+ token: 'ghp_xxxxxxxxxxxxx'
130
+ });
131
+ ```
132
+
133
+ ### Feature Request Widget (Left Position)
134
+
135
+ ```javascript
136
+ import { init } from 'beeport';
137
+
138
+ const beeport = init({
139
+ repo: 'acme/product',
140
+ token: 'ghp_xxxxxxxxxxxxx',
141
+ position: 'bottom-left'
142
+ });
143
+ ```
144
+
145
+ ### Custom Theme Control
146
+
147
+ ```javascript
148
+ import { init } from 'beeport';
149
+
150
+ const beeport = init({
151
+ repo: 'acme/app',
152
+ token: 'ghp_xxxxxxxxxxxxx',
153
+ theme: 'dark'
154
+ });
155
+
156
+ // Later, switch to light theme
157
+ beeport.setTheme('light');
158
+ ```
159
+
160
+ ### Conditional Widget Display
161
+
162
+ ```javascript
163
+ import { init } from 'beeport';
164
+
165
+ const beeport = init({
166
+ repo: 'acme/beta-app',
167
+ token: 'ghp_xxxxxxxxxxxxx'
168
+ });
169
+
170
+ // Hide widget for certain users
171
+ if (user.isPremium) {
172
+ beeport.hide();
173
+ }
174
+
175
+ // Show it again later
176
+ beeport.show();
177
+ ```
178
+
179
+ ### Complete Lifecycle Management
180
+
181
+ ```javascript
182
+ import { init } from 'beeport';
183
+
184
+ // Initialize
185
+ const beeport = init({
186
+ repo: 'acme/app',
187
+ token: 'ghp_xxxxxxxxxxxxx'
188
+ });
189
+
190
+ // Use the widget...
191
+
192
+ // Clean up when done (e.g., SPA route change)
193
+ beeport.destroy();
194
+ ```
195
+
196
+ ## TypeScript Support
197
+
198
+ BeePort is written in TypeScript and includes full type definitions.
199
+
200
+ ```typescript
201
+ import type {
202
+ BeePortConfig,
203
+ BeePortInstance,
204
+ Position,
205
+ Theme,
206
+ FeedbackType,
207
+ BrowserContext
208
+ } from 'beeport';
209
+ ```
210
+
211
+ All configuration options and methods are fully typed for excellent IDE autocomplete and type safety.
212
+
213
+ ## Browser Support
214
+
215
+ BeePort supports all modern browsers:
216
+
217
+ - Chrome/Edge 90+
218
+ - Firefox 88+
219
+ - Safari 14+
220
+ - Opera 76+
221
+
222
+ **ES2020 Required**: The widget uses modern JavaScript features (optional chaining, nullish coalescing, etc.). No IE11 support.
223
+
224
+ ## Bundle Size
225
+
226
+ - **Minified**: ~11KB
227
+ - **Gzipped**: ~3KB
228
+ - **Zero dependencies**
229
+
230
+ The widget uses Shadow DOM for style isolation and has no external dependencies, making it extremely lightweight and non-intrusive.
231
+
232
+ ## What Gets Captured?
233
+
234
+ When a user submits feedback, BeePort automatically captures:
235
+
236
+ - Page URL
237
+ - Browser name and version
238
+ - Operating system and version
239
+ - Screen resolution and pixel ratio
240
+ - Viewport dimensions
241
+ - Language and timezone
242
+ - Timestamp (ISO 8601)
243
+
244
+ This context is formatted into a GitHub issue with structured metadata for easy parsing by AI agents or automation tools.
245
+
246
+ ## Security
247
+
248
+ - **Token security**: Your GitHub token is used client-side to create issues. For production use, consider proxying requests through your backend or using a token with minimal scopes (only `public_repo` or `repo` for private repos).
249
+ - **No tracking**: BeePort doesn't phone home, track analytics, or send data anywhere except your specified GitHub repository.
250
+ - **Shadow DOM**: Isolates the widget from your page's CSS and JavaScript, preventing conflicts or security issues.
251
+
252
+ ## License
253
+
254
+ MIT License - see [LICENSE](https://github.com/quququ/beeport/blob/main/LICENSE) for details.
255
+
256
+ ## Contributing
257
+
258
+ We welcome contributions! Please see our [contributing guidelines](https://github.com/quququ/beeport#contributing) for details.
259
+
260
+ ## Links
261
+
262
+ - [GitHub Repository](https://github.com/quququ/beeport)
263
+ - [Issue Tracker](https://github.com/quququ/beeport/issues)
264
+ - [npm Package](https://www.npmjs.com/package/beeport)
265
+
266
+ ---
267
+
268
+ **Made with care for developers building better products.**
@@ -0,0 +1 @@
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).BeePort={})}(this,function(e){"use strict";function t(e,t){if(e.setAttribute("data-theme",t),"auto"!==t)return()=>{};if("undefined"==typeof window||!window.matchMedia)return()=>{};const n=window.matchMedia("(prefers-color-scheme: dark)"),r=()=>{};n.addEventListener("change",r);let o=!1;return()=>{o||(n.removeEventListener("change",r),o=!0)}}function n(e,t){const n=document.createElement("div");n.className="beeport-form-panel",n.style.display="none",n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true");const o=document.createElement("div");o.className="beeport-form-header";const i=document.createElement("h3");i.className="beeport-form-title",i.id="beeport-form-title",i.textContent="Report to BeePort";const a=document.createElement("p");a.className="beeport-form-subtitle",a.textContent="Help us fix bugs and shape features in under a minute.",n.setAttribute("aria-labelledby","beeport-form-title");const s=document.createElement("button");s.className="beeport-form-close",s.textContent="×",s.setAttribute("aria-label","Close");const p=document.createElement("div");p.className="beeport-form-heading",p.appendChild(i),p.appendChild(a),o.appendChild(p),o.appendChild(s);const c=document.createElement("p");c.className="beeport-type-legend",c.textContent="What are you sharing?";const l=document.createElement("div");l.className="beeport-type-selector",l.setAttribute("role","tablist"),l.setAttribute("aria-label","Select feedback type");const d=r("bug","Bug report",!0),u=r("feature","Feature idea",!1);l.appendChild(d),l.appendChild(u);const b=document.createElement("div");b.className="beeport-input-wrapper";const h=document.createElement("label");h.textContent="Title",h.htmlFor="beeport-title-input",h.className="beeport-input-label";const m=document.createElement("input");m.type="text",m.id="beeport-title-input",m.className="beeport-input-title",m.placeholder="Example: Save button does nothing on Safari";const f=document.createElement("div");f.className="beeport-input-hint beeport-input-hint-title",f.textContent="Use a short summary (3+ characters).";const g=document.createElement("div");g.id="beeport-title-error",g.className="beeport-error-message",g.setAttribute("role","alert"),b.appendChild(h),b.appendChild(m),b.appendChild(f),b.appendChild(g);const x=document.createElement("div");x.className="beeport-input-wrapper";const y=document.createElement("label");y.textContent="Description",y.htmlFor="beeport-description-input",y.className="beeport-input-label";const v=document.createElement("textarea");v.id="beeport-description-input",v.className="beeport-input-description",v.placeholder="What happened? What did you expect instead? Include steps to reproduce.",v.maxLength=1e3,v.rows=5;const w=document.createElement("div");w.className="beeport-input-hint beeport-input-hint-description",w.textContent="Include context, expected result, and actual result (10+ characters).";const k=document.createElement("div");k.className="beeport-char-counter",k.textContent="0/1000 characters";const E=document.createElement("div");E.className="beeport-description-meta",E.appendChild(w),E.appendChild(k);const C=document.createElement("div");C.id="beeport-description-error",C.className="beeport-error-message",C.setAttribute("role","alert"),x.appendChild(y),x.appendChild(v),x.appendChild(E),x.appendChild(C);const S=document.createElement("div");S.className="beeport-status-message",S.setAttribute("aria-live","polite"),S.setAttribute("aria-atomic","true"),S.textContent="";const A=document.createElement("div");A.className="beeport-form-buttons";const N=document.createElement("button");N.className="beeport-cancel-button",N.textContent="Not now";const L=document.createElement("button");L.className="beeport-submit-button",L.textContent="Create Issue",A.appendChild(N),A.appendChild(L);const B=document.createElement("div");B.className="beeport-form-footer";const $=document.createElement("a");$.href="https://beeport.ai",$.target="_blank",$.rel="noopener noreferrer",$.className="beeport-footer-link";const F=document.createElement("span");F.className="beeport-footer-bee",F.textContent="🐝";const O=document.createElement("span");O.className="beeport-footer-text",O.textContent="Powered by ";const P=document.createElement("span");P.className="beeport-footer-brand",P.textContent="BeePort",$.appendChild(F),$.appendChild(O),$.appendChild(P),B.appendChild($),n.appendChild(o),n.appendChild(c),n.appendChild(l),n.appendChild(b),n.appendChild(x),n.appendChild(S),n.appendChild(A),n.appendChild(B);const z=document.createElement("style");z.textContent='\n /* Form Panel */\n .beeport-form-panel {\n position: absolute;\n bottom: 72px;\n right: 0;\n width: min(420px, calc(100vw - 24px));\n max-height: min(80vh, 680px);\n background:\n radial-gradient(circle at top left, rgba(250, 204, 21, 0.18), transparent 55%),\n var(--beeport-bg-primary);\n border: 1px solid rgba(245, 158, 11, 0.32);\n border-radius: 18px;\n box-shadow: 0 20px 44px rgba(17, 24, 39, 0.28), 0 6px 18px rgba(17, 24, 39, 0.16);\n padding: 0;\n color: var(--beeport-text-primary);\n font-family: "Avenir Next", "Trebuchet MS", "Segoe UI", sans-serif;\n overflow: hidden;\n animation: beeport-form-enter 160ms cubic-bezier(0.22, 1, 0.36, 1);\n }\n\n /* Mobile responsive */\n @media (max-width: 500px) {\n .beeport-form-panel {\n right: 8px;\n bottom: 72px;\n width: calc(100vw - 16px);\n max-height: calc(100vh - 96px);\n border-radius: 14px;\n }\n }\n\n /* Header */\n .beeport-form-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 12px;\n padding: 18px 18px 14px;\n border-bottom: 1px solid rgba(245, 158, 11, 0.2);\n background: linear-gradient(180deg, rgba(250, 204, 21, 0.15) 0%, transparent 100%);\n }\n\n .beeport-form-heading {\n min-width: 0;\n flex: 1;\n }\n\n .beeport-form-title {\n margin: 0;\n font-size: 20px;\n font-weight: 700;\n color: var(--beeport-text-primary);\n line-height: 1.2;\n }\n\n .beeport-form-subtitle {\n margin: 6px 0 0;\n font-size: 12px;\n line-height: 1.4;\n color: var(--beeport-text-secondary);\n max-width: 32ch;\n }\n\n .beeport-form-close {\n background: var(--beeport-bg-secondary);\n border: 1px solid rgba(245, 158, 11, 0.3);\n font-size: 17px;\n line-height: 1;\n color: var(--beeport-text-primary);\n cursor: pointer;\n padding: 2px;\n width: 30px;\n height: 30px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n font-family: inherit;\n transition: all var(--beeport-transition-speed) var(--beeport-transition-easing);\n }\n\n .beeport-form-close:hover {\n border-color: #f59e0b;\n background: rgba(245, 158, 11, 0.18);\n transform: rotate(90deg);\n }\n\n .beeport-form-close:focus {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-form-close:focus:not(:focus-visible) {\n outline: none;\n }\n\n .beeport-form-close:focus-visible {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-type-legend {\n margin: 12px 18px 8px;\n color: var(--beeport-text-secondary);\n font-size: 12px;\n font-weight: 600;\n letter-spacing: 0.01em;\n }\n\n /* Type Selector */\n .beeport-type-selector {\n display: flex;\n gap: 8px;\n margin: 0 18px 14px;\n padding: 5px;\n min-height: 62px;\n border: 1px solid rgba(245, 158, 11, 0.28);\n border-radius: 12px;\n background: var(--beeport-bg-secondary);\n }\n\n .beeport-type-button {\n flex: 1;\n padding: 9px 10px;\n min-height: 52px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 9px;\n color: var(--beeport-text-secondary);\n font-size: 12px;\n font-weight: 700;\n font-family: inherit;\n letter-spacing: 0.01em;\n text-transform: none;\n cursor: pointer;\n transition: all var(--beeport-transition-speed) var(--beeport-transition-easing);\n }\n\n .beeport-type-button:last-child {\n border-right: 0;\n }\n\n .beeport-type-button:hover {\n background: rgba(245, 158, 11, 0.1);\n color: var(--beeport-text-primary);\n }\n\n .beeport-type-button.active {\n background: linear-gradient(135deg, #f59e0b, #facc15);\n color: #111827;\n box-shadow: 0 2px 8px rgba(245, 158, 11, 0.35);\n }\n\n .beeport-type-button:focus {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: -2px;\n }\n\n .beeport-type-button:focus:not(:focus-visible) {\n outline: none;\n }\n\n .beeport-type-button:focus-visible {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: -2px;\n }\n\n /* Input Wrapper */\n .beeport-input-wrapper {\n position: relative;\n padding: 0 18px 14px;\n border-bottom: none;\n }\n\n /* Input Label */\n .beeport-input-label {\n display: block;\n margin-bottom: 6px;\n color: var(--beeport-text-secondary);\n font-size: 11px;\n font-weight: 600;\n font-family: inherit;\n letter-spacing: 0.01em;\n }\n\n /* Input and Textarea */\n .beeport-input-title,\n .beeport-input-description {\n width: 100%;\n padding: 10px 12px;\n background: var(--beeport-input-bg);\n border: 1px solid rgba(107, 114, 128, 0.45);\n border-radius: 11px;\n color: var(--beeport-text-primary);\n font-size: 14px;\n font-family: inherit;\n transition: all var(--beeport-transition-speed) var(--beeport-transition-easing);\n box-sizing: border-box;\n }\n\n .beeport-input-title::placeholder,\n .beeport-input-description::placeholder {\n color: var(--beeport-text-secondary);\n opacity: 0.8;\n }\n\n .beeport-input-title:focus,\n .beeport-input-description:focus {\n outline: none;\n border-color: #f59e0b;\n box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.2);\n }\n\n .beeport-input-title:focus:not(:focus-visible),\n .beeport-input-description:focus:not(:focus-visible) {\n outline: none;\n }\n\n .beeport-input-title:focus-visible,\n .beeport-input-description:focus-visible {\n outline: none;\n border-color: #f59e0b;\n box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.2);\n }\n\n .beeport-input-description {\n resize: vertical;\n min-height: 108px;\n max-height: 240px;\n }\n\n /* Error state */\n .beeport-input-title.error,\n .beeport-input-description.error {\n border-color: #dc2626;\n box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.16);\n }\n\n .beeport-input-hint {\n margin-top: 7px;\n font-size: 11px;\n color: var(--beeport-text-secondary);\n line-height: 1.3;\n }\n\n .beeport-error-message {\n color: #dc2626;\n font-size: 12px;\n margin-top: 6px;\n min-height: 16px;\n }\n\n /* Status Message (Live Region) */\n .beeport-status-message {\n margin: 0 18px 14px;\n padding: 8px 10px;\n border-radius: 10px;\n font-size: var(--beeport-font-size-sm);\n min-height: 0;\n }\n\n .beeport-status-message:empty {\n display: none;\n }\n\n .beeport-status-message.success {\n background-color: #d1fae5;\n color: #065f46;\n border: 1px solid #10b981;\n }\n\n .beeport-status-message.error {\n background-color: #fee2e2;\n color: #991b1b;\n border: 1px solid #dc2626;\n }\n\n .beeport-description-meta {\n margin-top: 7px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n }\n\n /* Character Counter */\n .beeport-char-counter {\n font-size: 11px;\n font-family: inherit;\n color: var(--beeport-text-secondary);\n opacity: 0.8;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n transition: color var(--beeport-transition-speed) var(--beeport-transition-easing);\n }\n\n .beeport-char-counter.warning {\n color: #b45309;\n }\n\n .beeport-char-counter.limit {\n color: #dc2626;\n font-weight: 700;\n }\n\n /* Form Buttons */\n .beeport-form-buttons {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n padding: 0 18px 16px;\n }\n\n .beeport-cancel-button,\n .beeport-submit-button {\n padding: 10px 16px;\n border-radius: 10px;\n font-size: 12px;\n font-weight: 700;\n font-family: inherit;\n letter-spacing: 0.02em;\n text-transform: none;\n cursor: pointer;\n transition: all var(--beeport-transition-speed) var(--beeport-transition-easing);\n }\n\n .beeport-cancel-button {\n background: transparent;\n border: 1px solid rgba(107, 114, 128, 0.45);\n color: var(--beeport-text-primary);\n }\n\n .beeport-cancel-button:hover {\n background: rgba(107, 114, 128, 0.1);\n color: var(--beeport-text-primary);\n border-color: rgba(107, 114, 128, 0.8);\n }\n\n .beeport-cancel-button:focus {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-cancel-button:focus:not(:focus-visible) {\n outline: none;\n }\n\n .beeport-cancel-button:focus-visible {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-submit-button {\n background: linear-gradient(135deg, #f59e0b, #facc15);\n border: 1px solid #d97706;\n color: #ffffff;\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.32);\n box-shadow: 0 4px 12px rgba(245, 158, 11, 0.28);\n }\n\n .beeport-submit-button:hover {\n transform: translateY(-1px);\n box-shadow: 0 8px 16px rgba(245, 158, 11, 0.34);\n }\n\n .beeport-submit-button:active {\n background: #f59e0b;\n border-color: #B45309;\n box-shadow: 0 2px 8px rgba(245, 158, 11, 0.28);\n transform: translateY(0);\n }\n\n .beeport-submit-button:focus {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-submit-button:focus:not(:focus-visible) {\n outline: none;\n }\n\n .beeport-submit-button:focus-visible {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n /* Footer Branding */\n .beeport-form-footer {\n padding: 10px 16px 14px;\n text-align: center;\n background: linear-gradient(180deg, transparent 0%, rgba(245, 158, 11, 0.08) 100%);\n }\n\n .beeport-footer-link {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n text-decoration: none;\n color: var(--beeport-text-secondary);\n font-size: 11px;\n font-family: inherit;\n letter-spacing: 0.02em;\n transition: color var(--beeport-transition-speed) var(--beeport-transition-easing);\n }\n\n .beeport-footer-link:hover {\n color: var(--beeport-text-primary);\n }\n\n .beeport-footer-link:hover .beeport-footer-brand {\n text-decoration: underline;\n }\n\n .beeport-footer-bee {\n font-size: 11px;\n line-height: 1;\n }\n\n .beeport-footer-text {\n opacity: 0.5;\n }\n\n .beeport-footer-brand {\n font-weight: 700;\n color: #F59E0B;\n letter-spacing: 0.02em;\n }\n\n @keyframes beeport-form-enter {\n from {\n opacity: 0;\n transform: translateY(8px) scale(0.98);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n ',e.appendChild(z),e.appendChild(n);let G="bug",R=null;const T=e=>{G=e,[d,u].forEach(t=>{t.dataset.type===e?(t.classList.add("active"),t.setAttribute("aria-selected","true")):(t.classList.remove("active"),t.setAttribute("aria-selected","false"))})},j=()=>{t?.onCancel&&t.onCancel()},V=e=>{const t=e.target;"block"!==n.style.display||n.contains(t)||(n.style.display="none",I())},I=()=>{R&&(R.focus(),R=null)};return d.addEventListener("click",()=>T("bug")),u.addEventListener("click",()=>T("feature")),v.addEventListener("input",()=>{const e=v.value.length;k.textContent=`${e}/1000 characters`,k.classList.toggle("warning",e>=900&&e<1e3),k.classList.toggle("limit",e>=1e3)}),L.addEventListener("click",()=>{t?.onSubmit&&t.onSubmit()}),N.addEventListener("click",j),s.addEventListener("click",j),n.addEventListener("keydown",e=>{if("Escape"===e.key)return n.style.display="none",void I();if("Tab"===e.key){const t=(()=>{const e=n.querySelectorAll(["button:not([disabled])","input:not([disabled])","textarea:not([disabled])","select:not([disabled])",'[tabindex]:not([tabindex="-1"])'].join(","));return Array.from(e)})();if(0===t.length)return;const r=t[0],o=t[t.length-1];e.shiftKey?document.activeElement===r&&o&&(e.preventDefault(),o.focus()):document.activeElement===o&&r&&(e.preventDefault(),r.focus())}}),e.addEventListener("click",V),{show(){R=document.activeElement,n.style.display="block",m.focus()},hide(){n.style.display="none",I()},reset(){m.value="",v.value="",G="bug",T("bug"),k.textContent="0/1000 characters",k.classList.remove("warning","limit"),m.classList.remove("error"),v.classList.remove("error"),m.removeAttribute("aria-invalid"),v.removeAttribute("aria-invalid"),m.removeAttribute("aria-describedby"),v.removeAttribute("aria-describedby"),g.textContent="",C.textContent=""},destroy(){e.removeEventListener("click",V),n.remove(),z.remove()},getValues:()=>({type:G,title:m.value,description:v.value}),validate:()=>(()=>{const e={},t=m.value.trim(),n=v.value.trim();return m.classList.remove("error"),v.classList.remove("error"),m.removeAttribute("aria-invalid"),v.removeAttribute("aria-invalid"),m.removeAttribute("aria-describedby"),v.removeAttribute("aria-describedby"),g.textContent="",C.textContent="",0===t.length?e.title="Title is required":t.length<3&&(e.title="Title must be at least 3 characters"),0===n.length?e.description="Description is required":n.length<10&&(e.description="Description must be at least 10 characters"),e.title&&(m.classList.add("error"),m.setAttribute("aria-invalid","true"),m.setAttribute("aria-describedby","beeport-title-error"),g.textContent=e.title),e.description&&(v.classList.add("error"),v.setAttribute("aria-invalid","true"),v.setAttribute("aria-describedby","beeport-description-error"),C.textContent=e.description),{valid:0===Object.keys(e).length,errors:e}})()}}function r(e,t,n){const r=document.createElement("button");return r.className="beeport-type-button",r.dataset.type=e,r.textContent=t,r.setAttribute("role","tab"),r.setAttribute("aria-selected",String(n)),r.setAttribute("aria-label",{bug:"Report a bug",feature:"Request a feature",question:"Ask a question"}[e]),n&&r.classList.add("active"),r}function o(e,t){try{return e()}catch{return t}}const i=["token","key","secret","password","auth","session","api_key","access_token"];function a(e){const t=function(e){try{const t=new URL(e),n=t.searchParams;return n.forEach((e,t)=>{const r=t.toLowerCase();i.some(e=>r.includes(e))&&n.set(t,"[REDACTED]")}),decodeURIComponent(t.toString())}catch{return e}}(e.url),n=e.cookiesEnabled?"Enabled":"Disabled";return`## Environment\n- **URL**: ${t}\n- **Browser**: ${e.browser.name} ${e.browser.version}\n- **OS**: ${e.os.name} ${e.os.version}\n- **Viewport**: ${e.viewport.width}x${e.viewport.height}\n- **Screen**: ${e.screen.width}x${e.screen.height} @${e.screen.pixelRatio}x\n- **Language**: ${e.language}\n- **Cookies**: ${n}\n- **Timestamp**: ${e.timestamp}`}function s(e){if(!e||0===e.length)return"No errors captured";const t=function(e){const t=new Map;for(const n of e){const e=t.get(n.message);e?(e.count++,e.errors.push(n)):t.set(n.message,{errors:[n],count:1})}return t}(e),n=[];let r=1;return t.forEach(e=>{const t=e.errors[0];if(!t)return;const o=function(e){const t=["TypeError","ReferenceError","SyntaxError","RangeError","URIError","EvalError"];for(const n of t)if(e.startsWith(n))return n;return"Error"}(t.message),i=[`### Error ${r}`,`**Type**: ${o}`,`**Message**: ${t.message}`];if(e.count>1&&i.push(`**Occurrences**: (occurred ${e.count} times)`),t.stack){const e=function(e,t=20){const n=e.split("\n");if(n.length<=t)return e;const r=n.slice(0,t),o=n.length-t;return r.push(`... (${o} more lines)`),r.join("\n")}(t.stack);i.push(`**Stack trace**:\n\`\`\`\n${e}\n\`\`\``)}i.push(`**Timestamp**: ${t.timestamp}`),n.push(i.join("\n")),r++}),n.join("\n\n")}class p extends Error{constructor(e,t,n){super(e),this.status=t,this.response=n,this.name="GitHubError"}}class c extends p{constructor(e,t,n){super(e,t,n),this.name="GitHubAuthError"}}class l extends p{constructor(e,t,n){super(e,t,n),this.name="GitHubNotFoundError"}}class d extends p{constructor(e,t,n){super(e,t,n),this.name="GitHubValidationError"}}class u extends p{constructor(e,t,n,r){super(e,t,n),this.rateLimit=r,this.name="GitHubRateLimitError"}}class b extends p{constructor(e,t){super(e),this.cause=t,this.name="GitHubNetworkError"}}async function h(e){const{token:t,owner:n,repo:r,title:o,body:i,labels:a}=e,s=`https://api.github.com/repos/${n}/${r}/issues`,h={title:o,body:i};let m;a&&a.length>0&&(h.labels=a);try{m=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${t}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28","Content-Type":"application/json"},body:JSON.stringify(h)})}catch(g){throw new b("Network request failed",g instanceof Error?g:void 0)}if(!m.ok){let e;try{e=await m.json()}catch{e={}}const t=e.message||`HTTP ${m.status}`;switch(m.status){case 401:case 403:throw new c(t,m.status,e);case 404:throw new l(t,m.status,e);case 422:throw new d(t,m.status,e);case 429:{const n={limit:parseInt(m.headers.get("X-RateLimit-Limit")||"0",10),remaining:parseInt(m.headers.get("X-RateLimit-Remaining")||"0",10),reset:parseInt(m.headers.get("X-RateLimit-Reset")||"0",10)};throw new u(t,m.status,e,n)}default:throw new p(t,m.status,e)}}const f=await m.json();return{url:f.html_url,number:f.number,id:f.id}}function m(e,t){const n=e.toLowerCase().trim(),r=t.toLowerCase().trim();if(0===n.length&&0===r.length)return 1;if(0===n.length||0===r.length)return 0;const o=function(e,t){if(0===e.length)return t.length;if(0===t.length)return e.length;const n=[];for(let r=0;r<=t.length;r++)n[r]=[r];for(let r=0;r<=e.length;r++)n[0][r]=r;for(let r=1;r<=t.length;r++)for(let o=1;o<=e.length;o++)t.charAt(r-1)===e.charAt(o-1)?n[r][o]=n[r-1][o-1]:n[r][o]=Math.min(n[r-1][o-1]+1,n[r][o-1]+1,n[r-1][o]+1);return n[t.length][e.length]}(n,r);return 1-o/Math.max(n.length,r.length)}function f(e){return e instanceof c?"Authentication failed. Check your GitHub token.":e instanceof l?"Repository not found. Check the repo setting.":e instanceof u?"Rate limit exceeded. Please try again later.":e instanceof d?"Invalid issue data. Please check your input.":e instanceof b?"Network error. Check your connection.":e instanceof Error?e.message:"An unknown error occurred."}async function g(e){const{config:t,formValues:n,errors:r}=e;try{const e={url:o(()=>window.location.href,"unknown"),viewport:o(()=>({width:window.innerWidth,height:window.innerHeight}),{width:0,height:0}),screen:o(()=>({width:window.screen.width,height:window.screen.height,pixelRatio:window.devicePixelRatio}),{width:0,height:0,pixelRatio:1}),browser:o(()=>{const e=navigator.userAgentData;if(e?.brands){const t=e.brands;for(const e of t){const t=e.brand.toLowerCase();if(t.includes("chrome")&&!t.includes("chromium"))return{name:"Chrome",version:e.version};if(t.includes("edge"))return{name:"Edge",version:e.version};if(t.includes("opera"))return{name:"Opera",version:e.version}}}return function(e){if(/Edg\//.test(e)){const t=e.match(/Edg\/([\d.]+)/);return{name:"Edge",version:t?.[1]||"unknown"}}if(/OPR\//.test(e)){const t=e.match(/OPR\/([\d.]+)/);return{name:"Opera",version:t?.[1]||"unknown"}}if(/Chrome\//.test(e)&&!/Edg\//.test(e)){const t=e.match(/Chrome\/([\d.]+)/);return{name:"Chrome",version:t?.[1]||"unknown"}}if(/Firefox\//.test(e)){const t=e.match(/Firefox\/([\d.]+)/);return{name:"Firefox",version:t?.[1]||"unknown"}}if(/Safari\//.test(e)&&!/Chrome\//.test(e)){const t=e.match(/Version\/([\d.]+)/);return{name:"Safari",version:t?.[1]||"unknown"}}return{name:"Unknown",version:"unknown"}}(navigator.userAgent)},{name:"Unknown",version:"unknown"}),os:o(()=>function(e){if(/iPhone|iPad|iPod/.test(e)){const t=e.match(/OS ([\d_]+)/);return{name:"iOS",version:t?.[1]?.replace(/_/g,".")||"unknown"}}if(/Windows NT/.test(e)){const t=e.match(/Windows NT ([\d.]+)/),n=t?.[1]||"unknown";return{name:"Windows",version:{"10.0":"10",6.3:"8.1",6.2:"8",6.1:"7"}[n]||n}}if(/Mac OS X/.test(e)){const t=e.match(/Mac OS X ([\d_]+)/);return{name:"macOS",version:t?.[1]?.replace(/_/g,".")||"unknown"}}if(/Android/.test(e)){const t=e.match(/Android ([\d.]+)/);return{name:"Android",version:t?.[1]||"unknown"}}return/Linux/.test(e)?{name:"Linux",version:"unknown"}:{name:"Unknown",version:"unknown"}}(navigator.userAgent),{name:"Unknown",version:"unknown"}),timestamp:(new Date).toISOString(),language:o(()=>navigator.language,"unknown"),cookiesEnabled:o(()=>navigator.cookieEnabled,!1),doNotTrack:o(()=>navigator.doNotTrack,null)},c=function(e,t){return`${{bug:"[Bug]",feature:"[Feature]",question:"[Question]"}[e]} ${t}`}(n.type,n.title),l=function(e){switch(e.type){case"bug":return function(e){return[`## Summary\n${e.description}`,"## Steps to Reproduce\nNot provided","## Expected Behavior\nNot provided","## Actual Behavior\nNot provided",a(e.context),`## Console Errors\n${s(e.errors)}`].join("\n\n")+"\n---\n*Generated by [BeePort](https://beeport.ai) | agent-ready*"}(e);case"feature":return function(e){return[`## Summary\n${e.description}`,"## Use Case\nNot provided","## Proposed Solution\nNot provided",a(e.context)].join("\n\n")+"\n---\n*Generated by [BeePort](https://beeport.ai) | agent-ready*"}(e);case"question":return function(e){return[`## Question\n${e.description}`,"## Context\nNot provided",a(e.context)].join("\n\n")+"\n---\n*Generated by [BeePort](https://beeport.ai) | agent-ready*"}(e);default:throw new Error(`Unknown feedback type: ${e.type}`)}}({type:n.type,title:n.title,description:n.description,context:e,errors:r}),d=["bug"===(i=n.type)?"bug":"feature"===i?"enhancement":"question","beeport","agent-ready"],{owner:u,repo:b}=function(e){const t=e.split("/");return{owner:t[0]||"",repo:t.slice(1).join("/")}}(t.repo);let f=[];try{f=await async function(e){const{token:t,owner:n,repo:r,title:o}=e;try{const e=`https://api.github.com/repos/${n}/${r}/issues?labels=beeport&state=open`,i=await fetch(e,{method:"GET",headers:{Authorization:`Bearer ${t}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28"}});return i.ok?(await i.json()).map(e=>({number:e.number,title:e.title,url:e.html_url,similarity:m(o,e.title),state:e.state})).filter(e=>e.similarity>.7).sort((e,t)=>t.similarity-e.similarity).slice(0,5):(console.warn(`GitHub API error: ${i.status} ${i.statusText}`),[])}catch(i){return console.warn("Failed to search for duplicate issues:",i),[]}}({token:t.token,owner:u,repo:b,title:c})}catch(p){console.warn("Duplicate search failed, proceeding with submission:",p)}if(f.length>0)return{status:"duplicates_found",candidates:f,pendingSubmission:{title:c,body:l,labels:d,owner:u,repo:b,token:t.token}};const g=await h({token:t.token,owner:u,repo:b,title:c,body:l,labels:d});return{status:"success",issueUrl:g.url,issueNumber:g.number}}catch(p){return{status:"error",error:f(p)}}var i}const x=Object.freeze(Object.defineProperty({__proto__:null,confirmSubmit:async function(e){try{const t=await h({token:e.token,owner:e.owner,repo:e.repo,title:e.title,body:e.body,labels:e.labels});return{status:"success",issueUrl:t.url,issueNumber:t.number}}catch(t){return{status:"error",error:f(t)}}},submitFeedback:g},Symbol.toStringTag,{value:"Module"}));class y{constructor(e){this.isOpen=!1,this.themeCleanup=null,this.config=e,this.hostElement=document.createElement("div"),this.hostElement.setAttribute("data-beeport-container",""),this.shadowRoot=this.hostElement.attachShadow({mode:"open"}),this.container=document.createElement("div"),this.container.className="beeport-container",this.container.setAttribute("data-open","false"),this.applyPosition(e.position);const r=document.createElement("style");r.textContent='\n /* CSS Custom Properties (theme variables) */\n :host {\n --beeport-z-index: 999999;\n --beeport-spacing-sm: 8px;\n --beeport-spacing-md: 16px;\n --beeport-spacing-lg: 24px;\n --beeport-border-radius: 8px;\n --beeport-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n\n /* Typography */\n --beeport-font-size-sm: 12px;\n --beeport-font-size-md: 14px;\n --beeport-font-size-lg: 16px;\n --beeport-font-weight-normal: 400;\n --beeport-font-weight-medium: 500;\n --beeport-font-weight-bold: 600;\n\n /* Transitions */\n --beeport-transition-speed: 200ms;\n --beeport-transition-easing: ease-in-out;\n }\n\n /* Light theme colors */\n .beeport-container[data-theme="light"] {\n /* Base colors */\n --beeport-bg-primary: #ffffff;\n --beeport-bg-secondary: #f5f5f5;\n --beeport-text-primary: #1f2937;\n --beeport-text-secondary: #6b7280;\n --beeport-border-color: #d1d5db;\n --beeport-accent: #2563eb;\n\n /* Button states */\n --beeport-button-primary-bg: #2563eb;\n --beeport-button-primary-hover: #1d4ed8;\n --beeport-button-primary-active: #1e40af;\n --beeport-button-disabled-bg: #e5e7eb;\n --beeport-button-disabled-text: #4b5563;\n\n /* Input states */\n --beeport-input-bg: #ffffff;\n --beeport-input-border: #9ca3af;\n --beeport-input-focus-border: #2563eb;\n\n /* Status colors */\n --beeport-success: #047857;\n --beeport-error: #dc2626;\n --beeport-warning: #b45309;\n }\n\n /* Dark theme colors */\n .beeport-container[data-theme="dark"] {\n /* Base colors */\n --beeport-bg-primary: #1f2937;\n --beeport-bg-secondary: #111827;\n --beeport-text-primary: #f9fafb;\n --beeport-text-secondary: #9ca3af;\n --beeport-border-color: #6b7280;\n --beeport-accent: #60a5fa;\n\n /* Button states */\n --beeport-button-primary-bg: #2563eb;\n --beeport-button-primary-hover: #2563eb;\n --beeport-button-primary-active: #1d4ed8;\n --beeport-button-disabled-bg: #374151;\n --beeport-button-disabled-text: #d1d5db;\n\n /* Input states */\n --beeport-input-bg: #111827;\n --beeport-input-border: #6b7280;\n --beeport-input-focus-border: #60a5fa;\n\n /* Status colors */\n --beeport-success: #34d399;\n --beeport-error: #f87171;\n --beeport-warning: #fbbf24;\n }\n\n /* Auto theme - respect system preference */\n .beeport-container[data-theme="auto"] {\n /* Base colors */\n --beeport-bg-primary: #ffffff;\n --beeport-bg-secondary: #f5f5f5;\n --beeport-text-primary: #1f2937;\n --beeport-text-secondary: #6b7280;\n --beeport-border-color: #d1d5db;\n --beeport-accent: #2563eb;\n\n /* Button states */\n --beeport-button-primary-bg: #2563eb;\n --beeport-button-primary-hover: #1d4ed8;\n --beeport-button-primary-active: #1e40af;\n --beeport-button-disabled-bg: #e5e7eb;\n --beeport-button-disabled-text: #4b5563;\n\n /* Input states */\n --beeport-input-bg: #ffffff;\n --beeport-input-border: #9ca3af;\n --beeport-input-focus-border: #2563eb;\n\n /* Status colors */\n --beeport-success: #047857;\n --beeport-error: #dc2626;\n --beeport-warning: #b45309;\n }\n\n @media (prefers-color-scheme: dark) {\n .beeport-container[data-theme="auto"] {\n /* Base colors */\n --beeport-bg-primary: #1f2937;\n --beeport-bg-secondary: #111827;\n --beeport-text-primary: #f9fafb;\n --beeport-text-secondary: #9ca3af;\n --beeport-border-color: #6b7280;\n --beeport-accent: #60a5fa;\n\n /* Button states */\n --beeport-button-primary-bg: #2563eb;\n --beeport-button-primary-hover: #2563eb;\n --beeport-button-primary-active: #1d4ed8;\n --beeport-button-disabled-bg: #374151;\n --beeport-button-disabled-text: #d1d5db;\n\n /* Input states */\n --beeport-input-bg: #111827;\n --beeport-input-border: #6b7280;\n --beeport-input-focus-border: #60a5fa;\n\n /* Status colors */\n --beeport-success: #34d399;\n --beeport-error: #f87171;\n --beeport-warning: #fbbf24;\n }\n }\n\n /* Container base styles */\n .beeport-container {\n position: fixed;\n z-index: var(--beeport-z-index);\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n box-sizing: border-box;\n }\n\n .beeport-container *,\n .beeport-container *::before,\n .beeport-container *::after {\n box-sizing: border-box;\n }\n\n /* Reset button styles for future components */\n .beeport-container button {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n margin: 0;\n padding: 0;\n border: 0;\n background: none;\n cursor: pointer;\n }\n\n /* Feedback button - floating action button */\n .beeport-feedback-button {\n position: relative;\n width: 56px;\n height: 56px;\n border-radius: 50%;\n background: linear-gradient(135deg, #FFC107 0%, #F59E0B 50%, #D97706 100%);\n color: white;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(245, 158, 11, 0.4);\n transition: all var(--beeport-transition-speed) var(--beeport-transition-easing);\n border: none;\n cursor: pointer;\n outline: none;\n }\n\n .beeport-feedback-button:hover {\n background: linear-gradient(135deg, #FFD54F 0%, #FFC107 50%, #F59E0B 100%);\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(245, 158, 11, 0.5);\n }\n\n .beeport-feedback-button:focus {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-feedback-button:focus:not(:focus-visible) {\n outline: none;\n }\n\n .beeport-feedback-button:focus-visible {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-feedback-button:active {\n background: linear-gradient(135deg, #F59E0B 0%, #D97706 50%, #B45309 100%);\n transform: translateY(0);\n }\n\n .beeport-feedback-button svg {\n width: 28px;\n height: 28px;\n pointer-events: none;\n filter: drop-shadow(0 1px 1px rgba(0,0,0,0.15));\n }\n ',this.shadowRoot.appendChild(r),this.shadowRoot.appendChild(this.container),this.feedbackButton=this.createFeedbackButton(),this.container.appendChild(this.feedbackButton),this.form=n(this.container,{onSubmit:()=>{this.handleSubmit()},onCancel:()=>{this.isOpen&&this.toggleOpen()}}),document.body.appendChild(this.hostElement),this.themeCleanup=t(this.container,e.theme)}applyPosition(e){this.container.style.position="fixed",this.container.style.bottom="20px","bottom-right"===e?this.container.style.right="20px":this.container.style.left="20px"}createFeedbackButton(){const e=document.createElement("button");return e.className="beeport-feedback-button",e.setAttribute("aria-label","Send feedback"),e.setAttribute("aria-expanded","false"),e.setAttribute("tabindex","0"),this.updateButtonIcon(e,!1),e.addEventListener("click",e=>{e.stopPropagation(),this.toggleOpen()}),e.addEventListener("keydown",e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),this.toggleOpen())}),e}async handleSubmit(){if(!this.form.validate().valid)return;const e=this.form.getValues(),t=await g({config:this.config,formValues:e});if("success"===t.status)this.form.reset(),this.toggleOpen(),console.log(`[BeePort] Issue created: ${t.issueUrl}`);else if("duplicates_found"===t.status){console.log("[BeePort] Potential duplicates found, submitting anyway");const{confirmSubmit:e}=await Promise.resolve().then(()=>x),n=await e(t.pendingSubmission);"success"===n.status?(this.form.reset(),this.toggleOpen(),console.log(`[BeePort] Issue created: ${n.issueUrl}`)):"error"===n.status&&console.error(`[BeePort] Submit failed: ${n.error}`)}else"error"===t.status&&console.error(`[BeePort] Submit failed: ${t.error}`)}toggleOpen(){this.isOpen=!this.isOpen,this.container.setAttribute("data-open",String(this.isOpen)),this.feedbackButton.setAttribute("aria-expanded",String(this.isOpen)),this.updateButtonIcon(this.feedbackButton,this.isOpen),this.isOpen?this.form.show():this.form.hide();const e=new CustomEvent("beeport:toggle",{detail:{open:this.isOpen},bubbles:!0,composed:!0});this.hostElement.dispatchEvent(e)}updateButtonIcon(e,t){if(e.textContent="",t){const t=this.createSVGElement("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none"}),n=this.createSVGElement("path",{d:"M18 6L6 18M6 6L18 18",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"});t.appendChild(n),e.appendChild(t)}else{const t=this.createSVGElement("svg",{width:"28",height:"28",viewBox:"0 0 28 28",fill:"none"}),n=this.createSVGElement("ellipse",{cx:"14",cy:"15",rx:"6",ry:"7.5",fill:"#FFC107",stroke:"currentColor","stroke-width":"1.5"}),r=this.createSVGElement("path",{d:"M8.5 13.5 Q14 12 19.5 13.5",stroke:"#1a1a1a","stroke-width":"1.8","stroke-linecap":"round",fill:"none"}),o=this.createSVGElement("path",{d:"M8.2 16.5 Q14 15 19.8 16.5",stroke:"#1a1a1a","stroke-width":"1.8","stroke-linecap":"round",fill:"none"}),i=this.createSVGElement("ellipse",{cx:"9.5",cy:"9",rx:"3.5",ry:"4",fill:"rgba(255,255,255,0.7)",stroke:"currentColor","stroke-width":"1",transform:"rotate(-15 9.5 9)"}),a=this.createSVGElement("ellipse",{cx:"18.5",cy:"9",rx:"3.5",ry:"4",fill:"rgba(255,255,255,0.7)",stroke:"currentColor","stroke-width":"1",transform:"rotate(15 18.5 9)"}),s=this.createSVGElement("circle",{cx:"14",cy:"7.5",r:"3",fill:"#1a1a1a"}),p=this.createSVGElement("circle",{cx:"12.8",cy:"7",r:"0.8",fill:"white"}),c=this.createSVGElement("circle",{cx:"15.2",cy:"7",r:"0.8",fill:"white"}),l=this.createSVGElement("path",{d:"M12.5 5 Q11 2.5 9.5 2",stroke:"#1a1a1a","stroke-width":"1.2","stroke-linecap":"round",fill:"none"}),d=this.createSVGElement("path",{d:"M15.5 5 Q17 2.5 18.5 2",stroke:"#1a1a1a","stroke-width":"1.2","stroke-linecap":"round",fill:"none"}),u=this.createSVGElement("circle",{cx:"9.3",cy:"1.8",r:"1",fill:"#FFC107"}),b=this.createSVGElement("circle",{cx:"18.7",cy:"1.8",r:"1",fill:"#FFC107"}),h=this.createSVGElement("path",{d:"M14 22.5 L14 24.5",stroke:"#1a1a1a","stroke-width":"1.5","stroke-linecap":"round"});t.appendChild(i),t.appendChild(a),t.appendChild(n),t.appendChild(r),t.appendChild(o),t.appendChild(s),t.appendChild(p),t.appendChild(c),t.appendChild(l),t.appendChild(d),t.appendChild(u),t.appendChild(b),t.appendChild(h),e.appendChild(t)}}createSVGElement(e,t){const n=document.createElementNS("http://www.w3.org/2000/svg",e);for(const[r,o]of Object.entries(t))n.setAttribute(r,o);return n}show(){this.hostElement.style.display=""}hide(){this.hostElement.style.display="none"}destroy(){this.form.destroy(),this.themeCleanup&&(this.themeCleanup(),this.themeCleanup=null),this.hostElement.parentNode&&this.hostElement.parentNode.removeChild(this.hostElement)}setTheme(e){this.themeCleanup&&this.themeCleanup(),this.themeCleanup=t(this.container,e)}getContainer(){return this.container}}let v=null,w=null;e.destroy=function(){v&&(v.destroy(),v=null,w=null)},e.init=function(e){if(function(e){if(!e.repo)throw new Error("repo is required");if(!e.repo.includes("/")||2!==e.repo.split("/").length)throw new Error('repo must be in format "owner/repo"');if(!e.token||""===e.token.trim())throw new Error("token is required")}(e),v&&w)return w;const t={repo:e.repo,token:e.token,position:e.position??"bottom-right",theme:e.theme??"auto"};var n;return v=new y(t),n=v,w={show:()=>n.show(),hide:()=>n.hide(),destroy:()=>{n.destroy(),v=null,w=null},setTheme:e=>n.setTheme(e)},w},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
@@ -0,0 +1 @@
1
+ function e(e,t){if(e.setAttribute("data-theme",t),"auto"!==t)return()=>{};if("undefined"==typeof window||!window.matchMedia)return()=>{};const n=window.matchMedia("(prefers-color-scheme: dark)"),r=()=>{};n.addEventListener("change",r);let o=!1;return()=>{o||(n.removeEventListener("change",r),o=!0)}}function t(e,t){const r=document.createElement("div");r.className="beeport-form-panel",r.style.display="none",r.setAttribute("role","dialog"),r.setAttribute("aria-modal","true");const o=document.createElement("div");o.className="beeport-form-header";const i=document.createElement("h3");i.className="beeport-form-title",i.id="beeport-form-title",i.textContent="Report to BeePort";const a=document.createElement("p");a.className="beeport-form-subtitle",a.textContent="Help us fix bugs and shape features in under a minute.",r.setAttribute("aria-labelledby","beeport-form-title");const s=document.createElement("button");s.className="beeport-form-close",s.textContent="×",s.setAttribute("aria-label","Close");const p=document.createElement("div");p.className="beeport-form-heading",p.appendChild(i),p.appendChild(a),o.appendChild(p),o.appendChild(s);const c=document.createElement("p");c.className="beeport-type-legend",c.textContent="What are you sharing?";const l=document.createElement("div");l.className="beeport-type-selector",l.setAttribute("role","tablist"),l.setAttribute("aria-label","Select feedback type");const d=n("bug","Bug report",!0),b=n("feature","Feature idea",!1);l.appendChild(d),l.appendChild(b);const u=document.createElement("div");u.className="beeport-input-wrapper";const h=document.createElement("label");h.textContent="Title",h.htmlFor="beeport-title-input",h.className="beeport-input-label";const m=document.createElement("input");m.type="text",m.id="beeport-title-input",m.className="beeport-input-title",m.placeholder="Example: Save button does nothing on Safari";const f=document.createElement("div");f.className="beeport-input-hint beeport-input-hint-title",f.textContent="Use a short summary (3+ characters).";const g=document.createElement("div");g.id="beeport-title-error",g.className="beeport-error-message",g.setAttribute("role","alert"),u.appendChild(h),u.appendChild(m),u.appendChild(f),u.appendChild(g);const x=document.createElement("div");x.className="beeport-input-wrapper";const v=document.createElement("label");v.textContent="Description",v.htmlFor="beeport-description-input",v.className="beeport-input-label";const y=document.createElement("textarea");y.id="beeport-description-input",y.className="beeport-input-description",y.placeholder="What happened? What did you expect instead? Include steps to reproduce.",y.maxLength=1e3,y.rows=5;const w=document.createElement("div");w.className="beeport-input-hint beeport-input-hint-description",w.textContent="Include context, expected result, and actual result (10+ characters).";const k=document.createElement("div");k.className="beeport-char-counter",k.textContent="0/1000 characters";const E=document.createElement("div");E.className="beeport-description-meta",E.appendChild(w),E.appendChild(k);const C=document.createElement("div");C.id="beeport-description-error",C.className="beeport-error-message",C.setAttribute("role","alert"),x.appendChild(v),x.appendChild(y),x.appendChild(E),x.appendChild(C);const S=document.createElement("div");S.className="beeport-status-message",S.setAttribute("aria-live","polite"),S.setAttribute("aria-atomic","true"),S.textContent="";const A=document.createElement("div");A.className="beeport-form-buttons";const N=document.createElement("button");N.className="beeport-cancel-button",N.textContent="Not now";const L=document.createElement("button");L.className="beeport-submit-button",L.textContent="Create Issue",A.appendChild(N),A.appendChild(L);const B=document.createElement("div");B.className="beeport-form-footer";const $=document.createElement("a");$.href="https://beeport.ai",$.target="_blank",$.rel="noopener noreferrer",$.className="beeport-footer-link";const F=document.createElement("span");F.className="beeport-footer-bee",F.textContent="🐝";const O=document.createElement("span");O.className="beeport-footer-text",O.textContent="Powered by ";const P=document.createElement("span");P.className="beeport-footer-brand",P.textContent="BeePort",$.appendChild(F),$.appendChild(O),$.appendChild(P),B.appendChild($),r.appendChild(o),r.appendChild(c),r.appendChild(l),r.appendChild(u),r.appendChild(x),r.appendChild(S),r.appendChild(A),r.appendChild(B);const z=document.createElement("style");z.textContent='\n /* Form Panel */\n .beeport-form-panel {\n position: absolute;\n bottom: 72px;\n right: 0;\n width: min(420px, calc(100vw - 24px));\n max-height: min(80vh, 680px);\n background:\n radial-gradient(circle at top left, rgba(250, 204, 21, 0.18), transparent 55%),\n var(--beeport-bg-primary);\n border: 1px solid rgba(245, 158, 11, 0.32);\n border-radius: 18px;\n box-shadow: 0 20px 44px rgba(17, 24, 39, 0.28), 0 6px 18px rgba(17, 24, 39, 0.16);\n padding: 0;\n color: var(--beeport-text-primary);\n font-family: "Avenir Next", "Trebuchet MS", "Segoe UI", sans-serif;\n overflow: hidden;\n animation: beeport-form-enter 160ms cubic-bezier(0.22, 1, 0.36, 1);\n }\n\n /* Mobile responsive */\n @media (max-width: 500px) {\n .beeport-form-panel {\n right: 8px;\n bottom: 72px;\n width: calc(100vw - 16px);\n max-height: calc(100vh - 96px);\n border-radius: 14px;\n }\n }\n\n /* Header */\n .beeport-form-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 12px;\n padding: 18px 18px 14px;\n border-bottom: 1px solid rgba(245, 158, 11, 0.2);\n background: linear-gradient(180deg, rgba(250, 204, 21, 0.15) 0%, transparent 100%);\n }\n\n .beeport-form-heading {\n min-width: 0;\n flex: 1;\n }\n\n .beeport-form-title {\n margin: 0;\n font-size: 20px;\n font-weight: 700;\n color: var(--beeport-text-primary);\n line-height: 1.2;\n }\n\n .beeport-form-subtitle {\n margin: 6px 0 0;\n font-size: 12px;\n line-height: 1.4;\n color: var(--beeport-text-secondary);\n max-width: 32ch;\n }\n\n .beeport-form-close {\n background: var(--beeport-bg-secondary);\n border: 1px solid rgba(245, 158, 11, 0.3);\n font-size: 17px;\n line-height: 1;\n color: var(--beeport-text-primary);\n cursor: pointer;\n padding: 2px;\n width: 30px;\n height: 30px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n font-family: inherit;\n transition: all var(--beeport-transition-speed) var(--beeport-transition-easing);\n }\n\n .beeport-form-close:hover {\n border-color: #f59e0b;\n background: rgba(245, 158, 11, 0.18);\n transform: rotate(90deg);\n }\n\n .beeport-form-close:focus {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-form-close:focus:not(:focus-visible) {\n outline: none;\n }\n\n .beeport-form-close:focus-visible {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-type-legend {\n margin: 12px 18px 8px;\n color: var(--beeport-text-secondary);\n font-size: 12px;\n font-weight: 600;\n letter-spacing: 0.01em;\n }\n\n /* Type Selector */\n .beeport-type-selector {\n display: flex;\n gap: 8px;\n margin: 0 18px 14px;\n padding: 5px;\n min-height: 62px;\n border: 1px solid rgba(245, 158, 11, 0.28);\n border-radius: 12px;\n background: var(--beeport-bg-secondary);\n }\n\n .beeport-type-button {\n flex: 1;\n padding: 9px 10px;\n min-height: 52px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 9px;\n color: var(--beeport-text-secondary);\n font-size: 12px;\n font-weight: 700;\n font-family: inherit;\n letter-spacing: 0.01em;\n text-transform: none;\n cursor: pointer;\n transition: all var(--beeport-transition-speed) var(--beeport-transition-easing);\n }\n\n .beeport-type-button:last-child {\n border-right: 0;\n }\n\n .beeport-type-button:hover {\n background: rgba(245, 158, 11, 0.1);\n color: var(--beeport-text-primary);\n }\n\n .beeport-type-button.active {\n background: linear-gradient(135deg, #f59e0b, #facc15);\n color: #111827;\n box-shadow: 0 2px 8px rgba(245, 158, 11, 0.35);\n }\n\n .beeport-type-button:focus {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: -2px;\n }\n\n .beeport-type-button:focus:not(:focus-visible) {\n outline: none;\n }\n\n .beeport-type-button:focus-visible {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: -2px;\n }\n\n /* Input Wrapper */\n .beeport-input-wrapper {\n position: relative;\n padding: 0 18px 14px;\n border-bottom: none;\n }\n\n /* Input Label */\n .beeport-input-label {\n display: block;\n margin-bottom: 6px;\n color: var(--beeport-text-secondary);\n font-size: 11px;\n font-weight: 600;\n font-family: inherit;\n letter-spacing: 0.01em;\n }\n\n /* Input and Textarea */\n .beeport-input-title,\n .beeport-input-description {\n width: 100%;\n padding: 10px 12px;\n background: var(--beeport-input-bg);\n border: 1px solid rgba(107, 114, 128, 0.45);\n border-radius: 11px;\n color: var(--beeport-text-primary);\n font-size: 14px;\n font-family: inherit;\n transition: all var(--beeport-transition-speed) var(--beeport-transition-easing);\n box-sizing: border-box;\n }\n\n .beeport-input-title::placeholder,\n .beeport-input-description::placeholder {\n color: var(--beeport-text-secondary);\n opacity: 0.8;\n }\n\n .beeport-input-title:focus,\n .beeport-input-description:focus {\n outline: none;\n border-color: #f59e0b;\n box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.2);\n }\n\n .beeport-input-title:focus:not(:focus-visible),\n .beeport-input-description:focus:not(:focus-visible) {\n outline: none;\n }\n\n .beeport-input-title:focus-visible,\n .beeport-input-description:focus-visible {\n outline: none;\n border-color: #f59e0b;\n box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.2);\n }\n\n .beeport-input-description {\n resize: vertical;\n min-height: 108px;\n max-height: 240px;\n }\n\n /* Error state */\n .beeport-input-title.error,\n .beeport-input-description.error {\n border-color: #dc2626;\n box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.16);\n }\n\n .beeport-input-hint {\n margin-top: 7px;\n font-size: 11px;\n color: var(--beeport-text-secondary);\n line-height: 1.3;\n }\n\n .beeport-error-message {\n color: #dc2626;\n font-size: 12px;\n margin-top: 6px;\n min-height: 16px;\n }\n\n /* Status Message (Live Region) */\n .beeport-status-message {\n margin: 0 18px 14px;\n padding: 8px 10px;\n border-radius: 10px;\n font-size: var(--beeport-font-size-sm);\n min-height: 0;\n }\n\n .beeport-status-message:empty {\n display: none;\n }\n\n .beeport-status-message.success {\n background-color: #d1fae5;\n color: #065f46;\n border: 1px solid #10b981;\n }\n\n .beeport-status-message.error {\n background-color: #fee2e2;\n color: #991b1b;\n border: 1px solid #dc2626;\n }\n\n .beeport-description-meta {\n margin-top: 7px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n }\n\n /* Character Counter */\n .beeport-char-counter {\n font-size: 11px;\n font-family: inherit;\n color: var(--beeport-text-secondary);\n opacity: 0.8;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n transition: color var(--beeport-transition-speed) var(--beeport-transition-easing);\n }\n\n .beeport-char-counter.warning {\n color: #b45309;\n }\n\n .beeport-char-counter.limit {\n color: #dc2626;\n font-weight: 700;\n }\n\n /* Form Buttons */\n .beeport-form-buttons {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n padding: 0 18px 16px;\n }\n\n .beeport-cancel-button,\n .beeport-submit-button {\n padding: 10px 16px;\n border-radius: 10px;\n font-size: 12px;\n font-weight: 700;\n font-family: inherit;\n letter-spacing: 0.02em;\n text-transform: none;\n cursor: pointer;\n transition: all var(--beeport-transition-speed) var(--beeport-transition-easing);\n }\n\n .beeport-cancel-button {\n background: transparent;\n border: 1px solid rgba(107, 114, 128, 0.45);\n color: var(--beeport-text-primary);\n }\n\n .beeport-cancel-button:hover {\n background: rgba(107, 114, 128, 0.1);\n color: var(--beeport-text-primary);\n border-color: rgba(107, 114, 128, 0.8);\n }\n\n .beeport-cancel-button:focus {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-cancel-button:focus:not(:focus-visible) {\n outline: none;\n }\n\n .beeport-cancel-button:focus-visible {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-submit-button {\n background: linear-gradient(135deg, #f59e0b, #facc15);\n border: 1px solid #d97706;\n color: #ffffff;\n text-shadow: 0 1px 1px rgba(0, 0, 0, 0.32);\n box-shadow: 0 4px 12px rgba(245, 158, 11, 0.28);\n }\n\n .beeport-submit-button:hover {\n transform: translateY(-1px);\n box-shadow: 0 8px 16px rgba(245, 158, 11, 0.34);\n }\n\n .beeport-submit-button:active {\n background: #f59e0b;\n border-color: #B45309;\n box-shadow: 0 2px 8px rgba(245, 158, 11, 0.28);\n transform: translateY(0);\n }\n\n .beeport-submit-button:focus {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-submit-button:focus:not(:focus-visible) {\n outline: none;\n }\n\n .beeport-submit-button:focus-visible {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n /* Footer Branding */\n .beeport-form-footer {\n padding: 10px 16px 14px;\n text-align: center;\n background: linear-gradient(180deg, transparent 0%, rgba(245, 158, 11, 0.08) 100%);\n }\n\n .beeport-footer-link {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n text-decoration: none;\n color: var(--beeport-text-secondary);\n font-size: 11px;\n font-family: inherit;\n letter-spacing: 0.02em;\n transition: color var(--beeport-transition-speed) var(--beeport-transition-easing);\n }\n\n .beeport-footer-link:hover {\n color: var(--beeport-text-primary);\n }\n\n .beeport-footer-link:hover .beeport-footer-brand {\n text-decoration: underline;\n }\n\n .beeport-footer-bee {\n font-size: 11px;\n line-height: 1;\n }\n\n .beeport-footer-text {\n opacity: 0.5;\n }\n\n .beeport-footer-brand {\n font-weight: 700;\n color: #F59E0B;\n letter-spacing: 0.02em;\n }\n\n @keyframes beeport-form-enter {\n from {\n opacity: 0;\n transform: translateY(8px) scale(0.98);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n ',e.appendChild(z),e.appendChild(r);let G="bug",R=null;const T=e=>{G=e,[d,b].forEach(t=>{t.dataset.type===e?(t.classList.add("active"),t.setAttribute("aria-selected","true")):(t.classList.remove("active"),t.setAttribute("aria-selected","false"))})},V=()=>{t?.onCancel&&t.onCancel()},j=e=>{const t=e.target;"block"!==r.style.display||r.contains(t)||(r.style.display="none",I())},I=()=>{R&&(R.focus(),R=null)};return d.addEventListener("click",()=>T("bug")),b.addEventListener("click",()=>T("feature")),y.addEventListener("input",()=>{const e=y.value.length;k.textContent=`${e}/1000 characters`,k.classList.toggle("warning",e>=900&&e<1e3),k.classList.toggle("limit",e>=1e3)}),L.addEventListener("click",()=>{t?.onSubmit&&t.onSubmit()}),N.addEventListener("click",V),s.addEventListener("click",V),r.addEventListener("keydown",e=>{if("Escape"===e.key)return r.style.display="none",void I();if("Tab"===e.key){const t=(()=>{const e=r.querySelectorAll(["button:not([disabled])","input:not([disabled])","textarea:not([disabled])","select:not([disabled])",'[tabindex]:not([tabindex="-1"])'].join(","));return Array.from(e)})();if(0===t.length)return;const n=t[0],o=t[t.length-1];e.shiftKey?document.activeElement===n&&o&&(e.preventDefault(),o.focus()):document.activeElement===o&&n&&(e.preventDefault(),n.focus())}}),e.addEventListener("click",j),{show(){R=document.activeElement,r.style.display="block",m.focus()},hide(){r.style.display="none",I()},reset(){m.value="",y.value="",G="bug",T("bug"),k.textContent="0/1000 characters",k.classList.remove("warning","limit"),m.classList.remove("error"),y.classList.remove("error"),m.removeAttribute("aria-invalid"),y.removeAttribute("aria-invalid"),m.removeAttribute("aria-describedby"),y.removeAttribute("aria-describedby"),g.textContent="",C.textContent=""},destroy(){e.removeEventListener("click",j),r.remove(),z.remove()},getValues:()=>({type:G,title:m.value,description:y.value}),validate:()=>(()=>{const e={},t=m.value.trim(),n=y.value.trim();return m.classList.remove("error"),y.classList.remove("error"),m.removeAttribute("aria-invalid"),y.removeAttribute("aria-invalid"),m.removeAttribute("aria-describedby"),y.removeAttribute("aria-describedby"),g.textContent="",C.textContent="",0===t.length?e.title="Title is required":t.length<3&&(e.title="Title must be at least 3 characters"),0===n.length?e.description="Description is required":n.length<10&&(e.description="Description must be at least 10 characters"),e.title&&(m.classList.add("error"),m.setAttribute("aria-invalid","true"),m.setAttribute("aria-describedby","beeport-title-error"),g.textContent=e.title),e.description&&(y.classList.add("error"),y.setAttribute("aria-invalid","true"),y.setAttribute("aria-describedby","beeport-description-error"),C.textContent=e.description),{valid:0===Object.keys(e).length,errors:e}})()}}function n(e,t,n){const r=document.createElement("button");return r.className="beeport-type-button",r.dataset.type=e,r.textContent=t,r.setAttribute("role","tab"),r.setAttribute("aria-selected",String(n)),r.setAttribute("aria-label",{bug:"Report a bug",feature:"Request a feature",question:"Ask a question"}[e]),n&&r.classList.add("active"),r}function r(e,t){try{return e()}catch{return t}}const o=["token","key","secret","password","auth","session","api_key","access_token"];function i(e){const t=function(e){try{const t=new URL(e),n=t.searchParams;return n.forEach((e,t)=>{const r=t.toLowerCase();o.some(e=>r.includes(e))&&n.set(t,"[REDACTED]")}),decodeURIComponent(t.toString())}catch{return e}}(e.url),n=e.cookiesEnabled?"Enabled":"Disabled";return`## Environment\n- **URL**: ${t}\n- **Browser**: ${e.browser.name} ${e.browser.version}\n- **OS**: ${e.os.name} ${e.os.version}\n- **Viewport**: ${e.viewport.width}x${e.viewport.height}\n- **Screen**: ${e.screen.width}x${e.screen.height} @${e.screen.pixelRatio}x\n- **Language**: ${e.language}\n- **Cookies**: ${n}\n- **Timestamp**: ${e.timestamp}`}function a(e){if(!e||0===e.length)return"No errors captured";const t=function(e){const t=/* @__PURE__ */new Map;for(const n of e){const e=t.get(n.message);e?(e.count++,e.errors.push(n)):t.set(n.message,{errors:[n],count:1})}return t}(e),n=[];let r=1;return t.forEach(e=>{const t=e.errors[0];if(!t)return;const o=function(e){const t=["TypeError","ReferenceError","SyntaxError","RangeError","URIError","EvalError"];for(const n of t)if(e.startsWith(n))return n;return"Error"}(t.message),i=[`### Error ${r}`,`**Type**: ${o}`,`**Message**: ${t.message}`];if(e.count>1&&i.push(`**Occurrences**: (occurred ${e.count} times)`),t.stack){const e=function(e,t=20){const n=e.split("\n");if(n.length<=t)return e;const r=n.slice(0,t),o=n.length-t;return r.push(`... (${o} more lines)`),r.join("\n")}(t.stack);i.push(`**Stack trace**:\n\`\`\`\n${e}\n\`\`\``)}i.push(`**Timestamp**: ${t.timestamp}`),n.push(i.join("\n")),r++}),n.join("\n\n")}class s extends Error{constructor(e,t,n){super(e),this.status=t,this.response=n,this.name="GitHubError"}}class p extends s{constructor(e,t,n){super(e,t,n),this.name="GitHubAuthError"}}class c extends s{constructor(e,t,n){super(e,t,n),this.name="GitHubNotFoundError"}}class l extends s{constructor(e,t,n){super(e,t,n),this.name="GitHubValidationError"}}class d extends s{constructor(e,t,n,r){super(e,t,n),this.rateLimit=r,this.name="GitHubRateLimitError"}}class b extends s{constructor(e,t){super(e),this.cause=t,this.name="GitHubNetworkError"}}async function u(e){const{token:t,owner:n,repo:r,title:o,body:i,labels:a}=e,u=`https://api.github.com/repos/${n}/${r}/issues`,h={title:o,body:i};let m;a&&a.length>0&&(h.labels=a);try{m=await fetch(u,{method:"POST",headers:{Authorization:`Bearer ${t}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28","Content-Type":"application/json"},body:JSON.stringify(h)})}catch(g){throw new b("Network request failed",g instanceof Error?g:void 0)}if(!m.ok){let e;try{e=await m.json()}catch{e={}}const t=e.message||`HTTP ${m.status}`;switch(m.status){case 401:case 403:throw new p(t,m.status,e);case 404:throw new c(t,m.status,e);case 422:throw new l(t,m.status,e);case 429:{const n={limit:parseInt(m.headers.get("X-RateLimit-Limit")||"0",10),remaining:parseInt(m.headers.get("X-RateLimit-Remaining")||"0",10),reset:parseInt(m.headers.get("X-RateLimit-Reset")||"0",10)};throw new d(t,m.status,e,n)}default:throw new s(t,m.status,e)}}const f=await m.json();return{url:f.html_url,number:f.number,id:f.id}}function h(e,t){const n=e.toLowerCase().trim(),r=t.toLowerCase().trim();if(0===n.length&&0===r.length)return 1;if(0===n.length||0===r.length)return 0;const o=function(e,t){if(0===e.length)return t.length;if(0===t.length)return e.length;const n=[];for(let r=0;r<=t.length;r++)n[r]=[r];for(let r=0;r<=e.length;r++)n[0][r]=r;for(let r=1;r<=t.length;r++)for(let o=1;o<=e.length;o++)t.charAt(r-1)===e.charAt(o-1)?n[r][o]=n[r-1][o-1]:n[r][o]=Math.min(n[r-1][o-1]+1,n[r][o-1]+1,n[r-1][o]+1);return n[t.length][e.length]}(n,r);return 1-o/Math.max(n.length,r.length)}function m(e){return e instanceof p?"Authentication failed. Check your GitHub token.":e instanceof c?"Repository not found. Check the repo setting.":e instanceof d?"Rate limit exceeded. Please try again later.":e instanceof l?"Invalid issue data. Please check your input.":e instanceof b?"Network error. Check your connection.":e instanceof Error?e.message:"An unknown error occurred."}async function f(e){const{config:t,formValues:n,errors:o}=e;try{const e={url:r(()=>window.location.href,"unknown"),viewport:r(()=>({width:window.innerWidth,height:window.innerHeight}),{width:0,height:0}),screen:r(()=>({width:window.screen.width,height:window.screen.height,pixelRatio:window.devicePixelRatio}),{width:0,height:0,pixelRatio:1}),browser:r(()=>{const e=navigator.userAgentData;if(e?.brands){const t=e.brands;for(const e of t){const t=e.brand.toLowerCase();if(t.includes("chrome")&&!t.includes("chromium"))return{name:"Chrome",version:e.version};if(t.includes("edge"))return{name:"Edge",version:e.version};if(t.includes("opera"))return{name:"Opera",version:e.version}}}return function(e){if(/Edg\//.test(e)){const t=e.match(/Edg\/([\d.]+)/);return{name:"Edge",version:t?.[1]||"unknown"}}if(/OPR\//.test(e)){const t=e.match(/OPR\/([\d.]+)/);return{name:"Opera",version:t?.[1]||"unknown"}}if(/Chrome\//.test(e)&&!/Edg\//.test(e)){const t=e.match(/Chrome\/([\d.]+)/);return{name:"Chrome",version:t?.[1]||"unknown"}}if(/Firefox\//.test(e)){const t=e.match(/Firefox\/([\d.]+)/);return{name:"Firefox",version:t?.[1]||"unknown"}}if(/Safari\//.test(e)&&!/Chrome\//.test(e)){const t=e.match(/Version\/([\d.]+)/);return{name:"Safari",version:t?.[1]||"unknown"}}return{name:"Unknown",version:"unknown"}}(navigator.userAgent)},{name:"Unknown",version:"unknown"}),os:r(()=>function(e){if(/iPhone|iPad|iPod/.test(e)){const t=e.match(/OS ([\d_]+)/);return{name:"iOS",version:t?.[1]?.replace(/_/g,".")||"unknown"}}if(/Windows NT/.test(e)){const t=e.match(/Windows NT ([\d.]+)/),n=t?.[1]||"unknown";return{name:"Windows",version:{"10.0":"10",6.3:"8.1",6.2:"8",6.1:"7"}[n]||n}}if(/Mac OS X/.test(e)){const t=e.match(/Mac OS X ([\d_]+)/);return{name:"macOS",version:t?.[1]?.replace(/_/g,".")||"unknown"}}if(/Android/.test(e)){const t=e.match(/Android ([\d.]+)/);return{name:"Android",version:t?.[1]||"unknown"}}return/Linux/.test(e)?{name:"Linux",version:"unknown"}:{name:"Unknown",version:"unknown"}}(navigator.userAgent),{name:"Unknown",version:"unknown"}),timestamp:/* @__PURE__ */(new Date).toISOString(),language:r(()=>navigator.language,"unknown"),cookiesEnabled:r(()=>navigator.cookieEnabled,!1),doNotTrack:r(()=>navigator.doNotTrack,null)},c=function(e,t){return`${{bug:"[Bug]",feature:"[Feature]",question:"[Question]"}[e]} ${t}`}(n.type,n.title),l=function(e){switch(e.type){case"bug":return function(e){return[`## Summary\n${e.description}`,"## Steps to Reproduce\nNot provided","## Expected Behavior\nNot provided","## Actual Behavior\nNot provided",i(e.context),`## Console Errors\n${a(e.errors)}`].join("\n\n")+"\n---\n*Generated by [BeePort](https://beeport.ai) | agent-ready*"}(e);case"feature":return function(e){return[`## Summary\n${e.description}`,"## Use Case\nNot provided","## Proposed Solution\nNot provided",i(e.context)].join("\n\n")+"\n---\n*Generated by [BeePort](https://beeport.ai) | agent-ready*"}(e);case"question":return function(e){return[`## Question\n${e.description}`,"## Context\nNot provided",i(e.context)].join("\n\n")+"\n---\n*Generated by [BeePort](https://beeport.ai) | agent-ready*"}(e);default:throw new Error(`Unknown feedback type: ${e.type}`)}}({type:n.type,title:n.title,description:n.description,context:e,errors:o}),d=["bug"===(s=n.type)?"bug":"feature"===s?"enhancement":"question","beeport","agent-ready"],{owner:b,repo:m}=function(e){const t=e.split("/");return{owner:t[0]||"",repo:t.slice(1).join("/")}}(t.repo);let f=[];try{f=await async function(e){const{token:t,owner:n,repo:r,title:o}=e;try{const e=`https://api.github.com/repos/${n}/${r}/issues?labels=beeport&state=open`,i=await fetch(e,{method:"GET",headers:{Authorization:`Bearer ${t}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28"}});return i.ok?(await i.json()).map(e=>({number:e.number,title:e.title,url:e.html_url,similarity:h(o,e.title),state:e.state})).filter(e=>e.similarity>.7).sort((e,t)=>t.similarity-e.similarity).slice(0,5):(console.warn(`GitHub API error: ${i.status} ${i.statusText}`),[])}catch(i){return console.warn("Failed to search for duplicate issues:",i),[]}}({token:t.token,owner:b,repo:m,title:c})}catch(p){console.warn("Duplicate search failed, proceeding with submission:",p)}if(f.length>0)return{status:"duplicates_found",candidates:f,pendingSubmission:{title:c,body:l,labels:d,owner:b,repo:m,token:t.token}};const g=await u({token:t.token,owner:b,repo:m,title:c,body:l,labels:d});return{status:"success",issueUrl:g.url,issueNumber:g.number}}catch(p){return{status:"error",error:m(p)}}var s}const g=/* @__PURE__ */Object.freeze(/* @__PURE__ */Object.defineProperty({__proto__:null,confirmSubmit:async function(e){try{const t=await u({token:e.token,owner:e.owner,repo:e.repo,title:e.title,body:e.body,labels:e.labels});return{status:"success",issueUrl:t.url,issueNumber:t.number}}catch(t){return{status:"error",error:m(t)}}},submitFeedback:f},Symbol.toStringTag,{value:"Module"}));class x{constructor(n){this.isOpen=!1,this.themeCleanup=null,this.config=n,this.hostElement=document.createElement("div"),this.hostElement.setAttribute("data-beeport-container",""),this.shadowRoot=this.hostElement.attachShadow({mode:"open"}),this.container=document.createElement("div"),this.container.className="beeport-container",this.container.setAttribute("data-open","false"),this.applyPosition(n.position);const r=document.createElement("style");r.textContent='\n /* CSS Custom Properties (theme variables) */\n :host {\n --beeport-z-index: 999999;\n --beeport-spacing-sm: 8px;\n --beeport-spacing-md: 16px;\n --beeport-spacing-lg: 24px;\n --beeport-border-radius: 8px;\n --beeport-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n\n /* Typography */\n --beeport-font-size-sm: 12px;\n --beeport-font-size-md: 14px;\n --beeport-font-size-lg: 16px;\n --beeport-font-weight-normal: 400;\n --beeport-font-weight-medium: 500;\n --beeport-font-weight-bold: 600;\n\n /* Transitions */\n --beeport-transition-speed: 200ms;\n --beeport-transition-easing: ease-in-out;\n }\n\n /* Light theme colors */\n .beeport-container[data-theme="light"] {\n /* Base colors */\n --beeport-bg-primary: #ffffff;\n --beeport-bg-secondary: #f5f5f5;\n --beeport-text-primary: #1f2937;\n --beeport-text-secondary: #6b7280;\n --beeport-border-color: #d1d5db;\n --beeport-accent: #2563eb;\n\n /* Button states */\n --beeport-button-primary-bg: #2563eb;\n --beeport-button-primary-hover: #1d4ed8;\n --beeport-button-primary-active: #1e40af;\n --beeport-button-disabled-bg: #e5e7eb;\n --beeport-button-disabled-text: #4b5563;\n\n /* Input states */\n --beeport-input-bg: #ffffff;\n --beeport-input-border: #9ca3af;\n --beeport-input-focus-border: #2563eb;\n\n /* Status colors */\n --beeport-success: #047857;\n --beeport-error: #dc2626;\n --beeport-warning: #b45309;\n }\n\n /* Dark theme colors */\n .beeport-container[data-theme="dark"] {\n /* Base colors */\n --beeport-bg-primary: #1f2937;\n --beeport-bg-secondary: #111827;\n --beeport-text-primary: #f9fafb;\n --beeport-text-secondary: #9ca3af;\n --beeport-border-color: #6b7280;\n --beeport-accent: #60a5fa;\n\n /* Button states */\n --beeport-button-primary-bg: #2563eb;\n --beeport-button-primary-hover: #2563eb;\n --beeport-button-primary-active: #1d4ed8;\n --beeport-button-disabled-bg: #374151;\n --beeport-button-disabled-text: #d1d5db;\n\n /* Input states */\n --beeport-input-bg: #111827;\n --beeport-input-border: #6b7280;\n --beeport-input-focus-border: #60a5fa;\n\n /* Status colors */\n --beeport-success: #34d399;\n --beeport-error: #f87171;\n --beeport-warning: #fbbf24;\n }\n\n /* Auto theme - respect system preference */\n .beeport-container[data-theme="auto"] {\n /* Base colors */\n --beeport-bg-primary: #ffffff;\n --beeport-bg-secondary: #f5f5f5;\n --beeport-text-primary: #1f2937;\n --beeport-text-secondary: #6b7280;\n --beeport-border-color: #d1d5db;\n --beeport-accent: #2563eb;\n\n /* Button states */\n --beeport-button-primary-bg: #2563eb;\n --beeport-button-primary-hover: #1d4ed8;\n --beeport-button-primary-active: #1e40af;\n --beeport-button-disabled-bg: #e5e7eb;\n --beeport-button-disabled-text: #4b5563;\n\n /* Input states */\n --beeport-input-bg: #ffffff;\n --beeport-input-border: #9ca3af;\n --beeport-input-focus-border: #2563eb;\n\n /* Status colors */\n --beeport-success: #047857;\n --beeport-error: #dc2626;\n --beeport-warning: #b45309;\n }\n\n @media (prefers-color-scheme: dark) {\n .beeport-container[data-theme="auto"] {\n /* Base colors */\n --beeport-bg-primary: #1f2937;\n --beeport-bg-secondary: #111827;\n --beeport-text-primary: #f9fafb;\n --beeport-text-secondary: #9ca3af;\n --beeport-border-color: #6b7280;\n --beeport-accent: #60a5fa;\n\n /* Button states */\n --beeport-button-primary-bg: #2563eb;\n --beeport-button-primary-hover: #2563eb;\n --beeport-button-primary-active: #1d4ed8;\n --beeport-button-disabled-bg: #374151;\n --beeport-button-disabled-text: #d1d5db;\n\n /* Input states */\n --beeport-input-bg: #111827;\n --beeport-input-border: #6b7280;\n --beeport-input-focus-border: #60a5fa;\n\n /* Status colors */\n --beeport-success: #34d399;\n --beeport-error: #f87171;\n --beeport-warning: #fbbf24;\n }\n }\n\n /* Container base styles */\n .beeport-container {\n position: fixed;\n z-index: var(--beeport-z-index);\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n box-sizing: border-box;\n }\n\n .beeport-container *,\n .beeport-container *::before,\n .beeport-container *::after {\n box-sizing: border-box;\n }\n\n /* Reset button styles for future components */\n .beeport-container button {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n margin: 0;\n padding: 0;\n border: 0;\n background: none;\n cursor: pointer;\n }\n\n /* Feedback button - floating action button */\n .beeport-feedback-button {\n position: relative;\n width: 56px;\n height: 56px;\n border-radius: 50%;\n background: linear-gradient(135deg, #FFC107 0%, #F59E0B 50%, #D97706 100%);\n color: white;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(245, 158, 11, 0.4);\n transition: all var(--beeport-transition-speed) var(--beeport-transition-easing);\n border: none;\n cursor: pointer;\n outline: none;\n }\n\n .beeport-feedback-button:hover {\n background: linear-gradient(135deg, #FFD54F 0%, #FFC107 50%, #F59E0B 100%);\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(245, 158, 11, 0.5);\n }\n\n .beeport-feedback-button:focus {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-feedback-button:focus:not(:focus-visible) {\n outline: none;\n }\n\n .beeport-feedback-button:focus-visible {\n outline: 2px solid var(--beeport-input-focus-border);\n outline-offset: 2px;\n }\n\n .beeport-feedback-button:active {\n background: linear-gradient(135deg, #F59E0B 0%, #D97706 50%, #B45309 100%);\n transform: translateY(0);\n }\n\n .beeport-feedback-button svg {\n width: 28px;\n height: 28px;\n pointer-events: none;\n filter: drop-shadow(0 1px 1px rgba(0,0,0,0.15));\n }\n ',this.shadowRoot.appendChild(r),this.shadowRoot.appendChild(this.container),this.feedbackButton=this.createFeedbackButton(),this.container.appendChild(this.feedbackButton),this.form=t(this.container,{onSubmit:()=>{this.handleSubmit()},onCancel:()=>{this.isOpen&&this.toggleOpen()}}),document.body.appendChild(this.hostElement),this.themeCleanup=e(this.container,n.theme)}applyPosition(e){this.container.style.position="fixed",this.container.style.bottom="20px","bottom-right"===e?this.container.style.right="20px":this.container.style.left="20px"}createFeedbackButton(){const e=document.createElement("button");return e.className="beeport-feedback-button",e.setAttribute("aria-label","Send feedback"),e.setAttribute("aria-expanded","false"),e.setAttribute("tabindex","0"),this.updateButtonIcon(e,!1),e.addEventListener("click",e=>{e.stopPropagation(),this.toggleOpen()}),e.addEventListener("keydown",e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),this.toggleOpen())}),e}async handleSubmit(){if(!this.form.validate().valid)return;const e=this.form.getValues(),t=await f({config:this.config,formValues:e});if("success"===t.status)this.form.reset(),this.toggleOpen(),console.log(`[BeePort] Issue created: ${t.issueUrl}`);else if("duplicates_found"===t.status){console.log("[BeePort] Potential duplicates found, submitting anyway");const{confirmSubmit:e}=await Promise.resolve().then(()=>g),n=await e(t.pendingSubmission);"success"===n.status?(this.form.reset(),this.toggleOpen(),console.log(`[BeePort] Issue created: ${n.issueUrl}`)):"error"===n.status&&console.error(`[BeePort] Submit failed: ${n.error}`)}else"error"===t.status&&console.error(`[BeePort] Submit failed: ${t.error}`)}toggleOpen(){this.isOpen=!this.isOpen,this.container.setAttribute("data-open",String(this.isOpen)),this.feedbackButton.setAttribute("aria-expanded",String(this.isOpen)),this.updateButtonIcon(this.feedbackButton,this.isOpen),this.isOpen?this.form.show():this.form.hide();const e=new CustomEvent("beeport:toggle",{detail:{open:this.isOpen},bubbles:!0,composed:!0});this.hostElement.dispatchEvent(e)}updateButtonIcon(e,t){if(e.textContent="",t){const t=this.createSVGElement("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none"}),n=this.createSVGElement("path",{d:"M18 6L6 18M6 6L18 18",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"});t.appendChild(n),e.appendChild(t)}else{const t=this.createSVGElement("svg",{width:"28",height:"28",viewBox:"0 0 28 28",fill:"none"}),n=this.createSVGElement("ellipse",{cx:"14",cy:"15",rx:"6",ry:"7.5",fill:"#FFC107",stroke:"currentColor","stroke-width":"1.5"}),r=this.createSVGElement("path",{d:"M8.5 13.5 Q14 12 19.5 13.5",stroke:"#1a1a1a","stroke-width":"1.8","stroke-linecap":"round",fill:"none"}),o=this.createSVGElement("path",{d:"M8.2 16.5 Q14 15 19.8 16.5",stroke:"#1a1a1a","stroke-width":"1.8","stroke-linecap":"round",fill:"none"}),i=this.createSVGElement("ellipse",{cx:"9.5",cy:"9",rx:"3.5",ry:"4",fill:"rgba(255,255,255,0.7)",stroke:"currentColor","stroke-width":"1",transform:"rotate(-15 9.5 9)"}),a=this.createSVGElement("ellipse",{cx:"18.5",cy:"9",rx:"3.5",ry:"4",fill:"rgba(255,255,255,0.7)",stroke:"currentColor","stroke-width":"1",transform:"rotate(15 18.5 9)"}),s=this.createSVGElement("circle",{cx:"14",cy:"7.5",r:"3",fill:"#1a1a1a"}),p=this.createSVGElement("circle",{cx:"12.8",cy:"7",r:"0.8",fill:"white"}),c=this.createSVGElement("circle",{cx:"15.2",cy:"7",r:"0.8",fill:"white"}),l=this.createSVGElement("path",{d:"M12.5 5 Q11 2.5 9.5 2",stroke:"#1a1a1a","stroke-width":"1.2","stroke-linecap":"round",fill:"none"}),d=this.createSVGElement("path",{d:"M15.5 5 Q17 2.5 18.5 2",stroke:"#1a1a1a","stroke-width":"1.2","stroke-linecap":"round",fill:"none"}),b=this.createSVGElement("circle",{cx:"9.3",cy:"1.8",r:"1",fill:"#FFC107"}),u=this.createSVGElement("circle",{cx:"18.7",cy:"1.8",r:"1",fill:"#FFC107"}),h=this.createSVGElement("path",{d:"M14 22.5 L14 24.5",stroke:"#1a1a1a","stroke-width":"1.5","stroke-linecap":"round"});t.appendChild(i),t.appendChild(a),t.appendChild(n),t.appendChild(r),t.appendChild(o),t.appendChild(s),t.appendChild(p),t.appendChild(c),t.appendChild(l),t.appendChild(d),t.appendChild(b),t.appendChild(u),t.appendChild(h),e.appendChild(t)}}createSVGElement(e,t){const n=document.createElementNS("http://www.w3.org/2000/svg",e);for(const[r,o]of Object.entries(t))n.setAttribute(r,o);return n}show(){this.hostElement.style.display=""}hide(){this.hostElement.style.display="none"}destroy(){this.form.destroy(),this.themeCleanup&&(this.themeCleanup(),this.themeCleanup=null),this.hostElement.parentNode&&this.hostElement.parentNode.removeChild(this.hostElement)}setTheme(t){this.themeCleanup&&this.themeCleanup(),this.themeCleanup=e(this.container,t)}getContainer(){return this.container}}let v=null,y=null;function w(e){if(function(e){if(!e.repo)throw new Error("repo is required");if(!e.repo.includes("/")||2!==e.repo.split("/").length)throw new Error('repo must be in format "owner/repo"');if(!e.token||""===e.token.trim())throw new Error("token is required")}(e),v&&y)return y;const t={repo:e.repo,token:e.token,position:e.position??"bottom-right",theme:e.theme??"auto"};var n;return v=new x(t),n=v,y={show:()=>n.show(),hide:()=>n.hide(),destroy:()=>{n.destroy(),v=null,y=null},setTheme:e=>n.setTheme(e)},y}function k(){v&&(v.destroy(),v=null,y=null)}export{k as destroy,w as init};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Browser context capture module
3
+ * Captures technical context from the browser environment
4
+ */
5
+ import type { BrowserContext } from './types';
6
+ /**
7
+ * Capture all browser context in one call
8
+ * Returns comprehensive browser environment information
9
+ */
10
+ export declare function captureBrowserContext(): BrowserContext;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Duplicate confirmation UI component
3
+ * Shows similar issues list with title, number, similarity badge
4
+ */
5
+ import type { DuplicateCandidate } from './dedup';
6
+ export interface DuplicatePanelOptions {
7
+ candidates: DuplicateCandidate[];
8
+ onSubmitAnyway: () => void;
9
+ onCancel: () => void;
10
+ }
11
+ export interface DuplicatePanel {
12
+ show: () => void;
13
+ hide: () => void;
14
+ destroy: () => void;
15
+ }
16
+ export declare function createDuplicatePanel(shadowRoot: ShadowRoot, options: DuplicatePanelOptions): DuplicatePanel;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Duplicate issue detection with fuzzy matching
3
+ */
4
+ export interface DedupOptions {
5
+ token: string;
6
+ owner: string;
7
+ repo: string;
8
+ title: string;
9
+ }
10
+ export interface DuplicateCandidate {
11
+ number: number;
12
+ title: string;
13
+ url: string;
14
+ similarity: number;
15
+ state: string;
16
+ }
17
+ /**
18
+ * Calculate the Levenshtein distance between two strings
19
+ * @param a First string
20
+ * @param b Second string
21
+ * @returns Number of edits required to transform a into b
22
+ */
23
+ export declare function levenshteinDistance(a: string, b: string): number;
24
+ /**
25
+ * Search for duplicate issues using fuzzy matching
26
+ * @param options Search options including token, owner, repo, and title
27
+ * @returns Array of duplicate candidates sorted by similarity (top 5)
28
+ */
29
+ export declare function searchDuplicates(options: DedupOptions): Promise<DuplicateCandidate[]>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Error capture module
3
+ * Captures window errors and unhandled promise rejections
4
+ */
5
+ /**
6
+ * Captured error information
7
+ */
8
+ export interface CapturedError {
9
+ /** Error message */
10
+ message: string;
11
+ /** Stack trace (null if unavailable) */
12
+ stack: string | null;
13
+ /** Source file and line number (null if unavailable) */
14
+ source: string | null;
15
+ /** Timestamp when error was captured (ISO 8601) */
16
+ timestamp: string;
17
+ /** Type of error */
18
+ type: 'error' | 'unhandled-rejection';
19
+ }
20
+ /**
21
+ * Handle returned by startErrorCapture
22
+ * Used to stop error capture
23
+ */
24
+ export interface ErrorCaptureHandle {
25
+ /** Internal cleanup function */
26
+ _cleanup: () => void;
27
+ }
28
+ /**
29
+ * Safe JSON stringify that handles circular references
30
+ * Replaces circular references with "[Circular]"
31
+ */
32
+ export declare function safeStringify(obj: any): string;
33
+ /**
34
+ * Start capturing errors from window.onerror and window.onunhandledrejection
35
+ * Returns a handle that can be used to stop capture
36
+ */
37
+ export declare function startErrorCapture(): ErrorCaptureHandle;
38
+ /**
39
+ * Stop capturing errors
40
+ * Removes event listeners and restores previous handlers
41
+ */
42
+ export declare function stopErrorCapture(handle: ErrorCaptureHandle): void;
43
+ /**
44
+ * Get recently captured errors
45
+ * Returns array of up to 10 most recent errors
46
+ */
47
+ export declare function getRecentErrors(): CapturedError[];
48
+ /**
49
+ * Clear all captured errors from buffer
50
+ */
51
+ export declare function clearErrors(): void;
package/dist/form.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Feedback form component
3
+ */
4
+ import type { FeedbackType } from './types';
5
+ export interface FormValues {
6
+ type: FeedbackType;
7
+ title: string;
8
+ description: string;
9
+ }
10
+ export interface FormCallbacks {
11
+ onSubmit?: () => void;
12
+ onCancel?: () => void;
13
+ }
14
+ export interface ValidationResult {
15
+ valid: boolean;
16
+ errors: Record<string, string>;
17
+ }
18
+ export interface FormComponent {
19
+ show(): void;
20
+ hide(): void;
21
+ reset(): void;
22
+ destroy(): void;
23
+ getValues(): FormValues;
24
+ validate(): ValidationResult;
25
+ }
26
+ /**
27
+ * Create feedback form component
28
+ */
29
+ export declare function createForm(parent: HTMLElement | ShadowRoot, callbacks?: FormCallbacks): FormComponent;
30
+ /**
31
+ * Get form styles
32
+ */
33
+ export declare function getFormStyles(): string;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Issue body formatter module
3
+ * Generates agent-ready markdown for GitHub/GitLab issues
4
+ */
5
+ import type { BrowserContext } from './types';
6
+ /**
7
+ * Feedback type - determines issue format
8
+ */
9
+ export type FeedbackType = 'bug' | 'feature' | 'question';
10
+ /**
11
+ * Captured error from console
12
+ */
13
+ export interface CapturedError {
14
+ message: string;
15
+ stack?: string;
16
+ timestamp: string;
17
+ }
18
+ /**
19
+ * Options for formatting an issue body
20
+ */
21
+ export interface FormatOptions {
22
+ type: FeedbackType;
23
+ title: string;
24
+ description: string;
25
+ context: BrowserContext;
26
+ errors?: CapturedError[];
27
+ }
28
+ /**
29
+ * Sanitizes a URL by replacing sensitive query parameter values with [REDACTED]
30
+ * @param url - URL to sanitize
31
+ * @returns Sanitized URL with sensitive values redacted
32
+ */
33
+ export declare function sanitizeUrl(url: string): string;
34
+ /**
35
+ * Builds a formatted issue title with type prefix
36
+ */
37
+ export declare function buildIssueTitle(type: FeedbackType, title: string): string;
38
+ /**
39
+ * Builds a complete issue body in markdown format
40
+ * Format varies based on feedback type (bug, feature, question)
41
+ */
42
+ export declare function buildIssueBody(options: FormatOptions): string;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * GitHub API client for creating issues
3
+ */
4
+ export interface CreateIssueOptions {
5
+ token: string;
6
+ owner: string;
7
+ repo: string;
8
+ title: string;
9
+ body: string;
10
+ labels?: string[];
11
+ }
12
+ export interface CreateIssueResult {
13
+ url: string;
14
+ number: number;
15
+ id: number;
16
+ }
17
+ export declare class GitHubError extends Error {
18
+ status?: number | undefined;
19
+ response?: any | undefined;
20
+ constructor(message: string, status?: number | undefined, response?: any | undefined);
21
+ }
22
+ export declare class GitHubAuthError extends GitHubError {
23
+ constructor(message: string, status: number, response?: any);
24
+ }
25
+ export declare class GitHubNotFoundError extends GitHubError {
26
+ constructor(message: string, status: number, response?: any);
27
+ }
28
+ export declare class GitHubValidationError extends GitHubError {
29
+ constructor(message: string, status: number, response?: any);
30
+ }
31
+ export declare class GitHubRateLimitError extends GitHubError {
32
+ rateLimit?: {
33
+ limit: number;
34
+ remaining: number;
35
+ reset: number;
36
+ } | undefined;
37
+ constructor(message: string, status: number, response?: any, rateLimit?: {
38
+ limit: number;
39
+ remaining: number;
40
+ reset: number;
41
+ } | undefined);
42
+ }
43
+ export declare class GitHubNetworkError extends GitHubError {
44
+ cause?: Error | undefined;
45
+ constructor(message: string, cause?: Error | undefined);
46
+ }
47
+ export declare function createIssue(options: CreateIssueOptions): Promise<CreateIssueResult>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * BeePort Widget
3
+ * Embeddable feedback widget for capturing user issues
4
+ */
5
+ import type { BeePortConfig, BeePortInstance } from './types';
6
+ export type { BeePortConfig, BeePortInstance, Position, Theme } from './types';
7
+ /**
8
+ * Initialize the BeePort widget
9
+ */
10
+ export declare function init(config: BeePortConfig): BeePortInstance;
11
+ /**
12
+ * Destroy the widget
13
+ */
14
+ export declare function destroy(): void;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Label auto-assignment logic for BeePort feedback
3
+ */
4
+ /**
5
+ * Type of feedback being submitted
6
+ */
7
+ export type FeedbackType = 'bug' | 'feature' | 'question';
8
+ /**
9
+ * Get labels for a given feedback type
10
+ *
11
+ * @param type - The type of feedback (bug, feature, or question)
12
+ * @returns Array of labels in consistent order: type-specific, beeport, agent-ready
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * getLabelsForFeedback('bug');
17
+ * // => ['bug', 'beeport', 'agent-ready']
18
+ *
19
+ * getLabelsForFeedback('feature');
20
+ * // => ['enhancement', 'beeport', 'agent-ready']
21
+ * ```
22
+ */
23
+ export declare function getLabelsForFeedback(type: FeedbackType): string[];
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Widget styles (injected into Shadow DOM)
3
+ */
4
+ export declare function getStyles(): string;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Form submission orchestration module
3
+ * Coordinates the full flow from form data to GitHub issue creation
4
+ */
5
+ import type { BeePortConfig } from './types';
6
+ import type { FormValues } from './form';
7
+ import { type CapturedError } from './formatter';
8
+ import { type DuplicateCandidate } from './dedup';
9
+ /**
10
+ * Options for submitting feedback
11
+ */
12
+ export interface SubmitOptions {
13
+ config: BeePortConfig;
14
+ formValues: FormValues;
15
+ errors?: CapturedError[];
16
+ }
17
+ /**
18
+ * Pending submission data for confirmation after duplicate detection
19
+ */
20
+ export interface PendingSubmission {
21
+ title: string;
22
+ body: string;
23
+ labels: string[];
24
+ owner: string;
25
+ repo: string;
26
+ token: string;
27
+ }
28
+ /**
29
+ * Result of feedback submission
30
+ */
31
+ export type SubmitResult = {
32
+ status: 'success';
33
+ issueUrl: string;
34
+ issueNumber: number;
35
+ } | {
36
+ status: 'duplicates_found';
37
+ candidates: DuplicateCandidate[];
38
+ pendingSubmission: PendingSubmission;
39
+ } | {
40
+ status: 'error';
41
+ error: string;
42
+ };
43
+ /**
44
+ * Submit feedback and create a GitHub issue
45
+ *
46
+ * Orchestrates the full submission flow:
47
+ * 1. Capture browser context
48
+ * 2. Build issue title
49
+ * 3. Build issue body
50
+ * 4. Get labels
51
+ * 5. Parse owner/repo
52
+ * 6. Search for duplicates
53
+ * 7. If duplicates found, return them for user confirmation
54
+ * 8. If no duplicates, create GitHub issue
55
+ *
56
+ * @param options - Submission options
57
+ * @returns Result with issue URL and number, duplicates found, or error
58
+ */
59
+ export declare function submitFeedback(options: SubmitOptions): Promise<SubmitResult>;
60
+ /**
61
+ * Confirm submission after user has reviewed duplicate candidates
62
+ *
63
+ * @param pendingSubmission - Pre-computed submission data
64
+ * @returns Result with issue URL and number, or error
65
+ */
66
+ export declare function confirmSubmit(pendingSubmission: PendingSubmission): Promise<SubmitResult>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Theme management system
3
+ * Handles theme switching and auto mode with system preference watching
4
+ */
5
+ import type { Theme } from './types';
6
+ /**
7
+ * Apply theme to container and watch for system preference changes if auto mode
8
+ * @param container - The container element to apply theme to
9
+ * @param theme - Theme to apply ('light' | 'dark' | 'auto')
10
+ * @returns Cleanup function to stop watching system preference changes
11
+ */
12
+ export declare function createThemeWatcher(container: HTMLElement, theme: Theme): () => void;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * BeePort TypeScript type definitions
3
+ */
4
+ export type Position = 'bottom-right' | 'bottom-left';
5
+ export type Theme = 'light' | 'dark' | 'auto';
6
+ export type FeedbackType = 'bug' | 'feature' | 'question';
7
+ /**
8
+ * Configuration for initializing the BeePort widget
9
+ */
10
+ export interface BeePortConfig {
11
+ /**
12
+ * GitHub repository in "owner/repo" format
13
+ */
14
+ repo: string;
15
+ /**
16
+ * GitHub Personal Access Token (PAT)
17
+ */
18
+ token: string;
19
+ /**
20
+ * Position of the widget on the page
21
+ * @default "bottom-right"
22
+ */
23
+ position?: Position;
24
+ /**
25
+ * Theme for the widget
26
+ * @default "auto"
27
+ */
28
+ theme?: Theme;
29
+ }
30
+ /**
31
+ * Instance returned by BeePort.init()
32
+ */
33
+ export interface BeePortInstance {
34
+ /**
35
+ * Show the widget
36
+ */
37
+ show(): void;
38
+ /**
39
+ * Hide the widget
40
+ */
41
+ hide(): void;
42
+ /**
43
+ * Destroy the widget and remove it from DOM
44
+ */
45
+ destroy(): void;
46
+ /**
47
+ * Change the widget theme
48
+ */
49
+ setTheme(theme: Theme): void;
50
+ }
51
+ /**
52
+ * Browser context captured from the user's environment
53
+ */
54
+ export interface BrowserContext {
55
+ /** Current page URL */
56
+ url: string;
57
+ /** Viewport dimensions */
58
+ viewport: {
59
+ width: number;
60
+ height: number;
61
+ };
62
+ /** Screen dimensions and pixel ratio */
63
+ screen: {
64
+ width: number;
65
+ height: number;
66
+ pixelRatio: number;
67
+ };
68
+ /** Browser information */
69
+ browser: {
70
+ name: string;
71
+ version: string;
72
+ };
73
+ /** Operating system information */
74
+ os: {
75
+ name: string;
76
+ version: string;
77
+ };
78
+ /** Timestamp when context was captured (ISO 8601) */
79
+ timestamp: string;
80
+ /** Browser language */
81
+ language: string;
82
+ /** Whether cookies are enabled */
83
+ cookiesEnabled: boolean;
84
+ /** Do Not Track setting */
85
+ doNotTrack: string | null;
86
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Widget container with Shadow DOM
3
+ */
4
+ import type { BeePortConfig, Theme } from './types';
5
+ /**
6
+ * Widget container class
7
+ * Manages Shadow DOM creation, lifecycle, and visibility
8
+ */
9
+ export declare class Widget {
10
+ private hostElement;
11
+ private shadowRoot;
12
+ private container;
13
+ private feedbackButton;
14
+ private isOpen;
15
+ private themeCleanup;
16
+ private form;
17
+ private config;
18
+ constructor(config: Required<BeePortConfig>);
19
+ private applyPosition;
20
+ private createFeedbackButton;
21
+ private handleSubmit;
22
+ private toggleOpen;
23
+ private updateButtonIcon;
24
+ private createSVGElement;
25
+ show(): void;
26
+ hide(): void;
27
+ destroy(): void;
28
+ setTheme(theme: Theme): void;
29
+ getContainer(): HTMLElement;
30
+ }
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "beeport",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Embeddable feedback widget that turns user feedback into agent-ready GitHub issues",
6
+ "main": "./dist/beeport.min.js",
7
+ "module": "./dist/beeport.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/beeport.mjs",
13
+ "require": "./dist/beeport.min.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "dev": "vite",
21
+ "typecheck": "tsc --noEmit",
22
+ "build": "vite build && tsc --emitDeclarationOnly --outDir dist",
23
+ "build:check-size": "node scripts/check-size.js",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "test:coverage": "vitest run --coverage"
27
+ },
28
+ "keywords": [
29
+ "feedback",
30
+ "widget",
31
+ "github",
32
+ "issues",
33
+ "bug-report",
34
+ "agent-ready",
35
+ "shadow-dom"
36
+ ],
37
+ "author": "BeePort Contributors",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/srstomp/beeport.git",
42
+ "directory": "packages/widget"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/srstomp/beeport/issues"
46
+ },
47
+ "homepage": "https://github.com/srstomp/beeport#readme",
48
+ "engines": {
49
+ "node": ">=18"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ }
54
+ }