@mediansh/widget 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.
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ "use client";
3
+ import {
4
+ At,
5
+ Li,
6
+ R
7
+ } from "./chunk-6E3NBXLE.js";
8
+
9
+ // ../../node_modules/.bun/streamdown@2.5.0+005eabf3d8b6ef06/node_modules/streamdown/dist/highlighted-body-OFNGDK62.js
10
+ import { useContext, useState, useEffect } from "react";
11
+ import { jsx } from "react/jsx-runtime";
12
+ var R2 = ({ code: s, language: e, raw: t, className: h, startLine: d, lineNumbers: m, ...p }) => {
13
+ let { shikiTheme: l } = useContext(R), o = Li(), [a, i] = useState(t);
14
+ return useEffect(() => {
15
+ if (!o) {
16
+ i(t);
17
+ return;
18
+ }
19
+ let r = o.highlight({ code: s, language: e, themes: l }, (c) => {
20
+ i(c);
21
+ });
22
+ r && i(r);
23
+ }, [s, e, l, o, t]), jsx(At, { className: h, language: e, lineNumbers: m, result: a, startLine: d, ...p });
24
+ };
25
+ export {
26
+ R2 as HighlightedCodeBlockBody
27
+ };
28
+ //# sourceMappingURL=highlighted-body-OFNGDK62-UN447WAU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../node_modules/.bun/streamdown@2.5.0+005eabf3d8b6ef06/node_modules/streamdown/dist/highlighted-body-OFNGDK62.js"],"sourcesContent":["\"use client\";\nimport {A,f,c}from'./chunk-BO2N2NFS.js';import {useContext,useState,useEffect}from'react';import {jsx}from'react/jsx-runtime';var R=({code:s,language:e,raw:t,className:h,startLine:d,lineNumbers:m,...p})=>{let{shikiTheme:l}=useContext(A),o=f(),[a,i]=useState(t);return useEffect(()=>{if(!o){i(t);return}let r=o.highlight({code:s,language:e,themes:l},c=>{i(c);});r&&i(r);},[s,e,l,o,t]),jsx(c,{className:h,language:e,lineNumbers:m,result:a,startLine:d,...p})};export{R as HighlightedCodeBlockBody};"],"mappings":";;;;;;;;;AACwC,SAAQ,YAAW,UAAS,iBAAc;AAAQ,SAAQ,WAAQ;AAAoB,IAAIA,KAAE,CAAC,EAAC,MAAK,GAAE,UAAS,GAAE,KAAI,GAAE,WAAU,GAAE,WAAU,GAAE,aAAY,GAAE,GAAG,EAAC,MAAI;AAAC,MAAG,EAAC,YAAW,EAAC,IAAE,WAAW,CAAC,GAAE,IAAE,GAAE,GAAE,CAAC,GAAE,CAAC,IAAE,SAAS,CAAC;AAAE,SAAO,UAAU,MAAI;AAAC,QAAG,CAAC,GAAE;AAAC,QAAE,CAAC;AAAE;AAAA,IAAM;AAAC,QAAI,IAAE,EAAE,UAAU,EAAC,MAAK,GAAE,UAAS,GAAE,QAAO,EAAC,GAAE,OAAG;AAAC,QAAE,CAAC;AAAA,IAAE,CAAC;AAAE,SAAG,EAAE,CAAC;AAAA,EAAE,GAAE,CAAC,GAAE,GAAE,GAAE,GAAE,CAAC,CAAC,GAAE,IAAI,IAAE,EAAC,WAAU,GAAE,UAAS,GAAE,aAAY,GAAE,QAAO,GAAE,WAAU,GAAE,GAAG,EAAC,CAAC;AAAC;","names":["R"]}
@@ -0,0 +1,525 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * The customer facing side of Median. These types are the shape the SDK will
6
+ * hand the widget, so they stay free of anything Convex or Clerk specific: a
7
+ * host app should be able to build one of these from its own data.
8
+ */
9
+
10
+ /**
11
+ * What is on the page while the panel is closed.
12
+ *
13
+ * `bubble` is the button in the corner, and is what a widget is. `hidden`
14
+ * leaves the page alone entirely, for a host who opens support from their own
15
+ * nav, their own button, or their own keyboard shortcut. Either way the panel
16
+ * is one call away, and comes up out of the same corner.
17
+ */
18
+ type SupportLauncherKind = "bubble" | "hidden";
19
+
20
+ /**
21
+ * What has been going wrong on the page, kept ready in case somebody asks
22
+ * for help about it.
23
+ *
24
+ * A support agent's first three questions are always the same: what were you
25
+ * doing, what did it say, and what does the console show. The customer can
26
+ * answer the first one. This answers the other two, so a bug report arrives
27
+ * with the error already attached rather than after four messages of asking.
28
+ *
29
+ * Off unless a host switches it on. Everything here reads their program's
30
+ * error stream, which is theirs to offer rather than ours to take.
31
+ *
32
+ * A bounded ring buffer and nothing else. Nothing is sent on its own, nothing
33
+ * is stored, and nothing is polled: the newest snapshot rides the next message
34
+ * the customer sends, and only when there is something new in it.
35
+ */
36
+ /** What a host can switch on. `true` is the passive half. */
37
+ type MedianDiagnostics = boolean | {
38
+ /** Uncaught errors and rejected promises. On whenever diagnostics are. */
39
+ errors?: boolean;
40
+ /** Requests that failed or came back unhappy. Wraps window.fetch. */
41
+ network?: boolean;
42
+ /** console.error calls. Wraps console.error. */
43
+ console?: boolean;
44
+ };
45
+
46
+ /** What a host can tell us about who is in the widget. All of it optional. */
47
+ type MedianUser = {
48
+ /**
49
+ * Your own id for this person, and that id signed from MEDIAN_KEY.
50
+ *
51
+ * The pair is what makes a conversation follow somebody rather than a
52
+ * browser: sign in on a phone and the thread from the laptop is already
53
+ * there. Both or neither, and the signing belongs on your server, because
54
+ * whoever holds the key can claim to be any of your customers.
55
+ *
56
+ * Without them the widget still works. It is just this browser's thread,
57
+ * which is what it has always been.
58
+ */
59
+ id?: string;
60
+ hash?: string;
61
+ name?: string;
62
+ email?: string;
63
+ /**
64
+ * Their profile picture, so the team sees who they are talking to rather
65
+ * than a letter. Has to be an https URL; anything else is ignored.
66
+ */
67
+ avatarUrl?: string;
68
+ /** Anything else worth showing the team: plan, company, whatever. */
69
+ metadata?: Record<string, string>;
70
+ };
71
+
72
+ type MedianSupportProps = {
73
+ apiKey: string;
74
+ /** Who is signed in on the host's site, if anyone. Shown to the team. */
75
+ user?: MedianUser;
76
+ /**
77
+ * Whether a message carries what the browser already knows: time zone,
78
+ * language, browser, OS, device, screen, and the page it was sent from.
79
+ * On by default, because a team that has to ask for all six spends its
80
+ * first reply asking. Pass `false` and none of it is read at all.
81
+ */
82
+ telemetry?: boolean;
83
+ /**
84
+ * Take questions from signed in people only.
85
+ *
86
+ * Until `user` says somebody is there, the panel asks them to sign in
87
+ * instead of offering a composer, and no conversation is opened or read.
88
+ * Pass `user` the moment you have it and the thread takes over.
89
+ *
90
+ * A rule about your own page, kept on your own page: it decides what this
91
+ * widget offers, not what the API accepts. `user.id` with a signed `hash`
92
+ * is the part nobody can talk their way around.
93
+ */
94
+ requireSignIn?: boolean;
95
+ /**
96
+ * Where your sign in lives, for the button on that panel.
97
+ *
98
+ * <MedianSupport
99
+ * apiKey="median_pk_..."
100
+ * requireSignIn
101
+ * onSignIn={() => router.push("/login")}
102
+ * />
103
+ *
104
+ * Without it the panel says the same thing and offers no button, which is
105
+ * right for a site whose sign in is already the first thing on the page.
106
+ */
107
+ onSignIn?: () => void;
108
+ /**
109
+ * Ask for an email before a visitor's first message. On by default: an
110
+ * anonymous thread cannot be followed up, and half of support is getting
111
+ * back to somebody. Pass `false` and the composer opens straight away.
112
+ *
113
+ * The composer asks once per browser; the answer rides their first message
114
+ * and lands with their details on your side. A visitor your `user` already
115
+ * names an email for is never asked, and `medianSupport.reset()` asks the
116
+ * next person instead.
117
+ *
118
+ * Your team can turn the question off from the dashboard without a
119
+ * redeploy, and either side saying no is enough to keep it away.
120
+ */
121
+ requireEmail?: boolean;
122
+ /**
123
+ * Watch what goes wrong on the page, so a bug report arrives with the
124
+ * error already attached instead of after four messages of asking.
125
+ *
126
+ * <MedianSupport apiKey="median_pk_..." diagnostics />
127
+ *
128
+ * `true` listens for uncaught errors and rejected promises, which changes
129
+ * nothing about how your page runs. The object form adds the two that wrap
130
+ * something of yours:
131
+ *
132
+ * diagnostics={{ network: true, console: true }}
133
+ *
134
+ * `network` wraps window.fetch to note requests that failed, `console`
135
+ * wraps console.error. Both call through to what they replaced, and both
136
+ * are put back when the widget leaves the page.
137
+ *
138
+ * Off by default: this reads your program's error stream, which is yours
139
+ * to offer rather than ours to take. Nothing is sent on its own. The
140
+ * newest snapshot rides the next message the customer sends, and only when
141
+ * something new is in it.
142
+ */
143
+ diagnostics?: MedianDiagnostics;
144
+ /**
145
+ * Anything about your own app worth having next to a bug report: a build,
146
+ * a route, a flag. Read when a message is sent, so it can change as they
147
+ * move around.
148
+ *
149
+ * <MedianSupport
150
+ * apiKey="median_pk_..."
151
+ * diagnostics
152
+ * appState={{ version: BUILD_SHA, plan: user.plan }}
153
+ * />
154
+ *
155
+ * Twenty entries at most, and each one a line rather than a document.
156
+ */
157
+ appState?: Record<string, string | number | boolean>;
158
+ /** Our button in the corner while it is closed, or nothing at all. */
159
+ launcher?: SupportLauncherKind;
160
+ /** Pass it, with `onOpenChange`, to hold the open state yourself. */
161
+ open?: boolean;
162
+ /** Every open and close, whoever asked for it. */
163
+ onOpenChange?: (open: boolean) => void;
164
+ /**
165
+ * Take the visitor to a page, when one of your own tools asks for it.
166
+ *
167
+ * <MedianSupport apiKey="median_pk_..." onNavigate={router.push} />
168
+ *
169
+ * Called with a path on your own site, already checked against this page's
170
+ * origin. Your router decides what happens: pass `router.push` and the
171
+ * conversation stays open across the move.
172
+ *
173
+ * Without it a tool that asks to navigate cannot, and says so in the
174
+ * console while you are building. Nothing else needs it.
175
+ */
176
+ onNavigate?: (path: string) => void;
177
+ };
178
+ /**
179
+ * The whole SDK, as one component. Drop it on a page with your publishable API
180
+ * key and the launcher, the thread, the AI and the inbox on the other side are
181
+ * all wired up:
182
+ *
183
+ * <MedianSupport apiKey="median_pk_..." />
184
+ *
185
+ * Optionally say who is signed in, and the team sees a person rather than
186
+ * another anonymous visitor:
187
+ *
188
+ * <MedianSupport
189
+ * apiKey="median_pk_..."
190
+ * user={{ name: "Ada", email: "ada@example.com" }}
191
+ * />
192
+ *
193
+ * There are three states rather than two: open, closed as a button in the
194
+ * corner, and not on the page at all. Take the corner back and open it from
195
+ * your own nav, from anywhere in your app, with no state to thread through:
196
+ *
197
+ * <MedianSupport apiKey="median_pk_..." launcher="hidden" />
198
+ *
199
+ * const support = useMedianSupport();
200
+ * <button onClick={support.open}>Contact us</button>
201
+ *
202
+ * On a site nobody uses signed out, say so and the panel asks them to sign in
203
+ * rather than opening a conversation with a stranger:
204
+ *
205
+ * <MedianSupport
206
+ * apiKey="median_pk_..."
207
+ * user={session?.user}
208
+ * requireSignIn
209
+ * onSignIn={() => router.push("/login")}
210
+ * />
211
+ *
212
+ * When somebody signs out, `medianSupport.reset()` forgets the conversation
213
+ * along with them, so the next person at the keyboard starts fresh.
214
+ *
215
+ * That is the surface, on purpose. Everything else a support widget could be
216
+ * asked to do is either our decision or a setting on the dashboard, so it can
217
+ * change without every host having to redeploy.
218
+ *
219
+ * It brings its own connection, so it works on pages (and sites) that have
220
+ * never heard of Convex.
221
+ */
222
+ declare function MedianSupport(props: MedianSupportProps): react.JSX.Element;
223
+
224
+ /**
225
+ * What a mounted widget hands back: the two things a host without React has
226
+ * no re-render to do for them. Everything else goes through `medianSupport`,
227
+ * same as anywhere.
228
+ */
229
+ type MedianSupportInstance = {
230
+ /**
231
+ * Change any option after mount. Only the keys passed change, so
232
+ * `update({ user })` when somebody signs in leaves the rest alone.
233
+ */
234
+ update: (options: Partial<MedianSupportProps>) => void;
235
+ /** Take the widget off the page: panel, launcher, connection, all of it. */
236
+ unmount: () => void;
237
+ };
238
+ /**
239
+ * The same widget, for a page that has never heard of React: Vue, Svelte,
240
+ * Rails, a script on plain HTML. One call puts the launcher in the corner,
241
+ * wired to the same panel, thread and inbox as the component:
242
+ *
243
+ * const support = mountMedianSupport({ apiKey: "median_pk_..." });
244
+ *
245
+ * It takes the component's props, as one options object, and the rest of the
246
+ * SDK does not know the difference: `medianSupport.open()` opens this widget
247
+ * exactly as it opens the component.
248
+ *
249
+ * What would have been new props on a re-render is `update`:
250
+ *
251
+ * support.update({ user: { name: "Ada", email: "ada@example.com" } });
252
+ *
253
+ * It renders into a container of its own, appended to `<body>`, with the
254
+ * package's copy of React. The host's page needs none and never sees it.
255
+ */
256
+ declare function mountMedianSupport(options: MedianSupportProps): MedianSupportInstance;
257
+
258
+ /**
259
+ * Filing the crash the visitor is looking at.
260
+ *
261
+ * Every other send in the SDK starts with somebody typing. This one starts
262
+ * with a page that has stopped working, which is the report nobody writes:
263
+ * whoever was using it can see that it broke and has nothing to add about
264
+ * why. So the error describes itself, the browser hands over what it was
265
+ * doing, and one press files it.
266
+ *
267
+ * It brings its own connection and reads its key from the last widget that
268
+ * mounted, because by the time this runs the tree that widget lived in has
269
+ * usually been replaced by the error screen calling it. Nothing here needs a
270
+ * component, a provider, or a panel on the page.
271
+ *
272
+ * It never throws. A crash screen that crashes reporting a crash is a poor
273
+ * apology, so everything that can go wrong comes back as a reason instead.
274
+ */
275
+ type MedianErrorReportOptions = {
276
+ /**
277
+ * The key, when no widget has mounted on this page to remember it. Both
278
+ * panels write theirs down as they mount, so an app with either one
279
+ * installed can leave this alone.
280
+ */
281
+ apiKey?: string;
282
+ /** Who is in the chair. Defaults to whoever the mounted widget was given. */
283
+ user?: MedianUser;
284
+ /**
285
+ * Where it happened, when your router names the page better than its URL
286
+ * does: "/orders/[id]" rather than "/orders/8912". It leads the title, so
287
+ * the same crash on the same route is one row on the list however many
288
+ * people hit it. Defaults to the path in the address bar.
289
+ */
290
+ route?: string;
291
+ /** React's component stack, when your boundary was handed one. */
292
+ componentStack?: string;
293
+ /** Facts to put on the report, the same shape as `appState`. */
294
+ state?: Record<string, string | number | boolean>;
295
+ /** Blocks to attach, the same shape and limits as `attach`. */
296
+ attach?: Record<string, unknown>;
297
+ /** Whether the report carries what the browser knows. On by default. */
298
+ telemetry?: boolean;
299
+ };
300
+ /** Where the report went, or why it did not go. */
301
+ type MedianErrorReportOutcome = {
302
+ reported: true;
303
+ /** True when somebody had already reported this one. */
304
+ joined: boolean;
305
+ } | {
306
+ reported: false;
307
+ reason: string;
308
+ };
309
+ declare function reportError(error: unknown, options?: MedianErrorReportOptions): Promise<MedianErrorReportOutcome>;
310
+ /**
311
+ * Whether a report has anywhere to go: a widget has mounted on this page and
312
+ * left its key, and this build knows which Median to send to.
313
+ *
314
+ * For a "Report error" button, which should not be on the screen at all if
315
+ * pressing it could only ever fail. It follows the install rather than
316
+ * reading it once, since an error screen can render before the widget does.
317
+ */
318
+ declare function useCanReportError(): boolean;
319
+
320
+ /**
321
+ * The widget from anywhere, React or not: the same shape the hook hands
322
+ * back, for a plain `onclick` or a shortcut listener that never sees a
323
+ * component. `isOpen` reads live but does not re-render anything; the hook
324
+ * is for that.
325
+ */
326
+ declare const medianSupport: {
327
+ /** What the panel is doing right now. */
328
+ readonly isOpen: boolean;
329
+ open(): void;
330
+ close(): void;
331
+ toggle(): void;
332
+ /**
333
+ * Put your own data on the next bug report: a crash trace from your error
334
+ * handler, a request id, the state of a store. Objects are written out as
335
+ * JSON. Entries ride the visitor's next message and land on the report,
336
+ * whether or not diagnostics are on.
337
+ */
338
+ attach(label: string, value: unknown): void;
339
+ /**
340
+ * File the crash your error screen is showing, with everything the browser
341
+ * was doing when it happened. One press from a "Report error" button:
342
+ *
343
+ * const outcome = await medianSupport.reportError(error);
344
+ *
345
+ * It answers rather than throws, so the screen can say whether it landed.
346
+ * It works from a boundary with no widget left under it, which is most of
347
+ * them.
348
+ */
349
+ reportError(error: unknown, options?: MedianErrorReportOptions): Promise<MedianErrorReportOutcome>;
350
+ /**
351
+ * Forget this browser's conversation, so the next one starts fresh and
352
+ * anonymous. Call it when your user signs out: whoever sits down next
353
+ * should not find somebody else's thread waiting.
354
+ */
355
+ reset(): void;
356
+ };
357
+ /** The shape of {@link medianSupport}, and of what the hook returns. */
358
+ type MedianSupportControl = {
359
+ /** Whether the panel is open. */
360
+ isOpen: boolean;
361
+ open: () => void;
362
+ close: () => void;
363
+ toggle: () => void;
364
+ /** Put your own data on the next bug report: a crash trace, an id. */
365
+ attach: (label: string, value: unknown) => void;
366
+ /** File the crash your error screen is showing. For a report button. */
367
+ reportError: (error: unknown, options?: MedianErrorReportOptions) => Promise<MedianErrorReportOutcome>;
368
+ /** Forget this browser's conversation. For sign out. */
369
+ reset: () => void;
370
+ };
371
+ /**
372
+ * The widget, from anywhere else on the page:
373
+ *
374
+ * const support = useMedianSupport();
375
+ *
376
+ * <button onClick={support.open}>Contact us</button>
377
+ *
378
+ * `isOpen` follows the panel however it was opened, so a trigger can render
379
+ * itself as pressed, or step out of the way while the panel is up.
380
+ */
381
+ declare function useMedianSupport(): MedianSupportControl;
382
+
383
+ type MedianFeedbackProps = {
384
+ apiKey: string;
385
+ /**
386
+ * Your own trigger. Whatever you pass is rendered where you put it and the
387
+ * panel hangs off it, the way a dropdown hangs off the thing that opens
388
+ * it:
389
+ *
390
+ * <MedianFeedback apiKey="median_pk_...">
391
+ * <button>Share feedback</button>
392
+ * </MedianFeedback>
393
+ *
394
+ * It is wrapped in an inline span that takes the click, so your button
395
+ * keeps its own styles and needs to know nothing about any of this.
396
+ *
397
+ * Without children the panel sits in the bottom right corner instead, for
398
+ * a host opening it from somewhere else entirely with
399
+ * `useMedianFeedback()`.
400
+ */
401
+ children?: ReactNode;
402
+ /**
403
+ * Something already on your page for the panel to hang off, as a CSS
404
+ * selector. For the case children cannot cover: a row inside a dropdown,
405
+ * which closes itself opening this and takes the trigger with it.
406
+ *
407
+ * <MedianFeedback apiKey="median_pk_..." anchor="#account-button" />
408
+ *
409
+ * Looked up each time the panel is placed, so it works for anything that
410
+ * comes and goes. Unlike a trigger it is only a position: pressing it does
411
+ * not open or close the panel, and a press on it dismisses the panel like
412
+ * a press anywhere else. Ignored when `children` are passed.
413
+ */
414
+ anchor?: string;
415
+ /** Who is signed in on your site, if anyone. Shown to your team. */
416
+ user?: MedianUser;
417
+ /**
418
+ * Whether a note carries what the browser already knows: time zone,
419
+ * language, browser, OS, device, screen, and the page it was written on.
420
+ * On by default, because the page is most of what makes a note actionable,
421
+ * which is what the panel says out loud. Pass `false` and none of it is
422
+ * read at all.
423
+ */
424
+ telemetry?: boolean;
425
+ /**
426
+ * Watch what goes wrong on the page, so a bug filed from a note arrives
427
+ * with the error already attached. The same switch `<MedianSupport>`
428
+ * takes, and the same buffer behind it: mount either one with it on.
429
+ */
430
+ diagnostics?: MedianDiagnostics;
431
+ /**
432
+ * Anything about your own app worth having next to a bug report: a build,
433
+ * a route, a flag. Read when a note is sent.
434
+ */
435
+ appState?: Record<string, string | number | boolean>;
436
+ /** Pass it, with `onOpenChange`, to hold the open state yourself. */
437
+ open?: boolean;
438
+ onOpenChange?: (open: boolean) => void;
439
+ /** Any CSS length. The panel never grows past the viewport whatever this says. */
440
+ width?: string;
441
+ /** The heading, for a host whose product calls this something else. */
442
+ title?: string;
443
+ /** The line under it. */
444
+ description?: string;
445
+ placeholder?: string;
446
+ };
447
+ /**
448
+ * A feedback panel, hung off a trigger of your own.
449
+ *
450
+ * One box and one button. Whoever is on the page types a sentence, and a
451
+ * fast model reads it: a bug goes on the bug list, an idea goes on the
452
+ * suggestions list, and an advert goes nowhere. Nobody has to pick a
453
+ * category, rank anything, or write a title, which is the point, because a
454
+ * form that asks for those is a form nobody fills in.
455
+ *
456
+ * <MedianFeedback apiKey="median_pk_...">
457
+ * <MenuItem>Share feedback</MenuItem>
458
+ * </MedianFeedback>
459
+ *
460
+ * The page they were on rides along on its own, which the line under the
461
+ * title says plainly.
462
+ *
463
+ * It brings its own connection, so it works on pages that have never heard
464
+ * of Convex, and it is independent of `<MedianSupport>`: mount either, both,
465
+ * or neither. Mounted together they share a visitor, so the same person
466
+ * writing in and leaving a note is one customer with both to their name.
467
+ */
468
+ declare function MedianFeedback(props: MedianFeedbackProps): react.JSX.Element;
469
+
470
+ /**
471
+ * What a mounted panel hands back: the two things a host without React has
472
+ * no re-render to do for them. Opening and closing go through
473
+ * `medianFeedback`, same as anywhere.
474
+ */
475
+ type MedianFeedbackInstance = {
476
+ /**
477
+ * Change any option after mount. Only the keys passed change, so
478
+ * `update({ user })` when somebody signs in leaves the rest alone.
479
+ */
480
+ update: (options: Partial<MedianFeedbackProps>) => void;
481
+ /** Take the panel off the page. */
482
+ unmount: () => void;
483
+ };
484
+ /**
485
+ * The same panel, for a page that has never heard of React: Vue, Svelte,
486
+ * Rails, a script on plain HTML.
487
+ *
488
+ * const feedback = mountMedianFeedback({ apiKey: "median_pk_..." });
489
+ *
490
+ * document.querySelector("#feedback").onclick = () => medianFeedback.open();
491
+ *
492
+ * There is no trigger to wrap out here, so the panel opens in the bottom
493
+ * right corner and your own button says when. Everything else is the
494
+ * component's, unchanged.
495
+ */
496
+ declare function mountMedianFeedback(options: Omit<MedianFeedbackProps, "children">): MedianFeedbackInstance;
497
+
498
+ declare const medianFeedback: {
499
+ /** What the panel is doing right now. */
500
+ readonly isOpen: boolean;
501
+ open(): void;
502
+ close(): void;
503
+ toggle(): void;
504
+ };
505
+ /** The shape of {@link medianFeedback}, and of what the hook returns. */
506
+ type MedianFeedbackControl = {
507
+ /** Whether the panel is open. */
508
+ isOpen: boolean;
509
+ open: () => void;
510
+ close: () => void;
511
+ toggle: () => void;
512
+ };
513
+ /**
514
+ * The feedback panel, from anywhere else on the page:
515
+ *
516
+ * const feedback = useMedianFeedback();
517
+ *
518
+ * <button onClick={feedback.open}>Share feedback</button>
519
+ *
520
+ * `isOpen` follows the panel however it was opened, so a menu row can render
521
+ * itself as pressed while it is up.
522
+ */
523
+ declare function useMedianFeedback(): MedianFeedbackControl;
524
+
525
+ export { type MedianDiagnostics, type MedianErrorReportOptions, type MedianErrorReportOutcome, MedianFeedback, type MedianFeedbackControl, type MedianFeedbackInstance, type MedianFeedbackProps, MedianSupport, type MedianSupportControl, type MedianSupportInstance, type MedianSupportProps, type MedianUser, type MedianDiagnostics as PortalDiagnostics, type MedianErrorReportOptions as PortalErrorReportOptions, type MedianErrorReportOutcome as PortalErrorReportOutcome, MedianFeedback as PortalFeedback, type MedianFeedbackControl as PortalFeedbackControl, type MedianFeedbackInstance as PortalFeedbackInstance, type MedianFeedbackProps as PortalFeedbackProps, MedianSupport as PortalSupport, type MedianSupportControl as PortalSupportControl, type MedianSupportInstance as PortalSupportInstance, type MedianSupportProps as PortalSupportProps, type MedianUser as PortalUser, type SupportLauncherKind, medianFeedback, medianSupport, mountMedianFeedback, mountMedianSupport, mountMedianFeedback as mountPortalFeedback, mountMedianSupport as mountPortalSupport, medianFeedback as portalFeedback, medianSupport as portalSupport, reportError, useCanReportError, useMedianFeedback, useMedianSupport, useMedianFeedback as usePortalFeedback, useMedianSupport as usePortalSupport };